简介这份资源是从策略为王论坛通过SVN获取的开源交易软件源代码面向对股票行情系统与交易客户端实现感兴趣的开发者尤其是希望研究行情接收、K线绘制与实时走势展示等核心模块的中高级C程序员。压缩包共1299个文件约10.54MB以215个cpp源文件与219个h头文件为主体配合11个vcproj工程文件、2个sln解决方案及lib、def等库配置另有bmp、ico等界面资源与svn-base版本文件工程结构完整已用VS2008编译通过并可正常运行。资源涵盖行情、K线、实时走势图等常见交易软件功能适合作为学习交易终端架构与模块划分的参考样本。目前已有1143人学习下载。作者同时希望借此寻求行情服务器搭建与推送程序编写的思路读者可从中了解客户端与行情服务端的对应接收关系并据此扩展自己的行情推送方案。1. 策略为王一套开源交易软件源码能跑出什么很多人第一次接触量化交易是从一段“回测年化 300%”的截图开始的结果把策略挂上模拟盘第二天就发现信号根本没触发——因为行情源断了、时间戳对不上、下单接口返回的字段名和文档里写的完全不一样。这套“策略为王”开源交易软件源码解决的正是这个落差它把股票行情接入、策略信号计算、模拟撮合、持仓与资金管理这几块拼成一个能本地跑起来的完整闭环而不是丢给你一个孤零零的均线交叉函数。适合两类人想搞懂交易系统内部长什么样的开发者以及手里有策略想法、需要一个可改可调的工程骨架来验证的人。下面按“它是什么 → 怎么跑起来 → 核心代码怎么读 → 怎么改成自己的”这条线拆。2. 工程结构与技术选型行情、策略、撮合三层怎么分拿到一份交易软件源码最怕的是一上来就翻策略文件结果被行情解析和账户结算的代码绕晕。正确的顺序是先看目录搞清楚“数据从哪进、信号在哪算、订单往哪走”。这套源码的分层比较朴素但边界清晰适合二次开发时按层替换。2.1 目录骨架与各层职责典型的目录结构大致是这样不同分支文件名可能略有差异但职责划分一致strategy-king/ ├── config/ # 配置文件行情源、标的池、策略参数 │ ├── market.yaml │ └── strategy.yaml ├── data/ # 本地行情缓存与历史数据 │ ├── daily/ # 日线 CSV │ └── minute/ # 分钟线 ├── core/ │ ├── market_feed.py # 行情接入与标准化 │ ├── strategy_base.py # 策略基类定义 on_bar/on_tick │ ├── engine.py # 主循环拉数据 → 调策略 → 撮合 │ ├── broker_sim.py # 模拟撮合与成交回报 │ └── portfolio.py # 持仓、资金、盈亏计算 ├── strategies/ │ ├── ma_cross.py # 示例双均线 │ └── breakout.py # 示例区间突破 ├── backtest/ │ └── runner.py # 回测入口 └── live/ └── paper_trade.py # 模拟盘入口这个结构里core/是真正需要读透的部分strategies/只是插件。行情层负责把不同来源本地 CSV、实时推送统一成同一种 bar 结构策略层只关心“给我一根 bar我返回买/卖/不动”撮合层负责把信号变成带价格的成交记录并扣手续费和滑点。三层之间通过 engine 串起来任何一层都可以单独替换——比如你不想用它的模拟撮合接自己的柜台接口只需要改broker_sim.py的对外方法签名。2.2 技术栈与依赖取舍从源码的 import 和配置文件看这套东西走的是“轻依赖”路线Python 3.8 为主数据处理用 pandas 和 numpy配置文件用 YAML日志用标准库 logging没有引入重型回测框架。这个选择对二次开发很友好——你不需要先学一套框架的 DSL直接读 Python 就能改逻辑。常见做法是再补两个依赖pyyaml读配置matplotlib画资金曲线。如果要做实时行情源码里预留了行情适配器的抽象方法实际接入时一般用 websocket 客户端库但具体用哪个库取决于你的数据源源码本身不绑定。提示不要急着装一堆量化库。先把requirements.txt里列出的依赖装齐跑通示例策略再按需加。依赖越多版本冲突越难排查。2.3 配置驱动的设计为什么参数不写死在代码里这套源码一个值得学的点是标的池、均线周期、手续费率、初始资金全部放在config/strategy.yaml里策略代码只读配置。这样做的好处是同一份策略代码可以跑多组参数回测时批量扫参不用改代码。# config/strategy.yaml strategy: name: ma_cross params: fast_period: 5 # 快线周期 slow_period: 20 # 慢线周期 position_pct: 0.3 # 单次建仓占总资金比例 market: symbols: [600000, 000001] # 标的池 bar_interval: 1d broker: commission_rate: 0.0003 # 手续费率 slippage: 0.001 # 滑点 account: initial_cash: 100000参数说明fast_period和slow_period决定信号灵敏度周期越短信号越频繁、手续费吃掉越多position_pct控制单标的最大仓位防止满仓单票slippage是回测里最容易被忽略的一项设成 0 的回测结果基本不可信。改配置比改代码安全这也是后面做参数扫描的基础。3. 跑起来从本地回测到模拟盘的完整步骤源码能不能跑通决定了后面所有分析有没有意义。这一章按“装环境 → 准备数据 → 跑回测 → 看结果 → 切模拟盘”的顺序走每一步都给可复制的命令和失败时的排查方向。3.1 环境准备与依赖安装先确认 Python 版本再建虚拟环境避免污染系统环境# 确认版本源码要求 3.8 以上 python3 --version # 建虚拟环境并激活 python3 -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate # 安装依赖 pip install -r requirements.txt如果requirements.txt里没有锁版本pandas 和 numpy 可能装到不兼容的新版本表现为ImportError或运行时报 dtype 相关错误。血泪经验是先按源码作者给的版本装跑通后再考虑升级。装完后用pip list核对 pandas、numpy、pyyaml 三个关键包是否在位。3.2 准备行情数据CSV 格式与字段约定回测入口默认读data/daily/下的 CSV。字段约定通常是日期、开高低收、成交量列名要和market_feed.py里的解析逻辑对上date,open,high,low,close,volume 2023-01-03,10.20,10.55,10.10,10.48,1200000 2023-01-04,10.50,10.62,10.35,10.40,980000注意两点日期格式要和解析代码里的pd.to_datetime兼容别混用2023/01/03和2023-01-03数据要按日期升序排列乱序会导致均线计算错位而且这种错误不会报异常只会让回测结果莫名其妙。如果手上没有数据源码一般带一小段示例数据先用示例数据验证流程再换自己的。3.3 跑通第一个回测回测入口在backtest/runner.py典型调用方式python -m backtest.runner --config config/strategy.yaml --strategy ma_cross执行链路是读配置 → 加载标的池行情 → 逐 bar 推进 → 策略产生信号 → 撮合层按当日收盘价加滑点成交 → 更新持仓和资金 → 结束后输出绩效。跑完一般会在控制台打印总收益、最大回撤、交易次数并在output/下生成资金曲线图。如果报“找不到标的”检查symbols里的代码和data/daily/下的文件名是否一致如果交易次数为 0先看均线周期是不是比数据长度还大——20 日慢线配 15 天数据永远等不到金叉。3.4 从回测切到模拟盘模拟盘入口是live/paper_trade.py它复用同一套策略和撮合逻辑区别在于行情来源从本地 CSV 换成实时推送适配器撮合从“按历史 bar 成交”换成“按最新价成交”。python -m live.paper_trade --config config/strategy.yaml --strategy ma_cross切换时最容易翻车的是时间对齐回测里一根 bar 是完整的一天模拟盘里推送是逐笔或逐分钟的策略的on_bar如果假设“每根 bar 都是收盘价”在盘中就会用未完成的价格算信号。常见做法是在策略基类里区分on_bar已完结 bar和on_tick实时价模拟盘只对完结 bar 触发交易信号。4. 核心模块拆解一个信号从产生到成交的链路这一章是整篇的重点。读交易系统源码抓住“信号怎么来、订单怎么成、持仓怎么算”三条线基本就掌握了 80%。下面按 engine 主循环、策略基类、模拟撮合、持仓管理四块逐段看。4.1 engine 主循环驱动一切的那几十行engine 是整个系统的心脏逻辑不复杂但顺序很关键# core/engine.py节选示意 class Engine: def __init__(self, feed, strategy, broker, portfolio): self.feed feed # 行情源 self.strategy strategy # 策略实例 self.broker broker # 撮合器 self.portfolio portfolio # 账户 def run(self): for bar in self.feed.stream(): # 逐 bar 推进 self.portfolio.mark_to_market(bar) # 先按最新价更新市值 signal self.strategy.on_bar(bar) # 策略给信号 if signal: order self.broker.execute(signal, bar) if order.filled: self.portfolio.apply_fill(order) # 成交后更新持仓 return self.portfolio.summary()逻辑说明每个 bar 先做市值更新再让策略决策最后撮合和记账。这个顺序不能乱——如果先撮合再更新市值持仓的浮动盈亏会用上一根 bar 的价格回测曲线会出现一天延迟。参数上feed.stream()是生成器内存里不会一次性加载全部历史长周期回测也不会爆内存。4.2 策略基类只暴露 on_bar 的极简接口策略层刻意做薄基类只定义生命周期和持仓查询具体逻辑留给子类# core/strategy_base.py节选 class StrategyBase: def __init__(self, params, portfolio): self.params params self.portfolio portfolio def on_bar(self, bar): 子类实现返回 Signal 或 None raise NotImplementedError def position_of(self, symbol): return self.portfolio.get_position(symbol)双均线示例的实现思路是维护一个收盘价队列快慢线交叉时返回买或卖信号# strategies/ma_cross.py节选 class MaCross(StrategyBase): def on_bar(self, bar): self.closes.append(bar.close) fast self.params[fast_period] slow self.params[slow_period] if len(self.closes) slow 1: return None ma_fast sum(self.closes[-fast:]) / fast ma_slow sum(self.closes[-slow:]) / slow prev_fast sum(self.closes[-fast-1:-1]) / fast prev_slow sum(self.closes[-slow-1:-1]) / slow if prev_fast prev_slow and ma_fast ma_slow: return Signal(bar.symbol, BUY, self.params[position_pct]) if prev_fast prev_slow and ma_fast ma_slow: return Signal(bar.symbol, SELL, 1.0) return None这里用“前一根的快慢线关系”和“当前根的关系”做交叉判断而不是简单比较大小否则会在均线粘合时反复触发。position_pct来自配置卖出时传 1.0 表示清掉该标的全部持仓。这个写法牺牲了一点性能每根 bar 重新求和但可读性高二次开发时改逻辑不容易出错。4.3 模拟撮合滑点和手续费怎么扣撮合器负责把信号变成成交价这是回测可信度的关键# core/broker_sim.py节选 class SimBroker: def __init__(self, commission_rate, slippage): self.commission_rate commission_rate self.slippage slippage def execute(self, signal, bar): if signal.side BUY: price bar.close * (1 self.slippage) # 买入滑点向上 else: price bar.close * (1 - self.slippage) # 卖出滑点向下 qty self._calc_qty(signal, price) fee price * qty * self.commission_rate return Order(signal.symbol, signal.side, price, qty, fee, filledTrue)参数说明slippage买入加价、卖出减价方向不能反反了等于回测里白送钱commission_rate按成交额比例扣A 股还要考虑卖出印花税源码里如果没单独列二次开发时建议在fee里补上。_calc_qty一般按可用资金乘仓位比例再除以价格并向下取整到 100 股这个细节不做回测会出现零碎股和真实账户对不上。4.4 持仓与资金盈亏计算的三个易错点portfolio.py管两件事持仓数量和可用资金。易错点集中在成本价更新、浮动盈亏和已实现盈亏的区分上。常见做法是持仓对象记录avg_cost、qty买入时按加权平均更新成本卖出时用“卖出价减成本价”算已实现盈亏剩余持仓成本不变。市值更新用最新收盘价乘持仓数量和可用资金相加得到总资产。这三个数如果混在一起算资金曲线会出现跳变排查起来非常费劲。5. 避坑与排查回测和模拟盘里最容易翻车的几件事交易系统有个特点错了不一定报错但结果会悄悄失真。下面几条是这类源码在落地时高频出现的问题按“现象 → 原因 → 解决”记下来能省不少时间。5.1 回测收益好得离谱现象回测年化高得不像话交易次数却很少。原因通常是未来函数——策略在当根 bar 用了收盘价做决策又按同一个收盘价成交等于提前知道了结果。解决信号在 bar 收盘后产生成交价用下一根 bar 的开盘价或者至少加滑点。检查方法是把slippage调大如果收益断崖式下跌说明原来的收益主要来自成交价假设。5.2 模拟盘信号和回测对不上现象同一策略回测有 30 笔交易模拟盘一周只触发 2 次。原因多半是 bar 周期不一致回测用日线模拟盘推送的是分钟线策略按分钟算均线周期参数含义完全变了。解决在配置里显式声明bar_interval策略初始化时校验数据周期不一致直接报错退出别让它静默跑。5.3 持仓数量出现小数或零碎股现象持仓显示 1234.56 股。原因下单数量按资金除以价格直接算没做整手取整。解决在撮合器的_calc_qty里向下取整到 100 的倍数并检查取整后数量是否大于 0否则返回未成交避免产生无意义的零股订单。5.4 换标的后策略完全不交易现象改了symbols配置程序正常退出但零交易。原因新标的的 CSV 文件名、日期格式或列名和解析逻辑不匹配行情被静默跳过。解决在行情加载处加一条日志打印每个标的实际加载到的 bar 数量数量为 0 就告警。这个日志我一般强制保留排查数据问题全靠它。5.5 长时间运行后内存持续上涨现象模拟盘跑一天内存翻倍。原因策略里用列表无限追加收盘价或者日志对象持有大量 bar 引用。解决收盘价队列只保留slow_period 1长度超出就弹出日志只记录信号和成交不记录每根 bar 的完整对象。6. 二次开发把示例策略换成自己的信号逻辑源码的价值不在示例策略本身而在它给的骨架。改造时我建议按“先换信号、再换撮合、最后换行情”的顺序每换一层都跑一遍回测对比确认资金曲线变化符合预期而不是一次性全改完再调试。6.1 新增策略的标准动作新增一个策略只需要三步在strategies/下建文件、继承StrategyBase、实现on_bar返回Signal。关键是不要在策略里直接操作账户或下单所有交易意图都通过返回值表达这样回测和模拟盘才能复用同一份代码。下面是一个区间突破策略的骨架# strategies/breakout.py节选 class Breakout(StrategyBase): def __init__(self, params, portfolio): super().__init__(params, portfolio) self.highs, self.lows [], [] def on_bar(self, bar): n self.params[lookback] self.highs.append(bar.high) self.lows.append(bar.low) if len(self.highs) n: return None upper max(self.highs[-n-1:-1]) # 不含当前 bar避免未来函数 lower min(self.lows[-n-1:-1]) if bar.close upper: return Signal(bar.symbol, BUY, self.params[position_pct]) if bar.close lower and self.position_of(bar.symbol) 0: return Signal(bar.symbol, SELL, 1.0) return None注意upper和lower的切片排除了当前 bar这是防未来函数的关键一行。参数lookback控制突破窗口窗口越长信号越少但越稳短窗口在震荡市里会频繁假突破手续费能把利润吃光。6.2 参数扫描与结果对比配置驱动的好处在这里体现写个小脚本循环改 YAML 里的参数批量跑回测把结果汇总成表。对比时至少看三个指标——总收益、最大回撤、交易次数。只看收益会选出过拟合的参数交易次数过少的参数组也要警惕样本太少统计上不可信。参数组合总收益最大回撤交易次数fast5, slow2018.2%9.5%42fast10, slow3012.7%6.1%21fast3, slow1025.4%17.8%96这张表是示意重点在看法收益最高的那组回撤也最大、交易最频繁实盘里手续费和滑点会进一步侵蚀收益。我一般优先选回撤可控、交易次数适中的组合而不是收益最高的那组。6.3 接自己的行情源和柜台行情适配器只要实现stream()生成器产出带symbol/open/high/low/close/volume/timestamp的 bar 对象即可柜台接口则替换SimBroker.execute把Order转成真实下单请求并把成交回报回填到portfolio。改造时保留模拟撮合作为对照实盘和模拟同时跑一段时间对比成交价和持仓差异超过阈值就停下来查——这个对照机制是我踩过坑之后养成的习惯从那以后每次接新接口都强制走一遍模拟与实盘对账再小的改动也不跳过。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?