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 orders
from 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.0
from 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.0
from 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 metrics
from 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)
'''