The process implies that there should be a header file (.mqh) with all required stuff, which could be sufficient to include into a mq4-file, and after changing its extension to .mq5 compilation should produce a ready-made indicator for MetaTrader 5.
Of course, the mqh-file should contain some non-OOP defines and functions. For example, here is a part of them:
#define StrToDouble StringToDouble #define DoubleToStr DoubleToString #define TimeToStr TimeToString #define Ask SymbolInfoDouble(_Symbol, SYMBOL_ASK) #define Bid SymbolInfoDouble(_Symbol, SYMBOL_BID) #define Digits _Digits #define Point _Point #define extern input ... int ObjectsTotal() { return ObjectsTotal(0); } bool ObjectCreate(const string name, const ENUM_OBJECT type, const int subwindow, const datetime time1, const double price1) { return ObjectCreate(0, name, type, subwindow, time1, price1); }; ... bool ObjectSetText(const string name, const string text, const int fontsize = 0) { bool b = ObjectSetString(0, name, OBJPROP_TEXT, text); if(fontsize != 0) ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontsize); return b; } ... int iBarShift(string symbol, ENUM_TIMEFRAMES timeframe, datetime time, bool Exact = true) { datetime lastBar; SeriesInfoInteger(symbol, timeframe, SERIES_LASTBAR_DATE, lastBar); return(Bars(symbol, timeframe, time, lastBar) - 1); } ... long iVolume(string symbol, ENUM_TIMEFRAMES tf, int b) { long result[1]; return CopyTickVolume(symbol, tf, b, 1, result) > 0 ? result[0] : 0; } ... bool _SetIndexBuffer(const int index, double &buffer[], const ENUM_INDEXBUFFER_TYPE type = INDICATOR_DATA) { bool b = ::SetIndexBuffer(index, buffer, type); ArraySetAsSeries(buffer, true); ArrayInitialize(buffer, EMPTY_VALUE); // default filling return b; } #define SetIndexBuffer _SetIndexBuffer void SetIndexStyle(const int index, const int type, const int style = EMPTY, const int width = EMPTY, const color clr = clrNONE) { PlotIndexSetInteger(index, PLOT_DRAW_TYPE, type); if(style != EMPTY) PlotIndexSetInteger(index, PLOT_LINE_STYLE, style); if(width != EMPTY) PlotIndexSetInteger(index, PLOT_LINE_WIDTH, width); if(clr != clrNONE) PlotIndexSetInteger(index, PLOT_LINE_COLOR, clr); }
All of these is obvious. You'll find complete code in the attachment.
First, remember MT4 accessors for timeseries - Open[], High[], Low[], Close[], etc. We can mimic this by objects with overloaded operator[]. For example, for volume data we can use the class:
class VolumeBroker { public: long operator[](int b) { return iVolume(_Symbol, _Period, b); } }; VolumeBroker Volume;
Actually, as all these classes are very similar to each other, one can generate them by preprocessor using one define:
#define DefineBroker(NAME,TYPE) \ class NAME##Broker \ { \ public: \ TYPE operator[](int b) \ { \ return i##NAME(_Symbol, _Period, b); \ } \ }; \ NAME##Broker NAME; DefineBroker(Time, datetime); DefineBroker(Open, double); DefineBroker(High, double); DefineBroker(Low, double); DefineBroker(Close, double); DefineBroker(Volume, long);
Second, remember that object property setter function ObjectSet has changed in MT5 to a set of functions ObjectSetInteger, ObjectSetDouble, etc, accepting different number of parameters. For example, mql4-code:
ObjectSet(name, OBJPROP_TIME1, Time[0]); ObjectSet(name, OBJPROP_PRICE1, price);
should be somehow translated into mql5:
ObjectSetInteger(0, name, OBJPROP_TIME, 0, Time[0]); ObjectSetDouble(0, name, OBJPROP_PRICE, 0, price);
It's important that there are no constants such as OBJPROP_TIME1 and OBJPROP_PRICE1 in mql5, and instead of OBJPROP_TIME1, OBJPROP_TIME2, OBJPROP_TIME3 one need to use the single constant OBJPROP_TIME and additional index. Price-related constants changed in the same way. How to emulate this? Well, by objects. Let's define 2 classes for properties of integer and double types (other types you can add yourself).
class OBJPROP_INTEGER_BROKER { public: ENUM_OBJECT_PROPERTY_INTEGER p; int i; OBJPROP_INTEGER_BROKER(const ENUM_OBJECT_PROPERTY_INTEGER property, const int modifier) { p = property; i = modifier; } }; class OBJPROP_DOUBLE_BROKER { public: ENUM_OBJECT_PROPERTY_DOUBLE p; int i; OBJPROP_DOUBLE_BROKER(const ENUM_OBJECT_PROPERTY_DOUBLE property, const int modifier) { p = property; i = modifier; } }; OBJPROP_INTEGER_BROKER OBJPROP_TIME1(OBJPROP_TIME, 0); OBJPROP_DOUBLE_BROKER OBJPROP_PRICE1(OBJPROP_PRICE, 0); OBJPROP_INTEGER_BROKER OBJPROP_TIME2(OBJPROP_TIME, 1); OBJPROP_DOUBLE_BROKER OBJPROP_PRICE2(OBJPROP_PRICE, 1); OBJPROP_INTEGER_BROKER OBJPROP_TIME3(OBJPROP_TIME, 2); OBJPROP_DOUBLE_BROKER OBJPROP_PRICE3(OBJPROP_PRICE, 2);
Look how constants OBJPROP_TIMEn/OBJPROP_PRICEn "became" the objects storing corresponding index inside. Now we can implement new ObjectSet functions, and pass broker objects as parameters:
bool ObjectSet(const string name, const OBJPROP_INTEGER_BROKER &property, const long value) { return ObjectSetInteger(0, name, property.p, property.i, value); } bool ObjectSet(const string name, const OBJPROP_DOUBLE_BROKER &property, const double value) { return ObjectSetDouble(0, name, property.p, property.i, value); }
Voila!
ObjectSet(name, OBJPROP_TIME1, Time[0]); ObjectSet(name, OBJPROP_PRICE1, price);
works again in MetaTrader 5.
Please note that a couple of lines of code should be added manually anyway, because some completely new language structures have been introduced in MetaTrader 5, so there is no chance to generate them based on mql4 source code.
Let us consider a process of convertion of several indicators.
Let's take standard ZigZag.mq4 from MetaQuotes (specifically, dated 2014). You should add indicator_plots directive and change indicator_buffers number:
#property indicator_plots 1 // added #property indicator_buffers 3 // was 1
because this is how MetaTrader 5 adds auxiliary buffers, and IndicatorBuffers is not supported anymore (it should be deleted in the source code). Also lines:
SetIndexBuffer(1, ExtHighBuffer); SetIndexBuffer(2, ExtLowBuffer);
should be edited as follows:
SetIndexBuffer(1, ExtHighBuffer, INDICATOR_CALCULATIONS); SetIndexBuffer(2, ExtLowBuffer, INDICATOR_CALCULATIONS);
As the indicator uses new style of event handlers (OnInit, OnCalculate) add MT4_NEW_EVENT_HANDLERS define before include:
#define MT4_NEW_EVENT_HANDLERS #include <ind4to5.mqh>
That's it - the indicator is ready to compile (3 lines added, 1 removed, 2 changed).
#property indicator_plots 1
and
#include <ind4to5.mqh>
Probably not all portable features specific for MetaTrader 4 are covered in the attached mqh-file and not all possible replacements are provided for MetaTrader 5. Feel free to extend and alter the file as you need.