From 00a8429e69a20fe125755025b417b371a249cdbf Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Thu, 24 Apr 2025 18:28:04 -0400 Subject: [PATCH 01/15] Make ExtendedDictionary extend IDictionary --- Common/Data/Market/DataDictionary.cs | 4 +- Common/Data/Slice.cs | 4 +- Common/ExtendedDictionary.cs | 62 +++++++++++++++++++ Common/Interfaces/IExtendedDictionary.cs | 7 ++- Common/Securities/SecurityManager.cs | 4 +- Common/Securities/SecurityPortfolioManager.cs | 4 +- 6 files changed, 74 insertions(+), 11 deletions(-) diff --git a/Common/Data/Market/DataDictionary.cs b/Common/Data/Market/DataDictionary.cs index 22b4c1e4404b..0611e55610e3 100644 --- a/Common/Data/Market/DataDictionary.cs +++ b/Common/Data/Market/DataDictionary.cs @@ -146,7 +146,7 @@ public bool Remove(KeyValuePair item) /// /// The number of elements contained in the . /// - public int Count + public override int Count { get { return _data.Count; } } @@ -169,7 +169,7 @@ public override bool IsReadOnly /// true if the contains an element with the key; otherwise, false. /// /// The key to locate in the . is null. - public bool ContainsKey(Symbol key) + public override bool ContainsKey(Symbol key) { return _data.ContainsKey(key); } diff --git a/Common/Data/Slice.cs b/Common/Data/Slice.cs index a0c696bada05..b7e7f1368190 100644 --- a/Common/Data/Slice.cs +++ b/Common/Data/Slice.cs @@ -171,7 +171,7 @@ public MarginInterestRates MarginInterestRates /// /// Gets the number of symbols held in this slice /// - public virtual int Count + public override int Count { get { return _data.Value.Count; } } @@ -511,7 +511,7 @@ public T Get(Symbol symbol) /// /// The symbol we seek data for /// True if this instance contains data for the symbol, false otherwise - public virtual bool ContainsKey(Symbol symbol) + public override bool ContainsKey(Symbol symbol) { return _data.Value.ContainsKey(symbol); } diff --git a/Common/ExtendedDictionary.cs b/Common/ExtendedDictionary.cs index c0d3d1e4f969..2ba3e25d15f5 100644 --- a/Common/ExtendedDictionary.cs +++ b/Common/ExtendedDictionary.cs @@ -73,6 +73,23 @@ public virtual void Clear() /// IDictionary implementation public virtual bool IsReadOnly => true; + /// + /// Gets the number of elements contained in the . + /// + public abstract int Count { get; } + + bool IDictionary.IsFixedSize => false; + + bool ICollection.IsSynchronized => false; + + object ICollection.SyncRoot => this; + + ICollection IDictionary.Keys => GetKeys.ToList(); + + ICollection IDictionary.Values => GetValues.ToList(); + + object IDictionary.this[object key] { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + /// /// Removes the value with the specified Symbol /// @@ -349,5 +366,50 @@ public PyList values() { return GetValues.ToPyList(); } + + /// + /// Checks if the dictionary contains the specified Symbol. + /// + /// The symbol to look up + /// Whether the dictionary contains the specified symbol key + public abstract bool ContainsKey(Symbol key); + + void IDictionary.Add(object key, object value) + { + throw new NotImplementedException(); + } + + bool IDictionary.Contains(object key) + { + if (key is Symbol symbol) + { + return ContainsKey(symbol); + } + if (key is string ticker) + { + return SymbolCache.TryGetSymbol(ticker, out symbol) && ContainsKey(symbol); + } + return false; + } + + IDictionaryEnumerator IDictionary.GetEnumerator() + { + throw new NotImplementedException(); + } + + void IDictionary.Remove(object key) + { + throw new NotImplementedException(); + } + + void ICollection.CopyTo(Array array, int index) + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } } } diff --git a/Common/Interfaces/IExtendedDictionary.cs b/Common/Interfaces/IExtendedDictionary.cs index 72f2e9e69a42..92c953dda395 100644 --- a/Common/Interfaces/IExtendedDictionary.cs +++ b/Common/Interfaces/IExtendedDictionary.cs @@ -1,4 +1,4 @@ -/* +/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * @@ -14,13 +14,14 @@ */ using Python.Runtime; +using System.Collections; namespace QuantConnect.Interfaces { /// /// Represents a generic collection of key/value pairs that implements python dictionary methods. /// - public interface IExtendedDictionary + public interface IExtendedDictionary : IDictionary { /// /// Removes all keys and values from the . @@ -134,4 +135,4 @@ public interface IExtendedDictionary /// Returns a view object that displays a list of all values in a given dictionary. PyList values(); } -} \ No newline at end of file +} diff --git a/Common/Securities/SecurityManager.cs b/Common/Securities/SecurityManager.cs index 13ad9b6cc519..e9705585e1d2 100644 --- a/Common/Securities/SecurityManager.cs +++ b/Common/Securities/SecurityManager.cs @@ -145,7 +145,7 @@ public bool Contains(KeyValuePair pair) /// Symbol we're checking for. /// IDictionary implementation /// Bool true if contains this symbol pair - public bool ContainsKey(Symbol symbol) + public override bool ContainsKey(Symbol symbol) { lock (_securityManager) { @@ -171,7 +171,7 @@ public void CopyTo(KeyValuePair[] array, int number) /// Count of the number of securities in the collection. /// /// IDictionary implementation - public int Count + public override int Count { get { diff --git a/Common/Securities/SecurityPortfolioManager.cs b/Common/Securities/SecurityPortfolioManager.cs index e0873175d639..86f6048030b9 100644 --- a/Common/Securities/SecurityPortfolioManager.cs +++ b/Common/Securities/SecurityPortfolioManager.cs @@ -169,7 +169,7 @@ public SecurityPortfolioManager(SecurityManager securityManager, SecurityTransac /// /// String search symbol for the security /// Boolean true if portfolio contains this symbol - public bool ContainsKey(Symbol symbol) + public override bool ContainsKey(Symbol symbol) { return Securities.ContainsKey(symbol); } @@ -189,7 +189,7 @@ public bool Contains(KeyValuePair pair) /// Count the securities objects in the portfolio. /// /// IDictionary implementation calling the underlying Securities collection - public int Count + public override int Count { get { From 2230a3acf82638b0b01657cc2d7916ff6b4d494a Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Mon, 28 Apr 2025 09:47:11 -0400 Subject: [PATCH 02/15] Revert "Make ExtendedDictionary extend IDictionary" This reverts commit bbbe9c769be682767cb9e487b904480376b53596. --- Common/Data/Market/DataDictionary.cs | 4 +- Common/Data/Slice.cs | 4 +- Common/ExtendedDictionary.cs | 62 ------------------- Common/Interfaces/IExtendedDictionary.cs | 7 +-- Common/Securities/SecurityManager.cs | 4 +- Common/Securities/SecurityPortfolioManager.cs | 4 +- 6 files changed, 11 insertions(+), 74 deletions(-) diff --git a/Common/Data/Market/DataDictionary.cs b/Common/Data/Market/DataDictionary.cs index 0611e55610e3..22b4c1e4404b 100644 --- a/Common/Data/Market/DataDictionary.cs +++ b/Common/Data/Market/DataDictionary.cs @@ -146,7 +146,7 @@ public bool Remove(KeyValuePair item) /// /// The number of elements contained in the . /// - public override int Count + public int Count { get { return _data.Count; } } @@ -169,7 +169,7 @@ public override bool IsReadOnly /// true if the contains an element with the key; otherwise, false. /// /// The key to locate in the . is null. - public override bool ContainsKey(Symbol key) + public bool ContainsKey(Symbol key) { return _data.ContainsKey(key); } diff --git a/Common/Data/Slice.cs b/Common/Data/Slice.cs index b7e7f1368190..a0c696bada05 100644 --- a/Common/Data/Slice.cs +++ b/Common/Data/Slice.cs @@ -171,7 +171,7 @@ public MarginInterestRates MarginInterestRates /// /// Gets the number of symbols held in this slice /// - public override int Count + public virtual int Count { get { return _data.Value.Count; } } @@ -511,7 +511,7 @@ public T Get(Symbol symbol) /// /// The symbol we seek data for /// True if this instance contains data for the symbol, false otherwise - public override bool ContainsKey(Symbol symbol) + public virtual bool ContainsKey(Symbol symbol) { return _data.Value.ContainsKey(symbol); } diff --git a/Common/ExtendedDictionary.cs b/Common/ExtendedDictionary.cs index 2ba3e25d15f5..c0d3d1e4f969 100644 --- a/Common/ExtendedDictionary.cs +++ b/Common/ExtendedDictionary.cs @@ -73,23 +73,6 @@ public virtual void Clear() /// IDictionary implementation public virtual bool IsReadOnly => true; - /// - /// Gets the number of elements contained in the . - /// - public abstract int Count { get; } - - bool IDictionary.IsFixedSize => false; - - bool ICollection.IsSynchronized => false; - - object ICollection.SyncRoot => this; - - ICollection IDictionary.Keys => GetKeys.ToList(); - - ICollection IDictionary.Values => GetValues.ToList(); - - object IDictionary.this[object key] { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - /// /// Removes the value with the specified Symbol /// @@ -366,50 +349,5 @@ public PyList values() { return GetValues.ToPyList(); } - - /// - /// Checks if the dictionary contains the specified Symbol. - /// - /// The symbol to look up - /// Whether the dictionary contains the specified symbol key - public abstract bool ContainsKey(Symbol key); - - void IDictionary.Add(object key, object value) - { - throw new NotImplementedException(); - } - - bool IDictionary.Contains(object key) - { - if (key is Symbol symbol) - { - return ContainsKey(symbol); - } - if (key is string ticker) - { - return SymbolCache.TryGetSymbol(ticker, out symbol) && ContainsKey(symbol); - } - return false; - } - - IDictionaryEnumerator IDictionary.GetEnumerator() - { - throw new NotImplementedException(); - } - - void IDictionary.Remove(object key) - { - throw new NotImplementedException(); - } - - void ICollection.CopyTo(Array array, int index) - { - throw new NotImplementedException(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - throw new NotImplementedException(); - } } } diff --git a/Common/Interfaces/IExtendedDictionary.cs b/Common/Interfaces/IExtendedDictionary.cs index 92c953dda395..72f2e9e69a42 100644 --- a/Common/Interfaces/IExtendedDictionary.cs +++ b/Common/Interfaces/IExtendedDictionary.cs @@ -1,4 +1,4 @@ -/* +/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * @@ -14,14 +14,13 @@ */ using Python.Runtime; -using System.Collections; namespace QuantConnect.Interfaces { /// /// Represents a generic collection of key/value pairs that implements python dictionary methods. /// - public interface IExtendedDictionary : IDictionary + public interface IExtendedDictionary { /// /// Removes all keys and values from the . @@ -135,4 +134,4 @@ public interface IExtendedDictionary : IDictionary /// Returns a view object that displays a list of all values in a given dictionary. PyList values(); } -} +} \ No newline at end of file diff --git a/Common/Securities/SecurityManager.cs b/Common/Securities/SecurityManager.cs index e9705585e1d2..13ad9b6cc519 100644 --- a/Common/Securities/SecurityManager.cs +++ b/Common/Securities/SecurityManager.cs @@ -145,7 +145,7 @@ public bool Contains(KeyValuePair pair) /// Symbol we're checking for. /// IDictionary implementation /// Bool true if contains this symbol pair - public override bool ContainsKey(Symbol symbol) + public bool ContainsKey(Symbol symbol) { lock (_securityManager) { @@ -171,7 +171,7 @@ public void CopyTo(KeyValuePair[] array, int number) /// Count of the number of securities in the collection. /// /// IDictionary implementation - public override int Count + public int Count { get { diff --git a/Common/Securities/SecurityPortfolioManager.cs b/Common/Securities/SecurityPortfolioManager.cs index 86f6048030b9..e0873175d639 100644 --- a/Common/Securities/SecurityPortfolioManager.cs +++ b/Common/Securities/SecurityPortfolioManager.cs @@ -169,7 +169,7 @@ public SecurityPortfolioManager(SecurityManager securityManager, SecurityTransac /// /// String search symbol for the security /// Boolean true if portfolio contains this symbol - public override bool ContainsKey(Symbol symbol) + public bool ContainsKey(Symbol symbol) { return Securities.ContainsKey(symbol); } @@ -189,7 +189,7 @@ public bool Contains(KeyValuePair pair) /// Count the securities objects in the portfolio. /// /// IDictionary implementation calling the underlying Securities collection - public override int Count + public int Count { get { From 4e1d4c9c259cfb7549cc14349e2d75a5040e2192 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Mon, 28 Apr 2025 15:54:08 -0400 Subject: [PATCH 03/15] Make ExtendedDictionary key a type parameter --- .gitignore | 3 +- Common/Data/Market/DataDictionary.cs | 2 +- Common/Data/Market/Dividends.cs | 22 +- Common/Data/Market/FuturesContracts.cs | 22 +- Common/Data/Market/MarginInterestRates.cs | 20 -- Common/Data/Market/OptionContracts.cs | 22 +- Common/Data/Market/QuoteBars.cs | 22 +- Common/Data/Market/Splits.cs | 24 +-- Common/Data/Market/SymbolChangedEvents.cs | 22 +- Common/Data/Market/Ticks.cs | 22 +- Common/Data/Market/TradeBars.cs | 24 +-- Common/Data/Slice.cs | 8 +- Common/ExtendedDictionary.cs | 197 ++++++++---------- Common/Messages/Messages.QuantConnect.cs | 14 +- Common/Securities/SecurityManager.cs | 2 +- Common/Securities/SecurityPortfolioManager.cs | 2 +- 16 files changed, 113 insertions(+), 315 deletions(-) diff --git a/.gitignore b/.gitignore index 3a3f6e097b81..8f908a5b42ec 100644 --- a/.gitignore +++ b/.gitignore @@ -279,4 +279,5 @@ QuantConnect.Lean.sln.DotSettings* Research/Notebooks #Docker result files -Results/ \ No newline at end of file +Results/ +/myvenv diff --git a/Common/Data/Market/DataDictionary.cs b/Common/Data/Market/DataDictionary.cs index 22b4c1e4404b..2ba7f7008b74 100644 --- a/Common/Data/Market/DataDictionary.cs +++ b/Common/Data/Market/DataDictionary.cs @@ -24,7 +24,7 @@ namespace QuantConnect.Data.Market /// Provides a base class for types holding base data instances keyed by symbol /// [PandasNonExpandable] - public class DataDictionary : ExtendedDictionary, IDictionary + public class DataDictionary : ExtendedDictionary, IDictionary { // storage for the data private readonly IDictionary _data = new Dictionary(); diff --git a/Common/Data/Market/Dividends.cs b/Common/Data/Market/Dividends.cs index cf5906cefb30..b5ba9b3a8fad 100644 --- a/Common/Data/Market/Dividends.cs +++ b/Common/Data/Market/Dividends.cs @@ -1,4 +1,4 @@ -/* +/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * @@ -38,25 +38,5 @@ public Dividends(DateTime frontier) : base(frontier) { } - - /// - /// Gets or sets the Dividend with the specified ticker. - /// - /// - /// The Dividend with the specified ticker. - /// - /// The ticker of the element to get or set. - /// Wraps the base implementation to enable indexing in python algorithms due to pythonnet limitations - public new Dividend this[string ticker] { get { return base[ticker]; } set { base[ticker] = value; } } - - /// - /// Gets or sets the Dividend with the specified Symbol. - /// - /// - /// The Dividend with the specified Symbol. - /// - /// The Symbol of the element to get or set. - /// Wraps the base implementation to enable indexing in python algorithms due to pythonnet limitations - public new Dividend this[Symbol symbol] { get { return base[symbol]; } set { base[symbol] = value; } } } } diff --git a/Common/Data/Market/FuturesContracts.cs b/Common/Data/Market/FuturesContracts.cs index ba4ca9f459b5..c0d0f38a23fc 100644 --- a/Common/Data/Market/FuturesContracts.cs +++ b/Common/Data/Market/FuturesContracts.cs @@ -36,25 +36,5 @@ public FuturesContracts(DateTime time) : base(time) { } - - /// - /// Gets or sets the FuturesContract with the specified ticker. - /// - /// - /// The FuturesContract with the specified ticker. - /// - /// The ticker of the element to get or set. - /// Wraps the base implementation to enable indexing in python algorithms due to pythonnet limitations - public new FuturesContract this[string ticker] { get { return base[ticker]; } set { base[ticker] = value; } } - - /// - /// Gets or sets the FuturesContract with the specified Symbol. - /// - /// - /// The FuturesContract with the specified Symbol. - /// - /// The Symbol of the element to get or set. - /// Wraps the base implementation to enable indexing in python algorithms due to pythonnet limitations - public new FuturesContract this[Symbol symbol] { get { return base[symbol]; } set { base[symbol] = value; } } } -} \ No newline at end of file +} diff --git a/Common/Data/Market/MarginInterestRates.cs b/Common/Data/Market/MarginInterestRates.cs index 271cfc2a00b4..58b7fba67d0c 100644 --- a/Common/Data/Market/MarginInterestRates.cs +++ b/Common/Data/Market/MarginInterestRates.cs @@ -38,25 +38,5 @@ public MarginInterestRates(DateTime frontier) : base(frontier) { } - - /// - /// Gets or sets the Dividend with the specified ticker. - /// - /// - /// The Dividend with the specified ticker. - /// - /// The ticker of the element to get or set. - /// Wraps the base implementation to enable indexing in python algorithms due to pythonnet limitations - public new MarginInterestRate this[string ticker] { get { return base[ticker]; } set { base[ticker] = value; } } - - /// - /// Gets or sets the Dividend with the specified Symbol. - /// - /// - /// The Dividend with the specified Symbol. - /// - /// The Symbol of the element to get or set. - /// Wraps the base implementation to enable indexing in python algorithms due to pythonnet limitations - public new MarginInterestRate this[Symbol symbol] { get { return base[symbol]; } set { base[symbol] = value; } } } } diff --git a/Common/Data/Market/OptionContracts.cs b/Common/Data/Market/OptionContracts.cs index 7be31c12dcf9..6cd6b9c678c9 100644 --- a/Common/Data/Market/OptionContracts.cs +++ b/Common/Data/Market/OptionContracts.cs @@ -36,25 +36,5 @@ public OptionContracts(DateTime time) : base(time) { } - - /// - /// Gets or sets the OptionContract with the specified ticker. - /// - /// - /// The OptionContract with the specified ticker. - /// - /// The ticker of the element to get or set. - /// Wraps the base implementation to enable indexing in python algorithms due to pythonnet limitations - public new OptionContract this[string ticker] { get { return base[ticker]; } set { base[ticker] = value; } } - - /// - /// Gets or sets the OptionContract with the specified Symbol. - /// - /// - /// The OptionContract with the specified Symbol. - /// - /// The Symbol of the element to get or set. - /// Wraps the base implementation to enable indexing in python algorithms due to pythonnet limitations - public new OptionContract this[Symbol symbol] { get { return base[symbol]; } set { base[symbol] = value; } } } -} \ No newline at end of file +} diff --git a/Common/Data/Market/QuoteBars.cs b/Common/Data/Market/QuoteBars.cs index bcdea1520a06..5bcacd5418dd 100644 --- a/Common/Data/Market/QuoteBars.cs +++ b/Common/Data/Market/QuoteBars.cs @@ -36,25 +36,5 @@ public QuoteBars(DateTime time) : base(time) { } - - /// - /// Gets or sets the QuoteBar with the specified ticker. - /// - /// - /// The QuoteBar with the specified ticker. - /// - /// The ticker of the element to get or set. - /// Wraps the base implementation to enable indexing in python algorithms due to pythonnet limitations - public new QuoteBar this[string ticker] { get { return base[ticker]; } set { base[ticker] = value; } } - - /// - /// Gets or sets the QuoteBar with the specified Symbol. - /// - /// - /// The QuoteBar with the specified Symbol. - /// - /// The Symbol of the element to get or set. - /// Wraps the base implementation to enable indexing in python algorithms due to pythonnet limitations - public new QuoteBar this[Symbol symbol] { get { return base[symbol]; } set { base[symbol] = value; } } } -} \ No newline at end of file +} diff --git a/Common/Data/Market/Splits.cs b/Common/Data/Market/Splits.cs index 47bd2741369d..a952886ced72 100644 --- a/Common/Data/Market/Splits.cs +++ b/Common/Data/Market/Splits.cs @@ -1,4 +1,4 @@ -/* +/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * @@ -38,25 +38,5 @@ public Splits(DateTime frontier) : base(frontier) { } - - /// - /// Gets or sets the Split with the specified ticker. - /// - /// - /// The Split with the specified ticker. - /// - /// The ticker of the element to get or set. - /// Wraps the base implementation to enable indexing in python algorithms due to pythonnet limitations - public new Split this[string ticker] { get { return base[ticker]; } set { base[ticker] = value; } } - - /// - /// Gets or sets the Split with the specified Symbol. - /// - /// - /// The Split with the specified Symbol. - /// - /// The Symbol of the element to get or set. - /// Wraps the base implementation to enable indexing in python algorithms due to pythonnet limitations - public new Split this[Symbol symbol] { get { return base[symbol]; } set { base[symbol] = value; } } } -} \ No newline at end of file +} diff --git a/Common/Data/Market/SymbolChangedEvents.cs b/Common/Data/Market/SymbolChangedEvents.cs index 018ef9a55ff6..d15eb63f9cf6 100644 --- a/Common/Data/Market/SymbolChangedEvents.cs +++ b/Common/Data/Market/SymbolChangedEvents.cs @@ -1,4 +1,4 @@ -/* +/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * @@ -38,25 +38,5 @@ public SymbolChangedEvents(DateTime frontier) : base(frontier) { } - - /// - /// Gets or sets the SymbolChangedEvent with the specified ticker. - /// - /// - /// The SymbolChangedEvent with the specified ticker. - /// - /// The ticker of the element to get or set. - /// Wraps the base implementation to enable indexing in python algorithms due to pythonnet limitations - public new SymbolChangedEvent this[string ticker] { get { return base[ticker]; } set { base[ticker] = value; } } - - /// - /// Gets or sets the SymbolChangedEvent with the specified Symbol. - /// - /// - /// The SymbolChangedEvent with the specified Symbol. - /// - /// The Symbol of the element to get or set. - /// Wraps the base implementation to enable indexing in python algorithms due to pythonnet limitations - public new SymbolChangedEvent this[Symbol symbol] { get { return base[symbol]; } set { base[symbol] = value; } } } } diff --git a/Common/Data/Market/Ticks.cs b/Common/Data/Market/Ticks.cs index 92e53bf71b1e..b47130cd8a67 100644 --- a/Common/Data/Market/Ticks.cs +++ b/Common/Data/Market/Ticks.cs @@ -1,4 +1,4 @@ -/* +/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * @@ -39,25 +39,5 @@ public Ticks(DateTime frontier) : base(frontier) { } - - /// - /// Gets or sets the list of Tick with the specified ticker. - /// - /// - /// The list of Tick with the specified ticker. - /// - /// The ticker of the element to get or set. - /// Wraps the base implementation to enable indexing in python algorithms due to pythonnet limitations - public new List this[string ticker] { get { return base[ticker]; } set { base[ticker] = value; } } - - /// - /// Gets or sets the list of Tick with the specified Symbol. - /// - /// - /// The list of Tick with the specified Symbol. - /// - /// The Symbol of the element to get or set. - /// Wraps the base implementation to enable indexing in python algorithms due to pythonnet limitations - public new List this[Symbol symbol] { get { return base[symbol]; } set { base[symbol] = value; } } } } diff --git a/Common/Data/Market/TradeBars.cs b/Common/Data/Market/TradeBars.cs index cd12164e2667..57b65cd3d9d0 100644 --- a/Common/Data/Market/TradeBars.cs +++ b/Common/Data/Market/TradeBars.cs @@ -1,4 +1,4 @@ -/* +/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * @@ -37,25 +37,5 @@ public TradeBars(DateTime frontier) : base(frontier) { } - - /// - /// Gets or sets the TradeBar with the specified ticker. - /// - /// - /// The TradeBar with the specified ticker. - /// - /// The ticker of the element to get or set. - /// Wraps the base implementation to enable indexing in python algorithms due to pythonnet limitations - public new TradeBar this[string ticker] { get { return base[ticker]; } set { base[ticker] = value; } } - - /// - /// Gets or sets the TradeBar with the specified Symbol. - /// - /// - /// The TradeBar with the specified Symbol. - /// - /// The Symbol of the element to get or set. - /// Wraps the base implementation to enable indexing in python algorithms due to pythonnet limitations - public new TradeBar this[Symbol symbol] { get { return base[symbol]; } set { base[symbol] = value; } } } -} \ No newline at end of file +} diff --git a/Common/Data/Slice.cs b/Common/Data/Slice.cs index a0c696bada05..f63505cbbedf 100644 --- a/Common/Data/Slice.cs +++ b/Common/Data/Slice.cs @@ -30,7 +30,7 @@ namespace QuantConnect.Data /// /// Provides a data structure for all of an algorithm's data at a single time step /// - public class Slice : ExtendedDictionary, IEnumerable> + public class Slice : ExtendedDictionary, IEnumerable> { private Ticks _ticks; private TradeBars _bars; @@ -334,6 +334,12 @@ public override dynamic this[Symbol symbol] } throw new KeyNotFoundException($"'{symbol}' wasn't found in the Slice object, likely because there was no-data at this moment in time and it wasn't possible to fillforward historical data. Please check the data exists before accessing it with data.ContainsKey(\"{symbol}\")"); } + set + { + // this is a no-op, we don't want to allow setting data in the slice + // this is a read-only collection + throw new NotSupportedException("The Slice object is read-only. You cannot set data in the slice."); + } } /// diff --git a/Common/ExtendedDictionary.cs b/Common/ExtendedDictionary.cs index c0d3d1e4f969..691fed7687f7 100644 --- a/Common/ExtendedDictionary.cs +++ b/Common/ExtendedDictionary.cs @@ -24,10 +24,12 @@ namespace QuantConnect { /// - /// Provides a base class for types holding instances keyed by + /// Provides a base class for types holding key value pairs with helper methods for easy usage in Python /// [PandasNonExpandable] - public abstract class ExtendedDictionary : IExtendedDictionary +#pragma warning disable CA1708 // Identifiers should differ by more than case + public abstract class ExtendedDictionary : IExtendedDictionary +#pragma warning restore CA1708 // Identifiers should differ by more than case { /// /// Removes all items from the . @@ -43,21 +45,28 @@ public virtual void Clear() } /// - /// Gets the value associated with the specified Symbol. + /// Gets the value associated with the specified key. /// /// - /// true if the object that implements contains an element with the specified Symbol; otherwise, false. + /// true if the object that implements contains an element with the specified key; otherwise, false. /// - /// The Symbol whose value to get.When this method returns, the value associated with the specified Symbol, if the Symbol is found; otherwise, the default value for the type of the parameter. This parameter is passed uninitialized. is null. - public abstract bool TryGetValue(Symbol symbol, out T value); + /// The key whose value to get. + /// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the parameter. This parameter is passed uninitialized. + /// is null. + public abstract bool TryGetValue(TKey key, out TValue value); + + public virtual bool ContainsKey(TKey key) + { + return TryGetValue(key, out _); + } /// - /// Gets an containing the Symbol objects of the . + /// Gets an containing the key objects of the . /// /// - /// An containing the Symbol objects of the object that implements . + /// A containing the key objects of the object that implements . /// - protected abstract IEnumerable GetKeys { get; } + protected abstract IEnumerable GetKeys { get; } /// /// Gets an containing the values in the . @@ -65,7 +74,7 @@ public virtual void Clear() /// /// An containing the values in the object that implements . /// - protected abstract IEnumerable GetValues { get; } + protected abstract IEnumerable GetValues { get; } /// /// Gets a value indicating whether the object is read-only. @@ -74,11 +83,11 @@ public virtual void Clear() public virtual bool IsReadOnly => true; /// - /// Removes the value with the specified Symbol + /// Removes the value with the specified key /// - /// The Symbol object of the element to remove. + /// The key object of the element to remove. /// true if the element is successfully found and removed; otherwise, false. - public virtual bool Remove(Symbol symbol) + public virtual bool Remove(TKey key) { if (IsReadOnly) { @@ -91,47 +100,9 @@ public virtual bool Remove(Symbol symbol) /// Indexer method for the base dictioanry to access the objects by their symbol. /// /// IDictionary implementation - /// Symbol object indexer - /// Object of - public virtual T this[Symbol symbol] - { - get - { - throw new NotImplementedException(Messages.ExtendedDictionary.IndexerBySymbolNotImplemented); - } - set - { - throw new NotImplementedException(Messages.ExtendedDictionary.IndexerBySymbolNotImplemented); - } - } - - /// - /// Indexer method for the base dictioanry to access the objects by their symbol. - /// - /// IDictionary implementation - /// string ticker symbol indexer - /// Object of - public virtual T this[string ticker] - { - get - { - Symbol symbol; - if (!SymbolCache.TryGetSymbol(ticker, out symbol)) - { - throw new KeyNotFoundException(Messages.ExtendedDictionary.TickerNotFoundInSymbolCache(ticker)); - } - return this[symbol]; - } - set - { - Symbol symbol; - if (!SymbolCache.TryGetSymbol(ticker, out symbol)) - { - throw new KeyNotFoundException(Messages.ExtendedDictionary.TickerNotFoundInSymbolCache(ticker)); - } - this[symbol] = value; - } - } + /// Key object indexer + /// Object of + public abstract TValue this[TKey key] { get; set; } /// /// Removes all keys and values from the . @@ -155,9 +126,9 @@ public PyDict copy() /// /// Sequence of elements which is to be used as keys for the new dictionary /// Returns a new dictionary with the given sequence of elements as the keys of the dictionary. - public PyDict fromkeys(Symbol[] sequence) + public PyDict fromkeys(TKey[] sequence) { - return fromkeys(sequence, default(T)); + return fromkeys(sequence, default); } /// @@ -167,7 +138,7 @@ public PyDict fromkeys(Symbol[] sequence) /// Value which is set to each each element of the dictionary /// Returns a new dictionary with the given sequence of elements as the keys of the dictionary. /// Each element of the newly created dictionary is set to the provided value. - public PyDict fromkeys(Symbol[] sequence, T value) + public PyDict fromkeys(TKey[] sequence, TValue value) { using (Py.GIL()) { @@ -182,29 +153,29 @@ public PyDict fromkeys(Symbol[] sequence, T value) } /// - /// Returns the value for the specified Symbol if Symbol is in dictionary. + /// Returns the value for the specified key if key is in dictionary. /// - /// Symbol to be searched in the dictionary - /// The value for the specified Symbol if Symbol is in dictionary. - /// None if the Symbol is not found and value is not specified. - public T get(Symbol symbol) + /// key to be searched in the dictionary + /// The value for the specified key if key is in dictionary. + /// None if the key is not found and value is not specified. + public TValue get(TKey key) { - T data; - TryGetValue(symbol, out data); + TValue data; + TryGetValue(key, out data); return data; } /// - /// Returns the value for the specified Symbol if Symbol is in dictionary. + /// Returns the value for the specified key if key is in dictionary. /// - /// Symbol to be searched in the dictionary - /// Value to be returned if the Symbol is not found. The default value is null. - /// The value for the specified Symbol if Symbol is in dictionary. - /// value if the Symbol is not found and value is specified. - public T get(Symbol symbol, T value) + /// key to be searched in the dictionary + /// Value to be returned if the key is not found. The default value is null. + /// The value for the specified key if key is in dictionary. + /// value if the key is not found and value is specified. + public TValue get(TKey key, TValue value) { - T data; - if (TryGetValue(symbol, out data)) + TValue data; + if (TryGetValue(key, out data)) { return data; } @@ -212,9 +183,9 @@ public T get(Symbol symbol, T value) } /// - /// Returns a view object that displays a list of dictionary's (Symbol, value) tuple pairs. + /// Returns a view object that displays a list of dictionary's (key, value) tuple pairs. /// - /// Returns a view object that displays a list of a given dictionary's (Symbol, value) tuple pair. + /// Returns a view object that displays a list of a given dictionary's (key, value) tuple pair. public PyList items() { using (Py.GIL()) @@ -226,7 +197,7 @@ public PyList items() { using (var pyValue = this[key].ToPython()) { - using (var pyObject = new PyTuple(new PyObject[] { pyKey, pyValue })) + using (var pyObject = new PyTuple([pyKey, pyValue])) { pyList.Append(pyObject); } @@ -238,9 +209,9 @@ public PyList items() } /// - /// Returns and removes an arbitrary element (Symbol, value) pair from the dictionary. + /// Returns and removes an arbitrary element (key, value) pair from the dictionary. /// - /// Returns an arbitrary element (Symbol, value) pair from the dictionary + /// Returns an arbitrary element (key, value) pair from the dictionary /// removes an arbitrary element(the same element which is returned) from the dictionary. /// Note: Arbitrary elements and random elements are not same.The popitem() doesn't return a random element. public PyTuple popitem() @@ -249,74 +220,74 @@ public PyTuple popitem() } /// - /// Returns the value of a Symbol (if the Symbol is in dictionary). If not, it inserts Symbol with a value to the dictionary. + /// Returns the value of a key (if the key is in dictionary). If not, it inserts key with a value to the dictionary. /// - /// Key with null/None value is inserted to the dictionary if Symbol is not in the dictionary. - /// The value of the Symbol if it is in the dictionary - /// None if Symbol is not in the dictionary - public T setdefault(Symbol symbol) + /// Key with null/None value is inserted to the dictionary if key is not in the dictionary. + /// The value of the key if it is in the dictionary + /// None if key is not in the dictionary + public TValue setdefault(TKey key) { - return setdefault(symbol, default(T)); + return setdefault(key, default); } /// - /// Returns the value of a Symbol (if the Symbol is in dictionary). If not, it inserts Symbol with a value to the dictionary. + /// Returns the value of a key (if the key is in dictionary). If not, it inserts key with a value to the dictionary. /// - /// Key with a value default_value is inserted to the dictionary if Symbol is not in the dictionary. + /// Key with a value default_value is inserted to the dictionary if key is not in the dictionary. /// Default value - /// The value of the Symbol if it is in the dictionary - /// default_value if Symbol is not in the dictionary and default_value is specified - public T setdefault(Symbol symbol, T default_value) + /// The value of the key if it is in the dictionary + /// default_value if key is not in the dictionary and default_value is specified + public TValue setdefault(TKey key, TValue default_value) { - T data; - if (TryGetValue(symbol, out data)) + TValue data; + if (TryGetValue(key, out data)) { return data; } if (IsReadOnly) { - throw new KeyNotFoundException(Messages.ExtendedDictionary.SymbolNotFoundDueToNoData(this, symbol)); + throw new KeyNotFoundException(Messages.ExtendedDictionary.KeyNotFoundDueToNoData(this, key)); } - this[symbol] = default_value; + this[key] = default_value; return default_value; } /// - /// Removes and returns an element from a dictionary having the given Symbol. + /// Removes and returns an element from a dictionary having the given key. /// - /// Key which is to be searched for removal - /// If Symbol is found - removed/popped element from the dictionary - /// If Symbol is not found - KeyError exception is raised - public T pop(Symbol symbol) + /// Key which is to be searched for removal + /// If key is found - removed/popped element from the dictionary + /// If key is not found - KeyError exception is raised + public TValue pop(TKey key) { - return pop(symbol, default(T)); + return pop(key, default); } /// - /// Removes and returns an element from a dictionary having the given Symbol. + /// Removes and returns an element from a dictionary having the given key. /// - /// Key which is to be searched for removal - /// Value which is to be returned when the Symbol is not in the dictionary - /// If Symbol is found - removed/popped element from the dictionary - /// If Symbol is not found - value specified as the second argument(default) - public T pop(Symbol symbol, T default_value) + /// Key which is to be searched for removal + /// Value which is to be returned when the key is not in the dictionary + /// If key is found - removed/popped element from the dictionary + /// If key is not found - value specified as the second argument(default) + public TValue pop(TKey key, TValue default_value) { - T data; - if (TryGetValue(symbol, out data)) + TValue data; + if (TryGetValue(key, out data)) { - Remove(symbol); + Remove(key); return data; } return default_value; } /// - /// Updates the dictionary with the elements from the another dictionary object or from an iterable of Symbol/value pairs. - /// The update() method adds element(s) to the dictionary if the Symbol is not in the dictionary.If the Symbol is in the dictionary, it updates the Symbol with the new value. + /// Updates the dictionary with the elements from the another dictionary object or from an iterable of key/value pairs. + /// The update() method adds element(s) to the dictionary if the key is not in the dictionary.If the key is in the dictionary, it updates the key with the new value. /// - /// Takes either a dictionary or an iterable object of Symbol/value pairs (generally tuples). + /// Takes either a dictionary or an iterable object of key/value pairs (generally tuples). public void update(PyObject other) { if (IsReadOnly) @@ -324,7 +295,7 @@ public void update(PyObject other) throw new InvalidOperationException(Messages.ExtendedDictionary.UpdateInvalidOperation(this)); } - var dictionary = other.ConvertToDictionary(); + var dictionary = other.ConvertToDictionary(); foreach (var kvp in dictionary) { this[kvp.Key] = kvp.Value; @@ -332,9 +303,9 @@ public void update(PyObject other) } /// - /// Returns a view object that displays a list of all the Symbol objects in the dictionary + /// Returns a view object that displays a list of all the key objects in the dictionary /// - /// Returns a view object that displays a list of all the Symbol objects. + /// Returns a view object that displays a list of all the key objects. /// When the dictionary is changed, the view object also reflect these changes. public PyList keys() { diff --git a/Common/Messages/Messages.QuantConnect.cs b/Common/Messages/Messages.QuantConnect.cs index 1d2a8bd8cf06..9456a74ae79d 100644 --- a/Common/Messages/Messages.QuantConnect.cs +++ b/Common/Messages/Messages.QuantConnect.cs @@ -160,7 +160,7 @@ public static class ExtendedDictionary /// is a read-only collection /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string ClearInvalidOperation(ExtendedDictionary instance) + public static string ClearInvalidOperation(ExtendedDictionary instance) { return $"Clear/clear method call is an invalid operation. {instance.GetType().Name} is a read-only collection."; } @@ -170,7 +170,7 @@ public static string ClearInvalidOperation(ExtendedDictionary instance) /// is a read-only collection /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string RemoveInvalidOperation(ExtendedDictionary instance) + public static string RemoveInvalidOperation(ExtendedDictionary instance) { return $"Remove/pop method call is an invalid operation. {instance.GetType().Name} is a read-only collection."; } @@ -191,7 +191,7 @@ public static string TickerNotFoundInSymbolCache(string ticker) /// Returns a string message saying that the popitem method is not supported for the given instance /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string PopitemMethodNotSupported(ExtendedDictionary instance) + public static string PopitemMethodNotSupported(ExtendedDictionary instance) { return $"popitem method is not supported for {instance.GetType().Name}"; } @@ -201,11 +201,11 @@ public static string PopitemMethodNotSupported(ExtendedDictionary instance /// a recommendation for solving this problem /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string SymbolNotFoundDueToNoData(ExtendedDictionary instance, QuantConnect.Symbol symbol) + public static string KeyNotFoundDueToNoData(ExtendedDictionary instance, TKey key) { - return $"'{symbol}' wasn't found in the {instance.GetType().Name} object, likely because there was no-data at this moment in " + + return $"'{key}' wasn't found in the {instance.GetType().Name} object, likely because there was no-data at this moment in " + "time and it wasn't possible to fillforward historical data. Please check the data exists before accessing it with " + - $"data.ContainsKey(\"{symbol}\"). The collection is read-only, cannot set default."; + $"data.ContainsKey(\"{key}\"). The collection is read-only, cannot set default."; } /// @@ -213,7 +213,7 @@ public static string SymbolNotFoundDueToNoData(ExtendedDictionary instance /// instance is a read-only collection /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string UpdateInvalidOperation(ExtendedDictionary instance) + public static string UpdateInvalidOperation(ExtendedDictionary instance) { return $"update method call is an invalid operation. {instance.GetType().Name} is a read-only collection."; } diff --git a/Common/Securities/SecurityManager.cs b/Common/Securities/SecurityManager.cs index 13ad9b6cc519..6f43a7103872 100644 --- a/Common/Securities/SecurityManager.cs +++ b/Common/Securities/SecurityManager.cs @@ -27,7 +27,7 @@ namespace QuantConnect.Securities /// Enumerable security management class for grouping security objects into an array and providing any common properties. /// /// Implements IDictionary for the index searching of securities by symbol - public class SecurityManager : ExtendedDictionary, IDictionary, INotifyCollectionChanged + public class SecurityManager : ExtendedDictionary, IDictionary, INotifyCollectionChanged { /// /// Event fired when a security is added or removed from this collection diff --git a/Common/Securities/SecurityPortfolioManager.cs b/Common/Securities/SecurityPortfolioManager.cs index e0873175d639..f2ef4d1fca49 100644 --- a/Common/Securities/SecurityPortfolioManager.cs +++ b/Common/Securities/SecurityPortfolioManager.cs @@ -32,7 +32,7 @@ namespace QuantConnect.Securities /// Portfolio manager class groups popular properties and makes them accessible through one interface. /// It also provide indexing by the vehicle symbol to get the Security.Holding objects. /// - public class SecurityPortfolioManager : ExtendedDictionary, IDictionary, ISecurityProvider + public class SecurityPortfolioManager : ExtendedDictionary, IDictionary, ISecurityProvider { private Cash _baseCurrencyCash; private bool _setCashWasCalled; From 372bbc1afc8a77ffc81a577726d6943bfb31e751 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Mon, 28 Apr 2025 15:58:04 -0400 Subject: [PATCH 04/15] Make SecurityPositionGroupModel an ExtendedDictionary --- Common/Data/Market/Delistings.cs | 22 +------------- .../Positions/PositionGroupCollection.cs | 10 +++++++ .../Positions/SecurityPositionGroupModel.cs | 29 +++++++++++++++++-- 3 files changed, 38 insertions(+), 23 deletions(-) diff --git a/Common/Data/Market/Delistings.cs b/Common/Data/Market/Delistings.cs index 085c428d5a40..d9338011b1a2 100644 --- a/Common/Data/Market/Delistings.cs +++ b/Common/Data/Market/Delistings.cs @@ -1,4 +1,4 @@ -/* +/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * @@ -38,25 +38,5 @@ public Delistings(DateTime frontier) : base(frontier) { } - - /// - /// Gets or sets the Delisting with the specified ticker. - /// - /// - /// The Delisting with the specified ticker. - /// - /// The ticker of the element to get or set. - /// Wraps the base implementation to enable indexing in python algorithms due to pythonnet limitations - public new Delisting this[string ticker] { get { return base[ticker]; } set { base[ticker] = value; } } - - /// - /// Gets or sets the Delisting with the specified Symbol. - /// - /// - /// The Delisting with the specified Symbol. - /// - /// The Symbol of the element to get or set. - /// Wraps the base implementation to enable indexing in python algorithms due to pythonnet limitations - public new Delisting this[Symbol symbol] { get { return base[symbol]; } set { base[symbol] = value; } } } } diff --git a/Common/Securities/Positions/PositionGroupCollection.cs b/Common/Securities/Positions/PositionGroupCollection.cs index ae90511924f4..6bcbdf7fb1f6 100644 --- a/Common/Securities/Positions/PositionGroupCollection.cs +++ b/Common/Securities/Positions/PositionGroupCollection.cs @@ -50,6 +50,16 @@ public bool IsOnlyDefaultGroups } } + /// + /// Gets the position groups keys in this collection + /// + public IReadOnlyCollection Keys => _groups.Keys; + + /// + /// Gets the position groups in this collection + /// + public IReadOnlyCollection Values => _groups.Values; + private bool? _hasNonDefaultGroups; private readonly Dictionary _groups; private readonly Dictionary> _groupsBySymbol; diff --git a/Common/Securities/Positions/SecurityPositionGroupModel.cs b/Common/Securities/Positions/SecurityPositionGroupModel.cs index 20c8521ba129..e34ebce0d5a0 100644 --- a/Common/Securities/Positions/SecurityPositionGroupModel.cs +++ b/Common/Securities/Positions/SecurityPositionGroupModel.cs @@ -24,7 +24,7 @@ namespace QuantConnect.Securities.Positions /// /// Responsible for managing the resolution of position groups for an algorithm /// - public class SecurityPositionGroupModel + public class SecurityPositionGroupModel : ExtendedDictionary { /// /// Gets an implementation of that will not group multiple securities @@ -64,6 +64,16 @@ private set /// public bool IsOnlyDefaultGroups => Groups.IsOnlyDefaultGroups; + /// + /// Gets all the available position group keys + /// + protected override IEnumerable GetKeys => Groups.Keys; + + /// + /// Gets all the available position groups + /// + protected override IEnumerable GetValues => Groups.Values; + /// /// Initializes a new instance of the class /// @@ -122,7 +132,11 @@ public virtual void Initialize(SecurityManager securities) /// Gets the matching the specified . If one is not found, /// then a new empty position group is returned. /// - public IPositionGroup this[PositionGroupKey key] => Groups[key]; + public override IPositionGroup this[PositionGroupKey key] + { + get => Groups[key]; + set => throw new NotImplementedException("Read-only collection. Cannot set value."); + } /// /// Creates a position group for the specified order, pulling @@ -213,5 +227,16 @@ private void ResolvePositionGroups() Groups = ResolvePositionGroups(positionsCollection); } } + + /// + /// Tries to get the position group matching the specified key + /// + /// The key to search for + /// The position group matching the specified key + /// True if a group with the specified key was found, false otherwise + public override bool TryGetValue(PositionGroupKey key, out IPositionGroup value) + { + return Groups.TryGetGroup(key, out value); + } } } From fe0d2b1b87e1d5b46b8168c8018a1b284cb9e51c Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Mon, 28 Apr 2025 17:04:55 -0400 Subject: [PATCH 05/15] Add unit tests --- Common/Data/Market/DataDictionary.cs | 2 +- Common/Data/Slice.cs | 2 +- Common/ExtendedDictionary.cs | 10 ++ .../Positions/SecurityPositionGroupModel.cs | 6 +- Common/Securities/SecurityManager.cs | 2 +- Common/Securities/SecurityPortfolioManager.cs | 2 +- Tests/Common/ExtendedDictionaryTests.cs | 95 +++++++++++++++++++ 7 files changed, 114 insertions(+), 5 deletions(-) diff --git a/Common/Data/Market/DataDictionary.cs b/Common/Data/Market/DataDictionary.cs index 2ba7f7008b74..12e68638bf7e 100644 --- a/Common/Data/Market/DataDictionary.cs +++ b/Common/Data/Market/DataDictionary.cs @@ -146,7 +146,7 @@ public bool Remove(KeyValuePair item) /// /// The number of elements contained in the . /// - public int Count + public override int Count { get { return _data.Count; } } diff --git a/Common/Data/Slice.cs b/Common/Data/Slice.cs index f63505cbbedf..717574b15ca8 100644 --- a/Common/Data/Slice.cs +++ b/Common/Data/Slice.cs @@ -171,7 +171,7 @@ public MarginInterestRates MarginInterestRates /// /// Gets the number of symbols held in this slice /// - public virtual int Count + public override int Count { get { return _data.Value.Count; } } diff --git a/Common/ExtendedDictionary.cs b/Common/ExtendedDictionary.cs index 691fed7687f7..361308ea7231 100644 --- a/Common/ExtendedDictionary.cs +++ b/Common/ExtendedDictionary.cs @@ -31,6 +31,11 @@ namespace QuantConnect public abstract class ExtendedDictionary : IExtendedDictionary #pragma warning restore CA1708 // Identifiers should differ by more than case { + /// + /// Gets the number of elements contained in the dictionary + /// + public abstract int Count { get; } + /// /// Removes all items from the . /// @@ -55,6 +60,11 @@ public virtual void Clear() /// is null. public abstract bool TryGetValue(TKey key, out TValue value); + /// + /// Checks if the dictionary contains the specified key. + /// + /// The key to locate in the dictionary + /// true if the dictionary contains an element with the specified key; otherwise, false. public virtual bool ContainsKey(TKey key) { return TryGetValue(key, out _); diff --git a/Common/Securities/Positions/SecurityPositionGroupModel.cs b/Common/Securities/Positions/SecurityPositionGroupModel.cs index e34ebce0d5a0..ef38724383bd 100644 --- a/Common/Securities/Positions/SecurityPositionGroupModel.cs +++ b/Common/Securities/Positions/SecurityPositionGroupModel.cs @@ -42,7 +42,6 @@ public class SecurityPositionGroupModel : ExtendedDictionary protected virtual IPositionGroupBuyingPowerModel PositionGroupBuyingPowerModel { get; } = new SecurityPositionGroupBuyingPowerModel(); - /// /// Gets the set of currently resolved position groups /// @@ -64,6 +63,11 @@ private set /// public bool IsOnlyDefaultGroups => Groups.IsOnlyDefaultGroups; + /// + /// Gets the number of position groups in this collection + /// + public override int Count => Groups.Count; + /// /// Gets all the available position group keys /// diff --git a/Common/Securities/SecurityManager.cs b/Common/Securities/SecurityManager.cs index 6f43a7103872..2df86760318a 100644 --- a/Common/Securities/SecurityManager.cs +++ b/Common/Securities/SecurityManager.cs @@ -171,7 +171,7 @@ public void CopyTo(KeyValuePair[] array, int number) /// Count of the number of securities in the collection. /// /// IDictionary implementation - public int Count + public override int Count { get { diff --git a/Common/Securities/SecurityPortfolioManager.cs b/Common/Securities/SecurityPortfolioManager.cs index f2ef4d1fca49..f414de745a7a 100644 --- a/Common/Securities/SecurityPortfolioManager.cs +++ b/Common/Securities/SecurityPortfolioManager.cs @@ -189,7 +189,7 @@ public bool Contains(KeyValuePair pair) /// Count the securities objects in the portfolio. /// /// IDictionary implementation calling the underlying Securities collection - public int Count + public override int Count { get { diff --git a/Tests/Common/ExtendedDictionaryTests.cs b/Tests/Common/ExtendedDictionaryTests.cs index 69b0b277d57d..c189eda5de71 100644 --- a/Tests/Common/ExtendedDictionaryTests.cs +++ b/Tests/Common/ExtendedDictionaryTests.cs @@ -14,8 +14,10 @@ */ using NUnit.Framework; +using Python.Runtime; using QuantConnect.Statistics; using System.Collections.Generic; +using System.Linq; namespace QuantConnect.Tests.Common { @@ -58,5 +60,98 @@ public void RunPythonDictionaryFeatureRegressionAlgorithm() parameter.ExpectedFinalStatus, initialCash: 100000); } + + [Test] + public void ExtendedDictionaryBehavesAsPythonDictionary() + { + using var _ = Py.GIL(); + + var module = PyModule.FromString("ExtendedDictionaryBehavesAsPythonDictionary", + @" +from QuantConnect.Tests.Common import ExtendedDictionaryTests + +def contains(dictionary, key): + return key in dictionary + +def get(dictionary, key): + return dictionary.get(key) + +def keys(dictionary): + return dictionary.keys() + +def values(dictionary): + return dictionary.values() + +def pop(dictionary, key): + return dictionary.pop(key) +"); + + var dict = new TestDictionary + { + ["a"] = 1, + ["b"] = 2, + ["c"] = 3 + }; + using var pyDict = dict.ToPython(); + + var expectedKeys = new[] { "a", "b", "c" }; + var keys = module.InvokeMethod("keys", pyDict).GetAndDispose>(); + CollectionAssert.AreEquivalent(expectedKeys, keys); + + var expectedValues = new[] { 1, 2, 3 }; + var values = module.InvokeMethod("values", pyDict).GetAndDispose>(); + CollectionAssert.AreEquivalent(expectedValues, values); + + foreach (var (key, value) in keys.Zip(values)) + { + using var pyKey = key.ToPython(); + Assert.IsTrue(module.InvokeMethod("contains", pyDict, pyKey).As()); + Assert.AreEqual(value, module.InvokeMethod("get", pyDict, pyKey).As()); + } + + using var pyNonExistingKey = "d".ToPython(); + Assert.IsFalse(module.InvokeMethod("contains", pyDict, pyNonExistingKey).As()); + Assert.IsFalse(module.InvokeMethod("contains", pyDict, PyObject.None).As()); + + using var pyExistingKey = keys[0].ToPython(); + using var pyExistingValue = values[0].ToPython(); + var popped = module.InvokeMethod("pop", pyDict, pyExistingKey).As(); + Assert.AreEqual(1, popped); + Assert.IsFalse(module.InvokeMethod("contains", pyDict, pyExistingKey).As()); + } + + public class TestDictionary : ExtendedDictionary + { + private readonly Dictionary _data = new(); + + public override int Count => _data.Count; + + public override bool IsReadOnly => false; + + public override int this[string key] + { + get => _data[key]; + set => _data[key] = value; + } + + protected override IEnumerable GetKeys => _data.Keys; + + protected override IEnumerable GetValues => _data.Values; + + public override bool TryGetValue(string key, out int value) + { + return _data.TryGetValue(key, out value); + } + + public override bool ContainsKey(string key) + { + return _data.ContainsKey(key); + } + + public override bool Remove(string key) + { + return _data.Remove(key); + } + } } } From e3b7862795517b92a5bb97069190cf424d9720d0 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Mon, 28 Apr 2025 17:17:15 -0400 Subject: [PATCH 06/15] Minor change --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 8f908a5b42ec..3a3f6e097b81 100644 --- a/.gitignore +++ b/.gitignore @@ -279,5 +279,4 @@ QuantConnect.Lean.sln.DotSettings* Research/Notebooks #Docker result files -Results/ -/myvenv +Results/ \ No newline at end of file From a23603cd5ee12614b028fcd53f102c51065cbb79 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Mon, 28 Apr 2025 18:26:53 -0400 Subject: [PATCH 07/15] Add more unit tests and other minor changes --- Common/Data/Market/DataDictionary.cs | 2 + Common/Data/Slice.cs | 1 + Common/ExtendedDictionary.cs | 12 +++ Common/Securities/SecurityManager.cs | 2 + Common/Securities/SecurityPortfolioManager.cs | 12 ++- Tests/Common/ExtendedDictionaryTests.cs | 81 ++++++++++++++++--- 6 files changed, 97 insertions(+), 13 deletions(-) diff --git a/Common/Data/Market/DataDictionary.cs b/Common/Data/Market/DataDictionary.cs index 12e68638bf7e..b0abd8f2b420 100644 --- a/Common/Data/Market/DataDictionary.cs +++ b/Common/Data/Market/DataDictionary.cs @@ -221,6 +221,7 @@ public override T this[Symbol symbol] { get { + CheckForImplicitlyCreatedSymbol(symbol); T data; if (TryGetValue(symbol, out data)) { @@ -230,6 +231,7 @@ public override T this[Symbol symbol] } set { + CheckForImplicitlyCreatedSymbol(symbol); _data[symbol] = value; } } diff --git a/Common/Data/Slice.cs b/Common/Data/Slice.cs index 717574b15ca8..eb5503e12fad 100644 --- a/Common/Data/Slice.cs +++ b/Common/Data/Slice.cs @@ -327,6 +327,7 @@ public override dynamic this[Symbol symbol] { get { + CheckForImplicitlyCreatedSymbol(symbol); SymbolData value; if (_data.Value.TryGetValue(symbol, out value)) { diff --git a/Common/ExtendedDictionary.cs b/Common/ExtendedDictionary.cs index 361308ea7231..5d813ecf166d 100644 --- a/Common/ExtendedDictionary.cs +++ b/Common/ExtendedDictionary.cs @@ -330,5 +330,17 @@ public PyList values() { return GetValues.ToPyList(); } + + /// + /// Checks if the symbol is implicitly created from a string, in which case it is not in the symbol cache, + /// and throws a KeyNotFoundException. + /// + protected void CheckForImplicitlyCreatedSymbol(Symbol symbol) + { + if (symbol.ID == new SecurityIdentifier(symbol.ID.Symbol, 0)) + { + throw new KeyNotFoundException(Messages.ExtendedDictionary.TickerNotFoundInSymbolCache(symbol.ID.Symbol)); + } + } } } diff --git a/Common/Securities/SecurityManager.cs b/Common/Securities/SecurityManager.cs index 2df86760318a..3e4c8837e024 100644 --- a/Common/Securities/SecurityManager.cs +++ b/Common/Securities/SecurityManager.cs @@ -355,6 +355,7 @@ public override Security this[Symbol symbol] { get { + CheckForImplicitlyCreatedSymbol(symbol); Security security; lock (_securityManager) { @@ -367,6 +368,7 @@ public override Security this[Symbol symbol] } set { + CheckForImplicitlyCreatedSymbol(symbol); Security existing; lock (_securityManager) { diff --git a/Common/Securities/SecurityPortfolioManager.cs b/Common/Securities/SecurityPortfolioManager.cs index f414de745a7a..ace3619b4227 100644 --- a/Common/Securities/SecurityPortfolioManager.cs +++ b/Common/Securities/SecurityPortfolioManager.cs @@ -597,8 +597,16 @@ public decimal GetMarginRemaining(decimal totalPortfolioValue) /// SecurityHolding class from the algorithm securities public override SecurityHolding this[Symbol symbol] { - get { return Securities[symbol].Holdings; } - set { Securities[symbol].Holdings = value; } + get + { + CheckForImplicitlyCreatedSymbol(symbol); + return Securities[symbol].Holdings; + } + set + { + CheckForImplicitlyCreatedSymbol(symbol); + Securities[symbol].Holdings = value; + } } /// diff --git a/Tests/Common/ExtendedDictionaryTests.cs b/Tests/Common/ExtendedDictionaryTests.cs index c189eda5de71..1e9d5744832e 100644 --- a/Tests/Common/ExtendedDictionaryTests.cs +++ b/Tests/Common/ExtendedDictionaryTests.cs @@ -68,8 +68,6 @@ public void ExtendedDictionaryBehavesAsPythonDictionary() var module = PyModule.FromString("ExtendedDictionaryBehavesAsPythonDictionary", @" -from QuantConnect.Tests.Common import ExtendedDictionaryTests - def contains(dictionary, key): return key in dictionary @@ -86,7 +84,7 @@ def pop(dictionary, key): return dictionary.pop(key) "); - var dict = new TestDictionary + var dict = new TestDictionary { ["a"] = 1, ["b"] = 2, @@ -120,38 +118,99 @@ def pop(dictionary, key): Assert.IsFalse(module.InvokeMethod("contains", pyDict, pyExistingKey).As()); } - public class TestDictionary : ExtendedDictionary + [Test] + public void SymbolKeyCanBeIndexedWithStrings() + { + using var _ = Py.GIL(); + + var module = PyModule.FromString("SymbolKeyCanBeIndexedWithStrings", + @" +def get(dictionary, key): + return dictionary[key] + +def set(dictionary, key, value): + dictionary[key] = value +"); + + var symbol = Symbols.SPY; + using var pySymbol = symbol.ToPython(); + + SymbolCache.Set(symbol.Value, symbol); + + var dict = new TestDictionary + { + [symbol] = 1, + }; + using var pyDict = dict.ToPython(); + + var value = module.InvokeMethod("get", pyDict, pySymbol).As(); + Assert.AreEqual(1, value); + + using var pyStringSymbol = symbol.Value.ToPython(); + value = module.InvokeMethod("get", pyDict, pyStringSymbol).As(); + Assert.AreEqual(1, value); + + using var pyNewValue = 2.ToPython(); + module.InvokeMethod("set", pyDict, pySymbol, pyNewValue); + value = module.InvokeMethod("get", pyDict, pySymbol).As(); + Assert.AreEqual(2, value); + value = module.InvokeMethod("get", pyDict, pyStringSymbol).As(); + Assert.AreEqual(2, value); + + using var pyNewValue2 = 3.ToPython(); + module.InvokeMethod("set", pyDict, pyStringSymbol, pyNewValue2); + value = module.InvokeMethod("get", pyDict, pySymbol).As(); + Assert.AreEqual(3, value); + value = module.InvokeMethod("get", pyDict, pyStringSymbol).As(); + Assert.AreEqual(3, value); + + using var pyNonExistingSymbol = Symbols.AAPL.ToPython(); + using var pyStringNonExistingSymbol = Symbols.AAPL.Value.ToPython(); + + var exception = Assert.Throws(() => module.InvokeMethod("get", pyDict, pyNonExistingSymbol)); + Assert.IsInstanceOf(exception.InnerException); + + exception = Assert.Throws(() => module.InvokeMethod("get", pyDict, pyStringNonExistingSymbol)); + Assert.IsInstanceOf(exception.InnerException); + } + + private class TestDictionary : ExtendedDictionary { - private readonly Dictionary _data = new(); + private readonly Dictionary _data = new(); public override int Count => _data.Count; public override bool IsReadOnly => false; - public override int this[string key] + public override TValue this[TKey key] { get => _data[key]; set => _data[key] = value; } - protected override IEnumerable GetKeys => _data.Keys; + protected override IEnumerable GetKeys => _data.Keys; - protected override IEnumerable GetValues => _data.Values; + protected override IEnumerable GetValues => _data.Values; - public override bool TryGetValue(string key, out int value) + public override bool TryGetValue(TKey key, out TValue value) { return _data.TryGetValue(key, out value); } - public override bool ContainsKey(string key) + public override bool ContainsKey(TKey key) { return _data.ContainsKey(key); } - public override bool Remove(string key) + public override bool Remove(TKey key) { return _data.Remove(key); } } + + private class TestSymbolDictionary : TestDictionary + { + + } } } From fe0042d5203e86cc7db3d38fe4fa3448371aa3f8 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Tue, 29 Apr 2025 17:18:43 -0400 Subject: [PATCH 08/15] Add Count and ContainsKey to BaseChain --- Common/Data/Market/BaseChain.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Common/Data/Market/BaseChain.cs b/Common/Data/Market/BaseChain.cs index 937b1411dbbd..1341d16f51a8 100644 --- a/Common/Data/Market/BaseChain.cs +++ b/Common/Data/Market/BaseChain.cs @@ -109,6 +109,21 @@ public HashSet FilteredContracts [PandasIgnore] public PyObject DataFrame => _dataframe.Value; + /// + /// The number of contracts in this chain + /// + public int Count => Contracts.Count; + + /// + /// Checks if the chain contains a contract with the specified symbol + /// + /// The symbol of the contract to check for + /// True if the chain contains a contract with the specified symbol; otherwise, false. + public bool ContainsKey(Symbol key) + { + return Contracts.ContainsKey(key); + } + /// /// Initializes a new default instance of the class /// From 15f2567b7f82edd63963b3e9aae64e5e7a389a08 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Thu, 1 May 2025 10:27:49 -0400 Subject: [PATCH 09/15] Update pythonnet version to 2.0.43 --- Algorithm.CSharp/QuantConnect.Algorithm.CSharp.csproj | 2 +- Algorithm.Framework/QuantConnect.Algorithm.Framework.csproj | 2 +- Algorithm.Python/QuantConnect.Algorithm.Python.csproj | 2 +- Algorithm/QuantConnect.Algorithm.csproj | 2 +- AlgorithmFactory/QuantConnect.AlgorithmFactory.csproj | 2 +- Common/QuantConnect.csproj | 2 +- Engine/QuantConnect.Lean.Engine.csproj | 2 +- Indicators/QuantConnect.Indicators.csproj | 2 +- Report/QuantConnect.Report.csproj | 2 +- Research/QuantConnect.Research.csproj | 2 +- Tests/QuantConnect.Tests.csproj | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Algorithm.CSharp/QuantConnect.Algorithm.CSharp.csproj b/Algorithm.CSharp/QuantConnect.Algorithm.CSharp.csproj index 975f634aa089..f2c1eba0d0ad 100644 --- a/Algorithm.CSharp/QuantConnect.Algorithm.CSharp.csproj +++ b/Algorithm.CSharp/QuantConnect.Algorithm.CSharp.csproj @@ -32,7 +32,7 @@ portable - + diff --git a/Algorithm.Framework/QuantConnect.Algorithm.Framework.csproj b/Algorithm.Framework/QuantConnect.Algorithm.Framework.csproj index 964576ff57b4..8f24d1f87ac5 100644 --- a/Algorithm.Framework/QuantConnect.Algorithm.Framework.csproj +++ b/Algorithm.Framework/QuantConnect.Algorithm.Framework.csproj @@ -29,7 +29,7 @@ LICENSE - + diff --git a/Algorithm.Python/QuantConnect.Algorithm.Python.csproj b/Algorithm.Python/QuantConnect.Algorithm.Python.csproj index 9b566b925e35..613cfe4b7416 100644 --- a/Algorithm.Python/QuantConnect.Algorithm.Python.csproj +++ b/Algorithm.Python/QuantConnect.Algorithm.Python.csproj @@ -37,7 +37,7 @@ - + diff --git a/Algorithm/QuantConnect.Algorithm.csproj b/Algorithm/QuantConnect.Algorithm.csproj index 4b36a3533736..adb79b0a9486 100644 --- a/Algorithm/QuantConnect.Algorithm.csproj +++ b/Algorithm/QuantConnect.Algorithm.csproj @@ -29,7 +29,7 @@ LICENSE - + diff --git a/AlgorithmFactory/QuantConnect.AlgorithmFactory.csproj b/AlgorithmFactory/QuantConnect.AlgorithmFactory.csproj index cb5ec33a6424..d4e994e4040b 100644 --- a/AlgorithmFactory/QuantConnect.AlgorithmFactory.csproj +++ b/AlgorithmFactory/QuantConnect.AlgorithmFactory.csproj @@ -28,7 +28,7 @@ LICENSE - + diff --git a/Common/QuantConnect.csproj b/Common/QuantConnect.csproj index 826d79c646b8..09d57bce1c10 100644 --- a/Common/QuantConnect.csproj +++ b/Common/QuantConnect.csproj @@ -35,7 +35,7 @@ - + diff --git a/Engine/QuantConnect.Lean.Engine.csproj b/Engine/QuantConnect.Lean.Engine.csproj index 3f87eecfbf9b..a995d76fb836 100644 --- a/Engine/QuantConnect.Lean.Engine.csproj +++ b/Engine/QuantConnect.Lean.Engine.csproj @@ -41,7 +41,7 @@ - + diff --git a/Indicators/QuantConnect.Indicators.csproj b/Indicators/QuantConnect.Indicators.csproj index e3e05e2ba5e8..021f3502dd94 100644 --- a/Indicators/QuantConnect.Indicators.csproj +++ b/Indicators/QuantConnect.Indicators.csproj @@ -31,7 +31,7 @@ - + diff --git a/Report/QuantConnect.Report.csproj b/Report/QuantConnect.Report.csproj index e40043449b21..3326e91cefb4 100644 --- a/Report/QuantConnect.Report.csproj +++ b/Report/QuantConnect.Report.csproj @@ -39,7 +39,7 @@ LICENSE - + diff --git a/Research/QuantConnect.Research.csproj b/Research/QuantConnect.Research.csproj index cdab8dd9bdea..af5d0331bee1 100644 --- a/Research/QuantConnect.Research.csproj +++ b/Research/QuantConnect.Research.csproj @@ -34,7 +34,7 @@ - + diff --git a/Tests/QuantConnect.Tests.csproj b/Tests/QuantConnect.Tests.csproj index acb371684612..18c7193af9f4 100644 --- a/Tests/QuantConnect.Tests.csproj +++ b/Tests/QuantConnect.Tests.csproj @@ -31,7 +31,7 @@ - + From 87a3eb775adc40768daad6b55b80b186634672d4 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Thu, 1 May 2025 10:46:58 -0400 Subject: [PATCH 10/15] Update some python regression algorithms --- .../DynamicSecurityDataRegressionAlgorithm.py | 14 +++++++------- ...hDifferentDataMappingModeRegressionAlgorithm.py | 5 ++--- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Algorithm.Python/DynamicSecurityDataRegressionAlgorithm.py b/Algorithm.Python/DynamicSecurityDataRegressionAlgorithm.py index 5f7a08e1a3ab..3201720dbba3 100644 --- a/Algorithm.Python/DynamicSecurityDataRegressionAlgorithm.py +++ b/Algorithm.Python/DynamicSecurityDataRegressionAlgorithm.py @@ -28,24 +28,24 @@ def initialize(self): self._equity = self.add_equity(ticker, Resolution.DAILY) custom_linked_equity = self.add_data(LinkedData, ticker, Resolution.DAILY) - + first_linked_data = LinkedData() first_linked_data.count = 100 first_linked_data.symbol = custom_linked_equity.symbol first_linked_data.end_time = self.start_date - + second_linked_data = LinkedData() second_linked_data.count = 100 second_linked_data.symbol = custom_linked_equity.symbol second_linked_data.end_time = self.start_date - + # Adding linked data manually to cache for example purposes, since # LinkedData is a type used for testing and doesn't point to any real data. custom_linked_equity_type = list(custom_linked_equity.subscriptions)[0].type - custom_linked_data = List[LinkedData]() - custom_linked_data.add(first_linked_data) - custom_linked_data.add(second_linked_data) - self._equity.cache.add_data_list(custom_linked_data, custom_linked_equity_type, False) + custom_linked_data = list[LinkedData]() + custom_linked_data.append(first_linked_data) + custom_linked_data.append(second_linked_data) + self._equity.cache.add_data_list(custom_linked_data, custom_linked_equity_type, False) def on_data(self, data): # The Security object's Data property provides convenient access diff --git a/Algorithm.Python/HistoryWithDifferentDataMappingModeRegressionAlgorithm.py b/Algorithm.Python/HistoryWithDifferentDataMappingModeRegressionAlgorithm.py index 26c1b7fa200c..58590ae4c186 100644 --- a/Algorithm.Python/HistoryWithDifferentDataMappingModeRegressionAlgorithm.py +++ b/Algorithm.Python/HistoryWithDifferentDataMappingModeRegressionAlgorithm.py @@ -25,13 +25,12 @@ def initialize(self): self._continuous_contract_symbol = self.add_future(Futures.Indices.SP_500_E_MINI, Resolution.DAILY).symbol def on_end_of_algorithm(self): - data_mapping_modes = [DataMappingMode(x) for x in Enum.get_values(DataMappingMode)] history_results = [ self.history([self._continuous_contract_symbol], self.start_date, self.end_date, Resolution.DAILY, data_mapping_mode=data_mapping_mode) .droplevel(0, axis=0) .loc[self._continuous_contract_symbol] .close - for data_mapping_mode in data_mapping_modes + for data_mapping_mode in DataMappingMode ] if any(x.size != history_results[0].size for x in history_results): @@ -40,5 +39,5 @@ def on_end_of_algorithm(self): # Check that close prices at each time are different for different data mapping modes for j in range(history_results[0].size): close_prices = set(history_results[i][j] for i in range(len(history_results))) - if len(close_prices) != len(data_mapping_modes): + if len(close_prices) != len(DataMappingMode): raise AssertionError("History results close prices should have been different for each data mapping mode at each time") From dd06144bdcec8857f45f5c0302b844e76dca30bd Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Thu, 1 May 2025 12:00:43 -0400 Subject: [PATCH 11/15] Minor regression algorithms fix --- .../StringToSymbolImplicitConversionRegressionAlgorithm.cs | 2 +- .../StringToSymbolImplicitConversionRegressionAlgorithm.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Algorithm.CSharp/StringToSymbolImplicitConversionRegressionAlgorithm.cs b/Algorithm.CSharp/StringToSymbolImplicitConversionRegressionAlgorithm.cs index 681f686d3565..599b31677c93 100644 --- a/Algorithm.CSharp/StringToSymbolImplicitConversionRegressionAlgorithm.cs +++ b/Algorithm.CSharp/StringToSymbolImplicitConversionRegressionAlgorithm.cs @@ -49,7 +49,7 @@ public override void OnData(Slice slice) } catch (Exception exception) { - if (exception.Message.Contains("This asset symbol (PEPE 0) was not found in your security list") && !Portfolio.Invested) + if (exception.Message.Contains("PEPE was not found", StringComparison.InvariantCultureIgnoreCase) && !Portfolio.Invested) { SetHoldings("SPY", 1); } diff --git a/Algorithm.Python/StringToSymbolImplicitConversionRegressionAlgorithm.py b/Algorithm.Python/StringToSymbolImplicitConversionRegressionAlgorithm.py index 12025f4da3c9..fe23b230a046 100644 --- a/Algorithm.Python/StringToSymbolImplicitConversionRegressionAlgorithm.py +++ b/Algorithm.Python/StringToSymbolImplicitConversionRegressionAlgorithm.py @@ -34,5 +34,5 @@ def on_data(self, data): try: self.market_order("PEPE", 1) except Exception as exception: - if "This asset symbol (PEPE 0) was not found in your security list" in str(exception) and not self.portfolio.invested: + if "PEPE was not found" in str(exception) and not self.portfolio.invested: self.set_holdings("SPY", 1) From d56e548c7e5a0a3ac53df19bbbda6aaac80a2862 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Thu, 1 May 2025 12:53:31 -0400 Subject: [PATCH 12/15] Make CashBook and ExtendedDictionary --- .../DynamicSecurityDataRegressionAlgorithm.py | 1 - ...erentDataMappingModeRegressionAlgorithm.py | 1 - Common/Data/Market/DataDictionary.cs | 3 +- Common/Securities/CashBook.cs | 28 +++++++++++++------ 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/Algorithm.Python/DynamicSecurityDataRegressionAlgorithm.py b/Algorithm.Python/DynamicSecurityDataRegressionAlgorithm.py index 3201720dbba3..6bd8b05a3df2 100644 --- a/Algorithm.Python/DynamicSecurityDataRegressionAlgorithm.py +++ b/Algorithm.Python/DynamicSecurityDataRegressionAlgorithm.py @@ -12,7 +12,6 @@ # limitations under the License. from AlgorithmImports import * -from System.Collections.Generic import List from QuantConnect.Data.Custom.IconicTypes import * ### diff --git a/Algorithm.Python/HistoryWithDifferentDataMappingModeRegressionAlgorithm.py b/Algorithm.Python/HistoryWithDifferentDataMappingModeRegressionAlgorithm.py index 58590ae4c186..412f666cbaad 100644 --- a/Algorithm.Python/HistoryWithDifferentDataMappingModeRegressionAlgorithm.py +++ b/Algorithm.Python/HistoryWithDifferentDataMappingModeRegressionAlgorithm.py @@ -12,7 +12,6 @@ # limitations under the License. from AlgorithmImports import * -from System import * ### ### Regression algorithm illustrating how to request history data for different data mapping modes. diff --git a/Common/Data/Market/DataDictionary.cs b/Common/Data/Market/DataDictionary.cs index b0abd8f2b420..464853e50983 100644 --- a/Common/Data/Market/DataDictionary.cs +++ b/Common/Data/Market/DataDictionary.cs @@ -221,17 +221,16 @@ public override T this[Symbol symbol] { get { - CheckForImplicitlyCreatedSymbol(symbol); T data; if (TryGetValue(symbol, out data)) { return data; } + CheckForImplicitlyCreatedSymbol(symbol); throw new KeyNotFoundException($"'{symbol}' wasn't found in the {GetType().GetBetterTypeName()} object, likely because there was no-data at this moment in time and it wasn't possible to fillforward historical data. Please check the data exists before accessing it with data.ContainsKey(\"{symbol}\")"); } set { - CheckForImplicitlyCreatedSymbol(symbol); _data[symbol] = value; } } diff --git a/Common/Securities/CashBook.cs b/Common/Securities/CashBook.cs index 6727d8b57f30..03934dc9c8ad 100644 --- a/Common/Securities/CashBook.cs +++ b/Common/Securities/CashBook.cs @@ -29,7 +29,7 @@ namespace QuantConnect.Securities /// /// Provides a means of keeping track of the different cash holdings of an algorithm /// - public class CashBook : IDictionary, ICurrencyConverter + public class CashBook : ExtendedDictionary, IDictionary, ICurrencyConverter { private string _accountCurrency; @@ -209,7 +209,7 @@ public override string ToString() /// Gets the count of Cash items in this CashBook. /// /// The count. - public int Count + public override int Count { get { @@ -221,7 +221,7 @@ public int Count /// Gets a value indicating whether this instance is read only. /// /// true if this instance is read only; otherwise, false. - public bool IsReadOnly + public override bool IsReadOnly { get { return false; } } @@ -248,7 +248,7 @@ public void Add(string symbol, Cash value) /// /// Clear this instance of all Cash entries. /// - public void Clear() + public override void Clear() { _currencies = new(); OnUpdate(CashBookUpdateType.Removed, null); @@ -258,7 +258,7 @@ public void Clear() /// Remove the Cash item corresponding to the specified symbol /// /// The symbolto be removed - public bool Remove(string symbol) + public override bool Remove(string symbol) { return Remove(symbol, calledInternally: false); } @@ -277,7 +277,7 @@ public bool Remove(KeyValuePair item) /// /// true, if key was contained, false otherwise. /// Key. - public bool ContainsKey(string symbol) + public override bool ContainsKey(string symbol) { return _currencies.ContainsKey(symbol); } @@ -289,7 +289,7 @@ public bool ContainsKey(string symbol) /// true, if get value was tryed, false otherwise. /// The symbol. /// Value. - public bool TryGetValue(string symbol, out Cash value) + public override bool TryGetValue(string symbol, out Cash value) { return _currencies.TryGetValue(symbol, out value); } @@ -317,7 +317,7 @@ public void CopyTo(KeyValuePair[] array, int arrayIndex) /// Gets or sets the with the specified symbol. /// /// Symbol. - public Cash this[string symbol] + public override Cash this[string symbol] { get { @@ -350,6 +350,18 @@ public Cash this[string symbol] /// The values. public ICollection Values => _currencies.Values; + /// + /// Gets the keys. + /// + /// The keys. + protected override IEnumerable GetKeys => Keys; + + /// + /// Gets the values. + /// + /// The values. + protected override IEnumerable GetValues => Values; + /// /// Gets the enumerator. /// From 2c8cf5198be26242a470c72b9341bae0ed4ffe74 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Thu, 1 May 2025 14:31:22 -0400 Subject: [PATCH 13/15] Minor changes --- Common/Data/Slice.cs | 2 +- Common/Securities/SecurityManager.cs | 3 +-- Common/Securities/SecurityPortfolioManager.cs | 2 -- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/Common/Data/Slice.cs b/Common/Data/Slice.cs index eb5503e12fad..59c06c4acb7a 100644 --- a/Common/Data/Slice.cs +++ b/Common/Data/Slice.cs @@ -327,12 +327,12 @@ public override dynamic this[Symbol symbol] { get { - CheckForImplicitlyCreatedSymbol(symbol); SymbolData value; if (_data.Value.TryGetValue(symbol, out value)) { return value.GetData(); } + CheckForImplicitlyCreatedSymbol(symbol); throw new KeyNotFoundException($"'{symbol}' wasn't found in the Slice object, likely because there was no-data at this moment in time and it wasn't possible to fillforward historical data. Please check the data exists before accessing it with data.ContainsKey(\"{symbol}\")"); } set diff --git a/Common/Securities/SecurityManager.cs b/Common/Securities/SecurityManager.cs index 3e4c8837e024..5c3831a75fd1 100644 --- a/Common/Securities/SecurityManager.cs +++ b/Common/Securities/SecurityManager.cs @@ -355,12 +355,12 @@ public override Security this[Symbol symbol] { get { - CheckForImplicitlyCreatedSymbol(symbol); Security security; lock (_securityManager) { if (!_completeSecuritiesCollection.TryGetValue(symbol, out security)) { + CheckForImplicitlyCreatedSymbol(symbol); throw new KeyNotFoundException(Messages.SecurityManager.SymbolNotFoundInSecurities(symbol)); } } @@ -368,7 +368,6 @@ public override Security this[Symbol symbol] } set { - CheckForImplicitlyCreatedSymbol(symbol); Security existing; lock (_securityManager) { diff --git a/Common/Securities/SecurityPortfolioManager.cs b/Common/Securities/SecurityPortfolioManager.cs index ace3619b4227..facd13b57ad1 100644 --- a/Common/Securities/SecurityPortfolioManager.cs +++ b/Common/Securities/SecurityPortfolioManager.cs @@ -599,12 +599,10 @@ public override SecurityHolding this[Symbol symbol] { get { - CheckForImplicitlyCreatedSymbol(symbol); return Securities[symbol].Holdings; } set { - CheckForImplicitlyCreatedSymbol(symbol); Securities[symbol].Holdings = value; } } From d034616f7156641848eb089e886131d488acb317 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Thu, 1 May 2025 18:21:33 -0400 Subject: [PATCH 14/15] Minor changes --- Common/Data/Market/DataDictionary.cs | 8 ++++++- Common/Data/Slice.cs | 9 +++++++- Common/ExtendedDictionary.cs | 22 +++++++++---------- Common/Securities/CashBook.cs | 6 +++++ .../Positions/PositionGroupCollection.cs | 2 ++ .../Positions/SecurityPositionGroupModel.cs | 6 +++++ Common/Securities/SecurityManager.cs | 17 +++++++++++--- Common/Securities/SecurityPortfolioManager.cs | 9 +++++++- Tests/Common/ExtendedDictionaryTests.cs | 5 +++++ 9 files changed, 67 insertions(+), 17 deletions(-) diff --git a/Common/Data/Market/DataDictionary.cs b/Common/Data/Market/DataDictionary.cs index 464853e50983..5f2dc03d179f 100644 --- a/Common/Data/Market/DataDictionary.cs +++ b/Common/Data/Market/DataDictionary.cs @@ -169,11 +169,17 @@ public override bool IsReadOnly /// true if the contains an element with the key; otherwise, false. /// /// The key to locate in the . is null. - public bool ContainsKey(Symbol key) + public override bool ContainsKey(Symbol key) { return _data.ContainsKey(key); } + /// + /// Gets all the items in the dictionary + /// + /// All the items in the dictionary + public override IEnumerable> GetItems() => _data; + /// /// Adds an element with the provided key and value to the . /// diff --git a/Common/Data/Slice.cs b/Common/Data/Slice.cs index 59c06c4acb7a..d7009e3342f4 100644 --- a/Common/Data/Slice.cs +++ b/Common/Data/Slice.cs @@ -208,6 +208,13 @@ public virtual IReadOnlyList Values get { return GetKeyValuePairEnumerable().Select(x => x.Value).ToList(); } } + /// + /// Gets all the items in the dictionary + /// + /// All the items in the dictionary + public override IEnumerable> GetItems() => + GetKeyValuePairEnumerable().Select(kvp => KeyValuePair.Create(kvp.Key, kvp.Value)); + /// /// Initializes a new instance of the class, lazily /// instantiating the and @@ -518,7 +525,7 @@ public T Get(Symbol symbol) /// /// The symbol we seek data for /// True if this instance contains data for the symbol, false otherwise - public virtual bool ContainsKey(Symbol symbol) + public override bool ContainsKey(Symbol symbol) { return _data.Value.ContainsKey(symbol); } diff --git a/Common/ExtendedDictionary.cs b/Common/ExtendedDictionary.cs index 5d813ecf166d..f5a8458b13b0 100644 --- a/Common/ExtendedDictionary.cs +++ b/Common/ExtendedDictionary.cs @@ -70,6 +70,12 @@ public virtual bool ContainsKey(TKey key) return TryGetValue(key, out _); } + /// + /// Gets all the items in the dictionary + /// + /// All the items in the dictionary + public abstract IEnumerable> GetItems(); + /// /// Gets an containing the key objects of the . /// @@ -201,18 +207,12 @@ public PyList items() using (Py.GIL()) { var pyList = new PyList(); - foreach (var key in GetKeys) + foreach (var (key, value) in GetItems()) { - using (var pyKey = key.ToPython()) - { - using (var pyValue = this[key].ToPython()) - { - using (var pyObject = new PyTuple([pyKey, pyValue])) - { - pyList.Append(pyObject); - } - } - } + using var pyKey = key.ToPython(); + using var pyValue = value.ToPython(); + using var pyKvp = new PyTuple([pyKey, pyValue]); + pyList.Append(pyKvp); } return pyList; } diff --git a/Common/Securities/CashBook.cs b/Common/Securities/CashBook.cs index 03934dc9c8ad..620950e03bac 100644 --- a/Common/Securities/CashBook.cs +++ b/Common/Securities/CashBook.cs @@ -362,6 +362,12 @@ public override Cash this[string symbol] /// The values. protected override IEnumerable GetValues => Values; + /// + /// Gets all the items in the dictionary + /// + /// All the items in the dictionary + public override IEnumerable> GetItems() => _currencies; + /// /// Gets the enumerator. /// diff --git a/Common/Securities/Positions/PositionGroupCollection.cs b/Common/Securities/Positions/PositionGroupCollection.cs index 6bcbdf7fb1f6..20ec848bcaa6 100644 --- a/Common/Securities/Positions/PositionGroupCollection.cs +++ b/Common/Securities/Positions/PositionGroupCollection.cs @@ -64,6 +64,8 @@ public bool IsOnlyDefaultGroups private readonly Dictionary _groups; private readonly Dictionary> _groupsBySymbol; + internal IEnumerable> GetGroups() => _groups; + /// /// Initializes a new instance of the class /// diff --git a/Common/Securities/Positions/SecurityPositionGroupModel.cs b/Common/Securities/Positions/SecurityPositionGroupModel.cs index ef38724383bd..2e26ad1d6744 100644 --- a/Common/Securities/Positions/SecurityPositionGroupModel.cs +++ b/Common/Securities/Positions/SecurityPositionGroupModel.cs @@ -78,6 +78,12 @@ private set /// protected override IEnumerable GetValues => Groups.Values; + /// + /// Gets all the items in the dictionary + /// + /// All the items in the dictionary + public override IEnumerable> GetItems() => Groups.GetGroups(); + /// /// Initializes a new instance of the class /// diff --git a/Common/Securities/SecurityManager.cs b/Common/Securities/SecurityManager.cs index 5c3831a75fd1..eba37583b819 100644 --- a/Common/Securities/SecurityManager.cs +++ b/Common/Securities/SecurityManager.cs @@ -145,7 +145,7 @@ public bool Contains(KeyValuePair pair) /// Symbol we're checking for. /// IDictionary implementation /// Bool true if contains this symbol pair - public bool ContainsKey(Symbol symbol) + public override bool ContainsKey(Symbol symbol) { lock (_securityManager) { @@ -332,7 +332,7 @@ IEnumerator IEnumerable.GetEnumerator() return GetEnumeratorImplementation(); } - private List>.Enumerator GetEnumeratorImplementation() + private List> GetEnumerable() { var result = _enumerator; if (result == null) @@ -342,9 +342,20 @@ private List>.Enumerator GetEnumeratorImplementat _enumerator = result = _securityManager.ToList(); } } - return result.GetEnumerator(); + return result; + } + + private List>.Enumerator GetEnumeratorImplementation() + { + return GetEnumerable().GetEnumerator(); } + /// + /// Gets all the items in the dictionary + /// + /// All the items in the dictionary + public override IEnumerable> GetItems() => GetEnumerable(); + /// /// Indexer method for the security manager to access the securities objects by their symbol. /// diff --git a/Common/Securities/SecurityPortfolioManager.cs b/Common/Securities/SecurityPortfolioManager.cs index facd13b57ad1..2cbd6fee5b19 100644 --- a/Common/Securities/SecurityPortfolioManager.cs +++ b/Common/Securities/SecurityPortfolioManager.cs @@ -169,7 +169,7 @@ public SecurityPortfolioManager(SecurityManager securityManager, SecurityTransac /// /// String search symbol for the security /// Boolean true if portfolio contains this symbol - public bool ContainsKey(Symbol symbol) + public override bool ContainsKey(Symbol symbol) { return Securities.ContainsKey(symbol); } @@ -229,6 +229,13 @@ public void CopyTo(KeyValuePair[] array, int index) } } + /// + /// Gets all the items in the dictionary + /// + /// All the items in the dictionary + public override IEnumerable> GetItems() => + Securities.GetItems().Select(kvp => KeyValuePair.Create(kvp.Key, kvp.Value.Holdings)); + /// /// Gets an containing the Symbol objects of the . /// diff --git a/Tests/Common/ExtendedDictionaryTests.cs b/Tests/Common/ExtendedDictionaryTests.cs index 1e9d5744832e..cf9caf1afa5a 100644 --- a/Tests/Common/ExtendedDictionaryTests.cs +++ b/Tests/Common/ExtendedDictionaryTests.cs @@ -206,6 +206,11 @@ public override bool Remove(TKey key) { return _data.Remove(key); } + + public override IEnumerable> GetItems() + { + return _data; + } } private class TestSymbolDictionary : TestDictionary From 0f80b2477d1c3630aea4fa22f46fa98b17394ddd Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Fri, 2 May 2025 09:31:23 -0400 Subject: [PATCH 15/15] Update pythonnet version to 2.0.44 --- Algorithm.CSharp/QuantConnect.Algorithm.CSharp.csproj | 2 +- Algorithm.Framework/QuantConnect.Algorithm.Framework.csproj | 2 +- Algorithm.Python/QuantConnect.Algorithm.Python.csproj | 2 +- Algorithm/QuantConnect.Algorithm.csproj | 2 +- AlgorithmFactory/QuantConnect.AlgorithmFactory.csproj | 2 +- Common/QuantConnect.csproj | 2 +- Engine/QuantConnect.Lean.Engine.csproj | 2 +- Indicators/QuantConnect.Indicators.csproj | 2 +- Report/QuantConnect.Report.csproj | 2 +- Research/QuantConnect.Research.csproj | 2 +- Tests/QuantConnect.Tests.csproj | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Algorithm.CSharp/QuantConnect.Algorithm.CSharp.csproj b/Algorithm.CSharp/QuantConnect.Algorithm.CSharp.csproj index f2c1eba0d0ad..33ef01251290 100644 --- a/Algorithm.CSharp/QuantConnect.Algorithm.CSharp.csproj +++ b/Algorithm.CSharp/QuantConnect.Algorithm.CSharp.csproj @@ -32,7 +32,7 @@ portable - + diff --git a/Algorithm.Framework/QuantConnect.Algorithm.Framework.csproj b/Algorithm.Framework/QuantConnect.Algorithm.Framework.csproj index 8f24d1f87ac5..489c19902e38 100644 --- a/Algorithm.Framework/QuantConnect.Algorithm.Framework.csproj +++ b/Algorithm.Framework/QuantConnect.Algorithm.Framework.csproj @@ -29,7 +29,7 @@ LICENSE - + diff --git a/Algorithm.Python/QuantConnect.Algorithm.Python.csproj b/Algorithm.Python/QuantConnect.Algorithm.Python.csproj index 613cfe4b7416..e7237a5620a3 100644 --- a/Algorithm.Python/QuantConnect.Algorithm.Python.csproj +++ b/Algorithm.Python/QuantConnect.Algorithm.Python.csproj @@ -37,7 +37,7 @@ - + diff --git a/Algorithm/QuantConnect.Algorithm.csproj b/Algorithm/QuantConnect.Algorithm.csproj index adb79b0a9486..03881d8892cb 100644 --- a/Algorithm/QuantConnect.Algorithm.csproj +++ b/Algorithm/QuantConnect.Algorithm.csproj @@ -29,7 +29,7 @@ LICENSE - + diff --git a/AlgorithmFactory/QuantConnect.AlgorithmFactory.csproj b/AlgorithmFactory/QuantConnect.AlgorithmFactory.csproj index d4e994e4040b..9dd8b33e2c61 100644 --- a/AlgorithmFactory/QuantConnect.AlgorithmFactory.csproj +++ b/AlgorithmFactory/QuantConnect.AlgorithmFactory.csproj @@ -28,7 +28,7 @@ LICENSE - + diff --git a/Common/QuantConnect.csproj b/Common/QuantConnect.csproj index 09d57bce1c10..f58a3eeb3c76 100644 --- a/Common/QuantConnect.csproj +++ b/Common/QuantConnect.csproj @@ -35,7 +35,7 @@ - + diff --git a/Engine/QuantConnect.Lean.Engine.csproj b/Engine/QuantConnect.Lean.Engine.csproj index a995d76fb836..b8fdc593b9a2 100644 --- a/Engine/QuantConnect.Lean.Engine.csproj +++ b/Engine/QuantConnect.Lean.Engine.csproj @@ -41,7 +41,7 @@ - + diff --git a/Indicators/QuantConnect.Indicators.csproj b/Indicators/QuantConnect.Indicators.csproj index 021f3502dd94..f7d70dc72026 100644 --- a/Indicators/QuantConnect.Indicators.csproj +++ b/Indicators/QuantConnect.Indicators.csproj @@ -31,7 +31,7 @@ - + diff --git a/Report/QuantConnect.Report.csproj b/Report/QuantConnect.Report.csproj index 3326e91cefb4..c4151f2ec9aa 100644 --- a/Report/QuantConnect.Report.csproj +++ b/Report/QuantConnect.Report.csproj @@ -39,7 +39,7 @@ LICENSE - + diff --git a/Research/QuantConnect.Research.csproj b/Research/QuantConnect.Research.csproj index af5d0331bee1..872bbcd1d67b 100644 --- a/Research/QuantConnect.Research.csproj +++ b/Research/QuantConnect.Research.csproj @@ -34,7 +34,7 @@ - + diff --git a/Tests/QuantConnect.Tests.csproj b/Tests/QuantConnect.Tests.csproj index 18c7193af9f4..0f0762d3a859 100644 --- a/Tests/QuantConnect.Tests.csproj +++ b/Tests/QuantConnect.Tests.csproj @@ -31,7 +31,7 @@ - +