简介:这份资源面向具备一定Python基础、希望进入量化交易领域的开发者与金融从业者,围绕算法交易系统的完整链路展开,从数据获取、技术指标计算、策略设计到回测与实时执行均有涉及。包内共35个文件,以34个Python脚本和1份Markdown说明为主,压缩包约38KB,脚本按功能拆分为数据抓取、指标计算、组合再平衡、回撤与绩效评估、可视化等模块,便于按需查阅与二次改造。内容涵盖移动平均线、RSI、布林带、ADX、OBV、Renko等指标实现,以及夏普与索提诺比率、最大回撤与卡玛比率、CAGR等绩效度量,还包含魔法公式选股、皮氏评分等基本面筛选思路,配合回测与风险资金管理示例,可帮助读者理解从策略原型到自动化执行的落地路径。目前已有3967人学习下载,适合作为量化交易入门与项目实践的参考脚本集。
1. 用 Python 把交易策略跑成一套能自动下单的系统
很多人第一次接触量化交易,是从一段几十行的 Python 脚本开始的:拉历史行情、算个均线、画个收益曲线,看着回测图心里挺美。但真要把这套东西变成每天自动跑、自动判断、自动下单的系统,中间隔着的坑比想象中多得多。这份资源讲的就是这件事——用 Python 从零搭一套全自动算法交易系统,把定量策略从「笔记本里的回测」推进到「能持续运行的工程」。它适合两类人:一是会写 Python、想系统了解交易系统完整链路的开发者;二是做过简单策略回测、但卡在工程化落地这一环的量化爱好者。下面我按自己拆项目的习惯,把它的结构、核心代码和改造点讲清楚。
2. 系统架构与目录结构:一套交易系统到底由哪几块拼起来
2.1 从行情到订单的完整数据流
一套全自动交易系统,本质是一条流水线:数据进来,策略判断,风控过滤,订单出去,成交回报再回来。这条链路任何一环断了,系统就不是「自动」而是「半自动加人工盯盘」。这份资源里的架构基本遵循这个分层思路,我把它拆成五层来看:
| 层级 | 职责 | 典型模块 |
|---|---|---|
| 数据层 | 拉取、清洗、缓存行情与基本面数据 | 行情接口封装、本地存储 |
| 策略层 | 根据数据生成买卖信号 | 均线、动量、均值回归等策略类 |
| 风控层 | 仓位限制、单笔上限、最大回撤熔断 | 风控检查器 |
| 执行层 | 下单、撤单、订单状态跟踪 | 券商/模拟接口适配 |
| 监控层 | 日志、告警、绩效统计 | 日志模块、报表 |
这样分层的好处是每层可以独立替换。比如你换一家券商接口,只动执行层的适配器,策略代码一行不用改。我见过不少新手把所有逻辑塞进一个main.py,回测和实盘混在一起,最后想改个止损规则都得通读八百行,血泪经验就是:分层不是给架构师看的,是给自己留后悔药的。
2.2 目录结构长什么样
按这个分层,项目目录大致是这样组织的(具体文件名可能略有差异,但职责划分是清晰的):
algo_trading/ ├── config/ │ ├── settings.yaml # 账户、接口、运行参数 │ └── strategies.yaml # 各策略的启停与参数 ├── data/ │ ├── loader.py # 行情拉取与清洗 │ └── cache/ # 本地行情缓存 ├── strategies/ │ ├── base.py # 策略基类,定义统一接口 │ ├── ma_cross.py # 均线交叉策略 │ └── momentum.py # 动量策略 ├── risk/ │ └── manager.py # 仓位与回撤风控 ├── execution/ │ ├── broker_base.py # 券商接口抽象 │ └── paper_broker.py # 模拟撮合 ├── engine/ │ └── runner.py # 主循环,串起数据-策略-风控-执行 ├── monitor/ │ └── logger.py # 结构化日志与告警 └── main.py # 入口这个结构里最关键的是strategies/base.py和execution/broker_base.py两个抽象。前者让所有策略对外暴露同样的generate_signals()方法,后者让模拟盘和实盘可以无缝切换。新手照着这个骨架填代码,比从零想目录要快得多。
2.3 技术选型为什么是这些
Python 做量化交易,绕不开几个库:pandas处理时间序列、numpy做数值计算、ccxt或券商官方 SDK 对接行情与下单、APScheduler或schedule做定时任务、loguru或标准库logging做日志。这份资源的技术栈基本落在这个范围内,属于「够用且生态成熟」的组合。
选pandas而不是自己写数据结构,是因为行情数据天然是带时间索引的表格,resample、rolling这些操作一行顶几十行手写循环。选抽象基类而不是函数式回调,是因为策略数量一多,统一接口能让主循环保持稳定。这里没有用重型框架,我理解是刻意的——交易系统最怕黑匣子,自己写的每一行都能调试,比套一个不透明的框架踏实。
3. 核心模块拆解:策略、风控与执行三段代码
3.1 策略基类与信号生成
所有策略继承同一个基类,保证主循环调用方式一致。基类只约定接口,不写具体逻辑:
from abc import ABC, abstractmethod import pandas as pd class BaseStrategy(ABC): def __init__(self, params: dict): self.params = params @abstractmethod def generate_signals(self, df: pd.DataFrame) -> pd.Series: """输入带 OHLCV 的行情表,返回 -1/0/1 的信号序列""" raise NotImplementedErrorparams用来放策略参数,比如均线周期;generate_signals的返回值约定为1做多、-1做空或平多、0不动。这个约定很重要,主循环只认这三种值,策略内部怎么算都行。
一个均线交叉策略的实现:
class MACrossStrategy(BaseStrategy): def generate_signals(self, df: pd.DataFrame) -> pd.Series: fast = df["close"].rolling(self.params["fast"]).mean() slow = df["close"].rolling(self.params["slow"]).mean() signal = pd.Series(0, index=df.index) signal[fast > slow] = 1 # 快线上穿,做多 signal[fast < slow] = -1 # 快线下穿,平多 return signal逻辑很直白:快慢均线比较,金叉给1,死叉给-1。参数fast和slow从配置读,常见做法是fast=5、slow=20,但别迷信固定值,不同标的波动特性差别很大。这里有个细节:rolling前slow-1行是NaN,比较结果为False,信号默认0,正好避免开盘就乱下单。
3.2 风控层:在下单前拦住不该发的单
策略给出信号不等于直接下单,中间必须过风控。风控层做三件事:单笔仓位上限、总仓位上限、当日亏损熔断。
class RiskManager: def __init__(self, max_position: float, max_daily_loss: float): self.max_position = max_position # 单标的仓位占比上限 self.max_daily_loss = max_daily_loss # 当日亏损比例熔断线 self.daily_pnl = 0.0 def check(self, signal: int, current_position: float, price: float) -> int: if self.daily_pnl <= -self.max_daily_loss: return 0 # 触发熔断,强制不交易 if signal == 1 and current_position >= self.max_position: return 0 # 已达仓位上限,忽略加仓 return signalcheck的入参是原始信号、当前仓位和价格,返回经过过滤的信号。max_position常见设0.2到0.3,max_daily_loss设0.05左右,具体看你的风险偏好。熔断判断放在最前面,是因为亏损失控时任何加仓信号都该被拦下。这段代码看着简单,但它是实盘和回测最大的差别之一——回测里你很少考虑「今天已经亏了 5% 还要不要开新仓」,实盘里这是保命线。
3.3 执行层与主循环
执行层用抽象基类隔离券商差异,模拟盘和实盘实现同一套方法:
class BrokerBase(ABC): @abstractmethod def place_order(self, symbol: str, side: str, qty: float) -> dict: raise NotImplementedError @abstractmethod def get_position(self, symbol: str) -> float: raise NotImplementedError主循环把前面几层串起来,是系统的「心脏」:
def run_once(engine): for symbol in engine.symbols: df = engine.data.loader.fetch(symbol) # 1. 取行情 signal = engine.strategy.generate_signals(df).iloc[-1] # 2. 出信号 pos = engine.broker.get_position(symbol) # 3. 查仓位 signal = engine.risk.check(signal, pos, df["close"].iloc[-1]) # 4. 风控 if signal == 1 and pos == 0: engine.broker.place_order(symbol, "buy", engine.order_qty) elif signal == -1 and pos > 0: engine.broker.place_order(symbol, "sell", pos)每一步都有明确职责:取数、算信号、查仓位、过风控、下单。iloc[-1]取最新一根 K 线的信号,pos == 0和pos > 0的判断避免重复开仓。这个循环由定时任务按周期触发,比如每根 K 线收盘后跑一次。注意place_order的返回值要接住并记录,否则订单失败你都不知道。
4. 跑起来:环境、配置与模拟盘验证
4.1 环境准备与依赖安装
先把运行环境搭好。建议用虚拟环境隔离依赖,避免和系统里其他项目打架:
python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install pandas numpy pyyaml apscheduler loguru如果执行层对接的是某交易所或券商的 SDK,再按对应文档补装。依赖不多,装完用pip freeze > requirements.txt锁一下版本,换机器复现时省事。这里提醒一句:交易相关库版本变动可能影响接口行为,锁版本不是洁癖,是防止某天早上起来发现下单接口签名变了。
4.2 配置文件怎么写
配置集中在config/settings.yaml,把账户、标的、策略参数分开:
broker: mode: paper # paper 模拟盘 / live 实盘 api_key: "your_key" symbols: - "BTC/USDT" strategy: name: ma_cross params: fast: 5 slow: 20 risk: max_position: 0.25 max_daily_loss: 0.05 schedule: interval_minutes: 60mode是安全开关,第一次跑一定设paper。symbols支持多标的,主循环会逐个处理。schedule.interval_minutes控制运行频率,和你的 K 线周期对齐。参数改完不用动代码,这是配置外置的直接好处。
4.3 模拟盘跑通再切实盘
启动入口很简单:
python main.py --config config/settings.yaml第一次运行重点看三件事:日志里有没有正常拉到行情、信号有没有按预期生成、模拟订单有没有成交记录。模拟盘跑够一段时间(比如一两周),把日志里的信号和实际行情对照,确认策略逻辑没写反——我见过把金叉死叉写反、回测曲线漂亮实盘一路亏的案例,这种错误在模拟盘阶段就能抓出来。确认无误再把mode改成live,并且第一次实盘用小资金。
提示:实盘前务必确认接口的下单单位和最小下单量,不同标的规则不同,单位搞错可能直接报错或被拒单。
5. 避坑与常见问题:那些跑起来才会暴露的坑
5.1 回测赚钱实盘亏钱
现象:回测年化很好看,实盘一上就回撤。原因通常是回测没算交易成本、滑点,或者用了未来数据(比如用收盘价信号却在当天开盘成交)。解决:回测里加入手续费和滑点模型,信号生成和成交价格严格按时间先后对齐,shift(1)这类操作要检查清楚。
5.2 定时任务重复下单
现象:同一根 K 线触发了两次买入。原因多是任务执行时间超过间隔,或者进程被重复拉起。解决:在订单里带唯一幂等键,执行层记录已处理的 K 线时间戳,重复的直接跳过;定时任务加锁,防止多实例并发。
5.3 行情接口限频或断连
现象:跑一段时间后拉数据报错,循环中断。原因是对接的行情源有请求频率限制,或网络抖动。解决:加请求间隔和重试机制,本地缓存最近行情,接口失败时用缓存兜底并告警,别让一次拉取失败拖垮整个循环。
5.4 浮点数与精度问题
现象:仓位算出0.30000000000000004,下单被拒或数量异常。原因是浮点运算精度。解决:金额和数量统一用Decimal或按最小单位取整,下单前做一次精度规整,别直接把pandas算出来的浮点丢给接口。
5.5 日志缺失导致无法复盘
现象:出问题后不知道当时信号是什么、为什么下单。原因是只记了成交没记决策过程。解决:每次循环把行情快照、信号值、风控结果、订单响应都写进结构化日志,按日期分文件。复盘时这些日志就是黑匣子。
6. 二次开发:换策略、接实盘与验证方法
拿到这套代码后,最常做的改造是换策略。因为策略层有统一基类,新增一个策略只需要继承BaseStrategy实现generate_signals,然后在配置里改strategy.name和参数即可,主循环和风控完全不用动。比如加一个均值回归策略:
class MeanReversionStrategy(BaseStrategy): def generate_signals(self, df): ma = df["close"].rolling(self.params["window"]).mean() std = df["close"].rolling(self.params["window"]).std() z = (df["close"] - ma) / std signal = pd.Series(0, index=df.index) signal[z < -self.params["threshold"]] = 1 # 偏离下轨,做多 signal[z > self.params["threshold"]] = -1 # 偏离上轨,平多 return signalwindow控制回看周期,threshold控制偏离阈值,常见window=20、threshold=2。改完先在模拟盘跑,对比新旧策略在同一段行情上的信号差异,确认逻辑符合预期再上。
接实盘的核心是写一个LiveBroker(BrokerBase),把place_order和get_position映射到真实接口。这里有个我踩过的坑:不同接口的订单状态是异步返回的,place_order返回成功不代表成交,必须再查一次订单状态或监听回报。所以执行层最好加一个query_order方法,主循环在下单后做一次状态确认。
验证方法上,我习惯做三层:单元测试测策略信号在构造数据上的输出、模拟盘跑一段时间对比日志、小资金实盘观察成交和滑点。三层都过了再放大资金。从那以后我每次改策略或换接口,都强制走一遍这三层验证,哪怕改动只有一行——交易系统里一行代码的代价可能是真金白银。希望帮到你。
本文还有配套的精品资源,点击获取