feat(safety-sell): dcy-007 core 자율 안전 매도 — BAT-010 기반 + BAT-011 트리거
dcy-007(core 자율 안전 매도) 전체 delivery. BAT-010 기반 인프라가 미커밋 상태였고 BAT-011 트리거 레이어와 공유 파일에서 섞여 있어 한 커밋으로 묶음. BAT-010 (REQ-034/035/036/041, release-candidate): - WatchdogStore(application/watchdog.py) + snapshot RPC 워치독 갱신 - Slot stop_loss_price/take_profit_price 필드 + 004_add_price_thresholds.sql - SlotService.sell() 자율 매도 내부 경로 + idempotency - SafetySellConfig env 5종 파싱 BAT-011 (REQ-037/038/039/040/042, release-candidate): - SafetySellService(application/safety_sell.py): forced_close 만기 / stop-loss· take-profit KIS polling / 장 마감 강제 청산 + trex-safety-sell 7번째 스레드 - BrokerPort.get_current_price + KIS inquire-price(TTTC8397R/VTTC8397R, stck_prpr) + paper stub - SlotService.update_thresholds() + intent RPC update_thresholds 액션 (부분 갱신/null 해제, 워치독 last_seen_at 리셋) - baseline 3문서 정합(interface-contract/runtime-flows/project-structure) 거버넌스: REQ-034~042 Implemented 동기화, BAT-010/011 release-candidate, dcy-007 discovery 포함. 검증: 신규 38 테스트 green, 단위 236 passed/23 skipped, 인프라 게이트(PG+RabbitMQ) 259 passed(마이그레이션 001~004 round-trip 포함), ruff clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -69,6 +69,22 @@ class SnapshotScheduleConfig:
|
||||
tick_sec: float = 60.0
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SafetySellConfig:
|
||||
"""Safety-sell feature tuning (REQ-041 / BAT-010).
|
||||
|
||||
All five env vars are parsed here even though only WATCHDOG_TIMEOUT and
|
||||
WATCHDOG_GRACE_PERIOD are consumed in BAT-010; the remaining three are
|
||||
used by BAT-011 trigger loops once they land.
|
||||
"""
|
||||
|
||||
watchdog_timeout_sec: int = 120
|
||||
watchdog_grace_period_sec: int = 60
|
||||
forced_close_check_interval_sec: int = 60
|
||||
stop_loss_poll_interval_sec: int = 30
|
||||
market_close_liquidation_time: Optional[str] = None # "HH:MM" KST, optional
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class DaemonConfig:
|
||||
dsn: str
|
||||
@@ -87,6 +103,8 @@ class DaemonConfig:
|
||||
rabbitmq: Optional[RabbitMQConfig] = None
|
||||
# REQ-030 (BAT-008): daily snapshot refresh.
|
||||
snapshot_schedule: SnapshotScheduleConfig = SnapshotScheduleConfig()
|
||||
# REQ-041 (BAT-010): safety-sell feature config.
|
||||
safety_sell: SafetySellConfig = SafetySellConfig()
|
||||
|
||||
@staticmethod
|
||||
def from_env() -> "DaemonConfig":
|
||||
@@ -136,6 +154,18 @@ class DaemonConfig:
|
||||
kst_time=os.environ.get("TREX_SNAPSHOT_SCHEDULE_KST", "07:30").strip() or "07:30",
|
||||
tick_sec=float(os.environ.get("TREX_SNAPSHOT_TICK_SEC", "60")),
|
||||
)
|
||||
_mc_time = os.environ.get("TREX_MARKET_CLOSE_LIQUIDATION_TIME", "").strip() or None
|
||||
safety_sell = SafetySellConfig(
|
||||
watchdog_timeout_sec=int(os.environ.get("TREX_WATCHDOG_TIMEOUT", "120")),
|
||||
watchdog_grace_period_sec=int(os.environ.get("TREX_WATCHDOG_GRACE_PERIOD", "60")),
|
||||
forced_close_check_interval_sec=int(
|
||||
os.environ.get("TREX_FORCED_CLOSE_CHECK_INTERVAL", "60")
|
||||
),
|
||||
stop_loss_poll_interval_sec=int(
|
||||
os.environ.get("TREX_STOP_LOSS_POLL_INTERVAL", "30")
|
||||
),
|
||||
market_close_liquidation_time=_mc_time,
|
||||
)
|
||||
return DaemonConfig(
|
||||
dsn=dsn,
|
||||
total_slots=int(os.environ.get("TREX_TOTAL_SLOTS", "10")),
|
||||
@@ -147,4 +177,5 @@ class DaemonConfig:
|
||||
ledger_interval_sec=float(os.environ.get("TREX_LEDGER_INTERVAL_SEC", "1.0")),
|
||||
rabbitmq=rabbitmq,
|
||||
snapshot_schedule=snapshot_schedule,
|
||||
safety_sell=safety_sell,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user