"""ATR Wilder smoothing + entry/stop/target 계산.""" import pandas as pd def compute_atr_wilder(df_one_ticker: pd.DataFrame, window: int = 14) -> float: """단일 종목 DataFrame(date·open·high·low·close)에 대해 Wilder ATR 마지막 값.""" g = df_one_ticker.sort_values("date").copy() high = g["high"].astype(float) low = g["low"].astype(float) close = g["close"].astype(float) prev_close = close.shift(1) tr = pd.concat([ (high - low), (high - prev_close).abs(), (low - prev_close).abs(), ], axis=1).max(axis=1) atr = tr.ewm(alpha=1 / window, adjust=False).mean() return float(atr.iloc[-1]) def round_won(x: float) -> int: return int(round(x)) def plan_positions(ctx, tickers: list, params: dict) -> dict: """각 ticker 에 대해 entry/stop/target/atr14 반환.""" atr_window = int(params.get("atr_window", 14)) stop_mult = float(params.get("atr_stop_mult", 2.0)) rr = float(params.get("rr_ratio", 2.0)) prices = ctx.prices.sort_values("date") out: dict = {} for t in tickers: sub = prices[prices["ticker"] == t] if sub.empty: continue close = float(sub["close"].iloc[-1]) atr14 = compute_atr_wilder(sub, window=atr_window) entry = round_won(close * 1.005) stop = round_won(close - stop_mult * atr14) target = round_won(entry + rr * (entry - stop)) r_pct = (entry - stop) / entry * 100 if entry else 0.0 out[t] = { "entry_price": entry, "stop_price": stop, "target_price": target, "atr14": atr14, "r_pct": r_pct, } return out