def test_omega_ratio(self, test_risk_free, test_required_return): res_a = empyrical.omega_ratio(ret['a'], risk_free=test_risk_free, required_return=test_required_return) if np.isnan(res_a): res_a = np.inf res_b = empyrical.omega_ratio(ret['b'], risk_free=test_risk_free, required_return=test_required_return) if np.isnan(res_b): res_b = np.inf res_c = empyrical.omega_ratio(ret['c'], risk_free=test_risk_free, required_return=test_required_return) if np.isnan(res_c): res_c = np.inf assert isclose( ret['a'].vbt.returns.omega_ratio( risk_free=test_risk_free, required_return=test_required_return), res_a) pd.testing.assert_series_equal( ret.vbt.returns.omega_ratio(risk_free=test_risk_free, required_return=test_required_return), pd.Series([res_a, res_b, res_c], index=ret.columns).rename('omega_ratio')) pd.testing.assert_series_equal( ret.vbt.returns.rolling_omega_ratio( ret.shape[0], minp=1, risk_free=test_risk_free, required_return=test_required_return).iloc[-1], pd.Series([res_a, res_b, res_c], index=ret.columns).rename(ret.index[-1]))
def single_sim(self): bootret_by_year = [] cumulative_lengths = [] data_panel = [] for i in range(self.holding_period): index_start = self.data.sample().index[0] index_start = self.data.index.get_loc(index_start) L_i = self.holding_period + 1 while L_i > self.holding_period: L_i = geom.rvs(p = 1/self.mean_block_length) cumulative_lengths.append(L_i) if sum(cumulative_lengths) > self.holding_period: L_final = self.holding_period - sum(cumulative_lengths[:-1]) if L_final > len(self.data) - index_start: diff = L_final - (len(self.data) - index_start) subsample_generated = self.data.iloc[index_start-diff: (index_start-diff + L_final), :] else: subsample_generated = self.data.iloc[index_start: index_start + L_final, :] data_panel.append(subsample_generated) break else: subsample_generated = self.data.iloc[index_start: index_start + L_i, :] if L_i > len(self.data) - index_start : L_i = len(self.data) - index_start data_panel.append(subsample_generated) cumulative_lengths[-1] = L_i bootstrapSample = pd.concat([subsample for subsample in data_panel], axis = 0, ignore_index = True) if self.stress_freq: historical_ret_by_year = self.data @ np.array([self.w1_stock, self.w2_bond, self.w3_gold]).T year_min_ret = historical_ret_by_year.idxmin() for i in range(self.holding_period): extreme_event_dummy = True if np.random.rand() < 0.05 else False if extreme_event_dummy: if self.stress_intensity == 1: bootstrapSample.iloc[i,:] = self.data.loc[year_min_ret,:] else: bootstrapSample.iloc[i,:] = self.data.loc[year_min_ret,:] bootstrapSample.iloc[i,:] *= 1.5 total_ret_by_year = bootstrapSample @ np.array([self.w1_stock, self.w2_bond, self.w3_gold]).T total_ret_by_year -= self.TER portfolio_path = self.capital * np.cumprod(total_ret_by_year + 1) cagr = (portfolio_path.values[-1] / self.capital) ** (1/self.holding_period) - 1 annual_volatility = total_ret_by_year.std() maxDrawdown = max_drawdown(pd.Series(total_ret_by_year)) omega_ratio2 = omega_ratio(pd.Series(total_ret_by_year), required_return = 0.02, annualization = 1) omega_ratio4 = omega_ratio(pd.Series(total_ret_by_year), required_return = 0.04, annualization = 1) omega_ratio8 = omega_ratio(pd.Series(total_ret_by_year), required_return = 0.08, annualization = 1) return (np.insert(portfolio_path.values, 0, self.capital), cagr, annual_volatility, maxDrawdown, omega_ratio2, omega_ratio4, omega_ratio8)
def evaluation(self): ap.sound(f'entry: create_df') mdd = empyrical.max_drawdown(self.df.eac_stgy_rt) stgy_ret_an = empyrical.annual_return(self.df.eac_stgy_rt, annualization=self.cls.annualization) bcmk_ret_an = empyrical.annual_return(self.df.eac_bcmk_rt, annualization=self.cls.annualization) stgy_vlt_an = empyrical.annual_volatility(self.df.eac_stgy_rt, annualization=self.cls.annualization) bcmk_vlt_an = empyrical.annual_volatility(self.df.eac_bcmk_rt, annualization=self.cls.annualization) calmar = empyrical.calmar_ratio(self.df.eac_stgy_rt, annualization=self.cls.annualization) omega = empyrical.omega_ratio(self.df.eac_stgy_rt, risk_free=self.cls.rf, annualization=self.cls.annualization) sharpe = qp.sharpe_ratio(stgy_ret_an, self.df.cum_stgy_rt, self.cls.rf) sortino = empyrical.sortino_ratio(self.df.eac_stgy_rt, annualization=self.cls.annualization) dsrk = empyrical.downside_risk(self.df.eac_stgy_rt, annualization=self.cls.annualization) information = empyrical.information_ratio(self.df.eac_stgy_rt, factor_returns=self.df.eac_bcmk_rt) beta = empyrical.beta(self.df.eac_stgy_rt, factor_returns=self.df.eac_bcmk_rt, risk_free=self.cls.rf) tail_rt = empyrical.tail_ratio(self.df.eac_stgy_rt) alpha = qp.alpha_ratio(stgy_ret_an, bcmk_ret_an, self.cls.rf, beta) stgy_ttrt_rt = (self.cls.yd.ttas[-1] - self.cls.yd.ttas[0]) / self.cls.yd.ttas[0] bcmk_ttrt_rt = (self.cls.pc.close[-1] - self.cls.pc.close[0]) / self.cls.pc.close[0] car_rt = stgy_ttrt_rt - bcmk_ttrt_rt car_rt_an = stgy_ret_an - bcmk_ret_an self.cls.df_output = pd.DataFrame( {'sgty_ttrt_rt': [stgy_ttrt_rt], 'bcmk_ttrt_rt': [bcmk_ttrt_rt], 'car_rt': [car_rt], 'stgy_ret_an': [stgy_ret_an], 'bcmk_ret_an': [bcmk_ret_an], 'car_rt_an': [car_rt_an], 'stgy_vlt_an': [stgy_vlt_an], 'bcmk_vlt_an': [bcmk_vlt_an], 'mdd': [mdd], 'sharpe': [sharpe], 'alpha': [alpha], 'beta': [beta], 'information': [information], 'tail_rt': [tail_rt], 'calmar': [calmar], 'omega': [omega], 'sortino': [sortino], 'dsrk': [dsrk]}) print(f'feedback: \n{self.cls.df_output.T}')
def omega_ratio(returns, annual_return_threshhold=0.0): """ Determines the Omega ratio of a strategy. Parameters ---------- returns : pd.Series Daily returns of the strategy, noncumulative. - See full explanation in :func:`~pyfolio.timeseries.cum_returns`. annual_return_threshold : float, optional Minimum acceptable return of the investor. Annual threshold over which returns are considered positive or negative. It is converted to a value appropriate for the period of the returns for this ratio. E.g. An annual minimum acceptable return of 100 translates to a daily minimum acceptable return of 0.01848. (1 + 100) ** (1. / 252) - 1 = 0.01848 Daily returns must exceed this value to be considered positive. The daily return yields the desired annual return when compounded over the average number of business days in a year. (1 + 0.01848) ** 252 - 1 = 99.93 - Defaults to 0.0 Returns ------- float Omega ratio. Note ----- See https://en.wikipedia.org/wiki/Omega_ratio for more details. """ return empyrical.omega_ratio(returns, required_return=annual_return_threshhold)
def _get_reward(self) -> float: """ This method computes the reward from each action, by looking at the annualized ratio, provided in the reward_function :return: annualized value of the selected reward ratio """ lookback = min(self.current_step, self._returns_lookback) returns = np.diff(self.portfolio[-lookback:]) if np.count_nonzero(returns) < 1: return 0 if np.count_nonzero(returns) < 1: return 0 if self._reward_function == 'sortino': reward = sortino_ratio(returns, annualization=365 * 24) elif self._reward_function == 'calmar': reward = calmar_ratio(returns, annualization=365 * 24) elif self._reward_function == 'omega': reward = omega_ratio(returns, annualization=365 * 24) else: reward = returns[-1] return reward if np.isfinite(reward) else 0
def omega_ratio(returns, annual_return_threshhold=0.0): """ Determines the Omega ratio of a strategy. Parameters ---------- returns : pd.Series Daily returns of the strategy, noncumulative. - See full explanation in :func:`~pyfolio.timeseries.cum_returns`. annual_return_threshold : float, optional Minimum acceptable return of the investor. Annual threshold over which returns are considered positive or negative. It is converted to a value appropriate for the period of the returns for this ratio. E.g. An annual minimum acceptable return of 100 translates to a daily minimum acceptable return of 0.01848. (1 + 100) ** (1. / 252) - 1 = 0.01848 Daily returns must exceed this value to be considered positive. The daily return yields the desired annual return when compounded over the average number of business days in a year. (1 + 0.01848) ** 252 - 1 = 99.93 - Defaults to 0.0 Returns ------- float Omega ratio. Note ----- See https://en.wikipedia.org/wiki/Omega_ratio for more details. """ return ep.omega_ratio(returns, required_return=annual_return_threshhold)
def test_omega(self, returns, risk_free, required_return, expected): assert_almost_equal( empyrical.omega_ratio( returns, risk_free=risk_free, required_return=required_return), expected, DECIMAL_PLACES)
def calculate_empyrical(fund_type): filename = '%s/%s_return.pkl'%(const.FOF_DIR, fund_type) ret_df = pd.read_pickle(filename) # Omega Ratio df = pd.DataFrame(index=ret_df.columns) df.loc[:, 'omega'] = ret_df.apply(lambda x: empyrical.omega_ratio(x)) fname = '%s/%s_empyrical.xlsx'%(const.FOF_DIR, fund_type) df.to_excel(fname)
def plot(self): # show a plot of portfolio vs mean market performance df_info = pd.DataFrame(self.infos) df_info.set_index('current step', inplace=True) # df_info.set_index('date', inplace=True) rn = np.asarray(df_info['portfolio return']) try: spf = df_info['portfolio value'].iloc[1] # Start portfolio value epf = df_info['portfolio value'].iloc[-1] # End portfolio value pr = (epf - spf) / spf except: pr = 0 try: sr = sharpe_ratio(rn) except: sr = 0 try: sor = sortino_ratio(rn) except: sor = 0 try: mdd = max_drawdown(rn) except: mdd = 0 try: cr = calmar_ratio(rn) except: cr = 0 try: om = omega_ratio(rn) except: om = 0 try: dr = downside_risk(rn) except: dr = 0 print("First portfolio value: ", np.round(df_info['portfolio value'].iloc[1])) print("Last portfolio value: ", np.round(df_info['portfolio value'].iloc[-1])) title = self.strategy_name + ': ' + 'profit={: 2.2%} sharpe={: 2.2f} sortino={: 2.2f} max drawdown={: 2.2%} calmar={: 2.2f} omega={: 2.2f} downside risk={: 2.2f}'.format( pr, sr, sor, mdd, cr, om, dr) # df_info[['market value', 'portfolio value']].plot(title=title, fig=plt.gcf(), figsize=(15,10), rot=30) df_info[['portfolio value']].plot(title=title, fig=plt.gcf(), figsize=(15, 10), rot=30)
def omega_ratio(close, risk_free=0.0, required_return=0.0, trading_days=252): try: rets = daily_returns(close) omr_data = empyrical.omega_ratio(rets, risk_free=risk_free, required_return=required_return, annualization=trading_days) return omr_data except Exception as e: raise (e)
def _get_reward(self, current_prices, next_prices): if self.compute_reward == compute_reward.profit: returns_rate = next_prices / current_prices # pip_value = self._calculate_pip_value_in_account_currency(account_currency.USD, next_prices) # returns_rate = np.multiply(returns_rate, pip_value) log_returns = np.log(returns_rate) last_weight = self.current_weights securities_value = self.current_portfolio_values[:-1] * returns_rate self.current_portfolio_values[:-1] = securities_value self.current_weights = self.current_portfolio_values / np.sum( self.current_portfolio_values) reward = last_weight[:-1] * log_returns elif self.compute_reward == compute_reward.sharpe: try: sr = sharpe_ratio(np.asarray(self.returns)) except: sr = 0 reward = sr elif self.compute_reward == compute_reward.sortino: try: sr = sortino_ratio(np.asarray(self.returns)) except: sr = 0 reward = sr elif self.compute_reward == compute_reward.max_drawdown: try: mdd = max_drawdown(np.asarray(self.returns)) except: mdd = 0 reward = mdd elif self.compute_reward == compute_reward.calmar: try: cr = calmar_ratio(np.asarray(self.returns)) except: cr = 0 reward = cr elif self.compute_reward == compute_reward.omega: try: om = omega_ratio(np.asarray(self.returns)) except: om = 0 reward = om elif self.compute_reward == compute_reward.downside_risk: try: dr = downside_risk(np.asarray(self.returns)) except: dr = 0 reward = dr try: reward = reward.mean() except: reward = reward return reward
def RiskRewardStats(df): global RiskRewardList RiskRewardIndex = ['Sharpe Ratio','Sortino Ratio','Omega Ratio','Skewness','Kurtosis', 'Correlation vs MSCI World TR Index','Correlation vs Bloomberg Index'] OmegaRatio = omega_ratio(df['Monthly Return']) Kurtosis = df['Monthly Return'].kurt() Skewness = df['Monthly Return'].skew() SharpeRatio = sharpe_ratio(df['Monthly Return'],period='monthly') SortinoRatio = sortino_ratio(df['Monthly Return'],period='monthly') RiskRewardList = [SharpeRatio,SortinoRatio,OmegaRatio,Skewness,Kurtosis,MSCIIndex,BloombergIndex] RiskRewardDf = pd.DataFrame(RiskRewardList,columns=['Value'],index=RiskRewardIndex) return RiskRewardDf
def _reward(self): length = min(self.current_step, self.reward_len) returns = np.diff(self.net_worths)[-length:] if self.reward_func == 'sortino': reward = sortino_ratio(returns) elif self.reward_func == 'calmar': reward = calmar_ratio(returns) elif self.reward_func == 'omega': reward = omega_ratio(returns) else reward = np.mean(returns) return reward if abs(reward) != inf and not np.isnan(reward) else 0
def test_omega_ratio(self, test_risk_free, test_required_return): res_a = empyrical.omega_ratio(ret['a'], risk_free=test_risk_free, required_return=test_required_return) if np.isnan(res_a): res_a = np.inf res_b = empyrical.omega_ratio(ret['b'], risk_free=test_risk_free, required_return=test_required_return) if np.isnan(res_b): res_b = np.inf res_c = empyrical.omega_ratio(ret['c'], risk_free=test_risk_free, required_return=test_required_return) if np.isnan(res_c): res_c = np.inf assert isclose( ret['a'].vbt.returns.omega_ratio( risk_free=test_risk_free, required_return=test_required_return), res_a) pd.testing.assert_series_equal( ret.vbt.returns.omega_ratio(risk_free=test_risk_free, required_return=test_required_return), pd.Series([res_a, res_b, res_c], index=ret.columns))
def omega_ratio(daily_returns, risk_free=0.0, required_return=0.0, trading_days=252): """Omega Ratio""" try: logger.info("Calculating Omega Ratio...") omr_data = empyrical.omega_ratio(daily_returns, risk_free=risk_free, required_return=required_return, annualization=trading_days) return omr_data except Exception as exception: logger.error('Oops! An Error Occurred ⚠️') raise exception
def _reward(self): length = min(self.current_step, self.forecast_len) returns = np.diff(self.net_worths[-length:]) if np.count_nonzero(returns) < 1: return 0 if self.reward_func == 'sortino': reward = sortino_ratio(returns, annualization=365 * 24) elif self.reward_func == 'calmar': reward = calmar_ratio(returns, annualization=365 * 24) elif self.reward_func == 'omega': reward = omega_ratio(returns, annualization=365 * 24) else: reward = returns[-1] return reward if np.isfinite(reward) else 0
def _reward(self): length = min(self.current_step, self.window_size) returns = np.diff(self.net_worths[-length:]) if np.count_nonzero(returns) < 1: return 0 if self.reward_func == 'sortino': reward = sortino_ratio(returns, annualization=self.annualization) elif self.reward_func == 'calmar': reward = calmar_ratio(returns, annualization=self.annualization) elif self.reward_func == 'omega': reward = omega_ratio(returns, annualization=self.annualization) elif self.reward_func == "logret": reward = np.log(returns[-1]) else: reward = returns[-1] return reward if np.isfinite(reward) else 0
import pandas as pd import empyrical as emp df = pd.read_csv('ac-worth-from-2017/002138.csv') df['daily_return'] = df['worth'].pct_change() days = df['date'].count() return_days = days / 3.347 risk_free = 0.03 / return_days annual_return = emp.annual_return(df['daily_return'], annualization=return_days) max_drawdown = emp.max_drawdown(df['daily_return']) sharpe_ratio = emp.sharpe_ratio(df['daily_return'], risk_free, annualization=return_days) sortino_ratio = emp.sortino_ratio(df['daily_return'], risk_free, annualization=return_days) omega_ratio = emp.omega_ratio(df['daily_return'], risk_free, annualization=return_days) print(annual_return, max_drawdown, sharpe_ratio, sortino_ratio, omega_ratio)
def _omega(self): self.__omega = ey.omega_ratio(returns = self.__returns, risk_free = self.__risk_free)
def test_omega_returns(self, returns, required_return_less, required_return_more): assert empyrical.omega_ratio(returns, required_return_less) > \ empyrical.omega_ratio(returns, required_return_more)
def trades(trades_list: list, daily_balance: list): starting_balance = 0 current_balance = 0 for e in store.exchanges.storage: starting_balance += store.exchanges.storage[e].starting_assets[ jh.app_currency()] current_balance += store.exchanges.storage[e].assets[jh.app_currency()] starting_balance = round(starting_balance, 2) current_balance = round(current_balance, 2) if len(trades_list) == 0: return None df = pd.DataFrame.from_records([t.to_dict() for t in trades_list]) total_completed = len(df) winning_trades = df.loc[df['PNL'] > 0] total_winning_trades = len(winning_trades) losing_trades = df.loc[df['PNL'] < 0] total_losing_trades = len(losing_trades) losing_i = df['PNL'] < 0 losing_streaks = losing_i.ne(losing_i.shift()).cumsum() losing_streak = losing_streaks[losing_i].value_counts().max() winning_i = df['PNL'] > 0 winning_streaks = winning_i.ne(winning_i.shift()).cumsum() winning_streak = winning_streaks[winning_i].value_counts().max() largest_losing_trade = round(df['PNL'].min(), 2) largest_winning_trade = round(df['PNL'].max(), 2) win_rate = len(winning_trades) / (len(losing_trades) + len(winning_trades)) max_R = round(df['R'].max(), 2) min_R = round(df['R'].min(), 2) mean_R = round(df['R'].mean(), 2) longs_count = len(df.loc[df['type'] == 'long']) shorts_count = len(df.loc[df['type'] == 'short']) longs_percentage = longs_count / (longs_count + shorts_count) * 100 short_percentage = 100 - longs_percentage fee = df['fee'].sum() net_profit = round(df['PNL'].sum(), 2) net_profit_percentage = round((net_profit / starting_balance) * 100, 2) average_win = round(winning_trades['PNL'].mean(), 2) average_loss = round(abs(losing_trades['PNL'].mean()), 2) ratio_avg_win_loss = average_win / average_loss expectancy = (0 if np.isnan(average_win) else average_win) * win_rate - ( 0 if np.isnan(average_loss) else average_loss) * (1 - win_rate) expectancy = round(expectancy, 2) expectancy_percentage = round((expectancy / starting_balance) * 100, 2) expected_net_profit_every_100_trades = round(expectancy_percentage * 100, 2) average_holding_period = df['holding_period'].mean() average_winning_holding_period = winning_trades['holding_period'].mean() average_losing_holding_period = losing_trades['holding_period'].mean() gross_profit = round(df.loc[df['PNL'] > 0]['PNL'].sum(), 2) gross_loss = round(df.loc[df['PNL'] < 0]['PNL'].sum(), 2) daily_returns = pd.Series(daily_balance).pct_change(1).values max_drawdown = round(empyrical.max_drawdown(daily_returns) * 100, 2) annual_return = round(empyrical.annual_return(daily_returns) * 100, 2) sharpe_ratio = round(empyrical.sharpe_ratio(daily_returns), 2) calmar_ratio = round(empyrical.calmar_ratio(daily_returns), 2) sortino_ratio = round(empyrical.sortino_ratio(daily_returns), 2) omega_ratio = round(empyrical.omega_ratio(daily_returns), 2) total_open_trades = store.app.total_open_trades open_pl = store.app.total_open_pl return { 'total': np.nan if np.isnan(total_completed) else total_completed, 'total_winning_trades': np.nan if np.isnan(total_winning_trades) else total_winning_trades, 'total_losing_trades': np.nan if np.isnan(total_losing_trades) else total_losing_trades, 'starting_balance': np.nan if np.isnan(starting_balance) else starting_balance, 'finishing_balance': np.nan if np.isnan(current_balance) else current_balance, 'win_rate': np.nan if np.isnan(win_rate) else win_rate, 'max_R': np.nan if np.isnan(max_R) else max_R, 'min_R': np.nan if np.isnan(min_R) else min_R, 'mean_R': np.nan if np.isnan(mean_R) else mean_R, 'ratio_avg_win_loss': np.nan if np.isnan(ratio_avg_win_loss) else ratio_avg_win_loss, 'longs_count': np.nan if np.isnan(longs_count) else longs_count, 'longs_percentage': np.nan if np.isnan(longs_percentage) else longs_percentage, 'short_percentage': np.nan if np.isnan(short_percentage) else short_percentage, 'shorts_count': np.nan if np.isnan(shorts_count) else shorts_count, 'fee': np.nan if np.isnan(fee) else fee, 'net_profit': np.nan if np.isnan(net_profit) else net_profit, 'net_profit_percentage': np.nan if np.isnan(net_profit_percentage) else net_profit_percentage, 'average_win': np.nan if np.isnan(average_win) else average_win, 'average_loss': np.nan if np.isnan(average_loss) else average_loss, 'expectancy': np.nan if np.isnan(expectancy) else expectancy, 'expectancy_percentage': np.nan if np.isnan(expectancy_percentage) else expectancy_percentage, 'expected_net_profit_every_100_trades': np.nan if np.isnan(expected_net_profit_every_100_trades) else expected_net_profit_every_100_trades, 'average_holding_period': average_holding_period, 'average_winning_holding_period': average_winning_holding_period, 'average_losing_holding_period': average_losing_holding_period, 'gross_profit': gross_profit, 'gross_loss': gross_loss, 'max_drawdown': max_drawdown, 'annual_return': annual_return, 'sharpe_ratio': sharpe_ratio, 'calmar_ratio': calmar_ratio, 'sortino_ratio': sortino_ratio, 'omega_ratio': omega_ratio, 'total_open_trades': total_open_trades, 'open_pl': open_pl, 'winning_streak': winning_streak, 'losing_streak': losing_streak, 'largest_losing_trade': largest_losing_trade, 'largest_winning_trade': largest_winning_trade, }
def calculate_statistics(self, df: DataFrame = None, output=True): """""" self.output("开始计算策略统计指标") # Check DataFrame input exterior if df is None: df = self.daily_df # Check for init DataFrame if df is None: # Set all statistics to 0 if no trade. start_date = "" end_date = "" total_days = 0 profit_days = 0 loss_days = 0 end_balance = 0 max_drawdown = 0 max_ddpercent = 0 max_drawdown_duration = 0 max_drawdown_end = 0 total_net_pnl = 0 daily_net_pnl = 0 total_commission = 0 daily_commission = 0 total_slippage = 0 daily_slippage = 0 total_turnover = 0 daily_turnover = 0 total_trade_count = 0 daily_trade_count = 0 total_return = 0 annual_return = 0 daily_return = 0 return_std = 0 sharpe_ratio = 0 sortino_info = 0 win_ratio = 0 return_drawdown_ratio = 0 tail_ratio_info = 0 stability_return = 0 win_loss_pnl_ratio = 0 pnl_medio = 0 duration_medio = 0 calmar_ratio = 0 else: # Calculate balance related time series data df["balance"] = df["net_pnl"].cumsum() + self.capital df["return"] = np.log(df["balance"] / 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 # Calculate statistics value start_date = df.index[0] end_date = df.index[-1] total_days = len(df) profit_days = len(df[df["net_pnl"] > 0]) loss_days = len(df[df["net_pnl"] < 0]) end_balance = df["balance"].iloc[-1] max_drawdown = df["drawdown"].min() max_ddpercent = df["ddpercent"].min() max_drawdown_end = df["drawdown"].idxmin() if isinstance(max_drawdown_end, date): max_drawdown_start = df["balance"][:max_drawdown_end].idxmax() max_drawdown_duration = (max_drawdown_end - max_drawdown_start).days else: max_drawdown_duration = 0 total_net_pnl = df["net_pnl"].sum() daily_net_pnl = total_net_pnl / total_days win = df[df["net_pnl"] > 0] win_amount = win["net_pnl"].sum() win_pnl_medio = win["net_pnl"].mean() # win_duration_medio = win["duration"].mean().total_seconds()/3600 win_count = win["trade_count"].sum() pnl_medio = df["net_pnl"].mean() # duration_medio = df["duration"].mean().total_seconds()/3600 loss = df[df["net_pnl"] < 0] loss_amount = loss["net_pnl"].sum() loss_pnl_medio = loss["net_pnl"].mean() # loss_duration_medio = loss["duration"].mean().total_seconds()/3600 total_commission = df["commission"].sum() daily_commission = total_commission / total_days total_slippage = df["slippage"].sum() daily_slippage = total_slippage / total_days total_turnover = df["turnover"].sum() daily_turnover = total_turnover / total_days total_trade_count = df["trade_count"].sum() win_ratio = (win_count / total_trade_count) * 100 win_loss_pnl_ratio = -win_pnl_medio / loss_pnl_medio daily_trade_count = total_trade_count / total_days total_return = (end_balance / self.capital - 1) * 100 annual_return = total_return / total_days * 240 daily_return = df["return"].mean() * 100 return_std = df["return"].std() * 100 if return_std: sharpe_ratio = daily_return / return_std * np.sqrt(240) else: sharpe_ratio = 0 return_drawdown_ratio = -total_return / max_ddpercent #calmar_ratio:年化收益率与历史最大回撤率之间的比率 calmar_ratio = annual_return / abs(max_ddpercent) #sortino_info sortino_info = sortino_ratio(df['return']) omega_info = omega_ratio(df['return']) #年化波动率 annual_volatility_info = annual_volatility(df['return']) #年化复合增长率 cagr_info = cagr(df['return']) #年化下行风险率 annual_downside_risk = downside_risk(df['return']) """CVaR即条件风险价值,其含义为在投资组合的损失超过某个给定VaR值的条件下,该投资组合的平均损失值。""" c_var = conditional_value_at_risk(df['return']) """风险价值(VaR)是对投资损失风险的一种度量。它估计在正常的市场条件下,在设定的时间段(例如一天)中, 一组投资可能(以给定的概率)损失多少。金融业中的公司和监管机构通常使用VaR来衡量弥补可能损失所需的资产数量""" var_info = value_at_risk(df['return']) #收益稳定率 stability_return = stability_of_timeseries(df['return']) #尾部比率0.25 == 1/4,收益1,风险4 tail_ratio_info = tail_ratio(df['return']) # Output if output: self.output("-" * 30) self.output(f"首个交易日:\t{start_date}") self.output(f"最后交易日:\t{end_date}") self.output(f"总交易日:\t{total_days}") self.output(f"盈利交易日:\t{profit_days}") self.output(f"亏损交易日:\t{loss_days}") self.output(f"起始资金:\t{self.capital:,.2f}") self.output(f"结束资金:\t{end_balance:,.2f}") self.output(f"总收益率:\t{total_return:,.2f}%") self.output(f"年化收益:\t{annual_return:,.2f}%") self.output(f"最大回撤: \t{max_drawdown:,.2f}") self.output(f"百分比最大回撤: {max_ddpercent:,.2f}%") self.output(f"最长回撤天数: \t{max_drawdown_duration}") self.output(f"总盈亏:\t{total_net_pnl:,.2f}") self.output(f"总手续费:\t{total_commission:,.2f}") self.output(f"总滑点:\t{total_slippage:,.2f}") self.output(f"总成交金额:\t{total_turnover:,.2f}") self.output(f"总成交笔数:\t{total_trade_count}") self.output(f"日均盈亏:\t{daily_net_pnl:,.2f}") self.output(f"日均手续费:\t{daily_commission:,.2f}") self.output(f"日均滑点:\t{daily_slippage:,.2f}") self.output(f"日均成交金额:\t{daily_turnover:,.2f}") self.output(f"日均成交笔数:\t{daily_trade_count}") self.output(f"日均收益率:\t{daily_return:,.2f}%") self.output(f"收益标准差:\t{return_std:,.2f}%") self.output(f"胜率:\t{win_ratio:,.2f}") self.output(f"盈亏比:\t\t{win_loss_pnl_ratio:,.2f}") self.output(f"平均每笔盈亏:\t{pnl_medio:,.2f}") self.output(f"calmar_ratio:\t{calmar_ratio:,.3f}") # self.output(f"平均持仓小时:\t{duration_medio:,.2f}") self.output(f"Sharpe Ratio:\t{sharpe_ratio:,.2f}") self.output(f"sortino Ratio:\t{sortino_info:,.3f}") self.output(f"收益回撤比:\t{return_drawdown_ratio:,.2f}") statistics = { "start_date": start_date, "end_date": end_date, "total_days": total_days, "profit_days": profit_days, "loss_days": loss_days, "capital": self.capital, "end_balance": end_balance, "max_drawdown": max_drawdown, "max_ddpercent": max_ddpercent, "max_drawdown_end": max_drawdown_end, "max_drawdown_duration": max_drawdown_duration, "total_net_pnl": total_net_pnl, "daily_net_pnl": daily_net_pnl, "total_commission": total_commission, "daily_commission": daily_commission, "total_slippage": total_slippage, "daily_slippage": daily_slippage, "total_turnover": total_turnover, "daily_turnover": daily_turnover, "total_trade_count": total_trade_count, "daily_trade_count": daily_trade_count, "total_return": total_return, "annual_return": annual_return, "daily_return": daily_return, "return_std": return_std, "sharpe_ratio": sharpe_ratio, 'sortino_info': sortino_info, "win_ratio": win_ratio, "return_drawdown_ratio": return_drawdown_ratio, "tail_ratio_info": tail_ratio_info, "stability_return": stability_return, "win_loss_pnl_ratio": win_loss_pnl_ratio, "pnl_medio": pnl_medio, "calmar_ratio": calmar_ratio } # Filter potential error infinite value for key, value in statistics.items(): if value in (np.inf, -np.inf): value = 0 statistics[key] = np.nan_to_num(value) self.output("策略统计指标计算完成") return statistics
def getOmegaRatio(returns, risk_free=0.0, required_return=0.0, annualization=252): return empyrical.omega_ratio(returns, risk_free, required_return, annualization)
max_drawdown, sharpe_ratio, sortino_ratio, calmar_ratio, omega_ratio, tail_ratio ) import pandas as pd returns = pd.Series( index=pd.date_range('2017-03-10', '2017-03-19'), data=(-0.012143, 0.045350, 0.030957, 0.004902, 0.002341, -0.02103, 0.00148, 0.004820, -0.00023, 0.01201) ) benchmark_returns = pd.Series( index=pd.date_range('2017-03-10', '2017-03-19'), data=(-0.031940, 0.025350, -0.020957, -0.000902, 0.007341, -0.01103, 0.00248, 0.008820, -0.00123, 0.01091) ) creturns = cum_returns(returns) max_drawdown(returns) annual_return(returns) annual_volatility(returns, period='daily') calmar_ratio(returns) omega_ratio(returns=returns, risk_free=0.01) sharpe_ratio(returns=returns, risk_free=0.01) sortino_ratio(returns=returns) downside_risk(returns=returns) alpha(returns=returns, factor_returns=benchmark_returns, risk_free=0.01) beta(returns=returns, factor_returns=benchmark_returns, risk_free=0.01) tail_ratio(returns=returns)