remove fee table path: fee rate now computed solely from formula fee_category * 0.001 * taker_fee_coeff
This commit is contained in:
parent
faa88070e7
commit
405fc557dc
40
src/main.c
40
src/main.c
|
|
@ -70,32 +70,6 @@ int main(int argc, char *argv[]) {
|
||||||
log_write("[MAIN] Config: threshold=%.1f bps, cooldown=%.0fs, hold=%u currencies\n",
|
log_write("[MAIN] Config: threshold=%.1f bps, cooldown=%.0fs, hold=%u currencies\n",
|
||||||
cfg.signal_threshold_bps, cfg.cooldown_seconds, cfg.hold_currency_count);
|
cfg.signal_threshold_bps, cfg.cooldown_seconds, cfg.hold_currency_count);
|
||||||
|
|
||||||
// Fetch fee table with auth (KuCoin /api/v1/base-fee)
|
|
||||||
fee_entry_t *fees = NULL;
|
|
||||||
uint32_t fee_count = 0;
|
|
||||||
|
|
||||||
log_write("[MAIN] API key present: %s\n", cfg.kucoin_api_key[0] ? "yes" : "no");
|
|
||||||
if (cfg.kucoin_api_key[0] && cfg.kucoin_api_secret[0] && cfg.kucoin_api_passphrase[0]) {
|
|
||||||
log_write("[MAIN] >>> Calling https_get_auth for /api/v1/base-fee\n");
|
|
||||||
int out_len = 0;
|
|
||||||
char *fee_json = https_get_auth("api.kucoin.com", 443, "/api/v1/base-fee",
|
|
||||||
cfg.kucoin_api_key, cfg.kucoin_api_secret,
|
|
||||||
cfg.kucoin_api_passphrase, &out_len);
|
|
||||||
log_write("[MAIN] <<< https_get_auth returned: %p, len=%d\n", fee_json, out_len);
|
|
||||||
if (fee_json && out_len > 0) {
|
|
||||||
if (load_fee_table(fee_json, &fees, &fee_count) == 0) {
|
|
||||||
log_write("[MAIN] Fee table: %u currencies loaded\n", fee_count);
|
|
||||||
} else {
|
|
||||||
log_write("[MAIN] Fee table parse failed, using default 0.1%% fees\n");
|
|
||||||
}
|
|
||||||
free(fee_json);
|
|
||||||
} else {
|
|
||||||
log_write("[MAIN] Could not fetch fee table, using default 0.1%% fees\n");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log_write("[MAIN] No API credentials in config, using default 0.1%% fees\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Discover symbols from KuCoin: fetch pairs, enumerate triangles, populate table
|
// Discover symbols from KuCoin: fetch pairs, enumerate triangles, populate table
|
||||||
log_write("[MAIN] >>> Initializing symbol table\n");
|
log_write("[MAIN] >>> Initializing symbol table\n");
|
||||||
symbol_table_t symbols;
|
symbol_table_t symbols;
|
||||||
|
|
@ -107,9 +81,8 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
log_write("[MAIN] >>> Calling discover_symbols\n");
|
log_write("[MAIN] >>> Calling discover_symbols\n");
|
||||||
triangle_set_t triangles;
|
triangle_set_t triangles;
|
||||||
if (discover_symbols(&symbols, &triangles, &cfg, fees, fee_count) != 0) {
|
if (discover_symbols(&symbols, &triangles, &cfg) != 0) {
|
||||||
log_write("[MAIN] Symbol discovery failed\n");
|
log_write("[MAIN] Symbol discovery failed\n");
|
||||||
free_fee_table(fees);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
log_write("[MAIN] <<< discover_symbols done: %u symbols, %u triangles\n", symbols.count, triangles.triangle_count);
|
log_write("[MAIN] <<< discover_symbols done: %u symbols, %u triangles\n", symbols.count, triangles.triangle_count);
|
||||||
|
|
@ -120,7 +93,7 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
if (!books) {
|
if (!books) {
|
||||||
log_write("[MAIN] Failed to allocate books\n");
|
log_write("[MAIN] Failed to allocate books\n");
|
||||||
free_fee_table(fees);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -130,7 +103,7 @@ int main(int argc, char *argv[]) {
|
||||||
executor_slot_t *slots = calloc((size_t)n_slots, sizeof(executor_slot_t));
|
executor_slot_t *slots = calloc((size_t)n_slots, sizeof(executor_slot_t));
|
||||||
if (!slots) {
|
if (!slots) {
|
||||||
log_write("[MAIN] Failed to allocate slots\n");
|
log_write("[MAIN] Failed to allocate slots\n");
|
||||||
free_fee_table(fees);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < n_slots; i++) {
|
for (int i = 0; i < n_slots; i++) {
|
||||||
|
|
@ -139,7 +112,7 @@ int main(int argc, char *argv[]) {
|
||||||
log_write("[MAIN] Failed to create slot eventfd\n");
|
log_write("[MAIN] Failed to create slot eventfd\n");
|
||||||
for (int j = 0; j < i; j++) close(slots[j].eventfd);
|
for (int j = 0; j < i; j++) close(slots[j].eventfd);
|
||||||
free(slots);
|
free(slots);
|
||||||
free_fee_table(fees);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -153,7 +126,7 @@ int main(int argc, char *argv[]) {
|
||||||
if (ws_client_init(&ws_client, &cfg, &symbols, books, &evaluator) != 0) {
|
if (ws_client_init(&ws_client, &cfg, &symbols, books, &evaluator) != 0) {
|
||||||
log_write("[MAIN] Failed to init WS client\n");
|
log_write("[MAIN] Failed to init WS client\n");
|
||||||
triangle_set_free(&triangles);
|
triangle_set_free(&triangles);
|
||||||
free_fee_table(fees);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -162,7 +135,7 @@ int main(int argc, char *argv[]) {
|
||||||
log_write("[MAIN] Failed to init event loops\n");
|
log_write("[MAIN] Failed to init event loops\n");
|
||||||
ws_client_destroy(&ws_client);
|
ws_client_destroy(&ws_client);
|
||||||
triangle_set_free(&triangles);
|
triangle_set_free(&triangles);
|
||||||
free_fee_table(fees);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -273,7 +246,6 @@ int main(int argc, char *argv[]) {
|
||||||
free(slots);
|
free(slots);
|
||||||
triangle_set_free(&triangles);
|
triangle_set_free(&triangles);
|
||||||
free(books);
|
free(books);
|
||||||
free_fee_table(fees);
|
|
||||||
|
|
||||||
log_write("[MAIN] Shutdown complete.\n");
|
log_write("[MAIN] Shutdown complete.\n");
|
||||||
log_shutdown();
|
log_shutdown();
|
||||||
|
|
|
||||||
|
|
@ -91,17 +91,7 @@ static bool ph_find(const pair_hash_t *ph, const char *c1, const char *c2, uint3
|
||||||
|
|
||||||
/* Fee rate per category: cat1=0.1%, cat2=0.2%, cat3=0.3%.
|
/* Fee rate per category: cat1=0.1%, cat2=0.2%, cat3=0.3%.
|
||||||
Looks up the fee_currency in the fee table first; falls back to category formula. */
|
Looks up the fee_currency in the fee table first; falls back to category formula. */
|
||||||
static double fee_rate_for_pair(const trading_pair_t *pair, bool kcs_discount,
|
static double fee_rate_for_pair(const trading_pair_t *pair, bool kcs_discount) {
|
||||||
const fee_entry_t *fees, uint32_t fee_count) {
|
|
||||||
if (fees && fee_count > 0) {
|
|
||||||
for (uint32_t i = 0; i < fee_count; i++) {
|
|
||||||
if (strcmp(fees[i].currency, "ALL") == 0 ||
|
|
||||||
strcmp(fees[i].currency, pair->fee_currency) == 0) {
|
|
||||||
double rate = fees[i].taker_fee;
|
|
||||||
return kcs_discount ? rate * 0.8 : rate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
double base = pair->fee_category * 0.001;
|
double base = pair->fee_category * 0.001;
|
||||||
double rate = base * pair->taker_fee_coeff;
|
double rate = base * pair->taker_fee_coeff;
|
||||||
return kcs_discount ? rate * 0.8 : rate;
|
return kcs_discount ? rate * 0.8 : rate;
|
||||||
|
|
@ -223,7 +213,7 @@ int fetch_trading_pairs(pair_list_t *out) {
|
||||||
* tri_index (symbol -> flat triangle list) for fast symbol-based lookup.
|
* tri_index (symbol -> flat triangle list) for fast symbol-based lookup.
|
||||||
*/
|
*/
|
||||||
int discover_symbols(symbol_table_t *symbols, triangle_set_t *triangles,
|
int discover_symbols(symbol_table_t *symbols, triangle_set_t *triangles,
|
||||||
const config_t *cfg, const fee_entry_t *fees, uint32_t fee_count) {
|
const config_t *cfg) {
|
||||||
pair_list_t pairs;
|
pair_list_t pairs;
|
||||||
if (fetch_trading_pairs(&pairs) != 0) {
|
if (fetch_trading_pairs(&pairs) != 0) {
|
||||||
log_write("[SYMBOLS] Failed to fetch pairs\n");
|
log_write("[SYMBOLS] Failed to fetch pairs\n");
|
||||||
|
|
@ -358,9 +348,9 @@ int discover_symbols(symbol_table_t *symbols, triangle_set_t *triangles,
|
||||||
{ size_t _l = strlen(hold); if (_l >= CURRENCY_NAME_LEN) _l = CURRENCY_NAME_LEN - 1; memcpy(t->base, hold, _l); t->base[_l] = '\0'; }
|
{ size_t _l = strlen(hold); if (_l >= CURRENCY_NAME_LEN) _l = CURRENCY_NAME_LEN - 1; memcpy(t->base, hold, _l); t->base[_l] = '\0'; }
|
||||||
{ size_t _l = strlen(x); if (_l >= CURRENCY_NAME_LEN) _l = CURRENCY_NAME_LEN - 1; memcpy(t->mid, x, _l); t->mid[_l] = '\0'; }
|
{ size_t _l = strlen(x); if (_l >= CURRENCY_NAME_LEN) _l = CURRENCY_NAME_LEN - 1; memcpy(t->mid, x, _l); t->mid[_l] = '\0'; }
|
||||||
{ size_t _l = strlen(y); if (_l >= CURRENCY_NAME_LEN) _l = CURRENCY_NAME_LEN - 1; memcpy(t->quote, y, _l); t->quote[_l] = '\0'; }
|
{ size_t _l = strlen(y); if (_l >= CURRENCY_NAME_LEN) _l = CURRENCY_NAME_LEN - 1; memcpy(t->quote, y, _l); t->quote[_l] = '\0'; }
|
||||||
t->fee_factor[0] = 1.0 - fee_rate_for_pair(&pairs.pairs[i1], kcs, fees, fee_count);
|
t->fee_factor[0] = 1.0 - fee_rate_for_pair(&pairs.pairs[i1], kcs);
|
||||||
t->fee_factor[1] = 1.0 - fee_rate_for_pair(&pairs.pairs[i2], kcs, fees, fee_count);
|
t->fee_factor[1] = 1.0 - fee_rate_for_pair(&pairs.pairs[i2], kcs);
|
||||||
t->fee_factor[2] = 1.0 - fee_rate_for_pair(&pairs.pairs[i3], kcs, fees, fee_count);
|
t->fee_factor[2] = 1.0 - fee_rate_for_pair(&pairs.pairs[i3], kcs);
|
||||||
t->base_increment[0] = pairs.pairs[i1].base_increment;
|
t->base_increment[0] = pairs.pairs[i1].base_increment;
|
||||||
t->base_increment[1] = pairs.pairs[i2].base_increment;
|
t->base_increment[1] = pairs.pairs[i2].base_increment;
|
||||||
t->base_increment[2] = pairs.pairs[i3].base_increment;
|
t->base_increment[2] = pairs.pairs[i3].base_increment;
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,6 @@ int fetch_trading_pairs(pair_list_t *out);
|
||||||
|
|
||||||
/* Full symbol discovery: fetch pairs, enumerate triangles, populate tables */
|
/* Full symbol discovery: fetch pairs, enumerate triangles, populate tables */
|
||||||
int discover_symbols(symbol_table_t *symbols, triangle_set_t *triangles,
|
int discover_symbols(symbol_table_t *symbols, triangle_set_t *triangles,
|
||||||
const config_t *cfg, const fee_entry_t *fees, uint32_t fee_count);
|
const config_t *cfg);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,17 @@
|
||||||
/*
|
/*
|
||||||
* triangle.c - Triangle data structure management and fee table parsing
|
* triangle.c - Triangle data structure management
|
||||||
*
|
*
|
||||||
* triangle_set_init is a stub; actual triangle enumeration happens in
|
* triangle_set_init is a stub; actual triangle enumeration happens in
|
||||||
* symbols_api.c:discover_symbols. This file provides fee parsing from
|
* symbols_api.c:discover_symbols.
|
||||||
* KuCoin's /api/v1/base-fee response (both object and array formats).
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "triangle.h"
|
#include "triangle.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include "cJSON.h"
|
|
||||||
|
|
||||||
int triangle_set_init(triangle_set_t *set, const symbol_table_t *symbols,
|
int triangle_set_init(triangle_set_t *set, const symbol_table_t *symbols,
|
||||||
const config_t *cfg, const fee_entry_t *fees,
|
const config_t *cfg) {
|
||||||
uint32_t fee_count) {
|
(void)set; (void)symbols; (void)cfg;
|
||||||
/* Triangle enumeration is now done in discover_symbols (symbols_api.c) */
|
|
||||||
(void)set; (void)symbols; (void)cfg; (void)fees; (void)fee_count;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -29,68 +24,3 @@ void triangle_set_free(triangle_set_t *set) {
|
||||||
set->tri_flat = NULL;
|
set->tri_flat = NULL;
|
||||||
set->triangle_count = 0;
|
set->triangle_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Parse KuCoin /api/v1/base-fee JSON response.
|
|
||||||
* Supports two formats:
|
|
||||||
* - Object: {"takerFeeRate": 0.001, ...}
|
|
||||||
* - Array: [{"currency": "BTC", "takerFeeRate": 0.001}, ...]
|
|
||||||
* Stores result as fee_entry_t array (single "ALL" entry for object format).
|
|
||||||
*/
|
|
||||||
int load_fee_table(const char *json, fee_entry_t **out_fees, uint32_t *out_count) {
|
|
||||||
cJSON *root = cJSON_Parse(json);
|
|
||||||
if (!root) return -1;
|
|
||||||
|
|
||||||
cJSON *code = cJSON_GetObjectItem(root, "code");
|
|
||||||
if (!cJSON_IsString(code) || strcmp(code->valuestring, "200000") != 0) {
|
|
||||||
cJSON_Delete(root);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
cJSON *data = cJSON_GetObjectItem(root, "data");
|
|
||||||
if (!data) {
|
|
||||||
cJSON_Delete(root);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
*out_count = 0;
|
|
||||||
|
|
||||||
if (cJSON_IsObject(data)) {
|
|
||||||
// Single global fee rate
|
|
||||||
*out_fees = calloc(1, sizeof(fee_entry_t));
|
|
||||||
if (!*out_fees) { cJSON_Delete(root); return -1; }
|
|
||||||
fee_entry_t *fee = *out_fees;
|
|
||||||
strncpy(fee->currency, "ALL", CURRENCY_NAME_LEN - 1);
|
|
||||||
cJSON *taker = cJSON_GetObjectItem(data, "takerFeeRate");
|
|
||||||
cJSON *maker = cJSON_GetObjectItem(data, "makerFeeRate");
|
|
||||||
if (cJSON_IsNumber(taker)) fee->taker_fee = taker->valuedouble;
|
|
||||||
if (cJSON_IsNumber(maker)) fee->maker_fee = maker->valuedouble;
|
|
||||||
*out_count = 1;
|
|
||||||
} else if (cJSON_IsArray(data)) {
|
|
||||||
// Per-currency fee rates array
|
|
||||||
int arr_size = cJSON_GetArraySize(data);
|
|
||||||
*out_fees = calloc(arr_size, sizeof(fee_entry_t));
|
|
||||||
if (!*out_fees) { cJSON_Delete(root); return -1; }
|
|
||||||
cJSON *item;
|
|
||||||
int idx = 0;
|
|
||||||
cJSON_ArrayForEach(item, data) {
|
|
||||||
if (!cJSON_IsObject(item)) continue;
|
|
||||||
fee_entry_t *fee = &((*out_fees)[idx]);
|
|
||||||
cJSON *cur = cJSON_GetObjectItem(item, "currency");
|
|
||||||
cJSON *taker = cJSON_GetObjectItem(item, "takerFeeRate");
|
|
||||||
cJSON *maker = cJSON_GetObjectItem(item, "makerFeeRate");
|
|
||||||
if (cJSON_IsString(cur)) strncpy(fee->currency, cur->valuestring, CURRENCY_NAME_LEN - 1);
|
|
||||||
if (cJSON_IsNumber(taker)) fee->taker_fee = taker->valuedouble;
|
|
||||||
if (cJSON_IsNumber(maker)) fee->maker_fee = maker->valuedouble;
|
|
||||||
idx++;
|
|
||||||
}
|
|
||||||
*out_count = idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
cJSON_Delete(root);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void free_fee_table(fee_entry_t *fees) {
|
|
||||||
free(fees);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -111,14 +111,8 @@ typedef struct {
|
||||||
|
|
||||||
/* Initialise triangle set: enumerate all triangles from the symbol table */
|
/* Initialise triangle set: enumerate all triangles from the symbol table */
|
||||||
int triangle_set_init(triangle_set_t *set, const symbol_table_t *symbols,
|
int triangle_set_init(triangle_set_t *set, const symbol_table_t *symbols,
|
||||||
const config_t *cfg, const fee_entry_t *fees,
|
const config_t *cfg);
|
||||||
uint32_t fee_count);
|
|
||||||
/* Free all memory owned by a triangle set */
|
/* Free all memory owned by a triangle set */
|
||||||
void triangle_set_free(triangle_set_t *set);
|
void triangle_set_free(triangle_set_t *set);
|
||||||
|
|
||||||
/* Parse fee table JSON into an array of fee_entry_t */
|
|
||||||
int load_fee_table(const char *json, fee_entry_t **out_fees, uint32_t *out_count);
|
|
||||||
/* Free a fee table array */
|
|
||||||
void free_fee_table(fee_entry_t *fees);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue