Python 金融交易实用指南(四)(3)https://developer.aliyun.com/article/1523804
学习基于数学模型的策略
我们现在将在以下部分中看各种基于数学模型的策略。
每月交易的组合波动率最小化策略
该策略的目标是最小化组合波动率。它受到了github.com/letianzj/QuantResearch/tree/master/backtest
的启发。
在以下示例中,投资组合包括 道琼斯工业平均指数 中的所有股票。
该策略的关键成功因素如下:
- 股票范围 —— 或许全球指数 ETF 组合会更好。
- 滚动窗口 —— 我们回溯 200 天。
- 交易频率 —— 以下算法使用每月交易 —— 注意构造。
代码如下:
%matplotlib inline from zipline import run_algorithm from zipline.api import order_target_percent, symbol, set_commission, schedule_function, date_rules, time_rules from zipline.finance.commission import PerTrade import pandas as pd import pyfolio as pf from scipy.optimize import minimize import numpy as np import warnings warnings.filterwarnings('ignore') def initialize(context): context.stocks = [symbol('DIS'), symbol('WMT'), symbol('DOW'), symbol('CRM'), symbol('NKE'), symbol('HD'), symbol('V'), symbol('MSFT'), symbol('MMM'), symbol('CSCO'), symbol('KO'), symbol('AAPL'), symbol('HON'), symbol('JNJ'), symbol('TRV'), symbol('PG'), symbol('CVX'), symbol('VZ'), symbol('CAT'), symbol('BA'), symbol('AMGN'), symbol('IBM'), symbol('AXP'), symbol('JPM'), symbol('WBA'), symbol('MCD'), symbol('MRK'), symbol('GS'), symbol('UNH'), symbol('INTC')] context.rolling_window = 200 set_commission(PerTrade(cost=5)) schedule_function(handle_data, date_rules.month_end(), time_rules.market_open(hours=1)) def minimum_vol_obj(wo, cov): w = wo.reshape(-1, 1) sig_p = np.sqrt(np.matmul(w.T, np.matmul(cov, w)))[0, 0] return sig_p def handle_data(context, data): n_stocks = len(context.stocks) prices = None for i in range(n_stocks): price_history = \ data.history(context.stocks[i], "close", context.rolling_window, "1d") price = np.array(price_history) if prices is None: prices = price else: prices = np.c_[prices, price] rets = prices[1:,:]/prices[0:-1, :]-1.0 mu = np.mean(rets, axis=0) cov = np.cov(rets.T) w0 = np.ones(n_stocks) / n_stocks cons = ({'type': 'eq', 'fun': lambda w: np.sum(w) - 1.0}, {'type': 'ineq', 'fun': lambda w: w}) TOL = 1e-12 res = minimize(minimum_vol_obj, w0, args=cov, method='SLSQP', constraints=cons, tol=TOL, options={'disp': False}) if not res.success: return; w = res.x for i in range(n_stocks): order_target_percent(context.stocks[i], w[i]) def analyze(context, perf): returns, positions, transactions = \ pf.utils.extract_rets_pos_txn_from_zipline(perf) pf.create_returns_tear_sheet(returns, benchmark_rets = None) start_date = pd.to_datetime('2010-1-1', utc=True) end_date = pd.to_datetime('2018-1-1', utc=True) results = run_algorithm(start = start_date, end = end_date, initialize = initialize, analyze = analyze, capital_base = 10000, data_frequency = 'daily' bundle ='quandl')
输出如下:
图 9.81 – 组合波动率最小化策略;摘要回报和风险统计
结果是积极的 —— 见到强稳定性为 0.91
,而尾比率仅略高于 1。
注意结果包括交易成本,如果我们每日交易,结果会更糟。始终尝试最佳交易频率。
以下是最差的五个回撤期图表:
图 9.82 – 组合波动率最小化策略;最差的五个回撤期
最差的回撤期持续了一年,净回撤为 -18.22%。其他最差期间的净回撤幅度低于 -10%。
以下是 累积收益 图表:
图 9.83 – 组合波动率最小化策略;投资周期内的累积收益
我们看到累积收益持续增长,这是预期的,鉴于稳定性为 0.91。
以下是回报图表:
图 9.84 – 投资周期内投资组合波动率最小化策略; 回报
-0.3
至0.04
。
以下是滚动波动率图表:
图 9.85 – 投资周期内投资组合波动率最小化策略; 6 个月滚动波动率
0.18
以及滚动波动率约为0.1
。
以下是滚动夏普比率图表:
图 9.86 – 投资周期内投资组合波动率最小化策略; 6 个月滚动夏普比率
最小值为5.0
,最小值略高于-3.0
。
以下是前五次回撤期图表:
图 9.87 – 投资周期内投资组合波动率最小化策略; 前五次回撤期
前五次回撤期图表证实,如果我们通过更智能的进出规则避开最糟糕的回撤期,将极大地改善该策略的表现。
以下是月度回报、年度回报和月度回报分布图表:
图 9.88 – 投资周期内投资组合波动率最小化策略; 月度回报、年度回报和月度回报分布
月度回报表显示我们在 2010 年的前几个月没有交易。年度回报图表显示该策略每年都有盈利,但 2015 年除外。月度回报分布图表绘制了一个略微负偏态、小峰度的策略。
投资组合波动率最小化策略通常只对非日常交易有利。在这个例子中,我们采用了月度交易,实现了 0.93 的夏普比率,最大回撤为-18.2%。
月度交易的最大夏普比率策略
该策略基于哈利·马克维茨 1952 年的论文《投资组合选择》中的思想。简而言之,最佳投资组合位于有效边界上 - 一组在每个风险水平下具有最高预期投资组合回报的投资组合。
在该策略中,对于给定的股票,我们选择它们的权重,使其最大化投资组合的预期夏普比率 - 这样的投资组合位于有效边界上。
我们使用 PyPortfolioOpt
Python 库。要安装它,请使用本书提供的 conda
环境或以下命令:
pip install PyPortfolioOpt %matplotlib inline from zipline import run_algorithm from zipline.api import order_target_percent, symbols, set_commission, schedule_function, date_rules, time_rules from zipline.finance.commission import PerTrade import pandas as pd import pyfolio as pf import numpy as np from pypfopt.efficient_frontier import EfficientFrontier from pypfopt import risk_models from pypfopt import expected_returns import warnings warnings.filterwarnings('ignore') def initialize(context): context.stocks = \ symbols('DIS','WMT','DOW','CRM','NKE','HD','V','MSFT', 'MMM','CSCO','KO','AAPL','HON','JNJ','TRV', 'PG','CVX','VZ','CAT','BA','AMGN','IBM','AXP', 'JPM','WBA','MCD','MRK','GS','UNH','INTC') context.rolling_window = 252 set_commission(PerTrade(cost=5)) schedule_function(handle_data, date_rules.month_end(), time_rules.market_open(hours=1)) def handle_data(context, data): prices_history = data.history(context.stocks, "close", context.rolling_window, "1d") avg_returns = \ expected_returns.mean_historical_return(prices_history) cov_mat = risk_models.sample_cov(prices_history) efficient_frontier = EfficientFrontier(avg_returns, cov_mat) weights = efficient_frontier.max_sharpe() cleaned_weights = efficient_frontier.clean_weights() for stock in context.stocks: order_target_percent(stock, cleaned_weights[stock]) def analyze(context, perf): returns, positions, transactions = \ pf.utils.extract_rets_pos_txn_from_zipline(perf) pf.create_returns_tear_sheet(returns, benchmark_rets = None) start_date = pd.to_datetime('2010-1-1', utc=True) end_date = pd.to_datetime('2018-1-1', utc=True) results = run_algorithm(start = start_date, end = end_date, initialize = initialize, analyze = analyze, capital_base = 10000, data_frequency = 'daily', bundle ='quandl')
输出结果如下:
图 9.89 – 最大夏普比率策略;汇总收益和风险统计
该策略表现出稳定的稳定性,为 0.76
,尾部比率接近 1 (1.01
)。然而,该策略的年波动率非常高 (17.0%
)。
下面是最差五个回撤期图表:
图 9.90 – 最大夏普比率策略;最差五个回撤期
最差回撤期持续时间超过 2 年,净回撤幅度为 -21.14%。如果我们调整入场/出场规则以避免这个回撤期,结果将会大大改善。
下面是累积收益图表:
图 9.91 – 最大夏普比率策略;投资期内累积收益
累积收益图表显示了积极的稳定性。
下面是收益图表:
图 9.92 – 最大夏普比率策略;投资期内收益
收益图表显示该策略在投资期初非常成功。
下面是滚动波动率图表:
图 9.93 – 最大夏普比率策略;投资期内 6 个月滚动波动率
滚动波动率图表显示随着时间的推移,滚动波动率有所下降。
下面是滚动夏普比率图表:
图 9.94 – 最大夏普比率策略;投资期内 6 个月滚动夏普比率
5.0
,而其最小值高于 -3.0
。
下面是前五个回撤期图表:
图 9.95 – 最大夏普比率策略;投资期内前五个回撤期
前五个回撤期图表显示最大回撤期间很长。
下面是月度收益、年度收益和月度收益分布图表:
图 9.96 – 最大夏普比率策略;月度收益、年度收益以及投资期内月度收益的分布
月度收益表格证明我们几乎每个月都进行了交易。年度收益图表显示,除了 2016 年外,每年的年度收益都为正。月度收益分布图呈正偏态,具有轻微的峰度。
最大夏普比率策略通常只对非日常交易有利。
Python 金融交易实用指南(四)(5)https://developer.aliyun.com/article/1523806