Building interactive semi-automatic drag-and-drop Expert Advisor based on predefined risk and R/R ratio
Introduction
Some traders execute all their trades automatically, and some mix automatic and manual trades based on the output of several indicators. Being a member of the latter group I needed an interactive tool to asses dynamically risk and reward price levels directly from the chart.
Having declared maximum risk on my equity I wanted to calculate real-time parameters based on the stop-loss level I put on the chart and I needed to execute my trade directly from the EA based on calculated SL and TP levels.
This article will present a way to implement an interactive semi-automatic Expert Advisor with predefined equity risk and R/R ratio. The Expert Advisor risk, R/R and lot size parameters can be changed during runtime on the EA panel.
1. Requirements
The requirements for the EA were as follows:
- ability to predefine risk level at startup and to change it during runtime to see how it affects position size
- ability to predefine risk to reward ratio and change it during runtime
- ability to calculate real-time maximum lot size for given risk and stop-loss level
- ability to change lot size at runtime to see how it affect equity risk and reward
- ability to execute buy/sell market order directly from EA
- drag and drop interface to set stop-loss and to see price level for predefined risk to reward level
2. Design
Due to requirements for the EA of displaying and changing parameters during runtime I decided I would use CChartObject classes and its descendands to display GUI on the chart window and handle incoming chart events for user interaction. Therefore, the EA needed user interface with labels, buttons and edit fields.
At first I wanted to use CChartObjectPanel object for grouping other objects on panel, but I decided to try a different approach, I designed a class that holds labels, edit fields and buttons and displays it on an image background. The background image of the interface was made using GIMP software. MQL5 generated objects are edit fields, red labels updated real-time and buttons.
I simply put label objects on the chart and recorded their position and constructed CRRDialog class that handles all functions of displaying calculated output, receiving parameters of CChartObjectEdit fields and recording button states. Color risk and reward rectangles are objects of CChartObjectRectangle class and draggable stop loss pointer is a bitmap object of CChartObjectBitmap class.
Figure 1. Visual EA screenshot
3. Implementation of EA dialog class
The CRRDialog class handles all user interface of the EA. It contains a number of variables that are displayed, objects that are used to display the variables and methods to get/set variable values and refresh the dialog.
I am using CChartObjectBmpLabel object for the background, CChartObjectEdit objects for edit fields, CChartObjectLabel objects for displaying labels and CChartObjectButton objects for buttons:
class CRRDialog { private: int m_baseX; int m_baseY; int m_fontSize; string m_font; string m_dialogName; string m_bgFileName; double m_RRRatio; double m_riskPercent; double m_orderLots; double m_SL; double m_TP; double m_maxAllowedLots; double m_maxTicksLoss; double m_orderEquityRisk; double m_orderEquityReward; ENUM_ORDER_TYPE m_orderType; CChartObjectBmpLabel m_bgDialog; CChartObjectEdit m_riskRatioEdit; CChartObjectEdit m_riskValueEdit; CChartObjectEdit m_orderLotsEdit; CChartObjectLabel m_symbolNameLabel; CChartObjectLabel m_tickSizeLabel; CChartObjectLabel m_maxEquityLossLabel; CChartObjectLabel m_equityLabel; CChartObjectLabel m_profitValueLabel; CChartObjectLabel m_askLabel; CChartObjectLabel m_bidLabel; CChartObjectLabel m_tpLabel; CChartObjectLabel m_slLabel; CChartObjectLabel m_maxAllowedLotsLabel; CChartObjectLabel m_maxTicksLossLabel; CChartObjectLabel m_orderEquityRiskLabel; CChartObjectLabel m_orderEquityRewardLabel; CChartObjectLabel m_orderTypeLabel; CChartObjectButton m_switchOrderTypeButton; CChartObjectButton m_placeOrderButton; CChartObjectButton m_quitEAButton; public: void CRRDialog(); // CRRDialog constructor void ~CRRDialog(); // CRRDialog destructor bool CreateCRRDialog(int topX,int leftY); int DeleteCRRDialog(); void Refresh(); void SetRRRatio(double RRRatio); void SetRiskPercent(double riskPercent); double GetRiskPercent(); double GetRRRRatio(); void SetSL(double sl); void SetTP(double tp); double GetSL(); double GetTP(); void SetMaxAllowedLots(double lots); void SetMaxTicksLoss(double ticks); void SetOrderType(ENUM_ORDER_TYPE); void SwitchOrderType(); void ResetButtons(); ENUM_ORDER_TYPE GetOrderType(); void SetOrderLots(double orderLots); double GetOrderLots(); void SetOrderEquityRisk(double equityRisk); void SetOrderEquityReward(double equityReward); };
Since get/set variables methods are straightforward, I will concentrate on CreateCRRDialog() and Refresh() methods. CreateCRRDialog() method initializes background image, labels, buttons and edit fields.
For initializing labels and edit fields I use: Create() method with coordinate parameters to locate the object on the chart, Font() and FontSize() method to setup font and Description() method to put text on the label.
For buttons: Create() method additional parameters specify button size and BackColor() method specifies background color of the button.
bool CRRDialog::CreateCRRDialog(int topX,int leftY) { bool isCreated=false; MqlTick current_tick; SymbolInfoTick(Symbol(),current_tick); m_baseX = topX; m_baseY = leftY; m_bgDialog.Create(0, m_dialogName, 0, topX, leftY); m_bgDialog.BmpFileOn(m_bgFileName); m_symbolNameLabel.Create(0, "symbolNameLabel", 0, m_baseX + 120, m_baseY + 40); m_symbolNameLabel.Font("Verdana"); m_symbolNameLabel.FontSize(8); m_symbolNameLabel.Description(Symbol()); m_tickSizeLabel.Create(0, "tickSizeLabel", 0, m_baseX + 120, m_baseY + 57); m_tickSizeLabel.Font("Verdana"); m_tickSizeLabel.FontSize(8); m_tickSizeLabel.Description(DoubleToString(SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_SIZE), Digits())); m_riskRatioEdit.Create(0, "riskRatioEdit", 0, m_baseX + 120, m_baseY + 72, 35, 15); m_riskRatioEdit.Font("Verdana"); m_riskRatioEdit.FontSize(8); m_riskRatioEdit.Description(DoubleToString(m_RRRatio, 2)); m_riskValueEdit.Create(0, "riskValueEdit", 0, m_baseX + 120, m_baseY + 90, 35, 15); m_riskValueEdit.Font("Verdana"); m_riskValueEdit.FontSize(8); m_riskValueEdit.Description(DoubleToString(m_riskPercent, 2)); m_equityLabel.Create(0, "equityLabel", 0, m_baseX + 120, m_baseY + 107); m_equityLabel.Font("Verdana"); m_equityLabel.FontSize(8); m_equityLabel.Description(DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY),2)); m_maxEquityLossLabel.Create(0, "maxEquityLossLabel", 0, m_baseX + 120, m_baseY + 122); m_maxEquityLossLabel.Font("Verdana"); m_maxEquityLossLabel.FontSize(8); m_maxEquityLossLabel.Description(DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY)*m_riskPercent/100.0,2)); m_askLabel.Create(0, "askLabel", 0, m_baseX + 120, m_baseY + 145); m_askLabel.Font("Verdana"); m_askLabel.FontSize(8); m_askLabel.Description(""); m_bidLabel.Create(0, "bidLabel", 0, m_baseX + 120, m_baseY + 160); m_bidLabel.Font("Verdana"); m_bidLabel.FontSize(8); m_bidLabel.Description(""); m_slLabel.Create(0, "slLabel", 0, m_baseX + 120, m_baseY + 176); m_slLabel.Font("Verdana"); m_slLabel.FontSize(8); m_slLabel.Description(""); m_tpLabel.Create(0, "tpLabel", 0, m_baseX + 120, m_baseY + 191); m_tpLabel.Font("Verdana"); m_tpLabel.FontSize(8); m_tpLabel.Description(""); m_maxAllowedLotsLabel.Create(0, "maxAllowedLotsLabel", 0, m_baseX + 120, m_baseY + 208); m_maxAllowedLotsLabel.Font("Verdana"); m_maxAllowedLotsLabel.FontSize(8); m_maxAllowedLotsLabel.Description(""); m_maxTicksLossLabel.Create(0, "maxTicksLossLabel", 0, m_baseX + 120, m_baseY + 223); m_maxTicksLossLabel.Font("Verdana"); m_maxTicksLossLabel.FontSize(8); m_maxTicksLossLabel.Description(""); m_orderLotsEdit.Create(0, "orderLotsEdit", 0, m_baseX + 120, m_baseY + 238, 35, 15); m_orderLotsEdit.Font("Verdana"); m_orderLotsEdit.FontSize(8); m_orderLotsEdit.Description(""); m_orderEquityRiskLabel.Create(0, "orderEquityRiskLabel", 0, m_baseX + 120, m_baseY + 255); m_orderEquityRiskLabel.Font("Verdana"); m_orderEquityRiskLabel.FontSize(8); m_orderEquityRiskLabel.Description(""); m_orderEquityRewardLabel.Create(0, "orderEquityRewardLabel", 0, m_baseX + 120, m_baseY + 270); m_orderEquityRewardLabel.Font("Verdana"); m_orderEquityRewardLabel.FontSize(8); m_orderEquityRewardLabel.Description(""); m_switchOrderTypeButton.Create(0, "switchOrderTypeButton", 0, m_baseX + 20, m_baseY + 314, 160, 20); m_switchOrderTypeButton.Font("Verdana"); m_switchOrderTypeButton.FontSize(8); m_switchOrderTypeButton.BackColor(LightBlue); m_placeOrderButton.Create(0, "placeOrderButton", 0, m_baseX + 20, m_baseY + 334, 160, 20); m_placeOrderButton.Font("Verdana"); m_placeOrderButton.FontSize(8); m_placeOrderButton.BackColor(LightBlue); m_placeOrderButton.Description("Place Market Order"); m_quitEAButton.Create(0, "quitEAButton", 0, m_baseX + 20, m_baseY + 354, 160, 20); m_quitEAButton.Font("Verdana"); m_quitEAButton.FontSize(8); m_quitEAButton.BackColor(LightBlue); m_quitEAButton.Description("Quit"); return isCreated; }
Refresh() method refreshes all labels and buttons description with the CRRDialog variables and current bid/ask levels, account equity and equity risk values:
void CRRDialog::Refresh() { MqlTick current_tick; SymbolInfoTick(Symbol(),current_tick); m_equityLabel.Description(DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY),2)); m_maxEquityLossLabel.Description(DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY)* StringToDouble(m_riskValueEdit.Description())/100.0,2)); m_askLabel.Description(DoubleToString(current_tick.ask, Digits())); m_bidLabel.Description(DoubleToString(current_tick.bid, Digits())); m_slLabel.Description(DoubleToString(m_SL, Digits())); m_tpLabel.Description(DoubleToString(m_TP, Digits())); m_maxAllowedLotsLabel.Description(DoubleToString(m_maxAllowedLots,2)); m_maxTicksLossLabel.Description(DoubleToString(m_maxTicksLoss,0)); m_orderEquityRiskLabel.Description(DoubleToString(m_orderEquityRisk,2)); m_orderEquityRewardLabel.Description(DoubleToString(m_orderEquityReward,2)); if(m_orderType==ORDER_TYPE_BUY) m_switchOrderTypeButton.Description("Order Type: BUY"); else if(m_orderType==ORDER_TYPE_SELL) m_switchOrderTypeButton.Description("Order Type: SELL"); }
4. Chart Events
Since EA is designed to be interactive, it shall handle chart events.
The events that are handled include:
- dragging S/L pointer (SL_arrow object of CChartObjectBitmap class) on the chart - this will allow to collect S/L level and calculate T/P level based on R/R ratio
- switching order type (buy/sell) button
- pressing 'place market order' button
- editing risk, R/R and order lot fields
- closing EA after pressing 'Exit' button
Events that are handled are CHARTEVENT_OBJECT_CLICK for pointer selection and buttons, CHARTEVENT_OBJECT_DRAG for dragging S/L pointer, and CHARTEVENT_OBJECT_ENDEDIT after edit fields are updated by the trader.
At first implementation of OnChartEvent() function took a few pages of code, but I decided to split it into several event handlers, this converted the OnChartEvent() function to human readeable form:
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- Check the event by pressing a mouse button if(id==CHARTEVENT_OBJECT_CLICK) { string clickedChartObject=sparam; if(clickedChartObject==slButtonID) SL_arrow.Selected(!SL_arrow.Selected()); if(clickedChartObject==switchOrderTypeButtonID) { EA_switchOrderType(); }; if(clickedChartObject==placeOrderButtonID) { EA_placeOrder(); } if(clickedChartObject==quitEAButtonID) ExpertRemove(); ChartRedraw(); } if(id==CHARTEVENT_OBJECT_DRAG) { // BUY if(visualRRDialog.GetOrderType()==ORDER_TYPE_BUY) { EA_dragBuyHandle(); }; // SELL if(visualRRDialog.GetOrderType()==ORDER_TYPE_SELL) { EA_dragSellHandle(); }; ChartRedraw(); } if(id==CHARTEVENT_OBJECT_ENDEDIT) { if((sparam==riskRatioEditID || sparam==riskValueEditID || sparam==orderLotsEditID) && orderPlaced==false) { EA_editParamsUpdate(); } } }
Event handlers implementation will be described in more detail in next sections. Worth noticing is the trick I used for selecting SL_arrow object. Normally, to select an object on the chart one has to click twice on it. But it can be selected by clicking once and invoking Selected() method of CChartObject object or its descendant in OnChartEvent() function inside CHARTEVENT_OBJECT_CLICK event handler:
if(clickedChartObject==slButtonID)
SL_arrow.Selected(!SL_arrow.Selected());
Object it is selected or deselected depending on its previous state after one click.
5. Extended Money Management class based on CMoneyFixedRisk
Before I will be able to describe ChartEvent handlers I need to go through money management class.
For money management I reused CMoneyFixedRisk class provided by MetaQuotes and implemented CMoneyFixedRiskExt class.
Original CMoneyFixedRisk class methods return allowed order lot amounts for given price, stop-loss level and equity risk between minimum and maximum lot size allowed by broker. I changed CheckOpenLong() and CheckOpenShort() methods to return 0.0 lot size if risk requirements are not met and extended it with four methods: GetMaxSLPossible(), CalcMaxTicksLoss(), CalcOrderEquityRisk() and CalcOrderEquityReward():
class CMoneyFixedRiskExt : public CExpertMoney { public: //--- virtual double CheckOpenLong(double price,double sl); virtual double CheckOpenShort(double price,double sl); double GetMaxSLPossible(double price, ENUM_ORDER_TYPE orderType); double CalcMaxTicksLoss(); double CalcOrderEquityRisk(double price, double sl, double lots); double CalcOrderEquityReward(double price, double sl, double lots, double rrratio); };
GetMaxSLPossible() method calculates maximum stop-loss price value for given equity risk and minimum allowed trade size.
For example if account balance is 10 000 of account base currency and risk is 2%, we may put maximum 200 of account currency at risk. If the minimum trade lot size is 0.1 lot, this method returns price level for ORDER_TYPE_BUY or ORDER_TYPE_SELL order that will meet value of equity risk for position of 0.1 lot. This helps to estimate of what is the maximum stop-loss level we can afford for minimum lot size trade. This is a price level we cannot cross for given equity risk level.
double CMoneyFixedRiskExt::GetMaxSLPossible(double price, ENUM_ORDER_TYPE orderType) { double maxEquityLoss, tickValLoss, maxTicksLoss; double minvol=m_symbol.LotsMin(); double orderTypeMultiplier; if(m_symbol==NULL) return(0.0); switch (orderType) { case ORDER_TYPE_SELL: orderTypeMultiplier = -1.0; break; case ORDER_TYPE_BUY: orderTypeMultiplier = 1.0; break; default: orderTypeMultiplier = 0.0; } maxEquityLoss = m_account.Balance()*m_percent/100.0; // max loss tickValLoss = minvol*m_symbol.TickValueLoss(); // tick val loss maxTicksLoss = MathFloor(maxEquityLoss/tickValLoss); return (price - maxTicksLoss*m_symbol.TickSize()*orderTypeMultiplier); }
CalcMaxTickLoss() method returns maximum number of ticks we can afford to loose for given risk and minimum allowed lot size.
At first maximum equity loss is calculated as percentage of the current balance, then tick value loss for change of one tick for minimum allowed lot size for given symbol is calculated. Then maximum equity loss is divided by tick value loss and the result is. rounding it to integer value with MathFloor() function:
double CMoneyFixedRiskExt::CalcMaxTicksLoss() { double maxEquityLoss, tickValLoss, maxTicksLoss; double minvol=m_symbol.LotsMin(); if(m_symbol==NULL) return(0.0); maxEquityLoss = m_account.Balance()*m_percent/100.0; // max loss tickValLoss = minvol*m_symbol.TickValueLoss(); // tick val loss maxTicksLoss = MathFloor(maxEquityLoss/tickValLoss); return (maxTicksLoss); }
CalcOrderEquityRisk() method returns equity risk for given price, stop loss level and amount of lots. It is calculated by multiplying tick loss value by number of lots and price then multiplying by difference between current price and stop-loss level:
double CMoneyFixedRiskExt::CalcOrderEquityRisk(double price,double sl, double lots) { double equityRisk; equityRisk = lots*m_symbol.TickValueLoss()*(MathAbs(price-sl)/m_symbol.TickSize()); if (dbg) Print("calcEquityRisk: lots = " + DoubleToString(lots) + " TickValueLoss = " + DoubleToString(m_symbol.TickValueLoss()) + " risk = " + DoubleToString(equityRisk)); return equityRisk; }
CalcOrderEquityReward() method is analogic to CalcOrderEquityRisk() method but it uses TickValueProfit() instead of TickValueLoss() method and the result is multiplied by given risk to reward ratio:
double CMoneyFixedRiskExt::CalcOrderEquityReward(double price,double sl, double lots, double rrratio) { double equityReward; equityReward = lots*m_symbol.TickValueProfit()*(MathAbs(price-sl)/m_symbol.TickSize())*rrratio; if (dbg) Print("calcEquityReward: lots = " + DoubleToString(lots) + " TickValueProfit = " + DoubleToString(m_symbol.TickValueProfit()) + " reward = " + DoubleToString(equityReward)); return equityReward; }
Those methods are sufficient for calculating maximum stop-loss levels and returning real-time equity risk and reward. CalcMaxTickLoss() method is used to correct drawing of risk rectangle - if trader wants to put a trade that crosses the boundary of number of ticks he can afford to loose, the rectangle is drawn only to the maximum number of ticks he can loose.
It makes life easier to see it directly on the chart. You can see it in demo at the end of the article.
6. Chart Event handlers implementation
EA_switchOrderType() handler is triggered after receiving CHARTEVENT_OBJECT_CLICK event on m_switchOrderTypeButton object. It switches order type between ORDER_TYPE_BUY and ORDER_TYPE_SELL, resets buttons state, dialog's variables and deletes risk and reward rectangle objects on the chart:
void EA_switchOrderType() { symbolInfo.RefreshRates(); visualRRDialog.SwitchOrderType(); visualRRDialog.ResetButtons(); visualRRDialog.SetSL(0.0); visualRRDialog.SetTP(0.0); visualRRDialog.SetMaxAllowedLots(0.0); visualRRDialog.SetOrderLots(0.0); visualRRDialog.SetMaxTicksLoss(0); visualRRDialog.SetOrderEquityRisk(0.0); visualRRDialog.SetOrderEquityReward(0.0); if(visualRRDialog.GetOrderType()==ORDER_TYPE_BUY) SL_arrow.SetDouble(OBJPROP_PRICE,symbolInfo.Ask()); else if(visualRRDialog.GetOrderType()==ORDER_TYPE_SELL) SL_arrow.SetDouble(OBJPROP_PRICE,symbolInfo.Bid()); SL_arrow.SetInteger(OBJPROP_TIME,0,TimeCurrent()); rectReward.Delete(); rectRisk.Delete(); visualRRDialog.Refresh(); }
EA_dragBuyHandle() handler is triggered after SL_arrow object is dragged and dropped on the chart. At first it reads SL_arrow object drop point time and price parameters from the chart and sets price level as a hypotetical stop-loss for our trade.
Then it calculates how many lots can we open for given risk on the equity. If stop loss value cannot guarantee risk objective for the lowest trading lot possible on that symbol, it is automatically moved to the maximum SL level possible. This helps to assess how much space we have for stop loss for given risk.
After calculating risk and reward, rectangle objects are updated on the chart.
void EA_dragBuyHandle() { SL_arrow.GetDouble(OBJPROP_PRICE,0,SL_price); SL_arrow.GetInteger(OBJPROP_TIME,0,startTime); symbolInfo.RefreshRates(); currentTime=TimeCurrent(); // BUY double allowedLots=MM.CheckOpenLong(symbolInfo.Ask(),SL_price); Print("Allowed lots = "+DoubleToString(allowedLots,2)); double lowestSLAllowed=MM.GetMaxSLPossible(symbolInfo.Ask(),ORDER_TYPE_BUY); if(SL_price<lowestSLAllowed) { SL_price=lowestSLAllowed; ObjectSetDouble(0,slButtonID,OBJPROP_PRICE,lowestSLAllowed); } visualRRDialog.SetSL(SL_price); visualRRDialog.SetTP(symbolInfo.Ask()+(symbolInfo.Ask()-SL_price)*visualRRDialog.GetRRRRatio()); if(visualRRDialog.GetTP()<SL_price) { visualRRDialog.SetSL(0.0); visualRRDialog.SetTP(0.0); SL_arrow.SetDouble(OBJPROP_PRICE,symbolInfo.Ask()); rectReward.Delete(); rectRisk.Delete(); return; } double lotSize=MM.CheckOpenLong(symbolInfo.Ask(),SL_price); visualRRDialog.SetMaxAllowedLots(lotSize); visualRRDialog.SetOrderLots(lotSize); visualRRDialog.SetMaxTicksLoss(MM.CalcMaxTicksLoss()); visualRRDialog.SetOrderEquityRisk(MM.CalcOrderEquityRisk(symbolInfo.Ask(), SL_price, lotSize)); visualRRDialog.SetOrderEquityReward(MM.CalcOrderEquityReward(symbolInfo.Ask(), SL_price, lotSize, visualRRDialog.GetRRRRatio())); visualRRDialog.Refresh(); rectUpdate(visualRRDialog.GetOrderType()); }
EA_dragSellHandle() is triggered for sell order configuration.
Calculations are based on symbolInfo.Bid() price, and rectangles are drawn accordingly, that is green zone marking profit is below current price level.
void EA_dragSellHandle() { SL_arrow.GetDouble(OBJPROP_PRICE,0,SL_price); SL_arrow.GetInteger(OBJPROP_TIME,0,startTime); symbolInfo.RefreshRates(); currentTime=TimeCurrent(); double allowedLots=MM.CheckOpenShort(symbolInfo.Bid(),SL_price); Print("Allowed lots = "+DoubleToString(allowedLots,2)); double maxSLAllowed=MM.GetMaxSLPossible(symbolInfo.Bid(),ORDER_TYPE_SELL); if(SL_price>maxSLAllowed) { SL_price=maxSLAllowed; SL_arrow.SetDouble(OBJPROP_PRICE,0,maxSLAllowed); } visualRRDialog.SetSL(SL_price); visualRRDialog.SetTP(symbolInfo.Bid()-(SL_price-symbolInfo.Bid())*visualRRDialog.GetRRRRatio()); if(visualRRDialog.GetTP()>SL_price) { visualRRDialog.SetSL(0.0); visualRRDialog.SetTP(0.0); SL_arrow.SetDouble(OBJPROP_PRICE,symbolInfo.Bid()); rectReward.Delete(); rectRisk.Delete(); return; } double lotSize=MM.CheckOpenShort(symbolInfo.Bid(),SL_price); visualRRDialog.SetMaxAllowedLots(lotSize); visualRRDialog.SetOrderLots(lotSize); visualRRDialog.SetMaxTicksLoss(MM.CalcMaxTicksLoss()); visualRRDialog.SetOrderEquityRisk(MM.CalcOrderEquityRisk(symbolInfo.Bid(), SL_price, lotSize)); visualRRDialog.SetOrderEquityReward(MM.CalcOrderEquityReward(symbolInfo.Bid(), SL_price, lotSize, visualRRDialog.GetRRRRatio())); visualRRDialog.Refresh(); rectUpdate(visualRRDialog.GetOrderType()); }
EA_placeOrder() is triggered after m_placeOrderButton object was pressed. It places buy or sell market order for calculated SL and TP levels and given lot size.
Please notice how easy it is to place market order using CExpertTrade class.
bool EA_placeOrder() { symbolInfo.RefreshRates(); visualRRDialog.ResetButtons(); if(visualRRDialog.GetOrderType()==ORDER_TYPE_BUY) orderPlaced=trade.Buy(visualRRDialog.GetOrderLots(),symbolInfo.Ask(), visualRRDialog.GetSL(),visualRRDialog.GetTP(),TimeToString(TimeCurrent())); else if(visualRRDialog.GetOrderType()==ORDER_TYPE_SELL) orderPlaced=trade.Sell(visualRRDialog.GetOrderLots(),symbolInfo.Bid(), visualRRDialog.GetSL(),visualRRDialog.GetTP(),TimeToString(TimeCurrent())); return orderPlaced; }
EA_editParamsUpdate() handler is triggered when Enter key is pressed after editing one of the edit fields: riskRatioEdit, riskValueEdit and orderLotsEdit.
When this happens allowed lot size, TP level, max tick loss, equity risk and reward need to be recalculated:
void EA_editParamsUpdate() { MM.Percent(visualRRDialog.GetRiskPercent()); SL_arrow.GetDouble(OBJPROP_PRICE, 0, SL_price); SL_arrow.GetInteger(OBJPROP_TIME, 0, startTime); symbolInfo.RefreshRates(); currentTime=TimeCurrent(); double allowedLots=MM.CheckOpenLong(symbolInfo.Ask(),SL_price); double lowestSLAllowed=MM.GetMaxSLPossible(symbolInfo.Ask(),ORDER_TYPE_BUY); if(SL_price<lowestSLAllowed) { SL_price=lowestSLAllowed; ObjectSetDouble(0,slButtonID,OBJPROP_PRICE,lowestSLAllowed); } visualRRDialog.SetSL(SL_price); visualRRDialog.SetTP(symbolInfo.Ask()+(symbolInfo.Ask()-SL_price)*visualRRDialog.GetRRRRatio()); visualRRDialog.SetMaxTicksLoss(MM.CalcMaxTicksLoss()); visualRRDialog.SetOrderEquityRisk(MM.CalcOrderEquityRisk(symbolInfo.Ask(), SL_price, visualRRDialog.GetOrderLots())); visualRRDialog.SetOrderEquityReward(MM.CalcOrderEquityReward(symbolInfo.Ask(), SL_price, visualRRDialog.GetOrderLots(), visualRRDialog.GetRRRRatio())); visualRRDialog.Refresh(); rectUpdate(visualRRDialog.GetOrderType()); ChartRedraw(); }
EA_onTick() is invoked every time new tick arrives. Calculations are performed only if order was not placed yet and stop loss level was already chosen by dragging SL_arrow pointer.
After order is placed, risk and reward and TP level as well as redrawing of the risk and reward are not needed.
void EA_onTick() { if(SL_price!=0.0 && orderPlaced==false) { double lotSize=0.0; SL_price=visualRRDialog.GetSL(); symbolInfo.RefreshRates(); if(visualRRDialog.GetOrderType()==ORDER_TYPE_BUY) lotSize=MM.CheckOpenLong(symbolInfo.Ask(),SL_price); else if(visualRRDialog.GetOrderType()==ORDER_TYPE_SELL) lotSize=MM.CheckOpenShort(symbolInfo.Ask(),SL_price); visualRRDialog.SetMaxAllowedLots(lotSize); if(visualRRDialog.GetOrderLots()>lotSize) visualRRDialog.SetOrderLots(lotSize); visualRRDialog.SetMaxTicksLoss(MM.CalcMaxTicksLoss()); if(visualRRDialog.GetOrderType()==ORDER_TYPE_BUY) { visualRRDialog.SetTP(symbolInfo.Ask()+(symbolInfo.Ask()-SL_price)*visualRRDialog.GetRRRRatio()); visualRRDialog.SetOrderEquityRisk(MM.CalcOrderEquityRisk(symbolInfo.Ask(), SL_price, visualRRDialog.GetOrderLots())); visualRRDialog.SetOrderEquityReward(MM.CalcOrderEquityReward(symbolInfo.Ask(), SL_price, visualRRDialog.GetOrderLots(), visualRRDialog.GetRRRRatio())); } else if(visualRRDialog.GetOrderType()==ORDER_TYPE_SELL) { visualRRDialog.SetTP(symbolInfo.Bid()-(SL_price-symbolInfo.Bid())*visualRRDialog.GetRRRRatio()); visualRRDialog.SetOrderEquityRisk(MM.CalcOrderEquityRisk( symbolInfo.Bid(), SL_price, visualRRDialog.GetOrderLots())); visualRRDialog.SetOrderEquityReward(MM.CalcOrderEquityReward(symbolInfo.Bid(), SL_price, visualRRDialog.GetOrderLots(), visualRRDialog.GetRRRRatio())); } visualRRDialog.Refresh(); rectUpdate(visualRRDialog.GetOrderType()); } ChartRedraw(0); }
Function rectUpdate() is responsible for redrawing color risk and reward rectangles. The control points are SL_arrow object start time, current Ask or Bid price value depending on order type, and SL and TP levels. Light pink rectangle shows price range between current price and SL level and light green rectangle shows price range between current price and TP level.
Both rectangles are a great tool to observe risk to reward ratio impact on SL and TP price levels and help to adjust risk before entering the trade.
void rectUpdate(ENUM_ORDER_TYPE orderType) { symbolInfo.RefreshRates(); currentTime=TimeCurrent(); SL_arrow.GetInteger(OBJPROP_TIME,0,startTime); if(orderType==ORDER_TYPE_BUY) { rectReward.Create(0,rewardRectID,0,startTime,symbolInfo.Ask(),currentTime,symbolInfo.Ask()+ (symbolInfo.Ask()-visualRRDialog.GetSL())*visualRRDialog.GetRRRRatio()); rectReward.Color(LightGreen); rectReward.Background(true); rectRisk.Create(0,riskRectID,0,startTime,visualRRDialog.GetSL(),currentTime,symbolInfo.Ask()); rectRisk.Color(LightPink); rectRisk.Background(true); } else if(orderType==ORDER_TYPE_SELL) { rectReward.Create(0,rewardRectID,0,startTime,symbolInfo.Bid(),currentTime,symbolInfo.Bid()- (visualRRDialog.GetSL()-symbolInfo.Bid())*visualRRDialog.GetRRRRatio()); rectReward.Color(LightGreen); rectReward.Background(true); rectRisk.Create(0,riskRectID,0,startTime,visualRRDialog.GetSL(),currentTime,symbolInfo.Bid()); rectRisk.Color(LightPink); rectRisk.Background(true); } }
7. Demo
Please observe below demo of the working Expert Advisor in action. I am doing sell order after big rebounce shortly after market opened on Monday 01/11/2010.
For best viewing experience please set the video to full screen and quality to 480p. Comments are included in the video:
Conclusion
In the following article I presented a way to build interactive Expert Advisor for manual trading based on predefined risk and risk to reward ratio.
I showed how to use standard classes for displaying content on the chart and how to handle chart events for entering new data and handling drag-and-drop objects. I hope that the ideas I presented will serve as basis for building other configurable visual tools in MQL5.
All source files and bitmaps are attached to the article.
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use
Hi Nice Expert
but unfortunatelly you are forgot include also some ExpertTrade.mqh
#include <Experts\Money\MoneyFixedRiskExt.mqh>
#include <Experts\ExpertTrade.mqh> <-------------- THIS ONE :/
#include <ChartObjects\ChartObjectsShapes.mqh>
#include <ChartObjects\ChartObjectsBmpControls.mqh>
#include <Trade\SymbolInfo.mqh>
#include <CRRDialog.mqh>
#include <VisualRRIDs.mqh>
Hi Nice Expert
but unfortunatelly you are forgot include also some ExpertTrade.mqh
It is the CExpert class from Standard Library. Look up it in the include folder.
Please help with installing this EA, got it working partly...
Put both Images in the same folder with your EA.