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>
64 lines
2.2 KiB
Python
64 lines
2.2 KiB
Python
"""KRX 호가단위(tick-size) 산출 — 순수 도메인 함수 (REQ-083, dcy-030).
|
|
|
|
slippage 보호 지정가(REQ-070)가 KIS에 제출되기 전에 유효한 KRX 호가단위
|
|
배수로 스냅되도록 가격→tick 산출과 배수 내림/올림 헬퍼를 제공한다.
|
|
|
|
주식 전용이다. ETF/ETN 등 비주식 호가체계는 범위 밖이며(dcy-030 OQ-1),
|
|
2,000원 미만 비주식은 미지원(known limitation). 종목 유형 분기는 두지 않는다.
|
|
|
|
호가단위 표(KRX 주식, 2023-01-25~). 구간은 하한 포함/상한 미만 `[lo, hi)`:
|
|
|
|
가격(원) tick(원)
|
|
< 2,000 1
|
|
2,000 ~ 5,000 5
|
|
5,000 ~ 20,000 10
|
|
20,000 ~ 50,000 50
|
|
50,000 ~ 200,000 100
|
|
200,000 ~ 500,000 500
|
|
>= 500,000 1,000
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import math
|
|
|
|
# (lower_bound_inclusive, tick). 내림차순으로 평가한다. 단일 변경 지점(dcy-030 R-3).
|
|
_KRX_STOCK_TICKS: tuple[tuple[int, int], ...] = (
|
|
(500_000, 1_000),
|
|
(200_000, 500),
|
|
(50_000, 100),
|
|
(20_000, 50),
|
|
(5_000, 10),
|
|
(2_000, 5),
|
|
(0, 1),
|
|
)
|
|
|
|
|
|
def krx_stock_tick(price: float) -> int:
|
|
"""`price`(원)가 속한 KRX 주식 호가단위(원)를 반환한다.
|
|
|
|
구간 경계는 하한 포함(예: 정확히 50,000원 → 100원 tick).
|
|
"""
|
|
p = float(price)
|
|
for lower, tick in _KRX_STOCK_TICKS:
|
|
if p >= lower:
|
|
return tick
|
|
return 1 # p < 0 방어 (도달하지 않음)
|
|
|
|
|
|
# 부동소수 잡음 흡수용 epsilon. slippage raw = base*(1+pct/100)는 수학적으로
|
|
# 정수여도 float로는 1004.9999999999999처럼 나올 수 있다(잡음 ~ q*1e-16).
|
|
# q(=price/tick)는 유계라 1e-9면 잡음을 안전하게 흡수하면서 실제 값은 안 넘긴다.
|
|
_EPS = 1e-9
|
|
|
|
|
|
def floor_to_tick(price: float) -> int:
|
|
"""`price`를 해당 가격대 호가단위의 **내림 배수**로 정규화한다 (≤ price)."""
|
|
tick = krx_stock_tick(price)
|
|
return int(math.floor(float(price) / tick + _EPS) * tick)
|
|
|
|
|
|
def ceil_to_tick(price: float) -> int:
|
|
"""`price`를 해당 가격대 호가단위의 **올림 배수**로 정규화한다 (≥ price)."""
|
|
tick = krx_stock_tick(price)
|
|
return int(math.ceil(float(price) / tick - _EPS) * tick)
|