diff --git a/vnpy/trader/app/ctaStrategy/ctaBacktesting.py b/vnpy/trader/app/ctaStrategy/ctaBacktesting.py index c6a2e881..6b7fa509 100644 --- a/vnpy/trader/app/ctaStrategy/ctaBacktesting.py +++ b/vnpy/trader/app/ctaStrategy/ctaBacktesting.py @@ -955,6 +955,7 @@ class BacktestingEngine(object): resultList.sort(reverse=True, key=lambda result:result[1]) return resultList + #---------------------------------------------------------------------- def outputOptimizeResult(self, resultList): self.output('-' * 30) self.output(u'优化结果:') @@ -997,66 +998,71 @@ class BacktestingEngine(object): dailyResult.calculatePnl(openPosition, self.size, self.rate, self.slippage ) openPosition = dailyResult.closePosition - - # 生成DataFrame - resultDict = {k:[] for k in dailyResult.__dict__.keys()} - for dailyResult in self.dailyResultDict.values(): - for k, v in dailyResult.__dict__.items(): - resultDict[k].append(v) - - resultDf = pd.DataFrame.from_dict(resultDict) - - # 计算衍生数据 - resultDf = resultDf.set_index('date') - - return resultDf #---------------------------------------------------------------------- - def calculateDailyStatistics(self, df): + def calculateDailyStatistics(self, annualDays=240): """计算按日统计的结果""" - df['balance'] = df['netPnl'].cumsum() + self.capital - df['return'] = (np.log(df['balance']) - np.log(df['balance'].shift(1))).fillna(0) - df['highlevel'] = df['balance'].rolling(min_periods=1,window=len(df),center=False).max() - df['drawdown'] = df['balance'] - df['highlevel'] - df['ddPercent'] = df['drawdown'] / df['highlevel'] * 100 + dateList = self.dailyResultDict.keys() + resultList = self.dailyResultDict.values() - # 计算统计结果 - startDate = df.index[0] - endDate = df.index[-1] - - totalDays = len(df) - profitDays = len(df[df['netPnl']>0]) - lossDays = len(df[df['netPnl']<0]) + startDate = dateList[0] + endDate = dateList[-1] + totalDays = len(dateList) - endBalance = df['balance'].iloc[-1] - maxDrawdown = df['drawdown'].min() - maxDdPercent = df['ddPercent'].min() + profitDays = 0 + lossDays = 0 + endBalance = self.capital + highlevel = self.capital + totalNetPnl = 0 + totalTurnover = 0 + totalCommission = 0 + totalSlippage = 0 + totalTradeCount = 0 - totalNetPnl = df['netPnl'].sum() - dailyNetPnl = totalNetPnl / totalDays + netPnlList = [] + balanceList = [] + highlevelList = [] + drawdownList = [] + ddPercentList = [] + returnList = [] - totalCommission = df['commission'].sum() - dailyCommission = totalCommission / totalDays + for result in resultList: + if result.netPnl > 0: + profitDays += 1 + elif result.netPnl < 0: + lossDays += 1 + netPnlList.append(result.netPnl) + + prevBalance = endBalance + endBalance += result.netPnl + balanceList.append(endBalance) + returnList.append(endBalance/prevBalance - 1) + + highlevel = max(highlevel, endBalance) + highlevelList.append(highlevel) + + drawdown = endBalance - highlevel + drawdownList.append(drawdown) + ddPercentList.append(drawdown/highlevel*100) + + totalTurnover += result.turnover + totalCommission += result.commission + totalSlippage += result.slippage + totalTradeCount += result.tradeCount + totalNetPnl += result.netPnl - totalSlippage = df['slippage'].sum() - dailySlippage = totalSlippage / totalDays - - totalTurnover = df['turnover'].sum() - dailyTurnover = totalTurnover / totalDays - - totalTradeCount = df['tradeCount'].sum() - dailyTradeCount = totalTradeCount / totalDays - - totalReturn = (endBalance/self.capital - 1) * 100 - annualizedReturn = totalReturn / totalDays * 240 - dailyReturn = df['return'].mean() * 100 - returnStd = df['return'].std() * 100 + maxDrawdown = min(drawdownList) + maxDdPercent = min(ddPercentList) + totalReturn = (endBalance / self.capital - 1) * 100 + dailyReturn = np.mean(returnList) * 100 + annualizedReturn = dailyReturn * annualDays + returnStd = np.std(returnList) * 100 if returnStd: - sharpeRatio = dailyReturn / returnStd * np.sqrt(240) + sharpeRatio = dailyReturn / returnStd * np.sqrt(annualDays) else: sharpeRatio = 0 - + # 返回结果 result = { 'startDate': startDate, @@ -1068,30 +1074,39 @@ class BacktestingEngine(object): 'maxDrawdown': maxDrawdown, 'maxDdPercent': maxDdPercent, 'totalNetPnl': totalNetPnl, - 'dailyNetPnl': dailyNetPnl, + 'dailyNetPnl': totalNetPnl/totalDays, 'totalCommission': totalCommission, - 'dailyCommission': dailyCommission, + 'dailyCommission': totalCommission/totalDays, 'totalSlippage': totalSlippage, - 'dailySlippage': dailySlippage, + 'dailySlippage': totalSlippage/totalDays, 'totalTurnover': totalTurnover, - 'dailyTurnover': dailyTurnover, + 'dailyTurnover': totalTurnover/totalDays, 'totalTradeCount': totalTradeCount, - 'dailyTradeCount': dailyTradeCount, + 'dailyTradeCount': totalTradeCount/totalDays, 'totalReturn': totalReturn, 'annualizedReturn': annualizedReturn, 'dailyReturn': dailyReturn, 'returnStd': returnStd, 'sharpeRatio': sharpeRatio - } + } - return df, result + d = {} + d['balance'] = balanceList + d['return'] = returnList + d['highLevel'] = highlevelList + d['drawdown'] = drawdownList + d['ddPercent'] = ddPercentList + d['date'] = dateList + d['netPnl'] = netPnlList + + return d, result #---------------------------------------------------------------------- - def showDailyResult(self, df=None, result=None): + def showDailyResult(self, d=None, result=None): """显示按日统计的交易结果""" - if df is None: - df = self.calculateDailyResult() - df, result = self.calculateDailyStatistics(df) + if d is None: + self.calculateDailyResult() + d, result = self.calculateDailyStatistics() # 输出统计结果 self.output('-' * 30) @@ -1131,23 +1146,23 @@ class BacktestingEngine(object): pBalance = plt.subplot(4, 1, 1) pBalance.set_title('Balance') - df['balance'].plot(legend=True) + plt.plot(d['date'], d['balance']) pDrawdown = plt.subplot(4, 1, 2) pDrawdown.set_title('Drawdown') - pDrawdown.fill_between(range(len(df)), df['drawdown'].values) + pDrawdown.fill_between(range(len(d['drawdown'])), d['drawdown']) pPnl = plt.subplot(4, 1, 3) pPnl.set_title('Daily Pnl') - df['netPnl'].plot(kind='bar', legend=False, grid=False, xticks=[]) + plt.bar(range(len(d['drawdown'])), d['netPnl']) pKDE = plt.subplot(4, 1, 4) pKDE.set_title('Daily Pnl Distribution') - df['netPnl'].hist(bins=50) + plt.hist(d['netPnl'], bins=50) plt.show() - - + + ######################################################################## class TradingResult(object): """每笔交易的结果"""