2026-06-18 00:50:10 +03:00
|
|
|
|
2026-06-17 00:10:19 +03:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| PositionSizeTool.mq5 |
|
2026-06-17 03:53:35 +03:00
|
|
|
//| Copyright 2026, MetaQuotes Ltd. |
|
|
|
|
|
//| https://www.mql5.com/en/users/ririeh |
|
2026-06-17 00:10:19 +03:00
|
|
|
//+------------------------------------------------------------------+
|
2026-06-17 03:53:35 +03:00
|
|
|
|
|
|
|
|
#property copyright "Copyright 2026, MetaQuotes Ltd."
|
|
|
|
|
#property link "https://www.mql5.com/en/users/ririeh"
|
|
|
|
|
#property version "1.00"
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Position Line Object Names |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#define ENTRY_LINE_NAME "PST_Entry_Line"
|
|
|
|
|
#define SL_LINE_NAME "PST_StopLoss_Line"
|
|
|
|
|
#define TP_LINE_NAME "PST_TakeProfit_Line"
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Dashboard Object Prefix |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#define PANEL_PREFIX "PST_Panel_"
|
|
|
|
|
|
2026-06-18 00:50:10 +03:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Dashboard Settings |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#define PANEL_BG_NAME "PST_Panel_Background"
|
|
|
|
|
#define PANEL_X 20
|
|
|
|
|
#define PANEL_Y 30
|
|
|
|
|
#define PANEL_WIDTH 280
|
|
|
|
|
#define PANEL_HEIGHT 285
|
|
|
|
|
#define PANEL_LINE_GAP 18
|
|
|
|
|
|
|
|
|
|
#define PANEL_TEXT clrWhite
|
|
|
|
|
#define PANEL_TITLE clrGold
|
|
|
|
|
#define PANEL_BG clrBlack
|
|
|
|
|
#define PANEL_BORDER clrDimGray
|
|
|
|
|
|
2026-06-17 03:53:35 +03:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Position Tool Order Types |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
enum ENUM_POSITION_TOOL_ORDER_TYPE
|
|
|
|
|
{
|
|
|
|
|
PTO_BUY_MARKET = 0, // Buy Market
|
|
|
|
|
PTO_SELL_MARKET, // Sell Market
|
|
|
|
|
PTO_BUY_LIMIT, // Buy Limit
|
|
|
|
|
PTO_SELL_LIMIT, // Sell Limit
|
|
|
|
|
PTO_BUY_STOP, // Buy Stop
|
|
|
|
|
PTO_SELL_STOP // Sell Stop
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Position Tool Direction |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
enum ENUM_POSITION_TOOL_DIRECTION
|
|
|
|
|
{
|
|
|
|
|
PTD_BUY = 0, // Buy setup
|
|
|
|
|
PTD_SELL // Sell setup
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Returns setup direction from selected order type |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
ENUM_POSITION_TOOL_DIRECTION GetSetupDirection()
|
|
|
|
|
{
|
|
|
|
|
switch(InpOrderType)
|
|
|
|
|
{
|
|
|
|
|
case PTO_BUY_MARKET:
|
|
|
|
|
case PTO_BUY_LIMIT:
|
|
|
|
|
case PTO_BUY_STOP:
|
|
|
|
|
return PTD_BUY;
|
|
|
|
|
|
|
|
|
|
case PTO_SELL_MARKET:
|
|
|
|
|
case PTO_SELL_LIMIT:
|
|
|
|
|
case PTO_SELL_STOP:
|
|
|
|
|
return PTD_SELL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return PTD_BUY;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-18 00:50:10 +03:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Position Size Tool Inputs |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
input ENUM_POSITION_TOOL_ORDER_TYPE InpOrderType = PTO_BUY_MARKET;
|
|
|
|
|
input double InpRiskPercent = 1.0;
|
2026-06-17 03:53:35 +03:00
|
|
|
input color InpEntryLineColor = clrDodgerBlue;
|
|
|
|
|
input color InpSLLineColor = clrTomato;
|
|
|
|
|
input color InpTPLineColor = clrLimeGreen;
|
|
|
|
|
|
2026-06-18 00:50:10 +03:00
|
|
|
input ENUM_TIMEFRAMES InpATRTimeframe = PERIOD_H1;
|
|
|
|
|
input int InpATRPeriod = 14;
|
|
|
|
|
input double InpSLATRFactor = 1.0;
|
|
|
|
|
input double InpTPATRFactor = 2.0;
|
|
|
|
|
|
2026-06-17 03:53:35 +03:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Position Prices |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
double EntryPrice = 0.0;
|
|
|
|
|
double StopLossPrice = 0.0;
|
|
|
|
|
double TakeProfitPrice = 0.0;
|
|
|
|
|
|
2026-06-17 00:10:19 +03:00
|
|
|
//+------------------------------------------------------------------+
|
2026-06-18 00:50:10 +03:00
|
|
|
//| Expert deinitialization function |
|
2026-06-17 00:10:19 +03:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
int OnInit()
|
2026-06-17 03:53:35 +03:00
|
|
|
{
|
2026-06-18 00:50:10 +03:00
|
|
|
ConfigureChartAppearance();
|
|
|
|
|
|
|
|
|
|
ConfigureChartDisplay();
|
|
|
|
|
|
2026-06-17 03:53:35 +03:00
|
|
|
InitializeLinePrices();
|
|
|
|
|
|
|
|
|
|
if(!CreatePositionLines())
|
|
|
|
|
return INIT_FAILED;
|
|
|
|
|
|
2026-06-18 00:50:10 +03:00
|
|
|
CreateDashboard();
|
|
|
|
|
UpdateDashboard();
|
|
|
|
|
|
2026-06-17 03:53:35 +03:00
|
|
|
return INIT_SUCCEEDED;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-17 00:10:19 +03:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Expert deinitialization function |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void OnDeinit(const int reason)
|
2026-06-18 00:50:10 +03:00
|
|
|
{
|
|
|
|
|
DeleteDashboard();
|
|
|
|
|
DeletePositionLines();
|
|
|
|
|
|
|
|
|
|
ChartRedraw(0);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-17 00:10:19 +03:00
|
|
|
//+------------------------------------------------------------------+
|
2026-06-18 00:50:10 +03:00
|
|
|
//| Tick event handler |
|
2026-06-17 00:10:19 +03:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void OnTick()
|
2026-06-18 00:50:10 +03:00
|
|
|
{
|
|
|
|
|
UpdateMarketEntryPrice();
|
|
|
|
|
UpdateDashboard();
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-17 00:10:19 +03:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Timer function |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void OnTimer()
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
}
|
2026-06-17 03:53:35 +03:00
|
|
|
|
2026-06-18 00:50:10 +03:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Chart event handler |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void OnChartEvent(const int id,
|
|
|
|
|
const long &lparam,
|
|
|
|
|
const double &dparam,
|
|
|
|
|
const string &sparam)
|
|
|
|
|
{
|
|
|
|
|
if(id != CHARTEVENT_OBJECT_DRAG)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if(IsMarketOrderType() && sparam == ENTRY_LINE_NAME)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if(sparam == ENTRY_LINE_NAME ||
|
|
|
|
|
sparam == SL_LINE_NAME ||
|
|
|
|
|
sparam == TP_LINE_NAME)
|
|
|
|
|
{
|
|
|
|
|
UpdateDashboard();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-17 00:10:19 +03:00
|
|
|
//+------------------------------------------------------------------+
|
2026-06-17 03:53:35 +03:00
|
|
|
//| Initializes default line prices |
|
2026-06-17 00:10:19 +03:00
|
|
|
//+------------------------------------------------------------------+
|
2026-06-17 03:53:35 +03:00
|
|
|
void InitializeLinePrices()
|
|
|
|
|
{
|
2026-06-18 00:50:10 +03:00
|
|
|
double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
|
|
|
|
|
double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
|
|
|
|
|
|
|
|
|
|
double atr = GetATRValue();
|
|
|
|
|
|
|
|
|
|
if(atr <= 0.0)
|
|
|
|
|
atr = 100 * _Point;
|
|
|
|
|
|
|
|
|
|
double slDistance = atr * InpSLATRFactor;
|
|
|
|
|
double tpDistance = atr * InpTPATRFactor;
|
2026-06-17 03:53:35 +03:00
|
|
|
|
|
|
|
|
switch(InpOrderType)
|
|
|
|
|
{
|
|
|
|
|
case PTO_BUY_MARKET:
|
|
|
|
|
EntryPrice = ask;
|
2026-06-18 00:50:10 +03:00
|
|
|
StopLossPrice = EntryPrice - slDistance;
|
|
|
|
|
TakeProfitPrice = EntryPrice + tpDistance;
|
2026-06-17 03:53:35 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PTO_SELL_MARKET:
|
|
|
|
|
EntryPrice = bid;
|
2026-06-18 00:50:10 +03:00
|
|
|
StopLossPrice = EntryPrice + slDistance;
|
|
|
|
|
TakeProfitPrice = EntryPrice - tpDistance;
|
2026-06-17 03:53:35 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PTO_BUY_LIMIT:
|
2026-06-18 00:50:10 +03:00
|
|
|
EntryPrice = bid - slDistance;
|
|
|
|
|
StopLossPrice = EntryPrice - slDistance;
|
|
|
|
|
TakeProfitPrice = EntryPrice + tpDistance;
|
2026-06-17 03:53:35 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PTO_SELL_LIMIT:
|
2026-06-18 00:50:10 +03:00
|
|
|
EntryPrice = ask + slDistance;
|
|
|
|
|
StopLossPrice = EntryPrice + slDistance;
|
|
|
|
|
TakeProfitPrice = EntryPrice - tpDistance;
|
2026-06-17 03:53:35 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PTO_BUY_STOP:
|
2026-06-18 00:50:10 +03:00
|
|
|
EntryPrice = ask + slDistance;
|
|
|
|
|
StopLossPrice = EntryPrice - slDistance;
|
|
|
|
|
TakeProfitPrice = EntryPrice + tpDistance;
|
2026-06-17 03:53:35 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PTO_SELL_STOP:
|
2026-06-18 00:50:10 +03:00
|
|
|
EntryPrice = bid - slDistance;
|
|
|
|
|
StopLossPrice = EntryPrice + slDistance;
|
|
|
|
|
TakeProfitPrice = EntryPrice - tpDistance;
|
2026-06-17 03:53:35 +03:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-17 00:10:19 +03:00
|
|
|
//+------------------------------------------------------------------+
|
2026-06-17 03:53:35 +03:00
|
|
|
//| Creates an adjustable horizontal price line |
|
2026-06-17 00:10:19 +03:00
|
|
|
//+------------------------------------------------------------------+
|
2026-06-17 03:53:35 +03:00
|
|
|
bool CreatePriceLine(string name,double price,color lineColor,string text)
|
|
|
|
|
{
|
2026-06-18 00:50:10 +03:00
|
|
|
ObjectDelete(0,name);
|
2026-06-17 03:53:35 +03:00
|
|
|
|
|
|
|
|
if(!ObjectCreate(0,name,OBJ_HLINE,0,0,price))
|
2026-06-18 00:50:10 +03:00
|
|
|
{
|
|
|
|
|
Print("Failed to create line: ",name," Error: ",GetLastError());
|
2026-06-17 03:53:35 +03:00
|
|
|
return false;
|
2026-06-18 00:50:10 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool draggable = true;
|
|
|
|
|
|
|
|
|
|
if(name == ENTRY_LINE_NAME && IsMarketOrderType())
|
|
|
|
|
draggable = false;
|
|
|
|
|
|
|
|
|
|
ObjectMove(0,name,0,0,price);
|
2026-06-17 03:53:35 +03:00
|
|
|
|
|
|
|
|
ObjectSetInteger(0,name,OBJPROP_COLOR,lineColor);
|
|
|
|
|
ObjectSetInteger(0,name,OBJPROP_WIDTH,2);
|
|
|
|
|
ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID);
|
|
|
|
|
|
|
|
|
|
ObjectSetInteger(0,name,OBJPROP_BACK,false);
|
2026-06-18 00:50:10 +03:00
|
|
|
ObjectSetInteger(0,name,OBJPROP_SELECTABLE,draggable);
|
|
|
|
|
ObjectSetInteger(0,name,OBJPROP_SELECTED,true);
|
2026-06-17 03:53:35 +03:00
|
|
|
ObjectSetInteger(0,name,OBJPROP_HIDDEN,false);
|
|
|
|
|
|
|
|
|
|
ObjectSetString(0,name,OBJPROP_TEXT,text);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Creates all position tool lines |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CreatePositionLines()
|
|
|
|
|
{
|
|
|
|
|
if(!CreatePriceLine(ENTRY_LINE_NAME,EntryPrice,InpEntryLineColor,"Entry"))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if(!CreatePriceLine(SL_LINE_NAME,StopLossPrice,InpSLLineColor,"Stop Loss"))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if(!CreatePriceLine(TP_LINE_NAME,TakeProfitPrice,InpTPLineColor,"Take Profit"))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
ChartRedraw(0);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
2026-06-18 00:50:10 +03:00
|
|
|
//| Returns the current price of a horizontal line |
|
2026-06-17 03:53:35 +03:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
double GetLinePrice(string name)
|
|
|
|
|
{
|
|
|
|
|
if(ObjectFind(0,name) < 0)
|
|
|
|
|
return 0.0;
|
|
|
|
|
|
2026-06-18 00:50:10 +03:00
|
|
|
return ObjectGetDouble(0,name,OBJPROP_PRICE,0);
|
2026-06-17 03:53:35 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Updates current line prices |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void UpdateLinePrices()
|
|
|
|
|
{
|
|
|
|
|
EntryPrice = GetLinePrice(ENTRY_LINE_NAME);
|
|
|
|
|
StopLossPrice = GetLinePrice(SL_LINE_NAME);
|
|
|
|
|
TakeProfitPrice = GetLinePrice(TP_LINE_NAME);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Returns direction text |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
string GetDirectionText()
|
|
|
|
|
{
|
|
|
|
|
ENUM_POSITION_TOOL_DIRECTION direction = GetSetupDirection();
|
|
|
|
|
|
|
|
|
|
if(direction == PTD_BUY)
|
|
|
|
|
return "Buy";
|
|
|
|
|
|
|
|
|
|
return "Sell";
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-17 00:10:19 +03:00
|
|
|
//+------------------------------------------------------------------+
|
2026-06-18 00:50:10 +03:00
|
|
|
//| Calculates stop loss distance in price and points |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CalculateStopLossDistance(double &stopDistance,
|
|
|
|
|
double &stopPoints,
|
|
|
|
|
string &status)
|
|
|
|
|
{
|
|
|
|
|
stopDistance = MathAbs(EntryPrice - StopLossPrice);
|
|
|
|
|
stopPoints = stopDistance / _Point;
|
|
|
|
|
|
|
|
|
|
if(stopDistance <= 0.0)
|
|
|
|
|
{
|
|
|
|
|
status = "Invalid: Stop Loss distance must be greater than zero.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = "Valid stop loss distance.";
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Calculates risk amount from account balance |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
double CalculateRiskAmount()
|
|
|
|
|
{
|
|
|
|
|
double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE);
|
|
|
|
|
|
|
|
|
|
return accountBalance * InpRiskPercent / 100.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Normalizes volume according to symbol trading rules |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
double NormalizeVolume(double volume)
|
|
|
|
|
{
|
|
|
|
|
double minVolume = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
|
|
|
|
|
double maxVolume = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);
|
|
|
|
|
double volumeStep = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
|
|
|
|
|
|
|
|
|
|
if(volume < minVolume)
|
|
|
|
|
volume = minVolume;
|
|
|
|
|
|
|
|
|
|
if(volume > maxVolume)
|
|
|
|
|
volume = maxVolume;
|
|
|
|
|
|
|
|
|
|
volume = MathFloor(volume / volumeStep) * volumeStep;
|
|
|
|
|
|
|
|
|
|
return NormalizeDouble(volume,2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Calculates lot size from risk amount and stop loss distance |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CalculateLotSize(double stopDistance,
|
|
|
|
|
double riskMoney,
|
|
|
|
|
double &lotSize,
|
|
|
|
|
string &status)
|
|
|
|
|
{
|
|
|
|
|
double tickValue = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_VALUE);
|
|
|
|
|
double tickSize = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE);
|
|
|
|
|
|
|
|
|
|
if(tickValue <= 0.0 || tickSize <= 0.0)
|
|
|
|
|
{
|
|
|
|
|
status = "Invalid: Tick value or tick size is unavailable.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(stopDistance <= 0.0)
|
|
|
|
|
{
|
|
|
|
|
status = "Invalid: Stop distance must be greater than zero.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double moneyPerLot = stopDistance / tickSize * tickValue;
|
|
|
|
|
|
|
|
|
|
if(moneyPerLot <= 0.0)
|
|
|
|
|
{
|
|
|
|
|
status = "Invalid: Money risk per lot is zero.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double rawLotSize = riskMoney / moneyPerLot;
|
|
|
|
|
|
|
|
|
|
lotSize = NormalizeVolume(rawLotSize);
|
|
|
|
|
|
|
|
|
|
status = "Lot size calculated.";
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Calculates reward distance, RR, and estimated reward |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CalculateRewardMetrics(double stopPoints,
|
|
|
|
|
double riskMoney,
|
|
|
|
|
double &rewardPoints,
|
|
|
|
|
double &rr,
|
|
|
|
|
double &rewardMoney,
|
|
|
|
|
string &status)
|
|
|
|
|
{
|
|
|
|
|
double rewardDistance = MathAbs(TakeProfitPrice - EntryPrice);
|
|
|
|
|
rewardPoints = rewardDistance / _Point;
|
|
|
|
|
|
|
|
|
|
if(stopPoints <= 0.0)
|
|
|
|
|
{
|
|
|
|
|
status = "Invalid: Stop points must be greater than zero.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(rewardPoints <= 0.0)
|
|
|
|
|
{
|
|
|
|
|
status = "Invalid: Take Profit distance must be greater than zero.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rr = rewardPoints / stopPoints;
|
|
|
|
|
rewardMoney = riskMoney * rr;
|
|
|
|
|
|
|
|
|
|
status = "Reward metrics calculated.";
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Validates entry, stop loss, and take profit structure |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool ValidateLineStructure(string &status)
|
|
|
|
|
{
|
|
|
|
|
ENUM_POSITION_TOOL_DIRECTION direction = GetSetupDirection();
|
|
|
|
|
|
|
|
|
|
if(EntryPrice == StopLossPrice)
|
|
|
|
|
{
|
|
|
|
|
status = "Invalid: Entry and Stop Loss cannot be equal.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(direction == PTD_BUY)
|
|
|
|
|
{
|
|
|
|
|
if(StopLossPrice >= EntryPrice)
|
|
|
|
|
{
|
|
|
|
|
status = "Invalid: Buy setup requires Stop Loss below Entry.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(TakeProfitPrice <= EntryPrice)
|
|
|
|
|
{
|
|
|
|
|
status = "Warning: Buy setup requires Take Profit above Entry.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(direction == PTD_SELL)
|
|
|
|
|
{
|
|
|
|
|
if(StopLossPrice <= EntryPrice)
|
|
|
|
|
{
|
|
|
|
|
status = "Invalid: Sell setup requires Stop Loss above Entry.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(TakeProfitPrice >= EntryPrice)
|
|
|
|
|
{
|
|
|
|
|
status = "Warning: Sell setup requires Take Profit below Entry.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = "Valid setup.";
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Creates dashboard background panel |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CreateDashboardBackground()
|
|
|
|
|
{
|
|
|
|
|
ObjectDelete(0,PANEL_BG_NAME);
|
|
|
|
|
|
|
|
|
|
if(!ObjectCreate(0,PANEL_BG_NAME,OBJ_RECTANGLE_LABEL,0,0,0))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
ObjectSetInteger(0,PANEL_BG_NAME,OBJPROP_CORNER,CORNER_LEFT_UPPER);
|
|
|
|
|
ObjectSetInteger(0,PANEL_BG_NAME,OBJPROP_XDISTANCE,PANEL_X - 10);
|
|
|
|
|
ObjectSetInteger(0,PANEL_BG_NAME,OBJPROP_YDISTANCE,PANEL_Y - 10);
|
|
|
|
|
ObjectSetInteger(0,PANEL_BG_NAME,OBJPROP_XSIZE,PANEL_WIDTH);
|
|
|
|
|
ObjectSetInteger(0,PANEL_BG_NAME,OBJPROP_YSIZE,PANEL_HEIGHT);
|
|
|
|
|
|
|
|
|
|
ObjectSetInteger(0,PANEL_BG_NAME,OBJPROP_BGCOLOR,clrBlack);
|
|
|
|
|
ObjectSetInteger(0,PANEL_BG_NAME,OBJPROP_COLOR,clrDimGray);
|
|
|
|
|
ObjectSetInteger(0,PANEL_BG_NAME,OBJPROP_BORDER_TYPE,BORDER_FLAT);
|
|
|
|
|
|
|
|
|
|
ObjectSetInteger(0,PANEL_BG_NAME,OBJPROP_BACK,false);
|
|
|
|
|
ObjectSetInteger(0,PANEL_BG_NAME,OBJPROP_ZORDER,100);
|
|
|
|
|
ObjectSetInteger(0,PANEL_BG_NAME,OBJPROP_SELECTABLE,false);
|
|
|
|
|
ObjectSetInteger(0,PANEL_BG_NAME,OBJPROP_SELECTED,false);
|
|
|
|
|
ObjectSetInteger(0,PANEL_BG_NAME,OBJPROP_HIDDEN,true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Creates or updates a dashboard label |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void SetPanelText(string name,string text,int x,int y,color textColor)
|
|
|
|
|
{
|
|
|
|
|
if(ObjectFind(0,name) < 0)
|
|
|
|
|
{
|
|
|
|
|
if(!ObjectCreate(0,name,OBJ_LABEL,0,0,0))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
ObjectSetInteger(0,name,OBJPROP_CORNER,CORNER_LEFT_UPPER);
|
|
|
|
|
ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_LEFT_UPPER);
|
|
|
|
|
ObjectSetInteger(0,name,OBJPROP_FONTSIZE,9);
|
|
|
|
|
ObjectSetString(0,name,OBJPROP_FONT,"Tahoma");
|
|
|
|
|
|
|
|
|
|
ObjectSetInteger(0,name,OBJPROP_BACK,false);
|
|
|
|
|
ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
|
|
|
|
|
ObjectSetInteger(0,name,OBJPROP_SELECTED,false);
|
|
|
|
|
ObjectSetInteger(0,name,OBJPROP_HIDDEN,true);
|
|
|
|
|
ObjectSetInteger(0,name,OBJPROP_ZORDER,101);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x);
|
|
|
|
|
ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y);
|
|
|
|
|
ObjectSetInteger(0,name,OBJPROP_COLOR,textColor);
|
|
|
|
|
ObjectSetString(0,name,OBJPROP_TEXT,text);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CreateDashboard()
|
|
|
|
|
{
|
|
|
|
|
CreateDashboardBackground();
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < 15; i++)
|
|
|
|
|
{
|
|
|
|
|
string name = PANEL_PREFIX + IntegerToString(i);
|
|
|
|
|
SetPanelText(name,"",PANEL_X,PANEL_Y + i * PANEL_LINE_GAP,PANEL_TEXT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ChartRedraw(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Returns selected order type text |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
string GetOrderTypeText()
|
|
|
|
|
{
|
|
|
|
|
switch(InpOrderType)
|
|
|
|
|
{
|
|
|
|
|
case PTO_BUY_MARKET:
|
|
|
|
|
return "Buy Market";
|
|
|
|
|
|
|
|
|
|
case PTO_SELL_MARKET:
|
|
|
|
|
return "Sell Market";
|
|
|
|
|
|
|
|
|
|
case PTO_BUY_LIMIT:
|
|
|
|
|
return "Buy Limit";
|
|
|
|
|
|
|
|
|
|
case PTO_SELL_LIMIT:
|
|
|
|
|
return "Sell Limit";
|
|
|
|
|
|
|
|
|
|
case PTO_BUY_STOP:
|
|
|
|
|
return "Buy Stop";
|
|
|
|
|
|
|
|
|
|
case PTO_SELL_STOP:
|
|
|
|
|
return "Sell Stop";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return "Unknown";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Updates dashboard values |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void UpdateDashboard()
|
|
|
|
|
{
|
|
|
|
|
UpdateLinePrices();
|
|
|
|
|
|
|
|
|
|
string status = "";
|
|
|
|
|
|
|
|
|
|
double stopDistance = 0.0;
|
|
|
|
|
double stopPoints = 0.0;
|
|
|
|
|
double riskMoney = 0.0;
|
|
|
|
|
double lotSize = 0.0;
|
|
|
|
|
double rewardPoints = 0.0;
|
|
|
|
|
double rr = 0.0;
|
|
|
|
|
double rewardMoney = 0.0;
|
|
|
|
|
|
|
|
|
|
bool valid = ValidateLineStructure(status);
|
|
|
|
|
|
|
|
|
|
if(valid)
|
|
|
|
|
valid = CalculateStopLossDistance(stopDistance,stopPoints,status);
|
|
|
|
|
|
|
|
|
|
if(valid)
|
|
|
|
|
{
|
|
|
|
|
riskMoney = CalculateRiskAmount();
|
|
|
|
|
valid = CalculateLotSize(stopDistance,riskMoney,lotSize,status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(valid)
|
|
|
|
|
valid = CalculateRewardMetrics(stopPoints,riskMoney,rewardPoints,rr,rewardMoney,status);
|
|
|
|
|
|
|
|
|
|
SetPanelText(PANEL_PREFIX + "0","Position Size Tool",PANEL_X,PANEL_Y + 0 * PANEL_LINE_GAP,PANEL_TITLE);
|
|
|
|
|
SetPanelText(PANEL_PREFIX + "1","Symbol: " + _Symbol,PANEL_X,PANEL_Y + 1 * PANEL_LINE_GAP,PANEL_TEXT);
|
|
|
|
|
SetPanelText(PANEL_PREFIX + "2","Order Type: " + GetOrderTypeText(),PANEL_X,PANEL_Y + 2 * PANEL_LINE_GAP,PANEL_TEXT);
|
|
|
|
|
SetPanelText(PANEL_PREFIX + "3","Direction: " + GetDirectionText(),PANEL_X,PANEL_Y + 3 * PANEL_LINE_GAP,PANEL_TEXT);
|
|
|
|
|
|
|
|
|
|
SetPanelText(PANEL_PREFIX + "4","Entry: " + DoubleToString(EntryPrice,_Digits),PANEL_X,PANEL_Y + 4 * PANEL_LINE_GAP,PANEL_TEXT);
|
|
|
|
|
SetPanelText(PANEL_PREFIX + "5","Stop Loss: " + DoubleToString(StopLossPrice,_Digits),PANEL_X,PANEL_Y + 5 * PANEL_LINE_GAP,PANEL_TEXT);
|
|
|
|
|
SetPanelText(PANEL_PREFIX + "6","Take Profit: " + DoubleToString(TakeProfitPrice,_Digits),PANEL_X,PANEL_Y + 6 * PANEL_LINE_GAP,PANEL_TEXT);
|
|
|
|
|
|
|
|
|
|
SetPanelText(PANEL_PREFIX + "7","SL Points: " + DoubleToString(stopPoints,1),PANEL_X,PANEL_Y + 7 * PANEL_LINE_GAP,PANEL_TEXT);
|
|
|
|
|
SetPanelText(PANEL_PREFIX + "8","TP Points: " + DoubleToString(rewardPoints,1),PANEL_X,PANEL_Y + 8 * PANEL_LINE_GAP,PANEL_TEXT);
|
|
|
|
|
|
|
|
|
|
SetPanelText(PANEL_PREFIX + "9","Risk: " + DoubleToString(InpRiskPercent,2) + "%",PANEL_X,PANEL_Y + 9 * PANEL_LINE_GAP,PANEL_TEXT);
|
|
|
|
|
SetPanelText(PANEL_PREFIX + "10","Risk Money: " + DoubleToString(riskMoney,2),PANEL_X,PANEL_Y + 10 * PANEL_LINE_GAP,PANEL_TEXT);
|
|
|
|
|
SetPanelText(PANEL_PREFIX + "11","Reward Money: " + DoubleToString(rewardMoney,2),PANEL_X,PANEL_Y + 11 * PANEL_LINE_GAP,PANEL_TEXT);
|
|
|
|
|
SetPanelText(PANEL_PREFIX + "12","Lot Size: " + DoubleToString(lotSize,2),PANEL_X,PANEL_Y + 12 * PANEL_LINE_GAP,PANEL_TEXT);
|
|
|
|
|
SetPanelText(PANEL_PREFIX + "13","RR: 1:" + DoubleToString(rr,2),PANEL_X,PANEL_Y + 13 * PANEL_LINE_GAP,PANEL_TEXT);
|
|
|
|
|
|
|
|
|
|
SetPanelText(PANEL_PREFIX + "14","Status: " + status,PANEL_X,PANEL_Y + 14 * PANEL_LINE_GAP,valid ? clrLimeGreen : clrTomato);
|
|
|
|
|
|
|
|
|
|
ChartRedraw(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Deletes dashboard objects |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void DeleteDashboard()
|
|
|
|
|
{
|
|
|
|
|
ObjectDelete(0,PANEL_BG_NAME);
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < 15; i++)
|
|
|
|
|
{
|
|
|
|
|
string name = PANEL_PREFIX + IntegerToString(i);
|
|
|
|
|
ObjectDelete(0,name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Deletes position lines |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void DeletePositionLines()
|
|
|
|
|
{
|
|
|
|
|
ObjectDelete(0,ENTRY_LINE_NAME);
|
|
|
|
|
ObjectDelete(0,SL_LINE_NAME);
|
|
|
|
|
ObjectDelete(0,TP_LINE_NAME);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Returns current ATR value |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
double GetATRValue()
|
|
|
|
|
{
|
|
|
|
|
int handle = iATR(_Symbol,InpATRTimeframe,InpATRPeriod);
|
|
|
|
|
|
|
|
|
|
if(handle == INVALID_HANDLE)
|
|
|
|
|
return 0.0;
|
|
|
|
|
|
|
|
|
|
double buffer[];
|
|
|
|
|
|
|
|
|
|
if(CopyBuffer(handle,0,0,1,buffer) < 1)
|
|
|
|
|
{
|
|
|
|
|
IndicatorRelease(handle);
|
|
|
|
|
return 0.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IndicatorRelease(handle);
|
|
|
|
|
|
|
|
|
|
return buffer[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Checks whether selected order type is market execution |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool IsMarketOrderType()
|
|
|
|
|
{
|
|
|
|
|
return InpOrderType == PTO_BUY_MARKET ||
|
|
|
|
|
InpOrderType == PTO_SELL_MARKET;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Updates market order entry line from Bid/Ask |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void UpdateMarketEntryPrice()
|
|
|
|
|
{
|
|
|
|
|
if(!IsMarketOrderType())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if(InpOrderType == PTO_BUY_MARKET)
|
|
|
|
|
EntryPrice = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
|
|
|
|
|
else if(InpOrderType == PTO_SELL_MARKET)
|
|
|
|
|
EntryPrice = SymbolInfoDouble(_Symbol,SYMBOL_BID);
|
|
|
|
|
|
|
|
|
|
ObjectMove(0,ENTRY_LINE_NAME,0,0,EntryPrice);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Configures chart display settings |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void ConfigureChartDisplay()
|
|
|
|
|
{
|
|
|
|
|
ChartSetInteger(0,CHART_SHOW_ASK_LINE,false);
|
|
|
|
|
ChartSetInteger(0,CHART_SHOW_BID_LINE,false);
|
|
|
|
|
ChartSetInteger(0,CHART_FOREGROUND,false);
|
|
|
|
|
ChartSetInteger(0,CHART_SHOW_OBJECT_DESCR,true);
|
|
|
|
|
|
|
|
|
|
ChartRedraw(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Applies Position Size Tool chart appearance |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void ConfigureChartAppearance()
|
|
|
|
|
{
|
|
|
|
|
ChartSetInteger(0,CHART_COLOR_BACKGROUND,clrWhite);
|
|
|
|
|
|
|
|
|
|
ChartSetInteger(0,CHART_SHOW_GRID,false);
|
|
|
|
|
|
|
|
|
|
ChartSetInteger(0,CHART_MODE,CHART_CANDLES);
|
|
|
|
|
|
|
|
|
|
ChartSetInteger(0,CHART_COLOR_FOREGROUND,clrBlack);
|
|
|
|
|
|
|
|
|
|
ChartSetInteger(0,CHART_COLOR_CANDLE_BULL,clrLimeGreen);
|
|
|
|
|
ChartSetInteger(0,CHART_COLOR_CANDLE_BEAR,clrTomato);
|
|
|
|
|
|
|
|
|
|
ChartSetInteger(0,CHART_COLOR_CHART_UP,clrLimeGreen);
|
|
|
|
|
ChartSetInteger(0,CHART_COLOR_CHART_DOWN,clrTomato);
|
|
|
|
|
|
|
|
|
|
ChartSetInteger(0,CHART_COLOR_BID,clrDodgerBlue);
|
|
|
|
|
ChartSetInteger(0,CHART_COLOR_ASK,clrOrange);
|
|
|
|
|
|
|
|
|
|
ChartSetInteger(0,CHART_SHOW_ASK_LINE,false);
|
|
|
|
|
ChartSetInteger(0,CHART_SHOW_BID_LINE,false);
|
|
|
|
|
|
|
|
|
|
ChartRedraw(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|