| Overall Statistics |
|
Total Orders 0 Average Win 0% Average Loss 0% Compounding Annual Return 0% Drawdown 0% Expectancy 0 Start Equity 100000 End Equity 100000 Net Profit 0% Sharpe Ratio 0 Sortino 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 -2.303 Tracking Error 0.085 Treynor Ratio 0 Total Fees $0.00 Estimated Strategy Capacity $0 Lowest Capacity Asset Portfolio Turnover 0% |
from AlgorithmImports import *
from datetime import date
from typing import List
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):
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(2020, 12, 1)
self.set_end_date(2020, 12, 15)
self.set_cash(100000)
# Add a coarse universe for stock selection
self.add_universe(self.coarse_selection_function)
self._symbol_data = {}
self._previous_second = -1
def coarse_selection_function(self, coarse: List[CoarseFundamental]) -> List[Symbol]:
# Filter to fundamental data and limit to US exchanges
return [
c.symbol
for c in coarse
if c.has_fundamental_data
and c.symbol.id.market in ("usa", "nys", "ase", "arca", "nasdaq")
]
def on_data(self, data: Slice) -> None:
# 1) Exit logic: take profit or stop loss
for symbol, bar in data.bars.items():
if symbol not in self._symbol_data:
continue
sd = self._symbol_data[symbol]
if not sd.bought_today:
continue
current_price = bar.close
if sd.entry_price > 0:
# Using open price as reference
take_profit_level = sd.open_price * 1.60
stop_loss_level = sd.open_price * 0.90
if current_price >= take_profit_level:
self.liquidate(symbol)
sd.bought_today = False
sd.entry_price = 0.0
elif current_price <= stop_loss_level:
self.liquidate(symbol)
sd.bought_today = False
sd.entry_price = 0.0
# 2) Entry logic once per second
if self.time.second != self._previous_second:
self._previous_second = self.time.second
current_date = self.time.date()
for symbol in self.active_securities.keys:
# Ensure we have daily bars for this symbol
if symbol not in data.bars:
continue
bar = data.bars[symbol]
current_open = bar.open
current_close = bar.close
# Get the previous day's close
history = self.history(symbol, 2, Resolution.DAILY)
if history.empty or history.shape[0] < 2:
continue
prev_close = history["close"].iloc[-2]
# If new day or symbol not known, consider it for tracking
if (
symbol not in self._symbol_data
or self._symbol_data[symbol].current_date != current_date
):
# Check gap requirements
if current_open >= 2.75 and current_open >= prev_close * 1.10:
self._symbol_data[symbol] = SymbolData(current_open, current_date)
else:
sd = self._symbol_data[symbol]
# Look for breakout from today's open
if not sd.bought_today and sd.open_price > 0.0:
breakout_level = sd.open_price * 1.20
if current_close > breakout_level:
qty = self.calculate_order_quantity(symbol, 0.01)
if qty != 0:
self.buy(symbol, qty)
sd.bought_today = True
sd.entry_price = current_close
def on_end_of_day(self, symbol: Symbol) -> None:
# Liquidate positions in the symbol passed at the end of day
if symbol in self._symbol_data:
sd = self._symbol_data[symbol]
if sd.bought_today:
self.liquidate(symbol)
sd.bought_today = False
sd.entry_price = 0.0