Go to file
nicolas 17aac4b4ef cleanup: remove unused constants MAX_CURRENCIES and _D1 2026-05-29 12:05:30 -03:00
docs Add initial triangular arbitrage bot 2026-05-24 16:12:04 -03:00
notes fix: cascade-based threshold gate, status log, best_net_bps tracking 2026-05-28 10:01:17 -03:00
scripts Add initial triangular arbitrage bot 2026-05-24 16:12:04 -03:00
src cleanup: remove unused constants MAX_CURRENCIES and _D1 2026-05-29 12:05:30 -03:00
.gitignore Add initial triangular arbitrage bot 2026-05-24 16:12:04 -03:00
AGENTS.md fix: drain signal eventfd after pop to prevent CPU spin; fix all warnings 2026-05-28 15:43:43 -03:00
README.md perf: replace SPSC signal queue with per-executor atomic slot delivery 2026-05-28 22:15:59 -03:00
compare_enum.py Add initial triangular arbitrage bot 2026-05-24 16:12:04 -03:00
config.yaml.example cleanup: remove dead config fields (executor_socket_path, cooldown_seconds, stats_interval_seconds), add concurrent_slots to example 2026-05-29 10:31:49 -03:00
pyproject.toml Add initial triangular arbitrage bot 2026-05-24 16:12:04 -03:00
response.txt Add initial triangular arbitrage bot 2026-05-24 16:12:04 -03:00
session Add initial triangular arbitrage bot 2026-05-24 16:12:04 -03:00
test_fused.py Add initial triangular arbitrage bot 2026-05-24 16:12:04 -03:00

README.md

Triangular Arbitrage Bot

Real-time triangular arbitrage detection and execution for KuCoin Spot. Single C binary — WebSocket book feeds, triangle evaluation, and order placement in one process.

Architecture

Monolithic single-process design using N+1 pthreads:

Thread Role
Hot thread WebSocket I/O via epoll, order book maintenance, book update dispatch to evaluator
Evaluator (embedded in hot thread) Triangle profitability evaluation on every book update, delivers to first free executor slot
Executor thread(s) Spin on per-thread slot via atomic state machine; pick up signal, place 3-leg HF market orders via KuCoin REST API, wait for fills via WebSocket, optional balance-wait between legs
[KuCoin WS] ──▶ Hot Thread ──▶ Evaluator ──▶ Slot[0..N] ──▶ Executor Thread(s)
                     │                       (CAS state machine)   │
               Order Book                                        KuCoin REST

Each executor thread owns one slot. The evaluator writes the signal directly into the slot via a CAS state machine (FREE → CLAIMED → READY). The executor picks it up on the next spin iteration (nanoseconds). If all slots are busy or no free slot available, the signal is dropped — no queueing.

The evaluator runs in-process on every book update (no cooldown). The executor does not re-evaluate — it trusts the signal as valid at emission time.

Status

Component Status
WebSocket client (level2/top5 depth) Complete — multi-connection, reconnect, private balance/orderChange channels
Order book store Complete — bid/ask cache with sequence tracking
Triangle enumeration Complete — builder pattern from /api/v2/symbols
Profitability evaluation Complete — paper-trade simulation cascade matching executor
Per-slot signal delivery Complete — per-thread slot with atomic CAS state machine, no queue
Order execution Complete — HF market orders, fill event wait, balance wait
Fill channel Complete — eventfd-based cross-thread fill delivery
Concurrent execution Optional — configurable concurrent_slots (default 1)
Fee table Complete — fetched from /api/v1/base-fee, pair-specific lookup
Balance wait Optional — configurable balance_wait_enabled

Prerequisites

  • C compiler (gcc/clang), CMake 3.22+, OpenSSL, libyaml, pthreads

Building

mkdir -p build && cd build
cmake ../src -DCMAKE_BUILD_TYPE=Release
make -j$(nproc)

Binary at build/fused_engine.

Configuration

Edit config.yaml (see config.yaml.example):

Key Description
live_mode false = paper trades (test endpoint), true = real orders
signal_threshold_bps Minimum predicted bps to fire a signal (default: 2)
hold_currencies Currencies held as capital; triangles must start/end in one of these
excluded_currencies Currencies to exclude from triangle enumeration
kcs_discount_active Whether KCS fee discount applies
balance_wait_enabled Wait for WS balance settlement between live legs (default: false)
concurrent_slots Number of executor threads (default: 1, max 16)
initial_capital Map of currency to max quote per signal
kucoin_api_key/secret/passphrase KuCoin API credentials

Running

./build/fused_engine

Fetches fee table and symbol list, connects to KuCoin WebSocket, subscribes to order books, enumerates triangles, evaluates on every book update, and executes signals via REST.

Project Structure

tri_arb/
├── src/                     # C source
│   ├── main.c              # Entry point, thread spawn
│   ├── ws_client.c/h       # KuCoin WebSocket client (TLS, epoll, book/balance/order frames)
│   ├── book.c/h            # Order book store (top-5 bid/ask)
│   ├── symbols_api.c/h     # Symbol discovery, triangle enumeration
│   ├── triangle.c/h        # Triangle set, leg, signal types
│   ├── evaluate.c/h        # Triangle evaluation and signal dispatch
│   ├── executor.c/h        # Signal execution, REST order placement, fill+balance wait
│   ├── events.c/h          # Event loops (hot thread + executor thread(s))
│   ├── fill_handler.c/h    # Cross-thread fill event channel (SPSC ring + eventfd)
│   ├── rest_client.c/h     # KuCoin REST API client (signed requests, keepalive)
│   ├── http_client.c/h     # One-shot HTTPS requests (token fetch, fee table)
│   ├── slot.c/h            # Per-executor signal slot with atomic CAS delivery
│   ├── config.c/h          # YAML config parser
│   ├── hash.c/h            # Hash table
│   ├── cJSON.c/h           # JSON parser
│   ├── log.c/h             # Logging
│   └── CMakeLists.txt
├── build/                   # Build output
├── config.yaml              # Runtime config (gitignored)
├── config.yaml.example      # Config template
└── scripts/                 # Deployment scripts