...and here is an example for CList
//+------------------------------------------------------------------+ //| ObjArrayLoadSave.mq4 | //| nicholishen | //| http://www.reddit.com/u/nicholishenFX | //+------------------------------------------------------------------+ #property copyright "nicholishen" #property link "http://www.reddit.com/u/nicholishenFX" #property version "1.00" #property strict #include <Arrays\List.mqh> //+------------------------------------------------------------------+ class MyObj : public CObject { public: string name; double price; datetime time; virtual bool Save(const int file_handle) override // MUST BE OVERRIDEN FOR LOAD/SAVE { if(!FileWriteString(file_handle,name,10)) return false; if(!FileWriteDouble(file_handle,price)) return false; if(!FileWriteLong(file_handle,(long)time))return false; return(true); } virtual bool Load(const int file_handle) override // MUST BE OVERRIDEN FOR LOAD/SAVE { name = FileReadString(file_handle,10); price= FileReadDouble(file_handle); time = (datetime)FileReadLong(file_handle); return(true); } virtual int Compare(const CObject *node,const int mode=0)const override // MUST BE OVERRIDEN TO SORT { MyObj *other = (MyObj*)node; if(this.price > other.price) return 1; if(this.price < other.price) return -1; return 0; } string ToString(){ return name+" - "+string(time)+" - "+string(price);} }; //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ class MyList : public CList { public: virtual CObject* CreateElement() override //MUST BE OVERRIDEN FOR LOAD/SAVE { return new MyObj; } bool Load() { int h = FileOpen("MyTest.bin",FILE_READ|FILE_BIN); bool res = CList::Load(h); FileClose(h); return res; } bool Save() { int h = FileOpen("MyTest.bin",FILE_WRITE|FILE_BIN); bool res = CList::Save(h); FileClose(h); return res; } }; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- srand(GetTickCount()); MyList *list = new MyList; for(int i=0;i<SymbolsTotal(false)&&i<10;i++) { MyObj *obj = new MyObj; obj.name = SymbolName(i,false); obj.price= double(rand()%1000); obj.time = TimeCurrent(); list.Add(obj); } list.Save(); delete list; list = new MyList; list.Load(); list.Sort(0); for(MyObj *obj=list.GetFirstNode(); obj!=NULL; obj=list.GetNextNode()) Print(obj.ToString()); delete list; } //+------------------------------------------------------------------+
Hi,
First thanks nicholishen for the sample code.
I try to use it but I have 2 errors.
//+------------------------------------------------------------------+ //| CTradeParamsNode.mqh | //| Copyright 2017, Pierre Rougier | //| https://www.mql5.com/en/users/pierre8r | //+------------------------------------------------------------------+ #property copyright "Copyright 2017, Pierre Rougier" #property link "https://www.mql5.com/en/users/pierre8r" #property version "1.00" #include <Object.mqh> //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CTradeParamsNode : public CObject { private: datetime _OpenOrderAtThisDateTime; int _MagicNumber; int _TakeProfitInPips; int _StopLossInPips; double _NumberOfLotsToOpen; public: void CTradeParamsNode(datetime OpenOrderAtThisDateTime,int MagicNumber,int TakeProfitInPips,int StopLossInPips, double NumberOfLotsToOpen); virtual int Compare(const CObject *node,const int mode=0)const override; // MUST BE OVERRIDEN TO SORT datetime Get_OpenOrderAtThisDateTime(void){return this._OpenOrderAtThisDateTime;}; int Get_MagicNumber(void){return this._MagicNumber;}; int Get_TakeProfitInPips(void){return this._TakeProfitInPips;}; int Get_StopLossInPips(void){return this._StopLossInPips;}; double Get_NumberOfLotsToOpen(void){return this._NumberOfLotsToOpen;}; }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CTradeParamsNode::CTradeParamsNode(datetime OpenOrderAtThisDateTime,int MagicNumber,int TakeProfitInPips,int StopLossInPips, double NumberOfLotsToOpen) :_OpenOrderAtThisDateTime(OpenOrderAtThisDateTime),_MagicNumber(MagicNumber),_TakeProfitInPips(_TakeProfitInPips),_StopLossInPips(StopLossInPips), _NumberOfLotsToOpen(NumberOfLotsToOpen) { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int CTradeParamsNode::Compare(const CObject *node,const int mode=0)const override // MUST BE OVERRIDEN TO SORT { CTradeParamsNode *other=(CTradeParamsNode*)node; if(this.Get_OpenOrderAtThisDateTime() > other.Get_OpenOrderAtThisDateTime()) return 1; if(this.Get_OpenOrderAtThisDateTime()< other.Get_OpenOrderAtThisDateTime()) return -1; return 0; } //+------------------------------------------------------------------+
'CTradeParamsNode.mqh' CTradeParamsNode.mqh 1 1 'Object.mqh' Object.mqh 1 1 'StdLibErr.mqh' StdLibErr.mqh 1 1 'Get_OpenOrderAtThisDateTime' - call non-const method for constant object CTradeParamsNode.mqh 51 12 'Get_OpenOrderAtThisDateTime' - call non-const method for constant object CTradeParamsNode.mqh 52 12 2 error(s), 0 warning(s) 3 1
I think, i found my error.
datetime Get_OpenOrderAtThisDateTime(void)const{return this._OpenOrderAtThisDateTime;};
There are no more compilation errors, but the program is looping.
All source code comes with the attached file.
2018.01.24 17:35:17.015 eaTimeTrades (EURUSD,H1) Params : DateTime 2017.11.20 08:10:00 - MagicNumber: 2014 - TakeProfit: 0 - StopLoss: 30 - NumberOfLotsToOpen: 6.0 2018.01.24 17:35:17.015 eaTimeTrades (EURUSD,H1) Params : DateTime 2017.11.20 08:10:00 - MagicNumber: 2014 - TakeProfit: 0 - StopLoss: 30 - NumberOfLotsToOpen: 6.0 2018.01.24 17:35:17.015 eaTimeTrades (EURUSD,H1) Params : DateTime 2017.11.20 08:10:00 - MagicNumber: 2014 - TakeProfit: 0 - StopLoss: 30 - NumberOfLotsToOpen: 6.0 2018.01.24 17:35:17.015 eaTimeTrades (EURUSD,H1) Params : DateTime 2017.11.20 08:10:00 - MagicNumber: 2014 - TakeProfit: 0 - StopLoss: 30 - NumberOfLotsToOpen: 6.0 2018.01.24 17:35:17.015 eaTimeTrades (EURUSD,H1) Params : DateTime 2017.11.20 08:10:00 - MagicNumber: 2014 - TakeProfit: 0 - StopLoss: 30 - NumberOfLotsToOpen: 6.0 2018.01.24 17:35:17.015 eaTimeTrades (EURUSD,H1) Params : DateTime 2017.11.20 08:10:00 - MagicNumber: 2014 - TakeProfit: 0 - StopLoss: 30 - NumberOfLotsToOpen: 6.0 2018.01.24 17:35:17.015 eaTimeTrades (EURUSD,H1) Params : DateTime 2017.11.20 08:10:00 - MagicNumber: 2014 - TakeProfit: 0 - StopLoss: 30 - NumberOfLotsToOpen: 6.0
//+------------------------------------------------------------------+ //| eaTimeTrades.mq5 | //| Copyright 2017, Pierre Rougier | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2017, Pierre Rougier" #property link "https://www.mql5.com" #property version "1.02" #include "libLogger_V_01_01.mq5" #include<Arrays\List.mqh> #include "CTradesParamsList.mqh" //CList p_int_List=new CTradesParamsList; CTradesParamsList trades_params_list; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- // CTradeParamsNode(datetime OpenOrderAtThisDateTime,int MagicNumber,int TakeProfitInPips,int StopLossInPips,double NumberOfLotsToOpen); CTradeParamsNode *p_trades_params_node=new CTradeParamsNode(D'2017.11.16 06:01:00',2018,10,20,2); trades_params_list.Add(p_trades_params_node); // CTradeParamsNode(int MagicNumber,int TakeProfitInPips,int StopLossInPips,double NumberOfLotsToOpen,datetime OpenOrderAtThisDateTime); *p_trades_params_node=new CTradeParamsNode(D'2017.11.08 10:05:00',2015,5,20,5); trades_params_list.Add(p_trades_params_node); // CTradeParamsNode(int MagicNumber,int TakeProfitInPips,int StopLossInPips,double NumberOfLotsToOpen,datetime OpenOrderAtThisDateTime); *p_trades_params_node=new CTradeParamsNode(D'2017.11.20 08:10:00',2014,20,30,6); trades_params_list.Add(p_trades_params_node); trades_params_list.Sort(0); for(CTradeParamsNode *trade_params_node=trades_params_list.GetFirstNode(); trade_params_node!=NULL; trade_params_node=trades_params_list.GetNextNode()) { Print(trade_params_node.ToString()); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- /* //--- Define some MQL5 Structures we will use for our trade MqlTick last_tick; // To be used for getting recent/latest price quotes MqlTradeRequest mrequest; // To be used for sending our trade requests MqlTradeResult mresult; // To be used to get our trade results MqlRates mrate[]; // To be used to store the prices, volumes and spread of each bar if(SymbolInfoTick(Symbol(),last_tick)) { if(!m_isOpenedOrderPreviously) { if((last_tick.time==m_OpenFirstOrderAtThisDateTime)) { Log(DEBUG,__FILE__,__FUNCTION__,__LINE__, " Ask :"+DoubleToString(last_tick.ask)+" Bid :"+DoubleToString(last_tick.bid)+" Time :"+TimeToString(last_tick.time)); Log(DEBUG,__FILE__,__FUNCTION__,__LINE__, " Digits :"+IntegerToString(_Digits)+" Point :"+DoubleToString(_Point)); ZeroMemory(mrequest); mrequest.action=TRADE_ACTION_DEAL; // immediate order execution mrequest.price=NormalizeDouble(last_tick.ask,_Digits); // latest ask price mrequest.sl = NormalizeDouble(last_tick.ask - m_StopLossInPips*_Point,_Digits); // Stop Loss mrequest.tp = NormalizeDouble(last_tick.ask+m_TakeProfitInPips*_Point,_Digits); // Take Profit mrequest.symbol=_Symbol; // currency pair mrequest.volume=m_NumberOfLotsToOpen; // number of lots to trade mrequest.magic=m_MagicNumber; // Order Magic Number mrequest.type = ORDER_TYPE_BUY; // Buy Order mrequest.type_filling = ORDER_FILLING_FOK; // Order execution type mrequest.deviation=100; // Deviation from current price //--- send order OrderSend(mrequest,mresult); // get the result code if(mresult.retcode==10009 || mresult.retcode==10008) //Request is completed or order placed { Log(DEBUG,__FILE__,__FUNCTION__,__LINE__,"A Buy order has been successfully placed with Ticket#:"+IntegerToString(mresult.order)); Log(DEBUG,__FILE__,__FUNCTION__,__LINE__, " Ask :"+DoubleToString(last_tick.ask)+" Bid :"+DoubleToString(last_tick.bid)+" Time :"+TimeToString(last_tick.time)); } else { Log(ERROR,__FILE__,__FUNCTION__,__LINE__,"The Buy order request could not be completed - Error:"+IntegerToString(GetLastError())); ResetLastError(); return; } m_isOpenedOrderPreviously=true; } } } else { Log(FATAL,__FILE__,__FUNCTION__,__LINE__,IntegerToString(GetLastError())); } */ } //+------------------------------------------------------------------+
There are no more compilation errors, but the program is looping.
All source code comes with the attached file.
for(CTradeParamsNode *trade_params_node=trades_params_list.GetFirstNode(); trade_params_node!=NULL; trade_params_node=trades_params_node.Next()) { Print(trade_params_node.ToString()); }
I got another version to use CArrayObj with my own Object Class
- Array/Order.mqh : contain CObjectOrder (new class) derived from CObject
- Array/CArrayObjOrder.mqh: copy from CArrayObj.mqh and then replace all reference of
CArrayObj to CArrayObjOrder and
CObject to CObjectOrder - Then just use CArrayObjOrder or CObjectOrder as replacement for CArrayObj and CObject
nicholish en: Also, if anyone has a better way please let me know.
nicholish en, thank you for your example... just one add-in to it (that works for me - (?) though I lack explanation for such behavior yet):
if MyObjArrayis being declared globally & used in different functions (e.g. OnInit & OnCalculate in indicator) & deleted in OnDeinit, then:
MyObjArray *arr; int OnInit (){ arr = new MyObjArray; int num = 1; for(int i=0;i<SymbolsTotal(false)&&i<10;i++) { // ... each obj assigning & pushing to arr... Print ("pushed " , arr[i].ToString()); // <<<<<<<<< MEMBER-METHOD CALL } }
and in OnCalculate:
int OnCalculate (/*...*/ ) { //--- for (int i=0 ; i<arr.Total(); i++) Print ("pop " , GetPointer (arr)[i].ToString()); // <<<<<<<<< MEMBER-METHOD CALL /* int i=0; while (i<arr.Total()) { arr.Next(); i++; Print(arr[i].ToString()); // invalid pointer access } */ //--- return value of prev_calculated for next call return (rates_total); }in the second function - member-method is called only with GetPointer, otherwise "invalid pointer access "-error... while in the first function (where the arr is filled) its elements' member function can still be called from the object itself
- docs.mql4.com
After a lot of confusion, it seems the proper way to save/load CArrayObj is to create two new classes like the following modified code. The object array has to be pre-Reserve() and is large enough to load all data.
#include <Arrays\ArrayObj.mqh> //+------------------------------------------------------------------+ class MyObj : public CObject { public: string name; double price; datetime time; bool Save(const int file_handle) // MUST BE OVERRIDDEN FOR LOAD/SAVE { if(!FileWriteString(file_handle,name,10)) return false; if(!FileWriteDouble(file_handle,price)) return false; if(!FileWriteLong(file_handle,(long)time))return false; return true; } bool Load(const int file_handle) // MUST BE OVERRIDDEN FOR LOAD/SAVE { ResetLastError(); name = FileReadString(file_handle,10); price= FileReadDouble(file_handle); time = (datetime)FileReadLong(file_handle); if (0 != GetLastError()) return false; return true; } int Compare(const CObject *node,const int mode=0) const // MUST BE OVERRIDDEN FOR SORT { MyObj *other = (MyObj*)node; if(this.price > other.price) return 1; if(this.price < other.price) return -1; return 0; } }; //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ class MyObjArray : public CArrayObj { public: bool CreateElement(const int index) //MUST BE OVERRIDDEN FOR LOAD/SAVE { // assume array has been pre-Reserve() and is large enough. // dont use Insert() or Add() because these will increase the internal counter twice! m_data[index] = new MyObj; return (NULL != m_data[index]); } // Save() and Load() is already implemented! };
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use
I wasn't able to find any documentation on how to override the CObject virtual methods Load, Save, and Compare to make use of(CList and CArrayObj) load, save, and sort features - so I managed to hack my way through it. I'm posting it here in case anyone else is interested in using the Save/Load/Sort methods in CArrayObj and CList. Also, if anyone has a better way please let me know.