Skip to content

Instantly share code, notes, and snippets.

@paibamboo
Created October 6, 2025 01:06
Show Gist options
  • Select an option

  • Save paibamboo/b8190e13d1735f2f09548c9c41d7b927 to your computer and use it in GitHub Desktop.

Select an option

Save paibamboo/b8190e13d1735f2f09548c9c41d7b927 to your computer and use it in GitHub Desktop.
MyCustomTradePanel
//+------------------------------------------------------------------+
//| TradePanel.mq5 |
//| Copyright 2023, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
#property strict
#include <Controls/Dialog.mqh>
#include <Controls/SpinEdit.mqh>
#include <Controls/Button.mqh>
#include <Controls/Label.mqh>
#include <Controls/RadioGroup.mqh>
#include <Controls/CheckGroup.mqh>
#include "../Include/Utils.mqh";
input int MaxOpenTrades = 999;
input int MaxTradesPerDay = 999;
CAppDialog TradePanel;
CButton placeOrderButton;
CLabel slLabel, tpLabel, riskLabel, rrLabel, spreadLabel, maxOpenTradesLabel, maxTradesPerDayLabel, maxOrderSizeLabel, currentTimeLabel, lotSizeLabel;
CEdit lotEdit, slEdit, tpEdit, riskEdit, rrEdit, spreadEdit, preferredRREdit;
CRadioGroup radioGroup;
CCheckBox autoPlaceOrderCheckbox;
CCheckGroup checkboxGroup;
int numChartClickedAfterSlTpEdit = 0;
string editValue = "";
bool isSlEditLastClicked = false;
bool isTpEditLastClicked = false;
double defaultSl = 0;
double defaultTp = 0;
double defaultRiskAmount = 50;
bool defaultAutoPlaceOrder = false;
double defaultPreferredRR = 3.5;
int OnInit()
{
if (GlobalVariableCheck(_Symbol + "_SL") && GlobalVariableGet(_Symbol + "_SL") != 0)
{
defaultSl = GlobalVariableGet(_Symbol + "_SL");
}
if (GlobalVariableCheck(_Symbol + "_TP") && GlobalVariableGet(_Symbol + "_TP") != 0)
{
defaultTp = GlobalVariableGet(_Symbol + "_TP");
}
if (GlobalVariableCheck(_Symbol + "_RISK") && GlobalVariableGet(_Symbol + "_RISK") != 0)
{
defaultRiskAmount = GlobalVariableGet(_Symbol + "_RISK");
}
if (GlobalVariableCheck(_Symbol + "_AUTO_PLACE_ORDER") && GlobalVariableGet(_Symbol + "_AUTO_PLACE_ORDER") == 1)
{
defaultAutoPlaceOrder = true;
}
if (GlobalVariableCheck(_Symbol + "_PREFERRED_RR") && GlobalVariableGet(_Symbol + "_PREFERRED_RR") != 0)
{
defaultPreferredRR = GlobalVariableGet(_Symbol + "_PREFERRED_RR");
}
TradePanel.Create(0, "Trade Panel", 0, 0, 0, 380, 185);
radioGroup.Create(0, "radioGroup", 0, 0, 0, 90, 40);
radioGroup.ColorBackground(clrWhite);
radioGroup.ColorBorder(clrBlack);
radioGroup.AddItem("Buy", 0);
radioGroup.AddItem("Sell", 1);
slLabel.Create(0, "slLabel", 0, 100, 8, 130, 8);
slLabel.Text("SL");
slLabel.FontSize(12);
slEdit.Create(0, "slEdit", 0, 130, 8, 220, 32);
slEdit.FontSize(12);
slEdit.TextAlign(ALIGN_CENTER);
slEdit.Text(defaultSl); // have to implicitly convert to string
tpLabel.Create(0, "tpLabel", 0, 230, 8, 260, 8);
tpLabel.Text("TP");
tpLabel.FontSize(12);
tpEdit.Create(0, "tpEdit", 0, 260, 8, 350, 32);
tpEdit.FontSize(12);
tpEdit.TextAlign(ALIGN_CENTER);
tpEdit.Text(defaultTp); // have to implicitly convert to string
riskLabel.Create(0, "riskLabel", 0, 55, 50, 50, 50);
riskLabel.Text("Risk Amt");
riskLabel.FontSize(12);
riskEdit.Create(0, "riskEdit", 0, 130, 50, 220, 74);
riskEdit.FontSize(12);
riskEdit.TextAlign(ALIGN_CENTER);
riskEdit.Text(defaultRiskAmount); // have to implicitly convert to string
rrLabel.Create(0, "rrLabel", 0, 230, 50, 260, 80);
rrLabel.Text("RR");
rrLabel.FontSize(12);
rrEdit.Create(0, "rrEdit", 0, 260, 50, 350, 74);
rrEdit.FontSize(12);
rrEdit.TextAlign(ALIGN_CENTER);
rrEdit.ReadOnly(true);
rrEdit.ColorBackground(clrGray);
rrEdit.Color(clrWhite);
spreadLabel.Create(0, "spreadLabel", 0, 65, 90, 85, 90);
spreadLabel.Text("Spread");
spreadLabel.FontSize(12);
spreadEdit.Create(0, "spreadEdit", 0, 130, 90, 220, 114);
spreadEdit.FontSize(12);
spreadEdit.TextAlign(ALIGN_CENTER);
spreadEdit.ReadOnly(true);
spreadEdit.ColorBackground(clrGray);
spreadEdit.Color(clrWhite);
placeOrderButton.Create(0, "placeOrderButton", 0, 260, 90, 350, 114);
placeOrderButton.Text("PLACE ORDER");
autoPlaceOrderCheckbox.Create(0, "autoPlaceOrderCheckbox", 0, 40, 128, 260, 148);
autoPlaceOrderCheckbox.Text("Auto place order when RR >=");
autoPlaceOrderCheckbox.Checked(defaultAutoPlaceOrder);
preferredRREdit.Create(0, "preferredRREdit", 0, 260, 125, 350, 149);
preferredRREdit.FontSize(12);
preferredRREdit.TextAlign(ALIGN_CENTER);
preferredRREdit.Text(defaultPreferredRR); // have to implicitly convert to string
TradePanel.Add(radioGroup);
TradePanel.Add(slLabel);
TradePanel.Add(slEdit);
TradePanel.Add(tpLabel);
TradePanel.Add(tpEdit);
TradePanel.Add(riskLabel);
TradePanel.Add(riskEdit);
TradePanel.Add(rrLabel);
TradePanel.Add(rrEdit);
TradePanel.Add(spreadLabel);
TradePanel.Add(spreadEdit);
TradePanel.Add(placeOrderButton);
TradePanel.Add(autoPlaceOrderCheckbox);
TradePanel.Add(preferredRREdit);
EventSetMillisecondTimer(200);
TradePanel.Run();
ChartRedraw();
maxOpenTradesLabel.Create(0, "Max Open Trades Label", 0, 400, 0, 500, 10);
maxOpenTradesLabel.FontSize(10);
maxOpenTradesLabel.Text("Max Open Trades = " + IntegerToString(MaxOpenTrades) + ", Current Open Trades = " + IntegerToString(PositionsTotal()));
maxTradesPerDayLabel.Create(0, "Max Trades Per Day Label", 0, 400, 20, 500, 30);
maxTradesPerDayLabel.FontSize(10);
maxTradesPerDayLabel.Text("Max Trades Per Day = " + IntegerToString(MaxTradesPerDay) + ", Current Trades Per Day = " + IntegerToString(GetCurrentTradesPerDay()));
maxOrderSizeLabel.Create(0, "Max Order Size Label", 0, 400, 40, 500, 10);
maxOrderSizeLabel.FontSize(10);
maxOrderSizeLabel.Text("Max Order Size = " + DoubleToString(SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX), 2));
currentTimeLabel.Create(0, "Current Time Label", 0, 400, 60, 500, 60);
currentTimeLabel.FontSize(10);
currentTimeLabel.Text(TimeTradeServer());
lotSizeLabel.Create(0, "Lot Size Label", 0, 400, 80, 500, 60);
lotSizeLabel.FontSize(10);
lotSizeLabel.Text("Lots:");
return(INIT_SUCCEEDED);
}
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
TradePanel.ChartEvent(id, lparam, dparam, sparam);
if (sparam == "slEdit")
{
defaultSl = (double)slEdit.Text();
}
else if (sparam == "tpEdit")
{
defaultTp = (double)tpEdit.Text();
}
else if (sparam == "riskEdit")
{
defaultRiskAmount = (double)riskEdit.Text();
}
else if (sparam == "preferredRREdit")
{
defaultPreferredRR = (double)preferredRREdit.Text();
}
else if (sparam == "autoPlaceOrderCheckbox")
{
defaultAutoPlaceOrder = autoPlaceOrderCheckbox.Checked();
}
if (id == CHARTEVENT_OBJECT_CLICK)
{
if (sparam == "placeOrderButton")
{
PlaceOrder();
}
else if (sparam == "slEdit" || sparam == "tpEdit")
{
numChartClickedAfterSlTpEdit = 0;
isSlEditLastClicked = sparam == "slEdit";
isTpEditLastClicked = sparam == "tpEdit";
if (isSlEditLastClicked)
{
editValue = slEdit.Text();
}
else if (isTpEditLastClicked)
{
editValue = tpEdit.Text();
}
}
return;
}
if (id == CHARTEVENT_CLICK)
{
numChartClickedAfterSlTpEdit += 1;
// we don't want to listen for chart click event that's immediately triggered after user clicks on sl and tp edit
// and actually we just want to listen for the 2nd chart click event that's triggered after user clicks on sl and tp edit
if (numChartClickedAfterSlTpEdit != 2)
{
return;
}
datetime time;
double price;
int subWindow;
ChartXYToTimePrice(0, (int)lparam, (int)dparam, subWindow, time, price);
if (StringFind(_Symbol, "US30") != -1 || StringFind(_Symbol, "US100") != -1 || StringFind(_Symbol, "US500") != -1 || StringFind(_Symbol, "BTC") != -1)
{
price = NormalizeDouble(price, 2);
}
else if (StringFind(_Symbol, "OIL") != -1)
{
price = NormalizeDouble(price, 3);
}
else if (StringFind(_Symbol, "GOLD") != -1 || StringFind(_Symbol, "XAUUSD") != -1)
{
price = NormalizeDouble(price, 2);
}
else if (StringLen(_Symbol) >= 6)
{
if (StringFind(_Symbol, "JPY") != -1)
{
price = NormalizeDouble(price, 3);
}
else
{
price = NormalizeDouble(price, 5);
}
}
if (isSlEditLastClicked)
{
if (editValue == slEdit.Text())
{
defaultSl = price;
slEdit.Text(price); // have to implicitly convert to string
ChartRedraw();
}
isSlEditLastClicked = false;
}
else if (isTpEditLastClicked)
{
if (editValue == tpEdit.Text())
{
defaultTp = price;
tpEdit.Text(price); // have to implicitly convert to string
ChartRedraw();
}
isTpEditLastClicked = false;
}
}
}
void OnDeinit(const int reason)
{
GlobalVariableSet(_Symbol + "_SL", defaultSl);
GlobalVariableSet(_Symbol + "_TP", defaultTp);
GlobalVariableSet(_Symbol + "_RISK", defaultRiskAmount);
GlobalVariableSet(_Symbol + "_AUTO_PLACE_ORDER", defaultAutoPlaceOrder ? 1 : 0);
GlobalVariableSet(_Symbol + "_PREFERRED_RR", defaultPreferredRR);
TradePanel.Destroy(reason);
EventKillTimer();
}
void OnTick()
{
UpdateAutoPlaceOrder();
UpdateSpread();
double rr = UpdateRr();
if (rr != NULL)
{
if (autoPlaceOrderCheckbox.Checked() && rr >= (double)preferredRREdit.Text())
{
if (tpEdit.Text() != "" && slEdit.Text() != "" && riskEdit.Text() != "" && (radioGroup.Value() == 0 || radioGroup.Value() == 1))
{
if (PlaceOrder())
{
autoPlaceOrderCheckbox.Checked(false);
}
}
}
}
}
void OnTimer()
{
UpdateComments();
UpdateAutoPlaceOrder();
UpdateRr();
}
double PipToPrice(double val)
{
if (
StringFind(_Symbol, "US30") != -1 ||
StringFind(_Symbol, "US100") != -1 ||
StringFind(_Symbol, "NAS100") != -1 ||
StringFind(_Symbol, "US500") != -1 ||
StringFind(_Symbol, "BTC") != -1)
{
return val;
}
else if (StringFind(_Symbol, "OIL") != -1)
{
return val / 100;
}
else if (StringFind(_Symbol, "GOLD") != -1 || StringFind(_Symbol, "XAUUSD") != -1)
{
return val / 100;
}
else if (StringLen(_Symbol) >= 6)
{
if (StringFind(_Symbol, "JPY") != -1)
{
return val / 100;
}
else
{
return val / 10000;
}
}
return val;
}
double GetRr()
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double rawSpread = ask - bid;
if (tpEdit.Text() != "" && slEdit.Text() != "")
{
double tp = GetTp();
double sl = GetSl();
if (radioGroup.Value() == 0)
{
if ((tp - ask) <= 0 || (ask - sl) <= 0)
{
return NULL;
}
return (tp - ask) / (ask - sl);
}
else if (radioGroup.Value() == 1)
{
if ((bid - tp) <= 0 || (sl + rawSpread - bid) <= 0)
{
return NULL;
}
// Increase sl by one spread in case of sell
return (bid - tp) / (sl + rawSpread - bid);
}
}
return NULL;
}
double GetTp()
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
bool tpInPips = StringSubstr(tpEdit.Text(), StringLen(tpEdit.Text()) - 1) == "p";
if (tpInPips)
{
double pips = (double)StringSubstr(tpEdit.Text(), 0, StringLen(tpEdit.Text()) - 1);
return radioGroup.Value() == 0 ? ask + PipToPrice(pips) : bid - PipToPrice(pips);
}
return (double)tpEdit.Text();
}
double GetSl()
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
bool slInPips = StringSubstr(slEdit.Text(), StringLen(slEdit.Text()) - 1) == "p";
if (slInPips)
{
double pips = (double)StringSubstr(slEdit.Text(), 0, StringLen(slEdit.Text()) - 1);
return radioGroup.Value() == 0 ? ask - PipToPrice(pips) : bid + PipToPrice(pips);
}
return (double)slEdit.Text();
}
void UpdateComments()
{
maxOpenTradesLabel.Text("Max Open Trades = " + IntegerToString(MaxOpenTrades) + ", Current Open Trades = " + IntegerToString(PositionsTotal()));
maxTradesPerDayLabel.Text("Max Trades Per Day = " + IntegerToString(MaxTradesPerDay) + ", Current Trades Per Day = " + IntegerToString(GetCurrentTradesPerDay()));
currentTimeLabel.Text(TimeTradeServer());
if (slEdit.Text() != "" && riskEdit.Text() != "" && (radioGroup.Value() == 0 || radioGroup.Value() == 1))
{
bool isBuy = (radioGroup.Value() == 0);
double open = isBuy ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID);
double sl = GetSl();
double rawSpread = SymbolInfoDouble(_Symbol, SYMBOL_ASK) - SymbolInfoDouble(_Symbol, SYMBOL_BID);
double lotSize = LotsByRisk(_Symbol, isBuy, open, isBuy ? sl : (sl + rawSpread), (double)riskEdit.Text());
lotSizeLabel.Text("Lots: " + DoubleToString(lotSize, 2));
}
else
{
lotSizeLabel.Text("Lots:");
}
ChartRedraw();
}
void UpdateAutoPlaceOrder()
{
if (PositionsTotal() >= MaxOpenTrades && autoPlaceOrderCheckbox.Checked())
{
Print("Max open trades reached");
autoPlaceOrderCheckbox.Checked(false);
}
if (GetCurrentTradesPerDay() >= MaxTradesPerDay && autoPlaceOrderCheckbox.Checked())
{
Print("Max trades per day reached");
autoPlaceOrderCheckbox.Checked(false);
}
ChartRedraw();
}
double UpdateRr()
{
double rr = GetRr();
if (rr == NULL)
{
rrEdit.Text("NA");
}
else
{
rrEdit.Text(DoubleToString(rr, 2));
}
ChartRedraw();
return rr;
}
void UpdateSpread()
{
string spread = "";
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if (StringFind(_Symbol, "US30") != -1 || StringFind(_Symbol, "US100") != -1 || StringFind(_Symbol, "US500") != -1 || StringFind(_Symbol, "BTC") != -1)
{
spread = DoubleToString(ask - bid, 2);
}
else if (StringFind(_Symbol, "OIL") != -1)
{
spread = DoubleToString((ask - bid) * 100, 1);
}
else if (StringFind(_Symbol, "GOLD") != -1 || StringFind(_Symbol, "XAUUSD") != -1)
{
spread = DoubleToString((ask - bid) * 100, 0);
}
else if (
StringFind(_Symbol, "USD") != -1 ||
StringFind(_Symbol, "EUR") != -1 ||
StringFind(_Symbol, "JPY") != -1 ||
StringFind(_Symbol, "GBP") != -1 ||
StringFind(_Symbol, "CHF") != -1 ||
StringFind(_Symbol, "AUD") != -1 ||
StringFind(_Symbol, "CAD") != -1 ||
StringFind(_Symbol, "NZD") != -1
)
{
if (StringFind(_Symbol, "JPY") != -1)
{
spread = DoubleToString((ask - bid) * 100, 1);
}
else
{
spread = DoubleToString((ask - bid) * 10000, 1);
}
}
else
{
spread = DoubleToString(ask - bid, _Digits);
}
spreadEdit.Text(spread);
ChartRedraw();
}
int GetCurrentTradesPerDay()
{
datetime todayMidnight = iTime(Symbol(), PERIOD_D1, 0);
HistorySelect(todayMidnight, TimeCurrent());
ulong ticket = 0;
int count = 0;
for (int i = 0; i < HistoryDealsTotal(); i++)
{
if ((ticket = HistoryDealGetTicket(i)) > 0)
{
if (HistoryDealGetInteger(ticket, DEAL_ENTRY) == DEAL_ENTRY_IN)
{
count++;
}
}
}
return count;
}
bool PlaceOrder()
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if (PositionsTotal() >= MaxOpenTrades)
{
Print("Max open trades reached");
return false;
}
if (GetCurrentTradesPerDay() >= MaxTradesPerDay)
{
Print("Max trades per day reached");
return false;
}
double tp = GetTp();
double sl = GetSl();
if (tpEdit.Text() == "" || slEdit.Text() == "")
{
Print("Please set SL and TP.");
return false;
}
if (riskEdit.Text() == "")
{
Print("Please set risk.");
return false;
}
if (radioGroup.Value() != 0 && radioGroup.Value() != 1)
{
Print("Please select Buy or Sell.");
return false;
}
bool isBuy = (radioGroup.Value() == 0);
double open = isBuy ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID);
double rawSpread = ask - bid;
double lotSize = LotsByRisk(_Symbol, isBuy, open, isBuy ? sl : (sl + rawSpread), (double)riskEdit.Text());
if (lotSize > 0)
{
if (radioGroup.Value() == 0)
{
return PlaceOrderBuy(lotSize, sl, tp);
}
else
{
return PlaceOrderSell(lotSize, sl + rawSpread, tp);
}
}
else
{
Print("Invalid lot size: " + DoubleToString(lotSize));
return false;
}
}
bool PlaceOrderBuy(double lotSize, double sl, double tp)
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if (sl >= ask)
{
Print("To buy, SL should be less than Ask!");
return false;
}
if (tp <= ask)
{
Print("To buy, TP should be more than Ask!");
return false;
}
double remainingLotSize = lotSize;
do {
MqlTradeRequest request = {};
MqlTradeResult result = {};
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = MathMin(remainingLotSize, SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX));
request.type = ORDER_TYPE_BUY;
request.type_filling = ORDER_FILLING_FOK;
request.sl = sl;
request.tp = tp;
OrderSend(request, result);
if (result.retcode != 10009 && result.retcode != 10010)
{
Print("Lotsize = " + DoubleToString(MathMin(remainingLotSize, SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX))));
Print("OrderSend failed with error #", result.retcode);
return false;
}
remainingLotSize -= SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
}
while (remainingLotSize > 0);
Print("OrderSend placed successfully");
return true;
}
bool PlaceOrderSell(double lotSize, double sl, double tp)
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if (sl <= bid)
{
Print("To sell, SL should be more than Bid!");
return false;
}
if (tp >= bid)
{
Print("To sell, TP should be less than Bid!");
return false;
}
double remainingLotSize = lotSize;
do {
MqlTradeRequest request = {};
MqlTradeResult result = {};
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = MathMin(remainingLotSize, SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX));
request.type = ORDER_TYPE_SELL;
request.type_filling = ORDER_FILLING_FOK;
request.sl = sl;
request.tp = tp;
OrderSend(request, result);
if (result.retcode != 10009 && result.retcode != 10010)
{
Print("Lotsize = " + DoubleToString(MathMin(remainingLotSize, SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX))));
Print("OrderSend failed with error #", result.retcode);
return false;
}
remainingLotSize -= SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
}
while (remainingLotSize > 0);
Print("OrderSend placed successfully");
return true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment