feat: add allow_same_quote config, fix overlapping pair isolation to check all 3 legs, document config example

This commit is contained in:
nicolas 2026-05-30 11:22:16 -03:00
parent 60257f068f
commit 930365f072
5 changed files with 49 additions and 8 deletions

View File

@ -1,23 +1,54 @@
# Live trading mode. false = paper trades (test endpoint, no real orders).
live_mode: false
fused_engine:
# Log verbosity: INFO, DEBUG, or WARN
log_level: INFO
# Minimum predicted profit in basis points (0.01%) to fire a signal
signal_threshold_bps: 2
# Currencies to exclude from triangle enumeration entirely
excluded_currencies: [EUR, BRL]
# Currencies the bot holds as capital. Triangles must start and end in one of these.
hold_currencies: [USDT, USDC, USD1]
# KuCoin WebSocket base URL (override for alternate endpoint)
ws_url: wss://ws-api-spot.kucoin.com
# KuCoin token endpoint (public bullet for WS credentials)
token_url: https://api.kucoin.com/api/v1/bullet-public
# Initial WebSocket reconnect delay in seconds (exponential backoff)
reconnect_base_delay: 1.0
# Maximum WebSocket reconnect delay in seconds
reconnect_max_delay: 60.0
# WebSocket ping interval in seconds
heartbeat_interval: 18.0
# If true, apply 20% discount to fee rates (KCS holding benefit)
kcs_discount_active: false
# Number of parallel executor threads (each owns one signal slot, max 16)
concurrent_slots: 1
# Wait for WS balance settlement between live legs (requires private WS channel)
balance_wait_enabled: false
# Allow parallel triangles that share the same primary quote currency
# Useful when exchange balance can support multiple concurrent trades
allow_same_quote: false
# Maximum quote amount per triangle signal, per currency
initial_capital:
USDT: 5
USDC: 5
USD1: 5
# KuCoin API credentials for authenticated endpoints (top-level keys)
kucoin_api_key: ""
kucoin_api_secret: ""
kucoin_api_passphrase: ""

View File

@ -80,6 +80,8 @@ static void handle_value(parse_state_t *st, const char *val) {
st->cfg->concurrent_slots = atoi(val);
} else if (strcmp(key, "balance_wait_enabled") == 0) {
st->cfg->balance_wait_enabled = (strcmp(val, "true") == 0 || strcmp(val, "yes") == 0);
} else if (strcmp(key, "allow_same_quote") == 0) {
st->cfg->allow_same_quote = (strcmp(val, "true") == 0 || strcmp(val, "yes") == 0);
}
} else if (strcmp(st->section, "executor") == 0) {
return;
@ -124,6 +126,7 @@ int config_load(const char *path, config_t *cfg) {
cfg->concurrent_slots = 1;
cfg->live_mode = false;
cfg->balance_wait_enabled = false;
cfg->allow_same_quote = false;
FILE *f = fopen(path, "r");
if (!f) {

View File

@ -37,6 +37,7 @@ typedef struct {
int concurrent_slots; /* number of executor threads (1 = single-threaded) */
bool live_mode; /* live trading vs paper/simulation */
bool balance_wait_enabled; /* wait for WS balance settlement between legs */
bool allow_same_quote; /* allow same primary quote in parallel triangles */
/* Capital allocation limits — each entry maps a currency ticker to a max
* quote amount the fused engine may deploy for any one triangle signal. */

View File

@ -146,19 +146,21 @@ void executor_execute_triangle(executor_thread_t *et,
log_write("[EXEC] Dropping signal for overlapping triangle: %s\n", sig->triangle_key);
return;
}
if (strcmp(sh->primary_quotes[i], sig->primary_quote) == 0) {
if (!et->cfg->allow_same_quote && strcmp(sh->primary_quotes[i], sig->primary_quote) == 0) {
pthread_mutex_unlock(&sh->lock);
log_write("[EXEC] Dropping signal for same primary quote: %s\n", sig->triangle_key);
return;
}
for (int p = 0; p < 3; p++) {
if (sh->pairs[i] == pair_hashes[p]) {
for (int k = 0; k < 3; k++) {
if (sh->pairs[i][k] == pair_hashes[p]) {
pthread_mutex_unlock(&sh->lock);
log_write("[EXEC] Dropping signal for overlapping pair on %s\n", sig->triangle_key);
return;
}
}
}
}
/* Register this execution */
int slot = -1;
for (int i = 0; i < MAX_IN_FLIGHT; i++) {
@ -167,7 +169,9 @@ void executor_execute_triangle(executor_thread_t *et,
if (slot >= 0) {
strncpy(sh->triangles[slot], sig->triangle_key, sizeof(sh->triangles[slot]) - 1);
strncpy(sh->primary_quotes[slot], sig->primary_quote, sizeof(sh->primary_quotes[slot]) - 1);
sh->pairs[slot] = pair_hashes[0];
sh->pairs[slot][0] = pair_hashes[0];
sh->pairs[slot][1] = pair_hashes[1];
sh->pairs[slot][2] = pair_hashes[2];
sh->count++;
}
pthread_mutex_unlock(&sh->lock);
@ -493,7 +497,9 @@ void executor_execute_triangle(executor_thread_t *et,
if (strcmp(sh->triangles[i], sig->triangle_key) == 0) {
sh->triangles[i][0] = '\0';
sh->primary_quotes[i][0] = '\0';
sh->pairs[i] = 0;
sh->pairs[i][0] = 0;
sh->pairs[i][1] = 0;
sh->pairs[i][2] = 0;
if (sh->count > 0) sh->count--;
break;
}

View File

@ -15,7 +15,7 @@
typedef struct {
pthread_mutex_t lock;
char triangles[MAX_IN_FLIGHT][128];
uint64_t pairs[MAX_IN_FLIGHT];
uint64_t pairs[MAX_IN_FLIGHT][3];
char primary_quotes[MAX_IN_FLIGHT][16];
int count;
char last_triangles[MAX_TRACKED_TRIANGLES][128];