Overall Statistics
Total Trades
1502
Average Win
0.26%
Average Loss
-0.15%
Compounding Annual Return
1.195%
Drawdown
17.900%
Expectancy
0.044
Net Profit
4.217%
Sharpe Ratio
0.142
Probabilistic Sharpe Ratio
2.183%
Loss Rate
62%
Win Rate
38%
Profit-Loss Ratio
1.71
Alpha
0.015
Beta
-0.018
Annual Standard Deviation
0.094
Annual Variance
0.009
Information Ratio
-0.438
Tracking Error
0.199
Treynor Ratio
-0.751
Total Fees
$29328.98
import numpy as np

class QuantumNadionCompensator(QCAlgorithm):

    def Initialize(self):
        
        self.SetStartDate(2017, 1, 1)  # Set Start Date
        self.SetCash(100000)  # Set Strategy Cash

        _ = [self.AddCrypto(x, Resolution.Hour, Market.Bitfinex) for x in ['BTCUSD', 'ETHUSD']]
        self.SetBrokerageModel(BrokerageName.Bitfinex, AccountType.Margin)
        
        # Kalman filter parameters 
        self.theta = np.ones(2)          # Hidden state - weights of the linear model
        self.R = None                    # Variance-covariance matrix
        self.delta = 1e-6                # System noise
        self.wt = self.delta / (1 - self.delta) * np.eye(2)
        self.vt = 1e-7                   # Measurement noise
        
        # Strategy parameters
        self.invest_type = None         # long/short BTCUSD
        self.invest_portf = 0.1         # Share of the portfolio to be invested into each asset

    def OnData(self, data):
        
        # Kalman filtering update
        obs = np.array([data['ETHUSD'].Close, 1])
        y = data['BTCUSD'].Close
        
        if self.R is None:
            self.R = np.eye(2)#np.zeros((2,2))
            #self.R[0,0] = 100
        else:
            self.R = self.C + self.wt
        
        yhat = obs.dot(self.theta)
        et = y - yhat
        
        # Variance of the prediction at t=i
        Qt = obs.dot(self.R).dot(obs.T) + self.vt
        pred_err = np.sqrt(Qt)
        
        # Posterior values of states theta
        At = self.R.dot(obs.T) / Qt
        self.theta = self.theta + At.flatten() * et
        self.C = self.R - At * obs.dot(self.R)
        
        # Implement pairs trading strategy
        if self.invest_type is None:
            if et < -pred_err:
                # Undervalued BTC => long BTC, short ETH
                self.SetHoldings('BTCUSD', self.invest_portf, False)
                self.SetHoldings('ETHUSD', -self.theta[0] * self.invest_portf * data['ETHUSD'].Close / data['BTCUSD'].Close, False)
                self.invest_type = 'long'
            elif et > pred_err:
                # Overvalued BTC => long BTC, short ETH
                self.SetHoldings('BTCUSD', -self.invest_portf, False)
                self.SetHoldings('ETHUSD', self.theta[0] * self.invest_portf * data['ETHUSD'].Close / data['BTCUSD'].Close, False)
                self.invest_type = 'short'
        elif self.invest_type == 'long' and et > -pred_err or self.invest_type == 'short' and et < pred_err:
                self.SetHoldings('BTCUSD', 0, False)
                self.SetHoldings('ETHUSD', 0, False)
                self.invest_type = None