MQL5 : Un expert demonstrativ de portofoliu folosind medii mobile

[English version] [MQLmagazine.com in english] [Editia romaneasca]

Am scris acest expert ca o demonstratie completa privind scrierea expertilor de portofoliu, urmarind principiile prezentate in Linii directoare pentru scrierea expertilor de portofoliu in MQL5.

Expertul va lucra pe timeframe-ul ales, numai cand o bara noua se formeaza pe fiecare instrument participant. Dupa verificarea intersectiilor de medii mobile, va administra pozitiile. Exista si optiunea de a filtra dupa volatilitate (am reparat indicatorul BBoverSTD din articolul Analiza volatilitatii : de la previziunea volatilitatii la previziunea pretului).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//+------------------------------------------------------------------+
//|                                                  PortfolioMA.mq5 |
//|                                       Copyright Bogdan Caramalac |
//|                                           http://mqlmagazine.com |
//+------------------------------------------------------------------+
#property copyright "Copyright Bogdan Caramalac"
#property link      "http://mqlmagazine.com"
#property version   "1.00"
 
#define FLAT        0
#define LONG        1
#define SHORT       2
 
input int Slippage=30;
input double MarginUsagePerPosition=0.5;
input bool VolatilityFilter=false;
input int StopLoss=300;
input int TakeProfit=1000;
 
input int SlowMALength=14;
input int FastMALength=9;

Aceasta sectiune defineste constantele pentru lucrul cu pozitii (FLAT,LONG,SHORT) si parametrii sistemului.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int SymbolsCount;
 
struct IndicatorHandlersStruct
  {
   int SlowMovingAverageHandler;
   int FastMovingAverageHandler;
   int BBoverSTDHandler;
  };
 
struct SymbolData
  {
    string Symbol;
    IndicatorHandlersStruct IndicatorHandlers;
    datetime LastBarTime;    
  };
 
SymbolData SymbolsTable[30];

Aceasta sectiune defineste structura de date care retine simbolurile si handlerele indicatorilor.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
void MakeIndicatorHandlers()
  {
   int error1,error2,error3;
   for (int i=0;i<SymbolsCount;i++)
      {
       error1=0;
       error2=0;
       error3=0;
       SymbolsTable[i].IndicatorHandlers.SlowMovingAverageHandler=iMA(SymbolsTable[i].Symbol,Period(),SlowMALength,0,MODE_SMA,PRICE_OPEN);
       Print("SymbolsTable[",i,"].IndicatorHandlers.SlowMovingAverageHandler=",SymbolsTable[i].IndicatorHandlers.SlowMovingAverageHandler);
       if (SymbolsTable[i].IndicatorHandlers.SlowMovingAverageHandler<0)
         { 
          Print("Invalid handle.");
          error1=GetLastError();
          Print("Error ",error1," while initializing iMA(",SlowMALength,") for ",SymbolsTable[i].Symbol);
         }
       SymbolsTable[i].IndicatorHandlers.FastMovingAverageHandler=iMA(SymbolsTable[i].Symbol,Period(),FastMALength,0,MODE_SMA,PRICE_OPEN);
       Print("SymbolsTable[",i,"].IndicatorHandlers.FastMovingAverageHandler=",SymbolsTable[i].IndicatorHandlers.FastMovingAverageHandler);
       if (SymbolsTable[i].IndicatorHandlers.FastMovingAverageHandler<0)
         {
          Print("Invalid handle.");
          error2=GetLastError();
          Print("Error ",error2," while initializing iMA(",FastMALength,") for ",SymbolsTable[i].Symbol);
         }
       SymbolsTable[i].IndicatorHandlers.BBoverSTDHandler=iCustom(SymbolsTable[i].Symbol,Period(),"BBoverSTD",20,10,1.5,"SMA",2,0);
       Print("SymbolsTable[",i,"].IndicatorHandlers.BBoverSTDHandler=",SymbolsTable[i].IndicatorHandlers.BBoverSTDHandler);
       if (SymbolsTable[i].IndicatorHandlers.BBoverSTDHandler<0)
         { 
          Print("Invalid handle.");
          error3=GetLastError();
          Print("Error ",error3," while initializing BBoverSTD for ",SymbolsTable[i].Symbol);
         }                   
      }
   return;
  }

Aceasta este procedura MakeIndicatorHandlers() adaptata nevoilor acestui expert.

Acum urmeaza UnitsToLots(), GetPositionType(), PositionSetSLTP() si ManagePosition() care sunt cele prezentate in Linii directoare pentru scrierea expertilor de portofoliu in MQL5.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
string MASignal(int asset_index)
  {
   double maslowarray[1];
   double mafastarray[1];
   double mafast,maslow;
   datetime t;
   int copycount1,copycount2;
   int error1=0;
   int error2=0;
   string signal;
   copycount1=CopyBuffer(SymbolsTable[asset_index].IndicatorHandlers.SlowMovingAverageHandler,0,0,1,maslowarray);
   if (copycount1==-1)
     {
      Print("Nothing copied (1). Retrieving error.");
      error1=GetLastError();
     }
   copycount2=CopyBuffer(SymbolsTable[asset_index].IndicatorHandlers.FastMovingAverageHandler,0,0,1,mafastarray);
   if (copycount2==-1)
     {
      Print("Nothing copied (2). Retrieving error.");
      error2=GetLastError();
     }
   maslow=NormalizeDouble(maslowarray[0],SymbolInfoInteger(SymbolsTable[asset_index].Symbol,SYMBOL_DIGITS));
   mafast=NormalizeDouble(mafastarray[0],SymbolInfoInteger(SymbolsTable[asset_index].Symbol,SYMBOL_DIGITS));   
   t=TimeCurrent();
   if (maslow<mafast)
     signal="L";
   if (maslow==mafast)
     signal="-";
   if (maslow>mafast)
     signal="S";
   return(signal);
  }

Aceasta intoarce semnalul mediei mobile pentru activul solicitat. Semnalul poate fi “L”,”S” sau “-”.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
void TradeLogics(int asset_index)
  {
   string s;
   double stddev[1];
   double bbhi[1];
   double bblo[1];
   int copycount;
   s=MASignal(asset_index);   
   if (VolatilityFilter==true)
     {
      copycount=CopyBuffer(SymbolsTable[asset_index].IndicatorHandlers.BBoverSTDHandler,0,0,1,stddev);
      copycount=CopyBuffer(SymbolsTable[asset_index].IndicatorHandlers.BBoverSTDHandler,2,0,1,bbhi);
     }
   if (GetPositionType(asset_index)==FLAT)
     {      
      if (s=="L")
        {
         if (VolatilityFilter==true)
           {
            if (stddev[0]>bbhi[0])
              ManagePosition(asset_index,LONG);
           }
         else
           ManagePosition(asset_index,LONG);
        }
      if (s=="S")
        {
         if (VolatilityFilter==true)
           {
            if (stddev[0]>bbhi[0])
              ManagePosition(asset_index,SHORT);
           }
         else
           ManagePosition(asset_index,SHORT);
        }
     }//else if (GetPositionType(asset_index)==FLAT)
   else//position is not flat
     {
      if (GetPositionType(asset_index)==LONG)
        {
         if (VolatilityFilter==true)
           {
            if (s=="S"||stddev[0]<bbhi[0])
              ManagePosition(asset_index,FLAT);
           }           
         else
           {
            if (s=="S")
              ManagePosition(asset_index,FLAT);           
           }
        }
      if (GetPositionType(asset_index)==SHORT)
        {
         if (VolatilityFilter==true)
           {
            if (s=="L"||stddev[0]<bbhi[0])
              ManagePosition(asset_index,FLAT);
           }
         else
           {
            if (s=="L")
              ManagePosition(asset_index,FLAT);           
           }        
        }
     }           
   return;
  }

Aceasta este TradeLogics(). Mai intai verifica daca VolatilityFilter e activat, si numai in acest caz apeleaza indicatorul BBoverSTD, pentru a face economie de timp in situatia in care filtrul este dezactivat. Apoi testeaza daca avem pozitie inchisa. Daca da, cumpara sau vinde dupa semnalul MASignal(), eventual filtrat de valorile BBoverSTD, daca VolatilityFilter e activat. Daca avem pozitie lunga sau scurta deja, verifica aparitia unui semnal contrar (desigur, cu aceeasi filtrare). Daca un semnal contrar vine, pozitia este inchisa, altfel nu are loc nicio adaptare a pozitiei la noul capital (desi ManagePosition() o poate face, nu i se cere).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   string crtsymbol;
   SymbolsTable[0].Symbol="EURUSD";
   SymbolsTable[1].Symbol="USDJPY";
   SymbolsTable[2].Symbol="GBPUSD";
   SymbolsTable[3].Symbol="USDCHF";
   SymbolsCount=1;
   MakeIndicatorHandlers();
   return(0);
  }

Acesta este OnInit(). Este deja setat sa lucreze cu 4 perechi valutare. Daca SymbolsCount ramane 1, numai EURUSD va fi tranzactionat. Daca il setezi la 4, toate cele patru perechi vor fi tranzactionate. Poti modifica SymbolsTable[] dupa dorinta (probleme pot aparea totusi la tranzactionarea CFD-urilor datorita metodei de calcul a lotului). OnInit() trebuie sa se incheie cu un apel la MakeIndicatorHandlers() pentru a face indicatorii gata de utilizare.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   datetime datetime_array[1];
   for (int i=0;i<SymbolsCount;i++)
      {
       CopyTime(SymbolsTable[i].Symbol,Period(),0,1,datetime_array);
       if (datetime_array[0]!=SymbolsTable[i].LastBarTime)
         {
          //Print(SymbolsTable[i].Symbol," :: ",TimeToString(SymbolsTable[i].LastBarTime,TIME_DATE)," ",TimeToString(SymbolsTable[i].LastBarTime,TIME_MINUTES),"  vs. ",TimeToString(datetime_array[0],TIME_DATE)," ",TimeToString(datetime_array[0],TIME_SECONDS) );
          TradeLogics(i);
          SymbolsTable[i].LastBarTime=datetime_array[0];          
         }//if (datetime_array[0]!=LastBarOccurred[i])
      }//for (int i=0;i<SymbolsCount;i++)
   return;
  }

Acesta este OnTick(). OnTick() contine ciclul de simboluri, si solicita timpul ultimei bare de pe timeframe-ul curent la simbolul curent dat de ciclu. Daca acest timp este diferit decat cel retinut, inseamna ca o noua bara a aparut pe acel chart. Apoi este apelat TradeLogics() pentru decizie si apel la ManagePosition().

1
2
3
4
5
6
7
8
9
10
11
12
13
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   for (int i=0;i<SymbolsCount;i++)
      {   
       IndicatorRelease(SymbolsTable[i].IndicatorHandlers.SlowMovingAverageHandler);
       IndicatorRelease(SymbolsTable[i].IndicatorHandlers.FastMovingAverageHandler);
       IndicatorRelease(SymbolsTable[i].IndicatorHandlers.BBoverSTDHandler);
      }
   return;
  }

Acesta este OnDeinit(). Elibereaza handlerele indicatorilor. Daca OnInit() ar rula din nou, ar face alte handlere. Nu este complet necesar sa facem asta, dar risipirea memoriei cu handlere noi de fiecare data cand ruleaza OnInit() nu se poate numi programare curata.

Urmatoarele backtesturi au fost realizate fara StopLoss si TakeProfit, de asemenea si cu VolatilityFilter dezactivat.

Dupa cum se poate vedea, portofolizarea nu a fost o alegere buna. A adaugat instabilitate aditionala si caderi mai adanci. Totusi, acum avem de-a face cu sisteme de trading, in vreme ce teoria managementului portfofoliului presupune in general o strategie de tip “long or out”. De aceea, in teoria financiara, corelatiile negative scad riscurile, deoarece plusurile la unele active se compenseaza cu minusuri la altele. Dar, ceea ce avem acum e un portofoliu de sisteme de trading. Ne putem gandi la un sistem de trading ca o functie de equity a instrumentelor:

equity = sistem de trading(instrument)

Pe doua active corelate perfect negativ, functia sistem de trading va intoarce doua serii de equity corelate perfect pozitiv. O aplicare riguroasa ar cere ca o selectie Markowitz sau CAPM asupra sistemelor de trading sa fie facuta pe outputuri de sisteme de trading in loc sa fie facuta pe datele de piata.

Vezi de asemenea si articolul desprevirtualizarea sistemelor de trading, care priveste problema dintr-un alt punct de vedere.

Link:
PortfolioMA.mq5

Editii