Overall Statistics
Total Orders
3365
Average Win
0.35%
Average Loss
-0.49%
Compounding Annual Return
15.305%
Drawdown
17.800%
Expectancy
0.039
Start Equity
100000.00
End Equity
133055.75
Net Profit
33.056%
Sharpe Ratio
0.731
Sortino Ratio
0.686
Probabilistic Sharpe Ratio
32.140%
Loss Rate
40%
Win Rate
60%
Profit-Loss Ratio
0.72
Alpha
0.128
Beta
-0.092
Annual Standard Deviation
0.153
Annual Variance
0.024
Information Ratio
-0.238
Tracking Error
0.276
Treynor Ratio
-1.225
Total Fees
$0.00
Estimated Strategy Capacity
$530000.00
Lowest Capacity Asset
BTCUSD 2XR
Portfolio Turnover
220.02%
Drawdown Recovery
98
#region imports
from AlgorithmImports import *
#endregion

class CryingVioletCaterpillar(QCAlgorithm):

    def initialize(self):
        self.set_start_date(2020,1,1)
        self.set_end_date(2022,1,1)
        self.set_cash(100000)

        self.btc=self.add_crypto("BTCUSD",Resolution.MINUTE)

        self.MACD=self.macd(self.btc.symbol,12,26,9,MovingAverageType.EXPONENTIAL,Resolution.MINUTE)
        self.BB=self.bb(self.btc.symbol,20,2,MovingAverageType.SIMPLE,Resolution.MINUTE)

        self.position_size=0.8

        self.last_trade_time=datetime.min
        self.min_trade_interval=timedelta(minutes=15)

        self.set_warm_up(50)

    def on_data(self,data:Slice):
        if self.is_warming_up or not data.contains_key(self.btc.symbol):
            return

        if not(self.MACD.is_ready and self.BB.is_ready):
            return

        current_time=self.time
        if current_time-self.last_trade_time<self.min_trade_interval:
            return

        price=data[self.btc.symbol].close
        macd_value=self.MACD.current.value
        macd_signal=self.MACD.signal.current.value
        macd_histogram=self.MACD.histogram.current.value

        bb_upper=self.BB.upper_band.current.value
        bb_middle=self.BB.middle_band.current.value
        bb_lower=self.BB.lower_band.current.value
        bb_width=(bb_upper-bb_lower)/bb_middle

        strong_long_signal=(
            macd_value>macd_signal and
            macd_histogram>0 and
            price<=bb_lower*1.002 and
            bb_width>0.02
        )

        strong_short_signal=(
            macd_value<macd_signal and
            macd_histogram<0 and
            price>=bb_upper*0.998 and
            bb_width>0.02
        )

        moderate_long_signal=(
            macd_value>macd_signal and
            price<bb_middle and
            not self.portfolio[self.btc.symbol].is_long
        )

        moderate_short_signal=(
            macd_value<macd_signal and
            price>bb_middle and
            not self.portfolio[self.btc.symbol].is_short
        )

        exit_long_strong=(
            (price>bb_upper and macd_histogram<0)
        )

        exit_short_strong=(
            (price<bb_lower and macd_histogram>0)
        )

        exit_long_moderate=(
            price>bb_middle and
            macd_value<macd_signal
        )

        exit_short_moderate=(
            price<bb_middle and
            macd_value>macd_signal
        )

        current_holdings=self.portfolio[self.btc.symbol].quantity

        if current_holdings==0:
            if strong_long_signal:
                self.set_holdings(self.btc.symbol,self.position_size)
                self.last_trade_time=current_time
                self.debug(f"STRONGLONGat{price:.2f}|MACD:{macd_histogram:.4f}")

            elif strong_short_signal:
                self.set_holdings(self.btc.symbol,-self.position_size)
                self.last_trade_time=current_time
                self.debug(f"STRONGshortat{price:.2f}|MACD:{macd_histogram:.4f}")

            elif moderate_long_signal and bb_width>0.015:
                self.set_holdings(self.btc.symbol,self.position_size*0.6)
                self.last_trade_time=current_time
                self.debug(f"MODERATELONGat{price:.2f}")

            elif moderate_short_signal and bb_width>0.015:
                self.set_holdings(self.btc.symbol,-self.position_size*0.6)
                self.last_trade_time=current_time
                self.debug(f"MODERATEshortat{price:.2f}")

        elif current_holdings>0:
            if exit_long_strong:
                self.liquidate(self.btc.symbol)
                self.last_trade_time=current_time
                self.debug(f"EXITLONGSTRONGat{price:.2f}")

            elif exit_long_moderate and abs(current_holdings)<self.portfolio.total_portfolio_value*self.position_size*0.7:
                self.liquidate(self.btc.symbol)
                self.last_trade_time=current_time
                self.debug(f"EXITLONGMODERATEat{price:.2f}")

        elif current_holdings<0:
            if exit_short_strong:
                self.liquidate(self.btc.symbol)
                self.last_trade_time=current_time
                self.debug(f"EXITshortSTRONGat{price:.2f}")

            elif exit_short_moderate and abs(current_holdings)<self.portfolio.total_portfolio_value*self.position_size*0.7:
                self.liquidate(self.btc.symbol)
                self.last_trade_time=current_time
                self.debug(f"EXITshortMODERATEat{price:.2f}")