feat: TradingView 스타일 대시보드 + 차트 오버레이 API, Crisis 손절 소급 차단 플래그(기본 OFF)
- frontend: 대시보드를 TradingView 레이아웃으로 재구성(components/tv/*) — 캔들+MA/BB/거래량, 평단·손절·목표·트레일링 가격선, 체결 마커, RSI 기준선은 실효 설정값, 하단 탭(보유·체결·DT 카드· 비용 후 손익곡선/낙폭·저널), 디자인 토큰·한국식 색·최소 11px, 모바일 가로 스크롤 제거 - backend(GET 전용, require_api_key): /positions/overlay, /risk/equity-history, /strategy-config/display, /trades 응답 필드(side·strategy_type·commission·net_profit_loss), /risk/mdd-status thresholds, /market-data/chart 1m|5m(캐시·in-flight 공유) - mean_reversion_strategy: RSI 청산 임계를 resolve_swing_exit_rsi_v2 로 추출(동작 동일) — 청산과 화면 표시가 같은 값을 쓴다 - §8.5-39: SWING_STOP_IGNORE_CRISIS_OVERLAY(기본 False) — 반사실 재생에서 Crisis 조기 손절이 오히려 손실을 줄여 기본 OFF 유지, 정식 백테스트 전 켜지 말 것 - 유닛 스위트 2816 passed / 0 failed, tsc·build 통과 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -858,6 +858,26 @@ def _check_buy_signal_impl(
|
||||
return None
|
||||
|
||||
|
||||
def resolve_swing_exit_rsi_v2() -> Tuple[float, float, bool]:
|
||||
"""스윙 RSI 청산 임계 단일 출처 → (1차 익절 RSI, 전량 RSI, 적응형 적용 여부).
|
||||
|
||||
settings 기본 + 적응형(fallback_used=False)이면 적응형 값. check_exit_signal 과 프론트 표시
|
||||
(/strategy-config/display)가 같은 함수를 쓴다. 조회 실패는 warning 후 settings 값.
|
||||
"""
|
||||
from app.core.config import settings
|
||||
partial = settings.MEAN_REVERSION_RSI_EXIT_THRESHOLD
|
||||
full = settings.MEAN_REVERSION_RSI_FULL_EXIT
|
||||
try:
|
||||
from app.services.adaptive_strategy import get_current_adaptive_params
|
||||
_adp = get_current_adaptive_params()
|
||||
if not _adp.fallback_used:
|
||||
return _adp.rsi_partial_exit, _adp.rsi_full_exit, True
|
||||
except Exception as _adp_err:
|
||||
warn_throttled(logger, "mean_reversion:exit_rsi_overlay",
|
||||
"[MeanReversion] 적응형 RSI 청산 임계 조회 실패 — config 값 사용: %s", _adp_err)
|
||||
return partial, full, False
|
||||
|
||||
|
||||
def resolve_swing_stop_params_v2(atr: Optional[float], close: float) -> Tuple[float, float]:
|
||||
"""스윙 손절 파라미터 단일 출처 → (stop_loss_pct[항상 음수, %], atr_stop_mult).
|
||||
|
||||
@@ -897,6 +917,14 @@ def resolve_swing_stop_params_v2(atr: Optional[float], close: float) -> Tuple[fl
|
||||
warn_throttled(logger, "mean_reversion:exit_sl_overlay",
|
||||
"[MeanReversion] 적응형 손절/ATR 배수 조회 실패 — strategy_cfg/config 값 사용: %s", _adp_err)
|
||||
_adp_exit = None
|
||||
# [§8.5-39] Crisis 는 신규 진입 차단 용도 — 보유분 손절선을 소급 조정하지 않는다(플래그, 기본 OFF).
|
||||
# 플래그 조회 자체는 예외 가능성이 없는 getattr 이며, 모드 판정은 단순 속성 비교다.
|
||||
_ignore_crisis = getattr(settings, "SWING_STOP_IGNORE_CRISIS_OVERLAY", False) is True
|
||||
if (_ignore_crisis and _adp_exit is not None
|
||||
and str(getattr(_adp_exit, "trading_mode", "")) == "crisis"):
|
||||
warn_throttled(logger, "mean_reversion:exit_sl_crisis_ignored",
|
||||
"[MeanReversion] 적응형 모드=crisis — 손절 오버레이 미적용(strategy_cfg/config 값 사용)")
|
||||
_adp_exit = None
|
||||
if _adp_exit is not None and not getattr(_adp_exit, "fallback_used", True):
|
||||
_adp_mult = _stop_param_or_none_v2(getattr(_adp_exit, "atr_stop_multiplier", None),
|
||||
"adaptive.atr_stop_multiplier")
|
||||
@@ -1128,19 +1156,7 @@ def check_exit_signal(
|
||||
# ── 2. 1차 익절: 아직 1차 안 했을 때 ──────────────────────────
|
||||
# CLAUDE.md 6.3: RSI >= 50 OR 가격 > BB 중앙선 OR 수익 > 2*ATR (OR 로직)
|
||||
# 적응형 전략: 1차 익절 RSI / 전량 청산 RSI 오버라이드
|
||||
_adaptive_rsi_partial = settings.MEAN_REVERSION_RSI_EXIT_THRESHOLD
|
||||
_adaptive_rsi_full = settings.MEAN_REVERSION_RSI_FULL_EXIT
|
||||
try:
|
||||
from app.services.adaptive_strategy import get_current_adaptive_params
|
||||
_adp_exit2 = get_current_adaptive_params()
|
||||
if not _adp_exit2.fallback_used:
|
||||
_adaptive_rsi_partial = _adp_exit2.rsi_partial_exit
|
||||
_adaptive_rsi_full = _adp_exit2.rsi_full_exit
|
||||
except Exception as _adp_err:
|
||||
_adaptive_rsi_partial = settings.MEAN_REVERSION_RSI_EXIT_THRESHOLD
|
||||
_adaptive_rsi_full = settings.MEAN_REVERSION_RSI_FULL_EXIT
|
||||
warn_throttled(logger, "mean_reversion:exit_rsi_overlay",
|
||||
"[MeanReversion] 적응형 RSI 청산 임계 조회 실패 — config 값 사용: %s", _adp_err)
|
||||
_adaptive_rsi_partial, _adaptive_rsi_full, _ = resolve_swing_exit_rsi_v2()
|
||||
|
||||
min_profit_price = entry_price + 2.0 * atr # CLAUDE.md 6.3: 2×ATR
|
||||
_profit_pct = ((close - entry_price) / entry_price * 100) if entry_price > 0 else 0
|
||||
|
||||
Reference in New Issue
Block a user