From c6bd62a25ffc27e4a434b68ad42467676f8ec093 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 12 Aug 2026 10:19:19 +0900 Subject: [PATCH] refactor: enhance Optuna backtesting framework, optimize orderbook filtering, and update database management utilities. --- .agents/AGENTS.md | 264 + .cursor/rules/hts-condition-grids.mdc | 10 +- .cursor/rules/like-mcp-docs.mdc | 36 + .cursorrules | 5 +- __pycache__/database.cpython-312.pyc | Bin 216425 -> 216904 bytes _test_ls_condition_realtime.py | 6 +- backtest_web.py | 539 +- config_schema.py | 2 + database.py | 37 +- docs/like_mcp.md/MODIFICATION_GUIDE.md | 313 + docs/like_mcp.md/code_architecture.md | 1022 +++ docs/like_mcp.md/db_erd.md | 4863 ++++++++++++++ docs/like_mcp.md/소켓.md | 106 + docs/옵투나.md | 69 + docs/증권사_토큰_시세.md | 211 + docs/호가.md | 448 ++ holding_bot.py | 4 +- kis_token_manager.py | 167 +- .../backtest/backtest_portfolio_common.py | 6 +- .../backtest/breakout_backtest_common.py | 30 +- .../backtest/breakout_portfolio_backtest.py | 21 +- kis_trader/backtest/breakout_tick_loader.py | 8 + .../backtest/momentum_portfolio_backtest.py | 31 +- .../backtest/optuna_breakout_tpe_space.py | 11 +- kis_trader/backtest/optuna_common.py | 2 +- kis_trader/backtest/optuna_mode_combo.py | 20 + .../backtest/optuna_momentum_tpe_space.py | 10 +- .../backtest/optuna_orderbook_recommend.py | 427 ++ .../backtest/optuna_scalping_tpe_space.py | 14 +- kis_trader/backtest/optuna_tail_tpe_space.py | 14 +- kis_trader/backtest/optuna_web_jobs.py | 50 +- .../backtest/optuna_whipsaw_recommend.py | 267 + kis_trader/backtest/param_search_momentum.py | 18 +- kis_trader/backtest/param_search_optuna.py | 15 + ..._breakout_fast_20260807_214651.briefing.md | 22 + ..._breakout_fast_20260807_215031.briefing.md | 22 + ..._breakout_fast_20260808_011436.briefing.md | 23 + ..._breakout_fast_20260808_022923.briefing.md | 23 + ..._momentum_fast_20260807_214700.briefing.md | 22 + ..._momentum_fast_20260808_010912.briefing.md | 26 + ..._momentum_fast_20260808_021231.briefing.md | 22 + ...una_scalp_fast_20260807_215011.briefing.md | 21 + ...una_scalp_fast_20260807_215704.briefing.md | 22 + ...una_scalp_fast_20260808_011850.briefing.md | 22 + ...una_scalp_fast_20260808_023327.briefing.md | 22 + ...una_scalp_fast_20260812_001823.briefing.md | 23 + ...tuna_tail_fast_20260807_215258.briefing.md | 22 + ...tuna_tail_fast_20260807_215544.briefing.md | 22 + ...tuna_tail_fast_20260808_013039.briefing.md | 22 + ...tuna_tail_fast_20260808_022522.briefing.md | 22 + .../backtest/scalping_backtest_common.py | 3 + kis_trader/backtest/trade_orderbook_enrich.py | 413 ++ .../backtest/trigger_snapshot_loader.py | 152 +- kis_trader/engine/indicator_cache.py | 4 +- kis_trader/engine/momentum_engine.py | 28 + kis_trader/engine/momentum_env_keys.py | 30 + kis_trader/engine/momentum_hts_logic.py | 224 +- kis_trader/engine/momentum_tick_replay.py | 37 +- kis_trader/engine/orderbook_filter.py | 17 +- kis_trader/execution/kis_client.py | 20 +- kis_trader/execution/order_manager.py | 48 +- kis_trader/execution/orderbook_sell.py | 1 + kis_trader/execution/orphan_reconcile.py | 143 +- kis_trader/main.py | 508 +- kis_trader/network/condition_common.py | 50 + kis_trader/network/condition_job_events.py | 350 + kis_trader/network/condition_manager.py | 90 +- .../network/kiwoom_condition_manager.py | 135 +- kis_trader/network/ls_condition_manager.py | 575 +- kis_trader/network/ls_token.py | 3 + kis_trader/network/market_guard.py | 24 + kis_trader/network/ws_manager.py | 251 +- kis_trader/strategies/base.py | 95 +- kis_trader/strategies/breakout.py | 91 +- kis_trader/strategies/momentum.py | 23 +- kis_trader/strategies/scalping.py | 10 + kis_trader/utils/live_portfolio_common.py | 73 +- kis_trader/utils/ls_ws_session_windows.py | 170 + kis_trader/utils/ops_alert.py | 170 + kis_trader/utils/request_handler.py | 30 + kis_trader/web/live_config_schema.py | 793 ++- kis_trader/ws/kis_ws.py | 125 +- kis_trader/ws/kiwoom_ws.py | 56 +- kis_trader/ws/ls_ws.py | 335 +- kis_trader/ws/trigger_snapshot_recorder.py | 58 +- logs/kis_api_rejects.jsonl | 2653 ++++++++ logs/optuna_4strat_tpe_latest_master.logpath | 2 +- logs/optuna_breakout_fast_latest.jsonpath | 1 + logs/optuna_breakout_latest.jsonpath | 2 +- logs/optuna_breakout_tpe_latest.logpath | 2 +- logs/optuna_breakout_tpe_latest.study | 2 +- logs/optuna_latest.jsonpath | 2 +- logs/optuna_momentum_fast_latest.jsonpath | 2 +- logs/optuna_momentum_latest.jsonpath | 2 +- logs/optuna_momentum_tpe_latest.logpath | 2 +- logs/optuna_momentum_tpe_latest.study | 2 +- logs/optuna_scalp_fast_latest.jsonpath | 1 + logs/optuna_scalp_latest.jsonpath | 2 +- logs/optuna_scalp_tpe_latest.logpath | 2 +- logs/optuna_scalp_tpe_latest.study | 2 +- logs/optuna_tail_fast_latest.jsonpath | 1 + logs/optuna_tail_latest.jsonpath | 2 +- logs/optuna_tail_tpe_latest.logpath | 2 +- logs/optuna_tail_tpe_latest.study | 2 +- logs/optuna_web_latest_job.txt | 2 +- mm_butler.py | 130 +- scratch/analyze_daedong.py | 75 + scratch/apply_base_tpe.py | 15 + scratch/check_accidents.py | 16 + scratch/check_bs4.py | 23 + scratch/check_db.py | 4 + scratch/check_db_counts.py | 76 + scratch/check_db_schema.py | 40 + scratch/check_db_schema2.py | 12 + scratch/check_db_stats.py | 26 + scratch/check_db_stats2.py | 26 + scratch/check_db_stats3.py | 40 + scratch/check_db_stats4.py | 26 + scratch/check_db_stats_table.py | 34 + scratch/check_db_stats_table2.py | 35 + scratch/check_db_stats_today.py | 54 + scratch/check_env_snapshot.py | 20 + scratch/check_history.py | 15 + scratch/check_html.py | 5 + scratch/check_ids.py | 7 + scratch/check_live_settings.py | 38 + scratch/check_ls.py | 3 + scratch/check_ob_filter_params.py | 75 + scratch/check_ob_tables.py | 12 + scratch/check_orderbook_counts_correct.py | 33 + scratch/check_orderbook_schema.py | 38 + scratch/check_prev_user.py | 15 + scratch/check_thought.py | 10 + scratch/check_tick_normalize.py | 35 + scratch/check_tick_normalize2.py | 39 + scratch/check_user_req.py | 14 + scratch/check_ws_ob.py | 10 + scratch/check_ws_ob2.py | 10 + scratch/check_ws_ob3.py | 9 + scratch/check_ws_ob4.py | 16 + scratch/check_ws_price_validation.py | 37 + scratch/check_ws_price_validation_2.py | 22 + scratch/check_ws_price_validation_3.py | 23 + scratch/check_ws_price_validation_4.py | 9 + scratch/check_ws_provider.py | 2 + scratch/check_ws_ticks_schema.py | 7 + scratch/check_ws_ticks_source.py | 19 + scratch/db_check.py | 26 + scratch/db_check2.py | 45 + scratch/db_check3.py | 34 + scratch/db_check4.py | 38 + scratch/db_config_mom.py | 31 + scratch/db_config_mom3.py | 14 + scratch/db_config_scalp.py | 21 + scratch/db_config_short.py | 25 + scratch/db_env_mom.py | 21 + scratch/db_env_mom2.py | 27 + scratch/db_ob_filters.py | 24 + scratch/db_stat_compare.py | 120 + scratch/db_th_env.py | 29 + scratch/delete_old_data.py | 44 + scratch/delete_old_data_batch.py | 61 + scratch/delete_old_data_final.py | 53 + scratch/delete_old_data_pymysql.py | 68 + scratch/delete_old_data_sa.py | 54 + scratch/disable_entry_filters.py | 25 + scratch/disable_whipsaw.py | 20 + scratch/enable_orderbook_filter.py | 20 + scratch/extract_optuna.py | 70 + scratch/fast_db_stats.py | 50 + scratch/find_div_diff.py | 15 + scratch/find_unclosed.py | 37 + scratch/find_unclosed_2.py | 35 + scratch/fix_bloat.py | 136 + scratch/fix_ls_and_kis_env.py | 52 + scratch/fix_reject_sleep.py | 22 + scratch/fix_reject_sleep_zero.py | 18 + scratch/fix_ws_provider.py | 25 + scratch/group_optuna.py | 8 + scratch/move_eod.py | 63 + scratch/playwright_test.py | 21 + scratch/print_optuna_results.py | 45 + scratch/print_orig_stats.py | 18 + scratch/refactor_html.py | 28 + scratch/remove_cand_limits.py | 22 + scratch/revert_price_provider.py | 35 + scratch/run_fast_recommend.sh | 8 + scratch/run_optuna_all.sh | 16 + scratch/run_optuna_fast_1trial.sh | 6 + scratch/test_get_ws_ticks.py | 19 + scratch/test_kis_hoga_ws.py | 91 + scratch/test_scan_sleep.py | 39 + scratch/update_bot_config.py | 51 + scratch/update_bot_config2.py | 42 + scratch/update_optuna_jobs.py | 22 + scratch_db_count.py | 67 + scratch_db_query.py | 19 + scripts/apply_optuna_ob_consensus.py | 87 + scripts/apply_optuna_whipsaw_consensus.py | 66 + scripts/collect_ls_condition_history.py | 6 +- scripts/collect_ls_universe_history.py | 120 +- scripts/kis_error_watch_mm.py | 20 +- scripts/measure_ob_exit_early_fire.py | 551 ++ scripts/optuna_ob_filter.py | 76 + scripts/run_optuna_4strat_tpe_seq.sh | 40 +- scripts/run_optuna_postprocess.sh | 27 + scripts/run_optuna_today_afternoon.sh | 90 + ...l_config_env_column_comments.generated.sql | 3853 +++++++++++ .../sql/all_db_column_comments.generated.sql | 5763 +++++++++++++++++ .../config_breakout_ob_column_comments.sql | 47 + .../config_momentum_ob_column_comments.sql | 46 + .../fill_all_config_env_column_comments.py | 378 ++ scripts/sql/fill_all_db_column_comments.py | 434 ++ ...token_session_strategy_column_comments.sql | 116 + scripts/test_live_execution_validation.py | 184 + static/css/backtest.css | 19 +- static/js/backtest.js | 71 +- templates/backtest.html | 116 +- 218 files changed, 31613 insertions(+), 759 deletions(-) create mode 100644 .agents/AGENTS.md create mode 100644 .cursor/rules/like-mcp-docs.mdc create mode 100644 docs/like_mcp.md/MODIFICATION_GUIDE.md create mode 100644 docs/like_mcp.md/code_architecture.md create mode 100644 docs/like_mcp.md/db_erd.md create mode 100644 docs/like_mcp.md/소켓.md create mode 100644 docs/옵투나.md create mode 100644 docs/증권사_토큰_시세.md create mode 100644 docs/호가.md create mode 100644 kis_trader/backtest/optuna_orderbook_recommend.py create mode 100644 kis_trader/backtest/optuna_whipsaw_recommend.py create mode 100644 kis_trader/backtest/results/optuna_breakout_fast_20260807_214651.briefing.md create mode 100644 kis_trader/backtest/results/optuna_breakout_fast_20260807_215031.briefing.md create mode 100644 kis_trader/backtest/results/optuna_breakout_fast_20260808_011436.briefing.md create mode 100644 kis_trader/backtest/results/optuna_breakout_fast_20260808_022923.briefing.md create mode 100644 kis_trader/backtest/results/optuna_momentum_fast_20260807_214700.briefing.md create mode 100644 kis_trader/backtest/results/optuna_momentum_fast_20260808_010912.briefing.md create mode 100644 kis_trader/backtest/results/optuna_momentum_fast_20260808_021231.briefing.md create mode 100644 kis_trader/backtest/results/optuna_scalp_fast_20260807_215011.briefing.md create mode 100644 kis_trader/backtest/results/optuna_scalp_fast_20260807_215704.briefing.md create mode 100644 kis_trader/backtest/results/optuna_scalp_fast_20260808_011850.briefing.md create mode 100644 kis_trader/backtest/results/optuna_scalp_fast_20260808_023327.briefing.md create mode 100644 kis_trader/backtest/results/optuna_scalp_fast_20260812_001823.briefing.md create mode 100644 kis_trader/backtest/results/optuna_tail_fast_20260807_215258.briefing.md create mode 100644 kis_trader/backtest/results/optuna_tail_fast_20260807_215544.briefing.md create mode 100644 kis_trader/backtest/results/optuna_tail_fast_20260808_013039.briefing.md create mode 100644 kis_trader/backtest/results/optuna_tail_fast_20260808_022522.briefing.md create mode 100644 kis_trader/backtest/trade_orderbook_enrich.py create mode 100644 kis_trader/network/condition_common.py create mode 100644 kis_trader/network/condition_job_events.py create mode 100644 kis_trader/utils/ls_ws_session_windows.py create mode 100644 kis_trader/utils/ops_alert.py create mode 100644 logs/optuna_breakout_fast_latest.jsonpath create mode 100644 logs/optuna_scalp_fast_latest.jsonpath create mode 100644 logs/optuna_tail_fast_latest.jsonpath create mode 100644 scratch/analyze_daedong.py create mode 100644 scratch/apply_base_tpe.py create mode 100644 scratch/check_accidents.py create mode 100644 scratch/check_bs4.py create mode 100644 scratch/check_db.py create mode 100644 scratch/check_db_counts.py create mode 100644 scratch/check_db_schema.py create mode 100644 scratch/check_db_schema2.py create mode 100644 scratch/check_db_stats.py create mode 100644 scratch/check_db_stats2.py create mode 100644 scratch/check_db_stats3.py create mode 100644 scratch/check_db_stats4.py create mode 100644 scratch/check_db_stats_table.py create mode 100644 scratch/check_db_stats_table2.py create mode 100644 scratch/check_db_stats_today.py create mode 100644 scratch/check_env_snapshot.py create mode 100644 scratch/check_history.py create mode 100644 scratch/check_html.py create mode 100644 scratch/check_ids.py create mode 100644 scratch/check_live_settings.py create mode 100644 scratch/check_ls.py create mode 100644 scratch/check_ob_filter_params.py create mode 100644 scratch/check_ob_tables.py create mode 100644 scratch/check_orderbook_counts_correct.py create mode 100644 scratch/check_orderbook_schema.py create mode 100644 scratch/check_prev_user.py create mode 100644 scratch/check_thought.py create mode 100644 scratch/check_tick_normalize.py create mode 100644 scratch/check_tick_normalize2.py create mode 100644 scratch/check_user_req.py create mode 100644 scratch/check_ws_ob.py create mode 100644 scratch/check_ws_ob2.py create mode 100644 scratch/check_ws_ob3.py create mode 100644 scratch/check_ws_ob4.py create mode 100644 scratch/check_ws_price_validation.py create mode 100644 scratch/check_ws_price_validation_2.py create mode 100644 scratch/check_ws_price_validation_3.py create mode 100644 scratch/check_ws_price_validation_4.py create mode 100644 scratch/check_ws_provider.py create mode 100644 scratch/check_ws_ticks_schema.py create mode 100644 scratch/check_ws_ticks_source.py create mode 100644 scratch/db_check.py create mode 100644 scratch/db_check2.py create mode 100644 scratch/db_check3.py create mode 100644 scratch/db_check4.py create mode 100644 scratch/db_config_mom.py create mode 100644 scratch/db_config_mom3.py create mode 100644 scratch/db_config_scalp.py create mode 100644 scratch/db_config_short.py create mode 100644 scratch/db_env_mom.py create mode 100644 scratch/db_env_mom2.py create mode 100644 scratch/db_ob_filters.py create mode 100644 scratch/db_stat_compare.py create mode 100644 scratch/db_th_env.py create mode 100644 scratch/delete_old_data.py create mode 100644 scratch/delete_old_data_batch.py create mode 100644 scratch/delete_old_data_final.py create mode 100644 scratch/delete_old_data_pymysql.py create mode 100644 scratch/delete_old_data_sa.py create mode 100644 scratch/disable_entry_filters.py create mode 100644 scratch/disable_whipsaw.py create mode 100644 scratch/enable_orderbook_filter.py create mode 100644 scratch/extract_optuna.py create mode 100644 scratch/fast_db_stats.py create mode 100644 scratch/find_div_diff.py create mode 100644 scratch/find_unclosed.py create mode 100644 scratch/find_unclosed_2.py create mode 100644 scratch/fix_bloat.py create mode 100644 scratch/fix_ls_and_kis_env.py create mode 100644 scratch/fix_reject_sleep.py create mode 100644 scratch/fix_reject_sleep_zero.py create mode 100644 scratch/fix_ws_provider.py create mode 100644 scratch/group_optuna.py create mode 100644 scratch/move_eod.py create mode 100644 scratch/playwright_test.py create mode 100644 scratch/print_optuna_results.py create mode 100644 scratch/print_orig_stats.py create mode 100644 scratch/refactor_html.py create mode 100644 scratch/remove_cand_limits.py create mode 100644 scratch/revert_price_provider.py create mode 100755 scratch/run_fast_recommend.sh create mode 100755 scratch/run_optuna_all.sh create mode 100755 scratch/run_optuna_fast_1trial.sh create mode 100644 scratch/test_get_ws_ticks.py create mode 100644 scratch/test_kis_hoga_ws.py create mode 100644 scratch/test_scan_sleep.py create mode 100644 scratch/update_bot_config.py create mode 100644 scratch/update_bot_config2.py create mode 100644 scratch/update_optuna_jobs.py create mode 100644 scratch_db_count.py create mode 100644 scratch_db_query.py create mode 100755 scripts/apply_optuna_ob_consensus.py create mode 100755 scripts/apply_optuna_whipsaw_consensus.py create mode 100644 scripts/measure_ob_exit_early_fire.py create mode 100755 scripts/optuna_ob_filter.py create mode 100755 scripts/run_optuna_postprocess.sh create mode 100755 scripts/run_optuna_today_afternoon.sh create mode 100644 scripts/sql/all_config_env_column_comments.generated.sql create mode 100644 scripts/sql/all_db_column_comments.generated.sql create mode 100644 scripts/sql/config_breakout_ob_column_comments.sql create mode 100644 scripts/sql/config_momentum_ob_column_comments.sql create mode 100644 scripts/sql/fill_all_config_env_column_comments.py create mode 100644 scripts/sql/fill_all_db_column_comments.py create mode 100644 scripts/sql/token_session_strategy_column_comments.sql create mode 100755 scripts/test_live_execution_validation.py diff --git a/.agents/AGENTS.md b/.agents/AGENTS.md new file mode 100644 index 0000000..81ab14b --- /dev/null +++ b/.agents/AGENTS.md @@ -0,0 +1,264 @@ +# kis_bot 프로젝트 에이전트 규칙 (필수 준수 사항) + +## 📖 필수 참조 폴더 (`docs/like_mcp.md/`) 및 문서 + +이 프로젝트에서 작업할 때는 `docs/like_mcp.md/` 디렉토리 내의 핵심 문서들을 **항상** 인지하고 참조해야 합니다. + +### 필수 문서 목록 (경로: `docs/like_mcp.md/`) + +| 문서 | 정확한 파일 경로 | 용도 및 시점 | +|------|------|------| +| **코드 수정 가이드** | `docs/like_mcp.md/MODIFICATION_GUIDE.md` | **[수정 전 필수]** 수정 유형별 필수 `grep` 체크리스트 및 사이드 이펙트 검증 절차 | +| **코드 아키텍처 & 의존성 맵** | `docs/like_mcp.md/code_architecture.md` | **[구조 파악]** 파일 간 import 의존성, 핵심 허브 파일 Top 20, 전략별 모듈 분류 | +| **DB ERD & DDL** | `docs/like_mcp.md/db_erd.md` | **[DB 참고]** MariaDB 테이블 스키마, 컬럼 정의, PK/인덱스 및 관계 | + +--- + +## ⚖️ MCP 도구 vs MD 문서 활용 원칙 (토큰 최적화 & 정확도) + +프로젝트 분석 및 코드 작성 시, **MCP 도구(kis-code-assistant 등)**와 **MD 참조 문서**를 아래 역할 분담에 맞춰 사용합니다: + +1. **거시적 구조 & 영향 범위 파악 → MD 문서 (`docs/like_mcp.md/`)** + - 새 대화 시 또는 작업 전 전체적인 지도(Layer, Hub 파일)가 필요할 때 목차 및 관련 섹션을 빠르게 참고합니다. +2. **미시적 코드 & API 조회 → MCP 도구 우선 사용** + - 특정 파일의 소스코드를 보거나 API 스펙을 탐색할 때는 파일 전체를 읽어 토큰을 낭비하지 말고, MCP 도구(`read_source_code`, `search_*_api` 등)나 `grep_search`를 우선 사용하여 실시간 최신 상태를 조회합니다. +3. **수정 전 `grep` 필수 실행** + - MD 문서는 "어디를 검색할지" 알려주는 지도입니다. 실제 수정을 단행하기 전에는 반드시 `MODIFICATION_GUIDE.md`에 명시된 필수 `grep` 명령을 실행하여 관련 파일 목록을 정확히 확정하세요. + +--- + +## 🔄 코드 변경 시 MD 문서 필수 업데이트 룰 (Stale 방지) + +코드를 수정하거나 새로운 기능을 추가한 후(특히 커밋 전/후)에는 **반드시 MD 문서와의 완결성을 유지**해야 합니다. 아래 상황 발생 시 문서를 무조건 업데이트하세요: + +1. **파일 추가/삭제/이동 또는 import 변경 시** + - `code_architecture.md` 갱신 (스크립트 활용 또는 내용 반영) +2. **DB 테이블/컬럼 또는 환경변수(ENV) 추가/변경 시** + - `db_erd.md` 및 `MODIFICATION_GUIDE.md` 체크리스트 반영 +3. **검증 의무 (실매매 엔진 사전 100% 무결성 검열 필수에 관한 절대 규칙)** + - 코드를 수정하면 실매매/백테스트/웹/파라미터서치 정합성 및 영향도를 100% 검증하고 완료 알림에 변경 내용을 명시합니다. + - **핵심 실매매 코드 수정 또는 DB 파라미터/환경변수(ENV)/스키마 변경 시**에는 반드시 최종 완료 전 `.venv/bin/python3 scripts/test_live_execution_validation.py`를 가동하여 1~5단계 전 구간 100% 통과(`👑 [최종 판정] 완결!`)를 확정 지어야 합니다. + +--- + +## 🚨 하드코딩 절대 금지 (NO HARDCODING) + +- 어떠한 경우에도 코드 내부에 임계값, 비율, 점수, 시간 등의 수치를 직접 하드코딩하지 마세요. +- 숫자값을 추가하거나 수정할 때는 **반드시** `get_env_float()`, `get_env_int()`, `get_env_bool()`을 사용하여 DB/Env에서 불러오도록 작성하세요. + - ❌ `if rsi > 78:` (절대 금지) + - ✅ `rsi_limit = get_env_float("RSI_LIMIT", 78.0); if rsi > rsi_limit:` (필수 적용) +- 변수명은 직관적인 대문자 스네이크 케이스(예: `MAX_DROP_RATE`)로 작성하고 기본값을 설정하세요. +- **신규 env 키 추가 시 4개 세트 등록 의무**: 아래 4곳을 한 세트로 반드시 등록하세요. + 1. 코드 내 `get_env_*` 기본값 지정 + 2. `database.py` ENV 키 목록에 추가 + 3. 필요 시 `*_env_keys.py` 키 목록에 추가 + 4. `apply` 패치 맵에 추가 + - 저장 후 `get_env_from_db`로 **재조회 검증** 필수 + +--- + +## 🏗️ 시스템 아키텍처 원칙 + +### SCAN vs TRIGGER 분리 +- **SCAN 단계**: 조건 필터링을 최소화하여 후보를 DB에 최대한 많이 올립니다. +- **TRIGGER 단계**: 실제 매수 직전 모든 엄격한 필터(보조지표, 호가, 수급 등)를 한 번에 검사합니다. +- 무거운 연산(API 추가 호출, 분봉 분석 등)은 절대 5분 주기 스캔 함수에 넣지 말고, **매수 타점 체크 함수에 넣으세요.** + +### WebSocket 우선, REST 최소화 +- REST는 가능하면 **WebSocket/캐시/DB 재사용**으로 대체하세요. +- REST를 대체할 수 있는 경우에는 최대한 WebSocket을 사용합니다. +- 루프·재큐·벌크 등에서 REST 연타 금지. `SafeRequest`·기존 세마포어·sleep·쿨다운을 우회하는 새 경로를 만들지 마세요. + +### 공통 코드 함수화 +- 여러 전략에서 공통으로 사용할 수 있는 코드는 **반드시 공통 함수로 분리**하여 재사용하세요. +- 전략별로 동일한 로직을 중복 작성하지 마세요. + +### 손절 및 안전 장치 필수 +- 모든 매매 로직에는 손절(Stop-loss) 및 예외 처리 로직이 포함되어야 합니다. +- 코드 작성 후 스스로 아래 항목을 검토하세요: + 1. 손절(Stop-loss) 및 예외 처리 로직이 포함되었는가? + 2. API 호출 제한(429 Error) 및 슬리피지 고려가 되었는가? + 3. `None`/`null`로 인한 런타임 오류가 발생하지 않는가? + 4. 기존 로직과 100% 동일한 기능을 수행하는가? + +--- + +## 🔄 실매↔백테↔파람 정합 규칙 (필수) + +- **실매매 기준으로 백테스트, 파라미터서치 코드를 맞춥니다.** 백테/파람서치 코드는 실매매 코드와 100% 동일해야 합니다. +- **새로 추가한 코드의 기본값은 항상 DB에 추가**해야 하며, 웹 페이지 입력값과 일치해야 합니다. +- 코드 수정 시 **실매매·웹 백테스트·파라미터서치의 결과값이 동일**해야 하며, 검증을 반드시 거쳐야 합니다. +- 수정 사항이 **실매에 영향이 가는지 vs 백테/파라미터에만 영향이 가는지** 명확히 분류한 후 보고하고 수정합니다. +- Optuna 파라미터서치 실행 시: + - 주말/공휴일이면 **최근 거래일**로만 실행 + - 그리드 변경 후 **반드시 새 `--study-name`** 사용 (동일 study 재사용 금지) + - `*_SKIP_HTS_SCAN_DUPES` 는 **false 유지**, 임의로 true로 바꾸지 말 것 + +--- + +## 🚫 절대 금지 사항 (위반 시 작업 중지) + +### 봉 정합·진입 정렬 +- **신호 = T−1 확정봉, 진입 = T(시가/첫 틱).** `live_backtest_align=True` 잠금. +- 유니버스·신호·진입을 **±1분(또는 ±1봉) 보정·오프셋·슬롯 해킹**으로 맞추는 코드 **절대 금지**. +- **`WS_CANDLE_FREEZE_ON_CONFIRM` 끄기(false) 절대 금지.** 사용자 명시 승인 없이 변경 금지. + +### OHLC 폴백 금지 +- 틱 청산/진입 ON이면 봉 OHLC(high/low)로 체결·`max_price`·PnL을 채우지 마세요. +- OHLC 폴백으로 숫자를 변조하는 것은 실매와 다른 엔진을 만드는 행위입니다. + +### HTS 조건식과 코드 분리 +- HTS는 후보 유니버스 참고용입니다. 그리드를 HTS 밴드에 맞추라고 강제하지 말 것. +- `*_SKIP_HTS_SCAN_DUPES` 는 사용자가 언급하기 전까지 **false 유지**. 임의로 true로 바꾸지 말 것. + +### DB 쿼리 안전 규칙 +- SQL 전 **반드시** `SHOW COLUMNS FROM `로 실제 컬럼 확인. 추측 SELECT 금지. +- PyMySQL: SQL 문자열의 `%`는 포맷으로 해석됨. `LIKE '20260712%'` 금지 → `LIKE %s` + `('20260712%',)` 또는 `%%`. +- 실패 시 원인 고친 뒤 **1회만** 재실행. 같은 가정으로 날짜/strategy만 바꿔 반복 금지. + +--- + +## 🔍 근본원인 먼저, 땜빵 금지 + +- 증상을 가리는 땜빵용 코딩은 지양합니다. **항상 근본원인을 먼저 찾고** 초등학생도 이해하기 쉽게 설명 후 설계하고 보고합니다. +- "일단 재시도·재발급·봉수 늘리기·±1 보정"으로 증상만 가리기 금지. +- API 관련 코드를 작성/수정할 때는 본인의 지식에 의존하지 말고 **반드시 연동된 MCP 도구를 우선 호출**하여 스펙을 확인하세요. + +--- + +## 🤖 AI 에이전트 행동 및 도구 사용 원칙 (Behavior & Tool Execution Rules) + +에이전트는 사용자(USER)와의 페어 프로그래밍 및 코드 관리에서 최고의 품질과 토큰 효율을 발휘하기 위해 아래 지침을 100% 준수합니다. + +### 1. 🛠️ 도구 사용 기본 규칙 (Tool Calling) +- **파이썬 임시 구동 시 `python -c` 인라인 실행 절대 금지 (No Inline Python in Shell)**: 복잡한 다중 줄이나 따옴표/퍼센트(`%`) 기호가 포함된 파이썬 구문을 터미널에서 `python -c "..."`로 구동하면 셸 인식 및 줄바꿈 오류로 인해 명령이 멈추거나 터미널이 꼬이므로 **절대 금지**합니다. **반드시 `scratch/` 폴더나 임시 스크립트(`.py`) 파일로 작성(`PYTHONPATH=.` 또는 `sys.path` 설정 필수 포함)한 뒤 단건 파일 실행(`python script.py`)으로 구동**하세요. +- **내부 도구명 언급 금지**: 사용자에게 설명할 때 `replace_file_content 도구를 사용하여...` 또는 `grep_search를 호출하여...`와 같이 내부 도구 이름을 직접 텍스트로 언급하지 마세요. 대신 **"파일을 수정하겠습니다"**, **"코드를 검색하여 파악하겠습니다"** 처럼 자연스럽고 친절한 언어로 소통하세요. +- **불필요한 도구 호출 자제**: 일반적인 질의응답이나 이미 대화 및 문서로 알고 있는 내용, 단순 설명은 불필요하게 도구를 낭비하지 말고 즉시 답변하세요. +- **작업 목적 사전 설명**: 도구(검색, 읽기, 수정, 실행 등)를 사용하기 전에 왜 그 작업이 필요하며 목표 달성에 어떻게 기여하는지 한 문장으로 명쾌하게 먼저 설명하세요. + +### 2. ✏️ 코드 수정 및 오류 해결 규칙 (Making Code Changes) +- **채팅창 코드 전문 출력 금지**: 사용자가 "채팅창에 출력해줘"라고 명시하지 않는 한, 긴 코드를 대화창에 텍스트로 장황하게 늘어놓지 마세요. **반드시 편집 도구(Edit Tools)를 사용하여 실제 파일에 직접 안전하게 반영**하세요. +- **동일 파일 수정의 묶음(Batch) 처리**: 같은 파일 내에서 여러 군데를 뜯어고쳐야 할 때, 수정을 여러 턴에 걸쳐 잘게 쪼개어 부르지 말고 **단일 도구 호출(한 번의 패치)에 묶어서 통합 적용**하세요. +- **수정 전 조회(Read-before-Edit) 의무화**: 신규 파일을 만들거나 파일 맨 끝에 간단한 줄을 추가하는 경우를 제외하고는, 코드를 수정하기 전에 **반드시 해당 파일 또는 대상 섹션의 원본 소스를 먼저 읽고 문맥을 완전히 파악한 뒤 수정**하세요. (근거 없는 눈대중 수정 절대 금지) +- **오류 수정 3회 루프 제한 (Anti-Loop)**: 코드 변경 후 린트(Lint)나 문법/런타임 에러가 터졌을 때 확실한 해결 방안이 섰을 때만 수정하세요. **근거 없이 '이렇게 해볼까' 식으로 무리한 시도를 되풀이하지 말 것.** 특히 동일한 파일에서 오류 고치기를 **3회 이상 반복(Loop)하지 마세요**. 3번째 패치 후에도 실패하면 즉시 중단하고 사용자에게 상황을 명명백백히 설명한 뒤 다음 행동 지시를 받으세요. +- **즉시 구동 보장 (Executable Quality)**: 생성하거나 수정한 코드는 사용자가 언제든 터미널을 열어 실행하거나 실매매/백테스트 엔진에 올려도 오류가 없도록 온전하고 실행 가능한 상태로 완성되어야 합니다. +- **기존 코드 구조 존중 (No Over-engineering)**: 잘 작동하는 코드를 '더 나은 구조'를 명목으로 임의로 클래스화하거나 불필요하게 복잡하게 바꾸지 마세요. **기존의 함수형·절차적 구조를 최대한 유지**하는 것이 기본 원칙입니다. +- **핵심 로직 변경 시 선보고·승인 대기**: 매매 로직, 청산 우선순위, 공통 인프라 등 **핵심 로직을 변경해야 할 때는 코드를 바로 수정하지 말고**, 먼저 왜 변경이 필요한지 이유를 설명하고 사용자의 승인을 받은 뒤 수정하세요. (단순 버그 수정이나 오타 교정은 즉시 수정 가능) +- **주석 및 로거 절대 유지**: 기존에 작성된 주석(Comments)과 로거(Logger) 코드는 수정과 무관하게 **절대 삭제하지 마세요.** 새 코드를 추가하거나 수정하더라도 기존 주석과 로그는 그대로 보존합니다. +- **테스트 실행 시 백그라운드 + 로그 경로 안내**: 테스트 코드를 돌릴 때는 백그라운드로 실행하고, `tail -f`로 볼 수 있는 로그 파일 경로를 반드시 함께 안내하세요. +- **🚨 [절대 규칙] 작업 흐름 단절 금지 & 끝까지 자동 보정 완수 (Never Break Flow & Autonomous Retry)**: + - 스크립트 구동, 테스트 검증, DB 반영 등의 작업을 수행할 때 실행 중 오류가 터지거나 탈락하더라도 **사용자에게 로그만 넘기며 대화 턴을 멈추고 흐름을 끊지 마세요.** + - 에이전트는 **단일 대화 턴 내부에서 스스로 실패 로그와 원인 규명 ➔ 코드 보정 ➔ 즉각 재시도**의 루프를 주도적으로 돌려 **100% 정상 완수될 때까지 끝까지 책임을 관철한 뒤, 최종 통과된 완공 상태만 보고**해야 합니다. + - 단기 파괴검사나 수 초 내로 끝나는 검증·진단 모듈 구동 시에는 동기식 실행 대기 시간을 충분히 부여하여 에이전트가 실행 결과를 즉각 회수하고, 스스로 100% 정합성 통과를 확정한 후 대화 턴을 마무리하세요. +- **🚨 [절대 규칙] 핵심 코드·DB 변경 시 실매매 엔진 최종 검증 의무화 (Mandatory Live Execution Validation)**: + - 전략, 주문, 호가 필터, 공통 인프라 등 **핵심 실매매 코드를 수정**하거나, **데이터베이스(ENV 파라미터, 스키마, 테이블 등)에 변화가 생겼을 때**는 반드시 최종 완료 직전에 아래 검증 스크립트를 실행하여 100% 무결성을 증명해야 합니다. + - 실행 커맨드: `.venv/bin/python3 scripts/test_live_execution_validation.py` + - 1~5단계 중 단 하나라도 탈락이나 에러가 발생할 경우 작업을 마쳐서는 안 되며, **끝까지 자동 보정 완수 규칙**에 따라 원인 수정 후 재검증을 돌려 100% 최종 완공 판정(`👑 [최종 판정] 완결!`)을 확인한 뒤 보고하세요. + +### 3. 🔎 검색 및 코드 탐색 규칙 (Searching & Reading) +- **효율적인 타겟팅 최우선**: 무작정 루트 디렉토리를 통으로 긁는 것보다, `code_architecture.md`나 MCP 도구, 정밀 정규식 검색 등을 우선 활용하여 목표 범위를 날카롭게 압축하세요. +- **문맥 확보를 위한 충분한 범위 일괄 읽기**: 파일을 조사할 때는 15~20줄씩 쪼개어 여러 턴을 낭비하지 말고, 문맥과 의존성을 안심하고 파악할 수 있도록 **처음부터 필요한 충분한 크기(섹션/블록)를 큼직하게 잡고 한 번에 조회**하세요. +- **목적 달성 시 즉시 탐색 종료**: 수정이나 답변을 하기에 합리적이고 충분한 위치와 정보를 찾았다면, 불필요한 추가 조회 도구 호출을 중단하고 **즉시 코드 수정이나 답변 작성 단계로 진입**하세요. + +--- + +## 🌐 백테스트 웹페이지 운영 및 UI 동기화 규칙 (Web UI & Service Rules) + +### 1. 🖥️ 백테스트 웹 서비스 재시작 및 브라우저 검증 +- **웹 UI/API 수정 후 필수 재시작**: 웹 서버 코드나 UI 스크립트(`backtest_web.py`, 템플릿 등)를 수정한 뒤에는 반드시 아래 명령어를 가동하여 서비스를 새로고침하고 `active` 상태를 확인하세요. + - 명령어: `sudo systemctl restart kis_backtest_web.service` + - 주의: 웹과 무관한 실매매 봇 전용 로직만 고쳤다면 불필요하게 실매매 서비스를 재시작하지 마세요. +- **전용 URL 브라우저 확인**: 브라우저나 HTTP 검증 시 반드시 **`http://192.168.0.149:5050/`** 주소만 사용하세요. (`127.0.0.1`이나 `localhost`는 브라우저 연결 에러 유발로 금지) +- **진정한 검증 완결**: 단순 `curl 200 OK` 응답만으로 완료라 단정하지 말고, 실제 브라우저/화면상에서 버튼·탭 전환 시 콘솔 `Uncaught`/`ReferenceError` 오류가 없는지 100% 확인한 뒤 보고하세요. + +### 2. 🔄 탭 UI 통합 변경 및 국장/해외 동기화 +- **전 전략 탭 UI 일치 원칙**: 백테스트 웹페이지 내 한 전략의 UI(인풋 폼, 가상거래내역, 실거래내역 테이블 등)를 수정했다면, 모멘텀·돌파·스캘핑·꼬리잡기 등 **다른 전략 탭도 100% 동일한 구조와 스펙으로 함께 변경**해야 합니다. +- **해외 전략 탭(US_*) UX 동기화**: 해외 전략 웹 탭, Optuna, Top5 보기, 수익률 카드 등은 **국장 탭과 동일한 UX**로 맞추는 것이 기본 원칙입니다. 임의로 생략하거나 단축해서는 안 되며, 통화(USD) 표기나 시간 분리 등 필연적 차이가 있을 때만 선보고 및 문구 기재 후 반영합니다. +- **UI 덮어쓰기 금지**: Optuna 파라미터 폼 채우기나 종목 적용 후 페이지 리로드로 폼 수치가 기존 DB 핀셋 설정에 도로 덮어씌워져 유실되지 않도록 검증(하드새로고침 ➔ 종목선택 ➔ Optuna 수치 유지)을 의무화하세요. + +--- + +## 🚨 증권사 API 인프라 보호 및 DB 안전 조회 규칙 (Infrastructure & DB Safety) + +### 1. 🛡️ 키움/KIS API 유량 및 approval 키 보호 (폭주 절대 금지) +- **API 에러 ≠ 데이터 없음**: 키움 `return_code=5`(유량 한도 초과)나 `429 Error`를 '빈 봉/데이터 없음'으로 착각하여 루프·재큐·벌크 요청으로 API를 무한히 연타하지 마세요. 유량 초과 시 백오프(Backoff), 세마포어, 실패 상한선 도달 시 재큐 정지가 필수입니다. +- **WS approval_key 단일 공유 캐시 준수**: KIS WebSocket 실시간 접속키(`approval_key`)는 공용 `KISApprovalManager(공유 캐시)`를 사용하여 전 연결이 1개의 키를 나누어 씁니다. 모듈이 돌아갈 때마다 매번 새 키를 REST로 발급받아 타 소켓을 끊기거나 무효화하는 행위를 절대 금지합니다. (응급 재발급은 기본 6시간 1회 하드캡 지킬 것) +- **백테스트 REST 웜업 최소화**: 전일 장 시작 시가 보강 REST는 짧은 1차(기본 700봉)만 기본 적용하고, 실패할 때만 해당 종목에 한해 1회만 증량(1,500봉) 재시도하세요. + +### 2. 🗄️ DB 임시 조회(Ad-hoc) 및 SQL 쿼리 안전 수칙 +- **추측 SELECT 금지 (SHOW COLUMNS 필수)**: TradeDB로 SQL을 날리기 전에는 반드시 `SHOW COLUMNS FROM
`로 실제 컬럼 스키마를 확인하세요. + - 대표적 오류 사례: `target_candidates` 테이블에는 **`strategy_id` 컬럼이 없음** (code/name/score/price/scan_time/updated_at 등만 존재). + - **🚨 [필수 준수 템플릿]**: 아래와 같이 반드시 `SHOW COLUMNS`로 정확한 테이블명과 컬럼명을 딕셔너리로 받아 확인한 후(문법 고정)에만 본 쿼리를 실행하여 에러를 원천 차단하세요. 따옴표 사용에도 유의하세요. + ```python + # 임시 조회 스크립트 작성 시 고정 문법 (python -c 대신 반드시 파일로 작성) + from database import TradeDB + db = TradeDB() + try: + cols1 = [dict(r)["Field"] for r in db.conn.execute("SHOW COLUMNS FROM ws_price_validation").fetchall()] + print(f"ws_price_validation cols: {cols1}") + + cols2 = [dict(r)["Field"] for r in db.conn.execute("SHOW COLUMNS FROM ls_ws_ticks").fetchall()] + print(f"ls_ws_ticks cols: {cols2}") + + # 확인된 컬럼명(cols1, cols2)을 기반으로 이후 SELECT/조회 실행 + finally: + db.close() + ``` +- **PyMySQL `%` 포맷 충돌 회피**: PyMySQL 실행 시 SQL 문자열 안의 `%` 기호는 문자열 포맷팅으로 해석됩니다. `LIKE '20260712%'` 식의 쿼리를 금지하고 반드시 파라미터 바인딩(`LIKE %s`, `('20260712%',)`) 또는 `%%`를 사용하세요. +- **에러 반복 금지**: `Unknown column`이나 `not enough arguments for format string` 에러가 났을 때 똑같은 가정을 두고 날짜나 전략명만 바꿔가며 실패 쿼리를 수차례 연타하지 마세요. 원인을 규명한 뒤 1회만 고쳐서 재실행하세요. + +### 3. ⏳ 파라미터 서치(Optuna) 및 백테스트 토큰·시간 절약 +- **휴장일 보정 실행**: 주말이나 공휴일에는 `end=오늘`로 돌려 빈 봉 에러나 0건으로 통째 재실행하는 낭비를 저지르지 마세요. 반드시 **최근 거래일을 기준으로 종료 시점(end)을 보정**하여 1회에 완공하세요. +- **그리드 변경 시 새 study-name 사용**: Optuna 탐색 범위(categorical, TPE 등)나 그리드가 바뀌면 동일한 `--study-name` 재사용 시 `dynamic value space error`가 나므로, 반드시 새로운 스터디명을 부여하여 돌려야 합니다. +- **긴 잡은 백그라운드 구동**: 오래 걸리는 Optuna 나 백테스트는 `nohup`으로 돌리고 로그 파일 경로를 안내하세요. `for+sleep` 식의 무의미한 루프 대기를 걸지 마세요. + +--- + +## 🚨 [절대규칙] Optuna 백테스트 웹 UI 잡 트래킹 실행 방법 + +### 문제: nohup bash 직접 실행 시 웹 UI에 잡이 보이지 않음 + +`nohup bash scripts/run_optuna_*.sh` 또는 `python3 kis_trader/backtest/param_search_optuna.py`를 **쉘에서 직접 실행하면** 웹 UI의 프로그레스바 및 최근 잡 목록에 **절대 표시되지 않습니다.** + +웹 UI 잡 시스템은 `optuna_web_jobs.py`의 파일 기반 잡 트래킹(`logs/optuna_web_jobs/*.json`)을 통해서만 작동합니다. + +### ✅ 올바른 실행 방법: 웹 API(curl)로 실행 + +쉘에서 실행하면서 웹 UI에도 표시하려면 반드시 **웹 서버의 `/api/optuna/start` 엔드포인트를 통해 실행**해야 합니다. + +```bash +# 단일 전략 실행 (웹 UI에 표시됨) +curl -s -X POST http://192.168.0.149:5050/api/optuna/start \ + -H "Content-Type: application/json" \ + -d '{"strategy":"momentum","start":"2026-08-07","end":"2026-08-07","trials":200,"mode":"fast"}' | python3 -m json.tool + +# 4전략 순차 실행 (strategies 배열로 전달) +curl -s -X POST http://192.168.0.149:5050/api/optuna/start \ + -H "Content-Type: application/json" \ + -d '{"strategies":["momentum","breakout","scalp","tail"],"start":"2026-08-07","end":"2026-08-07","trials":200,"mode":"fast"}' | python3 -m json.tool + +# 진행 상황 확인 (job_id는 응답에서 확인) +curl -s http://192.168.0.149:5050/api/optuna/status/ | python3 -m json.tool + +# 잡 목록 확인 +curl -s http://192.168.0.149:5050/api/optuna/jobs | python3 -m json.tool +``` + +### 후처리(호가/휩쏘) 단독 실행은 직접 실행 허용 + +메인 TPE와 달리 후처리 모듈(`apply_optuna_ob_consensus.py`, `apply_optuna_whipsaw_consensus.py`)은 수 초~수 분 내에 끝나므로 **직접 실행 허용**합니다. 단, 결과 JSON은 별도로 웹 UI 결과 탭에 표시됩니다. + +```bash +# 후처리는 직접 실행 허용 +python3 scripts/apply_optuna_ob_consensus.py --strategy MOMENTUM --n-trials 1000 +python3 scripts/apply_optuna_whipsaw_consensus.py --strategy MOMENTUM --n-trials 500 +``` + +### 스크립트 작성 시 curl API 사용 원칙 + +에이전트가 Optuna 메인 TPE 실행 스크립트(`.sh`)를 작성할 때는 반드시: +- ❌ `nohup python3 kis_trader/backtest/param_search_optuna.py ...` (직접 실행 금지) +- ✅ `curl -X POST http://192.168.0.149:5050/api/optuna/start -d '...'` (API 통해 실행) + +단, 메인 TPE가 이미 실행 중일 때 curl API는 "이미 실행 중" 에러를 반환하므로, 순차 실행 스크립트에서는 완료 폴링(`/api/optuna/status/`) 후 다음 전략 시작 로직이 필요합니다. + diff --git a/.cursor/rules/hts-condition-grids.mdc b/.cursor/rules/hts-condition-grids.mdc index af1b1a1..5942b7c 100644 --- a/.cursor/rules/hts-condition-grids.mdc +++ b/.cursor/rules/hts-condition-grids.mdc @@ -18,7 +18,7 @@ alwaysApply: true ## 하지 말 것 -- HTS 등락 3~10% / vol 150% / min_price 2000 등을 **그리드에 넣어야 한다**고 강제하지 말 것 +- HTS 등락·가격·대금(O/P) 밴드를 **그리드에 넣어야 한다**고 강제하지 말 것 - “HTS와 안 맞는다”며 fast 그리드를 HTS 숫자로 채우지 말 것 - SCAN에서 이미 통과한 종목에 TRIGGER에서 **동일 의미의 일봉 조건**을 다시 적용하지 말 것 (의도적 이중필터가 아니면) @@ -50,8 +50,12 @@ alwaysApply: true ## 참고: 현재 HTS 원본 (SCAN 전용, 그리드 강제 아님) -### 돌파 -- 종가 1000~200000 · 5봉평균 대비 ≥150% · 전일고가 상향돌파 · 등락 3~10% +### 돌파 — A∧B∧E∧G∧H∧I∧J∧L∧M∧O∧P (LS HTS `breakout2`, 2026-08-04) +- A [일] 1봉전 고가 < 0봉전 종가 · B [일] 1봉전 고가 > 0봉전 시가 (당일 시가 아래→종가로 전일고 돌파) +- E 코스피+코스닥 · G 현재가 3000~80000 · H 정리매매·거래정지 제외 · I 보통주 · J ETF/ETN·주식선물기초 제외 +- L [일] 등락률 2~15% · M [일] 0봉전 고가→종가 등락 -2~5% +- O [5분] 5봉 누적 거래대금 ≥100백만 · P [일] 순매수체결거래대금 ≥100000만원 +- (구식 참고) 종가 1000~200000 · 5봉평균 ≥150% · 전일고 상향 · 등락 3~10% — **폐기**, 그리드 강제 금지 동일 ### 꼬리 — (A∨G)∧B∧D∧E∧F - A [일] 시가→종가 -10~-0.5% · G [1분] 직전종가→종가 -10~-0.5% diff --git a/.cursor/rules/like-mcp-docs.mdc b/.cursor/rules/like-mcp-docs.mdc new file mode 100644 index 0000000..9cde9d1 --- /dev/null +++ b/.cursor/rules/like-mcp-docs.mdc @@ -0,0 +1,36 @@ +--- +description: docs/like_mcp.md 지도 필수 참조 · MD vs MCP/grep 역할 · 변경 시 MD stale 방지 (체크리스트 본문은 문서에만) +alwaysApply: true +--- + +# like_mcp 문서 지도 (중복 없는 포인터) + +체크리스트·ERD·의존성 **본문은 여기에 복사하지 말고** 아래 파일만 연다. +정합·DB adhoc·웹 재시작·Optuna 운영 세부는 기존 룰(`.cursorrules` CRITICAL, `live-backtest-optuna-parity`, `db-adhoc-query-safety` 등)을 따른다. + +## 1. 필수 문서 (`docs/like_mcp.md/`) + +| 문서 | 경로 | 언제 | +|------|------|------| +| 수정 가이드 | `docs/like_mcp.md/MODIFICATION_GUIDE.md` | **수정 전** — 유형별 필수 grep·사이드이펙트 | +| 아키텍처 맵 | `docs/like_mcp.md/code_architecture.md` | 구조·허브·import 영향 범위 | +| DB ERD | `docs/like_mcp.md/db_erd.md` | 테이블/컬럼 지도 (실행 전 스키마는 실DB `SHOW COLUMNS` — `db-adhoc-query-safety`) | + +주석/docstring만 바꾸는 경우가 아니면, 수정 전 `MODIFICATION_GUIDE.md`의 해당 유형 grep을 **실제로 실행**해 파일 목록을 확정한다. MD는 “어디를 찾을지” 지도일 뿐. + +## 2. MD vs MCP / 도구 (토큰) + +1. **거시(Layer·Hub·영향 범위)** → `docs/like_mcp.md/` 목차·관련 섹션만. +2. **미시(현재 소스·API)** → MCP(`kis-code-assistant` 등) 또는 Grep/부분 Read. 거대 파일 통째 읽기 지양. +3. **수정 범위 확정** → 가이드의 grep 후 그 결과로만 패치. + +## 3. 코드 변경 후 MD 갱신 (stale 방지) + +| 변경 | 갱신 | +|------|------| +| 파일 추가·삭제·이동 / import 구조 | `code_architecture.md` | +| DB 테이블·컬럼 | `db_erd.md` | +| ENV·params 키 / 전략 추가·삭제 | `MODIFICATION_GUIDE.md` 체크리스트 (± architecture) | + +재생성 스크립트는 `MODIFICATION_GUIDE.md` 하단 표를 따른다. +완료 보고에 실매·웹백테·파람 영향 여부를 명시한다 (세부 절차는 `live-backtest-optuna-parity` 등 기존 룰). diff --git a/.cursorrules b/.cursorrules index 81446cc..e50f410 100644 --- a/.cursorrules +++ b/.cursorrules @@ -86,6 +86,7 @@ - 23. **키움/KIS 인프라** — CRITICAL §3 (유량≠빈응답, approval 공유, REST/키발급 한도 준수, 시세실키≠매매모의, REST 웜업 1차→실패시만 증량). - 24. **에이전트 테스트·adhoc 뻘짓 금지** — CRITICAL §4 (스키마 먼저, import/캐시 실명, env 키=config 테이블 등록, 실패 1회만). - 25. **의심·불확실 시 근본원인 먼저** — CRITICAL §5 (MCP·`docs/`·공식 API 문서·기존 SafeRequest/세마포어부터. 추측 패치·한도 무시 재시도 금지). +- 26. **구조·수정 전 지도** — `docs/like_mcp.md/` (`MODIFICATION_GUIDE` · `code_architecture` · `db_erd`) + `.cursor/rules/like-mcp-docs.mdc`. 체크리스트 본문은 룰에 복붙하지 말고 해당 MD만 연다. - 26. **국내→해외/탭 이식 — 복붙 ≠ 완료** — CRITICAL §6 (summary 키·전역/종목 저장·폼 덮어쓰기·브라우저 검증). 상세: `.cursor/rules/domestic-port-ui-parity.mdc` - 27. **해외 전략 UI = 국장과 최대한 동일** — CRITICAL §6-1. 꼭 다르게 해야 하면 **구현 전 선보고·승인**. 임의로 생략·단축 금지. # [CRITICAL SYSTEM DIRECTIVES: 절대 엄수 사항 - 위반 시 작업 중지] @@ -171,8 +172,8 @@ ### 5-1. 조사 순서 (필수) 1. **증상 로그·에러코드** (유량=5, 429, invalid approval, Unknown column 등)를 있는 그대로 분류. -2. **`docs/`** (`정합성.md`, `계정.md`, 전략 QA 등) + 레포 내 기존 헬퍼(`SafeRequest`, `KISApprovalManager`, 세마포어) 검색. -3. **MCP** (브라우저·관련 서버) / 공식 OpenAPI·키움 문서로 한도·엔드포인트·실전·모의 도메인 확인. +2. **`docs/`** (`정합성.md`, `계정.md`, 전략 QA 등) + **구조 지도** `docs/like_mcp.md/` (수정 전 grep·허브·ERD) + 레포 헬퍼(`SafeRequest`, `KISApprovalManager`, 세마포어) 검색. +3. **MCP** (브라우저·kis-code-assistant·관련 서버) / 공식 OpenAPI·키움 문서로 한도·엔드포인트·실전·모의 도메인 확인. 거시=MD, 미시=MCP/Grep (`.cursor/rules/like-mcp-docs.mdc`). 4. 근본원인·설계를 **짧게 보고** → 승인 후 수정 (핵심 매매/인프라는 선보고). 5. API 관련 코드를 작성/수정할 때는, 본인의 지식에 의존하지 말고 반드시 연동된 MCP 도구를 우선 호출하여 스펙을 확인하라. ### 5-2. 금지 diff --git a/__pycache__/database.cpython-312.pyc b/__pycache__/database.cpython-312.pyc index 3a61e0968f429c9353b00e41df2dbddcbee51dde..06e95fc4b923654a5368251c696674d662f6b081 100644 GIT binary patch delta 5731 zcmb7I34ByV(tlMwZ;m9BYjP7vLe3!wfp7#&kZ>t690^H`0mP6Ary=?hKp~u>94d$5 zqfm}d+(ke(3ZgV3vIGwlvKm%IHVVqRTng*NxMl%mx9bHE{oM8U&F?q=u2)rEU0q$( zQ*SOj>wIsc)BlLm>7%L70GvJLz2!Uo@7w}gcNJ}kwx$1}JjoMZ9^i47r%v$m)Gg9W zMtRP8{1oSZ%R{eA{L9-#s>u`Vo|lxopmxDap@lCmvXm5h&U<#0hfYAxPQ@=OPxrjC zNZZcqs87i~>iL2v!?YbR!Q$DqNWUp7_I&AiRav-KjjyS(#52jW+q1n^fB9dD%A&{V zaZIp!-cyoN&q-dxTXG|z2RoCB%QMJ~#LFoYml zmkZm2&ghzEuaIj0dwV}8(@JI4ody^@Tvtq zlY6&9cMJSeUit%U^@j+#<_#$EgITiq5H#4}b-DjTIIP28<fVArv6dN^}1Xp|s=gV3qsMfdXqF)#M`KC)!xpWHtAC8c6Bux5RB#!V{)!tXFGItx#e?1GNJp-~J~$+oT!nt%zlpt>-4>hJ4s`4XcvSaI z3TzFdF+YMkJlr>AFtmngLtr@67r#~F4QDT+X#J01*yFXx9Ju^#2N`w^Ityr$0|74r z9F=JT*XZiJ4%yLH2ePi9#<} zd)QRT_M-cvF&EZQT{OlG&QPr3MjsZSikx9#N-*dqkD)6=f|6c=zOzK%v@|ywC%F0p zteU3iEONESBB-S)?Qx&)0M;JJFi3XmfS*|*ll&7gJ#aAdZjn-6BHjjBG%peJV4Xag zh^Zp_mr81mS|OXKj-&`TPKV=ixf@sMP(-m^(ZDV?THO_&vXm$l3U~p-DKiaw!`HGp z4HJ+ybjOZpRLFhZ@fn12D(Z<9Cevobu+a*-*b|3BuKUdY+PtlHC8wjTkSB9hUH^K%mTKM&0))AIC3K<#v!Jb)1rJ#>-LyZYQl0u zeW$AL3}Z3RTEcLO)%vL*8od-W31hOySZ<@bJcuXzDC`W+%bugqYKNyNb2RpL&*MoC z@=}9&(pwDknb-4%4Wl=ep=v@Oev8LRnmIqyD((#K#| zI3>%*;8}~Uk_8{=wWZ-MDl5guA(p->#gfoj%zu|Xp28lO+nz`Fj>m!4^(@*z&yUAR zuz-BaFi~BH-OBK9B6=>1PV=mre!Cq;2ez=Dx=lnEETX{^F-e8=0Uq44b|Q|)UtR4* z@|mLYY@jTif}aa`Qf5!XG=MrXDlrQ-(2h!sf!WkpiDfWC_IMDN>M)1)Psgk~ma!XV zfMuiP%9o}FuAFhgm^*xcqQcJgl4oJ+Y2v7=h3av?@U`?tAop5PZe&)nW* z)?P%bW?)C(RlLU247Id>29`jLjGT#S7T7{LvvEk$RwZ)#T#o{)nHD5dCwM&_4nmCGJnYQD;kuQA-Zts@I@BD^ir zAI0@L%%a96_!(T1bxU!ruCjH=3LG71R)<*_12{IX(9RX;3SQ2ZIC*XaU;`arfieD^ zk4<{{!`0)aaSiPJ9Yj;iO6&%9nYR*83U$6+S7Qe3rb(+Y0hZ8HtFa$+l?|)$F^o1( z&5VP2lCSa6+S&r#Koz7)c`$uWxD#f}Rco;ZRGRodi|J4-2Rw_{z&4w`I%=c+UqBd* zU60{VDyOf<0C0Y!BLDfHzATyHP9UaL=Dtnjb0-rSg3vrCZ^kJY=g8F_A;EPmEl;Yrp>WevpMrq zW53bquEHk+EZQ8$9D9v}7T82|N6=~EsZ8yG^l2~Q-Ox2Ss$|o4572GymXWeYE z)bp&71e-C6nk6RFQZEKj(jLrD^KDDEXbz_td-FH(635vL@d&2OD`GvX+)1;4fDUwE z4<=iW1*)FoCAzc+6T1xpuU#7qL*YO#uO@Q9>2=st2js80!q+;U>$SCKV^Fu209#0U ztH0JGb7#gb%6c73yJlv)wf(`_36}ammm=#EbVj#hK&Yti9x)(L9P|N29}LtLmRV?i zA%x&@jW(TzUKVvE!aR~7i#k2uTMofOd3#kF==9KDjBls^6iXFHoTJaxA5q1_BfY#A z?}8Za!VIdA=uZ#7f&F5BBck-&c?hR@AAp0td;_aO+}2+Pah{dxo=`p7EQ$)QjJUd?0 z$LoqN6Xwq4eQ)A@==xS2iK6{r^b(%bm1YGz)2NBx%FLucMmy;%p@{asuO5HF7$!s7t9)AR3R0bP9`vwiJ$ zb>logx?%05j@sckq80{H4knOl~Zzc0X<>ce|m~IVVL6Q9KQ+x#q%h+Hb zbIeS2G4&(!;+Wz!)xjL#to=33ulUvuGiLFGo85x(CS5&=E82RDNLqOcABMHk@j0$U zI4hq$jpr=aeW1l>F&;X%_3wwy;;EopUR<)&D@|20!Rrvd<_hROm7en0p~GWSW}TyY zFGMI_Ahn#sb#2XkS(mmty*_LCc`OM0KW=?{9`A!h%DaG3X=a@!@g|eaJ#$--!jzlg zwAyi#UR~)aeZ<(#yEpd{EsRcfn!gKUsp*1h!2TkAzQS$@`ILDPQ+>^~0b}XoD@fKsrcpAks?NAn?z^fAQnkSPk7xiakPlzO zPyvs~J#-hmK6qBs42CG`1LU|D)R6gkx=WXr&i;uJ-RAhH4WE=m zlsURK5W?ieI5EHyYZmHng)+`4acf?Sa#hOmFflf^x4 z{&iHY<)w&lD5l9NA_;DjD^tW^>(t*`QLQnC7b@oXj%DgThH(t{GyL3xnZFET2H z4tdrQiaQT3YWxs=Xlz&UvU1OvCQ_|aS>w8MrBswA5};7dP7^_vwy@=^&h3jHVqIIX zjz)=O`9e?e1>6Yg#@?bgEM=7(ip>ytI6?iH5{WMPc!o#^AllnUOjG~pHM;cEsjLkq({7%nqhWzg(2 zF;h72=X>0UVTfl)W$4O~&M<&s2*X_rISjcB-*M%YFg2c`oM93}F+(XsCBs8>EK?f1p4$qz* zJEycT**qM&=!4<8YOluQ9yOQ80|hPS=)GUM`-^-l4!n!z3=#3&v-*2Qm>zawSY8W{ z&1p_Of~~4yPup;y%zSDry039u+0i18JTOFDMd+jc;S`h9KXi#XYJeqj`Q74oU@Pnm N-M`feDPx%E_HUE9C7l2O delta 5177 zcma)934D!b)_>1=?t5=;;@<3=Y)C{FGz5t)w%8)ph!R^RZ`2y{QcESOr8U(mY3N~M zYdf{J9|nEYsFE66m{u7~l;P{5_*ys8p<3#D&KosqI`f2 zg%ROOWvtzJZ-Fgu@NY`~M@e#xE5_Es;IU|IQx=94rW)H9Xj|Bu`sF>Tp1(2Dl~q@v z?^v7h#|8RB*-+z)AY+FzG0gbF*r~=mW1O+ec)#3t>;1;cq~S80%KZ@~8Dm^vCl<$M z9953;nNyy+EjRF={7)5syv48B)-m~?{L_j*`BxVHsyIc)nFU(jl-bV2t2@N@MTr<_ zD<62Y`Hl~(hYMPN2tvy}HC{V9UA$Q6R&s-PVg>{lrLt&VH=M1qoUsM93;_q zy`dYtN)sy~Ts>uHL7F4EL{w--Q(BY-$e9rHQBNMb^an2SokO?un;}TxbTV)H@fNKyxb2g$~^Uv||nG zW!to4O{4mxiz=s=qN@U8`m`2Rt$;CI&jv?TN1^z~!9treyQMhpipXvzjyKb@TS`wZ z+|WVDOT%G<4(alDV_=PdJlUiWN`-x_Q`4pqH7$fD^5i&}69CW1zH{J?0|I2v`A}km zczJLk?6W~9Isa8y?tmdOd>MS_gc9ks2A;RWe!1umu+s+BvePDLX@gtRd=ECdAw)*( zg}iz&OU^g~#~mPLw@=}i4wt0+47}$B4!QRt97Jd-pT7)?bVw*4a5P9ZxB_uV6R*N& z<;D90>D!;cSDM!#3p#hKry}(RO`E5Al470h&IN=}Vp-zZfY_4(u^ug;v7aYS3k+Kk zur#8!aX?`7?Kmx_$;xpR6FiMImtReSyn7wu1w6rSXx<5kkb`c(QTUZFx9oKjIzq-w z_H|Odcje1F#u;)hFqGPswLKdccQP=}qXowMdE&Le;HAN}O#*|$ZpUlk4Oe? zE`LuO`Ee~ww!;!?@guxLKm7>Df61i^D&+@vAp_j2*~6T=Y#Kz7{xi(iz2p6E2hrl6 zAu%-AJE%L{4$^u+KR7V-<2+9=TMnZWKZEJjPz%q3J2xX`&->6+z>jh|;{O30mt$=B zh7KTqaH5Y6ank0Cvjk+z(f~}*p+K$=#la}dWINpp$J_7<{i8m<124*o2=stG)uCx- zYbuMvY$&6PQP{A1nqu`c(^-J38m5gY!Ju0_hUN@$O8O(TdsehdNl8TWoUVAjc1=@s z2g;1bA@CYiMB`qcEJ#~u5RKHB-JtCzS;;|WYpl$Iu5Z;j31d~PN zbwC%X>topgRkW~EiewD zkUVWLUV2jT4FrQ0w!z7DyhipIHadx-(y%wUF{cwl#z=eu{w9}=#IJ2oM04_SEksa60p(^~GR&tL<1k*8 zX*CaV^58hk$KQ0liP}$8y^|qdpNM}K@QO62U<$yy^!8Nj2&?GwRE&XV35u}*vSeW~ zzN|wD-I#$LJH5;?Sxvi$DQ|WxRfvg!<^hhhn!&23Rc0&a9FDjT&n%_V`PfJ~S~nlV zBHeYlSk=75dR9W?dG>s&oQaKmma>mBhS%xFOw5CsGHVv5*kC=)DZw7`8KuV>;}jmCG>>7Sg`um;p)h z-g0~qBdwQbmBV_WyOfVaM_rw(Xw3?>N;{L^O5ARHRym-~twe}CzfyIJ>Y7d!mM!+rk7hN~IQ)5W)NI82r8-o|bM&ePoW7#;ago~WC4 zp60`~s^ik`^*9AGDPaRnfKpk$0k>fCLk06Z`~O!h%a?*@;^>q0YR8+mS#1vg%jrha zp3Q3AET7J2y{{)67^J{TVc* zq36Iih;0wk411|_t~2MsNK+;L8P3w>MG!{!Kf)*J+pWs*pLSygE!&Sl_-ub__yKmI z3G2~K#UJ3Hzl&PyG{XeH}r`x7}LPR-4kd(6T)^=85)Q60{Ek zy2RQJ_y#KSK&;M~;OrVC4zvjC>MJUppy*0pU15Q+Do-c(;du()i|xTh1NY)S&L@zQ zNup67Vu5>;UbhPB5}_^!Fp6$|h^h7~9O`7cw^?;t z9Ue}&#zEN!aG3oT>lHoJ%hSzTC6?_fu@XU-_YPtiKoBiCgoo6DKIE|4`-jlN!|Hw# zE>9oE6#&t4@=-hk(1tRraIju2CXSy*E2^-IkJC#z@@DXnUshqNZcpY4=gX**_&Nkz z=TbcuKQfUHeuhh-pUgdl-(YMmhid034hAm;o4}0ZLDUtZ9zAsi`@npvIg7z`;0&Jg z;aO%hLk#`ltcvs%sf54R?S9P5m1P(3kC2J!iL2B<*Pv1;v* zH5@btSl8)ISf0Rek9mnqB{5u9^Q!gfQ;adQ%`L2Bg&e6ar(U#uCpMsp>*|tnT2@`h z76=3B-nW?SW38JOtlmL+D05?qYu!N^jgnvUg{rJt*~_ zx(<$zrT4HGsC(a`pV0&;+0|AA3-zJXUx-bh?A)=5&Z_wBbnzE^JhxFl6ZvIX3uiEA zYjwjg=8fRTJO}wEhz8WJo(N5>>r<9RuWkjYJpKc8Zrponu7~JS~^Sis81#);j!e>enzLm&4#CRqdQOe&&=ZlSii*55kuN z5=0M90NJ6X@VC`fW)RGvH`|B`RgaEo!ka%@ta^=Z zE7D*Ai?Zl)TQN|{Q`?E20I$=W_M!-$plj`g5z5J^>r0-BwTHT_Jfpm+Ay*c65H5&m z&uv!s{lwH@h608u3^QnTM-eu`y4G12Eo&2amqpgyZWmM5y>ky!2N{kq9Ah}a@CgH7 zq0BQ3=NK+9TxR%^;Tpqt3^yqzLo|Adw^*|QLmWd>h7^WY4BZ%dGW2HX!_b%EdtNBR znJQoy%P^i{7(+h8R0fmY%usK83mwT2?cM!B4_`OER^zcIU;1|v%Wz=2-fpAtXlmFN zXbroKT7IgT4sm+OM#WRZHe+|I?GtL5tjUIEdUsei-J|iiTj}z+e{iiedPd1xUBnxC7C0Z7=Z?LOZJHqgHEsY0Fjvyeud8 U6>o!MKpM2XsSTje{-VYI0|LH@f&c&j diff --git a/_test_ls_condition_realtime.py b/_test_ls_condition_realtime.py index 7ee03aa..cebf9c3 100644 --- a/_test_ls_condition_realtime.py +++ b/_test_ls_condition_realtime.py @@ -64,7 +64,11 @@ ITEM_SEARCH_URL = f"{LS_REST_BASE}/stock/item-search" # 종목검색 TR 초당 1회 (스펙 transactionPerSec) _TR_GAP_SEC = 1.1 -AFR_JOB_KO = {"N": "진입", "R": "재진입", "O": "이탈"} +try: + from kis_trader.network.condition_common import JOB_KO as AFR_JOB_KO +except Exception: + AFR_JOB_KO = {"N": "진입", "R": "재진입", "O": "이탈"} + def _setup_logger(log_path: Path) -> logging.Logger: diff --git a/backtest_web.py b/backtest_web.py index 1909e27..9255589 100644 --- a/backtest_web.py +++ b/backtest_web.py @@ -172,8 +172,13 @@ def _portfolio_live_price(code: str, row: Dict[str, Any], market_client) -> floa # 헬퍼 함수 # ──────────────────────────────────────────────────────────────────────────── +import threading +_db_local = threading.local() + def _db() -> TradeDB: - return TradeDB() + if not hasattr(_db_local, 'db'): + _db_local.db = TradeDB() + return _db_local.db def _default_trading_day_ymd() -> str: @@ -415,6 +420,27 @@ def _trades_recent_first(trades: List[Dict], limit: int = 200) -> List[Dict]: return [_trade_with_int_prices(t) for t in sliced] +def _finalize_trades_ui( + db: Any, + trades: List[Dict], + limit: int = 200, + *, + strategy_hint: str = "", +) -> List[Dict]: + """최신순 슬라이스 + 매수/매도 시점 호가 부착 — 실매·가상 거래표 공용.""" + out = _trades_recent_first(trades, limit) + if not out: + return out + try: + from kis_trader.backtest.trade_orderbook_enrich import ( + enrich_trades_with_orderbook, + ) + enrich_trades_with_orderbook(db, out, strategy_hint=strategy_hint) + except Exception as e: + logger.warning("orderbook enrich 스킵: %s", e) + return out + + def _momentum_source_label(kind: str, src: str) -> str: s = str(src or "").strip().lower() if kind == "entry": @@ -851,6 +877,12 @@ def api_actual(): port_strat = _portfolio_strategy_key(strat_u) if port_strat not in ("TAIL", "SHORT", "SCALP", "BREAKOUT", "RANGE_BREAK", "UPDOW", "DBBAND"): port_strat = "SCALP" + if request.args.get("candle_source"): + os.environ["CANDLE_SOURCE"] = request.args.get("candle_source") + if request.args.get("tick_source"): + os.environ["TICK_SOURCE"] = request.args.get("tick_source") + if request.args.get("ob_source"): + os.environ["OB_SOURCE"] = request.args.get("ob_source") port = resolve_portfolio_params(env_row, strategy=port_strat) total_budget_krw = float(port.get("total_budget_krw") or 0) except Exception: @@ -922,7 +954,9 @@ def api_actual(): "daily": daily_list, "reasons": reasons, "top_codes": top_list, - "trades": _trades_recent_first(trades, 200), + "trades": _finalize_trades_ui( + db, trades, 200, strategy_hint=str(strategy or ""), + ), } # 해외 전략만 표시용 USD + 당일 환율 원 환산 (국내 핸들러 로직 변경 없음) if str(strategy or "").upper().startswith("US_"): @@ -1648,6 +1682,60 @@ def api_actual_dashboard(): db.close() +@app.route("/api/condition_job_events", methods=["GET"]) +def api_condition_job_events(): + """조건검색 진입/재진입/이탈 이벤트 (condition_job_events).""" + from kis_trader.network.condition_job_events import query_condition_job_events + + start = (request.args.get("start") or "").strip()[:19] + end = (request.args.get("end") or "").strip()[:19] + if not start or not end: + day = _default_trading_day_ymd() + start = start or day + end = end or day + try: + limit = int(request.args.get("limit") or 500) + except (TypeError, ValueError): + limit = 500 + db = _db() + try: + rows = query_condition_job_events( + db, + start=start, + end=end, + strategy_id=(request.args.get("strategy_id") or "").strip(), + broker=(request.args.get("broker") or "").strip(), + job_flag=(request.args.get("job_flag") or "").strip(), + code=(request.args.get("code") or "").strip(), + limit=limit, + ) + return jsonify({"ok": True, "rows": rows, "n": len(rows)}) + except Exception as e: + logger.exception("condition_job_events 조회 실패") + return jsonify({"ok": False, "error": str(e)}), 500 + finally: + db.close() + + +@app.route("/api/condition_parity", methods=["GET"]) +def api_condition_parity(): + """실매소스 vs history vs job — ALIGN/유니버스0 가독성 요약.""" + from kis_trader.network.condition_job_events import build_condition_parity_report + + day = (request.args.get("date") or request.args.get("start") or "").strip()[:10] + if not day: + day = _default_trading_day_ymd() + db = _db() + try: + payload = build_condition_parity_report(db, day=day) + return jsonify({"ok": True, **payload}) + except Exception as e: + logger.exception("condition_parity 실패") + return jsonify({"ok": False, "error": str(e)}), 500 + finally: + db.close() + + # ──────────────────────────────────────────────────────────────────────────── # API: 스캘핑 가격 재현 백테스트 (ws_candles 기반) # ──────────────────────────────────────────────────────────────────────────── @@ -2004,6 +2092,20 @@ def _momentum_exit_ui_from_engine() -> Dict[str, Any]: "pullback_max_pct": float(d.get("pullback_max_pct") or 3.0), "ema_fast_period": int(d.get("ema_fast_period") or 9), "ema_slow_period": int(d.get("ema_slow_period") or 21), + "exit_ob_enabled": bool(d.get("exit_ob_enabled", False)), + "exit_ob_ratio_min": float(d.get("exit_ob_ratio_min") or 0.4), + "exit_ob_ma_window": int(d.get("exit_ob_ma_window") or 5), + "exit_ob_min_profit_pct": _momentum_ratio_to_ui_pct( + d.get("exit_ob_min_profit_pct"), 0.5, + ), + "exit_ob_min_hold_bars": int(d.get("exit_ob_min_hold_bars") or 3), + "stop_ob_enabled": bool(d.get("stop_ob_enabled", False)), + "stop_ob_ratio_min": float(d.get("stop_ob_ratio_min") or 0.4), + "stop_ob_ma_window": int(d.get("stop_ob_ma_window") or 5), + "stop_ob_min_loss_pct": _momentum_ratio_to_ui_pct( + d.get("stop_ob_min_loss_pct"), 0.3, + ), + "stop_ob_min_hold_bars": int(d.get("stop_ob_min_hold_bars") or 2), } @@ -2068,6 +2170,20 @@ def _momentum_engine_dict_to_ui( "pullback_max_pct": d.get("pullback_max_pct"), "ema_fast_period": d.get("ema_fast_period"), "ema_slow_period": d.get("ema_slow_period"), + "exit_ob_enabled": bool(d.get("exit_ob_enabled", False)), + "exit_ob_ratio_min": float(d.get("exit_ob_ratio_min") or 0.4), + "exit_ob_ma_window": int(float(d.get("exit_ob_ma_window") or 5)), + "exit_ob_min_profit_pct": _momentum_ratio_to_ui_pct( + d.get("exit_ob_min_profit_pct"), 0.5, + ), + "exit_ob_min_hold_bars": int(float(d.get("exit_ob_min_hold_bars") or 3)), + "stop_ob_enabled": bool(d.get("stop_ob_enabled", False)), + "stop_ob_ratio_min": float(d.get("stop_ob_ratio_min") or 0.4), + "stop_ob_ma_window": int(float(d.get("stop_ob_ma_window") or 5)), + "stop_ob_min_loss_pct": _momentum_ratio_to_ui_pct( + d.get("stop_ob_min_loss_pct"), 0.3, + ), + "stop_ob_min_hold_bars": int(float(d.get("stop_ob_min_hold_bars") or 2)), } if slot_raw not in (None, ""): out["slot_money"] = int(float(slot_raw)) @@ -2497,6 +2613,22 @@ def _bo_defaults_from_db() -> Dict[str, Any]: "max_spread_pct": pick( ("BREAKOUT_ORDERBOOK_MAX_SPREAD_PCT",), 0.45, float, ), + "exit_ob_enabled": ( + str(env.get("BREAKOUT_EXIT_OB_ENABLED") or "").strip().lower() + in ("1", "true", "t", "y", "yes", "on") + ), + "exit_ob_ratio_min": pick(("BREAKOUT_EXIT_OB_RATIO_MIN",), 0.4, float), + "exit_ob_ma_window": pick(("BREAKOUT_EXIT_OB_MA_WINDOW",), 5, lambda v: int(float(v))), + "exit_ob_min_profit_pct": pct_ui(pick(("BREAKOUT_EXIT_OB_MIN_PROFIT_PCT",), 0.005, float)), + "exit_ob_min_hold_bars": pick(("BREAKOUT_EXIT_OB_MIN_HOLD_BARS",), 3, lambda v: int(float(v))), + "stop_ob_enabled": ( + str(env.get("BREAKOUT_STOP_OB_ENABLED") or "").strip().lower() + in ("1", "true", "t", "y", "yes", "on") + ), + "stop_ob_ratio_min": pick(("BREAKOUT_STOP_OB_RATIO_MIN",), 0.4, float), + "stop_ob_ma_window": pick(("BREAKOUT_STOP_OB_MA_WINDOW",), 5, lambda v: int(float(v))), + "stop_ob_min_loss_pct": pct_ui(pick(("BREAKOUT_STOP_OB_MIN_LOSS_PCT",), 0.003, float)), + "stop_ob_min_hold_bars": pick(("BREAKOUT_STOP_OB_MIN_HOLD_BARS",), 2, lambda v: int(float(v))), } @@ -2856,6 +2988,14 @@ def _strategy_trigger_filter_enabled( @app.route("/api/backtest/scalping", methods=["GET"]) def api_backtest_scalping(): + # 백테스트 엔진 구동 전 os.environ 환경 변수 세팅 (요청에 포함된 경우) + if request.args.get("candle_source"): + os.environ["CANDLE_SOURCE"] = request.args.get("candle_source") + if request.args.get("tick_source"): + os.environ["TICK_SOURCE"] = request.args.get("tick_source") + if request.args.get("ob_source"): + os.environ["OB_SOURCE"] = request.args.get("ob_source") + # 기본값 = DB(엔진 단일 소스) → 백테스트/param_search/실매매 동일 값 _def = se.get_scalping_defaults_from_db() start = request.args.get("start", "") @@ -3164,6 +3304,68 @@ def api_backtest_scalping(): if _rat_req is not None else str(_mom_eng.get("ratchet_tiers") or "") ) + # L3 호가 보조컷 (진입 호가필터와 별개 · 기본 OFF) + _eob = request.args.get("exit_ob_enabled") + params["exit_ob_enabled"] = ( + str(_eob).strip().lower() in ("1", "true", "t", "y", "yes", "on") + if _eob not in (None, "") + else bool(_mom_eng.get("exit_ob_enabled", False)) + ) + _eob_r = request.args.get("exit_ob_ratio_min") + params["exit_ob_ratio_min"] = ( + float(_eob_r) + if _eob_r not in (None, "") + else float(_mom_eng.get("exit_ob_ratio_min") or 0.4) + ) + _eob_w = request.args.get("exit_ob_ma_window") + params["exit_ob_ma_window"] = ( + int(float(_eob_w)) + if _eob_w not in (None, "") + else int(_mom_eng.get("exit_ob_ma_window") or 5) + ) + _eob_p = request.args.get("exit_ob_min_profit_pct") + params["exit_ob_min_profit_pct"] = ( + abs(float(_eob_p)) / 100.0 + if _eob_p not in (None, "") + else float(_mom_eng.get("exit_ob_min_profit_pct") or 0.005) + ) + _eob_h = request.args.get("exit_ob_min_hold_bars") + params["exit_ob_min_hold_bars"] = ( + int(float(_eob_h)) + if _eob_h not in (None, "") + else int(_mom_eng.get("exit_ob_min_hold_bars") or 3) + ) + # 손절호가 (수익구간과 별개 · 기본 OFF) + _sob = request.args.get("stop_ob_enabled") + params["stop_ob_enabled"] = ( + str(_sob).strip().lower() in ("1", "true", "t", "y", "yes", "on") + if _sob not in (None, "") + else bool(_mom_eng.get("stop_ob_enabled", False)) + ) + _sob_r = request.args.get("stop_ob_ratio_min") + params["stop_ob_ratio_min"] = ( + float(_sob_r) + if _sob_r not in (None, "") + else float(_mom_eng.get("stop_ob_ratio_min") or 0.4) + ) + _sob_w = request.args.get("stop_ob_ma_window") + params["stop_ob_ma_window"] = ( + int(float(_sob_w)) + if _sob_w not in (None, "") + else int(_mom_eng.get("stop_ob_ma_window") or 5) + ) + _sob_p = request.args.get("stop_ob_min_loss_pct") + params["stop_ob_min_loss_pct"] = ( + abs(float(_sob_p)) / 100.0 + if _sob_p not in (None, "") + else float(_mom_eng.get("stop_ob_min_loss_pct") or 0.003) + ) + _sob_h = request.args.get("stop_ob_min_hold_bars") + params["stop_ob_min_hold_bars"] = ( + int(float(_sob_h)) + if _sob_h not in (None, "") + else int(_mom_eng.get("stop_ob_min_hold_bars") or 2) + ) _uhcf = request.args.get("use_high_chase_filter") params["use_high_chase_filter"] = ( str(_uhcf).strip().lower() in ("1", "true", "y", "yes", "on") @@ -3391,7 +3593,9 @@ def api_backtest_scalping(): total_budget_krw=total_budget_v, with_tick_debug=(mode == "momentum"), ) - trades_out = _trades_recent_first(all_virtual_trades, 200) + trades_out = _finalize_trades_ui( + db, all_virtual_trades, 200, strategy_hint=str(mode or "SCALP").upper(), + ) _enrich_trades_with_names(db, trades_out) return jsonify({ @@ -4426,46 +4630,27 @@ def _momentum_tab_save_patch(body: Dict[str, Any]) -> Dict[str, str]: x = gv("sl_pct") if x is not None: patch["MOMENTUM_STOP_LOSS_PCT"] = str(abs(float(x)) / 100.0) - patch["SCALP_STOP_LOSS_PCT"] = str(abs(float(x)) / 100.0) x = gv("tp_pct") if x is not None: patch["MOMENTUM_TAKE_PROFIT_PCT"] = str(abs(float(x)) / 100.0) - patch["SCALP_TAKE_PROFIT_PCT"] = str(abs(float(x)) / 100.0) x = gv("tp_max_pct") if x is not None: - sr = str(abs(float(x)) / 100.0) - patch["MOMENTUM_TP_MAX_PCT"] = sr - patch["SCALP_TP_MAX_PCT"] = sr + patch["MOMENTUM_TP_MAX_PCT"] = str(abs(float(x)) / 100.0) x = gv("shoulder_min_high") if x is not None: - sr = str(abs(float(x)) / 100.0) - patch["MOMENTUM_SHOULDER_MIN_HIGH_PCT"] = sr - patch["SCALP_SHOULDER_MIN_HIGH_PCT"] = sr + patch["MOMENTUM_SHOULDER_MIN_HIGH_PCT"] = str(abs(float(x)) / 100.0) x = gv("shoulder_cut_pct") if x is not None: - sr = str(abs(float(x)) / 100.0) - patch["MOMENTUM_SHOULDER_CUT_PCT"] = sr - patch["SCALP_SHOULDER_CUT_PCT"] = sr - - x = gv("trail_trigger") - if x is not None: - patch["SCALP_ATR_UP_MULT"] = str(abs(float(x)) / 100.0) - x = gv("trail_stop") - if x is not None: - patch["SCALP_ATR_DOWN_MULT"] = str(abs(float(x)) / 100.0) + patch["MOMENTUM_SHOULDER_CUT_PCT"] = str(abs(float(x)) / 100.0) x = gv("cooldown_min") if x is not None: - cd = str(int(float(x) * 60)) - patch["MOMENTUM_COOLDOWN_SEC"] = cd - patch["SCALP_COOLDOWN_SEC"] = cd + patch["MOMENTUM_COOLDOWN_SEC"] = str(int(float(x) * 60)) x = gv("max_daily") if x is not None: - md = str(int(float(x))) - patch["MOMENTUM_MAX_DAILY"] = md - patch["SCALP_MAX_DAILY"] = md + patch["MOMENTUM_MAX_DAILY"] = str(int(float(x))) x = gv("slots") if x is not None: @@ -4485,35 +4670,23 @@ def _momentum_tab_save_patch(body: Dict[str, Any]) -> Dict[str, str]: if x is not None: hx = float(x) ratio = hx if 0 < hx <= 1 else hx / 100.0 - sr = str(ratio) - patch["HIGH_CHASE_THR"] = sr - patch["SCALP_HIGH_PRICE_CHASE_THRESHOLD"] = sr - patch["HIGH_PRICE_CHASE_THRESHOLD"] = sr + patch["MOMENTUM_HIGH_CHASE_THR"] = str(ratio) x = gv("max_daily_chg") if x is not None: - vchg = str(float(x)) - patch["MAX_DAILY_CHG"] = vchg - patch["SCALP_MAX_DAILY_CHANGE_PCT"] = vchg - patch["MAX_DAILY_CHANGE_PCT"] = vchg + patch["MOMENTUM_MAX_DAILY_CHG"] = str(float(x)) x = gv("min_price") if x is not None: - mp = str(float(x)) - patch["MOMENTUM_MIN_PRICE"] = mp - patch["SCALP_MIN_PRICE"] = mp + patch["MOMENTUM_MIN_PRICE"] = str(float(x)) x = gv("max_loss_krw") if x is not None: - ml = str(int(float(x))) - patch["MOMENTUM_MAX_LOSS_PER_TRADE_KRW"] = ml - patch["SCALP_MAX_LOSS_PER_TRADE_KRW"] = ml - patch["MAX_LOSS_PER_TRADE_KRW"] = ml + patch["MOMENTUM_MAX_LOSS_PER_TRADE_KRW"] = str(int(float(x))) x = gv("min_margin") if x is not None: patch["MOMENTUM_MIN_PROFIT_PCT"] = str(float(x)) - patch["SCALP_MIN_PROFIT_PCT"] = str(float(x)) x = gv("mom_max_from_open_pct") if x is not None: @@ -4524,7 +4697,6 @@ def _momentum_tab_save_patch(body: Dict[str, Any]) -> Dict[str, str]: if "use_defense_filters" in body: patch["MOMENTUM_USE_DEFENSE_FILTERS"] = _env_bool_10(body.get("use_defense_filters")) - patch["SCALP_USE_DEFENSE_FILTERS"] = _env_bool_10(body.get("use_defense_filters")) if "ob_filter" in body: patch["MOMENTUM_ORDERBOOK_FILTER_ENABLED"] = _env_bool_10(body.get("ob_filter")) @@ -4557,6 +4729,37 @@ def _momentum_tab_save_patch(body: Dict[str, Any]) -> Dict[str, str]: if x is not None: patch["MOMENTUM_RATCHET_TIERS"] = str(x).strip() + if "exit_ob_enabled" in body: + patch["MOMENTUM_EXIT_OB_ENABLED"] = _env_bool_10(body.get("exit_ob_enabled")) + x = gv("exit_ob_ratio_min") + if x is not None: + patch["MOMENTUM_EXIT_OB_RATIO_MIN"] = str(float(x)) + x = gv("exit_ob_ma_window") + if x is not None: + patch["MOMENTUM_EXIT_OB_MA_WINDOW"] = str(int(float(x))) + x = gv("exit_ob_min_profit_pct") + if x is not None: + # UI % → 엔진 분율 + patch["MOMENTUM_EXIT_OB_MIN_PROFIT_PCT"] = str(abs(float(x)) / 100.0) + x = gv("exit_ob_min_hold_bars") + if x is not None: + patch["MOMENTUM_EXIT_OB_MIN_HOLD_BARS"] = str(int(float(x))) + + if "stop_ob_enabled" in body: + patch["MOMENTUM_STOP_OB_ENABLED"] = _env_bool_10(body.get("stop_ob_enabled")) + x = gv("stop_ob_ratio_min") + if x is not None: + patch["MOMENTUM_STOP_OB_RATIO_MIN"] = str(float(x)) + x = gv("stop_ob_ma_window") + if x is not None: + patch["MOMENTUM_STOP_OB_MA_WINDOW"] = str(int(float(x))) + x = gv("stop_ob_min_loss_pct") + if x is not None: + patch["MOMENTUM_STOP_OB_MIN_LOSS_PCT"] = str(abs(float(x)) / 100.0) + x = gv("stop_ob_min_hold_bars") + if x is not None: + patch["MOMENTUM_STOP_OB_MIN_HOLD_BARS"] = str(int(float(x))) + if "use_high_chase_filter" in body: patch["MOMENTUM_USE_HIGH_CHASE_FILTER"] = _env_bool_10(body.get("use_high_chase_filter")) if "use_daily_range_filter" in body: @@ -4803,14 +5006,10 @@ def _scalp_reversal_tab_save_patch(body: Dict[str, Any]) -> Dict[str, str]: x = gv("shoulder_min_high") if x is not None: - sr = str(abs(float(x)) / 100.0) - patch["SCALP_SHOULDER_MIN_HIGH_PCT"] = sr - patch["SHOULDER_MIN_HIGH_PCT"] = sr + patch["SCALP_SHOULDER_MIN_HIGH_PCT"] = str(abs(float(x)) / 100.0) x = gv("shoulder_cut_pct") if x is not None: - sr = str(abs(float(x)) / 100.0) - patch["SCALP_SHOULDER_CUT_PCT"] = sr - patch["SHOULDER_CUT_PCT"] = sr + patch["SCALP_SHOULDER_CUT_PCT"] = str(abs(float(x)) / 100.0) x = gv("trail_trigger") if x is not None: @@ -4827,17 +5026,11 @@ def _scalp_reversal_tab_save_patch(body: Dict[str, Any]) -> Dict[str, str]: if x is not None: hx = float(x) ratio = hx if 0 < hx <= 1 else hx / 100.0 - sr = str(ratio) - patch["SCALP_HIGH_PRICE_CHASE_THRESHOLD"] = sr - patch["HIGH_CHASE_THR"] = sr - patch["HIGH_PRICE_CHASE_THRESHOLD"] = sr + patch["SCALP_HIGH_PRICE_CHASE_THRESHOLD"] = str(ratio) x = gv("max_daily_chg") if x is not None: - vchg = str(float(x)) - patch["SCALP_MAX_DAILY_CHANGE_PCT"] = vchg - patch["MAX_DAILY_CHG"] = vchg - patch["MAX_DAILY_CHANGE_PCT"] = vchg + patch["SCALP_MAX_DAILY_CHANGE_PCT"] = str(float(x)) x = gv("min_price") if x is not None: @@ -4845,15 +5038,11 @@ def _scalp_reversal_tab_save_patch(body: Dict[str, Any]) -> Dict[str, str]: x = gv("max_loss_krw") if x is not None: - ml = str(int(float(x))) - patch["SCALP_MAX_LOSS_PER_TRADE_KRW"] = ml - patch["MAX_LOSS_PER_TRADE_KRW"] = ml + patch["SCALP_MAX_LOSS_PER_TRADE_KRW"] = str(int(float(x))) x = gv("min_margin") if x is not None: - mm = str(float(x)) - patch["SCALP_MIN_PROFIT_PCT"] = mm - patch["MOMENTUM_MIN_PROFIT_PCT"] = mm + patch["SCALP_MIN_PROFIT_PCT"] = str(float(x)) if "use_defense_filters" in body: patch["SCALP_USE_DEFENSE_FILTERS"] = _env_bool_10(body.get("use_defense_filters")) @@ -4866,18 +5055,13 @@ def _scalp_reversal_tab_save_patch(body: Dict[str, Any]) -> Dict[str, str]: patch["SCALP_TIME_START"] = str(int(float(x))) x = gv("time_end_hm") if x is not None: - te = str(int(float(x))) - patch["SCALP_TIME_END"] = te - patch["TIME_END"] = te + patch["SCALP_TIME_END"] = str(int(float(x))) x = gv("max_daily") if x is not None: patch["SCALP_MAX_DAILY"] = str(int(float(x))) x = gv("slot_money") if x is not None: - sm = str(int(float(x))) - patch["SLOT_MONEY_DEFAULT"] = sm - patch["SCALP_MAX_BUY_AMOUNT"] = sm - patch["MAX_BUY_AMOUNT_PER_STOCK"] = sm + patch["SCALP_MAX_BUY_AMOUNT"] = str(int(float(x))) x = gv("max_stocks") if x is not None: patch["SCALP_MAX_STOCKS"] = str(int(float(x))) @@ -5051,6 +5235,36 @@ def _breakout_tab_save_patch(body: Dict[str, Any]) -> Dict[str, str]: if "skip_hts_scan_dupes" in body: patch["BREAKOUT_SKIP_HTS_SCAN_DUPES"] = _env_bool_10(body.get("skip_hts_scan_dupes")) + # 수익구간 호가매도 / 손절호가 (기본 OFF · UI% → 분율) + if "exit_ob_enabled" in body: + patch["BREAKOUT_EXIT_OB_ENABLED"] = _env_bool_10(body.get("exit_ob_enabled")) + x = gv("exit_ob_ratio_min") + if x is not None: + patch["BREAKOUT_EXIT_OB_RATIO_MIN"] = str(float(x)) + x = gv("exit_ob_ma_window") + if x is not None: + patch["BREAKOUT_EXIT_OB_MA_WINDOW"] = str(int(float(x))) + x = gv("exit_ob_min_profit_pct") + if x is not None: + patch["BREAKOUT_EXIT_OB_MIN_PROFIT_PCT"] = str(abs(float(x)) / 100.0) + x = gv("exit_ob_min_hold_bars") + if x is not None: + patch["BREAKOUT_EXIT_OB_MIN_HOLD_BARS"] = str(int(float(x))) + if "stop_ob_enabled" in body: + patch["BREAKOUT_STOP_OB_ENABLED"] = _env_bool_10(body.get("stop_ob_enabled")) + x = gv("stop_ob_ratio_min") + if x is not None: + patch["BREAKOUT_STOP_OB_RATIO_MIN"] = str(float(x)) + x = gv("stop_ob_ma_window") + if x is not None: + patch["BREAKOUT_STOP_OB_MA_WINDOW"] = str(int(float(x))) + x = gv("stop_ob_min_loss_pct") + if x is not None: + patch["BREAKOUT_STOP_OB_MIN_LOSS_PCT"] = str(abs(float(x)) / 100.0) + x = gv("stop_ob_min_hold_bars") + if x is not None: + patch["BREAKOUT_STOP_OB_MIN_HOLD_BARS"] = str(int(float(x))) + # 당일 누적손익 다단 트레일 익절 (레칫식) — 꼬리와 동일 (공통 헬퍼) patch.update(_daily_trail_save_patch(body, "BREAKOUT")) @@ -5256,6 +5470,13 @@ def api_backtest_tail(): [V3 통합]: 추가 방어 파라미터(MA20, ATR 배수, 피뢰침 등) 적용 기본값 = DB(env_config) → tail_engine.get_tail_defaults_from_db(), 요청으로 덮어쓰기. """ + if request.args.get("candle_source"): + os.environ["CANDLE_SOURCE"] = request.args.get("candle_source") + if request.args.get("tick_source"): + os.environ["TICK_SOURCE"] = request.args.get("tick_source") + if request.args.get("ob_source"): + os.environ["OB_SOURCE"] = request.args.get("ob_source") + _def = _get_tail_defaults_for_backtest() start = request.args.get("start", "") end = request.args.get("end", "") @@ -5775,7 +5996,9 @@ def api_backtest_tail(): _enrich_momentum_trades_debug( tail_trades_all, total_budget_krw=total_budget_krw, with_tick_debug=True, ) - tail_trades_out = _trades_recent_first(tail_trades_all, 200) + tail_trades_out = _finalize_trades_ui( + db, tail_trades_all, 200, strategy_hint="TAIL", + ) return jsonify({ "params": { @@ -5861,6 +6084,13 @@ def api_backtest_tail(): @app.route("/api/backtest/breakout", methods=["GET"]) def api_backtest_breakout(): """돌파매매 백테스트 — ``run_breakout_backtest`` (라이브 BreakoutStrategy 와 동일 함수).""" + if request.args.get("candle_source"): + os.environ["CANDLE_SOURCE"] = request.args.get("candle_source") + if request.args.get("tick_source"): + os.environ["TICK_SOURCE"] = request.args.get("tick_source") + if request.args.get("ob_source"): + os.environ["OB_SOURCE"] = request.args.get("ob_source") + _def = _bo_defaults_from_db() start = request.args.get("start", "") end = request.args.get("end", "") @@ -5923,6 +6153,36 @@ def api_backtest_breakout(): resolve_breakout_skip_hts_scan_dupes(), ), ), + "exit_ob_enabled": _tail_bool_arg( + request, "exit_ob_enabled", _def.get("exit_ob_enabled", False), + ), + "exit_ob_ratio_min": _arg( + "exit_ob_ratio_min", _def.get("exit_ob_ratio_min", 0.4), float, + ), + "exit_ob_ma_window": _arg( + "exit_ob_ma_window", _def.get("exit_ob_ma_window", 5), lambda v: int(float(v)), + ), + "exit_ob_min_profit_pct": _arg( + "exit_ob_min_profit_pct", _def.get("exit_ob_min_profit_pct", 0.5), float, + ), + "exit_ob_min_hold_bars": _arg( + "exit_ob_min_hold_bars", _def.get("exit_ob_min_hold_bars", 3), lambda v: int(float(v)), + ), + "stop_ob_enabled": _tail_bool_arg( + request, "stop_ob_enabled", _def.get("stop_ob_enabled", False), + ), + "stop_ob_ratio_min": _arg( + "stop_ob_ratio_min", _def.get("stop_ob_ratio_min", 0.4), float, + ), + "stop_ob_ma_window": _arg( + "stop_ob_ma_window", _def.get("stop_ob_ma_window", 5), lambda v: int(float(v)), + ), + "stop_ob_min_loss_pct": _arg( + "stop_ob_min_loss_pct", _def.get("stop_ob_min_loss_pct", 0.3), float, + ), + "stop_ob_min_hold_bars": _arg( + "stop_ob_min_hold_bars", _def.get("stop_ob_min_hold_bars", 2), lambda v: int(float(v)), + ), } _rat_req = request.args.get("ratchet_tiers") if _rat_req is None: @@ -6092,7 +6352,9 @@ def api_backtest_breakout(): _enrich_momentum_trades_debug( all_trades, total_budget_krw=total_budget_v, with_tick_debug=True, ) - trades_out = _trades_recent_first(all_trades, 200) + trades_out = _finalize_trades_ui( + db, all_trades, 200, strategy_hint="BREAKOUT", + ) _enrich_trades_with_names(db, trades_out) ts_hm = int(ui["time_start_hm"]) @@ -6332,7 +6594,9 @@ def api_backtest_range_break(): _enrich_momentum_trades_debug( all_trades, total_budget_krw=total_budget_v, with_tick_debug=False, ) - trades_out = _trades_recent_first(all_trades, 200) + trades_out = _finalize_trades_ui( + db, all_trades, 200, strategy_hint="RANGE_BREAK", + ) _enrich_trades_with_names(db, trades_out) ts_hm = int(ui["time_start_hm"]) @@ -8285,6 +8549,97 @@ def _parse_live_quote_codes(raw: str) -> List[str]: return out +@app.route("/api/trade_replay_candles", methods=["GET"]) +def api_trade_replay_candles(): + """거래내역 DB차트 — ws_candles 당일 분봉 + 진입/청산 마커용. + + 증권사 REST 추가 호출 없음. market=US 는 ws_candles.market=US. + """ + code = str(request.args.get("code") or "").strip() + if not code: + return jsonify({"ok": False, "error": "code 필요"}), 400 + market = str(request.args.get("market") or "KR").strip().upper() or "KR" + try: + tf = max(1, int(request.args.get("tf") or 1)) + except (TypeError, ValueError): + tf = 1 + buy_raw = str(request.args.get("buy") or "").strip() + sell_raw = str(request.args.get("sell") or "").strip() + day = str(request.args.get("day") or "").strip() + + def _ymd8(raw: str) -> str: + s = ( + str(raw or "") + .replace("-", "") + .replace(":", "") + .replace(" ", "") + .replace("T", "") + ) + return s[:8] if len(s) >= 8 else "" + + def _hm_key(raw: str) -> str: + s = ( + str(raw or "") + .replace("-", "") + .replace(":", "") + .replace(" ", "") + .replace("T", "") + ) + if len(s) >= 12: + return s[:12] + if len(s) == 8: + return s + "0900" + return "" + + ymd = _ymd8(day) or _ymd8(buy_raw) or _ymd8(sell_raw) + if not ymd: + return jsonify({"ok": False, "error": "day/buy 날짜 필요"}), 400 + lo = ymd + "0000" + hi = ymd + "2359" + buy_key = _hm_key(buy_raw) + sell_key = _hm_key(sell_raw) + + db = _db() + try: + rows = db.conn.execute( + "SELECT candle_time, open, high, low, close, volume " + "FROM ws_candles WHERE market=%s AND code=%s AND timeframe=%s " + "AND candle_time >= %s AND candle_time <= %s " + "ORDER BY candle_time ASC", + (market, code, tf, lo, hi), + ).fetchall() + candles = [] + for r in rows: + ct = str(r.get("candle_time") or "") + candles.append({ + "t": ct, + "o": float(r.get("open") or 0), + "h": float(r.get("high") or 0), + "l": float(r.get("low") or 0), + "c": float(r.get("close") or 0), + "v": float(r.get("volume") or 0), + }) + return jsonify({ + "ok": True, + "code": code, + "market": market, + "timeframe": tf, + "day": f"{ymd[:4]}-{ymd[4:6]}-{ymd[6:8]}", + "buy_key": buy_key, + "sell_key": sell_key, + "candles": candles, + "n": len(candles), + }) + except Exception as e: + logger.exception("trade_replay_candles: %s", e) + return jsonify({"ok": False, "error": str(e)}), 500 + finally: + try: + db.close() + except Exception: + pass + + def _recv_ts_age_ms(recv_ts: str) -> Optional[int]: s = str(recv_ts or "").strip() if not s: @@ -8963,6 +9318,10 @@ def api_optuna_start(): or "" ) hist_src = str(hist_src).strip().lower() or None + tick_src = (body.get("tick_source") or request.form.get("tick_source") or "").strip() + ob_src = (body.get("ob_source") or request.form.get("ob_source") or "").strip() + candle_src = (body.get("candle_source") or request.form.get("candle_source") or "").strip() + try: trials = int(body.get("trials") or request.form.get("trials") or 200) except (TypeError, ValueError): @@ -8977,6 +9336,9 @@ def api_optuna_start(): mode=mode, symbol=symbol or None, universe_history_source=hist_src, + candle_source=candle_src or None, + tick_source=tick_src or None, + ob_source=ob_src or None, ) return jsonify({"ok": True, "job": meta}) except Exception as e: @@ -9159,6 +9521,21 @@ def api_optuna_apply(): return jsonify({"ok": False, "error": str(e)}), 400 +@app.route("/api/optuna/postprocess", methods=["POST"]) +def api_optuna_postprocess(): + import subprocess + import os + try: + script_path = os.path.join(os.path.dirname(__file__), "scripts", "run_optuna_postprocess.sh") + log_path = os.path.join(os.path.dirname(__file__), "logs", "optuna_postprocess.log") + with open(log_path, "w") as f: + subprocess.Popen(["nohup", "bash", script_path], stdout=f, stderr=subprocess.STDOUT, start_new_session=True) + return jsonify({"ok": True, "message": "후처리가 백그라운드에서 시작되었습니다."}) + except Exception as e: + logger.warning("optuna postprocess failed: %s", e) + return jsonify({"ok": False, "error": str(e)}), 400 + + # ── 전략 백테 CLI 잡 (꼬리·스캘핑·돌파·모멘텀 공용) ───────────────────────── @app.route("/api/backtest/job/start", methods=["POST"]) def api_bt_job_start(): @@ -9338,6 +9715,17 @@ def _kis_mock_from_db() -> bool: def _kis_mock_status_payload() -> Dict[str, Any]: kis_mock = _kis_mock_from_db() rc, status = _systemctl_kis_trader("is-active", use_sudo=False, timeout=10) + from database import TradeDB + from kis_trader.utils.env import get_env_from_db + + db = TradeDB() + try: + candle_src = get_env_from_db("CANDLE_SOURCE", "") or "전체" + tick_src = get_env_from_db("TICK_SOURCE", "") or "전체" + ob_src = get_env_from_db("OB_SOURCE", "") or "전체" + finally: + db.close() + bot_active = (status or "").strip() == "active" return { "ok": True, @@ -9347,6 +9735,9 @@ def _kis_mock_status_payload() -> Dict[str, Any]: "bot_status": (status or "").strip() or "unknown", "unit": _KIS_TRADER_UNIT, "is_active_rc": rc, + "candle_source": candle_src.upper() if candle_src != "전체" else "전체", + "tick_source": tick_src.upper() if tick_src != "전체" else "전체", + "ob_source": ob_src.upper() if ob_src != "전체" else "전체", } diff --git a/config_schema.py b/config_schema.py index 5cb5534..ae70e73 100644 --- a/config_schema.py +++ b/config_schema.py @@ -47,6 +47,8 @@ _EXPLICIT_KEY_TABLE: Dict[str, str] = { "STRATEGY_UPDOW_ENABLED": "config_updow", "STRATEGY_DBBAND_ENABLED": "config_dbband", "STRATEGY_DART_ENABLED": "config_dart", + # 해외 WS 소켓 기동 — 인프라(공통 env_config). 전략 테이블과 분리. + "OVERSEAS_WS_ENABLED": "env_config", "KIS_SCALP_MM_CHANNEL": "config_scalp", "KIS_SHORT_MM_CHANNEL": "config_short", "KIS_MOMENTUM_MM_CHANNEL": "config_momentum", diff --git a/database.py b/database.py index 03c2c17..feb837f 100644 --- a/database.py +++ b/database.py @@ -1738,7 +1738,7 @@ class TradeDB: is_confirmed TINYINT NOT NULL DEFAULT 1, source VARCHAR(10) NOT NULL DEFAULT 'ws', updated_at VARCHAR(30) NOT NULL, - UNIQUE KEY uq_candle (code, timeframe, candle_time), + UNIQUE KEY uq_candle (code, timeframe, candle_time, source), KEY idx_ws_candles_market (market, code, timeframe, candle_time) ) CHARACTER SET utf8mb4 """) @@ -2249,8 +2249,8 @@ class TradeDB: feat_vals.append(v if isinstance(v, (int, float)) else None) cols = ", ".join(["code", "name", "strategy", "avg_buy_price", "current_price", "stop_price", "target_price", "max_price", "atr_entry", "target_qty", "current_qty", "total_invested", - "status", "buy_date", "updated_at", "size_class"] + list(ML_ENTRY_FEATURE_COLUMNS)) - placeholders = ", ".join(["%s"] * (16 + len(ML_ENTRY_FEATURE_COLUMNS))) + "status", "buy_date", "updated_at", "size_class", "env_snapshot"] + list(ML_ENTRY_FEATURE_COLUMNS)) + placeholders = ", ".join(["%s"] * (17 + len(ML_ENTRY_FEATURE_COLUMNS))) # MySQL: ON DUPLICATE KEY UPDATE (excluded. → VALUES()) updates = ( "avg_buy_price = VALUES(avg_buy_price), current_price = VALUES(current_price), " @@ -2260,7 +2260,8 @@ class TradeDB: "current_qty = VALUES(current_qty), total_invested = VALUES(total_invested), " "max_price = GREATEST(max_price, VALUES(max_price)), " "status = VALUES(status), updated_at = VALUES(updated_at), " - "size_class = COALESCE(VALUES(size_class), size_class)" + "size_class = COALESCE(VALUES(size_class), size_class), " + "env_snapshot = COALESCE(VALUES(env_snapshot), env_snapshot)" ) for c in ML_ENTRY_FEATURE_COLUMNS: updates += f", `{c}` = COALESCE(VALUES(`{c}`), `{c}`)" @@ -2294,6 +2295,7 @@ class TradeDB: trade_data.get('buy_date', now), now, size_class, + trade_data.get('env_snapshot'), ) + tuple(feat_vals) try: @@ -3164,11 +3166,12 @@ class TradeDB: INNER JOIN stock_meta m ON m.code = w.code WHERE m.theme = %s AND w.timeframe = %s + AND w.source = 'kis' AND w.is_confirmed = 1 AND w.candle_time = ( SELECT MAX(candle_time) FROM ws_candles - WHERE code = w.code AND timeframe = w.timeframe + WHERE code = w.code AND timeframe = w.timeframe AND source = 'kis' ) """, (theme, tf)).fetchone() @@ -3205,11 +3208,12 @@ class TradeDB: INNER JOIN stock_meta m ON m.code = w.code WHERE m.theme IS NOT NULL AND m.theme != '' AND w.timeframe = %s + AND w.source = 'kis' AND w.is_confirmed = 1 AND w.candle_time = ( SELECT MAX(candle_time) FROM ws_candles - WHERE code = w.code AND timeframe = w.timeframe + WHERE code = w.code AND timeframe = w.timeframe AND source = 'kis' ) GROUP BY m.theme ORDER BY avg_rsi DESC @@ -3255,10 +3259,11 @@ class TradeDB: FROM ws_candles WHERE code IN ({}) AND timeframe = %s + AND source = 'kis' AND is_confirmed = 1 AND candle_time = ( SELECT MAX(candle_time) FROM ws_candles - WHERE code = ws_candles.code AND timeframe = ws_candles.timeframe + WHERE code = ws_candles.code AND timeframe = ws_candles.timeframe AND source = 'kis' ) """.format(",".join(["%s"] * len(codes))), (*codes, tf)).fetchall() @@ -4301,7 +4306,7 @@ class TradeDB: logger.error("upsert_ws_candle 실패(%s): %s", code, e) def get_ws_candles(self, code: str, timeframe: int, limit: int = 100, - confirmed_only: bool = False) -> list: + confirmed_only: bool = False, source: str = "kis") -> list: """ 최근 N개 봉 리스트 반환 (오래된 순 → 최신 순). confirmed_only=True 면 확정 봉만 반환 (RSI 계산용). @@ -4312,10 +4317,10 @@ class TradeDB: SELECT candle_time, open, high, low, close, volume, rsi_2, rsi_3, rsi_5, is_confirmed, source FROM ws_candles - WHERE code = ? AND timeframe = ? {cond} + WHERE code = %s AND timeframe = %s AND source = %s {cond} ORDER BY candle_time DESC - LIMIT ? - """, (code, timeframe, limit)).fetchall() + LIMIT %s + """, (code, timeframe, source, limit)).fetchall() # 최신→오래된 순으로 왔으므로 역정렬해서 반환 (오래된→최신) result = [dict(r) for r in rows] result.reverse() @@ -4324,16 +4329,16 @@ class TradeDB: logger.error("get_ws_candles 실패(%s): %s", code, e) return [] - def get_latest_ws_candle(self, code: str, timeframe: int) -> Optional[dict]: + def get_latest_ws_candle(self, code: str, timeframe: int, source: str = "kis") -> Optional[dict]: """가장 최신 봉 1개 반환 (진행 중 봉 포함).""" try: row = self.conn.execute(""" SELECT candle_time, open, high, low, close, volume, rsi_2, rsi_3, rsi_5, is_confirmed FROM ws_candles - WHERE code = ? AND timeframe = ? + WHERE code = %s AND timeframe = %s AND source = %s ORDER BY candle_time DESC LIMIT 1 - """, (code, timeframe)).fetchone() + """, (code, timeframe, source)).fetchone() return dict(row) if row else None except Exception as e: logger.error("get_latest_ws_candle 실패(%s): %s", code, e) @@ -4597,12 +4602,12 @@ class TradeDB: INNER JOIN ( SELECT code, MAX(candle_time) AS mt FROM ws_candles - WHERE market=%s AND timeframe=%s + WHERE market=%s AND timeframe=%s AND source='kis' AND code IN ({ph}) AND candle_time < %s GROUP BY code ) x ON c.code = x.code AND c.candle_time = x.mt - WHERE c.market=%s AND c.timeframe=%s + WHERE c.market=%s AND c.timeframe=%s AND c.source='kis' """, (mkt, tf) + tuple(uniq) + (cutoff, mkt, tf), ).fetchall() diff --git a/docs/like_mcp.md/MODIFICATION_GUIDE.md b/docs/like_mcp.md/MODIFICATION_GUIDE.md new file mode 100644 index 0000000..68916b6 --- /dev/null +++ b/docs/like_mcp.md/MODIFICATION_GUIDE.md @@ -0,0 +1,313 @@ +# kis_bot 코드 수정 가이드 — AI 에이전트 필수 체크리스트 + +> **이 문서의 목적**: 코드 수정 시 사이드 이펙트를 빠짐없이 잡기 위한 절차 정의. +> 두 참조 문서와 함께 사용하세요. +> - 📦 [code_architecture.md](./code_architecture.md) — 파일 간 import 의존성 맵 +> - 🗄️ [db_erd.md](./db_erd.md) — DB 테이블 스키마 & ERD + +--- + +## ⚡ 핵심 원칙 + +``` +코드를 수정하기 전에, +반드시 아래 섹션에 해당하는 grep을 먼저 실행하고 +결과를 확인한 뒤 수정 범위를 확정한다. +``` + +**grep을 생략해도 되는 유일한 경우**: 주석/docstring 텍스트만 바꾸는 경우. + +--- + +## 🔍 수정 유형별 필수 grep 목록 + +### 0. 조건검색 유니버스 (LS / 키움) — 시드·재시도 + +> LS 공백·ALIGN·job 이벤트 수정 시. 상세: [code_architecture.md 수동 노트](./code_architecture.md) + +```bash +grep -rn "LS_T1859_EMPTY_RETRY\|t1859\|_refresh_empty\|STALE_RESYNC\|rows_prefer" /home/hoon/kis_bot/kis_trader/network --include="*.py" +grep -rn "condition_common\|condition_job_events\|LIVE_UNIVERSE_SLOT_ALIGN\|CAND_LIMIT" /home/hoon/kis_bot --include="*.py" -l +``` + +**체크리스트**: +- [ ] LS: AFR≠풀덤프 → RAM0 재시드(`LS_T1859_EMPTY_RETRY_SEC`, 기본 **1**·TR_GAP≥1.1). **0=OFF** (무한 REST 아님) +- [ ] 키움 CNSRREQ / LS t1859 시드 경로 **합치지 말 것** (공통은 `condition_common` JOB 플래그만) +- [ ] 빈 t1859 → history INSERT 금지 +- [ ] `database.py` ENV 키 목록 + `env_config_ext` 저장 검증 +- [ ] 웹 조건 이벤트/정합 탭 API (해당 시) + +--- + +### 1. ENV 파라미터 추가/변경/삭제 + +> 예: `MOMENTUM_E_MIN_CHG_PCT`, `SCALP_STOP_LOSS_PCT` 등 + +```bash +# ① env 키 이름 직접 참조 +grep -r "MOMENTUM_E_MIN_CHG_PCT" /home/hoon/kis_bot --include="*.py" -l + +# ② params dict snake_case 키 +grep -r "e_min_chg_pct" /home/hoon/kis_bot --include="*.py" -l + +# ③ database.py CREATE TABLE 컬럼 정의 +grep -n "MOMENTUM_E_MIN_CHG_PCT" /home/hoon/kis_bot/database.py + +# ④ env_keys.py 등록 여부 +grep -r "E_MIN_CHG_PCT" /home/hoon/kis_bot/kis_trader/engine/momentum_env_keys.py +``` + +**확인 후 수정해야 할 곳 체크리스트**: +- [ ] `database.py` — CREATE TABLE에 컬럼 추가 +- [ ] `kis_trader/engine/momentum_env_keys.py` — `MOMENTUM_CONFIG_KEYS` 등록 +- [ ] `kis_trader/engine/momentum_engine.py` — `get_momentum_defaults()` 로드 및 return dict +- [ ] `kis_trader/engine/momentum_hts_logic.py` — 실제 로직에서 사용 +- [ ] `kis_trader/backtest/param_search_momentum.py` — 그리드/텀 탐색 범위 +- [ ] `kis_trader/backtest/param_search_apply_snapshot.py` — 스냅샷 적용 매핑 +- [ ] `kis_trader/backtest/optuna_search_space.py` — Optuna 탐색 공간 (해당 시) + +--- + +### 2. DB 테이블 컬럼 추가/변경/삭제 + +```bash +# ① 해당 테이블 참조 파일 목록 +grep -r "active_trades" /home/hoon/kis_bot --include="*.py" -l + +# ② 컬럼명 직접 참조 +grep -rn "avg_buy_price" /home/hoon/kis_bot --include="*.py" + +# ③ INSERT/UPDATE 구문 (컬럼 순서에 민감) +grep -rn "INSERT INTO active_trades" /home/hoon/kis_bot --include="*.py" +grep -rn "UPDATE active_trades" /home/hoon/kis_bot --include="*.py" +``` + +**확인 후 수정해야 할 곳 체크리스트**: +- [ ] `database.py` — `CREATE TABLE`, `ALTER TABLE` +- [ ] `database.py` — INSERT/UPDATE 쿼리 전부 +- [ ] 해당 테이블 참조 파일들 — SELECT 컬럼 파싱 로직 + +--- + +### 3. 함수/메서드 시그니처 변경 + +```bash +# ① 함수 정의 위치 +grep -rn "def eval_momentum_hts_buy_at_index" /home/hoon/kis_bot --include="*.py" + +# ② 함수 호출 위치 전부 +grep -rn "eval_momentum_hts_buy_at_index" /home/hoon/kis_bot --include="*.py" + +# ③ import 위치 +grep -rn "from.*import.*eval_momentum_hts_buy_at_index" /home/hoon/kis_bot --include="*.py" +``` + +**확인 후 수정해야 할 곳 체크리스트**: +- [ ] 함수 정의 파일 +- [ ] 함수를 import하는 모든 파일 +- [ ] 함수를 호출하는 모든 파일 + +--- + +### 4. 전략 params dict 키 추가/변경 + +> 예: `mom_vol_mult`, `e_min_chg_pct` 같은 params 딕셔너리 키 + +```bash +# ① params에서 꺼내는 곳 +grep -rn '"e_min_chg_pct"' /home/hoon/kis_bot --include="*.py" + +# ② params에 넣는 곳 (파일 목록) +grep -rn "e_min_chg_pct" /home/hoon/kis_bot --include="*.py" -l +``` + +**확인 후 수정해야 할 곳 체크리스트**: +- [ ] 엔진 `get_*_defaults()` 함수 — params 생성 +- [ ] 엔진 로직 파일 — `params.get("key", default)` +- [ ] 파라미터 서치 — 그리드 정의, 스냅샷 적용, `_to_env` 매핑 +- [ ] Optuna 탐색 공간 — `_suggest_from_grid` 대상 그리드 + +--- + +### 5. 클래스/모듈 이름 변경 또는 파일 이동 + +```bash +# ① import 참조 전부 +grep -rn "from kis_trader.engine.momentum_hts_logic import" /home/hoon/kis_bot --include="*.py" + +# ② 문자열로 언급되는 곳 (동적 import, 로그 등) +grep -rn "momentum_hts_logic" /home/hoon/kis_bot --include="*.py" +``` + +--- + +### 6. 웜업(Warmup) 로직 변경 + +```bash +# ① 웜업 관련 함수 목록 +grep -rn "candle_warmup" /home/hoon/kis_bot --include="*.py" -l +grep -rn "REST_WARMUP" /home/hoon/kis_bot --include="*.py" -l + +# ② 웜업 bars env 키 +grep -rn "BACKTEST_CANDLE_WARMUP_BARS" /home/hoon/kis_bot --include="*.py" +grep -rn "BACKTEST_REST_WARMUP_BARS" /home/hoon/kis_bot --include="*.py" +``` + +--- + +### 7. HTS 조건식 로직 변경 + +```bash +# ① skip_hts_scan_dupes 관련 +grep -rn "skip_hts" /home/hoon/kis_bot --include="*.py" -l + +# ② hts_trigger 설정 +grep -rn "hts_trigger_defaults" /home/hoon/kis_bot --include="*.py" +grep -rn "trigger_e_confirm" /home/hoon/kis_bot --include="*.py" +``` + +--- + +## 📋 수정 전 표준 워크플로우 + +``` +1. 수정 요청 파악 + └─ 어떤 유형의 변경인가? (ENV / DB / 함수 / 파라미터 / 파일이동 ...) + +2. code_architecture.md 에서 연관 파일 1차 파악 + └─ 허브 파일 Top 20 체크 + └─ 전략별 파일 분류에서 관련 전략 파일 확인 + └─ DB 테이블 ↔ 코드 파일 연관 맵 확인 + +3. 위 섹션의 grep 명령어 실행 (반드시) + └─ 발견된 파일 목록 확정 + +4. db_erd.md 에서 DB 스키마 확인 (DB 관련 변경 시) + └─ 컬럼 타입, PK, 인덱스 확인 + +5. 수정 범위 확정 후 코드 수정 + +6. 문법 검사 + └─ python3 -m py_compile <수정한 파일> + +7. (필수) docs/ 문서 갱신 — 아래 조건 중 하나라도 해당하면 + └─ 파일 추가/삭제/이동이 있었다면 → code_architecture.md 재생성 + └─ DB 테이블/컬럼이 변경됐으면 → db_erd.md 재생성 + └─ ENV 키/params 키가 추가됐으면 → MODIFICATION_GUIDE.md 체크리스트 갱신 +``` + +--- + +## 🚨 이 프로젝트 특이 사항 (절대 무시 금지) + +### SKIP_HTS_SCAN_DUPES 규칙 +``` +`*_SKIP_HTS_SCAN_DUPES` 는 사용자가 언급하기 전까지 false 유지. +임의로 true로 바꾸지 말 것. +``` + +### HTS 조건식과 코드 분리 +``` +HTS는 후보 유니버스 참고용. +그리드를 HTS 밴드에 맞추라고 강제하지 말 것. +``` + +### 백테스트 웜업 전략별 분리 +``` +모멘텀 웜업 변경이 스캘핑/꼬리잡기 웜업에 영향을 주지 않는다. +각 전략 웜업 함수는 독립적이다: + - momentum_backtest_candle_warmup_bars() ← momentum_backtest_common.py + - scalp_backtest_candle_warmup_bars() ← scalping_backtest_common.py + - tail_backtest_candle_warmup_bars() ← tail_backtest_common.py +``` + +### env 키 저장 경로 +``` +database.py의 CREATE TABLE 컬럼에 있는 키 → 해당 config_* 테이블 컬럼 +database.py의 CREATE TABLE에 없는 키 → env_config_ext (key-value 테이블) +새 env 키 추가 시 두 경로 중 어디에 저장될지 확인 필요. +``` + +### e_min_chg_pct 단위 +``` +e_min_chg_pct 는 퍼센트 단위 (0.5 = 0.5%). +코드 내부에서 / 100.0 변환 후 사용. +``` + +--- + +## 🛠️ 자주 쓰는 grep 원라이너 모음 + +```bash +# ENV 키 전체 참조 추적 +grep -rn "ENV_KEY_NAME" /home/hoon/kis_bot --include="*.py" + +# DB 테이블 참조 파일 목록 +grep -rl "table_name" /home/hoon/kis_bot --include="*.py" + +# params dict 특정 키 추적 +grep -rn '"param_key"' /home/hoon/kis_bot --include="*.py" + +# 함수 호출 추적 +grep -rn "function_name(" /home/hoon/kis_bot --include="*.py" + +# import 경로 추적 +grep -rn "from module.path import" /home/hoon/kis_bot --include="*.py" + +# 특정 전략 파일만 검색 +grep -rn "keyword" /home/hoon/kis_bot/kis_trader/engine --include="*momentum*" + +# SQL 쿼리 내 테이블 참조 +grep -rn "FROM table_name\|INTO table_name\|UPDATE table_name" /home/hoon/kis_bot --include="*.py" +``` + +--- + +## ✅ 이 가이드 + 두 MD 파일이면 충분한가? + +| 상황 | 충분 여부 | +|------|----------| +| ENV 파라미터 추가/변경 | ✅ 충분 (위 체크리스트 따르면) | +| DB 컬럼 추가/변경 | ✅ 충분 (grep 필수 실행 시) | +| 함수 시그니처 변경 | ✅ 충분 | +| 전략 간 공유 유틸 변경 | ✅ `code_architecture.md` 허브 파일 + grep | +| 동적 import 패턴 변경 | ⚠️ grep으로만 확인 가능 (MD로 탐지 불가) | +| 런타임에만 나타나는 의존 | ❌ 실제 실행해봐야 함 | + +> **결론**: grep을 반드시 실행한다는 전제 하에, +> 이 세 파일(MODIFICATION_GUIDE.md + code_architecture.md + db_erd.md)이면 +> 런타임 의존성을 제외한 대부분의 케이스에서 누락 없이 수정 가능합니다. + +--- + +## 🔄 docs/ 문서 갱신 규칙 (필수) + +### 갱신 트리거 조건 + +| 변경 유형 | 갱신할 문서 | +|-----------|------------| +| .py 파일 추가/삭제/이동 | `code_architecture.md` | +| import 구조 변경 | `code_architecture.md` | +| DB 테이블/컬럼 변경 | `db_erd.md` | +| ENV 키/params dict 키 추가 | `MODIFICATION_GUIDE.md` 체크리스트 | +| 전략 추가/삭제 | `code_architecture.md` + `MODIFICATION_GUIDE.md` | + +### 갱신 방법 + +```bash +# code_architecture.md 재생성 (AST 분석 스크립트) +cd /home/hoon/kis_bot && python3 /tmp/gen_arch_md.py + +# db_erd.md 재생성 (DB DDL 추출 → MD 변환) +cd /home/hoon/kis_bot && python3 /tmp/gen_erd_md.py +``` + +### 동적 import 안전성 (2026-08-01 감사 결과) + +이 프로젝트의 동적 import는 **총 7건**이며, 전부 무해한 패턴입니다: +- 표준 라이브러리 인라인 호출: `__import__("json")`, `__import__("time")` — 3건 +- 테스트 모듈 로딩: `importlib.import_module("_test_...")` — 2건 +- 레거시 (remove/ 폴더, 비활성) — 2건 + +**핵심 매매/엔진/백테 코드에서 동적 import로 전략을 분기하는 패턴은 없습니다.** +코드 수정 불필요. diff --git a/docs/like_mcp.md/code_architecture.md b/docs/like_mcp.md/code_architecture.md new file mode 100644 index 0000000..32bfb5c --- /dev/null +++ b/docs/like_mcp.md/code_architecture.md @@ -0,0 +1,1022 @@ +# kis_bot 코드 아키텍처 & 의존성 맵 + +> **자동 생성 문서** — AST 기반 import 정적 분석 결과 +> DB 스키마는 [db_erd.md](./db_erd.md) 를 함께 참조하세요. + +--- + +## 📊 프로젝트 통계 + +| 항목 | 값 | +|------|-----| +| 총 Python 파일 수 | **372** 개 | +| 총 코드 라인 수 | **164,876** 줄 | +| DB 테이블 수 | **50** 개 (→ [db_erd.md](./db_erd.md)) | +| DB 참조 파일 수 | **94** 개 | + +### 주요 외부 라이브러리 의존성 + +| 라이브러리 | 사용 파일 수 | +|------------|------------| +| `utils` | 78 | +| `kiwoom_rest_api` | 67 | +| `PySide2` | 52 | +| `tr` | 52 | +| `argparse` | 50 | +| `requests` | 32 | +| `engine` | 26 | +| `optuna` | 20 | +| `dotenv` | 19 | +| `pandas` | 18 | +| `pythoncom` | 16 | +| `win32com` | 16 | +| `websocket` | 13 | +| `asyncio` | 13 | +| `google` | 11 | +| `ws` | 11 | +| `holding_bot` | 10 | +| `signal` | 10 | +| `base` | 10 | +| `atexit` | 9 | + +--- + +## 🏗️ 레이어 아키텍처 (계층도) + +```mermaid +graph TD + subgraph "🏠 진입점 (Entry Points)" + A[holding_bot.py] + A2[scalping_bot.py] + A3[momentum_bot.py] + end + subgraph "⚙️ 전략 엔진 (kis_trader/engine)" + B[momentum_engine.py] + B2[tail_engine.py] + B3[scalping_engine.py] + B4[momentum_hts_logic.py] + end + subgraph "🔬 백테스트 (kis_trader/backtest)" + C[momentum_backtest_common.py] + C2[param_search_momentum.py] + C3[optuna_momentum.py] + end + subgraph "🗄️ 데이터 레이어" + D[(kis_quant_db)] + D2[database.py] + D3[kis_trader/utils/env.py] + end + subgraph "📡 실시간 데이터" + E[ws_candles] + E2[ws_ticks] + E3[ls_ws_*] + end + A --> B + A2 --> B3 + B --> B4 + B --> C + C2 --> C3 + C --> D2 + B --> D2 + D2 --> D + D3 --> D2 + D --> E + D --> E2 +``` + +--- + +## 🔗 핵심 허브 파일 (많이 import 되는 모듈 Top 20) + +> 이 파일들을 수정할 때 가장 많은 사이드 이펙트가 발생합니다. + +| 순위 | 모듈 | 의존하는 파일 수 | 주요 의존자 | +|------|------|-----------------|------------| +| 1 | `database` | 97 | `_test_ls_condition_realtime.py`, `_verify_shared_e2e.py`, `_verify_shared_e2e_breakout.py` 외 94개 | +| 2 | `kis_trader.utils.env` | 89 | `_test_kiwoom_condition_list.py`, `_test_kiwoom_condition_realtime.py`, `_test_ls_condition_realtime.py` 외 86개 | +| 3 | `kis_trader.backtest` | 31 | `_verify_shared_e2e.py`, `_verify_shared_e2e_breakout.py`, `backtest_web.py` 외 28개 | +| 4 | `kis_trader.backtest.backtest_portfolio_common` | 31 | `backtest_web.py`, `breakout_backtest_common.py`, `breakout_portfolio_backtest.py` 외 28개 | +| 5 | `kis_trader.engine` | 26 | `backtest_web.py`, `diag_backtest_4_30.py`, `dart_backtest_common.py` 외 23개 | +| 6 | `kis_trader.backtest.shared_ticks` | 17 | `_verify_columnar_bitid.py`, `_verify_shared_e2e.py`, `_verify_shared_e2e_breakout.py` 외 14개 | +| 7 | `kis_trader.ws.kis_ws` | 15 | `_test_kiwoom_condition_list.py`, `_test_kiwoom_condition_realtime.py`, `etf_ver1.py` 외 12개 | +| 8 | `kis_trader.utils.trade_time` | 14 | `backtest_web.py`, `backtest_portfolio_common.py`, `dbband_backtest_common.py` 외 11개 | +| 9 | `kis_trader.engine.indicator_cache` | 14 | `database.py`, `breakout_backtest_common.py`, `breakout_portfolio_backtest.py` 외 11개 | +| 10 | `kis_trader.engine.tail_tick_replay` | 13 | `_verify_columnar_bitid.py`, `param_search_optuna.py`, `scalping_portfolio_backtest.py` 외 10개 | +| 11 | `kis_trader.backtest.universe_history_source` | 13 | `backtest_web.py`, `breakout_backtest_common.py`, `momentum_backtest_common.py` 외 10개 | +| 12 | `kis_trader.backtest.param_search_momentum` | 12 | `_verify_shared_e2e.py`, `backtest_web.py`, `diag_momentum_backtest.py` 외 9개 | +| 13 | `kis_trader.backtest.breakout_tick_loader` | 12 | `_verify_shared_e2e_breakout.py`, `backtest_web.py`, `breakout_backtest_common.py` 외 9개 | +| 14 | `kis_trader.backtest.backtest_env_timeline` | 12 | `backtest_web.py`, `breakout_backtest_common.py`, `breakout_portfolio_backtest.py` 외 9개 | +| 15 | `kis_trader.database.db_manager` | 12 | `backtest_web.py`, `breakout_backtest_common.py`, `dbband_backtest_common.py` 외 9개 | +| 16 | `kis_trader.engine.momentum_engine` | 12 | `backtest_web.py`, `backtest_env_timeline.py`, `momentum_backtest_common.py` 외 9개 | +| 17 | `kis_trader.strategies.breakout` | 12 | `backtest_web.py`, `backtest_env_timeline.py`, `breakout_backtest_common.py` 외 9개 | +| 18 | `kis_trader.backtest.trigger_snapshot_loader` | 12 | `backtest_portfolio_common.py`, `breakout_portfolio_backtest.py`, `momentum_portfolio_backtest.py` 외 9개 | +| 19 | `kis_trader.utils.logger` | 12 | `breakout_backtest_common.py`, `ls_history_loaders.py`, `momentum_backtest_common.py` 외 9개 | +| 20 | `kis_trader.backtest.optuna_common` | 12 | `optuna_breakout.py`, `optuna_dart.py`, `optuna_momentum.py` 외 9개 | + +--- + +## 🗄️ DB 테이블 ↔ 코드 파일 연관 맵 + +> [db_erd.md](./db_erd.md) 의 각 테이블을 실제로 읽고/쓰는 파일 목록입니다. +> DB 스키마를 변경하면 아래 파일들을 반드시 확인하세요. + +| DB 테이블 | 참조 파일들 | ERD 링크 | +|-----------|------------|---------| +| `active_trades` | `backtest_web.py`, `database.py`, `etf_ver1.py`, `holding_bot.py` 외 15개 | [ERD](./db_erd.md#active_trades) | +| `active_trades_paper` | `backtest_web.py`, `us_momentum.py` | [ERD](./db_erd.md#active_trades_paper) | +| `ai_analysis_log` | `database.py`, `mm_butler.py` | [ERD](./db_erd.md#ai_analysis_log) | +| `buy_execution_log` | `database.py`, `kis_long_ver1.py` | [ERD](./db_erd.md#buy_execution_log) | +| `config_breakout` | `backtest_web.py`, `database.py`, `param_search_breakout.py`, `apply_unified_risk_env.py` 외 2개 | [ERD](./db_erd.md#config_breakout) | +| `config_dart` | `backtest_web.py`, `database.py` | [ERD](./db_erd.md#config_dart) | +| `config_dbband` | `database.py`, `dbband_engine.py`, `prune_env_config_strategy_columns.py` | [ERD](./db_erd.md#config_dbband) | +| `config_momentum` | `backtest_web.py`, `database.py`, `backtest_portfolio_common.py`, `param_search_momentum.py` 외 5개 | [ERD](./db_erd.md#config_momentum) | +| `config_range_break` | `backtest_web.py`, `database.py`, `range_break_engine.py`, `prune_env_config_strategy_columns.py` | [ERD](./db_erd.md#config_range_break) | +| `config_scalp` | `backtest_web.py`, `database.py`, `param_search_apply_snapshot.py`, `param_search_scalping.py` 외 5개 | [ERD](./db_erd.md#config_scalp) | +| `config_short` | `backtest_web.py`, `database.py`, `tail_env_keys.py`, `apply_unified_risk_env.py` 외 3개 | [ERD](./db_erd.md#config_short) | +| `config_updow` | `database.py`, `apply_unified_risk_env.py`, `migrate_split_env_config.py`, `prune_env_config_strategy_columns.py` | [ERD](./db_erd.md#config_updow) | +| `config_us_momentum` | `database.py`, `optuna_momentum.py`, `param_search_momentum.py`, `prune_env_config_strategy_columns.py` | [ERD](./db_erd.md#config_us_momentum) | +| `daily_summary` | `database.py` | [ERD](./db_erd.md#daily_summary) | +| `dart_disclosures` | `backtest_web.py`, `database.py`, `dart_backtest_common.py`, `dart_engine.py` 외 1개 | [ERD](./db_erd.md#dart_disclosures) | +| `dart_watchlist` | `backtest_web.py`, `database.py`, `dart_engine.py`, `dart_order_tracker.py` | [ERD](./db_erd.md#dart_watchlist) | +| `env_config` | `_test_ls_condition_realtime.py`, `backtest_web.py`, `check_account_config.py`, `copy_env_row_to_latest.py` 외 48개 | [ERD](./db_erd.md#env_config) | +| `env_config_ext` | `backtest_web.py`, `database.py`, `optuna_common.py`, `migrate_split_env_config.py` 외 1개 | [ERD](./db_erd.md#env_config_ext) | +| `holding_candles` | `backtest_web.py`, `holding_bot.py`, `kis_holding_ver1.py` | [ERD](./db_erd.md#holding_candles) | +| `holding_min_candles` | `backtest_web.py`, `holding_bot.py`, `dbband_backtest_common.py`, `dbband_param_search.py` 외 4개 | [ERD](./db_erd.md#holding_min_candles) | +| `holding_stock_config` | `holding_bot.py`, `kis_holding_ver1.py` | [ERD](./db_erd.md#holding_stock_config) | +| `kv_store` | `database.py` | [ERD](./db_erd.md#kv_store) | +| `ls_candidates_history` | `backtest_web.py`, `database.py`, `param_search_optuna.py`, `db_manager.py` 외 2개 | [ERD](./db_erd.md#ls_candidates_history) | +| `ls_universe_history` | `database.py`, `collect_ls_condition_history.py`, `collect_ls_universe_history.py` | [ERD](./db_erd.md#ls_universe_history) | +| `ls_ws_candles` | `backtest_web.py`, `database.py`, `breakout_backtest_common.py`, `scalping_backtest_common.py` 외 2개 | [ERD](./db_erd.md#ls_ws_candles) | +| `ls_ws_orderbook` | `database.py`, `ls_ws.py` | [ERD](./db_erd.md#ls_ws_orderbook) | +| `ls_ws_ticks` | `database.py`, `breakout_backtest_common.py`, `optuna_breakout.py`, `optuna_scalping.py` 외 2개 | [ERD](./db_erd.md#ls_ws_ticks) | +| `ls_ws_vi` | `database.py`, `ls_ws.py` | [ERD](./db_erd.md#ls_ws_vi) | +| `order_execution_history` | `database.py` | [ERD](./db_erd.md#order_execution_history) | +| `orders` | `backtest_web.py`, `db_manager.py`, `us_momentum.py`, `kiwoom_trader_ver2.py` | [ERD](./db_erd.md#orders) | +| `permanent_subscriptions` | `us_momentum.py`, `test_ls_ws.py` | [ERD](./db_erd.md#permanent_subscriptions) | +| `stock_meta` | `backtest_web.py`, `database.py`, `fetch_stock_meta.py`, `param_search_apply_snapshot.py` 외 8개 | [ERD](./db_erd.md#stock_meta) | +| `stock_share_meta` | `database.py`, `param_search_breakout.py`, `fill_stock_share_meta.py`, `test_kiwoom_ws_sub_limit.py` | [ERD](./db_erd.md#stock_share_meta) | +| `target_candidates` | `backtest_web.py`, `database.py`, `diag_backtest_4_30.py`, `param_search_momentum.py` 외 26개 | [ERD](./db_erd.md#target_candidates) | +| `target_candidates_history` | `backtest_web.py`, `database.py`, `diag_backtest_4_30.py`, `param_search_momentum.py` 외 17개 | [ERD](./db_erd.md#target_candidates_history) | +| `trade_history` | `backtest_web.py`, `database.py`, `kis_long_ver1.py`, `kis_long_ver2.py` 외 15개 | [ERD](./db_erd.md#trade_history) | +| `trade_history_paper` | `backtest_web.py` | [ERD](./db_erd.md#trade_history_paper) | +| `ws_candles` | `backtest_web.py`, `database.py`, `diag_backtest_4_30.py`, `breakout_backtest_common.py` 외 24개 | [ERD](./db_erd.md#ws_candles) | +| `ws_orderbook` | `database.py`, `backtest_portfolio_common.py`, `optuna_scalping.py`, `param_search_breakout.py` 외 6개 | [ERD](./db_erd.md#ws_orderbook) | +| `ws_program` | `database.py`, `backtest_portfolio_common.py`, `optuna_scalping.py`, `param_search_breakout.py` 외 3개 | [ERD](./db_erd.md#ws_program) | +| `ws_ticks` | `_verify_shared_e2e.py`, `_verify_shared_e2e_breakout.py`, `backtest_web.py`, `database.py` 외 21개 | [ERD](./db_erd.md#ws_ticks) | +| `ws_ticks_us` | `backtest_web.py`, `database.py`, `breakout_tick_loader.py`, `momentum_backtest_common.py` 외 2개 | [ERD](./db_erd.md#ws_ticks_us) | + +--- + +## 📂 전략별 파일 분류 + +### DART공시 + +| 파일 | 라인 수 | 레이어 | 내부 의존 | +|------|---------|--------|----------| +| `dart_backtest_common.py` | 142 | 🔬 백테스트 | `database`, `backtest`, `engine` +1 | +| `optuna_dart.py` | 104 | 🔬 백테스트 | `database`, `optuna_common`, `param_search_dart` +1 | +| `param_search_dart.py` | 95 | 🔬 백테스트 | `database`, `dart_backtest_common`, `engine` +1 | +| `dart_engine.py` | 380 | ⚙️ 엔진 | `database`, `scalping_engine`, `env` | +| `dart_order_tracker.py` | 642 | 📦 kis_trader | `database`, `db_manager`, `dart_watchlist` +3 | +| `dart_watchlist.py` | 168 | 📦 kis_trader | `env`, `logger` | +| `dart_strategy.py` | 248 | 📦 kis_trader | | + +### 공매도/숏 + +| 파일 | 라인 수 | 레이어 | 내부 의존 | +|------|---------|--------|----------| +| `kis_short_ver1.py` | 3,826 | 🏠 루트 | `database` | +| `kis_short_ver2.py` | 3,885 | 🏠 루트 | `database` | +| `kis_short_ver3.py` | 3,610 | 🏠 루트 | `database` | +| `update_env_short.py` | 140 | 🏠 루트 | `database` | + +### 공통 + +| 파일 | 라인 수 | 레이어 | 내부 의존 | +|------|---------|--------|----------| +| `_test_kiwoom_condition_list.py` | 180 | 🏠 루트 | `env`, `kis_ws` | +| `_test_kiwoom_condition_realtime.py` | 228 | 🏠 루트 | `env`, `kis_ws` | +| `_test_ls_condition_realtime.py` | 693 | 🏠 루트 | `database`, `ls_token`, `env` | +| `_verify_columnar_bitid.py` | 236 | 🏠 루트 | `shared_ticks`, `momentum_tick_replay`, `tail_tick_replay` +1 | +| `_verify_shared_e2e.py` | 152 | 🏠 루트 | `database`, `backtest`, `momentum_backtest_common` +3 | +| `auto_ai_reporter.py` | 263 | 🏠 루트 | | +| `backtest_web.py` | 9,455 | 🔬 백테스트 | `database`, `backtest`, `backtest_env_timeline` +43 | +| `carrot_region_sel.py` | 366 | 🏠 루트 | | +| `check_account_config.py` | 103 | 🏠 루트 | `database` | +| `check_db.py` | 28 | 🏠 루트 | | +| `config_schema.py` | 150 | 🏠 루트 | | +| `copy_env_row_to_latest.py` | 40 | 🏠 루트 | `database` | +| `database.py` | 5,014 | 🗄️ DB 레이어 | `paper_store`, `indicator_cache`, `env` +1 | +| `diag_backtest_4_30.py` | 198 | 🔬 백테스트 | `database`, `engine` | +| `etf_backtest.py` | 404 | 🔬 백테스트 | | +| `etf_ver1.py` | 887 | 🏠 루트 | `database`, `kis_ws` | +| `export_sniper.py` | 132 | 🏠 루트 | | +| `fetch_stock_meta.py` | 327 | 🏠 루트 | `database`, `kis_ws` | +| `holding_bot.py` | 2,577 | 🏠 루트 | `database`, `kis_ws` | +| `init_db.py` | 78 | 🏠 루트 | `database` | +| `kis_approval_manager.py` | 404 | 🏠 루트 | | +| `kis_holding_ver1.py` | 492 | 🏠 루트 | `database` | +| `kis_long_alert.py` | 267 | 🏠 루트 | `database` | +| `kis_long_ver1.py` | 1,953 | 🏠 루트 | `database` | +| `kis_long_ver2.py` | 1,008 | 🏠 루트 | `database` | +| `kis_token_manager.py` | 466 | 🏠 루트 | `database` | +| `__init__.py` | 3 | 📦 kis_trader | | +| `__init__.py` | 1 | 🔬 백테스트 | | +| `backtest_env_timeline.py` | 158 | 🔬 백테스트 | `breakout_backtest_common`, `env_timeline`, `dbband_engine` +5 | +| `backtest_portfolio_common.py` | 919 | 🔬 백테스트 | `database`, `trigger_snapshot_loader`, `daily_profit_halt` +5 | +| `backtest_web.py` | 49 | 🔬 백테스트 | | +| `bt_web_jobs.py` | 443 | 🔬 백테스트 | `kr_trading_day` | +| `env_timeline.py` | 106 | 🔬 백테스트 | | +| `ls_history_loaders.py` | 228 | 🔬 백테스트 | `logger` | +| `optuna_briefing.py` | 432 | 🔬 백테스트 | `database`, `env` | +| `optuna_common.py` | 672 | 🔬 백테스트 | `database`, `optuna_briefing`, `env` | +| `optuna_daily_trail_recommend.py` | 315 | 🔬 백테스트 | `param_search_apply_snapshot`, `env` | +| `optuna_mode_combo.py` | 278 | 🔬 백테스트 | `optuna_daily_trail_recommend`, `optuna_tpe_common`, `env` | +| `optuna_search_space.py` | 89 | 🔬 백테스트 | `param_search_breakout`, `param_search_momentum`, `param_search_scalping` +1 | +| `optuna_tpe_common.py` | 218 | 🔬 백테스트 | | +| `optuna_web_jobs.py` | 1,121 | 🔬 백테스트 | `backtest_portfolio_common`, `optuna_common`, `optuna_daily_trail_recommend` +6 | +| `param_apply_ai.py` | 314 | 🔬 백테스트 | `database`, `backtest_portfolio_common` | +| `param_search_apply_snapshot.py` | 648 | 🔬 백테스트 | `database`, `backtest`, `backtest_portfolio_common` +4 | +| `param_search_cli_common.py` | 110 | 🔬 백테스트 | | +| `param_search_dates.py` | 94 | 🔬 백테스트 | `env`, `kr_trading_day` | +| `param_search_optuna.py` | 1,033 | 🔬 백테스트 | `database`, `backtest`, `backtest_portfolio_common` +21 | +| `param_search_pool.py` | 711 | 🔬 백테스트 | `env` | +| `shared_ticks.py` | 507 | 🔬 백테스트 | `whipsaw_filter` | +| `trigger_snapshot_loader.py` | 564 | 🔬 백테스트 | `orderbook_filter`, `program_filter`, `env` +3 | +| `universe_history_source.py` | 81 | 🔬 백테스트 | `env` | +| `universe_timeline.py` | 271 | 🔬 백테스트 | `universe_history_source`, `db_manager`, `env` | +| `__init__.py` | 4 | 🗄️ DB 레이어 | | +| `db_manager.py` | 857 | 🗄️ DB 레이어 | `database`, `momentum_universe_timeline`, `universe_history_source` +1 | +| `paper_store.py` | 500 | 🗄️ DB 레이어 | `strategy_ids` | +| `__init__.py` | 5 | ⚙️ 엔진 | | +| `atr_series.py` | 37 | ⚙️ 엔진 | | +| `candle_rollup.py` | 158 | ⚙️ 엔진 | | +| `daily_profit_halt.py` | 811 | ⚙️ 엔진 | `env`, `logger` | +| `ema_trend_filter.py` | 84 | ⚙️ 엔진 | | +| `indicator_cache.py` | 412 | ⚙️ 엔진 | `ema_trend_filter`, `momentum_engine`, `env` | +| `limit_entry_common.py` | 196 | ⚙️ 엔진 | | +| `ls_feed_gate.py` | 20 | ⚙️ 엔진 | `env`, `ls_ws` | +| `mid_enroll_entry_gate.py` | 241 | ⚙️ 엔진 | `env` | +| `orderbook_env.py` | 187 | ⚙️ 엔진 | `env` | +| `orderbook_filter.py` | 245 | ⚙️ 엔진 | `env`, `orderbook_cache`, `trigger_eval_recorder` | +| `post_sell_candle_backfill.py` | 439 | ⚙️ 엔진 | `database`, `candle_rollup`, `env` +2 | +| `program_filter.py` | 229 | ⚙️ 엔진 | `env`, `program_cache`, `trigger_eval_recorder` | +| `strategy_eod.py` | 149 | ⚙️ 엔진 | | +| `tick_exit_common.py` | 384 | ⚙️ 엔진 | `shared_ticks`, `scalping_engine`, `whipsaw_filter` +2 | +| `trigger_eval_collect.py` | 76 | ⚙️ 엔진 | `env` | +| `whipsaw_filter.py` | 570 | ⚙️ 엔진 | `shared_ticks`, `env` | +| `__init__.py` | 5 | 📦 kis_trader | | +| `account_cash.py` | 174 | 📦 kis_trader | | +| `kis_client.py` | 1,553 | 📦 kis_trader | `api_reject_log`, `non_stock` | +| `order_manager.py` | 2,381 | 📦 kis_trader | `post_sell_candle_backfill`, `us_momentum_env_keys`, `api_reject_log` | +| `orderbook_sell.py` | 131 | 📦 kis_trader | | +| `orphan_reconcile.py` | 571 | 📦 kis_trader | | +| `main.py` | 2,353 | 📦 kis_trader | | +| `__init__.py` | 4 | 📦 kis_trader | | +| `condition_manager.py` | 446 | 📦 kis_trader | | +| `kiwoom_condition_manager.py` | 790 | 📦 kis_trader | | +| `ls_chart.py` | 239 | 📦 kis_trader | `database` | +| `ls_condition_manager.py` | 821 | 📦 kis_trader | `ls_token` | +| `ls_token.py` | 231 | 📦 kis_trader | `env` | +| `ls_ws_validator.py` | 168 | 📦 kis_trader | | +| `market_guard.py` | 572 | 📦 kis_trader | | +| `ranking_manager.py` | 350 | 📦 kis_trader | | +| `ws_manager.py` | 1,849 | 📦 kis_trader | `stock_share` | +| `ws_validator.py` | 227 | 📦 kis_trader | | +| `__init__.py` | 1 | 📦 kis_trader | | +| `__init__.py` | 5 | 📦 kis_trader | | +| `apply_unified_risk_env.py` | 81 | 📦 kis_trader | `database` | +| `approx_param_search_from_log.py` | 131 | 📦 kis_trader | `db_manager` | +| `backfill_trigger_eval_from_log.py` | 318 | 📦 kis_trader | `db_manager` | +| `fill_kiwoom_candles.py` | 405 | 📦 kis_trader | `database`, `env`, `kis_ws` | +| `fill_stock_share_meta.py` | 190 | 📦 kis_trader | `database`, `stock_share`, `env` +1 | +| `migrate_split_env_config.py` | 105 | 📦 kis_trader | `database` | +| `prune_env_config_strategy_columns.py` | 188 | 📦 kis_trader | `database`, `env` | +| `rebuild_universe_history_from_logs.py` | 418 | 📦 kis_trader | `database`, `db_manager` | +| `show_account_snapshot.py` | 323 | 📦 kis_trader | `kis_client`, `env` | +| `test_kis_ws_diagnostic.py` | 403 | 📦 kis_trader | `kis_client`, `env` | +| `verify_three_paths.py` | 231 | 📦 kis_trader | `param_search_scalping`, `momentum_engine`, `scalping_engine` +3 | +| `__init__.py` | 1 | 📦 kis_trader | | +| `stock_share.py` | 210 | 📦 kis_trader | `env`, `logger` | +| `__init__.py` | 30 | 📦 kis_trader | | +| `base.py` | 1,126 | 📦 kis_trader | `ls_feed_gate`, `mid_enroll_entry_gate` | +| `__init__.py` | 1 | 🛠️ 유틸 | | +| `api_reject_log.py` | 242 | 🛠️ 유틸 | `env` | +| `env.py` | 159 | 🛠️ 유틸 | `database` | +| `kis_ws_session_windows.py` | 149 | 🛠️ 유틸 | | +| `ls_ws_session_windows.py` | — | 🛠️ 유틸 | `env`, `session_hm` | +| `kr_trading_day.py` | 232 | 🛠️ 유틸 | `env` | +| `live_portfolio_common.py` | 328 | 🛠️ 유틸 | | +| `logger.py` | 219 | 🛠️ 유틸 | | +| `non_stock.py` | 157 | 🛠️ 유틸 | | +| `position_sizing.py` | 41 | 🛠️ 유틸 | | +| `request_handler.py` | 208 | 🛠️ 유틸 | | +| `session_hm.py` | 26 | 🛠️ 유틸 | | +| `stock_name.py` | 97 | 🛠️ 유틸 | | +| `strategy_ids.py` | 124 | 🛠️ 유틸 | | +| `trade_time.py` | 32 | 🛠️ 유틸 | | +| `universe_source.py` | 59 | 🛠️ 유틸 | | +| `usd_krw_fx.py` | 410 | 🛠️ 유틸 | `env`, `request_handler` | +| `live_config_schema.py` | 915 | 📦 kis_trader | `orderbook_env` | +| `__init__.py` | 22 | 📦 kis_trader | | +| `kis_ws.py` | 2,656 | 📦 kis_trader | `candle_rollup`, `env`, `kis_ws_session_windows` | +| `kis_ws_overseas.py` | 591 | 📦 kis_trader | `env`, `kis_ws_session_windows` | +| `kiwoom_ws.py` | 938 | 📦 kis_trader | `env` | +| `ls_ws.py` | — | 📦 kis_trader | `database`, `ls_chart`, `ls_token`, `ls_ws_session_windows` | +| `orderbook_cache.py` | 287 | 📦 kis_trader | | +| `program_cache.py` | 157 | 📦 kis_trader | | +| `tick_recorder.py` | 287 | 📦 kis_trader | | +| `trigger_eval_recorder.py` | 270 | 📦 kis_trader | | +| `trigger_snapshot_recorder.py` | 268 | 📦 kis_trader | | +| `kis_ws.py` | 3 | 🏠 루트 | `kis_ws` | +| `__init__.py` | 0 | 🏠 루트 | | +| `api.py` | 0 | 🏠 루트 | | +| `api_async.py` | 0 | 🏠 루트 | | +| `__init__.py` | 0 | 🏠 루트 | | +| `token.py` | 109 | 🏠 루트 | | +| `__init__.py` | 0 | 🏠 루트 | | +| `main.py` | 88 | 🏠 루트 | | +| `config.py` | 60 | 🏠 루트 | | +| `__init__.py` | 0 | 🏠 루트 | | +| `async_client.py` | 49 | 🏠 루트 | | +| `base.py` | 224 | 🏠 루트 | | +| `base_api.py` | 56 | 🏠 루트 | | +| `sync_client.py` | 43 | 🏠 루트 | | +| `__init__.py` | 0 | 🏠 루트 | | +| `account.py` | 2,050 | 🏠 루트 | | +| `analysis.py` | 108 | 🏠 루트 | | +| `chart.py` | 739 | 🏠 루트 | | +| `credit_order.py` | 291 | 🏠 루트 | | +| `elw.py` | 1,113 | 🏠 루트 | | +| `etf.py` | 621 | 🏠 루트 | | +| `foreign_institution.py` | 234 | 🏠 루트 | | +| `investor.py` | 115 | 🏠 루트 | | +| `market_condition.py` | 1,111 | 🏠 루트 | | +| `order.py` | 317 | 🏠 루트 | | +| `rank_info.py` | 2,311 | 🏠 루트 | | +| `sector.py` | 507 | 🏠 루트 | | +| `slb.py` | 287 | 🏠 루트 | | +| `stockinfo.py` | 1,608 | 🏠 루트 | | +| `theme.py` | 175 | 🏠 루트 | | +| `trading.py` | 107 | 🏠 루트 | | +| `trader.py` | 325 | 🏠 루트 | | +| `websocket.py` | 321 | 🏠 루트 | | +| `websocket_constants.py` | 186 | 🏠 루트 | | +| `websocket_helper.py` | 283 | 🏠 루트 | | +| `kiwoom_trader_dual.py` | 1,131 | 🏠 루트 | | +| `kiwoom_trader_ver2.py` | 3,179 | 🏠 루트 | `database` | +| `kiwoom_universe_scanner.py` | 684 | 🏠 루트 | `database` | +| `kiwoom_ws.py` | 3 | 🏠 루트 | `kiwoom_ws` | +| `config.py` | 43 | 🏠 루트 | | +| `run.py` | 23 | 🏠 루트 | | +| `ws_candle_agg.py` | 122 | 🏠 루트 | | +| `balance_window.py` | 135 | 🏠 루트 | | +| `button_holder.py` | 15 | 🏠 루트 | | +| `main.py` | 303 | 🏠 루트 | | +| `main_window.py` | 272 | 🏠 루트 | | +| `order_window.py` | 351 | 🏠 루트 | | +| `rockwidget.py` | 22 | 🏠 루트 | | +| `search_window.py` | 130 | 🏠 루트 | | +| `second_window.py` | 31 | 🏠 루트 | | +| `slider.py` | 17 | 🏠 루트 | | +| `test.py` | 100 | 🏠 루트 | | +| `third_window.py` | 31 | 🏠 루트 | | +| `k3_real.py` | 63 | 🏠 루트 | | +| `s3_real.py` | 63 | 🏠 루트 | | +| `tr_0424.py` | 813 | 🏠 루트 | | +| `tr_1857.py` | 245 | 🏠 루트 | | +| `tr_cancel_order.py` | 118 | 🏠 루트 | | +| `tr_kh.py` | 72 | 🏠 루트 | | +| `tr_master.py` | 67 | 🏠 루트 | | +| `tr_order.py` | 197 | 🏠 루트 | | +| `tr_pending.py` | 326 | 🏠 루트 | | +| `tr_redo_order.py` | 115 | 🏠 루트 | | +| `tr_sc0.py` | 108 | 🏠 루트 | | +| `tr_sc1.py` | 97 | 🏠 루트 | | +| `tr_sc2.py` | 82 | 🏠 루트 | | +| `tr_sc3.py` | 88 | 🏠 루트 | | +| `ui_search_window.py` | 67 | 🏠 루트 | | +| `ui_second_window.py` | 35 | 🏠 루트 | | +| `ui_test.py` | 65 | 🏠 루트 | | +| `ui_third_window.py` | 34 | 🏠 루트 | | +| `server.py` | 448 | 🏠 루트 | | +| `fetch_spec.py` | 269 | 🏠 루트 | | +| `server.py` | 417 | 🏠 루트 | | +| `ml_predictor.py` | 230 | 🏠 루트 | `database` | +| `mm_butler.py` | 1,235 | 🏠 루트 | `database`, `env` | +| `mm_remote.py` | 290 | 🏠 루트 | `database` | +| `modify_db_schema.py` | 37 | 🏠 루트 | | +| `news_analyzer.py` | 215 | 🏠 루트 | | +| `permanent_subs.py` | 319 | 🏠 루트 | `env` | +| `ai_recommend_notice.py` | 337 | 🏠 루트 | `database` | +| `back_test.py` | 164 | 🏠 루트 | | +| `risk_manager.py` | 307 | 🏠 루트 | | +| `balance_window.py` | 135 | 🏠 루트 | | +| `button_holder.py` | 15 | 🏠 루트 | | +| `main.py` | 303 | 🏠 루트 | | +| `main_window.py` | 272 | 🏠 루트 | | +| `order_window.py` | 351 | 🏠 루트 | | +| `rockwidget.py` | 22 | 🏠 루트 | | +| `search_window.py` | 130 | 🏠 루트 | | +| `second_window.py` | 31 | 🏠 루트 | | +| `slider.py` | 17 | 🏠 루트 | | +| `test.py` | 100 | 🏠 루트 | | +| `third_window.py` | 31 | 🏠 루트 | | +| `k3_real.py` | 63 | 🏠 루트 | | +| `s3_real.py` | 63 | 🏠 루트 | | +| `tr_0424.py` | 813 | 🏠 루트 | | +| `tr_1857.py` | 245 | 🏠 루트 | | +| `tr_cancel_order.py` | 118 | 🏠 루트 | | +| `tr_kh.py` | 72 | 🏠 루트 | | +| `tr_master.py` | 67 | 🏠 루트 | | +| `tr_order.py` | 197 | 🏠 루트 | | +| `tr_pending.py` | 326 | 🏠 루트 | | +| `tr_redo_order.py` | 115 | 🏠 루트 | | +| `tr_sc0.py` | 108 | 🏠 루트 | | +| `tr_sc1.py` | 97 | 🏠 루트 | | +| `tr_sc2.py` | 82 | 🏠 루트 | | +| `tr_sc3.py` | 88 | 🏠 루트 | | +| `ui_search_window.py` | 67 | 🏠 루트 | | +| `ui_second_window.py` | 35 | 🏠 루트 | | +| `ui_test.py` | 65 | 🏠 루트 | | +| `ui_third_window.py` | 34 | 🏠 루트 | | +| `_diag_mom_2min_gap.py` | 124 | 🏠 루트 | `database` | +| `_diag_mom_tick_today.py` | 99 | 🏠 루트 | `database` | +| `backfill_trade_candles.py` | 70 | 🏠 루트 | `database`, `post_sell_candle_backfill` | +| `collect_ls_condition_history.py` | 654 | 🏠 루트 | `database`, `env` | +| `collect_ls_universe_history.py` | 401 | 🏠 루트 | `database` | +| `copy_env_row_to_latest.py` | 0 | 🏠 루트 | | +| `kis_error_watch_mm.py` | 387 | 🏠 루트 | `env`, `logger` | +| `market_open_sim_smoke.py` | 362 | 🏠 루트 | `backtest`, `engine`, `kis_client` +6 | +| `post_optuna_mid_enroll_verify.py` | 214 | 🏠 루트 | `database`, `backtest`, `us_momentum_web_backtest` +2 | +| `query_kiwoom_condition_snapshot.py` | 454 | 🏠 루트 | `database`, `env`, `kis_ws` | +| `run_strategy_backtest_cli.py` | 256 | 🔬 백테스트 | `kr_trading_day` | +| `smoke_candle_upsert_rollup.py` | 144 | 🏠 루트 | `candle_rollup`, `kis_ws` | +| `verify_live_portfolio_align.py` | 125 | 🏠 루트 | `strategies`, `live_portfolio_common` | +| `verify_optuna_tpe_apply_dryrun.py` | 153 | 🏠 루트 | `backtest_portfolio_common`, `param_search_apply_snapshot`, `param_search_scalping` +1 | +| `verify_optuna_tpe_parity_checklist.py` | 283 | 🏠 루트 | `backtest_portfolio_common`, `optuna_breakout_tpe_space`, `optuna_briefing` +6 | +| `smart_executor.py` | 259 | 🏠 루트 | | +| `star.py` | 11 | 🏠 루트 | | +| `test_kiwoom_token.py` | 339 | 🏠 루트 | `database` | +| `test_kiwoom_ws.py` | 196 | 🏠 루트 | `database`, `kis_ws`, `kiwoom_ws` | +| `test_kiwoom_ws_sub_limit.py` | 453 | 🏠 루트 | `database`, `env`, `kis_ws` +1 | +| `test_ls_ws.py` | 998 | 🏠 루트 | `database` | +| `test_psearch.py` | 299 | 🏠 루트 | `kis_client`, `env` | +| `tmp_mom_smoke_web.py` | 46 | 🏠 루트 | | +| `trend_divergence.py` | 248 | 🏠 루트 | | +| `update_env.py` | 145 | 🏠 루트 | `database` | +| `update_env_etf.py` | 61 | 🏠 루트 | `database` | +| `update_env_simple.py` | 71 | 🏠 루트 | `database` | + +### 꼬리잡기(Tail) + +| 파일 | 라인 수 | 레이어 | 내부 의존 | +|------|---------|--------|----------| +| `optuna_tail_tpe_space.py` | 188 | 🔬 백테스트 | `optuna_tpe_common` | +| `tail_backtest_common.py` | 840 | 🔬 백테스트 | `backtest_env_timeline`, `backtest_portfolio_common`, `momentum_backtest_common` +12 | +| `tail_bt_web_jobs.py` | 14 | 🔬 백테스트 | `bt_web_jobs` | +| `tail_exit_ab_test.py` | 122 | 🔬 백테스트 | `database`, `backtest`, `engine` | +| `tail_mfe_analysis.py` | 305 | 🔬 백테스트 | `database`, `backtest`, `engine` +1 | +| `tail_param_search.py` | 1,948 | 🔬 백테스트 | `database`, `backtest`, `backtest_portfolio_common` +13 | +| `tail_tick_loader.py` | 18 | 🔬 백테스트 | `breakout_tick_loader` | +| `tail_engine.py` | 2,788 | ⚙️ 엔진 | `database`, `backtest_env_timeline`, `trigger_snapshot_loader` +15 | +| `tail_env_keys.py` | 471 | ⚙️ 엔진 | `database`, `env` | +| `tail_tick_replay.py` | 303 | ⚙️ 엔진 | `shared_ticks`, `candle_rollup`, `limit_entry_common` +1 | +| `tail_catch.py` | 503 | 📦 kis_trader | `tail_tick_replay` | +| `append_tail_optuna_compare.py` | 180 | 🏠 루트 | `database`, `backtest`, `engine` | +| `run_tail_backtest_cli.py` | 376 | 🔬 백테스트 | `database`, `backtest`, `backtest_portfolio_common` +4 | +| `tail_live_bt_forensics.py` | 158 | 🏠 루트 | `database`, `backtest`, `universe_timeline` +2 | +| `tail_symbol_gate_verify_20260709.py` | 126 | 🏠 루트 | `database`, `backtest`, `engine` +1 | +| `tail_engine.py` | 3 | ⚙️ 엔진 | `tail_engine` | + +### 더블볼린저 + +| 파일 | 라인 수 | 레이어 | 내부 의존 | +|------|---------|--------|----------| +| `dbband_backtest_common.py` | 381 | 🔬 백테스트 | `backtest_portfolio_common`, `db_manager`, `dbband_engine` +1 | +| `dbband_param_search.py` | 157 | 🔬 백테스트 | `database`, `backtest`, `backtest_portfolio_common` +3 | +| `dbband_engine.py` | 972 | ⚙️ 엔진 | `database`, `backtest_env_timeline`, `dbband_env_keys` +3 | +| `dbband_env_keys.py` | 212 | ⚙️ 엔진 | | +| `dbband_stock_cfg.py` | 343 | 📦 kis_trader | | +| `dbband_strategy.py` | 228 | 📦 kis_trader | | + +### 레인지브레이크 + +| 파일 | 라인 수 | 레이어 | 내부 의존 | +|------|---------|--------|----------| +| `param_search_range_break.py` | 299 | 🔬 백테스트 | `database`, `backtest`, `backtest_portfolio_common` +6 | +| `range_break_backtest_common.py` | 239 | 🔬 백테스트 | `backtest_env_timeline`, `backtest_portfolio_common`, `breakout_tick_loader` +5 | +| `range_break_portfolio_backtest.py` | 402 | 🔬 백테스트 | `backtest_env_timeline`, `backtest_portfolio_common`, `range_break_engine` +5 | +| `range_break_engine.py` | 460 | ⚙️ 엔진 | `database`, `range_break_portfolio_backtest`, `range_break_env_keys` +2 | +| `range_break_env_keys.py` | 88 | ⚙️ 엔진 | | +| `range_break.py` | 303 | 📦 kis_trader | | + +### 모멘텀 + +| 파일 | 라인 수 | 레이어 | 내부 의존 | +|------|---------|--------|----------| +| `compare_web_cli_momentum.py` | 62 | 🏠 루트 | | +| `diag_momentum_backtest.py` | 173 | 🔬 백테스트 | `database`, `backtest`, `param_search_momentum` | +| `momentum_backtest_common.py` | 659 | 🔬 백테스트 | `backtest_env_timeline`, `backtest_portfolio_common`, `breakout_tick_loader` +12 | +| `momentum_exit_ab_test.py` | 100 | 🔬 백테스트 | `database`, `backtest`, `engine` | +| `momentum_portfolio_backtest.py` | 930 | 🔬 백테스트 | `backtest_env_timeline`, `backtest_portfolio_common`, `momentum_tick_loader` +11 | +| `momentum_random_benchmark.py` | 565 | 🔬 백테스트 | `database`, `backtest`, `backtest_portfolio_common` +4 | +| `momentum_rr_crossval.py` | 466 | 🔬 백테스트 | `database`, `backtest`, `backtest_portfolio_common` +4 | +| `momentum_rr_quick_eval.py` | 113 | 🔬 백테스트 | `backtest`, `backtest_portfolio_common`, `momentum_rr_crossval` +1 | +| `momentum_tick_loader.py` | 19 | 🔬 백테스트 | `breakout_tick_loader` | +| `momentum_universe_timeline.py` | 125 | 🔬 백테스트 | `momentum_backtest_common`, `universe_history_source`, `universe_timeline` +2 | +| `optuna_momentum.py` | 689 | 🔬 백테스트 | `database`, `backtest`, `backtest_portfolio_common` +18 | +| `optuna_momentum_tpe_space.py` | 159 | 🔬 백테스트 | `optuna_tpe_common`, `param_search_momentum` | +| `param_search_momentum.py` | 2,577 | 🔬 백테스트 | `database`, `backtest`, `backtest_portfolio_common` +17 | +| `us_momentum_web_backtest.py` | 400 | 🔬 백테스트 | `backtest`, `us_momentum_env_keys`, `us_momentum_stock_cfg` +2 | +| `momentum_chase_patterns.py` | 228 | ⚙️ 엔진 | `env` | +| `momentum_engine.py` | 673 | ⚙️ 엔진 | `database`, `momentum_portfolio_backtest`, `momentum_env_keys` +6 | +| `momentum_env_keys.py` | 249 | ⚙️ 엔진 | | +| `momentum_hts_logic.py` | 587 | ⚙️ 엔진 | `momentum_chase_patterns`, `momentum_env_keys`, `orderbook_filter` +5 | +| `momentum_tick_replay.py` | 620 | ⚙️ 엔진 | `shared_ticks`, `momentum_engine`, `whipsaw_filter` +2 | +| `us_momentum_env.py` | 193 | ⚙️ 엔진 | `env` | +| `us_momentum_env_keys.py` | 296 | ⚙️ 엔진 | `env` | +| `compare_momentum_scan_memo.py` | 233 | 📦 kis_trader | `backtest`, `momentum_backtest_common`, `param_search_momentum` | +| `momentum.py` | 360 | 📦 kis_trader | `tail_tick_replay` | +| `us_momentum.py` | 1,123 | 📦 kis_trader | `database`, `tail_tick_replay` | +| `us_momentum_stock_cfg.py` | 378 | 📦 kis_trader | | +| `momentum_engine.py` | 3 | ⚙️ 엔진 | `momentum_engine` | +| `momentum_ratchet_ab_715.py` | 191 | 🏠 루트 | `optuna_common`, `optuna_momentum`, `param_search_momentum` +1 | + +### 미국모멘텀 + +| 파일 | 라인 수 | 레이어 | 내부 의존 | +|------|---------|--------|----------| +| `us_momentum_web_backtest.py` | 400 | 🔬 백테스트 | `backtest`, `us_momentum_env_keys`, `us_momentum_stock_cfg` +2 | +| `us_momentum_env.py` | 193 | ⚙️ 엔진 | `env` | +| `us_momentum_env_keys.py` | 296 | ⚙️ 엔진 | `env` | +| `us_momentum.py` | 1,123 | 📦 kis_trader | `database`, `tail_tick_replay` | +| `us_momentum_stock_cfg.py` | 378 | 📦 kis_trader | | + +### 브레이크아웃 + +| 파일 | 라인 수 | 레이어 | 내부 의존 | +|------|---------|--------|----------| +| `_verify_shared_e2e_breakout.py` | 156 | 🏠 루트 | `database`, `backtest`, `breakout_backtest_common` +3 | +| `breakout_backtest_common.py` | 624 | 🔬 백테스트 | `backtest_env_timeline`, `backtest_portfolio_common`, `breakout_tick_loader` +9 | +| `breakout_portfolio_backtest.py` | 504 | 🔬 백테스트 | `backtest_env_timeline`, `backtest_portfolio_common`, `trigger_snapshot_loader` +10 | +| `breakout_tick_loader.py` | 529 | 🔬 백테스트 | `database`, `candle_rollup` | +| `optuna_breakout.py` | 524 | 🔬 백테스트 | `database`, `backtest`, `backtest_portfolio_common` +19 | +| `optuna_breakout_tpe_space.py` | 96 | 🔬 백테스트 | `optuna_tpe_common` | +| `param_search_breakout.py` | 1,587 | 🔬 백테스트 | `database`, `backtest`, `backtest_portfolio_common` +14 | +| `breakout.py` | 2,047 | 📦 kis_trader | `breakout_portfolio_backtest`, `shared_ticks`, `tail_tick_replay` +2 | + +### 스캘핑 + +| 파일 | 라인 수 | 레이어 | 내부 의존 | +|------|---------|--------|----------| +| `holding_param_search.py` | 210 | 🔬 백테스트 | `database` | +| `optuna_scalping.py` | 599 | 🔬 백테스트 | `database`, `backtest`, `backtest_portfolio_common` +16 | +| `optuna_scalping_tpe_space.py` | 88 | 🔬 백테스트 | `optuna_tpe_common` | +| `param_search_scalping.py` | 1,661 | 🔬 백테스트 | `database`, `backtest`, `backtest_portfolio_common` +11 | +| `scalping_backtest_common.py` | 408 | 🔬 백테스트 | `backtest`, `backtest_env_timeline`, `backtest_portfolio_common` +9 | +| `scalping_portfolio_backtest.py` | 506 | 🔬 백테스트 | `backtest_env_timeline`, `backtest_portfolio_common`, `momentum_portfolio_backtest` +7 | +| `scalping_engine.py` | 1,346 | ⚙️ 엔진 | `database`, `scalping_portfolio_backtest`, `momentum_engine` +5 | +| `scalping.py` | 434 | 📦 kis_trader | `tail_tick_replay` | +| `scalping_bot.py` | 261 | 🏠 루트 | `database` | +| `scalping_logic.py` | 45 | 🏠 루트 | `engine` | +| `kis_scalping_ver1.py` | 1,658 | 🏠 루트 | `database` | +| `kis_scalping_ver2.py` | 1,596 | 🏠 루트 | `database` | +| `scalping_engine.py` | 3 | ⚙️ 엔진 | `scalping_engine` | +| `_diag_scalp_live_bt_20260716.py` | 317 | 🏠 루트 | `database`, `scalping_backtest_common`, `scalping_engine` | + +### 업다운 + +| 파일 | 라인 수 | 레이어 | 내부 의존 | +|------|---------|--------|----------| +| `param_search_updow.py` | 409 | 🔬 백테스트 | `database`, `backtest_portfolio_common`, `param_search_apply_snapshot` +3 | +| `param_search_updow_us.py` | 284 | 🔬 백테스트 | `database`, `strategies`, `updow_holding_cfg` +1 | +| `param_search_updown_box.py` | 276 | 🔬 백테스트 | `database`, `param_search_dates`, `engine` +2 | +| `updown_box.py` | 958 | ⚙️ 엔진 | | +| `updow_buy.py` | 1,383 | 📦 kis_trader | `scalping_engine` | +| `updow_holding_cfg.py` | 590 | 📦 kis_trader | | +| `updow_strategy.py` | 769 | 📦 kis_trader | | +| `updown_box_stock_cfg.py` | 221 | 📦 kis_trader | | +| `updown_feed.py` | 198 | 📦 kis_trader | | +| `updown_scan.py` | 154 | 📦 kis_trader | | +| `updown_watchlist.py` | 419 | 📦 kis_trader | | +| `updowbuyy.py` | 36 | 🏠 루트 | `updow_buy` | + +--- + +## 📋 전체 파일 의존성 상세 (All Files) + +| 파일 | 라인 | 내부 모듈 의존 | 외부 라이브러리 | +|------|------|---------------|----------------| +| `_test_kiwoom_condition_list.py` | 180 | `kis_trader.utils.env`, `kis_trader.ws.kis_ws` | `websocket` | +| `_test_kiwoom_condition_realtime.py` | 228 | `kis_trader.utils.env`, `kis_trader.ws.kis_ws` | `websocket` | +| `_test_ls_condition_realtime.py` | 693 | `database`, `kis_trader.network.ls_token`, `kis_trader.utils.env` | `argparse`, `requests`, `websocket` | +| `_verify_columnar_bitid.py` | 236 | `kis_trader.backtest.shared_ticks`, `kis_trader.engine.momentum_tick_replay`, `kis_trader.engine.tail_tick_replay`, `kis_trader.engine.whipsaw_filter` | - | +| `_verify_shared_e2e.py` | 152 | `database`, `kis_trader.backtest`, `kis_trader.backtest.momentum_backtest_common`, `kis_trader.backtest.momentum_tick_loader`, `kis_trader.backtest.param_search_momentum`, `kis_trader.backtest.shared_ticks` | - | +| `_verify_shared_e2e_breakout.py` | 156 | `database`, `kis_trader.backtest`, `kis_trader.backtest.breakout_backtest_common`, `kis_trader.backtest.breakout_tick_loader`, `kis_trader.backtest.param_search_breakout`, `kis_trader.backtest.shared_ticks` | - | +| `auto_ai_reporter.py` | 263 | - | `dotenv`, `google`, `requests`, `sqlite3` | +| `backtest_scalping/holding_param_search.py` | 210 | `database` | `argparse`, `holding_bot` | +| `backtest_web.py` | 9,455 | `database`, `kis_trader.backtest`, `kis_trader.backtest.backtest_env_timeline`, `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.backtest.breakout_backtest_common`, `kis_trader.backtest.breakout_tick_loader`, `kis_trader.backtest.dart_backtest_common`, `kis_trader.backtest.dbband_param_search`, `kis_trader.backtest.momentum_backtest_common`, `kis_trader.backtest.param_search_breakout`, `kis_trader.backtest.param_search_momentum`, `kis_trader.backtest.scalping_backtest_common`, `kis_trader.backtest.tail_param_search`, `kis_trader.backtest.universe_history_source`, `kis_trader.backtest.universe_timeline`, `kis_trader.backtest.us_momentum_web_backtest`, `kis_trader.database.db_manager`, `kis_trader.database.paper_store`, `kis_trader.engine`, `kis_trader.engine.candle_rollup`, `kis_trader.engine.daily_profit_halt`, `kis_trader.engine.dbband_env_keys`, `kis_trader.engine.limit_entry_common`, `kis_trader.engine.momentum_engine`, `kis_trader.engine.tail_env_keys`, `kis_trader.engine.updown_box`, `kis_trader.execution.account_cash`, `kis_trader.execution.kis_client`, `kis_trader.execution.order_manager`, `kis_trader.execution.orphan_reconcile`, `kis_trader.scan.dart_order_tracker`, `kis_trader.scan.dart_watchlist`, `kis_trader.strategies`, `kis_trader.strategies.base`, `kis_trader.strategies.breakout`, `kis_trader.strategies.range_break`, `kis_trader.strategies.updown_box_stock_cfg`, `kis_trader.strategies.updown_watchlist`, `kis_trader.strategies.us_momentum_stock_cfg`, `kis_trader.utils.env`, `kis_trader.utils.kr_trading_day`, `kis_trader.utils.stock_name`, `kis_trader.utils.strategy_ids`, `kis_trader.utils.trade_time`, `kis_trader.utils.usd_krw_fx`, `kis_trader.web.live_config_schema` | `config_schema`, `flask`, `holding_bot`, `kis_holding_ver1`, `permanent_subs` | +| `carrot_region_sel.py` | 366 | - | `bs4`, `mm_sender`, `requests`, `telegram` | +| `check_account_config.py` | 103 | `database` | - | +| `check_db.py` | 28 | - | `sqlite3` | +| `compare_web_cli_momentum.py` | 62 | - | - | +| `config_schema.py` | 150 | - | - | +| `copy_env_row_to_latest.py` | 40 | `database` | - | +| `database.py` | 5,014 | `kis_trader.database.paper_store`, `kis_trader.engine.indicator_cache`, `kis_trader.utils.env`, `kis_trader.utils.strategy_ids` | `config_schema`, `pymysql` | +| `diag_backtest_4_30.py` | 198 | `database`, `kis_trader.engine` | - | +| `diag_momentum_backtest.py` | 173 | `database`, `kis_trader.backtest`, `kis_trader.backtest.param_search_momentum` | `argparse` | +| `etf_backtest.py` | 404 | - | `numpy`, `pandas`, `requests`, `yfinance` | +| `etf_ver1.py` | 887 | `database`, `kis_trader.ws.kis_ws` | `pandas`, `requests` | +| `export_sniper.py` | 132 | - | `bs4`, `requests` | +| `fetch_stock_meta.py` | 327 | `database`, `kis_trader.ws.kis_ws` | `argparse`, `requests` | +| `holding_bot.py` | 2,577 | `database`, `kis_trader.ws.kis_ws` | `argparse`, `kis_token_manager`, `kiwoom_rest_api`, `requests` | +| `init_db.py` | 78 | `database` | - | +| `kis_approval_manager.py` | 404 | - | `requests` | +| `kis_holding_ver1.py` | 492 | `database` | `argparse`, `holding_bot` | +| `kis_long_alert.py` | 267 | `database` | `kis_long_ver1` | +| `kis_long_ver1.py` | 1,953 | `database` | `google`, `hmac`, `kis_token_manager`, `ml_predictor`, `pandas`, `requests` | +| `kis_long_ver2.py` | 1,008 | `database` | `kis_long_ver1`, `news_analyzer`, `pandas` | +| `kis_token_manager.py` | 466 | `database` | `argparse`, `requests` | +| `kis_trader/__init__.py` | 3 | - | - | +| `kis_trader/backtest/__init__.py` | 1 | - | - | +| `kis_trader/backtest/backtest_env_timeline.py` | 158 | `kis_trader.backtest.breakout_backtest_common`, `kis_trader.backtest.env_timeline`, `kis_trader.engine.dbband_engine`, `kis_trader.engine.momentum_engine`, `kis_trader.engine.range_break_engine`, `kis_trader.engine.scalping_engine`, `kis_trader.engine.tail_engine`, `kis_trader.strategies.breakout` | - | +| `kis_trader/backtest/backtest_portfolio_common.py` | 919 | `database`, `kis_trader.backtest.trigger_snapshot_loader`, `kis_trader.engine.daily_profit_halt`, `kis_trader.engine.scalping_engine`, `kis_trader.engine.strategy_eod`, `kis_trader.utils.env`, `kis_trader.utils.position_sizing`, `kis_trader.utils.trade_time` | - | +| `kis_trader/backtest/backtest_web.py` | 49 | - | `backtest_web`, `runpy` | +| `kis_trader/backtest/breakout_backtest_common.py` | 624 | `kis_trader.backtest.backtest_env_timeline`, `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.backtest.breakout_tick_loader`, `kis_trader.backtest.ls_history_loaders`, `kis_trader.backtest.universe_history_source`, `kis_trader.backtest.universe_timeline`, `kis_trader.database.db_manager`, `kis_trader.engine.indicator_cache`, `kis_trader.share.stock_share`, `kis_trader.strategies.breakout`, `kis_trader.utils.env`, `kis_trader.utils.logger` | - | +| `kis_trader/backtest/breakout_portfolio_backtest.py` | 504 | `kis_trader.backtest.backtest_env_timeline`, `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.backtest.trigger_snapshot_loader`, `kis_trader.engine.atr_series`, `kis_trader.engine.indicator_cache`, `kis_trader.engine.mid_enroll_entry_gate`, `kis_trader.engine.scalping_engine`, `kis_trader.engine.tick_exit_common`, `kis_trader.engine.whipsaw_filter`, `kis_trader.share.stock_share`, `kis_trader.strategies.base`, `kis_trader.strategies.breakout`, `kis_trader.utils.env` | - | +| `kis_trader/backtest/breakout_tick_loader.py` | 529 | `database`, `kis_trader.engine.candle_rollup` | `pymysql`, `utils` | +| `kis_trader/backtest/bt_web_jobs.py` | 443 | `kis_trader.utils.kr_trading_day` | - | +| `kis_trader/backtest/dart_backtest_common.py` | 142 | `database`, `kis_trader.backtest`, `kis_trader.engine`, `kis_trader.scan.dart_order_tracker` | - | +| `kis_trader/backtest/dbband_backtest_common.py` | 381 | `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.database.db_manager`, `kis_trader.engine.dbband_engine`, `kis_trader.utils.trade_time` | - | +| `kis_trader/backtest/dbband_param_search.py` | 157 | `database`, `kis_trader.backtest`, `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.engine`, `kis_trader.engine.dbband_env_keys`, `kis_trader.strategies` | `argparse`, `holding_bot` | +| `kis_trader/backtest/env_timeline.py` | 106 | - | `bisect` | +| `kis_trader/backtest/ls_history_loaders.py` | 228 | `kis_trader.utils.logger` | - | +| `kis_trader/backtest/momentum_backtest_common.py` | 659 | `kis_trader.backtest.backtest_env_timeline`, `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.backtest.breakout_tick_loader`, `kis_trader.backtest.momentum_tick_loader`, `kis_trader.backtest.momentum_universe_timeline`, `kis_trader.backtest.universe_history_source`, `kis_trader.backtest.universe_timeline`, `kis_trader.database.db_manager`, `kis_trader.engine.indicator_cache`, `kis_trader.engine.momentum_engine`, `kis_trader.engine.momentum_hts_logic`, `kis_trader.engine.momentum_tick_replay`, `kis_trader.utils.env`, `kis_trader.utils.logger`, `kis_trader.ws.kis_ws` | - | +| `kis_trader/backtest/momentum_exit_ab_test.py` | 100 | `database`, `kis_trader.backtest`, `kis_trader.engine` | - | +| `kis_trader/backtest/momentum_portfolio_backtest.py` | 930 | `kis_trader.backtest.backtest_env_timeline`, `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.backtest.momentum_tick_loader`, `kis_trader.backtest.momentum_universe_timeline`, `kis_trader.backtest.shared_ticks`, `kis_trader.backtest.trigger_snapshot_loader`, `kis_trader.engine.indicator_cache`, `kis_trader.engine.mid_enroll_entry_gate`, `kis_trader.engine.momentum_engine`, `kis_trader.engine.momentum_tick_replay`, `kis_trader.engine.whipsaw_filter`, `kis_trader.strategies.base`, `kis_trader.utils.env`, `kis_trader.utils.session_hm` | - | +| `kis_trader/backtest/momentum_random_benchmark.py` | 565 | `database`, `kis_trader.backtest`, `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.backtest.momentum_portfolio_backtest`, `kis_trader.backtest.param_search_momentum`, `kis_trader.engine.momentum_engine`, `kis_trader.engine.momentum_hts_logic` | `argparse` | +| `kis_trader/backtest/momentum_rr_crossval.py` | 466 | `database`, `kis_trader.backtest`, `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.backtest.param_search_cli_common`, `kis_trader.backtest.param_search_momentum`, `kis_trader.engine`, `kis_trader.utils.env` | `argparse` | +| `kis_trader/backtest/momentum_rr_quick_eval.py` | 113 | `kis_trader.backtest`, `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.backtest.momentum_rr_crossval`, `kis_trader.backtest.param_search_momentum` | - | +| `kis_trader/backtest/momentum_tick_loader.py` | 19 | `kis_trader.backtest.breakout_tick_loader` | - | +| `kis_trader/backtest/momentum_universe_timeline.py` | 125 | `kis_trader.backtest.momentum_backtest_common`, `kis_trader.backtest.universe_history_source`, `kis_trader.backtest.universe_timeline`, `kis_trader.engine.momentum_engine`, `kis_trader.utils.env` | - | +| `kis_trader/backtest/optuna_breakout.py` | 524 | `database`, `kis_trader.backtest`, `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.backtest.breakout_backtest_common`, `kis_trader.backtest.breakout_tick_loader`, `kis_trader.backtest.ls_history_loaders`, `kis_trader.backtest.optuna_breakout_tpe_space`, `kis_trader.backtest.optuna_common`, `kis_trader.backtest.optuna_daily_trail_recommend`, `kis_trader.backtest.optuna_mode_combo`, `kis_trader.backtest.optuna_search_space`, `kis_trader.backtest.param_search_breakout`, `kis_trader.backtest.param_search_cli_common`, `kis_trader.backtest.shared_ticks`, `kis_trader.backtest.tail_param_search`, `kis_trader.backtest.trigger_snapshot_loader`, `kis_trader.backtest.universe_history_source`, `kis_trader.engine`, `kis_trader.engine.indicator_cache`, `kis_trader.share.stock_share`, `kis_trader.strategies.breakout`, `kis_trader.utils.env` | `atexit`, `ctypes`, `optuna` | +| `kis_trader/backtest/optuna_breakout_tpe_space.py` | 96 | `kis_trader.backtest.optuna_tpe_common` | `optuna` | +| `kis_trader/backtest/optuna_briefing.py` | 432 | `database`, `kis_trader.utils.env` | `anthropic`, `argparse`, `google` | +| `kis_trader/backtest/optuna_common.py` | 672 | `database`, `kis_trader.backtest.optuna_briefing`, `kis_trader.utils.env` | `optuna`, `pymysql` | +| `kis_trader/backtest/optuna_daily_trail_recommend.py` | 315 | `kis_trader.backtest.param_search_apply_snapshot`, `kis_trader.utils.env` | - | +| `kis_trader/backtest/optuna_dart.py` | 104 | `database`, `kis_trader.backtest.optuna_common`, `kis_trader.backtest.param_search_dart`, `kis_trader.engine` | `optuna` | +| `kis_trader/backtest/optuna_mode_combo.py` | 278 | `kis_trader.backtest.optuna_daily_trail_recommend`, `kis_trader.backtest.optuna_tpe_common`, `kis_trader.utils.env` | - | +| `kis_trader/backtest/optuna_momentum.py` | 689 | `database`, `kis_trader.backtest`, `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.backtest.momentum_backtest_common`, `kis_trader.backtest.momentum_tick_loader`, `kis_trader.backtest.optuna_common`, `kis_trader.backtest.optuna_daily_trail_recommend`, `kis_trader.backtest.optuna_mode_combo`, `kis_trader.backtest.optuna_momentum_tpe_space`, `kis_trader.backtest.optuna_search_space`, `kis_trader.backtest.param_search_cli_common`, `kis_trader.backtest.param_search_momentum`, `kis_trader.backtest.shared_ticks`, `kis_trader.backtest.tail_param_search`, `kis_trader.backtest.trigger_snapshot_loader`, `kis_trader.backtest.universe_history_source`, `kis_trader.engine`, `kis_trader.engine.indicator_cache`, `kis_trader.engine.momentum_tick_replay`, `kis_trader.engine.us_momentum_env_keys`, `kis_trader.utils.env` | `atexit`, `ctypes`, `optuna` | +| `kis_trader/backtest/optuna_momentum_tpe_space.py` | 159 | `kis_trader.backtest.optuna_tpe_common`, `kis_trader.backtest.param_search_momentum` | `optuna` | +| `kis_trader/backtest/optuna_scalping.py` | 599 | `database`, `kis_trader.backtest`, `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.backtest.breakout_tick_loader`, `kis_trader.backtest.ls_history_loaders`, `kis_trader.backtest.optuna_common`, `kis_trader.backtest.optuna_daily_trail_recommend`, `kis_trader.backtest.optuna_mode_combo`, `kis_trader.backtest.optuna_scalping_tpe_space`, `kis_trader.backtest.optuna_search_space`, `kis_trader.backtest.param_search_cli_common`, `kis_trader.backtest.param_search_scalping`, `kis_trader.backtest.shared_ticks`, `kis_trader.backtest.tail_param_search`, `kis_trader.backtest.trigger_snapshot_loader`, `kis_trader.backtest.universe_history_source`, `kis_trader.engine`, `kis_trader.engine.indicator_cache`, `kis_trader.utils.env` | `atexit`, `ctypes`, `optuna` | +| `kis_trader/backtest/optuna_scalping_tpe_space.py` | 88 | `kis_trader.backtest.optuna_tpe_common` | `optuna` | +| `kis_trader/backtest/optuna_search_space.py` | 89 | `kis_trader.backtest.param_search_breakout`, `kis_trader.backtest.param_search_momentum`, `kis_trader.backtest.param_search_scalping`, `kis_trader.backtest.tail_param_search` | `optuna` | +| `kis_trader/backtest/optuna_tail_tpe_space.py` | 188 | `kis_trader.backtest.optuna_tpe_common` | `optuna` | +| `kis_trader/backtest/optuna_tpe_common.py` | 218 | - | `optuna` | +| `kis_trader/backtest/optuna_web_jobs.py` | 1,121 | `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.backtest.optuna_common`, `kis_trader.backtest.optuna_daily_trail_recommend`, `kis_trader.backtest.param_search_breakout`, `kis_trader.backtest.param_search_momentum`, `kis_trader.backtest.param_search_scalping`, `kis_trader.backtest.tail_param_search`, `kis_trader.backtest.universe_history_source`, `kis_trader.utils.kr_trading_day` | `optuna` | +| `kis_trader/backtest/param_apply_ai.py` | 314 | `database`, `kis_trader.backtest.backtest_portfolio_common` | `google`, `param_search_scalping`, `tail_param_search` | +| `kis_trader/backtest/param_search_apply_snapshot.py` | 648 | `database`, `kis_trader.backtest`, `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.engine.orderbook_env`, `kis_trader.engine.us_momentum_env_keys`, `kis_trader.strategies`, `kis_trader.strategies.breakout` | `argparse`, `holding_bot`, `tail_param_search` | +| `kis_trader/backtest/param_search_breakout.py` | 1,587 | `database`, `kis_trader.backtest`, `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.backtest.breakout_backtest_common`, `kis_trader.backtest.breakout_tick_loader`, `kis_trader.backtest.optuna_common`, `kis_trader.backtest.param_search_apply_snapshot`, `kis_trader.backtest.param_search_cli_common`, `kis_trader.backtest.param_search_dates`, `kis_trader.backtest.param_search_pool`, `kis_trader.backtest.shared_ticks`, `kis_trader.backtest.trigger_snapshot_loader`, `kis_trader.engine`, `kis_trader.engine.indicator_cache`, `kis_trader.share.stock_share`, `kis_trader.strategies.breakout`, `kis_trader.utils.env` | `argparse`, `atexit`, `ctypes`, `heapq` | +| `kis_trader/backtest/param_search_cli_common.py` | 110 | - | `argparse` | +| `kis_trader/backtest/param_search_dart.py` | 95 | `database`, `kis_trader.backtest.dart_backtest_common`, `kis_trader.engine`, `kis_trader.utils.env` | - | +| `kis_trader/backtest/param_search_dates.py` | 94 | `kis_trader.utils.env`, `kis_trader.utils.kr_trading_day` | - | +| `kis_trader/backtest/param_search_momentum.py` | 2,577 | `database`, `kis_trader.backtest`, `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.backtest.momentum_backtest_common`, `kis_trader.backtest.momentum_tick_loader`, `kis_trader.backtest.optuna_common`, `kis_trader.backtest.param_search_apply_snapshot`, `kis_trader.backtest.param_search_cli_common`, `kis_trader.backtest.param_search_dates`, `kis_trader.backtest.param_search_pool`, `kis_trader.backtest.shared_ticks`, `kis_trader.backtest.trigger_snapshot_loader`, `kis_trader.engine`, `kis_trader.engine.indicator_cache`, `kis_trader.engine.momentum_hts_logic`, `kis_trader.engine.momentum_tick_replay`, `kis_trader.engine.us_momentum_env_keys`, `kis_trader.strategies.breakout`, `kis_trader.strategies.us_momentum_stock_cfg`, `kis_trader.utils.env` | `argparse`, `atexit`, `ctypes`, `heapq`, `permanent_subs` | +| `kis_trader/backtest/param_search_optuna.py` | 1,033 | `database`, `kis_trader.backtest`, `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.backtest.optuna_breakout`, `kis_trader.backtest.optuna_common`, `kis_trader.backtest.optuna_daily_trail_recommend`, `kis_trader.backtest.optuna_dart`, `kis_trader.backtest.optuna_mode_combo`, `kis_trader.backtest.optuna_momentum`, `kis_trader.backtest.optuna_scalping`, `kis_trader.backtest.optuna_search_space`, `kis_trader.backtest.optuna_tail_tpe_space`, `kis_trader.backtest.param_search_cli_common`, `kis_trader.backtest.param_search_dates`, `kis_trader.backtest.param_search_pool`, `kis_trader.backtest.shared_ticks`, `kis_trader.backtest.tail_param_search`, `kis_trader.backtest.tail_tick_loader`, `kis_trader.backtest.trigger_snapshot_loader`, `kis_trader.backtest.universe_history_source`, `kis_trader.engine`, `kis_trader.engine.indicator_cache`, `kis_trader.engine.tail_tick_replay`, `kis_trader.utils.env` | `argparse`, `atexit`, `ctypes`, `optuna` | +| `kis_trader/backtest/param_search_pool.py` | 711 | `kis_trader.utils.env` | `atexit`, `ctypes` | +| `kis_trader/backtest/param_search_range_break.py` | 299 | `database`, `kis_trader.backtest`, `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.backtest.param_search_cli_common`, `kis_trader.backtest.param_search_dates`, `kis_trader.backtest.param_search_pool`, `kis_trader.engine.range_break_engine`, `kis_trader.strategies.breakout`, `kis_trader.utils.env` | `argparse`, `heapq` | +| `kis_trader/backtest/param_search_scalping.py` | 1,661 | `database`, `kis_trader.backtest`, `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.backtest.breakout_tick_loader`, `kis_trader.backtest.optuna_common`, `kis_trader.backtest.param_search_apply_snapshot`, `kis_trader.backtest.param_search_cli_common`, `kis_trader.backtest.param_search_dates`, `kis_trader.backtest.param_search_pool`, `kis_trader.backtest.scalping_backtest_common`, `kis_trader.backtest.shared_ticks`, `kis_trader.engine`, `kis_trader.engine.orderbook_env`, `kis_trader.utils.env` | `argparse`, `atexit`, `ctypes`, `heapq`, `param_apply_ai` | +| `kis_trader/backtest/param_search_updow.py` | 409 | `database`, `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.backtest.param_search_apply_snapshot`, `kis_trader.backtest.param_search_dates`, `kis_trader.strategies`, `kis_trader.strategies.updow_holding_cfg` | `argparse`, `holding_bot` | +| `kis_trader/backtest/param_search_updow_us.py` | 284 | `database`, `kis_trader.strategies`, `kis_trader.strategies.updow_holding_cfg`, `kis_trader.utils.kr_trading_day` | `argparse`, `holding_bot` | +| `kis_trader/backtest/param_search_updown_box.py` | 276 | `database`, `kis_trader.backtest.param_search_dates`, `kis_trader.engine`, `kis_trader.strategies.updown_watchlist`, `kis_trader.utils.env` | `argparse`, `holding_bot` | +| `kis_trader/backtest/range_break_backtest_common.py` | 239 | `kis_trader.backtest.backtest_env_timeline`, `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.backtest.breakout_tick_loader`, `kis_trader.backtest.universe_timeline`, `kis_trader.database.db_manager`, `kis_trader.engine.range_break_engine`, `kis_trader.engine.tick_exit_common`, `kis_trader.utils.logger` | - | +| `kis_trader/backtest/range_break_portfolio_backtest.py` | 402 | `kis_trader.backtest.backtest_env_timeline`, `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.engine.range_break_engine`, `kis_trader.engine.scalping_engine`, `kis_trader.engine.tick_exit_common`, `kis_trader.share.stock_share`, `kis_trader.strategies.breakout`, `kis_trader.utils.env` | - | +| `kis_trader/backtest/scalping_backtest_common.py` | 408 | `kis_trader.backtest`, `kis_trader.backtest.backtest_env_timeline`, `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.backtest.breakout_tick_loader`, `kis_trader.backtest.ls_history_loaders`, `kis_trader.backtest.universe_history_source`, `kis_trader.backtest.universe_timeline`, `kis_trader.database.db_manager`, `kis_trader.engine`, `kis_trader.engine.tick_exit_common`, `kis_trader.utils.env`, `kis_trader.utils.logger` | - | +| `kis_trader/backtest/scalping_portfolio_backtest.py` | 506 | `kis_trader.backtest.backtest_env_timeline`, `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.backtest.momentum_portfolio_backtest`, `kis_trader.backtest.trigger_snapshot_loader`, `kis_trader.engine.mid_enroll_entry_gate`, `kis_trader.engine.scalping_engine`, `kis_trader.engine.strategy_eod`, `kis_trader.engine.tail_tick_replay`, `kis_trader.engine.tick_exit_common`, `kis_trader.utils.env` | - | +| `kis_trader/backtest/shared_ticks.py` | 507 | `kis_trader.engine.whipsaw_filter` | `numpy` | +| `kis_trader/backtest/tail_backtest_common.py` | 840 | `kis_trader.backtest.backtest_env_timeline`, `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.backtest.momentum_backtest_common`, `kis_trader.backtest.tail_tick_loader`, `kis_trader.backtest.universe_history_source`, `kis_trader.backtest.universe_timeline`, `kis_trader.database.db_manager`, `kis_trader.engine.candle_rollup`, `kis_trader.engine.indicator_cache`, `kis_trader.engine.tail_engine`, `kis_trader.engine.tail_tick_replay`, `kis_trader.utils.env`, `kis_trader.utils.logger`, `kis_trader.utils.trade_time`, `kis_trader.ws.kis_ws` | - | +| `kis_trader/backtest/tail_bt_web_jobs.py` | 14 | `kis_trader.backtest.bt_web_jobs` | - | +| `kis_trader/backtest/tail_exit_ab_test.py` | 122 | `database`, `kis_trader.backtest`, `kis_trader.engine` | - | +| `kis_trader/backtest/tail_mfe_analysis.py` | 305 | `database`, `kis_trader.backtest`, `kis_trader.engine`, `kis_trader.utils.trade_time` | `argparse` | +| `kis_trader/backtest/tail_param_search.py` | 1,948 | `database`, `kis_trader.backtest`, `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.backtest.optuna_common`, `kis_trader.backtest.param_search_cli_common`, `kis_trader.backtest.param_search_dates`, `kis_trader.backtest.param_search_pool`, `kis_trader.backtest.shared_ticks`, `kis_trader.backtest.tail_tick_loader`, `kis_trader.backtest.trigger_snapshot_loader`, `kis_trader.engine`, `kis_trader.engine.indicator_cache`, `kis_trader.engine.orderbook_env`, `kis_trader.engine.tail_env_keys`, `kis_trader.engine.tail_tick_replay`, `kis_trader.utils.env` | `argparse`, `atexit`, `ctypes`, `heapq`, `param_apply_ai` | +| `kis_trader/backtest/tail_tick_loader.py` | 18 | `kis_trader.backtest.breakout_tick_loader` | - | +| `kis_trader/backtest/trigger_snapshot_loader.py` | 564 | `kis_trader.engine.orderbook_filter`, `kis_trader.engine.program_filter`, `kis_trader.utils.env`, `kis_trader.ws.orderbook_cache`, `kis_trader.ws.program_cache`, `kis_trader.ws.trigger_eval_recorder` | - | +| `kis_trader/backtest/universe_history_source.py` | 81 | `kis_trader.utils.env` | - | +| `kis_trader/backtest/universe_timeline.py` | 271 | `kis_trader.backtest.universe_history_source`, `kis_trader.database.db_manager`, `kis_trader.utils.env` | `bisect` | +| `kis_trader/backtest/us_momentum_web_backtest.py` | 400 | `kis_trader.backtest`, `kis_trader.engine.us_momentum_env_keys`, `kis_trader.strategies.us_momentum_stock_cfg`, `kis_trader.utils.env`, `kis_trader.utils.usd_krw_fx` | `permanent_subs` | +| `kis_trader/database/__init__.py` | 4 | - | `db_manager` | +| `kis_trader/database/db_manager.py` | 857 | `database`, `kis_trader.backtest.momentum_universe_timeline`, `kis_trader.backtest.universe_history_source`, `kis_trader.utils.stock_name` | - | +| `kis_trader/database/paper_store.py` | 500 | `kis_trader.utils.strategy_ids` | - | +| `kis_trader/engine/__init__.py` | 5 | - | - | +| `kis_trader/engine/atr_series.py` | 37 | - | - | +| `kis_trader/engine/candle_rollup.py` | 158 | - | - | +| `kis_trader/engine/daily_profit_halt.py` | 811 | `kis_trader.utils.env`, `kis_trader.utils.logger` | - | +| `kis_trader/engine/dart_engine.py` | 380 | `database`, `kis_trader.engine.scalping_engine`, `kis_trader.utils.env` | - | +| `kis_trader/engine/dbband_engine.py` | 972 | `database`, `kis_trader.backtest.backtest_env_timeline`, `kis_trader.engine.dbband_env_keys`, `kis_trader.engine.tick_exit_common`, `kis_trader.utils.env`, `kis_trader.utils.trade_time` | - | +| `kis_trader/engine/dbband_env_keys.py` | 212 | - | - | +| `kis_trader/engine/ema_trend_filter.py` | 84 | - | - | +| `kis_trader/engine/indicator_cache.py` | 412 | `kis_trader.engine.ema_trend_filter`, `kis_trader.engine.momentum_engine`, `kis_trader.utils.env` | - | +| `kis_trader/engine/limit_entry_common.py` | 196 | - | `utils` | +| `kis_trader/engine/ls_feed_gate.py` | 20 | `kis_trader.utils.env`, `kis_trader.ws.ls_ws` | - | +| `kis_trader/engine/mid_enroll_entry_gate.py` | 241 | `kis_trader.utils.env` | - | +| `kis_trader/engine/momentum_chase_patterns.py` | 228 | `kis_trader.utils.env` | - | +| `kis_trader/engine/momentum_engine.py` | 673 | `database`, `kis_trader.backtest.momentum_portfolio_backtest`, `kis_trader.engine.momentum_env_keys`, `kis_trader.engine.momentum_hts_logic`, `kis_trader.engine.momentum_tick_replay`, `kis_trader.engine.strategy_eod`, `kis_trader.utils.env`, `kis_trader.utils.session_hm`, `kis_trader.utils.trade_time` | - | +| `kis_trader/engine/momentum_env_keys.py` | 249 | - | - | +| `kis_trader/engine/momentum_hts_logic.py` | 587 | `kis_trader.engine.momentum_chase_patterns`, `kis_trader.engine.momentum_env_keys`, `kis_trader.engine.orderbook_filter`, `kis_trader.engine.program_filter`, `kis_trader.engine.whipsaw_filter`, `kis_trader.utils.env`, `kis_trader.utils.session_hm`, `kis_trader.utils.trade_time` | - | +| `kis_trader/engine/momentum_tick_replay.py` | 620 | `kis_trader.backtest.shared_ticks`, `kis_trader.engine.momentum_engine`, `kis_trader.engine.whipsaw_filter`, `kis_trader.utils.env`, `kis_trader.utils.trade_time` | - | +| `kis_trader/engine/orderbook_env.py` | 187 | `kis_trader.utils.env` | - | +| `kis_trader/engine/orderbook_filter.py` | 245 | `kis_trader.utils.env`, `kis_trader.ws.orderbook_cache`, `kis_trader.ws.trigger_eval_recorder` | `orderbook_env`, `trigger_eval_collect` | +| `kis_trader/engine/post_sell_candle_backfill.py` | 439 | `database`, `kis_trader.engine.candle_rollup`, `kis_trader.utils.env`, `kis_trader.utils.logger`, `kis_trader.ws.kis_ws` | - | +| `kis_trader/engine/program_filter.py` | 229 | `kis_trader.utils.env`, `kis_trader.ws.program_cache`, `kis_trader.ws.trigger_eval_recorder` | `trigger_eval_collect` | +| `kis_trader/engine/range_break_engine.py` | 460 | `database`, `kis_trader.backtest.range_break_portfolio_backtest`, `kis_trader.engine.range_break_env_keys`, `kis_trader.strategies.breakout`, `kis_trader.utils.trade_time` | - | +| `kis_trader/engine/range_break_env_keys.py` | 88 | - | - | +| `kis_trader/engine/scalping_engine.py` | 1,346 | `database`, `kis_trader.backtest.scalping_portfolio_backtest`, `kis_trader.engine.momentum_engine`, `kis_trader.engine.orderbook_filter`, `kis_trader.engine.strategy_eod`, `kis_trader.engine.tick_exit_common`, `kis_trader.utils.env`, `kis_trader.utils.trade_time` | - | +| `kis_trader/engine/strategy_eod.py` | 149 | - | - | +| `kis_trader/engine/tail_engine.py` | 2,788 | `database`, `kis_trader.backtest.backtest_env_timeline`, `kis_trader.backtest.trigger_snapshot_loader`, `kis_trader.engine.atr_series`, `kis_trader.engine.candle_rollup`, `kis_trader.engine.indicator_cache`, `kis_trader.engine.limit_entry_common`, `kis_trader.engine.mid_enroll_entry_gate`, `kis_trader.engine.orderbook_filter`, `kis_trader.engine.program_filter`, `kis_trader.engine.strategy_eod`, `kis_trader.engine.tail_env_keys`, `kis_trader.engine.tail_tick_replay`, `kis_trader.engine.tick_exit_common`, `kis_trader.engine.whipsaw_filter`, `kis_trader.utils.env`, `kis_trader.utils.position_sizing`, `kis_trader.utils.trade_time` | - | +| `kis_trader/engine/tail_env_keys.py` | 471 | `database`, `kis_trader.utils.env` | - | +| `kis_trader/engine/tail_tick_replay.py` | 303 | `kis_trader.backtest.shared_ticks`, `kis_trader.engine.candle_rollup`, `kis_trader.engine.limit_entry_common`, `kis_trader.utils.env` | - | +| `kis_trader/engine/tick_exit_common.py` | 384 | `kis_trader.backtest.shared_ticks`, `kis_trader.engine.scalping_engine`, `kis_trader.engine.whipsaw_filter`, `kis_trader.utils.env`, `kis_trader.utils.trade_time` | - | +| `kis_trader/engine/trigger_eval_collect.py` | 76 | `kis_trader.utils.env` | - | +| `kis_trader/engine/updown_box.py` | 958 | - | `scalping_engine`, `strategies`, `utils` | +| `kis_trader/engine/us_momentum_env.py` | 193 | `kis_trader.utils.env` | - | +| `kis_trader/engine/us_momentum_env_keys.py` | 296 | `kis_trader.utils.env` | - | +| `kis_trader/engine/whipsaw_filter.py` | 570 | `kis_trader.backtest.shared_ticks`, `kis_trader.utils.env` | - | +| `kis_trader/execution/__init__.py` | 5 | - | `kis_client`, `order_manager` | +| `kis_trader/execution/account_cash.py` | 174 | - | `utils` | +| `kis_trader/execution/kis_client.py` | 1,553 | `kis_trader.utils.api_reject_log`, `kis_trader.utils.non_stock` | `kis_token_manager`, `pandas`, `utils` | +| `kis_trader/execution/order_manager.py` | 2,381 | `kis_trader.engine.post_sell_candle_backfill`, `kis_trader.engine.us_momentum_env_keys`, `kis_trader.utils.api_reject_log` | `account_cash`, `database`, `kis_client`, `orderbook_sell`, `utils` | +| `kis_trader/execution/orderbook_sell.py` | 131 | - | `utils` | +| `kis_trader/execution/orphan_reconcile.py` | 571 | - | `engine`, `utils` | +| `kis_trader/main.py` | 2,353 | - | `database`, `engine`, `execution`, `network`, `permanent_subs`, `strategies`, `utils`, `ws` | +| `kis_trader/network/__init__.py` | 4 | - | `ws_manager` | +| `kis_trader/network/condition_manager.py` | 446 | - | `utils` | +| `kis_trader/network/kiwoom_condition_manager.py` | 790 | - | `condition_manager`, `utils`, `websocket`, `ws` | +| `kis_trader/network/ls_chart.py` | 239 | `database` | `pandas`, `utils`, `ws` | +| `kis_trader/network/ls_condition_manager.py` | 821 | `kis_trader.network.ls_token` | `condition_manager`, `utils` | +| `kis_trader/network/ls_token.py` | 231 | `kis_trader.utils.env` | `requests` | +| `kis_trader/network/ls_ws_validator.py` | 168 | - | `utils` | +| `kis_trader/network/market_guard.py` | 572 | - | `utils` | +| `kis_trader/network/ranking_manager.py` | 350 | - | `utils` | +| `kis_trader/network/ws_manager.py` | 1,849 | `kis_trader.share.stock_share` | `permanent_subs`, `utils`, `ws` | +| `kis_trader/network/ws_validator.py` | 227 | - | `utils` | +| `kis_trader/scan/__init__.py` | 1 | - | - | +| `kis_trader/scan/dart_order_tracker.py` | 642 | `database`, `kis_trader.database.db_manager`, `kis_trader.scan.dart_watchlist`, `kis_trader.utils.env`, `kis_trader.utils.logger`, `kis_trader.utils.request_handler` | `argparse`, `zipfile` | +| `kis_trader/scan/dart_watchlist.py` | 168 | `kis_trader.utils.env`, `kis_trader.utils.logger` | - | +| `kis_trader/scripts/__init__.py` | 5 | - | - | +| `kis_trader/scripts/apply_unified_risk_env.py` | 81 | `database` | - | +| `kis_trader/scripts/approx_param_search_from_log.py` | 131 | `kis_trader.database.db_manager` | `argparse` | +| `kis_trader/scripts/backfill_trigger_eval_from_log.py` | 318 | `kis_trader.database.db_manager` | `argparse` | +| `kis_trader/scripts/compare_momentum_scan_memo.py` | 233 | `kis_trader.backtest`, `kis_trader.backtest.momentum_backtest_common`, `kis_trader.backtest.param_search_momentum` | `argparse` | +| `kis_trader/scripts/fill_kiwoom_candles.py` | 405 | `database`, `kis_trader.utils.env`, `kis_trader.ws.kis_ws` | `argparse` | +| `kis_trader/scripts/fill_stock_share_meta.py` | 190 | `database`, `kis_trader.share.stock_share`, `kis_trader.utils.env`, `kis_trader.ws.kis_ws` | `argparse` | +| `kis_trader/scripts/migrate_split_env_config.py` | 105 | `database` | `config_schema` | +| `kis_trader/scripts/prune_env_config_strategy_columns.py` | 188 | `database`, `kis_trader.utils.env` | `argparse`, `config_schema` | +| `kis_trader/scripts/rebuild_universe_history_from_logs.py` | 418 | `database`, `kis_trader.database.db_manager` | `argparse` | +| `kis_trader/scripts/show_account_snapshot.py` | 323 | `kis_trader.execution.kis_client`, `kis_trader.utils.env` | `argparse` | +| `kis_trader/scripts/test_kis_ws_diagnostic.py` | 403 | `kis_trader.execution.kis_client`, `kis_trader.utils.env` | `argparse`, `requests`, `websocket` | +| `kis_trader/scripts/verify_three_paths.py` | 231 | `kis_trader.backtest.param_search_scalping`, `kis_trader.engine.momentum_engine`, `kis_trader.engine.scalping_engine`, `kis_trader.engine.tail_engine`, `kis_trader.strategies.breakout`, `kis_trader.utils.env` | `backtest_web`, `scalping_engine` | +| `kis_trader/share/__init__.py` | 1 | - | - | +| `kis_trader/share/stock_share.py` | 210 | `kis_trader.utils.env`, `kis_trader.utils.logger` | - | +| `kis_trader/strategies/__init__.py` | 30 | - | `base`, `breakout`, `dart_strategy`, `dbband_strategy`, `momentum`, `range_break`, `scalping`, `tail_catch`, `updow_strategy`, `us_momentum` | +| `kis_trader/strategies/base.py` | 1,126 | `kis_trader.engine.ls_feed_gate`, `kis_trader.engine.mid_enroll_entry_gate` | `backtest`, `database`, `engine`, `execution`, `network`, `utils` | +| `kis_trader/strategies/breakout.py` | 2,047 | `kis_trader.backtest.breakout_portfolio_backtest`, `kis_trader.backtest.shared_ticks`, `kis_trader.engine.tail_tick_replay`, `kis_trader.utils.env`, `kis_trader.utils.trade_time` | `base`, `engine`, `share`, `utils` | +| `kis_trader/strategies/dart_strategy.py` | 248 | - | `base`, `engine`, `scan`, `utils` | +| `kis_trader/strategies/dbband_stock_cfg.py` | 343 | - | `engine`, `holding_bot`, `utils` | +| `kis_trader/strategies/dbband_strategy.py` | 228 | - | `base`, `dbband_stock_cfg`, `engine`, `utils` | +| `kis_trader/strategies/momentum.py` | 360 | `kis_trader.engine.tail_tick_replay` | `base`, `engine`, `utils` | +| `kis_trader/strategies/range_break.py` | 303 | - | `base`, `engine`, `utils` | +| `kis_trader/strategies/scalping.py` | 434 | `kis_trader.engine.tail_tick_replay` | `base`, `engine`, `utils` | +| `kis_trader/strategies/tail_catch.py` | 503 | `kis_trader.engine.tail_tick_replay` | `base`, `engine`, `execution`, `utils` | +| `kis_trader/strategies/updow_buy.py` | 1,383 | `kis_trader.engine.scalping_engine` | `bisect`, `engine`, `utils` | +| `kis_trader/strategies/updow_holding_cfg.py` | 590 | - | `engine`, `holding_bot`, `updow_buy`, `utils` | +| `kis_trader/strategies/updow_strategy.py` | 769 | - | `base`, `engine`, `execution`, `updow_buy`, `updow_holding_cfg`, `updown_box_stock_cfg`, `updown_scan`, `updown_watchlist`, `utils` | +| `kis_trader/strategies/updown_box_stock_cfg.py` | 221 | - | - | +| `kis_trader/strategies/updown_feed.py` | 198 | - | `utils` | +| `kis_trader/strategies/updown_scan.py` | 154 | - | `engine`, `updown_watchlist`, `utils` | +| `kis_trader/strategies/updown_watchlist.py` | 419 | - | `utils` | +| `kis_trader/strategies/us_momentum.py` | 1,123 | `database`, `kis_trader.engine.tail_tick_replay` | `base`, `engine`, `execution`, `momentum`, `permanent_subs`, `us_momentum_stock_cfg`, `utils` | +| `kis_trader/strategies/us_momentum_stock_cfg.py` | 378 | - | `permanent_subs` | +| `kis_trader/utils/__init__.py` | 1 | - | - | +| `kis_trader/utils/api_reject_log.py` | 242 | `kis_trader.utils.env` | - | +| `kis_trader/utils/env.py` | 159 | `database` | - | +| `kis_trader/utils/kis_ws_session_windows.py` | 149 | - | `env`, `session_hm` | +| `kis_trader/utils/ls_ws_session_windows.py` | — | - | `env`, `session_hm` | +| `kis_trader/utils/kr_trading_day.py` | 232 | `kis_trader.utils.env` | - | +| `kis_trader/utils/live_portfolio_common.py` | 328 | - | `backtest`, `utils` | +| `kis_trader/utils/logger.py` | 219 | - | `env`, `requests` | +| `kis_trader/utils/non_stock.py` | 157 | - | `env` | +| `kis_trader/utils/position_sizing.py` | 41 | - | `utils` | +| `kis_trader/utils/request_handler.py` | 208 | - | `logger`, `requests` | +| `kis_trader/utils/session_hm.py` | 26 | - | - | +| `kis_trader/utils/stock_name.py` | 97 | - | - | +| `kis_trader/utils/strategy_ids.py` | 124 | - | - | +| `kis_trader/utils/trade_time.py` | 32 | - | - | +| `kis_trader/utils/universe_source.py` | 59 | - | `env` | +| `kis_trader/utils/usd_krw_fx.py` | 410 | `kis_trader.utils.env`, `kis_trader.utils.request_handler` | - | +| `kis_trader/web/live_config_schema.py` | 915 | `kis_trader.engine.orderbook_env` | - | +| `kis_trader/ws/__init__.py` | 22 | - | `kis_ws`, `kiwoom_ws`, `tick_recorder` | +| `kis_trader/ws/kis_ws.py` | 2,656 | `kis_trader.engine.candle_rollup`, `kis_trader.utils.env`, `kis_trader.utils.kis_ws_session_windows` | `kis_approval_manager`, `kis_long_ver1`, `pandas`, `requests`, `websocket` | +| `kis_trader/ws/kis_ws_overseas.py` | 591 | `kis_trader.utils.env`, `kis_trader.utils.kis_ws_session_windows` | `kis_approval_manager`, `requests`, `websocket` | +| `kis_trader/ws/kiwoom_ws.py` | 938 | `kis_trader.utils.env` | `kis_long_ver1`, `kis_ws`, `orderbook_cache`, `program_cache`, `websocket` | +| `kis_trader/ws/ls_ws.py` | — | `database`, `kis_trader.network.ls_chart`, `kis_trader.network.ls_token`, `kis_trader.utils.env`, `kis_trader.utils.ls_ws_session_windows`, `kis_trader.ws.orderbook_cache` | `requests`, `websocket` | +| `kis_trader/ws/orderbook_cache.py` | 287 | - | - | +| `kis_trader/ws/program_cache.py` | 157 | - | - | +| `kis_trader/ws/tick_recorder.py` | 287 | - | `utils` | +| `kis_trader/ws/trigger_eval_recorder.py` | 270 | - | `orderbook_cache`, `program_cache`, `utils` | +| `kis_trader/ws/trigger_snapshot_recorder.py` | 268 | - | `orderbook_cache`, `program_cache`, `utils` | +| `kis_ws.py` | 3 | `kis_trader.ws.kis_ws` | - | +| `kiwoom_rest_api/__init__.py` | 0 | - | - | +| `kiwoom_rest_api/api.py` | 0 | - | - | +| `kiwoom_rest_api/api_async.py` | 0 | - | - | +| `kiwoom_rest_api/auth/__init__.py` | 0 | - | - | +| `kiwoom_rest_api/auth/token.py` | 109 | - | `kiwoom_rest_api` | +| `kiwoom_rest_api/cli/__init__.py` | 0 | - | - | +| `kiwoom_rest_api/cli/main.py` | 88 | - | `kiwoom_rest_api`, `rich`, `typer` | +| `kiwoom_rest_api/config.py` | 60 | - | - | +| `kiwoom_rest_api/core/__init__.py` | 0 | - | - | +| `kiwoom_rest_api/core/async_client.py` | 49 | - | `httpx`, `kiwoom_rest_api` | +| `kiwoom_rest_api/core/base.py` | 224 | - | `httpx`, `kiwoom_rest_api` | +| `kiwoom_rest_api/core/base_api.py` | 56 | - | `kiwoom_rest_api` | +| `kiwoom_rest_api/core/sync_client.py` | 43 | - | `httpx`, `kiwoom_rest_api` | +| `kiwoom_rest_api/koreanstock/__init__.py` | 0 | - | - | +| `kiwoom_rest_api/koreanstock/account.py` | 2,050 | - | `kiwoom_rest_api` | +| `kiwoom_rest_api/koreanstock/analysis.py` | 108 | - | `kiwoom_rest_api` | +| `kiwoom_rest_api/koreanstock/chart.py` | 739 | - | `kiwoom_rest_api` | +| `kiwoom_rest_api/koreanstock/credit_order.py` | 291 | - | `kiwoom_rest_api` | +| `kiwoom_rest_api/koreanstock/elw.py` | 1,113 | - | `kiwoom_rest_api` | +| `kiwoom_rest_api/koreanstock/etf.py` | 621 | - | `kiwoom_rest_api` | +| `kiwoom_rest_api/koreanstock/foreign_institution.py` | 234 | - | `kiwoom_rest_api` | +| `kiwoom_rest_api/koreanstock/investor.py` | 115 | - | `kiwoom_rest_api` | +| `kiwoom_rest_api/koreanstock/market_condition.py` | 1,111 | - | `kiwoom_rest_api` | +| `kiwoom_rest_api/koreanstock/order.py` | 317 | - | `kiwoom_rest_api` | +| `kiwoom_rest_api/koreanstock/rank_info.py` | 2,311 | - | `kiwoom_rest_api` | +| `kiwoom_rest_api/koreanstock/sector.py` | 507 | - | `kiwoom_rest_api` | +| `kiwoom_rest_api/koreanstock/slb.py` | 287 | - | `kiwoom_rest_api` | +| `kiwoom_rest_api/koreanstock/stockinfo.py` | 1,608 | - | `kiwoom_rest_api` | +| `kiwoom_rest_api/koreanstock/theme.py` | 175 | - | `kiwoom_rest_api` | +| `kiwoom_rest_api/koreanstock/trading.py` | 107 | - | `kiwoom_rest_api` | +| `kiwoom_rest_api/trader.py` | 325 | - | `numpy`, `pandas` | +| `kiwoom_rest_api/websocket.py` | 321 | - | `config`, `websockets` | +| `kiwoom_rest_api/websocket_constants.py` | 186 | - | - | +| `kiwoom_rest_api/websocket_helper.py` | 283 | - | `websocket`, `websocket_constants` | +| `kiwoom_trader_dual.py` | 1,131 | - | `dotenv`, `kiwoom_rest_api`, `numpy`, `pandas`, `requests` | +| `kiwoom_trader_ver2.py` | 3,179 | `database` | `dotenv`, `export_sniper`, `kiwoom_rest_api`, `ml_predictor`, `news_analyzer`, `numpy`, `pandas`, `requests`, `risk_manager`, `smart_executor`, `trend_divergence` | +| `kiwoom_universe_scanner.py` | 684 | `database` | `dotenv`, `kiwoom_rest_api`, `requests` | +| `kiwoom_ws.py` | 3 | `kis_trader.ws.kiwoom_ws` | - | +| `kiwwom_trader/config.py` | 43 | - | `dotenv` | +| `kiwwom_trader/run.py` | 23 | - | `config`, `scalping_bot` | +| `kiwwom_trader/scalping_bot.py` | 261 | `database` | `config`, `kiwoom_rest_api`, `scalping_logic`, `ws_candle_agg` | +| `kiwwom_trader/scalping_logic.py` | 45 | `kis_trader.engine` | - | +| `kiwwom_trader/ws_candle_agg.py` | 122 | - | `kiwoom_rest_api` | +| `ls_sample/sample_python/balance_window.py` | 135 | - | `PySide2`, `pythoncom`, `tr`, `win32com` | +| `ls_sample/sample_python/button_holder.py` | 15 | - | - | +| `ls_sample/sample_python/main.py` | 303 | - | `PySide2`, `apscheduler`, `balance_window`, `dotenv`, `main_window`, `pythoncom`, `tr`, `win32com` | +| `ls_sample/sample_python/main_window.py` | 272 | - | `PySide2`, `dotenv`, `order_window`, `pythoncom`, `search_window`, `tr`, `win32com` | +| `ls_sample/sample_python/order_window.py` | 351 | - | `PySide2`, `pythoncom`, `tr`, `win32com` | +| `ls_sample/sample_python/rockwidget.py` | 22 | - | - | +| `ls_sample/sample_python/search_window.py` | 130 | - | `PySide2`, `pythoncom`, `tr`, `ui`, `win32com` | +| `ls_sample/sample_python/second_window.py` | 31 | - | - | +| `ls_sample/sample_python/slider.py` | 17 | - | - | +| `ls_sample/sample_python/test.py` | 100 | - | - | +| `ls_sample/sample_python/third_window.py` | 31 | - | - | +| `ls_sample/sample_python/tr/k3_real.py` | 63 | - | - | +| `ls_sample/sample_python/tr/s3_real.py` | 63 | - | - | +| `ls_sample/sample_python/tr/tr_0424.py` | 813 | - | `apscheduler`, `dotenv`, `pythoncom`, `tr`, `win32com` | +| `ls_sample/sample_python/tr/tr_1857.py` | 245 | - | - | +| `ls_sample/sample_python/tr/tr_cancel_order.py` | 118 | - | `dotenv` | +| `ls_sample/sample_python/tr/tr_kh.py` | 72 | - | - | +| `ls_sample/sample_python/tr/tr_master.py` | 67 | - | - | +| `ls_sample/sample_python/tr/tr_order.py` | 197 | - | `PySide2`, `balance_window`, `dotenv`, `pythoncom`, `tr`, `win32com` | +| `ls_sample/sample_python/tr/tr_pending.py` | 326 | - | `PySide2`, `balance_window`, `dotenv`, `pythoncom`, `tr`, `win32com` | +| `ls_sample/sample_python/tr/tr_redo_order.py` | 115 | - | `dotenv` | +| `ls_sample/sample_python/tr/tr_sc0.py` | 108 | - | - | +| `ls_sample/sample_python/tr/tr_sc1.py` | 97 | - | - | +| `ls_sample/sample_python/tr/tr_sc2.py` | 82 | - | - | +| `ls_sample/sample_python/tr/tr_sc3.py` | 88 | - | - | +| `ls_sample/sample_python/ui/ui_search_window.py` | 67 | - | `PySide2` | +| `ls_sample/sample_python/ui/ui_second_window.py` | 35 | - | `PySide2` | +| `ls_sample/sample_python/ui/ui_test.py` | 65 | - | - | +| `ls_sample/sample_python/ui/ui_third_window.py` | 34 | - | - | +| `mcp/kiwoom-docs-mcp/server.py` | 448 | - | `fastmcp` | +| `mcp/ls-docs-mcp/fetch_spec.py` | 269 | - | `argparse` | +| `mcp/ls-docs-mcp/server.py` | 417 | - | `fastmcp` | +| `ml_predictor.py` | 230 | `database` | `numpy`, `pandas`, `sklearn` | +| `mm_butler.py` | 1,235 | `database`, `kis_trader.utils.env` | `anthropic`, `google`, `kis_long_ver2`, `news_analyzer`, `requests` | +| `mm_remote.py` | 290 | `database` | `kis_long_ver1`, `requests` | +| `modify_db_schema.py` | 37 | - | `sqlite3` | +| `momentum_engine.py` | 3 | `kis_trader.engine.momentum_engine` | - | +| `news_analyzer.py` | 215 | - | `anthropic`, `bs4`, `requests` | +| `permanent_subs.py` | 319 | `kis_trader.utils.env` | - | +| `remove/legacy_standalone_bots/ai_recommend_notice.py` | 337 | `database` | `google`, `requests` | +| `remove/legacy_standalone_bots/back_test.py` | 164 | - | `sqlite3` | +| `remove/legacy_standalone_bots/kis_scalping_ver1.py` | 1,658 | `database` | `google`, `kis_token_manager`, `kis_ws`, `ml_predictor`, `pandas`, `requests`, `risk_manager` | +| `remove/legacy_standalone_bots/kis_scalping_ver2.py` | 1,596 | `database` | `google`, `kis_token_manager`, `kis_ws`, `ml_predictor`, `pandas`, `requests`, `risk_manager`, `scalping_engine` | +| `remove/legacy_standalone_bots/kis_short_ver1.py` | 3,826 | `database` | `google`, `hmac`, `ml_predictor`, `mm_remote`, `pandas`, `requests`, `risk_manager` | +| `remove/legacy_standalone_bots/kis_short_ver2.py` | 3,885 | `database` | `google`, `hmac`, `kis_token_manager`, `kis_ws`, `ml_predictor`, `mm_remote`, `pandas`, `requests`, `risk_manager` | +| `remove/legacy_standalone_bots/kis_short_ver3.py` | 3,610 | `database` | `google`, `hmac`, `kis_token_manager`, `kis_ws`, `ml_predictor`, `mm_remote`, `pandas`, `requests`, `risk_manager`, `tail_engine` | +| `remove/legacy_standalone_bots/update_env_short.py` | 140 | `database` | - | +| `remove/legacy_standalone_bots/updowbuyy.py` | 36 | `kis_trader.strategies.updow_buy` | - | +| `risk_manager.py` | 307 | - | `numpy`, `pandas` | +| `sample_python/balance_window.py` | 135 | - | `PySide2`, `pythoncom`, `tr`, `win32com` | +| `sample_python/button_holder.py` | 15 | - | - | +| `sample_python/main.py` | 303 | - | `PySide2`, `apscheduler`, `balance_window`, `dotenv`, `main_window`, `pythoncom`, `tr`, `win32com` | +| `sample_python/main_window.py` | 272 | - | `PySide2`, `dotenv`, `order_window`, `pythoncom`, `search_window`, `tr`, `win32com` | +| `sample_python/order_window.py` | 351 | - | `PySide2`, `pythoncom`, `tr`, `win32com` | +| `sample_python/rockwidget.py` | 22 | - | - | +| `sample_python/search_window.py` | 130 | - | `PySide2`, `pythoncom`, `tr`, `ui`, `win32com` | +| `sample_python/second_window.py` | 31 | - | - | +| `sample_python/slider.py` | 17 | - | - | +| `sample_python/test.py` | 100 | - | - | +| `sample_python/third_window.py` | 31 | - | - | +| `sample_python/tr/k3_real.py` | 63 | - | - | +| `sample_python/tr/s3_real.py` | 63 | - | - | +| `sample_python/tr/tr_0424.py` | 813 | - | `apscheduler`, `dotenv`, `pythoncom`, `tr`, `win32com` | +| `sample_python/tr/tr_1857.py` | 245 | - | - | +| `sample_python/tr/tr_cancel_order.py` | 118 | - | `dotenv` | +| `sample_python/tr/tr_kh.py` | 72 | - | - | +| `sample_python/tr/tr_master.py` | 67 | - | - | +| `sample_python/tr/tr_order.py` | 197 | - | `PySide2`, `balance_window`, `dotenv`, `pythoncom`, `tr`, `win32com` | +| `sample_python/tr/tr_pending.py` | 326 | - | `PySide2`, `balance_window`, `dotenv`, `pythoncom`, `tr`, `win32com` | +| `sample_python/tr/tr_redo_order.py` | 115 | - | `dotenv` | +| `sample_python/tr/tr_sc0.py` | 108 | - | - | +| `sample_python/tr/tr_sc1.py` | 97 | - | - | +| `sample_python/tr/tr_sc2.py` | 82 | - | - | +| `sample_python/tr/tr_sc3.py` | 88 | - | - | +| `sample_python/ui/ui_search_window.py` | 67 | - | `PySide2` | +| `sample_python/ui/ui_second_window.py` | 35 | - | `PySide2` | +| `sample_python/ui/ui_test.py` | 65 | - | - | +| `sample_python/ui/ui_third_window.py` | 34 | - | - | +| `scalping_engine.py` | 3 | `kis_trader.engine.scalping_engine` | - | +| `scripts/_diag_mom_2min_gap.py` | 124 | `database` | - | +| `scripts/_diag_mom_tick_today.py` | 99 | `database` | - | +| `scripts/_diag_scalp_live_bt_20260716.py` | 317 | `database`, `kis_trader.backtest.scalping_backtest_common`, `kis_trader.engine.scalping_engine` | - | +| `scripts/append_tail_optuna_compare.py` | 180 | `database`, `kis_trader.backtest`, `kis_trader.engine` | `argparse` | +| `scripts/backfill_trade_candles.py` | 70 | `database`, `kis_trader.engine.post_sell_candle_backfill` | `argparse` | +| `scripts/collect_ls_condition_history.py` | 654 | `database`, `kis_trader.utils.env` | `argparse`, `websocket` | +| `scripts/collect_ls_universe_history.py` | 401 | `database` | `argparse` | +| `scripts/copy_env_row_to_latest.py` | 0 | - | - | +| `scripts/kis_error_watch_mm.py` | 387 | `kis_trader.utils.env`, `kis_trader.utils.logger` | `argparse`, `select` | +| `scripts/market_open_sim_smoke.py` | 362 | `kis_trader.backtest`, `kis_trader.engine`, `kis_trader.execution.kis_client`, `kis_trader.network.market_guard`, `kis_trader.scripts`, `kis_trader.strategies`, `kis_trader.strategies.base`, `kis_trader.utils.env`, `kis_trader.utils.kr_trading_day` | `backtest_web`, `unittest` | +| `scripts/momentum_ratchet_ab_715.py` | 191 | `kis_trader.backtest.optuna_common`, `kis_trader.backtest.optuna_momentum`, `kis_trader.backtest.param_search_momentum`, `kis_trader.utils.env` | - | +| `scripts/post_optuna_mid_enroll_verify.py` | 214 | `database`, `kis_trader.backtest`, `kis_trader.backtest.us_momentum_web_backtest`, `kis_trader.engine.momentum_engine`, `kis_trader.utils.env` | `argparse` | +| `scripts/query_kiwoom_condition_snapshot.py` | 454 | `database`, `kis_trader.utils.env`, `kis_trader.ws.kis_ws` | `argparse`, `websocket` | +| `scripts/run_strategy_backtest_cli.py` | 256 | `kis_trader.utils.kr_trading_day` | `argparse`, `backtest_web` | +| `scripts/run_tail_backtest_cli.py` | 376 | `database`, `kis_trader.backtest`, `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.engine`, `kis_trader.engine.tail_tick_replay`, `kis_trader.utils.kr_trading_day`, `kis_trader.utils.stock_name` | `argparse` | +| `scripts/smoke_candle_upsert_rollup.py` | 144 | `kis_trader.engine.candle_rollup`, `kis_trader.ws.kis_ws` | - | +| `scripts/tail_live_bt_forensics.py` | 158 | `database`, `kis_trader.backtest`, `kis_trader.backtest.universe_timeline`, `kis_trader.engine`, `kis_trader.engine.tail_engine` | `argparse` | +| `scripts/tail_symbol_gate_verify_20260709.py` | 126 | `database`, `kis_trader.backtest`, `kis_trader.engine`, `kis_trader.engine.tail_tick_replay` | - | +| `scripts/verify_live_portfolio_align.py` | 125 | `kis_trader.strategies`, `kis_trader.utils.live_portfolio_common` | - | +| `scripts/verify_optuna_tpe_apply_dryrun.py` | 153 | `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.backtest.param_search_apply_snapshot`, `kis_trader.backtest.param_search_scalping`, `kis_trader.backtest.tail_param_search` | `argparse` | +| `scripts/verify_optuna_tpe_parity_checklist.py` | 283 | `kis_trader.backtest.backtest_portfolio_common`, `kis_trader.backtest.optuna_breakout_tpe_space`, `kis_trader.backtest.optuna_briefing`, `kis_trader.backtest.optuna_common`, `kis_trader.backtest.optuna_momentum_tpe_space`, `kis_trader.backtest.optuna_scalping_tpe_space`, `kis_trader.backtest.optuna_tail_tpe_space`, `kis_trader.backtest.optuna_tpe_common`, `kis_trader.utils.env` | `optuna` | +| `smart_executor.py` | 259 | - | - | +| `star.py` | 11 | - | - | +| `tail_engine.py` | 3 | `kis_trader.engine.tail_engine` | - | +| `test_kiwoom_token.py` | 339 | `database` | `argparse`, `getpass`, `requests` | +| `test_kiwoom_ws.py` | 196 | `database`, `kis_trader.ws.kis_ws`, `kis_trader.ws.kiwoom_ws` | `argparse` | +| `test_kiwoom_ws_sub_limit.py` | 453 | `database`, `kis_trader.utils.env`, `kis_trader.ws.kis_ws`, `kis_trader.ws.kiwoom_ws` | `argparse` | +| `test_ls_ws.py` | 998 | `database` | `argparse`, `requests`, `websocket` | +| `test_psearch.py` | 299 | `kis_trader.execution.kis_client`, `kis_trader.utils.env` | `argparse` | +| `tmp_mom_smoke_web.py` | 46 | - | `backtest_web` | +| `trend_divergence.py` | 248 | - | `pandas`, `pytrends`, `yfinance` | +| `update_env.py` | 145 | `database` | - | +| `update_env_etf.py` | 61 | `database` | - | +| `update_env_simple.py` | 71 | `database` | - | +--- + +## 수동 노트: 조건검색 유니버스 (LS / 키움) — 2026-08-04 + +> AST 자동생성 본문과 별도. 재생성 시 이 섹션을 유지·재첨부할 것. + +### 허브 파일 + +| 파일 | 역할 | +|------|------| +| `kis_trader/network/condition_common.py` | **필수 공통만** — `JOB_KO`, `normalize_job_flag`, `job_kind`, `rows_for_codes` | +| `kis_trader/network/condition_job_events.py` | 진입/재진입/이탈 → `condition_job_events` | +| `kis_trader/network/condition_manager.py` | 멤버십 `_apply_result` (push/pop) · KIS REST 폴링 본체 | +| `kis_trader/network/kiwoom_condition_manager.py` | 키움 CNSRREQ 시드 + REAL I/D | +| `kis_trader/network/ls_condition_manager.py` | LS t1866→**t1859(REST 시드)**→t1860/AFR(델타) | + +시드·프로토콜(t1859 / CNSRREQ / AFR / REAL)은 **합치지 않음** (브로커 분기 유발). + +### LS 시드 공백 (키움에 없던 실패 모드) + +1. t1859 = REST 원샷. `rsp_cd=09000` 검색결과없음 가능 (HTS UI·키움 조건명과 **다른 스냅**). +2. AFR = N/R/O 델타만 — **풀목록 재전송 없음**. +3. `LS_CONDITION_SNAPSHOT_REFRESH_SEC=0` (기본) = 주기 전체교체 OFF (정상). +4. RAM=0 이면 **`LS_T1859_EMPTY_RETRY_SEC`(기본 15)** 로 t1859 재시도. + - **`=0` = 재시도 OFF** (무한 연타 아님 · 영구 0 위험). + - RAM>0 되면 재시도 안 함. REST 폭주 방지. +5. 빈 t1859 결과는 `ls_candidates_history` 에 **저장하지 않음** (ALIGN 깨짐 방지). + +### 관련 ENV + +| 키 | 기본 | 의미 | +|----|------|------| +| `LS_CONDITION_SNAPSHOT_REFRESH_SEC` | 0 | 주기 t1859 (AFR live+종목있으면 스킵) | +| `LS_T1859_EMPTY_RETRY_SEC` | 1 | RAM0만 재시드(초). 공식 t1859 1건/초 · 실간격≥TR_GAP(1.1). 0=OFF | +| `LS_T1859_ALLOW_SHRINK` | false | t1859 축소 전체교체 허용 | +| `CONDITION_JOB_EVENTS_SAVE` | true | job 이벤트 INSERT | +| `LIVE_UNIVERSE_SLOT_ALIGN` | (운영 ON) | live ∩ history | + +웹「조건 이벤트」탭: `/api/condition_job_events`, `/api/condition_parity`. diff --git a/docs/like_mcp.md/db_erd.md b/docs/like_mcp.md/db_erd.md new file mode 100644 index 0000000..4147ed7 --- /dev/null +++ b/docs/like_mcp.md/db_erd.md @@ -0,0 +1,4863 @@ +# kis_quant_db — ERD & DDL Reference + +> **DB**: `kis_quant_db` @ `192.168.0.141:3306` +> **Engine**: MariaDB / InnoDB · charset: `utf8mb4` +> **총 테이블 수**: 50개 +> **컬럼 COMMENT**: **전 테이블·전 컬럼** 채움(2026-08-04, empty=0). +> 재적용: `python3 scripts/sql/fill_all_db_column_comments.py --apply` +> SQL덤프: `scripts/sql/all_db_column_comments.generated.sql` +> (config 전용 구버전: `fill_all_config_env_column_comments.py` / `all_config_env_column_comments.generated.sql`) +> 호가컷 정밀: `config_*_ob_column_comments.sql` · 토큰/세션 정밀: `token_session_strategy_column_comments.sql` · 해설 `docs/증권사_토큰_시세.md` + +--- + +## 목차 + +- [📊 트레이딩 핵심 (Active Trades & Orders)](#-트레이딩-핵심-active-trades--orders) +- [⚙️ 전략 설정 (Strategy Config)](#-전략-설정-strategy-config) +- [🌍 환경 설정 (Env Config)](#-환경-설정-env-config) +- [📡 실시간 마켓 데이터 (WebSocket / Market Data)](#-실시간-마켓-데이터-websocket--market-data) +- [🔍 종목 스캔 / 후보 (Universe & Candidates)](#-종목-스캔--후보-universe--candidates) +- [📈 전략별 종목 설정 (Per-Stock Config)](#-전략별-종목-설정-per-stock-config) +- [📰 DART 공시](#-dart-공시) +- [🤖 AI 분석](#-ai-분석) +- [📋 주문 실행 로그](#-주문-실행-로그) + +--- + +## ERD (주요 관계) + +> 명시적 FK 제약이 없으므로 코드 상 참조 관계를 표현합니다. + +```mermaid +erDiagram + active_trades { + varchar code PK + varchar strategy PK + double avg_buy_price + int current_qty + varchar status + } + trade_history { + int id PK + varchar code + varchar strategy + double pnl + } + target_candidates_history { + int id PK + varchar code + varchar strategy + } + env_config { + int id PK + varchar created_at + } + env_config_ext { + varchar env_key PK + text env_value + } + ws_candles { + varchar code + varchar candle_time PK + double close + } + config_momentum { + int id PK + varchar created_at + } + active_trades ||--o{ trade_history : "청산→기록" + active_trades ||--o{ target_candidates_history : "후보→진입" + env_config ||--o{ config_momentum : "설정 스냅샷" + env_config_ext ||--|| env_config : "확장 KV" + ws_candles ||--o{ active_trades : "가격 참조" +``` + +--- + +## 테이블 상세 + +### 📊 트레이딩 핵심 (Active Trades & Orders) + +#### `active_trades` + +**PK**: `code`, `strategy` + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `code` 🔑 | `varchar(20)` | NOT NULL | | 실보유: 종목코드 (KR 6자리 / US 티커) | +| `name` | `varchar(100)` | NOT NULL | | 실보유: 종목명 | +| `strategy` 🔑 | `varchar(50)` | NOT NULL | MANUAL | 실보유: 전략 ID (SHORT/MOMENTUM/BREAKOUT/US_MOMENTUM 등) | +| `avg_buy_price` | `double` | NOT NULL | | 실보유: 평균 매수가 | +| `current_price` | `double` | NULL | NULL | 실보유: 최근 평가/현재가 스냅샷 | +| `stop_price` | `double` | NULL | NULL | 실보유: 손절가 | +| `target_price` | `double` | NULL | NULL | 실보유: 목표가 | +| `max_price` | `double` | NULL | NULL | 실보유: 보유 중 고점(트레일·어깨컷용) | +| `atr_entry` | `double` | NULL | NULL | 실보유: 진입 시점 ATR | +| `target_qty` | `int(11)` | NOT NULL | | 실보유: 목표 수량 | +| `current_qty` | `int(11)` | NOT NULL | | 실보유: 현재 보유 수량 | +| `total_invested` | `double` | NULL | NULL | 실보유: 총 투자금액 | +| `status` | `text` | NOT NULL | | 실보유: 상태 | +| `buy_date` | `text` | NOT NULL | | 실보유: 매수 시각/일자 | +| `updated_at` | `text` | NOT NULL | | 실보유: 마지막 갱신 시각 | +| `size_class` | `text` | NULL | NULL | 실보유: 사이즈 등급(소/중/대 등) | +| `rsi` | `double` | NULL | NULL | 실보유: 진입/기록 시점 RSI | +| `volume_ratio` | `double` | NULL | NULL | 실보유: 거래량 배수(기록 스냅샷) | +| `tail_length_pct` | `double` | NULL | NULL | 실보유: 꼬리 길이%(기록 스냅샷) | +| `ma5_gap_pct` | `double` | NULL | NULL | 실보유: MA5 괴리%(기록 스냅샷) | +| `ma20_gap_pct` | `double` | NULL | NULL | 실보유: MA20 괴리%(기록 스냅샷) | +| `foreign_net_buy` | `double` | NULL | NULL | 실보유: 외국인 순매수(기록 스냅샷) | +| `institution_net_buy` | `double` | NULL | NULL | 실보유: 기관 순매수(기록 스냅샷) | +| `market_hour` | `double` | NULL | NULL | 실보유: 장중 시각 메타 | + +
+DDL 원문 + +```sql +CREATE TABLE `active_trades` ( + `code` varchar(20) NOT NULL, + `name` varchar(100) NOT NULL, + `strategy` varchar(50) NOT NULL DEFAULT 'MANUAL', + `avg_buy_price` double NOT NULL, + `current_price` double DEFAULT NULL, + `stop_price` double DEFAULT NULL, + `target_price` double DEFAULT NULL, + `max_price` double DEFAULT NULL, + `atr_entry` double DEFAULT NULL, + `target_qty` int(11) NOT NULL, + `current_qty` int(11) NOT NULL, + `total_invested` double DEFAULT NULL, + `status` text NOT NULL, + `buy_date` text NOT NULL, + `updated_at` text NOT NULL, + `size_class` text DEFAULT NULL, + `rsi` double DEFAULT NULL, + `volume_ratio` double DEFAULT NULL, + `tail_length_pct` double DEFAULT NULL, + `ma5_gap_pct` double DEFAULT NULL, + `ma20_gap_pct` double DEFAULT NULL, + `foreign_net_buy` double DEFAULT NULL, + `institution_net_buy` double DEFAULT NULL, + `market_hour` double DEFAULT NULL, + PRIMARY KEY (`code`,`strategy`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `active_trades_paper` + +**PK**: `code`, `strategy` + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `code` 🔑 | `varchar(20)` | NOT NULL | | 페이퍼보유: 종목코드 (KR 6자리 / US 티커) | +| `name` | `varchar(100)` | NOT NULL | | 페이퍼보유: 종목명 | +| `strategy` 🔑 | `varchar(50)` | NOT NULL | MANUAL | 페이퍼보유: 전략 ID (SHORT/MOMENTUM/BREAKOUT/US_MOMENTUM 등) | +| `avg_buy_price` | `double` | NOT NULL | | 페이퍼보유: 평균 매수가 | +| `current_price` | `double` | NULL | NULL | 페이퍼보유: 최근 평가/현재가 스냅샷 | +| `stop_price` | `double` | NULL | NULL | 페이퍼보유: 손절가 | +| `target_price` | `double` | NULL | NULL | 페이퍼보유: 목표가 | +| `max_price` | `double` | NULL | NULL | 페이퍼보유: 보유 중 고점(트레일·어깨컷용) | +| `atr_entry` | `double` | NULL | NULL | 페이퍼보유: 진입 시점 ATR | +| `target_qty` | `int(11)` | NOT NULL | | 페이퍼보유: 목표 수량 | +| `current_qty` | `int(11)` | NOT NULL | | 페이퍼보유: 현재 보유 수량 | +| `total_invested` | `double` | NULL | NULL | 페이퍼보유: 총 투자금액 | +| `status` | `varchar(20)` | NOT NULL | | 페이퍼보유: 상태 | +| `buy_date` | `varchar(30)` | NOT NULL | | 페이퍼보유: 매수 시각/일자 | +| `updated_at` | `varchar(30)` | NOT NULL | | 페이퍼보유: 마지막 갱신 시각 | +| `size_class` | `varchar(20)` | NULL | NULL | 페이퍼보유: 사이즈 등급(소/중/대 등) | +| `fill_mode` | `varchar(16)` | NOT NULL | paper | 페이퍼보유: FILL MODE (fill_mode) | +| `note` | `varchar(200)` | NULL | NULL | 페이퍼보유: 메모 | + +
+DDL 원문 + +```sql +CREATE TABLE `active_trades_paper` ( + `code` varchar(20) NOT NULL, + `name` varchar(100) NOT NULL, + `strategy` varchar(50) NOT NULL DEFAULT 'MANUAL', + `avg_buy_price` double NOT NULL, + `current_price` double DEFAULT NULL, + `stop_price` double DEFAULT NULL, + `target_price` double DEFAULT NULL, + `max_price` double DEFAULT NULL, + `atr_entry` double DEFAULT NULL, + `target_qty` int(11) NOT NULL, + `current_qty` int(11) NOT NULL, + `total_invested` double DEFAULT NULL, + `status` varchar(20) NOT NULL, + `buy_date` varchar(30) NOT NULL, + `updated_at` varchar(30) NOT NULL, + `size_class` varchar(20) DEFAULT NULL, + `fill_mode` varchar(16) NOT NULL DEFAULT 'paper', + `note` varchar(200) DEFAULT NULL, + PRIMARY KEY (`code`,`strategy`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `orders` + +**PK**: `id` | AUTO_INCREMENT=7429 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `bigint(20)` | NOT NULL | | 실주문: PK · AUTO_INCREMENT | +| `ord_no` | `varchar(30)` | NOT NULL | | 실주문: 증권사 주문번호 | +| `strategy_id` | `varchar(40)` | NOT NULL | | 실주문: 전략 ID | +| `code` | `varchar(20)` | NOT NULL | | 실주문: 종목코드 (KR 6자리 / US 티커) | +| `name` | `varchar(100)` | NOT NULL | | 실주문: 종목명 | +| `side` | `varchar(8)` | NOT NULL | | 실주문: 매수/매도 구분 | +| `qty` | `int(11)` | NOT NULL | | 실주문: 수량(주) | +| `price` | `double` | NULL | 0 | 실주문: 주문/체결 가격 | +| `filled_qty` | `int(11)` | NOT NULL | 0 | 실주문: 체결 수량 | +| `filled_avg_price` | `double` | NULL | 0 | 실주문: 체결 평균가 | +| `status` | `varchar(16)` | NOT NULL | SUBMITTED | 실주문: 상태 | +| `msg_cd` | `varchar(20)` | NULL | NULL | 실주문: 응답 코드 | +| `msg1` | `varchar(300)` | NULL | NULL | 실주문: 응답 메시지 | +| `ord_date` | `varchar(10)` | NOT NULL | | 실주문: 주문일 | +| `submitted_at` | `varchar(30)` | NOT NULL | | 실주문: 주문 제출 시각 | +| `filled_at` | `varchar(30)` | NULL | NULL | 실주문: 체결 시각 | +| `raw_json` | `mediumtext` | NULL | NULL | 실주문: 원본 응답 JSON | + +**인덱스**: +- `uq_ord_no_ctx`: (`ord_no`, `strategy_id`, `code`, `side`, `ord_date`) +- `idx_strategy_date`: (`strategy_id`, `ord_date`) +- `idx_code_date`: (`code`, `ord_date`) + +
+DDL 원문 + +```sql +CREATE TABLE `orders` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `ord_no` varchar(30) NOT NULL, + `strategy_id` varchar(40) NOT NULL, + `code` varchar(20) NOT NULL, + `name` varchar(100) NOT NULL DEFAULT '', + `side` varchar(8) NOT NULL, + `qty` int(11) NOT NULL, + `price` double DEFAULT 0, + `filled_qty` int(11) NOT NULL DEFAULT 0, + `filled_avg_price` double DEFAULT 0, + `status` varchar(16) NOT NULL DEFAULT 'SUBMITTED', + `msg_cd` varchar(20) DEFAULT NULL, + `msg1` varchar(300) DEFAULT NULL, + `ord_date` varchar(10) NOT NULL, + `submitted_at` varchar(30) NOT NULL, + `filled_at` varchar(30) DEFAULT NULL, + `raw_json` mediumtext DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uq_ord_no_ctx` (`ord_no`,`strategy_id`,`code`,`side`,`ord_date`), + KEY `idx_strategy_date` (`strategy_id`,`ord_date`), + KEY `idx_code_date` (`code`,`ord_date`) +) ENGINE=InnoDB AUTO_INCREMENT=7429 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `orders_paper` + +**PK**: `id` | AUTO_INCREMENT=7 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `bigint(20)` | NOT NULL | | 페이퍼주문: PK · AUTO_INCREMENT | +| `ord_no` | `varchar(40)` | NOT NULL | | 페이퍼주문: 증권사 주문번호 | +| `strategy_id` | `varchar(40)` | NOT NULL | | 페이퍼주문: 전략 ID | +| `code` | `varchar(20)` | NOT NULL | | 페이퍼주문: 종목코드 (KR 6자리 / US 티커) | +| `name` | `varchar(100)` | NOT NULL | | 페이퍼주문: 종목명 | +| `side` | `varchar(8)` | NOT NULL | | 페이퍼주문: 매수/매도 구분 | +| `qty` | `int(11)` | NOT NULL | | 페이퍼주문: 수량(주) | +| `price` | `double` | NULL | 0 | 페이퍼주문: 주문/체결 가격 | +| `filled_qty` | `int(11)` | NOT NULL | 0 | 페이퍼주문: 체결 수량 | +| `filled_avg_price` | `double` | NULL | 0 | 페이퍼주문: 체결 평균가 | +| `status` | `varchar(16)` | NOT NULL | FILLED | 페이퍼주문: 상태 | +| `msg_cd` | `varchar(20)` | NULL | NULL | 페이퍼주문: 응답 코드 | +| `msg1` | `varchar(300)` | NULL | NULL | 페이퍼주문: 응답 메시지 | +| `ord_date` | `varchar(10)` | NOT NULL | | 페이퍼주문: 주문일 | +| `submitted_at` | `varchar(30)` | NOT NULL | | 페이퍼주문: 주문 제출 시각 | +| `filled_at` | `varchar(30)` | NULL | NULL | 페이퍼주문: 체결 시각 | +| `raw_json` | `mediumtext` | NULL | NULL | 페이퍼주문: 원본 응답 JSON | + +**인덱스**: +- `uq_paper_ord_no_ctx`: (`ord_no`, `strategy_id`, `code`, `side`, `ord_date`) +- `idx_paper_strategy_date`: (`strategy_id`, `ord_date`) +- `idx_paper_code_date`: (`code`, `ord_date`) + +
+DDL 원문 + +```sql +CREATE TABLE `orders_paper` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `ord_no` varchar(40) NOT NULL, + `strategy_id` varchar(40) NOT NULL, + `code` varchar(20) NOT NULL, + `name` varchar(100) NOT NULL DEFAULT '', + `side` varchar(8) NOT NULL, + `qty` int(11) NOT NULL, + `price` double DEFAULT 0, + `filled_qty` int(11) NOT NULL DEFAULT 0, + `filled_avg_price` double DEFAULT 0, + `status` varchar(16) NOT NULL DEFAULT 'FILLED', + `msg_cd` varchar(20) DEFAULT NULL, + `msg1` varchar(300) DEFAULT NULL, + `ord_date` varchar(10) NOT NULL, + `submitted_at` varchar(30) NOT NULL, + `filled_at` varchar(30) DEFAULT NULL, + `raw_json` mediumtext DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uq_paper_ord_no_ctx` (`ord_no`,`strategy_id`,`code`,`side`,`ord_date`), + KEY `idx_paper_strategy_date` (`strategy_id`,`ord_date`), + KEY `idx_paper_code_date` (`code`,`ord_date`) +) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `trade_history` + +**PK**: `id` | AUTO_INCREMENT=4399 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `int(11)` | NOT NULL | | 실청산이력: PK · AUTO_INCREMENT | +| `code` | `varchar(20)` | NOT NULL | | 실청산이력: 종목코드 (KR 6자리 / US 티커) | +| `name` | `varchar(100)` | NOT NULL | | 실청산이력: 종목명 | +| `strategy` | `varchar(50)` | NULL | NULL | 실청산이력: 전략 ID (SHORT/MOMENTUM/BREAKOUT/US_MOMENTUM 등) | +| `buy_price` | `double` | NOT NULL | | 실청산이력: 매수가 | +| `sell_price` | `double` | NOT NULL | | 실청산이력: 매도가 | +| `qty` | `int(11)` | NOT NULL | | 실청산이력: 수량(주) | +| `profit_rate` | `double` | NOT NULL | | 실청산이력: 수익률(비율) | +| `realized_pnl` | `double` | NOT NULL | | 실청산이력: 실현손익 | +| `hold_minutes` | `int(11)` | NULL | NULL | 실청산이력: 보유 분 | +| `buy_date` | `varchar(30)` | NULL | NULL | 실청산이력: 매수 시각/일자 | +| `sell_date` | `varchar(30)` | NOT NULL | | 실청산이력: 매도 시각/일자 | +| `sell_reason` | `varchar(200)` | NULL | NULL | 실청산이력: 매도 사유 | +| `env_snapshot` | `text` | NULL | NULL | 실청산이력: 체결 시점 env/파라미터 JSON 스냅샷 | +| `size_class` | `varchar(20)` | NULL | NULL | 실청산이력: 사이즈 등급(소/중/대 등) | +| `rsi` | `double` | NULL | NULL | 실청산이력: 진입/기록 시점 RSI | +| `volume_ratio` | `double` | NULL | NULL | 실청산이력: 거래량 배수(기록 스냅샷) | +| `tail_length_pct` | `double` | NULL | NULL | 실청산이력: 꼬리 길이%(기록 스냅샷) | +| `ma5_gap_pct` | `double` | NULL | NULL | 실청산이력: MA5 괴리%(기록 스냅샷) | +| `ma20_gap_pct` | `double` | NULL | NULL | 실청산이력: MA20 괴리%(기록 스냅샷) | +| `foreign_net_buy` | `double` | NULL | NULL | 실청산이력: 외국인 순매수(기록 스냅샷) | +| `institution_net_buy` | `double` | NULL | NULL | 실청산이력: 기관 순매수(기록 스냅샷) | +| `market_hour` | `double` | NULL | NULL | 실청산이력: 장중 시각 메타 | + +
+DDL 원문 + +```sql +CREATE TABLE `trade_history` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `code` varchar(20) NOT NULL, + `name` varchar(100) NOT NULL, + `strategy` varchar(50) DEFAULT NULL, + `buy_price` double NOT NULL, + `sell_price` double NOT NULL, + `qty` int(11) NOT NULL, + `profit_rate` double NOT NULL, + `realized_pnl` double NOT NULL, + `hold_minutes` int(11) DEFAULT NULL, + `buy_date` varchar(30) DEFAULT NULL, + `sell_date` varchar(30) NOT NULL, + `sell_reason` varchar(200) DEFAULT NULL, + `env_snapshot` text DEFAULT NULL, + `size_class` varchar(20) DEFAULT NULL, + `rsi` double DEFAULT NULL, + `volume_ratio` double DEFAULT NULL, + `tail_length_pct` double DEFAULT NULL, + `ma5_gap_pct` double DEFAULT NULL, + `ma20_gap_pct` double DEFAULT NULL, + `foreign_net_buy` double DEFAULT NULL, + `institution_net_buy` double DEFAULT NULL, + `market_hour` double DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=4399 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `trade_history_paper` + +**PK**: `id` | AUTO_INCREMENT=2 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `int(11)` | NOT NULL | | 페이퍼청산이력: PK · AUTO_INCREMENT | +| `code` | `varchar(20)` | NOT NULL | | 페이퍼청산이력: 종목코드 (KR 6자리 / US 티커) | +| `name` | `varchar(100)` | NOT NULL | | 페이퍼청산이력: 종목명 | +| `strategy` | `varchar(50)` | NULL | NULL | 페이퍼청산이력: 전략 ID (SHORT/MOMENTUM/BREAKOUT/US_MOMENTUM 등) | +| `buy_price` | `double` | NOT NULL | | 페이퍼청산이력: 매수가 | +| `sell_price` | `double` | NOT NULL | | 페이퍼청산이력: 매도가 | +| `qty` | `int(11)` | NOT NULL | | 페이퍼청산이력: 수량(주) | +| `profit_rate` | `double` | NOT NULL | | 페이퍼청산이력: 수익률(비율) | +| `realized_pnl` | `double` | NOT NULL | | 페이퍼청산이력: 실현손익 | +| `hold_minutes` | `int(11)` | NULL | NULL | 페이퍼청산이력: 보유 분 | +| `buy_date` | `varchar(30)` | NULL | NULL | 페이퍼청산이력: 매수 시각/일자 | +| `sell_date` | `varchar(30)` | NOT NULL | | 페이퍼청산이력: 매도 시각/일자 | +| `sell_reason` | `varchar(200)` | NULL | NULL | 페이퍼청산이력: 매도 사유 | +| `env_snapshot` | `text` | NULL | NULL | 페이퍼청산이력: 체결 시점 env/파라미터 JSON 스냅샷 | +| `size_class` | `varchar(20)` | NULL | NULL | 페이퍼청산이력: 사이즈 등급(소/중/대 등) | +| `fill_mode` | `varchar(16)` | NOT NULL | paper | 페이퍼청산이력: FILL MODE (fill_mode) | + +**인덱스**: +- `idx_thp_strategy_sell`: (`strategy`, `sell_date`) +- `idx_thp_code_sell`: (`code`, `sell_date`) + +
+DDL 원문 + +```sql +CREATE TABLE `trade_history_paper` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `code` varchar(20) NOT NULL, + `name` varchar(100) NOT NULL, + `strategy` varchar(50) DEFAULT NULL, + `buy_price` double NOT NULL, + `sell_price` double NOT NULL, + `qty` int(11) NOT NULL, + `profit_rate` double NOT NULL, + `realized_pnl` double NOT NULL, + `hold_minutes` int(11) DEFAULT NULL, + `buy_date` varchar(30) DEFAULT NULL, + `sell_date` varchar(30) NOT NULL, + `sell_reason` varchar(200) DEFAULT NULL, + `env_snapshot` text DEFAULT NULL, + `size_class` varchar(20) DEFAULT NULL, + `fill_mode` varchar(16) NOT NULL DEFAULT 'paper', + PRIMARY KEY (`id`), + KEY `idx_thp_strategy_sell` (`strategy`,`sell_date`), + KEY `idx_thp_code_sell` (`code`,`sell_date`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `buy_execution_log` + +**PK**: `id` + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `int(11)` | NOT NULL | | PK · AUTO_INCREMENT | +| `code` | `varchar(20)` | NOT NULL | | 종목코드 (KR 6자리 / US 티커) | +| `name` | `varchar(100)` | NOT NULL | | 종목명 | +| `strategy` | `varchar(50)` | NOT NULL | | 전략 ID (SHORT/MOMENTUM/BREAKOUT/US_MOMENTUM 등) | +| `buy_date` | `varchar(10)` | NOT NULL | | 매수 시각/일자 | +| `executed_at` | `varchar(30)` | NOT NULL | | buy_execution_log: EXECUTED AT (executed_at) | +| `amount` | `double` | NOT NULL | | buy_execution_log: AMOUNT (amount) | +| `qty` | `int(11)` | NOT NULL | | 수량(주) | + +
+DDL 원문 + +```sql +CREATE TABLE `buy_execution_log` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `code` varchar(20) NOT NULL, + `name` varchar(100) NOT NULL, + `strategy` varchar(50) NOT NULL, + `buy_date` varchar(10) NOT NULL, + `executed_at` varchar(30) NOT NULL, + `amount` double NOT NULL, + `qty` int(11) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `daily_summary` + +**PK**: `date` + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `date` 🔑 | `varchar(10)` | NOT NULL | | daily_summary: DATE (date) | +| `start_asset` | `double` | NULL | NULL | daily_summary: START ASSET (start_asset) | +| `end_asset` | `double` | NULL | NULL | daily_summary: END ASSET (end_asset) | +| `total_trades` | `int(11)` | NULL | NULL | daily_summary: TOTAL TRADES (total_trades) | +| `win_trades` | `int(11)` | NULL | NULL | daily_summary: WIN TRADES (win_trades) | +| `total_pnl` | `double` | NULL | NULL | daily_summary: TOTAL PNL (total_pnl) | +| `win_rate` | `double` | NULL | NULL | daily_summary: WIN RATE (win_rate) | + +
+DDL 원문 + +```sql +CREATE TABLE `daily_summary` ( + `date` varchar(10) NOT NULL, + `start_asset` double DEFAULT NULL, + `end_asset` double DEFAULT NULL, + `total_trades` int(11) DEFAULT NULL, + `win_trades` int(11) DEFAULT NULL, + `total_pnl` double DEFAULT NULL, + `win_rate` double DEFAULT NULL, + PRIMARY KEY (`date`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +### ⚙️ 전략 설정 (Strategy Config) + +#### `config_momentum` + +**PK**: `id` | AUTO_INCREMENT=387 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `int(11)` | NOT NULL | | PK · AUTO_INCREMENT | +| `created_at` | `varchar(30)` | NOT NULL | | 설정행 생성시각 | +| `MOMENTUM_LIVE_BACKTEST_ALIGN` | `text` | NULL | NULL | 모멘텀: LIVE BACKTEST ALIGN (MOMENTUM_LIVE_BACKTEST_ALIGN) | +| `MOMENTUM_LIVE_SIGNAL_LOOKBACK_BARS` | `text` | NULL | NULL | 모멘텀: LIVE SIGNAL LOOKBACK 봉수 (MOMENTUM_LIVE_SIGNAL_LOOKBACK_BARS) | +| `STRATEGY_MOMENTUM_ENABLED` | `text` | NULL | NULL | 국내모멘텀 ON/OFF. 다음루프 신규매수중단(보유청산유지). WS구독해제 안함. 기동시OFF→켜려면재시작 | +| `MOMENTUM_UNIVERSE_SOURCE` | `text` | NULL | NULL | 모멘텀 유니버스 · ls_condition=LS 동명 AFR · 시세는 키움/한투 유지 · 매니저 최초 기동은 봇 재시작 필요할 수 있음 · 키움으로 자동 폴백 안 함 | +| `RANK_MOMENTUM_SORT` | `text` | NULL | NULL | 모멘텀: RANK MOMENTUM SORT (RANK_MOMENTUM_SORT) | +| `RANK_MOMENTUM_LIMIT` | `text` | NULL | NULL | 모멘텀: RANK MOMENTUM LIMIT (RANK_MOMENTUM_LIMIT) | +| `CONDITION_MOMENTUM_NAME` | `text` | NULL | NULL | 모멘텀: CONDITION MOMENTUM NAME (CONDITION_MOMENTUM_NAME) | +| `CONDITION_MOMENTUM_SEQ` | `text` | NULL | NULL | 모멘텀: CONDITION MOMENTUM SEQ (CONDITION_MOMENTUM_SEQ) | +| `MOMENTUM_CAND_LIMIT` | `text` | NULL | NULL | 모멘텀 후보 상한 · 0=무제한 · 조건검색/랭킹이 N개 줘도 상위 N개만 매수체크·WS구독 · 전 전략 합산이 키움 WS 100종 한도를 넘지 않게 조절 | +| `MOMENTUM_TIME_START` | `text` | NULL | NULL | 모멘텀: 매수시작 (MOMENTUM_TIME_START) | +| `MOMENTUM_TIME_END` | `text` | NULL | NULL | 모멘텀: 매수종료 (MOMENTUM_TIME_END) | +| `MOMENTUM_MAX_STOCKS` | `text` | NULL | NULL | 모멘텀 동시 종목 | +| `MOMENTUM_MAX_BUY_AMOUNT` | `text` | NULL | NULL | 모멘텀: 1회최대매수금액 (MOMENTUM_MAX_BUY_AMOUNT) | +| `MOMENTUM_MAX_DAILY` | `text` | NULL | NULL | 모멘텀: 일일매수횟수상한 (MOMENTUM_MAX_DAILY) | +| `MOMENTUM_RSI_MIN` | `text` | NULL | NULL | 모멘텀: RSI하한 (MOMENTUM_RSI_MIN) | +| `MOMENTUM_RSI_MAX` | `text` | NULL | NULL | 모멘텀: RSI상한 (MOMENTUM_RSI_MAX) | +| `MOMENTUM_VOL_MULT` | `text` | NULL | NULL | 모멘텀: 거래량배수 (MOMENTUM_VOL_MULT) | +| `MOMENTUM_VOL_WIN` | `text` | NULL | NULL | 모멘텀: 거래량윈도우 (MOMENTUM_VOL_WIN) | +| `MOMENTUM_TIME_END_HM` | `text` | NULL | NULL | 모멘텀: 매수종료 HHMM (MOMENTUM_TIME_END_HM) | +| `MOMENTUM_MAX_FROM_OPEN_PCT` | `text` | NULL | NULL | 모멘텀: 최대 시가대비 % (MOMENTUM_MAX_FROM_OPEN_PCT) | +| `MOMENTUM_MIN_FROM_OPEN_PCT` | `text` | NULL | NULL | 모멘텀: 최소 시가대비 % (MOMENTUM_MIN_FROM_OPEN_PCT) | +| `MOMENTUM_SKIP_HTS_SCAN_DUPES` | `text` | NULL | NULL | 모멘텀: HTS스캔중복스킵 (MOMENTUM_SKIP_HTS_SCAN_DUPES) | +| `MOMENTUM_STOP_LOSS_PCT` | `text` | NULL | NULL | 모멘텀: 손절 % (MOMENTUM_STOP_LOSS_PCT) | +| `MOMENTUM_TAKE_PROFIT_PCT` | `text` | NULL | NULL | 모멘텀: 익절 % (MOMENTUM_TAKE_PROFIT_PCT) | +| `MOMENTUM_MIN_DROP_RATE` | `text` | NULL | NULL | 모멘텀: 최소 되돌림/하락 RATE (MOMENTUM_MIN_DROP_RATE) | +| `MOMENTUM_SLOT_MONEY` | `text` | NULL | NULL | 모멘텀 슬롯(원) | +| `MOMENTUM_MIN_PRICE` | `text` | NULL | NULL | 모멘텀: 최소가격 (MOMENTUM_MIN_PRICE) | +| `MOMENTUM_MAX_LOSS_PER_TRADE_KRW` | `text` | NULL | NULL | 모멘텀: 최대 LOSS PER TRADE 원 (MOMENTUM_MAX_LOSS_PER_TRADE_KRW) | +| `MOMENTUM_MIN_DROP_PCT_FOR_LOSS_CUT` | `text` | NULL | NULL | 모멘텀: 최소 되돌림/하락 % FOR LOSS CUT (MOMENTUM_MIN_DROP_PCT_FOR_LOSS_CUT) | +| `MOMENTUM_MIN_PROFIT_PCT` | `text` | NULL | NULL | 모멘텀: 최소 PROFIT % (MOMENTUM_MIN_PROFIT_PCT) | +| `MOMENTUM_TP_MAX_PCT` | `text` | NULL | NULL | 모멘텀: TP 최대 % (MOMENTUM_TP_MAX_PCT) | +| `MOMENTUM_SHOULDER_MIN_HIGH_PCT` | `text` | NULL | NULL | 모멘텀: 어깨무장고점 % (MOMENTUM_SHOULDER_MIN_HIGH_PCT) | +| `MOMENTUM_SHOULDER_CUT_PCT` | `text` | NULL | NULL | 모멘텀: 어깨컷 % (MOMENTUM_SHOULDER_CUT_PCT) | +| `MOMENTUM_TOTAL_BUDGET_KRW` | `text` | NULL | NULL | 모멘텀 총 한도(원) | +| `KIS_MOMENTUM_MM_CHANNEL` | `text` | NULL | NULL | 모멘텀: KIS MOMENTUM MM CHANNEL (KIS_MOMENTUM_MM_CHANNEL) | +| `MOMENTUM_RATCHET_TIERS` | `text` | NULL | NULL | 모멘텀: 다단래칫 (MOMENTUM_RATCHET_TIERS) | +| `MOMENTUM_TRAIL_PCT` | `text` | NULL | NULL | 모멘텀: 트레일 % (MOMENTUM_TRAIL_PCT) | +| `MOMENTUM_TRAIL_ARM_PCT` | `text` | NULL | NULL | 모멘텀: 트레일무장 % (MOMENTUM_TRAIL_ARM_PCT) | +| `MOMENTUM_MAX_HOLD_BARS` | `text` | NULL | NULL | 모멘텀: 최대보유봉 (MOMENTUM_MAX_HOLD_BARS) | +| `MOMENTUM_USE_DEFENSE_FILTERS` | `text` | NULL | NULL | 모멘텀: 사용_ 방어필터 필터 S (MOMENTUM_USE_DEFENSE_FILTERS) | +| `MOMENTUM_USE_HIGH_CHASE_FILTER` | `text` | NULL | NULL | 모멘텀: 사용_ 고점추격 필터 (MOMENTUM_USE_HIGH_CHASE_FILTER) | +| `MOMENTUM_USE_DAILY_RANGE_FILTER` | `text` | NULL | NULL | 모멘텀: 사용_ DAILY RANGE 필터 (MOMENTUM_USE_DAILY_RANGE_FILTER) | +| `MOMENTUM_HIGH_CHASE_THR` | `text` | NULL | NULL | 모멘텀: 고점추격 THR (MOMENTUM_HIGH_CHASE_THR) | +| `MOMENTUM_RSI_PERIOD` | `text` | NULL | NULL | 모멘텀: RSI 기간 (MOMENTUM_RSI_PERIOD) | +| `MOMENTUM_COOLDOWN_SEC` | `text` | NULL | NULL | 모멘텀: 쿨다운 초 (MOMENTUM_COOLDOWN_SEC) | +| `MOMENTUM_MIN_HOLD_SEC` | `text` | NULL | NULL | 모멘텀: 최소보유 초 (MOMENTUM_MIN_HOLD_SEC) | +| `MOMENTUM_FORCE_EOD_EXIT` | `text` | NULL | NULL | 모멘텀: FORCE 장마감 EXIT (MOMENTUM_FORCE_EOD_EXIT) | +| `MOMENTUM_USE_EMA_FILTER` | `text` | NULL | NULL | 모멘텀: 사용_ EMA 필터 (MOMENTUM_USE_EMA_FILTER) | +| `MOMENTUM_EMA_FAST_PERIOD` | `text` | NULL | NULL | 모멘텀: EMA단기 기간 (MOMENTUM_EMA_FAST_PERIOD) | +| `MOMENTUM_EMA_SLOW_PERIOD` | `text` | NULL | NULL | 모멘텀: EMA장기 기간 (MOMENTUM_EMA_SLOW_PERIOD) | +| `MOMENTUM_USE_RSI_MAX_FILTER` | `text` | NULL | NULL | 모멘텀: 사용_ RSI상한 필터 (MOMENTUM_USE_RSI_MAX_FILTER) | +| `MOMENTUM_PATTERN_BREAKOUT` | `text` | NULL | NULL | 모멘텀: 패턴 돌파 (MOMENTUM_PATTERN_BREAKOUT) | +| `MOMENTUM_PATTERN_PULLBACK` | `text` | NULL | NULL | 모멘텀: 패턴 되돌림 (MOMENTUM_PATTERN_PULLBACK) | +| `MOMENTUM_CHASE_LOOKBACK_MIN` | `text` | NULL | NULL | 모멘텀: CHASE LOOKBACK 최소 (MOMENTUM_CHASE_LOOKBACK_MIN) | +| `MOMENTUM_PULLBACK_LOOKBACK_MIN` | `text` | NULL | NULL | 모멘텀: 되돌림 LOOKBACK 최소 (MOMENTUM_PULLBACK_LOOKBACK_MIN) | +| `MOMENTUM_PULLBACK_MIN_PCT` | `text` | NULL | NULL | 모멘텀: 되돌림 최소 % (MOMENTUM_PULLBACK_MIN_PCT) | +| `MOMENTUM_PULLBACK_MAX_PCT` | `text` | NULL | NULL | 모멘텀: 되돌림 최대 % (MOMENTUM_PULLBACK_MAX_PCT) | +| `MOMENTUM_SETUP_VOL_MAX_MULT` | `text` | NULL | NULL | 모멘텀: SETUP 거래량 최대 배수 (MOMENTUM_SETUP_VOL_MAX_MULT) | +| `MOMENTUM_SETUP_BEAR_BARS_MIN` | `text` | NULL | NULL | 모멘텀: SETUP BEAR 봉수 최소 (MOMENTUM_SETUP_BEAR_BARS_MIN) | +| `MOMENTUM_ORDERBOOK_FILTER_ENABLED` | `text` | NULL | NULL | 모멘텀 호가필터 ON · ON=스프레드·잔량비·얇은호가·매도벽으로 매수 차단 · OFF=저장만 | +| `MOMENTUM_ORDERBOOK_MAX_SPREAD_PCT` | `text` | NULL | NULL | 모멘텀 스프레드상한(%) · 0.45=0.45% · 그리드 max_spread_pct 앵커 | +| `MOMENTUM_ORDERBOOK_ENTRY_BID_LEVELS` | `text` | NULL | NULL | 모멘텀 호가단계(N호가) | +| `MOMENTUM_ORDERBOOK_ENTRY_BID_DEPTH_MULT` | `text` | NULL | NULL | 모멘텀 매수호가깊이 배수 · 얇은호가 탈락 · 필요수량×N | +| `MOMENTUM_ORDERBOOK_ENTRY_ASK_MAX_MULT` | `text` | NULL | NULL | 모멘텀 매도벽 허용배수 · 필요수량×N · 그리드 ask_max_mult | +| `MOMENTUM_ORDERBOOK_MIN_BID_ASK_RATIO` | `text` | NULL | NULL | 모멘텀 매수/매도잔량비 하한 · 그리드 min_bid_ask_ratio | +| `MOMENTUM_PROGRAM_FILTER_ENABLED` | `text` | NULL | NULL | 모멘텀: 프로그램매매 필터 ON/OFF (MOMENTUM_PROGRAM_FILTER_ENABLED) | +| `MOMENTUM_PROGRAM_MIN_NET_BUY_QTY` | `text` | NULL | NULL | 모멘텀: 프로그램매매 최소 NET BUY QTY (MOMENTUM_PROGRAM_MIN_NET_BUY_QTY) | +| `MOMENTUM_PROGRAM_MIN_NET_BUY_AMT` | `text` | NULL | NULL | 모멘텀: 프로그램매매 최소 NET BUY AMT (MOMENTUM_PROGRAM_MIN_NET_BUY_AMT) | +| `MOMENTUM_PROGRAM_MAX_SELL_BUY_QTY_RATIO` | `text` | NULL | NULL | 모멘텀: 프로그램매매 최대 SELL BUY QTY RATIO (MOMENTUM_PROGRAM_MAX_SELL_BUY_QTY_RATIO) | +| `MOMENTUM_PROGRAM_MIN_NET_DELTA_QTY` | `text` | NULL | NULL | 모멘텀: 프로그램매매 최소 NET DELTA QTY (MOMENTUM_PROGRAM_MIN_NET_DELTA_QTY) | +| `MOMENTUM_PROGRAM_SNAPSHOT_MAX_AGE_SEC` | `text` | NULL | NULL | 모멘텀: 프로그램매매 SNAPSHOT 최대 AGE 초 (MOMENTUM_PROGRAM_SNAPSHOT_MAX_AGE_SEC) | +| `MOMENTUM_BACKTEST_USE_TRIGGER_SNAPSHOT_DB` | `text` | NULL | NULL | 모멘텀: BACKTEST 사용_ TRIGGER SNAPSHOT DB (MOMENTUM_BACKTEST_USE_TRIGGER_SNAPSHOT_DB) | +| `MOMENTUM_WHIPSAW_FILTER_ENABLED` | `text` | NULL | NULL | 모멘텀 휩쏘 필터 | +| `MOMENTUM_WHIPSAW_MODE` | `text` | NULL | NULL | 모멘텀: 휩쏘 모드 (MOMENTUM_WHIPSAW_MODE) | +| `MOMENTUM_WHIPSAW_SUBBAR_SEC` | `text` | NULL | NULL | 모멘텀: 휩쏘 SUBBAR 초 (MOMENTUM_WHIPSAW_SUBBAR_SEC) | +| `MOMENTUM_WHIPSAW_LOOKBACK_SEC` | `text` | NULL | NULL | 모멘텀: 휩쏘 LOOKBACK 초 (MOMENTUM_WHIPSAW_LOOKBACK_SEC) | +| `MOMENTUM_WHIPSAW_DIP_PCT` | `text` | NULL | NULL | 모멘텀: 휩쏘 DIP % (MOMENTUM_WHIPSAW_DIP_PCT) | +| `MOMENTUM_WHIPSAW_RECOVERY_TOL_PCT` | `text` | NULL | NULL | 모멘텀: 휩쏘 RECOVERY TOL % (MOMENTUM_WHIPSAW_RECOVERY_TOL_PCT) | +| `MOMENTUM_DAILY_PROFIT_TARGET_ENABLED` | `text` | NULL | NULL | 모멘텀 손익 감시 켜기 · OFF = 해당 전략 일일익절 비활성 (매수 차단 없음) | +| `MOMENTUM_DAILY_PROFIT_TARGET_KRW` | `text` | NULL | NULL | 모멘텀 목표(원) | +| `MOMENTUM_DAILY_PROFIT_TARGET_PCT` | `text` | NULL | NULL | 모멘텀 목표(%) · 퍼센트 그대로 (예: 2.5 = 2.5%) · 0=미사용 | +| `MOMENTUM_DAILY_PROFIT_HALT_NEW_BUYS` | `text` | NULL | NULL | 모멘텀 신규매수 수동 중단 · 목표 무관 수동 잠금 · 비우면 마스터 상속 · OFF=정상 | +| `MOMENTUM_BACKTEST_USE_TICK_EXIT` | `text` | NULL | NULL | 모멘텀: BACKTEST 사용_ TICK EXIT (MOMENTUM_BACKTEST_USE_TICK_EXIT) | +| `MOMENTUM_BACKTEST_TICK_FALLBACK_OHLC` | `text` | NULL | NULL | ⚠ 모멘텀 OHLC 폴백 (정합위험·금지) · 절대금지 — 틱 청산 ON 시 엔진 무시 · Optuna 강제 OFF | +| `MOMENTUM_BACKTEST_POLL_MS` | `text` | NULL | NULL | 모멘텀 틱청산 폴링(ms) · 실매 STRATEGY_LOOP_SLEEP≈0.1초 정합 · 틱 사이 청산 검사 간격 · 하한 50ms | +| `MOMENTUM_BACKTEST_SELL_SLIP_PCT` | `text` | NULL | NULL | 모멘텀: BACKTEST SELL SLIP % (MOMENTUM_BACKTEST_SELL_SLIP_PCT) | +| `MOMENTUM_BACKTEST_USE_TICK_ENTRY` | `text` | NULL | NULL | 모멘텀: BACKTEST 사용_ TICK ENTRY (MOMENTUM_BACKTEST_USE_TICK_ENTRY) | +| `MOMENTUM_BACKTEST_BUY_SLIP_PCT` | `text` | NULL | NULL | 모멘텀: BACKTEST BUY SLIP % (MOMENTUM_BACKTEST_BUY_SLIP_PCT) | +| `MOMENTUM_BACKTEST_UNIVERSE_STRICT` | `text` | NULL | NULL | 모멘텀: BACKTEST 유니버스 STRICT (MOMENTUM_BACKTEST_UNIVERSE_STRICT) | +| `MOMENTUM_BACKTEST_UNIVERSE_STRICT_LAG_MIN` | `text` | NULL | NULL | 모멘텀: BACKTEST 유니버스 STRICT LAG 최소 (MOMENTUM_BACKTEST_UNIVERSE_STRICT_LAG_MIN) | +| `MOMENTUM_BACKTEST_LIVE_SCAN_QUEUE` | `text` | NULL | NULL | 모멘텀: BACKTEST LIVE SCAN QUEUE (MOMENTUM_BACKTEST_LIVE_SCAN_QUEUE) | +| `MOMENTUM_BACKTEST_SCAN_SEC` | `text` | NULL | NULL | 모멘텀: BACKTEST SCAN 초 (MOMENTUM_BACKTEST_SCAN_SEC) | +| `MOMENTUM_BACKTEST_UNIVERSE_SCAN_AT` | `text` | NULL | NULL | 모멘텀: BACKTEST 유니버스 SCAN AT (MOMENTUM_BACKTEST_UNIVERSE_SCAN_AT) | +| `MOMENTUM_UNIVERSE_EXIT_DEBOUNCE_SEC` | `text` | NULL | NULL | 모멘텀: 유니버스 EXIT DEBOUNCE 초 (MOMENTUM_UNIVERSE_EXIT_DEBOUNCE_SEC) | +| `MOMENTUM_BACKTEST_CANDLE_WARMUP_BARS` | `text` | NULL | NULL | 모멘텀: BACKTEST CANDLE WARMUP 봉수 (MOMENTUM_BACKTEST_CANDLE_WARMUP_BARS) | +| `MOMENTUM_BACKTEST_USE_TRIGGER_EVAL_SNAPSHOT` | `text` | NULL | NULL | 모멘텀: BACKTEST 사용_ TRIGGER EVAL SNAPSHOT (MOMENTUM_BACKTEST_USE_TRIGGER_EVAL_SNAPSHOT) | +| `MOMENTUM_DAILY_PROFIT_MODE` | `text` | NULL | NULL | 모멘텀 익절방식 · 비우면 마스터 상속 · fixed/trailing/both | +| `MOMENTUM_DAILY_PROFIT_TRAIL_TIERS` | `text` | NULL | NULL | 모멘텀 다단 트레일 · 수익원:컷%,… · 비우면 마스터 상속 · 예: 30000:50,100000:35,300000:25 | +| `MOMENTUM_DAILY_PROFIT_TRAIL_DROP_PCT` | `text` | NULL | NULL | 모멘텀 단일 트레일 되돌림(%) · 다단 비었을 때만 · 고점 대비 % · 비우면 마스터 상속 · 0=미사용 | +| `MOMENTUM_DAILY_PROFIT_TRAIL_ARM_KRW` | `text` | NULL | NULL | 모멘텀 트레일 발동수익(원) · 이 이상 벌어야 트레일 감시 · 비우면 마스터 상속 | +| `MOMENTUM_DAILY_PROFIT_TRAIL_ARM_PCT` | `text` | NULL | NULL | 모멘텀: 일일익절 트레일무장 % (MOMENTUM_DAILY_PROFIT_TRAIL_ARM_PCT) | +| `CONDITION_MOMENTUM_KIWOOM_NAME` | `text` | NULL | NULL | 모멘텀: CONDITION MOMENTUM KIWOOM NAME (CONDITION_MOMENTUM_KIWOOM_NAME) | +| `CONDITION_MOMENTUM_KIWOOM_SEQ` | `text` | NULL | NULL | 모멘텀: CONDITION MOMENTUM KIWOOM SEQ (CONDITION_MOMENTUM_KIWOOM_SEQ) | +| `MOMENTUM_KIWOOM_UNIVERSE` | `text` | NULL | NULL | 모멘텀: KIWOOM 유니버스 (MOMENTUM_KIWOOM_UNIVERSE) | +| `MOMENTUM_EOD_ENABLED` | `text` | NULL | NULL | EOD 당일청산 사용 · ON=설정 시각 이후 보유 전량 장마감청산 · OFF=익절·손절·시간컷만 | +| `MOMENTUM_EOD_HM` | `text` | NULL | NULL | EOD 시각 (HH:MM) · 정규장 15:30 마감 기준 최소 10분 전 · 순차매도 여유 | +| `MOMENTUM_TRIGGER_E_CONFIRM` | `text` | NULL | NULL | 모멘텀: TRIGGER E CONFIRM (MOMENTUM_TRIGGER_E_CONFIRM) | +| `MOMENTUM_TRIGGER_REQUIRE_BULL_BAR` | `text` | NULL | NULL | 모멘텀: TRIGGER REQUIRE BULL BAR (MOMENTUM_TRIGGER_REQUIRE_BULL_BAR) | +| `MOMENTUM_USE_VOL_TRIGGER` | `text` | NULL | NULL | 모멘텀: 사용_ 거래량 TRIGGER (MOMENTUM_USE_VOL_TRIGGER) | +| `MOMENTUM_USE_RSI_FILTER` | `text` | NULL | NULL | 모멘텀: 사용_ RSI 필터 (MOMENTUM_USE_RSI_FILTER) | +| `MOMENTUM_LIVE_MIN_CANDLES` | `text` | NULL | NULL | 모멘텀: 실매최소봉수 (MOMENTUM_LIVE_MIN_CANDLES) | +| `MOMENTUM_GAP_FILL_LIMIT` | `text` | NULL | NULL | 모멘텀: GAP FILL LIMIT (MOMENTUM_GAP_FILL_LIMIT) | +| `MOMENTUM_GRID_FAST_SKIP_HTS_SCAN_DUPES` | `text` | NULL | NULL | 모멘텀: GRID FAST HTS스캔중복스킵 (MOMENTUM_GRID_FAST_SKIP_HTS_SCAN_DUPES) | +| `MOMENTUM_LIVE_ALIGN_USE_FORMING_BAR` | `text` | NULL | NULL | 모멘텀: LIVE ALIGN 사용_ FORMING BAR (MOMENTUM_LIVE_ALIGN_USE_FORMING_BAR) | +| `MOMENTUM_COOLDOWN_USE_CANDLE_FLOOR` | `text` | NULL | NULL | 모멘텀: 쿨다운 사용_ CANDLE FLOOR (MOMENTUM_COOLDOWN_USE_CANDLE_FLOOR) | +| `MOMENTUM_COOLDOWN_ENGINE_ONLY` | `text` | NULL | NULL | 모멘텀: 쿨다운 ENGINE ONLY (MOMENTUM_COOLDOWN_ENGINE_ONLY) | +| `MOMENTUM_BACKTEST_SKIP_PRE_SUBSCRIBE` | `text` | NULL | NULL | 모멘텀: BACKTEST SKIP PRE SUBSCRIBE (MOMENTUM_BACKTEST_SKIP_PRE_SUBSCRIBE) | +| `MOMENTUM_MAX_DAILY_CHG` | `text` | NULL | NULL | 모멘텀: 당일등락상한 (MOMENTUM_MAX_DAILY_CHG) | +| `MOMENTUM_BACKTEST_WALLCLOCK_LAST_PRICE` | `text` | NULL | NULL | 모멘텀: BACKTEST WALLCLOCK LAST PRICE (MOMENTUM_BACKTEST_WALLCLOCK_LAST_PRICE) | +| `MOMENTUM_PREV_DAY_OPEN_HM_MAX` | `text` | NULL | NULL | 모멘텀: PREV DAY OPEN HHMM 최대 (MOMENTUM_PREV_DAY_OPEN_HM_MAX) | +| `MOMENTUM_BACKTEST_REST_WARMUP` | `text` | NULL | NULL | 모멘텀: BACKTEST REST WARMUP (MOMENTUM_BACKTEST_REST_WARMUP) | +| `MOMENTUM_BACKTEST_REST_WARMUP_BARS` | `text` | NULL | NULL | 모멘텀: BACKTEST REST WARMUP 봉수 (MOMENTUM_BACKTEST_REST_WARMUP_BARS) | +| `MOMENTUM_BACKTEST_REST_WARMUP_BARS_RETRY` | `text` | NULL | NULL | 모멘텀: BACKTEST REST WARMUP 봉수 RETRY (MOMENTUM_BACKTEST_REST_WARMUP_BARS_RETRY) | +| `MOMENTUM_BACKTEST_REST_MAX_CODES` | `text` | NULL | NULL | 모멘텀: BACKTEST REST 최대 CODES (MOMENTUM_BACKTEST_REST_MAX_CODES) | +| `MOMENTUM_BACKTEST_REST_SLEEP_SEC` | `text` | NULL | NULL | 모멘텀: BACKTEST REST SLEEP 초 (MOMENTUM_BACKTEST_REST_SLEEP_SEC) | +| `MOMENTUM_BACKTEST_REST_KIWOOM_MOCK` | `text` | NULL | NULL | 모멘텀: BACKTEST REST KIWOOM MOCK (MOMENTUM_BACKTEST_REST_KIWOOM_MOCK) | +| `MOMENTUM_DAILY_PROFIT_RISK_BUDGET_ENABLED` | `text` | NULL | NULL | 모멘텀 리스크버짓(B) · ON=일일익절 hit 후 보유최악손절합>cushion 이면 전량정리 · 기본 OFF · 마스터 없음 | +| `MOMENTUM_DAILY_PROFIT_RISK_BUDGET_COOLDOWN_SEC` | `text` | NULL | NULL | 모멘텀 리스크버짓 쿨다운(초) · 같은 날 재평가 최소 간격 · 0=당일 1회만 | +| `MOMENTUM_TICK_SELL_ENABLED` | `text` | NULL | NULL | 모멘텀 틱매도 · 비우면 공통 TICK_SELL_ENABLED · true/false 명시 시 전략만 덮어씀 · 기본 false | +| `CONDITION_MOMENTUM_LS_NAME` | `text` | NULL | NULL | 모멘텀: CONDITION MOMENTUM LS NAME (CONDITION_MOMENTUM_LS_NAME) | +| `MOMENTUM_LIVE_UNIVERSE_HISTORY_SOURCE` | `text` | NULL | NULL | 모멘텀: LIVE 유니버스 HISTORY SOURCE (MOMENTUM_LIVE_UNIVERSE_HISTORY_SOURCE) | +| `MOMENTUM_E_MIN_CHG_PCT` | `text` | NULL | NULL | 모멘텀: E 최소 CHG % (MOMENTUM_E_MIN_CHG_PCT) | + +
+DDL 원문 + +```sql +CREATE TABLE `config_momentum` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `created_at` varchar(30) NOT NULL, + `MOMENTUM_LIVE_BACKTEST_ALIGN` text DEFAULT NULL, + `MOMENTUM_LIVE_SIGNAL_LOOKBACK_BARS` text DEFAULT NULL, + `STRATEGY_MOMENTUM_ENABLED` text DEFAULT NULL, + `MOMENTUM_UNIVERSE_SOURCE` text DEFAULT NULL, + `RANK_MOMENTUM_SORT` text DEFAULT NULL, + `RANK_MOMENTUM_LIMIT` text DEFAULT NULL, + `CONDITION_MOMENTUM_NAME` text DEFAULT NULL, + `CONDITION_MOMENTUM_SEQ` text DEFAULT NULL, + `MOMENTUM_CAND_LIMIT` text DEFAULT NULL, + `MOMENTUM_TIME_START` text DEFAULT NULL, + `MOMENTUM_TIME_END` text DEFAULT NULL, + `MOMENTUM_MAX_STOCKS` text DEFAULT NULL, + `MOMENTUM_MAX_BUY_AMOUNT` text DEFAULT NULL, + `MOMENTUM_MAX_DAILY` text DEFAULT NULL, + `MOMENTUM_RSI_MIN` text DEFAULT NULL, + `MOMENTUM_RSI_MAX` text DEFAULT NULL, + `MOMENTUM_VOL_MULT` text DEFAULT NULL, + `MOMENTUM_VOL_WIN` text DEFAULT NULL, + `MOMENTUM_TIME_END_HM` text DEFAULT NULL, + `MOMENTUM_MAX_FROM_OPEN_PCT` text DEFAULT NULL, + `MOMENTUM_MIN_FROM_OPEN_PCT` text DEFAULT NULL, + `MOMENTUM_SKIP_HTS_SCAN_DUPES` text DEFAULT NULL, + `MOMENTUM_STOP_LOSS_PCT` text DEFAULT NULL, + `MOMENTUM_TAKE_PROFIT_PCT` text DEFAULT NULL, + `MOMENTUM_MIN_DROP_RATE` text DEFAULT NULL, + `MOMENTUM_SLOT_MONEY` text DEFAULT NULL, + `MOMENTUM_MIN_PRICE` text DEFAULT NULL, + `MOMENTUM_MAX_LOSS_PER_TRADE_KRW` text DEFAULT NULL, + `MOMENTUM_MIN_DROP_PCT_FOR_LOSS_CUT` text DEFAULT NULL, + `MOMENTUM_MIN_PROFIT_PCT` text DEFAULT NULL, + `MOMENTUM_TP_MAX_PCT` text DEFAULT NULL, + `MOMENTUM_SHOULDER_MIN_HIGH_PCT` text DEFAULT NULL, + `MOMENTUM_SHOULDER_CUT_PCT` text DEFAULT NULL, + `MOMENTUM_TOTAL_BUDGET_KRW` text DEFAULT NULL, + `KIS_MOMENTUM_MM_CHANNEL` text DEFAULT NULL, + `MOMENTUM_RATCHET_TIERS` text DEFAULT NULL, + `MOMENTUM_TRAIL_PCT` text DEFAULT NULL, + `MOMENTUM_TRAIL_ARM_PCT` text DEFAULT NULL, + `MOMENTUM_MAX_HOLD_BARS` text DEFAULT NULL, + `MOMENTUM_USE_DEFENSE_FILTERS` text DEFAULT NULL, + `MOMENTUM_USE_HIGH_CHASE_FILTER` text DEFAULT NULL, + `MOMENTUM_USE_DAILY_RANGE_FILTER` text DEFAULT NULL, + `MOMENTUM_HIGH_CHASE_THR` text DEFAULT NULL, + `MOMENTUM_RSI_PERIOD` text DEFAULT NULL, + `MOMENTUM_COOLDOWN_SEC` text DEFAULT NULL, + `MOMENTUM_MIN_HOLD_SEC` text DEFAULT NULL, + `MOMENTUM_FORCE_EOD_EXIT` text DEFAULT NULL, + `MOMENTUM_USE_EMA_FILTER` text DEFAULT NULL, + `MOMENTUM_EMA_FAST_PERIOD` text DEFAULT NULL, + `MOMENTUM_EMA_SLOW_PERIOD` text DEFAULT NULL, + `MOMENTUM_USE_RSI_MAX_FILTER` text DEFAULT NULL, + `MOMENTUM_PATTERN_BREAKOUT` text DEFAULT NULL, + `MOMENTUM_PATTERN_PULLBACK` text DEFAULT NULL, + `MOMENTUM_CHASE_LOOKBACK_MIN` text DEFAULT NULL, + `MOMENTUM_PULLBACK_LOOKBACK_MIN` text DEFAULT NULL, + `MOMENTUM_PULLBACK_MIN_PCT` text DEFAULT NULL, + `MOMENTUM_PULLBACK_MAX_PCT` text DEFAULT NULL, + `MOMENTUM_SETUP_VOL_MAX_MULT` text DEFAULT NULL, + `MOMENTUM_SETUP_BEAR_BARS_MIN` text DEFAULT NULL, + `MOMENTUM_ORDERBOOK_FILTER_ENABLED` text DEFAULT NULL, + `MOMENTUM_ORDERBOOK_MAX_SPREAD_PCT` text DEFAULT NULL, + `MOMENTUM_ORDERBOOK_ENTRY_BID_LEVELS` text DEFAULT NULL, + `MOMENTUM_ORDERBOOK_ENTRY_BID_DEPTH_MULT` text DEFAULT NULL, + `MOMENTUM_ORDERBOOK_ENTRY_ASK_MAX_MULT` text DEFAULT NULL, + `MOMENTUM_ORDERBOOK_MIN_BID_ASK_RATIO` text DEFAULT NULL, + `MOMENTUM_PROGRAM_FILTER_ENABLED` text DEFAULT NULL, + `MOMENTUM_PROGRAM_MIN_NET_BUY_QTY` text DEFAULT NULL, + `MOMENTUM_PROGRAM_MIN_NET_BUY_AMT` text DEFAULT NULL, + `MOMENTUM_PROGRAM_MAX_SELL_BUY_QTY_RATIO` text DEFAULT NULL, + `MOMENTUM_PROGRAM_MIN_NET_DELTA_QTY` text DEFAULT NULL, + `MOMENTUM_PROGRAM_SNAPSHOT_MAX_AGE_SEC` text DEFAULT NULL, + `MOMENTUM_BACKTEST_USE_TRIGGER_SNAPSHOT_DB` text DEFAULT NULL, + `MOMENTUM_WHIPSAW_FILTER_ENABLED` text DEFAULT NULL, + `MOMENTUM_WHIPSAW_MODE` text DEFAULT NULL, + `MOMENTUM_WHIPSAW_SUBBAR_SEC` text DEFAULT NULL, + `MOMENTUM_WHIPSAW_LOOKBACK_SEC` text DEFAULT NULL, + `MOMENTUM_WHIPSAW_DIP_PCT` text DEFAULT NULL, + `MOMENTUM_WHIPSAW_RECOVERY_TOL_PCT` text DEFAULT NULL, + `MOMENTUM_DAILY_PROFIT_TARGET_ENABLED` text DEFAULT NULL, + `MOMENTUM_DAILY_PROFIT_TARGET_KRW` text DEFAULT NULL, + `MOMENTUM_DAILY_PROFIT_TARGET_PCT` text DEFAULT NULL, + `MOMENTUM_DAILY_PROFIT_HALT_NEW_BUYS` text DEFAULT NULL, + `MOMENTUM_BACKTEST_USE_TICK_EXIT` text DEFAULT NULL, + `MOMENTUM_BACKTEST_TICK_FALLBACK_OHLC` text DEFAULT NULL, + `MOMENTUM_BACKTEST_POLL_MS` text DEFAULT NULL, + `MOMENTUM_BACKTEST_SELL_SLIP_PCT` text DEFAULT NULL, + `MOMENTUM_BACKTEST_USE_TICK_ENTRY` text DEFAULT NULL, + `MOMENTUM_BACKTEST_BUY_SLIP_PCT` text DEFAULT NULL, + `MOMENTUM_BACKTEST_UNIVERSE_STRICT` text DEFAULT NULL, + `MOMENTUM_BACKTEST_UNIVERSE_STRICT_LAG_MIN` text DEFAULT NULL, + `MOMENTUM_BACKTEST_LIVE_SCAN_QUEUE` text DEFAULT NULL, + `MOMENTUM_BACKTEST_SCAN_SEC` text DEFAULT NULL, + `MOMENTUM_BACKTEST_UNIVERSE_SCAN_AT` text DEFAULT NULL, + `MOMENTUM_UNIVERSE_EXIT_DEBOUNCE_SEC` text DEFAULT NULL, + `MOMENTUM_BACKTEST_CANDLE_WARMUP_BARS` text DEFAULT NULL, + `MOMENTUM_BACKTEST_USE_TRIGGER_EVAL_SNAPSHOT` text DEFAULT NULL, + `MOMENTUM_DAILY_PROFIT_MODE` text DEFAULT NULL, + `MOMENTUM_DAILY_PROFIT_TRAIL_TIERS` text DEFAULT NULL, + `MOMENTUM_DAILY_PROFIT_TRAIL_DROP_PCT` text DEFAULT NULL, + `MOMENTUM_DAILY_PROFIT_TRAIL_ARM_KRW` text DEFAULT NULL, + `MOMENTUM_DAILY_PROFIT_TRAIL_ARM_PCT` text DEFAULT NULL, + `CONDITION_MOMENTUM_KIWOOM_NAME` text DEFAULT NULL, + `CONDITION_MOMENTUM_KIWOOM_SEQ` text DEFAULT NULL, + `MOMENTUM_KIWOOM_UNIVERSE` text DEFAULT NULL, + `MOMENTUM_EOD_ENABLED` text DEFAULT NULL, + `MOMENTUM_EOD_HM` text DEFAULT NULL, + `MOMENTUM_TRIGGER_E_CONFIRM` text DEFAULT NULL, + `MOMENTUM_TRIGGER_REQUIRE_BULL_BAR` text DEFAULT NULL, + `MOMENTUM_USE_VOL_TRIGGER` text DEFAULT NULL, + `MOMENTUM_USE_RSI_FILTER` text DEFAULT NULL, + `MOMENTUM_LIVE_MIN_CANDLES` text DEFAULT NULL, + `MOMENTUM_GAP_FILL_LIMIT` text DEFAULT NULL, + `MOMENTUM_GRID_FAST_SKIP_HTS_SCAN_DUPES` text DEFAULT NULL, + `MOMENTUM_LIVE_ALIGN_USE_FORMING_BAR` text DEFAULT NULL, + `MOMENTUM_COOLDOWN_USE_CANDLE_FLOOR` text DEFAULT NULL, + `MOMENTUM_COOLDOWN_ENGINE_ONLY` text DEFAULT NULL, + `MOMENTUM_BACKTEST_SKIP_PRE_SUBSCRIBE` text DEFAULT NULL, + `MOMENTUM_MAX_DAILY_CHG` text DEFAULT NULL, + `MOMENTUM_BACKTEST_WALLCLOCK_LAST_PRICE` text DEFAULT NULL, + `MOMENTUM_PREV_DAY_OPEN_HM_MAX` text DEFAULT NULL, + `MOMENTUM_BACKTEST_REST_WARMUP` text DEFAULT NULL, + `MOMENTUM_BACKTEST_REST_WARMUP_BARS` text DEFAULT NULL, + `MOMENTUM_BACKTEST_REST_WARMUP_BARS_RETRY` text DEFAULT NULL, + `MOMENTUM_BACKTEST_REST_MAX_CODES` text DEFAULT NULL, + `MOMENTUM_BACKTEST_REST_SLEEP_SEC` text DEFAULT NULL, + `MOMENTUM_BACKTEST_REST_KIWOOM_MOCK` text DEFAULT NULL, + `MOMENTUM_DAILY_PROFIT_RISK_BUDGET_ENABLED` text DEFAULT NULL, + `MOMENTUM_DAILY_PROFIT_RISK_BUDGET_COOLDOWN_SEC` text DEFAULT NULL, + `MOMENTUM_TICK_SELL_ENABLED` text DEFAULT NULL, + `CONDITION_MOMENTUM_LS_NAME` text DEFAULT NULL, + `MOMENTUM_LIVE_UNIVERSE_HISTORY_SOURCE` text DEFAULT NULL, + `MOMENTUM_E_MIN_CHG_PCT` text DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=387 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `config_scalp` + +**PK**: `id` | AUTO_INCREMENT=391 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `int(11)` | NOT NULL | | PK · AUTO_INCREMENT | +| `created_at` | `varchar(30)` | NOT NULL | | 설정행 생성시각 | +| `SCALP_ATR_UP_MULT` | `text` | NULL | NULL | 스캘핑: ATR UP 배수 (SCALP_ATR_UP_MULT) | +| `SCALP_ATR_DOWN_MULT` | `text` | NULL | NULL | 스캘핑: ATR DOWN 배수 (SCALP_ATR_DOWN_MULT) | +| `SCALP_ATR_DROP_MULT` | `text` | NULL | NULL | 스캘핑: ATR 되돌림/하락 배수 (SCALP_ATR_DROP_MULT) | +| `SCALP_RSI_OVERSOLD` | `text` | NULL | NULL | 스캘핑: RSI과매도 (SCALP_RSI_OVERSOLD) | +| `SCALP_RSI_OVERBOUGHT` | `text` | NULL | NULL | 스캘핑: RSI과매수 (SCALP_RSI_OVERBOUGHT) | +| `SCALP_CANDLE_TIMEFRAME` | `text` | NULL | NULL | 스캘핑: CANDLE TIMEFRAME (SCALP_CANDLE_TIMEFRAME) | +| `SCALP_MARKET_OPEN_WAIT_MIN` | `text` | NULL | NULL | 스캘핑: MARKET OPEN WAIT 최소 (SCALP_MARKET_OPEN_WAIT_MIN) | +| `SCALP_GAP_FILL_LIMIT` | `text` | NULL | NULL | 스캘핑: GAP FILL LIMIT (SCALP_GAP_FILL_LIMIT) | +| `SCALP_MIN_VOLUME` | `text` | NULL | NULL | 스캘핑: 최소 거래량 UME (SCALP_MIN_VOLUME) | +| `KIS_SCALP_MM_CHANNEL` | `text` | NULL | NULL | 스캘핑: KIS SCALP MM CHANNEL (KIS_SCALP_MM_CHANNEL) | +| `SCALP_CANDLE_KEEP_DAYS` | `text` | NULL | NULL | 스캘핑: CANDLE KEEP DAYS (SCALP_CANDLE_KEEP_DAYS) | +| `SCALP_STOP_LOSS_PCT` | `text` | NULL | NULL | 스캘핑: 손절 % (SCALP_STOP_LOSS_PCT) | +| `SCALP_TAKE_PROFIT_PCT` | `text` | NULL | NULL | 스캘핑: 익절 % (SCALP_TAKE_PROFIT_PCT) | +| `SCALP_MIN_DROP_RATE` | `text` | NULL | NULL | 스캘핑: 최소 되돌림/하락 RATE (SCALP_MIN_DROP_RATE) | +| `SCALP_GAP_RETRY_SEC` | `text` | NULL | NULL | 스캘핑: GAP RETRY 초 (SCALP_GAP_RETRY_SEC) | +| `SCALP_REQUIRE_REVERSAL_CANDLE` | `text` | NULL | NULL | 스캘핑: REQUIRE REVERSAL CANDLE (SCALP_REQUIRE_REVERSAL_CANDLE) | +| `SCALP_COOLDOWN_SEC` | `text` | NULL | NULL | 스캘핑: 쿨다운 초 (SCALP_COOLDOWN_SEC) | +| `SCALP_TRAIL_TRIGGER_PCT` | `text` | NULL | NULL | 스캘핑: 트레일 TRIGGER % (SCALP_TRAIL_TRIGGER_PCT) | +| `SCALP_MIN_PROFIT_PCT` | `text` | NULL | NULL | 스캘핑: 최소 PROFIT % (SCALP_MIN_PROFIT_PCT) | +| `SCALP_HIGH_PRICE_CHASE_THRESHOLD` | `text` | NULL | NULL | 스캘핑: HIGH PRICE CHASE THRESHOLD (SCALP_HIGH_PRICE_CHASE_THRESHOLD) | +| `SCALP_MAX_DAILY_CHANGE_PCT` | `text` | NULL | NULL | 스캘핑: 일일매수횟수상한 CHANGE % (SCALP_MAX_DAILY_CHANGE_PCT) | +| `SCALP_MIN_PRICE` | `text` | NULL | NULL | 스캘핑: 최소가격 (SCALP_MIN_PRICE) | +| `SCALP_MAX_LOSS_PER_TRADE_KRW` | `text` | NULL | NULL | 스캘핑: 최대 LOSS PER TRADE 원 (SCALP_MAX_LOSS_PER_TRADE_KRW) | +| `SCALP_MIN_DROP_PCT_FOR_LOSS_CUT` | `text` | NULL | NULL | 스캘핑: 최소 되돌림/하락 % FOR LOSS CUT (SCALP_MIN_DROP_PCT_FOR_LOSS_CUT) | +| `SCALP_USE_DEFENSE_FILTERS` | `text` | NULL | NULL | 스캘핑: 사용_ 방어필터 필터 S (SCALP_USE_DEFENSE_FILTERS) | +| `SCALP_USE_MACD_CROSS` | `text` | NULL | NULL | 스캘핑: 사용_ MACD CROSS (SCALP_USE_MACD_CROSS) | +| `SCALP_MACD_FAST` | `text` | NULL | NULL | 스캘핑: MACD FAST (SCALP_MACD_FAST) | +| `SCALP_MACD_SLOW` | `text` | NULL | NULL | 스캘핑: MACD SLOW (SCALP_MACD_SLOW) | +| `SCALP_MACD_SIGNAL` | `text` | NULL | NULL | 스캘핑: MACD SIGNAL (SCALP_MACD_SIGNAL) | +| `SCALP_STOCH_K_PERIOD` | `text` | NULL | NULL | 스캘핑: STOCH K 기간 (SCALP_STOCH_K_PERIOD) | +| `SCALP_STOCH_D_PERIOD` | `text` | NULL | NULL | 스캘핑: STOCH D 기간 (SCALP_STOCH_D_PERIOD) | +| `SCALP_STOCH_SLOW` | `text` | NULL | NULL | 스캘핑: STOCH SLOW (SCALP_STOCH_SLOW) | +| `SCALP_LIVE_BACKTEST_ALIGN` | `text` | NULL | NULL | 스캘핑: LIVE BACKTEST ALIGN (SCALP_LIVE_BACKTEST_ALIGN) | +| `SCALP_LIVE_SIGNAL_LOOKBACK_BARS` | `text` | NULL | NULL | 스캘핑: LIVE SIGNAL LOOKBACK 봉수 (SCALP_LIVE_SIGNAL_LOOKBACK_BARS) | +| `SCALP_UNIVERSE_SOURCE` | `text` | NULL | NULL | 스캘핑: 유니버스소스 (SCALP_UNIVERSE_SOURCE) | +| `RANK_SCALP_SORT` | `text` | NULL | NULL | 스캘핑: RANK SCALP SORT (RANK_SCALP_SORT) | +| `RANK_SCALP_LIMIT` | `text` | NULL | NULL | 스캘핑: RANK SCALP LIMIT (RANK_SCALP_LIMIT) | +| `CONDITION_SCALP_NAME` | `text` | NULL | NULL | 스캘핑: CONDITION SCALP NAME (CONDITION_SCALP_NAME) | +| `CONDITION_SCALP_SEQ` | `text` | NULL | NULL | 스캘핑: CONDITION SCALP SEQ (CONDITION_SCALP_SEQ) | +| `SCALP_MAX_BUY_AMOUNT` | `text` | NULL | NULL | 스캘핑: 1회최대매수금액 (SCALP_MAX_BUY_AMOUNT) | +| `SCALP_CAND_LIMIT` | `text` | NULL | NULL | 스캘핑: 후보상한 (SCALP_CAND_LIMIT) | +| `SCALP_TIME_START` | `text` | NULL | NULL | 스캘핑: 매수시작 (SCALP_TIME_START) | +| `SCALP_TIME_END` | `text` | NULL | NULL | 스캘핑: 매수종료 (SCALP_TIME_END) | +| `SCALP_MODE` | `text` | NULL | NULL | 스캘핑: 모드 (SCALP_MODE) | +| `SCALP_MOM_RSI_MIN` | `text` | NULL | NULL | 스캘핑: MOM RSI하한 (SCALP_MOM_RSI_MIN) | +| `SCALP_MOM_RSI_MAX` | `text` | NULL | NULL | 스캘핑: MOM RSI상한 (SCALP_MOM_RSI_MAX) | +| `SCALP_MOM_VOL_MULT` | `text` | NULL | NULL | 스캘핑: MOM 거래량배수 (SCALP_MOM_VOL_MULT) | +| `SCALP_MOM_VOL_WIN` | `text` | NULL | NULL | 스캘핑: MOM 거래량윈도우 (SCALP_MOM_VOL_WIN) | +| `SCALP_MOM_TIME_END_HM` | `text` | NULL | NULL | 스캘핑: MOM 매수종료 HHMM (SCALP_MOM_TIME_END_HM) | +| `SCALP_MAX_DAILY` | `text` | NULL | NULL | 스캘핑: 일일매수횟수상한 (SCALP_MAX_DAILY) | +| `SCALP_MAX_STOCKS` | `text` | NULL | NULL | 스캘핑: 동시보유상한 (SCALP_MAX_STOCKS) | +| `SCALP_TP_MAX_PCT` | `text` | NULL | NULL | 스캘핑: TP 최대 % (SCALP_TP_MAX_PCT) | +| `SCALP_SHOULDER_MIN_HIGH_PCT` | `text` | NULL | NULL | 스캘핑: 어깨무장고점 % (SCALP_SHOULDER_MIN_HIGH_PCT) | +| `SCALP_SHOULDER_CUT_PCT` | `text` | NULL | NULL | 스캘핑: 어깨컷 % (SCALP_SHOULDER_CUT_PCT) | +| `SCALP_TOTAL_BUDGET_KRW` | `text` | NULL | NULL | 스캘핑: 총운용한도 원 (SCALP_TOTAL_BUDGET_KRW) | +| `STRATEGY_SCALP_ENABLED` | `text` | NULL | NULL | 스캘핑 ON/OFF. 다음루프 신규매수중단(보유청산유지). WS구독해제 안함. 기동시OFF→켜려면재시작 | +| `SCALP_DAILY_PROFIT_TARGET_ENABLED` | `text` | NULL | NULL | 스캘핑: 일일익절 목표 ON/OFF (SCALP_DAILY_PROFIT_TARGET_ENABLED) | +| `SCALP_DAILY_PROFIT_TARGET_KRW` | `text` | NULL | NULL | 스캘핑: 일일익절 목표 원 (SCALP_DAILY_PROFIT_TARGET_KRW) | +| `SCALP_DAILY_PROFIT_TARGET_PCT` | `text` | NULL | NULL | 스캘핑: 일일익절 목표 % (SCALP_DAILY_PROFIT_TARGET_PCT) | +| `SCALP_DAILY_PROFIT_HALT_NEW_BUYS` | `text` | NULL | NULL | 스캘핑: 일일익절 중단 NEW BUYS (SCALP_DAILY_PROFIT_HALT_NEW_BUYS) | +| `SCALP_DAILY_PROFIT_MODE` | `text` | NULL | NULL | 스캘핑: 일일익절 모드 (SCALP_DAILY_PROFIT_MODE) | +| `SCALP_DAILY_PROFIT_TRAIL_TIERS` | `text` | NULL | NULL | 스캘핑: 일일익절 트레일 다단 (SCALP_DAILY_PROFIT_TRAIL_TIERS) | +| `SCALP_DAILY_PROFIT_TRAIL_DROP_PCT` | `text` | NULL | NULL | 스캘핑: 일일익절 트레일 되돌림/하락 % (SCALP_DAILY_PROFIT_TRAIL_DROP_PCT) | +| `SCALP_DAILY_PROFIT_TRAIL_ARM_KRW` | `text` | NULL | NULL | 스캘핑: 일일익절 트레일무장 원 (SCALP_DAILY_PROFIT_TRAIL_ARM_KRW) | +| `SCALP_DAILY_PROFIT_TRAIL_ARM_PCT` | `text` | NULL | NULL | 스캘핑: 일일익절 트레일무장 % (SCALP_DAILY_PROFIT_TRAIL_ARM_PCT) | +| `SCALP_RSI_PERIOD` | `text` | NULL | NULL | 스캘핑: RSI 기간 (SCALP_RSI_PERIOD) | +| `SCALP_GRID_TRIGGER_RSI_OVERSOLD` | `text` | NULL | NULL | 스캘핑: GRID TRIGGER RSI과매도 (SCALP_GRID_TRIGGER_RSI_OVERSOLD) | +| `SCALP_GRID_TRIGGER_DROP_RATE` | `text` | NULL | NULL | 스캘핑: GRID TRIGGER 되돌림/하락 RATE (SCALP_GRID_TRIGGER_DROP_RATE) | +| `SCALP_GRID_TRIGGER_HIGH_CHASE_THR` | `text` | NULL | NULL | 스캘핑: GRID TRIGGER 고점추격 THR (SCALP_GRID_TRIGGER_HIGH_CHASE_THR) | +| `SCALP_GRID_TRIGGER_VOL_MULT` | `text` | NULL | NULL | 스캘핑: GRID TRIGGER 거래량배수 (SCALP_GRID_TRIGGER_VOL_MULT) | +| `SCALP_GRID_TRIGGER_REQUIRE_REVERSAL` | `text` | NULL | NULL | 스캘핑: GRID TRIGGER REQUIRE REVERSAL (SCALP_GRID_TRIGGER_REQUIRE_REVERSAL) | +| `SCALP_GRID_EXIT_SL_PCT` | `text` | NULL | NULL | 스캘핑: GRID EXIT SL % (SCALP_GRID_EXIT_SL_PCT) | +| `SCALP_GRID_EXIT_TP_PCT` | `text` | NULL | NULL | 스캘핑: GRID EXIT TP % (SCALP_GRID_EXIT_TP_PCT) | +| `SCALP_GRID_EXIT_TP_MAX_PCT` | `text` | NULL | NULL | 스캘핑: GRID EXIT TP 최대 % (SCALP_GRID_EXIT_TP_MAX_PCT) | +| `SCALP_GRID_EXIT_SHOULDER_MIN_HIGH_PCT` | `text` | NULL | NULL | 스캘핑: GRID EXIT 어깨무장고점 % (SCALP_GRID_EXIT_SHOULDER_MIN_HIGH_PCT) | +| `SCALP_GRID_EXIT_SHOULDER_CUT_PCT` | `text` | NULL | NULL | 스캘핑: GRID EXIT 어깨컷 % (SCALP_GRID_EXIT_SHOULDER_CUT_PCT) | +| `SCALP_GRID_EXIT_MIN_HOLD_SEC` | `text` | NULL | NULL | 스캘핑: GRID EXIT 최소보유 초 (SCALP_GRID_EXIT_MIN_HOLD_SEC) | +| `SCALP_SCORE_MDD_FLOOR` | `text` | NULL | NULL | 스캘핑: SCORE MDD FLOOR (SCALP_SCORE_MDD_FLOOR) | +| `CONDITION_SCALP_KIWOOM_SEQ` | `text` | NULL | NULL | 스캘핑: CONDITION SCALP KIWOOM SEQ (CONDITION_SCALP_KIWOOM_SEQ) | +| `CONDITION_SCALP_KIWOOM_NAME` | `text` | NULL | NULL | 스캘핑: CONDITION SCALP KIWOOM NAME (CONDITION_SCALP_KIWOOM_NAME) | +| `SCALP_SKIP_HTS_SCAN_DUPES` | `text` | NULL | NULL | 스캘핑: HTS스캔중복스킵 (SCALP_SKIP_HTS_SCAN_DUPES) | +| `SCALP_GRID_SKIP_HTS_SCAN_DUPES` | `text` | NULL | NULL | 스캘핑: GRID HTS스캔중복스킵 (SCALP_GRID_SKIP_HTS_SCAN_DUPES) | +| `SCALP_BACKTEST_USE_TICK_EXIT` | `text` | NULL | NULL | 스캘핑 틱 청산 · ON=ws_ticks 시간순 청산(실매 폴링 정합) · OFF=OHLC intrabar | +| `SCALP_BACKTEST_USE_TICK_ENTRY` | `text` | NULL | NULL | 스캘핑 틱 진입 · ON=예약 체결 시 해당 분 첫 틱가 · OFF=분봉 시가 | +| `SCALP_BACKTEST_TICK_FALLBACK_OHLC` | `text` | NULL | NULL | ⚠ 스캘핑 OHLC 폴백 (정합위험·금지) · 절대금지 — 틱 청산 ON 시 엔진 무시 | +| `SCALP_BACKTEST_POLL_MS` | `text` | NULL | NULL | 스캘핑 틱청산 폴링(ms) · 실매 STRATEGY_LOOP_SLEEP≈0.1초 정합 · 하한 50ms | +| `SCALP_BACKTEST_SELL_SLIP_PCT` | `text` | NULL | NULL | 스캘핑: BACKTEST SELL SLIP % (SCALP_BACKTEST_SELL_SLIP_PCT) | +| `SCALP_MIN_HOLD_SEC` | `text` | NULL | NULL | 스캘핑: 최소보유 초 (SCALP_MIN_HOLD_SEC) | +| `SCALP_ORDERBOOK_FILTER_ENABLED` | `text` | NULL | NULL | 스캘핑 호가필터 ON · ON=스프레드·잔량비·얇은호가·매도벽으로 매수 차단 · OFF=저장만 | +| `SCALP_ORDERBOOK_MAX_SPREAD_PCT` | `text` | NULL | NULL | 스캘핑 스프레드상한(%) · 0.45=0.45% · 그리드 max_spread_pct 앵커 | +| `SCALP_ORDERBOOK_ENTRY_BID_LEVELS` | `text` | NULL | NULL | 스캘핑 호가단계(N호가) | +| `SCALP_ORDERBOOK_ENTRY_BID_DEPTH_MULT` | `text` | NULL | NULL | 스캘핑 매수호가깊이 배수 · 얇은호가 탈락 · 필요수량×N | +| `SCALP_ORDERBOOK_ENTRY_ASK_MAX_MULT` | `text` | NULL | NULL | 스캘핑 매도벽 허용배수 · 필요수량×N · 그리드 ask_max_mult | +| `SCALP_ORDERBOOK_MIN_BID_ASK_RATIO` | `text` | NULL | NULL | 스캘핑 매수/매도잔량비 하한 · 그리드 min_bid_ask_ratio | +| `SCALP_BACKTEST_CANDLE_WARMUP_BARS` | `text` | NULL | NULL | 스캘핑: BACKTEST CANDLE WARMUP 봉수 (SCALP_BACKTEST_CANDLE_WARMUP_BARS) | +| `SCALP_UNIVERSE_EXIT_DEBOUNCE_SEC` | `text` | NULL | NULL | 스캘핑: 유니버스 EXIT DEBOUNCE 초 (SCALP_UNIVERSE_EXIT_DEBOUNCE_SEC) | +| `SCALP_EOD_ENABLED` | `text` | NULL | NULL | EOD 당일청산 사용 · ON=설정 시각 이후 보유 전량 장마감청산 · OFF=익절·손절·어깨만 | +| `SCALP_EOD_HM` | `text` | NULL | NULL | EOD 시각 (HH:MM) · 실매 기존 15:25 · 정규장 15:30 마감 직전 순차매도 | +| `SCALP_DAILY_PROFIT_RISK_BUDGET_ENABLED` | `text` | NULL | NULL | 스캘핑: 일일익절 RISK 한도 ON/OFF (SCALP_DAILY_PROFIT_RISK_BUDGET_ENABLED) | +| `SCALP_DAILY_PROFIT_RISK_BUDGET_COOLDOWN_SEC` | `text` | NULL | NULL | 스캘핑: 일일익절 RISK 한도 쿨다운 초 (SCALP_DAILY_PROFIT_RISK_BUDGET_COOLDOWN_SEC) | +| `SCALP_TICK_SELL_ENABLED` | `text` | NULL | NULL | 스캘핑: 틱매도 ON/OFF (SCALP_TICK_SELL_ENABLED) | +| `SCALP_LIVE_ALIGN_USE_FORMING_BAR` | `text` | NULL | NULL | 스캘핑: LIVE ALIGN 사용_ FORMING BAR (SCALP_LIVE_ALIGN_USE_FORMING_BAR) | +| `CONDITION_SCALP_LS_NAME` | `text` | NULL | NULL | 스캘핑: CONDITION SCALP LS NAME (CONDITION_SCALP_LS_NAME) | +| `SCALP_LIVE_UNIVERSE_HISTORY_SOURCE` | `text` | NULL | NULL | 스캘핑: LIVE 유니버스 HISTORY SOURCE (SCALP_LIVE_UNIVERSE_HISTORY_SOURCE) | +| `SCALP_CANDLE_GAP_FORCE_SEC` | `text` | NULL | NULL | 스캘핑: CANDLE GAP FORCE 초 (SCALP_CANDLE_GAP_FORCE_SEC) | +| `SCALP_CANDLE_SHORT_LOG_SEC` | `text` | NULL | NULL | 스캘핑: CANDLE SHORT LOG 초 (SCALP_CANDLE_SHORT_LOG_SEC) | + +
+DDL 원문 + +```sql +CREATE TABLE `config_scalp` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `created_at` varchar(30) NOT NULL, + `SCALP_ATR_UP_MULT` text DEFAULT NULL, + `SCALP_ATR_DOWN_MULT` text DEFAULT NULL, + `SCALP_ATR_DROP_MULT` text DEFAULT NULL, + `SCALP_RSI_OVERSOLD` text DEFAULT NULL, + `SCALP_RSI_OVERBOUGHT` text DEFAULT NULL, + `SCALP_CANDLE_TIMEFRAME` text DEFAULT NULL, + `SCALP_MARKET_OPEN_WAIT_MIN` text DEFAULT NULL, + `SCALP_GAP_FILL_LIMIT` text DEFAULT NULL, + `SCALP_MIN_VOLUME` text DEFAULT NULL, + `KIS_SCALP_MM_CHANNEL` text DEFAULT NULL, + `SCALP_CANDLE_KEEP_DAYS` text DEFAULT NULL, + `SCALP_STOP_LOSS_PCT` text DEFAULT NULL, + `SCALP_TAKE_PROFIT_PCT` text DEFAULT NULL, + `SCALP_MIN_DROP_RATE` text DEFAULT NULL, + `SCALP_GAP_RETRY_SEC` text DEFAULT NULL, + `SCALP_REQUIRE_REVERSAL_CANDLE` text DEFAULT NULL, + `SCALP_COOLDOWN_SEC` text DEFAULT NULL, + `SCALP_TRAIL_TRIGGER_PCT` text DEFAULT NULL, + `SCALP_MIN_PROFIT_PCT` text DEFAULT NULL, + `SCALP_HIGH_PRICE_CHASE_THRESHOLD` text DEFAULT NULL, + `SCALP_MAX_DAILY_CHANGE_PCT` text DEFAULT NULL, + `SCALP_MIN_PRICE` text DEFAULT NULL, + `SCALP_MAX_LOSS_PER_TRADE_KRW` text DEFAULT NULL, + `SCALP_MIN_DROP_PCT_FOR_LOSS_CUT` text DEFAULT NULL, + `SCALP_USE_DEFENSE_FILTERS` text DEFAULT NULL, + `SCALP_USE_MACD_CROSS` text DEFAULT NULL, + `SCALP_MACD_FAST` text DEFAULT NULL, + `SCALP_MACD_SLOW` text DEFAULT NULL, + `SCALP_MACD_SIGNAL` text DEFAULT NULL, + `SCALP_STOCH_K_PERIOD` text DEFAULT NULL, + `SCALP_STOCH_D_PERIOD` text DEFAULT NULL, + `SCALP_STOCH_SLOW` text DEFAULT NULL, + `SCALP_LIVE_BACKTEST_ALIGN` text DEFAULT NULL, + `SCALP_LIVE_SIGNAL_LOOKBACK_BARS` text DEFAULT NULL, + `SCALP_UNIVERSE_SOURCE` text DEFAULT NULL, + `RANK_SCALP_SORT` text DEFAULT NULL, + `RANK_SCALP_LIMIT` text DEFAULT NULL, + `CONDITION_SCALP_NAME` text DEFAULT NULL, + `CONDITION_SCALP_SEQ` text DEFAULT NULL, + `SCALP_MAX_BUY_AMOUNT` text DEFAULT NULL, + `SCALP_CAND_LIMIT` text DEFAULT NULL, + `SCALP_TIME_START` text DEFAULT NULL, + `SCALP_TIME_END` text DEFAULT NULL, + `SCALP_MODE` text DEFAULT NULL, + `SCALP_MOM_RSI_MIN` text DEFAULT NULL, + `SCALP_MOM_RSI_MAX` text DEFAULT NULL, + `SCALP_MOM_VOL_MULT` text DEFAULT NULL, + `SCALP_MOM_VOL_WIN` text DEFAULT NULL, + `SCALP_MOM_TIME_END_HM` text DEFAULT NULL, + `SCALP_MAX_DAILY` text DEFAULT NULL, + `SCALP_MAX_STOCKS` text DEFAULT NULL, + `SCALP_TP_MAX_PCT` text DEFAULT NULL, + `SCALP_SHOULDER_MIN_HIGH_PCT` text DEFAULT NULL, + `SCALP_SHOULDER_CUT_PCT` text DEFAULT NULL, + `SCALP_TOTAL_BUDGET_KRW` text DEFAULT NULL, + `STRATEGY_SCALP_ENABLED` text DEFAULT NULL, + `SCALP_DAILY_PROFIT_TARGET_ENABLED` text DEFAULT NULL, + `SCALP_DAILY_PROFIT_TARGET_KRW` text DEFAULT NULL, + `SCALP_DAILY_PROFIT_TARGET_PCT` text DEFAULT NULL, + `SCALP_DAILY_PROFIT_HALT_NEW_BUYS` text DEFAULT NULL, + `SCALP_DAILY_PROFIT_MODE` text DEFAULT NULL, + `SCALP_DAILY_PROFIT_TRAIL_TIERS` text DEFAULT NULL, + `SCALP_DAILY_PROFIT_TRAIL_DROP_PCT` text DEFAULT NULL, + `SCALP_DAILY_PROFIT_TRAIL_ARM_KRW` text DEFAULT NULL, + `SCALP_DAILY_PROFIT_TRAIL_ARM_PCT` text DEFAULT NULL, + `SCALP_RSI_PERIOD` text DEFAULT NULL, + `SCALP_GRID_TRIGGER_RSI_OVERSOLD` text DEFAULT NULL, + `SCALP_GRID_TRIGGER_DROP_RATE` text DEFAULT NULL, + `SCALP_GRID_TRIGGER_HIGH_CHASE_THR` text DEFAULT NULL, + `SCALP_GRID_TRIGGER_VOL_MULT` text DEFAULT NULL, + `SCALP_GRID_TRIGGER_REQUIRE_REVERSAL` text DEFAULT NULL, + `SCALP_GRID_EXIT_SL_PCT` text DEFAULT NULL, + `SCALP_GRID_EXIT_TP_PCT` text DEFAULT NULL, + `SCALP_GRID_EXIT_TP_MAX_PCT` text DEFAULT NULL, + `SCALP_GRID_EXIT_SHOULDER_MIN_HIGH_PCT` text DEFAULT NULL, + `SCALP_GRID_EXIT_SHOULDER_CUT_PCT` text DEFAULT NULL, + `SCALP_GRID_EXIT_MIN_HOLD_SEC` text DEFAULT NULL, + `SCALP_SCORE_MDD_FLOOR` text DEFAULT NULL, + `CONDITION_SCALP_KIWOOM_SEQ` text DEFAULT NULL, + `CONDITION_SCALP_KIWOOM_NAME` text DEFAULT NULL, + `SCALP_SKIP_HTS_SCAN_DUPES` text DEFAULT NULL, + `SCALP_GRID_SKIP_HTS_SCAN_DUPES` text DEFAULT NULL, + `SCALP_BACKTEST_USE_TICK_EXIT` text DEFAULT NULL, + `SCALP_BACKTEST_USE_TICK_ENTRY` text DEFAULT NULL, + `SCALP_BACKTEST_TICK_FALLBACK_OHLC` text DEFAULT NULL, + `SCALP_BACKTEST_POLL_MS` text DEFAULT NULL, + `SCALP_BACKTEST_SELL_SLIP_PCT` text DEFAULT NULL, + `SCALP_MIN_HOLD_SEC` text DEFAULT NULL, + `SCALP_ORDERBOOK_FILTER_ENABLED` text DEFAULT NULL, + `SCALP_ORDERBOOK_MAX_SPREAD_PCT` text DEFAULT NULL, + `SCALP_ORDERBOOK_ENTRY_BID_LEVELS` text DEFAULT NULL, + `SCALP_ORDERBOOK_ENTRY_BID_DEPTH_MULT` text DEFAULT NULL, + `SCALP_ORDERBOOK_ENTRY_ASK_MAX_MULT` text DEFAULT NULL, + `SCALP_ORDERBOOK_MIN_BID_ASK_RATIO` text DEFAULT NULL, + `SCALP_BACKTEST_CANDLE_WARMUP_BARS` text DEFAULT NULL, + `SCALP_UNIVERSE_EXIT_DEBOUNCE_SEC` text DEFAULT NULL, + `SCALP_EOD_ENABLED` text DEFAULT NULL, + `SCALP_EOD_HM` text DEFAULT NULL, + `SCALP_DAILY_PROFIT_RISK_BUDGET_ENABLED` text DEFAULT NULL, + `SCALP_DAILY_PROFIT_RISK_BUDGET_COOLDOWN_SEC` text DEFAULT NULL, + `SCALP_TICK_SELL_ENABLED` text DEFAULT NULL, + `SCALP_LIVE_ALIGN_USE_FORMING_BAR` text DEFAULT NULL, + `CONDITION_SCALP_LS_NAME` text DEFAULT NULL, + `SCALP_LIVE_UNIVERSE_HISTORY_SOURCE` text DEFAULT NULL, + `SCALP_CANDLE_GAP_FORCE_SEC` text DEFAULT NULL, + `SCALP_CANDLE_SHORT_LOG_SEC` text DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=391 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `config_short` + +**PK**: `id` | AUTO_INCREMENT=388 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `int(11)` | NOT NULL | | PK · AUTO_INCREMENT | +| `created_at` | `varchar(30)` | NOT NULL | | 설정행 생성시각 | +| `STOP_LOSS_PCT` | `text` | NULL | NULL | 꼬리잡기: 손절 % (STOP_LOSS_PCT) | +| `SHOULDER_CUT_PCT` | `text` | NULL | NULL | 꼬리잡기: 어깨컷 % (SHOULDER_CUT_PCT) | +| `SHOULDER_MIN_HIGH_PCT` | `text` | NULL | NULL | 꼬리잡기: 어깨무장고점 % (SHOULDER_MIN_HIGH_PCT) | +| `SHOULDER_MIN_NET_PCT` | `text` | NULL | NULL | 꼬리잡기: 어깨 최소 NET % (SHOULDER_MIN_NET_PCT) | +| `STOP_ATR_MULTIPLIER_TAIL` | `text` | NULL | NULL | 꼬리잡기: STOP ATR 배수 IPLIER TAIL (STOP_ATR_MULTIPLIER_TAIL) | +| `TARGET_ATR_MULTIPLIER_TAIL` | `text` | NULL | NULL | 꼬리잡기: 목표 ATR 배수 IPLIER TAIL (TARGET_ATR_MULTIPLIER_TAIL) | +| `TAIL_ATR_SL_MIN_PCT` | `text` | NULL | NULL | 꼬리잡기: ATR손절 최소 % (TAIL_ATR_SL_MIN_PCT) | +| `TAIL_ATR_SL_MAX_PCT` | `text` | NULL | NULL | 꼬리잡기: ATR손절 최대 % (TAIL_ATR_SL_MAX_PCT) | +| `TAIL_ATR_TP_MIN_PCT` | `text` | NULL | NULL | 꼬리잡기: ATR익절 최소 % (TAIL_ATR_TP_MIN_PCT) | +| `TAIL_ATR_TP_MAX_PCT` | `text` | NULL | NULL | 꼬리잡기: ATR익절 최대 % (TAIL_ATR_TP_MAX_PCT) | +| `MIN_CANDLE_LEN_TAIL` | `text` | NULL | NULL | 꼬리잡기: 최소 CANDLE LEN TAIL (MIN_CANDLE_LEN_TAIL) | +| `MIN_PRICE_TAIL` | `text` | NULL | NULL | 꼬리잡기: 최소가격 TAIL (MIN_PRICE_TAIL) | +| `TAIL_SCORE_BASE` | `text` | NULL | NULL | 꼬리잡기: SCORE BASE (TAIL_SCORE_BASE) | +| `TAIL_SCORE_RATIO_MULT` | `text` | NULL | NULL | 꼬리잡기: SCORE RATIO 배수 (TAIL_SCORE_RATIO_MULT) | +| `TAIL_RATIO_MIN` | `text` | NULL | NULL | 꼬리잡기: RATIO 최소 (TAIL_RATIO_MIN) | +| `TAIL_PCT_MIN` | `text` | NULL | NULL | 꼬리잡기: % 최소 (TAIL_PCT_MIN) | +| `TAIL_SKIP_HTS_SCAN_DUPES` | `text` | NULL | NULL | 꼬리잡기: HTS스캔중복스킵 (TAIL_SKIP_HTS_SCAN_DUPES) | +| `TAIL_USE_INTRADAY_DROP` | `text` | NULL | NULL | 꼬리잡기: 사용_ INTRADAY 되돌림/하락 (TAIL_USE_INTRADAY_DROP) | +| `TAIL_USE_MA20_FILTER` | `text` | NULL | NULL | 꼬리잡기: 사용_ MA20 필터 (TAIL_USE_MA20_FILTER) | +| `TAIL_USE_RSI_FILTER` | `text` | NULL | NULL | 꼬리잡기: 사용_ RSI 필터 (TAIL_USE_RSI_FILTER) | +| `TAIL_USE_DAILY_RANGE_FILTER` | `text` | NULL | NULL | 꼬리잡기: 사용_ DAILY RANGE 필터 (TAIL_USE_DAILY_RANGE_FILTER) | +| `TAIL_USE_HIGH_CHASE_FILTER` | `text` | NULL | NULL | 꼬리잡기: 사용_ 고점추격 필터 (TAIL_USE_HIGH_CHASE_FILTER) | +| `TAIL_BAR_CHG_MIN_PCT` | `text` | NULL | NULL | 꼬리잡기: BAR CHG 최소 % (TAIL_BAR_CHG_MIN_PCT) | +| `TAIL_BAR_CHG_MAX_PCT` | `text` | NULL | NULL | 꼬리잡기: BAR CHG 최대 % (TAIL_BAR_CHG_MAX_PCT) | +| `TAIL_RSI_THRESHOLD` | `text` | NULL | NULL | 꼬리잡기: RSI THRESHOLD (TAIL_RSI_THRESHOLD) | +| `MIN_DROP_PCT_FOR_LOSS_CUT` | `text` | NULL | NULL | 꼬리잡기: 최소 되돌림/하락 % FOR LOSS CUT (MIN_DROP_PCT_FOR_LOSS_CUT) | +| `TAKE_PROFIT_PCT` | `text` | NULL | NULL | 꼬리잡기: 익절 % (TAKE_PROFIT_PCT) | +| `MIN_DROP_RATE` | `text` | NULL | NULL | 꼬리잡기: 최소 되돌림/하락 RATE (MIN_DROP_RATE) | +| `MIN_RECOVERY_RATIO_SHORT` | `text` | NULL | NULL | 꼬리잡기: 최소 RECOVERY RATIO SHORT (MIN_RECOVERY_RATIO_SHORT) | +| `KIS_SHORT_MM_CHANNEL` | `text` | NULL | NULL | 꼬리잡기: KIS SHORT MM CHANNEL (KIS_SHORT_MM_CHANNEL) | +| `SHORT_GAP_FILL_LIMIT` | `text` | NULL | NULL | 꼬리잡기: GAP FILL LIMIT (SHORT_GAP_FILL_LIMIT) | +| `MAX_RECOVERY_RATIO_3M` | `text` | NULL | NULL | 꼬리잡기: 최대 RECOVERY RATIO 3M (MAX_RECOVERY_RATIO_3M) | +| `TAIL_CANDLE_LOOKBACK` | `text` | NULL | NULL | 꼬리잡기: CANDLE LOOKBACK (TAIL_CANDLE_LOOKBACK) | +| `SHORT_LIVE_BACKTEST_ALIGN` | `text` | NULL | NULL | 꼬리잡기: LIVE BACKTEST ALIGN (SHORT_LIVE_BACKTEST_ALIGN) | +| `SHORT_LIVE_SIGNAL_LOOKBACK_BARS` | `text` | NULL | NULL | 꼬리잡기: LIVE SIGNAL LOOKBACK 봉수 (SHORT_LIVE_SIGNAL_LOOKBACK_BARS) | +| `SHORT_UNIVERSE_SOURCE` | `text` | NULL | NULL | 꼬리잡기 유니버스 · ls_condition=LS 동명 AFR · 시세는 키움/한투 유지 · 매니저 최초 기동은 봇 재시작 필요할 수 있음 · 키움으로 자동 폴백 안 함 | +| `RANK_SHORT_SORT` | `text` | NULL | NULL | 꼬리잡기: RANK SHORT SORT (RANK_SHORT_SORT) | +| `RANK_SHORT_LIMIT` | `text` | NULL | NULL | 꼬리잡기: RANK SHORT LIMIT (RANK_SHORT_LIMIT) | +| `CONDITION_SHORT_NAME` | `text` | NULL | NULL | 꼬리잡기: CONDITION SHORT NAME (CONDITION_SHORT_NAME) | +| `CONDITION_SHORT_SEQ` | `text` | NULL | NULL | 꼬리잡기: CONDITION SHORT SEQ (CONDITION_SHORT_SEQ) | +| `SHORT_MAX_BUY_AMOUNT` | `text` | NULL | NULL | 꼬리잡기: 1회최대매수금액 (SHORT_MAX_BUY_AMOUNT) | +| `TAIL_MAX_BUY_AMOUNT` | `text` | NULL | NULL | 꼬리잡기: 1회최대매수금액 (TAIL_MAX_BUY_AMOUNT) | +| `SHORT_CAND_LIMIT` | `text` | NULL | NULL | 꼬리잡기 후보 상한 · 0=무제한 · 조건검색/랭킹이 N개 줘도 상위 N개만 매수체크·WS구독 · 전 전략 합산이 키움 WS 100종 한도를 넘지 않게 조절 | +| `SHORT_TIME_START` | `text` | NULL | NULL | 꼬리잡기: 매수시작 (SHORT_TIME_START) | +| `SHORT_TIME_END` | `text` | NULL | NULL | 꼬리잡기: 매수종료 (SHORT_TIME_END) | +| `SHORT_MAX_STOCKS` | `text` | NULL | NULL | 꼬리잡기: 동시보유상한 (SHORT_MAX_STOCKS) | +| `SELL_USE_ORDERBOOK_ON_PROFIT` | `text` | NULL | NULL | 꼬리잡기: SELL 사용_ 호가 ON PROFIT (SELL_USE_ORDERBOOK_ON_PROFIT) | +| `SELL_ORDERBOOK_BID_LEVELS` | `text` | NULL | NULL | 꼬리잡기: SELL 호가 BID LEVELS (SELL_ORDERBOOK_BID_LEVELS) | +| `SELL_ORDERBOOK_DEPTH_MULT` | `text` | NULL | NULL | 꼬리잡기: SELL 호가 DEPTH 배수 (SELL_ORDERBOOK_DEPTH_MULT) | +| `SHORT_TOTAL_BUDGET_KRW` | `text` | NULL | NULL | 꼬리잡기: 총운용한도 원 (SHORT_TOTAL_BUDGET_KRW) | +| `STRATEGY_SHORT_ENABLED` | `text` | NULL | NULL | 꼬리잡기 ON/OFF. 다음루프 신규매수중단(보유청산유지). WS구독해제 안함. 기동시OFF→켜려면재시작 | +| `TAIL_MIN_DROP_RATE` | `text` | NULL | NULL | 꼬리잡기: 최소 되돌림/하락 RATE (TAIL_MIN_DROP_RATE) | +| `TAIL_MIN_RECOVERY_RATIO` | `text` | NULL | NULL | 꼬리잡기: 최소 RECOVERY RATIO (TAIL_MIN_RECOVERY_RATIO) | +| `TAIL_MAX_RECOVERY_3M` | `text` | NULL | NULL | 꼬리잡기: 최대 RECOVERY 3M (TAIL_MAX_RECOVERY_3M) | +| `TAIL_STOP_LOSS_PCT` | `text` | NULL | NULL | 꼬리잡기: 손절 % (TAIL_STOP_LOSS_PCT) | +| `TAIL_TAKE_PROFIT_PCT` | `text` | NULL | NULL | 꼬리잡기: 익절 % (TAIL_TAKE_PROFIT_PCT) | +| `TAIL_SHOULDER_MIN_HIGH_PCT` | `text` | NULL | NULL | 꼬리잡기: 어깨무장고점 % (TAIL_SHOULDER_MIN_HIGH_PCT) | +| `TAIL_SHOULDER_CUT_PCT` | `text` | NULL | NULL | 꼬리잡기: 어깨컷 % (TAIL_SHOULDER_CUT_PCT) | +| `TAIL_STOP_ATR_MULT` | `text` | NULL | NULL | 꼬리잡기: STOP ATR 배수 (TAIL_STOP_ATR_MULT) | +| `TAIL_TARGET_ATR_MULT` | `text` | NULL | NULL | 꼬리잡기: 목표 ATR 배수 (TAIL_TARGET_ATR_MULT) | +| `TAIL_MIN_PRICE` | `text` | NULL | NULL | 꼬리잡기: 최소가격 (TAIL_MIN_PRICE) | +| `TAIL_MAX_DAILY_CHG` | `text` | NULL | NULL | 꼬리잡기: 당일등락상한 (TAIL_MAX_DAILY_CHG) | +| `TAIL_MA20_MAX_ABOVE_PCT` | `text` | NULL | NULL | 꼬리잡기: MA20 최대 ABOVE % (TAIL_MA20_MAX_ABOVE_PCT) | +| `TAIL_MAX_LOSS_KRW` | `text` | NULL | NULL | 꼬리잡기: 최대 LOSS 원 (TAIL_MAX_LOSS_KRW) | +| `TAIL_MIN_DROP_FOR_LOSS_CUT` | `text` | NULL | NULL | 꼬리잡기: 최소 되돌림/하락 FOR LOSS CUT (TAIL_MIN_DROP_FOR_LOSS_CUT) | +| `TAIL_RSI_PERIOD` | `text` | NULL | NULL | 꼬리잡기: RSI 기간 (TAIL_RSI_PERIOD) | +| `TAIL_COOLDOWN_SEC` | `text` | NULL | NULL | 꼬리잡기: 쿨다운 초 (TAIL_COOLDOWN_SEC) | +| `TAIL_MAX_DAILY` | `text` | NULL | NULL | 꼬리잡기: 일일매수횟수상한 (TAIL_MAX_DAILY) | +| `TAIL_HIGH_CHASE_THR` | `text` | NULL | NULL | 꼬리잡기: 고점추격 THR (TAIL_HIGH_CHASE_THR) | +| `TAIL_TIME_START` | `text` | NULL | NULL | 꼬리잡기: 매수시작 (TAIL_TIME_START) | +| `TAIL_TIME_END` | `text` | NULL | NULL | 꼬리잡기: 매수종료 (TAIL_TIME_END) | +| `TAIL_MAX_STOCKS` | `text` | NULL | NULL | 꼬리잡기 동시 종목 | +| `TAIL_TOTAL_BUDGET_KRW` | `text` | NULL | NULL | 꼬리잡기 총 한도(원) | +| `TAIL_SLOT_MONEY` | `text` | NULL | NULL | 꼬리잡기 슬롯(원) | +| `TAIL_ENTRY_MODE` | `text` | NULL | NULL | 꼬리잡기: ENTRY 모드 (TAIL_ENTRY_MODE) | +| `TAIL_LIMIT_ATR_MULT` | `text` | NULL | NULL | 꼬리잡기: LIMIT ATR 배수 (TAIL_LIMIT_ATR_MULT) | +| `TAIL_LIMIT_ANCHOR` | `text` | NULL | NULL | 꼬리잡기: LIMIT ANCHOR (TAIL_LIMIT_ANCHOR) | +| `TAIL_LIMIT_VALID_BARS` | `text` | NULL | NULL | 꼬리잡기: LIMIT VALID 봉수 (TAIL_LIMIT_VALID_BARS) | +| `TAIL_LIMIT_FILL_SLIP_PCT` | `text` | NULL | NULL | 꼬리잡기: LIMIT FILL SLIP % (TAIL_LIMIT_FILL_SLIP_PCT) | +| `TAIL_MIN_INVEST_RATIO_OF_SLOT` | `text` | NULL | NULL | 꼬리잡기: 최소 INVEST RATIO OF SLOT (TAIL_MIN_INVEST_RATIO_OF_SLOT) | +| `TAIL_PARAM_SEARCH_ENTRY_MODE` | `text` | NULL | NULL | 꼬리잡기: PARAM SEARCH ENTRY 모드 (TAIL_PARAM_SEARCH_ENTRY_MODE) | +| `TAIL_GRID_FAST_MAX_DAILY_CHG` | `text` | NULL | NULL | 꼬리잡기: GRID FAST 당일등락상한 (TAIL_GRID_FAST_MAX_DAILY_CHG) | +| `TAIL_GRID_COARSE_MAX_DAILY_CHG` | `text` | NULL | NULL | 꼬리잡기: GRID COARSE 당일등락상한 (TAIL_GRID_COARSE_MAX_DAILY_CHG) | +| `TAIL_GRID_FAST_LIMIT_ATR_MULT` | `text` | NULL | NULL | 꼬리잡기: GRID FAST LIMIT ATR 배수 (TAIL_GRID_FAST_LIMIT_ATR_MULT) | +| `TAIL_GRID_COARSE_LIMIT_ATR_MULT` | `text` | NULL | NULL | 꼬리잡기: GRID COARSE LIMIT ATR 배수 (TAIL_GRID_COARSE_LIMIT_ATR_MULT) | +| `TAIL_RATCHET_TIERS` | `text` | NULL | NULL | 꼬리잡기: 다단래칫 (TAIL_RATCHET_TIERS) | +| `TAIL_MAX_HOLD_BARS` | `text` | NULL | NULL | 꼬리잡기: 최대보유봉 (TAIL_MAX_HOLD_BARS) | +| `TAIL_BACKTEST_USE_TICK_DB` | `text` | NULL | NULL | 꼬리잡기: BACKTEST 사용_ TICK DB (TAIL_BACKTEST_USE_TICK_DB) | +| `TAIL_BACKTEST_TICK_FALLBACK_OHLC` | `text` | NULL | NULL | ⚠ 꼬리 OHLC 폴백 (정합위험·금지) · 절대금지: 틱 모드에서 OHLC로 체결·고점 변조. 엔진은 틱 청산 ON 시 무시. Optuna/파람 강제 OFF | +| `TAIL_TRAIL_PCT` | `text` | NULL | NULL | 꼬리잡기: 트레일 % (TAIL_TRAIL_PCT) | +| `TAIL_TRAIL_ARM_PCT` | `text` | NULL | NULL | 꼬리잡기: 트레일무장 % (TAIL_TRAIL_ARM_PCT) | +| `TAIL_PATTERN_HAMMER` | `text` | NULL | NULL | 꼬리잡기: 패턴 HAMMER (TAIL_PATTERN_HAMMER) | +| `TAIL_PATTERN_PIN` | `text` | NULL | NULL | 꼬리잡기: 패턴 PIN (TAIL_PATTERN_PIN) | +| `TAIL_PATTERN_ENGULFING` | `text` | NULL | NULL | 꼬리잡기: 패턴 ENGULFING (TAIL_PATTERN_ENGULFING) | +| `TAIL_PATTERN_PIERCING` | `text` | NULL | NULL | 꼬리잡기: 패턴 PIERCING (TAIL_PATTERN_PIERCING) | +| `TAIL_PATTERN_HARAMI` | `text` | NULL | NULL | 꼬리잡기: 패턴 HARAMI (TAIL_PATTERN_HARAMI) | +| `TAIL_PATTERN_DOJI` | `text` | NULL | NULL | 꼬리잡기: 패턴 DOJI (TAIL_PATTERN_DOJI) | +| `TAIL_PATTERN_MORNING_STAR` | `text` | NULL | NULL | 꼬리잡기: 패턴 MORNING STAR (TAIL_PATTERN_MORNING_STAR) | +| `TAIL_PIN_CLOSE_UPPER_RATIO` | `text` | NULL | NULL | 꼬리잡기: PIN CLOSE UPPER RATIO (TAIL_PIN_CLOSE_UPPER_RATIO) | +| `TAIL_PIN_MAX_UPPER_TAIL_RATIO` | `text` | NULL | NULL | 꼬리잡기: PIN 최대 UPPER TAIL RATIO (TAIL_PIN_MAX_UPPER_TAIL_RATIO) | +| `TAIL_ENGULF_MIN_BODY_RATIO` | `text` | NULL | NULL | 꼬리잡기: ENGULF 최소 BODY RATIO (TAIL_ENGULF_MIN_BODY_RATIO) | +| `TAIL_PIERCING_PENETRATE_RATIO` | `text` | NULL | NULL | 꼬리잡기: PIERCING PENETRATE RATIO (TAIL_PIERCING_PENETRATE_RATIO) | +| `TAIL_HARAMI_MAX_BODY_RATIO` | `text` | NULL | NULL | 꼬리잡기: HARAMI 최대 BODY RATIO (TAIL_HARAMI_MAX_BODY_RATIO) | +| `TAIL_DOJI_BODY_MAX_RATIO` | `text` | NULL | NULL | 꼬리잡기: DOJI BODY 최대 RATIO (TAIL_DOJI_BODY_MAX_RATIO) | +| `TAIL_MORNING_STAR_BODY_MAX_RATIO` | `text` | NULL | NULL | 꼬리잡기: MORNING STAR BODY 최대 RATIO (TAIL_MORNING_STAR_BODY_MAX_RATIO) | +| `TAIL_ORDERBOOK_FILTER_ENABLED` | `text` | NULL | NULL | 꼬리잡기 호가필터 ON · ON=스프레드·잔량비·얇은호가·매도벽으로 매수 차단 · OFF=저장만 | +| `TAIL_ORDERBOOK_MAX_SPREAD_PCT` | `text` | NULL | NULL | 꼬리잡기 스프레드상한(%) · 0.45=0.45% · 그리드 max_spread_pct 앵커 | +| `TAIL_ORDERBOOK_ENTRY_BID_LEVELS` | `text` | NULL | NULL | 꼬리잡기 호가단계(N호가) | +| `TAIL_ORDERBOOK_ENTRY_BID_DEPTH_MULT` | `text` | NULL | NULL | 꼬리잡기 매수호가깊이 배수 · 얇은호가 탈락 · 필요수량×N | +| `TAIL_ORDERBOOK_ENTRY_ASK_MAX_MULT` | `text` | NULL | NULL | 꼬리잡기 매도벽 허용배수 · 필요수량×N · 그리드 ask_max_mult | +| `TAIL_ORDERBOOK_MIN_BID_ASK_RATIO` | `text` | NULL | NULL | 꼬리잡기 매수/매도잔량비 하한 · 그리드 min_bid_ask_ratio | +| `TAIL_PROGRAM_FILTER_ENABLED` | `text` | NULL | NULL | 꼬리잡기: 프로그램매매 필터 ON/OFF (TAIL_PROGRAM_FILTER_ENABLED) | +| `TAIL_PROGRAM_MIN_NET_BUY_QTY` | `text` | NULL | NULL | 꼬리잡기: 프로그램매매 최소 NET BUY QTY (TAIL_PROGRAM_MIN_NET_BUY_QTY) | +| `TAIL_PROGRAM_MIN_NET_BUY_AMT` | `text` | NULL | NULL | 꼬리잡기: 프로그램매매 최소 NET BUY AMT (TAIL_PROGRAM_MIN_NET_BUY_AMT) | +| `TAIL_PROGRAM_MAX_SELL_BUY_QTY_RATIO` | `text` | NULL | NULL | 꼬리잡기: 프로그램매매 최대 SELL BUY QTY RATIO (TAIL_PROGRAM_MAX_SELL_BUY_QTY_RATIO) | +| `TAIL_PROGRAM_MIN_NET_DELTA_QTY` | `text` | NULL | NULL | 꼬리잡기: 프로그램매매 최소 NET DELTA QTY (TAIL_PROGRAM_MIN_NET_DELTA_QTY) | +| `TAIL_PROGRAM_SNAPSHOT_MAX_AGE_SEC` | `text` | NULL | NULL | 꼬리잡기: 프로그램매매 SNAPSHOT 최대 AGE 초 (TAIL_PROGRAM_SNAPSHOT_MAX_AGE_SEC) | +| `TAIL_BACKTEST_USE_TRIGGER_SNAPSHOT_DB` | `text` | NULL | NULL | 꼬리잡기: BACKTEST 사용_ TRIGGER SNAPSHOT DB (TAIL_BACKTEST_USE_TRIGGER_SNAPSHOT_DB) | +| `TAIL_WHIPSAW_FILTER_ENABLED` | `text` | NULL | NULL | 꼬리잡기 휩쏘 필터 | +| `TAIL_WHIPSAW_MODE` | `text` | NULL | NULL | 꼬리잡기: 휩쏘 모드 (TAIL_WHIPSAW_MODE) | +| `TAIL_WHIPSAW_SUBBAR_SEC` | `text` | NULL | NULL | 꼬리 휩쏘 미니봉(초) · 비우면 글로벌 WHIPSAW_SUBBAR_SEC · Optuna 꼬리 TPE 축 | +| `TAIL_WHIPSAW_LOOKBACK_SEC` | `text` | NULL | NULL | 꼬리 휩쏘 룩백(초) · 비우면 글로벌 WHIPSAW_LOOKBACK_SEC | +| `TAIL_WHIPSAW_DIP_PCT` | `text` | NULL | NULL | 꼬리 휩쏘 하락폭(분율) · 0.003=0.3% · 비우면 글로벌 | +| `TAIL_WHIPSAW_RECOVERY_TOL_PCT` | `text` | NULL | NULL | 꼬리 휩쏘 회복허용(분율) · 0.001=0.1% · 비우면 글로벌 | +| `SHORT_DAILY_PROFIT_TARGET_ENABLED` | `text` | NULL | NULL | 꼬리잡기 손익 감시 켜기 · OFF = 해당 전략 일일익절 비활성 (매수 차단 없음) | +| `SHORT_DAILY_PROFIT_TARGET_KRW` | `text` | NULL | NULL | 꼬리잡기 목표(원) | +| `SHORT_DAILY_PROFIT_TARGET_PCT` | `text` | NULL | NULL | 꼬리잡기 목표(%) · 퍼센트 그대로 (예: 2.5 = 2.5%) · 0=미사용 | +| `SHORT_DAILY_PROFIT_HALT_NEW_BUYS` | `text` | NULL | NULL | 꼬리잡기 신규매수 수동 중단 · 목표 무관 수동 잠금 · 비우면 마스터 상속 · OFF=정상 | +| `TAIL_BACKTEST_USE_TRIGGER_EVAL_SNAPSHOT` | `text` | NULL | NULL | 꼬리잡기: BACKTEST 사용_ TRIGGER EVAL SNAPSHOT (TAIL_BACKTEST_USE_TRIGGER_EVAL_SNAPSHOT) | +| `SHORT_DAILY_PROFIT_MODE` | `text` | NULL | NULL | 꼬리잡기 익절방식 · 비우면 마스터 상속 · fixed/trailing/both | +| `SHORT_DAILY_PROFIT_TRAIL_TIERS` | `text` | NULL | NULL | 꼬리잡기 다단 트레일 · 수익원:컷%,… · 비우면 마스터 상속 · 예: 30000:50,100000:35,300000:25 | +| `SHORT_DAILY_PROFIT_TRAIL_DROP_PCT` | `text` | NULL | NULL | 꼬리잡기 단일 트레일 되돌림(%) · 다단 비었을 때만 · 고점 대비 % · 비우면 마스터 상속 · 0=미사용 | +| `SHORT_DAILY_PROFIT_TRAIL_ARM_KRW` | `text` | NULL | NULL | 꼬리잡기 트레일 발동수익(원) · 이 이상 벌어야 트레일 감시 · 비우면 마스터 상속 | +| `SHORT_DAILY_PROFIT_TRAIL_ARM_PCT` | `text` | NULL | NULL | 꼬리잡기: 일일익절 트레일무장 % (SHORT_DAILY_PROFIT_TRAIL_ARM_PCT) | +| `CONDITION_SHORT_KIWOOM_NAME` | `text` | NULL | NULL | 꼬리잡기: CONDITION SHORT KIWOOM NAME (CONDITION_SHORT_KIWOOM_NAME) | +| `CONDITION_SHORT_KIWOOM_SEQ` | `text` | NULL | NULL | 꼬리잡기: CONDITION SHORT KIWOOM SEQ (CONDITION_SHORT_KIWOOM_SEQ) | +| `TAIL_EOD_ENABLED` | `text` | NULL | NULL | EOD 당일청산 사용 · ON=설정 시각 이후 보유 전량 장마감 청산 · OFF=익절·손절·시간컷만 | +| `TAIL_EOD_HM` | `text` | NULL | NULL | EOD 시각 (HH:MM) · 정규장 15:30 마감 기준 최소 10분 전 · 순차매도 여유 | +| `TAIL_PARAM_SEARCH_MIN_START` | `text` | NULL | NULL | 꼬리 파라서치 최소 시작일 · 조건식 변경일 이후만 탐색 (YYYY-MM-DD) · CLI --start 와 max 적용 | +| `TAIL_SYMBOL_DAILY_LOSS_LIMIT_KRW` | `text` | NULL | NULL | 꼬리잡기: SYMBOL DAILY LOSS LIMIT 원 (TAIL_SYMBOL_DAILY_LOSS_LIMIT_KRW) | +| `TAIL_SYMBOL_DAILY_LOSS_LIMIT_PCT` | `text` | NULL | NULL | 꼬리잡기: SYMBOL DAILY LOSS LIMIT % (TAIL_SYMBOL_DAILY_LOSS_LIMIT_PCT) | +| `TAIL_REENTRY_MIN_EDGE_KRW` | `text` | NULL | NULL | 꼬리잡기: REENTRY 최소 EDGE 원 (TAIL_REENTRY_MIN_EDGE_KRW) | +| `TAIL_REENTRY_REQUIRE_NONNEG` | `text` | NULL | NULL | 꼬리잡기: REENTRY REQUIRE NONNEG (TAIL_REENTRY_REQUIRE_NONNEG) | +| `TAIL_GRID_FAST_SYMBOL_LOSS_PCT` | `text` | NULL | NULL | 꼬리잡기: GRID FAST SYMBOL LOSS % (TAIL_GRID_FAST_SYMBOL_LOSS_PCT) | +| `TAIL_GRID_FAST_SYMBOL_LOSS_KRW` | `text` | NULL | NULL | 꼬리잡기: GRID FAST SYMBOL LOSS 원 (TAIL_GRID_FAST_SYMBOL_LOSS_KRW) | +| `TAIL_GRID_FAST_REENTRY_MIN_EDGE` | `text` | NULL | NULL | 꼬리잡기: GRID FAST REENTRY 최소 EDGE (TAIL_GRID_FAST_REENTRY_MIN_EDGE) | +| `TAIL_BACKTEST_USE_TICK_EXIT` | `text` | NULL | NULL | 꼬리 틱 청산 · ON=ws_ticks 시간순 청산(실매 폴링 정합) · OFF=OHLC intrabar | +| `TAIL_BACKTEST_POLL_MS` | `text` | NULL | NULL | 꼬리 틱청산 폴링(ms) · 실매 STRATEGY_LOOP_SLEEP≈0.1초 정합 · 하한 50ms | +| `TAIL_BACKTEST_SELL_SLIP_PCT` | `text` | NULL | NULL | 꼬리잡기: BACKTEST SELL SLIP % (TAIL_BACKTEST_SELL_SLIP_PCT) | +| `TAIL_BT_SYNTH_3M_FROM_1M` | `text` | NULL | NULL | 꼬리잡기: BT SYNTH 3M FROM 1M (TAIL_BT_SYNTH_3M_FROM_1M) | +| `TAIL_BACKTEST_UNIVERSE_SCAN_AT` | `text` | NULL | NULL | 꼬리잡기: BACKTEST 유니버스 SCAN AT (TAIL_BACKTEST_UNIVERSE_SCAN_AT) | +| `TAIL_BT_INCLUSION_LOOKBACK` | `text` | NULL | NULL | 꼬리잡기: BT INCLUSION LOOKBACK (TAIL_BT_INCLUSION_LOOKBACK) | +| `TAIL_VOL_MULT` | `text` | NULL | NULL | 꼬리잡기: 거래량배수 (TAIL_VOL_MULT) | +| `TAIL_VOL_WIN` | `text` | NULL | NULL | 꼬리잡기: 거래량윈도우 (TAIL_VOL_WIN) | +| `TAIL_BACKTEST_CANDLE_WARMUP_BARS` | `text` | NULL | NULL | 꼬리잡기: BACKTEST CANDLE WARMUP 봉수 (TAIL_BACKTEST_CANDLE_WARMUP_BARS) | +| `TAIL_BACKTEST_REST_WARMUP` | `text` | NULL | NULL | 꼬리잡기: BACKTEST REST WARMUP (TAIL_BACKTEST_REST_WARMUP) | +| `TAIL_BACKTEST_REST_WARMUP_BARS` | `text` | NULL | NULL | 꼬리잡기: BACKTEST REST WARMUP 봉수 (TAIL_BACKTEST_REST_WARMUP_BARS) | +| `TAIL_BACKTEST_REST_SLEEP_SEC` | `text` | NULL | NULL | 꼬리잡기: BACKTEST REST SLEEP 초 (TAIL_BACKTEST_REST_SLEEP_SEC) | +| `TAIL_BACKTEST_REST_MAX_CODES` | `text` | NULL | NULL | 꼬리잡기: BACKTEST REST 최대 CODES (TAIL_BACKTEST_REST_MAX_CODES) | +| `TAIL_VOL_SPIKE_LOOKBACK` | `text` | NULL | NULL | 꼬리잡기: 거래량 SPIKE LOOKBACK (TAIL_VOL_SPIKE_LOOKBACK) | +| `TAIL_UNIVERSE_EXIT_DEBOUNCE_SEC` | `text` | NULL | NULL | 꼬리잡기: 유니버스 EXIT DEBOUNCE 초 (TAIL_UNIVERSE_EXIT_DEBOUNCE_SEC) | +| `SHORT_DAILY_PROFIT_RISK_BUDGET_ENABLED` | `text` | NULL | NULL | 꼬리잡기 리스크버짓(B) · ON=일일익절 hit 후 보유최악손절합>cushion 이면 전량정리 · 기본 OFF · 마스터 없음 | +| `SHORT_DAILY_PROFIT_RISK_BUDGET_COOLDOWN_SEC` | `text` | NULL | NULL | 꼬리잡기 리스크버짓 쿨다운(초) · 같은 날 재평가 최소 간격 · 0=당일 1회만 | +| `SHORT_TICK_SELL_ENABLED` | `text` | NULL | NULL | 꼬리잡기 틱매도 · 비우면 공통 TICK_SELL_ENABLED · true/false 명시 시 전략만 덮어씀 · 기본 false | +| `TAIL_WHIPSAW_MIN_SUBBARS` | `text` | NULL | NULL | 꼬리잡기: 휩쏘 최소 SUBBARS (TAIL_WHIPSAW_MIN_SUBBARS) | +| `TAIL_WHIPSAW_TICK_BUFFER_LIMIT` | `text` | NULL | NULL | 꼬리잡기: 휩쏘 TICK BUFFER LIMIT (TAIL_WHIPSAW_TICK_BUFFER_LIMIT) | +| `CONDITION_SHORT_LS_NAME` | `text` | NULL | NULL | 꼬리잡기: CONDITION SHORT LS NAME (CONDITION_SHORT_LS_NAME) | +| `SHORT_LIVE_UNIVERSE_HISTORY_SOURCE` | `text` | NULL | NULL | 꼬리잡기: LIVE 유니버스 HISTORY SOURCE (SHORT_LIVE_UNIVERSE_HISTORY_SOURCE) | + +
+DDL 원문 + +```sql +CREATE TABLE `config_short` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `created_at` varchar(30) NOT NULL, + `STOP_LOSS_PCT` text DEFAULT NULL, + `SHOULDER_CUT_PCT` text DEFAULT NULL, + `SHOULDER_MIN_HIGH_PCT` text DEFAULT NULL, + `SHOULDER_MIN_NET_PCT` text DEFAULT NULL, + `STOP_ATR_MULTIPLIER_TAIL` text DEFAULT NULL, + `TARGET_ATR_MULTIPLIER_TAIL` text DEFAULT NULL, + `TAIL_ATR_SL_MIN_PCT` text DEFAULT NULL, + `TAIL_ATR_SL_MAX_PCT` text DEFAULT NULL, + `TAIL_ATR_TP_MIN_PCT` text DEFAULT NULL, + `TAIL_ATR_TP_MAX_PCT` text DEFAULT NULL, + `MIN_CANDLE_LEN_TAIL` text DEFAULT NULL, + `MIN_PRICE_TAIL` text DEFAULT NULL, + `TAIL_SCORE_BASE` text DEFAULT NULL, + `TAIL_SCORE_RATIO_MULT` text DEFAULT NULL, + `TAIL_RATIO_MIN` text DEFAULT NULL, + `TAIL_PCT_MIN` text DEFAULT NULL, + `TAIL_SKIP_HTS_SCAN_DUPES` text DEFAULT NULL, + `TAIL_USE_INTRADAY_DROP` text DEFAULT NULL, + `TAIL_USE_MA20_FILTER` text DEFAULT NULL, + `TAIL_USE_RSI_FILTER` text DEFAULT NULL, + `TAIL_USE_DAILY_RANGE_FILTER` text DEFAULT NULL, + `TAIL_USE_HIGH_CHASE_FILTER` text DEFAULT NULL, + `TAIL_BAR_CHG_MIN_PCT` text DEFAULT NULL, + `TAIL_BAR_CHG_MAX_PCT` text DEFAULT NULL, + `TAIL_RSI_THRESHOLD` text DEFAULT NULL, + `MIN_DROP_PCT_FOR_LOSS_CUT` text DEFAULT NULL, + `TAKE_PROFIT_PCT` text DEFAULT NULL, + `MIN_DROP_RATE` text DEFAULT NULL, + `MIN_RECOVERY_RATIO_SHORT` text DEFAULT NULL, + `KIS_SHORT_MM_CHANNEL` text DEFAULT NULL, + `SHORT_GAP_FILL_LIMIT` text DEFAULT NULL, + `MAX_RECOVERY_RATIO_3M` text DEFAULT NULL, + `TAIL_CANDLE_LOOKBACK` text DEFAULT NULL, + `SHORT_LIVE_BACKTEST_ALIGN` text DEFAULT NULL, + `SHORT_LIVE_SIGNAL_LOOKBACK_BARS` text DEFAULT NULL, + `SHORT_UNIVERSE_SOURCE` text DEFAULT NULL, + `RANK_SHORT_SORT` text DEFAULT NULL, + `RANK_SHORT_LIMIT` text DEFAULT NULL, + `CONDITION_SHORT_NAME` text DEFAULT NULL, + `CONDITION_SHORT_SEQ` text DEFAULT NULL, + `SHORT_MAX_BUY_AMOUNT` text DEFAULT NULL, + `TAIL_MAX_BUY_AMOUNT` text DEFAULT NULL, + `SHORT_CAND_LIMIT` text DEFAULT NULL, + `SHORT_TIME_START` text DEFAULT NULL, + `SHORT_TIME_END` text DEFAULT NULL, + `SHORT_MAX_STOCKS` text DEFAULT NULL, + `SELL_USE_ORDERBOOK_ON_PROFIT` text DEFAULT NULL, + `SELL_ORDERBOOK_BID_LEVELS` text DEFAULT NULL, + `SELL_ORDERBOOK_DEPTH_MULT` text DEFAULT NULL, + `SHORT_TOTAL_BUDGET_KRW` text DEFAULT NULL, + `STRATEGY_SHORT_ENABLED` text DEFAULT NULL, + `TAIL_MIN_DROP_RATE` text DEFAULT NULL, + `TAIL_MIN_RECOVERY_RATIO` text DEFAULT NULL, + `TAIL_MAX_RECOVERY_3M` text DEFAULT NULL, + `TAIL_STOP_LOSS_PCT` text DEFAULT NULL, + `TAIL_TAKE_PROFIT_PCT` text DEFAULT NULL, + `TAIL_SHOULDER_MIN_HIGH_PCT` text DEFAULT NULL, + `TAIL_SHOULDER_CUT_PCT` text DEFAULT NULL, + `TAIL_STOP_ATR_MULT` text DEFAULT NULL, + `TAIL_TARGET_ATR_MULT` text DEFAULT NULL, + `TAIL_MIN_PRICE` text DEFAULT NULL, + `TAIL_MAX_DAILY_CHG` text DEFAULT NULL, + `TAIL_MA20_MAX_ABOVE_PCT` text DEFAULT NULL, + `TAIL_MAX_LOSS_KRW` text DEFAULT NULL, + `TAIL_MIN_DROP_FOR_LOSS_CUT` text DEFAULT NULL, + `TAIL_RSI_PERIOD` text DEFAULT NULL, + `TAIL_COOLDOWN_SEC` text DEFAULT NULL, + `TAIL_MAX_DAILY` text DEFAULT NULL, + `TAIL_HIGH_CHASE_THR` text DEFAULT NULL, + `TAIL_TIME_START` text DEFAULT NULL, + `TAIL_TIME_END` text DEFAULT NULL, + `TAIL_MAX_STOCKS` text DEFAULT NULL, + `TAIL_TOTAL_BUDGET_KRW` text DEFAULT NULL, + `TAIL_SLOT_MONEY` text DEFAULT NULL, + `TAIL_ENTRY_MODE` text DEFAULT NULL, + `TAIL_LIMIT_ATR_MULT` text DEFAULT NULL, + `TAIL_LIMIT_ANCHOR` text DEFAULT NULL, + `TAIL_LIMIT_VALID_BARS` text DEFAULT NULL, + `TAIL_LIMIT_FILL_SLIP_PCT` text DEFAULT NULL, + `TAIL_MIN_INVEST_RATIO_OF_SLOT` text DEFAULT NULL, + `TAIL_PARAM_SEARCH_ENTRY_MODE` text DEFAULT NULL, + `TAIL_GRID_FAST_MAX_DAILY_CHG` text DEFAULT NULL, + `TAIL_GRID_COARSE_MAX_DAILY_CHG` text DEFAULT NULL, + `TAIL_GRID_FAST_LIMIT_ATR_MULT` text DEFAULT NULL, + `TAIL_GRID_COARSE_LIMIT_ATR_MULT` text DEFAULT NULL, + `TAIL_RATCHET_TIERS` text DEFAULT NULL, + `TAIL_MAX_HOLD_BARS` text DEFAULT NULL, + `TAIL_BACKTEST_USE_TICK_DB` text DEFAULT NULL, + `TAIL_BACKTEST_TICK_FALLBACK_OHLC` text DEFAULT NULL, + `TAIL_TRAIL_PCT` text DEFAULT NULL, + `TAIL_TRAIL_ARM_PCT` text DEFAULT NULL, + `TAIL_PATTERN_HAMMER` text DEFAULT NULL, + `TAIL_PATTERN_PIN` text DEFAULT NULL, + `TAIL_PATTERN_ENGULFING` text DEFAULT NULL, + `TAIL_PATTERN_PIERCING` text DEFAULT NULL, + `TAIL_PATTERN_HARAMI` text DEFAULT NULL, + `TAIL_PATTERN_DOJI` text DEFAULT NULL, + `TAIL_PATTERN_MORNING_STAR` text DEFAULT NULL, + `TAIL_PIN_CLOSE_UPPER_RATIO` text DEFAULT NULL, + `TAIL_PIN_MAX_UPPER_TAIL_RATIO` text DEFAULT NULL, + `TAIL_ENGULF_MIN_BODY_RATIO` text DEFAULT NULL, + `TAIL_PIERCING_PENETRATE_RATIO` text DEFAULT NULL, + `TAIL_HARAMI_MAX_BODY_RATIO` text DEFAULT NULL, + `TAIL_DOJI_BODY_MAX_RATIO` text DEFAULT NULL, + `TAIL_MORNING_STAR_BODY_MAX_RATIO` text DEFAULT NULL, + `TAIL_ORDERBOOK_FILTER_ENABLED` text DEFAULT NULL, + `TAIL_ORDERBOOK_MAX_SPREAD_PCT` text DEFAULT NULL, + `TAIL_ORDERBOOK_ENTRY_BID_LEVELS` text DEFAULT NULL, + `TAIL_ORDERBOOK_ENTRY_BID_DEPTH_MULT` text DEFAULT NULL, + `TAIL_ORDERBOOK_ENTRY_ASK_MAX_MULT` text DEFAULT NULL, + `TAIL_ORDERBOOK_MIN_BID_ASK_RATIO` text DEFAULT NULL, + `TAIL_PROGRAM_FILTER_ENABLED` text DEFAULT NULL, + `TAIL_PROGRAM_MIN_NET_BUY_QTY` text DEFAULT NULL, + `TAIL_PROGRAM_MIN_NET_BUY_AMT` text DEFAULT NULL, + `TAIL_PROGRAM_MAX_SELL_BUY_QTY_RATIO` text DEFAULT NULL, + `TAIL_PROGRAM_MIN_NET_DELTA_QTY` text DEFAULT NULL, + `TAIL_PROGRAM_SNAPSHOT_MAX_AGE_SEC` text DEFAULT NULL, + `TAIL_BACKTEST_USE_TRIGGER_SNAPSHOT_DB` text DEFAULT NULL, + `TAIL_WHIPSAW_FILTER_ENABLED` text DEFAULT NULL, + `TAIL_WHIPSAW_MODE` text DEFAULT NULL, + `TAIL_WHIPSAW_SUBBAR_SEC` text DEFAULT NULL, + `TAIL_WHIPSAW_LOOKBACK_SEC` text DEFAULT NULL, + `TAIL_WHIPSAW_DIP_PCT` text DEFAULT NULL, + `TAIL_WHIPSAW_RECOVERY_TOL_PCT` text DEFAULT NULL, + `SHORT_DAILY_PROFIT_TARGET_ENABLED` text DEFAULT NULL, + `SHORT_DAILY_PROFIT_TARGET_KRW` text DEFAULT NULL, + `SHORT_DAILY_PROFIT_TARGET_PCT` text DEFAULT NULL, + `SHORT_DAILY_PROFIT_HALT_NEW_BUYS` text DEFAULT NULL, + `TAIL_BACKTEST_USE_TRIGGER_EVAL_SNAPSHOT` text DEFAULT NULL, + `SHORT_DAILY_PROFIT_MODE` text DEFAULT NULL, + `SHORT_DAILY_PROFIT_TRAIL_TIERS` text DEFAULT NULL, + `SHORT_DAILY_PROFIT_TRAIL_DROP_PCT` text DEFAULT NULL, + `SHORT_DAILY_PROFIT_TRAIL_ARM_KRW` text DEFAULT NULL, + `SHORT_DAILY_PROFIT_TRAIL_ARM_PCT` text DEFAULT NULL, + `CONDITION_SHORT_KIWOOM_NAME` text DEFAULT NULL, + `CONDITION_SHORT_KIWOOM_SEQ` text DEFAULT NULL, + `TAIL_EOD_ENABLED` text DEFAULT NULL, + `TAIL_EOD_HM` text DEFAULT NULL, + `TAIL_PARAM_SEARCH_MIN_START` text DEFAULT NULL, + `TAIL_SYMBOL_DAILY_LOSS_LIMIT_KRW` text DEFAULT NULL, + `TAIL_SYMBOL_DAILY_LOSS_LIMIT_PCT` text DEFAULT NULL, + `TAIL_REENTRY_MIN_EDGE_KRW` text DEFAULT NULL, + `TAIL_REENTRY_REQUIRE_NONNEG` text DEFAULT NULL, + `TAIL_GRID_FAST_SYMBOL_LOSS_PCT` text DEFAULT NULL, + `TAIL_GRID_FAST_SYMBOL_LOSS_KRW` text DEFAULT NULL, + `TAIL_GRID_FAST_REENTRY_MIN_EDGE` text DEFAULT NULL, + `TAIL_BACKTEST_USE_TICK_EXIT` text DEFAULT NULL, + `TAIL_BACKTEST_POLL_MS` text DEFAULT NULL, + `TAIL_BACKTEST_SELL_SLIP_PCT` text DEFAULT NULL, + `TAIL_BT_SYNTH_3M_FROM_1M` text DEFAULT NULL, + `TAIL_BACKTEST_UNIVERSE_SCAN_AT` text DEFAULT NULL, + `TAIL_BT_INCLUSION_LOOKBACK` text DEFAULT NULL, + `TAIL_VOL_MULT` text DEFAULT NULL, + `TAIL_VOL_WIN` text DEFAULT NULL, + `TAIL_BACKTEST_CANDLE_WARMUP_BARS` text DEFAULT NULL, + `TAIL_BACKTEST_REST_WARMUP` text DEFAULT NULL, + `TAIL_BACKTEST_REST_WARMUP_BARS` text DEFAULT NULL, + `TAIL_BACKTEST_REST_SLEEP_SEC` text DEFAULT NULL, + `TAIL_BACKTEST_REST_MAX_CODES` text DEFAULT NULL, + `TAIL_VOL_SPIKE_LOOKBACK` text DEFAULT NULL, + `TAIL_UNIVERSE_EXIT_DEBOUNCE_SEC` text DEFAULT NULL, + `SHORT_DAILY_PROFIT_RISK_BUDGET_ENABLED` text DEFAULT NULL, + `SHORT_DAILY_PROFIT_RISK_BUDGET_COOLDOWN_SEC` text DEFAULT NULL, + `SHORT_TICK_SELL_ENABLED` text DEFAULT NULL, + `TAIL_WHIPSAW_MIN_SUBBARS` text DEFAULT NULL, + `TAIL_WHIPSAW_TICK_BUFFER_LIMIT` text DEFAULT NULL, + `CONDITION_SHORT_LS_NAME` text DEFAULT NULL, + `SHORT_LIVE_UNIVERSE_HISTORY_SOURCE` text DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=388 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `config_breakout` + +**PK**: `id` | AUTO_INCREMENT=388 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `int(11)` | NOT NULL | | PK · AUTO_INCREMENT | +| `created_at` | `varchar(30)` | NOT NULL | | 설정행 생성시각 | +| `BREAKOUT_LIVE_BACKTEST_ALIGN` | `text` | NULL | NULL | 돌파: LIVE BACKTEST ALIGN (BREAKOUT_LIVE_BACKTEST_ALIGN) | +| `BREAKOUT_LIVE_SIGNAL_LOOKBACK_BARS` | `text` | NULL | NULL | 돌파: LIVE SIGNAL LOOKBACK 봉수 (BREAKOUT_LIVE_SIGNAL_LOOKBACK_BARS) | +| `BREAKOUT_UNIVERSE_SOURCE` | `text` | NULL | NULL | 돌파 유니버스 · ls_condition=LS 동명 AFR · 시세는 키움/한투 유지 · 매니저 최초 기동은 봇 재시작 필요할 수 있음 · 키움으로 자동 폴백 안 함 | +| `RANK_BREAKOUT_SORT` | `text` | NULL | NULL | RANK 돌파 SORT (RANK_BREAKOUT_SORT) | +| `RANK_BREAKOUT_LIMIT` | `text` | NULL | NULL | RANK 돌파 LIMIT (RANK_BREAKOUT_LIMIT) | +| `CONDITION_BREAKOUT_NAME` | `text` | NULL | NULL | CONDITION 돌파 NAME (CONDITION_BREAKOUT_NAME) | +| `CONDITION_BREAKOUT_SEQ` | `text` | NULL | NULL | CONDITION 돌파 SEQ (CONDITION_BREAKOUT_SEQ) | +| `BREAKOUT_CAND_LIMIT` | `text` | NULL | NULL | 돌파 후보 상한 · 0=무제한 · 조건검색/랭킹이 N개 줘도 상위 N개만 매수체크·WS구독 · 전 전략 합산이 키움 WS 100종 한도를 넘지 않게 조절 | +| `BREAKOUT_TIME_START` | `text` | NULL | NULL | 돌파: 매수시작 (BREAKOUT_TIME_START) | +| `BREAKOUT_TIME_END` | `text` | NULL | NULL | 돌파: 매수종료 (BREAKOUT_TIME_END) | +| `BREAKOUT_LOOKBACK_MIN` | `text` | NULL | NULL | 돌파: LOOKBACK 최소 (BREAKOUT_LOOKBACK_MIN) | +| `BREAKOUT_VOL_WIN` | `text` | NULL | NULL | 돌파: 거래량윈도우 (BREAKOUT_VOL_WIN) | +| `BREAKOUT_VOL_MULT` | `text` | NULL | NULL | 돌파: 거래량배수 (BREAKOUT_VOL_MULT) | +| `BREAKOUT_PREV_CHG_MIN` | `text` | NULL | NULL | 돌파: PREV CHG 최소 (BREAKOUT_PREV_CHG_MIN) | +| `BREAKOUT_PREV_CHG_MAX` | `text` | NULL | NULL | 돌파: PREV CHG 최대 (BREAKOUT_PREV_CHG_MAX) | +| `BREAKOUT_STOP_LOSS_PCT` | `text` | NULL | NULL | 돌파: 손절 % (BREAKOUT_STOP_LOSS_PCT) | +| `BREAKOUT_TAKE_PROFIT_PCT` | `text` | NULL | NULL | 돌파: 익절 % (BREAKOUT_TAKE_PROFIT_PCT) | +| `BREAKOUT_TRAIL_PCT` | `text` | NULL | NULL | 돌파: 트레일 % (BREAKOUT_TRAIL_PCT) | +| `BREAKOUT_GOLDEN_END_HM` | `text` | NULL | NULL | 돌파: GOLDEN END HHMM (BREAKOUT_GOLDEN_END_HM) | +| `BREAKOUT_MAX_DAILY_CHG` | `text` | NULL | NULL | 돌파: 당일등락상한 (BREAKOUT_MAX_DAILY_CHG) | +| `BREAKOUT_MIN_PRICE` | `text` | NULL | NULL | 돌파: 최소가격 (BREAKOUT_MIN_PRICE) | +| `BREAKOUT_MAX_LOSS_PER_TRADE_KRW` | `text` | NULL | NULL | 돌파: 최대 LOSS PER TRADE 원 (BREAKOUT_MAX_LOSS_PER_TRADE_KRW) | +| `BREAKOUT_SLOT_MONEY` | `text` | NULL | NULL | 돌파 슬롯(원) | +| `BREAKOUT_MAX_DAILY` | `text` | NULL | NULL | 돌파: 일일매수횟수상한 (BREAKOUT_MAX_DAILY) | +| `BREAKOUT_COOLDOWN_SEC` | `text` | NULL | NULL | 돌파: 쿨다운 초 (BREAKOUT_COOLDOWN_SEC) | +| `BREAKOUT_MAX_STOCKS` | `text` | NULL | NULL | 돌파 동시 종목 | +| `BREAKOUT_SHOULDER_MIN_HIGH_PCT` | `text` | NULL | NULL | 돌파: 어깨무장고점 % (BREAKOUT_SHOULDER_MIN_HIGH_PCT) | +| `BREAKOUT_SHOULDER_CUT_PCT` | `text` | NULL | NULL | 돌파: 어깨컷 % (BREAKOUT_SHOULDER_CUT_PCT) | +| `BREAKOUT_GRID_COARSE_SHOULDER_SMIN` | `text` | NULL | NULL | 돌파: GRID COARSE 어깨 SMIN (BREAKOUT_GRID_COARSE_SHOULDER_SMIN) | +| `BREAKOUT_GRID_COARSE_SHOULDER_CUT` | `text` | NULL | NULL | 돌파: GRID COARSE 어깨컷 (BREAKOUT_GRID_COARSE_SHOULDER_CUT) | +| `BREAKOUT_GRID_FINE_SHOULDER_SMIN` | `text` | NULL | NULL | 돌파: GRID FINE 어깨 SMIN (BREAKOUT_GRID_FINE_SHOULDER_SMIN) | +| `BREAKOUT_GRID_FINE_SHOULDER_CUT` | `text` | NULL | NULL | 돌파: GRID FINE 어깨컷 (BREAKOUT_GRID_FINE_SHOULDER_CUT) | +| `BREAKOUT_GRID_FULL_SHOULDER_SMIN` | `text` | NULL | NULL | 돌파: GRID FULL 어깨 SMIN (BREAKOUT_GRID_FULL_SHOULDER_SMIN) | +| `BREAKOUT_GRID_FULL_SHOULDER_CUT` | `text` | NULL | NULL | 돌파: GRID FULL 어깨컷 (BREAKOUT_GRID_FULL_SHOULDER_CUT) | +| `BREAKOUT_TOTAL_BUDGET_KRW` | `text` | NULL | NULL | 돌파 총 한도(원) | +| `STRATEGY_BREAKOUT_ENABLED` | `text` | NULL | NULL | 돌파 ON/OFF. 다음루프 신규매수중단(보유청산유지). WS구독해제 안함. 기동시OFF→켜려면재시작 | +| `KIS_BREAKOUT_MM_CHANNEL` | `text` | NULL | NULL | KIS 돌파 MM CHANNEL (KIS_BREAKOUT_MM_CHANNEL) | +| `BREAKOUT_ENTRY_MODE` | `text` | NULL | NULL | 돌파: ENTRY 모드 (BREAKOUT_ENTRY_MODE) | +| `BREAKOUT_INTRABAR_SLIPPAGE_PCT` | `text` | NULL | NULL | 돌파: INTRABAR SLIPPAGE % (BREAKOUT_INTRABAR_SLIPPAGE_PCT) | +| `BREAKOUT_RATCHET_TIERS` | `text` | NULL | NULL | 돌파: 다단래칫 (BREAKOUT_RATCHET_TIERS) | +| `BREAKOUT_MAX_HOLD_BARS` | `text` | NULL | NULL | 최대보유(분) · 0=OFF · 진입 후 N분 경과 시 max_hold 청산 | +| `BREAKOUT_TRAIL_ARM_PCT` | `text` | NULL | NULL | 돌파: 트레일무장 % (BREAKOUT_TRAIL_ARM_PCT) | +| `BREAKOUT_USE_EMA_FILTER` | `text` | NULL | NULL | 돌파: 사용_ EMA 필터 (BREAKOUT_USE_EMA_FILTER) | +| `BREAKOUT_EMA_FAST_PERIOD` | `text` | NULL | NULL | 돌파: EMA단기 기간 (BREAKOUT_EMA_FAST_PERIOD) | +| `BREAKOUT_EMA_SLOW_PERIOD` | `text` | NULL | NULL | 돌파: EMA장기 기간 (BREAKOUT_EMA_SLOW_PERIOD) | +| `BREAKOUT_MIN_BAR_TRADE_VALUE_KRW` | `text` | NULL | NULL | 돌파: 최소 BAR TRADE VALUE 원 (BREAKOUT_MIN_BAR_TRADE_VALUE_KRW) | +| `BREAKOUT_MIN_TURNOVER_1M_PCT` | `text` | NULL | NULL | 돌파: 최소 TURNOVER 1M % (BREAKOUT_MIN_TURNOVER_1M_PCT) | +| `BREAKOUT_SHARE_DENOM` | `text` | NULL | NULL | 돌파: SHARE DENOM (BREAKOUT_SHARE_DENOM) | +| `BREAKOUT_ORDERBOOK_FILTER_ENABLED` | `text` | NULL | NULL | 돌파 호가필터 ON · ON=스프레드·잔량비·얇은호가·매도벽으로 매수 차단 · OFF=저장만 | +| `BREAKOUT_ORDERBOOK_MAX_SPREAD_PCT` | `text` | NULL | NULL | 돌파 스프레드상한(%) · 0.45=0.45% · 그리드 max_spread_pct 앵커 | +| `BREAKOUT_ORDERBOOK_ENTRY_BID_LEVELS` | `text` | NULL | NULL | 돌파 호가단계(N호가) | +| `BREAKOUT_ORDERBOOK_ENTRY_BID_DEPTH_MULT` | `text` | NULL | NULL | 돌파 매수호가깊이 배수 · 얇은호가 탈락 · 필요수량×N | +| `BREAKOUT_ORDERBOOK_ENTRY_ASK_MAX_MULT` | `text` | NULL | NULL | 돌파 매도벽 허용배수 · 필요수량×N · 그리드 ask_max_mult | +| `BREAKOUT_ORDERBOOK_MIN_BID_ASK_RATIO` | `text` | NULL | NULL | 돌파 매수/매도잔량비 하한 · 그리드 min_bid_ask_ratio | +| `BREAKOUT_ORDERBOOK_ASK_WALL_MAX_QTY` | `text` | NULL | NULL | 돌파 매도벽 상한(주) · 그리드 ask_wall_max_qty | +| `BREAKOUT_PROGRAM_FILTER_ENABLED` | `text` | NULL | NULL | 돌파: 프로그램매매 필터 ON/OFF (BREAKOUT_PROGRAM_FILTER_ENABLED) | +| `BREAKOUT_PROGRAM_MIN_NET_BUY_QTY` | `text` | NULL | NULL | 돌파: 프로그램매매 최소 NET BUY QTY (BREAKOUT_PROGRAM_MIN_NET_BUY_QTY) | +| `BREAKOUT_PROGRAM_MIN_NET_BUY_AMT` | `text` | NULL | NULL | 돌파: 프로그램매매 최소 NET BUY AMT (BREAKOUT_PROGRAM_MIN_NET_BUY_AMT) | +| `BREAKOUT_PROGRAM_MAX_SELL_BUY_QTY_RATIO` | `text` | NULL | NULL | 돌파: 프로그램매매 최대 SELL BUY QTY RATIO (BREAKOUT_PROGRAM_MAX_SELL_BUY_QTY_RATIO) | +| `BREAKOUT_PROGRAM_MIN_NET_DELTA_QTY` | `text` | NULL | NULL | 돌파: 프로그램매매 최소 NET DELTA QTY (BREAKOUT_PROGRAM_MIN_NET_DELTA_QTY) | +| `BREAKOUT_PROGRAM_SNAPSHOT_MAX_AGE_SEC` | `text` | NULL | NULL | 돌파: 프로그램매매 SNAPSHOT 최대 AGE 초 (BREAKOUT_PROGRAM_SNAPSHOT_MAX_AGE_SEC) | +| `BREAKOUT_BACKTEST_USE_TRIGGER_SNAPSHOT_DB` | `text` | NULL | NULL | 돌파: BACKTEST 사용_ TRIGGER SNAPSHOT DB (BREAKOUT_BACKTEST_USE_TRIGGER_SNAPSHOT_DB) | +| `BREAKOUT_WHIPSAW_FILTER_ENABLED` | `text` | NULL | NULL | 돌파 휩쏘 필터 | +| `BREAKOUT_WHIPSAW_MODE` | `text` | NULL | NULL | 돌파: 휩쏘 모드 (BREAKOUT_WHIPSAW_MODE) | +| `BREAKOUT_WHIPSAW_SUBBAR_SEC` | `text` | NULL | NULL | 돌파: 휩쏘 SUBBAR 초 (BREAKOUT_WHIPSAW_SUBBAR_SEC) | +| `BREAKOUT_WHIPSAW_LOOKBACK_SEC` | `text` | NULL | NULL | 돌파: 휩쏘 LOOKBACK 초 (BREAKOUT_WHIPSAW_LOOKBACK_SEC) | +| `BREAKOUT_WHIPSAW_DIP_PCT` | `text` | NULL | NULL | 돌파: 휩쏘 DIP % (BREAKOUT_WHIPSAW_DIP_PCT) | +| `BREAKOUT_WHIPSAW_RECOVERY_TOL_PCT` | `text` | NULL | NULL | 돌파: 휩쏘 RECOVERY TOL % (BREAKOUT_WHIPSAW_RECOVERY_TOL_PCT) | +| `BREAKOUT_DAILY_PROFIT_TARGET_ENABLED` | `text` | NULL | NULL | 돌파 손익 감시 켜기 · OFF = 해당 전략 일일익절 비활성 (매수 차단 없음) | +| `BREAKOUT_DAILY_PROFIT_TARGET_KRW` | `text` | NULL | NULL | 돌파 목표(원) | +| `BREAKOUT_DAILY_PROFIT_TARGET_PCT` | `text` | NULL | NULL | 돌파 목표(%) · 퍼센트 그대로 (예: 2.5 = 2.5%) · 0=미사용 | +| `BREAKOUT_DAILY_PROFIT_HALT_NEW_BUYS` | `text` | NULL | NULL | 돌파 신규매수 수동 중단 · 목표 무관 수동 잠금 · 비우면 마스터 상속 · OFF=정상 | +| `BREAKOUT_BACKTEST_USE_TRIGGER_EVAL_SNAPSHOT` | `text` | NULL | NULL | 돌파: BACKTEST 사용_ TRIGGER EVAL SNAPSHOT (BREAKOUT_BACKTEST_USE_TRIGGER_EVAL_SNAPSHOT) | +| `BREAKOUT_DAILY_PROFIT_MODE` | `text` | NULL | NULL | 돌파 익절방식 · 비우면 마스터 상속 · fixed/trailing/both | +| `BREAKOUT_DAILY_PROFIT_TRAIL_TIERS` | `text` | NULL | NULL | 돌파 다단 트레일 · 수익원:컷%,… · 비우면 마스터 상속 · 예: 30000:50,100000:35,300000:25 | +| `BREAKOUT_DAILY_PROFIT_TRAIL_DROP_PCT` | `text` | NULL | NULL | 돌파 단일 트레일 되돌림(%) · 다단 비었을 때만 · 고점 대비 % · 비우면 마스터 상속 · 0=미사용 | +| `BREAKOUT_DAILY_PROFIT_TRAIL_ARM_KRW` | `text` | NULL | NULL | 돌파 트레일 발동수익(원) · 이 이상 벌어야 트레일 감시 · 비우면 마스터 상속 | +| `BREAKOUT_DAILY_PROFIT_TRAIL_ARM_PCT` | `text` | NULL | NULL | 돌파: 일일익절 트레일무장 % (BREAKOUT_DAILY_PROFIT_TRAIL_ARM_PCT) | +| `BREAKOUT_SL_MODE` | `text` | NULL | NULL | 돌파: SL 모드 (BREAKOUT_SL_MODE) | +| `BREAKOUT_ATR_PERIOD` | `text` | NULL | NULL | 돌파: ATR 기간 (BREAKOUT_ATR_PERIOD) | +| `BREAKOUT_ATR_SL_MULT` | `text` | NULL | NULL | 돌파: ATR손절 배수 (BREAKOUT_ATR_SL_MULT) | +| `BREAKOUT_ATR_SL_MIN_PCT` | `text` | NULL | NULL | 돌파: ATR손절 최소 % (BREAKOUT_ATR_SL_MIN_PCT) | +| `BREAKOUT_ATR_SL_MAX_PCT` | `text` | NULL | NULL | 돌파: ATR손절 최대 % (BREAKOUT_ATR_SL_MAX_PCT) | +| `CONDITION_BREAKOUT_KIWOOM_NAME` | `text` | NULL | NULL | CONDITION 돌파 KIWOOM NAME (CONDITION_BREAKOUT_KIWOOM_NAME) | +| `CONDITION_BREAKOUT_KIWOOM_SEQ` | `text` | NULL | NULL | CONDITION 돌파 KIWOOM SEQ (CONDITION_BREAKOUT_KIWOOM_SEQ) | +| `BREAKOUT_EOD_ENABLED` | `text` | NULL | NULL | EOD 당일청산 사용 · ON=설정 시각 이후 보유 전량 eod 청산 · OFF=익절·손절·시간컷만 | +| `BREAKOUT_EOD_HM` | `text` | NULL | NULL | EOD 시각 (HH:MM) · 이 시각 이후 보유 전량 청산 · 예: 15:15 | +| `BREAKOUT_SKIP_HTS_SCAN_DUPES` | `text` | NULL | NULL | 돌파: HTS스캔중복스킵 (BREAKOUT_SKIP_HTS_SCAN_DUPES) | +| `BREAKOUT_GRID_FAST_SKIP_HTS_SCAN_DUPES` | `text` | NULL | NULL | 돌파: GRID FAST HTS스캔중복스킵 (BREAKOUT_GRID_FAST_SKIP_HTS_SCAN_DUPES) | +| `BREAKOUT_BACKTEST_TICK_FALLBACK_OHLC` | `text` | NULL | NULL | ⚠ 돌파 OHLC 폴백 (정합위험·금지) · 절대금지 — 틱 진입 ON 시 봉 high 폴백 안 함 | +| `BREAKOUT_BACKTEST_USE_TICK_DB` | `text` | NULL | NULL | 돌파: BACKTEST 사용_ TICK DB (BREAKOUT_BACKTEST_USE_TICK_DB) | +| `BREAKOUT_BACKTEST_USE_TICK_EXIT` | `text` | NULL | NULL | 돌파 틱 청산 · ON=ws_ticks 시간순 청산 · OFF=OHLC intrabar | +| `BREAKOUT_BACKTEST_POLL_MS` | `text` | NULL | NULL | 돌파 틱청산 폴링(ms) · 실매 폴링 정합 · 하한 50ms | +| `BREAKOUT_BACKTEST_SELL_SLIP_PCT` | `text` | NULL | NULL | 돌파: BACKTEST SELL SLIP % (BREAKOUT_BACKTEST_SELL_SLIP_PCT) | +| `BREAKOUT_CONFIRM_MARGIN_PCT` | `text` | NULL | NULL | 돌파: CONFIRM MARGIN % (BREAKOUT_CONFIRM_MARGIN_PCT) | +| `BREAKOUT_BODY_MIN_PCT` | `text` | NULL | NULL | 돌파: BODY 최소 % (BREAKOUT_BODY_MIN_PCT) | +| `BREAKOUT_MAX_BUY_AMOUNT` | `text` | NULL | NULL | 돌파: 1회최대매수금액 (BREAKOUT_MAX_BUY_AMOUNT) | +| `BREAKOUT_MIN_INVEST_RATIO_OF_SLOT` | `text` | NULL | NULL | 돌파: 최소 INVEST RATIO OF SLOT (BREAKOUT_MIN_INVEST_RATIO_OF_SLOT) | +| `BREAKOUT_BACKTEST_CANDLE_WARMUP_BARS` | `text` | NULL | NULL | 돌파: BACKTEST CANDLE WARMUP 봉수 (BREAKOUT_BACKTEST_CANDLE_WARMUP_BARS) | +| `BREAKOUT_BACKTEST_UNIVERSE_SCAN_AT` | `text` | NULL | NULL | 돌파: BACKTEST 유니버스 SCAN AT (BREAKOUT_BACKTEST_UNIVERSE_SCAN_AT) | +| `BREAKOUT_UNIVERSE_EXIT_DEBOUNCE_SEC` | `text` | NULL | NULL | 돌파: 유니버스 EXIT DEBOUNCE 초 (BREAKOUT_UNIVERSE_EXIT_DEBOUNCE_SEC) | +| `BREAKOUT_DAILY_PROFIT_RISK_BUDGET_ENABLED` | `text` | NULL | NULL | 돌파 리스크버짓(B) · ON=일일익절 hit 후 보유최악손절합>cushion 이면 전량정리 · 기본 OFF · 마스터 없음 | +| `BREAKOUT_DAILY_PROFIT_RISK_BUDGET_COOLDOWN_SEC` | `text` | NULL | NULL | 돌파 리스크버짓 쿨다운(초) · 같은 날 재평가 최소 간격 · 0=당일 1회만 | +| `BREAKOUT_TICK_SELL_ENABLED` | `text` | NULL | NULL | 돌파 틱매도 · 비우면 공통 TICK_SELL_ENABLED · true/false 명시 시 전략만 덮어씀 · 기본 false | +| `CONDITION_BREAKOUT_LS_NAME` | `text` | NULL | NULL | CONDITION 돌파 LS NAME (CONDITION_BREAKOUT_LS_NAME) | +| `BREAKOUT_LIVE_UNIVERSE_HISTORY_SOURCE` | `text` | NULL | NULL | 돌파: LIVE 유니버스 HISTORY SOURCE (BREAKOUT_LIVE_UNIVERSE_HISTORY_SOURCE) | +| `BREAKOUT_CANDLE_GAP_FORCE_SEC` | `text` | NULL | NULL | 돌파: CANDLE GAP FORCE 초 (BREAKOUT_CANDLE_GAP_FORCE_SEC) | +| `BREAKOUT_CANDLE_SHORT_LOG_SEC` | `text` | NULL | NULL | 돌파: CANDLE SHORT LOG 초 (BREAKOUT_CANDLE_SHORT_LOG_SEC) | + +
+DDL 원문 + +```sql +CREATE TABLE `config_breakout` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `created_at` varchar(30) NOT NULL, + `BREAKOUT_LIVE_BACKTEST_ALIGN` text DEFAULT NULL, + `BREAKOUT_LIVE_SIGNAL_LOOKBACK_BARS` text DEFAULT NULL, + `BREAKOUT_UNIVERSE_SOURCE` text DEFAULT NULL, + `RANK_BREAKOUT_SORT` text DEFAULT NULL, + `RANK_BREAKOUT_LIMIT` text DEFAULT NULL, + `CONDITION_BREAKOUT_NAME` text DEFAULT NULL, + `CONDITION_BREAKOUT_SEQ` text DEFAULT NULL, + `BREAKOUT_CAND_LIMIT` text DEFAULT NULL, + `BREAKOUT_TIME_START` text DEFAULT NULL, + `BREAKOUT_TIME_END` text DEFAULT NULL, + `BREAKOUT_LOOKBACK_MIN` text DEFAULT NULL, + `BREAKOUT_VOL_WIN` text DEFAULT NULL, + `BREAKOUT_VOL_MULT` text DEFAULT NULL, + `BREAKOUT_PREV_CHG_MIN` text DEFAULT NULL, + `BREAKOUT_PREV_CHG_MAX` text DEFAULT NULL, + `BREAKOUT_STOP_LOSS_PCT` text DEFAULT NULL, + `BREAKOUT_TAKE_PROFIT_PCT` text DEFAULT NULL, + `BREAKOUT_TRAIL_PCT` text DEFAULT NULL, + `BREAKOUT_GOLDEN_END_HM` text DEFAULT NULL, + `BREAKOUT_MAX_DAILY_CHG` text DEFAULT NULL, + `BREAKOUT_MIN_PRICE` text DEFAULT NULL, + `BREAKOUT_MAX_LOSS_PER_TRADE_KRW` text DEFAULT NULL, + `BREAKOUT_SLOT_MONEY` text DEFAULT NULL, + `BREAKOUT_MAX_DAILY` text DEFAULT NULL, + `BREAKOUT_COOLDOWN_SEC` text DEFAULT NULL, + `BREAKOUT_MAX_STOCKS` text DEFAULT NULL, + `BREAKOUT_SHOULDER_MIN_HIGH_PCT` text DEFAULT NULL, + `BREAKOUT_SHOULDER_CUT_PCT` text DEFAULT NULL, + `BREAKOUT_GRID_COARSE_SHOULDER_SMIN` text DEFAULT NULL, + `BREAKOUT_GRID_COARSE_SHOULDER_CUT` text DEFAULT NULL, + `BREAKOUT_GRID_FINE_SHOULDER_SMIN` text DEFAULT NULL, + `BREAKOUT_GRID_FINE_SHOULDER_CUT` text DEFAULT NULL, + `BREAKOUT_GRID_FULL_SHOULDER_SMIN` text DEFAULT NULL, + `BREAKOUT_GRID_FULL_SHOULDER_CUT` text DEFAULT NULL, + `BREAKOUT_TOTAL_BUDGET_KRW` text DEFAULT NULL, + `STRATEGY_BREAKOUT_ENABLED` text DEFAULT NULL, + `KIS_BREAKOUT_MM_CHANNEL` text DEFAULT NULL, + `BREAKOUT_ENTRY_MODE` text DEFAULT NULL, + `BREAKOUT_INTRABAR_SLIPPAGE_PCT` text DEFAULT NULL, + `BREAKOUT_RATCHET_TIERS` text DEFAULT NULL, + `BREAKOUT_MAX_HOLD_BARS` text DEFAULT NULL, + `BREAKOUT_TRAIL_ARM_PCT` text DEFAULT NULL, + `BREAKOUT_USE_EMA_FILTER` text DEFAULT NULL, + `BREAKOUT_EMA_FAST_PERIOD` text DEFAULT NULL, + `BREAKOUT_EMA_SLOW_PERIOD` text DEFAULT NULL, + `BREAKOUT_MIN_BAR_TRADE_VALUE_KRW` text DEFAULT NULL, + `BREAKOUT_MIN_TURNOVER_1M_PCT` text DEFAULT NULL, + `BREAKOUT_SHARE_DENOM` text DEFAULT NULL, + `BREAKOUT_ORDERBOOK_FILTER_ENABLED` text DEFAULT NULL, + `BREAKOUT_ORDERBOOK_MAX_SPREAD_PCT` text DEFAULT NULL, + `BREAKOUT_ORDERBOOK_ENTRY_BID_LEVELS` text DEFAULT NULL, + `BREAKOUT_ORDERBOOK_ENTRY_BID_DEPTH_MULT` text DEFAULT NULL, + `BREAKOUT_ORDERBOOK_ENTRY_ASK_MAX_MULT` text DEFAULT NULL, + `BREAKOUT_ORDERBOOK_MIN_BID_ASK_RATIO` text DEFAULT NULL, + `BREAKOUT_ORDERBOOK_ASK_WALL_MAX_QTY` text DEFAULT NULL, + `BREAKOUT_PROGRAM_FILTER_ENABLED` text DEFAULT NULL, + `BREAKOUT_PROGRAM_MIN_NET_BUY_QTY` text DEFAULT NULL, + `BREAKOUT_PROGRAM_MIN_NET_BUY_AMT` text DEFAULT NULL, + `BREAKOUT_PROGRAM_MAX_SELL_BUY_QTY_RATIO` text DEFAULT NULL, + `BREAKOUT_PROGRAM_MIN_NET_DELTA_QTY` text DEFAULT NULL, + `BREAKOUT_PROGRAM_SNAPSHOT_MAX_AGE_SEC` text DEFAULT NULL, + `BREAKOUT_BACKTEST_USE_TRIGGER_SNAPSHOT_DB` text DEFAULT NULL, + `BREAKOUT_WHIPSAW_FILTER_ENABLED` text DEFAULT NULL, + `BREAKOUT_WHIPSAW_MODE` text DEFAULT NULL, + `BREAKOUT_WHIPSAW_SUBBAR_SEC` text DEFAULT NULL, + `BREAKOUT_WHIPSAW_LOOKBACK_SEC` text DEFAULT NULL, + `BREAKOUT_WHIPSAW_DIP_PCT` text DEFAULT NULL, + `BREAKOUT_WHIPSAW_RECOVERY_TOL_PCT` text DEFAULT NULL, + `BREAKOUT_DAILY_PROFIT_TARGET_ENABLED` text DEFAULT NULL, + `BREAKOUT_DAILY_PROFIT_TARGET_KRW` text DEFAULT NULL, + `BREAKOUT_DAILY_PROFIT_TARGET_PCT` text DEFAULT NULL, + `BREAKOUT_DAILY_PROFIT_HALT_NEW_BUYS` text DEFAULT NULL, + `BREAKOUT_BACKTEST_USE_TRIGGER_EVAL_SNAPSHOT` text DEFAULT NULL, + `BREAKOUT_DAILY_PROFIT_MODE` text DEFAULT NULL, + `BREAKOUT_DAILY_PROFIT_TRAIL_TIERS` text DEFAULT NULL, + `BREAKOUT_DAILY_PROFIT_TRAIL_DROP_PCT` text DEFAULT NULL, + `BREAKOUT_DAILY_PROFIT_TRAIL_ARM_KRW` text DEFAULT NULL, + `BREAKOUT_DAILY_PROFIT_TRAIL_ARM_PCT` text DEFAULT NULL, + `BREAKOUT_SL_MODE` text DEFAULT NULL, + `BREAKOUT_ATR_PERIOD` text DEFAULT NULL, + `BREAKOUT_ATR_SL_MULT` text DEFAULT NULL, + `BREAKOUT_ATR_SL_MIN_PCT` text DEFAULT NULL, + `BREAKOUT_ATR_SL_MAX_PCT` text DEFAULT NULL, + `CONDITION_BREAKOUT_KIWOOM_NAME` text DEFAULT NULL, + `CONDITION_BREAKOUT_KIWOOM_SEQ` text DEFAULT NULL, + `BREAKOUT_EOD_ENABLED` text DEFAULT NULL, + `BREAKOUT_EOD_HM` text DEFAULT NULL, + `BREAKOUT_SKIP_HTS_SCAN_DUPES` text DEFAULT NULL, + `BREAKOUT_GRID_FAST_SKIP_HTS_SCAN_DUPES` text DEFAULT NULL, + `BREAKOUT_BACKTEST_TICK_FALLBACK_OHLC` text DEFAULT NULL, + `BREAKOUT_BACKTEST_USE_TICK_DB` text DEFAULT NULL, + `BREAKOUT_BACKTEST_USE_TICK_EXIT` text DEFAULT NULL, + `BREAKOUT_BACKTEST_POLL_MS` text DEFAULT NULL, + `BREAKOUT_BACKTEST_SELL_SLIP_PCT` text DEFAULT NULL, + `BREAKOUT_CONFIRM_MARGIN_PCT` text DEFAULT NULL, + `BREAKOUT_BODY_MIN_PCT` text DEFAULT NULL, + `BREAKOUT_MAX_BUY_AMOUNT` text DEFAULT NULL, + `BREAKOUT_MIN_INVEST_RATIO_OF_SLOT` text DEFAULT NULL, + `BREAKOUT_BACKTEST_CANDLE_WARMUP_BARS` text DEFAULT NULL, + `BREAKOUT_BACKTEST_UNIVERSE_SCAN_AT` text DEFAULT NULL, + `BREAKOUT_UNIVERSE_EXIT_DEBOUNCE_SEC` text DEFAULT NULL, + `BREAKOUT_DAILY_PROFIT_RISK_BUDGET_ENABLED` text DEFAULT NULL, + `BREAKOUT_DAILY_PROFIT_RISK_BUDGET_COOLDOWN_SEC` text DEFAULT NULL, + `BREAKOUT_TICK_SELL_ENABLED` text DEFAULT NULL, + `CONDITION_BREAKOUT_LS_NAME` text DEFAULT NULL, + `BREAKOUT_LIVE_UNIVERSE_HISTORY_SOURCE` text DEFAULT NULL, + `BREAKOUT_CANDLE_GAP_FORCE_SEC` text DEFAULT NULL, + `BREAKOUT_CANDLE_SHORT_LOG_SEC` text DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=388 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `config_range_break` + +**PK**: `id` | AUTO_INCREMENT=307 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `int(11)` | NOT NULL | | PK · AUTO_INCREMENT | +| `created_at` | `varchar(30)` | NOT NULL | | 설정행 생성시각 | +| `STRATEGY_RANGE_BREAK_ENABLED` | `text` | NULL | NULL | 박스권돌파 ON/OFF. 다음루프 신규매수중단(보유청산유지). WS구독해제 안함 | +| `RANGE_BREAK_LIVE_BACKTEST_ALIGN` | `text` | NULL | NULL | 박스권돌파: LIVE BACKTEST ALIGN (RANGE_BREAK_LIVE_BACKTEST_ALIGN) | +| `RANGE_BREAK_LIVE_SIGNAL_LOOKBACK_BARS` | `text` | NULL | NULL | 박스권돌파: LIVE SIGNAL LOOKBACK 봉수 (RANGE_BREAK_LIVE_SIGNAL_LOOKBACK_BARS) | +| `RANGE_BREAK_UNIVERSE_SOURCE` | `text` | NULL | NULL | 박스권돌파: 유니버스소스 (RANGE_BREAK_UNIVERSE_SOURCE) | +| `RANK_RANGE_BREAK_SORT` | `text` | NULL | NULL | 박스권돌파: RANK RANGE BREAK SORT (RANK_RANGE_BREAK_SORT) | +| `RANK_RANGE_BREAK_LIMIT` | `text` | NULL | NULL | 박스권돌파: RANK RANGE BREAK LIMIT (RANK_RANGE_BREAK_LIMIT) | +| `CONDITION_RANGE_BREAK_NAME` | `text` | NULL | NULL | 박스권돌파: CONDITION RANGE BREAK NAME (CONDITION_RANGE_BREAK_NAME) | +| `CONDITION_RANGE_BREAK_SEQ` | `text` | NULL | NULL | 박스권돌파: CONDITION RANGE BREAK SEQ (CONDITION_RANGE_BREAK_SEQ) | +| `RANGE_BREAK_CAND_LIMIT` | `text` | NULL | NULL | 박스권돌파: 후보상한 (RANGE_BREAK_CAND_LIMIT) | +| `RANGE_BREAK_BOX_LOOKBACK_MIN` | `text` | NULL | NULL | 박스권돌파: BOX LOOKBACK 최소 (RANGE_BREAK_BOX_LOOKBACK_MIN) | +| `RANGE_BREAK_BOX_MAX_WIDTH_PCT` | `text` | NULL | NULL | 박스권돌파: BOX 최대 WIDTH % (RANGE_BREAK_BOX_MAX_WIDTH_PCT) | +| `RANGE_BREAK_BOX_MIN_WIDTH_PCT` | `text` | NULL | NULL | 박스권돌파: BOX 최소 WIDTH % (RANGE_BREAK_BOX_MIN_WIDTH_PCT) | +| `RANGE_BREAK_SETUP_VOL_MAX_MULT` | `text` | NULL | NULL | 박스권돌파: SETUP 거래량 최대 배수 (RANGE_BREAK_SETUP_VOL_MAX_MULT) | +| `RANGE_BREAK_SETUP_BEAR_BARS_MIN` | `text` | NULL | NULL | 박스권돌파: SETUP BEAR 봉수 최소 (RANGE_BREAK_SETUP_BEAR_BARS_MIN) | +| `RANGE_BREAK_VOL_MULT` | `text` | NULL | NULL | 박스권돌파: 거래량배수 (RANGE_BREAK_VOL_MULT) | +| `RANGE_BREAK_VOL_WIN` | `text` | NULL | NULL | 박스권돌파: 거래량윈도우 (RANGE_BREAK_VOL_WIN) | +| `RANGE_BREAK_VOL_BASELINE_WIN` | `text` | NULL | NULL | 박스권돌파: 거래량 BASELINE WIN (RANGE_BREAK_VOL_BASELINE_WIN) | +| `RANGE_BREAK_BREAK_MARGIN_PCT` | `text` | NULL | NULL | 박스권돌파: BREAK MARGIN % (RANGE_BREAK_BREAK_MARGIN_PCT) | +| `RANGE_BREAK_BODY_MIN_PCT` | `text` | NULL | NULL | 박스권돌파: BODY 최소 % (RANGE_BREAK_BODY_MIN_PCT) | +| `RANGE_BREAK_TIME_START` | `text` | NULL | NULL | 박스권돌파: 매수시작 (RANGE_BREAK_TIME_START) | +| `RANGE_BREAK_TIME_END_HM` | `text` | NULL | NULL | 박스권돌파: 매수종료 HHMM (RANGE_BREAK_TIME_END_HM) | +| `RANGE_BREAK_STOP_LOSS_PCT` | `text` | NULL | NULL | 박스권돌파: 손절 % (RANGE_BREAK_STOP_LOSS_PCT) | +| `RANGE_BREAK_TAKE_PROFIT_PCT` | `text` | NULL | NULL | 박스권돌파: 익절 % (RANGE_BREAK_TAKE_PROFIT_PCT) | +| `RANGE_BREAK_TRAIL_PCT` | `text` | NULL | NULL | 박스권돌파: 트레일 % (RANGE_BREAK_TRAIL_PCT) | +| `RANGE_BREAK_TRAIL_ARM_PCT` | `text` | NULL | NULL | 박스권돌파: 트레일무장 % (RANGE_BREAK_TRAIL_ARM_PCT) | +| `RANGE_BREAK_SHOULDER_MIN_HIGH_PCT` | `text` | NULL | NULL | 박스권돌파: 어깨무장고점 % (RANGE_BREAK_SHOULDER_MIN_HIGH_PCT) | +| `RANGE_BREAK_SHOULDER_CUT_PCT` | `text` | NULL | NULL | 박스권돌파: 어깨컷 % (RANGE_BREAK_SHOULDER_CUT_PCT) | +| `RANGE_BREAK_MAX_HOLD_BARS` | `text` | NULL | NULL | 박스권돌파: 최대보유봉 (RANGE_BREAK_MAX_HOLD_BARS) | +| `RANGE_BREAK_MAX_DAILY` | `text` | NULL | NULL | 박스권돌파: 일일매수횟수상한 (RANGE_BREAK_MAX_DAILY) | +| `RANGE_BREAK_COOLDOWN_SEC` | `text` | NULL | NULL | 박스권돌파: 쿨다운 초 (RANGE_BREAK_COOLDOWN_SEC) | +| `RANGE_BREAK_MAX_DAILY_CHG` | `text` | NULL | NULL | 박스권돌파: 당일등락상한 (RANGE_BREAK_MAX_DAILY_CHG) | +| `RANGE_BREAK_MIN_PRICE` | `text` | NULL | NULL | 박스권돌파: 최소가격 (RANGE_BREAK_MIN_PRICE) | +| `RANGE_BREAK_HIGH_CHASE_THR` | `text` | NULL | NULL | 박스권돌파: 고점추격 THR (RANGE_BREAK_HIGH_CHASE_THR) | +| `RANGE_BREAK_USE_HIGH_CHASE_FILTER` | `text` | NULL | NULL | 박스권돌파: 사용_ 고점추격 필터 (RANGE_BREAK_USE_HIGH_CHASE_FILTER) | +| `RANGE_BREAK_MAX_LOSS_PER_TRADE_KRW` | `text` | NULL | NULL | 박스권돌파: 최대 LOSS PER TRADE 원 (RANGE_BREAK_MAX_LOSS_PER_TRADE_KRW) | +| `RANGE_BREAK_SLOT_MONEY` | `text` | NULL | NULL | 박스권돌파: 슬롯금액 (RANGE_BREAK_SLOT_MONEY) | +| `RANGE_BREAK_MAX_BUY_AMOUNT` | `text` | NULL | NULL | 박스권돌파: 1회최대매수금액 (RANGE_BREAK_MAX_BUY_AMOUNT) | +| `RANGE_BREAK_MIN_INVEST_RATIO_OF_SLOT` | `text` | NULL | NULL | 박스권돌파: 최소 INVEST RATIO OF SLOT (RANGE_BREAK_MIN_INVEST_RATIO_OF_SLOT) | +| `RANGE_BREAK_FORCE_EOD_EXIT` | `text` | NULL | NULL | 박스권돌파: FORCE 장마감 EXIT (RANGE_BREAK_FORCE_EOD_EXIT) | +| `KIS_RANGE_BREAK_MM_CHANNEL` | `text` | NULL | NULL | 박스권돌파: KIS RANGE BREAK MM CHANNEL (KIS_RANGE_BREAK_MM_CHANNEL) | +| `RANGE_BREAK_MAX_STOCKS` | `text` | NULL | NULL | 박스권돌파: 동시보유상한 (RANGE_BREAK_MAX_STOCKS) | +| `RANGE_BREAK_TOTAL_BUDGET_KRW` | `text` | NULL | NULL | 박스권돌파: 총운용한도 원 (RANGE_BREAK_TOTAL_BUDGET_KRW) | +| `RANGE_BREAK_DAILY_PROFIT_TARGET_ENABLED` | `text` | NULL | NULL | 박스권돌파: 일일익절 목표 ON/OFF (RANGE_BREAK_DAILY_PROFIT_TARGET_ENABLED) | +| `RANGE_BREAK_DAILY_PROFIT_TARGET_KRW` | `text` | NULL | NULL | 박스권돌파: 일일익절 목표 원 (RANGE_BREAK_DAILY_PROFIT_TARGET_KRW) | +| `RANGE_BREAK_DAILY_PROFIT_TARGET_PCT` | `text` | NULL | NULL | 박스권돌파: 일일익절 목표 % (RANGE_BREAK_DAILY_PROFIT_TARGET_PCT) | +| `RANGE_BREAK_DAILY_PROFIT_HALT_NEW_BUYS` | `text` | NULL | NULL | 박스권돌파: 일일익절 중단 NEW BUYS (RANGE_BREAK_DAILY_PROFIT_HALT_NEW_BUYS) | +| `RANGE_BREAK_DAILY_PROFIT_MODE` | `text` | NULL | NULL | 박스권돌파: 일일익절 모드 (RANGE_BREAK_DAILY_PROFIT_MODE) | +| `RANGE_BREAK_DAILY_PROFIT_TRAIL_TIERS` | `text` | NULL | NULL | 박스권돌파: 일일익절 트레일 다단 (RANGE_BREAK_DAILY_PROFIT_TRAIL_TIERS) | +| `RANGE_BREAK_DAILY_PROFIT_TRAIL_DROP_PCT` | `text` | NULL | NULL | 박스권돌파: 일일익절 트레일 되돌림/하락 % (RANGE_BREAK_DAILY_PROFIT_TRAIL_DROP_PCT) | +| `RANGE_BREAK_DAILY_PROFIT_TRAIL_ARM_KRW` | `text` | NULL | NULL | 박스권돌파: 일일익절 트레일무장 원 (RANGE_BREAK_DAILY_PROFIT_TRAIL_ARM_KRW) | +| `RANGE_BREAK_DAILY_PROFIT_TRAIL_ARM_PCT` | `text` | NULL | NULL | 박스권돌파: 일일익절 트레일무장 % (RANGE_BREAK_DAILY_PROFIT_TRAIL_ARM_PCT) | +| `RANGE_BREAK_BACKTEST_USE_TICK_EXIT` | `text` | NULL | NULL | 박스돌파 틱 청산 · ON=ws_ticks 시간순 청산 · OFF=OHLC intrabar | +| `RANGE_BREAK_BACKTEST_TICK_FALLBACK_OHLC` | `text` | NULL | NULL | ⚠ 박스돌파 OHLC 폴백 (정합위험·금지) · 절대금지 — 틱 청산 ON 시 엔진 무시 | +| `RANGE_BREAK_BACKTEST_POLL_MS` | `text` | NULL | NULL | 박스돌파 틱청산 폴링(ms) · 실매 폴링 정합 · 하한 50ms | +| `RANGE_BREAK_BACKTEST_SELL_SLIP_PCT` | `text` | NULL | NULL | 박스권돌파: BACKTEST SELL SLIP % (RANGE_BREAK_BACKTEST_SELL_SLIP_PCT) | +| `RANGE_BREAK_DAILY_PROFIT_RISK_BUDGET_ENABLED` | `text` | NULL | NULL | 박스권돌파: 일일익절 RISK 한도 ON/OFF (RANGE_BREAK_DAILY_PROFIT_RISK_BUDGET_ENABLED) | +| `RANGE_BREAK_DAILY_PROFIT_RISK_BUDGET_COOLDOWN_SEC` | `text` | NULL | NULL | 박스권돌파: 일일익절 RISK 한도 쿨다운 초 (RANGE_BREAK_DAILY_PROFIT_RISK_BUDGET_COOLDOWN_SEC) | +| `RANGE_BREAK_TICK_SELL_ENABLED` | `text` | NULL | NULL | 박스권돌파: 틱매도 ON/OFF (RANGE_BREAK_TICK_SELL_ENABLED) | + +
+DDL 원문 + +```sql +CREATE TABLE `config_range_break` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `created_at` varchar(30) NOT NULL, + `STRATEGY_RANGE_BREAK_ENABLED` text DEFAULT NULL, + `RANGE_BREAK_LIVE_BACKTEST_ALIGN` text DEFAULT NULL, + `RANGE_BREAK_LIVE_SIGNAL_LOOKBACK_BARS` text DEFAULT NULL, + `RANGE_BREAK_UNIVERSE_SOURCE` text DEFAULT NULL, + `RANK_RANGE_BREAK_SORT` text DEFAULT NULL, + `RANK_RANGE_BREAK_LIMIT` text DEFAULT NULL, + `CONDITION_RANGE_BREAK_NAME` text DEFAULT NULL, + `CONDITION_RANGE_BREAK_SEQ` text DEFAULT NULL, + `RANGE_BREAK_CAND_LIMIT` text DEFAULT NULL, + `RANGE_BREAK_BOX_LOOKBACK_MIN` text DEFAULT NULL, + `RANGE_BREAK_BOX_MAX_WIDTH_PCT` text DEFAULT NULL, + `RANGE_BREAK_BOX_MIN_WIDTH_PCT` text DEFAULT NULL, + `RANGE_BREAK_SETUP_VOL_MAX_MULT` text DEFAULT NULL, + `RANGE_BREAK_SETUP_BEAR_BARS_MIN` text DEFAULT NULL, + `RANGE_BREAK_VOL_MULT` text DEFAULT NULL, + `RANGE_BREAK_VOL_WIN` text DEFAULT NULL, + `RANGE_BREAK_VOL_BASELINE_WIN` text DEFAULT NULL, + `RANGE_BREAK_BREAK_MARGIN_PCT` text DEFAULT NULL, + `RANGE_BREAK_BODY_MIN_PCT` text DEFAULT NULL, + `RANGE_BREAK_TIME_START` text DEFAULT NULL, + `RANGE_BREAK_TIME_END_HM` text DEFAULT NULL, + `RANGE_BREAK_STOP_LOSS_PCT` text DEFAULT NULL, + `RANGE_BREAK_TAKE_PROFIT_PCT` text DEFAULT NULL, + `RANGE_BREAK_TRAIL_PCT` text DEFAULT NULL, + `RANGE_BREAK_TRAIL_ARM_PCT` text DEFAULT NULL, + `RANGE_BREAK_SHOULDER_MIN_HIGH_PCT` text DEFAULT NULL, + `RANGE_BREAK_SHOULDER_CUT_PCT` text DEFAULT NULL, + `RANGE_BREAK_MAX_HOLD_BARS` text DEFAULT NULL, + `RANGE_BREAK_MAX_DAILY` text DEFAULT NULL, + `RANGE_BREAK_COOLDOWN_SEC` text DEFAULT NULL, + `RANGE_BREAK_MAX_DAILY_CHG` text DEFAULT NULL, + `RANGE_BREAK_MIN_PRICE` text DEFAULT NULL, + `RANGE_BREAK_HIGH_CHASE_THR` text DEFAULT NULL, + `RANGE_BREAK_USE_HIGH_CHASE_FILTER` text DEFAULT NULL, + `RANGE_BREAK_MAX_LOSS_PER_TRADE_KRW` text DEFAULT NULL, + `RANGE_BREAK_SLOT_MONEY` text DEFAULT NULL, + `RANGE_BREAK_MAX_BUY_AMOUNT` text DEFAULT NULL, + `RANGE_BREAK_MIN_INVEST_RATIO_OF_SLOT` text DEFAULT NULL, + `RANGE_BREAK_FORCE_EOD_EXIT` text DEFAULT NULL, + `KIS_RANGE_BREAK_MM_CHANNEL` text DEFAULT NULL, + `RANGE_BREAK_MAX_STOCKS` text DEFAULT NULL, + `RANGE_BREAK_TOTAL_BUDGET_KRW` text DEFAULT NULL, + `RANGE_BREAK_DAILY_PROFIT_TARGET_ENABLED` text DEFAULT NULL, + `RANGE_BREAK_DAILY_PROFIT_TARGET_KRW` text DEFAULT NULL, + `RANGE_BREAK_DAILY_PROFIT_TARGET_PCT` text DEFAULT NULL, + `RANGE_BREAK_DAILY_PROFIT_HALT_NEW_BUYS` text DEFAULT NULL, + `RANGE_BREAK_DAILY_PROFIT_MODE` text DEFAULT NULL, + `RANGE_BREAK_DAILY_PROFIT_TRAIL_TIERS` text DEFAULT NULL, + `RANGE_BREAK_DAILY_PROFIT_TRAIL_DROP_PCT` text DEFAULT NULL, + `RANGE_BREAK_DAILY_PROFIT_TRAIL_ARM_KRW` text DEFAULT NULL, + `RANGE_BREAK_DAILY_PROFIT_TRAIL_ARM_PCT` text DEFAULT NULL, + `RANGE_BREAK_BACKTEST_USE_TICK_EXIT` text DEFAULT NULL, + `RANGE_BREAK_BACKTEST_TICK_FALLBACK_OHLC` text DEFAULT NULL, + `RANGE_BREAK_BACKTEST_POLL_MS` text DEFAULT NULL, + `RANGE_BREAK_BACKTEST_SELL_SLIP_PCT` text DEFAULT NULL, + `RANGE_BREAK_DAILY_PROFIT_RISK_BUDGET_ENABLED` text DEFAULT NULL, + `RANGE_BREAK_DAILY_PROFIT_RISK_BUDGET_COOLDOWN_SEC` text DEFAULT NULL, + `RANGE_BREAK_TICK_SELL_ENABLED` text DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=307 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `config_dbband` + +**PK**: `id` | AUTO_INCREMENT=319 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `int(11)` | NOT NULL | | PK · AUTO_INCREMENT | +| `created_at` | `varchar(30)` | NOT NULL | | 설정행 생성시각 | +| `DBBAND_BB_PERIOD` | `text` | NULL | NULL | 더블BB: BB 기간 (DBBAND_BB_PERIOD) | +| `DBBAND_BB_INNER_STD` | `text` | NULL | NULL | 더블BB: BB INNER STD (DBBAND_BB_INNER_STD) | +| `DBBAND_BB_OUTER_STD` | `text` | NULL | NULL | 더블BB: BB OUTER STD (DBBAND_BB_OUTER_STD) | +| `DBBAND_TREND_MA_PERIOD` | `text` | NULL | NULL | 더블BB: TREND MA 기간 (DBBAND_TREND_MA_PERIOD) | +| `DBBAND_USE_TREND_FILTER` | `text` | NULL | NULL | 더블BB: 사용_ TREND 필터 (DBBAND_USE_TREND_FILTER) | +| `DBBAND_SIDE_MODE` | `text` | NULL | NULL | 더블BB: SIDE 모드 (DBBAND_SIDE_MODE) | +| `DBBAND_ENTRY_VALID_BARS` | `text` | NULL | NULL | 더블BB: ENTRY VALID 봉수 (DBBAND_ENTRY_VALID_BARS) | +| `DBBAND_ENTRY_MODE` | `text` | NULL | NULL | 더블BB: ENTRY 모드 (DBBAND_ENTRY_MODE) | +| `DBBAND_STOP_MODE` | `text` | NULL | NULL | 더블BB: STOP 모드 (DBBAND_STOP_MODE) | +| `DBBAND_STOP_BUFFER_PCT` | `text` | NULL | NULL | 더블BB: STOP BUFFER % (DBBAND_STOP_BUFFER_PCT) | +| `DBBAND_STOP_LOSS_PCT` | `text` | NULL | NULL | 더블BB: 손절 % (DBBAND_STOP_LOSS_PCT) | +| `DBBAND_TP_MODE` | `text` | NULL | NULL | 더블BB: TP 모드 (DBBAND_TP_MODE) | +| `DBBAND_TAKE_PROFIT_PCT` | `text` | NULL | NULL | 더블BB: 익절 % (DBBAND_TAKE_PROFIT_PCT) | +| `DBBAND_RR_RATIO` | `text` | NULL | NULL | 더블BB: RR RATIO (DBBAND_RR_RATIO) | +| `DBBAND_SHOULDER_MIN_HIGH_PCT` | `text` | NULL | NULL | 더블BB: 어깨무장고점 % (DBBAND_SHOULDER_MIN_HIGH_PCT) | +| `DBBAND_SHOULDER_CUT_PCT` | `text` | NULL | NULL | 더블BB: 어깨컷 % (DBBAND_SHOULDER_CUT_PCT) | +| `DBBAND_TRAIL_PCT` | `text` | NULL | NULL | 더블BB: 트레일 % (DBBAND_TRAIL_PCT) | +| `DBBAND_TRAIL_ARM_PCT` | `text` | NULL | NULL | 더블BB: 트레일무장 % (DBBAND_TRAIL_ARM_PCT) | +| `DBBAND_TIME_START` | `text` | NULL | NULL | 더블BB: 매수시작 (DBBAND_TIME_START) | +| `DBBAND_TIME_END` | `text` | NULL | NULL | 더블BB: 매수종료 (DBBAND_TIME_END) | +| `DBBAND_COOLDOWN_SEC` | `text` | NULL | NULL | 더블BB: 쿨다운 초 (DBBAND_COOLDOWN_SEC) | +| `DBBAND_MAX_DAILY` | `text` | NULL | NULL | 더블BB: 일일매수횟수상한 (DBBAND_MAX_DAILY) | +| `DBBAND_MIN_PRICE` | `text` | NULL | NULL | 더블BB: 최소가격 (DBBAND_MIN_PRICE) | +| `DBBAND_SLOT_MONEY` | `text` | NULL | NULL | 더블BB: 슬롯금액 (DBBAND_SLOT_MONEY) | +| `DBBAND_MAX_STOCKS` | `text` | NULL | NULL | 더블BB: 동시보유상한 (DBBAND_MAX_STOCKS) | +| `DBBAND_TOTAL_BUDGET_KRW` | `text` | NULL | NULL | 더블BB: 총운용한도 원 (DBBAND_TOTAL_BUDGET_KRW) | +| `DBBAND_MAX_BUY_AMOUNT` | `text` | NULL | NULL | 더블BB: 1회최대매수금액 (DBBAND_MAX_BUY_AMOUNT) | +| `DBBAND_MAX_HOLD_BARS` | `text` | NULL | NULL | 더블BB: 최대보유봉 (DBBAND_MAX_HOLD_BARS) | +| `DBBAND_TIMEFRAME` | `text` | NULL | NULL | 더블BB: TIMEFRAME (DBBAND_TIMEFRAME) | +| `DBBAND_MIN_INVEST_RATIO_OF_SLOT` | `text` | NULL | NULL | 더블BB: 최소 INVEST RATIO OF SLOT (DBBAND_MIN_INVEST_RATIO_OF_SLOT) | +| `DBBAND_FORCE_EOD_EXIT` | `text` | NULL | NULL | 더블BB: FORCE 장마감 EXIT (DBBAND_FORCE_EOD_EXIT) | +| `DBBAND_LIVE_MIN_CANDLES` | `text` | NULL | NULL | 더블BB: 실매최소봉수 (DBBAND_LIVE_MIN_CANDLES) | +| `DBBAND_LIVE_SIGNAL_LOOKBACK_BARS` | `text` | NULL | NULL | 더블BB: LIVE SIGNAL LOOKBACK 봉수 (DBBAND_LIVE_SIGNAL_LOOKBACK_BARS) | +| `DBBAND_UNIVERSE_SOURCE` | `text` | NULL | NULL | 더블BB: 유니버스소스 (DBBAND_UNIVERSE_SOURCE) | +| `DBBAND_CANDLE_FETCH_N` | `text` | NULL | NULL | 더블BB: CANDLE FETCH N (DBBAND_CANDLE_FETCH_N) | +| `STRATEGY_DBBAND_ENABLED` | `text` | NULL | NULL | 더블BB ON/OFF. 다음루프 신규매수중단(보유청산유지). WS구독해제 안함 | +| `KIS_DBBAND_MM_CHANNEL` | `text` | NULL | NULL | 더블BB: KIS DBBAND MM CHANNEL (KIS_DBBAND_MM_CHANNEL) | +| `RANK_DBBAND_SORT` | `text` | NULL | NULL | 더블BB: RANK DBBAND SORT (RANK_DBBAND_SORT) | +| `RANK_DBBAND_LIMIT` | `text` | NULL | NULL | 더블BB: RANK DBBAND LIMIT (RANK_DBBAND_LIMIT) | +| `CONDITION_DBBAND_NAME` | `text` | NULL | NULL | 더블BB: CONDITION DBBAND NAME (CONDITION_DBBAND_NAME) | +| `CONDITION_DBBAND_SEQ` | `text` | NULL | NULL | 더블BB: CONDITION DBBAND SEQ (CONDITION_DBBAND_SEQ) | +| `DBBAND_GRID_BB_PERIOD0` | `text` | NULL | NULL | 더블BB: GRID BB 기간 0 (DBBAND_GRID_BB_PERIOD0) | +| `DBBAND_GRID_BB_PERIOD1` | `text` | NULL | NULL | 더블BB: GRID BB 기간 1 (DBBAND_GRID_BB_PERIOD1) | +| `DBBAND_GRID_INNER_STD0` | `text` | NULL | NULL | 더블BB: GRID INNER STD0 (DBBAND_GRID_INNER_STD0) | +| `DBBAND_GRID_INNER_STD1` | `text` | NULL | NULL | 더블BB: GRID INNER STD1 (DBBAND_GRID_INNER_STD1) | +| `DBBAND_GRID_OUTER_STD0` | `text` | NULL | NULL | 더블BB: GRID OUTER STD0 (DBBAND_GRID_OUTER_STD0) | +| `DBBAND_GRID_OUTER_STD1` | `text` | NULL | NULL | 더블BB: GRID OUTER STD1 (DBBAND_GRID_OUTER_STD1) | +| `DBBAND_GRID_TREND_MA0` | `text` | NULL | NULL | 더블BB: GRID TREND MA0 (DBBAND_GRID_TREND_MA0) | +| `DBBAND_GRID_TREND_MA1` | `text` | NULL | NULL | 더블BB: GRID TREND MA1 (DBBAND_GRID_TREND_MA1) | +| `DBBAND_GRID_SL0` | `text` | NULL | NULL | 더블BB: GRID SL0 (DBBAND_GRID_SL0) | +| `DBBAND_GRID_SL1` | `text` | NULL | NULL | 더블BB: GRID SL1 (DBBAND_GRID_SL1) | +| `DBBAND_GRID_TP0` | `text` | NULL | NULL | 더블BB: GRID TP0 (DBBAND_GRID_TP0) | +| `DBBAND_GRID_TP1` | `text` | NULL | NULL | 더블BB: GRID TP1 (DBBAND_GRID_TP1) | +| `DBBAND_GRID_RR0` | `text` | NULL | NULL | 더블BB: GRID RR0 (DBBAND_GRID_RR0) | +| `DBBAND_GRID_RR1` | `text` | NULL | NULL | 더블BB: GRID RR1 (DBBAND_GRID_RR1) | +| `DBBAND_EXIT_MODE` | `text` | NULL | NULL | 더블BB: EXIT 모드 (DBBAND_EXIT_MODE) | +| `DBBAND_DAILY_PROFIT_TARGET_ENABLED` | `text` | NULL | NULL | 더블BB: 일일익절 목표 ON/OFF (DBBAND_DAILY_PROFIT_TARGET_ENABLED) | +| `DBBAND_DAILY_PROFIT_TARGET_KRW` | `text` | NULL | NULL | 더블BB: 일일익절 목표 원 (DBBAND_DAILY_PROFIT_TARGET_KRW) | +| `DBBAND_DAILY_PROFIT_TARGET_PCT` | `text` | NULL | NULL | 더블BB: 일일익절 목표 % (DBBAND_DAILY_PROFIT_TARGET_PCT) | +| `DBBAND_DAILY_PROFIT_HALT_NEW_BUYS` | `text` | NULL | NULL | 더블BB: 일일익절 중단 NEW BUYS (DBBAND_DAILY_PROFIT_HALT_NEW_BUYS) | +| `DBBAND_DAILY_PROFIT_MODE` | `text` | NULL | NULL | 더블BB: 일일익절 모드 (DBBAND_DAILY_PROFIT_MODE) | +| `DBBAND_DAILY_PROFIT_TRAIL_TIERS` | `text` | NULL | NULL | 더블BB: 일일익절 트레일 다단 (DBBAND_DAILY_PROFIT_TRAIL_TIERS) | +| `DBBAND_DAILY_PROFIT_TRAIL_DROP_PCT` | `text` | NULL | NULL | 더블BB: 일일익절 트레일 되돌림/하락 % (DBBAND_DAILY_PROFIT_TRAIL_DROP_PCT) | +| `DBBAND_DAILY_PROFIT_TRAIL_ARM_KRW` | `text` | NULL | NULL | 더블BB: 일일익절 트레일무장 원 (DBBAND_DAILY_PROFIT_TRAIL_ARM_KRW) | +| `DBBAND_DAILY_PROFIT_TRAIL_ARM_PCT` | `text` | NULL | NULL | 더블BB: 일일익절 트레일무장 % (DBBAND_DAILY_PROFIT_TRAIL_ARM_PCT) | +| `DBBAND_BACKTEST_USE_TICK_EXIT` | `text` | NULL | NULL | 더블BB 틱 청산 · ON=ws_ticks 시간순 청산 · OFF=봉 OHLC 1회 | +| `DBBAND_BACKTEST_TICK_FALLBACK_OHLC` | `text` | NULL | NULL | ⚠ 더블BB OHLC 폴백 (정합위험·금지) · 절대금지 — 틱 청산 ON 시 엔진 무시 | +| `DBBAND_BACKTEST_POLL_MS` | `text` | NULL | NULL | 더블BB 틱청산 폴링(ms) · 실매 폴링 정합 · 하한 50ms | +| `DBBAND_BACKTEST_SELL_SLIP_PCT` | `text` | NULL | NULL | 더블BB: BACKTEST SELL SLIP % (DBBAND_BACKTEST_SELL_SLIP_PCT) | +| `DBBAND_DAILY_PROFIT_RISK_BUDGET_ENABLED` | `text` | NULL | NULL | 더블BB: 일일익절 RISK 한도 ON/OFF (DBBAND_DAILY_PROFIT_RISK_BUDGET_ENABLED) | +| `DBBAND_DAILY_PROFIT_RISK_BUDGET_COOLDOWN_SEC` | `text` | NULL | NULL | 더블BB: 일일익절 RISK 한도 쿨다운 초 (DBBAND_DAILY_PROFIT_RISK_BUDGET_COOLDOWN_SEC) | +| `DBBAND_TICK_SELL_ENABLED` | `text` | NULL | NULL | 더블BB: 틱매도 ON/OFF (DBBAND_TICK_SELL_ENABLED) | + +
+DDL 원문 + +```sql +CREATE TABLE `config_dbband` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `created_at` varchar(30) NOT NULL, + `DBBAND_BB_PERIOD` text DEFAULT NULL, + `DBBAND_BB_INNER_STD` text DEFAULT NULL, + `DBBAND_BB_OUTER_STD` text DEFAULT NULL, + `DBBAND_TREND_MA_PERIOD` text DEFAULT NULL, + `DBBAND_USE_TREND_FILTER` text DEFAULT NULL, + `DBBAND_SIDE_MODE` text DEFAULT NULL, + `DBBAND_ENTRY_VALID_BARS` text DEFAULT NULL, + `DBBAND_ENTRY_MODE` text DEFAULT NULL, + `DBBAND_STOP_MODE` text DEFAULT NULL, + `DBBAND_STOP_BUFFER_PCT` text DEFAULT NULL, + `DBBAND_STOP_LOSS_PCT` text DEFAULT NULL, + `DBBAND_TP_MODE` text DEFAULT NULL, + `DBBAND_TAKE_PROFIT_PCT` text DEFAULT NULL, + `DBBAND_RR_RATIO` text DEFAULT NULL, + `DBBAND_SHOULDER_MIN_HIGH_PCT` text DEFAULT NULL, + `DBBAND_SHOULDER_CUT_PCT` text DEFAULT NULL, + `DBBAND_TRAIL_PCT` text DEFAULT NULL, + `DBBAND_TRAIL_ARM_PCT` text DEFAULT NULL, + `DBBAND_TIME_START` text DEFAULT NULL, + `DBBAND_TIME_END` text DEFAULT NULL, + `DBBAND_COOLDOWN_SEC` text DEFAULT NULL, + `DBBAND_MAX_DAILY` text DEFAULT NULL, + `DBBAND_MIN_PRICE` text DEFAULT NULL, + `DBBAND_SLOT_MONEY` text DEFAULT NULL, + `DBBAND_MAX_STOCKS` text DEFAULT NULL, + `DBBAND_TOTAL_BUDGET_KRW` text DEFAULT NULL, + `DBBAND_MAX_BUY_AMOUNT` text DEFAULT NULL, + `DBBAND_MAX_HOLD_BARS` text DEFAULT NULL, + `DBBAND_TIMEFRAME` text DEFAULT NULL, + `DBBAND_MIN_INVEST_RATIO_OF_SLOT` text DEFAULT NULL, + `DBBAND_FORCE_EOD_EXIT` text DEFAULT NULL, + `DBBAND_LIVE_MIN_CANDLES` text DEFAULT NULL, + `DBBAND_LIVE_SIGNAL_LOOKBACK_BARS` text DEFAULT NULL, + `DBBAND_UNIVERSE_SOURCE` text DEFAULT NULL, + `DBBAND_CANDLE_FETCH_N` text DEFAULT NULL, + `STRATEGY_DBBAND_ENABLED` text DEFAULT NULL, + `KIS_DBBAND_MM_CHANNEL` text DEFAULT NULL, + `RANK_DBBAND_SORT` text DEFAULT NULL, + `RANK_DBBAND_LIMIT` text DEFAULT NULL, + `CONDITION_DBBAND_NAME` text DEFAULT NULL, + `CONDITION_DBBAND_SEQ` text DEFAULT NULL, + `DBBAND_GRID_BB_PERIOD0` text DEFAULT NULL, + `DBBAND_GRID_BB_PERIOD1` text DEFAULT NULL, + `DBBAND_GRID_INNER_STD0` text DEFAULT NULL, + `DBBAND_GRID_INNER_STD1` text DEFAULT NULL, + `DBBAND_GRID_OUTER_STD0` text DEFAULT NULL, + `DBBAND_GRID_OUTER_STD1` text DEFAULT NULL, + `DBBAND_GRID_TREND_MA0` text DEFAULT NULL, + `DBBAND_GRID_TREND_MA1` text DEFAULT NULL, + `DBBAND_GRID_SL0` text DEFAULT NULL, + `DBBAND_GRID_SL1` text DEFAULT NULL, + `DBBAND_GRID_TP0` text DEFAULT NULL, + `DBBAND_GRID_TP1` text DEFAULT NULL, + `DBBAND_GRID_RR0` text DEFAULT NULL, + `DBBAND_GRID_RR1` text DEFAULT NULL, + `DBBAND_EXIT_MODE` text DEFAULT NULL, + `DBBAND_DAILY_PROFIT_TARGET_ENABLED` text DEFAULT NULL, + `DBBAND_DAILY_PROFIT_TARGET_KRW` text DEFAULT NULL, + `DBBAND_DAILY_PROFIT_TARGET_PCT` text DEFAULT NULL, + `DBBAND_DAILY_PROFIT_HALT_NEW_BUYS` text DEFAULT NULL, + `DBBAND_DAILY_PROFIT_MODE` text DEFAULT NULL, + `DBBAND_DAILY_PROFIT_TRAIL_TIERS` text DEFAULT NULL, + `DBBAND_DAILY_PROFIT_TRAIL_DROP_PCT` text DEFAULT NULL, + `DBBAND_DAILY_PROFIT_TRAIL_ARM_KRW` text DEFAULT NULL, + `DBBAND_DAILY_PROFIT_TRAIL_ARM_PCT` text DEFAULT NULL, + `DBBAND_BACKTEST_USE_TICK_EXIT` text DEFAULT NULL, + `DBBAND_BACKTEST_TICK_FALLBACK_OHLC` text DEFAULT NULL, + `DBBAND_BACKTEST_POLL_MS` text DEFAULT NULL, + `DBBAND_BACKTEST_SELL_SLIP_PCT` text DEFAULT NULL, + `DBBAND_DAILY_PROFIT_RISK_BUDGET_ENABLED` text DEFAULT NULL, + `DBBAND_DAILY_PROFIT_RISK_BUDGET_COOLDOWN_SEC` text DEFAULT NULL, + `DBBAND_TICK_SELL_ENABLED` text DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=319 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `config_dart` + +**PK**: `id` | AUTO_INCREMENT=114 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `int(11)` | NOT NULL | | PK · AUTO_INCREMENT | +| `created_at` | `varchar(30)` | NOT NULL | | 설정행 생성시각 | +| `STRATEGY_DART_ENABLED` | `text` | NULL | NULL | DART수주 ON/OFF. 다음루프 신규매수중단(보유청산유지). WS구독해제 안함 | +| `KIS_DART_MM_CHANNEL` | `text` | NULL | NULL | KIS DART MM CHANNEL (KIS_DART_MM_CHANNEL) | +| `DART_SCAN_ENABLED` | `text` | NULL | NULL | DART: SCAN ON/OFF (DART_SCAN_ENABLED) | +| `DART_ENABLED` | `text` | NULL | NULL | DART: ON/OFF (DART_ENABLED) | +| `DART_POLL_SEC` | `text` | NULL | NULL | DART: POLL 초 (DART_POLL_SEC) | +| `DART_LOOKBACK_DAYS` | `text` | NULL | NULL | DART: LOOKBACK DAYS (DART_LOOKBACK_DAYS) | +| `DART_MM_CHANNEL` | `text` | NULL | NULL | DART: MM CHANNEL (DART_MM_CHANNEL) | +| `DART_SAVE_CANDIDATES` | `text` | NULL | NULL | DART: SAVE CANDIDATES (DART_SAVE_CANDIDATES) | +| `DART_REPORT_NAME_KEYWORDS` | `text` | NULL | NULL | DART: REPORT NAME KEYWORDS (DART_REPORT_NAME_KEYWORDS) | +| `DART_SKIP_CORRECTION` | `text` | NULL | NULL | DART: SKIP CORRECTION (DART_SKIP_CORRECTION) | +| `DART_LISTED_ONLY` | `text` | NULL | NULL | DART: LISTED ONLY (DART_LISTED_ONLY) | +| `DART_LIST_MAX_PAGES` | `text` | NULL | NULL | DART: LIST 최대 PAGES (DART_LIST_MAX_PAGES) | +| `DART_SUBSCRIBE_ENABLED` | `text` | NULL | NULL | DART: SUBSCRIBE ON/OFF (DART_SUBSCRIBE_ENABLED) | +| `DART_WATCH_TTL_HOURS` | `text` | NULL | NULL | DART: WATCH TTL HOURS (DART_WATCH_TTL_HOURS) | +| `DART_WATCH_MAX` | `text` | NULL | NULL | DART: WATCH 최대 (DART_WATCH_MAX) | +| `DART_TRADE_ENABLED` | `text` | NULL | NULL | DART: TRADE ON/OFF (DART_TRADE_ENABLED) | +| `DART_SLOT_MONEY` | `text` | NULL | NULL | DART: 슬롯금액 (DART_SLOT_MONEY) | +| `DART_MAX_STOCKS` | `text` | NULL | NULL | DART: 동시보유상한 (DART_MAX_STOCKS) | +| `DART_TOTAL_BUDGET_KRW` | `text` | NULL | NULL | DART: 총운용한도 원 (DART_TOTAL_BUDGET_KRW) | +| `DART_MAX_BUY_AMOUNT` | `text` | NULL | NULL | DART: 1회최대매수금액 (DART_MAX_BUY_AMOUNT) | +| `DART_MIN_INVEST_RATIO_OF_SLOT` | `text` | NULL | NULL | DART: 최소 INVEST RATIO OF SLOT (DART_MIN_INVEST_RATIO_OF_SLOT) | +| `DART_CAND_LIMIT` | `text` | NULL | NULL | DART: 후보상한 (DART_CAND_LIMIT) | +| `DART_TIME_START` | `text` | NULL | NULL | DART: 매수시작 (DART_TIME_START) | +| `DART_TIME_END` | `text` | NULL | NULL | DART: 매수종료 (DART_TIME_END) | +| `DART_RSI_PERIOD` | `text` | NULL | NULL | DART: RSI 기간 (DART_RSI_PERIOD) | +| `DART_RSI_OVERSOLD` | `text` | NULL | NULL | DART: RSI과매도 (DART_RSI_OVERSOLD) | +| `DART_RSI_RECLAIM` | `text` | NULL | NULL | DART: RSI RECLAIM (DART_RSI_RECLAIM) | +| `DART_STOP_LOSS_PCT` | `text` | NULL | NULL | DART: 손절 % (DART_STOP_LOSS_PCT) | +| `DART_TAKE_PROFIT_PCT` | `text` | NULL | NULL | DART: 익절 % (DART_TAKE_PROFIT_PCT) | +| `DART_TRAIL_PCT` | `text` | NULL | NULL | DART: 트레일 % (DART_TRAIL_PCT) | +| `DART_TRAIL_ARM_PCT` | `text` | NULL | NULL | DART: 트레일무장 % (DART_TRAIL_ARM_PCT) | +| `DART_MAX_HOLD_BARS` | `text` | NULL | NULL | DART: 최대보유봉 (DART_MAX_HOLD_BARS) | +| `DART_EVENT_WINDOW_BARS` | `text` | NULL | NULL | DART: EVENT 윈도우 봉수 (DART_EVENT_WINDOW_BARS) | +| `DART_MIN_PRICE` | `text` | NULL | NULL | DART: 최소가격 (DART_MIN_PRICE) | +| `DART_VOL_MULT` | `text` | NULL | NULL | DART: 거래량배수 (DART_VOL_MULT) | +| `DART_VOL_WINDOW` | `text` | NULL | NULL | DART: 거래량윈도우 DOW (DART_VOL_WINDOW) | +| `DART_MAX_LOSS_PER_TRADE_KRW` | `text` | NULL | NULL | DART: 최대 LOSS PER TRADE 원 (DART_MAX_LOSS_PER_TRADE_KRW) | +| `DART_FORCE_EOD_EXIT` | `text` | NULL | NULL | DART: FORCE 장마감 EXIT (DART_FORCE_EOD_EXIT) | +| `DART_BACKTEST_USE_TICK_EXIT` | `text` | NULL | NULL | DART: BACKTEST 사용_ TICK EXIT (DART_BACKTEST_USE_TICK_EXIT) | +| `DART_BACKTEST_TICK_FALLBACK_OHLC` | `text` | NULL | NULL | DART: BACKTEST 틱없을때OHLC폴백(위험) (DART_BACKTEST_TICK_FALLBACK_OHLC) | +| `DART_QUALITY_FILTER_ENABLED` | `text` | NULL | NULL | DART: QUALITY 필터 ON/OFF (DART_QUALITY_FILTER_ENABLED) | +| `DART_EXCLUDE_CORP_KEYWORDS` | `text` | NULL | NULL | DART: EXCLUDE CORP KEYWORDS (DART_EXCLUDE_CORP_KEYWORDS) | +| `DART_EXCLUDE_CONTENT_KEYWORDS` | `text` | NULL | NULL | DART: EXCLUDE CONTENT KEYWORDS (DART_EXCLUDE_CONTENT_KEYWORDS) | +| `DART_MIN_SALES_PCT` | `text` | NULL | NULL | DART: 최소 SALES % (DART_MIN_SALES_PCT) | +| `DART_SKIP_IF_SALES_PCT_UNKNOWN` | `text` | NULL | NULL | DART: SKIP IF SALES % UNKNOWN (DART_SKIP_IF_SALES_PCT_UNKNOWN) | +| `DART_THEME_KEYWORDS` | `text` | NULL | NULL | DART: THEME KEYWORDS (DART_THEME_KEYWORDS) | +| `DART_REQUIRE_THEME` | `text` | NULL | NULL | DART: REQUIRE THEME (DART_REQUIRE_THEME) | +| `DART_FETCH_DOCUMENT` | `text` | NULL | NULL | DART: FETCH DOCUMENT (DART_FETCH_DOCUMENT) | + +
+DDL 원문 + +```sql +CREATE TABLE `config_dart` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `created_at` varchar(30) NOT NULL, + `STRATEGY_DART_ENABLED` text DEFAULT NULL, + `KIS_DART_MM_CHANNEL` text DEFAULT NULL, + `DART_SCAN_ENABLED` text DEFAULT NULL, + `DART_ENABLED` text DEFAULT NULL, + `DART_POLL_SEC` text DEFAULT NULL, + `DART_LOOKBACK_DAYS` text DEFAULT NULL, + `DART_MM_CHANNEL` text DEFAULT NULL, + `DART_SAVE_CANDIDATES` text DEFAULT NULL, + `DART_REPORT_NAME_KEYWORDS` text DEFAULT NULL, + `DART_SKIP_CORRECTION` text DEFAULT NULL, + `DART_LISTED_ONLY` text DEFAULT NULL, + `DART_LIST_MAX_PAGES` text DEFAULT NULL, + `DART_SUBSCRIBE_ENABLED` text DEFAULT NULL, + `DART_WATCH_TTL_HOURS` text DEFAULT NULL, + `DART_WATCH_MAX` text DEFAULT NULL, + `DART_TRADE_ENABLED` text DEFAULT NULL, + `DART_SLOT_MONEY` text DEFAULT NULL, + `DART_MAX_STOCKS` text DEFAULT NULL, + `DART_TOTAL_BUDGET_KRW` text DEFAULT NULL, + `DART_MAX_BUY_AMOUNT` text DEFAULT NULL, + `DART_MIN_INVEST_RATIO_OF_SLOT` text DEFAULT NULL, + `DART_CAND_LIMIT` text DEFAULT NULL, + `DART_TIME_START` text DEFAULT NULL, + `DART_TIME_END` text DEFAULT NULL, + `DART_RSI_PERIOD` text DEFAULT NULL, + `DART_RSI_OVERSOLD` text DEFAULT NULL, + `DART_RSI_RECLAIM` text DEFAULT NULL, + `DART_STOP_LOSS_PCT` text DEFAULT NULL, + `DART_TAKE_PROFIT_PCT` text DEFAULT NULL, + `DART_TRAIL_PCT` text DEFAULT NULL, + `DART_TRAIL_ARM_PCT` text DEFAULT NULL, + `DART_MAX_HOLD_BARS` text DEFAULT NULL, + `DART_EVENT_WINDOW_BARS` text DEFAULT NULL, + `DART_MIN_PRICE` text DEFAULT NULL, + `DART_VOL_MULT` text DEFAULT NULL, + `DART_VOL_WINDOW` text DEFAULT NULL, + `DART_MAX_LOSS_PER_TRADE_KRW` text DEFAULT NULL, + `DART_FORCE_EOD_EXIT` text DEFAULT NULL, + `DART_BACKTEST_USE_TICK_EXIT` text DEFAULT NULL, + `DART_BACKTEST_TICK_FALLBACK_OHLC` text DEFAULT NULL, + `DART_QUALITY_FILTER_ENABLED` text DEFAULT NULL, + `DART_EXCLUDE_CORP_KEYWORDS` text DEFAULT NULL, + `DART_EXCLUDE_CONTENT_KEYWORDS` text DEFAULT NULL, + `DART_MIN_SALES_PCT` text DEFAULT NULL, + `DART_SKIP_IF_SALES_PCT_UNKNOWN` text DEFAULT NULL, + `DART_THEME_KEYWORDS` text DEFAULT NULL, + `DART_REQUIRE_THEME` text DEFAULT NULL, + `DART_FETCH_DOCUMENT` text DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=114 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `config_updow` + +**PK**: `id` | AUTO_INCREMENT=388 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `int(11)` | NOT NULL | | PK · AUTO_INCREMENT | +| `created_at` | `varchar(30)` | NOT NULL | | 설정행 생성시각 | +| `UPDOW_UNIVERSE_SOURCE` | `text` | NULL | NULL | UPDOWN 박스 유니버스 · ls_condition=LS 동명 AFR · 시세는 키움/한투 유지 · 매니저 최초 기동은 봇 재시작 필요할 수 있음 · 키움으로 자동 폴백 안 함 | +| `RANK_UPDOW_SORT` | `text` | NULL | NULL | UPDOWN: RANK UPDOW SORT (RANK_UPDOW_SORT) | +| `RANK_UPDOW_LIMIT` | `text` | NULL | NULL | UPDOWN: RANK UPDOW LIMIT (RANK_UPDOW_LIMIT) | +| `CONDITION_UPDOW_NAME` | `text` | NULL | NULL | UPDOWN: CONDITION UPDOW NAME (CONDITION_UPDOW_NAME) | +| `CONDITION_UPDOW_SEQ` | `text` | NULL | NULL | UPDOWN: CONDITION UPDOW SEQ (CONDITION_UPDOW_SEQ) | +| `UPDOW_CAND_LIMIT` | `text` | NULL | NULL | UPDOWN 박스 후보 상한 · 0=무제한 · 조건검색/랭킹이 N개 줘도 상위 N개만 매수체크·WS구독 · 전 전략 합산이 키움 WS 100종 한도를 넘지 않게 조절 | +| `UPDOW_TIME_START` | `text` | NULL | NULL | UPDOWN: 매수시작 (UPDOW_TIME_START) | +| `UPDOW_TIME_END` | `text` | NULL | NULL | UPDOWN: 매수종료 (UPDOW_TIME_END) | +| `UPDOW_REENTRY_COOLDOWN_SEC` | `text` | NULL | NULL | UPDOWN: REENTRY 쿨다운 초 (UPDOW_REENTRY_COOLDOWN_SEC) | +| `UPDOW_MIN_CANDLE_LEN` | `text` | NULL | NULL | UPDOWN: 최소 CANDLE LEN (UPDOW_MIN_CANDLE_LEN) | +| `MIN_CANDLE_LEN_UPDOW` | `text` | NULL | NULL | UPDOWN: 최소 CANDLE LEN UPDOW (MIN_CANDLE_LEN_UPDOW) | +| `UPDOW_CANDLE_FETCH_N` | `text` | NULL | NULL | UPDOWN: CANDLE FETCH N (UPDOW_CANDLE_FETCH_N) | +| `UPDOW_LIVE_MIN_CANDLES` | `text` | NULL | NULL | UPDOWN: 실매최소봉수 (UPDOW_LIVE_MIN_CANDLES) | +| `UPDOW_MAX_LOSS_PER_TRADE_KRW` | `text` | NULL | NULL | UPDOWN: 최대 LOSS PER TRADE 원 (UPDOW_MAX_LOSS_PER_TRADE_KRW) | +| `UPDOW_MAX_BUY_AMOUNT` | `text` | NULL | NULL | UPDOWN: 1회최대매수금액 (UPDOW_MAX_BUY_AMOUNT) | +| `UPDOW_MAX_STOCKS` | `text` | NULL | NULL | UPDOWN 박스 동시 종목 | +| `UPDOW_BODY_DROP_MIN_PCT` | `text` | NULL | NULL | UPDOWN: BODY 되돌림/하락 최소 % (UPDOW_BODY_DROP_MIN_PCT) | +| `UPDOW_TP_PCT` | `text` | NULL | NULL | UPDOWN: TP % (UPDOW_TP_PCT) | +| `UPDOW_STOP_LOSS_PCT` | `text` | NULL | NULL | UPDOWN: 손절 % (UPDOW_STOP_LOSS_PCT) | +| `UPDOW_ATR_USE_DYNAMIC` | `text` | NULL | NULL | UPDOWN: ATR 사용_ DYNAMIC (UPDOW_ATR_USE_DYNAMIC) | +| `UPDOW_ATR_PERIOD` | `text` | NULL | NULL | UPDOWN: ATR 기간 (UPDOW_ATR_PERIOD) | +| `UPDOW_ATR_SL_MULT` | `text` | NULL | NULL | UPDOWN: ATR손절 배수 (UPDOW_ATR_SL_MULT) | +| `UPDOW_ATR_TP_MULT` | `text` | NULL | NULL | UPDOWN: ATR익절 배수 (UPDOW_ATR_TP_MULT) | +| `UPDOW_ATR_SL_MIN_PCT` | `text` | NULL | NULL | UPDOWN: ATR손절 최소 % (UPDOW_ATR_SL_MIN_PCT) | +| `UPDOW_ATR_SL_MAX_PCT` | `text` | NULL | NULL | UPDOWN: ATR손절 최대 % (UPDOW_ATR_SL_MAX_PCT) | +| `UPDOW_ATR_TP_MIN_PCT` | `text` | NULL | NULL | UPDOWN: ATR익절 최소 % (UPDOW_ATR_TP_MIN_PCT) | +| `UPDOW_ATR_TP_MAX_PCT` | `text` | NULL | NULL | UPDOWN: ATR익절 최대 % (UPDOW_ATR_TP_MAX_PCT) | +| `UPDOW_ATR_MULT_FLOOR` | `text` | NULL | NULL | UPDOWN: ATR 배수 FLOOR (UPDOW_ATR_MULT_FLOOR) | +| `UPDOW_ATR_PCT_FLOOR` | `text` | NULL | NULL | UPDOWN: ATR % FLOOR (UPDOW_ATR_PCT_FLOOR) | +| `UPDOW_EXIT_PCT_FLOOR` | `text` | NULL | NULL | UPDOWN: EXIT % FLOOR (UPDOW_EXIT_PCT_FLOOR) | +| `UPDOW_MAX_HOLD_BARS` | `text` | NULL | NULL | UPDOWN: 최대보유봉 (UPDOW_MAX_HOLD_BARS) | +| `UPDOW_EXIT_ON_GREEN` | `text` | NULL | NULL | UPDOWN: EXIT ON GREEN (UPDOW_EXIT_ON_GREEN) | +| `UPDOW_SLOT_MONEY` | `text` | NULL | NULL | UPDOWN 박스 슬롯(원) | +| `UPDOW_TF_MIN` | `text` | NULL | NULL | UPDOWN: TF 최소 (UPDOW_TF_MIN) | +| `UPDOW_GRID_BODY0` | `text` | NULL | NULL | UPDOWN: GRID BODY0 (UPDOW_GRID_BODY0) | +| `UPDOW_GRID_BODY1` | `text` | NULL | NULL | UPDOWN: GRID BODY1 (UPDOW_GRID_BODY1) | +| `UPDOW_GRID_BODY2` | `text` | NULL | NULL | UPDOWN: GRID BODY2 (UPDOW_GRID_BODY2) | +| `UPDOW_GRID_BODY3` | `text` | NULL | NULL | UPDOWN: GRID BODY3 (UPDOW_GRID_BODY3) | +| `UPDOW_GRID_TP0` | `text` | NULL | NULL | UPDOWN: GRID TP0 (UPDOW_GRID_TP0) | +| `UPDOW_GRID_TP1` | `text` | NULL | NULL | UPDOWN: GRID TP1 (UPDOW_GRID_TP1) | +| `UPDOW_GRID_TP2` | `text` | NULL | NULL | UPDOWN: GRID TP2 (UPDOW_GRID_TP2) | +| `UPDOW_GRID_TP3` | `text` | NULL | NULL | UPDOWN: GRID TP3 (UPDOW_GRID_TP3) | +| `UPDOW_GRID_TP4` | `text` | NULL | NULL | UPDOWN: GRID TP4 (UPDOW_GRID_TP4) | +| `UPDOW_GRID_SL0` | `text` | NULL | NULL | UPDOWN: GRID SL0 (UPDOW_GRID_SL0) | +| `UPDOW_GRID_SL1` | `text` | NULL | NULL | UPDOWN: GRID SL1 (UPDOW_GRID_SL1) | +| `UPDOW_GRID_SL2` | `text` | NULL | NULL | UPDOWN: GRID SL2 (UPDOW_GRID_SL2) | +| `UPDOW_GRID_SL3` | `text` | NULL | NULL | UPDOWN: GRID SL3 (UPDOW_GRID_SL3) | +| `UPDOW_GRID_HOLD0` | `text` | NULL | NULL | UPDOWN: GRID HOLD0 (UPDOW_GRID_HOLD0) | +| `UPDOW_GRID_HOLD1` | `text` | NULL | NULL | UPDOWN: GRID HOLD1 (UPDOW_GRID_HOLD1) | +| `UPDOW_GRID_HOLD2` | `text` | NULL | NULL | UPDOWN: GRID HOLD2 (UPDOW_GRID_HOLD2) | +| `UPDOW_GRID_HOLD3` | `text` | NULL | NULL | UPDOWN: GRID HOLD3 (UPDOW_GRID_HOLD3) | +| `UPDOW_GRID_HOLD4` | `text` | NULL | NULL | UPDOWN: GRID HOLD4 (UPDOW_GRID_HOLD4) | +| `UPDOW_GRID_REGIME0` | `text` | NULL | NULL | UPDOWN: GRID REGIME0 (UPDOW_GRID_REGIME0) | +| `UPDOW_GRID_REGIME1` | `text` | NULL | NULL | UPDOWN: GRID REGIME1 (UPDOW_GRID_REGIME1) | +| `UPDOW_REGIME_MA_BARS` | `text` | NULL | NULL | UPDOWN: REGIME MA 봉수 (UPDOW_REGIME_MA_BARS) | +| `UPDOW_REGIME_MA_EASE_PCT` | `text` | NULL | NULL | UPDOWN: REGIME MA EASE % (UPDOW_REGIME_MA_EASE_PCT) | +| `UPDOW_REGIME_MA_EASE_CAP` | `text` | NULL | NULL | UPDOWN: REGIME MA EASE CAP (UPDOW_REGIME_MA_EASE_CAP) | +| `UPDOW_GRID_REGIME_EASE0` | `text` | NULL | NULL | UPDOWN: GRID REGIME EASE0 (UPDOW_GRID_REGIME_EASE0) | +| `UPDOW_GRID_REGIME_EASE1` | `text` | NULL | NULL | UPDOWN: GRID REGIME EASE1 (UPDOW_GRID_REGIME_EASE1) | +| `UPDOW_KOSPI_1MIN_PROXY_CODE` | `text` | NULL | NULL | UPDOWN: KOSPI 1MIN PROXY CODE (UPDOW_KOSPI_1MIN_PROXY_CODE) | +| `UPDOW_REGIME_PREFER_WS_CANDLES` | `text` | NULL | NULL | UPDOWN: REGIME PREFER WS CANDLES (UPDOW_REGIME_PREFER_WS_CANDLES) | +| `UPDOW_REGIME_WS_CANDLE_MIN` | `text` | NULL | NULL | UPDOWN: REGIME WS CANDLE 최소 (UPDOW_REGIME_WS_CANDLE_MIN) | +| `UPDOW_SHOULDER_MIN_HIGH_PCT` | `text` | NULL | NULL | UPDOWN: 어깨무장고점 % (UPDOW_SHOULDER_MIN_HIGH_PCT) | +| `UPDOW_SHOULDER_CUT_PCT` | `text` | NULL | NULL | UPDOWN: 어깨컷 % (UPDOW_SHOULDER_CUT_PCT) | +| `UPDOW_GRID_SHOULDER_SMIN0` | `text` | NULL | NULL | UPDOWN: GRID 어깨 SMIN0 (UPDOW_GRID_SHOULDER_SMIN0) | +| `UPDOW_GRID_SHOULDER_SMIN1` | `text` | NULL | NULL | UPDOWN: GRID 어깨 SMIN1 (UPDOW_GRID_SHOULDER_SMIN1) | +| `UPDOW_GRID_SHOULDER_SMIN2` | `text` | NULL | NULL | UPDOWN: GRID 어깨 SMIN2 (UPDOW_GRID_SHOULDER_SMIN2) | +| `UPDOW_GRID_SHOULDER_CUT0` | `text` | NULL | NULL | UPDOWN: GRID 어깨컷 0 (UPDOW_GRID_SHOULDER_CUT0) | +| `UPDOW_GRID_SHOULDER_CUT1` | `text` | NULL | NULL | UPDOWN: GRID 어깨컷 1 (UPDOW_GRID_SHOULDER_CUT1) | +| `UPDOW_GRID_SHOULDER_CUT2` | `text` | NULL | NULL | UPDOWN: GRID 어깨컷 2 (UPDOW_GRID_SHOULDER_CUT2) | +| `UPDOW_TOTAL_BUDGET_KRW` | `text` | NULL | NULL | UPDOWN 박스 총 한도(원) | +| `STRATEGY_UPDOW_ENABLED` | `text` | NULL | NULL | UPDOWN박스 ON/OFF. 다음루프 신규매수중단(보유청산유지). WS구독해제 안함. 기동시OFF→켜려면재시작 | +| `KIS_UPDOW_MM_CHANNEL` | `text` | NULL | NULL | UPDOWN: KIS UPDOW MM CHANNEL (KIS_UPDOW_MM_CHANNEL) | +| `UPDOW_DAILY_PROFIT_TARGET_ENABLED` | `text` | NULL | NULL | UPDOWN 박스 손익 감시 켜기 · OFF = 해당 전략 일일익절 비활성 (매수 차단 없음) | +| `UPDOW_DAILY_PROFIT_TARGET_KRW` | `text` | NULL | NULL | UPDOWN 박스 목표(원) | +| `UPDOW_DAILY_PROFIT_TARGET_PCT` | `text` | NULL | NULL | UPDOWN 박스 목표(%) · 퍼센트 그대로 (예: 2.5 = 2.5%) · 0=미사용 | +| `UPDOW_DAILY_PROFIT_HALT_NEW_BUYS` | `text` | NULL | NULL | UPDOWN 박스 신규매수 수동 중단 · 목표 무관 수동 잠금 · 비우면 마스터 상속 · OFF=정상 | +| `UPDOW_DAILY_PROFIT_MODE` | `text` | NULL | NULL | UPDOWN 박스 익절방식 · 비우면 마스터 상속 · fixed/trailing/both | +| `UPDOW_DAILY_PROFIT_TRAIL_TIERS` | `text` | NULL | NULL | UPDOWN 박스 다단 트레일 · 수익원:컷%,… · 비우면 마스터 상속 · 예: 30000:50,100000:35,300000:25 | +| `UPDOW_DAILY_PROFIT_TRAIL_DROP_PCT` | `text` | NULL | NULL | UPDOWN 박스 단일 트레일 되돌림(%) · 다단 비었을 때만 · 고점 대비 % · 비우면 마스터 상속 · 0=미사용 | +| `UPDOW_DAILY_PROFIT_TRAIL_ARM_KRW` | `text` | NULL | NULL | UPDOWN 박스 트레일 발동수익(원) · 이 이상 벌어야 트레일 감시 · 비우면 마스터 상속 | +| `UPDOW_DAILY_PROFIT_TRAIL_ARM_PCT` | `text` | NULL | NULL | UPDOWN: 일일익절 트레일무장 % (UPDOW_DAILY_PROFIT_TRAIL_ARM_PCT) | +| `UPDOW_DAILY_PROFIT_RISK_BUDGET_ENABLED` | `text` | NULL | NULL | UPDOWN 박스 리스크버짓(B) · ON=일일익절 hit 후 보유최악손절합>cushion 이면 전량정리 · 기본 OFF · 마스터 없음 | +| `UPDOW_DAILY_PROFIT_RISK_BUDGET_COOLDOWN_SEC` | `text` | NULL | NULL | UPDOWN 박스 리스크버짓 쿨다운(초) · 같은 날 재평가 최소 간격 · 0=당일 1회만 | +| `UPDOW_TICK_SELL_ENABLED` | `text` | NULL | NULL | UPDOWN 박스 틱매도 · 비우면 공통 TICK_SELL_ENABLED · true/false 명시 시 전략만 덮어씀 · 기본 false | + +
+DDL 원문 + +```sql +CREATE TABLE `config_updow` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `created_at` varchar(30) NOT NULL, + `UPDOW_UNIVERSE_SOURCE` text DEFAULT NULL, + `RANK_UPDOW_SORT` text DEFAULT NULL, + `RANK_UPDOW_LIMIT` text DEFAULT NULL, + `CONDITION_UPDOW_NAME` text DEFAULT NULL, + `CONDITION_UPDOW_SEQ` text DEFAULT NULL, + `UPDOW_CAND_LIMIT` text DEFAULT NULL, + `UPDOW_TIME_START` text DEFAULT NULL, + `UPDOW_TIME_END` text DEFAULT NULL, + `UPDOW_REENTRY_COOLDOWN_SEC` text DEFAULT NULL, + `UPDOW_MIN_CANDLE_LEN` text DEFAULT NULL, + `MIN_CANDLE_LEN_UPDOW` text DEFAULT NULL, + `UPDOW_CANDLE_FETCH_N` text DEFAULT NULL, + `UPDOW_LIVE_MIN_CANDLES` text DEFAULT NULL, + `UPDOW_MAX_LOSS_PER_TRADE_KRW` text DEFAULT NULL, + `UPDOW_MAX_BUY_AMOUNT` text DEFAULT NULL, + `UPDOW_MAX_STOCKS` text DEFAULT NULL, + `UPDOW_BODY_DROP_MIN_PCT` text DEFAULT NULL, + `UPDOW_TP_PCT` text DEFAULT NULL, + `UPDOW_STOP_LOSS_PCT` text DEFAULT NULL, + `UPDOW_ATR_USE_DYNAMIC` text DEFAULT NULL, + `UPDOW_ATR_PERIOD` text DEFAULT NULL, + `UPDOW_ATR_SL_MULT` text DEFAULT NULL, + `UPDOW_ATR_TP_MULT` text DEFAULT NULL, + `UPDOW_ATR_SL_MIN_PCT` text DEFAULT NULL, + `UPDOW_ATR_SL_MAX_PCT` text DEFAULT NULL, + `UPDOW_ATR_TP_MIN_PCT` text DEFAULT NULL, + `UPDOW_ATR_TP_MAX_PCT` text DEFAULT NULL, + `UPDOW_ATR_MULT_FLOOR` text DEFAULT NULL, + `UPDOW_ATR_PCT_FLOOR` text DEFAULT NULL, + `UPDOW_EXIT_PCT_FLOOR` text DEFAULT NULL, + `UPDOW_MAX_HOLD_BARS` text DEFAULT NULL, + `UPDOW_EXIT_ON_GREEN` text DEFAULT NULL, + `UPDOW_SLOT_MONEY` text DEFAULT NULL, + `UPDOW_TF_MIN` text DEFAULT NULL, + `UPDOW_GRID_BODY0` text DEFAULT NULL, + `UPDOW_GRID_BODY1` text DEFAULT NULL, + `UPDOW_GRID_BODY2` text DEFAULT NULL, + `UPDOW_GRID_BODY3` text DEFAULT NULL, + `UPDOW_GRID_TP0` text DEFAULT NULL, + `UPDOW_GRID_TP1` text DEFAULT NULL, + `UPDOW_GRID_TP2` text DEFAULT NULL, + `UPDOW_GRID_TP3` text DEFAULT NULL, + `UPDOW_GRID_TP4` text DEFAULT NULL, + `UPDOW_GRID_SL0` text DEFAULT NULL, + `UPDOW_GRID_SL1` text DEFAULT NULL, + `UPDOW_GRID_SL2` text DEFAULT NULL, + `UPDOW_GRID_SL3` text DEFAULT NULL, + `UPDOW_GRID_HOLD0` text DEFAULT NULL, + `UPDOW_GRID_HOLD1` text DEFAULT NULL, + `UPDOW_GRID_HOLD2` text DEFAULT NULL, + `UPDOW_GRID_HOLD3` text DEFAULT NULL, + `UPDOW_GRID_HOLD4` text DEFAULT NULL, + `UPDOW_GRID_REGIME0` text DEFAULT NULL, + `UPDOW_GRID_REGIME1` text DEFAULT NULL, + `UPDOW_REGIME_MA_BARS` text DEFAULT NULL, + `UPDOW_REGIME_MA_EASE_PCT` text DEFAULT NULL, + `UPDOW_REGIME_MA_EASE_CAP` text DEFAULT NULL, + `UPDOW_GRID_REGIME_EASE0` text DEFAULT NULL, + `UPDOW_GRID_REGIME_EASE1` text DEFAULT NULL, + `UPDOW_KOSPI_1MIN_PROXY_CODE` text DEFAULT NULL, + `UPDOW_REGIME_PREFER_WS_CANDLES` text DEFAULT NULL, + `UPDOW_REGIME_WS_CANDLE_MIN` text DEFAULT NULL, + `UPDOW_SHOULDER_MIN_HIGH_PCT` text DEFAULT NULL, + `UPDOW_SHOULDER_CUT_PCT` text DEFAULT NULL, + `UPDOW_GRID_SHOULDER_SMIN0` text DEFAULT NULL, + `UPDOW_GRID_SHOULDER_SMIN1` text DEFAULT NULL, + `UPDOW_GRID_SHOULDER_SMIN2` text DEFAULT NULL, + `UPDOW_GRID_SHOULDER_CUT0` text DEFAULT NULL, + `UPDOW_GRID_SHOULDER_CUT1` text DEFAULT NULL, + `UPDOW_GRID_SHOULDER_CUT2` text DEFAULT NULL, + `UPDOW_TOTAL_BUDGET_KRW` text DEFAULT NULL, + `STRATEGY_UPDOW_ENABLED` text DEFAULT NULL, + `KIS_UPDOW_MM_CHANNEL` text DEFAULT NULL, + `UPDOW_DAILY_PROFIT_TARGET_ENABLED` text DEFAULT NULL, + `UPDOW_DAILY_PROFIT_TARGET_KRW` text DEFAULT NULL, + `UPDOW_DAILY_PROFIT_TARGET_PCT` text DEFAULT NULL, + `UPDOW_DAILY_PROFIT_HALT_NEW_BUYS` text DEFAULT NULL, + `UPDOW_DAILY_PROFIT_MODE` text DEFAULT NULL, + `UPDOW_DAILY_PROFIT_TRAIL_TIERS` text DEFAULT NULL, + `UPDOW_DAILY_PROFIT_TRAIL_DROP_PCT` text DEFAULT NULL, + `UPDOW_DAILY_PROFIT_TRAIL_ARM_KRW` text DEFAULT NULL, + `UPDOW_DAILY_PROFIT_TRAIL_ARM_PCT` text DEFAULT NULL, + `UPDOW_DAILY_PROFIT_RISK_BUDGET_ENABLED` text DEFAULT NULL, + `UPDOW_DAILY_PROFIT_RISK_BUDGET_COOLDOWN_SEC` text DEFAULT NULL, + `UPDOW_TICK_SELL_ENABLED` text DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=388 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `config_us_momentum` + +**PK**: `id` | AUTO_INCREMENT=96 + +> **테이블 COMMENT**: 해외모멘텀 `US_MOMENTUM_*` · `STRATEGY_US_MOMENTUM_ENABLED`. 시세=한투 HDFSCNT0 · 유니버스=permanent US + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `int(11)` | NOT NULL | | PK · AUTO_INCREMENT | +| `created_at` | `varchar(30)` | NOT NULL | | 설정행 생성시각 | +| `STRATEGY_US_MOMENTUM_ENABLED` | `text` | NULL | NULL | 해외모멘텀 ON/OFF. 다음루프 신규매수중단(보유청산유지). WS구독해제 안함. 기동시OFF→켜려면재시작 | +| `US_MOMENTUM_TIME_START` | `text` | NULL | NULL | 해외모멘텀: 매수시작 (US_MOMENTUM_TIME_START) | +| `US_MOMENTUM_TIME_END` | `text` | NULL | NULL | 해외모멘텀: 매수종료 (US_MOMENTUM_TIME_END) | +| `US_MOMENTUM_SELL_TIME_END` | `text` | NULL | NULL | 해외모멘텀: 매도세션종료 (US_MOMENTUM_SELL_TIME_END) | +| `US_MOMENTUM_DRY_RUN` | `text` | NULL | NULL | 해외모멘텀: 모의/페이퍼전용 (US_MOMENTUM_DRY_RUN) | +| `US_MOMENTUM_MAX_STOCKS` | `text` | NULL | NULL | 해외모멘텀: 동시보유상한 (US_MOMENTUM_MAX_STOCKS) | +| `US_MOMENTUM_SLOT_MONEY` | `text` | NULL | NULL | 해외모멘텀: 슬롯금액 (US_MOMENTUM_SLOT_MONEY) | +| `US_MOMENTUM_FEE_RATE` | `text` | NULL | NULL | 해외모멘텀: 수수료율 (US_MOMENTUM_FEE_RATE) | +| `US_MOMENTUM_SELL_TAX` | `text` | NULL | NULL | 해외모멘텀: 매도세금 (US_MOMENTUM_SELL_TAX) | +| `US_MOMENTUM_MIN_PRICE` | `text` | NULL | NULL | 해외모멘텀: 최소가격 (US_MOMENTUM_MIN_PRICE) | +| `US_MOMENTUM_LIVE_MIN_CANDLES` | `text` | NULL | NULL | 해외모멘텀: 실매최소봉수 (US_MOMENTUM_LIVE_MIN_CANDLES) | +| `US_MOMENTUM_EOD_ENABLED` | `text` | NULL | NULL | 해외모멘텀: 장마감청산ON (US_MOMENTUM_EOD_ENABLED) | +| `US_MOMENTUM_EOD_HM` | `text` | NULL | NULL | 해외모멘텀: 장마감시각HHMM (US_MOMENTUM_EOD_HM) | +| `US_MOMENTUM_MAX_BUY_AMOUNT` | `text` | NULL | NULL | 해외모멘텀: 1회최대매수금액 (US_MOMENTUM_MAX_BUY_AMOUNT) | +| `US_MOMENTUM_TOTAL_BUDGET` | `text` | NULL | NULL | 해외모멘텀: 총운용한도 (US_MOMENTUM_TOTAL_BUDGET) | +| `US_MOMENTUM_RSI_MIN` | `text` | NULL | NULL | 해외모멘텀: RSI하한 (US_MOMENTUM_RSI_MIN) | +| `US_MOMENTUM_RSI_MAX` | `text` | NULL | NULL | 해외모멘텀: RSI상한 (US_MOMENTUM_RSI_MAX) | +| `US_MOMENTUM_VOL_MULT` | `text` | NULL | NULL | 해외모멘텀: 거래량배수 (US_MOMENTUM_VOL_MULT) | +| `US_MOMENTUM_VOL_WIN` | `text` | NULL | NULL | 해외모멘텀: 거래량윈도우 (US_MOMENTUM_VOL_WIN) | +| `US_MOMENTUM_MAX_FROM_OPEN_PCT` | `text` | NULL | NULL | 해외모멘텀: 최대 시가대비 % (US_MOMENTUM_MAX_FROM_OPEN_PCT) | +| `US_MOMENTUM_MIN_FROM_OPEN_PCT` | `text` | NULL | NULL | 해외모멘텀: 최소 시가대비 % (US_MOMENTUM_MIN_FROM_OPEN_PCT) | +| `US_MOMENTUM_STOP_LOSS_PCT` | `text` | NULL | NULL | 해외모멘텀: 손절 % (US_MOMENTUM_STOP_LOSS_PCT) | +| `US_MOMENTUM_TAKE_PROFIT_PCT` | `text` | NULL | NULL | 해외모멘텀: 익절 % (US_MOMENTUM_TAKE_PROFIT_PCT) | +| `US_MOMENTUM_TP_MAX_PCT` | `text` | NULL | NULL | 해외모멘텀: TP 최대 % (US_MOMENTUM_TP_MAX_PCT) | +| `US_MOMENTUM_SHOULDER_MIN_HIGH_PCT` | `text` | NULL | NULL | 해외모멘텀: 어깨무장고점 % (US_MOMENTUM_SHOULDER_MIN_HIGH_PCT) | +| `US_MOMENTUM_SHOULDER_CUT_PCT` | `text` | NULL | NULL | 해외모멘텀: 어깨컷 % (US_MOMENTUM_SHOULDER_CUT_PCT) | +| `US_MOMENTUM_RATCHET_TIERS` | `text` | NULL | NULL | 해외모멘텀: 다단래칫 (US_MOMENTUM_RATCHET_TIERS) | +| `US_MOMENTUM_TRAIL_PCT` | `text` | NULL | NULL | 해외모멘텀: 트레일 % (US_MOMENTUM_TRAIL_PCT) | +| `US_MOMENTUM_TRAIL_ARM_PCT` | `text` | NULL | NULL | 해외모멘텀: 트레일무장 % (US_MOMENTUM_TRAIL_ARM_PCT) | +| `US_MOMENTUM_MAX_HOLD_BARS` | `text` | NULL | NULL | 해외모멘텀: 최대보유봉 (US_MOMENTUM_MAX_HOLD_BARS) | +| `US_MOMENTUM_MAX_DAILY` | `text` | NULL | NULL | 해외모멘텀: 일일매수횟수상한 (US_MOMENTUM_MAX_DAILY) | +| `US_MOMENTUM_MAX_DAILY_CHG` | `text` | NULL | NULL | 해외모멘텀: 당일등락상한 (US_MOMENTUM_MAX_DAILY_CHG) | +| `US_MOMENTUM_HIGH_CHASE_THR` | `text` | NULL | NULL | 해외모멘텀: 고점추격 THR (US_MOMENTUM_HIGH_CHASE_THR) | +| `US_MOMENTUM_USE_DEFENSE_FILTERS` | `text` | NULL | NULL | 해외모멘텀: 사용_ 방어필터 필터 S (US_MOMENTUM_USE_DEFENSE_FILTERS) | +| `US_MOMENTUM_USE_HIGH_CHASE_FILTER` | `text` | NULL | NULL | 해외모멘텀: 사용_ 고점추격 필터 (US_MOMENTUM_USE_HIGH_CHASE_FILTER) | +| `US_MOMENTUM_USE_DAILY_RANGE_FILTER` | `text` | NULL | NULL | 해외모멘텀: 사용_ DAILY RANGE 필터 (US_MOMENTUM_USE_DAILY_RANGE_FILTER) | +| `US_MOMENTUM_USE_EMA_FILTER` | `text` | NULL | NULL | 해외모멘텀: 사용_ EMA 필터 (US_MOMENTUM_USE_EMA_FILTER) | +| `US_MOMENTUM_EMA_FAST_PERIOD` | `text` | NULL | NULL | 해외모멘텀: EMA단기 기간 (US_MOMENTUM_EMA_FAST_PERIOD) | +| `US_MOMENTUM_EMA_SLOW_PERIOD` | `text` | NULL | NULL | 해외모멘텀: EMA장기 기간 (US_MOMENTUM_EMA_SLOW_PERIOD) | +| `US_MOMENTUM_USE_RSI_MAX_FILTER` | `text` | NULL | NULL | 해외모멘텀: 사용_ RSI상한 필터 (US_MOMENTUM_USE_RSI_MAX_FILTER) | +| `US_MOMENTUM_PATTERN_BREAKOUT` | `text` | NULL | NULL | 해외모멘텀: 패턴 돌파 (US_MOMENTUM_PATTERN_BREAKOUT) | +| `US_MOMENTUM_PATTERN_PULLBACK` | `text` | NULL | NULL | 해외모멘텀: 패턴 되돌림 (US_MOMENTUM_PATTERN_PULLBACK) | +| `US_MOMENTUM_CHASE_LOOKBACK_MIN` | `text` | NULL | NULL | 해외모멘텀: CHASE LOOKBACK 최소 (US_MOMENTUM_CHASE_LOOKBACK_MIN) | +| `US_MOMENTUM_PULLBACK_LOOKBACK_MIN` | `text` | NULL | NULL | 해외모멘텀: 되돌림 LOOKBACK 최소 (US_MOMENTUM_PULLBACK_LOOKBACK_MIN) | +| `US_MOMENTUM_PULLBACK_MIN_PCT` | `text` | NULL | NULL | 해외모멘텀: 되돌림 최소 % (US_MOMENTUM_PULLBACK_MIN_PCT) | +| `US_MOMENTUM_PULLBACK_MAX_PCT` | `text` | NULL | NULL | 해외모멘텀: 되돌림 최대 % (US_MOMENTUM_PULLBACK_MAX_PCT) | +| `US_MOMENTUM_SETUP_VOL_MAX_MULT` | `text` | NULL | NULL | 해외모멘텀: SETUP 거래량 최대 배수 (US_MOMENTUM_SETUP_VOL_MAX_MULT) | +| `US_MOMENTUM_SETUP_BEAR_BARS_MIN` | `text` | NULL | NULL | 해외모멘텀: SETUP BEAR 봉수 최소 (US_MOMENTUM_SETUP_BEAR_BARS_MIN) | +| `US_MOMENTUM_MAX_LOSS_PER_TRADE` | `text` | NULL | NULL | 해외모멘텀: 최대 LOSS PER TRADE (US_MOMENTUM_MAX_LOSS_PER_TRADE) | +| `US_MOMENTUM_MIN_PROFIT_PCT` | `text` | NULL | NULL | 해외모멘텀: 최소 PROFIT % (US_MOMENTUM_MIN_PROFIT_PCT) | +| `US_MOMENTUM_COOLDOWN_SEC` | `text` | NULL | NULL | 해외모멘텀: 쿨다운 초 (US_MOMENTUM_COOLDOWN_SEC) | +| `US_MOMENTUM_DAILY_PROFIT_TARGET_ENABLED` | `text` | NULL | NULL | 해외모멘텀: 일일익절 목표 ON/OFF (US_MOMENTUM_DAILY_PROFIT_TARGET_ENABLED) | +| `US_MOMENTUM_DAILY_PROFIT_TRAIL_TIERS` | `text` | NULL | NULL | 해외모멘텀: 일일익절 트레일 다단 (US_MOMENTUM_DAILY_PROFIT_TRAIL_TIERS) | +| `US_MOMENTUM_DAILY_PROFIT_MODE` | `text` | NULL | NULL | 해외모멘텀: 일일익절 모드 (US_MOMENTUM_DAILY_PROFIT_MODE) | +| `US_MOMENTUM_MAX_ORDER_QTY` | `text` | NULL | NULL | 해외모멘텀: 주문수량상한 (US_MOMENTUM_MAX_ORDER_QTY) | +| `US_MOMENTUM_ORDER_FAIL_COOLDOWN_SEC` | `text` | NULL | NULL | 해외모멘텀: ORDER FAIL 쿨다운 초 (US_MOMENTUM_ORDER_FAIL_COOLDOWN_SEC) | +| `US_MOMENTUM_RELOAD_INTERVAL_SEC` | `text` | NULL | NULL | 해외모멘텀: 설정리로드주기 초 (US_MOMENTUM_RELOAD_INTERVAL_SEC) | +| `US_MOMENTUM_PSAMOUNT_CACHE_SEC` | `text` | NULL | NULL | 해외모멘텀: 매수가능수량 CACHE 초 (US_MOMENTUM_PSAMOUNT_CACHE_SEC) | +| `US_MOMENTUM_LOOP_SLEEP_MIN` | `text` | NULL | NULL | 해외모멘텀: 루프대기 최소 (US_MOMENTUM_LOOP_SLEEP_MIN) | +| `US_MOMENTUM_LOOP_SLEEP_MAX` | `text` | NULL | NULL | 해외모멘텀: 루프대기 최대 (US_MOMENTUM_LOOP_SLEEP_MAX) | +| `US_MOMENTUM_SCAN_REJECT_SLEEP_MIN` | `text` | NULL | NULL | 해외모멘텀: 스캔탈락대기 SLEEP 최소 (US_MOMENTUM_SCAN_REJECT_SLEEP_MIN) | +| `US_MOMENTUM_SCAN_REJECT_SLEEP_MAX` | `text` | NULL | NULL | 해외모멘텀: 스캔탈락대기 SLEEP 최대 (US_MOMENTUM_SCAN_REJECT_SLEEP_MAX) | +| `US_MOMENTUM_PROFILE_BUY_SEC` | `text` | NULL | NULL | 해외모멘텀: PROFILE BUY 초 (US_MOMENTUM_PROFILE_BUY_SEC) | +| `US_MOMENTUM_TRADES_CACHE_SEC` | `text` | NULL | NULL | 해외모멘텀: TRADES CACHE 초 (US_MOMENTUM_TRADES_CACHE_SEC) | +| `US_MOMENTUM_CANDLE_LOCK_WAIT_SEC` | `text` | NULL | NULL | 해외모멘텀: CANDLE LOCK WAIT 초 (US_MOMENTUM_CANDLE_LOCK_WAIT_SEC) | +| `US_MOMENTUM_DAILY_PROFIT_RISK_BUDGET_ENABLED` | `text` | NULL | NULL | 해외모멘텀: 일일익절 RISK 한도 ON/OFF (US_MOMENTUM_DAILY_PROFIT_RISK_BUDGET_ENABLED) | +| `US_MOMENTUM_DAILY_PROFIT_RISK_BUDGET_COOLDOWN_SEC` | `text` | NULL | NULL | 해외모멘텀: 일일익절 RISK 한도 쿨다운 초 (US_MOMENTUM_DAILY_PROFIT_RISK_BUDGET_COOLDOWN_SEC) | +| `US_MOMENTUM_FORCE_BUY_QTY` | `text` | NULL | NULL | 해외모멘텀: 강제매수테스트 QTY (US_MOMENTUM_FORCE_BUY_QTY) | +| `US_MOMENTUM_SESSION_START` | `text` | NULL | NULL | 해외모멘텀: 세션시작 (US_MOMENTUM_SESSION_START) | +| `US_MOMENTUM_BUY_WAIT_LOG_SEC` | `text` | NULL | NULL | 해외모멘텀: 매수대기로그 초 (US_MOMENTUM_BUY_WAIT_LOG_SEC) | +| `US_MOMENTUM_FX_FEE_RATE` | `text` | NULL | NULL | 해외모멘텀: 환전수수료 RATE (US_MOMENTUM_FX_FEE_RATE) | + +
+DDL 원문 + +```sql +CREATE TABLE `config_us_momentum` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `created_at` varchar(30) NOT NULL, + `STRATEGY_US_MOMENTUM_ENABLED` text DEFAULT NULL COMMENT '해외모멘텀 ON/OFF. 다음루프 신규매수중단(보유청산유지). WS구독해제 안함. 기동시OFF→켜려면재시작', + `US_MOMENTUM_TIME_START` text DEFAULT NULL, + `US_MOMENTUM_TIME_END` text DEFAULT NULL, + `US_MOMENTUM_SELL_TIME_END` text DEFAULT NULL, + `US_MOMENTUM_DRY_RUN` text DEFAULT NULL, + `US_MOMENTUM_MAX_STOCKS` text DEFAULT NULL, + `US_MOMENTUM_SLOT_MONEY` text DEFAULT NULL, + `US_MOMENTUM_FEE_RATE` text DEFAULT NULL, + `US_MOMENTUM_SELL_TAX` text DEFAULT NULL, + `US_MOMENTUM_MIN_PRICE` text DEFAULT NULL, + `US_MOMENTUM_LIVE_MIN_CANDLES` text DEFAULT NULL, + `US_MOMENTUM_EOD_ENABLED` text DEFAULT NULL, + `US_MOMENTUM_EOD_HM` text DEFAULT NULL, + `US_MOMENTUM_MAX_BUY_AMOUNT` text DEFAULT NULL, + `US_MOMENTUM_TOTAL_BUDGET` text DEFAULT NULL, + `US_MOMENTUM_RSI_MIN` text DEFAULT NULL, + `US_MOMENTUM_RSI_MAX` text DEFAULT NULL, + `US_MOMENTUM_VOL_MULT` text DEFAULT NULL, + `US_MOMENTUM_VOL_WIN` text DEFAULT NULL, + `US_MOMENTUM_MAX_FROM_OPEN_PCT` text DEFAULT NULL, + `US_MOMENTUM_MIN_FROM_OPEN_PCT` text DEFAULT NULL, + `US_MOMENTUM_STOP_LOSS_PCT` text DEFAULT NULL, + `US_MOMENTUM_TAKE_PROFIT_PCT` text DEFAULT NULL, + `US_MOMENTUM_TP_MAX_PCT` text DEFAULT NULL, + `US_MOMENTUM_SHOULDER_MIN_HIGH_PCT` text DEFAULT NULL, + `US_MOMENTUM_SHOULDER_CUT_PCT` text DEFAULT NULL, + `US_MOMENTUM_RATCHET_TIERS` text DEFAULT NULL, + `US_MOMENTUM_TRAIL_PCT` text DEFAULT NULL, + `US_MOMENTUM_TRAIL_ARM_PCT` text DEFAULT NULL, + `US_MOMENTUM_MAX_HOLD_BARS` text DEFAULT NULL, + `US_MOMENTUM_MAX_DAILY` text DEFAULT NULL, + `US_MOMENTUM_MAX_DAILY_CHG` text DEFAULT NULL, + `US_MOMENTUM_HIGH_CHASE_THR` text DEFAULT NULL, + `US_MOMENTUM_USE_DEFENSE_FILTERS` text DEFAULT NULL, + `US_MOMENTUM_USE_HIGH_CHASE_FILTER` text DEFAULT NULL, + `US_MOMENTUM_USE_DAILY_RANGE_FILTER` text DEFAULT NULL, + `US_MOMENTUM_USE_EMA_FILTER` text DEFAULT NULL, + `US_MOMENTUM_EMA_FAST_PERIOD` text DEFAULT NULL, + `US_MOMENTUM_EMA_SLOW_PERIOD` text DEFAULT NULL, + `US_MOMENTUM_USE_RSI_MAX_FILTER` text DEFAULT NULL, + `US_MOMENTUM_PATTERN_BREAKOUT` text DEFAULT NULL, + `US_MOMENTUM_PATTERN_PULLBACK` text DEFAULT NULL, + `US_MOMENTUM_CHASE_LOOKBACK_MIN` text DEFAULT NULL, + `US_MOMENTUM_PULLBACK_LOOKBACK_MIN` text DEFAULT NULL, + `US_MOMENTUM_PULLBACK_MIN_PCT` text DEFAULT NULL, + `US_MOMENTUM_PULLBACK_MAX_PCT` text DEFAULT NULL, + `US_MOMENTUM_SETUP_VOL_MAX_MULT` text DEFAULT NULL, + `US_MOMENTUM_SETUP_BEAR_BARS_MIN` text DEFAULT NULL, + `US_MOMENTUM_MAX_LOSS_PER_TRADE` text DEFAULT NULL, + `US_MOMENTUM_MIN_PROFIT_PCT` text DEFAULT NULL, + `US_MOMENTUM_COOLDOWN_SEC` text DEFAULT NULL, + `US_MOMENTUM_DAILY_PROFIT_TARGET_ENABLED` text DEFAULT NULL, + `US_MOMENTUM_DAILY_PROFIT_TRAIL_TIERS` text DEFAULT NULL, + `US_MOMENTUM_DAILY_PROFIT_MODE` text DEFAULT NULL, + `US_MOMENTUM_MAX_ORDER_QTY` text DEFAULT NULL, + `US_MOMENTUM_ORDER_FAIL_COOLDOWN_SEC` text DEFAULT NULL, + `US_MOMENTUM_RELOAD_INTERVAL_SEC` text DEFAULT NULL, + `US_MOMENTUM_PSAMOUNT_CACHE_SEC` text DEFAULT NULL, + `US_MOMENTUM_LOOP_SLEEP_MIN` text DEFAULT NULL, + `US_MOMENTUM_LOOP_SLEEP_MAX` text DEFAULT NULL, + `US_MOMENTUM_SCAN_REJECT_SLEEP_MIN` text DEFAULT NULL, + `US_MOMENTUM_SCAN_REJECT_SLEEP_MAX` text DEFAULT NULL, + `US_MOMENTUM_PROFILE_BUY_SEC` text DEFAULT NULL, + `US_MOMENTUM_TRADES_CACHE_SEC` text DEFAULT NULL, + `US_MOMENTUM_CANDLE_LOCK_WAIT_SEC` text DEFAULT NULL, + `US_MOMENTUM_DAILY_PROFIT_RISK_BUDGET_ENABLED` text DEFAULT NULL, + `US_MOMENTUM_DAILY_PROFIT_RISK_BUDGET_COOLDOWN_SEC` text DEFAULT NULL, + `US_MOMENTUM_FORCE_BUY_QTY` text DEFAULT NULL, + `US_MOMENTUM_SESSION_START` text DEFAULT NULL, + `US_MOMENTUM_BUY_WAIT_LOG_SEC` text DEFAULT NULL, + `US_MOMENTUM_FX_FEE_RATE` text DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=96 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +### 🌍 환경 설정 (Env Config) + +#### `env_config` + +**PK**: `id` | AUTO_INCREMENT=1060 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `int(11)` | NOT NULL | | PK · AUTO_INCREMENT | +| `created_at` | `varchar(30)` | NOT NULL | | 설정행 생성시각 | +| `STOP_LOSS_PCT` | `text` | NULL | NULL | 공통env: 손절 % (STOP_LOSS_PCT) | +| `MAX_POSITION_PCT` | `text` | NULL | NULL | 공통env: 최대 POSITION % (MAX_POSITION_PCT) | +| `USE_SLOT_CAP` | `text` | NULL | NULL | 공통env: 사용_ SLOT CAP (USE_SLOT_CAP) | +| `SLOT_CAP_PCT` | `text` | NULL | NULL | 공통env: SLOT CAP % (SLOT_CAP_PCT) | +| `MAX_STOCKS` | `text` | NULL | NULL | 공통env: 동시보유상한 (MAX_STOCKS) | +| `USE_KELLY` | `text` | NULL | NULL | 공통env: 사용_ KELLY (USE_KELLY) | +| `RISK_PCT_PER_TRADE` | `text` | NULL | NULL | 공통env: RISK % PER TRADE (RISK_PCT_PER_TRADE) | +| `MIN_POSITION_AMOUNT` | `text` | NULL | NULL | 공통env: 최소 POSITION AMOUNT (MIN_POSITION_AMOUNT) | +| `USE_RISK_CHECK` | `text` | NULL | NULL | 리스크 체크 사용 | +| `DAILY_STOP_LOSS_PCT` | `text` | NULL | NULL | 일일 손실 한도(분율, -0.05=-5%) | +| `CONSECUTIVE_LOSS_LIMIT` | `text` | NULL | NULL | 연속 손실 제한(회) | +| `DAILY_PROFIT_TARGET_KRW` | `text` | NULL | NULL | 목표 금액(원) | +| `DAILY_PROFIT_TARGET_PCT` | `text` | NULL | NULL | 목표 수익률(%) · 퍼센트 그대로 (예: 2.5 = 2.5%) · 0=미사용 | +| `DAILY_PROFIT_TARGET_BUDGET_KRW` | `text` | NULL | NULL | 분모 운용한도(원) · 0 = ON 전략 TOTAL_BUDGET 합 자동 | +| `DAILY_PROFIT_HALT_NEW_BUYS` | `text` | NULL | NULL | 신규매수 수동 중단 · 목표·손익과 무관한 수동 매수 잠금. ON=즉시 신규매수 전면 차단 / OFF=정상 (기본) | +| `DAILY_PROFIT_NOTIFY_MM` | `text` | NULL | NULL | 달성 시 MM 알림 | +| `USE_BAN_SYSTEM` | `text` | NULL | NULL | 공통env: 사용_ BAN SYSTEM (USE_BAN_SYSTEM) | +| `BAN_HOURS` | `text` | NULL | NULL | 공통env: BAN HOURS (BAN_HOURS) | +| `USE_STOCK_FILTER` | `text` | NULL | NULL | 공통env: 사용_ STOCK 필터 (USE_STOCK_FILTER) | +| `RSI_OVERHEAT_THRESHOLD` | `text` | NULL | NULL | 공통env: RSI OVERHEAT THRESHOLD (RSI_OVERHEAT_THRESHOLD) | +| `MIN_RECOVERY_RATIO` | `text` | NULL | NULL | 공통env: 최소 RECOVERY RATIO (MIN_RECOVERY_RATIO) | +| `MAX_RECOVERY_RATIO` | `text` | NULL | NULL | 공통env: 최대 RECOVERY RATIO (MAX_RECOVERY_RATIO) | +| `USE_TWAP` | `text` | NULL | NULL | 공통env: 사용_ TWAP (USE_TWAP) | +| `TWAP_MIN_SPLIT` | `text` | NULL | NULL | 공통env: TWAP 최소 SPLIT (TWAP_MIN_SPLIT) | +| `TWAP_MAX_SPLIT` | `text` | NULL | NULL | 공통env: TWAP 최대 SPLIT (TWAP_MAX_SPLIT) | +| `TWAP_MIN_DELAY` | `text` | NULL | NULL | 공통env: TWAP 최소 DELAY (TWAP_MIN_DELAY) | +| `TWAP_MAX_DELAY` | `text` | NULL | NULL | 공통env: TWAP 최대 DELAY (TWAP_MAX_DELAY) | +| `USE_ML_SIGNAL` | `text` | NULL | NULL | 공통env: 사용_ ML SIGNAL (USE_ML_SIGNAL) | +| `ML_MIN_PROBABILITY` | `text` | NULL | NULL | 공통env: ML 최소 PROBABILITY (ML_MIN_PROBABILITY) | +| `USE_NEWS_ANALYSIS` | `text` | NULL | NULL | 공통env: 사용_ NEWS ANALYSIS (USE_NEWS_ANALYSIS) | +| `NEWS_ANALYSIS_HOUR` | `text` | NULL | NULL | 공통env: NEWS ANALYSIS HOUR (NEWS_ANALYSIS_HOUR) | +| `NEWS_MAX_COUNT` | `text` | NULL | NULL | 공통env: NEWS 최대 COUNT (NEWS_MAX_COUNT) | +| `USE_QUICK_PROFIT_PROTECTION` | `text` | NULL | NULL | 공통env: 사용_ QUICK PROFIT PROTECTION (USE_QUICK_PROFIT_PROTECTION) | +| `HIGH_PRICE_CHASE_THRESHOLD` | `text` | NULL | NULL | 공통env: HIGH PRICE CHASE THRESHOLD (HIGH_PRICE_CHASE_THRESHOLD) | +| `MAX_DAILY_CHANGE_PCT` | `text` | NULL | NULL | 공통env: 일일매수횟수상한 CHANGE % (MAX_DAILY_CHANGE_PCT) | +| `MA20_MAX_ABOVE_PCT` | `text` | NULL | NULL | 공통env: MA20 최대 ABOVE % (MA20_MAX_ABOVE_PCT) | +| `VOLUME_AVG_MULTIPLIER` | `text` | NULL | NULL | 공통env: 거래량 UME 평균 배수 IPLIER (VOLUME_AVG_MULTIPLIER) | +| `CANDLE_OPEN_PRICE_BUFFER` | `text` | NULL | NULL | 공통env: CANDLE OPEN PRICE BUFFER (CANDLE_OPEN_PRICE_BUFFER) | +| `MIN_CANDLE_LEN_TAIL` | `text` | NULL | NULL | 공통env: 최소 CANDLE LEN TAIL (MIN_CANDLE_LEN_TAIL) | +| `MIN_PRICE_TAIL` | `text` | NULL | NULL | 공통env: 최소가격 TAIL (MIN_PRICE_TAIL) | +| `INTRADAY_INVESTOR_NET_BUY_THRESHOLD` | `text` | NULL | NULL | 공통env: INTRADAY INVESTOR NET BUY THRESHOLD (INTRADAY_INVESTOR_NET_BUY_THRESHOLD) | +| `SIZE_CLASS_LARGE_MIN` | `text` | NULL | NULL | 공통env: SIZE CLASS LARGE 최소 (SIZE_CLASS_LARGE_MIN) | +| `SIZE_CLASS_MID_MIN` | `text` | NULL | NULL | 공통env: SIZE CLASS MID 최소 (SIZE_CLASS_MID_MIN) | +| `SCAN_INVESTOR_NET_STRONG` | `text` | NULL | NULL | 공통env: SCAN INVESTOR NET STRONG (SCAN_INVESTOR_NET_STRONG) | +| `SCAN_INVESTOR_SCORE_STRONG` | `text` | NULL | NULL | 공통env: SCAN INVESTOR SCORE STRONG (SCAN_INVESTOR_SCORE_STRONG) | +| `SCAN_INVESTOR_SCORE_WEAK` | `text` | NULL | NULL | 공통env: SCAN INVESTOR SCORE WEAK (SCAN_INVESTOR_SCORE_WEAK) | +| `SCAN_INVESTOR_BONUS_STRONG` | `text` | NULL | NULL | 공통env: SCAN INVESTOR BONUS STRONG (SCAN_INVESTOR_BONUS_STRONG) | +| `SCAN_INVESTOR_BONUS_WEAK` | `text` | NULL | NULL | 공통env: SCAN INVESTOR BONUS WEAK (SCAN_INVESTOR_BONUS_WEAK) | +| `SCAN_VOLUME_BONUS_MIN` | `text` | NULL | NULL | 공통env: SCAN 거래량 UME BONUS 최소 (SCAN_VOLUME_BONUS_MIN) | +| `SCAN_VOLUME_BONUS_POINT` | `text` | NULL | NULL | 공통env: SCAN 거래량 UME BONUS POINT (SCAN_VOLUME_BONUS_POINT) | +| `SCAN_EXEC_STRENGTH_HIGH` | `text` | NULL | NULL | 공통env: SCAN EXEC STRENGTH HIGH (SCAN_EXEC_STRENGTH_HIGH) | +| `SCAN_EXEC_STRENGTH_MID` | `text` | NULL | NULL | 공통env: SCAN EXEC STRENGTH MID (SCAN_EXEC_STRENGTH_MID) | +| `SCAN_EXEC_BONUS_HIGH` | `text` | NULL | NULL | 공통env: SCAN EXEC BONUS HIGH (SCAN_EXEC_BONUS_HIGH) | +| `SCAN_EXEC_BONUS_MID` | `text` | NULL | NULL | 공통env: SCAN EXEC BONUS MID (SCAN_EXEC_BONUS_MID) | +| `SCAN_SCORE_DROP_WEIGHT` | `text` | NULL | NULL | 공통env: SCAN SCORE 되돌림/하락 WEIGHT (SCAN_SCORE_DROP_WEIGHT) | +| `SCAN_SCORE_RECOVERY_WEIGHT` | `text` | NULL | NULL | 공통env: SCAN SCORE RECOVERY WEIGHT (SCAN_SCORE_RECOVERY_WEIGHT) | +| `MIN_SCORE_DISPLAY` | `text` | NULL | NULL | 공통env: 최소 SCORE DISPLAY (MIN_SCORE_DISPLAY) | +| `MM_TOP_N` | `text` | NULL | NULL | 공통env: MM TOP N (MM_TOP_N) | +| `SLOT_MONEY_DEFAULT` | `text` | NULL | NULL | 공통env: 슬롯금액 DEFAULT (SLOT_MONEY_DEFAULT) | +| `SLOT_BASE_AMOUNT_CAP` | `text` | NULL | NULL | 공통env: SLOT BASE AMOUNT CAP (SLOT_BASE_AMOUNT_CAP) | +| `SIZE_CLASS_SMALL_RATIO` | `text` | NULL | NULL | 공통env: SIZE CLASS SMALL RATIO (SIZE_CLASS_SMALL_RATIO) | +| `SIZE_CLASS_MID_RATIO` | `text` | NULL | NULL | 공통env: SIZE CLASS MID RATIO (SIZE_CLASS_MID_RATIO) | +| `SCAN_MIN_PREV_DAY_PCT` | `text` | NULL | NULL | 공통env: SCAN 최소 PREV DAY % (SCAN_MIN_PREV_DAY_PCT) | +| `UPDATE_UNIVERSE_MIN_CANDIDATES` | `text` | NULL | NULL | 공통env: UPDATE 유니버스 최소 CANDIDATES (UPDATE_UNIVERSE_MIN_CANDIDATES) | +| `UPDATE_UNIVERSE_MIN_SCORE` | `text` | NULL | NULL | 공통env: UPDATE 유니버스 최소 SCORE (UPDATE_UNIVERSE_MIN_SCORE) | +| `UPDATE_UNIVERSE_FALLBACK_TOP_N` | `text` | NULL | NULL | 공통env: UPDATE 유니버스 FALLBACK TOP N (UPDATE_UNIVERSE_FALLBACK_TOP_N) | +| `UPDATE_UNIVERSE_TOP_LOG` | `text` | NULL | NULL | 공통env: UPDATE 유니버스 TOP LOG (UPDATE_UNIVERSE_TOP_LOG) | +| `UPDATE_UNIVERSE_TOP_N` | `text` | NULL | NULL | 공통env: UPDATE 유니버스 TOP N (UPDATE_UNIVERSE_TOP_N) | +| `SCAN_INTERVAL_SEC` | `text` | NULL | NULL | 공통env: SCAN INTERVAL 초 (SCAN_INTERVAL_SEC) | +| `USE_RANDOM_SPLIT` | `text` | NULL | NULL | 공통env: 사용_ RANDOM SPLIT (USE_RANDOM_SPLIT) | +| `FORCE_MARKET_OPEN` | `text` | NULL | NULL | 공통env: FORCE MARKET OPEN (FORCE_MARKET_OPEN) | +| `FORCE_BUY_TEST` | `text` | NULL | NULL | 공통env: 강제매수테스트 TEST (FORCE_BUY_TEST) | +| `TOTAL_DEPOSIT` | `text` | NULL | NULL | 공통env: TOTAL DEPOSIT (TOTAL_DEPOSIT) | +| `ROUND_TRIP_COST_PCT` | `text` | NULL | NULL | 공통env: ROUND TRIP COST % (ROUND_TRIP_COST_PCT) | +| `POP_NET_PCT` | `text` | NULL | NULL | 공통env: POP NET % (POP_NET_PCT) | +| `LOCK_NET_PCT` | `text` | NULL | NULL | 공통env: LOCK NET % (LOCK_NET_PCT) | +| `MAX_LOSS_PER_TRADE_KRW` | `text` | NULL | NULL | 공통env: 최대 LOSS PER TRADE 원 (MAX_LOSS_PER_TRADE_KRW) | +| `MIN_DROP_PCT_FOR_LOSS_CUT` | `text` | NULL | NULL | 공통env: 최소 되돌림/하락 % FOR LOSS CUT (MIN_DROP_PCT_FOR_LOSS_CUT) | +| `KIS_APP_KEY_REAL` | `text` | NULL | NULL | 공통env: KIS APP KEY REAL (KIS_APP_KEY_REAL) | +| `KIS_APP_SECRET_REAL` | `text` | NULL | NULL | 공통env: KIS APP 초 RET REAL (KIS_APP_SECRET_REAL) | +| `KIS_APP_KEY_MOCK` | `text` | NULL | NULL | 공통env: KIS APP KEY MOCK (KIS_APP_KEY_MOCK) | +| `KIS_APP_SECRET_MOCK` | `text` | NULL | NULL | 공통env: KIS APP 초 RET MOCK (KIS_APP_SECRET_MOCK) | +| `KIS_ACCOUNT_NO_REAL` | `text` | NULL | NULL | 공통env: KIS ACCOUNT NO REAL (KIS_ACCOUNT_NO_REAL) | +| `KIS_ACCOUNT_CODE_REAL` | `text` | NULL | NULL | 공통env: KIS ACCOUNT CODE REAL (KIS_ACCOUNT_CODE_REAL) | +| `KIS_ACCOUNT_NO_MOCK` | `text` | NULL | NULL | 공통env: KIS ACCOUNT NO MOCK (KIS_ACCOUNT_NO_MOCK) | +| `KIS_ACCOUNT_CODE_MOCK` | `text` | NULL | NULL | 공통env: KIS ACCOUNT CODE MOCK (KIS_ACCOUNT_CODE_MOCK) | +| `KIS_MOCK` | `text` | NULL | NULL | 공통env: KIS MOCK (KIS_MOCK) | +| `TAKE_PROFIT_PCT` | `text` | NULL | NULL | 공통env: 익절 % (TAKE_PROFIT_PCT) | +| `MIN_DROP_RATE` | `text` | NULL | NULL | 공통env: 최소 되돌림/하락 RATE (MIN_DROP_RATE) | +| `MIN_RECOVERY_RATIO_SHORT` | `text` | NULL | NULL | 공통env: 최소 RECOVERY RATIO SHORT (MIN_RECOVERY_RATIO_SHORT) | +| `QUICK_PROFIT_PROTECT_HOURS` | `text` | NULL | NULL | 공통env: QUICK PROFIT PROTECT HOURS (QUICK_PROFIT_PROTECT_HOURS) | +| `QUICK_PROFIT_MAX_RATIO` | `text` | NULL | NULL | 공통env: QUICK PROFIT 최대 RATIO (QUICK_PROFIT_MAX_RATIO) | +| `QUICK_PROFIT_CURRENT_MIN` | `text` | NULL | NULL | 공통env: QUICK PROFIT CURRENT 최소 (QUICK_PROFIT_CURRENT_MIN) | +| `MIN_HOLD_EARLY_TAKE_PCT` | `text` | NULL | NULL | 공통env: 최소보유 EARLY TAKE % (MIN_HOLD_EARLY_TAKE_PCT) | +| `MIN_HOLD_HIGH_PCT` | `text` | NULL | NULL | 공통env: 최소보유 HIGH % (MIN_HOLD_HIGH_PCT) | +| `MIN_HOLD_DROP_FROM_HIGH` | `text` | NULL | NULL | 공통env: 최소보유 되돌림/하락 FROM HIGH (MIN_HOLD_DROP_FROM_HIGH) | +| `POST_HOLD_TAKE_PCT` | `text` | NULL | NULL | 공통env: POST HOLD TAKE % (POST_HOLD_TAKE_PCT) | +| `POST_HOLD_DROP_FROM_HIGH` | `text` | NULL | NULL | 공통env: POST HOLD 되돌림/하락 FROM HIGH (POST_HOLD_DROP_FROM_HIGH) | +| `MAX_PER` | `text` | NULL | NULL | 공통env: 최대 PER (MAX_PER) | +| `MAX_PEG` | `text` | NULL | NULL | 공통env: 최대 PEG (MAX_PEG) | +| `MIN_GROWTH_PCT` | `text` | NULL | NULL | 공통env: 최소 GROWTH % (MIN_GROWTH_PCT) | +| `DCA_INTERVALS` | `text` | NULL | NULL | 공통env: DCA INTERVALS (DCA_INTERVALS) | +| `DCA_AMOUNTS` | `text` | NULL | NULL | 공통env: DCA AMOUNTS (DCA_AMOUNTS) | +| `MM_SERVER_URL` | `text` | NULL | NULL | 공통env: MM SERVER URL (MM_SERVER_URL) | +| `MM_BOT_TOKEN_` | `text` | NULL | NULL | 공통env: MM BOT TOKEN (MM_BOT_TOKEN_) | +| `MATTERMOST_CHANNEL` | `text` | NULL | NULL | 공통env: MATTERMOST CHANNEL (MATTERMOST_CHANNEL) | +| `GEMINI_API_KEY` | `text` | NULL | NULL | 공통env: GEMINI API KEY (GEMINI_API_KEY) | +| `AI_JOURNAL_LINES` | `text` | NULL | NULL | 공통env: AI JOURNAL LINES (AI_JOURNAL_LINES) | +| `ANTHROPIC_API_KEY` | `text` | NULL | NULL | 공통env: ANTHROPIC API KEY (ANTHROPIC_API_KEY) | +| `CLAUDE_MODEL_ID` | `text` | NULL | NULL | 공통env: CLAUDE 모드 L ID (CLAUDE_MODEL_ID) | +| `CLAUDE_MAX_TOKENS` | `text` | NULL | NULL | 공통env: CLAUDE 최대 TOKENS (CLAUDE_MAX_TOKENS) | +| `MM_BUTLER_CHANNEL` | `text` | NULL | NULL | 공통env: MM BUTLER CHANNEL (MM_BUTLER_CHANNEL) | +| `MM_DUAL_CHANNEL_ENABLED` | `text` | NULL | NULL | 공통env: MM DUAL CHANNEL ON/OFF (MM_DUAL_CHANNEL_ENABLED) | +| `OPENROUTER_API_KEY` | `text` | NULL | NULL | 공통env: OPENROUTER API KEY (OPENROUTER_API_KEY) | +| `OPENROUTER_MODEL_ID` | `text` | NULL | NULL | 공통env: OPENROUTER 모드 L ID (OPENROUTER_MODEL_ID) | +| `KIS_LONG_MM_CHANNEL` | `text` | NULL | NULL | 공통env: KIS LONG MM CHANNEL (KIS_LONG_MM_CHANNEL) | +| `LONG_DAILY_LOOKBACK_DAYS` | `text` | NULL | NULL | 공통env: LONG DAILY LOOKBACK DAYS (LONG_DAILY_LOOKBACK_DAYS) | +| `LONG_MA_SHORT_DAYS` | `text` | NULL | NULL | 공통env: LONG MA SHORT DAYS (LONG_MA_SHORT_DAYS) | +| `LONG_MA_LONG_DAYS` | `text` | NULL | NULL | 공통env: LONG MA LONG DAYS (LONG_MA_LONG_DAYS) | +| `LONG_REPORT_AM_HOUR` | `text` | NULL | NULL | 공통env: LONG REPORT AM HOUR (LONG_REPORT_AM_HOUR) | +| `LONG_REPORT_AM_MIN` | `text` | NULL | NULL | 공통env: LONG REPORT AM 최소 (LONG_REPORT_AM_MIN) | +| `LONG_REPORT_PM_HOUR` | `text` | NULL | NULL | 공통env: LONG REPORT PM HOUR (LONG_REPORT_PM_HOUR) | +| `LONG_REPORT_PM_MIN` | `text` | NULL | NULL | 공통env: LONG REPORT PM 최소 (LONG_REPORT_PM_MIN) | +| `LONG_NEWS_ENABLED` | `text` | NULL | NULL | 공통env: LONG NEWS ON/OFF (LONG_NEWS_ENABLED) | +| `LONG_NEWS_INTERVAL_MIN` | `text` | NULL | NULL | 공통env: LONG NEWS INTERVAL 최소 (LONG_NEWS_INTERVAL_MIN) | +| `LONG_NEWS_ACTIVE_START_HOUR` | `text` | NULL | NULL | 공통env: LONG NEWS ACTIVE START HOUR (LONG_NEWS_ACTIVE_START_HOUR) | +| `LONG_NEWS_ACTIVE_END_HOUR` | `text` | NULL | NULL | 공통env: LONG NEWS ACTIVE END HOUR (LONG_NEWS_ACTIVE_END_HOUR) | +| `LONG_ANALYSIS_DELAY_MIN_SEC` | `text` | NULL | NULL | 공통env: LONG ANALYSIS DELAY 최소 초 (LONG_ANALYSIS_DELAY_MIN_SEC) | +| `LONG_ANALYSIS_DELAY_MAX_SEC` | `text` | NULL | NULL | 공통env: LONG ANALYSIS DELAY 최대 초 (LONG_ANALYSIS_DELAY_MAX_SEC) | +| `RSI_PERIOD` | `text` | NULL | NULL | 공통env: RSI 기간 (RSI_PERIOD) | +| `KIS_WS_URL_REAL` | `text` | NULL | NULL | 공통env: URL REAL (KIS_WS_URL_REAL) | +| `KIS_WS_URL_MOCK` | `text` | NULL | NULL | 공통env: URL MOCK (KIS_WS_URL_MOCK) | +| `KIS_WS_MOCK_ENABLED` | `text` | NULL | NULL | 공통env: MOCK ON/OFF (KIS_WS_MOCK_ENABLED) | +| `REENTRY_COOLDOWN_SEC` | `text` | NULL | NULL | 공통env: REENTRY 쿨다운 초 (REENTRY_COOLDOWN_SEC) | +| `TIME_START` | `text` | NULL | NULL | 공통env: 매수시작 (TIME_START) | +| `TIME_END` | `text` | NULL | NULL | 공통env: 매수종료 (TIME_END) | +| `SELL_FAILURE_BACKOFF_SEC` | `text` | NULL | NULL | 공통env: SELL FAILURE BACKOFF 초 (SELL_FAILURE_BACKOFF_SEC) | +| `BROKER_HOLDINGS_CACHE_TTL_SEC` | `text` | NULL | NULL | 공통env: BROKER HOLDINGS CACHE TTL 초 (BROKER_HOLDINGS_CACHE_TTL_SEC) | +| `GHOST_POSITION_COOLDOWN_SEC` | `text` | NULL | NULL | 공통env: GHOST POSITION 쿨다운 초 (GHOST_POSITION_COOLDOWN_SEC) | +| `WS_TICK_SAVE_ENABLED` | `text` | NULL | NULL | 공통env: WS TICK SAVE ON/OFF (WS_TICK_SAVE_ENABLED) | +| `WS_TICK_BUFFER_MAX_PER_CODE` | `text` | NULL | NULL | 공통env: WS TICK BUFFER 최대 PER CODE (WS_TICK_BUFFER_MAX_PER_CODE) | +| `WS_TICK_DB_BATCH_SIZE` | `text` | NULL | NULL | 공통env: WS TICK DB BATCH SIZE (WS_TICK_DB_BATCH_SIZE) | +| `WS_TICK_DB_FLUSH_SEC` | `text` | NULL | NULL | 공통env: WS TICK DB FLUSH 초 (WS_TICK_DB_FLUSH_SEC) | +| `WS_TICK_WRITE_QUEUE_MAX` | `text` | NULL | NULL | 공통env: WS TICK WRITE QUEUE 최대 (WS_TICK_WRITE_QUEUE_MAX) | +| `WS_TICK_KEEP_DAYS` | `text` | NULL | NULL | 공통env: WS TICK KEEP DAYS (WS_TICK_KEEP_DAYS) | +| `WS_TICK_RECORD_SCOPE` | `text` | NULL | NULL | 공통env: WS TICK RECORD SCOPE (WS_TICK_RECORD_SCOPE) | +| `WS_TICK_DEFAULT_MARKET` | `text` | NULL | NULL | 공통env: WS TICK DEFAULT MARKET (WS_TICK_DEFAULT_MARKET) | +| `WS_TICK_DEFAULT_EXCHANGE` | `text` | NULL | NULL | 공통env: WS TICK DEFAULT EXCHANGE (WS_TICK_DEFAULT_EXCHANGE) | +| `WS_TICK_DEFAULT_CURRENCY` | `text` | NULL | NULL | 공통env: WS TICK DEFAULT CURRENCY (WS_TICK_DEFAULT_CURRENCY) | +| `WS_ORDERBOOK_SAVE_ENABLED` | `text` | NULL | NULL | 호가 시계열(KIWOOM_0D) — 구독후보 ~3초 덤프 · 틱처럼 후보·보유만 연속 저장(source=kiwoom_0d) · 파람서치 호가축용 · 봇 재시작 필요 · 필터와 무관 | +| `WS_PROGRAM_SAVE_ENABLED` | `text` | NULL | NULL | 프로그램 시계열 — 구독후보 ~3초 덤프 · SAVE와 동일 Recorder · 봇 재시작 필요 · 필터와 무관 | +| `WS_TRIGGER_SNAPSHOT_DB_INTERVAL_SEC` | `text` | NULL | NULL | 호가/프로그램 시계열 간격(초) · SAVE ON일 때만 · 종목당 최소 저장 간격 · 봇 재시작 필요 | +| `WS_TRIGGER_SNAPSHOT_DB_BATCH_SIZE` | `text` | NULL | NULL | 공통env: WS TRIGGER SNAPSHOT DB BATCH SIZE (WS_TRIGGER_SNAPSHOT_DB_BATCH_SIZE) | +| `WS_TRIGGER_SNAPSHOT_DB_FLUSH_SEC` | `text` | NULL | NULL | 공통env: WS TRIGGER SNAPSHOT DB FLUSH 초 (WS_TRIGGER_SNAPSHOT_DB_FLUSH_SEC) | +| `WS_TRIGGER_SNAPSHOT_WRITE_QUEUE_MAX` | `text` | NULL | NULL | 공통env: WS TRIGGER SNAPSHOT WRITE QUEUE 최대 (WS_TRIGGER_SNAPSHOT_WRITE_QUEUE_MAX) | +| `WS_ORDERBOOK_KEEP_DAYS` | `text` | NULL | NULL | 공통env: WS 호가 KEEP DAYS (WS_ORDERBOOK_KEEP_DAYS) | +| `WS_PROGRAM_KEEP_DAYS` | `text` | NULL | NULL | 공통env: WS 프로그램매매 KEEP DAYS (WS_PROGRAM_KEEP_DAYS) | +| `BACKTEST_USE_TRIGGER_SNAPSHOT_DB` | `text` | NULL | NULL | 공통env: BACKTEST 사용_ TRIGGER SNAPSHOT DB (BACKTEST_USE_TRIGGER_SNAPSHOT_DB) | +| `WHIPSAW_FILTER_ENABLED` | `text` | NULL | NULL | 휩쏘 필터(글로벌) | +| `WHIPSAW_BACKTEST_OHLC_FALLBACK` | `text` | NULL | NULL | 공통env: 휩쏘 BACKTEST OHLC FALLBACK (WHIPSAW_BACKTEST_OHLC_FALLBACK) | +| `WHIPSAW_MODE` | `text` | NULL | NULL | 공통env: 휩쏘 모드 (WHIPSAW_MODE) | +| `WHIPSAW_SUBBAR_SEC` | `text` | NULL | NULL | 휩쏘 미니봉(초) · 틱을 N초 봉으로 묶음 · 실매 기본 30 · Optuna 꼬리 TPE 탐색 | +| `WHIPSAW_LOOKBACK_SEC` | `text` | NULL | NULL | 휩쏘 룩백(초) · 신호 직전 몇 초를 볼지 · 실매 기본 90 | +| `WHIPSAW_DIP_PCT` | `text` | NULL | NULL | 휩쏘 하락폭(분율, 0.003=0.3%) | +| `WHIPSAW_RECOVERY_TOL_PCT` | `text` | NULL | NULL | 휩쏘 회복 허용(분율, 0.001=0.1%) | +| `WHIPSAW_MIN_SUBBARS` | `text` | NULL | NULL | 공통env: 휩쏘 최소 SUBBARS (WHIPSAW_MIN_SUBBARS) | +| `WHIPSAW_TICK_BUFFER_LIMIT` | `text` | NULL | NULL | 공통env: 휩쏘 TICK BUFFER LIMIT (WHIPSAW_TICK_BUFFER_LIMIT) | +| `USE_KELLY_FORMULA` | `text` | NULL | NULL | 공통env: 사용_ KELLY FORMULA (USE_KELLY_FORMULA) | +| `KELLY_MULTIPLIER` | `text` | NULL | NULL | 공통env: KELLY 배수 IPLIER (KELLY_MULTIPLIER) | +| `USE_MARKET_IOC` | `text` | NULL | NULL | 매수 시장가 IOC · 실전 매수 ORD_DVSN=13(IOC) · OFF=01 · 모의는 01 고정 | +| `KIS_PRICE_CACHE_TTL_SEC` | `text` | NULL | NULL | 공통env: KIS PRICE CACHE TTL 초 (KIS_PRICE_CACHE_TTL_SEC) | +| `MIN_HOLD_AFTER_BUY_SEC` | `text` | NULL | NULL | 공통env: 최소보유 AFTER BUY 초 (MIN_HOLD_AFTER_BUY_SEC) | +| `MIN_HOLD_HOURS` | `text` | NULL | NULL | 공통env: 최소보유 HOURS (MIN_HOLD_HOURS) | +| `MAX_RECOVERY_RATIO_3M` | `text` | NULL | NULL | 공통env: 최대 RECOVERY RATIO 3M (MAX_RECOVERY_RATIO_3M) | +| `CANDIDATE_LIST_TOP_N_LIGHT` | `text` | NULL | NULL | 공통env: CANDIDATE LIST TOP N LIGHT (CANDIDATE_LIST_TOP_N_LIGHT) | +| `SCAN_UNIVERSE_MAX_CODES` | `text` | NULL | NULL | 공통env: SCAN 유니버스 최대 CODES (SCAN_UNIVERSE_MAX_CODES) | +| `KIS_APP_KEY` | `text` | NULL | NULL | 공통env: KIS APP KEY (KIS_APP_KEY) | +| `KIS_APP_SECRET` | `text` | NULL | NULL | 공통env: KIS APP 초 RET (KIS_APP_SECRET) | +| `KIS_ACCOUNT_NO` | `text` | NULL | NULL | 공통env: KIS ACCOUNT NO (KIS_ACCOUNT_NO) | +| `KIS_ACCOUNT_CODE` | `text` | NULL | NULL | 공통env: KIS ACCOUNT CODE (KIS_ACCOUNT_CODE) | +| `GEMINI_MODEL_ID` | `text` | NULL | NULL | 공통env: GEMINI 모드 L ID (GEMINI_MODEL_ID) | +| `MM_BUTLER_POLL_SEC` | `text` | NULL | NULL | 공통env: MM BUTLER POLL 초 (MM_BUTLER_POLL_SEC) | +| `AI_SOURCE_MAX_CHARS` | `text` | NULL | NULL | 공통env: AI SOURCE 최대 CHARS (AI_SOURCE_MAX_CHARS) | +| `FEE_RATE_PCT` | `text` | NULL | NULL | 공통env: 수수료율 % (FEE_RATE_PCT) | +| `SELL_TAX_RATE_PCT` | `text` | NULL | NULL | 공통env: 매도세금 RATE % (SELL_TAX_RATE_PCT) | +| `KIWOOM_APP_KEY_REAL` | `text` | NULL | NULL | 공통env: KIWOOM APP KEY REAL (KIWOOM_APP_KEY_REAL) | +| `KIWOOM_APP_SECRET_REAL` | `text` | NULL | NULL | 공통env: KIWOOM APP 초 RET REAL (KIWOOM_APP_SECRET_REAL) | +| `KIWOOM_APP_KEY_MOCK` | `text` | NULL | NULL | 공통env: KIWOOM APP KEY MOCK (KIWOOM_APP_KEY_MOCK) | +| `KIWOOM_APP_SECRET_MOCK` | `text` | NULL | NULL | 공통env: KIWOOM APP 초 RET MOCK (KIWOOM_APP_SECRET_MOCK) | +| `KIWOOM_APP_KEY` | `text` | NULL | NULL | 공통env: KIWOOM APP KEY (KIWOOM_APP_KEY) | +| `KIWOOM_APP_SECRET` | `text` | NULL | NULL | 공통env: KIWOOM APP 초 RET (KIWOOM_APP_SECRET) | +| `PERMANENT_WS_CODES` | `text` | NULL | NULL | 공통env: PERMANENT WS CODES (PERMANENT_WS_CODES) | +| `USE_MARKET_REGIME_FILTER` | `text` | NULL | NULL | 공통env: 사용_ MARKET REGIME 필터 (USE_MARKET_REGIME_FILTER) | +| `MARKET_REGIME_MIN_RSI` | `text` | NULL | NULL | 공통env: MARKET REGIME 최소 RSI (MARKET_REGIME_MIN_RSI) | +| `USE_THEME_HEAT_FILTER` | `text` | NULL | NULL | 공통env: 사용_ THEME HEAT 필터 (USE_THEME_HEAT_FILTER) | +| `THEME_HEAT_RSI_MAX` | `text` | NULL | NULL | 공통env: THEME HEAT RSI상한 (THEME_HEAT_RSI_MAX) | +| `STRATEGY_SAME_CODE_POLICY` | `text` | NULL | NULL | 공통env: 전략 SAME CODE POLICY (STRATEGY_SAME_CODE_POLICY) | +| `ORDER_CASH_PCT` | `text` | NULL | NULL | 공통env: ORDER CASH % (ORDER_CASH_PCT) | +| `ORDER_CASH_FEE_BUFFER` | `text` | NULL | NULL | 공통env: ORDER CASH FEE BUFFER (ORDER_CASH_FEE_BUFFER) | +| `ORDER_CASH_DIVIDE_BY_MAX_STOCKS` | `text` | NULL | NULL | 공통env: ORDER CASH DIVIDE BY 동시보유상한 (ORDER_CASH_DIVIDE_BY_MAX_STOCKS) | +| `ACCOUNT_CASH_PERSIST_SEC` | `text` | NULL | NULL | 공통env: ACCOUNT CASH PERSIST 초 (ACCOUNT_CASH_PERSIST_SEC) | +| `BACKTEST_EXIT_CHECKS_PER_BAR` | `text` | NULL | NULL | 공통env: BACKTEST EXIT CHECKS PER BAR (BACKTEST_EXIT_CHECKS_PER_BAR) | +| `RANKING_POLL_INTERVAL_SEC` | `text` | NULL | NULL | 공통env: RANKING POLL INTERVAL 초 (RANKING_POLL_INTERVAL_SEC) | +| `KIS_HTS_ID` | `text` | NULL | NULL | 공통env: KIS HTS ID (KIS_HTS_ID) | +| `CONDITION_POLL_INTERVAL_SEC` | `text` | NULL | NULL | 공통env: CONDITION POLL INTERVAL 초 (CONDITION_POLL_INTERVAL_SEC) | +| `CONDITION_EXIT_GRACE_SEC` | `text` | NULL | NULL | 공통env: CONDITION EXIT GRACE 초 (CONDITION_EXIT_GRACE_SEC) | +| `UNIVERSE_HISTORY_SAVE` | `text` | NULL | NULL | 공통env: 유니버스 HISTORY SAVE (UNIVERSE_HISTORY_SAVE) | +| `WS_GAP_FILL_OFF_HOURS` | `text` | NULL | NULL | 공통env: WS GAP FILL OFF HOURS (WS_GAP_FILL_OFF_HOURS) | +| `WS_GAP_FILL_LIMIT` | `text` | NULL | NULL | 공통env: WS GAP FILL LIMIT (WS_GAP_FILL_LIMIT) | +| `WS_TIMEFRAMES` | `text` | NULL | NULL | 공통env: WS TIMEFRAMES (WS_TIMEFRAMES) | +| `WS_GAP_FILL_KIS_FALLBACK` | `text` | NULL | NULL | 공통env: WS GAP FILL KIS FALLBACK (WS_GAP_FILL_KIS_FALLBACK) | +| `MAX_BUY_AMOUNT_PER_STOCK` | `text` | NULL | NULL | 공통env: 1회최대매수금액 PER STOCK (MAX_BUY_AMOUNT_PER_STOCK) | +| `KIS_MIN_INTERVAL_SEC` | `text` | NULL | NULL | 공통 폴백 최소간격(초) · 위 도메인별 키 미설정 시 폴백 | +| `KIS_REST_MAX_RETRIES` | `text` | NULL | NULL | 공통env: KIS REST 최대 RETRIES (KIS_REST_MAX_RETRIES) | +| `KIS_REST_BACKOFF_CAP_SEC` | `text` | NULL | NULL | 공통env: KIS REST BACKOFF CAP 초 (KIS_REST_BACKOFF_CAP_SEC) | +| `KIS_REST_TIMEOUT_SEC` | `text` | NULL | NULL | 공통env: KIS REST TIMEOUT 초 (KIS_REST_TIMEOUT_SEC) | +| `MARKET_GUARD_ENABLED` | `text` | NULL | NULL | 공통env: MARKET GUARD ON/OFF (MARKET_GUARD_ENABLED) | +| `MARKET_GUARD_5MIN_DROP_PCT` | `text` | NULL | NULL | 공통env: MARKET GUARD 5MIN 되돌림/하락 % (MARKET_GUARD_5MIN_DROP_PCT) | +| `MARKET_GUARD_DAILY_DROP_PCT` | `text` | NULL | NULL | 공통env: MARKET GUARD DAILY 되돌림/하락 % (MARKET_GUARD_DAILY_DROP_PCT) | +| `MARKET_GUARD_RECOVERY_PCT` | `text` | NULL | NULL | 공통env: MARKET GUARD RECOVERY % (MARKET_GUARD_RECOVERY_PCT) | +| `MARKET_GUARD_INDEX_CODE` | `text` | NULL | NULL | 공통env: MARKET GUARD INDEX CODE (MARKET_GUARD_INDEX_CODE) | +| `MARKET_GUARD_POLL_SEC` | `text` | NULL | NULL | 공통env: MARKET GUARD POLL 초 (MARKET_GUARD_POLL_SEC) | +| `MARKET_GUARD_INDEX_CANDLE_KEEP_MIN` | `text` | NULL | NULL | 공통env: MARKET GUARD INDEX CANDLE KEEP 최소 (MARKET_GUARD_INDEX_CANDLE_KEEP_MIN) | +| `MARKET_GUARD_PERSIST_STATE` | `text` | NULL | NULL | 공통env: MARKET GUARD PERSIST STATE (MARKET_GUARD_PERSIST_STATE) | +| `STOCK_SHARE_DENOM` | `text` | NULL | NULL | 공통env: STOCK SHARE DENOM (STOCK_SHARE_DENOM) | +| `EXCLUDE_NON_STOCK` | `text` | NULL | NULL | 비본주(우선주·ETF·ETN) 매수 제외 · 후보 name==code 여도 코드 끝 5/7/9·K/L… 로 우선주 차단 | +| `WS_PROVIDER` | `text` | NULL | NULL | 공통env: WS PROVIDER (WS_PROVIDER) | +| `WS_SUBSCRIBE_KIS_MINIMAL` | `text` | NULL | NULL | 공통env: WS SUBSCRIBE KIS 최소 IMAL (WS_SUBSCRIBE_KIS_MINIMAL) | +| `WS_VALIDATION_INTERVAL_SEC` | `text` | NULL | NULL | 공통env: WS VALIDATION INTERVAL 초 (WS_VALIDATION_INTERVAL_SEC) | +| `WS_VALIDATION_DIFF_WARN_PCT` | `text` | NULL | NULL | 공통env: WS VALIDATION DIFF WARN % (WS_VALIDATION_DIFF_WARN_PCT) | +| `KIWOOM_WS_FORCE_REAL` | `text` | NULL | NULL | 공통env: FORCE REAL (KIWOOM_WS_FORCE_REAL) | +| `KIWOOM_WS_MAX_SUBSCRIPTIONS` | `text` | NULL | NULL | 키움WS 그룹당최대구독. 기본100. 0B체결·0D호가·0w프로그램. 한투41과별개 | +| `KIWOOM_WS_REG_CHUNK_SIZE` | `text` | NULL | NULL | 공통env: REG CHUNK SIZE (KIWOOM_WS_REG_CHUNK_SIZE) | +| `KIWOOM_WS_REG_GAP_SEC` | `text` | NULL | NULL | 공통env: REG GAP 초 (KIWOOM_WS_REG_GAP_SEC) | +| `KIWOOM_WS_REG_DEBOUNCE_SEC` | `text` | NULL | NULL | 공통env: REG DEBOUNCE 초 (KIWOOM_WS_REG_DEBOUNCE_SEC) | +| `KIWOOM_WS_ORDERBOOK_ENABLED` | `text` | NULL | NULL | 호가 WS(0D) 구독 · 키움 실시간 호가 수신(RAM) · 꺼면 필터·수집·시계열 모두 불가 | +| `KIWOOM_WS_PROGRAM_ENABLED` | `text` | NULL | NULL | 프로그램 WS(0w) 구독 · 키움 프로그램매매 수신(RAM) | +| `ORDERBOOK_FILTER_ENABLED` | `text` | NULL | NULL | 공통env: 호가 필터 ON/OFF (ORDERBOOK_FILTER_ENABLED) | +| `ORDERBOOK_MAX_SPREAD_PCT` | `text` | NULL | NULL | 공통env: 호가 최대 SPREAD % (ORDERBOOK_MAX_SPREAD_PCT) | +| `ORDERBOOK_ENTRY_BID_LEVELS` | `text` | NULL | NULL | 공통env: 호가 ENTRY BID LEVELS (ORDERBOOK_ENTRY_BID_LEVELS) | +| `ORDERBOOK_ENTRY_BID_DEPTH_MULT` | `text` | NULL | NULL | 공통env: 호가 ENTRY BID DEPTH 배수 (ORDERBOOK_ENTRY_BID_DEPTH_MULT) | +| `ORDERBOOK_ENTRY_ASK_MAX_MULT` | `text` | NULL | NULL | 공통env: 호가 ENTRY ASK 최대 배수 (ORDERBOOK_ENTRY_ASK_MAX_MULT) | +| `ORDERBOOK_MIN_BID_ASK_RATIO` | `text` | NULL | NULL | 공통env: 호가 최소 BID ASK RATIO (ORDERBOOK_MIN_BID_ASK_RATIO) | +| `ORDERBOOK_BREAKOUT_ASK_WALL_MAX_QTY` | `text` | NULL | NULL | 공통env: 호가 돌파 ASK WALL 최대 QTY (ORDERBOOK_BREAKOUT_ASK_WALL_MAX_QTY) | +| `PROGRAM_FILTER_ENABLED` | `text` | NULL | NULL | 프로그램 필터(TRIGGER) — 실매 탈락 | +| `PROGRAM_MIN_NET_BUY_QTY` | `text` | NULL | NULL | 공통env: 프로그램매매 최소 NET BUY QTY (PROGRAM_MIN_NET_BUY_QTY) | +| `PROGRAM_MIN_NET_BUY_AMT` | `text` | NULL | NULL | 공통env: 프로그램매매 최소 NET BUY AMT (PROGRAM_MIN_NET_BUY_AMT) | +| `PROGRAM_MAX_SELL_BUY_QTY_RATIO` | `text` | NULL | NULL | 공통env: 프로그램매매 최대 SELL BUY QTY RATIO (PROGRAM_MAX_SELL_BUY_QTY_RATIO) | +| `PROGRAM_MIN_NET_DELTA_QTY` | `text` | NULL | NULL | 공통env: 프로그램매매 최소 NET DELTA QTY (PROGRAM_MIN_NET_DELTA_QTY) | +| `PROGRAM_SNAPSHOT_MAX_AGE_SEC` | `text` | NULL | NULL | 공통env: 프로그램매매 SNAPSHOT 최대 AGE 초 (PROGRAM_SNAPSHOT_MAX_AGE_SEC) | +| `STRICT_FILL_VERIFY` | `text` | NULL | NULL | 모의 엄격 체결 검증 · 모의 실매매만 영향 · ON=체결 API 필수(모의 API 한계로 매수 0건 가능) · 실전은 항상 엄격 | +| `ORDER_FILL_WAIT_SEC` | `text` | NULL | NULL | 시장가 체결 대기(초) | +| `LIMIT_ORDER_FILL_WAIT_SEC` | `text` | NULL | NULL | 지정가 체결 대기(초) | +| `PENDING_FILL_POLL_INTERVAL_SEC` | `text` | NULL | NULL | 미체결 재조회 주기(초) | +| `PENDING_FILL_POLL_SEC` | `text` | NULL | NULL | 재조회 1회 대기(초) | +| `PENDING_FILL_MAX_AGE_SEC` | `text` | NULL | NULL | 공통env: PENDING FILL 최대 AGE 초 (PENDING_FILL_MAX_AGE_SEC) | +| `PENDING_BUY_MAX_AGE_SEC` | `text` | NULL | NULL | 매수 미체결 만료(초) | +| `PENDING_SELL_MAX_AGE_SEC` | `text` | NULL | NULL | 익절 매도 미체결 만료(초) | +| `PENDING_SELL_STOP_MAX_AGE_SEC` | `text` | NULL | NULL | 손절 매도 미체결 만료(초) | +| `SELL_PENDING_REORDER_ON_EXPIRE` | `text` | NULL | NULL | 손절 만료 후 즉시 재매도 | +| `AUTO_CANCEL_PARTIAL_BUY_REMAINDER` | `text` | NULL | NULL | 부분체결 잔량 자동취소 | +| `WS_TRIGGER_EVAL_SAVE_ENABLED` | `text` | NULL | NULL | TRIGGER 판정 DB 저장(마스터) · OFF면 아래 COLLECT(판정 스냅) 전부 안 씀 · SAVE(시계열)와 무관 | +| `WS_TRIGGER_EVAL_DB_BATCH_SIZE` | `text` | NULL | NULL | 공통env: WS TRIGGER EVAL DB BATCH SIZE (WS_TRIGGER_EVAL_DB_BATCH_SIZE) | +| `WS_TRIGGER_EVAL_DB_FLUSH_SEC` | `text` | NULL | NULL | 공통env: WS TRIGGER EVAL DB FLUSH 초 (WS_TRIGGER_EVAL_DB_FLUSH_SEC) | +| `WS_TRIGGER_EVAL_WRITE_QUEUE_MAX` | `text` | NULL | NULL | 공통env: WS TRIGGER EVAL WRITE QUEUE 최대 (WS_TRIGGER_EVAL_WRITE_QUEUE_MAX) | +| `BACKTEST_USE_TRIGGER_EVAL_SNAPSHOT` | `text` | NULL | NULL | 공통env: BACKTEST 사용_ TRIGGER EVAL SNAPSHOT (BACKTEST_USE_TRIGGER_EVAL_SNAPSHOT) | +| `DAILY_PROFIT_MODE` | `text` | NULL | NULL | 익절 방식 · fixed=고정목표 / trailing=고점대비 되돌림 / both=둘 중 먼저 | +| `DAILY_PROFIT_TRAIL_TIERS` | `text` | NULL | NULL | 다단 트레일(수익원:컷%,…) · 벌수록 타이트하게 사수 · 예: 30000:50,100000:35,300000:25 · 비우면 단일 되돌림 사용 | +| `DAILY_PROFIT_TRAIL_DROP_PCT` | `text` | NULL | NULL | 단일 트레일 되돌림(%, 고점대비) · 다단 비었을 때만 · 예: 35 = 고점 대비 -35% 꺾이면 중단 · 0=미사용 | +| `DAILY_PROFIT_TRAIL_ARM_KRW` | `text` | NULL | NULL | 트레일 발동 최소수익(원) · 이 이상 벌어야 트레일 감시 시작 · 0=트레일 비활성(과민발동 방지) | +| `DAILY_PROFIT_TRAIL_ARM_PCT` | `text` | NULL | NULL | 트레일 발동 최소수익(%) · 운용한도 대비 % · KRW와 둘 중 큰 값 | +| `BUY_DEDUP_PENDING` | `text` | NULL | NULL | 매수 미체결 중복주문 방지 · 동일 전략·종목 PENDING_FILL 있으면 재주문 스킵 | +| `KIS_MIN_INTERVAL_SEC_MOCK` | `text` | NULL | NULL | 모의 REST 최소간격(초) · 모의 도메인(거래: 주문·체결·잔고) · 조회·랭킹·조건은 실키라 무관 · 429 로그 없으면 0.22 유지 · 한투 공지 1건/초 엄수 필요 시 1.0 · 재시작 없이 반영(1초 TTL) | +| `KIS_MIN_INTERVAL_SEC_REAL` | `text` | NULL | NULL | 실전 REST 최소간격(초) · 실전 도메인(시세 · 운영 시 거래+조회+취소+재주문 통합 한 앱키) · 18건/초 한도 → 0.12=약8건/초 · 신규앱키 첫 3일은 0.34(3건/초) · 재시작 없이 반영 | +| `PENDING_POLL_BATCH_FETCH` | `text` | NULL | NULL | 미체결 체결 일괄조회 · 실매 heartbeat 전용 · ON=poll 1회당 inquire-daily-ccld 1건(미체결 N건) · OFF=건별 조회 · 백테 무관 | +| `SCAN_REJECT_SLEEP_MIN` | `text` | NULL | NULL | 탈락 후 sleep 최소(초) · check_buy 탈락 종목마다 · REST 0건 · 0.05~0.1 로 낮추면 한 바퀴 대폭 단축 | +| `SCAN_REJECT_SLEEP_MAX` | `text` | NULL | NULL | 탈락 후 sleep 최대(초) | +| `SCAN_BUY_OK_SLEEP_MIN` | `text` | NULL | NULL | 매수성공 후 sleep 최소(초) · 1루프 1매수 후 대기 · 포지션 과집중 방지 | +| `SCAN_BUY_OK_SLEEP_MAX` | `text` | NULL | NULL | 매수성공 후 sleep 최대(초) | +| `SCAN_BUY_FAIL_SLEEP_MIN` | `text` | NULL | NULL | 주문실패 후 sleep 최소(초) | +| `SCAN_BUY_FAIL_SLEEP_MAX` | `text` | NULL | NULL | 주문실패 후 sleep 최대(초) | +| `STRATEGY_LOOP_SLEEP_MIN` | `text` | NULL | NULL | 루프 끝 sleep 최소(초) · 한 바퀴 끝 대기 · WS 구독 캐시 폴링 · 0.1=실매 반응 빠르게 | +| `STRATEGY_LOOP_SLEEP_MAX` | `text` | NULL | NULL | 루프 끝 sleep 최대(초) | +| `BALANCE_MAX_PAGES` | `text` | NULL | NULL | 잔고 연속조회 최대 페이지 · 잔고조회는 1페이지에 실전 50종목/모의 20종목만 내려옴. 보유가 많으면 다음 페이지를 이어 조회(누적 병합). 20p=최대 약 1000종목. 보유 종목이 50개를 넘으면 필수 — 부족하면 51번째부터 매도 검증에서 누락됨 | +| `KIS_SYSTEM_MM_CHANNEL` | `text` | NULL | NULL | 공통env: KIS SYSTEM MM CHANNEL (KIS_SYSTEM_MM_CHANNEL) | +| `KIS_LIFECYCLE_MM_CHANNEL` | `text` | NULL | NULL | 공통env: KIS LIFECYCLE MM CHANNEL (KIS_LIFECYCLE_MM_CHANNEL) | +| `BULK_SELL_INTERVAL_SEC` | `text` | NULL | NULL | 일괄매도 종목 간격(초) · 미등록 봇고아 일괄 시장가매도 시 REST 429 방지 · 종목마다 sleep | +| `MANUAL_HOLD_CODES` | `text` | NULL | NULL | 수동 보유 보호 종목 · 쉼표(,) 구분 종목코드 예: 005930,000660 · HTS 직접 매수·장기 보유분 보호 · 일괄매도·고아복구 제외 | +| `ORPHAN_RECONCILE_ENABLED` | `text` | NULL | NULL | 장마감 고아복구 · 매일 15:36~16:00 1회 · 실계좌 잔고↔active_trades 대조 · orders BUY 있는 미기록분만 복구 · 수동·MANUAL_HOLD 제외 | +| `WS_GAP_FILL_MAX_RETRIES` | `text` | NULL | NULL | 공통env: WS GAP FILL 최대 RETRIES (WS_GAP_FILL_MAX_RETRIES) | +| `WS_GAP_FILL_RETRY_DELAY_SEC` | `text` | NULL | NULL | 공통env: WS GAP FILL RETRY DELAY 초 (WS_GAP_FILL_RETRY_DELAY_SEC) | +| `WS_GAP_BULK_REFILL_DEBOUNCE_SEC` | `text` | NULL | NULL | 공통env: WS GAP BULK REFILL DEBOUNCE 초 (WS_GAP_BULK_REFILL_DEBOUNCE_SEC) | +| `WS_GAP_FILL_PRIORITY_TFS` | `text` | NULL | NULL | 공통env: WS GAP FILL PRIORITY TFS (WS_GAP_FILL_PRIORITY_TFS) | +| `WS_GAP_FILL_TF_SLEEP_MIN_SEC` | `text` | NULL | NULL | 공통env: WS GAP FILL TF SLEEP 최소 초 (WS_GAP_FILL_TF_SLEEP_MIN_SEC) | +| `WS_GAP_FILL_TF_SLEEP_MAX_SEC` | `text` | NULL | NULL | 공통env: WS GAP FILL TF SLEEP 최대 초 (WS_GAP_FILL_TF_SLEEP_MAX_SEC) | +| `WS_GAP_FILL_CODE_SLEEP_MIN_SEC` | `text` | NULL | NULL | 공통env: WS GAP FILL CODE SLEEP 최소 초 (WS_GAP_FILL_CODE_SLEEP_MIN_SEC) | +| `WS_GAP_FILL_CODE_SLEEP_MAX_SEC` | `text` | NULL | NULL | 공통env: WS GAP FILL CODE SLEEP 최대 초 (WS_GAP_FILL_CODE_SLEEP_MAX_SEC) | +| `WS_GAP_FILL_PHASE_PAUSE_SEC` | `text` | NULL | NULL | 공통env: WS GAP FILL PHASE PAUSE 초 (WS_GAP_FILL_PHASE_PAUSE_SEC) | +| `KIWOOM_CNSRREQ_GAP_MIN_SEC` | `text` | NULL | NULL | 공통env: KIWOOM CNSRREQ GAP 최소 초 (KIWOOM_CNSRREQ_GAP_MIN_SEC) | +| `KIWOOM_CNSRREQ_GAP_MAX_SEC` | `text` | NULL | NULL | 공통env: KIWOOM CNSRREQ GAP 최대 초 (KIWOOM_CNSRREQ_GAP_MAX_SEC) | +| `KIWOOM_CNSRREQ_MAX_RETRIES` | `text` | NULL | NULL | 공통env: KIWOOM CNSRREQ 최대 RETRIES (KIWOOM_CNSRREQ_MAX_RETRIES) | +| `KIWOOM_CNSRREQ_RETRY_SEC` | `text` | NULL | NULL | 공통env: KIWOOM CNSRREQ RETRY 초 (KIWOOM_CNSRREQ_RETRY_SEC) | +| `WS_GAP_FILL_WORKERS` | `text` | NULL | NULL | 공통env: WS GAP FILL WORKERS (WS_GAP_FILL_WORKERS) | +| `KIS_WS_SUBSCRIBE_GAP_MIN_SEC` | `text` | NULL | NULL | 공통env: SUBSCRIBE GAP 최소 초 (KIS_WS_SUBSCRIBE_GAP_MIN_SEC) | +| `KIS_WS_SUBSCRIBE_GAP_MAX_SEC` | `text` | NULL | NULL | 공통env: SUBSCRIBE GAP 최대 초 (KIS_WS_SUBSCRIBE_GAP_MAX_SEC) | +| `KIS_WS_RECONNECT_SESSION_WAIT_SEC` | `text` | NULL | NULL | 공통env: RECONNECT SESSION WAIT 초 (KIS_WS_RECONNECT_SESSION_WAIT_SEC) | +| `KIS_WS_RECONNECT_REFRESH_KEY` | `text` | NULL | NULL | 공통env: RECONNECT REFRESH KEY (KIS_WS_RECONNECT_REFRESH_KEY) | +| `KIS_WS_APPROVAL_MIN_REISSUE_SEC` | `text` | NULL | NULL | approval_key 최소재발급간격(초). 기본21600=6h. REST재발급은기존키무효화 | +| `KIS_WS_INSTANT_DROP_SEC` | `text` | NULL | NULL | 공통env: INSTANT 되돌림/하락 초 (KIS_WS_INSTANT_DROP_SEC) | +| `KIS_WS_INSTANT_DROP_MAX` | `text` | NULL | NULL | 공통env: INSTANT 되돌림/하락 최대 (KIS_WS_INSTANT_DROP_MAX) | +| `KIS_WS_INSTANT_DROP_COOLDOWN_SEC` | `text` | NULL | NULL | 공통env: INSTANT 되돌림/하락 쿨다운 초 (KIS_WS_INSTANT_DROP_COOLDOWN_SEC) | +| `WS_CANDLE_RAM_BUFFER` | `text` | NULL | NULL | 공통env: WS CANDLE RAM BUFFER (WS_CANDLE_RAM_BUFFER) | +| `KIS_WS_APPROVAL_MAX_VALID_SEC` | `text` | NULL | NULL | approval_key 최대유효(초). 기본82800≈23h. 파일캐시 kis_approval_manager | +| `ENV_MERGED_CACHE_TTL_SEC` | `text` | NULL | NULL | 공통env: ENV MERGED CACHE TTL 초 (ENV_MERGED_CACHE_TTL_SEC) | +| `WS_ORDERBOOK_COLLECT_ENABLED` | `text` | NULL | NULL | 호가 판정스냅(FILTER_EVAL) — 매수체크 순간 1장 · ≠ 연속시계열. TRIGGER 판정 때만 ws_orderbook source=filter_eval · 필터 OFF여도 저장 가능 | +| `WS_PROGRAM_COLLECT_ENABLED` | `text` | NULL | NULL | 프로그램 판정스냅 — 매수체크 순간 1장 · ≠ 연속시계열. 판정 때만 ws_program · 필터 OFF여도 가능 | +| `WS_CANDLE_FORCE_CONFIRM_ENABLED` | `text` | NULL | NULL | 공통env: WS CANDLE FORCE CONFIRM ON/OFF (WS_CANDLE_FORCE_CONFIRM_ENABLED) | +| `WS_CANDLE_FORCE_CONFIRM_GRACE_SEC` | `text` | NULL | NULL | 공통env: WS CANDLE FORCE CONFIRM GRACE 초 (WS_CANDLE_FORCE_CONFIRM_GRACE_SEC) | +| `WS_CANDLE_STALE_CHECK_INTERVAL_SEC` | `text` | NULL | NULL | 공통env: WS CANDLE STALE CHECK INTERVAL 초 (WS_CANDLE_STALE_CHECK_INTERVAL_SEC) | +| `ORPHAN_RECONCILE_PRE_EOD_ENABLED` | `text` | NULL | NULL | Pre-EOD 고아복구 · 활성 전략 중 가장 이른 EOD − N분 1회 · EOD 청산 전 active_trades 반영 | +| `ORPHAN_RECONCILE_PRE_EOD_LEAD_MIN` | `text` | NULL | NULL | Pre-EOD 선행(분) · 예: BREAKOUT EOD 15:15 → 15:08 실행 · REST 잔고 1회/일 | +| `DUPLICATE_ORDER_FILL_RECOVERY_ENABLED` | `text` | NULL | NULL | 주문DB중복 체결복구 · insert 실패(주문DB중복) 시 inquire-daily-ccld 1회 → active_trades 복구 · 실패 시에만 REST | +| `DUPLICATE_ORDER_RECOVERY_WAIT_SEC` | `text` | NULL | NULL | 중복복구 체결대기(초) · 주문DB중복 복구 전용 · 미설정 시 ORDER_FILL_WAIT_SEC 사용 | +| `INTRADAY_HOLDINGS_DRIFT_ENABLED` | `text` | NULL | NULL | 장중 qty 드리프트 감시 · 기본 OFF · ON=N분마다 잔고 REST · 알림/자동복구는 별도 키 | +| `INTRADAY_HOLDINGS_DRIFT_INTERVAL_SEC` | `text` | NULL | NULL | 드리프트 조회주기(초) · INTRADAY_HOLDINGS_DRIFT_ENABLED=ON 일 때만 | +| `INTRADAY_HOLDINGS_DRIFT_AUTO_RECOVER` | `text` | NULL | NULL | 드리프트 자동복구 · OFF=알림만 · ON=active_trades qty 보정(기본 OFF) | +| `GHOST_PURGE_ON_RECONCILE` | `text` | NULL | NULL | 고아복구 시 유령잔고 삭제 · ON=브로커 0주인데 active_trades 남은 종목 삭제(수동보호 제외) · Pre/Post EOD 동일 잔고조회에서 처리 | +| `WS_GAP_ROLLUP_3M_FROM_1M` | `text` | NULL | NULL | 공통env: WS GAP ROLLUP 3M FROM 1M (WS_GAP_ROLLUP_3M_FROM_1M) | +| `WS_GAP_FILL_CANDIDATE_MODE` | `text` | NULL | NULL | 공통env: WS GAP FILL CANDIDATE 모드 (WS_GAP_FILL_CANDIDATE_MODE) | +| `WS_TICK_GRACE_SEC` | `text` | NULL | NULL | 공통env: WS TICK GRACE 초 (WS_TICK_GRACE_SEC) | +| `WS_TICK_GRACE_HEADROOM` | `text` | NULL | NULL | 공통env: WS TICK GRACE HEADROOM (WS_TICK_GRACE_HEADROOM) | +| `WS_TICK_PREFER_KIWOOM` | `text` | NULL | NULL | 공통env: WS TICK PREFER KIWOOM (WS_TICK_PREFER_KIWOOM) | +| `VOL_MULTIPLIER` | `text` | NULL | NULL | 공통env: 거래량배수 IPLIER (VOL_MULTIPLIER) | +| `KIWOOM_CNSRCLR_BEFORE_REQ` | `text` | NULL | NULL | 공통env: KIWOOM CNSRCLR BEFORE REQ (KIWOOM_CNSRCLR_BEFORE_REQ) | +| `KIWOOM_CNSRCLR_GAP_MIN_SEC` | `text` | NULL | NULL | 공통env: KIWOOM CNSRCLR GAP 최소 초 (KIWOOM_CNSRCLR_GAP_MIN_SEC) | +| `KIWOOM_CNSRCLR_GAP_MAX_SEC` | `text` | NULL | NULL | 공통env: KIWOOM CNSRCLR GAP 최대 초 (KIWOOM_CNSRCLR_GAP_MAX_SEC) | +| `KIWOOM_CNSRCLR_SETTLE_SEC` | `text` | NULL | NULL | 공통env: KIWOOM CNSRCLR SETTLE 초 (KIWOOM_CNSRCLR_SETTLE_SEC) | +| `NON_STOCK_PREF_CODE_SUFFIXES` | `text` | NULL | NULL | 우선주 코드 끝자리(숫자) · KRX 구형 우선주. 콤마 구분 | +| `NON_STOCK_EXCLUDE_ALPHA_SUFFIX` | `text` | NULL | NULL | 코드 끝 알파벳=우선주 제외 · 00088K·00781K 등 | +| `NON_STOCK_NAME_KEYWORDS` | `text` | NULL | NULL | 공통env: NON STOCK NAME KEYWORDS (NON_STOCK_NAME_KEYWORDS) | +| `ERROR_WATCH_ENABLED` | `text` | NULL | NULL | 오류감시 ON · 감시 프로세스 시작 시 읽음 · OFF면 감시자 즉시 종료 | +| `ERROR_WATCH_UNIT` | `text` | NULL | NULL | 감시 systemd 유닛 · journalctl -u 대상 | +| `ERROR_WATCH_MM_CHANNEL` | `text` | NULL | NULL | 알림 MM alias · 비우면 KIS_SYSTEM_MM_CHANNEL(default=키스명령봇) | +| `ERROR_WATCH_COOLDOWN_SEC` | `text` | NULL | NULL | 동일오류 쿨다운(초) · 같은 Traceback 지문 재알림 간격 | +| `ERROR_WATCH_CONTEXT_LINES` | `text` | NULL | NULL | 공통env: ERROR WATCH CONTEXT LINES (ERROR_WATCH_CONTEXT_LINES) | +| `ERROR_WATCH_TRACEBACK_EXTRA_LINES` | `text` | NULL | NULL | Traceback 추가줄 · Traceback 이후 스택 수집 줄 수 | +| `ERROR_WATCH_HEALTH_CHECK_SEC` | `text` | NULL | NULL | 유닛생존 점검(초) · systemctl is-active 주기 | +| `ERROR_WATCH_GLOBAL_MIN_GAP_SEC` | `text` | NULL | NULL | 공통env: ERROR WATCH GLOBAL 최소 GAP 초 (ERROR_WATCH_GLOBAL_MIN_GAP_SEC) | +| `ERROR_WATCH_MATCH_REGEX` | `text` | NULL | NULL | 공통env: ERROR WATCH MATCH REGEX (ERROR_WATCH_MATCH_REGEX) | +| `ERROR_WATCH_IGNORE_REGEX` | `text` | NULL | NULL | 공통env: ERROR WATCH IGNORE REGEX (ERROR_WATCH_IGNORE_REGEX) | +| `ERROR_WATCH_MM_JITTER` | `text` | NULL | NULL | MM jitter · 오류 알림은 즉시 권장(OFF) | +| `ERROR_WATCH_STARTUP_NOTIFY` | `text` | NULL | NULL | 감시자 기동 MM · 감시 프로세스 시작 시 1회 알림 | +| `POST_SELL_CANDLE_BACKFILL` | `text` | NULL | NULL | 공통env: POST SELL CANDLE BACKFILL (POST_SELL_CANDLE_BACKFILL) | +| `POST_SELL_CANDLE_ROLLUP_3M` | `text` | NULL | NULL | 공통env: POST SELL CANDLE ROLLUP 3M (POST_SELL_CANDLE_ROLLUP_3M) | +| `POST_SELL_CANDLE_MAX_BARS` | `text` | NULL | NULL | 공통env: POST SELL CANDLE 최대 봉수 (POST_SELL_CANDLE_MAX_BARS) | +| `POST_SELL_CANDLE_SLEEP_MIN_SEC` | `text` | NULL | NULL | 공통env: POST SELL CANDLE SLEEP 최소 초 (POST_SELL_CANDLE_SLEEP_MIN_SEC) | +| `POST_SELL_CANDLE_SLEEP_MAX_SEC` | `text` | NULL | NULL | 공통env: POST SELL CANDLE SLEEP 최대 초 (POST_SELL_CANDLE_SLEEP_MAX_SEC) | +| `POST_SELL_CANDLE_FORCE_MOCK` | `text` | NULL | NULL | 공통env: POST SELL CANDLE FORCE MOCK (POST_SELL_CANDLE_FORCE_MOCK) | +| `WS_GAP_FILL_SKIP_INCOMPLETE_BUCKET` | `text` | NULL | NULL | 공통env: WS GAP FILL SKIP INCOMPLETE BUCKET (WS_GAP_FILL_SKIP_INCOMPLETE_BUCKET) | +| `WS_CANDLE_FREEZE_ON_CONFIRM` | `text` | NULL | NULL | 공통env: WS CANDLE FREEZE ON CONFIRM (WS_CANDLE_FREEZE_ON_CONFIRM) | +| `WS_CANDLE_FREEZE_DB_LOOKUP_CHUNK` | `text` | NULL | NULL | 공통env: WS CANDLE FREEZE DB LOOKUP CHUNK (WS_CANDLE_FREEZE_DB_LOOKUP_CHUNK) | +| `WS_GAP_FILL_GIVE_UP_SEC` | `text` | NULL | NULL | 공통env: WS GAP FILL GIVE UP 초 (WS_GAP_FILL_GIVE_UP_SEC) | +| `WS_GAP_FILL_EMPTY_CIRCUIT_WINDOW_SEC` | `text` | NULL | NULL | 공통env: WS GAP FILL EMPTY CIRCUIT 윈도우 초 (WS_GAP_FILL_EMPTY_CIRCUIT_WINDOW_SEC) | +| `WS_GAP_FILL_EMPTY_CIRCUIT_MAX` | `text` | NULL | NULL | 공통env: WS GAP FILL EMPTY CIRCUIT 최대 (WS_GAP_FILL_EMPTY_CIRCUIT_MAX) | +| `WS_GAP_FILL_EMPTY_CIRCUIT_PAUSE_SEC` | `text` | NULL | NULL | 공통env: WS GAP FILL EMPTY CIRCUIT PAUSE 초 (WS_GAP_FILL_EMPTY_CIRCUIT_PAUSE_SEC) | +| `WS_GAP_FILL_EMPTY_LOG_SEC` | `text` | NULL | NULL | 공통env: WS GAP FILL EMPTY LOG 초 (WS_GAP_FILL_EMPTY_LOG_SEC) | +| `LS_APP_KEY_REAL` | `text` | NULL | NULL | 공통env: LS APP KEY REAL (LS_APP_KEY_REAL) | +| `LS_APP_SECRET_REAL` | `text` | NULL | NULL | 공통env: LS APP 초 RET REAL (LS_APP_SECRET_REAL) | +| `LS_APP_KEY_MOCK` | `text` | NULL | NULL | 공통env: LS APP KEY MOCK (LS_APP_KEY_MOCK) | +| `LS_APP_SECRET_MOCK` | `text` | NULL | NULL | 공통env: LS APP 초 RET MOCK (LS_APP_SECRET_MOCK) | +| `FORCE_BUY_TEST_CODE` | `text` | NULL | NULL | 공통env: 강제매수테스트 TEST CODE (FORCE_BUY_TEST_CODE) | +| `KIS_WS_INVALID_APPROVAL_REISSUE_AFTER` | `text` | NULL | NULL | invalid approval 연속N회 후 응급재발급 허용. 6h가드우회조건 | +| `KIS_WS_INVALID_APPROVAL_BYPASS_6H` | `text` | NULL | NULL | true면 invalid루프시 6h가드우회응급재발급. 남용금지 | +| `KIS_WS_EMERGENCY_REISSUE_MIN_SEC` | `text` | NULL | NULL | 응급approval재발급 최소간격(초). 기본=정상6h와동일. 하드캡 | +| `KIS_WS_KR_HOLD_START_HM` | `text` | NULL | NULL | 국내WS approval점유 시작 HHMM. 기본700. 갭·해외창에선 소켓양보(재발급없음) | +| `KIS_WS_KR_HOLD_END_HM` | `text` | NULL | NULL | 국내WS approval점유 종료 HHMM. 기본2000. kis_ws_session_windows | +| `KIS_WS_US_HOLD_START_HM` | `text` | NULL | NULL | 해외WS(HDFSCNT0) 점유 시작 HHMM. 기본2100. 국내와 동시점유금지(1키=1세션) | +| `KIS_WS_US_HOLD_END_HM` | `text` | NULL | NULL | 해외WS 점유 종료 HHMM. 기본600(자정넘김). 세션전환시 approval재발급금지 | + +
+DDL 원문 + +```sql +CREATE TABLE `env_config` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `created_at` varchar(30) NOT NULL, + `STOP_LOSS_PCT` text DEFAULT NULL, + `MAX_POSITION_PCT` text DEFAULT NULL, + `USE_SLOT_CAP` text DEFAULT NULL, + `SLOT_CAP_PCT` text DEFAULT NULL, + `MAX_STOCKS` text DEFAULT NULL, + `USE_KELLY` text DEFAULT NULL, + `RISK_PCT_PER_TRADE` text DEFAULT NULL, + `MIN_POSITION_AMOUNT` text DEFAULT NULL, + `USE_RISK_CHECK` text DEFAULT NULL, + `DAILY_STOP_LOSS_PCT` text DEFAULT NULL, + `CONSECUTIVE_LOSS_LIMIT` text DEFAULT NULL, + `DAILY_PROFIT_TARGET_KRW` text DEFAULT NULL, + `DAILY_PROFIT_TARGET_PCT` text DEFAULT NULL, + `DAILY_PROFIT_TARGET_BUDGET_KRW` text DEFAULT NULL, + `DAILY_PROFIT_HALT_NEW_BUYS` text DEFAULT NULL, + `DAILY_PROFIT_NOTIFY_MM` text DEFAULT NULL, + `USE_BAN_SYSTEM` text DEFAULT NULL, + `BAN_HOURS` text DEFAULT NULL, + `USE_STOCK_FILTER` text DEFAULT NULL, + `RSI_OVERHEAT_THRESHOLD` text DEFAULT NULL, + `MIN_RECOVERY_RATIO` text DEFAULT NULL, + `MAX_RECOVERY_RATIO` text DEFAULT NULL, + `USE_TWAP` text DEFAULT NULL, + `TWAP_MIN_SPLIT` text DEFAULT NULL, + `TWAP_MAX_SPLIT` text DEFAULT NULL, + `TWAP_MIN_DELAY` text DEFAULT NULL, + `TWAP_MAX_DELAY` text DEFAULT NULL, + `USE_ML_SIGNAL` text DEFAULT NULL, + `ML_MIN_PROBABILITY` text DEFAULT NULL, + `USE_NEWS_ANALYSIS` text DEFAULT NULL, + `NEWS_ANALYSIS_HOUR` text DEFAULT NULL, + `NEWS_MAX_COUNT` text DEFAULT NULL, + `USE_QUICK_PROFIT_PROTECTION` text DEFAULT NULL, + `HIGH_PRICE_CHASE_THRESHOLD` text DEFAULT NULL, + `MAX_DAILY_CHANGE_PCT` text DEFAULT NULL, + `MA20_MAX_ABOVE_PCT` text DEFAULT NULL, + `VOLUME_AVG_MULTIPLIER` text DEFAULT NULL, + `CANDLE_OPEN_PRICE_BUFFER` text DEFAULT NULL, + `MIN_CANDLE_LEN_TAIL` text DEFAULT NULL, + `MIN_PRICE_TAIL` text DEFAULT NULL, + `INTRADAY_INVESTOR_NET_BUY_THRESHOLD` text DEFAULT NULL, + `SIZE_CLASS_LARGE_MIN` text DEFAULT NULL, + `SIZE_CLASS_MID_MIN` text DEFAULT NULL, + `SCAN_INVESTOR_NET_STRONG` text DEFAULT NULL, + `SCAN_INVESTOR_SCORE_STRONG` text DEFAULT NULL, + `SCAN_INVESTOR_SCORE_WEAK` text DEFAULT NULL, + `SCAN_INVESTOR_BONUS_STRONG` text DEFAULT NULL, + `SCAN_INVESTOR_BONUS_WEAK` text DEFAULT NULL, + `SCAN_VOLUME_BONUS_MIN` text DEFAULT NULL, + `SCAN_VOLUME_BONUS_POINT` text DEFAULT NULL, + `SCAN_EXEC_STRENGTH_HIGH` text DEFAULT NULL, + `SCAN_EXEC_STRENGTH_MID` text DEFAULT NULL, + `SCAN_EXEC_BONUS_HIGH` text DEFAULT NULL, + `SCAN_EXEC_BONUS_MID` text DEFAULT NULL, + `SCAN_SCORE_DROP_WEIGHT` text DEFAULT NULL, + `SCAN_SCORE_RECOVERY_WEIGHT` text DEFAULT NULL, + `MIN_SCORE_DISPLAY` text DEFAULT NULL, + `MM_TOP_N` text DEFAULT NULL, + `SLOT_MONEY_DEFAULT` text DEFAULT NULL, + `SLOT_BASE_AMOUNT_CAP` text DEFAULT NULL, + `SIZE_CLASS_SMALL_RATIO` text DEFAULT NULL, + `SIZE_CLASS_MID_RATIO` text DEFAULT NULL, + `SCAN_MIN_PREV_DAY_PCT` text DEFAULT NULL, + `UPDATE_UNIVERSE_MIN_CANDIDATES` text DEFAULT NULL, + `UPDATE_UNIVERSE_MIN_SCORE` text DEFAULT NULL, + `UPDATE_UNIVERSE_FALLBACK_TOP_N` text DEFAULT NULL, + `UPDATE_UNIVERSE_TOP_LOG` text DEFAULT NULL, + `UPDATE_UNIVERSE_TOP_N` text DEFAULT NULL, + `SCAN_INTERVAL_SEC` text DEFAULT NULL, + `USE_RANDOM_SPLIT` text DEFAULT NULL, + `FORCE_MARKET_OPEN` text DEFAULT NULL, + `FORCE_BUY_TEST` text DEFAULT NULL, + `TOTAL_DEPOSIT` text DEFAULT NULL, + `ROUND_TRIP_COST_PCT` text DEFAULT NULL, + `POP_NET_PCT` text DEFAULT NULL, + `LOCK_NET_PCT` text DEFAULT NULL, + `MAX_LOSS_PER_TRADE_KRW` text DEFAULT NULL, + `MIN_DROP_PCT_FOR_LOSS_CUT` text DEFAULT NULL, + `KIS_APP_KEY_REAL` text DEFAULT NULL, + `KIS_APP_SECRET_REAL` text DEFAULT NULL, + `KIS_APP_KEY_MOCK` text DEFAULT NULL, + `KIS_APP_SECRET_MOCK` text DEFAULT NULL, + `KIS_ACCOUNT_NO_REAL` text DEFAULT NULL, + `KIS_ACCOUNT_CODE_REAL` text DEFAULT NULL, + `KIS_ACCOUNT_NO_MOCK` text DEFAULT NULL, + `KIS_ACCOUNT_CODE_MOCK` text DEFAULT NULL, + `KIS_MOCK` text DEFAULT NULL, + `TAKE_PROFIT_PCT` text DEFAULT NULL, + `MIN_DROP_RATE` text DEFAULT NULL, + `MIN_RECOVERY_RATIO_SHORT` text DEFAULT NULL, + `QUICK_PROFIT_PROTECT_HOURS` text DEFAULT NULL, + `QUICK_PROFIT_MAX_RATIO` text DEFAULT NULL, + `QUICK_PROFIT_CURRENT_MIN` text DEFAULT NULL, + `MIN_HOLD_EARLY_TAKE_PCT` text DEFAULT NULL, + `MIN_HOLD_HIGH_PCT` text DEFAULT NULL, + `MIN_HOLD_DROP_FROM_HIGH` text DEFAULT NULL, + `POST_HOLD_TAKE_PCT` text DEFAULT NULL, + `POST_HOLD_DROP_FROM_HIGH` text DEFAULT NULL, + `MAX_PER` text DEFAULT NULL, + `MAX_PEG` text DEFAULT NULL, + `MIN_GROWTH_PCT` text DEFAULT NULL, + `DCA_INTERVALS` text DEFAULT NULL, + `DCA_AMOUNTS` text DEFAULT NULL, + `MM_SERVER_URL` text DEFAULT NULL, + `MM_BOT_TOKEN_` text DEFAULT NULL, + `MATTERMOST_CHANNEL` text DEFAULT NULL, + `GEMINI_API_KEY` text DEFAULT NULL, + `AI_JOURNAL_LINES` text DEFAULT NULL, + `ANTHROPIC_API_KEY` text DEFAULT NULL, + `CLAUDE_MODEL_ID` text DEFAULT NULL, + `CLAUDE_MAX_TOKENS` text DEFAULT NULL, + `MM_BUTLER_CHANNEL` text DEFAULT NULL, + `MM_DUAL_CHANNEL_ENABLED` text DEFAULT NULL, + `OPENROUTER_API_KEY` text DEFAULT NULL, + `OPENROUTER_MODEL_ID` text DEFAULT NULL, + `KIS_LONG_MM_CHANNEL` text DEFAULT NULL, + `LONG_DAILY_LOOKBACK_DAYS` text DEFAULT NULL, + `LONG_MA_SHORT_DAYS` text DEFAULT NULL, + `LONG_MA_LONG_DAYS` text DEFAULT NULL, + `LONG_REPORT_AM_HOUR` text DEFAULT NULL, + `LONG_REPORT_AM_MIN` text DEFAULT NULL, + `LONG_REPORT_PM_HOUR` text DEFAULT NULL, + `LONG_REPORT_PM_MIN` text DEFAULT NULL, + `LONG_NEWS_ENABLED` text DEFAULT NULL, + `LONG_NEWS_INTERVAL_MIN` text DEFAULT NULL, + `LONG_NEWS_ACTIVE_START_HOUR` text DEFAULT NULL, + `LONG_NEWS_ACTIVE_END_HOUR` text DEFAULT NULL, + `LONG_ANALYSIS_DELAY_MIN_SEC` text DEFAULT NULL, + `LONG_ANALYSIS_DELAY_MAX_SEC` text DEFAULT NULL, + `RSI_PERIOD` text DEFAULT NULL, + `KIS_WS_URL_REAL` text DEFAULT NULL, + `KIS_WS_URL_MOCK` text DEFAULT NULL, + `KIS_WS_MOCK_ENABLED` text DEFAULT NULL, + `REENTRY_COOLDOWN_SEC` text DEFAULT NULL, + `TIME_START` text DEFAULT NULL, + `TIME_END` text DEFAULT NULL, + `SELL_FAILURE_BACKOFF_SEC` text DEFAULT NULL, + `BROKER_HOLDINGS_CACHE_TTL_SEC` text DEFAULT NULL, + `GHOST_POSITION_COOLDOWN_SEC` text DEFAULT NULL, + `WS_TICK_SAVE_ENABLED` text DEFAULT NULL, + `WS_TICK_BUFFER_MAX_PER_CODE` text DEFAULT NULL, + `WS_TICK_DB_BATCH_SIZE` text DEFAULT NULL, + `WS_TICK_DB_FLUSH_SEC` text DEFAULT NULL, + `WS_TICK_WRITE_QUEUE_MAX` text DEFAULT NULL, + `WS_TICK_KEEP_DAYS` text DEFAULT NULL, + `WS_TICK_RECORD_SCOPE` text DEFAULT NULL, + `WS_TICK_DEFAULT_MARKET` text DEFAULT NULL, + `WS_TICK_DEFAULT_EXCHANGE` text DEFAULT NULL, + `WS_TICK_DEFAULT_CURRENCY` text DEFAULT NULL, + `WS_ORDERBOOK_SAVE_ENABLED` text DEFAULT NULL, + `WS_PROGRAM_SAVE_ENABLED` text DEFAULT NULL, + `WS_TRIGGER_SNAPSHOT_DB_INTERVAL_SEC` text DEFAULT NULL, + `WS_TRIGGER_SNAPSHOT_DB_BATCH_SIZE` text DEFAULT NULL, + `WS_TRIGGER_SNAPSHOT_DB_FLUSH_SEC` text DEFAULT NULL, + `WS_TRIGGER_SNAPSHOT_WRITE_QUEUE_MAX` text DEFAULT NULL, + `WS_ORDERBOOK_KEEP_DAYS` text DEFAULT NULL, + `WS_PROGRAM_KEEP_DAYS` text DEFAULT NULL, + `BACKTEST_USE_TRIGGER_SNAPSHOT_DB` text DEFAULT NULL, + `WHIPSAW_FILTER_ENABLED` text DEFAULT NULL, + `WHIPSAW_BACKTEST_OHLC_FALLBACK` text DEFAULT NULL, + `WHIPSAW_MODE` text DEFAULT NULL, + `WHIPSAW_SUBBAR_SEC` text DEFAULT NULL, + `WHIPSAW_LOOKBACK_SEC` text DEFAULT NULL, + `WHIPSAW_DIP_PCT` text DEFAULT NULL, + `WHIPSAW_RECOVERY_TOL_PCT` text DEFAULT NULL, + `WHIPSAW_MIN_SUBBARS` text DEFAULT NULL, + `WHIPSAW_TICK_BUFFER_LIMIT` text DEFAULT NULL, + `USE_KELLY_FORMULA` text DEFAULT NULL, + `KELLY_MULTIPLIER` text DEFAULT NULL, + `USE_MARKET_IOC` text DEFAULT NULL, + `KIS_PRICE_CACHE_TTL_SEC` text DEFAULT NULL, + `MIN_HOLD_AFTER_BUY_SEC` text DEFAULT NULL, + `MIN_HOLD_HOURS` text DEFAULT NULL, + `MAX_RECOVERY_RATIO_3M` text DEFAULT NULL, + `CANDIDATE_LIST_TOP_N_LIGHT` text DEFAULT NULL, + `SCAN_UNIVERSE_MAX_CODES` text DEFAULT NULL, + `KIS_APP_KEY` text DEFAULT NULL, + `KIS_APP_SECRET` text DEFAULT NULL, + `KIS_ACCOUNT_NO` text DEFAULT NULL, + `KIS_ACCOUNT_CODE` text DEFAULT NULL, + `GEMINI_MODEL_ID` text DEFAULT NULL, + `MM_BUTLER_POLL_SEC` text DEFAULT NULL, + `AI_SOURCE_MAX_CHARS` text DEFAULT NULL, + `FEE_RATE_PCT` text DEFAULT NULL, + `SELL_TAX_RATE_PCT` text DEFAULT NULL, + `KIWOOM_APP_KEY_REAL` text DEFAULT NULL, + `KIWOOM_APP_SECRET_REAL` text DEFAULT NULL, + `KIWOOM_APP_KEY_MOCK` text DEFAULT NULL, + `KIWOOM_APP_SECRET_MOCK` text DEFAULT NULL, + `KIWOOM_APP_KEY` text DEFAULT NULL, + `KIWOOM_APP_SECRET` text DEFAULT NULL, + `PERMANENT_WS_CODES` text DEFAULT NULL, + `USE_MARKET_REGIME_FILTER` text DEFAULT NULL, + `MARKET_REGIME_MIN_RSI` text DEFAULT NULL, + `USE_THEME_HEAT_FILTER` text DEFAULT NULL, + `THEME_HEAT_RSI_MAX` text DEFAULT NULL, + `STRATEGY_SAME_CODE_POLICY` text DEFAULT NULL, + `ORDER_CASH_PCT` text DEFAULT NULL, + `ORDER_CASH_FEE_BUFFER` text DEFAULT NULL, + `ORDER_CASH_DIVIDE_BY_MAX_STOCKS` text DEFAULT NULL, + `ACCOUNT_CASH_PERSIST_SEC` text DEFAULT NULL, + `BACKTEST_EXIT_CHECKS_PER_BAR` text DEFAULT NULL, + `RANKING_POLL_INTERVAL_SEC` text DEFAULT NULL, + `KIS_HTS_ID` text DEFAULT NULL, + `CONDITION_POLL_INTERVAL_SEC` text DEFAULT NULL, + `CONDITION_EXIT_GRACE_SEC` text DEFAULT NULL, + `UNIVERSE_HISTORY_SAVE` text DEFAULT NULL, + `WS_GAP_FILL_OFF_HOURS` text DEFAULT NULL, + `WS_GAP_FILL_LIMIT` text DEFAULT NULL, + `WS_TIMEFRAMES` text DEFAULT NULL, + `WS_GAP_FILL_KIS_FALLBACK` text DEFAULT NULL, + `MAX_BUY_AMOUNT_PER_STOCK` text DEFAULT NULL, + `KIS_MIN_INTERVAL_SEC` text DEFAULT NULL, + `KIS_REST_MAX_RETRIES` text DEFAULT NULL, + `KIS_REST_BACKOFF_CAP_SEC` text DEFAULT NULL, + `KIS_REST_TIMEOUT_SEC` text DEFAULT NULL, + `MARKET_GUARD_ENABLED` text DEFAULT NULL, + `MARKET_GUARD_5MIN_DROP_PCT` text DEFAULT NULL, + `MARKET_GUARD_DAILY_DROP_PCT` text DEFAULT NULL, + `MARKET_GUARD_RECOVERY_PCT` text DEFAULT NULL, + `MARKET_GUARD_INDEX_CODE` text DEFAULT NULL, + `MARKET_GUARD_POLL_SEC` text DEFAULT NULL, + `MARKET_GUARD_INDEX_CANDLE_KEEP_MIN` text DEFAULT NULL, + `MARKET_GUARD_PERSIST_STATE` text DEFAULT NULL, + `STOCK_SHARE_DENOM` text DEFAULT NULL, + `EXCLUDE_NON_STOCK` text DEFAULT NULL, + `WS_PROVIDER` text DEFAULT NULL, + `WS_SUBSCRIBE_KIS_MINIMAL` text DEFAULT NULL, + `WS_VALIDATION_INTERVAL_SEC` text DEFAULT NULL, + `WS_VALIDATION_DIFF_WARN_PCT` text DEFAULT NULL, + `KIWOOM_WS_FORCE_REAL` text DEFAULT NULL, + `KIWOOM_WS_MAX_SUBSCRIPTIONS` text DEFAULT NULL, + `KIWOOM_WS_REG_CHUNK_SIZE` text DEFAULT NULL, + `KIWOOM_WS_REG_GAP_SEC` text DEFAULT NULL, + `KIWOOM_WS_REG_DEBOUNCE_SEC` text DEFAULT NULL, + `KIWOOM_WS_ORDERBOOK_ENABLED` text DEFAULT NULL, + `KIWOOM_WS_PROGRAM_ENABLED` text DEFAULT NULL, + `ORDERBOOK_FILTER_ENABLED` text DEFAULT NULL, + `ORDERBOOK_MAX_SPREAD_PCT` text DEFAULT NULL, + `ORDERBOOK_ENTRY_BID_LEVELS` text DEFAULT NULL, + `ORDERBOOK_ENTRY_BID_DEPTH_MULT` text DEFAULT NULL, + `ORDERBOOK_ENTRY_ASK_MAX_MULT` text DEFAULT NULL, + `ORDERBOOK_MIN_BID_ASK_RATIO` text DEFAULT NULL, + `ORDERBOOK_BREAKOUT_ASK_WALL_MAX_QTY` text DEFAULT NULL, + `PROGRAM_FILTER_ENABLED` text DEFAULT NULL, + `PROGRAM_MIN_NET_BUY_QTY` text DEFAULT NULL, + `PROGRAM_MIN_NET_BUY_AMT` text DEFAULT NULL, + `PROGRAM_MAX_SELL_BUY_QTY_RATIO` text DEFAULT NULL, + `PROGRAM_MIN_NET_DELTA_QTY` text DEFAULT NULL, + `PROGRAM_SNAPSHOT_MAX_AGE_SEC` text DEFAULT NULL, + `STRICT_FILL_VERIFY` text DEFAULT NULL, + `ORDER_FILL_WAIT_SEC` text DEFAULT NULL, + `LIMIT_ORDER_FILL_WAIT_SEC` text DEFAULT NULL, + `PENDING_FILL_POLL_INTERVAL_SEC` text DEFAULT NULL, + `PENDING_FILL_POLL_SEC` text DEFAULT NULL, + `PENDING_FILL_MAX_AGE_SEC` text DEFAULT NULL, + `PENDING_BUY_MAX_AGE_SEC` text DEFAULT NULL, + `PENDING_SELL_MAX_AGE_SEC` text DEFAULT NULL, + `PENDING_SELL_STOP_MAX_AGE_SEC` text DEFAULT NULL, + `SELL_PENDING_REORDER_ON_EXPIRE` text DEFAULT NULL, + `AUTO_CANCEL_PARTIAL_BUY_REMAINDER` text DEFAULT NULL, + `WS_TRIGGER_EVAL_SAVE_ENABLED` text DEFAULT NULL, + `WS_TRIGGER_EVAL_DB_BATCH_SIZE` text DEFAULT NULL, + `WS_TRIGGER_EVAL_DB_FLUSH_SEC` text DEFAULT NULL, + `WS_TRIGGER_EVAL_WRITE_QUEUE_MAX` text DEFAULT NULL, + `BACKTEST_USE_TRIGGER_EVAL_SNAPSHOT` text DEFAULT NULL, + `DAILY_PROFIT_MODE` text DEFAULT NULL, + `DAILY_PROFIT_TRAIL_TIERS` text DEFAULT NULL, + `DAILY_PROFIT_TRAIL_DROP_PCT` text DEFAULT NULL, + `DAILY_PROFIT_TRAIL_ARM_KRW` text DEFAULT NULL, + `DAILY_PROFIT_TRAIL_ARM_PCT` text DEFAULT NULL, + `BUY_DEDUP_PENDING` text DEFAULT NULL, + `KIS_MIN_INTERVAL_SEC_MOCK` text DEFAULT NULL, + `KIS_MIN_INTERVAL_SEC_REAL` text DEFAULT NULL, + `PENDING_POLL_BATCH_FETCH` text DEFAULT NULL, + `SCAN_REJECT_SLEEP_MIN` text DEFAULT NULL, + `SCAN_REJECT_SLEEP_MAX` text DEFAULT NULL, + `SCAN_BUY_OK_SLEEP_MIN` text DEFAULT NULL, + `SCAN_BUY_OK_SLEEP_MAX` text DEFAULT NULL, + `SCAN_BUY_FAIL_SLEEP_MIN` text DEFAULT NULL, + `SCAN_BUY_FAIL_SLEEP_MAX` text DEFAULT NULL, + `STRATEGY_LOOP_SLEEP_MIN` text DEFAULT NULL, + `STRATEGY_LOOP_SLEEP_MAX` text DEFAULT NULL, + `BALANCE_MAX_PAGES` text DEFAULT NULL, + `KIS_SYSTEM_MM_CHANNEL` text DEFAULT NULL, + `KIS_LIFECYCLE_MM_CHANNEL` text DEFAULT NULL, + `BULK_SELL_INTERVAL_SEC` text DEFAULT NULL, + `MANUAL_HOLD_CODES` text DEFAULT NULL, + `ORPHAN_RECONCILE_ENABLED` text DEFAULT NULL, + `WS_GAP_FILL_MAX_RETRIES` text DEFAULT NULL, + `WS_GAP_FILL_RETRY_DELAY_SEC` text DEFAULT NULL, + `WS_GAP_BULK_REFILL_DEBOUNCE_SEC` text DEFAULT NULL, + `WS_GAP_FILL_PRIORITY_TFS` text DEFAULT NULL, + `WS_GAP_FILL_TF_SLEEP_MIN_SEC` text DEFAULT NULL, + `WS_GAP_FILL_TF_SLEEP_MAX_SEC` text DEFAULT NULL, + `WS_GAP_FILL_CODE_SLEEP_MIN_SEC` text DEFAULT NULL, + `WS_GAP_FILL_CODE_SLEEP_MAX_SEC` text DEFAULT NULL, + `WS_GAP_FILL_PHASE_PAUSE_SEC` text DEFAULT NULL, + `KIWOOM_CNSRREQ_GAP_MIN_SEC` text DEFAULT NULL, + `KIWOOM_CNSRREQ_GAP_MAX_SEC` text DEFAULT NULL, + `KIWOOM_CNSRREQ_MAX_RETRIES` text DEFAULT NULL, + `KIWOOM_CNSRREQ_RETRY_SEC` text DEFAULT NULL, + `WS_GAP_FILL_WORKERS` text DEFAULT NULL, + `KIS_WS_SUBSCRIBE_GAP_MIN_SEC` text DEFAULT NULL, + `KIS_WS_SUBSCRIBE_GAP_MAX_SEC` text DEFAULT NULL, + `KIS_WS_RECONNECT_SESSION_WAIT_SEC` text DEFAULT NULL, + `KIS_WS_RECONNECT_REFRESH_KEY` text DEFAULT NULL, + `KIS_WS_APPROVAL_MIN_REISSUE_SEC` text DEFAULT NULL, + `KIS_WS_INSTANT_DROP_SEC` text DEFAULT NULL, + `KIS_WS_INSTANT_DROP_MAX` text DEFAULT NULL, + `KIS_WS_INSTANT_DROP_COOLDOWN_SEC` text DEFAULT NULL, + `WS_CANDLE_RAM_BUFFER` text DEFAULT NULL, + `KIS_WS_APPROVAL_MAX_VALID_SEC` text DEFAULT NULL, + `ENV_MERGED_CACHE_TTL_SEC` text DEFAULT NULL, + `WS_ORDERBOOK_COLLECT_ENABLED` text DEFAULT NULL, + `WS_PROGRAM_COLLECT_ENABLED` text DEFAULT NULL, + `WS_CANDLE_FORCE_CONFIRM_ENABLED` text DEFAULT NULL, + `WS_CANDLE_FORCE_CONFIRM_GRACE_SEC` text DEFAULT NULL, + `WS_CANDLE_STALE_CHECK_INTERVAL_SEC` text DEFAULT NULL, + `ORPHAN_RECONCILE_PRE_EOD_ENABLED` text DEFAULT NULL, + `ORPHAN_RECONCILE_PRE_EOD_LEAD_MIN` text DEFAULT NULL, + `DUPLICATE_ORDER_FILL_RECOVERY_ENABLED` text DEFAULT NULL, + `DUPLICATE_ORDER_RECOVERY_WAIT_SEC` text DEFAULT NULL, + `INTRADAY_HOLDINGS_DRIFT_ENABLED` text DEFAULT NULL, + `INTRADAY_HOLDINGS_DRIFT_INTERVAL_SEC` text DEFAULT NULL, + `INTRADAY_HOLDINGS_DRIFT_AUTO_RECOVER` text DEFAULT NULL, + `GHOST_PURGE_ON_RECONCILE` text DEFAULT NULL, + `WS_GAP_ROLLUP_3M_FROM_1M` text DEFAULT NULL, + `WS_GAP_FILL_CANDIDATE_MODE` text DEFAULT NULL, + `WS_TICK_GRACE_SEC` text DEFAULT NULL, + `WS_TICK_GRACE_HEADROOM` text DEFAULT NULL, + `WS_TICK_PREFER_KIWOOM` text DEFAULT NULL, + `VOL_MULTIPLIER` text DEFAULT NULL, + `KIWOOM_CNSRCLR_BEFORE_REQ` text DEFAULT NULL, + `KIWOOM_CNSRCLR_GAP_MIN_SEC` text DEFAULT NULL, + `KIWOOM_CNSRCLR_GAP_MAX_SEC` text DEFAULT NULL, + `KIWOOM_CNSRCLR_SETTLE_SEC` text DEFAULT NULL, + `NON_STOCK_PREF_CODE_SUFFIXES` text DEFAULT NULL, + `NON_STOCK_EXCLUDE_ALPHA_SUFFIX` text DEFAULT NULL, + `NON_STOCK_NAME_KEYWORDS` text DEFAULT NULL, + `ERROR_WATCH_ENABLED` text DEFAULT NULL, + `ERROR_WATCH_UNIT` text DEFAULT NULL, + `ERROR_WATCH_MM_CHANNEL` text DEFAULT NULL, + `ERROR_WATCH_COOLDOWN_SEC` text DEFAULT NULL, + `ERROR_WATCH_CONTEXT_LINES` text DEFAULT NULL, + `ERROR_WATCH_TRACEBACK_EXTRA_LINES` text DEFAULT NULL, + `ERROR_WATCH_HEALTH_CHECK_SEC` text DEFAULT NULL, + `ERROR_WATCH_GLOBAL_MIN_GAP_SEC` text DEFAULT NULL, + `ERROR_WATCH_MATCH_REGEX` text DEFAULT NULL, + `ERROR_WATCH_IGNORE_REGEX` text DEFAULT NULL, + `ERROR_WATCH_MM_JITTER` text DEFAULT NULL, + `ERROR_WATCH_STARTUP_NOTIFY` text DEFAULT NULL, + `POST_SELL_CANDLE_BACKFILL` text DEFAULT NULL, + `POST_SELL_CANDLE_ROLLUP_3M` text DEFAULT NULL, + `POST_SELL_CANDLE_MAX_BARS` text DEFAULT NULL, + `POST_SELL_CANDLE_SLEEP_MIN_SEC` text DEFAULT NULL, + `POST_SELL_CANDLE_SLEEP_MAX_SEC` text DEFAULT NULL, + `POST_SELL_CANDLE_FORCE_MOCK` text DEFAULT NULL, + `WS_GAP_FILL_SKIP_INCOMPLETE_BUCKET` text DEFAULT NULL, + `WS_CANDLE_FREEZE_ON_CONFIRM` text DEFAULT NULL, + `WS_CANDLE_FREEZE_DB_LOOKUP_CHUNK` text DEFAULT NULL, + `WS_GAP_FILL_GIVE_UP_SEC` text DEFAULT NULL, + `WS_GAP_FILL_EMPTY_CIRCUIT_WINDOW_SEC` text DEFAULT NULL, + `WS_GAP_FILL_EMPTY_CIRCUIT_MAX` text DEFAULT NULL, + `WS_GAP_FILL_EMPTY_CIRCUIT_PAUSE_SEC` text DEFAULT NULL, + `WS_GAP_FILL_EMPTY_LOG_SEC` text DEFAULT NULL, + `LS_APP_KEY_REAL` text DEFAULT NULL, + `LS_APP_SECRET_REAL` text DEFAULT NULL, + `LS_APP_KEY_MOCK` text DEFAULT NULL, + `LS_APP_SECRET_MOCK` text DEFAULT NULL, + `FORCE_BUY_TEST_CODE` text DEFAULT NULL, + `KIS_WS_INVALID_APPROVAL_REISSUE_AFTER` text DEFAULT NULL, + `KIS_WS_INVALID_APPROVAL_BYPASS_6H` text DEFAULT NULL, + `KIS_WS_EMERGENCY_REISSUE_MIN_SEC` text DEFAULT NULL, + `KIS_WS_KR_HOLD_START_HM` text DEFAULT NULL COMMENT '국내WS approval점유 시작 HHMM. 기본700. 갭·해외창에선 소켓양보(재발급없음)', + `KIS_WS_KR_HOLD_END_HM` text DEFAULT NULL COMMENT '국내WS approval점유 종료 HHMM. 기본2000. kis_ws_session_windows', + `KIS_WS_US_HOLD_START_HM` text DEFAULT NULL COMMENT '해외WS(HDFSCNT0) 점유 시작 HHMM. 기본2100. 국내와 동시점유금지(1키=1세션)', + `KIS_WS_US_HOLD_END_HM` text DEFAULT NULL COMMENT '해외WS 점유 종료 HHMM. 기본600(자정넘김). 세션전환시 approval재발급금지', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1060 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +``` + +
+ +#### `env_config_ext` + +**PK**: `env_key` + +> **테이블 COMMENT**: env_config 확장 KV. `LS_WS_*`·`LS_TOKEN_*`·`OVERSEAS_WS_ENABLED`·워치독/hold 등 다수 키를 **컬럼 추가 없이** `env_key` 행으로 보관. +> 참고: `docs/증권사_토큰_시세.md` · `scripts/sql/token_session_strategy_column_comments.sql` +> 대표 키(행): `LS_WS_WATCHDOG_SESSION_GATE` / `LS_WS_WATCHDOG_SESSION_*_HM` / `LS_WS_KR_HOLD_*` / `LS_WS_US_HOLD_*` / `LS_WS_SESSION_HOLD_ENABLED` / `OVERSEAS_WS_ENABLED` / `LS_TOKEN_*` + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `env_key` 🔑 | `varchar(80)` | NOT NULL | | 설정키. 예: LS_WS_WATCHDOG_SESSION_GATE, LS_WS_KR_HOLD_*, OVERSEAS_WS_ENABLED, LS_TOKEN_* | +| `env_value` | `text` | NOT NULL | | 설정값 문자열. bool/int도 텍스트저장 | +| `updated_at` | `varchar(30)` | NOT NULL | | 마지막갱신시각 | + +
+DDL 원문 + +```sql +CREATE TABLE `env_config_ext` ( + `env_key` varchar(80) NOT NULL COMMENT '설정키. 예: LS_WS_WATCHDOG_SESSION_GATE, LS_WS_KR_HOLD_*, OVERSEAS_WS_ENABLED, LS_TOKEN_*', + `env_value` text NOT NULL COMMENT '설정값 문자열. bool/int도 텍스트저장', + `updated_at` varchar(30) NOT NULL COMMENT '마지막갱신시각', + PRIMARY KEY (`env_key`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='env_config 확장 KV. LS_WS_*·LS_TOKEN_*·OVERSEAS_WS_ENABLED·워치독/hold 등 다수키. 컬럼추가없이 env_key행'; +``` + +
+ +#### `kv_store` + +**PK**: `k` + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `k` 🔑 | `varchar(100)` | NOT NULL | | 키 | +| `v` | `mediumtext` | NULL | NULL | 값 | + +
+DDL 원문 + +```sql +CREATE TABLE `kv_store` ( + `k` varchar(100) NOT NULL, + `v` mediumtext DEFAULT NULL, + PRIMARY KEY (`k`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +### 📡 실시간 마켓 데이터 (WebSocket / Market Data) + +#### `ws_candles` + +**PK**: `id` | AUTO_INCREMENT=55175852 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `int(11)` | NOT NULL | | 분봉: PK · AUTO_INCREMENT | +| `code` | `varchar(20)` | NOT NULL | | 분봉: 종목코드 (KR 6자리 / US 티커) | +| `market` | `varchar(8)` | NOT NULL | KR | KR/US | +| `timeframe` | `tinyint(4)` | NOT NULL | | 분봉 주기(분) | +| `candle_time` | `varchar(12)` | NOT NULL | | 분봉: 봉 시각 YYYYMMDDHHMM | +| `open` | `double` | NOT NULL | | 분봉: 시가 | +| `high` | `double` | NOT NULL | | 분봉: 고가 | +| `low` | `double` | NOT NULL | | 분봉: 저가 | +| `close` | `double` | NOT NULL | | 분봉: 종가 | +| `volume` | `bigint(20)` | NOT NULL | 0 | 분봉: 거래량(체결량/봉거래량) | +| `rsi_2` | `double` | NULL | NULL | RSI(2) materialized | +| `rsi_3` | `double` | NULL | NULL | RSI(3) materialized | +| `rsi_5` | `double` | NULL | NULL | RSI(5) materialized | +| `is_confirmed` | `tinyint(4)` | NOT NULL | 1 | 분봉: 봉 확정 여부 (1=확정) | +| `source` | `varchar(10)` | NOT NULL | ws | 분봉: 데이터 소스 (kis/kiwoom/ls 등) | +| `updated_at` | `varchar(30)` | NOT NULL | | 분봉: 마지막 갱신 시각 | +| `holding_peak` | `double` | NULL | NULL COMMENT | 보유 중 트레일 고점(WS 틱 max_price 스냅샷) | +| `rsi_14` | `double` | NULL | NULL COMMENT | RSI(14) materialized | +| `ema_9` | `double` | NULL | NULL COMMENT | EMA(9) materialized | +| `ema_21` | `double` | NULL | NULL COMMENT | EMA(21) materialized | +| `rsi_7` | `double` | NULL | NULL COMMENT | RSI(7) materialized | +| `rsi_21` | `double` | NULL | NULL COMMENT | RSI(21) materialized | +| `ema_5` | `double` | NULL | NULL COMMENT | EMA(5) materialized | +| `ema_12` | `double` | NULL | NULL COMMENT | EMA(12) materialized | +| `ema_15` | `double` | NULL | NULL COMMENT | EMA(15) materialized | +| `ema_34` | `double` | NULL | NULL COMMENT | EMA(34) materialized | + +**인덱스**: +- `uq_candle`: (`code`, `timeframe`, `candle_time`) +- `idx_tf_time`: (`timeframe`, `candle_time`) +- `idx_ws_candles_market`: (`market`, `code`, `timeframe`, `candle_time`) + +
+DDL 원문 + +```sql +CREATE TABLE `ws_candles` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `code` varchar(20) NOT NULL, + `market` varchar(8) NOT NULL DEFAULT 'KR' COMMENT 'KR|US', + `timeframe` tinyint(4) NOT NULL, + `candle_time` varchar(12) NOT NULL, + `open` double NOT NULL, + `high` double NOT NULL, + `low` double NOT NULL, + `close` double NOT NULL, + `volume` bigint(20) NOT NULL DEFAULT 0, + `rsi_2` double DEFAULT NULL, + `rsi_3` double DEFAULT NULL, + `rsi_5` double DEFAULT NULL, + `is_confirmed` tinyint(4) NOT NULL DEFAULT 1, + `source` varchar(10) NOT NULL DEFAULT 'ws', + `updated_at` varchar(30) NOT NULL, + `holding_peak` double DEFAULT NULL COMMENT '보유 중 트레일 고점(WS 틱 max_price 스냅샷)', + `rsi_14` double DEFAULT NULL COMMENT 'RSI(14) materialized', + `ema_9` double DEFAULT NULL COMMENT 'EMA(9) materialized', + `ema_21` double DEFAULT NULL COMMENT 'EMA(21) materialized', + `rsi_7` double DEFAULT NULL COMMENT 'RSI(7) materialized', + `rsi_21` double DEFAULT NULL COMMENT 'RSI(21) materialized', + `ema_5` double DEFAULT NULL COMMENT 'EMA(5) materialized', + `ema_12` double DEFAULT NULL COMMENT 'EMA(12) materialized', + `ema_15` double DEFAULT NULL COMMENT 'EMA(15) materialized', + `ema_34` double DEFAULT NULL COMMENT 'EMA(34) materialized', + PRIMARY KEY (`id`), + UNIQUE KEY `uq_candle` (`code`,`timeframe`,`candle_time`), + KEY `idx_tf_time` (`timeframe`,`candle_time`), + KEY `idx_ws_candles_market` (`market`,`code`,`timeframe`,`candle_time`) +) ENGINE=InnoDB AUTO_INCREMENT=55175852 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `ws_ticks` + +**PK**: `id` | AUTO_INCREMENT=54623571 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `bigint(20)` | NOT NULL | | 국장틱: PK · AUTO_INCREMENT | +| `market` | `varchar(8)` | NOT NULL | KR | 국장틱: 시장 구분 (KR/US 등) | +| `exchange` | `varchar(16)` | NULL | NULL | 국장틱: 거래소 (NASD/NYSE/AMEX/KRX 등) | +| `code` | `varchar(32)` | NOT NULL | | 국장틱: 종목코드 (KR 6자리 / US 티커) | +| `tick_time` | `varchar(14)` | NOT NULL | | 국장틱: 틱 시각 YYYYMMDDHHMMSS | +| `price` | `double` | NOT NULL | | 국장틱: 주문/체결 가격 | +| `volume` | `bigint(20)` | NOT NULL | 0 | 국장틱: 거래량(체결량/봉거래량) | +| `tick_seq` | `bigint(20)` | NULL | NULL | 국장틱: 틱 일련번호 | +| `session` | `varchar(8)` | NULL | NULL | 국장틱: 세션 태그 | +| `currency` | `varchar(8)` | NOT NULL | KRW | 국장틱: 통화 (KRW/USD) | +| `source` | `varchar(16)` | NOT NULL | kis | 국장틱: 데이터 소스 (kis/kiwoom/ls 등) | +| `recv_ts` | `varchar(30)` | NOT NULL | | 국장틱: 수신 시각 | + +**인덱스**: +- `idx_ws_ticks_lookup`: (`market`, `code`, `tick_time`) +- `idx_ws_ticks_recv`: (`recv_ts`) + +
+DDL 원문 + +```sql +CREATE TABLE `ws_ticks` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `market` varchar(8) NOT NULL DEFAULT 'KR', + `exchange` varchar(16) DEFAULT NULL, + `code` varchar(32) NOT NULL, + `tick_time` varchar(14) NOT NULL, + `price` double NOT NULL, + `volume` bigint(20) NOT NULL DEFAULT 0, + `tick_seq` bigint(20) DEFAULT NULL, + `session` varchar(8) DEFAULT NULL, + `currency` varchar(8) NOT NULL DEFAULT 'KRW', + `source` varchar(16) NOT NULL DEFAULT 'kis', + `recv_ts` varchar(30) NOT NULL, + PRIMARY KEY (`id`), + KEY `idx_ws_ticks_lookup` (`market`,`code`,`tick_time`), + KEY `idx_ws_ticks_recv` (`recv_ts`) +) ENGINE=InnoDB AUTO_INCREMENT=54623571 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `ws_ticks_us` + +**PK**: `id` | AUTO_INCREMENT=1458013 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `bigint(20)` | NOT NULL | | 해외틱: PK · AUTO_INCREMENT | +| `market` | `varchar(8)` | NOT NULL | US | 해외틱: 시장 구분 (KR/US 등) | +| `exchange` | `varchar(16)` | NULL | NULL | 해외틱: 거래소 (NASD/NYSE/AMEX/KRX 등) | +| `code` | `varchar(32)` | NOT NULL | | 해외틱: 종목코드 (KR 6자리 / US 티커) | +| `tick_time` | `varchar(14)` | NOT NULL | | 해외틱: 틱 시각 YYYYMMDDHHMMSS | +| `price` | `double` | NOT NULL | | 해외틱: 주문/체결 가격 | +| `volume` | `bigint(20)` | NOT NULL | 0 | 해외틱: 거래량(체결량/봉거래량) | +| `tick_seq` | `bigint(20)` | NULL | NULL | 해외틱: 틱 일련번호 | +| `session` | `varchar(8)` | NULL | NULL | 해외틱: 세션 태그 | +| `currency` | `varchar(8)` | NOT NULL | USD | 해외틱: 통화 (KRW/USD) | +| `source` | `varchar(16)` | NOT NULL | kis_us | 해외틱: 데이터 소스 (kis/kiwoom/ls 등) | +| `recv_ts` | `varchar(30)` | NOT NULL | | 해외틱: 수신 시각 | + +**인덱스**: +- `idx_ws_ticks_us_lookup`: (`market`, `code`, `tick_time`) +- `idx_ws_ticks_us_recv`: (`recv_ts`) + +
+DDL 원문 + +```sql +CREATE TABLE `ws_ticks_us` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `market` varchar(8) NOT NULL DEFAULT 'US', + `exchange` varchar(16) DEFAULT NULL, + `code` varchar(32) NOT NULL, + `tick_time` varchar(14) NOT NULL, + `price` double NOT NULL, + `volume` bigint(20) NOT NULL DEFAULT 0, + `tick_seq` bigint(20) DEFAULT NULL, + `session` varchar(8) DEFAULT NULL, + `currency` varchar(8) NOT NULL DEFAULT 'USD', + `source` varchar(16) NOT NULL DEFAULT 'kis_us', + `recv_ts` varchar(30) NOT NULL, + PRIMARY KEY (`id`), + KEY `idx_ws_ticks_us_lookup` (`market`,`code`,`tick_time`), + KEY `idx_ws_ticks_us_recv` (`recv_ts`) +) ENGINE=InnoDB AUTO_INCREMENT=1458013 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `ws_orderbook` + +**PK**: `id` | AUTO_INCREMENT=1777328 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `bigint(20)` | NOT NULL | | 호가스냅: PK · AUTO_INCREMENT | +| `market` | `varchar(8)` | NOT NULL | KR | 호가스냅: 시장 구분 (KR/US 등) | +| `code` | `varchar(32)` | NOT NULL | | 호가스냅: 종목코드 (KR 6자리 / US 티커) | +| `snap_time` | `varchar(14)` | NOT NULL | | 호가스냅: 스냅샷 시각 YYYYMMDDHHMMSS | +| `best_bid` | `bigint(20)` | NOT NULL | 0 | 호가스냅: 최우선 매수호가 | +| `best_ask` | `bigint(20)` | NOT NULL | 0 | 호가스냅: 최우선 매도호가 | +| `total_bid_qty` | `bigint(20)` | NOT NULL | 0 | 호가스냅: 총 매수잔량 | +| `total_ask_qty` | `bigint(20)` | NOT NULL | 0 | 호가스냅: 총 매도잔량 | +| `bid_qty_l3` | `bigint(20)` | NOT NULL | 0 | 호가스냅: 매수 1~3호가 잔량합 | +| `ask_qty_l3` | `bigint(20)` | NOT NULL | 0 | 호가스냅: 매도 1~3호가 잔량합 | +| `levels_json` | `mediumtext` | NULL | NULL | 호가스냅: 호가 단계 JSON | +| `source` | `varchar(16)` | NOT NULL | kiwoom_0d | 호가스냅: 데이터 소스 (kis/kiwoom/ls 등) | +| `recv_ts` | `varchar(30)` | NOT NULL | | 호가스냅: 수신 시각 | +| `strategy` | `varchar(16)` | NULL | NULL | 호가스냅: 전략 ID (SHORT/MOMENTUM/BREAKOUT/US_MOMENTUM 등) | +| `reject_code` | `varchar(64)` | NULL | NULL | 호가스냅: REJECT 종목코드 (reject_code) | +| `reject_msg` | `varchar(255)` | NULL | NULL | 호가스냅: REJECT ms G (reject_msg) | +| `eval_price` | `bigint(20)` | NOT NULL | 0 | 호가스냅: EVAL 가격 (eval_price) | + +**인덱스**: +- `idx_ws_orderbook_lookup`: (`market`, `code`, `snap_time`) +- `idx_ws_orderbook_recv`: (`recv_ts`) + +
+DDL 원문 + +```sql +CREATE TABLE `ws_orderbook` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `market` varchar(8) NOT NULL DEFAULT 'KR', + `code` varchar(32) NOT NULL, + `snap_time` varchar(14) NOT NULL, + `best_bid` bigint(20) NOT NULL DEFAULT 0, + `best_ask` bigint(20) NOT NULL DEFAULT 0, + `total_bid_qty` bigint(20) NOT NULL DEFAULT 0, + `total_ask_qty` bigint(20) NOT NULL DEFAULT 0, + `bid_qty_l3` bigint(20) NOT NULL DEFAULT 0, + `ask_qty_l3` bigint(20) NOT NULL DEFAULT 0, + `levels_json` mediumtext DEFAULT NULL, + `source` varchar(16) NOT NULL DEFAULT 'kiwoom_0d', + `recv_ts` varchar(30) NOT NULL, + `strategy` varchar(16) DEFAULT NULL, + `reject_code` varchar(64) DEFAULT NULL, + `reject_msg` varchar(255) DEFAULT NULL, + `eval_price` bigint(20) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `idx_ws_orderbook_lookup` (`market`,`code`,`snap_time`), + KEY `idx_ws_orderbook_recv` (`recv_ts`) +) ENGINE=InnoDB AUTO_INCREMENT=1777328 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `ws_program` + +**PK**: `id` | AUTO_INCREMENT=134369 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `bigint(20)` | NOT NULL | | 프로그램매매: PK · AUTO_INCREMENT | +| `market` | `varchar(8)` | NOT NULL | KR | 프로그램매매: 시장 구분 (KR/US 등) | +| `code` | `varchar(32)` | NOT NULL | | 프로그램매매: 종목코드 (KR 6자리 / US 티커) | +| `snap_time` | `varchar(14)` | NOT NULL | | 프로그램매매: 스냅샷 시각 YYYYMMDDHHMMSS | +| `buy_qty` | `bigint(20)` | NOT NULL | 0 | 프로그램매매: BUY 수량 (buy_qty) | +| `sell_qty` | `bigint(20)` | NOT NULL | 0 | 프로그램매매: SELL 수량 (sell_qty) | +| `net_qty` | `bigint(20)` | NOT NULL | 0 | 프로그램매매: NET 수량 (net_qty) | +| `buy_amt` | `bigint(20)` | NOT NULL | 0 | 프로그램매매: BUY AMT (buy_amt) | +| `sell_amt` | `bigint(20)` | NOT NULL | 0 | 프로그램매매: SELL AMT (sell_amt) | +| `net_amt` | `bigint(20)` | NOT NULL | 0 | 프로그램매매: NET AMT (net_amt) | +| `source` | `varchar(16)` | NOT NULL | kiwoom_0w | 프로그램매매: 데이터 소스 (kis/kiwoom/ls 등) | +| `recv_ts` | `varchar(30)` | NOT NULL | | 프로그램매매: 수신 시각 | +| `strategy` | `varchar(16)` | NULL | NULL | 프로그램매매: 전략 ID (SHORT/MOMENTUM/BREAKOUT/US_MOMENTUM 등) | +| `reject_code` | `varchar(64)` | NULL | NULL | 프로그램매매: REJECT 종목코드 (reject_code) | +| `reject_msg` | `varchar(255)` | NULL | NULL | 프로그램매매: REJECT ms G (reject_msg) | +| `eval_price` | `bigint(20)` | NOT NULL | 0 | 프로그램매매: EVAL 가격 (eval_price) | + +**인덱스**: +- `idx_ws_program_lookup`: (`market`, `code`, `snap_time`) +- `idx_ws_program_recv`: (`recv_ts`) + +
+DDL 원문 + +```sql +CREATE TABLE `ws_program` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `market` varchar(8) NOT NULL DEFAULT 'KR', + `code` varchar(32) NOT NULL, + `snap_time` varchar(14) NOT NULL, + `buy_qty` bigint(20) NOT NULL DEFAULT 0, + `sell_qty` bigint(20) NOT NULL DEFAULT 0, + `net_qty` bigint(20) NOT NULL DEFAULT 0, + `buy_amt` bigint(20) NOT NULL DEFAULT 0, + `sell_amt` bigint(20) NOT NULL DEFAULT 0, + `net_amt` bigint(20) NOT NULL DEFAULT 0, + `source` varchar(16) NOT NULL DEFAULT 'kiwoom_0w', + `recv_ts` varchar(30) NOT NULL, + `strategy` varchar(16) DEFAULT NULL, + `reject_code` varchar(64) DEFAULT NULL, + `reject_msg` varchar(255) DEFAULT NULL, + `eval_price` bigint(20) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `idx_ws_program_lookup` (`market`,`code`,`snap_time`), + KEY `idx_ws_program_recv` (`recv_ts`) +) ENGINE=InnoDB AUTO_INCREMENT=134369 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `ws_price_validation` + +**PK**: `id` | AUTO_INCREMENT=7869235 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `bigint(20)` | NOT NULL | | PK · AUTO_INCREMENT | +| `ts` | `datetime(3)` | NOT NULL | | ws_price_validation: TS (ts) | +| `code` | `varchar(20)` | NOT NULL | | 종목코드 (KR 6자리 / US 티커) | +| `kis_price` | `double` | NULL | NULL | ws_price_validation: KIS 가격 (kis_price) | +| `kiwoom_price` | `double` | NULL | NULL | ws_price_validation: KIWOOM 가격 (kiwoom_price) | +| `diff_pct` | `double` | NULL | NULL | ws_price_validation: DIFF % (diff_pct) | +| `kis_age_ms` | `int(11)` | NULL | NULL | ws_price_validation: KIS AGE ms (kis_age_ms) | +| `kiwoom_age_ms` | `int(11)` | NULL | NULL | ws_price_validation: KIWOOM AGE ms (kiwoom_age_ms) | + +**인덱스**: +- `idx_ts`: (`ts`) +- `idx_code`: (`code`) +- `idx_diff`: (`diff_pct`) + +
+DDL 원문 + +```sql +CREATE TABLE `ws_price_validation` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `ts` datetime(3) NOT NULL, + `code` varchar(20) NOT NULL, + `kis_price` double DEFAULT NULL, + `kiwoom_price` double DEFAULT NULL, + `diff_pct` double DEFAULT NULL, + `kis_age_ms` int(11) DEFAULT NULL, + `kiwoom_age_ms` int(11) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `idx_ts` (`ts`), + KEY `idx_code` (`code`), + KEY `idx_diff` (`diff_pct`) +) ENGINE=InnoDB AUTO_INCREMENT=7869235 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `ws_price_validation_ls` + +**PK**: `id` | AUTO_INCREMENT=403761 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `bigint(20)` | NOT NULL | | PK · AUTO_INCREMENT | +| `ts` | `datetime(3)` | NOT NULL | | ws_price_validation_ls: TS (ts) | +| `code` | `varchar(20)` | NOT NULL | | 종목코드 (KR 6자리 / US 티커) | +| `kis_price` | `double` | NULL | NULL | ws_price_validation_ls: KIS 가격 (kis_price) | +| `kiwoom_price` | `double` | NULL | NULL | ws_price_validation_ls: KIWOOM 가격 (kiwoom_price) | +| `ls_price` | `double` | NULL | NULL | ws_price_validation_ls: LS 가격 (ls_price) | +| `diff_kis_ls_pct` | `double` | NULL | NULL | ws_price_validation_ls: DIFF KIS LS % (diff_kis_ls_pct) | +| `diff_kw_ls_pct` | `double` | NULL | NULL | ws_price_validation_ls: DIFF KW LS % (diff_kw_ls_pct) | +| `kis_age_ms` | `int(11)` | NULL | NULL | ws_price_validation_ls: KIS AGE ms (kis_age_ms) | +| `kiwoom_age_ms` | `int(11)` | NULL | NULL | ws_price_validation_ls: KIWOOM AGE ms (kiwoom_age_ms) | +| `ls_age_ms` | `int(11)` | NULL | NULL | ws_price_validation_ls: LS AGE ms (ls_age_ms) | + +**인덱스**: +- `idx_lsval_ts`: (`ts`) +- `idx_lsval_code`: (`code`) +- `idx_lsval_diff`: (`diff_kis_ls_pct`) + +
+DDL 원문 + +```sql +CREATE TABLE `ws_price_validation_ls` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `ts` datetime(3) NOT NULL, + `code` varchar(20) NOT NULL, + `kis_price` double DEFAULT NULL, + `kiwoom_price` double DEFAULT NULL, + `ls_price` double DEFAULT NULL, + `diff_kis_ls_pct` double DEFAULT NULL, + `diff_kw_ls_pct` double DEFAULT NULL, + `kis_age_ms` int(11) DEFAULT NULL, + `kiwoom_age_ms` int(11) DEFAULT NULL, + `ls_age_ms` int(11) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `idx_lsval_ts` (`ts`), + KEY `idx_lsval_code` (`code`), + KEY `idx_lsval_diff` (`diff_kis_ls_pct`) +) ENGINE=InnoDB AUTO_INCREMENT=403761 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `ls_ws_candles` + +**PK**: `code`, `datetime`, `tf_min` + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `code` 🔑 | `varchar(20)` | NOT NULL | | LS분봉: 종목코드 (KR 6자리 / US 티커) | +| `datetime` 🔑 | `varchar(20)` | NOT NULL | | LS분봉: DATETIME (datetime) | +| `tf_min` 🔑 | `int(11)` | NOT NULL | 1 | LS분봉: 분봉 주기(분) | +| `open` | `double` | NOT NULL | | LS분봉: 시가 | +| `high` | `double` | NOT NULL | | LS분봉: 고가 | +| `low` | `double` | NOT NULL | | LS분봉: 저가 | +| `close` | `double` | NOT NULL | | LS분봉: 종가 | +| `volume` | `double` | NOT NULL | 0 | LS분봉: 거래량(체결량/봉거래량) | +| `tick_count` | `int(11)` | NOT NULL | 0 | LS분봉: TICK COUNT (tick_count) | +| `updated_at` | `datetime(3)` | NOT NULL | | LS분봉: 마지막 갱신 시각 | + +**인덱스**: +- `idx_ls_cndl_dt`: (`datetime`) + +
+DDL 원문 + +```sql +CREATE TABLE `ls_ws_candles` ( + `code` varchar(20) NOT NULL, + `datetime` varchar(20) NOT NULL, + `tf_min` int(11) NOT NULL DEFAULT 1, + `open` double NOT NULL, + `high` double NOT NULL, + `low` double NOT NULL, + `close` double NOT NULL, + `volume` double NOT NULL DEFAULT 0, + `tick_count` int(11) NOT NULL DEFAULT 0, + `updated_at` datetime(3) NOT NULL, + PRIMARY KEY (`code`,`datetime`,`tf_min`), + KEY `idx_ls_cndl_dt` (`datetime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `ls_ws_ticks` + +**PK**: `id` | AUTO_INCREMENT=8419294 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `bigint(20)` | NOT NULL | | LS틱: PK · AUTO_INCREMENT | +| `ts` | `datetime(3)` | NOT NULL | | LS틱: TS (ts) | +| `code` | `varchar(20)` | NOT NULL | | LS틱: 종목코드 (KR 6자리 / US 티커) | +| `price` | `double` | NOT NULL | | LS틱: 주문/체결 가격 | +| `volume` | `double` | NULL | NULL | LS틱: 거래량(체결량/봉거래량) | +| `tot_volume` | `double` | NULL | NULL | LS틱: TOT 거래량 (tot_volume) | +| `chetime` | `varchar(16)` | NULL | NULL | LS틱: CHETIME (chetime) | +| `tr_cd` | `varchar(8)` | NULL | NULL | LS틱: TR CD (tr_cd) | + +**인덱스**: +- `idx_ls_tick_ts`: (`ts`) +- `idx_ls_tick_code_ts`: (`code`, `ts`) + +
+DDL 원문 + +```sql +CREATE TABLE `ls_ws_ticks` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `ts` datetime(3) NOT NULL, + `code` varchar(20) NOT NULL, + `price` double NOT NULL, + `volume` double DEFAULT NULL, + `tot_volume` double DEFAULT NULL, + `chetime` varchar(16) DEFAULT NULL, + `tr_cd` varchar(8) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `idx_ls_tick_ts` (`ts`), + KEY `idx_ls_tick_code_ts` (`code`,`ts`) +) ENGINE=InnoDB AUTO_INCREMENT=8419294 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `ls_ws_orderbook` + +**PK**: `id` | AUTO_INCREMENT=23812 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `bigint(20)` | NOT NULL | | LS호가: PK · AUTO_INCREMENT | +| `market` | `varchar(8)` | NOT NULL | KR | LS호가: 시장 구분 (KR/US 등) | +| `code` | `varchar(32)` | NOT NULL | | LS호가: 종목코드 (KR 6자리 / US 티커) | +| `snap_time` | `varchar(14)` | NOT NULL | | LS호가: 스냅샷 시각 YYYYMMDDHHMMSS | +| `best_bid` | `bigint(20)` | NOT NULL | 0 | LS호가: 최우선 매수호가 | +| `best_ask` | `bigint(20)` | NOT NULL | 0 | LS호가: 최우선 매도호가 | +| `total_bid_qty` | `bigint(20)` | NOT NULL | 0 | LS호가: 총 매수잔량 | +| `total_ask_qty` | `bigint(20)` | NOT NULL | 0 | LS호가: 총 매도잔량 | +| `bid_qty_l3` | `bigint(20)` | NOT NULL | 0 | LS호가: 매수 1~3호가 잔량합 | +| `ask_qty_l3` | `bigint(20)` | NOT NULL | 0 | LS호가: 매도 1~3호가 잔량합 | +| `levels_json` | `mediumtext` | NULL | NULL | LS호가: 호가 단계 JSON | +| `source` | `varchar(16)` | NOT NULL | ls_uh1 | LS호가: 데이터 소스 (kis/kiwoom/ls 등) | +| `recv_ts` | `varchar(30)` | NOT NULL | | LS호가: 수신 시각 | + +**인덱스**: +- `idx_ls_ob_lookup`: (`market`, `code`, `snap_time`) +- `idx_ls_ob_recv`: (`recv_ts`) +- `idx_ls_ob_code`: (`code`) + +
+DDL 원문 + +```sql +CREATE TABLE `ls_ws_orderbook` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `market` varchar(8) NOT NULL DEFAULT 'KR', + `code` varchar(32) NOT NULL, + `snap_time` varchar(14) NOT NULL, + `best_bid` bigint(20) NOT NULL DEFAULT 0, + `best_ask` bigint(20) NOT NULL DEFAULT 0, + `total_bid_qty` bigint(20) NOT NULL DEFAULT 0, + `total_ask_qty` bigint(20) NOT NULL DEFAULT 0, + `bid_qty_l3` bigint(20) NOT NULL DEFAULT 0, + `ask_qty_l3` bigint(20) NOT NULL DEFAULT 0, + `levels_json` mediumtext DEFAULT NULL, + `source` varchar(16) NOT NULL DEFAULT 'ls_uh1', + `recv_ts` varchar(30) NOT NULL, + PRIMARY KEY (`id`), + KEY `idx_ls_ob_lookup` (`market`,`code`,`snap_time`), + KEY `idx_ls_ob_recv` (`recv_ts`), + KEY `idx_ls_ob_code` (`code`) +) ENGINE=InnoDB AUTO_INCREMENT=23812 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `ls_ws_vi` + +**PK**: `id` + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `bigint(20)` | NOT NULL | | LS VI: PK · AUTO_INCREMENT | +| `ts` | `datetime(3)` | NOT NULL | | LS VI: TS (ts) | +| `code` | `varchar(20)` | NOT NULL | | LS VI: 종목코드 (KR 6자리 / US 티커) | +| `event_time` | `varchar(16)` | NULL | NULL | LS VI: EVENT TIME (event_time) | +| `vi_gubun` | `varchar(4)` | NOT NULL | 0 | LS VI: VI GUBUN (vi_gubun) | +| `krx_vi_gubun` | `varchar(4)` | NULL | NULL | LS VI: KRX VI GUBUN (krx_vi_gubun) | +| `nxt_vi_gubun` | `varchar(4)` | NULL | NULL | LS VI: NXT VI GUBUN (nxt_vi_gubun) | +| `svi_recprice` | `double` | NULL | NULL | LS VI: SVI RECPRICE (svi_recprice) | +| `dvi_recprice` | `double` | NULL | NULL | LS VI: DVI RECPRICE (dvi_recprice) | +| `vi_trgprice` | `double` | NULL | NULL | LS VI: VI TRGPRICE (vi_trgprice) | +| `tr_cd` | `varchar(8)` | NULL | NULL | LS VI: TR CD (tr_cd) | +| `exchname` | `varchar(8)` | NULL | NULL | LS VI: EXCHNAME (exchname) | + +**인덱스**: +- `idx_ls_vi_ts`: (`ts`) +- `idx_ls_vi_code_ts`: (`code`, `ts`) +- `idx_ls_vi_gubun`: (`vi_gubun`) + +
+DDL 원문 + +```sql +CREATE TABLE `ls_ws_vi` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `ts` datetime(3) NOT NULL, + `code` varchar(20) NOT NULL, + `event_time` varchar(16) DEFAULT NULL, + `vi_gubun` varchar(4) NOT NULL DEFAULT '0', + `krx_vi_gubun` varchar(4) DEFAULT NULL, + `nxt_vi_gubun` varchar(4) DEFAULT NULL, + `svi_recprice` double DEFAULT NULL, + `dvi_recprice` double DEFAULT NULL, + `vi_trgprice` double DEFAULT NULL, + `tr_cd` varchar(8) DEFAULT NULL, + `exchname` varchar(8) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `idx_ls_vi_ts` (`ts`), + KEY `idx_ls_vi_code_ts` (`code`,`ts`), + KEY `idx_ls_vi_gubun` (`vi_gubun`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +### 🔍 종목 스캔 / 후보 (Universe & Candidates) + +#### `target_candidates` + +**PK**: `code` + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `code` 🔑 | `varchar(20)` | NOT NULL | | 현재후보: 종목코드 (KR 6자리 / US 티커) | +| `name` | `varchar(100)` | NOT NULL | | 현재후보: 종목명 | +| `score` | `double` | NOT NULL | | 현재후보: 점수/랭크 | +| `price` | `double` | NOT NULL | | 현재후보: 주문/체결 가격 | +| `scan_time` | `varchar(30)` | NOT NULL | | 현재후보: 스캔 시각 | +| `updated_at` | `varchar(30)` | NOT NULL | | 현재후보: 마지막 갱신 시각 | +| `market` | `char(1)` | NULL | Q | 현재후보: 시장 구분 (KR/US 등) | +| `sector` | `varchar(100)` | NULL | NULL | 현재후보: 섹터 | +| `theme` | `varchar(100)` | NULL | NULL | 현재후보: 테마 | + +
+DDL 원문 + +```sql +CREATE TABLE `target_candidates` ( + `code` varchar(20) NOT NULL, + `name` varchar(100) NOT NULL, + `score` double NOT NULL, + `price` double NOT NULL, + `scan_time` varchar(30) NOT NULL, + `updated_at` varchar(30) NOT NULL, + `market` char(1) DEFAULT 'Q', + `sector` varchar(100) DEFAULT NULL, + `theme` varchar(100) DEFAULT NULL, + PRIMARY KEY (`code`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `target_candidates_history` + +**PK**: `id` | AUTO_INCREMENT=3718695 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `int(11)` | NOT NULL | | 후보이력: PK · AUTO_INCREMENT | +| `slot_key` | `varchar(12)` | NOT NULL | | 후보이력: 슬롯/히스토리 키 | +| `scan_time` | `varchar(30)` | NOT NULL | | 후보이력: 스캔 시각 | +| `code` | `varchar(20)` | NOT NULL | | 후보이력: 종목코드 (KR 6자리 / US 티커) | +| `name` | `varchar(100)` | NOT NULL | | 후보이력: 종목명 | +| `score` | `double` | NOT NULL | 0 | 후보이력: 점수/랭크 | +| `price` | `double` | NOT NULL | 0 | 후보이력: 주문/체결 가격 | +| `market` | `char(1)` | NULL | Q | 후보이력: 시장 구분 (KR/US 등) | +| `sector` | `varchar(100)` | NULL | NULL | 후보이력: 섹터 | +| `theme` | `varchar(100)` | NULL | NULL | 후보이력: 테마 | +| `strategy_id` | `varchar(30)` | NULL | NULL | 후보이력: 전략 ID | +| `event_time` | `varchar(19)` | NULL | NULL | 후보이력: EVENT TIME (event_time) | + +**인덱스**: +- `idx_slot`: (`slot_key`) +- `idx_scan`: (`scan_time`) +- `idx_strategy_event`: (`strategy_id`, `event_time`) + +
+DDL 원문 + +```sql +CREATE TABLE `target_candidates_history` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `slot_key` varchar(12) NOT NULL, + `scan_time` varchar(30) NOT NULL, + `code` varchar(20) NOT NULL, + `name` varchar(100) NOT NULL DEFAULT '', + `score` double NOT NULL DEFAULT 0, + `price` double NOT NULL DEFAULT 0, + `market` char(1) DEFAULT 'Q', + `sector` varchar(100) DEFAULT NULL, + `theme` varchar(100) DEFAULT NULL, + `strategy_id` varchar(30) DEFAULT NULL, + `event_time` varchar(19) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `idx_slot` (`slot_key`), + KEY `idx_scan` (`scan_time`), + KEY `idx_strategy_event` (`strategy_id`,`event_time`) +) ENGINE=InnoDB AUTO_INCREMENT=3718695 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `condition_job_events` + +조건검색 **진입/재진입/이탈** 1이벤트 1행 (스냅샷 테이블과 분리). +공통 플래그 맵: `kis_trader/network/condition_common.py` (`JOB_KO`). + +| 컬럼 | 비고 | +|------|------| +| `job_flag` | LS `N`/`R`/`O` · 키움 `I`/`D` | +| `job_ko` | 진입/재진입/이탈 | +| `broker` | `ls` / `kiwoom` | +| `broker_time` | 증권사 HHMMSS(초) | +| `event_time` | 수신 시각(초) | +| `source` | `ls_afr` / `kiwoom_real` 등 | + +**LS 시드 참고**: 유니버스 목록 본체는 `ls_candidates_history`(t1859/flush). +t1859 공백(09000)은 history에 안 넣고, RAM0 시 `LS_T1859_EMPTY_RETRY_SEC` REST 재시드. + +#### `ls_candidates_history` + +**PK**: `id` | AUTO_INCREMENT=969087 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `bigint(20)` | NOT NULL | | LS후보이력: PK · AUTO_INCREMENT | +| `slot_key` | `varchar(12)` | NOT NULL | | LS후보이력: 슬롯/히스토리 키 | +| `scan_time` | `varchar(30)` | NOT NULL | | LS후보이력: 스캔 시각 | +| `code` | `varchar(20)` | NOT NULL | | LS후보이력: 종목코드 (KR 6자리 / US 티커) | +| `name` | `varchar(100)` | NOT NULL | | LS후보이력: 종목명 | +| `score` | `double` | NOT NULL | 0 | LS후보이력: 점수/랭크 | +| `price` | `double` | NOT NULL | 0 | LS후보이력: 주문/체결 가격 | +| `market` | `char(1)` | NULL | Q | LS후보이력: 시장 구분 (KR/US 등) | +| `sector` | `varchar(100)` | NULL | NULL | LS후보이력: 섹터 | +| `theme` | `varchar(100)` | NULL | NULL | LS후보이력: 테마 | +| `strategy_id` | `varchar(32)` | NOT NULL | | LS후보이력: 전략 ID | +| `event_time` | `varchar(30)` | NOT NULL | | LS후보이력: EVENT TIME (event_time) | +| `query_name` | `varchar(64)` | NOT NULL | | LS후보이력: QUERY 종목명 (query_name) | +| `query_index` | `varchar(32)` | NOT NULL | | LS후보이력: QUERY INDEX (query_index) | +| `source` | `varchar(16)` | NOT NULL | ls_afr | LS후보이력: 데이터 소스 (kis/kiwoom/ls 등) | + +**인덱스**: +- `idx_ls_cand_slot`: (`slot_key`) +- `idx_ls_cand_sid_evt`: (`strategy_id`, `event_time`) +- `idx_ls_cand_code`: (`code`) +- `idx_ls_cand_scan`: (`scan_time`) + +
+DDL 원문 + +```sql +CREATE TABLE `ls_candidates_history` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `slot_key` varchar(12) NOT NULL, + `scan_time` varchar(30) NOT NULL, + `code` varchar(20) NOT NULL, + `name` varchar(100) NOT NULL DEFAULT '', + `score` double NOT NULL DEFAULT 0, + `price` double NOT NULL DEFAULT 0, + `market` char(1) DEFAULT 'Q', + `sector` varchar(100) DEFAULT NULL, + `theme` varchar(100) DEFAULT NULL, + `strategy_id` varchar(32) NOT NULL DEFAULT '', + `event_time` varchar(30) NOT NULL DEFAULT '', + `query_name` varchar(64) NOT NULL DEFAULT '', + `query_index` varchar(32) NOT NULL DEFAULT '', + `source` varchar(16) NOT NULL DEFAULT 'ls_afr', + PRIMARY KEY (`id`), + KEY `idx_ls_cand_slot` (`slot_key`), + KEY `idx_ls_cand_sid_evt` (`strategy_id`,`event_time`), + KEY `idx_ls_cand_code` (`code`), + KEY `idx_ls_cand_scan` (`scan_time`) +) ENGINE=InnoDB AUTO_INCREMENT=969087 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `ls_universe_history` + +**PK**: `id` | AUTO_INCREMENT=845 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `bigint(20)` | NOT NULL | | LS유니버스이력: PK · AUTO_INCREMENT | +| `slot_key` | `varchar(12)` | NOT NULL | | LS유니버스이력: 슬롯/히스토리 키 | +| `scan_time` | `varchar(30)` | NOT NULL | | LS유니버스이력: 스캔 시각 | +| `code` | `varchar(20)` | NOT NULL | | LS유니버스이력: 종목코드 (KR 6자리 / US 티커) | +| `name` | `varchar(100)` | NOT NULL | | LS유니버스이력: 종목명 | +| `price` | `double` | NOT NULL | 0 | LS유니버스이력: 주문/체결 가격 | +| `tick_count` | `int(11)` | NOT NULL | 0 | LS유니버스이력: TICK COUNT (tick_count) | +| `candle_1m_count` | `int(11)` | NOT NULL | 0 | LS유니버스이력: CANDLE 1M COUNT (candle_1m_count) | +| `last_tick_ts` | `datetime(3)` | NULL | NULL | LS유니버스이력: LAST TICK TS (last_tick_ts) | +| `last_candle_dt` | `varchar(20)` | NULL | NULL | LS유니버스이력: LAST CANDLE DT (last_candle_dt) | +| `gap_1m_missing` | `int(11)` | NOT NULL | 0 | LS유니버스이력: GAP 1M MISSING (gap_1m_missing) | +| `source` | `varchar(16)` | NOT NULL | ls_ws | LS유니버스이력: 데이터 소스 (kis/kiwoom/ls 등) | +| `note` | `varchar(200)` | NULL | NULL | LS유니버스이력: 메모 | + +**인덱스**: +- `uq_ls_univ_slot_code`: (`slot_key`, `code`) +- `idx_ls_univ_slot`: (`slot_key`) +- `idx_ls_univ_code`: (`code`) +- `idx_ls_univ_scan`: (`scan_time`) + +
+DDL 원문 + +```sql +CREATE TABLE `ls_universe_history` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `slot_key` varchar(12) NOT NULL, + `scan_time` varchar(30) NOT NULL, + `code` varchar(20) NOT NULL, + `name` varchar(100) NOT NULL DEFAULT '', + `price` double NOT NULL DEFAULT 0, + `tick_count` int(11) NOT NULL DEFAULT 0, + `candle_1m_count` int(11) NOT NULL DEFAULT 0, + `last_tick_ts` datetime(3) DEFAULT NULL, + `last_candle_dt` varchar(20) DEFAULT NULL, + `gap_1m_missing` int(11) NOT NULL DEFAULT 0, + `source` varchar(16) NOT NULL DEFAULT 'ls_ws', + `note` varchar(200) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uq_ls_univ_slot_code` (`slot_key`,`code`), + KEY `idx_ls_univ_slot` (`slot_key`), + KEY `idx_ls_univ_code` (`code`), + KEY `idx_ls_univ_scan` (`scan_time`) +) ENGINE=InnoDB AUTO_INCREMENT=845 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `permanent_subscriptions` + +**PK**: `id` | AUTO_INCREMENT=16 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `bigint(20)` | NOT NULL | | 영구구독: PK · AUTO_INCREMENT | +| `code` | `varchar(16)` | NOT NULL | | 영구구독: 종목코드 (KR 6자리 / US 티커) | +| `market_type` | `varchar(8)` | NOT NULL | KR | 영구구독: 시장유형 (KR/US) | +| `exchange` | `varchar(16)` | NOT NULL | KRX | 영구구독: 거래소 (NASD/NYSE/AMEX/KRX 등) | +| `symbol` | `varchar(32)` | NOT NULL | | 영구구독: 심볼/티커 | +| `tf_min` | `int(11)` | NOT NULL | 60 | 영구구독: 분봉 주기(분) | +| `enabled` | `tinyint(4)` | NOT NULL | 1 | 영구구독: 활성 여부 (1=ON) | +| `note` | `varchar(100)` | NOT NULL | | 영구구독: 메모 | +| `created_at` | `datetime` | NULL | current_timestamp() | 영구구독: 생성 시각 | + +**인덱스**: +- `uq_perm_code`: (`code`) + +
+DDL 원문 + +```sql +CREATE TABLE `permanent_subscriptions` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `code` varchar(16) NOT NULL, + `market_type` varchar(8) NOT NULL DEFAULT 'KR', + `exchange` varchar(16) NOT NULL DEFAULT 'KRX', + `symbol` varchar(32) NOT NULL DEFAULT '', + `tf_min` int(11) NOT NULL DEFAULT 60, + `enabled` tinyint(4) NOT NULL DEFAULT 1, + `note` varchar(100) NOT NULL DEFAULT '', + `created_at` datetime DEFAULT current_timestamp(), + PRIMARY KEY (`id`), + UNIQUE KEY `uq_perm_code` (`code`) +) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='영구구독(국내 WS + 해외 WS) 종목'; +``` + +
+ +#### `stock_meta` + +**PK**: `code` + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `code` 🔑 | `varchar(20)` | NOT NULL | | 종목코드 (KR 6자리 / US 티커) | +| `name` | `varchar(100)` | NOT NULL | | 종목명 | +| `market` | `char(1)` | NOT NULL | Q | 시장 구분 (KR/US 등) | +| `sector_code` | `varchar(20)` | NULL | NULL | stock_meta: 초 TOR 종목코드 (sector_code) | +| `sector` | `varchar(100)` | NULL | NULL | 섹터 | +| `theme` | `varchar(100)` | NULL | NULL | 테마 | +| `theme_rank` | `tinyint(4)` | NULL | 3 | stock_meta: THEME RANK (theme_rank) | +| `updated_at` | `varchar(30)` | NOT NULL | | 마지막 갱신 시각 | + +
+DDL 원문 + +```sql +CREATE TABLE `stock_meta` ( + `code` varchar(20) NOT NULL, + `name` varchar(100) NOT NULL DEFAULT '', + `market` char(1) NOT NULL DEFAULT 'Q', + `sector_code` varchar(20) DEFAULT NULL, + `sector` varchar(100) DEFAULT NULL, + `theme` varchar(100) DEFAULT NULL, + `theme_rank` tinyint(4) DEFAULT 3, + `updated_at` varchar(30) NOT NULL, + PRIMARY KEY (`code`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `stock_share_meta` + +**PK**: `code` + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `code` 🔑 | `varchar(20)` | NOT NULL | | 종목코드 (KR 6자리 / US 티커) | +| `flo_stk` | `bigint(20)` | NOT NULL | 0 | stock_share_meta: FLO STK (flo_stk) | +| `dstr_stk` | `bigint(20)` | NOT NULL | 0 | stock_share_meta: DSTR STK (dstr_stk) | +| `dstr_rt` | `double` | NULL | NULL | stock_share_meta: DSTR RT (dstr_rt) | +| `source` | `varchar(16)` | NOT NULL | ka10001 | 데이터 소스 (kis/kiwoom/ls 등) | +| `updated_at` | `varchar(30)` | NOT NULL | | 마지막 갱신 시각 | + +**인덱스**: +- `idx_share_updated`: (`updated_at`) + +
+DDL 원문 + +```sql +CREATE TABLE `stock_share_meta` ( + `code` varchar(20) NOT NULL, + `flo_stk` bigint(20) NOT NULL DEFAULT 0, + `dstr_stk` bigint(20) NOT NULL DEFAULT 0, + `dstr_rt` double DEFAULT NULL, + `source` varchar(16) NOT NULL DEFAULT 'ka10001', + `updated_at` varchar(30) NOT NULL, + PRIMARY KEY (`code`), + KEY `idx_share_updated` (`updated_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +### 📈 전략별 종목 설정 (Per-Stock Config) + +#### `holding_stock_config` + +**PK**: `id` | AUTO_INCREMENT=17 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `bigint(20)` | NOT NULL | | PK · AUTO_INCREMENT | +| `code` | `varchar(10)` | NOT NULL | | 종목코드 (KR 6자리 / US 티커) | +| `name` | `varchar(50)` | NULL | | 종목명 | +| `created_at` | `datetime` | NULL | current_timestamp() | 생성 시각 | +| `rsi_period` | `float` | NULL | 14 | holding_stock_config: RSI PERIOD (rsi_period) | +| `rsi_buy1` | `float` | NULL | 35 | holding_stock_config: RSI BUY1 (rsi_buy1) | +| `rsi_buy2` | `float` | NULL | 30 | holding_stock_config: RSI BUY2 (rsi_buy2) | +| `rsi_buy3` | `float` | NULL | 25 | holding_stock_config: RSI BUY3 (rsi_buy3) | +| `rsi_sell` | `float` | NULL | 70 | holding_stock_config: RSI SELL (rsi_sell) | +| `take_profit_pct` | `float` | NULL | 4 | holding_stock_config: 익절 % (take_profit_pct) | +| `stop_loss_pct` | `float` | NULL | 10 | holding_stock_config: 손절 % (stop_loss_pct) | +| `buy1_ratio` | `float` | NULL | 30 | holding_stock_config: BUY1 RATIO (buy1_ratio) | +| `buy2_ratio` | `float` | NULL | 30 | holding_stock_config: BUY2 RATIO (buy2_ratio) | +| `buy3_ratio` | `float` | NULL | 40 | holding_stock_config: BUY3 RATIO (buy3_ratio) | +| `slot_money` | `float` | NULL | 3000000 | holding_stock_config: SLOT MONEY (slot_money) | +| `atr_period` | `float` | NULL | 14 | holding_stock_config: ATR PERIOD (atr_period) | +| `ath_drop_min_pct` | `float` | NULL | 0 COMMENT | ATH 대비 최소 낙폭% (0=비활성) | +| `year_drop_min_pct` | `float` | NULL | 0 COMMENT | 연도고점 대비 최소 낙폭% (0=비활성) | +| `w52_drop_min_pct` | `float` | NULL | 0 COMMENT | 52주고점 대비 최소 낙폭% (0=비활성) | +| `ma_fast` | `float` | NULL | 20 COMMENT | 단기 이동평균 기간 | +| `ma_slow` | `float` | NULL | 60 COMMENT | 장기 이동평균 기간 | +| `trail_stop_pct` | `float` | NULL | 8 COMMENT | 트레일링 스탑 % (0=비활성) | +| `trend_filter` | `float` | NULL | 1 COMMENT | 추세 필터 (1=사용/0=미사용) | +| `atr_mult` | `float` | NULL | 3 COMMENT | 샹들리에 엑시트 ATR 배수 | +| `updow_body_drop_min_pct` | `double` | NULL | NULL COMMENT | UPDOW 직전봉 몸통하락% 하한, NULL=env UPDOW_BODY_DROP_MIN_PCT | +| `updow_max_hold_bars` | `int(11)` | NULL | NULL COMMENT | UPDOW 최대 보유 봉 수, NULL=env UPDOW_MAX_HOLD_BARS | +| `updow_exit_on_green` | `double` | NULL | NULL COMMENT | UPDOW 양봉청산 1=ON 0=OFF, NULL=env UPDOW_EXIT_ON_GREEN | +| `updow_tf_min` | `int(11)` | NULL | NULL COMMENT | UPDOW 분봉 tf NULL·<1=env UPDOW_TF_MIN | +| `updow_regime_ma_bars` | `int(11)` | NULL | NULL COMMENT | UPDOW KOSPI프록시 MA봉수 NULL=env | +| `updow_atr_use_dynamic` | `double` | NULL | NULL COMMENT | UPDOW ATR 동적 청산 1=ON 0=OFF NULL=env UPDOW_ATR_USE_DYNAMIC | +| `updow_atr_period` | `int(11)` | NULL | NULL COMMENT | UPDOW ATR 기간(봉), NULL=env UPDOW_ATR_PERIOD | +| `updow_atr_sl_mult` | `double` | NULL | NULL COMMENT | UPDOW ATR 손절 배수, NULL=env UPDOW_ATR_SL_MULT | +| `updow_atr_tp_mult` | `double` | NULL | NULL COMMENT | UPDOW ATR 익절 배수, NULL=env UPDOW_ATR_TP_MULT | +| `updow_atr_sl_min_pct` | `double` | NULL | NULL | holding_stock_config: UPDOW ATR SL MIN % (updow_atr_sl_min_pct) | +| `updow_atr_sl_max_pct` | `double` | NULL | NULL | holding_stock_config: UPDOW ATR SL MAX % (updow_atr_sl_max_pct) | +| `updow_atr_tp_min_pct` | `double` | NULL | NULL | holding_stock_config: UPDOW ATR TP MIN % (updow_atr_tp_min_pct) | +| `updow_atr_tp_max_pct` | `double` | NULL | NULL | holding_stock_config: UPDOW ATR TP MAX % (updow_atr_tp_max_pct) | +| `updow_shoulder_min_high_pct` | `double` | NULL | NULL COMMENT | UPDOW 어깨 발동(%) NULL=env UPDOW_SHOULDER_MIN_HIGH_PCT | +| `updow_shoulder_cut_pct` | `double` | NULL | NULL COMMENT | UPDOW 어깨 폭(%) NULL=env UPDOW_SHOULDER_CUT_PCT | + +**인덱스**: +- `idx_holding_config_code`: (`code`) + +
+DDL 원문 + +```sql +CREATE TABLE `holding_stock_config` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `code` varchar(10) NOT NULL, + `name` varchar(50) DEFAULT '', + `created_at` datetime DEFAULT current_timestamp(), + `rsi_period` float DEFAULT 14, + `rsi_buy1` float DEFAULT 35, + `rsi_buy2` float DEFAULT 30, + `rsi_buy3` float DEFAULT 25, + `rsi_sell` float DEFAULT 70, + `take_profit_pct` float DEFAULT 4, + `stop_loss_pct` float DEFAULT 10, + `buy1_ratio` float DEFAULT 30, + `buy2_ratio` float DEFAULT 30, + `buy3_ratio` float DEFAULT 40, + `slot_money` float DEFAULT 3000000, + `atr_period` float DEFAULT 14, + `ath_drop_min_pct` float DEFAULT 0 COMMENT 'ATH 대비 최소 낙폭% (0=비활성)', + `year_drop_min_pct` float DEFAULT 0 COMMENT '연도고점 대비 최소 낙폭% (0=비활성)', + `w52_drop_min_pct` float DEFAULT 0 COMMENT '52주고점 대비 최소 낙폭% (0=비활성)', + `ma_fast` float DEFAULT 20 COMMENT '단기 이동평균 기간', + `ma_slow` float DEFAULT 60 COMMENT '장기 이동평균 기간', + `trail_stop_pct` float DEFAULT 8 COMMENT '트레일링 스탑 % (0=비활성)', + `trend_filter` float DEFAULT 1 COMMENT '추세 필터 (1=사용/0=미사용)', + `atr_mult` float DEFAULT 3 COMMENT '샹들리에 엑시트 ATR 배수', + `updow_body_drop_min_pct` double DEFAULT NULL COMMENT 'UPDOW 직전봉 몸통하락% 하한, NULL=env UPDOW_BODY_DROP_MIN_PCT', + `updow_max_hold_bars` int(11) DEFAULT NULL COMMENT 'UPDOW 최대 보유 봉 수, NULL=env UPDOW_MAX_HOLD_BARS', + `updow_exit_on_green` double DEFAULT NULL COMMENT 'UPDOW 양봉청산 1=ON 0=OFF, NULL=env UPDOW_EXIT_ON_GREEN', + `updow_tf_min` int(11) DEFAULT NULL COMMENT 'UPDOW 분봉 tf NULL·<1=env UPDOW_TF_MIN', + `updow_regime_ma_bars` int(11) DEFAULT NULL COMMENT 'UPDOW KOSPI프록시 MA봉수 NULL=env', + `updow_atr_use_dynamic` double DEFAULT NULL COMMENT 'UPDOW ATR 동적 청산 1=ON 0=OFF NULL=env UPDOW_ATR_USE_DYNAMIC', + `updow_atr_period` int(11) DEFAULT NULL COMMENT 'UPDOW ATR 기간(봉), NULL=env UPDOW_ATR_PERIOD', + `updow_atr_sl_mult` double DEFAULT NULL COMMENT 'UPDOW ATR 손절 배수, NULL=env UPDOW_ATR_SL_MULT', + `updow_atr_tp_mult` double DEFAULT NULL COMMENT 'UPDOW ATR 익절 배수, NULL=env UPDOW_ATR_TP_MULT', + `updow_atr_sl_min_pct` double DEFAULT NULL, + `updow_atr_sl_max_pct` double DEFAULT NULL, + `updow_atr_tp_min_pct` double DEFAULT NULL, + `updow_atr_tp_max_pct` double DEFAULT NULL, + `updow_shoulder_min_high_pct` double DEFAULT NULL COMMENT 'UPDOW 어깨 발동(%) NULL=env UPDOW_SHOULDER_MIN_HIGH_PCT', + `updow_shoulder_cut_pct` double DEFAULT NULL COMMENT 'UPDOW 어깨 폭(%) NULL=env UPDOW_SHOULDER_CUT_PCT', + PRIMARY KEY (`id`), + KEY `idx_holding_config_code` (`code`) +) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `holding_candles` + +**PK**: `id` | AUTO_INCREMENT=6077 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `bigint(20)` | NOT NULL | | PK · AUTO_INCREMENT | +| `code` | `varchar(10)` | NOT NULL | | 종목코드 (KR 6자리 / US 티커) | +| `candle_date` | `date` | NOT NULL | | holding_candles: CANDLE DATE (candle_date) | +| `open` | `float` | NOT NULL | 0 | 시가 | +| `high` | `float` | NOT NULL | 0 | 고가 | +| `low` | `float` | NOT NULL | 0 | 저가 | +| `close` | `float` | NOT NULL | 0 | 종가 | +| `volume` | `bigint(20)` | NOT NULL | 0 | 거래량(체결량/봉거래량) | +| `updated_at` | `datetime` | NULL | current_timestamp() ON UPDATE current_timestamp() | 마지막 갱신 시각 | +| `change_rate` | `float` | NOT NULL | 0 COMMENT | 전일대비등락률(%) | + +**인덱스**: +- `uq_holding_candle`: (`code`, `candle_date`) + +
+DDL 원문 + +```sql +CREATE TABLE `holding_candles` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `code` varchar(10) NOT NULL, + `candle_date` date NOT NULL, + `open` float NOT NULL DEFAULT 0, + `high` float NOT NULL DEFAULT 0, + `low` float NOT NULL DEFAULT 0, + `close` float NOT NULL DEFAULT 0, + `volume` bigint(20) NOT NULL DEFAULT 0, + `updated_at` datetime DEFAULT current_timestamp() ON UPDATE current_timestamp(), + `change_rate` float NOT NULL DEFAULT 0 COMMENT '전일대비등락률(%)', + PRIMARY KEY (`id`), + UNIQUE KEY `uq_holding_candle` (`code`,`candle_date`) +) ENGINE=InnoDB AUTO_INCREMENT=6077 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `holding_min_candles` + +**PK**: `id` | AUTO_INCREMENT=102318 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `bigint(20)` | NOT NULL | | PK · AUTO_INCREMENT | +| `code` | `varchar(10)` | NOT NULL | | 종목코드 (KR 6자리 / US 티커) | +| `candle_dt` | `datetime` | NOT NULL | | 60분봉 기준 시작 시간(KST) | +| `tf` | `smallint(6)` | NOT NULL | 60 COMMENT | 분봉 단위 (60=60분봉) | +| `open` | `float` | NOT NULL | 0 | 시가 | +| `high` | `float` | NOT NULL | 0 | 고가 | +| `low` | `float` | NOT NULL | 0 | 저가 | +| `close` | `float` | NOT NULL | 0 | 종가 | +| `volume` | `bigint(20)` | NOT NULL | 0 | 거래량(체결량/봉거래량) | +| `updated_at` | `datetime` | NULL | current_timestamp() ON UPDATE current_timestamp() | 마지막 갱신 시각 | + +**인덱스**: +- `uq_holding_min`: (`code`, `tf`, `candle_dt`) + +
+DDL 원문 + +```sql +CREATE TABLE `holding_min_candles` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `code` varchar(10) NOT NULL, + `candle_dt` datetime NOT NULL COMMENT '60분봉 기준 시작 시간(KST)', + `tf` smallint(6) NOT NULL DEFAULT 60 COMMENT '분봉 단위 (60=60분봉)', + `open` float NOT NULL DEFAULT 0, + `high` float NOT NULL DEFAULT 0, + `low` float NOT NULL DEFAULT 0, + `close` float NOT NULL DEFAULT 0, + `volume` bigint(20) NOT NULL DEFAULT 0, + `updated_at` datetime DEFAULT current_timestamp() ON UPDATE current_timestamp(), + PRIMARY KEY (`id`), + UNIQUE KEY `uq_holding_min` (`code`,`tf`,`candle_dt`) +) ENGINE=InnoDB AUTO_INCREMENT=102318 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `dbband_stock_config` + +**PK**: `id` | AUTO_INCREMENT=5 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `bigint(20)` | NOT NULL | | PK · AUTO_INCREMENT | +| `code` | `varchar(32)` | NOT NULL | | 종목코드 (KR 6자리 / US 티커) | +| `market_type` | `varchar(8)` | NOT NULL | KR | 시장유형 (KR/US) | +| `exchange` | `varchar(16)` | NOT NULL | KRX | 거래소 (NASD/NYSE/AMEX/KRX 등) | +| `symbol` | `varchar(32)` | NOT NULL | | 심볼/티커 | +| `name` | `varchar(50)` | NULL | | 종목명 | +| `created_at` | `datetime` | NULL | current_timestamp() | 생성 시각 | +| `tf_min` | `int(11)` | NOT NULL | 15 | 분봉 주기(분) | +| `bb_period` | `int(11)` | NOT NULL | 20 | dbband_stock_config: BB PERIOD (bb_period) | +| `bb_inner_std` | `double` | NOT NULL | 2 | dbband_stock_config: BB INNER STD (bb_inner_std) | +| `bb_outer_std` | `double` | NOT NULL | 3 | dbband_stock_config: BB OUTER STD (bb_outer_std) | +| `trend_ma_period` | `int(11)` | NOT NULL | 200 | dbband_stock_config: TREND MA PERIOD (trend_ma_period) | +| `use_trend_filter` | `double` | NOT NULL | 1 | dbband_stock_config: USE TREND FILTER (use_trend_filter) | +| `side_mode` | `varchar(16)` | NOT NULL | long_only | dbband_stock_config: 매매방향 MODE (side_mode) | +| `entry_valid_bars` | `int(11)` | NOT NULL | 3 | dbband_stock_config: ENTRY VALID BARS (entry_valid_bars) | +| `entry_mode` | `varchar(16)` | NOT NULL | break_high | dbband_stock_config: ENTRY MODE (entry_mode) | +| `stop_mode` | `varchar(16)` | NOT NULL | signal_low | dbband_stock_config: STOP MODE (stop_mode) | +| `stop_buffer_pct` | `double` | NOT NULL | 0.1 | dbband_stock_config: STOP BUFFER % (stop_buffer_pct) | +| `stop_loss_pct` | `double` | NOT NULL | 2 | dbband_stock_config: 손절 % (stop_loss_pct) | +| `tp_mode` | `varchar(16)` | NOT NULL | opposite_band | dbband_stock_config: TP MODE (tp_mode) | +| `take_profit_pct` | `double` | NOT NULL | 3 | dbband_stock_config: 익절 % (take_profit_pct) | +| `rr_ratio` | `double` | NOT NULL | 2 | dbband_stock_config: RR RATIO (rr_ratio) | +| `shoulder_min_high_pct` | `double` | NOT NULL | 0.3 | dbband_stock_config: 어깨 MIN HIGH % (shoulder_min_high_pct) | +| `shoulder_cut_pct` | `double` | NOT NULL | 0.2 | dbband_stock_config: 어깨 CUT % (shoulder_cut_pct) | +| `trail_pct` | `double` | NOT NULL | 0 | dbband_stock_config: 트레일 % (trail_pct) | +| `trail_arm_pct` | `double` | NOT NULL | 0 | dbband_stock_config: 트레일 ARM % (trail_arm_pct) | +| `max_hold_bars` | `int(11)` | NOT NULL | 16 | dbband_stock_config: MAX HOLD BARS (max_hold_bars) | +| `slot_money` | `double` | NOT NULL | 3000000 | dbband_stock_config: SLOT MONEY (slot_money) | +| `cooldown_min` | `double` | NOT NULL | 15 | dbband_stock_config: COOLDOWN MIN (cooldown_min) | +| `max_daily` | `int(11)` | NOT NULL | 3 | dbband_stock_config: MAX DAILY (max_daily) | +| `exit_mode` | `varchar(16)` | NOT NULL | classic | dbband_stock_config: EXIT MODE (exit_mode) | + +**인덱스**: +- `idx_dbband_stock_code`: (`code`) + +
+DDL 원문 + +```sql +CREATE TABLE `dbband_stock_config` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `code` varchar(32) NOT NULL, + `market_type` varchar(8) NOT NULL DEFAULT 'KR', + `exchange` varchar(16) NOT NULL DEFAULT 'KRX', + `symbol` varchar(32) NOT NULL DEFAULT '', + `name` varchar(50) DEFAULT '', + `created_at` datetime DEFAULT current_timestamp(), + `tf_min` int(11) NOT NULL DEFAULT 15, + `bb_period` int(11) NOT NULL DEFAULT 20, + `bb_inner_std` double NOT NULL DEFAULT 2, + `bb_outer_std` double NOT NULL DEFAULT 3, + `trend_ma_period` int(11) NOT NULL DEFAULT 200, + `use_trend_filter` double NOT NULL DEFAULT 1, + `side_mode` varchar(16) NOT NULL DEFAULT 'long_only', + `entry_valid_bars` int(11) NOT NULL DEFAULT 3, + `entry_mode` varchar(16) NOT NULL DEFAULT 'break_high', + `stop_mode` varchar(16) NOT NULL DEFAULT 'signal_low', + `stop_buffer_pct` double NOT NULL DEFAULT 0.1, + `stop_loss_pct` double NOT NULL DEFAULT 2, + `tp_mode` varchar(16) NOT NULL DEFAULT 'opposite_band', + `take_profit_pct` double NOT NULL DEFAULT 3, + `rr_ratio` double NOT NULL DEFAULT 2, + `shoulder_min_high_pct` double NOT NULL DEFAULT 0.3, + `shoulder_cut_pct` double NOT NULL DEFAULT 0.2, + `trail_pct` double NOT NULL DEFAULT 0, + `trail_arm_pct` double NOT NULL DEFAULT 0, + `max_hold_bars` int(11) NOT NULL DEFAULT 16, + `slot_money` double NOT NULL DEFAULT 3000000, + `cooldown_min` double NOT NULL DEFAULT 15, + `max_daily` int(11) NOT NULL DEFAULT 3, + `exit_mode` varchar(16) NOT NULL DEFAULT 'classic', + PRIMARY KEY (`id`), + KEY `idx_dbband_stock_code` (`code`) +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='DBBAND 더블볼린저 종목별 파라미터'; +``` + +
+ +#### `updow_stock_config` + +**PK**: `id` | AUTO_INCREMENT=17 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `bigint(20)` | NOT NULL | | PK · AUTO_INCREMENT | +| `code` | `varchar(10)` | NOT NULL | | 종목코드 (KR 6자리 / US 티커) | +| `market_type` | `varchar(8)` | NOT NULL | KR | 시장유형 (KR/US) | +| `exchange` | `varchar(16)` | NOT NULL | KRX | 거래소 (NASD/NYSE/AMEX/KRX 등) | +| `symbol` | `varchar(32)` | NOT NULL | | 심볼/티커 | +| `name` | `varchar(50)` | NULL | | 종목명 | +| `created_at` | `datetime` | NULL | current_timestamp() | 생성 시각 | +| `body_drop_min_pct` | `double` | NOT NULL | | updow_stock_config: BODY DROP MIN % (body_drop_min_pct) | +| `body_drop_max_pct` | `double` | NOT NULL | 0 | updow_stock_config: BODY DROP MAX % (body_drop_max_pct) | +| `tp_pct` | `double` | NOT NULL | | updow_stock_config: TP % (tp_pct) | +| `stop_loss_pct` | `double` | NOT NULL | | updow_stock_config: 손절 % (stop_loss_pct) | +| `max_hold_bars` | `int(11)` | NOT NULL | | updow_stock_config: MAX HOLD BARS (max_hold_bars) | +| `exit_on_green` | `double` | NOT NULL | 0 | updow_stock_config: EXIT ON GREEN (exit_on_green) | +| `tf_min` | `int(11)` | NOT NULL | 60 | 분봉 주기(분) | +| `slot_money` | `double` | NOT NULL | 3000000 | updow_stock_config: SLOT MONEY (slot_money) | +| `regime_ma_bars` | `int(11)` | NOT NULL | 0 | updow_stock_config: REGIME MA BARS (regime_ma_bars) | +| `regime_ma_ease_pct` | `double` | NOT NULL | 0 | updow_stock_config: REGIME MA EASE % (regime_ma_ease_pct) | +| `atr_use_dynamic` | `double` | NOT NULL | 0 | updow_stock_config: ATR USE DYNAMIC (atr_use_dynamic) | +| `atr_period` | `int(11)` | NOT NULL | 14 | updow_stock_config: ATR PERIOD (atr_period) | +| `atr_sl_mult` | `double` | NOT NULL | 2 | updow_stock_config: ATR SL MULT (atr_sl_mult) | +| `atr_tp_mult` | `double` | NOT NULL | 4 | updow_stock_config: ATR TP MULT (atr_tp_mult) | +| `atr_sl_min_pct` | `double` | NOT NULL | 0.8 | updow_stock_config: ATR SL MIN % (atr_sl_min_pct) | +| `atr_sl_max_pct` | `double` | NOT NULL | 6 | updow_stock_config: ATR SL MAX % (atr_sl_max_pct) | +| `atr_tp_min_pct` | `double` | NOT NULL | 1.5 | updow_stock_config: ATR TP MIN % (atr_tp_min_pct) | +| `atr_tp_max_pct` | `double` | NOT NULL | 12 | updow_stock_config: ATR TP MAX % (atr_tp_max_pct) | +| `shoulder_min_high_pct` | `double` | NOT NULL | 0.3 | updow_stock_config: 어깨 MIN HIGH % (shoulder_min_high_pct) | +| `shoulder_cut_pct` | `double` | NOT NULL | 0.2 | updow_stock_config: 어깨 CUT % (shoulder_cut_pct) | +| `limit_atr_mult` | `double` | NOT NULL | 1.5 | updow_stock_config: LIMIT ATR MULT (limit_atr_mult) | +| `entry_mode` | `varchar(16)` | NOT NULL | | updow_stock_config: ENTRY MODE (entry_mode) | + +**인덱스**: +- `idx_updow_stock_code`: (`code`) + +
+DDL 원문 + +```sql +CREATE TABLE `updow_stock_config` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `code` varchar(10) NOT NULL, + `market_type` varchar(8) NOT NULL DEFAULT 'KR', + `exchange` varchar(16) NOT NULL DEFAULT 'KRX', + `symbol` varchar(32) NOT NULL DEFAULT '', + `name` varchar(50) DEFAULT '', + `created_at` datetime DEFAULT current_timestamp(), + `body_drop_min_pct` double NOT NULL, + `body_drop_max_pct` double NOT NULL DEFAULT 0, + `tp_pct` double NOT NULL, + `stop_loss_pct` double NOT NULL, + `max_hold_bars` int(11) NOT NULL, + `exit_on_green` double NOT NULL DEFAULT 0, + `tf_min` int(11) NOT NULL DEFAULT 60, + `slot_money` double NOT NULL DEFAULT 3000000, + `regime_ma_bars` int(11) NOT NULL DEFAULT 0, + `regime_ma_ease_pct` double NOT NULL DEFAULT 0, + `atr_use_dynamic` double NOT NULL DEFAULT 0, + `atr_period` int(11) NOT NULL DEFAULT 14, + `atr_sl_mult` double NOT NULL DEFAULT 2, + `atr_tp_mult` double NOT NULL DEFAULT 4, + `atr_sl_min_pct` double NOT NULL DEFAULT 0.8, + `atr_sl_max_pct` double NOT NULL DEFAULT 6, + `atr_tp_min_pct` double NOT NULL DEFAULT 1.5, + `atr_tp_max_pct` double NOT NULL DEFAULT 12, + `shoulder_min_high_pct` double NOT NULL DEFAULT 0.3, + `shoulder_cut_pct` double NOT NULL DEFAULT 0.2, + `limit_atr_mult` double NOT NULL DEFAULT 1.5, + `entry_mode` varchar(16) NOT NULL DEFAULT '', + PRIMARY KEY (`id`), + KEY `idx_updow_stock_code` (`code`) +) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='UPDOW 하락매수 종목별 파라미터'; +``` + +
+ +#### `updown_box_stock_cfg` + +**PK**: `id` | AUTO_INCREMENT=4 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `bigint(20)` | NOT NULL | | PK · AUTO_INCREMENT | +| `code` | `varchar(20)` | NOT NULL | | 종목코드 (KR 6자리 / US 티커) | +| `name` | `varchar(50)` | NOT NULL | | 종목명 | +| `near_low_pct` | `double` | NULL | NULL | updown_box_stock_cfg: NEAR LOW % (near_low_pct) | +| `range_max_pct` | `double` | NULL | NULL | updown_box_stock_cfg: RANGE MAX % (range_max_pct) | +| `bb_bw_max` | `double` | NULL | NULL | updown_box_stock_cfg: BB BW MAX (bb_bw_max) | +| `ma_slope_tol_pct` | `double` | NULL | NULL | updown_box_stock_cfg: MA SLOPE TOL % (ma_slope_tol_pct) | +| `body_drop_min_pct` | `double` | NULL | NULL | updown_box_stock_cfg: BODY DROP MIN % (body_drop_min_pct) | +| `breach_buffer_pct` | `double` | NULL | NULL | updown_box_stock_cfg: BREACH BUFFER % (breach_buffer_pct) | +| `panic_from_high_pct` | `double` | NULL | NULL | updown_box_stock_cfg: PANIC FROM HIGH % (panic_from_high_pct) | +| `stop_loss_pct` | `double` | NULL | NULL | updown_box_stock_cfg: 손절 % (stop_loss_pct) | +| `tp_pct` | `double` | NULL | NULL | updown_box_stock_cfg: TP % (tp_pct) | +| `shoulder_min_high_pct` | `double` | NULL | NULL | updown_box_stock_cfg: 어깨 MIN HIGH % (shoulder_min_high_pct) | +| `shoulder_cut_pct` | `double` | NULL | NULL | updown_box_stock_cfg: 어깨 CUT % (shoulder_cut_pct) | +| `max_hold_bars` | `double` | NULL | NULL | updown_box_stock_cfg: MAX HOLD BARS (max_hold_bars) | +| `lookback_bars` | `double` | NULL | NULL | updown_box_stock_cfg: LOOKBACK BARS (lookback_bars) | +| `ratchet_tiers` | `varchar(64)` | NULL | NULL | updown_box_stock_cfg: 래칫 TIERS (ratchet_tiers) | +| `updated_at` | `datetime` | NOT NULL | current_timestamp() ON UPDATE current_timestamp() | 마지막 갱신 시각 | +| `breakout_follow` | `double` | NULL | NULL | updown_box_stock_cfg: BREAKOUT FOLLOW (breakout_follow) | + +**인덱스**: +- `uq_updown_box_stock_code`: (`code`) + +
+DDL 원문 + +```sql +CREATE TABLE `updown_box_stock_cfg` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `code` varchar(20) NOT NULL, + `name` varchar(50) NOT NULL DEFAULT '', + `near_low_pct` double DEFAULT NULL, + `range_max_pct` double DEFAULT NULL, + `bb_bw_max` double DEFAULT NULL, + `ma_slope_tol_pct` double DEFAULT NULL, + `body_drop_min_pct` double DEFAULT NULL, + `breach_buffer_pct` double DEFAULT NULL, + `panic_from_high_pct` double DEFAULT NULL, + `stop_loss_pct` double DEFAULT NULL, + `tp_pct` double DEFAULT NULL, + `shoulder_min_high_pct` double DEFAULT NULL, + `shoulder_cut_pct` double DEFAULT NULL, + `max_hold_bars` double DEFAULT NULL, + `lookback_bars` double DEFAULT NULL, + `ratchet_tiers` varchar(64) DEFAULT NULL, + `updated_at` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), + `breakout_follow` double DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uq_updown_box_stock_code` (`code`) +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='UPDOWN 박스권 종목별 파라미터 오버라이드(NULL=글로벌 상속)'; +``` + +
+ +#### `updown_watchlist` + +**PK**: `id` | AUTO_INCREMENT=5 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `bigint(20)` | NOT NULL | | PK · AUTO_INCREMENT | +| `code` | `varchar(20)` | NOT NULL | | 종목코드 (KR 6자리 / US 티커) | +| `name` | `varchar(50)` | NOT NULL | | 종목명 | +| `market_type` | `varchar(8)` | NOT NULL | KR | 시장유형 (KR/US) | +| `exchange` | `varchar(16)` | NOT NULL | KRX | 거래소 (NASD/NYSE/AMEX/KRX 등) | +| `source` | `varchar(16)` | NOT NULL | manual | 데이터 소스 (kis/kiwoom/ls 등) | +| `status` | `varchar(16)` | NOT NULL | active | 상태 | +| `box_low` | `double` | NOT NULL | 0 | updown_watchlist: BOX LOW (box_low) | +| `box_high` | `double` | NOT NULL | 0 | updown_watchlist: BOX HIGH (box_high) | +| `box_score` | `double` | NOT NULL | 0 | updown_watchlist: BOX SCORE (box_score) | +| `added_at` | `datetime` | NOT NULL | current_timestamp() | updown_watchlist: ADDED AT (added_at) | +| `last_seen_at` | `datetime` | NULL | NULL | updown_watchlist: LAST SEEN AT (last_seen_at) | +| `last_signal_at` | `datetime` | NULL | NULL | updown_watchlist: LAST SIGNAL AT (last_signal_at) | +| `removed_at` | `datetime` | NULL | NULL | updown_watchlist: REMOVED AT (removed_at) | +| `updated_at` | `datetime` | NOT NULL | current_timestamp() ON UPDATE current_timestamp() | 마지막 갱신 시각 | + +**인덱스**: +- `uq_updown_watch_code`: (`code`) +- `idx_updown_watch_status`: (`status`) + +
+DDL 원문 + +```sql +CREATE TABLE `updown_watchlist` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `code` varchar(20) NOT NULL, + `name` varchar(50) NOT NULL DEFAULT '', + `market_type` varchar(8) NOT NULL DEFAULT 'KR', + `exchange` varchar(16) NOT NULL DEFAULT 'KRX', + `source` varchar(16) NOT NULL DEFAULT 'manual', + `status` varchar(16) NOT NULL DEFAULT 'active', + `box_low` double NOT NULL DEFAULT 0, + `box_high` double NOT NULL DEFAULT 0, + `box_score` double NOT NULL DEFAULT 0, + `added_at` datetime NOT NULL DEFAULT current_timestamp(), + `last_seen_at` datetime DEFAULT NULL, + `last_signal_at` datetime DEFAULT NULL, + `removed_at` datetime DEFAULT NULL, + `updated_at` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), + PRIMARY KEY (`id`), + UNIQUE KEY `uq_updown_watch_code` (`code`), + KEY `idx_updown_watch_status` (`status`) +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='UPDOWN 박스권 sticky 관심종목(멤버십)'; +``` + +
+ +#### `us_momentum_stock_config` + +**PK**: `id` | AUTO_INCREMENT=11 + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `bigint(20)` | NOT NULL | | PK · AUTO_INCREMENT | +| `code` | `varchar(32)` | NOT NULL | | 종목코드 (KR 6자리 / US 티커) | +| `exchange` | `varchar(16)` | NOT NULL | NASD | 거래소 (NASD/NYSE/AMEX/KRX 등) | +| `symbol` | `varchar(32)` | NOT NULL | | 심볼/티커 | +| `name` | `varchar(64)` | NULL | | 종목명 | +| `stock_group` | `varchar(16)` | NOT NULL | STOCK | us_momentum_stock_config: STOCK GROUP (stock_group) | +| `created_at` | `datetime` | NULL | current_timestamp() | 생성 시각 | +| `updated_at` | `datetime` | NULL | current_timestamp() ON UPDATE current_timestamp() | 마지막 갱신 시각 | +| `sl_pct` | `double` | NULL | NULL | us_momentum_stock_config: SL % (sl_pct) | +| `tp_pct` | `double` | NULL | NULL | us_momentum_stock_config: TP % (tp_pct) | +| `tp_max_pct` | `double` | NULL | NULL | us_momentum_stock_config: TP MAX % (tp_max_pct) | +| `shoulder_min_high_pct` | `double` | NULL | NULL | us_momentum_stock_config: 어깨 MIN HIGH % (shoulder_min_high_pct) | +| `shoulder_cut_pct` | `double` | NULL | NULL | us_momentum_stock_config: 어깨 CUT % (shoulder_cut_pct) | +| `trail_pct` | `double` | NULL | NULL | us_momentum_stock_config: 트레일 % (trail_pct) | +| `trail_arm_pct` | `double` | NULL | NULL | us_momentum_stock_config: 트레일 ARM % (trail_arm_pct) | +| `ratchet_tiers` | `varchar(255)` | NULL | NULL | us_momentum_stock_config: 래칫 TIERS (ratchet_tiers) | +| `max_hold_bars` | `int(11)` | NULL | NULL | us_momentum_stock_config: MAX HOLD BARS (max_hold_bars) | +| `cooldown_sec` | `int(11)` | NULL | NULL | us_momentum_stock_config: COOLDOWN 초 (cooldown_sec) | +| `max_daily` | `int(11)` | NULL | NULL | us_momentum_stock_config: MAX DAILY (max_daily) | +| `slot_money` | `double` | NULL | NULL | us_momentum_stock_config: SLOT MONEY (slot_money) | +| `rsi_min` | `double` | NULL | NULL | us_momentum_stock_config: RSI MIN (rsi_min) | +| `rsi_max` | `double` | NULL | NULL | us_momentum_stock_config: RSI MAX (rsi_max) | +| `vol_mult` | `double` | NULL | NULL | us_momentum_stock_config: VOL MULT (vol_mult) | +| `vol_win` | `int(11)` | NULL | NULL | us_momentum_stock_config: VOL WIN (vol_win) | +| `chase_lookback_min` | `int(11)` | NULL | NULL | us_momentum_stock_config: CHASE LOOKBACK MIN (chase_lookback_min) | +| `pullback_lookback_min` | `int(11)` | NULL | NULL | us_momentum_stock_config: PULLBACK LOOKBACK MIN (pullback_lookback_min) | +| `pullback_min_pct` | `double` | NULL | NULL | us_momentum_stock_config: PULLBACK MIN % (pullback_min_pct) | +| `pullback_max_pct` | `double` | NULL | NULL | us_momentum_stock_config: PULLBACK MAX % (pullback_max_pct) | +| `setup_vol_max_mult` | `double` | NULL | NULL | us_momentum_stock_config: SETUP VOL MAX MULT (setup_vol_max_mult) | +| `setup_bear_bars_min` | `int(11)` | NULL | NULL | us_momentum_stock_config: SETUP BEAR BARS MIN (setup_bear_bars_min) | +| `high_chase_thr` | `double` | NULL | NULL | us_momentum_stock_config: HIGH CHASE THR (high_chase_thr) | +| `max_daily_chg` | `double` | NULL | NULL | us_momentum_stock_config: MAX DAILY CHG (max_daily_chg) | +| `min_price` | `double` | NULL | NULL | us_momentum_stock_config: MIN 가격 (min_price) | +| `ema_fast_period` | `int(11)` | NULL | NULL | us_momentum_stock_config: EMA FAST PERIOD (ema_fast_period) | +| `ema_slow_period` | `int(11)` | NULL | NULL | us_momentum_stock_config: EMA SLOW PERIOD (ema_slow_period) | +| `use_defense_filters` | `tinyint(4)` | NULL | NULL | us_momentum_stock_config: USE DEFENSE FILTERS (use_defense_filters) | +| `use_high_chase_filter` | `tinyint(4)` | NULL | NULL | us_momentum_stock_config: USE HIGH CHASE FILTER (use_high_chase_filter) | +| `use_daily_range_filter` | `tinyint(4)` | NULL | NULL | us_momentum_stock_config: USE DAILY RANGE FILTER (use_daily_range_filter) | +| `use_ema_filter` | `tinyint(4)` | NULL | NULL | us_momentum_stock_config: USE EMA FILTER (use_ema_filter) | +| `use_rsi_max_filter` | `tinyint(4)` | NULL | NULL | us_momentum_stock_config: USE RSI MAX FILTER (use_rsi_max_filter) | +| `pattern_breakout` | `tinyint(4)` | NULL | NULL | us_momentum_stock_config: PATTERN BREAKOUT (pattern_breakout) | +| `pattern_pullback` | `tinyint(4)` | NULL | NULL | us_momentum_stock_config: PATTERN PULLBACK (pattern_pullback) | + +**인덱스**: +- `uk_usmom_code`: (`code`) +- `idx_usmom_group`: (`stock_group`) + +
+DDL 원문 + +```sql +CREATE TABLE `us_momentum_stock_config` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `code` varchar(32) NOT NULL, + `exchange` varchar(16) NOT NULL DEFAULT 'NASD', + `symbol` varchar(32) NOT NULL DEFAULT '', + `name` varchar(64) DEFAULT '', + `stock_group` varchar(16) NOT NULL DEFAULT 'STOCK', + `created_at` datetime DEFAULT current_timestamp(), + `updated_at` datetime DEFAULT current_timestamp() ON UPDATE current_timestamp(), + `sl_pct` double DEFAULT NULL, + `tp_pct` double DEFAULT NULL, + `tp_max_pct` double DEFAULT NULL, + `shoulder_min_high_pct` double DEFAULT NULL, + `shoulder_cut_pct` double DEFAULT NULL, + `trail_pct` double DEFAULT NULL, + `trail_arm_pct` double DEFAULT NULL, + `ratchet_tiers` varchar(255) DEFAULT NULL, + `max_hold_bars` int(11) DEFAULT NULL, + `cooldown_sec` int(11) DEFAULT NULL, + `max_daily` int(11) DEFAULT NULL, + `slot_money` double DEFAULT NULL, + `rsi_min` double DEFAULT NULL, + `rsi_max` double DEFAULT NULL, + `vol_mult` double DEFAULT NULL, + `vol_win` int(11) DEFAULT NULL, + `chase_lookback_min` int(11) DEFAULT NULL, + `pullback_lookback_min` int(11) DEFAULT NULL, + `pullback_min_pct` double DEFAULT NULL, + `pullback_max_pct` double DEFAULT NULL, + `setup_vol_max_mult` double DEFAULT NULL, + `setup_bear_bars_min` int(11) DEFAULT NULL, + `high_chase_thr` double DEFAULT NULL, + `max_daily_chg` double DEFAULT NULL, + `min_price` double DEFAULT NULL, + `ema_fast_period` int(11) DEFAULT NULL, + `ema_slow_period` int(11) DEFAULT NULL, + `use_defense_filters` tinyint(4) DEFAULT NULL, + `use_high_chase_filter` tinyint(4) DEFAULT NULL, + `use_daily_range_filter` tinyint(4) DEFAULT NULL, + `use_ema_filter` tinyint(4) DEFAULT NULL, + `use_rsi_max_filter` tinyint(4) DEFAULT NULL, + `pattern_breakout` tinyint(4) DEFAULT NULL, + `pattern_pullback` tinyint(4) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_usmom_code` (`code`), + KEY `idx_usmom_group` (`stock_group`) +) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='US_MOMENTUM 종목별 TRIGGER/청산 (NULL=전역 US_MOMENTUM_* 폴백)'; +``` + +
+ +### 📰 DART 공시 + +#### `dart_disclosures` + +**PK**: `rcept_no` + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `rcept_no` 🔑 | `varchar(32)` | NOT NULL | | dart_disclosures: RCEPT NO (rcept_no) | +| `corp_code` | `varchar(16)` | NOT NULL | | dart_disclosures: CORP 종목코드 (corp_code) | +| `corp_name` | `varchar(100)` | NOT NULL | | dart_disclosures: CORP 종목명 (corp_name) | +| `stock_code` | `varchar(20)` | NOT NULL | | dart_disclosures: STOCK 종목코드 (stock_code) | +| `report_nm` | `varchar(255)` | NOT NULL | | dart_disclosures: REPORT NM (report_nm) | +| `rcept_dt` | `varchar(8)` | NOT NULL | | dart_disclosures: RCEPT DT (rcept_dt) | +| `url` | `varchar(255)` | NOT NULL | | dart_disclosures: URL (url) | +| `first_seen_at` | `varchar(30)` | NOT NULL | | dart_disclosures: FIRST SEEN AT (first_seen_at) | +| `mm_sent` | `tinyint(4)` | NOT NULL | 0 | dart_disclosures: MM SENT (mm_sent) | +| `filter_ok` | `tinyint(4)` | NULL | NULL | dart_disclosures: FILTER OK (filter_ok) | +| `sales_pct` | `double` | NULL | NULL | dart_disclosures: SALES % (sales_pct) | +| `filter_reason` | `varchar(64)` | NULL | NULL | dart_disclosures: FILTER REASON (filter_reason) | + +**인덱스**: +- `idx_dart_seen`: (`first_seen_at`) +- `idx_dart_stock`: (`stock_code`) + +
+DDL 원문 + +```sql +CREATE TABLE `dart_disclosures` ( + `rcept_no` varchar(32) NOT NULL, + `corp_code` varchar(16) NOT NULL DEFAULT '', + `corp_name` varchar(100) NOT NULL DEFAULT '', + `stock_code` varchar(20) NOT NULL DEFAULT '', + `report_nm` varchar(255) NOT NULL DEFAULT '', + `rcept_dt` varchar(8) NOT NULL DEFAULT '', + `url` varchar(255) NOT NULL DEFAULT '', + `first_seen_at` varchar(30) NOT NULL, + `mm_sent` tinyint(4) NOT NULL DEFAULT 0, + `filter_ok` tinyint(4) DEFAULT NULL, + `sales_pct` double DEFAULT NULL, + `filter_reason` varchar(64) DEFAULT NULL, + PRIMARY KEY (`rcept_no`), + KEY `idx_dart_seen` (`first_seen_at`), + KEY `idx_dart_stock` (`stock_code`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +#### `dart_watchlist` + +**PK**: `stock_code` + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `stock_code` 🔑 | `varchar(20)` | NOT NULL | | dart_watchlist: STOCK 종목코드 (stock_code) | +| `corp_name` | `varchar(100)` | NOT NULL | | dart_watchlist: CORP 종목명 (corp_name) | +| `rcept_no` | `varchar(32)` | NOT NULL | | dart_watchlist: RCEPT NO (rcept_no) | +| `report_nm` | `varchar(255)` | NOT NULL | | dart_watchlist: REPORT NM (report_nm) | +| `added_at` | `varchar(30)` | NOT NULL | | dart_watchlist: ADDED AT (added_at) | +| `expires_at` | `varchar(30)` | NOT NULL | | dart_watchlist: EXPIRES AT (expires_at) | +| `enabled` | `tinyint(4)` | NOT NULL | 1 | 활성 여부 (1=ON) | + +**인덱스**: +- `idx_dart_watch_exp`: (`expires_at`) + +
+DDL 원문 + +```sql +CREATE TABLE `dart_watchlist` ( + `stock_code` varchar(20) NOT NULL, + `corp_name` varchar(100) NOT NULL DEFAULT '', + `rcept_no` varchar(32) NOT NULL DEFAULT '', + `report_nm` varchar(255) NOT NULL DEFAULT '', + `added_at` varchar(30) NOT NULL, + `expires_at` varchar(30) NOT NULL, + `enabled` tinyint(4) NOT NULL DEFAULT 1, + PRIMARY KEY (`stock_code`), + KEY `idx_dart_watch_exp` (`expires_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +### 🤖 AI 분석 + +#### `ai_analysis_log` + +**PK**: `id` + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `int(11)` | NOT NULL | | PK · AUTO_INCREMENT | +| `created_at` | `varchar(30)` | NOT NULL | | 생성 시각 | +| `model` | `varchar(50)` | NOT NULL | | ai_analysis_log: MODEL (model) | +| `context_summary` | `text` | NULL | NULL | ai_analysis_log: CONTEXT SUMMARY (context_summary) | +| `response` | `mediumtext` | NULL | NULL | ai_analysis_log: RESPONSE (response) | + +
+DDL 원문 + +```sql +CREATE TABLE `ai_analysis_log` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `created_at` varchar(30) NOT NULL, + `model` varchar(50) NOT NULL, + `context_summary` text DEFAULT NULL, + `response` mediumtext DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
+ +### 📋 주문 실행 로그 + +#### `order_execution_history` + +**PK**: `id` + +| 컬럼 | 타입 | NULL | DEFAULT | 비고 | +|------|------|------|---------|------| +| `id` 🔑 | `int(11)` | NOT NULL | | PK · AUTO_INCREMENT | +| `source` | `varchar(20)` | NOT NULL | | 데이터 소스 (kis/kiwoom/ls 등) | +| `ord_no` | `varchar(30)` | NULL | NULL | 증권사 주문번호 | +| `stk_cd` | `varchar(20)` | NULL | NULL | order_execution_history: STK CD (stk_cd) | +| `stk_nm` | `varchar(100)` | NULL | NULL | order_execution_history: STK NM (stk_nm) | +| `trde_tp` | `varchar(20)` | NULL | NULL | order_execution_history: TRDE TP (trde_tp) | +| `ord_qty` | `varchar(20)` | NULL | NULL | order_execution_history: ORD 수량 (ord_qty) | +| `ord_uv` | `varchar(20)` | NULL | NULL | order_execution_history: ORD UV (ord_uv) | +| `cntr_qty` | `varchar(20)` | NULL | NULL | order_execution_history: CNTR 수량 (cntr_qty) | +| `cntr_uv` | `varchar(20)` | NULL | NULL | order_execution_history: CNTR UV (cntr_uv) | +| `ord_tm` | `varchar(20)` | NULL | NULL | order_execution_history: ORD TM (ord_tm) | +| `cnfm_tm` | `varchar(20)` | NULL | NULL | order_execution_history: CNFM TM (cnfm_tm) | +| `sell_tp` | `varchar(20)` | NULL | NULL | order_execution_history: SELL TP (sell_tp) | +| `ord_dt` | `varchar(20)` | NULL | NULL | order_execution_history: ORD DT (ord_dt) | +| `raw_json` | `text` | NULL | NULL | 원본 응답 JSON | +| `fetched_at` | `varchar(30)` | NOT NULL | | order_execution_history: FETCHED AT (fetched_at) | + +
+DDL 원문 + +```sql +CREATE TABLE `order_execution_history` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `source` varchar(20) NOT NULL, + `ord_no` varchar(30) DEFAULT NULL, + `stk_cd` varchar(20) DEFAULT NULL, + `stk_nm` varchar(100) DEFAULT NULL, + `trde_tp` varchar(20) DEFAULT NULL, + `ord_qty` varchar(20) DEFAULT NULL, + `ord_uv` varchar(20) DEFAULT NULL, + `cntr_qty` varchar(20) DEFAULT NULL, + `cntr_uv` varchar(20) DEFAULT NULL, + `ord_tm` varchar(20) DEFAULT NULL, + `cnfm_tm` varchar(20) DEFAULT NULL, + `sell_tp` varchar(20) DEFAULT NULL, + `ord_dt` varchar(20) DEFAULT NULL, + `raw_json` text DEFAULT NULL, + `fetched_at` varchar(30) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +``` + +
diff --git a/docs/like_mcp.md/소켓.md b/docs/like_mcp.md/소켓.md new file mode 100644 index 0000000..05bb1b9 --- /dev/null +++ b/docs/like_mcp.md/소켓.md @@ -0,0 +1,106 @@ +# 키움 ↔ LS증권 웹소켓 및 조건검색 실시간 아키텍처 백서 + +이 문서는 키움증권(Kiwoom)과 LS증권(LS Open API)의 조건검색 및 시세 웹소켓 통신 메커니즘의 차이점, 과거 운영 중 발생한 꼬임(오류)의 근본원인과 금번 개편된 100점짜리 아키텍처 설계 지침을 총정리한 문서입니다. + +--- + +## 🏛️ 1. 키움 ↔ LS증권 총괄 비교 및 초등학생 비유 요약 + +| 구분 | 🟣 **키움증권 (Kiwoom)** | 🔵 **LS증권 (LS Open API)** | +|:---:|:---|:---| +| **통신 메커니즘** | **100% 단일 WebSocket 일체형**
*(REST API 일절 사용하지 않음)* | **REST API (TR 조회) + WebSocket (AFR 알람)**
*(4단계 릴레이 분산형 하이브리드)* | +| **국제표준 Ping (RFC 6455)** | ❌ **비호환** (`ping_interval=0`)
*(대신 서버가 30초마다 JSON `PING` 보냄)* | ❌ **비호환** (`ping OFF`)
*(표준 Ping 보내면 무시하고 Pong 안 줌)* | +| **장중 생존 감시 방식** | **서버의 30초 심장박동 (JSON PING)**
👉 장중 워치독(의심병 타이머) 불필요! | **심장박동 아예 없음**
👉 15~30분마다 소켓 ID만 안전하게 리부팅 필요 | +| **초기 종목 수집** | **`CNSRREQ` 소켓 응답에 포함**
*(구독 신청하면 현재 명단 같이 줌)* | **`t1859` (REST API 별도 호출 필수)**
*(소켓은 연결 이전 과거 멤버를 안 알려줌)* | +| **초등학교 사장님 비유** | *"주문 접수했으니 수락 카톡(ACK) 줘요!
안 주면 3초마다 전화함!" (수락 즉시 타이머 삭제)
+ 사장님이 30초마다 켜져있다고 방송해 줌.* | *"사장님이 주문만 받고 장중 영업 고백을 안 함."
너무 조용하면 15분마다 "사장님 안 자죠?" 하고
알람 ID만 새것으로 교체해 소켓을 새로 열어봄.* | + +--- + +## 🟣 2. 키움증권 (Kiwoom) 조건검색 및 소켓 동작 원리 + +### (1) 작동 순서 +1. **`CNSRLST` (WS)**: 웹소켓 접속 후 계정에 저장된 조건식 명단과 일련번호(`seq`) 조회. +2. **`CNSRCLR` (WS)**: 충돌 방지를 위해 기존 등록을 선행 청산하고 완료 응답(ACK) 대기. +3. **`CNSRREQ` (WS)**: 실시간 감시 구독 요청. **응답 데이터에 현재 조건에 걸린 초기 종목 스냅샷이 동봉**됨. +4. **`REAL` (WS)**: 같은 소켓을 통해 신규 편입/이탈 시 실시간 푸시 수신. + +### (2) 아까 발생했던 오류와 금번 수정(해결)의 본질 +* **과거 치명적 오류 (왜 터졌었는가?)**: + - 새벽 `Bye` 메시지 후 소켓 재접속 시, 키움 서버에 이전 등록이 남아있는 경우 `CNSRREQ`를 보내면 **`900003` (이미 등록된 조건식) 충돌 에러**가 발생. + - 구형 로직은 이 900003 충돌이 두 번 나면 **즉시 해당 조건식을 영구 폐기(discard)**하여 유니버스가 평생 0종목이 되는 자살골을 범함. 또한 재시도 간격이 지수 백오프(5초 → 15초 → 30초 → 300초)로 멍들어 있어 장중 5분 이상 매매 감시가 비어있는 대참사 유발. +* **금번 혁신적 개편 (100% ACK 기반 고정 회복)**: + - **영구 폐기(discard) 퇴출**: 어떤 충돌이 나도 절대 조건식을 포기하거나 유니버스를 0개로 버리지 않음. + - **3초 고정 주기 초재기 (ACK 획득 전용)**: `CNSRCLR` 후 서버 완료 확인증(ACK)을 인지하여 2초 settle 후 safe 등록 수행. 만약 응답이 누락되거나 거절되면 **3.0초 고정 주기 / 최대 30회(약 90초)** 동안 완료 확인증(ACK)을 받아낼 때까지 끝까지 전화해서 살려냄. + - 💡 **핵심**: 서버가 "수락 완료(ACK)"를 쏘는 즉시 이 3초짜리 초재기 타이머는 100% 삭제(소멸)되며, 장중 내내 드르륵 도는 것이 아님! + +### (3) 키움은 왜 장중 유지보수(15~30분 주기 재기동)가 안 필요한가? +* 키움 웹소켓은 장중에 아무 거래가 없어도, 서버가 **30초마다 0.1초도 틀리지 않고 `{"trnm": "PING"}` JSON 메시지를 내려줌**. +* 봇은 이를 그대로 echo하여 소켓이 100% 팽그르르 돌고 있음을 지속 확인. +* 통신선이 끊어지면 30초마다 오던 PING이 실패하므로 OS의 비동기 `_on_close` / `_on_error`가 즉각 펑 터져 자동 재접속됨. 따라서 LS처럼 "조용히 죽었나?" 의심해서 주기적으로 소켓을 건드릴 필요가 전혀 없음! + +--- + +## 🔵 3. LS증권 (LS Open API) 조건검색 및 소켓 동작 원리 + +### (1) 국제표준 PING(20초)을 못 쓰고 `ping OFF`를 한 이유 +* **LS 서버 사양의 한계**: LS Open API 웹소켓(`wss://openapi.ls-sec.co.kr:9443`)은 클라이언트가 국제 표준 RFC 6455 PING 프레임을 20초마다 보내면, **이를 무시하고 PONG 응답을 돌려주지 않는 서버 비호환 상태**임. +* 만약 표준 PING을 켜두면, Python 라이브러리가 "10초째 PONG 대답이 없다!"며 멀쩡한 소켓을 에러 쳐서 끊고 재접속을 연타하게 됨. 이 때문에 `AfrHistoryWatcher`에서 강제로 `ping OFF`로 설정할 수밖에 없었음. +* 또한 키움과 달리 30초 주기로 내려오는 사제 JSON PING조차 없음 (심장박동 제로). + +### (2) `t1866`, `t1859`, `t1860`, `AFR` — 4대 TR의 100% 정확한 역할 +LS는 REST API와 WebSocket이 물리적으로 찢어져 있어 4단계를 조립해야 합니다. (**`t1866`, `t1859`, `t1860`은 100% REST API이며, `AFR`만 웹소켓 소켓입니다!**) + +1. **`t1866` (REST API) - [조건식 명단 장부]**: + - 내 계정에 저장된 조건식들의 **이름(title)과 고유 인덱스(`query_index`)** 목록을 조회하는 TR. (종목 명단은 절대 주지 않음). +2. **`t1859` (REST API) - [현재 종목 풀덤프 장부 / 🚨장중 위험 인물]**: + - 현재 시간 기준 해당 조건식(`query_index`)에 충족해 있는 **전체 종목 명단을 한꺼번에 조회**하는 TR. + - *왜 필요한가?* AFR 웹소켓은 소켓 연결 **이후** 발생하는 일만 말해주므로, **봇을 맨 처음 켤 때(기동 시) 또는 재시작할 때 최초 1번은 RAM 장부를 채우기 위해 100% 필수**로 불러야 함! +3. **`t1860` (REST API) - [실시간 알람 신청 및 ID 발급기]**: + - 옵션 `'E'` (등록): "이 조건식 실시간 감시 시작하게 소켓 전용 **알람 ID(`sAlertNum`, 11자리)** 하나 발급해주세요!" 하고 ID를 따내는 TR. + - 옵션 `'D'` (해제): "이전 알람 ID 이제 필요 없으니 감시 멈춰주세요." 하고 닫는 TR. (역시 종목 명단은 안 줌). +4. **`AFR` (WebSocket) - [실시간 편입/이탈 스피커]**: + - 위 3번에서 받은 알람 ID(`sAlertNum`)를 들고 접속하는 실시간 시세 소켓. 접속 시점 이후 발생한 **`N` (진입), `R` (재진입), `O` (이탈)** 이벤트만 날려줌. + +--- + +## 💥 4. 과거 LS 로직의 근본 원인과 "859 연타 땜질"이 부른 참사 + +### (1) 진짜 근본 원인 (Root Cause: 꿀 먹은 AFR 소켓) +* 장중 LS 서버 부하 또는 사용자의 HTS 조건식 변경 등으로 인해 **AFR 실시간 소켓이 에러 신호도 띄우지 않고 조용히 굳어버리거나 푸시를 멈추는 침묵 현상**이 일어남. +* 소켓이 조용해지면 이탈(`O`) 메시지를 끝내 못 받아, HTS 화면에는 종목이 탈락해 사라졌는데 봇 RAM 장부에는 끝까지 살아있어 썩어가는 **죽은 유령 종목(Sticky) 잔존 사고** 발생. + +### (2) 과거 개발자의 어리석은 땜장이 처방 (60초 규칙) +* LS는 PING도 안 주고 소켓 침묵이 감지 안 되니까 극도의 공포감을 느낀 과거 개발자가 **"에이! 소켓으로 편입/이탈 신호가 60초(1분) 동안 안 오면, 무조건 소켓이 먹통 되었거나 HTS 조건을 수정한 걸로 칠래!"** 하고 간주. +* 그래서 60초간 이벤트가 없으면 **`t1859`(REST 조회)를 무조건 연타해서 RAM 장부를 통째로 강제 덮어쓰기(교차보정) 시키는 기형 로직**을 탑재함. + +### (3) 859 땜장이 처방이 부른 2차 대참사 (자살골) +* 평화로운 점심시간이나 장이 한산할 땐 멀쩡한 소켓도 5~30분간 조용한 것이 완벽한 정상임! +* 그런데 구형 봇이 "60초 동안 조용하네? 찔러야지!" 하고 1분마다 `t1859` REST를 난타하다가... +* LS 서버가 장중 순간 지연으로 엉뚱한 **"조회결과 0종목(Empty)"** 응답을 툭 반환하면, **"헐! 0개네! 다 나갔나 보다!" 하고 RAM 속 우량 감시 종목 20개를 싹뚝 쳐내서 살해(Clear)해버리는 대참사** 발발. +* 이를 가리겠다고 "축소 방지(`ALLOW_SHRINK=false`)", "레이스 컨디션 방지" 등 누더기 코드가 기하급수적으로 증식했던 것. + +--- + +## 🎯 5. 금번 개편된 LS 조건검색 실시간 아키텍처 (최종 합의 마스터 로직) + +### 1️⃣ 왜 Pong을 안 주거나 줘도 문제인가? (국내 증권사 좀비 소켓의 비밀) +* **프록시(문지기) ↔ 백엔드 불일치 현상**: 아침 9시 부하 시, 앞단 웹소켓 프록시(Gateway)는 멀쩡해 Ping/Pong 통신이나 TCP 연결이 녹색불(정상)을 띄우고 있어도, 뒷단 실시간 조건검색 방송 엔진이 혼자 부하로 먹통이 되어 알람을 안 쏴주는 **"꿀 먹은 좀비 소켓"** 사태가 발생함. +* 일반 주식 시세(`ls_ws.py`)는 거래 틱이 45초간 안 오면 100% 좀비로 판단해 차단하고 재연결(Watchdog)할 수 있지만, **조건검색은 평안한 장세에서 2시간 동안 이벤트가 없는 게 정상이므로 짧은 워치독 타이머를 둬서는 안 됨!** + +### 2️⃣ 시세·조건 전용 이점 극대화 ➡️ 60초(1분) 주기 "소켓 심폐소생술 & Delta Diff" +* **매매 전용이 아닌 조건·시세 전용 (Scouter) 구조의 위대한 판정**: + - 당사의 봇은 실제 주식 매수/매도 주문(TR)을 LS 증권으로 쏘지 않고, 오직 **조건검색 감시와 호가/시세 전용 스카우터 기지**로만 운용함. + - 따라서 REST API를 매분 찔러도 **정작 제일 중요한 실매매 주문이 429 에러(유량 상한 초과)로 막힐 위험이 100% 영구 소멸**함! +* **하루 총 눈감는 시간 58초 (수학적 극대화 효율)**: + - 60초(`LS_T1859_STALE_RESYNC_SEC=60`) 주기로 국장 정규시간 390분(09:00~15:30) 내내 하루 390회 소켓 ID를 재발급(`t1860 D ➡️ E ➡️ AFR 재구독`)하더라도, 1회당 발생하는 마의 암흑기 틈새 0.15초를 곱하면 **하루 누적 눈감는 총 시간이 고작 '58.5초'(1분 미만)**에 불과함! + - 60초마다 HTS 조건 수동 변경 사항을 즉각 반입하고, 멍든 소켓을 단 1분 만에 깨우는 극강의 실시간 스카우터를 완주함. + +### 3️⃣ 신의 한 수: "Delta Diff (1:1 비교 보정) & 0종목 보호 방패" +60초마다 소켓이 재개통되는 0.15초 틈새(Gap) 동안에 HTS에서 몰래 탈퇴한 **유령 종목(Sticky Ghost)**이 RAM에 남는 사태(100개 퇴적 악몽)와 서버 순간 지연을 막기 위해, 60초 주기 `t1859` 대조 시 **아래 2중 철옹성 방어막을 영구 적용**함! + +* 🔴 **0종목 보호 방패 (자살골 절대 차단)**: + - 기존 RAM에 종목이 있는데 `t1859` 응답이 0종목(Empty)으로 내려오면, 이는 99.9% LS 서버 지연이나 통신 장애로 인한 가짓말 응답임. + - 즉시 **"⚠️ 0종목 오류 응답 감지 → 자살골 청산 없이 기존 RAM 장부 100% 보존!"** 판정으로 서버 응답을 무시하여 우량 감시 주식을 수호함! +* 🟢 **Delta Diff 정교 소결 (빼고 넣고 1:1 대조)**: + - 유효한 응답(예: RAM 20종 ➡️ t1859 15종)이 오면, 무지성 전체 삭제 후 덮어쓰지 않음. + - 기존 RAM 명단과 최신 명단을 대조하여 **틈새 동안 사라진 유령 종목 5개만 정확히 솎아내어(Pop) 내버리고, 신규 편입 종목만 상단에 반영(Push)**하는 최상의 비동기 정제 엔진을 작동시킴! diff --git a/docs/옵투나.md b/docs/옵투나.md new file mode 100644 index 0000000..b315d11 --- /dev/null +++ b/docs/옵투나.md @@ -0,0 +1,69 @@ +# Optuna 파라미터 탐색 및 호가 수급 최적화 구조 가이드 (옵투나.md) + +이 문서는 백테스트 웹페이지 및 CLI 환경에서 구동되는 **Optuna 파라미터 최적화(Stage 1 & 2) 메커니즘**과, 특히 **호가 수급 필터(Orderbook Filter) 및 수익구간 호가매도(Exit Orderbook) 수치의 탐색·표기·DB 반영 체계**를 설명합니다. + +--- + +## 🚀 1. 투-스테이지(2-Stage) 최적화 작동 원리 + +백테스트 웹페이지 또는 스크립트에서 Optuna를 돌릴 경우 다음과 같이 2단계로 연쇄 구동됩니다. + +``` ++---------------------------------------------------------------------------------------------+ +| [Stage 1: 캔들 차트 파라미터 탐색] | +| - RSI, 손익비(TP/SL), 풀백, 휩쏘, 이평선 등 주가 차트 지표 최적 수치 탐색 | ++---------------------------------------------------------------------------------------------+ + │ (Stage 1 완료 즉시 자동 연쇄 기동) + ▼ ++---------------------------------------------------------------------------------------------+ +| [Stage 2: 호가 수급 합의 수치 1,000회 고속 핀셋 탐색] (`optuna_orderbook_recommend.py`) | +| - Stage 1의 최우량 진입/청산 타점 데이터를 바탕으로, 진입 전후의 실매 호가 스냅샷을 분석 | +| - 최적의 호가 스프레드 상한(%), 잔량비 하한, 수익구간 호가매도 마이크로 수치 도출 | ++---------------------------------------------------------------------------------------------+ +``` + +--- + +## 🌐 2. 백테스트 웹페이지 연동 및 결과 표기 + +### ① 웹페이지 Optuna 실행 시 자동 1,000회 탑재 +* 웹 UI(`http://192.168.0.149:5050/`)에서 Optuna 버튼을 작동시키면, 서버 단의 `optuna_mode_combo.py` 모듈이 차트 최적화 종료 시점에 `attach_orderbook_recommend`를 자동으로 호출합니다. +* 별도의 추가 조작 없이도 호가 1,000회 시뮬레이션이 백그라운드에서 즉각 동반 구동됩니다. + +### ② 웹 화면 및 보고서(JSON) 표기 위치 +* 도출된 호가 수급 추천 합의 수치는 웹에서 반환되는 Optuna 결과 딕셔너리(`out_data` 및 `mode_combo`)의 **`"orderbook_recommend"`** 전용 블록에 실려서 화면에 출력됩니다. + +```json +/* 웹 UI 및 optuna_*_tpe_*.json 내 출력 예시 */ +"orderbook_recommend": { + "ok": true, + "strategy": "MOMENTUM", + "params": { + "orderbook_filter_enabled": true, + "orderbook_max_spread_pct": 0.46, + "orderbook_min_bid_ask_ratio": 0.59, + "exit_ob_enabled": true, + "exit_ob_ratio_min": 1.25, + "exit_ob_ma_window": 58 + } +} +``` + +--- + +## 🚨 3. 실매매 DB 적용 안전 분리 원칙 (필수 숙지) + +Optuna가 찾아낸 수치 중 **차트 파라미터**와 **호가 수급 파라미터**는 실전 DB 각인 방식이 완전히 분리되어 있습니다. + +| 구분 | 차트 캔들 파라미터 (RSI, 손익 등) | 호가 수급 파라미터 (스프레드, 호가 익절 등) | +|------|---------------------------------------|-------------------------------------------| +| **웹 UI "Apply(적용)" 버튼** | ✅ 버튼 한방으로 DB 환경변수 및 웹 인풋 적용 완료 | ⚪ **미리보기 및 화면 보고서 확인 전용** (차트 수치에 휩쓸려 실수로 DB 덮어씌워짐 방지) | +| **실매매 DB 최종 갱신 커맨드** | 웹 버튼 또는 `param_search_optuna.py --apply-best` | 🚨 **반드시 전용 독립 스크립트를 가동해야 DB에 안전 새김** | + +### 🛠️ 호가 수급 최적 수치 실매매 DB 전용 각인 커맨드 +호가 쪽 Optuna 합의 수치를 실매매 DB에 정기적으로 세팅하거나 갱신할 때는 반드시 아래 스크립트를 가동하여 도장 찍고 적용해야 합니다. +```bash +# 특정 전략(MOMENTUM, BREAKOUT, SCALPING, TAIL) 호가 합의 수치 DB 갱신 +.venv/bin/python3 scripts/apply_optuna_ob_consensus.py --strategy MOMENTUM +``` +* **수행 내용:** 1,000회 고속 최적화 ➔ 합의 수치 도출 ➔ TradeDB 환경변수 세트 안전 패치 ➔ `test_live_execution_validation.py`로 사전 정합성 증명 가동! diff --git a/docs/증권사_토큰_시세.md b/docs/증권사_토큰_시세.md new file mode 100644 index 0000000..39f5d26 --- /dev/null +++ b/docs/증권사_토큰_시세.md @@ -0,0 +1,211 @@ +# 증권사별 토큰 · 키 · 시세/구독 정리 + +> 운영 기준 메모 (코드·공식 스펙·실측). **추측 재발급 금지.** +> 관련: `kis_token_manager.py`, `kis_approval_manager.py`, `kis_trader/network/ls_token.py`, `kis_trader/ws/*`, `docs/계정.md` + +최종 갱신: 2026-08-03 + +--- + +## 0. 한 줄 지도 + +| 증권사 | 매매 REST | 시세/조회 REST | WS 인증 | WS 구독 한도(실측/코드) | 국내↔해외 | +|--------|-----------|----------------|---------|-------------------------|-----------| +| **한투(KIS)** | access_token (모의/실전 분리) | **시세는 실키** 권장 | **approval_key** (REST 토큰과 별개) | 국내 **41**/세션 | approval **1키=1세션** → hold 시간분할 | +| **키움** | access token (au10001) | 동일 토큰 | WS LOGIN = **같은 토큰** | 그룹당 **100** (`KIWOOM_WS_MAX_SUBSCRIPTIONS`) | 국내 WS 중심 (해외 모멘텀은 한투) | +| **LS** | access_token (`/oauth2/token`) | 동일 토큰 | WS header `token` = **같은 토큰** | 문서상 유연 · 코드는 폭주 가드 | **토큰 1개**로 국내+해외 TR | + +공통 사고 유형: **유효 토큰/키를 또 발급** → 상대 경로(국내↔해외·REST↔WS) **무효화**. + +--- + +## 1. 한투 (KIS) + +### 1.1 키 종류 (역할 분리) + +| 이름 | 발급 | 용도 | 무효화 주의 | +|------|------|------|-------------| +| **접근토큰** `access_token` | `POST /oauth2/tokenP` | 주문·잔고·시세 REST | 잦은 재발급 → 제한(EGW00133 등). **1일 1회 원칙** · 유효 ~24h | +| **approval_key** | `POST /oauth2/Approval` | **WebSocket만** | **6h 이내 재발급 금지**(운영 가드). 새 키 = **기존 WS 세션 무효** | +| AppKey/Secret | 콘솔 | 위 둘의 자격 | 실전/모의 **도메인·키 쌍 분리** | + +코드: + +- 매매 클라이언트: `KIS_MOCK` 따름 (`kis_trader.main` `self.client`) +- 시세/조회: **항상 실키** (`market_client`) — 모의 도메인 시세 미지원/500 다수 +- 토큰 파일 캐시: `kis_token_manager` (실전+모의 **세션 미커버/만료만** 발급) +- approval 파일 캐시: `kis_approval_manager` (국내·해외 WS **공유**) + +### 1.2 REST 도메인 + +| | REST | WS | +|--|------|-----| +| 실전 | `https://openapi.koreainvestment.com:9443` | `ws://ops.koreainvestment.com:21000` | +| 모의 | `https://openapivts.koreainvestment.com:29443` | `ws://ops.koreainvestment.com:31000` | + +### 1.3 WebSocket 시세 + +| TR | 시장 | 내용 | 한도 | +|----|------|------|------| +| `H0STCNT0` | 국내 | 실시간 체결 | **세션당 최대 41종** (`kis_ws.MAX_SUBSCRIPTIONS`) | +| `HDFSCNT0` | 해외 | 실시간(지연) 체결 | 별도 소켓 · **같은 approval** 공유 | +| (호가 등) | 국내 | 주로 REST/다른 TR · 실매 호가필터는 **키움 0D / LS UH1** 경로도 사용 | — | + +**국내↔해외 자동전환:** `kis_ws_session_windows` + +- 국내 hold 기본 `07:00~20:00` / 해외 `21:00~06:00` +- 갭에서는 **소켓 close만** (approval **재발급 없음**) +- 동시에 두 소켓을 열면 뒤쪽(대개 해외)이 끊김 → **시간분할 필수** + +### 1.4 히스토리 · 기타 + +| 용도 | 경로 | +|------|------| +| 분봉 | REST `inquire-time-itemchartprice` 등 (시세=실키) | +| 틱 적재 | WS → `ws_ticks` / CandleAggregator → `ws_candles` | +| 해외 봉 | `ws_candles.market=US` (HDFSCNT0 롤업) | +| 주문 | REST (모의/실전 = 매매 키) | + +env 스위치: + +- `STRATEGY_US_MOMENTUM_ENABLED` — 전략 루프 (config_us_momentum) +- `OVERSEAS_WS_ENABLED` — HDFSCNT0 소켓 **기동 시** (env_config) +- `US_MOMENTUM_DRY_RUN` — paper vs 실주문 + +--- + +## 2. 키움 + +### 2.1 토큰 + +| 항목 | 내용 | +|------|------| +| API | `POST /oauth2/token` (`au10001`) | +| 응답 | `token`, `token_type`, **`expires_dt`** (일시 문자열) | +| 사용처 | REST + **WS LOGIN** (approval 분리 없음) | +| 위험 | **중복 발급 시 이전 토큰 무효** → 시세 WS·조건검색 동시 사망 (과거 사고) | + +코드: 공유 캐시 + 서버 거부(805004 등) 시에만 invalidate 후 재발급. +재연결 연타 쿨다운: `KIWOOM_WS_MAX_RECONNECT_*`. + +### 2.2 WebSocket + +| | 실전 | 모의 | +|--|------|------| +| URL | `wss://api.kiwoom.com:10000/api/dostk/websocket` | `wss://mockapi.kiwoom.com:10000/...` | + +| type | 이름 | 용도 | +|------|------|------| +| `0B` | 주식체결 | 틱·현재가·분봉 롤업 | +| `0D` | 주식호가잔량 | 호가필터 / OR 히스토리 | +| `0w` | 종목프로그램매매 | 프로그램 필터 | +| (조건) | 조건검색 실시간 | `kiwoom_condition` 유니버스 | + +**구독 한도:** 그룹당 **100종** (`KIWOOM_WS_MAX_SUBSCRIPTIONS`, 기본 100). +실매 운영: 후보 상한 합 + 영구구독 + 보유 ≤ 100. + +### 2.3 히스토리 + +| 용도 | 경로 | +|------|------| +| 분봉 갭 | REST `ka10080` 등 (키움 차트) | +| 틱/호가/프로그램 | WS → RAM + DB (`ws_ticks`, orderbook/program 스냅샷) | + +시세 실키 vs 매매 모의는 **한투 쪽 정책**과 혼동하지 말 것. 키움은 주로 **시세/조건** 담당. + +--- + +## 3. LS증권 + +### 3.1 토큰 + +| 항목 | 내용 | +|------|------| +| API | `POST https://openapi.ls-sec.co.kr:8080/oauth2/token` | +| grant | `client_credentials` + appkey/appsecretkey | +| 응답 | `access_token`, **`expires_in`/~86400초(24h)** | +| 오류 | `IGW00121` / `IGW00123` (무효·기간만료) → 그때만 재발급 | +| 캐시 | `kis_trader/network/ls_token.py` — 만료 전 재사용 · `LS_TOKEN_MIN_REISSUE_SEC` · **force 남용 금지** | + +**국내·해외·조건검색(AFR)·WS 가 토큰 1개 공유.** +세션 전환·워치독 재연결에서 force 재발급하면 **전 경로 무효화** (키움 중복발급과 동형). + +`/oauth2/revoke` = 정상 폐기용. 루프에서 쓰지 않음. + +### 3.2 WebSocket + +| | 실전 | 모의 | +|--|------|------| +| URL | `wss://openapi.ls-sec.co.kr:9443/websocket` | `…:29443/websocket` | + +| TR | 시장 | 내용 | +|----|------|------| +| `US3` (기본) / `S3_`·`K3_` | 국내 | 체결 틱 | +| `UH1` 등 | 국내 | 호가 | +| `UVI` 등 | 국내 | VI | +| `JIF` | 국내 | 장운영정보 (워치독 보조) | +| `GSC` | 해외 | 해외 체결 (`82{symbol}` 키) | + +소켓 hold (벽시계): `ls_ws_session_windows` +- 국내 `07:00~20:00` / 해외 `21:00~06:00`(해외 구독 있을 때) +- 갭·장외: **소켓 close + 대기**, 토큰 캐시 유지 +- 틱 silence 워치독: **정규장 벽시계 우선** (sticky JIF `21`로 장외 연장 금지) + +### 3.3 히스토리 · 조건 + +| 용도 | 경로 | +|------|------| +| 분봉 갭 | REST `t8412` → `ls_ws_candles` | +| 틱/호가 | WS → `ls_ws_ticks` / `ls_ws_orderbook` | +| 조건식 | AFR `t1860` 등 (`ls_condition`) — JIF/벽시계 AFR 세션 | + +--- + +## 4. 이 봇에서의 역할 분담 (현행) + +| 기능 | 주 소스 | 비고 | +|------|---------|------| +| 국내 주문 | **한투** REST | 모의/실전 = `KIS_MOCK` | +| 국내 틱(운영) | **키움** WS (또는 한투 H0STCNT0) | `WS_PROVIDER` / 유니버스 소스에 따름 | +| 국내 호가·프로그램 | **키움** 0D / 0w | 모멘텀 등 | +| 돌파 등 LS 유니버스 | **LS** 조건 + LS 틱/호가 | `BREAKOUT_UNIVERSE_SOURCE=ls_condition` | +| 해외 모멘텀 시세 | **한투** HDFSCNT0 | `permanent_subscriptions` US | +| 해외 모멘텀 주문 | **한투** 해외주문 | `US_MOMENTUM_DRY_RUN` | + +### 4.1 전략 ON/OFF vs 소켓 + +| 스위치 | 테이블 | 효과 | 구독 해제? | +|--------|--------|------|------------| +| `STRATEGY_*_ENABLED` | `config_{strategy}` | 다음 루프부터 **신규매수 중단** (보유 청산은 유지) | **안 함** | +| `STRATEGY_US_MOMENTUM_ENABLED` | `config_us_momentum` | 위와 동일 | **안 함** | +| `OVERSEAS_WS_ENABLED` | `env_config` | **기동 시** 해외 WS 기동 여부 | 기동 전제 · 변경 시 **재시작** | +| `permanent_subscriptions` | DB 테이블 | 영구구독 종목 목록 | UI에서 종목 on/off | + +기동 시 OFF였던 전략을 켜려면 **봇 재시작**(쓰레드 미등록). + +--- + +## 5. 재발급 체크리스트 (위반 시 작업 중지) + +1. 만료·서버 거부 코드 확인 전에는 **force 발급 금지** +2. KIS approval: 6h 가드 · 국내/해외 **공유 캐시만** · 세션 전환은 **close/open** +3. LS: `expires_in` 캐시 · 세션 hold 전환 시 **oauth 호출 금지** +4. 키움: LOGIN 거부 시에만 invalidate · 재연결 폭주 쿨다운 +5. 진단 스크립트도 실매와 **동일 한도** + +--- + +## 6. 코드 앵커 + +| 주제 | 파일 | +|------|------| +| KIS 접근토큰 | `kis_token_manager.py` | +| KIS approval | `kis_approval_manager.py` | +| KIS 국내 WS | `kis_trader/ws/kis_ws.py` | +| KIS 해외 WS | `kis_trader/ws/kis_ws_overseas.py` | +| KIS hold 창 | `kis_trader/utils/kis_ws_session_windows.py` | +| 키움 WS | `kis_trader/ws/kiwoom_ws.py` | +| LS 토큰 | `kis_trader/network/ls_token.py` | +| LS WS | `kis_trader/ws/ls_ws.py` | +| LS hold 창 | `kis_trader/utils/ls_ws_session_windows.py` | +| 운영 ON/OFF UI | `kis_trader/web/live_config_schema.py` (`strategy_switch`) | diff --git a/docs/호가.md b/docs/호가.md new file mode 100644 index 0000000..bf9b393 --- /dev/null +++ b/docs/호가.md @@ -0,0 +1,448 @@ +# 호가.md — 진입 필터 · 익절 지정가 · IOC(13) · 수익구간 호가매도 · 손실구간 호가매도 + +이 문서는 대화·코드·DB 기준으로 **호가 관련 기능을 역할별로 구분**해 정리한다. +이름이 비슷해도 **서로 다른 스위치**이므로 혼동하지 말 것. +아래에서는 **L3 / SL-OB 같은 줄임말을 쓰지 않고** 한글로 푼다. + +--- + +## 0-0. 「호가 쌓기」는 설계가 아니다 (헷갈림 방지) + +**DB에 호가 한 장씩 넣는 것(저장)은 이미 돌아가는 수집 기능이다.** +새 매매 로직·새 설계 항목이 **아니다.** + +| 말 | 진짜 뜻 | +|----|---------| +| “호가 INSERT / 쌓기” | 봇이 장중 호가를 `ws_orderbook` / `ls_ws_orderbook` 에 **기록**하는 중 (지금 하고 있음) | +| “필터·호가매도 끄고 쌓기만” | **끄는 건 매매 스위치**다. 저장은 켠 채, **매수 막을지 / 호가로 팔지** 스위치만 OFF | +| 설계에 적어 둔 이유 | “저장이 새 기능”이 아니라 **스위치 켜서 실험하지 말자**는 **운영 메모** | + +초등 비유: +일기장에 날씨를 매일 적는 것 = **쌓기(이미 함)**. +일기 보고 “비 오면 우산 사기” 규칙을 켜는 것 = **필터·호가매도 ON**. +규칙은 아직 끄고, 일기만 모으자는 뜻이었다. + +### 2026-08-03 장일 확인 (틱 동기) + +| 테이블 | 오늘 | 방식 | 판정 | +|--------|------|------|------| +| `ls_ws_orderbook` (`ls_uh1`) | **약 274만 행** / 88종목 | 체결 1건 ≈ 호가 1장 (비율 ≈ **0.999**) | **틱 동기화로 쌓임 ✅** | +| `ws_orderbook` (`kiwoom_0d`) | **약 12.5만 행** / 38종목 | 간격 중앙값 **약 3초** | 연속 저장 ✅ · **틱 1:1은 아님** | +| `ws_orderbook` (`filter_eval`) | 29행 | 진입 판정 순간 1장 | 필터 판정 로그용 | + +모멘텀 실매 매수 **오늘 0건** (유니버스 이슈) → 매도쪽 측정 스크립트는 **오늘 모수 없음**. +최근 모수: **2026-07-31** 모멘텀 7건 + 키움 호가 (§4 측정). + +--- + +## 0-1. 운영 메모 (스위치 OFF · 저장은 ON) + +1. **호가 저장** — 이미 ON. 장중 행 수만 가끔 확인하면 됨. +2. **매수 호가필터** `*_ORDERBOOK_FILTER_ENABLED` = **OFF 유지** (아직 실험 금지) +3. **수익구간 호가매도** `MOMENTUM_EXIT_OB_ENABLED` / `BREAKOUT_EXIT_OB_ENABLED` = **OFF 유지** +4. **손절호가** `MOMENTUM_STOP_OB_ENABLED` / `BREAKOUT_STOP_OB_ENABLED` = **OFF 유지** (코드 배선됨 · 켜기 전 측정) +5. 데이터가 모이면 + - 매수필터: 탈락 사유 비율 보고 쓸지 결정 + - 수익구간 호가매도: `scripts/measure_ob_exit_early_fire.py` 재측정 + - 손실구간 호가매도: 손실·손절 구간 측정 후 구현 여부 +6. **Optuna TPE**: 위 스위치들 **당분간 그리드에 넣지 말 것** (§8) + +실매·백테는 스위치 OFF면 **예전과 동일**. 저장만 계속된다. + +--- + +## 0. 한눈에 구분 + +| 구분 | 언제 | 하는 일 | 대표 키 | 상태 | +|------|------|---------|---------|------| +| **호가 저장(쌓기)** | 장중 항상 | DB에 호가 기록 | `LS_WS_ORDERBOOK_SAVE` / `WS_ORDERBOOK_SAVE_ENABLED` 등 | **이미 동작 중** · 설계 아님 | +| **매수 호가필터** | 사기 직전 | 호가 나쁘면 **안 삼** | `{전략}_ORDERBOOK_FILTER_ENABLED` | 코드 있음 · 운영 OFF | +| **익절 호가 ON** | 이미 매도 신호 난 뒤 | 익절을 **지정가**로 냄 | `SELL_USE_ORDERBOOK_ON_PROFIT` | 운영 ON | +| **매도 시장가 IOC(13)** | 시장가로 팔 때 | 실전만 13 | `USE_MARKET_IOC_SELL` | 운영·코드 분리 | +| **수익구간 호가매도** | 들고 있을 때 **이득 난 뒤** | 사려는 사람 줄면 **미리 이익 실현** | `MOMENTUM_EXIT_OB_*` / `BREAKOUT_EXIT_OB_*` | 코드 있음 · **기본 OFF** | +| **손실구간 호가매도** | 들고 있을 때 **손해 난 뒤** | 사려는 사람 줄면 **큰 손절 전에 먼저 나옴** | `MOMENTUM_STOP_OB_*` / `BREAKOUT_STOP_OB_*` | **배선 완료 · 기본 OFF** | + +거래내역 **「매수호가」** = 살 때 근처 · **「매도호가」** = 팔 때 근처. 서로 다른 칸. + +### 수익구간 호가매도 vs 손실구간 호가매도 (초등) + +둘 다 “호가판에 사려는 사람이 확 줄었다”를 본다. +**언제 쓰느냐**만 다르다. + +| | 수익구간 호가매도 | 손실구간 호가매도 | +|--|------------------|------------------| +| 내 장부 | **이미 조금 이득**일 때만 | **이미 조금 손해**일 때만 | +| 하는 일 | “이득 났는데 분위기 깨지면 이익 챙기고 나감” | “손해인데 분위기 더 깨지면, −몇% 손절선까지 안 가고 먼저 나감” | +| −몇% 손절선 | **안 건드림** | **공식은 그대로** · 그 **앞에서** 따로 한 번 더 봄 | +| 지금 | 코드 있음, 스위치 OFF | 글만, 코드 없음 | +| 파람서치(TPE) | **당분간 안 넣음** | **당분간 안 넣음** | + +--- + +## 1. 매수 호가필터 (진입) + +### 역할 +- TRIGGER 단계에서 스프레드·매수/매도 잔량비·얇은 호가·매도벽 등으로 **매수 차단**. +- **청산 신호가 아님.** + +### 키 (전략별) +글로벌 `ORDERBOOK_*` 수치 폐기 → 전략 prefix만 사용. + +- `SCALP_ORDERBOOK_FILTER_ENABLED` +- `MOMENTUM_ORDERBOOK_FILTER_ENABLED` +- `BREAKOUT_ORDERBOOK_FILTER_ENABLED` +- `TAIL_ORDERBOOK_FILTER_ENABLED` (SHORT → TAIL) + +임계 예: `*_ORDERBOOK_MAX_SPREAD_PCT`, `*_MIN_BID_ASK_RATIO`, `*_ENTRY_BID_LEVELS` 등. + +### 코드 +- 판정 공통: `kis_trader/engine/orderbook_filter.py` +- env: `kis_trader/engine/orderbook_env.py` +- 웹: 예) 모멘텀 「호가필터 ON」(`mom_ob_filter`) — **이 백테 회차 진입 필터** + +### 왜 전략별로 갈라졌나 +스캘프·모멘텀·돌파·꼬리는 호가 의미가 달라 **ON/OFF·임계를 전략마다** 둔다. +판정 함수는 공유, **설정 키만** 분리. + +### 수집 vs 필터 +- `KIWOOM_WS_ORDERBOOK_ENABLED` — 호가 WS 수신 +- 시계열 `kiwoom_0d` / LS `ls_ws_orderbook` — 저장(필터 OFF여도 저장 가능) +- 필터 ON = 그 스냅으로 **매수 탈락 적용** + +### 켜면 매매가 확 줄어드는 이유 (조건식 탓 아님) +- HTS/조건식 = “살 만한 **이야기**(타점)” 선별 +- 호가필터 = “지금 이 순간 **대기표가 들어가기 좋은가**” 추가 게이트 +- 모멘텀·돌파류는 급등·얇은 종이 많아, 통과 직후 호가가 **스프레드 큼 / 살힘 약함 / 매도벽**인 경우가 흔함 +- 거래내역 「매수호가」에 **간격큼·살힘 0.1x대**가 보여도 **데이터가 이상한 게 아니라** 그 타점 유동성이 그런 것 +→ “조건식이 잘못 뽑았다”로 단정하지 말 것. **유동성 게이트가 한 겹 더 있어서** 건수가 줄어든다. + +### RAM(실매) vs DB 3초(백테) +| | 실매 필터 ON | 백테 필터 ON | +|--|--------------|--------------| +| 보는 것 | **RAM 최신** (`get_orderbook_snapshot`) | DB 스냅 / `filter_eval` 재생 | +| 스냅 없음 | **안 막음**(통과) | 재생·본체 없으면 그 타점 못 탐·탈락 재현 | + +DB ~3초 저장은 **백테·표시용**. 실매 차단은 RAM. +“3초라서 가짜로 막는다”보다 **그 시각 근처 호가가 이미 나빴다**에 가깝다. + +### 임계값(0.45 · 0.85 …) — 업계 표준 아님 +`orderbook_env.py` **프로젝트 시드/시작값** (Optuna·웹에서 변경 가능). “보편적으로 다들 0.45”가 **아님**. + +| 기본 | 의미 | 체감 | +|------|------|------| +| 스프레드 **0.45%** | `(ask−bid)/mid` 상한 | 중소형·급등에서 **자주 걸림** | +| 잔량비 **0.85** | 상위 N호가 매수÷매도 | 살 쪽이 거의 비슷해야 통과 | +| 깊이 **1.2** | 필요수량 ×1.2 ≤ 매수호가 합 | | +| 매도벽 **3.0** | 매도잔량 과다 컷 | | + +AND라서 하나라도 깨지면 탈락 → 체감이 셈. + +### 이 필터가 굳이 필요한가? → **필수 아님** +| 목표 | 선택 | +|------|------| +| 거래 수·백테 건수 유지 | **OFF** (현 운영 기본) | +| 얇은 호가·큰 갭 진입을 줄이고 싶다 | ON 또는 **임계 완화** | +| 조건식이 이미 유동성 좋은 종만 | 필터 체감↓ | + +- **진입 필터** = 살까 말까 +- **익절 호가 ON** = 이미 산 뒤 어떻게 팔까 (`SELL_USE_ORDERBOOK_ON_PROFIT`) — **다른 스위치** +호가가 안 좋게 찍힌다는 사실 자체가 “필터 필수” 근거가 아님. +안 좋은 호가에 **들어가느냐를 막을지** 선택일 뿐. 과하면 끄거나 숫자만 푼다. + +### 키움 조건 vs LS 조건 (진입·수익구간 호가매도 공통) +- `kiwoom_condition` / `condition` → 호가 **키움 0D RAM** · DB `ws_orderbook` +- `ls_condition` → 호가 **LS UH1 RAM** · DB `ls_ws_orderbook` (키움으로 메우지 않음) +수익구간·손절호가도 같은 스냅샷 API를 탐. **전략 범위: 모멘텀(키움) + 돌파(LS)** · 둘 다 기본 OFF. (진입 필터는 전 전략 키 있음). + +--- + +## 2. 익절 「호가 ON」— 지정가 매도 (실행) + +### 역할 +청산 **판정(래칫·어깨·트레일)이 이미 난 뒤**, 주문을 **어떻게 내는지**. + +| 스위치 | ON | OFF / 호가 없음 | +|--------|----|-----------------| +| `SELL_USE_ORDERBOOK_ON_PROFIT` | 익절·어깨·트레일 → 매수1호가 **지정가(`ORD_DVSN=00`)** · 잔량 부족 시 **대기** | 같은 익절 신호 → **시장가** | + +**손절 · 긴급 · 장마감(EOD)** → 이 스위치와 무관, **항상 시장가**. + +### 운영 값 (확인 시점 기준) +저장: `config_short` + +| 키 | 값 | 의미 | +|----|----|------| +| `SELL_USE_ORDERBOOK_ON_PROFIT` | **true** | 익절 지정가 경로 ON | +| `SELL_ORDERBOOK_BID_LEVELS` | 2 | 매수 1~2호가 잔량 합 검사 | +| `SELL_ORDERBOOK_DEPTH_MULT` | 1.5 | 필요 잔량 = 수량 × 1.5 | + +### 코드 +- `kis_trader/execution/order_manager.py` — 매도 전송 분기 +- `kis_trader/execution/orderbook_sell.py` — 잔량·지정가 가격 산출 + +전 전략 공통(OrderManager 한 경로). 진입 필터처럼 전략별 키가 아님. + +### UI +라이브 스키마에 진입 「호가필터 ON」은 잘 보이지만, +`SELL_USE_ORDERBOOK_ON_PROFIT`는 **스키마 전용 체크가 약하고 DB(`config_short`)·get_env로 동작**. + +--- + +## 3. 매도 주문코드 13 — 시장가 IOC (실전만) + +### 역할 +**시장가 매도**를 낼 때의 `ORD_DVSN` 선택. +익절 지정가(00)와는 별개. + +| 환경 | 시장가 매도 코드 | +|------|------------------| +| **모의** | 항상 **`01`** (IOC 13 미지원 → 코드에서 고정) | +| **실전** + `USE_MARKET_IOC_SELL=true` | **`13`** (시장가 IOC) | +| **실전** + false | **`01`** (일반 시장가) | + +매수용은 별도: `USE_MARKET_IOC` (실전 매수 13 / 모의 01). + +### 코드 +`kis_trader/execution/kis_client.py` +- `sell_market_order` / `uses_market_sell_ioc()` +- `sell_limit_order` → 항상 `00` + +### 운영 UI +`live_config_schema`: 「매도 시장가 IOC」=`USE_MARKET_IOC_SELL` + +### 흐름 요약 +``` +익절 신호 + ├─ SELL_USE_ORDERBOOK_ON_PROFIT ON + 잔량 OK → 지정가 00 + └─ OFF / 호가없음 / 잔량실패 후 시장가 + ├─ 실전 + IOC ON → 13 + └─ 모의 → 01 + +손절·긴급·EOD → 시장가 (위와 동일하게 실전 13 / 모의 01) +``` + +--- + +## 4. 수익구간 호가매도 (코드 있음 · 기본 OFF) + +진입 필터·익절 지정가와 **다른 축**: +이미 산 뒤, **이득이 난 상태에서** “사려는 사람이 줄었다”면 **팔지 말지**를 본다. + +### 초등으로 +1. 주식을 샀다. +2. 가격이 조금 올랐다 (이득). +3. 호가판을 보니 “사고 싶다”는 줄이 확 짧아졌다. +4. 그때 **이익을 챙기고 나간다** = 수익구간 호가매도. +5. **아직 이득이 아니면** 이 규칙으로 안 판다. (그건 손실구간 쪽) + +### 왜 맨 위에 두면 안 되나 +- 장 초반·급등 초입은 원래 파는 사람이 많다 → “분위기 끝”으로 **오해**하기 쉬움 +- 사자마자 팔면 수수료만 나고 기회 날림 +→ **맨 위(1순위) 금지.** 래칫·어깨 **다음**, −% 손절 **앞**. + +측정(실매 매수 + `ws_orderbook`, 예: 2026-07-27 모멘텀): +- 가드 없이 순간만 보면: 조기 신호 **많음** +- 이득·최소보유 가드 후: **크게 줄어듦** +※ 이 %는 **수익률이 아님** = “산 뒤 N분 안에 이 신호가 뜬 건수 비율” + +### 합의된 매도 순서 +``` +래칫 → 어깨 → 수익구간 호가매도 → 손실구간 호가매도 → −% 손절 → 트레일 → … +``` +- 코드: 어깨 다음 · 손절 전 = 수익구간(`호가컷`) + 손절호가 · **기본 OFF** +- 모멘텀=키움 호가 · 돌파=LS 호가 (교차 메우기 금지) +- −% 손절은 **가격 하나만**. 호가를 손절 % 숫자에 섞지 않음. + +### env 키 (모멘텀=키움 · 돌파=LS · 수익구간) +| 키 | 기본 | 역할 | +|----|------|------| +| `MOMENTUM_EXIT_OB_ENABLED` / `BREAKOUT_EXIT_OB_ENABLED` | **false** | 켜기/끄기 | +| `*_EXIT_OB_RATIO_MIN` | 0.4 | 사려는/팔려는 비율 이동평균이 이하면 “줄었다” | +| `*_EXIT_OB_MA_WINDOW` | 5 | 몇 장 평균 볼지 | +| `*_EXIT_OB_MIN_PROFIT_PCT` | 0.005 (+0.5%) | **이만큼 이득일 때만** | +| `*_EXIT_OB_MIN_HOLD_BARS` | 3 | 산 지 최소 N분 | + +등록: `database.py` (`config_momentum` / `config_breakout`) · defaults → `exit_ob_*`. +웹 모멘텀·돌파 탭에 컨트롤 있음. **ENABLED 기본 false 유지.** + +### 발동 조건 (모두 만족) +| 조건 | | +|------|--| +| 스위치 ON | | +| 비율 이동평균 < 기준 | 스냅 부족 → **안 함** | +| 최소 이득 | | +| 최소 보유 | | + +### 배선 상태 (2026-08) +실매·백테 OR 히스토리 주입 ✅ · 웹 ✅ · Optuna **미포함**(§8). +OFF면 예전과 동일. + +### 측정 스크립트 (매도쪽 · 필터 켜기 전) +`scripts/measure_ob_exit_early_fire.py` +- 실매 매수 + 실매 호가 테이블 +- A) 맨 위+가드없음 / B) 맨 위+가드 / C) 3순위+가드(현 설계) +- 예: `--date 2026-07-31 --ob-table ws_orderbook` +- **2026-08-03**: 모멘텀 매수 0건 → 측정 불가 (호가는 쌓여 있음) + +### 2026-07-31 매도쪽 한 줄 (키움 `ws_orderbook`) +모멘텀 7건 전부 호가 있음. +A 28.6% → B/C 14.3% (가드가 핵심). +같은 날 `ls_ws_orderbook`은 그 종목들 스냅 0 → LS로 못 재현 (모멘텀=키움 조건 쪽). + +### 전략 범위 +파일럿 **모멘텀만**. 진입 필터 키와 **혼용 금지**. + +--- + +## 4-1. 손실구간 호가매도 (배선 완료 · 기본 OFF · 모멘텀+돌파) + +수익구간과 **같은 호가 눈**, **반대 장부**. + +### 초등으로 +1. 주식을 샀다. +2. 가격이 조금 떨어졌다 (손해). +3. 호가판에 사려는 사람이 확 줄었다. +4. −3% 같은 **큰 손절선까지 기다리기 전에** 먼저 나온다. +5. **이득 중이면** 이 규칙으로 안 판다. (그건 수익구간) + +### 왜 만드나 +−% 손절만 있으면, 호가가 이미 무너진 **뒤에** 가격이 손절선에 닿아 **더 나쁜 가격**에 팔 수 있음. +수익구간 규칙만 있으면 **손해 날 때**는 안 도와줌. + +### 원칙 +1. −% 손절 **숫자는 그대로**. 이 규칙은 **앞에서 한 번 더 보는 것**뿐. +2. **손해일 때만.** +3. 맨 위 금지 — 래칫·어깨 다음, −% 손절 직전. +4. 호가 스냅 없으면 **안 함** (가격봉으로 호가 지어내기 금지). +5. 나갈 때는 **시장가/IOC**(도망). 익절 지정가「호가 ON」과 다름. +6. 기본 **OFF**. 측정 → 승인 → 코드 → ON. + +### 목표 순서 +``` +1 래칫 +2 어깨 +3 수익구간 호가매도 ← 이득 + 호가 줄음 +4 손실구간 호가매도 ← 손해 + 호가 줄음 ★배선·기본 OFF +5 −% 손절 ← 가격만 +6 트레일 · 시간 · … +``` + +### env (모멘텀 · 배선됨) — 한글 뜻 + +| 키 (영문) | 한글 이름 | 기본 | 한줄 | +|-----------|-----------|------|------| +| `MOMENTUM_STOP_OB_ENABLED` / `BREAKOUT_STOP_OB_ENABLED` | 손절호가 켜기 | **false** | 끄면 이 규칙 전부 무시 | +| `MOMENTUM_STOP_OB_RATIO_MIN` | 호가 붕괴 기준 | 0.4 | 총사÷총팔 평균이 **이보다 작으면** “사람 줄었다” | +| `MOMENTUM_STOP_OB_MA_WINDOW` | 평균 몇 장 | 5 | 위 비율을 **최근 5장** 평균내서 봄 (순간 깜빡임 방지) | +| `MOMENTUM_STOP_OB_MIN_LOSS_PCT` | 최소 손해 폭 | 0.003 (−0.3%) | **이만큼은 이미 손해**여야 발동 (수익 중엔 안 씀) | +| `MOMENTUM_STOP_OB_MIN_HOLD_BARS` | 최소 보유 분 | 2 | 산 지 **2분 전**엔 안 씀 | + +DDL·컬럼 COMMENT: `scripts/sql/config_momentum_ob_column_comments.sql` · `scripts/sql/config_breakout_ob_column_comments.sql` + +### 하드 손절 4%일 때 언제 나감? (손절호가 ON 가정) + +1. **산 지 2분 미만** → 손절호가 **안 함**. (−4% 하드만 가능) +2. **2분 이상** + 현재가 **−0.3% 이하** + OR평균 **< 0.4** → 사유 **`손절호가`** 로 **즉시** 시장가 (하드 4%까지 **안 기다림**) +3. 호가는 안 무너졌는데 가격만 **−4%** → 예전처럼 사유 **`손절`** +4. 지금 DB 기본은 **ENABLED=false** → 2번은 아예 없고 **4% 하드만**. + +**“얼마나 빨리”** = “−0.3%에 OR만 깨지면 그때”. 4%까지 버티는 규칙이 아님. +OR 5장 채우는 시간: LS 틱동기면 체결 몇 번(초 단위) · 키움 ~3초 저장이면 대략 **십수 초**면 충분. 최소보유 **2분**이 보통 더 긴 병목. + +**삼성전자급 두꺼운 호가**: 평소 OR이 0.4 아래로 잘 안 떨어짐 → 손절호가가 **거의 안 울릴 수 있음**. 진짜 수급 붕괴·급락 구간에서야 −0.3%~−4% 사이에서 선제. 얇은 중소형에서 체감이 큼. + +사유 문자열: **`손절호가`** (수익구간의 `호가컷`과 구분 · 시장가 긴급). + +### 상태 +| 항목 | | +|------|--| +| 설계 | ✅ | +| 코드·DB·웹 | ✅ 2026-08-03 배선 | +| 기본값 | **OFF** (켜기 전 측정 권장) | +| TPE | **안 넣음** | + +### 측정 (호가 쌓인 뒤 · 모멘텀 매수가 있는 날) +1. 손실→−%손절(또는 큰 손실) 건 중, 손절 **직전**에 이미 호가가 줄었던 비율 +2. 손해였다가 회복한 건에서 이 규칙이 **너무 일찍** 나갔을 비율 +→ 측정 후 **ON 여부** 결정 (지금 기본 OFF). + +--- + +## 5. 데이터 · UI 참고 + +| 항목 | 내용 | +|------|------| +| 호가 테이블 | `ws_orderbook` (키움), `ls_ws_orderbook` (LS) | +| 테이블에 **있는** 것 | `best_bid`/`best_ask`, `total_bid_qty`/`total_ask_qty`, `bid_qty_l3`/`ask_qty_l3` 등 **원본** | +| 테이블에 **없는** 것 | `0.45%`·살힘·OR 같은 **계산된 %/비율** — DB 컬럼이 아님 | +| 거래내역 「매수호가」「매도호가」 | `trade_orderbook_enrich.py`가 매수/매도 시각 근처 스냅을 붙인 뒤, **웹 JS가 원본으로 계산·색칠** | +| 기준 숫자(시드) | 매수: 간격≤0.45% · 살힘≥0.85 / 매도 OR: ≈0.40 (수익구간 호가매도 기본) — 업계 표준 아님 | +| LS 오늘 | **틱 동기** (체결≈호가 1:1) | +| 키움 오늘 | 연속 저장 · 간격 **약 3초** (틱 1:1 아님) | + +### UI 칸 — 이상(높을수록 / 낮을수록) + +| 칸 | 지표 | 이상 | 초등 | +|----|------|------|------| +| **매수호가** | 가격간격 % | **↓ 낮을수록** | 사·팔 가격이 가까울수록 들어가기 쉬움 | +| **매수호가** | 살 힘 (위3칸 사÷팔) | **↑ 높을수록** | 사겠다는 대기가 많을수록 버팀 | +| **매도호가** | OR (총사÷총팔) | **↑ 높을수록** 수급OK · **↓ 낮으면** 약함 | 팔 때 사려는 사람이 많으면 버팀, 적으면 무너짐 | +| **매도호가** | 가격간격 % | **↓ 낮을수록** | 팔 때도 간격 작을수록 체결 덜 미끄러짐 | + +열 헤더: `↓간격 ↑살힘` / `↑OR ↓간격` (마우스 올리면 상세). + + +--- + +## 6. 하지 말 것 (요약) + +1. 매수필터 · 익절지정가 · 수익구간호가매도 · 손실구간호가매도를 **한 스위치로** 취급하지 말 것. +2. “호가 쌓기”를 **새 매매 설계**로 착각하지 말 것 — **이미 있는 저장**. +3. 호가를 −% 손절 **공식에 섞지** 말 것. +4. 호가매도를 **1순위**에 두지 말 것. +5. 스냅 없이 OHLC로 호가 **지어내기** 금지. +6. 모의에 ORD_DVSN=13 강제 금지. +7. 측정 없이 ENABLED=true 금지. +8. 데이터 없이 TPE에 넣지 말 것 (§8). +9. 거래대금·시총회전 **만**으로 손절 만들지 말 것. + +--- + +## 7. 관련 파일 + +| 파일 | 내용 | +|------|------| +| `kis_trader/engine/orderbook_filter.py` | 매수 호가필터 | +| `kis_trader/engine/orderbook_env.py` | 전략별 진입 env | +| `kis_trader/execution/orderbook_sell.py` | 익절 지정가·잔량 | +| `kis_trader/execution/order_manager.py` | 매도 실행 분기 | +| `kis_trader/execution/kis_client.py` | 00/01/13 주문 | +| `kis_trader/backtest/trade_orderbook_enrich.py` | 거래내역 호가 표시 | +| `kis_trader/engine/momentum_hts_logic.py` | 수익구간·손절호가 판정 (모멘텀·돌파 공용) | +| `kis_trader/engine/momentum_engine.py` | `exit_ob_*` defaults | +| `kis_trader/engine/momentum_env_keys.py` · `BREAKOUT_*_OB_*`(database/config_breakout) | 전략별 env | +| `kis_trader/strategies/momentum.py` | 실매 OR append | +| `scripts/measure_ob_exit_early_fire.py` | 매도쪽 조기신호 측정 | +| `docs/layered_exit_design.md` | 옛 설계서 (현행은 본 MD §4·§4-1) | +| `docs/호가.md` | 본 문서 | + +--- + +## 8. Optuna / TPE 넣을까? + +### 결론 (당분간) — **넣지 않음** +매수필터 · 수익구간 호가매도 · 손실구간 호가매도 모두. + +켜기/끄기를 TPE에 넣으면: 끈 구간=예전 점수, 켠 구간=호가 날씨에 흔들려 **순위가 엉망**이 된다. + +나중에라도: 호가 충분 + 측정 숫자 + 사용자가 **전용 mode**를 말할 때만. +그때도 본체 `tpe`/`fine`이 아니라 **좁은 mode**에서 숫자만. + +### 지금 +쌓여 있는 호가로 **측정** → (승인) 코드 → OFF 스모크 → ON 여부. **TPE는 그 다음.** + +--- + +## 9. env 등록 주의 + +신규 키는 `database.py` ENV 등록 + `get_env_from_db` 재조회 검증. diff --git a/holding_bot.py b/holding_bot.py index f43a1df..422c046 100644 --- a/holding_bot.py +++ b/holding_bot.py @@ -305,8 +305,8 @@ def _ensure_token(mock: bool) -> str: """ KisTokenManager 싱글톤으로 토큰 반환. - 유효하면 메모리 캐시 즉시 반환 - - 만료 10분 전 선제 갱신 후 반환 - - KIS 23시간 정책 자동 준수 + - 완전 만료 시에만 비상 갱신 (장중 10분 선제 발급 없음) + - 장전 ensure_both / 08:00 타이머가 1일1회 원칙으로 선확보 """ try: from kis_token_manager import KisTokenManager diff --git a/kis_token_manager.py b/kis_token_manager.py index a06ec15..707ca71 100644 --- a/kis_token_manager.py +++ b/kis_token_manager.py @@ -4,21 +4,26 @@ kis_token_manager.py — KIS 실전/모의 토큰 통합 자동 관리 ========================================================== 문제: 모의 모드로만 봇을 돌리면 실전 토큰이 갱신되지 않음. → 실전 토큰 만료 → WebSocket/홀딩봇 REST 호출 실패 → 거래 없음 + → 20h 표류 타이머 + 장중 10분 선제 갱신으로 발급 시각이 장중에 밀림 -해결: 어느 모드로 봇을 실행해도 실전+모의 토큰 모두 유효하게 유지. - 파일 잠금(lockfile) 으로 동시에 여러 봇이 실행해도 중복 발급 방지. +해결: + - 매일 장전(systemd OnCalendar 08:00)에 ensure_both_tokens() + - 실전+모의 둘 다: **오늘 세션을 못 덮거나 이미 만료일 때만** 발급 + - 장중 get_token(): **완전 만료(±60초)일 때만** 비상 발급 (선제 SMS 금지) -KIS 제한: - - 하루 1회 원칙 (23시간 내 중복 발급 시 제한 가능) - - 이 모듈은 만료 1시간 전까지 기존 토큰 재사용 → 하루 1회만 발급 +KIS 접근토큰(/oauth2/tokenP) 한도 (우선 준수): + - 유효 약 24시간 + - 1일 1회 발급 원칙 (유효기간 내 잦은 발급 → 이용 제한 가능) + - EGW00133: 1분당 1회 + ※ WS approval_key 의 6h 재발급 가드는 별도(kis_approval_manager) — 여기서 건드리지 않음 사용 예시 (각 봇 시작부): from kis_token_manager import ensure_both_tokens - ensure_both_tokens() # 실전+모의 토큰 자동 갱신 (만료된 것만) + ensure_both_tokens() # 실전+모의: 세션 미커버분만 갱신 CLI: - python3 kis_token_manager.py # 상태 확인 - python3 kis_token_manager.py --refresh # 강제 갱신 시도 + python3 kis_token_manager.py # 상태 확인 + 필요 시만 갱신 + python3 kis_token_manager.py --refresh # 강제 갱신 시도 (한도 주의) """ import json @@ -38,8 +43,10 @@ ROOT = Path(__file__).parent CACHE_MOCK = ROOT / ".kis_token_cache_mock.json" CACHE_REAL = ROOT / ".kis_token_cache_real.json" LOCK_FILE = ROOT / ".kis_token_manager.lock" -EXPIRE_MARGIN_H = 1 # 파일 캐시 상태 조회용 (get_token_status, ensure_token) 버퍼 -EXPIRE_MARGIN_M = 10 # KisTokenManager.get_token() 선제 갱신 버퍼 (10분) +# 장중 비상: 만료 N초 전부터만 재발급 시도 (옛 10분 선제 = 장중 카톡 유발 → 폐기) +EXPIRE_SAFETY_SEC = 60 +# 오늘 정규장+버퍼를 덮는지 판정 (HHMM). 이 시각 전에 만료면 장전 발급. +SESSION_END_HHMM = 1600 LOCK_TIMEOUT_S = 60 # 잠금 최대 대기 시간(초) @@ -71,33 +78,99 @@ def _parse_expired(s: str): return None +def _session_end_hhmm() -> int: + """오늘 세션 커버 목표 HHMM (기본 1600). OS env 로만 오버라이드.""" + raw = (os.environ.get("KIS_TOKEN_SESSION_END_HHMM", "") or "").strip() + if not raw: + return SESSION_END_HHMM + try: + v = int(raw) + if 0 <= v <= 2359: + return v + except (TypeError, ValueError): + pass + return SESSION_END_HHMM + + +def _session_cover_deadline(now: Optional[datetime] = None) -> datetime: + """당일 세션 커버 데드라인 (시:분).""" + now = now or datetime.now() + hhmm = _session_end_hhmm() + return now.replace( + hour=hhmm // 100, + minute=hhmm % 100, + second=0, + microsecond=0, + ) + + +def token_covers_session( + exp_dt: Optional[datetime], + now: Optional[datetime] = None, +) -> bool: + """ + 접근토큰이 '오늘 운용'을 덮는지. + - 이미 만료 → False (발급 필요) + - 지금이 세션 종료 이전이면: 만료시각 >= 세션종료 이어야 True + - 세션 종료 이후(장후)면: 아직 만료 전이면 True (불필요 재발급 금지) + """ + now = now or datetime.now() + if exp_dt is None: + return False + if now >= exp_dt: + return False + deadline = _session_cover_deadline(now) + if now >= deadline: + return True + return exp_dt >= deadline + + def get_token_status(is_mock: bool) -> dict: """ 캐시 파일 상태 반환. - 반환: {"valid": bool, "token": str, "expires": str, "expires_in_h": float} + 반환: valid=세션커버(ensure 재사용 기준), usable=만료 전 API 사용 가능 """ cache_path = CACHE_MOCK if is_mock else CACHE_REAL - mode = "모의" if is_mock else "실전" if not cache_path.exists(): - return {"valid": False, "token": "", "expires": "파일없음", "expires_in_h": -999} + return { + "valid": False, + "usable": False, + "token": "", + "expires": "파일없음", + "expires_in_h": -999, + } try: cache = json.loads(cache_path.read_text(encoding="utf-8")) token = cache.get("access_token", "") expired_s = cache.get("access_token_token_expired", "") exp_dt = _parse_expired(expired_s) if not token or exp_dt is None: - return {"valid": False, "token": "", "expires": expired_s, "expires_in_h": -999} + return { + "valid": False, + "usable": False, + "token": "", + "expires": expired_s, + "expires_in_h": -999, + } now = datetime.now() expires_in = (exp_dt - now).total_seconds() / 3600 - valid = now < exp_dt - timedelta(hours=EXPIRE_MARGIN_H) + usable = now < exp_dt + covers = token_covers_session(exp_dt, now) return { - "valid": valid, - "token": token[:12] + "…", - "expires": expired_s, + "valid": covers, + "usable": usable, + "token": token[:12] + "…", + "expires": expired_s, "expires_in_h": round(expires_in, 1), } except Exception as e: - return {"valid": False, "token": "", "expires": f"읽기오류: {e}", "expires_in_h": -999} + return { + "valid": False, + "usable": False, + "token": "", + "expires": f"읽기오류: {e}", + "expires_in_h": -999, + } def _acquire_lock() -> bool: @@ -175,8 +248,8 @@ def _issue_token(app_key: str, app_secret: str, is_mock: bool) -> bool: KIS /oauth2/tokenP 엔드포인트로 새 토큰 발급 후 캐시 저장. 성공 시 True, 실패 시 False. - ※ KIS 정책: 23시간 내 중복 발급 시 서비스 제한 가능 + 분당 1회 제한 (EGW00133). - 이 함수는 만료 1시간 전까지 기존 토큰을 재사용하므로 하루 1회만 호출됨. + ※ KIS 정책: 1일 1회 원칙 + 분당 1회 제한 (EGW00133). + ensure_token 이 세션 미커버/만료일 때만 여기로 온다. 발급 실패 시 백오프(`_set_backoff`)로 호출자 폭주 방지. """ # 백오프 미경과 → 즉시 실패 (호출자에게 재시도 부담 떠넘기기 금지) @@ -226,6 +299,7 @@ def _issue_token(app_key: str, app_secret: str, is_mock: bool) -> bool: "access_token": token, "access_token_token_expired": exp, "mock": is_mock, + "issued_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), }, ensure_ascii=False, indent=2), encoding="utf-8", ) @@ -245,19 +319,23 @@ def _issue_token(app_key: str, app_secret: str, is_mock: bool) -> bool: # ───────────────────────────────────────────────────────────────────────────── def ensure_token(is_mock: bool, env: dict = None) -> bool: """ - 단일 모드(실전/모의) 토큰 유효성 확인 + 필요 시 자동 갱신. - 이미 유효한 경우 → 발급 없이 True 반환 (23시간 정책 자동 준수). + 단일 모드(실전/모의) 토큰: 오늘 세션을 덮으면 재사용, 아니면만 발급. + 1일 1회 원칙 — 충분하면 tokenP 호출 없음. """ status = get_token_status(is_mock) mode = "모의" if is_mock else "실전" if status["valid"]: logger.info( - f"🔑 {mode} 토큰 유효 ({status['expires_in_h']:.1f}h 남음) → 재사용" + f"🔑 {mode} 토큰 세션커버 OK ({status['expires_in_h']:.1f}h 남음, " + f"만료 {status['expires']}) → 재사용 (발급 안 함)" ) return True - logger.info(f"⏰ {mode} 토큰 만료/없음 → 갱신 시도 (남은시간: {status['expires_in_h']:.1f}h)") + logger.info( + f"⏰ {mode} 토큰 세션미커버/만료 → 갱신 시도 " + f"(남은시간: {status['expires_in_h']:.1f}h, 만료 {status['expires']})" + ) if env is None: env = _load_env() @@ -285,10 +363,10 @@ def ensure_token(is_mock: bool, env: dict = None) -> bool: def ensure_both_tokens(env: dict = None) -> dict: """ - 실전 + 모의 토큰 모두 유효하게 유지. + 실전 + 모의 토큰 모두 세션커버 유지. - 어느 모드로 봇을 실행하든 두 토큰을 동시에 관리. - 만료된 것만 갱신 → KIS 23시간 정책 자동 준수. + 어느 모드로 봇을 실행하든 두 토큰을 동시에 점검. + 세션 미커버/만료분만 발급 → KIS 1일 1회 원칙 준수. 반환: {"real": True/False, "mock": True/False} """ @@ -315,7 +393,7 @@ def ensure_both_tokens(env: dict = None) -> dict: # 동작 원리: # 1. 메모리 캐시 (_token, _expiry) — 파일 읽기 최소화 # 2. JSON 파일 캐시 — 크로스 프로세스 공유 + 재시작 후 토큰 유지 -# 3. 만료 10분 전 선제 갱신 — API 에러(EGW00123) 없이 자동 교체 +# 3. 만료(±60초) 시에만 비상 발급 — 장중 10분 선제 발급 금지 # # KIS 에는 토큰 상태 조회 API 가 없음 → 발급 시 받은 expires_dt 를 # 서버 현재시간과 비교해서 판단 (JSON 파일에 저장된 날짜 사용) @@ -331,7 +409,7 @@ _km_instances_lock = threading.Lock() class KisTokenManager: """ KIS 토큰 싱글톤 매니저. - - get_token(): 유효하면 바로 반환, 만료 10분 전부터 선제 갱신 + - get_token(): 유효하면 바로 반환, 완전 만료 시에만 비상 갱신 - kiwoom_rest_api TokenManager / KiwoomTokenManager 와 동일한 인터페이스 """ @@ -369,18 +447,18 @@ class KisTokenManager: def _is_valid(self) -> bool: """ - 만료 10분 전부터 유효하지 않다고 판단 → 선제 갱신 트리거. - KIS에는 토큰 상태 조회 API 없음 → JSON 파일의 expires_dt 와 서버시간 비교. + API 사용 가능 여부. 장중 선제 발급 트리거용 10분 마진 없음. + 만료 SAFETY초 전까지만 유효 → 그 전에는 tokenP 안 침. """ if not self._token or not self._expiry: return False - return datetime.now() < self._expiry - timedelta(minutes=EXPIRE_MARGIN_M) + return datetime.now() < self._expiry - timedelta(seconds=EXPIRE_SAFETY_SEC) # ── 공개 ────────────────────────────────────────────────────── def get_token(self) -> Optional[str]: """ - 유효한 토큰 반환. 만료 10분 전 자동 갱신. - 매수/매도 API 호출 직전마다 호출해도 안전 (갱신은 만료 임박 시만). + 유효한 토큰 반환. 완전 만료(±60초) 시에만 비상 갱신. + 평소 발급은 장전 ensure_both_tokens / 타이머가 담당. 흐름: 1. 메모리 캐시 유효 → 즉시 반환 (파일 읽기 없음) @@ -408,13 +486,13 @@ class KisTokenManager: # 만료 토큰이라도 반환 (None 보다 안전 — 호출자 None 폭주 방지) return self._token - # 파일도 만료 → 잠금 기반 신규 발급 + # 파일도 만료 → 잠금 기반 신규 발급 (ensure 는 세션커버 판정) ok = ensure_token(self._is_mock) if ok: with self._lock: self._load_from_file() logger.info( - "🔄 %s 토큰 선제 갱신 완료 (만료 10분 전) → 앞8자: %s…", + "🔄 %s 토큰 비상 갱신 완료 (만료/세션미커버) → 앞8자: %s…", self._mode_str, (self._token[:8] if self._token else "N/A"), ) @@ -442,15 +520,26 @@ if __name__ == "__main__": ) parser = argparse.ArgumentParser(description="KIS 토큰 관리자") - parser.add_argument("--refresh", action="store_true", help="만료 여부와 관계없이 강제 갱신 시도") + parser.add_argument( + "--refresh", + action="store_true", + help="캐시 삭제 후 강제 갱신 (1일1회 한도 주의 — 평소 사용 금지)", + ) args = parser.parse_args() + _dl = _session_cover_deadline() print("\n" + "=" * 55) print(" KIS 토큰 상태") + print(f" 세션커버 기준: 당일 {_dl.strftime('%H:%M')} 까지 유효해야 재사용") print("=" * 55) for is_mock, label in [(False, "실전"), (True, "모의")]: s = get_token_status(is_mock) - flag = "✅ 유효" if s["valid"] else "❌ 만료/없음" + if s["valid"]: + flag = "✅ 세션커버" + elif s.get("usable"): + flag = "⚠️ 사용가능·세션미커버" + else: + flag = "❌ 만료/없음" print(f" {label:4s} | {flag} | 만료: {s['expires']} ({s['expires_in_h']:+.1f}h)") print("=" * 55) diff --git a/kis_trader/backtest/backtest_portfolio_common.py b/kis_trader/backtest/backtest_portfolio_common.py index a37435d..724d11a 100644 --- a/kis_trader/backtest/backtest_portfolio_common.py +++ b/kis_trader/backtest/backtest_portfolio_common.py @@ -915,5 +915,9 @@ def resolve_trigger_snapshots_for_backtest( if not pg: pg = pg_loaded if snap_meta.get("log_verdict_by_code") and engine_params is not None: - engine_params["_backtest_log_verdict_by_code"] = snap_meta["log_verdict_by_code"] + if not engine_params.get("_backtest_disable_log_orderbook_verdict"): + if str(snap_meta.get("orderbook_history_source") or "").lower() != "ls": + engine_params["_backtest_log_verdict_by_code"] = snap_meta[ + "log_verdict_by_code" + ] return (ob or None), (pg or None), snap_meta diff --git a/kis_trader/backtest/breakout_backtest_common.py b/kis_trader/backtest/breakout_backtest_common.py index 397e519..f2cb33f 100644 --- a/kis_trader/backtest/breakout_backtest_common.py +++ b/kis_trader/backtest/breakout_backtest_common.py @@ -3,11 +3,12 @@ 돌파매매 백테스트 공통 로더 — backtest_web / param_search 가 동일한 캔들·유니버스·손익 계산을 쓰도록 단일 진입점. -청산: ``check_sell_signal_breakout_live`` — EOD → 익절 → 어깨 → 손절 → 트레일. +청산: ``check_sell_signal_breakout_live`` — EOD → 익절 → 어깨 → 호가컷/손절호가(OFF) → 손절 → 트레일. """ from __future__ import annotations from typing import Any, Dict, List, Optional, Tuple +import os from kis_trader.backtest.backtest_portfolio_common import ( attach_scalp_trade_pnl, @@ -209,6 +210,23 @@ def get_breakout_defaults_from_env_row(env: Dict[str, Any]) -> Dict[str, Any]: "max_spread_pct": pick( ("BREAKOUT_ORDERBOOK_MAX_SPREAD_PCT",), 0.45, float, ), + # 수익구간 호가매도 / 손절호가 (UI: 이익·손실은 % 표시 · 기본 OFF) + "exit_ob_enabled": ( + str(env.get("BREAKOUT_EXIT_OB_ENABLED") or "").strip().lower() + in ("1", "true", "t", "y", "yes", "on") + ), + "exit_ob_ratio_min": pick(("BREAKOUT_EXIT_OB_RATIO_MIN",), 0.4, float), + "exit_ob_ma_window": pick(("BREAKOUT_EXIT_OB_MA_WINDOW",), 5, lambda v: int(float(v))), + "exit_ob_min_profit_pct": pct_ui(pick(("BREAKOUT_EXIT_OB_MIN_PROFIT_PCT",), 0.005, float)), + "exit_ob_min_hold_bars": pick(("BREAKOUT_EXIT_OB_MIN_HOLD_BARS",), 3, lambda v: int(float(v))), + "stop_ob_enabled": ( + str(env.get("BREAKOUT_STOP_OB_ENABLED") or "").strip().lower() + in ("1", "true", "t", "y", "yes", "on") + ), + "stop_ob_ratio_min": pick(("BREAKOUT_STOP_OB_RATIO_MIN",), 0.4, float), + "stop_ob_ma_window": pick(("BREAKOUT_STOP_OB_MA_WINDOW",), 5, lambda v: int(float(v))), + "stop_ob_min_loss_pct": pct_ui(pick(("BREAKOUT_STOP_OB_MIN_LOSS_PCT",), 0.003, float)), + "stop_ob_min_hold_bars": pick(("BREAKOUT_STOP_OB_MIN_HOLD_BARS",), 2, lambda v: int(float(v))), } @@ -362,6 +380,8 @@ def load_breakout_candles_by_code( db, start_key, end_key, min_bars=min_bars, ) + candle_src = os.environ.get("CANDLE_SOURCE", "kis") or "kis" + ind_cols = ws_candles_select_indicator_cols(db) codes_raw = db.conn.execute( "SELECT DISTINCT code FROM ws_candles WHERE timeframe=1 " @@ -375,10 +395,10 @@ def load_breakout_candles_by_code( for code in codes: rows = db.conn.execute( - f"SELECT candle_time, open, high, low, close, volume{ind_cols} " - "FROM ws_candles WHERE timeframe=1 AND code=%s " - "AND candle_time >= %s AND candle_time <= %s AND is_confirmed=1 " - "ORDER BY candle_time ASC", + f"SELECT candle_time, open, high, low, close, volume {ind_cols} " + f"FROM ws_candles WHERE timeframe=1 AND code=%s " + f"AND candle_time >= %s AND candle_time <= %s AND is_confirmed=1 " + f"ORDER BY candle_time ASC", [code, start_key, end_key], ).fetchall() if len(rows) < min_bars: diff --git a/kis_trader/backtest/breakout_portfolio_backtest.py b/kis_trader/backtest/breakout_portfolio_backtest.py index aa8bdd4..6189c40 100644 --- a/kis_trader/backtest/breakout_portfolio_backtest.py +++ b/kis_trader/backtest/breakout_portfolio_backtest.py @@ -326,10 +326,29 @@ def run_breakout_backtest_portfolio( minute_ticks = ( collect_minute_ticks(ticks_by_code, code, t) if use_tick_exit else None ) + # 수익구간·손절호가: LS(또는 history_source) 호가 OR (둘 중 ON일 때만 · 기본 OFF) + from kis_trader.engine.momentum_hts_logic import ( + collect_exit_ob_or_history, + need_ob_or_history, + _ob_or_ma_window_for_history, + ) + sell_params = dict(params) + if need_ob_or_history(params) and orderbook_by_code: + ors = collect_exit_ob_or_history( + orderbook_by_code, + code, + entry_time=str(pos.get("entry_time") or ""), + asof_time=t, + ma_window=_ob_or_ma_window_for_history(params), + ) + pos["_ob_or_history"] = ors + sell_params["_ob_or_history"] = list(ors) + else: + sell_params["_ob_or_history"] = list(pos.get("_ob_or_history") or []) res = resolve_backtest_sell( pos, bar, - params, + sell_params, is_eod=is_eod, sell_fn=check_sell_signal_breakout_live, low_mode="current", diff --git a/kis_trader/backtest/breakout_tick_loader.py b/kis_trader/backtest/breakout_tick_loader.py index 2eb81a6..c43c5c8 100644 --- a/kis_trader/backtest/breakout_tick_loader.py +++ b/kis_trader/backtest/breakout_tick_loader.py @@ -3,6 +3,7 @@ kis_trader/backtest/breakout_tick_loader.py — ws_ticks 로드·분봉 인덱 """ from __future__ import annotations +import os from collections import defaultdict from datetime import datetime, timedelta from typing import Any, Dict, Iterator, List, Optional, Set, Tuple @@ -113,6 +114,12 @@ def _fetch_ws_ticks_day_rows( code_filter = f" AND code IN ({placeholders})" params.extend(sorted(codes)) + tick_source = os.environ.get("TICK_SOURCE", "").strip() + source_filter = "" + if tick_source: + source_filter = " AND source = %s" + params.append(tick_source) + sql = f""" SELECT code, tick_time, price, volume, source FROM {table} @@ -120,6 +127,7 @@ def _fetch_ws_ticks_day_rows( AND tick_time >= %s AND tick_time <= %s {code_filter} + {source_filter} """ last_err: Optional[BaseException] = None for attempt in range(1, max_retries + 1): diff --git a/kis_trader/backtest/momentum_portfolio_backtest.py b/kis_trader/backtest/momentum_portfolio_backtest.py index 43a36de..f45500c 100644 --- a/kis_trader/backtest/momentum_portfolio_backtest.py +++ b/kis_trader/backtest/momentum_portfolio_backtest.py @@ -269,6 +269,7 @@ def _process_sells_for_scan( *, params: Dict[str, Any], ticks_by_code: Optional[Dict[str, Dict[str, List[Dict]]]], + orderbook_by_code: Optional[Dict[str, Dict[str, List[Any]]]] = None, all_trades: List[Dict], tick_exit_count: int, ohlc_exit_count: int, @@ -276,6 +277,12 @@ def _process_sells_for_scan( scan_sec: int, ) -> Tuple[int, int, int]: """스캔 시각까지 틱·벽시계 last 청산 (실매 루프: 매도 먼저).""" + from kis_trader.engine.momentum_hts_logic import ( + collect_exit_ob_or_history, + need_ob_or_history, + _ob_or_ma_window_for_history, + ) + bar_t = scan_key[:12] is_eod = is_strategy_eod_bar(bar_t, params, "MOMENTUM") use_wall = momentum_backtest_wallclock_last_price(params) @@ -291,6 +298,23 @@ def _process_sells_for_scan( continue entry_time = str(pos.get("entry_time") or "") + # 수익구간·손절호가: 틱 청산 경로에서도 OR 히스토리 필요 (둘 중 ON일 때만 · 기본 OFF) + sell_params = params + if need_ob_or_history(params) and orderbook_by_code: + ors = collect_exit_ob_or_history( + orderbook_by_code, + code, + entry_time=entry_time, + asof_time=bar_t, + ma_window=_ob_or_ma_window_for_history(params), + ) + pos["_ob_or_history"] = ors + sell_params = dict(params) + sell_params["_ob_or_history"] = list(ors) + else: + sell_params = dict(params) + sell_params["_ob_or_history"] = list(pos.get("_ob_or_history") or []) + minute_ticks = [] if momentum_backtest_use_tick_exit(params) and ticks_by_code: minute_ticks = collect_minute_ticks(ticks_by_code, code, bar_t) @@ -348,7 +372,7 @@ def _process_sells_for_scan( ] if capped: tick_res = try_momentum_sell_on_ticks( - pos, capped, params, is_eod=is_eod, entry_time=entry_time, + pos, capped, sell_params, is_eod=is_eod, entry_time=entry_time, ) if tick_res: reason, fill_px, sell_time, hold_min = tick_res @@ -391,10 +415,11 @@ def _process_sells_for_scan( "candle_time": bar_t, } sell_res = resolve_momentum_sell_for_bar( - pos, cur_c_info, params, + pos, cur_c_info, sell_params, is_eod=is_eod, ticks_by_code=ticks_by_code, code=code, + orderbook_by_code=orderbook_by_code, ) if not sell_res: continue @@ -688,6 +713,7 @@ def run_momentum_backtest_portfolio( portfolio, ctx_by_code, scan_key, params=params, ticks_by_code=ticks_by_code, + orderbook_by_code=orderbook_by_code, all_trades=all_trades, tick_exit_count=tick_exit_count, ohlc_exit_count=ohlc_exit_count, @@ -836,6 +862,7 @@ def run_momentum_backtest_portfolio( is_eod=is_eod, ticks_by_code=ticks_by_code, code=code, + orderbook_by_code=orderbook_by_code, ) if not sell_res: continue diff --git a/kis_trader/backtest/optuna_breakout_tpe_space.py b/kis_trader/backtest/optuna_breakout_tpe_space.py index 90d3802..2ffe3f6 100644 --- a/kis_trader/backtest/optuna_breakout_tpe_space.py +++ b/kis_trader/backtest/optuna_breakout_tpe_space.py @@ -50,7 +50,7 @@ def breakout_tpe_axis_keys() -> List[str]: def suggest_breakout_params_tpe(trial: optuna.Trial) -> Dict[str, Any]: combo: Dict[str, Any] = {} combo["max_daily_chg"] = r1(trial.suggest_float("max_daily_chg", 20.0, 55.0, step=1.0)) - combo["vol_mult"] = r2(trial.suggest_float("vol_mult", 1.5, 4.0, step=0.1)) + combo["vol_mult"] = r2(trial.suggest_float("vol_mult", 1.0, 5.0, step=0.1)) # 확장: 1.5→1.0, 4.0→5.0 combo["vol_window"] = trial.suggest_int("vol_window", 1, 15) combo["min_turnover_1m_pct"] = r2( trial.suggest_float("min_turnover_1m_pct", 0.05, 0.5, step=0.05), @@ -61,12 +61,12 @@ def suggest_breakout_params_tpe(trial: optuna.Trial) -> Dict[str, Any]: raise optuna.TrialPruned("prev_chg invalid") combo["min_price"] = trial.suggest_int("min_price", 1000, 5000, step=500) - combo["tp_pct"] = r2(trial.suggest_float("tp_pct", 2.0, 18.0, step=0.5)) - combo["sl_pct"] = r2(trial.suggest_float("sl_pct", 2.0, 6.0, step=0.1)) + combo["tp_pct"] = r2(trial.suggest_float("tp_pct", 1.0, 25.0, step=0.5)) # 확장: 2.0→1.0, 18.0→25.0 + combo["sl_pct"] = r2(trial.suggest_float("sl_pct", 1.0, 8.0, step=0.1)) # 확장: 2.0→1.0, 6.0→8.0 combo["sl_mode"] = trial.suggest_categorical("sl_mode", _SL_MODE_CHOICES) combo["atr_sl_mult"] = r2(trial.suggest_float("atr_sl_mult", 1.5, 3.5, step=0.1)) - combo["trail_pct"] = r2(trial.suggest_float("trail_pct", 1.0, 4.0, step=0.1)) - combo["trail_arm_pct"] = r2(trial.suggest_float("trail_arm_pct", 1.0, 4.0, step=0.1)) + combo["trail_pct"] = r2(trial.suggest_float("trail_pct", 0.0, 6.0, step=0.1)) # 확장: 1.0→0.0, 4.0→6.0 + combo["trail_arm_pct"] = r2(trial.suggest_float("trail_arm_pct", 0.0, 6.0, step=0.1)) # 확장: 1.0→0.0, 4.0→6.0 combo["shoulder_min_high_pct"] = r2( trial.suggest_float("shoulder_min_high_pct", 1.0, 6.0, step=0.1), ) @@ -92,5 +92,6 @@ def suggest_breakout_params_tpe(trial: optuna.Trial) -> Dict[str, Any]: ) combo["body_min_pct"] = r2(trial.suggest_float("body_min_pct", 0.0, 0.5, step=0.1)) combo["max_hold_bars"] = trial.suggest_int("max_hold_bars", 0, 180, step=10) + combo["skip_hts_scan_dupes"] = False return combo diff --git a/kis_trader/backtest/optuna_common.py b/kis_trader/backtest/optuna_common.py index a7f911d..df461dd 100644 --- a/kis_trader/backtest/optuna_common.py +++ b/kis_trader/backtest/optuna_common.py @@ -546,7 +546,7 @@ def ensure_optuna_database(db_name: Optional[str] = None) -> str: creds["user"], creds["host"], creds["port"], name, ) else: - logger.info( + logger.debug( "📦 Optuna storage DB: %s@%s:%s/%s", creds["user"], creds["host"], creds["port"], name, ) diff --git a/kis_trader/backtest/optuna_mode_combo.py b/kis_trader/backtest/optuna_mode_combo.py index fb4f1fe..c25ad7d 100644 --- a/kis_trader/backtest/optuna_mode_combo.py +++ b/kis_trader/backtest/optuna_mode_combo.py @@ -275,4 +275,24 @@ def enrich_out_data_with_mode_combo( attach_daily_trail_recommend(out_data, log=lg) except Exception as exc: lg.warning("⚠️ daily_trail_recommend 첨부 실패: %s", exc) + + # 호가 수급 합의 추천 (탐색 축 아님 — apply/미리보기 전용 1000회 핀셋 탐색) + try: + from kis_trader.backtest.optuna_orderbook_recommend import ( + attach_orderbook_recommend, + ) + + attach_orderbook_recommend(out_data, log=lg) + except Exception as exc: + lg.warning("⚠️ orderbook_recommend 첨부 실패: %s", exc) + + # 휩쏘 필터 합의 추천 + try: + from kis_trader.backtest.optuna_whipsaw_recommend import ( + attach_whipsaw_recommend, + ) + attach_whipsaw_recommend(out_data, log=lg) + except Exception as exc: + lg.warning("⚠️ whipsaw_recommend 첨부 실패: %s", exc) + return out_data diff --git a/kis_trader/backtest/optuna_momentum_tpe_space.py b/kis_trader/backtest/optuna_momentum_tpe_space.py index e3ac6df..2f77992 100644 --- a/kis_trader/backtest/optuna_momentum_tpe_space.py +++ b/kis_trader/backtest/optuna_momentum_tpe_space.py @@ -85,7 +85,7 @@ def suggest_momentum_params_tpe( combo["mom_vol_win"] = trial.suggest_int("mom_vol_win", 3, 10) # HTS K: 전일종가 대비 최소등락(%) — 실매 앵커 0.2 포함 연속 텀 (step 0.1) combo["e_min_chg_pct"] = r1( - trial.suggest_float("e_min_chg_pct", 0.0, 2.0, step=0.1), + trial.suggest_float("e_min_chg_pct", 0.0, 3.0, step=0.1), # 확장: 2.0→3.0 ) combo["tp_pct"] = r2(trial.suggest_float("tp_pct", 1.5, 15.0, step=0.1)) @@ -93,7 +93,7 @@ def suggest_momentum_params_tpe( if combo["tp_max_pct"] + 1e-9 < combo["tp_pct"]: raise optuna.TrialPruned("tp_max < tp") - combo["sl_pct"] = r2(trial.suggest_float("sl_pct", 1.0, 6.0, step=0.1)) + combo["sl_pct"] = r2(trial.suggest_float("sl_pct", 0.5, 8.0, step=0.1)) # 확장: 1.0→0.5, 6.0→8.0 combo["shoulder_min_high"] = r2( trial.suggest_float("shoulder_min_high", 0.3, 5.0, step=0.1), @@ -106,7 +106,7 @@ def suggest_momentum_params_tpe( trial.suggest_float("trail_arm_pct", 0.0, 5.0, step=0.1), ) - combo["max_hold_bars"] = trial.suggest_int("max_hold_bars", 0, 180, step=5) + combo["max_hold_bars"] = trial.suggest_int("max_hold_bars", 0, 300, step=5) # 확장: 180→300 combo["cooldown_min"] = r1(trial.suggest_float("cooldown_min", 0.0, 30.0, step=1.0)) combo["max_daily"] = trial.suggest_int("max_daily", 5, 100, step=5) combo["max_daily_chg"] = r1( @@ -123,13 +123,13 @@ def suggest_momentum_params_tpe( trial.suggest_float("pullback_min_pct", 0.1, 1.0, step=0.1), ) combo["pullback_max_pct"] = r2( - trial.suggest_float("pullback_max_pct", 1.0, 6.0, step=0.1), + trial.suggest_float("pullback_max_pct", 1.0, 10.0, step=0.1), # 확장: 6.0→10.0 ) if combo["pullback_min_pct"] >= combo["pullback_max_pct"]: raise optuna.TrialPruned("pullback min >= max") combo["setup_vol_max_mult"] = r2( - trial.suggest_float("setup_vol_max_mult", 0.3, 1.5, step=0.1), + trial.suggest_float("setup_vol_max_mult", 0.5, 3.0, step=0.1), ) combo["setup_bear_bars_min"] = trial.suggest_int("setup_bear_bars_min", 0, 3) diff --git a/kis_trader/backtest/optuna_orderbook_recommend.py b/kis_trader/backtest/optuna_orderbook_recommend.py new file mode 100644 index 0000000..1b520ae --- /dev/null +++ b/kis_trader/backtest/optuna_orderbook_recommend.py @@ -0,0 +1,427 @@ +""" +kis_trader/backtest/optuna_orderbook_recommend.py +================================================= +Optuna 차트 캔들 최적화(Stage 1)가 완료된 후, 후처리(Stage 2)로 1,000회 고속 호가 탐색을 수행하여 +전략(모멘텀/돌파 등)별 최적의 진입 호가필터 & 수익구간 호가매도 합의 수치(Consensus)를 도출하고 +Optuna out_data 및 Apply 패치에 자동으로 결합하는 핵심 모듈입니다. +""" + +from __future__ import annotations + +import logging +import math +from dataclasses import dataclass +from datetime import datetime, timedelta +from typing import Any, Dict, List, Optional, Tuple + +import optuna + +from database import TradeDB + +logger = logging.getLogger("OptunaOBRecommend") +optuna.logging.set_verbosity(optuna.logging.WARNING) + + +@dataclass +class Snap: + t: datetime + total_bid: int + total_ask: int + best_bid: int + best_ask: int + + +@dataclass +class TradeInfo: + code: str + name: str + buy_dt: datetime + buy_price: float + sell_price: float + qty: int + actual_pnl: float + actual_profit_rate: float + entry_snaps: List[Snap] + holding_snaps: List[Snap] + orig_spread_pct: float + orig_bid_ask_ratio: float + passed_current: bool + + +def _parse_dt(v: Any) -> datetime: + if isinstance(v, datetime): + return v + return datetime.strptime(str(v).strip()[:19], "%Y-%m-%d %H:%M:%S") + + +def _snap_to_dt(snap_time: str) -> Optional[datetime]: + s = str(snap_time or "").strip() + if not s or len(s) < 14 or s == "None": + return None + try: + return datetime(int(s[:4]), int(s[4:6]), int(s[6:8]), int(s[8:10]), int(s[10:12]), int(s[12:14])) + except ValueError: + return None + + +def _parse_ratchet_tiers(val: str) -> List[Tuple[int, float]]: + t = [] + for p in str(val or "").strip().split(","): + if ":" in p: + parts = p.split(":") + try: + t.append((int(parts[0].strip()), float(parts[1].strip()))) + except ValueError: + pass + t.sort(key=lambda x: x[0]) + if not t: + t = [(10, 2.6), (13, 2.2)] + return t + + +def get_orderbook_table_for_strategy(strategy: str) -> str: + s = strategy.upper() + if any(p in s for p in ("BREAKOUT", "SCALP", "LS")): + return "ls_ws_orderbook" + return "ws_orderbook" + + +def recommend_orderbook_parameters( + strategy: str = "MOMENTUM", + n_trials: int = 1000, + ob_table: Optional[str] = None, + log: Optional[logging.Logger] = None, +) -> Dict[str, Any]: + lg = log or logger + strat_upper = strategy.upper() + table = ob_table or get_orderbook_table_for_strategy(strat_upper) + + db = TradeDB() + # 1. 테이블 존재 여부 및 컬럼 검사 + try: + cols = [r["Field"] for r in db.conn.execute(f"SHOW COLUMNS FROM {table}").fetchall()] + need = {"code", "snap_time", "total_bid_qty", "total_ask_qty", "best_bid", "best_ask"} + if need - set(cols): + lg.warning("⚠️ [%s] 호가 테이블 필수 컬럼 부족. 추천 생략.", table) + return {"ok": False, "reason": "insufficient_columns"} + except Exception as exc: + lg.warning("⚠️ [%s] 테이블 조회 실패: %s. 추천 생략.", table, exc) + return {"ok": False, "reason": "table_not_found"} + + date_rows = db.conn.execute(f"SELECT DISTINCT SUBSTR(snap_time, 1, 8) as dt FROM {table} ORDER BY dt").fetchall() + avail_dates = [str(r["dt"]) for r in date_rows if r["dt"] and str(r["dt"]) != "None"] + if not avail_dates: + return {"ok": False, "reason": "no_orderbook_snapshots"} + + # 2. 전략별 config (래칫/어깨) 로딩 + cfg_table = "config_breakout" if "BREAKOUT" in strat_upper else "config_momentum" + pfx = "BREAKOUT_" if "BREAKOUT" in strat_upper else "MOMENTUM_" + try: + row_cfg = db.conn.execute( + f"SELECT {pfx}RATCHET_TIERS, {pfx}SHOULDER_MIN_HIGH_PCT, {pfx}SHOULDER_CUT_PCT FROM {cfg_table} ORDER BY id DESC LIMIT 1" + ).fetchone() + cfg_dict = dict(row_cfg) if row_cfg else {} + except Exception: + cfg_dict = {} + + tiers = _parse_ratchet_tiers(str(cfg_dict.get(f"{pfx}RATCHET_TIERS") or "10:2.6,13:2.2")) + smh = float(cfg_dict.get(f"{pfx}SHOULDER_MIN_HIGH_PCT") or 0.05) + sc = float(cfg_dict.get(f"{pfx}SHOULDER_CUT_PCT") or 0.0055) + if smh > 1.0: + smh /= 100.0 + if sc > 1.0: + sc /= 100.0 + + trades: List[TradeInfo] = [] + for dt_str in avail_dates: + day_hyphen = f"{dt_str[:4]}-{dt_str[4:6]}-{dt_str[6:]}" + buys = db.conn.execute( + """ + SELECT id, code, name, buy_date, buy_price, sell_price, qty, profit_rate, realized_pnl + FROM trade_history + WHERE strategy=%s AND DATE(buy_date)=%s + ORDER BY buy_date + """, + (strat_upper, day_hyphen), + ).fetchall() + + for b in buys: + try: + buy_dt = _parse_dt(b["buy_date"]) + except Exception: + continue + buy_price = float(b["buy_price"] or 0) + code = str(b["code"]) + qty = int(b["qty"] or 0) + actual_pnl = float(b["realized_pnl"] or 0) + actual_profit_rate = float(b["profit_rate"] or 0) + + lookback = timedelta(minutes=10) + horizon = timedelta(minutes=6) + + s_rows = db.conn.execute( + f"SELECT snap_time, total_bid_qty, total_ask_qty, best_bid, best_ask FROM {table} " + "WHERE code=%s AND snap_time >= %s AND snap_time < %s ORDER BY snap_time ASC", + (code, (buy_dt - lookback).strftime("%Y%m%d%H%M%S"), (buy_dt + horizon).strftime("%Y%m%d%H%M%S")), + ).fetchall() + snaps: List[Snap] = [] + for sr in s_rows: + s_dt = _snap_to_dt(sr["snap_time"]) + if not s_dt: + continue + snaps.append( + Snap( + t=s_dt, + total_bid=int(sr["total_bid_qty"] or 0), + total_ask=int(sr["total_ask_qty"] or 0), + best_bid=int(sr["best_bid"] or 0), + best_ask=int(sr["best_ask"] or 0), + ) + ) + + if not snaps or buy_price <= 0 or qty <= 0: + continue + + before = [s for s in snaps if s.t <= buy_dt] + entry_snaps = before if before else snaps + holding_snaps = [s for s in snaps if s.t > buy_dt] + + # 진입 호가 지표 산정 + best_entry_snap = entry_snaps[-1] + bid, ask = best_entry_snap.best_bid, best_entry_snap.best_ask + spread_pct = ((ask - bid) / ((ask + bid) / 2.0)) * 100.0 if (ask > 0 and bid > 0) else 0.0 + tot_b, tot_a = best_entry_snap.total_bid, best_entry_snap.total_ask + ratio = (tot_b / tot_a) if tot_a > 0 else 999.0 + + trades.append( + TradeInfo( + code=code, + name=str(b.get("name") or code), + buy_dt=buy_dt, + buy_price=buy_price, + sell_price=float(b["sell_price"] or 0), + qty=qty, + actual_pnl=actual_pnl, + actual_profit_rate=actual_profit_rate, + entry_snaps=entry_snaps, + holding_snaps=holding_snaps, + orig_spread_pct=spread_pct, + orig_bid_ask_ratio=ratio, + passed_current=(spread_pct <= 0.45 and ratio >= 0.85), + ) + ) + + if len(trades) < 3: + lg.warning("⚠️ [%s] 호가 연제 가능한 실제 매수 건수(%s건)가 부족하여 최적화 생략.", strat_upper, len(trades)) + return {"ok": False, "reason": "not_enough_trades", "trade_count": len(trades)} + + # 원본 실측값 + orig_cnt = len(trades) + orig_win = sum(1 for t in trades if t.actual_pnl > 0) / orig_cnt * 100.0 + orig_pnl = sum(t.actual_pnl for t in trades) + orig_rate = sum(t.actual_profit_rate for t in trades) / orig_cnt + + def _sim_trade(tr: TradeInfo, p: Dict[str, Any]) -> Tuple[float, float, str]: + if tr.orig_spread_pct > p["max_spread_pct"] or tr.orig_bid_ask_ratio < p["min_bid_ask_ratio"]: + return (0.0, 0.0, "ENTRY_REJECTED") + + if not p["use_ob_exit"]: + return (tr.actual_pnl, tr.actual_profit_rate, "ORIGINAL") + + ma_win = p["ma_window"] + ratio_min = p["ob_ratio_min"] + min_prof = p["min_profit_pct"] + min_hold = p["min_hold_bars"] + + history_ratio: List[float] = [] + high_price = tr.buy_price + + for s in tr.holding_snaps: + cur_p = float(s.best_bid if s.best_bid > 0 else s.best_ask) + if cur_p <= 0: + continue + if cur_p > high_price: + high_price = cur_p + + r = (s.total_bid / s.total_ask) if s.total_ask > 0 else 1.0 + history_ratio.append(r) + + held_sec = (s.t - tr.buy_dt).total_seconds() + if held_sec < min_hold * 60: + continue + if len(history_ratio) < ma_win: + continue + + ma_r = sum(history_ratio[-ma_win:]) / float(ma_win) + prof_rate = (cur_p - tr.buy_price) / tr.buy_price + + if prof_rate < min_prof: + continue + + # 래칫 & 어깨 체크 + high_rate = (high_price - tr.buy_price) / tr.buy_price + if high_rate >= smh and ((high_price - cur_p) / high_price) >= sc: + continue + is_ratchet_blocked = False + for bar_m, target_r in tiers: + if (held_sec >= bar_m * 60) and (prof_rate >= (target_r / 100.0)): + is_ratchet_blocked = True + break + if is_ratchet_blocked: + continue + + if ma_r < ratio_min: + realized = (cur_p - tr.buy_price) * tr.qty + return (realized, prof_rate * 100.0, "OB_EXIT") + + return (tr.actual_pnl, tr.actual_profit_rate, "HOLD_TO_ORIG") + + def _calc_suite(p: Dict[str, Any]) -> Tuple[int, float, float, float]: + t_cnt = 0 + w_cnt = 0 + tot_pnl = 0.0 + tot_rate = 0.0 + for t in trades: + pnl, rate, rtype = _sim_trade(t, p) + if rtype != "ENTRY_REJECTED": + t_cnt += 1 + tot_pnl += pnl + tot_rate += rate + if pnl > 0: + w_cnt += 1 + w_rate = (w_cnt / t_cnt * 100.0) if t_cnt > 0 else 0.0 + avg_r = (tot_rate / t_cnt) if t_cnt > 0 else 0.0 + return t_cnt, w_rate, tot_pnl, avg_r + + valid_records: List[Dict[str, Any]] = [] + + can_use_ob_exit = strat_upper in ("MOMENTUM", "BREAKOUT") + + def obj_func(trial: optuna.Trial) -> float: + params = { + "max_spread_pct": trial.suggest_float("max_spread_pct", 0.3, 3.5, step=0.1), + "min_bid_ask_ratio": trial.suggest_float("min_bid_ask_ratio", 0.1, 1.0, step=0.05), + "use_ob_exit": trial.suggest_categorical("use_ob_exit", [True, False]) if can_use_ob_exit else False, + "min_hold_bars": trial.suggest_int("min_hold_bars", 1, 5) if can_use_ob_exit else 3, + "ob_ratio_min": trial.suggest_float("ob_ratio_min", 0.2, 0.8, step=0.05) if can_use_ob_exit else 0.4, + "min_profit_pct": trial.suggest_float("min_profit_pct", 0.003, 0.02, step=0.001) if can_use_ob_exit else 0.005, + "ma_window": trial.suggest_int("ma_window", 3, 10) if can_use_ob_exit else 5, + } + + cnt, win_r, pnl, rate = _calc_suite(params) + if cnt < max(3, int(orig_cnt * 0.3)): + return -999999999.0 + + w_p = (pnl / 100000.0) + w_w = win_r * 2.0 + score = w_p + w_w + if win_r >= 60.0: + score += (win_r - 60.0) * 1.5 + + valid_records.append({"score": score, "pnl": pnl, "win_rate": win_r, "count": cnt, "rate": rate, "params": params}) + return score + + study = optuna.create_study(direction="maximize") + study.optimize(obj_func, n_trials=n_trials) + + valid_records.sort(key=lambda x: x["score"], reverse=True) + top5 = valid_records[: min(5, len(valid_records))] + if not top5: + return {"ok": False, "reason": "no_valid_trials"} + + avg_spread = round(sum(r["params"]["max_spread_pct"] for r in top5) / len(top5), 2) + avg_bid_ask = round(sum(r["params"]["min_bid_ask_ratio"] for r in top5) / len(top5), 2) + use_ob_votes = sum(1 for r in top5 if r["params"]["use_ob_exit"]) + cons_ob_exit = use_ob_votes >= (len(top5) / 2.0) + avg_ob_ratio = round(sum(r["params"]["ob_ratio_min"] for r in top5) / len(top5), 2) + avg_hold = int(round(sum(r["params"]["min_hold_bars"] for r in top5) / len(top5))) + avg_ma = int(round(sum(r["params"]["ma_window"] for r in top5) / len(top5))) + avg_prof = round(sum(r["params"]["min_profit_pct"] for r in top5) / len(top5), 4) + + cons_params = { + "max_spread_pct": avg_spread, + "min_bid_ask_ratio": avg_bid_ask, + "use_ob_exit": cons_ob_exit, + "min_hold_bars": avg_hold, + "ob_ratio_min": avg_ob_ratio, + "min_profit_pct": avg_prof, + "ma_window": avg_ma, + } + c_cnt, c_win, c_pnl, c_rate = _calc_suite(cons_params) + + lg.info( + "⚡ [호가 수급 합의 추천] 전략=%s (모수=%d건, %d회 탐색) | 스프레드≤%.2f%% 잔량비≥%.2f | 호가익절=%s | 승률: %.1f%% 손익: %.0f원", + strat_upper, + len(trades), + n_trials, + avg_spread, + avg_bid_ask, + "ON" if cons_ob_exit else "OFF", + c_win, + c_pnl, + ) + + return { + "ok": True, + "strategy": strat_upper, + "ob_table": table, + "n_trials": n_trials, + "trade_count": len(trades), + "orig_stats": {"count": orig_cnt, "win_rate": round(orig_win, 1), "pnl": orig_pnl, "avg_rate": round(orig_rate, 2)}, + "recommended_stats": {"count": c_cnt, "win_rate": round(c_win, 1), "pnl": c_pnl, "avg_rate": round(c_rate, 2), "pnl_diff": c_pnl - orig_pnl}, + "params": { + "orderbook_filter_enabled": True, + "orderbook_max_spread_pct": avg_spread, + "orderbook_min_bid_ask_ratio": avg_bid_ask, + "exit_ob_enabled": cons_ob_exit, + "exit_ob_min_hold_bars": avg_hold, + "exit_ob_min_profit_pct": avg_prof, + "exit_ob_ratio_min": avg_ob_ratio, + "exit_ob_ma_window": avg_ma, + }, + } + + +def attach_orderbook_recommend( + out_data: Dict[str, Any], + *, + log: Optional[logging.Logger] = None, +) -> Dict[str, Any]: + """out_data에 호가 진입/청산 합의 수치 추천(orderbook_recommend)을 첨부.""" + lg = log or logger + strat = str(out_data.get("strategy") or "MOMENTUM").strip().upper() + rec = recommend_orderbook_parameters(strategy=strat, n_trials=1000, log=lg) + out_data["orderbook_recommend"] = rec + mc = out_data.get("mode_combo") + if isinstance(mc, dict): + mc["orderbook_recommend"] = rec + if not rec.get("ok"): + lg.info("⚡ [호가 수급 합의 추천] 생략 — %s", rec.get("reason") or "n/a") + return out_data + + +def build_orderbook_env_patch(rec: Dict[str, Any]) -> Dict[str, str]: + """호가 수급 합의 추천 결과를 DB env 패치 dict로 변환.""" + if not rec or not rec.get("ok"): + return {} + strat = str(rec.get("strategy") or "").strip().upper() + pfx = "TAIL" if strat in ("SHORT", "TAIL") else strat + p = rec.get("params", {}) + if not pfx or not p: + return {} + + patch = { + f"{pfx}_ORDERBOOK_FILTER_ENABLED": "true" if p.get("orderbook_filter_enabled", True) else "false", + f"{pfx}_ORDERBOOK_MAX_SPREAD_PCT": str(p["orderbook_max_spread_pct"]), + f"{pfx}_ORDERBOOK_MIN_BID_ASK_RATIO": str(p["orderbook_min_bid_ask_ratio"]), + } + + if pfx in ("MOMENTUM", "BREAKOUT") and "exit_ob_enabled" in p: + patch[f"{pfx}_EXIT_OB_ENABLED"] = "true" if p.get("exit_ob_enabled") else "false" + if p.get("exit_ob_enabled"): + patch[f"{pfx}_EXIT_OB_RATIO_MIN"] = str(p["exit_ob_ratio_min"]) + patch[f"{pfx}_EXIT_OB_MA_WINDOW"] = str(p["exit_ob_ma_window"]) + patch[f"{pfx}_EXIT_OB_MIN_PROFIT_PCT"] = str(p["exit_ob_min_profit_pct"]) + patch[f"{pfx}_EXIT_OB_MIN_HOLD_BARS"] = str(p["exit_ob_min_hold_bars"]) + + return patch diff --git a/kis_trader/backtest/optuna_scalping_tpe_space.py b/kis_trader/backtest/optuna_scalping_tpe_space.py index 83d8ca0..e3bb191 100644 --- a/kis_trader/backtest/optuna_scalping_tpe_space.py +++ b/kis_trader/backtest/optuna_scalping_tpe_space.py @@ -49,13 +49,13 @@ def suggest_scalp_params_tpe(trial: optuna.Trial) -> Dict[str, Any]: if combo["rsi_oversold"] >= combo["rsi_overbought"]: raise optuna.TrialPruned("rsi oversold >= overbought") - combo["sl_pct"] = r2(trial.suggest_float("sl_pct", 1.5, 4.5, step=0.1)) - combo["tp_pct"] = r2(trial.suggest_float("tp_pct", 1.5, 4.0, step=0.1)) - combo["tp_max_pct"] = r2(trial.suggest_float("tp_max_pct", 2.0, 8.0, step=0.5)) + combo["sl_pct"] = r2(trial.suggest_float("sl_pct", 1.0, 6.0, step=0.1)) # 확장: 1.5→1.0, 4.5→6.0 + combo["tp_pct"] = r2(trial.suggest_float("tp_pct", 0.5, 8.0, step=0.1)) # 확장: 1.5→0.5, 4.0→8.0 + combo["tp_max_pct"] = r2(trial.suggest_float("tp_max_pct", 1.0, 12.0, step=0.5)) # 확장: 2.0→1.0, 8.0→12.0 if combo["tp_max_pct"] + 1e-9 < combo["tp_pct"]: raise optuna.TrialPruned("tp_max < tp") - combo["drop_rate"] = r2(trial.suggest_float("drop_rate", 1.0, 6.0, step=0.1)) + combo["drop_rate"] = r2(trial.suggest_float("drop_rate", 0.5, 10.0, step=0.1)) # 확장: 1.0→0.5, 6.0→10.0 combo["shoulder_min_high"] = r2( trial.suggest_float("shoulder_min_high", 0.3, 4.0, step=0.1), ) @@ -66,7 +66,7 @@ def suggest_scalp_params_tpe(trial: optuna.Trial) -> Dict[str, Any]: combo["high_chase_thr"] = r3( trial.suggest_float("high_chase_thr", 0.95, 1.0, step=0.005), ) - combo["max_daily_chg"] = r1(trial.suggest_float("max_daily_chg", 10.0, 50.0, step=1.0)) + combo["max_daily_chg"] = r1(trial.suggest_float("max_daily_chg", 5.0, 60.0, step=1.0)) # 확장: 10→5, 50→60 combo["min_price"] = trial.suggest_int("min_price", 1000, 8000, step=500) combo["vol_mult"] = r2(trial.suggest_float("vol_mult", 0.0, 2.0, step=0.1)) combo["use_defense_filters"] = trial.suggest_categorical( @@ -80,8 +80,8 @@ def suggest_scalp_params_tpe(trial: optuna.Trial) -> Dict[str, Any]: trial.suggest_float("min_drop_pct_for_loss_cut", 0.005, 0.03, step=0.001), ) combo["min_margin"] = r2(trial.suggest_float("min_margin", 0.05, 0.5, step=0.05)) - combo["min_hold_sec"] = trial.suggest_int("min_hold_sec", 0, 120, step=10) - combo["max_daily"] = trial.suggest_int("max_daily", 10, 100, step=5) + combo["min_hold_sec"] = trial.suggest_int("min_hold_sec", 0, 300, step=10) # 확장: 120→300초 + combo["max_daily"] = trial.suggest_int("max_daily", 5, 150, step=5) # 확장: 10→5, 100→150 combo["use_macd_cross"] = False combo["skip_hts_scan_dupes"] = False diff --git a/kis_trader/backtest/optuna_tail_tpe_space.py b/kis_trader/backtest/optuna_tail_tpe_space.py index 3979bbe..8c66020 100644 --- a/kis_trader/backtest/optuna_tail_tpe_space.py +++ b/kis_trader/backtest/optuna_tail_tpe_space.py @@ -79,10 +79,10 @@ def suggest_tail_params_tpe(trial: optuna.Trial) -> Dict[str, Any]: combo["entry_mode"] = "align" combo["cand_limit"] = trial.suggest_categorical("cand_limit", [0, 20]) combo["max_daily_change"] = r1( - trial.suggest_float("max_daily_change", 20.0, 45.0, step=1.0), + trial.suggest_float("max_daily_change", 10.0, 60.0, step=1.0), # 확장: 20→10, 45→60 ) combo["min_drop_rate"] = r3( - trial.suggest_float("min_drop_rate", 0.01, 0.08, step=0.005), + trial.suggest_float("min_drop_rate", 0.005, 0.15, step=0.005), # 확장: 0.01→0.005, 0.08→0.15 ) combo["min_recovery_ratio"] = r2( trial.suggest_float("min_recovery_ratio", 0.08, 0.35, step=0.01), @@ -103,9 +103,9 @@ def suggest_tail_params_tpe(trial: optuna.Trial) -> Dict[str, Any]: trial.suggest_float("target_atr_mult", 0.8, 3.5, step=0.1), ) combo["atr_sl_min_pct"] = r2(trial.suggest_float("atr_sl_min_pct", 0.3, 1.0, step=0.1)) - combo["atr_sl_max_pct"] = r1(trial.suggest_float("atr_sl_max_pct", 1.0, 8.0, step=0.5)) - combo["atr_tp_min_pct"] = r2(trial.suggest_float("atr_tp_min_pct", 0.2, 1.0, step=0.1)) - combo["atr_tp_max_pct"] = r1(trial.suggest_float("atr_tp_max_pct", 1.5, 6.0, step=0.5)) + combo["atr_sl_max_pct"] = r1(trial.suggest_float("atr_sl_max_pct", 1.0, 12.0, step=0.5)) # 확장: 8.0→12.0 + combo["atr_tp_min_pct"] = r2(trial.suggest_float("atr_tp_min_pct", 0.1, 2.0, step=0.1)) # 확장: 0.2→0.1 + combo["atr_tp_max_pct"] = r1(trial.suggest_float("atr_tp_max_pct", 1.0, 10.0, step=0.5)) # 확장: 6.0→10.0 combo["tail_vol_mult"] = r2(trial.suggest_float("tail_vol_mult", 0.0, 4.0, step=0.1)) combo["tail_vol_win"] = trial.suggest_int("tail_vol_win", 2, 8) @@ -134,8 +134,8 @@ def suggest_tail_params_tpe(trial: optuna.Trial) -> Dict[str, Any]: "reentry_min_edge_krw", 0, 1000, step=100, ) combo["reentry_require_nonneg"] = False - combo["max_daily"] = trial.suggest_int("max_daily", 5, 60, step=5) - combo["cooldown_min"] = r1(trial.suggest_float("cooldown_min", 0.0, 15.0, step=1.0)) + combo["max_daily"] = trial.suggest_int("max_daily", 3, 80, step=5) # 확장: 5→3, 60→80 + combo["cooldown_min"] = r1(trial.suggest_float("cooldown_min", 0.0, 30.0, step=1.0)) # 확장: 15→30 combo["bar_chg_min_pct"] = r1( trial.suggest_float("bar_chg_min_pct", -15.0, -3.0, step=0.5), ) diff --git a/kis_trader/backtest/optuna_web_jobs.py b/kis_trader/backtest/optuna_web_jobs.py index 830534f..b228343 100644 --- a/kis_trader/backtest/optuna_web_jobs.py +++ b/kis_trader/backtest/optuna_web_jobs.py @@ -199,17 +199,22 @@ def _tail_text(path: Optional[str], n: int = 40) -> str: return "" -def _parse_result_paths_from_log(log_path: str) -> Dict[str, Optional[str]]: - tail = _tail_text(log_path, 80) - rj = None - bm = None - m = re.search(r"OPTUNA_RESULT_JSON=(.+)", tail) - if m: - rj = m.group(1).strip() - m2 = re.search(r"OPTUNA_BRIEFING_MD=(.+)", tail) - if m2: - bm = m2.group(1).strip() - return {"result_json": rj, "briefing_md": bm} +def _parse_result_paths_from_log(log_path: str) -> Dict[str, Any]: + tail = _tail_text(log_path, 800) # 다중 전략일 경우 수백 줄 위에 있을 수 있음 + rj_list = [] + bm_list = [] + + for m in re.finditer(r"OPTUNA_RESULT_JSON=(.+)", tail): + rj_list.append(m.group(1).strip()) + for m in re.finditer(r"OPTUNA_BRIEFING_MD=(.+)", tail): + bm_list.append(m.group(1).strip()) + + return { + "result_json": rj_list[-1] if rj_list else None, + "result_jsons": rj_list, + "briefing_md": bm_list[-1] if bm_list else None, + "briefing_mds": bm_list, + } def _study_progress(study_name: str, trials_total: int) -> Dict[str, Any]: @@ -407,6 +412,10 @@ def get_candidate_detail( result_json: Optional[str] = None, source: str = "gated", rank: int = 1, + hist_src: str = "", + candle_source: str = "", + tick_source: str = "", + ob_source: str = "", ) -> Dict[str, Any]: """보기용: gated/learn/mode 후보 메트릭 + params 미리보기.""" path = result_json @@ -764,8 +773,10 @@ def refresh_job_status(meta: Dict[str, Any]) -> Dict[str, Any]: paths = _parse_result_paths_from_log(log_path) if log_path else {} if paths.get("result_json"): m["result_json"] = paths["result_json"] + m["result_jsons"] = paths.get("result_jsons", []) if paths.get("briefing_md"): m["briefing_md"] = paths["briefing_md"] + m["briefing_mds"] = paths.get("briefing_mds", []) prog = _study_progress(str(m.get("study_name") or ""), int(m.get("trials") or 0)) # 순차(seq/seq4): 마스터 로그에서 현재 전략 힌트 @@ -940,6 +951,8 @@ def start_optuna_job( mode: str = "tpe", symbol: Optional[str] = None, universe_history_source: Optional[str] = None, + tick_source: Optional[str] = None, + ob_source: Optional[str] = None, ) -> Dict[str, Any]: """ subprocess 로 Optuna 시작. apply-best 없음. @@ -1017,6 +1030,12 @@ def start_optuna_job( env["MIN_TRADES"] = "1" env["STRATEGIES"] = " ".join(picked) env["UNIVERSE_HISTORY_SOURCE"] = hist_src + if candle_source: + env["CANDLE_SOURCE"] = candle_source + if tick_source: + env["TICK_SOURCE"] = tick_source + if ob_source: + env["OB_SOURCE"] = ob_source kind = "seq" label = "순차(" + "+".join(_labels.get(s, s) for s in picked) + ")" strat_field = ",".join(picked) @@ -1052,6 +1071,15 @@ def start_optuna_job( "--sort-by", sort_by, "--universe-history-source", hist_src, ] + if candle_source: + cmd.extend(["--candle-source", candle_source]) + env["CANDLE_SOURCE"] = candle_source + if tick_source: + cmd.extend(["--tick-source", tick_source]) + env["TICK_SOURCE"] = tick_source + if ob_source: + cmd.extend(["--ob-source", ob_source]) + env["OB_SOURCE"] = ob_source if sym and strat == "us_momentum": cmd.extend(["--symbol", sym]) kind = "single" diff --git a/kis_trader/backtest/optuna_whipsaw_recommend.py b/kis_trader/backtest/optuna_whipsaw_recommend.py new file mode 100644 index 0000000..d10627b --- /dev/null +++ b/kis_trader/backtest/optuna_whipsaw_recommend.py @@ -0,0 +1,267 @@ +""" +kis_trader/backtest/optuna_whipsaw_recommend.py +================================================= +Optuna 차트 캔들 최적화 완료 후, 후처리로 고속 휩쏘 파라미터 탐색을 수행하여 +전략별 최적의 휩쏘 필터 수치(Consensus)를 도출하고 +Optuna out_data 및 Apply 패치에 자동으로 결합하는 모듈입니다. +""" + +from __future__ import annotations + +import logging +from dataclasses import dataclass +from datetime import datetime, timedelta +from typing import Any, Dict, List, Optional, Tuple + +import optuna + +from database import TradeDB +from kis_trader.engine.whipsaw_filter import whipsaw_reject_for_signal + +logger = logging.getLogger("OptunaWhipsawRecommend") +optuna.logging.set_verbosity(optuna.logging.WARNING) + + +@dataclass +class TradeInfo: + code: str + name: str + buy_dt: datetime + buy_price: float + actual_pnl: float + actual_profit_rate: float + ticks: List[Dict[str, Any]] + + +def recommend_whipsaw_parameters( + *, + strategy: str = "MOMENTUM", + n_trials: int = 500, + days: int = 7, + log: Optional[logging.Logger] = None, +) -> Dict[str, Any]: + lg = log or logger + strat_upper = str(strategy).strip().upper() + + db = TradeDB() + try: + now = datetime.now() + start_date = (now - timedelta(days=days)).strftime("%Y-%m-%d") + + buys = db.conn.execute( + """ + SELECT id, code, name, buy_date, buy_price, realized_pnl, profit_rate + FROM trade_history + WHERE strategy=%s AND DATE(buy_date) >= %s + ORDER BY buy_date + """, + (strat_upper, start_date), + ).fetchall() + + trades: List[TradeInfo] = [] + for b in buys: + code = b["code"] + raw_dt = b["buy_date"] + if isinstance(raw_dt, str): + try: + buy_dt = datetime.strptime(raw_dt, "%Y-%m-%d %H:%M:%S") + except ValueError: + buy_dt = datetime.fromisoformat(raw_dt) + else: + buy_dt = raw_dt + + buy_price = float(b["buy_price"] or 0) + pnl = float(b["realized_pnl"] or 0) + rate = float(b["profit_rate"] or 0) + + start_tick_time = (buy_dt - timedelta(seconds=180)).strftime("%Y%m%d%H%M%S") + end_tick_time = buy_dt.strftime("%Y%m%d%H%M%S") + + ticks = db.get_ws_ticks(code, market="KR", start_tick_time=start_tick_time, end_tick_time=end_tick_time) + if not ticks: + continue + + trades.append( + TradeInfo( + code=code, + name=str(b.get("name") or code), + buy_dt=buy_dt, + buy_price=buy_price, + actual_pnl=pnl, + actual_profit_rate=rate, + ticks=ticks, + ) + ) + + finally: + db.close() + + if len(trades) < 3: + lg.warning("⚠️ [%s] 휩쏘 연산 가능한 실제 틱 보유 매수 건수(%s건)가 부족하여 최적화 생략.", strat_upper, len(trades)) + return {"ok": False, "reason": "not_enough_trades", "trade_count": len(trades)} + + orig_cnt = len(trades) + orig_win = sum(1 for t in trades if t.actual_pnl > 0) / orig_cnt * 100.0 + orig_pnl = sum(t.actual_pnl for t in trades) + orig_rate = sum(t.actual_profit_rate for t in trades) / orig_cnt + + def _sim_trade(tr: TradeInfo, p: Dict[str, Any]) -> Tuple[float, float, str]: + params_for_eval = { + "whipsaw_filter_enabled": True, + "whipsaw_subbar_sec": p["subbar_sec"], + "whipsaw_lookback_sec": p["lookback_sec"], + "whipsaw_dip_pct": p["dip_pct"], + "whipsaw_recovery_tol_pct": p.get("recov_pct", 0.0), + } + + sig_bar = {"low": tr.buy_price, "dt": tr.buy_dt} + reject_reason, _ = whipsaw_reject_for_signal( + params=params_for_eval, + strategy=strat_upper, + signal_bar=sig_bar, + current_price=tr.buy_price, + ticks=tr.ticks + ) + + if reject_reason: + return (0.0, 0.0, "ENTRY_REJECTED") + + return (tr.actual_pnl, tr.actual_profit_rate, "ORIGINAL") + + def _calc_suite(p: Dict[str, Any]) -> Tuple[int, float, float, float]: + t_cnt = 0 + w_cnt = 0 + tot_pnl = 0.0 + tot_rate = 0.0 + for t in trades: + pnl, rate, rtype = _sim_trade(t, p) + if rtype != "ENTRY_REJECTED": + t_cnt += 1 + tot_pnl += pnl + tot_rate += rate + if pnl > 0: + w_cnt += 1 + w_rate = (w_cnt / t_cnt * 100.0) if t_cnt > 0 else 0.0 + avg_r = (tot_rate / t_cnt) if t_cnt > 0 else 0.0 + return t_cnt, w_rate, tot_pnl, avg_r + + valid_records: List[Dict[str, Any]] = [] + + def obj_func(trial: optuna.Trial) -> float: + params = { + "subbar_sec": trial.suggest_categorical("subbar_sec", [10, 15, 20, 30, 45, 60]), + "lookback_sec": trial.suggest_categorical("lookback_sec", [30, 45, 60, 90, 120, 180]), + "dip_pct": trial.suggest_float("dip_pct", 0.001, 0.010, step=0.001), + } + + cnt, win_r, pnl, rate = _calc_suite(params) + if cnt < max(3, int(orig_cnt * 0.3)): + return -999999999.0 + + w_p = (pnl / 100000.0) + w_w = win_r * 2.0 + score = w_p + w_w + if win_r >= 60.0: + score += (win_r - 60.0) * 1.5 + + valid_records.append({"score": score, "pnl": pnl, "win_rate": win_r, "count": cnt, "rate": rate, "params": params}) + return score + + study = optuna.create_study(direction="maximize") + study.optimize(obj_func, n_trials=n_trials) + + valid_records.sort(key=lambda x: x["score"], reverse=True) + top5 = valid_records[: min(5, len(valid_records))] + if not top5: + return {"ok": False, "reason": "no_valid_trials"} + + # Consensus 도출 + best = top5[0] + avg_subbar = int(sum(r["params"]["subbar_sec"] for r in top5) / len(top5)) + avg_lookback = int(sum(r["params"]["lookback_sec"] for r in top5) / len(top5)) + avg_dip = round(sum(r["params"]["dip_pct"] for r in top5) / len(top5), 4) + + cons_params = { + "subbar_sec": avg_subbar, + "lookback_sec": avg_lookback, + "dip_pct": avg_dip, + } + c_cnt, c_win, c_pnl, c_rate = _calc_suite(cons_params) + + lg.info( + "⚡ [휩쏘 필터 합의 추천] 전략=%s (모수=%d건, %d회 탐색) | subbar=%d lookback=%d dip=%.3f | 승률: %.1f%% 손익: %.0f원", + strat_upper, + len(trades), + n_trials, + avg_subbar, + avg_lookback, + avg_dip, + c_win, + c_pnl, + ) + + return { + "ok": True, + "strategy": strat_upper, + "n_trials": n_trials, + "trade_count": len(trades), + "orig_stats": {"count": orig_cnt, "win_rate": round(orig_win, 1), "pnl": orig_pnl, "avg_rate": round(orig_rate, 2)}, + "recommended_stats": {"count": c_cnt, "win_rate": round(c_win, 1), "pnl": c_pnl, "avg_rate": round(c_rate, 2), "pnl_diff": c_pnl - orig_pnl}, + "params": { + "whipsaw_filter_enabled": True, + "whipsaw_subbar_sec": avg_subbar, + "whipsaw_lookback_sec": avg_lookback, + "whipsaw_dip_pct": avg_dip, + }, + } + + +def attach_whipsaw_recommend( + out_data: Dict[str, Any], + *, + log: Optional[logging.Logger] = None, +) -> Dict[str, Any]: + """out_data에 휩쏘 필터 추천 결과를 첨부.""" + lg = log or logger + strat = str(out_data.get("strategy") or "MOMENTUM").strip().upper() + rec = recommend_whipsaw_parameters(strategy=strat, n_trials=500, log=lg) + out_data["whipsaw_recommend"] = rec + mc = out_data.get("mode_combo") + if isinstance(mc, dict): + mc["whipsaw_recommend"] = rec + if not rec.get("ok"): + lg.info("⚡ [휩쏘 필터 합의 추천] 생략 — %s", rec.get("reason") or "n/a") + return out_data + + +_WHIPSAW_DB_SKIP_STRATS = {"TAIL", "SHORT", "BREAKOUT"} + + +def build_whipsaw_env_patch(rec: Dict[str, Any]) -> Dict[str, str]: + """휩쏘 추천 결과를 DB env 패치 dict로 변환.""" + if not rec or not rec.get("ok"): + return {} + + strat = str(rec.get("strategy") or "").strip().upper() + pfx = "TAIL" if strat in ("SHORT", "TAIL") else strat + p = rec.get("params", {}) + if not pfx or not p: + return {} + + if strat in _WHIPSAW_DB_SKIP_STRATS: + logger.info( + "🚫 [%s] 휩쏘 필터 DB 적용 차단 (전략 특성상 UI 표시만) — 수치: subbar=%s lookback=%s dip=%s", + strat, + p.get('whipsaw_subbar_sec'), + p.get('whipsaw_lookback_sec'), + p.get('whipsaw_dip_pct'), + ) + return {} + + patch = { + f"{pfx}_WHIPSAW_FILTER_ENABLED": "true", + f"{pfx}_WHIPSAW_SUBBAR_SEC": str(p["whipsaw_subbar_sec"]), + f"{pfx}_WHIPSAW_LOOKBACK_SEC": str(p["whipsaw_lookback_sec"]), + f"{pfx}_WHIPSAW_DIP_PCT": str(p["whipsaw_dip_pct"]), + } + return patch diff --git a/kis_trader/backtest/param_search_momentum.py b/kis_trader/backtest/param_search_momentum.py index 8e5dcff..5039905 100644 --- a/kis_trader/backtest/param_search_momentum.py +++ b/kis_trader/backtest/param_search_momentum.py @@ -1811,18 +1811,20 @@ def _load_candles_for_search( for c in (codes_filter or []) if str(c).strip() } + + candle_src = os.environ.get("CANDLE_SOURCE", "kis") or "kis" if mk in ("US", "KR"): codes_raw = db.conn.execute( "SELECT DISTINCT code FROM ws_candles WHERE timeframe=1 AND market=%s " - "AND candle_time >= %s AND candle_time <= %s ORDER BY code", - [mk, start_key, end_key], + "AND candle_time >= %s AND candle_time <= %s AND source=%s ORDER BY code", + [mk, start_key, end_key, candle_src], ).fetchall() else: codes_raw = db.conn.execute( "SELECT DISTINCT code FROM ws_candles WHERE timeframe=1 " - "AND candle_time >= %s AND candle_time <= %s ORDER BY code", - [start_key, end_key] + "AND candle_time >= %s AND candle_time <= %s AND source=%s ORDER BY code", + [start_key, end_key, candle_src] ).fetchall() codes = [r["code"] for r in codes_raw] if mk == "US": @@ -1843,9 +1845,9 @@ def _load_candles_for_search( "FROM ws_candles " "WHERE timeframe=1 AND code=%s AND market=%s " "AND candle_time >= %s AND candle_time <= %s " - "AND is_confirmed=1 " + "AND is_confirmed=1 AND source=%s " "ORDER BY candle_time ASC", - [code, mk, start_key, end_key] + [code, mk, start_key, end_key, candle_src] ).fetchall() else: rows = db.conn.execute( @@ -1853,9 +1855,9 @@ def _load_candles_for_search( "FROM ws_candles " "WHERE timeframe=1 AND code=%s " "AND candle_time >= %s AND candle_time <= %s " - "AND is_confirmed=1 " + "AND is_confirmed=1 AND source=%s " "ORDER BY candle_time ASC", - [code, start_key, end_key] + [code, start_key, end_key, candle_src] ).fetchall() if len(rows) < rsi_period + 5: continue diff --git a/kis_trader/backtest/param_search_optuna.py b/kis_trader/backtest/param_search_optuna.py index 7ac5e26..9f97aed 100644 --- a/kis_trader/backtest/param_search_optuna.py +++ b/kis_trader/backtest/param_search_optuna.py @@ -769,6 +769,21 @@ def main() -> None: help="이력 테이블: kiwoom=target_candidates_history, ls=ls_candidates_history " "(기본 env BACKTEST_UNIVERSE_HISTORY_SOURCE 또는 kiwoom)", ) + parser.add_argument( + "--candle-source", default="", choices=["", "kis", "kiwoom"], + dest="candle_source", + help="캔들 소스 필터 (기본 빈문자열 = kis)", + ) + parser.add_argument( + "--tick-source", default="", choices=["", "kis", "kiwoom"], + dest="tick_source", + help="틱 소스 필터 (기본 빈문자열 = 전체 검색)", + ) + parser.add_argument( + "--ob-source", default="", choices=["", "kis", "kiwoom", "kiwoom_0d"], + dest="ob_source", + help="호가 소스 필터 (기본 빈문자열 = 전체 검색)", + ) parser.add_argument( "--orderbook-filter", default="off", choices=["off", "on", "auto"], dest="orderbook_filter", diff --git a/kis_trader/backtest/results/optuna_breakout_fast_20260807_214651.briefing.md b/kis_trader/backtest/results/optuna_breakout_fast_20260807_214651.briefing.md new file mode 100644 index 0000000..4696e90 --- /dev/null +++ b/kis_trader/backtest/results/optuna_breakout_fast_20260807_214651.briefing.md @@ -0,0 +1,22 @@ +# Optuna 브리핑 — 돌파 (fast) +기간: 2026-07-31 ~ 2026-08-07 (거래일≈8일) · trials 1/1 +탐색 게이트: WR≥0% PF≥0 · min_trades≥1 | 후보 게이트: WR≥40% PF≥1 · min_trades≥1 +- min_trades=1: **합격 인정 최소 거래 건수**(표본 부족·우연 승률 방지). 이보다 적은 조합은 학습/후보 집계에서 제외됩니다. +학습용 통과 1건 · 사후합격(results_gated) 0건 + +## 이전 장에서는 +- 실매 동기간 청산 119건 · 승률 32% · 합산 PnL -107,677원 (백테 Optuna와 숫자 달라도 정상 — 고스트·유니버스·틱 경로 차이). +- 학습 1위(사후게이트 미달 가능): trial #0 · 거래 12건 · 승률 16.7% · PF 0.16 · PnL -46,867원 +- 플러스/학습 후보는 있으나 승률·PF 사후 기준은 못 넘김 → **당장 DB 적용은 비권장.** + +## 앞으로 장에서는 +- **1~2일 Optuna best를 바로 실매에 넣지 마세요.** 다일(≥5거래일) 재검증·웹백테 1회 후 검토. +- 적용 시에는 `results_gated` 후보만. 탐색용 best(objective)와 다를 수 있습니다. +- 장이 좁아지거나 변동성이 커지면: 손절·트레일·시간창이 먼저 깨집니다. 승률 자랑보다 **손절이 살아 있는지**를 보세요. +- 현재 사후합격 0건 → 파라미터 유지(현행 DB) + 유니버스/데이터 점검이 우선입니다. + +_생성: 2026-08-07 21:48:33 (rule)_ + +## AI 코멘트 + +_(미생성: api_call_failed. 구글 키는 GEMINI_API_KEY 를 DB/환경에 넣으세요 — kis_trader/backtest/optuna_briefing.py 모듈 docstring 참고)_ diff --git a/kis_trader/backtest/results/optuna_breakout_fast_20260807_215031.briefing.md b/kis_trader/backtest/results/optuna_breakout_fast_20260807_215031.briefing.md new file mode 100644 index 0000000..0b6b2bb --- /dev/null +++ b/kis_trader/backtest/results/optuna_breakout_fast_20260807_215031.briefing.md @@ -0,0 +1,22 @@ +# Optuna 브리핑 — 돌파 (fast) +기간: 2026-07-31 ~ 2026-08-07 (거래일≈8일) · trials 2/1 +탐색 게이트: WR≥0% PF≥0 · min_trades≥1 | 후보 게이트: WR≥40% PF≥1 · min_trades≥1 +- min_trades=1: **합격 인정 최소 거래 건수**(표본 부족·우연 승률 방지). 이보다 적은 조합은 학습/후보 집계에서 제외됩니다. +학습용 통과 2건 · 사후합격(results_gated) 0건 + +## 이전 장에서는 +- 실매 동기간 청산 119건 · 승률 32% · 합산 PnL -107,677원 (백테 Optuna와 숫자 달라도 정상 — 고스트·유니버스·틱 경로 차이). +- 학습 1위(사후게이트 미달 가능): trial #0 · 거래 12건 · 승률 16.7% · PF 0.16 · PnL -46,867원 +- 플러스/학습 후보는 있으나 승률·PF 사후 기준은 못 넘김 → **당장 DB 적용은 비권장.** + +## 앞으로 장에서는 +- **1~2일 Optuna best를 바로 실매에 넣지 마세요.** 다일(≥5거래일) 재검증·웹백테 1회 후 검토. +- 적용 시에는 `results_gated` 후보만. 탐색용 best(objective)와 다를 수 있습니다. +- 장이 좁아지거나 변동성이 커지면: 손절·트레일·시간창이 먼저 깨집니다. 승률 자랑보다 **손절이 살아 있는지**를 보세요. +- 현재 사후합격 0건 → 파라미터 유지(현행 DB) + 유니버스/데이터 점검이 우선입니다. + +_생성: 2026-08-07 21:52:07 (rule)_ + +## AI 코멘트 + +_(미생성: api_call_failed. 구글 키는 GEMINI_API_KEY 를 DB/환경에 넣으세요 — kis_trader/backtest/optuna_briefing.py 모듈 docstring 참고)_ diff --git a/kis_trader/backtest/results/optuna_breakout_fast_20260808_011436.briefing.md b/kis_trader/backtest/results/optuna_breakout_fast_20260808_011436.briefing.md new file mode 100644 index 0000000..bb41a33 --- /dev/null +++ b/kis_trader/backtest/results/optuna_breakout_fast_20260808_011436.briefing.md @@ -0,0 +1,23 @@ +# Optuna 브리핑 — 돌파 (fast) +기간: 2026-08-07 ~ 2026-08-07 (거래일≈1일) · trials 200/200 +탐색 게이트: WR≥0% PF≥0 · min_trades≥1 | 후보 게이트: WR≥40% PF≥1 · min_trades≥1 +- min_trades=1: **합격 인정 최소 거래 건수**(표본 부족·우연 승률 방지). 이보다 적은 조합은 학습/후보 집계에서 제외됩니다. +학습용 통과 200건 · 사후합격(results_gated) 0건 + +## 이전 장에서는 +- 실매 동기간 청산 6건 · 승률 17% · 합산 PnL -21,406원 (백테 Optuna와 숫자 달라도 정상 — 고스트·유니버스·틱 경로 차이). +- 학습 1위(사후게이트 미달 가능): trial #80 · 거래 3건 · 승률 33.3% · PF 1.02 · PnL 154원 +- 표본 장이 1일뿐이라, ‘이전 장에 잘 먹힌 숫자’일 뿐 **추세가 바뀌면 바로 깨질 수 있습니다.** +- 플러스/학습 후보는 있으나 승률·PF 사후 기준은 못 넘김 → **당장 DB 적용은 비권장.** + +## 앞으로 장에서는 +- **1~2일 Optuna best를 바로 실매에 넣지 마세요.** 다일(≥5거래일) 재검증·웹백테 1회 후 검토. +- 적용 시에는 `results_gated` 후보만. 탐색용 best(objective)와 다를 수 있습니다. +- 장이 좁아지거나 변동성이 커지면: 손절·트레일·시간창이 먼저 깨집니다. 승률 자랑보다 **손절이 살아 있는지**를 보세요. +- 현재 사후합격 0건 → 파라미터 유지(현행 DB) + 유니버스/데이터 점검이 우선입니다. + +_생성: 2026-08-08 01:15:39 (rule)_ + +## AI 코멘트 + +_(미생성: api_call_failed. 구글 키는 GEMINI_API_KEY 를 DB/환경에 넣으세요 — kis_trader/backtest/optuna_briefing.py 모듈 docstring 참고)_ diff --git a/kis_trader/backtest/results/optuna_breakout_fast_20260808_022923.briefing.md b/kis_trader/backtest/results/optuna_breakout_fast_20260808_022923.briefing.md new file mode 100644 index 0000000..532ca1a --- /dev/null +++ b/kis_trader/backtest/results/optuna_breakout_fast_20260808_022923.briefing.md @@ -0,0 +1,23 @@ +# Optuna 브리핑 — 돌파 (fast) +기간: 2026-08-07 ~ 2026-08-07 (거래일≈1일) · trials 200/200 +탐색 게이트: WR≥0% PF≥0 · min_trades≥1 | 후보 게이트: WR≥40% PF≥1 · min_trades≥1 +- min_trades=1: **합격 인정 최소 거래 건수**(표본 부족·우연 승률 방지). 이보다 적은 조합은 학습/후보 집계에서 제외됩니다. +학습용 통과 200건 · 사후합격(results_gated) 0건 + +## 이전 장에서는 +- 실매 동기간 청산 6건 · 승률 17% · 합산 PnL -21,406원 (백테 Optuna와 숫자 달라도 정상 — 고스트·유니버스·틱 경로 차이). +- 학습 1위(사후게이트 미달 가능): trial #35 · 거래 3건 · 승률 33.3% · PF 0.87 · PnL -1,193원 +- 표본 장이 1일뿐이라, ‘이전 장에 잘 먹힌 숫자’일 뿐 **추세가 바뀌면 바로 깨질 수 있습니다.** +- 플러스/학습 후보는 있으나 승률·PF 사후 기준은 못 넘김 → **당장 DB 적용은 비권장.** + +## 앞으로 장에서는 +- **1~2일 Optuna best를 바로 실매에 넣지 마세요.** 다일(≥5거래일) 재검증·웹백테 1회 후 검토. +- 적용 시에는 `results_gated` 후보만. 탐색용 best(objective)와 다를 수 있습니다. +- 장이 좁아지거나 변동성이 커지면: 손절·트레일·시간창이 먼저 깨집니다. 승률 자랑보다 **손절이 살아 있는지**를 보세요. +- 현재 사후합격 0건 → 파라미터 유지(현행 DB) + 유니버스/데이터 점검이 우선입니다. + +_생성: 2026-08-08 02:30:26 (rule)_ + +## AI 코멘트 + +_(미생성: api_call_failed. 구글 키는 GEMINI_API_KEY 를 DB/환경에 넣으세요 — kis_trader/backtest/optuna_briefing.py 모듈 docstring 참고)_ diff --git a/kis_trader/backtest/results/optuna_momentum_fast_20260807_214700.briefing.md b/kis_trader/backtest/results/optuna_momentum_fast_20260807_214700.briefing.md new file mode 100644 index 0000000..8d7b679 --- /dev/null +++ b/kis_trader/backtest/results/optuna_momentum_fast_20260807_214700.briefing.md @@ -0,0 +1,22 @@ +# Optuna 브리핑 — 모멘텀 (fast) +기간: 2026-07-31 ~ 2026-08-07 (거래일≈8일) · trials 1/1 +탐색 게이트: WR≥0% PF≥0 · min_trades≥1 | 후보 게이트: WR≥40% PF≥1 · min_trades≥1 +- min_trades=1: **합격 인정 최소 거래 건수**(표본 부족·우연 승률 방지). 이보다 적은 조합은 학습/후보 집계에서 제외됩니다. +학습용 통과 1건 · 사후합격(results_gated) 0건 + +## 이전 장에서는 +- 실매 동기간 청산 53건 · 승률 17% · 합산 PnL 18,737원 (백테 Optuna와 숫자 달라도 정상 — 고스트·유니버스·틱 경로 차이). +- 학습 1위(사후게이트 미달 가능): trial #0 · 거래 302건 · 승률 33.1% · PF 0.57 · PnL -249,237원 +- 플러스/학습 후보는 있으나 승률·PF 사후 기준은 못 넘김 → **당장 DB 적용은 비권장.** + +## 앞으로 장에서는 +- **1~2일 Optuna best를 바로 실매에 넣지 마세요.** 다일(≥5거래일) 재검증·웹백테 1회 후 검토. +- 적용 시에는 `results_gated` 후보만. 탐색용 best(objective)와 다를 수 있습니다. +- 장이 좁아지거나 변동성이 커지면: 손절·트레일·시간창이 먼저 깨집니다. 승률 자랑보다 **손절이 살아 있는지**를 보세요. +- 현재 사후합격 0건 → 파라미터 유지(현행 DB) + 유니버스/데이터 점검이 우선입니다. + +_생성: 2026-08-07 21:48:40 (rule)_ + +## AI 코멘트 + +_(미생성: api_call_failed. 구글 키는 GEMINI_API_KEY 를 DB/환경에 넣으세요 — kis_trader/backtest/optuna_briefing.py 모듈 docstring 참고)_ diff --git a/kis_trader/backtest/results/optuna_momentum_fast_20260808_010912.briefing.md b/kis_trader/backtest/results/optuna_momentum_fast_20260808_010912.briefing.md new file mode 100644 index 0000000..00efcb8 --- /dev/null +++ b/kis_trader/backtest/results/optuna_momentum_fast_20260808_010912.briefing.md @@ -0,0 +1,26 @@ +# Optuna 브리핑 — 모멘텀 (fast) +기간: 2026-08-07 ~ 2026-08-07 (거래일≈1일) · trials 200/200 +탐색 게이트: WR≥0% PF≥0 · min_trades≥1 | 후보 게이트: WR≥40% PF≥1 · min_trades≥1 +- min_trades=1: **합격 인정 최소 거래 건수**(표본 부족·우연 승률 방지). 이보다 적은 조합은 학습/후보 집계에서 제외됩니다. +학습용 통과 200건 · 사후합격(results_gated) 97건 + +## 이전 장에서는 +- 실매 동기간 청산 9건 · 승률 22% · 합산 PnL 9,384원 (백테 Optuna와 숫자 달라도 정상 — 고스트·유니버스·틱 경로 차이). +- 사후합격 1위: trial #26 · 거래 15건 · 승률 86.7% · PF 2.90 · PnL 34,293원 +- 표본 장이 1일뿐이라, ‘이전 장에 잘 먹힌 숫자’일 뿐 **추세가 바뀌면 바로 깨질 수 있습니다.** + +## 앞으로 장에서는 +- **1~2일 Optuna best를 바로 실매에 넣지 마세요.** 다일(≥5거래일) 재검증·웹백테 1회 후 검토. +- 적용 시에는 `results_gated` 후보만. 탐색용 best(objective)와 다를 수 있습니다. +- 장이 좁아지거나 변동성이 커지면: 손절·트레일·시간창이 먼저 깨집니다. 승률 자랑보다 **손절이 살아 있는지**를 보세요. +- 사후합격이 있어도 ‘내일도 같은 장’이 아닙니다. 페이퍼/소액·모의로 1~2일 관찰 후 확대하세요. + +_생성: 2026-08-08 01:10:30 (rule)_ + +## AI 코멘트 (이전 장 / 앞으로 장) [gemini] + +## 이전 장에서는 +실매에서는 9건 청산, 승률 22%, PnL 9,384원을 기록했습니다. Optuna 백테스트는 단 하루(2026-08-07)만에 Trial #26이 15건, 승률 86.7%, PF 2.90, PnL 34,293원으로 최고 성과를 보였습니다. 하지만 이는 매우 작은 표본으로 인한 과적합 및 우연의 가능성이 높으며, 실매와 백테스트 간 괴리(고스트 퍼지, 틱 경로 차이 등)는 실제 시장과의 차이를 명확히 보여줍니다. + +## 앞으로 장에서는 +단 하루치 결과로 나온 최적 파라미터를 즉시 실매 적용하는 것은 위험합니다. 최소 5거래일 이상 다일(多日) 백테스트로 전략의 견고함을 재검증하고, `results_gated` 후보들을 페이퍼/소액 모의로 1~2일 관찰 후 확대하십시오. 높은 승률보다 시장 변화 시 손절매 등 리스크 관리 기능이 제대로 작동하는지 확인하는 것이 중요하며, 백테스트는 과거 데이터에 기반할 뿐 미래 수익을 확정적으로 보장하지 않습니다. diff --git a/kis_trader/backtest/results/optuna_momentum_fast_20260808_021231.briefing.md b/kis_trader/backtest/results/optuna_momentum_fast_20260808_021231.briefing.md new file mode 100644 index 0000000..5c8a256 --- /dev/null +++ b/kis_trader/backtest/results/optuna_momentum_fast_20260808_021231.briefing.md @@ -0,0 +1,22 @@ +# Optuna 브리핑 — 모멘텀 (fast) +기간: 2026-08-07 ~ 2026-08-07 (거래일≈1일) · trials 200/200 +탐색 게이트: WR≥0% PF≥0 · min_trades≥1 | 후보 게이트: WR≥40% PF≥1 · min_trades≥1 +- min_trades=1: **합격 인정 최소 거래 건수**(표본 부족·우연 승률 방지). 이보다 적은 조합은 학습/후보 집계에서 제외됩니다. +학습용 통과 200건 · 사후합격(results_gated) 77건 + +## 이전 장에서는 +- 실매 동기간 청산 9건 · 승률 22% · 합산 PnL 9,384원 (백테 Optuna와 숫자 달라도 정상 — 고스트·유니버스·틱 경로 차이). +- 사후합격 1위: trial #65 · 거래 6건 · 승률 50.0% · PF 2.09 · PnL 22,505원 +- 표본 장이 1일뿐이라, ‘이전 장에 잘 먹힌 숫자’일 뿐 **추세가 바뀌면 바로 깨질 수 있습니다.** + +## 앞으로 장에서는 +- **1~2일 Optuna best를 바로 실매에 넣지 마세요.** 다일(≥5거래일) 재검증·웹백테 1회 후 검토. +- 적용 시에는 `results_gated` 후보만. 탐색용 best(objective)와 다를 수 있습니다. +- 장이 좁아지거나 변동성이 커지면: 손절·트레일·시간창이 먼저 깨집니다. 승률 자랑보다 **손절이 살아 있는지**를 보세요. +- 사후합격이 있어도 ‘내일도 같은 장’이 아닙니다. 페이퍼/소액·모의로 1~2일 관찰 후 확대하세요. + +_생성: 2026-08-08 02:13:46 (rule)_ + +## AI 코멘트 + +_(미생성: api_call_failed. 구글 키는 GEMINI_API_KEY 를 DB/환경에 넣으세요 — kis_trader/backtest/optuna_briefing.py 모듈 docstring 참고)_ diff --git a/kis_trader/backtest/results/optuna_scalp_fast_20260807_215011.briefing.md b/kis_trader/backtest/results/optuna_scalp_fast_20260807_215011.briefing.md new file mode 100644 index 0000000..da9528d --- /dev/null +++ b/kis_trader/backtest/results/optuna_scalp_fast_20260807_215011.briefing.md @@ -0,0 +1,21 @@ +# Optuna 브리핑 — 스캘핑 (fast) +기간: 2026-07-31 ~ 2026-08-07 (거래일≈8일) · trials 1/1 +탐색 게이트: WR≥0% PF≥0 · min_trades≥1 | 후보 게이트: WR≥40% PF≥1 · min_trades≥1 +- min_trades=1: **합격 인정 최소 거래 건수**(표본 부족·우연 승률 방지). 이보다 적은 조합은 학습/후보 집계에서 제외됩니다. +학습용 통과 1건 · 사후합격(results_gated) 1건 + +## 이전 장에서는 +- 실매 동기간 청산 56건 · 승률 27% · 합산 PnL 24,202원 (백테 Optuna와 숫자 달라도 정상 — 고스트·유니버스·틱 경로 차이). +- 사후합격 1위: trial #0 · 거래 9건 · 승률 77.8% · PF 1.48 · PnL 17,372원 + +## 앞으로 장에서는 +- **1~2일 Optuna best를 바로 실매에 넣지 마세요.** 다일(≥5거래일) 재검증·웹백테 1회 후 검토. +- 적용 시에는 `results_gated` 후보만. 탐색용 best(objective)와 다를 수 있습니다. +- 장이 좁아지거나 변동성이 커지면: 손절·트레일·시간창이 먼저 깨집니다. 승률 자랑보다 **손절이 살아 있는지**를 보세요. +- 사후합격이 있어도 ‘내일도 같은 장’이 아닙니다. 페이퍼/소액·모의로 1~2일 관찰 후 확대하세요. + +_생성: 2026-08-07 21:50:44 (rule)_ + +## AI 코멘트 + +_(미생성: api_call_failed. 구글 키는 GEMINI_API_KEY 를 DB/환경에 넣으세요 — kis_trader/backtest/optuna_briefing.py 모듈 docstring 참고)_ diff --git a/kis_trader/backtest/results/optuna_scalp_fast_20260807_215704.briefing.md b/kis_trader/backtest/results/optuna_scalp_fast_20260807_215704.briefing.md new file mode 100644 index 0000000..2ab0d97 --- /dev/null +++ b/kis_trader/backtest/results/optuna_scalp_fast_20260807_215704.briefing.md @@ -0,0 +1,22 @@ +# Optuna 브리핑 — 스캘핑 (fast) +기간: 2026-07-31 ~ 2026-08-07 (거래일≈8일) · trials 1/1 +탐색 게이트: WR≥0% PF≥0 · min_trades≥1 | 후보 게이트: WR≥40% PF≥1 · min_trades≥1 +- min_trades=1: **합격 인정 최소 거래 건수**(표본 부족·우연 승률 방지). 이보다 적은 조합은 학습/후보 집계에서 제외됩니다. +학습용 통과 1건 · 사후합격(results_gated) 0건 + +## 이전 장에서는 +- 실매 동기간 청산 56건 · 승률 27% · 합산 PnL 24,202원 (백테 Optuna와 숫자 달라도 정상 — 고스트·유니버스·틱 경로 차이). +- 학습 1위(사후게이트 미달 가능): trial #0 · 거래 6건 · 승률 16.7% · PF 0.36 · PnL -21,818원 +- 플러스/학습 후보는 있으나 승률·PF 사후 기준은 못 넘김 → **당장 DB 적용은 비권장.** + +## 앞으로 장에서는 +- **1~2일 Optuna best를 바로 실매에 넣지 마세요.** 다일(≥5거래일) 재검증·웹백테 1회 후 검토. +- 적용 시에는 `results_gated` 후보만. 탐색용 best(objective)와 다를 수 있습니다. +- 장이 좁아지거나 변동성이 커지면: 손절·트레일·시간창이 먼저 깨집니다. 승률 자랑보다 **손절이 살아 있는지**를 보세요. +- 현재 사후합격 0건 → 파라미터 유지(현행 DB) + 유니버스/데이터 점검이 우선입니다. + +_생성: 2026-08-07 21:57:31 (rule)_ + +## AI 코멘트 + +_(미생성: api_call_failed. 구글 키는 GEMINI_API_KEY 를 DB/환경에 넣으세요 — kis_trader/backtest/optuna_briefing.py 모듈 docstring 참고)_ diff --git a/kis_trader/backtest/results/optuna_scalp_fast_20260808_011850.briefing.md b/kis_trader/backtest/results/optuna_scalp_fast_20260808_011850.briefing.md new file mode 100644 index 0000000..52e56fe --- /dev/null +++ b/kis_trader/backtest/results/optuna_scalp_fast_20260808_011850.briefing.md @@ -0,0 +1,22 @@ +# Optuna 브리핑 — 스캘핑 (fast) +기간: 2026-08-07 ~ 2026-08-07 (거래일≈1일) · trials 200/200 +탐색 게이트: WR≥0% PF≥0 · min_trades≥1 | 후보 게이트: WR≥40% PF≥1 · min_trades≥1 +- min_trades=1: **합격 인정 최소 거래 건수**(표본 부족·우연 승률 방지). 이보다 적은 조합은 학습/후보 집계에서 제외됩니다. +학습용 통과 179건 · 사후합격(results_gated) 171건 + +## 이전 장에서는 +- 실매 동기간 청산 9건 · 승률 33% · 합산 PnL 5,036원 (백테 Optuna와 숫자 달라도 정상 — 고스트·유니버스·틱 경로 차이). +- 사후합격 1위: trial #114 · 거래 7건 · 승률 85.7% · PF 2323.95 · PnL 44,136원 +- 표본 장이 1일뿐이라, ‘이전 장에 잘 먹힌 숫자’일 뿐 **추세가 바뀌면 바로 깨질 수 있습니다.** + +## 앞으로 장에서는 +- **1~2일 Optuna best를 바로 실매에 넣지 마세요.** 다일(≥5거래일) 재검증·웹백테 1회 후 검토. +- 적용 시에는 `results_gated` 후보만. 탐색용 best(objective)와 다를 수 있습니다. +- 장이 좁아지거나 변동성이 커지면: 손절·트레일·시간창이 먼저 깨집니다. 승률 자랑보다 **손절이 살아 있는지**를 보세요. +- 사후합격이 있어도 ‘내일도 같은 장’이 아닙니다. 페이퍼/소액·모의로 1~2일 관찰 후 확대하세요. + +_생성: 2026-08-08 01:19:01 (rule)_ + +## AI 코멘트 + +_(미생성: api_call_failed. 구글 키는 GEMINI_API_KEY 를 DB/환경에 넣으세요 — kis_trader/backtest/optuna_briefing.py 모듈 docstring 참고)_ diff --git a/kis_trader/backtest/results/optuna_scalp_fast_20260808_023327.briefing.md b/kis_trader/backtest/results/optuna_scalp_fast_20260808_023327.briefing.md new file mode 100644 index 0000000..149804a --- /dev/null +++ b/kis_trader/backtest/results/optuna_scalp_fast_20260808_023327.briefing.md @@ -0,0 +1,22 @@ +# Optuna 브리핑 — 스캘핑 (fast) +기간: 2026-08-07 ~ 2026-08-07 (거래일≈1일) · trials 200/200 +탐색 게이트: WR≥0% PF≥0 · min_trades≥1 | 후보 게이트: WR≥40% PF≥1 · min_trades≥1 +- min_trades=1: **합격 인정 최소 거래 건수**(표본 부족·우연 승률 방지). 이보다 적은 조합은 학습/후보 집계에서 제외됩니다. +학습용 통과 180건 · 사후합격(results_gated) 177건 + +## 이전 장에서는 +- 실매 동기간 청산 9건 · 승률 33% · 합산 PnL 5,036원 (백테 Optuna와 숫자 달라도 정상 — 고스트·유니버스·틱 경로 차이). +- 사후합격 1위: trial #110 · 거래 7건 · 승률 85.7% · PF 2323.95 · PnL 44,136원 +- 표본 장이 1일뿐이라, ‘이전 장에 잘 먹힌 숫자’일 뿐 **추세가 바뀌면 바로 깨질 수 있습니다.** + +## 앞으로 장에서는 +- **1~2일 Optuna best를 바로 실매에 넣지 마세요.** 다일(≥5거래일) 재검증·웹백테 1회 후 검토. +- 적용 시에는 `results_gated` 후보만. 탐색용 best(objective)와 다를 수 있습니다. +- 장이 좁아지거나 변동성이 커지면: 손절·트레일·시간창이 먼저 깨집니다. 승률 자랑보다 **손절이 살아 있는지**를 보세요. +- 사후합격이 있어도 ‘내일도 같은 장’이 아닙니다. 페이퍼/소액·모의로 1~2일 관찰 후 확대하세요. + +_생성: 2026-08-08 02:33:38 (rule)_ + +## AI 코멘트 + +_(미생성: api_call_failed. 구글 키는 GEMINI_API_KEY 를 DB/환경에 넣으세요 — kis_trader/backtest/optuna_briefing.py 모듈 docstring 참고)_ diff --git a/kis_trader/backtest/results/optuna_scalp_fast_20260812_001823.briefing.md b/kis_trader/backtest/results/optuna_scalp_fast_20260812_001823.briefing.md new file mode 100644 index 0000000..500c10a --- /dev/null +++ b/kis_trader/backtest/results/optuna_scalp_fast_20260812_001823.briefing.md @@ -0,0 +1,23 @@ +# Optuna 브리핑 — 스캘핑 (fast) +기간: 2026-08-07 ~ 2026-08-07 (거래일≈1일) · trials 10/10 +탐색 게이트: WR≥0% PF≥0 · min_trades≥1 | 후보 게이트: WR≥40% PF≥1 · min_trades≥1 +- min_trades=1: **합격 인정 최소 거래 건수**(표본 부족·우연 승률 방지). 이보다 적은 조합은 학습/후보 집계에서 제외됩니다. +학습용 통과 7건 · 사후합격(results_gated) 5건 + +## 이전 장에서는 +- 실매 동기간 청산 9건 · 승률 33% · 합산 PnL 5,036원 (백테 Optuna와 숫자 달라도 정상 — 고스트·유니버스·틱 경로 차이). +- 사후합격 1위: trial #4 · 거래 1건 · 승률 100.0% · PF 9999.00 · PnL 12,352원 +- 승률이 비정상적으로 높고 표본이 작습니다 → **해당 장에만 맞는 과적합** 신호. +- 표본 장이 1일뿐이라, ‘이전 장에 잘 먹힌 숫자’일 뿐 **추세가 바뀌면 바로 깨질 수 있습니다.** + +## 앞으로 장에서는 +- **1~2일 Optuna best를 바로 실매에 넣지 마세요.** 다일(≥5거래일) 재검증·웹백테 1회 후 검토. +- 적용 시에는 `results_gated` 후보만. 탐색용 best(objective)와 다를 수 있습니다. +- 장이 좁아지거나 변동성이 커지면: 손절·트레일·시간창이 먼저 깨집니다. 승률 자랑보다 **손절이 살아 있는지**를 보세요. +- 사후합격이 있어도 ‘내일도 같은 장’이 아닙니다. 페이퍼/소액·모의로 1~2일 관찰 후 확대하세요. + +_생성: 2026-08-12 00:18:37 (rule)_ + +## AI 코멘트 + +_(미생성: api_call_failed. 구글 키는 GEMINI_API_KEY 를 DB/환경에 넣으세요 — kis_trader/backtest/optuna_briefing.py 모듈 docstring 참고)_ diff --git a/kis_trader/backtest/results/optuna_tail_fast_20260807_215258.briefing.md b/kis_trader/backtest/results/optuna_tail_fast_20260807_215258.briefing.md new file mode 100644 index 0000000..380108e --- /dev/null +++ b/kis_trader/backtest/results/optuna_tail_fast_20260807_215258.briefing.md @@ -0,0 +1,22 @@ +# Optuna 브리핑 — 꼬리 (fast) +기간: 2026-07-31 ~ 2026-08-07 (거래일≈8일) · trials 1/1 +탐색 게이트: WR≥0% PF≥0 · min_trades≥1 | 후보 게이트: WR≥40% PF≥1 · min_trades≥1 +- min_trades=1: **합격 인정 최소 거래 건수**(표본 부족·우연 승률 방지). 이보다 적은 조합은 학습/후보 집계에서 제외됩니다. +학습용 통과 1건 · 사후합격(results_gated) 0건 + +## 이전 장에서는 +- 실매 동기간 청산 7건 · 승률 43% · 합산 PnL -9,000원 (백테 Optuna와 숫자 달라도 정상 — 고스트·유니버스·틱 경로 차이). +- 학습 1위(사후게이트 미달 가능): trial #0 · 거래 9건 · 승률 33.3% · PF 0.22 · PnL -11,369원 +- 플러스/학습 후보는 있으나 승률·PF 사후 기준은 못 넘김 → **당장 DB 적용은 비권장.** + +## 앞으로 장에서는 +- **1~2일 Optuna best를 바로 실매에 넣지 마세요.** 다일(≥5거래일) 재검증·웹백테 1회 후 검토. +- 적용 시에는 `results_gated` 후보만. 탐색용 best(objective)와 다를 수 있습니다. +- 장이 좁아지거나 변동성이 커지면: 손절·트레일·시간창이 먼저 깨집니다. 승률 자랑보다 **손절이 살아 있는지**를 보세요. +- 현재 사후합격 0건 → 파라미터 유지(현행 DB) + 유니버스/데이터 점검이 우선입니다. + +_생성: 2026-08-07 21:53:28 (rule)_ + +## AI 코멘트 + +_(미생성: api_call_failed. 구글 키는 GEMINI_API_KEY 를 DB/환경에 넣으세요 — kis_trader/backtest/optuna_briefing.py 모듈 docstring 참고)_ diff --git a/kis_trader/backtest/results/optuna_tail_fast_20260807_215544.briefing.md b/kis_trader/backtest/results/optuna_tail_fast_20260807_215544.briefing.md new file mode 100644 index 0000000..87b8f47 --- /dev/null +++ b/kis_trader/backtest/results/optuna_tail_fast_20260807_215544.briefing.md @@ -0,0 +1,22 @@ +# Optuna 브리핑 — 꼬리 (fast) +기간: 2026-07-31 ~ 2026-08-07 (거래일≈8일) · trials 2/1 +탐색 게이트: WR≥0% PF≥0 · min_trades≥1 | 후보 게이트: WR≥40% PF≥1 · min_trades≥1 +- min_trades=1: **합격 인정 최소 거래 건수**(표본 부족·우연 승률 방지). 이보다 적은 조합은 학습/후보 집계에서 제외됩니다. +학습용 통과 2건 · 사후합격(results_gated) 0건 + +## 이전 장에서는 +- 실매 동기간 청산 7건 · 승률 43% · 합산 PnL -9,000원 (백테 Optuna와 숫자 달라도 정상 — 고스트·유니버스·틱 경로 차이). +- 학습 1위(사후게이트 미달 가능): trial #0 · 거래 9건 · 승률 33.3% · PF 0.22 · PnL -11,369원 +- 플러스/학습 후보는 있으나 승률·PF 사후 기준은 못 넘김 → **당장 DB 적용은 비권장.** + +## 앞으로 장에서는 +- **1~2일 Optuna best를 바로 실매에 넣지 마세요.** 다일(≥5거래일) 재검증·웹백테 1회 후 검토. +- 적용 시에는 `results_gated` 후보만. 탐색용 best(objective)와 다를 수 있습니다. +- 장이 좁아지거나 변동성이 커지면: 손절·트레일·시간창이 먼저 깨집니다. 승률 자랑보다 **손절이 살아 있는지**를 보세요. +- 현재 사후합격 0건 → 파라미터 유지(현행 DB) + 유니버스/데이터 점검이 우선입니다. + +_생성: 2026-08-07 21:56:15 (rule)_ + +## AI 코멘트 + +_(미생성: api_call_failed. 구글 키는 GEMINI_API_KEY 를 DB/환경에 넣으세요 — kis_trader/backtest/optuna_briefing.py 모듈 docstring 참고)_ diff --git a/kis_trader/backtest/results/optuna_tail_fast_20260808_013039.briefing.md b/kis_trader/backtest/results/optuna_tail_fast_20260808_013039.briefing.md new file mode 100644 index 0000000..5fb72e8 --- /dev/null +++ b/kis_trader/backtest/results/optuna_tail_fast_20260808_013039.briefing.md @@ -0,0 +1,22 @@ +# Optuna 브리핑 — 꼬리 (fast) +기간: 2026-08-07 ~ 2026-08-07 (거래일≈1일) · trials 200/200 +탐색 게이트: WR≥0% PF≥0 · min_trades≥1 | 후보 게이트: WR≥40% PF≥1 · min_trades≥1 +- min_trades=1: **합격 인정 최소 거래 건수**(표본 부족·우연 승률 방지). 이보다 적은 조합은 학습/후보 집계에서 제외됩니다. +학습용 통과 196건 · 사후합격(results_gated) 150건 + +## 이전 장에서는 +- 실매 동기간 청산 1건 · 승률 0% · 합산 PnL -7,783원 (백테 Optuna와 숫자 달라도 정상 — 고스트·유니버스·틱 경로 차이). +- 사후합격 1위: trial #43 · 거래 4건 · 승률 75.0% · PF 10.02 · PnL 4,851원 +- 표본 장이 1일뿐이라, ‘이전 장에 잘 먹힌 숫자’일 뿐 **추세가 바뀌면 바로 깨질 수 있습니다.** + +## 앞으로 장에서는 +- **1~2일 Optuna best를 바로 실매에 넣지 마세요.** 다일(≥5거래일) 재검증·웹백테 1회 후 검토. +- 적용 시에는 `results_gated` 후보만. 탐색용 best(objective)와 다를 수 있습니다. +- 장이 좁아지거나 변동성이 커지면: 손절·트레일·시간창이 먼저 깨집니다. 승률 자랑보다 **손절이 살아 있는지**를 보세요. +- 사후합격이 있어도 ‘내일도 같은 장’이 아닙니다. 페이퍼/소액·모의로 1~2일 관찰 후 확대하세요. + +_생성: 2026-08-08 01:30:43 (rule)_ + +## AI 코멘트 + +_(미생성: api_call_failed. 구글 키는 GEMINI_API_KEY 를 DB/환경에 넣으세요 — kis_trader/backtest/optuna_briefing.py 모듈 docstring 참고)_ diff --git a/kis_trader/backtest/results/optuna_tail_fast_20260808_022522.briefing.md b/kis_trader/backtest/results/optuna_tail_fast_20260808_022522.briefing.md new file mode 100644 index 0000000..c5fc41e --- /dev/null +++ b/kis_trader/backtest/results/optuna_tail_fast_20260808_022522.briefing.md @@ -0,0 +1,22 @@ +# Optuna 브리핑 — 꼬리 (fast) +기간: 2026-08-07 ~ 2026-08-07 (거래일≈1일) · trials 200/200 +탐색 게이트: WR≥0% PF≥0 · min_trades≥1 | 후보 게이트: WR≥40% PF≥1 · min_trades≥1 +- min_trades=1: **합격 인정 최소 거래 건수**(표본 부족·우연 승률 방지). 이보다 적은 조합은 학습/후보 집계에서 제외됩니다. +학습용 통과 196건 · 사후합격(results_gated) 143건 + +## 이전 장에서는 +- 실매 동기간 청산 1건 · 승률 0% · 합산 PnL -7,783원 (백테 Optuna와 숫자 달라도 정상 — 고스트·유니버스·틱 경로 차이). +- 사후합격 1위: trial #31 · 거래 4건 · 승률 75.0% · PF 10.02 · PnL 4,851원 +- 표본 장이 1일뿐이라, ‘이전 장에 잘 먹힌 숫자’일 뿐 **추세가 바뀌면 바로 깨질 수 있습니다.** + +## 앞으로 장에서는 +- **1~2일 Optuna best를 바로 실매에 넣지 마세요.** 다일(≥5거래일) 재검증·웹백테 1회 후 검토. +- 적용 시에는 `results_gated` 후보만. 탐색용 best(objective)와 다를 수 있습니다. +- 장이 좁아지거나 변동성이 커지면: 손절·트레일·시간창이 먼저 깨집니다. 승률 자랑보다 **손절이 살아 있는지**를 보세요. +- 사후합격이 있어도 ‘내일도 같은 장’이 아닙니다. 페이퍼/소액·모의로 1~2일 관찰 후 확대하세요. + +_생성: 2026-08-08 02:25:27 (rule)_ + +## AI 코멘트 + +_(미생성: api_call_failed. 구글 키는 GEMINI_API_KEY 를 DB/환경에 넣으세요 — kis_trader/backtest/optuna_briefing.py 모듈 docstring 참고)_ diff --git a/kis_trader/backtest/scalping_backtest_common.py b/kis_trader/backtest/scalping_backtest_common.py index 988152d..a3a30e2 100644 --- a/kis_trader/backtest/scalping_backtest_common.py +++ b/kis_trader/backtest/scalping_backtest_common.py @@ -5,6 +5,7 @@ """ from __future__ import annotations +import os from datetime import datetime from typing import Any, Dict, List, Optional, Tuple @@ -175,6 +176,8 @@ def load_scalp_candles_by_code( ) return candles_by_code, total_candles + candle_src = os.environ.get("CANDLE_SOURCE", "kis") or "kis" + codes_raw = db.conn.execute( "SELECT DISTINCT code FROM ws_candles WHERE timeframe=1 " "AND candle_time >= %s AND candle_time <= %s ORDER BY code", diff --git a/kis_trader/backtest/trade_orderbook_enrich.py b/kis_trader/backtest/trade_orderbook_enrich.py new file mode 100644 index 0000000..2601b72 --- /dev/null +++ b/kis_trader/backtest/trade_orderbook_enrich.py @@ -0,0 +1,413 @@ +""" +거래내역 UI용 — 매수/매도 시각 근처 호가 스냅 부착. + +필터 ON/OFF 와 무관: 수집된 ws_orderbook / ls_ws_orderbook 으로 +진입(매수) · 청산(매도) 시점 유동성을 각각 보여 준다. +""" +from __future__ import annotations + +import logging +from datetime import datetime, timedelta +from typing import Any, Dict, List, Optional, Sequence, Tuple + +logger = logging.getLogger("trade_orderbook_enrich") + +# 표시용 매칭 창 — 체결≠호가저장 이므로 실매 UI는 ±15분까지 근처 기록 허용 +_STRICT_DELTA_SEC = 180 # 판정(filter_eval) 정밀 창 +_SOFT_DELTA_SEC = 900 # 본체·표시용 완화 창 (±15분) +_BODY_PREF_DELTA_SEC = 120 # 본체 우선 창 +_EXIT_OB_OR_DEFAULT = 0.4 # L3 OR 임계 표시용 (MOMENTUM_EXIT_OB_RATIO_MIN 기본) + + +def _strategy_canon(strategy: str) -> str: + s = (strategy or "").strip().upper() + if s in ("SHORT", "TAIL_CATCH", "TAIL"): + return "TAIL" + if s in ("BO", "BREAKOUT"): + return "BREAKOUT" + if s in ("MOM", "MOMENTUM"): + return "MOMENTUM" + if s in ("SCALP", "SCALPING", "REVERSAL"): + return "SCALP" + if s.startswith("US_"): + return "US" + return s + + +def _ts14(raw: Any) -> str: + if raw is None: + return "" + s = str(raw).strip() + if not s: + return "" + s = ( + s.replace("-", "") + .replace(":", "") + .replace(" ", "") + .replace("T", "") + .replace(".", "") + ) + if len(s) < 8: + return "" + return (s + "000000")[:14] + + +def _parse14(ts14: str) -> Optional[datetime]: + st = (ts14 or "").strip() + if len(st) < 12: + return None + try: + return datetime.strptime(st[:14], "%Y%m%d%H%M%S") + except ValueError: + try: + return datetime.strptime(st[:12], "%Y%m%d%H%M") + except ValueError: + return None + + +def _entry_ts14(trade: Dict[str, Any]) -> str: + for k in ("buy_time", "entry_time", "buy_date"): + t = _ts14(trade.get(k)) + if t: + return t + return "" + + +def _exit_ts14(trade: Dict[str, Any]) -> str: + for k in ("sell_time", "exit_time", "sell_date"): + t = _ts14(trade.get(k)) + if t: + return t + return "" + + +def _spread_pct(best_bid: float, best_ask: float) -> Optional[float]: + if best_bid <= 0 or best_ask <= 0: + return None + mid = (best_bid + best_ask) / 2.0 + if mid <= 0: + return None + return (best_ask - best_bid) / mid * 100.0 + + +def _or_ratio(total_bid: float, total_ask: float) -> Optional[float]: + try: + ask = float(total_ask or 0) + bid = float(total_bid or 0) + except (TypeError, ValueError): + return None + if ask <= 0: + return None + return bid / ask + + +def _row_to_ob( + row: Dict[str, Any], + *, + delta_sec: int, + lim_spread_pct: float, + lim_ratio: float, + lim_or_ratio: float, + side: str, +) -> Dict[str, Any]: + bid = float(row.get("best_bid") or 0) + ask = float(row.get("best_ask") or 0) + bid_l3 = int(row.get("bid_qty_l3") or 0) + ask_l3 = int(row.get("ask_qty_l3") or 0) + ratio = (bid_l3 / ask_l3) if ask_l3 > 0 else None + tot_bid = int(row.get("total_bid_qty") or 0) + tot_ask = int(row.get("total_ask_qty") or 0) + or_r = _or_ratio(tot_bid, tot_ask) + rej = (row.get("reject_code") or "").strip() or None + msg = (row.get("reject_msg") or "").strip() or None + src = (row.get("source") or "").strip() + if side == "entry" and src == "filter_eval": + verdict = rej or "PASS" + else: + verdict = "BODY" # 주기 스냅 — 판정 메타 없음 (매도도 본체 위주) + near_only = int(delta_sec) > _STRICT_DELTA_SEC + return { + "side": side, + "snap_time": str(row.get("snap_time") or "")[:14], + "source": src, + "strategy": row.get("strategy"), + "best_bid": int(bid) if bid else 0, + "best_ask": int(ask) if ask else 0, + "spread_pct": round(_spread_pct(bid, ask) or 0.0, 3), + "bid_qty_l3": bid_l3, + "ask_qty_l3": ask_l3, + "ratio": round(ratio, 3) if ratio is not None else None, + "total_bid_qty": tot_bid, + "total_ask_qty": tot_ask, + "or_ratio": round(or_r, 3) if or_r is not None else None, + "reject_code": rej, + "reject_msg": msg, + "verdict": verdict, + "delta_sec": int(delta_sec), + "matched": True, + "near_only": bool(near_only), + "lim_spread_pct": float(lim_spread_pct), + "lim_ratio": float(lim_ratio), + "lim_or_ratio": float(lim_or_ratio), + } + + +def _pick_best( + candidates: Sequence[Tuple[int, Dict[str, Any]]], + *, + prefer_strategy: str, + prefer_body: bool = False, +) -> Optional[Tuple[int, Dict[str, Any]]]: + if not candidates: + return None + + def _fe(c: Tuple[int, Dict[str, Any]]) -> bool: + return (c[1].get("source") or "") == "filter_eval" + + def _is_body(c: Tuple[int, Dict[str, Any]]) -> bool: + # filter_eval = 진입 TRIGGER 판정 스냅 · 그 외(kiwoom_0d·ls_*)는 본체 + return (c[1].get("source") or "") != "filter_eval" + + # 매도: filter_eval(진입판정)보다 본체 시계열 우선 + if prefer_body: + body_pref = [c for c in candidates if _is_body(c) and c[0] <= _BODY_PREF_DELTA_SEC] + if body_pref: + return min(body_pref, key=lambda x: x[0]) + body_soft = [c for c in candidates if _is_body(c) and c[0] <= _SOFT_DELTA_SEC] + if body_soft: + return min(body_soft, key=lambda x: x[0]) + fe_any = [c for c in candidates if _fe(c) and c[0] <= _SOFT_DELTA_SEC] + if fe_any: + return min(fe_any, key=lambda x: x[0]) + return None + + # 매수: 기존 우선순위 (filter_eval → 본체) + fe_match = [ + c for c in candidates + if _fe(c) + and c[0] <= _STRICT_DELTA_SEC + and _strategy_canon(str(c[1].get("strategy") or "")) == prefer_strategy + ] + if fe_match: + return min(fe_match, key=lambda x: x[0]) + fe_any = [c for c in candidates if _fe(c) and c[0] <= _STRICT_DELTA_SEC] + if fe_any: + return min(fe_any, key=lambda x: x[0]) + body_pref = [c for c in candidates if _is_body(c) and c[0] <= _BODY_PREF_DELTA_SEC] + if body_pref: + return min(body_pref, key=lambda x: x[0]) + fe_soft = [c for c in candidates if _fe(c) and c[0] <= _SOFT_DELTA_SEC] + if fe_soft: + return min(fe_soft, key=lambda x: x[0]) + body_soft = [c for c in candidates if _is_body(c) and c[0] <= _SOFT_DELTA_SEC] + if body_soft: + return min(body_soft, key=lambda x: x[0]) + return None + + +def _load_lims(prefer: str) -> Tuple[float, float, float]: + lim_spread = 0.45 + lim_ratio = 0.85 + lim_or = float(_EXIT_OB_OR_DEFAULT) + try: + from kis_trader.engine.orderbook_env import ( + OB_DEFAULT_MAX_SPREAD_PCT, + OB_DEFAULT_MIN_BID_ASK_RATIO, + load_orderbook_threshold_cfg as _load_ob_cfg, + ) + lim_spread = float(OB_DEFAULT_MAX_SPREAD_PCT) + lim_ratio = float(OB_DEFAULT_MIN_BID_ASK_RATIO) + if prefer and prefer != "US": + cfg = _load_ob_cfg(prefer) + lim_spread = float(cfg.get("max_spread_pct") or lim_spread) + lim_ratio = float(cfg.get("min_bid_ask_ratio") or lim_ratio) + except Exception: + pass + try: + from kis_trader.utils.env import get_env_float + lim_or = float(get_env_float("MOMENTUM_EXIT_OB_RATIO_MIN", _EXIT_OB_OR_DEFAULT)) + except Exception: + pass + return lim_spread, lim_ratio, lim_or + + +def _fetch_ob_rows( + db: Any, + code_list: List[str], + lo: str, + hi: str, +) -> List[Dict[str, Any]]: + conn = getattr(db, "conn", None) or db + placeholders = ",".join(["%s"] * len(code_list)) + params: List[Any] = ["KR", *code_list, lo, hi] + sql_kw = ( + f"SELECT code, snap_time, best_bid, best_ask, total_bid_qty, total_ask_qty, " + f"bid_qty_l3, ask_qty_l3, source, strategy, reject_code, reject_msg " + f"FROM ws_orderbook WHERE market = %s AND code IN ({placeholders}) " + f"AND snap_time >= %s AND snap_time <= %s " + f"AND source IN ('filter_eval', 'kiwoom_0d') " + f"ORDER BY code, snap_time" + ) + rows = [dict(r) for r in conn.execute(sql_kw, params).fetchall()] + sql_ls = ( + f"SELECT code, snap_time, best_bid, best_ask, total_bid_qty, total_ask_qty, " + f"bid_qty_l3, ask_qty_l3, source, " + f"NULL AS strategy, NULL AS reject_code, NULL AS reject_msg " + f"FROM ls_ws_orderbook WHERE market = %s AND code IN ({placeholders}) " + f"AND snap_time >= %s AND snap_time <= %s " + f"ORDER BY code, snap_time" + ) + try: + ls_rows = [dict(r) for r in conn.execute(sql_ls, params).fetchall()] + if ls_rows: + rows.extend(ls_rows) + rows.sort( + key=lambda r: ( + str(r.get("code") or ""), + str(r.get("snap_time") or ""), + ) + ) + except Exception as e_ls: + logger.debug("ls_ws_orderbook 조회 스킵: %s", e_ls) + return rows + + +def enrich_trades_with_entry_orderbook( + db: Any, + trades: List[Dict[str, Any]], + *, + strategy_hint: str = "", +) -> List[Dict[str, Any]]: + """하위호환 — 매수·매도 호가 모두 부착.""" + return enrich_trades_with_orderbook(db, trades, strategy_hint=strategy_hint) + + +def enrich_trades_with_orderbook( + db: Any, + trades: List[Dict[str, Any]], + *, + strategy_hint: str = "", +) -> List[Dict[str, Any]]: + """거래 dict 에 ``entry_ob`` · ``exit_ob`` 를 in-place 부착.""" + if not trades: + return trades + + hint = _strategy_canon(strategy_hint) + if hint == "US": + for t in trades: + t["entry_ob"] = None + t["exit_ob"] = None + return trades + + # (trade_idx, trade, code, entry_dt|None, exit_dt|None) + keyed: List[Tuple[int, Dict[str, Any], str, Optional[datetime], Optional[datetime]]] = [] + codes = set() + t_min: Optional[datetime] = None + t_max: Optional[datetime] = None + + def _bump(dt: Optional[datetime]) -> None: + nonlocal t_min, t_max + if dt is None: + return + if t_min is None or dt < t_min: + t_min = dt + if t_max is None or dt > t_max: + t_max = dt + + for i, t in enumerate(trades): + if _strategy_canon(str(t.get("strategy") or strategy_hint)) == "US": + t["entry_ob"] = None + t["exit_ob"] = None + continue + code = str(t.get("code") or "").strip() + edt = _parse14(_entry_ts14(t)) + xdt = _parse14(_exit_ts14(t)) + if not code or (edt is None and xdt is None): + t["entry_ob"] = None + t["exit_ob"] = None + continue + keyed.append((i, t, code, edt, xdt)) + codes.add(code) + _bump(edt) + _bump(xdt) + + if not keyed or t_min is None or t_max is None: + for t in trades: + t.setdefault("entry_ob", None) + t.setdefault("exit_ob", None) + return trades + + lo = (t_min - timedelta(seconds=_SOFT_DELTA_SEC)).strftime("%Y%m%d%H%M%S") + hi = (t_max + timedelta(seconds=_SOFT_DELTA_SEC)).strftime("%Y%m%d%H%M%S") + code_list = sorted(codes) + + try: + rows = _fetch_ob_rows(db, code_list, lo, hi) + except Exception as e: + logger.warning("orderbook enrich 조회 실패: %s", e) + for t in trades: + t.setdefault("entry_ob", None) + t.setdefault("exit_ob", None) + return trades + + by_code: Dict[str, List[Dict[str, Any]]] = {} + for r in rows: + c = str(r.get("code") or "").strip() + if not c: + continue + by_code.setdefault(c, []).append(r) + + for _i, t, code, edt, xdt in keyed: + prefer = _strategy_canon(str(t.get("strategy") or strategy_hint) or hint) + lim_spread, lim_ratio, lim_or = _load_lims(prefer) + + def _cands_for(dt: Optional[datetime]) -> List[Tuple[int, Dict[str, Any]]]: + if dt is None: + return [] + out: List[Tuple[int, Dict[str, Any]]] = [] + for r in by_code.get(code, []): + rdt = _parse14(str(r.get("snap_time") or "")) + if rdt is None: + continue + delta = abs(int((rdt - dt).total_seconds())) + if delta > _SOFT_DELTA_SEC: + continue + out.append((delta, r)) + return out + + ep = _pick_best(_cands_for(edt), prefer_strategy=prefer, prefer_body=False) + if ep: + delta, row = ep + t["entry_ob"] = _row_to_ob( + row, + delta_sec=delta, + lim_spread_pct=lim_spread, + lim_ratio=lim_ratio, + lim_or_ratio=lim_or, + side="entry", + ) + else: + t["entry_ob"] = None + + # 미청산(보유중) — 매도호가 없음 + if xdt is None: + t["exit_ob"] = None + else: + xp = _pick_best(_cands_for(xdt), prefer_strategy=prefer, prefer_body=True) + if xp: + delta, row = xp + t["exit_ob"] = _row_to_ob( + row, + delta_sec=delta, + lim_spread_pct=lim_spread, + lim_ratio=lim_ratio, + lim_or_ratio=lim_or, + side="exit", + ) + else: + t["exit_ob"] = None + + for t in trades: + t.setdefault("entry_ob", None) + t.setdefault("exit_ob", None) + return trades diff --git a/kis_trader/backtest/trigger_snapshot_loader.py b/kis_trader/backtest/trigger_snapshot_loader.py index 3c9b784..097d72f 100644 --- a/kis_trader/backtest/trigger_snapshot_loader.py +++ b/kis_trader/backtest/trigger_snapshot_loader.py @@ -20,6 +20,32 @@ LOG_BACKFILL_SOURCE = "log_backfill" # 키움 실시간 호가 주기 스냅샷(본체 levels_json 보유) — 스프레드 등 재계산용. # log_backfill(판정만)·filter_eval(판정시점만)과 달리 종목·시각을 폭넓게 커버한다. KIWOOM_BODY_SOURCE = "kiwoom_0d" +# LS UH1 등 — history_source=ls 일 때 ls_ws_orderbook 본체 +LS_BODY_SOURCES = ("ls_uh1", "ls_h1", "ls_ha", "ls_nh1") + + +def resolve_orderbook_history_source( + engine_params: Optional[Dict[str, Any]] = None, +) -> str: + """봉/틱과 동일 축 — universe history_source (ls|kiwoom).""" + try: + from kis_trader.backtest.universe_history_source import ( + normalize_universe_history_source, + ) + except Exception: + def normalize_universe_history_source(raw): # type: ignore + s = str(raw or "").strip().lower() + return "ls" if s in ("ls", "ls_condition", "ls_afr", "ls_ws") else "kiwoom" + + if not engine_params: + return "kiwoom" + raw = ( + engine_params.get("_orderbook_history_source") + or engine_params.get("_universe_history_source") + or engine_params.get("universe_history_source") + or engine_params.get("history_source") + ) + return normalize_universe_history_source(raw) def backtest_use_kiwoom_body_snapshot( @@ -71,7 +97,16 @@ def backtest_needs_trigger_snapshot_load( p = params or {} ob_on = _orderbook_filter_enabled_for_entry(p, strategy) pg_on = _program_filter_enabled_for_entry(p, strategy) - return bool(ob_on or pg_on) + # 수익구간·손절호가 ON 이면 진입필터 OFF여도 OR 본체가 필요 (모멘텀 키움 / 돌파 LS) + try: + from kis_trader.engine.momentum_hts_logic import need_ob_or_history + + exit_ob_on = need_ob_or_history(p) + except Exception: + exit_ob_on = bool( + p.get("exit_ob_enabled", False) or p.get("stop_ob_enabled", False) + ) + return bool(ob_on or pg_on or exit_ob_on) def backtest_use_trigger_snapshot_db( @@ -203,8 +238,10 @@ def _load_orderbook_rows( market: str, sources: Tuple[str, ...], strategy: str = "", + table: str = "ws_orderbook", + use_strategy_col: bool = True, ) -> List[Dict[str, Any]]: - """ws_orderbook 행 조회 (source·strategy 필터).""" + """호가 스냅 행 조회 (ws_orderbook | ls_ws_orderbook).""" tt_start = (start_key or "")[:14] if len(tt_start) == 12: tt_start += "00" @@ -212,6 +249,17 @@ def _load_orderbook_rows( if len(tt_end) == 12: tt_end += "59" + tbl = "ls_ws_orderbook" if str(table).strip().lower() == "ls_ws_orderbook" else "ws_orderbook" + import os + ob_source = os.environ.get("OB_SOURCE", "").strip() + if ob_source == "kis": + tbl = "kis_ws_orderbook" + sources = () + use_strategy_col = False + elif ob_source == "kiwoom_0d": + tbl = "ws_orderbook" + sources = ("kiwoom_0d",) + code_filter = "" params: List[Any] = [market, tt_start, tt_end] if codes: @@ -224,19 +272,31 @@ def _load_orderbook_rows( source_filter = f" AND source IN ({','.join(['%s'] * len(sources))})" params.extend(sources) - strat_vals = _strategy_db_values(strategy) strat_filter = "" - if strat_vals: - strat_filter = ( - f" AND (strategy IS NULL OR strategy IN ({','.join(['%s'] * len(strat_vals))}))" + if use_strategy_col and tbl == "ws_orderbook": + strat_vals = _strategy_db_values(strategy) + if strat_vals: + strat_filter = ( + f" AND (strategy IS NULL OR strategy IN ({','.join(['%s'] * len(strat_vals))}))" + ) + params.extend(strat_vals) + + # ls 테이블에는 strategy/reject 컬럼 없음 — 본체만 + if tbl == "ls_ws_orderbook": + cols = ( + "code, snap_time, best_bid, best_ask, total_bid_qty, total_ask_qty, " + "bid_qty_l3, ask_qty_l3, levels_json, source, recv_ts" + ) + else: + cols = ( + "code, snap_time, best_bid, best_ask, total_bid_qty, total_ask_qty, " + "bid_qty_l3, ask_qty_l3, levels_json, source, recv_ts, " + "strategy, reject_code, reject_msg" ) - params.extend(strat_vals) sql = f""" - SELECT code, snap_time, best_bid, best_ask, total_bid_qty, total_ask_qty, - bid_qty_l3, ask_qty_l3, levels_json, source, recv_ts, - strategy, reject_code, reject_msg - FROM ws_orderbook + SELECT {cols} + FROM {tbl} WHERE market = %s AND snap_time >= %s AND snap_time <= %s @@ -249,7 +309,7 @@ def _load_orderbook_rows( rows = db.conn.execute(sql, tuple(params)).fetchall() return [dict(r) for r in rows] except Exception as e: - logger.warning("ws_orderbook 조회 실패 — TRIGGER 호가 스킵: %s", e) + logger.warning("%s 조회 실패 — TRIGGER 호가 스킵: %s", tbl, e) return [] @@ -282,8 +342,50 @@ def load_orderbook_snapshots_by_code( engine_params: Optional[Dict[str, Any]] = None, strategy: str = "", ) -> Tuple[Dict[str, Dict[str, List[OrderbookSnapshot]]], int, Dict[str, Dict[str, List[Dict[str, Any]]]]]: - """기간 내 ``ws_orderbook`` → ``{code: {minute: [snap, ...]}}`` + log_backfill 판정.""" + """기간 내 호가 → ``{code: {minute: [snap, ...]}}``. + + history_source=ls → ``ls_ws_orderbook`` 본체만 (키움 log_backfill/filter_eval 폴백 금지). + """ mkt = (market or get_env_from_db("WS_TICK_DEFAULT_MARKET", "KR") or "KR").strip().upper() + hist_src = resolve_orderbook_history_source(engine_params) + + # ── LS: 본체만, 판정 메모 폴백 없음 ── + if hist_src == "ls": + try: + if hasattr(db, "ensure_ws_orderbook_table"): + # ls 테이블은 TradeDB migrate 에서 생성됨 — no-op ensure 없으면 무시 + pass + except Exception: + pass + rows = _load_orderbook_rows( + db, start_key, end_key, codes, market=mkt, + sources=LS_BODY_SOURCES, strategy="", + table="ls_ws_orderbook", use_strategy_col=False, + ) + # source 필터에 안 잡힌 구 데이터도 있으면 전체 재조회 (source 비어있는 행) + if not rows: + rows = _load_orderbook_rows( + db, start_key, end_key, codes, market=mkt, + sources=tuple(), strategy="", + table="ls_ws_orderbook", use_strategy_col=False, + ) + out: Dict[str, Dict[str, List[OrderbookSnapshot]]] = defaultdict(dict) + total = 0 + for r in rows: + code = str(r.get("code") or "").strip() + st = str(r.get("snap_time") or "").strip() + minute_key = _minute_key(st) + if not code or not minute_key: + continue + snap = orderbook_snapshot_from_storage(r) + bucket = out[code].setdefault(minute_key, []) + bucket.append(snap) + total += 1 + if engine_params is not None: + engine_params["_orderbook_history_source"] = "ls" + engine_params["_backtest_disable_log_orderbook_verdict"] = True + return dict(out), total, {} + try: if hasattr(db, "ensure_ws_orderbook_table"): db.ensure_ws_orderbook_table() @@ -293,13 +395,25 @@ def load_orderbook_snapshots_by_code( use_eval = backtest_use_trigger_eval_snapshot(engine_params, strategy=strategy) use_log_bf = backtest_use_log_backfill_snapshot(engine_params, strategy=strategy) use_kiwoom = backtest_use_kiwoom_body_snapshot(engine_params, strategy=strategy) + # 수익구간·손절호가: 보유 중 OR_MA 용 본체 스냅 필요. + # 진입필터 재계산 플래그(use_kiwoom)와 분리 — 본체만 추가 적재. + need_exit_ob = False + try: + from kis_trader.engine.momentum_hts_logic import need_ob_or_history + + need_exit_ob = need_ob_or_history(engine_params or {}) + except Exception: + _p = engine_params or {} + need_exit_ob = bool( + _p.get("exit_ob_enabled", False) or _p.get("stop_ob_enabled", False) + ) sources: List[str] = [] if use_eval: sources.append(FILTER_EVAL_SOURCE) if use_log_bf: sources.append(LOG_BACKFILL_SOURCE) - if use_kiwoom: + if use_kiwoom or need_exit_ob: sources.append(KIWOOM_BODY_SOURCE) if not sources: return {}, 0, {} @@ -307,9 +421,10 @@ def load_orderbook_snapshots_by_code( rows = _load_orderbook_rows( db, start_key, end_key, codes, market=mkt, sources=tuple(sources), strategy=strategy, + table="ws_orderbook", use_strategy_col=True, ) - out: Dict[str, Dict[str, List[OrderbookSnapshot]]] = defaultdict(dict) + out = defaultdict(dict) log_verdict: Dict[str, Dict[str, List[Dict[str, Any]]]] = defaultdict(dict) seen_eval: Set[Tuple[str, str]] = set() # 본체(kiwoom_0d) 다운샘플: (code, minute) 당 최신 1건만 유지 (분단위 백테·메모리 절약) @@ -465,12 +580,15 @@ def load_trigger_snapshots_by_code( ) meta = snapshot_coverage_stats(ob_by_code, pg_by_code) meta["ws_orderbook_rows_loaded"] = ob_rows + meta["orderbook_history_source"] = resolve_orderbook_history_source(engine_params) meta["ws_program_rows_loaded"] = pg_rows meta["trigger_eval_only"] = backtest_use_trigger_eval_snapshot( engine_params, strategy=strategy, ) - meta["log_backfill_enabled"] = backtest_use_log_backfill_snapshot( - engine_params, strategy=strategy, + meta["log_backfill_enabled"] = ( + False + if meta["orderbook_history_source"] == "ls" + else backtest_use_log_backfill_snapshot(engine_params, strategy=strategy) ) meta["log_verdict_by_code"] = log_verdict meta["log_verdict_rows"] = sum( diff --git a/kis_trader/engine/indicator_cache.py b/kis_trader/engine/indicator_cache.py index c1ebec3..d068a16 100644 --- a/kis_trader/engine/indicator_cache.py +++ b/kis_trader/engine/indicator_cache.py @@ -355,7 +355,7 @@ def materialize_ws_candles_batch( if not batch: return 0 - where = "WHERE code = ? AND timeframe = ? AND candle_time = ?" + where = "WHERE code = ? AND timeframe = ? AND candle_time = ? AND source = 'kis'" sql_sqlite = f""" UPDATE ws_candles SET {set_sql}, updated_at = datetime('now','localtime') @@ -368,7 +368,7 @@ def materialize_ws_candles_batch( sql_mysql = f""" UPDATE ws_candles SET {set_mysql} - WHERE code = %s AND timeframe = %s AND candle_time = %s + WHERE code = %s AND timeframe = %s AND candle_time = %s AND source = 'kis' """ try: with db.conn: diff --git a/kis_trader/engine/momentum_engine.py b/kis_trader/engine/momentum_engine.py index 1280b05..2f96f51 100644 --- a/kis_trader/engine/momentum_engine.py +++ b/kis_trader/engine/momentum_engine.py @@ -183,6 +183,18 @@ def get_momentum_defaults_from_db(db=None, *, env_row: Optional[Dict[str, Any]] eod_enabled = force_eod eod_hm = str(r.get("MOMENTUM_EOD_HM") or "15:20").strip() or "15:20" portfolio_mode = True + # 수익구간 호가매도 (기본 OFF) + exit_ob_enabled = momentum_env_bool(r, "MOMENTUM_EXIT_OB_ENABLED", False) + exit_ob_ratio_min = momentum_env_float(r, "MOMENTUM_EXIT_OB_RATIO_MIN", 0.4) + exit_ob_ma_window = momentum_env_int(r, "MOMENTUM_EXIT_OB_MA_WINDOW", 5) + exit_ob_min_profit_pct = momentum_env_float(r, "MOMENTUM_EXIT_OB_MIN_PROFIT_PCT", 0.005) + exit_ob_min_hold_bars = momentum_env_int(r, "MOMENTUM_EXIT_OB_MIN_HOLD_BARS", 3) + # 손절호가 (기본 OFF) + stop_ob_enabled = momentum_env_bool(r, "MOMENTUM_STOP_OB_ENABLED", False) + stop_ob_ratio_min = momentum_env_float(r, "MOMENTUM_STOP_OB_RATIO_MIN", 0.4) + stop_ob_ma_window = momentum_env_int(r, "MOMENTUM_STOP_OB_MA_WINDOW", 5) + stop_ob_min_loss_pct = momentum_env_float(r, "MOMENTUM_STOP_OB_MIN_LOSS_PCT", 0.003) + stop_ob_min_hold_bars = momentum_env_int(r, "MOMENTUM_STOP_OB_MIN_HOLD_BARS", 2) except Exception: rsi_period, mom_rsi_min, mom_rsi_max = 3, 50.0, 80.0 mom_vol_mult, mom_vol_win, mom_time_end = 1.2, 5, 1530 @@ -205,6 +217,10 @@ def get_momentum_defaults_from_db(db=None, *, env_row: Optional[Dict[str, Any]] skip_pre_sub = False force_eod, portfolio_mode = False, True eod_enabled, eod_hm = True, "15:20" + exit_ob_enabled, exit_ob_ratio_min, exit_ob_ma_window = False, 0.4, 5 + exit_ob_min_profit_pct, exit_ob_min_hold_bars = 0.005, 3 + stop_ob_enabled, stop_ob_ratio_min, stop_ob_ma_window = False, 0.4, 5 + stop_ob_min_loss_pct, stop_ob_min_hold_bars = 0.003, 2 finally: if own_db is not None: try: @@ -261,6 +277,18 @@ def get_momentum_defaults_from_db(db=None, *, env_row: Optional[Dict[str, Any]] "force_eod_exit": eod_enabled, "portfolio_mode": portfolio_mode, "scan_interval_min": 1, + # 수익구간 호가매도 + "exit_ob_enabled": exit_ob_enabled, + "exit_ob_ratio_min": exit_ob_ratio_min, + "exit_ob_ma_window": exit_ob_ma_window, + "exit_ob_min_profit_pct": exit_ob_min_profit_pct, + "exit_ob_min_hold_bars": exit_ob_min_hold_bars, + # 손절호가 + "stop_ob_enabled": stop_ob_enabled, + "stop_ob_ratio_min": stop_ob_ratio_min, + "stop_ob_ma_window": stop_ob_ma_window, + "stop_ob_min_loss_pct": stop_ob_min_loss_pct, + "stop_ob_min_hold_bars": stop_ob_min_hold_bars, } diff --git a/kis_trader/engine/momentum_env_keys.py b/kis_trader/engine/momentum_env_keys.py index 5255a1b..a5d776f 100644 --- a/kis_trader/engine/momentum_env_keys.py +++ b/kis_trader/engine/momentum_env_keys.py @@ -43,6 +43,16 @@ MOMENTUM_CONFIG_KEYS = frozenset({ "MOMENTUM_USE_VOL_TRIGGER", "MOMENTUM_USE_RSI_FILTER", "MOMENTUM_E_MIN_CHG_PCT", + "MOMENTUM_EXIT_OB_ENABLED", + "MOMENTUM_EXIT_OB_RATIO_MIN", + "MOMENTUM_EXIT_OB_MA_WINDOW", + "MOMENTUM_EXIT_OB_MIN_PROFIT_PCT", + "MOMENTUM_EXIT_OB_MIN_HOLD_BARS", + "MOMENTUM_STOP_OB_ENABLED", + "MOMENTUM_STOP_OB_RATIO_MIN", + "MOMENTUM_STOP_OB_MA_WINDOW", + "MOMENTUM_STOP_OB_MIN_LOSS_PCT", + "MOMENTUM_STOP_OB_MIN_HOLD_BARS", "MOMENTUM_PATTERN_BREAKOUT", "MOMENTUM_PATTERN_PULLBACK", "MOMENTUM_CHASE_LOOKBACK_MIN", @@ -246,4 +256,24 @@ def params_to_momentum_env_patch(p: Dict[str, Any]) -> Dict[str, str]: _set("MOMENTUM_MAX_STOCKS", int(float(p["max_stocks"]))) if "total_budget_krw" in p: _set("MOMENTUM_TOTAL_BUDGET_KRW", int(float(p["total_budget_krw"]))) + if "exit_ob_enabled" in p: + _set("MOMENTUM_EXIT_OB_ENABLED", str(p["exit_ob_enabled"]).lower()) + if "exit_ob_ratio_min" in p and p.get("exit_ob_ratio_min") not in (None, ""): + _set("MOMENTUM_EXIT_OB_RATIO_MIN", float(p["exit_ob_ratio_min"])) + if "exit_ob_ma_window" in p and p.get("exit_ob_ma_window") not in (None, ""): + _set("MOMENTUM_EXIT_OB_MA_WINDOW", int(float(p["exit_ob_ma_window"]))) + if "exit_ob_min_profit_pct" in p and p.get("exit_ob_min_profit_pct") not in (None, ""): + _set("MOMENTUM_EXIT_OB_MIN_PROFIT_PCT", float(p["exit_ob_min_profit_pct"])) + if "exit_ob_min_hold_bars" in p and p.get("exit_ob_min_hold_bars") not in (None, ""): + _set("MOMENTUM_EXIT_OB_MIN_HOLD_BARS", int(float(p["exit_ob_min_hold_bars"]))) + if "stop_ob_enabled" in p: + _set("MOMENTUM_STOP_OB_ENABLED", str(p["stop_ob_enabled"]).lower()) + if "stop_ob_ratio_min" in p and p.get("stop_ob_ratio_min") not in (None, ""): + _set("MOMENTUM_STOP_OB_RATIO_MIN", float(p["stop_ob_ratio_min"])) + if "stop_ob_ma_window" in p and p.get("stop_ob_ma_window") not in (None, ""): + _set("MOMENTUM_STOP_OB_MA_WINDOW", int(float(p["stop_ob_ma_window"]))) + if "stop_ob_min_loss_pct" in p and p.get("stop_ob_min_loss_pct") not in (None, ""): + _set("MOMENTUM_STOP_OB_MIN_LOSS_PCT", float(p["stop_ob_min_loss_pct"])) + if "stop_ob_min_hold_bars" in p and p.get("stop_ob_min_hold_bars") not in (None, ""): + _set("MOMENTUM_STOP_OB_MIN_HOLD_BARS", int(float(p["stop_ob_min_hold_bars"]))) return patch diff --git a/kis_trader/engine/momentum_hts_logic.py b/kis_trader/engine/momentum_hts_logic.py index 32c96b2..2b9ffe6 100644 --- a/kis_trader/engine/momentum_hts_logic.py +++ b/kis_trader/engine/momentum_hts_logic.py @@ -199,6 +199,191 @@ def _shoulder_ratios(params: Dict[str, Any]) -> Tuple[float, float]: return max(0.0, smh), max(0.0, sc) +def _or_ratio_from_qty(bid_qty: float, ask_qty: float) -> Optional[float]: + """매수총잔량/매도총잔량. 매도 0이면 None (발동 금지).""" + try: + ask = float(ask_qty or 0) + bid = float(bid_qty or 0) + except (TypeError, ValueError): + return None + if ask <= 0: + return None + return bid / ask + + +def _or_from_orderbook_obj(ob: Any) -> Optional[float]: + """OrderbookSnapshot / storage dict / 일부 WS dict → OR.""" + if ob is None: + return None + if hasattr(ob, "total_bid_qty") and hasattr(ob, "total_ask_qty"): + return _or_ratio_from_qty(ob.total_bid_qty, ob.total_ask_qty) + if isinstance(ob, dict): + if "total_bid_qty" in ob or "total_ask_qty" in ob: + return _or_ratio_from_qty(ob.get("total_bid_qty"), ob.get("total_ask_qty")) + return None + + +def collect_exit_ob_or_history( + orderbook_by_code: Optional[Dict[str, Any]], + code: str, + *, + entry_time: str, + asof_time: str, + ma_window: int, +) -> List[float]: + """ + 백테: 진입~asof 구간의 호가 스냅 OR 목록 (시간순). + 스냅 없으면 빈 리스트 → L3 미발동. + """ + if not orderbook_by_code or not code: + return [] + by_min = orderbook_by_code.get(str(code).strip()) + if not by_min or not isinstance(by_min, dict): + return [] + entry12 = str(entry_time or "").replace("-", "").replace(":", "").replace(" ", "")[:12] + asof12 = str(asof_time or "").replace("-", "").replace(":", "").replace(" ", "")[:12] + if len(asof12) < 12: + return [] + if len(entry12) < 12: + entry12 = "000000000000" + out: List[float] = [] + # minute key 는 보통 YYYYMMDDHHMM + for mk in sorted(by_min.keys()): + mk12 = str(mk)[:12] + if mk12 < entry12 or mk12 > asof12: + continue + snaps = by_min.get(mk) or [] + for snap in snaps: + orv = _or_from_orderbook_obj(snap) + if orv is not None: + out.append(orv) + need = max(1, int(ma_window)) * 3 + if len(out) > need: + out = out[-need:] + return out + + +def append_live_exit_ob_or( + history: List[Any], + ws: Any, + code: str, + *, + ma_window: int, + max_age_sec: float = 3.0, +) -> List[Any]: + """실매: WS 호가 스냅 1장 OR append. 없으면 history 그대로.""" + hist: List[Any] = list(history or []) + if ws is None or not code: + return hist + snap = None + try: + getter = getattr(ws, "get_orderbook_snapshot", None) + if callable(getter): + snap = getter(code, max_age_sec=max_age_sec) + except Exception: + snap = None + orv = _or_from_orderbook_obj(snap) + if orv is not None: + hist.append(orv) + need = max(1, int(ma_window)) * 3 + if len(hist) > need: + hist = hist[-need:] + return hist + + +def _ob_or_ma_window_for_history(params: Dict[str, Any]) -> int: + """수익구간·손절호가 중 켜진 쪽 MA창의 최댓값 (히스토리 수집용).""" + w = 1 + if params.get("exit_ob_enabled", False): + w = max(w, max(1, int(params.get("exit_ob_ma_window", 5) or 5))) + if params.get("stop_ob_enabled", False): + w = max(w, max(1, int(params.get("stop_ob_ma_window", 5) or 5))) + return w + + +def need_ob_or_history(params: Dict[str, Any]) -> bool: + """호가 OR 히스토리가 필요한지 (수익구간 또는 손절호가 ON).""" + return bool(params.get("exit_ob_enabled", False) or params.get("stop_ob_enabled", False)) + + +def _check_exit_ob_l3( + params: Dict[str, Any], + ob_or_history: List[Optional[float]], + entry: float, + current_price: float, + hold_bars: int, +) -> bool: + """ + 수익구간 호가매도 판정 (래칫·어깨 다음 · 손절 전). + + 발동 조건 (모두 충족 시만 True): + 1) exit_ob_enabled + 2) OR_MA < exit_ob_ratio_min + 3) 현재가 >= 진입가 * (1 + exit_ob_min_profit_pct) + 4) hold_bars >= exit_ob_min_hold_bars + 5) OR 스냅 부족 시 발동 금지 (폴백 금지) + """ + if not params.get("exit_ob_enabled", False): + return False + + ratio_min = float(params.get("exit_ob_ratio_min", 0.4)) + ma_window = max(1, int(params.get("exit_ob_ma_window", 5))) + min_profit = float(params.get("exit_ob_min_profit_pct", 0.005)) + min_hold = max(0, int(params.get("exit_ob_min_hold_bars", 3))) + + if hold_bars < min_hold: + return False + + if entry <= 0 or current_price < entry * (1.0 + min_profit): + return False + + valid = [float(v) for v in ob_or_history if v is not None] + if len(valid) < ma_window: + return False + or_ma = sum(valid[-ma_window:]) / float(ma_window) + + return or_ma < ratio_min + + +def _check_stop_ob( + params: Dict[str, Any], + ob_or_history: List[Optional[float]], + entry: float, + current_price: float, + hold_bars: int, +) -> bool: + """ + 손실구간 손절호가 판정 (수익구간 호가매도 다음 · 하드 손절 직전). + + 발동 조건 (모두 충족 시만 True): + 1) stop_ob_enabled + 2) OR_MA < stop_ob_ratio_min + 3) 현재가 <= 진입가 * (1 - stop_ob_min_loss_pct) # 이미 손해 + 4) hold_bars >= stop_ob_min_hold_bars + 5) OR 스냅 부족 시 발동 금지 (폴백 금지) + """ + if not params.get("stop_ob_enabled", False): + return False + + ratio_min = float(params.get("stop_ob_ratio_min", 0.4)) + ma_window = max(1, int(params.get("stop_ob_ma_window", 5))) + min_loss = abs(float(params.get("stop_ob_min_loss_pct", 0.003))) + min_hold = max(0, int(params.get("stop_ob_min_hold_bars", 2))) + + if hold_bars < min_hold: + return False + + if entry <= 0 or current_price > entry * (1.0 - min_loss): + return False + + valid = [float(v) for v in ob_or_history if v is not None] + if len(valid) < ma_window: + return False + or_ma = sum(valid[-ma_window:]) / float(ma_window) + + return or_ma < ratio_min + + def _minutes_held(position: Dict[str, Any], candle: Dict[str, Any]) -> Optional[int]: try: e = _t2dt(position.get("entry_time") or position.get("buy_time", "")) @@ -486,12 +671,14 @@ def check_sell_signal_momentum_hts_live( [청산 우선순위] 1순위 래칫컷 (설정 시) 2순위 어깨컷 (고점 대비 되돌림) - 3순위 손절 (하드 스탑) - 4순위 트레일컷 (추세 이익 보호) - 5순위 시간컷 - 6순위 금액손실컷 (어깨·래칫 미발동 시) - 7순위 익절 (tp_max 상한, 하드 캡) - 8순위 장마감청산 + 3순위 수익구간 호가매도 (exit_ob_enabled, 기본 OFF) + 4순위 손절호가 (stop_ob_enabled, 기본 OFF · 손실+OR붕괴) + 5순위 손절 (하드 스탑 · 가격 하나) + 6순위 트레일컷 (추세 이익 보호) + 7순위 시간컷 + 8순위 금액손실컷 (어깨·래칫 미발동 시) + 9순위 익절 (tp_max 상한, 하드 캡) + 10순위 장마감청산 """ sl_pct = -abs(float(params.get("sl_pct", params.get("stop_loss_pct", 0.03)))) tp_pct = effective_tp_pct_from_params(params) @@ -546,11 +733,22 @@ def check_sell_signal_momentum_hts_live( if lo <= shoulder_line: return ("어깨컷", shoulder_line) - # 3순위 손절 + # 3순위 수익구간 호가매도 (OFF 기본) — hold_bars = 보유 분(1분봉≈1봉) + ob_or_history: List[Optional[float]] = list(params.get("_ob_or_history", [])) + held_min = _minutes_held(position, current_candle) + hold_bars_now = int(held_min) if held_min is not None else int(position.get("hold_bars", 0) or 0) + if _check_exit_ob_l3(params, ob_or_history, entry, cl, hold_bars_now): + return ("호가컷", cl) + + # 4순위 손절호가 (OFF 기본) — 손실+OR붕괴 · 하드 손절보다 앞 + if _check_stop_ob(params, ob_or_history, entry, cl, hold_bars_now): + return ("손절호가", cl) + + # 5순위 손절 if lo <= sl_line: return ("손절", sl_line) - # 4순위 트레일 + # 6순위 트레일 if trail_pct > 0 and max_price > entry: trail_arm_line = entry * (1.0 + trail_arm_pct) if trail_arm_pct <= 0 or max_price >= trail_arm_line: @@ -558,13 +756,13 @@ def check_sell_signal_momentum_hts_live( if lo <= trail_line: return ("트레일컷", trail_line) - # 5순위 시간컷 + # 7순위 시간컷 if max_hold_bars > 0: - held = _minutes_held(position, current_candle) + held = held_min if held_min is not None else _minutes_held(position, current_candle) if held is not None and held >= max_hold_bars: return ("시간컷", cl) - # 6순위 금액손실컷 + # 8순위 금액손실컷 profit_val = (lo - entry) * qty drop_pct = (entry - lo) / entry if entry > 0 else 0.0 if ( @@ -576,11 +774,11 @@ def check_sell_signal_momentum_hts_live( exit_px = entry - (max_loss_krw / qty) if qty > 0 else lo return ("금액손실컷", exit_px) - # 7순위 익절 (하드 캡) + # 9순위 익절 (하드 캡) if hi >= tp_line: return ("익절", tp_line) - # 8순위 장마감 + # 9순위 장마감 if is_eod: return ("장마감청산", cl) diff --git a/kis_trader/engine/momentum_tick_replay.py b/kis_trader/engine/momentum_tick_replay.py index 6c7e9af..48142f8 100644 --- a/kis_trader/engine/momentum_tick_replay.py +++ b/kis_trader/engine/momentum_tick_replay.py @@ -542,6 +542,7 @@ def resolve_momentum_sell_for_bar( is_eod: bool = False, ticks_by_code: Optional[Dict[str, Dict[str, List[Dict[str, Any]]]]] = None, code: str = "", + orderbook_by_code: Optional[Dict[str, Any]] = None, ) -> Optional[Tuple[str, float, str, float, str]]: """ 한 분봉 청산 — 틱 우선 → (옵션) OHLC intrabar → last-price 벽시계. @@ -551,15 +552,37 @@ def resolve_momentum_sell_for_bar( exit_source: ws_ticks | ohlc_bar | wallclock_last """ from kis_trader.engine.momentum_engine import check_sell_signal_momentum_backtest_bar + from kis_trader.engine.momentum_hts_logic import ( + collect_exit_ob_or_history, + need_ob_or_history, + _ob_or_ma_window_for_history, + ) ct = str(bar.get("candle_time") or "") entry_time = str(position.get("entry_time") or "") - if momentum_backtest_use_tick_exit(params) and ticks_by_code and code: + # 수익구간·손절호가 OR 히스토리 (둘 중 ON일 때만 DB 스냅 수집 · 기본 OFF → no-op) + p = params + if need_ob_or_history(p) and code and orderbook_by_code: + ors = collect_exit_ob_or_history( + orderbook_by_code, + code, + entry_time=entry_time, + asof_time=ct, + ma_window=_ob_or_ma_window_for_history(p), + ) + position["_ob_or_history"] = ors + p = dict(params) + p["_ob_or_history"] = list(ors) + else: + p = dict(params) + p["_ob_or_history"] = list(position.get("_ob_or_history") or []) + + if momentum_backtest_use_tick_exit(p) and ticks_by_code and code: minute_ticks = collect_minute_ticks(ticks_by_code, code, ct) if minute_ticks: tick_res = try_momentum_sell_on_ticks( - position, minute_ticks, params, + position, minute_ticks, p, is_eod=is_eod, entry_time=entry_time, ) if tick_res: @@ -588,10 +611,10 @@ def resolve_momentum_sell_for_bar( # 절대규칙: 틱 청산 ON 이면 OHLC 봉 폴백으로 숫자 변조 금지 # (FALLBACK_OHLC 체크 ON 이어도 무시. 벽시계 last-price 는 OHLC intrabar 가 아님) if ( - not momentum_backtest_use_tick_exit(params) - and momentum_backtest_tick_fallback_ohlc(params) + not momentum_backtest_use_tick_exit(p) + and momentum_backtest_tick_fallback_ohlc(p) ): - res = check_sell_signal_momentum_backtest_bar(position, bar, params, is_eod=is_eod) + res = check_sell_signal_momentum_backtest_bar(position, bar, p, is_eod=is_eod) if res: reason, exit_price = res try: @@ -602,7 +625,7 @@ def resolve_momentum_sell_for_bar( hold_min = 0.0 return reason, float(exit_price), ct, hold_min, "ohlc_bar" - if not momentum_backtest_wallclock_last_price(params): + if not momentum_backtest_wallclock_last_price(p): return None last_px = resolve_momentum_wallclock_last_px( @@ -612,7 +635,7 @@ def resolve_momentum_sell_for_bar( return None update_momentum_bt_last_px(position, last_px, ct) wall_res = try_momentum_sell_wallclock_last( - position, last_px, ct, params, is_eod=is_eod, + position, last_px, ct, p, is_eod=is_eod, ) if not wall_res: return None diff --git a/kis_trader/engine/orderbook_filter.py b/kis_trader/engine/orderbook_filter.py index 03133d4..30b3058 100644 --- a/kis_trader/engine/orderbook_filter.py +++ b/kis_trader/engine/orderbook_filter.py @@ -1,11 +1,11 @@ """ -kis_trader/engine/orderbook_filter.py — TRIGGER 진입 호가 필터 (키움 0D 캐시) +kis_trader/engine/orderbook_filter.py — TRIGGER 진입 호가 필터 ============================================================================ -실매 전용 — ``params['_orderbook_ws']`` / ``_whipsaw_ws`` 의 ``get_orderbook`` 사용. -백테는 호가 없으면 스킵(OFF) 또는 env 로 비활성. +실매 — ``params['_orderbook_ws']`` 의 ``get_orderbook`` (ls_condition→LS UH1, 그외 키움 0D). +백테 — DB 스냅 본체 재계산. history_source=ls 이면 ls_ws_orderbook 만 (키움 판정 폴백 금지). -필터(주문 차단)와 수집(ws_orderbook 저장)은 분리: - - ``{전략}_ORDERBOOK_FILTER_ENABLED`` — 실매 탈락 적용 (글로벌 ORDERBOOK_* 폐기) +필터(주문 차단)와 수집(호가 저장)은 분리: + - ``{전략}_ORDERBOOK_FILTER_ENABLED`` — 실매 탈락 적용 - ``WS_ORDERBOOK_COLLECT_ENABLED`` — TRIGGER 판정 스냅 저장 (필터 OFF여도 가능) 모든 임계값 env/DB — 하드코딩 금지 (기본값은 orderbook_env 상수). @@ -213,8 +213,15 @@ def orderbook_reject_for_entry( return (None, None) # log_backfill 판정 재생 — 호가 본체 없이 실매와 동일 pass/reject (필터 ON일 때만) + # LS 소스·명시 비활성 시 금지 (키움 판정 메모로 구멍 메우기 = 정합 깨짐) log_verdict = params.get("_backtest_log_orderbook_verdict") bt_snap = params.get("_backtest_orderbook_snapshot") + if params.get("_backtest_disable_log_orderbook_verdict"): + log_verdict = None + if str(params.get("_orderbook_history_source") or "").strip().lower() in ( + "ls", "ls_condition", "ls_afr", "ls_ws", + ): + log_verdict = None if filter_on and log_verdict is not None and ( bt_snap is None or str(getattr(bt_snap, "source", "") or "") == LOG_BACKFILL_SOURCE ): diff --git a/kis_trader/execution/kis_client.py b/kis_trader/execution/kis_client.py index ba62ea4..546929c 100644 --- a/kis_trader/execution/kis_client.py +++ b/kis_trader/execution/kis_client.py @@ -101,6 +101,7 @@ class KISClient(SafeRequest): self._last_order_msg1: Optional[str] = None self._last_sell_msg_cd: Optional[str] = None self._last_sell_msg1: Optional[str] = None + self._inquire_price_cache: dict = {} self._init_token() @@ -128,11 +129,11 @@ class KISClient(SafeRequest): return val def _init_token(self) -> None: - """kis_token_manager 경로로만 발급 (23시간 캐시, 파일 잠금 준수).""" + """kis_token_manager 경로로만 (세션커버 시 재사용, 파일 잠금·1일1회 준수).""" try: from kis_token_manager import KisTokenManager, ensure_token - # 캐시가 유효하면 즉시 사용, 만료면 재발급 + # 해당 모드만: 세션미커버/만료일 때만 tokenP. 양쪽은 기동 ensure_both. ensure_token(self.mock) self._token = KisTokenManager.instance(is_mock=self.mock).get_token() except Exception as e: @@ -140,7 +141,7 @@ class KISClient(SafeRequest): self._token = None def _refresh_token_if_needed(self) -> None: - """호출 직전 토큰 만료 임박 시 선제 갱신.""" + """호출 직전: 만료 시에만 비상 갱신 (장중 10분 선제 발급 없음).""" try: from kis_token_manager import KisTokenManager @@ -209,7 +210,13 @@ class KISClient(SafeRequest): return None def inquire_price(self, code: str) -> Optional[dict]: - """현재가 [v1_국내주식-007]""" + """현재가 [v1_국내주식-007] (60초 캐싱)""" + now = __import__("time").time() + if code in self._inquire_price_cache: + ts, data = self._inquire_price_cache[code] + if now - ts < 60.0: + return data + r = self._get( "/uapi/domestic-stock/v1/quotations/inquire-price", "FHKST01010100", @@ -218,7 +225,10 @@ class KISClient(SafeRequest): if r.status_code != 200: return None j = r.json() - return j.get("output") if j.get("rt_cd") == "0" else None + out = j.get("output") if j.get("rt_cd") == "0" else None + if out: + self._inquire_price_cache[code] = (now, out) + return out def inquire_stock_name(self, code: str) -> Optional[str]: """국내 종목명 [주식기본조회 CTPF1002R]. 약명(prdt_abrv_name) 우선.""" diff --git a/kis_trader/execution/order_manager.py b/kis_trader/execution/order_manager.py index 38ae45d..b401be5 100644 --- a/kis_trader/execution/order_manager.py +++ b/kis_trader/execution/order_manager.py @@ -595,6 +595,10 @@ class OrderManager: now_str = dt.now().strftime("%Y-%m-%d %H:%M:%S") from ..utils.strategy_ids import canonical_strategy_id + # entry_features 안에 임시로 숨겨온 _env_snapshot 분리 + feats = dict(req.entry_features or {}) + snap_str = feats.pop("_env_snapshot", None) + self.db.upsert_trade({ "code": req.code, "name": req.name, @@ -611,7 +615,8 @@ class OrderManager: "status": "HOLDING", "buy_date": now_str, "size_class": req.size_class or "", - "entry_features": req.entry_features or {}, + "entry_features": feats, + "env_snapshot": snap_str, }) self.invalidate_holdings_cache() if self.cash_ledger is not None and delta_qty > 0: @@ -1973,8 +1978,28 @@ class OrderManager: "%s❌ [매수주문거부] %s %s: %s%s", LOG_RED, req.name, req.code, m1 or cd, LOG_RESET, ) + try: + from kis_trader.utils.ops_alert import note_counter, ops_alert + streak = note_counter("buy_reject") + need = max(1, get_env_int("OPS_ALERT_ORDER_REJECT_STREAK", 3)) + if streak >= need: + ops_alert( + "order_buy_reject", + f"매수주문 연속거부 {streak}회", + detail=f"{req.strategy_id} {req.name}({req.code}) {m1 or cd}", + level="critical", + ) + note_counter("buy_reject", reset=True) + except Exception: + pass return OrderResult(False, reason=reason, request=req) + try: + from kis_trader.utils.ops_alert import note_counter + note_counter("buy_reject", reset=True) + except Exception: + pass + # ── [4] DB에 주문 기록 (ODNO PK, 전략단위 UNIQUE) ────────── inserted = self.db.insert_order( ord_no=ord_no, @@ -2216,8 +2241,29 @@ class OrderManager: ) elif "잔고" in m1 or "보유" in m1 or "APBK3020" in cd: return self._purge_ghost_position(req, "broker_response") + else: + try: + from kis_trader.utils.ops_alert import note_counter, ops_alert + streak = note_counter("sell_reject") + need = max(1, get_env_int("OPS_ALERT_ORDER_REJECT_STREAK", 3)) + if streak >= need: + ops_alert( + "order_sell_reject", + f"매도주문 연속실패 {streak}회", + detail=f"{req.strategy_id} {req.name}({req.code}) {m1 or cd}", + level="critical", + ) + note_counter("sell_reject", reset=True) + except Exception: + pass return OrderResult(False, reason=f"sell_reject:{cd or m1}", request=req) + try: + from kis_trader.utils.ops_alert import note_counter + note_counter("sell_reject", reset=True) + except Exception: + pass + # ── [4] 주문 기록 ──────────────────────────────────────── self.db.insert_order( ord_no=ord_no, strategy_id=req.strategy_id, diff --git a/kis_trader/execution/orderbook_sell.py b/kis_trader/execution/orderbook_sell.py index 000c2f6..0322e16 100644 --- a/kis_trader/execution/orderbook_sell.py +++ b/kis_trader/execution/orderbook_sell.py @@ -16,6 +16,7 @@ logger = logging.getLogger("kis_trader.orderbook_sell") _URGENT_SELL_TOKENS = ( "손절", "손실컷", "금액손실", "stop_loss", "stop loss", + "손절호가", # 손실구간 호가매도 (손절 부분문자와도 매칭되나 명시) "장마감", "eod", "emergency", "긴급", ) diff --git a/kis_trader/execution/orphan_reconcile.py b/kis_trader/execution/orphan_reconcile.py index bbe5267..8ed76c6 100644 --- a/kis_trader/execution/orphan_reconcile.py +++ b/kis_trader/execution/orphan_reconcile.py @@ -18,7 +18,7 @@ import logging from typing import Any, Dict, List, Optional, Set, Tuple from ..engine.strategy_eod import _STRATEGY_EOD_SPEC, parse_eod_hm, resolve_strategy_eod_params -from ..utils.env import get_env_bool, get_env_from_db, get_env_int +from ..utils.env import get_env_bool, get_env_float, get_env_from_db, get_env_int from ..utils.strategy_ids import canonical_strategy_id logger = logging.getLogger("kis_trader.orphan_reconcile") @@ -393,6 +393,134 @@ def purge_ghost_active_trades(order_mgr, broker: Optional[Dict[str, Any]] = None return out +def sync_active_trades_with_broker(order_mgr, broker: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: + """ + 개장 전(08:35~09:15) 또는 고아복구 시 DB(active_trades)와 증권사 실계좌 잔고를 동기화. + - 기업 이벤트(액면분할, 무상증자 등) 또는 수기 매입으로 계좌 매수단가/수량이 변경된 경우, + DB의 avg_buy_price, current_qty, stop_price, target_price, max_price 를 비율에 맞춰 보정(Sync). + - 오진입 손절 및 트레이링 스톱 착시 방지. + """ + out: Dict[str, Any] = {"synced": [], "failed": []} + if not get_env_bool("ORPHAN_RECONCILE_ENABLED", True): + out["msg"] = "ORPHAN_RECONCILE_ENABLED=false" + return out + + db = order_mgr.db + if broker is None: + broker = order_mgr.get_broker_holdings(force=True) + if not getattr(order_mgr, "_holdings_last_fetch_ok", False): + out["error"] = "실계좌 잔고 조회 실패 — 안전을 위해 동기화 스킵" + logger.warning("🔄 [계좌동기화] 실계좌 잔고 조회 실패 — 안전을 위해 스킵") + return out + + manual_hold = parse_manual_hold_codes() + min_diff_px = get_env_float("SYNC_PRICE_DIFF_MIN", 0.5) + + try: + rows = db.conn.execute("SELECT * FROM active_trades").fetchall() or [] + except Exception as e: + out["error"] = str(e) + logger.warning("🔄 [계좌동기화] active_trades 조회 실패: %s", e) + return out + + now_str = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + for row in rows: + d = dict(row) + code = str(d.get("code") or "").strip() + if not code: + continue + name = str(d.get("name") or code) + strategy = str(d.get("strategy") or "").strip() + + # 수동 보호 및 장기 투자 포지션은 개입 차단 + if code in manual_hold or strategy.upper() == "HOLDING": + continue + + br = broker.get(code) + if not br: + # 브로커에 0주인 종목은 purge_ghost_active_trades 가 처리하므로 여기선 스킵 + continue + + br_qty = int(br.get("qty") or 0) + br_avg = float(br.get("avg_price") or 0.0) + db_qty = int(d.get("current_qty") or 0) + db_avg = float(d.get("avg_buy_price") or 0.0) + + if br_qty <= 0 or br_avg <= 0: + continue + + qty_diff = (br_qty != db_qty) + price_diff = abs(br_avg - db_avg) >= min_diff_px + + if not (qty_diff or price_diff): + continue + + # 단가/수량 변경 감지! 비율(ratio) 계산하여 손절가·익절가·고점(max_price) 비례 조정 + ratio = (br_avg / db_avg) if (db_avg > 0) else 1.0 + old_stop = float(d.get("stop_price") or 0.0) + old_target = float(d.get("target_price") or 0.0) + old_max = float(d.get("max_price") or 0.0) + + new_stop = round(old_stop * ratio, 2) if old_stop > 0 else 0.0 + new_target = round(old_target * ratio, 2) if old_target > 0 else 0.0 + new_max = max(br_avg, round(old_max * ratio, 2)) if old_max > 0 else br_avg + new_inv = br_avg * br_qty + + try: + if strategy: + db.conn.execute( + "UPDATE active_trades SET avg_buy_price=%s, current_qty=%s, target_qty=%s, " + "stop_price=%s, target_price=%s, max_price=%s, total_invested=%s, updated_at=%s " + "WHERE code=%s AND strategy=%s", + (br_avg, br_qty, br_qty, new_stop, new_target, new_max, new_inv, now_str, code, strategy) + ) + else: + db.conn.execute( + "UPDATE active_trades SET avg_buy_price=%s, current_qty=%s, target_qty=%s, " + "stop_price=%s, target_price=%s, max_price=%s, total_invested=%s, updated_at=%s " + "WHERE code=%s", + (br_avg, br_qty, br_qty, new_stop, new_target, new_max, new_inv, now_str, code) + ) + + msg = ( + f"🔄 [계좌 싱크 로봇] 원장 변동 동기화! {name}({code}) [{strategy}]\n" + f" · 수량: {db_qty}주 ➔ {br_qty}주\n" + f" · 매입단가: {db_avg:,.0f}원 ➔ {br_avg:,.0f}원 (비율 {ratio:.4f})\n" + f" · 손절가 보정: {old_stop:,.0f}원 ➔ {new_stop:,.0f}원\n" + f" · 고점(max) 보정: {old_max:,.0f}원 ➔ {new_max:,.0f}원" + ) + logger.info(msg) + out["synced"].append({ + "code": code, "name": name, "strategy": strategy, + "old_qty": db_qty, "new_qty": br_qty, + "old_avg": db_avg, "new_avg": br_avg, + }) + # 알림 발송 + if hasattr(order_mgr, "notifier") and order_mgr.notifier and hasattr(order_mgr.notifier, "send_message"): + try: + order_mgr.notifier.send_message(msg) + except Exception: + pass + elif hasattr(order_mgr, "send_telegram_msg"): + try: + order_mgr.send_telegram_msg(msg) + except Exception: + pass + except Exception as e: + out["failed"].append({"code": code, "name": name, "error": str(e)}) + logger.exception("🔄 [계좌동기화] UPDATE 실패 %s", code) + + if out["synced"]: + try: + order_mgr.invalidate_holdings_cache() + except Exception: + pass + out["synced_count"] = len(out["synced"]) + else: + out["synced_count"] = 0 + return out + + def reconcile_orphan_positions(order_mgr) -> Dict[str, Any]: """ 장마감 후 1회 호출 — 봇 고아만 active_trades 에 upsert. @@ -412,6 +540,7 @@ def reconcile_orphan_positions(order_mgr) -> Dict[str, Any]: "failed": [], "ghost_purged": [], "stale_purged": [], + "synced": [], } if not get_env_bool("ORPHAN_RECONCILE_ENABLED", True): @@ -544,6 +673,15 @@ def reconcile_orphan_positions(order_mgr) -> Dict[str, Any]: except Exception: pass + # 실계좌 평단가·수량 동기화 (액면조정·수기매입 등 보정) + sync = sync_active_trades_with_broker(order_mgr, broker=broker) + result["synced"] = list(sync.get("synced") or []) + result["synced_count"] = int(sync.get("synced_count") or 0) + if sync.get("error"): + result["sync_error"] = sync["error"] + for f in sync.get("failed") or []: + result["failed"].append(f) + # 유령(DB有·브로커0) — 동일 잔고 스냅샷으로 삭제 (추가 REST 없음) ghost = purge_ghost_active_trades(order_mgr, broker=broker) result["ghost_purged"] = list(ghost.get("purged") or []) @@ -557,9 +695,10 @@ def reconcile_orphan_positions(order_mgr) -> Dict[str, Any]: result["failed_count"] = len(result["failed"]) result["skipped_after_sell_count"] = len(result["skipped_after_sell"]) logger.info( - "🧩 [고아복구] 완료 — 복구 %d / 청산후스킵 %d / 청산후유령정리 %d / " + "🧩 [고아복구] 완료 — 복구 %d / 단가·수량동기화 %d / 청산후스킵 %d / 청산후유령정리 %d / " "유령삭제 %d / 실패 %d / 보호(수동) %d / 이미추적 %d / 주문없음 %d", len(result["reconciled"]), + result["synced_count"], len(result["skipped_after_sell"]), result["stale_purged_count"], result["ghost_purged_count"], diff --git a/kis_trader/main.py b/kis_trader/main.py index 5d65070..05251a4 100644 --- a/kis_trader/main.py +++ b/kis_trader/main.py @@ -229,6 +229,8 @@ class TradingOrchestrator: self._orphan_reconcile_date: str = "" # Pre-EOD 고아복구 중복 실행 가드 (당일 1회) self._orphan_pre_eod_reconcile_date: str = "" + # 08:35~09:15 개장 전/초기 계좌 평단가·수량 동기화 가드 (당일 1회 — 권리락·액면분할 방어) + self._morning_sync_date: str = "" # start_day_asset 조회가 모의 서버 500 등으로 실패할 때 무한 재시도 방지. # 다음 시도 가능 epoch (0 = 즉시 가능). 실패 시 N초 백오프. # 한투 모의 inquire-balance 가 간헐 500 → 20초 hb 마다 폭주하던 이슈 방지. @@ -551,6 +553,24 @@ class TradingOrchestrator: detail = self._universe_tag(sid) lines.append(f"{sid}: {src}") lines.append(f" └ {detail}") + + ws_info = [] + if getattr(self, "kiwoom_ws", None) is not None: + km = "모의" if getattr(self.kiwoom_ws, "is_mock", False) else "실전" + ws_info.append(f"키움({km})") + + kis_cache = None + if getattr(self, "ws", None) is not None: + kis_cache = getattr(self.ws, "ws_cache", None) + + if kis_cache is not None: + km = "모의" if getattr(kis_cache, "is_mock", False) else "실전" + ws_info.append(f"KIS({km})") + + if ws_info: + lines.append("") + lines.append(f"📡 [시세웹소켓] {', '.join(ws_info)}") + if startup_cash_line: lines.append("") prefix = "✅" if startup_cash_ok else "⚠️" @@ -608,6 +628,15 @@ class TradingOrchestrator: logger.info("🚀 kis_trader 통합 봇 시작") logger.info("=" * 70) + # KIS 접근토큰: 모의만 쓰면 실전이 방치되던 구멍 차단. + # 세션미커버/만료분만 발급 (1일1회·EGW00133 준수). approval 6h 와 무관. + try: + from kis_token_manager import ensure_both_tokens + + ensure_both_tokens() + except Exception as e: + logger.warning("KIS 토큰(실전+모의) 기동 점검 실패: %s", e) + # WS 허브 먼저 기동 (전략이 구독 요청하기 전에) ws_ok = self.ws.start() logger.info("WSManager active=%s", ws_ok) @@ -949,32 +978,14 @@ class TradingOrchestrator: # ------------------------------------------------------------------ def _start_ws_validator(self) -> None: - """키움 WS 기동 + (선택) KIS↔키움 가격 검증기. - - - ``WS_PROVIDER=kis_with_validation`` : 검증기 + 키움 WS - - ``WS_SUBSCRIBE_KIS_MINIMAL=true`` : KIS 구독 최소화용 키움 WS (검증기는 provider 에 따라) - """ + """키움 WS 기동 + (선택) KIS↔키움 가격 검증기.""" from .utils.env import get_env_from_db minimal = get_env_bool("WS_SUBSCRIBE_KIS_MINIMAL", False) - provider = (get_env_from_db("WS_PROVIDER", "kis_only") or "kis_only").strip().lower() - - need_kiwoom = bool(minimal or provider == "kis_with_validation") - need_validator = provider == "kis_with_validation" - - if not need_kiwoom: - if provider not in ("kis_only", "kis_with_validation"): - logger.warning( - "⚠️ WS_PROVIDER='%s' 알 수 없음 → kis_only 로 취급", - provider, - ) - logger.info("ℹ️ 키움 WS 미기동 (WS_PROVIDER=kis_only & WS_SUBSCRIBE_KIS_MINIMAL=false)") - return - - if minimal and not need_validator: - logger.info( - "ℹ️ WS_SUBSCRIBE_KIS_MINIMAL=true → 키움 WS 기동 (WS_PROVIDER=kis_only: 검증기 비활성)", - ) + need_validator = get_env_bool("LIVE_VALIDATOR_ENABLED", False) + + # 키움 WS는 이제 틱/호가 프로바이더 및 적재 옵션에 따라 무조건 기동 + need_kiwoom = True # 키움 키 로드 — 검증/분리 시세 모두 실키·실전 권장 force_real_str = (get_env_from_db("KIWOOM_WS_FORCE_REAL", "true") or "true").strip().lower() @@ -1024,6 +1035,18 @@ class TradingOrchestrator: logger.warning("키움 WS 시작 실패") self.kiwoom_ws = None return + else: + # 🚀 키움 틱 적재 설정 적용 + tr = getattr(self.ws, "tick_recorder", None) + if tr is not None and get_env_bool("WS_TICK_SAVE_KIWOOM", True): + self.kiwoom_ws.attach_tick_recorder(tr) + logger.info("✅ 키움 WS에 TickRecorder 부착 완료 (키움 틱 → ws_ticks 적재)") + + # 🚀 키움 호가 적재 설정 적용 + tsr = getattr(self.ws, "trigger_snapshot_recorder", None) + if tsr is not None and get_env_bool("WS_ORDERBOOK_SAVE_KIWOOM", True): + self.kiwoom_ws.attach_trigger_snapshot_recorder(tsr) + logger.info("✅ 키움 WS에 TriggerSnapshotRecorder 부착 완료 (키움 호가 → ws_orderbook 적재)") except Exception as e: logger.warning("키움 WS 인스턴스 생성 실패: %s", e) self.kiwoom_ws = None @@ -1041,7 +1064,8 @@ class TradingOrchestrator: sync_kiwoom_to_kis=not minimal, ) self.ws_validator.start() - logger.info("🔬 [시세 검증] WS_PROVIDER=%s — ws_price_validation 기록", provider) + self.ws_validator.start() + logger.info("🔬 [시세 검증] LIVE_VALIDATOR_ENABLED=True — ws_price_validation 기록 (KIS↔키움 교차 검증)") except Exception as e: logger.warning("Validator 기동 실패: %s", e) self.ws_validator = None @@ -1079,11 +1103,9 @@ class TradingOrchestrator: "(갭보정·분봉은 키움)" ) - force_real = get_env_bool("LS_WS_FORCE_REAL", True) try: row = self.db.conn.execute( - "SELECT LS_APP_KEY_REAL, LS_APP_SECRET_REAL, " - "LS_APP_KEY_MOCK, LS_APP_SECRET_MOCK " + "SELECT LS_APP_KEY_REAL, LS_APP_SECRET_REAL " "FROM env_config ORDER BY id DESC LIMIT 1" ).fetchone() except Exception as e: @@ -1093,14 +1115,9 @@ class TradingOrchestrator: logger.warning("env_config 없음 → LS WS 비활성") return r = dict(row) - if force_real: - app_key = (r.get("LS_APP_KEY_REAL") or "").strip() - app_secret = (r.get("LS_APP_SECRET_REAL") or "").strip() - is_mock = False - else: - app_key = (r.get("LS_APP_KEY_MOCK") or "").strip() - app_secret = (r.get("LS_APP_SECRET_MOCK") or "").strip() - is_mock = True + app_key = (r.get("LS_APP_KEY_REAL") or "").strip() + app_secret = (r.get("LS_APP_SECRET_REAL") or "").strip() + is_mock = False if not app_key or not app_secret: logger.warning( "LS AppKey/Secret 미설정 → LS WS 비활성 " @@ -1130,6 +1147,11 @@ class TradingOrchestrator: chetime=str(payload.get("chetime") or ""), tr_cd=str(payload.get("tr_cd") or ""), ) + # 1b) 호가 틱동기 — 체결 1건당 RAM 호가 1장 (스냅 없으면 생략=실매와 동일) + try: + self._maybe_save_ls_orderbook_on_tick(code, payload) + except Exception: + pass # 2) 실매 get_recent_ticks 용 RAM 만 (기본). # ws_ticks 이중 INSERT 는 용량 낭비 → LS_WS_TICK_MIRROR_WS_TICKS=true 때만. tr = getattr(self.ws, "tick_recorder", None) @@ -1174,10 +1196,14 @@ class TradingOrchestrator: self.ls_ws.attach_orderbook_recorder(_on_orderbook) self.ls_ws.attach_vi_recorder(_on_vi) if also_hoga: + _ob_mode = ( + get_env_from_db("LS_WS_ORDERBOOK_SAVE_MODE", "tick") or "tick" + ).strip().lower() logger.info( "LS WS 호가(UH1) 구독 ON → ls_ws_orderbook " - "(save=%s gap_ms=%s)", + "(save=%s mode=%s gap_ms=%s)", get_env_bool("LS_WS_ORDERBOOK_SAVE", True), + _ob_mode, get_env_int("LS_WS_ORDERBOOK_SAVE_MS", 1000), ) if get_env_bool("LS_WS_UVI_ENABLED", True): @@ -1220,7 +1246,7 @@ class TradingOrchestrator: ) else: logger.info( - "📡 [LS WS] 기동 — ticks/candles " + "📡 [LS WS] 기동 — ticks/candles/hoga " "(VALIDATION=%s HISTORY=%s trade_ls=%s)", validation_on, history_on, trade_ls, ) @@ -1234,6 +1260,78 @@ class TradingOrchestrator: self.ls_ws = None self.ls_ws_validator = None + def _maybe_save_ls_orderbook_on_tick(self, code: str, payload: dict) -> None: + """LS 체결 1건당 호가 RAM 스냅 1장 → ls_ws_orderbook (틱 동기). + + 스냅 없거나 만료면 저장 안 함 (OHLC·키움으로 메우지 않음 = 실매 필터와 동일). + """ + if not get_env_bool("LS_WS_ORDERBOOK_SAVE", True): + return + mode = ( + get_env_from_db("LS_WS_ORDERBOOK_SAVE_MODE", "tick") or "tick" + ).strip().lower() + if mode not in ("tick", "on_tick", "tick_sync", "sync"): + return + ls = getattr(self, "ls_ws", None) + if ls is None: + return + max_age = float(get_env_float("LS_WS_ORDERBOOK_TICK_MAX_AGE_SEC", 3.0) or 3.0) + getter = getattr(ls, "get_orderbook_snapshot", None) + if not callable(getter): + return + snap = getter(code, max_age_sec=max_age) + if snap is None: + return + if hasattr(snap, "to_storage_dict"): + body = snap.to_storage_dict() + elif isinstance(snap, dict): + body = dict(snap) + else: + return + # 틱 시각과 snap_time 정렬 (백테 pick ≤ 진입시각) + snap_time = "" + ts = payload.get("ts") + if ts is not None: + try: + if hasattr(ts, "strftime"): + snap_time = ts.strftime("%Y%m%d%H%M%S") + else: + s = str(ts).strip() + digits = ( + s.replace("-", "") + .replace(":", "") + .replace(" ", "") + .replace("T", "") + .replace(".", "") + ) + if len(digits) >= 14: + snap_time = digits[:14] + except Exception: + snap_time = "" + if len(snap_time) < 14: + che = str(payload.get("chetime") or "").strip() + che_d = "".join(ch for ch in che if ch.isdigit()) + if len(che_d) >= 6: + from datetime import datetime as _dt + day = _dt.now().strftime("%Y%m%d") + snap_time = day + che_d[-6:].ljust(6, "0")[:6] + if len(snap_time) >= 14: + body["snap_time"] = snap_time[:14] + body["source"] = str(body.get("source") or "ls_uh1")[:16] + self.db.insert_ls_ws_orderbook(code=code, snap=body, market="KR") + # 가끔 오래된 행 정리 (틱 폭주 대비) + try: + keep = int(get_env_int("LS_WS_ORDERBOOK_KEEP_DAYS", 7) or 7) + if keep > 0 and hasattr(self.db, "cleanup_old_ls_ws_orderbook"): + # 매 틱마다 DELETE 금지 — 몬otonic 스로틀 + now_m = time.monotonic() + last = float(getattr(self, "_ls_ob_cleanup_mono", 0.0) or 0.0) + if now_m - last >= 3600.0: + self._ls_ob_cleanup_mono = now_m + self.db.cleanup_old_ls_ws_orderbook(keep_days=keep) + except Exception: + pass + def _start_kiwoom_condition_manager(self) -> None: """키움 조건검색 — 시세 WS(KiwoomWebSocketPriceCache) 와 단일 세션 공유. @@ -1273,9 +1371,283 @@ class TradingOrchestrator: ) if not self.kiwoom_condition_mgr.start(): self.kiwoom_condition_mgr = None + try: + from .utils.ops_alert import ops_alert + ops_alert( + "kwcond_start_fail", + "키움 조건검색 매니저 기동 실패", + detail="kiwoom_condition 전략은 DB폴백·유니버스 위험", + level="critical", + session_only=False, + ) + except Exception: + pass except Exception as e: logger.error("키움 조건검색 매니저 기동 실패: %s", e) self.kiwoom_condition_mgr = None + try: + from .utils.ops_alert import ops_alert + ops_alert( + "kwcond_start_fail", + "키움 조건검색 매니저 기동 예외", + detail=str(e), + level="critical", + session_only=False, + ) + except Exception: + pass + + def _attach_kiwoom_condition_mgr_to_strategies(self) -> None: + """기동 실패→복구 시 전략이 들고 있던 None 핸들을 갱신.""" + mgr = self.kiwoom_condition_mgr + for s in self.strategies: + try: + s.kiwoom_condition_mgr = mgr + except Exception: + pass + + def _maybe_recover_kiwoom_condition_manager(self) -> None: + """시세 WS LOGIN 후 토큰이 살아나면 조건검색을 재기동. + + 주말/야간 재시작 때 키움 토큰 JSON 파싱 실패로 매니저가 죽은 채 + 시세만 나중에 붙으면 MOMENTUM/SHORT 가 DB sticky + 낡은 history + 교집합으로 유니버스 0이 된다. heartbeat 에서 복구한다. + """ + if self._stop: + return + if self.kiwoom_condition_mgr is not None: + return + if not (self._pending_kiwoom_condition_configs or []): + return + shared = self.kiwoom_ws + if shared is None: + return + if not getattr(shared, "is_authenticated", lambda: False)(): + return + now_m = time.time() + last = float(getattr(self, "_kwcond_recover_mono", 0.0) or 0.0) + interval = float(get_env_int("KIWOOM_COND_RECOVER_INTERVAL_SEC", 60)) + if now_m - last < max(15.0, interval): + return + self._kwcond_recover_mono = now_m + logger.info( + "🔄 키움 조건검색 매니저 복구 시도 " + "(시세 WS LOGIN OK · 이전 기동 실패 후 재시도)" + ) + try: + self._start_kiwoom_condition_manager() + except Exception as e: + logger.warning("키움 조건검색 복구 예외: %s", e) + return + if self.kiwoom_condition_mgr is None: + return + self._attach_kiwoom_condition_mgr_to_strategies() + logger.info( + "✅ 키움 조건검색 매니저 복구 완료 → 전략 핸들 갱신 (%s)", + self._mgr_heartbeat_desc(self.kiwoom_condition_mgr), + ) + + def _ops_health_tick(self) -> None: + """장중 운영건강 — WS/유니버스/history/kwcond. 쿨다운은 ops_alert 내부.""" + try: + from .utils.ops_alert import ops_alert, _in_kr_session + except Exception: + return + if not _in_kr_session(): + return + + # 1) 시세 WS + try: + if self.ws is not None and not getattr(self.ws, "is_active", False): + ops_alert( + "ws_kis_down", + "KIS 시세 WS 비활성(IDLE)", + detail="장중 is_active=False — REST fallback 가능하나 지연·누락 위험", + level="critical", + ) + except Exception: + pass + try: + kw = self.kiwoom_ws + if kw is not None and not getattr(kw, "is_authenticated", lambda: False)(): + ops_alert( + "ws_kiwoom_down", + "키움 시세 WS LOGIN 안 됨", + detail="장중 미인증 — 조건검색·시세 위험", + level="critical", + ) + except Exception: + pass + try: + ls = getattr(self, "ls_ws", None) + if ls is not None: + opened = getattr(ls, "_opened", None) + down = ( + opened is not None + and hasattr(opened, "is_set") + and not opened.is_set() + ) + hold_need = max(15, get_env_int("OPS_ALERT_WS_DOWN_HOLD_SEC", 60)) + now_ls = time.time() + if down: + t0 = float(getattr(self, "_ls_ws_down_since", 0.0) or 0.0) + if t0 <= 0: + self._ls_ws_down_since = now_ls + elif (now_ls - t0) >= hold_need: + ops_alert( + "ws_ls_down", + "LS 시세 WS 미OPEN", + detail=f"장중 LS 소켓 미연결 {int(now_ls - t0)}초+", + level="critical", + ) + # 쿨다운과 별도로 타이머 리셋(연속 스팸 방지) + self._ls_ws_down_since = now_ls + else: + self._ls_ws_down_since = 0.0 + except Exception: + pass + + # 2) kiwoom_condition 필요한데 매니저 없음 + need_kw = False + for s in self.strategies: + sid = str(getattr(s, "strategy_id", "") or "").upper() + if not sid or sid.startswith("US_"): + continue + if self._resolve_source(sid) == "kiwoom_condition": + need_kw = True + break + if need_kw and self.kiwoom_condition_mgr is None: + ops_alert( + "kwcond_off", + "키움 조건검색 OFF (필요 전략 있음)", + detail="MOMENTUM/SHORT 등 kiwoom_condition → DB폴백·매매0 위험", + level="critical", + ) + + # 3) 전략별 유니버스 0 지속 + zero_need = max(60, get_env_int("OPS_ALERT_UNIVERSE_ZERO_SEC", 180)) + now_m = time.time() + zero_since = getattr(self, "_univ_zero_since", None) + if not isinstance(zero_since, dict): + zero_since = {} + self._univ_zero_since = zero_since + for s in self.strategies: + sid = str(getattr(s, "strategy_id", "") or "").upper() + if not sid or sid.startswith("US_"): + continue + try: + n = 0 + src = self._resolve_source(sid) + if src == "kiwoom_condition" and self.kiwoom_condition_mgr: + n = len(self.kiwoom_condition_mgr.get_universe_for(sid) or []) + elif src == "ls_condition" and self.ls_condition_mgr: + n = len(self.ls_condition_mgr.get_universe_for(sid) or []) + elif src == "condition" and self.condition_mgr: + n = len(self.condition_mgr.get_universe_for(sid) or []) + elif src == "ranking" and self.ranking_mgr: + n = len(self.ranking_mgr.get_universe_for(sid) or []) + else: + continue + if n <= 0: + t0 = float(zero_since.get(sid) or 0.0) + if t0 <= 0: + zero_since[sid] = now_m + elif (now_m - t0) >= zero_need: + # 실매 소스 0 인데 다른 브로커에는 있으면 원인 힌트 + kw_n = -1 + ls_n = -1 + try: + if self.kiwoom_condition_mgr: + kw_n = len( + self.kiwoom_condition_mgr.get_universe_for(sid) + or [] + ) + except Exception: + pass + try: + if self.ls_condition_mgr: + ls_n = len( + self.ls_condition_mgr.get_universe_for(sid) + or [] + ) + except Exception: + pass + hint = "" + if src == "ls_condition" and kw_n > 0 and ls_n <= 0: + hint = ( + f" | 키움={kw_n} LS=0 → t1859공백/AFR미적재 " + f"(LS_T1859_EMPTY_RETRY 확인)" + ) + elif src == "kiwoom_condition" and ls_n > 0 and kw_n <= 0: + hint = f" | LS={ls_n} 키움=0" + ops_alert( + "universe_zero", + f"{sid} 유니버스 0 지속 {int(now_m - t0)}초", + detail=f"source={src} kw={kw_n} ls={ls_n}{hint}", + level="critical", + ) + zero_since[sid] = now_m # 쿨다운과 별도로 타이머 리셋 + else: + zero_since.pop(sid, None) + except Exception: + continue + + # 4) 키움 history 당일 공백 (kiwoom_condition 전략) + if need_kw and self.db is not None: + stale_need = max(120, get_env_int("OPS_ALERT_HISTORY_STALE_SEC", 600)) + open_grace = max(0, get_env_int("OPS_ALERT_HISTORY_OPEN_GRACE_SEC", 180)) + try: + # 개장 직후 N초는 history 첫 적재 레이스 — CRITICAL 스킵 + skip_open_race = False + if open_grace > 0: + start_hm = int(get_env_int("OPS_ALERT_SESSION_START_HM", 900) or 900) + now_dt = dt.now() + open_dt = now_dt.replace( + hour=start_hm // 100, + minute=start_hm % 100, + second=0, + microsecond=0, + ) + if 0 <= (now_dt - open_dt).total_seconds() < float(open_grace): + skip_open_race = True + if not skip_open_race: + today = dt.now().strftime("%Y-%m-%d") + row = self.db.conn.execute( + """ + SELECT MAX(event_time) AS et + FROM target_candidates_history + WHERE strategy_id IN ('MOMENTUM','SHORT') + AND event_time >= %s + """, + (today + " 00:00:00",), + ).fetchone() + et = (row or {}).get("et") if row else None + if not et: + ops_alert( + "history_stale", + "키움 target_candidates_history 당일 0건", + detail="조건검색 history 미적재 — 슬롯정합/백테 위험", + level="critical", + ) + else: + if hasattr(et, "timestamp"): + age = time.time() - float(et.timestamp()) + else: + try: + age = time.time() - dt.strptime( + str(et)[:19], "%Y-%m-%d %H:%M:%S" + ).timestamp() + except Exception: + age = 0.0 + if age >= stale_need: + ops_alert( + "history_stale", + f"키움 history 공백 {int(age)}초", + detail=f"last_event={et}", + level="critical", + ) + except Exception as e: + logger.debug("ops history check: %s", e) def _start_ls_condition_manager(self) -> None: """LS 서버저장조건(AFR) — 시세 get_price 와 무관. @@ -1429,13 +1801,7 @@ class TradingOrchestrator: def _wire_ws_split_feed_if_needed(self) -> None: """``WS_SUBSCRIBE_KIS_MINIMAL`` : 후보 틱→CandleAggregator + KIS/키움 분리 구독.""" - if not get_env_bool("WS_SUBSCRIBE_KIS_MINIMAL", False): - return if not self.kiwoom_ws: - logger.warning( - "WS_SUBSCRIBE_KIS_MINIMAL=true 이지만 키움 WS 미기동 — " - "KIWOOM_APP_KEY_REAL 등 확인 (WS_PROVIDER=kis_with_validation 병행 권장)", - ) return ca = getattr(self.ws, "candle_agg", None) if not ca: @@ -1443,13 +1809,17 @@ class TradingOrchestrator: try: self.kiwoom_ws.attach_candle_aggregator(ca) tr = getattr(self.ws, "tick_recorder", None) - if tr is not None: + if tr is not None and get_env_bool("WS_TICK_SAVE_KIWOOM", True): self.kiwoom_ws.attach_tick_recorder(tr) tsr = getattr(self.ws, "trigger_snapshot_recorder", None) - if tsr is not None: + if tsr is not None and get_env_bool("WS_ORDERBOOK_SAVE_KIWOOM", True): self.kiwoom_ws.attach_trigger_snapshot_recorder(tsr) + + # 무조건 WSManager에 키움 WS 인스턴스를 주입 (폴백/중복구독용) self.ws.set_kiwoom_ws(self.kiwoom_ws) - self.ws.activate_split_feed(True) + + if get_env_bool("WS_SUBSCRIBE_KIS_MINIMAL", False): + self.ws.activate_split_feed(True) except Exception as e: logger.warning("WS 분리 시세 연결 실패: %s", e) @@ -1513,22 +1883,38 @@ class TradingOrchestrator: last_hb = 0.0 last_daily_tick = 0.0 last_pending_fill = 0.0 + last_kwcond_recover = 0.0 pending_interval = float(get_env_int("PENDING_FILL_POLL_INTERVAL_SEC", 10)) while not self._stop: now = time.time() + # 키움 조건검색: 토큰/LOGIN 지연 시 60초마다 복구 시도 + if now - last_kwcond_recover >= 15: + try: + self._maybe_recover_kiwoom_condition_manager() + except Exception as e: + logger.debug("kwcond recover tick 예외: %s", e) + last_kwcond_recover = now if now - last_hb >= 60: alive = [s.name for s in self.strategies if s.is_alive()] dead = [s.name for s in self.strategies if not s.is_alive()] ws_session = "OPEN" if self.ws.is_active else "IDLE" rank_desc = self._mgr_heartbeat_desc(self.ranking_mgr) cond_desc = self._mgr_heartbeat_desc(self.condition_mgr) + kw_desc = self._mgr_heartbeat_desc(self.kiwoom_condition_mgr) + ls_desc = self._mgr_heartbeat_desc(self.ls_condition_mgr) logger.info( - "❤️ heartbeat ws=%s rank[%s] cond[%s] alive=%s dead=%s", - ws_session, rank_desc, cond_desc, alive, dead, + "❤️ heartbeat ws=%s rank[%s] cond[%s] kwcond[%s] lscond[%s] " + "alive=%s dead=%s", + ws_session, rank_desc, cond_desc, kw_desc, ls_desc, + alive, dead, ) if dead: logger.warning("⚠️ 죽은 전략 쓰레드 감지: %s → 재기동", dead) self._restart_dead() + try: + self._ops_health_tick() + except Exception as e: + logger.debug("ops_health_tick 예외: %s", e) last_hb = now if now - last_pending_fill >= pending_interval: try: @@ -1924,6 +2310,17 @@ class TradingOrchestrator: e, self._start_asset_backoff_sec, ) + # 08:35~09:15 개장 전/초기 계좌 평단가·수량 동기화 (하루 1회 — 권리락/액면분할 방어) + if (self._morning_sync_date != today) and ((h == 8 and m >= 35) or (h == 9 and m <= 15)): + try: + from .execution.orphan_reconcile import sync_active_trades_with_broker + res = sync_active_trades_with_broker(self.order_mgr) + self._morning_sync_date = today + if res.get("synced"): + logger.info("🌅 [개장 전/초기 계좌 동기화 완료] %d건 변경 감지 및 보정", len(res["synced"])) + except Exception as e: + logger.error("개장 전/초기 계좌 동기화 실패: %s", e) + # 09:00 장 시작 알림 (09:00~09:10 윈도우, 하루 1회) # 봇 기동 시점과 무관하게 매일 장 시작에 1회 발송 (아침 알람 용도). if (h == 9 and 0 <= m <= 10 and self._market_open_report_date != today): @@ -1984,7 +2381,8 @@ class TradingOrchestrator: g = int(result.get("ghost_purged_count") or 0) stale = int(result.get("stale_purged_count") or 0) skip_sell = int(result.get("skipped_after_sell_count") or 0) - if n <= 0 and g <= 0 and stale <= 0 and skip_sell <= 0 and not result.get("error"): + s = int(result.get("synced_count") or 0) + if n <= 0 and g <= 0 and stale <= 0 and skip_sell <= 0 and s <= 0 and not result.get("error"): label = "Pre-EOD" if phase == "pre" else "장마감" logger.debug("🧩 [%s 고아복구] 복구·유령삭제 대상 없음", label) return @@ -1995,6 +2393,7 @@ class TradingOrchestrator: lines = [ f"🧩 **[{title}]**", f"- 복구: {n}종목", + f"- 계좌동기화: {s}종목", f"- 청산후스킵: {skip_sell}종목", f"- 청산후유령정리: {stale}종목", f"- 유령삭제: {g}종목", @@ -2016,6 +2415,10 @@ class TradingOrchestrator: lines.append( f" ✕청산후유령 {it.get('name')}({it.get('code')}) [{it.get('strategy')}]" ) + for it in (result.get("synced") or [])[:5]: + lines.append( + f" 🔄동기화 {it.get('name')}({it.get('code')}) [{it.get('strategy')}] {it.get('old_qty')}➔{it.get('new_qty')}주 ({it.get('old_avg'):,.0f}➔{it.get('new_avg'):,.0f}원)" + ) for it in (result.get("ghost_purged") or [])[:5]: lines.append( f" ✕유령 {it.get('name')}({it.get('code')}) [{it.get('strategy')}]" @@ -2027,8 +2430,8 @@ class TradingOrchestrator: except Exception as e: logger.debug("고아복구 MM 전송 실패: %s", e) logger.info( - "🧩 [고아복구] 복구 %d · 청산후스킵 %d · 청산후유령정리 %d · 유령삭제 %d → active_trades 반영", - n, skip_sell, stale, g, + "🧩 [고아복구] 복구 %d · 계좌동기화 %d · 청산후스킵 %d · 청산후유령정리 %d · 유령삭제 %d → active_trades 반영", + n, s, skip_sell, stale, g, ) def _build_market_open_report(self, today_ymd: str) -> str: @@ -2199,6 +2602,9 @@ class TradingOrchestrator: order_mgr=self.order_mgr, condition_mgr=self.condition_mgr, ranking_mgr=self.ranking_mgr, + kiwoom_condition_mgr=self.kiwoom_condition_mgr, + ls_condition_mgr=self.ls_condition_mgr, + market_guard=self.market_guard, ) if getattr(ns, "strategy_id", "") == "US_MOMENTUM" and self.overseas_ws: ns.overseas_ws = self.overseas_ws diff --git a/kis_trader/network/condition_common.py b/kis_trader/network/condition_common.py new file mode 100644 index 0000000..33b0789 --- /dev/null +++ b/kis_trader/network/condition_common.py @@ -0,0 +1,50 @@ +"""조건검색 공통 — 브로커 무관 필수만. + +키움 REAL / LS AFR 시드·프로토콜은 여기 넣지 않는다 (분기 유발). +멤버십 반영은 ``ConditionSearchManager._apply_result`` 가 본체. +""" +from __future__ import annotations + +from typing import Dict, Iterable, List, Optional + +# LS: N/R/O · 키움: I/D (키움은 재진입 플래그 없음 → I 재수신) +JOB_KO = { + "N": "진입", + "R": "재진입", + "O": "이탈", + "I": "진입", + "D": "이탈", +} + + +def normalize_job_flag(flag: Optional[str]) -> str: + return str(flag or "").strip().upper() + + +def job_kind(flag: Optional[str]) -> Optional[str]: + """enter | reenter | exit | None.""" + j = normalize_job_flag(flag) + if j in ("N", "I"): + return "enter" + if j == "R": + return "reenter" + if j in ("O", "D"): + return "exit" + return None + + +def rows_for_codes( + codes: Iterable[str], + names: Optional[Dict[str, str]] = None, +) -> List[Dict[str, str]]: + """``_apply_result`` 입력 형태 (code/name).""" + nm = names or {} + out: List[Dict[str, str]] = [] + seen = set() + for c in codes: + code = str(c or "").strip() + if not code or code in seen: + continue + seen.add(code) + out.append({"code": code, "name": str(nm.get(code) or code)}) + return out diff --git a/kis_trader/network/condition_job_events.py b/kis_trader/network/condition_job_events.py new file mode 100644 index 0000000..fc909ef --- /dev/null +++ b/kis_trader/network/condition_job_events.py @@ -0,0 +1,350 @@ +"""조건검색 진입/재진입/이탈 이벤트 → DB. + +스냅샷 테이블(target/ls_candidates_history)은 **종목 목록**만 두고, +job(N/R/O·I/D) 은 이 테이블에 1이벤트 1행으로 적재한다. +""" +from __future__ import annotations + +from datetime import datetime +from typing import Any, Dict, List, Optional + +from ..utils.env import get_env_bool +from ..utils.logger import get_logger +from .condition_common import JOB_KO, normalize_job_flag + +logger = get_logger("kis_trader.cond_job") + +CONDITION_JOB_EVENTS_DDL = """ +CREATE TABLE IF NOT EXISTS condition_job_events ( + id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + event_time VARCHAR(30) NOT NULL, + strategy_id VARCHAR(32) NOT NULL DEFAULT '', + code VARCHAR(20) NOT NULL, + name VARCHAR(100) NOT NULL DEFAULT '', + job_flag VARCHAR(8) NOT NULL, + job_ko VARCHAR(16) NOT NULL DEFAULT '', + broker VARCHAR(16) NOT NULL, + source VARCHAR(32) NOT NULL DEFAULT '', + price DOUBLE NOT NULL DEFAULT 0, + broker_time VARCHAR(32) NOT NULL DEFAULT '', + query_name VARCHAR(64) NOT NULL DEFAULT '', + INDEX idx_cje_sid_et (strategy_id, event_time), + INDEX idx_cje_code_et (code, event_time), + INDEX idx_cje_et (event_time), + INDEX idx_cje_broker_job (broker, job_flag) +) CHARACTER SET utf8mb4 +""" + +# JOB_KO 는 condition_common 단일 소스 (여기서 재정의 금지) + + +def ensure_condition_job_events_table(db) -> bool: + if db is None: + return False + try: + db.conn.execute(CONDITION_JOB_EVENTS_DDL) + return True + except Exception as e: + logger.debug("condition_job_events DDL: %s", e) + return False + + +def insert_condition_job_event( + db, + *, + strategy_id: str, + code: str, + job_flag: str, + broker: str, + name: str = "", + price: float = 0.0, + broker_time: str = "", + query_name: str = "", + source: str = "", + event_time: Optional[str] = None, +) -> bool: + """1건 INSERT. CONDITION_JOB_EVENTS_SAVE=false 면 스킵.""" + if db is None: + return False + if not get_env_bool("CONDITION_JOB_EVENTS_SAVE", True): + return False + c = str(code or "").strip() + j = normalize_job_flag(job_flag) + if not c or not j: + return False + if j not in JOB_KO: + return False + sid = str(strategy_id or "").strip().upper() + br = str(broker or "").strip().lower() or "unknown" + et = event_time or datetime.now().strftime("%Y-%m-%d %H:%M:%S") + try: + px = float(price or 0) + except (TypeError, ValueError): + px = 0.0 + ensure_condition_job_events_table(db) + try: + with db.conn: + db.conn.execute( + """ + INSERT INTO condition_job_events + (event_time, strategy_id, code, name, job_flag, job_ko, + broker, source, price, broker_time, query_name) + VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) + """, + ( + et, + sid, + c, + str(name or c)[:100], + j, + JOB_KO.get(j, j), + br[:16], + str(source or "")[:32], + px, + str(broker_time or "")[:32], + str(query_name or "")[:64], + ), + ) + return True + except Exception as e: + logger.debug("condition_job_events INSERT 실패: %s", e) + return False + + +def query_condition_job_events( + db, + *, + start: str, + end: str, + strategy_id: str = "", + broker: str = "", + job_flag: str = "", + code: str = "", + limit: int = 500, +) -> List[Dict[str, Any]]: + """웹/분석용 SELECT. start/end 는 YYYY-MM-DD 또는 datetime 문자열.""" + if db is None: + return [] + ensure_condition_job_events_table(db) + start_s = str(start or "").strip() + end_s = str(end or "").strip() + if len(start_s) == 10: + start_s = start_s + " 00:00:00" + if len(end_s) == 10: + end_s = end_s + " 23:59:59" + lim = max(1, min(int(limit or 500), 5000)) + where = ["event_time >= %s", "event_time <= %s"] + params: List[Any] = [start_s, end_s] + sid = str(strategy_id or "").strip().upper() + if sid and sid != "ALL": + where.append("strategy_id = %s") + params.append(sid) + br = str(broker or "").strip().lower() + if br and br != "all": + where.append("broker = %s") + params.append(br) + jf = str(job_flag or "").strip().upper() + if jf and jf != "ALL": + where.append("job_flag = %s") + params.append(jf) + cd = str(code or "").strip() + if cd: + where.append("code LIKE %s") + params.append(cd + "%") + params.append(lim) + sql = ( + "SELECT id, event_time, strategy_id, code, name, job_flag, job_ko, " + "broker, source, price, broker_time, query_name " + "FROM condition_job_events WHERE " + + " AND ".join(where) + + " ORDER BY event_time DESC, id DESC LIMIT %s" + ) + try: + rows = db.conn.execute(sql, tuple(params)).fetchall() + except Exception as e: + logger.warning("condition_job_events SELECT: %s", e) + return [] + out: List[Dict[str, Any]] = [] + for r in rows or []: + if hasattr(r, "keys"): + out.append(dict(r)) + else: + out.append({ + "id": r[0], "event_time": r[1], "strategy_id": r[2], + "code": r[3], "name": r[4], "job_flag": r[5], "job_ko": r[6], + "broker": r[7], "source": r[8], "price": r[9], + "broker_time": r[10], "query_name": r[11], + }) + return out + + +def build_condition_parity_report(db, *, day: str) -> Dict[str, Any]: + """전략별 live소스·history·job·ALIGN 정합 요약 (웹 탭용). + + 봇 RAM 은 웹에서 직접 못 읽으므로 **당일 history 최신 스냅샷** + job 집계로 + 구멍(LS history 공백·키움만 살아있음)을 가독성 있게 표시한다. + """ + from kis_trader.utils.env import get_env_bool, get_env_from_db + + day_s = str(day or "").strip()[:10] + if len(day_s) != 10: + day_s = datetime.now().strftime("%Y-%m-%d") + start_s = day_s + " 00:00:00" + end_s = day_s + " 23:59:59" + align_on = get_env_bool("LIVE_UNIVERSE_SLOT_ALIGN", False) + sids = ("SCALP", "SHORT", "BREAKOUT", "MOMENTUM") + ensure_condition_job_events_table(db) + + def _latest_hist(table: str, sid: str) -> Dict[str, Any]: + try: + row = db.conn.execute( + f""" + SELECT event_time AS et, COUNT(*) AS n + FROM {table} + WHERE strategy_id=%s AND event_time >= %s AND event_time <= %s + GROUP BY event_time + ORDER BY event_time DESC + LIMIT 1 + """, + (sid, start_s, end_s), + ).fetchone() + except Exception: + return {"event_time": None, "n": 0, "age_sec": None} + if not row: + return {"event_time": None, "n": 0, "age_sec": None} + et = row["et"] if hasattr(row, "keys") else row[0] + n = int(row["n"] if hasattr(row, "keys") else row[1] or 0) + age = None + try: + et_dt = datetime.strptime(str(et)[:19], "%Y-%m-%d %H:%M:%S") + age = max(0, int((datetime.now() - et_dt).total_seconds())) + except Exception: + age = None + return {"event_time": str(et) if et else None, "n": n, "age_sec": age} + + def _job_counts(sid: str) -> Dict[str, int]: + outc: Dict[str, int] = { + "ls_N": 0, "ls_R": 0, "ls_O": 0, + "kw_I": 0, "kw_D": 0, "total": 0, + } + try: + rows = db.conn.execute( + """ + SELECT broker, job_flag, COUNT(*) AS n + FROM condition_job_events + WHERE strategy_id=%s AND event_time >= %s AND event_time <= %s + GROUP BY broker, job_flag + """, + (sid, start_s, end_s), + ).fetchall() + except Exception: + return outc + for r in rows or []: + br = str(r["broker"] if hasattr(r, "keys") else r[0] or "").lower() + jf = str(r["job_flag"] if hasattr(r, "keys") else r[1] or "").upper() + n = int(r["n"] if hasattr(r, "keys") else r[2] or 0) + outc["total"] += n + key = None + if br == "ls" and jf in ("N", "R", "O"): + key = f"ls_{jf}" + elif br == "kiwoom" and jf in ("I", "D"): + key = f"kw_{jf}" + if key: + outc[key] = n + return outc + + timeline: List[Dict[str, Any]] = [] + try: + trows = db.conn.execute( + """ + SELECT LEFT(event_time, 16) AS bucket, + job_flag, COUNT(*) AS n + FROM condition_job_events + WHERE event_time >= %s AND event_time <= %s + GROUP BY LEFT(event_time, 16), job_flag + ORDER BY bucket + """, + (start_s, end_s), + ).fetchall() + agg: Dict[str, Dict[str, int]] = {} + for r in trows or []: + raw = str(r["bucket"] if hasattr(r, "keys") else r[0] or "") + # 'YYYY-MM-DD HH:MM' → 10분 버킷 + try: + base = raw[:14] # 'YYYY-MM-DD HH:' + minute = int(raw[14:16]) + bkey = f"{base}{(minute // 10) * 10:02d}" + except Exception: + bkey = raw + jf = str(r["job_flag"] if hasattr(r, "keys") else r[1] or "").upper() + n = int(r["n"] if hasattr(r, "keys") else r[2] or 0) + slot = agg.setdefault(bkey, {"enter": 0, "exit": 0, "reenter": 0}) + if jf in ("N", "I"): + slot["enter"] += n + elif jf == "R": + slot["reenter"] += n + elif jf in ("O", "D"): + slot["exit"] += n + timeline = [{"t": k, **v} for k, v in sorted(agg.items())] + except Exception as e: + logger.debug("parity timeline: %s", e) + + strategies: List[Dict[str, Any]] = [] + for sid in sids: + live_src = str( + get_env_from_db(f"{sid}_UNIVERSE_SOURCE", "") or "" + ).strip().lower() or "—" + ls = _latest_hist("ls_candidates_history", sid) + kw = _latest_hist("target_candidates_history", sid) + jobs = _job_counts(sid) + status = "ok" + warn = "" + if live_src in ("ls_condition", "ls", "ls_afr"): + if (ls.get("n") or 0) <= 0: + status = "danger" + warn = "실매=LS 인데 당일 LS history 최신 스냅 0/없음 → 유니버스0·ALIGN 위험" + if (kw.get("n") or 0) > 0: + warn += f" (키움 history는 {kw['n']}종 있음 — t1859공백/AFR미적재 의심)" + elif ls.get("age_sec") is not None and ls["age_sec"] > 600: + status = "warn" + warn = f"LS history {ls['age_sec']}초 전 스냅 — 장중 공백 가능" + elif live_src in ("kiwoom_condition", "kiwoom"): + if (kw.get("n") or 0) <= 0: + status = "danger" + warn = "실매=키움 인데 당일 키움 history 없음" + elif kw.get("age_sec") is not None and kw["age_sec"] > 600: + status = "warn" + warn = f"키움 history {kw['age_sec']}초 전" + + strategies.append({ + "strategy_id": sid, + "live_source": live_src, + "align_on": align_on, + "ls_hist_n": ls.get("n") or 0, + "ls_hist_at": ls.get("event_time"), + "ls_age_sec": ls.get("age_sec"), + "kw_hist_n": kw.get("n") or 0, + "kw_hist_at": kw.get("event_time"), + "kw_age_sec": kw.get("age_sec"), + "jobs": jobs, + "status": status, + "warn": warn, + }) + + try: + grace_sec = int(str(get_env_from_db("CONDITION_EXIT_GRACE_SEC", "0") or "0")) + except (TypeError, ValueError): + grace_sec = 0 + + return { + "day": day_s, + "align_on": align_on, + "grace_sec": grace_sec, + "strategies": strategies, + "timeline": timeline, + "notes": ( + "history=봇이 저장한 effective 스냅. 실매 소스가 LS인데 LS history 0 이면 " + "universe_zero 알림이 정상(키움 이중이력과 무관). " + "ALIGN=ON 이면 live∩history — history 구멍 시 교집합 위험." + ), + } diff --git a/kis_trader/network/condition_manager.py b/kis_trader/network/condition_manager.py index 29c5bff..c657ee8 100644 --- a/kis_trader/network/condition_manager.py +++ b/kis_trader/network/condition_manager.py @@ -86,12 +86,11 @@ class ConditionSearchManager: "CONDITION_HISTORY_SAVE", get_env_bool("UNIVERSE_HISTORY_SAVE", True), ) - # EXIT grace period: 한 번 빠진 종목을 N초간 universe 에 keep. - # 단발성 EXIT/RE-ENTER 회전을 흡수해 WS 구독 해제 → 캐시·갭보정 리셋 - # → resubscribe 후 데이터 부족으로 매수 시그널 못내는 사이클을 차단. - # 기본 0 = 비활성 (기존 동작과 동일). 권장 60. + # EXIT grace: 빠진 종목을 N초간 universe 에 keep (0=즉시 pop = 순차 push/pop). + # 정합(실매=history)을 깨지 않으려면 history 도 effective(이 목록)을 저장해야 함. + # 기본 0. 켜도 history=effective 이면 슬롯정합은 유지됨(실매만 sticky). self._exit_grace_sec = float(get_env_int("CONDITION_EXIT_GRACE_SEC", 0)) - # strategy_id → {code: first_missing_at_epoch} + # strategy_id → {code: first_missing_at_epoch} — grace>0 일 때만 사용 self._pending_exit: Dict[str, Dict[str, float]] = {} # 설정 정규화: legacy(single) → multi 형식으로 흡수 @@ -114,9 +113,9 @@ class ConditionSearchManager: self._thread: Optional[threading.Thread] = None self._running = False - # 상태 + # 상태 — 순서 리스트가 본체(순차 ENTER append / EXIT remove), Set 은 조회 캐시 self._current: Dict[str, Set[str]] = {} # strategy_id → code set - self._current_order: Dict[str, List[str]] = {} # strategy_id → 매수 우선순위(HTS 응답 순) + self._current_order: Dict[str, List[str]] = {} # strategy_id → push/pop 순차 목록 self._name_map: Dict[str, str] = {} # code → name (전역) # 초기 tick 에서 "빈 set → 첫 결과" 를 변동으로 간주해 1회는 저장 self._initialized: Set[str] = set() @@ -294,41 +293,39 @@ class ConditionSearchManager: def _apply_result(self, strategy_id: str, rows: List[Dict]) -> None: """ - 조건검색 raw 결과를 universe 에 반영. + 조건검색 raw → 유니버스 순차 반영 (ENTER append / EXIT remove). - EXIT grace 정책 (``CONDITION_EXIT_GRACE_SEC`` > 0 일 때): - - raw 결과에서 빠진 종목을 즉시 EXIT 처리하지 않고 ``_pending_exit`` 에 - ``first_missing_at`` 시각과 함께 등록. - - grace 초가 지나야 진짜 EXIT (universe 에서 제거) → WS 구독 해제 → 갭보정 리셋. - - grace 중 다시 raw 에 등장하면 pending 에서 빼고 universe 에 그대로 keep - (구독·캐시·봉 데이터 보존). - - 효과: 단발성 EXIT/RE-ENTER 폭주 흡수. 시장 노이즈로 1~2 tick 빠지는 케이스를 - 걸러 매수 시그널 평가용 RAM 데이터 (회복률·낙폭 등) 가 휘발 안 됨. + 기본(``CONDITION_EXIT_GRACE_SEC=0``): raw 변동을 즉시 push/pop. + grace>0: EXIT 를 N초 지연(sticky). history 는 반드시 effective 목록을 + 저장해야 슬롯정합이 안 깨짐 — 부모 ``_save_snapshot`` / LS override 경로 동일. """ - raw_set = {r["code"] for r in rows if r.get("code")} - new_names = { - r["code"]: r.get("name", r["code"]) for r in rows if r.get("code") - } + raw_order: List[str] = [] + raw_seen: Set[str] = set() + new_names: Dict[str, str] = {} + for r in rows or []: + c = str(r.get("code") or "").strip() + if not c or c in raw_seen: + continue + raw_seen.add(c) + raw_order.append(c) + new_names[c] = str(r.get("name") or c) + raw_set = set(raw_seen) now = time.time() grace = self._exit_grace_sec with self._lock: - prev = self._current.get(strategy_id, set()) + prev_order = list(self._current_order.get(strategy_id) or []) + prev = set(prev_order) if prev_order else set(self._current.get(strategy_id) or []) first_tick = strategy_id not in self._initialized pending = self._pending_exit.setdefault(strategy_id, {}) - # ── grace 적용 ──────────────────────────────────────── kept_in_grace: Set[str] = set() if grace > 0: - # 1) raw 에 다시 등장 → grace 해제 (회생) for c in raw_set & set(pending.keys()): pending.pop(c, None) - # 2) raw 에서 빠진 prev 종목 → pending 등록 (처음 사라진 시각) for c in prev - raw_set: if c not in pending: pending[c] = now - # 3) grace 미경과 종목 → universe 에 keep / 경과 → pending 제거 expired: Set[str] = set() for c, first_at in list(pending.items()): if now - first_at >= grace: @@ -336,20 +333,26 @@ class ConditionSearchManager: for c in expired: pending.pop(c, None) kept_in_grace = set(pending.keys()) - # grace=0 (기존 동작) 이면 kept_in_grace = ∅, pending 안 쓰임. + else: + pending.clear() - # 봇이 실제로 보는 effective universe (raw + grace keep) new_set = raw_set | kept_in_grace - - # ── 변동 계산 (effective 기준) ─────────────────────── + # 순차 목록: raw 응답 순서 유지 + grace 잔여(이전 순서) + 나머지 + ordered = self._build_ordered_universe( + rows=[{"code": c, "name": new_names.get(c, c)} for c in raw_order], + new_set=new_set, + prev_order=prev_order, + ) enters = new_set - prev exits = prev - new_set - self._current[strategy_id] = new_set - self._current_order[strategy_id] = self._build_ordered_universe( - rows=rows, - new_set=new_set, - prev_order=list(self._current_order.get(strategy_id) or []), - ) + # 신규 ENTER 를 맨 앞 — {SID}_CAND_LIMIT 하드캡이 오래된 저코드만 + # 남기고 AFR/스냅 신규를 잘라먹던 sticky 를 막는다. + if enters: + head = [c for c in ordered if c in enters] + tail = [c for c in ordered if c not in enters] + ordered = head + tail + self._current_order[strategy_id] = ordered + self._current[strategy_id] = set(ordered) self._initialized.add(strategy_id) for c, n in new_names.items(): self._name_map[c] = n @@ -359,9 +362,10 @@ class ConditionSearchManager: if changed: grace_tag = f", grace={n_grace}" if n_grace else "" + mode_tag = "push/pop" if grace <= 0 else "grace-sticky" logger.info( - "🔄 [%s] +%d / -%d (현재 %d종목%s)", - strategy_id, len(enters), len(exits), len(new_set), grace_tag, + "🔄 [%s] +%d / -%d (현재 %d종목%s, %s)", + strategy_id, len(enters), len(exits), len(ordered), grace_tag, mode_tag, ) if enters: preview = ", ".join( @@ -376,17 +380,15 @@ class ConditionSearchManager: if self.on_change and changed: try: - self.on_change(strategy_id, new_set, enters, exits) + self.on_change(strategy_id, set(ordered), enters, exits) except Exception as e: logger.warning("on_change 콜백 예외: %s", e) - # 백테스트 재현성 보장: 첫 tick 또는 변동 발생 tick 마다 풀 스냅샷 저장 - # (effective universe 기준 — 봇이 실제로 보던 universe 가 그대로 기록됨) - # 변동 없는 tick 은 공간 절약 위해 skip + # effective 순서 그대로 history (첫 tick 또는 변동 시) if changed or first_tick: with self._lock: - ordered = list(self._current_order.get(strategy_id) or []) - self._save_snapshot(strategy_id, ordered, new_names) + ordered_save = list(self._current_order.get(strategy_id) or []) + self._save_snapshot(strategy_id, ordered_save, new_names) @staticmethod def _build_ordered_universe( diff --git a/kis_trader/network/kiwoom_condition_manager.py b/kis_trader/network/kiwoom_condition_manager.py index fe57d3f..1e0c22d 100644 --- a/kis_trader/network/kiwoom_condition_manager.py +++ b/kis_trader/network/kiwoom_condition_manager.py @@ -158,6 +158,8 @@ class KiwoomConditionSearchManager(ConditionSearchManager): self._cnsrreq_pending: Set[str] = set() self._cnsrreq_confirmed: Set[str] = set() self._cnsrreq_retry_timer: Optional[threading.Timer] = None + # CNSRCLR 해제 완료(rc=0) 응답 대기 이벤트 추적 + self._cnsrclr_ack_events: Dict[str, threading.Event] = {} # 900003(이미 등록) 에 대한 CNSRCLR→재요청은 seq당 사이클 1회만 (무한 재시도 금지) self._cnsr_dup_cleared: Set[str] = set() # CNSRCLR→REQ 파이프라인에서 send 대상 WS 캐시 @@ -196,6 +198,17 @@ class KiwoomConditionSearchManager(ConditionSearchManager): ) if not self._token: logger.warning("키움 토큰 발급 실패 → 조건검색 매니저 비활성") + try: + from kis_trader.utils.ops_alert import ops_alert + ops_alert( + "token_kiwoom_cond", + "키움 조건검색 토큰 실패 → 매니저 OFF", + detail="MOMENTUM/SHORT 등 kiwoom_condition 유니버스 위험", + level="critical", + session_only=False, + ) + except Exception: + pass return False self._bind_shared_handlers() @@ -225,6 +238,17 @@ class KiwoomConditionSearchManager(ConditionSearchManager): ) if not self._token: logger.warning("키움 토큰 발급 실패 → 조건검색 매니저 비활성") + try: + from kis_trader.utils.ops_alert import ops_alert + ops_alert( + "token_kiwoom_cond", + "키움 조건검색 토큰 실패 → 매니저 OFF", + detail="단독 WS 경로", + level="critical", + session_only=False, + ) + except Exception: + pass return False self._running = True @@ -250,6 +274,16 @@ class KiwoomConditionSearchManager(ConditionSearchManager): def stop(self) -> None: self._running = False + self._cancel_cnsrreq_retry() + # 종료 전 등록된 조건식 CNSRCLR 발송 (시세 WS의 종목 REMOVE와 대칭, 죽은 소켓이면 생략) + try: + target_ws = self._shared_ws if self._shared_mode else self._ws + is_connected = getattr(target_ws, "_connected", True) if target_ws is not None else False + if target_ws is not None and is_connected and self._active_seqs: + logger.info("🛑 키움 조건검색 매니저 종료 전 CNSRCLR 발송 (seq=%s)", self._active_seqs) + self._clear_active_seqs(target_ws, self._active_seqs) + except Exception as e: + logger.warning("키움 조건검색 종료 전 CNSRCLR 발송 실패: %s", e) if self._shared_mode: self._unbind_shared_handlers() return @@ -307,6 +341,14 @@ class KiwoomConditionSearchManager(ConditionSearchManager): except Exception as e: logger.debug("키움 조건검색 CNSRLST 발송 실패: %s", e) + def _trigger_cnsrclr_ack(self, seq) -> None: + if not seq: + return + with self._kw_lock: + ev = self._cnsrclr_ack_events.get(str(seq)) + if ev: + ev.set() + def _on_shared_trnm(self, ws, msg: dict) -> None: trnm = msg.get("trnm") if trnm == "CNSRLST": @@ -314,12 +356,14 @@ class KiwoomConditionSearchManager(ConditionSearchManager): elif trnm == "CNSRREQ": self._handle_cnsrreq(msg) elif trnm == "CNSRCLR": + seq_val = _extract_cnsr_seq(msg) logger.debug( "키움 조건검색 CNSRCLR 응답: seq=%s rc=%s msg=%s", - _extract_cnsr_seq(msg), + seq_val, msg.get("return_code"), msg.get("return_msg"), ) + self._trigger_cnsrclr_ack(seq_val) def _on_shared_real(self, ws, msg: dict) -> None: """조건검색 편입/이탈 REAL — 843 필드 있는 항목만 처리.""" @@ -412,12 +456,14 @@ class KiwoomConditionSearchManager(ConditionSearchManager): return if trnm == "CNSRCLR": + seq_val = _extract_cnsr_seq(data) logger.debug( "키움 조건검색 CNSRCLR 응답: seq=%s rc=%s msg=%s", - _extract_cnsr_seq(data), + seq_val, data.get("return_code"), data.get("return_msg"), ) + self._trigger_cnsrclr_ack(seq_val) return # ------------------------------------------------------------------ @@ -490,12 +536,18 @@ class KiwoomConditionSearchManager(ConditionSearchManager): def _send_one_cnsrclr(self, ws, seq: str) -> bool: """단일 seq CNSRCLR(실시간 해제) 발송 — Bye 후 유령 등록 제거용.""" + if ws is not None and not getattr(ws, "_connected", True): + logger.debug("키움 CNSRCLR 스킵 (소켓 연결 끊김): seq=%s", seq) + return False + with self._kw_lock: + ev = threading.Event() + self._cnsrclr_ack_events[str(seq)] = ev payload = {"trnm": "CNSRCLR", "seq": seq} try: if self._shared_mode and self._shared_ws: return bool(self._shared_ws.send_json(payload)) target = ws if ws is not None else self._ws - if target is None: + if target is None or not getattr(target, "_connected", True): return False target.send(json.dumps(payload)) return True @@ -558,9 +610,22 @@ class KiwoomConditionSearchManager(ConditionSearchManager): # Bye 후 유령 등록(900003) 방지: 재등록 전에 항상 클리어 (기본 ON) if get_env_bool("KIWOOM_CNSRCLR_BEFORE_REQ", True): self._clear_active_seqs(ws, seqs) - settle = float(get_env_float("KIWOOM_CNSRCLR_SETTLE_SEC", 1.0)) + settle = float(get_env_float("KIWOOM_CNSRCLR_SETTLE_SEC", 2.0)) if settle > 0: - time.sleep(settle) + start_ts = time.time() + while time.time() - start_ts < settle: + with self._kw_lock: + all_acked = all( + self._cnsrclr_ack_events.get(str(s), threading.Event()).is_set() + for s in seqs + ) + if all_acked: + logger.debug( + "⚡ 키움 CNSRCLR 응답 수신 완료(초기) — 즉시 CNSRREQ 진행 (%.2fs)", + time.time() - start_ts, + ) + break + time.sleep(0.05) gap_lo = float(get_env_float("KIWOOM_CNSRREQ_GAP_MIN_SEC", 0.8)) gap_hi = float(get_env_float("KIWOOM_CNSRREQ_GAP_MAX_SEC", 1.5)) @@ -585,8 +650,8 @@ class KiwoomConditionSearchManager(ConditionSearchManager): def _schedule_cnsrreq_retry(self, ws, *, attempt: int) -> None: """CNSRREQ 응답이 안 온 seq 만 간격 두고 재발송 (상한 초과 시 중단 · 무한 금지).""" - max_retries = get_env_int("KIWOOM_CNSRREQ_MAX_RETRIES", 3) - retry_delay = float(get_env_float("KIWOOM_CNSRREQ_RETRY_SEC", 5.0)) + max_retries = get_env_int("KIWOOM_CNSRREQ_MAX_RETRIES", 30) + retry_delay = float(get_env_float("KIWOOM_CNSRREQ_RETRY_SEC", 3.0)) gap_lo = float(get_env_float("KIWOOM_CNSRREQ_GAP_MIN_SEC", 0.8)) gap_hi = float(get_env_float("KIWOOM_CNSRREQ_GAP_MAX_SEC", 1.5)) if gap_hi < gap_lo: @@ -621,6 +686,15 @@ class KiwoomConditionSearchManager(ConditionSearchManager): for i, seq in enumerate(missing): if i > 0: time.sleep(random.uniform(gap_lo, gap_hi)) + if get_env_bool("KIWOOM_CNSRCLR_BEFORE_REQ", True): + self._send_one_cnsrclr(ws, seq) + start_ts = time.time() + while time.time() - start_ts < 1.0: + with self._kw_lock: + ev = self._cnsrclr_ack_events.get(str(seq)) + if ev and ev.is_set(): + break + time.sleep(0.05) self._send_one_cnsrreq(ws, seq) self._schedule_cnsrreq_retry(ws, attempt=attempt + 1) @@ -629,18 +703,19 @@ class KiwoomConditionSearchManager(ConditionSearchManager): self._cnsrreq_retry_timer.start() def _handle_cnsr_already_registered(self, seq: str, data: Dict) -> None: - """rc=900003: seq당 1회만 CNSRCLR→CNSRREQ. 두 번째면 포기(무한 금지).""" + """rc=900003: seq당 1회만 CNSRCLR→CNSRREQ. 두 번째면 포기(무한 금지) 대신 백그라운드 고정 재시도로 인계.""" with self._kw_lock: already = seq in self._cnsr_dup_cleared if not already: self._cnsr_dup_cleared.add(seq) if already: logger.warning( - "⚠️ 키움 CNSRREQ 900003 재발 (seq=%s) — CLR 후에도 충돌, 추가 재시도 없음 msg=%s", + "⚠️ 키움 CNSRREQ 900003 재발 (seq=%s) — CLR 후에도 충돌, 즉시 시도 중단 및 백그라운드 고정 대기(3초) 유지 msg=%s", seq, data.get("return_msg"), ) - with self._kw_lock: - self._cnsrreq_pending.discard(seq) + # 900003 두 번째 충돌 시 즉시 해당 조건을 폐기(discard)하여 유니버스 0으로 만드는 로직 퇴출. + # with self._kw_lock: + # self._cnsrreq_pending.discard(seq) return ws = self._cnsr_send_ws @@ -653,9 +728,16 @@ class KiwoomConditionSearchManager(ConditionSearchManager): seq, ) self._send_one_cnsrclr(ws, seq) - settle = float(get_env_float("KIWOOM_CNSRCLR_SETTLE_SEC", 1.0)) + settle = float(get_env_float("KIWOOM_CNSRCLR_SETTLE_SEC", 2.0)) if settle > 0: - time.sleep(settle) + start_ts = time.time() + while time.time() - start_ts < settle: + with self._kw_lock: + ev = self._cnsrclr_ack_events.get(str(seq)) + if ev and ev.is_set(): + logger.debug("⚡ 키움 CNSRCLR 응답 수신 완료(900003 회복) — 즉시 CNSRREQ 진행 (seq=%s)", seq) + break + time.sleep(0.05) self._send_one_cnsrreq(ws, seq) def _handle_cnsrreq(self, data: Dict) -> None: @@ -705,6 +787,7 @@ class KiwoomConditionSearchManager(ConditionSearchManager): def _handle_real(self, rows: List) -> None: """REAL push: 843=I(편입)/D(이탈), 9001=종목코드. seq 별 universe 갱신.""" touched: Set[str] = set() + job_events: List[Dict[str, Any]] = [] with self._kw_lock: for it in rows: vals = it.get("values") if isinstance(it, dict) else None @@ -735,7 +818,33 @@ class KiwoomConditionSearchManager(ConditionSearchManager): else: # "I" 또는 기타 → 편입으로 처리 (삽입순 유지) if code not in bucket: bucket[code] = code + if ins_del in ("I", "D"): + bt = str(vals.get("20") or "").strip() # HHMMSS + for sid in list(self._sid_by_seq.get(seq, [])): + job_events.append({ + "strategy_id": sid, + "code": code, + "job_flag": ins_del, + "broker_time": bt, + }) touched.add(seq) + if job_events and self.db is not None: + try: + from .condition_job_events import insert_condition_job_event + from .condition_common import normalize_job_flag + for ev in job_events: + insert_condition_job_event( + self.db, + strategy_id=ev["strategy_id"], + code=ev["code"], + job_flag=normalize_job_flag(ev["job_flag"]), + broker="kiwoom", + name=ev["code"], + broker_time=ev.get("broker_time") or "", + source="kiwoom_real", + ) + except Exception as e: + logger.debug("키움 job_event: %s", e) for seq in touched: self._publish_seq(seq) diff --git a/kis_trader/network/ls_condition_manager.py b/kis_trader/network/ls_condition_manager.py index 145c816..35bfbbf 100644 --- a/kis_trader/network/ls_condition_manager.py +++ b/kis_trader/network/ls_condition_manager.py @@ -14,8 +14,31 @@ kis_trader/network/ls_condition_manager.py — LS 서버저장조건(AFR) 운영 중 LS HTS 에서 조건 CRUD(삭제·이름변경·인덱스 재배열) 시: ``LS_CONDITION_REMAP_SEC`` 주기로 t1866 이름→query_index 재매핑, 바뀌면 구 AFR UNREG + t1860 D → t1859 → t1860 E → AFR REG (재시작 불필요). - ``LS_CONDITION_SNAPSHOT_REFRESH_SEC`` 주기로 동일 인덱스라도 t1859 스냅샷 - 재동기화 (AFR 는 델타만이라 장중 sticky/빈 RAM 보정). + +t1859 주기 스냅샷 (``LS_CONDITION_SNAPSHOT_REFRESH_SEC``, 기본 0=비활성): + **AFR alert 정상 + RAM 비어있지 않으면 스킵** (clear 교체는 레이스 구멍·빈 DB 이력 유발). + 시드/복구만: alert 없음 · RAM 0종목 · rematch/기동 시. + 국장 세션·장전 준비 밖이면 주기 t1859 도 스킵 (주말 REST 절약). + 축소(20→1) 전체교체 기본 거부 (``LS_T1859_ALLOW_SHRINK=false``) — EXIT 는 AFR O 만. + +RAM 0 재시드 (``LS_T1859_EMPTY_RETRY_SEC``, 기본 1): + AFR 는 풀덤프 없음 → 기동 t1859=검색결과없음 이면 **REST t1859 를 RAM>0 될 때까지** 재시도. + 공식 t1859 초당 1건 → 기본 1초(실제 간격 ≥ ``LS_CONDITION_TR_GAP_SEC`` 1.1). + ``=0`` 은 무한 연타가 아니라 **재시도 OFF** (영구 0 위험). 키움 CNSRREQ 와 다름. + +AFR 침묵 재동기 (``LS_T1859_STALE_RESYNC_SEC``, 기본 60=1분): + alert 등록·RAM>0 인데 AFR N/R/O 가 N초 없으면 **소켓 침묵 방어(1분 주기 심폐소생술)** 수행. + - LS는 매매 주문이 아닌 시세·조건식 전용 기지이므로 429 주문 방해 차단 위험 0%! + - 1분 주기로 하루 390번 눈 감아도 총 암흑기 누계 58초(1분 미만)에 불과! + - 0종목 응답 시: 통신 장애/가성 오류로 간주하여 기존 RAM 종목을 자살골 청산 없이 완벽 보존! + - 정상 응답 시: 기존 RAM 리스트와 1:1 비교(Delta Diff)하여 끊김 틈새에 발생한 유령 종목 소거 및 신규 반영. + HTS 조건식 수정·AFR 무푸시 시 HTS만 늘고 봇 RAM 고정되던 구멍을 안전하게 보정. + +AFR(t1860) 국장 세션 게이트 (``LS_AFR_SESSION_GATE``, 기본 true): + - 장전 준비: 평일 ``START_HM``(기본 07:00)~``OPEN_DEADLINE``(09:30) 전은 + JIF 개장 전에도 허용 (09:00 jstatus=21 기다리면 늦음). + - 개장 JIF(21~25) sticky / 마감 JIF(30·31·41~44) 당일 차단. + - 해외 밤장과 무관. DEADLINE 지나 개장 JIF 없으면 휴장으로 OFF. """ from __future__ import annotations @@ -142,19 +165,107 @@ class _LsConditionState: self.alert_num = alert_num self.codes: Dict[str, Dict[str, Any]] = {} self.lock = threading.Lock() + # AFR N/R/O 수신 mono — 0 이면 시드만 있고 실시간 미수신 + self.last_afr_event_mono: float = 0.0 + self.last_seed_mono: float = 0.0 - def set_from_snapshot(self, rows: List[Dict[str, Any]]) -> None: + def set_from_snapshot( + self, + rows: List[Dict[str, Any]], + *, + allow_empty_clear: bool = False, + allow_shrink: bool = False, + ) -> bool: + """t1859 → RAM. + + - 빈 목록으로 기존 RAM 지우기: 기본 거부 (구멍·빈이력). + - 종목수 축소 전체교체: 기본 거부 (AFR ``O`` 만 EXIT). 장전 1종 응답이 + sticky 20종을 덮어쓰던 20→1 사고를 막는다. + - 빈 RAM 시드 / ``allow_shrink=True`` 일 때만 축소 허용. + """ with self.lock: - self.codes.clear() - for r in rows: + new_codes: Dict[str, Dict[str, Any]] = {} + for r in rows or []: code = str(r.get("shcode") or r.get("code") or "").strip() if not code: continue + new_codes[code] = { + "code": code, + "name": str(r.get("hname") or r.get("name") or code)[:100], + "price": r.get("price") or 0, + } + if (not new_codes) and self.codes and (not allow_empty_clear): + return False + if ( + self.codes + and new_codes + and (not allow_shrink) + and len(new_codes) < len(self.codes) + ): + return False + self.codes.clear() + self.codes.update(new_codes) + return True + + def merge_grow_from_snapshot(self, rows: List[Dict[str, Any]]) -> int: + """t1859 결과에서 **없는 종목만 추가** (축소·삭제 없음). 추가 개수 반환.""" + added = 0 + with self.lock: + for r in rows or []: + code = str(r.get("shcode") or r.get("code") or "").strip() + if not code or code in self.codes: + continue self.codes[code] = { "code": code, "name": str(r.get("hname") or r.get("name") or code)[:100], "price": r.get("price") or 0, } + added += 1 + return added + + def rows_prefer_snapshot_order( + self, snap_rows: List[Dict[str, Any]] + ) -> List[Dict[str, Any]]: + """t1859 스냅 순서 우선 + RAM 잔여(sticky) 뒤쪽. + + ``CAND_LIMIT`` 은 앞 N개만 매수체크하므로, AFR침묵 grow 후에도 + **지금 서버 스냅에 있는 종목**이 캡 안에 들어오게 한다. + """ + head: List[Dict[str, Any]] = [] + seen: set = set() + for r in snap_rows or []: + code = str(r.get("shcode") or r.get("code") or "").strip() + if not code or code in seen: + continue + seen.add(code) + with self.lock: + cur = self.codes.get(code) or {} + head.append({ + "code": code, + "name": str( + r.get("hname") or r.get("name") or cur.get("name") or code + )[:100], + }) + with self.lock: + rest = [ + {"code": c, "name": str(v.get("name") or c)[:100]} + for c, v in self.codes.items() + if c not in seen + ] + return head + rest + + def rows_prefer_code_first( + self, code: str, *, name: str = "" + ) -> List[Dict[str, Any]]: + """AFR N/R 종목을 맨 앞 — CAND_LIMIT 에 즉시 들어가게.""" + code = str(code or "").strip() + rows = self.as_rows() + if not code: + return rows + nm = (name or "").strip() or code + head = [{"code": code, "name": nm[:100]}] + tail = [r for r in rows if str(r.get("code") or "") != code] + return head + tail def apply_afr(self, body: Dict[str, Any]) -> tuple[str, Optional[str]]: job = str(body.get("gsJobFlag") or "").strip().upper() @@ -171,6 +282,8 @@ class _LsConditionState: self.codes[code] = {"code": code, "name": name, "price": price} elif job == "O": self.codes.pop(code, None) + if job in ("N", "R", "O"): + self.last_afr_event_mono = time.monotonic() return job, code def items(self) -> List[Dict[str, Any]]: @@ -209,21 +322,34 @@ class LsConditionSearchManager(ConditionSearchManager): self._by_alert: Dict[str, _LsConditionState] = {} self._states_by_sid: Dict[str, _LsConditionState] = {} self._watcher: Any = None - self._flush_sec = float(get_env_int("LS_CONDITION_FLUSH_SEC", 60)) + self._flush_sec = float(get_env_int("LS_CONDITION_FLUSH_SEC", 0)) # 서버 조건 CRUD 후 query_index/alert 재부착 (재시작 대체) self._remap_sec = float(get_env_int("LS_CONDITION_REMAP_SEC", 60)) - # AFR 델타만 보정 — 동일 인덱스라도 t1859 로 RAM sticky 동기화 + # 주기 t1859 — 기본 0=비활성 (AFR push/pop 본체). 시드/복구만 rematch·기동. self._snapshot_refresh_sec = float( - get_env_int("LS_CONDITION_SNAPSHOT_REFRESH_SEC", 120) + get_env_int("LS_CONDITION_SNAPSHOT_REFRESH_SEC", 0) ) + # RAM 0 일 때 t1859 재시드 주기(초). snap_refresh=0 이어도 동작 (영구0 방지) + self._empty_retry_sec = float( + get_env_int("LS_T1859_EMPTY_RETRY_SEC", 1) + ) + # AFR alert+종목 있는데 N/R/O 침묵 → t1859 Delta Diff+재REG (하루 누적 눈감는시간 58초, 기본 60=1분) + self._stale_resync_sec = float( + get_env_int("LS_T1859_STALE_RESYNC_SEC", 60) + ) + # t1859 가 기존 RAM 보다 적을 때 전체교체 허용 (기본 false — 20→1 방지) + self._t1859_allow_shrink = get_env_bool("LS_T1859_ALLOW_SHRINK", False) self._tr_gap_sec = float(get_env_float("LS_CONDITION_TR_GAP_SEC", 1.1)) self._flush_thread: Optional[threading.Thread] = None self._maint_lock = threading.RLock() self._last_remap_mono = 0.0 self._last_snap_refresh_mono = 0.0 + self._last_empty_retry_mono = 0.0 + self._last_stale_resync_mono = 0.0 self._last_missing_warn: Dict[str, float] = {} # t1860 E 가 sAlertNum=0 이면 장중 재시도 (장외 정상 ACK + 키 미발급) self._afr_pending_retry: Set[str] = set() + self._afr_offhours_log_mono: float = 0.0 self._ready = threading.Event() self._start_ok = False # LS 조건 유니버스 합집합 → LS WS sync 등 (main 이 등록) @@ -459,9 +585,61 @@ class LsConditionSearchManager(ConditionSearchManager): ws_url = self._rt.LS_WS_MOCK if self._use_mock else self._rt.LS_WS_REAL - def on_change(st: Any) -> None: + def on_change( + st: Any, + job: Optional[str] = None, + code: Optional[str] = None, + body: Optional[Dict[str, Any]] = None, + ) -> None: try: - self._apply_result(st.strategy_id, st.as_rows()) + b = body if isinstance(body, dict) else {} + nm = "" + if job and code: + try: + from .condition_job_events import insert_condition_job_event + from .condition_common import normalize_job_flag + try: + st.last_afr_event_mono = time.monotonic() + except Exception: + pass + nm = str( + b.get("gsHname") or b.get("hname") or code + )[:100] + try: + px = float( + str(b.get("gsPrice") or b.get("price") or 0) + .replace(",", "") + ) + except (TypeError, ValueError): + px = 0.0 + bt = str( + b.get("gsTime") or b.get("sTime") or "" + ).strip() + insert_condition_job_event( + self.db, + strategy_id=st.strategy_id, + code=str(code), + job_flag=normalize_job_flag(job), + broker="ls", + name=nm, + price=px, + broker_time=bt, + query_name=str(getattr(st, "query_name", "") or ""), + source="ls_afr", + ) + except Exception as e: + logger.debug("LS job_event: %s", e) + # N/R 은 맨 앞 — BREAKOUT_CAND_LIMIT 등에 신규가 안 잘리게 + j_up = str(job or "").strip().upper() + if j_up in ("N", "R") and code: + if not nm: + nm = str( + b.get("gsHname") or b.get("hname") or code + )[:100] + rows = st.rows_prefer_code_first(str(code), name=nm) + else: + rows = st.as_rows() + self._apply_result(st.strategy_id, rows) self._save_ls_snapshot(st, source="ls_afr") except Exception as e: logger.warning("LS on_change: %s", e) @@ -521,20 +699,86 @@ class LsConditionSearchManager(ConditionSearchManager): logger.debug("t1860 D %s: %s", st.strategy_id, e) st.alert_num = "" if clear_ram: - st.set_from_snapshot([]) + st.set_from_snapshot([], allow_empty_clear=True) self._apply_result(st.strategy_id, []) self._save_ls_snapshot(st, source="ls_remap_clear") + def _kr_afr_session_allows(self) -> bool: + """국장 AFR(t1860) 허용 — LS JIF + 장전 준비 창 (해외 밤장과 무관). + + ``LS_AFR_SESSION_GATE=false`` 이면 항상 허용(레거시 연타). + LS WS 미기동 시: 평일 START~DEADLINE 전 준비 허용, DEADLINE 후·주말 거부. + """ + if not get_env_bool("LS_AFR_SESSION_GATE", True): + return True + try: + from kis_trader.ws.ls_ws import get_active_ls_ws + ws = get_active_ls_ws() + if ws is not None and hasattr(ws, "is_kr_afr_session_open"): + return bool(ws.is_kr_afr_session_open()) + except Exception as e: + logger.debug("LS AFR 세션 조회 실패 → 벽시계 폴백: %s", e) + now = datetime.now() + if now.weekday() >= 5: + return False + hhmm = now.hour * 100 + now.minute + start = int(get_env_int("LS_AFR_SESSION_START_HM", 700) or 700) + end = int(get_env_int("LS_AFR_SESSION_END_HM", 1530) or 1530) + deadline = int(get_env_int("LS_AFR_OPEN_DEADLINE_HM", 930) or 930) + if not (start <= hhmm <= end): + return False + # WS 없으면 장전 준비만 (DEADLINE 이후 종일 연타 금지) + return hhmm < deadline + + def _log_afr_session_wait(self, st: _LsConditionState, *, why: str) -> None: + """장외 AFR 스킵 로그 — 스로틀 (해외 세션과 혼동 방지 문구).""" + gap = float(get_env_float("LS_AFR_OFFHOURS_LOG_SEC", 600.0) or 600.0) + now = time.monotonic() + if gap > 0 and (now - float(self._afr_offhours_log_mono or 0)) < gap: + return + self._afr_offhours_log_mono = now + jif = {} + try: + from kis_trader.ws.ls_ws import get_active_ls_ws + ws = get_active_ls_ws() + if ws is not None and hasattr(ws, "get_jif_snapshot"): + jif = ws.get_jif_snapshot() or {} + except Exception: + jif = {} + logger.info( + "⏸ LS AFR 대기(국장세션 아님) sid=%s name=%s why=%s " + "jstatus=%s afr_live=%s pending=%d — 해외밤장 무관·JIF/개장 시 재시도", + st.strategy_id, st.query_name, why, + jif.get("jstatus") or "-", + jif.get("kr_afr_session_live"), + len(self._afr_pending_retry), + ) + def _mount_snapshot_and_afr( self, st: _LsConditionState, *, do_snapshot: bool, do_afr: bool, + stale_resync: bool = False, ) -> None: - """t1859 → RAM, (옵션) t1860 E + AFR REG.""" + """t1859 → RAM, (옵션) t1860 E + AFR REG. + + ``stale_resync=True`` (AFR 침묵): + - snap 종목수 ≥ RAM 이면 전체교체(동수·증가 — 조건식 변경 반영) + - snap 이 더 적으면 **merge grow 만** (20→1 축소 사고 방지) + - 이어서 AFR 재REG + """ if not self._token or self._rt is None: return + + # 국장 장외: t1860 연타 금지 (sAlertNum=0 ACK 폭주 → 한도/정지 리스크). + # rematch/스냅샷은 유지. 개장(JIF sticky) 되면 pending 이 재시도. + if do_afr and not self._kr_afr_session_allows(): + self._afr_pending_retry.add(st.strategy_id) + self._log_afr_session_wait(st, why="session_gate") + do_afr = False + if do_snapshot: try: snap = self._rt.t1859_snapshot( @@ -544,21 +788,64 @@ class LsConditionSearchManager(ConditionSearchManager): except Exception as e: logger.warning("t1859 실패 %s: %s", st.strategy_id, e) snap = [] - st.set_from_snapshot(snap or []) - self._apply_result(st.strategy_id, st.as_rows()) - self._save_ls_snapshot(st, source="ls_t1859") - n = len(st.as_rows()) - if n <= 0: - logger.warning( - "⚠️ LS 스냅샷 0종목 sid=%s name=%s idx=%s " - "(AFR 델타만으로는 장중 sticky 불가 → 주기 t1859 대기)", - st.strategy_id, st.query_name, st.query_index, - ) + n_before = len(st.items()) + n_snap = 0 + try: + n_snap = len(snap or []) + except Exception: + n_snap = 0 + + if stale_resync: + if n_before > 0 and n_snap <= 0: + logger.warning( + "⚠️ [0종목 보호 방패] LS 15분 주기 소켓 재동기 중 t1859 응답 0종목(Empty) 감지 → " + "통신 장애/일시 공백으로 판단하여 자살골 청산 없이 기존 RAM(%d종목) 완벽 보존 sid=%s name=%s", + n_before, st.strategy_id, st.query_name, + ) + elif n_snap > 0: + st.set_from_snapshot(snap or [], allow_shrink=True) + st.last_seed_mono = time.monotonic() + self._apply_result(st.strategy_id, st.as_rows()) + self._save_ls_snapshot(st, source="ls_t1859_stale_diff") + logger.info( + "🔄 [Delta Diff 갱신] LS 15분 주기 소켓 재동기 sid=%s name=%s " + "RAM 기존 %d → Diff 반영 %d종목 (유령종목 정제)", + st.strategy_id, st.query_name, n_before, n_snap, + ) else: - logger.info( - "📌 LS 스냅샷 sid=%s name=%s %d종목", - st.strategy_id, st.query_name, n, + allow_shrink = ( + bool(self._t1859_allow_shrink) or (n_before <= 0) ) + applied = st.set_from_snapshot( + snap or [], allow_shrink=allow_shrink, + ) + if not applied: + logger.warning( + "⚠️ t1859 적용 거부 → RAM 유지 sid=%s (기존 %d / snap %d, " + "빈응답·축소교체 방지 allow_shrink=%s)", + st.strategy_id, n_before, n_snap, allow_shrink, + ) + else: + n = len(st.items()) + if n <= 0: + logger.warning( + "⚠️ LS t1859 스냅 0종목 sid=%s name=%s idx=%s " + "(OpenAPI 검색결과없음/일시공백 — HTS·키움 CNSRREQ 와 별개. " + "AFR 은 풀덤프 없음 → LS_T1859_EMPTY_RETRY 재시드)", + st.strategy_id, st.query_name, st.query_index, + ) + else: + st.last_seed_mono = time.monotonic() + self._apply_result(st.strategy_id, st.as_rows()) + src = ( + "ls_t1859_stale" if stale_resync else "ls_t1859" + ) + self._save_ls_snapshot(st, source=src) + logger.info( + "📌 LS 스냅샷 sid=%s name=%s %d종목%s", + st.strategy_id, st.query_name, n, + " (AFR침묵재동기)" if stale_resync else "", + ) if not do_afr: return @@ -707,12 +994,18 @@ class LsConditionSearchManager(ConditionSearchManager): self._teardown_afr(st, clear_ram=False) st.query_name = qname st.query_index = qidx + # 스냅샷은 유지, AFR 는 _mount 내부 국장세션 게이트 self._mount_snapshot_and_afr( st, do_snapshot=True, do_afr=True, ) elif need_afr: + # 장외: t1859+t1860 연타 금지 — pending 만 유지, 개장 시 재시도 + if not self._kr_afr_session_allows(): + self._afr_pending_retry.add(sid) + self._log_afr_session_wait(st, why="pending_offhours") + continue logger.info( - "🔄 LS AFR 재등록 sid=%s name=%s (alert 없음/강제/대기재시도)", + "🔄 LS AFR 재등록 sid=%s name=%s (alert없음/대기재시도)", sid, st.query_name, ) self._mount_snapshot_and_afr( @@ -736,38 +1029,196 @@ class LsConditionSearchManager(ConditionSearchManager): if force_snapshot: self._last_snap_refresh_mono = self._last_remap_mono + def _afr_alert_live(self, st: _LsConditionState) -> bool: + alert = str(st.alert_num or "").strip() + return bool(alert) and (alert.strip("0") != "") + + def _afr_activity_mono(self, st: _LsConditionState) -> float: + return max( + float(getattr(st, "last_afr_event_mono", 0) or 0), + float(getattr(st, "last_seed_mono", 0) or 0), + ) + + def _afr_is_stale(self, st: _LsConditionState) -> bool: + """AFR 침묵 sticky — HTS 만 갱신되고 봇 RAM 고정되는 구멍.""" + if self._stale_resync_sec <= 0: + return False + if not str(st.query_index or "").strip(): + return False + if len(st.items()) <= 0: + return False + last = self._afr_activity_mono(st) + if last <= 0: + return True + return (time.monotonic() - last) >= max(15.0, self._stale_resync_sec) + + def _any_afr_stale(self) -> bool: + return any(self._afr_is_stale(st) for st in self._states_by_sid.values()) + + def _snapshot_periodic_needed(self, st: _LsConditionState) -> bool: + """주기 t1859 가 필요한가. + + AFR 구독이 살아 있고 RAM 에 종목이 있으면 **교체 금지** + (clear 레이스 → 순간 구멍·ls_candidates_history 빈 슬롯). + AFR 침묵은 ``_refresh_stale_snapshots`` 가 별도 처리. + """ + if self._afr_alert_live(st) and len(st.items()) > 0: + return False + # 장외·주말: 시드용 주기도 REST 부담 — 세션/장전 준비 창에서만 + if get_env_bool("LS_AFR_SESSION_GATE", True) and not self._kr_afr_session_allows(): + return False + return True + def _refresh_snapshots_only(self) -> None: - """인덱스 유지한 채 t1859 만 재동기화 (AFR sticky 보정).""" + """필요 시에만 t1859 (AFR live 교체 금지 · 빈 RAM/미등록 시드).""" with self._maint_lock: if not self._ensure_token(force=False, reason="snap_refresh"): return + n_skip = 0 + n_run = 0 for st in list(self._states_by_sid.values()): if not str(st.query_index or "").strip(): continue + if not self._snapshot_periodic_needed(st): + n_skip += 1 + continue try: self._mount_snapshot_and_afr( st, do_snapshot=True, do_afr=False, ) + n_run += 1 except Exception as e: logger.warning( "LS snapshot refresh 실패 %s: %s", st.strategy_id, e, ) self._last_snap_refresh_mono = time.monotonic() + if n_skip and not n_run: + logger.debug( + "LS snapshot refresh 스킵 %d건 (AFR live 또는 장외)", + n_skip, + ) + + def _refresh_empty_snapshots(self) -> None: + """RAM 0 인 조건만 t1859 재시드. + + AFR REG 후 서버는 풀목록을 안 밀어주고 델타(N/R/O)만 준다. + 기동 시 t1859=검색결과없음 이면 snap_refresh=0 일 때 **영구 0** → 여기로 회복. + 축소교체는 ``LS_T1859_ALLOW_SHRINK`` (기본 false) 로 계속 거부. + """ + with self._maint_lock: + if not self._ensure_token(force=False, reason="empty_seed"): + return + if get_env_bool("LS_AFR_SESSION_GATE", True) and not self._kr_afr_session_allows(): + return + n_run = 0 + for st in list(self._states_by_sid.values()): + if not str(st.query_index or "").strip(): + continue + if len(st.items()) > 0: + continue + try: + logger.warning( + "🔄 LS RAM0 → t1859 재시드 sid=%s name=%s", + st.strategy_id, st.query_name, + ) + self._mount_snapshot_and_afr( + st, do_snapshot=True, do_afr=False, + ) + n_run += 1 + except Exception as e: + logger.warning( + "LS empty seed 실패 %s: %s", st.strategy_id, e, + ) + self._last_empty_retry_mono = time.monotonic() + if n_run: + logger.info("LS empty RAM t1859 재시드 %d건", n_run) + + def _refresh_stale_snapshots(self) -> None: + """AFR 침묵 sticky → t1859 재동기 + AFR 재REG. + + alert 있어 주기 t1859 가 스킵되는데 서버가 N/R/O 를 안 주면 + HTS 만 늘고 봇 RAM 이 고정됨 (BREAKOUT 실측). + """ + with self._maint_lock: + if not self._ensure_token(force=False, reason="stale_resync"): + return + if get_env_bool("LS_AFR_SESSION_GATE", True) and not self._kr_afr_session_allows(): + return + n_run = 0 + for st in list(self._states_by_sid.values()): + if not self._afr_is_stale(st): + continue + age = 0 + last = self._afr_activity_mono(st) + if last > 0: + age = int(time.monotonic() - last) + try: + logger.warning( + "🔄 LS AFR침묵 → t1859+재REG sid=%s name=%s " + "RAM=%d silence=%ss", + st.strategy_id, st.query_name, len(st.items()), age, + ) + self._mount_snapshot_and_afr( + st, + do_snapshot=True, + do_afr=True, + stale_resync=True, + ) + n_run += 1 + except Exception as e: + logger.warning( + "LS stale resync 실패 %s: %s", st.strategy_id, e, + ) + self._last_stale_resync_mono = time.monotonic() + if n_run: + logger.info("LS AFR침묵 재동기 %d건", n_run) + + def _empty_retry_interval(self) -> float: + """RAM0 t1859 재시드 간격. 공식 초당1건 → TR_GAP(기본 1.1) 이상.""" + if self._empty_retry_sec <= 0: + return 0.0 + gap = max(1.0, float(self._tr_gap_sec or 1.0)) + return max(gap, float(self._empty_retry_sec)) + + def _stale_resync_interval(self) -> float: + if self._stale_resync_sec <= 0: + return 0.0 + gap = max(1.0, float(self._tr_gap_sec or 1.0)) + return max(gap, float(self._stale_resync_sec)) + + def _any_ram_empty(self) -> bool: + for st in list(self._states_by_sid.values()): + if not str(st.query_index or "").strip(): + continue + if len(st.items()) <= 0: + return True + return False def _maint_loop(self) -> None: - """flush + rematch + t1859 sticky 주기 루프.""" + """flush + rematch + (옵션) t1859 + RAM0 재시드 + AFR침묵 재동기.""" # 기동 직후 즉시 rematch 하지 않음 (start 에서 1회 완료) now0 = time.monotonic() self._last_remap_mono = now0 self._last_snap_refresh_mono = now0 + # empty: 기동 t1859 공백이면 첫 sleep 후 바로 재시드 (만료시각을 과거로) + _er = self._empty_retry_interval() + self._last_empty_retry_mono = (now0 - _er) if _er > 0 else now0 + _sr = self._stale_resync_interval() + # 기동 직후 곧바로 침묵판정하지 않음 — 시드 직후 여유 + self._last_stale_resync_mono = now0 last_flush_mono = now0 while self._running: - tick = min( - max(5.0, self._flush_sec), - max(5.0, self._remap_sec), - max(5.0, self._snapshot_refresh_sec), - ) - time.sleep(tick) + intervals = [max(5.0, self._remap_sec)] + if self._snapshot_refresh_sec > 0: + intervals.append(max(5.0, self._snapshot_refresh_sec)) + # RAM 비어 있을 때만 짧은 wake (채워지면 remap 주기로) + if self._empty_retry_sec > 0 and self._any_ram_empty(): + intervals.append(self._empty_retry_interval()) + if self._stale_resync_sec > 0 and self._any_afr_stale(): + intervals.append(min(30.0, self._stale_resync_interval())) + if self._flush_sec > 0: + intervals.append(max(5.0, self._flush_sec)) + time.sleep(min(intervals)) if not self._running: return now = time.monotonic() @@ -776,12 +1227,30 @@ class LsConditionSearchManager(ConditionSearchManager): self._remap_all(force_snapshot=False, force_afr=False) now = time.monotonic() if ( - now - self._last_snap_refresh_mono + self._snapshot_refresh_sec > 0 + and now - self._last_snap_refresh_mono >= max(5.0, self._snapshot_refresh_sec) ): self._refresh_snapshots_only() now = time.monotonic() - if now - last_flush_mono >= max(5.0, self._flush_sec): + _er = self._empty_retry_interval() + if ( + _er > 0 + and now - self._last_empty_retry_mono >= _er + ): + self._refresh_empty_snapshots() + now = time.monotonic() + _sr = self._stale_resync_interval() + if ( + _sr > 0 + and now - self._last_stale_resync_mono >= _sr + ): + self._refresh_stale_snapshots() + now = time.monotonic() + if ( + self._flush_sec > 0 + and now - last_flush_mono >= max(5.0, self._flush_sec) + ): for st in list(self._states_by_sid.values()): try: self._save_ls_snapshot(st, source="ls_flush") @@ -804,17 +1273,47 @@ class LsConditionSearchManager(ConditionSearchManager): return def _save_ls_snapshot(self, st: _LsConditionState, *, source: str) -> None: + """ls_candidates_history 적재 — **봇이 실제로 보는** effective 유니버스. + + ``st.items()``(t1859 raw)만 쓰면 grace/sticky 로 RAM 에 남은 종목이 + history 에 안 들어가 슬롯교집합이 20→1 로 전량탈락한다. + """ if self.history_enabled and self.db is not None: + sid = st.strategy_id + items: List[Dict[str, Any]] = [] + try: + with self._lock: + ordered = list(self._current_order.get(sid) or []) + if not ordered: + ordered = list(self._current.get(sid) or []) + nm = dict(self._name_map) + st_by_code = { + str(it.get("code") or "").strip(): it for it in (st.items() or []) + } + for c in ordered: + code = str(c or "").strip() + if not code: + continue + base = st_by_code.get(code) or {} + items.append({ + "code": code, + "name": str(base.get("name") or nm.get(code) or code)[:100], + "price": base.get("price") or 0, + }) + except Exception: + items = [] + if not items: + items = st.items() n = insert_ls_candidates_snapshot( self.db, strategy_id=st.strategy_id, query_name=st.query_name, query_index=st.query_index, - items=st.items(), + items=items, source=source, ) logger.debug( - "📼 [ls_history] %s src=%s %d종목", + "📼 [ls_history] %s src=%s %d종목 (effective)", st.strategy_id, source, n, ) # 이력 OFF 여도 WS follow 를 위해 합집합 emit diff --git a/kis_trader/network/ls_token.py b/kis_trader/network/ls_token.py index 7b363f7..a2e9ca1 100644 --- a/kis_trader/network/ls_token.py +++ b/kis_trader/network/ls_token.py @@ -5,6 +5,9 @@ - 만료·IGW00121/123(무효/기간만료) 시에만 재발급 - 프로세스 공유 캐시 + 최소 재발급 간격(한도·폭주 방지) - ``/oauth2/revoke`` 는 정상 폐기용 — 매 루프 강제 발급에 쓰지 않음 + - **국내↔해외 세션 전환·WS 재연결에서 force 재발급 금지** + (LS 토큰은 조건검색 REST·국내/해외 WS 공용 — 재발급 시 상대 경로 토큰 무효화. + 키움 과거 중복발급 사고와 동일 유형. force 는 auth 오류 1회 복구만.) 스펙 예시: expires_in=86400 (24h). transactionPerSec='-'. """ diff --git a/kis_trader/network/market_guard.py b/kis_trader/network/market_guard.py index 3e6b23e..ac2b0ba 100644 --- a/kis_trader/network/market_guard.py +++ b/kis_trader/network/market_guard.py @@ -515,6 +515,18 @@ class MarketGuard: "🚨 [MarketGuard] PANIC 진입: %s — 모든 전략 신규 매수 차단", self._panic_reason, ) + if get_env_bool("OPS_ALERT_MARKET_GUARD_ENABLED", True): + try: + from kis_trader.utils.ops_alert import ops_alert + ops_alert( + "market_guard_panic", + "MarketGuard PANIC 진입 — 신규매수 차단", + detail=self._panic_reason, + level="critical", + session_only=False, + ) + except Exception: + pass else: if code != self._panic_index: return @@ -525,6 +537,18 @@ class MarketGuard: "(지속 %.1f분, 매수 재개)", name, five_min_chg, duration_min, ) + if get_env_bool("OPS_ALERT_MARKET_GUARD_ENABLED", True): + try: + from kis_trader.utils.ops_alert import ops_alert + ops_alert( + "market_guard_clear", + "MarketGuard PANIC 해제 — 매수 재개", + detail=f"{name} 5분 {five_min_chg:+.2f}% (지속 {duration_min:.1f}분)", + level="warn", + session_only=False, + ) + except Exception: + pass self._panic = False self._panic_reason = "" self._panic_index = "" diff --git a/kis_trader/network/ws_manager.py b/kis_trader/network/ws_manager.py index be73da7..6338c15 100644 --- a/kis_trader/network/ws_manager.py +++ b/kis_trader/network/ws_manager.py @@ -90,6 +90,7 @@ class WSManager: self.kis_client = kis_client self.ws_cache: Optional["KISWebSocketPriceCache"] = None + self.kis_ws_ob: Optional["KISWebSocketPriceCache"] = None self.candle_agg: Optional["CandleAggregator"] = None self.tick_recorder: Optional["TickRecorder"] = None self.trigger_snapshot_recorder: Optional["TriggerSnapshotRecorder"] = None @@ -170,6 +171,7 @@ class WSManager: # ── [중요] WS 는 데이터 수신용이므로 무조건 실전 서버로 접속 ── # kis_scalping_ver2 와 동일 정책 (모의 계좌라도 시세는 실전 필요) + is_mock = get_env_bool("KIS_MOCK", True) ws_app_key = get_env_from_db("KIS_APP_KEY_REAL", "") or self.kis_client.app_key ws_app_secret = get_env_from_db("KIS_APP_SECRET_REAL", "") or self.kis_client.app_secret if not ws_app_key or not ws_app_secret: @@ -188,14 +190,38 @@ class WSManager: self.candle_agg = CandleAggregator(db=self.db, timeframes=tfs) self.ws_cache.attach_candle_aggregator(self.candle_agg) - if get_env_bool("WS_TICK_SAVE_ENABLED", True): + if get_env_bool("WS_TICK_SAVE_KIS", True) or get_env_bool("WS_TICK_SAVE_KIWOOM", True): try: self.tick_recorder = TickRecorder(db=self.db) - self.ws_cache.attach_tick_recorder(self.tick_recorder) + if get_env_bool("WS_TICK_SAVE_KIS", True): + self.ws_cache.attach_tick_recorder(self.tick_recorder) except Exception as tr_ex: logger.warning("TickRecorder 기동 실패 (봉 집계만 동작): %s", tr_ex) self.tick_recorder = None + # KIS 2번째 호가 전용 웹소켓 기동 + ob_key = (get_env_from_db("KIS_APP_KEY_OB_MOCK" if is_mock else "KIS_APP_KEY_OB_REAL", "") or "").strip() + ob_secret = (get_env_from_db("KIS_APP_SECRET_OB_MOCK" if is_mock else "KIS_APP_SECRET_OB_REAL", "") or "").strip() + + if ob_key and ob_secret: + try: + logger.info(f"🔑 KIS 호가 전용(OB) WS 자격증명 로드됨 (mock={is_mock}) -> 2nd WS 연결 시도") + self.kis_ws_ob = KISWebSocketPriceCache( + ob_key, + ob_secret, + is_mock=is_mock, + ) + if not self.kis_ws_ob.start(): + logger.warning("KIS 호가 전용 WS(OB) 시작 실패 -> 메인 KIS WS로 폴백") + self.kis_ws_ob = self.ws_cache + else: + logger.info("✅ KIS 호가 전용 WS(OB) 정상 시작 완료") + except Exception as e: + logger.warning("KIS 호가 전용 WS(OB) 생성 중 오류: %s", e) + self.kis_ws_ob = self.ws_cache + else: + self.kis_ws_ob = self.ws_cache + if get_env_bool("WS_TRIGGER_EVAL_SAVE_ENABLED", True): try: self.trigger_eval_recorder = TriggerEvalRecorder(db=self.db) @@ -204,12 +230,16 @@ class WSManager: self.trigger_eval_recorder = None # 레거시: WS 3초 주기 덤프 (기본 OFF — filter_eval 판정 스냅 사용) - if get_env_bool("WS_ORDERBOOK_SAVE_ENABLED", False) or get_env_bool( + if get_env_bool("WS_ORDERBOOK_SAVE_KIWOOM", True) or get_env_bool("WS_ORDERBOOK_SAVE_KIS", False) or get_env_bool( "WS_PROGRAM_SAVE_ENABLED", False, ): try: if TriggerSnapshotRecorder is not None: self.trigger_snapshot_recorder = TriggerSnapshotRecorder(db=self.db) + # Orderbook Snapshot Recorder 부착 (호가용, kis_ws_ob 기준) + if self.trigger_snapshot_recorder and get_env_bool("WS_ORDERBOOK_SAVE_KIS", False): + self.kis_ws_ob.attach_trigger_snapshot_recorder(self.trigger_snapshot_recorder) + logger.info("✅ KIS 호가 WS(OB)에 TriggerSnapshotRecorder 부착 완료 (KIS 호가 → kis_ws_orderbook 적재)") except Exception as ts_ex: logger.warning("TriggerSnapshotRecorder 기동 실패: %s", ts_ex) self.trigger_snapshot_recorder = None @@ -272,6 +302,17 @@ class WSManager: self._kiwoom_ws = kiwoom_ws self._reattach_all_price_listeners() + if not self._split_feed_active: + with self._lock: + active_codes = list(self._code_refs.keys()) + if active_codes: + self.logger.info("🔄 [WSManager] 키움 WS 사후 주입 → 기존 KIS 구독 %d종목 키움에도 동기화", len(active_codes)) + try: + self._kiwoom_ws.subscribe_many(active_codes) + except Exception: + for code in active_codes: + self._kiwoom_ws.subscribe(code) + def activate_split_feed(self, active: bool) -> None: """``WS_SUBSCRIBE_KIS_MINIMAL`` + 키움 준비 완료 후 True → 후보 구독을 키움으로.""" self._split_feed_active = bool(active and self._kiwoom_ws and self.ws_cache) @@ -563,8 +604,9 @@ class WSManager: self._sync_tick_record_codes() def _ws_grace_sec(self) -> int: - # 후보/보유 이탈 후 틱 조금 더 쌓기 — 길면 파람이 이탈 구간 기회에 과적합되기 쉬워 30초 기본 - return max(0, get_env_int("WS_TICK_GRACE_SEC", 30)) + # 후보/보유 이탈 후에도 시세 피드 유지 (기본 180초, 3분) + # 전략 매수 검사는 Grace=0으로 즉시 중단되나, 분봉 시세는 끊김 없이 적재하여 재진입 시 갭보정 생략 & 0.001초 순간 포착 보장 (백테스트 100% 일치) + return max(0, get_env_int("WS_TICK_GRACE_SEC", 180)) def _ws_grace_headroom(self) -> int: return max(0, get_env_int("WS_TICK_GRACE_HEADROOM", 5)) @@ -708,8 +750,11 @@ class WSManager: first_ref = not self._code_refs[code] self._code_refs[code].add(owner) - if first_ref and self.ws_cache: - self.ws_cache.subscribe(code) + if first_ref: + if self.ws_cache: + self.ws_cache.subscribe(code) + if self._kiwoom_ws and not self._split_feed_active: + self._kiwoom_ws.subscribe(code) # 신규 구독 → 워커에게 갭보정 위임 (논블로킹) — 키움/KIS 경로 self._enqueue_gap_fill(code) self._sync_tick_record_codes() @@ -727,8 +772,11 @@ class WSManager: still_refs = bool(self._code_refs.get(code)) is_permanent = code in self._permanent_codes - if not still_refs and not is_permanent and self.ws_cache: - self.ws_cache.unsubscribe(code) + if not still_refs and not is_permanent: + if self.ws_cache: + self.ws_cache.unsubscribe(code) + if self._kiwoom_ws and not self._split_feed_active: + self._kiwoom_ws.unsubscribe(code) self._remove_candle_ram(code) if self.tick_recorder: self.tick_recorder.remove_code(code) @@ -779,18 +827,40 @@ class WSManager: pass else: logger.debug("LS 피드 코드이나 LS WS 없음: %s", code) - if self.ws_cache: - try: - p = self.ws_cache.get_price(code, max_age_sec=max_age_sec) - if p: - return p - except Exception: - pass - if self._split_feed_active and self._kiwoom_ws: - try: - return self._kiwoom_ws.get_price(code, max_age_sec=max_age_sec) - except Exception: - return None + from ..utils.env import get_env_from_db + live_tick_provider = (get_env_from_db("LIVE_TICK_PROVIDER", "kiwoom") or "kiwoom").strip().lower() + + if live_tick_provider == "kiwoom": + # 1. 키움 우선 + if self._kiwoom_ws: + try: + p = self._kiwoom_ws.get_price(code, max_age_sec=max_age_sec) + if p: return p + except Exception: + pass + # 2. KIS 폴백 + if self.ws_cache: + try: + p = self.ws_cache.get_price(code, max_age_sec=max_age_sec) + if p: return p + except Exception: + pass + else: + # 1. KIS 우선 + if self.ws_cache: + try: + p = self.ws_cache.get_price(code, max_age_sec=max_age_sec) + if p: return p + except Exception: + pass + # 2. 키움 폴백 + if self._kiwoom_ws: + try: + p = self._kiwoom_ws.get_price(code, max_age_sec=max_age_sec) + if p: return p + except Exception: + pass + return None def register_price_listener(self, callback) -> None: @@ -829,25 +899,107 @@ class WSManager: self._attach_price_listener(cb) def get_orderbook_snapshot(self, code: str, max_age_sec: float = 3.0): - """키움 0D 호가 스냅샷 (분리 시세·키움 WS 활성 시).""" - if self._kiwoom_ws and hasattr(self._kiwoom_ws, "get_orderbook_snapshot"): - try: - snap = self._kiwoom_ws.get_orderbook_snapshot(code, max_age_sec=max_age_sec) - if snap is not None: - return snap - except Exception: - pass + """호가 스냅샷 — ls_condition 코드는 LS UH1 RAM 우선 (키움 폴백 없음).""" + if self.is_ls_feed_code(code): + ls_ws = self._get_ls_ws() + if ls_ws is not None and hasattr(ls_ws, "get_orderbook_snapshot"): + try: + snap = ls_ws.get_orderbook_snapshot(code, max_age_sec=max_age_sec) + if snap is not None: + return snap + except Exception: + pass + return None + + from ..utils.env import get_env_from_db + live_ob_provider = (get_env_from_db("LIVE_OB_PROVIDER", "kiwoom") or "kiwoom").strip().lower() + + kis_src = self.kis_ws_ob or self.ws_cache + + if live_ob_provider == "kiwoom": + # 1. 키움 우선 + if self._kiwoom_ws and hasattr(self._kiwoom_ws, "get_orderbook_snapshot"): + try: + snap = self._kiwoom_ws.get_orderbook_snapshot(code, max_age_sec=max_age_sec) + if snap is not None: return snap + except Exception: + pass + # 2. KIS 폴백 + if kis_src and hasattr(kis_src, "get_orderbook_snapshot"): + try: + snap = kis_src.get_orderbook_snapshot(code, max_age_sec=max_age_sec) + if snap is not None: return snap + except Exception: + pass + else: + # 1. KIS 우선 + if kis_src and hasattr(kis_src, "get_orderbook_snapshot"): + try: + snap = kis_src.get_orderbook_snapshot(code, max_age_sec=max_age_sec) + if snap is not None: return snap + except Exception: + pass + # 2. 키움 폴백 + if self._kiwoom_ws and hasattr(self._kiwoom_ws, "get_orderbook_snapshot"): + try: + snap = self._kiwoom_ws.get_orderbook_snapshot(code, max_age_sec=max_age_sec) + if snap is not None: return snap + except Exception: + pass + return None def get_orderbook(self, code: str, max_age_sec: float = 3.0) -> Optional[dict]: - """호가 dict (KIS REST 호환 bidp*) — 키움 WS 캐시 우선.""" - if self._kiwoom_ws and hasattr(self._kiwoom_ws, "get_orderbook"): - try: - ob = self._kiwoom_ws.get_orderbook(code, max_age_sec=max_age_sec) - if ob: - return ob - except Exception: - pass + """호가 dict — ls_condition 코드는 LS 우선 (키움으로 메우지 않음).""" + if self.is_ls_feed_code(code): + ls_ws = self._get_ls_ws() + if ls_ws is not None: + try: + snap = ls_ws.get_orderbook_snapshot(code, max_age_sec=max_age_sec) + if snap is not None and hasattr(snap, "to_kis_bid_dict"): + return snap.to_kis_bid_dict() + if isinstance(snap, dict): + return snap + except Exception: + pass + return None + + from ..utils.env import get_env_from_db + live_ob_provider = (get_env_from_db("LIVE_OB_PROVIDER", "kiwoom") or "kiwoom").strip().lower() + + kis_src = self.kis_ws_ob or self.ws_cache + + if live_ob_provider == "kiwoom": + # 1. 키움 우선 + if self._kiwoom_ws and hasattr(self._kiwoom_ws, "get_orderbook"): + try: + ob = self._kiwoom_ws.get_orderbook(code, max_age_sec=max_age_sec) + if ob: return ob + except Exception: + pass + # 2. KIS 폴백 + if kis_src and hasattr(kis_src, "get_orderbook"): + try: + ob = kis_src.get_orderbook(code, max_age_sec=max_age_sec) + if ob: return ob + except Exception: + pass + else: + # 1. KIS 우선 + if kis_src and hasattr(kis_src, "get_orderbook"): + try: + ob = kis_src.get_orderbook(code, max_age_sec=max_age_sec) + if ob: return ob + except Exception: + pass + # 2. 키움 폴백 + if self._kiwoom_ws and hasattr(self._kiwoom_ws, "get_orderbook"): + try: + ob = self._kiwoom_ws.get_orderbook(code, max_age_sec=max_age_sec) + if ob: return ob + except Exception: + pass + return None def get_program_snapshot(self, code: str, max_age_sec: float = 30.0): @@ -1645,17 +1797,24 @@ class WSManager: def _gap_tf_already_ok(self, code: str, tf: int) -> bool: with self._gap_lock: - if tf not in self._gap_tf_ok.get(code, set()): - return False - # 1M 심층: 모멘텀 등만 500봉 미달 시 ok 해제(재갭). SHORT-only 150은 유지. - if tf == 1 and self.candle_agg and self._code_needs_deep_1m(code): - need = self._momentum_min_candles() - have = self.candle_agg.get_confirmed_count(code, 1) - if have < need: + already_ok = tf in self._gap_tf_ok.get(code, set()) + + # 🛡️ [스마트 갭보정 사전 검열] RAM 봉 실측을 통해 무지성 REST 호출 차단 + if self.candle_agg: + need = self._gap_fill_limit_for_tf(tf, code=code) + have = self.candle_agg.get_confirmed_count(code, tf) + if have >= need: + if not already_ok: + self._mark_gap_tf_ok(code, tf) + logger.debug("🛡️ [스마트갭보정] %s %dM: RAM 실측 %d봉(>=%d) 충족 → REST 무지성 호출 차단", code, tf, have, need) + return True + # 이미 ok로 찍혔어도, 모멘텀 등 500봉이 필요한 종목이 500봉 미달 시 ok 해제 및 재보정 허용 + elif already_ok and tf == 1 and self._code_needs_deep_1m(code) and have < self._momentum_min_candles(): with self._gap_lock: self._gap_tf_ok.get(code, set()).discard(1) - return False - return True + already_ok = False + + return already_ok def _mark_gap_tf_ok(self, code: str, tf: int) -> None: with self._gap_lock: diff --git a/kis_trader/strategies/base.py b/kis_trader/strategies/base.py index 725a974..9c157ab 100644 --- a/kis_trader/strategies/base.py +++ b/kis_trader/strategies/base.py @@ -26,6 +26,22 @@ from ..execution.order_manager import OrderManager from ..network.ws_manager import WSManager from ..utils.env import get_env_bool, get_env_from_db, get_env_int from ..utils.logger import get_logger +import logging + +class FeedPrefixLoggerAdapter(logging.LoggerAdapter): + def process(self, msg, kwargs): + if isinstance(msg, str) and "🔍 [" in msg and "시세:" not in msg and "LIVE_TICK_PROVIDER" in self.extra: + try: + db_obj = self.extra.get("db") + if db_obj: + tick_p = str(db_obj.get_merged_env_snapshot().get("LIVE_TICK_PROVIDER") or "kiwoom").strip().lower() + ob_p = str(db_obj.get_merged_env_snapshot().get("LIVE_OB_PROVIDER") or "kiwoom").strip().lower() + if tick_p or ob_p: + msg = msg.replace("🔍 [", f"🔍 [T:{tick_p}|O:{ob_p}|", 1) + except Exception: + pass + return msg, kwargs + # 비본주 판별 — kis_trader.utils.non_stock 공용 (KISClient 랭킹 필터와 동일) @@ -91,7 +107,8 @@ class BaseStrategy(ABC, threading.Thread): # LsConditionSearchManager (LS AFR 조건검색, 선택). 시세는 키움/한투 유지. self.ls_condition_mgr = ls_condition_mgr self.market_guard = market_guard # MarketGuard (선택, None 이면 가드 없음) - self.logger = get_logger(f"kis_trader.strategy.{self.strategy_id}") + base_logger = get_logger(f"kis_trader.strategy.{self.strategy_id}") + self.logger = FeedPrefixLoggerAdapter(base_logger, {"db": self.db, "LIVE_TICK_PROVIDER": True}) # MarketGuard PANIC 차단 로그 스팸 방지용 (분당 1회) self._panic_log_ts: float = 0.0 @@ -316,6 +333,20 @@ class BaseStrategy(ABC, threading.Thread): # 보유 목록 = DB 진실 + _runtime 오버레이 (poll 체결·재시작 정합) self._sync_holdings_from_db() + # 전략 ON/OFF 핫게이트 — WS 구독 해제 없음. 보유 청산만 유지. + if not self._strategy_switch_enabled(): + if self.holdings: + with self._sell_lock: + sell_signals = self.check_sell_signals() + if sell_signals and get_env_bool( + "REAL_BALANCE_VERIFY_BEFORE_SELL", True + ): + self.order_mgr.prefetch_broker_holdings() + for sig in sell_signals: + self._submit_sell(sig) + time.sleep(self._scan_sleep("loop")) + continue + # 일일익절 B안 — 매수루프 비어도 hit 후 보유 리스크 정리 guard = getattr(self, "daily_profit_halt", None) if guard is not None: @@ -524,13 +555,17 @@ class BaseStrategy(ABC, threading.Thread): _codes = [c for c in _codes if c] if _codes and str(getattr(self, "strategy_id", "")).upper().startswith("US_"): self.logger.info( - "🔍 [매수체크] 후보 %d (보유 %d/%d) codes=%s", + "🔍 [매수체크/T:%s|O:%s] 후보 %d (보유 %d/%d) codes=%s", + self.db.get_merged_env_snapshot().get("LIVE_TICK_PROVIDER", "kiwoom"), + self.db.get_merged_env_snapshot().get("LIVE_OB_PROVIDER", "kiwoom"), len(candidates), active_cnt, max_stocks, ",".join(_codes[:12]), ) else: self.logger.info( - "🔍 [매수체크] 후보 %d (보유 %d/%d)", + "🔍 [매수체크/T:%s|O:%s] 후보 %d (보유 %d/%d)", + self.db.get_merged_env_snapshot().get("LIVE_TICK_PROVIDER", "kiwoom"), + self.db.get_merged_env_snapshot().get("LIVE_OB_PROVIDER", "kiwoom"), len(candidates), active_cnt, max_stocks, ) for c in candidates: @@ -616,6 +651,25 @@ class BaseStrategy(ABC, threading.Thread): # ------------------------------------------------------------------ def _submit_buy(self, signal: Dict): from ..execution.order_manager import OrderRequest + import json + + code = signal["code"] + feats = signal.get("entry_features") or {} + if "_env_snapshot" not in feats: + snap = {} + try: + ob = self.ws.get_orderbook(code) + if ob: + snap = ob.copy() + except Exception: + pass + if snap: + # 불필요한 큰 필드 제거 (메모리/DB 최적화) + snap.pop("raw", None) + feats["_env_snapshot"] = json.dumps(snap, ensure_ascii=False) + else: + feats["_env_snapshot"] = "{}" + signal["entry_features"] = feats req = OrderRequest( strategy_id=self.strategy_id, @@ -1028,6 +1082,7 @@ class BaseStrategy(ABC, threading.Thread): ``kis_trader.utils.non_stock.is_non_stock`` (코드 끝 5/7/9·알파벳 + 이름). - ``{STRATEGY_ID}_CAND_LIMIT`` (기본 0=무제한): 매니저 원본 순서 보존하며 상위 N개만 사용. WS 구독 41 한도 안전 + cond/ranking 폭주 시 매수 체크 분당 회전율 보장. + (조건검색 매니저는 신규 ENTER·t1859 스냅을 앞으로 두어 캡에 신규가 안 잘리게 함) """ if not candidates: return [] @@ -1087,6 +1142,18 @@ class BaseStrategy(ABC, threading.Thread): "(history=%s 교집합, 제외 %d)", sid, before, len(candidates), hist_src, dropped, ) + # 전량 탈락 = 운영 치명 (장중만 ops_alert 세션게이트) + if before > 0 and len(candidates) == 0: + try: + from ..utils.ops_alert import ops_alert + ops_alert( + "universe_wipe", + f"{sid} 유니버스 전량탈락 {before}→0", + detail=f"history={hist_src} 교집합 제외 {dropped}", + level="critical", + ) + except Exception: + pass return candidates @@ -1108,6 +1175,28 @@ class BaseStrategy(ABC, threading.Thread): """설정 리로드 훅 (서브클래스 오버라이드).""" pass + def _strategy_switch_enabled(self) -> bool: + """``STRATEGY_{SID}_ENABLED`` — 운영설정 ON/OFF 핫게이트. + + False 여도 WS 구독은 유지(해제 안 함). 기동 시 OFF 전략은 쓰레드 자체가 없음. + """ + sid = str(getattr(self, "strategy_id", "") or "").strip().upper() + if not sid: + return True + # main._register_strategies 기본값과 동기 + defaults = { + "SCALP": True, + "SHORT": True, + "MOMENTUM": False, + "US_MOMENTUM": True, + "BREAKOUT": False, + "RANGE_BREAK": False, + "UPDOW": False, + "DBBAND": False, + "DART": False, + } + return bool(get_env_bool(f"STRATEGY_{sid}_ENABLED", defaults.get(sid, True))) + # ------------------------------------------------------------------ # 서브클래스 구현 필수 메서드 # ------------------------------------------------------------------ diff --git a/kis_trader/strategies/breakout.py b/kis_trader/strategies/breakout.py index 087aa43..22bfe95 100644 --- a/kis_trader/strategies/breakout.py +++ b/kis_trader/strategies/breakout.py @@ -984,7 +984,7 @@ def breakout_ui_to_engine_params(ui: Dict[str, Any]) -> Dict[str, Any]: return raw return str(raw).strip().lower() in ("1", "true", "t", "y", "yes", "on") - return { + ep = { "lookback_min": int(ui.get("lookback_min", 1)), "vol_window": int(ui.get("vol_window", 1)), "vol_mult": float(ui.get("vol_mult", 0) or 0), @@ -1041,8 +1041,21 @@ def breakout_ui_to_engine_params(ui: Dict[str, Any]) -> Dict[str, Any]: "skip_hts_scan_dupes", resolve_breakout_skip_hts_scan_dupes(), ), + # 수익구간 호가매도 / 손절호가 — UI% → 엔진 분율 (기본 OFF) + "exit_ob_enabled": _ui_bool("exit_ob_enabled", False), + "exit_ob_ratio_min": float(ui.get("exit_ob_ratio_min", 0.4) or 0.4), + "exit_ob_ma_window": int(float(ui.get("exit_ob_ma_window", 5) or 5)), + "exit_ob_min_profit_pct": abs(float(ui.get("exit_ob_min_profit_pct", 0.5) or 0.5)) / 100.0, + "exit_ob_min_hold_bars": int(float(ui.get("exit_ob_min_hold_bars", 3) or 3)), + "stop_ob_enabled": _ui_bool("stop_ob_enabled", False), + "stop_ob_ratio_min": float(ui.get("stop_ob_ratio_min", 0.4) or 0.4), + "stop_ob_ma_window": int(float(ui.get("stop_ob_ma_window", 5) or 5)), + "stop_ob_min_loss_pct": abs(float(ui.get("stop_ob_min_loss_pct", 0.3) or 0.3)) / 100.0, + "stop_ob_min_hold_bars": int(float(ui.get("stop_ob_min_hold_bars", 2) or 2)), } + return ep + def _breakout_params_with_ema_closes( params: Dict[str, Any], @@ -1199,7 +1212,7 @@ def check_buy_signal_breakout_live( # ════════════════════════════════════════════════════════════════════════════ # 매도 룰 모듈 함수 — 백테스트·실매 공용. 돌파 전용 우선순위: -# EOD → 익절 → 어깨컷 → 손절 → 트레일링(보조) +# EOD → 익절 → 어깨컷 → 수익구간호가매도 → 손절호가 → 손절 → 트레일링(보조) # ════════════════════════════════════════════════════════════════════════════ def check_sell_signal_breakout_live( position: Dict[str, Any], @@ -1254,6 +1267,21 @@ def check_sell_signal_breakout_live( if trail_armed and lo <= shoulder_line: return ("shoulder_cut", shoulder_line) + # 2.5·2.6: 수익구간 호가매도 / 손절호가 (기본 OFF · OR 부족 시 미발동 · OHLC 폴백 금지) + # 호가 소스: params["_ob_or_history"] (돌파=LS · 모멘텀=키움 — 주입 경로가 분리) + from kis_trader.engine.momentum_hts_logic import ( + _check_exit_ob_l3, + _check_stop_ob, + ) + hold_bars_now = _breakout_minutes_held(position, current_candle) + if hold_bars_now is None: + hold_bars_now = 0 + ob_or_history = list(params.get("_ob_or_history", []) or []) + if _check_exit_ob_l3(params, ob_or_history, entry, cl, hold_bars_now): + return ("호가컷", cl) + if _check_stop_ob(params, ob_or_history, entry, cl, hold_bars_now): + return ("손절호가", cl) + # 3순위: 손절 if lo <= sl_line: return ("stop_loss", sl_line) @@ -1410,10 +1438,28 @@ def run_breakout_backtest( collect_minute_ticks(ticks_by_code, code, c["candle_time"]) if use_tick_exit else None ) + from kis_trader.engine.momentum_hts_logic import ( + collect_exit_ob_or_history, + need_ob_or_history, + _ob_or_ma_window_for_history, + ) + sell_params = dict(params) + if need_ob_or_history(params) and orderbook_by_code: + ors = collect_exit_ob_or_history( + orderbook_by_code, + code, + entry_time=str(position.get("entry_time") or ""), + asof_time=c["candle_time"], + ma_window=_ob_or_ma_window_for_history(params), + ) + position["_ob_or_history"] = ors + sell_params["_ob_or_history"] = list(ors) + else: + sell_params["_ob_or_history"] = list(position.get("_ob_or_history") or []) rp = resolve_backtest_sell( position, bar, - params, + sell_params, is_eod=is_eod, sell_fn=check_sell_signal_breakout_live, low_mode="current", @@ -1585,6 +1631,17 @@ class BreakoutStrategy(BaseStrategy): self.atr_sl_min_pct = get_env_float("BREAKOUT_ATR_SL_MIN_PCT", 0.8) self.atr_sl_max_pct = get_env_float("BREAKOUT_ATR_SL_MAX_PCT", 6.0) self.skip_hts_scan_dupes = resolve_breakout_skip_hts_scan_dupes() + # 수익구간 호가매도 / 손절호가 (기본 OFF · LS WS 호가 OR) + self.exit_ob_enabled = get_env_bool("BREAKOUT_EXIT_OB_ENABLED", False) + self.exit_ob_ratio_min = get_env_float("BREAKOUT_EXIT_OB_RATIO_MIN", 0.4) + self.exit_ob_ma_window = get_env_int("BREAKOUT_EXIT_OB_MA_WINDOW", 5) + self.exit_ob_min_profit_pct = get_env_float("BREAKOUT_EXIT_OB_MIN_PROFIT_PCT", 0.005) + self.exit_ob_min_hold_bars = get_env_int("BREAKOUT_EXIT_OB_MIN_HOLD_BARS", 3) + self.stop_ob_enabled = get_env_bool("BREAKOUT_STOP_OB_ENABLED", False) + self.stop_ob_ratio_min = get_env_float("BREAKOUT_STOP_OB_RATIO_MIN", 0.4) + self.stop_ob_ma_window = get_env_int("BREAKOUT_STOP_OB_MA_WINDOW", 5) + self.stop_ob_min_loss_pct = get_env_float("BREAKOUT_STOP_OB_MIN_LOSS_PCT", 0.003) + self.stop_ob_min_hold_bars = get_env_int("BREAKOUT_STOP_OB_MIN_HOLD_BARS", 2) def _breakout_invest_cap_krw(self) -> float: max_loss = normalize_breakout_max_loss_krw( @@ -1959,7 +2016,35 @@ class BreakoutStrategy(BaseStrategy): "atr_sl_mult": self.atr_sl_mult, "atr_sl_min_pct": self.atr_sl_min_pct, "atr_sl_max_pct": self.atr_sl_max_pct, + "exit_ob_enabled": self.exit_ob_enabled, + "exit_ob_ratio_min": self.exit_ob_ratio_min, + "exit_ob_ma_window": self.exit_ob_ma_window, + "exit_ob_min_profit_pct": self.exit_ob_min_profit_pct, + "exit_ob_min_hold_bars": self.exit_ob_min_hold_bars, + "stop_ob_enabled": self.stop_ob_enabled, + "stop_ob_ratio_min": self.stop_ob_ratio_min, + "stop_ob_ma_window": self.stop_ob_ma_window, + "stop_ob_min_loss_pct": self.stop_ob_min_loss_pct, + "stop_ob_min_hold_bars": self.stop_ob_min_hold_bars, } + # 수익구간·손절호가: 전략 WS(돌파=LS) 호가 OR — 둘 중 ON일 때만 + from kis_trader.engine.momentum_hts_logic import ( + append_live_exit_ob_or, + need_ob_or_history, + _ob_or_ma_window_for_history, + ) + if need_ob_or_history(sell_params): + hist = list(holding.get("_ob_or_history") or []) + hist = append_live_exit_ob_or( + hist, + self.ws, + code, + ma_window=_ob_or_ma_window_for_history(sell_params), + ) + holding["_ob_or_history"] = hist + sell_params["_ob_or_history"] = list(hist) + else: + sell_params["_ob_or_history"] = [] # 진입 변동성(ATR): 매수 시 저장값 우선, 없으면 1회 lazy 계산 후 holding 캐시. # sl_mode='fixed' 면 계산 자체를 건너뛴다(불필요한 캔들 조회 방지). entry_atr = float(holding.get("entry_atr") or holding.get("atr_entry") or 0.0) diff --git a/kis_trader/strategies/momentum.py b/kis_trader/strategies/momentum.py index 5ffe739..d25bd5a 100644 --- a/kis_trader/strategies/momentum.py +++ b/kis_trader/strategies/momentum.py @@ -271,7 +271,7 @@ class MomentumStrategy(BaseStrategy): now, default_hm="15:20", ) - params = dict(self._engine_params or me.get_momentum_defaults_from_db()) + params_base = dict(self._engine_params or me.get_momentum_defaults_from_db()) for code, holding in list(self.holdings.items()): try: @@ -326,6 +326,27 @@ class MomentumStrategy(BaseStrategy): "candle_time": now.strftime("%Y%m%d%H%M"), } + params = dict(params_base) + # 수익구간·손절호가: 실매 호가 OR 히스토리 (둘 중 하나 ON일 때만 · 기본 OFF) + from kis_trader.engine.momentum_hts_logic import ( + append_live_exit_ob_or, + need_ob_or_history, + _ob_or_ma_window_for_history, + ) + + if need_ob_or_history(params): + hist = list(holding.get("_ob_or_history") or []) + hist = append_live_exit_ob_or( + hist, + self.ws, + code, + ma_window=_ob_or_ma_window_for_history(params), + ) + holding["_ob_or_history"] = hist + params["_ob_or_history"] = list(hist) + else: + params["_ob_or_history"] = [] + res = me.check_sell_signal_momentum_live(position, candle, params, is_eod=is_eod) if not res: continue diff --git a/kis_trader/strategies/scalping.py b/kis_trader/strategies/scalping.py index 98b1984..7b95508 100644 --- a/kis_trader/strategies/scalping.py +++ b/kis_trader/strategies/scalping.py @@ -195,6 +195,16 @@ class ScalpingStrategy(BaseStrategy): state = {"last_exit_dt": last_exit_dt, "daily_cnt": daily_cnt} # SCALP 는 reversal 단일 모드 (momentum 은 MomentumStrategy 로 분리됨) + # 호가/프로그램/휩쏘 — 모멘텀·꼬리·돌파와 동일하게 WS 주입 + # (미주입 시 orderbook_reject 가 스냅 None → 수집·필터 둘 다 스킵) + params = dict(params or {}) + params["_whipsaw_ws"] = self.ws + params["_whipsaw_code"] = code + params["_orderbook_ws"] = self.ws + params["_orderbook_code"] = code + params["_program_ws"] = self.ws + params["_program_code"] = code + params["slot_money"] = self.slot_money reject, msg, sig = se.check_buy_signal_live(candles, params, state) if reject: self.logger.info("🔍 [%s] %s %s: %s", reject, name, code, msg or "") diff --git a/kis_trader/utils/live_portfolio_common.py b/kis_trader/utils/live_portfolio_common.py index 60882d8..6945416 100644 --- a/kis_trader/utils/live_portfolio_common.py +++ b/kis_trader/utils/live_portfolio_common.py @@ -37,13 +37,18 @@ def _env_flag(key: str, default: bool) -> bool: def live_universe_slot_align_enabled(strategy_id: str) -> bool: + """실매 후보 ∩ history 슬롯 필터. + + 기본 **False** — history 지연/축소 시 전량탈락(universe_wipe) 방지. + 백테 슬롯 정합이 필요하면 ``LIVE_UNIVERSE_SLOT_ALIGN=true`` 또는 전략별 키로 ON. + """ sid = (strategy_id or "").upper() per_key = f"{sid}_LIVE_UNIVERSE_SLOT_ALIGN" if get_env_from_db(per_key, "") not in (None, "", "None"): - return _env_flag(per_key, True) + return _env_flag(per_key, False) if sid in _UNIVERSE_SLOT_SKIP_DEFAULT: return False - return _env_flag("LIVE_UNIVERSE_SLOT_ALIGN", True) + return _env_flag("LIVE_UNIVERSE_SLOT_ALIGN", False) def resolve_live_universe_history_source( @@ -144,6 +149,59 @@ def resolve_live_min_invest_ratio(strategy_id: str) -> float: return min_invest_ratio_of_slot({}, strategy=portfolio_strategy_key(strategy_id)) +def _history_snapshot_event_time( + db: Any, + strategy_id: str, + at_time: str, + history_source: str, +) -> Optional[Any]: + """``at_time`` 이전 최신 ``event_time`` (없으면 None).""" + conn = getattr(db, "conn", None) + if conn is None: + return None + table_fn = getattr(db, "_universe_history_table", None) + if callable(table_fn): + try: + table = table_fn(history_source) + except Exception: + table = None + else: + table = None + if not table: + from kis_trader.backtest.universe_history_source import history_table_for_source + table = history_table_for_source(history_source) + try: + row = conn.execute( + f""" + SELECT MAX(event_time) AS et + FROM {table} + WHERE strategy_id=%s AND event_time <= %s + """, + (strategy_id, at_time), + ).fetchone() + return (row or {}).get("et") if row else None + except Exception: + return None + + +def _event_time_ymd(et: Any) -> str: + if et is None: + return "" + if hasattr(et, "strftime"): + try: + return et.strftime("%Y-%m-%d") + except Exception: + pass + s = str(et).strip() + if len(s) >= 10 and s[4] == "-" and s[7] == "-": + return s[:10] + # YYYYMMDDHHMM / YYYYMMDD... + digits = "".join(ch for ch in s if ch.isdigit()) + if len(digits) >= 8: + return f"{digits[:4]}-{digits[4:6]}-{digits[6:8]}" + return "" + + def history_universe_codes_at( db: Any, strategy_id: str, @@ -157,6 +215,9 @@ def history_universe_codes_at( - kiwoom → ``target_candidates_history`` - ls → ``ls_candidates_history`` 스냅샷 없으면 ``None`` (필터 생략 = 실시간 후보 유지). + + **당일 스냅샷만** 사용한다. 주말 재시작·키움 매니저 다운 뒤 남은 + 며칠 전 history 와 sticky 후보를 교집합하면 전원 탈락(11→0) 한다. """ when = when or dt.now() at_time = when.strftime("%Y-%m-%d %H:%M:%S") @@ -170,6 +231,14 @@ def history_universe_codes_at( src = resolve_live_universe_history_source(sid, universe_source=None) if src not in ("ls", "kiwoom"): src = "kiwoom" + + # 당일(캘린더) 스냅샷만 슬롯정합에 쓴다 — 낡은 history 전멸 방지 + et = _history_snapshot_event_time(db, sid, at_time, src) + if not et: + return None + if _event_time_ymd(et) != when.strftime("%Y-%m-%d"): + return None + getter = getattr(db, "get_universe_at", None) if getter is None: return None diff --git a/kis_trader/utils/ls_ws_session_windows.py b/kis_trader/utils/ls_ws_session_windows.py new file mode 100644 index 0000000..16ff11d --- /dev/null +++ b/kis_trader/utils/ls_ws_session_windows.py @@ -0,0 +1,170 @@ +""" +LS WebSocket 세션 시간 분할 (국내 ↔ 해외) — 벽시계 기준 +====================================================== +LS 는 KIS approval 과 다름: + - 접근토큰 1개 + WS URL 1개로 국내(US3) · 해외(GSC) 동시 구독 가능 + - 세션 전환 시 **토큰 재발급 금지** (재발급하면 조건검색 REST·WS 공용 토큰이 무효화됨) + - 이 모듈은 **소켓 hold / 대기 / 워치독 게이트** 만 담당 (oauth 호출 없음) + +운용 (KIS ``kis_ws_session_windows`` 와 동일 시각 철학): + - 국내 hold: 기본 07:00~20:00 (장 전후 여유) — 소켓 유지·국장 워치독은 별도 정규창 + - 해외 hold: 기본 21:00~06:00 — 해외 구독이 있을 때만 소켓 유지 사유 + - 갭(20:00~21:00, 06:00~07:00): 소켓 close 후 대기 (**재발급 없음**) + +env (DB 등록): + LS_WS_KR_HOLD_START_HM / LS_WS_KR_HOLD_END_HM (기본 700 / 2000) + LS_WS_US_HOLD_START_HM / LS_WS_US_HOLD_END_HM (기본 2100 / 600) + LS_WS_SESSION_GUARD_SEC +""" +from __future__ import annotations + +import datetime as _dt +from typing import Optional, Tuple + +from .env import get_env_float, get_env_int +from .session_hm import hm_in_trading_window + + +def kr_ws_hold_bounds() -> Tuple[int, int]: + """국내 LS WS 세션 점유 HHMM (당일 구간). 기본 0700~2000.""" + start = int(get_env_int("LS_WS_KR_HOLD_START_HM", 700) or 700) + end = int(get_env_int("LS_WS_KR_HOLD_END_HM", 2000) or 2000) + if start <= 0: + start = 700 + if end <= 0: + end = 2000 + return start, end + + +def us_ws_hold_bounds() -> Tuple[int, int]: + """해외 LS WS 세션 점유 HHMM (자정 넘김). 기본 2100~0600.""" + start = int(get_env_int("LS_WS_US_HOLD_START_HM", 2100) or 2100) + end = int(get_env_int("LS_WS_US_HOLD_END_HM", 600) or 600) + if start <= 0: + start = 2100 + if end <= 0: + end = 600 + return start, end + + +def session_guard_interval_sec() -> float: + """연결 유지 중 hold 창 이탈 감시 주기(초).""" + return max(5.0, float(get_env_float("LS_WS_SESSION_GUARD_SEC", 15.0) or 15.0)) + + +def _hm_now(now: Optional[_dt.datetime] = None) -> int: + n = now or _dt.datetime.now() + return int(n.hour * 100 + n.minute) + + +def in_kr_ws_hold_window(now: Optional[_dt.datetime] = None) -> bool: + """True = 국내 LS WS 소켓을 유지해도 되는 시간 (월~금).""" + n = now or _dt.datetime.now() + if n.weekday() >= 5: + return False + start, end = kr_ws_hold_bounds() + return hm_in_trading_window(_hm_now(n), start, end, wrap_midnight=False) + + +def in_us_ws_hold_window(now: Optional[_dt.datetime] = None) -> bool: + """ + True = 해외 LS WS 소켓 유지 사유가 되는 시간. + - start~23:59: 월~금 + - 00:00~end: 화~토 (미국장 새벽 마감) + """ + n = now or _dt.datetime.now() + wd = n.weekday() + hm = _hm_now(n) + start, end = us_ws_hold_bounds() + if start > end: + if hm >= start: + return 0 <= wd <= 4 + if hm < end: + return 1 <= wd <= 5 + return False + if n.weekday() >= 5: + return False + return hm_in_trading_window(hm, start, end, wrap_midnight=False) + + +def should_hold_ls_socket( + *, + n_us_subscribed: int = 0, + now: Optional[_dt.datetime] = None, +) -> bool: + """소켓을 열어 둘지. 해외 hold 는 해외 구독이 있을 때만.""" + n = now or _dt.datetime.now() + if in_kr_ws_hold_window(n): + return True + if int(n_us_subscribed or 0) > 0 and in_us_ws_hold_window(n): + return True + return False + + +def seconds_until_kr_ws_open(now: Optional[_dt.datetime] = None) -> float: + """다음 국내 hold 시작까지 초 (최소 60). hold 중이면 짧은 쿨다운.""" + n = now or _dt.datetime.now() + if in_kr_ws_hold_window(n): + return max(30.0, float(get_env_float("LS_WS_HOLD_INNER_COOLDOWN_SEC", 90.0) or 90.0)) + start, _end = kr_ws_hold_bounds() + sh, sm = divmod(int(start), 100) + hm = _hm_now(n) + target = n.replace(hour=sh, minute=sm, second=0, microsecond=0) + if n.weekday() >= 5: + days = (0 - n.weekday()) % 7 + if days == 0: + days = 7 + target += _dt.timedelta(days=days) + elif hm >= start: + target += _dt.timedelta(days=1) + while target.weekday() >= 5: + target += _dt.timedelta(days=1) + return max(60.0, (target - n).total_seconds()) + + +def seconds_until_us_ws_open(now: Optional[_dt.datetime] = None) -> float: + """다음 해외 hold 시작까지 초 (최소 60). hold 중이면 짧은 쿨다운.""" + n = now or _dt.datetime.now() + if in_us_ws_hold_window(n): + return max(30.0, float(get_env_float("LS_WS_HOLD_INNER_COOLDOWN_SEC", 90.0) or 90.0)) + start, end = us_ws_hold_bounds() + sh, sm = divmod(int(start), 100) + hm = _hm_now(n) + target = n.replace(hour=sh, minute=sm, second=0, microsecond=0) + if start > end: + if end <= hm < start: + if n.weekday() >= 5: + days = (0 - n.weekday()) % 7 + if days == 0: + days = 7 + target += _dt.timedelta(days=days) + elif hm >= start: + target += _dt.timedelta(days=1) + while target.weekday() >= 5: + target += _dt.timedelta(days=1) + else: + if n.weekday() == 6: + target += _dt.timedelta(days=1) + while target.weekday() >= 5: + target += _dt.timedelta(days=1) + else: + if hm >= start or n.weekday() >= 5: + target += _dt.timedelta(days=1) + while target.weekday() >= 5: + target += _dt.timedelta(days=1) + return max(60.0, (target - n).total_seconds()) + + +def seconds_until_ls_socket_open( + *, + n_us_subscribed: int = 0, + now: Optional[_dt.datetime] = None, +) -> float: + """다음 소켓 hold 시작까지 초. 해외 구독 없으면 국내만.""" + n = now or _dt.datetime.now() + if should_hold_ls_socket(n_us_subscribed=n_us_subscribed, now=n): + return max(30.0, float(get_env_float("LS_WS_HOLD_INNER_COOLDOWN_SEC", 90.0) or 90.0)) + wait = seconds_until_kr_ws_open(n) + if int(n_us_subscribed or 0) > 0: + wait = min(wait, seconds_until_us_ws_open(n)) + return float(wait) diff --git a/kis_trader/utils/ops_alert.py b/kis_trader/utils/ops_alert.py new file mode 100644 index 0000000..0236dd9 --- /dev/null +++ b/kis_trader/utils/ops_alert.py @@ -0,0 +1,170 @@ +""" +운영 치명 알림 — 예외 삼킴/로그만 하던 인프라 장애를 MM(+선택 TG)로 올린다. + +- 체결·기동 알림과 분리. 채널 기본 = KIS_SYSTEM_MM_CHANNEL. +- 코드별 쿨다운 + 전역 최소간격으로 스팸 방지. +- 장중 전용 코드는 session gate (주말·장외 오탐 완화). +""" +from __future__ import annotations + +import threading +import time +from datetime import datetime +from pathlib import Path +from typing import Any, Dict, Optional + +from .env import get_env_bool, get_env_from_db, get_env_int +from .logger import atomic_load_json, atomic_save_json, get_logger, msg_mm, msg_tg + +logger = get_logger("kis_trader.ops_alert") + +_ROOT = Path(__file__).resolve().parents[2] +_STATE_PATH = _ROOT / "logs" / "ops_alert_state.json" +_LOCK = threading.Lock() + +# 장중 세션에서만 의미 있는 코드 (토큰·PANIC·VI 는 상시) +_SESSION_CODES = frozenset({ + "ws_kis_down", + "ws_kiwoom_down", + "ws_ls_down", + "ws_tick_silence", + "universe_zero", + "universe_wipe", + "history_stale", + "kwcond_off", + "order_buy_reject", + "order_sell_reject", + "rate_limit", +}) + + +def _channel() -> str: + ch = (get_env_from_db("OPS_ALERT_MM_CHANNEL", "") or "").strip() + if not ch: + ch = (get_env_from_db("KIS_SYSTEM_MM_CHANNEL", "default") or "default").strip() + return ch or "default" + + +def _cooldown_sec(code: str) -> int: + per = get_env_int(f"OPS_ALERT_COOLDOWN_{code.upper()}_SEC", 0) + if per > 0: + return max(30, int(per)) + return max(30, get_env_int("OPS_ALERT_COOLDOWN_SEC", 300)) + + +def _in_kr_session(now: Optional[datetime] = None) -> bool: + now = now or datetime.now() + if now.weekday() >= 5: + return False + hm = now.hour * 100 + now.minute + start = int(get_env_int("OPS_ALERT_SESSION_START_HM", 900) or 900) + end = int(get_env_int("OPS_ALERT_SESSION_END_HM", 1535) or 1535) + return start <= hm <= end + + +def _load_state() -> Dict[str, Any]: + st = atomic_load_json(_STATE_PATH, default={}) + return st if isinstance(st, dict) else {} + + +def _save_state(st: Dict[str, Any]) -> None: + try: + _STATE_PATH.parent.mkdir(parents=True, exist_ok=True) + atomic_save_json(_STATE_PATH, st) + except Exception as e: + logger.debug("ops_alert state save: %s", e) + + +def ops_alert( + code: str, + title: str, + *, + detail: str = "", + level: str = "critical", + force: bool = False, + session_only: Optional[bool] = None, +) -> bool: + """운영 알림 1건. True=발송됨. + + ``level``: critical | warn + ``session_only``: None 이면 코드 기본(장중 전용 집합), True/False 강제. + """ + if not get_env_bool("OPS_ALERT_ENABLED", True): + return False + code = str(code or "").strip().lower() or "unknown" + title = str(title or "").strip() or code + level = str(level or "critical").strip().lower() + if level not in ("critical", "warn"): + level = "critical" + + need_session = ( + bool(session_only) if session_only is not None + else (code in _SESSION_CODES) + ) + if need_session and not _in_kr_session(): + return False + + now = time.time() + with _LOCK: + st = _load_state() + by_code = st.setdefault("by_code", {}) + if not isinstance(by_code, dict): + by_code = {} + st["by_code"] = by_code + last = float(by_code.get(code) or 0.0) + cd = _cooldown_sec(code) + if not force and (now - last) < cd: + return False + global_gap = float(get_env_int("OPS_ALERT_GLOBAL_MIN_GAP_SEC", 20) or 20) + last_any = float(st.get("last_any_ts") or 0.0) + if not force and (now - last_any) < global_gap: + return False + + icon = "🚨" if level == "critical" else "⚠️" + lines = [ + f"{icon} **[운영알림/{level.upper()}] {title}**", + f"- 코드: `{code}`", + f"- 시각: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}", + ] + if detail: + clipped = str(detail).strip() + if len(clipped) > 1200: + clipped = clipped[:1200] + "…" + lines.append(f"- 상세: {clipped}") + body = "\n".join(lines) + + ok = False + try: + ok = bool(msg_mm(body, channel_alias=_channel(), jitter=False)) + except Exception as e: + logger.debug("ops_alert MM 실패: %s", e) + if level == "critical" and get_env_bool("OPS_ALERT_TG_ON_CRITICAL", True): + try: + msg_tg(body, jitter=False) + except Exception as e: + logger.debug("ops_alert TG 실패: %s", e) + + by_code[code] = now + st["last_any_ts"] = now + st["last_code"] = code + _save_state(st) + logger.warning("[ops_alert] sent code=%s ok_mm=%s title=%s", code, ok, title) + return ok + + +def note_counter(key: str, *, reset: bool = False) -> int: + """연속 실패 카운터 (주문거부·유량 등). reset=True 이면 0.""" + with _LOCK: + st = _load_state() + counters = st.setdefault("counters", {}) + if not isinstance(counters, dict): + counters = {} + st["counters"] = counters + if reset: + counters[key] = 0 + _save_state(st) + return 0 + n = int(counters.get(key) or 0) + 1 + counters[key] = n + _save_state(st) + return n diff --git a/kis_trader/utils/request_handler.py b/kis_trader/utils/request_handler.py index 5ec6da6..4b99522 100644 --- a/kis_trader/utils/request_handler.py +++ b/kis_trader/utils/request_handler.py @@ -163,6 +163,21 @@ class SafeRequest: if resp.status_code in self.RETRYABLE_STATUSES: if resp.status_code == 429: self._rate_limit_hits += 1 + try: + from kis_trader.utils.ops_alert import note_counter, ops_alert + from kis_trader.utils.env import get_env_int + streak = note_counter("rate_limit") + need = max(1, get_env_int("OPS_ALERT_RATE_LIMIT_STREAK", 5)) + if streak >= need: + ops_alert( + "rate_limit", + f"HTTP 429 유량 연속 {streak}회", + detail=f"{method.upper()} {url}", + level="critical", + ) + note_counter("rate_limit", reset=True) + except Exception: + pass logger.warning( "HTTP %d on %s %s (%d/%d) → 백오프 후 재시도", resp.status_code, method.upper(), url, attempt, self.max_retries, @@ -178,6 +193,21 @@ class SafeRequest: body = None if body and self._is_kis_rate_limited(body): self._rate_limit_hits += 1 + try: + from kis_trader.utils.ops_alert import note_counter, ops_alert + from kis_trader.utils.env import get_env_int + streak = note_counter("rate_limit") + need = max(1, get_env_int("OPS_ALERT_RATE_LIMIT_STREAK", 5)) + if streak >= need: + ops_alert( + "rate_limit", + f"KIS 유량초과 연속 {streak}회", + detail=f"{body.get('msg_cd')} {url}", + level="critical", + ) + note_counter("rate_limit", reset=True) + except Exception: + pass logger.warning( "KIS rate-limit %s on %s (%d/%d) → 백오프 후 재시도", body.get("msg_cd"), url, attempt, self.max_retries, diff --git a/kis_trader/web/live_config_schema.py b/kis_trader/web/live_config_schema.py index f80f083..224e122 100644 --- a/kis_trader/web/live_config_schema.py +++ b/kis_trader/web/live_config_schema.py @@ -11,6 +11,7 @@ from typing import Any, Dict, List, Optional, Tuple # 웹 탭에 노출할 전략 ID (kis_trader 봇) # ※ SCALP·RANGE_BREAK·DBBAND 는 사용자 지정으로 숨김 (strategy_ids.HIDDEN_STRATEGY_IDS) # → 운영설정 ON/OFF·운용한도·일일익절·휩쏘 폼에서 제외. 실거래 중인 SHORT·BREAKOUT·MOMENTUM + UPDOW 만 노출. +# ※ US_MOMENTUM 은 전략 ON/OFF 에만 노출 (유니버스/KRW 한도 폼은 국내와 축이 다름). LIVE_STRATEGY_IDS: Tuple[str, ...] = ( "SHORT", "MOMENTUM", @@ -18,10 +19,20 @@ LIVE_STRATEGY_IDS: Tuple[str, ...] = ( "UPDOW", ) +# 전략 ON/OFF 전용 — 해외를 맨 위 +LIVE_STRATEGY_SWITCH_IDS: Tuple[str, ...] = ( + "US_MOMENTUM", + "SHORT", + "MOMENTUM", + "BREAKOUT", + "UPDOW", +) + LIVE_STRATEGY_LABELS: Dict[str, str] = { "SCALP": "스캘핑", "SHORT": "꼬리잡기", "MOMENTUM": "모멘텀", + "US_MOMENTUM": "해외 모멘텀", "BREAKOUT": "돌파", "RANGE_BREAK": "박스권돌파", "UPDOW": "UPDOWN 박스", @@ -29,6 +40,19 @@ LIVE_STRATEGY_LABELS: Dict[str, str] = { "DART": "DART 수주", } +# 기동 시 _register_strategies 기본값과 동일 (핫게이트 폴백) +STRATEGY_ENABLED_DEFAULTS: Dict[str, bool] = { + "SCALP": True, + "SHORT": True, + "MOMENTUM": False, + "US_MOMENTUM": True, + "BREAKOUT": False, + "RANGE_BREAK": False, + "UPDOW": False, + "DBBAND": False, + "DART": False, +} + # 전략 ID → 실제 env 키 (SHORT 는 TAIL_* 가 canonical — tail_engine·config_short) STRATEGY_BUDGET_ENV_KEY: Dict[str, Dict[str, str]] = { "SHORT": { @@ -156,68 +180,227 @@ def build_live_config_groups() -> List[GroupDef]: ], }, { - "id": "ws_trigger_global", - "title": "TRIGGER 필터 — WS 공통", + "id": "ws_feed_settings", + "title": "📡 시세 소스 설정 (봇 재시작 필요)", "hint": ( - "호가(0D)·프로그램(0w) · env_config(컬럼 없으면 env_config_ext) · " - "COLLECT=판정순간1장(filter_eval) / SAVE=후보~3초시계열(kiwoom_0d) — 둘은 다름" + "키움과 KIS 중 어느 쪽 시세·호가로 매매할지 선택. " + "어느 쪽을 선택하든 틱·캔들 데이터는 키움+KIS 양쪽 모두 DB에 저장됨. " + "시세/호가만 선택한 증권사를 사용." + ), + "fields": [ + _f("LIVE_TICK_PROVIDER", "📊 실매매 틱(시세) 소스 (재시작 필요)", "text", default="kiwoom", + hint=( + "kiwoom (권장) = 키움증권 실시간 체결가 사용 | " + "kis = 한국투자증권 실시간 체결가 사용" + )), + _f("LIVE_OB_PROVIDER", "📊 실매매 호가(OB) 소스 (재시작 필요)", "text", default="kiwoom", + hint=( + "kiwoom (권장) = 키움증권 10단계 호가 사용 | " + "kis = 한국투자증권 10단계 호가 사용 (kis 선택 시 아래 'KIS 실시간 호가 수집' 스위치도 ON이어야 합니다)" + )), + _f("WS_TICK_SAVE_KIWOOM", "📈 키움 체결 틱 수집", "bool", default=True, + hint="ON = 키움 체결 틱을 ws_ticks 테이블에 저장 (기본값: ON)"), + _f("WS_ORDERBOOK_SAVE_KIWOOM", "📋 키움 실시간 호가 수집", "bool", default=True, + hint="ON = 키움 호가를 ws_orderbook 테이블에 저장 (기본값: ON)"), + _f("WS_TICK_SAVE_KIS", "📈 KIS 체결 틱 수집", "bool", default=True, + hint="ON = KIS 체결 틱을 ws_ticks 테이블에 저장 (기본값: ON)"), + _f("WS_ORDERBOOK_SAVE_KIS", "📋 KIS 실시간 호가 수집", "bool", default=False, + hint=( + "OFF(기본) = KIS 호가는 수집 안 함 (한도 40개 보호) | " + "ON = KIS 호가도 kis_ws_orderbook 테이블에 저장. " + "단, KIS는 시세+호가 합산 40개 한도라서 ON 시 최대 20종목만 추적 가능" + )), + _f("WS_SUBSCRIBE_KIS_MINIMAL", "📉 KIS 최소 구독 모드", "bool", default=False, + hint=( + "OFF(기본) = KIS가 키움과 동일한 후보 40종목을 전부 수신하여 DB에 저장 | " + "ON = KIS가 현재 내가 사고 있는 종목만 수신 (KIS ID 한도 아낄 때)" + )), + _f("KIS_APP_KEY_OB_REAL", "🔑 KIS 실전 호가 전용 앱키 (선택)", "text", + hint="한투 40종목 구독 한도 우회를 위한 2번째 실전 앱키. 미입력 시 기존 앱키로 호가까지 공용 구독합니다."), + _f("KIS_APP_SECRET_OB_REAL", "🔑 KIS 실전 호가 전용 시크릿 (선택)", "text", + hint="호가 전용 2번째 실전 시크릿키."), + _f("KIS_APP_KEY_OB_MOCK", "🔑 KIS 모의 호가 전용 앱키 (선택)", "text", + hint="모의투자용 호가 전용 2번째 앱키."), + _f("KIS_APP_SECRET_OB_MOCK", "🔑 KIS 모의 호가 전용 시크릿 (선택)", "text", + hint="모의투자용 호가 전용 2번째 시크릿키."), + _f("LIVE_VALIDATOR_ENABLED", "🔬 시세 무결성 검증기 기동 (재시작 필요)", "bool", default=False, + hint="ON = 실매매 중 키움/KIS 시세 교차 검증 데몬을 백그라운드에 함께 기동 (기본값: OFF)"), + ], + }, + { + "id": "ws_trigger_global", + "title": "⚡ 매수 판단 필터 설정 (실시간 WS 기반)", + "hint": ( + "매수 타이밍을 결정할 때 호가·프로그램 데이터를 어떻게 사용할지 설정. " + "모든 데이터는 실시간 메모리(RAM)에서 직접 읽으므로 딜레이 없음." ), "fields": [ - _f("KIWOOM_WS_ORDERBOOK_ENABLED", "호가 WS(0D) 구독", "bool", default=True, - hint="키움 실시간 호가 수신(RAM) · 꺼면 필터·수집·시계열 모두 불가"), - _f("KIWOOM_WS_PROGRAM_ENABLED", "프로그램 WS(0w) 구독", "bool", default=True, - hint="키움 프로그램매매 수신(RAM)"), _f( - "WS_TRIGGER_EVAL_SAVE_ENABLED", - "TRIGGER 판정 DB 저장(마스터)", + "KIWOOM_WS_PROGRAM_ENABLED", + "🤖 키움 프로그램 매매 데이터 수신", "bool", default=True, - hint="OFF면 아래 COLLECT(판정 스냅) 전부 안 씀 · SAVE(시계열)와 무관", + hint=( + "ON = 기관·외국인 프로그램 매매 흐름을 메모리에 저장 | " + "OFF = 프로그램 매매 필터 사용 불가" + ), + ), + _f( + "WS_TRIGGER_EVAL_SAVE_ENABLED", + "💾 매수 판단 결과 DB 저장 (마스터 스위치)", + "bool", + default=True, + hint=( + "ON = 매수 검토할 때 호가·프로그램 스냅샷을 DB에 기록 (분석·디버깅용) | " + "OFF = 아래 모든 저장 비활성 (DB 용량 아낄 때)" + ), + ), + + _f( + "WS_ORDERBOOK_TICK_MAX_AGE_SEC", + "⏱ 호가 유효 시간(초)", + "float", + default=3.0, + hint=( + "틱이 들어왔을 때 호가 데이터가 이 시간(초)보다 오래됐으면 저장 안 함 | " + "→ 오래된 호가로 가짜 데이터 채우는 것 방지. 기본 3초" + ), ), _f( "WS_ORDERBOOK_COLLECT_ENABLED", - "호가 판정스냅(FILTER_EVAL) — 매수체크 순간 1장", + "📸 매수 검토 순간 호가창 1장 저장", "bool", default=True, - hint="≠ 연속시계열. TRIGGER 판정 때만 ws_orderbook source=filter_eval · 필터 OFF여도 저장 가능", + hint=( + "ON = 매수 검토 순간 호가창(10호가 잔량 등) 스냅샷도 DB에 저장 | " + "→ 매수/거절 당시의 호가 상태 정밀 분석용 (용량 주의)" + ), ), - _f( - "WS_ORDERBOOK_SAVE_ENABLED", - "호가 시계열(KIWOOM_0D) — 구독후보 ~3초 덤프", - "bool", - default=False, - hint="틱처럼 후보·보유만 연속 저장(source=kiwoom_0d) · 파람서치 호가축용 · 봇 재시작 필요 · 필터와 무관", - ), - _f( - "WS_TRIGGER_SNAPSHOT_DB_INTERVAL_SEC", - "호가/프로그램 시계열 간격(초)", - "float", - default=3.0, - hint="SAVE ON일 때만 · 종목당 최소 저장 간격 · 봇 재시작 필요", - ), - # 글로벌 ORDERBOOK_FILTER / MAX_SPREAD 폐기 → 아래「호가필터 — 전략별」 _f( "WS_PROGRAM_COLLECT_ENABLED", - "프로그램 판정스냅 — 매수체크 순간 1장", + "📸 매수 검토 순간 프로그램 매매 1장 저장", "bool", default=True, - hint="≠ 연속시계열. 판정 때만 ws_program · 필터 OFF여도 가능", + hint=( + "ON = 매수 검토 순간 프로그램 매매 스냅샷도 DB에 저장 | " + "→ 호가 저장과 동일 개념, 프로그램 필터 분석용" + ), ), _f( "WS_PROGRAM_SAVE_ENABLED", - "프로그램 시계열 — 구독후보 ~3초 덤프", + "📼 프로그램 매매 연속 시계열 저장", "bool", default=False, - hint="SAVE와 동일 Recorder · 봇 재시작 필요 · 필터와 무관", + hint=( + "ON = 프로그램 매매 데이터를 계속 DB에 쌓음 | " + "평소엔 OFF 권장, 변경 후 봇 재시작 필요" + ), + ), + _f( + "PROGRAM_FILTER_ENABLED", + "🚫 프로그램 매매 필터 (매수 차단)", + "bool", + default=True, + hint=( + "ON = 프로그램 매매 흐름이 나쁠 때 매수를 차단 (기본 ON) | " + "OFF = 프로그램 매매 무시하고 그냥 매수" + ), + ), + ], + }, + { + "id": "ws_whipsaw_global", + "title": "🌊 휩쏘 필터 (가짜 상승 차단)", + "hint": ( + "매수 신호가 떴는데 직전에 급락이 있었으면 가짜 신호(휩쏘)로 판단해서 차단. " + "예: 30초 안에 0.3% 이상 뚝 떨어졌다가 올라온 종목은 패스." + ), + "fields": [ + _f( + "WHIPSAW_FILTER_ENABLED", + "🚫 휩쏘 필터 ON/OFF (전략 공통)", + "bool", + default=True, + hint="ON = 가짜 상승 종목 매수 차단 | OFF = 필터 끄고 다 매수", + ), + _f( + "WHIPSAW_SUBBAR_SEC", + "⏱ 관찰 단위 시간(초)", + "int", + default=30, + hint=( + "틱을 몇 초 단위로 묶어서 미니봉을 만들지 | " + "기본 30초 = 30초짜리 막대기로 패턴 분석" + ), + ), + _f( + "WHIPSAW_LOOKBACK_SEC", + "⏳ 몇 초 전까지 볼지", + "int", + default=90, + hint=( + "매수 신호 직전 몇 초를 돌아볼지 | " + "기본 90초 = 1분 30초 전까지 확인" + ), + ), + _f( + "WHIPSAW_DIP_PCT", + "📉 이 이상 떨어졌으면 가짜 신호로 판단 (분율)", + "float", + default=0.003, + hint=( + "0.003 = 0.3% | 직전에 0.3% 이상 하락했다가 올라온 종목은 차단 | " + "더 큰 값 = 필터가 느슨해짐" + ), + ), + _f( + "WHIPSAW_RECOVERY_TOL_PCT", + "📈 이 정도는 회복 허용 (분율)", + "float", + default=0.001, + hint=( + "0.001 = 0.1% | 하락 후 0.1% 이내로 빠르게 회복하면 정상으로 봄 | " + "노이즈성 미세 출렁임 허용" + ), + ), + ], + }, + { + "id": "ws_universe_alert", + "title": "🔔 유니버스·알림 임계값", + "hint": "봇이 이상한 상황을 감지해서 알람을 보내기 위한 임계값 설정.", + "fields": [ + _f( + "LIVE_UNIVERSE_SLOT_ALIGN", + "🔗 유니버스 히스토리 교집합 필터", + "bool", + default=False, + hint=( + "OFF(기본권장) = 실시간 후보 그대로 사용 | " + "ON = 실시간 후보와 과거 히스토리 스냅이 겹치는 종목만 매수. " + "히스토리가 늦게 올라오면 매수 기회 놓칠 수 있음" + ), + ), + _f( + "OPS_ALERT_HISTORY_OPEN_GRACE_SEC", + "🕐 개장 직후 히스토리 0건 허용 시간(초)", + "int", + default=180, + hint=( + "개장 후 N초 안에는 히스토리가 0건이어도 알람 안 보냄 | " + "기본 180초(3분) = 개장 3분까지는 정상으로 취급" + ), + ), + _f( + "OPS_ALERT_WS_DOWN_HOLD_SEC", + "📡 소켓 끊김 알람 기준 시간(초)", + "int", + default=60, + hint=( + "키움/LS 소켓이 이 시간(초) 이상 끊겨 있을 때만 위험 알람 발송 | " + "기본 60초 = 1분 이상 끊겨야 알람 (순간 재연결은 무시)" + ), ), - _f("PROGRAM_FILTER_ENABLED", "프로그램 필터(TRIGGER) — 실매 탈락", "bool", default=True), - _f("WHIPSAW_FILTER_ENABLED", "휩쏘 필터(글로벌)", "bool", default=True), - _f("WHIPSAW_SUBBAR_SEC", "휩쏘 미니봉(초)", "int", default=30, - hint="틱을 N초 봉으로 묶음 · 실매 기본 30 · Optuna 꼬리 TPE 탐색"), - _f("WHIPSAW_LOOKBACK_SEC", "휩쏘 룩백(초)", "int", default=90, - hint="신호 직전 몇 초를 볼지 · 실매 기본 90"), - _f("WHIPSAW_DIP_PCT", "휩쏘 하락폭(분율, 0.003=0.3%)", "float", default=0.003), - _f("WHIPSAW_RECOVERY_TOL_PCT", "휩쏘 회복 허용(분율, 0.001=0.1%)", "float", default=0.001), ], }, ] @@ -309,10 +492,38 @@ def build_live_config_groups() -> List[GroupDef]: groups.append({ "id": "strategy_switch", "title": "전략 ON/OFF", - "hint": "config_{strategy} · 봇 재시작 없이 다음 루프 반영", + "hint": ( + "config_{strategy} · STRATEGY_*_ENABLED 는 다음 루프부터 신규매수 중단/재개 " + "(보유 청산은 유지 · WS 구독 해제는 안 함). " + "기동 시 OFF 였던 전략을 켜려면 봇 재시작. " + "해외 WS 소켓은 OVERSEAS_WS_ENABLED(기동 시) · permanent US 구독과 별개." + ), "fields": [ - _f(f"STRATEGY_{sid}_ENABLED", LIVE_STRATEGY_LABELS.get(sid, sid), "bool") - for sid in LIVE_STRATEGY_IDS + _f( + "STRATEGY_US_MOMENTUM_ENABLED", + "해외 모멘텀", + "bool", + default=True, + hint="config_us_momentum · permanent US 유니버스 · 소켓 해제 없음", + ), + _f( + "OVERSEAS_WS_ENABLED", + "해외 WS(HDFSCNT0)", + "bool", + default=True, + hint="기동 시만 · false=해외 틱 소켓 미기동 · 전략 ON/OFF 와 별개 · 변경 후 재시작", + ), + *[ + _f( + f"STRATEGY_{sid}_ENABLED", + LIVE_STRATEGY_LABELS.get(sid, sid), + "bool", + default=bool(STRATEGY_ENABLED_DEFAULTS.get(sid, False)), + hint=f"config_{sid.lower()}", + ) + for sid in LIVE_STRATEGY_SWITCH_IDS + if sid != "US_MOMENTUM" + ], ], }) @@ -346,7 +557,39 @@ def build_live_config_groups() -> List[GroupDef]: "hint": "{SID}_UNIVERSE_SOURCE · 기본 키움 · LS는 opt-in", "fields": univ_fields, }) - + groups.append({ + "id": "kiwoom_condition_config", + "title": "키움 조건검색 (안정화·회복)", + "hint": "KIWOOM_CNSR* · 유니버스 0종목 공백(Blind Spot) 차단 및 3초 고정 회복", + "fields": [ + _f("KIWOOM_CNSRCLR_BEFORE_REQ", "재등록 전 선행 해제(CNSRCLR)", "bool", default=True, + hint="Bye 후 유령 등록(900003) 방지용 선행 클리어"), + _f("KIWOOM_CNSRCLR_SETTLE_SEC", "CNSRCLR 해제 응답 ACK 대기(초)", "float", default=2.0, + hint="해제 응답(rc=0) 완료 대기 최대 타임아웃 (조기 ACK 수신 시 즉시 통과)"), + _f("KIWOOM_CNSRREQ_RETRY_SEC", "미응답/900003 고정 재시도 주기(초)", "float", default=3.0, + hint="지수 백오프 대신 3초 고정 주기로 빠른 회복 시도"), + _f("KIWOOM_CNSRREQ_MAX_RETRIES", "최대 재시도 상한", "int", default=30, + hint="30회(약 90초) 동안 RAM 후보를 보존하며 지속 회복 시도"), + _f("KIWOOM_CNSRCLR_GAP_MIN_SEC", "CNSRCLR 연속 발송 최소 간격(초)", "float", default=0.3), + _f("KIWOOM_CNSRCLR_GAP_MAX_SEC", "CNSRCLR 연속 발송 최대 간격(초)", "float", default=0.6), + _f("KIWOOM_CNSRREQ_GAP_MIN_SEC", "CNSRREQ 연속 발송 최소 간격(초)", "float", default=0.8), + ], + }) + groups.append({ + "id": "ls_condition_config", + "title": "LS 조건검색 (안정화·Diff 보정)", + "hint": "LS_T1859* · 매매 전용이 아닌 조건·시세 전용 이점을 극대화한 1분 주기 0.15초 소생술", + "fields": [ + _f("LS_T1859_STALE_RESYNC_SEC", "AFR 침묵 시 소켓 재동기화 주기(초)", "int", default=60, + hint="기본 60초(1분). 매매 주문을 처리하지 않으므로 429 차단 위험 0%! 하루 총 눈감는 시간 약 58초 동안 Delta Diff 1:1 보정"), + _f("LS_T1859_EMPTY_RETRY_SEC", "RAM 0종목 시 t1859 재시드 주기(초)", "int", default=1, + hint="기동 초기 0종목 시 1초마다 재시드 (영구 0종목 공백 록인 방지)"), + _f("LS_T1859_ALLOW_SHRINK", "t1859 축소 전체교체 허용", "bool", default=False, + hint="기본 false (장중 1종목 오류 응답이 20종목 덮어 쓰는 자살골 사고 차단)"), + _f("LS_CONDITION_TR_GAP_SEC", "t1859 REST 호출 최소 간격(초)", "float", default=1.1, + hint="LS 증권 공식 1초 1건 상한 보호 쿨다운"), + ], + }) # 전략별 운용한도 budget_fields: List[FieldDef] = [] @@ -453,149 +696,287 @@ def build_live_config_groups() -> List[GroupDef]: ws_fields.append( _f(f"{pfx}_WHIPSAW_FILTER_ENABLED", f"{lbl} 휩쏘 필터", "bool"), ) - ws_fields.extend([ - _f("TAIL_WHIPSAW_SUBBAR_SEC", "꼬리 휩쏘 미니봉(초)", "int", default=30, - hint="비우면 글로벌 WHIPSAW_SUBBAR_SEC · Optuna 꼬리 TPE 축"), - _f("TAIL_WHIPSAW_LOOKBACK_SEC", "꼬리 휩쏘 룩백(초)", "int", default=90, - hint="비우면 글로벌 WHIPSAW_LOOKBACK_SEC"), - _f("TAIL_WHIPSAW_DIP_PCT", "꼬리 휩쏘 하락폭(분율)", "float", default=0.003, - hint="0.003=0.3% · 비우면 글로벌"), - _f("TAIL_WHIPSAW_RECOVERY_TOL_PCT", "꼬리 휩쏘 회복허용(분율)", "float", default=0.001, - hint="0.001=0.1% · 비우면 글로벌"), - ]) groups.append({ "id": "whipsaw_strategy", - "title": "휩쏘 — 전략별 ON/OFF · 꼬리 초/%", + "title": "휩쏘 — 전략별 ON/OFF", "hint": ( - "ON/OFF와 초·%는 별개. 꼬리 Optuna --mode tpe 가 이 축을 탐색. " - "실매 현재값(enabled=false, 30초, 90초, 0.003, 0.001)이 그리드에 포함됨." + "각 전략별로 휩쏘 필터를 켤지 끌지 결정합니다. (글로벌 휩쏘 필터 수치 상속)" ), "fields": ws_fields, }) groups.append({ "id": "execution_fill", - "title": "체결 검증 (실전·모의 훈련)", - "hint": "실전은 항상 fill 확인. 모의는 STRICT_FILL_VERIFY=true 시 동일", + "title": "🏦 주문·체결 설정 (실전+모의 공통)", + "hint": ( + "⚠️ 현재 모의(mock) 모드로 운영 중. " + "실전 전환 시 STRICT_FILL_VERIFY 는 무관 (실전은 항상 체결 확인). " + "나머지 설정은 모의·실전 모두 동일하게 적용됨." + ), "fields": [ - _f("STRICT_FILL_VERIFY", "모의 엄격 체결 검증", "bool", default=False, - hint="모의 실매매만 영향 · ON=체결 API 필수(모의 API 한계로 매수 0건 가능) · 실전은 항상 엄격"), - _f("BUY_DEDUP_PENDING", "매수 미체결 중복주문 방지", "bool", default=True, - hint="동일 전략·종목 PENDING_FILL 있으면 재주문 스킵"), - _f("BACKTEST_SLIP_PCT", "백테 체결 슬리피지(편도 %)", "float", default=0.0, - hint="STRICT_FILL_VERIFY ON 일 때만 백테/파람서치에 적용 · 전 전략 공통 · 0=순수 백테 · 0.2=0.2%"), - _f("BACKTEST_VOL_FILL_CAP_PCT", "백테 체결량 상한(진입봉 거래량×%)", "float", default=0.0, - hint="STRICT_FILL_VERIFY ON 일 때만 · 전 전략 공통 · 0=무제한 · 소형주 IOC 미체결 근사"), - _f("TAIL_BACKTEST_TICK_FALLBACK_OHLC", "⚠ 꼬리 OHLC 폴백 (정합위험·금지)", "bool", default=False, - danger=True, - hint="절대금지: 틱 모드에서 OHLC로 체결·고점 변조. 엔진은 틱 청산 ON 시 무시. Optuna/파람 강제 OFF"), - _f("TAIL_BACKTEST_USE_TICK_EXIT", "꼬리 틱 청산", "bool", default=True, - hint="ON=ws_ticks 시간순 청산(실매 폴링 정합) · OFF=OHLC intrabar"), - _f("TAIL_BACKTEST_POLL_MS", "꼬리 틱청산 폴링(ms)", "int", default=100, - hint="실매 STRATEGY_LOOP_SLEEP≈0.1초 정합 · 하한 50ms"), - _f("MOMENTUM_BACKTEST_TICK_FALLBACK_OHLC", "⚠ 모멘텀 OHLC 폴백 (정합위험·금지)", "bool", default=False, - danger=True, - hint="절대금지 — 틱 청산 ON 시 엔진 무시 · Optuna 강제 OFF"), - _f("MOMENTUM_BACKTEST_POLL_MS", "모멘텀 틱청산 폴링(ms)", "int", default=100, - hint="실매 STRATEGY_LOOP_SLEEP≈0.1초 정합 · 틱 사이 청산 검사 간격 · 하한 50ms"), - _f("BREAKOUT_BACKTEST_USE_TICK_EXIT", "돌파 틱 청산", "bool", default=True, - hint="ON=ws_ticks 시간순 청산 · OFF=OHLC intrabar"), - _f("BREAKOUT_BACKTEST_TICK_FALLBACK_OHLC", "⚠ 돌파 OHLC 폴백 (정합위험·금지)", "bool", default=False, - danger=True, - hint="절대금지 — 틱 진입 ON 시 봉 high 폴백 안 함"), - _f("BREAKOUT_BACKTEST_POLL_MS", "돌파 틱청산 폴링(ms)", "int", default=100, - hint="실매 폴링 정합 · 하한 50ms"), - _f("SCALP_BACKTEST_USE_TICK_EXIT", "스캘핑 틱 청산", "bool", default=True, - hint="ON=ws_ticks 시간순 청산(실매 폴링 정합) · OFF=OHLC intrabar"), - _f("SCALP_BACKTEST_USE_TICK_ENTRY", "스캘핑 틱 진입", "bool", default=True, - hint="ON=예약 체결 시 해당 분 첫 틱가 · OFF=분봉 시가"), - _f("SCALP_BACKTEST_TICK_FALLBACK_OHLC", "⚠ 스캘핑 OHLC 폴백 (정합위험·금지)", "bool", default=False, - danger=True, - hint="절대금지 — 틱 청산 ON 시 엔진 무시"), - _f("SCALP_BACKTEST_POLL_MS", "스캘핑 틱청산 폴링(ms)", "int", default=100, - hint="실매 STRATEGY_LOOP_SLEEP≈0.1초 정합 · 하한 50ms"), - _f("RANGE_BREAK_BACKTEST_USE_TICK_EXIT", "박스돌파 틱 청산", "bool", default=True, - hint="ON=ws_ticks 시간순 청산 · OFF=OHLC intrabar"), - _f("RANGE_BREAK_BACKTEST_TICK_FALLBACK_OHLC", "⚠ 박스돌파 OHLC 폴백 (정합위험·금지)", "bool", default=False, - danger=True, - hint="절대금지 — 틱 청산 ON 시 엔진 무시"), - _f("RANGE_BREAK_BACKTEST_POLL_MS", "박스돌파 틱청산 폴링(ms)", "int", default=100, - hint="실매 폴링 정합 · 하한 50ms"), - _f("DBBAND_BACKTEST_USE_TICK_EXIT", "더블BB 틱 청산", "bool", default=True, - hint="ON=ws_ticks 시간순 청산 · OFF=봉 OHLC 1회"), - _f("DBBAND_BACKTEST_TICK_FALLBACK_OHLC", "⚠ 더블BB OHLC 폴백 (정합위험·금지)", "bool", default=False, - danger=True, - hint="절대금지 — 틱 청산 ON 시 엔진 무시"), - _f("DBBAND_BACKTEST_POLL_MS", "더블BB 틱청산 폴링(ms)", "int", default=100, - hint="실매 폴링 정합 · 하한 50ms"), - _f("TAIL_PARAM_SEARCH_MIN_START", "꼬리 파라서치 최소 시작일", "str", default="2026-07-07", - hint="조건식 변경일 이후만 탐색 (YYYY-MM-DD) · CLI --start 와 max 적용"), - _f("USE_MARKET_IOC", "매수 시장가 IOC", "bool", default=True, - hint="실전 매수 ORD_DVSN=13(IOC) · OFF=01 · 모의는 01 고정"), - _f("USE_MARKET_IOC_SELL", "매도 시장가 IOC", "bool", default=True, - hint="실전 매도 ORD_DVSN=13(IOC) · OFF=01 · 모의는 01 고정 · 거절 많으면 OFF"), - _f("ORDER_FILL_WAIT_SEC", "시장가 체결 대기(초)", "int", default=2), - _f("DUPLICATE_ORDER_FILL_RECOVERY_ENABLED", "주문DB중복 체결복구", "bool", default=True, - hint="insert 실패(주문DB중복) 시 inquire-daily-ccld 1회 → active_trades 복구 · 실패 시에만 REST"), - _f("DUPLICATE_ORDER_RECOVERY_WAIT_SEC", "중복복구 체결대기(초)", "int", default=2, - hint="주문DB중복 복구 전용 · 미설정 시 ORDER_FILL_WAIT_SEC 사용"), - _f("LIMIT_ORDER_FILL_WAIT_SEC", "지정가 체결 대기(초)", "int", default=1), - _f("PENDING_FILL_POLL_INTERVAL_SEC", "미체결 재조회 주기(초)", "int", default=10), - _f("PENDING_FILL_POLL_SEC", "재조회 1회 대기(초)", "int", default=1), - _f("PENDING_POLL_BATCH_FETCH", "미체결 체결 일괄조회", "bool", default=True, - hint="실매 heartbeat 전용 · ON=poll 1회당 inquire-daily-ccld 1건(미체결 N건) · OFF=건별 조회 · 백테 무관"), - _f("PENDING_BUY_MAX_AGE_SEC", "매수 미체결 만료(초)", "int", default=120), - _f("PENDING_SELL_MAX_AGE_SEC", "익절 매도 미체결 만료(초)", "int", default=60), - _f("PENDING_SELL_STOP_MAX_AGE_SEC", "손절 매도 미체결 만료(초)", "int", default=15), - _f("SELL_PENDING_REORDER_ON_EXPIRE", "손절 만료 후 즉시 재매도", "bool", default=True), - _f("AUTO_CANCEL_PARTIAL_BUY_REMAINDER", "부분체결 잔량 자동취소", "bool", default=True), _f( - "SELL_LIMIT_CANCEL_BEFORE_MARKET_RETRY", - "익절지정가→시장가 전 취소", + "USE_MARKET_IOC", + "⚡ 매수 시장가 IOC (즉시체결 후 미체결 취소)", "bool", default=True, hint=( - "ON=지정가 미확인/부분체결 시 잔여 취소 후 ODNO 재조회, " - "취소실패·미확정이면 시장가 금지(PENDING) · " - "OFF=레거시 즉시 시장가(이중매도 위험) · 전전략 OrderManager 공통" + "ON(권장) = 매수 주문을 시장가로 내고, 즉시 안 채워진 나머지는 자동 취소 (실전만 해당, 모의는 항상 지정가) | " + "OFF = 시장가 보통주문 (미체결이 그냥 남아있음)" ), ), _f( - "SELL_LIMIT_RECHECK_WAIT_SEC", - "지정가 취소 후 재조회(초)", - "int", - default=1, - hint="SELL_LIMIT_CANCEL_BEFORE_MARKET_RETRY=ON 일 때 ODNO 재조회 대기", - ), - _f( - "SELL_LIMIT_CANCEL_FAIL_BALANCE_CONFIRM", - "취소실패+잔고0→매도확정", + "USE_MARKET_IOC_SELL", + "⚡ 매도 시장가 IOC", "bool", default=True, hint=( - "ON=익절지정가 취소 실패·ODNO 미확인이어도 브로커 0주면 " - "지정가 체결로 finalize(유령금지) · OFF=PENDING만" + "ON = 매도도 IOC 시장가 (실전만 해당, 모의는 항상 지정가) | " + "OFF = 시장가 보통주문. 거절(주문거부) 많이 나오면 OFF로" + ), + ), + _f( + "STRICT_FILL_VERIFY", + "🔍 모의 체결 강제 확인 (모의 전용)", + "bool", + default=False, + hint=( + "★ 모의 모드에서만 의미 있음 (실전은 항상 체결 확인) ★ | " + "ON = 모의에서도 매수 후 체결 API 로 실제 체결 됐는지 확인. " + "모의 API 특성상 확인 실패해서 매수 0건으로 집계될 수 있음 | " + "OFF(기본) = 모의는 주문만 내고 체결됐다고 가정" + ), + ), + _f( + "BUY_DEDUP_PENDING", + "🚫 동일 종목 매수 중복 방지", + "bool", + default=True, + hint=( + "ON(권장) = 같은 전략·종목의 주문이 아직 체결 대기 중이면 추가 주문 안 냄 | " + "OFF = 중복 주문 허용 (같은 종목을 두 번 살 수 있음)" + ), + ), + _f( + "ORDER_FILL_WAIT_SEC", + "⏱ 시장가 주문 체결 대기 시간(초)", + "int", + default=2, + hint="주문 낸 후 체결됐는지 확인하기까지 기다리는 시간. 기본 2초.", + ), + _f( + "PENDING_BUY_MAX_AGE_SEC", + "⏳ 매수 미체결 주문 포기 시간(초)", + "int", + default=120, + hint=( + "매수 주문을 냈는데 이 시간(초) 안에 체결이 안 되면 포기하고 주문 취소 | " + "기본 120초(2분)" + ), + ), + _f( + "PENDING_SELL_MAX_AGE_SEC", + "⏳ 익절 매도 미체결 주문 포기 시간(초)", + "int", + default=60, + hint="익절 매도 주문이 이 시간(초) 안에 안 채워지면 포기. 기본 60초.", + ), + _f( + "PENDING_SELL_STOP_MAX_AGE_SEC", + "⏳ 손절 매도 미체결 주문 포기 시간(초)", + "int", + default=15, + hint=( + "손절은 빨리 안 팔리면 위험! 이 시간(초) 안에 체결 안 되면 재주문 | " + "기본 15초 (손절은 짧게)" + ), + ), + _f( + "SELL_PENDING_REORDER_ON_EXPIRE", + "🔄 손절 포기 후 즉시 재주문", + "bool", + default=True, + hint=( + "ON(권장) = 손절 주문이 시간 초과되면 즉시 새로 손절 주문 | " + "OFF = 그냥 포기" + ), + ), + _f( + "AUTO_CANCEL_PARTIAL_BUY_REMAINDER", + "✂ 매수 일부 체결 후 나머지 자동 취소", + "bool", + default=True, + hint=( + "ON(권장) = 100주 주문했는데 50주만 체결됐으면 나머지 50주 주문 자동 취소 | " + "OFF = 나머지 50주 주문이 그대로 남아있음" + ), + ), + _f( + "SELL_LIMIT_CANCEL_BEFORE_MARKET_RETRY", + "🔒 익절 지정가 취소 후 시장가 전환", + "bool", + default=True, + hint=( + "ON(권장) = 익절 지정가 매도가 안 팔리면 → 취소 확인 후 → 시장가로 바꿔서 팜. " + "안전하게 이중매도 방지 | " + "OFF = 레거시 방식 (이중매도 위험, 비권장)" + ), + ), + _f( + "SELL_LIMIT_CANCEL_FAIL_BALANCE_CONFIRM", + "🛡 취소 실패해도 잔고 0이면 매도 완료 처리", + "bool", + default=True, + hint=( + "ON(권장) = 취소 요청이 실패해도 실제 잔고를 보니 0주면 지정가에 팔린 걸로 확정 | " + "→ 유령 포지션(DB엔 있는데 실제론 없는 것) 방지" ), ), _f( "GHOST_PURGE_BLOCK_WHILE_PENDING_SELL", - "PENDING매도 시 유령정리 금지", + "👻 매도 주문 대기 중 유령 정리 금지", "bool", default=True, hint=( - "ON=미확정 매도 ODNO 있으면 ghost_purge 대신 체결재조회/잔고확정 청산" + "ON(권장) = 아직 체결 확인 안 된 매도 주문이 있으면 유령(DB만 있는 포지션) 정리 안 함 | " + "→ '팔리는 중인데 유령이라고 지워버리는' 사고 방지" ), ), _f( - "DAILY_CCLD_TR_ID", - "일별체결조회 TR_ID", + "BACKTEST_SLIP_PCT", + "📊 백테스트 슬리피지 (백테 전용)", + "float", + default=0.0, + hint=( + "★ 백테스트·파람서치에만 적용, 실제 매매 무관 ★ | " + "0.2 = 매수가에 0.2%, 매도가에서 0.2% 차감해서 현실적인 수익 계산 | " + "0 = 이상적인 체결 가정 (실제보다 성과 좋게 나옴)" + ), + ), + _f( + "BACKTEST_VOL_FILL_CAP_PCT", + "📊 백테스트 체결량 상한 (백테 전용)", + "float", + default=0.0, + hint=( + "★ 백테스트·파람서치에만 적용 ★ | " + "0 = 무제한 (다 체결됐다고 가정) | " + "예: 20 = 해당 봉 거래량의 20%까지만 체결 가정 (소형주 현실 반영)" + ), + ), + _f( + "TAIL_BACKTEST_USE_TICK_EXIT", + "📊 꼬리잡기 백테 틱 청산 (백테 전용)", + "bool", + default=True, + hint=( + "★ 백테스트에서만 사용 ★ | " + "ON(권장) = 실제 틱 데이터 시간순으로 손절/익절 판단 (실매매와 동일 방식) | " + "OFF = 봉 OHLC로 판단 (덜 정확)" + ), + ), + _f( + "TAIL_BACKTEST_TICK_FALLBACK_OHLC", + "⛔ 꼬리잡기 OHLC 폴백 (절대 금지)", + "bool", + default=False, + danger=True, + hint=( + "⛔ 절대 ON 하지 말 것! ⛔ | " + "ON = 틱 데이터로 청산하는 대신 봉 고점/저점으로 체결가 계산 → 실매매와 완전 다른 결과" + ), + ), + _f( + "TAIL_BACKTEST_POLL_MS", + "📊 꼬리잡기 틱 청산 검사 간격(ms) (백테 전용)", + "int", + default=100, + hint="백테에서 틱을 몇 ms마다 확인할지. 100ms = 0.1초 = 실매매 루프와 동일.", + ), + _f( + "TAIL_PARAM_SEARCH_MIN_START", + "📅 꼬리잡기 파라미터 서치 시작일 하한", "str", - default="", - hint="비우면 모의 VTTC0081R / 실전 TTTC0081R (레거시 *8001R 사용 금지)", + default="2026-07-07", + hint=( + "꼬리잡기 Optuna 파람서치를 이 날짜 이전 데이터로는 탐색 안 함 | " + "조건식 바꾼 날 이후만 탐색하도록. YYYY-MM-DD 형식." + ), + ), + _f( + "BREAKOUT_BACKTEST_USE_TICK_EXIT", + "📊 돌파 백테 틱 청산 (백테 전용)", + "bool", + default=True, + hint=( + "★ 백테스트에서만 사용 ★ | " + "ON(권장) = 실제 틱 순서로 청산 판단 (실매매 정합)" + ), + ), + _f( + "BREAKOUT_BACKTEST_TICK_FALLBACK_OHLC", + "⛔ 돌파 OHLC 폴백 (절대 금지)", + "bool", + default=False, + danger=True, + hint="⛔ 절대 ON 하지 말 것! ⛔ | 봉 high/low로 체결가 계산 → 실매와 다른 가짜 결과", + ), + _f( + "BREAKOUT_BACKTEST_POLL_MS", + "📊 돌파 틱 청산 검사 간격(ms) (백테 전용)", + "int", + default=100, + hint="백테에서 틱을 몇 ms마다 확인할지. 기본 100ms.", + ), + _f( + "MOMENTUM_BACKTEST_TICK_FALLBACK_OHLC", + "⛔ 모멘텀 OHLC 폴백 (절대 금지)", + "bool", + default=False, + danger=True, + hint="⛔ 절대 ON 하지 말 것! ⛔", + ), + _f( + "MOMENTUM_BACKTEST_POLL_MS", + "📊 모멘텀 틱 청산 검사 간격(ms) (백테 전용)", + "int", + default=100, + ), + _f( + "SCALP_BACKTEST_USE_TICK_EXIT", + "📊 스캘핑 백테 틱 청산 (백테 전용)", + "bool", + default=True, + hint="ON = 실제 틱 순서로 청산 (실매매 정합)", + ), + _f( + "SCALP_BACKTEST_USE_TICK_ENTRY", + "📊 스캘핑 백테 틱 진입 (백테 전용)", + "bool", + default=True, + hint=( + "ON = 예약 체결 시 해당 분봉의 첫 틱 가격으로 진입 | " + "OFF = 분봉 시가로 진입" + ), + ), + _f( + "SCALP_BACKTEST_TICK_FALLBACK_OHLC", + "⛔ 스캘핑 OHLC 폴백 (절대 금지)", + "bool", + default=False, + danger=True, + hint="⛔ 절대 ON 하지 말 것! ⛔", + ), + _f( + "SCALP_BACKTEST_POLL_MS", + "📊 스캘핑 틱 청산 검사 간격(ms) (백테 전용)", + "int", + default=100, ), ], }) + + groups.append({ "id": "rest_throttle", "title": "REST 유량 제한 (한투 API)", @@ -752,42 +1133,79 @@ def build_live_config_groups() -> List[GroupDef]: groups.append({ "id": "breakout_exit", - "title": "돌파(BREAKOUT) — EOD·시간컷", - "hint": ( - "env_config · 저장 후 다음 매도 루프부터 반영(재시작 불필요). " - "EOD OFF 시 당일 15:15 강제청산 없음 — 손절·익절·어깨·시간컷만." - ), + "title": "Optuna 청산 파라미터 (돌파)", + "hint": "AI(Optuna)가 최적값을 찾아내는 수익/손실구간 호가매도 파라미터 모음입니다.", "fields": [ - _f("BREAKOUT_EOD_ENABLED", "EOD 당일청산 사용", "bool", default=True, - hint="ON=설정 시각 이후 보유 전량 eod 청산 · OFF=익절·손절·시간컷만"), - _f("BREAKOUT_EOD_HM", "EOD 시각 (HH:MM)", "text", default="15:15", - hint="이 시각 이후 보유 전량 청산 · 예: 15:15"), _f("BREAKOUT_MAX_HOLD_BARS", "최대보유(분)", "int", default=0, hint="0=OFF · 진입 후 N분 경과 시 max_hold 청산"), + _f("BREAKOUT_EXIT_OB_ENABLED", "수익구간 호가매도 ON", "bool", default=False, + danger=True, + hint="[주의] 꼬리잡기/스캘핑(히트앤런 단타)은 필요 없어서 스위치가 없습니다. ON=이득+매수잔량 붕괴 시 즉시 탈출"), + _f("BREAKOUT_EXIT_OB_RATIO_MIN", "수익구간 OR_MA 임계", "float", default=0.4, + hint="매수잔량/매도잔량 MA 미만이면 붕괴 · 예: 0.4"), + _f("BREAKOUT_EXIT_OB_MA_WINDOW", "수익구간 OR MA 창", "int", default=5, + hint="OR 이동평균에 쓰는 최근 스냅 수"), + _f("BREAKOUT_EXIT_OB_MIN_PROFIT_PCT", "수익구간 최소이익(분율)", "float", default=0.005, + hint="0.005=+0.5% · 미달이면 호가컷 미발동"), + _f("BREAKOUT_EXIT_OB_MIN_HOLD_BARS", "수익구간 최소보유(분)", "int", default=3, + hint="보유 분(1분봉≈1) 미달이면 미발동"), + _f("BREAKOUT_STOP_OB_ENABLED", "손절호가 ON", "bool", default=False, + danger=True, + hint="[주의] 꼬리잡기/스캘핑(히트앤런 단타)은 필요 없어서 스위치가 없습니다. ON=손해+매수잔량 붕괴 시 하드손절 전 미리 탈출"), + _f("BREAKOUT_STOP_OB_RATIO_MIN", "손절호가 OR_MA 임계", "float", default=0.4, + hint="손실구간 OR_MA 임계"), + _f("BREAKOUT_STOP_OB_MA_WINDOW", "손절호가 OR MA 창", "int", default=5, + hint="손절호가 OR 이동평균 스냅 수"), + _f("BREAKOUT_STOP_OB_MIN_LOSS_PCT", "손절호가 최소손실(분율)", "float", default=0.003, + hint="0.003=−0.3% · 이만큼 이상 손해일 때만"), + _f("BREAKOUT_STOP_OB_MIN_HOLD_BARS", "손절호가 최소보유(분)", "int", default=2, + hint="보유 분 미달이면 손절호가 미발동"), ], }) groups.append({ "id": "momentum_exit", - "title": "모멘텀(MOMENTUM) — EOD", - "hint": "env_config · 저장 후 다음 매도 루프부터 반영(재시작 불필요).", + "title": "Optuna 청산 파라미터 (모멘텀)", + "hint": "AI(Optuna)가 최적값을 찾아내는 수익/손실구간 호가매도 파라미터 모음입니다.", "fields": [ - _f("MOMENTUM_EOD_ENABLED", "EOD 당일청산 사용", "bool", default=True, - hint="ON=설정 시각 이후 보유 전량 장마감청산 · OFF=익절·손절·시간컷만"), - _f("MOMENTUM_EOD_HM", "EOD 시각 (HH:MM)", "text", default="15:20", - hint="정규장 15:30 마감 기준 최소 10분 전 · 순차매도 여유"), + _f("MOMENTUM_EXIT_OB_ENABLED", "수익구간 호가매도 ON", "bool", default=False, + danger=True, + hint="[주의] 꼬리잡기/스캘핑(히트앤런 단타)은 필요 없어서 스위치가 없습니다. ON=이득+매수잔량 붕괴 시 즉시 탈출"), + _f("MOMENTUM_EXIT_OB_RATIO_MIN", "수익구간 OR_MA 임계", "float", default=0.4, + hint="매수잔량/매도잔량 MA 미만이면 붕괴 · 예: 0.4"), + _f("MOMENTUM_EXIT_OB_MA_WINDOW", "수익구간 OR MA 창", "int", default=5, + hint="OR 이동평균에 쓰는 최근 스냅 수"), + _f("MOMENTUM_EXIT_OB_MIN_PROFIT_PCT", "수익구간 최소이익(분율)", "float", default=0.005, + hint="0.005=+0.5% · 미달이면 호가컷 미발동"), + _f("MOMENTUM_EXIT_OB_MIN_HOLD_BARS", "수익구간 최소보유(분)", "int", default=3, + hint="보유 분(1분봉≈1) 미달이면 미발동"), + _f("MOMENTUM_STOP_OB_ENABLED", "손절호가 ON", "bool", default=False, + danger=True, + hint="[주의] 꼬리잡기/스캘핑(히트앤런 단타)은 필요 없어서 스위치가 없습니다. ON=손해+매수잔량 붕괴 시 하드손절 전 미리 탈출"), + _f("MOMENTUM_STOP_OB_RATIO_MIN", "손절호가 OR_MA 임계", "float", default=0.4, + hint="총사÷총팔 MA 미만이면 붕괴 · 예: 0.4"), + _f("MOMENTUM_STOP_OB_MA_WINDOW", "손절호가 OR MA 창", "int", default=5, + hint="OR 이동평균 스냅 수"), + _f("MOMENTUM_STOP_OB_MIN_LOSS_PCT", "손절호가 최소손실(분율)", "float", default=0.003, + hint="0.003=−0.3% · 이만큼 이상 손해일 때만"), + _f("MOMENTUM_STOP_OB_MIN_HOLD_BARS", "손절호가 최소보유(분)", "int", default=2, + hint="보유 분 미달이면 미발동"), ], }) groups.append({ - "id": "tail_exit", - "title": "꼬리(SHORT/TAIL) — EOD", - "hint": "env_config · 저장 후 다음 매도 루프부터 반영(재시작 불필요).", + "id": "eod_global", + "title": "🌅 EOD 당일청산 설정 (전략 통합)", + "hint": "각 전략별로 정해진 시각이 되면 보유 물량을 전량 청산(퇴근)합니다.", "fields": [ - _f("TAIL_EOD_ENABLED", "EOD 당일청산 사용", "bool", default=True, - hint="ON=설정 시각 이후 보유 전량 장마감 청산 · OFF=익절·손절·시간컷만"), - _f("TAIL_EOD_HM", "EOD 시각 (HH:MM)", "text", default="15:20", - hint="정규장 15:30 마감 기준 최소 10분 전 · 순차매도 여유"), + _f("BREAKOUT_EOD_ENABLED", "돌파 EOD 사용", "bool", default=True), + _f("BREAKOUT_EOD_HM", "돌파 EOD 시각 (HH:MM)", "text", default="15:15", + hint="예: 15:15 (빠르게 정리)"), + _f("MOMENTUM_EOD_ENABLED", "모멘텀 EOD 사용", "bool", default=True), + _f("MOMENTUM_EOD_HM", "모멘텀 EOD 시각 (HH:MM)", "text", default="15:20", + hint="순차매도 여유분 감안"), + _f("TAIL_EOD_ENABLED", "꼬리잡기 EOD 사용", "bool", default=True), + _f("TAIL_EOD_HM", "꼬리잡기 EOD 시각 (HH:MM)", "text", default="15:20"), ], }) @@ -819,6 +1237,33 @@ def build_live_config_groups() -> List[GroupDef]: ], }) + groups.append({ + "id": "ops_alert", + "title": "운영치명 알림 (봇 내부)", + "hint": ( + "실매 프로세스 안에서 토큰/WS/유니버스0/주문거부/유량/VI/MarketGuard PANIC 을 " + "시스템 MM(+TG)로 보냄. journal 오류감시와 별개·보완." + ), + "fields": [ + _f("OPS_ALERT_ENABLED", "운영알림 ON", "bool", default=True), + _f("OPS_ALERT_MM_CHANNEL", "MM alias", "text", default="", + hint="비우면 KIS_SYSTEM_MM_CHANNEL"), + _f("OPS_ALERT_TG_ON_CRITICAL", "critical 시 TG", "bool", default=True), + _f("OPS_ALERT_COOLDOWN_SEC", "코드별 쿨다운(초)", "int", default=300), + _f("OPS_ALERT_GLOBAL_MIN_GAP_SEC", "전역 최소간격(초)", "int", default=20), + _f("OPS_ALERT_SESSION_START_HM", "장중게이트 시작 HHMM", "int", default=900), + _f("OPS_ALERT_SESSION_END_HM", "장중게이트 종료 HHMM", "int", default=1535), + _f("OPS_ALERT_UNIVERSE_ZERO_SEC", "유니버스0 지속(초)", "int", default=180, + hint="장중 유니버스 0 이 이 시간 이상이면 알림"), + _f("OPS_ALERT_HISTORY_STALE_SEC", "히스토리 공백(초)", "int", default=600, + hint="키움 history 당일 최신 스냅샷이 이보다 오래면 알림"), + _f("OPS_ALERT_ORDER_REJECT_STREAK", "주문거부 연속 N회", "int", default=3), + _f("OPS_ALERT_RATE_LIMIT_STREAK", "유량 연속 N회", "int", default=5), + _f("OPS_ALERT_VI_ENABLED", "VI 발동 알림", "bool", default=True), + _f("OPS_ALERT_MARKET_GUARD_ENABLED", "MarketGuard PANIC 알림", "bool", default=True), + ], + }) + groups.append({ "id": "portfolio_hold", "title": "보유·매도 / 고아복구", diff --git a/kis_trader/ws/kis_ws.py b/kis_trader/ws/kis_ws.py index 29246b8..bf47af4 100644 --- a/kis_trader/ws/kis_ws.py +++ b/kis_trader/ws/kis_ws.py @@ -349,7 +349,9 @@ class KISWebSocketPriceCache: return self._subscribed.add(code) if self._connected and self._ws: - self._send_sub_msg(code, subscribe=True) + self._send_sub_msg(code, subscribe=True, tr_id="H0STCNT0") + if get_env_bool("WS_ORDERBOOK_SAVE_KIS", False): + self._send_sub_msg(code, subscribe=True, tr_id="H0STASP0") logger.info("📡 WebSocket 구독 추가: %s (%d/%d)", code, len(self._subscribed), self.MAX_SUBSCRIPTIONS) def unsubscribe(self, code: str) -> None: @@ -366,7 +368,9 @@ class KISWebSocketPriceCache: with self._cache_lock: self._cache.pop(code, None) if self._connected and self._ws: - self._send_sub_msg(code, subscribe=False) + self._send_sub_msg(code, subscribe=False, tr_id="H0STCNT0") + if get_env_bool("WS_ORDERBOOK_SAVE_KIS", False): + self._send_sub_msg(code, subscribe=False, tr_id="H0STASP0") logger.info("📡 WebSocket 구독 해제: %s", code) def add_price_listener(self, callback) -> None: @@ -425,6 +429,17 @@ class KISWebSocketPriceCache: return None return entry.get("data") + def get_orderbook(self, code: str, max_age_sec: float = 3.0) -> Optional[dict]: + """메모리에 저장된 최신 KIS 호가를 반환합니다.""" + with self._cache_lock: + entry = self._ob_cache.get(code) + if not entry: + return None + age = time.time() - entry.get("ts", 0) + if age > max_age_sec: + return None + return entry.get("data") + @property def is_active(self) -> bool: """WebSocket이 연결되어 실시간 데이터를 수신 중이면 True.""" @@ -607,7 +622,7 @@ class KISWebSocketPriceCache: except Exception: pass - def _build_sub_payload(self, code: str, subscribe: bool) -> str: + def _build_sub_payload(self, code: str, subscribe: bool, tr_id: str = "H0STCNT0") -> str: """구독(tr_type=1) / 해제(tr_type=2) JSON 메시지 생성.""" return json.dumps({ "header": { @@ -618,18 +633,18 @@ class KISWebSocketPriceCache: }, "body": { "input": { - "tr_id": "H0STCNT0", + "tr_id": tr_id, "tr_key": code, } }, }) - def _send_sub_msg(self, code: str, subscribe: bool = True) -> None: + def _send_sub_msg(self, code: str, subscribe: bool = True, tr_id: str = "H0STCNT0") -> None: """WebSocket으로 구독/해제 메시지 전송. 실패 시 조용히 무시.""" if not self._ws: return try: - self._ws.send(self._build_sub_payload(code, subscribe)) + self._ws.send(self._build_sub_payload(code, subscribe, tr_id)) except Exception as e: logger.debug("구독 메시지 전송 실패(%s): %s", code, e) @@ -694,9 +709,38 @@ class KISWebSocketPriceCache: if len(parts) < 4: return # parts[0]=암호화구분, parts[1]=TR_ID, parts[2]=건수, parts[3]=데이터 - if parts[1] != "H0STCNT0": + if parts[1] == "H0STASP0": + # 실시간 호가 (H0STASP0) + try: + raw_data = parts[3] + fields = raw_data.split('^') + if len(fields) >= 42: + code_val = fields[0] + snap = { + "BSOP_HOUR": fields[1], + "ASKP1": fields[3], "BIDP1": fields[13], + "TOTAL_ASKP_RSQN": fields[43], + "TOTAL_BIDP_RSQN": fields[44], + } + + levels = [] + for i in range(10): + ask = float(fields[3 + i] or 0) + bid = float(fields[13 + i] or 0) + ask_vol = int(fields[23 + i] or 0) + bid_vol = int(fields[33 + i] or 0) + if ask > 0 or bid > 0: + levels.append({"a": ask, "av": ask_vol, "b": bid, "bv": bid_vol}) + snap["levels"] = levels + + if hasattr(self.db, "insert_kis_ws_orderbook"): + self.db.insert_kis_ws_orderbook(code=code_val, snap=snap, market="KR") + except Exception as e: + logger.debug("H0STASP0 호가 파싱 오류: %s", e) return + if parts[1] != "H0STCNT0": + return # 암호화된 데이터는 아직 미지원 (평문만 처리) if parts[0] == "1": logger.debug("H0STCNT0 암호화 데이터 수신 (처리 스킵) → REST fallback 권장") @@ -1297,7 +1341,7 @@ class CandleAggregator: return bool(getattr(self, "_freeze_on_confirm_cached", True)) def _load_confirmed_ohlcv_from_db( - self, code: str, tf: int, candle_times: list, + self, code: str, tf: int, candle_times: list, source: str = "kis" ) -> Dict[str, Dict]: """ freeze 재시작 정합: RAM 이 비어도 DB 에 이미 확정된 봉은 REST 로 덮지 않고 @@ -1320,10 +1364,10 @@ class CandleAggregator: SELECT candle_time, `open`, high, low, close, volume, rsi_2, rsi_3, rsi_5, source, holding_peak FROM ws_candles - WHERE code=%s AND timeframe=%s AND is_confirmed=1 + WHERE code=%s AND timeframe=%s AND source=%s AND is_confirmed=1 AND candle_time IN ({ph}) """, - (code, int(tf), *chunk), + (code, int(tf), source, *chunk), ).fetchall() for r in rows or []: ct = str(r.get("candle_time") or "")[:12] @@ -1520,6 +1564,7 @@ class CandleAggregator: *, market: str = "KR", timeframes: Optional[list] = None, + source: str = "kis", ) -> None: """ KISWebSocketPriceCache._parse_realtime_msg 에서 틱마다 호출됨. @@ -1533,17 +1578,17 @@ class CandleAggregator: tfs = list(timeframes) if timeframes is not None else self.timeframes with self._lock: for tf in tfs: - self._process_tick(code, price, volume, tick_time, tf, market=mk) + self._process_tick(code, price, volume, tick_time, tf, market=mk, source=source) def _process_tick(self, code: str, price: float, volume: int, - tick_time: str, tf: int, *, market: str = "KR") -> None: + tick_time: str, tf: int, *, market: str = "KR", source: str = "kis") -> None: """ 단일 timeframe 에 대한 틱 처리 (lock 내부에서 호출). [트랙 1] RAM 갱신만 수행, DB 호출 없음 → 블로킹 0ms [트랙 2] 봉 확정 순간에만 Queue.put_nowait() → 기록원이 비동기 배치 저장 """ - key = (code, tf) + key = (code, tf, source) new_ctime = self._candle_key(tick_time, tf) if key not in self._current: @@ -1556,6 +1601,7 @@ class CandleAggregator: "volume": int(volume) if inc else 0, "_acml_base": None if inc else int(volume), "market": market, + "source": source, } return @@ -1580,6 +1626,7 @@ class CandleAggregator: "volume": int(volume) if inc else 0, "_acml_base": None if inc else int(volume), "market": market, + "source": source, } else: # 같은 봉: OHLCV 갱신 (RAM만, DB 쓰기 없음) @@ -1607,7 +1654,9 @@ class CandleAggregator: - ``WS_CANDLE_FREEZE_ON_CONFIRM``(기본 true): OHLCV 유지(첫 확정 승), holding_peak 만 갱신 - freeze OFF: volume 더 큰 쪽 upsert (레거시) """ - code, tf = key + code = key[0] + tf = key[1] + source = key[2] if len(key) >= 3 else "kis" ctime = str(cur.get("candle_time") or "")[:12] buf = self._confirmed.setdefault(key, []) closes = self._closes.setdefault(key, []) @@ -1692,7 +1741,7 @@ class CandleAggregator: "rsi_3": rsi3, "rsi_5": rsi5, "is_confirmed": 1, - "source": "ws", + "source": cur.get("source", "ws"), } if hp is not None: confirmed_candle["holding_peak"] = hp @@ -1741,7 +1790,8 @@ class CandleAggregator: cur = self._current.get(key) if not cur: continue - code, tf = key + code = key[0] + tf = key[1] try: bucket_start = _dt.datetime.strptime(cur["candle_time"], "%Y%m%d%H%M") except Exception: @@ -1846,6 +1896,10 @@ class CandleAggregator: self._open_bucket_ctime(tf, now=now) if skip_incomplete_bucket else "" ) + first_src = "kis" + if bars: + first_src = str(bars[0].get("source") or "kis") + # lock 밖에서 DB 조회 (재시작 후 RAM 공백 → REST 가 DB 확정봉을 덮는 것 방지) db_frozen: Dict[str, Dict] = {} if freeze and bars and self.db is not None: @@ -1854,10 +1908,10 @@ class CandleAggregator: ct = str(row.get("candle_time") or row.get("time") or "")[:12] if ct and len(ct) >= 12: want_times.append(ct) - db_frozen = self._load_confirmed_ohlcv_from_db(code, tf, want_times) + db_frozen = self._load_confirmed_ohlcv_from_db(code, tf, want_times, source=first_src) with self._lock: - key = (code, tf) + key = (code, tf, first_src) closes = self._closes.setdefault(key, []) conf_buf = self._confirmed.setdefault(key, []) @@ -2064,7 +2118,7 @@ class CandleAggregator: if tf <= 1: return 0 with self._lock: - bars_1m = list(self._confirmed.get((code, 1), [])) + bars_1m = list(self._confirmed.get((code, 1, "kis"), [])) if not bars_1m: return 0 rolled = rollup_1m_bars_to_tf(bars_1m, tf) @@ -2076,46 +2130,46 @@ class CandleAggregator: # [트랙 1] RAM 버퍼 조회 — 매수/매도 루프에서 직접 호출 (DB 조회 없음) # ------------------------------------------------------------------ - def get_latest_confirmed(self, code: str, tf: int) -> Optional[dict]: + def get_latest_confirmed(self, code: str, tf: int, source: str = "kis") -> Optional[dict]: """ 가장 최근 확정된 봉(완성된 마지막 봉)을 반환. None이면 아직 봉이 확정되지 않음 (장 초반 등). """ with self._lock: - buf = self._confirmed.get((code, tf)) + buf = self._confirmed.get((code, tf, source)) return buf[-1] if buf else None - def get_prev_confirmed(self, code: str, tf: int) -> Optional[dict]: + def get_prev_confirmed(self, code: str, tf: int, source: str = "kis") -> Optional[dict]: """직전 확정봉 (최신에서 2번째). 패턴 확인용 (현재봉 - 1).""" with self._lock: - buf = self._confirmed.get((code, tf)) + buf = self._confirmed.get((code, tf, source)) return buf[-2] if buf and len(buf) >= 2 else None - def get_candles(self, code: str, tf: int, n: int = 10) -> list: + def get_candles(self, code: str, tf: int, n: int = 10, source: str = "kis") -> list: """최근 n개 확정 봉 리스트 반환 (오래된→최신 순).""" with self._lock: - buf = self._confirmed.get((code, tf), []) + buf = self._confirmed.get((code, tf, source), []) return list(buf[-n:]) - def get_confirmed_count(self, code: str, tf: int) -> int: + def get_confirmed_count(self, code: str, tf: int, source: str = "kis") -> int: """확정된 봉 수 (RSI 안정화 여부 확인용).""" with self._lock: - return len(self._confirmed.get((code, tf), [])) + return len(self._confirmed.get((code, tf, source), [])) - def get_current_candle(self, code: str, tf: int) -> Optional[dict]: + def get_current_candle(self, code: str, tf: int, source: str = "kis") -> Optional[dict]: """ 현재 진행 중인 봉(미확정, is_confirmed=0) 반환. RSI는 포함되지 않음 (확정 봉 기준으로만 계산). """ with self._lock: - return dict(self._current.get((code, tf), {})) or None + return dict(self._current.get((code, tf, source), {})) or None - def get_rsi(self, code: str, tf: int, period: int = 3) -> Optional[float]: + def get_rsi(self, code: str, tf: int, period: int = 3, source: str = "kis") -> Optional[float]: """ 최신 확정 봉의 RSI(period) 값 반환. period: 2, 3, 5 중 하나 (스캘핑 단타용 초단기 RSI) """ - candle = self.get_latest_confirmed(code, tf) + candle = self.get_latest_confirmed(code, tf, source=source) if candle is None: return None return candle.get(f"rsi_{period}") @@ -2128,10 +2182,11 @@ class CandleAggregator: """ with self._lock: for tf in list(self.timeframes): - key = (code, tf) - self._confirmed.pop(key, None) - self._closes.pop(key, None) - self._current.pop(key, None) + for src in ("kis", "kiwoom", "ls", "ws", "kw_rest"): + key = (code, tf, src) + self._confirmed.pop(key, None) + self._closes.pop(key, None) + self._current.pop(key, None) logger.debug("🗑️ CandleAggregator RAM 정리: %s", code) diff --git a/kis_trader/ws/kiwoom_ws.py b/kis_trader/ws/kiwoom_ws.py index f409aed..cc14ae0 100644 --- a/kis_trader/ws/kiwoom_ws.py +++ b/kis_trader/ws/kiwoom_ws.py @@ -294,7 +294,14 @@ class KiwoomWebSocketPriceCache: return max(0.0, min(1.0, get_env_float("KIWOOM_WS_REG_DEBOUNCE_SEC", 0.12))) def _orderbook_ws_enabled(self) -> bool: - return get_env_bool("KIWOOM_WS_ORDERBOOK_ENABLED", True) + # 사용자의 혼동 방지를 위해 KIWOOM_WS_ORDERBOOK_ENABLED 대신, + # LIVE_OB_PROVIDER가 kiwoom이거나, 키움 호가 적재(WS_ORDERBOOK_SAVE_KIWOOM)가 켜져있으면 자동 수신. + from ..utils.env import get_env_from_db, get_env_bool + live_ob_provider = (get_env_from_db("LIVE_OB_PROVIDER", "kiwoom") or "kiwoom").strip().lower() + save_kiwoom = get_env_bool("WS_ORDERBOOK_SAVE_KIWOOM", True) + if live_ob_provider == "kiwoom" or save_kiwoom: + return True + return get_env_bool("KIWOOM_WS_ORDERBOOK_ENABLED", False) def _program_ws_enabled(self) -> bool: return get_env_bool("KIWOOM_WS_PROGRAM_ENABLED", True) @@ -594,6 +601,17 @@ class KiwoomWebSocketPriceCache: token = self._get_kiwoom_token() if not token: logger.warning("⚠️ 키움 토큰 발급 실패 → WS 연결 보류 (60s)") + try: + from kis_trader.utils.ops_alert import ops_alert + ops_alert( + "token_kiwoom", + "키움 토큰 발급 실패", + detail="시세 WS·조건검색 불가 → 60s 후 재시도", + level="critical", + session_only=False, + ) + except Exception: + pass time.sleep(60) return @@ -666,6 +684,17 @@ class KiwoomWebSocketPriceCache: self._fire_login_callbacks(ws) else: logger.warning("❌ 키움 WS LOGIN 실패 rc=%s msg=%s", rc, rm) + try: + from kis_trader.utils.ops_alert import ops_alert + ops_alert( + "ws_kiwoom_login", + "키움 WS LOGIN 실패", + detail=f"rc={rc} msg={rm}", + level="critical", + session_only=False, + ) + except Exception: + pass # 805004 등 서버 토큰 거부 → 캐시 만료 전이라도 무효화 후 재발급 if self._is_token_rejected_login(rc, rm): self._invalidate_token_cache( @@ -775,7 +804,7 @@ class KiwoomWebSocketPriceCache: filt = self._candle_agg_codes if filt is None or code in filt: try: - self._candle_agg.on_tick(code, price, tick_vol, tick_time) + self._candle_agg.on_tick(code, price, tick_vol, tick_time, source="kiwoom") except Exception as ex: logger.debug("키움→CandleAggregator on_tick 실패 %s: %s", code, ex) @@ -787,6 +816,25 @@ class KiwoomWebSocketPriceCache: ) except Exception as ex: logger.debug("키움→TickRecorder on_tick 실패 %s: %s", code, ex) + + # ── 호가 틱동기: 체결 1건당 RAM 0D 스냅 1장 (LS tick 모드와 동일) ── + if self._trigger_snapshot_recorder is not None: + try: + max_age = float( + getattr( + self._trigger_snapshot_recorder, + "orderbook_tick_max_age_sec", + 3.0, + ) + or 3.0 + ) + snap = self.get_orderbook_snapshot(code, max_age_sec=max_age) + if snap is not None: + self._trigger_snapshot_recorder.on_orderbook_tick_sync( + code, snap, snap_time=tick_time, + ) + except Exception as ex: + logger.debug("키움→호가틱동기 실패 %s: %s", code, ex) except (ValueError, TypeError) as e: logger.debug("키움 0B 파싱 오류 %s: %s", code, e) @@ -818,7 +866,7 @@ class KiwoomWebSocketPriceCache: def _send_reg(self, codes: list) -> bool: """REG 발송 (그룹 1, 0B+0D 타입). 한 번에 여러 종목 OK.""" - if not codes or not self._ws: + if not codes or not self._ws or not self._connected: return False try: self._ws.send(json.dumps({ @@ -839,7 +887,7 @@ class KiwoomWebSocketPriceCache: def _send_remove(self, codes: list) -> bool: """REMOVE 발송.""" - if not codes or not self._ws: + if not codes or not self._ws or not self._connected: return False try: self._ws.send(json.dumps({ diff --git a/kis_trader/ws/ls_ws.py b/kis_trader/ws/ls_ws.py index 4e444a8..4f28406 100644 --- a/kis_trader/ws/ls_ws.py +++ b/kis_trader/ws/ls_ws.py @@ -15,9 +15,12 @@ kis_trader/ws/ls_ws.py — LS증권 WebSocket 시세 캐시 (그림자 검증용 - A: ``LS_WS_TR_MODE=us3``(기본) → 통합 ``US3`` + ``U``+패딩. ``s3k3`` → ``S3_``/``K3_`` (실험용). - C: 생존 = **프로토콜 ping**(기본 20s). 앱 JSON 하트비트는 보내지 않음(스펙 없음). - 틱 silence 강제재연결은 **정규장 세션(JIF/벽시계)에서만** + 연속 실패 백오프. - ``JIF``(장운영정보) 구독으로 장상태 참고. + 틱 silence 강제재연결 = **벽시계 우선** (국장 정규창 / 해외 hold). + sticky JIF ``jstatus=21`` 만으로 장외 워치독 금지. + ``JIF`` 마감 상태는 벽시계 안에서만 추가 차단. - D: 재연결·REG 복구 중 ``is_recovering()`` — 매수 게이트는 env 로 선택. +- E: 국내/해외 **hold 창**(``ls_ws_session_windows``) — 갭·장외는 소켓 close 후 대기. + 세션 전환 시 **oauth 재발급 금지**(공용 토큰 무효화 방지, 키움 과거 사고와 동일 유형). 토글: ``LS_WS_VALIDATION_ENABLED`` (기본 false) """ @@ -58,6 +61,19 @@ LS_WS_MOCK = "wss://openapi.ls-sec.co.kr:29443/websocket" # JIF jstatus — 정규장 체결이 기대되는 상태 (스펙: 21=장시작) _JIF_STATUS_EXPECT_TICKS = frozenset({"21"}) +# AFR 세션 sticky — 장중 다른 jstatus(66/70 등)가 덮어써도 개장 sticky 유지. +# 실측(2026-07-31): 08:50~ 25/24/23/22 → 09:00 21 / 마감 30·31·41~44 +_JIF_AFR_OPEN_DEFAULT = frozenset({"21", "22", "23", "24", "25"}) +_JIF_AFR_CLOSE_DEFAULT = frozenset({"30", "31", "41", "42", "43", "44"}) + + +def _parse_jif_status_set(env_key: str, default: frozenset) -> frozenset: + raw = str(get_env_from_db(env_key, "") or "").strip() + if not raw: + return default + out = {p.strip() for p in raw.replace(";", ",").split(",") if p.strip()} + return frozenset(out) if out else default + TickRecorder = Callable[[str, Dict[str, Any]], None] CandleFlusher = Callable[[str, Dict[str, Any]], None] OrderbookRecorder = Callable[[str, Dict[str, Any]], None] @@ -130,6 +146,7 @@ class LSWebSocketPriceCache: self._thread: Optional[threading.Thread] = None self._reg_thread: Optional[threading.Thread] = None self._watch_thread: Optional[threading.Thread] = None + self._session_guard_thread: Optional[threading.Thread] = None self._running = False self._opened = threading.Event() @@ -156,6 +173,11 @@ class LSWebSocketPriceCache: self._jangubun: str = "" self._jstatus: str = "" self._jif_mono: float = 0.0 + # 국장 AFR용 sticky (해외 밤장과 무관 — LS JIF 만) + self._kr_afr_session_live: bool = False + self._kr_afr_session_src: str = "" + self._kr_afr_opened_ymd: str = "" # 당일 개장전/장시작 JIF 수신일 + self._kr_afr_closed_ymd: str = "" # 당일 마감 JIF 수신일 # 1분봉 롤업 (code → bucket) + 확정봉 RAM (전략 get_candles 호환) self._candle_lock = threading.Lock() @@ -235,7 +257,7 @@ class LSWebSocketPriceCache: return True try: self._token = fetch_ls_access_token( - self.app_key, self.app_secret, reason="ls_ws_start", + self.app_key, self.app_secret, force=False, reason="ls_ws_start", ) self._token_at = time.time() except Exception as e: @@ -254,15 +276,34 @@ class LSWebSocketPriceCache: self._watch_thread = threading.Thread( target=self._watchdog_loop, name="LS-WS-Watch", daemon=True ) + self._session_guard_thread = threading.Thread( + target=self._session_guard_loop, name="LS-WS-SessionGuard", daemon=True + ) self._thread.start() self._reg_thread.start() self._watch_thread.start() + self._session_guard_thread.start() + with _active_lock: + _active_ls_ws = self + # hold 외 기동: OPEN 대기하지 않음 — 루프가 다음 hold 까지 슬립 (재발급 없음) + if not self._should_hold_socket(): + from kis_trader.utils.ls_ws_session_windows import ( + seconds_until_ls_socket_open, + ) + + wait_sec = seconds_until_ls_socket_open( + n_us_subscribed=self._n_us_subscribed(), + ) + logger.info( + "✅ LS WS 기동(hold 외 대기) — 다음 hold까지 약 %.0f분 " + "(소켓 미연결·토큰 캐시 유지)", + wait_sec / 60.0, + ) + return True if not self._opened.wait(timeout=15.0): logger.error("LS WS OPEN timeout") self.stop() return False - with _active_lock: - _active_ls_ws = self logger.info( "✅ LS WS 연결 (%s mock=%s tr_mode=%s ping=%ss)", self.ws_url, @@ -377,7 +418,12 @@ class LSWebSocketPriceCache: self._ws.close() except Exception: pass - for th in (self._reg_thread, self._watch_thread, self._thread): + for th in ( + self._reg_thread, + self._watch_thread, + self._session_guard_thread, + self._thread, + ): if th is not None and th.is_alive(): try: th.join(timeout=2.0) @@ -787,13 +833,72 @@ class LSWebSocketPriceCache: # ── internals ───────────────────────────────────────────────────── def _ensure_token(self) -> str: - # 스펙 expires_in 공유 캐시 — 하드코딩 12h/익일07시 추측 금지 + # 스펙 expires_in 공유 캐시 — force 금지(세션 전환·재연결에서 공용 토큰 무효화 방지) self._token = fetch_ls_access_token( - self.app_key, self.app_secret, reason="ls_ws_ensure", + self.app_key, self.app_secret, force=False, reason="ls_ws_ensure", ) self._token_at = time.time() return self._token + def _force_close_socket(self, reason: str) -> None: + """hold 이탈·갭 — 소켓만 양보. 토큰 revoke/재발급 없음.""" + ws = self._ws + if ws is None: + return + try: + logger.info("🔌 [LS WS] 세션 양보 close (%s)", reason) + ws.close() + except Exception as exc: + logger.debug("LS WS force close 예외: %s", exc) + + def _n_us_subscribed(self) -> int: + with self._sub_lock: + return len(self._us_subscribed) + + def _should_hold_socket(self, now: Optional[datetime] = None) -> bool: + """국내 hold 또는 (해외구독+해외 hold). ``LS_WS_SESSION_HOLD_ENABLED=false`` 면 항상 True.""" + if not get_env_bool("LS_WS_SESSION_HOLD_ENABLED", True): + return True + from kis_trader.utils.ls_ws_session_windows import should_hold_ls_socket + + return should_hold_ls_socket( + n_us_subscribed=self._n_us_subscribed(), + now=now, + ) + + def _session_guard_loop(self) -> None: + """hold 창 이탈 시 소켓 능동 종료 — oauth 재발급 없음.""" + from kis_trader.utils.ls_ws_session_windows import ( + in_us_ws_hold_window, + session_guard_interval_sec, + ) + + while self._running: + try: + time.sleep(session_guard_interval_sec()) + except Exception: + time.sleep(15.0) + if not self._running: + break + if not get_env_bool("LS_WS_SESSION_HOLD_ENABLED", True): + continue + if not self._opened.is_set(): + continue + if self._should_hold_socket(): + continue + why = "해외hold외" if not in_us_ws_hold_window() else "KR hold외" + self._force_close_socket("%s → 소켓 양보(토큰 유지)" % why) + + def _in_kr_watchdog_wallclock(self, now: Optional[datetime] = None) -> bool: + """국장 틱 silence 워치독용 정규장 벽시계 (기본 09:00~15:25, 월~금).""" + n = now or datetime.now() + if n.weekday() >= 5: + return False + hhmm = n.hour * 100 + n.minute + start = int(get_env_int("LS_WS_WATCHDOG_SESSION_START_HM", 900) or 900) + end = int(get_env_int("LS_WS_WATCHDOG_SESSION_END_HM", 1525) or 1525) + return start <= hhmm <= end + def _send_raw(self, payload: dict) -> None: if self._ws is None or not self._opened.is_set(): return @@ -829,36 +934,140 @@ class LSWebSocketPriceCache: "body": {"tr_cd": tr_cd, "tr_key": tr_key}, }) - def _session_expects_trade_ticks(self) -> bool: - """틱 silence 강제재연결을 허용할 세션인가 (VI·점심·장외 오탐 방지). + def _session_expects_trade_ticks(self, now: Optional[datetime] = None) -> bool: + """틱 silence 강제재연결을 허용할 세션인가 (VI·장외 오탐 방지). - ``LS_WS_WATCHDOG_SESSION_GATE=false`` 이면 항상 True(레거시). - - JIF ``jstatus=21``(장시작) 이면 True. - - JIF 미수신 시 벽시계 정규장 창(기본 09:00~15:25) 폴백. + - **벽시계 우선**: sticky JIF ``jstatus=21`` 만으로 장외 True 금지. + - 국장 구독: 정규창(기본 09:00~15:25) + 당일 마감 JIF/close 상태면 차단. + - 해외 구독: ``LS_WS_US_HOLD_*`` 창에서만. """ if not get_env_bool("LS_WS_WATCHDOG_SESSION_GATE", True): return True - with self._jif_lock: - st = str(self._jstatus or "").strip() - got_jif = bool(self._jif_mono > 0) - if got_jif: - return st in _JIF_STATUS_EXPECT_TICKS + now = now or datetime.now() + ymd = now.strftime("%Y%m%d") + with self._sub_lock: + n_kr = len(self._subscribed) + n_us = len(self._us_subscribed) + kr_ok = False + if n_kr > 0 and self._in_kr_watchdog_wallclock(now): + close_set = _parse_jif_status_set( + "LS_AFR_JIF_CLOSE_STATUSES", _JIF_AFR_CLOSE_DEFAULT, + ) + with self._jif_lock: + st = str(self._jstatus or "").strip() + closed = str(self._kr_afr_closed_ymd or "") + # 벽시계 안이면 기본 OK. sticky JIF 21 로 장외 연장 안 함. + # 당일 마감 JIF/close status 만 추가 차단. + if closed == ymd: + kr_ok = False + elif st and st in close_set: + kr_ok = False + else: + kr_ok = True + us_ok = False + if n_us > 0: + from kis_trader.utils.ls_ws_session_windows import in_us_ws_hold_window + + us_ok = in_us_ws_hold_window(now) + return bool(kr_ok or us_ok) + + def is_kr_afr_session_open(self) -> bool: + """국장 LS 조건 AFR(t1860) 허용 세션인가. + + 해외 밤장과 무관. **장전 준비** + 마감 후 차단. + + - 평일 ``LS_AFR_SESSION_START_HM``(기본 07:00)~END(15:30) + - **장전**: JIF 22~25 오기 전에도 START~``OPEN_DEADLINE``(기본 09:30) 전은 허용 + → AFR/스냅샷 미리 준비 (09:00 jstatus=21 기다리면 늦음) + - 개장 JIF(21~25) → sticky ON / opened_ymd + - 마감 JIF(30·31·41~44) → closed_ymd → 당일 재시도 금지 + - DEADLINE 지나도 개장 JIF 없으면 휴장으로 보고 OFF (연타 방지) + """ now = datetime.now() if now.weekday() >= 5: return False hhmm = now.hour * 100 + now.minute - start = int(get_env_int("LS_WS_WATCHDOG_SESSION_START_HM", 900) or 900) - end = int(get_env_int("LS_WS_WATCHDOG_SESSION_END_HM", 1525) or 1525) - return start <= hhmm <= end + start = int(get_env_int("LS_AFR_SESSION_START_HM", 700) or 700) + end = int(get_env_int("LS_AFR_SESSION_END_HM", 1530) or 1530) + deadline = int(get_env_int("LS_AFR_OPEN_DEADLINE_HM", 930) or 930) + if not (start <= hhmm <= end): + return False + ymd = now.strftime("%Y%m%d") + with self._jif_lock: + live = bool(self._kr_afr_session_live) + opened = str(self._kr_afr_opened_ymd or "") + closed = str(self._kr_afr_closed_ymd or "") + # 당일 마감 후에는 상관없음(재시도 불필요) + if closed == ymd: + return False + # 당일 개장 신호 있으면 장중 유지 (jstatus 66/70 덮어쓰기 무시) + if live or opened == ymd: + return True + # 장전 준비 구간 — sticky 없어도 허용 + if hhmm < deadline: + return True + # 장중 재기동: JIF sticky 유실(재시작 직후 jstatus 없음). + # 구로직은 DEADLINE 후 sticky 없으면 휴장으로 OFF → AFR 영구 0 + 주기 t1859 + # 끄면 LS 유니버스 동결. 거래일·미마감이면 벽시계 장중 허용. + try: + from kis_trader.utils.kr_trading_day import is_kr_trading_day + + if is_kr_trading_day(now.date()): + return True + except Exception: + pass + return False + + def get_jif_snapshot(self) -> Dict[str, Any]: + """진단용 — jangubun/jstatus/AFR sticky.""" + with self._jif_lock: + return { + "jangubun": self._jangubun, + "jstatus": self._jstatus, + "jif_age_sec": ( + (time.monotonic() - self._jif_mono) if self._jif_mono > 0 else None + ), + "kr_afr_session_live": bool(self._kr_afr_session_live), + "kr_afr_session_src": self._kr_afr_session_src, + "kr_afr_opened_ymd": self._kr_afr_opened_ymd, + "kr_afr_closed_ymd": self._kr_afr_closed_ymd, + } def _on_jif(self, body: Dict[str, Any]) -> None: jangubun = str(body.get("jangubun") or "").strip() jstatus = str(body.get("jstatus") or "").strip() + open_set = _parse_jif_status_set( + "LS_AFR_JIF_OPEN_STATUSES", _JIF_AFR_OPEN_DEFAULT, + ) + close_set = _parse_jif_status_set( + "LS_AFR_JIF_CLOSE_STATUSES", _JIF_AFR_CLOSE_DEFAULT, + ) + sticky_changed = False + ymd = datetime.now().strftime("%Y%m%d") with self._jif_lock: self._jangubun = jangubun self._jstatus = jstatus self._jif_mono = time.monotonic() - logger.info("LS JIF 장운영 jangubun=%s jstatus=%s", jangubun, jstatus) + prev = bool(self._kr_afr_session_live) + if jstatus in open_set: + self._kr_afr_session_live = True + self._kr_afr_opened_ymd = ymd + # 다음날 개장 대비 — 당일 마감 플래그는 개장 시 클리어 + if self._kr_afr_closed_ymd == ymd: + self._kr_afr_closed_ymd = "" + self._kr_afr_session_src = f"open:{jangubun}/{jstatus}" + elif jstatus in close_set: + self._kr_afr_session_live = False + self._kr_afr_closed_ymd = ymd + self._kr_afr_session_src = f"close:{jangubun}/{jstatus}" + sticky_changed = prev != bool(self._kr_afr_session_live) + live = bool(self._kr_afr_session_live) + logger.info( + "LS JIF 장운영 jangubun=%s jstatus=%s afr_live=%s%s", + jangubun, jstatus, live, + " (sticky변경)" if sticky_changed else "", + ) def _normalize_kr_code(self, raw: str) -> str: code = str(raw or "").strip() @@ -873,7 +1082,10 @@ class LSWebSocketPriceCache: return code def _on_hoga(self, tr_cd: str, body: Dict[str, Any]) -> None: - """UH1(통합호가) 등 — RAM 갱신 + (옵션) ls_ws_orderbook 스로틀 저장.""" + """UH1(통합호가) 등 — RAM 갱신 + (옵션) interval 모드 DB 저장. + + tick 모드(기본)는 체결 콜백에서 틱과 1:1 저장하므로 여기선 DB INSERT 안 함. + """ if self._orderbook_cache is None: return code = self._normalize_kr_code( @@ -892,6 +1104,12 @@ class LSWebSocketPriceCache: return if not get_env_bool("LS_WS_ORDERBOOK_SAVE", True): return + mode = ( + get_env_from_db("LS_WS_ORDERBOOK_SAVE_MODE", "tick") or "tick" + ).strip().lower() + # tick / on_tick / tick_sync → 체결 경로에서만 저장 (여기는 RAM만) + if mode in ("tick", "on_tick", "tick_sync", "sync"): + return gap_ms = max(0, int(get_env_int("LS_WS_ORDERBOOK_SAVE_MS", 1000) or 1000)) now_m = time.monotonic() last = self._ob_last_save_mono.get(code, 0.0) @@ -976,6 +1194,22 @@ class LSWebSocketPriceCache: tr_cd, code, vi_g, krx_g or "-", nxt_g or "-", active, ) + if active and get_env_bool("OPS_ALERT_VI_ENABLED", True): + try: + from kis_trader.utils.ops_alert import ops_alert + ops_alert( + f"vi_{code}", + f"VI 발동 {code}", + detail=( + f"tr={tr_cd} gubun={vi_g} krx={krx_g or '-'} " + f"nxt={nxt_g or '-'} trg={trg}" + ), + level="warn", + session_only=False, + ) + except Exception: + pass + if not self._vi_recorder or not get_env_bool("LS_WS_VI_SAVE", True): return payload = { @@ -1105,6 +1339,19 @@ class LSWebSocketPriceCache: silence, jst, ) + try: + from kis_trader.utils.ops_alert import ops_alert + ops_alert( + "ws_tick_silence", + "LS 시세 WS 틱 공백 → 강제 재연결", + detail=( + f"silence={int(now - self._last_tick_mono)}s " + f"subs={n_sub} streak={self._watchdog_empty_streak} jstatus={jst}" + ), + level="critical", + ) + except Exception: + pass self._set_recovering(True) try: if self._ws is not None: @@ -1115,9 +1362,46 @@ class LSWebSocketPriceCache: def _run_forever(self) -> None: backoff = 1.0 while self._running: + # ── hold 외 → 소켓 양보 후 다음 국내/해외 창까지 대기 (재발급 없음) ── + if not self._should_hold_socket(): + if self._opened.is_set() or self._ws is not None: + self._force_close_socket("hold 외 — 루프 대기 전 양보") + from kis_trader.utils.ls_ws_session_windows import ( + seconds_until_ls_socket_open, + ) + + wait_sec = float( + seconds_until_ls_socket_open( + n_us_subscribed=self._n_us_subscribed(), + ) + ) + logger.info( + "🌙 LS WS hold 외 — 재연결 중지, 다음 hold까지 %.0f분 대기 " + "(토큰 캐시 유지·재발급 없음)", + wait_sec / 60.0, + ) + self._opened.clear() + self._set_recovering(True) + backoff = 1.0 + self._watchdog_empty_streak = 0 + for _ in range(int(wait_sec // 60)): + if not self._running: + return + time.sleep(60) + time.sleep(wait_sec % 60) + continue + try: self._opened.clear() self._set_recovering(True) + # 만료/임박만 갱신 — force 없음 (세션 전환·재연결이 공용 토큰 무효화 금지) + try: + self._ensure_token() + except Exception as e: + logger.warning("LS 토큰 ensure 실패(캐시/한도): %s", e) + time.sleep(min(backoff, 60.0)) + backoff = min(backoff * 2, 60.0) + continue self._ws = self._websocket.WebSocketApp( self.ws_url, on_open=self._on_open, @@ -1140,12 +1424,11 @@ class LSWebSocketPriceCache: self._set_recovering(True) if not self._running: break + # hold 이탈로 끊긴 경우 즉시 대기 분기로 (불필요 재발급·재연결 폭주 방지) + if not self._should_hold_socket(): + continue time.sleep(min(backoff, 60.0)) backoff = min(backoff * 2, 60.0) - try: - self._ensure_token() - except Exception as e: - logger.warning("LS 토큰 재발급 실패: %s", e) def _on_open(self, _ws: Any) -> None: # 콜백에서 sleep/REG 연타 금지 → 워커에 replay 위임 diff --git a/kis_trader/ws/trigger_snapshot_recorder.py b/kis_trader/ws/trigger_snapshot_recorder.py index 460d774..d4787ac 100644 --- a/kis_trader/ws/trigger_snapshot_recorder.py +++ b/kis_trader/ws/trigger_snapshot_recorder.py @@ -21,16 +21,31 @@ logger = get_logger("kis_trader.trigger_snapshot_recorder") class TriggerSnapshotRecorder: - """키움 0D/0w 스냅샷 기록기 — 후보 종목만 필터·종목당 최소 간격 스로틀.""" + """키움 0D/0w 스냅샷 기록기 — 후보 종목만 필터·종목당 최소 간격 스로틀. + + ``WS_ORDERBOOK_SAVE_MODE``: + - ``tick`` (기본): 체결(0B)마다 ``on_orderbook_tick_sync`` — 0D 경로 DB 저장 안 함 + - ``interval``: 0D 수신 + ``WS_TRIGGER_SNAPSHOT_DB_INTERVAL_SEC`` 스로틀 + """ + + _TICK_MODES = frozenset({"tick", "on_tick", "tick_sync", "sync"}) def __init__(self, db=None) -> None: self.db = db - self._ob_enabled = get_env_bool("WS_ORDERBOOK_SAVE_ENABLED", False) + self._ob_enabled = True # 인스턴스가 부착되었다면 무조건 활성화된 것으로 간주 (설정은 부착 측에서 제어) self._pg_enabled = get_env_bool("WS_PROGRAM_SAVE_ENABLED", False) + self._ob_mode = ( + get_env_from_db("WS_ORDERBOOK_SAVE_MODE", "tick") or "tick" + ).strip().lower() self._interval_sec = max( - 0.5, + 0.0, get_env_float("WS_TRIGGER_SNAPSHOT_DB_INTERVAL_SEC", 3.0), ) + if self._ob_mode not in self._TICK_MODES: + self._interval_sec = max(0.5, self._interval_sec) + self._tick_max_age = float( + get_env_float("WS_ORDERBOOK_TICK_MAX_AGE_SEC", 3.0) or 3.0 + ) self._batch_size = get_env_int("WS_TRIGGER_SNAPSHOT_DB_BATCH_SIZE", 100) self._flush_sec = get_env_float("WS_TRIGGER_SNAPSHOT_DB_FLUSH_SEC", 3.0) self._queue_max = get_env_int("WS_TRIGGER_SNAPSHOT_WRITE_QUEUE_MAX", 20000) @@ -53,8 +68,8 @@ class TriggerSnapshotRecorder: self._ensure_tables() self._start_writer() logger.info( - "✅ TriggerSnapshotRecorder 활성 (ob=%s pg=%s interval=%.1fs keep=%d/%dd)", - self._ob_enabled, self._pg_enabled, self._interval_sec, + "✅ TriggerSnapshotRecorder 활성 (ob=%s mode=%s pg=%s interval=%.1fs keep=%d/%dd)", + self._ob_enabled, self._ob_mode, self._pg_enabled, self._interval_sec, self._ob_keep_days, self._pg_keep_days, ) elif self._ob_enabled or self._pg_enabled: @@ -70,7 +85,10 @@ class TriggerSnapshotRecorder: self._record_codes = {str(c).strip() for c in codes if c} def on_orderbook(self, snap: OrderbookSnapshot, snap_time: Optional[str] = None) -> None: - if not self._ob_enabled or self.db is None or snap is None: + """0D 수신 경로. tick 모드에서는 DB 저장 안 함(RAM만 kiwoom_ws 에서 갱신).""" + if self.db is None or snap is None: + return + if self._ob_mode in self._TICK_MODES: return code = (snap.code or "").strip() if not code: @@ -80,12 +98,35 @@ class TriggerSnapshotRecorder: now = time.time() with self._lock: last = self._last_ob_save.get(code, 0.0) - if now - last < self._interval_sec: + if self._interval_sec > 0 and now - last < self._interval_sec: return self._last_ob_save[code] = now item = self._build_orderbook_item(snap, snap_time) self._enqueue(item) + def on_orderbook_tick_sync( + self, + code: str, + snap: Optional[OrderbookSnapshot], + snap_time: Optional[str] = None, + ) -> None: + """체결(0B) 1건당 호가 RAM 스냅 1장 — LS ``LS_WS_ORDERBOOK_SAVE_MODE=tick`` 과 동일.""" + if self.db is None: + return + if self._ob_mode not in self._TICK_MODES: + return + code = (code or "").strip() + if not code or snap is None: + return + if not self._should_record(code): + return + item = self._build_orderbook_item(snap, snap_time) + self._enqueue(item) + + @property + def orderbook_tick_max_age_sec(self) -> float: + return float(self._tick_max_age) + def on_program(self, snap: ProgramSnapshot, snap_time: Optional[str] = None) -> None: if not self._pg_enabled or self.db is None or snap is None: return @@ -97,7 +138,8 @@ class TriggerSnapshotRecorder: now = time.time() with self._lock: last = self._last_pg_save.get(code, 0.0) - if now - last < self._interval_sec: + gap = max(0.5, self._interval_sec) if self._interval_sec > 0 else 0.5 + if now - last < gap: return self._last_pg_save[code] = now item = self._build_program_item(snap, snap_time) diff --git a/logs/kis_api_rejects.jsonl b/logs/kis_api_rejects.jsonl index b8a059c..a75ea36 100644 --- a/logs/kis_api_rejects.jsonl +++ b/logs/kis_api_rejects.jsonl @@ -2217,3 +2217,2656 @@ {"ts": "2026-08-01T05:59:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1018}} {"ts": "2026-08-01T05:59:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2793}} {"ts": "2026-08-01T06:00:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2828}} +{"ts": "2026-08-03T09:58:06+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "052860", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "03ee94d6d4e13a8b", "extra": {"mock": true}} +{"ts": "2026-08-03T11:48:29+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "008490", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "3523e3fafe35cbe0", "extra": {"mock": true}} +{"ts": "2026-08-03T13:09:34+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "000950", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "d211a1831ace9752", "extra": {"mock": true}} +{"ts": "2026-08-03T13:23:51+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "004710", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "730881e9f7ec1eeb", "extra": {"mock": true}} +{"ts": "2026-08-03T15:10:55+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "052860", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "03ee94d6d4e13a8b", "extra": {"mock": true}} +{"ts": "2026-08-03T15:11:22+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "085310", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "61fa35f879bcb059", "extra": {"mock": true}} +{"ts": "2026-08-03T15:17:41+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "025530", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "dea0b06ec581079e", "extra": {"mock": true}} +{"ts": "2026-08-03T15:17:44+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "043710", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "d6d253b6e5a2336d", "extra": {"mock": true}} +{"ts": "2026-08-03T15:17:46+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "053160", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "55a186f9aba8f36c", "extra": {"mock": true}} +{"ts": "2026-08-03T15:17:56+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "000950", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "d211a1831ace9752", "extra": {"mock": true}} +{"ts": "2026-08-03T15:25:25+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "064850", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "f8ef47978763625f", "extra": {"mock": true}} +{"ts": "2026-08-03T15:25:29+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "123330", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "802156180694d53c", "extra": {"mock": true}} +{"ts": "2026-08-03T23:22:05+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-03T23:22:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3590}} +{"ts": "2026-08-03T23:25:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3404}} +{"ts": "2026-08-03T23:28:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3222}} +{"ts": "2026-08-03T23:31:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3033}} +{"ts": "2026-08-03T23:34:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2850}} +{"ts": "2026-08-03T23:37:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2667}} +{"ts": "2026-08-03T23:40:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2481}} +{"ts": "2026-08-03T23:43:46+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2299}} +{"ts": "2026-08-03T23:46:46+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2119}} +{"ts": "2026-08-03T23:50:03+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-03T23:50:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3598}} +{"ts": "2026-08-03T23:53:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3417}} +{"ts": "2026-08-03T23:56:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3237}} +{"ts": "2026-08-03T23:59:03+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-03T23:59:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3598}} +{"ts": "2026-08-04T00:02:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3418}} +{"ts": "2026-08-04T00:05:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3237}} +{"ts": "2026-08-04T00:08:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3056}} +{"ts": "2026-08-04T00:11:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2875}} +{"ts": "2026-08-04T00:14:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2694}} +{"ts": "2026-08-04T00:17:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2513}} +{"ts": "2026-08-04T00:20:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2332}} +{"ts": "2026-08-04T00:23:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2151}} +{"ts": "2026-08-04T00:26:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1970}} +{"ts": "2026-08-04T00:29:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1789}} +{"ts": "2026-08-04T00:32:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1608}} +{"ts": "2026-08-04T00:35:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1428}} +{"ts": "2026-08-04T00:38:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1247}} +{"ts": "2026-08-04T00:41:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1066}} +{"ts": "2026-08-04T00:44:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 886}} +{"ts": "2026-08-04T00:47:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 705}} +{"ts": "2026-08-04T00:50:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 525}} +{"ts": "2026-08-04T00:53:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 344}} +{"ts": "2026-08-04T00:56:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 164}} +{"ts": "2026-08-04T00:59:04+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-04T00:59:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3585}} +{"ts": "2026-08-04T01:02:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3404}} +{"ts": "2026-08-04T01:05:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3224}} +{"ts": "2026-08-04T01:08:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3044}} +{"ts": "2026-08-04T01:11:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2863}} +{"ts": "2026-08-04T01:14:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2682}} +{"ts": "2026-08-04T01:17:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2502}} +{"ts": "2026-08-04T01:20:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2322}} +{"ts": "2026-08-04T01:23:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2141}} +{"ts": "2026-08-04T01:26:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1961}} +{"ts": "2026-08-04T01:29:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1780}} +{"ts": "2026-08-04T01:32:02+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-04T01:32:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3598}} +{"ts": "2026-08-04T01:32:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1599}} +{"ts": "2026-08-04T01:35:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3418}} +{"ts": "2026-08-04T01:35:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1419}} +{"ts": "2026-08-04T01:38:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3238}} +{"ts": "2026-08-04T01:38:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1238}} +{"ts": "2026-08-04T01:41:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3057}} +{"ts": "2026-08-04T01:41:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1058}} +{"ts": "2026-08-04T01:44:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2877}} +{"ts": "2026-08-04T01:44:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 878}} +{"ts": "2026-08-04T01:47:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2696}} +{"ts": "2026-08-04T01:47:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 697}} +{"ts": "2026-08-04T01:50:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2516}} +{"ts": "2026-08-04T01:50:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 516}} +{"ts": "2026-08-04T01:53:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2335}} +{"ts": "2026-08-04T01:53:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 335}} +{"ts": "2026-08-04T01:56:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2154}} +{"ts": "2026-08-04T01:56:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 154}} +{"ts": "2026-08-04T01:59:06+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-04T01:59:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1974}} +{"ts": "2026-08-04T01:59:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3576}} +{"ts": "2026-08-04T02:02:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1794}} +{"ts": "2026-08-04T02:02:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3396}} +{"ts": "2026-08-04T02:05:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1613}} +{"ts": "2026-08-04T02:05:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3215}} +{"ts": "2026-08-04T02:08:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1432}} +{"ts": "2026-08-04T02:08:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3034}} +{"ts": "2026-08-04T02:11:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1251}} +{"ts": "2026-08-04T02:11:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2853}} +{"ts": "2026-08-04T02:14:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1071}} +{"ts": "2026-08-04T02:14:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2673}} +{"ts": "2026-08-04T02:17:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 891}} +{"ts": "2026-08-04T02:17:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2492}} +{"ts": "2026-08-04T02:20:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 711}} +{"ts": "2026-08-04T02:20:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2311}} +{"ts": "2026-08-04T02:23:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 531}} +{"ts": "2026-08-04T02:23:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2130}} +{"ts": "2026-08-04T02:26:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 349}} +{"ts": "2026-08-04T02:26:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1950}} +{"ts": "2026-08-04T02:29:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 168}} +{"ts": "2026-08-04T02:29:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1770}} +{"ts": "2026-08-04T02:32:04+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-04T02:32:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3589}} +{"ts": "2026-08-04T02:32:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1589}} +{"ts": "2026-08-04T02:35:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3408}} +{"ts": "2026-08-04T02:35:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1408}} +{"ts": "2026-08-04T02:38:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3227}} +{"ts": "2026-08-04T02:38:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1227}} +{"ts": "2026-08-04T02:41:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3047}} +{"ts": "2026-08-04T02:41:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1047}} +{"ts": "2026-08-04T02:44:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2867}} +{"ts": "2026-08-04T02:44:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 866}} +{"ts": "2026-08-04T02:47:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2687}} +{"ts": "2026-08-04T02:47:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 686}} +{"ts": "2026-08-04T02:50:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2505}} +{"ts": "2026-08-04T02:50:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 506}} +{"ts": "2026-08-04T02:53:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2325}} +{"ts": "2026-08-04T02:53:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 325}} +{"ts": "2026-08-04T02:56:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2145}} +{"ts": "2026-08-04T02:56:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 144}} +{"ts": "2026-08-04T02:59:07+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-04T02:59:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1964}} +{"ts": "2026-08-04T02:59:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3566}} +{"ts": "2026-08-04T03:02:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1783}} +{"ts": "2026-08-04T03:02:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3385}} +{"ts": "2026-08-04T03:05:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1601}} +{"ts": "2026-08-04T03:05:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3204}} +{"ts": "2026-08-04T03:08:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1421}} +{"ts": "2026-08-04T03:08:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3023}} +{"ts": "2026-08-04T03:11:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1241}} +{"ts": "2026-08-04T03:11:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2842}} +{"ts": "2026-08-04T03:14:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1060}} +{"ts": "2026-08-04T03:14:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2661}} +{"ts": "2026-08-04T03:17:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 878}} +{"ts": "2026-08-04T03:17:46+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2481}} +{"ts": "2026-08-04T03:20:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 698}} +{"ts": "2026-08-04T03:20:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2300}} +{"ts": "2026-08-04T03:23:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 518}} +{"ts": "2026-08-04T03:23:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2120}} +{"ts": "2026-08-04T03:26:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 336}} +{"ts": "2026-08-04T03:26:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1940}} +{"ts": "2026-08-04T03:29:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 156}} +{"ts": "2026-08-04T03:29:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1760}} +{"ts": "2026-08-04T03:32:05+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-04T03:32:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3575}} +{"ts": "2026-08-04T03:32:48+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1579}} +{"ts": "2026-08-04T03:35:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3395}} +{"ts": "2026-08-04T03:35:49+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1398}} +{"ts": "2026-08-04T03:38:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3214}} +{"ts": "2026-08-04T03:38:49+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1218}} +{"ts": "2026-08-04T03:41:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3034}} +{"ts": "2026-08-04T03:41:50+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1037}} +{"ts": "2026-08-04T03:44:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2852}} +{"ts": "2026-08-04T03:44:50+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 857}} +{"ts": "2026-08-04T03:47:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2671}} +{"ts": "2026-08-04T03:47:50+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 677}} +{"ts": "2026-08-04T03:50:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2491}} +{"ts": "2026-08-04T03:50:50+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 497}} +{"ts": "2026-08-04T03:53:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2311}} +{"ts": "2026-08-04T03:53:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 316}} +{"ts": "2026-08-04T03:56:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2131}} +{"ts": "2026-08-04T03:56:52+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 135}} +{"ts": "2026-08-04T03:59:08+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-04T03:59:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1949}} +{"ts": "2026-08-04T03:59:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3555}} +{"ts": "2026-08-04T04:02:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1769}} +{"ts": "2026-08-04T04:02:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3375}} +{"ts": "2026-08-04T04:05:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1588}} +{"ts": "2026-08-04T04:05:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3194}} +{"ts": "2026-08-04T04:08:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1408}} +{"ts": "2026-08-04T04:08:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3014}} +{"ts": "2026-08-04T04:11:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1227}} +{"ts": "2026-08-04T04:11:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2833}} +{"ts": "2026-08-04T04:14:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1047}} +{"ts": "2026-08-04T04:14:55+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2652}} +{"ts": "2026-08-04T04:17:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 866}} +{"ts": "2026-08-04T04:17:56+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2472}} +{"ts": "2026-08-04T04:20:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 685}} +{"ts": "2026-08-04T04:20:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2291}} +{"ts": "2026-08-04T04:23:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 505}} +{"ts": "2026-08-04T04:23:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2110}} +{"ts": "2026-08-04T04:26:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 325}} +{"ts": "2026-08-04T04:26:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1930}} +{"ts": "2026-08-04T04:29:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 144}} +{"ts": "2026-08-04T04:29:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1749}} +{"ts": "2026-08-04T04:32:05+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-04T04:32:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3564}} +{"ts": "2026-08-04T04:32:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1569}} +{"ts": "2026-08-04T04:35:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3383}} +{"ts": "2026-08-04T04:35:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1389}} +{"ts": "2026-08-04T04:38:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3203}} +{"ts": "2026-08-04T04:38:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1209}} +{"ts": "2026-08-04T04:41:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3022}} +{"ts": "2026-08-04T04:41:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1028}} +{"ts": "2026-08-04T04:44:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2841}} +{"ts": "2026-08-04T04:45:00+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 848}} +{"ts": "2026-08-04T04:47:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2661}} +{"ts": "2026-08-04T04:48:00+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 667}} +{"ts": "2026-08-04T04:50:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2481}} +{"ts": "2026-08-04T04:51:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 486}} +{"ts": "2026-08-04T04:53:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2301}} +{"ts": "2026-08-04T04:54:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 306}} +{"ts": "2026-08-04T04:56:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2120}} +{"ts": "2026-08-04T04:57:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 125}} +{"ts": "2026-08-04T04:59:09+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-04T04:59:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1939}} +{"ts": "2026-08-04T05:00:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3545}} +{"ts": "2026-08-04T05:02:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1759}} +{"ts": "2026-08-04T05:03:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3365}} +{"ts": "2026-08-04T05:05:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1577}} +{"ts": "2026-08-04T05:06:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3184}} +{"ts": "2026-08-04T05:08:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1397}} +{"ts": "2026-08-04T05:09:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3003}} +{"ts": "2026-08-04T05:11:49+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1216}} +{"ts": "2026-08-04T05:12:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2822}} +{"ts": "2026-08-04T05:14:50+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1035}} +{"ts": "2026-08-04T05:15:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2642}} +{"ts": "2026-08-04T05:17:50+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 855}} +{"ts": "2026-08-04T05:18:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2462}} +{"ts": "2026-08-04T05:20:50+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 675}} +{"ts": "2026-08-04T05:21:03+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPYM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "41e444bf93130aef", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-04T05:21:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3599}} +{"ts": "2026-08-04T05:21:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2282}} +{"ts": "2026-08-04T05:23:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 494}} +{"ts": "2026-08-04T05:24:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3419}} +{"ts": "2026-08-04T05:24:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2101}} +{"ts": "2026-08-04T05:26:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 314}} +{"ts": "2026-08-04T05:27:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3238}} +{"ts": "2026-08-04T05:27:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1920}} +{"ts": "2026-08-04T05:29:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 133}} +{"ts": "2026-08-04T05:30:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3058}} +{"ts": "2026-08-04T05:30:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1739}} +{"ts": "2026-08-04T05:32:06+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-04T05:32:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3555}} +{"ts": "2026-08-04T05:33:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2877}} +{"ts": "2026-08-04T05:33:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1558}} +{"ts": "2026-08-04T05:35:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3373}} +{"ts": "2026-08-04T05:36:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2696}} +{"ts": "2026-08-04T05:36:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1377}} +{"ts": "2026-08-04T05:38:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3193}} +{"ts": "2026-08-04T05:39:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2516}} +{"ts": "2026-08-04T05:39:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1197}} +{"ts": "2026-08-04T05:41:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3012}} +{"ts": "2026-08-04T05:42:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2335}} +{"ts": "2026-08-04T05:42:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1017}} +{"ts": "2026-08-04T05:44:55+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2831}} +{"ts": "2026-08-04T05:45:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2154}} +{"ts": "2026-08-04T05:45:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 837}} +{"ts": "2026-08-04T05:47:55+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2651}} +{"ts": "2026-08-04T05:48:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1974}} +{"ts": "2026-08-04T05:48:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 657}} +{"ts": "2026-08-04T05:50:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2469}} +{"ts": "2026-08-04T05:51:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1792}} +{"ts": "2026-08-04T05:51:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 476}} +{"ts": "2026-08-04T05:53:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2289}} +{"ts": "2026-08-04T05:54:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1612}} +{"ts": "2026-08-04T05:54:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 295}} +{"ts": "2026-08-04T05:56:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2109}} +{"ts": "2026-08-04T05:57:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1430}} +{"ts": "2026-08-04T05:57:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 115}} +{"ts": "2026-08-04T05:59:09+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-04T05:59:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1929}} +{"ts": "2026-08-04T09:00:32+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "000950", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "d211a1831ace9752", "extra": {"mock": true}} +{"ts": "2026-08-04T09:00:33+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "025530", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "dea0b06ec581079e", "extra": {"mock": true}} +{"ts": "2026-08-04T09:00:35+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "043710", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "d6d253b6e5a2336d", "extra": {"mock": true}} +{"ts": "2026-08-04T11:19:59+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "096250", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "f1bec13612f019fc", "extra": {"mock": true}} +{"ts": "2026-08-04T12:15:15+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "033560", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "153f2b68bc0fa15c", "extra": {"mock": true}} +{"ts": "2026-08-04T13:09:04+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "000700", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "62dd611668d1f244", "extra": {"mock": true}} +{"ts": "2026-08-04T13:09:44+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "002460", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "aeb1c96a14296c2f", "extra": {"mock": true}} +{"ts": "2026-08-04T13:09:52+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "033250", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "6c3f4e83a20919eb", "extra": {"mock": true}} +{"ts": "2026-08-04T13:10:34+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "002220", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "7bd1dbd7529e3f78", "extra": {"mock": true}} +{"ts": "2026-08-04T13:36:49+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "046120", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "8c55ecf258c162bd", "extra": {"mock": true}} +{"ts": "2026-08-04T15:20:34+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "030530", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "34e5fb5f3fe6c1ec", "extra": {"mock": true}} +{"ts": "2026-08-04T15:22:12+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "223250", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "0a4f466102950357", "extra": {"mock": true}} +{"ts": "2026-08-04T15:22:23+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "327260", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "6eac5414e41b71e8", "extra": {"mock": true}} +{"ts": "2026-08-04T15:22:32+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "418470", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "62f3113ea1cdee65", "extra": {"mock": true}} +{"ts": "2026-08-04T15:25:28+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "298000", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "a7998ca5f729d574", "extra": {"mock": true}} +{"ts": "2026-08-04T22:37:52+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-04T22:37:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3598}} +{"ts": "2026-08-04T22:40:09+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "TSLA", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "bfa875052195e5f8", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-04T22:40:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3598}} +{"ts": "2026-08-04T22:40:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3417}} +{"ts": "2026-08-04T22:43:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3414}} +{"ts": "2026-08-04T22:43:55+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3236}} +{"ts": "2026-08-04T22:46:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3233}} +{"ts": "2026-08-04T22:46:56+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3056}} +{"ts": "2026-08-04T22:49:56+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2875}} +{"ts": "2026-08-04T22:52:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2695}} +{"ts": "2026-08-04T22:53:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2804}} +{"ts": "2026-08-04T22:55:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2514}} +{"ts": "2026-08-04T22:56:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2620}} +{"ts": "2026-08-04T22:58:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2333}} +{"ts": "2026-08-04T22:59:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2439}} +{"ts": "2026-08-04T23:01:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2152}} +{"ts": "2026-08-04T23:02:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2245}} +{"ts": "2026-08-04T23:05:00+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1971}} +{"ts": "2026-08-04T23:08:00+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1791}} +{"ts": "2026-08-04T23:11:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1610}} +{"ts": "2026-08-04T23:14:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1430}} +{"ts": "2026-08-04T23:17:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1250}} +{"ts": "2026-08-04T23:20:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1069}} +{"ts": "2026-08-04T23:23:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 888}} +{"ts": "2026-08-04T23:26:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 707}} +{"ts": "2026-08-04T23:27:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 787}} +{"ts": "2026-08-04T23:29:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 526}} +{"ts": "2026-08-04T23:30:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 607}} +{"ts": "2026-08-04T23:32:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 346}} +{"ts": "2026-08-04T23:33:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 426}} +{"ts": "2026-08-04T23:35:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 165}} +{"ts": "2026-08-04T23:36:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 246}} +{"ts": "2026-08-04T23:37:54+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-04T23:38:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3587}} +{"ts": "2026-08-04T23:39:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 65}} +{"ts": "2026-08-04T23:40:09+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "TSLA", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "bfa875052195e5f8", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-04T23:41:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3406}} +{"ts": "2026-08-04T23:42:01+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-04T23:42:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3598}} +{"ts": "2026-08-04T23:42:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3485}} +{"ts": "2026-08-04T23:44:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3225}} +{"ts": "2026-08-04T23:45:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3417}} +{"ts": "2026-08-04T23:45:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3304}} +{"ts": "2026-08-04T23:47:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3044}} +{"ts": "2026-08-04T23:48:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3236}} +{"ts": "2026-08-04T23:48:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3123}} +{"ts": "2026-08-04T23:50:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2863}} +{"ts": "2026-08-04T23:51:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3056}} +{"ts": "2026-08-04T23:51:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2943}} +{"ts": "2026-08-04T23:53:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2683}} +{"ts": "2026-08-04T23:54:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2876}} +{"ts": "2026-08-04T23:54:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2762}} +{"ts": "2026-08-04T23:56:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2502}} +{"ts": "2026-08-04T23:57:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2695}} +{"ts": "2026-08-04T23:57:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2581}} +{"ts": "2026-08-04T23:59:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2322}} +{"ts": "2026-08-05T00:00:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2512}} +{"ts": "2026-08-05T00:00:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2400}} +{"ts": "2026-08-05T00:02:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2141}} +{"ts": "2026-08-05T00:03:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2331}} +{"ts": "2026-08-05T00:03:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2219}} +{"ts": "2026-08-05T00:05:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1960}} +{"ts": "2026-08-05T00:06:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2151}} +{"ts": "2026-08-05T00:06:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2039}} +{"ts": "2026-08-05T00:08:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1780}} +{"ts": "2026-08-05T00:09:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1970}} +{"ts": "2026-08-05T00:09:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1858}} +{"ts": "2026-08-05T00:11:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1600}} +{"ts": "2026-08-05T00:12:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1789}} +{"ts": "2026-08-05T00:12:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1678}} +{"ts": "2026-08-05T00:14:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1419}} +{"ts": "2026-08-05T00:15:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1609}} +{"ts": "2026-08-05T00:15:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1497}} +{"ts": "2026-08-05T00:17:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1237}} +{"ts": "2026-08-05T00:18:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1428}} +{"ts": "2026-08-05T00:18:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1316}} +{"ts": "2026-08-05T00:20:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1056}} +{"ts": "2026-08-05T00:21:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1247}} +{"ts": "2026-08-05T00:21:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1135}} +{"ts": "2026-08-05T00:23:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 876}} +{"ts": "2026-08-05T00:24:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1067}} +{"ts": "2026-08-05T00:24:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 955}} +{"ts": "2026-08-05T00:26:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 695}} +{"ts": "2026-08-05T00:27:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 886}} +{"ts": "2026-08-05T00:27:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 774}} +{"ts": "2026-08-05T00:29:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 513}} +{"ts": "2026-08-05T00:30:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 705}} +{"ts": "2026-08-05T00:30:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 593}} +{"ts": "2026-08-05T00:32:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 332}} +{"ts": "2026-08-05T00:33:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 525}} +{"ts": "2026-08-05T00:33:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 413}} +{"ts": "2026-08-05T00:35:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 152}} +{"ts": "2026-08-05T00:36:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 343}} +{"ts": "2026-08-05T00:36:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 231}} +{"ts": "2026-08-05T00:37:55+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-05T00:38:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3573}} +{"ts": "2026-08-05T00:39:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 162}} +{"ts": "2026-08-05T00:39:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 50}} +{"ts": "2026-08-05T00:40:10+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "TSLA", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "bfa875052195e5f8", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-05T00:41:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3392}} +{"ts": "2026-08-05T00:42:01+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-05T00:42:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3583}} +{"ts": "2026-08-05T00:42:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3471}} +{"ts": "2026-08-05T00:44:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3212}} +{"ts": "2026-08-05T00:45:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3402}} +{"ts": "2026-08-05T00:45:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3291}} +{"ts": "2026-08-05T00:47:01+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPYM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "41e444bf93130aef", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-05T00:47:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3598}} +{"ts": "2026-08-05T00:47:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3031}} +{"ts": "2026-08-05T00:48:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3222}} +{"ts": "2026-08-05T00:48:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3111}} +{"ts": "2026-08-05T00:50:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3417}} +{"ts": "2026-08-05T00:50:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2851}} +{"ts": "2026-08-05T00:51:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3042}} +{"ts": "2026-08-05T00:51:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2931}} +{"ts": "2026-08-05T00:53:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3236}} +{"ts": "2026-08-05T00:53:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2671}} +{"ts": "2026-08-05T00:54:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2862}} +{"ts": "2026-08-05T00:54:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2750}} +{"ts": "2026-08-05T00:56:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3056}} +{"ts": "2026-08-05T00:56:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2491}} +{"ts": "2026-08-05T00:57:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2681}} +{"ts": "2026-08-05T00:57:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2570}} +{"ts": "2026-08-05T00:59:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2876}} +{"ts": "2026-08-05T00:59:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2309}} +{"ts": "2026-08-05T01:00:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2501}} +{"ts": "2026-08-05T01:00:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2390}} +{"ts": "2026-08-05T01:02:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2696}} +{"ts": "2026-08-05T01:02:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2129}} +{"ts": "2026-08-05T01:03:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2321}} +{"ts": "2026-08-05T01:03:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2209}} +{"ts": "2026-08-05T01:05:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2515}} +{"ts": "2026-08-05T01:05:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1949}} +{"ts": "2026-08-05T01:06:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2140}} +{"ts": "2026-08-05T01:06:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2028}} +{"ts": "2026-08-05T01:08:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2335}} +{"ts": "2026-08-05T01:08:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1768}} +{"ts": "2026-08-05T01:09:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1959}} +{"ts": "2026-08-05T01:09:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1847}} +{"ts": "2026-08-05T01:11:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2155}} +{"ts": "2026-08-05T01:11:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1588}} +{"ts": "2026-08-05T01:12:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1779}} +{"ts": "2026-08-05T01:12:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1667}} +{"ts": "2026-08-05T01:14:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1974}} +{"ts": "2026-08-05T01:14:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1408}} +{"ts": "2026-08-05T01:15:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1599}} +{"ts": "2026-08-05T01:15:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1487}} +{"ts": "2026-08-05T01:17:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1794}} +{"ts": "2026-08-05T01:17:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1227}} +{"ts": "2026-08-05T01:18:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1417}} +{"ts": "2026-08-05T01:18:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1306}} +{"ts": "2026-08-05T01:20:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1613}} +{"ts": "2026-08-05T01:20:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1047}} +{"ts": "2026-08-05T01:21:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1237}} +{"ts": "2026-08-05T01:21:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1125}} +{"ts": "2026-08-05T01:23:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1432}} +{"ts": "2026-08-05T01:23:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 866}} +{"ts": "2026-08-05T01:24:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1056}} +{"ts": "2026-08-05T01:24:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 944}} +{"ts": "2026-08-05T01:26:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1251}} +{"ts": "2026-08-05T01:26:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 686}} +{"ts": "2026-08-05T01:27:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 875}} +{"ts": "2026-08-05T01:27:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 764}} +{"ts": "2026-08-05T01:29:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1070}} +{"ts": "2026-08-05T01:29:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 505}} +{"ts": "2026-08-05T01:30:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 694}} +{"ts": "2026-08-05T01:30:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 583}} +{"ts": "2026-08-05T01:32:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 889}} +{"ts": "2026-08-05T01:32:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 325}} +{"ts": "2026-08-05T01:33:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 513}} +{"ts": "2026-08-05T01:33:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 401}} +{"ts": "2026-08-05T01:35:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 708}} +{"ts": "2026-08-05T01:35:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 143}} +{"ts": "2026-08-05T01:36:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 331}} +{"ts": "2026-08-05T01:36:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 220}} +{"ts": "2026-08-05T01:37:58+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-05T01:38:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 527}} +{"ts": "2026-08-05T01:38:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3564}} +{"ts": "2026-08-05T01:39:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 151}} +{"ts": "2026-08-05T01:39:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 40}} +{"ts": "2026-08-05T01:40:11+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "TSLA", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "bfa875052195e5f8", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-05T01:41:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 346}} +{"ts": "2026-08-05T01:41:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3384}} +{"ts": "2026-08-05T01:42:03+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-05T01:42:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3573}} +{"ts": "2026-08-05T01:42:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3460}} +{"ts": "2026-08-05T01:44:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 165}} +{"ts": "2026-08-05T01:44:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3204}} +{"ts": "2026-08-05T01:45:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3391}} +{"ts": "2026-08-05T01:45:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3279}} +{"ts": "2026-08-05T01:47:02+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPYM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "41e444bf93130aef", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-05T01:47:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3586}} +{"ts": "2026-08-05T01:47:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3024}} +{"ts": "2026-08-05T01:48:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3210}} +{"ts": "2026-08-05T01:48:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3098}} +{"ts": "2026-08-05T01:50:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3404}} +{"ts": "2026-08-05T01:50:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2843}} +{"ts": "2026-08-05T01:51:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3029}} +{"ts": "2026-08-05T01:51:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2917}} +{"ts": "2026-08-05T01:53:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3224}} +{"ts": "2026-08-05T01:53:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2662}} +{"ts": "2026-08-05T01:54:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2849}} +{"ts": "2026-08-05T01:54:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2736}} +{"ts": "2026-08-05T01:56:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3043}} +{"ts": "2026-08-05T01:56:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2482}} +{"ts": "2026-08-05T01:57:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2668}} +{"ts": "2026-08-05T01:57:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2555}} +{"ts": "2026-08-05T01:59:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2863}} +{"ts": "2026-08-05T01:59:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2301}} +{"ts": "2026-08-05T02:00:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2487}} +{"ts": "2026-08-05T02:00:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2375}} +{"ts": "2026-08-05T02:02:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2682}} +{"ts": "2026-08-05T02:02:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2120}} +{"ts": "2026-08-05T02:03:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2307}} +{"ts": "2026-08-05T02:03:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2194}} +{"ts": "2026-08-05T02:05:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2501}} +{"ts": "2026-08-05T02:05:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1939}} +{"ts": "2026-08-05T02:06:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2126}} +{"ts": "2026-08-05T02:06:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2014}} +{"ts": "2026-08-05T02:08:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2321}} +{"ts": "2026-08-05T02:08:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1759}} +{"ts": "2026-08-05T02:09:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1945}} +{"ts": "2026-08-05T02:09:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1833}} +{"ts": "2026-08-05T02:11:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2141}} +{"ts": "2026-08-05T02:11:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1579}} +{"ts": "2026-08-05T02:12:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1765}} +{"ts": "2026-08-05T02:12:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1652}} +{"ts": "2026-08-05T02:14:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1960}} +{"ts": "2026-08-05T02:14:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1398}} +{"ts": "2026-08-05T02:15:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1585}} +{"ts": "2026-08-05T02:15:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1472}} +{"ts": "2026-08-05T02:17:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1778}} +{"ts": "2026-08-05T02:17:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1216}} +{"ts": "2026-08-05T02:18:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1404}} +{"ts": "2026-08-05T02:18:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1291}} +{"ts": "2026-08-05T02:20:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1597}} +{"ts": "2026-08-05T02:20:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1035}} +{"ts": "2026-08-05T02:21:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1224}} +{"ts": "2026-08-05T02:21:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1111}} +{"ts": "2026-08-05T02:23:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1417}} +{"ts": "2026-08-05T02:23:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 855}} +{"ts": "2026-08-05T02:24:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1043}} +{"ts": "2026-08-05T02:24:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 930}} +{"ts": "2026-08-05T02:26:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1236}} +{"ts": "2026-08-05T02:26:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 675}} +{"ts": "2026-08-05T02:27:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 862}} +{"ts": "2026-08-05T02:27:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 749}} +{"ts": "2026-08-05T02:29:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1055}} +{"ts": "2026-08-05T02:29:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 494}} +{"ts": "2026-08-05T02:30:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 682}} +{"ts": "2026-08-05T02:30:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 569}} +{"ts": "2026-08-05T02:32:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 874}} +{"ts": "2026-08-05T02:32:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 313}} +{"ts": "2026-08-05T02:33:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 501}} +{"ts": "2026-08-05T02:33:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 389}} +{"ts": "2026-08-05T02:35:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 694}} +{"ts": "2026-08-05T02:35:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 133}} +{"ts": "2026-08-05T02:36:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 320}} +{"ts": "2026-08-05T02:36:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 208}} +{"ts": "2026-08-05T02:37:59+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-05T02:38:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 513}} +{"ts": "2026-08-05T02:38:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3554}} +{"ts": "2026-08-05T02:39:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 140}} +{"ts": "2026-08-05T02:39:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 27}} +{"ts": "2026-08-05T02:40:12+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "TSLA", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "bfa875052195e5f8", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-05T02:41:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 332}} +{"ts": "2026-08-05T02:41:46+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3372}} +{"ts": "2026-08-05T02:42:04+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-05T02:42:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3560}} +{"ts": "2026-08-05T02:42:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3448}} +{"ts": "2026-08-05T02:44:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 152}} +{"ts": "2026-08-05T02:44:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3191}} +{"ts": "2026-08-05T02:45:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3378}} +{"ts": "2026-08-05T02:45:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3266}} +{"ts": "2026-08-05T02:47:05+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPYM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "41e444bf93130aef", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-05T02:47:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3575}} +{"ts": "2026-08-05T02:47:48+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3010}} +{"ts": "2026-08-05T02:48:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3196}} +{"ts": "2026-08-05T02:48:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3084}} +{"ts": "2026-08-05T02:50:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3394}} +{"ts": "2026-08-05T02:50:49+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2829}} +{"ts": "2026-08-05T02:51:50+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3014}} +{"ts": "2026-08-05T02:51:50+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2902}} +{"ts": "2026-08-05T02:53:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3212}} +{"ts": "2026-08-05T02:53:52+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2647}} +{"ts": "2026-08-05T02:54:50+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2833}} +{"ts": "2026-08-05T02:54:50+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2721}} +{"ts": "2026-08-05T02:56:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3031}} +{"ts": "2026-08-05T02:56:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2466}} +{"ts": "2026-08-05T02:57:52+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2652}} +{"ts": "2026-08-05T02:57:52+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2540}} +{"ts": "2026-08-05T02:59:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2848}} +{"ts": "2026-08-05T02:59:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2285}} +{"ts": "2026-08-05T03:00:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2469}} +{"ts": "2026-08-05T03:00:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2357}} +{"ts": "2026-08-05T03:02:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2668}} +{"ts": "2026-08-05T03:02:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2104}} +{"ts": "2026-08-05T03:03:56+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2287}} +{"ts": "2026-08-05T03:03:56+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2175}} +{"ts": "2026-08-05T03:05:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2486}} +{"ts": "2026-08-05T03:05:55+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1923}} +{"ts": "2026-08-05T03:06:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2106}} +{"ts": "2026-08-05T03:06:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1994}} +{"ts": "2026-08-05T03:08:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2306}} +{"ts": "2026-08-05T03:08:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1740}} +{"ts": "2026-08-05T03:09:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1925}} +{"ts": "2026-08-05T03:09:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1813}} +{"ts": "2026-08-05T03:11:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2123}} +{"ts": "2026-08-05T03:11:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1560}} +{"ts": "2026-08-05T03:13:00+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1744}} +{"ts": "2026-08-05T03:13:00+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1632}} +{"ts": "2026-08-05T03:14:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1942}} +{"ts": "2026-08-05T03:14:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1379}} +{"ts": "2026-08-05T03:16:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1562}} +{"ts": "2026-08-05T03:16:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1450}} +{"ts": "2026-08-05T03:17:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1762}} +{"ts": "2026-08-05T03:18:00+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1198}} +{"ts": "2026-08-05T03:19:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1380}} +{"ts": "2026-08-05T03:19:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1268}} +{"ts": "2026-08-05T03:20:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1580}} +{"ts": "2026-08-05T03:21:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1017}} +{"ts": "2026-08-05T03:22:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1197}} +{"ts": "2026-08-05T03:22:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1085}} +{"ts": "2026-08-05T03:23:46+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1399}} +{"ts": "2026-08-05T03:24:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 836}} +{"ts": "2026-08-05T03:25:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1017}} +{"ts": "2026-08-05T03:25:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 905}} +{"ts": "2026-08-05T03:26:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1218}} +{"ts": "2026-08-05T03:27:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 655}} +{"ts": "2026-08-05T03:28:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 837}} +{"ts": "2026-08-05T03:28:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 725}} +{"ts": "2026-08-05T03:29:49+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1035}} +{"ts": "2026-08-05T03:30:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 475}} +{"ts": "2026-08-05T03:31:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 657}} +{"ts": "2026-08-05T03:31:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 544}} +{"ts": "2026-08-05T03:32:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 854}} +{"ts": "2026-08-05T03:33:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 295}} +{"ts": "2026-08-05T03:34:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 474}} +{"ts": "2026-08-05T03:34:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 362}} +{"ts": "2026-08-05T03:35:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 672}} +{"ts": "2026-08-05T03:36:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 114}} +{"ts": "2026-08-05T03:37:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 292}} +{"ts": "2026-08-05T03:37:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 180}} +{"ts": "2026-08-05T03:38:02+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-05T03:38:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 491}} +{"ts": "2026-08-05T03:39:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3537}} +{"ts": "2026-08-05T03:40:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 112}} +{"ts": "2026-08-05T03:40:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 0}} +{"ts": "2026-08-05T03:40:15+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "TSLA", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "bfa875052195e5f8", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-05T03:41:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 311}} +{"ts": "2026-08-05T03:42:07+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-05T03:42:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3355}} +{"ts": "2026-08-05T03:43:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3534}} +{"ts": "2026-08-05T03:43:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3423}} +{"ts": "2026-08-05T03:44:56+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 129}} +{"ts": "2026-08-05T03:45:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3175}} +{"ts": "2026-08-05T03:46:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3353}} +{"ts": "2026-08-05T03:46:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3241}} +{"ts": "2026-08-05T03:47:09+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPYM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "41e444bf93130aef", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-05T03:47:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3551}} +{"ts": "2026-08-05T03:48:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2994}} +{"ts": "2026-08-05T03:49:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3172}} +{"ts": "2026-08-05T03:49:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3061}} +{"ts": "2026-08-05T03:50:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3370}} +{"ts": "2026-08-05T03:51:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2813}} +{"ts": "2026-08-05T03:52:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2991}} +{"ts": "2026-08-05T03:52:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2880}} +{"ts": "2026-08-05T03:54:00+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3188}} +{"ts": "2026-08-05T03:54:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2633}} +{"ts": "2026-08-05T03:55:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2810}} +{"ts": "2026-08-05T03:55:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2699}} +{"ts": "2026-08-05T03:57:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3005}} +{"ts": "2026-08-05T03:57:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2451}} +{"ts": "2026-08-05T03:58:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2629}} +{"ts": "2026-08-05T03:58:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2518}} +{"ts": "2026-08-05T04:00:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2824}} +{"ts": "2026-08-05T04:00:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2270}} +{"ts": "2026-08-05T04:01:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2448}} +{"ts": "2026-08-05T04:01:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2337}} +{"ts": "2026-08-05T04:03:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2643}} +{"ts": "2026-08-05T04:03:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2090}} +{"ts": "2026-08-05T04:04:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2268}} +{"ts": "2026-08-05T04:04:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2156}} +{"ts": "2026-08-05T04:06:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2463}} +{"ts": "2026-08-05T04:06:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1909}} +{"ts": "2026-08-05T04:07:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2088}} +{"ts": "2026-08-05T04:07:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1976}} +{"ts": "2026-08-05T04:09:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2282}} +{"ts": "2026-08-05T04:09:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1729}} +{"ts": "2026-08-05T04:10:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1907}} +{"ts": "2026-08-05T04:10:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1796}} +{"ts": "2026-08-05T04:12:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2101}} +{"ts": "2026-08-05T04:12:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1548}} +{"ts": "2026-08-05T04:13:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1727}} +{"ts": "2026-08-05T04:13:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1615}} +{"ts": "2026-08-05T04:15:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1921}} +{"ts": "2026-08-05T04:15:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1367}} +{"ts": "2026-08-05T04:16:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1546}} +{"ts": "2026-08-05T04:16:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1434}} +{"ts": "2026-08-05T04:18:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1740}} +{"ts": "2026-08-05T04:18:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1187}} +{"ts": "2026-08-05T04:19:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1365}} +{"ts": "2026-08-05T04:19:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1254}} +{"ts": "2026-08-05T04:21:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1559}} +{"ts": "2026-08-05T04:21:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1007}} +{"ts": "2026-08-05T04:22:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1184}} +{"ts": "2026-08-05T04:22:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1073}} +{"ts": "2026-08-05T04:24:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1378}} +{"ts": "2026-08-05T04:24:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 826}} +{"ts": "2026-08-05T04:25:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1003}} +{"ts": "2026-08-05T04:25:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 892}} +{"ts": "2026-08-05T04:27:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1197}} +{"ts": "2026-08-05T04:27:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 645}} +{"ts": "2026-08-05T04:28:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 823}} +{"ts": "2026-08-05T04:28:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 711}} +{"ts": "2026-08-05T04:30:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1017}} +{"ts": "2026-08-05T04:30:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 464}} +{"ts": "2026-08-05T04:31:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 642}} +{"ts": "2026-08-05T04:31:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 530}} +{"ts": "2026-08-05T04:33:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 836}} +{"ts": "2026-08-05T04:33:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 282}} +{"ts": "2026-08-05T04:34:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 461}} +{"ts": "2026-08-05T04:34:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 350}} +{"ts": "2026-08-05T04:36:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 656}} +{"ts": "2026-08-05T04:36:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 102}} +{"ts": "2026-08-05T04:37:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 280}} +{"ts": "2026-08-05T04:37:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 169}} +{"ts": "2026-08-05T04:38:03+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-05T04:39:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 475}} +{"ts": "2026-08-05T04:39:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3522}} +{"ts": "2026-08-05T04:40:17+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "TSLA", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "bfa875052195e5f8", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-05T04:40:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 100}} +{"ts": "2026-08-05T04:40:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3590}} +{"ts": "2026-08-05T04:42:09+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-05T04:42:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 294}} +{"ts": "2026-08-05T04:42:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3342}} +{"ts": "2026-08-05T04:43:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3522}} +{"ts": "2026-08-05T04:43:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3410}} +{"ts": "2026-08-05T04:45:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 113}} +{"ts": "2026-08-05T04:45:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3162}} +{"ts": "2026-08-05T04:46:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3340}} +{"ts": "2026-08-05T04:46:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3228}} +{"ts": "2026-08-05T04:47:11+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPYM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "41e444bf93130aef", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-05T04:48:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3534}} +{"ts": "2026-08-05T04:48:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2981}} +{"ts": "2026-08-05T04:49:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3159}} +{"ts": "2026-08-05T04:49:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3047}} +{"ts": "2026-08-05T04:51:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3354}} +{"ts": "2026-08-05T04:51:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2801}} +{"ts": "2026-08-05T04:52:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2979}} +{"ts": "2026-08-05T04:52:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2867}} +{"ts": "2026-08-05T04:54:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3173}} +{"ts": "2026-08-05T04:54:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2620}} +{"ts": "2026-08-05T04:55:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2798}} +{"ts": "2026-08-05T04:55:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2686}} +{"ts": "2026-08-05T04:57:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2992}} +{"ts": "2026-08-05T04:57:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2439}} +{"ts": "2026-08-05T04:58:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2617}} +{"ts": "2026-08-05T04:58:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2506}} +{"ts": "2026-08-05T05:00:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2811}} +{"ts": "2026-08-05T05:00:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2257}} +{"ts": "2026-08-05T05:01:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2437}} +{"ts": "2026-08-05T05:01:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2326}} +{"ts": "2026-08-05T05:03:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2631}} +{"ts": "2026-08-05T05:03:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2077}} +{"ts": "2026-08-05T05:04:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2257}} +{"ts": "2026-08-05T05:04:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2145}} +{"ts": "2026-08-05T05:06:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2451}} +{"ts": "2026-08-05T05:06:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1897}} +{"ts": "2026-08-05T05:07:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2076}} +{"ts": "2026-08-05T05:07:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1964}} +{"ts": "2026-08-05T05:09:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2270}} +{"ts": "2026-08-05T05:09:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1716}} +{"ts": "2026-08-05T05:10:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1896}} +{"ts": "2026-08-05T05:10:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1784}} +{"ts": "2026-08-05T05:12:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2089}} +{"ts": "2026-08-05T05:12:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1535}} +{"ts": "2026-08-05T05:13:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1716}} +{"ts": "2026-08-05T05:13:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1604}} +{"ts": "2026-08-05T05:15:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1908}} +{"ts": "2026-08-05T05:15:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1354}} +{"ts": "2026-08-05T05:16:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1536}} +{"ts": "2026-08-05T05:16:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1424}} +{"ts": "2026-08-05T05:18:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1727}} +{"ts": "2026-08-05T05:18:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1174}} +{"ts": "2026-08-05T05:19:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1356}} +{"ts": "2026-08-05T05:19:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1244}} +{"ts": "2026-08-05T05:21:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1547}} +{"ts": "2026-08-05T05:21:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 993}} +{"ts": "2026-08-05T05:22:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1175}} +{"ts": "2026-08-05T05:22:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1063}} +{"ts": "2026-08-05T05:24:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1367}} +{"ts": "2026-08-05T05:24:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 812}} +{"ts": "2026-08-05T05:25:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 995}} +{"ts": "2026-08-05T05:25:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 883}} +{"ts": "2026-08-05T05:27:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1186}} +{"ts": "2026-08-05T05:27:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 631}} +{"ts": "2026-08-05T05:28:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 814}} +{"ts": "2026-08-05T05:28:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 702}} +{"ts": "2026-08-05T05:30:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1006}} +{"ts": "2026-08-05T05:30:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 451}} +{"ts": "2026-08-05T05:31:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 632}} +{"ts": "2026-08-05T05:31:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 520}} +{"ts": "2026-08-05T05:33:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 825}} +{"ts": "2026-08-05T05:33:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 269}} +{"ts": "2026-08-05T05:34:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 452}} +{"ts": "2026-08-05T05:34:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 340}} +{"ts": "2026-08-05T05:36:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 644}} +{"ts": "2026-08-05T05:36:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 89}} +{"ts": "2026-08-05T05:37:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 271}} +{"ts": "2026-08-05T05:37:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 159}} +{"ts": "2026-08-05T05:38:06+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-05T05:39:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 464}} +{"ts": "2026-08-05T05:39:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3512}} +{"ts": "2026-08-05T05:40:19+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "TSLA", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "bfa875052195e5f8", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-05T05:40:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 90}} +{"ts": "2026-08-05T05:40:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3579}} +{"ts": "2026-08-05T05:42:09+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-05T05:42:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 284}} +{"ts": "2026-08-05T05:42:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3332}} +{"ts": "2026-08-05T05:43:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3510}} +{"ts": "2026-08-05T05:43:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3399}} +{"ts": "2026-08-05T05:45:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 103}} +{"ts": "2026-08-05T05:45:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3152}} +{"ts": "2026-08-05T05:46:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3329}} +{"ts": "2026-08-05T05:46:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3219}} +{"ts": "2026-08-05T05:47:12+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPYM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "41e444bf93130aef", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-05T05:48:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3523}} +{"ts": "2026-08-05T05:48:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2972}} +{"ts": "2026-08-05T05:49:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3149}} +{"ts": "2026-08-05T05:49:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3038}} +{"ts": "2026-08-05T05:51:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3342}} +{"ts": "2026-08-05T05:51:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2791}} +{"ts": "2026-08-05T05:52:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2968}} +{"ts": "2026-08-05T05:52:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2858}} +{"ts": "2026-08-05T05:54:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3162}} +{"ts": "2026-08-05T05:54:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2611}} +{"ts": "2026-08-05T05:55:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2788}} +{"ts": "2026-08-05T05:55:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2677}} +{"ts": "2026-08-05T05:57:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2982}} +{"ts": "2026-08-05T05:57:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2430}} +{"ts": "2026-08-05T05:58:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2607}} +{"ts": "2026-08-05T05:58:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2497}} +{"ts": "2026-08-05T09:04:17+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "055490", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "4050817477fc68ba", "extra": {"mock": true}} +{"ts": "2026-08-05T13:02:05+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "950160", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "c8255c46ccabd6cd", "extra": {"mock": true}} +{"ts": "2026-08-05T14:46:26+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "072950", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "122442d96834a9aa", "extra": {"mock": true}} +{"ts": "2026-08-05T15:22:47+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "304100", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "9d230a6cb97f7a1c", "extra": {"mock": true}} +{"ts": "2026-08-05T15:23:02+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "477850", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "c6b43bf44d43a529", "extra": {"mock": true}} +{"ts": "2026-08-05T15:26:26+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "218410", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "41d5c9e07f20a859", "extra": {"mock": true}} +{"ts": "2026-08-05T15:26:32+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "320000", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "787641a0fad71ebe", "extra": {"mock": true}} +{"ts": "2026-08-05T15:26:59+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "072950", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "122442d96834a9aa", "extra": {"mock": true}} +{"ts": "2026-08-05T23:24:02+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-05T23:24:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3598}} +{"ts": "2026-08-05T23:27:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3417}} +{"ts": "2026-08-05T23:30:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3236}} +{"ts": "2026-08-05T23:33:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3056}} +{"ts": "2026-08-05T23:36:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2876}} +{"ts": "2026-08-05T23:39:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2696}} +{"ts": "2026-08-05T23:42:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2516}} +{"ts": "2026-08-05T23:45:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2335}} +{"ts": "2026-08-05T23:48:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2155}} +{"ts": "2026-08-05T23:51:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1974}} +{"ts": "2026-08-05T23:54:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1793}} +{"ts": "2026-08-05T23:56:00+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-05T23:56:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3598}} +{"ts": "2026-08-05T23:57:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1611}} +{"ts": "2026-08-05T23:59:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3417}} +{"ts": "2026-08-06T00:00:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1431}} +{"ts": "2026-08-06T00:02:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3236}} +{"ts": "2026-08-06T00:03:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1251}} +{"ts": "2026-08-06T00:05:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3056}} +{"ts": "2026-08-06T00:06:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1071}} +{"ts": "2026-08-06T00:08:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2875}} +{"ts": "2026-08-06T00:09:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 889}} +{"ts": "2026-08-06T00:11:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2695}} +{"ts": "2026-08-06T00:12:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 708}} +{"ts": "2026-08-06T00:14:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2515}} +{"ts": "2026-08-06T00:15:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 528}} +{"ts": "2026-08-06T00:17:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2334}} +{"ts": "2026-08-06T00:18:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 348}} +{"ts": "2026-08-06T00:20:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2152}} +{"ts": "2026-08-06T00:21:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 168}} +{"ts": "2026-08-06T00:23:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1972}} +{"ts": "2026-08-06T00:24:04+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-06T00:24:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3590}} +{"ts": "2026-08-06T00:26:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1792}} +{"ts": "2026-08-06T00:27:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3410}} +{"ts": "2026-08-06T00:29:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1611}} +{"ts": "2026-08-06T00:30:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3228}} +{"ts": "2026-08-06T00:32:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1430}} +{"ts": "2026-08-06T00:33:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3048}} +{"ts": "2026-08-06T00:35:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1250}} +{"ts": "2026-08-06T00:36:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2867}} +{"ts": "2026-08-06T00:38:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1069}} +{"ts": "2026-08-06T00:39:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2687}} +{"ts": "2026-08-06T00:41:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 889}} +{"ts": "2026-08-06T00:42:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2506}} +{"ts": "2026-08-06T00:44:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 708}} +{"ts": "2026-08-06T00:45:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2325}} +{"ts": "2026-08-06T00:47:00+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPYM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "41e444bf93130aef", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-06T00:47:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3598}} +{"ts": "2026-08-06T00:47:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 528}} +{"ts": "2026-08-06T00:48:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2145}} +{"ts": "2026-08-06T00:50:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3417}} +{"ts": "2026-08-06T00:50:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 348}} +{"ts": "2026-08-06T00:51:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1965}} +{"ts": "2026-08-06T00:53:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3236}} +{"ts": "2026-08-06T00:53:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 167}} +{"ts": "2026-08-06T00:54:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1784}} +{"ts": "2026-08-06T00:56:02+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-06T00:56:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3055}} +{"ts": "2026-08-06T00:56:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3588}} +{"ts": "2026-08-06T00:57:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1603}} +{"ts": "2026-08-06T00:59:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2875}} +{"ts": "2026-08-06T00:59:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3407}} +{"ts": "2026-08-06T01:00:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1423}} +{"ts": "2026-08-06T01:02:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2694}} +{"ts": "2026-08-06T01:02:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3226}} +{"ts": "2026-08-06T01:03:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1241}} +{"ts": "2026-08-06T01:05:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2513}} +{"ts": "2026-08-06T01:05:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3045}} +{"ts": "2026-08-06T01:06:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1061}} +{"ts": "2026-08-06T01:08:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2332}} +{"ts": "2026-08-06T01:08:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2865}} +{"ts": "2026-08-06T01:09:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 880}} +{"ts": "2026-08-06T01:11:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2151}} +{"ts": "2026-08-06T01:11:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2684}} +{"ts": "2026-08-06T01:12:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 699}} +{"ts": "2026-08-06T01:14:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1970}} +{"ts": "2026-08-06T01:14:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2503}} +{"ts": "2026-08-06T01:15:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 519}} +{"ts": "2026-08-06T01:17:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1790}} +{"ts": "2026-08-06T01:17:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2322}} +{"ts": "2026-08-06T01:18:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 339}} +{"ts": "2026-08-06T01:20:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1610}} +{"ts": "2026-08-06T01:20:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2141}} +{"ts": "2026-08-06T01:21:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 158}} +{"ts": "2026-08-06T01:23:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1430}} +{"ts": "2026-08-06T01:23:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1960}} +{"ts": "2026-08-06T01:24:05+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-06T01:24:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3578}} +{"ts": "2026-08-06T01:26:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1249}} +{"ts": "2026-08-06T01:26:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1779}} +{"ts": "2026-08-06T01:27:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3397}} +{"ts": "2026-08-06T01:29:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1068}} +{"ts": "2026-08-06T01:29:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1598}} +{"ts": "2026-08-06T01:30:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3216}} +{"ts": "2026-08-06T01:32:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 888}} +{"ts": "2026-08-06T01:32:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1418}} +{"ts": "2026-08-06T01:33:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3035}} +{"ts": "2026-08-06T01:35:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 707}} +{"ts": "2026-08-06T01:35:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1237}} +{"ts": "2026-08-06T01:36:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2854}} +{"ts": "2026-08-06T01:38:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 526}} +{"ts": "2026-08-06T01:38:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1056}} +{"ts": "2026-08-06T01:39:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2674}} +{"ts": "2026-08-06T01:41:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 345}} +{"ts": "2026-08-06T01:41:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 875}} +{"ts": "2026-08-06T01:42:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2494}} +{"ts": "2026-08-06T01:44:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 164}} +{"ts": "2026-08-06T01:44:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 695}} +{"ts": "2026-08-06T01:45:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2313}} +{"ts": "2026-08-06T01:47:03+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPYM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "41e444bf93130aef", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-06T01:47:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3586}} +{"ts": "2026-08-06T01:47:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 515}} +{"ts": "2026-08-06T01:48:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2133}} +{"ts": "2026-08-06T01:50:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3405}} +{"ts": "2026-08-06T01:50:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 334}} +{"ts": "2026-08-06T01:51:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1952}} +{"ts": "2026-08-06T01:53:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3225}} +{"ts": "2026-08-06T01:53:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 154}} +{"ts": "2026-08-06T01:54:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1772}} +{"ts": "2026-08-06T01:56:03+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-06T01:56:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3041}} +{"ts": "2026-08-06T01:56:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3574}} +{"ts": "2026-08-06T01:57:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1592}} +{"ts": "2026-08-06T01:59:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2861}} +{"ts": "2026-08-06T01:59:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3393}} +{"ts": "2026-08-06T02:00:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1410}} +{"ts": "2026-08-06T02:02:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2680}} +{"ts": "2026-08-06T02:02:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3212}} +{"ts": "2026-08-06T02:03:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1229}} +{"ts": "2026-08-06T02:05:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2499}} +{"ts": "2026-08-06T02:05:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3032}} +{"ts": "2026-08-06T02:06:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1049}} +{"ts": "2026-08-06T02:08:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2318}} +{"ts": "2026-08-06T02:08:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2851}} +{"ts": "2026-08-06T02:09:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 868}} +{"ts": "2026-08-06T02:11:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2137}} +{"ts": "2026-08-06T02:11:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2669}} +{"ts": "2026-08-06T02:12:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 688}} +{"ts": "2026-08-06T02:14:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1957}} +{"ts": "2026-08-06T02:14:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2489}} +{"ts": "2026-08-06T02:15:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 507}} +{"ts": "2026-08-06T02:17:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1775}} +{"ts": "2026-08-06T02:17:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2309}} +{"ts": "2026-08-06T02:18:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 326}} +{"ts": "2026-08-06T02:20:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1594}} +{"ts": "2026-08-06T02:20:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2128}} +{"ts": "2026-08-06T02:21:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 145}} +{"ts": "2026-08-06T02:23:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1414}} +{"ts": "2026-08-06T02:23:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1948}} +{"ts": "2026-08-06T02:24:07+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-06T02:24:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3567}} +{"ts": "2026-08-06T02:26:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1233}} +{"ts": "2026-08-06T02:26:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1767}} +{"ts": "2026-08-06T02:27:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3386}} +{"ts": "2026-08-06T02:29:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1053}} +{"ts": "2026-08-06T02:29:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1587}} +{"ts": "2026-08-06T02:30:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3206}} +{"ts": "2026-08-06T02:32:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 872}} +{"ts": "2026-08-06T02:32:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1406}} +{"ts": "2026-08-06T02:33:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3025}} +{"ts": "2026-08-06T02:35:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 691}} +{"ts": "2026-08-06T02:35:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1224}} +{"ts": "2026-08-06T02:36:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2845}} +{"ts": "2026-08-06T02:38:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 510}} +{"ts": "2026-08-06T02:38:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1044}} +{"ts": "2026-08-06T02:39:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2664}} +{"ts": "2026-08-06T02:41:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 330}} +{"ts": "2026-08-06T02:41:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 863}} +{"ts": "2026-08-06T02:42:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2484}} +{"ts": "2026-08-06T02:44:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 149}} +{"ts": "2026-08-06T02:44:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 682}} +{"ts": "2026-08-06T02:45:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2303}} +{"ts": "2026-08-06T02:47:04+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPYM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "41e444bf93130aef", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-06T02:47:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3570}} +{"ts": "2026-08-06T02:47:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 502}} +{"ts": "2026-08-06T02:48:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2122}} +{"ts": "2026-08-06T02:50:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3390}} +{"ts": "2026-08-06T02:50:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 322}} +{"ts": "2026-08-06T02:51:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1941}} +{"ts": "2026-08-06T02:53:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3209}} +{"ts": "2026-08-06T02:53:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 142}} +{"ts": "2026-08-06T02:54:46+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1760}} +{"ts": "2026-08-06T02:56:04+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-06T02:56:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3029}} +{"ts": "2026-08-06T02:56:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3561}} +{"ts": "2026-08-06T02:57:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1579}} +{"ts": "2026-08-06T02:59:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2847}} +{"ts": "2026-08-06T02:59:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3380}} +{"ts": "2026-08-06T03:00:48+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1398}} +{"ts": "2026-08-06T03:02:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2666}} +{"ts": "2026-08-06T03:02:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3199}} +{"ts": "2026-08-06T03:03:49+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1218}} +{"ts": "2026-08-06T03:05:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2486}} +{"ts": "2026-08-06T03:05:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3019}} +{"ts": "2026-08-06T03:06:50+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1037}} +{"ts": "2026-08-06T03:08:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2306}} +{"ts": "2026-08-06T03:08:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2839}} +{"ts": "2026-08-06T03:09:50+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 856}} +{"ts": "2026-08-06T03:11:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2126}} +{"ts": "2026-08-06T03:11:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2658}} +{"ts": "2026-08-06T03:12:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 676}} +{"ts": "2026-08-06T03:14:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1944}} +{"ts": "2026-08-06T03:14:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2478}} +{"ts": "2026-08-06T03:15:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 496}} +{"ts": "2026-08-06T03:17:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1764}} +{"ts": "2026-08-06T03:17:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2298}} +{"ts": "2026-08-06T03:18:52+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 315}} +{"ts": "2026-08-06T03:20:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1583}} +{"ts": "2026-08-06T03:20:46+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2117}} +{"ts": "2026-08-06T03:21:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 134}} +{"ts": "2026-08-06T03:23:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1403}} +{"ts": "2026-08-06T03:23:46+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1937}} +{"ts": "2026-08-06T03:24:09+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-06T03:24:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3555}} +{"ts": "2026-08-06T03:26:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1223}} +{"ts": "2026-08-06T03:26:46+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1757}} +{"ts": "2026-08-06T03:27:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3375}} +{"ts": "2026-08-06T03:29:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1041}} +{"ts": "2026-08-06T03:29:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1576}} +{"ts": "2026-08-06T03:30:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3195}} +{"ts": "2026-08-06T03:32:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 861}} +{"ts": "2026-08-06T03:32:48+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1395}} +{"ts": "2026-08-06T03:33:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3015}} +{"ts": "2026-08-06T03:35:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 680}} +{"ts": "2026-08-06T03:35:49+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1214}} +{"ts": "2026-08-06T03:36:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2835}} +{"ts": "2026-08-06T03:38:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 499}} +{"ts": "2026-08-06T03:38:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1033}} +{"ts": "2026-08-06T03:39:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2654}} +{"ts": "2026-08-06T03:41:46+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 318}} +{"ts": "2026-08-06T03:41:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 852}} +{"ts": "2026-08-06T03:42:55+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2473}} +{"ts": "2026-08-06T03:44:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 137}} +{"ts": "2026-08-06T03:44:52+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 671}} +{"ts": "2026-08-06T03:45:55+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2293}} +{"ts": "2026-08-06T03:47:05+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPYM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "41e444bf93130aef", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-06T03:47:48+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3557}} +{"ts": "2026-08-06T03:47:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 491}} +{"ts": "2026-08-06T03:48:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2111}} +{"ts": "2026-08-06T03:50:48+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3376}} +{"ts": "2026-08-06T03:50:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 310}} +{"ts": "2026-08-06T03:51:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1931}} +{"ts": "2026-08-06T03:53:49+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3196}} +{"ts": "2026-08-06T03:53:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 129}} +{"ts": "2026-08-06T03:54:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1750}} +{"ts": "2026-08-06T03:56:06+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-06T03:56:50+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3015}} +{"ts": "2026-08-06T03:56:55+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3550}} +{"ts": "2026-08-06T03:57:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1569}} +{"ts": "2026-08-06T03:59:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2834}} +{"ts": "2026-08-06T03:59:56+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3369}} +{"ts": "2026-08-06T04:01:00+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1388}} +{"ts": "2026-08-06T04:02:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2654}} +{"ts": "2026-08-06T04:02:56+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3189}} +{"ts": "2026-08-06T04:04:00+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1208}} +{"ts": "2026-08-06T04:05:52+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2473}} +{"ts": "2026-08-06T04:05:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3008}} +{"ts": "2026-08-06T04:07:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1027}} +{"ts": "2026-08-06T04:08:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2291}} +{"ts": "2026-08-06T04:08:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2828}} +{"ts": "2026-08-06T04:10:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 847}} +{"ts": "2026-08-06T04:11:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2111}} +{"ts": "2026-08-06T04:11:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2647}} +{"ts": "2026-08-06T04:13:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 667}} +{"ts": "2026-08-06T04:14:55+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1930}} +{"ts": "2026-08-06T04:14:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2467}} +{"ts": "2026-08-06T04:16:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 487}} +{"ts": "2026-08-06T04:17:55+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1750}} +{"ts": "2026-08-06T04:17:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2286}} +{"ts": "2026-08-06T04:19:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 306}} +{"ts": "2026-08-06T04:20:56+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1569}} +{"ts": "2026-08-06T04:20:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2106}} +{"ts": "2026-08-06T04:22:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 125}} +{"ts": "2026-08-06T04:23:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1388}} +{"ts": "2026-08-06T04:24:00+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1926}} +{"ts": "2026-08-06T04:24:10+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-06T04:25:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3546}} +{"ts": "2026-08-06T04:26:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1207}} +{"ts": "2026-08-06T04:27:00+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1745}} +{"ts": "2026-08-06T04:28:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3365}} +{"ts": "2026-08-06T04:29:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1027}} +{"ts": "2026-08-06T04:30:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1564}} +{"ts": "2026-08-06T04:31:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3185}} +{"ts": "2026-08-06T04:32:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 846}} +{"ts": "2026-08-06T04:33:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1384}} +{"ts": "2026-08-06T04:34:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3005}} +{"ts": "2026-08-06T04:35:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 666}} +{"ts": "2026-08-06T04:36:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1204}} +{"ts": "2026-08-06T04:37:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2824}} +{"ts": "2026-08-06T04:38:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 486}} +{"ts": "2026-08-06T04:39:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1023}} +{"ts": "2026-08-06T04:40:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2643}} +{"ts": "2026-08-06T04:42:00+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 305}} +{"ts": "2026-08-06T04:42:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 843}} +{"ts": "2026-08-06T04:43:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2463}} +{"ts": "2026-08-06T04:45:00+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 124}} +{"ts": "2026-08-06T04:45:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 662}} +{"ts": "2026-08-06T04:46:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2282}} +{"ts": "2026-08-06T04:47:06+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPYM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "41e444bf93130aef", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-06T04:48:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3544}} +{"ts": "2026-08-06T04:48:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 481}} +{"ts": "2026-08-06T04:49:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2102}} +{"ts": "2026-08-06T04:51:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3363}} +{"ts": "2026-08-06T04:51:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 300}} +{"ts": "2026-08-06T04:52:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1922}} +{"ts": "2026-08-06T04:54:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3182}} +{"ts": "2026-08-06T04:54:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 119}} +{"ts": "2026-08-06T04:55:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1741}} +{"ts": "2026-08-06T04:56:07+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-06T04:57:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3001}} +{"ts": "2026-08-06T04:57:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3540}} +{"ts": "2026-08-06T04:58:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1560}} +{"ts": "2026-08-06T05:00:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2821}} +{"ts": "2026-08-06T05:00:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3359}} +{"ts": "2026-08-06T05:01:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1379}} +{"ts": "2026-08-06T05:03:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2641}} +{"ts": "2026-08-06T05:03:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3178}} +{"ts": "2026-08-06T05:04:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1198}} +{"ts": "2026-08-06T05:06:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2461}} +{"ts": "2026-08-06T05:06:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2998}} +{"ts": "2026-08-06T05:07:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1018}} +{"ts": "2026-08-06T05:09:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2280}} +{"ts": "2026-08-06T05:09:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2817}} +{"ts": "2026-08-06T05:10:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 836}} +{"ts": "2026-08-06T05:12:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2099}} +{"ts": "2026-08-06T05:12:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2637}} +{"ts": "2026-08-06T05:13:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 655}} +{"ts": "2026-08-06T05:15:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1918}} +{"ts": "2026-08-06T05:15:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2456}} +{"ts": "2026-08-06T05:16:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 473}} +{"ts": "2026-08-06T05:18:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1738}} +{"ts": "2026-08-06T05:18:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2276}} +{"ts": "2026-08-06T05:19:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 293}} +{"ts": "2026-08-06T05:21:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1557}} +{"ts": "2026-08-06T05:21:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2095}} +{"ts": "2026-08-06T05:22:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 112}} +{"ts": "2026-08-06T05:24:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1376}} +{"ts": "2026-08-06T05:24:12+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-06T05:24:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1913}} +{"ts": "2026-08-06T05:25:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3532}} +{"ts": "2026-08-06T05:27:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1195}} +{"ts": "2026-08-06T05:27:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1732}} +{"ts": "2026-08-06T05:28:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3351}} +{"ts": "2026-08-06T05:30:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1015}} +{"ts": "2026-08-06T05:30:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1552}} +{"ts": "2026-08-06T05:31:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3171}} +{"ts": "2026-08-06T05:33:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 835}} +{"ts": "2026-08-06T05:33:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1371}} +{"ts": "2026-08-06T05:34:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2991}} +{"ts": "2026-08-06T05:36:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 654}} +{"ts": "2026-08-06T05:36:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1190}} +{"ts": "2026-08-06T05:37:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2810}} +{"ts": "2026-08-06T05:39:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 473}} +{"ts": "2026-08-06T05:39:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1010}} +{"ts": "2026-08-06T05:40:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2630}} +{"ts": "2026-08-06T05:42:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 292}} +{"ts": "2026-08-06T05:42:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 830}} +{"ts": "2026-08-06T05:43:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2449}} +{"ts": "2026-08-06T05:45:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 112}} +{"ts": "2026-08-06T05:45:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 648}} +{"ts": "2026-08-06T05:46:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2269}} +{"ts": "2026-08-06T05:47:07+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPYM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "41e444bf93130aef", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-06T05:48:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3532}} +{"ts": "2026-08-06T05:48:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 467}} +{"ts": "2026-08-06T05:49:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2088}} +{"ts": "2026-08-06T05:51:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3352}} +{"ts": "2026-08-06T05:51:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 286}} +{"ts": "2026-08-06T05:52:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1907}} +{"ts": "2026-08-06T05:54:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3172}} +{"ts": "2026-08-06T05:54:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 106}} +{"ts": "2026-08-06T05:55:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1727}} +{"ts": "2026-08-06T05:56:08+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-06T05:57:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2991}} +{"ts": "2026-08-06T05:57:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3525}} +{"ts": "2026-08-06T05:58:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1547}} +{"ts": "2026-08-06T11:25:43+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "394420", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "cacd383105d57c6e", "extra": {"mock": true}} +{"ts": "2026-08-06T11:46:26+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "080580", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "af52d64a23e9b6d4", "extra": {"mock": true}} +{"ts": "2026-08-06T12:12:52+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "491000", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "7e4f0d919cca2253", "extra": {"mock": true}} +{"ts": "2026-08-06T13:51:48+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "033320", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "542296cf6e0a3c51", "extra": {"mock": true}} +{"ts": "2026-08-06T15:17:54+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "087600", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "0bb0bf419693d230", "extra": {"mock": true}} +{"ts": "2026-08-06T15:21:30+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "330350", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "cecab4fd5dd311d6", "extra": {"mock": true}} +{"ts": "2026-08-06T15:21:50+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "011040", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "f7fc81ccef17a449", "extra": {"mock": true}} +{"ts": "2026-08-06T15:21:58+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "104700", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "4e28cce4345bd243", "extra": {"mock": true}} +{"ts": "2026-08-06T15:23:34+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "060280", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "1d3f976dcd1c0ea1", "extra": {"mock": true}} +{"ts": "2026-08-06T15:25:27+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "368770", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "efc4b03268dcb84e", "extra": {"mock": true}} +{"ts": "2026-08-06T15:25:35+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "396300", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "c0a46e4324e261c4", "extra": {"mock": true}} +{"ts": "2026-08-06T15:25:46+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "220100", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "d9a152995f83a009", "extra": {"mock": true}} +{"ts": "2026-08-06T15:25:49+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "314140", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "2485c3acdfb56dd5", "extra": {"mock": true}} +{"ts": "2026-08-06T15:25:52+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "381620", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "000bbb3ca2c6d33d", "extra": {"mock": true}} +{"ts": "2026-08-06T15:26:01+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "472850", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "d47660841061b975", "extra": {"mock": true}} +{"ts": "2026-08-06T23:29:03+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-06T23:29:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3596}} +{"ts": "2026-08-06T23:32:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3408}} +{"ts": "2026-08-06T23:36:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3170}} +{"ts": "2026-08-06T23:39:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2990}} +{"ts": "2026-08-06T23:42:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2809}} +{"ts": "2026-08-06T23:45:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2629}} +{"ts": "2026-08-06T23:48:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2448}} +{"ts": "2026-08-06T23:51:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2268}} +{"ts": "2026-08-06T23:54:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2088}} +{"ts": "2026-08-06T23:57:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1907}} +{"ts": "2026-08-07T00:00:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1726}} +{"ts": "2026-08-07T00:01:01+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-07T00:01:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3598}} +{"ts": "2026-08-07T00:03:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1545}} +{"ts": "2026-08-07T00:04:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3417}} +{"ts": "2026-08-07T00:06:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1365}} +{"ts": "2026-08-07T00:07:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3237}} +{"ts": "2026-08-07T00:09:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1183}} +{"ts": "2026-08-07T00:10:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3057}} +{"ts": "2026-08-07T00:12:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1002}} +{"ts": "2026-08-07T00:13:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2876}} +{"ts": "2026-08-07T00:15:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 821}} +{"ts": "2026-08-07T00:16:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2695}} +{"ts": "2026-08-07T00:18:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 640}} +{"ts": "2026-08-07T00:19:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2515}} +{"ts": "2026-08-07T00:21:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 460}} +{"ts": "2026-08-07T00:22:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2333}} +{"ts": "2026-08-07T00:24:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 280}} +{"ts": "2026-08-07T00:25:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2153}} +{"ts": "2026-08-07T00:27:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 98}} +{"ts": "2026-08-07T00:28:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1973}} +{"ts": "2026-08-07T00:29:04+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-07T00:30:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3519}} +{"ts": "2026-08-07T00:31:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1793}} +{"ts": "2026-08-07T00:33:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3337}} +{"ts": "2026-08-07T00:34:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1612}} +{"ts": "2026-08-07T00:36:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3156}} +{"ts": "2026-08-07T00:37:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1431}} +{"ts": "2026-08-07T00:39:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2975}} +{"ts": "2026-08-07T00:40:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1251}} +{"ts": "2026-08-07T00:42:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2794}} +{"ts": "2026-08-07T00:43:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1070}} +{"ts": "2026-08-07T00:45:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2614}} +{"ts": "2026-08-07T00:46:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 889}} +{"ts": "2026-08-07T00:48:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2433}} +{"ts": "2026-08-07T00:49:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 708}} +{"ts": "2026-08-07T00:51:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2252}} +{"ts": "2026-08-07T00:52:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 527}} +{"ts": "2026-08-07T00:54:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2072}} +{"ts": "2026-08-07T00:55:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 346}} +{"ts": "2026-08-07T00:57:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1891}} +{"ts": "2026-08-07T00:58:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 166}} +{"ts": "2026-08-07T01:00:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1710}} +{"ts": "2026-08-07T01:01:01+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-07T01:01:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3586}} +{"ts": "2026-08-07T01:03:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1530}} +{"ts": "2026-08-07T01:04:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3405}} +{"ts": "2026-08-07T01:06:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1349}} +{"ts": "2026-08-07T01:07:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3224}} +{"ts": "2026-08-07T01:09:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1169}} +{"ts": "2026-08-07T01:10:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3044}} +{"ts": "2026-08-07T01:12:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 988}} +{"ts": "2026-08-07T01:13:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2864}} +{"ts": "2026-08-07T01:15:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 808}} +{"ts": "2026-08-07T01:16:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2683}} +{"ts": "2026-08-07T01:18:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 628}} +{"ts": "2026-08-07T01:19:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2503}} +{"ts": "2026-08-07T01:21:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 447}} +{"ts": "2026-08-07T01:22:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2322}} +{"ts": "2026-08-07T01:24:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 266}} +{"ts": "2026-08-07T01:25:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2142}} +{"ts": "2026-08-07T01:27:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 86}} +{"ts": "2026-08-07T01:28:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1961}} +{"ts": "2026-08-07T01:29:05+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-07T01:30:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3506}} +{"ts": "2026-08-07T01:31:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1781}} +{"ts": "2026-08-07T01:33:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3325}} +{"ts": "2026-08-07T01:34:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1600}} +{"ts": "2026-08-07T01:36:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3145}} +{"ts": "2026-08-07T01:37:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1419}} +{"ts": "2026-08-07T01:39:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2964}} +{"ts": "2026-08-07T01:40:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1238}} +{"ts": "2026-08-07T01:42:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2783}} +{"ts": "2026-08-07T01:43:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1057}} +{"ts": "2026-08-07T01:45:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2603}} +{"ts": "2026-08-07T01:46:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 877}} +{"ts": "2026-08-07T01:48:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2422}} +{"ts": "2026-08-07T01:49:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 697}} +{"ts": "2026-08-07T01:51:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2240}} +{"ts": "2026-08-07T01:52:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 516}} +{"ts": "2026-08-07T01:54:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2059}} +{"ts": "2026-08-07T01:55:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 335}} +{"ts": "2026-08-07T01:57:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1879}} +{"ts": "2026-08-07T01:58:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 154}} +{"ts": "2026-08-07T02:00:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1698}} +{"ts": "2026-08-07T02:01:02+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-07T02:01:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3575}} +{"ts": "2026-08-07T02:03:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1517}} +{"ts": "2026-08-07T02:04:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3394}} +{"ts": "2026-08-07T02:06:48+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1336}} +{"ts": "2026-08-07T02:07:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3213}} +{"ts": "2026-08-07T02:09:49+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1155}} +{"ts": "2026-08-07T02:10:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3032}} +{"ts": "2026-08-07T02:12:50+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 974}} +{"ts": "2026-08-07T02:13:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2851}} +{"ts": "2026-08-07T02:15:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 794}} +{"ts": "2026-08-07T02:16:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2671}} +{"ts": "2026-08-07T02:18:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 613}} +{"ts": "2026-08-07T02:19:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2490}} +{"ts": "2026-08-07T02:21:52+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 432}} +{"ts": "2026-08-07T02:22:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2310}} +{"ts": "2026-08-07T02:24:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 251}} +{"ts": "2026-08-07T02:25:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2129}} +{"ts": "2026-08-07T02:27:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 71}} +{"ts": "2026-08-07T02:28:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1948}} +{"ts": "2026-08-07T02:29:07+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-07T02:30:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3494}} +{"ts": "2026-08-07T02:31:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1767}} +{"ts": "2026-08-07T02:33:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3313}} +{"ts": "2026-08-07T02:34:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1586}} +{"ts": "2026-08-07T02:36:55+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3132}} +{"ts": "2026-08-07T02:37:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1405}} +{"ts": "2026-08-07T02:39:56+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2951}} +{"ts": "2026-08-07T02:40:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1224}} +{"ts": "2026-08-07T02:42:56+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2771}} +{"ts": "2026-08-07T02:43:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1043}} +{"ts": "2026-08-07T02:45:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2589}} +{"ts": "2026-08-07T02:46:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 863}} +{"ts": "2026-08-07T02:48:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2408}} +{"ts": "2026-08-07T02:49:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 682}} +{"ts": "2026-08-07T02:52:00+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2227}} +{"ts": "2026-08-07T02:52:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 501}} +{"ts": "2026-08-07T02:55:00+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2047}} +{"ts": "2026-08-07T02:55:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 319}} +{"ts": "2026-08-07T02:58:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1866}} +{"ts": "2026-08-07T02:58:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 139}} +{"ts": "2026-08-07T03:01:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1685}} +{"ts": "2026-08-07T03:01:04+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-07T03:01:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3560}} +{"ts": "2026-08-07T03:04:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1505}} +{"ts": "2026-08-07T03:04:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3380}} +{"ts": "2026-08-07T03:07:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1324}} +{"ts": "2026-08-07T03:07:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3199}} +{"ts": "2026-08-07T03:10:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1144}} +{"ts": "2026-08-07T03:10:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3018}} +{"ts": "2026-08-07T03:13:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 963}} +{"ts": "2026-08-07T03:13:46+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2838}} +{"ts": "2026-08-07T03:16:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 782}} +{"ts": "2026-08-07T03:16:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2657}} +{"ts": "2026-08-07T03:19:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 601}} +{"ts": "2026-08-07T03:19:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2477}} +{"ts": "2026-08-07T03:22:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 421}} +{"ts": "2026-08-07T03:22:48+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2296}} +{"ts": "2026-08-07T03:25:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 240}} +{"ts": "2026-08-07T03:25:48+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2116}} +{"ts": "2026-08-07T03:28:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 59}} +{"ts": "2026-08-07T03:28:49+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1935}} +{"ts": "2026-08-07T03:29:10+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-07T03:31:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3480}} +{"ts": "2026-08-07T03:31:50+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1754}} +{"ts": "2026-08-07T03:34:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3299}} +{"ts": "2026-08-07T03:34:50+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1574}} +{"ts": "2026-08-07T03:37:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3118}} +{"ts": "2026-08-07T03:37:50+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1394}} +{"ts": "2026-08-07T03:40:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2938}} +{"ts": "2026-08-07T03:40:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1213}} +{"ts": "2026-08-07T03:43:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2757}} +{"ts": "2026-08-07T03:43:52+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1032}} +{"ts": "2026-08-07T03:46:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2576}} +{"ts": "2026-08-07T03:46:52+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 852}} +{"ts": "2026-08-07T03:49:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2395}} +{"ts": "2026-08-07T03:49:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 671}} +{"ts": "2026-08-07T03:52:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2214}} +{"ts": "2026-08-07T03:52:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 490}} +{"ts": "2026-08-07T03:55:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2034}} +{"ts": "2026-08-07T03:55:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 310}} +{"ts": "2026-08-07T03:58:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1853}} +{"ts": "2026-08-07T03:58:55+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 129}} +{"ts": "2026-08-07T04:01:05+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-07T04:01:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1672}} +{"ts": "2026-08-07T04:01:55+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3550}} +{"ts": "2026-08-07T04:04:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1492}} +{"ts": "2026-08-07T04:04:56+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3369}} +{"ts": "2026-08-07T04:07:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1312}} +{"ts": "2026-08-07T04:07:56+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3189}} +{"ts": "2026-08-07T04:10:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1131}} +{"ts": "2026-08-07T04:10:56+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3009}} +{"ts": "2026-08-07T04:13:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 950}} +{"ts": "2026-08-07T04:13:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2828}} +{"ts": "2026-08-07T04:16:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 769}} +{"ts": "2026-08-07T04:16:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2648}} +{"ts": "2026-08-07T04:19:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 588}} +{"ts": "2026-08-07T04:19:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2467}} +{"ts": "2026-08-07T04:22:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 407}} +{"ts": "2026-08-07T04:22:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2285}} +{"ts": "2026-08-07T04:25:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 226}} +{"ts": "2026-08-07T04:26:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2104}} +{"ts": "2026-08-07T04:28:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 45}} +{"ts": "2026-08-07T04:29:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1923}} +{"ts": "2026-08-07T04:29:12+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-07T04:31:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3466}} +{"ts": "2026-08-07T04:32:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1743}} +{"ts": "2026-08-07T04:34:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3286}} +{"ts": "2026-08-07T04:35:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1562}} +{"ts": "2026-08-07T04:37:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3105}} +{"ts": "2026-08-07T04:38:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1382}} +{"ts": "2026-08-07T04:40:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2924}} +{"ts": "2026-08-07T04:41:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1202}} +{"ts": "2026-08-07T04:43:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2743}} +{"ts": "2026-08-07T04:44:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1021}} +{"ts": "2026-08-07T04:46:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2562}} +{"ts": "2026-08-07T04:47:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 841}} +{"ts": "2026-08-07T04:49:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2382}} +{"ts": "2026-08-07T04:50:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 660}} +{"ts": "2026-08-07T04:52:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2201}} +{"ts": "2026-08-07T04:53:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 479}} +{"ts": "2026-08-07T04:55:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2020}} +{"ts": "2026-08-07T04:56:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 298}} +{"ts": "2026-08-07T04:58:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1839}} +{"ts": "2026-08-07T04:59:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 117}} +{"ts": "2026-08-07T05:01:07+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-07T05:01:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1659}} +{"ts": "2026-08-07T05:02:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3538}} +{"ts": "2026-08-07T05:04:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1478}} +{"ts": "2026-08-07T05:05:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3357}} +{"ts": "2026-08-07T05:07:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1298}} +{"ts": "2026-08-07T05:08:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3177}} +{"ts": "2026-08-07T05:10:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1116}} +{"ts": "2026-08-07T05:11:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2995}} +{"ts": "2026-08-07T05:13:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 936}} +{"ts": "2026-08-07T05:14:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2815}} +{"ts": "2026-08-07T05:16:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 755}} +{"ts": "2026-08-07T05:17:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2635}} +{"ts": "2026-08-07T05:19:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 574}} +{"ts": "2026-08-07T05:20:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2455}} +{"ts": "2026-08-07T05:22:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 394}} +{"ts": "2026-08-07T05:23:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2275}} +{"ts": "2026-08-07T05:25:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 213}} +{"ts": "2026-08-07T05:26:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2094}} +{"ts": "2026-08-07T05:28:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 33}} +{"ts": "2026-08-07T05:29:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1914}} +{"ts": "2026-08-07T05:29:13+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-07T05:31:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3453}} +{"ts": "2026-08-07T05:32:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1733}} +{"ts": "2026-08-07T05:33:01+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPYM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "41e444bf93130aef", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-07T05:33:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3599}} +{"ts": "2026-08-07T05:34:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3273}} +{"ts": "2026-08-07T05:35:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1553}} +{"ts": "2026-08-07T05:36:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3418}} +{"ts": "2026-08-07T05:37:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3092}} +{"ts": "2026-08-07T05:38:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1373}} +{"ts": "2026-08-07T05:39:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3238}} +{"ts": "2026-08-07T05:40:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2912}} +{"ts": "2026-08-07T05:41:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1191}} +{"ts": "2026-08-07T05:42:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3058}} +{"ts": "2026-08-07T05:43:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2732}} +{"ts": "2026-08-07T05:44:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1011}} +{"ts": "2026-08-07T05:45:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2877}} +{"ts": "2026-08-07T05:46:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2552}} +{"ts": "2026-08-07T05:47:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 830}} +{"ts": "2026-08-07T05:48:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2696}} +{"ts": "2026-08-07T05:49:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2371}} +{"ts": "2026-08-07T05:50:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 650}} +{"ts": "2026-08-07T05:51:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2516}} +{"ts": "2026-08-07T05:52:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2189}} +{"ts": "2026-08-07T05:53:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 469}} +{"ts": "2026-08-07T05:54:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2336}} +{"ts": "2026-08-07T05:55:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2009}} +{"ts": "2026-08-07T05:56:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 289}} +{"ts": "2026-08-07T05:57:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2154}} +{"ts": "2026-08-07T05:58:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1829}} +{"ts": "2026-08-07T05:59:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 108}} +{"ts": "2026-08-07T06:00:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1973}} +{"ts": "2026-08-07T09:00:39+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "314140", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "2485c3acdfb56dd5", "extra": {"mock": true}} +{"ts": "2026-08-07T09:00:42+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "087600", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "0bb0bf419693d230", "extra": {"mock": true}} +{"ts": "2026-08-07T15:21:19+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "011040", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "f7fc81ccef17a449", "extra": {"mock": true}} +{"ts": "2026-08-07T15:21:21+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "089600", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "cd35c1aed261c586", "extra": {"mock": true}} +{"ts": "2026-08-07T15:21:28+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "330350", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "cecab4fd5dd311d6", "extra": {"mock": true}} +{"ts": "2026-08-07T15:25:53+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "101490", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "719d74e421af7f02", "extra": {"mock": true}} +{"ts": "2026-08-07T15:25:54+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "232680", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "1cef93753b0a6c51", "extra": {"mock": true}} +{"ts": "2026-08-07T15:25:56+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "469610", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "e9278eb918ecddc9", "extra": {"mock": true}} +{"ts": "2026-08-07T22:47:47+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-07T22:57:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3044}} +{"ts": "2026-08-07T23:00:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2864}} +{"ts": "2026-08-07T23:03:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2683}} +{"ts": "2026-08-07T23:06:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2502}} +{"ts": "2026-08-07T23:09:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2321}} +{"ts": "2026-08-07T23:12:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2141}} +{"ts": "2026-08-07T23:15:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1960}} +{"ts": "2026-08-07T23:18:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1780}} +{"ts": "2026-08-07T23:21:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1599}} +{"ts": "2026-08-07T23:24:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1419}} +{"ts": "2026-08-07T23:27:01+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "TSLA", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "bfa875052195e5f8", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-07T23:27:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3598}} +{"ts": "2026-08-07T23:27:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1238}} +{"ts": "2026-08-07T23:30:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3417}} +{"ts": "2026-08-07T23:30:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1057}} +{"ts": "2026-08-07T23:33:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3236}} +{"ts": "2026-08-07T23:33:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 876}} +{"ts": "2026-08-07T23:36:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3055}} +{"ts": "2026-08-07T23:36:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 695}} +{"ts": "2026-08-07T23:39:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2875}} +{"ts": "2026-08-07T23:39:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 514}} +{"ts": "2026-08-07T23:42:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2694}} +{"ts": "2026-08-07T23:42:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 333}} +{"ts": "2026-08-07T23:45:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2513}} +{"ts": "2026-08-07T23:45:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 152}} +{"ts": "2026-08-07T23:47:48+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-07T23:48:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2333}} +{"ts": "2026-08-07T23:48:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3572}} +{"ts": "2026-08-07T23:51:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2153}} +{"ts": "2026-08-07T23:51:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3391}} +{"ts": "2026-08-07T23:54:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1972}} +{"ts": "2026-08-07T23:54:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3210}} +{"ts": "2026-08-07T23:57:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1791}} +{"ts": "2026-08-07T23:57:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3029}} +{"ts": "2026-08-08T00:00:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1611}} +{"ts": "2026-08-08T00:00:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2849}} +{"ts": "2026-08-08T00:03:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1430}} +{"ts": "2026-08-08T00:03:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2669}} +{"ts": "2026-08-08T00:06:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1249}} +{"ts": "2026-08-08T00:06:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2489}} +{"ts": "2026-08-08T00:09:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1068}} +{"ts": "2026-08-08T00:09:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2308}} +{"ts": "2026-08-08T00:12:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 888}} +{"ts": "2026-08-08T00:12:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2127}} +{"ts": "2026-08-08T00:15:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 706}} +{"ts": "2026-08-08T00:15:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1947}} +{"ts": "2026-08-08T00:18:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 526}} +{"ts": "2026-08-08T00:18:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1766}} +{"ts": "2026-08-08T00:21:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 346}} +{"ts": "2026-08-08T00:21:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1584}} +{"ts": "2026-08-08T00:24:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 165}} +{"ts": "2026-08-08T00:24:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1403}} +{"ts": "2026-08-08T00:27:02+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "TSLA", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "bfa875052195e5f8", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-08T00:27:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3585}} +{"ts": "2026-08-08T00:27:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1223}} +{"ts": "2026-08-08T00:30:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3405}} +{"ts": "2026-08-08T00:30:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1042}} +{"ts": "2026-08-08T00:33:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3224}} +{"ts": "2026-08-08T00:33:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 861}} +{"ts": "2026-08-08T00:36:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3043}} +{"ts": "2026-08-08T00:36:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 680}} +{"ts": "2026-08-08T00:39:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2862}} +{"ts": "2026-08-08T00:39:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 499}} +{"ts": "2026-08-08T00:42:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2681}} +{"ts": "2026-08-08T00:42:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 319}} +{"ts": "2026-08-08T00:45:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2500}} +{"ts": "2026-08-08T00:45:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 138}} +{"ts": "2026-08-08T00:47:49+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-08T00:48:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2319}} +{"ts": "2026-08-08T00:48:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3558}} +{"ts": "2026-08-08T00:51:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2138}} +{"ts": "2026-08-08T00:51:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3377}} +{"ts": "2026-08-08T00:54:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1958}} +{"ts": "2026-08-08T00:54:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3196}} +{"ts": "2026-08-08T00:57:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1777}} +{"ts": "2026-08-08T00:57:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3016}} +{"ts": "2026-08-08T00:59:00+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-08T00:59:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3598}} +{"ts": "2026-08-08T01:00:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1596}} +{"ts": "2026-08-08T01:00:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2836}} +{"ts": "2026-08-08T01:02:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3417}} +{"ts": "2026-08-08T01:03:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1414}} +{"ts": "2026-08-08T01:03:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2655}} +{"ts": "2026-08-08T01:05:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3237}} +{"ts": "2026-08-08T01:06:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1233}} +{"ts": "2026-08-08T01:06:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2474}} +{"ts": "2026-08-08T01:08:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3057}} +{"ts": "2026-08-08T01:09:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1053}} +{"ts": "2026-08-08T01:09:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2293}} +{"ts": "2026-08-08T01:11:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2876}} +{"ts": "2026-08-08T01:12:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 872}} +{"ts": "2026-08-08T01:12:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2112}} +{"ts": "2026-08-08T01:14:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2695}} +{"ts": "2026-08-08T01:15:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 691}} +{"ts": "2026-08-08T01:15:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1931}} +{"ts": "2026-08-08T01:17:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2514}} +{"ts": "2026-08-08T01:18:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 510}} +{"ts": "2026-08-08T01:18:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1750}} +{"ts": "2026-08-08T01:20:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2333}} +{"ts": "2026-08-08T01:21:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 329}} +{"ts": "2026-08-08T01:21:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1569}} +{"ts": "2026-08-08T01:23:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2152}} +{"ts": "2026-08-08T01:23:49+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-08T01:23:50+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-08T01:23:52+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "TSLA", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "bfa875052195e5f8", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-08T01:23:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3595}} +{"ts": "2026-08-08T01:23:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3596}} +{"ts": "2026-08-08T01:23:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3598}} +{"ts": "2026-08-08T01:26:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3415}} +{"ts": "2026-08-08T01:26:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3416}} +{"ts": "2026-08-08T01:26:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3418}} +{"ts": "2026-08-08T01:29:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3234}} +{"ts": "2026-08-08T01:29:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3235}} +{"ts": "2026-08-08T01:29:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3237}} +{"ts": "2026-08-08T01:32:55+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3054}} +{"ts": "2026-08-08T01:32:55+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3055}} +{"ts": "2026-08-08T01:32:55+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3057}} +{"ts": "2026-08-08T01:35:55+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2873}} +{"ts": "2026-08-08T01:35:55+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2874}} +{"ts": "2026-08-08T01:35:55+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2876}} +{"ts": "2026-08-08T01:38:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2692}} +{"ts": "2026-08-08T01:38:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2693}} +{"ts": "2026-08-08T01:38:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2695}} +{"ts": "2026-08-08T01:41:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2511}} +{"ts": "2026-08-08T01:41:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2512}} +{"ts": "2026-08-08T01:41:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2514}} +{"ts": "2026-08-08T01:44:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2331}} +{"ts": "2026-08-08T01:44:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2332}} +{"ts": "2026-08-08T01:44:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2334}} +{"ts": "2026-08-08T01:47:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2150}} +{"ts": "2026-08-08T01:47:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2151}} +{"ts": "2026-08-08T01:47:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2153}} +{"ts": "2026-08-08T01:50:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1970}} +{"ts": "2026-08-08T01:50:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1971}} +{"ts": "2026-08-08T01:50:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1973}} +{"ts": "2026-08-08T01:53:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1789}} +{"ts": "2026-08-08T01:53:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1791}} +{"ts": "2026-08-08T01:53:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1792}} +{"ts": "2026-08-08T01:56:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1609}} +{"ts": "2026-08-08T01:56:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1611}} +{"ts": "2026-08-08T01:56:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1612}} +{"ts": "2026-08-08T01:59:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1429}} +{"ts": "2026-08-08T01:59:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1430}} +{"ts": "2026-08-08T01:59:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1432}} +{"ts": "2026-08-08T02:02:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1249}} +{"ts": "2026-08-08T02:02:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1250}} +{"ts": "2026-08-08T02:02:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1252}} +{"ts": "2026-08-08T02:06:00+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1068}} +{"ts": "2026-08-08T02:06:00+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1070}} +{"ts": "2026-08-08T02:06:00+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1071}} +{"ts": "2026-08-08T02:09:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 887}} +{"ts": "2026-08-08T02:09:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 889}} +{"ts": "2026-08-08T02:09:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 890}} +{"ts": "2026-08-08T02:12:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 706}} +{"ts": "2026-08-08T02:12:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 708}} +{"ts": "2026-08-08T02:12:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 709}} +{"ts": "2026-08-08T02:15:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 526}} +{"ts": "2026-08-08T02:15:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 527}} +{"ts": "2026-08-08T02:15:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 529}} +{"ts": "2026-08-08T02:18:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 345}} +{"ts": "2026-08-08T02:18:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 346}} +{"ts": "2026-08-08T02:18:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 348}} +{"ts": "2026-08-08T02:21:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 164}} +{"ts": "2026-08-08T02:21:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 165}} +{"ts": "2026-08-08T02:21:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 167}} +{"ts": "2026-08-08T02:23:49+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-08T02:23:52+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-08T02:23:53+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "TSLA", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "bfa875052195e5f8", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-08T02:24:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3583}} +{"ts": "2026-08-08T02:24:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3586}} +{"ts": "2026-08-08T02:24:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3587}} +{"ts": "2026-08-08T02:27:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3403}} +{"ts": "2026-08-08T02:27:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3406}} +{"ts": "2026-08-08T02:27:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3406}} +{"ts": "2026-08-08T02:30:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3221}} +{"ts": "2026-08-08T02:30:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3224}} +{"ts": "2026-08-08T02:30:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3224}} +{"ts": "2026-08-08T02:33:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3040}} +{"ts": "2026-08-08T02:33:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3043}} +{"ts": "2026-08-08T02:33:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3043}} +{"ts": "2026-08-08T02:36:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2860}} +{"ts": "2026-08-08T02:36:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2863}} +{"ts": "2026-08-08T02:36:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2863}} +{"ts": "2026-08-08T02:39:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2679}} +{"ts": "2026-08-08T02:39:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2682}} +{"ts": "2026-08-08T02:39:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2682}} +{"ts": "2026-08-08T02:42:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2498}} +{"ts": "2026-08-08T02:42:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2501}} +{"ts": "2026-08-08T02:42:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2501}} +{"ts": "2026-08-08T02:45:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2317}} +{"ts": "2026-08-08T02:45:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2320}} +{"ts": "2026-08-08T02:45:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2320}} +{"ts": "2026-08-08T02:48:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2136}} +{"ts": "2026-08-08T02:48:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2139}} +{"ts": "2026-08-08T02:48:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2139}} +{"ts": "2026-08-08T02:51:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1955}} +{"ts": "2026-08-08T02:51:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1958}} +{"ts": "2026-08-08T02:51:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1959}} +{"ts": "2026-08-08T02:54:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1775}} +{"ts": "2026-08-08T02:54:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1778}} +{"ts": "2026-08-08T02:54:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1778}} +{"ts": "2026-08-08T02:57:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1594}} +{"ts": "2026-08-08T02:57:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1597}} +{"ts": "2026-08-08T02:57:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1597}} +{"ts": "2026-08-08T03:00:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1414}} +{"ts": "2026-08-08T03:00:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1417}} +{"ts": "2026-08-08T03:00:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1417}} +{"ts": "2026-08-08T03:03:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1233}} +{"ts": "2026-08-08T03:03:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1236}} +{"ts": "2026-08-08T03:03:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1237}} +{"ts": "2026-08-08T03:06:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1052}} +{"ts": "2026-08-08T03:06:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1055}} +{"ts": "2026-08-08T03:06:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1055}} +{"ts": "2026-08-08T03:09:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 872}} +{"ts": "2026-08-08T03:09:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 875}} +{"ts": "2026-08-08T03:09:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 875}} +{"ts": "2026-08-08T03:12:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 692}} +{"ts": "2026-08-08T03:12:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 695}} +{"ts": "2026-08-08T03:12:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 695}} +{"ts": "2026-08-08T03:15:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 511}} +{"ts": "2026-08-08T03:15:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 514}} +{"ts": "2026-08-08T03:15:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 514}} +{"ts": "2026-08-08T03:18:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 331}} +{"ts": "2026-08-08T03:18:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 334}} +{"ts": "2026-08-08T03:18:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 334}} +{"ts": "2026-08-08T03:21:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 150}} +{"ts": "2026-08-08T03:21:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 153}} +{"ts": "2026-08-08T03:21:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 154}} +{"ts": "2026-08-08T03:23:50+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-08T03:23:53+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-08T03:23:54+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "TSLA", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "bfa875052195e5f8", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-08T03:24:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3570}} +{"ts": "2026-08-08T03:24:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3573}} +{"ts": "2026-08-08T03:24:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3574}} +{"ts": "2026-08-08T03:27:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3388}} +{"ts": "2026-08-08T03:27:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3391}} +{"ts": "2026-08-08T03:27:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3392}} +{"ts": "2026-08-08T03:30:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3208}} +{"ts": "2026-08-08T03:30:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3211}} +{"ts": "2026-08-08T03:30:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3212}} +{"ts": "2026-08-08T03:33:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3028}} +{"ts": "2026-08-08T03:33:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3031}} +{"ts": "2026-08-08T03:33:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3032}} +{"ts": "2026-08-08T03:36:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2847}} +{"ts": "2026-08-08T03:36:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2850}} +{"ts": "2026-08-08T03:36:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2852}} +{"ts": "2026-08-08T03:39:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2666}} +{"ts": "2026-08-08T03:39:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2669}} +{"ts": "2026-08-08T03:39:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2671}} +{"ts": "2026-08-08T03:42:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2485}} +{"ts": "2026-08-08T03:42:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2488}} +{"ts": "2026-08-08T03:42:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2490}} +{"ts": "2026-08-08T03:45:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2304}} +{"ts": "2026-08-08T03:45:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2307}} +{"ts": "2026-08-08T03:45:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2308}} +{"ts": "2026-08-08T03:48:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2124}} +{"ts": "2026-08-08T03:48:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2127}} +{"ts": "2026-08-08T03:48:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2128}} +{"ts": "2026-08-08T03:51:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1943}} +{"ts": "2026-08-08T03:51:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1946}} +{"ts": "2026-08-08T03:51:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1947}} +{"ts": "2026-08-08T03:54:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1762}} +{"ts": "2026-08-08T03:54:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1765}} +{"ts": "2026-08-08T03:54:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1766}} +{"ts": "2026-08-08T03:57:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1581}} +{"ts": "2026-08-08T03:57:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1584}} +{"ts": "2026-08-08T03:57:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1586}} +{"ts": "2026-08-08T04:00:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1400}} +{"ts": "2026-08-08T04:00:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1403}} +{"ts": "2026-08-08T04:00:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1404}} +{"ts": "2026-08-08T04:03:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1219}} +{"ts": "2026-08-08T04:03:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1222}} +{"ts": "2026-08-08T04:03:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1223}} +{"ts": "2026-08-08T04:06:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1038}} +{"ts": "2026-08-08T04:06:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1041}} +{"ts": "2026-08-08T04:06:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1043}} +{"ts": "2026-08-08T04:09:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 858}} +{"ts": "2026-08-08T04:09:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 861}} +{"ts": "2026-08-08T04:09:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 862}} +{"ts": "2026-08-08T04:12:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 677}} +{"ts": "2026-08-08T04:12:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 680}} +{"ts": "2026-08-08T04:12:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 682}} +{"ts": "2026-08-08T04:15:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 497}} +{"ts": "2026-08-08T04:15:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 500}} +{"ts": "2026-08-08T04:15:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 502}} +{"ts": "2026-08-08T04:18:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 316}} +{"ts": "2026-08-08T04:18:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 319}} +{"ts": "2026-08-08T04:18:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 320}} +{"ts": "2026-08-08T04:21:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 135}} +{"ts": "2026-08-08T04:21:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 138}} +{"ts": "2026-08-08T04:21:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 140}} +{"ts": "2026-08-08T04:23:51+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-08T04:23:54+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-08T04:23:55+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "TSLA", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "bfa875052195e5f8", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-08T04:24:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3555}} +{"ts": "2026-08-08T04:24:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3558}} +{"ts": "2026-08-08T04:24:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3560}} +{"ts": "2026-08-08T04:27:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3375}} +{"ts": "2026-08-08T04:27:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3378}} +{"ts": "2026-08-08T04:27:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3380}} +{"ts": "2026-08-08T04:30:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3195}} +{"ts": "2026-08-08T04:30:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3198}} +{"ts": "2026-08-08T04:30:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3200}} +{"ts": "2026-08-08T04:33:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3014}} +{"ts": "2026-08-08T04:33:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3017}} +{"ts": "2026-08-08T04:33:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3019}} +{"ts": "2026-08-08T04:36:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2833}} +{"ts": "2026-08-08T04:36:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2836}} +{"ts": "2026-08-08T04:36:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2838}} +{"ts": "2026-08-08T04:39:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2653}} +{"ts": "2026-08-08T04:39:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2656}} +{"ts": "2026-08-08T04:39:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2658}} +{"ts": "2026-08-08T04:42:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2472}} +{"ts": "2026-08-08T04:42:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2475}} +{"ts": "2026-08-08T04:42:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2477}} +{"ts": "2026-08-08T04:45:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2291}} +{"ts": "2026-08-08T04:45:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2294}} +{"ts": "2026-08-08T04:45:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2295}} +{"ts": "2026-08-08T04:48:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2110}} +{"ts": "2026-08-08T04:48:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2112}} +{"ts": "2026-08-08T04:48:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2114}} +{"ts": "2026-08-08T04:51:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1929}} +{"ts": "2026-08-08T04:51:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1932}} +{"ts": "2026-08-08T04:51:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1934}} +{"ts": "2026-08-08T04:54:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1749}} +{"ts": "2026-08-08T04:54:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1751}} +{"ts": "2026-08-08T04:54:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1753}} +{"ts": "2026-08-08T04:57:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1568}} +{"ts": "2026-08-08T04:57:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1570}} +{"ts": "2026-08-08T04:57:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1572}} +{"ts": "2026-08-08T05:00:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1388}} +{"ts": "2026-08-08T05:00:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1390}} +{"ts": "2026-08-08T05:00:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1392}} +{"ts": "2026-08-08T05:03:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1207}} +{"ts": "2026-08-08T05:03:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1210}} +{"ts": "2026-08-08T05:03:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1212}} +{"ts": "2026-08-08T05:06:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1027}} +{"ts": "2026-08-08T05:06:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1030}} +{"ts": "2026-08-08T05:06:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1032}} +{"ts": "2026-08-08T05:09:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 846}} +{"ts": "2026-08-08T05:09:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 849}} +{"ts": "2026-08-08T05:09:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 851}} +{"ts": "2026-08-08T05:12:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 666}} +{"ts": "2026-08-08T05:12:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 669}} +{"ts": "2026-08-08T05:12:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 670}} +{"ts": "2026-08-08T05:15:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 486}} +{"ts": "2026-08-08T05:15:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 489}} +{"ts": "2026-08-08T05:15:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 490}} +{"ts": "2026-08-08T05:18:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 306}} +{"ts": "2026-08-08T05:18:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 309}} +{"ts": "2026-08-08T05:18:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 310}} +{"ts": "2026-08-08T05:21:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 125}} +{"ts": "2026-08-08T05:21:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 128}} +{"ts": "2026-08-08T05:21:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 129}} +{"ts": "2026-08-08T05:23:52+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-08T05:23:54+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-08T05:23:56+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "TSLA", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "bfa875052195e5f8", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-08T05:24:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3544}} +{"ts": "2026-08-08T05:24:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3547}} +{"ts": "2026-08-08T05:24:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3549}} +{"ts": "2026-08-08T05:27:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3364}} +{"ts": "2026-08-08T05:27:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3366}} +{"ts": "2026-08-08T05:27:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3368}} +{"ts": "2026-08-08T05:30:48+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3184}} +{"ts": "2026-08-08T05:30:48+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3186}} +{"ts": "2026-08-08T05:30:48+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3188}} +{"ts": "2026-08-08T05:33:48+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3003}} +{"ts": "2026-08-08T05:33:48+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3006}} +{"ts": "2026-08-08T05:33:48+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 3007}} +{"ts": "2026-08-08T05:36:48+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2823}} +{"ts": "2026-08-08T05:36:48+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2825}} +{"ts": "2026-08-08T05:36:48+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2827}} +{"ts": "2026-08-08T05:39:49+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2642}} +{"ts": "2026-08-08T05:39:49+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2644}} +{"ts": "2026-08-08T05:39:49+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2646}} +{"ts": "2026-08-08T05:42:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2461}} +{"ts": "2026-08-08T05:42:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2463}} +{"ts": "2026-08-08T05:42:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2465}} +{"ts": "2026-08-08T05:45:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2280}} +{"ts": "2026-08-08T05:45:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2283}} +{"ts": "2026-08-08T05:45:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2285}} +{"ts": "2026-08-08T05:48:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2100}} +{"ts": "2026-08-08T05:48:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2102}} +{"ts": "2026-08-08T05:48:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 2104}} +{"ts": "2026-08-08T05:51:52+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1919}} +{"ts": "2026-08-08T05:51:52+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1922}} +{"ts": "2026-08-08T05:51:52+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1924}} +{"ts": "2026-08-08T05:54:52+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1739}} +{"ts": "2026-08-08T05:54:52+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1741}} +{"ts": "2026-08-08T05:54:52+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1743}} +{"ts": "2026-08-08T05:57:52+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1559}} +{"ts": "2026-08-08T05:57:52+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1561}} +{"ts": "2026-08-08T05:57:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "TSLA", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "3909903914c34181", "extra": {"remain_sec": 1563}} +{"ts": "2026-08-10T15:20:23+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "086670", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "7584631a0802a56d", "extra": {"mock": true}} +{"ts": "2026-08-10T15:21:24+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "045100", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "07e714ceb38ab78e", "extra": {"mock": true}} +{"ts": "2026-08-10T15:21:30+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "484120", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "6fbd19c15b512db0", "extra": {"mock": true}} +{"ts": "2026-08-10T15:25:14+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "123330", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "802156180694d53c", "extra": {"mock": true}} +{"ts": "2026-08-10T23:42:01+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-10T23:42:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3598}} +{"ts": "2026-08-10T23:44:02+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-10T23:44:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3598}} +{"ts": "2026-08-10T23:45:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3416}} +{"ts": "2026-08-10T23:47:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3416}} +{"ts": "2026-08-10T23:48:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3235}} +{"ts": "2026-08-10T23:50:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3236}} +{"ts": "2026-08-10T23:51:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3055}} +{"ts": "2026-08-10T23:53:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3056}} +{"ts": "2026-08-10T23:54:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2874}} +{"ts": "2026-08-10T23:56:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2875}} +{"ts": "2026-08-10T23:57:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2694}} +{"ts": "2026-08-10T23:59:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2695}} +{"ts": "2026-08-11T00:00:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2513}} +{"ts": "2026-08-11T00:02:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2514}} +{"ts": "2026-08-11T00:03:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2333}} +{"ts": "2026-08-11T00:05:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2333}} +{"ts": "2026-08-11T00:06:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2152}} +{"ts": "2026-08-11T00:08:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2152}} +{"ts": "2026-08-11T00:09:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1971}} +{"ts": "2026-08-11T00:11:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1971}} +{"ts": "2026-08-11T00:12:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1790}} +{"ts": "2026-08-11T00:14:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1790}} +{"ts": "2026-08-11T00:15:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1610}} +{"ts": "2026-08-11T00:17:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1610}} +{"ts": "2026-08-11T00:18:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1429}} +{"ts": "2026-08-11T00:20:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1429}} +{"ts": "2026-08-11T00:21:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1248}} +{"ts": "2026-08-11T00:23:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1248}} +{"ts": "2026-08-11T00:24:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1067}} +{"ts": "2026-08-11T00:26:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1068}} +{"ts": "2026-08-11T00:27:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 886}} +{"ts": "2026-08-11T00:29:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 888}} +{"ts": "2026-08-11T00:30:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 705}} +{"ts": "2026-08-11T00:32:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 707}} +{"ts": "2026-08-11T00:33:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 524}} +{"ts": "2026-08-11T00:35:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 527}} +{"ts": "2026-08-11T00:36:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 344}} +{"ts": "2026-08-11T00:38:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 346}} +{"ts": "2026-08-11T00:39:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 163}} +{"ts": "2026-08-11T00:41:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 166}} +{"ts": "2026-08-11T00:42:03+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-11T00:42:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3583}} +{"ts": "2026-08-11T00:44:04+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-11T00:44:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3587}} +{"ts": "2026-08-11T00:45:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3402}} +{"ts": "2026-08-11T00:47:01+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPYM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "41e444bf93130aef", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-11T00:47:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3598}} +{"ts": "2026-08-11T00:47:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3406}} +{"ts": "2026-08-11T00:48:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3222}} +{"ts": "2026-08-11T00:50:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3417}} +{"ts": "2026-08-11T00:50:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3226}} +{"ts": "2026-08-11T00:51:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3041}} +{"ts": "2026-08-11T00:53:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3237}} +{"ts": "2026-08-11T00:53:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3045}} +{"ts": "2026-08-11T00:54:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2860}} +{"ts": "2026-08-11T00:56:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3056}} +{"ts": "2026-08-11T00:56:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2865}} +{"ts": "2026-08-11T00:57:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2680}} +{"ts": "2026-08-11T00:59:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2875}} +{"ts": "2026-08-11T00:59:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2684}} +{"ts": "2026-08-11T01:00:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2498}} +{"ts": "2026-08-11T01:02:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2695}} +{"ts": "2026-08-11T01:02:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2504}} +{"ts": "2026-08-11T01:03:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2317}} +{"ts": "2026-08-11T01:05:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2514}} +{"ts": "2026-08-11T01:05:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2323}} +{"ts": "2026-08-11T01:06:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2135}} +{"ts": "2026-08-11T01:08:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2334}} +{"ts": "2026-08-11T01:08:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2143}} +{"ts": "2026-08-11T01:09:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1955}} +{"ts": "2026-08-11T01:11:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2153}} +{"ts": "2026-08-11T01:11:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1962}} +{"ts": "2026-08-11T01:12:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1774}} +{"ts": "2026-08-11T01:14:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1972}} +{"ts": "2026-08-11T01:14:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1782}} +{"ts": "2026-08-11T01:15:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1593}} +{"ts": "2026-08-11T01:17:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1791}} +{"ts": "2026-08-11T01:17:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1601}} +{"ts": "2026-08-11T01:18:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1412}} +{"ts": "2026-08-11T01:20:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1611}} +{"ts": "2026-08-11T01:20:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1420}} +{"ts": "2026-08-11T01:21:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1231}} +{"ts": "2026-08-11T01:23:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1431}} +{"ts": "2026-08-11T01:23:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1240}} +{"ts": "2026-08-11T01:24:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1050}} +{"ts": "2026-08-11T01:26:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1250}} +{"ts": "2026-08-11T01:26:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1059}} +{"ts": "2026-08-11T01:27:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 870}} +{"ts": "2026-08-11T01:29:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1069}} +{"ts": "2026-08-11T01:29:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 878}} +{"ts": "2026-08-11T01:30:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 687}} +{"ts": "2026-08-11T01:32:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 887}} +{"ts": "2026-08-11T01:32:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 698}} +{"ts": "2026-08-11T01:33:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 506}} +{"ts": "2026-08-11T01:35:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 707}} +{"ts": "2026-08-11T01:35:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 517}} +{"ts": "2026-08-11T01:36:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 326}} +{"ts": "2026-08-11T01:38:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 525}} +{"ts": "2026-08-11T01:38:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 336}} +{"ts": "2026-08-11T01:39:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 145}} +{"ts": "2026-08-11T01:41:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 345}} +{"ts": "2026-08-11T01:41:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 155}} +{"ts": "2026-08-11T01:42:04+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-11T01:42:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3565}} +{"ts": "2026-08-11T01:44:06+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-11T01:44:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 164}} +{"ts": "2026-08-11T01:44:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3576}} +{"ts": "2026-08-11T01:45:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3385}} +{"ts": "2026-08-11T01:47:03+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPYM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "41e444bf93130aef", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-11T01:47:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3586}} +{"ts": "2026-08-11T01:47:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3395}} +{"ts": "2026-08-11T01:48:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3204}} +{"ts": "2026-08-11T01:50:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3404}} +{"ts": "2026-08-11T01:50:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3215}} +{"ts": "2026-08-11T01:51:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3023}} +{"ts": "2026-08-11T01:53:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3223}} +{"ts": "2026-08-11T01:53:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3034}} +{"ts": "2026-08-11T01:54:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2842}} +{"ts": "2026-08-11T01:56:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3043}} +{"ts": "2026-08-11T01:56:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2854}} +{"ts": "2026-08-11T01:57:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2662}} +{"ts": "2026-08-11T01:59:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2862}} +{"ts": "2026-08-11T01:59:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2673}} +{"ts": "2026-08-11T02:00:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2481}} +{"ts": "2026-08-11T02:02:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2681}} +{"ts": "2026-08-11T02:02:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2493}} +{"ts": "2026-08-11T02:03:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2301}} +{"ts": "2026-08-11T02:05:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2501}} +{"ts": "2026-08-11T02:05:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2312}} +{"ts": "2026-08-11T02:06:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2120}} +{"ts": "2026-08-11T02:08:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2320}} +{"ts": "2026-08-11T02:08:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2132}} +{"ts": "2026-08-11T02:09:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1939}} +{"ts": "2026-08-11T02:11:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2139}} +{"ts": "2026-08-11T02:11:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1951}} +{"ts": "2026-08-11T02:12:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1758}} +{"ts": "2026-08-11T02:14:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1958}} +{"ts": "2026-08-11T02:14:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1771}} +{"ts": "2026-08-11T02:15:46+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1578}} +{"ts": "2026-08-11T02:17:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1778}} +{"ts": "2026-08-11T02:17:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1590}} +{"ts": "2026-08-11T02:18:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1396}} +{"ts": "2026-08-11T02:20:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1597}} +{"ts": "2026-08-11T02:20:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1409}} +{"ts": "2026-08-11T02:21:49+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1215}} +{"ts": "2026-08-11T02:23:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1417}} +{"ts": "2026-08-11T02:23:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1228}} +{"ts": "2026-08-11T02:24:49+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1034}} +{"ts": "2026-08-11T02:26:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1237}} +{"ts": "2026-08-11T02:26:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1048}} +{"ts": "2026-08-11T02:27:50+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 853}} +{"ts": "2026-08-11T02:29:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1055}} +{"ts": "2026-08-11T02:29:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 868}} +{"ts": "2026-08-11T02:30:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 673}} +{"ts": "2026-08-11T02:32:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 875}} +{"ts": "2026-08-11T02:32:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 687}} +{"ts": "2026-08-11T02:33:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 493}} +{"ts": "2026-08-11T02:35:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 693}} +{"ts": "2026-08-11T02:35:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 506}} +{"ts": "2026-08-11T02:36:52+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 311}} +{"ts": "2026-08-11T02:38:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 512}} +{"ts": "2026-08-11T02:38:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 325}} +{"ts": "2026-08-11T02:39:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 131}} +{"ts": "2026-08-11T02:41:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 331}} +{"ts": "2026-08-11T02:41:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 144}} +{"ts": "2026-08-11T02:42:05+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-11T02:42:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3550}} +{"ts": "2026-08-11T02:44:07+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-11T02:44:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 151}} +{"ts": "2026-08-11T02:44:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3565}} +{"ts": "2026-08-11T02:45:55+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3369}} +{"ts": "2026-08-11T02:47:05+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPYM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "41e444bf93130aef", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-11T02:47:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3571}} +{"ts": "2026-08-11T02:47:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3384}} +{"ts": "2026-08-11T02:48:56+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3188}} +{"ts": "2026-08-11T02:50:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3391}} +{"ts": "2026-08-11T02:50:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3203}} +{"ts": "2026-08-11T02:51:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3007}} +{"ts": "2026-08-11T02:53:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3211}} +{"ts": "2026-08-11T02:53:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3022}} +{"ts": "2026-08-11T02:54:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2826}} +{"ts": "2026-08-11T02:56:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3031}} +{"ts": "2026-08-11T02:56:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2841}} +{"ts": "2026-08-11T02:58:00+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2645}} +{"ts": "2026-08-11T02:59:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2850}} +{"ts": "2026-08-11T02:59:46+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2660}} +{"ts": "2026-08-11T03:01:00+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2464}} +{"ts": "2026-08-11T03:02:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2669}} +{"ts": "2026-08-11T03:02:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2479}} +{"ts": "2026-08-11T03:04:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2283}} +{"ts": "2026-08-11T03:05:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2489}} +{"ts": "2026-08-11T03:05:48+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2298}} +{"ts": "2026-08-11T03:07:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2103}} +{"ts": "2026-08-11T03:08:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2309}} +{"ts": "2026-08-11T03:08:49+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2117}} +{"ts": "2026-08-11T03:10:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1923}} +{"ts": "2026-08-11T03:11:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2128}} +{"ts": "2026-08-11T03:11:49+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1937}} +{"ts": "2026-08-11T03:13:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1742}} +{"ts": "2026-08-11T03:14:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1948}} +{"ts": "2026-08-11T03:14:49+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1757}} +{"ts": "2026-08-11T03:16:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1561}} +{"ts": "2026-08-11T03:17:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1768}} +{"ts": "2026-08-11T03:17:50+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1576}} +{"ts": "2026-08-11T03:19:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1380}} +{"ts": "2026-08-11T03:20:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1587}} +{"ts": "2026-08-11T03:20:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1395}} +{"ts": "2026-08-11T03:22:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1200}} +{"ts": "2026-08-11T03:23:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1407}} +{"ts": "2026-08-11T03:23:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1215}} +{"ts": "2026-08-11T03:25:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1019}} +{"ts": "2026-08-11T03:26:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1226}} +{"ts": "2026-08-11T03:26:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1033}} +{"ts": "2026-08-11T03:28:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 839}} +{"ts": "2026-08-11T03:29:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1046}} +{"ts": "2026-08-11T03:29:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 853}} +{"ts": "2026-08-11T03:31:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 658}} +{"ts": "2026-08-11T03:32:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 866}} +{"ts": "2026-08-11T03:32:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 673}} +{"ts": "2026-08-11T03:34:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 478}} +{"ts": "2026-08-11T03:35:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 684}} +{"ts": "2026-08-11T03:35:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 492}} +{"ts": "2026-08-11T03:37:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 297}} +{"ts": "2026-08-11T03:38:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 503}} +{"ts": "2026-08-11T03:38:55+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 311}} +{"ts": "2026-08-11T03:40:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 116}} +{"ts": "2026-08-11T03:41:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 322}} +{"ts": "2026-08-11T03:41:55+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 131}} +{"ts": "2026-08-11T03:42:07+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-11T03:43:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3536}} +{"ts": "2026-08-11T03:44:09+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-11T03:44:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 141}} +{"ts": "2026-08-11T03:44:56+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3553}} +{"ts": "2026-08-11T03:46:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3355}} +{"ts": "2026-08-11T03:47:06+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPYM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "41e444bf93130aef", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-11T03:47:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3562}} +{"ts": "2026-08-11T03:47:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3372}} +{"ts": "2026-08-11T03:49:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3175}} +{"ts": "2026-08-11T03:50:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3381}} +{"ts": "2026-08-11T03:50:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3192}} +{"ts": "2026-08-11T03:52:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2994}} +{"ts": "2026-08-11T03:53:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3201}} +{"ts": "2026-08-11T03:53:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3011}} +{"ts": "2026-08-11T03:55:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2813}} +{"ts": "2026-08-11T03:56:46+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3020}} +{"ts": "2026-08-11T03:56:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2830}} +{"ts": "2026-08-11T03:58:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2633}} +{"ts": "2026-08-11T03:59:48+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2838}} +{"ts": "2026-08-11T04:00:00+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2649}} +{"ts": "2026-08-11T04:01:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2453}} +{"ts": "2026-08-11T04:02:49+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2657}} +{"ts": "2026-08-11T04:03:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2467}} +{"ts": "2026-08-11T04:04:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2272}} +{"ts": "2026-08-11T04:05:49+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2477}} +{"ts": "2026-08-11T04:06:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2287}} +{"ts": "2026-08-11T04:07:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2092}} +{"ts": "2026-08-11T04:08:49+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2296}} +{"ts": "2026-08-11T04:09:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2105}} +{"ts": "2026-08-11T04:10:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1912}} +{"ts": "2026-08-11T04:11:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2115}} +{"ts": "2026-08-11T04:12:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1924}} +{"ts": "2026-08-11T04:13:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1731}} +{"ts": "2026-08-11T04:14:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1935}} +{"ts": "2026-08-11T04:15:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1743}} +{"ts": "2026-08-11T04:16:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1550}} +{"ts": "2026-08-11T04:17:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1753}} +{"ts": "2026-08-11T04:18:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1562}} +{"ts": "2026-08-11T04:19:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1369}} +{"ts": "2026-08-11T04:20:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1573}} +{"ts": "2026-08-11T04:21:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1380}} +{"ts": "2026-08-11T04:22:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1189}} +{"ts": "2026-08-11T04:23:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1393}} +{"ts": "2026-08-11T04:24:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1200}} +{"ts": "2026-08-11T04:25:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1007}} +{"ts": "2026-08-11T04:26:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1212}} +{"ts": "2026-08-11T04:27:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1019}} +{"ts": "2026-08-11T04:28:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 827}} +{"ts": "2026-08-11T04:29:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1032}} +{"ts": "2026-08-11T04:30:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 838}} +{"ts": "2026-08-11T04:31:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 646}} +{"ts": "2026-08-11T04:32:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 852}} +{"ts": "2026-08-11T04:33:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 657}} +{"ts": "2026-08-11T04:34:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 465}} +{"ts": "2026-08-11T04:35:55+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 671}} +{"ts": "2026-08-11T04:36:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 476}} +{"ts": "2026-08-11T04:37:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 285}} +{"ts": "2026-08-11T04:38:56+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 489}} +{"ts": "2026-08-11T04:39:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 295}} +{"ts": "2026-08-11T04:40:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 104}} +{"ts": "2026-08-11T04:41:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 308}} +{"ts": "2026-08-11T04:42:08+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-11T04:42:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 115}} +{"ts": "2026-08-11T04:43:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3525}} +{"ts": "2026-08-11T04:44:12+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-11T04:44:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 128}} +{"ts": "2026-08-11T04:45:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3536}} +{"ts": "2026-08-11T04:46:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3344}} +{"ts": "2026-08-11T04:47:08+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPYM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "41e444bf93130aef", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-11T04:47:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3549}} +{"ts": "2026-08-11T04:48:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3355}} +{"ts": "2026-08-11T04:49:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3163}} +{"ts": "2026-08-11T04:50:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3369}} +{"ts": "2026-08-11T04:51:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3174}} +{"ts": "2026-08-11T04:52:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2982}} +{"ts": "2026-08-11T04:54:00+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3188}} +{"ts": "2026-08-11T04:54:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2993}} +{"ts": "2026-08-11T04:55:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2802}} +{"ts": "2026-08-11T04:57:00+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3007}} +{"ts": "2026-08-11T04:57:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2811}} +{"ts": "2026-08-11T04:58:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2621}} +{"ts": "2026-08-11T05:00:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2826}} +{"ts": "2026-08-11T05:00:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2630}} +{"ts": "2026-08-11T05:01:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2441}} +{"ts": "2026-08-11T05:03:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2644}} +{"ts": "2026-08-11T05:03:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2450}} +{"ts": "2026-08-11T05:04:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2259}} +{"ts": "2026-08-11T05:06:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2463}} +{"ts": "2026-08-11T05:06:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2269}} +{"ts": "2026-08-11T05:07:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2079}} +{"ts": "2026-08-11T05:09:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2283}} +{"ts": "2026-08-11T05:09:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2088}} +{"ts": "2026-08-11T05:10:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1898}} +{"ts": "2026-08-11T05:12:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2102}} +{"ts": "2026-08-11T05:12:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1907}} +{"ts": "2026-08-11T05:13:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1717}} +{"ts": "2026-08-11T05:15:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1922}} +{"ts": "2026-08-11T05:15:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1727}} +{"ts": "2026-08-11T05:16:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1536}} +{"ts": "2026-08-11T05:18:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1741}} +{"ts": "2026-08-11T05:18:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1546}} +{"ts": "2026-08-11T05:19:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1354}} +{"ts": "2026-08-11T05:21:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1560}} +{"ts": "2026-08-11T05:21:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1366}} +{"ts": "2026-08-11T05:22:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1174}} +{"ts": "2026-08-11T05:24:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1380}} +{"ts": "2026-08-11T05:24:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1185}} +{"ts": "2026-08-11T05:25:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 993}} +{"ts": "2026-08-11T05:27:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1200}} +{"ts": "2026-08-11T05:27:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1004}} +{"ts": "2026-08-11T05:28:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 812}} +{"ts": "2026-08-11T05:30:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1019}} +{"ts": "2026-08-11T05:30:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 824}} +{"ts": "2026-08-11T05:31:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 632}} +{"ts": "2026-08-11T05:33:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 839}} +{"ts": "2026-08-11T05:33:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 644}} +{"ts": "2026-08-11T05:34:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 450}} +{"ts": "2026-08-11T05:36:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 659}} +{"ts": "2026-08-11T05:36:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 464}} +{"ts": "2026-08-11T05:37:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 269}} +{"ts": "2026-08-11T05:39:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 479}} +{"ts": "2026-08-11T05:39:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 284}} +{"ts": "2026-08-11T05:40:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 88}} +{"ts": "2026-08-11T05:42:09+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-11T05:42:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 299}} +{"ts": "2026-08-11T05:42:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 102}} +{"ts": "2026-08-11T05:43:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3508}} +{"ts": "2026-08-11T05:44:13+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-11T05:45:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 118}} +{"ts": "2026-08-11T05:45:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3523}} +{"ts": "2026-08-11T05:46:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3327}} +{"ts": "2026-08-11T05:47:08+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPYM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "41e444bf93130aef", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-11T05:48:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3538}} +{"ts": "2026-08-11T05:48:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3343}} +{"ts": "2026-08-11T05:49:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3147}} +{"ts": "2026-08-11T05:51:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3358}} +{"ts": "2026-08-11T05:51:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3163}} +{"ts": "2026-08-11T05:52:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2967}} +{"ts": "2026-08-11T05:54:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3177}} +{"ts": "2026-08-11T05:54:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2983}} +{"ts": "2026-08-11T05:55:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2785}} +{"ts": "2026-08-11T05:57:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2996}} +{"ts": "2026-08-11T05:57:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2802}} +{"ts": "2026-08-11T05:58:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2604}} +{"ts": "2026-08-11T15:24:41+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "161000", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "b53f4900a96a91dd", "extra": {"mock": true}} +{"ts": "2026-08-11T15:25:38+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "059120", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "339028762bfb6b0d", "extra": {"mock": true}} +{"ts": "2026-08-11T15:25:50+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "160980", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "2659f5054ecb392f", "extra": {"mock": true}} +{"ts": "2026-08-11T15:26:53+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "049960", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "465e91bb4cf05097", "extra": {"mock": true}} +{"ts": "2026-08-11T15:26:59+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "039980", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "72181809844760e0", "extra": {"mock": true}} +{"ts": "2026-08-11T15:27:06+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "319400", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "5edc6aae3c47b5a9", "extra": {"mock": true}} +{"ts": "2026-08-11T15:27:18+09:00", "kind": "domestic_order_reject", "strategy_id": "", "side": "SELL", "code": "123420", "msg_cd": "40240000", "msg1": "모의투자 잔고내역이 없습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/domestic-stock/v1/trading/order-cash", "fingerprint": "06e7a13ebefeb12c", "extra": {"mock": true}} +{"ts": "2026-08-11T22:57:02+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-11T22:57:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3598}} +{"ts": "2026-08-11T23:00:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3413}} +{"ts": "2026-08-11T23:03:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3232}} +{"ts": "2026-08-11T23:06:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3051}} +{"ts": "2026-08-11T23:09:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2871}} +{"ts": "2026-08-11T23:12:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2689}} +{"ts": "2026-08-11T23:15:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2509}} +{"ts": "2026-08-11T23:18:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2328}} +{"ts": "2026-08-11T23:21:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2146}} +{"ts": "2026-08-11T23:24:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1965}} +{"ts": "2026-08-11T23:27:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1784}} +{"ts": "2026-08-11T23:30:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1604}} +{"ts": "2026-08-11T23:33:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1424}} +{"ts": "2026-08-11T23:36:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1243}} +{"ts": "2026-08-11T23:39:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1062}} +{"ts": "2026-08-11T23:42:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 882}} +{"ts": "2026-08-11T23:45:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 699}} +{"ts": "2026-08-11T23:48:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 518}} +{"ts": "2026-08-11T23:51:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 337}} +{"ts": "2026-08-11T23:54:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 156}} +{"ts": "2026-08-11T23:57:04+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-11T23:57:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3577}} +{"ts": "2026-08-12T00:00:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3393}} +{"ts": "2026-08-12T00:03:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3211}} +{"ts": "2026-08-12T00:06:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3030}} +{"ts": "2026-08-12T00:09:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2848}} +{"ts": "2026-08-12T00:11:02+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-12T00:11:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3597}} +{"ts": "2026-08-12T00:12:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2667}} +{"ts": "2026-08-12T00:14:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3416}} +{"ts": "2026-08-12T00:15:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2485}} +{"ts": "2026-08-12T00:17:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3235}} +{"ts": "2026-08-12T00:18:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2303}} +{"ts": "2026-08-12T00:20:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3054}} +{"ts": "2026-08-12T00:21:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2122}} +{"ts": "2026-08-12T00:23:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2873}} +{"ts": "2026-08-12T00:24:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1941}} +{"ts": "2026-08-12T00:26:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2692}} +{"ts": "2026-08-12T00:27:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1760}} +{"ts": "2026-08-12T00:29:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2511}} +{"ts": "2026-08-12T00:30:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1578}} +{"ts": "2026-08-12T00:32:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2329}} +{"ts": "2026-08-12T00:33:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1396}} +{"ts": "2026-08-12T00:35:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2149}} +{"ts": "2026-08-12T00:36:48+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1216}} +{"ts": "2026-08-12T00:38:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1969}} +{"ts": "2026-08-12T00:39:48+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1035}} +{"ts": "2026-08-12T00:41:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1788}} +{"ts": "2026-08-12T00:42:48+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 855}} +{"ts": "2026-08-12T00:44:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1608}} +{"ts": "2026-08-12T00:45:48+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 675}} +{"ts": "2026-08-12T00:47:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1428}} +{"ts": "2026-08-12T00:48:49+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 494}} +{"ts": "2026-08-12T00:50:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1246}} +{"ts": "2026-08-12T00:51:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 313}} +{"ts": "2026-08-12T00:53:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1066}} +{"ts": "2026-08-12T00:54:52+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 131}} +{"ts": "2026-08-12T00:56:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 885}} +{"ts": "2026-08-12T00:57:06+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-12T00:57:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3552}} +{"ts": "2026-08-12T00:59:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 704}} +{"ts": "2026-08-12T01:00:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3372}} +{"ts": "2026-08-12T01:02:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 524}} +{"ts": "2026-08-12T01:03:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3192}} +{"ts": "2026-08-12T01:05:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 344}} +{"ts": "2026-08-12T01:06:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3012}} +{"ts": "2026-08-12T01:08:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 163}} +{"ts": "2026-08-12T01:09:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2831}} +{"ts": "2026-08-12T01:11:04+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-12T01:11:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3585}} +{"ts": "2026-08-12T01:12:56+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2650}} +{"ts": "2026-08-12T01:14:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3403}} +{"ts": "2026-08-12T01:15:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2469}} +{"ts": "2026-08-12T01:17:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3223}} +{"ts": "2026-08-12T01:18:01+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPYM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "41e444bf93130aef", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-12T01:18:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3598}} +{"ts": "2026-08-12T01:18:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2287}} +{"ts": "2026-08-12T01:20:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3041}} +{"ts": "2026-08-12T01:21:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3418}} +{"ts": "2026-08-12T01:22:00+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2106}} +{"ts": "2026-08-12T01:23:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2860}} +{"ts": "2026-08-12T01:24:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3237}} +{"ts": "2026-08-12T01:25:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1925}} +{"ts": "2026-08-12T01:26:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2679}} +{"ts": "2026-08-12T01:27:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3055}} +{"ts": "2026-08-12T01:28:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1745}} +{"ts": "2026-08-12T01:29:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2498}} +{"ts": "2026-08-12T01:30:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2873}} +{"ts": "2026-08-12T01:31:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1564}} +{"ts": "2026-08-12T01:32:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2317}} +{"ts": "2026-08-12T01:33:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2691}} +{"ts": "2026-08-12T01:34:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1383}} +{"ts": "2026-08-12T01:35:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2135}} +{"ts": "2026-08-12T01:36:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2510}} +{"ts": "2026-08-12T01:37:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1202}} +{"ts": "2026-08-12T01:38:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1955}} +{"ts": "2026-08-12T01:39:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2328}} +{"ts": "2026-08-12T01:40:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1021}} +{"ts": "2026-08-12T01:41:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1775}} +{"ts": "2026-08-12T01:42:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2147}} +{"ts": "2026-08-12T01:43:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 841}} +{"ts": "2026-08-12T01:44:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1594}} +{"ts": "2026-08-12T01:45:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1966}} +{"ts": "2026-08-12T01:46:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 659}} +{"ts": "2026-08-12T01:47:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1412}} +{"ts": "2026-08-12T01:48:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1785}} +{"ts": "2026-08-12T01:49:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 479}} +{"ts": "2026-08-12T01:50:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1232}} +{"ts": "2026-08-12T01:51:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1605}} +{"ts": "2026-08-12T01:52:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 298}} +{"ts": "2026-08-12T01:53:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1050}} +{"ts": "2026-08-12T01:54:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1425}} +{"ts": "2026-08-12T01:55:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 118}} +{"ts": "2026-08-12T01:56:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 869}} +{"ts": "2026-08-12T01:57:07+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-12T01:57:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1245}} +{"ts": "2026-08-12T01:58:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3538}} +{"ts": "2026-08-12T01:59:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 689}} +{"ts": "2026-08-12T02:00:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1063}} +{"ts": "2026-08-12T02:01:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3354}} +{"ts": "2026-08-12T02:02:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 506}} +{"ts": "2026-08-12T02:03:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 881}} +{"ts": "2026-08-12T02:04:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3173}} +{"ts": "2026-08-12T02:05:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 326}} +{"ts": "2026-08-12T02:06:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 699}} +{"ts": "2026-08-12T02:07:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2991}} +{"ts": "2026-08-12T02:08:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 145}} +{"ts": "2026-08-12T02:09:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 519}} +{"ts": "2026-08-12T02:10:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2811}} +{"ts": "2026-08-12T02:11:07+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-12T02:11:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3567}} +{"ts": "2026-08-12T02:12:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 338}} +{"ts": "2026-08-12T02:13:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2629}} +{"ts": "2026-08-12T02:14:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3385}} +{"ts": "2026-08-12T02:15:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 158}} +{"ts": "2026-08-12T02:16:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2449}} +{"ts": "2026-08-12T02:17:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3204}} +{"ts": "2026-08-12T02:18:02+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPYM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "41e444bf93130aef", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-12T02:18:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3580}} +{"ts": "2026-08-12T02:19:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2268}} +{"ts": "2026-08-12T02:20:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3023}} +{"ts": "2026-08-12T02:21:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3400}} +{"ts": "2026-08-12T02:22:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2087}} +{"ts": "2026-08-12T02:23:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2842}} +{"ts": "2026-08-12T02:24:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3219}} +{"ts": "2026-08-12T02:25:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1906}} +{"ts": "2026-08-12T02:26:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2662}} +{"ts": "2026-08-12T02:27:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3039}} +{"ts": "2026-08-12T02:28:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1725}} +{"ts": "2026-08-12T02:29:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2481}} +{"ts": "2026-08-12T02:30:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2858}} +{"ts": "2026-08-12T02:31:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1544}} +{"ts": "2026-08-12T02:32:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2301}} +{"ts": "2026-08-12T02:33:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2677}} +{"ts": "2026-08-12T02:34:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1362}} +{"ts": "2026-08-12T02:35:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2119}} +{"ts": "2026-08-12T02:36:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2496}} +{"ts": "2026-08-12T02:37:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1181}} +{"ts": "2026-08-12T02:38:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1939}} +{"ts": "2026-08-12T02:39:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2315}} +{"ts": "2026-08-12T02:40:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1000}} +{"ts": "2026-08-12T02:41:48+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1758}} +{"ts": "2026-08-12T02:42:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2133}} +{"ts": "2026-08-12T02:43:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 819}} +{"ts": "2026-08-12T02:44:49+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1577}} +{"ts": "2026-08-12T02:45:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1952}} +{"ts": "2026-08-12T02:46:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 637}} +{"ts": "2026-08-12T02:47:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1395}} +{"ts": "2026-08-12T02:48:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1771}} +{"ts": "2026-08-12T02:49:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 456}} +{"ts": "2026-08-12T02:50:52+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1214}} +{"ts": "2026-08-12T02:51:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1590}} +{"ts": "2026-08-12T02:52:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 276}} +{"ts": "2026-08-12T02:53:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1033}} +{"ts": "2026-08-12T02:54:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1409}} +{"ts": "2026-08-12T02:55:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 95}} +{"ts": "2026-08-12T02:56:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 852}} +{"ts": "2026-08-12T02:57:09+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-12T02:57:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1229}} +{"ts": "2026-08-12T02:58:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3515}} +{"ts": "2026-08-12T02:59:55+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 671}} +{"ts": "2026-08-12T03:00:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1048}} +{"ts": "2026-08-12T03:01:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3334}} +{"ts": "2026-08-12T03:02:56+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 490}} +{"ts": "2026-08-12T03:03:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 867}} +{"ts": "2026-08-12T03:04:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3153}} +{"ts": "2026-08-12T03:05:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 310}} +{"ts": "2026-08-12T03:06:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 686}} +{"ts": "2026-08-12T03:07:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2972}} +{"ts": "2026-08-12T03:08:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 130}} +{"ts": "2026-08-12T03:09:37+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 505}} +{"ts": "2026-08-12T03:10:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2791}} +{"ts": "2026-08-12T03:11:09+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-12T03:11:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3552}} +{"ts": "2026-08-12T03:12:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 324}} +{"ts": "2026-08-12T03:13:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2610}} +{"ts": "2026-08-12T03:14:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3372}} +{"ts": "2026-08-12T03:15:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 143}} +{"ts": "2026-08-12T03:16:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2430}} +{"ts": "2026-08-12T03:17:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3191}} +{"ts": "2026-08-12T03:18:04+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPYM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "41e444bf93130aef", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-12T03:18:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3565}} +{"ts": "2026-08-12T03:19:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2249}} +{"ts": "2026-08-12T03:20:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3011}} +{"ts": "2026-08-12T03:21:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3383}} +{"ts": "2026-08-12T03:22:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2069}} +{"ts": "2026-08-12T03:23:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2830}} +{"ts": "2026-08-12T03:24:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3202}} +{"ts": "2026-08-12T03:25:40+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1888}} +{"ts": "2026-08-12T03:26:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2650}} +{"ts": "2026-08-12T03:27:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3020}} +{"ts": "2026-08-12T03:28:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1707}} +{"ts": "2026-08-12T03:30:00+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2469}} +{"ts": "2026-08-12T03:30:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2840}} +{"ts": "2026-08-12T03:31:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1527}} +{"ts": "2026-08-12T03:33:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2288}} +{"ts": "2026-08-12T03:33:46+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2658}} +{"ts": "2026-08-12T03:34:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1347}} +{"ts": "2026-08-12T03:36:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2106}} +{"ts": "2026-08-12T03:36:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2477}} +{"ts": "2026-08-12T03:37:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1166}} +{"ts": "2026-08-12T03:39:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1925}} +{"ts": "2026-08-12T03:39:48+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2296}} +{"ts": "2026-08-12T03:40:43+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 986}} +{"ts": "2026-08-12T03:42:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1744}} +{"ts": "2026-08-12T03:42:50+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2114}} +{"ts": "2026-08-12T03:43:44+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 804}} +{"ts": "2026-08-12T03:45:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1563}} +{"ts": "2026-08-12T03:45:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1933}} +{"ts": "2026-08-12T03:46:45+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 623}} +{"ts": "2026-08-12T03:48:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1381}} +{"ts": "2026-08-12T03:48:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1753}} +{"ts": "2026-08-12T03:49:46+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 443}} +{"ts": "2026-08-12T03:51:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1201}} +{"ts": "2026-08-12T03:51:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1571}} +{"ts": "2026-08-12T03:52:46+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 262}} +{"ts": "2026-08-12T03:54:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1020}} +{"ts": "2026-08-12T03:54:53+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1390}} +{"ts": "2026-08-12T03:55:46+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 82}} +{"ts": "2026-08-12T03:57:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 840}} +{"ts": "2026-08-12T03:57:11+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-12T03:57:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1210}} +{"ts": "2026-08-12T03:58:47+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3503}} +{"ts": "2026-08-12T04:00:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 657}} +{"ts": "2026-08-12T04:00:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1030}} +{"ts": "2026-08-12T04:01:48+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3323}} +{"ts": "2026-08-12T04:03:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 477}} +{"ts": "2026-08-12T04:03:55+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 849}} +{"ts": "2026-08-12T04:04:49+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3141}} +{"ts": "2026-08-12T04:06:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 296}} +{"ts": "2026-08-12T04:06:56+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 667}} +{"ts": "2026-08-12T04:07:49+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2961}} +{"ts": "2026-08-12T04:09:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 115}} +{"ts": "2026-08-12T04:09:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 487}} +{"ts": "2026-08-12T04:10:50+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2780}} +{"ts": "2026-08-12T04:11:12+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-12T04:12:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3537}} +{"ts": "2026-08-12T04:12:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 306}} +{"ts": "2026-08-12T04:13:51+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2599}} +{"ts": "2026-08-12T04:15:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3357}} +{"ts": "2026-08-12T04:15:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 124}} +{"ts": "2026-08-12T04:16:52+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2418}} +{"ts": "2026-08-12T04:18:05+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPYM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "41e444bf93130aef", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-12T04:18:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3177}} +{"ts": "2026-08-12T04:19:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3543}} +{"ts": "2026-08-12T04:19:52+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2238}} +{"ts": "2026-08-12T04:21:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2996}} +{"ts": "2026-08-12T04:22:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3362}} +{"ts": "2026-08-12T04:22:52+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2058}} +{"ts": "2026-08-12T04:24:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2814}} +{"ts": "2026-08-12T04:25:03+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3181}} +{"ts": "2026-08-12T04:25:52+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1878}} +{"ts": "2026-08-12T04:27:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2634}} +{"ts": "2026-08-12T04:28:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3000}} +{"ts": "2026-08-12T04:28:54+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1696}} +{"ts": "2026-08-12T04:30:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2452}} +{"ts": "2026-08-12T04:31:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2819}} +{"ts": "2026-08-12T04:31:55+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1515}} +{"ts": "2026-08-12T04:33:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2271}} +{"ts": "2026-08-12T04:34:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2638}} +{"ts": "2026-08-12T04:34:55+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1335}} +{"ts": "2026-08-12T04:36:21+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2090}} +{"ts": "2026-08-12T04:37:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2456}} +{"ts": "2026-08-12T04:37:56+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1154}} +{"ts": "2026-08-12T04:39:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1908}} +{"ts": "2026-08-12T04:40:08+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2276}} +{"ts": "2026-08-12T04:40:56+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 974}} +{"ts": "2026-08-12T04:42:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1727}} +{"ts": "2026-08-12T04:43:10+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2095}} +{"ts": "2026-08-12T04:43:56+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 794}} +{"ts": "2026-08-12T04:45:24+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1547}} +{"ts": "2026-08-12T04:46:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1913}} +{"ts": "2026-08-12T04:46:57+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 613}} +{"ts": "2026-08-12T04:48:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1366}} +{"ts": "2026-08-12T04:49:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1733}} +{"ts": "2026-08-12T04:49:58+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 432}} +{"ts": "2026-08-12T04:51:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1185}} +{"ts": "2026-08-12T04:52:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1552}} +{"ts": "2026-08-12T04:52:59+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 252}} +{"ts": "2026-08-12T04:54:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1005}} +{"ts": "2026-08-12T04:55:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1371}} +{"ts": "2026-08-12T04:56:00+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 70}} +{"ts": "2026-08-12T04:57:12+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-12T04:57:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 824}} +{"ts": "2026-08-12T04:58:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1190}} +{"ts": "2026-08-12T04:59:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3491}} +{"ts": "2026-08-12T05:00:28+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 643}} +{"ts": "2026-08-12T05:01:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1010}} +{"ts": "2026-08-12T05:02:01+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3311}} +{"ts": "2026-08-12T05:03:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 462}} +{"ts": "2026-08-12T05:04:15+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 829}} +{"ts": "2026-08-12T05:05:02+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3129}} +{"ts": "2026-08-12T05:06:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 281}} +{"ts": "2026-08-12T05:07:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 648}} +{"ts": "2026-08-12T05:08:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2948}} +{"ts": "2026-08-12T05:09:30+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 101}} +{"ts": "2026-08-12T05:10:16+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 468}} +{"ts": "2026-08-12T05:11:04+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2768}} +{"ts": "2026-08-12T05:11:12+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "QQQM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "2775739c630f6811", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-12T05:12:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3521}} +{"ts": "2026-08-12T05:13:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 287}} +{"ts": "2026-08-12T05:14:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2587}} +{"ts": "2026-08-12T05:15:31+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3341}} +{"ts": "2026-08-12T05:16:17+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 107}} +{"ts": "2026-08-12T05:17:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2407}} +{"ts": "2026-08-12T05:18:05+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPYM", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "41e444bf93130aef", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-12T05:18:32+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 3159}} +{"ts": "2026-08-12T05:19:18+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3527}} +{"ts": "2026-08-12T05:20:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2226}} +{"ts": "2026-08-12T05:21:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2979}} +{"ts": "2026-08-12T05:22:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3346}} +{"ts": "2026-08-12T05:23:05+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 2046}} +{"ts": "2026-08-12T05:24:33+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2799}} +{"ts": "2026-08-12T05:25:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 3166}} +{"ts": "2026-08-12T05:26:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1866}} +{"ts": "2026-08-12T05:27:34+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2617}} +{"ts": "2026-08-12T05:28:19+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2985}} +{"ts": "2026-08-12T05:29:06+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1686}} +{"ts": "2026-08-12T05:30:35+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2437}} +{"ts": "2026-08-12T05:31:20+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2804}} +{"ts": "2026-08-12T05:32:07+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1505}} +{"ts": "2026-08-12T05:33:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2256}} +{"ts": "2026-08-12T05:34:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2623}} +{"ts": "2026-08-12T05:35:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1323}} +{"ts": "2026-08-12T05:36:36+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 2075}} +{"ts": "2026-08-12T05:37:22+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2443}} +{"ts": "2026-08-12T05:38:09+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 1142}} +{"ts": "2026-08-12T05:39:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1894}} +{"ts": "2026-08-12T05:40:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2261}} +{"ts": "2026-08-12T05:41:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 961}} +{"ts": "2026-08-12T05:42:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1713}} +{"ts": "2026-08-12T05:43:23+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 2081}} +{"ts": "2026-08-12T05:44:11+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 781}} +{"ts": "2026-08-12T05:45:38+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1533}} +{"ts": "2026-08-12T05:46:25+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1900}} +{"ts": "2026-08-12T05:47:12+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 599}} +{"ts": "2026-08-12T05:48:39+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1352}} +{"ts": "2026-08-12T05:49:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1719}} +{"ts": "2026-08-12T05:50:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 419}} +{"ts": "2026-08-12T05:51:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 1171}} +{"ts": "2026-08-12T05:52:26+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1538}} +{"ts": "2026-08-12T05:53:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 239}} +{"ts": "2026-08-12T05:54:41+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 990}} +{"ts": "2026-08-12T05:55:27+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1357}} +{"ts": "2026-08-12T05:56:13+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 58}} +{"ts": "2026-08-12T05:57:14+09:00", "kind": "overseas_order_reject", "strategy_id": "", "side": "SELL", "code": "SPCX", "msg_cd": "90000000", "msg1": "모의투자에서는 해당업무가 제공되지 않습니다.", "rt_cd": "1", "http": 200, "path": "/uapi/overseas-stock/v1/trading/order", "fingerprint": "dcc855767f588e72", "extra": {"tr_id": "VTTT1006U", "mock": true}} +{"ts": "2026-08-12T05:57:42+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "QQQM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "bb7a668daab6556e", "extra": {"remain_sec": 810}} +{"ts": "2026-08-12T05:58:29+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPYM", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "7ca0a5f6999ae4e8", "extra": {"remain_sec": 1176}} +{"ts": "2026-08-12T05:59:14+09:00", "kind": "overseas_sell_cooldown_skip", "strategy_id": "US_MOMENTUM", "side": "SELL", "code": "SPCX", "msg_cd": "COOLDOWN", "msg1": "order_api_skipped", "rt_cd": "", "http": "", "path": "order_manager", "fingerprint": "c1523c0aa8ef4b36", "extra": {"remain_sec": 3479}} diff --git a/logs/optuna_4strat_tpe_latest_master.logpath b/logs/optuna_4strat_tpe_latest_master.logpath index 9c9792e..42f9a8d 100644 --- a/logs/optuna_4strat_tpe_latest_master.logpath +++ b/logs/optuna_4strat_tpe_latest_master.logpath @@ -1 +1 @@ -logs/optuna_4strat_tpe_2026-07-20_2026-07-21_20260721_192215_master.log +logs/optuna_4strat_tpe_2026-08-07_2026-08-07_20260808_020634_master.log diff --git a/logs/optuna_breakout_fast_latest.jsonpath b/logs/optuna_breakout_fast_latest.jsonpath new file mode 100644 index 0000000..3066ddf --- /dev/null +++ b/logs/optuna_breakout_fast_latest.jsonpath @@ -0,0 +1 @@ +/home/hoon/kis_bot/kis_trader/backtest/results/optuna_breakout_fast_20260808_022923.json diff --git a/logs/optuna_breakout_latest.jsonpath b/logs/optuna_breakout_latest.jsonpath index b5728be..3066ddf 100644 --- a/logs/optuna_breakout_latest.jsonpath +++ b/logs/optuna_breakout_latest.jsonpath @@ -1 +1 @@ -/home/hoon/kis_bot/kis_trader/backtest/results/optuna_breakout_tpe_20260731_112436.json +/home/hoon/kis_bot/kis_trader/backtest/results/optuna_breakout_fast_20260808_022923.json diff --git a/logs/optuna_breakout_tpe_latest.logpath b/logs/optuna_breakout_tpe_latest.logpath index 0167453..c180b6f 100644 --- a/logs/optuna_breakout_tpe_latest.logpath +++ b/logs/optuna_breakout_tpe_latest.logpath @@ -1 +1 @@ -logs/optuna_breakout_tpe_20260723_213354.log +logs/optuna_breakout_tpe_20260808_022529.log diff --git a/logs/optuna_breakout_tpe_latest.study b/logs/optuna_breakout_tpe_latest.study index 750b9ac..f486280 100644 --- a/logs/optuna_breakout_tpe_latest.study +++ b/logs/optuna_breakout_tpe_latest.study @@ -1 +1 @@ -breakout_tpe_20260721_20260723_20260723_213354 +breakout_tpe_20260807_20260807_20260808_022529 diff --git a/logs/optuna_latest.jsonpath b/logs/optuna_latest.jsonpath index b5728be..4ccdb34 100644 --- a/logs/optuna_latest.jsonpath +++ b/logs/optuna_latest.jsonpath @@ -1 +1 @@ -/home/hoon/kis_bot/kis_trader/backtest/results/optuna_breakout_tpe_20260731_112436.json +/home/hoon/kis_bot/kis_trader/backtest/results/optuna_scalp_fast_20260812_001823.json diff --git a/logs/optuna_momentum_fast_latest.jsonpath b/logs/optuna_momentum_fast_latest.jsonpath index ad55df6..e391e61 100644 --- a/logs/optuna_momentum_fast_latest.jsonpath +++ b/logs/optuna_momentum_fast_latest.jsonpath @@ -1 +1 @@ -/home/hoon/kis_bot/kis_trader/backtest/results/optuna_momentum_fast_20260722_000857.json +/home/hoon/kis_bot/kis_trader/backtest/results/optuna_momentum_fast_20260808_021231.json diff --git a/logs/optuna_momentum_latest.jsonpath b/logs/optuna_momentum_latest.jsonpath index 5768590..e391e61 100644 --- a/logs/optuna_momentum_latest.jsonpath +++ b/logs/optuna_momentum_latest.jsonpath @@ -1 +1 @@ -/home/hoon/kis_bot/kis_trader/backtest/results/optuna_momentum_tpe_20260728_200954.json +/home/hoon/kis_bot/kis_trader/backtest/results/optuna_momentum_fast_20260808_021231.json diff --git a/logs/optuna_momentum_tpe_latest.logpath b/logs/optuna_momentum_tpe_latest.logpath index 2c6ff95..7d96d30 100644 --- a/logs/optuna_momentum_tpe_latest.logpath +++ b/logs/optuna_momentum_tpe_latest.logpath @@ -1 +1 @@ -logs/optuna_momentum_tpe_20260723_201537.log +logs/optuna_momentum_tpe_20260808_020634.log diff --git a/logs/optuna_momentum_tpe_latest.study b/logs/optuna_momentum_tpe_latest.study index 8a63aab..39632ea 100644 --- a/logs/optuna_momentum_tpe_latest.study +++ b/logs/optuna_momentum_tpe_latest.study @@ -1 +1 @@ -momentum_tpe_20260721_20260723_20260723_201537 +momentum_tpe_20260807_20260807_20260808_020634 diff --git a/logs/optuna_scalp_fast_latest.jsonpath b/logs/optuna_scalp_fast_latest.jsonpath new file mode 100644 index 0000000..4ccdb34 --- /dev/null +++ b/logs/optuna_scalp_fast_latest.jsonpath @@ -0,0 +1 @@ +/home/hoon/kis_bot/kis_trader/backtest/results/optuna_scalp_fast_20260812_001823.json diff --git a/logs/optuna_scalp_latest.jsonpath b/logs/optuna_scalp_latest.jsonpath index 30f82ec..4ccdb34 100644 --- a/logs/optuna_scalp_latest.jsonpath +++ b/logs/optuna_scalp_latest.jsonpath @@ -1 +1 @@ -/home/hoon/kis_bot/kis_trader/backtest/results/optuna_scalp_tpe_20260728_201954.json +/home/hoon/kis_bot/kis_trader/backtest/results/optuna_scalp_fast_20260812_001823.json diff --git a/logs/optuna_scalp_tpe_latest.logpath b/logs/optuna_scalp_tpe_latest.logpath index 22f3bde..b7d127b 100644 --- a/logs/optuna_scalp_tpe_latest.logpath +++ b/logs/optuna_scalp_tpe_latest.logpath @@ -1 +1 @@ -logs/optuna_scalp_tpe_20260723_214213.log +logs/optuna_scalp_tpe_20260808_023028.log diff --git a/logs/optuna_scalp_tpe_latest.study b/logs/optuna_scalp_tpe_latest.study index 2f5a1f5..6225998 100644 --- a/logs/optuna_scalp_tpe_latest.study +++ b/logs/optuna_scalp_tpe_latest.study @@ -1 +1 @@ -scalp_tpe_20260721_20260723_20260723_214213 +scalp_tpe_20260807_20260807_20260808_023028 diff --git a/logs/optuna_tail_fast_latest.jsonpath b/logs/optuna_tail_fast_latest.jsonpath new file mode 100644 index 0000000..bd82281 --- /dev/null +++ b/logs/optuna_tail_fast_latest.jsonpath @@ -0,0 +1 @@ +/home/hoon/kis_bot/kis_trader/backtest/results/optuna_tail_fast_20260808_022522.json diff --git a/logs/optuna_tail_latest.jsonpath b/logs/optuna_tail_latest.jsonpath index 59295fb..bd82281 100644 --- a/logs/optuna_tail_latest.jsonpath +++ b/logs/optuna_tail_latest.jsonpath @@ -1 +1 @@ -/home/hoon/kis_bot/kis_trader/backtest/results/optuna_tail_tpe_20260728_022918.json +/home/hoon/kis_bot/kis_trader/backtest/results/optuna_tail_fast_20260808_022522.json diff --git a/logs/optuna_tail_tpe_latest.logpath b/logs/optuna_tail_tpe_latest.logpath index 9913a6f..1d5f25e 100644 --- a/logs/optuna_tail_tpe_latest.logpath +++ b/logs/optuna_tail_tpe_latest.logpath @@ -1 +1 @@ -logs/optuna_tail_tpe_20260723_204401.log +logs/optuna_tail_tpe_20260808_021348.log diff --git a/logs/optuna_tail_tpe_latest.study b/logs/optuna_tail_tpe_latest.study index 524a6d4..d0a60f1 100644 --- a/logs/optuna_tail_tpe_latest.study +++ b/logs/optuna_tail_tpe_latest.study @@ -1 +1 @@ -tail_tpe_20260721_20260723_20260723_204401 +tail_tpe_20260807_20260807_20260808_021348 diff --git a/logs/optuna_web_latest_job.txt b/logs/optuna_web_latest_job.txt index 53c9747..29db4eb 100644 --- a/logs/optuna_web_latest_job.txt +++ b/logs/optuna_web_latest_job.txt @@ -1 +1 @@ -opt_20260731_112334_brea +opt_20260812_001757_scal diff --git a/mm_butler.py b/mm_butler.py index 44ae7d5..e0d3351 100644 --- a/mm_butler.py +++ b/mm_butler.py @@ -795,6 +795,131 @@ def handler_apply(args: str, db: TradeDB, hub: "CommandHub") -> str: return "\n".join(lines) +# ================================================================== +# 유튜브 음원 추출 핸들러 (yt-dlp 연동) +# ================================================================== +# 사용법: !유튜브 [유튜브URL] [시작시간] [종료시간] +# 예시: !유튜브 https://youtu.be/XXXX 00:01:00 00:02:30 +# 저장경로: /mnt/nc/share_drive/mp3/ + +_YOUTUBE_OUTPUT_DIR = "/mnt/nc/share_drive/mp3" +_YOUTUBE_USAGE = "사용법: `!유튜브 [유튜브URL] [시작시간] [종료시간]`\n예시: `!유튜브 https://youtu.be/XXXX 00:01:00 00:02:30`" + + +def handler_youtube(args: str, db: TradeDB, hub: "CommandHub") -> str: + """!유튜브 [URL] [시작] [종료] — 유튜브 구간 MP3 추출 후 공유드라이브에 저장.""" + import time as _time + + parts = (args or "").strip().split() + if len(parts) != 3: + return ( + f"❌ **입력 오류**: 인자 3개가 필요합니다.\n" + f"{_YOUTUBE_USAGE}" + ) + + url, start_time, end_time = parts + + # 기본 URL 형식 체크 (command injection 방지용 간단 검증) + if not (url.startswith("https://") or url.startswith("http://")): + return "❌ URL은 http:// 또는 https://로 시작해야 합니다." + + # 시간 형식 체크 (HH:MM:SS 또는 MM:SS 또는 초 단위 정수) + import re as _re + _time_pattern = _re.compile(r'^(\d{1,2}:)?\d{1,2}:\d{2}(\.\d+)?$') + for t_val, t_name in ((start_time, "시작시간"), (end_time, "종료시간")): + if not _time_pattern.match(t_val): + return f"❌ {t_name} 형식 오류: `{t_val}`\n`HH:MM:SS` 또는 `MM:SS` 형식으로 입력하세요." + + # 출력 파일명 (타임스탬프로 중복 방지) + ts = int(_time.time()) + safe_start = start_time.replace(":", "") + safe_end = end_time.replace(":", "") + output_filename = f"hymn_{safe_start}_{safe_end}_{ts}.mp3" + + # 1. 독립된 로컬 폴더에 먼저 다운로드 (동시 요청 꼬임 방지) + import os as _os + import shutil as _shutil + local_tmp_dir = f"/home/hoon/mp3/tmp_{ts}" + _os.makedirs(local_tmp_dir, exist_ok=True) + + # 영상 제목을 반영한 파일명 템플릿 + output_template = f"{local_tmp_dir}/%(title)s_[{safe_start}-{safe_end}].%(ext)s" + + command = [ + "yt-dlp", + "--no-progress", # 제어 문자 출력 방지 + "-x", # 오디오만 추출 + "--audio-format", "mp3", # MP3 포맷 지정 + "--audio-quality", "0", # 최고 음질 + "--no-playlist", # 플레이리스트 방지 + "--download-sections", f"*{start_time}-{end_time}", # 구간 지정 + "-o", output_template, # 임시 로컬 경로로 출력 + url, # 타겟 유튜브 URL + ] + + logger.info("[유튜브추출] 시작: %s / %s~%s → (임시) %s", url, start_time, end_time, local_tmp_dir) + + try: + result = subprocess.run( + command, check=True, capture_output=True, text=True, timeout=300 + ) + logger.info("[유튜브추출] yt-dlp 정상 종료") + except subprocess.TimeoutExpired: + logger.error("[유튜브추출] 타임아웃 (5분 초과)") + _shutil.rmtree(local_tmp_dir, ignore_errors=True) + return "❌ **추출 실패**: 5분 제한 초과." + except subprocess.CalledProcessError as e: + err_msg = (e.stderr or "") + (e.stdout or "") + err_msg = err_msg.replace('\r', '\n') + err_msg = ''.join(c for c in err_msg if c.isprintable() or c in '\n\t') + logger.error("[유튜브추출] yt-dlp 오류: %s", err_msg) + err_snippet = err_msg[-500:].strip() or "알 수 없는 오류" + _shutil.rmtree(local_tmp_dir, ignore_errors=True) + return f"❌ **추출 실패**: yt-dlp 오류.\n```text\n{err_snippet}\n```" + except Exception as e: + logger.error("[유튜브추출] 예외: %s", e) + _shutil.rmtree(local_tmp_dir, ignore_errors=True) + return f"❌ **추출 실패**: 예외 발생 — {e}" + + # 실제 다운로드된 파일명 찾기 (yt-dlp가 생성한 최종 파일) + downloaded_files = _os.listdir(local_tmp_dir) + if not downloaded_files: + _shutil.rmtree(local_tmp_dir, ignore_errors=True) + return "❌ **추출 실패**: 파일이 생성되지 않았습니다." + + actual_filename = downloaded_files[0] + actual_local_path = f"{local_tmp_dir}/{actual_filename}" + + # rclone으로 클라우드 드라이브에 파일 이동 + rclone_cmd = [ + "rclone", "move", actual_local_path, "nc:/share_drive/mp3/" + ] + logger.info("[유튜브추출] rclone move 시작: %s", rclone_cmd) + try: + rclone_res = subprocess.run(rclone_cmd, check=True, capture_output=True, text=True) + logger.info("[유튜브추출] rclone 완료: %s", rclone_res.stdout) + except subprocess.CalledProcessError as e: + err_msg = (e.stderr or "") + (e.stdout or "") + err_msg = err_msg.replace('\r', '\n') + err_msg = ''.join(c for c in err_msg if c.isprintable() or c in '\n\t') + logger.error("[유튜브추출] rclone 이동 실패: %s", err_msg) + err_snippet = err_msg[-500:].strip() or "알 수 없는 오류" + return f"❌ **이동 실패**: rclone 이동 중 오류 발생.\n```text\n{err_snippet}\n```" + except Exception as e: + logger.error("[유튜브추출] rclone 예외: %s", e) + return f"❌ **이동 실패**: rclone 예외 발생 — {e}" + + # 이동 완료 후 껍데기 폴더 삭제 + _shutil.rmtree(local_tmp_dir, ignore_errors=True) + + return ( + f"🎵 **유튜브 추출 및 업로드 완료!**\n" + f"- 구간: `{start_time}` ~ `{end_time}`\n" + f"- 파일: `{actual_filename}`\n" + f"- Nextcloud에서 `share_drive/mp3/` 폴더를 확인하세요 🎶" + ) + + # ================================================================== # 봇 제어 핸들러 (systemctl + journalctl) — 화이트리스트 방식 # ================================================================== @@ -1037,6 +1162,8 @@ class CommandHub: self.register_command("봇시작", handler_bot_start, "메인 봇 시작") self.register_command("봇상태", handler_bot_status, "메인 봇 상태 + status 출력 25줄") self.register_command("봇로그", handler_bot_log, "메인 봇 최근 로그 (예: !봇로그 200, 최대 500)") + # ── 유튜브 음원 추출 ─────────────────────────────────────────────── + self.register_command("유튜브", handler_youtube, "유튜브 구간 MP3 추출 → /mnt/nc/share_drive/mp3 저장 (예: !유튜브 [URL] [시작] [종료])") def register_command(self, cmd: str, handler: Callable, description: str = "") -> None: """외부에서 동적으로 명령어 추가 가능.""" @@ -1168,7 +1295,8 @@ class CommandHub: msg_text = (p.get("message") or "").strip() reply = self._dispatch(msg_text, p.get("id", "")) if reply: - self._post_reply(reply, root_id=p.get("id")) + r_id = p.get("root_id") or p.get("id") + self._post_reply(reply, root_id=r_id) logger.info("명령 응답 전송: %s -> %s…", msg_text[:30], reply[:60]) self.db.set_kv(self.KV_LAST_SEEN_TS, str(last_seen_ts)) diff --git a/scratch/analyze_daedong.py b/scratch/analyze_daedong.py new file mode 100644 index 0000000..7a669f5 --- /dev/null +++ b/scratch/analyze_daedong.py @@ -0,0 +1,75 @@ +import sys +sys.path.insert(0, "/home/hoon/kis_bot") +from database import TradeDB + +db = TradeDB() +code = "004490" # 대동기어 + +# 실제 컬럼: id, code, name, strategy, buy_price, sell_price, qty, +# profit_rate, realized_pnl, hold_minutes, buy_date, sell_date, +# sell_reason, env_snapshot, size_class, rsi, volume_ratio, +# tail_length_pct, ma5_gap_pct, ma20_gap_pct, foreign_net_buy, +# institution_net_buy, market_hour + +try: + print(f"\n{'='*60}") + print(f" 대동기어({code}) 매매 이력 분석") + print(f"{'='*60}") + + # 1. 오늘 매도 이력 + hist = db.conn.execute( + "SELECT strategy, buy_price, sell_price, qty, profit_rate, realized_pnl, " + "buy_date, sell_date, sell_reason, rsi, volume_ratio " + "FROM trade_history WHERE code=%s AND sell_date >= '2026-08-07 00:00:00' ORDER BY sell_date DESC", + (code,) + ).fetchall() + print(f"\n[오늘(8/7) 매도 이력 - {len(hist)}건]") + for r in hist: + d = dict(r) + print(f" [{d.get('strategy')}]") + print(f" 매수: {d.get('buy_date')} @ {d.get('buy_price'):,.0f}원") + print(f" 매도: {d.get('sell_date')} @ {d.get('sell_price'):,.0f}원") + print(f" 수량: {d.get('qty')}주 | 수익률: {d.get('profit_rate'):.2f}%") + print(f" 실현손익: {d.get('realized_pnl'):,.0f}원 | 사유: {d.get('sell_reason')}") + print(f" 진입당시 RSI: {d.get('rsi')} | 거래량비율: {d.get('volume_ratio')}") + print() + + # 2. 최근 5건 전체 + hist2 = db.conn.execute( + "SELECT strategy, buy_price, sell_price, qty, profit_rate, realized_pnl, " + "buy_date, sell_date, sell_reason, rsi, volume_ratio " + "FROM trade_history WHERE code=%s ORDER BY buy_date DESC LIMIT 5", + (code,) + ).fetchall() + print(f"[최근 전체 매매 이력 - {len(hist2)}건]") + for r in hist2: + d = dict(r) + print(f" [{d.get('strategy')}] 매수: {d.get('buy_date')} @ {d.get('buy_price'):,.0f}원 " + f"→ 매도: {d.get('sell_date')} @ {d.get('sell_price'):,.0f}원 " + f"| {d.get('profit_rate'):.2f}% | 사유: {d.get('sell_reason')}") + + # 3. 유니버스 포착 이력 + print(f"\n[오늘 유니버스 포착 이력]") + rows = db.conn.execute( + "SELECT event_time, strategy_id FROM target_candidates_history " + "WHERE code=%s AND event_time >= '2026-08-07 00:00:00' ORDER BY event_time ASC LIMIT 5", + (code,) + ).fetchall() + if rows: + for r in rows: + d = dict(r) + print(f" {d.get('event_time')} → 전략: {d.get('strategy_id')}") + else: + print(" 오늘 포착 없음. 어제 유입→오늘 이월 보유 케이스") + rows2 = db.conn.execute( + "SELECT event_time, strategy_id FROM target_candidates_history " + "WHERE code=%s ORDER BY event_time DESC LIMIT 3", + (code,) + ).fetchall() + for r in rows2: + d = dict(r) + print(f" [최근] {d.get('event_time')} → 전략: {d.get('strategy_id')}") + +finally: + db.close() + print(f"\n{'='*60}") diff --git a/scratch/apply_base_tpe.py b/scratch/apply_base_tpe.py new file mode 100644 index 0000000..91a3ba2 --- /dev/null +++ b/scratch/apply_base_tpe.py @@ -0,0 +1,15 @@ +import json +import glob +from kis_trader.backtest.param_search_optuna import _save_best_params_to_db + +for path in glob.glob("kis_trader/backtest/results/optuna_*_fast_20260808_02*.json"): + with open(path, "r", encoding="utf-8") as f: + data = json.load(f) + + strat = data.get("strategy") + # TPE의 best 파라미터는 json 내부에 저장되어 있음 + # 하지만 param_search_optuna.py 에는 _save_best_params_to_db(best_params, strat) 가 있음. + # JSON에서 파싱: + # "job" 안에 있거나 최상단에 있을 수 있음. + # JSON 파일 포맷 확인을 위해 그냥 로드해봄 + pass diff --git a/scratch/check_accidents.py b/scratch/check_accidents.py new file mode 100644 index 0000000..44d25fa --- /dev/null +++ b/scratch/check_accidents.py @@ -0,0 +1,16 @@ +import json + +with open('/home/hoon/.gemini/antigravity-ide/brain/e07d5342-58aa-4443-a5b3-73bde0f593dd/.system_generated/logs/transcript.jsonl', 'r') as f: + for line in f: + try: + data = json.loads(line) + except: + continue + if data.get('step_index') in [5795, 5804, 5805, 5806, 5807, 5808, 5809]: + if data.get('type') == 'PLANNER_RESPONSE': + for call in data.get('tool_calls', []): + print(f"Step {data.get('step_index')}: {call.get('name')}") + if 'args' in call: + for k, v in call['args'].items(): + if k == 'TargetFile': + print(f" Modified: {v}") diff --git a/scratch/check_bs4.py b/scratch/check_bs4.py new file mode 100644 index 0000000..4fafa7b --- /dev/null +++ b/scratch/check_bs4.py @@ -0,0 +1,23 @@ +from bs4 import BeautifulSoup +import sys + +with open("templates/backtest.html", "r", encoding="utf-8") as f: + html = f.read() + +soup = BeautifulSoup(html, "html.parser") +tab = soup.find(id="tab-tail") +if not tab: + print("tab-tail not found!") + sys.exit(1) + +print("tab-tail children count:", len(list(tab.children))) +card = tab.find(class_="card") +if card: + print("card found in tab-tail") + print("card text length:", len(card.text)) +else: + print("card NOT found in tab-tail!") + +# Print the IDs of all tabs to see if any are swallowed +tabs = soup.find_all(id=lambda x: x and x.startswith("tab-")) +print("Found tabs:", [t.get('id') for t in tabs]) diff --git a/scratch/check_db.py b/scratch/check_db.py new file mode 100644 index 0000000..6dbaa89 --- /dev/null +++ b/scratch/check_db.py @@ -0,0 +1,4 @@ +from kis_trader.database.db_manager import TradeDBExt +db = TradeDBExt() +cols = [r['Field'] for r in db.conn.execute('SHOW COLUMNS FROM env_config').fetchall()] +print([c for c in cols if 'SOURCE' in c or 'PROVIDER' in c]) diff --git a/scratch/check_db_counts.py b/scratch/check_db_counts.py new file mode 100644 index 0000000..834d346 --- /dev/null +++ b/scratch/check_db_counts.py @@ -0,0 +1,76 @@ +import sys +import os +import datetime + +# PYTHONPATH=. setting +sys.path.insert(0, "/home/hoon/kis_bot") + +from database import TradeDB + +def main(): + db = TradeDB() + today_str = "2026-08-07" + today_ts = datetime.datetime.strptime(today_str, "%Y-%m-%d").timestamp() + + tables_to_check = [ + "ws_price_validation", # 키움/KIS 체결가 검증 + "ls_ws_ticks", # LS 체결 틱 + "ws_orderbook", # 키움 실시간 호가 + "kis_ws_orderbook", # KIS 실시간 호가 + "ls_ws_orderbook" # LS 실시간 호가 + ] + + results = {} + + try: + for table in tables_to_check: + # 테이블 및 컬럼 존재 여부 확인 (SHOW COLUMNS) + try: + cols_raw = db.conn.execute(f"SHOW COLUMNS FROM {table}").fetchall() + cols = [dict(r)["Field"] for r in cols_raw] + except Exception as e: + results[table] = {"error": f"테이블/컬럼 조회 실패: {e}"} + continue + + time_col = None + if "created_at" in cols: + time_col = "created_at" + elif "recv_ts" in cols: + time_col = "recv_ts" + elif "timestamp" in cols: + time_col = "timestamp" + elif "ts" in cols: + time_col = "ts" + + if not time_col: + results[table] = {"error": f"시간 필드(created_at/recv_ts 등) 없음. (존재하는 컬럼: {cols})"} + continue + + # PyMySQL 안전한 파라미터 바인딩 (%s) 사용 + try: + if time_col == "created_at": + q = f"SELECT COUNT(*) as c FROM {table} WHERE {time_col} >= %s" + param = (f"{today_str} 00:00:00",) + else: + q = f"SELECT COUNT(*) as c FROM {table} WHERE {time_col} >= %s" + param = (today_ts,) + + res = db.conn.execute(q, param).fetchone() + count = dict(res)["c"] if res else 0 + results[table] = {"count": count, "time_col": time_col} + except Exception as e: + results[table] = {"error": f"조회 쿼리 실패: {e}"} + + print("=== RESULT_START ===") + for table, data in results.items(): + if "error" in data: + print(f"{table}|ERROR|{data['error']}") + else: + print(f"{table}|{data['count']}|{data['time_col']}") + print("=== RESULT_END ===") + + finally: + db.close() + +if __name__ == "__main__": + main() diff --git a/scratch/check_db_schema.py b/scratch/check_db_schema.py new file mode 100644 index 0000000..3276c31 --- /dev/null +++ b/scratch/check_db_schema.py @@ -0,0 +1,40 @@ +import sys +from pathlib import Path +sys.path.append(str(Path(__file__).resolve().parent.parent)) +from database import TradeDB + +db = TradeDB() + +print("--- ws_ticks Schema ---") +cols = db.conn.execute("SHOW COLUMNS FROM ws_ticks").fetchall() +for r in cols: + print(dict(r)) + +print("\n--- ws_ticks Sample ---") +sample = db.conn.execute("SELECT * FROM ws_ticks ORDER BY id DESC LIMIT 5").fetchall() +for r in sample: + print(dict(r)) + +print("\n--- ls_ws_ticks Sample ---") +sample = db.conn.execute("SELECT * FROM ls_ws_ticks ORDER BY id DESC LIMIT 5").fetchall() +for r in sample: + print(dict(r)) + +print("\n--- ws_candles Sample ---") +sample = db.conn.execute("SELECT * FROM ws_candles ORDER BY id DESC LIMIT 5").fetchall() +for r in sample: + print(dict(r)) + +print("\n--- ws_orderbook Schema & Sample ---") +cols = db.conn.execute("SHOW COLUMNS FROM ws_orderbook").fetchall() +print([dict(r)['Field'] for r in cols]) +sample = db.conn.execute("SELECT * FROM ws_orderbook ORDER BY id DESC LIMIT 5").fetchall() +for r in sample: + print(dict(r)) + +print("\n--- ls_ws_orderbook Sample ---") +sample = db.conn.execute("SELECT * FROM ls_ws_orderbook ORDER BY id DESC LIMIT 5").fetchall() +for r in sample: + print(dict(r)) + +db.close() diff --git a/scratch/check_db_schema2.py b/scratch/check_db_schema2.py new file mode 100644 index 0000000..52fe2cf --- /dev/null +++ b/scratch/check_db_schema2.py @@ -0,0 +1,12 @@ +import sys +import os +sys.path.insert(0, '/home/hoon/kis_bot') +from database import TradeDB + +db = TradeDB() +try: + cols = [dict(r)["Field"] for r in db.conn.execute(f"SHOW COLUMNS FROM active_trades").fetchall()] + print(f"active_trades cols: {cols}") +except Exception as e: + print(f"Error: {e}") +db.close() diff --git a/scratch/check_db_stats.py b/scratch/check_db_stats.py new file mode 100644 index 0000000..84467e3 --- /dev/null +++ b/scratch/check_db_stats.py @@ -0,0 +1,26 @@ +import sys +import os +sys.path.insert(0, '/home/hoon/kis_bot') +from database import TradeDB + +db = TradeDB() +today = "2026-08-11" + +queries = { + " trade_history (오늘)": f"SELECT COUNT(*) FROM trade_history WHERE buy_date >= '{today} 00:00:00'", + " kis_ws_orderbook (오늘)": f"SELECT COUNT(*) FROM kis_ws_orderbook WHERE recv_ts >= '{today} 00:00:00'", + " ls_ws_orderbook (오늘)": f"SELECT COUNT(*) FROM ls_ws_orderbook WHERE recv_ts >= '{today} 00:00:00'", + " ws_orderbook (키움, 오늘)": f"SELECT COUNT(*) FROM ws_orderbook WHERE recv_ts >= '{today} 00:00:00'", + " ls_ws_ticks (오늘)": f"SELECT COUNT(*) FROM ls_ws_ticks WHERE ts >= '{today} 00:00:00'", + " ws_ticks (키움/KIS, 오늘)": f"SELECT COUNT(*) FROM ws_ticks WHERE recv_ts >= '{today} 00:00:00'", + " ls_ws_candles (오늘)": f"SELECT COUNT(*) FROM ls_ws_candles WHERE datetime >= '{today} 00:00:00'", + " ws_candles (오늘)": f"SELECT COUNT(*) FROM ws_candles WHERE candle_time >= '{today} 00:00:00'", +} + +for name, q in queries.items(): + try: + res = db.conn.execute(q).fetchone() + print(f"{name}: {res[0]:,}") + except Exception as e: + print(f"{name}: Error - {e}") +db.close() diff --git a/scratch/check_db_stats2.py b/scratch/check_db_stats2.py new file mode 100644 index 0000000..6eab44e --- /dev/null +++ b/scratch/check_db_stats2.py @@ -0,0 +1,26 @@ +import sys +import os +sys.path.insert(0, '/home/hoon/kis_bot') +from database import TradeDB + +db = TradeDB() +today = "2026-08-11" + +queries = { + "trade_history": f"SELECT COUNT(*) FROM trade_history WHERE buy_date >= '{today} 00:00:00'", + "kis_ws_orderbook": f"SELECT COUNT(*) FROM kis_ws_orderbook WHERE recv_ts >= '{today} 00:00:00'", + "ls_ws_orderbook": f"SELECT COUNT(*) FROM ls_ws_orderbook WHERE recv_ts >= '{today} 00:00:00'", + "ws_orderbook": f"SELECT COUNT(*) FROM ws_orderbook WHERE recv_ts >= '{today} 00:00:00'", + "ls_ws_ticks": f"SELECT COUNT(*) FROM ls_ws_ticks WHERE ts >= '{today} 00:00:00'", + "ws_ticks": f"SELECT COUNT(*) FROM ws_ticks WHERE recv_ts >= '{today} 00:00:00'", + "ls_ws_candles": f"SELECT COUNT(*) FROM ls_ws_candles WHERE datetime >= '{today} 00:00:00'", + "ws_candles": f"SELECT COUNT(*) FROM ws_candles WHERE candle_time >= '{today} 00:00:00'", +} + +for name, q in queries.items(): + try: + res = db.conn.execute(q).fetchone() + print(f"COUNT {name}: {res[0]}") + except Exception as e: + print(f"ERROR {name}: {e}") +db.close() diff --git a/scratch/check_db_stats3.py b/scratch/check_db_stats3.py new file mode 100644 index 0000000..0007eb0 --- /dev/null +++ b/scratch/check_db_stats3.py @@ -0,0 +1,40 @@ +import sys +import os +import pymysql +import json + +sys.path.insert(0, '/home/hoon/kis_bot') +from kis_trader.utils.env import get_env_from_db + +host = "192.168.0.141" +user = "kis" +password = "kis" +db_name = "kis_quant_db" + +conn = pymysql.connect(host=host, user=user, password=password, database=db_name) +today = "2026-08-11" + +queries = { + "trade_history": f"SELECT COUNT(*) FROM trade_history WHERE buy_date >= '{today} 00:00:00'", + "kis_ws_orderbook": f"SELECT COUNT(*) FROM kis_ws_orderbook WHERE recv_ts >= '{today} 00:00:00'", + "ls_ws_orderbook": f"SELECT COUNT(*) FROM ls_ws_orderbook WHERE recv_ts >= '{today} 00:00:00'", + "ws_orderbook (키움)": f"SELECT COUNT(*) FROM ws_orderbook WHERE recv_ts >= '{today} 00:00:00'", + "ls_ws_ticks": f"SELECT COUNT(*) FROM ls_ws_ticks WHERE ts >= '{today} 00:00:00'", + "ws_ticks (키움/KIS)": f"SELECT COUNT(*) FROM ws_ticks WHERE recv_ts >= '{today} 00:00:00'", + "ls_ws_candles": f"SELECT COUNT(*) FROM ls_ws_candles WHERE datetime >= '{today} 00:00:00'", + "ws_candles (키움/KIS)": f"SELECT COUNT(*) FROM ws_candles WHERE candle_time >= '{today} 00:00:00'", +} + +results = {} +with conn.cursor() as cur: + for name, q in queries.items(): + try: + cur.execute(q) + res = cur.fetchone() + results[name] = res[0] + except Exception as e: + results[name] = f"Error: {e}" + +conn.close() +for k, v in results.items(): + print(f"{k}: {v:,}" if isinstance(v, int) else f"{k}: {v}") diff --git a/scratch/check_db_stats4.py b/scratch/check_db_stats4.py new file mode 100644 index 0000000..2088125 --- /dev/null +++ b/scratch/check_db_stats4.py @@ -0,0 +1,26 @@ +import sys +import os +sys.path.insert(0, '/home/hoon/kis_bot') +from database import TradeDB + +db = TradeDB() +today = "2026-08-11" + +queries = { + "trade_history": f"SELECT COUNT(*) as c FROM trade_history WHERE buy_date >= '{today} 00:00:00'", + "kis_ws_orderbook": f"SELECT COUNT(*) as c FROM kis_ws_orderbook WHERE recv_ts >= '{today} 00:00:00'", + "ls_ws_orderbook": f"SELECT COUNT(*) as c FROM ls_ws_orderbook WHERE recv_ts >= '{today} 00:00:00'", + "ws_orderbook": f"SELECT COUNT(*) as c FROM ws_orderbook WHERE recv_ts >= '{today} 00:00:00'", + "ls_ws_ticks": f"SELECT COUNT(*) as c FROM ls_ws_ticks WHERE ts >= '{today} 00:00:00'", + "ws_ticks": f"SELECT COUNT(*) as c FROM ws_ticks WHERE recv_ts >= '{today} 00:00:00'", + "ls_ws_candles": f"SELECT COUNT(*) as c FROM ls_ws_candles WHERE datetime >= '{today} 00:00:00'", + "ws_candles": f"SELECT COUNT(*) as c FROM ws_candles WHERE candle_time >= '{today} 00:00:00'", +} + +for name, q in queries.items(): + try: + res = db.conn.execute(q).fetchone() + print(f"COUNT {name}: {res['c']:,}") + except Exception as e: + print(f"ERROR {name}: {e}") +db.close() diff --git a/scratch/check_db_stats_table.py b/scratch/check_db_stats_table.py new file mode 100644 index 0000000..5a82551 --- /dev/null +++ b/scratch/check_db_stats_table.py @@ -0,0 +1,34 @@ +import sys +sys.path.insert(0, '/home/hoon/kis_bot') +from database import TradeDB + +db = TradeDB() +tables = [ + 'trade_history', + 'ws_ticks', 'ws_candles', 'ws_orderbook', 'kis_ws_orderbook', + 'ls_ws_ticks', 'ls_ws_candles', 'ls_ws_orderbook' +] + +print("| 증권사 | 데이터 유형 | 테이블명 | 현재 적재 건수 |") +print("|---|---|---|---|") + +for t in tables: + try: + count = db.conn.execute(f"SELECT COUNT(*) FROM {t}").fetchone()[0] + + broker = "공통" + if "ls_" in t: + broker = "LS증권" + elif "kis_" in t or t in ['ws_ticks', 'ws_candles', 'ws_orderbook']: + broker = "한국투자증권(KIS) / 키움" + + data_type = "매매내역" + if "tick" in t: data_type = "실시간 체결 틱" + elif "candle" in t: data_type = "실시간 캔들 (분봉)" + elif "orderbook" in t: data_type = "호가 스냅샷 (Orderbook)" + + print(f"| {broker} | {data_type} | `{t}` | {count:,} 건 |") + except Exception as e: + print(f"| - | - | `{t}` | 에러: {e} |") + +db.close() diff --git a/scratch/check_db_stats_table2.py b/scratch/check_db_stats_table2.py new file mode 100644 index 0000000..5d5803a --- /dev/null +++ b/scratch/check_db_stats_table2.py @@ -0,0 +1,35 @@ +import sys +sys.path.insert(0, '/home/hoon/kis_bot') +from database import TradeDB + +db = TradeDB() +tables = [ + 'trade_history', + 'ws_ticks', 'ws_candles', 'ws_orderbook', 'kis_ws_orderbook', + 'ls_ws_ticks', 'ls_ws_candles', 'ls_ws_orderbook' +] + +print("| 증권사 | 데이터 유형 | 테이블명 | 현재 적재 건수 |") +print("|---|---|---|---|") + +for t in tables: + try: + res = db.conn.execute(f"SELECT COUNT(*) as c FROM {t}").fetchone() + count = dict(res)['c'] + + broker = "공통" + if "ls_" in t: + broker = "LS증권" + elif "kis_" in t or t in ['ws_ticks', 'ws_candles', 'ws_orderbook']: + broker = "한국투자증권(KIS) / 키움" + + data_type = "매매내역" + if "tick" in t: data_type = "실시간 체결 틱" + elif "candle" in t: data_type = "실시간 캔들 (분봉)" + elif "orderbook" in t: data_type = "호가 스냅샷 (Orderbook)" + + print(f"| {broker} | {data_type} | `{t}` | {count:,} 건 |") + except Exception as e: + print(f"| - | - | `{t}` | 에러: {e} |") + +db.close() diff --git a/scratch/check_db_stats_today.py b/scratch/check_db_stats_today.py new file mode 100644 index 0000000..39a11c6 --- /dev/null +++ b/scratch/check_db_stats_today.py @@ -0,0 +1,54 @@ +import sys +import datetime +sys.path.insert(0, '/home/hoon/kis_bot') +from database import TradeDB + +db = TradeDB() + +# 오늘 날짜 +today_str = "2026-08-11" +today_dt = datetime.datetime.strptime(today_str, "%Y-%m-%d") + +# 테이블별 타임스탬프 컬럼 +table_time_col = { + 'trade_history': 'buy_date', + 'ws_ticks': 'tick_time', + 'ws_candles': 'start_time', + 'ws_orderbook': 'recv_ts', + 'kis_ws_orderbook': 'recv_ts', + 'ls_ws_ticks': 'recv_ts', + 'ls_ws_candles': 'recv_ts', + 'ls_ws_orderbook': 'recv_ts' +} + +print(f"| 증권사 | 데이터 유형 | 테이블명 | {today_str} 당일 적재 건수 |") +print("|---|---|---|---|") + +for t, col in table_time_col.items(): + try: + if t == 'trade_history': + sql = f"SELECT COUNT(*) as c FROM {t} WHERE DATE({col}) = %s" + params = (today_str,) + else: + sql = f"SELECT COUNT(*) as c FROM {t} WHERE {col} LIKE %s" + params = (f"{today_str}%",) + + res = db.conn.execute(sql, params).fetchone() + count = dict(res)['c'] + + broker = "공통" + if "ls_" in t: + broker = "LS증권" + elif "kis_" in t or t in ['ws_ticks', 'ws_candles', 'ws_orderbook']: + broker = "한국투자증권(KIS) / 키움" + + data_type = "매매내역" + if "tick" in t: data_type = "실시간 체결 틱" + elif "candle" in t: data_type = "실시간 캔들 (분봉)" + elif "orderbook" in t: data_type = "호가 스냅샷 (Orderbook)" + + print(f"| {broker} | {data_type} | `{t}` | {count:,} 건 |") + except Exception as e: + print(f"| - | - | `{t}` | 에러: {e} |") + +db.close() diff --git a/scratch/check_env_snapshot.py b/scratch/check_env_snapshot.py new file mode 100644 index 0000000..d0f7363 --- /dev/null +++ b/scratch/check_env_snapshot.py @@ -0,0 +1,20 @@ +import sys +import json +sys.path.insert(0, '/home/hoon/kis_bot') +from database import TradeDB + +db = TradeDB() +try: + res = db.conn.execute("SELECT code, name, strategy, env_snapshot FROM active_trades WHERE env_snapshot IS NOT NULL ORDER BY updated_at DESC LIMIT 5").fetchall() + if not res: + print("현재 active_trades에 env_snapshot이 저장된 거래가 없습니다.") + else: + print(f"env_snapshot 저장 내역 {len(res)}건 확인:") + for r in res: + r_dict = dict(r) + snap = r_dict['env_snapshot'] + snap_len = len(snap) if snap else 0 + print(f"- {r_dict['name']}({r_dict['code']}) [{r_dict['strategy']}] | 스냅샷 길이: {snap_len} bytes") +except Exception as e: + print(f"조회 실패: {e}") +db.close() diff --git a/scratch/check_history.py b/scratch/check_history.py new file mode 100644 index 0000000..6f3c547 --- /dev/null +++ b/scratch/check_history.py @@ -0,0 +1,15 @@ +import json + +with open('/home/hoon/.gemini/antigravity-ide/brain/e07d5342-58aa-4443-a5b3-73bde0f593dd/.system_generated/logs/transcript.jsonl', 'r') as f: + for line in f: + try: + data = json.loads(line) + except: + continue + if 'trigger_eval_collect' in line and data.get('type') == 'PLANNER_RESPONSE': + for call in data.get('tool_calls', []): + if call.get('name') in ('multi_replace_file_content', 'replace_file_content'): + args = call.get('args', {}) + if 'trigger_eval_collect.py' in str(args): + print(f"Step {data.get('step_index')}: modified trigger_eval_collect") + print(args.get('Instruction')) diff --git a/scratch/check_html.py b/scratch/check_html.py new file mode 100644 index 0000000..b82a4ca --- /dev/null +++ b/scratch/check_html.py @@ -0,0 +1,5 @@ +from urllib.request import urlopen +html = urlopen("http://127.0.0.1:5050/").read().decode('utf-8') +print("tab-tail exists:", "id=\"tab-tail\"" in html) +idx = html.find('id="tab-tail"') +print(html[idx:idx+200]) diff --git a/scratch/check_ids.py b/scratch/check_ids.py new file mode 100644 index 0000000..76c9db4 --- /dev/null +++ b/scratch/check_ids.py @@ -0,0 +1,7 @@ +from urllib.request import urlopen +import re +html = urlopen("http://127.0.0.1:5050/").read().decode('utf-8') +ids_in_html = set(re.findall(r'id="(tab-[^"]+)"', html)) +ids_in_js = ["tab-actual", "tab-dashboard", "tab-portfolio", "tab-backtest", "tab-tail", "tab-dbband", "tab-breakout", "tab-range_break", "tab-momentum", "tab-us_momentum", "tab-optuna", "tab-holding", "tab-updownbox", "tab-updow", "tab-dart", "tab-liveconfig"] +missing = [i for i in ids_in_js if i not in ids_in_html] +print("Missing IDs in HTML:", missing) diff --git a/scratch/check_live_settings.py b/scratch/check_live_settings.py new file mode 100644 index 0000000..798a091 --- /dev/null +++ b/scratch/check_live_settings.py @@ -0,0 +1,38 @@ +"""현재 운영 설정 핵심 키 조회""" +import sys +sys.path.insert(0, "/home/hoon/kis_bot") +from database import TradeDB + +db = TradeDB() +try: + snap = db.get_merged_env_snapshot() or {} + + keys = [ + "KIWOOM_WS_ORDERBOOK_ENABLED", + "WS_ORDERBOOK_COLLECT_ENABLED", + "WS_ORDERBOOK_SAVE_ENABLED", + "WS_ORDERBOOK_TICK_MAX_AGE_SEC", + "WS_TRIGGER_EVAL_SAVE_ENABLED", + "WHIPSAW_FILTER_ENABLED", + "TAIL_WHIPSAW_FILTER_ENABLED", + "MOMENTUM_WHIPSAW_FILTER_ENABLED", + "BREAKOUT_WHIPSAW_FILTER_ENABLED", + "TAIL_ORDERBOOK_FILTER_ENABLED", + "MOMENTUM_ORDERBOOK_FILTER_ENABLED", + "BREAKOUT_ORDERBOOK_FILTER_ENABLED", + "SCALP_ORDERBOOK_FILTER_ENABLED", + "STRICT_FILL_VERIFY", + "USE_MARKET_IOC", + "USE_MARKET_IOC_SELL", + "WS_PROVIDER", + "WS_SUBSCRIBE_KIS_MINIMAL", + "KIS_WS_ORDERBOOK_ENABLED", + ] + + print("=== 현재 DB 설정값 ===") + for k in keys: + v = snap.get(k) + print(f" {k} = {repr(v)}") + +finally: + db.close() diff --git a/scratch/check_ls.py b/scratch/check_ls.py new file mode 100644 index 0000000..31aa34b --- /dev/null +++ b/scratch/check_ls.py @@ -0,0 +1,3 @@ +from database import get_env_bool +print(get_env_bool("LS_WS_TICK_SAVE", True)) +print(get_env_bool("LS_WS_ORDERBOOK_SAVE", True)) diff --git a/scratch/check_ob_filter_params.py b/scratch/check_ob_filter_params.py new file mode 100644 index 0000000..cc28f36 --- /dev/null +++ b/scratch/check_ob_filter_params.py @@ -0,0 +1,75 @@ +"""Optuna MariaDB에서 최근 파람서치 호가 파라미터 조회""" +import sys +sys.path.insert(0, "/home/hoon/kis_bot") +import pymysql +from kis_trader.utils.env_loader import get_env_from_db + +# MariaDB 접속 정보 환경변수에서 +db_host = get_env_from_db("DB_HOST", "192.168.0.141") +db_port = int(get_env_from_db("DB_PORT", "3306")) +db_user = get_env_from_db("DB_USER", "root") +db_pass = get_env_from_db("DB_PASS", "") +optuna_db = get_env_from_db("OPTUNA_DB_NAME", "kis_optuna_db") + +print(f"접속: {db_host}:{db_port}/{optuna_db}") + +conn = pymysql.connect(host=db_host, port=db_port, user=db_user, password=db_pass, + database=optuna_db, cursorclass=pymysql.cursors.DictCursor) + +try: + cur = conn.cursor() + + # 최근 study 목록 + cur.execute(""" + SELECT study_id, study_name + FROM studies + ORDER BY study_id DESC + LIMIT 20 + """) + studies = cur.fetchall() + + print("\n최근 study 목록:") + for s in studies: + print(f" [{s['study_id']}] {s['study_name']}") + + ob_param_keywords = ["orderbook", "spread", "bid_ask", "ask_max", "bid_depth", "bid_level", "ask_wall"] + + print("\n=== 각 strategy 최근 best trial 호가 파라미터 ===\n") + for s in studies[:15]: + study_id = s["study_id"] + study_name = s["study_name"] + + # best trial + cur.execute(""" + SELECT trial_id, value + FROM trials + WHERE study_id = %s AND state = 'COMPLETE' AND value IS NOT NULL + ORDER BY value DESC + LIMIT 1 + """, (study_id,)) + best = cur.fetchone() + if not best: + continue + + trial_id = best["trial_id"] + value = best["value"] + + # 호가 파라미터 + cur.execute(""" + SELECT param_name, param_value + FROM trial_params + WHERE trial_id = %s + """, (trial_id,)) + all_params = cur.fetchall() + + ob_params = [p for p in all_params + if any(kw in p["param_name"].lower() for kw in ob_param_keywords)] + + if ob_params: + print(f"[{study_name}] best value={value:.4f} (trial#{trial_id})") + for p in sorted(ob_params, key=lambda x: x["param_name"]): + print(f" {p['param_name']:50s} = {p['param_value']:.4f}") + print() + +finally: + conn.close() diff --git a/scratch/check_ob_tables.py b/scratch/check_ob_tables.py new file mode 100644 index 0000000..0dcb1be --- /dev/null +++ b/scratch/check_ob_tables.py @@ -0,0 +1,12 @@ +from database import TradeDB +db = TradeDB() +try: + for t in ['kis_ws_orderbook', 'ls_ws_orderbook']: + try: + sql = f"SELECT COUNT(*) as cnt FROM {t} WHERE DATE(recv_ts) = '2026-08-10'" + cnt = db.conn.execute(sql).fetchone()['cnt'] + print(f"Table {t}: {cnt} rows today") + except Exception as e: + print(f"Could not count {t}: {e}") +finally: + db.close() diff --git a/scratch/check_orderbook_counts_correct.py b/scratch/check_orderbook_counts_correct.py new file mode 100644 index 0000000..338edb6 --- /dev/null +++ b/scratch/check_orderbook_counts_correct.py @@ -0,0 +1,33 @@ +import sys +import os +import datetime + +sys.path.insert(0, "/home/hoon/kis_bot") +from database import TradeDB + +def main(): + db = TradeDB() + today_str = "2026-08-07" + + tables = { + "ws_orderbook": "recv_ts", + "kis_ws_orderbook": "recv_ts", + "ls_ws_orderbook": "recv_ts" + } + + try: + for table, time_col in tables.items(): + try: + # since recv_ts is stored as string '2026-08-07 ...' + q = f"SELECT COUNT(*) as c FROM {table} WHERE {time_col} LIKE %s OR {time_col} >= %s" + param = (f"{today_str}%", f"{today_str} 00:00:00") + res = db.conn.execute(q, param).fetchone() + count = dict(res)["c"] if res else 0 + print(f"{table}: {count}") + except Exception as e: + print(f"Error for {table}: {e}") + finally: + db.close() + +if __name__ == "__main__": + main() diff --git a/scratch/check_orderbook_schema.py b/scratch/check_orderbook_schema.py new file mode 100644 index 0000000..5be249f --- /dev/null +++ b/scratch/check_orderbook_schema.py @@ -0,0 +1,38 @@ +import sys +import os +import datetime + +sys.path.insert(0, "/home/hoon/kis_bot") +from database import TradeDB + +def main(): + db = TradeDB() + try: + tables = ["ws_orderbook", "kis_ws_orderbook", "ls_ws_orderbook"] + + for table in tables: + print(f"\n--- {table} ---") + try: + # 1. Show columns + cols = db.conn.execute(f"SHOW COLUMNS FROM {table}").fetchall() + print("Columns:", [dict(c)["Field"] for c in cols]) + + # 2. Get 1 sample row to see data types + sample = db.conn.execute(f"SELECT * FROM {table} ORDER BY id DESC LIMIT 1").fetchone() + if sample: + print("Sample row:") + for k, v in dict(sample).items(): + print(f" {k}: {v} (type: {type(v)})") + else: + print("Table is completely empty.") + + # 3. Total count in table + total = db.conn.execute(f"SELECT COUNT(*) as c FROM {table}").fetchone() + print("Total rows:", dict(total)["c"]) + except Exception as e: + print(f"Error checking {table}: {e}") + finally: + db.close() + +if __name__ == "__main__": + main() diff --git a/scratch/check_prev_user.py b/scratch/check_prev_user.py new file mode 100644 index 0000000..2481492 --- /dev/null +++ b/scratch/check_prev_user.py @@ -0,0 +1,15 @@ +import json + +with open('/home/hoon/.gemini/antigravity-ide/brain/e07d5342-58aa-4443-a5b3-73bde0f593dd/.system_generated/logs/transcript.jsonl', 'r') as f: + messages = [] + for line in f: + try: + data = json.loads(line) + except: + continue + if data.get('type') == 'USER_INPUT' and data.get('step_index') < 5795: + messages.append((data.get('step_index'), data.get('content'))) + + if len(messages) >= 3: + for idx in range(-3, 0): + print(messages[idx]) diff --git a/scratch/check_thought.py b/scratch/check_thought.py new file mode 100644 index 0000000..850288c --- /dev/null +++ b/scratch/check_thought.py @@ -0,0 +1,10 @@ +import json + +with open('/home/hoon/.gemini/antigravity-ide/brain/e07d5342-58aa-4443-a5b3-73bde0f593dd/.system_generated/logs/transcript.jsonl', 'r') as f: + for line in f: + try: + data = json.loads(line) + except: + continue + if data.get('step_index') == 5795: + print("Thinking:", data.get('thinking', '')[:1000]) diff --git a/scratch/check_tick_normalize.py b/scratch/check_tick_normalize.py new file mode 100644 index 0000000..1dedb1b --- /dev/null +++ b/scratch/check_tick_normalize.py @@ -0,0 +1,35 @@ +import sys +sys.path.insert(0, '.') +from database import TradeDB + +db = TradeDB() +try: + # ws_ticks 컬럼 확인 완료 - tick_time 기반으로 오늘 조회 + # tick_time 형식 확인 + sample = db.conn.execute( + "SELECT tick_time, source FROM ws_ticks WHERE market='KR' ORDER BY id DESC LIMIT 5" + ).fetchall() + print("샘플:", [dict(r) for r in sample]) + + # 오늘 20260808 시간대별 건수 - tick_time으로 필터 + rows = db.conn.execute( + """ + SELECT + SUBSTR(tick_time, 1, 11) as hhmm10, + source, + COUNT(*) as cnt + FROM ws_ticks + WHERE market='KR' AND tick_time LIKE %s + GROUP BY SUBSTR(tick_time, 1, 11), source + ORDER BY hhmm10 ASC + LIMIT 100 + """, + ('20260808%',) + ).fetchall() + + print(f"\n[20260808] 시간대별 틱 적재 현황 (10분 단위):") + for r in rows: + print(f" {dict(r)['hhmm10']} | {dict(r).get('source','?'):6s} | {dict(r)['cnt']:,}건") + +finally: + db.close() diff --git a/scratch/check_tick_normalize2.py b/scratch/check_tick_normalize2.py new file mode 100644 index 0000000..0df0620 --- /dev/null +++ b/scratch/check_tick_normalize2.py @@ -0,0 +1,39 @@ +import sys +sys.path.insert(0, '.') +from database import TradeDB + +db = TradeDB() +try: + # 07일 날짜로 시간대별 확인 - tick_time 형식: 20260807HHMMSS + rows = db.conn.execute( + """ + SELECT + SUBSTR(tick_time, 9, 2) as hh, + source, + COUNT(*) as cnt + FROM ws_ticks + WHERE market='KR' AND tick_time >= '20260807090000' AND tick_time <= '20260807160000' + GROUP BY SUBSTR(tick_time, 9, 2), source + ORDER BY hh ASC, source ASC + """, + ).fetchall() + + print("[20260807 장중 시간대별 틱 건수]") + for r in rows: + print(f" {dict(r)['hh']}시 | {dict(r).get('source','?'):6s} | {dict(r)['cnt']:,}건") + + # 어제 어디서 데이터가 얼마나 있는지 전체 + total_rows = db.conn.execute( + """ + SELECT source, COUNT(*) as cnt + FROM ws_ticks + WHERE market='KR' AND tick_time >= '20260807090000' AND tick_time <= '20260807160000' + GROUP BY source + """, + ).fetchall() + print("\n[전체 소스별]") + for r in total_rows: + print(f" {dict(r).get('source','?'):6s}: {dict(r)['cnt']:,}건") + +finally: + db.close() diff --git a/scratch/check_user_req.py b/scratch/check_user_req.py new file mode 100644 index 0000000..43e352a --- /dev/null +++ b/scratch/check_user_req.py @@ -0,0 +1,14 @@ +import json + +with open('/home/hoon/.gemini/antigravity-ide/brain/e07d5342-58aa-4443-a5b3-73bde0f593dd/.system_generated/logs/transcript.jsonl', 'r') as f: + messages = [] + for line in f: + try: + data = json.loads(line) + except: + continue + if data.get('type') == 'USER_INPUT' and data.get('step_index') < 5795: + messages.append((data.get('step_index'), data.get('content'))) + + if messages: + print(messages[-1]) diff --git a/scratch/check_ws_ob.py b/scratch/check_ws_ob.py new file mode 100644 index 0000000..233d2d0 --- /dev/null +++ b/scratch/check_ws_ob.py @@ -0,0 +1,10 @@ +from database import TradeDB +db = TradeDB() +try: + sql = "SELECT source, COUNT(*) as cnt FROM ws_orderbook WHERE DATE(recv_ts) = '2026-08-10' GROUP BY source" + rows = db.conn.execute(sql).fetchall() + print("ws_orderbook today:", [dict(r) for r in rows]) +except Exception as e: + print(f"Could not count ws_orderbook: {e}") +finally: + db.close() diff --git a/scratch/check_ws_ob2.py b/scratch/check_ws_ob2.py new file mode 100644 index 0000000..1502ffe --- /dev/null +++ b/scratch/check_ws_ob2.py @@ -0,0 +1,10 @@ +from database import TradeDB +db = TradeDB() +try: + sql = "SELECT source, COUNT(*) as cnt FROM ws_orderbook WHERE DATE(recv_ts) = '2026-08-10' GROUP BY source" + rows = db.conn.execute(sql).fetchall() + print("OB COUNT:", [dict(r) for r in rows]) +except Exception as e: + print(f"ERROR: {e}") +finally: + db.close() diff --git a/scratch/check_ws_ob3.py b/scratch/check_ws_ob3.py new file mode 100644 index 0000000..7299da2 --- /dev/null +++ b/scratch/check_ws_ob3.py @@ -0,0 +1,9 @@ +from database import TradeDB +db = TradeDB() +try: + cnt = db.conn.execute("SELECT COUNT(*) as cnt FROM ws_orderbook").fetchone()['cnt'] + print(f"Total ws_orderbook rows: {cnt}") +except Exception as e: + print(f"ERROR: {e}") +finally: + db.close() diff --git a/scratch/check_ws_ob4.py b/scratch/check_ws_ob4.py new file mode 100644 index 0000000..166813a --- /dev/null +++ b/scratch/check_ws_ob4.py @@ -0,0 +1,16 @@ +from database import TradeDB +db = TradeDB() +try: + sql = """ + SELECT DATE(recv_ts) as dt, source, COUNT(*) as cnt + FROM ws_orderbook + GROUP BY dt, source + ORDER BY dt DESC + LIMIT 10 + """ + rows = db.conn.execute(sql).fetchall() + print("OB DATES:", [dict(r) for r in rows]) +except Exception as e: + print(f"ERROR: {e}") +finally: + db.close() diff --git a/scratch/check_ws_price_validation.py b/scratch/check_ws_price_validation.py new file mode 100644 index 0000000..fc3a20f --- /dev/null +++ b/scratch/check_ws_price_validation.py @@ -0,0 +1,37 @@ +from database import TradeDB +db = TradeDB() +try: + cols = [dict(r)["Field"] for r in db.conn.execute("SHOW COLUMNS FROM ws_price_validation").fetchall()] + print(f"ws_price_validation cols: {cols}") + + sql = """ + SELECT + code, provider, COUNT(*) as cnt + FROM ws_price_validation + WHERE DATE(recv_time) = '2026-08-10' + GROUP BY code, provider + ORDER BY code, provider; + """ + rows = db.conn.execute(sql).fetchall() + + # 종합 + sql_total = """ + SELECT + provider, COUNT(*) as cnt + FROM ws_price_validation + WHERE DATE(recv_time) = '2026-08-10' + GROUP BY provider; + """ + totals = db.conn.execute(sql_total).fetchall() + + import json + print("\n--- By Stock ---") + for r in rows: + print(json.dumps(dict(r))) + + print("\n--- Totals ---") + for r in totals: + print(json.dumps(dict(r))) + +finally: + db.close() diff --git a/scratch/check_ws_price_validation_2.py b/scratch/check_ws_price_validation_2.py new file mode 100644 index 0000000..1f75fe2 --- /dev/null +++ b/scratch/check_ws_price_validation_2.py @@ -0,0 +1,22 @@ +from database import TradeDB +db = TradeDB() +try: + sql = """ + SELECT + COUNT(*) as total_rows, + AVG(kis_age_ms) as avg_kis_age, + AVG(kiwoom_age_ms) as avg_kiwoom_age, + SUM(CASE WHEN kis_age_ms < kiwoom_age_ms THEN 1 ELSE 0 END) as kis_faster_cnt, + SUM(CASE WHEN kiwoom_age_ms < kis_age_ms THEN 1 ELSE 0 END) as kiwoom_faster_cnt, + AVG(diff_pct) as avg_diff_pct + FROM ws_price_validation + WHERE DATE(ts) = '2026-08-10' + """ + row = db.conn.execute(sql).fetchone() + + import json + print("\n--- ws_price_validation Today ---") + print(json.dumps(dict(row), indent=2)) + +finally: + db.close() diff --git a/scratch/check_ws_price_validation_3.py b/scratch/check_ws_price_validation_3.py new file mode 100644 index 0000000..e839ea9 --- /dev/null +++ b/scratch/check_ws_price_validation_3.py @@ -0,0 +1,23 @@ +from database import TradeDB +db = TradeDB() +try: + sql = """ + SELECT + COUNT(*) as total_rows, + AVG(kis_age_ms) as avg_kis_age, + AVG(kiwoom_age_ms) as avg_kiwoom_age, + SUM(CASE WHEN kis_age_ms < kiwoom_age_ms THEN 1 ELSE 0 END) as kis_faster_cnt, + SUM(CASE WHEN kiwoom_age_ms < kis_age_ms THEN 1 ELSE 0 END) as kiwoom_faster_cnt, + SUM(CASE WHEN kis_age_ms = kiwoom_age_ms THEN 1 ELSE 0 END) as tie_cnt, + AVG(diff_pct) as avg_diff_pct + FROM ws_price_validation + WHERE DATE(ts) = '2026-08-10' + """ + row = db.conn.execute(sql).fetchone() + + print("\n--- ws_price_validation Today ---") + for k, v in dict(row).items(): + print(f"{k}: {v}") + +finally: + db.close() diff --git a/scratch/check_ws_price_validation_4.py b/scratch/check_ws_price_validation_4.py new file mode 100644 index 0000000..e48826e --- /dev/null +++ b/scratch/check_ws_price_validation_4.py @@ -0,0 +1,9 @@ +from database import TradeDB +db = TradeDB() +try: + sql = "SELECT * FROM ws_price_validation WHERE DATE(ts) = '2026-08-10' LIMIT 5" + rows = db.conn.execute(sql).fetchall() + for row in rows: + print(dict(row)) +finally: + db.close() diff --git a/scratch/check_ws_provider.py b/scratch/check_ws_provider.py new file mode 100644 index 0000000..4022d56 --- /dev/null +++ b/scratch/check_ws_provider.py @@ -0,0 +1,2 @@ +from kis_trader.utils.env import get_env_from_db +print(f"WS_PROVIDER = {get_env_from_db('WS_PROVIDER')}") diff --git a/scratch/check_ws_ticks_schema.py b/scratch/check_ws_ticks_schema.py new file mode 100644 index 0000000..3b2ade8 --- /dev/null +++ b/scratch/check_ws_ticks_schema.py @@ -0,0 +1,7 @@ +from database import TradeDB +db = TradeDB() +try: + cols = [dict(r)["Field"] for r in db.conn.execute("SHOW COLUMNS FROM ws_ticks").fetchall()] + print(f"ws_ticks cols: {cols}") +finally: + db.close() diff --git a/scratch/check_ws_ticks_source.py b/scratch/check_ws_ticks_source.py new file mode 100644 index 0000000..96c9d61 --- /dev/null +++ b/scratch/check_ws_ticks_source.py @@ -0,0 +1,19 @@ +from database import TradeDB +db = TradeDB() +try: + sql = """ + SELECT + source, COUNT(*) as cnt + FROM ws_ticks + WHERE DATE(recv_ts) = '2026-08-10' + GROUP BY source; + """ + rows = db.conn.execute(sql).fetchall() + + import json + print("\n--- ws_ticks source count Today ---") + for r in rows: + print(json.dumps(dict(r))) + +finally: + db.close() diff --git a/scratch/db_check.py b/scratch/db_check.py new file mode 100644 index 0000000..99fb597 --- /dev/null +++ b/scratch/db_check.py @@ -0,0 +1,26 @@ +import sys +import os +sys.path.append('/home/hoon/kis_bot') + +from database import TradeDB + +def check_db(): + db = TradeDB() + try: + # Check tables + tables = [dict(r) for r in db.conn.execute("SHOW TABLES").fetchall()] + print("Tables:", tables) + + # Let's check environment variables and settings related to slots and buying + cols_env = [dict(r)["Field"] for r in db.conn.execute("SHOW COLUMNS FROM env_settings").fetchall()] + print("env_settings cols:", cols_env) + + env_vars = db.conn.execute("SELECT * FROM env_settings").fetchall() + for env in env_vars: + print(dict(env)) + + finally: + db.close() + +if __name__ == "__main__": + check_db() diff --git a/scratch/db_check2.py b/scratch/db_check2.py new file mode 100644 index 0000000..d3b8d82 --- /dev/null +++ b/scratch/db_check2.py @@ -0,0 +1,45 @@ +import sys +from datetime import datetime + +sys.path.append('/home/hoon/kis_bot') +from database import TradeDB + +def check_db(): + db = TradeDB() + try: + today = datetime.now().strftime("%Y-%m-%d") + print(f"--- Trades for {today} ---") + + # Check trade history + query_trades = """ + SELECT strategy, code, name, buy_price, buy_qty, sell_price, sell_qty, status, buy_time + FROM trade_history + WHERE DATE(buy_time) = %s + """ + trades = db.conn.execute(query_trades, (today,)).fetchall() + for t in trades: + print(dict(t)) + + print(f"--- Active Trades for {today} ---") + # Check active trades + query_active = """ + SELECT strategy, code, name, buy_price, qty, buy_time + FROM active_trades + WHERE DATE(buy_time) = %s + """ + active_trades = db.conn.execute(query_active, (today,)).fetchall() + for t in active_trades: + print(dict(t)) + + # Check config tables for momentum, scalp, tail(range_break or whatever tail is) + print("--- Env Configs ---") + query_env = "SELECT env_key, env_val FROM env_config WHERE env_key LIKE '%MOMENTUM%' OR env_key LIKE '%SCALP%' OR env_key LIKE '%TAIL%'" + envs = db.conn.execute(query_env).fetchall() + for e in envs: + print(dict(e)) + + finally: + db.close() + +if __name__ == "__main__": + check_db() diff --git a/scratch/db_check3.py b/scratch/db_check3.py new file mode 100644 index 0000000..e23dcf7 --- /dev/null +++ b/scratch/db_check3.py @@ -0,0 +1,34 @@ +import sys +sys.path.append('/home/hoon/kis_bot') +from database import TradeDB + +def check_db(): + db = TradeDB() + try: + th_cols = [dict(r)["Field"] for r in db.conn.execute("SHOW COLUMNS FROM trade_history").fetchall()] + print("trade_history cols:", th_cols) + + at_cols = [dict(r)["Field"] for r in db.conn.execute("SHOW COLUMNS FROM active_trades").fetchall()] + print("active_trades cols:", at_cols) + + env_cols = [dict(r)["Field"] for r in db.conn.execute("SHOW COLUMNS FROM env_config").fetchall()] + print("env_config cols:", env_cols) + + print("--- Env Configs ---") + query_env = "SELECT env_key, env_val FROM env_config" + envs = db.conn.execute(query_env).fetchall() + for e in envs: + if "MOMENTUM" in e["env_key"] or "SCALP" in e["env_key"] or "TAIL" in e["env_key"] or "SLOT" in e["env_key"]: + print(dict(e)) + + # Also let's just SELECT * FROM active_trades LIMIT 5 + print("--- Active Trades ---") + ats = db.conn.execute("SELECT * FROM active_trades LIMIT 5").fetchall() + for a in ats: + print(dict(a)) + + finally: + db.close() + +if __name__ == "__main__": + check_db() diff --git a/scratch/db_check4.py b/scratch/db_check4.py new file mode 100644 index 0000000..18d1e76 --- /dev/null +++ b/scratch/db_check4.py @@ -0,0 +1,38 @@ +import sys +from datetime import datetime +sys.path.append('/home/hoon/kis_bot') +from database import TradeDB + +def check_db(): + db = TradeDB() + try: + th_cols = [dict(r)["Field"] for r in db.conn.execute("SHOW COLUMNS FROM trade_history").fetchall()] + print("trade_history cols:", th_cols) + + at_cols = [dict(r)["Field"] for r in db.conn.execute("SHOW COLUMNS FROM active_trades").fetchall()] + print("active_trades cols:", at_cols) + + env_cols = [dict(r)["Field"] for r in db.conn.execute("SHOW COLUMNS FROM env_config").fetchall()] + print("env_config cols:", env_cols) + + query = "SELECT * FROM env_config" + envs = db.conn.execute(query).fetchall() + for e in envs: + print(dict(e)) + + today = datetime.now().strftime("%Y-%m-%d") + print(f"--- Trades for {today} ---") + trades = db.conn.execute("SELECT * FROM trade_history WHERE DATE(buy_time) = %s", (today,)).fetchall() + for t in trades: + print(dict(t)) + + print(f"--- Active Trades for {today} ---") + active_trades = db.conn.execute("SELECT * FROM active_trades WHERE DATE(buy_time) = %s", (today,)).fetchall() + for t in active_trades: + print(dict(t)) + + finally: + db.close() + +if __name__ == "__main__": + check_db() diff --git a/scratch/db_config_mom.py b/scratch/db_config_mom.py new file mode 100644 index 0000000..c437255 --- /dev/null +++ b/scratch/db_config_mom.py @@ -0,0 +1,31 @@ +import sys +sys.path.append('/home/hoon/kis_bot') +from database import TradeDB + +def check_db(): + db = TradeDB() + try: + row = db.conn.execute("SELECT * FROM config_momentum ORDER BY id DESC LIMIT 1").fetchone() + if row: + d = dict(row) + print("config_momentum:") + for k, v in d.items(): + if v is not None and v != "": + print(f" {k}: {v}") + else: + print("config_momentum is empty") + + row_scalp = db.conn.execute("SELECT * FROM config_scalp ORDER BY id DESC LIMIT 1").fetchone() + if row_scalp: + d = dict(row_scalp) + print("config_scalp STRATEGY_SCALP_ENABLED:", d.get("STRATEGY_SCALP_ENABLED")) + + row_tail = db.conn.execute("SELECT * FROM config_range_break ORDER BY id DESC LIMIT 1").fetchone() + if row_tail: + d = dict(row_tail) + print("config_range_break STRATEGY_RANGE_BREAK_ENABLED:", d.get("STRATEGY_RANGE_BREAK_ENABLED")) + finally: + db.close() + +if __name__ == "__main__": + check_db() diff --git a/scratch/db_config_mom3.py b/scratch/db_config_mom3.py new file mode 100644 index 0000000..8552e84 --- /dev/null +++ b/scratch/db_config_mom3.py @@ -0,0 +1,14 @@ +import sys +sys.path.append('/home/hoon/kis_bot') +from database import TradeDB + +def check_db(): + db = TradeDB() + try: + row = db.conn.execute("SELECT STRATEGY_MOMENTUM_ENABLED, MOMENTUM_DAILY_PROFIT_HALT_NEW_BUYS FROM config_momentum ORDER BY id DESC LIMIT 1").fetchone() + print("config_momentum:", dict(row) if row else "None") + finally: + db.close() + +if __name__ == "__main__": + check_db() diff --git a/scratch/db_config_scalp.py b/scratch/db_config_scalp.py new file mode 100644 index 0000000..150a87b --- /dev/null +++ b/scratch/db_config_scalp.py @@ -0,0 +1,21 @@ +import sys +sys.path.append('/home/hoon/kis_bot') +from database import TradeDB + +def check_db(): + db = TradeDB() + try: + row = db.conn.execute("SELECT * FROM config_scalp ORDER BY id DESC LIMIT 1").fetchone() + if row: + d = dict(row) + print("config_scalp:") + for k, v in d.items(): + if v is not None and v != "": + print(f" {k}: {v}") + else: + print("config_scalp is empty") + finally: + db.close() + +if __name__ == "__main__": + check_db() diff --git a/scratch/db_config_short.py b/scratch/db_config_short.py new file mode 100644 index 0000000..788aa4b --- /dev/null +++ b/scratch/db_config_short.py @@ -0,0 +1,25 @@ +import sys +sys.path.append('/home/hoon/kis_bot') +from database import TradeDB + +def check_db(): + db = TradeDB() + try: + row = db.conn.execute("SELECT * FROM config_short ORDER BY id DESC LIMIT 1").fetchone() + if row: + d = dict(row) + print("config_short STRATEGY_SHORT_ENABLED:", d.get("STRATEGY_SHORT_ENABLED")) + print("config_short STRATEGY_TAIL_ENABLED:", d.get("STRATEGY_TAIL_ENABLED")) + + # Print all non-empty keys + print("config_short dump:") + for k, v in d.items(): + if v is not None and v != "": + print(f" {k}: {v}") + else: + print("config_short is empty") + finally: + db.close() + +if __name__ == "__main__": + check_db() diff --git a/scratch/db_env_mom.py b/scratch/db_env_mom.py new file mode 100644 index 0000000..0480d4d --- /dev/null +++ b/scratch/db_env_mom.py @@ -0,0 +1,21 @@ +import sys +sys.path.append('/home/hoon/kis_bot') +from database import TradeDB + +def check_db(): + db = TradeDB() + try: + keys = ["MOMENTUM_SLOT_MONEY", "MOMENTUM_MAX_LOSS_PER_TRADE_KRW", "MOMENTUM_STOP_LOSS_PCT", "MOMENTUM_MIN_DROP_PCT_FOR_LOSS_CUT", "MOMENTUM_TOTAL_BUDGET_KRW"] + for k in keys: + row = db.conn.execute("SELECT env_val FROM env_config WHERE env_key = %s", (k,)).fetchone() + if not row: + row = db.conn.execute("SELECT env_value FROM env_config_ext WHERE env_key = %s", (k,)).fetchone() + if row: + print(f"{k}: {dict(row)}") + else: + print(f"{k}: NOT FOUND") + finally: + db.close() + +if __name__ == "__main__": + check_db() diff --git a/scratch/db_env_mom2.py b/scratch/db_env_mom2.py new file mode 100644 index 0000000..34b9358 --- /dev/null +++ b/scratch/db_env_mom2.py @@ -0,0 +1,27 @@ +import sys +sys.path.append('/home/hoon/kis_bot') +from database import TradeDB + +def check_db(): + db = TradeDB() + try: + keys = ["MOMENTUM_SLOT_MONEY", "MOMENTUM_MAX_LOSS_PER_TRADE_KRW", "MOMENTUM_STOP_LOSS_PCT", "MOMENTUM_MIN_DROP_PCT_FOR_LOSS_CUT", "MOMENTUM_TOTAL_BUDGET_KRW"] + for k in keys: + row = db.conn.execute("SELECT env_value FROM env_config_ext WHERE env_key = %s", (k,)).fetchone() + if row: + print(f"{k}: {dict(row)}") + else: + print(f"{k}: NOT FOUND") + + # Let's also check active trades and trades today + print("--- Trades Today ---") + from datetime import datetime + today = datetime.now().strftime("%Y-%m-%d") + trades = db.conn.execute("SELECT strategy, code, name, qty, buy_price, buy_date, sell_price, sell_date FROM trade_history WHERE buy_date LIKE %s OR sell_date LIKE %s", (f"{today}%", f"{today}%")).fetchall() + for t in trades: + print(dict(t)) + finally: + db.close() + +if __name__ == "__main__": + check_db() diff --git a/scratch/db_ob_filters.py b/scratch/db_ob_filters.py new file mode 100644 index 0000000..3d75f24 --- /dev/null +++ b/scratch/db_ob_filters.py @@ -0,0 +1,24 @@ +import sys +sys.path.append('/home/hoon/kis_bot') +from database import TradeDB + +def check_db(): + db = TradeDB() + try: + # momentum + r_mom = db.conn.execute("SELECT MOMENTUM_ORDERBOOK_FILTER_ENABLED FROM config_momentum ORDER BY id DESC LIMIT 1").fetchone() + print("MOMENTUM_ORDERBOOK_FILTER_ENABLED:", dict(r_mom) if r_mom else "None") + + # short / tail + r_short = db.conn.execute("SELECT TAIL_ORDERBOOK_FILTER_ENABLED FROM config_short ORDER BY id DESC LIMIT 1").fetchone() + print("TAIL_ORDERBOOK_FILTER_ENABLED:", dict(r_short) if r_short else "None") + + # scalp + r_scalp = db.conn.execute("SELECT SCALP_ORDERBOOK_FILTER_ENABLED FROM config_scalp ORDER BY id DESC LIMIT 1").fetchone() + print("SCALP_ORDERBOOK_FILTER_ENABLED:", dict(r_scalp) if r_scalp else "None") + + finally: + db.close() + +if __name__ == "__main__": + check_db() diff --git a/scratch/db_stat_compare.py b/scratch/db_stat_compare.py new file mode 100644 index 0000000..9b63185 --- /dev/null +++ b/scratch/db_stat_compare.py @@ -0,0 +1,120 @@ +""" +ws_price_validation: KIS 틱 vs 키움 틱 빈도(age) 비교 +kis_age_ms = 마지막 KIS 시세가 얼마나 오래됐나 (낮을수록 더 자주 옴) +kiwoom_age_ms = 마지막 키움 시세가 얼마나 오래됐나 +""" +import sys +sys.path.insert(0, "/home/hoon/kis_bot") +from database import TradeDB + +db = TradeDB() +ts_from = "2026-08-07 09:00:00" + +try: + print(f"\n{'='*65}") + print(f" KIS 시세 vs 키움 시세 틱 빈도 비교 (ws_price_validation)") + print(f"{'='*65}") + print(f" ※ age_ms = 마지막 시세 수신 후 경과 시간(ms)") + print(f" 낮을수록 더 자주(빠르게) 틱이 오는 것\n") + + # 전체 평균 비교 + r = db.conn.execute( + "SELECT " + " COUNT(*) as total, " + " SUM(kis_age_ms IS NOT NULL) as kis_cnt, " + " SUM(kiwoom_age_ms IS NOT NULL) as kw_cnt, " + " AVG(kis_age_ms) as avg_kis, " + " AVG(kiwoom_age_ms) as avg_kw, " + " MIN(kis_age_ms) as min_kis, " + " MIN(kiwoom_age_ms) as min_kw, " + " MAX(kis_age_ms) as max_kis, " + " MAX(kiwoom_age_ms) as max_kw " + "FROM ws_price_validation " + "WHERE ts >= %s AND (kis_age_ms IS NOT NULL OR kiwoom_age_ms IS NOT NULL)", + (ts_from,) + ).fetchone() + d = dict(r) + + total = d["total"] or 0 + kis_cnt = int(d["kis_cnt"] or 0) + kw_cnt = int(d["kw_cnt"] or 0) + avg_kis = d["avg_kis"] or 0 + avg_kw = d["avg_kw"] or 0 + + print(f"[ 전체 집계 (9시 이후) ]") + print(f" 총 비교 이벤트 : {total:,}건") + print(f" KIS 시세 있는 건 : {kis_cnt:,}건 ({kis_cnt/total*100:.1f}%)") + print(f" 키움 시세 있는 건: {kw_cnt:,}건 ({kw_cnt/total*100:.1f}%)") + + print(f"\n[ 시세 나이(age) 비교 — 낮을수록 빠른 틱 ]") + print(f" {'구분':<12} | {'평균 age(ms)':>12} | {'최소(ms)':>9} | {'최대(ms)':>9}") + print(f" {'-'*52}") + print(f" {'KIS':<12} | {avg_kis:>12.0f} | {d['min_kis'] or 0:>9.0f} | {d['max_kis'] or 0:>9.0f}") + print(f" {'키움':<12} | {avg_kw:>12.0f} | {d['min_kw'] or 0:>9.0f} | {d['max_kw'] or 0:>9.0f}") + + if avg_kis > 0 and avg_kw > 0: + ratio = avg_kw / avg_kis + faster = "KIS" if avg_kis < avg_kw else "키움" + print(f"\n 비율 (키움age / KIS age): {ratio:.2f}배") + print(f" → {faster}가 더 자주 틱이 옵니다!") + if ratio >= 4: + print(f" ✅ KIS가 키움보다 약 {ratio:.1f}배 빠름 → KIS로 시세 구독 전환 권장!") + elif ratio >= 2: + print(f" ⚠️ KIS가 약 {ratio:.1f}배 빠름 → 전환 고려할 수준") + elif ratio >= 0.8: + print(f" ➡️ KIS/키움 거의 동등 ({ratio:.1f}배) → 굳이 전환 불필요") + else: + print(f" ❌ 키움이 더 빠름 → KIS 전환 불필요") + + # 두 값이 모두 있는 케이스만 정밀 비교 + print(f"\n[ 양측 모두 있는 케이스만 비교 ]") + r2 = db.conn.execute( + "SELECT COUNT(*) as cnt, " + "AVG(kis_age_ms) avg_kis, AVG(kiwoom_age_ms) avg_kw, " + "AVG(kiwoom_age_ms - kis_age_ms) as avg_diff " + "FROM ws_price_validation " + "WHERE ts >= %s AND kis_age_ms IS NOT NULL AND kiwoom_age_ms IS NOT NULL", + (ts_from,) + ).fetchone() + d2 = dict(r2) + cnt2 = d2.get("cnt") or 0 + avg_diff = d2.get("avg_diff") or 0 + print(f" 양측 동시 존재 : {cnt2:,}건") + print(f" KIS 평균 age : {d2.get('avg_kis') or 0:.0f}ms") + print(f" 키움 평균 age : {d2.get('avg_kw') or 0:.0f}ms") + print(f" 차이 (키움-KIS) : {avg_diff:+.0f}ms ({'KIS가 더 빠름' if avg_diff > 0 else '키움이 더 빠름'})") + + # 종목별 KIS vs 키움 상위 5종목 + print(f"\n[ 종목별 평균 age 비교 상위 10 ]") + rows = db.conn.execute( + "SELECT code, " + "AVG(kis_age_ms) avg_kis, AVG(kiwoom_age_ms) avg_kw, COUNT(*) cnt " + "FROM ws_price_validation " + "WHERE ts >= %s AND kis_age_ms IS NOT NULL AND kiwoom_age_ms IS NOT NULL " + "GROUP BY code HAVING cnt >= 10 " + "ORDER BY cnt DESC LIMIT 10", + (ts_from,) + ).fetchall() + print(f" {'종목':<10} | {'KIS(ms)':>7} | {'키움(ms)':>7} | {'차이(ms)':>8} | {'건수':>5}") + print(f" {'-'*50}") + for row in rows: + d3 = dict(row) + diff = (d3.get("avg_kw") or 0) - (d3.get("avg_kis") or 0) + print(f" {d3['code']:<10} | {d3.get('avg_kis') or 0:>7.0f} | {d3.get('avg_kw') or 0:>7.0f} | {diff:>+8.0f} | {d3['cnt']:>5}") + + # 결론 + print(f"\n{'='*65}") + print(f" 결론 및 시세 구독 전환 권고") + print(f"{'='*65}") + if avg_kis > 0 and avg_kw > 0: + ratio = avg_kw / avg_kis + if ratio >= 2: + print(f" ✅ KIS 시세가 키움보다 평균 {ratio:.1f}배 빠름") + print(f" → KIS WebSocket으로 시세 구독 전환을 권장합니다!") + print(f" → 특히 스캘핑·돌파 전략의 타점 정밀도가 올라갑니다.") + else: + print(f" ➡️ KIS({avg_kis:.0f}ms) vs 키움({avg_kw:.0f}ms): 차이 {ratio:.1f}배 수준") + print(f" → 굳이 시세 구독을 전환할 만큼 압도적 차이는 아닙니다.") + +finally: + db.close() diff --git a/scratch/db_th_env.py b/scratch/db_th_env.py new file mode 100644 index 0000000..8fac7f0 --- /dev/null +++ b/scratch/db_th_env.py @@ -0,0 +1,29 @@ +import sys +import json +sys.path.append('/home/hoon/kis_bot') +from database import TradeDB + +def check_db(): + db = TradeDB() + try: + # Check last 5 momentum trades + trades = db.conn.execute( + "SELECT code, name, env_snapshot FROM trade_history WHERE strategy='MOMENTUM' ORDER BY id DESC LIMIT 5" + ).fetchall() + for t in trades: + print(f"Code: {t['code']}, Name: {t['name']}") + if t['env_snapshot']: + try: + snap = json.loads(t['env_snapshot']) + ob = snap.get("orderbook", {}) + print(" Orderbook snapshot:", ob) + print(" Entry features:", snap.get("entry_features", {})) + except Exception as e: + print(" Error parsing JSON:", e) + else: + print(" env_snapshot is NULL") + finally: + db.close() + +if __name__ == "__main__": + check_db() diff --git a/scratch/delete_old_data.py b/scratch/delete_old_data.py new file mode 100644 index 0000000..1617921 --- /dev/null +++ b/scratch/delete_old_data.py @@ -0,0 +1,44 @@ +import sys +from kis_trader.database.db_manager import TradeDB + +db = TradeDB() +try: + rows = db.conn.execute("SHOW TABLES").fetchall() + tables = [list(r.values())[0] for r in rows] + + target_prefixes = ( + 'ws_ticks', 'ws_orderbook', 'ws_candles', + 'ls_ws_ticks', 'ls_ws_orderbook', 'ls_ws_candles', + 'ws_price_validation' + ) + + for table in tables: + if not table.startswith(target_prefixes): + continue + + cols = [dict(r)["Field"] for r in db.conn.execute(f"SHOW COLUMNS FROM {table}").fetchall()] + time_col = None + for candidate in ('tick_time', 'ob_time', 'candle_time', 'timestamp', 'recv_ts'): + if candidate in cols: + time_col = candidate + break + + if time_col: + # We want to delete data before 2026-08-08. + cutoff = '20260808' + + # Print count before deletion + count_q = f"SELECT COUNT(*) as cnt FROM {table} WHERE {time_col} < %s" + count = db.conn.execute(count_q, (cutoff,)).fetchone()['cnt'] + + if count > 0: + print(f"Deleting {count} rows from {table} (using column {time_col})") + del_q = f"DELETE FROM {table} WHERE {time_col} < %s" + db.conn.execute(del_q, (cutoff,)) + db.conn.commit() + else: + print(f"No rows to delete in {table} before {cutoff}") + else: + print(f"Warning: No time column found for {table}, columns: {cols}") +finally: + db.close() diff --git a/scratch/delete_old_data_batch.py b/scratch/delete_old_data_batch.py new file mode 100644 index 0000000..0190b8e --- /dev/null +++ b/scratch/delete_old_data_batch.py @@ -0,0 +1,61 @@ +import sys +import time +from kis_trader.database.db_manager import TradeDB + +db = TradeDB() +try: + rows = db.conn.execute("SHOW TABLES").fetchall() + tables = [list(r.values())[0] for r in rows] + + target_prefixes = ( + 'ws_ticks', 'ws_orderbook', 'ws_candles', + 'ls_ws_ticks', 'ls_ws_orderbook', 'ls_ws_candles', + 'ws_price_validation' + ) + + for table in tables: + if not table.startswith(target_prefixes): + continue + + cols = [dict(r)["Field"] for r in db.conn.execute(f"SHOW COLUMNS FROM {table}").fetchall()] + time_col = None + for candidate in ('tick_time', 'ob_time', 'candle_time', 'timestamp', 'recv_ts', 'datetime'): + if candidate in cols: + time_col = candidate + break + + if time_col: + cutoff = '20260808' + + # Print count before deletion + count_q = f"SELECT COUNT(*) as cnt FROM {table} WHERE {time_col} < %s" + count = db.conn.execute(count_q, (cutoff,)).fetchone()['cnt'] + + if count > 0: + print(f"Deleting {count} rows from {table} (using column {time_col})") + deleted = 0 + while True: + del_q = f"DELETE FROM {table} WHERE {time_col} < %s LIMIT 100000" + db.conn.execute(del_q, (cutoff,)) + db.conn.commit() + + cur_deleted = db.conn.cursor().rowcount + # PyMySQL execute might not return rowcount directly, + # we can use a separate cursor or just check if affected rows is 0 + # Wait, db.conn is SQLAlchemy Engine or raw pymysql connection? + # It's an SQLAlchemy Engine in db_manager.py! + # Actually, SQLAlchemy execute() returns ResultProxy with rowcount + res = db.conn.execute(del_q, (cutoff,)) + db.conn.commit() + if res.rowcount <= 0: + break + deleted += res.rowcount + print(f" ... deleted {deleted}/{count} rows in {table}") + time.sleep(0.1) + print(f"Finished deleting from {table}.") + else: + print(f"No rows to delete in {table} before {cutoff}") + else: + print(f"Warning: No time column found for {table}, columns: {cols}") +finally: + db.close() diff --git a/scratch/delete_old_data_final.py b/scratch/delete_old_data_final.py new file mode 100644 index 0000000..1f9716f --- /dev/null +++ b/scratch/delete_old_data_final.py @@ -0,0 +1,53 @@ +import sys +import time +from database import TradeDB + +db = TradeDB() +try: + # TradeDB in database.py inherits or delegates to _MariaDBConn, which has .execute(sql) + tables = [list(r.values())[0] for r in db.execute("SHOW TABLES").fetchall()] + + target_prefixes = ( + 'ws_ticks', 'ws_orderbook', 'ws_candles', + 'ls_ws_ticks', 'ls_ws_orderbook', 'ls_ws_candles', + 'ws_price_validation' + ) + + for table in tables: + if not table.startswith(target_prefixes): + continue + + cols = [dict(r)["Field"] for r in db.execute(f"SHOW COLUMNS FROM {table}").fetchall()] + time_col = None + for candidate in ('tick_time', 'ob_time', 'candle_time', 'timestamp', 'recv_ts', 'datetime'): + if candidate in cols: + time_col = candidate + break + + if time_col: + cutoff = '20260808' + + # Print count before deletion + count_q = f"SELECT COUNT(*) as cnt FROM {table} WHERE {time_col} < %s" + count = db.execute(count_q, (cutoff,)).fetchone()['cnt'] + + if count > 0: + print(f"Deleting {count} rows from {table} (using column {time_col})") + deleted = 0 + while True: + del_q = f"DELETE FROM {table} WHERE {time_col} < %s LIMIT 100000" + cur = db.execute(del_q, (cutoff,)) + + rows_affected = cur.rowcount + if rows_affected <= 0: + break + deleted += rows_affected + print(f" ... deleted {deleted}/{count} rows in {table}") + time.sleep(0.1) + print(f"Finished deleting from {table}.") + else: + print(f"No rows to delete in {table} before {cutoff}") + else: + print(f"Warning: No time column found for {table}, columns: {cols}") +finally: + db.close() diff --git a/scratch/delete_old_data_pymysql.py b/scratch/delete_old_data_pymysql.py new file mode 100644 index 0000000..1f2fb55 --- /dev/null +++ b/scratch/delete_old_data_pymysql.py @@ -0,0 +1,68 @@ +import sys +import time +import pymysql + +conn = pymysql.connect( + host='192.168.0.141', + port=3306, + user='jae', + password='1234', + database='kis_quant_db', + cursorclass=pymysql.cursors.DictCursor, + autocommit=True +) + +try: + with conn.cursor() as cur: + cur.execute("SHOW TABLES") + tables = [list(r.values())[0] for r in cur.fetchall()] + + target_prefixes = ( + 'ws_ticks', 'ws_orderbook', 'ws_candles', + 'ls_ws_ticks', 'ls_ws_orderbook', 'ls_ws_candles', + 'ws_price_validation' + ) + + for table in tables: + if not table.startswith(target_prefixes): + continue + + with conn.cursor() as cur: + cur.execute(f"SHOW COLUMNS FROM {table}") + cols = [r["Field"] for r in cur.fetchall()] + + time_col = None + for candidate in ('tick_time', 'ob_time', 'candle_time', 'timestamp', 'recv_ts', 'datetime'): + if candidate in cols: + time_col = candidate + break + + if time_col: + cutoff = '20260808' + + with conn.cursor() as cur: + count_q = f"SELECT COUNT(*) as cnt FROM {table} WHERE {time_col} < %s" + cur.execute(count_q, (cutoff,)) + count = cur.fetchone()['cnt'] + + if count > 0: + print(f"Deleting {count} rows from {table} (using column {time_col})") + deleted = 0 + while True: + with conn.cursor() as cur: + del_q = f"DELETE FROM {table} WHERE {time_col} < %s LIMIT 100000" + cur.execute(del_q, (cutoff,)) + rows_affected = cur.rowcount + + if rows_affected <= 0: + break + deleted += rows_affected + print(f" ... deleted {deleted}/{count} rows in {table}") + time.sleep(0.01) + print(f"Finished deleting from {table}.") + else: + print(f"No rows to delete in {table} before {cutoff}") + else: + print(f"Warning: No time column found for {table}, columns: {cols}") +finally: + conn.close() diff --git a/scratch/delete_old_data_sa.py b/scratch/delete_old_data_sa.py new file mode 100644 index 0000000..9ee6f2f --- /dev/null +++ b/scratch/delete_old_data_sa.py @@ -0,0 +1,54 @@ +import sys +import time +from sqlalchemy import text +from kis_trader.database.db_manager import TradeDB + +db = TradeDB() +try: + tables = [list(r._mapping.values())[0] for r in db.conn.execute(text("SHOW TABLES")).fetchall()] + + target_prefixes = ( + 'ws_ticks', 'ws_orderbook', 'ws_candles', + 'ls_ws_ticks', 'ls_ws_orderbook', 'ls_ws_candles', + 'ws_price_validation' + ) + + for table in tables: + if not table.startswith(target_prefixes): + continue + + cols = [dict(r._mapping)["Field"] for r in db.conn.execute(text(f"SHOW COLUMNS FROM {table}")).fetchall()] + time_col = None + for candidate in ('tick_time', 'ob_time', 'candle_time', 'timestamp', 'recv_ts', 'datetime'): + if candidate in cols: + time_col = candidate + break + + if time_col: + cutoff = '20260808' + + # Print count before deletion + count_q = text(f"SELECT COUNT(*) as cnt FROM {table} WHERE {time_col} < :cutoff") + count = db.conn.execute(count_q, {"cutoff": cutoff}).fetchone()._mapping['cnt'] + + if count > 0: + print(f"Deleting {count} rows from {table} (using column {time_col})") + deleted = 0 + while True: + del_q = text(f"DELETE FROM {table} WHERE {time_col} < :cutoff LIMIT 100000") + res = db.conn.execute(del_q, {"cutoff": cutoff}) + db.conn.commit() + + rows_affected = res.rowcount + if rows_affected <= 0: + break + deleted += rows_affected + print(f" ... deleted {deleted}/{count} rows in {table}") + time.sleep(0.1) + print(f"Finished deleting from {table}.") + else: + print(f"No rows to delete in {table} before {cutoff}") + else: + print(f"Warning: No time column found for {table}, columns: {cols}") +finally: + db.close() diff --git a/scratch/disable_entry_filters.py b/scratch/disable_entry_filters.py new file mode 100644 index 0000000..fa81fc9 --- /dev/null +++ b/scratch/disable_entry_filters.py @@ -0,0 +1,25 @@ +from database import TradeDB + +db = TradeDB() +try: + snap = db.get_merged_env_snapshot() + updated = False + for prefix in ["MOMENTUM", "BREAKOUT", "SCALP", "SHORT"]: + # 2단계: 호가 수급 필터 (진입) + ob_key = f"{prefix}_ORDERBOOK_FILTER_ENABLED" + # 3단계: 휩쏘 필터 (진입) + ws_key = f"{prefix}_WHIPSAW_FILTER_ENABLED" + + for key in [ob_key, ws_key]: + if snap.get(key) != "false": + snap[key] = "false" + updated = True + print(f"Disabled {key}") + + if updated: + db.insert_env_snapshot(snap) + print("Inserted new env snapshot with all ENTRY filters OFF.") + else: + print("All ENTRY filters are already OFF.") +finally: + db.close() diff --git a/scratch/disable_whipsaw.py b/scratch/disable_whipsaw.py new file mode 100644 index 0000000..6f8d4d5 --- /dev/null +++ b/scratch/disable_whipsaw.py @@ -0,0 +1,20 @@ +from database import TradeDB + +db = TradeDB() +try: + snap = db.get_merged_env_snapshot() + updated = False + for prefix in ["MOMENTUM", "BREAKOUT", "SCALP", "SHORT"]: + key = f"{prefix}_WHIPSAW_FILTER_ENABLED" + if snap.get(key) != "false": + snap[key] = "false" + updated = True + print(f"Disabled {key}") + + if updated: + db.insert_env_snapshot(snap) + print("Inserted new env snapshot.") + else: + print("Already disabled.") +finally: + db.close() diff --git a/scratch/enable_orderbook_filter.py b/scratch/enable_orderbook_filter.py new file mode 100644 index 0000000..35b119f --- /dev/null +++ b/scratch/enable_orderbook_filter.py @@ -0,0 +1,20 @@ +from database import TradeDB + +db = TradeDB() +try: + snap = db.get_merged_env_snapshot() + updated = False + for prefix in ["MOMENTUM", "BREAKOUT", "SCALP", "SHORT"]: + ob_key = f"{prefix}_ORDERBOOK_FILTER_ENABLED" + if snap.get(ob_key) != "true": + snap[ob_key] = "true" + updated = True + print(f"Enabled {ob_key}") + + if updated: + db.insert_env_snapshot(snap) + print("Inserted new env snapshot with ORDERBOOK filters ON.") + else: + print("All ORDERBOOK filters are already ON.") +finally: + db.close() diff --git a/scratch/extract_optuna.py b/scratch/extract_optuna.py new file mode 100644 index 0000000..38f5697 --- /dev/null +++ b/scratch/extract_optuna.py @@ -0,0 +1,70 @@ +import re + +html_path = 'templates/backtest.html' +with open(html_path, 'r', encoding='utf-8') as f: + html = f.read() + +def extract_and_remove(html_str, input_id): + pattern = r'(\s*
\s*\s*]*id="' + input_id + r'"[^>]*>\s*
)' + match = re.search(pattern, html_str, re.DOTALL) + if match: + block = match.group(1) + html_str = html_str.replace(block, '') + return html_str, block + + # Try col-auto fallback + pattern = r'(\s*
\s*\s*]*id="' + input_id + r'"[^>]*>\s*
)' + match = re.search(pattern, html_str, re.DOTALL) + if match: + block = match.group(1) + html_str = html_str.replace(block, '') + return html_str, block + + print(f"Could not find {input_id}") + return html_str, "" + +# For each prefix, we will extract the optuna variables and group them. +prefixes = { + 'bt': ['bt_stop', 'bt_target', 'bt_smin', 'bt_scut', 'bt_vol_mult'], + 'tl': ['tl_dip', 'tl_stop', 'tl_target', 'tl_smin', 'tl_scut'], + 'bo': ['bo_sl', 'bo_tp', 'bo_smin', 'bo_scut', 'bo_tp_max'], + 'mom': ['mom_sl', 'mom_tp', 'mom_smin', 'mom_scut', 'mom_tp_max'], +} + +for prefix, ids in prefixes.items(): + blocks = [] + for pid in ids: + html, block = extract_and_remove(html, pid) + if block: + blocks.append(block.strip()) + + if blocks: + # Create a new container for Optuna values + group_html = f""" + +
+
🔬 Optuna 탐색 변수 (TPE 최적화 대상)
+ {''.join(blocks)} +
""" + + # Insert this group right before the EOD block we created earlier, + # or before the TRIGGER block. + if prefix == 'bt': + html = html.replace('', group_html + '\n ') + elif prefix == 'tl': + html = html.replace('', group_html + '\n ') + elif prefix == 'bo': + html = html.replace('', group_html + '\n ') + elif prefix == 'mom': + html = html.replace('
\n
EOD 장마감 청산
', + group_html + '\n
\n
EOD 장마감 청산
') + +# Fix EOD_BLOCK markers +html = html.replace('', '') +html = html.replace('', '') +html = html.replace('', '') +html = html.replace('', '') + +with open(html_path, 'w', encoding='utf-8') as f: + f.write(html) +print("Optuna variables grouped.") diff --git a/scratch/fast_db_stats.py b/scratch/fast_db_stats.py new file mode 100644 index 0000000..f658d95 --- /dev/null +++ b/scratch/fast_db_stats.py @@ -0,0 +1,50 @@ +import sys +from pathlib import Path +sys.path.append(str(Path(__file__).resolve().parent.parent)) +from database import TradeDB + +db = TradeDB() + +tables = [ + ("공통", "매매내역", "trade_history"), + ("한국투자증권(KIS) / 키움", "실시간 체결 틱", "ws_ticks"), + ("한국투자증권(KIS) / 키움", "실시간 캔들 (분봉)", "ws_candles"), + ("한국투자증권(KIS) / 키움", "호가 스냅샷 (Orderbook)", "ws_orderbook"), + ("한국투자증권(KIS) / 키움", "호가 스냅샷 (Orderbook)", "kis_ws_orderbook"), + ("LS증권", "실시간 체결 틱", "ls_ws_ticks"), + ("LS증권", "실시간 캔들 (분봉)", "ls_ws_candles"), + ("LS증권", "호가 스냅샷 (Orderbook)", "ls_ws_orderbook"), +] + +print("| 증권사 | 데이터 유형 | 테이블명 | 2026-08-11 당일 적재 건수 |") +print("|---|---|---|---|") + +today_start = '20260811000000' +today_end = '20260812000000' + +for broker, ttype, table in tables: + try: + cols = [dict(r)["Field"] for r in db.conn.execute(f"SHOW COLUMNS FROM {table}").fetchall()] + + time_col = None + if "timestamp" in cols: + time_col = "timestamp" + elif "recv_ts" in cols: + time_col = "recv_ts" + elif "created_at" in cols: + time_col = "created_at" + + if time_col: + sql = f"SELECT COUNT(*) as c FROM {table} WHERE {time_col} >= %s AND {time_col} < %s" + c = db.conn.execute(sql, (today_start, today_end)).fetchone()["c"] + else: + c = "에러: 시간 컬럼 없음" + + if isinstance(c, int): + print(f"| {broker} | {ttype} | `{table}` | {c:,} 건 |") + else: + print(f"| {broker} | {ttype} | `{table}` | {c} |") + except Exception as e: + print(f"| {broker} | {ttype} | `{table}` | 에러: {e} |") + +db.close() diff --git a/scratch/find_div_diff.py b/scratch/find_div_diff.py new file mode 100644 index 0000000..938fe3a --- /dev/null +++ b/scratch/find_div_diff.py @@ -0,0 +1,15 @@ +with open('templates/backtest.html', 'r', encoding='utf-8') as f: + lines = f.readlines() + +count = 0 +for i, line in enumerate(lines): + if i < 490: continue + if i > 816: break + + opens = line.count(' before it + end_div_idx = html.rfind('
', 0, end_idx) + html = html[:end_div_idx+6] + +tracker = DivTracker() +tracker.feed(html) +print("Unclosed div start positions inside tab-backtest:") +for pos in tracker.stack: + print(pos) diff --git a/scratch/find_unclosed_2.py b/scratch/find_unclosed_2.py new file mode 100644 index 0000000..dcccbbb --- /dev/null +++ b/scratch/find_unclosed_2.py @@ -0,0 +1,35 @@ +from html.parser import HTMLParser + +class DivTracker(HTMLParser): + def __init__(self): + super().__init__() + self.stack = [] + self.found = False + + def handle_starttag(self, tag, attrs): + if tag == 'div': + attrs_dict = dict(attrs) + if attrs_dict.get('id') == 'tab-backtest': + self.found = True + + if self.found: + # store class and id for debugging + cls = attrs_dict.get('class', '') + id_ = attrs_dict.get('id', '') + self.stack.append((self.getpos(), cls, id_)) + + def handle_endtag(self, tag): + if tag == 'div' and self.found: + if self.stack: + self.stack.pop() + if len(self.stack) == 0: + self.found = False # End of tab-backtest + +with open('templates/backtest.html', 'r', encoding='utf-8') as f: + html = f.read() + +tracker = DivTracker() +tracker.feed(html) +print("Unclosed divs inside tab-backtest:") +for item in tracker.stack: + print(item) diff --git a/scratch/fix_bloat.py b/scratch/fix_bloat.py new file mode 100644 index 0000000..b2e989b --- /dev/null +++ b/scratch/fix_bloat.py @@ -0,0 +1,136 @@ +import subprocess +import os + +ROOT = "/home/hoon/kis_bot" + +def run(cmd): + subprocess.run(cmd, shell=True, check=True, cwd=ROOT) + +print("Restoring bloated files...") +run("git checkout templates/backtest.html static/js/backtest.js") +print("Done restoring files.") + +# Re-apply HTML changes +html_path = f"{ROOT}/templates/backtest.html" +with open(html_path, "r", encoding="utf-8") as f: + html = f.read() + +html = html.replace( + "📈 KIS Quant 백테스트 대시보드", + "📈 KIS Quant 백테스트 대시보드\n" +) + +eod_block = """
+ +
+
EOD 장마감 청산
+
+
+ + +
+
+
+ + +
+
+ + +
+
+ + +
""" + +html = html.replace( + """ + +
""", + f""" + + {eod_block}""" +) + +with open(html_path, "w", encoding="utf-8") as f: + f.write(html) +print("Applied HTML changes.") + +# Re-apply JS changes +js_path = f"{ROOT}/static/js/backtest.js" +with open(js_path, "r", encoding="utf-8") as f: + js = f.read() + +js = js.replace( + "cumReturnPct: Number(t.cum_return_pct ?? NaN),", + "cumReturnPct: Number(t.cum_return_pct ?? NaN),\n entryOb: t.entry_ob || null,\n exitOb: t.exit_ob || null," +) + +js = js.replace( + "const nameCell = (r.name || r.code)\n ? `${r.name || r.code}
${r.code || ''}`\n : (r.code || '-');", + "const nameCell = (r.name || r.code)\n ? `${r.name || r.code}
${r.code || ''}`\n : (r.code || '-');\n const linksHtml = _tradeExtLinksHtml(r);" +) + +js = js.replace( + "
", + "" +) + +js = js.replace( + "if (opts.showDebug) html += ``;", + "if (opts.showDebug) html += ``;\n html += ``;\n html += ``;" +) + +js = js.replace( + " html += ``;", + " html += ``;" # unchanged but kept for context, already handled above +) + +helper_funcs = """/** KR 6자리 종목코드 */ +function _normKrStockCode(code) { + const d = String(code || '').replace(/\\D/g, ''); + if (!d) return ''; + return d.length >= 6 ? d.slice(-6) : d.padStart(6, '0'); +} + +function _tradeExtLinksHtml(r) { + if (r.isUsd || !r.code) return ''; + const c = _normKrStockCode(r.code); + return ` +
+ N + N(M) +
+ `; +} + +function _entryObCellHtml(ob) { + if (!ob) return '-'; + const mr = (ob.mid_ratio || 0).toFixed(1); + const wr = (ob.whale_ratio || 0).toFixed(1); + return `M:${mr}%
W:${wr}%`; +} + +function _exitObCellHtml(ob, isOpen) { + if (isOpen) return '보유중'; + if (!ob) return '-'; + const mr = (ob.mid_ratio || 0).toFixed(1); + const wr = (ob.whale_ratio || 0).toFixed(1); + return `M:${mr}%
W:${wr}%`; +} +""" + +js = js.replace( + "function renderVirtualTrades(tbodyId, trades, opts) {", + helper_funcs + "\nfunction renderVirtualTrades(tbodyId, trades, opts) {" +) + +with open(js_path, "w", encoding="utf-8") as f: + f.write(js) +print("Applied JS changes.") diff --git a/scratch/fix_ls_and_kis_env.py b/scratch/fix_ls_and_kis_env.py new file mode 100644 index 0000000..36ab8e2 --- /dev/null +++ b/scratch/fix_ls_and_kis_env.py @@ -0,0 +1,52 @@ +""" +LS 데이터 누적 중단 및 KIS 검증 모드 설정 스크립트 +""" +import sys +sys.path.insert(0, "/home/hoon/kis_bot") +from database import TradeDB +from kis_trader.utils.env import invalidate_merged_env_cache + +db = TradeDB() + +try: + # 현재 스냅샷 가져오기 + snap = dict(db.get_merged_env_snapshot() or {}) + + changed = False + + # 1. LS 데이터 누적 중단 (사용자 요청: 조건식 연결 안 하면 안 쌓이게) + # LS 관련 환경변수를 False로 변경 + ls_keys = [ + "LS_WS_VALIDATION_ENABLED", + "LS_CONDITION_HISTORY_ENABLED", + "LS_WS_FOLLOW_CONDITION_UNIVERSE", + "LS_WS_ALSO_HOGA" + ] + + print("=== 기존 LS 설정 ===") + for k in ls_keys: + print(f" {k}: {snap.get(k)}") + if str(snap.get(k)).lower() == 'true' or snap.get(k) is True: + snap[k] = False + changed = True + + # 2. KIS 시세 구독 비교를 위해 WS_PROVIDER 변경 + # (이미 kis_with_validation일 수 있지만 확실히 함) + current_provider = snap.get("WS_PROVIDER") + print(f"\n=== 기존 WS_PROVIDER ===") + print(f" WS_PROVIDER: {current_provider}") + + if current_provider != "kis_with_validation": + snap["WS_PROVIDER"] = "kis_with_validation" + changed = True + + if changed: + print("\n=== 설정 변경 적용 중... ===") + db.insert_env_snapshot(snap) + invalidate_merged_env_cache() + print("✅ DB 환경변수 업데이트 및 캐시 무효화 완료!") + else: + print("\n✅ 이미 설정이 올바르게 반영되어 있어 변경할 내용이 없습니다.") + +finally: + db.close() diff --git a/scratch/fix_reject_sleep.py b/scratch/fix_reject_sleep.py new file mode 100644 index 0000000..ec6b6b7 --- /dev/null +++ b/scratch/fix_reject_sleep.py @@ -0,0 +1,22 @@ +from database import TradeDB + +db = TradeDB() +try: + snap = db.get_merged_env_snapshot() + updated = False + + # 기본 스캔 딜레이를 0.01초 ~ 0.05초로 최적화 + snap["SCAN_REJECT_SLEEP_MIN"] = "0.01" + snap["SCAN_REJECT_SLEEP_MAX"] = "0.05" + + # 봇 루프 자체 딜레이도 최소화 + snap["STRATEGY_LOOP_SLEEP_MIN"] = "0.01" + snap["STRATEGY_LOOP_SLEEP_MAX"] = "0.1" + + updated = True + + if updated: + db.insert_env_snapshot(snap) + print("Inserted new env snapshot with optimized sleep.") +finally: + db.close() diff --git a/scratch/fix_reject_sleep_zero.py b/scratch/fix_reject_sleep_zero.py new file mode 100644 index 0000000..98d22d5 --- /dev/null +++ b/scratch/fix_reject_sleep_zero.py @@ -0,0 +1,18 @@ +from database import TradeDB + +db = TradeDB() +try: + snap = db.get_merged_env_snapshot() + + # 딜레이 0으로 완전 제거 (스레드 Yield 만 발생하도록) + snap["SCAN_REJECT_SLEEP_MIN"] = "0.0" + snap["SCAN_REJECT_SLEEP_MAX"] = "0.0" + + # 루프 자체도 딜레이 0 + snap["STRATEGY_LOOP_SLEEP_MIN"] = "0.0" + snap["STRATEGY_LOOP_SLEEP_MAX"] = "0.0" + + db.insert_env_snapshot(snap) + print("Inserted new env snapshot with ZERO sleep.") +finally: + db.close() diff --git a/scratch/fix_ws_provider.py b/scratch/fix_ws_provider.py new file mode 100644 index 0000000..58a3523 --- /dev/null +++ b/scratch/fix_ws_provider.py @@ -0,0 +1,25 @@ +""" +DB env_config 테이블 실제 컬럼 확인 후 LS_WS_VALIDATION_ENABLED 끄기 +""" +import sys +sys.path.insert(0, "/home/hoon/kis_bot") +from database import TradeDB + +db = TradeDB() + +try: + # env_config 테이블 컬럼 확인 + cols = [dict(r)["Field"] for r in db.conn.execute("SHOW COLUMNS FROM env_config").fetchall()] + print(f"env_config 컬럼: {cols}") + + # 샘플 3건 확인 + rows = db.conn.execute("SELECT * FROM env_config LIMIT 3").fetchall() + for r in rows: + print(f" {dict(r)}") + + # LS WS 끄기 - 실제 컬럼명 기준 + # 컬럼: [id, key_name, value, ...] 또는 [id, env_key, env_val, ...] + # 위에서 확인 후 결정 + +finally: + db.close() diff --git a/scratch/group_optuna.py b/scratch/group_optuna.py new file mode 100644 index 0000000..4a84cfa --- /dev/null +++ b/scratch/group_optuna.py @@ -0,0 +1,8 @@ +import re + +path = 'templates/backtest.html' +with open(path, 'r', encoding='utf-8') as f: + html = f.read() + +# I will just add a small badge Optuna to the label +# of the known optuna parameters. The user said "어차피 빨갛게 표시햇으니", maybe I already did this? diff --git a/scratch/move_eod.py b/scratch/move_eod.py new file mode 100644 index 0000000..3fa639d --- /dev/null +++ b/scratch/move_eod.py @@ -0,0 +1,63 @@ +import re + +path = 'templates/backtest.html' +with open(path, 'r', encoding='utf-8') as f: + html = f.read() + +prefixes = ['bt', 'tl', 'bo', 'mom', 'usmom'] + +for prefix in prefixes: + # 1. find the eod_enabled div + regex_enabled = re.compile( + r'(\s*
\s*
\s*]*>\s*]*>EOD청산\s*
\s*
)', + re.DOTALL + ) + # 2. find the eod_hm div + regex_hm = re.compile( + r'(\s*
\s*]*>EOD시각.*?\s*]*>\s*
)', + re.DOTALL + ) + # fallback for bt_eod_hm which uses col-4 col-md-1 + regex_hm2 = re.compile( + r'(\s*
\s*]*>EOD시각.*?\s*]*>\s*
)', + re.DOTALL + ) + + enabled_match = regex_enabled.search(html) + hm_match = regex_hm.search(html) or regex_hm2.search(html) + + if not enabled_match or not hm_match: + print(f"Failed to match {prefix}") + continue + + enabled_str = enabled_match.group(1) + hm_str = hm_match.group(1) + + # Remove from html + html = html.replace(enabled_str, '') + html = html.replace(hm_str, '') + + # We want to insert the new EOD block after the closing of the row. + # To do this robustly, we will insert it right before the next "', new_block + '\n ') + elif prefix == 'tl': + html = html.replace('', new_block + '\n ') + elif prefix == 'bo': + html = html.replace('', new_block + '\n ') + elif prefix == 'mom': + html = html.replace('
\n
\n
- + @@ -372,6 +408,8 @@ function renderVirtualTrades(tbodyId, trades, opts) { } html += ``; if (opts.showDebug) html += ``; + html += ``; + html += ``; html += ``; if (opts.showRsi) html += ``; html += ''; @@ -2072,6 +2110,9 @@ function runBreakoutBacktest() { total_budget_krw: $('bo_total_budget')?.value || '0', universe: $('bo_use_univ_history')?.checked ? 'history' : 'all', universe_history_source: univHistorySourceParam('bo_univ_history_source'), + candle_source: $('bo_candle_source')?.value || '', + tick_source: $('bo_tick_source')?.value || '', + ob_source: $('bo_ob_source')?.value || '', ob_filter: $('bo_ob_filter')?.checked ? 1 : 0, pg_filter: $('bo_pg_filter')?.checked ? 1 : 0, max_spread_pct: $('bo_max_spread_pct')?.value, @@ -2541,6 +2582,9 @@ function runMomentumBacktest() { mom_min_from_open_pct: v('mom_min_from_open') || '-999', universe: $('mom_use_univ_history')?.checked ? 'history' : 'sim', universe_history_source: univHistorySourceParam('mom_univ_history_source'), + candle_source: $('mom_candle_source')?.value || '', + tick_source: $('mom_tick_source')?.value || '', + ob_source: $('mom_ob_source')?.value || '', daily_trail_tiers: (v('mom_daily_trail_tiers') || '').trim(), daily_trail_drop_pct: v('mom_daily_trail_drop') || '0', daily_trail_arm_krw: v('mom_daily_trail_arm') || '0', @@ -3216,6 +3260,9 @@ async function usmomStockOptunaStart() { if ($('usmom_bt_codes')) $('usmom_bt_codes').value = code; const trials = parseInt($('usmom_opt_trials')?.value || '100', 10) || 100; const mode = ($('usmom_opt_mode')?.value || 'tpe').trim() || 'tpe'; + const candleSrc = $('opt_candle_source')?.value || ''; + const tickSrc = $('opt_tick_source')?.value || ''; + const obSrc = $('opt_ob_source')?.value || ''; if (!confirm( `종목 Optuna 시작?\n${code} (1종목 유니버스)\n${start}~${end} trials=${trials}\n` + `완료 후 「폼에 넣기」→백테 검증 · 「종목행에 적용」→ stock_config\n(전역 US_MOMENTUM_* 미적용)` @@ -3228,6 +3275,9 @@ async function usmomStockOptunaStart() { strategy: 'us_momentum', symbol: code, start, end, trials, mode, + candle_source: candleSrc, + tick_source: tickSrc, + ob_source: obSrc, }), }); const j = await r.json(); @@ -4590,6 +4640,9 @@ function runBacktest() { }; params.universe = $('bt_use_univ_history')?.checked ? 'history' : 'sim'; params.universe_history_source = univHistorySourceParam('bt_univ_history_source'); + params.candle_source = $('bt_candle_source')?.value || ''; + params.tick_source = $('bt_tick_source')?.value || ''; + params.ob_source = $('bt_ob_source')?.value || ''; const qs = new URLSearchParams(params).toString(); showSpinner(true); fetch('/api/backtest/scalping?' + qs) @@ -5272,6 +5325,9 @@ function runTailBacktest() { atr_tp_max_pct: $('tl_atr_tp_max').value, universe: $('tl_use_univ_history')?.checked ? 'history' : 'all', universe_history_source: univHistorySourceParam('tl_univ_history_source'), + candle_source: $('tl_candle_source')?.value || '', + tick_source: $('tl_tick_source')?.value || '', + ob_source: $('tl_ob_source')?.value || '', entry_mode: ($('tl_entry_mode') && $('tl_entry_mode').value) || 'limit_atr', limit_atr_mult: $('tl_limit_atr_mult')?.value || '1.5', limit_anchor: ($('tl_limit_anchor') && $('tl_limit_anchor').value) || 'signal_low', @@ -7904,6 +7960,9 @@ async function optunaStart() { const trials = parseInt($('opt_trials')?.value || '200', 10); const mode = $('opt_mode')?.value || 'tpe'; const univSrc = univHistorySourceParam('opt_univ_history_source'); + const candleSrc = $('opt_candle_source')?.value || ''; + const tickSrc = $('opt_tick_source')?.value || ''; + const obSrc = $('opt_ob_source')?.value || ''; if (!strategies.length) { alert('전략을 1개 이상 체크하세요'); return; } if (!start || !end) { alert('시작·종료일을 입력하세요'); return; } const how = strategies.length >= 2 ? `순차 ${strategies.length}개` : strategies[0]; @@ -7916,6 +7975,9 @@ async function optunaStart() { body: JSON.stringify({ strategies, start, end, trials, mode, universe_history_source: univSrc, + candle_source: candleSrc, + tick_source: tickSrc, + ob_source: obSrc, }), }); const j = await r.json(); @@ -8018,6 +8080,13 @@ function kisMockApplyUi(d) { st.style.fontSize = '11px'; st.style.whiteSpace = 'nowrap'; } + const lss = $('live_source_status'); + if (lss && d) { + const c = d.candle_source || '-'; + const t = d.tick_source || '-'; + const o = d.ob_source || '-'; + lss.textContent = `캔들:${c} | 시세:${t} | 호가:${o}`; + } } async function kisMockLoad() { diff --git a/templates/backtest.html b/templates/backtest.html index fe9725e..31b714f 100644 --- a/templates/backtest.html +++ b/templates/backtest.html @@ -4,6 +4,7 @@ 📈 KIS Quant 백테스트 대시보드 + @@ -51,7 +52,10 @@ - +
+ + 캔들: - | 시세: - +
@@ -528,9 +532,21 @@ + + + + + +
@@ -599,6 +615,34 @@
+ + +
+
EOD 장마감 청산
+
+
+ + +
+
+
+ + +
+
+ + +
+
+ + +
@@ -847,9 +891,21 @@ + + + + + +
@@ -1768,9 +1824,21 @@ + + + + + +
@@ -2180,9 +2248,21 @@ + + + + + +
@@ -3694,10 +3774,34 @@
+
+ + +
+
+ + +
+
+ + +
${causeBadge}${nameCell}${causeBadge}${nameCell}${linksHtml}${debugHtml}${debugHtml}${_entryObCellHtml(r.entryOb)}${_exitObCellHtml(r.exitOb, r.isOpen)}${reasonHtml}${reasonHtml}
${causeBadge}${nameCell}${causeBadge}${nameCell}${linksHtml} ${fmtTradeTime(r.buyRaw)} ${sellCell} ${fmtPx(r.ep, rowFx)}${r.hold}${debugHtml}${_entryObCellHtml(r.entryOb)}${_exitObCellHtml(r.exitOb, r.isOpen)}${reasonHtml}${r.rsi != null && r.rsi !== '' ? r.rsi : '-'}