전 봉 BB·일목 조합 분석 및 simulation 단일 실행으로 통합

9개 간격(1~1440분) BB·일목 위치 특징을 3분 타임라인에 맞춰 분석하고,
discover로 매수·매도 규칙을 찾은 뒤 HTML 차트에 해당 체결만 표시한다.
simulation_1h.py를 simulation.py로 변경했으며, 파라미터 없이 실행하면
analyze→discover→차트가 한 번에 수행된다.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dsyoon
2026-05-29 01:20:36 +09:00
parent 7d53090034
commit e218a8ea32
15 changed files with 1510 additions and 803 deletions

View File

@@ -1,7 +1,7 @@
"""
모든 봉·캔들 특징 행렬에서 매수/매도 규칙을 탐색합니다 (인과적 백테스트).
python simulation_1h.py discover
python simulation.py discover
"""
from __future__ import annotations
@@ -46,6 +46,9 @@ NEG_BLOCK_FEATURES: tuple[str, ...] = (
"above_upper",
"cross_down_lower",
"shooting_star",
"ichi_above_cloud",
"bb_zone_top",
"bb_pos_high",
)
# 탐색 규칙 적용 시 항상 매수 차단 (상단 돌파·과열)
@@ -238,7 +241,7 @@ def backtest_rules(
"""
HTML 시뮬과 동일한 run_backtest 로직으로 수익률 계산.
"""
from simulation_1h import run_backtest
from simulation import run_backtest
import strategy as st
df = entry_ohlc.loc[matrix.index].copy()
@@ -261,12 +264,39 @@ def backtest_rules(
def _baseline_rules() -> DiscoveredRules:
"""다봉 BB 하단 돌파 + 상단 돌파 기준선."""
p3 = interval_prefix(ENTRY_INTERVAL)
return DiscoveredRules(
name="baseline_bb",
buy_all=[f"{p3}:cross_up_lower"],
sell_all=[f"{p3}:cross_up_upper"],
sell_stop=[],
name="baseline_bb_mtf",
buy_all=[
f"{p3}:cross_up_lower",
f"{interval_prefix(60)}:ichi_tk_bull",
f"{interval_prefix(1440)}:!ichi_below_cloud",
],
sell_all=[
f"{p3}:cross_up_upper",
f"{interval_prefix(60)}:cross_up_upper",
],
sell_stop=[f"{p3}:cross_down_lower"],
)
def _seed_from_combination_report() -> DiscoveredRules | None:
"""combination_report.json 제안 규칙."""
path = Path(__file__).parent / "combination_report.json"
if not path.exists():
return None
data = json.loads(path.read_text(encoding="utf-8"))
sug = data.get("suggested_rules") or {}
buy_all = list(sug.get("buy_all") or [])
if not buy_all:
return None
return DiscoveredRules(
name="combination_seed",
buy_all=buy_all,
buy_any=[list(g) for g in (sug.get("buy_any") or []) if g],
sell_all=list(sug.get("sell_all") or [f"{interval_prefix(ENTRY_INTERVAL)}:cross_up_upper"]),
sell_stop=list(sug.get("sell_stop") or []),
)
@@ -278,8 +308,8 @@ def greedy_search(
df_1d: pd.DataFrame,
df_1h: pd.DataFrame,
entry_ohlc: pd.DataFrame,
max_buy: int = 5,
max_sell: int = 4,
max_buy: int = 8,
max_sell: int = 6,
max_stop: int = 2,
) -> DiscoveredRules:
"""학습 구간 수익률을 올리도록 매수/매도 조건을 탐욕적으로 확장."""
@@ -375,7 +405,14 @@ def try_buy_any_branches(
) -> DiscoveredRules:
"""매수 OR 분기: 다른 봉의 cross_up_lower / hammer 등."""
train = matrix.iloc[:train_end]
triggers = [p for p in pool if p.endswith(":cross_up_lower") or p.endswith(":hammer")]
triggers = [
p
for p in pool
if p.endswith(":cross_up_lower")
or p.endswith(":hammer")
or p.endswith(":bb_zone_bottom")
or p.endswith(":ichi_tk_cross_up")
]
best = DiscoveredRules(
name=base.name,
buy_all=list(base.buy_all),
@@ -476,8 +513,9 @@ def discover_rules(frames: dict[int, pd.DataFrame]) -> DiscoveredRules:
pool = generate_predicate_pool(intervals)
print(f" 샘플 {n}봉 | 학습 {train_end} | predicate 후보 {len(pool)}")
baseline = _baseline_rules()
baseline = _seed_from_combination_report() or _baseline_rules()
br, bt = backtest_rules(matrix.iloc[:train_end], baseline, df_1d, df_1h, entry_ohlc)
print(f" 시드 규칙: {baseline.name}")
bf, _ = backtest_rules(matrix, baseline, df_1d, df_1h, entry_ohlc)
print(f" 기준선(3분 BB만): 학습 {br:+.2f}% | 전체 {bf:+.2f}%")