在讲MACD策略之前,先介绍几个名词。

名词解释

均线MA(N)
N日平均线的平均数 MA(N)=(C1+C2+C3+..)/N
MA就是一般的平均,一段时间内所有值加起来再除以该段时间内的值的个数
指数平滑移动平均线EMA(N)
EMA(N)=2/(n+1)*(今日收盘价-昨日EMA)
EMA是加权平均,可以理解为越近期的值越被重视,越早期的值越轻视。

好了,现在开始我们的正题,下面我们讲一下MACD策略。

MAC策略是什么

MACD是从双指数移动平均线发展而来的,由快的指数移动平均数(EMA12)减去慢的指数移动平均数(EMA26)
当MACD线上穿signal线的时候看涨。(买入信号)
当MACD线下穿signal线的时候看跌。(卖出信号)
其中signal线是MACD的EMA(9)

总结公式如下:

  • MACD=价格EMA(12) - 价格EMA(26).
  • signal线=MACD的EMA(9)

于是,我们有了下面的策略
买入:当MACD线在前一天的值 < 信号线前一天的值,同时,当天MACD线的值 > 当天信号线的值时,说明发生了金叉,此时看涨,第二天买入。
卖出:若已盈利10%,则卖出;若已亏损10%,则卖出。

代码

数据文件603186.csv,如下
603186.csv

  1. #-*- coding:utf-8 -*-
  2. # @Time:2021/5/19 11:12
  3. import datetime
  4. import backtrader as bt
  5. # dataclose[0] # 当日的收盘价
  6. # dataclose[-1] # 昨天的收盘价
  7. # dataclose[-2] # 前天的收盘价
  8. class TestStrategy(bt.Strategy):
  9. """
  10. 继承并构建自己的bt策略
  11. """
  12. def __init__(self):
  13. # 初始化相关数据
  14. self.dataclose = self.datas[0].close
  15. self.order = None
  16. self.buyprice = None
  17. self.buycomm = None
  18. # self.volume = self.datas[0].volume
  19. self.me12 = bt.indicators.EMA(self.data, period=12)
  20. self.me26 = bt.indicators.EMA(self.data, period=26)
  21. self.macd = self.me12 - self.me26
  22. self.signal = bt.indicators.EMA(self.macd, period=9)
  23. # bt.indicators.MACDHisto(self.data)
  24. def notify_order(self, order):
  25. """
  26. 订单状态处理
  27. :param order: 订单状态
  28. """
  29. if order.status in [order.Submitted, order.Accepted]:
  30. # 如订单已被处理,则不用做任何事情
  31. return
  32. # 检查订单是否完成
  33. if order.status in [order.Completed]:
  34. if order.isbuy():
  35. self.buyprice = order.executed.price
  36. self.buycomm = order.executed.comm
  37. self.bar_executed_close = self.dataclose[0]
  38. self.bar_exected = len(self)
  39. # 订单因为缺少资金之类的原因被拒绝执行
  40. elif order.status in [order.Canceled, order.Margin, order.Rejected]:
  41. self.log('Order Caceled/Margin/Rejected')
  42. # 订单状态处理完成,设为空
  43. self.order = None
  44. def notify_trade(self, trade):
  45. """
  46. 交易成果
  47. :param trade: 交易状态
  48. :return:
  49. """
  50. if not trade.isclosed:
  51. return
  52. # 显示交易的毛利率和净利润
  53. self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %
  54. (trade.pnl, trade.pnlcomm), doprint=True)
  55. def log(self, txt, dt=None, doprint=False):
  56. '''
  57. 日志函数, 用于统一输出日志格式
  58. '''
  59. dt = dt or self.datas[0].datetime.date(0)
  60. print('%s, %s' % (dt.isoformat(), txt))
  61. return
  62. def next(self):
  63. # 记录收盘价
  64. self.log('Close,%.2f' % self.dataclose[0])
  65. # 是否正在下单,如果是的话不能提交第二次订单
  66. if self.order:
  67. return
  68. # 是否已经买入
  69. if not self.position:
  70. # 如果没有持仓,若前一天MACD < Signal, 当天 Signal < MACD,则第二天买入
  71. if self.macd[-1] < self.signal[-1] and self.macd[0] > self.signal[0]:
  72. self.log('BUY CREATE, %.2f' % self.dataclose[0])
  73. self.order = self.buy()
  74. else:
  75. # 若已盈利10%,则卖出;若已亏损10%,则卖出。
  76. condition = (self.dataclose[0] - self.bar_executed_close) / self.dataclose[0]
  77. if condition > 0.1 or condition < -0.1:
  78. self.log('SELL CREATE, %.2f' % self.dataclose[0])
  79. self.order = self.sell()
  80. def stop(self):
  81. self.log(u'(金叉死叉有用吗) Ending Value %.2f' %
  82. (self.broker.getvalue()), doprint=True)
  83. def load_data():
  84. data = bt.feeds.GenericCSVData(
  85. dataname='603186.csv',
  86. fromdate=datetime.datetime(2010, 1, 1),
  87. todate=datetime.datetime(2020, 4, 12),
  88. dtformat='%Y%m%d',
  89. datetime=2,
  90. open=3,
  91. high=4,
  92. low=5,
  93. close=6,
  94. volume=10,
  95. reverse=True
  96. )
  97. return data
  98. def main():
  99. # 初始化模型
  100. cerebro = bt.Cerebro()
  101. # 构建策略
  102. strats = cerebro.addstrategy(TestStrategy)
  103. # 设定买入的股数
  104. cerebro.addsizer(bt.sizers.FixedSize, stake=100)
  105. data = load_data()
  106. #加载数据到模型
  107. cerebro.adddata(data)
  108. # 设定初始资金
  109. cerebro.broker.set_cash(10000)
  110. # 设定交易佣金(万五,交易额每满一万元收取5元佣金)
  111. cerebro.broker.setcommission(0.005)
  112. # 策略执行前的资金
  113. print('Starting Portfolio Value:%.2f' % cerebro.broker.get_value())
  114. # 策略执行
  115. cerebro.run()
  116. # 策略执行后的资金
  117. print('Final Portfolio Value:%.2f' % cerebro.broker.get_value())
  118. cerebro.plot()
  119. if __name__ == '__main__':
  120. main()

结果预览
image.png
Figure_0.png
由上图可以看到,8次操作中盈利了7次,总盈利2600。
虽然量化策略是有效果的,但是最重要的还是选股,如何选到最佳的股票,就需要多看财报、多动脑了,世界上没有不劳而获的财富!