Overall Statistics
Total Orders
1864
Average Win
1.14%
Average Loss
-0.35%
Compounding Annual Return
13.280%
Drawdown
26.500%
Expectancy
0.292
Start Equity
100000.00
End Equity
240841.28
Net Profit
140.841%
Sharpe Ratio
0.495
Sortino Ratio
0.656
Probabilistic Sharpe Ratio
10.268%
Loss Rate
69%
Win Rate
31%
Profit-Loss Ratio
3.23
Alpha
0.069
Beta
-0.001
Annual Standard Deviation
0.14
Annual Variance
0.02
Information Ratio
-0.09
Tracking Error
0.217
Treynor Ratio
-70.146
Total Fees
$0.00
Estimated Strategy Capacity
$100000.00
Lowest Capacity Asset
BTCUSD E3
Portfolio Turnover
72.20%
Drawdown Recovery
634
# region imports
from AlgorithmImports import *
# endregion

class HyperActiveAsparagusBarracuda(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2018, 12, 1)
        self.set_cash(100_000)

        self.set_time_zone('UTC')

        fast_period: int = 12
        slow_period: int = 26
        crossover: int = 9

        self._traded_asset: Symbol = self.add_crypto('BTCUSD', Resolution.HOUR, market=Market.BITFINEX).symbol

        # Evaluate day trend signal at UTC+4
        self._consolidator = TradeBarConsolidator(
            timedelta(days=1),
            start_time=timedelta(hours=4)
        )

        self._consolidator.data_consolidated += self._consolidation_handler
        self.subscription_manager.add_consolidator(self._traded_asset, self._consolidator)
        self._signal_date: datetime.date = datetime(1,1,1).date()

        self._daily_macd: MovingAverageConvergenceDivergence = MovingAverageConvergenceDivergence(fast_period, slow_period, crossover, MovingAverageType.EXPONENTIAL)
        self._hour_macd: MovingAverageConvergenceDivergence = self.macd(self._traded_asset, fast_period, slow_period, crossover, MovingAverageType.EXPONENTIAL, Resolution.HOUR)

        self._daily_signal_flag: bool = False
        self._update_flag: bool = False

        # Update daily indicator at UTC+4 according to documentation
        self.schedule.on(
            self.date_rules.every_day(),
            self.time_rules.at(4,0),
            self._update
        )

        # self.schedule.on(
        #     self.date_rules.every_day(),
        #     self.time_rules.at(0,0),
        #     self._rebalance
        # )

    def on_data(self, slice: Slice) -> None:
        # Update MACD indicator daily
        if self._update_flag:
            self._update_flag = False
            if slice.bars.contains_key(self._traded_asset):
                bar = slice.bars[self._traded_asset]
                self._daily_macd.update(bar.end_time, bar.close)

        if self.portfolio.invested:
            # Liquidate at first negative hour candle
            if slice[self._traded_asset].close < slice[self._traded_asset].open:
                self.liquidate()

        # if self._daily_signal_flag:
        if self._signal_date == self.time.date():
            # Evaluate hourly signal
            if (self._hour_macd.current.value > self._hour_macd.signal.current.value and
                self._hour_macd.previous.value <= self._hour_macd.signal.previous.value):
                if not self.portfolio.invested:
                    self.set_holdings(self._traded_asset, 1)
            
    def _update(self) -> None:
        self._update_flag = True

    def _consolidation_handler(self, sender: object, consolidated_bar: TradeBar) -> None:
        if consolidated_bar.close > consolidated_bar.open:
            self._signal_date = consolidated_bar.end_time.date() + timedelta(days=1)
            
    # def _rebalance(self) -> None:
    #     # Evaluate daily signal
    #     if self._daily_macd.is_ready:
    #         self._daily_signal_flag = self._daily_macd.current.value > self._daily_macd.signal.current.value