============================================================
ファイル名: mql6.jl
説明: MQL6 - 次世代トレーディング言語エンジン
Platinum MQL の後継。MQL5を超える爆速バックテスト
使い方: julia mql6.jl
============================================================
module MQL6
using CSV, DataFrames, Dates, Statistics, Printf
============================================================
MQL6 バージョン情報
============================================================
const MQL6_VERSION = "0.1.0-alpha"
const MQL6_CODENAME = "Platinum"
============================================================
データ構造
============================================================
mutable struct TickData
timestamp::DateTime
bid::Float64
ask::Float64
spread::Float64
volume::Int64
end
mutable struct Position
ticket::Int64
symbol::String
direction::Int64 # 1=Buy, -1=Sell
volume::Float64
entry_price::Float64
entry_time::DateTime
sl::Float64
tp::Float64
profit::Float64
end
mutable struct Account
balance::Float64
equity::Float64
margin::Float64
positions::Vector{Position}
history::Vector{Dict{Symbol,Any}}
Account(balance::Float64) = new(balance, balance, 0.0, Position[], Dict[])
end
============================================================
データローダー
============================================================
function load_ticks(filepath::String)
println("📂 ティックデータを読み込み中: $filepath")
df = CSV.read(filepath, DataFrame)
ticks = TickData[]
for row in eachrow(df)
push!(ticks, TickData(
DateTime(row.timestamp),
row.bid,
row.ask,
row.ask - row.bid,
get(row, :volume, 0)
))
end
println("✅ $(length(ticks)) ティックをロードしました")
return ticks
end
function export_mt5_to_csv(symbol::String, start_date::DateTime, end_date::DateTime, output_path::String)
"""
MT5からティックデータをエクスポートする手順:
1. MT5を開く
2. 「気配値表示」でシンボルを右クリック → 「データ保存」
3. 「バー」ではなく「ティック」を選択
4. 期間を指定してエクスポート
5. CSVとして保存
または、このJuliaコードからMT5のPython APIを叩く:
"""
println("📤 MT5から $symbol のティックデータをエクスポート中...")
println(" 期間: $start_date → $end_date")
# pip install MetaTrader5 が必要
try
# ここでMetaTrader5ライブラリを使う
@eval using MetaTrader5
mt5_initialize() = pyimport("MetaTrader5").initialize()
if mt5_initialize()
ticks = pyimport("MetaTrader5").copy_ticks_range(
symbol,
start_date,
end_date,
pyimport("MetaTrader5").COPY_TICKS_ALL
)
# DataFrameに変換してCSV保存
df = DataFrame(ticks)
CSV.write(output_path, df)
println("✅ エクスポート完了: $output_path")
else
println("❌ MT5に接続できませんでした")
end
catch e
println("⚠️ 自動エクスポートに失敗: $e")
println(" 手動でMT5からCSVエクスポートしてください")
end
end
============================================================
テクニカル指標
============================================================
function sma(prices::Vector{Float64}, period::Int)
n = length(prices)
result = fill(NaN, n)
for i in period:n
result[i] = mean(prices[i-period+1:i])
end
return result
end
function ema(prices::Vector{Float64}, period::Int)
n = length(prices)
result = fill(NaN, n)
multiplier = 2.0 / (period + 1)
result[period] = mean(prices[1:period])
for i in period+1:n
result[i] = (prices[i] - result[i-1]) * multiplier + result[i-1]
end
return result
end
function rsi(prices::Vector{Float64}, period::Int)
n = length(prices)
result = fill(NaN, n)
gains = zeros(n-1)
losses = zeros(n-1)
for i in 2:n
diff = prices[i] - prices[i-1]
if diff > 0
gains[i-1] = diff
else
losses[i-1] = -diff
end
end
avg_gain = mean(gains[1:period])
avg_loss = mean(losses[1:period])
for i in period+1:n
avg_gain = (avg_gain * (period-1) + gains[i-1]) / period
avg_loss = (avg_loss * (period-1) + losses[i-1]) / period
rs = avg_gain / (avg_loss == 0 ? 1e-10 : avg_loss)
result[i] = 100.0 - (100.0 / (1.0 + rs))
end
return result
end
function atr(high::Vector{Float64}, low::Vector{Float64}, close::Vector{Float64}, period::Int)
n = length(close)
result = fill(NaN, n)
tr = zeros(n)
tr[1] = high[1] - low[1]
for i in 2:n
tr[i] = max(high[i] - low[i],
abs(high[i] - close[i-1]),
abs(low[i] - close[i-1]))
end
result[period] = mean(tr[1:period])
for i in period+1:n
result[i] = (result[i-1] * (period-1) + tr[i]) / period
end
return result
end
function bollinger_bands(prices::Vector{Float64}, period::Int, deviation::Float64=2.0)
ma = sma(prices, period)
n = length(prices)
upper = fill(NaN, n)
lower = fill(NaN, n)
for i in period:n
std = std(prices[i-period+1:i])
upper[i] = ma[i] + deviation * std
lower[i] = ma[i] - deviation * std
end
return ma, upper, lower
end
============================================================
クロス検出
============================================================
function cross_above(line1::Vector{Float64}, line2::Vector{Float64})
for i in 2:length(line1)
if !isnan(line1[i]) && !isnan(line2[i]) &&
!isnan(line1[i-1]) && !isnan(line2[i-1])
if line1[i] > line2[i] && line1[i-1] <= line2[i-1]
return true, i
end
end
end
return false, 0
end
function cross_below(line1::Vector{Float64}, line2::Vector{Float64})
for i in 2:length(line1)
if !isnan(line1[i]) && !isnan(line2[i]) &&
!isnan(line1[i-1]) && !isnan(line2[i-1])
if line1[i] < line2[i] && line1[i-1] >= line2[i-1]
return true, i
end
end
end
return false, 0
end
============================================================
ブラック-ショールズ モデル(オプション価格計算)
============================================================
function cdf_normal(x::Float64)
# 累積正規分布関数
a1 = 0.254829592
a2 = -0.284496736
a3 = 1.421413741
a4 = -1.453152027
a5 = 1.061405429
p = 0.3275911
sign = x >= 0 ? 1 : -1
x = abs(x) / sqrt(2.0)
t = 1.0 / (1.0 + p * x)
y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * exp(-x * x)
return 0.5 * (1.0 + sign * y)
end
function black_scholes_price(S::Float64, K::Float64, r::Float64, sigma::Float64, T::Float64)
if T <= 0
return max(S - K, 0.0)
end
d1 = (log(S / K) + (r + sigma^2 / 2.0) * T) / (sigma * sqrt(T))
d2 = d1 - sigma * sqrt(T)
return S * cdf_normal(d1) - K * exp(-r * T) * cdf_normal(d2)
end
function black_scholes_delta(S::Float64, K::Float64, r::Float64, sigma::Float64, T::Float64)
if T <= 0
return S > K ? 1.0 : 0.0
end
d1 = (log(S / K) + (r + sigma^2 / 2.0) * T) / (sigma * sqrt(T))
return cdf_normal(d1)
end
============================================================
資金管理
============================================================
function risk_lots(account::Account, risk_percent::Float64, sl_pips::Float64, symbol::String="EURJPY")
if sl_pips <= 0
return 0.1
end
risk_money = account.balance * risk_percent / 100.0
# EURJPYの簡易計算:1pip = 0.01 = 1000円(1ロットの場合)
pip_value = 1000.0 # 実際は動的に計算すべき
lots = risk_money / (sl_pips * pip_value)
# ロットの最小・最大・ステップ
min_lot = 0.01
max_lot = 100.0
lot_step = 0.01
lots = floor(lots / lot_step) * lot_step
lots = max(min_lot, min(max_lot, lots))
return round(lots, digits=2)
end
============================================================
バックテストエンジン(MQL6のコア)
============================================================
function backtest(
ticks::Vector{TickData},
strategy_fn::Function;
initial_balance::Float64=10000.0,
spread_compensation::Bool=true
)
println("🚀 MQL6 バックテスト開始")
println(" ティック数: $(length(ticks))")
println(" 初期残高: ¥$initial_balance")
account = Account(initial_balance)
start_time = now()
# 価格配列を先に構築(高速化のため)
bids = [t.bid for t in ticks]
asks = [t.ask for t in ticks]
closes = (bids + asks) / 2.0
highs = closes # 簡易版(実際は高値・安値を別途取得)
lows = closes
spreads = [t.spread for t in ticks]
timestamps = [t.timestamp for t in ticks]
# バックテストメインループ
for i in 1:length(ticks)
tick = ticks[i]
# 決済条件のチェック
for pos in account.positions
if pos.direction == 1 # Buy
if tick.bid <= pos.sl
# ストップロス
close_position!(account, pos, tick.bid, tick.timestamp, "SL")
elseif tick.bid >= pos.tp
# テイクプロフィット
close_position!(account, pos, tick.tp, tick.timestamp, "TP")
end
else # Sell
if tick.ask >= pos.sl
close_position!(account, pos, tick.ask, tick.timestamp, "SL")
elseif tick.ask <= pos.tp
close_position!(account, pos, tick.tp, tick.timestamp, "TP")
end
end
end
# 戦略シグナル
signal = strategy_fn(i, bids, asks, closes, highs, lows, spreads, timestamps, account)
if signal == :buy
lot = 0.1
sl = tick.ask - 1.0
tp = tick.ask + 2.0
open_position!(account, 1, lot, tick.ask, tick.timestamp, sl, tp)
elseif signal == :sell
lot = 0.1
sl = tick.bid + 1.0
tp = tick.bid - 2.0
open_position!(account, -1, lot, tick.bid, tick.timestamp, sl, tp)
end
# エクイティ更新
unrealized_pnl = sum(p -> p.profit, account.positions)
account.equity = account.balance + unrealized_pnl
end
# 全ポジションを最終価格で決済
close_all!(account, closes[end], timestamps[end])
elapsed = now() - start_time
println("✅ バックテスト完了: $(elapsed)")
return account
end
function open_position!(account::Account, direction::Int, volume::Float64, price::Float64, time::DateTime, sl::Float64, tp::Float64)
ticket = length(account.history) + 1
pos = Position(ticket, "EURJPY", direction, volume, price, time, sl, tp, 0.0)
push!(account.positions, pos)
end
function close_position!(account::Account, pos::Position, exit_price::Float64, exit_time::DateTime, reason::String)
if pos.direction == 1
profit = (exit_price - pos.entry_price) * pos.volume * 100_000 # EURJPY
else
profit = (pos.entry_price - exit_price) * pos.volume * 100_000
end
account.balance += profit
account.equity = account.balance
# 履歴に記録
push!(account.history, Dict(
:ticket => pos.ticket,
:direction => pos.direction == 1 ? "Buy" : "Sell",
:volume => pos.volume,
:entry => pos.entry_price,
:exit => exit_price,
:profit => profit,
:reason => reason,
:entry_time => pos.entry_time,
:exit_time => exit_time
))
# ポジション削除
filter!(p -> p.ticket != pos.ticket, account.positions)
end
function close_all!(account::Account, price::Float64, time::DateTime)
for pos in copy(account.positions)
close_position!(account, pos, price, time, "CloseAll")
end
end
============================================================
分析・レポート
============================================================
function calculate_metrics(account::Account)
trades = account.history
if isempty(trades)
return Dict(:message => "取引なし")
end
profits = [t[:profit] for t in trades]
winners = filter(p -> p > 0, profits)
losers = filter(p -> p <= 0, profits)
win_rate = length(winners) / length(profits) * 100
gross_profit = sum(winners)
gross_loss = abs(sum(losers))
profit_factor = gross_loss > 0 ? gross_profit / gross_loss : Inf
# ドローダウン計算(簡易版)
peak = account.balance
max_dd = 0.0
Metrics = Dict(
:initial_balance => 10000.0,
:final_balance => account.balance,
:net_profit => account.balance - 10000.0,
:total_trades => length(profits),
:winners => length(winners),
:losers => length(losers),
:win_rate => win_rate,
:gross_profit => gross_profit,
:gross_loss => gross_loss,
:profit_factor => profit_factor,
:avg_profit => length(profits) > 0 ? mean(profits) : 0.0,
:max_profit => length(winners) > 0 ? maximum(winners) : 0.0,
:max_loss => length(losers) > 0 ? minimum(profits) : 0.0,
:max_drawdown => max_dd
)
return Metrics
end
function print_report(account::Account)
metrics = calculate_metrics(account)
println("\n" * "="^60)
println(" 📊 MQL6 バックテスト レポート")
println("="^60)
@printf(" 初期残高: ¥%.2f\n", metrics[:initial_balance])
@printf(" 最終残高: ¥%.2f\n", metrics[:final_balance])
@printf(" 純利益: ¥%.2f\n", metrics[:net_profit])
println(" ---")
@printf(" 総取引数: %d\n", metrics[:total_trades])
@printf(" 勝率: %.1f%%\n", metrics[:win_rate])
@printf(" プロフィットファクター: %.2f\n", metrics[:profit_factor])
@printf(" 最大利益: ¥%.2f\n", metrics[:max_profit])
@printf(" 最大損失: ¥%.2f\n", metrics[:max_loss])
println("="^60)
return metrics
end
============================================================
MQL6 DSL マクロ
============================================================
macro mql6(expr)
return :(interpret_mql6($(QuoteNode(expr))))
end
function interpret_mql6(expr::Expr)
if expr.head == :block
strategy_name = "MQL6_EA"
symbol = "EURJPY"
timeframe = 60
for sub in expr.args
if sub isa Expr && sub.head == :call
func = sub.args[1]
if func == :strategy
strategy_name = string(sub.args[2])
elseif func == :symbol
symbol = string(sub.args[2])
end
end
end
println("📝 MQL6 EA: $strategy_name on $symbol")
return strategy_name, symbol
end
end
============================================================
MT5 API 連携(リアルトレード用)
============================================================
function mt5_initialize()
try
MetaTrader5.initialize()
println("🔗 MT5に接続しました")
return true
catch
println("⚠️ MT5が利用できません。バックテストモードで動作します")
return false
end
end
function mt5_send_order(symbol::String, cmd::String, volume::Float64, price::Float64=0.0)
try
if cmd == "buy"
MetaTrader5.order_send(symbol, MetaTrader5.ORDER_TYPE_BUY, volume, price)
elseif cmd == "sell"
MetaTrader5.order_send(symbol, MetaTrader5.ORDER_TYPE_SELL, volume, price)
end
println("✅ 注文送信: $cmd $volume lots @ $price")
catch e
println("❌ 注文失敗: $e")
end
end
============================================================
メイン関数
============================================================
function main()
println("""
╔══════════════════════════════════════════╗
║ MQL6 - Platinum Trading Language v$(MQL6_VERSION) ║
║ "MQL5を超える、次世代のトレーディング言語" ║
╚══════════════════════════════════════════╝
""")
# サンプルデータ生成(実際はCSVから読み込む)
println("\n📦 サンプルデータを生成中...")
base_time = DateTime(2023, 1, 1, 0, 0, 0)
ticks = TickData[]
price = 140.0
for i in 0:10000
price += randn() * 0.01
bid = price
ask = price + 0.002
push!(ticks, TickData(
base_time + Second(i),
bid,
ask,
ask - bid,
rand(1:100)
))
end
println("✅ $(length(ticks)) ティック生成完了")
# 簡単な戦略:短期MAが長期MAを上回ったら買い
function simple_strategy(i, bids, asks, closes, highs, lows, spreads, timestamps, account)
if i < 50
return :hold
end
short_ma = mean(closes[i-9:i])
long_ma = mean(closes[i-49:i])
if short_ma > long_ma && length(account.positions) == 0
return :buy
elseif short_ma < long_ma && length(account.positions) > 0
return :sell
end
return :hold
end
# バックテスト実行
println("\n🔄 バックテスト実行中...")
account = backtest(ticks, simple_strategy, initial_balance=10000.0)
# レポート出力
print_report(account)
println("\n✨ MQL6: これが次世代のバックテストです。")
println(" platinum-mql から進化しました。")
end
============================================================
エクスポート
============================================================
export load_ticks, backtest, print_report, calculate_metrics
export sma, ema, rsi, atr, bollinger_bands
export cross_above, cross_below
export black_scholes_price, black_scholes_delta
export risk_lots
export TickData, Position, Account
export @mql6
end # module MQL6
============================================================
直接実行用
============================================================
if abspath(PROGRAM_FILE) == @__FILE__
using .MQL6
MQL6.main()
end