Пример #1
0
def get_pl_ratio(start_date, end_date, holdings, df_price):
    assert check_time(start_date=start_date, end_date=end_date)

    periods = list(holdings.holdings.keys())
    # trim log to fit within the time period
    periods = [
        date for date in periods if start_date <= date.start_time <= end_date
    ][1:]
    profit, loss = [], []

    for period in periods:
        portfolio = holdings.holdings[period]
        # get portfolio value at start and end time
        start_val = portfolio.get_net_liquidation(period.start_time, df_price)
        end_val = portfolio.get_net_liquidation(period.end_time, df_price)

        # if the rebalance made a profit, increase gains by 1
        if end_val > start_val:
            profit.append(end_val - start_val)
        elif end_val < start_val:
            loss.append(start_val - end_val)

    # calculate average profits and losses
    avg_profit = np.mean(profit)
    avg_loss = np.mean(loss)
    # calculate P/L ratio
    pl_ratio = avg_profit / avg_loss
    return pl_ratio
Пример #2
0
def get_win_rate(start_date, end_date, holdings, df_price) -> float:
    assert check_time(start_date=start_date, end_date=end_date)

    periods = list(holdings.holdings.keys())
    # trim log to fit within the time period
    periods = [
        date for date in periods if start_date <= date.start_time <= end_date
    ][1:]
    # how many periods are there?
    n_periods = len(periods)
    winning_periods = 0

    for period in periods:
        portfolio = holdings.holdings[period]
        # get portfolio value at start and end time
        start_val = portfolio.get_net_liquidation(period.start_time, df_price)
        end_val = portfolio.get_net_liquidation(period.end_time, df_price)

        # if the rebalance made a profit, increase gains by 1
        if end_val > start_val:
            winning_periods += 1

    # calculate win rate
    win_rate = winning_periods / n_periods
    return win_rate
Пример #3
0
 def contains_time(self, time:Union[datetime.datetime, pd.Timestamp]) -> bool:
     '''checks if a time is within a period'''
     assert check_time(time=time)
     if self.start_time <= time <= self.end_time:
         return True
     else:
         return False
Пример #4
0
    def get_stock_liquidation(self, date: Union[pd.Timestamp, datetime],
                              df_prices: pd.DataFrame) -> float:
        '''calculates the value of all long/short positions in a portfolio at a given time'''
        assert check_time(date=date)

        df_prices = df_prices.loc[:date]  # trim df to be within valid dates
        agg_stock_value = 0

        # first, consider long positions
        for stock, shares in self.long.items():
            try:
                long_price = df_prices.at[date, stock]
            except KeyError:
                # when was this stock last traded?
                last_traded = df_prices[stock].last_valid_index()
                long_price = df_prices.at[last_traded, stock]

            long_stock_value = long_price * shares
            agg_stock_value += long_stock_value

        # then, consider short positions
        for stock, shares in self.short.items():
            try:
                short_price = df_prices.at[date, stock]
            except KeyError:
                # when was this stock last traded?
                last_traded = df_prices[stock].last_valid_index()
                short_price = df_prices.at[last_traded, stock]

            short_stock_value = short_price * shares
            agg_stock_value -= short_stock_value

        return agg_stock_value
Пример #5
0
def get_tracking_error(strategy, benchmark, start_date, end_date) -> float:
    '''calculates the tracking error of a strategy compared to a benchmark'''
    assert check_prices(strategy=strategy, benchmark=benchmark)
    assert check_time(start_date=start_date, end_date=end_date)

    ann_ex_r = get_annualized_excess_return(strategy, benchmark, start_date,
                                            end_date)
    ir = get_information_ratio(strategy, benchmark, start_date, end_date)

    tracking_error = ann_ex_r / ir
    return tracking_error
Пример #6
0
def get_risk_adjusted_return(prices, start_date, end_date) -> float:
    '''calculates the risk-adjusted return of a prices time-series between two dates'''
    assert check_prices(prices=prices)
    assert check_time(start_date=start_date, end_date=end_date)

    prices = prices.loc[start_date:end_date]
    start_date, end_date = prices.index[0], prices.index[-1]

    ann_rtn = get_annualized_return(prices, start_date, end_date)
    vo = get_strategy_volatility(prices, start_date, end_date)

    risk_adj_rtn = ann_rtn / vo
    risk_adj_rt = round(risk_adj_rtn, 4)
    return risk_adj_rt
Пример #7
0
def get_cumulative_return(prices, start_date, end_date) -> float:
    '''calculates the cumulative return between two dates'''
    assert check_prices(prices=prices)
    assert check_time(start_date=start_date, end_date=end_date)

    prices.sort_index(inplace=True)  # make sure dates is in ascending order
    prices = prices.loc[start_date:end_date]
    start_date, end_date = prices.index[0], prices.index[-1]
    cost = prices[start_date]
    revenue = prices[end_date]
    cum_rtn = (revenue - cost) / cost

    cum_rtn = round(cum_rtn, 4)
    return cum_rtn
Пример #8
0
def get_strategy_volatility(prices, start_date, end_date) -> float:
    '''calculates the volatility of a prices time-series between two dates'''
    assert check_prices(prices=prices)
    assert check_time(start_date=start_date, end_date=end_date)

    change = prices.pct_change().dropna()
    change = change[change != 0]

    change_avg = np.mean(change)
    daily_sum = np.sum([(c - change_avg)**2 for c in change])

    vo = np.sqrt((250 / ((end_date - start_date).days - 1)) * daily_sum)
    vo = round(vo, 4)
    return vo
Пример #9
0
def get_excess_return(strategy, benchmark, start_date, end_date) -> float:
    '''calculates the excess return of a strategy over a benchmark between two dates'''
    assert check_prices(strategy=strategy, benchmark=benchmark)
    assert check_time(start_date=start_date, end_date=end_date)

    strategy = strategy.loc[start_date:end_date]
    benchmark = benchmark.loc[start_date:end_date]
    start_date, end_date = strategy.index[0], strategy.index[-1]

    r_strategy = get_daily_returns(strategy)
    r_benchmark = get_daily_returns(benchmark)

    excess_return = (r_strategy - r_benchmark).cumsum()
    excess_return = round(excess_return, 4)
    return excess_return
Пример #10
0
def get_annualized_excess_return(strategy, benchmark, start_date,
                                 end_date) -> float:
    '''calculate the annuliazed return of a strategy compared to a benchmark'''
    assert check_prices(strategy=strategy, benchmark=benchmark)
    assert check_time(start_date=start_date, end_date=end_date)

    strategy = strategy.loc[start_date:end_date]
    benchmark = benchmark.loc[start_date:end_date]
    start_date, end_date = strategy.index[0], strategy.index[-1]

    strategy_return = get_annualized_return(strategy, start_date, end_date)
    market_return = get_annualized_return(benchmark, start_date, end_date)

    ann_ex_rtn = strategy_return - market_return
    ann_ex_rtn = round(ann_ex_rtn, 4)
    return ann_ex_rtn
Пример #11
0
def get_sharpe_ratio(prices, start_date, end_date, risk_free=0.04) -> float:
    '''calculates the sharpe ratio of a prices time-series between two dates'''
    assert check_prices(prices=prices)
    assert check_time(start_date=start_date, end_date=end_date)
    assert 0 <= risk_free <= 1, 'the risk free rate must be between 0 and 1'

    prices = prices.loc[start_date:end_date]
    start_date, end_date = prices.index[0], prices.index[-1]

    ann_rtn = get_annualized_return(prices, start_date, end_date)
    excess_rtn = ann_rtn - risk_free
    vo = get_strategy_volatility(prices, start_date, end_date)

    sharpe_ratio = excess_rtn / vo
    sharpe_ratio = round(sharpe_ratio, 4)
    return sharpe_ratio
Пример #12
0
def get_turnover_ratio(start_date, end_date, holdings, df_price):
    '''
    Calculates the turnover ratio of a portfolio over a holding period.
    Only long positions are taken into consideration, short positions are ignored.
    '''
    assert check_time(start_date=start_date, end_date=end_date)

    periods = sorted(list(holdings.holdings.keys()))
    # trim log to fit within the time period
    periods = [
        period for period in periods
        if start_date <= period.start_time <= end_date
    ]
    rebalance_dates = [period.end_time for period in periods][:-1]

    start_portfolio = holdings.holdings[periods[0]]
    end_portfolio = holdings.holdings[periods[-1]]
    start_val = start_portfolio.get_net_liquidation(periods[0].start_time,
                                                    df_price)
    end_val = end_portfolio.get_net_liquidation(periods[-1].end_time, df_price)

    agg_turnover = 0
    buy_value = sell_value = []

    for day in rebalance_dates:

        pre_portfolio = holdings.get_portfolio(day).long
        post_portfolio = holdings.get_portfolio(day +
                                                pd.Timedelta(days=1)).long
        buy_portfolio = Portfolio(
            long=dict(Counter(post_portfolio) - Counter(pre_portfolio)))
        sell_portfolio = Portfolio(
            long=dict(Counter(pre_portfolio) - Counter(post_portfolio)))

        buy_value = buy_portfolio.get_net_liquidation(day, df_price)
        sell_value = sell_portfolio.get_net_liquidation(day, df_price)

        turnover = (buy_value + sell_value) / 2
        agg_turnover += turnover

    avg_liquidation = (end_val + start_val) / 2
    n_years = periods[-1].end_time.year - periods[0].start_time.year
    avg_annual_turnover = agg_turnover / n_years
    avg_turnover_ratio = avg_annual_turnover / avg_liquidation

    return avg_turnover_ratio
Пример #13
0
def get_max_drawdown(prices, start_date, end_date) -> float:
    '''calculates the maximum drawdown of a prices time-series between two dates'''
    assert check_prices(prices=prices)
    assert check_time(start_date=start_date, end_date=end_date)

    prices = prices.loc[start_date:end_date]
    start_date, end_date = prices.index[0], prices.index[-1]
    max_drawdown = 0
    # for each day, get the lowest price in the period after
    for day in prices.index:
        day_price = prices[day]
        lowest = prices.loc[day:].min()
        drawdown = (day_price - lowest) / day_price
        if drawdown > max_drawdown:
            max_drawdown = drawdown

    max_drawdown = round(max_drawdown, 4)
    return max_drawdown
Пример #14
0
def get_beta(strategy, benchmark, start_date, end_date) -> float:
    '''calculates the beta of a prices time-series between two dates'''
    assert check_prices(strategy=strategy, benchmark=benchmark)
    assert check_time(start_date=start_date, end_date=end_date)

    strategy = strategy.loc[start_date:end_date]
    benchmark = benchmark.loc[start_date:end_date]
    start_date, end_date = strategy.index[0], strategy.index[-1]

    r_strategy = get_daily_returns(strategy)
    r_benchmark = get_daily_returns(benchmark)

    var = np.var(r_benchmark, ddof=1)
    cov = np.cov(r_strategy, r_benchmark)[0][1]

    beta = cov / var
    beta = round(beta, 4)
    return beta
Пример #15
0
def get_information_ratio(strategy, benchmark, start_date, end_date) -> float:
    '''calculates the information ratio of a prices time-series between two dates'''
    assert check_prices(strategy=strategy, benchmark=benchmark)
    assert check_time(start_date=start_date, end_date=end_date)

    excess_return = get_annualized_excess_return(strategy, benchmark,
                                                 start_date, end_date)

    strategy_daily = get_daily_returns(strategy)
    benchmark_daily = get_daily_returns(benchmark)

    strategy_daily = strategy_daily[strategy_daily != 0]
    benchmark_daily = benchmark_daily[benchmark_daily != 0]

    daily_excess_return = strategy_daily - benchmark_daily
    daily_stdev = np.std(daily_excess_return) * np.sqrt(250)

    ir = excess_return / daily_stdev
    ir = round(ir, 4)
    return ir
Пример #16
0
def get_daily_win_rate(strategy, benchmark, start_date, end_date) -> float:
    '''calculates the daily win rate of a strategy compared to a benchmark'''
    assert check_prices(strategy=strategy, benchmark=benchmark)
    assert check_time(start_date=start_date, end_date=end_date)

    strategy = strategy.loc[start_date:end_date]
    benchmark = benchmark.loc[start_date:end_date]
    start_date, end_date = strategy.index[0], strategy.index[-1]

    r_strategy = get_daily_returns(strategy)
    r_benchmark = get_daily_returns(benchmark)

    daily_diff = r_strategy - r_benchmark
    win = 0
    for day in daily_diff:
        if day > 0:
            win += 1

    daily_win_rate = win / len(daily_diff)
    daily_win_rate = round(daily_win_rate, 4)
    return daily_win_rate
Пример #17
0
def get_alpha(strategy,
              benchmark,
              start_date,
              end_date,
              risk_free=0.04) -> float:
    '''calculates the alpha of a prices time-series between two dates'''
    assert check_prices(strategy=strategy, benchmark=benchmark)
    assert check_time(start_date=start_date, end_date=end_date)
    assert 0 <= risk_free <= 1, 'the risk free rate must be between 0 and 1'

    strategy = strategy.loc[start_date:end_date]
    benchmark = benchmark.loc[start_date:end_date]
    start_date, end_date = strategy.index[0], strategy.index[-1]

    market_return = get_annualized_return(benchmark, start_date, end_date)
    beta = get_beta(strategy, benchmark, start_date, end_date)

    capm = risk_free + beta * (market_return - risk_free
                               )  # asset price under the CAPM model
    annualized_return = get_annualized_return(strategy, start_date, end_date)

    alpha = annualized_return - capm
    alpha = round(alpha, 4)
    return alpha