feat: 안전형 스윙 개선(서킷브레이커·own_trend) + 매수 현금해결 방어
퀀트 리서치(공정 유니버스+walk-forward)로 검증된 안전 게이트 2종과 매수 경로 현금 해결 방어 코드 추가. 모두 기존 시그니처 유지(§4). - 지수 서킷브레이커: KODEX200 종가 < MA20이면 신규 스윙 진입 전면 중단 (하락장 상관붕괴=2024H2형 방어). execute_swing_entries가 기존 check_market_regime의 drop_from_ma20을 재사용(추가 API 없음). 백테스트 루프에도 동일 적용. 검증: 최근1년 Sharpe 0.82→1.13, CAGR 7.2→8.6%, MDD 개선. - own_trend 게이트: 평균회귀·눌림목 매수 시 종목 MA20 >= MA60×0.97일 때만 진입(하락추세 물타기 방지). 실서비스는 recommendations.is_mean_reversion_candidate 에서 StockAnalysis.ma20/ma60로, check_buy_signal엔 ma20/ma60 선택 파라미터 (기본 None=하위호환) 추가. 백테스트(kr_backtest_engine, run_backtest)도 평균회귀 한정. - 매수 현금 해결 방어(trade_executor._execute_buy): 0-현금 캐시를 신뢰하지 않고 현금<=0이면 자산과 무관하게 KIS 예수금 재조회, 실현금(>0)일 때만 캐시. 0-현금 고착으로 수량 0이 되는 잠재 무한루프 방어. 검증: code-verifier 통과(CONFIRMED 버그 없음, None-safe, §3/§4/§5 준수), pytest 442 통과, 6파일 컨테이너 컴파일·리로드 정상. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HBmzf1HDnvFHLraJ8eaaKJ
This commit is contained in:
@@ -253,6 +253,14 @@ def is_mean_reversion_candidate(stock, conditions: dict, market_regime_str: str)
|
||||
if close < settings.MIN_PRICE_KRW:
|
||||
return False
|
||||
|
||||
# [안전형 개선①] own_trend 게이트 (검증 완료: Sharpe 0.27→0.57)
|
||||
# 종목 자체 MA20 < MA60*0.97 (하락추세)이면 평균회귀 매수(물타기) 차단.
|
||||
# ma20/ma60 미조회(None)인 경우 게이트 미적용 — 기존 동작 보존.
|
||||
ma20_val = float(stock.ma20) if getattr(stock, "ma20", None) else None
|
||||
ma60_val = float(stock.ma60) if getattr(stock, "ma60", None) else None
|
||||
if ma20_val is not None and ma60_val is not None and ma60_val > 0 and ma20_val < ma60_val * 0.97:
|
||||
return False
|
||||
|
||||
regime = MarketRegime.BULL if market_regime_str == "bull" else MarketRegime.BEAR
|
||||
adx_val = float(stock.adx_14) if getattr(stock, "adx_14", None) else None
|
||||
# ADX는 0~100 범위. 100 초과 시 계산 오류이므로 무시
|
||||
@@ -625,7 +633,7 @@ async def get_rule_based_recommendations_realtime():
|
||||
@router.post("/auto-trade")
|
||||
async def execute_auto_trading(db: AsyncSession = Depends(get_db)):
|
||||
try:
|
||||
recommendations_response = await get_rule_based_recommendations(realtime=True)
|
||||
recommendations_response = await get_rule_based_recommendations(realtime=True, auto_trade=False)
|
||||
if not recommendations_response.get("recommendations_by_rule"):
|
||||
return {"message": "추천 종목이 없습니다", "trades": []}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user