深色模式
开发指南
面向: 后续开发者。覆盖环境搭建、代码结构、如何加功能/品种/策略、测试、发布。 版本: 1.0.0 | 配套: 02-技术架构
一、开发环境搭建
| 项 | 要求 |
|---|---|
| .NET SDK | 9.0+(.slnx 格式) |
| IDE | VS 2022 17.10+ / VS2026 / Rider |
| Python | 3.10+(采集脚本,打包 exe 用 build_venv) |
| PostgreSQL | 15+(可连开发库或本地便携) |
| Git | 已配置(Gitee: wenzhou-boyi-network/boyangtong) |
首次克隆与运行
bash
git clone https://gitee.com/wenzhou-boyi-network/boyangtong.git
cd boyangtong
# 配置数据库(src/Futures.Desktop/config/appsettings.json,可复制 example)
# Debug 运行(无任何商业化校验限制)
dotnet build Futures.Desktop.slnx
dotnet run --project src/Futures.Desktop二、代码结构
src/
├─ Futures.Core/ # 核心:模型、数据访问、服务(无 UI)
│ ├─ Models/ # 实体模型
│ ├─ Data/ # 数据服务(Npgsql + Dapper)
│ ├─ Services/ # 业务服务(License/Update/ErrorReport)
│ ├─ Helpers/ # 工具类(OptionMarginCalculator 等)
│ ├─ Logging/ # 日志
│ └─ Tushare/ # Tushare API 客户端
├─ Futures.Desktop/ # 主程序:Shell + 导航 + Views/ViewModels
│ ├─ Views/ # XAML 视图
│ ├─ ViewModels/ # Prism ViewModel
│ └─ Services/ # 托盘通知等
├─ Futures.Module.Futures/ # 期货模块
├─ Futures.Module.Options/ # 期权模块
├─ Futures.Module.Shared/ # 共享 UI/本地化
└─ Futures.Module.Stock/ # 股票模块
script/ # Python 采集/打包脚本
tools/ # 厂商工具(LicenseGenerator/ReleasePublisher/server)
tests/Futures.Core.Tests/ # xUnit 单元测试(335 用例)三、开发规范
分层规则
- UI 层(Futures.Desktop/Modules)只做展示与交互,业务逻辑在 Core
- Core 层不依赖 UI,可测试
- 新功能优先放 Core 服务 + 接口,UI 通过 DI 注入
命名
- 项目名/命名空间保持
Futures.*(技术代号),产品显示名用「博衍通」 - 服务用
I XxxService接口 +XxxService实现,DI 注册单例 - 表名小写下划线,列名小写
日志
- 用
LogManager.CreateLogger("类名")(统一走文件 + system_logs) - 不用
Console.WriteLine
四、如何加一个新功能页面
- 创建 ViewModel + View
src/Futures.Desktop/ViewModels/XxxViewModel.cs(继承 BindableBase)src/Futures.Desktop/Views/XxxView.xaml+.xaml.cs
- 注册导航
App.xaml.cs:RegisterForNavigation<XxxView, XxxViewModel>("Xxx")ShellWindowViewModel.csViewMap:["Xxx"] = typeof(XxxView)
- 加菜单
ShellWindow.xaml系统/对应分组加 Button(Tag="Xxx")UIStrings.resx+UIStrings.cs加 Menu.Xxx / Tab.Xxx
- 业务逻辑:在 Futures.Core 加服务,ViewModel 注入
- 测试:Core 逻辑写 xUnit 测试
五、如何加一个品种/合约
- 品种自动发现: 采集脚本
tq_futures_pg.py从天勤自动获取,一般无需手动 - 品种中文名: 「品种名称管理」页面或 product_chinese_names 表
- 品种参数(乘数/保证金/手续费): 「品种参数」页面 → product_params 表
- 主力映射: 运行
tq_mapping_pg→ fut_mappings - 交易时段: product_trading_hours 表(无记录=按全局规则)
六、如何加一个期权策略
45 个策略在 OptionsStrategy.All(src/Futures.Core/Models/OptionsStrategy.cs)。 加新策略(46 号):
- 在
CreateAll()加策略定义(名称/构造/风险/多空权重/复杂度) - 在
OptionsStrategyMatcher.GenerateExecutionPlan加case 46:(行权价计算 + 腿文本 + 止盈止损) - 在
OptionsLegResolver确认腿文本可解析(或加特殊腿处理) - 回测模板:
OptionsBacktestService自动从 All 生成
七、如何加一个交易信号源
- 在 Core 写数据源服务(如新 API),返回模型
- 在对应研判服务(如 FuturesStrategyService)接入评分
- 在
TradingOpportunityService.ScoreOpportunityAsync加评分维度 - 信号落库: signal_history(可加 Source 标识)
八、测试
bash
# 全部测试
dotnet test tests/Futures.Core.Tests/Futures.Core.Tests.csproj
# 单个测试
dotnet test --filter "FullyQualifiedName~AutoTradeExecution"约定: Core 业务逻辑必须配测试;测试不连真实数据库(用内存/桩)。
九、数据库变更规范
- 表结构变更:在对应服务
EnsureTableAsync加ADD COLUMN IF NOT EXISTS迁移(幂等) - 新表:
init_timescaledb.py(采集表)或服务自建(业务表) - 不改表结构只加数据:直接脚本/服务写入
十、提交与发布
bash
git add .
git commit -m "feat: 新增X功能"
git push发布: 07-发布流程(用发布工具或 build_release.ps1)
十一、常见开发任务速查
| 任务 | 位置 |
|---|---|
| 改窗口标题/产品名 | ShellWindowViewModel / Directory.Build.props |
| 改菜单文案 | UIStrings.resx |
| 加定时任务 | SchedulerConfigService 默认任务列表 |
| 加依赖 DB 的后台服务 | 注册在模块,启动在 App.StartBackgroundDbServices()(见第十二节) |
| 改自动执行逻辑 | AutoTradeExecutionService |
| 改交易时间判定 | TradingTimeHelper / ProductSessionService |
| 改行情采集 | script/*.py(改后需重新 build_exe) |
| 改数据库备份 | DatabaseMaintenanceService |
⚠️ WPF 下拉绑定坑(踩过):ComboBox 的
ItemsSource若绑定ValueTuple列表(如(string Value, string Label)),DisplayMemberPath="Label"会因元组无此公开属性而下拉空白。必须用带Value/Label属性的普通类(见SettingsViewModel.QuoteModeOption)。
十二、新增数据库功能规范(首启时序)
背景:主窗口(ShellWindow/Dashboard 等)在首启向导之前显示,而向导完成前数据库尚未创建/配置。 若在向导完成前执行 DB 查询或启动依赖 DB 的后台服务,会反复报
Failed to connect to 127.0.0.1:5432(SocketException 10061)并刷屏。 2026-08 曾三度踩坑(模块OnInitialized自启动、ViewModel 构造查库、窗口Loaded查库),以下规范务必遵守。
三条铁律
依赖 DB 的后台服务 → 统一在
App.StartBackgroundDbServices()启动- ❌ 禁止在
Module.OnInitialized、服务构造函数、属性初始化中自启动 - ✅ 模块只
RegisterTypes注册;启动逻辑放StartBackgroundDbServices()(向导/激活弹窗关闭后执行) - 参考:
StockModule(数据采集调度器 + 股价预警已按此迁移) - 注意:
IModule.OnInitialized是接口必需成员,移除服务后要留空实现
- ❌ 禁止在
早于向导显示的 DB 查询 → 等待
DatabaseReadiness.WaitAsync()- 主窗口在向导前显示,其启动期查询(Dashboard 刷新、今日提醒等)必须等待
await DatabaseReadiness.WaitAsync();(已就绪时零开销,直接返回)- 用户手动打开(点导航才创建)的页面无需等待(届时向导已完成)
构造函数不做 DB 调用
- 构造查库会在容器解析的任意时刻触发,且必然早于向导完成
自动防护基建(新功能推荐用法)
| 场景 | 用法 |
|---|---|
| 通过连接工厂建连 | await _factory.CreateConnectionReadyAsync()(自动等待就绪后返回连接) |
| 一次性 DB 操作 | await DatabaseReadiness.ExecuteAsync(() => QueryAsync(...)) |
| 纯等待门控 | await DatabaseReadiness.WaitAsync() |
DatabaseReadiness(src/Futures.Core/Data/DatabaseReadiness.cs):静态门控。MarkReady()由App.StartBackgroundDbServices()调用(幂等,仅首次生效);置位后WaitAsync()立即返回。IDbConnectionFactory.CreateConnectionReadyAsync():就绪后返回连接。新代码建连用它替代CreateConnection()即自动获得防护,无需单独记忆等待。
接入检查清单(新增 DB 功能时逐项打勾)
- [ ] 后台定时服务 → 注册在模块,启动在
StartBackgroundDbServices() - [ ] Shell 初始显示的页面查询 → 已
await DatabaseReadiness.WaitAsync()或使用CreateConnectionReadyAsync() - [ ] 构造函数无 DB 调用
- [ ] 编译 + 测试通过;首次安装场景(删库重装)冒烟无 Npgsql 连接报错
与发布流程的关系
新增 DB 功能不需要修改发布流程(build_release.ps1/发布工具为通用全量覆盖), 只需遵守上述时序规范;发布后到 publish\futures-1.0.0 做一次首次安装冒烟验证。