Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Quant Research Framework — Investor-Flow Accumulation

A research-grade pipeline for detecting institutional + foreign accumulation in Korean equities (KIS / Korea Investment & Securities API) and back-testing the "follow smart-money dual net-buying" hypothesis without look-ahead bias.

This repository is a methodology & engineering showcase. The signal concept (following days when both foreign and institutional investors net-buy the same name) is public knowledge. The tuned parameters that produced edge in my own research — scoring weights, take-profit / stop-loss levels, streak / window thresholds — live in a private config and are not included here. See Reproducibility.


Why this exists

Most retail "smart-money follower" strategies are back-tested wrong: they peek at the close to generate a signal and then assume they could have bought at that same close. That single look-ahead leak is enough to turn a losing rule into a "winner" on paper.

This framework was built to answer one question honestly:

If you systematically buy names where foreign and institutional investors are both accumulating, and you can only act on information available before the trade — does it actually pay?

The short answer from my data so far: inconclusive — and the framework is designed to say so loudly rather than hide it. (See Honest limitations.)


What's inside

Module Role
src/collect.py Daily ingestion: foreign/institutional flow ranks, dual net-buy detection, minute bars, execution-strength snapshots, fundamental features → SQLite
src/signal.py Accumulation detector: streak, N-day cumulative net-buy, acceleration, density → transparent additive score
src/backtest.py Event-driven back-test, 3 entry timings × TP/SL/time exit, look-ahead-free by construction
src/query.py Read-side helpers over the SQLite store
config.example.yaml All tunable parameters, externalized (real values withheld)

Design principles (the part that matters)

These are deliberate choices to keep the back-test from lying to me:

  1. No look-ahead. Signals are computed on confirmed end-of-day data; entry is the next session's open. The signal at index t can only read panel[0..t] (signal.py, backtest.py:signal_*).

  2. Pessimistic fills. When a bar's high hits the target and its low hits the stop on the same day, the back-test assumes the stop filled first. Gaps are filled at the open. Ambiguity always resolves against the strategy.

  3. Survivorship bias is named, not hidden. The candidate universe is "today's net-buy leaders," which is itself a forward-looking-ish pool. This is documented at every output, not swept under the rug.

  4. Small-sample honesty. The KIS investor endpoint returns ~30 trading days. That is not enough for a statistical conclusion, so the tool prints sample size and refuses to call a result significant. The intended use is to accumulate data daily via collect.py and re-validate on months of history.

  5. Transparent scoring. The accumulation score is a plain additive function (streak + dual-cumulative + acceleration + density), not a black box — so every rank is explainable. The weights are the tunable, withheld part.


Architecture

KIS REST API ──┐
               ├─►  collect.py  ──►  SQLite (quant.db)
               │      • investor_flow_rank      • minute_bar
               │      • dual_net_buy            • strength_snapshot
               │      • stock_feature
               │
   signal.py  ◄┘   build universe → fetch 30d investor history → score → rank
               │
 backtest.py   ◄─  build OHLC×investor panel → entry signal → simulate exit → stats

Single-file modules, no framework lock-in, SQLite for the store. The point is clarity and auditability, not infrastructure.


Reproducibility

The code is intentionally not runnable out of the box as a signal generator:

  • api.py (KIS auth, token, signed headers) is excluded — you must supply your own KIS credentials and a thin client matching the interface in src/api_stub.py.
  • config.example.yaml ships with neutral placeholder parameters. The weights and thresholds that worked in my research are not published.

So you can read, audit, and understand the methodology — but you cannot run it to lift a live signal. That is by design.


Honest limitations

  • ~30-day lookback per name → tiny sample, directional reference only, not a result.
  • Candidate pool = today's net-buy leaders → survivorship / selection bias.
  • No transaction-cost / slippage model beyond pessimistic fills and open-gap execution.
  • Korea-only, single data vendor (KIS).

These are listed first, not buried — because for a research tool, knowing why you can't trust a number yet is the whole job.


Stack

Python 3.11+ · SQLite · KIS Open API (REST) · requests. No heavyweight dependencies.


Author

Lee Geonjun (이건준) — Statistics, Korea University · quant research / algorithmic trading (KR & US markets), WorldQuant IQC. 📧 mark461182@gmail.com

Releases

Packages

Contributors

Languages