Description
I have written an implementation of the Ultron robot for the cTrader platform.
THIS ROBOT WORKS ONLY FOR GBPUSD H1.
The Ultron robot is an idea developed by Pawel Gawron.
This strategy is explained in this thread of the forum of ForexFactory:
https://www.forexfactory.com/thread/840339-my-ultron-ea-for-gbpusd-h1-timeframe
To understand the strategy read the whole thread, the posts by Pawel Gawron with more attention, and this post in particular:
https://www.forexfactory.com/thread/post/11677971#post11677971
Before you ask questions about the robot, make sure that you have read and understood the whole thread in the forum and the manual of the robot too.
The robot uses GMT times, and automatically takes into account the shift of one hour in the summer months (daylight saving time). That is, the allowed trading hours are shifted one hour earlier in summer.
The robot is profitable in my backtest between January 2014 and September 2020. The backtest has been done with high quality tick data, starting capital 10000 EUR and fixed lot size 0.01.
This robot, with source code and input parameter files and manual, can be downloaded from this link:
https://drive.google.com/drive/folders/1_sEDeLZQm5CZTzKCJIFOt_5qWgam41C3?usp=sharing
If you know how to program and you manage to make the robot and this strategy to be profitable in other symbols, please let us know in the comments.
/*
Ultron cBot for GBPUSD H1
Thread in forum
https://www.forexfactory.com/thread/840339-my-ultron-ea-for-gbpusd-h1-timeframe
Video
https://youtu.be/y1Vs7AiYlpQ
Download robot, manual and input parameters files from this link
https://drive.google.com/drive/folders/1_sEDeLZQm5CZTzKCJIFOt_5qWgam41C3?usp=sharing
*/
using System;
using System.Linq;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
using cAlgo.Indicators;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class UltroncBot : Robot
{
// Choose if you want to use fixed lot size or risk %
[Parameter("Method", Group = "Money management", DefaultValue = MMType.FIXED_LOT)]
public MMType ParamMoneyMgmntType { get; set; }
// fIXED LOT SIZE
[Parameter("Lot size", Group = "Money management", DefaultValue = 0.01, MinValue = 0.01)]
public double ParamLotSize { get; set; }
// pERCENT OF THE ACCOUNT THAT YOU WANT TO RISK IN EACH OPERATION
[Parameter("Risk %", Group = "Money management", DefaultValue = 2, MinValue = 0.1, MaxValue = 50.0)]
public double ParamRisk { get; set; }
// ===============================================
// Do not touch these values
[Parameter("Stop loss (pips)", Group = "TP & SL", DefaultValue = 55)]
public double ParamStopLossPips { get; set; }
private double ParamStopLoss;
[Parameter("Take profit (pips)", Group = "TP & SL", DefaultValue = 68)]
public double ParamTakeProfitPips { get; set; }
private double ParamTakeProfit;
// ===============================================
// This robot works best during the London and NY session, 4.00 to 21.00 GMT.
[Parameter("Use restricted trading hours", Group = "Trading hours (GMT)", DefaultValue = true)]
public bool ParamRestrictedTradingHours { get; set; }
[Parameter("GMT start hour", Group = "Trading hours (GMT)", DefaultValue = 4, MinValue = 0, MaxValue = 23)]
public int ParamStartHour { get; set; }
[Parameter("GMT start minute", Group = "Trading hours (GMT)", DefaultValue = 0, MinValue = 0, MaxValue = 59)]
public int ParamStartMinute { get; set; }
[Parameter("GMT end hour", Group = "Trading hours (GMT)", DefaultValue = 21, MinValue = 0, MaxValue = 23)]
public int ParamEndHour { get; set; }
[Parameter("GMT end minute", Group = "Trading hours (GMT)", DefaultValue = 0, MinValue = 0, MaxValue = 59)]
public int ParamEndMinute { get; set; }
[Parameter("Adjust summer time", Group = "Trading hours (GMT)", DefaultValue = true)]
public bool ParamAdjustSummerTime { get; set; }
private int status;
// Estado a la espera sin posicion
private const int STATUS_IDLE = 0;
// Estado con posicion abierta
private const int STATUS_TRADING = 1;
private MovingAverage ma1;
private MovingAverage ma2;
private MovingAverage ma3;
private MovingAverage ma4;
private double volume;
private string label = "Ultron cBot";
private string version = "1.0";
protected override void OnStart()
{
if (!Symbol.Name.Equals("GBPUSD") || Bars.TimeFrame != TimeFrame.Hour)
{
Print("Error. This robot must be used only in GBPUSD H1.");
throw new Exception("Error. This robot must be used only in GBPUSD H1.");
}
Positions.Closed += this.OnClosedPosition;
this.status = STATUS_IDLE;
this.ParamStopLoss = this.ParamStopLossPips * Symbol.PipSize;
this.ParamTakeProfit = this.ParamTakeProfitPips * Symbol.PipSize;
this.ma1 = Indicators.MovingAverage(Bars.OpenPrices, 9, MovingAverageType.Weighted);
this.ma2 = Indicators.MovingAverage(Bars.ClosePrices, 9, MovingAverageType.Weighted);
this.ma3 = Indicators.MovingAverage(Bars.ClosePrices, 50, MovingAverageType.Simple);
this.ma4 = Indicators.MovingAverage(Bars.ClosePrices, 1, MovingAverageType.Simple);
this.volume = Symbol.QuantityToVolumeInUnits(this.ParamLotSize);
if (this.RunningMode == RunningMode.RealTime || this.RunningMode == RunningMode.VisualBacktesting)
{
Chart.DrawStaticText("version", "Ultron cBot " + this.version, VerticalAlignment.Bottom, HorizontalAlignment.Left, Chart.ColorSettings.ForegroundColor);
}
Print("Started " + this.label + " version " + this.version);
}
protected override void OnBar()
{
base.OnBar();
if (this.status == STATUS_IDLE && this.isTradingHours(Bars.OpenTimes.LastValue))
{
double ma1Value = this.ma1.Result.LastValue;
double ma2Value = this.ma2.Result.LastValue;
double ma3Value = this.ma3.Result.LastValue;
double ma4Value = this.ma4.Result.LastValue;
double ma1ma2 = ma1Value - ma2Value;
double ma2ma1 = ma2Value - ma1Value;
double ma3ma4 = ma3Value - ma4Value;
double ma4ma3 = ma4Value - ma3Value;
if (ma3ma4 < 0.0048 && ma3Value > ma1Value && ma3Value > ma2Value && Bars.ClosePrices.Last(1) < Bars.ClosePrices.Last(2) && Bars.ClosePrices.Last(2) < Bars.OpenPrices.Last(2) && ma1ma2 < 0.0013 && ma1ma2 > 0.0004)
{
// Sell
TradeResult result = ExecuteMarketOrder(TradeType.Sell, Symbol.Name, this.calculateVolume(), this.label, this.ParamStopLossPips, this.ParamTakeProfitPips);
if (!result.IsSuccessful)
{
Print("Error when placing a sell order");
}
else
{
if (this.RunningMode == RunningMode.VisualBacktesting)
{
Chart.DrawIcon(Guid.NewGuid().ToString(), ChartIconType.DownArrow, Bars.OpenTimes.LastValue, Bars.HighPrices.LastValue, Color.Yellow);
}
this.status = STATUS_TRADING;
}
}
if (ma4ma3 < 0.0048 && ma3Value < ma1Value && ma3Value < ma2Value && Bars.ClosePrices.Last(1) > Bars.ClosePrices.Last(2) && Bars.ClosePrices.Last(2) > Bars.OpenPrices.Last(2) && ma2ma1 < 0.0013 && ma2ma1 > 0.0004)
{
// Buy
TradeResult result = ExecuteMarketOrder(TradeType.Buy, Symbol.Name, this.calculateVolume(), this.label, this.ParamStopLossPips, this.ParamTakeProfitPips);
if (!result.IsSuccessful)
{
Print("Error when placing a buy order");
}
else
{
if (this.RunningMode == RunningMode.VisualBacktesting)
{
Chart.DrawIcon(Guid.NewGuid().ToString(), ChartIconType.UpArrow, Bars.OpenTimes.LastValue, Bars.LowPrices.LastValue, Color.Yellow);
}
this.status = STATUS_TRADING;
}
}
}
}
private double calculateVolume()
{
double vol = this.volume;
switch (this.ParamMoneyMgmntType)
{
case MMType.FIXED_LOT:
vol = this.volume;
break;
case MMType.RISK:
double conversionRate = 1.0;
if (Account.Currency.Equals("EUR"))
{
Symbol symbolEURYYY = Symbols.GetSymbol("EUR" + Symbol.Name.Substring(3, 3));
conversionRate = symbolEURYYY.Bid;
}
vol = this.normalizeVolume(Account.Equity * this.ParamRisk * conversionRate / (100.0 * this.ParamStopLoss));
break;
}
return vol;
}
private double normalizeVolume(double vol)
{
double result = 1000;
while (result + 1000 < vol)
{
result += 1000;
}
return result;
}
private void OnClosedPosition(PositionClosedEventArgs args)
{
if (args.Position.SymbolName.Equals(Symbol.Name) && this.label.Equals(args.Position.Label))
{
Print("Closed position with net profit " + args.Position.NetProfit);
this.status = STATUS_IDLE;
}
}
private bool isTradingHours(DateTime dt)
{
if (!this.ParamRestrictedTradingHours)
{
return true;
}
BoxHours boxHours = this.getBoxHours(dt);
int startMin = boxHours.startHour * 60 + boxHours.startMinute;
int endMin = boxHours.endHour * 60 + boxHours.endMinute;
int nowMin = dt.Hour * 60 + dt.Minute;
bool result = false;
if (startMin < endMin)
{
result = startMin <= nowMin && nowMin <= endMin;
}
else
{
result = endMin <= nowMin || nowMin <= startMin;
}
return result;
}
private BoxHours getBoxHours(DateTime dt)
{
BoxHours bh = new BoxHours();
if (this.ParamAdjustSummerTime)
{
if (this.isSummer(dt))
{
bh.startHour = this.ParamStartHour - 1;
if (bh.startHour < 0)
{
bh.startHour += 24;
}
bh.endHour = this.ParamEndHour - 1;
if (bh.endHour < 0)
{
bh.endHour += 24;
}
}
else
{
bh.startHour = this.ParamStartHour;
bh.endHour = this.ParamEndHour;
}
}
else
{
bh.startHour = this.ParamStartHour;
bh.endHour = this.ParamEndHour;
}
bh.startMinute = this.ParamStartMinute;
bh.endMinute = this.ParamEndMinute;
return bh;
}
private bool isSummer(DateTime dt)
{
// Winter: November to March
// Summer: April to October
return dt.Month >= 4 && dt.Month <= 10;
}
}
public class BoxHours
{
public int startHour { get; set; }
public int startMinute { get; set; }
public int endHour { get; set; }
public int endMinute { get; set; }
}
public enum Status
{
IDLE,
TRADING
}
public enum MMType
{
FIXED_LOT,
RISK
}
}
guillermo
Joined on 07.06.2019
- Distribution: Free
- Language: C#
- Trading platform: cTrader Automate
- File name: Ultron cBot.algo
- Rating: 0
- Installs: 2196
- Modified: 13/10/2021 09:54