Utils

Utility functions for portfolio analysis

source

mtd

 mtd (df)

Restrict a dataframe to only month to date


source

qtd

 qtd (df)

Restrict a dataframe to only quarter to date (quarters starting in Jan, Apr, Jun, Oct)


source

ytd

 ytd (df)

Restrict a dataframe to only year to date


source

pandas_date

 pandas_date (df, dates)

Filters a dataframe (with date as the index), to its values on specific days

df = pd.DataFrame({
    'name': ['alice','bob','charlie','ryan'],
    'DoB': ['2005-10-15','2002-09-03','2001-01-01','1999-12-31']
})
df = df.set_index('DoB')
dates = ['2001-01-01','2005-10-15']
pandas_date(df, dates)
name
DoB
2005-10-15 alice
2001-01-01 charlie

source

pandas_current_month

 pandas_current_month (df)

an alternative method to mtd. remove?


source

multi_shift

 multi_shift (df, shift=3)

Get last N rows relative to another row in dataframe of values, with a sorted index

df = pd.DataFrame({
    'value': [10,15,13,7,12,6],
    'Date': ['1999-10-15','1999-10-16','1999-10-17','1999-10-18','1999-10-19','1999-10-20']
})
df = df.set_index('Date')
multi_shift(df,3)
value value1 value2
Date
1999-10-15 10 NaN NaN
1999-10-16 15 10.0 NaN
1999-10-17 13 15.0 10.0
1999-10-18 7 13.0 15.0
1999-10-19 12 7.0 13.0
1999-10-20 6 12.0 7.0

source

to_excess_returns

 to_excess_returns
                    (returns:Union[ForwardRef('~pd.Series'),ForwardRef('~p
                    d.DataFrame')], rf:Union[float,ForwardRef('~pd.Series'
                    ),ForwardRef('~pd.DataFrame')], nperiods:int=None)

Calculates excess returns by subtracting risk-free returns from total returns

Type Default Details
returns Union Returns
rf Union Risk-Free rate(s)
nperiods int None Will convert rf to different frequency using deannualize
Returns Union Returns - risk free rate

source

prepare_returns

 prepare_returns (data, rf=0.0, nperiods=None)

Converts price data into returns + cleanup


source

to_returns

 to_returns (prices, rf=0.0)

Calculates the simple arithmetic returns of a price series


source

to_prices

 to_prices (returns, base=100000.0)

Converts returns series to price data

# mock returns array
data = np.array([0.1, -0.2, 0.25, 0.5, -0.8])
returns = pd.Series(data)
to_prices(returns, base=100)
0    110.0
1     88.0
2    110.0
3    165.0
4     33.0
dtype: float64

source

to_log_returns

 to_log_returns (returns, rf=0.0, nperiods=None)

Converts returns series to log returns

The log return is defined as \(lr_{i}=\log\frac{p_{i}}{p_{i-1}}\) where \(lr_{i}\) and \(p_{i}\) are the log returns and price on day \(i\) respectively


source

log_returns

 log_returns (returns, rf=0.0, nperiods=None)

Shorthand for to_log_returns


source

exponential_stdev

 exponential_stdev (returns, window=30, is_halflife=False)

Returns series representing exponential volatility of returns


source

download_prices

 download_prices (ticker, period='max')

download daily adjusted close prices from yahoo

download_prices('SPY',  period="5y").tail(10)
Date
2022-09-16    385.559998
2022-09-19    388.549988
2022-09-20    384.089996
2022-09-21    377.390015
2022-09-22    374.220001
2022-09-23    367.950012
2022-09-26    364.309998
2022-09-27    363.380005
2022-09-28    370.529999
2022-09-29    362.790009
Name: Close, dtype: float64

source

download_returns

 download_returns (ticker, period='max')

download returns from yahoo

download_returns('SPY',  period="5y").tail(10)
Date
2022-09-16   -0.007629
2022-09-19    0.007755
2022-09-20   -0.011479
2022-09-21   -0.017444
2022-09-22   -0.008400
2022-09-23   -0.016755
2022-09-26   -0.009893
2022-09-27   -0.002553
2022-09-28    0.019676
2022-09-29   -0.020889
Name: Close, dtype: float64

source

prepare_benchmark

 prepare_benchmark (benchmark=None, period='max', rf=0.0,
                    prep_returns=True)

Fetch benchmark if ticker is provided, and pass through prepare_returns() period can be options or (expected) pd.DatetimeIndex range

prepare_benchmark('SPY',  period="5y").tail(10)
Date
2022-09-16   -0.007629
2022-09-19    0.007755
2022-09-20   -0.011479
2022-09-21   -0.017444
2022-09-22   -0.008400
2022-09-23   -0.016755
2022-09-26   -0.009893
2022-09-27   -0.002553
2022-09-28    0.019676
2022-09-29   -0.020889
Name: Close, dtype: float64

source

rebase

 rebase (prices, base=100.0)

Rebase all series to a given intial base. This makes comparing/plotting different series together easier. Args: * prices: Expects a price series/dataframe * base (number): starting value for all series.

prices = download_prices('SPY',  period="5y")
rebase(prices, 100).head(10)
Date
2017-10-02    100.000000
2017-10-03    100.214005
2017-10-04    100.332908
2017-10-05    100.927388
2017-10-06    100.812445
2017-10-09    100.645994
2017-10-10    100.911539
2017-10-11    101.070056
2017-10-12    100.919460
2017-10-13    101.042305
Name: Close, dtype: float64

source

group_returns

 group_returns (returns, groupby, compounded=True)

Summarize returns group_returns(df, df.index.year) group_returns(df, [df.index.year, df.index.month])

returns = download_returns('SPY',  period="1y")
group_returns(returns, [returns.index.year,returns.index.month])
Date  Date
2021  9       0.000000
      10      0.070163
      11     -0.008035
      12      0.046248
2022  1      -0.052741
      2      -0.029517
      3       0.037590
      4      -0.087769
      5       0.002257
      6      -0.082460
      7       0.092087
      8      -0.040802
      9      -0.078191
Name: Close, dtype: float64

Note that for year and month, it creates a multicolumns index


source

aggregate_returns

 aggregate_returns (returns, period=None, compounded=True)

Aggregates returns based on date periods, and flattens index

returns = download_returns('SPY',  period="1y")
aggregate_returns(returns, 'M')
(2021, 9)     0.000000
(2021, 10)    0.070164
(2021, 11)   -0.008035
(2021, 12)    0.046248
(2022, 1)    -0.052741
(2022, 2)    -0.029517
(2022, 3)     0.037590
(2022, 4)    -0.087769
(2022, 5)     0.002257
(2022, 6)    -0.082460
(2022, 7)     0.092087
(2022, 8)    -0.040802
(2022, 9)    -0.078191
Name: Close, dtype: float64

source

prepare_prices

 prepare_prices (data, base=1.0)

Converts return data into prices + cleanup


source

round_to_closest

 round_to_closest (val, res, decimals=None)

Round to closest resolution


source

file_stream

 file_stream ()

Returns a file stream


source

make_index

 make_index (ticker_weights:dict, rebalance='1M', period:str='max',
             returns:Union[ForwardRef('pd.Series'),ForwardRef('pd.DataFram
             e')]=None, match_dates:bool=False)

Makes an index out of the given tickers and weights. Optionally you can pass a dataframe with the returns. If returns is not given it try to download them with yfinance

Type Default Details
ticker_weights dict A python dict with tickers as keys and weights as values
rebalance str 1M Pandas resample interval or None for never
period str max time period of the returns to be downloaded
returns Union None Returns: If provided, check if returns for the ticker are in this dataframe, before trying to download from yahoo
match_dates bool False whether to match dates?
Returns Union Returns for the index
make_index({'SPY':0.5, 'QQQ':0.5}, period = '5y').tail(10)
C:\Users\kirpa\AppData\Local\Temp\ipykernel_14216\3101533360.py:62: FutureWarning: Dropping of nuisance columns in DataFrame reductions (with 'numeric_only=None') is deprecated; in a future version this will raise TypeError.  Select only valid columns before calling the reduction.
  return index[index.index <= last_day].sum(axis=1)
Date
2022-09-16   -0.013744
2022-09-19    0.015542
2022-09-20   -0.019450
2022-09-21   -0.035350
2022-09-22   -0.020708
2022-09-23   -0.033036
2022-09-26   -0.014031
2022-09-27   -0.002152
2022-09-28    0.039568
2022-09-29   -0.049717
dtype: float64

source

make_portfolio

 make_portfolio (returns, start_balance=100000.0, mode='comp',
                 round_to=None)

Calculates compounded value of portfolio