今天我们先单纯用CCI指标来完成策略的编写,后续我们会改进这个策略,将CCI指标和前面讲到的MACD和RSI相结合来优化,看看我们优化后的效果会不会更好。
一、量化策略
CCI指标在量化交易中的策略:
在以下情况下生成买入信号:
- 当 CCI 指标的值低于下限(self.params.lower)并且在上一根K线上低于下限时,生成买入信号。
在以下情况下生成卖出信号:
- 当 CCI 指标的值高于上限(self.params.upper)并且在上一根K线上高于上限时,生成卖出信号。
策略的目的是在 CCI 指标的值低于下限时买入,以获得较低的价格,并在 CCI 指标的值高于上限时卖出,以获得较高的价格,从而实现利润。
二、代码实现
我们基于CCI指标使用苹果股票2020年1月1日到2023年12月30日的历史数据进行回测。以下是完整的代码:
import backtrader as bt
import yfinance as yf
class CCIStrategy(bt.Strategy):
params = (
('period', 20),
('upper', 100),
('lower', -100),
)
def __init__(self):
self.cci = bt.indicators.CCI(self.data, period=self.params.period)
def next(self):
if not self.position:
if self.cci[-1] < self.params.lower and self.cci[0] >= self.params.lower:
commission_info = self.broker.getcommissioninfo(self.data)
cash = self.broker.get_cash()
size = int(cash / (self.data.close[0] * (1 + commission_info.p.commission)))
self.order = self.buy(size=size)
print(f'BUY: {size} shares')
else:
if self.cci[-1] > self.params.upper and self.cci[0] <= self.params.upper:
self.order = self.close()
print(f'SELL: {self.position.size} shares')
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
return
if order.status in [order.Completed]:
if order.isbuy():
print(f'BUY executed at {self.data.num2date(order.executed.dt).date()}, Price: {order.executed.price:.2f}, Cost: {order.executed.value:.2f}, Comm: {order.executed.comm:.2f}')
elif order.issell():
cost = order.executed.value
profit = order.executed.value - order.created.size * order.created.price
profit_percent = (profit / cost) * 100
print(f'SELL executed at {self.data.num2date(order.executed.dt).date()}, Price: {order.executed.price:.2f}, Cost: {cost:.2f}, Profit: {profit:.2f}, Profit %: {profit_percent:.2f}%')
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
print('Order Canceled/Margin/Rejected')
# 创建Cerebro引擎
cerebro = bt.Cerebro()
# 设置初始资金
cerebro.broker.setcash(100000.0)
# 下载苹果股票数据
data = yf.download('AAPL', '2020-01-01', '2023-12-30')
data = data.dropna()
# 将数据添加到Cerebro引擎中
data = bt.feeds.PandasData(dataname=data)
cerebro.adddata(data)
# 添加MACD策略
cerebro.addstrategy(CCIStrategy)
# 设置佣金为0.1%
cerebro.broker.setcommission(commission=0.001)
# 添加分析指标
cerebro.addanalyzer(bt.analyzers.Returns, _name='returns')
cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe')
cerebro.addanalyzer(bt.analyzers.DrawDown, _name='drawdown')
# 运行回测
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
results = cerebro.run()
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
# 获取回测结果
strat = results[0]
returns = strat.analyzers.returns.get_analysis()
sharpe = strat.analyzers.sharpe.get_analysis()
drawdown = strat.analyzers.drawdown.get_analysis()
# 打印回测指标
print('Annualized Return: %.2f%%' % (returns['rnorm100']))
print('Sharpe Ratio: %.2f' % (sharpe['sharperatio']))
print('Max Drawdown: %.2f%%' % (drawdown['max']['drawdown']))
print('Max Drawdown Period: %s' % (drawdown['max']['len']))
# 绘制回测结果
cerebro.plot()
三、代码解析
这段代码是一个使用 backtrader
库进行交易策略回测的 Python 脚本,并使用 yfinance
库获取历史金融数据。它具体实现了基于商品渠道指数(CCI)指标的交易策略。让我们来逐部分解析这些代码:
引入库
import backtrader as bt
import yfinance as yf
backtrader
: 一个用于回测交易算法的 Python 库。yfinance
: 用于从 Yahoo 财经下载金融数据。
定义策略:CCIStrategy
这个类继承自 bt.Strategy
,并使用 CCI 指标定义交易策略。
params
: 一个元组,定义策略参数 - CCI 周期,以及上下阈值。__init__
: 初始化方法,在这里使用价格数据和指定的周期实例化 CCI 指标。next
: 对于每个新的数据点,都会调用此方法。它包含了基于CCI值穿越定义阈值执行买入和卖出订单的逻辑。
交易逻辑
- 买入条件:如果没有开仓,并且CCI值从低于下阈值穿越到上面,则下达买入订单。
- 卖出条件:如果有开仓,并且CCI值从高于上阈值穿越到下面,则平仓(卖出订单)。
订单通知
notify_order
: 处理有关订单的通知(例如,当订单被提交、接受、完成或拒绝时)。它会打印有关执行订单的详细信息,包括执行价格、成本、佣金和利润。
设置并运行回测
- 创建一个
Cerebro
引擎实例,并设置初始资本。 - 使用
yfinance
获取苹果公司(AAPL)从2020年1月1日到2023年12月30日的历史数据,并添加到 Cerebro 引擎中。 - 将
CCIStrategy
添加到 Cerebro 中进行回测。 - 设置交易佣金为0.1%。
- 添加分析器,用于评估策略性能,包括回报率、夏普比率和最大回撤。
- 运行回测,并打印最终的投资组合价值以及性能指标(年化回报率、夏普比率和最大回撤)。
绘制结果
最后,调用 cerebro.plot()
来视觉上审查交易策略在回测期间的表现。
这段脚本提供了一个结构化方法来评估基于 CCI 指标的交易策略,包括交易的实际方面,如佣金和订单执行逻辑。
四、策略运行结果及解读
执行的结果:
Starting Portfolio Value: 100000.00
Final Portfolio Value: 110402.49
Annualized Return: 2.51%
Sharpe Ratio: 0.18
Max Drawdown: 22.65%
Max Drawdown Period: 441
哈哈,这个结果免强还行,最终结果没有亏钱,让我们逐项分析这些结果:
初始和最终投资组合价值
- 初始投资组合价值: 100,000.00
- 最终投资组合价值: 110,402.49
这意味着在回测期间,投资组合价值从 100,000 增加到了 110,402.49,实现了约 10.4% 的增长。这表明策略在整个回测期间是盈利的。
年化回报率
- 年化回报率: 2.51%
年化回报率是将投资收益率调整为一年期的标准度量,便于与其他投资或策略进行比较。2.51% 的年化回报率意味着,如果以相同的市场条件和策略表现,投资者可以期待每年获得约 2.51% 的回报。
夏普比率
- 夏普比率: 0.18
夏普比率是衡量风险调整后回报的指标,计算为超过无风险回报率的投资回报与投资的标准差(风险)之比。夏普比率越高,表示每承受一单位风险,能获得更多的超额回报。0.18 的夏普比率较低,表明策略产生的每单位风险调整后回报较少,或者说策略的风险相对于回报来说较高。
最大回撤
- 最大回撤: 22.65%
- 最大回撤期间: 441
最大回撤是指投资组合在选定的时期内从峰值跌到谷底的最大跌幅,是衡量投资风险的一项重要指标。22.65% 的最大回撤意味着在最糟糕的情况下,投资组合的价值可能会从峰值暂时性下降约 22.65%。这是一个相对较大的回撤,表明策略在回测期间承受了较高的风险。
最大回撤期间 441,意味着最大回撤发生在一个相对较长的时间框架内,这可能表明策略在这段时间内遇到了持续的不利市场条件。
结论
这个策略在回测期间实现了正收益,但年化回报率较低,且承担了较高的最大回撤风险。夏普比率也表明该策略的风险调整后回报不是特别高。因此,尽管策略是盈利的,投资者应该谨慎考虑与其他策略相比较时的风险与收益。