複数をポジを一括で指値、逆指値出す(mt5)

ポジが最近多くなってきて一個ずつ指値とか出すの面倒だな~って思ってAIが昔より進化したのでサクッと作って貰いました。

私は全くプログラムとかできないんで細かいことは分かりませんができました。
まだちゃんと機能しない部分もあるんですけどもねww

一応乗っけておきますけど(自己責任でお願いします)ver1としておきます。まだAIと格闘中(8/3)

//+------------------------------------------------------------------+
//|                    MultiChart_TradingPanel.mq5                 |
//|                              Copyright 2025, Your Name         |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, Your Name"
#property version   "2.00"
#property indicator_chart_window

#include <Controls\Panel.mqh>
#include <Controls\Button.mqh>
#include <Controls\Label.mqh>
#include <Controls\Edit.mqh>
#include <Controls\ComboBox.mqh>

//+------------------------------------------------------------------+
//| パラメータ                                                        |
//+------------------------------------------------------------------+
input int PanelX=10, PanelY=30, PanelWidth=400, PanelHeight=600;
input bool StartMinimized=false;
input int MinimizedHeight=35;
input bool ShowSpread=true, ShowPositionInfo=true;
input double DefaultTP=1.10000, DefaultSL=1.09500;
input color PanelBG=C'40,40,40', PanelBorder=C'60,60,60', TextColor=clrWhite;
input bool UltraFastMode=true;
input int MaxUpdatesPerSecond=30;
input int MaxSymbols=10; // 最大表示通貨ペア数

//+------------------------------------------------------------------+
//| 構造体定義                                                        |
//+------------------------------------------------------------------+
struct SymbolData {
    string name;
    double bid, ask;
    int spread;
    int pos_cnt[5];      // [合計,売り,買い,損,益]
    double pos_pnl[5];   // 対応する損益
    bool active;
};

//+------------------------------------------------------------------+
//| グローバル変数                                                    |
//+------------------------------------------------------------------+
CPanel p;
CLabel l[50];           // 増加
CButton b[20];          // 増加
CEdit e[2];
CComboBox combo;

SymbolData symbols[10]; // 最大10通貨ペア
int symbol_count=0;
int selected_symbol=0;
bool minimized=false;
int pw, ph, eh, mh;
double tp=0.0, sl=0.0;

// 最適化変数
ulong last_tick=0, min_interval;
bool force_update=false;
int tick_count=0, update_count=0;

//+------------------------------------------------------------------+
//| 初期化                                                          |
//+------------------------------------------------------------------+
int OnInit()
{
    tp = DefaultTP;
    sl = DefaultSL;
    min_interval = UltraFastMode ? (1000000/MaxUpdatesPerSecond) : 100000;
    force_update = true;
   
    // アクティブなチャートの通貨ペアを収集
    CollectActiveSymbols();
   
    CalcSize();
   
    if(!p.Create(0, "MultiTradingPanel", 0, PanelX, PanelY, PanelX+pw, PanelY+(minimized?mh:eh)))
        return INIT_FAILED;
   
    p.ColorBackground(PanelBG);
    p.ColorBorder(PanelBorder);
   
    CreateUI();
    ChartRedraw();
   
    Print("マルチチャートパネル初期化完了 - 対象通貨ペア:", symbol_count);
    return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| アクティブ通貨ペア収集                                            |
//+------------------------------------------------------------------+
void CollectActiveSymbols()
{
    symbol_count = 0;
   
    // 構造体配列を手動で初期化
    for(int i=0; i<10; i++) {
        symbols[i].name = "";
        symbols[i].bid = 0;
        symbols[i].ask = 0;
        symbols[i].spread = 0;
        symbols[i].active = false;
        for(int j=0; j<5; j++) {
            symbols[i].pos_cnt[j] = 0;
            symbols[i].pos_pnl[j] = 0.0;
        }
    }
   
    // 現在のチャートの通貨ペアを追加
    AddSymbol(Symbol());
   
    // 他の開いているチャートの通貨ペアを収集
    long chart_id = ChartFirst();
    while(chart_id != -1 && symbol_count < MaxSymbols) {
        string chart_symbol = ChartSymbol(chart_id);
        if(chart_symbol != "" && chart_symbol != Symbol()) {
            AddSymbol(chart_symbol);
        }
        chart_id = ChartNext(chart_id);
    }
   
    Print("収集した通貨ペア数: ", symbol_count);
    for(int i=0; i<symbol_count; i++) {
        Print("  ", i, ": ", symbols[i].name);
    }
}

//+------------------------------------------------------------------+
//| 通貨ペア追加                                                      |
//+------------------------------------------------------------------+
void AddSymbol(string sym)
{
    // 重複チェック
    for(int i=0; i<symbol_count; i++) {
        if(symbols[i].name == sym) return;
    }
   
    if(symbol_count >= MaxSymbols) return;
   
    symbols[symbol_count].name = sym;
    symbols[symbol_count].active = true;
    for(int j=0; j<5; j++) {
        symbols[symbol_count].pos_cnt[j] = 0;
        symbols[symbol_count].pos_pnl[j] = 0.0;
    }
    symbol_count++;
}

//+------------------------------------------------------------------+
//| サイズ計算                                                      |
//+------------------------------------------------------------------+
void CalcSize()
{
    pw = PanelWidth;
    eh = 80 + (ShowSpread?25:0) + 120 + 150 + (ShowPositionInfo ? symbol_count*25 : 0) + 200;
    mh = MinimizedHeight;
    ph = minimized ? mh : eh;
}

//+------------------------------------------------------------------+
//| UI作成                                                          |
//+------------------------------------------------------------------+
void CreateUI()
{
    DestroyUI();
   
    // タイトル & 最小化
    MakeLabel(0, "Multi-Chart Trading Panel", 10, 5, pw-40, 20, 9);
    MakeButton(10, minimized?"+":"-", pw-35, 5, 30, 20, C'70,70,70');
   
    if(minimized) return;
   
    int y = 30;
   
    // 通貨ペア選択コンボボックス
    MakeLabel(1, "通貨ペア選択:", 10, y, 80, 20, 8);
    MakeCombo(y);
    y += 30;
   
    // 選択された通貨ペアの情報表示
    string sel_sym = symbols[selected_symbol].name;
    MakeLabel(2, "選択中: "+sel_sym, 10, y, 150, 20, 8);
   
    // スプレッド表示
    if(ShowSpread) {
        MakeLabel(3, "Spread: "+IntegerToString(symbols[selected_symbol].spread)+" pts", 170, y, 120, 20, 8);
    }
    y += 25;
   
    // 全決済ボタン(全通貨ペア対象)
    MakeButton(0, "全通貨ペア 全決済", 10, y, pw-20, 40, clrDarkRed);
    y += 50;
   
    // 選択通貨ペアの操作ボタン
    MakeLabel(4, "【 "+sel_sym+" 操作 】", 10, y, pw-20, 20, 8);
    y += 25;
   
    int bw = (pw-35)/2;
    MakeButton(1, sel_sym+" 売り決済", 10, y, bw, 30, clrDeepPink);
    MakeButton(2, sel_sym+" 買い決済", 15+bw, y, bw, 30, clrBlue);
    y += 35;
    MakeButton(3, sel_sym+" 含み損決済", 10, y, bw, 30, clrMaroon);
    MakeButton(4, sel_sym+" 含み益決済", 15+bw, y, bw, 30, clrNavy);
    y += 40;
   
    // 選択通貨ペア 全決済
    MakeButton(5, sel_sym+" 全決済", 10, y, pw-20, 30, clrRed);
    y += 40;
   
    // 全通貨ペアのポジション情報
    if(ShowPositionInfo) {
        MakeLabel(5, "━━━ 全通貨ペア ポジション状況 ━━━", 10, y, pw-20, 20, 8);
        y += 25;
       
        for(int i=0; i<symbol_count; i++) {
            if(!symbols[i].active) continue;
            string info = symbols[i].name + " | ポジ:" + IntegerToString(symbols[i].pos_cnt[0]) +
                         " | 損益:" + DoubleToString(symbols[i].pos_pnl[0], 2);
            MakeLabel(10+i, info, 10, y, pw-20, 20, 8);
            y += 22;
        }
        y += 10;
    }
   
    // TP/SL設定エリア
    MakeLabel(30, "━━━ "+sel_sym+" TP/SL一括設定 ━━━", 10, y, pw-20, 20, 8);
    y += 25;
   
    // 現在価格表示
    string price_text = "Bid: "+DoubleToString(symbols[selected_symbol].bid, GetDigits(sel_sym))+
                       " Ask: "+DoubleToString(symbols[selected_symbol].ask, GetDigits(sel_sym));
    MakeLabel(31, price_text, 10, y, pw-20, 20, 8);
    y += 25;
   
    // TP設定行
    MakeLabel(32, "利確レート:", 10, y, 70, 20, 8);
    MakeEdit(0, DoubleToString(tp, GetDigits(sel_sym)), 85, y, 80, 20);
    MakeButton(6, "TP設定", 175, y, 50, 20, clrGreen);
    MakeButton(7, "TP削除", 235, y, 50, 20, clrOrange);
    y += 25;
   
    // SL設定行
    MakeLabel(33, "損切レート:", 10, y, 70, 20, 8);
    MakeEdit(1, DoubleToString(sl, GetDigits(sel_sym)), 85, y, 80, 20);
    MakeButton(8, "SL設定", 175, y, 50, 20, clrRed);
    MakeButton(9, "SL削除", 235, y, 50, 20, clrOrange);
    y += 25;
   
    // 価格取得ボタン
    MakeButton(11, "Bid取得", 10, y, 70, 20, clrDarkBlue);
    MakeButton(12, "Ask取得", 85, y, 70, 20, clrDarkGreen);
    MakeButton(13, "更新", 170, y, 50, 20, clrGray);
}

//+------------------------------------------------------------------+
//| コンボボックス作成                                                |
//+------------------------------------------------------------------+
void MakeCombo(int y)
{
    if(combo.Id()>0) combo.Destroy();
    if(ObjectFind(0,"SymbolCombo")>=0) ObjectDelete(0,"SymbolCombo");
   
    if(combo.Create(0, "SymbolCombo", 0, PanelX+100, PanelY+y, PanelX+280, PanelY+y+20)) {
        for(int i=0; i<symbol_count; i++) {
            combo.ItemAdd(symbols[i].name);
        }
        combo.Select(selected_symbol);
        combo.Show();
    }
}

//+------------------------------------------------------------------+
//| ヘルパー関数                                                    |
//+------------------------------------------------------------------+
int GetDigits(string sym) { return (int)SymbolInfoInteger(sym, SYMBOL_DIGITS); }

//+------------------------------------------------------------------+
//| UI作成関数                                                      |
//+------------------------------------------------------------------+
void MakeLabel(int i, string txt, int x, int y, int w, int h, int fs)
{
    if(l[i].Id()>0) l[i].Destroy();
    string n = "Label"+IntegerToString(i);
    if(ObjectFind(0,n)>=0) ObjectDelete(0,n);
   
    if(l[i].Create(0, n, 0, PanelX+x, PanelY+y, PanelX+x+w, PanelY+y+h)) {
        l[i].Text(txt);
        l[i].Color(TextColor);
        l[i].ColorBackground(PanelBG);
        l[i].FontSize(fs);
        l[i].Show();
    }
}

void MakeButton(int i, string txt, int x, int y, int w, int h, color bg)
{
    if(b[i].Id()>0) b[i].Destroy();
    string n = "Button"+IntegerToString(i);
    if(ObjectFind(0,n)>=0) ObjectDelete(0,n);
   
    if(b[i].Create(0, n, 0, PanelX+x, PanelY+y, PanelX+x+w, PanelY+y+h)) {
        b[i].Text(txt);
        b[i].ColorBackground(bg);
        b[i].Color(clrWhite);
        b[i].FontSize(8);
        b[i].Show();
    }
}

void MakeEdit(int i, string txt, int x, int y, int w, int h)
{
    if(e[i].Id()>0) e[i].Destroy();
    string n = "Edit"+IntegerToString(i);
    if(ObjectFind(0,n)>=0) ObjectDelete(0,n);
   
    if(e[i].Create(0, n, 0, PanelX+x, PanelY+y, PanelX+x+w, PanelY+y+h)) {
        e[i].Text(txt);
        e[i].Color(clrBlack);
        e[i].ColorBackground(clrWhite);
        e[i].FontSize(8);
        e[i].Show();
    }
}

//+------------------------------------------------------------------+
//| 折りたたみ                                                      |
//+------------------------------------------------------------------+
void Toggle()
{
    minimized = !minimized;
    ChartRedraw();
    Sleep(50);
   
    DestroyUI();
    p.Destroy();
    ChartRedraw();
    Sleep(50);
   
    CalcSize();
    if(p.Create(0, "MultiTradingPanel", 0, PanelX, PanelY, PanelX+pw, PanelY+(minimized?mh:eh))) {
        p.ColorBackground(PanelBG);
        p.ColorBorder(PanelBorder);
        CreateUI();
        ChartRedraw();
    }
}

//+------------------------------------------------------------------+
//| UI削除                                                          |
//+------------------------------------------------------------------+
void DestroyUI()
{
    for(int i=0; i<50; i++) {
        if(l[i].Id()>0) l[i].Destroy();
        ObjectDelete(0, "Label"+IntegerToString(i));
    }
    for(int i=0; i<20; i++) {
        if(b[i].Id()>0) b[i].Destroy();
        ObjectDelete(0, "Button"+IntegerToString(i));
    }
    for(int i=0; i<2; i++) {
        if(e[i].Id()>0) e[i].Destroy();
        ObjectDelete(0, "Edit"+IntegerToString(i));
    }
    if(combo.Id()>0) combo.Destroy();
    ObjectDelete(0, "SymbolCombo");
}

//+------------------------------------------------------------------+
//| Tick更新                                                        |
//+------------------------------------------------------------------+
void OnTick()
{
    if(minimized) return;
   
    tick_count++;
    ulong now = GetTickCount();
   
    if(UltraFastMode || force_update || (now-last_tick) >= (min_interval/1000)) {
        UpdateAllSymbols();
        UpdateUI();
       
        last_tick = now;
        update_count++;
        force_update = false;
    }
}

//+------------------------------------------------------------------+
//| 全通貨ペア更新                                                  |
//+------------------------------------------------------------------+
void UpdateAllSymbols()
{
    for(int i=0; i<symbol_count; i++) {
        if(!symbols[i].active) continue;
       
        string sym = symbols[i].name;
       
        // 価格更新
        symbols[i].bid = SymbolInfoDouble(sym, SYMBOL_BID);
        symbols[i].ask = SymbolInfoDouble(sym, SYMBOL_ASK);
        symbols[i].spread = (int)SymbolInfoInteger(sym, SYMBOL_SPREAD);
       
        // ポジション情報更新
        UpdateSymbolPositions(i);
    }
}

//+------------------------------------------------------------------+
//| 通貨ペア別ポジション更新                                          |
//+------------------------------------------------------------------+
void UpdateSymbolPositions(int sym_idx)
{
    for(int j=0; j<5; j++) {
        symbols[sym_idx].pos_cnt[j] = 0;
        symbols[sym_idx].pos_pnl[j] = 0.0;
    }
   
    string sym = symbols[sym_idx].name;
    int total = PositionsTotal();
   
    for(int i=0; i<total; i++) {
        if(PositionGetSymbol(i) != sym) continue;
       
        double profit = PositionGetDouble(POSITION_PROFIT);
        bool is_sell = (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL);
       
        symbols[sym_idx].pos_cnt[0]++; symbols[sym_idx].pos_pnl[0] += profit;
       
        if(is_sell) { symbols[sym_idx].pos_cnt[1]++; symbols[sym_idx].pos_pnl[1] += profit; }
        else { symbols[sym_idx].pos_cnt[2]++; symbols[sym_idx].pos_pnl[2] += profit; }
       
        if(profit < 0) { symbols[sym_idx].pos_cnt[3]++; symbols[sym_idx].pos_pnl[3] += profit; }
        else if(profit > 0) { symbols[sym_idx].pos_cnt[4]++; symbols[sym_idx].pos_pnl[4] += profit; }
    }
}

//+------------------------------------------------------------------+
//| UI更新                                                          |
//+------------------------------------------------------------------+
void UpdateUI()
{
    if(selected_symbol >= symbol_count) return;
   
    string sel_sym = symbols[selected_symbol].name;
   
    // スプレッド更新
    if(ShowSpread && l[3].Id()>0) {
        l[3].Text("Spread: "+IntegerToString(symbols[selected_symbol].spread)+" pts");
    }
   
    // 価格更新
    if(l[31].Id()>0) {
        string price_text = "Bid: "+DoubleToString(symbols[selected_symbol].bid, GetDigits(sel_sym))+
                           " Ask: "+DoubleToString(symbols[selected_symbol].ask, GetDigits(sel_sym));
        l[31].Text(price_text);
    }
   
    // ポジション情報更新
    if(ShowPositionInfo) {
        for(int i=0; i<symbol_count; i++) {
            if(!symbols[i].active || l[10+i].Id()<=0) continue;
            string info = symbols[i].name + " | ポジ:" + IntegerToString(symbols[i].pos_cnt[0]) +
                         " | 損益:" + DoubleToString(symbols[i].pos_pnl[0], 2);
            l[10+i].Text(info);
        }
    }
}

//+------------------------------------------------------------------+
//| チャートイベント                                                |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
    // コンボボックス選択変更
    if(id == CHARTEVENT_OBJECT_ENDEDIT && sparam == "SymbolCombo") {
        selected_symbol = combo.Value();
        CreateUI(); // UI再作成
        return;
    }
   
    if(id != CHARTEVENT_OBJECT_CLICK) return;
   
    if(sparam == "Button10") {
        Toggle();
        ObjectSetInteger(0, "Button10", OBJPROP_STATE, false);
        return;
    }
   
    if(minimized) return;
    ObjectSetInteger(0, sparam, OBJPROP_STATE, false);
   
    string sel_sym = symbols[selected_symbol].name;
   
    // 全通貨ペア全決済
    if(sparam == "Button0") {
        CloseAllSymbols();
        force_update = true;
        return;
    }
   
    // 選択通貨ペアの操作
    for(int i=1; i<=5; i++) {
        if(sparam == "Button"+IntegerToString(i)) {
            CloseAction(sel_sym, i-1);
            force_update = true;
            return;
        }
    }
   
    // TP/SL制御
    if(sparam == "Button6") {
        tp = StringToDouble(e[0].Text());
        SetTPSL(sel_sym, tp, -1);
    }
    else if(sparam == "Button8") {
        sl = StringToDouble(e[1].Text());
        SetTPSL(sel_sym, -1, sl);
    }
    else if(sparam == "Button7") SetTPSL(sel_sym, 0, -1);
    else if(sparam == "Button9") SetTPSL(sel_sym, -1, 0);
    else if(sparam == "Button11") {
        string price = DoubleToString(symbols[selected_symbol].bid, GetDigits(sel_sym));
        e[0].Text(price); e[1].Text(price);
    }
    else if(sparam == "Button12") {
        string price = DoubleToString(symbols[selected_symbol].ask, GetDigits(sel_sym));
        e[0].Text(price); e[1].Text(price);
    }
    else if(sparam == "Button13") {
        CollectActiveSymbols();
        CreateUI();
        force_update = true;
    }
}

//+------------------------------------------------------------------+
//| 全通貨ペア全決済                                                |
//+------------------------------------------------------------------+
void CloseAllSymbols()
{
    Print("全通貨ペア全決済開始");
    int closed_count = 0;
   
    for(int i=PositionsTotal()-1; i>=0; i--) {
        string pos_sym = PositionGetSymbol(i);
       
        // 管理対象の通貨ペアかチェック
        bool is_managed = false;
        for(int j=0; j<symbol_count; j++) {
            if(symbols[j].name == pos_sym) {
                is_managed = true;
                break;
            }
        }
       
        if(is_managed) {
            ulong ticket = PositionGetInteger(POSITION_TICKET);
            if(ClosePos(ticket, pos_sym)) closed_count++;
        }
    }
   
    Print("全通貨ペア全決済完了 - 決済数:", closed_count);
}

//+------------------------------------------------------------------+
//| TP/SL設定                                                       |
//+------------------------------------------------------------------+
void SetTPSL(string sym, double tp_rate, double sl_rate)
{
    int digits = GetDigits(sym);
    int success=0, fail=0;
   
    ulong tickets[];
    int count=0;
   
    for(int i=0; i<PositionsTotal(); i++) {
        if(PositionGetSymbol(i) == sym) {
            count++;
            ArrayResize(tickets, count);
            tickets[count-1] = PositionGetInteger(POSITION_TICKET);
        }
    }
   
    for(int i=0; i<count; i++) {
        if(!PositionSelectByTicket(tickets[i])) { fail++; continue; }
       
        double cur_tp = PositionGetDouble(POSITION_TP);
        double cur_sl = PositionGetDouble(POSITION_SL);
       
        double new_tp = (tp_rate==-1) ? cur_tp : ((tp_rate<=0) ? 0 : NormalizeDouble(tp_rate, digits));
        double new_sl = (sl_rate==-1) ? cur_sl : ((sl_rate<=0) ? 0 : NormalizeDouble(sl_rate, digits));
       
        if(MathAbs(new_tp-cur_tp)<Point() && MathAbs(new_sl-cur_sl)<Point()) continue;
       
        if(ModifyPos(tickets[i], sym, new_sl, new_tp)) success++;
        else fail++;
       
        Sleep(50);
    }
   
    Print(sym, " TP/SL設定完了 - 成功:", success, " 失敗:", fail);
    force_update = true;
}

//+------------------------------------------------------------------+
//| ポジション修正                                                  |
//+------------------------------------------------------------------+
bool ModifyPos(ulong ticket, string sym, double sl, double tp)
{
    MqlTradeRequest req;
    MqlTradeResult res;
   
    ZeroMemory(req);
    req.action = TRADE_ACTION_SLTP;
    req.position = ticket;
    req.symbol = sym;
    req.sl = sl;
    req.tp = tp;
   
    return OrderSend(req, res) && res.retcode==TRADE_RETCODE_DONE;
}

//+------------------------------------------------------------------+
//| クローズアクション                                              |
//+------------------------------------------------------------------+
void CloseAction(string sym, int action)
{
    for(int i=PositionsTotal()-1; i>=0; i--) {
        if(PositionGetSymbol(i) != sym) continue;
       
        ulong ticket = PositionGetInteger(POSITION_TICKET);
        ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
        double profit = PositionGetDouble(POSITION_PROFIT);
       
        bool close = false;
        switch(action) {
            case 0: close = true; break;
            case 1: close = (type == POSITION_TYPE_SELL); break;
            case 2: close = (type == POSITION_TYPE_BUY); break;
            case 3: close = (profit < 0); break;
            case 4: close = (profit > 0); break;
        }
       
        if(close) ClosePos(ticket, sym);
    }
}

//+------------------------------------------------------------------+
//| ポジションクローズ                                              |
//+------------------------------------------------------------------+
bool ClosePos(ulong ticket, string sym)
{
    MqlTradeRequest req;
    MqlTradeResult res;
   
    ZeroMemory(req);
    req.action = TRADE_ACTION_DEAL;
    req.position = ticket;
    req.symbol = sym;
   
    if(PositionSelectByTicket(ticket)) {
        req.volume = PositionGetDouble(POSITION_VOLUME);
       
        if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
            req.type = ORDER_TYPE_SELL;
            req.price = SymbolInfoDouble(sym, SYMBOL_BID);
        } else {
            req.type = ORDER_TYPE_BUY;
            req.price = SymbolInfoDouble(sym, SYMBOL_ASK);
        }
       
        req.deviation = 3;
        return OrderSend(req, res) && res.retcode==TRADE_RETCODE_DONE;
    }
    return false;
}

//+------------------------------------------------------------------+
//| 終了処理                                                        |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    Print("マルチチャートパネル終了 - 対象通貨ペア:", symbol_count);
    DestroyUI();
    p.Destroy();
}

指値、逆指値は出せましたがまだまだよくない部分がありますが使えないことはないですかね???
見た目とかもっとシンプルで昨日も無くせばよくなると思いますが。

AIが面白くなってつい色々機能をつけ足してしまいました。

コメント

タイトルとURLをコピーしました