Implementing carry portfolios (quantpylib code)
Our last update was the bybit wrapper:
We have added some important modifications to quantpylib’s backtesting library, that allows us to implement and test carry portfolios. I will write on this more, together with some research - but for now, we will stick with a short demo.
Previously, fees were split into execution, commission and swaps - and were all static. We have added a dynamic portion to swap cash flows.
We will demonstrate on hourly data, but this works fine on daily intervals too with funding data provided in hourly or daily granularity.
Let’s do the usual imports for the gateway, ticker and datapollers: we will use hyperliquid
import os
import pytz
import asyncio
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from datetime import datetime
from quantpylib.utilities.general import save_pickle,load_pickle
from quantpylib.standards import Period
from quantpylib.gateway.master import Gateway
from quantpylib.datapoller.master import DataPoller
from quantpylib.simulator.gene import GeneticAlpha
tickers = ['FIL', 'POLYX', 'JTO'
,'DYM', 'MATIC', 'BCH', 'INJ', 'ORDI', 'REZ', 'ETHFI', 'ADA', 'PYTH', 'BLUR',
'JUP', 'LINK', 'XRP', 'DYDX', 'kSHIB', 'WLD', 'BNB', 'TAO', 'MKR', 'AR', 'SEI', 'TIA',
'SUI', 'kBONK', 'LDO', 'RUNE', 'AVAX', 'OP', 'ONDO', 'PENDLE', 'STX', 'APT', 'W', 'ARB', 'NEAR', 'TON', 'FTM',
'ENA', 'WIF', 'kPEPE', 'DOGE', 'SOL', 'BTC', 'ETH', 'RDNT', 'CFX', 'COMP', 'AAVE', 'UNI', 'DOT', 'LOOM', 'OGN',
'PEOPLE','MANTA']
config_keys = {"hyperliquid": {}}
interval = Period.HOURLY
datapoller = DataPoller(config_keys=config_keys)
gateway = Gateway(config_keys=config_keys)
async def main():
await datapoller.init_clients()
#code goes here...
asyncio.run(main())
We retrieve some data…
period_start = datetime(2024,2,1, tzinfo=pytz.utc)
period_end = datetime(2024,8,1, tzinfo=pytz.utc)
ticker_dfs = await asyncio.gather(*[
datapoller.crypto.get_trade_bars(
ticker=ticker,
start=period_start,
end=period_end,
granularity=interval,
kline_close=True,
src="hyperliquid"
) for ticker in tickers
])
print('retrieved ohlcv')
funding_dfs = await asyncio.gather(*[
datapoller.crypto.get_funding_rates(
ticker=ticker,
start=period_start,
end=period_end,
src="hyperliquid"
) for ticker in tickers
])
Our datapoller datapoller.crypto.get_funding_rates
strings multiple requests to hpl server to get a funding rate series for the specified interval. Also, a rate limiter has been imposed on info endpoints to prevent bursting the hpl server (which has recently introduced blocking of such burst requests).
To exaggerate the impact of funding rates, on this universe data - we are going to go long/short the lowest/highest funding rates at 10/90 percentiles using the genetic alpha formulation. To do this, we need to prep the dfs
funding_rates = {ticker:df for ticker,df in zip(tickers,funding_dfs)}
dfs = {ticker:df for ticker,df in zip(tickers,ticker_dfs)}
for ticker in tickers: dfs[ticker + '_fr'] = funding_rates[ticker]['fr']
Now, let me show you how much the funding component affects this particular carry portfolio:
for use_funding in [False,True]:
strat_configs = {
"dfs":dfs,
"granularity":interval,
"instruments":tickers,
"weekend_trading":True,
"positional_inertia": 0.1,
}
if use_funding : strat_configs.update({"funding_rates":funding_rates})
alpha = GeneticAlpha(genome="ls_10/90(neg(ewma_10(fr)))",portfolio_vol=0.2,**strat_configs)
df = await alpha.run_simulation()
plt.plot((1 + df.capital_ret).cumprod(),label=f'funding : {use_funding}')
plt.legend(loc="lower right")
plt.show()
Under rational expectations theory, the carry portfolio rule that does not account for the portfolio cash flows should in fact lose money, since the funding swaps are market expectations for underlying spot returns.
The portfolio return reflects said poor performance, while the carry portfolio with cash flows accrued breaks even.
We will continue to expound on this feature and extend quantpylib. I believe this one is a significant improvement that allows quantpylib users to start testing carry strategies.
Quantpylib is our community github repo meant for annual readers of hangukquant: comment here for access
The code: