using cAlgo.API; using cAlgo.API.Indicators; namespace SampleCBot { // A minimal but complete example cBot: a simple moving-average crossover. // // It goes long when the fast SMA crosses above the slow SMA and short on the // opposite cross, closing any opposing position first. It is intentionally // simple — its job is to exercise the full Linux build/run toolchain end to // end (compile to .algo with `dotnet build`, run headless in the cTrader // console Docker image), NOT to be a profitable strategy. [Robot(AccessRights = AccessRights.None)] public class SmaCrossOverBot : Robot { [Parameter("Fast MA Period", DefaultValue = 12, MinValue = 1, Group = "Moving Averages")] public int FastPeriod { get; set; } [Parameter("Slow MA Period", DefaultValue = 26, MinValue = 1, Group = "Moving Averages")] public int SlowPeriod { get; set; } [Parameter("Volume (lots)", DefaultValue = 0.01, MinValue = 0.01, Step = 0.01, Group = "Trade")] public double VolumeInLots { get; set; } [Parameter("Stop Loss (pips)", DefaultValue = 20, MinValue = 0, Group = "Trade")] public double StopLossPips { get; set; } [Parameter("Take Profit (pips)", DefaultValue = 40, MinValue = 0, Group = "Trade")] public double TakeProfitPips { get; set; } private const string Label = "SmaCrossOverBot"; private SimpleMovingAverage _fastMa; private SimpleMovingAverage _slowMa; protected override void OnStart() { _fastMa = Indicators.SimpleMovingAverage(Bars.ClosePrices, FastPeriod); _slowMa = Indicators.SimpleMovingAverage(Bars.ClosePrices, SlowPeriod); } protected override void OnBar() { // Compare the two most recently *closed* bars. Index 1 is the last // closed bar; index 2 the one before it. (Index 0 is still forming.) var fastPrev = _fastMa.Result.Last(2); var slowPrev = _slowMa.Result.Last(2); var fastNow = _fastMa.Result.Last(1); var slowNow = _slowMa.Result.Last(1); var crossedUp = fastPrev <= slowPrev && fastNow > slowNow; var crossedDown = fastPrev >= slowPrev && fastNow < slowNow; if (crossedUp) EnterMarket(TradeType.Buy); else if (crossedDown) EnterMarket(TradeType.Sell); } private void EnterMarket(TradeType direction) { var ours = Positions.FindAll(Label, SymbolName); // Close any of our positions pointing the other way; bail out if we // already hold one in the desired direction. foreach (var position in ours) { if (position.TradeType != direction) ClosePosition(position); else return; } var volume = Symbol.QuantityToVolumeInUnits(VolumeInLots); double? stopLoss = StopLossPips > 0 ? StopLossPips : (double?)null; double? takeProfit = TakeProfitPips > 0 ? TakeProfitPips : (double?)null; ExecuteMarketOrder(direction, SymbolName, volume, Label, stopLoss, takeProfit); } } }