Architecture Overview¶
How FLOX components fit together. The diagrams below describe the runtime engine — the same architecture you get from every binding (Python, Node.js, Codon, C++). Bindings are thin wrappers; the engine layout is identical.
System Layers¶
flowchart TB
subgraph L3["Application Layer"]
strategy[Your Strategy]
end
subgraph L2["Core Layer"]
eventbus[EventBus]
execution[Order Execution]
risk[Risk Management]
end
subgraph L1["Infrastructure Layer"]
connectors[Connectors]
orderbooks[Order Books]
registry[Symbol Registry]
replay[Replay]
end
L1 --> L2
L2 --> L3
| Layer | Components | Purpose |
|---|---|---|
| Infrastructure | Connectors, Replay, Symbol Registry, Order Books | Low-level I/O and data management |
| Core | EventBus, Order Execution, Risk Management | Event routing and order flow |
| Application | Your Strategy | Trading logic |
Data Flow¶
flowchart TD
subgraph External
EX[Exchange API]
end
subgraph Connectors
CONN[IExchangeConnector]
end
subgraph EventBuses[Event Buses - Disruptor Ring Buffers]
TB[TradeBus]
BB[BookUpdateBus]
CB[BarBus]
end
subgraph Aggregators
CA[BarAggregator]
end
subgraph Strategies
ST[IStrategy]
end
subgraph Execution
OEB[OrderExecutionBus]
EXE[IOrderExecutor]
RM[IRiskManager]
KS[IKillSwitch]
end
EX --> CONN
CONN --> TB
CONN --> BB
TB --> ST
BB --> ST
TB --> CA
CA --> CB
CB --> ST
ST -->|Order| RM
RM -->|Allowed| KS
KS -->|Not Triggered| EXE
EXE --> OEB
Core Components¶
Engine¶
The Engine class orchestrates the system lifecycle:
class Engine : public ISubsystem
{
public:
Engine(const EngineConfig& config,
std::vector<std::unique_ptr<ISubsystem>> subsystems,
std::vector<std::shared_ptr<IExchangeConnector>> connectors);
void start() override;
void stop() override;
};
- Takes ownership of all subsystems
- Starts subsystems first, then connectors
- Stops connectors first, then subsystems
- No configuration file parsing — you wire components manually
Event Buses¶
All buses use the Disruptor pattern (see The Disruptor Pattern):
| Bus | Event Type | Purpose |
|---|---|---|
TradeBus |
TradeEvent |
Individual trades |
BookUpdateBus |
pool::Handle<BookUpdateEvent> |
Order book snapshots/deltas |
BarBus |
BarEvent |
OHLCV bars |
OrderExecutionBus |
OrderEvent |
Order state changes |
Key characteristics:
- Lock-free ring buffer
- Single producer, multiple consumers
- Consumers run in dedicated threads
- Backpressure via sequence gating
Connectors¶
IExchangeConnector interface:
class IExchangeConnector
{
public:
virtual ~IExchangeConnector() = default;
virtual void start() = 0;
virtual void stop() = 0;
virtual std::string exchangeId() const = 0;
};
Connectors:
- Parse exchange-specific wire protocols
- Convert to FLOX event types
- Publish to event buses
- Run their own network threads
Strategies¶
IStrategy combines ISubsystem + IMarketDataSubscriber:
class IStrategy : public ISubsystem, public IMarketDataSubscriber
{
public:
virtual ~IStrategy() = default;
};
From IMarketDataSubscriber:
onTrade(const TradeEvent&)onBookUpdate(const BookUpdateEvent&)onBar(const BarEvent&)
From ISubsystem:
start()stop()
Subsystem Interface¶
Everything that participates in engine lifecycle implements:
class ISubsystem
{
public:
virtual ~ISubsystem() = default;
virtual void start() = 0;
virtual void stop() = 0;
};
Subsystems include:
- Event buses
- Strategies
- Aggregators (e.g., BarAggregator)
- Execution trackers
- Custom components
Symbol Management¶
Symbols are identified by SymbolId (uint32_t):
SymbolRegistry registry;
registry.registerSymbol("binance", "BTCUSDT"); // Returns SymbolId
auto id = registry.getSymbolId("binance", "BTCUSDT");
Benefits:
- Fast comparison (integer vs string)
- Compact event structures
- Consistent across components
Type System¶
FLOX uses strong types to prevent unit confusion:
| Type | Underlying | Purpose |
|---|---|---|
Price |
Fixed-point | Prices (avoid floating-point) |
Quantity |
Fixed-point | Quantities |
SymbolId |
uint32_t |
Symbol identifier |
OrderId |
uint64_t |
Order identifier |
UnixNanos |
int64_t |
Nanosecond timestamp |
Threading Model¶
flowchart TB
subgraph main["Main Thread"]
engine["Engine lifecycle<br/>Subsystem start/stop"]
end
subgraph connectors["Connector Threads"]
c1["Connector 1<br/>Network I/O, Parsing"]
c2["Connector 2<br/>Network I/O, Parsing"]
c3["Connector N<br/>Network I/O, Parsing"]
end
subgraph consumers["Bus Consumer Threads"]
s1["Strategy A"]
s2["Strategy B"]
agg["Aggregator"]
end
engine --> connectors
engine --> consumers
c1 --> s1
c2 --> s2
c3 --> agg
- Each connector manages its own threads
- Each bus consumer gets a dedicated thread
- Consumer threads can be pinned to isolated CPU cores
CPU Affinity (Optional)¶
With FLOX_ENABLE_CPU_AFFINITY=ON:
This:
- Pins consumer threads to isolated cores
- Sets real-time scheduling priority
- Enables NUMA-aware core assignment
Backtest realism stack¶
Backtesting reuses the same Engine, the same buses, and the same strategy class as live. What changes is the executor and the surrounding sim objects.
flowchart LR
DATA[Tape / CSV / floxlog] --> EXE
subgraph stack["VenueStack (one call)"]
EXE[SimulatedExecutor]
ACC[Cross-margin Account]
LIQ[LiquidationEngine<br/>MM tiers + ADL]
FEE[VIP fee schedule]
FUND[Funding schedule]
RL[Rate-limit policy]
AVAIL[Venue-availability hook]
end
EXE --> ACC
LIQ -. on_marks .-> ACC
FEE -. records realized notional .-> ACC
FUND -. settles on interval .-> ACC
EXE --> RL
EXE --> AVAIL
flox.VenueStack.binance_um_futures(...) (and the other factories) wires the whole stack in one call. The strategy class doesn't know the difference between this and a live exchange. See Realistic backtest in one call and Cross-margin accounts.
The bare BacktestRunner skips everything except a flat fee — useful for indicator sanity checks, not for capital decisions.
Execution paths (broker pattern)¶
One strategy class runs backtest, paper, and live. The piece that varies is the broker behind the signal callback:
flowchart LR
STRAT[Your Strategy] --> SIG[Signal]
SIG --> BR{Broker}
BR -->|backtest| SIM[SimulatedExecutor<br/>+ VenueStack]
BR -->|paper| PAPER[PaperBroker<br/>live feed -> SimulatedExecutor]
BR -->|live| CCXT[CcxtBroker<br/>ccxt.pro -> exchange]
PaperBroker runs the same SimulatedExecutor used in the realistic backtest, but on a live feed. CcxtBroker routes the same signals through a real exchange. Switching execution paths is a constructor change, not a code rewrite. See Paper trading and Connect FLOX to a CCXT exchange.
MCP control plane¶
FLOX ships an MCP server that exposes the engine to AI agents under scoped tokens. Read tokens see strategy state, decision logs, and event history. Paper tokens can also place orders against a PaperBroker. Live tokens can route real orders, but each one passes through an out-of-band approval step and is written to the audit log.
flowchart LR
AI[AI agent] -->|MCP, scoped token| SRV[FLOX MCP server]
SRV -->|read| STATE[Strategy state<br/>decisions, events]
SRV -->|paper / live| BR{Broker}
SRV -->|live order| OOB[Out-of-band approval]
OOB --> BR
SRV --> AUDIT[Audit log]
See Control engine over MCP and MCP control plane for the design.
Next Steps¶
- The Disruptor Pattern — Deep dive into event delivery
- Memory Model — Zero-allocation design
- MCP control plane — scoped AI control over the engine
- First Strategy — Write your first strategy