Files
quantconnect--lean/Algorithm.Python/SecurityCustomPropertiesAlgorithm.py
T
Jhonathan Abreu 956428b7bf
Regression Tests / build (push) Has been cancelled
Benchmarks / build (push) Has been cancelled
Research Regression Tests / build (push) Has been cancelled
Build & Test Lean / build (push) Has been cancelled
Python Virtual Environments / build (push) Has been cancelled
Security cache dynamic collection for custom properties (#7327)
* Dynamic security properties for quick objects storage

* Documentation and other minor changes

* Add Security indexer to access custom properties

* Address peer review

* Minor changes

* Improve regression algorithms

* Bumped pythonnet to version 2.0.19

* Bumped pythonnet to version 2.0.20

* Address peer review
2023-06-27 10:39:20 -04:00

91 lines
4.0 KiB
Python

# 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 cmath import isclose
from AlgorithmImports import *
### <summary>
### Demonstration of how to use custom security properties.
### In this algorithm we trade a security based on the values of a slow and fast EMAs which are stored in the security itself.
### </summary>
class SecurityCustomPropertiesAlgorithm(QCAlgorithm):
'''Demonstration of how to use custom security properties.
In this algorithm we trade a security based on the values of a slow and fast EMAs which are stored in the security itself.'''
def Initialize(self):
self.SetStartDate(2013,10, 7)
self.SetEndDate(2013,10,11)
self.SetCash(100000)
self.spy = self.AddEquity("SPY", Resolution.Minute)
# Using the dynamic interface to store our indicator as a custom property.
self.spy.SlowEma = self.EMA(self.spy.Symbol, 30, Resolution.Minute)
# Using the generic interface to store our indicator as a custom property.
self.spy.Add("FastEma", self.EMA(self.spy.Symbol, 60, Resolution.Minute))
# Using the indexer to store our indicator as a custom property
self.spy["BB"] = self.BB(self.spy.Symbol, 20, 1, MovingAverageType.Simple, Resolution.Minute);
# Fee factor to be used by the custom fee model
self.spy.FeeFactor = 0.00002
self.spy.SetFeeModel(CustomFeeModel())
# This property will be used to store the prices used to calculate the fees in order to assert the correct fee factor is used.
self.spy.OrdersFeesPrices = {}
def OnData(self, data):
if not self.spy.FastEma.IsReady:
return
if not self.Portfolio.Invested:
# Using the property and the generic interface to access our indicator
if self.spy.SlowEma > self.spy.FastEma:
self.SetHoldings(self.spy.Symbol, 1)
else:
if self.spy.Get[ExponentialMovingAverage]("SlowEma") < self.spy.Get[ExponentialMovingAverage]("FastEma"):
self.Liquidate(self.spy.Symbol)
# Using the indexer to access our indicator
bb: BollingerBands = self.spy["BB"]
self.Plot("BB", bb.UpperBand, bb.MiddleBand, bb.LowerBand)
def OnOrderEvent(self, orderEvent):
if orderEvent.Status == OrderStatus.Filled:
fee = orderEvent.OrderFee
expectedFee = self.spy.OrdersFeesPrices[orderEvent.OrderId] * orderEvent.AbsoluteFillQuantity * self.spy.FeeFactor
if not isclose(fee.Value.Amount, expectedFee, rel_tol=1e-15):
raise Exception(f"Custom fee model failed to set the correct fee. Expected: {expectedFee}. Actual: {fee.Value.Amount}")
def OnEndOfAlgorithm(self):
if self.Transactions.OrdersCount == 0:
raise Exception("No orders executed")
class CustomFeeModel(FeeModel):
'''This custom fee is implemented for demonstration purposes only.'''
def GetOrderFee(self, parameters):
security = parameters.Security
# custom fee math using the fee factor stored in security instance
feeFactor = security.FeeFactor
if feeFactor is None:
feeFactor = 0.00001
# Store the price used to calculate the fee for this order
security["OrdersFeesPrices"][parameters.Order.Id] = security.Price
fee = max(1.0, security.Price * parameters.Order.AbsoluteQuantity * feeFactor)
return OrderFee(CashAmount(fee, "USD"))