0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

mql5ではなく、mql6を作ってみた。

0
Posted at

============================================================

ファイル名: 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

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?