commit 9c39a743d04ccfcdea0be1a2d4ad32f256719a27 Author: Brummel Date: Thu Jun 18 20:54:56 2026 +0200 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/.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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dcb195b --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +# .NET build output +bin/ +obj/ +dist/ + +# IDE +.vs/ +.vscode/ +.idea/ +*.user + +# Local tool installs +.dotnet/ diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..f83b0e1 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,33 @@ +# cTrader cBot scaffold — project rules + +A Linux-native toolchain for building cTrader cBots in C# and compiling them to +`.algo`, plus running/backtesting them headless via Spotware's official cTrader CLI +Docker image. No Windows, no cTrader desktop app. See `README.md` and +`docs/research-findings.md`. + +## Domain facts (load-bearing, verified) + +- cBots are .NET 6 class libraries referencing the **official** NuGet package + `cTrader.Automate` (owner **spotware**). Do **not** use the third-party `cAlgo.API` + package — it is not the Spotware SDK. +- `dotnet build` emits the `.algo`; the cTrader desktop app is not needed to compile. +- `.algo` is a proprietary, encrypted container (file magic = ASCII `algo`), not a ZIP. +- `cTrader.Automate` 1.0.17 names the `.algo` after the project's **parent folder** + (no override property); `scripts/build.sh` renames artifacts to `.algo`. +- Run/backtest on Linux requires cTrader 5.4+ console image (`ghcr.io/spotware/ctrader-console`). + +## Conventions + +- Everything committed is **English** (code, comments, docs, commit messages). +- New cBots go under `src//`, one class-library project each. + +## Skills plugin: project facts + +- **Code roots:** `src/` (cBots/indicators), `scripts/` (toolchain). +- **Build command:** `./scripts/build.sh` (wraps `dotnet build -c Release`, collects + `dist/.algo`). Requires the .NET SDK on PATH or in `~/.dotnet` + (`./scripts/install-dotnet.sh`). +- **Test command:** none yet (no test project). Build success + a valid `.algo` + (magic bytes `algo`) is the current acceptance check. +- **Run/backtest:** `./scripts/run-console.sh` (Docker, official cTrader console). +- **Issue tracker:** none yet. diff --git a/README.md b/README.md new file mode 100644 index 0000000..832cf52 --- /dev/null +++ b/README.md @@ -0,0 +1,102 @@ +# cTrader cBots on Linux — no Windows required + +Build cTrader **cBots** in C#, compile them to `.algo` files, and run/backtest them +**entirely on Linux** — no Windows, no Wine, no cTrader desktop GUI. + +This repo is a working scaffold that proves the full toolchain end to end and gives +you a starting point. The build was verified on this machine: `dotnet build` against +the official `cTrader.Automate` NuGet package produces a valid `.algo` on Linux. + +## TL;DR — is it really possible? + +**Yes**, with two pieces, both officially supported by Spotware: + +1. **Compile on Linux** (since cTrader Desktop 4.2, March 2022). A cBot is an + ordinary .NET class library that references the official `cTrader.Automate` + NuGet package. `dotnet build` runs the package's MSBuild targets, which emit the + `.algo`. No Windows, no desktop app. Spotware's own words: *"write code and + compile it on Linux or Mac."* +2. **Run/backtest on Linux** (since cTrader 5.4, ~Aug 2025) via the official + **cTrader CLI / Console** Docker image `ghcr.io/spotware/ctrader-console`. It + runs `.algo` cBots headless — live and backtest — without the desktop GUI. + +The nuance worth knowing: step 2 is *recent*. Before cTrader 5.4 there was no Linux +runtime, and Spotware staff said so on the forum as late as Nov 2024. Anyone working +from older information will (incorrectly) tell you it's impossible. + +See [`docs/research-findings.md`](docs/research-findings.md) for the sourced details. + +## Prerequisites + +- The **.NET SDK** (6.0+). Nothing else is needed to compile. + Install it locally without root: `./scripts/install-dotnet.sh` + (drops it into `~/.dotnet`). +- **Docker** — only if you want to run/backtest via the official console image. + +## Quickstart + +```bash +# 1. Install the .NET SDK locally (skip if you already have `dotnet`) +./scripts/install-dotnet.sh + +# 2. Compile every cBot in src/ into dist/*.algo +./scripts/build.sh + +# 3. Run / backtest headless in the official cTrader console container +# (discover the exact flags for your image version first) +./scripts/run-console.sh --help +``` + +After step 2 you get `dist/SampleCBot.algo` — a ready `.algo` you can also import +into the cTrader desktop/web app directly. + +## Repo layout + +``` +src/SampleCBot/ A complete example cBot (SMA crossover) + SampleCBot.csproj SDK-style class library referencing cTrader.Automate + SmaCrossOverBot.cs The Robot class +scripts/ + install-dotnet.sh Local, root-free .NET SDK install (~/.dotnet) + build.sh dotnet build -> collects dist/.algo + run-console.sh Thin wrapper over ghcr.io/spotware/ctrader-console +dist/ Built .algo artifacts (gitignored) +docs/research-findings.md What the research established, with sources +``` + +## How it works + +**Build.** A cBot project is a normal `Microsoft.NET.Sdk` class library targeting +`net6.0` with a single ``. The package +ships MSBuild `.targets` that, after a successful compile, bundle the assembly into the +proprietary, encrypted `.algo` container (file magic: the ASCII bytes `algo`). The +`.algo` lands in `bin///`; `build.sh` collects it into `dist/`. + +**Run.** `run-console.sh` mounts `dist/` into Spotware's official console container at +`/mnt/Robots` and forwards your arguments to the cTrader CLI, which hosts the `.algo` +live or in backtest without the desktop app. + +## To add your own cBot + +1. `dotnet new classlib --name MyBot --output src/MyBot` +2. `cd src/MyBot && dotnet add package cTrader.Automate` +3. Write a `class MyBot : Robot` (`using cAlgo.API;`) — see `SmaCrossOverBot.cs`. +4. `./scripts/build.sh` → `dist/MyBot.algo`. + +## Known quirk (handled) + +`cTrader.Automate` 1.0.17 names the raw `.algo` after the project's **parent folder**, +not the project — so multiple bots would collide on one name. `build.sh` works around +this by renaming each collected artifact to `.algo`. (The bot's name shown +inside cTrader comes from the class metadata, not the file name, so this is cosmetic.) + +## Limitations + +- The cTrader **desktop app** itself is Windows/macOS only — but you don't need it for + this workflow. +- Must be a **modern .NET 6 algo** (cTrader 4.8+). Legacy .NET Framework cAlgo bots + must be recompiled. +- **Live trading** requires a real broker account + cTrader ID credentials passed to + the console. +- The `.algo` format is proprietary/encrypted by Spotware; this scaffold goes *through* + the official package — there is no independent open-source `.algo` packer. diff --git a/cTraderBots.sln b/cTraderBots.sln new file mode 100644 index 0000000..0a43682 --- /dev/null +++ b/cTraderBots.sln @@ -0,0 +1,27 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{49D31EB1-4380-417B-A69E-0F21240EFC29}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleCBot", "src\SampleCBot\SampleCBot.csproj", "{CE1934B5-C4C0-474D-874F-575A8436A27C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CE1934B5-C4C0-474D-874F-575A8436A27C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CE1934B5-C4C0-474D-874F-575A8436A27C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CE1934B5-C4C0-474D-874F-575A8436A27C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CE1934B5-C4C0-474D-874F-575A8436A27C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {CE1934B5-C4C0-474D-874F-575A8436A27C} = {49D31EB1-4380-417B-A69E-0F21240EFC29} + EndGlobalSection +EndGlobal diff --git a/docs/research-findings.md b/docs/research-findings.md new file mode 100644 index 0000000..e218f0b --- /dev/null +++ b/docs/research-findings.md @@ -0,0 +1,111 @@ +# Research: building & running cTrader cBots without Windows + +This documents what was established before building the scaffold in this repo. The two +load-bearing claims were each adversarially fact-checked (the verifier was instructed to +*refute* them); both came back **CONFIRMED**. Where a fact was verified locally on this +machine rather than only from the web, it is marked **[verified locally]**. + +## The claim under test + +> It is possible to create cTrader cBots directly in C# with no Windows, produce `.algo` +> files, and run them in a container. + +**Verdict: true**, in two officially-supported halves — but the second half is recent +enough that older sources (and people relying on them) say it's impossible. + +## The era split (why answers conflict) + +| Era | Build | Run on Linux | +|---|---|---| +| Old **cAlgo**, .NET Framework 4.x (≤ cTrader 4.1, pre-2022) | Windows-only `cTrader.exe /compile` (Spotware: *"not actually supported"*) | No | +| Modern **cTrader Automate**, .NET 6 (cTrader 4.2+, Mar 2022) | `dotnet build` + `cTrader.Automate` NuGet — cross-platform | Desktop only at first; **Linux Docker since cTrader 5.4 (~Aug 2025)** | + +cTrader Desktop 4.2 (30 Mar 2022) *"removed any dependency from the cTrader application +for the build process"* and let developers *"compile cBots and indicators using the +'dotnet build' command and even write code and compile it on Linux or Mac."* + +## 1. Compiling `.algo` on Linux — CONFIRMED + +- **Official SDK package:** `cTrader.Automate` on NuGet, owner **spotware** + (Spotware Systems Ltd.). Description: *"Provides cTrader Automate SDK to build cBots + and Indicators in ALGO format."* Multi-targets **net6.0+** and net40, **no + dependencies** → installs and runs fine via `dotnet` on Linux. (Latest seen: 1.0.17, + 2026-03-27.) +- **Mechanism:** the package ships MSBuild `.targets`. Building a class library that + references it and contains an algo type (a class deriving from `Robot`/`Indicator`) + bundles the compiled assembly into the `.algo`. **[verified locally]** — a clean + `dotnet build -c Release` on this Linux box produced a valid `.algo` with **0 errors**. +- **What you need on Linux:** just the .NET SDK + NuGet access. No Windows, no Wine, no + cTrader desktop. +- **Beware the name collision:** there is a *separate* third-party NuGet package + `cAlgo.API` (owner `st.alexofgod`, MIT). It is **not** the Spotware SDK. Use + `cTrader.Automate`. + +### Build verified, plus a local finding + +- The `.algo` filename quirk: `cTrader.Automate` 1.0.17 names the output after the + **parent directory of the project folder** — `_AlgoFileName = + GetFileName(GetDirectoryName(MSBuildProjectDirectory))` in its + `cTrader.Automate.targets`, with no override property. **[verified locally]** Two bots + in one folder collide; this repo renames artifacts in `build.sh`. +- **`.algo` format — what it actually is. [verified locally]** A built `.algo` begins + with the ASCII magic bytes `algo` (`61 6c 67 6f`) and is **not** a ZIP (no `PK` header, + no central directory). So it is Spotware's own proprietary container, not a renamed + archive — settling a question the web sources left open. Per Spotware it wraps the + compiled assembly (+ referenced DLLs, + optional source/symbols) and is encrypted; the + encryption algorithm is undisclosed and there is no public byte-level spec. + +## 2. Running `.algo` headless in a container — CONFIRMED (recent) + +- **Official runner:** **cTrader CLI / Console**, distributed as a Linux Docker image + `ghcr.io/spotware/ctrader-console` (repo `spotware/ctrader-console-docker`). Docs: + it *"enables traders to manage account and algo-trading operations directly from a + console or terminal without launching or relying on the regular cTrader application"* + and *"is available as a Linux Docker image … All cTrader CLI features are available + when using the Docker image, including backtesting."* Mount `.algo` files to + `/mnt/Robots`. +- **Recency caveat:** available only in **cTrader 4.8+**, works only with **modern .NET 6 + algos**, and the Linux image arrived with **cTrader 5.4 (~Aug 2025)**. As late as + **Nov 2024** a Spotware moderator wrote *"Unfortunately cTrader CLI is not supported on + Linux"*, and DIY Wine/Mono attempts failed. So pre-2025 sources correctly said it was + impossible — this is the single most likely source of disagreement. +- The runner is a **host, not a compiler** — it runs/backtests `.algo` files; it does not + build them. Compilation is the `dotnet build` step above. + +## 3. cTrader Automate vs. Open API (don't conflate) + +Two different products that can both run on headless Linux: + +- **cTrader Automate (cBots / `.algo`)** — the strategy framework (`cAlgo.API`, + indicators, backtesting, optimization). Runs *inside* the cTrader runtime / the cTrader + CLI. This is what this repo targets. +- **cTrader Open API** — a *separate* protocol (Protobuf over TLS/TCP or WebSocket, OAuth2; + endpoints `live.ctraderapi.com` / `demo.ctraderapi.com`) for **external** apps that + connect to a broker backend. Official SDKs: .NET (`cTrader.OpenAPI.Net`) and Python + (`ctrader-open-api`); language-neutral otherwise. It gives you trading + market data + + account access, but **no** cBot framework, indicators, or backtesting — you write all + strategy logic yourself. + +Choose **Automate + cTrader CLI** to reuse the cBot framework and backtesting on Linux; +choose **Open API** for a fully standalone, language-flexible service. + +## Limitations / honesty notes + +- The cTrader **desktop app** is Windows/macOS only — not needed for this workflow, but + there is no native Linux *desktop*. +- **Live** trading needs a broker account + cTrader ID credentials. +- The `.algo` container is proprietary and encrypted; any legitimate Linux build goes + *through* the official `cTrader.Automate` package — there is no independent OSS packer. +- cTrader's docs/tooling move quickly; version numbers above reflect mid-2026. + +## Key sources + +- cTrader Desktop 4.2 release — https://www.spotware.com/news/ctrader-desktop-4-2/ +- Use Visual Studio and other IDEs — https://help.ctrader.com/ctrader-algo/documentation/visual-studio-ides/ +- Compiler (MSBuild properties) — https://help.ctrader.com/ctrader-algo/documentation/all-algos/compiling/ +- cTrader CLI — https://help.ctrader.com/ctrader-algo/documentation/ctrader-cli/ +- Console Docker image — https://github.com/spotware/ctrader-console-docker +- `cTrader.Automate` package — https://www.nuget.org/packages/cTrader.Automate/ +- Algo security measures (encryption) — https://help.ctrader.com/ctrader-algo/documentation/protection-measures/ +- Open API getting started — https://help.ctrader.com/open-api/ +- Forum: Linux not supported (Nov 2024) — https://community.ctrader.com/forum/ctrader-algo/45395/ diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 0000000..17caae4 --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# +# Compile every cBot/indicator project in src/ into .algo files — on Linux, with +# no Windows and no cTrader desktop app. The .algo files are collected into dist/. +# +# Usage: ./scripts/build.sh [Release|Debug] # defaults to Release +# +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +CONFIG="${1:-Release}" + +# Pick up the locally-installed SDK (see scripts/install-dotnet.sh). +export DOTNET_ROOT="${DOTNET_ROOT:-$HOME/.dotnet}" +export PATH="$DOTNET_ROOT:$PATH" +export DOTNET_CLI_TELEMETRY_OPTOUT=1 +export DOTNET_NOLOGO=1 + +if ! command -v dotnet >/dev/null 2>&1; then + echo "dotnet not found. Run ./scripts/install-dotnet.sh first." >&2 + exit 1 +fi + +SLN="$REPO_ROOT/cTraderBots.sln" +if [[ ! -f "$SLN" ]]; then + echo ">> Creating solution and adding projects..." + dotnet new sln --name cTraderBots --output "$REPO_ROOT" + find "$REPO_ROOT/src" -name '*.csproj' -print0 \ + | xargs -0 -I{} dotnet sln "$SLN" add {} +fi + +echo ">> Building ($CONFIG)..." +dotnet build "$SLN" --configuration "$CONFIG" + +DIST="$REPO_ROOT/dist" +mkdir -p "$DIST" + +# Collect artifacts. cTrader.Automate names the raw .algo after the project's parent +# folder (which collides across bots), so rename each to .algo here. +while IFS= read -r -d '' csproj; do + name="$(basename "${csproj%.csproj}")" + algo="$(find "$(dirname "$csproj")/bin/$CONFIG" -name '*.algo' 2>/dev/null | head -1)" + if [[ -n "$algo" ]]; then + cp -v "$algo" "$DIST/$name.algo" + fi +done < <(find "$REPO_ROOT/src" -name '*.csproj' -print0) + +echo +echo ">> .algo artifacts in $DIST:" +ls -1 "$DIST"/*.algo 2>/dev/null || echo " (none found — check the build output above)" diff --git a/scripts/install-dotnet.sh b/scripts/install-dotnet.sh new file mode 100755 index 0000000..5f89413 --- /dev/null +++ b/scripts/install-dotnet.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +# +# Install the .NET SDK locally — no root, no system packages — into ~/.dotnet. +# This is the only prerequisite for compiling cBots into .algo files on Linux. +# +# Usage: ./scripts/install-dotnet.sh [channel] # channel defaults to 8.0 +# +set -euo pipefail + +INSTALL_DIR="${DOTNET_ROOT:-$HOME/.dotnet}" +CHANNEL="${1:-8.0}" + +if command -v curl >/dev/null 2>&1; then + curl -fsSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh +else + wget -qO /tmp/dotnet-install.sh https://dot.net/v1/dotnet-install.sh +fi + +bash /tmp/dotnet-install.sh --channel "$CHANNEL" --install-dir "$INSTALL_DIR" --no-path + +cat </dev/null 2>&1; then + echo "docker not found — install Docker to run the cTrader console image." >&2 + exit 1 +fi + +docker pull "$IMAGE" + +exec docker run --rm -it \ + -v "$REPO_ROOT/dist:/mnt/Robots" \ + "$IMAGE" "$@" diff --git a/src/SampleCBot/SampleCBot.csproj b/src/SampleCBot/SampleCBot.csproj new file mode 100644 index 0000000..382c661 --- /dev/null +++ b/src/SampleCBot/SampleCBot.csproj @@ -0,0 +1,43 @@ + + + + + net6.0 + latest + disable + disable + + + false + + + + + + + + + + diff --git a/src/SampleCBot/SmaCrossOverBot.cs b/src/SampleCBot/SmaCrossOverBot.cs new file mode 100644 index 0000000..15568f6 --- /dev/null +++ b/src/SampleCBot/SmaCrossOverBot.cs @@ -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); + } + } +}