简介:这份资源是策略为王论坛上通过SVN获取的开源交易软件源代码,面向对股票行情系统与交易客户端实现感兴趣的开发者,尤其适合具备C++基础、希望研究行情展示与交易功能完整链路的中高级学习者。压缩包共1299个文件,约10.54MB,以215个cpp源文件与219个h头文件为核心,配合11个vcproj工程文件、2个sln解决方案及lib、def等编译配置,另有bmp、ico等界面资源与少量htm、txt说明文档,已用VS2008编译通过并可正常运行。软件基本覆盖行情、K线、实时走势图等常见交易功能,读者可据此梳理客户端模块划分、界面绘制与数据接收逻辑,并对照工程结构理解各组件协作方式。目前已有1143人学习下载,适合作为研究行情推送与交易软件架构的参考底本。
1. 策略为王:一套开源交易软件源码能帮你把行情和策略跑通到什么程度
很多人第一次接触量化交易,都是从“我想验证一个想法”开始的。比如你盯上了某只股票的均线金叉,想回测过去三年的表现,结果发现要么得买商业软件,要么得自己从零搭行情接口、撮合逻辑、账户管理。这套“策略为王”开源交易软件源码,解决的正是这个尴尬:它把股票行情接入、策略编写、回测执行、交易信号输出这几块拼在一起,给你一个能跑起来的骨架。适合两类人——想学交易系统内部结构的开发者,以及手里有策略想法、缺一套可改可调框架的量化爱好者。下面我按“能干什么、怎么跑、怎么改”的顺序,把这套源码拆开讲。
2. 技术栈与目录结构:先看清这套交易软件由哪些模块拼起来
拿到一份交易软件源码,最怕上来就找 main 函数。我一般先看目录,因为目录结构基本暴露了作者的分层思路。这套项目的组织方式比较典型:行情、策略、交易、数据四条线分开,中间靠事件或接口串起来。理解了这个分层,后面改代码才不会牵一发动全身。
2.1 技术选型:为什么是 Python + 轻量数据库
从源码的依赖文件看,这套系统的主语言是 Python,版本在 3.8 以上。选 Python 做交易软件不稀奇,原因是策略表达快、生态里行情和数值计算库多。核心依赖大致分三类:行情获取用 requests 或现成的行情 SDK,数据处理用 pandas 和 numpy,存储用 SQLite 或 MySQL。前端部分如果是桌面端,常见做法是 PyQt 或 Tkinter;如果是 Web 端,则是 Flask 或 FastAPI 加一个简单页面。
这里有个血泪经验:交易软件对时间精度敏感,Python 的 datetime 在跨时区、跨市场时容易翻车。源码里如果统一用带时区的时间戳存储,说明作者踩过坑;如果只存字符串,你二次开发时得自己补上。数据库选 SQLite 的好处是零配置、单文件,适合个人回测;但如果你要跑多策略并发或高频数据,SQLite 的写锁会成为瓶颈,常见做法是换成 MySQL 或时序库。
| 模块 | 常见技术 | 作用 |
|---|---|---|
| 行情接入 | requests / 行情 SDK | 拉取股票实时与历史 K 线 |
| 策略引擎 | 纯 Python 类 | 加载策略、逐 bar 驱动 |
| 回测撮合 | pandas | 模拟成交、计算收益 |
| 数据存储 | SQLite / MySQL | 存行情、订单、持仓 |
| 界面展示 | PyQt / Flask | 看行情、看回测曲线 |
2.2 目录结构:四个核心目录各管一摊
源码的顶层目录一般长这样,我按职责拆开说:
project/ ├── config/ # 配置文件:行情源、数据库、策略参数 ├── data/ # 行情数据与本地缓存 ├── strategy/ # 策略文件,一个策略一个 py ├── engine/ # 回测引擎、撮合、账户 ├── trader/ # 实盘/模拟交易接口 ├── ui/ # 界面或 Web 路由 └── main.py # 入口,串起行情→策略→交易config/里通常有一个settings.py或config.yaml,写着行情接口地址、数据库连接串、初始资金。strategy/是二次开发的主战场,每个策略继承一个基类,实现on_bar或on_tick方法。engine/是黑匣子,负责按时间顺序喂数据、调策略、记成交。trader/区分模拟和实盘,模拟盘直接走引擎,实盘则对接券商接口。ui/看项目形态,桌面端是窗口类,Web 端是路由函数。
提示:先读
main.py和engine/里的回测主循环,这两处决定了数据怎么流动。策略文件反而可以最后看,因为它是插拔式的。
2.3 数据流:一根 K 线从接口到策略的路径
这套系统的数据流不复杂,但每一步都有可改的点。行情模块拉取数据后,先落库或进内存缓存,然后由引擎按时间排序,逐根 K 线推给策略。策略产生信号后,交给撮合模块判断能否成交,成交结果更新账户持仓和资金,最后写回数据库并推给界面。
# engine/backtest.py 简化示意 def run(self, strategy, bars): for bar in bars: # 按时间顺序逐根驱动 self.account.update_price(bar) # 先按最新价更新持仓市值 signal = strategy.on_bar(bar) # 策略返回买卖信号 if signal: order = self.risk.check(signal, self.account) # 风控过滤 if order: trade = self.matcher.match(order, bar) # 模拟撮合 self.account.apply(trade) # 更新账户 return self.account.summary() # 输出收益、回撤等指标这段循环里,bars是排好序的 K 线列表,strategy.on_bar是你写的策略入口,risk.check做仓位和资金校验,matcher.match决定成交价和成交量。参数上,撮合价常见用当根收盘价或下一根开盘价,前者偏乐观,后者更接近实盘。改策略时只需要动strategy/下的文件,引擎和账户不用碰,这是这套结构最舒服的地方。
3. 跑起来:从装依赖到看到第一张回测曲线
源码能不能跑通,决定了你对它的信任度。这一章按实际顺序走:环境准备、配置行情源、导入数据、跑回测、看结果。每一步我都标出容易卡住的地方,新手照着做基本能出图。
3.1 环境准备与依赖安装
先确认 Python 版本,源码里如果有requirements.txt,直接装;没有的话按 import 反推。常见命令如下:
# 建议用虚拟环境,避免污染全局 python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate # 安装核心依赖,版本按源码实际 import 调整 pip install pandas numpy requests sqlalchemy pip install PyQt5 # 如果是桌面端界面装完之后先跑python main.py --help或直接python main.py,看报错缺什么。常见缺的是行情 SDK 或数据库驱动,按提示补装即可。注意不要一上来就装最新版 pandas,交易软件里有些旧写法在新版 pandas 上会报FutureWarning甚至行为变化,源码如果锁了版本就按锁的来。
3.2 配置行情源与数据库
打开config/settings.py,重点看三项:行情接口地址、数据库路径、初始资金。行情源如果是公开接口,通常需要填一个 token 或直接可用;如果是本地 CSV,则指定数据目录。数据库默认 SQLite 的话,路径写相对路径即可,首次运行会自动建表。
# config/settings.py 示例 MARKET_API = "https://example-quote-api.local/kline" # 行情接口 DB_URL = "sqlite:///data/trade.db" # 本地库 INIT_CASH = 1000000 # 初始资金 COMMISSION = 0.0003 # 佣金费率 SLIPPAGE = 0.001 # 滑点INIT_CASH影响回测收益率的分母,改它不会改变策略逻辑,但会让绝对收益数字变化。COMMISSION和SLIPPAGE是回测真实度的关键,很多人回测曲线漂亮、实盘拉胯,就是这两项设成 0 了。我一般把佣金设到万三、滑点设到千一,先看策略还成不成立。
3.3 导入历史数据并跑通回测
数据导入有两种方式:接口拉取或本地文件导入。源码里一般有data/loader.py之类的脚本。以 CSV 导入为例:
# data/loader.py 简化示意 import pandas as pd from sqlalchemy import create_engine df = pd.read_csv("data/600000.csv") # 列:date, open, high, low, close, volume df["date"] = pd.to_datetime(df["date"]) # 统一时间类型 engine = create_engine("sqlite:///data/trade.db") df.to_sql("kline", engine, if_exists="append", index=False) print(f"导入 {len(df)} 条 K 线")导入后跑回测,命令通常是python main.py --strategy ma_cross --start 2022-01-01 --end 2024-12-31。参数里--strategy对应strategy/下的文件名,--start和--end控制回测区间。跑完看输出目录里的收益曲线图或控制台指标,重点看年化收益、最大回撤、胜率、交易次数。交易次数太少说明信号太稀疏,太多说明手续费吃掉了利润。
注意:如果回测结果全是 0 或没有交易,先查数据日期范围和策略参数是否匹配,再查撮合模块是否因为资金不足拒绝了所有订单。
4. 核心模块拆解:策略基类、撮合引擎与账户管理怎么写
能跑通只是第一步,真正决定这套源码能不能为你所用,是看懂三个核心模块:策略怎么被加载和驱动、订单怎么被撮合、账户怎么记账。这三块改明白了,你就能把自己的想法塞进去,而不是被框架牵着走。
4.1 策略基类:一个 on_bar 方法就是全部入口
源码里的策略通常继承一个BaseStrategy,必须实现on_bar,可选实现on_init和on_order。基类负责持有账户引用、记录信号、提供下单方法。
# strategy/base.py 简化示意 class BaseStrategy: def __init__(self, context): self.ctx = context # 上下文:账户、数据、参数 self.params = context.params def on_init(self): pass # 初始化指标、订阅数据 def on_bar(self, bar): raise NotImplementedError # 子类必须实现 def buy(self, price, volume): return self.ctx.order("buy", price, volume) def sell(self, price, volume): return self.ctx.order("sell", price, volume)context是策略和引擎之间的桥梁,里面挂着账户、当前持仓、历史数据窗口。buy和sell不直接成交,只是把订单丢给引擎,这样回测和实盘可以共用同一套策略代码。参数通过context.params传入,常见做法是从配置文件或命令行读取,避免把参数写死在策略里。
一个均线策略大概长这样:
# strategy/ma_cross.py class MaCross(BaseStrategy): def on_init(self): self.fast = self.params.get("fast", 5) self.slow = self.params.get("slow", 20) def on_bar(self, bar): closes = self.ctx.history("close", self.slow) # 取最近 N 根收盘价 if len(closes) < self.slow: return fast_ma = sum(closes[-self.fast:]) / self.fast slow_ma = sum(closes) / self.slow if fast_ma > slow_ma and not self.ctx.position: self.buy(bar.close, self.ctx.max_volume()) elif fast_ma < slow_ma and self.ctx.position: self.sell(bar.close, self.ctx.position)history取历史窗口,position是当前持仓,max_volume按可用资金算最大可买量。这段代码里没有考虑滑点和手续费,因为那些在引擎层统一处理,策略只管信号。这种职责分离是这套源码值得学的地方。
4.2 撮合引擎:成交价和成交量怎么定
撮合模块决定回测可信度。常见做法是按当根 K 线的收盘价成交,或者按下一根开盘价成交。成交量受限于资金、持仓和涨跌停规则。
# engine/matcher.py 简化示意 class Matcher: def match(self, order, bar): if order.side == "buy": price = bar.close * (1 + self.slippage) # 买入加滑点 volume = min(order.volume, self.max_buy(bar)) else: price = bar.close * (1 - self.slippage) # 卖出减滑点 volume = min(order.volume, self.max_sell(bar)) if volume <= 0: return None fee = price * volume * self.commission return Trade(order.side, price, volume, fee)max_buy要考虑可用资金和涨停无法买入的情况,max_sell要考虑持仓和跌停无法卖出。滑点方向必须对买方不利、对卖方不利,否则回测会虚高。手续费按成交额乘费率,卖出时如果市场有印花税,也要在这里扣。
4.3 账户管理:持仓、资金与收益曲线
账户模块负责记账:买入扣现金、加持仓,卖出加现金、减持仓,每天收盘算一次总资产。收益曲线就是总资产序列。
# engine/account.py 简化示意 class Account: def __init__(self, cash): self.cash = cash self.position = 0 self.equity_curve = [] def apply(self, trade): amount = trade.price * trade.volume if trade.side == "buy": self.cash -= amount + trade.fee self.position += trade.volume else: self.cash += amount - trade.fee self.position -= trade.volume def update_price(self, bar): equity = self.cash + self.position * bar.close self.equity_curve.append(equity)equity_curve是画收益曲线的数据源,也是算最大回撤的依据。这里有个容易忽略的点:持仓市值要用最新价更新,否则曲线是阶梯状的。如果源码里没有update_price,说明它只在成交时更新,回测曲线会失真,二次开发时得补上。
5. 避坑与排查:跑这套交易源码时最容易翻车的五件事
交易软件源码的坑,大多不在语法,而在数据和时序。下面五条是我拆这类项目时反复遇到的,每条按现象、原因、解决写,照着排查能省不少时间。
5.1 回测曲线完美但实盘完全不是一回事
现象:回测年化很高,实盘一跑就亏。原因通常是回测用了未来数据,比如用当根收盘价算信号又用当根收盘价成交,或者滑点手续费设成 0。解决:把成交价改成下一根开盘价,佣金和滑点按真实水平设,再跑一遍看策略还成不成立。
5.2 行情数据缺失导致指标计算报错
现象:策略跑到某一天突然抛异常,提示数组越界或 NaN。原因:历史数据有停牌缺口,或者日期没排序。解决:导入数据后先做完整性检查,按日期排序、去重、补缺失交易日,策略里对len(closes)做足判断。
5.3 数据库锁死导致回测卡住
现象:多策略同时跑时程序卡住不动。原因:SQLite 写锁只允许一个写操作,并发回测会互相等。解决:回测阶段把数据读进内存,不频繁写库;或者换 MySQL,每个策略用独立连接。
5.4 时间戳时区不一致导致信号错位
现象:明明当天该出的信号,第二天才触发。原因:行情时间是 UTC,策略按本地时间比较。解决:全链路统一时区,入库前转成同一时区,策略里不要混用 naive 和 aware 时间。
5.5 实盘接口和模拟接口行为不一致
现象:模拟盘成交正常,接实盘后订单被拒。原因:实盘有最小交易单位、涨跌停、可用资金冻结等规则,模拟盘没模拟。解决:在trader/层加一层校验,把最小手数、涨跌停价、冻结资金都补上,让模拟和实盘走同一套风控。
6. 二次开发:把自有策略接进框架并验证的三个动作
这套源码的价值不在它自带的均线策略,而在你能否把自己的想法快速接进去、快速证伪。我一般分三步走:先写一个最小策略跑通链路,再用样本外数据验证,最后把参数和风控抽出来做成可配置。下面用一个动量策略举例。
第一步,新建strategy/momentum.py,只实现on_bar,逻辑尽量简单:
# strategy/momentum.py class Momentum(BaseStrategy): def on_init(self): self.window = self.params.get("window", 20) self.threshold = self.params.get("threshold", 0.05) def on_bar(self, bar): closes = self.ctx.history("close", self.window + 1) if len(closes) < self.window + 1: return ret = closes[-1] / closes[0] - 1 # 窗口内涨幅 if ret > self.threshold and not self.ctx.position: self.buy(bar.close, self.ctx.max_volume()) elif ret < -self.threshold and self.ctx.position: self.sell(bar.close, self.ctx.position)window是回看周期,threshold是触发阈值。参数不写死,从命令行或配置传入,方便后面批量调参。
第二步,样本外验证。把数据切成训练段和验证段,训练段调参,验证段只看不调。命令上可以用--start和--end分段跑,对比两段的最大回撤和胜率。如果训练段好、验证段崩,说明过拟合,回去简化逻辑或加风控。
第三步,把风控抽出来。在engine/risk.py里加单笔最大仓位、单日最大亏损、连续亏损熔断。这些规则不写在策略里,因为策略只管信号,风控是全局的。改完之后用同一组参数重跑,看收益曲线是否更平滑。
| 改造点 | 改哪里 | 验证方法 |
|---|---|---|
| 新策略 | strategy/新增文件 | 跑通回测看交易次数 |
| 调参数 | 命令行或config/ | 训练段/验证段对比 |
| 加风控 | engine/risk.py | 看最大回撤是否下降 |
| 换数据源 | data/loader.py | 检查日期连续性和字段 |
从那以后我每次接新策略,都强制先跑一遍最小链路,确认数据、撮合、账户三块没问题,再往里加逻辑。这套源码的结构刚好支持这种节奏:策略插拔、引擎不动、风控独立。你要是手里有想法,不妨先把它跑起来,再按上面的路子改一版自己的策略,比空想靠谱得多。希望帮到你。
本文还有配套的精品资源,点击获取