88 lines
2.4 KiB
Python
88 lines
2.4 KiB
Python
from __future__ import annotations
|
|
|
|
from tools.calibrate_torch_thresholds import (
|
|
CalibrationResult,
|
|
ForecastRecord,
|
|
_apply_platt_calibration,
|
|
_choose_recommendation,
|
|
_fit_platt_calibration,
|
|
_entry_validation_skill,
|
|
)
|
|
|
|
|
|
def _result(*, trades: int, average: float, total: float, profit_factor: float) -> CalibrationResult:
|
|
return CalibrationResult(
|
|
edge=0.05,
|
|
probability=0.52,
|
|
confidence=0.4,
|
|
trades=trades,
|
|
wins=max(0, trades // 2),
|
|
win_rate=0.5,
|
|
total_net_percent=total,
|
|
average_net_percent=average,
|
|
max_drawdown_percent=1.0,
|
|
profit_factor=profit_factor,
|
|
score=1.0,
|
|
)
|
|
|
|
|
|
def _record(index: int, probability: float, future: float) -> ForecastRecord:
|
|
return ForecastRecord(
|
|
symbol="BTCUSDT",
|
|
index=index,
|
|
timestamp=index,
|
|
close=100.0,
|
|
high=101.0,
|
|
low=99.0,
|
|
next_open=100.0,
|
|
next_timestamp=index + 1,
|
|
atr=1.0,
|
|
expected_percent=0.1,
|
|
probability_up=probability,
|
|
confidence=0.5,
|
|
skill=0.1,
|
|
q50_percent=0.1,
|
|
block_entry=False,
|
|
future_net_percent=future,
|
|
benchmark_entry=False,
|
|
benchmark_exit=False,
|
|
)
|
|
|
|
|
|
def test_calibration_does_not_fallback_to_too_few_trades() -> None:
|
|
selected = _choose_recommendation(
|
|
[_result(trades=1, average=2.0, total=2.0, profit_factor=999.0)],
|
|
min_trades=30,
|
|
)
|
|
|
|
assert selected is None
|
|
|
|
|
|
def test_calibration_selects_only_viable_result() -> None:
|
|
viable = _result(trades=30, average=0.2, total=6.0, profit_factor=1.4)
|
|
|
|
assert _choose_recommendation([viable], min_trades=30) is viable
|
|
|
|
|
|
def test_platt_calibration_learns_probability_direction_from_train_records() -> None:
|
|
records = [
|
|
_record(index, 0.8 if index % 2 else 0.2, -1.0 if index % 2 else 1.0)
|
|
for index in range(100)
|
|
]
|
|
|
|
calibration = _fit_platt_calibration(records)
|
|
calibrated = _apply_platt_calibration(
|
|
[_record(101, 0.8, -1.0), _record(102, 0.2, 1.0)],
|
|
calibration,
|
|
)
|
|
|
|
assert calibration["slope"] < 0
|
|
assert calibrated[0].probability_up < calibrated[1].probability_up
|
|
|
|
|
|
def test_entry_quality_never_falls_back_to_holdout_skill() -> None:
|
|
entry = {"validation_skill": 0.12, "skill": 0.99, "holdout_skill": 0.99}
|
|
|
|
assert _entry_validation_skill(entry) == 0.12
|
|
assert _entry_validation_skill({"skill": 0.99, "holdout_skill": 0.99}) == 0.0
|