The jsmn zero-alloc parser had token-navigation bugs that caused
all book updates to fail silently. Restore cJSON-based parsing
while preserving the coalescing architecture (accumulate dirty
symbols, evaluate once per burst).
Engine (ws_client.c/h):
- Replace cJSON with jsmn (stack tokens, zero malloc) for book updates
- Quick-route message frames ("type":"message") to jsmn, bypass cJSON
- Coalesce same-symbol updates within one SSL_read burst: evaluate once
- ws_client_process_frame returns symbol_idx for batch tracking
- Restore book->sequence field update from sequence/sequenceNum
Executor (ws_client.py):
- Fix race: fill event arriving before await_fill registers future
is now detected via FillAccumulator._done flag, resolved immediately
Engine (evaluate.c):
- Compute per-leg minimum order size from quoteMinSize
(max(baseMinSize * price, quoteMinSize), rounded to quoteIncrement)
- Convert each leg's minimum to starting-quote via pure-rate product (no fees)
- Viability gate: skip triangle if candidate < min_volume (strictest leg)
- Floor starting_volume at min_volume; supersedes old base_min_size guard
Data (symbols_api.h, triangle.h, symbols_api.c):
- Parse quoteMinSize from KuCoin /api/v2/symbols; propagate to triangle struct
Executor (executor.py):
- Remove _precheck_volume: sizing is the engine's responsibility
- Live mode: don't deduct estimated fee from filled_volume (exchange nets fees)
- Live mode: LegFill.fee always zero
Two-process architecture: a C17 fused engine (WebSocket order book
mirror, triangle enumeration, real-time profitability evaluation)
communicating via Unix domain socket to a Python 3 executor (order
placement with paper/live trading modes, REST control API).
Targets KuCoin spot market.