| Overall Statistics |
|
Total Trades 2744 Average Win 0.17% Average Loss -0.14% Compounding Annual Return 7.144% Drawdown 12.400% Expectancy 0.100 Net Profit 19.446% Sharpe Ratio 0.724 Probabilistic Sharpe Ratio 27.753% Loss Rate 51% Win Rate 49% Profit-Loss Ratio 1.26 Alpha 0.049 Beta 0.022 Annual Standard Deviation 0.071 Annual Variance 0.005 Information Ratio -0.207 Tracking Error 0.217 Treynor Ratio 2.358 Total Fees $4540.00 Estimated Strategy Capacity $6200000.00 Lowest Capacity Asset NOX R735QTJ8XC9X |
"""
Basic Liquidation System Strategy
@version: 0.5
@creation date: 10/06/2022
- At Open, do 15min VWAP/TWAP entry for a total position of $15,000.
- At 9:45 set stop at HOD
- At 10:05 if P/L > 0, exit 50%. If P/L < 0, exit 100% (Adjust stop size accordingly)
- At 10:30 exit all.
"""
import pandas as pd
from io import StringIO
from AlgorithmImports import *
from ast import literal_eval
TICKERS_CSV = "https://drive.google.com/uc?export=download&id=1did0Sk3F9Sn5Il_nUX252jOB_n0UFqat"
AGG_OPS = {"open": "first", "close": "last",
"high": "max", "low": "min",
"volume": "sum"}
class LiquidationBasic(QCAlgorithm):
def Initialize(self):
self.capital = literal_eval(self.GetParameter("capital"))
self.entry_size = literal_eval(self.GetParameter("entry_size")) # Negative value for shorts
self.wap_type = literal_eval(self.GetParameter("wap_type")) # VWAP or TWAP
self.wap_res = literal_eval(self.GetParameter("wap_resolution")) # Resolution, in seconds, for WAP calculation
self.wap_fract = self.wap_res/(15*60)
self.SetCash(self.capital) # Set Strategy Cash
self.SetStartDate(2019, 5, 1)
self.SetEndDate(2022, 6, 1)
csv = StringIO(self.Download(TICKERS_CSV))
self.overhang = pd.read_csv(csv, parse_dates=["Agreement Start Date"],
dayfirst=True)
self.overhang["Date"] = self.overhang["Agreement Start Date"].dt.date
self.AddUniverse(self.coarse_filter)
self.resolution = Resolution.Second
self.UniverseSettings.Resolution = self.resolution
self.SetSecurityInitializer(lambda x: x.SetMarketPrice(self.GetLastKnownPrice(x)))
every_day = self.DateRules.EveryDay()
every_second = self.TimeRules.Every(TimeSpan.FromSeconds(self.wap_res))
at = self.TimeRules.At
self.Schedule.On(every_day, every_second, self.open_trade)
self.Schedule.On(every_day, at(9, 45), self.set_stop)
self.Schedule.On(every_day, at(10, 5), self.adjust_position)
self.Schedule.On(every_day, at(10, 30), self.close_trade)
def open_trade(self):
if time(9, 30) < self.Time.time() < time(9, 45):
symbols = list(self.ActiveSecurities.Keys)
history = self.History(symbols, self.Time.date(), self.Time,
resolution=self.resolution)
if len(history) > 0:
self.Transactions.CancelOpenOrders()
for symbol in symbols:
order_value = self.entry_size*self.wap_fract
price = self.Securities[symbol].Price
quantity = int(order_value / price)
self.LimitOrder(symbol, quantity, price)
def set_stop(self):
symbols = self.get_owned_stocks()
history = self.History(symbols, self.Time.date(), self.Time,
resolution=self.resolution)
if len(history) > 0:
self.Debug(f"{self.Time} - Set Stop")
self.Transactions.CancelOpenOrders()
today_bar = history.groupby("symbol").agg(AGG_OPS)
limits = today_bar.eval("high + (high - low)*0.5") # Intra range 1.05 as limit price
for s in symbols:
self.StopLimitOrder(s, -self.Portfolio[s].Quantity,
today_bar["high"][s], limits[s])
def adjust_position(self):
symbols = self.get_owned_stocks()
history = self.History(symbols, self.Time.date(), self.Time,
resolution=self.resolution)
if len(history) > 0:
self.Debug(f"{self.Time} - Adjust Position")
self.Transactions.CancelOpenOrders()
today_bar = history.groupby("symbol").agg(AGG_OPS)
limits = today_bar.eval("high + (high - low)*0.5") # Intra range 1.05 as limit price
for s in symbols:
pl = self.Portfolio[s].get_Profit() \
+ self.Portfolio[s].get_UnrealizedProfit()
price = self.Securities[s].Price
qty = self.Portfolio[s].Quantity
if pl > 0:
self.LimitOrder(s, -int(qty/2), price)
self.StopLimitOrder(s, int(qty/2)-qty,
today_bar["high"][s], limits[s])
else:
self.LimitOrder(s, -int(qty), price)
def get_owned_stocks(self):
return [s for s in self.ActiveSecurities.Keys
if self.Portfolio[s].Quantity != 0]
def close_trade(self):
if len(list(self.ActiveSecurities.Keys)) > 0:
self.Debug(f"{self.Time} - Close Trade")
self.Transactions.CancelOpenOrders()
self.Liquidate()
def coarse_filter(self, coarse):
tickers = self.overhang.query("Date == @self.Time.date()")
universe = [] if len(tickers) == 0 else \
[x.Symbol for x in coarse if
(x.Symbol.Value == tickers["ticker"]).any()]
self.Debug(f"{self.Time} - Universe {len(tickers)} tickers")
return universe
"""
Basic Liquidation System Strategy
@version: 0.7
@creation date: 10/06/2022
- At Open, do 15min VWAP/TWAP entry for a total position of $15,000.
- At 9:45 set stop at HOD
- At 10:05 if P/L > 0, exit 50%. If P/L < 0, exit 100% (Adjust stop size accordingly)
- At 10:30 exit all.
"""
import pandas as pd
from io import StringIO
from AlgorithmImports import *
from ast import literal_eval
TICKERS_CSV = "https://drive.google.com/uc?export=download&id=1did0Sk3F9Sn5Il_nUX252jOB_n0UFqat"
AGG_OPS = {"open": "first", "close": "last",
"high": "max", "low": "min",
"volume": "sum"}
class LiquidationBasic(QCAlgorithm):
def Initialize(self):
self.capital = literal_eval(self.GetParameter("capital"))
self.entry_size = literal_eval(self.GetParameter("entry_size")) # Negative value for shorts
self.wap_type = literal_eval(self.GetParameter("wap_type")) # VWAP or TWAP
self.wap_res = literal_eval(self.GetParameter("wap_resolution")) # Resolution, in seconds, for WAP calculation
self.wap_fract = self.wap_res/(15*60)
self.SetCash(self.capital) # Set Strategy Cash
self.SetStartDate(2019, 5, 1)
self.SetEndDate(2022, 6, 1)
csv = StringIO(self.Download(TICKERS_CSV))
self.overhang = pd.read_csv(csv, parse_dates=["Agreement Start Date"],
dayfirst=True)
self.overhang["Date"] = self.overhang["Agreement Start Date"].dt.date
self.AddUniverse(self.coarse_filter)
self.resolution = Resolution.Second
self.UniverseSettings.Resolution = self.resolution
self.SetSecurityInitializer(lambda x: x.SetMarketPrice(self.GetLastKnownPrice(x)))
every_day = self.DateRules.EveryDay()
every_second = self.TimeRules.Every(TimeSpan.FromSeconds(self.wap_res))
at = self.TimeRules.At
self.Schedule.On(every_day, every_second, self.open_trade)
self.Schedule.On(every_day, at(9, 45), self.set_stop)
self.Schedule.On(every_day, at(10, 5), self.adjust_position)
self.Schedule.On(every_day, at(10, 30), self.close_trade)
def open_trade(self):
if time(9, 30) < self.Time.time() < time(9, 45):
symbols = list(self.ActiveSecurities.Keys)
history = self.History(symbols, self.Time.date(), self.Time,
resolution=self.resolution)
if len(history) > 0:
self.Transactions.CancelOpenOrders()
for symbol in symbols:
order_value = self.entry_size*self.wap_fract
price = self.Securities[symbol].Price
quantity = int(order_value / price)
self.LimitOrder(symbol, quantity, price)
def set_stop(self):
symbols = self.get_owned_stocks()
history = self.History(symbols, self.Time.date(), self.Time,
resolution=self.resolution)
if len(history) > 0:
self.Debug(f"{self.Time} - Set Stop")
self.Transactions.CancelOpenOrders()
today_bar = history.groupby("symbol").agg(AGG_OPS)
limits = today_bar.eval("high + (high - low)*0.05") # Intra range 1.05 as limit price
for s in symbols:
self.StopLimitOrder(s, -self.Portfolio[s].Quantity,
today_bar["high"][s], limits[s])
def adjust_position(self):
symbols = self.get_owned_stocks()
history = self.History(symbols, self.Time.date(), self.Time,
resolution=self.resolution)
if len(history) > 0:
self.Debug(f"{self.Time} - Adjust Position")
self.Transactions.CancelOpenOrders()
today_bar = history.groupby("symbol").agg(AGG_OPS)
limits = today_bar.eval("high + (high - low)*0.05") # Intra range 1.05 as limit price
for s in symbols:
pl = self.Portfolio[s].get_Profit() \
+ self.Portfolio[s].get_UnrealizedProfit()
price = self.Securities[s].Price
qty = self.Portfolio[s].Quantity
if pl > 0:
self.LimitOrder(s, -int(qty/2), price)
self.StopLimitOrder(s, int(qty/2)-qty,
today_bar["high"][s], limits[s])
else:
self.LimitOrder(s, -int(qty), price)
def get_owned_stocks(self):
return [s for s in self.ActiveSecurities.Keys
if self.Portfolio[s].Quantity != 0]
def close_trade(self):
if len(list(self.ActiveSecurities.Keys)) > 0:
self.Debug(f"{self.Time} - Close Trade")
self.Transactions.CancelOpenOrders()
self.Liquidate()
def coarse_filter(self, coarse):
tickers = self.overhang.query("Date == @self.Time.date()")
universe = [] if len(tickers) == 0 else \
[x.Symbol for x in coarse if
(x.Symbol.Value == tickers["ticker"]).any()]
self.Debug(f"{self.Time} - Universe {len(tickers)} tickers")
return universe
"""
Full Liquidation System Strategy
@version: 0.1
@creation date: 13/7/2022
"""
import pandas as pd
from io import StringIO
from AlgorithmImports import *
from ast import literal_eval
from datetime import datetime, timedelta
AGG_OPS = {"open": "first", "high": "max", "low": "min",
"close": "last", "volume": "sum"}
TICKERS_CSV = "https://drive.google.com/uc?export=download&id=1bzOypNRbhLMRsQzS5DJUxG0OaIRi7hI8"
#"https://drive.google.com/uc?export=download&id=1cReDW0EPToXFmOfIWtdK9-bR5doecw0-"
class LiquidationFulll(QCAlgorithm):
def Initialize(self):
self.capital = literal_eval(self.GetParameter("capital"))
self.entry_size = literal_eval(self.GetParameter("entry_size")) # Negative value for shorts
self.SetCash(self.capital) # Set Strategy Cash
self.SetStartDate(2021, 10, 1)
self.SetEndDate(2022, 7, 1)
csv = StringIO(self.Download(TICKERS_CSV))
self.overhang = pd.read_csv(csv, parse_dates=["Date"], dayfirst=False)
self.overhang["Date"] = self.overhang["Date"].dt.date
self.AddUniverse(self.coarse_filter)
self.resolution = Resolution.Second
self.UniverseSettings.Resolution = self.resolution
every_day = self.DateRules.EveryDay()
every_second = self.TimeRules.Every(TimeSpan.FromSeconds(30))
every_minute = self.TimeRules.Every(TimeSpan.FromMinutes(15))
at = self.TimeRules.At
self.Schedule.On(every_day, every_second, self.open_trade)
self.Schedule.On(every_day, every_second, self.adding_trade)
self.Schedule.On(every_day, at(7, 5), self.PM_entry)
self.Schedule.On(every_day, at(7, 15), self.PM_stop)
self.Schedule.On(every_day, at(8, 10), self.PM_second_entry)
self.Schedule.On(every_day, at(8, 15), self.PM_second_stop)
self.Schedule.On(every_day, at(9, 45), self.set_stop)
self.Schedule.On(every_day, at(10, 5), self.adjust_position)
self.Schedule.On(every_day, at(10, 30), self.adjust_second_position)
self.Schedule.On(every_day, every_minute, self.set_second_stop)
self.Schedule.On(every_day, at(15, 55), self.close_trade)
self.Schedule.On(every_day, at(15, 58), self.liquidate_trade)
self.SetSecurityInitializer(lambda x: x.SetMarketPrice(self.GetLastKnownPrice(x)))
def PM_entry(self):
symbols = list(self.ActiveSecurities.Keys)
history = self.History(symbols, self.Time.date(), self.Time,
resolution=self.resolution)
if len(history) > 0:
self.Transactions.CancelOpenOrders()
self.Debug(f"{self.Time} - PM entry1")
hod = history["high"].groupby("symbol").max()
lod = history["low"].groupby("symbol").min()
intra_range = hod - lod
intra_range1 = (hod - lod) * .1
vwap_bars = history.eval("volume*(high+low+close)/3")
total_vwap = vwap_bars.groupby("symbol").sum()
vwap = total_vwap/history["volume"].groupby("symbol").sum()
limit_price = vwap - intra_range1
for s in symbols:
lasts = self.Securities[s].Price
order_val = self.entry_size
quantity = int(order_val/vwap[s]/8)
self.LimitOrder(s, quantity, lasts)
def PM_stop(self):
symbols = self.get_owned_stocks()
history = self.History(symbols, self.Time.replace(hour=7,minute=00,second=00), self.Time,
resolution=self.resolution)
if len(history) > 0:
self.Transactions.CancelOpenOrders()
self.Debug(f"{self.Time} - PM stop1")
hod = history["high"].groupby("symbol").max()
lod = history["low"].groupby("symbol").min()
intra_range = hod - lod
intra_range1 = (hod - lod) * .1
vwap_bars = history.eval("volume*(high+low+close)/3")
total_vwap = vwap_bars.groupby("symbol").sum()
vwap = total_vwap/history["volume"].groupby("symbol").sum()
limit_price = hod + intra_range1
for s in symbols:
lasts = self.Securities[s].Price
order_val = self.entry_size
qty = self.Portfolio[s].Quantity
self.StopLimitOrder(s, -qty, hod[s],limit_price[s])
def PM_second_entry(self):
symbols = self.get_owned_stocks()
history = self.History(symbols, self.Time.replace(hour=7,minute=00,second=00), self.Time,
resolution=self.resolution)
if len(history) > 0:
self.Transactions.CancelOpenOrders()
self.Debug(f"{self.Time} - PM entry2")
hod = history["high"].groupby("symbol").max()
lod = history["low"].groupby("symbol").min()
intra_range = hod - lod
intra_range1 = (hod - lod) * .1
vwap_bars = history.eval("volume*(high+low+close)/3")
total_vwap = vwap_bars.groupby("symbol").sum()
vwap = total_vwap/history["volume"].groupby("symbol").sum()
limit_price = hod + intra_range1
for s in symbols:
lasts = self.Securities[s].Price
order_val = self.entry_size
holding = self.Portfolio[s]
pos_avg = holding.Price
stop_limit = pos_avg + intra_range1[s]
#_current = data.Bars[self.symbol.Symbol]
pl = self.Portfolio[s].get_UnrealizedProfit()
if pl >= 0:
qty = self.Portfolio[s].Quantity
#self.MarketOrder(s, -int(qty/2))
#self.LimitOrder(s, -int(qty/2),target_price[s])
self.LimitOrder(s, qty,lasts)
#self.StopLimitOrder(s, int(qty/2)-qty, hod[s],limit_price[s])
self.StopLimitOrder(s, -qty, hod[s],limit_price[s])
else:
qty = self.Portfolio[s].Quantity
self.LimitOrder(s, -int(qty),lasts)
def PM_second_stop(self):
symbols = self.get_owned_stocks()
history = self.History(symbols, self.Time.replace(hour=7,minute=00,second=00), self.Time,
resolution=self.resolution)
if len(history) > 0:
self.Transactions.CancelOpenOrders()
self.Debug(f"{self.Time} - PM stop2")
hod = history["high"].groupby("symbol").max()
lod = history["low"].groupby("symbol").min()
intra_range = hod - lod
intra_range1 = (hod - lod) * .1
vwap_bars = history.eval("volume*(high+low+close)/3")
total_vwap = vwap_bars.groupby("symbol").sum()
vwap = total_vwap/history["volume"].groupby("symbol").sum()
limit_price = hod + intra_range1
for s in symbols:
lasts = self.Securities[s].Price
order_val = self.entry_size
qty = self.Portfolio[s].Quantity
self.StopLimitOrder(s, -self.Portfolio[s].Quantity, hod[s],limit_price[s])
def open_trade(self):
if time(9, 30) < self.Time.time() < time(9, 35):
symbols = self.get_owned_stocks()
history = self.History(symbols, self.Time.replace(hour=9,minute=30,second=00), self.Time,
resolution=self.resolution)
if len(history) > 0:
self.Transactions.CancelOpenOrders()
hod = history["high"].groupby("symbol").max()
lod = history["low"].groupby("symbol").min()
intra_range = hod - lod
intra_range1 = (hod - lod) * .1
vwap_bars = history.eval("volume*(high+low+close)/3")
total_vwap = vwap_bars.groupby("symbol").sum()
vwap = total_vwap/history["volume"].groupby("symbol").sum()
limit_price = vwap - intra_range1
for s in symbols:
lasts = self.Securities[s].Price
holding = self.Portfolio[s]
order_val = self.entry_size
pl = self.Portfolio[s].get_UnrealizedProfit()
if pl >= 0:
quantity = int(order_val/vwap[s]/20)
#limit_price = vwap[symbol] - intra_range1[symbol]
self.LimitOrder(s, quantity, lasts)
#self.LimitOrder(symbol,quantity,self.lasts_close[symbol])
else:
qty = self.Portfolio[s].Quantity
self.LimitOrder(s, -int(qty), lasts)
def adding_trade(self):
if time(9, 35) < self.Time.time() < time(9, 45):
symbols = self.get_owned_stocks()
history = self.History(symbols, self.Time.replace(hour=9,minute=30,second=00), self.Time,
resolution=self.resolution)
if len(history) > 0:
self.Transactions.CancelOpenOrders()
hod = history["high"].groupby("symbol").max()
lod = history["low"].groupby("symbol").min()
intra_range = hod - lod
intra_range1 = (hod - lod) * .1
vwap_bars = history.eval("volume*(high+low+close)/3")
total_vwap = vwap_bars.groupby("symbol").sum()
vwap = total_vwap/history["volume"].groupby("symbol").sum()
limit_price = vwap - intra_range1
for s in symbols:
lasts = self.Securities[s].Price
holding = self.Portfolio[s]
order_val = self.entry_size
pl = self.Portfolio[s].get_UnrealizedProfit()
quantity = int(order_val/vwap[s]/80)
#limit_price = vwap[symbol] - intra_range1[symbol]
self.LimitOrder(s, quantity, lasts)
#self.LimitOrder(symbol,quantity,self.lasts_close[symbol])
def set_stop(self):
symbols = self.get_owned_stocks()
history = self.History(symbols, self.Time.replace(hour=9,minute=30,second=00), self.Time,
resolution=self.resolution)
if len(history) > 0:
self.Debug(f"{self.Time} - Set Stop")
self.Transactions.CancelOpenOrders()
hod = history["high"].groupby("symbol").max()
lod = history["low"].groupby("symbol").min()
intra_range = hod - lod
intra_range1 = (hod - lod) *.05
intra_range2 = (hod - lod) *.15
stop_price = hod + intra_range1
limit_price = hod + intra_range2
target_price = lod + intra_range1
for s in symbols:
holding = self.Portfolio[s]
pos_avg = holding.Price
lasts = self.Securities[s].Price
qty = self.Portfolio[s].Quantity
#self.StopLimitOrder(s, -self.Portfolio[s].Quantity, self.Portfolio[s].Price, hod[s])
self.StopLimitOrder(s, -self.Portfolio[s].Quantity, stop_price[s],limit_price[s])
#self.LimitOrder(s, -int(qty/4),target_price[s])
def adjust_position(self):
symbols = self.get_owned_stocks()
history = self.History(symbols, self.Time.replace(hour=9,minute=30,second=00), self.Time,
resolution=self.resolution)
if len(history) > 0:
self.Debug(f"{self.Time} - Adjust Position")
self.Transactions.CancelOpenOrders()
hod = history["high"].groupby("symbol").max()
lod = history["low"].groupby("symbol").min()
intra_range = hod - lod
intra_range1 = (hod - lod) * .05
limit_price = hod + intra_range1
target_price = lod + intra_range1
for s in symbols:
lasts = self.Securities[s].Price
holding = self.Portfolio[s]
pos_avg = holding.Price
stop_limit = pos_avg + intra_range1[s]
pl = self.Portfolio[s].get_UnrealizedProfit()
# pl = self.Portfolio[s].get_Profit() \
# + self.Portfolio[s].get_UnrealizedProfit()
if pl > 0:
qty = self.Portfolio[s].Quantity
#self.MarketOrder(s, -int(qty/2))
#self.LimitOrder(s, -int(qty/2),target_price[s])
#self.LimitOrder(s, -int(qty/4),lasts)
#self.StopLimitOrder(s, int(qty/2)-qty, hod[s],limit_price[s])
self.StopLimitOrder(s, -int(qty), pos_avg, stop_limit)
else:
qty = self.Portfolio[s].Quantity
self.LimitOrder(s, -int(qty),lasts)
#self.Liquidate(s)
def adjust_second_position(self):
symbols = self.get_owned_stocks()
history = self.History(symbols, self.Time.replace(hour=9,minute=30,second=00), self.Time,
resolution=self.resolution)
if len(history) > 0:
self.Debug(f"{self.Time} - Adjust Position_2")
self.Transactions.CancelOpenOrders()
hod = history["high"].groupby("symbol").max()
lod = history["low"].groupby("symbol").min()
intra_range = hod - lod
intra_range1 = (hod - lod) * .05
intra_range2 = (hod - lod) * .15
limit_price = hod + intra_range1
target_price = lod + intra_range1
for s in symbols:
lasts = self.Securities[s].Price
holding = self.Portfolio[s]
pos_avg = holding.Price
stop_limit = pos_avg + intra_range2[s]
pl = self.Portfolio[s].get_UnrealizedProfit()
if pl > 0:
qty = self.Portfolio[s].Quantity
#self.MarketOrder(s, -int(qty/2))
self.LimitOrder(s, int(qty/2),target_price[s])
#self.StopLimitOrder(s, int(qty/2)-qty, hod[s],limit_price[s])
self.StopLimitOrder(s, -int(qty), pos_avg, stop_limit)
else:
qty = self.Portfolio[s].Quantity
self.LimitOrder(s, -int(qty),lasts)
#self.Liquidate(s)
def set_second_stop(self):
if time(10, 35) < self.Time.time() < time(15, 45):
symbols = self.get_owned_stocks()
history = self.History(symbols, self.Time.replace(hour=9,minute=30,second=00), self.Time,
resolution=self.resolution)
if len(history) > 0:
self.Debug(f"{self.Time} - Set Stop2")
#self.Transactions.CancelOpenOrders()
hod = history["high"].groupby("symbol").max()
lod = history["low"].groupby("symbol").min()
intra_range = hod - lod
intra_range1 = (hod - lod) *.05
intra_range2 = (hod - lod) *.15
stop_price = hod + intra_range1
limit_price = hod + intra_range2
target_price = lod + intra_range1
for s in symbols:
holding = self.Portfolio[s]
pos_avg = holding.Price
lasts = self.Securities[s].Price
qty = self.Portfolio[s].Quantity
r_pl = self.Portfolio[s].get_Profit()
stop_limit = pos_avg + intra_range2[s]
#self.StopLimitOrder(s, -self.Portfolio[s].Quantity, self.Portfolio[s].Price, hod[s])
if r_pl > 0:
self.Transactions.CancelOpenOrders()
self.StopLimitOrder(s, -int(qty), pos_avg, stop_limit[s])
#self.LimitOrder(s, -int(qty/4),target_price[s])
def get_owned_stocks(self):
return [s for s in self.ActiveSecurities.Keys
if self.Portfolio[s].Quantity != 0]
def close_trade(self):
#if len(list(self.ActiveSecurities.Keys)) > 0:
symbols = self.get_owned_stocks()
history = self.History(symbols, self.Time.replace(hour=9,minute=30,second=00), self.Time,
resolution=self.resolution)
if len(history) > 0:
self.Debug(f"{self.Time} - Close Trade")
self.Transactions.CancelOpenOrders()
hod = history["high"].groupby("symbol").max()
lod = history["low"].groupby("symbol").min()
intra_range = hod - lod
intra_range1 = (hod - lod) * .05
for s in symbols:
lasts = self.Securities[s].Price
qty = self.Portfolio[s].Quantity
limit_price = lasts + intra_range1[s]
self.LimitOrder(s, -int(qty), limit_price)
#self.Liquidate()
def liquidate_trade(self):
if len(list(self.ActiveSecurities.Keys)) > 0:
self.Debug(f"{self.Time} - Liquidate Trade")
self.Transactions.CancelOpenOrders()
self.Liquidate()
def coarse_filter(self, coarse):
tickers = self.overhang.query("Date == @self.Time.date()")
universe = [] if len(tickers) == 0 else \
[x.Symbol for x in coarse if
(x.Symbol.Value == tickers["Symbol"]).any()]
self.Debug(f"{self.Time} - Universe {len(tickers)} tickers")
return universe
#def OnData(self, data: Slice):
# if self.aapl_symbol in data.Bars:
# aapl_current_trade = data.Bars[self.aapl_symbol]#region imports
from AlgorithmImports import *
#endregion
"""
Library of indicators
@version: 0.9
"""
import pandas as pd
def filter_bars(bars, start, end):
output = bars.unstack("symbol").between_time(start, end).stack("symbol")
output.index = output.index.reorder_levels(["symbol", "time"])
return output.sort_index()
def rename(bars, name):
return bars.rename(name) if isinstance(bars, pd.Series) \
else bars.add_prefix(f"{name}_")
# Daily indicators
# define daily indicators
def roll_max(bars, window, groupby="symbol"):
groups = bars.groupby(groupby)
output = groups.apply(lambda x: x.rolling(window, min_periods=1).max())
return output
def roll_min(bars, window, groupby="symbol"):
groups = bars.groupby(groupby)
return groups.apply(lambda x: x.rolling(window).min())
def roll_average(bars, window, groupby="symbol", mean_type="arit"):
mean_func = (lambda x: x.ewm(span=window).mean()) if mean_type=="exp" \
else (lambda x: x.rolling(window).mean())
return bars.groupby(groupby).apply(mean_func)
def roll_range(bars, window):
max_high = roll_max(bars["high"], window).squeeze()
min_low = roll_min(bars["low"], window).squeeze()
avg_close = roll_average(bars["close"], window).squeeze()
return (avg_close-min_low)/(max_high-min_low)
def roll_change(bars, window):
return bars.groupby("symbol").pct_change(window)
def position_range(bars, window):
yesterday_bars = bars.groupby("symbol").shift(1) # Not including trading date
max_high = roll_max(yesterday_bars["high"], window).squeeze()
min_low = roll_min(yesterday_bars["low"], window).squeeze()
return (bars["open"]-min_low)/(max_high-min_low)
def gap(bars):
yesterday_bars = bars.groupby("symbol").shift(1) # Not including trading date
return bars["open"]/yesterday_bars["close"]-1
def extension(bars, window):
max_high = roll_max(bars["high"], window).squeeze()
min_low = roll_max(bars["low"], window).squeeze()
return (bars["high"]-max_high)/(max_high-min_low)
def retracement(bars, window):
max_high = roll_max(bars["high"], window).squeeze()
min_low = roll_max(bars["low"], window).squeeze()
return (max_high-bars["low"])/(max_high-min_low)
def gap_extension(bars):
yesterday_bars = bars.groupby("symbol").shift(1) # Not including trading date
return (yesterday_bars["high"]-bars["open"])/(bars["open"]-yesterday_bars["close"])
def day_range(bars):
return bars.eval("(open-low)/(high-low)")
def gap_retracement(bars):
yesterday_bars = bars.groupby("symbol").shift(1) # Not including trading date
return (bars["open"]-yesterday_bars["low"])/(bars["open"]-yesterday_bars["close"])
def roll_vwap(bars, window):
price_volume = bars[["high","low","close"]].mean(axis=1)*bars["volume"]
avg_price_volume = price_volume.groupby("symbol").apply(lambda x: x.rolling(window, min_periods=1).sum())
avg_volume = bars["volume"].groupby("symbol").apply(lambda x: x.rolling(window, min_periods=1).sum())
return avg_price_volume/avg_volume
def shift(bars, shift):
return bars.groupby("symbol").shift(shift)
def divergence(num_bars, den_bars):
return num_bars/den_bars-1
# Intra day indicators
day_grouper = [pd.Grouper(level="symbol"), pd.Grouper(level="time", freq="1D")]
def intra_change(bars):
grouper = bars.groupby(day_grouper)
return grouper.last()/grouper.first()-1
def intra_vwap(bars):
price_volume = bars.eval("(high + low + close)/3 * volume")
price_volume = price_volume.groupby("symbol").cumsum()
volume = bars["volume"].groupby("symbol").cumsum()
return price_volume/volume
def intra_average(bars):
return bars.groupby(day_grouper).average()
def intra_max(bars):
return bars.groupby(day_grouper).max()
def intra_min(bars):
return bars.groupby(day_grouper).min()
def intra_gapext(daily_bars, intra_bars): # Gap Extension
numerator = intra_max(intra_bars["high"])-daily_bars["open"]
denominator = daily_bars["open"] - daily_bars["close"].groupby("symbol").shift(1)
return numerator.divide(denominator, axis="index")
def intra_highext(daily_bars, intra_bars): # Total High Extension
intra_high = intra_max(intra_bars["high"])
intra_low = intra_min(intra_bars["low"])
return (daily_bars["high"]-intra_high).divide(intra_high-intra_low,
axis="index")
def intra_retrace(bars): # Retrace
grouper = bars.groupby(day_grouper)
start_bars = grouper.first()
end_bars = grouper.last()
return (end_bars["high"]-start_bars["high"])/(start_bars["high"]-start_bars["low"])
def intra_divup(bars): # Divergence Up
vwap = intra_vwap(bars)
return (bars["high"] - vwap) / vwap
def intra_divdown(bars): # Divergence Down
vwap = intra_vwap(bars)
return (vwap - bars["low"]) / vwap
def intra_position_range(bars): # Posin Range
#grouper = bars.groupby(day_grouper) TODO : Reuse when new version of Pandas is available in QC
grouper = bars.groupby([pd.Grouper(level="symbol"),
pd.Grouper(level="time", freq="1D")])
return (grouper["close"].last()-grouper["low"].min())/(grouper["high"].max()-grouper["low"].min())
def intra_relvolume(daily_bars, intra_bars, avg_days=10):
grouper = intra_bars.groupby(day_grouper)
intra_volume = grouper["volume"].sum()
avg_volume = shift(roll_average(daily_bars["volume"], avg_days), 1) # Shift 1 day later to match with intra-day data
return intra_volume/avg_volume.squeeze()
def intra_volume_hod(bars):
grouper = bars.groupby(day_grouper)
index = grouper.apply(lambda x: x.idxmax()[1])
return grouper["volume"].cumsum()[index].groupby(day_grouper).last()
"""
Big Bertha Strategy with Machine Learning
Done
- Offline data storage to avoid symbols limitation
- Trade execution on high probability trades
Todo
- Risk management with stop loss
@version: 0.9
@creation date: 05/07/2022
"""
from AlgorithmImports import *
import numpy as np
import pandas as pd
from ast import literal_eval
from sklearn.model_selection import cross_val_score
from sklearn.ensemble import GradientBoostingClassifier
import indicators as idx
pd.set_option('mode.use_inf_as_na', True)
GROUPER = [pd.Grouper(level="symbol"), pd.Grouper(level="time", freq="1D")]
AGG_OPS = {"open": "first", "close": "last",
"high": "max", "low": "min",
"volume": "sum"}
class BigBerthaML(QCAlgorithm):
def Initialize(self):
self.min_usd_volume = literal_eval(self.GetParameter("min_usd_volume"))
self.capital = literal_eval(self.GetParameter("capital"))
self.benchmark = self.GetParameter("benchmark")
self.SetStartDate(2020, 1, 1)
self.SetCash(self.capital)
self.UniverseSettings.Resolution = Resolution.Minute
self.UniverseSettings.ExtendedMarketHours = True
self.AddUniverse(self.coarse_filter)
self.AddEquity(self.benchmark, Resolution.Minute)
self.SetBenchmark(self.benchmark)
self.confidence = 0
self.features, self.targets = None, None
self.train_days = 252 # Training on the last year of data
self.model = GradientBoostingClassifier(warm_start=True,
n_iter_no_change=3)
at = self.TimeRules.At
every_day = self.DateRules.EveryDay(self.benchmark)
self.Train(self.DateRules.MonthStart(), at(0, 0), self.train_model)
self.Schedule.On(every_day, at(9, 35), self.update_data)
self.Schedule.On(every_day, at(9, 35), self.trade)
self.Schedule.On(every_day, at(15, 55), self.stop_trading)
def coarse_filter(self, coarse):
return [x.Symbol for x in coarse if
x.HasFundamentalData and
x.DollarVolume > self.min_usd_volume]
def train_model(self):
if self.features is None: return
self.Debug(f"{self.Time} Training")
x, y = self.get_train_data()
fit_params = dict(sample_weight=abs(y))
cv_scores = cross_val_score(self.model, X=x, y=(y > 0).astype(float),
scoring="accuracy", fit_params=fit_params)
self.confidence = max(np.mean(cv_scores) - 0.5, 0) * 2 # 100% if accuracy 100%, 0% if below 50%
self.model.fit(x, (y > 0).astype(float), **fit_params)
self.Debug(f"{self.Time} Points:{len(x)} Accuracy:{self.confidence:.1%}")
self.Plot("ML", "Test Accuracy", self.confidence)
def trade(self):
if self.confidence <= 0: return
self.Debug(f"{self.Time} Trading")
x_pred = self.get_pred_data()
y_proba = pd.Series(self.model.predict_proba(x_pred)[:, 1],
index=x_pred.index).groupby("symbol").last()
trades = y_proba[(y_proba < 0.25) | (y_proba > 0.75)]
positions = (trades - 0.5) * 2 * self.confidence # TODO: Fix risk management Max portfolio size 100% including shorts
for symbol, position in positions.items():
self.Debug(f"{self.Time} - Trading {symbol} at {position:.1%}")
self.SetHoldings(symbol, position)
def stop_trading(self):
self.Transactions.CancelOpenOrders()
self.Liquidate()
def update_data(self):
trade_days = self.TradingCalendar.GetTradingDays(self.Time - timedelta(7),
self.Time - timedelta(1))
last_day = list(filter(lambda p: p.BusinessDay and not p.PublicHoliday,
trade_days))[-1].Date
start = last_day.replace(hour=9, minute=30, second=0)
end = self.Time.replace(hour=9, minute=35, second=0)
tickers = [ticker for ticker in list(self.ActiveSecurities.Keys)
if str(ticker) not in self.benchmark]
minute_bars = self.History(tickers, start, end, Resolution.Minute)
features = self.calculate_features(minute_bars).dropna()
self.features = pd.concat([self.features, features]).drop_duplicates()
targets = self.calculate_targets(minute_bars).dropna()
self.targets = pd.concat([self.targets, targets]).drop_duplicates()
memory = self.features.memory_usage(deep=True).sum()
memory += self.targets.memory_usage(deep=True)
self.Debug(f"{self.Time} Data updated: {len(tickers)} tickers {memory/10**6:.1f} MB")
def calculate_features(self, minute_bars):
day_bars = idx.filter_bars(minute_bars, "09:31", "16:00")
day_bar = day_bars.groupby(GROUPER).agg(AGG_OPS)
pm_bars = idx.filter_bars(minute_bars, "00:01", "09:30")
pm_bar = pm_bars.groupby(GROUPER).agg(AGG_OPS)
min5_bars = idx.filter_bars(day_bars, "09:31", "09:35")
min5_bar = min5_bars.groupby(GROUPER).agg(AGG_OPS)
features = pd.DataFrame()
features["big_bertha"] = min5_bar.eval("(high-low)/open")
features["close_range"] = min5_bar.eval("(close-low)/(high-low)")
features["open_range"] = min5_bar.eval("(open-low)/(high-low)")
features["pm_volume_usd"] = pm_bar.eval("close*volume")
yesterday_close = day_bar["close"].groupby("symbol").shift(1)
features["gap"] = day_bar["open"] / yesterday_close
return features
def calculate_targets(self, minute_bars):
trade_day_bars = idx.filter_bars(minute_bars, "09:36", "15:55")
trade_day_bar = trade_day_bars.groupby(GROUPER).agg(AGG_OPS)
return trade_day_bar.eval("close/open-1").apply(np.log1p)
def get_train_data(self):
common_index = self.targets.index.intersection(self.features.index)
y = self.targets.loc[common_index].groupby("symbol").tail(self.train_days)
x = self.features.loc[y.index]
return x, y
def get_pred_data(self):
return self.features.query("time == @self.Time.date()")
def get_dataset_days(self):
return len(self.features.index.get_level_values("time").unique()) \
if self.features is not None else 0