
The strategy involves trading Bitcoin-dollar pairs across exchanges, using price premiums and discounts. It buys Bitcoin in markets with low discounts and sells in Bitfinex, rebalanced daily with equally weighted portfolios.
ASSET CLASS: cryptos | REGION: Global | FREQUENCY:
Daily | MARKET: cryptos | KEYWORD: Discounts
I. STRATEGY IN A NUTSHELL
The strategy trades Bitcoin-dollar pairs across multiple exchanges, treating each pair as a separate asset. For each exchange, the Bitcoin price is compared to Bitfinex, and a discount is computed. Assets are ranked by discount and grouped into five portfolios. The strategy buys Bitcoin on exchanges with the lowest discounts (highest premiums) and sells on Bitfinex. Portfolios are equally weighted, rebalanced daily, and use Bitcoin futures with borrowed dollars, including interest.
II. ECONOMIC RATIONALE
Bitcoin prices differ across exchanges due to market inefficiencies, despite the law of one price. Pure arbitrage is costly due to fees and delays, but exploiting these price discrepancies remains profitable after trading costs, allowing investors to capture substantial returns from persistent exchange-level price gaps.
III. SOURCE PAPER
Cryptomarket Discounts [Click to Open PDF]
Borri, Nicola and Shakhnov, Kirill, LUISS University – Department of Economics and Finance, University of Surrey.
<Abstract>
This paper studies the efficiency of the cryptocurrency market by looking at the distribution
of bitcoin prices over time and across exchange-currency pairs. We document persistent
differences in relative bitcoin prices (or discounts), with a half-life of 1 day, and a distribution
which is leptokurtic, skewed to the right, with a standard deviation of 3.9%. The variability
of discounts is larger in countries with tighter capital controls due to the combined effect
of market segmentation and local supply and demand shocks, which we relate to locationspecific mining activities and investor attention.


IV. BACKTEST PERFORMANCE
| Annualised Return | 17.82% |
| Volatility | 3.28% |
| Beta | -0.005 |
| Sharpe Ratio | 4.21 |
| Sortino Ratio | N/A |
| Maximum Drawdown | N/A |
| Win Rate | 62% |
V. FULL PYTHON CODE
from AlgorithmImports import *
import data_tools
# endregion
class CryptomarketDiscounts(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2015, 1, 1)
self.SetCash(100000)
# exchanges
tickers:list[str] = [
'ABUCOINS_BTCUSD', 'BITBAY_BTCUSD', 'BITSTAMP_BTCUSD', 'BITTREX_BTCUSD',
'CEX_BTCUSD', 'COINBASE_BTCUSD', 'EXMO_BTCUSD', 'GEMINI_BTCUSD',
'HITBTC_BTCUSD', 'ITBIT_BTCUSD', 'KRAKEN_BTCUSD', 'OKCOIN_BTCUSD',
'YOBIT_BTCUSD'
]
bitfinex_btc_ticker:str = 'BITFINEX_BTCUSD'
self.quantile:int = 5
self.portfolio_percentage:float = .1
self.exchange_btc_symbols:list[Symbol] = []
# subscribe symbols
for ticker in tickers + [bitfinex_btc_ticker]:
security:Security = self.AddData(data_tools.QuantpediaBTCExchanges, ticker, Resolution.Daily)
security.SetFeeModel(data_tools.CustomFeeModel())
security.SetLeverage(5)
if ticker == bitfinex_btc_ticker:
self.bitfinex_btc_symbol:Symbol = security.Symbol
else:
self.exchange_btc_symbols.append(security.Symbol)
def OnData(self, data: Slice):
premium_by_symbol:dict[Symbol, float] = {}
# calculate discount (premium)
if data.ContainsKey(self.bitfinex_btc_symbol):
bitfinex_btc_price:float = data[self.bitfinex_btc_symbol].Value
for exch_symbol in self.exchange_btc_symbols:
if data.ContainsKey(exch_symbol):
exch_price:float = data[exch_symbol].Value
premium:float = exch_price / bitfinex_btc_price - 1
premium_by_symbol[exch_symbol] = premium
if len(premium_by_symbol) < self.quantile:
self.Liquidate()
return
quantile:int = int(len(premium_by_symbol) / self.quantile)
sorted_by_discount:list[Symbol] = [x[0] for x in sorted(premium_by_symbol.items(), key=lambda item: item[1])]
# investing in the markets with the highest discounts
long_leg:list[Symbol] = sorted_by_discount[:quantile]
long_length = len(long_leg)
# liquidate
invested = [x.Key for x in self.Portfolio if x.Value.Invested]
for exch_symbol in invested:
if exch_symbol not in long_leg and exch_symbol != self.bitfinex_btc_symbol:
self.Liquidate(exch_symbol)
# trade arbitrage
for exch_symbol in long_leg:
self.SetHoldings(exch_symbol, (1 / long_length) * self.portfolio_percentage)
self.SetHoldings(self.bitfinex_btc_symbol, -1 * self.portfolio_percentage)