完成回测的基本计算
This commit is contained in:
1050
backtest.ipynb
1050
backtest.ipynb
File diff suppressed because it is too large
Load Diff
126
note.md
Normal file
126
note.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# 基础时间与权益指标
|
||||
|
||||
## Exposure Time [%] - 持仓时间占比(%)
|
||||
|
||||
衡量策略在统计周期内实际持有头寸的时间占总时间的百分比。反映策略的“活跃度”(如占比100%=全程持仓,占比低=多数时间空仓),辅助判断交易频率和资金使用效率。
|
||||
|
||||
## Equity Final [$] - 最终权益
|
||||
|
||||
策略运行结束时的账户总资金(本金+盈亏),是最基础的收益结果指标,直接体现最终资金规模。
|
||||
|
||||
## Equity Peak [$] - 权益峰值
|
||||
|
||||
统计周期内账户权益的最大值。对比最终权益可判断策略是否从峰值回落,结合回撤指标能评估收益稳定性。
|
||||
|
||||
# 收益率类指标(核心收益衡量)
|
||||
|
||||
## Return [%] - 总收益率(%)
|
||||
|
||||
周期内总收益占初始本金的百分比(`(最终权益-初始本金)/初始本金×100%`),直观反映整体盈利/亏损程度,但未考虑时间因素。
|
||||
|
||||
## Buy & Hold Return [%] - 买入并持有收益率(%)
|
||||
|
||||
作为“基准收益率”,模拟“买入标的后长期持有不操作”的收益。用于对比策略收益,判断策略是否跑赢简单持有,是评估策略有效性的核心参照。
|
||||
|
||||
## Return (Ann.) [%] - 年化收益率(%)
|
||||
|
||||
将周期总收益折算为按年计算的收益(`(1+总收益率)^(365/持仓天数) - 1`),消除时间周期差异,是跨周期对比不同策略收益的关键指标。
|
||||
|
||||
## CAGR [%] - 复合年均增长率(%)
|
||||
|
||||
即“年均复合收益率”(`(最终权益/初始本金)^(1/年数) - 1`),衡量资金长期复利增长速度,更贴合多年期策略的收益评估,避免短期高收益误导。
|
||||
|
||||
# 风险类指标(衡量亏损与波动风险)
|
||||
|
||||
## Volatility (Ann.) [%] - 年化波动率(%)
|
||||
|
||||
收益波动程度的年化标准差,反映收益稳定性。波动率越高,收益起伏越大,策略“震荡风险”越高;波动率低则收益更平稳。
|
||||
|
||||
## Max. Drawdown [%] - 最大回撤(%)
|
||||
|
||||
权益从峰值回落至谷值的最大幅度(`(峰值-谷值)/峰值×100%`),核心风险指标。比如最大回撤20%,说明策略曾从最高点亏掉20%,直接体现抗风险能力。
|
||||
|
||||
## Avg. Drawdown [%] - 平均回撤(%)
|
||||
|
||||
所有回撤的平均幅度。若最大回撤远高于平均回撤,说明策略曾出现极端亏损;两者接近则回撤风险更均匀。
|
||||
|
||||
## Max. Drawdown Duration - 最大回撤持续时长
|
||||
|
||||
最大回撤从峰值回落至恢复峰值的时间(天/月),反映策略从最大亏损中回血的速度,时长越长则恢复能力越弱。
|
||||
|
||||
## Avg. Drawdown Duration - 平均回撤持续时长
|
||||
|
||||
所有回撤周期的平均时间,辅助判断策略亏损后的恢复效率。
|
||||
|
||||
# 风险收益比指标(性价比核心)
|
||||
|
||||
## Sharpe Ratio - 夏普比率
|
||||
|
||||
衡量“每承担1单位风险的超额收益”(`(年化收益率-无风险利率)/年化波动率`
|
||||
),无风险利率通常取国债利率(3%左右)。比率越高性价比越高(>1较好,>2优秀)。
|
||||
|
||||
## Sortino Ratio - 索提诺比率
|
||||
|
||||
改进版夏普比率,仅用“下行波动率”(亏损方向波动)计算风险,更贴合投资者对“亏损风险”的关注,适合评估稳健型策略。
|
||||
|
||||
## Calmar Ratio - 卡尔玛比率
|
||||
|
||||
`年化收益率/最大回撤(绝对值)`,直接反映“收益能否覆盖最大亏损”,比率越高,策略用小回撤换高收益的能力越强。
|
||||
|
||||
# 资产定价系数(市场相关性)
|
||||
|
||||
## Alpha [%] - 阿尔法系数(%)
|
||||
|
||||
衡量策略“超额收益”(超出市场基准的收益),Alpha为正说明策略能跑赢市场,反映主动管理能力;为负则跑输市场。
|
||||
|
||||
## Beta - 贝塔系数
|
||||
|
||||
衡量策略与市场基准的波动同步性。Beta=1=和市场波动一致;Beta>1=策略更激进(波动更大);Beta<1=策略更稳健;Beta<0=与市场反向波动。
|
||||
|
||||
# 交易行为与质量指标
|
||||
|
||||
## Trades - 交易总次数
|
||||
|
||||
统计周期内总交易笔数,反映策略交易频率(高频/低频),结合胜率可判断策略有效性。
|
||||
|
||||
## Win Rate [%] - 胜率(%)
|
||||
|
||||
盈利交易次数/总次数×100%,直观反映“赚钱交易的比例”,但需结合盈亏比(平均盈利/平均亏损)使用(如胜率50%+盈亏比3:1仍盈利)。
|
||||
|
||||
## Best Trade [%] - 最佳单笔交易收益率(%)
|
||||
|
||||
单次盈利幅度最大值,反映策略盈利上限,但需警惕是否由偶然高收益拉高整体表现。
|
||||
|
||||
## Worst Trade [%] - 最差单笔交易收益率(%)
|
||||
|
||||
单次亏损幅度最大值(通常为负),反映单笔交易的最大亏损风险。
|
||||
|
||||
## Avg. Trade [%] - 平均单笔交易收益率(%)
|
||||
|
||||
所有交易收益率的平均值,反映单笔交易的平均盈利能力,为正则单笔交易平均赚钱。
|
||||
|
||||
## Max. Trade Duration - 单笔交易最长持有时长
|
||||
|
||||
单次持仓的最长时间,定义策略风格(日内/短线/中长线)。
|
||||
|
||||
## Avg. Trade Duration - 单笔交易平均持有时长
|
||||
|
||||
所有交易持仓时长的平均值,辅助判断资金周转效率。
|
||||
|
||||
# 综合绩效指标(策略可持续性)
|
||||
|
||||
## Profit Factor - 盈利因子
|
||||
|
||||
`总盈利金额/总亏损金额(绝对值)`,核心指标。>1说明总盈利>总亏损,数值越高盈利能力越强(如盈利因子2=每亏1元赚2元)。
|
||||
|
||||
## Expectancy [%] - 期望收益(%)
|
||||
|
||||
每笔交易的预期收益(`胜率×平均盈利 - (1-胜率)×平均亏损`),为正则策略长期可盈利,数值越高预期收益越好。
|
||||
|
||||
## SQN - 系统质量数
|
||||
|
||||
衡量策略收益的“统计显著性”(`√交易次数 × 期望收益 / 交易收益标准差`)。SQN>1.6合格,>2.5优秀,>3.0卓越,判断收益是否为偶然结果。
|
||||
|
||||
## Kelly Criterion - 凯利准则
|
||||
|
||||
计算“最优仓位比例”(`(胜率×(盈亏比+1)-1)/盈亏比`),确定每笔交易的最佳资金投入比例,最大化长期复利收益,同时控制破产风险(实际应用中通常减半使用)。
|
||||
@@ -5,8 +5,10 @@ description = "Stock analysis"
|
||||
requires-python = ">=3.14"
|
||||
dependencies = [
|
||||
"backtesting~=0.6.5",
|
||||
"duckdb>=1.4.3",
|
||||
"jupyter~=1.1.1",
|
||||
"matplotlib~=3.10.8",
|
||||
"mplfinance>=0.12.10b0",
|
||||
"pandas~=2.3.3",
|
||||
"pandas-stubs~=2.3.3",
|
||||
"peewee~=3.19.0",
|
||||
|
||||
72
sql2dataframe.ipynb
Normal file
72
sql2dataframe.ipynb
Normal file
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"metadata": {},
|
||||
"cell_type": "code",
|
||||
"outputs": [],
|
||||
"execution_count": null,
|
||||
"source": [
|
||||
"import pandas as pd\n",
|
||||
"\n",
|
||||
"host = '81.71.3.24'\n",
|
||||
"port = 6785\n",
|
||||
"database = 'leopard_dev'\n",
|
||||
"username = 'leopard'\n",
|
||||
"password = '9NEzFzovnddf@PyEP?e*AYAWnCyd7UhYwQK$pJf>7?ccFiN^x4$eKEZ5~E<7<+~X'"
|
||||
],
|
||||
"id": "1e8d815ee9b8c936"
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "initial_id",
|
||||
"metadata": {
|
||||
"collapsed": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import psycopg2\n",
|
||||
"\n",
|
||||
"dailies_df = pd.DataFrame()\n",
|
||||
"\n",
|
||||
"with psycopg2.connect(host=host, port=port, database=database, user=username, password=password) as connection:\n",
|
||||
" with connection.cursor() as cursor:\n",
|
||||
" # language=PostgreSQL\n",
|
||||
" cursor.execute(\n",
|
||||
" \"\"\"select trade_date, open, close, high, low, factor\n",
|
||||
"from leopard_daily daily\n",
|
||||
" left join leopard_stock stock on stock.id = daily.stock_id\n",
|
||||
"where stock.code = '000001.SZ'\n",
|
||||
" and daily.trade_date between '2025-01-01 00:00:00' and '2025-12-31 23:59:59'\n",
|
||||
"order by daily.trade_date\"\"\"\n",
|
||||
" )\n",
|
||||
" rows = cursor.fetchall()\n",
|
||||
"\n",
|
||||
" dailies_df = pd.DataFrame.from_records(rows, columns=['trade_date', 'open', 'close', 'high', 'low', 'factor'])\n",
|
||||
"\n",
|
||||
"dailies_df"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 2
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython2",
|
||||
"version": "2.7.6"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
32
uv.lock
generated
32
uv.lock
generated
@@ -338,6 +338,21 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604, upload-time = "2021-03-08T10:59:24.45Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "duckdb"
|
||||
version = "1.4.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7f/da/17c3eb5458af69d54dedc8d18e4a32ceaa8ce4d4c699d45d6d8287e790c3/duckdb-1.4.3.tar.gz", hash = "sha256:fea43e03604c713e25a25211ada87d30cd2a044d8f27afab5deba26ac49e5268", size = 18478418, upload-time = "2025-12-09T10:59:22.945Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/f4/a38651e478fa41eeb8e43a0a9c0d4cd8633adea856e3ac5ac95124b0fdbf/duckdb-1.4.3-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:316711a9e852bcfe1ed6241a5f654983f67e909e290495f3562cccdf43be8180", size = 29042272, upload-time = "2025-12-09T10:58:51.826Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/de/2cf171a66098ce5aeeb7371511bd2b3d7b73a2090603b0b9df39f8aaf814/duckdb-1.4.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9e625b2b4d52bafa1fd0ebdb0990c3961dac8bb00e30d327185de95b68202131", size = 15419343, upload-time = "2025-12-09T10:58:54.439Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/28/6b0a7830828d4e9a37420d87e80fe6171d2869a9d3d960bf5d7c3b8c7ee4/duckdb-1.4.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:130c6760f6c573f9c9fe9aba56adba0fab48811a4871b7b8fd667318b4a3e8da", size = 13748905, upload-time = "2025-12-09T10:58:56.656Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/4d/778628e194d63967870873b9581c8a6b4626974aa4fbe09f32708a2d3d3a/duckdb-1.4.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:20c88effaa557a11267706b01419c542fe42f893dee66e5a6daa5974ea2d4a46", size = 18487261, upload-time = "2025-12-09T10:58:58.866Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/5f/87e43af2e4a0135f9675449563e7c2f9b6f1fe6a2d1691c96b091f3904dd/duckdb-1.4.3-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1b35491db98ccd11d151165497c084a9d29d3dc42fc80abea2715a6c861ca43d", size = 20497138, upload-time = "2025-12-09T10:59:01.241Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/41/abec537cc7c519121a2a83b9a6f180af8915fabb433777dc147744513e74/duckdb-1.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:23b12854032c1a58d0452e2b212afa908d4ce64171862f3792ba9a596ba7c765", size = 12836056, upload-time = "2025-12-09T10:59:03.388Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/5a/8af5b96ce5622b6168854f479ce846cf7fb589813dcc7d8724233c37ded3/duckdb-1.4.3-cp314-cp314-win_arm64.whl", hash = "sha256:90f241f25cffe7241bf9f376754a5845c74775e00e1c5731119dc88cd71e0cb2", size = 13527759, upload-time = "2025-12-09T10:59:05.496Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "executing"
|
||||
version = "2.2.1"
|
||||
@@ -850,8 +865,10 @@ version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "backtesting" },
|
||||
{ name = "duckdb" },
|
||||
{ name = "jupyter" },
|
||||
{ name = "matplotlib" },
|
||||
{ name = "mplfinance" },
|
||||
{ name = "pandas" },
|
||||
{ name = "pandas-stubs" },
|
||||
{ name = "peewee" },
|
||||
@@ -861,8 +878,10 @@ dependencies = [
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "backtesting", specifier = "~=0.6.5" },
|
||||
{ name = "duckdb", specifier = ">=1.4.3" },
|
||||
{ name = "jupyter", specifier = "~=1.1.1" },
|
||||
{ name = "matplotlib", specifier = "~=3.10.8" },
|
||||
{ name = "mplfinance", specifier = ">=0.12.10b0" },
|
||||
{ name = "pandas", specifier = "~=2.3.3" },
|
||||
{ name = "pandas-stubs", specifier = "~=2.3.3" },
|
||||
{ name = "peewee", specifier = "~=3.19.0" },
|
||||
@@ -953,6 +972,19 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/f7/4a5e785ec9fbd65146a27b6b70b6cdc161a66f2024e4b04ac06a67f5578b/mistune-3.2.0-py3-none-any.whl", hash = "sha256:febdc629a3c78616b94393c6580551e0e34cc289987ec6c35ed3f4be42d0eee1", size = 53598, upload-time = "2025-12-23T11:36:33.211Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mplfinance"
|
||||
version = "0.12.10b0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "matplotlib" },
|
||||
{ name = "pandas" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6c/a9/34e7998d02fb58fae04f750444ce4e95e75f3a08dad17fb2d32098a97834/mplfinance-0.12.10b0.tar.gz", hash = "sha256:7da150b5851aa5119ad6e06b55e48338b619bb6773f1b85df5de67a5ffd917bf", size = 70117, upload-time = "2023-08-02T15:13:53.829Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/d9/31c436ea7673c21a5bf3fc747bc7f63377582dfe845c3004d3e46f9deee0/mplfinance-0.12.10b0-py3-none-any.whl", hash = "sha256:76d3b095f05ff35de730751649de063bea4064d0c49b21b6182c82997a7f52bb", size = 75016, upload-time = "2023-08-02T15:13:52.022Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "narwhals"
|
||||
version = "2.15.0"
|
||||
|
||||
Reference in New Issue
Block a user