feat: WBS-3.3 주문 시뮬레이터 tick 정규화 완성 (ETF 및 미국 주식 호가 단위 세분화)

This commit is contained in:
2026-06-13 14:05:30 +09:00
parent d9e3a43e61
commit 92823e30a3
2 changed files with 39 additions and 8 deletions
+38 -7
View File
@@ -36,6 +36,32 @@ TICK_TABLE = [
]
TICK_ABOVE_2M = 1_000
# Load names from GatherTradingData.json to detect ETF or US Stocks
TICKER_NAMES: dict[str, str] = {}
gtd_path = ROOT / "GatherTradingData.json"
if gtd_path.exists():
try:
gtd = json.loads(gtd_path.read_text(encoding="utf-8"))
df_rows = gtd.get("data", {}).get("data_feed") or []
for r in df_rows:
if isinstance(r, dict) and r.get("Ticker"):
TICKER_NAMES[str(r["Ticker"])] = str(r.get("Name") or "")
univ_rows = gtd.get("data", {}).get("universe") or []
for r in univ_rows:
if isinstance(r, dict) and r.get("Ticker"):
TICKER_NAMES[str(r["Ticker"])] = str(r.get("Name") or "")
except Exception:
pass
def _is_etf(ticker: str) -> bool:
name = TICKER_NAMES.get(ticker, "").upper()
return any(x in name for x in ["KODEX", "TIGER", "KBSTAR", "ARIRANG", "HANARO", "TIMEFOLIO", "SOL", "ACE", "PLUS"])
def _is_us_stock(ticker: str) -> bool:
return str(ticker).isalpha()
def _load_json(path: Path) -> dict:
if not path.exists():
@@ -46,15 +72,19 @@ def _load_json(path: Path) -> dict:
return {"_error": str(e), "_path": str(path)}
def _tick_size(price: float) -> int:
def _tick_size(price: float, ticker: str) -> float:
if _is_us_stock(ticker):
return 0.01
if _is_etf(ticker):
return 5.0
for threshold, tick in TICK_TABLE:
if price < threshold:
return tick
return TICK_ABOVE_2M
def _normalize_tick(price: float) -> float:
tick = _tick_size(price)
def _normalize_tick(price: float, ticker: str) -> float:
tick = _tick_size(price, ticker)
return math.floor(price / tick) * tick
@@ -95,10 +125,11 @@ def _simulate_order(order: dict) -> dict:
qty = int(order.get("quantity") or order.get("qty") or 0)
action = str(order.get("action") or order.get("order_type") or "").upper()
side = "BUY" if "BUY" in action else "SELL"
ticker = str(order.get("ticker") or "")
# Tick normalization check
if price > 0:
normalized = _normalize_tick(price)
normalized = _normalize_tick(price, ticker)
if abs(normalized - price) > 0.01:
errors.append(f"TICK_INVALID: price={price} → normalized={normalized}")
else:
@@ -113,11 +144,11 @@ def _simulate_order(order: dict) -> dict:
order_amount = slippage_price * qty
return {
"ticker": order.get("ticker"),
"ticker": ticker,
"action": action,
"original_price": price,
"normalized_price": _normalize_tick(price) if price > 0 else 0,
"slippage_adjusted_price": round(slippage_price, 0),
"normalized_price": _normalize_tick(price, ticker) if price > 0 else 0,
"slippage_adjusted_price": round(slippage_price, 2) if _is_us_stock(ticker) else round(slippage_price, 0),
"quantity": qty,
"order_amount_krw": round(order_amount),
"errors": errors,