feat(slot): BAT-030 — 자산 중심 cutover 완성 (dcy-020 BAT-δ, REQ-056·057·062·064·065)
M1 literal inactive 전면화: SlotState 5-state(idle/closed 별칭 제거), 마이그레이션 010(CHECK 교체+행 rewrite), ops_checks 를 transition_log 기반 재작성. M2 집행 매도 수량 브로커 정본(D3): dispatch 매도 qty=broker hldg_qty, 미러 fallback 없음, 브로커 0 → skip+WARN, 리스트 기반 dispatch(head-block 방지). M3 컬럼 drop + legacy pool 제거: 마이그레이션 011(trading_date·entry_count DROP), repo 계약 instance 스코프 개편(집행 큐 포함), preallocation 폐기(데몬 7스레드), admission 자산 중심 단일 경로(slot_id 정본), capacity 재정의(available=M−active), daily_symbol_entries 코드 경로 제거. M4 cutover 도구(D1·D4): trex-core-cutover(전체 rebuild, preflight/브로커 carry-over/ --allow-unmapped/--promote-unique/dry-run) + PG 리허설 테스트 8건. M5 baseline 마감: data-model/runtime-flows/project-structure/interface-contract 자산 중심 최종 갱신 + run-trex-core-daemon 스킬 정합. 검증: 단위 367 passed + ruff clean; PG-gated 29 passed(010/011 round-trip·드리프트 가드·cutover 리허설); RabbitMQ 21 passed; run-the-app e2e(test DB+실 MQ, M=1): reconcile 슬롯 생성→slot_id 매수→dedup→capacity_full→fill→already_active→ 매도→inactive(자산 바인딩 보존)→영구 슬롯 재활성 전 구간 green. 실 cutover 는 운영자 실행(REL 단계, D4). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
"""Slot domain data classes (REQ-001, REQ-002)."""
|
||||
"""Slot domain data classes (REQ-001, REQ-002; dcy-020/REQ-056 asset-centric)."""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import date, datetime
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
from typing import Optional
|
||||
from uuid import UUID
|
||||
@@ -14,43 +14,37 @@ from trex_core.domain.slot.state import SlotState
|
||||
class Slot:
|
||||
"""Single daily_slot row.
|
||||
|
||||
Matches the slot column contract recorded in dcy-001 부록 A and
|
||||
docs/data-model.md (trex-core stores this in-memory for now; a future
|
||||
persistence layer must satisfy the same contract).
|
||||
dcy-020/REQ-056 (BAT-030): the slot is a **permanent asset slot** — one row
|
||||
per (instance_key, portfolio_id) universe asset, living across trading days.
|
||||
Pool-model columns `trading_date` and `entry_count` were dropped at the
|
||||
cutover (migration 011); time attributes live on events/ledger rows, not on
|
||||
the slot. Matches docs/data-model.md.
|
||||
"""
|
||||
|
||||
slot_id: UUID
|
||||
trading_date: date
|
||||
status: SlotState
|
||||
symbol: Optional[str] = None
|
||||
qty: Optional[float] = None
|
||||
avg_price: Optional[float] = None
|
||||
# dcy-012 / REQ-002: side-agnostic "current pending order limit price"
|
||||
# (renamed from entry_price). Set on pending entry (buy/sell limit), cleared
|
||||
# on pending exit (→HOLDING, →CLOSED) so a buy limit never leaks into a sell
|
||||
# order (issue #10). None => market order at dispatch.
|
||||
# on pending exit (→HOLDING, →INACTIVE) so a buy limit never leaks into a
|
||||
# sell order (issue #10). None => market order at dispatch.
|
||||
limit_price: Optional[float] = None
|
||||
allocated_capital: Optional[float] = None
|
||||
order_id: Optional[str] = None
|
||||
strategy_id: Optional[str] = None
|
||||
broker_order_id: Optional[str] = None # BAT-003 / REQ-011 (trex-orbit lineage)
|
||||
portfolio_id: Optional[str] = None # BAT-006 / REQ-025 (trex-orbit lineage)
|
||||
portfolio_id: Optional[str] = None # BAT-006 / REQ-025; asset binding (REQ-056)
|
||||
sell_strategy_id: Optional[str] = None # BAT-008 / REQ-047 (trex-orbit lineage)
|
||||
forced_close_at: Optional[datetime] = None # BAT-014 / REQ-066 (trex-orbit lineage)
|
||||
stop_loss_price: Optional[Decimal] = None # REQ-035 / BAT-010
|
||||
take_profit_price: Optional[Decimal] = None # REQ-035 / BAT-010
|
||||
# dcy-019 / REQ-002 (BAT-025): slot-scoped cumulative entry count. +1 on each
|
||||
# allocation transition (idle/closed → pending_buy); drives admission slot
|
||||
# selection (lowest entry_count first). Distinct from the symbol-scoped
|
||||
# daily_symbol_entries counter (REQ-002 AC-3) — different key/purpose.
|
||||
entry_count: int = 0
|
||||
created_at: Optional[datetime] = None
|
||||
updated_at: Optional[datetime] = None
|
||||
# dcy-020 / REQ-056 (BAT-027): asset-centric permanent slot foundation (안 B).
|
||||
# instance_key scopes slots per daemon instance in a shared DB
|
||||
# (UNIQUE(instance_key, portfolio_id)); advisory lock is also instance-scoped.
|
||||
# retired_at marks a soft-retired / universe-detached slot (OQ-2). Both added
|
||||
# additively here; entry_count/trading_date removal is deferred to cutover (BAT-δ).
|
||||
# dcy-020 / REQ-056: instance_key scopes slots per daemon instance in a
|
||||
# shared DB (UNIQUE(instance_key, portfolio_id)); advisory lock is also
|
||||
# instance-scoped. retired_at marks a soft-retired / universe-detached slot.
|
||||
instance_key: str = "default"
|
||||
retired_at: Optional[datetime] = None
|
||||
|
||||
@@ -61,7 +55,6 @@ class Slot:
|
||||
# / docs/data-model.md), not here — names/presence only.
|
||||
EXPECTED_SLOT_FIELDS: frozenset[str] = frozenset({
|
||||
"slot_id",
|
||||
"trading_date",
|
||||
"status",
|
||||
"symbol",
|
||||
"qty",
|
||||
@@ -76,7 +69,6 @@ EXPECTED_SLOT_FIELDS: frozenset[str] = frozenset({
|
||||
"forced_close_at",
|
||||
"stop_loss_price", # REQ-035
|
||||
"take_profit_price", # REQ-035
|
||||
"entry_count", # dcy-019 / REQ-002 (BAT-025)
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"instance_key", # dcy-020 / REQ-056 (BAT-027)
|
||||
@@ -86,11 +78,20 @@ EXPECTED_SLOT_FIELDS: frozenset[str] = frozenset({
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SlotCapacity:
|
||||
"""Snapshot of slot capacity returned by compute_slot_capacity (REQ-002 AC-6)."""
|
||||
"""Capacity snapshot returned by compute_slot_capacity (REQ-002 AC-6;
|
||||
dcy-020/REQ-059 asset-centric redefinition, BAT-030).
|
||||
|
||||
idle_count: int
|
||||
active_count = holding + pending (position-occupying). available_count is
|
||||
the remaining admission headroom: max(0, M − active) under a max_active_slots
|
||||
cap M, else the inactive (admittable) slot count. total_slots = M when set,
|
||||
else the instance's slot count. scarcity_ratio = 1 − available/total.
|
||||
"""
|
||||
|
||||
inactive_count: int
|
||||
holding_count: int
|
||||
pending_count: int
|
||||
active_count: int
|
||||
available_count: int
|
||||
total_slots: int
|
||||
scarcity_ratio: float
|
||||
|
||||
|
||||
Reference in New Issue
Block a user