Skip to content
codfish-zzPublic

About

Python back testing system for trading strategies, based on backtrader and AkShare, customized for China market.

Topics

Resources

Stars

30 stars

Watchers

2 watching

Forks

Latest commit

 

History

32 Commits

Folders and files

Repository files navigation

cn-trader

license.png

English | 简体中文

Overview

Python back testing system for trading strategies, customized for China market.

Features

  • Real A-share trading rules: commission (0.03% + ¥5 minimum) + stamp duty (sell-side 0.1%) + transfer fee (Shanghai 0.002%) + 100-share lot rounding + T+1 restriction + slippage.
  • Unified data layer: eastmoney/sina dual-source failover, Parquet local cache, incremental update, normalized columns [date, open, high, low, close, volume, amount].
  • Core metrics: total return, annualized return, max drawdown, Sharpe ratio, win rate, profit/loss ratio, average trade pnl.
  • Benchmark comparison: CSI 300 by default, automatic Alpha / Beta / excess cumulative return.
  • Parameter optimization: cartesian grid search via backtrader optstrategy.
  • Layered architecture: data (data.py) → engine (engine.py) → orchestration (mainland.py) → renderer (renderer.py) → UI (ui.py).

Installation

This repository is tested on Python 3.10+, backtrader 1.9+ and AkShare 1.4+.

With pip

pip install cn-trader

With source

$ git clone https://github.com/codfish-zz/cn-trader
$ cd cn-trader
$ pip install -r requirements.txt

Or

$ git clone https://gitee.com/huui/cn-trader.git
$ cd cn-trader
$ pip install -r requirements.txt

Quick tour

Launch the interactive UI

>>> from cn_trader import ui
>>> ui.main()

Run a single backtest from a script

from cn_trader.stock.mainland import Mainland

m = Mainland()
m.strategy_check_sma_cross(
    symbol="000002",
    start_date="20210101",
    end_date="20240101",
    pfast=10,
    pslow=30,
    print_log=True,
)

The output contains two rich tables — Backtest Summary and Core Metrics — covering Sharpe ratio, max drawdown, win rate, profit/loss ratio, plus Alpha / Beta / excess return against CSI 300.

Use the unified data layer

from cn_trader.stock.data import get_daily, get_index_daily, get_benchmark_returns

# Daily K-line for a stock (with local cache + incremental update)
df = get_daily(symbol="000002", start_date="20210101", end_date="20240101", adjust="qfq")

# Daily K-line for CSI 300
bench = get_index_daily(index_code="000300", start_date="20210101", end_date="20240101")

# Daily return series of the benchmark
bench_rets = get_benchmark_returns("000300", "20210101", "20240101")

Drive the backtest engine directly

import backtrader as bt
from cn_trader.stock.data import get_daily
from cn_trader.stock.engine import BacktestEngine
from cn_trader.stock.strategy.sma import SmaCross

df = get_daily(symbol="000002", start_date="20210101", end_date="20240101", adjust="qfq")
df.index = df["date"]
data = bt.feeds.PandasData(dataname=df)

engine = BacktestEngine()
cerebro, strat, end_cash, metrics = engine.run(
    strategy=SmaCross,
    data=data,
    symbol="000002",
    start_cash=1_000_000,
    commission=0.0003,
    percent_sizer=50,
    pfast=10, pslow=30,
    print_log=False,
)
print(metrics)

Parameter optimization (P3)

from cn_trader.stock.engine import BacktestEngine
from cn_trader.stock.strategy.sma import SmaCross

engine = BacktestEngine()
results = engine.optimize(
    strategy=SmaCross,
    data=data,
    symbol="000002",
    start_cash=1_000_000,
    commission=0.0003,
    percent_sizer=50,
    opt_kwargs={"pfast": [5, 10, 15], "pslow": [20, 30, 40]},
    print_log=False,
)
# results: list[list[Strategy]]; read metrics via strat.analyzers.sharpe.get_analysis()
# to pick the best parameter combination.

Environment variables

Variable Default Description
CN_TRADER_CACHE_DIR ~/.cn_trader/cache Data cache directory
CN_TRADER_USE_CACHE 1 Enable local cache; 0/false to disable
CN_TRADER_BENCHMARK 000300 Benchmark index code; empty to disable
MAX_RANK 3 (legacy) max rank

Testing

Run a single test case:

$ python -m unittest tests.test_data.DataLayerTestCase.test_adapt_eastmoney_columns

Run all non-network tests (data / engine / metrics / benchmark / optimization / A-share rules / util):

$ python -m unittest tests.test_data tests.test_ashare_rules tests.test_engine_metrics tests.test_util

tests.test_stock_mainland still calls the real akshare API and requires network access.

Architecture

cn_trader/stock/
├── data.py          # Data layer: akshare adapters / column normalization / Parquet cache / incremental / failover / index
├── commission.py    # A-share commission scheme: commission + stamp duty + transfer fee + ¥5 minimum
├── sizer.py         # A-share sizer: percent-based, rounded down to 100-share lots
├── strategy/base.py # Strategy base: T+1 restriction / order notifications / logging
├── metrics.py       # Metrics extraction: Sharpe / MaxDD / win rate / annualized / Alpha / Beta
├── engine.py        # Backtest engine: Cerebro assembly / run / optimize
├── renderer.py      # Result rendering: rich table summary + candlestick chart
└── mainland.py      # Orchestration: thin shell wiring data + engine + renderer
cn_trader/ui.py      # Interactive CLI: StrategyConfig + generic param collector

Create new strategy

  1. Create a subclass under cn_trader/stock/strategy/ inheriting from cn_trader.stock.strategy.base.Strategy.
  2. Implement next() with self.buy() / self.sell() (T+1 is enforced automatically).
  3. Append a StrategyConfig entry to the STRATEGY_CONFIGS registry in cn_trader/ui.py; the UI picks it up automatically.
  4. Add a strategy_check_xxx entry on Mainland (or call Mainland._backtest directly).

License

Apache License 2.0

About

Python back testing system for trading strategies, based on backtrader and AkShare, customized for China market.

Topics

Resources

Stars

30 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages