diff --git a/.gitignore b/.gitignore index 57ffaad..5f95d9e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,172 +1,173 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control - -# uv -.venv/ -.uv/ -__pypackages__/ -.uvrc.local -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/latest/usage/project/#working-with-version-control -.pdm.toml -.pdm-python -.pdm-build/ - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ - -*.ipynb -uv.lock -output +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control + +# uv +.venv/ +.uv/ +__pypackages__/ +.uvrc.local +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +*.ipynb +uv.lock +output +datadir \ No newline at end of file diff --git a/README.md b/README.md index 81947d0..d432469 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,274 @@ -# qka (快量化) +# QKA - 快量化 -快捷量化助手(Quick Quantitative Assistant)是一个简洁易用,可实操A股的量化交易框架。 +[![Python Version](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/) +[![License](https://img.shields.io/badge/license-MIT-green)](LICENSE) +[![PyPI](https://img.shields.io/pypi/v/qka)](https://pypi.org/project/qka/) + +**快捷量化助手(Quick Quantitative Assistant)** 是一个简洁易用、功能完整的A股量化交易框架,支持数据获取、策略回测、实盘交易等全流程量化交易功能。 + +## 特性 + +- 🚀 **简洁易用**: 统一的API设计,降低量化交易门槛 +- 📊 **数据丰富**: 支持Akshare数据源,提供多周期、多因子数据 +- 🔄 **高效回测**: 基于时间序列的回测引擎,支持多股票横截面处理 +- 💰 **实盘交易**: 集成QMT交易接口,支持实盘交易 +- 📈 **可视化**: 内置Plotly图表,提供交互式回测结果展示 +- 🔧 **模块化**: 高度模块化设计,易于扩展和维护 +- 📝 **文档完整**: 提供详细的API文档和使用示例 ## 安装 +### 从PyPI安装 + ```bash pip install qka ``` -## 使用方法 +### 从源码安装 + +```bash +git clone https://github.com/zsrl/qka.git +cd qka +pip install -e . +``` + +## 快速开始 -### QMTServer +### 1. 数据获取 ```python -from qka.server import QMTServer +import qka -server = QMTServer("YOUR_ACCOUNT_ID", "YOUR_QMT_PATH") -# 服务器启动时会打印生成的token -server.start() +# 创建数据对象 +data = qka.Data( + symbols=['000001.SZ', '600000.SH'], # 股票代码列表 + period='1d', # 日线数据 + adjust='qfq' # 前复权 +) + +# 获取数据 +df = data.get() +print(df.head()) ``` -### QMTClient +### 2. 策略开发 + +```python +import qka + +class MyStrategy(qka.Strategy): + def __init__(self): + super().__init__() + self.cash = 100000 # 初始资金 + + def on_bar(self, date, get): + """每个bar的处理逻辑""" + # 获取当前价格数据 + close_prices = get('close') + + # 示例策略:当000001.SZ价格低于10元时买入 + if '000001.SZ' in close_prices and close_prices['000001.SZ'] < 10: + # 买入1000股 + self.broker.buy('000001.SZ', close_prices['000001.SZ'], 1000) +``` -#### 查询 +### 3. 回测分析 ```python -from qka.client import QMTClient +import qka -client = QMTClient(token="服务器打印的token") -# 调用接口 -result = client.api("query_stock_asset") +# 创建策略实例 +strategy = MyStrategy() + +# 创建回测引擎 +backtest = qka.Backtest(data, strategy) + +# 运行回测 +backtest.run() + +# 绘制收益曲线 +backtest.plot("我的策略回测结果") ``` -#### 下单 +### 4. QMT实盘交易 + +#### 启动交易服务器 ```python -from qka.client import QMTClient +from qka.brokers.server import QMTServer + +# 创建交易服务器 +server = QMTServer( + account_id="YOUR_ACCOUNT_ID", # 你的账户ID + mini_qmt_path="YOUR_QMT_PATH" # QMT安装路径 +) + +# 启动服务器(会打印token供客户端使用) +server.start() +``` + +#### 使用交易客户端 + +```python +from qka.brokers.client import QMTClient + +# 创建交易客户端 +client = QMTClient( + base_url="http://localhost:8000", # 服务器地址 + token="服务器打印的token" # 访问令牌 +) + +# 查询账户资产 +assets = client.api("query_stock_asset") +print(assets) + +# 下单交易 from xtquant import xtconstant +result = client.api( + "order_stock", + stock_code='600000.SH', + order_type=xtconstant.STOCK_BUY, + order_volume=1000, + price_type=xtconstant.FIX_PRICE, + price=10.5 +) +``` + +## 核心模块 + +### 数据模块 (qka.Data) + +- **多数据源**: 支持Akshare、QMT等数据源 +- **缓存机制**: 自动缓存数据,提高访问效率 +- **并发下载**: 多线程并发下载,提升数据获取速度 +- **数据标准化**: 统一数据格式,便于策略开发 + +### 回测模块 (qka.Backtest) + +- **时间序列**: 基于时间序列的回测引擎 +- **多资产支持**: 支持多股票横截面数据处理 +- **交易记录**: 完整的交易记录和持仓跟踪 +- **可视化**: 交互式回测结果图表 + +### 策略模块 (qka.Strategy) -client = QMTClient(token="服务器打印的token", url="服务端地址") -# 调用接口 -result = client.api("order_stock", stock_code='600000.SH', order_type=xtconstant.STOCK_BUY, order_volume =1000, price_type=xtconstant.FIX_PRICE, price=10.5) +- **抽象基类**: 提供策略开发的标准接口 +- **事件驱动**: 基于bar的事件处理机制 +- **交易接口**: 内置买入卖出操作接口 +- **状态管理**: 自动管理资金和持仓状态 + +### 经纪商模块 (qka.brokers) + +- **QMT集成**: 完整的QMT交易接口封装 +- **客户端/服务器**: 支持远程交易服务 +- **订单管理**: 完整的订单生命周期管理 +- **错误处理**: 完善的错误处理和日志记录 + +### MCP模块 (qka.mcp) + +- **模型服务**: 提供模型上下文协议支持 +- **数据查询**: 支持Akshare数据查询工具 +- **异步处理**: 基于异步IO的高性能处理 + +### 工具模块 (qka.utils) + +- **日志系统**: 结构化日志记录,支持文件和控制台输出 +- **颜色输出**: 带颜色的控制台输出 +- **工具函数**: 各种实用工具函数 + +## 高级用法 + +### 自定义因子计算 + +```python +import pandas as pd + +def calculate_ma_factor(df): + """计算移动平均因子""" + df['ma5'] = df['close'].rolling(5).mean() + df['ma20'] = df['close'].rolling(20).mean() + return df + +data = qka.Data( + symbols=['000001.SZ'], + factor=calculate_ma_factor # 应用自定义因子 +) +``` + +### 批量数据处理 + +```python +# 批量处理多只股票 +symbols = ['000001.SZ', '600000.SH', '000002.SZ', '600036.SH'] +data = qka.Data( + symbols=symbols, + pool_size=20 # 增加并发数提高下载速度 +) +``` + +### 事件驱动策略 + +```python +class EventDrivenStrategy(qka.Strategy): + def on_bar(self, date, get): + close_prices = get('close') + volumes = get('volume') + + # 基于成交量的事件 + for symbol in close_prices.index: + if volumes[symbol] > volumes.mean() * 2: # 成交量放大 + self.broker.buy(symbol, close_prices[symbol], 100) ``` - +**注意**: 量化交易存在风险,请在充分了解风险的情况下使用本框架。作者不对使用本框架产生的任何投资损失负责。 diff --git "a/docs/A\350\202\241\345\270\202\345\234\272\350\257\201\345\210\270\344\273\243\347\240\201\345\221\275\345\220\215\350\247\204\345\210\231.md" "b/docs/A\350\202\241\345\270\202\345\234\272\350\257\201\345\210\270\344\273\243\347\240\201\345\221\275\345\220\215\350\247\204\345\210\231.md" deleted file mode 100644 index 30dd614..0000000 --- "a/docs/A\350\202\241\345\270\202\345\234\272\350\257\201\345\210\270\344\273\243\347\240\201\345\221\275\345\220\215\350\247\204\345\210\231.md" +++ /dev/null @@ -1,276 +0,0 @@ -# A股市场证券代码命名规则详解 - -## 概述 - -本文档详细记录了A股市场(沪深两市)所有证券品种的代码命名规则,包括股票、基金、指数等各类金融工具的代码分配方案。 - -**最后更新时间**: 2025年6月20日 - ---- - -## 1. 上海证券交易所(SH)代码规则 - -### 1.1 股票代码 - -#### 主板股票 -- **600001-600999**: 主板股票(最早批次) - - 示例:600000 浦发银行、600036 招商银行 -- **601001-601999**: 主板股票(第二批次) - - 示例:601318 中国平安、601398 工商银行 -- **603001-603999**: 主板股票(第三批次) - - 示例:603993 洛阳钼业、603883 老百姓 -- **605001-605999**: 主板股票(第四批次) - - 示例:605499 东鹏饮料 - -#### 科创板股票 -- **688001-688999**: 科创板股票 - - 示例:688001 华兴源创、688009 中国通号 - -### 1.2 基金代码 - -#### ETF基金 -- **501001-501999**: 跨境ETF - - 示例:501018 南方原油 -- **502001-502999**: 本市场股票ETF - - 示例:502010 易方达创业板ETF -- **510001-510999**: 本市场股票ETF(主要区间) - - 示例:510050 50ETF、510300 300ETF -- **511001-511999**: 债券ETF、货币ETF - - 示例:511010 国债ETF、511880 银华日利 -- **512001-512999**: 行业ETF、主题ETF - - 示例:512000 券商ETF、512170 医疗ETF -- **513001-513999**: 境外市场ETF - - 示例:513100 纳指ETF、513500 标普500 -- **515001-515999**: 创新型ETF - - 示例:515050 5GETF、515030 新能源ETF -- **516001-516999**: 创新型ETF(扩展) - - 示例:516160 新能源车ETF -- **518001-518999**: 黄金ETF等商品ETF - - 示例:518800 黄金ETF、518880 黄金ETF - -#### 开放式基金 -- **519001-519999**: 开放式基金 - - 示例:519056 海富通股票、519066 汇添富蓝筹 - -### 1.3 指数代码 - -#### 上证指数系列 -- **000001-000999**: 上证指数 - - 000001: 上证指数 - - 000002: 上证A股指数 - - 000003: 上证B股指数 - - 000016: 上证50指数 - - 000300: 沪深300指数(跨市场) - - 000905: 中证500指数(跨市场) - - 000852: 中证1000指数(跨市场) - -#### 中证指数系列 -- **000001-009999**: 中证指数公司发布的指数 - - 注:部分与上证指数重叠 - -### 1.4 债券代码 -- **010001-019999**: 国债 -- **020001-029999**: 企业债 -- **101001-109999**: 国债(新编码) -- **110001-119999**: 可转债 - - 示例:110001 万科转债 -- **113001-113999**: 可转债(扩展) -- **127001-127999**: 可转债(再扩展) - ---- - -## 2. 深圳证券交易所(SZ)代码规则 - -### 2.1 股票代码 - -#### 主板股票 -- **000001-000999**: 主板股票(最早批次) - - 示例:000001 平安银行、000002 万科A -- **001001-001999**: 主板股票(第二批次) - - 示例:001979 招商蛇口 -- **002001-002999**: 中小板股票(已并入主板) - - 示例:002001 新和成、002415 海康威视 -- **003001-003999**: 主板股票(第三批次) - - 示例:003816 中国广核 - -#### 创业板股票 -- **300001-300999**: 创业板股票 - - 示例:300001 特锐德、300059 东方财富 - -### 2.2 基金代码 - -#### ETF基金 -- **159001-159999**: ETF基金 - - 示例:159001 易方达深100ETF、159915 创业板ETF - -#### LOF基金 -- **160001-160999**: LOF基金(第一批次) - - 示例:160105 南方积配、160119 南方中证500 -- **161001-161999**: LOF基金(第二批次) - - 示例:161005 富国天惠、161725 招商中证白酒 -- **162001-162999**: LOF基金(第三批次) -- **163001-163999**: LOF基金(第四批次) -- **164001-164999**: LOF基金(第五批次) -- **165001-165999**: LOF基金(第六批次) -- **166001-166999**: LOF基金(第七批次) -- **167001-167999**: LOF基金(第八批次) -- **168001-168999**: LOF基金(第九批次) - -### 2.3 指数代码 - -#### 深证指数系列 -- **399001-399999**: 深证指数 - - 399001: 深证成指 - - 399002: 深成指R - - 399003: 成份B指 - - 399006: 创业板指 - - 399300: 沪深300指数(跨市场) - - 399905: 中证500指数(跨市场) - -### 2.4 债券代码 -- **100001-109999**: 国债 -- **111001-119999**: 企业债 -- **120001-129999**: 可转债 - - 示例:123001 蓝思转债 -- **128001-128999**: 可转债(扩展) - ---- - -## 3. 代码冲突分析 - -### 3.1 已知冲突案例 - -| 代码 | 上交所(SH) | 深交所(SZ) | 冲突类型 | -|------|------------|------------|----------| -| 000001 | 上证指数 | 平安银行 | 指数 vs 股票 | -| 000002 | 上证A股指数 | 万科A | 指数 vs 股票 | -| 000003 | 上证B股指数 | PT金田A | 指数 vs 股票 | -| 000300 | 沪深300指数 | - | 跨市场指数 | - -### 3.2 潜在冲突区间 - -| 代码区间 | 上交所用途 | 深交所用途 | 冲突风险 | -|----------|------------|------------|----------| -| 000001-000999 | 指数 | 主板股票 | 高 | -| 100001-109999 | 国债 | 国债 | 低(同类型) | -| 110001-119999 | 可转债 | 企业债 | 中 | - ---- - -## 4. 智能识别规则建议 - -### 4.1 无歧义代码 - -```python -# 明确归属上交所的代码前缀 -SH_PREFIXES = [ - '6', # 所有股票 - '50', '51', '52', # ETF基金 - '519', # 开放式基金 - '68' # 科创板 -] - -# 明确归属深交所的代码前缀 -SZ_PREFIXES = [ - '30', # 创业板 - '159', # ETF基金 - '16', # LOF基金 - '399' # 指数 -] -``` - -### 4.2 歧义代码处理 - -```python -# 存在歧义的代码前缀 -AMBIGUOUS_PREFIXES = [ - '000', # SH:指数 vs SZ:主板股票 - '001', # SH:指数 vs SZ:主板股票 - '002', # SH:指数 vs SZ:中小板股票 - '003', # SH:指数 vs SZ:主板股票 - '1', # 两市都有债券产品 -] -``` - -### 4.3 推荐处理策略 - -1. **股票优先原则**: 歧义代码默认当作股票处理 -2. **用途频次原则**: 按实际使用频率选择默认归属 -3. **显式指定原则**: 鼓励用户使用带后缀的完整代码 -4. **警告提示原则**: 遇到歧义代码时给出警告信息 - ---- - -## 5. 实现建议 - -### 5.1 代码标准化函数 - -```python -def normalize_security_code(code: str, prefer_type: str = 'stock') -> str: - """ - 证券代码标准化 - - Args: - code: 原始代码 (6位数字或带后缀) - prefer_type: 歧义时的偏好类型 ('stock', 'index', 'fund') - - Returns: - 标准化后的代码 (格式: XXXXXX.XX) - - Raises: - ValueError: 无法识别的代码格式 - """ - pass -``` - -### 5.2 歧义检测函数 - -```python -def detect_code_ambiguity(code: str) -> List[Dict]: - """ - 检测代码歧义 - - Returns: - 可能的证券信息列表 - [ - { - 'code': '000001.SH', - 'name': '上证指数', - 'type': 'index', - 'exchange': 'SH' - }, - { - 'code': '000001.SZ', - 'name': '平安银行', - 'type': 'stock', - 'exchange': 'SZ' - } - ] - """ - pass -``` - ---- - -## 6. 参考资料 - -- [上海证券交易所官网](https://www.sse.com.cn/) -- [深圳证券交易所官网](https://www.szse.cn/) -- [中证指数有限公司](https://www.csindex.com.cn/) -- 各大数据服务商API文档 - ---- - -## 7. 更新日志 - -- 2025-06-20: 初始版本,整理基础命名规则 -- TODO: 补充港股通、债券等更多品种 -- TODO: 验证部分代码区间的准确性 -- TODO: 添加实际代码冲突案例 - ---- - -**注意**: -1. 本文档基于公开资料整理,部分细节可能存在变化 -2. 建议在实际应用前验证关键代码区间 -3. 监管规则可能调整,建议定期更新此文档 diff --git a/docs/api/brokers.md b/docs/api/brokers.md new file mode 100644 index 0000000..aae3208 --- /dev/null +++ b/docs/api/brokers.md @@ -0,0 +1,166 @@ +# 交易模块 API 参考 + +QKA 的交易接口模块,提供 QMT 交易服务器的客户端和服务器端实现。 + +## qka.brokers.QMTClient + +QMT 交易客户端类,提供与 QMT 交易服务器的通信接口。 + +::: qka.brokers.client.QMTClient + options: + show_root_heading: false + show_source: true + members_order: source + heading_level: 3 + +### 使用示例 + +```python +from qka.brokers.client import QMTClient + +# 创建交易客户端 +client = QMTClient( + base_url="http://localhost:8000", + token="服务器打印的token" +) + +# 调用交易接口 +assets = client.api("query_stock_asset") +print(assets) +``` + +## qka.brokers.QMTServer + +QMT 交易服务器类,将 QMT 交易接口封装为 RESTful API。 + +::: qka.brokers.server.QMTServer + options: + show_root_heading: false + show_source: true + members_order: source + heading_level: 3 + +### 使用示例 + +```python +from qka.brokers.server import QMTServer + +# 创建交易服务器 +server = QMTServer( + account_id="YOUR_ACCOUNT_ID", + mini_qmt_path="YOUR_QMT_PATH" +) + +# 启动服务器 +server.start() +``` + +## qka.brokers.trade + +交易执行相关类和函数,包含订单、交易和持仓管理。 + +::: qka.brokers.trade + options: + show_root_heading: false + show_source: true + members_order: source + heading_level: 3 + +### 主要组件 + +#### Order 类 +订单对象,表示一个交易订单。 + +#### Trade 类 +交易记录,表示一个已成交的交易。 + +#### Position 类 +持仓信息,表示一个持仓头寸。 + +#### create_trader 函数 +创建 QMT 交易对象的便捷函数。 + +### 使用示例 + +```python +from qka.brokers.trade import create_trader, Order + +# 创建交易对象 +trader, account = create_trader(account_id, mini_qmt_path) + +# 创建订单 +order = Order( + symbol='000001.SZ', + side='buy', + quantity=1000, + order_type='market' +) +``` + +## 模块导入方式 + +交易模块需要从子模块导入: + +```python +# 客户端 +from qka.brokers.client import QMTClient + +# 服务器 +from qka.brokers.server import QMTServer + +# 交易执行 +from qka.brokers.trade import create_trader, Order, Trade, Position +``` + +## 工作流程 + +### 1. 启动交易服务器 + +```python +from qka.brokers.server import QMTServer + +server = QMTServer( + account_id="123456789", + mini_qmt_path="D:/qmt" +) +server.start() # 会打印token供客户端使用 +``` + +### 2. 使用交易客户端 + +```python +from qka.brokers.client import QMTClient + +client = QMTClient( + base_url="http://localhost:8000", + token="服务器打印的token" +) + +# 查询账户信息 +assets = client.api("query_stock_asset") + +# 下单交易 +from xtquant import xtconstant +result = client.api( + "order_stock", + stock_code='600000.SH', + order_type=xtconstant.STOCK_BUY, + order_volume=1000, + price_type=xtconstant.FIX_PRICE, + price=10.5 +) +``` + +## 注意事项 + +1. **QMT 依赖**: 需要安装 QMT 并正确配置环境 +2. **网络连接**: 确保服务器和客户端网络连通 +3. **权限验证**: 使用 token 进行身份验证 +4. **错误处理**: 妥善处理网络错误和交易失败 + +## 相关链接 + +- [用户指南 - 实盘交易](../../user-guide/trading.md) +- [核心模块 API](core.md) +- [工具模块 API](utils.md) +- [xtquant 文档](https://github.com/ShiMiaoYS/xtquant) \ No newline at end of file diff --git a/docs/api/brokers/index.md b/docs/api/brokers/index.md deleted file mode 100644 index 72278d5..0000000 --- a/docs/api/brokers/index.md +++ /dev/null @@ -1,106 +0,0 @@ -# Brokers 模块 - -QKA系统的交易接口模块,提供与不同券商和交易平台的接口封装。 - -## 模块列表 - -### [client.py](client.md) -交易客户端接口,提供统一的交易API封装。 - -**主要类:** -- `BrokerClient` - 基础交易客户端 -- `SimulatedClient` - 模拟交易客户端 -- `LiveClient` - 实盘交易客户端 - -**核心功能:** -- 账户信息查询 -- 订单管理 -- 持仓查询 -- 交易执行 - -### [server.py](server.md) -交易服务器实现,处理交易请求和订单路由。 - -**主要类:** -- `BrokerServer` - 交易服务器 -- `OrderRouter` - 订单路由器 -- `RiskManager` - 风险管理器 - -**核心功能:** -- 订单路由 -- 风险控制 -- 执行监控 -- 状态管理 - -### [trade.py](trade.md) -交易执行模块,处理具体的交易逻辑。 - -**主要类:** -- `Trade` - 交易记录 -- `Order` - 订单对象 -- `Position` - 持仓信息 -- `Portfolio` - 投资组合 - -**核心功能:** -- 交易执行 -- 持仓管理 -- 盈亏计算 -- 组合分析 - -## 使用示例 - -```python -from qka.brokers import BrokerClient, Order - -# 创建交易客户端 -client = BrokerClient(broker='simulation') - -# 连接到券商 -client.connect() - -# 查询账户信息 -account = client.get_account() -print(f"可用资金: {account.available_cash}") - -# 创建订单 -order = Order( - symbol='AAPL', - side='buy', - quantity=100, - order_type='market' -) - -# 提交订单 -order_id = client.submit_order(order) -print(f"订单ID: {order_id}") - -# 查询订单状态 -status = client.get_order_status(order_id) -print(f"订单状态: {status}") -``` - -## 支持的券商 - -- **模拟交易** - 用于回测和策略验证 -- **Interactive Brokers** - 专业交易平台 -- **TD Ameritrade** - 美股交易 -- **富途证券** - 港美股交易 -- **华泰证券** - A股交易 - -## 架构图 - -```mermaid -graph TD - A[Strategy] --> B[BrokerClient] - B --> C[BrokerServer] - C --> D[OrderRouter] - C --> E[RiskManager] - D --> F[Exchange/Broker] - E --> D - - G[Trade] --> H[Position] - H --> I[Portfolio] - B --> G -``` - -Brokers模块是QKA系统与外部交易系统的桥梁,确保策略能够安全、高效地执行交易。 diff --git a/docs/api/core.md b/docs/api/core.md new file mode 100644 index 0000000..df669ad --- /dev/null +++ b/docs/api/core.md @@ -0,0 +1,124 @@ +# 核心模块 API 参考 + +QKA 的核心功能模块,包含数据管理、回测引擎、策略基类和虚拟经纪商。 + +## qka.Data + +数据管理类,负责股票数据的获取、缓存和管理。 + +::: qka.core.data.Data + options: + show_root_heading: false + show_source: true + members_order: source + heading_level: 3 + +### 使用示例 + +```python +import qka + +# 创建数据对象 +data = qka.Data( + symbols=['000001.SZ', '600000.SH'], + period='1d', + adjust='qfq' +) + +# 获取数据 +df = data.get() +print(df.head()) +``` + +## qka.Backtest + +回测引擎类,提供基于时间序列的回测功能。 + +::: qka.core.backtest.Backtest + options: + show_root_heading: false + show_source: true + members_order: source + heading_level: 3 + +### 使用示例 + +```python +# 运行回测 +strategy = MyStrategy() +backtest = qka.Backtest(data, strategy) +backtest.run() + +# 可视化结果 +backtest.plot("我的策略回测结果") +``` + +## qka.Strategy + +策略抽象基类,所有自定义策略都应该继承此类。 + +::: qka.core.strategy.Strategy + options: + show_root_heading: false + show_source: true + members_order: source + heading_level: 3 + +### 使用示例 + +```python +class MyStrategy(qka.Strategy): + def __init__(self): + super().__init__() + self.ma_short = 5 + self.ma_long = 20 + + def on_bar(self, date, get): + close_prices = get('close') + # 策略逻辑... +``` + +## qka.Broker + +虚拟交易经纪商类,管理资金、持仓和交易记录。 + +::: qka.core.broker.Broker + options: + show_root_heading: false + show_source: true + members_order: source + heading_level: 3 + +### 使用示例 + +```python +# 在策略中使用 +class MyStrategy(qka.Strategy): + def on_bar(self, date, get): + close_prices = get('close') + for symbol in close_prices.index: + if self.should_buy(symbol, close_prices[symbol]): + self.broker.buy(symbol, close_prices[symbol], 100) +``` + +## 模块导入方式 + +根据 [`qka/__init__.py`](../../qka/__init__.py) 的配置,所有核心模块都可以直接从 `qka` 包导入: + +```python +import qka + +# 直接使用 +data = qka.Data(...) +backtest = qka.Backtest(...) +strategy = qka.Strategy(...) # 作为基类 +broker = qka.Broker(...) +``` + +## 相关链接 + +- [用户指南 - 数据获取](../../user-guide/data.md) +- [用户指南 - 回测分析](../../user-guide/backtest.md) +- [快速开始 - 第一个策略](../../getting-started/first-strategy.md) +- [交易模块 API](../brokers.md) +- [工具模块 API](../utils.md) \ No newline at end of file diff --git a/docs/api/core/config.md b/docs/api/core/config.md deleted file mode 100644 index b362537..0000000 --- a/docs/api/core/config.md +++ /dev/null @@ -1,200 +0,0 @@ -# Config API 参考 - -::: qka.core.config - options: - show_root_heading: true - show_source: true - heading_level: 2 - members_order: source - show_signature_annotations: true - separate_signature: true - -## 配置示例 - -### 基本用法 - -```python -from qka.core.config import Config - -# 创建配置实例 -config = Config() - -# 从文件加载配置 -config.load_from_file('config.yaml') - -# 获取配置值 -database_url = config.get('database.url', 'sqlite:///default.db') -debug_mode = config.get('debug', False) - -# 设置配置值 -config.set('api.timeout', 30) -config.set('logging.level', 'INFO') -``` - -### 环境变量配置 - -```python -import os - -# 设置环境变量 -os.environ['QKA_DEBUG'] = 'true' -os.environ['QKA_DATABASE_URL'] = 'postgresql://localhost/qka' - -# 加载环境变量配置 -config = Config() -config.load_from_env() - -# 访问配置 -debug = config.get('debug') # True -db_url = config.get('database.url') # postgresql://localhost/qka -``` - -### 配置文件示例 - -#### YAML格式 (config.yaml) - -```yaml -# QKA量化系统配置文件 - -# 数据库配置 -database: - url: "sqlite:///qka.db" - pool_size: 10 - echo: false - -# API配置 -api: - host: "0.0.0.0" - port: 8000 - timeout: 30 - rate_limit: 100 - -# 日志配置 -logging: - level: "INFO" - format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s" - file: "logs/qka.log" - max_size: "10MB" - backup_count: 5 - -# 交易配置 -trading: - commission: 0.001 - slippage: 0.0005 - initial_capital: 100000 - -# 风险管理 -risk: - max_position_size: 0.1 - max_drawdown: 0.2 - stop_loss: 0.05 - -# 策略配置 -strategy: - default_lookback: 252 - rebalance_frequency: "monthly" - -# 通知配置 -notifications: - email: - enabled: false - smtp_server: "smtp.gmail.com" - port: 587 - wechat: - enabled: false - webhook_url: "" -``` - -#### JSON格式 (config.json) - -```json -{ - "database": { - "url": "sqlite:///qka.db", - "pool_size": 10, - "echo": false - }, - "api": { - "host": "0.0.0.0", - "port": 8000, - "timeout": 30 - }, - "logging": { - "level": "INFO", - "file": "logs/qka.log" - } -} -``` - -### 配置验证 - -```python -from qka.core.config import Config - -config = Config() - -# 定义配置验证规则 -validation_rules = { - 'database.url': {'required': True, 'type': str}, - 'api.port': {'required': True, 'type': int, 'min': 1, 'max': 65535}, - 'trading.commission': {'type': float, 'min': 0, 'max': 1}, - 'logging.level': {'type': str, 'choices': ['DEBUG', 'INFO', 'WARNING', 'ERROR']} -} - -# 验证配置 -try: - config.validate(validation_rules) - print("配置验证通过") -except ConfigError as e: - print(f"配置验证失败: {e}") -``` - -### 动态配置更新 - -```python -# 监听配置文件变化 -config.watch_file('config.yaml', auto_reload=True) - -# 注册配置变更回调 -@config.on_change('database.url') -def on_database_change(old_value, new_value): - print(f"数据库配置从 {old_value} 更改为 {new_value}") - # 重新初始化数据库连接 - reconnect_database(new_value) - -# 手动重新加载配置 -config.reload() -``` - -### 配置模板生成 - -```python -# 生成示例配置文件 -config.create_sample_config('sample_config.yaml') - -# 生成配置模板 -template = config.get_config_template() -print(template) -``` - -## 最佳实践 - -1. **配置文件管理** - - 使用版本控制管理配置模板 - - 敏感信息使用环境变量 - - 不同环境使用不同配置文件 - -2. **配置验证** - - 应用启动时验证必需配置 - - 定义清晰的验证规则 - - 提供有意义的错误信息 - -3. **配置更新** - - 谨慎使用动态配置更新 - - 关键配置变更需要重启服务 - - 记录配置变更日志 - -4. **安全考虑** - - 敏感配置信息加密存储 - - 限制配置文件访问权限 - - 审计配置变更操作 diff --git a/docs/api/core/events.md b/docs/api/core/events.md deleted file mode 100644 index 5f05ddc..0000000 --- a/docs/api/core/events.md +++ /dev/null @@ -1,253 +0,0 @@ -# Events API 参考 - -::: qka.core.events - options: - show_root_heading: true - show_source: true - heading_level: 2 - members_order: source - show_signature_annotations: true - separate_signature: true - -## 事件系统使用指南 - -### 基本概念 - -事件系统采用发布-订阅模式,支持: -- 事件发布和订阅 -- 异步事件处理 -- 事件过滤和转换 -- 事件统计和监控 - -### 基本用法 - -```python -from qka.core.events import EventBus, Event - -# 创建事件总线 -bus = EventBus() - -# 定义事件处理器 -def handle_order(event): - print(f"处理订单事件: {event}") - -# 订阅事件 -bus.subscribe('order_created', handle_order) - -# 发布事件 -event = Event('order_created', {'symbol': 'AAPL', 'quantity': 100}) -bus.publish(event) -``` - -### 预定义事件类型 - -#### MarketDataEvent - 市场数据事件 - -```python -from qka.core.events import MarketDataEvent - -# 创建市场数据事件 -event = MarketDataEvent( - symbol='AAPL', - timestamp=datetime.now(), - data={ - 'open': 150.0, - 'high': 152.0, - 'low': 149.0, - 'close': 151.0, - 'volume': 1000000 - } -) - -# 订阅市场数据事件 -@bus.subscribe('market_data') -def handle_market_data(event): - symbol = event.symbol - price = event.data['close'] - print(f"{symbol}: ${price}") -``` - -#### OrderEvent - 订单事件 - -```python -from qka.core.events import OrderEvent - -# 创建订单事件 -order_event = OrderEvent( - order_id='ORD_001', - symbol='AAPL', - side='buy', - quantity=100, - price=150.0, - order_type='limit', - status='pending' -) - -# 订阅订单事件 -@bus.subscribe('order') -def handle_order(event): - print(f"订单 {event.order_id}: {event.status}") -``` - -#### TradeEvent - 交易事件 - -```python -from qka.core.events import TradeEvent - -# 创建交易事件 -trade_event = TradeEvent( - trade_id='TRD_001', - order_id='ORD_001', - symbol='AAPL', - side='buy', - quantity=100, - price=150.5, - commission=0.15, - timestamp=datetime.now() -) - -# 订阅交易事件 -@bus.subscribe('trade') -def handle_trade(event): - print(f"交易完成: {event.symbol} {event.side} {event.quantity}@{event.price}") -``` - -### 高级功能 - -#### 异步事件处理 - -```python -import asyncio -from qka.core.events import EventBus - -# 创建支持异步的事件总线 -bus = EventBus(async_mode=True) - -# 异步事件处理器 -async def async_handler(event): - await asyncio.sleep(1) # 模拟异步操作 - print(f"异步处理事件: {event}") - -# 订阅异步处理器 -bus.subscribe('async_event', async_handler) - -# 发布事件(异步处理) -await bus.publish_async(Event('async_event', {'data': 'test'})) -``` - -#### 事件过滤 - -```python -# 带条件的事件订阅 -def price_filter(event): - return event.data.get('price', 0) > 100 - -bus.subscribe('market_data', handle_expensive_stocks, filter_func=price_filter) - -# 仅处理价格大于100的股票数据 -event = MarketDataEvent('AAPL', data={'price': 150}) -bus.publish(event) # 会被处理 - -event = MarketDataEvent('PENNY', data={'price': 5}) -bus.publish(event) # 不会被处理 -``` - -#### 事件转换 - -```python -# 事件转换器 -def price_transformer(event): - # 将价格转换为人民币 - if 'price' in event.data: - event.data['price_cny'] = event.data['price'] * 7.0 - return event - -bus.subscribe('market_data', handle_cny_price, transformer=price_transformer) -``` - -#### 批量事件处理 - -```python -# 批量事件处理器 -@bus.subscribe_batch('market_data', batch_size=10, timeout=5) -def handle_batch(events): - prices = [e.data['price'] for e in events] - avg_price = sum(prices) / len(prices) - print(f"批量处理 {len(events)} 条数据,平均价格: {avg_price}") - -# 发布多个事件 -for i in range(20): - event = MarketDataEvent(f'STOCK_{i}', data={'price': 100 + i}) - bus.publish(event) -``` - -### 事件统计和监控 - -```python -# 获取事件统计 -stats = bus.get_statistics() -print(f"总事件数: {stats['total_events']}") -print(f"订阅者数: {stats['total_subscribers']}") -print(f"事件类型分布: {stats['event_types']}") - -# 监控事件处理性能 -@bus.subscribe('performance_monitor') -def monitor_handler(event): - processing_time = event.processing_time - if processing_time > 1.0: # 超过1秒 - print(f"事件处理较慢: {processing_time:.2f}s") -``` - -### 错误处理 - -```python -# 错误处理器 -def error_handler(event, exception): - print(f"事件处理失败: {event}, 错误: {exception}") - # 记录错误日志或发送告警 - -bus.set_error_handler(error_handler) - -# 带重试的事件处理 -@bus.subscribe('critical_event', retry_count=3, retry_delay=1) -def critical_handler(event): - if random.random() < 0.5: - raise Exception("模拟处理失败") - print(f"关键事件处理成功: {event}") -``` - -### 事件持久化 - -```python -# 启用事件持久化 -bus.enable_persistence('events.db') - -# 重放历史事件 -bus.replay_events( - event_type='market_data', - start_time=datetime(2024, 1, 1), - end_time=datetime(2024, 1, 31) -) -``` - -## 最佳实践 - -1. **事件设计** - - 事件名称要清晰、一致 - - 事件数据结构要稳定 - - 避免事件过于频繁 - -2. **性能优化** - - 异步处理耗时操作 - - 合理使用批量处理 - - 监控事件处理性能 - -3. **错误处理** - - 处理器要有错误处理逻辑 - - 关键事件要有重试机制 - - 记录事件处理日志 - -4. **测试** - - 模拟事件进行单元测试 - - 测试异常情况处理 - - 性能测试和压力测试 diff --git a/docs/api/core/index.md b/docs/api/core/index.md deleted file mode 100644 index 2441756..0000000 --- a/docs/api/core/index.md +++ /dev/null @@ -1,98 +0,0 @@ -# Core 模块 - -QKA系统的核心功能模块,包含配置管理、事件系统、回测引擎等关键组件。 - -## 模块列表 - -### [config.py](config.md) -配置管理系统,支持多种配置源和动态配置更新。 - -**主要类:** -- `Config` - 主配置管理类 -- `ConfigError` - 配置相关异常 - -**核心功能:** -- 文件配置加载(YAML、JSON、TOML) -- 环境变量配置 -- 代码配置 -- 配置验证和模板生成 - -### [events.py](events.md) -事件驱动框架,提供发布-订阅模式的事件处理机制。 - -**主要类:** -- `Event` - 基础事件类 -- `EventBus` - 事件总线 -- `MarketDataEvent` - 市场数据事件 -- `OrderEvent` - 订单事件 -- `TradeEvent` - 交易事件 - -**核心功能:** -- 事件发布和订阅 -- 异步事件处理 -- 事件统计和监控 -- 内置交易相关事件 - -### [backtest.py](backtest.md) -回测引擎,提供策略回测的核心逻辑。 - -**主要功能:** -- 历史数据回测 -- 策略执行模拟 -- 性能指标计算 -- 结果分析和报告 - -### [data.py](data.md) -数据处理模块,负责市场数据的获取和处理。 - -**主要功能:** -- 数据源接入 -- 数据清洗和预处理 -- 数据缓存和存储 -- 数据格式转换 - -### [plot.py](plot.md) -绘图工具模块,提供回测结果和数据的可视化功能。 - -**主要功能:** -- 回测结果可视化 -- 技术指标图表 -- 交易信号展示 -- 性能分析图表 - -## 使用示例 - -```python -from qka.core import Config, EventBus -from qka.core.events import MarketDataEvent - -# 初始化配置 -config = Config() -config.load_from_file('config.yaml') - -# 创建事件总线 -bus = EventBus() - -# 注册事件处理器 -@bus.subscribe('market_data') -def handle_market_data(event): - print(f"收到市场数据: {event.data}") - -# 发布事件 -event = MarketDataEvent(symbol='AAPL', price=150.0, volume=1000) -bus.publish(event) -``` - -## 模块依赖关系 - -```mermaid -graph TD - A[config.py] --> D[backtest.py] - B[events.py] --> D - C[data.py] --> D - D --> E[plot.py] - A --> B - A --> C -``` - -Core模块是整个QKA系统的基础,其他模块都依赖于这些核心组件。 diff --git a/docs/api/index.md b/docs/api/index.md deleted file mode 100644 index e432986..0000000 --- a/docs/api/index.md +++ /dev/null @@ -1,73 +0,0 @@ -# API 参考 - -QKA量化回测系统的完整API参考文档。 - -## 模块概览 - -### 核心模块 - -- [**Core**](core/index.md) - 核心功能模块 - - [配置管理](core/config.md) - 系统配置管理 - - [事件系统](core/events.md) - 事件驱动框架 - - [回测引擎](core/backtest.md) - 回测核心逻辑 - - [数据处理](core/data.md) - 数据获取和处理 - - [绘图工具](core/plot.md) - 结果可视化 - -### 工具模块 - -- [**Utils**](utils/index.md) - 工具类模块 - - [日志系统](utils/logger.md) - 增强日志功能 - - [通用工具](utils/tools.md) - 常用工具类 - - [动画工具](utils/anis.md) - 动画显示工具 - - [通用函数](utils/util.md) - 通用辅助函数 - -### 交易模块 - -- [**Brokers**](brokers/index.md) - 交易接口模块 - - [交易客户端](brokers/client.md) - 交易客户端接口 - - [交易服务器](brokers/server.md) - 交易服务器实现 - - [交易执行](brokers/trade.md) - 交易执行逻辑 - -### MCP模块 - -- [**MCP**](mcp/index.md) - Model Context Protocol模块 - - [API接口](mcp/api.md) - MCP API定义 - - [服务器实现](mcp/server.md) - MCP服务器 - -## 快速导航 - -- [配置系统 API](core/config.md) - 管理系统配置 -- [事件系统 API](core/events.md) - 事件驱动编程 -- [日志系统 API](utils/logger.md) - 结构化日志记录 -- [工具类 API](utils/tools.md) - 通用工具函数 - -## 使用示例 - -```python -from qka.core import Config, EventBus -from qka.utils import Logger, cache, timeit - -# 配置管理 -config = Config() -config.load_from_file('config.yaml') - -# 事件系统 -bus = EventBus() -bus.subscribe('market_data', handler) - -# 日志记录 -logger = Logger() -logger.info("系统启动") - -# 工具使用 -@cache(ttl=300) -@timeit -def expensive_function(): - pass -``` - -## 版本信息 - -当前文档对应QKA系统版本:`1.0.0` - -API文档会随代码更新自动同步,确保文档的准确性和时效性。 diff --git a/docs/api/mcp/index.md b/docs/api/mcp/index.md deleted file mode 100644 index 5c4a2e1..0000000 --- a/docs/api/mcp/index.md +++ /dev/null @@ -1,83 +0,0 @@ -# MCP 模块 - -QKA系统的Model Context Protocol模块,提供与AI模型的标准化接口。 - -## 模块列表 - -### [api.py](api.md) -MCP API定义,提供标准的模型上下文协议接口。 - -**主要类:** -- `MCPServer` - MCP服务器 -- `MCPClient` - MCP客户端 -- `ContextManager` - 上下文管理器 - -**核心功能:** -- 模型接口标准化 -- 上下文管理 -- 会话管理 -- 数据交换 - -### [server.py](server.md) -MCP服务器实现,处理模型请求和响应。 - -**主要类:** -- `ModelServer` - 模型服务器 -- `RequestHandler` - 请求处理器 -- `ResponseFormatter` - 响应格式化器 - -**核心功能:** -- 请求路由 -- 模型调用 -- 结果处理 -- 错误处理 - -## 使用示例 - -```python -from qka.mcp import MCPServer, MCPClient - -# 启动MCP服务器 -server = MCPServer(port=8080) -server.start() - -# 创建MCP客户端 -client = MCPClient(server_url='http://localhost:8080') - -# 发送模型请求 -response = client.request({ - 'model': 'strategy_advisor', - 'context': { - 'portfolio': portfolio_data, - 'market_data': market_data - }, - 'query': '分析当前投资组合风险' -}) - -print(response['advice']) -``` - -## MCP协议特性 - -- **标准化接口** - 统一的模型调用方式 -- **上下文感知** - 智能的上下文管理 -- **异步处理** - 支持异步模型调用 -- **可扩展性** - 易于添加新的模型 - -## 架构图 - -```mermaid -graph TD - A[QKA Strategy] --> B[MCP Client] - B --> C[MCP Server] - C --> D[Model Router] - D --> E[AI Model 1] - D --> F[AI Model 2] - D --> G[AI Model N] - - H[Context Manager] --> C - I[Request Handler] --> C - J[Response Formatter] --> C -``` - -MCP模块为QKA系统提供了与AI模型交互的标准化方式,支持策略智能化和决策辅助。 diff --git a/docs/api/utils.md b/docs/api/utils.md new file mode 100644 index 0000000..b7cfd05 --- /dev/null +++ b/docs/api/utils.md @@ -0,0 +1,97 @@ +# 工具模块 API 参考 + +QKA 的工具模块,提供日志系统、颜色输出和各种实用工具函数。 + +## qka.utils.logger + +日志系统模块,提供结构化的日志记录功能。 + +::: qka.utils.logger + options: + show_root_heading: false + show_source: true + members_order: source + heading_level: 3 + +### 主要功能 + +#### create_logger 函数 +创建增强的日志记录器,支持控制台和文件输出。 + +#### StructuredLogger 类 +结构化日志记录器,支持附加额外字段。 + +#### WeChatHandler 类 +微信消息处理器,支持通过企业微信机器人发送日志。 + +### 使用示例 + +```python +from qka.utils.logger import create_logger + +# 创建日志记录器 +logger = create_logger( + name='my_strategy', + level='DEBUG', + console_output=True, + file_output=True, + log_dir='my_logs' +) + +# 记录日志 +logger.info("策略开始运行") +logger.error("交易失败", symbol='000001.SZ', price=10.5) +``` + +## qka.utils.util + +通用工具函数模块。 + +::: qka.utils.util + options: + show_root_heading: false + show_source: true + members_order: source + heading_level: 3 + +## qka.utils.anis + +ANSI 颜色代码工具,提供带颜色的控制台输出。 + +::: qka.utils.anis + options: + show_root_heading: false + show_source: true + members_order: source + heading_level: 3 + +### 使用示例 + +```python +from qka.utils.anis import RED, GREEN, BLUE, RESET + +print(f"{RED}错误信息{RESET}") +print(f"{GREEN}成功信息{RESET}") +print(f"{BLUE}提示信息{RESET}") +``` + +## 模块导入方式 + +工具模块需要从子模块导入: + +```python +# 日志系统 +from qka.utils.logger import create_logger, StructuredLogger + +# 工具函数 +from qka.utils.util import timestamp_to_datetime_string + +# 颜色输出 +from qka.utils.anis import RED, GREEN, BLUE, RESET +``` + +## 相关链接 + +- [核心模块 API](core.md) +- [交易模块 API](brokers.md) +- [用户指南 - 日志系统](../../user-guide/logging.md) \ No newline at end of file diff --git a/docs/api/utils/index.md b/docs/api/utils/index.md deleted file mode 100644 index e9e6286..0000000 --- a/docs/api/utils/index.md +++ /dev/null @@ -1,97 +0,0 @@ -# Utils 模块 - -QKA系统的工具类模块,提供日志、缓存、计时、格式化等通用功能。 - -## 模块列表 - -### [logger.py](logger.md) -增强日志系统,支持彩色输出、结构化日志、文件轮转等功能。 - -**主要类:** -- `Logger` - 增强日志记录器 -- `ColorFormatter` - 彩色日志格式化器 -- `StructuredLogger` - 结构化日志记录器 - -**核心功能:** -- 彩色控制台输出 -- 结构化日志记录 -- 文件轮转和压缩 -- 微信通知集成 -- 性能监控日志 - -### [tools.py](tools.md) -通用工具类,包含缓存、计时器、装饰器等实用功能。 - -**主要功能:** -- `@cache` - 函数结果缓存 -- `@timeit` - 函数执行计时 -- `@retry` - 函数重试机制 -- `FileHelper` - 文件操作工具 -- `FormatHelper` - 格式化工具 -- `ValidationHelper` - 数据验证工具 - -### [anis.py](anis.md) -动画显示工具,提供进度条和动画效果。 - -**主要功能:** -- 进度条显示 -- 动画效果 -- 状态指示器 - -### [util.py](util.md) -通用辅助函数集合。 - -**主要功能:** -- 数据处理函数 -- 字符串操作 -- 数学计算工具 - -## 使用示例 - -```python -from qka.utils import Logger, cache, timeit, FileHelper - -# 日志记录 -logger = Logger() -logger.info("系统启动", extra={'module': 'main'}) - -# 缓存装饰器 -@cache(ttl=300) -def get_market_data(symbol): - # 模拟耗时操作 - return fetch_data(symbol) - -# 计时装饰器 -@timeit -def expensive_calculation(): - # 耗时计算 - pass - -# 文件操作 -helper = FileHelper() -helper.ensure_dir('logs') -helper.safe_write('config.json', data) -``` - -## 工具类关系图 - -```mermaid -graph TD - A[logger.py] --> E[日志系统] - B[tools.py] --> F[缓存工具] - B --> G[计时工具] - B --> H[文件工具] - B --> I[验证工具] - C[anis.py] --> J[动画工具] - D[util.py] --> K[通用函数] - - E --> L[应用模块] - F --> L - G --> L - H --> L - I --> L - J --> L - K --> L -``` - -Utils模块为整个QKA系统提供基础工具支持,提高开发效率和代码质量。 diff --git a/docs/api/utils/logger.md b/docs/api/utils/logger.md deleted file mode 100644 index d317371..0000000 --- a/docs/api/utils/logger.md +++ /dev/null @@ -1,317 +0,0 @@ -# Logger API 参考 - -::: qka.utils.logger - options: - show_root_heading: true - show_source: true - heading_level: 2 - members_order: source - show_signature_annotations: true - separate_signature: true - -## 日志系统使用指南 - -### 基本用法 - -```python -from qka.utils.logger import Logger - -# 创建日志实例 -logger = Logger(name='my_app') - -# 基本日志记录 -logger.debug("调试信息") -logger.info("普通信息") -logger.warning("警告信息") -logger.error("错误信息") -logger.critical("严重错误") -``` - -### 彩色日志输出 - -```python -from qka.utils.logger import Logger, ColorFormatter - -# 启用彩色输出 -logger = Logger(name='colorful_app', enable_color=True) - -# 日志会以不同颜色显示 -logger.info("这是蓝色的信息日志") -logger.warning("这是黄色的警告日志") -logger.error("这是红色的错误日志") -``` - -### 结构化日志 - -```python -from qka.utils.logger import StructuredLogger - -# 创建结构化日志记录器 -logger = StructuredLogger(name='structured_app') - -# 记录结构化日志 -logger.info("用户登录", extra={ - 'user_id': 12345, - 'username': 'john_doe', - 'ip_address': '192.168.1.100', - 'user_agent': 'Mozilla/5.0...' -}) - -logger.error("数据库连接失败", extra={ - 'database': 'mysql', - 'host': 'localhost', - 'port': 3306, - 'error_code': 2003 -}) -``` - -### 文件日志配置 - -```python -# 配置文件日志 -logger = Logger( - name='file_app', - log_file='logs/app.log', - max_size='10MB', - backup_count=5, - compression=True -) - -# 日志会自动轮转和压缩 -for i in range(10000): - logger.info(f"日志消息 {i}") -``` - -### 微信通知集成 - -```python -# 配置微信通知 -logger = Logger( - name='notify_app', - wechat_webhook='https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_KEY' -) - -# 发送重要通知到微信 -logger.critical("系统出现严重错误", notify_wechat=True) -logger.error("数据库连接失败", notify_wechat=True) -``` - -### 性能监控日志 - -```python -import time -from qka.utils.logger import Logger - -logger = Logger(name='perf_app') - -# 记录函数执行时间 -@logger.log_performance -def slow_function(): - time.sleep(2) - return "完成" - -# 记录代码块执行时间 -with logger.timer("数据处理"): - # 数据处理代码 - process_data() -``` - -### 日志过滤和格式化 - -```python -import logging -from qka.utils.logger import Logger - -# 自定义日志过滤器 -class SensitiveFilter(logging.Filter): - def filter(self, record): - # 过滤敏感信息 - if hasattr(record, 'msg'): - record.msg = record.msg.replace('password=', 'password=***') - return True - -# 应用过滤器 -logger = Logger(name='secure_app') -logger.logger.addFilter(SensitiveFilter()) - -# 自定义格式化器 -formatter = logging.Formatter( - '%(asctime)s | %(name)s | %(levelname)s | %(message)s', - datefmt='%Y-%m-%d %H:%M:%S' -) -logger.set_formatter(formatter) -``` - -### 上下文日志 - -```python -from contextvars import ContextVar -from qka.utils.logger import Logger - -# 定义上下文变量 -request_id = ContextVar('request_id', default='unknown') - -# 自定义格式化器 -class ContextFormatter(logging.Formatter): - def format(self, record): - record.request_id = request_id.get() - return super().format(record) - -# 配置日志 -logger = Logger(name='context_app') -formatter = ContextFormatter( - '%(asctime)s [%(request_id)s] %(name)s - %(levelname)s - %(message)s' -) -logger.set_formatter(formatter) - -# 使用上下文 -request_id.set('req_123456') -logger.info("处理请求") # 日志中会包含 request_id -``` - -### 异步日志 - -```python -import asyncio -from qka.utils.logger import Logger - -# 异步日志处理 -logger = Logger(name='async_app', async_mode=True) - -async def async_operation(): - logger.info("开始异步操作") - await asyncio.sleep(1) - logger.info("异步操作完成") - -# 运行异步代码 -asyncio.run(async_operation()) -``` - -### 日志分析和监控 - -```python -# 日志统计 -stats = logger.get_statistics() -print(f"总日志条数: {stats['total_logs']}") -print(f"错误日志数: {stats['error_count']}") -print(f"警告日志数: {stats['warning_count']}") - -# 设置日志阈值告警 -logger.set_error_threshold(100) # 错误数超过100时告警 -logger.set_warning_threshold(500) # 警告数超过500时告警 - -# 日志采样(高频日志场景) -logger.set_sampling_rate(0.1) # 只记录10%的日志 -``` - -### 多进程日志 - -```python -import multiprocessing -from qka.utils.logger import Logger - -def worker_process(worker_id): - # 每个进程创建独立的日志实例 - logger = Logger( - name=f'worker_{worker_id}', - log_file=f'logs/worker_{worker_id}.log' - ) - - for i in range(100): - logger.info(f"工作进程 {worker_id} 处理任务 {i}") - -# 启动多个工作进程 -processes = [] -for i in range(4): - p = multiprocessing.Process(target=worker_process, args=(i,)) - processes.append(p) - p.start() - -for p in processes: - p.join() -``` - -## 配置示例 - -### 日志配置文件 - -```yaml -# logging.yaml -version: 1 -disable_existing_loggers: false - -formatters: - standard: - format: '%(asctime)s [%(levelname)s] %(name)s: %(message)s' - datefmt: '%Y-%m-%d %H:%M:%S' - - detailed: - format: '%(asctime)s [%(levelname)s] %(name)s [%(filename)s:%(lineno)d] %(message)s' - datefmt: '%Y-%m-%d %H:%M:%S' - -handlers: - console: - class: logging.StreamHandler - level: INFO - formatter: standard - stream: ext://sys.stdout - - file: - class: logging.handlers.RotatingFileHandler - level: DEBUG - formatter: detailed - filename: logs/app.log - maxBytes: 10485760 # 10MB - backupCount: 5 - encoding: utf8 - -loggers: - qka: - level: DEBUG - handlers: [console, file] - propagate: false - -root: - level: WARNING - handlers: [console] -``` - -### 加载配置文件 - -```python -import logging.config -import yaml - -# 加载日志配置 -with open('logging.yaml', 'r') as f: - config = yaml.safe_load(f.read()) - logging.config.dictConfig(config) - -# 使用配置的日志器 -logger = logging.getLogger('qka.strategy') -logger.info("策略启动") -``` - -## 最佳实践 - -1. **日志级别使用** - - DEBUG: 详细的调试信息 - - INFO: 一般信息记录 - - WARNING: 警告但不影响运行 - - ERROR: 错误信息,影响功能 - - CRITICAL: 严重错误,可能导致程序终止 - -2. **结构化日志** - - 使用统一的日志格式 - - 包含足够的上下文信息 - - 便于日志分析和查询 - -3. **性能考虑** - - 避免在热点路径记录大量日志 - - 使用异步日志处理 - - 合理设置日志级别 - -4. **安全注意** - - 不要记录敏感信息 - - 使用日志过滤器清理敏感数据 - - 控制日志文件访问权限 diff --git a/docs/api/utils/tools.md b/docs/api/utils/tools.md deleted file mode 100644 index c199b96..0000000 --- a/docs/api/utils/tools.md +++ /dev/null @@ -1,468 +0,0 @@ -# Tools API 参考 - -::: qka.utils.tools - options: - show_root_heading: true - show_source: true - heading_level: 2 - members_order: source - show_signature_annotations: true - separate_signature: true - -## 工具类使用指南 - -### 缓存装饰器 - -#### 基本缓存 - -```python -from qka.utils.tools import cache - -# 简单缓存(内存) -@cache() -def expensive_function(x, y): - # 模拟耗时计算 - import time - time.sleep(2) - return x + y - -# 第一次调用需要2秒 -result1 = expensive_function(1, 2) # 耗时:2秒 - -# 第二次调用直接返回缓存结果 -result2 = expensive_function(1, 2) # 耗时:几乎为0 -``` - -#### 带TTL的缓存 - -```python -# 缓存有效期为5分钟 -@cache(ttl=300) -def get_stock_price(symbol): - # 模拟API调用 - return fetch_price_from_api(symbol) - -# 5分钟内重复调用返回缓存结果 -# 5分钟后会重新调用API -price = get_stock_price('AAPL') -``` - -#### 文件缓存 - -```python -# 使用文件缓存(持久化) -@cache(cache_type='file', cache_dir='cache') -def process_large_dataset(file_path): - # 处理大型数据集 - return expensive_data_processing(file_path) - -# 结果会保存到文件,程序重启后仍然有效 -result = process_large_dataset('data/large_file.csv') -``` - -#### Redis缓存 - -```python -# 使用Redis缓存(分布式) -@cache(cache_type='redis', redis_url='redis://localhost:6379/0') -def get_user_profile(user_id): - return fetch_user_from_database(user_id) - -# 多个进程/服务器可以共享缓存 -profile = get_user_profile(12345) -``` - -### 计时装饰器 - -#### 函数计时 - -```python -from qka.utils.tools import timeit - -# 简单计时 -@timeit -def data_processing(): - # 数据处理逻辑 - pass - -# 执行后会打印执行时间 -data_processing() # 输出: data_processing 执行时间: 1.23秒 -``` - -#### 详细计时信息 - -```python -# 详细计时信息 -@timeit(detailed=True) -def complex_calculation(n): - result = 0 - for i in range(n): - result += i * i - return result - -# 输出更详细的时间信息 -result = complex_calculation(1000000) -# 输出: complex_calculation(n=1000000) 执行时间: 0.15秒, 内存使用: +2.3MB -``` - -#### 性能监控 - -```python -# 性能监控和统计 -@timeit(monitor=True, threshold=1.0) -def api_call(endpoint): - # API调用 - return make_request(endpoint) - -# 如果执行时间超过阈值,会记录警告 -# 同时收集性能统计数据 -api_call('/api/data') - -# 获取性能统计 -stats = timeit.get_statistics() -print(f"平均执行时间: {stats['avg_time']}") -print(f"最大执行时间: {stats['max_time']}") -``` - -### 重试装饰器 - -#### 基本重试 - -```python -from qka.utils.tools import retry - -# 最多重试3次 -@retry(max_attempts=3) -def unstable_api_call(): - # 可能失败的API调用 - response = requests.get('https://api.example.com/data') - response.raise_for_status() - return response.json() - -# 如果失败会自动重试 -data = unstable_api_call() -``` - -#### 指定异常类型和重试间隔 - -```python -import requests - -# 只对特定异常重试,指定重试间隔 -@retry( - max_attempts=5, - exceptions=(requests.RequestException, ConnectionError), - delay=1.0, - backoff=2.0 -) -def robust_api_call(): - return requests.get('https://api.example.com/data').json() - -# 重试间隔:1秒、2秒、4秒、8秒 -data = robust_api_call() -``` - -#### 自定义重试条件 - -```python -# 自定义重试条件 -def should_retry(exception): - if isinstance(exception, requests.HTTPError): - return exception.response.status_code >= 500 - return True - -@retry(max_attempts=3, should_retry=should_retry) -def smart_api_call(): - response = requests.get('https://api.example.com/data') - response.raise_for_status() - return response.json() -``` - -### 文件操作工具 - -#### 基本文件操作 - -```python -from qka.utils.tools import FileHelper - -helper = FileHelper() - -# 确保目录存在 -helper.ensure_dir('logs') -helper.ensure_dir('data/processed') - -# 安全写入文件(原子操作) -data = {'key': 'value'} -helper.safe_write('config.json', data) - -# 安全读取文件 -config = helper.safe_read('config.json') - -# 备份文件 -helper.backup_file('important.txt') -``` - -#### 文件监控 - -```python -# 监控文件变化 -def on_file_change(file_path): - print(f"文件 {file_path} 已更改") - -helper.watch_file('config.json', on_file_change) - -# 监控目录变化 -def on_dir_change(event_type, file_path): - print(f"目录事件: {event_type} - {file_path}") - -helper.watch_directory('data', on_dir_change) -``` - -#### 文件压缩和解压 - -```python -# 压缩文件 -helper.compress_file('large_file.txt', 'compressed.gz') - -# 压缩目录 -helper.compress_directory('logs', 'logs_backup.tar.gz') - -# 解压文件 -helper.extract_file('backup.tar.gz', 'restore_dir') -``` - -### 格式化工具 - -#### 数据格式化 - -```python -from qka.utils.tools import FormatHelper - -formatter = FormatHelper() - -# 格式化数字 -formatted = formatter.format_number(1234567.89) -print(formatted) # 1,234,567.89 - -# 格式化百分比 -percentage = formatter.format_percentage(0.1234) -print(percentage) # 12.34% - -# 格式化文件大小 -size = formatter.format_size(1024*1024*1.5) -print(size) # 1.5 MB - -# 格式化时间段 -duration = formatter.format_duration(3661) -print(duration) # 1小时1分1秒 -``` - -#### 货币格式化 - -```python -# 格式化货币 -price = formatter.format_currency(1234.56, currency='USD') -print(price) # $1,234.56 - -price_cny = formatter.format_currency(8888.88, currency='CNY') -print(price_cny) # ¥8,888.88 -``` - -#### 日期时间格式化 - -```python -from datetime import datetime - -now = datetime.now() - -# 不同格式的日期时间 -print(formatter.format_datetime(now, 'full')) # 2024年1月15日 星期一 14:30:25 -print(formatter.format_datetime(now, 'date')) # 2024-01-15 -print(formatter.format_datetime(now, 'time')) # 14:30:25 -print(formatter.format_datetime(now, 'relative')) # 刚刚 -``` - -### 数据验证工具 - -#### 基本验证 - -```python -from qka.utils.tools import ValidationHelper - -validator = ValidationHelper() - -# 验证邮箱 -is_valid = validator.validate_email('user@example.com') -print(is_valid) # True - -# 验证手机号 -is_valid = validator.validate_phone('13812345678') -print(is_valid) # True - -# 验证身份证号 -is_valid = validator.validate_id_card('110101199001011234') -print(is_valid) # False (示例号码) -``` - -#### 数据结构验证 - -```python -# 验证字典结构 -schema = { - 'name': {'type': str, 'required': True}, - 'age': {'type': int, 'min': 0, 'max': 150}, - 'email': {'type': str, 'validator': validator.validate_email} -} - -data = { - 'name': 'John Doe', - 'age': 30, - 'email': 'john@example.com' -} - -is_valid, errors = validator.validate_dict(data, schema) -if not is_valid: - print("验证失败:", errors) -``` - -#### 自定义验证器 - -```python -# 自定义验证函数 -def validate_stock_symbol(symbol): - return isinstance(symbol, str) and symbol.isupper() and len(symbol) <= 6 - -# 注册自定义验证器 -validator.register_validator('stock_symbol', validate_stock_symbol) - -# 使用自定义验证器 -is_valid = validator.validate('AAPL', 'stock_symbol') -print(is_valid) # True -``` - -### 配置管理工具 - -#### 环境配置 - -```python -from qka.utils.tools import ConfigHelper - -config = ConfigHelper() - -# 获取环境变量(带默认值) -debug_mode = config.get_env('DEBUG', default=False, cast=bool) -api_timeout = config.get_env('API_TIMEOUT', default=30, cast=int) - -# 检查必需的环境变量 -required_vars = ['DATABASE_URL', 'SECRET_KEY'] -missing = config.check_required_env(required_vars) -if missing: - raise ValueError(f"缺少必需的环境变量: {missing}") -``` - -#### 配置合并 - -```python -# 合并多个配置源 -default_config = {'host': 'localhost', 'port': 8000, 'debug': False} -user_config = {'port': 9000, 'debug': True} -env_config = config.from_env_prefix('APP_') - -final_config = config.merge_configs(default_config, user_config, env_config) -print(final_config) # {'host': 'localhost', 'port': 9000, 'debug': True} -``` - -## 组合使用示例 - -### 完整的数据处理流水线 - -```python -from qka.utils.tools import cache, timeit, retry, FileHelper, FormatHelper - -# 组合多个装饰器 -@cache(ttl=3600) # 缓存1小时 -@timeit(detailed=True) # 详细计时 -@retry(max_attempts=3) # 最多重试3次 -def process_market_data(symbol, date): - """处理市场数据的完整流水线""" - - # 下载数据 - raw_data = download_data(symbol, date) - - # 数据清洗 - cleaned_data = clean_data(raw_data) - - # 数据分析 - analysis_result = analyze_data(cleaned_data) - - # 保存结果 - helper = FileHelper() - formatter = FormatHelper() - - output_file = f"analysis_{symbol}_{formatter.format_date(date)}.json" - helper.safe_write(output_file, analysis_result) - - return analysis_result - -# 使用 -result = process_market_data('AAPL', '2024-01-15') -``` - -### 批量数据处理 - -```python -import concurrent.futures -from qka.utils.tools import timeit, cache - -@cache(cache_type='redis') -def get_stock_data(symbol): - return fetch_stock_data(symbol) - -@timeit -def process_portfolio(symbols): - """并行处理投资组合数据""" - - with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor: - # 并行获取数据 - future_to_symbol = { - executor.submit(get_stock_data, symbol): symbol - for symbol in symbols - } - - results = {} - for future in concurrent.futures.as_completed(future_to_symbol): - symbol = future_to_symbol[future] - try: - data = future.result() - results[symbol] = data - except Exception as exc: - print(f'{symbol} 处理失败: {exc}') - - return results - -# 处理大型投资组合 -portfolio = ['AAPL', 'GOOGL', 'MSFT', 'AMZN', 'TSLA'] * 20 -results = process_portfolio(portfolio) -``` - -## 最佳实践 - -1. **缓存策略** - - 选择合适的缓存类型和TTL - - 注意缓存一致性问题 - - 监控缓存命中率 - -2. **性能监控** - - 对关键函数使用计时装饰器 - - 设置合理的性能阈值 - - 定期分析性能数据 - -3. **错误处理** - - 合理设置重试策略 - - 记录重试和失败信息 - - 区分可重试和不可重试的错误 - -4. **文件操作** - - 使用原子操作确保数据一致性 - - 定期备份重要文件 - - 监控磁盘空间使用 diff --git a/docs/enhanced_features_phase1.md b/docs/enhanced_features_phase1.md deleted file mode 100644 index a8b57e0..0000000 --- a/docs/enhanced_features_phase1.md +++ /dev/null @@ -1,472 +0,0 @@ -# QKA 增强功能文档 - 阶段1 - -## 概述 - -QKA 阶段1增强功能主要包括四个核心模块: -- 📋 **配置管理系统** - 统一的配置管理 -- 📡 **事件驱动框架** - 发布-订阅模式的事件系统 -- 📝 **增强日志系统** - 结构化和彩色日志 -- 🛠️ **基础工具类** - 通用工具函数和装饰器 - ---- - -## 📋 配置管理系统 - -### 快速开始 - -```python -import qka -from qka.core.config import load_config, create_sample_config - -# 创建示例配置文件 -create_sample_config('my_config.json') - -# 加载配置 -config = load_config('my_config.json') - -# 使用配置 -print(f"初始资金: {qka.config.backtest.initial_cash:,}") -print(f"数据源: {qka.config.data.default_source}") -``` - -### 配置结构 - -#### BacktestConfig - 回测配置 -```python -config.backtest.initial_cash = 1_000_000 # 初始资金 -config.backtest.commission_rate = 0.0003 # 手续费率 -config.backtest.slippage = 0.001 # 滑点率 -config.backtest.min_trade_amount = 100 # 最小交易股数 -config.backtest.max_position_ratio = 0.3 # 单只股票最大仓位比例 -config.backtest.benchmark = '000300.SH' # 基准指数 -``` - -#### DataConfig - 数据配置 -```python -config.data.default_source = 'akshare' # 默认数据源 -config.data.cache_enabled = True # 是否启用缓存 -config.data.cache_dir = './data_cache' # 缓存目录 -config.data.cache_expire_days = 7 # 缓存过期天数 -config.data.quality_check = True # 是否进行数据质量检查 -config.data.auto_download = True # 是否自动下载缺失数据 -``` - -#### TradingConfig - 交易配置 -```python -config.trading.server_host = '0.0.0.0' # 服务器地址 -config.trading.server_port = 8000 # 服务器端口 -config.trading.token_auto_generate = True # 自动生成token -config.trading.order_timeout = 30 # 订单超时时间(秒) -config.trading.max_retry_times = 3 # 最大重试次数 -config.trading.heartbeat_interval = 30 # 心跳间隔(秒) -``` - -### 环境变量支持 - -```bash -# 设置环境变量 -export QKA_INITIAL_CASH=2000000 -export QKA_DATA_SOURCE=qmt -export QKA_SERVER_PORT=9000 -``` - -### 配置文件示例 - -```json -{ - "backtest": { - "initial_cash": 1000000, - "commission_rate": 0.0003, - "slippage": 0.001 - }, - "data": { - "default_source": "akshare", - "cache_enabled": true, - "cache_dir": "./data_cache" - }, - "trading": { - "server_host": "0.0.0.0", - "server_port": 8000 - } -} -``` - ---- - -## 📡 事件驱动框架 - -### 快速开始 - -```python -from qka.core.events import EventType, event_handler, emit_event, start_event_engine - -# 启动事件引擎 -start_event_engine() - -# 定义事件处理器 -@event_handler(EventType.DATA_LOADED) -def on_data_loaded(event): - print(f"数据加载完成: {event.data}") - -# 发送事件 -emit_event(EventType.DATA_LOADED, {"symbol": "000001.SZ", "count": 1000}) -``` - -### 事件类型 - -#### 数据相关事件 -- `DATA_LOADED` - 数据加载完成 -- `DATA_ERROR` - 数据加载错误 - -#### 回测相关事件 -- `BACKTEST_START` - 回测开始 -- `BACKTEST_END` - 回测结束 -- `BACKTEST_ERROR` - 回测错误 - -#### 交易相关事件 -- `ORDER_CREATED` - 订单创建 -- `ORDER_FILLED` - 订单成交 -- `ORDER_CANCELLED` - 订单取消 -- `ORDER_ERROR` - 订单错误 - -#### 策略相关事件 -- `STRATEGY_START` - 策略开始 -- `STRATEGY_END` - 策略结束 -- `SIGNAL_GENERATED` - 信号生成 - -### 自定义事件处理器 - -```python -from qka.core.events import EventHandler, Event - -class MyEventHandler(EventHandler): - def handle(self, event: Event): - if event.event_type == EventType.ORDER_FILLED: - print(f"处理订单成交事件: {event.data}") - - def can_handle(self, event: Event) -> bool: - return event.event_type == EventType.ORDER_FILLED - -# 注册处理器 -handler = MyEventHandler() -event_engine.subscribe(EventType.ORDER_FILLED, handler) -``` - -### 事件统计 - -```python -# 获取事件统计信息 -stats = event_engine.get_statistics() -print(f"事件计数: {stats['event_count']}") -print(f"错误计数: {stats['error_count']}") -print(f"队列大小: {stats['queue_size']}") -``` - ---- - -## 📝 增强日志系统 - -### 快速开始 - -```python -from qka.utils.logger import create_logger, get_structured_logger - -# 创建彩色日志记录器 -logger = create_logger('my_app', colored_console=True) - -logger.debug("这是调试信息") -logger.info("这是普通信息") -logger.warning("这是警告信息") -logger.error("这是错误信息") -``` - -### 结构化日志 - -```python -# 创建结构化日志记录器 -struct_logger = get_structured_logger('my_app') - -# 记录结构化日志 -struct_logger.info("用户登录", - user_id=12345, - ip="192.168.1.100", - action="login") - -struct_logger.error("交易失败", - symbol="000001.SZ", - reason="余额不足", - amount=10000) -``` - -### 日志配置选项 - -```python -logger = create_logger( - name='my_app', # 日志记录器名称 - level='INFO', # 日志级别 - console_output=True, # 是否输出到控制台 - file_output=True, # 是否输出到文件 - log_dir='logs', # 日志文件目录 - max_file_size='10MB', # 最大文件大小 - backup_count=10, # 备份文件数量 - json_format=False, # 是否使用JSON格式 - colored_console=True # 控制台是否使用颜色 -) -``` - -### 微信通知 - -```python -from qka.utils.logger import add_wechat_handler - -# 添加微信通知处理器 -webhook_url = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_KEY" -add_wechat_handler(logger, webhook_url, level='ERROR') - -# 现在ERROR级别的日志会发送到微信 -logger.error("这条错误信息会发送到微信群") -``` - ---- - -## 🛠️ 基础工具类 - -### 缓存工具 - -```python -from qka.utils.tools import Cache - -# 创建缓存 -cache = Cache(max_size=1000, ttl=3600) # 最大1000条,1小时过期 - -# 使用缓存 -cache.set('key1', 'value1') -value = cache.get('key1') -print(f"缓存大小: {cache.size()}") -``` - -### 计时器工具 - -```python -from qka.utils.tools import Timer, timer - -# 使用上下文管理器 -with Timer() as t: - # 一些耗时操作 - time.sleep(1) -print(f"耗时: {t.elapsed():.3f}秒") - -# 使用装饰器 -@timer -def slow_function(): - time.sleep(1) - return "完成" - -result = slow_function() # 自动打印执行时间 -``` - -### 重试装饰器 - -```python -from qka.utils.tools import retry - -@retry(max_attempts=3, delay=1.0, backoff=2.0) -def unreliable_function(): - # 可能失败的函数 - import random - if random.random() < 0.7: - raise Exception("随机失败") - return "成功" - -result = unreliable_function() # 自动重试 -``` - -### 记忆化装饰器 - -```python -from qka.utils.tools import memoize - -@memoize(ttl=300) # 缓存5分钟 -def expensive_calculation(x, y): - time.sleep(1) # 模拟耗时计算 - return x * y - -result1 = expensive_calculation(10, 20) # 耗时1秒 -result2 = expensive_calculation(10, 20) # 从缓存返回,瞬间完成 -``` - -### 文件工具 - -```python -from qka.utils.tools import FileUtils - -# JSON文件操作 -data = {"key": "value"} -FileUtils.save_json(data, "data.json") -loaded_data = FileUtils.load_json("data.json") - -# Pickle文件操作 -FileUtils.save_pickle(data, "data.pkl") -loaded_data = FileUtils.load_pickle("data.pkl") - -# 文件信息 -size = FileUtils.get_file_size("data.json") -mtime = FileUtils.get_file_mtime("data.json") -``` - -### 格式化工具 - -```python -from qka.utils.tools import format_number, format_percentage, format_currency - -print(format_number(1234567.89)) # 1,234,567.89 -print(format_percentage(0.1234)) # 12.34% -print(format_currency(123456.78)) # ¥123,456.78 -``` - -### 验证工具 - -```python -from qka.utils.tools import ValidationUtils - -# 验证股票代码 -is_valid = ValidationUtils.is_valid_symbol("000001.SZ") # True -is_valid = ValidationUtils.is_valid_symbol("AAPL") # False - -# 验证正数 -is_positive = ValidationUtils.is_positive_number(100) # True -is_positive = ValidationUtils.is_positive_number(-10) # False - -# 验证日期范围 -is_valid_range = ValidationUtils.is_valid_date_range("2023-01-01", "2023-12-31") # True -``` - ---- - -## 🚀 集成使用示例 - -### 增强的策略类 - -```python -from qka.core.backtest import Strategy -from qka.core.events import EventType, emit_event -from qka.utils.logger import create_logger -from qka.utils.tools import timer - -class EnhancedStrategy(Strategy): - def __init__(self): - super().__init__() - self.logger = create_logger('strategy') - - def on_start(self, broker): - self.logger.info(f"策略启动: {self.name}") - emit_event(EventType.STRATEGY_START, {"strategy": self.name}) - - @timer - def on_bar(self, data, broker, current_date): - # 策略逻辑 - for symbol, df in data.items(): - if len(df) >= 20: - current_price = df['close'].iloc[-1] - ma20 = df['close'].rolling(20).mean().iloc[-1] - - if current_price > ma20: - emit_event(EventType.SIGNAL_GENERATED, { - "symbol": symbol, - "signal": "BUY", - "price": current_price - }) - broker.buy(symbol, 0.3, current_price) - - def on_end(self, broker): - self.logger.info(f"策略结束: {self.name}") - emit_event(EventType.STRATEGY_END, {"strategy": self.name}) -``` - -### 完整使用流程 - -```python -import qka -from qka.core.config import load_config -from qka.core.events import start_event_engine, stop_event_engine - -# 1. 加载配置 -config = load_config('my_config.json') - -# 2. 启动事件系统 -start_event_engine() - -# 3. 获取数据 -data_obj = qka.data(config.data.default_source, stocks=['000001', '000002']) - -# 4. 运行回测 -result = qka.backtest( - data=data_obj, - strategy=EnhancedStrategy(), - start_time='2023-01-01', - end_time='2023-12-31' -) - -# 5. 查看结果 -print(f"总收益率: {result['total_return']:.2%}") - -# 6. 清理 -stop_event_engine() -``` - ---- - -## 📚 API 参考 - -### 配置管理 API - -| 函数/类 | 说明 | -|---------|------| -| `Config()` | 配置管理器类 | -| `load_config(file_path)` | 加载配置文件 | -| `create_sample_config(file_path)` | 创建示例配置 | -| `config.get(section, key, default)` | 获取配置值 | -| `config.set(section, key, value)` | 设置配置值 | - -### 事件系统 API - -| 函数/类 | 说明 | -|---------|------| -| `start_event_engine()` | 启动事件引擎 | -| `stop_event_engine()` | 停止事件引擎 | -| `emit_event(event_type, data)` | 发送事件 | -| `@event_handler(event_type)` | 事件处理器装饰器 | -| `event_engine.get_statistics()` | 获取事件统计 | - -### 日志系统 API - -| 函数/类 | 说明 | -|---------|------| -| `create_logger(name, **options)` | 创建日志记录器 | -| `get_structured_logger(name)` | 创建结构化日志记录器 | -| `add_wechat_handler(logger, webhook_url)` | 添加微信通知 | - -### 工具类 API - -| 函数/类 | 说明 | -|---------|------| -| `Cache(max_size, ttl)` | 内存缓存类 | -| `Timer()` | 计时器类 | -| `@timer` | 计时装饰器 | -| `@retry(max_attempts, delay)` | 重试装饰器 | -| `@memoize(ttl)` | 记忆化装饰器 | -| `FileUtils` | 文件操作工具类 | -| `ValidationUtils` | 验证工具类 | - ---- - -## 🔄 下一阶段预告 - -**阶段2:数据层增强** -- 数据缓存机制 -- 数据质量检查和清洗 -- 增量数据更新 -- 多频率数据支持 -- 数据订阅管理器 - -敬请期待! 🎉 diff --git a/docs/examples/index.md b/docs/examples/index.md deleted file mode 100644 index 62b1169..0000000 --- a/docs/examples/index.md +++ /dev/null @@ -1,143 +0,0 @@ -# 示例和教程 - -QKA量化回测系统的实用示例和完整教程。 - -## 快速入门示例 - -### [基础示例](basic/index.md) -- [第一个策略](basic/first-strategy.md) - 创建你的第一个交易策略 -- [数据获取](basic/data-fetching.md) - 学习如何获取和处理市场数据 -- [简单回测](basic/simple-backtest.md) - 运行基本的策略回测 - -### [进阶示例](advanced/index.md) -- [事件驱动策略](advanced/event-driven.md) - 使用事件系统的高级策略 -- [多资产策略](advanced/multi-asset.md) - 跨资产类别的投资策略 -- [风险管理](advanced/risk-management.md) - 集成风险控制的策略 - -### [完整案例](complete/index.md) -- [动量策略](complete/momentum-strategy.md) - 完整的动量交易策略 -- [均值回归策略](complete/mean-reversion.md) - 均值回归策略实现 -- [配对交易](complete/pairs-trading.md) - 统计套利策略 - -## 实际应用 - -### [实盘交易](live-trading/index.md) -- [环境配置](live-trading/setup.md) - 实盘交易环境搭建 -- [风险控制](live-trading/risk-control.md) - 实盘风险管理 -- [监控告警](live-trading/monitoring.md) - 交易监控和告警 - -### [性能优化](optimization/index.md) -- [策略优化](optimization/strategy-optimization.md) - 策略参数优化 -- [性能调优](optimization/performance-tuning.md) - 系统性能优化 -- [并行计算](optimization/parallel-computing.md) - 使用并行计算加速回测 - -### [集成案例](integration/index.md) -- [数据源集成](integration/data-sources.md) - 集成多种数据源 -- [交易接口](integration/broker-apis.md) - 连接不同的交易平台 -- [外部工具](integration/external-tools.md) - 与其他工具的集成 - -## 代码片段 - -### 常用模式 - -```python -# 策略模板 -from qka.core import Strategy, EventBus -from qka.utils import Logger - -class MyStrategy(Strategy): - def __init__(self): - super().__init__() - self.logger = Logger() - - def on_market_data(self, event): - # 处理市场数据 - pass - - def on_signal(self, signal): - # 处理交易信号 - pass -``` - -### 工具使用 - -```python -# 配置管理 -from qka.core import Config - -config = Config() -config.load_from_file('config.yaml') - -# 日志记录 -from qka.utils import Logger - -logger = Logger() -logger.info("策略启动") - -# 缓存装饰器 -from qka.utils.tools import cache - -@cache(ttl=300) -def expensive_calculation(): - return result -``` - -## 学习路径 - -### 初学者路径 -1. [安装和配置](../getting-started/installation.md) -2. [基础概念](../getting-started/concepts.md) -3. [第一个策略](../getting-started/first-strategy.md) -4. [基础示例](basic/index.md) - -### 进阶路径 -1. [进阶示例](advanced/index.md) -2. [系统架构](../user-guide/architecture.md) -3. [性能优化](optimization/index.md) -4. [完整案例](complete/index.md) - -### 专家路径 -1. [源码分析](../development/source-analysis.md) -2. [扩展开发](../development/extensions.md) -3. [集成案例](integration/index.md) -4. [贡献指南](../development/contributing.md) - -## 常见问题 - -### 策略开发 -- **Q: 如何处理数据缺失?** - A: 使用数据验证和填充机制,参考[数据处理示例](basic/data-processing.md) - -- **Q: 如何优化策略性能?** - A: 查看[性能优化指南](optimization/performance-tuning.md) - -### 回测分析 -- **Q: 如何设置回测参数?** - A: 参考[回测配置示例](basic/backtest-config.md) - -- **Q: 如何分析回测结果?** - A: 查看[结果分析教程](basic/result-analysis.md) - -### 实盘交易 -- **Q: 如何配置交易接口?** - A: 参考[交易接口配置](live-trading/broker-setup.md) - -- **Q: 如何监控策略运行?** - A: 查看[监控配置指南](live-trading/monitoring.md) - -## 贡献示例 - -我们欢迎社区贡献更多示例: - -1. Fork 项目仓库 -2. 在 `examples/` 目录下添加示例 -3. 在 `docs/examples/` 下添加对应文档 -4. 提交 Pull Request - -### 示例规范 -- 代码要有详细注释 -- 包含完整的运行说明 -- 提供示例数据 -- 说明预期结果 - -更多信息请查看[贡献指南](../development/contributing.md)。 diff --git a/docs/getting-started/concepts.md b/docs/getting-started/concepts.md index b3ee887..38a59d6 100644 --- a/docs/getting-started/concepts.md +++ b/docs/getting-started/concepts.md @@ -1,349 +1,199 @@ -# 基础概念 - -在开始使用 QKA 之前,了解一些基础概念将帮助您更好地理解和使用这个框架。 - -## 核心概念 - -### 策略 (Strategy) - -策略是量化交易的核心,定义了买卖股票的逻辑规则。 - -```python -from qka.core.backtest import Strategy - -class MyStrategy(Strategy): - def on_data(self, data): - # 在这里实现您的交易逻辑 - pass -``` - -**关键特点**: -- 策略继承自 `Strategy` 基类 -- 通过重写回调方法实现交易逻辑 -- 支持参数化配置 - -### 回测引擎 (Backtest Engine) - -回测引擎模拟历史交易环境,验证策略的有效性。 - -```python -from qka.core.backtest import BacktestEngine - -engine = BacktestEngine( - initial_cash=1000000, # 初始资金 - start_date='2023-01-01', # 开始日期 - end_date='2023-12-31', # 结束日期 - commission_rate=0.0003 # 手续费率 -) -``` - -**主要功能**: -- 模拟真实的交易环境 -- 计算手续费和滑点 -- 生成详细的交易记录 -- 计算各种性能指标 - -### 数据源 (Data Source) - -数据源提供股票的历史和实时价格数据。 - -```python -from qka.core.data import get_stock_data - -# 获取股票数据 -data = get_stock_data('000001.SZ', start='2023-01-01', end='2023-12-31') -``` - -**支持的数据源**: -- **AkShare**: 免费的A股数据接口 -- **Tushare**: 专业的金融数据接口 -- **QMT**: 迅投QMT交易软件数据 -- **Wind**: 万得金融数据库 - -### 交易客户端 (Trading Client) - -交易客户端连接券商接口,执行实际的买卖操作。 - -```python -from qka.brokers import QMTClient - -client = TradingClient() -client.connect() # 连接券商 - -# 下单买入 -order = client.place_order( - symbol='000001.SZ', - side='BUY', - volume=100, - price=15.50 -) -``` - -## 数据结构 - -### 股票代码格式 - -QKA 使用标准的股票代码格式: - -| 交易所 | 代码格式 | 示例 | 说明 | -|--------|----------|------|------| -| 深交所 | XXXXXX.SZ | 000001.SZ | 平安银行 | -| 上交所 | XXXXXX.SH | 600000.SH | 浦发银行 | -| 科创板 | 688XXX.SH | 688001.SH | 华兴源创 | -| 创业板 | 300XXX.SZ | 300001.SZ | 特锐德 | - -### 价格数据格式 - -标准的 OHLCV 格式: - -```python -# 数据示例 -{ - 'open': 15.20, # 开盘价 - 'high': 15.80, # 最高价 - 'low': 15.10, # 最低价 - 'close': 15.50, # 收盘价 - 'volume': 1000000, # 成交量 - 'amount': 15500000 # 成交额 -} -``` - -### 订单状态 - -| 状态 | 说明 | -|------|------| -| `PENDING` | 待提交 | -| `SUBMITTED` | 已提交 | -| `FILLED` | 已成交 | -| `PARTIAL_FILLED` | 部分成交 | -| `CANCELLED` | 已撤销 | -| `REJECTED` | 已拒绝 | - -## 交易流程 - -### 1. 策略开发流程 - -```mermaid -graph TD - A[策略想法] --> B[编写策略代码] - B --> C[历史数据回测] - C --> D{回测结果满意?} - D -->|否| E[优化策略参数] - E --> C - D -->|是| F[模拟交易验证] - F --> G[实盘小资金测试] - G --> H[正式实盘交易] -``` - -### 2. 回测流程 - -```mermaid -graph LR - A[准备数据] --> B[配置回测引擎] - B --> C[加载策略] - C --> D[运行回测] - D --> E[分析结果] - E --> F[生成报告] -``` - -### 3. 实盘交易流程 - -```mermaid -graph TD - A[连接券商] --> B[订阅实时数据] - B --> C[策略计算信号] - C --> D[风险检查] - D --> E[提交订单] - E --> F[监控执行] - F --> G[记录交易] -``` - -## 性能指标 - -### 收益率指标 - -- **总收益率**: 投资期间的总收益百分比 -- **年化收益率**: 年化后的收益率 -- **基准收益率**: 相对于基准指数的收益率 - -```python -total_return = (final_value - initial_value) / initial_value -annual_return = (1 + total_return) ** (365 / trading_days) - 1 -``` - -### 风险指标 - -- **波动率**: 收益率的标准差,衡量收益的不确定性 -- **最大回撤**: 从峰值到谷值的最大跌幅 -- **VaR**: 在一定置信水平下的潜在损失 - -```python -volatility = returns.std() * np.sqrt(252) # 年化波动率 -max_drawdown = (peak_value - valley_value) / peak_value -``` - -### 风险调整收益指标 - -- **夏普比率**: 超额收益与波动率的比值 -- **索提诺比率**: 超额收益与下行风险的比值 -- **卡尔马比率**: 年化收益率与最大回撤的比值 - -```python -sharpe_ratio = (annual_return - risk_free_rate) / volatility -sortino_ratio = (annual_return - risk_free_rate) / downside_deviation -calmar_ratio = annual_return / max_drawdown -``` - -## 风险管理 - -### 仓位管理 - -控制单个股票和总体的仓位大小: - -```python -# 单股最大仓位不超过10% -max_position_ratio = 0.1 -position_value = volume * price -max_volume = (total_value * max_position_ratio) / price - -# 总仓位不超过90% -total_position_ratio = total_position_value / total_value -if total_position_ratio > 0.9: - # 减仓操作 - pass -``` - -### 止损止盈 - -设置合理的止损止盈点: - -```python -def check_stop_loss_take_profit(position, current_price): - cost_price = position.avg_price - return_rate = (current_price - cost_price) / cost_price - - # 止损:亏损超过5% - if return_rate <= -0.05: - return 'STOP_LOSS' - - # 止盈:盈利超过15% - elif return_rate >= 0.15: - return 'TAKE_PROFIT' - - return 'HOLD' -``` - -### 风险分散 - -- **行业分散**: 不要集中投资单一行业 -- **时间分散**: 分批建仓,避免一次性投入 -- **策略分散**: 使用多种不同类型的策略 - -## 常用交易策略类型 - -### 趋势跟踪策略 - -基于价格趋势进行交易: - -- **移动平均线策略**: 基于不同周期均线的交叉 -- **突破策略**: 基于价格突破重要阻力或支撑 -- **动量策略**: 基于价格动量指标 - -### 均值回归策略 - -基于价格会回归均值的假设: - -- **布林带策略**: 价格触及上下轨时交易 -- **RSI策略**: 基于相对强弱指标的超买超卖 -- **配对交易**: 利用相关股票的价格差异 - -### 套利策略 - -利用价格差异获得无风险收益: - -- **统计套利**: 基于统计关系的套利 -- **事件驱动**: 基于公司事件的套利 -- **跨市场套利**: 利用不同市场的价差 - -## 开发工具和环境 - -### 推荐开发环境 - -- **IDE**: VS Code、PyCharm、Jupyter Notebook -- **Python版本**: 3.8 或更高 -- **依赖管理**: uv、pip、conda - -### 版本控制 - -```bash -# 初始化Git仓库 -git init -git add . -git commit -m "初始化量化策略项目" - -# 创建分支 -git checkout -b feature/new-strategy -``` - -### 项目结构 - -推荐的项目目录结构: - -``` -my_quant_project/ -├── strategies/ # 策略代码 -│ ├── __init__.py -│ ├── ma_strategy.py -│ └── bollinger_strategy.py -├── data/ # 数据文件 -│ ├── stocks/ -│ └── index/ -├── backtest/ # 回测结果 -│ ├── reports/ -│ └── logs/ -├── config/ # 配置文件 -│ ├── development.json -│ └── production.json -├── tests/ # 测试代码 -│ └── test_strategies.py -├── requirements.txt # 依赖列表 -└── README.md # 项目说明 -``` - -## 学习路径 - -### 新手入门 - -1. **理解基本概念** ← 您在这里 -2. **完成第一个策略** → [第一个策略](first-strategy.md) -3. **学习数据获取** → [数据获取](../user-guide/data.md) -4. **掌握回测分析** → [回测分析](../user-guide/backtest.md) - -### 进阶学习 - -5. **策略优化技巧** → [策略开发](../user-guide/strategy.md) -6. **风险管理实践** → [风险管理](../examples/risk-management.md) -7. **实盘交易部署** → [实盘交易](../user-guide/trading.md) - -### 高级应用 - -8. **多因子策略** → [多因子策略](../examples/multi-factor.md) -9. **机器学习集成** → [高级示例](../examples/indicators.md) -10. **系统架构设计** → [架构设计](../development/architecture.md) - -## 常见术语 - -| 术语 | 英文 | 说明 | -|------|------|------| -| 多头 | Long | 买入并持有股票的仓位 | -| 空头 | Short | 卖出股票的仓位(A股不支持做空) | -| 开仓 | Open Position | 建立新的交易仓位 | -| 平仓 | Close Position | 了结现有的交易仓位 | -| 滑点 | Slippage | 实际成交价与预期价格的差异 | -| 回撤 | Drawdown | 从峰值到谷值的损失 | -| 基准 | Benchmark | 用于比较策略表现的指数 | -| 因子 | Factor | 影响股票收益的特征变量 | -| 阿尔法 | Alpha | 策略相对于基准的超额收益 | -| 贝塔 | Beta | 策略相对于市场的敏感度 | - -现在您已经了解了 QKA 的基础概念,可以开始 [创建您的第一个策略](first-strategy.md) 了! +# 基础概念 + +了解 QKA 的核心概念和架构设计,帮助你更好地使用这个量化交易框架。 + +## 核心架构 + +QKA 采用模块化设计,主要包含以下几个核心模块: + +``` +qka/ +├── core/ # 核心功能模块 +│ ├── data.py # 数据管理 +│ ├── backtest.py # 回测引擎 +│ ├── strategy.py # 策略基类 +│ └── broker.py # 虚拟经纪商 +├── brokers/ # 交易接口 +│ ├── client.py # 交易客户端 +│ ├── server.py # 交易服务器 +│ └── trade.py # 交易执行 +├── mcp/ # MCP 协议支持 +│ ├── api.py # MCP API +│ └── server.py # MCP 服务器 +└── utils/ # 工具模块 + ├── logger.py # 日志系统 + └── util.py # 通用工具 +``` + +## 数据流 + +QKA 的数据处理流程如下: + +1. **数据获取**: 通过 `qka.Data` 类从数据源获取股票数据 +2. **策略处理**: 在 `on_bar` 方法中处理每个时间点的数据 +3. **交易执行**: 通过 `broker` 执行买卖操作 +4. **状态记录**: 自动记录资金、持仓和交易历史 +5. **结果分析**: 通过回测结果进行策略评估 + +## 核心概念详解 + +### 1. 数据维度 + +QKA 的数据模型基于四个维度: + +- **标的维度**: 股票代码列表,如 `['000001.SZ', '600000.SH']` +- **周期维度**: 数据频率,如 `'1d'`(日线)、`'1m'`(分钟线) +- **因子维度**: 技术指标和特征,如移动平均、RSI 等 +- **数据源维度**: 数据来源,如 `'akshare'`、`'qmt'` + +### 2. 策略开发 + +所有策略都必须继承 `qka.Strategy` 基类: + +```python +class MyStrategy(qka.Strategy): + def __init__(self): + super().__init__() + # 初始化策略参数 + + def on_bar(self, date, get): + """ + date: 当前时间戳 + get: 获取因子数据的函数,格式为 get(factor_name) -> pd.Series + """ + # 策略逻辑 + pass +``` + +### 3. 回测引擎 + +回测引擎负责: + +- 按时间顺序遍历数据 +- 在每个时间点调用策略的 `on_bar` 方法 +- 记录交易状态和资金变化 +- 提供可视化分析 + +### 4. 经纪商接口 + +`qka.Broker` 类提供虚拟交易功能: + +- 资金管理 +- 持仓跟踪 +- 交易执行 +- 交易记录 + +## 关键特性 + +### 并发数据下载 + +QKA 使用多线程并发下载数据,显著提高数据获取效率: + +```python +data = qka.Data( + symbols=['000001.SZ', '600000.SH', ...], # 多只股票 + pool_size=10 # 并发线程数 +) +``` + +### 数据缓存 + +自动缓存下载的数据到本地,避免重复下载: + +```python +# 数据会自动缓存到 datadir/ 目录 +# 下次使用时直接从缓存读取 +``` + +### 统一数据格式 + +无论数据来源如何,都统一为标准的 DataFrame 格式: + +```python +# 列名格式: {symbol}_{column} +# 例如: 000001.SZ_close, 600000.SH_volume +``` + +## 设计原则 + +### 1. 简洁性 + +提供简单直观的 API,降低量化交易的学习门槛: + +```python +# 三行代码完成基本回测 +data = qka.Data(symbols=['000001.SZ']) +strategy = MyStrategy() +backtest = qka.Backtest(data, strategy) +backtest.run() +``` + +### 2. 扩展性 + +模块化设计,易于扩展新功能: + +- 可以轻松添加新的数据源 +- 支持自定义因子计算 +- 可以扩展新的交易接口 + +### 3. 实用性 + +专注于 A 股市场的实际需求: + +- 支持 A 股特有的交易规则 +- 集成常用的 A 股数据源 +- 提供实盘交易接口 + +## 使用模式 + +### 回测模式 + +用于策略开发和验证: + +```python +# 1. 准备数据 +data = qka.Data(symbols=stock_list) + +# 2. 开发策略 +class MyStrategy(qka.Strategy): + def on_bar(self, date, get): + # 策略逻辑 + pass + +# 3. 运行回测 +backtest = qka.Backtest(data, MyStrategy()) +backtest.run() + +# 4. 分析结果 +backtest.plot() +``` + +### 实盘模式 + +用于实盘交易: + +```python +# 1. 启动交易服务器 +from qka.brokers.server import QMTServer +server = QMTServer(account_id, qmt_path) +server.start() + +# 2. 使用交易客户端 +from qka.brokers.client import QMTClient +client = QMTClient(token=token) +client.api("order_stock", ...) +``` + +## 最佳实践 + +1. **数据预处理**: 在策略开发前确保数据质量 +2. **参数优化**: 使用网格搜索等方法优化策略参数 +3. **风险控制**: 在策略中加入止损和仓位控制 +4. **回测验证**: 使用不同时间段的数据验证策略稳定性 +5. **实盘测试**: 从小资金开始实盘测试 + +## 下一步 + +- 查看 [用户指南](../user-guide/data.md) 学习详细功能用法 +- 参考 [API 文档](../api/core/data.md) 了解完整接口说明 +- 学习 [示例教程](../examples/basic/first-strategy.md) 获取实用代码示例 \ No newline at end of file diff --git a/docs/getting-started/first-strategy.md b/docs/getting-started/first-strategy.md index e38002b..fd95dec 100644 --- a/docs/getting-started/first-strategy.md +++ b/docs/getting-started/first-strategy.md @@ -1,335 +1,176 @@ -# 第一个策略 - -本教程将指导您创建和运行您的第一个量化交易策略。 - -## 策略概述 - -我们将创建一个简单的均线策略: -- 当短期均线上穿长期均线时买入(金叉) -- 当短期均线下穿长期均线时卖出(死叉) - -## 创建策略文件 - -创建一个新文件 `my_first_strategy.py`: - -```python -""" -我的第一个量化策略 -简单的双均线策略 -""" - -import qka -from qka.core.backtest import Strategy -from qka.core.data import get_stock_data - - -class MovingAverageStrategy(Strategy): - """双均线策略""" - - def __init__(self, short_window=20, long_window=50): - super().__init__() - self.short_window = short_window - self.long_window = long_window - self.name = f"双均线策略({short_window}/{long_window})" - - # 存储均线数据 - self.short_ma = {} - self.long_ma = {} - self.last_signal = {} - - def on_init(self): - """策略初始化""" - self.log("策略初始化开始") - - # 订阅股票数据 - self.subscribe('000001.SZ') # 平安银行 - - self.log("策略初始化完成") - - def calculate_moving_average(self, prices, window): - """计算移动平均线""" - if len(prices) < window: - return None - return sum(prices[-window:]) / window - - def on_data(self, data): - """处理数据更新""" - for symbol in data.index: - # 获取历史价格 - prices = self.get_price_history(symbol, self.long_window + 10) - - if len(prices) < self.long_window: - continue - - # 计算短期和长期均线 - short_ma = self.calculate_moving_average(prices, self.short_window) - long_ma = self.calculate_moving_average(prices, self.long_window) - - if short_ma is None or long_ma is None: - continue - - # 获取之前的均线值 - prev_short = self.short_ma.get(symbol, 0) - prev_long = self.long_ma.get(symbol, 0) - - # 当前持仓 - position = self.get_position(symbol) - current_price = data.loc[symbol, 'close'] - - # 交易信号判断 - if short_ma > long_ma and prev_short <= prev_long: - # 金叉 - 买入信号 - if not position: - volume = self.calculate_position_size(symbol, current_price) - if volume > 0: - self.buy(symbol, volume) - self.log(f"{symbol} 金叉买入,价格: {current_price:.2f}, 数量: {volume}") - self.last_signal[symbol] = 'BUY' - - elif short_ma < long_ma and prev_short >= prev_long: - # 死叉 - 卖出信号 - if position and position.volume > 0: - self.sell(symbol, position.volume) - self.log(f"{symbol} 死叉卖出,价格: {current_price:.2f}, 数量: {position.volume}") - self.last_signal[symbol] = 'SELL' - - # 保存当前均线值 - self.short_ma[symbol] = short_ma - self.long_ma[symbol] = long_ma - - def calculate_position_size(self, symbol, price): - """计算买入数量""" - # 使用可用资金的10%买入 - available_cash = self.get_available_cash() - target_value = available_cash * 0.1 - volume = int(target_value / price / 100) * 100 # 整手买入 - return volume - - def on_order(self, order): - """订单状态变化回调""" - self.log(f"订单更新: {order.symbol} {order.side} {order.volume}股 @ {order.price:.2f} - {order.status}") - - def on_trade(self, trade): - """成交回调""" - self.log(f"成交确认: {trade.symbol} {trade.side} {trade.volume}股 @ {trade.price:.2f}") - - -if __name__ == "__main__": - # 运行策略 - print("开始运行双均线策略...") - - # 1. 加载配置 - qka.config.backtest.initial_cash = 1000000 # 100万初始资金 - qka.config.backtest.commission_rate = 0.0003 # 万三手续费 - - # 2. 创建回测引擎 - from qka.core.backtest import BacktestEngine - - engine = BacktestEngine( - initial_cash=qka.config.backtest.initial_cash, - start_date='2024-01-01', - end_date='2024-06-30', - commission_rate=qka.config.backtest.commission_rate - ) - - # 3. 获取数据 - print("正在获取数据...") - data = get_stock_data('000001.SZ', start='2024-01-01', end='2024-06-30') - engine.add_data(data) - - # 4. 创建并运行策略 - strategy = MovingAverageStrategy(short_window=20, long_window=50) - - print("开始回测...") - result = engine.run(strategy) - - # 5. 显示结果 - print("\n" + "="*50) - print("回测结果") - print("="*50) - print(f"策略名称: {strategy.name}") - print(f"回测期间: 2024-01-01 至 2024-06-30") - print(f"初始资金: ¥{qka.config.backtest.initial_cash:,.2f}") - print(f"最终资金: ¥{result.final_value:,.2f}") - print(f"总收益率: {result.total_return:.2%}") - print(f"年化收益率: {result.annual_return:.2%}") - print(f"最大回撤: {result.max_drawdown:.2%}") - print(f"夏普比率: {result.sharpe_ratio:.2f}") - print(f"交易次数: {result.trade_count}") - print(f"胜率: {result.win_rate:.2%}") - - # 6. 绘制收益曲线(如果有matplotlib) - try: - from qka.core.plot import plot_returns - plot_returns(result.returns, title="双均线策略收益曲线") - print("\n收益曲线图已显示") - except ImportError: - print("\n提示: 安装 matplotlib 可以查看收益曲线图") - print("命令: pip install matplotlib") - - print("\n策略运行完成!") -``` - -## 运行策略 - -### 命令行运行 - -```bash -# 确保已安装 QKA -pip install qka - -# 运行策略 -python my_first_strategy.py -``` - -### 预期输出 - -``` -开始运行双均线策略... -正在获取数据... -开始回测... - -================================================== -回测结果 -================================================== -策略名称: 双均线策略(20/50) -回测期间: 2024-01-01 至 2024-06-30 -初始资金: ¥1,000,000.00 -最终资金: ¥1,050,000.00 -总收益率: 5.00% -年化收益率: 10.25% -最大回撤: -3.20% -夏普比率: 1.15 -交易次数: 8 -胜率: 62.50% - -收益曲线图已显示 -策略运行完成! -``` - -## 理解代码 - -### 策略类结构 - -```python -class MovingAverageStrategy(Strategy): - def __init__(self): - # 初始化参数 - - def on_init(self): - # 策略启动时执行一次 - - def on_data(self, data): - # 每次数据更新时执行 - - def on_order(self, order): - # 订单状态变化时执行 - - def on_trade(self, trade): - # 成交时执行 -``` - -### 关键方法说明 - -| 方法 | 作用 | 调用时机 | -|------|------|----------| -| `on_init()` | 策略初始化 | 策略开始时 | -| `on_data()` | 处理数据 | 每个交易日 | -| `buy()` | 买入股票 | 产生买入信号时 | -| `sell()` | 卖出股票 | 产生卖出信号时 | -| `get_position()` | 获取持仓 | 需要查询持仓时 | -| `log()` | 记录日志 | 任何时候 | - -## 策略优化 - -### 参数调优 - -```python -# 测试不同参数组合 -strategies = [] -for short in [10, 15, 20]: - for long in [30, 40, 50]: - if short < long: - strategy = MovingAverageStrategy(short, long) - result = engine.run(strategy) - strategies.append((short, long, result.sharpe_ratio)) - -# 找出最优参数 -best_params = max(strategies, key=lambda x: x[2]) -print(f"最优参数: 短线={best_params[0]}, 长线={best_params[1]}") -``` - -### 添加止损止盈 - -```python -def on_data(self, data): - # ...原有逻辑... - - # 检查止损止盈 - for symbol in self.positions: - position = self.get_position(symbol) - if position and position.volume > 0: - current_price = data.loc[symbol, 'close'] - cost_price = position.avg_price - - # 计算收益率 - return_rate = (current_price - cost_price) / cost_price - - # 止损(-5%) - if return_rate <= -0.05: - self.sell(symbol, position.volume) - self.log(f"{symbol} 触发止损") - - # 止盈(+10%) - elif return_rate >= 0.10: - self.sell(symbol, position.volume) - self.log(f"{symbol} 触发止盈") -``` - -## 下一步 - -现在您已经创建了第一个策略,可以: - -1. **学习更多策略**:查看 [示例教程](../examples/simple-strategy.md) -2. **优化策略**:了解 [参数优化](../user-guide/backtest.md#参数优化) -3. **风险管理**:学习 [风险控制](../examples/risk-management.md) -4. **实盘交易**:准备 [实盘部署](../user-guide/trading.md) - -## 常见问题 - -### Q: 如何添加更多股票? - -```python -def on_init(self): - # 添加多只股票 - stocks = ['000001.SZ', '000002.SZ', '600000.SH'] - for stock in stocks: - self.subscribe(stock) -``` - -### Q: 如何修改买入金额? - -```python -def calculate_position_size(self, symbol, price): - # 固定金额买入(比如每次10000元) - target_value = 10000 - volume = int(target_value / price / 100) * 100 - return volume -``` - -### Q: 如何保存回测结果? - -```python -# 保存结果到文件 -import json -results = { - 'total_return': result.total_return, - 'sharpe_ratio': result.sharpe_ratio, - 'max_drawdown': result.max_drawdown -} - -with open('backtest_results.json', 'w') as f: - json.dump(results, f, indent=2) -``` - -恭喜!您已经创建并运行了第一个量化交易策略。 +# 第一个策略 + +本指南将带你创建第一个简单的量化策略,包括数据获取、策略开发和回测分析。 + +## 策略目标 + +创建一个简单的移动平均策略: +- 当短期均线上穿长期均线时买入 +- 当短期均线下穿长期均线时卖出 + +## 步骤1:数据获取 + +首先,我们需要获取股票数据: + +```python +import qka + +# 创建数据对象 +data = qka.Data( + symbols=['000001.SZ', '600000.SH'], # 股票代码列表 + period='1d', # 日线数据 + adjust='qfq' # 前复权 +) + +# 获取数据 +df = data.get() +print(f"数据形状: {df.shape}") +print(df.head()) +``` + +## 步骤2:创建策略 + +继承 `qka.Strategy` 类并实现 `on_bar` 方法: + +```python +class MovingAverageStrategy(qka.Strategy): + def __init__(self): + super().__init__() + self.ma_short = 5 # 短期均线周期 + self.ma_long = 20 # 长期均线周期 + self.positions = {} # 持仓记录 + + def on_bar(self, date, get): + """ + 每个bar的处理逻辑 + + Args: + date: 当前时间戳 + get: 获取因子数据的函数 + """ + # 获取当前价格数据 + close_prices = get('close') + + # 遍历所有股票 + for symbol in close_prices.index: + # 获取该股票的历史数据 + symbol_close = get('close')[symbol] + + # 计算移动平均 + if len(symbol_close) >= self.ma_long: + ma_short = symbol_close[-self.ma_short:].mean() + ma_long = symbol_close[-self.ma_long:].mean() + + current_price = symbol_close.iloc[-1] + + # 交易逻辑 + if ma_short > ma_long and symbol not in self.positions: + # 短期均线上穿长期均线,买入 + size = int(self.broker.cash * 0.1 / current_price) # 使用10%资金 + if size > 0: + self.broker.buy(symbol, current_price, size) + self.positions[symbol] = True + + elif ma_short < ma_long and symbol in self.positions: + # 短期均线下穿长期均线,卖出 + position = self.broker.positions.get(symbol) + if position: + self.broker.sell(symbol, current_price, position['size']) + del self.positions[symbol] +``` + +## 步骤3:运行回测 + +创建策略实例并运行回测: + +```python +# 创建策略实例 +strategy = MovingAverageStrategy() + +# 创建回测引擎 +backtest = qka.Backtest(data, strategy) + +# 运行回测 +print("开始回测...") +backtest.run() +print("回测完成!") + +# 查看回测结果 +print(f"最终资金: {strategy.broker.cash:.2f}") +print(f"持仓情况: {strategy.broker.positions}") +``` + +## 步骤4:可视化结果 + +使用内置的可视化功能查看回测结果: + +```python +# 绘制收益曲线 +fig = backtest.plot("移动平均策略回测结果") + +# 查看详细交易记录 +trades_df = strategy.broker.trades +print("交易记录:") +print(trades_df.tail()) + +# 计算收益率 +initial_cash = 100000 +final_total = trades_df['total'].iloc[-1] +total_return = (final_total - initial_cash) / initial_cash * 100 +print(f"总收益率: {total_return:.2f}%") +``` + +## 完整代码示例 + +```python +import qka + +class MovingAverageStrategy(qka.Strategy): + def __init__(self): + super().__init__() + self.ma_short = 5 + self.ma_long = 20 + self.positions = {} + + def on_bar(self, date, get): + close_prices = get('close') + + for symbol in close_prices.index: + symbol_close = get('close')[symbol] + + if len(symbol_close) >= self.ma_long: + ma_short = symbol_close[-self.ma_short:].mean() + ma_long = symbol_close[-self.ma_long:].mean() + current_price = symbol_close.iloc[-1] + + if ma_short > ma_long and symbol not in self.positions: + size = int(self.broker.cash * 0.1 / current_price) + if size > 0: + self.broker.buy(symbol, current_price, size) + self.positions[symbol] = True + elif ma_short < ma_long and symbol in self.positions: + position = self.broker.positions.get(symbol) + if position: + self.broker.sell(symbol, current_price, position['size']) + del self.positions[symbol] + +# 执行策略 +data = qka.Data(symbols=['000001.SZ', '600000.SH'], period='1d') +strategy = MovingAverageStrategy() +backtest = qka.Backtest(data, strategy) +backtest.run() +backtest.plot() +``` + +## 策略优化建议 + +1. **参数调优**: 尝试不同的均线周期组合 +2. **风险控制**: 添加止损和仓位控制 +3. **多因子**: 结合其他技术指标 +4. **数据验证**: 使用更长时间段的数据进行验证 + +## 下一步 + +- 学习 [基础概念](concepts.md) 深入了解 QKA 架构 +- 查看 [用户指南](../user-guide/data.md) 学习更多高级功能 +- 参考 [API 文档](../api/core/data.md) 了解完整接口说明 \ No newline at end of file diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index c7f4103..5892e7b 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -1,214 +1,99 @@ -# 安装指南 - -## 系统要求 - -!!! info "环境要求" - - Python 3.8 或更高版本 - - Windows 10/11 (推荐用于QMT集成) - - macOS / Linux (支持基础功能) - -## 安装方式 - -### 使用 pip 安装 - -```bash -pip install qka -``` - -### 使用 uv 安装 (推荐) - -```bash -# 安装 uv (如果尚未安装) -curl -LsSf https://astral.sh/uv/install.sh | sh - -# 创建新项目 -uv init my-quant-project -cd my-quant-project - -# 添加 qka 依赖 -uv add qka -``` - -### 从源码安装 - -```bash -git clone https://github.com/your-username/qka.git -cd qka -uv sync -uv pip install -e . -``` - -## 验证安装 - -创建一个测试文件 `test_install.py`: - -```python -import qka -from qka.core.config import config - -print(f"QKA 版本: {qka.__version__}") -print(f"配置加载成功: {config.backtest.initial_cash:,}") -print("✅ QKA 安装成功!") -``` - -运行测试: - -```bash -python test_install.py -``` - -预期输出: -``` -QKA 版本: 0.2.0 -配置加载成功: 1,000,000 -✅ QKA 安装成功! -``` - -## 可选依赖 - -### QMT 集成 - -如果需要使用QMT进行实盘交易,需要安装: - -1. **迅投QMT客户端** - [官方下载](https://www.xtquant.com/) -2. **xtquant 库**: - ```bash - pip install xtquant - ``` - -### 数据源 - -根据使用的数据源安装对应依赖: - -=== "Akshare" - ```bash - pip install akshare - ``` - -=== "Tushare" - ```bash - pip install tushare - ``` - -=== "Wind" - ```bash - pip install WindPy - ``` - -### 可视化增强 - -```bash -# 交互式图表 -pip install plotly - -# Jupyter支持 -pip install jupyter notebook - -# 额外图表库 -pip install matplotlib seaborn -``` - -## 开发环境设置 - -如果您计划贡献代码或深度定制,建议设置开发环境: - -### 1. 克隆仓库 - -```bash -git clone https://github.com/your-username/qka.git -cd qka -``` - -### 2. 安装开发依赖 - -```bash -uv sync --all-extras -``` - -### 3. 安装预提交钩子 - -```bash -pre-commit install -``` - -### 4. 运行测试 - -```bash -# 运行全部测试 -pytest - -# 运行特定测试 -pytest tests/test_config.py - -# 生成覆盖率报告 -pytest --cov=qka --cov-report=html -``` - -### 5. 构建文档 - -```bash -# 启动文档服务器 -mkdocs serve - -# 构建静态文档 -mkdocs build -``` - -## 故障排除 - -### 常见问题 - -!!! warning "ImportError: No module named 'qka'" - - **原因**: QKA 未正确安装 - - **解决**: - ```bash - pip install --upgrade qka - # 或 - uv add qka - ``` - -!!! warning "xtquant 相关错误" - - **原因**: QMT客户端未安装或路径配置错误 - - **解决**: - 1. 确保已安装QMT客户端 - 2. 检查QMT路径配置 - 3. 参考 [QMT配置指南](../user-guide/trading.md#qmt-配置) - -!!! warning "数据获取失败" - - **原因**: 网络问题或数据源配置错误 - - **解决**: - 1. 检查网络连接 - 2. 确认数据源API密钥配置 - 3. 参考 [数据配置指南](../user-guide/data.md#数据源配置) - -### 获取帮助 - -如果遇到问题,可以通过以下方式获取帮助: - -1. **查看文档** - 本文档包含了详细的使用说明 -2. **搜索Issues** - [GitHub Issues](https://github.com/your-username/qka/issues) -3. **提交问题** - 如果没有找到解决方案,请提交新的Issue -4. **社区讨论** - [GitHub Discussions](https://github.com/your-username/qka/discussions) - -### 版本兼容性 - -| QKA 版本 | Python 版本 | 主要变化 | -|----------|-------------|----------| -| 0.2.x | 3.8+ | 配置管理、事件系统 | -| 0.1.x | 3.8+ | 基础功能 | - ---- - -## 下一步 - -安装完成后,建议阅读: - -- [第一个策略](first-strategy.md) - 创建您的第一个量化策略 -- [基础概念](concepts.md) - 了解QKA的核心概念 -- [用户指南](../user-guide/data.md) - 深入学习各个功能模块 +# 安装指南 + +## 系统要求 + +- Python 3.10 或更高版本 +- Windows 系统(推荐 Windows 10/11) +- 支持 QMT 交易(可选,用于实盘交易) + +## 安装方法 + +### 从 PyPI 安装(推荐) + +```bash +pip install qka +``` + +### 从源码安装 + +如果你想使用最新的开发版本,可以从 GitHub 源码安装: + +```bash +git clone https://github.com/zsrl/qka.git +cd qka +pip install -e . +``` + +### 安装开发依赖 + +如果你想要构建文档或运行测试,可以安装开发依赖: + +```bash +pip install qka[dev] +``` + +## 依赖说明 + +QKA 依赖于以下主要包: + +- **akshare**: 数据获取 +- **fastapi**: Web 服务器框架 +- **plotly**: 数据可视化 +- **pandas**: 数据处理 +- **dask**: 并行计算 +- **xtquant**: QMT Python 接口 + +完整的依赖列表请参考 [pyproject.toml](../pyproject.toml)。 + +## 验证安装 + +安装完成后,可以通过以下方式验证安装是否成功: + +```python +import qka + +print(f"QKA 版本: {qka.__version__}") + +# 检查核心模块是否可用 +try: + from qka import Data, Backtest, Strategy + print("核心模块导入成功") +except ImportError as e: + print(f"导入失败: {e}") +``` + +## 常见问题 + +### Q: 安装过程中出现依赖冲突怎么办? + +A: 建议使用虚拟环境来避免依赖冲突: + +```bash +# 创建虚拟环境 +python -m venv qka_env + +# 激活虚拟环境(Windows) +qka_env\Scripts\activate + +# 安装 QKA +pip install qka +``` + +### Q: 如何更新到最新版本? + +A: 使用 pip 更新: + +```bash +pip install --upgrade qka +``` + +### Q: 安装 xtquant 失败怎么办? + +A: xtquant 是 QMT 的 Python 接口,如果你不需要实盘交易功能,可以跳过这个依赖。如果需要,请确保已安装 QMT 并正确配置环境。 + +## 下一步 + +安装完成后,建议继续阅读: + +- [第一个策略](first-strategy.md) - 学习如何创建你的第一个量化策略 +- [基础概念](concepts.md) - 了解 QKA 的核心概念和架构 \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index f2fb520..62fc86e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,182 +1,67 @@ -# QKA - 快量化 - -
- -**Quick Quantitative Assistant** - -一个简洁易用、可实操A股的量化交易框架 - -[![PyPI version](https://badge.fury.io/py/qka.svg)](https://badge.fury.io/py/qka) -[![Python](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/) -[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE) - -[快速开始](getting-started/installation.md){ .md-button .md-button--primary } -[GitHub](https://github.com/zsrl/qka){ .md-button } - -
- ---- - -## ✨ 特性 - -!!! tip "核心特性" - - - 🚀 **极简API** - 3行代码完成回测 - - 📊 **多数据源** - 支持QMT、Akshare等数据源 - - 🔄 **事件驱动** - 现代化的事件系统架构 - - 📈 **实盘对接** - 直接对接QMT进行A股实盘交易 - - 🛠️ **工具丰富** - 内置缓存、日志、配置管理等工具 - - 📖 **文档完善** - 详细的文档和示例 - -## 🚀 快速体验 - -### 安装 - -```bash -pip install qka -``` - -### 3分钟上手 - -=== "获取数据" - - ```python - import qka - - # 获取股票数据 - data_obj = qka.data('akshare', stocks=['000001', '000002']) - hist_data = data_obj.get(start_time='2023-01-01', end_time='2023-12-31') - ``` - -=== "定义策略" - - ```python - from qka.core.backtest import Strategy - - class MyStrategy(Strategy): - def on_bar(self, data, broker, current_date): - for symbol, df in data.items(): - if len(df) >= 20: - price = df['close'].iloc[-1] - ma20 = df['close'].rolling(20).mean().iloc[-1] - - if price > ma20 and broker.get_position(symbol) == 0: - broker.buy(symbol, 0.3, price) # 买入30%资金 - elif price < ma20 and broker.get_position(symbol) > 0: - broker.sell(symbol, 1.0, price) # 全部卖出 - ``` - -=== "运行回测" - - ```python - # 运行回测 - result = qka.backtest( - data=data_obj, - strategy=MyStrategy(), - start_time='2023-01-01', - end_time='2023-12-31' - ) - - # 查看结果 - print(f"总收益率: {result['total_return']:.2%}") - print(f"年化收益率: {result['annual_return']:.2%}") - print(f"最大回撤: {result['max_drawdown']:.2%}") - ``` - -## 📋 功能模块 - -
- -- 📊 **数据管理** - - --- - - 支持多种数据源,自动缓存,数据质量检查 - - [→ 了解更多](user-guide/data.md) - -- 🧠 **策略开发** - - --- - - 简洁的策略框架,丰富的技术指标,事件驱动 - - [→ 了解更多](user-guide/strategy.md) - -- 📈 **回测分析** - - --- - - 高效的回测引擎,详细的绩效分析,可视化图表 - - [→ 了解更多](user-guide/backtest.md) - -- 🚀 **实盘交易** - - --- - - 直接对接QMT,支持A股实盘交易,风险控制 - - [→ 了解更多](user-guide/trading.md) - -
- -## 🏗️ 架构优势 - -```mermaid -graph TB - A[策略层] --> B[事件系统] - B --> C[数据层] - B --> D[回测引擎] - B --> E[交易接口] - - C --> F[QMT数据] - C --> G[Akshare数据] - - E --> H[模拟交易] - E --> I[实盘交易] - - J[配置管理] --> A - J --> C - J --> D - J --> E - - K[日志系统] --> A - K --> C - K --> D - K --> E -``` - -## 📊 使用场景 - -!!! example "典型应用" - - - **量化策略研究** - 快速验证交易想法 - - **A股程序化交易** - 实盘自动化交易 - - **金融数据分析** - 多源数据整合分析 - - **风险管理** - 投资组合监控和风控 - - **教学研究** - 量化金融教学和研究 - -## 🎯 版本规划 - -| 版本 | 状态 | 主要功能 | -|------|------|----------| -| v0.1.x | ✅ 已发布 | 基础回测、数据接口、QMT交易 | -| v0.2.x | 🚧 开发中 | 配置管理、事件系统、增强日志 | -| v0.3.x | 📋 规划中 | 数据缓存、质量检查、多频率 | -| v0.4.x | 📋 规划中 | 策略优化、风险管理、指标库 | -| v1.0.x | 📋 规划中 | 稳定版本、完整文档、生态 | - -## 🐛 问题反馈 - -- [GitHub Issues](https://github.com/zsrl/qka/issues) - 报告bug或提出功能建议 - ---- - -
- -**开始您的量化之旅** 🚀 - -[立即开始](getting-started/installation.md){ .md-button .md-button--primary } - -
+# QKA - 快捷量化助手 + +欢迎使用 QKA(Quick Quantitative Assistant)文档! + +## 简介 + +QKA 是一个简洁易用、功能完整的A股量化交易框架,支持数据获取、策略回测、实盘交易等全流程量化交易功能。 + +## 主要特性 + +- 🚀 **简洁易用**: 统一的API设计,降低量化交易门槛 +- 📊 **数据丰富**: 支持Akshare数据源,提供多周期、多因子数据 +- 🔄 **高效回测**: 基于时间序列的回测引擎,支持多股票横截面处理 +- 💰 **实盘交易**: 集成QMT交易接口,支持实盘交易 +- 📈 **可视化**: 内置Plotly图表,提供交互式回测结果展示 +- 🔧 **模块化**: 高度模块化设计,易于扩展和维护 + +## 快速开始 + +### 安装 + +```bash +pip install qka +``` + +### 基本用法 + +```python +import qka + +# 数据获取 +data = qka.Data(symbols=['000001.SZ', '600000.SH'], period='1d') +df = data.get() + +# 策略开发 +class MyStrategy(qka.Strategy): + def on_bar(self, date, get): + close_prices = get('close') + # 你的策略逻辑 + +# 回测分析 +strategy = MyStrategy() +backtest = qka.Backtest(data, strategy) +backtest.run() +backtest.plot() +``` + +## 文档结构 + +- **快速开始**: 安装和基础使用指南 +- **用户指南**: 详细的功能使用说明 +- **API参考**: 完整的API文档 +- **示例教程**: 实用的代码示例 + +## 获取帮助 + +- 查看 [GitHub Issues](https://github.com/zsrl/qka/issues) 获取技术支持 +- 阅读 [用户指南](user-guide/data.md) 了解详细用法 +- 参考 [API文档](api/core/data.md) 查看完整接口说明 + +## 许可证 + +本项目采用 MIT 许可证 - 查看 [LICENSE](https://github.com/zsrl/qka/blob/main/LICENSE) 文件了解详情。 + +--- + +**注意**: 量化交易存在风险,请在充分了解风险的情况下使用本框架。 \ No newline at end of file diff --git a/docs/user-guide/backtest.md b/docs/user-guide/backtest.md index 7984a8b..0237c71 100644 --- a/docs/user-guide/backtest.md +++ b/docs/user-guide/backtest.md @@ -1,465 +1,342 @@ -# 回测分析 - -QKA 提供了简洁易用的回测功能,帮助您快速验证交易策略的有效性。 - -## 快速开始 - -### 三步完成回测 - -QKA 回测非常简单,只需三步: - -```python -import qka - -# 第1步:获取数据 -data_obj = qka.data(stocks=['000001', '000002']) # 默认使用akshare数据源 - -# 第2步:定义策略 -class SimpleStrategy(qka.Strategy): - def on_bar(self, data, broker, current_date): - for symbol, df in data.items(): - if len(df) >= 20: - price = df['close'].iloc[-1] - ma20 = df['close'].rolling(20).mean().iloc[-1] - - if price > ma20: # 突破均线买入 - broker.buy(symbol, 0.5, price) - elif price < ma20: # 跌破均线卖出 - broker.sell(symbol, 1.0, price) - -# 第3步:运行回测并查看结果 -result = qka.backtest(data_obj, SimpleStrategy(), start_time='2023-01-01', end_time='2023-12-31') - -print(f"总收益率: {result['total_return']:.2%}") -print(f"年化收益率: {result['annual_return']:.2%}") -print(f"夏普比率: {result['sharpe_ratio']:.2f}") -``` - -> **数据源说明**: 默认使用akshare数据源。如需使用其他数据源,可以调用 `set_source('qmt')` 或在 `data()` 函数中指定 `source` 参数。 - -## 策略开发 - -### 策略基类 - -所有策略都需要继承 `qka.Strategy` 基类: - -```python -import qka - -class MyStrategy(qka.Strategy): - def on_bar(self, data, broker, current_date): - """ - 每个交易日调用的策略逻辑 - - Args: - data: 历史数据字典 {股票代码: DataFrame} - broker: 交易接口 - current_date: 当前日期 - """ - # 在这里实现你的策略逻辑 - pass - - def on_start(self, broker): - """回测开始时调用""" - print(f"策略 {self.name} 开始运行") - - def on_end(self, broker): - """回测结束时调用""" - print(f"策略 {self.name} 运行结束") -``` - -### 策略示例 - -#### 移动平均策略 - -```python -class MovingAverageStrategy(qka.Strategy): - def __init__(self, short_window=5, long_window=20): - super().__init__() - self.short_window = short_window - self.long_window = long_window - - def on_bar(self, data, broker, current_date): - for symbol, df in data.items(): - if len(df) < self.long_window: - continue - - # 计算短期和长期移动平均 - short_ma = df['close'].rolling(self.short_window).mean().iloc[-1] - long_ma = df['close'].rolling(self.long_window).mean().iloc[-1] - current_price = df['close'].iloc[-1] - - # 金叉买入,死叉卖出 - if short_ma > long_ma and broker.get_position(symbol) == 0: - broker.buy(symbol, 0.3, current_price) # 用30%资金买入 - elif short_ma < long_ma and broker.get_position(symbol) > 0: - broker.sell(symbol, 1.0, current_price) # 全部卖出 -``` - -#### 布林带策略 - -```python -class BollingerBandStrategy(qka.Strategy): - def __init__(self, window=20, num_std=2): - super().__init__() - self.window = window - self.num_std = num_std - - def on_bar(self, data, broker, current_date): - for symbol, df in data.items(): - if len(df) < self.window: - continue - - # 计算布林带 - close_prices = df['close'] - rolling_mean = close_prices.rolling(self.window).mean().iloc[-1] - rolling_std = close_prices.rolling(self.window).std().iloc[-1] - - upper_band = rolling_mean + (rolling_std * self.num_std) - lower_band = rolling_mean - (rolling_std * self.num_std) - current_price = close_prices.iloc[-1] - - # 价格突破下轨买入,突破上轨卖出 - if current_price < lower_band and broker.get_position(symbol) == 0: - broker.buy(symbol, 0.4, current_price) - elif current_price > upper_band and broker.get_position(symbol) > 0: - broker.sell(symbol, 1.0, current_price) -``` - -## 交易接口 - -### Broker 类 - -`Broker` 类提供了所有交易相关的功能: - -```python -# 获取当前持仓 -position = broker.get_position('000001') # 返回持仓股数 - -# 获取可用现金 -cash = broker.get_cash() - -# 获取所有持仓 -positions = broker.get_positions() # 返回 {股票代码: 持仓数量} - -# 计算总资产 -prices = {'000001': 10.5, '000002': 8.3} -total_value = broker.get_total_value(prices) -``` - -### 买入操作 - -```python -# 按比例买入(推荐) -broker.buy('000001', 0.3, price) # 用30%的资金买入 - -# 按股数买入 -broker.buy('000001', 1000, price) # 买入1000股(自动调整为整手) - -# 买入条件检查 -if broker.get_cash() > 10000: # 现金充足 - if broker.get_position('000001') == 0: # 没有持仓 - broker.buy('000001', 0.2, current_price) -``` - -### 卖出操作 - -```python -# 按比例卖出 -broker.sell('000001', 0.5, price) # 卖出50%的持仓 -broker.sell('000001', 1.0, price) # 全部卖出 - -# 按股数卖出 -broker.sell('000001', 500, price) # 卖出500股 - -# 卖出条件检查 -if broker.get_position('000001') > 0: # 有持仓 - broker.sell('000001', 1.0, current_price) # 全部卖出 -``` - -## 回测配置 - -### 基本配置 - -```python -import qka - -# 自定义 Broker 配置 -custom_broker = qka.Broker( - initial_cash=500000, # 初始资金50万 - commission_rate=0.0003 # 手续费率0.03% -) - -# 使用自定义配置运行回测 -result = qka.backtest( - data=data_obj, - strategy=MyStrategy(), - broker=custom_broker, - start_time='2023-01-01', - end_time='2023-12-31' -) -``` - -### 数据获取 - -```python -import qka - -# 使用默认数据源(akshare) -data_obj = qka.data(stocks=['000001']) - -# 多只股票 -data_obj = qka.data(stocks=['000001', '000002', '600000']) - -# 设置全局数据源 -qka.set_source('qmt') -data_obj = qka.data(stocks=['000001.SZ', '600000.SH']) # QMT格式股票代码 - -# 临时指定数据源 -data_obj = qka.data(stocks=['000001'], source='akshare') -``` - -## 回测结果分析 - -### 基本指标 - -回测结果包含以下关键指标: - -```python -# 基本收益指标 -print(f"初始资金: {result['initial_capital']:,.0f}") -print(f"最终资产: {result['final_value']:,.0f}") -print(f"总收益率: {result['total_return']:.2%}") -print(f"年化收益率: {result['annual_return']:.2%}") - -# 风险指标 -print(f"收益波动率: {result['volatility']:.2%}") -print(f"夏普比率: {result['sharpe_ratio']:.2f}") -print(f"最大回撤: {result['max_drawdown']:.2%}") - -# 交易指标 -print(f"总交易次数: {result['total_trades']}") -print(f"总手续费: {result['total_commission']:,.2f}") -print(f"胜率: {result['win_rate']:.2%}") -print(f"交易天数: {result['trading_days']}") -``` - -### 详细数据 - -```python -# 每日净值数据 -daily_values = result['daily_values'] -for record in daily_values[:5]: # 显示前5天 - print(f"日期: {record['date']}, 总资产: {record['total_value']:,.2f}") - -# 交易记录 -trades = result['trades'] -for trade in trades[:5]: # 显示前5笔交易 - print(f"{trade['date']}: {trade['action']} {trade['symbol']} " - f"{trade['shares']}股 @{trade['price']}") - -# 最终持仓 -positions = result['positions'] -print(f"最终持仓: {positions}") -``` - -## 结果可视化 - -```python -import qka - -# 绘制回测结果图表 -qka.plot(result) -``` - -## 策略开发技巧 - -### 数据处理 - -```python -def on_bar(self, data, broker, current_date): - for symbol, df in data.items(): - # 检查数据长度 - if len(df) < 20: - continue - - # 获取最新价格 - current_price = df['close'].iloc[-1] - - # 计算技术指标 - sma_20 = df['close'].rolling(20).mean().iloc[-1] - rsi = self.calculate_rsi(df['close'], 14) - - # 处理缺失值 - if pd.isna(sma_20) or pd.isna(rsi): - continue - - # 策略逻辑 - if rsi < 30 and current_price > sma_20: - broker.buy(symbol, 0.2, current_price) -``` - -### 风险控制 - -```python -def on_bar(self, data, broker, current_date): - for symbol, df in data.items(): - current_price = df['close'].iloc[-1] - position = broker.get_position(symbol) - - # 止损逻辑 - if position > 0: - avg_cost = broker.avg_costs.get(symbol, current_price) - if current_price < avg_cost * 0.95: # 5%止损 - broker.sell(symbol, 1.0, current_price) - continue - - # 仓位控制 - total_value = broker.get_total_value({symbol: current_price}) - position_value = position * current_price - position_ratio = position_value / total_value - - if position_ratio > 0.3: # 单只股票最大30%仓位 - continue - - # 买入逻辑 - # ... -``` - -### 多股票策略 - -```python -class MultiStockStrategy(qka.Strategy): - def on_bar(self, data, broker, current_date): - # 收集所有股票的信号 - signals = {} - for symbol, df in data.items(): - if len(df) >= 20: - signal = self.calculate_signal(df) - signals[symbol] = signal - - # 按信号强度排序 - sorted_signals = sorted(signals.items(), - key=lambda x: x[1], reverse=True) - - # 只选择前3只股票 - selected_stocks = sorted_signals[:3] - - # 平均分配资金 - for symbol, signal in selected_stocks: - if signal > 0.5 and broker.get_position(symbol) == 0: - broker.buy(symbol, 0.3, data[symbol]['close'].iloc[-1]) -``` - -## 最佳实践 - -### 1. 数据质量检查 - -```python -def on_bar(self, data, broker, current_date): - for symbol, df in data.items(): - # 检查价格合理性 - current_price = df['close'].iloc[-1] - if current_price <= 0 or pd.isna(current_price): - continue - - # 检查成交量 - volume = df['volume'].iloc[-1] - if volume <= 0: - continue - - # 策略逻辑 - # ... -``` - -### 2. 参数化策略 - -```python -class ParameterizedStrategy(qka.Strategy): - def __init__(self, ma_period=20, position_size=0.3, stop_loss=0.05): - super().__init__() - self.ma_period = ma_period - self.position_size = position_size - self.stop_loss = stop_loss - - def on_bar(self, data, broker, current_date): - for symbol, df in data.items(): - if len(df) < self.ma_period: - continue - - current_price = df['close'].iloc[-1] - ma = df['close'].rolling(self.ma_period).mean().iloc[-1] - - # 使用参数化的逻辑 - if current_price > ma: - broker.buy(symbol, self.position_size, current_price) -``` - -### 3. 策略状态管理 - -```python -class StatefulStrategy(qka.Strategy): - def __init__(self): - super().__init__() - self.last_signal = {} - self.entry_prices = {} - - def on_bar(self, data, broker, current_date): - for symbol, df in data.items(): - # 保存策略状态 - current_signal = self.calculate_signal(df) - last_signal = self.last_signal.get(symbol, 0) - - # 信号变化时才交易 - if current_signal != last_signal: - if current_signal > 0: - price = df['close'].iloc[-1] - broker.buy(symbol, 0.3, price) - self.entry_prices[symbol] = price - else: - broker.sell(symbol, 1.0, df['close'].iloc[-1]) - self.entry_prices.pop(symbol, None) - - self.last_signal[symbol] = current_signal -``` - -## 常见问题 - -### Q: 如何处理停牌股票? - -```python -def on_bar(self, data, broker, current_date): - for symbol, df in data.items(): - # 检查是否停牌 - if current_date not in df.index: - continue # 跳过停牌股票 - - current_price = df.loc[current_date, 'close'] - if current_price == 0: - continue # 价格为0可能是停牌 -``` - -### Q: 如何避免未来函数? - -```python -def on_bar(self, data, broker, current_date): - for symbol, df in data.items(): - # 只使用当前日期之前的数据 - historical_data = df.loc[:current_date] - - # 计算指标时确保不使用未来数据 - sma = historical_data['close'].rolling(20).mean().iloc[-1] -``` - -### Q: 如何处理数据缺失? - -```python -def on_bar(self, data, broker, current_date): - for symbol, df in data.items(): - # 检查关键数据是否缺失 - if df[['open', 'high', 'low', 'close', 'volume']].iloc[-1].isna().any(): - continue - - # 使用前向填充处理缺失值 - df_filled = df.fillna(method='ffill') -``` +# 回测分析 + +QKA 提供完整的回测功能,支持策略验证、性能分析和结果可视化。 + +## 基本用法 + +### 创建回测 + +```python +import qka + +# 1. 准备数据 +data = qka.Data(symbols=['000001.SZ', '600000.SH']) + +# 2. 创建策略 +class MyStrategy(qka.Strategy): + def on_bar(self, date, get): + close_prices = get('close') + # 策略逻辑... + +# 3. 运行回测 +strategy = MyStrategy() +backtest = qka.Backtest(data, strategy) +backtest.run() +``` + +### 查看回测结果 + +```python +# 查看最终状态 +print(f"最终资金: {strategy.broker.cash:.2f}") +print(f"持仓情况: {strategy.broker.positions}") + +# 查看交易记录 +trades_df = strategy.broker.trades +print("交易记录:") +print(trades_df.tail()) +``` + +## 可视化分析 + +### 收益曲线 + +```python +# 绘制收益曲线 +fig = backtest.plot("我的策略回测结果") + +# 自定义图表标题 +fig = backtest.plot("自定义标题") +``` + +### 详细分析 + +```python +# 计算关键指标 +trades_df = strategy.broker.trades + +# 总收益率 +initial_cash = 100000 +final_total = trades_df['total'].iloc[-1] +total_return = (final_total - initial_cash) / initial_cash * 100 + +# 最大回撤 +peak = trades_df['total'].expanding().max() +drawdown = (trades_df['total'] - peak) / peak * 100 +max_drawdown = drawdown.min() + +print(f"总收益率: {total_return:.2f}%") +print(f"最大回撤: {max_drawdown:.2f}%") +print(f"夏普比率: {calculate_sharpe(trades_df):.2f}") +``` + +## 策略开发指南 + +### 策略结构 + +所有策略必须继承 `qka.Strategy` 并实现 `on_bar` 方法: + +```python +class MyStrategy(qka.Strategy): + def __init__(self): + super().__init__() + # 初始化策略参数 + self.param1 = value1 + self.param2 = value2 + + def on_bar(self, date, get): + """ + date: 当前时间戳 + get: 获取因子数据的函数 + """ + # 获取数据 + close_prices = get('close') + volumes = get('volume') + + # 策略逻辑 + for symbol in close_prices.index: + current_price = close_prices[symbol] + current_volume = volumes[symbol] + + # 交易决策 + if self.should_buy(symbol, current_price, current_volume): + self.buy(symbol, current_price) + elif self.should_sell(symbol, current_price, current_volume): + self.sell(symbol, current_price) + + def should_buy(self, symbol, price, volume): + # 买入条件 + return True # 替换为实际条件 + + def should_sell(self, symbol, price, volume): + # 卖出条件 + return True # 替换为实际条件 + + def buy(self, symbol, price): + # 买入逻辑 + size = self.calculate_position_size(price) + self.broker.buy(symbol, price, size) + + def sell(self, symbol, price): + # 卖出逻辑 + position = self.broker.positions.get(symbol) + if position: + self.broker.sell(symbol, price, position['size']) +``` + +### 数据访问 + +在 `on_bar` 方法中,使用 `get` 函数访问数据: + +```python +def on_bar(self, date, get): + # 获取收盘价(所有股票) + close_prices = get('close') + + # 获取成交量 + volumes = get('volume') + + # 获取开盘价 + open_prices = get('open') + + # 获取自定义因子(如果定义了) + ma5_values = get('ma5') # 假设在数据中定义了ma5因子 +``` + +## 风险控制 + +### 仓位管理 + +```python +class RiskManagedStrategy(qka.Strategy): + def __init__(self): + super().__init__() + self.max_position_ratio = 0.1 # 单只股票最大仓位比例 + self.max_total_position = 0.8 # 总仓位上限 + + def calculate_position_size(self, symbol, price): + # 计算合理的买入数量 + available_cash = self.broker.cash + total_assets = self.broker.get('total') + + # 单只股票仓位限制 + max_position_value = total_assets * self.max_position_ratio + max_shares = int(max_position_value / price) + + # 总仓位限制 + current_position_value = sum( + pos['size'] * pos['avg_price'] + for pos in self.broker.positions.values() + ) + available_for_new = total_assets * self.max_total_position - current_position_value + max_shares_by_total = int(available_for_new / price) + + return min(max_shares, max_shares_by_total, int(available_cash / price)) +``` + +### 止损策略 + +```python +class StopLossStrategy(qka.Strategy): + def __init__(self): + super().__init__() + self.stop_loss_rate = 0.05 # 5%止损 + self.entry_prices = {} # 记录买入价格 + + def on_bar(self, date, get): + close_prices = get('close') + + # 检查止损 + for symbol, position in self.broker.positions.items(): + if symbol in self.entry_prices: + entry_price = self.entry_prices[symbol] + current_price = close_prices[symbol] + loss_rate = (current_price - entry_price) / entry_price + + if loss_rate <= -self.stop_loss_rate: + # 触发止损 + self.broker.sell(symbol, current_price, position['size']) + del self.entry_prices[symbol] + + # 正常的买入逻辑 + for symbol in close_prices.index: + if self.should_buy(symbol, close_prices[symbol]): + size = self.calculate_position_size(symbol, close_prices[symbol]) + if self.broker.buy(symbol, close_prices[symbol], size): + self.entry_prices[symbol] = close_prices[symbol] +``` + +## 性能优化 + +### 避免重复计算 + +```python +class OptimizedStrategy(qka.Strategy): + def __init__(self): + super().__init__() + self.cache = {} # 缓存计算结果 + + def on_bar(self, date, get): + # 缓存数据访问 + close_prices = get('close') + + for symbol in close_prices.index: + # 避免重复获取相同数据 + if symbol not in self.cache: + self.cache[symbol] = self.calculate_indicators(symbol, get) + + indicators = self.cache[symbol] + + # 使用缓存的数据进行决策 + if self.should_trade(symbol, indicators): + # 交易逻辑... + pass +``` + +### 批量处理 + +```python +class BatchStrategy(qka.Strategy): + def on_bar(self, date, get): + # 一次性获取所有需要的数据 + close_prices = get('close') + volumes = get('volume') + ma5_values = get('ma5') + ma20_values = get('ma20') + + # 批量计算交易信号 + buy_signals = self.calculate_buy_signals(close_prices, volumes, ma5_values, ma20_values) + sell_signals = self.calculate_sell_signals(close_prices, volumes, ma5_values, ma20_values) + + # 批量执行交易 + for symbol, should_buy in buy_signals.items(): + if should_buy: + self.buy(symbol, close_prices[symbol]) + + for symbol, should_sell in sell_signals.items(): + if should_sell: + self.sell(symbol, close_prices[symbol]) +``` + +## 高级功能 + +### 多时间框架 + +```python +class MultiTimeframeStrategy(qka.Strategy): + def __init__(self): + super().__init__() + # 加载不同时间框架的数据 + self.daily_data = qka.Data(symbols=['000001.SZ'], period='1d') + self.hourly_data = qka.Data(symbols=['000001.SZ'], period='1h') + + def on_bar(self, date, get): + # 日线级别判断趋势 + daily_trend = self.analyze_daily_trend() + + # 小时线级别寻找入场点 + if daily_trend == 'up': + hourly_signal = self.analyze_hourly_signal() + if hourly_signal == 'buy': + # 执行买入 + pass +``` + +### 参数优化 + +```python +# 简单的参数网格搜索 +def optimize_strategy(): + best_params = None + best_return = -float('inf') + + for ma_short in [5, 10, 20]: + for ma_long in [20, 30, 50]: + # 使用不同参数运行回测 + data = qka.Data(symbols=['000001.SZ']) + strategy = MovingAverageStrategy(ma_short, ma_long) + backtest = qka.Backtest(data, strategy) + backtest.run() + + # 计算收益率 + final_total = strategy.broker.get('total') + total_return = (final_total - 100000) / 100000 + + if total_return > best_return: + best_return = total_return + best_params = (ma_short, ma_long) + + return best_params, best_return +``` + +## 常见问题 + +### Q: 回测速度很慢怎么办? + +A: 可以尝试: +- 减少股票数量 +- 使用更短的时间段 +- 优化策略代码,避免重复计算 +- 使用更简单的因子 + +### Q: 如何验证策略的稳定性? + +A: 建议: +- 使用不同时间段的数据进行回测 +- 进行参数敏感性分析 +- 使用交叉验证 +- 考虑交易成本和滑点 + +### Q: 回测结果和实盘差异很大? + +A: 可能原因: +- 未来函数(使用了未来数据) +- 未考虑交易成本和滑点 +- 数据质量问题 +- 市场环境变化 + +## 下一步 + +- 学习 [实盘交易](trading.md) 了解如何将策略用于实盘 +- 查看 [API 文档](../api/core/backtest.md) 了解完整的回测接口 +- 参考 [示例教程](../examples/basic/simple-backtest.md) 获取更多代码示例 \ No newline at end of file diff --git a/docs/user-guide/config.md b/docs/user-guide/config.md deleted file mode 100644 index ec2350c..0000000 --- a/docs/user-guide/config.md +++ /dev/null @@ -1,322 +0,0 @@ -# 配置管理 - -QKA 提供了强大而灵活的配置管理系统,支持多种配置方式和模块化配置。 - -## 快速开始 - -### 创建配置文件 - -```python -from qka.core.config import create_sample_config - -# 创建示例配置文件 -create_sample_config('my_config.json') -``` - -### 加载配置 - -```python -import qka -from qka.core.config import load_config - -# 加载自定义配置 -config = load_config('my_config.json') - -# 使用全局配置 -print(f"初始资金: {qka.config.backtest.initial_cash:,}") -print(f"数据源: {qka.config.data.default_source}") -``` - -## 配置结构 - -### 回测配置 (BacktestConfig) - -```python -# 访问回测配置 -config.backtest.initial_cash = 2_000_000 # 初始资金 -config.backtest.commission_rate = 0.0003 # 手续费率 -config.backtest.slippage = 0.001 # 滑点率 -config.backtest.min_trade_amount = 100 # 最小交易股数 -config.backtest.max_position_ratio = 0.3 # 单只股票最大仓位比例 -config.backtest.benchmark = '000300.SH' # 基准指数 -``` - -### 数据配置 (DataConfig) - -```python -# 访问数据配置 -config.data.default_source = 'akshare' # 默认数据源 -config.data.cache_enabled = True # 是否启用缓存 -config.data.cache_dir = './data_cache' # 缓存目录 -config.data.cache_expire_days = 7 # 缓存过期天数 -config.data.quality_check = True # 数据质量检查 -config.data.auto_download = True # 自动下载缺失数据 -``` - -### 交易配置 (TradingConfig) - -```python -# 访问交易配置 -config.trading.server_host = '0.0.0.0' # 服务器地址 -config.trading.server_port = 8000 # 服务器端口 -config.trading.token_auto_generate = True # 自动生成token -config.trading.order_timeout = 30 # 订单超时时间(秒) -config.trading.max_retry_times = 3 # 最大重试次数 -config.trading.heartbeat_interval = 30 # 心跳间隔(秒) -``` - -### 日志配置 (LogConfig) - -```python -# 访问日志配置 -config.log.level = 'INFO' # 日志级别 -config.log.console_output = True # 控制台输出 -config.log.file_output = True # 文件输出 -config.log.log_dir = './logs' # 日志目录 -config.log.max_file_size = '10MB' # 最大文件大小 -config.log.backup_count = 10 # 备份文件数量 -``` - -### 绘图配置 (PlotConfig) - -```python -# 访问绘图配置 -config.plot.theme = 'plotly_white' # 图表主题 -config.plot.figure_height = 600 # 图表高度 -config.plot.figure_width = 1000 # 图表宽度 -config.plot.color_scheme = 'default' # 色彩方案 -config.plot.show_grid = True # 显示网格 -config.plot.auto_open = True # 自动打开图表 -``` - -## 配置方式 - -### 1. 配置文件 - -创建 JSON 格式的配置文件: - -```json -{ - "backtest": { - "initial_cash": 2000000, - "commission_rate": 0.0002, - "benchmark": "000300.SH" - }, - "data": { - "default_source": "qmt", - "cache_enabled": true, - "cache_dir": "./cache" - }, - "trading": { - "server_port": 9000, - "order_timeout": 60 - } -} -``` - -加载配置: - -```python -from qka.core.config import load_config - -config = load_config('production.json') -``` - -### 2. 环境变量 - -支持通过环境变量覆盖配置: - -```bash -# 设置环境变量 -export QKA_INITIAL_CASH=5000000 -export QKA_DATA_SOURCE=qmt -export QKA_SERVER_PORT=9000 -export QKA_COMMISSION_RATE=0.0002 -export QKA_CACHE_DIR=/tmp/qka_cache -``` - -环境变量会自动覆盖配置文件中的值。 - -### 3. 代码配置 - -直接在代码中修改配置: - -```python -import qka - -# 修改全局配置 -qka.config.backtest.initial_cash = 3_000_000 -qka.config.data.default_source = 'tushare' -qka.config.trading.server_port = 8080 - -# 或者创建新的配置实例 -from qka.core.config import Config - -custom_config = Config() -custom_config.backtest.initial_cash = 5_000_000 -``` - -## 高级用法 - -### 配置继承 - -```python -from qka.core.config import Config - -# 基础配置 -base_config = Config('base.json') - -# 继承并修改 -prod_config = Config() -prod_config.__dict__.update(base_config.__dict__) -prod_config.backtest.initial_cash = 10_000_000 -prod_config.save_to_file('production.json') -``` - -### 动态配置更新 - -```python -# 运行时修改配置 -def update_config_for_market_hours(): - if is_market_open(): - qka.config.data.auto_download = True - qka.config.trading.heartbeat_interval = 10 - else: - qka.config.data.auto_download = False - qka.config.trading.heartbeat_interval = 60 -``` - -### 配置验证 - -```python -def validate_config(config): - """验证配置的有效性""" - assert config.backtest.initial_cash > 0, "初始资金必须大于0" - assert config.backtest.commission_rate >= 0, "手续费率不能为负数" - assert config.trading.server_port > 1024, "端口号必须大于1024" - - print("✅ 配置验证通过") - -# 使用验证 -validate_config(qka.config) -``` - -## 最佳实践 - -### 1. 环境分离 - -为不同环境创建不同的配置文件: - -``` -config/ -├── development.json # 开发环境 -├── testing.json # 测试环境 -├── staging.json # 预发布环境 -└── production.json # 生产环境 -``` - -```python -import os -from qka.core.config import load_config - -# 根据环境变量选择配置 -env = os.getenv('QKA_ENV', 'development') -config = load_config(f'config/{env}.json') -``` - -### 2. 敏感信息处理 - -敏感信息(如API密钥)建议通过环境变量配置: - -```bash -# .env 文件 -QKA_API_KEY=your_secret_api_key -QKA_DATABASE_URL=postgresql://user:pass@localhost/db -``` - -```python -import os -from dotenv import load_dotenv - -# 加载 .env 文件 -load_dotenv() - -# 在配置中使用 -api_key = os.getenv('QKA_API_KEY') -``` - -### 3. 配置备份 - -```python -from qka.core.config import Config -from datetime import datetime - -def backup_config(): - """备份当前配置""" - timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') - backup_file = f'config_backup_{timestamp}.json' - qka.config.save_to_file(backup_file) - print(f"配置已备份到: {backup_file}") - -# 定期备份 -backup_config() -``` - -## 配置示例 - -### 开发环境配置 - -```json -{ - "backtest": { - "initial_cash": 1000000, - "commission_rate": 0.0003 - }, - "data": { - "default_source": "akshare", - "cache_enabled": true, - "cache_dir": "./dev_cache" - }, - "log": { - "level": "DEBUG", - "console_output": true - } -} -``` - -### 生产环境配置 - -```json -{ - "backtest": { - "initial_cash": 10000000, - "commission_rate": 0.0002 - }, - "data": { - "default_source": "qmt", - "cache_enabled": true, - "cache_dir": "/var/cache/qka" - }, - "log": { - "level": "INFO", - "file_output": true, - "log_dir": "/var/log/qka" - }, - "trading": { - "server_host": "0.0.0.0", - "server_port": 8000 - } -} -``` - -## API 参考 - -配置管理的详细API参考请查看 [Config API文档](../api/core/config.md)。 - -### 主要类和函数 - -- **Config** - 主配置管理类 -- **load_config** - 加载配置函数 -- **create_sample_config** - 创建示例配置函数 - -更多详细信息和使用示例请参考API文档页面。 diff --git a/docs/user-guide/data.md b/docs/user-guide/data.md index 050e850..3b7d850 100644 --- a/docs/user-guide/data.md +++ b/docs/user-guide/data.md @@ -1,283 +1,243 @@ -# 数据获取 - -QKA 提供了统一的数据获取接口,目前支持QMT和Akshare两种数据源。 - -## 支持的数据源 - -- **QMT** - 迅投QMT数据源,支持历史数据和实时数据 -- **Akshare** - 开源财经数据接口,支持历史数据(默认数据源) - -## 快速开始 - -### 设置默认数据源 - -```python -import qka - -# 查看可用的数据源 -print(qka.get_available_sources()) # ['qmt', 'akshare'] - -# 设置默认数据源(推荐在程序开始时设置) -qka.set_source('akshare') # 或 'qmt' - -# 查看当前默认数据源 -print(qka.get_source()) # akshare -``` - -### 简化的数据获取 - -设置默认数据源后,创建数据对象更加简洁: - -```python -import qka - -# 设置默认数据源 -qka.set_source('akshare') - -# 创建数据对象(自动使用默认数据源) -data_obj = qka.data(stocks=['000001', '600000']) - -# 获取历史数据 -hist_data = data_obj.get( - start_time='2023-01-01', - end_time='2023-12-31' -) -``` - -## 基本用法 - -### 使用QMT数据源 - -```python -import qka - -# 方式1:设置为默认数据源 -qka.set_source('qmt') -data_obj = qka.data(stocks=['000001.SZ', '600000.SH']) - -# 方式2:临时指定数据源 -data_obj = qka.data(stocks=['000001.SZ', '600000.SH'], source='qmt') - -# 获取历史数据 -hist_data = data_obj.get( - period='1d', - start_time='2023-01-01', - end_time='2023-12-31' -) - -# 订阅实时数据(需要QMT环境) -def on_data(code, item): - print(f"{code}: 价格={item['lastPrice']}") - -data_obj.subscribe(on_data) -``` - -### 使用Akshare数据源 - -```python -import qka - -# 方式1:使用默认数据源(akshare为默认) -data_obj = qka.data(stocks=['000001', '600000']) - -# 方式2:显式指定数据源 -data_obj = qka.data(stocks=['000001', '600000'], source='akshare') - -# 获取历史数据 -hist_data = data_obj.get( - start_time='2023-01-01', - end_time='2023-12-31' -) - -# 注意:Akshare不支持实时数据订阅 -``` - -### 板块数据获取 - -```python -import qka - -# 使用QMT获取板块股票 -qka.set_source('qmt') -data_obj = qka.data(sector='沪深A股主板') - -# 使用Akshare获取沪深A股(限制前100只) -qka.set_source('akshare') -data_obj = qka.data(sector='沪深A股') - -# 获取板块内所有股票的历史数据 -hist_data = data_obj.get(start_time='2023-01-01') -``` - -## 扩展数据源 - -QKA支持注册自定义数据源: - -```python -import qka -from qka.core.data.base import DataSource - -# 自定义数据源示例 -class TushareData(DataSource): - def get_historical_data(self, period='1d', start_time='', end_time=''): - # 实现tushare数据获取逻辑 - pass - - def subscribe_realtime(self, callback): - # 实现实时数据订阅逻辑 - pass - -# 注册新数据源 -qka.register_data_source('tushare', TushareData) - -# 使用新数据源 -qka.set_source('tushare') -data_obj = qka.data(stocks=['000001']) -``` - -## 数据格式 - -QKA统一了不同数据源的数据格式,返回标准的pandas DataFrame: - -```python -# 查看数据结构 -print(hist_data.keys()) # 获取股票列表 -print(hist_data['000001'].head()) # 查看具体股票数据 - -# 标准数据列 -# open: 开盘价 -# high: 最高价 -# low: 最低价 -# close: 收盘价 -# volume: 成交量 -# amount: 成交额(Akshare提供) -``` - -## 数据源差异 - -### QMT数据源特点 -- ✅ 支持历史数据和实时数据 -- ✅ 数据质量高,延迟低 -- ✅ 支持多种周期(日线、分钟线等) -- ❗ 需要安装QMT软件环境 - -### Akshare数据源特点 -- ✅ 免费开源,无需授权 -- ✅ 支持前复权数据调整 -- ✅ 安装简单,依赖较少 -- ❌ 不支持实时数据订阅 -- ❗ 仅支持日线数据 - -## 股票代码格式 - -不同数据源的股票代码格式: - -| 数据源 | 格式示例 | 说明 | -|--------|----------|------| -| QMT | `000001.SZ` | 带交易所后缀 | -| QMT | `600000.SH` | 沪市股票 | -| Akshare | `000001` | 不带后缀 | -| Akshare | `600000` | 6位数字代码 | - -## 常用数据字段 - -基础数据字段(所有数据源共有): - -| 字段名 | 描述 | 类型 | -|--------|------|------| -| open | 开盘价 | float | -| high | 最高价 | float | -| low | 最低价 | float | -| close | 收盘价 | float | -| volume | 成交量 | int | - -Akshare额外提供的字段: - -| 字段名 | 描述 | 类型 | -|--------|------|------| -| amount | 成交额 | float | - -## 错误处理 - -```python -try: - # 使用新的API方式 - qka.set_source('qmt') - data_obj = qka.data(stocks=['000001.SZ']) - hist_data = data_obj.get(start_time='2023-01-01') -except ImportError as e: - print(f"数据源依赖缺失: {e}") -except Exception as e: - print(f"数据获取失败: {e}") -``` - -## 注意事项 - -1. **默认数据源设置**:建议在程序开始时设置默认数据源,避免重复指定 -2. **QMT环境依赖**:使用QMT数据源需要安装QMT软件和xtquant库 -3. **Akshare限制**:仅支持日线数据,不支持实时数据订阅 -4. **股票代码格式**:QMT需要带交易所后缀,Akshare使用6位数字代码 -5. **数据获取频率**:避免过于频繁的API调用 -6. **数据源扩展**:可以通过`register_data_source()`注册自定义数据源 - -## API参考 - -### 配置函数 - -- `qka.set_source(source)` - 设置默认数据源 -- `qka.get_source()` - 获取当前默认数据源 -- `qka.get_available_sources()` - 获取所有可用数据源 -- `qka.register_data_source(name, class)` - 注册新数据源 - -### 工厂函数 - -- `qka.data(stocks=None, sector=None, source=None)` - 创建数据对象 - -### Data对象方法 - -- `data_obj.get(period='1d', start_time='', end_time='')` - 获取历史数据 -- `data_obj.subscribe(callback)` - 订阅实时数据 -- `data_obj.stocks` - 获取股票列表 - -## 完整示例 - -### 基础使用流程 - -```python -import qka - -# 1. 设置默认数据源 -qka.set_source('akshare') - -# 2. 创建数据对象 -data_obj = qka.data(stocks=['000001', '600000']) - -# 3. 获取历史数据 -hist_data = data_obj.get(start_time='2023-01-01', end_time='2023-12-31') - -# 4. 查看数据 -for stock_code, df in hist_data.items(): - print(f"\n{stock_code} 数据概览:") - print(df.head()) - print(f"数据行数: {len(df)}") -``` - -### 混合使用多个数据源 - -```python -import qka - -# 设置默认数据源为akshare -qka.set_source('akshare') - -# 大部分使用默认数据源 -data_ak = qka.data(stocks=['000001', '600000']) -hist_data_ak = data_ak.get(start_time='2023-01-01') - -# 临时使用QMT获取实时数据 -data_qmt = qka.data(stocks=['000001.SZ'], source='qmt') -def on_realtime_data(code, item): - print(f"实时数据 - {code}: {item['lastPrice']}") - -data_qmt.subscribe(on_realtime_data) -``` +# 数据获取 + +QKA 提供统一的数据获取接口,支持多种数据源和灵活的数据处理。 + +## 基本用法 + +### 创建数据对象 + +```python +import qka + +# 最简单的数据获取 +data = qka.Data(symbols=['000001.SZ', '600000.SH']) +df = data.get() +``` + +### 参数说明 + +```python +data = qka.Data( + symbols=['000001.SZ', '600000.SH'], # 股票代码列表 + period='1d', # 数据周期 + adjust='qfq', # 复权方式 + source='akshare', # 数据源 + pool_size=10, # 并发线程数 + datadir='./mydata' # 缓存目录 +) +``` + +## 数据周期 + +支持多种数据周期: + +```python +# 日线数据 +daily_data = qka.Data(symbols=['000001.SZ'], period='1d') + +# 分钟线数据(如果数据源支持) +minute_data = qka.Data(symbols=['000001.SZ'], period='1m') + +# 周线数据 +weekly_data = qka.Data(symbols=['000001.SZ'], period='1w') +``` + +## 复权方式 + +支持三种复权方式: + +```python +# 前复权(推荐) +qfq_data = qka.Data(symbols=['000001.SZ'], adjust='qfq') + +# 后复权 +hfq_data = qka.Data(symbols=['000001.SZ'], adjust='hfq') + +# 不复权 +bfq_data = qka.Data(symbols=['000001.SZ'], adjust='bfq') +``` + +## 数据源配置 + +### Akshare 数据源 + +Akshare 是默认的数据源,提供丰富的 A 股数据: + +```python +data = qka.Data( + symbols=['000001.SZ', '600000.SH'], + source='akshare' +) +``` + +### 自定义数据源 + +你可以扩展 Data 类来支持其他数据源: + +```python +class MyData(qka.Data): + def _get_from_custom_source(self, symbol: str): + # 实现自定义数据获取逻辑 + pass +``` + +## 因子计算 + +### 内置因子 + +数据获取时自动包含基础因子: + +- `open`: 开盘价 +- `high`: 最高价 +- `low`: 最低价 +- `close`: 收盘价 +- `volume`: 成交量 +- `amount`: 成交额 + +### 自定义因子 + +可以通过 `factor` 参数添加自定义因子: + +```python +def add_technical_indicators(df): + """添加技术指标""" + # 移动平均 + df['ma5'] = df['close'].rolling(5).mean() + df['ma20'] = df['close'].rolling(20).mean() + + # 相对强弱指标 (RSI) + delta = df['close'].diff() + gain = (delta.where(delta > 0, 0)).rolling(window=14).mean() + loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean() + rs = gain / loss + df['rsi'] = 100 - (100 / (1 + rs)) + + return df + +data = qka.Data( + symbols=['000001.SZ'], + factor=add_technical_indicators +) +``` + +## 并发下载 + +使用多线程并发下载提高效率: + +```python +# 设置并发线程数 +data = qka.Data( + symbols=large_stock_list, # 大量股票 + pool_size=20 # 增加并发数 +) +``` + +## 数据缓存 + +### 自动缓存 + +数据会自动缓存到本地,避免重复下载: + +```python +# 第一次下载数据 +data1 = qka.Data(symbols=['000001.SZ']) +df1 = data1.get() # 下载并缓存 + +# 第二次使用相同参数,直接从缓存读取 +data2 = qka.Data(symbols=['000001.SZ']) +df2 = data2.get() # 从缓存读取 +``` + +### 自定义缓存目录 + +```python +from pathlib import Path + +data = qka.Data( + symbols=['000001.SZ'], + datadir=Path('/path/to/cache') # 自定义缓存目录 +) +``` + +## 数据格式 + +### 返回数据格式 + +`data.get()` 返回 Dask DataFrame,每只股票的列名格式为 `{symbol}_{column}`: + +```python +df = data.get() +print(df.columns) +# 输出: ['000001.SZ_open', '000001.SZ_high', '000001.SZ_low', +# '000001.SZ_close', '000001.SZ_volume', '000001.SZ_amount', +# '600000.SH_open', ...] +``` + +### 数据访问 + +```python +# 获取特定股票的数据 +stock_000001 = df[['000001.SZ_open', '000001.SZ_close']] + +# 获取特定因子的所有股票数据 +all_close = df[[col for col in df.columns if col.endswith('_close')]] +``` + +## 高级用法 + +### 批量处理大量股票 + +```python +# 获取A股所有股票列表(需要akshare) +import akshare as ak +stock_list = ak.stock_info_a_code_name()['code'].tolist() + +# 分批处理 +batch_size = 100 +for i in range(0, len(stock_list), batch_size): + batch = stock_list[i:i+batch_size] + data = qka.Data(symbols=batch, pool_size=10) + df_batch = data.get() + # 处理这批数据... +``` + +### 数据更新 + +```python +# 强制更新缓存(重新下载) +import shutil +shutil.rmtree('datadir') # 删除缓存目录 + +# 或者指定新的缓存目录 +data = qka.Data(symbols=['000001.SZ'], datadir='./new_cache') +``` + +## 性能优化建议 + +1. **合理设置并发数**: 根据网络情况和系统资源调整 `pool_size` +2. **使用缓存**: 充分利用数据缓存避免重复下载 +3. **分批处理**: 对于大量股票,分批处理避免内存不足 +4. **选择合适的周期**: 根据策略需求选择合适的数据周期 + +## 常见问题 + +### Q: 数据下载失败怎么办? + +A: 检查网络连接,确认股票代码格式正确,可以尝试: +- 减少并发数 +- 更换数据源 +- 检查股票代码是否有效 + +### Q: 如何获取实时数据? + +A: 目前主要支持历史数据,实时数据可以通过 QMT 接口获取。 + +### Q: 数据包含哪些时间段? + +A: 默认包含该股票的所有可用历史数据。 + +## 下一步 + +- 学习 [回测分析](backtest.md) 了解如何使用数据进行策略回测 +- 查看 [API 文档](../api/core/data.md) 了解完整的数据接口 +- 参考 [示例教程](../examples/basic/data-fetching.md) 获取更多代码示例 \ No newline at end of file diff --git a/docs/user-guide/events.md b/docs/user-guide/events.md deleted file mode 100644 index 14d9997..0000000 --- a/docs/user-guide/events.md +++ /dev/null @@ -1,414 +0,0 @@ -# 事件系统 - -QKA 采用事件驱动架构,通过发布-订阅模式实现模块间的松耦合通信。事件系统让您可以监听和响应系统中发生的各种事件。 - -## 为什么使用事件系统? - -!!! tip "事件系统的优势" - - 🔗 **松耦合** - 模块间无需直接依赖 - - 📡 **实时响应** - 立即响应系统事件 - - 🔧 **易扩展** - 轻松添加新的事件处理逻辑 - - 📊 **可监控** - 完整的事件历史和统计 - -## 快速开始 - -### 启动事件系统 - -```python -from qka.core.events import start_event_engine, stop_event_engine - -# 启动事件引擎 -start_event_engine() - -# 程序结束时停止 -stop_event_engine() -``` - -### 监听事件 - -使用装饰器方式监听事件: - -```python -from qka.core.events import EventType, event_handler - -@event_handler(EventType.DATA_LOADED) -def on_data_loaded(event): - print(f"数据加载完成: {event.data}") - -@event_handler(EventType.ORDER_FILLED) -def on_order_filled(event): - print(f"订单成交: {event.data}") -``` - -### 发送事件 - -```python -from qka.core.events import emit_event - -# 发送数据加载事件 -emit_event(EventType.DATA_LOADED, { - "symbol": "000001.SZ", - "rows": 1000, - "timespan": "2023-01-01 to 2023-12-31" -}) - -# 发送订单成交事件 -emit_event(EventType.ORDER_FILLED, { - "order_id": "12345", - "symbol": "000001.SZ", - "price": 10.50, - "quantity": 1000 -}) -``` - -## 事件类型详解 - -### 数据相关事件 - -#### DATA_LOADED - 数据加载完成 -当数据成功加载时触发。 - -```python -@event_handler(EventType.DATA_LOADED) -def on_data_loaded(event): - data = event.data - print(f"加载了 {data['symbol']} 的 {data['rows']} 条数据") -``` - -**事件数据示例**: -```python -{ - "symbol": "000001.SZ", - "rows": 1000, - "timespan": "2023-01-01 to 2023-12-31", - "source": "akshare" -} -``` - -#### DATA_ERROR - 数据加载错误 -当数据加载失败时触发。 - -```python -@event_handler(EventType.DATA_ERROR) -def on_data_error(event): - error = event.data - print(f"数据加载失败: {error['message']}") -``` - -### 交易相关事件 - -#### ORDER_CREATED - 订单创建 -当新订单被创建时触发。 - -```python -@event_handler(EventType.ORDER_CREATED) -def on_order_created(event): - order = event.data - print(f"创建订单: {order['action']} {order['symbol']} @ {order['price']}") -``` - -#### ORDER_FILLED - 订单成交 -当订单成交时触发。 - -```python -@event_handler(EventType.ORDER_FILLED) -def on_order_filled(event): - trade = event.data - print(f"订单成交: {trade['symbol']} 成交价 {trade['price']}") -``` - -#### ORDER_CANCELLED - 订单取消 -当订单被取消时触发。 - -```python -@event_handler(EventType.ORDER_CANCELLED) -def on_order_cancelled(event): - order = event.data - print(f"订单取消: {order['order_id']}") -``` - -### 策略相关事件 - -#### STRATEGY_START - 策略开始 -当策略开始运行时触发。 - -```python -@event_handler(EventType.STRATEGY_START) -def on_strategy_start(event): - strategy = event.data - print(f"策略 {strategy['name']} 开始运行") -``` - -#### SIGNAL_GENERATED - 信号生成 -当策略生成交易信号时触发。 - -```python -@event_handler(EventType.SIGNAL_GENERATED) -def on_signal_generated(event): - signal = event.data - print(f"信号: {signal['action']} {signal['symbol']}") - - # 可以在这里添加信号过滤、风险检查等逻辑 - if signal['strength'] > 0.8: - print("强信号,建议关注!") -``` - -## 高级用法 - -### 自定义事件处理器 - -创建自定义的事件处理器类: - -```python -from qka.core.events import EventHandler, Event - -class TradingEventHandler(EventHandler): - def __init__(self): - self.order_count = 0 - self.total_volume = 0 - - def handle(self, event: Event): - if event.event_type == EventType.ORDER_FILLED: - self.order_count += 1 - self.total_volume += event.data.get('quantity', 0) - print(f"总订单数: {self.order_count}, 总成交量: {self.total_volume}") - - def can_handle(self, event: Event) -> bool: - return event.event_type == EventType.ORDER_FILLED - -# 注册处理器 -from qka.core.events import event_engine - -handler = TradingEventHandler() -event_engine.subscribe(EventType.ORDER_FILLED, handler) -``` - -### 事件过滤 - -只处理特定条件的事件: - -```python -@event_handler(EventType.SIGNAL_GENERATED) -def handle_strong_signals(event): - signal = event.data - - # 只处理强信号 - if signal.get('strength', 0) > 0.8: - print(f"收到强信号: {signal}") - # 执行相应操作 -``` - -### 异步事件处理 - -```python -from qka.core.events import AsyncEventHandler -import asyncio - -class AsyncOrderHandler(AsyncEventHandler): - async def handle_async(self, event: Event): - if event.event_type == EventType.ORDER_FILLED: - # 异步处理订单 - await self.update_portfolio(event.data) - await self.send_notification(event.data) - - async def update_portfolio(self, order_data): - # 模拟异步数据库操作 - await asyncio.sleep(0.1) - print(f"组合更新完成: {order_data}") - - async def send_notification(self, order_data): - # 模拟异步通知发送 - await asyncio.sleep(0.1) - print(f"通知已发送: {order_data}") -``` - -## 事件统计和监控 - -### 查看事件统计 - -```python -from qka.core.events import event_engine - -# 获取统计信息 -stats = event_engine.get_statistics() - -print(f"事件计数: {stats['event_count']}") -print(f"错误计数: {stats['error_count']}") -print(f"队列大小: {stats['queue_size']}") -print(f"处理器数量: {stats['handler_count']}") -print(f"运行状态: {stats['is_running']}") -``` - -### 查看事件历史 - -```python -# 获取所有事件历史 -all_events = event_engine.get_event_history(limit=100) - -# 获取特定类型的事件历史 -order_events = event_engine.get_event_history( - event_type=EventType.ORDER_FILLED, - limit=50 -) - -for event in order_events: - print(f"{event.timestamp}: {event.data}") -``` - -## 在策略中使用事件 - -### 事件驱动的策略 - -```python -from qka.core.backtest import Strategy -from qka.core.events import EventType, emit_event - -class EventDrivenStrategy(Strategy): - def on_start(self, broker): - # 策略开始时发送事件 - emit_event(EventType.STRATEGY_START, { - "strategy": self.name, - "initial_cash": broker.initial_cash - }) - - def on_bar(self, data, broker, current_date): - for symbol, df in data.items(): - if len(df) >= 20: - current_price = df['close'].iloc[-1] - ma20 = df['close'].rolling(20).mean().iloc[-1] - - # 生成信号事件 - if current_price > ma20: - emit_event(EventType.SIGNAL_GENERATED, { - "symbol": symbol, - "action": "BUY", - "price": current_price, - "strength": 0.8, - "reason": "价格突破20日均线" - }) - - # 执行买入 - if broker.buy(symbol, 0.3, current_price): - emit_event(EventType.ORDER_FILLED, { - "symbol": symbol, - "action": "BUY", - "price": current_price, - "quantity": broker.get_position(symbol) - }) -``` - -### 事件监听器 - -```python -# 策略性能监控 -@event_handler(EventType.ORDER_FILLED) -def monitor_strategy_performance(event): - order = event.data - - # 记录交易日志 - with open('trades.log', 'a') as f: - f.write(f"{event.timestamp}: {order}\n") - - # 计算收益 - if order['action'] == 'SELL': - # 计算这笔交易的盈亏 - pass - -# 风险监控 -@event_handler(EventType.SIGNAL_GENERATED) -def risk_monitor(event): - signal = event.data - - # 检查是否有过度交易 - if signal['strength'] < 0.5: - print(f"⚠️ 弱信号警告: {signal}") - - # 检查仓位集中度 - # ... -``` - -## 最佳实践 - -### 1. 事件命名 - -为自定义事件使用清晰的命名: - -```python -# 好的命名 -EventType.PORTFOLIO_REBALANCED -EventType.RISK_LIMIT_EXCEEDED -EventType.MARKET_DATA_UPDATED - -# 避免的命名 -EventType.EVENT1 -EventType.SOMETHING_HAPPENED -``` - -### 2. 事件数据结构 - -保持事件数据结构的一致性: - -```python -# 推荐的事件数据结构 -{ - "timestamp": "2023-12-01T10:30:00", - "symbol": "000001.SZ", - "action": "BUY", - "price": 10.50, - "quantity": 1000, - "metadata": { - "strategy": "MA_CROSS", - "signal_strength": 0.85 - } -} -``` - -### 3. 错误处理 - -在事件处理器中添加适当的错误处理: - -```python -@event_handler(EventType.ORDER_FILLED) -def safe_order_handler(event): - try: - # 处理订单逻辑 - process_order(event.data) - except Exception as e: - print(f"处理订单事件时出错: {e}") - # 发送错误事件 - emit_event(EventType.ORDER_ERROR, { - "original_event": event.to_dict(), - "error": str(e) - }) -``` - -### 4. 性能考虑 - -避免在事件处理器中执行耗时操作: - -```python -# ❌ 避免在事件处理器中执行耗时操作 -@event_handler(EventType.DATA_LOADED) -def slow_handler(event): - time.sleep(5) # 这会阻塞事件队列 - -# ✅ 使用异步处理或后台任务 -@event_handler(EventType.DATA_LOADED) -def fast_handler(event): - # 快速处理或提交到后台队列 - background_queue.put(event.data) -``` - -## API 参考 - -事件系统的详细API参考请查看 [Events API文档](../api/core/events.md)。 - -### 主要类和函数 - -- **EventBus** - 事件总线 -- **Event** - 基础事件类 -- **MarketDataEvent** - 市场数据事件 -- **OrderEvent** - 订单事件 -- **TradeEvent** - 交易事件 - -更多详细信息和使用示例请参考API文档页面。 diff --git a/docs/user-guide/logging.md b/docs/user-guide/logging.md deleted file mode 100644 index 9feffac..0000000 --- a/docs/user-guide/logging.md +++ /dev/null @@ -1,522 +0,0 @@ -# 日志系统 - -QKA 提供了强大而灵活的日志系统,支持彩色输出、结构化日志、文件轮转和远程通知等功能。 - -## 为什么需要好的日志系统? - -!!! tip "日志系统的重要性" - - 🐛 **问题诊断** - 快速定位和解决问题 - - 📊 **性能监控** - 监控系统运行状态 - - 🔍 **行为追踪** - 记录用户操作和系统行为 - - 📈 **数据分析** - 提供业务分析数据 - - ⚠️ **告警通知** - 及时发现系统异常 - -## 快速开始 - -### 基础日志记录 - -```python -from qka.utils.logger import create_logger - -# 创建日志记录器 -logger = create_logger('my_app') - -# 记录不同级别的日志 -logger.debug("调试信息:变量值为 x=10") -logger.info("程序正常运行") -logger.warning("这是一个警告") -logger.error("发生错误") -logger.critical("严重错误,程序可能无法继续") -``` - -### 彩色日志输出 - -```python -# 启用彩色控制台输出 -logger = create_logger('my_app', colored_console=True) - -logger.debug("调试信息") # 青色 -logger.info("普通信息") # 绿色 -logger.warning("警告信息") # 黄色 -logger.error("错误信息") # 红色 -logger.critical("严重错误") # 紫色 -``` - -## 日志配置 - -### 基本配置 - -```python -from qka.utils.logger import create_logger - -logger = create_logger( - name='my_app', # 日志记录器名称 - level='INFO', # 日志级别: DEBUG, INFO, WARNING, ERROR, CRITICAL - console_output=True, # 是否输出到控制台 - file_output=True, # 是否输出到文件 - log_dir='logs', # 日志文件目录 - max_file_size='10MB', # 最大文件大小 - backup_count=10, # 备份文件数量 - json_format=False, # 是否使用JSON格式 - colored_console=True # 控制台是否使用颜色 -) -``` - -### 使用配置文件 - -```python -import qka -from qka.utils.logger import setup_logging_from_config - -# 使用全局配置 -logger = create_logger( - level=qka.config.log.level, - log_dir=qka.config.log.log_dir, - max_file_size=qka.config.log.max_file_size, - backup_count=qka.config.log.backup_count -) -``` - -## 结构化日志 - -### 基础结构化日志 - -```python -from qka.utils.logger import get_structured_logger - -# 创建结构化日志记录器 -struct_logger = get_structured_logger('my_app') - -# 记录带有额外字段的日志 -struct_logger.info("用户登录", - user_id=12345, - ip="192.168.1.100", - action="login", - success=True) - -struct_logger.error("交易失败", - symbol="000001.SZ", - reason="余额不足", - amount=10000, - user_id=12345) -``` - -### 交易日志示例 - -```python -# 记录订单信息 -struct_logger.info("订单创建", - order_id="ORD001", - symbol="000001.SZ", - side="BUY", - quantity=1000, - price=10.50, - strategy="MA_CROSS") - -# 记录成交信息 -struct_logger.info("订单成交", - order_id="ORD001", - fill_price=10.48, - fill_quantity=1000, - commission=3.14, - timestamp="2023-12-01T10:30:00") - -# 记录策略信号 -struct_logger.info("信号生成", - strategy="MA_CROSS", - symbol="000001.SZ", - signal="BUY", - strength=0.85, - indicators={ - "ma5": 10.45, - "ma20": 10.30, - "volume": 1500000 - }) -``` - -## 文件日志管理 - -### 自动文件轮转 - -QKA 支持自动文件轮转,防止日志文件过大: - -```python -logger = create_logger( - 'my_app', - max_file_size='50MB', # 单个文件最大50MB - backup_count=20 # 保留20个备份文件 -) -``` - -文件结构示例: -``` -logs/ -├── 2023-12-01.log # 当前日志文件 -├── 2023-12-01.log.1 # 备份文件1 -├── 2023-12-01.log.2 # 备份文件2 -└── ... -``` - -### JSON格式日志 - -适合后续分析和处理: - -```python -logger = create_logger('my_app', json_format=True) - -logger.info("这将以JSON格式记录", extra_field="value") -``` - -JSON输出示例: -```json -{ - "timestamp": "2023-12-01T10:30:00.123456", - "level": "INFO", - "logger": "my_app", - "message": "这将以JSON格式记录", - "module": "main", - "function": "main", - "line": 15, - "extra_field": "value" -} -``` - -## 远程通知 - -### 微信群通知 - -为重要错误添加微信群通知: - -```python -from qka.utils.logger import add_wechat_handler - -# 添加微信通知(只通知ERROR级别及以上) -webhook_url = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_KEY" -add_wechat_handler(logger, webhook_url, level='ERROR') - -# 现在ERROR和CRITICAL级别的日志会发送到微信群 -logger.error("这条错误信息会发送到微信群") -logger.critical("这条严重错误也会发送到微信群") -logger.warning("这条警告不会发送到微信群") -``` - -### 自定义通知处理器 - -```python -import requests -from qka.utils.logger import logger - -class SlackHandler(logging.Handler): - def __init__(self, webhook_url): - super().__init__() - self.webhook_url = webhook_url - - def emit(self, record): - message = self.format(record) - payload = {"text": message} - try: - requests.post(self.webhook_url, json=payload) - except Exception as e: - print(f"发送Slack消息失败: {e}") - -# 添加Slack通知 -slack_handler = SlackHandler("YOUR_SLACK_WEBHOOK_URL") -slack_handler.setLevel(logging.ERROR) -logger.addHandler(slack_handler) -``` - -## 在策略中使用日志 - -### 策略日志记录 - -```python -from qka.core.backtest import Strategy -from qka.utils.logger import create_logger - -class LoggedStrategy(Strategy): - def __init__(self): - super().__init__() - # 为策略创建专用日志记录器 - self.logger = create_logger(f'strategy_{self.name}', colored_console=True) - - def on_start(self, broker): - self.logger.info(f"策略启动", - strategy=self.name, - initial_cash=broker.initial_cash, - commission_rate=broker.commission_rate) - - def on_bar(self, data, broker, current_date): - for symbol, df in data.items(): - if len(df) >= 20: - current_price = df['close'].iloc[-1] - ma20 = df['close'].rolling(20).mean().iloc[-1] - - if current_price > ma20 and broker.get_position(symbol) == 0: - self.logger.info(f"买入信号", - symbol=symbol, - price=current_price, - ma20=ma20, - signal_strength=(current_price - ma20) / ma20) - - if broker.buy(symbol, 0.3, current_price): - self.logger.info(f"买入成功", - symbol=symbol, - price=current_price, - quantity=broker.get_position(symbol), - cash_remaining=broker.get_cash()) - else: - self.logger.warning(f"买入失败", - symbol=symbol, - price=current_price, - reason="资金不足或其他原因") - - elif current_price < ma20 and broker.get_position(symbol) > 0: - self.logger.info(f"卖出信号", - symbol=symbol, - price=current_price, - ma20=ma20, - position=broker.get_position(symbol)) - - if broker.sell(symbol, 1.0, current_price): - self.logger.info(f"卖出成功", - symbol=symbol, - price=current_price, - cash_after=broker.get_cash()) - - def on_end(self, broker): - final_value = broker.get_total_value({}) - self.logger.info(f"策略结束", - strategy=self.name, - final_value=final_value, - return_rate=(final_value - broker.initial_cash) / broker.initial_cash, - total_trades=len(broker.trades)) -``` - -### 交易日志分析 - -```python -import json -from datetime import datetime - -def analyze_trading_logs(log_file): - """分析交易日志文件""" - trades = [] - - with open(log_file, 'r', encoding='utf-8') as f: - for line in f: - if '买入成功' in line or '卖出成功' in line: - # 解析日志行(如果是JSON格式) - try: - log_data = json.loads(line) - trades.append(log_data) - except: - # 普通格式的解析 - pass - - # 分析交易数据 - print(f"总交易次数: {len(trades)}") - return trades - -# 使用示例 -trades = analyze_trading_logs('logs/strategy_MA_CROSS.log') -``` - -## 性能监控日志 - -### 函数执行时间记录 - -```python -from qka.utils.tools import timer -from qka.utils.logger import create_logger - -logger = create_logger('performance') - -@timer -def slow_function(): - """这个装饰器会自动记录执行时间""" - import time - time.sleep(1) - return "完成" - -# 手动记录性能 -import time - -def manual_timing_example(): - start_time = time.time() - - # 执行一些操作 - result = complex_calculation() - - end_time = time.time() - logger.info("计算完成", - function="complex_calculation", - execution_time=end_time - start_time, - result_size=len(result)) -``` - -### 内存使用监控 - -```python -import psutil -import os - -def log_memory_usage(): - process = psutil.Process(os.getpid()) - memory_info = process.memory_info() - - logger.info("内存使用情况", - rss_mb=memory_info.rss / 1024 / 1024, # 物理内存 - vms_mb=memory_info.vms / 1024 / 1024, # 虚拟内存 - cpu_percent=process.cpu_percent()) - -# 定期记录内存使用 -import threading -import time - -def memory_monitor(): - while True: - log_memory_usage() - time.sleep(60) # 每分钟记录一次 - -# 启动内存监控线程 -monitor_thread = threading.Thread(target=memory_monitor, daemon=True) -monitor_thread.start() -``` - -## 日志最佳实践 - -### 1. 日志级别使用 - -```python -# DEBUG - 详细的诊断信息,仅在诊断问题时使用 -logger.debug(f"计算中间结果: ma5={ma5}, ma20={ma20}") - -# INFO - 一般信息,记录程序正常运行状态 -logger.info("策略开始运行", strategy="MA_CROSS") - -# WARNING - 警告信息,程序可以继续运行但需要注意 -logger.warning("数据缺失", symbol="000001.SZ", missing_days=3) - -# ERROR - 错误信息,功能无法正常执行但程序可以继续 -logger.error("订单下单失败", symbol="000001.SZ", reason="余额不足") - -# CRITICAL - 严重错误,程序可能无法继续运行 -logger.critical("数据库连接失败", error="连接超时") -``` - -### 2. 敏感信息处理 - -```python -# ❌ 不要记录敏感信息 -logger.info("用户登录", password="123456", api_key="secret_key") - -# ✅ 正确的做法 -logger.info("用户登录", - user_id="12345", - ip="192.168.1.100", - # 密码和API密钥不记录 - login_method="password") -``` - -### 3. 结构化信息 - -```python -# ❌ 字符串拼接,难以解析 -logger.info(f"用户{user_id}在{timestamp}买入{symbol}数量{quantity}") - -# ✅ 结构化记录,便于后续分析 -logger.info("用户下单", - user_id=user_id, - timestamp=timestamp, - action="BUY", - symbol=symbol, - quantity=quantity) -``` - -### 4. 异常记录 - -```python -try: - result = risky_operation() -except Exception as e: - # 记录完整的异常信息 - logger.error("操作失败", - operation="risky_operation", - error_type=type(e).__name__, - error_message=str(e), - exc_info=True) # 包含完整的堆栈跟踪 -``` - -## 日志分析工具 - -### 实时日志监控 - -```python -import subprocess -import re - -def tail_logs(log_file, pattern=None): - """实时监控日志文件""" - process = subprocess.Popen(['tail', '-f', log_file], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True) - - try: - for line in iter(process.stdout.readline, ''): - if pattern is None or re.search(pattern, line): - print(line.strip()) - except KeyboardInterrupt: - process.terminate() - -# 使用示例 -# tail_logs('logs/2023-12-01.log', 'ERROR') # 只显示错误日志 -``` - -### 日志统计 - -```python -import re -from collections import defaultdict - -def analyze_log_file(log_file): - """分析日志文件统计信息""" - level_counts = defaultdict(int) - error_types = defaultdict(int) - - with open(log_file, 'r', encoding='utf-8') as f: - for line in f: - # 统计日志级别 - if match := re.search(r'\[(DEBUG|INFO|WARNING|ERROR|CRITICAL)\]', line): - level_counts[match.group(1)] += 1 - - # 统计错误类型 - if 'ERROR' in line and '失败' in line: - if '下单失败' in line: - error_types['下单失败'] += 1 - elif '数据获取失败' in line: - error_types['数据获取失败'] += 1 - - print("日志级别统计:") - for level, count in level_counts.items(): - print(f" {level}: {count}") - - print("\n错误类型统计:") - for error_type, count in error_types.items(): - print(f" {error_type}: {count}") - -# 使用示例 -analyze_log_file('logs/2023-12-01.log') -``` - -## API 参考 - -日志系统的详细API参考请查看 [Logger API文档](../api/utils/logger.md)。 - -### 主要类和函数 - -- **Logger** - 增强日志记录器 -- **ColorFormatter** - 彩色日志格式化器 -- **StructuredLogger** - 结构化日志记录器 - -更多详细信息和使用示例请参考API文档页面。 diff --git a/docs/user-guide/strategy.md b/docs/user-guide/strategy.md deleted file mode 100644 index b8c3c74..0000000 --- a/docs/user-guide/strategy.md +++ /dev/null @@ -1,340 +0,0 @@ -# 策略开发 - -QKA 提供了灵活的策略开发框架,支持多种交易策略模式。 - -## 策略基类 - -所有策略都需要继承 `Strategy` 基类: - -```python -from qka.core.backtest import Strategy - -class MyStrategy(Strategy): - def __init__(self): - super().__init__() - self.name = "我的策略" - self.description = "策略描述" - - def on_init(self): - """策略初始化""" - self.log("策略初始化完成") - - def on_data(self, data): - """数据更新时调用""" - # 策略逻辑 - pass - - def on_order(self, order): - """订单状态变化时调用""" - self.log(f"订单状态: {order.status}") - - def on_trade(self, trade): - """成交时调用""" - self.log(f"成交: {trade.symbol} {trade.volume}股") -``` - -## 简单策略示例 - -### 均线策略 - -```python -import pandas as pd -from qka.core.backtest import Strategy - -class MovingAverageStrategy(Strategy): - def __init__(self, short_window=20, long_window=50): - super().__init__() - self.short_window = short_window - self.long_window = long_window - self.name = f"MA策略({short_window}/{long_window})" - - def on_init(self): - # 订阅数据 - self.subscribe('000001.SZ') - - # 初始化指标 - self.short_ma = {} - self.long_ma = {} - - def on_data(self, data): - for symbol in data.index: - # 计算移动平均线 - prices = self.get_price_history(symbol, self.long_window) - if len(prices) < self.long_window: - continue - - short_ma = prices[-self.short_window:].mean() - long_ma = prices.mean() - - prev_short = self.short_ma.get(symbol, 0) - prev_long = self.long_ma.get(symbol, 0) - - # 金叉买入 - if short_ma > long_ma and prev_short <= prev_long: - if not self.get_position(symbol): - self.buy(symbol, 100) - self.log(f"{symbol} 金叉买入") - - # 死叉卖出 - elif short_ma < long_ma and prev_short >= prev_long: - if self.get_position(symbol): - self.sell(symbol, self.get_position(symbol).volume) - self.log(f"{symbol} 死叉卖出") - - self.short_ma[symbol] = short_ma - self.long_ma[symbol] = long_ma -``` - -### 布林带策略 - -```python -class BollingerBandStrategy(Strategy): - def __init__(self, window=20, num_std=2): - super().__init__() - self.window = window - self.num_std = num_std - self.name = f"布林带策略({window}, {num_std})" - - def calculate_bollinger_bands(self, prices): - """计算布林带""" - rolling_mean = prices.rolling(window=self.window).mean() - rolling_std = prices.rolling(window=self.window).std() - - upper_band = rolling_mean + (rolling_std * self.num_std) - lower_band = rolling_mean - (rolling_std * self.num_std) - - return upper_band, rolling_mean, lower_band - - def on_data(self, data): - for symbol in data.index: - prices = self.get_price_history(symbol, self.window + 10) - if len(prices) < self.window: - continue - - upper, middle, lower = self.calculate_bollinger_bands(prices) - current_price = data.loc[symbol, 'close'] - - position = self.get_position(symbol) - - # 价格触及下轨买入 - if current_price <= lower.iloc[-1] and not position: - self.buy(symbol, 100) - self.log(f"{symbol} 触及下轨买入") - - # 价格触及上轨卖出 - elif current_price >= upper.iloc[-1] and position: - self.sell(symbol, position.volume) - self.log(f"{symbol} 触及上轨卖出") -``` - -## 高级策略特性 - -### 多股票策略 - -```python -class MultiStockStrategy(Strategy): - def __init__(self): - super().__init__() - self.stock_pool = ['000001.SZ', '000002.SZ', '600000.SH'] - self.max_positions = 3 - - def on_init(self): - # 订阅股票池 - for symbol in self.stock_pool: - self.subscribe(symbol) - - def select_stocks(self, data): - """股票选择逻辑""" - scores = {} - for symbol in self.stock_pool: - # 计算股票评分 - score = self.calculate_score(symbol, data) - scores[symbol] = score - - # 返回评分最高的股票 - return sorted(scores.items(), key=lambda x: x[1], reverse=True)[:self.max_positions] - - def calculate_score(self, symbol, data): - """计算股票评分""" - # 实现评分逻辑 - return 0.5 -``` - -### 动态调仓策略 - -```python -class RebalanceStrategy(Strategy): - def __init__(self): - super().__init__() - self.rebalance_frequency = 'monthly' # 调仓频率 - self.target_weights = { - '000001.SZ': 0.4, - '000002.SZ': 0.3, - '600000.SH': 0.3 - } - - def should_rebalance(self): - """判断是否需要调仓""" - # 实现调仓条件判断 - return True - - def rebalance(self): - """执行调仓""" - total_value = self.get_total_value() - - for symbol, target_weight in self.target_weights.items(): - target_value = total_value * target_weight - current_value = self.get_position_value(symbol) - - if abs(target_value - current_value) > 1000: # 阈值 - if target_value > current_value: - # 买入 - amount = target_value - current_value - self.buy_value(symbol, amount) - else: - # 卖出 - amount = current_value - target_value - self.sell_value(symbol, amount) -``` - -## 策略参数优化 - -### 参数扫描 - -```python -from qka.core.backtest import ParameterOptimizer - -# 定义参数范围 -param_ranges = { - 'short_window': range(5, 21, 5), - 'long_window': range(20, 61, 10) -} - -# 创建优化器 -optimizer = ParameterOptimizer(MovingAverageStrategy, param_ranges) - -# 执行优化 -best_params = optimizer.optimize( - objective='sharpe_ratio', # 优化目标 - data_range=('2023-01-01', '2023-12-31') -) - -print(f"最优参数: {best_params}") -``` - -### 遗传算法优化 - -```python -from qka.core.backtest import GeneticOptimizer - -optimizer = GeneticOptimizer( - strategy_class=MovingAverageStrategy, - param_ranges=param_ranges, - population_size=50, - generations=100 -) - -best_params = optimizer.evolve() -``` - -## 风险管理 - -### 止损止盈 - -```python -class RiskManagedStrategy(Strategy): - def __init__(self): - super().__init__() - self.stop_loss_ratio = 0.05 # 5% 止损 - self.take_profit_ratio = 0.10 # 10% 止盈 - - def check_risk_management(self, symbol): - """检查风险管理条件""" - position = self.get_position(symbol) - if not position: - return - - current_price = self.get_current_price(symbol) - cost_price = position.avg_price - - # 计算收益率 - return_rate = (current_price - cost_price) / cost_price - - # 止损 - if return_rate <= -self.stop_loss_ratio: - self.sell(symbol, position.volume) - self.log(f"{symbol} 触发止损") - - # 止盈 - elif return_rate >= self.take_profit_ratio: - self.sell(symbol, position.volume) - self.log(f"{symbol} 触发止盈") -``` - -### 仓位管理 - -```python -def calculate_position_size(self, symbol, signal_strength): - """计算仓位大小""" - available_cash = self.get_available_cash() - risk_per_trade = available_cash * 0.02 # 每次交易风险2% - - stop_loss_distance = self.calculate_stop_loss_distance(symbol) - position_size = risk_per_trade / stop_loss_distance - - # 调整仓位大小基于信号强度 - position_size *= signal_strength - - return min(position_size, available_cash * 0.1) # 最大10%仓位 -``` - -## 策略评估 - -### 性能指标 - -```python -# 在策略中记录关键指标 -def on_trade(self, trade): - super().on_trade(trade) - - # 记录自定义指标 - self.record('trade_count', self.get_trade_count()) - self.record('win_rate', self.calculate_win_rate()) - self.record('max_drawdown', self.calculate_max_drawdown()) -``` - -### 策略诊断 - -```python -from qka.core.backtest import StrategyAnalyzer - -analyzer = StrategyAnalyzer() - -# 分析策略表现 -analysis = analyzer.analyze_strategy(strategy_result) - -print(f"年化收益率: {analysis.annual_return:.2%}") -print(f"夏普比率: {analysis.sharpe_ratio:.2f}") -print(f"最大回撤: {analysis.max_drawdown:.2%}") -``` - -## 最佳实践 - -1. **清晰的逻辑结构**:将策略逻辑分解为独立的方法 -2. **参数化设计**:将关键参数设为可配置 -3. **充分的日志记录**:记录关键决策点和执行过程 -4. **风险控制**:始终包含风险管理逻辑 -5. **性能监控**:定期评估策略表现 -6. **版本控制**:保存策略的不同版本 - -## API 参考 - -策略开发的详细API参考请查看 [Strategy API文档](../api/core/backtest.md)。 - -### 主要类和接口 - -- **Strategy** - 策略基类 -- **BacktestEngine** - 回测引擎 -- **Portfolio** - 投资组合管理 - -更多详细信息和示例请参考API文档页面。 diff --git a/docs/user-guide/trading.md b/docs/user-guide/trading.md index 482f195..533fe1d 100644 --- a/docs/user-guide/trading.md +++ b/docs/user-guide/trading.md @@ -1,512 +1,272 @@ -# 实盘交易 - -QKA 提供了完整的实盘交易解决方案,支持多种券商接口和交易模式。 - -## 交易环境配置 - -### 券商配置 - -在配置文件中设置券商信息: - -```json -{ - "trading": { - "broker": "华泰证券", - "account": "your_account", - "server_host": "localhost", - "server_port": 8888, - "timeout": 30, - "auto_login": true, - "credentials": { - "username": "your_username", - "password": "your_password", - "communication_password": "your_comm_password" - } - } -} -``` - -### 安全设置 - -```python -import os -from qka.core.config import config - -# 使用环境变量存储敏感信息 -os.environ['QKA_BROKER_USERNAME'] = 'your_username' -os.environ['QKA_BROKER_PASSWORD'] = 'your_password' - -# 配置会自动读取环境变量 -config.trading.credentials.username = os.getenv('QKA_BROKER_USERNAME') -``` - -## 交易客户端 - -### 创建交易客户端 - -```python -from qka.brokers import QMTClient -from qka.brokers.trade import Order, Trade, Position - -# 创建交易客户端 -client = QMTClient(base_url="http://localhost:8000", token="your_token") - -# 连接到券商 -try: - client.connect() - print("连接成功") -except Exception as e: - print(f"连接失败: {e}") -``` - -### 账户信息查询 - -```python -# 查询账户资金 -account_info = client.get_account_info() -print(f"总资产: {account_info.total_value:,.2f}") -print(f"可用资金: {account_info.available_cash:,.2f}") -print(f"市值: {account_info.market_value:,.2f}") - -# 查询持仓 -positions = client.get_positions() -for position in positions: - print(f"{position.symbol}: {position.volume}股, 成本: {position.cost:.2f}") - -# 查询委托 -orders = client.get_orders() -for order in orders: - print(f"{order.symbol}: {order.status} {order.volume}股 @ {order.price:.2f}") -``` - -## 下单交易 - -### 基本下单 - -```python -from qka.brokers.trade import OrderType, OrderSide - -# 市价买入 -buy_order = client.place_order( - symbol='000001.SZ', - side=OrderSide.BUY, - order_type=OrderType.MARKET, - volume=100 -) - -print(f"买单ID: {buy_order.order_id}") - -# 限价卖出 -sell_order = client.place_order( - symbol='000001.SZ', - side=OrderSide.SELL, - order_type=OrderType.LIMIT, - volume=100, - price=15.50 -) - -print(f"卖单ID: {sell_order.order_id}") -``` - -### 高级订单类型 - -```python -# 止损单 -stop_loss_order = client.place_order( - symbol='000001.SZ', - side=OrderSide.SELL, - order_type=OrderType.STOP_LOSS, - volume=100, - stop_price=14.50 -) - -# 止盈单 -take_profit_order = client.place_order( - symbol='000001.SZ', - side=OrderSide.SELL, - order_type=OrderType.TAKE_PROFIT, - volume=100, - stop_price=16.50 -) - -# 条件单 -conditional_order = client.place_conditional_order( - symbol='000001.SZ', - condition='price >= 16.0', - action='buy', - volume=200 -) -``` - -### 批量下单 - -```python -# 批量下单 -orders = [ - { - 'symbol': '000001.SZ', - 'side': OrderSide.BUY, - 'volume': 100, - 'price': 15.00 - }, - { - 'symbol': '000002.SZ', - 'side': OrderSide.BUY, - 'volume': 200, - 'price': 12.50 - } -] - -batch_results = client.place_batch_orders(orders) -for result in batch_results: - if result.success: - print(f"订单 {result.order_id} 提交成功") - else: - print(f"订单提交失败: {result.error}") -``` - -## 实盘策略运行 - -### 策略实盘化 - -```python -from qka.core.backtest import Strategy -from qka.brokers.live import LiveEngine - -class LiveTradingStrategy(Strategy): - def __init__(self): - super().__init__() - self.name = "实盘交易策略" - self.client = TradingClient() - - def on_init(self): - """策略初始化""" - # 连接交易客户端 - self.client.connect() - - # 订阅实时数据 - self.subscribe_realtime('000001.SZ') - - # 初始化风险控制 - self.setup_risk_management() - - def on_data(self, data): - """实时数据处理""" - for symbol in data.index: - # 获取当前持仓 - position = self.client.get_position(symbol) - current_price = data.loc[symbol, 'last_price'] - - # 策略逻辑 - signal = self.generate_signal(symbol, data) - - if signal == 'BUY' and not position: - self.execute_buy(symbol, current_price) - elif signal == 'SELL' and position: - self.execute_sell(symbol, position.volume, current_price) - - def execute_buy(self, symbol, price): - """执行买入""" - # 计算仓位大小 - volume = self.calculate_position_size(symbol, price) - - # 风险检查 - if self.check_risk_limits(symbol, volume, price): - order = self.client.place_order( - symbol=symbol, - side=OrderSide.BUY, - order_type=OrderType.LIMIT, - volume=volume, - price=price * 1.001 # 稍微高于市价确保成交 - ) - self.log(f"买入订单已提交: {order.order_id}") - else: - self.log(f"风险控制: 跳过买入 {symbol}") - - def execute_sell(self, symbol, volume, price): - """执行卖出""" - order = self.client.place_order( - symbol=symbol, - side=OrderSide.SELL, - order_type=OrderType.LIMIT, - volume=volume, - price=price * 0.999 # 稍微低于市价确保成交 - ) - self.log(f"卖出订单已提交: {order.order_id}") -``` - -### 启动实盘引擎 - -```python -# 创建实盘引擎 -live_engine = LiveEngine() - -# 配置实盘引擎 -live_engine.configure( - strategy=LiveTradingStrategy(), - data_frequency='1min', # 分钟级数据 - order_timeout=30, # 订单超时时间 - max_retry=3 # 最大重试次数 -) - -# 启动实盘交易 -live_engine.start() - -# 策略将在后台运行,处理实时数据和交易 -``` - -## 风险管理 - -### 实盘风险控制 - -```python -class RiskManager: - def __init__(self, client): - self.client = client - self.max_position_ratio = 0.1 # 单股最大仓位10% - self.max_daily_loss = 0.05 # 日最大亏损5% - self.max_drawdown = 0.15 # 最大回撤15% - - def check_position_limit(self, symbol, volume, price): - """检查仓位限制""" - account_info = self.client.get_account_info() - position_value = volume * price - position_ratio = position_value / account_info.total_value - - return position_ratio <= self.max_position_ratio - - def check_daily_loss_limit(self): - """检查日损失限制""" - today_pnl = self.client.get_today_pnl() - account_info = self.client.get_account_info() - loss_ratio = abs(today_pnl) / account_info.total_value - - return loss_ratio <= self.max_daily_loss - - def emergency_stop(self): - """紧急止损""" - # 撤销所有未成交订单 - pending_orders = self.client.get_pending_orders() - for order in pending_orders: - self.client.cancel_order(order.order_id) - - # 平仓所有持仓 - positions = self.client.get_positions() - for position in positions: - self.client.place_order( - symbol=position.symbol, - side=OrderSide.SELL, - order_type=OrderType.MARKET, - volume=position.volume - ) - - self.log("执行紧急止损") -``` - -### 实时监控 - -```python -from qka.brokers.monitor import TradingMonitor - -monitor = TradingMonitor(client) - -# 设置监控规则 -monitor.add_rule( - name='日亏损监控', - condition=lambda: monitor.get_daily_pnl_ratio() < -0.05, - action=monitor.send_alert -) - -monitor.add_rule( - name='仓位监控', - condition=lambda: monitor.get_max_position_ratio() > 0.15, - action=monitor.reduce_positions -) - -# 启动监控 -monitor.start() -``` - -## 数据同步 - -### 实时行情订阅 - -```python -from qka.brokers.data import RealtimeDataFeed - -# 创建实时数据源 -data_feed = RealtimeDataFeed() - -# 订阅股票行情 -symbols = ['000001.SZ', '000002.SZ', '600000.SH'] -data_feed.subscribe(symbols) - -# 设置数据回调 -def on_tick_data(tick): - print(f"{tick.symbol}: {tick.last_price} ({tick.timestamp})") - -data_feed.set_tick_callback(on_tick_data) - -# 启动数据接收 -data_feed.start() -``` - -### 数据存储 - -```python -from qka.core.data import DataStorage - -storage = DataStorage() - -# 存储实时数据 -def save_tick_data(tick): - storage.save_tick( - symbol=tick.symbol, - timestamp=tick.timestamp, - price=tick.last_price, - volume=tick.volume, - bid=tick.bid, - ask=tick.ask - ) - -data_feed.set_tick_callback(save_tick_data) -``` - -## 交易记录与分析 - -### 交易日志 - -```python -from qka.utils.logger import get_structured_logger - -trade_logger = get_structured_logger('trading') - -def log_trade(order): - trade_logger.info('trade_executed', { - 'order_id': order.order_id, - 'symbol': order.symbol, - 'side': order.side, - 'volume': order.volume, - 'price': order.executed_price, - 'timestamp': order.executed_time, - 'commission': order.commission - }) -``` - -### 实盘表现分析 - -```python -from qka.core.backtest import LivePerformanceAnalyzer - -analyzer = LivePerformanceAnalyzer(client) - -# 生成日报 -daily_report = analyzer.generate_daily_report() -print(f"今日收益: {daily_report.daily_return:.2%}") -print(f"今日交易次数: {daily_report.trade_count}") - -# 生成周报 -weekly_report = analyzer.generate_weekly_report() -print(f"本周收益: {weekly_report.weekly_return:.2%}") -print(f"本周胜率: {weekly_report.win_rate:.2%}") -``` - -## 模拟交易 - -### 模拟环境 - -```python -from qka.brokers.simulator import SimulatorClient - -# 创建模拟交易客户端 -sim_client = SimulatorClient( - initial_cash=1000000, - commission_rate=0.0003 -) - -# 使用模拟客户端进行测试 -strategy = LiveTradingStrategy() -strategy.client = sim_client - -# 在模拟环境中运行策略 -sim_engine = LiveEngine() -sim_engine.configure( - strategy=strategy, - client=sim_client, - mode='simulation' -) - -sim_engine.start() -``` - -## 异常处理 - -### 连接异常 - -```python -def handle_connection_error(): - """处理连接异常""" - max_retry = 3 - retry_count = 0 - - while retry_count < max_retry: - try: - client.reconnect() - print("重连成功") - break - except Exception as e: - retry_count += 1 - print(f"重连失败 ({retry_count}/{max_retry}): {e}") - time.sleep(5) - - if retry_count >= max_retry: - print("连接失败,停止交易") - live_engine.stop() -``` - -### 订单异常 - -```python -def handle_order_error(order, error): - """处理订单异常""" - if '资金不足' in str(error): - print("资金不足,调整仓位大小") - # 重新计算仓位 - elif '涨跌停' in str(error): - print("股票涨跌停,取消订单") - # 取消相关订单 - else: - print(f"订单异常: {error}") - # 记录错误日志 -``` - -## 最佳实践 - -1. **充分测试**:在模拟环境中充分测试策略 -2. **风险控制**:设置多层风险控制机制 -3. **实时监控**:建立完善的监控和告警系统 -4. **异常处理**:考虑各种异常情况的处理 -5. **数据备份**:定期备份交易数据和日志 -6. **版本控制**:对策略代码进行版本管理 -7. **渐进上线**:从小资金开始,逐步增加投入 - -## 注意事项 - -- **合规要求**:确保符合监管要求 -- **系统稳定性**:确保网络和系统稳定 -- **数据延迟**:考虑数据传输延迟的影响 -- **交易时间**:注意交易时间段的限制 -- **资金安全**:妥善保管账户信息 - -## API 参考 - -交易模块的详细API参考请查看 [API文档](../api/brokers/index.md)。 - -### 主要类和函数 - -- **QMTClient** - 交易客户端接口 -- **Order** - 订单对象 -- **Trade** - 交易记录 -- **Position** - 持仓信息 - -更多详细信息请参考对应的API文档页面。 +# 实盘交易 + +QKA 提供完整的实盘交易功能,支持通过 QMT 接口进行 A 股实盘交易。 + +## 交易架构 + +QKA 的实盘交易采用客户端-服务器架构: + +``` +策略代码 → QMTClient → QMTServer → QMT交易接口 → 券商系统 +``` + +## 准备工作 + +### 1. 安装 QMT + +确保已安装 QMT 并正确配置: +- 下载并安装 QMT +- 获取有效的账户 ID +- 配置交易权限 + +### 2. 启动交易服务器 + +```python +from qka.brokers.server import QMTServer + +# 创建交易服务器 +server = QMTServer( + account_id="YOUR_ACCOUNT_ID", # 你的账户ID + mini_qmt_path="YOUR_QMT_PATH", # QMT安装路径 + host="0.0.0.0", # 服务器地址 + port=8000 # 服务器端口 +) + +# 启动服务器 +server.start() # 会打印token供客户端使用 +``` + +### 3. 使用交易客户端 + +```python +from qka.brokers.client import QMTClient + +# 创建交易客户端 +client = QMTClient( + base_url="http://localhost:8000", # 服务器地址 + token="服务器打印的token" # 访问令牌 +) +``` + +## 交易操作 + +### 查询账户信息 + +```python +# 查询股票资产 +assets = client.api("query_stock_asset") +print(assets) + +# 查询资金信息 +capital = client.api("query_account_status") +print(capital) + +# 查询持仓 +positions = client.api("query_stock_positions") +print(positions) +``` + +### 下单交易 + +```python +from xtquant import xtconstant + +# 买入股票 +result = client.api( + "order_stock", + stock_code='600000.SH', # 股票代码 + order_type=xtconstant.STOCK_BUY, # 买入 + order_volume=1000, # 数量 + price_type=xtconstant.FIX_PRICE, # 限价单 + price=10.5 # 价格 +) + +# 卖出股票 +result = client.api( + "order_stock", + stock_code='600000.SH', + order_type=xtconstant.STOCK_SELL, # 卖出 + order_volume=500, + price_type=xtconstant.FIX_PRICE, + price=11.0 +) +``` + +### 订单管理 + +```python +# 查询委托 +orders = client.api("query_stock_orders") +print(orders) + +# 查询成交 +trades = client.api("query_stock_trades") +print(trades) + +# 撤单 +cancel_result = client.api( + "cancel_order_stock", + order_id="订单ID" +) +``` + +## 策略实盘化 + +### 回测转实盘 + +将回测策略转换为实盘策略: + +```python +class RealTimeStrategy: + def __init__(self, client): + self.client = client + self.positions = {} + + def run(self): + """运行实盘策略""" + while True: + # 获取实时数据 + data = self.get_realtime_data() + + # 执行策略逻辑 + signals = self.generate_signals(data) + + # 执行交易 + self.execute_trades(signals) + + # 等待下一轮 + time.sleep(60) # 每分钟执行一次 + + def get_realtime_data(self): + """获取实时数据""" + # 可以通过其他数据源获取实时数据 + pass + + def generate_signals(self, data): + """生成交易信号""" + # 策略逻辑 + pass + + def execute_trades(self, signals): + """执行交易""" + for symbol, signal in signals.items(): + if signal == 'buy': + self.buy(symbol) + elif signal == 'sell': + self.sell(symbol) + + def buy(self, symbol): + """买入操作""" + # 获取当前价格 + # 计算买入数量 + # 执行买入 + pass + + def sell(self, symbol): + """卖出操作""" + # 执行卖出 + pass +``` + +## 风险控制 + +### 仓位管理 + +```python +class RiskManagedTrader: + def __init__(self, client, max_position_ratio=0.1): + self.client = client + self.max_position_ratio = max_position_ratio + + def calculate_position_size(self, symbol, price): + """计算合理的买入数量""" + # 查询总资产 + assets = self.client.api("query_stock_asset") + total_assets = assets['总资产'] + + # 单股票仓位限制 + max_position_value = total_assets * self.max_position_ratio + max_shares = int(max_position_value / price) + + return max_shares +``` + +### 交易频率控制 + +```python +import time + +class RateLimitedTrader: + def __init__(self, client, max_trades_per_minute=5): + self.client = client + self.max_trades_per_minute = max_trades_per_minute + self.trade_times = [] + + def can_trade(self): + """检查是否可以交易""" + current_time = time.time() + # 移除1分钟前的交易记录 + self.trade_times = [t for t in self.trade_times + if current_time - t < 60] + + return len(self.trade_times) < self.max_trades_per_minute + + def record_trade(self): + """记录交易时间""" + self.trade_times.append(time.time()) +``` + +## 监控和日志 + +### 交易监控 + +```python +class TradingMonitor: + def __init__(self, client): + self.client = client + + def monitor_positions(self): + """监控持仓""" + positions = self.client.api("query_stock_positions") + for position in positions: + print(f"持仓: {position['证券代码']} {position['当前数量']}股") + + def monitor_orders(self): + """监控委托""" + orders = self.client.api("query_stock_orders") + for order in orders: + print(f"委托: {order['证券代码']} {order['委托状态']}") +``` + +### 错误处理 + +```python +try: + result = client.api("order_stock", ...) + if not result.get('success'): + print(f"交易失败: {result.get('detail')}") +except Exception as e: + print(f"API调用失败: {e}") + # 重试逻辑或报警 +``` + +## 最佳实践 + +1. **小资金测试**: 先用小资金测试策略 +2. **风险控制**: 设置严格的仓位和止损限制 +3. **监控报警**: 设置交易异常报警 +4. **日志记录**: 详细记录所有交易操作 +5. **备份策略**: 准备手动干预方案 + +## 注意事项 + +1. **交易时间**: 只在交易时间段内运行策略 +2. **网络稳定性**: 确保网络连接稳定 +3. **系统维护**: 定期检查系统状态 +4. **合规性**: 遵守相关交易规则 + +## 相关链接 + +- [API参考 - 交易模块](../api/brokers.md) +- [快速开始 - 第一个策略](../getting-started/first-strategy.md) +- [用户指南 - 回测分析](backtest.md) \ No newline at end of file diff --git a/examples/basic_test.py b/examples/basic_test.py deleted file mode 100644 index 8de21c4..0000000 --- a/examples/basic_test.py +++ /dev/null @@ -1,135 +0,0 @@ -""" -QKA 基础功能测试 -测试配置管理、事件系统和日志系统是否正常工作 -""" - -def test_config_system(): - """测试配置管理系统""" - print("=" * 30) - print("测试配置管理系统") - print("=" * 30) - - try: - from qka.core.config import config, create_sample_config - - # 创建示例配置 - create_sample_config("test_config.json") - print("✅ 配置文件创建成功") - - # 测试配置访问 - print(f"初始资金: {config.backtest.initial_cash:,}") - print(f"数据源: {config.data.default_source}") - print("✅ 配置系统正常") - - except Exception as e: - print(f"❌ 配置系统错误: {e}") - import traceback - traceback.print_exc() - - -def test_event_system(): - """测试事件系统""" - print("\n" + "=" * 30) - print("测试事件系统") - print("=" * 30) - - try: - from qka.core.events import EventType, event_handler, emit_event, start_event_engine, stop_event_engine - import time - - # 定义事件处理器 - @event_handler(EventType.DATA_LOADED) - def on_data_loaded(event): - print(f"📊 收到数据加载事件: {event.data}") - - # 启动事件引擎 - start_event_engine() - print("✅ 事件引擎启动成功") - - # 发送测试事件 - emit_event(EventType.DATA_LOADED, {"test": "data"}) - time.sleep(0.5) # 等待事件处理 - - # 停止事件引擎 - stop_event_engine() - print("✅ 事件系统正常") - - except Exception as e: - print(f"❌ 事件系统错误: {e}") - import traceback - traceback.print_exc() - - -def test_logging_system(): - """测试日志系统""" - print("\n" + "=" * 30) - print("测试日志系统") - print("=" * 30) - - try: - from qka.utils.logger import create_logger - - # 创建日志记录器 - logger = create_logger('test', colored_console=True) - - logger.info("日志系统测试 - 信息") - logger.warning("日志系统测试 - 警告") - logger.error("日志系统测试 - 错误") - - print("✅ 日志系统正常") - - except Exception as e: - print(f"❌ 日志系统错误: {e}") - import traceback - traceback.print_exc() - - -def test_tools(): - """测试工具类""" - print("\n" + "=" * 30) - print("测试工具类") - print("=" * 30) - - try: - from qka.utils.tools import Cache, Timer, format_number - import time - - # 测试缓存 - cache = Cache(max_size=10, ttl=60) - cache.set('test_key', 'test_value') - value = cache.get('test_key') - print(f"缓存测试: {value}") - - # 测试计时器 - with Timer() as t: - time.sleep(0.01) - print(f"计时器测试: {t.elapsed():.4f}秒") - - # 测试格式化 - formatted = format_number(123456.789) - print(f"格式化测试: {formatted}") - - print("✅ 工具类正常") - - except Exception as e: - print(f"❌ 工具类错误: {e}") - import traceback - traceback.print_exc() - - -def main(): - """主测试函数""" - print("🧪 QKA 基础功能测试开始\n") - - test_config_system() - test_event_system() - test_logging_system() - test_tools() - - print("\n" + "=" * 30) - print("🎉 测试完成") - print("=" * 30) - - -if __name__ == "__main__": - main() diff --git a/examples/enhanced_features_demo.py b/examples/enhanced_features_demo.py deleted file mode 100644 index 9556c29..0000000 --- a/examples/enhanced_features_demo.py +++ /dev/null @@ -1,261 +0,0 @@ -""" -QKA 增强功能演示 -展示配置管理、事件系统、日志系统等新功能 -""" - -import time -import qka -from qka.core.config import config, load_config, create_sample_config -from qka.core.events import EventType, event_handler, emit_event, start_event_engine, stop_event_engine -from qka.core.backtest import Strategy -from qka.utils.logger import create_logger, get_structured_logger -from qka.utils.tools import timer, retry - - -def demo_config_system(): - """演示配置管理系统""" - print("=" * 50) - print("🔧 配置管理系统演示") - print("=" * 50) - - # 创建示例配置文件 - create_sample_config("demo_config.json") - - # 加载配置 - cfg = load_config("demo_config.json") - - print(f"初始资金: {qka.config.backtest.initial_cash:,}") - print(f"手续费率: {qka.config.backtest.commission_rate}") - print(f"数据源: {qka.config.data.default_source}") - print(f"缓存目录: {qka.config.data.cache_dir}") - print(f"服务器端口: {qka.config.trading.server_port}") - - # 修改配置 - qka.config.backtest.initial_cash = 2_000_000 - print(f"修改后初始资金: {qka.config.backtest.initial_cash:,}") - - -def demo_event_system(): - """演示事件驱动系统""" - print("\n" + "=" * 50) - print("📡 事件驱动系统演示") - print("=" * 50) - - # 定义事件处理器 - @event_handler(EventType.DATA_LOADED) - def on_data_loaded(event): - print(f"📊 数据加载完成: {event.data}") - - @event_handler(EventType.ORDER_FILLED) - def on_order_filled(event): - print(f"✅ 订单成交: {event.data}") - - @event_handler(EventType.SIGNAL_GENERATED) - def on_signal_generated(event): - print(f"🎯 信号生成: {event.data}") - - # 启动事件引擎 - start_event_engine() - - # 发送测试事件 - emit_event(EventType.DATA_LOADED, {"symbol": "000001.SZ", "rows": 1000}) - emit_event(EventType.ORDER_FILLED, {"order_id": "123", "symbol": "000001.SZ", "price": 10.5}) - emit_event(EventType.SIGNAL_GENERATED, {"symbol": "000002.SZ", "signal": "BUY", "strength": 0.8}) - - # 等待事件处理 - time.sleep(0.5) - - # 查看事件统计 - stats = qka.event_engine.get_statistics() - print(f"\n📈 事件统计:") - for event_type, count in stats['event_count'].items(): - print(f" {event_type}: {count}次") - - -def demo_logging_system(): - """演示增强日志系统""" - print("\n" + "=" * 50) - print("📝 增强日志系统演示") - print("=" * 50) - - # 创建彩色日志记录器 - logger = create_logger('demo', colored_console=True, level='DEBUG') - - logger.debug("这是调试信息") - logger.info("这是普通信息") - logger.warning("这是警告信息") - logger.error("这是错误信息") - - # 结构化日志 - struct_logger = get_structured_logger('demo_struct') - struct_logger.info("用户登录", user_id=12345, ip="192.168.1.100", action="login") - struct_logger.error("交易失败", symbol="000001.SZ", reason="余额不足", amount=10000) - - print("📁 日志文件已保存到 logs 目录") - - -class EnhancedStrategy(Strategy): - """增强的策略类,集成事件系统""" - - def __init__(self): - super().__init__() - self.logger = create_logger('strategy', colored_console=True) - - def on_start(self, broker): - """策略启动时的事件处理""" - self.logger.info(f"策略 {self.name} 启动") - emit_event(EventType.STRATEGY_START, { - "strategy": self.name, - "initial_cash": broker.initial_cash - }) - - def on_bar(self, data, broker, current_date): - """策略核心逻辑""" - for symbol, df in data.items(): - if len(df) < 20: - continue - - current_price = df['close'].iloc[-1] - ma20 = df['close'].rolling(20).mean().iloc[-1] - - # 生成交易信号 - if current_price > ma20 and broker.get_position(symbol) == 0: - # 发送买入信号事件 - emit_event(EventType.SIGNAL_GENERATED, { - "symbol": symbol, - "signal": "BUY", - "price": current_price, - "reason": "价格突破20日均线" - }) - - # 执行买入 - if broker.buy(symbol, 0.3, current_price): - self.logger.info(f"买入 {symbol} @ {current_price:.2f}") - emit_event(EventType.ORDER_FILLED, { - "symbol": symbol, - "action": "BUY", - "price": current_price, - "amount": broker.get_position(symbol) - }) - - elif current_price < ma20 and broker.get_position(symbol) > 0: - # 发送卖出信号事件 - emit_event(EventType.SIGNAL_GENERATED, { - "symbol": symbol, - "signal": "SELL", - "price": current_price, - "reason": "价格跌破20日均线" - }) - - # 执行卖出 - if broker.sell(symbol, 1.0, current_price): - self.logger.info(f"卖出 {symbol} @ {current_price:.2f}") - emit_event(EventType.ORDER_FILLED, { - "symbol": symbol, - "action": "SELL", - "price": current_price, - "amount": 0 - }) - - def on_end(self, broker): - """策略结束时的事件处理""" - self.logger.info(f"策略 {self.name} 结束") - emit_event(EventType.STRATEGY_END, { - "strategy": self.name, - "final_value": broker.get_total_value({}) - }) - - -@timer -def demo_enhanced_backtest(): - """演示增强回测功能""" - print("\n" + "=" * 50) - print("🚀 增强回测演示") - print("=" * 50) - - # 使用配置系统的参数 - print(f"使用配置: 初始资金 {qka.config.backtest.initial_cash:,}, 手续费 {qka.config.backtest.commission_rate}") - - # 获取数据 - data_obj = qka.data(qka.config.data.default_source, stocks=['000001', '000002']) - - # 运行回测 - result = qka.backtest( - data=data_obj, - strategy=EnhancedStrategy(), - start_time='2023-06-01', - end_time='2023-08-31' - ) - - # 输出结果 - print(f"\n📊 回测结果:") - print(f"总收益率: {result['total_return']:.2%}") - print(f"年化收益率: {result['annual_return']:.2%}") - print(f"最大回撤: {result['max_drawdown']:.2%}") - print(f"夏普比率: {result['sharpe_ratio']:.2f}") - print(f"交易次数: {result['total_trades']}") - - -@retry(max_attempts=3, delay=1.0) -def demo_tools(): - """演示工具函数""" - print("\n" + "=" * 50) - print("🛠️ 工具函数演示") - print("=" * 50) - - from qka.utils.tools import format_number, format_percentage, format_currency - from qka.utils.tools import Timer, Cache - - # 格式化工具 - print(f"数字格式化: {format_number(1234567.89)}") - print(f"百分比格式化: {format_percentage(0.1567)}") - print(f"货币格式化: {format_currency(123456.78)}") - - # 计时器 - with Timer() as t: - time.sleep(0.1) - print(f"计时器测试: {t.elapsed():.3f}秒") - - # 缓存 - cache = Cache(max_size=100, ttl=60) - cache.set('test_key', 'test_value') - print(f"缓存测试: {cache.get('test_key')}") - - print("✅ 重试装饰器测试成功") - - -def main(): - """主演示函数""" - print("🎉 QKA 增强功能演示") - - try: - # 演示各个功能模块 - demo_config_system() - demo_event_system() - demo_logging_system() - demo_tools() - demo_enhanced_backtest() - - print("\n" + "=" * 50) - print("✨ 所有功能演示完成") - print("=" * 50) - - # 查看最终事件统计 - final_stats = qka.event_engine.get_statistics() - print(f"\n📊 最终事件统计:") - for event_type, count in final_stats['event_count'].items(): - print(f" {event_type}: {count}次") - - except Exception as e: - print(f"❌ 演示过程中出现错误: {e}") - import traceback - traceback.print_exc() - - finally: - # 停止事件引擎 - stop_event_engine() - print("\n🔚 事件引擎已关闭") - - -if __name__ == "__main__": - main() diff --git a/examples/simple_backtest_demo.py b/examples/simple_backtest_demo.py deleted file mode 100644 index 0080e23..0000000 --- a/examples/simple_backtest_demo.py +++ /dev/null @@ -1,44 +0,0 @@ -""" -QKA 简化回测API演示 - 极简版本 -只需3步:获取数据 -> 定义策略 -> 运行回测 -""" - -import qka - -# 第1步:定义策略 -class SimpleStrategy(qka.Strategy): - def on_bar(self, data, broker, current_date): - for symbol, df in data.items(): - if len(df) < 20: # 需要足够的历史数据 - continue - - current_price = df['close'].iloc[-1] - ma20 = df['close'].rolling(20).mean().iloc[-1] - - # 简单策略:价格突破20日均线买入,跌破卖出 - if current_price > ma20 and broker.get_position(symbol) == 0: - broker.buy(symbol, 0.5, current_price) # 用50%资金买入 - - elif current_price < ma20 and broker.get_position(symbol) > 0: - broker.sell(symbol, 1.0, current_price) # 全部卖出 - -# 第2步:获取数据并运行回测 -if __name__ == "__main__": - # 获取数据 - data_obj = qka.data(stocks=['000001', '000002']) # 使用默认数据源 - - # 运行回测 - result = qka.backtest( - data=data_obj, - strategy=SimpleStrategy(), - start_time='2023-01-01', - end_time='2023-12-31' - ) - - qka.plot(result) - - # 第3步:查看结果 - print(f"总收益率: {result['total_return']:.2%}") - print(f"年化收益率: {result['annual_return']:.2%}") - print(f"最大回撤: {result['max_drawdown']:.2%}") - print(f"夏普比率: {result['sharpe_ratio']:.2f}") diff --git a/examples/simple_demo.py b/examples/simple_demo.py deleted file mode 100644 index 42b727b..0000000 --- a/examples/simple_demo.py +++ /dev/null @@ -1,111 +0,0 @@ -""" -QKA 简化功能演示 -演示阶段1的核心增强功能 -""" - -print("🎉 QKA 增强功能演示") -print("=" * 50) - -# 1. 配置管理系统演示 -print("\n📋 配置管理系统") -print("-" * 30) - -from qka.core.config import config, create_sample_config - -# 创建示例配置 -create_sample_config("demo_config.json") -print("✅ 配置文件创建成功") - -# 显示当前配置 -print(f"初始资金: {config.backtest.initial_cash:,}") -print(f"手续费率: {config.backtest.commission_rate}") -print(f"数据源: {config.data.default_source}") -print(f"服务器端口: {config.trading.server_port}") - -# 2. 事件系统演示 -print("\n📡 事件驱动系统") -print("-" * 30) - -from qka.core.events import EventType, event_handler, emit_event, start_event_engine, stop_event_engine -import time - -# 定义事件处理器 -@event_handler(EventType.DATA_LOADED) -def on_data_loaded(event): - print(f"📊 数据加载事件: {event.data}") - -@event_handler(EventType.SIGNAL_GENERATED) -def on_signal_generated(event): - print(f"🎯 信号生成事件: {event.data}") - -# 启动事件引擎 -start_event_engine() -print("✅ 事件引擎启动成功") - -# 发送测试事件 -emit_event(EventType.DATA_LOADED, {"symbol": "000001.SZ", "rows": 1000}) -emit_event(EventType.SIGNAL_GENERATED, {"symbol": "000002.SZ", "signal": "BUY"}) - -# 等待事件处理 -time.sleep(1) - -# 3. 日志系统演示 -print("\n📝 日志系统") -print("-" * 30) - -from qka.utils.logger import create_logger - -logger = create_logger('demo', colored_console=True) -logger.info("这是一条信息日志") -logger.warning("这是一条警告日志") -logger.error("这是一条错误日志") - -print("✅ 彩色日志显示正常") - -# 4. 工具类演示 -print("\n🛠️ 工具类") -print("-" * 30) - -from qka.utils.tools import Cache, Timer, format_number, format_percentage - -# 缓存测试 -cache = Cache(max_size=10, ttl=60) -cache.set('test', 'value') -print(f"缓存测试: {cache.get('test')}") - -# 计时器测试 -with Timer() as t: - time.sleep(0.01) -print(f"计时器测试: {t.elapsed():.4f}秒") - -# 格式化测试 -print(f"数字格式化: {format_number(123456.789)}") -print(f"百分比格式化: {format_percentage(0.1234)}") - -# 获取统计信息 -from qka.core.events import event_engine -stats = event_engine.get_statistics() -print(f"\n📊 事件统计:") -for event_type, count in stats['event_count'].items(): - print(f" {event_type}: {count}次") - -# 停止事件引擎 -stop_event_engine() - -print("\n" + "=" * 50) -print("✨ 演示完成!阶段1功能正常运行") -print("=" * 50) - -print(""" -🎯 阶段1完成的功能: - ✅ 配置管理系统 - 统一的配置管理 - ✅ 事件驱动框架 - 发布-订阅模式 - ✅ 增强日志系统 - 彩色和结构化日志 - ✅ 基础工具类 - 缓存、计时器、格式化等 - -🔄 下一步: 阶段2 - 数据层增强 - 📊 数据缓存机制 - 🔍 数据质量检查 - 📈 多频率数据支持 - 🔄 增量数据更新 -""") diff --git a/mkdocs.yml b/mkdocs.yml index e1792c7..0988ca1 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -126,47 +126,10 @@ nav: - 数据获取: user-guide/data.md - 回测分析: user-guide/backtest.md - 实盘交易: user-guide/trading.md - - 配置管理: user-guide/config.md - - 事件系统: user-guide/events.md - - 日志系统: user-guide/logging.md - - 参考资料: - - A股市场证券代码命名规则: A股市场证券代码命名规则.md - API参考: - - API概览: api/index.md - - Core模块: - - 模块概览: api/core/index.md - - 配置管理: api/core/config.md - - 事件系统: api/core/events.md - - 回测引擎: api/core/backtest.md - - 数据处理: api/core/data.md - - 绘图工具: api/core/plot.md - - Utils模块: - - 模块概览: api/utils/index.md - - 日志系统: api/utils/logger.md - - 通用工具: api/utils/tools.md - - 动画工具: api/utils/anis.md - - 通用函数: api/utils/util.md - - Brokers模块: - - 模块概览: api/brokers/index.md - - 交易客户端: api/brokers/client.md - - 交易服务器: api/brokers/server.md - - 交易执行: api/brokers/trade.md - - MCP模块: - - 模块概览: api/mcp/index.md - - API接口: api/mcp/api.md - - 服务器实现: api/mcp/server.md + - 核心模块: api/core.md + - 交易模块: api/brokers.md + - 工具模块: api/utils.md - 示例教程: - - 示例概览: examples/index.md - - 基础示例: - - 第一个策略: examples/basic/first-strategy.md - - 数据获取: examples/basic/data-fetching.md - - 简单回测: examples/basic/simple-backtest.md - - 进阶示例: - - 事件驱动策略: examples/advanced/event-driven.md - - 多资产策略: examples/advanced/multi-asset.md - - 风险管理: examples/advanced/risk-management.md - - 完整案例: - - 动量策略: examples/complete/momentum-strategy.md - - 均值回归策略: examples/complete/mean-reversion.md - - 配对交易: examples/complete/pairs-trading.md + - 基础示例: examples/basic.md - 更新日志: changelog.md diff --git a/pyproject.toml b/pyproject.toml index 6b31665..fe41db5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,10 @@ dependencies = [ "uvicorn>=0.34.3", "xtquant>=241014.1.2", "ipykernel>=6.29.5", + "pyarrow>=21.0.0", + "tqdm>=4.67.1", + "dask[complete]>=2025.7.0", + "ta>=0.11.0", ] [project.optional-dependencies] diff --git a/qka/__init__.py b/qka/__init__.py index 8fd61da..d186d5e 100644 --- a/qka/__init__.py +++ b/qka/__init__.py @@ -12,11 +12,9 @@ __version__ = "0.1.0" # fallback version # 核心功能直接导入 -from qka.core.data import data, set_source, get_source, register_data_source, get_available_sources -from qka.core.backtest import backtest, Strategy, Broker -from qka.core.config import config, load_config -from qka.core.events import event_engine, emit_event -from qka.core.plot import plot +from qka.core.data import Data +from qka.core.backtest import Backtest +from qka.core.strategy import Strategy # 子模块导入 from qka import core, utils, mcp @@ -28,13 +26,7 @@ __all__ = [ # 核心功能 - 'data', 'backtest', 'Strategy', 'Broker', 'plot', - # 配置 - 'config', 'load_config', - # 数据源管理 - 'set_source', 'get_source', 'register_data_source', 'get_available_sources', - # 事件系统 - 'event_engine', 'emit_event', + 'Data', 'Backtest', 'Strategy', # 子模块 'core', 'utils', 'mcp' ] \ No newline at end of file diff --git a/qka/brokers/client.py b/qka/brokers/client.py index c9ad480..bca9bbf 100644 --- a/qka/brokers/client.py +++ b/qka/brokers/client.py @@ -1,13 +1,36 @@ +""" +QKA交易客户端模块 + +提供QMT交易服务器的客户端接口,支持远程调用交易功能。 +""" + import requests -from typing import Any, Dict +from typing import Any, Dict, Optional from qka.utils.logger import logger class QMTClient: - def __init__(self, base_url: str = "http://localhost:8000", token: str = None): - """初始化交易客户端 + """ + QMT交易客户端类 + + 提供与QMT交易服务器的通信接口,支持各种交易操作。 + + Attributes: + base_url (str): API服务器地址 + session (requests.Session): HTTP会话对象 + token (str): 访问令牌 + headers (Dict): HTTP请求头 + """ + + def __init__(self, base_url: str = "http://localhost:8000", token: Optional[str] = None): + """ + 初始化交易客户端 + Args: - base_url: API服务器地址,默认为本地8000端口 - token: 访问令牌,必须与服务器的token一致 + base_url (str): API服务器地址,默认为本地8000端口 + token (str): 访问令牌,必须与服务器的token一致 + + Raises: + ValueError: 如果未提供token """ self.base_url = base_url.rstrip('/') self.session = requests.Session() @@ -17,12 +40,18 @@ def __init__(self, base_url: str = "http://localhost:8000", token: str = None): self.headers = {"X-Token": self.token} def api(self, method_name: str, **params) -> Any: - """通用调用接口方法 + """ + 通用调用接口方法 + Args: - method_name: 要调用的接口名称 + method_name (str): 要调用的接口名称 **params: 接口参数,作为关键字参数传入 + Returns: - 接口返回的数据 + Any: 接口返回的数据 + + Raises: + Exception: API调用失败时抛出异常 """ try: response = self.session.post( diff --git a/qka/brokers/server.py b/qka/brokers/server.py index 242cf11..0377034 100644 --- a/qka/brokers/server.py +++ b/qka/brokers/server.py @@ -1,21 +1,45 @@ +""" +QKA交易服务器模块 + +提供基于FastAPI的QMT交易服务器,将QMT交易接口封装为RESTful API。 +""" + from fastapi import FastAPI, HTTPException, Header, Depends from pydantic import BaseModel import inspect -from typing import Any, Dict +from typing import Any, Dict, Optional from qka.brokers.trade import create_trader import uvicorn import uuid import hashlib class QMTServer: - def __init__(self, account_id: str, mini_qmt_path: str, host: str = "0.0.0.0", port: int = 8000, token: str = None): - """初始化交易服务器 + """ + QMT交易服务器类 + + 将QMT交易接口封装为RESTful API,支持远程调用交易功能。 + + Attributes: + account_id (str): 账户ID + mini_qmt_path (str): miniQMT安装路径 + host (str): 服务器地址 + port (int): 服务器端口 + app (FastAPI): FastAPI应用实例 + trader: QMT交易对象 + account: 账户对象 + token (str): 访问令牌 + """ + + def __init__(self, account_id: str, mini_qmt_path: str, host: str = "0.0.0.0", port: int = 8000, token: Optional[str] = None): + """ + 初始化交易服务器 + Args: - account_id: 账户ID - mini_qmt_path: miniQMT路径 - host: 服务器地址,默认0.0.0.0 - port: 服务器端口,默认8000 - token: 可选的自定义token + account_id (str): 账户ID + mini_qmt_path (str): miniQMT路径 + host (str): 服务器地址,默认0.0.0.0 + port (int): 服务器端口,默认8000 + token (Optional[str]): 可选的自定义token """ self.account_id = account_id self.mini_qmt_path = mini_qmt_path @@ -28,7 +52,12 @@ def __init__(self, account_id: str, mini_qmt_path: str, host: str = "0.0.0.0", p print(f"\n授权Token: {self.token}\n") # 打印token供客户端使用 def generate_token(self) -> str: - """生成基于机器码的固定token""" + """ + 生成基于机器码的固定token + + Returns: + str: 生成的token字符串 + """ # 获取机器码(例如MAC地址) mac = uuid.getnode() # 将机器码转换为字符串 @@ -38,7 +67,18 @@ def generate_token(self) -> str: return token async def verify_token(self, x_token: str = Header(...)): - """验证token的依赖函数""" + """ + 验证token的依赖函数 + + Args: + x_token (str): 请求头中的token + + Returns: + str: 验证通过的token + + Raises: + HTTPException: token无效时抛出401错误 + """ if x_token != self.token: raise HTTPException(status_code=401, detail="无效的Token") return x_token @@ -47,8 +87,16 @@ def init_trader(self): """初始化交易对象""" self.trader, self.account = create_trader(self.account_id, self.mini_qmt_path) - def convert_to_dict(self, obj): - """将结果转换为可序列化的字典""" + def convert_to_dict(self, obj) -> Dict[str, Any]: + """ + 将结果转换为可序列化的字典 + + Args: + obj: 任意对象 + + Returns: + Dict[str, Any]: 可序列化的字典 + """ # 如果是基本类型,直接返回 if isinstance(obj, (int, float, str, bool)): return obj @@ -62,15 +110,21 @@ def convert_to_dict(self, obj): elif hasattr(obj, '__dir__'): attrs = obj.__dir__() # 过滤掉内部属性和方法 - public_attrs = {attr: getattr(obj, attr) - for attr in attrs + public_attrs = {attr: getattr(obj, attr) + for attr in attrs if not attr.startswith('_') and not callable(getattr(obj, attr))} return public_attrs # 其他类型直接返回 return str(obj) # 将无法处理的类型转换为字符串 def convert_method_to_endpoint(self, method_name: str, method): - """将 XtQuantTrader 方法转换为 FastAPI 端点""" + """ + 将 XtQuantTrader 方法转换为 FastAPI 端点 + + Args: + method_name (str): 方法名称 + method: 方法对象 + """ sig = inspect.signature(method) param_names = list(sig.parameters.keys()) @@ -103,7 +157,7 @@ async def endpoint(request: RequestModel, token: str = Depends(self.verify_token def setup_routes(self): """设置所有路由""" trader_methods = inspect.getmembers( - self.trader.__class__, + self.trader.__class__, predicate=lambda x: inspect.isfunction(x) or inspect.ismethod(x) ) @@ -120,7 +174,16 @@ def start(self): self.setup_routes() uvicorn.run(self.app, host=self.host, port=self.port) -def qmt_server(account_id: str, mini_qmt_path: str, host: str = "0.0.0.0", port: int = 8000, token: str = None): - """快速创建并启动服务器的便捷函数""" +def qmt_server(account_id: str, mini_qmt_path: str, host: str = "0.0.0.0", port: int = 8000, token: Optional[str] = None): + """ + 快速创建并启动服务器的便捷函数 + + Args: + account_id (str): 账户ID + mini_qmt_path (str): miniQMT路径 + host (str): 服务器地址,默认0.0.0.0 + port (int): 服务器端口,默认8000 + token (Optional[str]): 可选的自定义token + """ server = QMTServer(account_id, mini_qmt_path, host, port, token) server.start() \ No newline at end of file diff --git a/qka/core/__init__.py b/qka/core/__init__.py index 9027379..05a3676 100644 --- a/qka/core/__init__.py +++ b/qka/core/__init__.py @@ -3,15 +3,17 @@ 包含核心功能:数据处理、回测引擎、配置管理、事件系统等 """ -from .data import data -from .backtest import backtest, Strategy -from .config import config, load_config -from .events import EventType, event_engine, emit_event, start_event_engine, stop_event_engine -from .plot import plot + +# 数据相关 +from .data import Data +# 回测相关 +from .backtest import Backtest +from .strategy import Strategy +from .broker import Broker __all__ = [ - 'data', 'backtest', 'Strategy', - 'config', 'load_config', - 'EventType', 'event_engine', 'emit_event', 'start_event_engine', 'stop_event_engine', - 'plot' + # 数据相关 + 'Data', + # 回测相关 + 'Backtest', 'Strategy', 'Broker', ] diff --git a/qka/core/backtest.py b/qka/core/backtest.py index e3c8cb6..3948722 100644 --- a/qka/core/backtest.py +++ b/qka/core/backtest.py @@ -1,329 +1,117 @@ +""" +QKA回测引擎模块 -import pandas as pd -import numpy as np -from datetime import datetime, timedelta -from typing import Dict, List, Optional, Any -from abc import ABC, abstractmethod -import warnings -warnings.filterwarnings('ignore') +提供基于时间序列的回测功能,支持多股票横截面数据处理,包含完整的交易记录和可视化功能。 +""" -from qka.core.data.base import Data +import plotly.graph_objects as go -class Strategy(ABC): - """策略基类""" +class Backtest: + """ + QKA回测引擎类 + + 提供基于时间序列的回测功能,支持多股票横截面数据处理。 - def __init__(self): - self.name = self.__class__.__name__ + Attributes: + data (Data): 数据对象实例 + strategy (Strategy): 策略对象实例 + """ - @abstractmethod - def on_bar(self, data: Dict[str, pd.DataFrame], broker, current_date: datetime): + def __init__(self, data, strategy): """ - 每个交易日调用的策略逻辑 + 初始化回测引擎 Args: - data: 历史数据 {股票代码: DataFrame} - broker: 交易接口 - current_date: 当前日期 + data (Data): Data类的实例,包含股票数据 + strategy (Strategy): 策略对象,必须包含on_bar方法 """ - pass - - def on_start(self, broker): - """回测开始时调用""" - pass + self.data = data + self.strategy = strategy - def on_end(self, broker): - """回测结束时调用""" - pass - -class Broker: - """简化的交易接口""" - - def __init__(self, initial_cash: float = 1000000, commission_rate: float = 0.0003): - self.initial_cash = initial_cash - self.cash = initial_cash - self.commission_rate = commission_rate - self.positions = {} # {股票代码: 数量} - self.avg_costs = {} # {股票代码: 平均成本} - self.trades = [] # 交易记录 - self.daily_values = [] # 每日净值 - - def buy(self, symbol: str, amount: float, price: Optional[float] = None) -> bool: + def run(self): """ - 买入股票 + 执行回测 - Args: - symbol: 股票代码 - amount: 金额或数量,如果 < 1 则视为金额比例,否则视为股数 - price: 价格,为None时使用当前价格 + 遍历所有时间点,在每个时间点调用策略的on_bar方法进行交易决策, + 并记录交易后的状态。 """ - if amount <= 0: - return False - - if amount < 1: # 按比例买入 - buy_amount = self.cash * amount - shares = int(buy_amount / price / 100) * 100 # 按手买入 - else: # 按股数买入 - shares = int(amount / 100) * 100 # 按手买入 - buy_amount = shares * price - - commission = buy_amount * self.commission_rate - total_cost = buy_amount + commission - - if self.cash >= total_cost and shares >= 100: - # 更新持仓 - old_shares = self.positions.get(symbol, 0) - old_cost = self.avg_costs.get(symbol, 0.0) - - new_shares = old_shares + shares - new_avg_cost = ((old_shares * old_cost) + buy_amount) / new_shares + # 获取所有股票数据(dask DataFrame) + df = self.data.get() + + for date, row in df.iterrows(): + def get(factor): + """ + 获取指定因子的数据 + + Args: + factor (str): 因子名称,如 'close', 'volume' 等 + + Returns: + pd.Series: 该因子在所有股票上的值 + """ + s = row[row.index.str.endswith(factor)] + s.index = s.index.str.replace(f'_{factor}$', '', regex=True) + return s - self.positions[symbol] = new_shares - self.avg_costs[symbol] = new_avg_cost - self.cash -= total_cost - # 记录交易 - self.trades.append({ - 'date': getattr(self, 'current_date', datetime.now()), - 'symbol': symbol, - 'action': 'buy', - 'shares': shares, - 'price': price, - 'amount': buy_amount, - 'commission': commission - }) + # 先调用策略的on_bar(可能包含交易操作) + self.strategy.on_bar(date, get) - return True - return False + # 再调用broker的on_bar记录状态(包含交易后的状态) + self.strategy.broker.on_bar(date, get) - def sell(self, symbol: str, amount: float = 1.0, price: Optional[float] = None) -> bool: + def plot(self, title="回测收益曲线"): """ - 卖出股票 + 绘制回测收益曲线图 Args: - symbol: 股票代码 - amount: 比例或数量,如果 <= 1 则视为比例,否则视为股数 - price: 价格,为None时使用当前价格 - """ - current_shares = self.positions.get(symbol, 0) - if current_shares == 0: - return False - - if amount <= 1: # 按比例卖出 - sell_shares = int(current_shares * amount) - else: # 按股数卖出 - sell_shares = min(int(amount / 100) * 100, current_shares) + title (str): 图表标题,默认为"回测收益曲线" - if sell_shares <= 0: - return False - - sell_amount = sell_shares * price - commission = sell_amount * self.commission_rate - net_proceeds = sell_amount - commission + Returns: + plotly.graph_objects.Figure: 交互式图表对象,如果无数据则返回None + """ - # 更新持仓 - self.positions[symbol] = current_shares - sell_shares - if self.positions[symbol] == 0: - del self.positions[symbol] - if symbol in self.avg_costs: - del self.avg_costs[symbol] - - self.cash += net_proceeds - # 记录交易 - self.trades.append({ - 'date': getattr(self, 'current_date', datetime.now()), - 'symbol': symbol, - 'action': 'sell', - 'shares': sell_shares, - 'price': price, - 'amount': sell_amount, - 'commission': commission - }) + # 获取交易数据 + trades_df = self.strategy.broker.trades - return True - - def get_position(self, symbol: str) -> int: - """获取持仓数量""" - return self.positions.get(symbol, 0) - - def get_cash(self) -> float: - """获取可用现金""" - return self.cash - - def get_total_value(self, prices: Dict[str, float]) -> float: - """计算总资产""" - stock_value = sum(shares * prices.get(symbol, 0) - for symbol, shares in self.positions.items()) - return self.cash + stock_value - - def get_positions(self) -> Dict[str, int]: - """获取所有持仓""" - return self.positions.copy() - -def backtest(data: Data, strategy: Strategy, broker: Optional[Broker] = None, - start_time: str = '', end_time: str = '') -> Dict[str, Any]: - """ - 简化的回测函数 - - Args: - data: 数据对象 - strategy: 策略对象 - broker: 交易接口,为None时使用默认设置 - start_time: 开始时间 'YYYY-MM-DD' - end_time: 结束时间 'YYYY-MM-DD' - - Returns: - 回测结果字典 - """ - # 获取历史数据 - historical_data = data.get(period='1d', start_time=start_time, end_time=end_time) - - if not historical_data: - raise ValueError("未获取到有效数据") - - # 初始化broker - if broker is None: - broker = Broker() - - # 获取所有交易日期 - all_dates = set() - for symbol, df in historical_data.items(): - if isinstance(df, pd.DataFrame) and not df.empty: - dates = df.index - if start_time: - start_dt = pd.to_datetime(start_time) - dates = dates[dates >= start_dt] - if end_time: - end_dt = pd.to_datetime(end_time) - dates = dates[dates <= end_dt] - all_dates.update(dates) - - trading_days = sorted(list(all_dates)) - - if not trading_days: - raise ValueError("没有找到有效的交易日期") - - # 策略初始化 - strategy.on_start(broker) - - # 逐日回测 - for current_date in trading_days: - broker.current_date = current_date + # 检查是否有数据 + if trades_df.empty or 'total' not in trades_df.columns: + print("错误:没有可用的回测数据或缺少total列") + return None - # 准备当日数据(截止到当前日期的历史数据) - current_data = {} - current_prices = {} + # 提取总资产数据 + total_assets = trades_df['total'] - for symbol, df in historical_data.items(): - if current_date in df.index: - # 获取截止到当前日期的所有历史数据 - historical_slice = df.loc[:current_date].copy() - current_data[symbol] = historical_slice - current_prices[symbol] = df.loc[current_date, 'close'] + # 创建交互式图表 + fig = go.Figure() - # 执行策略 - if current_data: - try: - strategy.on_bar(current_data, broker, current_date) - except Exception as e: - print(f"策略执行错误 {current_date}: {e}") - continue + fig.add_trace(go.Scatter( + x=total_assets.index, + y=total_assets.values, + mode='lines', + name='总资产', + line=dict(color='#2E86AB', width=3), + hovertemplate='日期: %{x}
总资产: ¥%{y:,.2f}' + )) - # 记录当日资产状况 - total_value = broker.get_total_value(current_prices) - broker.daily_values.append({ - 'date': current_date, - 'cash': broker.cash, - 'total_value': total_value, - 'positions': broker.get_positions().copy(), - 'prices': current_prices.copy() - }) - - # 策略结束 - strategy.on_end(broker) - - # 计算回测结果 - return _calculate_performance(broker) - -def _calculate_performance(broker: Broker) -> Dict[str, Any]: - """计算回测绩效指标""" - - if not broker.daily_values: - return { - 'error': '没有有效的回测数据' - } - - # 提取数据 - dates = [record['date'] for record in broker.daily_values] - values = [record['total_value'] for record in broker.daily_values] - - # 基本统计 - initial_value = broker.initial_cash - final_value = values[-1] if values else initial_value - total_return = (final_value - initial_value) / initial_value - - # 计算年化收益率 - days = len(values) - if days > 0: - annual_return = (final_value / initial_value) ** (252 / days) - 1 - else: - annual_return = 0 - - # 计算波动率和夏普比率 - returns = pd.Series(values).pct_change().dropna() - if len(returns) > 1: - volatility = returns.std() * np.sqrt(252) - sharpe_ratio = annual_return / volatility if volatility > 0 else 0 - else: - volatility = 0 - sharpe_ratio = 0 - - # 计算最大回撤 - peak = pd.Series(values).expanding().max() - drawdown = (pd.Series(values) - peak) / peak - max_drawdown = drawdown.min() - # 交易统计 - trades = broker.trades - total_trades = len(trades) - total_commission = sum(trade['commission'] for trade in trades) - - # 简化的胜率计算 - profitable_trades = 0 - buy_trades = [t for t in trades if t['action'] == 'buy'] - sell_trades = [t for t in trades if t['action'] == 'sell'] - - if total_trades > 0 and sell_trades: - for sell_trade in sell_trades: - symbol = sell_trade['symbol'] - sell_date = sell_trade['date'] - - # 查找最近的买入记录 - recent_buy = None - for buy_trade in reversed(buy_trades): - if (buy_trade['symbol'] == symbol and - buy_trade['date'] <= sell_date): - recent_buy = buy_trade - break - - if recent_buy: - profit = (sell_trade['price'] - recent_buy['price']) * sell_trade['shares'] - profit -= (sell_trade['commission'] + recent_buy['commission']) - if profit > 0: - profitable_trades += 1 - - win_rate = profitable_trades / len(sell_trades) if sell_trades else 0 - - return { - 'initial_capital': initial_value, - 'final_value': final_value, - 'total_return': total_return, - 'annual_return': annual_return, - 'volatility': volatility, - 'sharpe_ratio': sharpe_ratio, - 'max_drawdown': max_drawdown, - 'total_trades': total_trades, - 'total_commission': total_commission, - 'win_rate': win_rate, - 'trading_days': days, - 'daily_values': broker.daily_values, - 'trades': trades, - 'positions': broker.get_positions() - } \ No newline at end of file + # 更新布局 + fig.update_layout( + title=dict( + text=title, + x=0.5, + font=dict(size=16) + ), + xaxis_title="日期", + yaxis_title="总资产 (¥)", + height=600, + showlegend=True, + template='plotly_white', + hovermode='x unified' + ) + + # 添加网格线 + fig.update_xaxes(showgrid=True, gridwidth=1, gridcolor='LightGrey') + + # 显示图表 + fig.show() + + return fig \ No newline at end of file diff --git a/qka/core/broker.py b/qka/core/broker.py new file mode 100644 index 0000000..861ae07 --- /dev/null +++ b/qka/core/broker.py @@ -0,0 +1,215 @@ +""" +QKA经纪商模块 + +提供虚拟交易经纪商功能,管理资金、持仓和交易记录,支持回测环境下的交易操作。 +""" + +import pandas as pd +from typing import Dict, List, Any, Optional + +class Broker: + """ + 虚拟交易经纪商类 + + 管理资金、持仓和交易记录,提供买入卖出操作接口。 + + Attributes: + cash (float): 可用现金 + positions (Dict): 持仓记录,格式: {symbol: {'size': 数量, 'avg_price': 平均成本}} + trade_history (List): 交易历史记录 + timestamp: 当前时间戳 + trades (pd.DataFrame): 交易记录DataFrame + """ + + def __init__(self, initial_cash=100000.0): + """ + 初始化Broker类 + + Args: + initial_cash (float): 初始资金,默认10万元 + """ + self.cash = initial_cash # 可用现金 + self.positions = {} # 持仓记录,格式: {symbol: {'size': 数量, 'avg_price': 平均成本}} + self.trade_history = [] # 交易历史记录 + self.timestamp = None # 当前时间戳 + + # 交易记录 + self.trades = pd.DataFrame(columns=[ + 'cash', 'value', 'total', + 'positions', 'trades' + ]) + + def on_bar(self, date, get): + """ + 处理每个bar的数据,记录状态 + + Args: + date: 当前时间戳 + get: 获取因子数据的函数,格式为 get(factor_name) -> pd.Series + """ + self.timestamp = date + + # 获取当前市场价格 + close_prices = get('close') + market_prices = close_prices.to_dict() if hasattr(close_prices, 'to_dict') else {} + + # 记录状态到trades DataFrame + if self.timestamp is None: + return + + # 获取当日交易记录 + daily_trades = [] + for trade in self.trade_history: + if trade['timestamp'] == self.timestamp: + daily_trades.append(trade) + + # 计算持仓市值 + position_value = 0.0 + for symbol, position in self.positions.items(): + if market_prices and symbol in market_prices: + # 使用市场价格计算市值 + position_value += position['size'] * market_prices[symbol] + else: + # 使用平均成本估算市值 + position_value += position['size'] * position['avg_price'] + + # 计算总资产 + total_assets = self.cash + position_value + + # 记录状态 + state_data = { + 'cash': self.cash, + 'value': position_value, + 'total': total_assets, + 'positions': self.positions.copy(), + 'trades': daily_trades.copy() + } + + # 添加到trades + self.trades.loc[self.timestamp] = state_data + + def buy(self, symbol: str, price: float, size: int) -> bool: + """ + 买入操作 + + Args: + symbol (str): 交易标的代码 + price (float): 买入价格 + size (int): 买入数量 + + Returns: + bool: 交易是否成功 + """ + # 计算买入所需金额 + required_cash = price * size + + # 检查资金是否足够 + if self.cash < required_cash: + print(f"资金不足!需要 {required_cash:.2f},当前可用 {self.cash:.2f}") + return False + + # 执行买入操作 + self.cash -= required_cash + + # 更新持仓 + if symbol in self.positions: + # 已有持仓,计算新的平均成本 + old_position = self.positions[symbol] + old_size = old_position['size'] + old_avg_price = old_position['avg_price'] + old_total_value = old_size * old_avg_price + new_total_value = old_total_value + required_cash + new_size = old_size + size + new_avg_price = new_total_value / new_size + + self.positions[symbol] = { + 'size': new_size, + 'avg_price': new_avg_price + } + else: + # 新建持仓 + self.positions[symbol] = { + 'size': size, + 'avg_price': price + } + + # 记录交易历史 + self.trade_history.append({ + 'action': 'buy', + 'symbol': symbol, + 'price': price, + 'size': size, + 'timestamp': self.timestamp + }) + + print(f"买入成功: {symbol} {size}股 @ {price:.2f},花费 {required_cash:.2f}") + return True + + def sell(self, symbol: str, price: float, size: int) -> bool: + """ + 卖出操作 + + Args: + symbol (str): 交易标的代码 + price (float): 卖出价格 + size (int): 卖出数量 + + Returns: + bool: 交易是否成功 + """ + # 检查是否有足够的持仓 + if symbol not in self.positions: + print(f"没有 {symbol} 的持仓!") + return False + + position = self.positions[symbol] + if position['size'] < size: + print(f"持仓不足!当前持有 {position['size']},尝试卖出 {size}") + return False + + # 计算卖出所得金额 + sale_proceeds = price * size + + # 执行卖出操作 + self.cash += sale_proceeds + + # 更新持仓 + if position['size'] == size: + # 全部卖出,删除持仓记录 + del self.positions[symbol] + else: + # 部分卖出,更新持仓数量 + self.positions[symbol]['size'] -= size + + # 记录交易历史 + self.trade_history.append({ + 'action': 'sell', + 'symbol': symbol, + 'price': price, + 'size': size, + 'timestamp': self.timestamp + }) + + print(f"卖出成功: {symbol} {size}股 @ {price:.2f},获得 {sale_proceeds:.2f}") + return True + + def get(self, factor: str, timestamp=None) -> Any: + """ + 从trades DataFrame中获取数据 + + Args: + factor (str): 列名,可选 'cash', 'value', 'total', 'positions', 'trades' + timestamp: 时间戳,如果为None则使用当前时间戳 + + Returns: + Any: 对应列的数据,如果不存在则返回None + """ + if timestamp is None: + timestamp = self.timestamp + + if timestamp is None or timestamp not in self.trades.index: + return None + + return self.trades.at[timestamp, factor] + + diff --git a/qka/core/config.py b/qka/core/config.py deleted file mode 100644 index 719ff2d..0000000 --- a/qka/core/config.py +++ /dev/null @@ -1,230 +0,0 @@ -""" -QKA 配置管理系统 -提供统一的配置管理,支持环境变量、配置文件和代码配置 -""" - -import os -import json -from typing import Any, Dict, Optional -from pathlib import Path -from dataclasses import dataclass, asdict - - -@dataclass -class BacktestConfig: - """回测配置""" - initial_cash: float = 1_000_000 # 初始资金 - commission_rate: float = 0.0003 # 手续费率 - slippage: float = 0.001 # 滑点率 - min_trade_amount: int = 100 # 最小交易股数 - max_position_ratio: float = 0.3 # 单只股票最大仓位比例 - benchmark: str = '000300.SH' # 基准指数 - - -@dataclass -class DataConfig: - """数据配置""" - default_source: str = 'akshare' # 默认数据源 - cache_enabled: bool = True # 是否启用缓存 - cache_dir: str = './data_cache' # 缓存目录 - cache_expire_days: int = 7 # 缓存过期天数 - quality_check: bool = True # 是否进行数据质量检查 - auto_download: bool = True # 是否自动下载缺失数据 - - -@dataclass -class TradingConfig: - """交易配置""" - server_host: str = '0.0.0.0' - server_port: int = 8000 - token_auto_generate: bool = True - order_timeout: int = 30 # 订单超时时间(秒) - max_retry_times: int = 3 # 最大重试次数 - heartbeat_interval: int = 30 # 心跳间隔(秒) - - -@dataclass -class LogConfig: - """日志配置""" - level: str = 'INFO' - console_output: bool = True - file_output: bool = True - log_dir: str = './logs' - max_file_size: str = '10MB' - backup_count: int = 10 - format: str = '[%(levelname)s][%(asctime)s][%(name)s] %(message)s' - - -@dataclass -class PlotConfig: - """绘图配置""" - theme: str = 'plotly_white' - figure_height: int = 600 - figure_width: int = 1000 - color_scheme: str = 'default' # default, dark, colorful - show_grid: bool = True - auto_open: bool = True - - -class Config: - """QKA 配置管理器""" - - def __init__(self, config_file: Optional[str] = None): - """ - 初始化配置管理器 - - Args: - config_file: 配置文件路径,为None时使用默认配置 - """ - # 默认配置 - self.backtest = BacktestConfig() - self.data = DataConfig() - self.trading = TradingConfig() - self.log = LogConfig() - self.plot = PlotConfig() - - # 加载配置文件 - if config_file and os.path.exists(config_file): - self.load_from_file(config_file) - - # 加载环境变量配置 - self.load_from_env() - - def load_from_file(self, config_file: str): - """从配置文件加载配置""" - try: - with open(config_file, 'r', encoding='utf-8') as f: - config_data = json.load(f) - - # 更新各个配置段 - if 'backtest' in config_data: - self._update_config(self.backtest, config_data['backtest']) - if 'data' in config_data: - self._update_config(self.data, config_data['data']) - if 'trading' in config_data: - self._update_config(self.trading, config_data['trading']) - if 'log' in config_data: - self._update_config(self.log, config_data['log']) - if 'plot' in config_data: - self._update_config(self.plot, config_data['plot']) - - except Exception as e: - print(f"加载配置文件失败: {e}") - - def load_from_env(self): - """从环境变量加载配置""" - # 回测配置 - if os.getenv('QKA_INITIAL_CASH'): - self.backtest.initial_cash = float(os.getenv('QKA_INITIAL_CASH')) - if os.getenv('QKA_COMMISSION_RATE'): - self.backtest.commission_rate = float(os.getenv('QKA_COMMISSION_RATE')) - - # 数据配置 - if os.getenv('QKA_DATA_SOURCE'): - self.data.default_source = os.getenv('QKA_DATA_SOURCE') - if os.getenv('QKA_CACHE_DIR'): - self.data.cache_dir = os.getenv('QKA_CACHE_DIR') - # 交易配置 - if os.getenv('QKA_SERVER_PORT'): - self.trading.server_port = int(os.getenv('QKA_SERVER_PORT')) - - def _update_config(self, config_obj, config_dict: Dict[str, Any]): - """更新配置对象""" - for key, value in config_dict.items(): - if hasattr(config_obj, key): - setattr(config_obj, key, value) - - def save_to_file(self, config_file: str): - """保存配置到文件""" - config_data = { - 'backtest': asdict(self.backtest), - 'data': asdict(self.data), - 'trading': asdict(self.trading), - 'log': asdict(self.log), - 'plot': asdict(self.plot) - } - - # 确保目录存在(只有当目录路径不为空时) - dir_path = os.path.dirname(config_file) - if dir_path: - os.makedirs(dir_path, exist_ok=True) - - with open(config_file, 'w', encoding='utf-8') as f: - json.dump(config_data, f, indent=2, ensure_ascii=False) - - def to_dict(self) -> Dict[str, Any]: - """转换为字典格式""" - return { - 'backtest': asdict(self.backtest), - 'data': asdict(self.data), - 'trading': asdict(self.trading), - 'log': asdict(self.log), - 'plot': asdict(self.plot) - } - - def get(self, section: str, key: str, default: Any = None) -> Any: - """获取配置值""" - section_obj = getattr(self, section, None) - if section_obj: - return getattr(section_obj, key, default) - return default - - def set(self, section: str, key: str, value: Any): - """设置配置值""" - section_obj = getattr(self, section, None) - if section_obj: - setattr(section_obj, key, value) - - -# 全局配置实例 -config = Config() - - -def load_config(config_file: Optional[str] = None) -> Config: - """ - 加载配置文件 - - Args: - config_file: 配置文件路径 - - Returns: - Config对象 - - Examples: - # 使用默认配置 - config = load_config() - - # 从文件加载配置 - config = load_config('config.json') - - # 访问配置 - print(config.backtest.initial_cash) - print(config.data.default_source) - """ - global config - config = Config(config_file) - return config - - -def create_sample_config(file_path: str = 'qka_config.json'): - """ - 创建示例配置文件 - - Args: - file_path: 配置文件路径 - """ - sample_config = Config() - sample_config.save_to_file(file_path) - print(f"示例配置文件已创建: {file_path}") - - -if __name__ == '__main__': - # 创建示例配置文件 - create_sample_config() - - # 测试配置加载 - cfg = load_config('qka_config.json') - print("配置加载完成:") - print(f"初始资金: {cfg.backtest.initial_cash:,}") - print(f"数据源: {cfg.data.default_source}") - print(f"服务器端口: {cfg.trading.server_port}") diff --git a/qka/core/data.py b/qka/core/data.py new file mode 100644 index 0000000..55a5be1 --- /dev/null +++ b/qka/core/data.py @@ -0,0 +1,180 @@ +""" +QKA数据模块 + +提供统一的数据获取、缓存和管理功能,支持多数据源、多周期、多因子的数据获取。 +""" + +from pathlib import Path +from tqdm import tqdm +from concurrent.futures import ThreadPoolExecutor, as_completed +import pandas as pd +import pyarrow as pa +import pyarrow.parquet as pq +import akshare as ak +import dask.dataframe as dd +from typing import List, Dict, Optional, Callable +from qka.utils.logger import logger + +class Data(): + """ + 数据管理类 + + 负责股票数据的获取、缓存和管理,支持多数据源、并发下载和自定义因子计算。 + + Attributes: + symbols (List[str]): 股票代码列表 + period (str): 数据周期,如 '1d'、'1m' 等 + adjust (str): 复权方式,如 'qfq'、'hfq'、'bfq' + factor (Callable): 因子计算函数 + source (str): 数据源,如 'akshare'、'qmt' + pool_size (int): 并发下载线程数 + datadir (Path): 数据缓存目录 + target_dir (Path): 目标存储目录 + """ + + def __init__( + self, + symbols: Optional[List[str]] = None, + period: str = '1d', + adjust: str = 'qfq', + factor: Callable[[pd.DataFrame], pd.DataFrame] = lambda df: df, + source: str = 'akshare', + pool_size: int = 10, + datadir: Optional[Path] = None + ): + """ + 初始化数据对象 + + Args: + symbols: [维度1] 标的,如 ['000001.SZ', '600000.SH'] + period: [维度2] 周期,如 '1m', '5m', '1d' 等 + factor: [维度3] 因子字典,key为因子名,value为因子函数 + source: [维度4] 数据来源 ('qmt', 'akshare') + pool_size: 并发池大小 + datadir: 缓存根目录,默认为项目根目录下的 datadir + """ + self.symbols = symbols or [] + self.period = period + self.adjust = adjust + self.factor = factor + self.source = source + self.pool_size = pool_size + + # 初始化缓存目录 + if datadir is None: + # 默认使用当前工作目录下的 datadir + self.datadir = Path.cwd() / "datadir" + else: + self.datadir = Path(datadir) + + self.datadir.mkdir(parents=True, exist_ok=True) + + self.target_dir = self.datadir / self.source / self.period / (self.adjust or "bfq") + self.target_dir.mkdir(parents=True, exist_ok=True) + + def _download(self, symbol: str) -> Path: + """ + 下载单个股票的数据 + + Args: + symbol (str): 股票代码 + + Returns: + Path: 数据文件路径 + """ + path = self.target_dir / f"{symbol}.parquet" + + if path.exists(): + return path + + if self.source == 'akshare': + df = self._get_from_akshare(symbol) + else: + df = pd.DataFrame() + + if len(df) == 0: + return path + + table = pa.Table.from_pandas(df) + pq.write_table(table, path) + + return path + + def get(self) -> dd.DataFrame: + """ + 获取历史数据 + + 并发下载所有股票数据,应用因子计算,并返回合并后的Dask DataFrame。 + + Returns: + dd.DataFrame: 合并后的股票数据,每只股票的列名格式为 {symbol}_{column} + """ + # 准备缓存目录 + + with ThreadPoolExecutor(max_workers=self.pool_size) as executor: + # 提交下载任务 + futures = { + executor.submit(self._download, symbol): symbol + for symbol in self.symbols + } + + # 添加tqdm进度条 + with tqdm(total=len(self.symbols), desc="下载数据") as pbar: + for future in as_completed(futures): + symbol = futures[future] + pbar.update(1) + pbar.set_postfix_str(f"当前: {symbol}") + + dfs = [] + for symbol in self.symbols: + df = dd.read_parquet(str(self.target_dir / f"{symbol}.parquet")) + df = self.factor(df) + column_mapping = {col: f'{symbol}_{col}' for col in df.columns} + dfs.append(df.rename(columns=column_mapping)) + + df = dd.concat(dfs, axis=1, join='outer') + + return df + + def _get_from_akshare(self, symbol: str) -> pd.DataFrame: + """ + 从 akshare 获取单个股票的数据。 + + Args: + symbol (str): 股票代码,支持带后缀如 000001.SZ 或不带后缀的 000001 + + Returns: + pd.DataFrame: 股票数据,以 date 为索引,包含 open, high, low, close, volume, amount 列 + """ + column_mapping = { + "日期": "date", + "开盘": "open", + "收盘": "close", + "最高": "high", + "最低": "low", + "成交量": "volume", + "成交额": "amount", + } + + # 下载数据 + df = ak.stock_zh_a_hist(symbol=symbol, period='daily', adjust=self.adjust) + + # 数据标准化处理 + # 1. 标准化列名 + df = df.rename(columns=column_mapping) + if "date" in df.columns: + df["date"] = pd.to_datetime(df["date"]) + + # 2. 确保数值列为数值类型 + numeric_cols = [c for c in ("open", "high", "low", "close", "volume", "amount") if c in df.columns] + for col in numeric_cols: + df[col] = pd.to_numeric(df[col], errors="coerce") + + # 3. 只保留需要的列 + mapped_columns = list(column_mapping.values()) + available_columns = [col for col in mapped_columns if col in df.columns] + df = df[available_columns] + + df = df.set_index('date') + # 设置索引 + return df diff --git a/qka/core/data/__init__.py b/qka/core/data/__init__.py deleted file mode 100644 index df7695b..0000000 --- a/qka/core/data/__init__.py +++ /dev/null @@ -1,38 +0,0 @@ -""" -QKA数据模块 - -提供统一的数据源接口,支持多种数据源: -- QMT: 专业量化交易数据源 -- Akshare: 开源金融数据接口 - -使用方式: - import qka - - # 设置默认数据源 - qka.set_source('qmt') - - # 创建数据对象 - data_obj = qka.data(stocks=['000001.SZ', '600000.SH']) - - # 获取历史数据 - hist_data = data_obj.get('1d', '2024-01-01', '2024-12-31') -""" - -# 从base模块导入所有公共接口 -from .base import ( - set_source, - get_source, - data, - Data, - register_data_source, - get_available_sources -) - -# 公开的API - 这些是qka包级别要暴露的 -__all__ = [ - 'set_source', - 'get_source', - 'data', - 'register_data_source', - 'get_available_sources' -] diff --git a/qka/core/data/akshare.py b/qka/core/data/akshare.py deleted file mode 100644 index 78ba8e7..0000000 --- a/qka/core/data/akshare.py +++ /dev/null @@ -1,106 +0,0 @@ -from datetime import datetime, timedelta -from typing import List, Dict -import pandas as pd -from qka.utils.logger import logger -from .base import DataSource - -class AkshareData(DataSource): - """Akshare数据源""" - - def __init__(self, stocks: List[str] = None, sector: str = None): - super().__init__(stocks, sector) - try: - import akshare as ak - self.ak = ak - except ImportError: - logger.error("无法导入akshare,请安装: pip install akshare") - raise - - if sector is not None: - self.stocks = self._get_stocks_by_sector(sector) - - def _get_stocks_by_sector(self, sector: str) -> List[str]: - """根据板块获取股票列表""" - try: - if sector == "沪深A股": - # 获取沪深A股列表 - stock_info = self.ak.stock_info_a_code_name() - return stock_info['code'].tolist()[:100] # 限制数量避免过多 - else: - logger.warning(f"暂不支持板块: {sector}") - return [] - except Exception as e: - logger.error(f"获取板块股票失败: {e}") - return [] - - def get_historical_data(self, period: str = '1d', start_time: str = '', end_time: str = '') -> Dict[str, pd.DataFrame]: - """获取Akshare历史数据""" - if not self.stocks: - logger.warning("未指定股票列表") - return {} - - result = {} - - # 处理日期格式 - if start_time: - start_date = start_time.replace('-', '') if '-' in start_time else start_time - else: - start_date = (datetime.now() - timedelta(days=365)).strftime('%Y%m%d') - - if end_time: - end_date = end_time.replace('-', '') if '-' in end_time else end_time - else: - end_date = datetime.now().strftime('%Y%m%d') - - for stock in self.stocks: - try: - # 转换股票代码格式 - if '.' in stock: - code = stock.split('.')[0] - else: - code = stock - - # 获取股票历史数据 - df = self.ak.stock_zh_a_hist( - symbol=code, - period='daily', - start_date=start_date, - end_date=end_date, - adjust="qfq" # 前复权 - ) - - if df is not None and not df.empty: - # 重命名列以匹配标准格式 - column_mapping = { - '日期': 'date', - '开盘': 'open', - '收盘': 'close', - '最高': 'high', - '最低': 'low', - '成交量': 'volume', - '成交额': 'amount' - } - - df = df.rename(columns=column_mapping) - df['date'] = pd.to_datetime(df['date']) - df = df.set_index('date') - - # 确保数值类型 - numeric_cols = ['open', 'high', 'low', 'close', 'volume'] - for col in numeric_cols: - if col in df.columns: - df[col] = pd.to_numeric(df[col], errors='coerce') - - result[stock] = df - logger.debug(f"成功获取 {stock} 数据,共 {len(df)} 条记录") - - except Exception as e: - logger.error(f"获取 {stock} 数据失败: {e}") - continue - - return self.normalize_data(result) - - def subscribe_realtime(self, callback): - """Akshare暂不支持实时数据订阅""" - logger.warning("Akshare数据源暂不支持实时数据订阅") - raise NotImplementedError("Akshare数据源暂不支持实时数据订阅") diff --git a/qka/core/data/base.py b/qka/core/data/base.py deleted file mode 100644 index 59ac878..0000000 --- a/qka/core/data/base.py +++ /dev/null @@ -1,219 +0,0 @@ -""" -QKA数据模块基础功能 - -包含数据源管理、配置、工厂函数等核心功能 -""" - -from abc import ABC, abstractmethod -import pandas as pd -from typing import List, Dict -from qka.utils.logger import logger - -# ============================================================================ -# 1. 全局配置 -# ============================================================================ - -# 全局默认数据源配置 -_DEFAULT_DATA_SOURCE = 'akshare' - -def set_source(source: str) -> None: - """ - 设置全局默认数据源 - - Args: - source: 数据源类型 ('qmt', 'akshare') - - Examples: - import qka - - # 设置默认使用QMT数据源 - qka.set_source('qmt') - - # 之后创建数据对象就不需要指定source了 - data_obj = qka.data(stocks=['000001.SZ', '600000.SH']) - """ - global _DEFAULT_DATA_SOURCE - - if source.lower() not in DATA_SOURCE_MAPPING: - available_sources = ', '.join(DATA_SOURCE_MAPPING.keys()) - raise ValueError(f"不支持的数据源类型: {source},支持的数据源: {available_sources}") - - _DEFAULT_DATA_SOURCE = source.lower() - logger.info(f"已设置默认数据源为: {source}") - -def get_source() -> str: - """ - 获取当前默认数据源 - - Returns: - 当前默认数据源名称 - """ - return _DEFAULT_DATA_SOURCE - -# ============================================================================ -# 2. 数据源注册机制 -# ============================================================================ - -# 数据源映射字典(稍后会被初始化) -DATA_SOURCE_MAPPING = {} - -def get_available_sources(): - """获取所有可用的数据源名称""" - return list(DATA_SOURCE_MAPPING.keys()) - -def register_data_source(name: str, data_source_class): - """ - 注册新的数据源 - - Args: - name: 数据源名称 - data_source_class: 数据源类(必须继承DataSource) - - Examples: - # 注册新的数据源 - register_data_source('tushare', TushareData) - """ - if not issubclass(data_source_class, DataSource): - raise TypeError(f"数据源类 {data_source_class} 必须继承 DataSource") - - DATA_SOURCE_MAPPING[name.lower()] = data_source_class - logger.info(f"已注册数据源: {name}") - -# ============================================================================ -# 3. 基础抽象类 -# ============================================================================ - -class DataSource(ABC): - """数据源基类""" - - def __init__(self, stocks: List[str] = None, sector: str = None): - self.stocks = stocks or [] - self.sector = sector - - @abstractmethod - def get_historical_data(self, period: str = '1d', start_time: str = '', end_time: str = '') -> Dict[str, pd.DataFrame]: - """获取历史数据""" - pass - - @abstractmethod - def subscribe_realtime(self, callback): - """订阅实时数据""" - pass - - def normalize_data(self, data: Dict) -> Dict[str, pd.DataFrame]: - """标准化数据格式为统一的DataFrame格式""" - normalized = {} - for symbol, df in data.items(): - if isinstance(df, pd.DataFrame): - # 确保包含必要的列 - required_cols = ['open', 'high', 'low', 'close', 'volume'] - if all(col in df.columns for col in required_cols): - # 确保索引是datetime类型 - if not isinstance(df.index, pd.DatetimeIndex): - if 'time' in df.columns: - df = df.set_index('time') - df.index = pd.to_datetime(df.index) - normalized[symbol] = df - else: - logger.warning(f"股票 {symbol} 缺少必要的数据列") - return normalized - -# ============================================================================ -# 4. 统一数据接口 -# ============================================================================ - -class Data: - """统一数据接口""" - - def __init__(self, source: str = 'akshare', stocks: List[str] = None, sector: str = None): - """ - 初始化数据对象 - - Args: - source: 数据源类型 ('qmt', 'akshare') - stocks: 股票代码列表 - sector: 板块名称 - """ - self.source_type = source - - if source.lower() not in DATA_SOURCE_MAPPING: - available_sources = ', '.join(DATA_SOURCE_MAPPING.keys()) - raise ValueError(f"不支持的数据源类型: {source},支持的数据源: {available_sources}") - - # 动态创建数据源实例 - data_source_class = DATA_SOURCE_MAPPING[source.lower()] - self.data_source = data_source_class(stocks, sector) - - def get(self, period: str = '1d', start_time: str = '', end_time: str = '') -> Dict[str, pd.DataFrame]: - """获取历史数据""" - return self.data_source.get_historical_data(period, start_time, end_time) - - def subscribe(self, callback): - """订阅实时数据""" - return self.data_source.subscribe_realtime(callback) - - @property - def stocks(self) -> List[str]: - """获取股票列表""" - return self.data_source.stocks - -# ============================================================================ -# 5. 工厂函数 -# ============================================================================ - -def data(stocks=None, sector=None, source=None): - """ - 创建数据对象的工厂函数 - - Args: - stocks: 股票代码列表 - sector: 板块名称 - source: 数据源类型 ('qmt', 'akshare'),可选参数,如果不指定则使用全局默认数据源 - - Returns: - Data对象 - - Examples: - import qka - - # 设置默认数据源 - qka.set_source('qmt') - - # 使用默认数据源创建数据对象 - data_obj = qka.data(stocks=['000001.SZ', '600000.SH']) - - # 临时指定其他数据源 - data_ak = qka.data(stocks=['000001', '600000'], source='akshare') - - # 获取板块数据 - data_sector = qka.data(sector='沪深A股') - - # 获取历史数据 - hist_data = data_obj.get('1d', '2024-01-01', '2024-12-31') - """ - # 如果没有指定source,使用全局默认数据源 - if source is None: - source = _DEFAULT_DATA_SOURCE - - return Data(source, stocks, sector) - -# ============================================================================ -# 6. 初始化数据源映射 -# ============================================================================ - -def _initialize_data_sources(): - """初始化内置数据源""" - try: - from .qmt import QMTData - register_data_source('qmt', QMTData) - except ImportError: - pass # QMT可能未安装 - - try: - from .akshare import AkshareData - register_data_source('akshare', AkshareData) - except ImportError: - pass # Akshare可能未安装 - -# 自动初始化内置数据源 -_initialize_data_sources() diff --git a/qka/core/data/qmt.py b/qka/core/data/qmt.py deleted file mode 100644 index c00d63e..0000000 --- a/qka/core/data/qmt.py +++ /dev/null @@ -1,64 +0,0 @@ -import time -from datetime import datetime -from typing import List, Dict -import pandas as pd -from qka.utils.logger import logger -from .base import DataSource - -class QMTData(DataSource): - """QMT数据源""" - - def __init__(self, stocks: List[str] = None, sector: str = None): - super().__init__(stocks, sector) - try: - from xtquant import xtdata - self.xtdata = xtdata - if sector is not None: - self.stocks = xtdata.get_stock_list_in_sector(sector) - except ImportError: - logger.error("无法导入xtquant,请检查QMT是否正确安装") - raise - - def get_historical_data(self, period: str = '1d', start_time: str = '', end_time: str = '') -> Dict[str, pd.DataFrame]: - """获取QMT历史数据""" - if not self.stocks: - logger.warning("未指定股票列表") - return {} - - # 下载数据 - for stock in self.stocks: - self.xtdata.download_history_data( - stock_code=stock, - period=period, - start_time=start_time, - end_time=end_time, - incrementally=True - ) - - # 获取本地数据 - res = self.xtdata.get_local_data( - stock_list=self.stocks, - period=period, - start_time=start_time, - end_time=end_time - ) - - return self.normalize_data(res) - - def subscribe_realtime(self, callback): - """订阅QMT实时数据""" - delays = [] - - def task(res): - for code, item in res.items(): - rate = (item['lastPrice'] - item['lastClose']) / item['lastClose'] - timetag = datetime.fromtimestamp(item['time'] / 1000) - current_time = datetime.fromtimestamp(time.time()) - delay_seconds = (current_time - timetag).total_seconds() - if delay_seconds < 1000: - if delay_seconds > 3: - logger.warning(f'{code} 延迟 {delay_seconds}') - callback(code, item) - - self.xtdata.subscribe_whole_quote(code_list=self.stocks, callback=task) - self.xtdata.run() diff --git a/qka/core/events.py b/qka/core/events.py deleted file mode 100644 index ed211a2..0000000 --- a/qka/core/events.py +++ /dev/null @@ -1,365 +0,0 @@ -""" -QKA 事件驱动框架 -提供发布-订阅模式的事件系统,支持异步和同步事件处理 -""" - -import asyncio -from abc import ABC, abstractmethod -from collections import defaultdict -from datetime import datetime -from enum import Enum -from typing import Any, Callable, Dict, List, Optional, Union -from dataclasses import dataclass, asdict -import threading -import queue -import traceback -from qka.utils.logger import logger - - -class EventType(Enum): - """事件类型枚举""" - # 数据相关事件 - DATA_LOADED = "data_loaded" - DATA_ERROR = "data_error" - - # 回测相关事件 - BACKTEST_START = "backtest_start" - BACKTEST_END = "backtest_end" - BACKTEST_ERROR = "backtest_error" - - # 交易相关事件 - ORDER_CREATED = "order_created" - ORDER_FILLED = "order_filled" - ORDER_CANCELLED = "order_cancelled" - ORDER_ERROR = "order_error" - - # 策略相关事件 - STRATEGY_START = "strategy_start" - STRATEGY_END = "strategy_end" - STRATEGY_ERROR = "strategy_error" - SIGNAL_GENERATED = "signal_generated" - - # 风险管理事件 - RISK_CHECK = "risk_check" - RISK_ALERT = "risk_alert" - - # 系统事件 - SYSTEM_START = "system_start" - SYSTEM_SHUTDOWN = "system_shutdown" - HEARTBEAT = "heartbeat" - - -@dataclass -class Event: - """事件基类""" - event_type: EventType - data: Any = None - timestamp: datetime = None - source: str = None - event_id: str = None - - def __post_init__(self): - if self.timestamp is None: - self.timestamp = datetime.now() - if self.event_id is None: - self.event_id = f"{self.event_type.value}_{self.timestamp.strftime('%Y%m%d_%H%M%S_%f')}" - - def to_dict(self) -> Dict[str, Any]: - """转换为字典""" - result = asdict(self) - result['event_type'] = self.event_type.value - result['timestamp'] = self.timestamp.isoformat() - return result - - -@dataclass -class DataEvent(Event): - """数据事件""" - symbol: str = None - period: str = None - count: int = None - - -@dataclass -class OrderEvent(Event): - """订单事件""" - order_id: str = None - symbol: str = None - action: str = None # buy/sell - quantity: int = None - price: float = None - order_type: str = None - - -@dataclass -class SignalEvent(Event): - """信号事件""" - symbol: str = None - signal_type: str = None # buy/sell/hold - strength: float = None # 信号强度 0-1 - reason: str = None - - -class EventHandler(ABC): - """事件处理器基类""" - - @abstractmethod - def handle(self, event: Event) -> Optional[Any]: - """处理事件""" - pass - - def can_handle(self, event: Event) -> bool: - """判断是否可以处理该事件""" - return True - - -class AsyncEventHandler(EventHandler): - """异步事件处理器基类""" - - @abstractmethod - async def handle_async(self, event: Event) -> Optional[Any]: - """异步处理事件""" - pass - - def handle(self, event: Event) -> Optional[Any]: - """同步接口,内部调用异步方法""" - return asyncio.run(self.handle_async(event)) - - -class EventEngine: - """事件引擎""" - - def __init__(self, max_queue_size: int = 10000): - """ - 初始化事件引擎 - - Args: - max_queue_size: 事件队列最大大小 - """ - self._handlers: Dict[EventType, List[Union[EventHandler, Callable]]] = defaultdict(list) - self._event_queue = queue.Queue(maxsize=max_queue_size) - self._running = False - self._worker_thread = None - self._event_history: List[Event] = [] - self._max_history_size = 1000 - - # 统计信息 - self._event_count = defaultdict(int) - self._error_count = 0 - - def subscribe(self, event_type: EventType, handler: Union[EventHandler, Callable]): - """ - 订阅事件 - - Args: - event_type: 事件类型 - handler: 事件处理器或回调函数 - """ - self._handlers[event_type].append(handler) - logger.debug(f"事件订阅: {event_type.value} -> {handler}") - - def unsubscribe(self, event_type: EventType, handler: Union[EventHandler, Callable]): - """取消订阅""" - if handler in self._handlers[event_type]: - self._handlers[event_type].remove(handler) - logger.debug(f"取消事件订阅: {event_type.value} -> {handler}") - - def emit(self, event: Event, sync: bool = False): - """ - 发送事件 - - Args: - event: 事件对象 - sync: 是否同步处理 - """ - if sync: - self._process_event(event) - else: - try: - self._event_queue.put_nowait(event) - except queue.Full: - logger.error(f"事件队列已满,丢弃事件: {event.event_type.value}") - - def emit_simple(self, event_type: EventType, data: Any = None, source: str = None, sync: bool = False): - """ - 发送简单事件 - - Args: - event_type: 事件类型 - data: 事件数据 - source: 事件源 - sync: 是否同步处理 - """ - event = Event(event_type=event_type, data=data, source=source) - self.emit(event, sync) - - def start(self): - """启动事件引擎""" - if self._running: - logger.warning("事件引擎已经在运行") - return - - self._running = True - self._worker_thread = threading.Thread(target=self._run, daemon=True) - self._worker_thread.start() - - logger.info("事件引擎已启动") - self.emit_simple(EventType.SYSTEM_START, {"timestamp": datetime.now()}) - - def stop(self): - """停止事件引擎""" - if not self._running: - return - - self.emit_simple(EventType.SYSTEM_SHUTDOWN, {"timestamp": datetime.now()}) - self._running = False - - if self._worker_thread: - self._worker_thread.join(timeout=5) - - logger.info("事件引擎已停止") - - def _run(self): - """事件处理主循环""" - while self._running: - try: - # 带超时的获取事件,避免阻塞 - event = self._event_queue.get(timeout=1) - self._process_event(event) - self._event_queue.task_done() - except queue.Empty: - continue - except Exception as e: - logger.error(f"事件处理异常: {e}\n{traceback.format_exc()}") - self._error_count += 1 - - def _process_event(self, event: Event): - """处理单个事件""" - try: - # 记录事件历史 - self._add_to_history(event) - self._event_count[event.event_type] += 1 - - # 获取处理器列表 - handlers = self._handlers.get(event.event_type, []) - - if not handlers: - logger.debug(f"没有找到事件处理器: {event.event_type.value}") - return - - # 执行处理器 - for handler in handlers: - try: - if isinstance(handler, EventHandler): - if handler.can_handle(event): - handler.handle(event) - elif callable(handler): - handler(event) - except Exception as e: - logger.error(f"事件处理器执行失败: {handler}, 事件: {event.event_type.value}, 错误: {e}") - self._error_count += 1 - - except Exception as e: - logger.error(f"事件处理异常: {e}\n{traceback.format_exc()}") - self._error_count += 1 - - def _add_to_history(self, event: Event): - """添加到事件历史""" - self._event_history.append(event) - - # 保持历史记录大小限制 - if len(self._event_history) > self._max_history_size: - self._event_history = self._event_history[-self._max_history_size:] - - def get_statistics(self) -> Dict[str, Any]: - """获取统计信息""" - return { - "event_count": dict(self._event_count), - "error_count": self._error_count, - "queue_size": self._event_queue.qsize(), - "handler_count": { - event_type.value: len(handlers) - for event_type, handlers in self._handlers.items() - }, - "is_running": self._running - } - - def get_event_history(self, event_type: Optional[EventType] = None, limit: int = 100) -> List[Event]: - """ - 获取事件历史 - - Args: - event_type: 筛选事件类型 - limit: 限制返回数量 - """ - history = self._event_history - - if event_type: - history = [e for e in history if e.event_type == event_type] - - return history[-limit:] - - -# 全局事件引擎实例 -event_engine = EventEngine() - - -# 便捷的装饰器函数 -def event_handler(event_type: EventType): - """ - 事件处理器装饰器 - - Examples: - @event_handler(EventType.ORDER_FILLED) - def on_order_filled(event): - print(f"订单成交: {event.data}") - """ - def decorator(func): - event_engine.subscribe(event_type, func) - return func - return decorator - - -# 便捷函数 -def emit_event(event_type: EventType, data: Any = None, source: str = None, sync: bool = False): - """发送事件的便捷函数""" - event_engine.emit_simple(event_type, data, source, sync) - - -def start_event_engine(): - """启动事件引擎""" - event_engine.start() - - -def stop_event_engine(): - """停止事件引擎""" - event_engine.stop() - - -if __name__ == '__main__': - # 测试事件系统 - - @event_handler(EventType.DATA_LOADED) - def on_data_loaded(event): - print(f"数据加载完成: {event.data}") - - @event_handler(EventType.ORDER_FILLED) - def on_order_filled(event): - print(f"订单成交: {event.data}") - - # 启动事件引擎 - start_event_engine() - - # 发送测试事件 - emit_event(EventType.DATA_LOADED, {"symbol": "000001", "count": 1000}) - emit_event(EventType.ORDER_FILLED, {"order_id": "123", "price": 10.5}) - - # 等待处理完成 - import time - time.sleep(1) - - # 查看统计信息 - print("事件统计:", event_engine.get_statistics()) - - # 停止事件引擎 - stop_event_engine() diff --git a/qka/core/plot.py b/qka/core/plot.py deleted file mode 100644 index 78009a9..0000000 --- a/qka/core/plot.py +++ /dev/null @@ -1,218 +0,0 @@ -import pandas as pd -import plotly.graph_objects as go -from plotly.subplots import make_subplots -from typing import Dict, Any -import numpy as np - -def plot(result: Dict[str, Any], title: str = "回测结果"): - """ - 绘制回测结果图表 - - Args: - result: backtest()函数返回的结果字典 - title: 图表标题 - - Examples: - import qka - - result = qka.backtest(data, strategy) - qka.plot(result) - """ - - if 'daily_values' not in result or not result['daily_values']: - print("错误:回测结果中没有日收益数据") - return - - # 提取数据 - daily_data = result['daily_values'] - dates = [record['date'] for record in daily_data] - values = [record['total_value'] for record in daily_data] - - # 计算累计收益率和其他指标 - initial_value = result['initial_capital'] - returns = [(v - initial_value) / initial_value * 100 for v in values] - - # 计算每日收益率 - daily_returns = [] - for i in range(1, len(values)): - daily_return = (values[i] - values[i-1]) / values[i-1] * 100 - daily_returns.append(daily_return) - - # 计算回撤 - peak_values = pd.Series(values).expanding().max() - drawdowns = [(v - peak) / peak * 100 for v, peak in zip(values, peak_values)] - - # 定义颜色函数 - def get_color_style(value): - """根据值的正负返回颜色样式""" - if value < 0: - return "color: red;" - else: - return "color: green;" - - # 创建子图 - fig = make_subplots( - rows=3, cols=2, - subplot_titles=( - '累计收益率曲线', '回撤分析', - '资产价值变化', '每日收益率分布', - '滚动夏普比率', '月度收益' - ), - specs=[ - [{"colspan": 2}, None], - [{"secondary_y": False}, {"type": "histogram"}], - [{"secondary_y": False}, {"secondary_y": False}] - ], - vertical_spacing=0.12, - horizontal_spacing=0.1 - ) - - # 1. 累计收益率曲线 (主图) - fig.add_trace( - go.Scatter( - x=dates, - y=returns, - mode='lines', - name='累计收益率', - line=dict(color='#2E86AB', width=3), - hovertemplate='日期: %{x}
收益率: %{y:.2f}%' - ), - row=1, col=1 - ) - - # 添加零轴线 - fig.add_hline(y=0, line_dash="dash", line_color="gray", opacity=0.5, row=1, col=1) - - # 2. 回撤分析 - fig.add_trace( - go.Scatter( - x=dates, - y=drawdowns, - mode='lines', - name='回撤', - line=dict(color='#A23B72', width=2), - fill='tonexty', - fillcolor='rgba(162, 59, 114, 0.2)', - hovertemplate='日期: %{x}
回撤: %{y:.2f}%' - ), - row=2, col=1 - ) - - # 3. 资产价值变化 - fig.add_trace( - go.Scatter( - x=dates, - y=values, - mode='lines', - name='总资产', - line=dict(color='#F18F01', width=2), - hovertemplate='日期: %{x}
资产: ¥%{y:,.0f}' - ), - row=2, col=2 - ) - - # 4. 每日收益率分布 - if daily_returns: - fig.add_trace( - go.Histogram( - x=daily_returns, - name='每日收益率', - nbinsx=30, - marker_color='#C73E1D', - opacity=0.7, - hovertemplate='收益率: %{x:.2f}%
频次: %{y}' - ), - row=3, col=1 - ) - - # 5. 滚动夏普比率 (30日) - if len(daily_returns) > 30: - rolling_sharpe = [] - for i in range(30, len(daily_returns)): - window_returns = daily_returns[i-30:i] - if len(window_returns) > 0: - mean_return = np.mean(window_returns) - std_return = np.std(window_returns) - sharpe = (mean_return * 252) / (std_return * np.sqrt(252)) if std_return > 0 else 0 - rolling_sharpe.append(sharpe) - - if rolling_sharpe: - fig.add_trace( - go.Scatter( - x=dates[30:30+len(rolling_sharpe)], - y=rolling_sharpe, - mode='lines', - name='30日滚动夏普', - line=dict(color='#3F88C5', width=2), - hovertemplate='日期: %{x}
夏普比率: %{y:.2f}' - ), - row=3, col=2 - ) - - # 更新布局 - fig.update_layout( - title=dict( - text=f"{title}
总收益: {result['total_return']:.2%} | 年化收益: {result['annual_return']:.2%} | 夏普比率: {result['sharpe_ratio']:.2f} | 最大回撤: {result['max_drawdown']:.2%}", - x=0.5, - font=dict(size=16) - ), - height=800, - showlegend=True, - template='plotly_white', - margin=dict(t=120, b=60, l=60, r=60) - ) - - # 更新子图标题 - fig.update_xaxes(title_text="日期", row=1, col=1) - fig.update_yaxes(title_text="累计收益率 (%)", row=1, col=1) - - fig.update_xaxes(title_text="日期", row=2, col=1) - fig.update_yaxes(title_text="回撤 (%)", row=2, col=1) - - fig.update_xaxes(title_text="日期", row=2, col=2) - fig.update_yaxes(title_text="资产价值 (¥)", row=2, col=2) - - fig.update_xaxes(title_text="每日收益率 (%)", row=3, col=1) - fig.update_yaxes(title_text="频次", row=3, col=1) - - fig.update_xaxes(title_text="日期", row=3, col=2) - fig.update_yaxes(title_text="夏普比率", row=3, col=2) - - # 显示图表 - fig.show() - - # 打印关键指标 - print(f"\n📊 回测结果摘要") - print(f"{'='*50}") - print(f"📈 收益指标:") - print(f" 总收益率: {result['total_return']:.2%}") - print(f" 年化收益率: {result['annual_return']:.2%}") - print(f" 基准收益率: {result.get('benchmark_return', 'N/A')}") - print(f"") - print(f"⚠️ 风险指标:") - print(f" 波动率: {result['volatility']:.2%}") - print(f" 夏普比率: {result['sharpe_ratio']:.2f}") - print(f" 最大回撤: {result['max_drawdown']:.2%}") - print(f"") - print(f"📊 交易指标:") - print(f" 胜率: {result['win_rate']:.2%}") - print(f" 总交易次数: {result['total_trades']}") - print(f" 交易成本: ¥{result['total_commission']:,.2f}") - print(f" 交易天数: {result['trading_days']}") - - # 计算额外统计信息 - if daily_returns: - print(f"") - print(f"📋 统计信息:") - print(f" 平均日收益: {np.mean(daily_returns):.3f}%") - print(f" 收益标准差: {np.std(daily_returns):.3f}%") - print(f" 最大单日收益: {max(daily_returns):.2f}%") - print(f" 最大单日亏损: {min(daily_returns):.2f}%") - - # 计算胜负比 - positive_days = len([r for r in daily_returns if r > 0]) - total_days = len(daily_returns) - if total_days > 0: - print(f" 盈利天数占比: {positive_days/total_days:.2%}") - - print(f"{'='*50}") diff --git a/qka/core/strategy.py b/qka/core/strategy.py new file mode 100644 index 0000000..a08a1bd --- /dev/null +++ b/qka/core/strategy.py @@ -0,0 +1,33 @@ +""" +QKA策略模块 + +提供策略开发的抽象基类,定义策略开发的标准接口和事件处理机制。 +""" + +from abc import ABC, abstractmethod +from qka.core.broker import Broker + +class Strategy(ABC): + """ + 策略抽象基类 + + 所有自定义策略都应该继承此类,并实现on_bar方法。 + + Attributes: + broker (Broker): 交易经纪商实例,用于执行交易操作 + """ + + def __init__(self): + """初始化策略""" + self.broker = Broker() + + @abstractmethod + def on_bar(self, date, get): + """ + 每个bar的处理逻辑,必须由子类实现 + + Args: + date: 当前时间戳 + get: 获取因子数据的函数,格式为 get(factor_name) -> pd.Series + """ + pass \ No newline at end of file diff --git a/qka/utils/__init__.py b/qka/utils/__init__.py index 0dc7439..ef61e08 100644 --- a/qka/utils/__init__.py +++ b/qka/utils/__init__.py @@ -4,16 +4,11 @@ """ from .logger import logger, create_logger, get_structured_logger -from .tools import Cache, Timer, timer, retry, memoize, FileUtils, DateUtils, MathUtils, ValidationUtils -from .tools import format_number, format_percentage, format_currency from .anis import RED, GREEN, YELLOW, BLUE, RESET from .util import timestamp_to_datetime_string, parse_order_type, convert_to_current_date __all__ = [ 'logger', 'create_logger', 'get_structured_logger', - 'Cache', 'Timer', 'timer', 'retry', 'memoize', - 'FileUtils', 'DateUtils', 'MathUtils', 'ValidationUtils', - 'format_number', 'format_percentage', 'format_currency', 'RED', 'GREEN', 'YELLOW', 'BLUE', 'RESET', 'timestamp_to_datetime_string', 'parse_order_type', 'convert_to_current_date' ] diff --git a/qka/utils/tools.py b/qka/utils/tools.py deleted file mode 100644 index c63c3e8..0000000 --- a/qka/utils/tools.py +++ /dev/null @@ -1,397 +0,0 @@ -""" -QKA 基础工具类 -提供通用的工具函数和类 -""" - -import time -import hashlib -import threading -from datetime import datetime, timedelta -from typing import Any, Dict, List, Optional, Union, Callable -from functools import wraps -import inspect -from pathlib import Path -import pickle -import json - - -class Singleton(type): - """单例模式元类""" - _instances = {} - _lock = threading.Lock() - - def __call__(cls, *args, **kwargs): - if cls not in cls._instances: - with cls._lock: - if cls not in cls._instances: - cls._instances[cls] = super().__call__(*args, **kwargs) - return cls._instances[cls] - - -class Cache: - """简单的内存缓存""" - - def __init__(self, max_size: int = 1000, ttl: int = 3600): - """ - 初始化缓存 - - Args: - max_size: 最大缓存条目数 - ttl: 过期时间(秒) - """ - self.max_size = max_size - self.ttl = ttl - self._cache = {} - self._access_times = {} - self._lock = threading.RLock() - - def get(self, key: str) -> Optional[Any]: - """获取缓存值""" - with self._lock: - if key not in self._cache: - return None - - # 检查是否过期 - if time.time() - self._access_times[key] > self.ttl: - self._remove(key) - return None - - # 更新访问时间 - self._access_times[key] = time.time() - return self._cache[key] - - def set(self, key: str, value: Any): - """设置缓存值""" - with self._lock: - # 如果缓存已满,移除最老的条目 - if len(self._cache) >= self.max_size and key not in self._cache: - oldest_key = min(self._access_times.keys(), - key=lambda k: self._access_times[k]) - self._remove(oldest_key) - - self._cache[key] = value - self._access_times[key] = time.time() - - def _remove(self, key: str): - """移除缓存条目""" - self._cache.pop(key, None) - self._access_times.pop(key, None) - - def clear(self): - """清空缓存""" - with self._lock: - self._cache.clear() - self._access_times.clear() - - def size(self) -> int: - """获取缓存大小""" - return len(self._cache) - - -class Timer: - """计时器工具""" - - def __init__(self): - self.start_time = None - self.end_time = None - - def start(self): - """开始计时""" - self.start_time = time.time() - - def stop(self): - """停止计时""" - self.end_time = time.time() - - def elapsed(self) -> float: - """获取已耗时间(秒)""" - if self.start_time is None: - return 0 - - end = self.end_time if self.end_time else time.time() - return end - self.start_time - - def __enter__(self): - self.start() - return self - - def __exit__(self, *args): - self.stop() - - -def timer(func: Callable) -> Callable: - """计时装饰器""" - @wraps(func) - def wrapper(*args, **kwargs): - start_time = time.time() - result = func(*args, **kwargs) - end_time = time.time() - - func_name = f"{func.__module__}.{func.__name__}" - print(f"⏱️ {func_name} 执行时间: {end_time - start_time:.4f}秒") - - return result - return wrapper - - -def retry(max_attempts: int = 3, delay: float = 1.0, backoff: float = 2.0): - """重试装饰器""" - def decorator(func: Callable) -> Callable: - @wraps(func) - def wrapper(*args, **kwargs): - attempts = 0 - current_delay = delay - - while attempts < max_attempts: - try: - return func(*args, **kwargs) - except Exception as e: - attempts += 1 - if attempts >= max_attempts: - raise e - - print(f"🔄 {func.__name__} 第{attempts}次尝试失败: {e}, {current_delay}秒后重试") - time.sleep(current_delay) - current_delay *= backoff - - return None - return wrapper - return decorator - - -def memoize(ttl: Optional[int] = None): - """记忆化装饰器""" - def decorator(func: Callable) -> Callable: - cache = {} - cache_times = {} - - @wraps(func) - def wrapper(*args, **kwargs): - # 创建缓存键 - key = _create_cache_key(func, args, kwargs) - - # 检查缓存 - if key in cache: - if ttl is None or (time.time() - cache_times[key]) < ttl: - return cache[key] - else: - # 过期,移除缓存 - del cache[key] - del cache_times[key] - - # 执行函数并缓存结果 - result = func(*args, **kwargs) - cache[key] = result - cache_times[key] = time.time() - - return result - - # 添加清除缓存的方法 - wrapper.clear_cache = lambda: cache.clear() or cache_times.clear() - - return wrapper - return decorator - - -def _create_cache_key(func: Callable, args: tuple, kwargs: dict) -> str: - """创建缓存键""" - key_parts = [func.__name__] - - # 添加位置参数 - for arg in args: - if hasattr(arg, '__dict__'): - key_parts.append(str(hash(str(sorted(arg.__dict__.items()))))) - else: - key_parts.append(str(hash(str(arg)))) - - # 添加关键字参数 - for k, v in sorted(kwargs.items()): - key_parts.append(f"{k}={hash(str(v))}") - - key_string = "|".join(key_parts) - return hashlib.md5(key_string.encode()).hexdigest() - - -class FileUtils: - """文件操作工具类""" - - @staticmethod - def ensure_dir(path: Union[str, Path]): - """确保目录存在""" - Path(path).mkdir(parents=True, exist_ok=True) - - @staticmethod - def save_json(data: Any, file_path: Union[str, Path], indent: int = 2): - """保存JSON文件""" - FileUtils.ensure_dir(Path(file_path).parent) - with open(file_path, 'w', encoding='utf-8') as f: - json.dump(data, f, indent=indent, ensure_ascii=False, default=str) - - @staticmethod - def load_json(file_path: Union[str, Path]) -> Any: - """加载JSON文件""" - with open(file_path, 'r', encoding='utf-8') as f: - return json.load(f) - - @staticmethod - def save_pickle(data: Any, file_path: Union[str, Path]): - """保存pickle文件""" - FileUtils.ensure_dir(Path(file_path).parent) - with open(file_path, 'wb') as f: - pickle.dump(data, f) - - @staticmethod - def load_pickle(file_path: Union[str, Path]) -> Any: - """加载pickle文件""" - with open(file_path, 'rb') as f: - return pickle.load(f) - - @staticmethod - def get_file_size(file_path: Union[str, Path]) -> int: - """获取文件大小(字节)""" - return Path(file_path).stat().st_size - - @staticmethod - def get_file_mtime(file_path: Union[str, Path]) -> datetime: - """获取文件修改时间""" - return datetime.fromtimestamp(Path(file_path).stat().st_mtime) - - -class DateUtils: - """日期时间工具类""" - - @staticmethod - def is_trading_day(date: datetime) -> bool: - """判断是否为交易日(简单实现,只排除周末)""" - return date.weekday() < 5 - - @staticmethod - def get_trading_days(start_date: datetime, end_date: datetime) -> List[datetime]: - """获取指定期间的交易日列表""" - days = [] - current = start_date - - while current <= end_date: - if DateUtils.is_trading_day(current): - days.append(current) - current += timedelta(days=1) - - return days - - @staticmethod - def format_datetime(dt: datetime, format_str: str = '%Y-%m-%d %H:%M:%S') -> str: - """格式化日期时间""" - return dt.strftime(format_str) - - @staticmethod - def parse_datetime(date_str: str, format_str: str = '%Y-%m-%d') -> datetime: - """解析日期时间字符串""" - return datetime.strptime(date_str, format_str) - - @staticmethod - def get_date_range(days: int, end_date: Optional[datetime] = None) -> tuple: - """获取日期范围""" - if end_date is None: - end_date = datetime.now() - start_date = end_date - timedelta(days=days) - return start_date, end_date - - -class MathUtils: - """数学工具类""" - - @staticmethod - def safe_divide(a: float, b: float, default: float = 0) -> float: - """安全除法,避免除零错误""" - return a / b if b != 0 else default - - @staticmethod - def clamp(value: float, min_val: float, max_val: float) -> float: - """将值限制在指定范围内""" - return max(min_val, min(value, max_val)) - - @staticmethod - def percent_change(old_value: float, new_value: float) -> float: - """计算百分比变化""" - if old_value == 0: - return 0 - return (new_value - old_value) / old_value * 100 - - -class ValidationUtils: - """验证工具类""" - - @staticmethod - def is_valid_symbol(symbol: str) -> bool: - """验证股票代码格式""" - import re - # 简单的A股股票代码验证 - pattern = r'^(000|002|300|600|688)\d{3}\.(SZ|SH)$' - return bool(re.match(pattern, symbol)) - - @staticmethod - def is_positive_number(value: Any) -> bool: - """验证是否为正数""" - try: - return float(value) > 0 - except (ValueError, TypeError): - return False - - @staticmethod - def is_valid_date_range(start_date: str, end_date: str) -> bool: - """验证日期范围是否有效""" - try: - start = datetime.strptime(start_date, '%Y-%m-%d') - end = datetime.strptime(end_date, '%Y-%m-%d') - return start <= end - except ValueError: - return False - - -def format_number(num: float, precision: int = 2, use_separator: bool = True) -> str: - """格式化数字显示""" - if use_separator: - return f"{num:,.{precision}f}" - else: - return f"{num:.{precision}f}" - - -def format_percentage(ratio: float, precision: int = 2) -> str: - """格式化百分比显示""" - return f"{ratio * 100:.{precision}f}%" - - -def format_currency(amount: float, currency: str = '¥', precision: int = 2) -> str: - """格式化货币显示""" - return f"{currency}{amount:,.{precision}f}" - - -if __name__ == '__main__': - # 测试缓存 - cache = Cache(max_size=3, ttl=2) - cache.set('key1', 'value1') - cache.set('key2', 'value2') - print(f"缓存大小: {cache.size()}") - print(f"key1: {cache.get('key1')}") - - # 测试计时器 - with Timer() as t: - time.sleep(0.1) - print(f"计时: {t.elapsed():.3f}秒") - - # 测试装饰器 - @timer - @retry(max_attempts=2) - def test_func(): - print("测试函数执行") - return "success" - - result = test_func() - print(f"结果: {result}") - - # 测试工具函数 - print(f"格式化数字: {format_number(123456.789)}") - print(f"格式化百分比: {format_percentage(0.1234)}") - print(f"格式化货币: {format_currency(123456.78)}") - - print("工具类测试完成") diff --git "a/\345\267\245\344\275\234\350\277\233\345\272\246\350\256\260\345\275\225.md" "b/\345\267\245\344\275\234\350\277\233\345\272\246\350\256\260\345\275\225.md" deleted file mode 100644 index 9b17df0..0000000 --- "a/\345\267\245\344\275\234\350\277\233\345\272\246\350\256\260\345\275\225.md" +++ /dev/null @@ -1,217 +0,0 @@ -# QKA量化回测系统 - 第一阶段工作进度记录 - -**工作日期:** 2025年6月19日 -**任务阶段:** 第一阶段 - 基础设施建设和文档体系搭建 -**整体进度:** 第一阶段完成度约85%,整个项目计划的第一阶段基本完成 - -## 整体项目规划回顾 - -### 🎯 第一阶段:基础设施建设(当前阶段,85%完成) -- ✅ 配置管理系统 -- ✅ 事件驱动框架 -- ✅ 增强日志系统 -- ✅ 基础工具类 -- ✅ 文档体系搭建 - -### 🚀 后续阶段(待规划) -- **第二阶段:** 数据层增强 -- **第三阶段:** 策略引擎优化 -- **第四阶段:** 执行引擎升级 -- **第五阶段:** 风险管理强化 -- **第六阶段:** 系统集成 - -## 已完成工作 - -### 1. 核心基础设施完善 ✅ -- **配置管理系统** (`qka/core/config.py`) - 支持文件、环境变量、代码多种配置方式 -- **事件驱动框架** (`qka/core/events.py`) - 支持多类型事件、发布-订阅、异步处理、事件统计 -- **增强日志系统** (`qka/utils/logger.py`) - 支持彩色输出、结构化日志、文件轮转、微信通知 -- **通用工具类** (`qka/utils/tools.py`) - 包括缓存、计时器、装饰器、文件/格式化/验证工具等 - -### 2. Bug修复和代码质量 ✅ -- 修复了`create_sample_config`目录创建bug和相关缩进问题 -- 添加/修复了各模块的`__init__.py`文件,保证模块可正确导入 -- 清理了冗余示例,仅保留核心演示文件 - -### 3. 现代化文档站搭建 ✅ -- **MkDocs配置** (`mkdocs.yml`) - 使用Material主题,配置了API自动生成插件 -- **文档结构设计** - 建立了清晰的文档导航和层次结构 -- **API自动生成配置** - 集成mkdocstrings,支持从源码自动生成API文档 - -### 4. 用户指南文档完善 ✅ -- `docs/index.md` - 项目首页 -- `docs/getting-started/` 目录: - - `installation.md` - 安装指南 - - `first-strategy.md` - 第一个策略教程 - - `concepts.md` - 基础概念 -- `docs/user-guide/` 目录: - - `config.md` - 配置管理指南 - - `events.md` - 事件系统指南 - - `logging.md` - 日志系统指南 - - `data.md` - 数据获取指南 - - `strategy.md` - 策略开发指南 - - `backtest.md` - 回测分析指南 - - `trading.md` - 实盘交易指南 - -### 5. API参考文档建设 ✅ -- `docs/api/index.md` - API概览页面 -- `docs/api/core/` 目录: - - `index.md` - Core模块概览 - - `config.md` - 配置管理API - - `events.md` - 事件系统API -- `docs/api/utils/` 目录: - - `index.md` - Utils模块概览 - - `logger.md` - 日志系统API - - `tools.md` - 通用工具API -- `docs/api/brokers/index.md` - 交易模块概览 -- `docs/api/mcp/index.md` - MCP模块概览 - -### 6. 示例和开发指南框架 ✅ -- `docs/examples/index.md` - 示例教程概览 -- `docs/development/index.md` - 开发指南概览 - -### 7. 模块结构修复 ✅ -- 为`qka/brokers/`目录添加了`__init__.py`文件 -- 为`qka/mcp/`目录添加了`__init__.py`文件 -- 在相关模块文件中添加了基础类定义: - - `qka/brokers/trade.py` - 添加了Order、Trade、Position等类 - - `qka/mcp/api.py` - 添加了MCPServer、MCPClient、ContextManager等类 - - `qka/mcp/server.py` - 添加了ModelServer类 - -## 遇到的问题和解决状态 - -### 1. YAML语法错误 ✅ 已解决 -- **问题:** `mkdocs.yml`文件存在YAML语法错误(缺少换行符) -- **解决:** 修复了第132行和第156行的语法错误 - -### 2. 模块导入错误 ✅ 已解决 -- **问题:** 缺少`__init__.py`文件导致模块无法导入 -- **解决:** 为所有模块目录添加了正确的`__init__.py`文件 - -### 3. API文档引用问题 ✅ 已解决 -- **问题:** 用户指南中引用了不存在的函数和类 -- **解决:** 将直接的API引用改为指向专门的API文档页面的链接 - -## 待完成工作 - -### 1. 文档内容补充 🔄 -- **API参考文档细化:** - - `docs/api/core/backtest.md` - 回测引擎API - - `docs/api/core/data.md` - 数据处理API - - `docs/api/core/plot.md` - 绘图工具API - - `docs/api/utils/anis.md` - 动画工具API - - `docs/api/utils/util.md` - 通用函数API - - `docs/api/brokers/client.md` - 交易客户端API - - `docs/api/brokers/server.md` - 交易服务器API - - `docs/api/brokers/trade.md` - 交易执行API - - `docs/api/mcp/api.md` - MCP API接口 - - `docs/api/mcp/server.md` - MCP服务器API - -- **示例教程补充:** - - `docs/examples/basic/` - 基础示例 - - `docs/examples/advanced/` - 进阶示例 - - `docs/examples/complete/` - 完整案例 - -- **开发指南补充:** - - `docs/development/architecture.md` - 架构设计 - - `docs/development/development-setup.md` - 开发环境 - - `docs/development/coding-standards.md` - 编码规范 - - `docs/development/api-design.md` - API设计 - - `docs/development/testing.md` - 测试指南 - - `docs/development/deployment.md` - 部署指南 - - `docs/development/contributing.md` - 贡献指南 - -### 2. 功能实现完善 🔄 -- **模块功能补充:** - - 完善brokers模块的实际功能实现 - - 完善mcp模块的实际功能实现 - - 添加更多实用的工具函数 - - 完善事件系统的具体事件类型 - -### 3. 文档站测试和部署 ⏳ -- **测试验证:** - - 验证MkDocs能正常启动(已修复语法错误,但可能还需要进一步测试) - - 验证所有链接和导航正常工作 - - 验证API文档自动生成功能 - - 测试文档的响应式布局 - -- **部署配置:** - - 配置GitHub Pages或其他文档托管平台 - - 设置自动构建和部署流程 - -## 当前文件状态 - -### 核心代码文件 -``` -qka/ -├── core/ -│ ├── __init__.py ✅ -│ ├── config.py ✅ (完整实现) -│ ├── events.py ✅ (完整实现) -│ ├── backtest.py ⚠️ (原有文件,待增强) -│ ├── data.py ⚠️ (原有文件,待增强) -│ └── plot.py ⚠️ (原有文件,待增强) -├── utils/ -│ ├── __init__.py ✅ -│ ├── logger.py ✅ (完整实现) -│ ├── tools.py ✅ (完整实现) -│ ├── anis.py ⚠️ (原有文件) -│ └── util.py ⚠️ (原有文件) -├── brokers/ -│ ├── __init__.py ✅ (新增) -│ ├── client.py ⚠️ (部分实现) -│ ├── server.py ⚠️ (原有文件) -│ └── trade.py ✅ (增强实现) -├── mcp/ -│ ├── __init__.py ✅ (新增) -│ ├── api.py ✅ (新增完整实现) -│ └── server.py ✅ (增强实现) -└── examples/ - ├── simple_demo.py ✅ - └── simple_backtest_demo.py ✅ -``` - -### 文档文件 -``` -docs/ -├── index.md ✅ -├── mkdocs.yml ✅ (已修复语法错误) -├── getting-started/ ✅ (完整) -├── user-guide/ ✅ (完整,已修复API引用) -├── api/ ✅ (结构完成,部分内容待补充) -├── examples/ 🔄 (结构完成,内容待补充) -└── development/ 🔄 (结构完成,内容待补充) -``` - -## 继续工作的建议 - -### 完成第一阶段的剩余工作: -1. **验证文档站:** 运行`uv run mkdocs serve`确认无错误 -2. **补充API文档:** 完善各模块的具体API文档内容 -3. **添加基础示例:** 创建第一阶段功能的使用示例 -4. **第一阶段总结:** 完成第一阶段的验收和总结 - -### 后续阶段规划: -- **第二阶段启动前:** 需要详细规划数据层增强的具体任务 -- **重点关注:** 多数据源集成、数据质量检查、高性能缓存等 -- **时间规划:** 建议每个阶段预留充足的开发和测试时间 - -### 重要提醒: -我们目前专注于**第一阶段的收尾工作**,不要急于进入第二阶段。第一阶段的基础设施必须稳固,才能支撑后续阶段的开发。 -```bash -# 启动文档服务器 -uv run mkdocs serve - -# 构建文档 -uv run mkdocs build - -# 部署到GitHub Pages -uv run mkdocs gh-deploy -``` - -### 重要文件位置: -- **主配置:** `mkdocs.yml` -- **文档源码:** `docs/` 目录 -- **项目源码:** `qka/` 目录 - -这个记录文件已保存,您可以随时根据这个进度继续工作。整体框架已经搭建完成,剩下的主要是内容补充和功能完善。