PINK: kernel fee prediction + calibration loop
ExchangeFeeConfig in AccountState: taker_rate, maker_rate, lot_step, tick_size, funding_interval_secs calibration_ratio: EMA of actual/expected, updated on every fill Kernel now predicts fees at fill time (PREDICTED_FILL event): k_capital updated immediately without waiting for WS FILL_SETTLED When actual fee arrives, prediction is replaced and ratio recalibrated Reconcile delta: 0.000000 (was ~0.9 USDT in canary without prediction) Calibration loop on connect(): Fetches recent fill history, validates model vs exchange actuals deviation < 1pct -> OK; < 5pct -> WARN; >= 5pct -> ERROR (pre-trade gate) New FFI: dita_kernel_set_exchange_config_json, dita_kernel_calibrate_fee_json New ExecutionKernel methods: set_exchange_config(), calibrate_fee() pink_direct.py: loads BingX fee config on connect, calibrates before stream 131/131 offline pass.
This commit is contained in:
@@ -121,6 +121,10 @@ class _RustKernelLib:
|
||||
self.lib.dita_kernel_snapshot_json.restype = ctypes.c_void_p
|
||||
self.lib.dita_kernel_set_seed_capital.argtypes = [ctypes.c_void_p, ctypes.c_double]
|
||||
self.lib.dita_kernel_set_seed_capital.restype = ctypes.c_int
|
||||
self.lib.dita_kernel_set_exchange_config_json.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
|
||||
self.lib.dita_kernel_set_exchange_config_json.restype = ctypes.c_int
|
||||
self.lib.dita_kernel_calibrate_fee_json.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
|
||||
self.lib.dita_kernel_calibrate_fee_json.restype = ctypes.c_void_p
|
||||
self.lib.dita_kernel_on_account_event_json.argtypes = [
|
||||
ctypes.c_void_p,
|
||||
ctypes.c_char_p,
|
||||
@@ -220,6 +224,18 @@ class _RustKernelLib:
|
||||
rc = self.lib.dita_kernel_set_seed_capital(handle, ctypes.c_double(seed))
|
||||
return rc == 0
|
||||
|
||||
def set_exchange_config(self, handle: ctypes.c_void_p, config: Dict[str, Any]) -> bool:
|
||||
encoded = json.dumps(config, separators=(",", ":")).encode("utf-8")
|
||||
rc = self.lib.dita_kernel_set_exchange_config_json(handle, ctypes.c_char_p(encoded))
|
||||
return rc == 0
|
||||
|
||||
def calibrate_fee(self, handle: ctypes.c_void_p, fill_price: float, fill_qty: float, actual_fee: float) -> Dict[str, Any]:
|
||||
payload = json.dumps({"fill_price": fill_price, "fill_qty": fill_qty, "actual_fee": actual_fee}).encode("utf-8")
|
||||
raw = self.lib.dita_kernel_calibrate_fee_json(handle, ctypes.c_char_p(payload))
|
||||
if not raw:
|
||||
return {}
|
||||
return json.loads(self._take_string(raw))
|
||||
|
||||
def on_account_event(
|
||||
self, handle: ctypes.c_void_p, event: Dict[str, Any]
|
||||
) -> Dict[str, Any]:
|
||||
@@ -761,6 +777,46 @@ class ExecutionKernel:
|
||||
"""Set the kernel's seed capital for K-value fold. Call once at init."""
|
||||
_get_rust().set_seed_capital(self._backend, float(seed))
|
||||
|
||||
def set_exchange_config(self, config: Dict[str, Any]) -> None:
|
||||
"""
|
||||
Load the exchange fee schedule into the kernel's fee prediction model.
|
||||
|
||||
config keys (all optional — unset keys keep defaults):
|
||||
taker_rate float fraction (0.0005 = 0.05%)
|
||||
maker_rate float fraction (0.0002 = 0.02%)
|
||||
lot_step float quantity increment
|
||||
tick_size float price increment
|
||||
funding_interval_secs int seconds between funding payments (28800 = 8 h)
|
||||
|
||||
After this call the kernel can predict fees at fill time so K-capital
|
||||
tracks E-wallet without waiting for the WS FILL_SETTLED event.
|
||||
"""
|
||||
_get_rust().set_exchange_config(self._backend, config)
|
||||
|
||||
def calibrate_fee(
|
||||
self,
|
||||
fill_price: float,
|
||||
fill_qty: float,
|
||||
actual_fee: float,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Validate the fee model against one known fill.
|
||||
|
||||
Returns:
|
||||
expected_fee model prediction before calibration
|
||||
actual_fee exchange-reported value
|
||||
ratio actual / expected
|
||||
deviation_pct |ratio - 1| × 100
|
||||
calibration_status "OK" (<1%) / "WARN" (<5%) / "ERROR" (≥5%)
|
||||
calibration_ratio updated EMA of actual/expected ratio
|
||||
calibration_samples fills seen so far
|
||||
|
||||
Call once on startup with a recent fill from account history before
|
||||
enabling live trading. If status == ERROR, the fee model needs manual
|
||||
review before K-capital figures can be trusted.
|
||||
"""
|
||||
return _get_rust().calibrate_fee(self._backend, float(fill_price), float(fill_qty), float(actual_fee))
|
||||
|
||||
def on_account_event(self, event: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
Apply an account-level exchange event atomically to the kernel.
|
||||
|
||||
Reference in New Issue
Block a user