Zum Hauptinhalt springen

Copy Trading

Spiegeln Sie das Master-cTrader-Konto auf ein oder mehr Slave-Konten — über Broker hinweg, über cID hinweg — mit pro-Ziel-Steuerung und Geldklassen-Abgleich.

Konzepte​

  • Copy-Profil — ein Master (SourceAccountId) + ein oder mehr Ziele. Lebenszyklus: Draft → Running → Paused → Stopped (Error bei Fehler). Aggregate-Wurzel: CopyProfile (besitzt CopyDestination).
  • Ziel — ein Slave-Konto + vollständiger Regelsatz für die Art und Weise, wie der Master darauf gespiegelt wird. Alle Konfigurationen pro Ziel, sodass ein Master gleichzeitig konservative und aggressive Slaves versorgen kann.
  • Copy-Engine-Host — laufender Worker für das Profil (CopyEngineHost). Abonniert den Master-Ausführungsstrom und wendet jedes Ereignis auf jedes Ziel an.
  • Supervisor — CopyEngineSupervisor, Hintergrunddienst auf jedem Knoten. Hostet zugewiesene Profile, selbstheilend über Cluster (siehe Skalierung).

Was wird gespiegelt​

Master eventSlave action
Market / market-range position openOpen a sized copy (labelled with the source position id)
Limit / stop / stop-limit pending orderPlace the matching pending order, carrying the master's stop-loss / take-profit
Pending order amendAmend the mirrored pending order in place (including its stop-loss / take-profit)
Pending order cancel / expiryCancel the mirrored pending order
Partial closeClose the same proportion of the slave position
Scale-in (volume increase)Open the added volume (opt-in)
Stop-loss / trailing-stop changeAmend the slave position's protection
Full closeClose the slave copy

Every copy labelled with source position/order id. After reconnect host rebuilds state from reconcile: opens copies master holds but slave missing, closes slave "orphans" master no longer holds — without duplicating trades.

Erstellen eines Profils​

Das Neue Profil öffnet ein dediziertes Formular für die gesamte Seite (/copy-trading/new), keinen Dialog — die Optionsmenge ist groß genug, dass eine Seite auf Telefon und Desktop besser lesbar ist. Es sammelt alles im Voraus: Profilname, Quellenkonto (Master), Zielkonten (Slave) (Mehrfachauswahl mit Schaltfläche Alles auswählen; gewählter Master aus der Slave-Liste ausgeschlossen), + das vollständige Satz an Optionen pro Ziel. Nur über die cTrader Open API verknüpfte Konten sind wählbar als Master oder Ziel — das Kopieren platziert Aufträge über die Open API, daher kann ein manuell hinzugefügtes (nur cID) Konto nicht kopieren und wird nicht aufgelistet; wenn keine verknüpft sind, zeigt die Seite einen Hinweis auf Handelskonten an. Größenmodi, Richtung und Symbolfilter werden als menschliche Labels mit pro-Modus-Erklärung in Aufzählungsform auf dem Hilfetext zur Geldverwaltung angezeigt. Jedes Steuerelement trägt einen Hilfetext, der erklärt, was es tut und wie es verwendet wird. Strukturierte Eingaben verwenden ordnungsgemäß validierte Steuerelemente — Zahlen/Prozent über numerische Felder, Modi/Richtung/Filter über Auswahlfelder, Symbolfilter über eine Hinzufügen/Entfernen-Liste von Symbolen und Symbolzuordnung über eine Hinzufügen/Entfernen-Tabelle von Zeilen Quelle → Ziel (× Multiplikator) — niemals ein durch Kommas getrenntes Textblob. Alle Eingaben werden vor dem Speichern validiert — fehlender Name/Quelle/Ziel, nicht-positiver Größenparameter, negative/inkonsistente Lot-Grenzen, außerhalb des Bereichs Drawdown %, kein aktivierter Auftragstyp oder leerer Symbolfilter wird als Fehlerliste angezeigt + blockiert das Speichern. Beim Erstellen wird das Profil erstellt + jedes ausgewählte Slave mit den gewählten Einstellungen hinzugefügt, dann kehrt die Seite zur Liste Kopierhandelsliste zurück.

Import / Export. Der gesamte Einstellungsblock kann als JSON-Datei exportiert und erneut importiert werden, um das Formular auszufüllen, sodass eine Feinabstimmung über Profile hinweg ohne Neuschreiben wiederverwendet werden kann. Die Symbolzuordnung kann ebenfalls als CSV-Datei exportiert / importiert werden (Source,Destination,VolumeMultiplier) — bereiten Sie eine große Broker-Symbolzuordnung in einer Tabellenkalkulation vor und laden Sie sie in einem Schritt. Dieselben Symbolsteuerelemente und CSV-Import-/Export-Optionen sind auch im Zieldialog auf der Seite Kopierhandel verfügbar.

Zeilenaktionen respektieren den Lebenszyklus: Start ist nur aktiviert, wenn nicht läuft, Stop + Pause nur wenn läuft, Löschen ist deaktiviert während läuft + fordert Bestätigung vor dem Entfernen von Profil + Zielen an.

Ein gerade gestartetes Profil zeigt kurzzeitig einen Starting-Status (nicht ein grünes Running) an, während sein Host Referenzdaten lädt und die erste Resync durchführt — es spiegelt noch keine Aufträge über die Ziele. Es wechselt zu Running, sobald diese erste Resync abgeschlossen ist und die Engine kopieren kann. Starting wird für die Zeilensteuerelemente als läuft behandelt (Start deaktiviert, Stop und Live-Logs aktiviert, Bearbeiten/Löschen blockiert), daher kann ein wärmendes Profil nicht neu gestartet oder während des Starts bearbeitet werden. Die Aufwärmphase wird prozessiert auf dem Knoten, der das Profil hostet; ein Profil, das auf einer anderen Replik gehostet wird (oder eines, das nicht gehostet werden kann — seine Quellen-/Zielkonten sind nicht über die Open API verknüpft) zeigt seinen einfachen Status.

Pro-Ziel-Optionen​

Set on the New Profile page, in the destination dialog on the Copy Trading page, or via POST /api/copy/profiles/{id}/destinations:

  • Sizing (MoneyManagementMode + parameter): fixed lot, lot/notional multiplier, proportional balance/equity/free-margin, fixed risk %, fixed leverage, auto-proportional, risk-%-from-stop (M7). Plus min/max lot bounds + force-min-lot. Risk-from-stop sizes destination so it risks configured percent of its own balance, derived from master's stop-loss distance (master risks 2% → slave auto-risks 2%): lots = balance×% ÷ (stopDistance × contractSize). Master open without stop-loss has no distance to size against → uses configured max-risk fallback lot (M7) if set, else skipped (no_stop_loss) not guessed. Proportional-equity/free-margin size off real account equity (balance + Σ floating P&L, derived per cTrader Open API which doesn't deliver equity), not plain balance — so master sitting on open profit/loss sizes copies right. Used margin not exposed by reconcile API, so free-margin treated as equity (honest available-funds proxy); other modes read balance + skip extra revaluation round-trip.
  • Direction filter: both / long-only / short-only. Reverse: flip side (+ swap SL↔TP) for contrarian copy.
  • Manage-only (Ignore-New-Trades / Close-Only): mirror closes, partial closes + protection changes on already-copied positions, but open no new positions/pending orders (skipped manage_only). Use to wind destination down without cutting existing copies.
  • Sync-Open-on-start / Sync-Closed-on-start (default on): on profile's first resync, whether to open copies for master's pre-existing positions, + whether to close copies master closed while profile stopped. Both apply only at start — mid-run reconnect always reconciles fully so desync recovers regardless.
  • Symbol map + symbol filter (whitelist / blacklist). Each symbol-map entry carries optional per-symbol volume multiplier (cMAM per-symbol override) scaling copy size for that symbol on top of destination's sizing (1 = no change). Whole map imports/exports as CSV (GET …/symbol-map.csv, PUT …/symbol-map/csv; columns Source,Destination,VolumeMultiplier) — each row validated through domain value objects, so malformed file can't produce invalid map.
  • Trading-hours window (C18) — per-destination daily UTC window (start/end minutes-of-day, end exclusive; start == end = all-day). New opens outside window skipped (trading_hours); window with start > end wraps past midnight (e.g. 22:00–06:00). Existing positions stay managed.
  • Source-label filter (C18, cTrader equivalent of MT magic-number filter) — when set, copy only master trades whose label matches exactly (e.g. one bot's trades, or manual-only label); else skipped (source_label). Empty = copy all. Carried on ExecutionEvent.SourceLabel from master position/order's TradeData.Label, honored on resync too.
  • Account protection (ZuluGuard / Global Account Protection) — watch destination's live equity (balance + Σ floating P&L, polled every CopyDefaults.EquityGuardInterval) against StopEquity floor and/or optional TakeEquity ceiling. On breach, apply mode: CloseOnly (stop new copies, keep managing existing), Frozen (stop opening), SellOut (close every copy on destination immediately). Once fired, destination latched — no new opens until host restarts — + CopyAccountProtectionTriggered alert raised. SellOut requires StopEquity; TakeEquity must sit above StopEquity. No-guarantee caveat: sell-out uses market execution — like every competitor's equivalent, can't guarantee fill price in fast/gapped market.
  • Flatten-All panic button (C8) — POST /api/copy/profiles/{id}/flatten immediately closes every copied position on every destination + locks against new opens. Routed cross-process: API sets flag, supervisor delivers to running host (reusing token-rotation channel), which flattens in place; flag cleared so fires exactly once (CopyFlattenAll alert). User then pauses/stops profile.
  • Prop-firm rule guard (C7) — enforcement prop-firm copier users ask for. Per destination, daily-loss cap (loss from day's opening equity) and/or trailing-drawdown limit (loss from running peak equity), both in deposit currency. On breach destination auto-flattened (every copy closed) + locked out rest of UTC day (new opens skipped prop_lockout); CopyPropRuleBreached alert fires. Lockout clears when UTC day rolls over (fresh baseline/peak taken). Shares same live-equity poll as account protection.
  • Execution jitter (C11, off by default) — random 0..N ms delay before placing each copy, to de-correlate near-identical order timestamps across user's own accounts. Compliance caveat: aid for prop firms that permit copying — not tool to evade firm that forbids it; staying within your firm's rules is your responsibility.
  • Config lock (C9) — freeze destination's settings for period (POST …/destinations/{id}/lock with minutes). While locked, destination can't be removed (aggregate rejects with CopyDestinationConfigLocked) — deliberate guard against impulsive changes during drawdown. Lock expires automatically at its timestamp.
  • Consistency pre-alert (C10) — warn (once per UTC day) when destination's daily profit reaches configured percent of day's opening equity (CopyConsistencyThresholdApproaching), so prop-firm consistency rule respected before it trips. Profit-side, independent of loss-side lockout; runs off same day baseline as prop-rule guard.
  • Order-type filter — choose exactly which master order types to copy: market, market-range, limit, stop, stop-limit (CopyOrderTypes flags; default all). cMAM-style selectivity.
  • Copy SL / Copy TP — mirror master's stop-loss / take-profit, or manage protection independently. Applies to both open positions and resting pending orders — a limit/stop/stop-limit copy is placed and amended with the master order's SL/TP (swapped under Reverse), so the protection is attached the moment the pending fills, not only after.
  • Copy trailing stop, mirror partial close, mirror scale-in — each independently toggleable.
  • Copy pending expiry (default on) — mirror master pending order's Good-Till-Date expiry timestamp.
  • Copy master slippage (default on) — for market-range + stop-limit orders, place slave order with master's exact slippage-in-points (base price taken from slave's live spot).
  • Guards: max drawdown %, daily loss cap, max copy delay, slippage filter (skip copy if slave price moved beyond N pips from master entry). Max copy delay measured against master event's real server timestamp (ExecutionEvent.ServerTimestamp) via injected TimeProvider: signal older than configured max-lag skipped, so stale copy never placed late (previously delay always zero + guard dead).
  • SL/TP precision normalization (M6) — copied stop-loss/take-profit prices rounded to destination symbol's digit precision before amend (on positions and pending-order placement/amend), so master price at finer precision (or cross-broker digit mismatch) never trips server's INVALID_STOPLOSS_TAKEPROFIT.
  • Rejection circuit breaker / Follower Guard (G8) — destination rejecting CopyDefaults.RejectionBudget opens in a row is tripped: no new opens for cooldown window (CopyDestinationTripped alert fires), stopping rejection storm from hammering (prop-firm) account. Existing positions still managed + closed while tripped; breaker auto-resets after cooldown + successful copy clears counter.
  • Lot sanity ceiling (C14) — absolute max copy size and/or multiple-of-master cap. Computed copy exceeding absolute cap, or exceeding N× master's own lot size, hard-blocked (surfaced as lot_sanity skip, counted on cmind.copy.skipped) not placed — defends against catastrophic-oversize class (0.23-lot master turning into 3 lots on each receiver via runaway multiplier or rounding bug). Both dimensions default 0 (off).

Zuverlässigkeit & Grenzfälle​

Engine built for reality that anything can fail anytime:

  • Slave-pending fill-correlation timeout (C13) — mirrored slave pending whose master pending vanished (neither resting nor freshly filled) cancelled after correlation timeout, so slave copy can't fill uncorrelated into unmanaged position (CopyPendingTimedOut). Resync also cleans order-id-labelled filled-pending orphan.

  • Cross-broker pending-fill race — a slave's own pending can fill (its price hit) in the small window before the master's fill/cancel event is processed. That leaves a slave position labelled by the source order id, which the canonical close/SL-TP paths (keyed by source position id) would miss. On a master fill the early slave fill is retired and replaced by one canonically-labelled market copy — so the destination ends with exactly one copy, never a doubled position; on a master cancel it is closed outright (the master never took the trade). Both act immediately, not only on the next resync. A slave-side SL/TP hit that closes a copy the master still holds is source-driven and re-opened on the next reconcile (the engine mirrors master events; it does not consume destination-side executions).

  • Robust close/flatten (M8) — closing orphan on resync, or flattening on guard breach, tolerates position broker already closed (POSITION_NOT_FOUND): each close runs independently, so one stale id never aborts resync or leaves rest of account un-flattened.

  • Start with master already in trades — on start host reconciles + opens copies for master's existing positions.

  • Connection drops / desync — on reconnect host reconciles: opens missing copies, closes orphans, re-labels pendings. No duplicate orders.

  • Order placement failure — failure on one destination logged, never blocks other destinations.

  • Single valid token per cID — cTrader invalidates cID's old access token moment new one issued. cMind swaps running host's token in place (re-auth on live socket) so copying continues without dropping stream. See token lifecycle.

Nachvollziehbarkeit​

Every action emits structured, source-generated log event (LogMessages) with profile id, destination cID, order/position ids, + values — order placed/skipped (with reason), partial close, protection applied, trailing applied, pending placed/amended/cancelled, expiry mirrored, market-range slippage mirrored, token swapped, resync summary. This is the audit trail for compliance + dispute resolution.

Alongside logs, engine emits OpenTelemetry metrics on cMind.Copy meter (registered in shared OTel pipeline, exported over OTLP / to Azure Monitor like rest): cmind.copy.latency (master-event → dispatch, ms), cmind.copy.dispatch.duration (fan-out to all destinations, ms), cmind.copy.slippage.points, cmind.copy.placed (tagged by destination), cmind.copy.skipped (tagged by reason), + cmind.copy.failed. These make latency/slippage regression measurable, not just visible in log line — live suite asserts them against budget.

API​

  • GET /api/copy/profiles — list.
  • POST /api/copy/profiles — create (with optional destination account ids).
  • GET /api/copy/profiles/{id} — full detail incl. every destination option.
  • POST /api/copy/profiles/{id}/destinations — add a destination with the full option set.
  • DELETE /api/copy/profiles/{id}/destinations/{destinationId} — remove.
  • POST /api/copy/profiles/{id}/{start|pause|stop} — lifecycle.

Tests​

  • Unit (tests/UnitTests/CopyTrading) — sizing modes, decision filters, order-type filter, expiry copy, market-range/stop-limit slippage, SL/TP toggles, partial close, pending amend/cancel, start-with-open, disconnect→desync→resync, in-place token swap, cross-cID invalidation. Runs against FakeTradingSession, cTrader-faithful in-memory simulator.
  • Integration (tests/IntegrationTests/CopyLive) — node-affinity/lease claim, token-version propagation on real Postgres.
  • E2E (tests/E2ETests) — destination-option round-trip through API + UI, full lifecycle.
  • Stress / DST (tests/StressTests) — deterministic-simulation testing: seeded randomized workloads + fault injection (socket flap, order rejection, market-range rejection, token rotation, node death) drive CopyEngineHost to quiescence + assert convergence invariants. See testing/stress-testing.md. This suite surfaced + fixed real startup race: OnReconnected wired before initial reference-load + resync, so socket flap during startup could run second resync concurrently + corrupt host's non-concurrent state dictionaries — startup load + first resync now run under _stateGate.
  • Live — real cTrader demo accounts; see testing/live-copy-trading.md.

See dev-credentials.md for single credentials file live + E2E tiers read.

Profil-Steuerung und Ziel-Verwaltung​

Start/stop are icon buttons on each profile row (disabled when the action does not apply). Source and destination accounts are shown by their account number, never an internal id. Clicking a profile opens a dialog to manage its destination accounts (add/remove with full per-destination settings).