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)