feat: Add new files and enhance backtesting functionality
Changes: - Introduced new files for strategy definitions and study names. - Enhanced `backtest_web.py` with functions to handle integer display prices and trade data formatting. - Updated backtesting logic to incorporate end-of-day (EOD) parameters for breakout and momentum strategies. - Added EOD configuration options in the database and parameter search files. Impact: - These changes improve the modularity and usability of the backtesting framework, allowing for better integration of EOD strategies and clearer trade data presentation.
This commit is contained in:
@@ -7,7 +7,10 @@ param_search_apply_snapshot.py — 파라미터 탐색 결과 JSON → insert_en
|
||||
MOMENTUM search_momentum_*.json top[].merged_params
|
||||
SCALP search_*.json (rsi_oversold) top[].db_snapshot 우선, 없으면 merged에서 생성
|
||||
BREAKOUT search_breakout_*.json top[].merged_params
|
||||
BREAKOUT optuna_breakout_*.json results[N-1] (Optuna — top 없음)
|
||||
MOMENTUM optuna_momentum_*.json results[N-1] (Optuna)
|
||||
TAIL search_tail_*.json results[N-1].params (정렬된 순서)
|
||||
TAIL optuna_tail_*.json results[N-1] (Optuna)
|
||||
UPDOW updow_param_*.json top[N-1].apply_cfg + tf
|
||||
|
||||
사용 예:
|
||||
@@ -44,6 +47,17 @@ from kis_trader.backtest.backtest_portfolio_common import ( # noqa: E402
|
||||
)
|
||||
|
||||
|
||||
def _ranked_items(data: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
"""Grid ``top[]`` 또는 Optuna ``results[]`` — rank 1 = index 0."""
|
||||
top = data.get("top")
|
||||
if isinstance(top, list) and top:
|
||||
return top
|
||||
results = data.get("results")
|
||||
if isinstance(results, list) and results:
|
||||
return results
|
||||
return []
|
||||
|
||||
|
||||
def _env_bool_10(v: Any) -> str:
|
||||
if isinstance(v, bool):
|
||||
return "1" if v else "0"
|
||||
@@ -61,10 +75,12 @@ def _detect_strategy(data: Dict[str, Any], path: str) -> str:
|
||||
return "UPDOW"
|
||||
if base.startswith("search_tail_") or base.startswith("tail_search_"):
|
||||
return "TAIL"
|
||||
if base.startswith("search_breakout_"):
|
||||
if base.startswith("search_breakout_") or base.startswith("optuna_breakout_"):
|
||||
return "BREAKOUT"
|
||||
if base.startswith("search_momentum_"):
|
||||
if base.startswith("search_momentum_") or base.startswith("optuna_momentum_"):
|
||||
return "MOMENTUM"
|
||||
if base.startswith("optuna_tail_"):
|
||||
return "TAIL"
|
||||
|
||||
if isinstance(data.get("code"), str) and len(str(data.get("code")).strip()) == 6:
|
||||
if "tf" in data and isinstance(data.get("top"), list):
|
||||
@@ -546,16 +562,18 @@ def main(argv: Optional[List[str]] = None) -> int:
|
||||
print("⚠️ JSON 에 code 없음 → updow_stock_config 건너뜀")
|
||||
return 0
|
||||
|
||||
# ── top[] 기반 (MOMENTUM / SCALP / BREAKOUT) ─────────────────
|
||||
top = data.get("top") or []
|
||||
if not top:
|
||||
print("❌ JSON 에 top 배열이 없습니다.")
|
||||
# ── top[] (Grid) 또는 results[] (Optuna) — MOMENTUM / SCALP / BREAKOUT ──
|
||||
ranked = _ranked_items(data)
|
||||
if not ranked:
|
||||
print("❌ JSON 에 top[] 또는 results[] 배열이 없습니다.")
|
||||
return 8
|
||||
if rank > len(top):
|
||||
print(f"❌ rank 범위 초과 (1~{len(top)})")
|
||||
if rank > len(ranked):
|
||||
print(f"❌ rank 범위 초과 (1~{len(ranked)})")
|
||||
return 5
|
||||
|
||||
item = top[rank - 1]
|
||||
item = ranked[rank - 1]
|
||||
if str(data.get("engine") or "").lower() == "optuna":
|
||||
print(f" (Optuna results[{rank - 1}], trial=#{item.get('optuna_trial_number')})")
|
||||
pnl = int(item.get("total_pnl") or 0)
|
||||
if pnl <= 0 and not args.allow_non_positive_pnl:
|
||||
print(f"⚠️ total_pnl={pnl} ≤ 0 → 중단. 적용하려면 --allow-non-positive-pnl")
|
||||
|
||||
Reference in New Issue
Block a user