feat: Enhance trading system with new e_min_chg_pct parameter and related logic
Changes: - Introduced the `e_min_chg_pct` parameter to define the minimum price change percentage compared to the previous day's close, enhancing the momentum trading strategy. - Updated various functions and classes to incorporate this new parameter, ensuring it is utilized in both backtesting and live trading scenarios. - Improved documentation and comments to clarify the purpose and usage of the new parameter across the codebase. Impact: - This addition allows for more precise control over trading conditions, potentially increasing the effectiveness of the momentum strategy while maintaining system integrity and performance.
This commit is contained in:
@@ -121,6 +121,8 @@ def get_momentum_defaults_from_db(db=None, *, env_row: Optional[Dict[str, Any]]
|
||||
mom_vol_mult = momentum_env_float(r, "MOMENTUM_VOL_MULT", 1.05)
|
||||
mom_vol_win = momentum_env_int(r, "MOMENTUM_VOL_WIN", 5)
|
||||
mom_time_end = momentum_env_int(r, "MOMENTUM_TIME_END_HM", 1530)
|
||||
# HTS K: 전일 종가 대비 최소 등락률(%). 0=단순 종가 초과만. HTS 기본 0.2.
|
||||
e_min_chg_pct = momentum_env_float(r, "MOMENTUM_E_MIN_CHG_PCT", 0.2)
|
||||
sl_pct = abs(_legacy_float(
|
||||
r, "MOMENTUM_STOP_LOSS_PCT", ("SCALP_STOP_LOSS_PCT",), 0.03,
|
||||
))
|
||||
@@ -184,6 +186,7 @@ def get_momentum_defaults_from_db(db=None, *, env_row: Optional[Dict[str, Any]]
|
||||
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
|
||||
e_min_chg_pct = 0.2
|
||||
sl_pct, tp_pct, tp_max = 0.03, 0.08, 0.10
|
||||
shoulder_high, shoulder_cut = 0.005, 0.003
|
||||
ratchet_tiers, trail_pct, trail_arm_pct = "", 0.02, 0.01
|
||||
@@ -216,6 +219,7 @@ def get_momentum_defaults_from_db(db=None, *, env_row: Optional[Dict[str, Any]]
|
||||
"mom_vol_mult": mom_vol_mult,
|
||||
"mom_vol_win": mom_vol_win,
|
||||
"mom_time_end_hm": mom_time_end,
|
||||
"e_min_chg_pct": e_min_chg_pct,
|
||||
"mom_max_from_open_pct": mom_max_open,
|
||||
"mom_min_from_open_pct": mom_min_open,
|
||||
"sl_pct": sl_pct,
|
||||
|
||||
@@ -42,6 +42,7 @@ MOMENTUM_CONFIG_KEYS = frozenset({
|
||||
"MOMENTUM_TRIGGER_REQUIRE_BULL_BAR",
|
||||
"MOMENTUM_USE_VOL_TRIGGER",
|
||||
"MOMENTUM_USE_RSI_FILTER",
|
||||
"MOMENTUM_E_MIN_CHG_PCT",
|
||||
"MOMENTUM_PATTERN_BREAKOUT",
|
||||
"MOMENTUM_PATTERN_PULLBACK",
|
||||
"MOMENTUM_CHASE_LOOKBACK_MIN",
|
||||
@@ -169,6 +170,8 @@ def params_to_momentum_env_patch(p: Dict[str, Any]) -> Dict[str, str]:
|
||||
_set("MOMENTUM_VOL_MULT", p["mom_vol_mult"])
|
||||
if "mom_vol_win" in p:
|
||||
_set("MOMENTUM_VOL_WIN", p["mom_vol_win"])
|
||||
if "e_min_chg_pct" in p:
|
||||
_set("MOMENTUM_E_MIN_CHG_PCT", p["e_min_chg_pct"])
|
||||
if "mom_time_end_hm" in p:
|
||||
_set("MOMENTUM_TIME_END_HM", int(float(p["mom_time_end_hm"])))
|
||||
if "mom_max_from_open_pct" in p:
|
||||
|
||||
@@ -4,14 +4,12 @@ momentum_hts_logic.py — HTS momentum 조건식(E∧F∧H∧I) 정합 TRIGGER·
|
||||
====================================================================
|
||||
|
||||
[역할 분담 — SCAN vs TRIGGER vs 청산]
|
||||
- SCAN (키움 WS ``momentum`` 조건검색): E∧F∧H∧I 일봉 필터 → target_candidates_history
|
||||
· E: 종가 > 전일 시가
|
||||
· F: 2천~20만원
|
||||
· H: 5일 평균거래량 105% 이상
|
||||
· I: 거래량증감률 상위 400
|
||||
- SCAN (HTS ``momentum``): F∧H∧I∧K 등 → 후보 풀
|
||||
· F: 가격대 · H: 1분 거래량 펄스 · I: 거래량증감 상위(SCAN만)
|
||||
· K: 전일 종가 대비 최소 등락(예 0.2%) — TRIGGER ``e_min_chg_pct`` 와 대응
|
||||
- TRIGGER (본 모듈): SCAN 후 **진입 타이밍**만 검사
|
||||
· ``MOMENTUM_SKIP_HTS_SCAN_DUPES=true`` (kiwoom 기본): E·양봉·분봉거래량·RSI 중복 생략
|
||||
· false: E 유지·양봉·거래량 펄스(선택) — 당일 모멘텀 살아있음 확인
|
||||
· ``MOMENTUM_SKIP_HTS_SCAN_DUPES=true``: K·양봉·분봉거래량 중복 생략
|
||||
· false: 전일종가+e_min_chg · 양봉 · 거래량 펄스(선택)
|
||||
- 청산 (본 모듈): 래칫·어깨·트레일·손절·시간컷 (A안 돌파 추격)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
@@ -45,7 +43,7 @@ def resolve_prev_trading_day_open(
|
||||
day: str,
|
||||
) -> Optional[float]:
|
||||
"""
|
||||
전일(직전 거래일) 시가 — HTS momentum E 조건 ``close > prev_open`` 확인용.
|
||||
전일(직전 거래일) 시가 — 레거시 참고용 (현재 E 조건은 종가 사용).
|
||||
|
||||
1분봉에서 전일 **장 시작 구간** 첫 봉 open = 일봉 시가.
|
||||
전일 오후 봉만 있으면(웜업 부족) 오후 open을 시가로 오인하므로 None 반환.
|
||||
@@ -54,7 +52,6 @@ def resolve_prev_trading_day_open(
|
||||
|
||||
# 전일 시가로 인정할 최대 HHMM.
|
||||
# 기본 1000: 저유동 종목이 09:14 첫체결만 있어도 시가로 인정.
|
||||
# (구 910은 월요 갭 후 전일 오전 봉이 얇으면 전부 '전일시가없음'으로 매수 불가)
|
||||
open_hm_max = max(900, int(get_env_int("MOMENTUM_PREV_DAY_OPEN_HM_MAX", 1000)))
|
||||
|
||||
prev_day: Optional[str] = None
|
||||
@@ -83,17 +80,44 @@ def resolve_prev_trading_day_open(
|
||||
earliest_hm = hm
|
||||
if prev_open is None or prev_open <= 0:
|
||||
return None
|
||||
# 전일 시가 = 정규장 시작 근처 봉이 시리즈에 있어야 함 (오후만 있으면 미확정)
|
||||
if earliest_hm is None or earliest_hm > open_hm_max:
|
||||
return None
|
||||
return prev_open
|
||||
|
||||
|
||||
def resolve_prev_trading_day_close(
|
||||
candles: List[Dict],
|
||||
i: int,
|
||||
day: str,
|
||||
) -> Optional[float]:
|
||||
"""
|
||||
전일(직전 거래일) 종가 — HTS momentum E 조건 ``close > prev_close`` 확인용.
|
||||
|
||||
1분봉 역스캔에서 전일(day 이전) 마지막 봉 close = 일봉 종가.
|
||||
전일 봉이 1개도 없으면 None 반환.
|
||||
"""
|
||||
prev_day: Optional[str] = None
|
||||
prev_close: Optional[float] = None
|
||||
for j in range(i - 1, -1, -1):
|
||||
ct = str(candles[j].get("candle_time", ""))
|
||||
d = ct[:8]
|
||||
if not d or d >= day:
|
||||
continue
|
||||
if prev_day is None:
|
||||
prev_day = d
|
||||
# 역스캔 첫 번째로 만난 전일 봉 = 전일 마지막(최신) 봉 → 종가
|
||||
cl = float(candles[j].get("close", 0) or 0)
|
||||
if cl > 0:
|
||||
prev_close = cl
|
||||
break
|
||||
return prev_close if (prev_close is not None and prev_close > 0) else None
|
||||
|
||||
|
||||
def candles_have_prev_session_open(
|
||||
candles: List[Dict],
|
||||
day: str,
|
||||
) -> bool:
|
||||
"""기간일 ``day`` 기준 직전 거래일 **장시작 시가** 봉이 있는지 (E조건 해석 가능)."""
|
||||
"""기간일 ``day`` 기준 직전 거래일 **장시작 시가** 봉이 있는지 (레거시 호환)."""
|
||||
if not candles:
|
||||
return False
|
||||
d = str(day or "")[:8]
|
||||
@@ -102,6 +126,19 @@ def candles_have_prev_session_open(
|
||||
return resolve_prev_trading_day_open(candles, len(candles) - 1, d) is not None
|
||||
|
||||
|
||||
def candles_have_prev_session_close(
|
||||
candles: List[Dict],
|
||||
day: str,
|
||||
) -> bool:
|
||||
"""기간일 ``day`` 기준 직전 거래일 **종가** 봉이 있는지 (E 조건 해석 가능)."""
|
||||
if not candles:
|
||||
return False
|
||||
d = str(day or "")[:8]
|
||||
if len(d) < 8:
|
||||
return True
|
||||
return resolve_prev_trading_day_close(candles, len(candles) - 1, d) is not None
|
||||
|
||||
|
||||
def _volume_pulse_ok(
|
||||
candles: List[Dict],
|
||||
i: int,
|
||||
@@ -285,7 +322,7 @@ def eval_momentum_hts_buy_at_index(
|
||||
return (None, None, None)
|
||||
|
||||
skip_hts = _to_bool(params.get("skip_hts_scan_dupes"), False)
|
||||
prev_open = resolve_prev_trading_day_open(candles, i, day)
|
||||
prev_close = resolve_prev_trading_day_close(candles, i, day)
|
||||
|
||||
# kiwoom momentum SCAN — E∧F∧H∧I 이미 통과, TRIGGER 는 타이밍·호가·휩쏘만
|
||||
# ※ 해외(US)는 HTS SCAN 이 없으므로 skip_hts 숏컷으로 매 봉 시그널 내면 안 됨
|
||||
@@ -322,7 +359,7 @@ def eval_momentum_hts_buy_at_index(
|
||||
"mode": "us_momentum_chase",
|
||||
"pattern": pat_name or "chase",
|
||||
"signal_candle_time": c.get("candle_time"),
|
||||
"prev_day_open": prev_open,
|
||||
"prev_day_close": prev_close,
|
||||
}
|
||||
if metrics:
|
||||
sig_hts.update(metrics)
|
||||
@@ -335,7 +372,7 @@ def eval_momentum_hts_buy_at_index(
|
||||
"mode": "momentum_hts",
|
||||
"pattern": "momentum_hts_scan",
|
||||
"signal_candle_time": c.get("candle_time"),
|
||||
"prev_day_open": prev_open,
|
||||
"prev_day_close": prev_close,
|
||||
}
|
||||
ws_rej, ws_msg = whipsaw_reject_for_signal(
|
||||
params, "MOMENTUM", signal_bar=c, current_price=cl,
|
||||
@@ -350,14 +387,19 @@ def eval_momentum_hts_buy_at_index(
|
||||
return (prog_rej, prog_msg, None)
|
||||
return (None, None, sig_hts)
|
||||
|
||||
# ── E: 전일 시가 돌파 유지 (HTS momentum 핵심) ─────────────────────
|
||||
# ── E: 전일 종가 대비 최소 등락률 이상 (HTS K 조건 대응)
|
||||
# e_min_chg_pct > 0 이면 "전일 종가 + X%" 이상이어야 진입 (HTS 0.2% = 0.2 입력).
|
||||
# 0.0 = 전일 종가 초과만 확인 (기존 동작).
|
||||
e_min_chg_pct = float(params.get("e_min_chg_pct", 0.0))
|
||||
if trigger_e_confirm:
|
||||
if prev_open is None or prev_open <= 0:
|
||||
return ("탈락-전일시가없음", "전일 시가 미확인", None)
|
||||
if cl <= prev_open:
|
||||
if prev_close is None or prev_close <= 0:
|
||||
return ("탈락-전일종가없음", "전일 종가 미확인", None)
|
||||
threshold = prev_close * (1.0 + e_min_chg_pct / 100.0)
|
||||
if cl < threshold:
|
||||
chg_pct = (cl - prev_close) / prev_close * 100.0
|
||||
return (
|
||||
"탈락-E미충족",
|
||||
"종가 %.0f ≤ 전일시가 %.0f" % (cl, prev_open),
|
||||
"등락 %.2f%% < %.1f%% (종가%.0f 전일종가%.0f)" % (chg_pct, e_min_chg_pct, cl, prev_close),
|
||||
None,
|
||||
)
|
||||
|
||||
@@ -407,7 +449,7 @@ def eval_momentum_hts_buy_at_index(
|
||||
"mode": "momentum_hts",
|
||||
"pattern": "hts_e_confirm",
|
||||
"signal_candle_time": c.get("candle_time"),
|
||||
"prev_day_open": prev_open,
|
||||
"prev_day_close": prev_close,
|
||||
}
|
||||
if rsi_val is not None:
|
||||
sig["rsi"] = rsi_val
|
||||
|
||||
Reference in New Issue
Block a user