MQLArticles/Examples/Risk_Management_Panel.mq5

606 lines
25 KiB
MQL5

//+------------------------------------------------------------------+
//| Risk Management Panel.mq5 |
//| Your name |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Your name"
#property link "https://www.mql5.com"
#property version "1.00"
#property strict
//+------------------------------------------------------------------+
//| Includes |
//+------------------------------------------------------------------+
#include <Controls\Dialog.mqh>
#include <Controls\Edit.mqh>
#include <Controls\Label.mqh>
#include <Controls\Button.mqh>
#include <Controls\ComboBox.mqh>
#include "..\\RM\\RiskManagement.mqh"
//+------------------------------------------------------------------+
//| Defines |
//+------------------------------------------------------------------+
//--- for edits
#define EDIT_HEIGHT (20) // edit height
#define EDIT_WIDTH (100) // edit width
//--- for buttons
#define BUTTON_WIDTH (80) // size by X coordinate
#define BUTTON_HEIGHT (20) // size by Y coordinate
//--- for combo box
#define COMBO_BOX_WIDTH (200) // size by X coordinate
#define COMBO_BOX_HEIGHT (20) // size by Y coordinate
string elements_order_type[8] =
{
"ORDER_TYPE_BUY",
"ORDER_TYPE_SELL",
"ORDER_TYPE_BUY_LIMIT",
"ORDER_TYPE_SELL_LIMIT",
"ORDER_TYPE_BUY_STOP",
"ORDER_TYPE_SELL_STOP",
"ORDER_TYPE_BUY_STOP_LIMIT",
"ORDER_TYPE_SELL_STOP_LIMIT"
};
//+-------------------------------------------------------------------+
//| Class CRiskManagementPanel |
//| This class inherits from CAppDialog and will define the panel for |
//| managing risk parameters. |
//+-------------------------------------------------------------------+
class CRiskManagementPanel : public CAppDialog
{
private:
//---
CGetLote* m_get_lote; // Object por calculations
//--- Labels for risk parameters
CLabel m_label_risk_per_operation; // Label for risk per operation
CEdit m_edit_risk_per_operation; // Edit control for entering risk per operation
CLabel m_label_deviation; // Label for deviation
CEdit m_edit_deviation; // Edit control for entering deviation
CLabel m_label_stop_limit; // Label for stop limit
CEdit m_edit_stop_limit; // Edit control for entering stop limit
CLabel m_label_entry_price; // Label por entry price
CEdit m_edit_entry_price; // Edit control for entering entry price
CButton m_buttom_entry_bid; // Button that sets the entry price in the bid
CButton m_buttom_entry_ask; // Button that sets the entry price in the ask
//--- Labels and controls for order type and lot size
CLabel m_label_get_lote; // Label for get lot size
CComboBox m_combobox_order_type_get_lot; // ComboBox for selecting order type to get lot size
CLabel m_label_sl; // Label for stop loss
CEdit m_edit_sl; // Edit control for entering stop loss
CButton m_button_get_lote; // Button for calculating lot size
CLabel m_label_result_lote; // Label to display result for lot size
CLabel m_label_the_result_lote; // Label for the result of lot size
//--- Labels and controls for stop loss order type
CLabel m_label_get_sl; // Label for stop loss
CComboBox m_combobox_order_type_get_sl; // ComboBox for selecting order type for stop loss
CLabel m_label_lote; // Label for lot
CButton m_button_get_sl; // Button to get stop loss
CLabel m_label_result_sl; // Label for result of stop loss
CLabel m_label_the_result_sl; // Label for the result of stop loss
//--- Variables to store the data entered by the user
ulong deviation; // Stores deviation entered by the user
ulong stop_limit; // Stores stop limit entered by the user
double risk_per_operation; // Stores risk per operation entered by the user
long sl; // Stores stop loss value entered by the user
ENUM_ORDER_TYPE order_type_get_sl; // Stores the selected order type for stop loss
ENUM_ORDER_TYPE order_type_get_lot; // Stores the selected order type for lot size
double entry_price; // Entry price selected by the user
//--- create labels and buttons
bool CreateAreaClientPanel();
//--- functions to edit labels dynamically
void EditLabelResultSL(string text);
void EditLabelResultLote(string text);
//--- create controls (buttons, labels, edits, combo boxes)
bool CreateEdit(CEdit &m_edit, const string name, const int x1, const int y1, string initial_Text = "", int e_width = EDIT_WIDTH);
bool CreateLabel(CLabel &label, const string name, const string text, const int x1, const int y1);
bool CreateButton(CButton &button, const string name, const string text, const int x1, const int y1, int x2_ = BUTTON_WIDTH, int y2_ = BUTTON_HEIGHT);
bool CreateComboBox(CComboBox &combo_box, const string name, string &elements[], string initial_text, const int x1, const int y1);
//--- combo box functions for handling user input
void OnChangeComBoxOrderTypeGetLote();
void OnChangeComBoxOrderTypeGetSL();
public:
CRiskManagementPanel(void);
~CRiskManagementPanel(void);
//--- create panel and controls
virtual bool Create(const long chart, const string name, const int subwin, const int x1, const int y1, const int x2, const int y2);
//--- chart event handler
virtual bool OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam);
//--- function to convert string to ENUM_ORDER_TYPE
static ENUM_ORDER_TYPE StringOrderTypeToEnum(const string OrderType);
};
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CRiskManagementPanel::CRiskManagementPanel(void)
{
m_get_lote = new CGetLote(_Symbol);
}
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CRiskManagementPanel::~CRiskManagementPanel(void)
{
delete m_get_lote;
}
//+------------------------------------------------------------------+
//| Function to convert a string into an order type |
//+------------------------------------------------------------------+
ENUM_ORDER_TYPE CRiskManagementPanel::StringOrderTypeToEnum(const string OrderType)
{
// Convert the string order type to its corresponding enum value
if(OrderType == "ORDER_TYPE_BUY")
return ORDER_TYPE_BUY;
if(OrderType == "ORDER_TYPE_SELL")
return ORDER_TYPE_SELL;
if(OrderType == "ORDER_TYPE_BUY_LIMIT")
return ORDER_TYPE_BUY_LIMIT;
if(OrderType == "ORDER_TYPE_SELL_LIMIT")
return ORDER_TYPE_SELL_LIMIT;
if(OrderType == "ORDER_TYPE_BUY_STOP")
return ORDER_TYPE_BUY_STOP;
if(OrderType == "ORDER_TYPE_SELL_STOP")
return ORDER_TYPE_SELL_STOP;
if(OrderType == "ORDER_TYPE_BUY_STOP_LIMIT")
return ORDER_TYPE_BUY_STOP_LIMIT;
if(OrderType == "ORDER_TYPE_SELL_STOP_LIMIT")
return ORDER_TYPE_SELL_STOP_LIMIT;
// Return WRONG_VALUE if no match is found
return WRONG_VALUE;
}
//+------------------------------------------------------------------+
//| Function to update the variable that stores |
//| the order type to obtain the ideal sl |
//+------------------------------------------------------------------+
void CRiskManagementPanel::OnChangeComBoxOrderTypeGetSL(void)
{
// Iterate through the order types array to find the selected type
for(int i = 0; i < ArraySize(elements_order_type); i++)
{
// If the selected order type matches the one in the array
if(m_combobox_order_type_get_sl.Select() == elements_order_type[i])
{
// Update the order type variable for stop loss
this.order_type_get_sl = StringOrderTypeToEnum(m_combobox_order_type_get_sl.Select());
Print("New order type for sl: ", EnumToString(this.order_type_get_sl)); // Log the selected order type
break;
}
}
}
//+------------------------------------------------------------------+
//| Function to update the variable that stores |
//| the order type to obtain the ideal lot |
//+------------------------------------------------------------------+
void CRiskManagementPanel::OnChangeComBoxOrderTypeGetLote(void)
{
// Iterate through the order types array to find the selected type
for(int i = 0; i < ArraySize(elements_order_type); i++)
{
// If the selected order type matches the one in the array
if(m_combobox_order_type_get_lot.Select() == elements_order_type[i])
{
// Update the order type variable for lot size
this.order_type_get_lot = StringOrderTypeToEnum(m_combobox_order_type_get_lot.Select());
Print("New order type for lot: ", EnumToString(this.order_type_get_lot)); // Log the selected order type
break;
}
}
}
//+------------------------------------------------------------------+
//| Function to edit the text of the stop loss label |
//+------------------------------------------------------------------+
void CRiskManagementPanel::EditLabelResultSL(string text)
{
// This function updates the text of the label that shows the ideal stop loss value.
this.m_label_the_result_sl.Text(text); // Set the new text to the stop loss label
}
//+------------------------------------------------------------------+
//| Function to edit the text of the lot size label |
//+------------------------------------------------------------------+
void CRiskManagementPanel::EditLabelResultLote(string text)
{
// This function updates the text of the label that shows the ideal lot size.
this.m_label_the_result_lote.Text(text); // Set the new text to the label
}
//+-------------------------------------------------------------------+
//| Function to create the complex object: combo box |
//| This function creates a combo box with multiple selectable items. |
//+-------------------------------------------------------------------+
bool CRiskManagementPanel::CreateComboBox(CComboBox &combo_box, const string name, string &elements[], string initial_text, const int x1, const int y1)
{
//--- calculate coordinates for the combo box
int x2 = x1 + COMBO_BOX_WIDTH;
int y2 = y1 + COMBO_BOX_HEIGHT;
//--- create the combo box control
if(!combo_box.Create(m_chart_id, name, m_subwin, x1, y1, x2, y2))
return (false);
//--- add items to the combo box
for(int i = 0; i < ArraySize(elements); i++)
{
if(!combo_box.AddItem(elements[i], i))
return (false);
}
//--- select the initial text
combo_box.SelectByText(initial_text);
//--- add the combo box to the panel
if(!Add(combo_box))
return (false);
//--- successfully created the combo box
return (true);
}
//+------------------------------------------------------------------+
//| Main function to create the components of the panel area |
//+------------------------------------------------------------------+
bool CRiskManagementPanel::CreateAreaClientPanel(void)
{
int x1 = 11; // Initial X coordinate
int y1 = 15; // Initial Y coordinate
#define XGAP_INITIAL_SECTION 135
#define EDIT_WIDTH_INITIAL_SECTION 80
// --- General Section: Risk Per Operation Configuration ---
if(!CreateLabel(m_label_risk_per_operation, "L-Risk-Per-operation", "Risk per operation %: ", x1, y1))
return false; // Create the label for risk per operation
if(!CreateEdit(m_edit_risk_per_operation, "Risk-Per-operation", x1 + XGAP_INITIAL_SECTION, y1, "0.00", EDIT_WIDTH_INITIAL_SECTION))
return false; // Create the editable field for risk per operation
y1 += 30; // Move the Y coordinate down for the next section
this.risk_per_operation = 0.00;
//--- Deviation
if(!CreateLabel(m_label_deviation, "L-Deviation", "Deviation (Points):", x1, y1))
return false; // Create the label for deviation
if(!CreateEdit(m_edit_deviation, "Deviation", x1 + XGAP_INITIAL_SECTION, y1, "0", EDIT_WIDTH_INITIAL_SECTION))
return false; // Create the editable field for deviation
this.deviation = 0; // Default value for deviation
y1 += 30;
//--- Stop Limit
if(!CreateLabel(m_label_stop_limit, "L-StopLimit", "Stop Limit (Points):", x1, y1))
return false; // Create the label for stop limit
if(!CreateEdit(m_edit_stop_limit, "Stop Limit", x1 + XGAP_INITIAL_SECTION, y1, "0", EDIT_WIDTH_INITIAL_SECTION))
return false; // Create the editable field for stop limit
this.stop_limit = 0; // Default value for stop limit
y1 += 30;
//--- Entry price
if(!CreateLabel(m_label_entry_price, "L-EntryPrice", "Entry price: ", x1, y1))
return false;
if(!CreateEdit(m_edit_entry_price, "EntryPrice", x1 + XGAP_INITIAL_SECTION, y1, "0.00", EDIT_WIDTH_INITIAL_SECTION))
return false;
if(!CreateButton(m_buttom_entry_bid, "EntryPriceDefbid", "Bid", x1 + XGAP_INITIAL_SECTION + 90, y1, 30))
return false;
if(!CreateButton(m_buttom_entry_ask, "EntryPriceDefask", "Ask", x1 + XGAP_INITIAL_SECTION + 127, y1, 30))
return false;
this.entry_price = 0.00;
//--- Gap
y1 += 50;
// --- Lot Calculation Section ---
if(!CreateLabel(m_label_get_lote, "L-Get-Lote-Title", "Get Lote", x1, y1))
return false; // Create the label for lot calculation section
if(!CreateComboBox(m_combobox_order_type_get_lot, "ORDER_TYPE_LOT", elements_order_type, "ORDER_TYPE_BUY", x1 + 60, y1))
return false; // Create the combo box to select order type for lot calculation
this.order_type_get_lot = ORDER_TYPE_BUY; // Default order type
y1 += 30;
if(!CreateLabel(m_label_sl, "L-SL", "SL Point: ", x1, y1))
return false; // Create the label for SL point
if(!CreateEdit(m_edit_sl, "WRITE-SL", x1 + 60, y1))
return false; // Create the editable field for SL
if(!CreateButton(m_button_get_lote, "GET-LOTE", "Save", x1 + 160 + 5, y1))
return false; // Create the button to save the lot calculation
y1 += 25;
if(!CreateLabel(m_label_result_lote, "L-Result-Lote", "Ideal Lot: ", x1, y1))
return false; // Create the label for displaying the ideal lot
if(!CreateLabel(m_label_the_result_lote, "L-The-Result-lot", " ", x1 + 65, y1))
return false; // Create a label to display the calculated ideal lot
y1 += 50;
// --- Stop Loss Calculation Section ---
if(!CreateLabel(m_label_get_sl, "L-Get-SL-Title", "Get SL", x1, y1))
return false; // Create the label for stop loss calculation section
if(!CreateComboBox(m_combobox_order_type_get_sl, "ORDER_TYPE_SL", elements_order_type, "ORDER_TYPE_BUY", x1 + 50, y1))
return false; // Create the combo box to select order type for stop loss calculation
this.order_type_get_sl = ORDER_TYPE_BUY; // Default order type
y1 += 30;
if(!CreateLabel(m_label_lote, "L-LOTE", "Get ideal sl:", x1, y1))
return false; // Create the label for getting the ideal stop loss
if(!CreateButton(m_button_get_sl, "GET-SL", "Get", x1 + 90, y1))
return false; // Create the button to get the stop loss value
y1 += 25;
if(!CreateLabel(m_label_result_sl, "L-Result-sl", "Ideal SL:", x1, y1))
return false; // Create the label for displaying the ideal stop loss
if(!CreateLabel(m_label_the_result_sl, "L-The-result-sl", " ", x1 + 65, y1))
return false; // Create a label to display the calculated ideal stop loss
return true; // If all components are successfully created
}
//+------------------------------------------------------------------+
//| function to create the interface |
//+------------------------------------------------------------------+
bool CRiskManagementPanel::Create(const long chart, const string name, const int subwin, const int x1, const int y1, const int x2, const int y2)
{
if(!CAppDialog::Create(chart, name, subwin, x1, y1, x2, y2))
return(false);
if(!CreateAreaClientPanel())
return(false);
//--- succeed
return(true);
}
//+------------------------------------------------------------------+
//| Function to create edits |
//+------------------------------------------------------------------+
bool CRiskManagementPanel::CreateEdit(CEdit &m_edit, const string name, const int x1, const int y1, string initial_Text = "", int e_width = EDIT_WIDTH)
{
//--- coordinates
int y2 = y1 + EDIT_HEIGHT;
int x2 = x1 + e_width; // 100 defualt width
//--- create
if(!m_edit.Create(m_chart_id, name + "Edit", m_subwin, x1, y1, x2, y2))
return(false);
//--- allow editing the content
if(!m_edit.ReadOnly(false))
return(false);
if(!Add(m_edit))
return(false);
m_edit.Text(initial_Text);
//--- succeed
return(true);
}
//+------------------------------------------------------------------+
//| Function to create labels |
//+------------------------------------------------------------------+
bool CRiskManagementPanel::CreateLabel(CLabel &label, const string name, const string text, const int x1, const int y1)
{
//--- coordinates
int x2 = x1 + 50;
int y2 = y1 + 20;
//--- create
if(!label.Create(m_chart_id, name + "Label", m_subwin, x1, y1, x2, y2))
return(false);
if(!label.Text(text))
return(false);
if(!Add(label))
return(false);
//--- succeed
return(true);
}
//+------------------------------------------------------------------+
//| Function to create buttons |
//+------------------------------------------------------------------+
bool CRiskManagementPanel::CreateButton(CButton &buttom, const string name, const string text, const int x1, const int y1, int x2_ = BUTTON_WIDTH, int y2_ = BUTTON_HEIGHT)
{
int x2 = x1 + x2_;
int y2 = y1 + y2_;
//--- create
if(!buttom.Create(m_chart_id, name, m_subwin, x1, y1, x2, y2))
return(false);
if(!buttom.Text(text))
return(false);
if(!Add(buttom))
return(false);
//--- succeed
return(true);
}
//+------------------------------------------------------------------+
//| On Event function to execute chart events |
//+------------------------------------------------------------------+
bool CRiskManagementPanel::OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
//--- OnChange Combox
if(id == ON_CHANGE + CHARTEVENT_CUSTOM)
{
if(lparam == m_combobox_order_type_get_lot.Id())
{
OnChangeComBoxOrderTypeGetLote();
}
else
if(lparam == m_combobox_order_type_get_sl.Id())
{
OnChangeComBoxOrderTypeGetSL();
}
}
//--- On en edit
if(id == ON_END_EDIT + CHARTEVENT_CUSTOM)
{
if(lparam == m_edit_risk_per_operation.Id())
{
this.risk_per_operation = StringToDouble(m_edit_risk_per_operation.Text());
this.risk_per_operation = NormalizeDouble((this.risk_per_operation / 100.0) * AccountInfoDouble(ACCOUNT_BALANCE), 2);
Print("Edit Risk Per Operation: ", this.risk_per_operation);
}
else
if(lparam == m_edit_sl.Id())
{
this.sl = StringToInteger(m_edit_sl.Text());
Print("Edit SL: ", this.sl);
}
else
if(lparam == m_edit_deviation.Id())
{
this.deviation = (ulong)StringToInteger(m_edit_deviation.Text());
Print("Edit Deviation: ", this.deviation);
}
else
if(lparam == m_edit_stop_limit.Id())
{
this.stop_limit = (ulong)StringToInteger(m_edit_stop_limit.Text());
Print("Edit Stop Limit: ", this.stop_limit);
}
else
if(lparam == m_edit_entry_price.Id())
{
this.entry_price = StringToDouble(m_edit_entry_price.Text());
Print("Edit Entry price: ", this.entry_price);
}
}
//--- One click
if(id == ON_CLICK + CHARTEVENT_CUSTOM)
{
//+------------------------------------------------------------------+
// Calculate ideal lot size
if(lparam == m_button_get_lote.Id())
{
Print("---- Lot calculation ----");
Print("Risk Per operation: ", this.risk_per_operation);
Print("SL in points: ", this.sl);
Print("Order type get lot: ", EnumToString(this.order_type_get_lot));
//---
double new_risk_per_operation;
//--- Calculate
const double new_lot = m_get_lote.GetLoteByRiskPerOperationAndSL(m_get_lote.GetMaxLote(this.order_type_get_lot, this.entry_price, this.deviation, this.stop_limit)
, this.risk_per_operation, new_risk_per_operation, this.sl);
//--- Print
PrintFormat("Loss in case the following operation fail, with the parameters: lot %.2f and stop loss of %i points will be %.2f ", new_lot, this.sl, new_risk_per_operation);
//--- Update panel
EditLabelResultLote(DoubleToString(new_lot, 2));
m_button_get_lote.Pressed(false);
}
//+------------------------------------------------------------------+
// Calculate ideal stoploss and lot by risk per operation
else
if(lparam == m_button_get_sl.Id())
{
Print("---- Stop loss calculation ----");
Print("Risk Per operation: ", this.risk_per_operation);
Print("Order type get sl: ", EnumToString(this.order_type_get_lot));
//--- Calculate
double new_lot;
const long new_sl = m_get_lote.CalculateSL(this.order_type_get_sl, this.risk_per_operation, this.entry_price, new_lot, this.deviation, this.stop_limit);
//--- Print
PrintFormat("For the risk per operation %.2f the chosen lot is %.2f and the ideal stop loss in points is %i", this.risk_per_operation, new_lot, new_sl);
//--- Edit
EditLabelResultSL(IntegerToString(new_sl));
m_button_get_sl.Pressed(false);
}
else
if(lparam == m_buttom_entry_ask.Id())
{
this.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
Print("New entry price ask: ", this.entry_price);
m_buttom_entry_ask.Pressed(false);
m_edit_entry_price.Text(StringFormat("%.*f", _Digits, entry_price));
}
else
if(lparam == m_buttom_entry_bid.Id())
{
this.entry_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
Print("New entry price bid: ", this.entry_price);
m_buttom_entry_bid.Pressed(false);
m_edit_entry_price.Text(StringFormat("%.*f", _Digits, entry_price));
}
}
return(CAppDialog::OnEvent(id, lparam, dparam, sparam));
}
//+------------------------------------------------------------------+
//| Global Variables |
//+------------------------------------------------------------------+
CRiskManagementPanel g_risk_panel;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- create application dialog
if(!g_risk_panel.Create(0, "Test Risk Management", 0, 40, 40, 380, 420))
return(INIT_FAILED);
//--- run application
g_risk_panel.Run();
//--- succeed
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//--- destroy dialog
g_risk_panel.Destroy(reason);
}
//+------------------------------------------------------------------+
//| Expert chart event function |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, // event ID
const long& lparam, // event parameter of the long type
const double& dparam, // event parameter of the double type
const string& sparam) // event parameter of the string type
{
g_risk_panel.ChartEvent(id, lparam, dparam, sparam);
}
//+------------------------------------------------------------------+