refactor: enhance Optuna backtesting framework, optimize orderbook filtering, and update database management utilities.
This commit is contained in:
3853
scripts/sql/all_config_env_column_comments.generated.sql
Normal file
3853
scripts/sql/all_config_env_column_comments.generated.sql
Normal file
File diff suppressed because it is too large
Load Diff
5763
scripts/sql/all_db_column_comments.generated.sql
Normal file
5763
scripts/sql/all_db_column_comments.generated.sql
Normal file
File diff suppressed because it is too large
Load Diff
47
scripts/sql/config_breakout_ob_column_comments.sql
Normal file
47
scripts/sql/config_breakout_ob_column_comments.sql
Normal file
@@ -0,0 +1,47 @@
|
||||
-- config_breakout — 수익구간 호가매도 / 손절호가 컬럼 설명 (MariaDB)
|
||||
-- 실행: mysql ... < scripts/sql/config_breakout_ob_column_comments.sql
|
||||
-- 타입은 기존과 동일 TEXT. COMMENT만 부여.
|
||||
-- 주의: pymysql로 돌릴 때 % 는 %% 로 이스케이프 필요 → 본 파일은 %% 사용.
|
||||
-- 호가 소스: 돌파=LS(ls_ws_orderbook) · 모멘텀=키움 — 교차 금지.
|
||||
|
||||
ALTER TABLE `config_breakout`
|
||||
MODIFY COLUMN `BREAKOUT_EXIT_OB_ENABLED`
|
||||
TEXT NULL
|
||||
COMMENT '수익구간 호가매도 ON/OFF. true=이미 이익일 때 OR붕괴면 호가컷. 기본 false. LS호가. 진입필터와 무관'
|
||||
|
||||
, MODIFY COLUMN `BREAKOUT_EXIT_OB_RATIO_MIN`
|
||||
TEXT NULL
|
||||
COMMENT '수익구간: 총사겠음÷총팔겠음(OR) 이동평균이 이 값 미만이면 붕괴. 시드 0.4'
|
||||
|
||||
, MODIFY COLUMN `BREAKOUT_EXIT_OB_MA_WINDOW`
|
||||
TEXT NULL
|
||||
COMMENT '수익구간: OR 이동평균에 쓰는 최근 호가 스냅 개수. 시드 5'
|
||||
|
||||
, MODIFY COLUMN `BREAKOUT_EXIT_OB_MIN_PROFIT_PCT`
|
||||
TEXT NULL
|
||||
COMMENT '수익구간: 최소 이익(분율). 0.005=+0.5%%. 미달이면 호가컷 안 함'
|
||||
|
||||
, MODIFY COLUMN `BREAKOUT_EXIT_OB_MIN_HOLD_BARS`
|
||||
TEXT NULL
|
||||
COMMENT '수익구간: 산 뒤 최소 보유 분(1분봉≈1). 시드 3. 미달이면 안 함'
|
||||
|
||||
, MODIFY COLUMN `BREAKOUT_STOP_OB_ENABLED`
|
||||
TEXT NULL
|
||||
COMMENT '손절호가 ON/OFF. true=이미 손해+OR붕괴면 하드손절 전에 손절호가. 기본 false. LS호가'
|
||||
|
||||
, MODIFY COLUMN `BREAKOUT_STOP_OB_RATIO_MIN`
|
||||
TEXT NULL
|
||||
COMMENT '손절호가: OR 이동평균이 이 값 미만이면 붕괴. 시드 0.4'
|
||||
|
||||
, MODIFY COLUMN `BREAKOUT_STOP_OB_MA_WINDOW`
|
||||
TEXT NULL
|
||||
COMMENT '손절호가: OR 이동평균 스냅 개수. 시드 5'
|
||||
|
||||
, MODIFY COLUMN `BREAKOUT_STOP_OB_MIN_LOSS_PCT`
|
||||
TEXT NULL
|
||||
COMMENT '손절호가: 최소 손실 밴드(분율). 0.003=−0.3%%. 이만큼 이상 손해일 때만'
|
||||
|
||||
, MODIFY COLUMN `BREAKOUT_STOP_OB_MIN_HOLD_BARS`
|
||||
TEXT NULL
|
||||
COMMENT '손절호가: 산 뒤 최소 보유 분. 시드 2. 미달이면 안 함'
|
||||
;
|
||||
46
scripts/sql/config_momentum_ob_column_comments.sql
Normal file
46
scripts/sql/config_momentum_ob_column_comments.sql
Normal file
@@ -0,0 +1,46 @@
|
||||
-- config_momentum — 수익구간 호가매도 / 손절호가 컬럼 설명 (MariaDB)
|
||||
-- 실행: mysql ... < scripts/sql/config_momentum_ob_column_comments.sql
|
||||
-- 타입은 기존과 동일 TEXT. COMMENT만 부여.
|
||||
-- 주의: pymysql로 돌릴 때 % 는 %% 로 이스케이프 필요 → 본 파일은 %% 사용.
|
||||
|
||||
ALTER TABLE `config_momentum`
|
||||
MODIFY COLUMN `MOMENTUM_EXIT_OB_ENABLED`
|
||||
TEXT NULL
|
||||
COMMENT '수익구간 호가매도 ON/OFF. true=이미 이익일 때 OR붕괴면 호가컷. 기본 false. 진입필터와 무관'
|
||||
|
||||
, MODIFY COLUMN `MOMENTUM_EXIT_OB_RATIO_MIN`
|
||||
TEXT NULL
|
||||
COMMENT '수익구간: 총사겠음÷총팔겠음(OR) 이동평균이 이 값 미만이면 붕괴. 시드 0.4'
|
||||
|
||||
, MODIFY COLUMN `MOMENTUM_EXIT_OB_MA_WINDOW`
|
||||
TEXT NULL
|
||||
COMMENT '수익구간: OR 이동평균에 쓰는 최근 호가 스냅 개수. 시드 5'
|
||||
|
||||
, MODIFY COLUMN `MOMENTUM_EXIT_OB_MIN_PROFIT_PCT`
|
||||
TEXT NULL
|
||||
COMMENT '수익구간: 최소 이익(분율). 0.005=+0.5%%. 미달이면 호가컷 안 함'
|
||||
|
||||
, MODIFY COLUMN `MOMENTUM_EXIT_OB_MIN_HOLD_BARS`
|
||||
TEXT NULL
|
||||
COMMENT '수익구간: 산 뒤 최소 보유 분(1분봉≈1). 시드 3. 미달이면 안 함'
|
||||
|
||||
, MODIFY COLUMN `MOMENTUM_STOP_OB_ENABLED`
|
||||
TEXT NULL
|
||||
COMMENT '손절호가 ON/OFF. true=이미 손해+OR붕괴면 하드손절 전에 손절호가. 기본 false'
|
||||
|
||||
, MODIFY COLUMN `MOMENTUM_STOP_OB_RATIO_MIN`
|
||||
TEXT NULL
|
||||
COMMENT '손절호가: OR 이동평균이 이 값 미만이면 붕괴. 시드 0.4'
|
||||
|
||||
, MODIFY COLUMN `MOMENTUM_STOP_OB_MA_WINDOW`
|
||||
TEXT NULL
|
||||
COMMENT '손절호가: OR 이동평균 스냅 개수. 시드 5. 수익구간과 히스토리 공유 가능'
|
||||
|
||||
, MODIFY COLUMN `MOMENTUM_STOP_OB_MIN_LOSS_PCT`
|
||||
TEXT NULL
|
||||
COMMENT '손절호가: 최소 손실 밴드(분율). 0.003=−0.3%%. 이만큼 이상 손해일 때만'
|
||||
|
||||
, MODIFY COLUMN `MOMENTUM_STOP_OB_MIN_HOLD_BARS`
|
||||
TEXT NULL
|
||||
COMMENT '손절호가: 산 뒤 최소 보유 분. 시드 2. 미달이면 안 함'
|
||||
;
|
||||
378
scripts/sql/fill_all_config_env_column_comments.py
Normal file
378
scripts/sql/fill_all_config_env_column_comments.py
Normal file
@@ -0,0 +1,378 @@
|
||||
#!/usr/bin/env python3
|
||||
"""config_* / env_config 전 컬럼 MariaDB COMMENT 채우기 (타입·값 변경 없음).
|
||||
|
||||
소스 우선순위:
|
||||
1) 이미 DB에 있는 COMMENT (비어있지 않으면 유지)
|
||||
2) live_config_schema label+hint
|
||||
3) 키 이름 휴리스틱 한글 설명
|
||||
4) 메타 컬럼(id/created_at) 고정문구
|
||||
|
||||
실행:
|
||||
cd /home/hoon/kis_bot && python3 scripts/sql/fill_all_config_env_column_comments.py
|
||||
# SQL만: ... --sql-only > scripts/sql/all_config_env_column_comments.generated.sql
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
TABLES = [
|
||||
"config_us_momentum",
|
||||
"config_momentum",
|
||||
"config_short",
|
||||
"config_breakout",
|
||||
"config_updow",
|
||||
"config_scalp",
|
||||
"config_range_break",
|
||||
"config_dbband",
|
||||
"config_dart",
|
||||
"env_config",
|
||||
]
|
||||
|
||||
# 긴 토큰 우선
|
||||
_TOKEN_KO: List[Tuple[str, str]] = [
|
||||
("STRATEGY", "전략"),
|
||||
("DAILY_PROFIT", "일일익절"),
|
||||
("STOP_LOSS", "손절"),
|
||||
("TAKE_PROFIT", "익절"),
|
||||
("TRAIL_ARM", "트레일무장"),
|
||||
("TRAIL", "트레일"),
|
||||
("SHOULDER_MIN_HIGH", "어깨무장고점"),
|
||||
("SHOULDER_CUT", "어깨컷"),
|
||||
("SHOULDER", "어깨"),
|
||||
("RATCHET_TIERS", "다단래칫"),
|
||||
("RATCHET", "래칫"),
|
||||
("ORDERBOOK", "호가"),
|
||||
("PROGRAM", "프로그램매매"),
|
||||
("WHIPSAW", "휩쏘"),
|
||||
("UNIVERSE_SOURCE", "유니버스소스"),
|
||||
("UNIVERSE", "유니버스"),
|
||||
("CAND_LIMIT", "후보상한"),
|
||||
("MAX_STOCKS", "동시보유상한"),
|
||||
("SLOT_MONEY", "슬롯금액"),
|
||||
("TOTAL_BUDGET", "총운용한도"),
|
||||
("MAX_BUY_AMOUNT", "1회최대매수금액"),
|
||||
("MAX_ORDER_QTY", "주문수량상한"),
|
||||
("FORCE_BUY", "강제매수테스트"),
|
||||
("LIVE_MIN_CANDLES", "실매최소봉수"),
|
||||
("MIN_CANDLES", "최소봉수"),
|
||||
("MAX_HOLD_BARS", "최대보유봉"),
|
||||
("MIN_HOLD", "최소보유"),
|
||||
("HOLD_BARS", "보유봉"),
|
||||
("MAX_DAILY_CHG", "당일등락상한"),
|
||||
("MAX_DAILY", "일일매수횟수상한"),
|
||||
("HIGH_CHASE", "고점추격"),
|
||||
("FROM_OPEN", "시가대비"),
|
||||
("PULLBACK", "되돌림"),
|
||||
("BREAKOUT", "돌파"),
|
||||
("PATTERN", "패턴"),
|
||||
("DEFENSE", "방어필터"),
|
||||
("EMA_FAST", "EMA단기"),
|
||||
("EMA_SLOW", "EMA장기"),
|
||||
("EMA", "EMA"),
|
||||
("RSI_OVERBOUGHT", "RSI과매수"),
|
||||
("RSI_OVERSOLD", "RSI과매도"),
|
||||
("RSI_MAX", "RSI상한"),
|
||||
("RSI_MIN", "RSI하한"),
|
||||
("RSI", "RSI"),
|
||||
("VOL_MULT", "거래량배수"),
|
||||
("VOL_WIN", "거래량윈도우"),
|
||||
("VOL", "거래량"),
|
||||
("ATR_SL", "ATR손절"),
|
||||
("ATR_TP", "ATR익절"),
|
||||
("ATR", "ATR"),
|
||||
("EXIT_OB", "수익구간호가매도"),
|
||||
("STOP_OB", "손절호가"),
|
||||
("TICK_SELL", "틱매도"),
|
||||
("TICK_FALLBACK_OHLC", "틱없을때OHLC폴백(위험)"),
|
||||
("FALLBACK_OHLC", "OHLC폴백"),
|
||||
("SKIP_HTS_SCAN_DUPES", "HTS스캔중복스킵"),
|
||||
("SESSION_START", "세션시작"),
|
||||
("SESSION_END", "세션종료"),
|
||||
("SELL_TIME_END", "매도세션종료"),
|
||||
("TIME_START", "매수시작"),
|
||||
("TIME_END", "매수종료"),
|
||||
("EOD_ENABLED", "장마감청산ON"),
|
||||
("EOD_HM", "장마감시각HHMM"),
|
||||
("EOD", "장마감"),
|
||||
("DRY_RUN", "모의/페이퍼전용"),
|
||||
("FEE_RATE", "수수료율"),
|
||||
("SELL_TAX", "매도세금"),
|
||||
("FX_FEE", "환전수수료"),
|
||||
("MIN_PRICE", "최소가격"),
|
||||
("MAX_PRICE", "최대가격"),
|
||||
("COOLDOWN", "쿨다운"),
|
||||
("RELOAD_INTERVAL", "설정리로드주기"),
|
||||
("LOOP_SLEEP", "루프대기"),
|
||||
("SCAN_REJECT", "스캔탈락대기"),
|
||||
("PSAMOUNT", "매수가능수량"),
|
||||
("BUY_WAIT_LOG", "매수대기로그"),
|
||||
("APPROVAL", "WS_approval키"),
|
||||
("REISSUE", "재발급"),
|
||||
("HOLD_START", "hold시작"),
|
||||
("HOLD_END", "hold종료"),
|
||||
("WATCHDOG", "워치독"),
|
||||
("SILENCE", "틱공백"),
|
||||
("PING", "프로토콜ping"),
|
||||
("ORDERBOOK", "호가"),
|
||||
("ENABLED", "ON/OFF"),
|
||||
("TARGET", "목표"),
|
||||
("BUDGET", "한도"),
|
||||
("HALT", "중단"),
|
||||
("NOTIFY", "알림"),
|
||||
("MODE", "모드"),
|
||||
("TIERS", "다단"),
|
||||
("DROP", "되돌림/하락"),
|
||||
("ARM", "무장"),
|
||||
("PCT", "%"),
|
||||
("KRW", "원"),
|
||||
("USD", "USD"),
|
||||
("SEC", "초"),
|
||||
("MS", "밀리초"),
|
||||
("HM", "HHMM"),
|
||||
("BARS", "봉수"),
|
||||
("PERIOD", "기간"),
|
||||
("WINDOW", "윈도우"),
|
||||
("MULT", "배수"),
|
||||
("MIN", "최소"),
|
||||
("MAX", "최대"),
|
||||
("AVG", "평균"),
|
||||
("FILTER", "필터"),
|
||||
("USE_", "사용_"),
|
||||
]
|
||||
|
||||
_META = {
|
||||
"id": "PK · AUTO_INCREMENT",
|
||||
"created_at": "설정행 생성시각",
|
||||
"updated_at": "마지막 갱신시각",
|
||||
}
|
||||
|
||||
_TABLE_PREFIX = {
|
||||
"config_us_momentum": "해외모멘텀",
|
||||
"config_momentum": "모멘텀",
|
||||
"config_short": "꼬리잡기",
|
||||
"config_breakout": "돌파",
|
||||
"config_updow": "UPDOWN",
|
||||
"config_scalp": "스캘핑",
|
||||
"config_range_break": "박스권돌파",
|
||||
"config_dbband": "더블BB",
|
||||
"config_dart": "DART",
|
||||
"env_config": "공통env",
|
||||
}
|
||||
|
||||
|
||||
def _esc_comment(s: str) -> str:
|
||||
"""MariaDB COMMENT 문자열 — 작은따옴표 이스케이프, 길이 제한.
|
||||
|
||||
TradeDB/pymysql 은 SQL 의 ``%`` 를 포맷으로 해석 → COMMENT 안 ``%`` 는 ``%%``.
|
||||
"""
|
||||
s = re.sub(r"\s+", " ", (s or "").strip())
|
||||
s = s.replace("\\", "\\\\").replace("'", "''")
|
||||
s = s.replace("%", "%%")
|
||||
# MySQL comment 최대 1024; 안전하게 240자 (%% 두 배 전에 자름)
|
||||
if len(s) > 240:
|
||||
s = s[:237] + "..."
|
||||
return s
|
||||
|
||||
|
||||
def _heuristic(key: str, table: str) -> str:
|
||||
if key in _META:
|
||||
return _META[key]
|
||||
k = key
|
||||
# strip known strategy prefixes for readability
|
||||
for pref in (
|
||||
"US_MOMENTUM_", "MOMENTUM_", "BREAKOUT_", "SCALP_", "TAIL_", "SHORT_",
|
||||
"UPDOW_", "UPDOWN_", "RANGE_BREAK_", "DBBAND_", "DART_",
|
||||
"KIS_WS_", "KIWOOM_WS_", "LS_WS_", "LS_TOKEN_", "LS_AFR_",
|
||||
):
|
||||
if k.startswith(pref):
|
||||
k = k[len(pref):]
|
||||
break
|
||||
if k.startswith("STRATEGY_") and k.endswith("_ENABLED"):
|
||||
sid = k[len("STRATEGY_"):-len("_ENABLED")]
|
||||
return f"{sid} 전략 ON/OFF. 다음루프 신규매수중단(보유청산유지). WS구독해제 안함"
|
||||
|
||||
parts: List[str] = []
|
||||
rest = k
|
||||
# greedy token replace
|
||||
while rest:
|
||||
matched = False
|
||||
for tok, ko in _TOKEN_KO:
|
||||
if rest.startswith(tok):
|
||||
parts.append(ko)
|
||||
rest = rest[len(tok):]
|
||||
if rest.startswith("_"):
|
||||
rest = rest[1:]
|
||||
matched = True
|
||||
break
|
||||
if not matched:
|
||||
# take next underscore chunk
|
||||
if "_" in rest:
|
||||
chunk, rest = rest.split("_", 1)
|
||||
else:
|
||||
chunk, rest = rest, ""
|
||||
parts.append(chunk)
|
||||
body = " ".join(parts) if parts else k
|
||||
scope = _TABLE_PREFIX.get(table, table)
|
||||
# avoid duplicate scope if already in body
|
||||
if scope and scope not in body:
|
||||
return f"{scope}: {body} ({key})"
|
||||
return f"{body} ({key})"
|
||||
|
||||
|
||||
def _load_live_labels() -> Dict[str, str]:
|
||||
out: Dict[str, str] = {}
|
||||
try:
|
||||
from kis_trader.web.live_config_schema import build_live_config_groups
|
||||
except Exception as e:
|
||||
print("live_config_schema import 실패:", e)
|
||||
return out
|
||||
for g in build_live_config_groups():
|
||||
for f in g.get("fields") or []:
|
||||
k = f.get("key")
|
||||
if not k:
|
||||
continue
|
||||
lab = str(f.get("label") or "").strip()
|
||||
hint = str(f.get("hint") or "").strip()
|
||||
bits = [b for b in (lab, hint) if b]
|
||||
if bits:
|
||||
out[str(k)] = " · ".join(bits)
|
||||
return out
|
||||
|
||||
|
||||
def _describe(key: str, table: str, labels: Dict[str, str], existing: str) -> str:
|
||||
if (existing or "").strip():
|
||||
return existing.strip()
|
||||
if key in labels:
|
||||
return labels[key]
|
||||
return _heuristic(key, table)
|
||||
|
||||
|
||||
def _col_type_clause(row: dict) -> str:
|
||||
"""SHOW FULL COLUMNS → MODIFY 타입 절 (Null/Default 유지)."""
|
||||
typ = row["Type"]
|
||||
null = "NULL" if row["Null"] == "YES" else "NOT NULL"
|
||||
default = row["Default"]
|
||||
extra = row.get("Extra") or ""
|
||||
parts = [typ, null]
|
||||
if default is None:
|
||||
if row["Null"] == "YES":
|
||||
parts.append("DEFAULT NULL")
|
||||
else:
|
||||
# numeric vs string
|
||||
if re.match(r"^(tinyint|smallint|int|bigint|decimal|float|double|bit)", typ, re.I):
|
||||
parts.append(f"DEFAULT {default}")
|
||||
else:
|
||||
d = str(default).replace("\\", "\\\\").replace("'", "''")
|
||||
parts.append(f"DEFAULT '{d}'")
|
||||
if "auto_increment" in extra.lower():
|
||||
parts.append("AUTO_INCREMENT")
|
||||
return " ".join(parts)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--sql-only", action="store_true")
|
||||
ap.add_argument("--apply", action="store_true", help="DB에 COMMENT 적용")
|
||||
ap.add_argument(
|
||||
"--out",
|
||||
default=str(ROOT / "scripts/sql/all_config_env_column_comments.generated.sql"),
|
||||
)
|
||||
args = ap.parse_args()
|
||||
if not args.sql_only and not args.apply:
|
||||
args.apply = True # 기본 적용
|
||||
|
||||
labels = _load_live_labels()
|
||||
print(f"live_config labels: {len(labels)}")
|
||||
|
||||
from database import TradeDB
|
||||
|
||||
db = TradeDB()
|
||||
sql_chunks: List[str] = [
|
||||
"-- AUTO-GENERATED: config_*/env_config 전 컬럼 COMMENT (타입·값 불변)",
|
||||
"-- 생성: scripts/sql/fill_all_config_env_column_comments.py",
|
||||
"",
|
||||
]
|
||||
stats = {"tables": 0, "cols": 0, "kept": 0, "new": 0, "updated": 0}
|
||||
|
||||
for table in TABLES:
|
||||
rows = db.conn.execute(f"SHOW FULL COLUMNS FROM `{table}`").fetchall()
|
||||
stats["tables"] += 1
|
||||
mods: List[str] = []
|
||||
for r in rows:
|
||||
field = r["Field"]
|
||||
stats["cols"] += 1
|
||||
existing = (r.get("Comment") or "").strip()
|
||||
desc = _describe(field, table, labels, existing)
|
||||
if existing:
|
||||
stats["kept"] += 1
|
||||
# 기존 유지 — 그래도 SQL에 명시해 문서화
|
||||
else:
|
||||
stats["new"] += 1
|
||||
if existing == desc and existing:
|
||||
# still emit for generated SQL completeness
|
||||
pass
|
||||
else:
|
||||
if existing and existing != desc:
|
||||
stats["updated"] += 1 # shouldn't happen (we keep existing)
|
||||
type_clause = _col_type_clause(r)
|
||||
cmt = _esc_comment(desc)
|
||||
mods.append(
|
||||
f" MODIFY COLUMN `{field}`\n"
|
||||
f" {type_clause}\n"
|
||||
f" COMMENT '{cmt}'"
|
||||
)
|
||||
sql_chunks.append(f"-- ── {table} ({len(rows)} cols) ──")
|
||||
sql_chunks.append(f"ALTER TABLE `{table}`")
|
||||
sql_chunks.append(",\n".join(mods))
|
||||
sql_chunks.append(";\n")
|
||||
|
||||
if args.apply:
|
||||
# apply one MODIFY at a time (safer than huge multi)
|
||||
for r in rows:
|
||||
field = r["Field"]
|
||||
existing = (r.get("Comment") or "").strip()
|
||||
desc = _describe(field, table, labels, existing)
|
||||
if existing == desc:
|
||||
continue
|
||||
type_clause = _col_type_clause(r)
|
||||
cmt = _esc_comment(desc)
|
||||
stmt = (
|
||||
f"ALTER TABLE `{table}` MODIFY COLUMN `{field}` "
|
||||
f"{type_clause} COMMENT '{cmt}'"
|
||||
)
|
||||
try:
|
||||
db.conn.execute(stmt)
|
||||
except Exception as e:
|
||||
print(f"FAIL {table}.{field}: {e}")
|
||||
raise
|
||||
print(f"applied {table}")
|
||||
|
||||
out = Path(args.out)
|
||||
out.write_text("\n".join(sql_chunks) + "\n", encoding="utf-8")
|
||||
print(f"wrote {out} ({out.stat().st_size} bytes)")
|
||||
print(
|
||||
f"tables={stats['tables']} cols={stats['cols']} "
|
||||
f"kept_existing={stats['kept']} filled_empty={stats['new']}"
|
||||
)
|
||||
|
||||
# empty remaining?
|
||||
if args.apply:
|
||||
empty = 0
|
||||
for table in TABLES:
|
||||
for r in db.conn.execute(f"SHOW FULL COLUMNS FROM `{table}`").fetchall():
|
||||
if not (r.get("Comment") or "").strip():
|
||||
empty += 1
|
||||
print("still empty", table, r["Field"])
|
||||
print("remaining empty comments:", empty)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
434
scripts/sql/fill_all_db_column_comments.py
Normal file
434
scripts/sql/fill_all_db_column_comments.py
Normal file
@@ -0,0 +1,434 @@
|
||||
#!/usr/bin/env python3
|
||||
"""kis_quant_db 전 테이블·전 컬럼 MariaDB COMMENT 채우기 (타입·값 변경 없음).
|
||||
|
||||
우선순위:
|
||||
1) 기존 COMMENT 유지
|
||||
2) live_config_schema label+hint (env/config 키)
|
||||
3) 공통 컬럼명 사전 + 테이블 문맥
|
||||
4) 키/컬럼명 휴리스틱
|
||||
|
||||
실행:
|
||||
cd /home/hoon/kis_bot && python3 scripts/sql/fill_all_db_column_comments.py --apply
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Tuple
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
# 공통 컬럼명 → 설명 (테이블 무관)
|
||||
_COL_EXACT: Dict[str, str] = {
|
||||
"id": "PK · AUTO_INCREMENT",
|
||||
"code": "종목코드 (KR 6자리 / US 티커)",
|
||||
"name": "종목명",
|
||||
"strategy": "전략 ID (SHORT/MOMENTUM/BREAKOUT/US_MOMENTUM 등)",
|
||||
"strategy_id": "전략 ID",
|
||||
"avg_buy_price": "평균 매수가",
|
||||
"current_price": "최근 평가/현재가 스냅샷",
|
||||
"stop_price": "손절가",
|
||||
"target_price": "목표가",
|
||||
"max_price": "보유 중 고점(트레일·어깨컷용)",
|
||||
"atr_entry": "진입 시점 ATR",
|
||||
"target_qty": "목표 수량",
|
||||
"current_qty": "현재 보유 수량",
|
||||
"total_invested": "총 투자금액",
|
||||
"status": "상태",
|
||||
"buy_date": "매수 시각/일자",
|
||||
"sell_date": "매도 시각/일자",
|
||||
"updated_at": "마지막 갱신 시각",
|
||||
"created_at": "생성 시각",
|
||||
"size_class": "사이즈 등급(소/중/대 등)",
|
||||
"rsi": "진입/기록 시점 RSI",
|
||||
"volume_ratio": "거래량 배수(기록 스냅샷)",
|
||||
"tail_length_pct": "꼬리 길이%(기록 스냅샷)",
|
||||
"ma5_gap_pct": "MA5 괴리%(기록 스냅샷)",
|
||||
"ma20_gap_pct": "MA20 괴리%(기록 스냅샷)",
|
||||
"foreign_net_buy": "외국인 순매수(기록 스냅샷)",
|
||||
"institution_net_buy": "기관 순매수(기록 스냅샷)",
|
||||
"market_hour": "장중 시각 메타",
|
||||
"buy_price": "매수가",
|
||||
"sell_price": "매도가",
|
||||
"qty": "수량(주)",
|
||||
"profit_rate": "수익률(비율)",
|
||||
"realized_pnl": "실현손익",
|
||||
"hold_minutes": "보유 분",
|
||||
"sell_reason": "매도 사유",
|
||||
"env_snapshot": "체결 시점 env/파라미터 JSON 스냅샷",
|
||||
"ord_no": "증권사 주문번호",
|
||||
"side": "매수/매도 구분",
|
||||
"price": "주문/체결 가격",
|
||||
"filled_qty": "체결 수량",
|
||||
"filled_avg_price": "체결 평균가",
|
||||
"msg_cd": "응답 코드",
|
||||
"msg1": "응답 메시지",
|
||||
"ord_date": "주문일",
|
||||
"submitted_at": "주문 제출 시각",
|
||||
"filled_at": "체결 시각",
|
||||
"raw_json": "원본 응답 JSON",
|
||||
"market": "시장 구분 (KR|US 등)",
|
||||
"exchange": "거래소 (NASD/NYSE/AMEX/KRX 등)",
|
||||
"symbol": "심볼/티커",
|
||||
"tick_time": "틱 시각 YYYYMMDDHHMMSS",
|
||||
"candle_time": "봉 시각 YYYYMMDDHHMM",
|
||||
"snap_time": "스냅샷 시각 YYYYMMDDHHMMSS",
|
||||
"volume": "거래량(체결량/봉거래량)",
|
||||
"tick_seq": "틱 일련번호",
|
||||
"session": "세션 태그",
|
||||
"currency": "통화 (KRW|USD)",
|
||||
"source": "데이터 소스 (kis/kiwoom/ls 등)",
|
||||
"recv_ts": "수신 시각",
|
||||
"timeframe": "분봉 주기(분)",
|
||||
"tf_min": "분봉 주기(분)",
|
||||
"open": "시가",
|
||||
"high": "고가",
|
||||
"low": "저가",
|
||||
"close": "종가",
|
||||
"is_confirmed": "봉 확정 여부 (1=확정)",
|
||||
"holding_peak": "보유 중 트레일 고점(WS 틱 max_price 스냅샷)",
|
||||
"best_bid": "최우선 매수호가",
|
||||
"best_ask": "최우선 매도호가",
|
||||
"total_bid_qty": "총 매수잔량",
|
||||
"total_ask_qty": "총 매도잔량",
|
||||
"bid_qty_l3": "매수 1~3호가 잔량합",
|
||||
"ask_qty_l3": "매도 1~3호가 잔량합",
|
||||
"levels_json": "호가 단계 JSON",
|
||||
"enabled": "활성 여부 (1=ON)",
|
||||
"note": "메모",
|
||||
"market_type": "시장유형 (KR|US)",
|
||||
"k": "키",
|
||||
"v": "값",
|
||||
"env_key": "설정 키",
|
||||
"env_value": "설정 값",
|
||||
"score": "점수/랭크",
|
||||
"scan_time": "스캔 시각",
|
||||
"slot_key": "슬롯/히스토리 키",
|
||||
"sector": "섹터",
|
||||
"theme": "테마",
|
||||
}
|
||||
|
||||
_TABLE_SCOPE: Dict[str, str] = {
|
||||
"active_trades": "실보유",
|
||||
"active_trades_paper": "페이퍼보유",
|
||||
"orders": "실주문",
|
||||
"orders_paper": "페이퍼주문",
|
||||
"trade_history": "실청산이력",
|
||||
"trade_history_paper": "페이퍼청산이력",
|
||||
"ws_ticks": "국장틱",
|
||||
"ws_ticks_us": "해외틱",
|
||||
"ws_candles": "분봉",
|
||||
"ws_orderbook": "호가스냅",
|
||||
"ws_program": "프로그램매매",
|
||||
"ls_ws_ticks": "LS틱",
|
||||
"ls_ws_candles": "LS분봉",
|
||||
"ls_ws_orderbook": "LS호가",
|
||||
"ls_ws_vi": "LS VI",
|
||||
"permanent_subscriptions": "영구구독",
|
||||
"target_candidates": "현재후보",
|
||||
"target_candidates_history": "후보이력",
|
||||
"ls_candidates_history": "LS후보이력",
|
||||
"ls_universe_history": "LS유니버스이력",
|
||||
}
|
||||
|
||||
_TOKEN_KO: List[Tuple[str, str]] = [
|
||||
("STRATEGY", "전략"),
|
||||
("STOP_LOSS", "손절"),
|
||||
("TAKE_PROFIT", "익절"),
|
||||
("TRAIL", "트레일"),
|
||||
("SHOULDER", "어깨"),
|
||||
("RATCHET", "래칫"),
|
||||
("ORDERBOOK", "호가"),
|
||||
("PROGRAM", "프로그램"),
|
||||
("WHIPSAW", "휩쏘"),
|
||||
("ENABLED", "ON/OFF"),
|
||||
("HOLDING_PEAK", "보유고점"),
|
||||
("IS_CONFIRMED", "봉확정"),
|
||||
("CANDLE_TIME", "봉시각"),
|
||||
("TICK_TIME", "틱시각"),
|
||||
("SNAP_TIME", "스냅시각"),
|
||||
("BEST_BID", "최우선매수"),
|
||||
("BEST_ASK", "최우선매도"),
|
||||
("TOTAL_BID", "총매수잔량"),
|
||||
("TOTAL_ASK", "총매도잔량"),
|
||||
("LEVELS_JSON", "호가단계JSON"),
|
||||
("RAW_JSON", "원본JSON"),
|
||||
("FILLED_AVG", "체결평균"),
|
||||
("FILLED_QTY", "체결수량"),
|
||||
("FILLED_AT", "체결시각"),
|
||||
("SUBMITTED", "제출"),
|
||||
("REALIZED_PNL", "실현손익"),
|
||||
("PROFIT_RATE", "수익률"),
|
||||
("HOLD_MINUTES", "보유분"),
|
||||
("SELL_REASON", "매도사유"),
|
||||
("ENV_SNAPSHOT", "env스냅샷"),
|
||||
("AVG_BUY", "평균매수"),
|
||||
("CURRENT_QTY", "현재수량"),
|
||||
("TARGET_QTY", "목표수량"),
|
||||
("CURRENT_PRICE", "현재가"),
|
||||
("STOP_PRICE", "손절가"),
|
||||
("TARGET_PRICE", "목표가"),
|
||||
("MAX_PRICE", "고점"),
|
||||
("MARKET_TYPE", "시장유형"),
|
||||
("TIMEFRAME", "분봉주기"),
|
||||
("TF_MIN", "분봉분"),
|
||||
("RECV_TS", "수신시각"),
|
||||
("TICK_SEQ", "틱일련"),
|
||||
("CURRENCY", "통화"),
|
||||
("EXCHANGE", "거래소"),
|
||||
("SESSION", "세션"),
|
||||
("VOLUME", "거래량"),
|
||||
("SOURCE", "소스"),
|
||||
("STATUS", "상태"),
|
||||
("SIDE", "매매방향"),
|
||||
("PRICE", "가격"),
|
||||
("QTY", "수량"),
|
||||
("CODE", "종목코드"),
|
||||
("NAME", "종목명"),
|
||||
("SYMBOL", "심볼"),
|
||||
("MARKET", "시장"),
|
||||
("RSI", "RSI"),
|
||||
("EMA", "EMA"),
|
||||
("ATR", "ATR"),
|
||||
("PCT", "%"),
|
||||
("SEC", "초"),
|
||||
("MS", "ms"),
|
||||
("JSON", "JSON"),
|
||||
]
|
||||
|
||||
|
||||
def _esc_comment(s: str) -> str:
|
||||
s = re.sub(r"\s+", " ", (s or "").strip())
|
||||
s = s.replace("\\", "\\\\").replace("'", "''")
|
||||
s = s.replace("%", "%%")
|
||||
if len(s) > 240:
|
||||
s = s[:237] + "..."
|
||||
return s
|
||||
|
||||
|
||||
def _heuristic(col: str, table: str) -> str:
|
||||
if col in _COL_EXACT:
|
||||
base = _COL_EXACT[col]
|
||||
scope = _TABLE_SCOPE.get(table)
|
||||
if scope and scope not in base:
|
||||
return f"{scope}: {base}"
|
||||
return base
|
||||
# rsi_14 / ema_9 style
|
||||
m = re.match(r"^(rsi|ema)_(\d+)$", col, re.I)
|
||||
if m:
|
||||
return f"{m.group(1).upper()}({m.group(2)}) materialized"
|
||||
k = col.upper()
|
||||
parts: List[str] = []
|
||||
rest = k
|
||||
while rest:
|
||||
matched = False
|
||||
for tok, ko in _TOKEN_KO:
|
||||
if rest.startswith(tok):
|
||||
parts.append(ko)
|
||||
rest = rest[len(tok):]
|
||||
if rest.startswith("_"):
|
||||
rest = rest[1:]
|
||||
matched = True
|
||||
break
|
||||
if not matched:
|
||||
if "_" in rest:
|
||||
chunk, rest = rest.split("_", 1)
|
||||
else:
|
||||
chunk, rest = rest, ""
|
||||
parts.append(chunk)
|
||||
body = " ".join(parts) if parts else col
|
||||
scope = _TABLE_SCOPE.get(table, table)
|
||||
return f"{scope}: {body} ({col})"
|
||||
|
||||
|
||||
def _load_live_labels() -> Dict[str, str]:
|
||||
out: Dict[str, str] = {}
|
||||
try:
|
||||
from kis_trader.web.live_config_schema import build_live_config_groups
|
||||
except Exception:
|
||||
return out
|
||||
for g in build_live_config_groups():
|
||||
for f in g.get("fields") or []:
|
||||
k = f.get("key")
|
||||
if not k:
|
||||
continue
|
||||
lab = str(f.get("label") or "").strip()
|
||||
hint = str(f.get("hint") or "").strip()
|
||||
bits = [b for b in (lab, hint) if b]
|
||||
if bits:
|
||||
out[str(k)] = " · ".join(bits)
|
||||
return out
|
||||
|
||||
|
||||
def _describe(col: str, table: str, labels: Dict[str, str], existing: str) -> str:
|
||||
if (existing or "").strip():
|
||||
return existing.strip()
|
||||
if col in labels:
|
||||
return labels[col]
|
||||
return _heuristic(col, table)
|
||||
|
||||
|
||||
def _col_type_clause(row: dict) -> str:
|
||||
typ = row["Type"]
|
||||
null = "NULL" if row["Null"] == "YES" else "NOT NULL"
|
||||
default = row["Default"]
|
||||
extra = row.get("Extra") or ""
|
||||
parts = [typ, null]
|
||||
if default is None:
|
||||
if row["Null"] == "YES":
|
||||
parts.append("DEFAULT NULL")
|
||||
else:
|
||||
draw = str(default).strip()
|
||||
dnorm = draw.upper().replace(" ", "")
|
||||
# MariaDB SHOW 가 current_timestamp() 문자열로 주는 경우
|
||||
if dnorm in (
|
||||
"CURRENT_TIMESTAMP",
|
||||
"CURRENT_TIMESTAMP()",
|
||||
"NOW()",
|
||||
"CURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP",
|
||||
) or dnorm.startswith("CURRENT_TIMESTAMP"):
|
||||
# ON UPDATE 는 Extra 에 있을 수 있음
|
||||
parts.append("DEFAULT CURRENT_TIMESTAMP")
|
||||
if "on update" in extra.lower():
|
||||
parts.append("ON UPDATE CURRENT_TIMESTAMP")
|
||||
elif re.match(
|
||||
r"^(tinyint|smallint|mediumint|int|bigint|decimal|float|double|bit)",
|
||||
typ,
|
||||
re.I,
|
||||
):
|
||||
parts.append(f"DEFAULT {draw}")
|
||||
else:
|
||||
d = draw.replace("\\", "\\\\").replace("'", "''")
|
||||
parts.append(f"DEFAULT '{d}'")
|
||||
if "auto_increment" in extra.lower():
|
||||
parts.append("AUTO_INCREMENT")
|
||||
return " ".join(parts)
|
||||
|
||||
|
||||
def _list_tables(db) -> List[str]:
|
||||
rows = db.conn.execute(
|
||||
"SELECT TABLE_NAME AS t FROM information_schema.TABLES "
|
||||
"WHERE TABLE_SCHEMA=DATABASE() AND TABLE_TYPE='BASE TABLE' "
|
||||
"ORDER BY TABLE_NAME"
|
||||
).fetchall()
|
||||
return [r["t"] for r in rows]
|
||||
|
||||
|
||||
def _sync_erd(db, tables: List[str]) -> int:
|
||||
path = ROOT / "docs/like_mcp.md/db_erd.md"
|
||||
if not path.exists():
|
||||
return 0
|
||||
comments = {}
|
||||
for t in tables:
|
||||
for r in db.conn.execute(f"SHOW FULL COLUMNS FROM `{t}`").fetchall():
|
||||
comments[(t, r["Field"])] = (r.get("Comment") or "").strip()
|
||||
text = path.read_text(encoding="utf-8")
|
||||
current = None
|
||||
out = []
|
||||
row_re = re.compile(
|
||||
r"^\| `([^`]+)`( 🔑)? \| `([^`]+)` \| ([^|]*) \| ([^|]*) \| (.*) \|$"
|
||||
)
|
||||
n_upd = 0
|
||||
for line in text.splitlines():
|
||||
m = re.match(r"^#### `([^`]+)`\s*$", line)
|
||||
if m:
|
||||
current = m.group(1)
|
||||
out.append(line)
|
||||
continue
|
||||
m = row_re.match(line)
|
||||
if m and current:
|
||||
col = m.group(1)
|
||||
key = (current, col)
|
||||
if key in comments and comments[key]:
|
||||
cmt = comments[key].replace("|", "/")
|
||||
keymark = m.group(2) or ""
|
||||
typ = m.group(3)
|
||||
nul = m.group(4).strip()
|
||||
dflt = m.group(5).strip()
|
||||
newline = f"| `{col}`{keymark} | `{typ}` | {nul} | {dflt} | {cmt} |"
|
||||
if newline != line:
|
||||
n_upd += 1
|
||||
out.append(newline)
|
||||
continue
|
||||
out.append(line)
|
||||
path.write_text("\n".join(out) + "\n", encoding="utf-8")
|
||||
return n_upd
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--apply", action="store_true", default=True)
|
||||
ap.add_argument("--sql-only", action="store_true")
|
||||
ap.add_argument(
|
||||
"--out",
|
||||
default=str(ROOT / "scripts/sql/all_db_column_comments.generated.sql"),
|
||||
)
|
||||
ap.add_argument("--no-erd", action="store_true")
|
||||
args = ap.parse_args()
|
||||
if args.sql_only:
|
||||
args.apply = False
|
||||
|
||||
from database import TradeDB
|
||||
|
||||
db = TradeDB()
|
||||
labels = _load_live_labels()
|
||||
tables = _list_tables(db)
|
||||
print(f"tables={len(tables)} live_labels={len(labels)}")
|
||||
|
||||
sql_chunks: List[str] = [
|
||||
"-- AUTO-GENERATED: 전 DB 테이블 컬럼 COMMENT (타입·값 불변)",
|
||||
"-- python3 scripts/sql/fill_all_db_column_comments.py --apply",
|
||||
"",
|
||||
]
|
||||
filled = kept = 0
|
||||
for table in tables:
|
||||
rows = db.conn.execute(f"SHOW FULL COLUMNS FROM `{table}`").fetchall()
|
||||
mods = []
|
||||
for r in rows:
|
||||
field = r["Field"]
|
||||
existing = (r.get("Comment") or "").strip()
|
||||
desc = _describe(field, table, labels, existing)
|
||||
if existing:
|
||||
kept += 1
|
||||
else:
|
||||
filled += 1
|
||||
type_clause = _col_type_clause(r)
|
||||
cmt = _esc_comment(desc)
|
||||
mods.append(
|
||||
f" MODIFY COLUMN `{field}`\n {type_clause}\n COMMENT '{cmt}'"
|
||||
)
|
||||
if args.apply and existing != desc:
|
||||
stmt = (
|
||||
f"ALTER TABLE `{table}` MODIFY COLUMN `{field}` "
|
||||
f"{type_clause} COMMENT '{cmt}'"
|
||||
)
|
||||
db.conn.execute(stmt)
|
||||
sql_chunks.append(f"-- ── {table} ({len(rows)} cols) ──")
|
||||
sql_chunks.append(f"ALTER TABLE `{table}`")
|
||||
sql_chunks.append(",\n".join(mods) + ";\n")
|
||||
print(f"{'applied' if args.apply else 'planned'} {table} ({len(rows)})")
|
||||
|
||||
out = Path(args.out)
|
||||
out.write_text("\n".join(sql_chunks) + "\n", encoding="utf-8")
|
||||
print(f"wrote {out} ({out.stat().st_size} bytes)")
|
||||
print(f"kept_existing={kept} newly_filled={filled}")
|
||||
|
||||
if args.apply:
|
||||
empty = 0
|
||||
for table in tables:
|
||||
for r in db.conn.execute(f"SHOW FULL COLUMNS FROM `{table}`").fetchall():
|
||||
if not (r.get("Comment") or "").strip():
|
||||
empty += 1
|
||||
print("EMPTY", table, r["Field"])
|
||||
print("remaining empty:", empty)
|
||||
if not args.no_erd:
|
||||
n = _sync_erd(db, tables)
|
||||
print(f"db_erd.md rows updated: {n}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
116
scripts/sql/token_session_strategy_column_comments.sql
Normal file
116
scripts/sql/token_session_strategy_column_comments.sql
Normal file
@@ -0,0 +1,116 @@
|
||||
-- 토큰·세션·전략 ON/OFF 관련 컬럼 COMMENT (선별·정밀 문구)
|
||||
-- 전 컬럼(config_*/env_config ~1200+)은 아래로 일괄:
|
||||
-- python3 scripts/sql/fill_all_config_env_column_comments.py --apply
|
||||
-- → scripts/sql/all_config_env_column_comments.generated.sql
|
||||
-- 참고: docs/증권사_토큰_시세.md · kis_ws_session_windows · ls_ws_session_windows
|
||||
-- 실행: python 또는 mysql 클라이언트. pymysql 사용 시 COMMENT 안 % → %% .
|
||||
|
||||
-- ── config_* : STRATEGY_*_ENABLED ─────────────────────────────────────
|
||||
ALTER TABLE `config_us_momentum`
|
||||
MODIFY COLUMN `STRATEGY_US_MOMENTUM_ENABLED`
|
||||
TEXT NULL
|
||||
COMMENT '해외모멘텀 ON/OFF. 다음루프 신규매수중단(보유청산유지). WS구독해제 안함. 기동시OFF→켜려면재시작';
|
||||
|
||||
ALTER TABLE `config_momentum`
|
||||
MODIFY COLUMN `STRATEGY_MOMENTUM_ENABLED`
|
||||
TEXT NULL
|
||||
COMMENT '국내모멘텀 ON/OFF. 다음루프 신규매수중단(보유청산유지). WS구독해제 안함. 기동시OFF→켜려면재시작';
|
||||
|
||||
ALTER TABLE `config_short`
|
||||
MODIFY COLUMN `STRATEGY_SHORT_ENABLED`
|
||||
TEXT NULL
|
||||
COMMENT '꼬리잡기 ON/OFF. 다음루프 신규매수중단(보유청산유지). WS구독해제 안함. 기동시OFF→켜려면재시작';
|
||||
|
||||
ALTER TABLE `config_breakout`
|
||||
MODIFY COLUMN `STRATEGY_BREAKOUT_ENABLED`
|
||||
TEXT NULL
|
||||
COMMENT '돌파 ON/OFF. 다음루프 신규매수중단(보유청산유지). WS구독해제 안함. 기동시OFF→켜려면재시작';
|
||||
|
||||
ALTER TABLE `config_updow`
|
||||
MODIFY COLUMN `STRATEGY_UPDOW_ENABLED`
|
||||
TEXT NULL
|
||||
COMMENT 'UPDOWN박스 ON/OFF. 다음루프 신규매수중단(보유청산유지). WS구독해제 안함. 기동시OFF→켜려면재시작';
|
||||
|
||||
ALTER TABLE `config_scalp`
|
||||
MODIFY COLUMN `STRATEGY_SCALP_ENABLED`
|
||||
TEXT NULL
|
||||
COMMENT '스캘핑 ON/OFF. 다음루프 신규매수중단(보유청산유지). WS구독해제 안함. 기동시OFF→켜려면재시작';
|
||||
|
||||
ALTER TABLE `config_range_break`
|
||||
MODIFY COLUMN `STRATEGY_RANGE_BREAK_ENABLED`
|
||||
TEXT NULL
|
||||
COMMENT '박스권돌파 ON/OFF. 다음루프 신규매수중단(보유청산유지). WS구독해제 안함';
|
||||
|
||||
ALTER TABLE `config_dbband`
|
||||
MODIFY COLUMN `STRATEGY_DBBAND_ENABLED`
|
||||
TEXT NULL
|
||||
COMMENT '더블BB ON/OFF. 다음루프 신규매수중단(보유청산유지). WS구독해제 안함';
|
||||
|
||||
ALTER TABLE `config_dart`
|
||||
MODIFY COLUMN `STRATEGY_DART_ENABLED`
|
||||
TEXT NULL
|
||||
COMMENT 'DART수주 ON/OFF. 다음루프 신규매수중단(보유청산유지). WS구독해제 안함';
|
||||
|
||||
-- ── env_config : KIS approval·hold · 키움 구독한도 ─────────────────────
|
||||
ALTER TABLE `env_config`
|
||||
MODIFY COLUMN `KIS_WS_KR_HOLD_START_HM`
|
||||
TEXT NULL
|
||||
COMMENT '국내WS approval점유 시작 HHMM. 기본700. 갭·해외창에선 소켓양보(재발급없음)'
|
||||
|
||||
, MODIFY COLUMN `KIS_WS_KR_HOLD_END_HM`
|
||||
TEXT NULL
|
||||
COMMENT '국내WS approval점유 종료 HHMM. 기본2000. kis_ws_session_windows'
|
||||
|
||||
, MODIFY COLUMN `KIS_WS_US_HOLD_START_HM`
|
||||
TEXT NULL
|
||||
COMMENT '해외WS(HDFSCNT0) 점유 시작 HHMM. 기본2100. 국내와 동시점유금지(1키=1세션)'
|
||||
|
||||
, MODIFY COLUMN `KIS_WS_US_HOLD_END_HM`
|
||||
TEXT NULL
|
||||
COMMENT '해외WS 점유 종료 HHMM. 기본600(자정넘김). 세션전환시 approval재발급금지'
|
||||
|
||||
, MODIFY COLUMN `KIS_WS_APPROVAL_MIN_REISSUE_SEC`
|
||||
TEXT NULL
|
||||
COMMENT 'approval_key 최소재발급간격(초). 기본21600=6h. REST재발급은기존키무효화'
|
||||
|
||||
, MODIFY COLUMN `KIS_WS_APPROVAL_MAX_VALID_SEC`
|
||||
TEXT NULL
|
||||
COMMENT 'approval_key 최대유효(초). 기본82800≈23h. 파일캐시 kis_approval_manager'
|
||||
|
||||
, MODIFY COLUMN `KIS_WS_INVALID_APPROVAL_REISSUE_AFTER`
|
||||
TEXT NULL
|
||||
COMMENT 'invalid approval 연속N회 후 응급재발급 허용. 6h가드우회조건'
|
||||
|
||||
, MODIFY COLUMN `KIS_WS_INVALID_APPROVAL_BYPASS_6H`
|
||||
TEXT NULL
|
||||
COMMENT 'true면 invalid루프시 6h가드우회응급재발급. 남용금지'
|
||||
|
||||
, MODIFY COLUMN `KIS_WS_EMERGENCY_REISSUE_MIN_SEC`
|
||||
TEXT NULL
|
||||
COMMENT '응급approval재발급 최소간격(초). 기본=정상6h와동일. 하드캡'
|
||||
|
||||
, MODIFY COLUMN `KIWOOM_WS_MAX_SUBSCRIPTIONS`
|
||||
TEXT NULL
|
||||
COMMENT '키움WS 그룹당최대구독. 기본100. 0B체결·0D호가·0w프로그램. 한투41과별개'
|
||||
;
|
||||
|
||||
-- ── env_config_ext : KV 테이블 자체 설명 (키별 행은 컬럼 아님) ───────────
|
||||
ALTER TABLE `env_config_ext`
|
||||
COMMENT='env_config 확장 KV. LS_WS_*·LS_TOKEN_*·OVERSEAS_WS_ENABLED·워치독/hold 등 다수키. 컬럼추가없이 env_key행'
|
||||
|
||||
, MODIFY COLUMN `env_key`
|
||||
VARCHAR(80) NOT NULL
|
||||
COMMENT '설정키. 예: LS_WS_WATCHDOG_SESSION_GATE, LS_WS_KR_HOLD_*, OVERSEAS_WS_ENABLED, LS_TOKEN_*'
|
||||
|
||||
, MODIFY COLUMN `env_value`
|
||||
TEXT NOT NULL
|
||||
COMMENT '설정값 문자열. bool/int도 텍스트저장'
|
||||
|
||||
, MODIFY COLUMN `updated_at`
|
||||
VARCHAR(30) NOT NULL
|
||||
COMMENT '마지막갱신시각'
|
||||
;
|
||||
|
||||
ALTER TABLE `config_us_momentum`
|
||||
COMMENT='해외모멘텀 US_MOMENTUM_* · STRATEGY_US_MOMENTUM_ENABLED. 시세=한투HDFSCNT0 · 유니버스=permanent US'
|
||||
;
|
||||
Reference in New Issue
Block a user