五 量化交易全流程(12)

< self.__sma2[-1]def exitShortSignal(self):return self.__sma1[-1] > self.__sma2[-1]def enterShortSignal(self):return self.__sma1[-1] < self.__sma2[-1]# 拉取数据stock_data , 填充到onbars里面 , 特别要注意GenericBarFeed、bar.BasicBar里面的参数传递有哪些def get_stock_data(self, stock_code, start_date, end_date):conn = pymysql.connect(host='127.0.0.1', user='root', password='152617', port=3306, db='stock_info', charset='utf8')cur = conn.cursor()sql = f"select * from `stocks` where stock_code = {stock_code} and date > {start_date} and date < {end_date}"cursor = conn.cursor()cursor.execute(sql)rows = cursor.fetchall()rows = pd.DataFrame(rows)rows = rows.rename(columns={0: "date", 1: "stock_code", 2: "open", 3: "high", 4: "low", 5: "close", 6: "volume"})rows = self.data_convert(rows)#print(rows)bars = GenericBarFeed(Frequency.DAY, None, None)for i in range(len(rows)):date_time = rows['date'][i]# print(date_time)bar_data = http://www.kingceram.com/post/bar.BasicBar(date_time, rows['open'][i], rows['high'][i], rows['low'][i], /rows['close'][i], rows['volume'][i], None, Frequency.DAY)# print(bar_data)bars.addBarsFromSequence(stock_code, [bar_data])# 关闭连接conn.close()return bars# 根据MA交叉信号进行交易决策 , 买入后只买一次def onBars(self, bars):if self.__sma1[-1] is None or self.__sma2[-1] is None: # 检查指标是否可用returnbar = bars[self.__stock_code] # 拉取每一天k线数据if self.__longPos is not None:# 检查是否存在多头持仓 , 若存在 , 检查是否有退出多头持仓信号if self.exitLongSignal():self.__longPos.exitMarket()elif self.__shortPos is not None:if self.exitShortSignal():self.__shortPos.exitMarket()else:# 既无多头也无空头持仓 , 判断是否需要进入多头持仓# 或空头if self.enterLongSignal(bar):shares = int(self.getBroker().getCash() / bars[self.__stock_code].getPrice() * 0.8)# 计算可购买股票数量 , 0.8是成交委托比self.__longPos = self.enterLong(self.__stock_code,shares,True)# elif self.enterShortSignal(bar):#shares = int(self.getBroker().getCash() / bars[self.__stock_code].getPrice() * 0.8)#self.__shortPos = self.enterShort(self.__stock_code, shares, True)def run_strategy(smaPeriod1,smaPeriod2, stocks_code, start_date, end_date, par_value):# feed = GenericBarFeed(Frequency.DAY,None,None)# feed.addBarsFromCSV("stock_code","stockdt") # 从本地文件读取csv数据myStrategy = MyStrategy(None, stocks_code, smaPeriod1, smaPeriod2, par_value)bars = myStrategy.get_stock_data(stocks_code, start_date, end_date)myStrategy.setFeed(bars, stocks_code, smaPeriod1, smaPeriod2, par_value)retAnalyzer = returns.Returns()myStrategy.attachAnalyzer(retAnalyzer)plt = plotter.StrategyPlotter(myStrategy)plt.getInstrumentSubplot(stocks_code).addDataSeries('SMA1',myStrategy.getSMA()[0])plt.getInstrumentSubplot(stocks_code).addDataSeries('SMA2',myStrategy.getSMA()[1])plt.getOrCreateSubplot('returns').addDataSeries('returns',retAnalyzer.getReturns())myStrategy.run()plt.plot()# sharpeRatioAnalyzer = sharpe.SharpeRatio()# returned = retAnalyzer.getReturns()# returned = returned.dropna()# sharpeRatio = sharpeRatioAnalyzer.getSharpeRatio(returned, riskFreeRate=0.05)# print("Sharpe Ratio: %.2f" % sharpeRatio)# myStrategy.attachAnalyzer(sharpeRatioAnalyzer)# print("Sharpe ratio: %.2f" % sharpeRatioAnalyzer.getSharpeRatio(0.05))print("Final portfolio value: %.4f" % myStrategy.getBroker().getEquity())ma1 = 3ma2 = 7stock_codes = '000001'start_date = '20230104'end_date = '20230904'par_value = http://www.kingceram.com/post/100000run_strategy(ma1, ma2, stock_codes, start_date, end_date, par_value)
详细的代码注解都有注释 , 有几点强调:
该策略回测效果并不好 。
未设置手续费和滑点等参数 , 有效性和可靠性有待校正 。
操作的买卖信号被定义成了买入后不会追加 , 且不能做空 。