feat: Enhance trading system with new permanent subscription features and order book management

Changes:
- Added a new API endpoint for managing permanent subscriptions, allowing users to enable or disable subscriptions dynamically.
- Implemented a function to fill candle data from Kiwoom, ensuring that only relevant data is inserted into the database.
- Introduced a mechanism to handle master subscription states, improving the management of subscription statuses.
- Updated the database schema to include new fields for managing subscription states and order book filtering.

Impact:
- These enhancements improve the flexibility and reliability of the trading system, allowing for better management of subscriptions and order book data, while reducing the risk of data inconsistencies.

히스토리 align 제거 븅신같은 초기설계 아예 제거
진입모드에 구멍메움
호가진입을 켜도 호가가 안들어올때 호가 안보고 그냥 사버림
This commit is contained in:
Your Name
2026-08-15 23:01:14 +09:00
parent 4a18ce2697
commit 36a3e2b4a1
94 changed files with 6368 additions and 1639 deletions

View File

@@ -92,7 +92,11 @@ from kis_trader.backtest.optuna_dart import (
run_dart_optuna,
)
from kis_trader.backtest.optuna_search_space import suggest_tail_params, tail_grid_axis_keys
from kis_trader.backtest.optuna_tail_tpe_space import suggest_tail_params_tpe, tail_tpe_axis_keys
from kis_trader.backtest.optuna_tail_tpe_space import (
normalize_tpe_tail_entry_mode,
suggest_tail_params_tpe,
tail_tpe_axis_keys,
)
from kis_trader.backtest.param_search_cli_common import (
add_portfolio_cli_args,
add_search_filter_cli_args,
@@ -161,6 +165,7 @@ class TailSearchContext:
ob_filter_on: bool
cache_holder: Dict[str, Any] = field(default_factory=dict)
shared_tick_store: Any = None # ws_ticks 공유메모리 핸들 (종료 시 unlink)
tpe_entry_mode: str = "align" # TPE 고정 진입모드(탐색 축 아님)
def prepare_tail_search_context(
@@ -177,6 +182,7 @@ def prepare_tail_search_context(
total_budget_krw: Optional[float] = None,
orderbook_filter: str = "off",
history_source: Optional[str] = None,
entry_mode: Optional[str] = None,
) -> Optional[TailSearchContext]:
"""
run_search 와 동일한 데이터·base_params 1회 로드 (Grid 중복 최소화).
@@ -271,6 +277,17 @@ def prepare_tail_search_context(
base_params.setdefault("backtest_use_tick_exit", _tail_use_tick_exit(None))
# 절대규칙: Optuna/파람은 OHLC 폴백으로 숫자 변조 금지 (DB에 ON이어도 강제 OFF)
base_params["backtest_tick_fallback_ohlc"] = False
tpe_entry_mode = "align"
if mode == "tpe":
_raw_em = entry_mode
if _raw_em in (None, "", "None"):
_raw_em = get_env_from_db("TAIL_PARAM_SEARCH_ENTRY_MODE", "") or "align"
tpe_entry_mode = normalize_tpe_tail_entry_mode(_raw_em)
base_params["entry_mode"] = tpe_entry_mode
logger.info(
"📌 TPE 진입모드 고정: %s (한 스터디=한 모드, 탐색 축 아님)",
tpe_entry_mode,
)
if base_params.get("backtest_use_tick_db") or base_params.get("backtest_use_tick_exit"):
logger.info("📌 틱재생(ws_ticks): ON — OHLC 폴백 강제 OFF (정합 절대규칙)")
@@ -293,7 +310,6 @@ def prepare_tail_search_context(
from kis_trader.engine.tail_tick_replay import tail_backtest_wants_tick_replay
from kis_trader.backtest.tail_tick_loader import load_tail_ticks_by_code, tick_coverage_stats
_tick_probe = dict(base_params)
_tick_probe["entry_mode"] = "align"
if tail_backtest_wants_tick_replay(_tick_probe):
_tick_db = TradeDB()
try:
@@ -381,7 +397,7 @@ def prepare_tail_search_context(
if portfolio.get("budget_warning"):
logger.warning(f"💰 {portfolio['budget_warning']}")
if "entry_mode" not in pre_grid:
if mode != "tpe" and "entry_mode" not in pre_grid:
_search_entry = get_env_from_db("TAIL_PARAM_SEARCH_ENTRY_MODE", "")
if _search_entry not in (None, "", "None"):
base_params["entry_mode"] = str(_search_entry).strip().lower()
@@ -419,6 +435,7 @@ def prepare_tail_search_context(
ob_filter_on=ob_filter_on,
cache_holder=cache_holder,
shared_tick_store=shared_tick_store,
tpe_entry_mode=tpe_entry_mode,
)
finally:
db.close()
@@ -461,7 +478,7 @@ def run_tail_optuna(
def objective(trial: optuna.Trial) -> float:
if ctx.mode == "tpe":
combo = suggest_tail_params_tpe(trial)
combo = suggest_tail_params_tpe(trial, entry_mode=ctx.tpe_entry_mode)
else:
combo = suggest_tail_params(trial, ctx.mode)
result = evaluate_tail_param_combo(
@@ -615,6 +632,7 @@ def run_tail_optuna(
orderbook_by_code=ctx.orderbook_by_code,
program_by_code=ctx.program_by_code,
log_verdict_by_code=ctx.log_verdict_by_code,
include_trades=True,
)
def _save_partial(_data: Dict[str, Any]) -> None:
@@ -784,6 +802,14 @@ def main() -> None:
dest="ob_source",
help="호가 소스 필터 (기본 빈문자열 = 전체 검색)",
)
parser.add_argument(
"--entry-mode",
default=None,
dest="entry_mode",
choices=["align", "limit_atr"],
help="꼬리 TPE만. 한 스터디에 한 모드(미지정=TAIL_PARAM_SEARCH_ENTRY_MODE 또는 align). "
"둘 다 보려면 스터디를 나눠 두 번 실행.",
)
parser.add_argument(
"--orderbook-filter", default="off", choices=["off", "on", "auto"],
dest="orderbook_filter",
@@ -870,12 +896,18 @@ def main() -> None:
use_fallback = False
storage_url = resolve_optuna_storage_url(args.storage)
_study_extra = None
if strategy == "tail" and mode == "tpe":
_study_extra = normalize_tpe_tail_entry_mode(
getattr(args, "entry_mode", None) or "align",
)
study_name = resolve_study_name(
strategy=strategy,
mode=mode,
start=args.start,
end=args.end,
cli_override=args.study_name,
extra=_study_extra,
)
study = None
@@ -892,6 +924,7 @@ def main() -> None:
total_budget_krw=args.total_budget,
orderbook_filter=args.orderbook_filter,
history_source=args.universe_history_source,
entry_mode=getattr(args, "entry_mode", None),
)
if ctx is None:
sys.exit(1)