/* * 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. */ using System; using System.Linq; using System.Collections; using QuantConnect.Interfaces; using System.Collections.Generic; namespace QuantConnect.Algorithm.Framework.Portfolio { /// /// Provides a collection for managing s for each symbol /// public class PortfolioTargetCollection : ICollection, IDictionary { private List _enumerable; private List> _kvpEnumerable; private readonly Dictionary _targets = new (); /// /// Gets the number of targets in this collection /// public int Count { get { lock (_targets) { return _targets.Count; } } } /// /// True if there is no target in the collection /// public bool IsEmpty { get { lock (_targets) { return _targets.Count == 0; } } } /// /// Gets `false`. This collection is not read-only. /// public bool IsReadOnly => false; /// /// Gets the symbol keys for this collection /// public ICollection Keys { get { lock (_targets) { return _targets.Keys.ToList(); } } } /// /// Gets all portfolio targets in this collection /// Careful, will return targets for securities that might have no data yet. /// public ICollection Values { get { var result = _enumerable; if (result == null) { lock (_targets) { result = _enumerable = _targets.Values.ToList(); } } return result; } } /// /// Adds the specified target to the collection. If a target for the same symbol /// already exists it wil be overwritten. /// /// The portfolio target to add public void Add(IPortfolioTarget target) { if (target == null) { return; } lock (_targets) { _enumerable = null; _kvpEnumerable = null; _targets[target.Symbol] = target; } } /// /// Adds the specified target to the collection. If a target for the same symbol /// already exists it wil be overwritten. /// /// The portfolio target to add public void Add(KeyValuePair target) { Add(target); } /// /// Adds the specified target to the collection. If a target for the same symbol /// already exists it wil be overwritten. /// /// The symbol key /// The portfolio target to add public void Add(Symbol symbol, IPortfolioTarget target) { Add(target); } /// /// Adds the specified targets to the collection. If a target for the same symbol /// already exists it will be overwritten. /// /// The portfolio targets to add public void AddRange(IEnumerable targets) { lock (_targets) { _enumerable = null; _kvpEnumerable = null; foreach (var item in targets) { _targets[item.Symbol] = item; } } } /// /// Adds the specified targets to the collection. If a target for the same symbol /// already exists it will be overwritten. /// /// The portfolio targets to add public void AddRange(IPortfolioTarget[] targets) { AddRange((IEnumerable)targets); } /// /// Removes all portfolio targets from this collection /// public void Clear() { lock (_targets) { _enumerable = null; _kvpEnumerable = null; _targets.Clear(); } } /// /// Removes fulfilled portfolio targets from this collection. /// Will only take into account actual holdings and ignore open orders. /// public void ClearFulfilled(IAlgorithm algorithm) { foreach (var target in this) { var security = algorithm.Securities[target.Symbol]; var holdings = security.Holdings.Quantity; // check to see if we're done with this target if (Math.Abs(target.Quantity - holdings) < security.SymbolProperties.LotSize) { Remove(target.Symbol); } } } /// /// Determines whether or not the specified target exists in this collection. /// NOTE: This checks for the exact specified target, not by symbol. Use ContainsKey /// to check by symbol. /// /// The portfolio target to check for existence. /// True if the target exists, false otherwise public bool Contains(IPortfolioTarget target) { if (target == null) { return false; } lock (_targets) { return _targets.ContainsKey(target.Symbol); } } /// /// Determines whether the specified symbol/target pair exists in this collection /// /// The symbol/target pair /// True if the pair exists, false otherwise public bool Contains(KeyValuePair target) { return Contains(target); } /// /// Determines whether the specified symbol exists as a key in this collection /// /// The symbol key /// True if the symbol exists in this collection, false otherwise public bool ContainsKey(Symbol symbol) { lock (_targets) { return _targets.ContainsKey(symbol); } } /// /// Copies the targets in this collection to the specified array /// /// The destination array to copy to /// The index in the array to start copying to public void CopyTo(IPortfolioTarget[] array, int arrayIndex) { lock (_targets) { _targets.Values.CopyTo(array, arrayIndex); } } /// /// Copies the targets in this collection to the specified array /// /// The destination array to copy to /// The index in the array to start copying to public void CopyTo(KeyValuePair[] array, int arrayIndex) { WithDictionary(d => d.CopyTo(array, arrayIndex)); } /// /// Removes the target for the specified symbol if it exists in this collection. /// /// The symbol to remove /// True if the symbol's target was removed, false if it doesn't exist in the collection public bool Remove(Symbol symbol) { lock (_targets) { if (_targets.Remove(symbol)) { _enumerable = null; _kvpEnumerable = null; return true; } return false; } } /// /// Removes the target for the specified symbol/target pair if it exists in this collection. /// /// The symbol/target pair to remove /// True if the symbol's target was removed, false if it doesn't exist in the collection public bool Remove(KeyValuePair target) { return Remove(target.Value); } /// /// Removes the target if it exists in this collection. /// /// The target to remove /// True if the target was removed, false if it doesn't exist in the collection public bool Remove(IPortfolioTarget target) { if (target == null) { return false; } lock (_targets) { IPortfolioTarget existing; if (_targets.TryGetValue(target.Symbol, out existing)) { // need to confirm that we're removing the requested target and not a different target w/ the same symbol key if (existing.Equals(target)) { return Remove(target.Symbol); } } } return false; } /// /// Attempts to retrieve the target for the specified symbol /// /// The symbol /// The portfolio target for the symbol, or null if not found /// True if the symbol's target was found, false if it does not exist in this collection public bool TryGetValue(Symbol symbol, out IPortfolioTarget target) { lock (_targets) { return _targets.TryGetValue(symbol, out target); } } /// /// Gets or sets the portfolio target for the specified symbol /// /// The symbol /// The symbol's portfolio target if it exists in this collection, if not a will be thrown. public IPortfolioTarget this[Symbol symbol] { get { lock (_targets) { return _targets[symbol]; } } set { lock (_targets) { _enumerable = null; _kvpEnumerable = null; _targets[symbol] = value; } } } /// /// Gets an enumerator to iterator over the symbol/target key value pairs in this collection. /// /// Symbol/target key value pair enumerator IEnumerator> IEnumerable>.GetEnumerator() { var result = _kvpEnumerable; if (result == null) { lock (_targets) { _kvpEnumerable = result = _targets.ToList(); } } return result.GetEnumerator(); } /// /// Gets an enumerator to iterator over all portfolio targets in this collection. /// This is the default enumerator for this collection. /// /// Portfolio targets enumerator public IEnumerator GetEnumerator() { var result = _enumerable; if (result == null) { lock (_targets) { _enumerable = result = _targets.Values.ToList(); } } return result.GetEnumerator(); } /// /// Gets an enumerator to iterator over all portfolio targets in this collection. /// This is the default enumerator for this collection. /// Careful, will return targets for securities that might have no data yet. /// /// Portfolio targets enumerator IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } /// /// Helper function to easily access explicitly implemented interface methods against concurrent dictionary /// private void WithDictionary(Action> action) { lock (_targets) { action(_targets); } } /// /// Returned an ordered enumerable where position reducing orders are executed first /// and the remaining orders are executed in decreasing order value. /// Will NOT return targets for securities that have no data yet. /// Will NOT return targets for which current holdings + open orders quantity, sum up to the target quantity /// /// The algorithm instance public IEnumerable OrderByMarginImpact(IAlgorithm algorithm) { if (IsEmpty) { return Enumerable.Empty(); } return this.OrderTargetsByMarginImpact(algorithm); } } }