fix: address follow-up review findings

- fix(notifier): capture fire-and-forget Future exceptions via done_callback
- fix(bot): add _close_event.set() in SYNC path to unblock _close_and_reenter
- fix(ml_features): apply z-score to oi_price_spread (oi_z - ret_1_z) matching training
- fix(backtester): clean up import ordering after _calc_trade_stats extraction
- fix(backtester): correct Sharpe annualization for 24/7 crypto (365d × 96 = 35,040)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
21in7
2026-03-19 23:10:02 +09:00
parent 181f82d3c0
commit e3a78974b3
4 changed files with 22 additions and 8 deletions

View File

@@ -6,14 +6,20 @@
from __future__ import annotations
import json
import warnings
from dataclasses import dataclass, field, asdict
from datetime import datetime
from pathlib import Path
import joblib
import lightgbm as lgb
import numpy as np
import pandas as pd
from loguru import logger
# 크립토 24/7 시장: 15분봉 × 96봉/일 × 365일 = 35,040
_ANNUALIZE_FACTOR = 35_040
def _calc_trade_stats(trades: list[dict], initial_balance: float) -> dict:
"""거래 리스트에서 통계 요약을 계산한다. Backtester와 WalkForward 공통 사용."""
@@ -43,7 +49,7 @@ def _calc_trade_stats(trades: list[dict], initial_balance: float) -> dict:
if len(pnls) > 1:
pnl_arr = np.array(pnls)
sharpe = float(np.mean(pnl_arr) / np.std(pnl_arr) * np.sqrt(24192)) if np.std(pnl_arr) > 0 else 0.0
sharpe = float(np.mean(pnl_arr) / np.std(pnl_arr) * np.sqrt(_ANNUALIZE_FACTOR)) if np.std(pnl_arr) > 0 else 0.0
else:
sharpe = 0.0
@@ -81,11 +87,6 @@ def _calc_trade_stats(trades: list[dict], initial_balance: float) -> dict:
"close_reasons": reasons,
}
import warnings
import joblib
import lightgbm as lgb
from src.dataset_builder import (
_calc_indicators, _calc_signals, _calc_features_vectorized,
generate_dataset_vectorized, stratified_undersample,