Overall Statistics
Total Orders
32
Average Win
4.62%
Average Loss
-3.74%
Compounding Annual Return
8.188%
Drawdown
16.200%
Expectancy
0.341
Start Equity
100000
End Equity
126648.61
Net Profit
26.649%
Sharpe Ratio
0.438
Sortino Ratio
0.347
Probabilistic Sharpe Ratio
22.070%
Loss Rate
40%
Win Rate
60%
Profit-Loss Ratio
1.23
Alpha
0.023
Beta
0.195
Annual Standard Deviation
0.094
Annual Variance
0.009
Information Ratio
-0.309
Tracking Error
0.175
Treynor Ratio
0.211
Total Fees
$72.89
Estimated Strategy Capacity
$73000000.00
Lowest Capacity Asset
QQQ RIWIV7K5Z9LX
Portfolio Turnover
2.54%
Drawdown Recovery
707
# region imports
from AlgorithmImports import *
# endregion

class SleepyApricotDog(QCAlgorithm):

    def initialize(self):
        self.set_start_date(2018, 1, 1)
        self.set_end_date(2021, 1, 1)
        self.set_cash(100000)

        self.qqq = self.add_equity("QQQ", Resolution.HOUR).symbol
        
        self.entryTicket = None
        self.stopMarketTicket = None
        self.entryTime = self.time
        self.stopMarketOrderFillTime = self.time
        self.highestPrice = 0

    def on_data(self, data: Slice):
        
        # wait 30 days after last exit
        if (self.time - self.stopMarketOrderFillTime).days < 30:
            return

        price = self.securities[self.qqq].price
        
        # send entry limit order
        if not self.portfolio.invested and not self.transactions.get_open_orders(self.qqq):
            quantity = self.calculate_order_quantity(self.qqq, 0.9)
            self.entryTicket = self.limit_order(self.qqq, quantity, price, tag="Entry Order")
            self.entryTime = self.time 

        # move limit price if not filled after 1 day
        if (self.time - self.entryTime).days > 1 and self.entryTicket.status != OrderStatus.FILLED:
            self.entryTime = self.time
            updateFileds = UpdateOrderFields()
            updateFileds.limit_price = price
            self.entryTicket.update(updateFileds)

        # move up trailing stop price
        if self.stop_market_order is not None and self.portfolio.invested:
            if price > self.highestPrice:
                self.highestPrice = price
                updateFileds = UpdateOrderFields()
                updateFileds.stop_price = price * 0.95
                self.stopMarketTicket.update(updateFileds)
                self.debug(updateFileds.stop_price)

    def on_order_event(self, order_event):
        if order_event.status != OrderStatus.FILLED:
            return

        # send stop loss order if entry limit order is filled
        if self.entryTicket is not None and self.entryTicket.order_id == order_event.order_id:
            self.stopMarketTicket = self.stop_market_order(self.qqq, -self.entryTicket.quantity, 0.95*self.entryTicket.average_fill_price)

        # save fill time of stop loss order
        if self.stopMarketTicket is not None and self.stopMarketTicket.order_id == order_event.order_id:
            self.stopMarketOrderFillTime = self.time
            self.highestPrice = 0