Evaluating Urban Towers Strategy (Source Code)

Evaluating Urban Towers Strategy (Source Code)

26 January 2017, 23:44
Besarion Turmanauli
3
1 354

Hello, it's Besso and I'm excited to test a very special fully automatic EA based on a famous Urban Forex Scalping strategy - Urban Towers, which I give you for FREE!

Main entry criteria being fully customizable you can adjust the strategy to your needs and to particular instruments setting everything from moving average periods to minimum tower quantity.

I like this strategy but I never was disciplined enough to practice it over a long period of time when I first saw the video describing its rules,  jumping from one strategy to another after seeing a couple of losses instead. 

Looking at how popular and  highly respected the strategy is really makes me curious how it really performs without human intervention and emotions.

So I decided to test it with Tick Data Suite on real variable spreads + swap + commissions and see the results - is it as profitable as it seems from authors' examples or it has little to no value.

Let's go: 

 

What were initial backtest results? 

I made sure I applied everything exactly as required by strategy's rules and then launched it on last 4 year real srpread backtest (EURUSD) and interestingly enough EA consistently lost money over this period. That's not a bad sign as far as this consistency remains intact on longer time periods (at least the same pair), therefore I enhanced expert to be able to reverse trades. I have only 87 trades on EURUSD H1 chart (4 years) and I still need more data to figure out whether these results are random or have something to do with reality.

here is a statement after reversing trades on EURUSD:

EURUSD Reversed 

 

 same settings, great results on EURGBP as well: 

EURGBP Reversed 

 

and on USDCAD:  

 

 

but results are random on most other pairs...  Unless we have consistency on USDCAD, EURGBP and EURUSD, any profits made are 

a product of luck, but if we successfully add another few years with similar results, there may be some substance to it. 


and the results are random (EURUSD 2010-2013): 

 

 

 Applying trailing stop doesn't help either, meaning it's not just exits that make trades random, but entries themselves. 

Anyways at least you know you don't have to waste time on strategies like this...  

//+------------------------------------------------------------------+
//|                                                 Urban Towers.mq4 |
//|                             Copyright 2017, Besarion Turmanauli. |
//|                       https://www.mql5.com/en/users/tobeone/blog |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Besarion Turmanauli."
#property link      "https://www.mql5.com/en/users/tobeone/blog"
#property version   "1.00"
#property strict
#include "..\..\Classes\wte.mqh" //double-u (w) trade engine
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
enum STOP_METHOD
  {
   STOP_CANDLE=0,//(0) Stop Loss on Candle High/Low
   STOP_MA=1,//(1) Stop Loss on Moving Average
   STOP_COMBINED=2 //(2) Stop Loss on Farthest Among (0) and (1)
  };

input bool reverse=false;
input int fastestMAPeriod=37;
input ENUM_MA_METHOD fastestMAMethod=MODE_EMA;
input ENUM_APPLIED_PRICE fastestMAAppliedPrice=PRICE_CLOSE;
input int slowestMAPeriod=50;
input ENUM_MA_METHOD slowestMAMethod=MODE_EMA;
input ENUM_APPLIED_PRICE slowestMAAppliedPrice=PRICE_CLOSE;
input int towerCount=3;
input double minMAInterDistPoints=0;
input double maxMAInterDistPoints=30;

input string stx="";// 
input int utOrderMagic=444333;
input string utOrderComment="urbanTowers";
input double trailingStop = 0;
input double trailingStep = 0;

input double fixedLot=0.1;
input double tradeRiskPercent=2;
input STOP_METHOD stopLoss=0;
input double expandSLPoints=50; //expand SL by fixed Points (negative value = contraction)
input double minSLPoints= 250;
input double tpToSlRatio=0.5;

wte engine;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

   RefreshRates();
  
   engine.trailPositions(utOrderMagic,trailingStop,trailingStep);
  
//-- get some market data right here...
   double fastestMA=iMA(NULL,Period(),fastestMAPeriod,0,fastestMAMethod,fastestMAAppliedPrice,1);
   double slowestMA=iMA(NULL,Period(),slowestMAPeriod,0,slowestMAMethod,slowestMAAppliedPrice,1);
   double highestMA=MathMax(fastestMA,slowestMA);
   double lowestMA=MathMin(fastestMA,slowestMA);
//- end getting some market data

//sell criteria
/*
     moving averages aren't congested
     we have a touch[1] on one of the MAs closing below all MAs
     Low[2] < Low[1]; Low[3] < Low[2]
     Close[1] < lowestMA, Close[2]<lowestMA, Close[3]<lowestMA
     */


   if(//SELL
      OrdersTotal()==0 &&
      //shorts==0 && // checking if there are no other trades
      fastestMA<(slowestMA-minMAInterDistPoints*Point) && //checking trend direction and MA congestion
      fastestMA>(slowestMA-maxMAInterDistPoints*Point) && //checking MA interdistance maximum
      High[1]>=lowestMA && //checking touch signal
      towerCheck("short",towerCount,highestMA) //checking towers and that no bars have been closed above MA
      )
     {
      if((MarketInfo(Symbol(),MODE_BID)-Low[1])>=MarketInfo(Symbol(),MODE_STOPLEVEL)*2*Point)
        {//not needed in case of reversed trades

         double slPts=NormalizeDouble((High[1]-Low[1])/Point,0)+expandSLPoints;
         if(stopLoss==1)
           {//highestMA
            slPts=NormalizeDouble((highestMA-Low[1])/Point,0)+expandSLPoints;
              }else if(stopLoss==2){//farthest between 0 and 1
            slPts=NormalizeDouble((MathMax(highestMA,High[1])-Low[1])/Point,0)+expandSLPoints;
           }

         double tpPts=NormalizeDouble(slPts*tpToSlRatio,0);
         double vol=fixedLot;

         if(slPts>=minSLPoints)
           {//Min sl check
            if(!reverse)
              {
               if(tradeRiskPercent>0)vol=engine.getVolume(tradeRiskPercent,slPts);
               engine.openTrade(OP_SELLSTOP,Low[1],vol,slPts,tpPts,utOrderComment,utOrderMagic,(TimeCurrent()+Period()*60-15));

                 }else{//reverse trades here
               if(tradeRiskPercent>0)vol=engine.getVolume(tradeRiskPercent,tpPts);
               engine.openTrade(OP_BUYLIMIT,Low[1],vol,tpPts,slPts,utOrderComment,utOrderMagic,(TimeCurrent()+Period()*60-15));
              }
           }//end checking min sl points

        }//end checking for stoplevel
     }//end checking sell criteria

//-- move entry point at a highest low // OK
//-- delete entry point if a candle closes above highestMA //OK



//buy criteria
/*
     moving averages aren't congested
     we have a touch[1] on one of the MAs closing above all MAs
     High[2] > High[1]; High[3] > High[2]
     Close[1] > highestMA, Close[2]>highestMA, Close[3]>highestMA
     */

   if(//BUY
      OrdersTotal()==0 &&
      //longs==0 && //checking if there are no other trades
      fastestMA>slowestMA+minMAInterDistPoints*Point && //checking trend direction and MA congestion
      fastestMA<slowestMA+maxMAInterDistPoints*Point && //checking max MA interdistance
      Low[1]<=highestMA && //checking touch signal
      towerCheck("long",towerCount,lowestMA) //checking towers and that no bars have been closed below MA
      )
     {
      if((High[1]-MarketInfo(Symbol(),MODE_ASK))>=MarketInfo(Symbol(),MODE_STOPLEVEL)*2*Point)
        {//not needed in case of reversed trades
         double slPts=NormalizeDouble((High[1]-Low[1])/Point,0)+expandSLPoints;

         if(stopLoss==1)
           {//lowestMA
            slPts=NormalizeDouble((High[1]-lowestMA)/Point,0)+expandSLPoints;
              }else if(stopLoss==2){//farthest between 0 and 1
            slPts=NormalizeDouble((High[1]-MathMin(lowestMA,Low[1]))/Point,0)+expandSLPoints;
           }

         double tpPts=NormalizeDouble(slPts*tpToSlRatio,0);
         double vol=fixedLot;

         if(slPts>=minSLPoints)
           {//check min sl points
            if(!reverse)
              {
               if(tradeRiskPercent>0)engine.getVolume(tradeRiskPercent,slPts);
               engine.openTrade(OP_BUYSTOP,High[1],vol,slPts,tpPts,utOrderComment,utOrderMagic,(TimeCurrent()+Period()*60-15));

                 }else{//reverse

               if(tradeRiskPercent>0)engine.getVolume(tradeRiskPercent,tpPts);
               engine.openTrade(OP_SELLLIMIT,High[1],vol,tpPts,slPts,utOrderComment,utOrderMagic,(TimeCurrent()+Period()*60-15));

              }
           }//end checking min sl points
        }//end checking stoplevel distance
     }//end checking for buying criteria

//-- move entry point at a lowest High // OK
//-- delete entry point if a candle closes below lowestMA  //OK

  }//end of ontick
//+------------------------------------------------------------------+

bool towerCheck(string direction,int count,double closeCheckPrice)
  {
   bool retu=true;

   double curLow=Low[1];
   double curHigh=High[1];

   if(direction=="short")
     {//if trade direction is short

      for(int i=2; i<=count; i++)
        {
         if(
            Low[i]<curLow &&
            Close[i]<closeCheckPrice //highestMA
            )
           {
            curLow=Low[i];
              }else{
            retu=false;
            break;
           }

        }

     }//end direction = short

   if(direction=="long")
     {//if trade direction is long

      for(int i=2; i<=count; i++)
        {
         if(
            High[i]>curHigh &&
            Close[i]>closeCheckPrice //lowestMA
            )
           {
            curHigh=High[i];
              }else{
            retu=false;
            break;
           }

        }

     }//end direction = long

   return(retu);
  }
//+------------------------------------------------------------------+


Source code includes proprietary classes for order management (trade engine) but you don't have to use them, you can use the ex4 file from attachments (in order to test and optimize EA on different pairs) or modify the code and open / modify positions with default MQL4 functions. 

 

Thanks for your time!

Besso

 

Other Posts For Further Reading:

Ultimate Divergence Trader (source Code): https://www.mql5.com/en/blogs/post/689222

Reviewing W12 Quad Turbo v3 Live Performance: Part 1: https://www.mql5.com/en/blogs/post/688967

Metaeditor Dark Theme: https://www.mql5.com/en/blogs/post/687043

Traders' Dynamic Index EA (Source Code): https://www.mql5.com/en/blogs/post/686791 


Files:
Share it with friends: