FQuant 后端开发指南
1. 项目架构概述
FQuant 采用计算与展示分离的架构,由两个核心部分组成:
FQuant.Server: 后端计算引擎。基于 Python,使用 Celery 执行定时的、耗时的数据获取、清洗、计算和分析任务。FQuantWeb2: 前端展示应用。基于 Nuxt.js,负责数据的可视化和用户交互。
两者之间完全解耦,通过静态 JSON 文件进行通信。
数据流
整个系统的数据流如下:
- 计算:
FQuant.Server中的 Celery 定时任务被触发,执行各种数据分析和计算。 - 生成: 任务执行完毕后,将结果生成为静态的
.json文件。这些文件主要有两种格式:- 图表配置: 直接由
pyecharts库生成的、ECharts 可直接渲染的图表配置对象。 - 原始数据: 结构化的原始数据,供前端进行表格渲染或其他自定义展示。
- 图表配置: 直接由
- 发布: 一个独立的 Nginx 服务器将这些生成的 JSON 文件作为静态资源发布。
- 访问:
FQuantWeb2前端应用通过标准的 HTTP 请求访问 Nginx 服务器上的 JSON 文件,获取数据并渲染页面。
2. 开发工作流
我们采用本地开发,服务器调试的工作模式。
- 本地编码: 在本地环境中完成新功能的代码编写。
- 同步代码: 将本地编写好的代码同步到测试服务器。
- Jupyter Notebook 调试:
- 这是最核心的调试步骤。在测试服务器上,打开 Jupyter Notebook。
- 在 Notebook 中,导入你新编写的函数或模块。
- 直接调用你的函数,传入测试参数,执行它。
- 检查函数的返回值或其生成的临时 JSON 文件,验证其输出是否完全符合预期。
- 反复修改代码并在此调试,直到功能正确无误。
- 集成到 Celery (可选):
- 只有当一个函数的功能在 Jupyter Notebook 中被完全验证通过后,才能考虑将其集成到自动化流程中。
- 如果该功能需要定时执行,再将其封装成一个 Celery 任务,添加到
TaskData.py或TaskMessage.py中,并配置好相应的执行时间。
核心理念: 始终将核心业务逻辑与任务调度(Celery)分离。先确保函数本身是可独立运行且正确的,再考虑如何去调用它。
3. 后端代码规范与实践
数据源
- 项目的主要数据来源是通达信 (TongDaXin),通过
pytdx库进行数据获取。 - 相关的数据获取逻辑主要封装在
FQData模块中。在进行常规功能开发时,您无需关心FQData的内部实现细节,可以将其视为一个可靠的数据提供方。
数据访问
- 标准方式: 在开发新功能时,获取项目内各种预计算数据(如日线、扩展数据、因子数据等)的标准方式是调用
FQuant.Server/FQMarket/FQMarket/FQUtil/ToolsGetData.py文件中提供的函数。 - 实现细节:
ToolsGetData.py中的函数封装了对 MongoDB 和 Redis 的查询逻辑,将原始的数据库查询转换为了易于调用的 Python 函数,返回 Pandas DataFrame 格式的数据。
图表生成
- 当新功能需要输出图表时,应使用
pyecharts库来生成 ECharts 的 JSON 配置。 - 最终输出的应是一个完整的、前端可直接使用的 JSON 文件。
临时文件
- 在开发和调试过程中,您可以将生成的 JSON 文件输出到任意临时目录,以便在 Jupyter Notebook 中进行检查和验证。
已排除的组件
- 位于
FQuant.Server/FQServer/Flask/server.py的 Flask 应用是一个临时的测试服务,不是项目的主服务,在开发中应忽略它。
核心配置文件
- 项目的大部分硬编码配置(如文件路径、金融参数、黑白名单、消息推送密钥等)都集中在
FQuant.Server/FQMarket/FQMarket/FQUtil/Parameter.py文件中。在进行新功能开发前,应首先查阅此文件以了解相关的全局变量和配置。
关键参数说明
GLOBALMAP.TODAY(): 这是项目中处理交易日切换的唯一标准函数。它的逻辑是:每日早上 8:00 会将返回的日期切换到新的交易日。所有需要根据交易日进行计算的功能都必须调用此函数来获取日期,以确保逻辑的统一性。TODAYEND()和DAY()是已废弃或未使用的函数,应避免使用。defaultGrade字典: 此字典中的值(如'连板': 56)不包含复杂的金融学意义。它们的主要用途是作为前端 ECharts 雷达图视觉展示的**“刻度”或“最大值”**,目的是让图表在视觉上更匀称美观。开发时无需深究其数值的业务逻辑。n1name_list列表: 这是一个用于简化输出的常量列表,主要在生成多个相关的 JSON 文件时,作为文件名或字段名使用,以避免在代码中重复定义字符串。微信推送函数: 文件中多个
send*2Wechat函数使用了不同的密钥,是为了实现信息分发,将不同主题的消息推送到企业微信中不同的应用或群组,属于业务层面的信息隔离。
4. 策略开发范式
项目中添加新策略遵循一套标准的编写模式,主要涉及 FQuant.Server/FQMarket/FQMarket/StrategyPools/ 目录。
开发一个新策略(例如 mynewstrategy)的步骤如下:
创建策略文件: 在
StrategyPools目录下创建新文件sp_mynewstrategy.py。编写策略逻辑函数: 在文件中实现策略的核心筛选逻辑。
- 函数定义: 创建一个函数(如
def sp_mynewstrategy(date, lists=None):),它接收日期和可选的股票列表作为输入。 - 数据获取: 函数内部通过
DATABASE.collection.find()从stock_data_factor或stock_data_extent等集合获取数据。 - 筛选逻辑: 使用 Pandas 对获取的 DataFrame 进行条件过滤。
- 函数返回: 函数最终返回一个符合策略条件的股票代码列表 (
code.tolist())。
- 函数定义: 创建一个函数(如
编写数据保存函数: 在同一文件中,实现将策略结果每日保存到数据库的调度函数。
- 函数定义: 创建一个名为
saveStrategyPools_mynewstrategy(end_date=None, renew=0)的函数。 - 核心功能: 此函数负责调度上面的策略逻辑函数,并管理数据的增量更新。
- 实现模式:
- 定义一个唯一的策略名称字符串,如
code = 'mynewstrategy'。 - 查询
strategy_pools数据库,找到该策略最后一次保存的日期。 - 从该日期的下一天开始,循环调用
sp_mynewstrategy(date)函数,直到指定的end_date。 - 将每日返回的结果(股票列表、策略名、日期、数量)作为一个新文档插入到
strategy_pools集合中。
- 定义一个唯一的策略名称字符串,如
- 函数定义: 创建一个名为
通过遵循这种“策略逻辑”与“保存调度”分离的“两函数”模式,可以确保新策略与现有框架的兼容性,并能方便地通过 Jupyter Notebook 调用 saveStrategyPools_* 函数进行回测和数据生成。
5. 核心函数解析
关于项目中一些核心函数的详细解析,请参考独立的文档页面: