dcy-026 confirm-discovery + REQ-018 change-req(CHG-20260612-01)→구현→ confirm-req-implemented(CHG-20260612-02) 전체 delivery. - broker_port.py: calculate_tax(side, amount) 신규 + fee/tax 율 env 상수화 (TREX_KIS_FEE_BPS=1.5, TREX_KRX_TAX_SELL_BPS=20.0). 매도 0.2%/매수 0. - paper.py / kis.py: tax=0.0 하드코딩 3곳 제거 → 정책 함수 호출. KIS daily-ccld 응답에 fee/tax 필드 부재 → 정책 폴백(향후 정본 우선 훅). - runtime-flows.md: 체결→ledger fee/tax 산출 흐름 갱신. - tests: fee/tax 정책 단위 + issue #24 4건 회귀(fee+tax=1,376.9원). 429 passed (+6), ruff clean. REQ-018 Approved→Implemented, BAT-038 release-candidate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
77 lines
2.8 KiB
Python
77 lines
2.8 KiB
Python
"""Fee/tax policy tests (REQ-018 CHG-20260612-01, dcy-026, issue #24).
|
|
|
|
Validates the single fee/tax policy point (`calculate_fee`/`calculate_tax`)
|
|
and the paper broker carrying the policy-computed sell transaction tax — the
|
|
correction for the `tax=0.0` hardcoding that under-counted realized P&L.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from trex_core.adapters.broker.paper import PaperBroker
|
|
from trex_core.application.broker_port import OrderRequest, calculate_fee, calculate_tax
|
|
|
|
|
|
# --- AC-6/AC-7: policy functions -------------------------------------------
|
|
|
|
def test_calculate_fee_same_rate_buy_and_sell():
|
|
# 0.015 % commission on both sides (TREX_KIS_FEE_BPS=1.5 default)
|
|
assert calculate_fee("buy", 1_000_000) == 150.0
|
|
assert calculate_fee("sell", 1_000_000) == 150.0
|
|
|
|
|
|
def test_calculate_tax_sell_only():
|
|
# 0.2 % securities transaction tax on sells (TREX_KRX_TAX_SELL_BPS=20.0)
|
|
assert calculate_tax("sell", 1_000_000) == 2000.0
|
|
# buys are tax-free (KR equities)
|
|
assert calculate_tax("buy", 1_000_000) == 0.0
|
|
|
|
|
|
def test_fee_and_tax_zero_for_nonpositive_amount():
|
|
assert calculate_fee("sell", 0) == 0.0
|
|
assert calculate_fee("buy", -100) == 0.0
|
|
assert calculate_tax("sell", 0) == 0.0
|
|
assert calculate_tax("sell", -100) == 0.0
|
|
|
|
|
|
# --- AC-9: issue #24 four-fill regression (expected total 1,376.9원) --------
|
|
|
|
def test_issue_24_four_fill_regression():
|
|
# (side, price) with qty=1 — from issue #24 evidence (2026-06-11).
|
|
fills = [
|
|
("buy", 291_500),
|
|
("sell", 301_000),
|
|
("buy", 295_000),
|
|
("sell", 298_500),
|
|
]
|
|
total_fee = sum(calculate_fee(side, amount) for side, amount in fills)
|
|
total_tax = sum(calculate_tax(side, amount) for side, amount in fills)
|
|
|
|
assert round(total_fee, 1) == 177.9
|
|
assert round(total_tax, 1) == 1199.0
|
|
assert round(total_fee + total_tax, 1) == 1376.9
|
|
|
|
|
|
# --- AC-7: paper broker carries policy tax (parity with KIS policy) ---------
|
|
|
|
def _fill(broker, *, side, symbol, qty, price):
|
|
ack = broker.submit_order(
|
|
OrderRequest(slot_id="s1", side=side, symbol=symbol, qty=qty, limit_price=price)
|
|
)
|
|
fs = broker.poll_fill(ack.broker_order_id, side=side, symbol=symbol)
|
|
assert fs.is_filled
|
|
return fs
|
|
|
|
|
|
def test_paper_sell_fill_carries_policy_tax():
|
|
broker = PaperBroker(fill_after_polls=0)
|
|
fs = _fill(broker, side="sell", symbol="005930", qty=1, price=301_000)
|
|
assert fs.fee == calculate_fee("sell", 301_000)
|
|
assert fs.tax == calculate_tax("sell", 301_000)
|
|
assert fs.tax > 0.0 # the bug: previously hardcoded to 0.0
|
|
|
|
|
|
def test_paper_buy_fill_has_zero_tax():
|
|
broker = PaperBroker(fill_after_polls=0)
|
|
fs = _fill(broker, side="buy", symbol="005930", qty=1, price=291_500)
|
|
assert fs.fee == calculate_fee("buy", 291_500)
|
|
assert fs.tax == 0.0
|