profitline
Download scriptThis script is a professional trading tool designed for MetaTrader that allows users to visually calculate potential profits or required trade sizes (lots) directly on a price chart. It provides a user-friendly interface that helps traders visualize their goals by drawing interactive lines on the chart.
Core Functionality and Logic
The tool operates through a dedicated on-screen control panel where you can set specific parameters for your calculations. Here is how the logic flows:
1. User Input Configuration The script provides a menu where you can choose between two primary calculation modes:
- Lot Mode: You enter a volume (lot size), and the tool calculates the resulting profit based on price movement.
- Profit Mode: You enter a target currency amount, and the tool calculates how many lots are required to reach that specific goal.
Additionally, you can toggle a Spread option, which ensures the calculations remain accurate by accounting for the broker's transaction costs (the difference between buy and sell prices).
2. Dynamic Chart Drawing When you interact with the "New" button on the panel:
- The script automatically identifies available space and draws a trend line on your chart.
- It labels the line based on your settings (e.g., "Lot 0.5" or "Profit 100").
- A corresponding text object is placed at the end of the line, which serves as your dynamic result display.
3. Real-Time Interactive Calculations The most powerful feature of this script is its interactivity. You do not need to click buttons repeatedly to update your numbers:
- Drag and Drop: If you grab the trend line with your mouse and move it across the chart, the script detects the new price coordinates instantly.
- Live Updates: As you move the line, the text display automatically recalculates and updates the profit amount or lot size in real-time.
- Automatic Formatting: The tool handles all the complex math—including point values, tick values, and currency conversions—so the results are always displayed clearly in your account's local currency.
4. Management and Maintenance
- Editing: There is an "Edit" function that allows you to modify the most recently created line quickly.
- Cleanup: The script is designed to be tidy; if you delete a line from the chart manually, the script intelligently removes the associated calculation text to keep your workspace clean.
- Persistence: When you close your platform, the tool ensures that it cleans up its temporary objects so it doesn't clutter your saved charts.
#property copyright "Copyright © Matus German 2015, www.mtexperts.net"
#property version "1.1"
#property strict
#property indicator_chart_window
#include <Controls\Dialog.mqh>
#include <Controls\Label.mqh>
#include <Controls\RadioButton.mqh>
#include <Controls\RadioGroup.mqh>
#include <Controls\CheckBox.mqh>
#include <Controls\Edit.mqh>
#include <Controls\Button.mqh>
extern int Font = 12;
extern color Color = Red;
#define INDENT_LEFT (1) // indent from left (with allowance for border width)
#define INDENT_TOP (1) // indent from top (with allowance for border width)
#define INDENT_RIGHT (1) // indent from right (with allowance for border width)
#define INDENT_BOTTOM (1) // indent from bottom (with allowance for border width)
//--- for buttons
#define BUTTON_WIDTH (52) // size by X coordinate
#define BUTTON_HEIGHT (20) // size by Y coordinate
#define RADIO_BUTTON_WIDTH (20) // size by X coordinate
//--- for the edit area
#define EDIT_WIDTH 104
#define EDIT_HEIGHT 20
#define LABEL_WIDTH 28
#define LABEL_HEIGHT 20
//+------------------------------------------------------------------+
//| Class CPanelDialog |
//| Usage: main dialog of the SimplePanel application |
//+------------------------------------------------------------------+
class CPanelDialog : public CAppDialog
{
private:
CRadioButton m_Lot;
CRadioButton m_Profit;
CCheckBox m_Spread;
CEdit m_Value;
CButton m_InsertLine;
CButton m_EditLine;
CLabel m_Label_Lot;
CLabel m_Label_Profit;
CLabel m_Label_Spread;
int lastID;
public:
CPanelDialog(void);
~CPanelDialog(void);
//--- create
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);
protected:
//--- create dependent controls
bool CreateLabel(void);
bool CreateValue(void);
bool CreateLot(void);
bool CreateProfit(void);
bool CreateSpread(void);
bool CreateLabelSpread(void);
bool CreateLabelLot(void);
bool CreateLabelProfit(void);
bool CreateInsertLine(void);
bool CreateEditLine(void);
//--- internal event handlers
//virtual bool OnResize(void);
//--- handlers of the dependent controls events
void OnClickLot(void);
void OnClickProfit(void);
void OnClickInsertLine(void);
void OnClickEditLine(void);
};
//+------------------------------------------------------------------+
//| Event Handling |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CPanelDialog)
ON_EVENT(ON_CHANGE,m_Lot,OnClickLot)
ON_EVENT(ON_CHANGE,m_Profit,OnClickProfit)
ON_EVENT(ON_CLICK,m_InsertLine,OnClickInsertLine)
ON_EVENT(ON_CLICK,m_EditLine,OnClickEditLine)
EVENT_MAP_END(CAppDialog)
CPanelDialog::CPanelDialog(void){}
CPanelDialog::~CPanelDialog(void){}
//+------------------------------------------------------------------+
//| Create the "Background Panel" element |
//+------------------------------------------------------------------+
bool CPanelDialog::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);
//--- create dependent controls
if(!CreateValue())
return(false);
if(!CreateLot())
return(false);
if(!CreateLabelLot())
return(false);
if(!CreateProfit())
return(false);
if(!CreateLabelProfit())
return(false);
if(!CreateSpread())
return(false);
if(!CreateLabelSpread())
return(false);
if(!CreateInsertLine())
return(false);
if(!CreateEditLine())
return(false);
//--- succeed
lastID=-1;
return(true);
}
//+------------------------------------------------------------------+
//| Create the "Lot Radio button" element |
//+------------------------------------------------------------------+
bool CPanelDialog::CreateLot(void)
{
int x1 = INDENT_LEFT;
int y1 = INDENT_TOP;
int x2 = x1 + RADIO_BUTTON_WIDTH;
int y2 = y1 + LABEL_HEIGHT;
if(!m_Lot.Create(m_chart_id, m_name+"Lot", m_subwin, x1, y1, x2, y2))
return(false);
m_Lot.State(true);
if(!Add(m_Lot)) // attach m_Value to dialog
return(false);
return(true);
}
//+------------------------------------------------------------------+
//| Create the "label for radio button Lot" element |
//+------------------------------------------------------------------+
bool CPanelDialog::CreateLabelLot(void)
{
int x1 = INDENT_LEFT+RADIO_BUTTON_WIDTH;
int y1 = INDENT_TOP;
int x2 = x1 + LABEL_WIDTH;
int y2 = y1 + LABEL_HEIGHT;
if(!m_Label_Lot.Create(m_chart_id, m_name+"LabelLot", m_subwin, x1, y1, x2, y2))
return(false);
if(!m_Label_Lot.Text("Lot"))
return(false);
if(!Add(m_Label_Lot))
return(false);
return(true);
}
//+------------------------------------------------------------------+
//| Create the "Profit Radio button" element |
//+------------------------------------------------------------------+
bool CPanelDialog::CreateProfit(void)
{
int x1 = INDENT_LEFT+RADIO_BUTTON_WIDTH+LABEL_WIDTH;
int y1 = INDENT_TOP;
int x2 = x1 + RADIO_BUTTON_WIDTH;
int y2 = y1 + LABEL_HEIGHT;
if(!m_Profit.Create(m_chart_id, m_name+"Profit", m_subwin, x1, y1, x2, y2))
return(false);
if(!Add(m_Profit))
return(false);
return(true);
}
//+------------------------------------------------------------------+
//| Create the "Label for Radio button profit" element |
//+------------------------------------------------------------------+
bool CPanelDialog::CreateLabelProfit(void)
{
int x1 = INDENT_LEFT+RADIO_BUTTON_WIDTH+LABEL_WIDTH+RADIO_BUTTON_WIDTH;
int y1 = INDENT_TOP;
int x2 = x1 + LABEL_WIDTH;
int y2 = y1 + LABEL_HEIGHT;
if(!m_Label_Profit.Create(m_chart_id, m_name+"LabelProfit", m_subwin, x1, y1, x2, y2))
return(false);
if(!m_Label_Profit.Text("Profit"))
return(false);
if(!Add(m_Label_Profit))
return(false);
return(true);
}
//+------------------------------------------------------------------+
//| Create the "Spread check box" element |
//+------------------------------------------------------------------+
bool CPanelDialog::CreateSpread(void)
{
int x1 = INDENT_LEFT;
int y1 = INDENT_TOP+EDIT_HEIGHT;
int x2 = x1 + RADIO_BUTTON_WIDTH;
int y2 = y1 + EDIT_HEIGHT;
if(!m_Spread.Create(m_chart_id, m_name+"Spread", m_subwin, x1, y1, x2, y2))
return(false);
if(!Add(m_Spread))
return(false);
return(true);
}
//+------------------------------------------------------------------+
//| Create the "Label for spread" element |
//+------------------------------------------------------------------+
bool CPanelDialog::CreateLabelSpread(void)
{
int x1 = INDENT_LEFT+RADIO_BUTTON_WIDTH;
int y1 = INDENT_TOP+EDIT_HEIGHT;
int x2 = x1 + LABEL_WIDTH;
int y2 = y1 + LABEL_HEIGHT;
if(!m_Label_Spread.Create(m_chart_id, m_name+"LabelSpread", m_subwin, x1, y1, x2, y2))
return(false);
if(!m_Label_Spread.Text("Spread"))
return(false);
if(!Add(m_Label_Spread))
return(false);
return(true);
}
//+------------------------------------------------------------------+
//| Create the "Edit Value" element |
//+------------------------------------------------------------------+
bool CPanelDialog::CreateValue(void)
{
int x1 = INDENT_LEFT;
int y1 = INDENT_TOP+2*EDIT_HEIGHT;
int x2 = x1 + EDIT_WIDTH;
int y2 = y1 + EDIT_HEIGHT;
if(!m_Value.Create(m_chart_id, m_name+"Value", m_subwin, x1, y1, x2, y2))
return(false);
if(!m_Value.Text("0.01"))
return(false);
if(!m_Value.TextAlign(ALIGN_RIGHT))
return(false);
if(!Add(m_Value))
return(false);
return(true);
}
//+------------------------------------------------------------------+
//| Create the "Insert new line button" element |
//+------------------------------------------------------------------+
bool CPanelDialog::CreateInsertLine(void)
{
int x1 = INDENT_LEFT;
int y1 = INDENT_TOP+3*EDIT_HEIGHT;
int x2 = x1 + BUTTON_WIDTH;
int y2 = y1 + BUTTON_HEIGHT;
if(!m_InsertLine.Create(m_chart_id, m_name+"InsertLine", m_subwin, x1, y1, x2, y2))
return(false);
if(!m_InsertLine.Text("New"))
return(false);
if(!Add(m_InsertLine)) // attach m_Value to dialog
return(false);
return(true);
}
//+------------------------------------------------------------------+
//| Create the "Edit Last button" element |
//+------------------------------------------------------------------+
bool CPanelDialog::CreateEditLine(void)
{
int x1 = INDENT_LEFT+BUTTON_WIDTH;
int y1 = INDENT_TOP+3*EDIT_HEIGHT;
int x2 = x1 + BUTTON_WIDTH;
int y2 = y1 + BUTTON_HEIGHT;
if(!m_EditLine.Create(m_chart_id, m_name+"EditLine", m_subwin, x1, y1, x2, y2))
return(false);
if(!m_EditLine.Text("Edit"))
return(false);
if(!Add(m_EditLine)) // attach m_Value to dialog
return(false);
return(true);
}
//
//+------------------------------------------------------------------+
//| ON CLIC FUNCTION |
//+------------------------------------------------------------------+
void CPanelDialog::OnClickLot(void)
{
if(m_Lot.State())
m_Profit.State(false);
else
m_Profit.State(true);
}
void CPanelDialog::OnClickProfit(void)
{
if(m_Profit.State())
m_Lot.State(false);
else
m_Lot.State(true);
}
//+------------------------------------------------------------------+
//| ON CLIC FUNCTION insert new profit line |
//+------------------------------------------------------------------+
void CPanelDialog::OnClickInsertLine(void)
{
string lineDescription;
double price1, price2;
datetime time1, time2;
bool lineCreated;
int lineID=FindFreeID();
lineCreated=true;
if(ObjectFind(StringConcatenate("Profit_Line_",lineID))<0)
{
time1=iTime(Symbol(), PERIOD_CURRENT, (int)NormalizeDouble(WindowBarsPerChart()/2,0));
time2=iTime(Symbol(), PERIOD_CURRENT, 0);
price1=High[iBarShift(Symbol(), PERIOD_CURRENT, time1)];
price2=Low[iBarShift(Symbol(), PERIOD_CURRENT, time2)];
if(!ObjectCreate(0,StringConcatenate("Profit_Line_",lineID),OBJ_TREND,0,time1,price1,time2,price2))
lineCreated=false;
ObjectSet(StringConcatenate("Profit_Line_",lineID),OBJPROP_RAY,false);
ObjectSet(StringConcatenate("Profit_Line_",lineID),OBJPROP_COLOR,Color);
// set description if it is set lot or profit
if(m_Lot.State())
lineDescription="Lot";
if(m_Profit.State())
lineDescription="Profit";
lineDescription=StringConcatenate(lineDescription," ", m_Value.Text());
StringReplace(lineDescription,",",".");
if(m_Spread.Checked())
lineDescription=StringConcatenate(lineDescription," Spread");
ObjectSetString(0,StringConcatenate("Profit_Line_",lineID),OBJPROP_TEXT,lineDescription);
}
else
lineCreated=false;
if(ObjectFind(StringConcatenate("Profit_Value_",lineID))<0)
{
if(!ObjectCreate(0,StringConcatenate("Profit_Value_",lineID),OBJ_TEXT,0,time2,price2))
lineCreated=false;
ObjectSet(StringConcatenate("Profit_Value_",lineID),OBJPROP_FONTSIZE,Font);
ObjectSet(StringConcatenate("Profit_Value_",lineID),OBJPROP_COLOR,Color);
ObjectSetText(StringConcatenate("Profit_Value_",lineID),"Drag to calculate");
}
else
lineCreated=false;
m_InsertLine.Pressed(false);
if(lineCreated)
{
lines++;
lastID=lineID;
}
}
//+------------------------------------------------------------------+
//| ON CLIC FUNCTION edit last profit line |
//+------------------------------------------------------------------+
void CPanelDialog::OnClickEditLine(void)
{
string lineDescription;
datetime time1, time2;
double price1, price2, calculatedProfit, calculatedLot;
int lineID;
if(lastID>=0)
lineID=FindLastID();
else
lineID=lastID;
if(ObjectFind(StringConcatenate("Profit_Line_",lineID))>=0)
{
time1=(datetime)ObjectGetInteger(0,StringConcatenate("Profit_Line_",lineID), OBJPROP_TIME1);
price1=ObjectGetDouble(0,StringConcatenate("Profit_Line_",lineID), OBJPROP_PRICE1);
time2=(datetime)ObjectGetInteger(0,StringConcatenate("Profit_Line_",lineID), OBJPROP_TIME2);
price2=ObjectGetDouble(0,StringConcatenate("Profit_Line_",lineID), OBJPROP_PRICE2);
// edit description
if(m_Lot.State())
lineDescription="Lot";
if(m_Profit.State())
lineDescription="Profit";
lineDescription=StringConcatenate(lineDescription," ", m_Value.Text());
StringReplace(lineDescription,",",".");
if(m_Spread.Checked())
lineDescription=StringConcatenate(lineDescription," Spread");
ObjectSetString(0,StringConcatenate("Profit_Line_",lineID),OBJPROP_TEXT,lineDescription);
if(MathAbs(price1-price2)==0) // zero divide protectio
price1+=Point;
if(m_Lot.State()) // if radio button selected Lot
{
if(m_Spread.Checked())
calculatedProfit=(MathAbs(price1-price2)/Point + MarketInfo(Symbol(), MODE_SPREAD))*MarketInfo(Symbol(), MODE_TICKVALUE)*StringToDouble(m_Value.Text());
else
calculatedProfit=(MathAbs(price1-price2)/Point)*MarketInfo(Symbol(), MODE_TICKVALUE)*StringToDouble(m_Value.Text());
ObjectSetText(StringConcatenate("Profit_Value_",lineID),StringConcatenate(DoubleToStr(calculatedProfit,2)," ",AccountCurrency()));
}
else if(m_Profit.State()) // if radio button selected Profit
{
if(m_Spread.Checked())
calculatedLot=StringToDouble(m_Value.Text())/((MathAbs(price1-price2)/Point + MarketInfo(Symbol(), MODE_SPREAD))*MarketInfo(Symbol(), MODE_TICKVALUE));
else
calculatedLot=StringToDouble(m_Value.Text())/((MathAbs(price1-price2)/Point)*MarketInfo(Symbol(), MODE_TICKVALUE));
ObjectSetText(StringConcatenate("Profit_Value_",lineID),StringConcatenate(DoubleToStr(calculatedLot,2)," Lot"));
}
}
m_InsertLine.Pressed(false);
}
int lines=0;
CPanelDialog PLDialog;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
if(WindowOnDropped()!=0)
{
Print("ProfitLine must be applied to main chart window!");
return(INIT_FAILED);
}
if(!PLDialog.Create(0,"Profit Line", 0, 5,5,119, 115)) return(INIT_FAILED);
if(!PLDialog.Run()) return(INIT_FAILED);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
for(int i=0; i<lines; i++)
{
ObjectDelete(StringConcatenate("Profit_Line_",i));
ObjectDelete(StringConcatenate("Profit_Value_",i));
}
PLDialog.Destroy(reason);
return;
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
// on calculate only to remove error, logic of the indicator is handled by events
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime& time[],
const double& open[],
const double& high[],
const double& low[],
const double& close[],
const long& tick_volume[],
const long& volume[],
const int& spread[])
{
return(0);
}
//////////////////////////////////////////////////////////////////////////////////////////
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
PLDialog.ChartEvent(id,lparam,dparam,sparam);
double price1, price2, calculatedProfit, calculatedLot;
datetime time1, time2;
string objDes, objDesArr[3];
// if moved line, calculate values
if(id==CHARTEVENT_OBJECT_DRAG)
{
for(int i=0; i<lines; i++)
{
if(sparam==StringConcatenate("Profit_Line_",i)) // line moved
{
time1=(datetime)ObjectGetInteger(0,StringConcatenate("Profit_Line_",i), OBJPROP_TIME1);
price1=ObjectGetDouble(0,StringConcatenate("Profit_Line_",i), OBJPROP_PRICE1);
time2=(datetime)ObjectGetInteger(0,StringConcatenate("Profit_Line_",i), OBJPROP_TIME2);
price2=ObjectGetDouble(0,StringConcatenate("Profit_Line_",i), OBJPROP_PRICE2);
if(price1>price2)
ObjectSetInteger(0,StringConcatenate("Profit_Value_",i), OBJPROP_ANCHOR, 0);
else
ObjectSetInteger(0,StringConcatenate("Profit_Value_",i), OBJPROP_ANCHOR, 2);
ObjectSetInteger(0,StringConcatenate("Profit_Value_",i), OBJPROP_TIME, time2);
ObjectSetDouble(0,StringConcatenate("Profit_Value_",i), OBJPROP_PRICE, price2);
objDes=ObjectDescription(StringConcatenate("Profit_Line_",i));
StringToLower(objDes);
// split to find value
string sep=" ";
ushort u_sep;
u_sep=StringGetCharacter(sep,0);
int k=StringSplit(objDes,u_sep,objDesArr); // objDesArr[1] is set value ov Lot/profit
if(MathAbs(price1-price2)==0) // zero divide protectio
price1+=Point;
if(StringFind(objDes, "lot")>=0) // if the description is lot calculate profit
{
if(StringFind(objDes, "spread")>=0)
calculatedProfit=(MathAbs(price1-price2)/Point + MarketInfo(Symbol(), MODE_SPREAD))*MarketInfo(Symbol(), MODE_TICKVALUE)* StringToDouble(objDesArr[1]);
else
calculatedProfit=(MathAbs(price1-price2)/Point)*MarketInfo(Symbol(), MODE_TICKVALUE)*StringToDouble(objDesArr[1]);
ObjectSetText(StringConcatenate("Profit_Value_",i),StringConcatenate(DoubleToStr(calculatedProfit,2)," ",AccountCurrency()));
}
else if(StringFind(objDes, "profit")>=0)
{
if(StringFind(objDes, "spread")>=0)
calculatedLot=StringToDouble(objDesArr[1])/((MathAbs(price1-price2)/Point + MarketInfo(Symbol(), MODE_SPREAD))*MarketInfo(Symbol(), MODE_TICKVALUE));
else
calculatedLot=StringToDouble(objDesArr[1])/((MathAbs(price1-price2)/Point)*MarketInfo(Symbol(), MODE_TICKVALUE));
ObjectSetText(StringConcatenate("Profit_Value_",i),StringConcatenate(DoubleToStr(calculatedLot,2)," Lot"));
}
}
}
}
// if deleted line delete value and vice versa
if(id==CHARTEVENT_OBJECT_DELETE)
{
for(int i=0; i<lines; i++)
{
if(sparam==StringConcatenate("Profit_Line_",i)) // line deleted
ObjectDelete(StringConcatenate("Profit_Value_",i));
if(sparam==StringConcatenate("Profit_Value_",i)) // value deleted
ObjectDelete(StringConcatenate("Profit_Line_",i));
}
}
}
///////////////////////////////////////////////////////////
int FindFreeID()
{
int i;
for(i=0; i<lines; i++)
{
if(ObjectFind(StringConcatenate("Profit_Line_",i))<0)
return(i);
}
return(i++);
}
///////////////////////////////////////////////////////////
int FindLastID()
{
int i;
for(i=lines-1; i>=0; i--)
{
if(ObjectFind(StringConcatenate("Profit_Line_",i))>=0)
return(i);
}
return(-1);
}
Comments