triangular_arbitrage_bot/src/ws_client.h

132 lines
6.5 KiB
C

#ifndef FUSED_WS_CLIENT_H
#define FUSED_WS_CLIENT_H
#include <stdint.h>
#include <stdbool.h>
#include <pthread.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#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