KIS + KRX Pre-Trade Validation Matrix (Executable Rules Playbook)

2026-02-25 · finance

KIS + KRX Pre-Trade Validation Matrix (Executable Rules Playbook)

Date: 2026-02-25 (KST)

TL;DR

If Vellab goes live on Korean equities, the main risk is not alpha quality but invalid order intents reaching the broker.

This playbook converts KRX market micro-rules + KIS API constraints into an executable pre-trade matrix:

  1. Validate session/order-type/time windows before any API call
  2. Normalize price to KRX tick-size ladder before submit
  3. Enforce throttle + websocket budget + reconnect discipline as hard gates
  4. Add a market-abuse risk filter (pattern-based, machine-enforced)

1) Source-Backed Constraints to Encode as Code

1.1 KRX Trading Time Windows (KOSPI/KOSDAQ baseline)

From 찾기쉬운 생활법령정보 summary of KRX business rules:

Implementation principle: strategy should declare allowed session blocks explicitly (no implicit “always-on” trading).

1.2 KRX Tick-Size Ladder (Must Round Before Send)

Price tick units (생활법령정보 summary):

If your engine computes fair value at 73,742 KRW, you must map to a legal tick (73,700 or 73,800 depending on side policy).

1.3 Market-Abuse Legal Boundary (Automation Still Liable)

Capital Markets Act Article 176 prohibits deceptive/manipulative patterns (e.g., collusive matched behavior, non-beneficial wash-like trades, misleading market activity).

Engine implication: implement anti-pattern gates in automation instead of relying on operator intuition.

1.4 KIS Connectivity + Auth Shape (from official sample repo)

From open-trading-api (kis_auth.py, kis_devlp.yaml, README):

Notable operational note in sample README: token reissue guidance mentions “1 minute per issue” behavior.


2) Executable Pre-Trade Validation Matrix

Gate Input Rule Action on Fail Telemetry Key
Session gate nowKST, targetSession Order allowed only in declared session block reject intent (no broker call) reject.session_window
Market-day gate nowKST block Saturdays + known exchange holidays/closed days reject intent reject.market_closed
Order-type gate session, orderType allowed order-type subset per session reject intent reject.order_type_session
Tick-size gate px, side price must align to legal KRX tick auto-normalize or reject (policy) adjust.tick_round / reject.tick
Qty gate qty min unit (1 share baseline, strategy-specific min lot optional) reject intent reject.qty_unit
Notional cap qty, px per-order and per-symbol cap reject intent reject.notional_cap
Position cap currentPos, projectedPos projected exposure <= configured max reject intent reject.position_cap
Throttle gate account/appkey budget REST/WS/token budget not exceeded queue/defer/reject throttle.hit
WS budget gate wsSubs active realtime regs <= allowed budget deny new sub / rotate LRU ws.sub_budget_hit
Abuse-pattern gate order stream features block suspicious repetitive self-reinforcing patterns freeze strategy + alert risk.abuse_pattern

3) Session × Order Policy Table (Starter)

Session block Time (KST) Allowed strategy intent (example) Notes
PRE_CROSS 08:30–08:40 low-risk carryover only no aggressive catch-up
REGULAR 09:00–15:30 full strategy set strict loss + participation caps
POST_CLOSE_CROSS 15:40–16:00 controlled residual unwind no new directional entries
POST_SINGLE 16:00–18:00 optional residual management only symbol whitelist recommended

Keep this table config-driven (session_policy.yaml), not hardcoded constants.


4) Tick Normalization Policy (Side-Aware)

Use deterministic rounding policy to avoid random reject patterns:

This prevents hidden strategy drift and makes TCA attribution cleaner.


5) KIS Control-Plane Hardening Rules

5.1 Separate limiter buckets

Implement independent token buckets:

  1. rest_real
  2. rest_paper
  3. token_issue
  4. ws_subscribe_ops

Do not share one global limiter; each has different failure costs.

5.2 Conservative token issuance policy

Because documentation/operational notes can differ over time, enforce conservative defaults:

5.3 WebSocket anti-thrash


6) Abuse-Pattern Safety Layer (Article 176 aware)

Minimum machine checks before order send:

  1. Cancel/replace burst detector per symbol/window
  2. Same-price repetitive pinging detector without inventory objective
  3. Participation spike detector vs ADV/intraday curve
  4. Cross-account correlation alarm (if multi-account infra exists)

On trigger:


7) Suggested Data Contract for Validator

interface PreTradeIntent {
  account: string
  symbol: string
  side: 'BUY' | 'SELL'
  orderType: 'LIMIT' | 'MARKET' | '...'
  qty: number
  price?: number
  strategyId: string
  sessionTag: 'PRE_CROSS' | 'REGULAR' | 'POST_CLOSE_CROSS' | 'POST_SINGLE'
  tsKst: string
}
interface ValidationResult {
  ok: boolean
  normalized?: { price?: number; qty?: number }
  rejectCode?: string
  rejectReason?: string
  metrics: string[]
}

All rejected intents should be persisted to an audit table for postmortem and policy tuning.


8) Implementation Sequence (Fast + Safe)

  1. Build pure function validator (no network calls)
  2. Add unit tests for 50+ boundary scenarios (time edges, tick edges, cap edges)
  3. Integrate into execution gateway as first gate
  4. Enforce “no validation, no order” invariant at compile/runtime boundaries
  5. Add dashboard for reject/adjust rates by rule

9) References


One-line takeaway

For KR live execution, treat pre-trade validation as a deterministic compiler pass: if intent doesn’t compile against market, broker, and legal rules, it never reaches the exchange.