Files
genichinandClaude Opus 4.8 d8d9021260 feat(execution,obs): 호가단위 스냅(dcy-030/#28) + 운영 관측성(dcy-029/#29) + REL-007 환류
dcy-030 (issue #28, P0 체결 복구):
- REQ-083 KRX 호가단위(tick) 순수 유틸 (domain/pricing.py)
- REQ-070 CHG: 슬리피지 실효가 호가단위 스냅 — 상한 준수(매수 floor/매도 ceil)
  + band<1tick 하한/상한 보장 + 부동소수 epsilon 가드. 147700→148400/54500→54700.
- BAT-044 release-candidate

dcy-029 (issue #29, 운영 관측성):
- REQ-084 구조화 로깅 기반 (runtime/logging_setup.py): ISO8601 KST·레벨·Loki JSON
  ·correlation id(slot_id)
- REQ-085 주문 결과 로깅 (event=order_submit|fill|reject)
- REQ-087 거부 ERROR 가시성 (조용한 롤백 제거)
- REQ-086 청산 reason+actor 동반 노출 (안전망/전략 구분, 신규 필드 없음)
- REQ-088 운영 조회 CLI report (application/operator_report.py, 보유·오늘 체결
  ·실현 PnL net·대기, direct-DB)
- REQ-089 운영자 런북 docs/operations.md
- REQ-090 baseline 정합 (runtime-flows·project-structure·interface-contract)
- BAT-045(P0)·BAT-046(P1) release-candidate

REL-007: confirmed → feedback-captured (운영 인스턴스 라이브 검증 + #28/#29 환류
정규화). BAT-037~041 released.

검증: 537 passed, 47 skipped, 회귀 0, ruff clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 13:19:31 +09:00

60 lines
1.8 KiB
Python

"""KRX 호가단위(tick) 유틸 단위 테스트 (REQ-083, dcy-030)."""
import pytest
from trex_core.domain.pricing import ceil_to_tick, floor_to_tick, krx_stock_tick
class TestKrxStockTick:
@pytest.mark.parametrize("price,tick", [
(1_500, 1),
(3_000, 5),
(12_000, 10),
(35_000, 50),
(147_700, 100),
(300_000, 500),
(800_000, 1_000),
])
def test_representative_prices(self, price, tick):
assert krx_stock_tick(price) == tick
@pytest.mark.parametrize("boundary,tick", [
(2_000, 5), # [2000, 5000) -> 5 (lower bound inclusive)
(5_000, 10),
(20_000, 50),
(50_000, 100),
(200_000, 500),
(500_000, 1_000),
])
def test_boundaries_are_lower_inclusive(self, boundary, tick):
assert krx_stock_tick(boundary) == tick
def test_just_below_boundary_uses_lower_band(self):
assert krx_stock_tick(1_999) == 1
assert krx_stock_tick(49_999) == 50
assert krx_stock_tick(199_999) == 100
class TestFloorCeilToTick:
def test_issue28_floor_cases(self):
assert floor_to_tick(148_439) == 148_400
assert floor_to_tick(54_773) == 54_700
def test_ceil_case(self):
assert ceil_to_tick(54_773) == 54_800
def test_already_on_tick_is_unchanged(self):
assert floor_to_tick(147_700) == 147_700
assert ceil_to_tick(147_700) == 147_700
@pytest.mark.parametrize("price", [
1_500, 2_777, 4_999, 12_345, 33_333, 148_439, 234_567, 777_777,
])
def test_invariants_floor_le_price_le_ceil_and_valid_multiples(self, price):
tick = krx_stock_tick(price)
f = floor_to_tick(price)
c = ceil_to_tick(price)
assert f <= price <= c
assert f % krx_stock_tick(f) == 0
assert c % krx_stock_tick(c) == 0
assert c - f <= tick