Overall Statistics
Total Orders
1173
Average Win
4.23%
Average Loss
-2.59%
Compounding Annual Return
55.527%
Drawdown
57.600%
Expectancy
0.340
Start Equity
100000
End Equity
7598293.77
Net Profit
7498.294%
Sharpe Ratio
1.135
Sortino Ratio
1.011
Probabilistic Sharpe Ratio
46.684%
Loss Rate
49%
Win Rate
51%
Profit-Loss Ratio
1.63
Alpha
0.411
Beta
0.403
Annual Standard Deviation
0.391
Annual Variance
0.153
Information Ratio
0.918
Tracking Error
0.396
Treynor Ratio
1.102
Total Fees
$209888.75
Estimated Strategy Capacity
$680000.00
Lowest Capacity Asset
BTCUSD 2XR
Portfolio Turnover
32.47%
# https://quantpedia.com/strategies/mean-reversion-and-trend-following-based-on-min-and-max-in-btc/
#
# The investment universe consists of Bitcoin, and the data are obtained from the Gemini exchange. As the first step, at day t, calculate the past ten-days maximum (MAX) and minimum (MIN) price (including the price at day t).
# If the price at day t is equal to the MAX, long the BTC and hold the position for one day. If the price at day t is equal to the MIN, long the BTC and hold the position for one day.
#
# QC implementation changes:
#   - BTC data are obtained from Coinbase (GDAX) exchange.
#   - Daily close is considered at US session UTC close time.

# region imports
from AlgorithmImports import *
# endregion

class MeanreversionandTrendFollowingBasedonMINandMAXinBTC(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2015, 1, 1)
        self.SetCash(100000)
        
        # NOTE Coinbase Pro, CoinAPI, and Bitfinex data is all set in UTC Time. This means that when accessing data from this brokerage, all data will be time stamped in UTC Time.
        self.crypto:Crypto = self.AddCrypto("BTCUSD", Resolution.Minute, Market.GDAX)
        self.crypto.SetLeverage(10)
        self.crypto.SetFeeModel(CustomFeeModel())
        self.crypto:Symbol = self.crypto.Symbol

        self.period:int = 10
        self.daily_prices:RollingWindow = RollingWindow[float](self.period)
        self.daily_close_hour:int = 22

    def OnData(self, data):
        if self.crypto in data and data[self.crypto]:
            time:datetime.datetime = self.Time
            if time.hour == self.daily_close_hour and time.minute == 0:
                price:float = data[self.crypto].Value
                self.daily_prices.Add(price)

                if self.daily_prices.IsReady:
                    daily_prices:list[float] = [x for x in self.daily_prices]
                    daily_max:float = np.max(daily_prices)
                    daily_min:float = np.min(daily_prices)

                    # open/rebalance long position
                    if price == daily_max or price == daily_min:
                        self.SetHoldings(self.crypto, 1)
                    else:
                        # close position
                        if self.Portfolio[self.crypto].Invested:
                            self.Liquidate(self.crypto)

class CustomFeeModel(FeeModel):
    def GetOrderFee(self, parameters):
        fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005
        return OrderFee(CashAmount(fee, "USD"))