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:
Codex
2026-06-01 23:45:50 +02:00
parent 7d13df35db
commit b3b28bb44a
3 changed files with 331 additions and 10 deletions

View File

@@ -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.