forked from paperswithbacktest/awesome-systematic-trading
-
Notifications
You must be signed in to change notification settings - Fork 0
/
rebalancing-premium-in-cryptocurrencies.py
123 lines (102 loc) · 5.38 KB
/
rebalancing-premium-in-cryptocurrencies.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# https://quantpedia.com/strategies/rebalancing-premium-in-cryptocurrencies/
#
# The investment universe consists of 27 cryptocurrencies: BAT (Basic Attention Token), BTC (Bitcoin), BTG (Bitcoin Gold),
# DAI (Dai), DATA (Data Coin), DGB (DigiByte), EOS (EIS.io), ETH (Ethereum), FUN (FUN Token), IOTA (Iota), LRC (Loopring token),
# LTC (Litecoin), MANA (Mana coin), NEO (Neo), OMG (OMG, Formally known as OmiseGo), REQ (Request), SAN (Santiment Network Token),
# SNT (Status), TRX (Tron), WAX (Wax), XLM (Stellar), XMR (Monero), XRP (Ripple), XVG (Verge), ZEC (Zcash), ZIL (Zilliqa) and ZRX (0x).
# Two portfolios are created. The first portfolio is the daily rebalanced portfolio of all 27 cryptos to ensure that the assets have equal weights.
# The second portfolio is not rebalanced at all: an investor buys the equally-weighted crypto portfolio and lets the weights drift.
# Then the investor goes long the first portfolio and shorts the second portfolio with 70% weight.
#
# QC Implementation:
# - BTGUSD is not traded due to data error.
from AlgorithmImports import *
class RebalancingPremiumInCryptocurrencies(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2015, 1, 1)
self.SetCash(100000000)
self.cryptos = [
"BTCUSD",
"BATUSD",
# "BTGUSD",
"DAIUSD",
"DGBUSD", "EOSUSD",
"ETHUSD", "FUNUSD",
"LTCUSD", "NEOUSD",
"OMGUSD", "SNTUSD",
"TRXUSD", "XLMUSD",
"XMRUSD", "XRPUSD",
"XVGUSD", "ZECUSD",
"ZRXUSD", "LRCUSD",
"REQUSD", "SANUSD",
"WAXUSD", "ZILUSD",
"IOTAUSD",
"MANAUSD",
"DATAUSD"
]
self.short_side_percentage = 0.7
self.data = {}
self.SetBrokerageModel(BrokerageName.Bitfinex)
for crypto in self.cryptos:
# GDAX is coinmarket, but it doesn't support this many cryptos, so we choose Bitfinex
data = self.AddCrypto(crypto, Resolution.Minute, Market.Bitfinex)
data.SetFeeModel(CustomFeeModel())
data.SetLeverage(10)
self.data[crypto] = SymbolData()
self.was_traded_already = False # wait for the price data to come only once
self.prev_short_portfolio_equity = 0 # short leg equity tracking
def OnData(self, data):
if not (self.Time.hour == 9 and self.Time.minute == 30):
return
all_cryptos_are_ready = True # data warmup flag
# check if all cryptos has ready data
for crypto in self.cryptos:
if crypto in data and data[crypto]:
# update crypto price for weight calculation
self.data[crypto].last_price = data[crypto].Value
# if there is at least one crypto, which doesn't have data, then don't trade and break cycle
else:
all_cryptos_are_ready = False
break
if all_cryptos_are_ready or self.was_traded_already:
self.was_traded_already = True
# long strategy equity calculation
long_portfolio_equity = self.Portfolio.TotalPortfolioValue
long_equity_to_trade = long_portfolio_equity / len(self.cryptos)
# short strategy equity calculation
short_portfolio_equity = self.Portfolio.TotalPortfolioValue * self.short_side_percentage
short_equity_to_trade = short_portfolio_equity / len(self.cryptos)
# trading/rebalance
for crypto, symbol_obj in self.data.items():
if crypto in data and data[crypto]:
# short strategy
if not self.Portfolio[crypto].Invested:
short_q = np.floor(short_equity_to_trade / symbol_obj.last_price)
if abs(short_q) >= self.Securities[crypto].SymbolProperties.MinimumOrderSize:
self.MarketOrder(crypto, -short_q)
# long strategy
long_q = np.floor(long_equity_to_trade / symbol_obj.last_price)
# currency was traded before
if symbol_obj.quantity is not None:
# calculate quantity difference
diff_q = long_q - symbol_obj.quantity
# rebalance position
if abs(diff_q) >= self.Securities[crypto].SymbolProperties.MinimumOrderSize:
self.MarketOrder(crypto, diff_q)
# change new quantity
symbol_obj.quantity += diff_q
else:
# rebalance position
if abs(long_q) >= self.Securities[crypto].SymbolProperties.MinimumOrderSize:
self.MarketOrder(crypto, long_q)
# change new quantity
symbol_obj.quantity = long_q
class SymbolData():
def __init__(self):
self.last_price = None
self.quantity = None
# Custom fee model.
class CustomFeeModel(FeeModel):
def GetOrderFee(self, parameters):
fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005
return OrderFee(CashAmount(fee, "USD"))