第 1 章 · 01 OpenBB 终端体验与自动交易流水线(对应 QS001/002) 速查摘要:OpenBB 是一个免费开源的金融数据终端,号称 29000 美元彭博终端的"平替"。本节分两段:QS001 体验它的命令行(CLI)无代码能力——纯键盘敲命令就能看行情、画技术指标、跑 ARIMA 预测;QS002 则用 Python SDK 把"选股→拉价→组合优化→算下单股数"串成一条端到端自动交易流水线,真正落地到"该买多少股"。 涉及脚本:原项目 QS001-openbb-financial-data/openbb-financial-scripts.bash(约 33 行,纯 bash 交互命令);QS002-automate-trades/automate-trades.
速查摘要:OpenBB 是一个免费开源的金融数据终端,号称 29000 美元彭博终端的"平替"。本节分两段:QS001 体验它的命令行(CLI)无代码能力——纯键盘敲命令就能看行情、画技术指标、跑 ARIMA 预测;QS002 则用 Python SDK 把"选股→拉价→组合优化→算下单股数"串成一条端到端自动交易流水线,真正落地到"该买多少股"。
涉及脚本:原项目 QS001-openbb-financial-data/openbb-financial-scripts.bash(约 33 行,纯 bash 交互命令);QS002-automate-trades/automate-trades.py(约 65 行 Python)
⚠️ 注意:OpenBB 3 与 OpenBB 4 的 API 完全不兼容。本章脚本全部基于
openbb==^3.2.4,import 路径是from openbb_terminal.sdk import openbb。OpenBB 4 改成了import openbb+openbb.obb.equity.price.historical(...),且返回类型要.to_df()转换、列名全小写。跑本章代码前务必确认版本,否则会直接 ImportError。另外 QS002 里先调screener_data又紧接着read_csv覆盖——这是有意的缓存写法(下一节详解),不是 bug。
OpenBB 终端是一个交互式命令行工具,启动后进入一个"菜单树"。核心交互模式是:
stocks(股票)、crypto、economy、forecast(预测)等。load tsla 加载一只股票作为"当前上下文",ta 进入技术分析,sma 画简单均线。q(quit)。help 看当前菜单下能用哪些命令。这本质上是一个把"几百个数据 API + 几十个图表函数"包装成菜单树的产品,适合不会写代码但要做金融分析的人,也适合快速探索。
QS001 最后一步调 forecast 菜单的 autoarima,对 TSLA 收盘价做 14 天预测。ARIMA(p, d, q)是经典时间序列模型,三个参数分别表示:
autoarima 会自动选最优的 (p, d, q) 组合,不用人工调参。需要强调的是,直接用 ARIMA 预测股价在实战里效果一般(价格接近随机游走),这里更多是演示终端能力,而非推荐策略。
QS002 把一个完整的"自动交易决策"拆成四步,这是机构量化的标准骨架:
Price > 15 滤掉低价仙股(流动性差、波动噪声大),Country == "USA" 限定市场。关键公式——风险平价:目标是各资产的风险贡献相等,权重解出的不是"预期收益最大化",而是"风险均衡"。riskfolio 用 EWMA(指数加权)估计协方差,衰减因子 d=0.94 是 RiskMetrics 经验值,意味着每天保留 94% 的旧协方差、吸收 6% 的新信息,对市场结构变化反应平滑。
openbb-financial-scripts.bash 实际不是脚本而是"操作录屏",一行行敲进终端:
# 进入股票菜单 stocks # 加载 TSLA 作为当前上下文(后续命令都作用于它) load tsla # 任何时候看帮助 help # 进入技术分析子菜单 ta # 画简单移动平均线 sma # 退出当前菜单(回到上一级) q # 进入预测(实验性)菜单 forecast # 对 TSLA 收盘价做 autoarima,预测未来 14 天 autoarima -d TSLA --target-column adj_close -n 14
要点:
-d TSLA 指定数据源是已加载的 TSLA;--target-column adj_close 指定预测列是复权收盘价;-n 14 是预测 14 天。这些参数名和 pandas/sklearn 风格一致。整段脚本按四步推进。
第一步——选股 + 缓存覆盖(11-14 行):
# GET NEW HIGHS new_highs = openbb.stocks.screener.screener_data("new_high") new_highs = pd.read_csv("QS002-automate-trades/new_highs.csv")
screener_data("new_high") 调 FinViz 的"创新高"预设,返回含 Ticker、Price、Country 等列的 DataFrame。注意第二行立刻用本地 CSV 覆盖——这是缓存模式:第一次跑调 API,之后为了不反复请求 FinViz(慢且可能限流),改成读已落盘的 csv。下一节 QS003 会把这种模式讲透。
第二步——过滤 + 拉价(16-31 行):
port_data = new_highs[(new_highs.Price>15) & (new_highs.Country == "USA")] tickers = port_data.Ticker.tolist() # 拉每只 ticker 的历史收盘价 data = openbb.economy.index(tickers, start_date = "2016-01-01", end_date="2019-12-30") returns = data.pct_change()[1:] returns.dropna(how="any", axis=1, inplace=True)
economy.index(tickers, ...) 批量拉多只股票的收盘价,返回列名是 ticker、行索引是日期的宽表。pct_change() 算日收益率,[1:] 切掉第一行(第一天的 pct_change 是 NaN)。dropna(how="any", axis=1, inplace=True) 删掉任何含缺失的列——有些股票可能在这一区间中途退市或停牌,导致整列有 NaN。**删列(axis=1)而不是删行(axis=0)** 是因为缺一只股票比缺一天更能接受,删行会丢掉所有股票某天的数据。第三步——风险平价优化(37-49 行):
port = rp.Portfolio(returns=returns) port.assets_stats(method_mu='hist', method_cov='hist', d=0.94) port.lowerret = 0.0008 w_rp_c = port.rp_optimization( model = "Classic", rm = "MV", hist = True, rf = 0, b = None )
逐行解读:
rp.Portfolio(returns=returns) 用收益 DataFrame 构造组合对象。assets_stats(..., d=0.94) 估算各资产的期望收益和协方差,method_mu='hist'/method_cov='hist' 表示用历史样本均值和历史样本协方差,d=0.94 是 EWMA 衰减因子(详见下方技巧)。port.lowerret = 0.0008 设最小期望日收益约 0.08%(年化约 20%),作为约束。rp_optimization(...) 是核心:model="Classic" 用历史数据(非 Black-Litterman、非稳健估计)、rm="MV" 用均值-方差风险度量、hist=True 用历史估计、rf=0 无风险利率设 0、b=None 用默认风险预算(各资产等风险贡献)。返回的是一列权重,加起来约等于 1。第四步——把权重变股数(53-59 行):
port_val = 10_000 w_rp_c["invest_amt"] = w_rp_c * port_val w_rp_c["last_price"] = data.iloc[-1] w_rp_c["shares"] = (w_rp_c.invest_amt / w_rp_c.last_price).astype(int)
这是整段脚本最有价值的一步。逻辑:
port_val = 10_000(一万美元)。Python 里的下划线 10_000 是数字分隔符,等同于 10000,纯粹为可读性。w_rp_c * port_val 权重乘本金 = 每只股票应该投多少钱。data.iloc[-1] 取每只股票最后一日的收盘价(列名与权重表对齐,直接赋值即对齐)。invest_amt / last_price 投资金额除以单价 = 应买股数,.astype(int) 向下取整(不能买半股)。输出 w_rp_c 就成了一张"下单清单":每只股票投多少钱、现价多少、买几股——可以直接交给券商或手动执行。
技巧一:shares 把抽象权重变可执行下单。组合优化器返回的权重(比如 0.23、0.15)是数学抽象,不能直接用。乘本金得金额、除单价得股数,这一步把"研究成果"翻译成"交易指令",是从研究到实盘的关键桥梁。几乎所有自动交易系统最后都要做这步换算。
技巧二:d=0.94 是 RiskMetrics EWMA 衰减因子。J.P. Morgan 的 RiskMetrics 模型用 σ²ₜ = 0.94·σ²ₜ₋₁ + 0.06·r²ₜ 估计波动率,0.94 是经过实证标定的"半衰期约 22 个交易日(一个月)"的最优值。在 riskfolio 里给 assets_stats 传 d=0.94 即启用这个估计方式,它让协方差对最近行情更敏感、对远古数据衰减,比简单等权样本协方差更贴近当前市场结构。
技巧三:缓存覆盖写法的识别。看到"先调 API 紧接着 read_csv 覆盖同一变量"的代码,要立刻识别为缓存模式——作者第一次跑时调 API 把数据落盘,之后改成本地读复用。这种写法在 QS002/QS003 都出现,是这类教程项目处理"慢 API"的通用手法。
技巧四:dropna(how="any", axis=1) 处理多股票数据缺失。多股票对齐的宽表里,缺一只(删列)比缺一天(删行)更划算,因为删行会污染所有股票。这是面板数据清洗的常用判断。
💡 速查要点:QS002 的核心是"权重→股数"这一步——
shares = invest_amt / last_price,把组合优化的数学结果翻译成可下单的整数股数。配合d=0.94的 EWMA 协方差估计,就是一条从选股到下单的完整工业级流水线。
stocks/load/ta/forecast 是核心交互。autoarima 自动选参,但直接预测股价实战效果有限,这里演示为主。d=0.94 是 RiskMetrics 的 EWMA 衰减因子(半衰期约 1 个月)。