MTF 필터 백테스트, paper/live 체결, 빗썸 Private API 연동 및 운영 스크립트·설계 문서를 추가해 2단계 전략을 실거래 단계에 연결한다. Co-authored-by: Cursor <cursoragent@cursor.com>
60 lines
1.8 KiB
Python
60 lines
1.8 KiB
Python
"""운영 상태 JSON 저장·로드."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
|
|
def _today_str() -> str:
|
|
"""오늘 날짜 문자열 (KST naive)."""
|
|
return datetime.now().strftime("%Y-%m-%d")
|
|
|
|
|
|
def default_state(initial_cash_krw: float = 200_000.0) -> dict[str, Any]:
|
|
"""빈 운영 상태 dict."""
|
|
return {
|
|
"version": 1,
|
|
"created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
|
"last_run_at": None,
|
|
"last_processed_datetime": None,
|
|
"last_processed_bar_index": -1,
|
|
"trades_today_date": _today_str(),
|
|
"trades_today_count": 0,
|
|
"portfolio": {
|
|
"cash_krw": float(initial_cash_krw),
|
|
"coin_qty": 0.0,
|
|
"mode": "paper",
|
|
},
|
|
"trade_history": [],
|
|
}
|
|
|
|
|
|
def load_state(path: Path, *, initial_cash_krw: float = 200_000.0) -> dict[str, Any]:
|
|
"""운영 상태를 로드한다. 없으면 기본값 생성."""
|
|
if not path.exists():
|
|
return default_state(initial_cash_krw)
|
|
with path.open(encoding="utf-8") as fp:
|
|
state = json.load(fp)
|
|
if "portfolio" not in state:
|
|
state["portfolio"] = default_state(initial_cash_krw)["portfolio"]
|
|
return state
|
|
|
|
|
|
def save_state(path: Path, state: dict[str, Any]) -> Path:
|
|
"""운영 상태를 저장한다."""
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
with path.open("w", encoding="utf-8") as fp:
|
|
json.dump(state, fp, ensure_ascii=False, indent=2)
|
|
return path
|
|
|
|
|
|
def reset_daily_trade_count(state: dict[str, Any]) -> None:
|
|
"""날짜가 바뀌면 일일 체결 카운터를 초기화한다."""
|
|
today = _today_str()
|
|
if state.get("trades_today_date") != today:
|
|
state["trades_today_date"] = today
|
|
state["trades_today_count"] = 0
|