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>
60 lines
1.8 KiB
Python
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
|