"""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