def test_alpha(self, test_risk_free): res_a = empyrical.alpha(ret['a'], benchmark_rets['a'], risk_free=test_risk_free) res_b = empyrical.alpha(ret['b'], benchmark_rets['b'], risk_free=test_risk_free) res_c = empyrical.alpha(ret['c'], benchmark_rets['c'], risk_free=test_risk_free) assert isclose(ret['a'].vbt.returns.alpha(benchmark_rets['a'], risk_free=test_risk_free), res_a) pd.testing.assert_series_equal( ret.vbt.returns.alpha(benchmark_rets, risk_free=test_risk_free), pd.Series([res_a, res_b, res_c], index=ret.columns).rename('alpha') )
def perf_stats(returns, factor_returns=None, positions=None, transactions=None, turnover_denom='AGB'): """ Calculates various performance metrics of a strategy, for use in plotting.show_perf_stats. Parameters ---------- returns : pd.Series Daily returns of the strategy, noncumulative. - See full explanation in tears.create_full_tear_sheet. factor_returns : pd.Series, optional Daily noncumulative returns of the benchmark factor to which betas are computed. Usually a benchmark such as market returns. - This is in the same style as returns. - If None, do not compute alpha, beta, and information ratio. positions : pd.DataFrame Daily net position values. - See full explanation in tears.create_full_tear_sheet. transactions : pd.DataFrame Prices and amounts of executed trades. One row per trade. - See full explanation in tears.create_full_tear_sheet. turnover_denom : str Either AGB or portfolio_value, default AGB. - See full explanation in txn.get_turnover. Returns ------- pd.Series Performance metrics. """ stats = pd.Series() for stat_func in SIMPLE_STAT_FUNCS: stats[STAT_FUNC_NAMES[stat_func.__name__]] = stat_func( returns, annualization=APPROX_BDAYS_PER_YEAR) for stat_func in SIMPLE_STAT_FUNCS1: stats[STAT_FUNC_NAMES1[stat_func.__name__]] = stat_func(returns) if positions is not None: stats['Gross leverage'] = gross_lev(positions).mean() if transactions is not None: stats['Daily turnover'] = get_turnover(positions, transactions, turnover_denom).mean() if factor_returns is not None: stats['Alpha'] = ep.alpha(returns, factor_returns, annualization=APPROX_BDAYS_PER_YEAR) stats['Beta'] = ep.beta(returns, factor_returns) # for stat_func in FACTOR_STAT_FUNCS: # res = stat_func(returns, factor_returns, annualization=APPROX_BDAYS_PER_YEAR) # stats[STAT_FUNC_NAMES[stat_func.__name__]] = res # for stat_func in FACTOR_STAT_FUNCS1: # res = stat_func(returns, factor_returns) # stats[STAT_FUNC_NAMES1[stat_func.__name__]] = res return stats
def get_perf_att(series, bnchmark, rf=0.03 / 12, freq='monthly'): """F: that provides performance statistic of the returns params ------- series: daily or monthly returns returns: dataframe of Strategy name and statistics""" port_mean, port_std, port_sr = (get_stats(series, dtime=freq)) perf = pd.Series( { 'Annualized_Mean': '{:,.2f}'.format(round(port_mean, 3)), 'Annualized_Volatility': round(port_std, 3), 'Sharpe Ratio': round(port_sr, 3), 'Calmar Ratio': round(empyrical.calmar_ratio(series, period=freq), 3), 'Alpha': round(empyrical.alpha(series, bnchmark, risk_free=rf, period=freq), 3), 'Beta': round(empyrical.beta(series, bnchmark), 3), 'Max Drawdown': '{:,.2%}'.format(drawdown(series, ret_='nottext')), 'Sortino Ratio': round( empyrical.sortino_ratio( series, required_return=rf, period=freq), 3), }, ) perf.name = series.name return perf.to_frame()
def calculate_metrics(self): self.benchmark_period_returns = \ cum_returns(self.benchmark_returns).iloc[-1] self.algorithm_period_returns = \ cum_returns(self.algorithm_returns).iloc[-1] if not self.algorithm_returns.index.equals( self.benchmark_returns.index): message = "Mismatch between benchmark_returns ({bm_count}) and \ algorithm_returns ({algo_count}) in range {start} : {end}" message = message.format(bm_count=len(self.benchmark_returns), algo_count=len(self.algorithm_returns), start=self._start_session, end=self._end_session) raise Exception(message) self.num_trading_days = len(self.benchmark_returns) self.trading_day_counts = pd.stats.moments.rolling_count( self.algorithm_returns, self.num_trading_days) self.mean_algorithm_returns = \ self.algorithm_returns.cumsum() / self.trading_day_counts self.benchmark_volatility = annual_volatility(self.benchmark_returns) self.algorithm_volatility = annual_volatility(self.algorithm_returns) self.treasury_period_return = choose_treasury( self.treasury_curves, self._start_session, self._end_session, self.trading_calendar, ) self.sharpe = sharpe_ratio(self.algorithm_returns, ) # The consumer currently expects a 0.0 value for sharpe in period, # this differs from cumulative which was np.nan. # When factoring out the sharpe_ratio, the different return types # were collapsed into `np.nan`. # TODO: Either fix consumer to accept `np.nan` or make the # `sharpe_ratio` return type configurable. # In the meantime, convert nan values to 0.0 if pd.isnull(self.sharpe): self.sharpe = 0.0 self.downside_risk = downside_risk(self.algorithm_returns) self.sortino = sortino_ratio(self.algorithm_returns, _downside_risk=self.downside_risk) self.information = information_ratio(self.algorithm_returns, self.benchmark_returns) self.beta = beta(self.algorithm_returns, self.benchmark_returns) self.alpha = alpha(self.algorithm_returns, self.benchmark_returns, _beta=self.beta) self.excess_return = self.algorithm_period_returns - \ self.treasury_period_return self.max_drawdown = max_drawdown(self.algorithm_returns) self.max_leverage = self.calculate_max_leverage()
def test_alpha_beta_equality(self, returns, benchmark): alpha_beta = empyrical.alpha_beta(returns, benchmark) assert_almost_equal( alpha_beta[0], empyrical.alpha(returns, benchmark), DECIMAL_PLACES) assert_almost_equal( alpha_beta[1], empyrical.beta(returns, benchmark), DECIMAL_PLACES)
def _get_backtest_performance_metrics(ret, benchmark_ret): metrics = { 'alpha': empyrical.alpha(ret, benchmark_ret), 'beta': empyrical.beta(ret, benchmark_ret), 'return': empyrical.cum_returns_final(ret), 'cagr': empyrical.cagr(ret), 'sharpe': empyrical.sharpe_ratio(ret), 'max_drawdown': empyrical.max_drawdown(ret), 'var': empyrical.value_at_risk(ret), 'volatility': empyrical.annual_volatility(ret), } return metrics
def test_alpha(self, returns, benchmark, expected): observed = empyrical.alpha(returns, benchmark) assert_almost_equal(observed, expected, DECIMAL_PLACES) if len(returns) == len(benchmark): # Compare to scipy linregress returns_arr = returns.values benchmark_arr = benchmark.values mask = ~np.isnan(returns_arr) & ~np.isnan(benchmark_arr) slope, intercept, _, _, _ = stats.linregress( benchmark_arr[mask], returns_arr[mask]) assert_almost_equal(observed, intercept * 252, DECIMAL_PLACES)
def test_alpha_beta_equality(self, returns, benchmark): alpha_beta = empyrical.alpha_beta(returns, benchmark) assert_almost_equal(alpha_beta[0], empyrical.alpha(returns, benchmark), DECIMAL_PLACES) assert_almost_equal(alpha_beta[1], empyrical.beta(returns, benchmark), DECIMAL_PLACES) if len(returns) == len(benchmark): # Compare to scipy linregress returns_arr = returns.values benchmark_arr = benchmark.values mask = ~np.isnan(returns_arr) & ~np.isnan(benchmark_arr) slope, intercept, _, _, _ = stats.linregress( returns_arr[mask], benchmark_arr[mask]) assert_almost_equal(alpha_beta[0], intercept) assert_almost_equal(alpha_beta[1], slope)
def alpha(close, benchmark_close, risk_free=0.0, period='daily', annualization=None, _beta=None): try: rets = daily_returns(close) benchmark_rets = daily_returns(benchmark_close) alpha_data = empyrical.alpha(rets, benchmark_rets, risk_free=risk_free, period=period, annualization=annualization, _beta=_beta) return alpha_data except Exception as e: raise (e)
def alpha(returns, factor_returns): """Calculates annualized alpha. Parameters ---------- returns : pd.Series Daily returns of the strategy, noncumulative. - See full explanation in :func:`~pyfolio.timeseries.cum_returns`. factor_returns : pd.Series Daily noncumulative returns of the factor to which beta is computed. Usually a benchmark such as the market. - This is in the same style as returns. Returns ------- float Alpha. """ return empyrical.alpha(returns, factor_returns=factor_returns)
def alpha(daily_returns: Any, benchmark_daily_returns: Any, risk_free: float = 0.0, period: str = 'daily', annualization: Any = None, _beta: Any = None): """Alpha""" try: logger.info('Calculating Alpha...') check_inputs_length(daily_returns, benchmark_daily_returns) alpha_data = empyrical.alpha(daily_returns, benchmark_daily_returns, risk_free=risk_free, period=period, annualization=annualization, _beta=_beta) return alpha_data except Exception as exception: logger.error('Oops! An Error Occurred ⚠️') raise exception
def alpha(returns, factor_returns): """ Calculates annualized alpha. Parameters ---------- returns : pd.Series Daily returns of the strategy, noncumulative. - See full explanation in :func:`~pyfolio.timeseries.cum_returns`. factor_returns : pd.Series Daily noncumulative returns of the factor to which beta is computed. Usually a benchmark such as the market. - This is in the same style as returns. Returns ------- float Alpha. """ return empyrical.alpha(returns, factor_returns=factor_returns)
def get_performance_table(return_df: pd.DataFrame, benchmark_name: str = None, periods: str = 'daily') -> pd.DataFrame: """收益指标 Args: return_df (pd.DataFrame): 收益率表格 benchmark_name (str): 基准的列名 periods (str, optional): 频率. Defaults to 'daily'. Returns: pd.DataFrame """ ser: pd.DataFrame = pd.DataFrame() ser['年化收益率'] = ep.annual_return(return_df, period=periods) ser['累计收益'] = ep.cum_returns(return_df).iloc[-1] ser['波动率'] = return_df.apply( lambda x: ep.annual_volatility(x, period=periods)) ser['夏普'] = return_df.apply(ep.sharpe_ratio, period=periods) ser['最大回撤'] = return_df.apply(lambda x: ep.max_drawdown(x)) if benchmark_name is not None: select_col = [ col for col in return_df.columns if col != benchmark_name ] ser['IR'] = return_df[select_col].apply( lambda x: information_ratio(x, return_df[benchmark_name])) ser['Alpha'] = return_df[select_col].apply( lambda x: ep.alpha(x, return_df[benchmark_name], period=periods)) ser['超额收益'] = ser['年化收益率'] - ser.loc[benchmark_name, '年化收益率'] #计算相对年化波动率 return ser.T
def alpha(portfolio_daily_returns, benchmark_returns=None): if benchmark_returns is None: benchmark_returns = sdata.get_sp500_index_returns( portfolio_daily_returns.index[0], portfolio_daily_returns.index[-1]) return ep.alpha(portfolio_daily_returns, benchmark_returns)
def update(self, dt, algorithm_returns, benchmark_returns, leverage): # Keep track of latest dt for use in to_dict and other methods # that report current state. self.latest_dt = dt dt_loc = self.cont_index.get_loc(dt) self.latest_dt_loc = dt_loc self.algorithm_returns_cont[dt_loc] = algorithm_returns self.algorithm_returns = self.algorithm_returns_cont[: dt_loc + 1] algorithm_returns_series = pd.Series(self.algorithm_returns) self.num_trading_days = len(self.algorithm_returns) if self.create_first_day_stats: if len(self.algorithm_returns) == 1: self.algorithm_returns = np.append(0.0, self.algorithm_returns) self.algorithm_cumulative_returns[dt_loc] = cum_returns(algorithm_returns_series).iloc[-1] algo_cumulative_returns_to_date = self.algorithm_cumulative_returns[: dt_loc + 1] self.mean_returns_cont[dt_loc] = algo_cumulative_returns_to_date[dt_loc] / self.num_trading_days self.mean_returns = self.mean_returns_cont[: dt_loc + 1] self.annualized_mean_returns_cont[dt_loc] = self.mean_returns_cont[dt_loc] * 252 self.annualized_mean_returns = self.annualized_mean_returns_cont[: dt_loc + 1] if self.create_first_day_stats: if len(self.mean_returns) == 1: self.mean_returns = np.append(0.0, self.mean_returns) self.annualized_mean_returns = np.append(0.0, self.annualized_mean_returns) self.benchmark_returns_cont[dt_loc] = benchmark_returns self.benchmark_returns = self.benchmark_returns_cont[: dt_loc + 1] benchmark_returns_series = pd.Series(self.benchmark_returns) if self.create_first_day_stats: if len(self.benchmark_returns) == 1: self.benchmark_returns = np.append(0.0, self.benchmark_returns) self.benchmark_cumulative_returns[dt_loc] = cum_returns(benchmark_returns_series).iloc[-1] benchmark_cumulative_returns_to_date = self.benchmark_cumulative_returns[: dt_loc + 1] self.mean_benchmark_returns_cont[dt_loc] = benchmark_cumulative_returns_to_date[dt_loc] / self.num_trading_days self.mean_benchmark_returns = self.mean_benchmark_returns_cont[:dt_loc] self.annualized_mean_benchmark_returns_cont[dt_loc] = self.mean_benchmark_returns_cont[dt_loc] * 252 self.annualized_mean_benchmark_returns = self.annualized_mean_benchmark_returns_cont[: dt_loc + 1] self.algorithm_cumulative_leverages_cont[dt_loc] = leverage self.algorithm_cumulative_leverages = self.algorithm_cumulative_leverages_cont[: dt_loc + 1] if self.create_first_day_stats: if len(self.algorithm_cumulative_leverages) == 1: self.algorithm_cumulative_leverages = np.append(0.0, self.algorithm_cumulative_leverages) if not len(self.algorithm_returns) and len(self.benchmark_returns): message = "Mismatch between benchmark_returns ({bm_count}) and \ algorithm_returns ({algo_count}) in range {start} : {end} on {dt}" message = message.format( bm_count=len(self.benchmark_returns), algo_count=len(self.algorithm_returns), start=self.start_session, end=self.end_session, dt=dt, ) raise Exception(message) self.update_current_max() self.benchmark_volatility[dt_loc] = annual_volatility(benchmark_returns_series) self.algorithm_volatility[dt_loc] = annual_volatility(algorithm_returns_series) # caching the treasury rates for the minutely case is a # big speedup, because it avoids searching the treasury # curves on every minute. # In both minutely and daily, the daily curve is always used. treasury_end = dt.replace(hour=0, minute=0) if np.isnan(self.daily_treasury[treasury_end]): treasury_period_return = choose_treasury( self.treasury_curves, self.start_session, treasury_end, self.trading_calendar ) self.daily_treasury[treasury_end] = treasury_period_return self.treasury_period_return = self.daily_treasury[treasury_end] self.excess_returns[dt_loc] = self.algorithm_cumulative_returns[dt_loc] - self.treasury_period_return self.beta[dt_loc] = beta(algorithm_returns_series, benchmark_returns_series) self.alpha[dt_loc] = alpha(algorithm_returns_series, benchmark_returns_series, _beta=self.beta[dt_loc]) self.sharpe[dt_loc] = sharpe_ratio(algorithm_returns_series) self.downside_risk[dt_loc] = downside_risk(algorithm_returns_series) self.sortino[dt_loc] = sortino_ratio(algorithm_returns_series, _downside_risk=self.downside_risk[dt_loc]) self.information[dt_loc] = information_ratio(algorithm_returns_series, benchmark_returns_series) self.max_drawdown = max_drawdown(algorithm_returns_series) self.max_drawdowns[dt_loc] = self.max_drawdown self.max_leverage = self.calculate_max_leverage() self.max_leverages[dt_loc] = self.max_leverage
def Strategy_performance(returns: pd.DataFrame, mark_benchmark: str = 'benchmark', periods: str = 'daily') -> pd.DataFrame: ''' 风险指标计算 returns:index-date col-数据字段 mark_benchmark:用于指明基准 periods:频率 ''' df: pd.DataFrame = pd.DataFrame() df['年化收益率'] = ep.annual_return(returns, period=periods) df['累计收益'] = returns.apply(lambda x: ep.cum_returns(x).iloc[-1]) df['波动率'] = returns.apply( lambda x: ep.annual_volatility(x, period=periods)) df['夏普'] = returns.apply(ep.sharpe_ratio, period=periods) df['最大回撤'] = returns.apply(lambda x: ep.max_drawdown(x)) df['索提诺比率'] = returns.apply(lambda x: ep.sortino_ratio(x, period=periods)) df['Calmar'] = returns.apply(lambda x: ep.calmar_ratio(x, period=periods)) # 相对指标计算 if mark_benchmark in returns.columns: select_col = [col for col in returns.columns if col != mark_benchmark] df['IR'] = returns[select_col].apply( lambda x: information_ratio(x, returns[mark_benchmark])) df['Alpha'] = returns[select_col].apply( lambda x: ep.alpha(x, returns[mark_benchmark], period=periods)) df['Beta'] = returns[select_col].apply( lambda x: ep.beta(x, returns[mark_benchmark])) # 计算相对年化波动率 df['超额收益率'] = df['年化收益率'] - \ df.loc[mark_benchmark, '年化收益率'] return df.T # def show_worst_drawdown_periods(returns: pd.Series, # benchmark_code: str = "000300.SH", # top: int = 5): # """ # Prints information about the worst drawdown periods. # Prints peak dates, valley dates, recovery dates, and net # drawdowns. # Parameters # ---------- # returns : pd.Series # Daily returns of the strategy, noncumulative. # - See full explanation in tears.create_full_tear_sheet. # top : int, optional # Amount of top drawdowns periods to plot (default 5). # """ # drawdown_df = ts.gen_drawdown_table(returns, top=top) # drawdown_df.index = list(range(1, len(drawdown_df) + 1)) # phase_change = compare_phase_change(returns, benchmark_code, top) # df = pd.concat((drawdown_df, phase_change), axis=1) # # print_table( # # df.sort_values('区间最大回撤 %', ascending=False), # # name='序号', # # float_format='{0:.2f}'.format, # # ) # return df # def compare_phase_change(returns: pd.Series, # benchmark_code: str, # top: int = 5) -> pd.DataFrame: # ''' # 对比策略与基准在回撤区间内的收益 # ------ # returns:策略净值收益率 # benchmark_code:基准的代码 # ''' # beginDt = returns.index.min() # endDt = returns.index.max() # benchmark = get_wsd_data(benchmark_code, # 'pct_chg', # beginDt, # endDt, # 'priceAdj=B', # usedf=True) # benchmark = benchmark['PCT_CHG'] / 100 # df = pd.DataFrame(columns=['策略收益%', '基准收益%'], # index=list(range(1, top + 1))) # drawdowns_list = ts.get_top_drawdowns(returns, top=top) # for i, v in enumerate(drawdowns_list): # peak_date, _, recovery_date = v # if pd.isnull(recovery_date): # df.loc[i + 1, '策略收益%'] = np.nan # df.loc[i + 1, '基准收益'] = np.nan # else: # df.loc[i + 1, '策略收益%'] = ep.cum_returns( # returns.loc[peak_date:recovery_date]).iloc[-1] # df.loc[i + 1, '基准收益%'] = ep.cum_returns( # benchmark.loc[peak_date:recovery_date])[-1] # return df
def update(self, dt, algorithm_returns, benchmark_returns, leverage): # Keep track of latest dt for use in to_dict and other methods # that report current state. self.latest_dt = dt dt_loc = self.cont_index.get_loc(dt) self.latest_dt_loc = dt_loc self.algorithm_returns_cont[dt_loc] = algorithm_returns self.algorithm_returns = self.algorithm_returns_cont[:dt_loc + 1] algorithm_returns_series = pd.Series(self.algorithm_returns) self.num_trading_days = len(self.algorithm_returns) if self.create_first_day_stats: if len(self.algorithm_returns) == 1: self.algorithm_returns = np.append(0.0, self.algorithm_returns) self.algorithm_cumulative_returns[dt_loc] = cum_returns( algorithm_returns_series).iloc[-1] algo_cumulative_returns_to_date = \ self.algorithm_cumulative_returns[:dt_loc + 1] self.mean_returns_cont[dt_loc] = \ algo_cumulative_returns_to_date[dt_loc] / self.num_trading_days self.mean_returns = self.mean_returns_cont[:dt_loc + 1] self.annualized_mean_returns_cont[dt_loc] = \ self.mean_returns_cont[dt_loc] * 252 self.annualized_mean_returns = \ self.annualized_mean_returns_cont[:dt_loc + 1] if self.create_first_day_stats: if len(self.mean_returns) == 1: self.mean_returns = np.append(0.0, self.mean_returns) self.annualized_mean_returns = np.append( 0.0, self.annualized_mean_returns) self.benchmark_returns_cont[dt_loc] = benchmark_returns self.benchmark_returns = self.benchmark_returns_cont[:dt_loc + 1] benchmark_returns_series = pd.Series(self.benchmark_returns) if self.create_first_day_stats: if len(self.benchmark_returns) == 1: self.benchmark_returns = np.append(0.0, self.benchmark_returns) self.benchmark_cumulative_returns[dt_loc] = cum_returns( benchmark_returns_series).iloc[-1] benchmark_cumulative_returns_to_date = \ self.benchmark_cumulative_returns[:dt_loc + 1] self.mean_benchmark_returns_cont[dt_loc] = \ benchmark_cumulative_returns_to_date[dt_loc] / \ self.num_trading_days self.mean_benchmark_returns = self.mean_benchmark_returns_cont[:dt_loc] self.annualized_mean_benchmark_returns_cont[dt_loc] = \ self.mean_benchmark_returns_cont[dt_loc] * 252 self.annualized_mean_benchmark_returns = \ self.annualized_mean_benchmark_returns_cont[:dt_loc + 1] self.algorithm_cumulative_leverages_cont[dt_loc] = leverage self.algorithm_cumulative_leverages = \ self.algorithm_cumulative_leverages_cont[:dt_loc + 1] if self.create_first_day_stats: if len(self.algorithm_cumulative_leverages) == 1: self.algorithm_cumulative_leverages = np.append( 0.0, self.algorithm_cumulative_leverages) if not len(self.algorithm_returns) and len(self.benchmark_returns): message = "Mismatch between benchmark_returns ({bm_count}) and \ algorithm_returns ({algo_count}) in range {start} : {end} on {dt}" message = message.format(bm_count=len(self.benchmark_returns), algo_count=len(self.algorithm_returns), start=self.start_session, end=self.end_session, dt=dt) raise Exception(message) self.update_current_max() self.benchmark_volatility[dt_loc] = annual_volatility( benchmark_returns_series) self.algorithm_volatility[dt_loc] = annual_volatility( algorithm_returns_series) # caching the treasury rates for the minutely case is a # big speedup, because it avoids searching the treasury # curves on every minute. # In both minutely and daily, the daily curve is always used. treasury_end = dt.replace(hour=0, minute=0) if np.isnan(self.daily_treasury[treasury_end]): treasury_period_return = choose_treasury( self.treasury_curves, self.start_session, treasury_end, self.trading_calendar, ) self.daily_treasury[treasury_end] = treasury_period_return self.treasury_period_return = self.daily_treasury[treasury_end] self.excess_returns[dt_loc] = ( self.algorithm_cumulative_returns[dt_loc] - self.treasury_period_return) self.beta[dt_loc] = beta(algorithm_returns_series, benchmark_returns_series) self.alpha[dt_loc] = alpha(algorithm_returns_series, benchmark_returns_series) self.sharpe[dt_loc] = sharpe_ratio(algorithm_returns_series, benchmark_returns_series) self.downside_risk[dt_loc] = downside_risk(algorithm_returns_series, benchmark_returns_series) self.sortino[dt_loc] = sortino_ratio(algorithm_returns_series, benchmark_returns_series) self.information[dt_loc] = information_ratio(algorithm_returns_series, benchmark_returns_series) self.max_drawdown = max_drawdown(algorithm_returns_series) self.max_drawdowns[dt_loc] = self.max_drawdown self.max_leverage = self.calculate_max_leverage() self.max_leverages[dt_loc] = self.max_leverage
def run_turtle(): PROPERTY = START_MONEY CASH = START_MONEY show_df = None show_df = stock_df_dict['NDX'].copy() order_df = None order_df = pd.DataFrame(columns=[ 'buy_date', 'symbol', 'buy_count', 'buy_price', 'buy_reason', 'sell_date', 'sell_price', 'sell_reason', 'profit', 'cash', 'property' ]) count_day = 0 yesterday = None for today in pd.period_range(start=start_date, end=end_date, freq='D'): count_day += 1 if yesterday is None: yesterday = today continue if today not in stock_df_dict['NDX'].index: continue if IS_HAPPY_MONEY: if PROPERTY > START_MONEY * 2: global HAPPY_MONEY HAPPY_MONEY += int(START_MONEY / 2) PROPERTY -= int(START_MONEY / 2) CASH = PROPERTY # 买卖过程 sell_signal = [] buy_signal = [] for symbol in NASDAQ100[:]: # for symbol in ['TSLA']: if symbol in [ 'ALGN', 'ROST', 'ORLY', 'ESRX', 'ULTA', 'REGN', 'MNST' ]: # continue pass if symbol == 'NDX': continue if today not in stock_df_dict[ symbol].index or yesterday not in stock_df_dict[ symbol].index: continue # 突破下行趋势,清仓退出 order_arr = order_df.to_records(index=False) if len(order_arr[(order_arr.symbol == symbol) & (order_arr.sell_price == 0)]) != 0: is_sell = False for idx in order_df[(order_df['symbol'] == symbol) & (order_df['sell_price'] == 0)].index: if order_df.loc[idx, 'buy_reason'] == 'SHORT': is_sell = ( stock_df_dict[symbol].loc[today, 'open'] <= stock_df_dict[symbol].loc[today, 'ROLLING_%d_MIN' % TURTLE_SHORT_SELL_N]) if order_df.loc[idx, 'buy_reason'] == 'LONG': is_sell = ( stock_df_dict[symbol].loc[today, 'open'] <= stock_df_dict[symbol].loc[today, 'ROLLING_%d_MIN' % TURTLE_LONG_SELL_N]) if is_sell: CASH += order_df.loc[idx, 'buy_count'] * \ stock_df_dict[symbol].loc[today, 'open'] order_df.loc[idx, 'sell_date'] = today order_df.loc[idx, 'sell_price'] = stock_df_dict[symbol].loc[ today, 'open'] order_df.loc[idx, 'sell_reason'] = 'EXIT' order_df.loc[idx, 'profit'] = \ (order_df.loc[idx, 'sell_price'] - order_df.loc[idx, 'buy_price']) * order_df.loc[idx, 'buy_count'] # print(today, '退出', stock_df_dict[symbol].loc[today, 'open'], CASH) # 突破上行趋势,买入一份 order_arr = order_df.to_records(index=False) if stock_df_dict[symbol].loc[ today, 'MA30'] >= stock_df_dict[symbol].loc[today, 'MA180']: is_buy = False if stock_df_dict[symbol].loc[today, 'open'] >= stock_df_dict[ symbol].loc[today, 'ROLLING_%d_MAX' % TURTLE_LONG_BUY_N]: is_buy = True buy_reason = 'LONG' elif stock_df_dict[symbol].loc[today, 'open'] >= stock_df_dict[ symbol].loc[today, 'ROLLING_%d_MAX' % TURTLE_SHORT_BUY_N]: is_buy = True buy_reason = 'SHORT' if is_buy: buy_count = 0 if CASH >= PROPERTY / TURTLE_POS: buy_count = int( (PROPERTY / TURTLE_POS) / stock_df_dict[symbol].loc[today, 'open']) if buy_count > 0: CASH -= buy_count * \ stock_df_dict[symbol].loc[today, 'open'] # print(today, '买入', buy_count, stock_df_dict[symbol].loc[today, 'open'], CASH) order_df = order_df.append( { 'buy_date': today, 'symbol': symbol, 'buy_count': buy_count, 'buy_price': stock_df_dict[symbol].loc[today, 'open'], 'buy_reason': buy_reason, 'sell_date': pd.np.nan, 'sell_price': 0, 'profit': 0, 'cash': CASH, 'property': PROPERTY, }, ignore_index=True) # 每天盘点财产 show_df.loc[today, 'CASH_TURTLE_%d_%d_%d' % (TURTLE_POS, TURTLE_LONG_BUY_N, TURTLE_LONG_SELL_N)] = CASH PROPERTY = CASH + \ sum( [ stock_df_dict[order_df.loc[idx, 'symbol']].loc[today, 'open'] * order_df.loc[idx, 'buy_count'] for idx in order_df.loc[order_df['sell_price'] == 0].index ] ) show_df.loc[today, 'PROPERTY_TURTLE_%d_%d_%d' % (TURTLE_POS, TURTLE_LONG_BUY_N, TURTLE_LONG_SELL_N)] = PROPERTY yesterday = today # 最终结果 print('CASH', CASH) print('HAPPY_MONEY', HAPPY_MONEY) print('PROPERTY', PROPERTY) benchmark_symbol = 'NDX' s_p = stock_df_dict[benchmark_symbol][start_date:].iloc[0].open e_p = stock_df_dict[benchmark_symbol].iloc[-1].open print(benchmark_symbol, s_p, e_p, e_p / s_p) show_df = show_df[start_date:].dropna(how='any', inplace=False) show_df['strategy_pct'] = show_df['PROPERTY_TURTLE_%d_%d_%d' % (TURTLE_POS, TURTLE_LONG_BUY_N, TURTLE_LONG_SELL_N)].pct_change() # show_df['benchmark_pct'] = show_df['open'].pct_change() show_df['benchmark_pct'] = stock_df_dict[benchmark_symbol].open.pct_change( ) # print('cum_returns', emp.cum_returns(show_df.strategy_pct)) print('max_drawdown', emp.max_drawdown(show_df.strategy_pct)) print( 'MDD', MDD(show_df['PROPERTY_TURTLE_%d_%d_%d' % (TURTLE_POS, TURTLE_LONG_BUY_N, TURTLE_LONG_SELL_N)])) print('annual_return', emp.annual_return(show_df.strategy_pct)) print('annual_volatility', emp.annual_volatility(show_df.strategy_pct, period='daily')) print('calmar_ratio', emp.calmar_ratio(show_df.strategy_pct)) print('sharpe_ratio', emp.sharpe_ratio(returns=show_df.strategy_pct)) print( 'alpha', emp.alpha(returns=show_df.strategy_pct, factor_returns=show_df.benchmark_pct, risk_free=0.00)) print( 'beta', emp.beta(returns=show_df.strategy_pct, factor_returns=show_df.benchmark_pct, risk_free=0.00))
def getAlpha(self, period='daily', annualization=None): return empyrical.alpha(self.returns, self.riskFreeRate, period, annualization, beta_=None)
def work(PARAMS): info('work %s' % str(PARAMS)) stock_df_dict = None show_df = None order_df = None PROPERTY = None STRATEGY = PARAMS[0] POS = PARAMS[1] N = PARAMS[2] K = PARAMS[3] M = PARAMS[4] global ROTATION_LIST ROTATION_LIST = ROTATION_LIST stock_df_dict = get_stock_df_dict(N, M) show_df, order_df, PROPERTY = run_turtle(ROTATION_LIST, stock_df_dict, STRATEGY, POS, N, K, M) df = show_df.dropna(how='any', inplace=False).copy() df = df.loc[start_date:end_date] algo = df['PROPERTY'].pct_change() benchmark = df.open.pct_change() DAYS_ALL = len(df) DAYS_NOFULLHOLD = len(df[df['CASH'] > (df['PROPERTY'] / POS)]) output_str = '' for y in range(int(start_date.split('-')[0]), int(end_date.split('-')[0]) + 1, 1): # info('y = %d' % y) y_df = df.loc['%d-01-01' % y:'%d-01-01' % (y + 1)] if len(y_df) == 0: continue y_algo = y_df['PROPERTY'].pct_change() # info(y_algo) y_benchmark = y_df.open.pct_change() # info('y_benc') result = '%d-%d,%.3f,%.3f,%.3f,%.3f' % ( y, y + 1, emp.cum_returns(y_algo)[-1], emp.cum_returns(y_benchmark)[-1], emp.max_drawdown(y_algo), emp.max_drawdown(y_benchmark) ) output_str += result output_str += ';' # info(output_str) df = order_df.copy() df['pro_pct'] = (df.borrow_price - df.return_price) / df.return_price df = df.loc[:, ['symbol', 'pro_pct']] df = df.groupby(by='symbol').sum() buy_stock_count = len(df) score_sr = pd.Series({ 'START': start_date, 'END': end_date, 'STRATEGY': STRATEGY, 'POS': POS, 'N': N, 'K': K, 'M': M, 'ORDER': len(order_df), 'STOCK': buy_stock_count, 'RETURN_ALGO': emp.cum_returns(algo)[-1], 'RETURN_BENC': emp.cum_returns(benchmark)[-1], 'MAXDROPDOWN_ALGO': emp.max_drawdown(algo), 'MAXDROPDOWN_BENC': emp.max_drawdown(benchmark), 'WINRATE_ORDER': len(order_df[order_df.profit > 0]) / len(order_df[order_df.profit != 0]), 'WINRATE_YEARLY': 0, 'ANNUAL_RETURN': emp.annual_return(algo), 'ANNUAL_VOLATILITY': emp.annual_volatility(algo, period='daily'), 'CALMAR_RATIO': emp.calmar_ratio(algo), 'SHARPE_RATIO': emp.sharpe_ratio(returns=algo), 'ALPHA': emp.alpha(returns=algo, factor_returns=benchmark, risk_free=0.00), 'BETA': emp.beta(returns=algo, factor_returns=benchmark, risk_free=0.00), 'DAYS_ALL': DAYS_ALL, 'DAYS_NOFULLHOLD': DAYS_NOFULLHOLD, 'RET_PER_YEAR': output_str, }) YEAR_COUNT = 0 ALGO_WIN_YEAR_COUNT = 0 df = show_df.dropna(how='any', inplace=False).copy() df = df.loc[start_date:end_date] for y in range(int(start_date.split('-')[0]), int(end_date.split('-')[0]) + 1, 1): y_df = df.loc['%d-01-01' % y:'%d-01-01' % (y + 1)] # info('y = %d' % y) if len(y_df) == 0: continue y_algo = y_df['PROPERTY'].pct_change() y_benchmark = y_df.open.pct_change() score_sr['RETURN_ALGO_%d' % y] = emp.cum_returns(y_algo)[-1] score_sr['RETURN_BENC_%d' % y] = emp.cum_returns(y_benchmark)[-1] YEAR_COUNT += 1 if score_sr['RETURN_ALGO_%d' % y] > score_sr['RETURN_BENC_%d' % y]: ALGO_WIN_YEAR_COUNT += 1 score_sr['WINRATE_YEARLY'] = ALGO_WIN_YEAR_COUNT / YEAR_COUNT return PARAMS, score_sr, order_df
def calculate_metrics(self): self.benchmark_period_returns = \ cum_returns(self.benchmark_returns).iloc[-1] self.algorithm_period_returns = \ cum_returns(self.algorithm_returns).iloc[-1] if not self.algorithm_returns.index.equals( self.benchmark_returns.index ): message = "Mismatch between benchmark_returns ({bm_count}) and \ algorithm_returns ({algo_count}) in range {start} : {end}" message = message.format( bm_count=len(self.benchmark_returns), algo_count=len(self.algorithm_returns), start=self._start_session, end=self._end_session ) raise Exception(message) self.num_trading_days = len(self.benchmark_returns) self.trading_day_counts = pd.stats.moments.rolling_count( self.algorithm_returns, self.num_trading_days) self.mean_algorithm_returns = \ self.algorithm_returns.cumsum() / self.trading_day_counts self.benchmark_volatility = annual_volatility(self.benchmark_returns) self.algorithm_volatility = annual_volatility(self.algorithm_returns) self.treasury_period_return = choose_treasury( self.treasury_curves, self._start_session, self._end_session, self.trading_calendar, ) self.sharpe = sharpe_ratio( self.algorithm_returns, ) # The consumer currently expects a 0.0 value for sharpe in period, # this differs from cumulative which was np.nan. # When factoring out the sharpe_ratio, the different return types # were collapsed into `np.nan`. # TODO: Either fix consumer to accept `np.nan` or make the # `sharpe_ratio` return type configurable. # In the meantime, convert nan values to 0.0 if pd.isnull(self.sharpe): self.sharpe = 0.0 self.downside_risk = downside_risk( self.algorithm_returns ) self.sortino = sortino_ratio( self.algorithm_returns, _downside_risk=self.downside_risk ) self.information = information_ratio( self.algorithm_returns, self.benchmark_returns ) self.beta = beta( self.algorithm_returns, self.benchmark_returns ) self.alpha = alpha( self.algorithm_returns, self.benchmark_returns, _beta=self.beta ) self.excess_return = self.algorithm_period_returns - \ self.treasury_period_return self.max_drawdown = max_drawdown(self.algorithm_returns) self.max_leverage = self.calculate_max_leverage()
# 画出收益曲线图 draw_return_rate_line(perf) return_list = perf['returns'] # 计算年化收益率 ann_return = annual_return(return_list) # 计算累计收益率 cum_return_list = cum_returns(return_list) # 计算sharp ratio sharp = sharpe_ratio(return_list) # 最大回撤 max_drawdown_ratio = max_drawdown(return_list) print("年化收益率 = {:.2%}, 累计收益率 = {:.2%}, 最大回撤 = {:.2%}, 夏普比率 = {:.2f} ".format (ann_return, cum_return_list[-1], max_drawdown_ratio, sharp)) 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) ) alpha_return = alpha(returns=returns, factor_returns=benchmark_returns, risk_free=0.01) beta_return = beta(returns=returns, factor_returns=benchmark_returns, risk_free=0.01) print("alpha_return", alpha_return) print("\nbeta_return", beta_return)
def test(): # 构造测试数据 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)) print(returns) 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)) print(benchmark_returns) # 计算累积收益率 creturns = ey.cum_returns(returns) print("累积收益率\n", creturns) risk = riskAnalyzer(returns, benchmark_returns, riskFreeRate = 0.01) results = risk.run() print(results) # 直接调用empyrical试试 alpha = ey.alpha(returns = returns, factor_returns = benchmark_returns, risk_free = 0.01) calmar = ey.calmar_ratio(returns) print(alpha, calmar) # 自己计算阿尔法值 annual_return = ey.annual_return(returns) annual_bench = ey.annual_return(benchmark_returns) print(annual_return, annual_bench) alpha2 = (annual_return - 0.01) - results["贝塔"]*(annual_bench - 0.01) print(alpha2) # 自己计算阿尔法贝塔 def get_return(code, startdate, endate): df = ts.get_k_data(code, ktype = "D", autype = "qfq", start = startdate, end = endate) p1 = np.array(df.close[1:]) p0 = np.array(df.close[:-1]) logret = np.log(p1/p0) rate = pd.DataFrame() rate[code] = logret rate.index = df["date"][1:] return rate def alpha_beta(code, startdate, endate): mkt_ret = get_return("sh", startdate, endate) stock_ret = get_return(code, startdate, endate) df = pd.merge(mkt_ret, stock_ret, left_index = True, right_index = True) x = df.iloc[:, 0] y = df.iloc[:, 1] beta, alpha, r_value, p_value, std_err = stats.linregress(x, y) return (alpha, beta) def stocks_alpha_beta(stocks, startdate, endate): df = pd.DataFrame() alpha = [] beta = [] for code in stocks.values(): a, b = alpha_beta(code, startdate, endate) alpha.append(float("%.4f"%a)) beta.append(float("%.4f"%b)) df["alpha"] = alpha df["beta"] = beta df.index = stocks.keys() return df startdate = "2017-01-01" endate = "2018-11-09" stocks={'中国平安':'601318','格力电器':'000651','招商银行':'600036','恒生电子':'600570','中信证券':'600030','贵州茅台':'600519'} results = stocks_alpha_beta(stocks, startdate, endate) print("自己计算结果") print(results) # 用empyrical计算 def stocks_alpha_beta2(stocks, startdate, endate): df = pd.DataFrame() alpha = [] beta = [] for code in stocks.values(): a, b = empyrical_alpha_beta(code, startdate, endate) alpha.append(float("%.4f"%a)) beta.append(float("%.4f"%b)) df["alpha"] = alpha df["beta"] = beta df.index = stocks.keys() return df def empyrical_alpha_beta(code, startdate, endate): mkt_ret = get_return("sh", startdate, endate) stock_ret = get_return(code, startdate, endate) alpha, beta = ey.alpha_beta(returns = stock_ret, factor_returns = mkt_ret, annualization = 1) return (alpha, beta) results2 = stocks_alpha_beta2(stocks, startdate, endate) print("empyrical计算结果") print(results2) print(results2["alpha"]/results["alpha"])
def test_alpha(self, returns, benchmark, expected): assert_almost_equal( empyrical.alpha(returns, benchmark), expected, DECIMAL_PLACES)