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:
same settings, great results on EURGBP as well:
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