| Overall Statistics |
|
Total Trades 255 Average Win 0.08% Average Loss -0.11% Compounding Annual Return 85.691% Drawdown 0.500% Expectancy 0.530 Net Profit 5.397% Sharpe Ratio 12.762 Loss Rate 11% Win Rate 89% Profit-Loss Ratio 0.72 Alpha 0.474 Beta 0.002 Annual Standard Deviation 0.037 Annual Variance 0.001 Information Ratio 2.475 Tracking Error 0.056 Treynor Ratio 255.228 Total Fees $369.79 |
'''
ChangeLog:
05/29/17 - creation of alpha generator #4 WVF with optimize
06/02/17 - Added new Quicksell logic to be more aggressive on non-daytrades
07/07/17 - Changed Exvet cash and weight calculation + formatting
07/15/17 - Removed RSI2, limited WVF to 20% weight
07/27/17 - Added Asset class rotation ACR
07/30/17 - Make Zscore algo look into past data, Normalized leverage to 1.0
07/30/17 - Changed Exvet pipeline to only look at top upward trending stocks
08/01/17 - lowered daytrade to 4.5% gain and lowered price to sell daytrades to -.02 (fixed reserve code)
08/15/17 - changed context variables to reset to prevent errors on restart
08/15/17 - daytrades limit price now max of current price or dayprice *.99
08/30/17 - Begin Import to QC.
09/03/17 - Modify SLSPQ to weekly with 2 month daily lookback
09/29/17 - First Live tests - Exvet 50 stocks, top 1M price change with top dollar volume
'''
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Common")
from System import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Data.UniverseSelection import *
import numpy as np
import itertools
from Manage_Modular import *
from Utility_Functions import *
from AlphaGenerator_SLSQP import *
from AlphaGenerator_ZScore import *
from AlphaGenerator_WVF_opt import *
from AlphaGenerator_ACR import *
from Exvet import *
class Modexvet(QCAlgorithm):
def Initialize(self):
try:
self.initialized
return
except:
self.initialized = True
"""
Algo Settings
"""
self.allow_unlimited_daytrades = False #Enables unlimited day trading
self.allow_unlimited_over25k = True #Enables daytrading once over 26k portfolio, turns daytrading off below 25.5k
self.daytrade_limited = True #Enables lmited day trading (limit of 3, 5 day expiration per trade).
"""
QC Trading Settings
"""
StartDate = [int(x) for x in self.GetParameter("StartDate").split(',')]
EndDate = [int(x) for x in self.GetParameter("EndDate").split(',')]
self.SetCash(int(self.GetParameter("StartingCash")))
self.SetStartDate(StartDate[0],StartDate[1],StartDate[2])
self.SetEndDate(EndDate[0],EndDate[1],EndDate[2])
"""
Initialization of all components of the algorithm
"""
self.Util = Utilities(self) #import utility functions
self.Exvet = Exvet(self) #import Exvet
self.AddEquity("SPY", Resolution.Minute)
# creation of the portfolio manager
self.p_manager = PortfolioManager_EquiWeight(self)
# creation of market order execution handler
self.exec_handler = ExecutionHandler_Market(self)
# creation of alpha generator #1
if self.GetParameter("SLSQP") == "True":
self.alpha_slsqp = AlphaGenerator_SLSQP(self)
self.Log("SLSQP ACTIVE")
self.p_manager.list_alpha.append(self.alpha_slsqp)
# creation of alpha generator #2
if self.GetParameter("ZScore") == "True":
self.alpha_zscore = AlphaGenerator_ZScore(self)
self.Log("ZScore ACTIVE")
self.p_manager.list_alpha.append(self.alpha_zscore)
# creation of alpha generator #3
if self.GetParameter("WVF") == "True":
self.alpha_WVF_opt = AlphaGenerator_WVF_opt(self)
self.Log("WVF_OPT ACTIVE")
self.p_manager.list_alpha.append(self.alpha_WVF_opt)
self.Schedule.On(self.DateRules.Every([DayOfWeek.Friday]),
self.TimeRules.BeforeMarketClose("SPY", 5),
Action(self.alpha_WVF_opt.WVF_opt_reset))
# creation of alpha generator #4
if self.GetParameter("ACR") == "True":
self.alpha_ACR = AlphaGenerator_ACR(self)#args
self.Log("ACR ACTIVE")
self.p_manager.list_alpha.append(self.alpha_ACR)
"""
Define The Fixed Universe for modular
"""
#Create a unique universe out of our combined asset lists
self.securities = ["SPY"]
self.securities += list(itertools.chain.from_iterable([alpha.stocks for alpha in self.p_manager.list_alpha]))
self.securities = self.Util.Unique(self.securities)
#self.Debug(str(self.securities))
self.consolidator = TradeBarConsolidator(1)
self.consolidator.DataConsolidated += self.OnDataConsolidated
self.SubscriptionManager.AddConsolidator("SPY", self.consolidator)
for stock in self.securities:
#self.Debug(str(stock))
if not stock == "SPY":
self.AddEquity(stock, Resolution.Daily)
"""
Rebalance Schedule settings
"""
self.Schedule.On(self.DateRules.EveryDay("SPY"),
self.TimeRules.Every(TimeSpan.FromMinutes(5)),
Action(self.handle_tp))
self.Schedule.On(self.DateRules.EveryDay("SPY"),
self.TimeRules.AfterMarketOpen("SPY", -15),
Action(self.my_before_trading))
self.Schedule.On(self.DateRules.EveryDay("SPY"),
self.TimeRules.BeforeMarketClose("SPY", 1),
Action(self.Util.cancel_open_orders))
'''
self.Schedule.On(self.DateRules.EveryDay("SPY"),
self.TimeRules.Every(TimeSpan.FromMinutes(180)),
Action(self.handle_exvet))
'''
self.Schedule.On(self.DateRules.EveryDay("SPY"),
self.TimeRules.AfterMarketOpen("SPY", 14),
Action(self.handle_exvet))
self.Schedule.On(self.DateRules.EveryDay("SPY"),
self.TimeRules.AfterMarketOpen("SPY", 59),
Action(self.handle_exvet))
self.Schedule.On(self.DateRules.EveryDay("SPY"),
self.TimeRules.AfterMarketOpen("SPY", 179),
Action(self.handle_exvet))
self.Schedule.On(self.DateRules.EveryDay("SPY"),
self.TimeRules.AfterMarketOpen("SPY", 359),
Action(self.handle_exvet))
"""
Define the Exvet universe
"""
self.AddUniverse(self.Exvet.CoarseSelectionFunction,self.Exvet.FineSelectionFunction)
"""
Define risk management parameters
"""
self.max_leverage = 1.0 #highest combined leverage
self.reserved = 0.0 #Tell algo to reserve (won't trade with) this amount of cash
self.normalize = True #noramalize leverage (attempt to stay at max leverage always)
self.max_exvet = 27500000 #$25000 maximum money that will be allocated to Exvet
self.exvet_pct = 1.0 #30% of your portfolio will go to exvet until you reach max_exvet amount
self.trail_stoppct = 0.10 #10% trailing stop
"""
Flags - Do not Change these!
"""
self.modular_rebal_needed = True
self.calculated_alphas = False
self.computed_portfolio = False
self.before_trading_success = False
self.exvet_sell_needed = True
self.exvet_buy_needed = False
self.tp_needed = True
"""
Data - Do not Change these!
"""
self.modular_leverage = 0.0
self.exvet_leverage = 0.0
self.bought = []
self.sold = []
self.reorder = []
self.tp = []
self.daytrades = []
self.age = {}
self.max_price = {}
self.stocks_long = []
self.previous = None
self.buy_chunk = 0
if self.GetParameter("Exvet") == "False":
self.exvet_pct = 0.0
"""
DO NOT ORDER LIST
"""
self.TSPP = ["ACTG", "BBGI", "CGIX", "DTEA", "GEC", "JAKK", "MPVD", "OTEL", "SALM", "TLF",
"ACY", "BBOX", "CHKE", "DVAX", "GFN", "JIVE", "MRIN", "OXBR", "SALT", "TNAV",
"ADMP", "BBRG", "CHKR", "DVD", "GIFI", "JNP", "MRTX", "PCYO", "SANW", "TORM",
"ADRO", "BBW", "CHMA", "DWCH", "GLBL", "JONE", "MRVC", "PDEX", "SBBP", "TRIL",
"ADVM", "BCOV", "CLIR", "DXLG", "GLBR", "JTPY", "MSON", "PEIX", "SCLN", "TRST",
"AETI", "BDE", "CLMT", "DXYN", "GLDD", "KFFB", "MTRX", "PER", "SCX", "TSRI",
"AFMD", "BDSI", "CLRO", "DYNT", "GSOL", "KFS", "MX", "PES", "SGMO", "TTNP",
"AGTC", "BKEP", "CLUB", "EACQ", "GTIM", "KIN", "NAII", "PESI", "SGOC", "TUES",
"AHC", "BKMU", "CLWT", "ECT", "GV", "KMDA", "NATR", "PIH", "SHOR", "TWMC",
"AKER", "BKS", "CNFR", "EDUC", "GVP", "KMPH", "NBY", "PKD", "SHOS", "TZOO",
"ALDX", "BSQR", "COGT", "EGAN", "HBIO", "KND", "NDLS", "PLXP", "SIF", "UBFO",
"AMRC", "BTN", "COVS", "EIGI", "HBM", "KNDI", "NDRO", "PMBC", "SIGM", "ULBI",
"AMS", "BV", "CRBP", "EIGR", "HGT", "KODK", "NEOS", "PMTS", "SKIS", "UNAM",
"APDN", "BVSN", "CRHM", "EMAN", "HIHO", "KONA", "NERV", "PNTR", "SNFCA", "UONE",
"APT", "BYBK", "CRWS", "EMKR", "HIL", "KURA", "NL", "PPIH", "SPAR", "UONEK",
"APWC", "CADC", "CSLT", "ENOC", "HLIT", "KVHI", "NOA", "PPSI", "SRNE", "USAT",
"ARAY", "CALL", "CSTM", "ENZY", "HLTH", "LBIO", "NOG", "PRGX", "SSFN", "UUU",
"ARCO", "CALX", "CTSO", "EPM", "HPJ", "LEE", "NR", "PRQR", "STAA", "VBLT",
"ARCW", "CAW", "CVRR", "ERN", "HWCC", "LFGR", "NSSC", "PRTO", "STDY", "VCEL",
"ARDX", "CBAK", "CVU", "ESXB", "HYGS", "LIFE", "NTEC", "RDNT", "STKL", "VHC",
"ARTW", "CBMG", "CXDC", "ETM", "IIN", "LOAN", "NTIP", "REED", "STML", "VIRC",
"ARTX", "CBMX", "CYAN", "EURN", "IMDZ", "LPSN", "NTP", "RELY", "SVBI", "VISI",
"ASC", "CCO", "CYRN", "EVC", "INFU", "LQDT", "NTWK", "RFIL", "SYNC", "VOXX",
"ASFI", "CDI", "DAC", "EVK", "INOD", "MATR", "NVGS", "RFP", "TA", "VSLR",
"ASRV", "CDTX", "DAVE", "EVOL", "INTT", "MDCA", "NVIV", "RGLS", "TAC", "VTL",
"AST", "CDXS", "DFBG", "EXFO", "IO", "MDLY", "OBAS", "RIC", "TACT", "VUZI",
"AT", "CEMI", "DGII", "EZPW", "IOTS", "MDWD", "OCC", "RLH", "TCCO", "WAIR",
"ATAX", "CEMP", "DGLY", "FORK", "IRMD", "MGIC", "OCIP", "RTIX", "TCON", "WMIH",
"ATTO", "CETV", "DHX", "FRBK", "ISNS", "MITK", "OCX", "RWC", "TESO", "WSCI",
"AWRE", "CFRX", "DHXM", "FSAM", "ITEK", "MLSS", "OFG", "RWLK", "TGH", "XELB",
"AXSM", "CGEN", "DMTX", "FSIC", "ITUS", "MN", "OMN", "RXDX", "TIK", "XENE",
"AXTI", "CGI", "DRAD", "FVE", "IVTY", "MOBL", "ONVI", "RYI", "TIPT", "ZAGG",
"AAME", "AAV", "ABEO", "ABIO", "ABUS", "ACRX",
]
"""
Charting
"""
stockPlot = Chart('Data Graph')
#stockPlot.AddSeries(Series('Leverage', SeriesType.Line, 0))
stockPlot.AddSeries(Series('Long', SeriesType.Line, 0))
stockPlot.AddSeries(Series('Short', SeriesType.Line, 0))
stockPlot.AddSeries(Series('Exvet_orders', SeriesType.Line, 0))
stockPlot.AddSeries(Series('Max ag', SeriesType.Line, 0))
stockPlot2 = Chart('Leverage')
stockPlot2.AddSeries(Series('Leverage', SeriesType.Line, 0))
self.AddChart(stockPlot)
def my_before_trading(self):
self.Util.cancel_open_orders()
#self.Log("before trading")
portfolio = self.Portfolio
portfolio_val_total = float(portfolio.TotalPortfolioValue)
#Reset Daily Flags
self.computed_portfolio = False
self.modular_rebal_needed = True
self.tp_needed = True
self.exvet_sell_needed = True
self.exvet_buy_needed = False
self.exvet_buy_enabled = False
self.calculated_alphas = False
'''
#Dynamic cash reserve
if portfolio_val_total >= 20000:
self.reserved = 11240 #Tell algo to reserve (won't trade with) this amount of cash
else:
self.reserved = 0 #Tell algo to reserve (won't trade with) this amount of cash
'''
#Adjust portions of modular and Exvet
port_value = (portfolio_val_total - self.reserved) * self.max_leverage
exvet_amt = min(self.exvet_pct * port_value,self.max_exvet)
self.exvet_leverage = exvet_amt/port_value
#self.modular_leverage = max(0.0,(self.max_leverage-self.exvet_leverage))
self.modular_leverage = max(0.0,((port_value-exvet_amt)/port_value))
#Enable day-trading if balance is >26000 and disable if less than 25500 (for safety)
if self.allow_unlimited_over25k and portfolio_val_total >25000:
self.allow_unlimited_daytrades = True
elif self.allow_unlimited_over25k and portfolio_val_total <=25000:
self.allow_unlimited_daytrades = False
else:
pass
#Advance the age of all stocks held from yesterday
positions = [i for i in portfolio.Keys if portfolio[i].Invested]
for symbol in positions:
if symbol.Value in self.securities: continue
if symbol in self.age:
self.age[symbol] += 1
elif not self.before_trading_success:
self.age[symbol] = 2
else:
self.age[symbol] = 1
self.age = {key: value for key, value in self.age.items() if key in positions}
#Advance the age of all daytrades and remove daytrades 5 days old
for i, item in enumerate(self.daytrades):
self.daytrades[i] += 1
to_remove = [i for i, val in enumerate(self.daytrades) if val > 4]
for index in reversed(to_remove):
del self.daytrades[index]
#Reset daily storage variables
self.bought = []
self.sold = []
self.tp = []
self.reorder = []
self.buy_chunk = 0
#success
self.before_trading_success = True
"""
Timed functions - These are spaced out for run-speed optimization
"""
#Run every 15 minutes
def handle_tp(self):
self.tp_needed = True
self.exvet_buy_enabled = True
def handle_exvet(self):
#pass
self.Util.my_record_vars()
self.Util.cancel_open_orders()
self.tp = []
self.exvet_sell_needed = True
def OnDataConsolidated(self, sender, bar):
#self.Debug(str(self.Time) + " > New Bar!")
# if we have no changes, do nothing
if not self.changes == SecurityChanges.None:
for security in self.changes.RemovedSecurities:
if security in self.stocks_long:
self.stocks_long.remove(security)
#Add securities to long list that meet our price criteria
for security in self.changes.AddedSecurities:
try:
if not security in self.stocks_long and not str(security.Symbol.Value) in self.securities:
history = self.History(security.Symbol, 1, Resolution.Daily)
if history.empty: continue
if (len(self.stocks_long) < self.Exvet.MaxOrders) and (history['close'][0] <= self.Exvet.MyMostPrice) and (history['close'][0] >= self.Exvet.MyLeastPrice):
self.stocks_long.append(security)
else:
pass
except:
#failed to load security
self.Debug("failed to load security")
pass
self.changes = SecurityChanges.None
self.Debug("stocks Long = " + str(len(self.stocks_long)))
#if before_trading_start has not run due to error:
if not self.before_trading_success == True:
self.my_before_trading()
return
#Reorder failed orders:
if not self.reorder == []:
self.Exvet.process_reorder()
#Profit Taking/Stop Functions
if self.tp_needed:
self.Exvet.process_tp()
#Rebalance when needed:
#if self.exvet_buy_needed and self.exvet_buy_enabled:
if self.exvet_buy_needed:
if not self.exvet_pct == 0.0:
self.Exvet.process_exvet_buy()
if self.exvet_sell_needed:
if not self.exvet_pct == 0.0:
self.Exvet.process_exvet_sell()
if self.modular_rebal_needed:
self.Modular_Rebalance()
#Handles Modular Rebalance, first it calculates alphas once per minute, once all are calculated it assembles the target_portfolio, then it executes orders until all orders are satisfied.
def Modular_Rebalance(self):
if not self.calculated_alphas:
for alpha in np.setdiff1d(self.p_manager.list_alpha,self.p_manager.calculated):
alpha.calculate_alpha()
self.p_manager.calculated.append(alpha)
return
self.calculated_alphas = True
self.p_manager.calculated = []
if not self.computed_portfolio:
# compute new allocation each alpha would like
for alpha in self.p_manager.list_alpha:
alpha.compute_allocation()
# compute new target portfolio
self.p_manager.compute_target()
# compute order strategy
self.computed_portfolio = True
target = self.p_manager.target_portfolio
self.exec_handler.execute_orders(target)
# this event fires whenever we have changes to our universe
def OnSecuritiesChanged(self, changes):
self.changes = changes"""
Misc/Utility Functions
"""
import numpy as np
class Utilities(object):
def __init__(self, data):
self.data = data
def Variance(self,x,*args):
#Variance Function for SLSQP
p = np.squeeze(np.asarray(args))
Acov = np.cov(p.T)
return np.dot(x,np.dot(Acov,x))
def Jac_Variance(self,x,*args):
#jac_variance Function for SLSQP
p = np.squeeze(np.asarray(args))
Acov = np.cov(p.T)
return 2*np.dot(Acov,x)
def Unique(self,seq,idfun=None):
# order preserving
if idfun is None:
def idfun(x): return x
seen = {}
result = []
for item in seq:
marker = idfun(item)
if marker in seen:
continue
seen[marker] = 1
result.append(item)
return result
def my_record_vars(self):
#self.data.Debug("my_record_vars")
account_leverage = float(self.data.Portfolio.TotalHoldingsValue) / float(self.data.Portfolio.TotalPortfolioValue)
self.data.Plot('Leverage', 'Leverage', account_leverage)
portfolio = self.data.Portfolio
positions = portfolio.Keys
pos = 0
short = 0
for symbol in positions:
if not self.data.Securities.ContainsKey(symbol): continue
if portfolio[symbol].IsLong:
pos += 1
if portfolio[symbol].IsShort:
short += 1
self.data.Plot('Data Graph', 'Long', pos)
self.data.Plot('Data Graph', 'Short', short)
max_age = 0
for symbol in self.data.age:
if self.data.age[symbol] > max_age:
max_age = self.data.age[symbol]
self.data.Plot('Data Graph', 'Max age', max_age)
def cancel_open_orders(self):
#self.data.Debug("cancel_open_orders")
oo = [order.Symbol for order in self.data.Transactions.GetOpenOrders()]
if len(oo) == 0: return
oo = self.Unique(oo)
for symbol in oo:
if not self.data.Securities.ContainsKey(symbol): return
self.data.Transactions.CancelOpenOrders(symbol)
def cancel_open_order(self, symbol):
#self.data.Debug("cancel_open_order")
if not self.data.Securities.ContainsKey(symbol): return
oo = self.data.Transactions.GetOpenOrders(symbol)
if len(oo) == 0: return
self.data.Transactions.CancelOpenOrders(symbol)
def chunks(self, listicle, n):
"""Yield successive n-sized chunks from l."""
for i in range(0, len(listicle), n):
yield listicle[i:i + n]
def get_gain(self, symbol):
#self.data.Debug("get_gain")
portfolio = self.data.Portfolio
positions = [i for i in portfolio.Keys if portfolio[i].HoldStock]
if symbol in positions:
if not self.data.Securities.ContainsKey(symbol):
return 0.0
return float(portfolio[symbol].UnrealizedProfitPercent)
else:
return 0.0
def process_trailing_stop(self):
#self.data.Debug("process_trailing_stop")
portfolio = self.data.Portfolio
positions = [i for i in portfolio.Keys if portfolio[i].HoldStock]
self.data.max_price = {symbol: value for symbol, value in self.data.max_price.items() if symbol in positions}
for symbol in positions:
if not self.data.Securities.ContainsKey(symbol):
continue
stock = str(symbol.Value)
amount = int(portfolio[symbol].Quantity)
if symbol in self.data.max_price.keys():
if not amount == 0:
self.data.max_price[symbol] = max(0.0, self.data.max_price[symbol],float(portfolio[symbol].UnrealizedProfitPercent))
else:
self.data.max_price[symbol] = 0.0
else:
self.data.max_price[symbol] = float(portfolio[symbol].UnrealizedProfitPercent)
#Get current gain
gain = float(portfolio[symbol].UnrealizedProfitPercent)
#close positions
if (self.data.max_price[symbol] - gain) > self.data.trail_stoppct:
if self.get_open_orders(symbol):
self.cancel_open_order(symbol)
continue
if not amount == 0:
self.data.sold.append(stock)
self.data.Log("Sell Order " + str(stock) + " @ " + str(float(self.data.Securities[symbol].Price)) + " TRAILING STOP ORDER.")
try:
self.data.MarketOrder(symbol, -amount)
except:
self.data.Debug("trailing stop sell order failed")
def get_open_orders(self, symbol=None):
#self.data.Debug("get_open_orders")
orders = False
if symbol == None:
if len(self.data.Transactions.GetOpenOrders()) > 0:
orders = True
else:
if not self.data.Securities.ContainsKey(symbol):
return orders
if len(self.data.Transactions.GetOpenOrders(symbol)) > 0:
orders = True
return ordersfrom Utility_Functions import *
import numpy as np
"""
Set up Modular Framework Classes
"""
#Alpha generator module is supposed to find edge on the market and ask the portfolio manager for allocation.
class AlphaGenerator(object):
def __init__(self):
self.alloc = dict() # allocation wanted
self.stocks = [] # positions associated to the strategy
def compute_allocation(self):
raise NotImplementedError()
def calculate_alpha(self):
raise NotImplementedError()
#Execution Handler module takes care of the strategy to reach allocation once the portfolio manager gives a target allocation.
class ExecutionHandler(object):
#make orders
def execute_orders(self, target_portfolio):
raise NotImplementedError()
#Portfolio manager module manages the portfolio on the chosen frequency.
class PortfolioManager(object):
def __init__(self):
self.target_portfolio = dict() # target_portfolio
self.list_alpha = [] # list of strategies
self.calculated = [] # Which strategies have been calculated
# computes the target portfolio
def compute_target(self):
raise NotImplementedError()
"""
Manage Modular Execution
"""
# PortfolioManagerEquiWeight gives equal dollar allocation to each alpha and builds a target portfolio
class PortfolioManager_EquiWeight(PortfolioManager):
def __init__(self, data):
PortfolioManager.__init__(self)
self.data = data
def compute_target(self):
# get number of alpha generators
nb_alpha = max(1,len(self.list_alpha))
# clear target allocation
for stock in self.data.securities:
self.target_portfolio[stock] = 0
# for each alpha add allocation to the target
for alpha in self.list_alpha:
for stock in alpha.alloc:
alloc_alpha = alpha.alloc[stock] / nb_alpha
self.target_portfolio[stock] = self.target_portfolio[stock] + alloc_alpha
#update the number of shares the alpha is responsible for
#option to normalize target_portfolio to fill all availible leverage
if self.data.normalize:
original_wt = sum(np.abs(self.target_portfolio.values()))
if original_wt > 0.0:
factor = 1.0/original_wt
else:
factor = 0.0
for stock in self.target_portfolio:
self.target_portfolio[stock] = self.target_portfolio[stock]*factor
# ExecutionHandler_Market makes market orders to reach allocation
class ExecutionHandler_Market(ExecutionHandler):
def __init__(self, data):
self.data = data
'''
def execute_orders(self, target_portfolio):
portfolio = self.data.Portfolio
portfolio_val_total = float(portfolio.TotalPortfolioValue)
port_val = (portfolio_val_total - self.data.reserved) * self.data.modular_leverage
pos_val = float(portfolio.TotalHoldingsValue)
cash = max(0.0,(port_val - pos_val))
for stock in target_portfolio:
if self.data.Util.get_open_orders(stock):
self.data.Debug("Open Orders, waiting")
return
for stock in self.data.securities:
price = float(self.data.Securities[stock].Price)
goal_val = port_val * min(1.0,target_portfolio[stock])
goal_shares = 0
if goal_val >= 0 and price > 0:
goal_shares = np.floor(goal_val / price)
if goal_val < 0 and price > 0:
goal_shares = np.ceil(goal_val / price)
current_shares = float(self.data.Portfolio[stock].Quantity)
current_val = current_shares * price
shares_to_buy = (goal_shares - current_shares)
cost = np.abs(shares_to_buy) * price
diff = 1.0
if not goal_val == 0:
diff = np.abs((current_val-goal_val)/goal_val)
if shares_to_buy > 1 and diff > .05:
if (np.abs(goal_shares) < np.abs(current_shares)) or (cost < cash):
self.data.MarketOrder(stock, shares_to_buy)
msg = ''.join(["Ordering ",str(stock)," shares: ",str(shares_to_buy)])
#self.data.Log(msg)
return
else:
self.data.modular_rebal_needed = True
continue
else:
continue
self.data.modular_rebal_needed = False
for stock in self.data.securities:
self.data.Log(str(stock) + ". Allocation = " + str(round(target_portfolio[stock]*100,1)) + "%")
self.data.Log("Modular Execution Finished")
'''
def execute_orders(self, target_portfolio):
portfolio = self.data.Portfolio
portfolio_val_total = float(portfolio.TotalPortfolioValue)
port_value_adjusted = (portfolio_val_total - self.data.reserved) * self.data.max_leverage
modular_leverage = (port_value_adjusted * self.data.modular_leverage)/portfolio_val_total
sold = False
for stock in self.data.securities:
if self.data.Util.get_open_orders(stock):
self.data.Debug("Open Orders on: " + str(stock) + ", waiting")
self.data.modular_rebal_needed = True
return
current = float(self.data.Portfolio[stock].AbsoluteHoldingsValue)
goal = (port_value_adjusted*target_portfolio[stock])
goal_pct = (modular_leverage*target_portfolio[stock])
amount = (goal-current)
if amount < 0.0 and (abs(amount) > 1000 or goal_pct == 0.0):
self.data.SetHoldings(stock, goal_pct)
sold = True
else:
continue
if sold:
self.data.modular_rebal_needed = True
return
for stock in self.data.securities:
current = float(self.data.Portfolio[stock].AbsoluteHoldingsValue)
goal = (port_value_adjusted*target_portfolio[stock])
goal_pct = (modular_leverage*target_portfolio[stock])
amount = (goal-current)
if amount > 0.0 and abs(amount) > 1000:
self.data.SetHoldings(stock, goal_pct)
else:
continue
for stock in self.data.securities:
self.data.Log(str(stock) + ". Allocation = " + str(round(target_portfolio[stock]*100,1)) + "%")
self.data.modular_rebal_needed = False
self.data.Log("Modular Execution Finished")from Manage_Modular import *
from Utility_Functions import *
import numpy as np
import pandas as pd
from scipy import optimize
class AlphaGenerator_SLSQP(AlphaGenerator):
def __init__(self, data):
AlphaGenerator.__init__(self)
self.data = data
self.Util = Utilities(self.data)
#SLSQP assets
self.stocks = [
"SPY",
"QQQ",
"TLT",
"TIP",
"AGG",
]
self.allocation = {}
"""
SLSQP parameters
"""
self.sqslp_days = 42
self.x1 = np.asarray([1.0/len(self.stocks) for i in self.stocks])
self.eps = 0.01
self.tol = float(1.0e-6) #assume convergence is 10 time SLSQP ftol of 1e-6
def compute_allocation(self):
#self.data.Log("compute_allocation")
for sid in self.allocation:
self.alloc[sid] = self.allocation[sid]
def calculate_alpha(self):
prices = self.data.History(self.stocks, self.sqslp_days, Resolution.Daily)['close'].unstack(level=0)
ret = prices.pct_change()[1:].values
ret_mean = prices.pct_change().mean()
ret_std = prices.pct_change().std()
ret_norm = ret_mean/ret_std
ret_norm = ret_norm.values
ret_norm_max = np.max(ret_norm)
eps_factor = 0.9 if ret_norm_max >0 else 1.0
self.eps = eps_factor*ret_norm_max
bnds = []
limits = [0,1] #[0,1] for long only [-1,1] for long/short
for stock in self.stocks:
bnds.append(limits)
bnds = tuple(tuple(x) for x in bnds)
cons = ({'type': 'eq', 'fun': lambda x: np.sum(x)-1.0},
{'type': 'ineq', 'fun': lambda x: np.dot(x,ret_norm)-self.eps})
res= optimize.minimize(self.Util.Variance, self.x1, args=ret,jac=self.Util.Jac_Variance, method='SLSQP',constraints=cons,bounds=bnds)
if res.success: # if SLSQP declares success
weighted_ret_norm = np.dot(res.x,ret_norm)
w_ret_constraint = weighted_ret_norm - self.eps + self.tol
if(w_ret_constraint > 0): # and constraint is actually met
allocation = res.x
allocation[allocation<0.0] = 0.0 #Remove to allow short
factor=sum(np.abs(allocation))
if factor > 0:
allocation = allocation/factor
for i,stock in enumerate(self.stocks):
self.allocation[stock] = allocation[i]
self.data.WVF_opt_calculated = True
else:
self.data.Log("SLSQP: constraint fail, SLSQP status = {0}".format(res.status))
for i,stock in enumerate(self.stocks):
self.allocation[stock] = 0.0
else:
self.data.Log("SLSQP: SLSQP fail, SLSQP status = {0}".format(res.status))
for i,stock in enumerate(self.stocks):
self.allocation[stock] = 0.0from Manage_Modular import *
from Utility_Functions import *
import numpy as np
import pandas as pd
import math
class AlphaGenerator_ZScore(AlphaGenerator):
def __init__(self, data):
AlphaGenerator.__init__(self)
self.data = data
#zscore assets
self.stocks = [
"SPY",
"TLT",
"XLP",
"XIV",
]
"""
zscore parameters
"""
self.fixed_wt_pct = 0.50
self.fixed_wt = {
"XLP": 0.50,
"TLT": 0.40,
"XIV": 0.10,
}
self.vol_factor = 0.5 #TLT = .5, SPY = 1
self.ext_factor = 4.0 #move too extreme, leave asset
self.lookback = 150
self.allocation = {}
def compute_allocation(self):
for sid in self.allocation:
self.alloc[sid] = self.allocation[sid]
def calculate_alpha(self):
'''
for sid in self.stocks:
if not self.data.data.ContainsKey(sid):
self.data.Log("ZScore No data")
return
'''
history = self.data.History(['TLT'], self.lookback, Resolution.Daily)
mean = history['close'].mean()
sigma = history['close'].std()
price = float(self.data.Securities['TLT'].Price)
if sigma != 0.0:
z = (price - mean) / sigma**self.vol_factor
else:
z = 0.0
if -self.ext_factor <= z <= self.ext_factor:
tlt_target = 1.0/(1+math.exp(-1.2*z)) # Pure momentum adding 1 to the sin wave to prevent shorting
else:
tlt_target = 0.0
spy_target = (1.0-tlt_target)
for sid in self.stocks:
self.allocation[sid] = 0.0
if sid in self.fixed_wt:
self.allocation[sid] += self.fixed_wt[sid] * self.fixed_wt_pct
self.allocation["TLT"] += tlt_target * (1.0 - self.fixed_wt_pct)
self.allocation["SPY"] += spy_target * (1.0 - self.fixed_wt_pct)from Manage_Modular import *
from Utility_Functions import *
import numpy as np
import pandas as pd
from scipy import optimize
class AlphaGenerator_WVF_opt(AlphaGenerator):
def __init__(self, data):
AlphaGenerator.__init__(self)
self.data = data
self.Util = Utilities(self.data)
"""
WVF parameters
"""
self.WFV_limit = 14 # (Kory used 14 but it becomes a bit too agressive)
self.n = 28
self.WVF_opt_calculated = False
self.allocation = {}
#WVF_opt assets
self.bull = "TQQQ"
self.bear = "TMF"
self.stocks = [
self.bull,
self.bear,
"XIV",
]
self.vxx = self.data.AddEquity("VXX")
"""
WVF_SLSQP parameters
"""
self.sqslp_days = 17
self.x1 = np.asarray([1.0/len(self.stocks) for i in self.stocks])
self.eps = 0.01
self.tol = float(1.0e-6) #assume convergence is 10 time SLSQP ftol of 1e-6
"""
SPY_FIX parameters
"""
self.period = 28 #"LookBack Period Standard Deviation High")
self.bbl = 22 # "Bolinger Band Length")
self.mult = 1.05 # "Bollinger Band Standard Devaition Up")
self.lb = 22 # "Look Back Period Percentile High")
self.ph = .90 # "Highest Percentile - 0.90=90%, 0.95=95%, 0.99=99%")
# Criteria for Down Trend Definition for Filtered Pivots and Aggressive Filtered Pivots
self.ltLB = 40 # Long-Term Look Back Current Bar Has To Close Below This Value OR Medium Term--Default=40")
self.mtLB = 14 # Medium-Term Look Back Current Bar Has To Close Below This Value OR Long Term--Default=14")
self.Str = 3 # Entry Price Action Strength--Close > X Bars Back---Default=3")
def compute_allocation(self):
#self.data.Log("compute_allocation")
for sid in self.allocation:
self.alloc[sid] = self.allocation[sid]
def calculate_alpha(self):
#self.data.Log("calculate_alpha")
"""
VOL_calculate
"""
history = self.data.History(["VXX"], int(self.n + 2), Resolution.Daily)
vxx_prices = history.loc["VXX"]["close"][:-1]
vxx_lows = history.loc["VXX"]["low"][:-1]
vxx_highest = vxx_prices.rolling(window = self.n, center=False).max()
# William's VIX Fix indicator a.k.a. the Synthetic VIX
WVF = ((vxx_highest - vxx_lows)/(vxx_highest)) * 100
# Sell position when WVF crosses under 14
if(WVF[-2] > self.WFV_limit and WVF[-1] <= self.WFV_limit):
self.allocation["XIV"] = 0.0
#self.data.Log("sell XIV")
"""
TMF_calculate
"""
history = self.data.History([self.bear], 200, Resolution.Daily)
ma_tmf = history.loc[self.bear]["close"].mean()
cp_tmf = float(self.data.Securities[self.bear].Price)
if cp_tmf < ma_tmf:
self.allocation[self.bear] = 0.0
"""
Allocate stocks
"""
if not self.WVF_opt_calculated:
self.WVF_opt_allocate()
"""
SPY_FIX calculate
"""
bullish_size = self.allocation[self.bull]
bearish_size = self.allocation[self.bear]
history = self.data.History([self.bull], int((2*self.period) + 2), Resolution.Daily)
spy_close = history.loc[self.bull]["close"]
spy_lows = history.loc[self.bull]["close"]
spy_highest = spy_close.rolling(window = self.period).max()
# Williams Vix Fix Formula
WVF_s = ((spy_highest - spy_lows)/(spy_highest)) * 100
sDev = self.mult * np.std(WVF_s[-int(self.bbl):])
midLine = np.mean(WVF_s[-int(self.bbl):])
upperBand = midLine + sDev
rangeHigh = (max(WVF_s[-int(self.lb):])) * self.ph
spy_higher_then_Xdays_back = spy_close[-1] > spy_close[-int(self.Str)]
spy_lower_then_longterm = spy_close[-1] < spy_close[-int(self.ltLB)]
spy_lower_then_midterm = spy_close[-1] < spy_close[-int(self.mtLB)]
# Alerts Criteria
alert2 = not (WVF_s[-1] >= upperBand and WVF_s[-1] >= rangeHigh) and (WVF_s[-2] >= upperBand and WVF_s[-2] >= rangeHigh)
spy_change = (alert2 or spy_higher_then_Xdays_back) and (spy_lower_then_longterm or spy_lower_then_midterm)
if (spy_change and bearish_size > bullish_size) or (not spy_change and bullish_size > bearish_size):
self.allocation[self.bear] = bullish_size
self.allocation[self.bear] = bearish_size
def WVF_opt_reset(self):
#self.data.Log("WVF_opt_reset")
self.WVF_opt_calculated = False
def WVF_opt_allocate(self):
#self.data.Log("WVF_opt_allocate")
prices = self.data.History(self.stocks, self.sqslp_days, Resolution.Daily)['close'].unstack(level=0)
ret = prices.pct_change()[1:].values
ret_mean = prices.pct_change().mean()
ret_std = prices.pct_change().std()
ret_norm = ret_mean/ret_std
ret_norm = ret_norm.values
ret_norm_max = np.max(ret_norm)
eps_factor = 0.9 if ret_norm_max >0 else 1.0
self.eps = eps_factor*ret_norm_max
bnds = []
limits = [0,1] #[0,1] for long only [-1,1] for long/short
for stock in self.stocks:
bnds.append(limits)
bnds = tuple(tuple(x) for x in bnds)
cons = ({'type': 'eq', 'fun': lambda x: np.sum(x)-1.0},
{'type': 'ineq', 'fun': lambda x: np.dot(x,ret_norm)-self.eps})
res= optimize.minimize(self.Util.Variance, self.x1, args=ret,jac=self.Util.Jac_Variance, method='SLSQP',constraints=cons,bounds=bnds)
if res.success: # if SLSQP declares success
weighted_ret_norm = np.dot(res.x,ret_norm)
w_ret_constraint = weighted_ret_norm - self.eps + self.tol
if(w_ret_constraint > 0): # and constraint is actually met
allocation = res.x
allocation[allocation<0] = 0 #Remove to allow short
factor=sum(np.abs(allocation))
if factor > 0:
allocation = allocation/factor
for i,stock in enumerate(self.stocks):
self.allocation[stock] = allocation[i]
self.data.WVF_opt_calculated = True
else:
self.data.Log("WVF: constraint fail, SLSQP status = {0}".format(res.status))
for i,stock in enumerate(self.stocks):
self.allocation[stock] = 0.0
else:
self.data.Log("WVF: SLSQP fail, SLSQP status = {0}".format(res.status))
for i,stock in enumerate(self.stocks):
self.allocation[stock] = 0.0from Manage_Modular import *
from Utility_Functions import *
import numpy as np
import pandas as pd
import itertools
class AlphaGenerator_ACR(AlphaGenerator):
def __init__(self, data):
AlphaGenerator.__init__(self)
self.data = data
self.Util = Utilities(self.data)
self.ACR_assets = [
"VOE",
"VDC",
"XLP",
"IJR",
]
self.ACR_bonds = [
"TLT",
"TIP",
"DBC",
"SHY",
]
self.ACR_sectors = [
"XLB", #Materials
"XLY", #Consumer Cyclical
"XLF", #Financials
"IYR", #ISHARES Real Estate
"XLP", #Consumer Defensive
"XLV", #Healthcare
"XLU", #Utilities
"XLE", #Energy
"XLI", #Industrials
"XLK", #Tech
]
self.ACR_fixed = [
"SPY",
]
for stock in self.ACR_sectors:
self.data.AddEquity(stock)
self.stocks = self.ACR_assets+self.ACR_bonds+self.ACR_fixed
"""
ACR (Asset Class Rotation) parameters
"""
self.ACR_sector_step = 13 #12% step change = all bonds if 9 of 11 sectors down
self.ACR_asset_step = 20 #20% step change
self.allocation = {}
self.ACR_fixed_weight = [
0.0, #SPY
]
def compute_allocation(self):
#self.data.Log("compute_allocation")
for sid in self.allocation:
self.alloc[sid] = self.allocation[sid]
def calculate_alpha(self):
for sid in self.stocks:
if not sid in self.allocation:
self.allocation[sid] = 0.0
ACR_assets_weight = np.zeros(len(self.ACR_assets))
ACR_bonds_data = pd.DataFrame(0, columns=['Weight','Ratio','20Day','60Day'],index=self.ACR_bonds)
ACR_sectors_data = pd.DataFrame(0, columns=['Ratio','20Day','200Day'],index=self.ACR_sectors)
"""
Determine sector trends and calculate weight to assets/bonds
"""
ACR_sectors_data.loc[:,'20Day'] = self.data.History(self.ACR_sectors, 20, Resolution.Daily)["close"].unstack(level=0).mean()
ACR_sectors_data.loc[:, '200Day'] = self.data.History(self.ACR_sectors, 200, Resolution.Daily)["close"].unstack(level=0).mean()
ACR_sectors_data['Ratio'] = ACR_sectors_data['20Day']/ACR_sectors_data['200Day'] - 1
ACR_bonds_weight = len(ACR_sectors_data[ACR_sectors_data['Ratio'] < 0]) * self.ACR_sector_step/100.0
if ACR_bonds_weight > 1.0:
ACR_bonds_weight = 1.0
ACR_bonds_weight = ACR_bonds_weight * (1-sum(self.ACR_fixed_weight))
"""
Determine bond trends and which duration to be in
"""
if ACR_bonds_weight > 0.0:
ACR_bonds_data.loc[:,'20Day'] = self.data.History(self.ACR_bonds, 20, Resolution.Daily)["close"].unstack(level=0).mean()
ACR_bonds_data.loc[:, '60Day'] = self.data.History(self.ACR_bonds, 60, Resolution.Daily)["close"].unstack(level=0).mean()
ACR_bonds_data['Ratio'] = ACR_bonds_data['20Day']/ACR_bonds_data['60Day'] - 1
ACR_bonds_data['Weight'] = 0
ACR_bonds_data.loc[ACR_bonds_data['Ratio'].idxmax(), 'Weight'] = ACR_bonds_weight
#log.info(self.ACR_bonds_data)
returns = self.data.History(self.ACR_assets, 126, Resolution.Daily)["close"].unstack(level=0).dropna().pct_change().dropna() + 1.0
"""
Create portfolio combinations
"""
n = len(self.ACR_assets)
steps = [x/100.0 for x in range(0,101,int(self.ACR_asset_step))]
a = [steps for x in xrange(n)]
b = list(itertools.product(*a))
x = [sum(i) for i in b]
port = pd.DataFrame(b)
port['Sum'] = x
port = port[port.Sum == 1]
del port['Sum']
"""
Score and Weight portoflio
"""
port_returns = pd.DataFrame(np.dot(returns, port.T), index=returns.index)
port_metrics = self.ACR_get_specs(port_returns)
port_metrics = self.ACR_score(port_metrics)
port_metrics['Z3'] = port_metrics.ZMean\
-port_metrics.ZSTD\
-port_metrics.ZDownSide\
+port_metrics.ZAnnualized\
-port_metrics.ZMax_Draw\
-port_metrics.ZSTD10\
+port_metrics.ZMean10\
+port_metrics.ZMinimum15
port_metrics = port_metrics.sort_values(by='Z3', ascending=False)
portfolios = port
portfolios.columns = list(returns.columns.values)
best = pd.concat([pd.DataFrame(portfolios.iloc[port_metrics['Z3'].idxmax()]).T])
#log.info(best.loc[:, (best != 0).any(axis=0)].T)
best = pd.DataFrame(portfolios.iloc[port_metrics['Z3'].idxmax()])
#log.info(best)
ACR_assets_weight = [i[0]*(1-ACR_bonds_weight-sum(self.ACR_fixed_weight)) for i in best.values]
for x in range(n):
self.allocation[self.ACR_assets[x]] = ACR_assets_weight[x]
for stock in self.ACR_bonds:
self.allocation[stock] = ACR_bonds_data.loc[stock, 'Weight']
for x in range(len(self.ACR_fixed)):
self.allocation[self.ACR_fixed[x]] = self.ACR_fixed_weight[x]
def ACR_drawdown(self, returns):
mat = returns.cumprod().values
[n, m] = np.shape(mat)
maxes = np.maximum.accumulate(np.array(mat))
for i in range(0,n):
for j in range(m):
mat[i,j] = mat[i,j] / maxes[i,j]
df = pd.DataFrame(mat)
df[df > 1] = 1
return df
def ACR_moving_returns(self, returns, w):
mat = returns.values
[n, m] = np.shape(mat)
ret = np.zeros(shape = (n-w+1,m))
for i in range(w-1,n):
for j in range(m):
ret[i-w+1,j] = np.power(np.prod(mat[(i-w+1):i+1,j]),(1.0/w))- 1.0
return pd.DataFrame(ret)
def ACR_get_specs(self, returns):
metrics = pd.DataFrame((returns.mean()),columns=['Mean']) - 1.0
metrics['STD'] = pd.DataFrame((returns.std()))
metrics['Annualized'] = np.power(returns.cumprod().values.tolist()[-1],1.0/len(returns))- 1.0
downside = returns.copy(deep=True) - 1
downside[downside > 0] = 0
downside = downside ** 2
metrics['DownSide'] = pd.DataFrame(downside.mean() ** 0.5)
draw = self.ACR_drawdown(returns)
metrics['Max_Draw'] = 1.0 - draw.min().values
ret15 = self.ACR_moving_returns(returns,21)
metrics['Minimum15'] = ret15.min().values
ret10 = self.ACR_moving_returns(returns,21)
metrics['Mean10'] = ret10.mean().values
metrics['STD10'] = ret10.std().values
return metrics
def ACR_zscore(self, stocks, var, var_save):
stocks[var_save] = (stocks[var] - stocks[var].mean())/stocks[var].std(ddof=0)
return stocks
def ACR_score(self, metrics):
metrics = self.ACR_zscore(metrics, 'Mean', 'ZMean')
metrics = self.ACR_zscore(metrics, 'STD', 'ZSTD')
metrics = self.ACR_zscore(metrics, 'Annualized', 'ZAnnualized')
metrics = self.ACR_zscore(metrics, 'DownSide', 'ZDownSide')
metrics = self.ACR_zscore(metrics, 'Max_Draw', 'ZMax_Draw')
metrics = self.ACR_zscore(metrics, 'Minimum15', 'ZMinimum15')
metrics = self.ACR_zscore(metrics, 'STD10', 'ZSTD10')
metrics = self.ACR_zscore(metrics, 'Mean10', 'ZMean10')
return metricsfrom Utility_Functions import *
from QuantConnect.Data.UniverseSelection import *
import numpy as np
import pandas as pd
"""
Manage Exvet Execution
"""
class Exvet(object):
def __init__(self, data):
self.data = data
"""
Exvet parameters
"""
self.MyLeastPrice = 1.10 #1.10 #.7 Minimum stock price criteria
self.MyMostPrice = 2.50 #2.49 #1.49Maximum stock price criteria
self.LowVar = 0.06 #6 Bottom range percentile of DollarVolume universe
self.HighVar = 0.40 #40 Top range percentile of DollarVolume universe
self.rounding = True
self.min_order = 200
self.MyFireSaleAge = 3 #3 Age at which stocks will be sold, regardless of profit/loss
self.BuyFactor = .95 #.97 #.96Attempt to Buy stocks with this discount
self.SellFactor = 1.03 #1.03 #1.02 Attempt to sell stocks with this profit
self.DayFactor = 1.2 #1.05 Profit Threshold to attempt a daytrade sell, if daytrading is disabled it will still function as a "quick sell" threshold.
self.Top_Stocks = 300 #150
self.MaxOrders = 50 #100
self.chunks = 10 #10
"""
Temp variables - Do not modify
"""
self.exvet_buy_cash = 0.0
self.cash_used = 0.0
def process_exvet_sell(self):
###
#Sell Logic
###
if self.data.exvet_pct == 0.0:
return
#Order sell at profit target in hope that somebody actually buys it
portfolio = self.data.Portfolio
positions = [i for i in portfolio.Keys if portfolio[i].HoldStock]
#self.data.Debug("positions(sell) = " + str(positions))
i = 0
#self.data.Debug("exvet sell ")
for symbol in positions:
if not self.data.Securities.ContainsKey(symbol): continue
if i > self.chunks: return
stock = str(symbol.Value)
StockShares = int(portfolio[symbol].Quantity)
CostBasis = float(portfolio[symbol].HoldingsCost)/StockShares
price = float(self.data.Securities[symbol].Price)
#Only buy Valid stocks that don't have open orders and respect daytrades.
if not self.data.Util.get_open_orders(symbol) and not stock in self.data.bought and not stock in self.data.securities:
if stock in self.data.TSPP or StockShares < 0:
i +=1
try:
self.data.MarketOrder(symbol, -StockShares)
except:
self.data.Debug("TSPP/short liquidation failed")
continue
SellPrice = CostBasis*self.SellFactor
Quick_sell = (price*1.0)-.01
#if len(self.data.age.keys())>0:
#self.data.Debug(str(self.data.age.keys()[0]))
#if symbol in self.data.age.keys():
#age = self.data.age[symbol]
#else:
#age = 0
#self.data.Debug("age of " + str(symbol) + " = " + str(age))
too_old = (symbol in self.data.age and self.data.age[symbol]>=self.MyFireSaleAge and CostBasis>price)
#high_profit = (price > (CostBasis*(self.SellFactor+.01)))
high_profit = False
try:
if too_old or high_profit:
#Profit Target has been greatly exceeded or stock held too long. Sell quickly.
msg = ""
if high_profit: #Log info why we sold it.
self.data.LimitOrder(symbol, -StockShares, round((CostBasis*(self.SellFactor-.01)),2))
msg = ''.join(["Sell Limit Order ",str(stock)," @ ",str((CostBasis*(self.SellFactor-.01)))," Profit target exceeded."])
else:
self.data.LimitOrder(symbol, -StockShares, round(Quick_sell,2))
msg = ''.join(["Sell Limit Order ",str(stock)," @ ",str(Quick_sell)," Held too long: ",str(self.data.age[symbol])," days."])
#self.data.Debug(msg)
i +=1
self.data.Log(msg)
if not self.data.allow_unlimited_daytrades: #mark as sold to prevent daytrades.
self.data.sold.append(stock)
else:
#Attempt to sell at Profit target
i +=1
self.data.LimitOrder(symbol, -StockShares, round(SellPrice,2))
msg = ''.join(["Sell Limit Order ",str(stock)," @ ",str(SellPrice)])
self.data.Log(msg)
if not self.data.allow_unlimited_daytrades: #mark this as sold to prevent daytrades.
self.data.sold.append(stock)
except:
self.data.Debug("exvet sell order failed")
else:
pass
#flag for successful sell, move to buy logic
self.data.exvet_sell_needed = False
self.data.exvet_buy_needed = True
def process_exvet_buy(self):
###
#Buy Logic
###
if self.data.exvet_pct == 0.0:
return
portfolio = self.data.Portfolio
positions = [i for i in portfolio.Keys if portfolio[i].HoldStock]
#self.data.Debug("positions = " + str(positions))
if self.data.buy_chunk == 0:
port_val_total = float(portfolio.TotalPortfolioValue)
port_val_adjusted = (port_val_total - self.data.reserved) * self.data.max_leverage
num = 0
for sid in self.data.stocks_long:
stock = str(sid.Symbol.Value)
symbol = str(sid.Symbol)
if not stock in self.data.sold and not sid.Invested and not stock in self.data.TSPP:
num += 1
self.data.Plot('Data Graph', 'Exvet_orders', num)
if num == 0:
self.WeightThisBuyOrder = 0.0
else:
self.WeightThisBuyOrder = min(.05,(1.0/num))
modular_used = 0.0
exvet_used = 0.0
for symbol in positions:
stock = str(symbol.Value)
if stock in self.data.securities:
modular_used += float(portfolio[symbol].HoldingsValue)
else:
exvet_used += float(portfolio[symbol].HoldingsValue)
exvet_val_max = port_val_adjusted * self.data.exvet_leverage
#self.exvet_buy_cash = max(0.0,min(self.data.max_exvet,(exvet_val_max - exvet_used)*4.0))
self.exvet_buy_cash = max(0.0,min(self.data.max_exvet,(exvet_val_max - exvet_used)))
self.cash_used = 0.0
stocks_chunk = []
try:
stocks_chunk = list(self.data.Util.chunks(self.data.stocks_long,self.chunks))[self.data.buy_chunk]
except:
#flag for successful buy
self.data.exvet_buy_needed = False
self.data.buy_chunk = 0
return
#Loop through candidates and place buy orders
#self.data.Debug("chunk " + str(self.data.buy_chunk))
#history = self.data.History(stocks_chunk, 1, Resolution.Daily)
#self.data.Debug(str(history))
for sid in stocks_chunk:
stock = str(sid.Symbol.Value)
symbol = str(sid.Symbol)
if stock in self.data.TSPP or stock in self.data.securities: continue
#self.data.Debug(stock_symbol + " in positions: " + str(stock_data in positions) + ". In data: " + str(self.data.Securities.ContainsKey(stock_data)))
if not stock in self.data.sold and not sid.Invested and self.data.Securities.ContainsKey(symbol):
price = float(self.data.Securities[symbol].Price)
BuyPrice = price*self.BuyFactor
if BuyPrice <= 0.0: continue
StockShares = max(0,np.floor(self.WeightThisBuyOrder*self.exvet_buy_cash/BuyPrice))
if self.rounding == True:
StockShares = round(StockShares, -2)
if StockShares < self.min_order: continue
cost = (BuyPrice*StockShares)
if StockShares == 0 or ((self.cash_used + cost) >= self.exvet_buy_cash): continue
self.cash_used += cost
orders = 0
while StockShares > 0 and orders < 15:
if StockShares == 300:
BuyShares = 300
else:
BuyShares = 200
msg = ''.join(["Buy Limit Order ",str(BuyShares)," shares of ",stock," @ ",str(BuyPrice)])
self.data.Log(msg)
self.data.LimitOrder(symbol, BuyShares, round(BuyPrice,2))
StockShares -= BuyShares
orders += 1
if not self.data.allow_unlimited_daytrades: #mark this as bought to prevent daytrades.
self.data.bought.append(stock)
self.data.buy_chunk += 1
#self.data.Debug("cash used " + str(self.cash_used))
#self.data.Debug("total cash " + str(self.exvet_buy_cash))
def process_tp(self):
#self.data.Debug("TP ")
self.data.tp_needed = False
self.data.Util.process_trailing_stop()
if self.data.exvet_pct == 0.0:
return
portfolio = self.data.Portfolio
positions = [i for i in portfolio.Keys if portfolio[i].HoldStock]
for symbol in positions:
stock = str(symbol.Value)
if stock in self.data.securities or not self.data.Securities.ContainsKey(symbol): continue
try:
gain = self.data.Util.get_gain(symbol)
price = float(self.data.Securities[symbol].Price)
StockShares = int(portfolio[symbol].Quantity)
DayPrice = max(price,(float(portfolio[symbol].HoldingsCost)/StockShares*self.DayFactor))*.99
except:
self.data.Debug("tp data failed")
continue
if gain > (self.DayFactor-1) and stock not in self.data.tp:
if not self.data.allow_unlimited_daytrades:
if stock in self.data.bought:
if not self.data.daytrade_limited:
continue
else:
if len(self.data.daytrades) >= 3:
continue
else:
msg = ''.join(["Sell Limit Order ",str(stock)," @ ",str(DayPrice)," HIGH PROFIT LIMITED DAYTRADE."])
self.data.daytrades.append(0) #mark this as a daytrade.
else:
msg = ''.join(["Sell Limit Order ",str(stock)," @ ",str(DayPrice)," HIGH PROFIT SALE."])
else:
msg = ''.join(["Sell Limit Order ",str(stock)," @ ",str(DayPrice)," HIGH PROFIT DAYTRADE."])
self.data.Log(msg)
if self.data.Util.get_open_orders(symbol):
self.data.reorder.append(symbol)
self.data.Util.cancel_open_order(symbol)
else:
#Daytrade profit target has been exceeded. Sell quickly for high profit.
try:
self.data.LimitOrder(symbol, -StockShares, round(DayPrice,2))
except:
self.data.Debug("failed TP limitorder")
self.data.tp.append(stock)
if not self.data.allow_unlimited_daytrades:
self.data.sold.append(stock) #mark this as sold to prevent daytrades.
def process_reorder(self):
if self.data.exvet_pct == 0.0:
return
#self.data.Debug("process_reorder")
portfolio = self.data.Portfolio
positions = [i for i in portfolio.Keys if portfolio[i].HoldStock]
for symbol in self.data.reorder:
if not symbol in positions or not self.data.Securities.ContainsKey(symbol): continue #check to make sure we still own this stock
DayPrice = float(portfolio[symbol].HoldingsCost)*self.DayFactor
StockShares = int(portfolio[symbol].Quantity)
try:
self.data.LimitOrder(symbol, -StockShares, round(DayPrice,2))
except:
self.data.Debug("reorder sell failed")
self.data.reorder = []
"""
DEFINE UNIVERSE
"""
'''
def CoarseSelectionFunction(self, coarse):
if self.data.exvet_pct == 0.0:
return []
selected = [x for x in coarse if (x.HasFundamentalData)
and (float(x.Price) >= self.MyLeastPrice)
and (float(x.Price) <= self.MyMostPrice)]
#percentiles = selected.Percentile(self.LowVar,self.HighVar)
DollarVolume = sorted(selected, key=lambda x: x.DollarVolume, reverse=True)
#self.data.Log('initial select %d'%(len(DollarVolume)))
upper = int(len(DollarVolume)*self.HighVar/100)
lower = int(len(DollarVolume)*self.LowVar/100)
brackets = DollarVolume[lower:upper]
top = brackets[:self.Top_Stocks]
#self.data.Log('top select %d'%(len(top)))
return [x.Symbol for x in top]
'''
def CoarseSelectionFunction(self, coarse):
if self.data.exvet_pct == 0.0:
return []
# First narrow the original coarse universe down to a managable level.
init_select = [stock for stock in coarse if (stock.HasFundamentalData)
and (float(stock.Price) >= self.MyLeastPrice)
and (float(stock.Price) <= self.MyMostPrice)]
# second convert the initial selection to a pandas dataframe.
stock_symbols = [stock.Symbol for stock in init_select]
stock_data = [(stock.DollarVolume,) for stock in init_select]
column_names = ['dollar_volume']
# Use coerce parameter = True to convert data objects to numbers
data_df = pd.DataFrame.from_records(
stock_data,
index=stock_symbols,
columns=column_names,
coerce_float=True)
# Use pandas methods to select the assets we want
# First find the values of dollar_volume at our desired bounds
lower_percent = data_df.dollar_volume.quantile(self.LowVar)
upper_percent = data_df.dollar_volume.quantile(self.HighVar)
# Now simply query using those values
# Filter for has_fundamentals to remove ETFs
my_universe = (data_df.
query('(dollar_volume >= @lower_percent) & (dollar_volume <= @upper_percent)'))
# See how many securities are found in our universe
self.data.Debug("{} potential securities found ".format(my_universe.shape[0]))
# Expects a list of symbols returned
return my_universe.index.tolist()
def FineSelectionFunction(self, fine):
if self.data.exvet_pct == 0.0:
return []
filtered_fine = [x for x in fine if (x.ValuationRatios.PriceChange1M)]
#self.data.Log('filtered_fine select %d'%(len(filtered_fine)))
sortedByChange = sorted(filtered_fine, key=lambda x: x.ValuationRatios.PriceChange1M, reverse=True)
#sortedByChange = sorted(filtered_fine, key=lambda x: x.ValuationRatios.PriceChange1M, reverse=False)
# take the top entries from our sorted collection
topFine = sortedByChange[:self.MaxOrders]
self.data.Debug('Top select %d'%(len(topFine)))
stocks = [x.Symbol for x in topFine]
return stocks''' Exvet_buy is ordering stocks that we already hold positions in, need to figure out why that isn't working and what other implications it might have (positions not registering) '''