def __test_returns(self): p = Performance() df = pd.DataFrame(index=pd.date_range('2010-01-01', periods=10), columns=[ 'returns', ]) df['returns'] = [ 0.1, 0.1, 0.1, -0.156, -0.1, 0.1, 0.1, 0.1, -0.11, -0.1 ] #print(df) r = df['returns'] ret = p.calc_period_return(r) ret_annual = p.calc_annual_return(r) print(stats.cum_returns_final(r)) #收益率 self.assertEqual(ret, stats.cum_returns_final(r)) self.assertEqual(ret_annual, stats.annual_return(r)) #print('标准差-年化波动率',) #print(stats.annual_volatility(df['returns'])) #波动率,夏普比 self.assertEqual(p.calc_volatility(r), stats.annual_volatility(r)) self.assertEqual(p.calc_sharpe(r), stats.sharpe_ratio(r))
def ret_vol_combos(ret1, ret2, nsteps, period='monthly', rebal_period=3): """Return and volatility that results from combining two return series in proportions from 0 to 100%. Args: - ret1: return series 1 (sequence of period returns, such as output by computePortfolioReturns()) - ret2: return series 2 - nsteps: number of steps to take in allocation to the returns, between 0 and 100% - period: return interval in `ret1`, `ret2`: {'daily', 'weekly', 'monthly'} - rebal_period: interval for rebalancing the allocation; expressed as number of periods of type `period` Return: - Dataframe with results for each allocation between 0 and 100% - annualized return - annualized standard deviation - annualized downside deviation - Date range of returns used for these results """ # convert period returns to wr's ret1 = ret1.copy() + 1 ret2 = ret2.copy() + 1 # combine the two return sequences combined = pd.concat([ret1, ret2], axis=1, join='inner') # Note: the concat/join ensures we have the same date range for both return sequences; # computing the date range here ensures that it accurately reflects dates to compute results date_range = DateRange( min(combined.index).date(), max(combined.index).date()) stepsize = 1 / nsteps return_list = [] vol_list = [] downside_list = [] w1_list = [] for ix in range(nsteps + 1): w1 = 1 - ix * stepsize w2 = ix * stepsize res = rfp.pf_period_returns(combined, [w1, w2], rebal_period, f'combined_{w1:4.2}') # print(res.head()) ann_ret = estats.annual_return(res.values, period=period) ann_vol = estats.annual_volatility(res.values, period=period) ann_downside = estats.downside_risk( res.values, period=period) # option to add `required_return` parameter #print(w1, w2, ann_ret, ann_vol) return_list.append(ann_ret[0]) vol_list.append(ann_vol[0]) downside_list.append(ann_downside[0]) w1_list.append(w1) df = pd.DataFrame({ 'w1': w1_list, 'ann_ret': return_list, 'standard_dev': vol_list, 'downside_dev': downside_list }) return df, date_range
def get_summary(self, freq='m'): if freq == 'm': monthlyRet = self.MonthlyRet(False) monthlyBenchmarkRet = self.BenchmarkMonthlyRet() hedgeMonthlyRet = self.MonthlyRet() _dict = { 'annual_return': stats.annual_return(hedgeMonthlyRet, annualization=12), 'annual_vol': stats.annual_volatility(hedgeMonthlyRet, annualization=12), 'IR': stats.information_ratio(monthlyRet, monthlyBenchmarkRet), 't_stats': stats_scp.ttest_1samp(hedgeMonthlyRet, 0)[0], 'win_rate': stats.win_rate(monthlyRet, monthlyBenchmarkRet), 'max_drawdown': stats.max_drawdown(hedgeMonthlyRet), 'drawn_down_start': pf.timeseries.get_top_drawdowns(hedgeMonthlyRet, 1)[0][0], 'draw_down_end': pf.timeseries.get_top_drawdowns(hedgeMonthlyRet, 1)[0][1], } else: _dict = { 'annual_return': stats.annual_return(self.activeRet, annualization=12), 'annual_vol': stats.annual_volatility(self.activeRet, annualization=12), 'IR': stats.information_ratio(self.Ret, self.BenchmarkRet), 't_stats': stats_scp.ttest_1samp(self.activeRet, 0)[0], 'win_rate': stats.win_rate(self.Ret, self.BenchmarkRet), 'max_drawdown': stats.max_drawdown(self.activeRet), 'drawn_down_start': pf.timeseries.get_top_drawdowns(self.activeRet, 1)[0], 'draw_down_end': pf.timeseries.get_top_drawdowns(self.activeRet, 1)[1], } return pd.Series(_dict)
def default_stats_spec(period='weekly', ann_rf_rate=0): """Generate specs for some standard statistics, given annual risk-free rate and return period of data.""" per_rf_rate = _ann_rate_to_period_rate(ann_rf_rate, period) stats_spec = { 'Annual Return': lambda x: estats.annual_return(x, period), 'Max Drawdown': estats.max_drawdown, 'Annual Volatility': lambda x: estats.annual_volatility(x, period), 'Sharpe Ratio': lambda x: estats.sharpe_ratio(x, risk_free=per_rf_rate, period=period), 'Downside Volatility': lambda x: estats.downside_risk(x, period=period), 'Sortino Ratio': lambda x: estats.sortino_ratio(x, period=period) } return stats_spec
def total_performance(self): """策略全局表现""" win_rate_func = lambda x, y: stats.win_rate( x, factor_returns=0, period=y) df = pd.DataFrame( [[ self.abs_total_return, self.total_benchmark_return, self.rel_total_return ], [ self.abs_maxdrawdown, stats.max_drawdown(self.benchmark_return), self.rel_maxdrawdown ], [ self.abs_annual_volatility, stats.annual_volatility(self.benchmark_return), self.rel_annual_volatility ], [ self.abs_annual_return, stats.annual_return(self.benchmark_return), self.rel_annual_return ], [ self.abs_sharp_ratio, stats.sharpe_ratio(self.benchmark_return, simple_interest=True), self.rel_sharp_ratio ], [ win_rate_func(self.portfolio_return, 'monthly'), win_rate_func(self.benchmark_return, 'monthly'), win_rate_func(self.active_return, 'monthly') ]], columns=['portfolio', 'benchmark', 'hedge'], index=[ 'total_return', 'maxdd', 'volatility', 'annual_return', 'sharp', 'win_rate' ]) return df
def portfolio_performance(weights, daily_returns, overall_returns=None, previous_weights=None): """ Calculates performance of a portfolio """ returns = np.matmul(daily_returns, weights) # Add statistics present in SIMPLE_STAT_FUNCS statistics = list([]) statistics.append(stats.annual_return(returns)) statistics.append(stats.annual_volatility(returns)) statistics.append(nan_to_zero(stats.sharpe_ratio(returns))) statistics.append(stats.sortino_ratio(returns)) statistics.append(stats.omega_ratio(returns)) statistics.append(stats.calmar_ratio(returns)) statistics.append(stats.tail_ratio(returns)) statistics.append(ssd(returns)) statistics.append(scipy.stats.skew(returns)) statistics.append(scipy.stats.kurtosis(returns)) statistics.append(turnover(weights, previous_weights)) if overall_returns is not None: overall_returns.extend(returns) return statistics
def calc(self, df): print('计算绩效') df['equity'] = (df['returns'] + 1).cumprod() df['bench_equity'] = (df['bench_returns'] + 1).cumprod() self.period_return = df['equity'][-1] - 1 self.benchmark_return = df['bench_equity'][-1] - 1 self.trading_days = len(df) #交易天数 self.annu_return = self.period_return * 252 / self.trading_days self.bench_annu_return = self.benchmark_return * 252 / self.trading_days # 波动率 self.volatility = stats.annual_volatility(df['returns']) # 夏普比率 self.sharpe = stats.sharpe_ratio(df['returns']) # 最大回撤 self.max_drawdown = stats.max_drawdown(df['returns'].values) #信息比率 # self.information = stats.information_ratio(df['returns'].values,df['benchmark_returns'].values) self.alpha, self.beta = stats.alpha_beta_aligned( df['returns'].values, df['bench_returns'].values) return { 'returns': self.period_return, 'annu_returns': self.annu_return, 'bench_returns': self.benchmark_return, 'bench_annu_returns': self.bench_annu_return, 'trading_days': self.trading_days, 'max_drawdown': self.max_drawdown, 'volatility': self.volatility, 'sharpe': self.sharpe, 'alpha': self.alpha, 'beta': self.beta }
def AnnualVol(self, hedge=True): ret = self.activeRet if hedge else self.Ret return stats.annual_volatility(ret, period=self.Freq)
def rel_annual_volatility(self): return stats.annual_volatility(self.active_return)
def abs_annual_volatility(self): return stats.annual_volatility(self.portfolio_return)