feat(stock-lab): position_sizer — ATR Wilder + entry/stop/target
This commit is contained in:
51
stock-lab/app/screener/position_sizer.py
Normal file
51
stock-lab/app/screener/position_sizer.py
Normal file
@@ -0,0 +1,51 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user