//+------------------------------------------------------------------+ //| ProfitLossCalculator.mqh | //| Copyright 2020, Thomas Schwabhaeuser | //| schwabts@gmail.com | //+------------------------------------------------------------------+ #include #include #include #include #include #include #include #include //--- #include "GridTk.mqh" // #include #include "DoubleSpinEdit.mqh" // #include //+------------------------------------------------------------------+ //| defines | //+------------------------------------------------------------------+ #define CONTROLS_WIDTH (80) #define CONTROLS_HEIGHT (20) #define EDIT_WIDTH (80) //--- #define GRID_ROWS (4) #define GRID_COLS (4) #define GRID_HGAP (5) #define GRID_VGAP (5) //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ enum RADIO_ALIAS { RADIOBUTTON_BUY=0, RADIOBUTTON_SELL=1 }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ enum BUTTON_ALIAS { BUTTON_RESET=0 }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ enum EDIT_ALIAS { EDIT_ENTRY=0, EDIT_LOT=1, EDIT_LOSS_PIPS=2, EDIT_PROFIT_PIPS=3, EDIT_LOSS_MONEY=4, EDIT_PROFIT_MONEY=5 }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ enum LINE_ALIAS { LINE_ENTRY=0, LINE_LOSS=1, LINE_PROFIT=2 }; //--- string label_name[]= { "EmptyLabel","EntryLabel","LotLabel","LossPipsLabel","ProfitPipsLabel","LossMoneyLabel","ProfitMoneyLabel" }; string label_text[]= { // first label with index 0 is hard coded to be hidden by CreateLabel() "INVISIBLE","Entry:","Lot:","Loss, pips:","Target, pips:","Loss, ","Target, " }; string spinedit_name[]= { "EntryEdit","LotEdit","LossPipsEdit","ProfitPipsEdit","LossMoneyEdit","ProfitMoneyEdit" }; string line_name[] = { "PLC_EntryLine", "PLC_LossLine", "PLC_ProfitLine" }; color line_color[] = { clrOrange, clrRed, clrGreen }; string radio_name[]= { "Buy","Sell" }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CProfitLossCalculator : public CAppDialog { private: // TODO: rename m_sym to Symbol CSymbolInfo m_sym; long OrderType; double EntryPrice; double StopPrice; double TargetPrice; double LotValue; //--- UI Controls CSize CellSize; CGridTk Layout; // TODO: define new controls grouping edit fields with labels, checkboxes, and/or lines CRadioButton RadioButton[]; CButton Button[]; CLabel Label[]; CDoubleSpinEdit SpinEdit[]; CChartObjectHLine Line[]; public: CProfitLossCalculator(); ~CProfitLossCalculator(); virtual bool Create(const long chart,const string name,const int subwin, const int x1,const int y1,const int x2,const int y2); virtual void Destroy(const int reason=REASON_PROGRAM); virtual bool OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam); virtual bool OnEventLine(const int id,const long &lparam,const double &dparam,const string &sparam); protected: //--- create lines and controls bool CreateLayout(); bool CreateLines(int index); bool CreateRadioButtons(int index); bool CreateButton(int index); bool CreateLabel(int index); bool CreateDoubleSpinEdit(int index); // CreateDoubleSpinEdit(int index); //--- handlers of the dependent controls events bool OnChangeType(int index); bool OnClickButton(int index); bool OnChangeDoubleSpinEdit(int index); void OnChangeEntry(); void OnChangeLot(); void OnChangeLossPips(); void OnChangeProfitPips(); void OnChangeLossMoney(); void OnChangeProfitMoney(); void OnChangeEntryLine(); void OnChangeLossLine(); void OnChangeProfitLine(); //--- void ResetData(); void UpdateProfitLoss(); int LossPips(); int MaxLossPips(); int ProfitPips(); int MaxProfitPips(); double CalculateMoney(double exit_price); double LossMoney(); double MinLossMoney(); double MaxLossMoney(); double ProfitMoney(); double MinProfitMoney(); double MaxProfitMoney(); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CProfitLossCalculator::CProfitLossCalculator() { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CProfitLossCalculator::~CProfitLossCalculator() { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ EVENT_MAP_BEGIN(CProfitLossCalculator) ON_INDEXED_EVENT(ON_CHANGE,RadioButton,OnChangeType) ON_INDEXED_EVENT(ON_CLICK,Button,OnClickButton) ON_INDEXED_EVENT(ON_CHANGE,SpinEdit,OnChangeDoubleSpinEdit) EVENT_MAP_END(CAppDialog) //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProfitLossCalculator::Create(const long chart,const string name,const int subwin, const int x1,const int y1,const int x2,const int y2) { //--- bool ok=true; for(int i=0; i CellSize=(%d,%d)", // size.cx,size.cy, // GRID_ROWS,GRID_COLS,GRID_HGAP,GRID_VGAP, // this.CellSize.cx,this.CellSize.cy); return(true); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProfitLossCalculator::CreateLines(int index) { //PrintFormat(__FUNCTION__+"(%d) ...",index); if(index>=ArraySize(this.Line)) { ArrayResize(this.Line,index+1); } //--- bool ok=true; if(ObjectFind(0,line_name[index])<0) { ok=this.Line[index].Create(0,line_name[index],0,0.0); ok &= this.Line[index].Color(line_color[index]); ok &= this.Line[index].Selectable(true); if(!ok) { return(false); } } //--- return(true); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProfitLossCalculator::CreateRadioButtons(int index) { //PrintFormat(__FUNCTION__+"(%d) ...",index); if(index>=ArraySize(this.RadioButton)) { ArrayResize(this.RadioButton,index+1); } //--- bool ok=true; CRadioButton *radiobutton=GetPointer(this.RadioButton[index]); string fullname=m_name+"_Radio_"+radio_name[index]; ok=radiobutton.Create(m_chart_id,fullname,m_subwin,0,0,this.CellSize.cx,this.CellSize.cy); ok&=radiobutton.Text(radio_name[index]); ok&=radiobutton.State(index==0); if(!ok) return(false); if(!this.Layout.Add(radiobutton)) return(false); //--- return(ok); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProfitLossCalculator::CreateButton(int index) { //PrintFormat(__FUNCTION__+"(%d) ...",index); if(index>=ArraySize(this.Button)) { ArrayResize(this.Button,index+1); } //--- if(!CreateLabel(0)) return(false); CButton *button; button=GetPointer(this.Button[0]); string fullname=m_name+"ResetButton"; if(!button.Create(m_chart_id,fullname,m_subwin,0,0,this.CellSize.cx,this.CellSize.cy)) return(false); if(!button.Text("Reset")) return(false); if(!this.Layout.Grid(button,0,3,1,1)) return(false); //--- return(true); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProfitLossCalculator::CreateLabel(int index) { //PrintFormat(__FUNCTION__+"(%d) ...",index); if(index>=ArraySize(this.Label)) { ArrayResize(this.Label,index+1); } //--- string currency=AccountInfoString(ACCOUNT_CURRENCY); CLabel *label=GetPointer(this.Label[index]); string fullname=m_name+label_name[index]; if(!label.Create(m_chart_id,fullname,m_subwin,0,0,this.CellSize.cx,this.CellSize.cy)) return(false); string text=label_text[index]; // TODO: Get rid of constants *_EDIT when creating labels! if(index==1+EDIT_LOSS_MONEY || index==1+EDIT_PROFIT_MONEY) text+=currency+":"; if(!label.Text(text)) return(false); if(index==0) label.Hide(); if(!this.Layout.Add(label)) return(false); //--- return(true); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProfitLossCalculator::CreateDoubleSpinEdit(int index) { //PrintFormat(__FUNCTION__+"(%d) ...",index); if(index>=ArraySize(this.SpinEdit)) { ArrayResize(this.SpinEdit,index+1); } //--- CDoubleSpinEdit *spinedit=GetPointer(this.SpinEdit[index]); if(spinedit==NULL) { PrintFormat("failed: (pointer->SpinEdit[%d])!=NULL",index); return(false); } // PrintFormat("ok: (pointer->SpinEdit[%d])!=NULL",index); string fullname=m_name+spinedit_name[index]; if(!spinedit.Create(m_chart_id,fullname,m_subwin,0,0,this.CellSize.cx,this.CellSize.cy)) return(false); // PrintFormat("ok: SpinEdit[%d,%s].Create(%d,%s,%d,%d,%d,%d,%d) ... x2-x1=%d y2-y1=%d",index,EnumToString((EDIT_ALIAS)index), // m_chart_id, m_name+spinedit_name[index], m_subwin, 0,0,this.CellSize.cx,this.CellSize.cy, // this.CellSize.cx, this.CellSize.cy); spinedit.ReadOnly(false); if(!this.Layout.Add(spinedit)) return(false); //--- return(true); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProfitLossCalculator::OnClickButton(int index) { switch(index) { case BUTTON_RESET: ResetData(); break; } //--- return(true); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CProfitLossCalculator::ResetData() { //--- bool ok=false; double min_price=0.0,max_price=0.0; m_sym.Name(_Symbol); for(int tries=1; tries<5; tries++) { if(m_sym.IsSynchronized() && m_sym.Refresh() && ChartGetDouble(m_chart_id,CHART_PRICE_MIN,m_subwin,min_price) && ChartGetDouble(m_chart_id,CHART_PRICE_MAX,m_subwin,max_price)) { ok=true; break; } Sleep(1000); } if(ok) { //--- members this.OrderType=(long)ORDER_TYPE_BUY; this.EntryPrice= NormalizeDouble((max_price+min_price)/2,m_sym.Digits()); this.StopPrice = NormalizeDouble(this.EntryPrice-(this.EntryPrice-min_price)/2,m_sym.Digits()); this.TargetPrice=NormalizeDouble(max_price-(max_price-this.EntryPrice)/2,m_sym.Digits()); this.LotValue=NormalizeDouble(10*m_sym.LotsMin(),2); //--- lines this.Line[LINE_ENTRY].Price(0,this.EntryPrice); this.Line[LINE_LOSS].Price(0,this.StopPrice); this.Line[LINE_PROFIT].Price(0,this.TargetPrice); for(int i=0; ivalid_max || new_entrythis.TargetPrice+stops_level); bool valid_sell_entry=(this.OrderType==(long)ORDER_TYPE_BUY && new_pricethis.StopPrice+stops_level); if(!valid_buy_entry && !valid_sell_entry) { this.Line[LINE_ENTRY].Price(0,this.EntryPrice); ChartRedraw(); return; } this.EntryPrice=new_price; this.SpinEdit[EDIT_ENTRY].Value(new_price); this.SpinEdit[EDIT_LOSS_PIPS].Value(LossPips()); this.SpinEdit[EDIT_PROFIT_PIPS].Value(ProfitPips()); this.SpinEdit[EDIT_LOSS_MONEY].Value(LossMoney()); this.SpinEdit[EDIT_PROFIT_MONEY].Value(ProfitMoney()); //--- } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CProfitLossCalculator::OnChangeLossLine(void) { //--- double new_price=NormalizeDouble(ObjectGetDouble(0,line_name[LINE_LOSS],OBJPROP_PRICE),_Digits); double stops_level=m_sym.StopsLevel()*m_sym.Point(); bool valid_buy_loss=(this.OrderType==(long)ORDER_TYPE_BUY && new_price >= this.SpinEdit[EDIT_ENTRY].MinValue() - stops_level && new_price <= this.EntryPrice - stops_level); bool valis_sell_loss=(this.OrderType==(long)ORDER_TYPE_SELL && new_price <= this.SpinEdit[EDIT_ENTRY].MaxValue() + stops_level && new_price >= this.EntryPrice + stops_level); if(!valid_buy_loss && !valis_sell_loss) { this.Line[LINE_LOSS].Price(0,this.StopPrice); ChartRedraw(); return; } this.StopPrice=new_price; this.SpinEdit[EDIT_LOSS_PIPS].Value(LossPips()); this.SpinEdit[EDIT_LOSS_MONEY].Value(LossMoney()); //--- } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CProfitLossCalculator::OnChangeProfitLine(void) { //--- double new_price=NormalizeDouble(ObjectGetDouble(0,line_name[LINE_PROFIT],OBJPROP_PRICE),_Digits); double stops_level=m_sym.StopsLevel()*m_sym.Point(); bool valid_buy_profit=(this.OrderType==(long)ORDER_TYPE_BUY && new_price <= this.SpinEdit[EDIT_ENTRY].MaxValue() + stops_level && new_price >= this.EntryPrice + stops_level); bool valid_sell_profit=(this.OrderType==(long)ORDER_TYPE_SELL && new_price >= this.SpinEdit[EDIT_ENTRY].MinValue() - stops_level && new_price <= this.EntryPrice - stops_level); if(!valid_buy_profit && !valid_sell_profit) { this.Line[LINE_PROFIT].Price(0,this.TargetPrice); ChartRedraw(); return; } this.TargetPrice=new_price; this.SpinEdit[EDIT_PROFIT_PIPS].Value(ProfitPips()); this.SpinEdit[EDIT_PROFIT_MONEY].Value(ProfitMoney()); //--- } //+------------------------------------------------------------------+