Overall Statistics
Total Trades
1693
Average Win
0.53%
Average Loss
-0.33%
Compounding Annual Return
10.573%
Drawdown
16.300%
Expectancy
0.450
Net Profit
238.641%
Sharpe Ratio
0.797
Loss Rate
45%
Win Rate
55%
Profit-Loss Ratio
1.62
Alpha
0.08
Beta
0.006
Annual Standard Deviation
0.101
Annual Variance
0.01
Information Ratio
0.069
Tracking Error
0.191
Treynor Ratio
12.795
Total Fees
$2581.74
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 AlphaGenerator_VAA import *

class Modexvet(QCAlgorithm):
    def Initialize(self):
        """
        Algo Settings
        """
        self.Minimum_order_value = 1000 #This reduces fees by trading only when a large reblance is needed
        
        """
        QC Trading Settings
        """
        self.AddEquity("SPY", Resolution.Minute)
        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])
        self.SetBenchmark("SPY")
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)
        self.SetWarmup(252) #252
        
        """
        Initialization of all components of the algorithm
        """
        self.Util = Utilities(self) #import Utilities functions
        
        #Start the Portfolio Manager - Defines the allocation between the different component alpha signals
        self.portfolio_manager = PortfolioManager_EquiWeight(self) #Simple even weighting of alpha signals
        
        #Start the Execution Handler - Performs trades
        self.exec_handler = ExecutionHandler_Market(self) #Simple Market orders
        
        #Build list of alpha signals
        self.alpha_slsqp = AlphaGenerator_SLSQP(self)
        self.alpha_zscore = AlphaGenerator_ZScore(self)
        self.alpha_WVF_opt = AlphaGenerator_WVF_opt(self)
        self.alpha_ACR = AlphaGenerator_ACR(self)
        self.alpha_VAA = AlphaGenerator_VAA(self)
        
        self.portfolio_manager.create_alpha_list()
        
        """
        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.portfolio_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:
            if not stock == "SPY":
                self.AddSecurity(SecurityType.Equity, stock, Resolution.Daily)
                #self.AddEquity(stock, Resolution.Daily)
        
        """
        Register Indicators
        """
        for alpha in self.portfolio_manager.list_alpha:
            alpha.register_indicators()
        
        """
        Rebalance Schedule settings
        """
        self.Schedule.On(self.DateRules.EveryDay("SPY"),
                self.TimeRules.Every(TimeSpan.FromMinutes(60)),
                Action(self.Util.my_record_vars))
        
        self.Schedule.On(self.DateRules.EveryDay("SPY"),
                self.TimeRules.AfterMarketOpen("SPY", -15),
                Action(self.my_before_trading))
                
        if self.GetParameter("WVF") == "True":
            self.Schedule.On(self.DateRules.Every([DayOfWeek.Friday]),
                self.TimeRules.BeforeMarketClose("SPY", 5),
                Action(self.alpha_WVF_opt.WVF_opt_reset))
        
        if self.GetParameter("VAA") == "True":
            self.Schedule.On(self.DateRules.Every([DayOfWeek.Friday]),
                self.TimeRules.BeforeMarketClose("SPY", 5),
                Action(self.alpha_VAA.VAA_reset))
        
        """
        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)
        
        """
        Flags - Do not Change these!
        """
        self.modular_rebal_needed = True
        self.calculated_alphas = False
        self.computed_portfolio = False
        self.before_trading_success = False
        
        """
        Temp Variables - Do not Change these!
        """
        self.modular_leverage = 1.0
        
        """
        Setup Charting
        """
        stockPlot = Chart('Data Graph')
        stockPlot.AddSeries(Series('Long', SeriesType.Line, 0))
        stockPlot.AddSeries(Series('Short', 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.calculated_alphas = False
        
        ''' NOT USED
        #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
        '''
        
        #success
        self.before_trading_success = True
    
    """
    Timed Functions
    """
    def OnDataConsolidated(self, sender, bar):
        #if before_trading_start has not run due to error:
        if not self.before_trading_success == True:
            self.my_before_trading()
            return
                
        #Rebalance when needed:
        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.portfolio_manager.list_alpha,self.portfolio_manager.calculated):
                alpha.calculate_alpha()
                self.portfolio_manager.calculated.append(alpha)
                return
            self.calculated_alphas = True
            self.portfolio_manager.calculated = []
        if not self.computed_portfolio:
            # compute new allocation each alpha would like
            for alpha in self.portfolio_manager.list_alpha:
                alpha.compute_allocation()
            # compute new target portfolio
            self.portfolio_manager.compute_target()
            # compute order strategy
            self.computed_portfolio = True
        
        target = self.portfolio_manager.target_portfolio
        
        self.exec_handler.execute_orders(target)
"""
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)
    
    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 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 register_indicators(self):
        raise NotImplementedError()
    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
    
    #Builds a list of alphas
    def create_alpha_list(self):
        raise NotImplementedError()
        
    # 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
    
    #Builds a list of alphas
    def create_alpha_list(self):    
        # creation of alpha generator #1
        if self.data.GetParameter("SLSQP") == "True":
            self.data.Log("SLSQP ACTIVE")
            self.list_alpha.append(self.data.alpha_slsqp)
        
        # creation of alpha generator #2
        if self.data.GetParameter("ZScore") == "True":
            self.data.Log("ZScore ACTIVE")
            self.list_alpha.append(self.data.alpha_zscore)
        
        # creation of alpha generator #3
        if self.data.GetParameter("WVF") == "True":
            self.data.Log("WVF_OPT ACTIVE")
            self.list_alpha.append(self.data.alpha_WVF_opt)
        
        # creation of alpha generator #4
        if self.data.GetParameter("ACR") == "True":
            self.data.Log("ACR ACTIVE")
            self.list_alpha.append(self.data.alpha_ACR)
        
        # creation of alpha generator #5
        if self.data.GetParameter("VAA") == "True":
            self.data.Log("VAA ACTIVE")
            self.list_alpha.append(self.data.alpha_VAA)
    
    def compute_target(self):
        #clear desired allocation
        for stock in self.data.securities:
            self.target_portfolio[stock] = 0
        
        #update allocation for each alpha signal
        for alpha in self.list_alpha:
            for stock in alpha.alloc:
                #Calculate the weight with simple equal weighting
                nb_alpha = max(1,len(self.list_alpha)) #prevents divide by 0
                alloc_alpha = alpha.alloc[stock] / nb_alpha
                #update the total allocation for the stock
                self.target_portfolio[stock] = self.target_portfolio[stock] + alloc_alpha
        
        #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):
        #Gets curernt Portfolio values
        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
        
        #Process Sells
        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
            #Calculate required rebalance amount for each allocation
            current = float(self.data.Portfolio[stock].AbsoluteHoldingsValue)
            goal = (port_value_adjusted*target_portfolio[stock])
            goal_pct = (modular_leverage*target_portfolio[stock])
            amount = (goal-current)
            
            #Sell if needed and order exceeds minimum, always sells if allocation is 0%
            if amount < 0.0 and (abs(amount) > self.data.Minimum_order_value or goal_pct == 0.0):
                self.data.SetHoldings(stock, goal_pct)
                sold = True
            
            else:
                continue
                
        if sold: #wait until next minute for order execution if any stock was sold
            self.data.modular_rebal_needed = True
            return
        
        #Process Buys
        for stock in self.data.securities:
            #Calculate required rebalance amount for each allocation
            current = float(self.data.Portfolio[stock].AbsoluteHoldingsValue)
            goal = (port_value_adjusted*target_portfolio[stock])
            goal_pct = (modular_leverage*target_portfolio[stock])
            amount = (goal-current)
            
            #Buy if needed and order exceeds minimum
            if amount > 0.0 and abs(amount) > self.data.Minimum_order_value:
                self.data.SetHoldings(stock, goal_pct)
            
            else:
                continue
        
        #Log final desired allocation
        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",
                    "BND",
                    ]
                    
        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 register_indicators(self):
        pass
    
    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
        
        stocks_temp = ret_mean.keys()
        
        bnds = []
        limits = [0,1] #[0,1] for long only [-1,1] for long/short
        
        for stock in stocks_temp:
            bnds.append(limits)
        
        self.x1 = np.asarray([1.0/len(stocks_temp) for i in stocks_temp])
        
        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(stocks_temp):
                    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",
                    "ZIV",
                    ]
        """
        zscore parameters
        """
        self.fixed_wt_pct = 0.50
        self.fixed_wt = {
                            "XLP": 0.50,
                            "TLT": 0.40, 
                            "ZIV": 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 register_indicators(self):
        pass
    
    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
        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,
                    "ZIV",
                    ]
        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 register_indicators(self):
        pass
    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["ZIV"] = 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.bull] = 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",
                        ]
        self.Hedge = ["VXX",]
        for stock in self.ACR_sectors:
            self.data.AddEquity(stock)
            
        self.stocks = self.ACR_assets+self.ACR_bonds+self.ACR_fixed + self.Hedge
        
        """
        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 register_indicators(self):
        pass
    
    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]
        
        risk_weight = 0
        for x in range(n):
            self.allocation[self.ACR_assets[x]] = ACR_assets_weight[x]#*.95
            risk_weight += ACR_assets_weight[x]
        
        #self.allocation[self.Hedge[0]] = risk_weight * .05
        
        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
'''
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)
'''
from Manage_Modular import *
from Utility_Functions import *

class AlphaGenerator_VAA(AlphaGenerator):
    def __init__(self, data):
        AlphaGenerator.__init__(self)
        
        self.data = data
        self.Util = Utilities(self.data)
        
        # these are the growth symbols we'll rotate through
        self.GrowthSymbols =["SPY", 
                             "VEA",
                             "AGG",
                             "VTI",
                             ]

        # these are the safety symbols we go to when things are looking bad for growth
        self.SafetySymbols = ["LQD", 
                              "IEF", 
                              "SHY",
                              "TBF",
                              "DBC",
                              "EEM",
                              ]
                              
        #Hedge
        self.Hedge = ["VXX",]
        
        #VAA assets
        self.stocks = self.GrowthSymbols + self.SafetySymbols + self.Hedge
        
        self.allocation = {}
        
        self.SymbolData = []
        self.SafetyData = []
        self.VAA_calculated = False
        
        """
        VAA parameters
        """
        self.weight1 = 12
        self.weight2 = 4
        self.weight3 = 2
        self.weight4 = 1
    
    def register_indicators(self):
        """
        VAA Indicators
        """
        # I split the indicators into two different sets to make it easier for illustrative purposes below.
        # Storing all risky asset data into SymbolData object
        for symbol in list(self.GrowthSymbols):
            self.oneMonthPerformance = self.data.MOMP(symbol, 21, Resolution.Daily)
            self.threeMonthPerformance = self.data.MOMP(symbol, 63, Resolution.Daily)
            self.sixMonthPerformance = self.data.MOMP(symbol, 126, Resolution.Daily)
            self.twelveMonthPerformance = self.data.MOMP(symbol, 252, Resolution.Daily)
            self.SymbolData.append([symbol, self.oneMonthPerformance, self.threeMonthPerformance, self.sixMonthPerformance, self.twelveMonthPerformance])
            
        # Storing all risk-free data into SafetyData object
        for symbol in list(self.SafetySymbols):
            self.oneMonthPerformance = self.data.MOMP(symbol, 21, Resolution.Daily)
            self.threeMonthPerformance = self.data.MOMP(symbol, 63, Resolution.Daily)
            self.sixMonthPerformance = self.data.MOMP(symbol, 126, Resolution.Daily)
            self.twelveMonthPerformance = self.data.MOMP(symbol, 252, Resolution.Daily)
            self.SafetyData.append([symbol, self.oneMonthPerformance, self.threeMonthPerformance, self.sixMonthPerformance, self.twelveMonthPerformance])
            
    def compute_allocation(self):
        #self.data.Log("compute_allocation")
        for sid in self.allocation:
            self.alloc[sid] = self.allocation[sid]
        
    def calculate_alpha(self):
        if self.VAA_calculated:
            return
        
        for sid in self.stocks:
                self.allocation[sid] = 0.0

        ##Using the Score class at the bottom, compute the score for each risky asset.
        ##This approach overweights the front month momentum value and progressively underweights older momentum values
        
        orderedObjScores = sorted(self.SymbolData, key=lambda x: self.ObjectiveScore(x[1].Current.Value,x[2].Current.Value,x[3].Current.Value,x[4].Current.Value), reverse=True)
        
        ##Using the Score class at the bottom, compute the score for each risk-free asset.
        orderedSafeScores = sorted(self.SafetyData, key=lambda x: self.ObjectiveScore(x[1].Current.Value,x[2].Current.Value,x[3].Current.Value,x[4].Current.Value), reverse=True)
        
        ##Count the number of risky assets with negative momentum scores and store in N. If all four of the offensive assets exhibit positive momentum scores, 
        ##select the offensive asset with the highest score and allocate 100% of the portfolio to that asset at the close
        N = 0
        for x in orderedObjScores:
            if self.ObjectiveScore(x[1].Current.Value,x[2].Current.Value,x[3].Current.Value,x[4].Current.Value) < 0:
                N += 1
        
        if N >= 1:
            ## If any of the four risky assets exhibit negative momentum scores, select the risk-free asset (LQD, IEF or SHY) with the highest score 
            ## (regardless of whether the score is > 0) and allocate 100% of the portfolio to that asset at the close.
            self.allocation[orderedSafeScores[0][0]] = 1.0
            #self.allocation[self.Hedge[0]] = 0.0
            
        else:
            ## If none of the risky assets come back with negative momentum scores, allocation 100% to the best scoring risky asset and hold until month end
            self.allocation[orderedObjScores[0][0]] = 1.0 #.95
            #self.allocation[self.Hedge[0]] = .05
        
        self.VAA_calculated = True
        
    def ObjectiveScore(self,oneMonthPerformanceValue,threeMonthPerformanceValue,sixMonthPerformanceValue,twelveMonthPerformanceValue):
        
        return  ((self.weight1 * oneMonthPerformanceValue)
                + (self.weight2 * threeMonthPerformanceValue)
                + (self.weight3 * sixMonthPerformanceValue)
                + (self.weight4 * twelveMonthPerformanceValue))
                
    def VAA_reset(self):
        #self.data.Log("VAA_reset")
        self.VAA_calculated = False