/* * 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.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using QuantConnect.Interfaces; using QuantConnect.Orders; namespace QuantConnect.Algorithm.Framework.Portfolio { /// /// Provides a collection for managing s for each symbol /// public class PortfolioTargetCollection : ICollection, IDictionary { private readonly ConcurrentDictionary _targets = new ConcurrentDictionary(); /// /// Gets the number of targets in this collection /// public int Count => _targets.Count; /// /// Gets `false`. This collection is not read-only. /// public bool IsReadOnly => false; /// /// Gets the symbol keys for this collection /// public ICollection Keys => _targets.Keys; /// /// Gets all portfolio targets in this collection /// Careful, will return targets for securities that might have no data yet. /// public ICollection Values => _targets.Values; /// /// 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; } _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) { WithDictionary(d => d.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) { WithDictionary(d => d.Add(symbol, 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) { 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) { foreach (var item in targets) { _targets[item.Symbol] = item; } } /// /// Removes all portfolio targets from this collection /// public void Clear() { _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 _targets) { var security = algorithm.Securities[target.Key]; var holdings = security.Holdings.Quantity; // check to see if we're done with this target if (Math.Abs(target.Value.Quantity - holdings) < security.SymbolProperties.LotSize) { Remove(target.Key); } } } /// /// 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; } 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 WithDictionary(d => d.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) { 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) { _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) { return WithDictionary(d => d.Remove(symbol)); } /// /// 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 WithDictionary(d => d.Remove(target)); } /// /// 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; } 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) { return _targets.TryGetValue(symbol, out target); } /// /// Gets or sets the portfolio target for the specified symbol /// /// The symbol /// The symbol's portolio target if it exists in this collection, if not a will be thrown. public IPortfolioTarget this[Symbol symbol] { get { return _targets[symbol]; } set { _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() { return _targets.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() { return _targets.Select(kvp => kvp.Value).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) { action(_targets); } /// /// Helper function to easily access explicitly implemented interface methods against concurrent dictionary /// private T WithDictionary(Func, T> func) { return func(_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) { return _targets .Select(x => x.Value) .Where(x => { var security = algorithm.Securities[x.Symbol]; return security.HasData && Math.Abs(OrderSizing.GetUnorderedQuantity(algorithm, x)) >= security.SymbolProperties.LotSize; }) .Select(x => new { PortfolioTarget = x, TargetQuantity = x.Quantity, ExistingQuantity = algorithm.Portfolio[x.Symbol].Quantity + algorithm.Transactions.GetOpenOrderTickets(x.Symbol) .Aggregate(0m, (d, t) => d + t.Quantity - t.QuantityFilled), Price = algorithm.Securities[x.Symbol].Price }) .Select(x => new { PortfolioTarget = x.PortfolioTarget, OrderValue = Math.Abs((x.TargetQuantity - x.ExistingQuantity) * x.Price), IsReducingPosition = x.ExistingQuantity != 0 && Math.Abs(x.TargetQuantity) < Math.Abs(x.ExistingQuantity) }) .OrderByDescending(x => x.IsReducingPosition) .ThenByDescending(x => x.OrderValue) .Select(x => x.PortfolioTarget); } } }