YH_backtrader.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. import os
  2. import numpy as np
  3. from sqlalchemy import create_engine
  4. import pandas as pd
  5. import pymysql
  6. import backtrader as bt
  7. import backtrader.indicators as btind
  8. import datetime
  9. import math
  10. from datetime import datetime as dt
  11. import multiprocessing as mp
  12. from backtrader.feeds import PandasData
  13. from numba import jit, cuda, njit
  14. # import multiprocessing
  15. # import matplotlib
  16. class MyPandasData(PandasData):
  17. lines = ()
  18. params = ()
  19. '''
  20. lines = ('change_pct', 'net_amount_main', 'net_pct_main', 'net_amount_xl', 'net_pct_xl', 'net_amount_l', 'net_pct_l'
  21. , 'net_amount_m', 'net_pct_m', 'net_amount_s', 'net_pct_s',)
  22. params = (('change_pct', 7),
  23. ('net_amount_main', 8),
  24. ('net_pct_main', 9),
  25. ('net_amount_xl', 10),
  26. ('net_pct_xl', 11),
  27. ('net_amount_l', 12),
  28. ('net_pct_l', 13),
  29. ('net_amount_m', 14),
  30. ('net_pct_m', 15),
  31. ('net_amount_s', 16),
  32. ('net_pct_s', 17),
  33. )
  34. '''
  35. class TestStrategy(bt.Strategy):
  36. params = (
  37. ("num", 3),
  38. ('Volatility', 0),
  39. ('rate', 5), # 注意要有逗号!!
  40. )
  41. def log(self, txt, dt=None):
  42. ''' Logging function for this strategy'''
  43. dt = dt or self.datas[0].datetime.date(0)
  44. # print('%s, %s' % (dt.isoformat(), txt))
  45. def __init__(self):
  46. print('__init__', dt.now())
  47. print(f'{self.params.num}天波动率为{self.params.Volatility}%乖离率为{self.params.rate}', 'myPID is ', os.getpid())
  48. # self.num = num
  49. # self.Volatility = Volatility/100
  50. # Keep a reference to the "close" line in the data[0] dataseries
  51. self.dataclose = self.datas[0].close
  52. self.dataopen = self.datas[0].open
  53. self.high = self.datas[0].high
  54. self.low = self.datas[0].low
  55. self.volume = self.datas[0].volume
  56. # self.change_pct = self.datas[0].change_pct
  57. # self.net_amount_main = self.datas[0].net_amount_main
  58. # self.net_pct_main = self.datas[0].net_pct_main
  59. # self.net_amount_xl = self.datas[0].net_amount_xl
  60. # self.net_pct_xl = self.datas[0].net_pct_xl
  61. # self.net_amount_l = self.datas[0].net_amount_l
  62. # self.net_pct_l = self.datas[0].net_pct_l
  63. self.sma5 = btind.MovingAverageSimple(self.datas[0].close, period=5)
  64. self.sma10 = btind.MovingAverageSimple(self.datas[0].close, period=10)
  65. self.sma20 = btind.MovingAverageSimple(self.datas[0].close, period=20)
  66. self.yx = self.dataclose[0] > self.dataopen[0]
  67. self.lowest = btind.Lowest(self.params.num)
  68. self.highest = btind.Highest(self.params.num)
  69. self.vola = self.params.Volatility / 100
  70. self.rate = self.params.rate / 100
  71. def notify_order(self, order):
  72. """
  73. 订单状态处理
  74. Arguments:
  75. order {object} -- 订单状态
  76. """
  77. if order.status in [order.Submitted, order.Accepted]:
  78. # 如订单已被处理,则不用做任何事情
  79. return
  80. # 检查订单是否完成
  81. if order.status in [order.Completed]:
  82. if order.isbuy():
  83. self.buyprice = order.executed.price
  84. self.buycomm = order.executed.comm
  85. self.bar_executed = len(self)
  86. # 订单因为缺少资金之类的原因被拒绝执行
  87. elif order.status in [order.Canceled, order.Margin, order.Rejected]:
  88. pass
  89. # self.log('Order Canceled/Margin/Rejected')
  90. # 订单状态处理完成,设为空
  91. self.order = None
  92. def notify_trade(self, trade):
  93. """
  94. 交易成果
  95. Arguments:
  96. trade {object} -- 交易状态
  97. """
  98. if not trade.isclosed:
  99. return
  100. # 显示交易的毛利率和净利润
  101. # self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' % (trade.pnl, trade.pnlcomm))
  102. @njit
  103. def next(self):
  104. # print(self.num,self.Volatility)
  105. # Simply log the closing price of the series from the reference
  106. # self.sma20[-2] < self.sma20[-1] < self.sma20[0] and self.sma10[-2] < self.sma10[-1] < self.sma10[0]
  107. # and (self.sma5[-1] < self.sma10[-1])
  108. # and (self.net_pct_l[0] > 10) and (self.net_pct_xl[0] > 3) \
  109. # and (self.net_amount_main[-1] > 0) and (self.net_amount_main[0] > 0)
  110. if len(self) > self.params.num:
  111. # print(f'{self.params.num}日天最低值:{lowest},波动率为{self.params.Volatility/100}')
  112. if self.yx \
  113. and (((self.lowest[0] * (1 - self.vola)) < self.low[-2] < (self.lowest[0] * (1 + self.vola))) or (
  114. (self.lowest[0] * (1 - self.vola)) < self.low[-1] < (self.lowest[0] * (1 + self.vola)))) \
  115. and (self.dataclose[0] > self.sma5[0]) and self.sma5[0] > self.sma5[-1] \
  116. and (not self.position) and (self.sma5[0] > self.sma10[0]):
  117. # self.log('BUY CREATE, %.2f' % self.dataclose[0])
  118. self.order = self.buy()
  119. elif self.dataclose < self.sma5[0] or self.sma5[0] < self.sma10[0] \
  120. or (self.dataclose[0] > (self.sma5[0] * (1 + self.rate))) or \
  121. (((self.highest[0] * (1 - self.vola)) < self.high[-2] < (self.highest[0] * (1 + self.vola))) or (
  122. (self.highest[0] * (1 - self.vola)) < self.high[-1] < (self.highest[0] * (1 + self.vola)))):
  123. self.order = self.close()
  124. # self.log('Close, %.2f' % self.dataclose[0])
  125. def stop(self):
  126. # pass
  127. self.log(u'(MA趋势交易效果) Ending Value %.2f' % (self.broker.getvalue()))
  128. def err_call_back(err):
  129. print(f'出错啦~ error:{str(err)}')
  130. def to_df(lt):
  131. df = pd.DataFrame(list(lt), columns=['周期', '波动率', '乖离率', '盈利个数', '盈利比例', '总盈利', '平均盈利', '最大盈利',
  132. '最小盈利', '总亏损', '平均亏损', '最大亏损', '最小亏损'])
  133. df.sort_values(by=['周期', '波动率', '乖离率'], ascending=True, inplace=True)
  134. df = df.reset_index(drop=True)
  135. df.to_csv(r'D:\Daniel\策略\策略穷举.csv', index=True, encoding='utf-8', mode='w')
  136. print(df)
  137. def backtrader(list_date, table_list, result, result_change, result_change_fall, err_list):
  138. sttime = dt.now()
  139. engine = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/qmt_stocks?charset=utf8')
  140. cerebro = bt.Cerebro()
  141. # cerebro.addstrategy(TestStrategy, num=num, Volatility=Volatility, rate=rate)
  142. cerebro.addsizer(bt.sizers.FixedSize, stake=10000)
  143. cerebro.broker.setcash(100000.0)
  144. cerebro.broker.setcommission(0.005)
  145. for stock in table_list:
  146. # print(stock)
  147. stk_df = pd.read_sql_table(stock, engine)
  148. stk_df.time = pd.to_datetime(stk_df.time)
  149. data = MyPandasData(dataname=stk_df,
  150. fromdate=datetime.datetime(2010, 1, 1),
  151. todate=datetime.datetime(2022, 12, 31),
  152. datetime='time',
  153. open='open',
  154. close='close',
  155. high='high',
  156. low='low',
  157. volume='volume',
  158. # change_pct='change_pct',
  159. # net_amount_main='net_amount_main',
  160. # net_pct_main='net_pct_main',
  161. # net_amount_xl='net_amount_xl',
  162. # net_pct_xl='net_pct_xl',
  163. # net_amount_l='net_amount_l',
  164. # net_pct_l='net_pct_l',
  165. # net_amount_m='net_amount_m',
  166. # net_pct_m='net_pct_m',
  167. # net_amount_s='net_amount_s',
  168. # net_pct_s='net_pct_s',
  169. )
  170. cerebro.adddata(data, name=stock)
  171. print('取值完成', dt.now())
  172. cerebro.optstrategy(TestStrategy, num=range(60, 80, 20), Volatility=range(3, 7), rate=range(5, 12))
  173. print('最优参定义', dt.now())
  174. cerebro.addanalyzer(bt.analyzers.PyFolio)
  175. # 策略执行前的资金
  176. # print('启动资金: %.2f' % cerebro.broker.getvalue())
  177. try:
  178. # 策略执行
  179. print('开始执行', dt.now())
  180. cerebro.run(maxcpus=None)
  181. except IndexError:
  182. err_list.append(stock)
  183. else:
  184. if cerebro.broker.getvalue() > 100000.0:
  185. result_change.append((cerebro.broker.getvalue() / 10000 - 1))
  186. result.append(stock)
  187. # print('recode!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
  188. # print(result)
  189. else:
  190. result_change_fall.append((1 - cerebro.broker.getvalue() / 10000))
  191. # print('aaaaaaaaaaa')
  192. # print(result_change_fall)
  193. # if len(result) * len(result_change) * len(result_change_fall) != 0:
  194. # print(f'以{num}内最低值波动{Volatility}为支撑、乖离率为{rate}%,结果状态为:')
  195. # print('正盈利的个股为:', len(result_change), '成功率为:', len(result) / len(table_list))
  196. # print(
  197. # f'总盈利:{np.sum(result_change)} 平均盈利:{np.mean(result_change)},最大盈利:{np.max(result_change)}, 最小盈利:{np.min(result_change)}')
  198. # print(
  199. # f'总亏损:{np.sum(result_change_fall)},平均亏损:{np.mean(result_change_fall)},最大亏损:{np.min(result_change_fall)} 最小亏损:{np.max(result_change_fall)}')
  200. #
  201. # list_date.append([num, Volatility, rate, len(result), len(result) / len(table_list), np.nansum(result_change),
  202. # np.nanmean(result_change), np.nanmax(result_change), np.min(result_change),
  203. # np.nansum(result_change_fall), np.nanmean(result_change_fall),
  204. # np.nanmin(result_change_fall), np.nanmax(result_change_fall)])
  205. # to_df(list_date)
  206. # endtime = dt.now()
  207. # print(f'{num}天波动率为{Volatility}%乖离率为{rate},myPID is {os.getpid()}.本轮耗时为{endtime - sttime}')
  208. # else:
  209. # print(result, result_change, result_change_fall, num, Volatility, rate, err_list)
  210. # cerebro.plot()
  211. # df = pd.DataFrame(
  212. # columns=['周期', '波动率', '盈利个数', '盈利比例', '总盈利', '平均盈利', '最大盈利', '最小盈利', '总亏损',
  213. # '平均亏损', '最大亏损', '最小亏损'])
  214. if __name__ == '__main__':
  215. starttime = dt.now()
  216. print(starttime)
  217. # engine = create_engine('mysql+pymysql://root:r6kEwqWU9!v3@localhost:3307/hlfx?charset=utf8', poolclass=NullPool)
  218. # stocks = pd.read_sql_query(
  219. # 'select value from MA5_1d', engine_hlfx)
  220. fre = '1d'
  221. db = pymysql.connect(host='localhost',
  222. user='root',
  223. port=3307,
  224. password='r6kEwqWU9!v3',
  225. database='qmt_stocks')
  226. cursor = db.cursor()
  227. cursor.execute("show tables like '%%%s%%' " % fre)
  228. table_list = [tuple[0] for tuple in cursor.fetchall()]
  229. # print(table_list)
  230. table_list = table_list[0:100]
  231. list_date = mp.Manager().list()
  232. thread_list = []
  233. pool = mp.Pool(processes=mp.cpu_count())
  234. # for num in range(60, 100, 20):
  235. # for Volatility in range(3, 7, 1):
  236. # for rate in range(7, 9, 1):
  237. step = math.ceil(len(table_list) / mp.cpu_count())
  238. result = []
  239. result_change = []
  240. result_change_fall = []
  241. err_list = []
  242. # print(f'{num}天波动率为{Volatility}%乖离率为{rate}')
  243. backtrader(list_date, table_list, result, result_change, result_change_fall,
  244. err_list)
  245. # for i in range(0, len(table_list), step):
  246. stattime = dt.now()
  247. print(stattime)
  248. # thd = threading.local()
  249. # print(i)
  250. # p = mp.Process(target=backtrader, args=(df, table_list, result, result_change, result_change_fall,
  251. # num, Volatility, rate, err_list))
  252. # thread_list.append(p)
  253. # pool.apply_async(func=backtrader,
  254. # args=(list_date, table_list, result, result_change, result_change_fall,
  255. # num, Volatility, rate, err_list,), error_callback=err_call_back)
  256. # p.start()
  257. # p.join()
  258. # print(thread_list)
  259. # for thread in thread_list:
  260. # thread.start()
  261. # for thread in thread_list:
  262. # thread.join()
  263. pool.close()
  264. pool.join()
  265. edtime = dt.now()
  266. print('总耗时:', edtime - starttime)
  267. # df.to_csv(r'C:\Users\Daniel\Documents\策略穷举2.csv', index=True)