Overall Statistics
from AlgorithmImports import *
from datetime import date

class SymbolData:
    def __init__(self, open_price: float, current_date: date):
        self.open_price = open_price
        self.bought_today = False
        self.entry_price = 0.0
        self.current_date = current_date

    def __repr__(self) -> str:
        return f"Open: {self.open_price}, Bought: {self.bought_today}, Entry: {self.entry_price}"
class GapUpBreakout(QCAlgorithm):
    def initialize(self) -> None:
        self.set_start_date(2025, 5, 25)
        self.set_end_date(2025, 5, 29)
        self.set_cash(100000)

        self.add_universe(self.coarse_selection_function)
        self.add_equity("SBET")


        self._symbol_data = {}
        self._previous_minute = -1

    def coarse_selection_function(self, fundamental: List[Fundamental]) -> List[Symbol]:
        selected = []
        for f in fundamental:
            self.debug(f"Coarse selection: {f.symbol.value}, market: {f.symbol.id.market}, has_fund: {f.has_fundamental_data}")
            if f.has_fundamental_data and f.symbol.id.market in ("usa", "nys", "ase", "arca", "nasdaq"):
                selected.append(f.symbol)

        self.debug(f"Universe selected: {[str(s) for s in selected]}")

        if any(str(s) == "SBET" for s in selected):
            self.debug("SBET is present in the universe on this day.")
        else:
            self.debug("SBET is NOT present in the universe on this day.")

        return selected

    def on_data(self, data: Slice) -> None:
        for symbol, bar in data.bars.items():
            # Additional debug: check if symbol is SBET.
            if symbol.value == "SBET":
                self.debug("SBET: Entering on_data for position management.")

            if symbol not in self._symbol_data:
                self.debug(f"{symbol.value}: Not in symbol_data, skipping.")
                continue

            if symbol.value == "SBET":
                self.debug("SBET: Found in symbol_data, checking if bought today.")

            sd = self._symbol_data[symbol]
            if not sd.bought_today:
                self.debug(f"{symbol.value}: Not bought today, skipping position management.")
                continue

            current_price = bar.close
            if sd.entry_price > 0:
                take_profit_level = sd.open_price * 1.50
                stop_loss_level = sd.open_price * 1.10
                self.debug(
                    f"{symbol.value}: Managing position. Open: {sd.open_price}, "
                    f"Entry: {sd.entry_price}, Current: {current_price}, "
                    f"TP: {take_profit_level}, SL: {stop_loss_level}"
                )
                if current_price >= take_profit_level:
                    self.debug(f"{symbol.value}: Hitting take profit, liquidating.")
                    self.liquidate(symbol)
                    sd.bought_today = False
                    sd.entry_price = 0.0
                    if symbol.value == "SBET":
                        self.debug("SBET: Liquidated at take profit.")
                elif current_price <= stop_loss_level:
                    self.debug(f"{symbol.value}: Hitting stop loss, liquidating.")
                    self.liquidate(symbol)
                    sd.bought_today = False
                    sd.entry_price = 0.0
                    if symbol.value == "SBET":
                        self.debug("SBET: Liquidated at stop loss.")

        # Only run once per second
        if self.time.minute != self._previous_minute:
            self._previous_minute = self.time.minute
            current_date = self.time.date()

            for symbol in self.active_securities.keys:
                # Additional debug: check if symbol is SBET.
                if symbol.value == "SBET":
                    self.debug("SBET: Checking bar data and potential breakout.")

                if symbol not in data.bars:
                    self.debug(f"{symbol.value}: No bar data for this second, skipping.")
                    continue

                bar = data.bars[symbol]
                current_open = bar.open
                current_close = bar.close

                # Additional debug for SBET.
                if symbol.value == "SBET":
                    self.debug(f"SBET: Current Open={current_open}, Current Close={current_close}")

                history = self.history(symbol, 2, Resolution.DAILY)
                self.debug(f"{symbol.value}: Open: {current_open}, Close: {current_close}, History:")
                if not history.empty:
                    self.debug(str(history))
                    if symbol.value == "SBET" and current_date == date(2025, 5, 27):
                        self.debug("SBET: History contents specifically for 5/27:")
                        self.debug(f"{history}")
                else:
                    self.debug("History is empty!")

                # Make sure we have data on the prior day to measure gap.
                if history.empty or history.shape[0] < 2:
                    self.debug(f"{symbol.value}: Not enough history for previous close, skipping.")
                    continue

                prev_close = history['close'].iloc[-2]
                self.debug(f"{symbol.value}: Prev Close: {prev_close}")

                if symbol.value == "SBET":
                    self.debug(f"SBET: Gap-up check with open={current_open} vs prev_close={prev_close}")

                # If we don't have symbol_data or the date is different, we want to check the gap-up condition.
                if (symbol not in self._symbol_data or self._symbol_data[symbol].current_date != current_date):
                    gap_up_check_1 = current_open >= 2.50
                    gap_up_check_2 = current_open >= prev_close * 1.10
                    self.debug(
                        f"{symbol.value}: Gap check - open >= 2.5: {gap_up_check_1}, "
                        f"open >= prev_close*1.10: {gap_up_check_2}"
                    )
                    if gap_up_check_1 and gap_up_check_2:
                        self.debug(f"{symbol.value}: PASS gap up conditions; initializing SymbolData.")
                        self._symbol_data[symbol] = SymbolData(current_open, current_date)
                        if symbol.value == "SBET":
                            self.debug("SBET: SymbolData initialized.")
                    else:
                        self.debug(f"{symbol.value}: FAIL gap up conditions; skipping symbol_data init.")
                        if symbol.value == "SBET":
                            self.debug("SBET: Did not meet gap up conditions, ignored.")
                else:
                    sd = self._symbol_data[symbol]
                    if not sd.bought_today and sd.open_price > 0.0:
                        breakout_level = sd.open_price * 1.3
                        breakout_check = current_close > breakout_level
                        self.debug(f"{symbol.value}: Breakout: {current_close} > {breakout_level}? {breakout_check}")
                        if breakout_check:
                            qty = self.calculate_order_quantity(symbol, 0.1)
                            if qty != 0:
                                self.debug(f"{symbol.value}: BUY breakout triggered! Buying {qty} shares at {current_close}")
                                self.buy(symbol, qty)
                                sd.bought_today = True
                                sd.entry_price = current_close
                                if symbol.value == "SBET":
                                    self.debug("SBET: Breakout buy order submitted.")
                            else:
                                self.debug(f"{symbol.value}: Breakout but qty=0, not buying.")
                                if symbol.value == "SBET":
                                    self.debug("SBET: Breakout triggered but zero quantity, ignoring.")
                        else:
                            self.debug(f"{symbol.value}: No breakout, not buying.")
                            if symbol.value == "SBET":
                                self.debug("SBET: Current price has not breached breakout level.")

    def on_end_of_day(self, symbol: Symbol) -> None:
        if symbol in self._symbol_data:
            sd = self._symbol_data[symbol]
            if sd.bought_today:
                self.debug(f"{symbol.value}: End of day, liquidating position.")
                self.liquidate(symbol)
                sd.bought_today = False
                sd.entry_price = 0.0
                if symbol.value == "SBET":
                    self.debug("SBET: End of day liquidation.")