8/5 もっとシンプルにするように検討中
mt5にて複数のポジションを指値できるやつ
コード
//+------------------------------------------------------------------+
//| 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にて損益分岐点のラインを引いて損益もちょっとでるインジケーター
コード
//+------------------------------------------------------------------+
//| BreakEvenPointLine_Improved.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 InpShowBEPLabel = true;
// ラベル表示設定
input bool InpShowPipsLabel = true;
input bool InpShowProfit = true; // 損益表示のオン/オフ
input bool InpMovableLabel = true; // ラベルを手動移動可能にする
input int InpLabelCorner = 3; // 角の位置 (0:左上, 1:左下, 2:右下, 3:右上)
input int InpXDistance = 15; // 右端からの距離
input int InpYDistance = 30; // 上端からの距離
input int InpFontSize = 14; // フォントサイズ
input string InpFontName = "Arial"; // フォント名
input bool InpBoldFont = false; // 太字設定
input color InpProfitColor = clrLime; // 利益時の色
input color InpLossColor = clrRed; // 損失時の色
// グローバル変数
string g_line_name;
string g_pips_label;
const double EPSILON = 1e-8;
int OnInit()
{
g_line_name = "BEP_Line_" + _Symbol + "_" + IntegerToString(ChartID());
g_pips_label = "PipsLabel_" + _Symbol + "_" + IntegerToString(ChartID());
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
ObjectDelete(0, g_line_name);
ObjectDelete(0, g_pips_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_pips = 0.0;
double total_profit = 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 = 0.0; // 手数料(必要に応じて設定)
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_size = 0.0001; // デフォルト値
// JPY通貨ペアの場合は0.01がpip
if (StringFind(_Symbol, "JPY") != -1)
pip_size = 0.01;
double pips = diff / pip_size;
total_pips += pips * lots; // ロット数で重み付け
// 損益を加算
total_profit += PositionGetDouble(POSITION_PROFIT);
position_count++;
}
// ポジションがない場合の処理
if (position_count == 0)
{
ObjectDelete(0, g_line_name);
ObjectDelete(0, g_pips_label);
Comment("ポジションがありません。");
return rates_total;
}
// 両建て状態での処理
if (MathAbs(total_net_lots) < EPSILON)
{
ObjectDelete(0, g_line_name);
if (InpShowPipsLabel)
{
CreatePipsLabel();
// 平均pips
double avg_pips = total_pips / position_count;
UpdatePipsLabel(avg_pips, total_profit);
}
Comment("両建て状態でBEP算出不可。");
return rates_total;
}
// 損益分岐点の計算
double bep_price = total_weighted_price / total_net_lots;
// BEPライン作成・更新
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); // BEPラインは固定
ObjectSetInteger(0, g_line_name, OBJPROP_COLOR, InpLineColor);
ObjectSetInteger(0, g_line_name, OBJPROP_STYLE, InpLineStyle);
ObjectSetInteger(0, g_line_name, OBJPROP_WIDTH, InpLineWidth);
ObjectSetInteger(0, g_line_name, OBJPROP_BACK, false);
}
ObjectSetDouble(0, g_line_name, OBJPROP_PRICE, 0, bep_price);
if (InpShowBEPLabel)
{
ObjectSetString(0, g_line_name, OBJPROP_TEXT, "BEP: " + DoubleToString(bep_price, _Digits));
}
else
{
ObjectSetString(0, g_line_name, OBJPROP_TEXT, "");
}
// Pipsラベル作成・更新
if (InpShowPipsLabel)
{
CreatePipsLabel();
// 総ロット数で重み付けした平均pips
double weighted_avg_pips = total_pips / total_net_lots;
UpdatePipsLabel(weighted_avg_pips, total_profit);
}
else
{
ObjectDelete(0, g_pips_label);
}
return rates_total;
}
void CreatePipsLabel()
{
if (ObjectFind(0, g_pips_label) < 0)
{
ObjectCreate(0, g_pips_label, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, g_pips_label, OBJPROP_CORNER, InpLabelCorner);
ObjectSetInteger(0, g_pips_label, OBJPROP_XDISTANCE, InpXDistance);
ObjectSetInteger(0, g_pips_label, OBJPROP_YDISTANCE, InpYDistance);
ObjectSetInteger(0, g_pips_label, OBJPROP_FONTSIZE, InpFontSize);
ObjectSetInteger(0, g_pips_label, OBJPROP_SELECTABLE, InpMovableLabel); // 移動可能設定
ObjectSetInteger(0, g_pips_label, OBJPROP_BACK, false);
// フォント設定
string font_name = InpFontName;
if (InpBoldFont)
{
font_name = InpFontName + " Bold";
}
ObjectSetString(0, g_pips_label, OBJPROP_FONT, font_name);
}
else
{
// 既存オブジェクトの移動可能設定を更新
ObjectSetInteger(0, g_pips_label, OBJPROP_SELECTABLE, InpMovableLabel);
}
}
void UpdatePipsLabel(double pips, double profit)
{
// 色の設定(損益に基づいて判定)
color label_color = (profit >= 0) ? InpProfitColor : InpLossColor;
ObjectSetInteger(0, g_pips_label, OBJPROP_COLOR, label_color);
// テキスト設定
string display_text = "";
if (InpShowProfit)
{
// Pipsと損益両方表示
display_text = StringFormat("%+.1f pips / %+.2f", pips, profit);
}
else
{
// Pipsのみ表示
display_text = StringFormat("%+.1f pips", pips);
}
ObjectSetString(0, g_pips_label, OBJPROP_TEXT, display_text);
}
ポジが最近多くなってきて一個ずつ指値とか出すの面倒だな~って思ってAIが昔より進化したのでサクッと作って貰いました。
私は全くプログラムとかできないんで細かいことは分かりませんができました。
まだちゃんと機能しない部分もあるんですけどもねww
一応乗っけておきますけど(自己責任でお願いします)ver1としておきます。
まだ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ですぐ作成できるのは楽しいです。
追加するなら損益くらいですかね~
コメント