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

2025/9/1に更新

コード①
//+------------------------------------------------------------------+
//| BreakEvenPointLine.mq5                                          |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots   0

input color           InpLineColor = clrDodgerBlue;
input ENUM_LINE_STYLE InpLineStyle = STYLE_DASHDOT;
input int             InpLineWidth = 2;
input bool            InpShowLabel = true;

// 追加設定(表示に関するパラメータ)
input ENUM_ANCHOR_POSITION InpLabelCorner = CORNER_RIGHT_UPPER;
input int InpXDistance = 10;
input int InpYDistance = 20;
input int InpFontSize = 18;
input bool InpBoldFont = true;
input bool InpShowInJPY = true;

// グローバル変数
string g_line_name;
string g_profit_label;
const double EPSILON = 1e-8;

int OnInit()
{
  g_line_name     = "BEP_Line_" + _Symbol + "_" + IntegerToString(ChartID());
  g_profit_label  = "TotalProfitLabel_" + _Symbol + "_" + IntegerToString(ChartID());
  return INIT_SUCCEEDED;
}

void OnDeinit(const int reason)
{
  ObjectDelete(0, g_line_name);
  ObjectDelete(0, g_profit_label);
}

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
  double total_weighted_price = 0.0;
  double total_net_lots       = 0.0;
  int    position_count       = 0;

  double contract_size = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE);
  if (contract_size <= 0)
  {
    Print("契約サイズの取得に失敗: ", _Symbol);
    return rates_total;
  }

  double total_profit = 0.0;
  double total_pips   = 0.0;

  for (int i = PositionsTotal() - 1; i >= 0; i--)
  {
    ulong ticket = PositionGetTicket(i);
    if (!PositionSelectByTicket(ticket))
      continue;

    if (PositionGetString(POSITION_SYMBOL) != _Symbol)
      continue;

    double lots       = PositionGetDouble(POSITION_VOLUME);
    double open_price = PositionGetDouble(POSITION_PRICE_OPEN);
    double cur_price  = PositionGetDouble(POSITION_PRICE_CURRENT);
    long type         = PositionGetInteger(POSITION_TYPE);
    double commission = PositionGetDouble(POSITION_COMMISSION);
    double swap       = PositionGetDouble(POSITION_SWAP);

    int direction = (type == POSITION_TYPE_BUY) ? 1 : -1;

    total_weighted_price += (open_price * lots * direction) + ((commission + swap) / contract_size);
    total_net_lots       += lots * direction;

    //--- 損益とPIPS計算
    double diff = (cur_price - open_price) * direction;
    double pip_value = _Point * ((StringFind(_Symbol, "JPY") != -1) ? 1 : 10);
    double pips = diff / pip_value;
    total_pips += pips;

    total_profit += PositionGetDouble(POSITION_PROFIT);
    position_count++;
  }

  // 損益分岐点ライン描画・削除
  if (position_count == 0 || MathAbs(total_net_lots) < EPSILON)
  {
    ObjectDelete(0, g_line_name);
    ObjectDelete(0, g_profit_label);
    Comment((position_count == 0) ? "ポジションがありません。" : "両建て状態でBEP算出不可。");
    return rates_total;
  }

  double bep_price = total_weighted_price / total_net_lots;

  if (ObjectFind(0, g_line_name) < 0)
  {
    ObjectCreate(0, g_line_name, OBJ_HLINE, 0, 0, 0);
    ObjectSetInteger(0, g_line_name, OBJPROP_SELECTABLE, false);
    ObjectSetInteger(0, g_line_name, OBJPROP_COLOR,   InpLineColor);
    ObjectSetInteger(0, g_line_name, OBJPROP_STYLE,   InpLineStyle);
    ObjectSetInteger(0, g_line_name, OBJPROP_WIDTH,   InpLineWidth);
  }

  ObjectSetDouble(0, g_line_name, OBJPROP_PRICE, 0, bep_price);
  ObjectSetString(0, g_line_name, OBJPROP_TEXT, (InpShowLabel ? "BEP: " + DoubleToString(bep_price, _Digits) : ""));

  //--- 損益ラベル
  if (ObjectFind(0, g_profit_label) < 0)
  {
    ObjectCreate(0, g_profit_label, OBJ_LABEL, 0, 0, 0);
    ObjectSetInteger(0, g_profit_label, OBJPROP_CORNER, InpLabelCorner);
    ObjectSetInteger(0, g_profit_label, OBJPROP_XDISTANCE, InpXDistance);
    ObjectSetInteger(0, g_profit_label, OBJPROP_YDISTANCE, InpYDistance);
    ObjectSetInteger(0, g_profit_label, OBJPROP_FONTSIZE, InpFontSize);
    ObjectSetInteger(0, g_profit_label, OBJPROP_SELECTABLE, false);
    ObjectSetInteger(0, g_profit_label, OBJPROP_CORNER, InpLabelCorner);
    ObjectSetInteger(0, g_profit_label, OBJPROP_FONTSIZE, InpFontSize);
    ObjectSetInteger(0, g_profit_label, OBJPROP_BOLD, InpBoldFont);
  }

  //--- 表示色
  color profit_color = (total_profit >= 0) ? clrBlue : clrRed;
  ObjectSetInteger(0, g_profit_label, OBJPROP_COLOR, profit_color);

  //--- 円換算(必要な場合)
  double jpy_profit = total_profit;
  if (InpShowInJPY && StringFind(_Symbol, "JPY") == -1)
  {
    string jpy_symbol = StringSubstr(_Symbol, 3, 3) + "JPY";
    if (!SymbolSelect(jpy_symbol, true) || !SymbolInfoDouble(jpy_symbol, SYMBOL_BID))
      jpy_symbol = "USDJPY"; // fallback

    double rate = SymbolInfoDouble(jpy_symbol, SYMBOL_BID);
    jpy_profit *= rate;
  }

  //--- テキスト作成
  string profit_text = StringFormat("%+.1f pips / %+.2f JPY", total_pips, jpy_profit);
  ObjectSetString(0, g_profit_label, OBJPROP_TEXT, profit_text);

  return rates_total;
}

mt5にて損益分岐点のラインを引いて損益もちょっとでるインジケーター(9/1確定)
・損益表示に難がありますが問題なく使えます。
・青がプラス、赤がマイナス
・チャート内なら移動も可

コード②
//+------------------------------------------------------------------+

//|            SimpleTpSlPanel.mq5          |

//+------------------------------------------------------------------+

#property copyright "Copyright 2025, Your Name"

#property version "1.00"

#property indicator_chart_window



#include <Controls\Panel.mqh>

#include <Controls\Button.mqh>

#include <Controls\Label.mqh>

#include <Controls\Edit.mqh>



// パネル設定

input int PanelX = 10;

input int PanelY = 30;

input int PanelWidth = 220;

input int PanelHeight = 140;

input color PanelBG = clrNONE;

input color PanelBorder = clrNONE;

input color TextColor = clrWhite;

input bool StartMinimized = false;



// デフォルト値

input double DefaultTP = 0.0;

input double DefaultSL = 0.0;



// UI コントロール

CPanel panel_main;

CLabel labels[10];

CButton buttons[8];

CEdit edits[2];



// グローバル変数

double take_profit = 0.0;

double stop_loss = 0.0;

bool minimized = false;

string current_symbol = "";

int symbol_digits = 4;



// ドラッグ関連変数

bool is_dragging = false;

bool drag_mode = false; // クリックドラッグモード用

int drag_start_x = 0;

int drag_start_y = 0;

int panel_x = 0;

int panel_y = 0;

int last_mouse_x = 0;

int last_mouse_y = 0;



//+------------------------------------------------------------------+

//| 初期化関数                            |

//+------------------------------------------------------------------+

int OnInit()

{

  current_symbol = Symbol();

  symbol_digits = (int)SymbolInfoInteger(current_symbol, SYMBOL_DIGITS);

  take_profit = DefaultTP;

  stop_loss = DefaultSL;

  minimized = StartMinimized;

  panel_x = PanelX;

  panel_y = PanelY;

  

  // マウスイベントを有効化

  ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);

  

  if(!CreatePanel()) {

    return INIT_FAILED;

  }

  

  CreateUI();

  ChartRedraw();

  return INIT_SUCCEEDED;

}



//+------------------------------------------------------------------+

//| パネル作成                            |

//+------------------------------------------------------------------+

bool CreatePanel()

{

  int panel_h = minimized ? 25 : PanelHeight;

  

  if(!panel_main.Create(0, "TpSlPanel", 0, panel_x, panel_y, panel_x + PanelWidth, panel_y + panel_h)) {

    return false;

  }

  

  panel_main.ColorBackground(clrNONE);

  panel_main.ColorBorder(clrNONE);

  return true;

}



//+------------------------------------------------------------------+

//| UI作成                             |

//+------------------------------------------------------------------+

void CreateUI()

{

  if(minimized) {

    CreateMinimizedUI();

  } else {

    CreateFullUI();

  }

}



//+------------------------------------------------------------------+

//| 最小化UI作成                           |

//+------------------------------------------------------------------+

void CreateMinimizedUI()

{

  CreateLabel(0, "TP/SL", 5, 8, 60, 18, 8);

  CreateButton(7, "□", PanelWidth - 20, 5, 15, 20, C'80,80,80');

}



//+------------------------------------------------------------------+

//| フルUI作成                            |

//+------------------------------------------------------------------+

void CreateFullUI()

{

  int y = 5;

  

  // タイトルと最小化ボタン

  CreateLabel(0, "TP/SL設定", 5, y, PanelWidth - 25, 18, 8);

  CreateButton(7, "-", PanelWidth - 20, y, 18, 18, C'70,70,70');

  y += 22;

  

  // TP設定(1行)

  CreateLabel(1, "TP:", 5, y, 20, 18, 7);

  CreateEdit(0, DoubleToString(take_profit, symbol_digits), 28, y, 70, 18);

  CreateButton(0, "設定", 102, y, 35, 18, clrGreen);

  CreateButton(1, "削除", 140, y, 35, 18, clrOrange);

  CreateButton(4, "Bid", 178, y, 30, 18, clrDarkBlue);

  y += 22;

  

  // SL設定(1行)

  CreateLabel(2, "SL:", 5, y, 20, 18, 7);

  CreateEdit(1, DoubleToString(stop_loss, symbol_digits), 28, y, 70, 18);

  CreateButton(2, "設定", 102, y, 35, 18, clrRed);

  CreateButton(3, "削除", 140, y, 35, 18, clrOrange);

  CreateButton(5, "Ask", 178, y, 30, 18, clrDarkGreen);

  y += 22;

  

  // 価格表示

  double bid = SymbolInfoDouble(current_symbol, SYMBOL_BID);

  double ask = SymbolInfoDouble(current_symbol, SYMBOL_ASK);

  string price_text = DoubleToString(bid, symbol_digits) + "/" + DoubleToString(ask, symbol_digits);

  CreateLabel(3, current_symbol + " " + price_text, 5, y, 140, 18, 7);

  CreateButton(6, "更新", 148, y, 30, 18, clrGray);

  y += 22;

  

  // ポジション数

  int pos_count = GetPositionCount();

  CreateLabel(4, "ポジ:" + IntegerToString(pos_count), 5, y, PanelWidth - 10, 18, 7);

}



//+------------------------------------------------------------------+

//| ラベル作成                            |

//+------------------------------------------------------------------+

void CreateLabel(int i, string txt, int x, int y, int w, int h, int fs)

{

  DestroyLabel(i);

  string name = "TpSlLabel" + IntegerToString(i);

  

  if(labels[i].Create(0, name, 0, panel_x + x, panel_y + y, panel_x + x + w, panel_y + y + h)) {

    labels[i].Text(txt);

    labels[i].Color(TextColor);

    labels[i].ColorBackground(clrNONE);

    labels[i].FontSize(fs);

    labels[i].Show();

  }

}



//+------------------------------------------------------------------+

//| ボタン作成                            |

//+------------------------------------------------------------------+

void CreateButton(int i, string txt, int x, int y, int w, int h, color bg)

{

  DestroyButton(i);

  string name = "TpSlButton" + IntegerToString(i);

  

  if(buttons[i].Create(0, name, 0, panel_x + x, panel_y + y, panel_x + x + w, panel_y + y + h)) {

    buttons[i].Text(txt);

    buttons[i].ColorBackground(bg);

    buttons[i].Color(clrWhite);

    buttons[i].FontSize(8);

    buttons[i].Show();

  }

}



//+------------------------------------------------------------------+

//| エディット作成                          |

//+------------------------------------------------------------------+

void CreateEdit(int i, string txt, int x, int y, int w, int h)

{

  DestroyEdit(i);

  string name = "TpSlEdit" + IntegerToString(i);

  

  if(edits[i].Create(0, name, 0, panel_x + x, panel_y + y, panel_x + x + w, panel_y + y + h)) {

    edits[i].Text(txt);

    edits[i].Color(clrBlack);

    edits[i].ColorBackground(clrWhite);

    edits[i].FontSize(8);

    edits[i].Show();

  }

}



//+------------------------------------------------------------------+

//| UI要素削除                            |

//+------------------------------------------------------------------+

void DestroyLabel(int i)

{

  if(labels[i].Id() > 0) labels[i].Destroy();

  string name = "TpSlLabel" + IntegerToString(i);

  if(ObjectFind(0, name) >= 0) ObjectDelete(0, name);

}



void DestroyButton(int i)

{

  if(buttons[i].Id() > 0) buttons[i].Destroy();

  string name = "TpSlButton" + IntegerToString(i);

  if(ObjectFind(0, name) >= 0) ObjectDelete(0, name);

}



void DestroyEdit(int i)

{

  if(edits[i].Id() > 0) edits[i].Destroy();

  string name = "TpSlEdit" + IntegerToString(i);

  if(ObjectFind(0, name) >= 0) ObjectDelete(0, name);

}



//+------------------------------------------------------------------+

//| 最小化/展開切り替え                       |

//+------------------------------------------------------------------+

void TogglePanel()

{

  minimized = !minimized;

  

  // UI破棄

  DestroyUI();

  

  // パネル再作成

  if(panel_main.Id() > 0) panel_main.Destroy();

  Sleep(100);

  

  if(!CreatePanel()) return;

  

  CreateUI();

  ChartRedraw();

}



//+------------------------------------------------------------------+

//| UI全体破棄                            |

//+------------------------------------------------------------------+

void DestroyUI()

{

  for(int i = 0; i < 10; i++) DestroyLabel(i);

  for(int i = 0; i < 8; i++) DestroyButton(i);

  for(int i = 0; i < 2; i++) DestroyEdit(i);

  ChartRedraw();

}



//+------------------------------------------------------------------+

//| ポジション数取得                         |

//+------------------------------------------------------------------+

int GetPositionCount()

{

  int count = 0;

  for(int i = 0; i < PositionsTotal(); i++) {

    if(PositionGetSymbol(i) == current_symbol) {

      count++;

    }

  }

  return count;

}



//+------------------------------------------------------------------+

//| TP/SL一括設定                          |

//+------------------------------------------------------------------+

void SetTPSL(double tp_value, double sl_value)

{

  ulong tickets[];

  int count = 0;

  

  // 現在の通貨ペアのポジションチケット収集

  for(int i = 0; i < PositionsTotal(); i++) {

    if(PositionGetSymbol(i) == current_symbol) {

      count++;

      ArrayResize(tickets, count);

      tickets[count - 1] = PositionGetInteger(POSITION_TICKET);

    }

  }

  

  // 各ポジションのTP/SL設定

  for(int i = 0; i < count; i++) {

    if(!PositionSelectByTicket(tickets[i])) continue;

    

    double current_tp = PositionGetDouble(POSITION_TP);

    double current_sl = PositionGetDouble(POSITION_SL);

    

    double new_tp = (tp_value == -1) ? current_tp : NormalizeDouble(tp_value, symbol_digits);

    double new_sl = (sl_value == -1) ? current_sl : NormalizeDouble(sl_value, symbol_digits);

    

    ModifyPosition(tickets[i], new_sl, new_tp);

    Sleep(50);

  }

  

  UpdateUI();

}



//+------------------------------------------------------------------+

//| ポジション変更                          |

//+------------------------------------------------------------------+

bool ModifyPosition(ulong ticket, double sl_value, double tp_value)

{

  MqlTradeRequest req;

  MqlTradeResult res;

  ZeroMemory(req);

  

  req.action = TRADE_ACTION_SLTP;

  req.position = ticket;

  req.symbol = current_symbol;

   req.sl = sl_value;

  req.tp = tp_value;

  

  return OrderSend(req, res) && res.retcode == TRADE_RETCODE_DONE;

}



//+------------------------------------------------------------------+

//| UI更新                             |

//+------------------------------------------------------------------+

void UpdateUI()

{

  if(minimized) return;

  

  // 価格表示更新

  if(labels[3].Id() > 0) {

    double bid = SymbolInfoDouble(current_symbol, SYMBOL_BID);

    double ask = SymbolInfoDouble(current_symbol, SYMBOL_ASK);

    string price_text = DoubleToString(bid, symbol_digits) + "/" + DoubleToString(ask, symbol_digits);

    labels[3].Text(current_symbol + " " + price_text);

  }

  

  // ポジション数更新

  if(labels[4].Id() > 0) {

    int pos_count = GetPositionCount();

    labels[4].Text("ポジ:" + IntegerToString(pos_count));

  }

}



//+------------------------------------------------------------------+

//| パネル移動処理                          |

//+------------------------------------------------------------------+

void MovePanel(int new_x, int new_y)

{

  // チャートの境界内に制限

  int chart_width = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);

  int chart_height = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);

  

  panel_x = MathMax(0, MathMin(new_x, chart_width - PanelWidth));

  panel_y = MathMax(0, MathMin(new_y, chart_height - (minimized ? 25 : PanelHeight)));

  

  // UI再作成

  DestroyUI();

  if(panel_main.Id() > 0) panel_main.Destroy();

  

  if(!CreatePanel()) return;

  CreateUI();

  ChartRedraw();

}



//+------------------------------------------------------------------+

//| ドラッグ開始判定                         |

//+------------------------------------------------------------------+

bool IsInDragArea(int x, int y)

{

  // タイトル部分をドラッグエリアとする

  int title_height = 22;

  return (x >= panel_x && x <= panel_x + PanelWidth - 40 &&

      y >= panel_y && y <= panel_y + title_height);

}



//+------------------------------------------------------------------+

//| チャートイベント処理                       |

//+------------------------------------------------------------------+

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)

{

  // マウス移動処理

  if(id == CHARTEVENT_MOUSE_MOVE) {

    int x = (int)lparam;

    int y = (int)dparam;

    long mouse_state = sparam;

    

    // マウスドラッグモード(マウス左ボタンが押されている場合)

    if((mouse_state & 1) && is_dragging) {

      // 前回位置からの移動量を計算

      int move_x = x - last_mouse_x;

      int move_y = y - last_mouse_y;

      

      // パネルを移動

      if(move_x != 0 || move_y != 0) {

        int new_x = panel_x + move_x;

        int new_y = panel_y + move_y;

        MovePanel(new_x, new_y);

      }

    }

    // クリックドラッグモード(左クリックでオン/オフ切り替え)

    else if(drag_mode) {

      // 前回位置からの移動量を計算

      int move_x = x - last_mouse_x;

      int move_y = y - last_mouse_y;

      

      // パネルを移動

      if(move_x != 0 || move_y != 0) {

        int new_x = panel_x + move_x;

        int new_y = panel_y + move_y;

        MovePanel(new_x, new_y);

      }

    }

    // マウスボタンが離されたらマウスドラッグ終了

    else if(!(mouse_state & 1)) {

      is_dragging = false;

    }

    

    last_mouse_x = x;

    last_mouse_y = y;

    return;

  }

  

  // 左クリック処理

  if(id == CHARTEVENT_CLICK) {

    int x = (int)lparam;

    int y = (int)dparam;

    

    if(IsInDragArea(x, y)) {

      // マウスドラッグモード開始

      is_dragging = true;

      drag_start_x = x;

      drag_start_y = y;

      last_mouse_x = x;

      last_mouse_y = y;

      

      // クリックドラッグモードの切り替え

      drag_mode = !drag_mode;

      

      if(drag_mode) {

        Print("クリックドラッグモード開始: ", x, ",", y);

        // タイトル表示を変更してドラッグモードを示す

        if(!minimized && labels[0].Id() > 0) {

          labels[0].Text("TP/SL設定 [移動中]");

          labels[0].Color(clrYellow);

        }

      } else {

        Print("クリックドラッグモード終了");

        // タイトル表示を元に戻す

        if(!minimized && labels[0].Id() > 0) {

          labels[0].Text("TP/SL設定");

          labels[0].Color(TextColor);

        }

      }

      return;

    } else {

      // ドラッグエリア外をクリックした場合、両方のモードを終了

      is_dragging = false;

      drag_mode = false;

      if(!minimized && labels[0].Id() > 0) {

        labels[0].Text("TP/SL設定");

        labels[0].Color(TextColor);

      }

    }

  }

  

  // オブジェクトクリック処理

  if(id == CHARTEVENT_OBJECT_CLICK) {

    // ボタンクリック時はドラッグを終了

    is_dragging = false;

    drag_mode = false;

    if(!minimized && labels[0].Id() > 0) {

      labels[0].Text("TP/SL設定");

      labels[0].Color(TextColor);

    }

    

    // ボタン状態リセット

    ObjectSetInteger(0, sparam, OBJPROP_STATE, false);

    

    // 最小化ボタン

    if(sparam == "TpSlButton7") {

      TogglePanel();

      return;

    }

    

    if(minimized) return;

    

    // TP設定ボタン

    if(sparam == "TpSlButton0") {

      take_profit = StringToDouble(edits[0].Text());

      SetTPSL(take_profit, -1);

    }

    // TP削除ボタン

    else if(sparam == "TpSlButton1") {

      SetTPSL(0, -1);

    }

    // SL設定ボタン

    else if(sparam == "TpSlButton2") {

      stop_loss = StringToDouble(edits[1].Text());

      SetTPSL(-1, stop_loss);

    }

    // SL削除ボタン

    else if(sparam == "TpSlButton3") {

      SetTPSL(-1, 0);

    }

    // Bid取得ボタン

    else if(sparam == "TpSlButton4") {

      double bid = SymbolInfoDouble(current_symbol, SYMBOL_BID);

      string price = DoubleToString(bid, symbol_digits);

      edits[0].Text(price);

    }

    // Ask取得ボタン

    else if(sparam == "TpSlButton5") {

      double ask = SymbolInfoDouble(current_symbol, SYMBOL_ASK);

      string price = DoubleToString(ask, symbol_digits);

      edits[1].Text(price);

    }

    // 更新ボタン

    else if(sparam == "TpSlButton6") {

      current_symbol = Symbol();

      symbol_digits = (int)SymbolInfoInteger(current_symbol, SYMBOL_DIGITS);

      

      // UI再作成

      DestroyUI();

      Sleep(50);

      CreateUI();

      ChartRedraw();

    }

  }

  

  // キーボード処理(ESCキーでドラッグ終了)

  if(id == CHARTEVENT_KEYDOWN) {

    if(lparam == 27) { // ESCキー

      is_dragging = false;

      drag_mode = false;

      if(!minimized && labels[0].Id() > 0) {

        labels[0].Text("TP/SL設定");

        labels[0].Color(TextColor);

      }

      Print("ドラッグモード終了(ESC)");

    }

  }

}



//+------------------------------------------------------------------+

//| Tick処理                            |

//+------------------------------------------------------------------+

void OnTick()

{

  static ulong last_update = 0;

  ulong current_time = GetTickCount();

  

  // 1秒間隔で更新

  if(current_time - last_update > 1000) {

    UpdateUI();

    last_update = current_time;

  }

}



//+------------------------------------------------------------------+

//| 終了処理                            |

//+------------------------------------------------------------------+

void OnDeinit(const int reason)

{

  DestroyUI();

  if(panel_main.Id() > 0) panel_main.Destroy();

}

複数のポジションの指値、逆指値一括に注文を指す(9/1確定)
・チャート内の移動が少し難しいですけどクリックで何とか動かせます。ESCで逃れられます。
・指値、逆指値に関しては問題なく利用可能

どちらも一旦自分的には問題ないので使ってますが使うときは自己責任でお願いします


まだAIと格闘中(8/3)
・8/4 ver2更新 できること以下の通り(不具合あり)
複数通貨ペアの管理: 開いているチャートの通貨ペアを自動収集
パネルの最小化/展開: コンパクト表示と詳細表示の切り替え
ポジション決済機能: 売り/買い、含み損/含み益、全決済など
TP/SL一括設定: Take ProfitとStop Lossの一括設定・削除
リアルタイム更新: 価格、スプレッド、ポジション情報の自動更新
コンボボックスの挙動が変

コードを見る
//+------------------------------------------------------------------+
//|                    MultiChart_TradingPanel.mq5                 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, Your Name"
#property version   "2.10"
#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, pos_cnt[5];
    double pos_pnl[5];
    bool active;
};

CPanel panel_main;
CLabel labels[50];
CButton buttons[20];
CEdit edits[2];
CComboBox symbol_combo;

SymbolData symbols[10];
int symbol_count=0, selected_symbol=0, panel_w, panel_h, expand_h, min_h;
bool minimized=false, force_update=false;
double take_profit=0.0, stop_loss=0.0;
ulong last_tick=0, min_interval;
int tick_count=0, update_count=0;

int OnInit()
{
    take_profit = DefaultTP; stop_loss = DefaultSL;
    min_interval = UltraFastMode ? (1000000/MaxUpdatesPerSecond) : 100000;
    force_update = true;
   
    CollectActiveSymbols();
    CalcSize();
   
    if(!panel_main.Create(0, "MultiTradingPanel", 0, PanelX, PanelY, PanelX+panel_w, PanelY+(minimized?min_h:expand_h)))
        return INIT_FAILED;
   
    panel_main.ColorBackground(PanelBG);
    panel_main.ColorBorder(PanelBorder);
    CreateUI();
    ChartRedraw();
    return INIT_SUCCEEDED;
}

void CollectActiveSymbols()
{
    symbol_count = 0;
    for(int i=0; i<10; i++) {
        symbols[i].name = "";
        symbols[i].active = false;
        ArrayInitialize(symbols[i].pos_cnt, 0);
        ArrayInitialize(symbols[i].pos_pnl, 0.0);
    }
   
    // まず全ての通貨ペアを収集
    string all_symbols[];
    int all_count = 0;
   
    // 現在のチャートシンボルを追加
    ArrayResize(all_symbols, all_count + 1);
    all_symbols[all_count] = Symbol();
    all_count++;
   
    // 開いているチャートから通貨ペアを収集
    long chart_id = ChartFirst();
    while(chart_id != -1 && all_count < MaxSymbols) {
        string chart_symbol = ChartSymbol(chart_id);
        if(chart_symbol != "" && chart_symbol != Symbol()) {
            bool exists = false;
            for(int j=0; j<all_count; j++) {
                if(all_symbols[j] == chart_symbol) {
                    exists = true;
                    break;
                }
            }
            if(!exists) {
                ArrayResize(all_symbols, all_count + 1);
                all_symbols[all_count] = chart_symbol;
                all_count++;
            }
        }
        chart_id = ChartNext(chart_id);
    }
   
    // ポジションデータを更新
    for(int i=0; i<all_count; i++) {
        AddTempSymbol(all_symbols[i], i);
        UpdateSymbolPositions(i);
    }
   
    // ポジションを持つ通貨ペアのみを最終リストに追加
    for(int i=0; i<all_count; i++) {
        if(symbols[i].pos_cnt[0] > 0) {
            AddFinalSymbol(symbols[i].name);
        }
    }
   
    // ポジションを持つ通貨ペアがない場合、現在のチャートシンボルを追加
    if(symbol_count == 0) {
        AddFinalSymbol(Symbol());
    }
   
    // 選択されたインデックスの調整
    if(selected_symbol >= symbol_count) selected_symbol = 0;
}

void AddTempSymbol(string sym, int index)
{
    symbols[index].name = sym;
    symbols[index].active = true;
    symbols[index].bid = SymbolInfoDouble(sym, SYMBOL_BID);
    symbols[index].ask = SymbolInfoDouble(sym, SYMBOL_ASK);
    symbols[index].spread = (int)SymbolInfoInteger(sym, SYMBOL_SPREAD);
}

void AddFinalSymbol(string sym)
{
    // 重複チェック
    for(int i=0; i<symbol_count; i++) {
        if(symbols[i].name == sym) return;
    }
   
    if(symbol_count >= MaxSymbols) return;
   
    // 一時データから最終データにコピー
    for(int i=0; i<10; i++) {
        if(symbols[i].name == sym) {
            // 位置を調整してコピー
            if(i != symbol_count) {
                symbols[symbol_count] = symbols[i];
            }
            symbols[symbol_count].active = true;
            symbol_count++;
            break;
        }
    }
}

void CalcSize()
{
    panel_w = minimized ? 250 : PanelWidth;
    if(minimized) {
        expand_h = min_h = 30;
    } else {
        int pos_lines = 0;
        if(ShowPositionInfo) {
            for(int i=0; i<symbol_count; i++) {
                if(symbols[i].active && symbols[i].pos_cnt[0] > 0) pos_lines++;
            }
            pos_lines = MathMax(1, pos_lines); // 最低1行("ポジションなし"または実際のポジション)
            pos_lines += 2; // ヘッダーとトータル行
        }
        expand_h = 80 + (ShowSpread?25:0) + 120 + 150 + (ShowPositionInfo ? pos_lines*25 : 0) + 200;
        min_h = MinimizedHeight;
    }
    panel_h = minimized ? min_h : expand_h;
}

void CreateUI()
{
    if(minimized) {
        CreateLabel(0, "MT Panel", 5, 8, 80, 18, 8);
        CreateLabel(1, GetMinimizedPositionInfo(), 90, 8, 140, 18, 8);
        CreateButton(10, "□", panel_w-25, 5, 20, 20, C'80,80,80');
        MoveComboOffScreen();
        return;
    }
   
    int y = 5;
    CreateLabel(0, "Multi-Chart Trading Panel", 10, y, panel_w-40, 20, 9);
    CreateButton(10, "-", panel_w-35, y, 30, 20, C'70,70,70');
    y += 30;
   
    // ポジションを持つ通貨ペアがある場合のみコンボボックスを表示
    if(symbol_count > 0) {
        CreateLabel(1, "ポジション通貨選択:", 10, y, 100, 20, 8);
        CreateCombo(y);
        y += 30;
       
        string sel_sym = symbols[selected_symbol].name;
        CreateLabel(2, "選択中: "+sel_sym, 10, y, 150, 20, 8);
        if(ShowSpread) CreateLabel(3, "Spread: "+IntegerToString(symbols[selected_symbol].spread)+" pts", 170, y, 120, 20, 8);
        y += 25;
       
        CreateButton(0, "全通貨ペア 全決済", 10, y, panel_w-20, 40, clrDarkRed);
        y += 50;
       
        CreateLabel(4, "【 "+sel_sym+" 操作 】", 10, y, panel_w-20, 20, 8);
        y += 25;
       
        int bw = (panel_w-35)/2;
        CreateButton(1, sel_sym+" 売り決済", 10, y, bw, 30, clrDeepPink);
        CreateButton(2, sel_sym+" 買い決済", 15+bw, y, bw, 30, clrBlue);
        y += 35;
        CreateButton(3, sel_sym+" 含み損決済", 10, y, bw, 30, clrMaroon);
        CreateButton(4, sel_sym+" 含み益決済", 15+bw, y, bw, 30, clrNavy);
        y += 40;
       
        CreateButton(5, sel_sym+" 全決済", 10, y, panel_w-20, 30, clrRed);
        y += 40;
       
        if(ShowPositionInfo) {
            y = CreatePositionDisplay(y);
            y += 10;
        }
       
        CreateLabel(30, "━━━ "+sel_sym+" TP/SL一括設定 ━━━", 10, y, panel_w-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));
        CreateLabel(31, price_text, 10, y, panel_w-20, 20, 8);
        y += 25;
       
        CreateLabel(32, "利確レート:", 10, y, 70, 20, 8);
        CreateEdit(0, DoubleToString(take_profit, GetDigits(sel_sym)), 85, y, 80, 20);
        CreateButton(6, "TP設定", 175, y, 50, 20, clrGreen);
        CreateButton(7, "TP削除", 235, y, 50, 20, clrOrange);
        y += 25;
       
        CreateLabel(33, "損切レート:", 10, y, 70, 20, 8);
        CreateEdit(1, DoubleToString(stop_loss, GetDigits(sel_sym)), 85, y, 80, 20);
        CreateButton(8, "SL設定", 175, y, 50, 20, clrRed);
        CreateButton(9, "SL削除", 235, y, 50, 20, clrOrange);
        y += 25;
       
        CreateButton(11, "Bid取得", 10, y, 70, 20, clrDarkBlue);
        CreateButton(12, "Ask取得", 85, y, 70, 20, clrDarkGreen);
        CreateButton(13, "更新", 170, y, 50, 20, clrGray);
    } else {
        // ポジションがない場合の表示
        CreateLabel(1, "現在ポジションがありません", 10, y, panel_w-20, 30, 10);
        y += 40;
        CreateButton(13, "更新", 10, y, 100, 30, clrGray);
    }
}

string GetMinimizedPositionInfo()
{
    int position_count = 0;
    string first_symbol = "";
   
    for(int i=0; i<symbol_count; i++) {
        if(symbols[i].active && symbols[i].pos_cnt[0] > 0) {
            if(first_symbol == "") first_symbol = symbols[i].name + "[" + IntegerToString(symbols[i].pos_cnt[0]) + "]";
            position_count++;
        }
    }
   
    if(position_count == 0) return "ポジションなし";
    if(position_count == 1) return first_symbol;
    return "複数通貨[" + IntegerToString(position_count) + "]";
}

int CreatePositionDisplay(int start_y)
{
    int y = start_y;
    CreateLabel(5, "━━━ ポジション保有通貨ペア ━━━", 10, y, panel_w-20, 20, 8);
    y += 25;
   
    double total_pnl = 0.0;
    int position_count = 0;
   
    for(int i=0; i<symbol_count; i++) {
        if(!symbols[i].active || symbols[i].pos_cnt[0] <= 0) continue;
       
        string info = symbols[i].name + " | ポジ:" + IntegerToString(symbols[i].pos_cnt[0]) +
                     " | 損益:" + DoubleToString(symbols[i].pos_pnl[0], 2);
       
        color text_color = (symbols[i].pos_pnl[0] > 0) ? clrLime :
                          (symbols[i].pos_pnl[0] < 0) ? clrRed : clrWhite;
       
        CreateColoredLabel(10+position_count, info, 10, y, panel_w-20, 20, 8, text_color);
        total_pnl += symbols[i].pos_pnl[0];
        y += 22;
        position_count++;
    }
   
    if(position_count == 0) {
        CreateLabel(10, "ポジションなし", 10, y, panel_w-20, 20, 8);
        y += 22;
    } else {
        string total_info = "━━━ トータル損益: " + DoubleToString(total_pnl, 2) + " ━━━";
        color total_color = (total_pnl > 0) ? clrLime : (total_pnl < 0) ? clrRed : clrWhite;
        CreateColoredLabel(40, total_info, 10, y, panel_w-20, 20, 9, total_color);
        y += 25;
    }
   
    return y;
}

void MoveComboOffScreen()
{
    if(symbol_combo.Id()>0) {
        ObjectSetInteger(0, "SymbolCombo", OBJPROP_XDISTANCE, -1000);
        ObjectSetInteger(0, "SymbolCombo", OBJPROP_YDISTANCE, -1000);
    }
}

void CreateCombo(int y)
{
    if(minimized) return;
   
    if(symbol_combo.Id()>0) {
        symbol_combo.Hide();
        symbol_combo.Destroy();
    }
   
    string combo_name = "SymbolCombo";
    if(ObjectFind(0, combo_name) >= 0) ObjectDelete(0, combo_name);
   
    ChartRedraw();
    Sleep(50); // 短い待機時間を追加
   
    if(symbol_combo.Create(0, combo_name, 0, PanelX+100, PanelY+y, PanelX+290, PanelY+y+25)) {
        // ポジションを持つ通貨ペアのみをコンボボックスに追加
        for(int i=0; i<symbol_count; i++) {
            if(symbols[i].active) {
                string display_name = symbols[i].name + " [" + IntegerToString(symbols[i].pos_cnt[0]) + "]";
                symbol_combo.ItemAdd(display_name);
                Print("コンボボックスに追加: ", display_name);
            }
        }
       
        // 現在の選択を設定
        if(selected_symbol >= 0 && selected_symbol < symbol_count) {
            symbol_combo.Select(selected_symbol);
            Print("コンボボックス選択設定: ", selected_symbol, " - ", symbols[selected_symbol].name);
        }
       
        symbol_combo.Show();
        Print("コンボボックス作成完了 - 通貨ペア数: ", symbol_count);
    } else {
        Print("コンボボックス作成失敗");
    }
}

int GetDigits(string sym) { return (int)SymbolInfoInteger(sym, SYMBOL_DIGITS); }

void CreateLabel(int i, string txt, int x, int y, int w, int h, int fs)
{
    DestroyLabel(i);
    string n = "Label"+IntegerToString(i);
   
    if(labels[i].Create(0, n, 0, PanelX+x, PanelY+y, PanelX+x+w, PanelY+y+h)) {
        labels[i].Text(txt);
        labels[i].Color(TextColor);
        labels[i].ColorBackground(PanelBG);
        labels[i].FontSize(fs);
        labels[i].Show();
    }
}

void CreateColoredLabel(int i, string txt, int x, int y, int w, int h, int fs, color text_color)
{
    DestroyLabel(i);
    string n = "Label"+IntegerToString(i);
   
    if(labels[i].Create(0, n, 0, PanelX+x, PanelY+y, PanelX+x+w, PanelY+y+h)) {
        labels[i].Text(txt);
        labels[i].Color(text_color);
        labels[i].ColorBackground(PanelBG);
        labels[i].FontSize(fs);
        labels[i].Show();
    }
}

void CreateButton(int i, string txt, int x, int y, int w, int h, color bg)
{
    DestroyButton(i);
    string n = "Button"+IntegerToString(i);
   
    if(buttons[i].Create(0, n, 0, PanelX+x, PanelY+y, PanelX+x+w, PanelY+y+h)) {
        buttons[i].Text(txt);
        buttons[i].ColorBackground(bg);
        buttons[i].Color(clrWhite);
        buttons[i].FontSize(8);
        buttons[i].Show();
    }
}

void CreateEdit(int i, string txt, int x, int y, int w, int h)
{
    DestroyEdit(i);
    string n = "Edit"+IntegerToString(i);
   
    if(edits[i].Create(0, n, 0, PanelX+x, PanelY+y, PanelX+x+w, PanelY+y+h)) {
        edits[i].Text(txt);
        edits[i].Color(clrBlack);
        edits[i].ColorBackground(clrWhite);
        edits[i].FontSize(8);
        edits[i].Show();
    }
}

void DestroyLabel(int i)
{
    if(labels[i].Id()>0) labels[i].Destroy();
    string n = "Label"+IntegerToString(i);
    if(ObjectFind(0,n)>=0) ObjectDelete(0,n);
}

void DestroyButton(int i)
{
    if(buttons[i].Id()>0) buttons[i].Destroy();
    string n = "Button"+IntegerToString(i);
    if(ObjectFind(0,n)>=0) ObjectDelete(0,n);
}

void DestroyEdit(int i)
{
    if(edits[i].Id()>0) edits[i].Destroy();
    string n = "Edit"+IntegerToString(i);
    if(ObjectFind(0,n)>=0) ObjectDelete(0,n);
}

void Toggle()
{
    minimized = !minimized;
   
    DestroyUI();
   
    if(panel_main.Id()>0) panel_main.Destroy();
    Sleep(100);
   
    CalcSize();
   
    if(panel_main.Create(0, "MultiTradingPanel", 0, PanelX, PanelY, PanelX+panel_w, PanelY+(minimized?min_h:expand_h))) {
        panel_main.ColorBackground(PanelBG);
        panel_main.ColorBorder(PanelBorder);
        panel_main.Show();
       
        Sleep(50);
        CreateUI();
    }
   
    force_update = true;
    ChartRedraw();
}

void DestroyUI()
{
    if(symbol_combo.Id()>0) {
        symbol_combo.Hide();
        symbol_combo.Destroy();
    }
   
    for(int i=0; i<50; i++) DestroyLabel(i);
    for(int i=0; i<20; i++) DestroyButton(i);
    for(int i=0; i<2; i++) DestroyEdit(i);
   
    if(ObjectFind(0, "SymbolCombo") >= 0) ObjectDelete(0, "SymbolCombo");
    if(ObjectFind(0, "SymbolCombo_retry") >= 0) ObjectDelete(0, "SymbolCombo_retry");
   
    ChartRedraw();
}

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)
{
    ArrayInitialize(symbols[sym_idx].pos_cnt, 0);
    ArrayInitialize(symbols[sym_idx].pos_pnl, 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;
        }
    }
}

void UpdateUI()
{
    if(selected_symbol >= symbol_count) return;
    string sel_sym = symbols[selected_symbol].name;
   
    if(minimized) {
        if(labels[1].Id()>0) labels[1].Text(GetMinimizedPositionInfo());
        return;
    }
   
    if(ShowSpread && labels[3].Id()>0)
        labels[3].Text("Spread: "+IntegerToString(symbols[selected_symbol].spread)+" pts");
   
    if(labels[31].Id()>0) {
        string price_text = "Bid: "+DoubleToString(symbols[selected_symbol].bid, GetDigits(sel_sym))+
                           " Ask: "+DoubleToString(symbols[selected_symbol].ask, GetDigits(sel_sym));
        labels[31].Text(price_text);
    }
   
    if(ShowPositionInfo) UpdatePositionDisplay();
}

void UpdatePositionDisplay()
{
    double total_pnl = 0.0;
    int position_count = 0;
   
    for(int i=0; i<symbol_count; i++) {
        if(!symbols[i].active || symbols[i].pos_cnt[0] <= 0) continue;
       
        if(labels[10+position_count].Id()>0) {
            string info = symbols[i].name + " | ポジ:" + IntegerToString(symbols[i].pos_cnt[0]) +
                         " | 損益:" + DoubleToString(symbols[i].pos_pnl[0], 2);
           
            color text_color = (symbols[i].pos_pnl[0] > 0) ? clrLime :
                              (symbols[i].pos_pnl[0] < 0) ? clrRed : clrWhite;
           
            labels[10+position_count].Text(info);
            labels[10+position_count].Color(text_color);
           
            total_pnl += symbols[i].pos_pnl[0];
            position_count++;
        }
    }
   
    if(position_count > 0 && labels[40].Id()>0) {
        string total_info = "━━━ トータル損益: " + DoubleToString(total_pnl, 2) + " ━━━";
        color total_color = (total_pnl > 0) ? clrLime : (total_pnl < 0) ? clrRed : clrWhite;
       
        labels[40].Text(total_info);
        labels[40].Color(total_color);
    }
}

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
    Print("ChartEvent: ID=", id, " sparam=", sparam);
   
    // コンボボックスの選択変更イベントを処理
    if(StringFind(sparam, "SymbolCombo") >= 0) {
        Print("コンボボックスイベント検出: ", sparam, " イベントID: ", id);
       
        if(id == CHARTEVENT_OBJECT_ENDEDIT || id == CHARTEVENT_OBJECT_CHANGE) {
            // より確実な選択値取得方法を使用
            int new_selection = -1;
           
            // 複数の方法で選択値を取得
            if(symbol_combo.Id() > 0) {
                new_selection = symbol_combo.Value();
                Print("コンボボックスから取得した選択値: ", new_selection);
            }
           
            // オブジェクトプロパティからも取得を試行
            if(new_selection < 0) {
                new_selection = (int)ObjectGetInteger(0, sparam, OBJPROP_SELECTED);
                Print("オブジェクトプロパティから取得した選択値: ", new_selection);
            }
           
            Print("最終選択値: ", new_selection, " 現在の選択: ", selected_symbol, " 最大値: ", symbol_count-1);
           
            if(new_selection >= 0 && new_selection < symbol_count && new_selection != selected_symbol) {
                selected_symbol = new_selection;
                Print("通貨ペア変更成功: ", symbols[selected_symbol].name);
               
                RefreshUI();
                force_update = true;
            } else {
                Print("通貨ペア変更失敗 - 範囲外または同じ選択");
            }
        }
        return;
    }
   
    // ボタンクリックイベント
    if(id == CHARTEVENT_OBJECT_CLICK) {
        Print("ボタンクリック: ", sparam);
       
        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;
            }
        }
       
        if(sparam == "Button6") { take_profit = StringToDouble(edits[0].Text()); SetTPSL(sel_sym, take_profit, -1); }
        else if(sparam == "Button8") { stop_loss = StringToDouble(edits[1].Text()); SetTPSL(sel_sym, -1, stop_loss); }
        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));
            edits[0].Text(price); edits[1].Text(price);
        }
        else if(sparam == "Button12") {
            string price = DoubleToString(symbols[selected_symbol].ask, GetDigits(sel_sym));
            edits[0].Text(price); edits[1].Text(price);
        }
        else if(sparam == "Button13") {
            Print("更新ボタンクリック - 通貨ペアを再収集");
            CollectActiveSymbols();
            if(selected_symbol >= symbol_count) selected_symbol = 0;
           
            // UI全体を再作成
            DestroyUI();
            Sleep(100);
            CreateUI();
            force_update = true;
            Print("更新完了 - 新しい通貨ペア数: ", symbol_count);
        }
    }
}

void CloseAllSymbols()
{
    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);
            ClosePos(ticket, pos_sym);
        }
    }
}

void SetTPSL(string sym, double tp_value, double sl_value)
{
    int digits = GetDigits(sym);
    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])) continue;
       
        double cur_tp = PositionGetDouble(POSITION_TP);
        double cur_sl = PositionGetDouble(POSITION_SL);
       
        double new_tp = (tp_value==-1) ? cur_tp : ((tp_value<=0) ? 0 : NormalizeDouble(tp_value, digits));
        double new_sl = (sl_value==-1) ? cur_sl : ((sl_value<=0) ? 0 : NormalizeDouble(sl_value, digits));
       
        if(MathAbs(new_tp-cur_tp)<Point() && MathAbs(new_sl-cur_sl)<Point()) continue;
       
        ModifyPos(tickets[i], sym, new_sl, new_tp);
        Sleep(50);
    }
    force_update = true;
}

bool ModifyPos(ulong ticket, string sym, double sl_value, double tp_value)
{
    MqlTradeRequest req; MqlTradeResult res;
    ZeroMemory(req);
    req.action = TRADE_ACTION_SLTP;
    req.position = ticket;
    req.symbol = sym;
    req.sl = sl_value;
    req.tp = tp_value;
    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 RefreshUI()
{
    if(minimized) {
        if(labels[1].Id()>0) labels[1].Text(GetMinimizedPositionInfo());
        return;
    }
   
    string sel_sym = symbols[selected_symbol].name;
   
    // 選択中通貨ペア表示を更新
    if(labels[2].Id()>0) labels[2].Text("選択中: "+sel_sym);
   
    // 操作セクションのタイトル更新
    if(labels[4].Id()>0) labels[4].Text("【 "+sel_sym+" 操作 】");
   
    // ボタンテキスト更新
    if(buttons[1].Id()>0) buttons[1].Text(sel_sym+" 売り決済");
    if(buttons[2].Id()>0) buttons[2].Text(sel_sym+" 買い決済");
    if(buttons[3].Id()>0) buttons[3].Text(sel_sym+" 含み損決済");
    if(buttons[4].Id()>0) buttons[4].Text(sel_sym+" 含み益決済");
    if(buttons[5].Id()>0) buttons[5].Text(sel_sym+" 全決済");
   
    // TP/SLセクションのタイトル更新
    if(labels[30].Id()>0) labels[30].Text("━━━ "+sel_sym+" TP/SL一括設定 ━━━");
   
    // エディットボックスの値を更新
    if(edits[0].Id()>0) edits[0].Text(DoubleToString(take_profit, GetDigits(sel_sym)));
    if(edits[1].Id()>0) edits[1].Text(DoubleToString(stop_loss, GetDigits(sel_sym)));
   
    // 価格表示も即座に更新
    if(labels[31].Id()>0) {
        string price_text = "Bid: "+DoubleToString(symbols[selected_symbol].bid, GetDigits(sel_sym))+
                           " Ask: "+DoubleToString(symbols[selected_symbol].ask, GetDigits(sel_sym));
        labels[31].Text(price_text);
    }
   
    // スプレッド表示更新
    if(ShowSpread && labels[3].Id()>0) {
        labels[3].Text("Spread: "+IntegerToString(symbols[selected_symbol].spread)+" pts");
    }
   
    // ポジション情報更新
    if(ShowPositionInfo) UpdatePositionDisplay();
   
    ChartRedraw();
}

void OnDeinit(const int reason)
{
    DestroyUI();
    panel_main.Destroy();
}

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

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

さらに損益分岐点を出してくれるインディケータも作成(シンプル)

コードを見る
//+------------------------------------------------------------------+

//| BreakEvenPointLine.mq5                      |

//| Copyright 2023, Taisuke Shimada                 |

//| https://example.com                       |

//+------------------------------------------------------------------+

#property copyright "Copyright 2023, Taisuke Shimada"

#property link   "https://example.com"

#property version  "1.00"

#property indicator_chart_window

#property indicator_buffers 0

#property indicator_plots  0



//--- ユーザー入力

input color      InpLineColor = clrDodgerBlue;   // ラインの色

input ENUM_LINE_STYLE InpLineStyle = STYLE_DASHDOT;   // ラインのスタイル

input int       InpLineWidth = 2;         // ラインの太さ

input bool      InpShowLabel = true;        // 価格ラベル表示



//--- グローバル変数

string g_line_name;                   // ラインオブジェクト名

const double EPSILON = 1e-8;               // 微小誤差許容値



//+------------------------------------------------------------------+

//| 初期化関数                            |

//+------------------------------------------------------------------+

int OnInit()

{

  g_line_name = "BEP_Line_" + _Symbol + "_" + IntegerToString(ChartID());

  return INIT_SUCCEEDED;

}



//+------------------------------------------------------------------+

//| 終了時処理(ライン削除)                     |

//+------------------------------------------------------------------+

void OnDeinit(const int reason)

{

  ObjectDelete(0, g_line_name);

}



//+------------------------------------------------------------------+

//| メイン計算関数                          |

//+------------------------------------------------------------------+

int OnCalculate(const int rates_total,

        const int prev_calculated,

        const datetime &time[],

        const double &open[],

        const double &high[],

        const double &low[],

        const double &close[],

        const long &tick_volume[],

        const long &volume[],

        const int &spread[])

{

  double total_weighted_price = 0.0;

  double total_net_lots    = 0.0;

  int  position_count    = 0;



  double contract_size = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE);

  if (contract_size <= 0)

  {

   Print("契約サイズの取得に失敗: ", _Symbol);

   return rates_total;

  }



  //--- ポジションのループ

  for (int i = PositionsTotal() - 1; i >= 0; i--)

  {

   ulong ticket = PositionGetTicket(i);

   if (!PositionSelectByTicket(ticket))

     continue;



   if (PositionGetString(POSITION_SYMBOL) != _Symbol)

     continue;



   double lots    = PositionGetDouble(POSITION_VOLUME);

   double open_price = PositionGetDouble(POSITION_PRICE_OPEN);

   long  type    = PositionGetInteger(POSITION_TYPE);

   double commission = PositionGetDouble(POSITION_COMMISSION);

   double swap    = PositionGetDouble(POSITION_SWAP);



   int direction = (type == POSITION_TYPE_BUY) ? 1 : -1;



   total_weighted_price += (open_price * lots * direction) + ((commission + swap) / contract_size);

   total_net_lots    += lots * direction;

   position_count++;

  }



  //--- ラインの描画・削除判定

  if (position_count == 0)

  {

   ObjectDelete(0, g_line_name);

   Comment("ポジションがありません。");

   return rates_total;

  }



  if (MathAbs(total_net_lots) < EPSILON)

  {

   ObjectDelete(0, g_line_name);

   Comment("両建て状態のため、損益分岐点を計算できません。");

   return rates_total;

  }



  //--- 損益分岐点の計算

  double bep_price = total_weighted_price / total_net_lots;



  //--- ラインが存在しない場合は作成

  if (ObjectFind(0, g_line_name) < 0)

  {

   ObjectCreate(0, g_line_name, OBJ_HLINE, 0, 0, 0);

   ObjectSetInteger(0, g_line_name, OBJPROP_SELECTABLE, false);

   ObjectSetInteger(0, g_line_name, OBJPROP_COLOR,   InpLineColor);

   ObjectSetInteger(0, g_line_name, OBJPROP_STYLE,   InpLineStyle);

   ObjectSetInteger(0, g_line_name, OBJPROP_WIDTH,   InpLineWidth);

  }



  //--- ライン位置とテキスト

  ObjectSetDouble(0, g_line_name, OBJPROP_PRICE, 0, bep_price);



  if (InpShowLabel)

   ObjectSetString(0, g_line_name, OBJPROP_TEXT, "BEP: " + DoubleToString(bep_price, _Digits));

  else

   ObjectSetString(0, g_line_name, OBJPROP_TEXT, "");



  Comment(""); // コメントクリア

  return rates_total;

}

//+------------------------------------------------------------------+

シンプルで見やすくていいですね~
これくらいならAIですぐ作成できるのは楽しいです。
追加するなら損益くらいですかね~

コメント

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