fix: use double-precision timestamps for sub-millisecond signal timing

This commit is contained in:
nicolas 2026-05-29 15:24:38 -03:00
parent 17aac4b4ef
commit 60257f068f
5 changed files with 20 additions and 20 deletions

View File

@ -21,10 +21,10 @@
#include <math.h> #include <math.h>
#include <stdlib.h> #include <stdlib.h>
static inline int64_t now_ms(void) { static double now_ms(void) {
struct timespec ts; struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts); clock_gettime(CLOCK_REALTIME, &ts);
return (int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; return (double)ts.tv_sec * 1000.0 + (double)ts.tv_nsec / 1000000.0;
} }
/* ── Copied from executor.c for paper-trade simulation ── */ /* ── Copied from executor.c for paper-trade simulation ── */
@ -54,7 +54,7 @@ void evaluator_init(evaluator_t *ev, const triangle_set_t *triangles,
ev->stats.best_triangle_key[0] = '\0'; ev->stats.best_triangle_key[0] = '\0';
} }
bool evaluate_symbol(evaluator_t *ev, uint16_t symbol_idx, int64_t t_sock_arrive_ms, int64_t t_arrive_ms) { bool evaluate_symbol(evaluator_t *ev, uint16_t symbol_idx, int64_t t_sock_arrive_ms, double t_arrive_ms) {
const triangle_set_t *tris = ev->triangles; const triangle_set_t *tris = ev->triangles;
const order_book_t *books = ev->books; const order_book_t *books = ev->books;
const config_t *cfg = ev->cfg; const config_t *cfg = ev->cfg;
@ -79,7 +79,7 @@ bool evaluate_symbol(evaluator_t *ev, uint16_t symbol_idx, int64_t t_sock_arrive
uint32_t *tri_flat = tris->tri_flat; uint32_t *tri_flat = tris->tri_flat;
if (!tri_flat) return false; if (!tri_flat) return false;
static int64_t last_status_ms = 0; static double last_status_ms = 0.0;
for (uint32_t j = 0; j < count; j++) { for (uint32_t j = 0; j < count; j++) {
uint32_t i = tri_flat[offset + j]; uint32_t i = tri_flat[offset + j];
@ -161,7 +161,7 @@ bool evaluate_symbol(evaluator_t *ev, uint16_t symbol_idx, int64_t t_sock_arrive
} }
if (net_bps < ev->stats.worst_net_bps) ev->stats.worst_net_bps = net_bps; if (net_bps < ev->stats.worst_net_bps) ev->stats.worst_net_bps = net_bps;
int64_t now = now_ms(); double now = now_ms();
if (now - last_status_ms >= 30000) { if (now - last_status_ms >= 30000) {
last_status_ms = now; last_status_ms = now;
log_write_screen("[STATUS] evals=%lu signals=%lu " log_write_screen("[STATUS] evals=%lu signals=%lu "

View File

@ -16,7 +16,7 @@ typedef struct {
uint64_t triangles_skipped; /* count of triangles skipped for other reasons */ uint64_t triangles_skipped; /* count of triangles skipped for other reasons */
double best_net_bps; /* best net profit seen (basis points) */ double best_net_bps; /* best net profit seen (basis points) */
double worst_net_bps; /* worst net profit seen (basis points) */ double worst_net_bps; /* worst net profit seen (basis points) */
int64_t last_eval_ts_ms; /* timestamp of last evaluation (milliseconds) */ double last_eval_ts_ms; /* timestamp of last evaluation (milliseconds) */
char best_triangle_key[48]; /* triangle key that produced best_net_bps */ char best_triangle_key[48]; /* triangle key that produced best_net_bps */
} eval_stats_t; } eval_stats_t;
@ -37,6 +37,6 @@ void evaluator_init(evaluator_t *ev, const triangle_set_t *triangles,
executor_slot_t *slots, int n_slots, bool kcs_discount); executor_slot_t *slots, int n_slots, bool kcs_discount);
/* Evaluate all triangles involving the given symbol; returns true if a signal was fired */ /* Evaluate all triangles involving the given symbol; returns true if a signal was fired */
bool evaluate_symbol(evaluator_t *ev, uint16_t symbol_idx, int64_t t_sock_arrive_ms, int64_t t_arrive_ms); bool evaluate_symbol(evaluator_t *ev, uint16_t symbol_idx, int64_t t_sock_arrive_ms, double t_arrive_ms);
#endif #endif

View File

@ -178,10 +178,10 @@ void executor_execute_triangle(executor_thread_t *et,
double exec_start_mono = now_mono_ms(); double exec_start_mono = now_mono_ms();
double leg_timings[6] = {0}; /* leg0_order, leg0_fill, leg1_order, leg1_fill, leg2_order, leg2_fill */ double leg_timings[6] = {0}; /* leg0_order, leg0_fill, leg1_order, leg1_fill, leg2_order, leg2_fill */
snprintf(corr_id, sizeof(corr_id), "%08x%08x%08x%08x", snprintf(corr_id, sizeof(corr_id), "%08x%08x%08x%08x",
(unsigned)(uintptr_t)&sig->legs.legs[0] ^ (unsigned)sig->ts_ms, (unsigned)(uintptr_t)&sig->legs.legs[0] ^ (unsigned)(uint64_t)sig->ts_ms,
(unsigned)sig->ts_ms ^ (unsigned)sig->book_ts_ms, (unsigned)(uint64_t)sig->ts_ms ^ (unsigned)sig->book_ts_ms,
(unsigned)sig->predicted_bps, (unsigned)sig->predicted_bps,
(unsigned)sig->t_arrive_ms); (unsigned)(uint64_t)sig->t_arrive_ms);
double leg_output[3] = {0}; double leg_output[3] = {0};
double fills[3][6] = {{0}}; /* leg, output, avg_price, fee, input_vol, latency_ms */ double fills[3][6] = {{0}}; /* leg, output, avg_price, fee, input_vol, latency_ms */
@ -410,13 +410,13 @@ void executor_execute_triangle(executor_thread_t *et,
/* ── Build timing string (t0 = book_update_arrived = 0.0ms) ── */ /* ── Build timing string (t0 = book_update_arrived = 0.0ms) ── */
char timings_str[512] = ""; char timings_str[512] = "";
int tp = 0; int tp = 0;
int64_t book_base = sig->t_arrive_ms; double book_base = sig->t_arrive_ms;
if (book_base > 0) { if (book_base > 0) {
if (sig->book_ts_ms > 0) if (sig->book_ts_ms > 0)
tp += snprintf(timings_str + tp, sizeof(timings_str) - tp, tp += snprintf(timings_str + tp, sizeof(timings_str) - tp,
"t-1_snapshot=%.3fms ", "t-1_snapshot=%.3fms ",
(double)((int64_t)sig->book_ts_ms - book_base)); sig->book_ts_ms - book_base);
tp += snprintf(timings_str + tp, sizeof(timings_str) - tp, tp += snprintf(timings_str + tp, sizeof(timings_str) - tp,
"t0_arrival=0.0ms "); "t0_arrival=0.0ms ");
@ -424,9 +424,9 @@ void executor_execute_triangle(executor_thread_t *et,
if (sig->ts_ms > 0) if (sig->ts_ms > 0)
tp += snprintf(timings_str + tp, sizeof(timings_str) - tp, tp += snprintf(timings_str + tp, sizeof(timings_str) - tp,
"t1_signal=%.3fms ", "t1_signal=%.3fms ",
(double)((int64_t)sig->ts_ms - book_base)); sig->ts_ms - book_base);
double sig_recv = (double)(exec_start_rt - book_base); double sig_recv = exec_start_rt - book_base;
for (int l = 0; l < 3; l++) { for (int l = 0; l < 3; l++) {
double o = leg_timings[l * 2]; double o = leg_timings[l * 2];
double f = leg_timings[l * 2 + 1]; double f = leg_timings[l * 2 + 1];

View File

@ -98,11 +98,11 @@ typedef struct {
char max_volume[32]; char max_volume[32];
double starting_volume; double starting_volume;
bool live; bool live;
int64_t ts_ms; double ts_ms;
int64_t book_ts_ms; int64_t book_ts_ms;
int64_t t_sock_arrive_ms; int64_t t_sock_arrive_ms;
int64_t t_arrive_ms; double t_arrive_ms;
int64_t t_eval_ms; double t_eval_ms;
uint8_t book_count; uint8_t book_count;
signal_book_t books[3]; signal_book_t books[3];
signal_legs_t legs; signal_legs_t legs;

View File

@ -42,10 +42,10 @@ uint64_t ws_client_now_ms(void) {
return now_ms_impl(); return now_ms_impl();
} }
static uint64_t now_realtime_ms(void) { static double now_realtime_ms(void) {
struct timespec ts; struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts); clock_gettime(CLOCK_REALTIME, &ts);
return (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; return (double)ts.tv_sec * 1000.0 + (double)ts.tv_nsec / 1000000.0;
} }
/* Reset a WebSocket connection to its initial disconnected state, freeing /* Reset a WebSocket connection to its initial disconnected state, freeing
@ -955,7 +955,7 @@ int ws_client_read(ws_client_t *client, uint32_t conn_idx) {
/* Flush: evaluate all symbols updated in this burst */ /* Flush: evaluate all symbols updated in this burst */
if (dirty_count > 0) { if (dirty_count > 0) {
int64_t t_arrive = (int64_t)now_realtime_ms(); double t_arrive = now_realtime_ms();
for (uint32_t d = 0; d < dirty_count; d++) { for (uint32_t d = 0; d < dirty_count; d++) {
evaluate_symbol(client->evaluator, dirty[d], evaluate_symbol(client->evaluator, dirty[d],
conn->t_sock_arrive_ms, t_arrive); conn->t_sock_arrive_ms, t_arrive);