#ifndef FUSED_WS_CLIENT_H #define FUSED_WS_CLIENT_H #include #include #include #include #include #include "book.h" #include "config.h" #include "hash.h" #include "evaluate.h" #include "fill_handler.h" #define MAX_BALANCE_ENTRIES 64 typedef struct { char currency[16]; double available; } balance_entry_t; #define WS_MAX_FRAME_SIZE (128 * 1024) #define WS_MAX_CONNECTIONS 8 #define WS_READ_BUF_SIZE (64 * 1024) /* WebSocket connection state machine */ typedef enum { WS_STATE_DISCONNECTED, /* not connected */ WS_STATE_CONNECTING, /* TCP/TLS handshake in progress */ WS_STATE_GETTING_TOKEN, /* awaiting KuCoin WebSocket token */ WS_STATE_SUBSCRIBING, /* sending subscription messages */ WS_STATE_CONNECTED, /* fully connected and subscribed */ WS_STATE_CLOSING /* graceful close in progress */ } ws_state_t; /* State for a single WebSocket connection */ typedef struct { int fd; /* TCP socket fd */ SSL *ssl; /* OpenSSL SSL object */ SSL_CTX *ctx; /* OpenSSL SSL context */ BIO *bio_mem; /* memory BIO for SSL read buffering */ BIO *bio_ssl; /* SSL BIO */ BIO *bio_socket; /* socket BIO */ char host[256]; /* WebSocket server hostname */ int port; /* WebSocket server port */ char token[256]; /* KuCoin connection token */ char connect_id[64]; /* KuCoin connection ID */ uint32_t ping_interval_ms; /* negotiated ping interval (ms) */ uint32_t ping_timeout_ms; /* ping response timeout (ms) */ ws_state_t state; /* current connection state */ uint64_t last_ping_ms; /* timestamp of last sent ping */ uint64_t last_activity_ms; /* timestamp of last rx/tx activity */ uint32_t reconnect_count; /* consecutive reconnection count */ double reconnect_delay; /* current reconnect backoff delay */ double reconnect_base_delay; /* initial reconnect delay */ double reconnect_max_delay; /* max reconnect delay */ uint8_t read_buf[WS_READ_BUF_SIZE]; /* raw socket read buffer */ size_t read_pos; /* current read position */ size_t read_len; /* bytes available in read_buf */ uint8_t frame_buf[WS_MAX_FRAME_SIZE]; /* reassembled WebSocket frame */ size_t frame_payload_len; /* payload length of current frame */ bool frame_finished; /* true when full frame received */ uint8_t frame_opcode; /* opcode of current frame */ int64_t t_sock_arrive_ms; /* wall-clock when last SSL_read returned */ uint16_t symbol_indices[MAX_SYMBOLS]; /* subscribed symbol indices */ uint32_t symbol_count; /* number of subscribed symbols */ } ws_connection_t; /* Top-level WebSocket client managing multiple connections */ typedef struct { ws_connection_t connections[WS_MAX_CONNECTIONS]; /* fixed-size connection pool */ uint32_t connection_count; /* number of active connections */ const config_t *cfg; /* pointer to configuration */ symbol_table_t *symbols; /* pointer to symbol table */ order_book_t *books; /* pointer to shared order books */ evaluator_t *evaluator; /* pointer to evaluator */ bool running; /* false signals client to stop */ fill_channel_t *fill_ch; /* fill event channel (hot→executor) */ int fill_drop_warn; /* rate-limited fill drop warning counter */ balance_entry_t balance_cache[MAX_BALANCE_ENTRIES]; /* latest available balances from WS */ int balance_count; pthread_mutex_t balance_lock; int balance_wake_fd; /* eventfd: written on every balance update */ } ws_client_t; /* Initialise a WebSocket client with config, symbol table, books, and evaluator */ int ws_client_init(ws_client_t *client, const config_t *cfg, symbol_table_t *symbols, order_book_t *books, evaluator_t *evaluator); /* Destroy WebSocket client and close all connections */ void ws_client_destroy(ws_client_t *client); /* Initiate a WebSocket connection (non-blocking) */ int ws_client_connect(ws_client_t *client, uint32_t conn_idx); /* Disconnect a WebSocket connection */ void ws_client_disconnect(ws_client_t *client, uint32_t conn_idx); /* Read and process data from a WebSocket connection */ int ws_client_read(ws_client_t *client, uint32_t conn_idx); /* Write raw data to a WebSocket connection */ int ws_client_write(ws_connection_t *conn, const void *data, size_t len); /* Subscribe to a set of symbols on a connection */ int ws_client_subscribe(ws_client_t *client, uint32_t conn_idx, const uint16_t *symbol_indices, uint32_t count); /* Unsubscribe from a set of symbols on a connection */ int ws_client_unsubscribe(ws_client_t *client, uint32_t conn_idx, const uint16_t *symbol_indices, uint32_t count); /* Fetch a WebSocket token from the KuCoin API (prefers private when cfg has keys) */ int ws_client_fetch_token_priv(ws_connection_t *conn, const config_t *cfg); /* Process a received WebSocket frame (dispatch to book updates, etc.). * Returns symbol index on book update, -1 otherwise. */ int16_t ws_client_process_frame(ws_client_t *client, uint32_t conn_idx); /* Send a WebSocket ping frame */ int ws_client_send_ping(ws_connection_t *conn); /* Get current monotonic timestamp in milliseconds */ uint64_t ws_client_now_ms(void); /* Wait for balance WS to confirm available >= min_amount for a currency. Blocks on the balance wake eventfd for up to timeout_ms. Returns true once the condition is met, false on timeout. */ bool ws_client_await_balance(ws_client_t *client, const char *currency, double min_amount, int64_t timeout_ms); /* Read the latest known available balance for a currency from cache (thread-safe). Returns 0 if currency is not in cache. */ double ws_client_latest_balance(ws_client_t *client, const char *currency); #endif