| Overall Statistics |
|
Total Orders 3454 Average Win 0.89% Average Loss -0.78% Compounding Annual Return 28.888% Drawdown 30.500% Expectancy 0.198 Start Equity 100000.0 End Equity 1204086.73 Net Profit 1104.087% Sharpe Ratio 1.202 Sortino Ratio 0.985 Probabilistic Sharpe Ratio 77.684% Loss Rate 44% Win Rate 56% Profit-Loss Ratio 1.14 Alpha 0.182 Beta 0.026 Annual Standard Deviation 0.153 Annual Variance 0.024 Information Ratio 0.498 Tracking Error 0.21 Treynor Ratio 7.182 Total Fees $83086.22 Estimated Strategy Capacity $61000.00 Lowest Capacity Asset BTCUSD E3 Portfolio Turnover 96.18% |
# https://quantpedia.com/strategies/overnight-effect-during-high-volatility-days-in-bitcoin/
#
# The investment universe consists of Bitcoin cryptocurrency.
# Firstly, each day at 0.00 UTC, calculate the 30-day historical volatility of the BTC returns. Subsequently, determine the median of the historical volatility over one
# year (365 days). Use this moving median value as the benchmark for categorizing the next 24-hour period as “High Volatility” (the 30-day historical volatility is above
# the moving median) or “Low Volatility” (the 30-day historical volatility is above the moving median).
# Finally, if the period is classified as “High Volatility” period, buy Bitcoin at 21:00 and sell it at 23:00.
# region imports
from AlgorithmImports import *
import numpy as np
from typing import List
from pandas.core.frame import DataFrame
# endregion
class OvernightEffectduringHighVolatilityDaysinBitcoin(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2015, 1, 1)
self.SetCash(100000)
self.volatility_period:int = 30 * 24
self.history_period:int = 365
self.warmup_period:int = self.history_period * 24
self.btc:Symbol = self.AddCrypto('BTCUSD', Resolution.Hour, Market.Bitfinex).Symbol
self.Securities[self.btc].SetFeeModel(CustomFeeModel())
self.calculation_hour:int = 0 # calculation at 00:00
self.traded_window:List[int] = [21, 23] # trading from 21:00 to 23:00
self.trade_flag:bool = False
self.settings.daily_precise_end_time = False
self.settings.minimum_order_margin_portfolio_percentage = 0.
self.btc_volatility:RollingWindow = RollingWindow[float](self.history_period)
self.SetWarmup(self.warmup_period, Resolution.Hour)
def OnData(self, data: Slice) -> None:
if self.UtcTime.hour == self.calculation_hour:
monthly_volatility:float = self.History(self.btc, self.volatility_period, Resolution.Hour).close.unstack(level=0).pct_change().std().values[0]
self.btc_volatility.Add(monthly_volatility)
if self.IsWarmingUp:
return
if not self.btc_volatility.IsReady:
return
if self.btc_volatility[0] > np.median(list(self.btc_volatility)[1:]):
self.trade_flag = True
# trade execution
if self.UtcTime.hour == self.traded_window[0]:
if not self.trade_flag:
return
self.trade_flag = False
if self.btc in data and data[self.btc]:
self.SetHoldings(self.btc, 1)
if self.UtcTime.hour == self.traded_window[1] and self.Portfolio[self.btc].Invested:
self.Liquidate()
# custom fee model
class CustomFeeModel(FeeModel):
def GetOrderFee(self, parameters):
fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005
return OrderFee(CashAmount(fee, "USD"))