Source of truth: the Markdown files in docs/. Everything here is traceable to them.
Approved by the project owner on 2026-07-03 ("Approve all, start Milestone 1").
Presentation → Application → Domain ← Infrastructure (implements Domain ports)
- Domain (
src/domain/) — pure business logic, no external dependencies: state machine, leverage calculator, validation, blocking rules, SL/trailing/ hedge/emergency/re-entry policies, trade summary. Ports (interfaces) for all external services. - Application (
src/application/) — use cases orchestrating Domain against ports: ProcessIncomingSignal, ExecuteOrder, ManageActiveTrade, BybitReconciler, central error handler, event handlers. - Infrastructure (
src/infrastructure/) — adapters: Bybit V5 gateway, Telegram MTProto listener, Telegram Bot API notifier, message bank, persistence, config, logging. - Presentation (
src/presentation/) — composition root (the only place concrete classes are instantiated) and entrypoint.
- State machine (docs/06, 07): one
TradeStateMachine, explicit transition table, 18 required events + 5 blocking events as typed domain events. - Bybit = single source of truth (docs/05): every transition passes through
the Reconciler — proposed transition → query Bybit → confirm, or emit
BOT_ERRORand halt. Bybit state always overrides local state. - Message bank (docs/08, 09, 18, 21): one template per event type keyed by
(event, locale), EN + SV. - Fail-safe (docs/00 §28, 04, 19): validation chain before any order; any failure → stop, log, notify; nothing reaches Bybit.
- Idempotency (docs/00 §32): signal fingerprinting + duplicate detection in the repository.
| # | Topic | Approved decision |
|---|---|---|
| Q1 | Signal format | Pattern-based configurable parser; assumed VIP-group format (#SYMBOL LONG/SHORT, Entry, TP1..TPn, SL). Real samples still required before go-live. |
| Q2 | Missing-SL leverage | Leverage stays 10x; "Swing" refers to management behavior only, not 6x. |
| Q3 | SL movement ladder | TP1 → no change; TP2 → break even; TP3 → TP1; TPn → TP(n−2). Config: stopLossMovement.ladder. |
| Q4 | Main-trade trailing distance | 1.2% behind price (same as hedge). |
| Q5 | Re-entry trigger | After a profitable trailing-stop/final-TP close, price retraces to/beyond the exit price in the original direction while signal is not "old" (Rule 1). Least confident — re-confirm before Milestone 8. |
| Q6 | Hedge sizing | Opposite direction, 100% of remaining position size, same leverage, hedge mode. |
| Q7 | Percent basis | All thresholds (−1.7%, −2%, 6.1%, 0.5%, 3%) are raw price movement from the actual Bybit entry price, per docs/17 Rule 1 definition. One shared TradeMovement calculation. |
| Q8 | Wallet math | Simulated wallet 1000 USDT; 2% risk = 20 USDT margin per trade. |
| Q9 | Boundaries | Exactly 6.75x → Swing; exactly 5% entry difference → block (conservative side). |
| Q10 | README extras | EN/SV message bank via locale config; Bybit account via env; schema designed multi-account, runtime single-user in Step 1. |
| Q11 | Environment | Bybit testnet first (config switch to mainnet); SQLite persistence behind repository interface. |
| Q12 | Telegram access | MTProto user session (GramJS) for reading groups; Bot API for notifications. |
| Q13 | TP distribution | Approved 2026-07-03: equal split across TP levels; TP1..TPn−1 as reduce-only limit orders, final TP share stays open as the trailing stop anchor (docs/13). |
- ✅ M1 Project setup — TS strict, lint/test tooling, validated config, logger, DI skeleton.
- ✅ M2 Domain core — value objects, Signal/Trade entities, 18 required + 6 supplementary
events,
TradeStateMachinewith explicit transition table, Bybit-confirmation enforcement. Design decisions recorded insrc/domain/state-machine/transitions.ts: emergency stop passes through CLOSING; re-entry only after summary (docs/22); ERROR state exits only via Bybit-confirmed reconciliation events. - ✅ M3 Signal parsing & validation — ISignalSource/ISignalParser/INotificationService/ IEventBus ports; pattern-based SignalParser (noise ignored silently, incomplete signal candidates → SIGNAL_REJECTED with reasons); domain SignalValidator (TP/SL geometry per direction); ProcessIncomingSignal use case with message-level dedupe; InMemoryEventBus; GramJS listener + session generator; Bot-API notifier with retry/backoff and interim templates (full message bank in M6). Parser patterns MUST be tuned with real group samples before go-live (assumption Q1).
- ✅ M4 Leverage & order validation — LeverageCalculator (Swing ≤6.75→6x, Dynamic
clamp[7.5,22], Missing-SL→10x + auto −3% SL, managed-as-swing per Q2); formula
calculated = entry / |entry − SL|so an SL hit loses exactly the 20 USDT margin (= 2% of 1000 wallet, Q8 — owner to re-confirm). OrderValidator covering docs/19 (symbol/leverage/margin/qty/existing-position/duplicates, all reasons reported); IExchangeGateway + ITradeRepository ports; InMemoryTradeRepository (SQLite in M10); ValidateAndPlanOrder emits ORDER_VALIDATION_STARTED/FAILED and yields the OrderPlan. Fail-safe: ANY existing Bybit position on the symbol blocks orders until the docs/17 rules land in M9. - ✅ M5 Bybit integration & execution — BybitApiClient (V5 HMAC signing, transient-only retry, idempotent orders via orderLinkId); BybitGateway (linear category, HEDGE MODE positionIdx 1/2 so docs/15 hedges can coexist in M8; "not modified" retCodes = success); BybitReconciler (bounded polling for position/SL confirmation, 0.1% SL tick tolerance); ExecuteOrder use case (hedge mode → leverage → market order → CONFIRM → Bybit-confirmed ORDER_SUBMITTED/POSITION_OPENED → SL + confirm → reduce-only TP orders); TradePipeline wires listener → signal → validation → execution. NEW ASSUMPTION Q13 (owner to confirm): position split equally across TP levels, final TP share left open for the trailing stop (docs/13). Added explicit transition VALIDATING_ORDER→ERROR on BOT_ERROR (docs/20 failed order submission).
- ✅ M6 Notifications — full standardized MessageBank: one template per event type (24 types), EN + SV catalogues, docs/09 TP template and docs/21 error template verbatim, docs/18 blocking-event fields, docs/11 content fields on trade events. Placeholder resolver with payload aliases; missing values render as '—'; bybit_confirmed comes from the event flag. Interim templates removed; TelegramNotifier now renders via MessageBank.
- ✅ M7 Trade management — tradeMovementPercent as THE shared movement calc (Q7); TakeProfitTracker (final TP excluded — trailing anchor); StopLossPolicy ladder (TP2→break-even, TPn→TP(n−2), favorable-only moves); TrailingStopPolicy (6.1%/final-TP activation, 1.2% trail, 0.5% steps, stop only tightens); EmergencyStopPolicy (−2%); ManageActiveTrade tick (position gone → POSITION_CLOSED; TP_REACHED only when Bybit shows the reduce-only fill via size reduction; every SL move set on Bybit AND confirmed before the event); TradeMonitor polling scheduler (config monitoring.pollIntervalMs, overlap-guarded). Trailing SL is tracked as the trade's current SL.
- ✅ M8 Hedge & re-entry — HedgePolicy (−1.7% trigger, opposite side 100% of remaining size per Q6, 1.2% trail, −2% hedge emergency from hedge entry); ManageHedge lifecycle (activation/trailing/emergency/closed, all Bybit-confirmed; hedge may outlive the main position — summary waits for it); direction-aware gateway (getOpenPositions) and reconciler (hedge mode holds both sides); basic TradeSummaryBuilder emitted on close (docs/22 — enriched with Bybit closed-PnL in M10); ReEntryPolicy per Q5: eligible only after a TRAILING_STOP close, arms on ≥0.5% retrace from exit (config reEntry.retraceConfirmationPercent), triggers on recross unless price ran >3% beyond exit; ExecuteReEntry repeats the original plan (same margin/leverage, original SL logic, remaining TPs only). Transition table: hedge events legal in CLOSING/CLOSED.
- ✅ M9 Blocking/update rules (docs/17 Rules 1–5) — SignalBlockingRules with explicit precedence (design decision, docs list no order): Rule 5 opposite-direction → Rule 1 old trade (>3% ABSOLUTE movement) → Rule 3 entry diff >5% update → Rule 4 better TP update → Rule 2 block (≤5%, boundary per Q9). Trade movement vs entry difference kept as separate calculations. EvaluateSignalBlocking runs between signal validation and order validation: blocks publish the docs/18 event and quietly reject the new signal; updates set the new SL (Rule 3, when present) + replace TP orders on Bybit (cancel-all + re-place ahead-of- market TPs) with reconciler confirmation, then apply the Bybit-confirmed update event to the EXISTING trade (new TP structure + trailing anchor; TP progress restarts). Trade now tracks currentTakeProfits/currentFinalTakeProfit; manager and re-entry use them. OrderValidator keeps fail-safe backstops (untracked Bybit positions, in-flight trades).
- ✅ M10 Persistence, crash recovery & summary enrichment — SqliteTradeRepository (better-sqlite3, WAL): event-sourced — trades rehydrate by replaying their persisted event history through the state machine; identity cache so pipeline and monitor share one instance. Restart recovery: TRAILING runtime reseeded from the live Bybit SL (the stop can never loosen after a crash), HEDGED runtime rebuilt from the live hedge position, re-entry watch rebuilt from the last POSITION_CLOSED event. Summaries use Bybit closed-PnL (verified exit price + realized P/L) with bot-computed fallback.
- ✅ M11 Hardening — KeyedAsyncLock serializes pipeline vs monitor per symbol (§30); ERROR-state reconciliation (monitor resolves ERROR trades via Bybit-confirmed POSITION_OPENED/POSITION_CLOSED — no trade is ever stuck "awaiting verified state"); per-trade tick isolation in the monitor; Telegram 4096-char truncation; full-lifecycle integration test (signal → execution → TP fills → SL ladder → trailing → close → Bybit-verified summary) plus blocking and noise regressions.
Step 1 code complete. Remaining before live trading (operational): real signal samples for parser tuning (Q1), credential setup + hedge-mode account check, supervised testnet run, Swedish template review. See README "Go-live checklist".