Add Linux-native cTrader cBot build/run scaffold
Build cBots in C# and compile them to .algo entirely on Linux via the official cTrader.Automate NuGet package + 'dotnet build' — no Windows and no cTrader desktop app. Run/backtest headless through Spotware's official cTrader console Docker image. Includes: - src/SampleCBot: example SMA-crossover cBot (SDK-style net6.0 class library) - scripts/install-dotnet.sh: root-free local .NET SDK install (~/.dotnet) - scripts/build.sh: dotnet build -> dist/<ProjectName>.algo, with a workaround for cTrader.Automate naming the .algo after the project's parent folder - scripts/run-console.sh: wrapper over ghcr.io/spotware/ctrader-console - docs/research-findings.md: sourced research; .algo verified as a proprietary 'algo'-magic container (not a ZIP); build verified end to end on Linux
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user