量化交易入门(三十六)DMI指标Python实现和回测

本文详细介绍了使用DMI指标在Backtrader框架下对苹果股票进行回测的策略,评估了策略的年度化回报率、夏普比率和风险指标,如最大回撤。代码展示了策略逻辑和回测结果的解读。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先我们来看一张回测后打印的回测效果图以及回测后的结果,让我们看看这个指标在股票买卖中效果如何。

 

一、策略运行结果及解读

执行的结果:
Starting Portfolio Value: 100000.00 
Final Portfolio Value: 130565.60
Annualized Return: 6.91%
Sharpe Ratio: 0.39
Max Drawdown: 20.30%
Max Drawdown Period: 737

1、初始投资组合价值(Starting Portfolio Value)为100,000.00:

  • 这表示在回测开始时,我们的初始资金为100,000。

2、最终投资组合价值(Final Portfolio Value)为130,565.60:

  • 这表示在回测结束时,我们的投资组合价值增长到了130,565.60。
  • 从初始的100,000增长到130,565.60,表明我们的策略在回测期间获得了正回报。

3、年化回报率(Annualized Return)为6.91%:

  • 年化回报率表示策略在一年内可以获得的平均回报率,假设回报率保持不变。
  • 6.91%的年化回报率说明我们的策略在回测期间平均每年获得6.91%的回报。
  • 这是一个相对较好的回报率,但具体评估还需要考虑回测的时间范围和市场状况。

4、夏普比率(Sharpe Ratio)为0.39:

  • 夏普比率衡量策略的风险调整后收益。它表示每承受一单位风险,策略可以获得多少超额回报。
  • 夏普比率越高,表明策略的风险调整后收益越好。
  • 0.39的夏普比率说明我们的策略在考虑风险后获得了一定的超额回报,但还有提升的空间。

5、最大回撤(Max Drawdown)为20.30%:

  • 最大回撤表示从最高点到最低点的最大下跌百分比。
  • 20.30%的最大回撤说明在回测期间,我们的投资组合从最高点下跌了20.30%。
  • 这个数值衡量了策略在最糟糕情况下可能遭受的最大损失。
  • 较高的最大回撤表明策略的风险较大,可能需要优化风险管理。

6、最大回撤期间(Max Drawdown Period)为737:

  • 最大回撤期间表示从最高点到最低点的持续时间,通常以交易日或数据点的数量表示。
  • 737的最大回撤期间说明我们的投资组合从最高点到最低点经历了737个交易日或数据点。
  • 较长的最大回撤期间表明策略从高点回到之前的水平需要更长的时间,这可能对投资者的心理和资金管理产生负面影响。

综合来看,这个回测结果表明我们的DMI策略在回测期间获得了正回报,年化回报率为6.91%,夏普比率为0.39。然而,20.30%的最大回撤和737的最大回撤期间表明策略的风险较高,可能需要进一步优化风险管理。

二、代码实现

基于DMI(Dynamic Momentum Index)指标对苹果股票进行回测。以下是完整的代码:

import backtrader as bt
import yfinance as yf

class DMIStrategy(bt.Strategy):
    params = (
        ('period', 14),
        ('up_trend_threshold', 25),
        ('down_trend_threshold', 25),
    )

    def __init__(self):
        self.dmi = bt.indicators.DMI(period=self.params.period)
        self.crossover_dmi = bt.indicators.CrossOver(self.dmi.plusDI, self.dmi.minusDI)

    def next(self):
        if not self.position:
            if self.dmi.plusDI[-1] > self.params.up_trend_threshold and self.crossover_dmi > 0:
                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.dmi.minusDI[-1] > self.params.down_trend_threshold and self.crossover_dmi < 0:
                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(DMIStrategy)

# 设置佣金为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()

 三、代码解析

让我们详细解析这段代码:

1、导入必要的库:

  • backtrader是一个用于量化交易的Python框架。
  • yfinance是一个用于从Yahoo Finance下载历史股票数据的库。

2、定义DMIStrategy类,继承自bt.Strategy:

  • params是策略的参数,包括DMI指标的时间周期(period)、上升趋势阈值(up_trend_threshold)和下降趋势阈值(down_trend_threshold)。
  • __init__方法初始化策略,创建DMI指标(bt.indicators.DMI)和DMI的正负DI线的交叉信号(bt.indicators.CrossOver)。
  • next方法在每个新的数据点上被调用,包含策略的主要逻辑:
    • 如果没有持仓,并且+DI大于上升趋势阈值且+DI和-DI发生向上交叉,则执行买入操作。
    • 如果持有头寸,并且-DI大于下降趋势阈值且+DI和-DI发生向下交叉,则执行卖出操作。
  • notify_order方法在每次订单状态发生变化时被调用,用于跟踪订单的执行情况并打印相关信息。

3、创建Cerebro引擎:

  • cerebro = bt.Cerebro()创建一个新的Cerebro引擎实例。

4、设置初始资金:

  • cerebro.broker.setcash(100000.0)将初始资金设置为100,000。

5、下载苹果股票数据:

  • 使用yfinance库下载苹果股票(AAPL)从2020年1月1日到2023年12月30日的数据。
  • 使用dropna()函数删除数据中的任何缺失值。

6、将数据添加到Cerebro引擎中:

  • 将下载的数据转换为bt.feeds.PandasData格式。
  • 使用cerebro.adddata()将数据添加到Cerebro引擎中。

7、添加DMIStrategy策略:

  • cerebro.addstrategy(DMIStrategy)将DMIStrategy添加到Cerebro引擎中。

8、设置佣金:

  • cerebro.broker.setcommission(commission=0.001)将交易佣金设置为0.1%。

9、添加分析指标:

  • 添加三个分析指标:回报率(bt.analyzers.Returns)、夏普比率(bt.analyzers.SharpeRatio)和最大回撤(bt.analyzers.DrawDown)。

10、运行回测:

  • cerebro.run()运行回测并打印初始和最终的投资组合价值。

11、获取回测结果:

  • 从回测结果中提取策略对象(results[0])。
  • 获取回报率、夏普比率和最大回撤的分析结果。

12、打印回测指标:

  • 打印年化回报率、夏普比率、最大回撤百分比和最大回撤期间。

13、绘制回测结果:

  • cerebro.plot()绘制回测结果的图表。

这段代码展示了如何使用Backtrader框架和DMI指标对苹果股票进行回测。它下载历史数据,创建DMIStrategy策略,设置初始资金和

佣金,添加分析指标,运行回测,并打印和绘制回测结果。

让我们更详细地解释策略的关键部分:

1、在__init__方法中:

  • self.dmi = bt.indicators.DMI(period=self.params.period)创建了一个DMI指标,时间周期由self.params.period参数设置。
  • self.crossover_dmi = bt.indicators.CrossOver(self.dmi.plusDI, self.dmi.minusDI)创建了一个DMI的正负DI线的交叉信号。当+DI上穿-DI时,self.crossover_dmi的值为正;当-DI上穿+DI时,self.crossover_dmi的值为负。

2、在next方法中:

  • if not self.position检查当前是否持有头寸。如果没有持仓,则进一步检查买入条件:
    • if self.dmi.plusDI[-1] > self.params.up_trend_threshold and self.crossover_dmi > 0检查+DI是否大于上升趋势阈值,以及是否发生了+DI和-DI的向上交叉。
    • 如果满足买入条件,则计算可以买入的股票数量,并执行买入操作。
  • else表示当前持有头寸。在这种情况下,检查卖出条件:
    • if self.dmi.minusDI[-1] > self.params.down_trend_threshold and self.crossover_dmi < 0检查-DI是否大于下降趋势阈值,以及是否发生了+DI和-DI的向下交叉。
    • 如果满足卖出条件,则执行卖出操作,平掉所有头寸。

3、在notify_order方法中:

  • 当订单状态变为Completed时,打印买入或卖出的执行详情,包括执行日期、价格、成本和佣金。
  • 对于卖出订单,还计算并打印利润和利润百分比。

通过这个策略,我们可以根据DMI指标的状态和交叉信号来做出买入和卖出决策。当+DI上穿-DI且+DI大于上升趋势阈值时,我们买入股票;当-DI上穿+DI且-DI大于下降趋势阈值时,我们卖出股票。

这个示例展示了如何使用Backtrader框架来实现和回测一个基于DMI指标的交易策略。你可以进一步优化和调整这个策略,例如添加止损和止盈条件,结合其他技术指标,或者根据市场状况动态调整参数,以提高策略的性能和适应性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Coder加油!

感谢您的认可和支持!!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值