New article Cross-Platform Expert Advisor: Signals has been published:
This article discusses the CSignal and CSignals classes which will be used in cross-platform expert advisors. It examines the differences between MQL4 and MQL5 on how particular data needed for evaluation of trade signals are accessed to ensure that the code written will be compatible with both compilers.
All the other calculations needed for the generation of the current
signal were moved to the CSignal and CSignals objects. Thus, all we need
to do is to have CSignals perform a check, and then gets its output by
invoking its methods CheckOpenLong and CheckOpenShort. The following
screen shots show the results of testing the expert on MetaTrader 4 and
Author: Enrico Lambino
Hi Enrico. I have just been looking at your work in search of a solution to my problem. On compiling the StopBase class, I get a list of errors, please see below
implicit conversion from 'number' to 'string' OrderStopBase.mqh 395 25
implicit conversion from 'number' to 'string' OrderStopBase.mqh 467 26
implicit enum conversion OrderStopBase.mqh 468 33
implicit enum conversion OrderStop.mqh 37 33
implicit conversion from 'number' to 'string' OrderStop.mqh 44 43
'COrderBase' - import not defined OrderStop.mqh 54 7
'else' - semicolon expected OrderStop.mqh 81 4
')' - unexpected token OrderStop.mqh 81 46
implicit conversion from 'number' to 'string' StopBase.mqh 767 22
implicit conversion from 'number' to 'string' StopBase.mqh 774 28
implicit conversion from 'number' to 'string' StopBase.mqh 796 22
implicit conversion from 'number' to 'string' StopBase.mqh 803 28
implicit conversion from 'number' to 'string' StopBase.mqh 698 36
'OrderType' - cannot convert enum Stop.mqh 227 52
'StopLossCustom' - no one of the overloads can be applied to the function call Stop.mqh 228 61
could be one of 2 function(s) Stop.mqh 228 61
double CStopBase::StopLossCustom(const string,const ENUM_ORDER_TYPE,const double) StopBase.mqh 136 22
bool CStopBase::StopLossCustom() StopBase.mqh 99 22
'OrderType' - cannot convert enum Stop.mqh 215 71
'TakeProfitCustom' - no one of the overloads can be applied to the function call Stop.mqh 215 98
could be one of 2 function(s) Stop.mqh 215 98
double CStopBase::TakeProfitCustom(const string,const ENUM_ORDER_TYPE,const double) StopBase.mqh 140 22
bool CStopBase::TakeProfitCustom() StopBase.mqh 111 22
implicit conversion from 'number' to 'string' Stop.mqh 250 39
implicit enum conversion Stop.mqh 291 31
implicit conversion from 'number' to 'string' Stop.mqh 292 36
Ignoring the warnings, the errors seem to come from order type issues. Also, compiling your example signal_ma yields the following errors
'GetPointer' - parameter conversion not allowed OrderStopVirtualBase.mqh 51 39
'GetPointer' - parameter conversion not allowed OrderStopVirtualBase.mqh 58 41
'=' - type mismatch OrderStopsBase.mqh 106 23
'=' - type mismatch OrderStopsBase.mqh 108 23
'=' - type mismatch OrderStopsBase.mqh 110 23
'=' - type mismatch OrderStopsBase.mqh 180 20
'=' - type mismatch OrderStopsBase.mqh 182 20
'=' - type mismatch OrderStopsBase.mqh 184 20
Where are these errors coming from please and how can I resolve them? On related issue, from my understanding, the Liskov Substitution stipulates that we should program to an interface as the super class can be substituted with its base class. Why have you chosen to pass objects into the functions(which are in themselves interfaces), instead of using their base classes? I might be wrong but I believe that would be best practice would it not?
Hi Shephard, thank you for your comment. Regarding your questions:
Thanks very much for your quick response. I understand your reasoning behind passing concrete classes instead of their base classes. Certain implementations that are straightforward in C++ are just not possible in MQL, but that is the way it is.
With regards to type mismatches, no, I did not change anything. I simply downloaded and compiled. I was looking at your work in search of an annoying problem I had in my own system. Your approach is really great.
I developed my system like this. I considered a signal outputting system as a strategy. Say I have an MA crossover system, if I create an object of this with say EURUSD, this is a strategy. I add this strategy into a list of strategies. I can create another strategy, and add it to the list. But the strategies are called not by OnTick, rather, they are part of an Observer pattern. They are updated, or called when specific events occur, say a new bar on 5 Minute, a new 10 Pip Renko bar etc. I had problems with the Stops, which triggered my research.
Thanks once again for taking time to share your knowledge and skill, and your prompt response.
Thanks for sharing your suggestions and insights.
I think what you are working on is a great idea. In am currently working on the last few articles for this series (order stops and stops included). I am not sure if the Stops classes are a good fit (it depends on your implementation). If you can make them work independently of the order manager, that would be good because it would help simplify your class objects. But I hope that you may find them to be useful in your work.
Regarding the compilation:
I'm testing your signals module. I've got one signal SHORT and one signal NEUTRAL. How come I finally got CMD_VOID? When CMD_VOID appears?
Actually this appears in CSignalsBase::Check
There are only 2 signals. Previous signal was CMD_SHORT. Current signal is CMD_NEUTRAL. Can you confirm that CMD_SHORT and CMD_NEUTRAL give as a result CMD_VOID?
If my first signal was CMD_NEUTRAL and the second signal CMD_SHORT the total signal would be CMD_SHORT. But if the first signal CMD_SHORT and the second CMD_NEUTRAL it gives CMD_VOID.
I guess it must be like this:
else if(m_signal_open!=signal_open && signal_open!=CMD_NEUTRAL)
m_signal_open = CMD_NEUTRAL;
This is not fully correct in terms of close signals. Why there cannot be two consequent same direction close signals?
For example I have entry signal Short, next exit signal Long (closing short), next entry signal Long, next entry signal Short, next exit signal Long. So the last signal for exit short position will not work since it's the same direction as previous exit signal.
It depends on what you wanted to achieve on how the signals should be evaluated.
The last code you posted is only executed when the class member m_new_signal is set to true. This is for trading on new signals only. You can set this protected class member by using the method NewSignal available in the class.
I know that. But if I set it to false it will also affect my entry signals. It's ok for entry signal but not for exit since exit rules can be different. It can be reversion or exit signal so 2 same direction exit signals is a normal thing.
It looks like in m_signal_close is saved previous signal value in SignalBase.mqh. For example I have some exit signal. If it checks it and Calculate() method returns false it gives last signal value which was at previous exit.
I am sorry for overlooking your first question. I had not noticed it until now. It was a very good feedback.
Thank you for pointing this out. Yes, you are right. I will update the code and revise the article.
You have a point here. But for example, if I only want to give an exit signal at an MA crossover, this would be needed. I think separating them would be a better option than the current code:
m_signal_open = CMD_NEUTRAL;
The Calculate method is a virtual method. If the method is to return false, you should reset those class members to neutral from within the method itself.