feat(stock): market_events (급변·거래량Z·외인순매도)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -124,6 +124,54 @@ def exit_rules(holding: dict, ticker_prices: "pd.DataFrame", params: dict) -> di
|
||||
ADD_SCORE = 70.0 # 이 이상이면 추가매수 후보
|
||||
|
||||
|
||||
# ---- Task 3.1: market_events ----
|
||||
|
||||
_DEFAULT_EVENT_PARAMS = {"move_pct": 7.0, "vol_z": 2.5}
|
||||
|
||||
|
||||
def market_events(ticker: str, ticker_prices: "pd.DataFrame",
|
||||
ticker_flow: "pd.DataFrame | None", params: dict) -> list[dict]:
|
||||
"""일봉/flow 기반 시장 이벤트 (급변·거래량 Z·외인 순매도)."""
|
||||
p = {**_DEFAULT_EVENT_PARAMS, **(params or {})}
|
||||
events = []
|
||||
if ticker_prices is None or ticker_prices.empty or len(ticker_prices) < 2:
|
||||
return events
|
||||
tp = ticker_prices.sort_values("date").reset_index(drop=True)
|
||||
close = tp["close"].astype(float)
|
||||
pct = (close.iloc[-1] - close.iloc[-2]) / close.iloc[-2] * 100.0 if close.iloc[-2] else 0.0
|
||||
if abs(pct) >= p["move_pct"]:
|
||||
events.append({
|
||||
"type": "price_move",
|
||||
"severity": "high" if abs(pct) >= p["move_pct"] * 1.5 else "med",
|
||||
"summary": f"전일 대비 {pct:+.1f}%",
|
||||
})
|
||||
vol = tp["volume"].astype(float)
|
||||
if len(vol) >= 21:
|
||||
base = vol.iloc[-21:-1]
|
||||
mu, sd = base.mean(), base.std(ddof=0)
|
||||
last_vol = vol.iloc[-1]
|
||||
if mu > 0 and (
|
||||
(sd and (last_vol - mu) / sd >= p["vol_z"])
|
||||
or (not sd and last_vol >= mu * p["vol_z"]) # sd=0 fallback: plain ratio
|
||||
):
|
||||
z_txt = f"{(last_vol - mu) / sd:.1f}" if sd else f"ratio={last_vol / mu:.1f}x"
|
||||
events.append({
|
||||
"type": "volume_surge",
|
||||
"severity": "med",
|
||||
"summary": f"거래량 평소 대비 급증(Z={z_txt})",
|
||||
})
|
||||
if ticker_flow is not None and not ticker_flow.empty:
|
||||
tf = ticker_flow.sort_values("date")
|
||||
recent = tf["foreign_net"].astype(float).iloc[-3:]
|
||||
if len(recent) >= 3 and (recent < 0).all():
|
||||
events.append({
|
||||
"type": "foreign_selling",
|
||||
"severity": "med",
|
||||
"summary": "외국인 3일 연속 순매도",
|
||||
})
|
||||
return events
|
||||
|
||||
|
||||
def decide_action(tech_score: float, exit_flags: dict, pnl: float | None,
|
||||
add_score: float = ADD_SCORE) -> tuple[str, str]:
|
||||
"""액션 결정 매트릭스: sell > trim > add > hold (우선순위 순).
|
||||
|
||||
Reference in New Issue
Block a user