Lean  $LEAN_TAG$
PortfolioTargetCollection.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14 */
15 
16 using System;
17 using System.Linq;
18 using System.Collections;
20 using System.Collections.Generic;
21 
23 {
24  /// <summary>
25  /// Provides a collection for managing <see cref="IPortfolioTarget"/>s for each symbol
26  /// </summary>
27  public class PortfolioTargetCollection : ICollection<IPortfolioTarget>, IDictionary<Symbol, IPortfolioTarget>
28  {
29  private List<IPortfolioTarget> _enumerable;
30  private List<KeyValuePair<Symbol, IPortfolioTarget>> _kvpEnumerable;
31  private readonly Dictionary<Symbol, IPortfolioTarget> _targets = new ();
32 
33  /// <summary>
34  /// Gets the number of targets in this collection
35  /// </summary>
36  public int Count
37  {
38  get
39  {
40  lock (_targets)
41  {
42  return _targets.Count;
43  }
44  }
45  }
46 
47  /// <summary>
48  /// True if there is no target in the collection
49  /// </summary>
50  public bool IsEmpty
51  {
52  get
53  {
54  lock (_targets)
55  {
56  return _targets.Count == 0;
57  }
58  }
59  }
60 
61  /// <summary>
62  /// Gets `false`. This collection is not read-only.
63  /// </summary>
64  public bool IsReadOnly => false;
65 
66  /// <summary>
67  /// Gets the symbol keys for this collection
68  /// </summary>
69  public ICollection<Symbol> Keys
70  {
71  get
72  {
73  lock (_targets)
74  {
75  return _targets.Keys.ToList();
76  }
77  }
78  }
79 
80  /// <summary>
81  /// Gets all portfolio targets in this collection
82  /// Careful, will return targets for securities that might have no data yet.
83  /// </summary>
84  public ICollection<IPortfolioTarget> Values
85  {
86  get
87  {
88  var result = _enumerable;
89  if (result == null)
90  {
91  lock (_targets)
92  {
93  result = _enumerable = _targets.Values.ToList();
94  }
95  }
96  return result;
97  }
98  }
99 
100  /// <summary>
101  /// Adds the specified target to the collection. If a target for the same symbol
102  /// already exists it wil be overwritten.
103  /// </summary>
104  /// <param name="target">The portfolio target to add</param>
105  public void Add(IPortfolioTarget target)
106  {
107  if (target == null)
108  {
109  return;
110  }
111 
112  lock (_targets)
113  {
114  _enumerable = null;
115  _kvpEnumerable = null;
116  _targets[target.Symbol] = target;
117  }
118  }
119 
120  /// <summary>
121  /// Adds the specified target to the collection. If a target for the same symbol
122  /// already exists it wil be overwritten.
123  /// </summary>
124  /// <param name="target">The portfolio target to add</param>
125  public void Add(KeyValuePair<Symbol, IPortfolioTarget> target)
126  {
127  Add(target);
128  }
129 
130  /// <summary>
131  /// Adds the specified target to the collection. If a target for the same symbol
132  /// already exists it wil be overwritten.
133  /// </summary>
134  /// <param name="symbol">The symbol key</param>
135  /// <param name="target">The portfolio target to add</param>
136  public void Add(Symbol symbol, IPortfolioTarget target)
137  {
138  Add(target);
139  }
140 
141  /// <summary>
142  /// Adds the specified targets to the collection. If a target for the same symbol
143  /// already exists it will be overwritten.
144  /// </summary>
145  /// <param name="targets">The portfolio targets to add</param>
146  public void AddRange(IEnumerable<IPortfolioTarget> targets)
147  {
148  lock (_targets)
149  {
150  _enumerable = null;
151  _kvpEnumerable = null;
152  foreach (var item in targets)
153  {
154  _targets[item.Symbol] = item;
155  }
156  }
157  }
158 
159  /// <summary>
160  /// Adds the specified targets to the collection. If a target for the same symbol
161  /// already exists it will be overwritten.
162  /// </summary>
163  /// <param name="targets">The portfolio targets to add</param>
164  public void AddRange(IPortfolioTarget[] targets)
165  {
166  AddRange((IEnumerable<IPortfolioTarget>)targets);
167  }
168 
169  /// <summary>
170  /// Removes all portfolio targets from this collection
171  /// </summary>
172  public void Clear()
173  {
174  lock (_targets)
175  {
176  _enumerable = null;
177  _kvpEnumerable = null;
178  _targets.Clear();
179  }
180  }
181 
182  /// <summary>
183  /// Removes fulfilled portfolio targets from this collection.
184  /// Will only take into account actual holdings and ignore open orders.
185  /// </summary>
186  public void ClearFulfilled(IAlgorithm algorithm)
187  {
188  foreach (var target in this)
189  {
190  var security = algorithm.Securities[target.Symbol];
191  var holdings = security.Holdings.Quantity;
192  // check to see if we're done with this target
193  if (Math.Abs(target.Quantity - holdings) < security.SymbolProperties.LotSize)
194  {
195  Remove(target.Symbol);
196  }
197  }
198  }
199 
200  /// <summary>
201  /// Determines whether or not the specified target exists in this collection.
202  /// NOTE: This checks for the exact specified target, not by symbol. Use ContainsKey
203  /// to check by symbol.
204  /// </summary>
205  /// <param name="target">The portfolio target to check for existence.</param>
206  /// <returns>True if the target exists, false otherwise</returns>
207  public bool Contains(IPortfolioTarget target)
208  {
209  if (target == null)
210  {
211  return false;
212  }
213 
214  lock (_targets)
215  {
216  return _targets.ContainsKey(target.Symbol);
217  }
218  }
219 
220  /// <summary>
221  /// Determines whether the specified symbol/target pair exists in this collection
222  /// </summary>
223  /// <param name="target">The symbol/target pair</param>
224  /// <returns>True if the pair exists, false otherwise</returns>
225  public bool Contains(KeyValuePair<Symbol, IPortfolioTarget> target)
226  {
227  return Contains(target);
228  }
229 
230  /// <summary>
231  /// Determines whether the specified symbol exists as a key in this collection
232  /// </summary>
233  /// <param name="symbol">The symbol key</param>
234  /// <returns>True if the symbol exists in this collection, false otherwise</returns>
235  public bool ContainsKey(Symbol symbol)
236  {
237  lock (_targets)
238  {
239  return _targets.ContainsKey(symbol);
240  }
241  }
242 
243  /// <summary>
244  /// Copies the targets in this collection to the specified array
245  /// </summary>
246  /// <param name="array">The destination array to copy to</param>
247  /// <param name="arrayIndex">The index in the array to start copying to</param>
248  public void CopyTo(IPortfolioTarget[] array, int arrayIndex)
249  {
250  lock (_targets)
251  {
252  _targets.Values.CopyTo(array, arrayIndex);
253  }
254  }
255 
256  /// <summary>
257  /// Copies the targets in this collection to the specified array
258  /// </summary>
259  /// <param name="array">The destination array to copy to</param>
260  /// <param name="arrayIndex">The index in the array to start copying to</param>
261  public void CopyTo(KeyValuePair<Symbol, IPortfolioTarget>[] array, int arrayIndex)
262  {
263  WithDictionary(d => d.CopyTo(array, arrayIndex));
264  }
265 
266  /// <summary>
267  /// Removes the target for the specified symbol if it exists in this collection.
268  /// </summary>
269  /// <param name="symbol">The symbol to remove</param>
270  /// <returns>True if the symbol's target was removed, false if it doesn't exist in the collection</returns>
271  public bool Remove(Symbol symbol)
272  {
273  lock (_targets)
274  {
275  if (_targets.Remove(symbol))
276  {
277  _enumerable = null;
278  _kvpEnumerable = null;
279  return true;
280  }
281  return false;
282  }
283  }
284 
285  /// <summary>
286  /// Removes the target for the specified symbol/target pair if it exists in this collection.
287  /// </summary>
288  /// <param name="target">The symbol/target pair to remove</param>
289  /// <returns>True if the symbol's target was removed, false if it doesn't exist in the collection</returns>
290  public bool Remove(KeyValuePair<Symbol, IPortfolioTarget> target)
291  {
292  return Remove(target.Value);
293  }
294 
295  /// <summary>
296  /// Removes the target if it exists in this collection.
297  /// </summary>
298  /// <param name="target">The target to remove</param>
299  /// <returns>True if the target was removed, false if it doesn't exist in the collection</returns>
300  public bool Remove(IPortfolioTarget target)
301  {
302  if (target == null)
303  {
304  return false;
305  }
306 
307  lock (_targets)
308  {
309  IPortfolioTarget existing;
310  if (_targets.TryGetValue(target.Symbol, out existing))
311  {
312  // need to confirm that we're removing the requested target and not a different target w/ the same symbol key
313  if (existing.Equals(target))
314  {
315  return Remove(target.Symbol);
316  }
317  }
318  }
319 
320  return false;
321  }
322 
323  /// <summary>
324  /// Attempts to retrieve the target for the specified symbol
325  /// </summary>
326  /// <param name="symbol">The symbol</param>
327  /// <param name="target">The portfolio target for the symbol, or null if not found</param>
328  /// <returns>True if the symbol's target was found, false if it does not exist in this collection</returns>
329  public bool TryGetValue(Symbol symbol, out IPortfolioTarget target)
330  {
331  lock (_targets)
332  {
333  return _targets.TryGetValue(symbol, out target);
334  }
335  }
336 
337  /// <summary>
338  /// Gets or sets the portfolio target for the specified symbol
339  /// </summary>
340  /// <param name="symbol">The symbol</param>
341  /// <returns>The symbol's portfolio target if it exists in this collection, if not a <see cref="KeyNotFoundException"/> will be thrown.</returns>
342  public IPortfolioTarget this[Symbol symbol]
343  {
344  get
345  {
346  lock (_targets)
347  {
348  return _targets[symbol];
349  }
350  }
351  set
352  {
353  lock (_targets)
354  {
355  _enumerable = null;
356  _kvpEnumerable = null;
357  _targets[symbol] = value;
358  }
359  }
360  }
361 
362  /// <summary>
363  /// Gets an enumerator to iterator over the symbol/target key value pairs in this collection.
364  /// </summary>
365  /// <returns>Symbol/target key value pair enumerator</returns>
366  IEnumerator<KeyValuePair<Symbol, IPortfolioTarget>> IEnumerable<KeyValuePair<Symbol, IPortfolioTarget>>.GetEnumerator()
367  {
368  var result = _kvpEnumerable;
369  if (result == null)
370  {
371  lock (_targets)
372  {
373  _kvpEnumerable = result = _targets.ToList();
374  }
375  }
376  return result.GetEnumerator();
377  }
378 
379  /// <summary>
380  /// Gets an enumerator to iterator over all portfolio targets in this collection.
381  /// This is the default enumerator for this collection.
382  /// </summary>
383  /// <returns>Portfolio targets enumerator</returns>
384  public IEnumerator<IPortfolioTarget> GetEnumerator()
385  {
386  var result = _enumerable;
387  if (result == null)
388  {
389  lock (_targets)
390  {
391  _enumerable = result = _targets.Values.ToList();
392  }
393  }
394  return result.GetEnumerator();
395  }
396 
397  /// <summary>
398  /// Gets an enumerator to iterator over all portfolio targets in this collection.
399  /// This is the default enumerator for this collection.
400  /// Careful, will return targets for securities that might have no data yet.
401  /// </summary>
402  /// <returns>Portfolio targets enumerator</returns>
403  IEnumerator IEnumerable.GetEnumerator()
404  {
405  return GetEnumerator();
406  }
407 
408  /// <summary>
409  /// Helper function to easily access explicitly implemented interface methods against concurrent dictionary
410  /// </summary>
411  private void WithDictionary(Action<IDictionary<Symbol, IPortfolioTarget>> action)
412  {
413  lock (_targets)
414  {
415  action(_targets);
416  }
417  }
418 
419  /// <summary>
420  /// Returned an ordered enumerable where position reducing orders are executed first
421  /// and the remaining orders are executed in decreasing order value.
422  /// Will NOT return targets for securities that have no data yet.
423  /// Will NOT return targets for which current holdings + open orders quantity, sum up to the target quantity
424  /// </summary>
425  /// <param name="algorithm">The algorithm instance</param>
426  public IEnumerable<IPortfolioTarget> OrderByMarginImpact(IAlgorithm algorithm)
427  {
428  if (IsEmpty)
429  {
430  return Enumerable.Empty<IPortfolioTarget>();
431  }
432  return this.OrderTargetsByMarginImpact(algorithm);
433  }
434  }
435 }