An open API service indexing awesome lists of open source software.

https://github.com/aabbtree77/cryptobot

Cryptocurrency trading bot with resync, modulo actual trading.
https://github.com/aabbtree77/cryptobot

distributed-computing latency reconnection resilience resync trading websocket

Last synced: 5 days ago
JSON representation

Cryptocurrency trading bot with resync, modulo actual trading.

Awesome Lists containing this project

README

          

# spec.md

```text
BINANCE LOW-LATENCY TRADING BOT
Deterministic Event-Loop Architecture
Go / Single Process / Epoch-Based Resynchronization

======================================================================
0. CORE PHILOSOPHY
======================================================================

The system is NOT fundamentally a websocket client.

The system is:
a continuously repaired local simulation of exchange reality.

The websocket connection is merely:
a transport mechanism feeding authoritative market state.

The core architectural objective is NOT:
- socket survival
- reconnect minimization
- uptime maximization

The actual objective is:

minimize duration of invalid local market state

Meaning:
the system must rapidly detect when confidence in local state
has degraded, then restore trustworthy synchronized state.

Primary metric:

time-to-correct-resync

NOT:
- reconnect count
- socket longevity
- ping success rate

======================================================================
1. PROCESS MODEL
======================================================================

Single binary.
Single process.
Single strategy loop.

No:
- microservices
- Kafka
- ZooKeeper
- actor framework
- Erlang supervision tree emulation
- distributed consensus
- service mesh
- Kubernetes orchestration complexity

Reasoning:

The system currently contains:
- one exchange
- one websocket stream
- one strategy engine

Local memory inside one process provides:
- strongest consistency model
- lowest coordination overhead
- deterministic debugging
- lowest reconnect latency

Distributed systems complexity is intentionally avoided.

======================================================================
2. EXECUTION MODEL
======================================================================

The architecture intentionally separates:

1. deterministic state mutation
2. asynchronous network I/O

Trading state mutates ONLY inside:
the strategy event loop.

Network goroutines:
- never mutate strategy state
- never perform trading decisions
- never reconnect themselves
- never supervise themselves

This preserves:
- deterministic reasoning
- replayability
- debuggability
- absence of mutexes

======================================================================
3. GOROUTINE MODEL
======================================================================

Exactly three long-lived goroutines:

1. Strategy Loop
2. Websocket Reader
3. Websocket Writer

----------------------------------------------------------------------
3.1 Strategy Loop
----------------------------------------------------------------------

Responsibilities:
- maintain authoritative local market state
- maintain orderbook
- maintain positions
- maintain balances
- maintain risk state
- maintain session confidence metrics
- detect degraded state
- trigger resynchronization decisions
- emit outbound exchange commands

The strategy loop is:
single-threaded
serialized
deterministic

All state mutation occurs here.

----------------------------------------------------------------------
3.2 Websocket Reader
----------------------------------------------------------------------

Responsibilities:
- read websocket frames
- decode Binance payloads
- immediately timestamp local receive time
- attach connection epoch
- validate sequence continuity
- emit market events
- emit control events

Reader NEVER:
- mutates strategy state
- reconnects
- decides trading actions

----------------------------------------------------------------------
3.3 Websocket Writer
----------------------------------------------------------------------

Responsibilities:
- serialize outbound websocket writes
- send subscriptions
- send orders
- optionally send websocket pings
- detect outbound transport failures
- emit write-failure control events

Writer NEVER:
- mutates strategy state
- reconnects
- supervises sessions

======================================================================
4. CHANNEL MODEL
======================================================================

Three channels exist:

1. marketEvents
2. controlEvents
3. outboundCommands

----------------------------------------------------------------------
4.1 marketEvents
----------------------------------------------------------------------

Purpose:
high-volume exchange market data stream

Examples:
- trades
- orderbook deltas
- ticker updates
- liquidation updates

Characteristics:
- bursty
- high throughput
- freshness-sensitive
- lower priority than control plane

----------------------------------------------------------------------
4.2 controlEvents
----------------------------------------------------------------------

Purpose:
system/session/control-plane events

Examples:
- GapDetected
- Disconnected
- WriteFailed
- SubscriptionConfirmed
- Resynchronized
- LatencyDegraded
- ShutdownRequested

Characteristics:
- low volume
- latency-sensitive
- state-transition-critical

Control events must NOT compete equally with market flood traffic.

----------------------------------------------------------------------
4.3 outboundCommands
----------------------------------------------------------------------

Purpose:
commands emitted by strategy toward Binance

Examples:
- PlaceOrder
- CancelOrder
- Subscribe
- Unsubscribe
- CloseConnection

Characteristics:
- low volume
- latency-sensitive
- serialized by writer goroutine

======================================================================
5. DATA FLOW MODEL
======================================================================

MARKET DATA FLOW

Binance
->
Websocket Reader
->
marketEvents
->
Strategy Loop

CONTROL FLOW

Reader/Writer/Main
->
controlEvents
->
Strategy Loop

OUTBOUND FLOW

Strategy Loop
->
outboundCommands
->
Websocket Writer
->
Binance

IMPORTANT:

There is NO global event bus.

There is NO many-to-many actor mesh.

The Strategy Loop is the single authoritative consumer
of all meaningful system events.

======================================================================
6. STRATEGY EVENT LOOP MODEL
======================================================================

The architecture resembles a deterministic game simulation loop.

Conceptually:

while running:
prioritize control signals
process market updates
mutate world state
emit actions

The Strategy Loop may internally prioritize:

1. controlEvents
2. marketEvents

using select-based priority handling.

Example conceptual structure:

select {
case ctrl := <-controlEvents:
handleControl(ctrl)

case market := <-marketEvents:
handleMarket(market)
}

This is intentionally:
- centralized
- deterministic
- cooperative

NOT actor-style distributed mutation.

======================================================================
7. GAME LOOP INTERPRETATION
======================================================================

The system maps naturally onto a real-time simulation model.

WorldState
=
local market simulation

NetworkSession
=
exchange connectivity

Entity
=
orders / positions / instruments

Tick
=
event processing iteration

Desync
=
local divergence from exchange reality

Resync
=
authoritative state correction

Epoch
=
generation counter

This framing is intentional because:
real-time games solved deterministic synchronization
problems decades ago.

The architecture resembles:
a deterministic simulation fed by asynchronous I/O.

======================================================================
8. CONNECTION EPOCH MODEL
======================================================================

Every successful websocket connection increments:

epoch++

Each websocket session owns:
- exactly one reader goroutine
- exactly one writer goroutine
- exactly one websocket connection

Every emitted event carries:

Event {
Epoch
...
}

The Strategy Loop stores:

currentEpoch

If:

event.Epoch != currentEpoch

then:
discard event immediately.

Purpose:
prevent stale transport artifacts from corrupting current state.

This prevents:
- reconnect races
- stale delayed sends
- old goroutine emissions
- duplicated subscriptions
- zombie transport behavior

======================================================================
9. SESSION LIFECYCLE MODEL
======================================================================

Reconnect is NOT crash recovery.

Reconnect is:
transport session replacement.

Lifecycle:

1. increment epoch
2. dial websocket
3. spawn reader/writer pair
4. process session
5. detect degraded confidence
6. terminate entire session
7. create fresh session

IMPORTANT:

Reader/writer NEVER reconnect themselves.

No goroutine supervises itself.

No recursive reconnect logic exists.

Transport sessions are disposable.

======================================================================
10. TIME MODEL
======================================================================

Three different temporal observations exist.

----------------------------------------------------------------------
10.1 ExchangeTS
----------------------------------------------------------------------

Timestamp from Binance payload.

Purpose:
authoritative market chronology.

This is the canonical market timeline.

----------------------------------------------------------------------
10.2 RecvTS
----------------------------------------------------------------------

Local monotonic timestamp taken immediately after websocket
frame decode inside Reader goroutine.

Purpose:
measure:
- transport latency
- freshness
- stream silence
- transport degradation

RecvTS is NOT authoritative ordering.

----------------------------------------------------------------------
10.3 ProcessTS
----------------------------------------------------------------------

Timestamp taken when Strategy Loop actually processes event.

Purpose:
measure:
- internal queue delay
- local processing backlog
- event-loop saturation

This is critical telemetry.

======================================================================
11. LATENCY TELEMETRY MODEL
======================================================================

The system continuously computes:

transport_latency
=
RecvTS - ExchangeTS

queue_delay
=
ProcessTS - RecvTS

market_silence
=
now - lastRecvTS

These represent DIFFERENT failure classes.

----------------------------------------------------------------------
11.1 Example: Transport Collapse
----------------------------------------------------------------------

ExchangeTS -> RecvTS = 12s
RecvTS -> ProcessTS = 2ms

Interpretation:
- network/TCP degradation
- retransmission swamp
- stale arriving data
- strategy loop healthy

----------------------------------------------------------------------
11.2 Example: Internal Saturation
----------------------------------------------------------------------

ExchangeTS -> RecvTS = 30ms
RecvTS -> ProcessTS = 4s

Interpretation:
- network healthy
- local processing backlog
- strategy loop overloaded

This distinction is essential for debugging.

======================================================================
12. HEARTBEAT MODEL
======================================================================

Primary heartbeat:
arrival of useful market data.

NOT:
ping/pong.

Ping/pong only assists:
- dead TCP detection
- NAT keepalive
- idle socket handling

Primary liveness metric:

market_silence
=
now - lastRecvTS

This avoids false confidence from sockets that remain technically
open while delivering stale or useless data.

======================================================================
13. CONFIDENCE MODEL
======================================================================

The system continuously estimates:

confidence(local state ~= exchange reality)

Reconnect/resync triggers include:

- no market flow
- sequence gaps
- ancient timestamps
- excessive transport latency
- write failures
- subscription failures
- invalid orderbook continuity
- excessive queue delay
- stale snapshots

Reconnect is therefore:
confidence-driven

NOT:
ping-timeout-driven.

======================================================================
14. RESYNCHRONIZATION MODEL
======================================================================

The primary system metric:

time-to-correct-resync

Definition:

time from degraded confidence
to restored trustworthy synchronized state

Pipeline components:

detect stale stream 1-3s
tcp reconnect 100-500ms
resubscribe 50-200ms
snapshot fetch 100-500ms
book rebuild 10-50ms
validation <10ms

Target total:
~2-5 seconds

This is preferred over:
maintaining technically alive but stale sessions.

======================================================================
15. ORDERBOOK RESYNC MODEL
======================================================================

Orderbook correctness takes priority over stream continuity.

If:
- sequence gaps occur
- continuity invalidated
- stale deltas detected

then:
- local orderbook invalidated
- snapshot refetched
- deltas replayed
- continuity revalidated

The system prefers:
authoritative correction

over:
speculative continuity.

======================================================================
16. DEGRADED MODE MODEL
======================================================================

The Strategy Loop may enter degraded trading modes.

Examples:
- disable opening positions
- allow exits only
- cancel maker orders
- suspend strategy
- force resync

Mode decisions depend on:
- freshness
- queue delay
- transport latency
- continuity confidence

======================================================================
17. FAILURE MODEL
======================================================================

Transport failures:
recoverable.

State corruption:
fatal.

Examples of recoverable failures:
- websocket disconnect
- delayed packets
- write failure
- subscription timeout

Examples of fatal failures:
- inconsistent order state
- impossible position state
- invariant violations
- corrupted local book state

The dangerous state is NOT disconnected socket.

The dangerous state is:
continuing to trade using invalid local reality.

======================================================================
18. SCHEDULING MODEL
======================================================================

Go runtime scheduling:
preemptive underneath.

Trading architecture:
cooperative and serialized.

All meaningful state mutation occurs:
inside one deterministic event loop.

This avoids:
- mutex-heavy concurrency
- temporal races
- distributed mutation
- actor-style debugging complexity

======================================================================
19. DEBUGGING MODEL
======================================================================

Structured logs should include:

- epoch
- exchange timestamp
- receive timestamp
- process timestamp
- queue delay
- transport latency
- sequence numbers
- order IDs
- reconnect cause
- confidence degradation reason

The architecture intentionally preserves:
causal observability.

The system should support:
- event replay
- deterministic simulation
- postmortem analysis
- latency attribution
- reconnect timeline reconstruction

======================================================================
20. DESIGN PRINCIPLE SUMMARY
======================================================================

The architecture intentionally optimizes for:

- deterministic reasoning
- rapid resynchronization
- explicit failure boundaries
- transport disposability
- freshness awareness
- replayability
- observability
- low operational complexity

The architecture intentionally avoids:

- distributed coordination
- actor meshes
- supervision forests
- recursive reconnect logic
- hidden goroutine ownership
- shared mutable state
- microservice decomposition
- "never disconnect" thinking

The system behaves as:
a deterministic market-state simulation engine
continuously repaired from asynchronous exchange input.
```