Skip to content

Latest commit

 

History

8 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

A-Share AutoResearch

Autonomous quantitative strategy research for Chinese A-shares. An AI agent modifies a single strategy file, runs backtests on a held-out training window, and iteratively discovers profitable trading signals — inspired by karpathy/autoresearch.

⚠️ Methodology warnings before you start. Read Known Biases & Limitations before drawing any conclusions from a backtest result. The most important one — survivorship bias — is structural to the data source and not fixable inside this repo.

How It Works

┌─────────────┐     ┌──────────────┐     ┌─────────────┐
│  program.md │────▶│  AI Agent    │────▶│ strategy.py │
│ (human goal)│     │ (autonomous) │     │ (editable)  │
└─────────────┘     └──────┬───────┘     └──────┬──────┘
                           │                    │
                           │  run backtest      │
                           ▼                    ▼
                  ┌────────────────────┐  ┌─────────────────┐
                  │ results/           │◀─│ backtest.py     │
                  │  best_train.json   │  │ (fixed)         │
                  │  best_val.json     │  │  train window   │
                  │  test_final_*.json │  │  by default     │
                  └────────────────────┘  └─────────────────┘
  1. You write program.md with research goals and constraints.
  2. Agent reads it, modifies strategy.py, runs python backtest.py (= train split).
  3. Backtest evaluates under realistic A-share rules (T+1, price limits, costs).
  4. Result compared against best_train.json — if better, promoted; if worse, discarded.
  5. Every 5 iterations the agent runs --split val as a guardrail; if train improves but val doesn't, the agent backs off.
  6. At the end of a research phase, --split test writes a timestamped held-out report. Editing strategy.py based on that number invalidates it.

Quick Start

# 1. Install dependencies
uv sync

# 2. Download data (~25-40 min first time, ~5,200 stocks × 12 years).
#    Fetches OHLCV from Tencent then runs feature engineering.
python prepare.py

# 3. Incremental update later (only the days since last fetch)
python prepare.py --update

# 4. Run backtest on the train window (2017-01 → 2022-12)
python backtest.py

# 5. Sanity check on validation (2023-01 → 2024-06)
python backtest.py --split val

# 6. Diagnostic: all three splits side-by-side (does NOT promote any best file)
python backtest.py --split all

# 7. Final evaluation on the held-out test set (~2y)
python backtest.py --split test

Running the autonomous loop

By design, there is no driver script. The "agent" is whichever coding agent you spin up inside the repo — Claude Code, Cursor, Codex CLI, an SDK harness, etc. The entire orchestration lives in program.md, which the agent reads as its operating manual (setup → loop → logging → stop conditions). This mirrors the karpathy/autoresearch design choice: keep the surface area minimal, put the loop in Markdown.

Start a session by opening a coding agent in this directory and saying something like:

"Have a look at program.md and let's kick off a new experiment. Do the setup first."

The agent will create an autoresearch/<tag> branch, read the in-scope files, verify the data is prepared, and then LOOP FOREVER until you manually stop it — committing each experiment, recording results to a local results.tsv, reverting failures, and checking val every 5 iterations as a guardrail.

A typical overnight session at ~1 min/backtest yields ~500 experiments. You wake up to a branch with hundreds of commits and the TSV.

Project Structure

File Who Touches It Purpose
program.md Human Research direction, constraints, split discipline
strategy.py Agent Trading signal generation logic — the only file the agent modifies
prepare.py Fixed Data pipeline: fetch → features → cache
backtest.py Fixed A-share backtesting engine + split runner
results/research_log.md Human Phase-by-phase notes on data infra, bugs, methodology

Data

  • Source (active): Tencent web.ifzq.gtimg.cn for daily HFQ klines (paginated for the 640-row cap); akshare for the A-share code list (with Sina spot fallback when SZSE rate-limits).
  • Adjustment: HFQ (后复权), not QFQ. HFQ guarantees non-negative prices and produces a true total-return series. See results/research_log.md Phase 4 for the reasoning.
  • Frequency: Daily OHLCV + ~50 derived technical features.
  • Coverage: A-shares currently listed, 2014–present (~5,200 stocks, ~11M rows). North Exchange 920xxx codes are not in Tencent and are skipped.

Data source matrix — what each source unlocks

This is the most important table in the repo: your source caps which strategies you can ever evaluate. All numbers verified against akshare 1.18+ in May 2026.

Source OHLCV hfq/qfq real amount turnover_rate outstanding_share PE/PB history BJ 920xxx Throughput Thread-safe Reliability
Tencent fqkline (active) ✅ both ❌ approx ~5 stocks/s ★★★★★
Sina (stock_zh_a_daily) ✅ both ✅ (via spot list) ~0.3 stocks/s ❌ (mini-racer) ★★★★
Eastmoney (stock_zh_a_hist) ✅ both sep. endpoint medium ★ (blocked from many CN IPs)
Tushare Pro (paid) ✅ both high paid / point cap

Feature-unlock map — pick a source by what you need

If your strategy needs… Minimum source
Any pure-technical signal (MA, RSI, MACD, Bollinger, ATR, KDJ, momentum, volatility) Tencent (default)
Cross-section liquidity (real Amihud illiq), retail-vs-institutional turnover Sina (+ ProcessPool + ~70 min fetch)
Real market cap (size factor, size-neutralization) Sina (close_raw × outstanding_share)
Historical PE / PB / market cap as time series Tushare Pro (paid) — or skip
Point-in-time delisted universe (survivorship-bias fix) Tushare Pro / Wind / CSMAR — no free option
BJ 920xxx coverage Sina or Eastmoney (Tencent doesn't serve them)

Why Tencent is the default

Tencent gives us the fastest, most reliable feed for everything that depends only on OHLCV — and that's the entire technical-analysis feature set (~45 of the ~50 features in the pipeline). Switching to Sina to unlock 4–5 more features costs ~3x fetch time and a process-pool workaround; only worth it once you have a strategy hypothesis that specifically needs them.

The switch is mechanical: drop in the _fetch_single_stock_sina template in prepare.py and swap ThreadPoolExecutorProcessPoolExecutor(max_workers=4).

Backtest Engine

Realistic A-share market simulation:

  • T+1 settlement (signal at day-T close → trade at day-(T+1) open).
  • Price-limit filtering: 10 % main board, 20 % ChiNext/STAR, 30 % BJ.
  • Stamp tax 0.05 % (sell), commission 0.025 % (both sides), slippage 0.01 %.
  • Suspension detection (volume = 0), liquidity filters, position-vaporization guard.
  • Metrics: Sharpe, Calmar, MaxDD, Win Rate, VaR, daily/annual turnover.

Train / Validation / Test

Single-holdout split, enforced by --split:

Split Window Purpose
warmup 2014-01 → 2016-12 (3y) Features only; no trading. Lookback for 252-day indicators.
train 2017-01 → 2022-12 (6y) Agent iterates here. best_train.json tracks improvements.
val 2023-01 → 2024-06 (1.5y) Overfitting guardrail. Auto-promotes in best_val.json.
test 2024-07 → end of data (~2y) Sacred. Each run writes test_final_<timestamp>.json; never auto-promoted.

--split all runs all three side-by-side without touching any best file.

Known Biases & Limitations

Read this section before you (or your agent) celebrate any backtest number.

1. Survivorship bias — STRUCTURAL

Tencent's endpoint only serves stocks that are currently listed. Stocks that have delisted (退市) — failures, fraud blowups, *ST →退市 chains — are completely absent from the historical universe. Every backtest in this repo implicitly assumes you could have known in 2017 that today's 5,200 stocks would survive to 2026.

Empirically this lifts annual returns by roughly 2-4 % p.a. for naive long strategies and exaggerates Sharpe by 0.2-0.4 versus a delisting- inclusive universe. The direction of the bias is always favorable, and the agent loop has no way to detect it from within the data.

Mitigations (not implemented here):

  • Source a "point-in-time universe" with delisted tickers from Wind / Choice / Tushare Pro / CSMAR. Re-run prepare.py against that universe.
  • Be explicit in your hypothesis: "this strategy is being judged on the surviving universe and will likely overstate live PnL."

2. ST / *ST stocks are not specially handled

ST stocks have a 5 % daily price limit (not 10 %) and many institutions forbid trading them. The backtest's price-limit logic uses code prefix only and treats ST stocks as ordinary main-board names. If your strategy disproportionately picks ST tickers in the train window, the numbers are optimistic.

3. amount is in HFQ-adjusted units, not real CNY

Because the pipeline approximates amount ≈ close × volume and close is HFQ-adjusted, amount scales with the cumulative dividend factor. Any feature derived from it (illiq, amount_ratio, etc.) is per-stock relative only — a heavy-dividend old stock looks artificially liquid versus a young one in cross-section comparisons.

4. No fundamentals

PE, PB, market cap, turnover_rate — and anything derived from them — are absent. Eastmoney (the only free source) blocks frequent IPs; shipping stale fundamentals is worse than shipping none. Value/quality strategies require an external feed.

5. North Exchange 920xxx codes are missing

Tencent does not serve them. The pipeline reports them as permanently failed (~316 codes). Strategies that need full A-share coverage need a secondary feed for these names.

6. Trading-day calendar is implicit

Trading days are inferred from the union of dates that have at least one row across the universe. There is no explicit holiday calendar. Single- session vendor outages would silently look like a market holiday.

7. No order-book / intraday model

Execution is modeled at the next day's open with a flat 1 bp slippage, 0.025 % commission, and 0.05 % stamp tax. There is no participation model, no market impact, no open-call queuing. High-turnover strategies will likely underperform their backtest in live trading by more than the explicit costs alone.

License

MIT — use it, fork it, build on it. Let the community find alpha.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages