chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
# 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 AlgorithmImports import *
|
||||
|
||||
class ConstantAlphaModel(AlphaModel):
|
||||
''' Provides an implementation of IAlphaModel that always returns the same insight for each security'''
|
||||
|
||||
def __init__(self, type, direction, period, magnitude = None, confidence = None, weight = None):
|
||||
'''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
|
||||
weight: The portfolio weight of the insights'''
|
||||
self.type = type
|
||||
self.direction = direction
|
||||
self.period = period
|
||||
self.magnitude = magnitude
|
||||
self.confidence = confidence
|
||||
self.weight = weight
|
||||
self.securities = []
|
||||
self.insights_time_by_symbol = {}
|
||||
|
||||
self.Name = '{}({},{},{}'.format(self.__class__.__name__, type, direction, strfdelta(period))
|
||||
if magnitude is not None:
|
||||
self.Name += ',{}'.format(magnitude)
|
||||
if confidence is not None:
|
||||
self.Name += ',{}'.format(confidence)
|
||||
|
||||
self.Name += ')'
|
||||
|
||||
|
||||
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'''
|
||||
insights = []
|
||||
|
||||
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
|
||||
if security.price != 0 and self.should_emit_insight(algorithm.utc_time, security.symbol):
|
||||
insights.append(Insight(security.symbol, self.period, self.type, self.direction, self.magnitude, self.confidence, weight = self.weight))
|
||||
|
||||
return insights
|
||||
|
||||
|
||||
def on_securities_changed(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.added_securities:
|
||||
self.securities.append(added)
|
||||
|
||||
# this will allow the insight to be re-sent when the security re-joins the universe
|
||||
for removed in changes.removed_securities:
|
||||
if removed in self.securities:
|
||||
self.securities.remove(removed)
|
||||
if removed.symbol in self.insights_time_by_symbol:
|
||||
self.insights_time_by_symbol.pop(removed.symbol)
|
||||
|
||||
|
||||
def should_emit_insight(self, utc_time, symbol):
|
||||
if symbol.is_canonical():
|
||||
# canonical futures & options are none tradable
|
||||
return False
|
||||
|
||||
generated_time_utc = self.insights_time_by_symbol.get(symbol)
|
||||
|
||||
if generated_time_utc is not None:
|
||||
# we previously emitted a insight for this symbol, check it's period to see
|
||||
# if we should emit another insight
|
||||
if utc_time - generated_time_utc < 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.insights_time_by_symbol[symbol] = utc_time
|
||||
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)
|
||||
Reference in New Issue
Block a user