Overall Statistics
Total Trades
7
Average Win
0.58%
Average Loss
0%
Compounding Annual Return
48.493%
Drawdown
1.500%
Expectancy
0
Net Profit
3.415%
Sharpe Ratio
3.821
Probabilistic Sharpe Ratio
78.916%
Loss Rate
0%
Win Rate
100%
Profit-Loss Ratio
0
Alpha
0
Beta
0
Annual Standard Deviation
0.087
Annual Variance
0.007
Information Ratio
3.821
Tracking Error
0.087
Treynor Ratio
0
Total Fees
$7.00
Estimated Strategy Capacity
$430000000.00
Lowest Capacity Asset
SPY R735QTJ8XC9X
from AlgorithmImports import *
#endregion

import numpy as np
import pandas as pd

class GapAndGoAlgorithm(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2023, 1, 1)  # Set start date
        self.SetEndDate(2023, 1, 31)  # Set end date
        self.SetCash(100000)  # Set strategy cash

        # Add SPY to the universe and request daily data for it
        self.AddEquity("SPY", Resolution.Daily)
        
        # Use universe selection to select a universe of stocks that are gapping up
        self.UniverseSettings.Resolution = Resolution.Daily
        self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction)
        self.SelectionCount = 10
        
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)  # Set brokerage model
        
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("SPY", 30), self.GapAndGo)  # Schedule GapAndGo function to run after market open

    def CoarseSelectionFunction(self, coarse):
        # Select only stocks with a price above $5 and a daily dollar volume above $10,000,000
        selected = [x.Symbol for x in coarse if x.Price > 5 and x.DollarVolume > 10000000]
        return selected
    
    def FineSelectionFunction(self, fine):
        # Select stocks that gapped up at least 1% and have positive earnings and revenue growth
        fine = [x for x in fine if hasattr(x.EarningReports, 'BasicEPSGrowth') and x.EarningReports.BasicEPSGrowth.OneYear > 0 and x.FinancialStatements.RevenueGrowth.OneYear > 0]
        fine = [x for x in fine if (x.Open - x.Close[-2])/x.Close[-2] > 0.01]
        fine = sorted(fine, key=lambda x: (x.Volume, x.Price), reverse=True)[:self.SelectionCount]
        return [x.Symbol for x in fine]

    def GapAndGo(self):
        # Loop over all securities in the portfolio
        for security in self.Portfolio.Values:
            # Check if the security is currently tradable
            if not self.Securities[security.Symbol].IsTradable:
                continue

            # Get daily historical data for the last 2 days
            history = self.History([security.Symbol], 2, Resolution.Daily)

            # Get opening and closing prices for the last 2 days
            opens = history.loc[security.Symbol].open.values
            closes = history.loc[security.Symbol].close.values

            # Check if the open price today is greater than the close price of yesterday
            if opens[-1] > closes[-2]:
                # Calculate the percentage change between the two days
                pct_change = (closes[-1] - closes[-2]) / closes[-2]

                # If the percentage change is greater than 1% (i.e., a gap), execute a trade
                if pct_change > 0.01:
                    # Calculate the stop loss and take profit levels based on the current price
                    current_price = self.Securities[security.Symbol].Price
                    stop_loss = np.array([current_price * 0.98]).item()
                    take_profit = np.array([current_price * 1.02]).item()
                    quantity = 100
                    # Place the market order with stop loss and take profit levels
                    #, False, None, stop_loss, take_profit
                    self.ticket = self.MarketOrder(security.Symbol, quantity)
                    self.LimitOrder(security.Symbol, -quantity, self.ticket.AverageFillPrice* 1.02)
                    self.StopMarketOrder(security.Symbol, -quantity, self.ticket.AverageFillPrice* 0.98)
    
    def OnOrderEvent(self, orderEvent):
        order = self.Transactions.GetOrderById(orderEvent.OrderId)
        if order.Status == OrderStatus.Filled:
            if order.Type == OrderType.Limit or order.Type == OrderType.StopMarket:
                self.Transactions.CancelOpenOrders(order.Symbol)