Code in MQL5 the Harry Browne Permanent Portfolio ( Backtest ETF )

 

Hello,

I have the project of coding in MQL5 Harry Browne's Permanent Portfolio in the form of an Expert Advisor.

This will allow me to test it with ETFs.

https://www.metaquotes.net/en/company/news/5123


My motivations :
Backtest myself the Harry Browne Permanent Portfolio.
Test the multi-product backtest feature of MetaTrader 5.

Presentation of Harry Browne's Permanent Portfolio:

Which ETFs to choose? :
I propose these 4 ETF, GLD, SHY, TLT, VTI, indicated in this article:
Quote:
These assets are (for US Investors) Stocks, Long Term Treasury Bonds, Treasury Money Market Funds and Gold. So, a 4-asset Perm Port might have VTI, TLT, SHY, and GLD.


I have just started, there are many mistakes.
If you wish to participate, you are welcome to post an improved version in a new message.

//+------------------------------------------------------------------+
//|                                         eaPermanentPortfolio.mq5 |
//|                                   Copyright 2018, Pierre Rougier |
//|                                    http://www.apprendre-mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, Pierre Rougier"
#property link      "http://www.apprendre-mql5.com"
#property version   "1.00"

#include <Trade\Trade.mqh>

CTrade myTradingControlPanel;

input  int  capital_dedicated_to_strategy=10000;

input string stocks= "VTI";
input string bonds ="TLTUS";
input string cash = "SHY";
input string gold = "GLD";

input int      magicNumber=12345;

int year_saved=0;
bool is_trades_already_open=false;

double total_current_value=0;
double current_value_stocks= 0;
double current_value_bonds = 0;
double current_value_cash = 0;
double current_value_gold = 0;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   MqlDateTime mql_datetime;
   TimeToStruct(iTime(_Symbol,_Period,0),mql_datetime);
   year_saved=mql_datetime.year;
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   if(isNewYear())
     {
      if(!is_trades_already_open)
        {StartStrategy();}
      else
        {Rebalance();}
     }
  }
//+------------------------------------------------------------------+
bool isNewYear()
  {

   MqlDateTime mql_datetime;
   TimeToStruct(iTime(_Symbol,_Period,0),mql_datetime);

   if(mql_datetime.year!=year_saved)
     {
      year_saved=mql_datetime.year;
      return true;
     }
   else
     {return false;}
  }
//+------------------------------------------------------------------+
void StartStrategy()
  {
   int amount=capital_dedicated_to_strategy/4;

   OpenPosition(stocks,amount);
   OpenPosition(bonds,amount);
   OpenPosition(cash,amount);
   OpenPosition(gold,amount);

  }
//+------------------------------------------------------------------+
void Rebalance()
  {
   ComputeValuePositionsByAssetsClass();
   total_current_value=current_value_stocks+current_value_bonds+current_value_cash+current_value_gold;
   double target_value=total_current_value/4;

  }
//+------------------------------------------------------------------+
void OpenPosition(string asset_class_type,int amount)
  {
   double  ask=SymbolInfoDouble(asset_class_type,SYMBOL_ASK);
   double   quantity_to_buy=NormalizeDouble((amount/ask),0);

// Open a Buy (i.e. long) position
   myTradingControlPanel.PositionOpen(asset_class_type,ORDER_TYPE_BUY,quantity_to_buy,
                                      0,
                                      //                                      SymbolInfoDouble(asset_class_type,SYMBOL_ASK),
                                      0,0,"Buy Trade. Magic Number #"
                                      +(string) myTradingControlPanel.RequestMagic());
// Request is completed or order placed
   if(myTradingControlPanel.ResultRetcode()==TRADE_RETCODE_PLACED || myTradingControlPanel.ResultRetcode()==TRADE_RETCODE_DONE)
     {
      Print("Entry rules: A ",asset_class_type," Buy order has been successfully placed with Ticket#: ",myTradingControlPanel.ResultOrder());
     }
   else
     {
      Print("Entry rules: A ",asset_class_type," Buy order request could not be completed. Error: ",GetLastError());
     }
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void ComputeValuePositionsByAssetsClass()
  {
// Loop through all open positions 
   for(int i=PositionsTotal()-1; i>=0; i--)
     {
      ulong ticketNumber=0;
      // This selects open position by the order they appear in our list of positions
      ticketNumber=PositionGetTicket(i);
      if(ticketNumber==0)
        {
         Print("Exit rules: Position exists but not selected. Error: ",GetLastError());
         ResetLastError();
         continue;
        }

      if(PositionGetInteger(POSITION_MAGIC)==magicNumber)
        {
         string symbol=PositionGetString(POSITION_SYMBOL);

         if(symbol==stocks)
           {
            current_value_stocks=current_value_stocks+PositionGetDouble(POSITION_PRICE_CURRENT);
           }
         else if(symbol==bonds)
           {
            current_value_bonds=current_value_bonds+PositionGetDouble(POSITION_PRICE_CURRENT);
           }
         else if(symbol==cash)
           {
            current_value_cash=current_value_cash+PositionGetDouble(POSITION_PRICE_CURRENT);
           }
         else if(symbol==gold)
           {
            current_value_gold=current_value_gold+PositionGetDouble(POSITION_PRICE_CURRENT);
           }
        }
     }

  };
//+------------------------------------------------------------------+
Permanent Portfolio
Permanent Portfolio
  • Investopedia Staff
  • www.investopedia.com
The permanent portfolio is an investment portfolio designed to perform well in all economic conditions. It was devised by free-market investment analyst Harry Browne in the 1980s. The permanent portfolio is composed of equal allocation of stocks, bonds, gold and cash or Treasury bills. BREAKING DOWN 'Permanent Portfolio' The permanent portfolio...
 

There are still a lot of bugs


//+------------------------------------------------------------------+
//|                                         eaPermanentPortfolio.mq5 |
//|                                   Copyright 2018, Pierre Rougier |
//|                                    http://www.apprendre-mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, Pierre Rougier"
#property link      "http://www.apprendre-mql5.com"
#property version   "1.00"

#include <Trade\Trade.mqh>

CTrade myTradingControlPanel;

input  int  capital_dedicated_to_strategy=10000;

input string stocks= "VTI";
input string bonds ="TLTUS";
input string cash = "SHY";
input string gold = "GLD";

input int      magicNumber=12345;

int year_saved=0;
bool is_trades_already_open=false;

double total_current_value=0;
double target_value=0;
double current_value_stocks= 0;
double current_value_bonds = 0;
double current_value_cash = 0;
double current_value_gold = 0;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   MqlDateTime mql_datetime;
   TimeToStruct(iTime(_Symbol,_Period,0),mql_datetime);
   year_saved=mql_datetime.year;
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   if(isNewYear())
     {
      if(!is_trades_already_open)
        {StartStrategy();}
      else
        {RebalanceAllAssetsClass();}
     }
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool isNewYear()
  {

   MqlDateTime mql_datetime;
   TimeToStruct(iTime(_Symbol,_Period,0),mql_datetime);

   if(mql_datetime.year!=year_saved)
     {
      year_saved=mql_datetime.year;
      return true;
     }
   else
     {return false;}
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void StartStrategy()
  {
  
   target_value=capital_dedicated_to_strategy/4;

   Print("/*§§*/ total_current_value :"+DoubleToString(total_current_value));
   Print("/*§§*/ target_value :"+DoubleToString(target_value));

   Rebalance(stocks,0);
   Rebalance(bonds,0);
   Rebalance(cash,0);
   Rebalance(gold,0);

   is_trades_already_open=true;

  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void RebalanceAllAssetsClass()
  {
   ComputeValuePositionsByAssetsClass();
   total_current_value=current_value_stocks+current_value_bonds+current_value_cash+current_value_gold;
   target_value=total_current_value/4;

   Print("/*§§*/ total_current_value :"+DoubleToString(total_current_value));
   Print("/*§§*/ target_value :"+DoubleToString(target_value));

   Rebalance(stocks,current_value_stocks);
   Rebalance(bonds,current_value_bonds);
   Rebalance(cash,current_value_cash);
   Rebalance(gold,current_value_gold);

  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void Rebalance(string asset_class_type,double current_value_asset)
  {

   double amount=0;
   int  quantity=0;
   double  ask=SymbolInfoDouble(asset_class_type,SYMBOL_ASK);

   if(current_value_asset>target_value)
     {
      amount=current_value_asset-target_value;
      quantity=(int)NormalizeDouble((amount/ask),0);
      if(quantity>0)
        {
         SellSurplusAssetClass(asset_class_type,quantity);
        }
     }
   else
     {
      amount=target_value-current_value_asset;
      quantity=(int)NormalizeDouble((amount/ask),0);
      if(quantity>0)
        {
         BuyMissingPartAssetClass(asset_class_type,quantity);
        }
     }

  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void BuyMissingPartAssetClass(string asset_class_type,int quantity)
  {

// Open a Buy (i.e. long) position
   myTradingControlPanel.PositionOpen(asset_class_type,ORDER_TYPE_BUY,quantity,
                                      0,
                                      //                                      SymbolInfoDouble(asset_class_type,SYMBOL_ASK),
                                      0,0,"Buy Trade. Magic Number #"
                                      +(string) myTradingControlPanel.RequestMagic());
// Request is completed or order placed
   if(myTradingControlPanel.ResultRetcode()==TRADE_RETCODE_PLACED || myTradingControlPanel.ResultRetcode()==TRADE_RETCODE_DONE)
     {
      Print("Entry rules: A ",asset_class_type," Buy order has been successfully placed with Ticket#: ",myTradingControlPanel.ResultOrder());
     }
   else
     {
      Print("Entry rules: A ",asset_class_type," Buy order request could not be completed. Error: ",GetLastError());
     }
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void SellSurplusAssetClass(string asset_class_type,int quantity)
  {
// Close position
   myTradingControlPanel.PositionOpen(asset_class_type,ORDER_TYPE_SELL,quantity,
                                      0,
                                      //                                      SymbolInfoDouble(asset_class_type,SYMBOL_ASK),
                                      0,0,"Sell Trade. Magic Number #"
                                      +(string) myTradingControlPanel.RequestMagic());
// Request is completed or order placed
   if(myTradingControlPanel.ResultRetcode()==TRADE_RETCODE_PLACED || myTradingControlPanel.ResultRetcode()==TRADE_RETCODE_DONE)
     {
      Print("Entry rules: A ",asset_class_type," Sell order has been successfully placed with Ticket#: ",myTradingControlPanel.ResultOrder());
     }
   else
     {
      Print("Entry rules: A ",asset_class_type," Sell order request could not be completed. Error: ",GetLastError());
     }
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void ComputeValuePositionsByAssetsClass()
  {
// Loop through all open positions 
   for(int i=PositionsTotal()-1; i>=0; i--)
     {
      ulong ticketNumber=0;
      // This selects open position by the order they appear in our list of positions
      ticketNumber=PositionGetTicket(i);
      if(ticketNumber==0)
        {
         Print("Exit rules: Position exists but not selected. Error: ",GetLastError());
         ResetLastError();
         continue;
        }

      if(PositionGetInteger(POSITION_MAGIC)==magicNumber)
        {
         string symbol=PositionGetString(POSITION_SYMBOL);

         if(symbol==stocks)
           {
            current_value_stocks=current_value_stocks+PositionGetDouble(POSITION_PRICE_CURRENT);
           }
         else if(symbol==bonds)
           {
            current_value_bonds=current_value_bonds+PositionGetDouble(POSITION_PRICE_CURRENT);
           }
         else if(symbol==cash)
           {
            current_value_cash=current_value_cash+PositionGetDouble(POSITION_PRICE_CURRENT);
           }
         else if(symbol==gold)
           {
            current_value_gold=current_value_gold+PositionGetDouble(POSITION_PRICE_CURRENT);
           }
        }
     }

  };
//+------------------------------------------------------------------+


Reason: