banner



How To Make Forex Robot


One More Time about the MQL5 Wizard

The globe effectually us is changing apace, and we attempt to continue upwardly with information technology. We practise not have time to larn something new, and this is a normal attitude of a normal human being. Traders are people just similar everyone else, they want to go maximum results for the minimum of try. Specially for traders, MetaEditor 5 offers a wonderful MQL5 Sorcerer. At that place are several articles describing how to create an automated trading system using the wizard, including a "calorie-free version" MQL5 Magician for Dummies and a "version from developers " - MQL5 Wizard: New Version.

It all seems skilful - a trading robot is created in 5 mouse clicks, y'all can examination it in the Strategy Tester and optimize the parameters of a trading system, you can let the resulting robot trade on your account without the need to do annihilation else manually. But the trouble arises when the a trader/MQL5 developer wants to create something of his own, something unique which has never been described anywhere, and is going to write his own module of trading signals. The trader opens the MQL5 documentation, gets to the Standard Library, and is horrified to encounter...

5 Terrible Classes

True, the MQL5 Wizard greatly simplifies the creation of Expert Advisors, only first you need to learn what will be used every bit input for it. To automatically create an Expert Counselor using the MQL5 Wizard, brand sure that its components adhere to five bones classes of the section Base Classes of Expert Advisors:

  • CExpertBase is a base class for iv other classes.
  • CExpert is the class for creating a trading robot; this is the class that trades.
  • CExpertSignal is a class for creating a module of trading signals; the article is about this class.
  • CExpertTrailing is a form for trailing a protecting Stop Loss.
  • CExpertMoney is the coin management class.

Here is the whole strength of the "great and terrible" arroyo that is called Object-oriented programming (OOP). But don't be afraid, at present most everyone has a prison cell telephone with lots of office, and most no ane knows how information technology works. We do not need to study all this, we will only talk over some functions of the CExpertSignal grade.

In this article nosotros will become through the stages of creating a module of trading signals, and you will come across how to do this without having to learn OOP or the classes. Simply if you want, you can go a lilliputian further then.

1. Creating a Class from Scratch

Nosotros will not alter any existing module of trading signals to our needs, because it's the fashion to get confused. Therefore, nosotros will simply write our ain class, but commencement we will use the Navigator to create a new folder to store our signals in MQL5/Include/Proficient/.


Right-click on the folder we take created, select "New File" and create a new class for our module of trading signals.


Fill in the fields:

  • Class Name - the name of the class. This will be a module for generating signals at the intersection of two moving averages, then let'south name it MA_Cross.
  • Base Name is the class from which our class is derived. And we should derive it from the base form CExpertSignal.

Click "Finish" and a draft of our module usa ready. It's all east then far. We only need to add the #include declaration to the resulting file then that the compiler knows where to observe the base of operations course CExpertSignal

          #include            "..\ExpertSignal.mqh"                  

The effect:

                         #property copyright            "Copyright 2012, MetaQuotes Software Corp."                    #property link"https://world wide web.mql5.com"                    #property version            "1.00"                    #include            "..\ExpertSignal.mqh"                                course          MA_Cross :          public          CExpertSignal   {          private:          public:                      MA_Cross();                     ~MA_Cross();   };    MA_Cross::MA_Cross()   {   }    MA_Cross::~MA_Cross()   {   }         

Check the resulting class (it must be free of compilation errors) and click F7. In that location are no errors and we can movement on.

ii. A Handle to the Module

Our class is completely empty, it has no errors and we tin can exam it - let's try to create a new Skillful Counselor in the MQL5 Wizard based on it. Nosotros reach the step of selecting a module of trading signals and see ... that our module is non there.


And how can it be there? We practise not add any indications for the MQL5 Wizard to sympathise that our class could exist something useful. Permit's fix this. If you await at the modules of the standard bundle, you'll come across that each of them contains a header at the beginning of the file. This is the handle of the module compiled according to certain rules. And the rules are very simple.

Open, for example, the source code of the module of AMA based trading signals (see the logic clarification in Signals of the Adaptive Moving Average.) And run the MQL5 Magician choosing this module. Compare:

The concluding block in the handle refers to the module parameters, the first line contains the name of the module to be displayed in the MQL5 Magician. As you can run into, at that place is cypher complicated. Thus, the handle of each module contains the following entries:

  • Title - the module name to exist shown in the MQL5 Wizard.
  • Type - the version of the module of signals. It must always exist SignalAdvanced.
  • Name - the proper noun of the module later on its is selected in the MQL5 Sorcerer and is used in comments for describing internal parameters of the generated Expert Advisor (preferably specified).
  • ShortName - a prefix for automatic naming of external parameters in the generated Expert Advisor (in the grade of Signal_<ShortName>_<ParameterName>).
  • Grade - the proper noun of the, which is contained in the module.
  • Page - a parameter to become Help for this module (only for modules from the standard delivery).

Side by side comes the description of the parameters in the form of Parameter=list_of_values, in which the following is specified (comma-separated):

  1. The name of the office to ready the value of the parameter when starting the Expert Advisor.
  2. The parameter type tin exist enumeration.
  3. The default value for the parameter, i.e. the value that volition be ready to the parameter, if yous do not change information technology in the MQL5 Sorcerer.
  4. Description of the parameter, which you run into when you start the Practiced Counselor generated in the MQL5 Wizard.

Now, knowing all this, let'due south create the handle of our module of trading signals. So, nosotros are writing a module for getting trading signals at the intersection of two moving averages. We need to prepare at least four external parameters:

  • FastPeriod - the menses of the fast moving boilerplate
  • FastMethod - the type of smoothing of the fast moving average
  • SlowPeriod - the period of the slow moving average
  • SlowMethod - the type of smoothing of the deadening moving average

Y'all could also add a shift and the type of prices to calculate each of the moving averages, but it does not change annihilation fundamentally. Then the electric current version is as follows:

                              

The module handle is ready, and we accept described the following in it:

  1. The proper noun displayed in the MQL5 Wizard - "Signals at the intersection of two moving averages".
  2. Four external parameter to configure the trading signals.
    • FastPeriod - the period of the fast moving average with the default value of xiii.
    • FastMethod - the type of smoothing of the fast moving average, uncomplicated smoothing by default.
    • SlowPeriod - the period of the dull moving average with the default value of 21.
    • SlowMethod - the blazon of smoothing of the ho-hum moving average, simple smoothing by default.

Save the changes and compile. There should non exist whatsoever errors. Run the MQL5 Wizard to check. You see, our module is now available for selection, and it shows all of our parameters!


Congratulations, our module of trading bespeak looks great now!

iii. Methods for Setting Parameters

Now it is time to work with the external parameters. Since our trading module is represented by the form MA_Cross, so its parameters must be stored within the same class as private members. Let's add 4 lines (equal to the number of parameters) to the class declaration. We've already described the parameter in the handle and know the following:

          class          MA_Cross :          public          CExpertSignal   {          private:                          int               m_period_fast;                int               m_period_slow;                ENUM_MA_METHOD    m_method_fast;                ENUM_MA_METHOD    m_method_slow;        

But how practice the values ​​of the external parameters of the module appear in the appropriate members of our course MA_Cross? Information technology's all very simple, yous only need to declare public methods of the same proper noun in the course, namely, to add four lines to the public section:

          class          MA_Cross :          public          CExpertSignal   {          individual:              int               m_period_fast;              int               m_period_slow;        ENUM_MA_METHOD    m_method_fast;        ENUM_MA_METHOD    m_method_slow;              public:                          MA_Cross();                         ~MA_Cross();                          void              FastPeriod(int            value)               { m_period_fast=value;        }            void              FastMethod(ENUM_MA_METHOD            value)    { m_method_fast=value;        }            void              SlowPeriod(int            value)               { m_period_slow=value;        }            void              SlowMethod(ENUM_MA_METHOD            value)    { m_method_slow=value;        }          };

When you generate an Good Advisor on the ground of this module using the MQL5 Wizard and run it on the chart, these 4 methods are automatically chosen when initializing the Skillful Advisor. So here is a simple rule:

The rule of parameter creation in the module - for each parameter that we accept declared in the handle, we should create a private member in the form for storing its value and a public member for setting a value to information technology. The method proper name must match the name of the parameter.

And the last moment is to set default values ​​for our parameters that will be used in case the methods of value setting are not chosen. Each declared variable or class fellow member must be initialized. This technique allows to avoid many of hard-to-find errors.

For automatic initialization, the best suiting 1 is the class constructor; it is always the start one to be called when creating an object. For default values, we volition use those written in the module handle.

          class          MA_Cross :          public          CExpertSignal   {          private:              int               m_period_fast;              ENUM_MA_METHOD    m_method_fast;               int               m_period_slow;              ENUM_MA_METHOD    m_method_slow;               public:                          MA_Cross(void);                         ~MA_Cross(void);             MA_Cross::MA_Cross(void) : m_period_fast(xiii),                                m_method_fast(MODE_SMA),                                m_period_slow(21),                                          m_method_slow(MODE_SMA)                              {   }

Here the class members are initialized using the initialization list.

As yous can see, we haven't used moving average indicators withal. We found a unproblematic rule - as many parameters are stated in the handle of the module, so many methods and members should be in the grade that implements the module. There is zip complicated! All the same, don't forget to set default values of parameters on the constructor.

4. Check the Definiteness of Input Parameters

We have created parameters for our trading module, written methods for setting values ​​to them, and now comes the next important phase - the correctness of parameters must be checked. In our instance, we must check the periods of moving averages and the type of smoothing for their calculation. For this purpose you should write your own ValidationSettings() method in the form. This method is divers in the parent class CExpertBase, and in all its children it is obligatorily redefined.

Simply if you do not know anything about object-oriented programming, just remember - in our form we should write the ValidationSettings() function, which requires no parameters and returns true or false.

          class          MA_Cross :          public          CExpertSignal   { ...                          MA_Cross(void);                         ~MA_Cross(void);                          bool              ValidationSettings();          ...    };             bool          MA_Cross:: ValidationSettings()   {                          if(!CExpertSignal::ValidationSettings())return(false);                    if(m_period_fast<1          || m_period_slow<1)      {PrintFormat("Wrong value set for one of the periods! FastPeriod=%d, SlowPeriod=%d",                   m_period_fast,m_period_slow);return          false;      }           if(m_period_fast>m_period_slow)      {PrintFormat("SlowPeriod=%d must exist greater than FastPeriod=%d!",                   m_period_slow,m_period_fast);return          false;      }           if(m_method_fast!=MODE_SMA          && m_method_fast!=MODE_EMA          && m_method_fast!=MODE_SMMA          && m_method_fast!=MODE_LWMA)      {PrintFormat("Invalid blazon of smoothing of the fast MA!");return          false;      }           if(m_method_slow!=MODE_SMA          && m_method_slow!=MODE_EMA          && m_method_slow!=MODE_SMMA          && m_method_slow!=MODE_LWMA)       {PrintFormat("Invalid type of smoothing of the tiresome MA!");return          false;      }           return          true;   }

Equally you can encounter, in the public part of the MA_Cross grade we've added declaration of the ValidationSettings() method, and then added the method body in the following form:

          bool          MA_Cross:: ValidationSettings()

Offset comes the return type, then the class name, then scope resolution operator ::, and all this is followed by the proper noun of the previously alleged method. Practise not forget that the name and blazon of parameters must lucifer in the declaration and description of the class method. However, the compiler will warn you of such an error.

Note that beginning the base course method is chosen, and then input parameters are checked.

            if(!CExpertSignal::ValidationSettings())return(false);         

If you do not add this line, the generated Expert Advisor will non exist able to initialize our module of trading signals.

5. Where Are Our Indicators?

Information technology's time to work with the indicators, since all the preparatory work with the parameters for them have been completed. Each module of trading signals contains the InitIndicators() method, which is automatically called when you run the generated Expert Advisor. In this method, we must provide indicators of moving averages for our module.

Offset, declare the InitIndicators() method in the class and paste its draft:

          public:                          MA_Cross(void);                         ~MA_Cross(void);              void              FastPeriod(int          value)               { m_period_fast=value;        }          void              FastMethod(ENUM_MA_METHOD          value)    { m_method_fast=value;        }          void              SlowPeriod(int          value)               { m_period_slow=value;        }          void              SlowMethod(ENUM_MA_METHOD          value)    { m_method_slow=value;        }              bool              ValidationSettings();                          bool              InitIndicators(CIndicators *indicators);          }; ...               bool          MA_Сross::          InitIndicators(CIndicators* indicators)          {           if(indicators==NULL)          render(false);           if(!CExpertSignal::InitIndicators(indicators))          return(faux);           ... Some code here                    return(true);   }

So in that location is nothing complicated, we declare the method and so simply create the method body, every bit nosotros accept done for the ValidationSettings() method. Above all, do not forget to insert the class name and the operator :: in the function definition. We have a draft, which we can insert into a code to create moving averages. Allow's do this properly - for each indicator we create a split role in the grade, which returns true if successful. The function can have any name, but permit it reflect its purpose, then let's call the functions CreateFastMA() and CreateSlowMA().

          protected:              boolCreateFastMA(CIndicators *indicators);          boolCreateSlowMA(CIndicators *indicators);          };               bool          MA_Cross::InitIndicators(CIndicators *indicators)   {           if(indicators==Nil)          return(false);           if(!CExpertSignal::InitIndicators(indicators))          render(false);                       if(!CreateFastMA(indicators))return(faux);            if(!CreateSlowMA(indicators))render(false);                    render(true);   }             bool          MA_Cross::CreateFastMA(CIndicators *indicators)   {          ... Some code                    return(true);   }             bool          MA_Cross::CreateSlowMA(CIndicators *indicators)   {                                                    return(truthful);   }

That's all, we only need to write code that generates the MA indicators and somehow integrates the handles of these indicators into the trading module, and then that the module can employ the values ​​of these indicators. That is why a pointer to a variable of type CIndicators is passed every bit a parameter. The following is written in Documentation about it:

The CIndicators is a grade for collecting instances of timeseries and technical indicators classes. The CIndicators class provides cosmos of instanced of technical indicator classes, their storage and management (information synchronization, handle and retention management).

This means that nosotros must create our indicators and identify them in this collection. Since merely indicators of the CIndicator grade and its children can be stored in the collection, we should apply this fact. Nosotros will utilise CiCustom, which is the above mentioned child. For each moving average we declare an object of blazon CiCustom in the individual role of the class:

          class          MA_Cross :          public          CExpertSignal   {          private:             CiCustom          m_fast_ma;            CiCustom          m_slow_ma;                    int              m_period_fast;             ENUM_MA_METHOD    m_method_fast;              int              m_period_slow;             ENUM_MA_METHOD    m_method_slow;

Of course, you tin can create your ain indicator class, which will exist derived from CIndicator, and implement all the necessary methods for employ with the MQL5 Wizard. But in this instance we desire to bear witness how you tin can use whatever custom indicator in the module of trading signals using CiCustom.

Here'south how it looks in the code:

                       bool          MA_Cross::CreateFastMA(CIndicators *indicators)   {           if(indicators==NULL)          return(imitation);           if(!indicators.Add(GetPointer(m_fast_ma)))      {printf(__FUNCTION__+": Error calculation an object of the fast MA");return(false);      }                      MqlParam            parameters[4];     parameters[0].type=TYPE_STRING;    parameters[0].string_value="Examples\\Custom Moving Boilerplate.ex5";    parameters[1].type=TYPE_INT;    parameters[i].integer_value=m_period_fast;          parameters[2].type=TYPE_INT;    parameters[2].integer_value=0;                      parameters[3].type=TYPE_INT;    parameters[3].integer_value=m_method_fast;                   if(!m_fast_ma.Create(m_symbol.Name(),m_period,IND_CUSTOM,4,parameters))      {printf(__FUNCTION__+": Error initializing the object of the fast MA");return(imitation);      }             if(!m_fast_ma.NumBuffers(one))            render(false);                    render(true);   }

In the CreateFastMA() method, start check the pointer of the collection of indicators, and then add a pointer of the fast MA m_fast_ma to this collection. Then declare the MqlParam construction, which is particularly designed for storing parameters of custom indicators, and fill it with values.

Nosotros use Custom Moving Average from the standard terminal delivery pack as the custom MA indicator. The name of the indicator must be indicated relative to the binder data_folder/MQL5/Indicators/. Since Custom Moving Average.mq5' from the standard package is located in data_folder/MQL5/Indicators/Examples/, we specify its path including the Examples binder:

parameters[0].string_value="Examples\\Custom Moving Boilerplate.ex5";

If yous await at the lawmaking for this indicator, you can run into all the required data:

            input          int            InpMAPeriod=thirteen;                 input          int            InpMAShift=0;                   input          ENUM_MA_METHOD          InpMAMethod=MODE_SMMA;

The values ​​of the structure incorporate the type-value pairs:

  1. parameter type - cord (to transfer the name of the indicator)
  2. the proper noun of the executable file of the custom indicator - "Custom Moving Averages.exe"
  3. parameter type - int (value of the period)
  4. menstruation of the moving boilerplate
  5. parameter type - int (shift value)
  6. horizontal shift of the average in bars
  7. parameter blazon - int (enumeration value is an integer)
  8. method of averaging

Afterward filling the structure, the indicator is initialized past the Create() method of all the required parameters: symbol name and the timeframe on which information technology is calculated, the blazon of the indicator from the ENUM_INDICATOR enumeration, the number of indicator parameters and the MqlParam structure with parameter values. And the final i is specifying the number of indicator buffers using the NumBuffers() method.

The CreateSlowMA() method for creating the dull moving average is simple. When using custom indicators in the module, do not forget that the Expert Advisor generated past the MQL5 Wizard will likewise run in the tester. So at the kickoff of our file we add the property #property tester_indicator that communicates to the tester the location of required indicators:

          #include            "..\ExpertSignal.mqh"                    #property tester_indicator            "Examples\\Custom Moving Average.ex5"                  

If nosotros use several dissimilar indicators, we should add this line for each of them. So, nosotros take added the indicators. For more convenience, allow's provide ii methods of receiving MA values:

             bool              ValidationSettings(void);              bool              InitIndicators(CIndicators *indicators);                          double            FastMA(const            int            index)            const            {            render(m_fast_ma.GetData(0,index)); }            double            SlowMA(const            int            alphabetize)            const            {            return(m_slow_ma.GetData(0,index)); }        

As y'all tin see, the methods are very simple, they used the GetData() method of the SIndicator parent class, which returns a value from the specified indicator buffer at the specified position.

If you lot need classes for working with classical indicators of the standard package, they are bachelor in section Classes for working with indicators. We are ready to proceed to the final stage.


half-dozen. Define the LongCondition and ShortCondition Methods

Everything is ready to brand our module work and generate trading signals. This functionality is provided by two methods that must exist described in each child of CExpertSignal:

  • LongCondition() checks the buy conditions and returns the force of the Long signal from 0 to 100.
  • ShortCondition() - checks the sell condition and returns the force of the Short signal from 0 to 100 .

If the part returns a null value, it means that in that location is no trading signal. If in that location are conditions for the bespeak, then yous can estimate the strength of the signal and return whatsoever value non exceeding 100. Evaluation of the betoken strength allows you to flexibly build trading systems based on several modules and market models. Read more than nearly this in MQL5 Sorcerer: New Version.

Since we are writing a elementary module of trading signals, we tin can agree that the buy and sell signals are valued equally (100). Allow'southward add necessary methods in the form declaration.

   ...          bool              InitIndicators(CIndicators *indicators);              double            FastMA(const          int          index)          const          {          return(m_fast_ma.GetData(0,index)); }          double            SlowMA(const          int          index)          const          {          return(m_slow_ma.GetData(0,index)); }                          virtual            int       LongCondition();            virtual            int       ShortCondition();        

As well, let's create the description of functions. This is how the buy betoken is checked (information technology's nonetheless with the sell signal):

                       int          MA_Cross::LongCondition()   {          int          point=0;                       int            idx=StartIndex();                    double          last_fast_value=FastMA(idx);          double          last_slow_value=SlowMA(idx);           double          prev_fast_value=FastMA(idx+1);          double          prev_slow_value=SlowMA(idx+i);           if((last_fast_value>last_slow_value) && (prev_fast_value<prev_slow_value))      {       point=100;       }           return(betoken);   }

Note that we take declare the idx variable, to which the value returned by the StartIndex() office of the parent class CExpertBase is assigned. The StartIndex() office returns 0, if the Expert Counselor is designed to piece of work on all ticks, and in this case the analysis starts with the current bar. If the Expert Advisor is designed to work at open prices, StartIndex() returns 1 and the analysis starts with the final formed bar.

By default StartIndex() returns one, which means that the Skilful Advisor generated by the MQL5 Wizard will simply run at the opening of a new bar and will ignore incoming ticks during formation of the current bar.

How to activate this way and how it can be used will exist described later in the finishing stroke.

The module is prepare for use, and then let'southward create a trading robot in the MQL5 Wizard based on this module.

Checking an Expert Counselor in the Tester

To examination the efficiency of our module, let's generate an Expert Advisor based on it in the MQL5 Wizard and run it on the chart. The "Inputs" tab of the appeared get-go window contains the parameters of the MA_Cross module.

All other parameters have as well been added by the MQL5 Wizard while generating the EA based on the selected money management module and position maintenance module (Trailing Stop). Thus, we just had to write a module of trading signals and received a fix solution. This is the master reward of using the MQL5 Wizard!

Now allow's test the trading robot in the MetaTrader 5 Strategy Tester. Let'southward try to run a quick optimization of key parameters.

In these settings of input parameters, more than half a 1000000 of passes is required for full optimization. Therefore, nosotros choose fast optimization (genetic algorithm) and additionally utilize MQL5 Cloud Network to accelerate the optimization. The optimization has been done in 10 minutes and we take got the results.


Equally you tin can run into, creating a trading robot in MQL5 and optimization of input parameters have taken much less time than would be required for writing the position management servicing logic, debugging and searching for the best algorithms.

Finishing Stroke

You lot can skip this item or become back to it after when you lot are completely comfy with the technique of writing a module of trading signals.

If you lot open the source code of the Adept Advisor generated by the MQL5 Wizard, you will find the global variable Expert_EveryTick with the fake value. Based on this variable, the StartIndex() function returns its value. It communicates to the Skillful Advisor the mode it should run in.

                         #property copyright            "Copyright 2012, MetaQuotes Software Corp."                    #property link"https://www.mql5.com"                    #belongings version            "1.00"                                #include <Expert\Expert.mqh>                    #include <Adept\MySignals\MA_Cross.mqh>                    #include <Expert\Trailing\TrailingNone.mqh>                    #include <Skillful\Money\MoneyFixedLot.mqh>                       input          string         Expert_Title             ="TestMA_Cross";            ulong               Expert_MagicNumber       =22655;                                bool                  Expert_EveryTick             =false;                     input          int            Signal_ThresholdOpen     =x;                       input          int            Signal_ThresholdClose    =10;        

If you set up Expert_EveryTick truthful and compile the code, the trading robot volition analyze each incoming tick, and thus make decisions on the values ​​of the current incomplete bar. Exercise this merely if you understand how it works. Non all trading systems are designed to work inside the bar.

You can also add together a keyword input for the Expert_EveryTick parameter, and and then you will have a new input parameter of the Expert Counselor, which you tin ready at the EA startup on a chart or in the tester:

          input          bool          Expert_EveryTick         =false;        

And now it'southward time to summarize what we have done.

half-dozen Steps to Create a Module of Trading Signals

If you have mastered MQL5, and so yous no longer demand to write an Expert Counselor from scratch. Just create a module of trading signals and, based on this module, automatically generate a trading robot with the enabled trailing and trade volume direction modules. And even if you are non familiar with OOP or practise not want to delve much into the structure of merchandise classes, you tin can just go through 6 steps:

  1. Create a new course using the MQL5 Wizard in a separate folder MQL5/Include/MySignals/. Our module of trading signals will be stored in that location.
  2. Create a module handle that describes the parameters, their type and default values.
  3. Declare module parameters in the form and add methods for initialization in the constructor.
  4. Check the input parameters and do not forget to telephone call ValidationSettings() of the CExpertSignal base class.
  5. Create indicator-objects and add a predefined initialization method InitIndicators().
  6. Identify atmospheric condition of trading signals in the methods LongCondition() and ShortCondition().

Each footstep is simple and requires little skill in MQL5 programming. You only demand to write your module once, following the instructions, and further verification of any trade thought volition take no more than an hour, without tiring hours of coding and debugging.

From Simple to Complex

Remember that the trading strategy implemented by your trading robot created using the MQL5 Wizard, is as circuitous equally the module of trading signals it uses. But before y'all first to build a complex trading system based on a set of rules for entry and exit, split up information technology into several unproblematic systems and check each ane separately.

Based on simple modules you can create complex trading strategies using the fix-made modules of trading signals, but this is a topic for another article!

Source: https://www.mql5.com/en/articles/367

Posted by: mottandook.blogspot.com

0 Response to "How To Make Forex Robot"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel