Overall Statistics
Total Trades
56
Average Win
0.08%
Average Loss
-0.01%
Compounding Annual Return
5.359%
Drawdown
16.700%
Expectancy
3.663
Net Profit
13.411%
Sharpe Ratio
0.499
Probabilistic Sharpe Ratio
19.554%
Loss Rate
32%
Win Rate
68%
Profit-Loss Ratio
5.87
Alpha
0.055
Beta
-0.07
Annual Standard Deviation
0.098
Annual Variance
0.01
Information Ratio
-0.15
Tracking Error
0.249
Treynor Ratio
-0.694
Total Fees
$57.25
Estimated Strategy Capacity
$67000000.00
Lowest Capacity Asset
AGG SSC0EI5J2F6T
from datetime import timedelta
from Portfolio.EqualWeightingPortfolioConstructionModel import EqualWeightingPortfolioConstructionModel


class QuarterlyRebalance(QCAlgorithm):

    def Initialize(self):
        
        # Set Start Date, End Date, and Cash
        #---------------------------------------------------------------------------------------
        self.SetStartDate(2018, 1, 1)     
        self.SetEndDate(2020, 5, 31)        
        self.SetCash(100000)                 
        #---------------------------------------------------------------------------------------
        
        # Resolution
        #---------------------------------------------------------------------------------------
        self.resolution = Resolution.Daily    
        #---------------------------------------------------------------------------------------
        
        # Brokerage, Orders, Portfolio Settings
        #---------------------------------------------------------------------------------------
        # self.SetBrokerageModel(BrokerageName.TradierBrokerage)#, AccountType.Margin)      # AccountType.Margin required for "non-null" Portfolio and Execution Models
        self.SetBrokerageModel(BrokerageName.AlphaStreams)
        self.DefaultOrderProperties.TimeInForce = TimeInForce.GoodTilCanceled           # Time in Force for open orders. Default is GoodTilCanceled (GTC)
        self.Settings.RebalancePortfolioOnInsightChanges = False          
        self.Settings.RebalancePortfolioOnSecurityChanges = False
        #---------------------------------------------------------------------------------------

        # Universe Settings
        #---------------------------------------------------------------------------------------
        self.UniverseSettings.Resolution = self.resolution
        self.UniverseSettings.SetDataNormalizationMode = DataNormalizationMode.SplitAdjusted
        self.UniverseSettings.FeeModel = ConstantFeeModel(0.0)
        self.UniverseSettings.ExtendedMarketHours = False
        self.UniverseSettings.Leverage = 0
        self.weekday = self.Time.isocalendar()[2]     
        #---------------------------------------------------------------------------------------
        
        # Algorithm Framework Configuration
        #---------------------------------------------------------------------------------------
        self.SetUniverseSelection(CustomUniverseSelectionModel("CustomUniverseSelectionModel", lambda time: [ "SPY", "AGG"]))
        self.SetAlpha(EMAAlphaModel())  
        self.SetPortfolioConstruction(MyInsightWeightingPortfolioConstructionModel(timedelta(weeks=4)))       
        self.SetExecution(ImmediateExecutionModel())                               
        self.SetRiskManagement(NullRiskManagementModel()) 
        #---------------------------------------------------------------------------------------

        
     
class EMAAlphaModel(AlphaModel):
    
    def __init__(self):
        
        pass
            
        
    def OnSecuritiesChanged(self, algorithm, changes):
        # Create indicator for each new security
        for security in changes.AddedSecurities:
            pass
                
        for security in changes.RemovedSecurities:
            pass
        
            
            
    def Update(self, algorithm, data):
        
        insights = []
        
        for security in algorithm.ActiveSecurities.Values:
            insights.append(Insight.Price(security.Symbol, timedelta(minutes = 20), InsightDirection.Up, 0.003, None, None, 0.45))


        return insights



class MyInsightWeightingPortfolioConstructionModel(EqualWeightingPortfolioConstructionModel):
    '''Provides an implementation of IPortfolioConstructionModel that generates percent targets based on the
    Insight.Weight. The target percent holdings of each Symbol is given by the Insight.Weight from the last
    active Insight for that symbol.
    For insights of direction InsightDirection.Up, long targets are returned and for insights of direction
    InsightDirection.Down, short targets are returned.
    If the sum of all the last active Insight per symbol is bigger than 1, it will factor down each target
    percent holdings proportionally so the sum is 1.
    It will ignore Insight that have no Insight.Weight value.'''
    
    def __init__(self, rebalance = Resolution.Daily, portfolioBias = PortfolioBias.LongShort):
        '''Initialize a new instance of InsightWeightingPortfolioConstructionModel
        Args:
            rebalance: Rebalancing parameter. If it is a timedelta, date rules or Resolution, it will be converted into a function.
                              If None will be ignored.
                              The function returns the next expected rebalance time for a given algorithm UTC DateTime.
                              The function returns null if unknown, in which case the function will be called again in the
                              next loop. Returning current time will trigger rebalance.
            portfolioBias: Specifies the bias of the portfolio (Short, Long/Short, Long)'''
            
        #if algorithm.Time.month == 6:    # $$$$$$$$$$CROSSFIELD CAPITAL EDIT$$$$$$$$$$$$$
        #    algorithm.Debug("YOLO")
        self.months = 0
        super().__init__(rebalance, portfolioBias)
    
    def ShouldCreateTargetForInsight(self, insight):
        '''Method that will determine if the portfolio construction model should create a
        target for this insight
        Args:
            insight: The insight to create a target for'''
        # Ignore insights that don't have Weight value
        return insight.Weight is not None
    
    def DetermineTargetPercent(self, activeInsights):
        '''Will determine the target percent for each insight
        Args:
            activeInsights: The active insights to generate a target for'''
        
        result = {}

        # We will adjust weights proportionally in case the sum is > 1 so it sums to 1.
        weightSums = sum(self.GetValue(insight) for insight in activeInsights if self.RespectPortfolioBias(insight))
        weightFactor = 1.0
        if weightSums > 1:
            weightFactor = 1 / weightSums
        for insight in activeInsights:
            result[insight] = (insight.Direction if self.RespectPortfolioBias(insight) else InsightDirection.Flat) * self.GetValue(insight) * weightFactor
        return result
    
    def GetValue(self, insight):
        '''Method that will determine which member will be used to compute the weights and gets its value
        Args:
            insight: The insight to create a target for
        Returns:
            The value of the selected insight member'''
        return insight.Weight