Overall Statistics
Total Trades
0
Average Win
0%
Average Loss
0%
Compounding Annual Return
0%
Drawdown
0%
Expectancy
0
Net Profit
0%
Sharpe Ratio
0
Probabilistic Sharpe Ratio
0%
Loss Rate
0%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
0
Beta
0
Annual Standard Deviation
0
Annual Variance
0
Information Ratio
-0.488
Tracking Error
0.24
Treynor Ratio
0
Total Fees
$0.00
Estimated Strategy Capacity
$0
Lowest Capacity Asset
Portfolio Turnover
0%
from AlgorithmImports import *
from collections import deque

class MaccaDacca(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2021, 1, 1)
        self.SetEndDate(2023, 11, 8)
        self.SetCash(3000)

        self.symbol = self.AddEquity('AAPL', Resolution.Hour).Symbol
        self.window = deque(maxlen=3)  # Use deque as a rolling window
        self.macd = self.MACD(self.symbol, 1, 2, 2, MovingAverageType.Exponential)
        self.candle_states = []
        self.SetBenchmark(self.symbol)

        # Pre-fill the window with historical data
        history_data = self.History(self.symbol, 3, Resolution.Hour)
        for index, row in history_data.iterrows():
            candle = TradeBar()
            candle.Open = row['open']
            candle.Close = row['close']
            candle.High = row['high']
            candle.Low = row['low']
            candle.Volume = row['volume']
            self.window.append(candle)

        self.Settings.FreePortfolioValuePercentage = 0.05


    def OnData(self, data: Slice):
        try:
            #if data.ContainsKey(self.symbol) and self.macd.IsReady: 
            #    self.window.append(data[self.symbol])

            if len(self.window) == 3:  # Check if the rolling window is ready
                    recent_candle = self.window[-1]
                    middle_candle = self.window[-2]
                    oldest_candle = self.window[-3]
                    self.CheckCandle(recent_candle, middle_candle, oldest_candle)

            self.macd_value = self.macd.Current.Value
            self.macd_indicator = self.CheckMACD(self.macd_value)

            # if you have yet to invest, open a long position 
            if not self.Portfolio[self.symbol].Invested and data[self.symbol] is not None: 
                quantity = (self.Portfolio.Cash / 2) / recent_candle.Close
                price = data[self.symbol].Price
                
                if self.candle_states:
                    # long positions
                    if self.macd_indicator == 'Bull' and self.candle_states[0] == 'Bullish Engulfing'\
                            or self.macd_indicator == 'Bull' and self.candle_states[0] == 'Bearish Doji'\
                            or self.macd_indicator == 'Bull' and self.candle_states[0] == 'Bullish Momentum'\
                            or self.macd_indicator == 'Bull' and self.candle_states[0] == 'Bullish Hammer':
                        self.MarketTicket = self.LimitOrder(self.symbol, quantity, price)
                        self.TrailingStop = self.TrailingStopOrder(self.symbol, quantity, price * 0.9, 1.2, False)

                    # short positions
                    elif self.macd_indicator == 'Bear' and self.candle_states[0] == 'Bearish Engulfing'\
                            or self.macd_indicator == 'Bear' and self.candle_states[0] == 'Bearish Doji'\
                            or self.macd_indicator == 'Bear' and self.candle_states[0] == 'Bearish Momentum'\
                            or self.macd_indicator == 'Bear' and self.candle_states[0] == 'Bearish Hammer':
                        self.MarketTicket = self.LimitOrder(self.symbol, -quantity, price)
                        self.TrailingStop = self.StopMarketOrder(self.symbol, -quantity, price * 1.1)

        except KeyError: 
            self.Debug('Might be an error with data')

        # if you already have an open position, hold until the trailing stop loss or indicator turns to 
        # a large red candle or a doji green candle 

    def CheckBull(self, data): 
        if data.Close > data.Open: 
            return True
        else: 
            return False

    def CheckBear(self, data):
        if data.Close < data.Open: 
            return True
        else: 
            return False


    def RollCandleStates(self, candle_states): 
        if len(candle_states) > 3:
            candle_states.pop()


    def CheckCandle(self, recent_candle, middle_candle, oldest_candle): 
        # Check for engulfing candles  
        if self.CheckBull(recent_candle) and self.CheckBear(middle_candle):
            if recent_candle.Close > middle_candle.Open and recent_candle.Open < middle_candle.Close:
                self.candle_states.insert(0, 'Bullish Engulfing')
                self.RollCandleStates(self.candle_states)

        if self.CheckBear(recent_candle) and self.CheckBull(middle_candle):
            if recent_candle.Close < middle_candle.Open and recent_candle.Open > middle_candle.Close: 
                self.candle_states.insert(0, 'Bearish Engulfing')
                self.RollCandleStates(self.candle_states)

        if self.CheckBear(recent_candle) and self.CheckBear(middle_candle):
            if recent_candle.Close < middle_candle.Close and recent_candle.Open > middle_candle.Open: 
                self.candle_states.insert(0, 'Bearish Engulfing')
                self.RollCandleStates(self.candle_states)

        elif self.CheckBull(recent_candle) and self.CheckBull(middle_candle):
            if recent_candle.Close > middle_candle.Close and recent_candle.Open < middle_candle.Open:
                self.candle_states.insert(0, 'Bullish Engulfing')
                self.RollCandleStates(self.candle_states)

        # Check for doji candles 
        if self.CheckBull(recent_candle) and recent_candle.Open * 1.1 < recent_candle.Close: 
            self.candle_states.insert(0, 'Bullish Doji')
            self.RollCandleStates(self.candle_states)

        elif self.CheckBear(recent_candle) and recent_candle.Close * 1.1 < recent_candle.Open: 
            self.candle_states.insert(0, 'Bearish Doji')
            self.RollCandleStates(self.candle_states)

        # Check for momentum candles (momentum candles)
        if self.CheckBull(recent_candle) and any('Engulfing' in state for state in self.candle_states):
            size = abs(recent_candle.Close - recent_candle.Open)
            previous_size = abs(middle_candle.Close - middle_candle.Open)
            if size >= previous_size:
                self.candle_states.insert(0, 'Bullish Momentum')
                self.RollCandleStates(self.candle_states)
                    
        elif self.CheckBear(recent_candle) and any('Engulfing' in state for state in self.candle_states):
            size = abs(recent_candle.Close - recent_candle.Open)
            previous_size = abs(middle_candle.Close - middle_candle.Open)
            if size >= previous_size:
                self.candle_states.insert(0, 'Bearish Momentum')
                self.RollCandleStates(self.candle_states)     
        
        # check for Maribozu candles 
        if self.CheckBull(recent_candle) and recent_candle.Open == recent_candle.Low and recent_candle.Close == recent_candle.High:
            self.candle_states.insert(0, 'Bullish Maribozu')
            self.RollCandleStates(self.candle_states)

        elif self.CheckBear(recent_candle) and recent_candle.Open == recent_candle.High and recent_candle.Close == recent_candle.Low:
            self.candle_states.insert(0, 'Bearish Maribozu') 
            self.RollCandleStates(self.candle_states)

        # check for hammer candle (wick is longer than body of candle)
        if self.CheckBull(recent_candle) and recent_candle.Open - recent_candle.Low > recent_candle.Close - recent_candle.Open: 
            self.candle_states.insert(0, 'Bullish Hammer')
            self.RollCandleStates(self.candle_states)

        elif self.CheckBear(recent_candle) and recent_candle.Close - recent_candle.Low > recent_candle.Open - recent_candle.Close: 
            self.candle_states.insert(0, 'Bearish Shooting Star')
            self.RollCandleStates(self.candle_states)


    def CheckMACD(self, macd_value):
        if macd_value > 0: 
            return 'Bull'
        else:
            return 'Bear'


    def OnOrderEvent(self, orderEvent): 
        if orderEvent.Status != OrderStatus.Filled: 
            return 
        
        elif orderEvent.Status == OrderStatus.Filled and self.Portfolio[self.symbol].IsLong:
            # Check if the order event was for a trailing stop order
            if orderEvent.OrderId == self.TrailingStop.OrderId:
                self.Liquidate(self.symbol)
        
        elif orderEvent.Status == OrderStatus.Filled and self.Portfolio[self.symbol].IsShort:
            # Check if the order event was for a trailing stop order
            if orderEvent.OrderId == self.TrailingStop.OrderId:
                self.Liquidate(self.symbol)