Overall Statistics
Total Trades
5
Average Win
0.58%
Average Loss
0%
Compounding Annual Return
4.744%
Drawdown
1.000%
Expectancy
0
Net Profit
1.162%
Sharpe Ratio
0.992
Probabilistic Sharpe Ratio
49.822%
Loss Rate
0%
Win Rate
100%
Profit-Loss Ratio
0
Alpha
0.073
Beta
-0.073
Annual Standard Deviation
0.039
Annual Variance
0.002
Information Ratio
-3.111
Tracking Error
0.137
Treynor Ratio
-0.538
Total Fees
$5.00
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Algorithm.Framework")
AddReference("QuantConnect.Common")

from System import *
from QuantConnect import *
from QuantConnect.Orders import *
from QuantConnect.Algorithm import *
from QuantConnect.Securities import *
from QuantConnect.Algorithm.Framework import *
from QuantConnect.Algorithm.Framework.Alphas import *
from QuantConnect.Algorithm.Framework.Execution import *
from QuantConnect.Algorithm.Framework.Portfolio import *
from QuantConnect.Algorithm.Framework.Selection import *
from datetime import timedelta


from alpha_custom import custom_alpha_model
from port_insight_wt import port_insight_wt


### <summary>
### Regression algorithm testing portfolio construction model control over rebalancing,
### specifying a custom rebalance function that returns null in some cases, see GH 4075.
### </summary>
class PortfolioRebalanceOnCustomFuncRegressionAlgorithm(QCAlgorithm):
    def Initialize(self):
        ''' Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''

        self.UniverseSettings.Resolution = Resolution.Daily

        self.SetStartDate(2019, 1, 1)
        self.SetEndDate(2019, 4, 1)

        # does this refer to its own insight, or insight of other symbols?
        
        # no orders generated
        #self.Settings.RebalancePortfolioOnInsightChanges = False;

        # if this is true no orders based on insights are generated!
        self.Settings.RebalancePortfolioOnInsightChanges = True;

        self.Settings.RebalancePortfolioOnSecurityChanges = False;

        #self.SetUniverseSelection(CustomUniverseSelectionModel("CustomUniverseSelectionModel", lambda time: [ "AAPL", "IBM", "FB", "SPY", "AIG", "BAC", "BNO" ]))
        self.SetUniverseSelection(CustomUniverseSelectionModel("CustomUniverseSelectionModel", lambda time: [ "AAPL", "AMZN"]))
        self.SetAlpha(custom_alpha_model());
        
        # no rebalancing of already bought stocks
        self.SetPortfolioConstruction(port_insight_wt(self.RebalanceFunction))
        self.SetExecution(ImmediateExecutionModel())
        self.lastRebalanceTime = self.StartDate

    # no rebalancing based on time
    def RebalanceFunction(self, time):
        None


    # def OnOrderEvent(self, orderEvent):
    #     if orderEvent.Status == OrderStatus.Submitted:
    #         if self.UtcTime != self.lastRebalanceTime or self.UtcTime.weekday() != 0:
    #             raise ValueError(f"{self.UtcTime} {orderEvent.Symbol}")
                
                
                
                
                
# END
from clr import AddReference
AddReference("QuantConnect.Common")
AddReference("QuantConnect.Algorithm.Framework")

from QuantConnect import Resolution
from QuantConnect.Algorithm.Framework.Alphas import *
#from Portfolio.EqualWeightingPortfolioConstructionModel import EqualWeightingPortfolioConstructionModel, PortfolioBias


class port_insight_wt(PortfolioConstructionModel):
    '''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, rebalancingParam = Resolution.Daily, portfolioBias = PortfolioBias.LongShort):
        '''Initialize a new instance of InsightWeightingPortfolioConstructionModel
        Args:
            rebalancingParam: 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)'''
        #super().__init__(rebalancingParam, portfolioBias)
        self.portfolioBias = portfolioBias

        # If the argument is an instance of Resolution or Timedelta
        # Redefine rebalancingFunc
        rebalancingFunc = rebalancingParam
        if isinstance(rebalancingParam, int):
            rebalancingParam = Extensions.ToTimeSpan(rebalancingParam)
        if isinstance(rebalancingParam, timedelta):
            rebalancingFunc = lambda dt: dt + rebalancingParam
        if rebalancingFunc:
            self.SetRebalancingFunc(rebalancingFunc)

    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

    def RespectPortfolioBias(self, insight):
        '''Method that will determine if a given insight respects the portfolio bias
        Args:
            insight: The insight to create a target for
        '''
        return self.portfolioBias == PortfolioBias.LongShort or insight.Direction == self.portfolioBias
from clr import AddReference
AddReference("QuantConnect.Common")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Algorithm.Framework")

from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Algorithm.Framework import *
from QuantConnect.Algorithm.Framework.Alphas import AlphaModel, Insight, InsightType, InsightDirection
import datetime

class custom_alpha_model(AlphaModel):
    ''' Provides an implementation of IAlphaModel that always returns the same insight for each security'''

    def __init__(self):
        '''Initializes a new instance of the ConstantAlphaModel class
        Args:
            type: The type of insight
            direction: The direction of the insight
            period: The period over which the insight with come to fruition
            magnitude: The predicted change in magnitude as a +- percentage
            confidence: The confidence in the insight'''
        self.securities = []
        self.insightsTimeBySymbol = {}


    def Update(self, algorithm, data):
        ''' Creates a constant insight for each security as specified via the constructor
        Args:
            algorithm: The algorithm instance
            data: The new data available
        Returns:
            The new insights generated

        Jan 1st:  UP for AAPL for 30 days, at 10% portfolio
        Jan 15th: UP for AMZN for 30 days, at 10% portfolio
        
        Expectation: 
            Don't sell any AAPL on Jan15th (its % might be higher than 10%, that's OK)
            Sell APPL on Jan 31st (after expiration of the Alpha signal)
        '''
        insights = []

        dt1 = datetime.datetime(2019,1,1)
        dt2 = datetime.datetime(2019,1,15)
        dt = algorithm.Time
        wt = 0.1

        for security in self.securities:
            # security price could be zero until we get the first data point. e.g. this could happen
            # when adding both forex and equities, we will first get a forex data point
            # https://www.geeksforgeeks.org/comparing-dates-python/
            if security.Symbol.Value == "AAPL" and dt == dt1:
                period = TimeSpan.FromDays(30)
                #if security.Price != 0 and self.ShouldEmitInsight(algorithm.UtcTime, security.Symbol):
                ins_type = InsightType.Price
                direction = InsightDirection.Up
                # Insight(symbol, timedelta, type, direction, magnitude=None, confidence=None, sourceModel=None, weight=None)
                insights.append(Insight(security.Symbol, period, ins_type, direction, 0.025, 1, "custom", wt))
                
            if security.Symbol.Value == "AMZN" and dt == dt2:
                period = TimeSpan.FromDays(30)
                #if security.Price != 0 and self.ShouldEmitInsight(algorithm.UtcTime, security.Symbol):
                ins_type = InsightType.Price
                direction = InsightDirection.Up
                insights.append(Insight(security.Symbol, period, ins_type, direction, 0.025, 1, "custom", wt))

        return insights


    def OnSecuritiesChanged(self, algorithm, changes):
        ''' Event fired each time the we add/remove securities from the data feed
        Args:
            algorithm: The algorithm instance that experienced the change in securities
            changes: The security additions and removals from the algorithm'''
        for added in changes.AddedSecurities:
            self.securities.append(added)

        # this will allow the insight to be re-sent when the security re-joins the universe
        for removed in changes.RemovedSecurities:
            if removed in self.securities:
                self.securities.remove(removed)
            if removed.Symbol in self.insightsTimeBySymbol:
                self.insightsTimeBySymbol.pop(removed.Symbol)


    def ShouldEmitInsight(self, utcTime, symbol):

        generatedTimeUtc = self.insightsTimeBySymbol.get(symbol)

        if generatedTimeUtc is not None:
            # we previously emitted a insight for this symbol, check it's period to see
            # if we should emit another insight
            if utcTime - generatedTimeUtc < self.period:
                return False

        # we either haven't emitted a insight for this symbol or the previous
        # insight's period has expired, so emit a new insight now for this symbol
        self.insightsTimeBySymbol[symbol] = utcTime
        return True

def strfdelta(tdelta):
    d = tdelta.days
    h, rem = divmod(tdelta.seconds, 3600)
    m, s = divmod(rem, 60)
    return "{}.{:02d}:{:02d}:{:02d}".format(d,h,m,s)