
升级说明

上一篇,我们用Dash实现一个轮动策略Web版交互界面(只概念性地展示的界面布局,未接入实际的数据和功能)。
[升级]21天搭建ETF量化交易系统Day19---设计一款基于轮动策略的Web版界面!
本篇升级,我们把backtrader回测框架集成到系统里面,这样就可以用真实的历史数据回测轮动策略。




搭建过程

go
这个系列,我们用Python从0一步步搭建出一套ETF量化交易系统(选择ETF标的是因为对于普通交易者来说,ETF相对于选强势股难度要小,而且没有退市风险)。大家可以跟随着我们的实现路径来一起学习,从过程中掌握方法。
掌握了方法之后,可以换成期货系统、比特币系统、美股系统,然后在实战中不断去完善自己的系统了。
DAY1-DAY15链接如下:15天搭建ETF量化交易系统
我们对"15天搭建ETF量化交易系统"系列的学习进行升级!进阶系列链接如下:
[进阶]21天搭建ETF量化交易系统Day16---用DeepSeek学习数据处理
[进阶]21天搭建ETF量化交易系统Day17---Backtrader回测马丁格尔补仓法
[进阶]21天搭建ETF量化交易系统Day18---爆改backtrader升级为实盘交易!
[进阶]21天搭建ETF量化交易系统Day20---BackTrader回测动量&趋势&成交量三因子轮动(年化近50%)
[进阶]21天搭建ETF量化交易系统Day21---机器学习玩转ETF量化投资
go
我们与时俱进,还会持续对现有的DAY21的内容升级!
````
```go
集成backtrader
```
整体架构:前端采用Dash构建响应式Web界面,通过Bootstrap进行现代化UI设计;后端核心是Backtrader量化回测引擎,通过回调函数与前端交互;数据层使用AKShare从新浪财经获取实时ETF历史数据,形成三层架构设计。
数据流:用户在Web界面设置参数 → 点击回测按钮触发Dash回调 → Backtrader引擎加载ETF历史数据 → 执行动量轮动策略 → 生成交易记录和绩效指标 → 结果返回前端可视化展示。
技术栈:Dash(前端框架)+ Backtrader(量化回测)+ AKShare(数据获取)+ Pandas(数据处理)+ Plotly(数据可视化),形成一个完整的量化策略研发工具链。
#### **Backtrader策略类 (`MomentumRotationStrategy`)**
`class MomentumRotationStrategy(bt.Strategy): """ 动量轮动策略的核心实现 继承自Backtrader的bt.Strategy基类 """ params = ( ('momentum_period', 5), # 动量计算周期 ('rebalance_period', 5), # 调仓周期 ('hold_num', 2), # 持有标的数量 ('min_momentum', 0), # 最小动量阈值 ('position_size', 0.4), # 单个标的仓位比例 ('printlog', True), # 日志开关 )``参数说明:`
````
-
momentum_period: 计算动量(收益率)的回顾周期 -
rebalance_period: 调仓间隔天数 -
hold_num: 每次持有表现最好的ETF数量 -
min_momentum: 动量过滤阈值,避免买入弱势标的 -
position_size: 每个ETF的最大仓位比例
接下来介绍下类中的各个方法。
go
__init__() - 初始化方法
-
使用Backtrader内置的
Momentum指标计算器 -
self.datas包含所有添加到回测的ETF数据 -
使用字典管理动量值,便于快速访问
python
def __init__(self): # 为每个ETF计算动量指标 self.momentum = { data: bt.indicators.Momentum(data.close, period=self.p.momentum_period) for data in self.datas } # 存储ETF排序结果 self.rankings = list(self.datas) # 调仓计数器 self.rebalance_day = 0 # 订单字典,管理未完成订单 self.order_dict = {}
next() - 逐日执行方法
```
`执行逻辑:`
```
-
每个交易日调用一次
-
计数器累加,达到调仓周期时触发调仓
-
重置计数器
ruby
def next(self): self.rebalance_day += 1 # 到达调仓日时执行调仓 if self.rebalance_day >= self.p.rebalance_period: self.rebalance_portfolio() self.rebalance_day = 0
rebalance_portfolio() - 核心调仓逻辑
```
`调仓算法:`
```
-
排序阶段:计算所有ETF的动量并排序
-
卖出阶段:检查当前持仓,如果不在前N名则卖出
-
买入阶段:买入排名前N且动量达标的ETF
-
仓位控制:按比例分配资金,按手数取整
python
def rebalance_portfolio(self): """执行调仓:卖出不在前列的,买入排名前列的""" # 1. 按动量值排序(从高到低) self.rankings.sort(key=lambda d: self.momentum[d][0], reverse=True) # 2. 卖出不在前hold_num名的持仓 for data in self.datas: pos = self.getposition(data).size if pos > 0 and data not in self.rankings[:self.p.hold_num]: self.close(data=data) # 平仓 # 3. 买入排名前hold_num且动量达标的标的 for i in range(min(self.p.hold_num, len(self.rankings))): data = self.rankings[i] pos = self.getposition(data).size momentum_value = self.momentum[data][0] if pos == 0 and momentum_value > self.p.min_momentum and data not in self.order_dict: # 计算买入数量 target_value = self.broker.getvalue() * self.p.position_size size = int(target_value / data.close[0] / 100) * 100 # 按手数取整 if size > 0: self.order_dict[data] = self.buy(data=data, size=size)
`notify_order() - 订单状态回调`
````
```go
作用:
```
````
-
记录交易日志
-
清理已完成订单
-
处理异常订单状态
python
def notify_order(self, order): """处理订单状态变化""" if order.status in [order.Completed]: if order.isbuy(): self.log(f'买入执行 {order.data._name}, 价格:{order.executed.price:.2f}') elif order.issell(): self.log(f'卖出执行 {order.data._name}, 价格:{order.executed.price:.2f}') self.order_dict.pop(order.data, None) # 移除已完成的订单
go
ETF数据获取函数
python
def get_etf_data(etf_code, start_date='2023-01-01', end_date='2023-12-31'): """ 从新浪财经获取ETF历史数据 返回格式:DataFrame with columns: ['open', 'high', 'low', 'close', 'volume'] """ # 市场代码转换:沪市51开头,深市15开头 symbol = f'sh{etf_code}' if etf_code.startswith('51') else f'sz{etf_code}' # 使用AKShare获取数据 df = ak.fund_etf_hist_sina(symbol=symbol) # 数据清洗和格式化 df['date'] = pd.to_datetime(df['date']) df.set_index('date', inplace=True) df = df[(df.index >= start_date) & (df.index <= end_date)] return df
数据格式转换
perl
# Backtrader需要PandasData格式data = bt.feeds.PandasData( dataname=df, # 原始DataFrame name=etf_code, # 数据名称(用于识别) datetime=None, # 使用索引作为日期 open=0, high=1, # 列索引映射 low=2, close=3, volume=4, # 成交量列 openinterest=-1 # 无持仓量数据)
回测引擎配置与执行。 Cerebro引擎初始化
python
def run_backtest(start_date, end_date, initial_capital, params): # 创建回测引擎 cerebro = bt.Cerebro() # 设置初始资金 cerebro.broker.setcash(initial_capital) # 设置交易费用(万三) cerebro.broker.setcommission(commission=0.0003) # 添加数据 for etf_code in etf_pool: df = get_etf_data(etf_code, start_date, end_date) if df is not None and len(df) > 50: data = bt.feeds.PandasData( dataname=df, name=etf_code, datetime=None, open=0, high=1, low=2, close=3, volume=4, openinterest=-1 ) cerebro.adddata(data) # 添加策略 cerebro.addstrategy( MomentumRotationStrategy, momentum_period=params['momentum_period'], rebalance_period=params['rebalance_period'], hold_num=params['hold_num'], position_size=params['position_size'], printlog=False ) # 添加分析器 cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe', riskfreerate=0.0) cerebro.addanalyzer(bt.analyzers.DrawDown, _name='drawdown') cerebro.addanalyzer(bt.analyzers.Returns, _name='returns') cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='trades') cerebro.addanalyzer(bt.analyzers.TimeReturn, _name='timereturn') # 运行回测 results = cerebro.run() strat = results[0] return cerebro, strat
Dash回调函数集成。回测触发回调`@app.callback( [Output('total-return-display', 'children'), Output('sharpe-display', 'children'), Output('max-drawdown-display', 'children'), Output('win-rate-display', 'children'), Output('total-trades-display', 'children'), Output('profit-factor-display', 'children'), Output('equity-curve-graph', 'figure'), Output('backtest-status', 'children')], [Input('start-backtest-btn', 'n_clicks')], [State('backtest-date-range', 'start_date'), State('backtest-date-range', 'end_date'), State('initial-capital-input', 'value'), State('rebalance-period-select', 'value'), State('hold-num-input', 'value'), State('momentum-period-input', 'value')])defrun_real_backtest(n_clicks, start_date, end_date, initial_capital, rebalance_period, hold_num, momentum_period): """ 完整的回测流程: 1. 获取参数 2. 获取数据 3. 运行Backtrader回测 4. 提取结果 5. 生成可视化 """ # 参数验证 if n_clicks isNone: return ['-'] * 7 + ['等待开始回测...'] try: # 1. 准备ETF池 etf_pool = list(IDX_SYM.values())
# 2. 初始化Backtrader引擎 cerebro = bt.Cerebro() cerebro.broker.setcash(initial_capital) cerebro.broker.setcommission(commission=0.0003)
# 3. 批量获取数据 data_frames = {} for etf_code in etf_pool: try: df = get_etf_data(etf_code, start_date, end_date) if df isnotNoneandnot df.empty andlen(df) > 50: data_frames[etf_code] = df except Exception as e: continue
# 4. 添加数据到引擎 for etf_code, df in data_frames.items(): data = bt.feeds.PandasData( dataname=df, name=etf_code, datetime=None, open=0, high=1, low=2, close=3, volume=4, openinterest=-1 ) cerebro.adddata(data)
# 5. 添加策略 cerebro.addstrategy( MomentumRotationStrategy, momentum_period=momentum_period, rebalance_period=rebalance_period, hold_num=hold_num, position_size=0.4, printlog=False )
# 6. 添加分析器 cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe', riskfreerate=0.0) cerebro.addanalyzer(bt.analyzers.DrawDown, _name='drawdown') cerebro.addanalyzer(bt.analyzers.Returns, _name='returns', timeframe=bt.TimeFrame.Days) cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='trades') cerebro.addanalyzer(bt.analyzers.TimeReturn, _name='timereturn')
# 7. 运行回测 results = cerebro.run() strat = results[0]
# 8. 提取绩效指标 final_value = cerebro.broker.getvalue() total_return = (final_value / initial_capital - 1) * 100
# 获取夏普比率 sharpe_analysis = strat.analyzers.sharpe.get_analysis() sharpe_ratio = sharpe_analysis.get('sharperatio', 0)
# 获取最大回撤 drawdown_analysis = strat.analyzers.drawdown.get_analysis() max_drawdown = drawdown_analysis.get('max', {}).get('drawdown', 0)
# 获取交易分析 trade_analysis = strat.analyzers.trades.get_analysis() total_trades = trade_analysis.get('total', {}).get('closed', 0) ifhasattr(trade_analysis, 'total') else0
# 9. 生成净值曲线 time_return = strat.analyzers.timereturn.get_analysis() dates = [] values = [] current_value = initial_capital
for date_key insorted(time_return.keys()): daily_return = time_return[date_key] current_value *= (1 + daily_return) dates.append(date_key) values.append(current_value)
# 10. 创建图表 equity_fig = { 'data': [{ 'x': dates, 'y': values, 'type': 'line', 'name': '策略净值', 'line': {'color': '#3498db', 'width': 2} }], 'layout': { 'title': f'动量轮动策略净值曲线', 'xaxis': {'title': '日期'}, 'yaxis': {'title': '净值'}, 'height': 400 } }
# 11. 格式化返回结果 return [ f"{total_return:+.2f}%", # 总收益率 f"{sharpe_ratio:.2f}", # 夏普比率 f"-{max_drawdown:.2f}%", # 最大回撤 f"{win_rate:.2f}%"if total_trades > 0else"-", # 胜率 f"{total_trades}", # 交易次数 f"{profit_factor:.2f}"if total_trades > 0else"-", # 盈亏比 equity_fig, # 净值曲线图 f"回测完成 | 总收益: {total_return:+.2f}%" # 状态信息 ]
except Exception as e: return ['-'] * 6 + [empty_fig, f"❌ 回测失败: {str(e)}"]`
```
`以上的设计实现了从数据获取(AKShare实时行情)、策略执行(动量轮动算法)到绩效分析(夏普比率、最大回撤等指标)的完整闭环。
执行日志如下所示:``============================================================2024-04-23 | 📅 调仓日期: 2024-04-232024-04-23 | 💰 当前总资产: 1000000.002024-04-23 | 📊 ETF动量排名 (周期=20天):2024-04-23 | 1. 159562 动量: +0.09 2024-04-23 | 2. 159652 动量: +0.04 2024-04-23 | 3. 513230 动量: +0.03 2024-04-23 | 4. 513530 动量: +0.02 2024-04-23 | 5. 159870 动量: +0.01 2024-04-23 | 6. 510050 动量: +0.01 2024-04-23 | 7. 159825 动量: +0.00 2024-04-23 | 8. 159750 动量: -0.01 2024-04-23 | 9. 512690 动量: -0.01 2024-04-23 | 10. 159840 动量: -0.02 2024-04-23 | 📥 买入清单 (2只):2024-04-23 | ✔ 159562: 312000股 (动量: +0.09)2024-04-23 | ✔ 159652: 447900股 (动量: +0.04)2024-04-23 | 📈 最终持仓 (0只):2024-04-23 | ✅ 调仓完成2024-04-23 | ============================================================2024-04-24 | 💰 买入执行 #001 159562, 价格:1.282, 数量:312000, 金额:399984.002024-04-24 | 💰 买入执行 #002 159652, 价格:0.879, 数量:447900, 金额:393704.102024-05-24 | ============================================================2024-05-24 | 📅 调仓日期: 2024-05-242024-05-24 | 💰 当前总资产: 1044997.992024-05-24 | 📊 ETF动量排名 (周期=20天):2024-05-24 | 1. 513530 动量: +0.20 2024-05-24 | 2. 159509 动量: +0.15 2024-05-24 | 3. 513090 动量: +0.11 2024-05-24 | 4. 159919 动量: +0.11 2024-05-24 | 5. 159902 动量: +0.10 2024-05-24 | 6. 159562 动量: +0.09 持有2024-05-24 | 7. 512200 动量: +0.09 2024-05-24 | 8. 159915 动量: +0.08 2024-05-24 | 9. 513290 动量: +0.07 2024-05-24 | 10. 510050 动量: +0.06 2024-05-24 | 📤 卖出清单 (2只):2024-05-24 | ✖ 159562: 312000股 (不在前2名)2024-05-24 | ✖ 159652: 447900股 (不在前2名)2024-05-24 | 📥 买入清单 (2只):2024-05-24 | ✔ 513530: 306900股 (动量: +0.20)2024-05-24 | ✔ 159509: 296400股 (动量: +0.15)2024-05-24 | 📈 最终持仓 (2只):2024-05-24 | 📌 159562: 312000股2024-05-24 | 📌 159652: 447900股2024-05-24 | ✅ 调仓完成2024-05-24 | ============================================================2024-05-27 | 💰 卖出执行 #003 159562, 价格:1.371, 数量:-312000, 金额:-427752.00, 盈利:27768.002024-05-27 | 💰 卖出执行 #004 159652, 价格:0.914, 数量:-447900, 金额:-409380.60, 盈利:15676.502024-05-27 | 💰 买入执行 #005 513530, 价格:1.366, 数量:306900, 金额:419225.402024-05-27 | 💰 买入执行 #006 159509, 价格:1.451, 数量:296400, 金额:430076.402024-06-24 |`
```
````
```go
为什么用Dash
```
`在量化交易领域,我们经常面临一个尴尬的局面:策略模型很强大,回测结果很漂亮,但展示给他人时却只能拿出冰冷的Excel表格或简单的Matplotlib图表。传统的Web开发需要HTML、CSS、JavaScript等前端技术,这对很多量化分析师和数据科学家来说是个不小的门槛。
直到我发现了Dash------这个基于Python的Web应用框架,让我能够用纯Python代码构建出专业级的交互式Web应用。`Dash是Plotly公司开源的Python框架,专门用于构建分析型Web应用。它的核心优势在于:
````
-
纯Python开发:无需编写任何HTML/CSS/JavaScript
-
响应式设计:自动适配桌面和移动端
-
丰富的组件:内置图表、表格、下拉菜单等交互组件
-
实时更新:支持数据实时刷新和交互
-
企业级应用:支持多用户、权限控制等高级功能
以下是Dash 最简完整例程说明
python
# demo.py - 最简单的Dash应用import dashfrom dash import dcc, html, Input, Output# 1️⃣ 创建应用对象(必须)app = dash.Dash(__name__)# 2️⃣ 定义布局:网页上显示什么app.layout = html.Div([ # A. 标题(用html.H1创建大标题) html.H1("最简单的Dash应用", id='main-title'), # B. 文本输入框(用户在这里输入内容) dcc.Input( id='user-input', # 组件ID,回调函数用这个找组件 type='text', # 输入类型 value='请输入文字', # 默认显示的文字 style={'width': '300px'} # CSS样式:宽度300像素 ), # C. 显示区域(用来显示处理后的结果) html.Div(id='output-div', style={'marginTop': '20px'}), # D. 按钮(用户点击触发动作) html.Button('清空输入', id='clear-btn')])# 3️⃣ 定义回调:用户操作后系统怎么响应@app.callback( # 输出:更新哪个组件的哪个属性 Output('output-div', 'children'), # 输入:监听哪个组件的哪个属性变化 [ Input('user-input', 'value'), # 监听输入框内容变化 Input('clear-btn', 'n_clicks') # 监听按钮点击次数 ])def update_output(input_text, click_count): """ 回调函数:当输入框内容变化或按钮被点击时自动执行 参数: input_text: 输入框当前的内容 click_count: 按钮被点击的次数 返回: 要显示在output-div中的内容 """ # 判断触发回调的是哪个组件 ctx = dash.callback_context if not ctx.triggered: # 第一次加载页面时执行 return "等待输入..." else: # 获取是哪个输入触发的 trigger_id = ctx.triggered[0]['prop_id'].split('.')[0] if trigger_id == 'clear-btn': # 如果点击了清空按钮 return "输入已清空!" else: # 如果输入框内容变化了 return f"你输入了:{input_text}"# 4️⃣ 运行应用(启动服务器)if __name__ == '__main__': # 打印访问地址 print("✅ 应用已启动!") print("🌐 请用浏览器访问:http://localhost:8050") print("📝 在输入框中打字,下面会实时显示") print("🔄 点击按钮可以清空内容") # 启动服务器 app.run_server( debug=True, # 调试模式,代码修改会自动重启 port=8050 # 端口号 )
`在Dash框架中,布局(Layout) 定义了网页的整体结构与外观,即页面长什么样;它由一个个组件(Component) 构成,这些组件就是网页上的各个可视部分,如输入框、按钮、图表等。回调(Callback) 则负责编写交互逻辑,它通过监听组件状态的变化来触发相应的处理函数。而响应(Response) 描述了事件触发后的处理流程,即系统如何响应具体的用户操作或数据变化,从而动态更新页面内容。
在这个最简例程基础上,可以添加图表、页面和接入真实数据
添加图表:`
bash
# 添加图表组件dcc.Graph(id='my-chart')# 在回调中返回图表return dcc.Graph(figure=create_chart(data))
`添加更多页面`
python
# 用回调切换不同内容@app.callback( Output('content', 'children'), Input('tab1', 'n_clicks'), Input('tab2', 'n_clicks'))def switch_page(click1, click2): # 返回不同页面的内容
`连接真实数据`
python
# 读取CSV文件import pandas as pddf = pd.read_csv('data.csv')# 在回调中使用数据def update_chart(selection): filtered = df[df['category'] == selection] return create_chart(filtered)
```
`框架功能展示我的轮动池:提供ETF池管理,支持添加、筛选、排序ETF以构建个性化轮动池;实现动量排名可视化,能够实时计算并展示各ETF的动量分数及排名变化;配备一键式操作界面,集成导入/导出池子、批量操作等便捷功能;并通过多维度数据展示,将价格、涨跌幅、动量分数、排名等关键指标清晰呈现,实现策略状态一目了然。动量轮动策略核心逻辑:
每20天定期调仓
选择动量排名前2的ETF持有
动量分数基于20日涨幅计算
实现"强者恒强"的轮动效应
我的账户:提供了全方位的资产全景视角:通过总资产、持仓市值、可用资金、累计收益四大关键指标呈现核心财务状况;以交互式资产配置饼图直观展示各ETF持仓比例,并支持点击查看详情;同时配备动态收益曲线,实时追踪账户净值变化以评估策略表现;并清晰展示当前仓位比例,为投资决策提供直接的仓位管理参考。策略回测:构建参数化回测系统,支持灵活设置回测周期、初始资金及调仓频率等核心参数。系统通过绩效指标矩阵以卡片化形式展示12项关键指标,对策略表现进行全面评估;同时提供净值对比分析功能,将策略净值与基准净值进行多时间维度的可视化比较。所有分析均以交互式图表呈现,支持缩放、平移及数据点悬停查看,使历史验证过程直观而深入。参数配置:策略参数涵盖动量周期、调仓频率及持有数量等核心策略变量;交易参数包括手续费率、滑点及交易时间等执行细节;风险参数则聚焦于止损止盈、仓位限制与风险控制等风控规则,形成从策略逻辑、交易执行到风险管控的完整闭环。实盘明细:提供交易全流程监控。支持按时间、交易类型及ETF代码等多维度精准筛选交易记录;采用清晰的状态标记体系,有效区分买入/卖出、盈利/亏损以及完成/待处理等关键状态;完整记录每笔交易的时间、价格、数量及金额,实现交易详情可追溯;同时自动计算并展示每笔交易的盈亏情况,为绩效归因提供精确数据支撑。数据管理:实现一体化数据管理。支持每日、每周及每月定时更新的自动配置计划;集成新浪财经、东方财富、聚宽等多类主流数据源;实时监控各类数据的更新状态,并通过清晰的状态标识进行可视化反馈;同时提供手动控制接口,允许用户按需触发数据的立即更新,保障数据时效性与系统灵活性。
系统监控:提供全方位的稳定性保障。通过服务状态看板实时展示服务器、数据库、策略引擎及交易接口四大核心组件的运行状态;以动态曲线图监控CPU与内存等资源的使用率;按时间倒序追踪并展示关键操作日志,确保运行过程可审计;同时建立异常预警机制,实现对系统异常的即时发现与快速处理,有效保障系统持续稳定运行。策略文档:构建了完善的知识沉淀体系。详细阐述动量轮动策略的理论基础与核心原理;提供从入门到精通的完整操作使用指南;分享基于实际系统运行总结的最佳实践经验;并设立明确的技术支持通道,确保用户在遇到问题时能通过指定联系方式获得快速响应与解决方案。
关键技术实现
整体布局结构:系统采用经典的侧边栏导航+主内容区设计,左侧为功能导航菜单,右侧为具体内容展示区:`
```
`app.layout = html.Div([ # 标题栏 html.Div([...]), # 主内容区域 html.Div([ # 侧边栏导航(固定250px) html.Div([...], id="sidebar"), # 主内容展示区 html.Div([...], id="main-content") ])])`响应式导航系统:通过Dash的回调机制,实现页面间的平滑切换:`@app.callback( [Output("content-pool", "style"), Output("content-account", "style"), ...], [Input("nav-pool", "n_clicks"), Input("nav-account", "n_clicks"), ...])def switch_page(pool_clicks, account_clicks, ...): # 动态显示/隐藏页面,更新导航状态 ...`部署与运行`# 安装依赖pip install dash plotly pandas numpy akshare# 运行应用python app.py# 生产环境部署(使用Gunicorn)gunicorn -w 4 -b 0.0.0.0:8050 app:server`应用启动后,在浏览器中访问 `http://localhost:8050` 即可使用。

总结

Dash真正实现了"用Python解决所有问题"的理念,让数据科学家和量化分析师能够专注于核心业务逻辑,而不是前端技术细节。
在这套动量轮动策略的Web界面框架中,我们采用了界面原型与功能框架先行的开发策略。当前版本已完成完整的交互界面布局和各功能模块的逻辑架构,其中展示数据采用了模拟数据与真实数据结构相结合的方式。
后续所有数据接口、回调函数和计算模块均采用标准化设计,使用者只需将现有模拟数据源替换为真实的行情API,并将策略逻辑的核心计算函数替换为实盘算法,即可在不改动任何界面代码的情况下,快速将演示系统升级为完整的生产级交易平台。
说明
此系列为连载专栏,完整代码会上传知识星球《Python量化编程基础》!作为《玩转股票量化交易》学员们的学习资料。非《玩转股票量化交易》星球学员需要的话,也可以联系我加入!
知识星球介绍点击:知识星球《玩转股票量化交易》精华内容概览
