diff --git a/Algorithm.CSharp/QuantConnect.Algorithm.CSharp.csproj b/Algorithm.CSharp/QuantConnect.Algorithm.CSharp.csproj
index 975f634aa089..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.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.Framework/QuantConnect.Algorithm.Framework.csproj b/Algorithm.Framework/QuantConnect.Algorithm.Framework.csproj
index 964576ff57b4..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/DynamicSecurityDataRegressionAlgorithm.py b/Algorithm.Python/DynamicSecurityDataRegressionAlgorithm.py
index 5f7a08e1a3ab..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 *
###
@@ -28,24 +27,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..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.
@@ -25,13 +24,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 +38,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")
diff --git a/Algorithm.Python/QuantConnect.Algorithm.Python.csproj b/Algorithm.Python/QuantConnect.Algorithm.Python.csproj
index 9b566b925e35..e7237a5620a3 100644
--- a/Algorithm.Python/QuantConnect.Algorithm.Python.csproj
+++ b/Algorithm.Python/QuantConnect.Algorithm.Python.csproj
@@ -37,7 +37,7 @@
-
+
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)
diff --git a/Algorithm/QuantConnect.Algorithm.csproj b/Algorithm/QuantConnect.Algorithm.csproj
index 4b36a3533736..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 cb5ec33a6424..9dd8b33e2c61 100644
--- a/AlgorithmFactory/QuantConnect.AlgorithmFactory.csproj
+++ b/AlgorithmFactory/QuantConnect.AlgorithmFactory.csproj
@@ -28,7 +28,7 @@
LICENSE
-
+
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
///
diff --git a/Common/Data/Market/DataDictionary.cs b/Common/Data/Market/DataDictionary.cs
index 22b4c1e4404b..5f2dc03d179f 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();
@@ -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,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 .
///
@@ -226,6 +232,7 @@ public override T this[Symbol symbol]
{
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
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/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..d7009e3342f4 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;
@@ -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; }
}
@@ -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
@@ -332,8 +339,15 @@ public override dynamic this[Symbol symbol]
{
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
+ {
+ // 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.");
+ }
}
///
@@ -511,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 c0d3d1e4f969..f5a8458b13b0 100644
--- a/Common/ExtendedDictionary.cs
+++ b/Common/ExtendedDictionary.cs
@@ -24,11 +24,18 @@
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
{
+ ///
+ /// Gets the number of elements contained in the dictionary
+ ///
+ public abstract int Count { get; }
+
///
/// Removes all items from the .
///
@@ -43,21 +50,39 @@ 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);
+
+ ///
+ /// 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 _);
+ }
+
+ ///
+ /// Gets all the items in the dictionary
+ ///
+ /// All the items in the dictionary
+ public abstract IEnumerable> GetItems();
///
- /// 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 +90,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 +99,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 +116,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 +142,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 +154,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 +169,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,35 +199,29 @@ 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())
{
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(new PyObject[] { 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;
}
}
///
- /// 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 +230,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 +305,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 +313,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()
{
@@ -349,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/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/QuantConnect.csproj b/Common/QuantConnect.csproj
index 826d79c646b8..f58a3eeb3c76 100644
--- a/Common/QuantConnect.csproj
+++ b/Common/QuantConnect.csproj
@@ -35,7 +35,7 @@
-
+
diff --git a/Common/Securities/CashBook.cs b/Common/Securities/CashBook.cs
index 6727d8b57f30..620950e03bac 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,24 @@ 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 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 ae90511924f4..20ec848bcaa6 100644
--- a/Common/Securities/Positions/PositionGroupCollection.cs
+++ b/Common/Securities/Positions/PositionGroupCollection.cs
@@ -50,10 +50,22 @@ 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;
+ 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 20c8521ba129..2e26ad1d6744 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
@@ -42,7 +42,6 @@ public class SecurityPositionGroupModel
///
protected virtual IPositionGroupBuyingPowerModel PositionGroupBuyingPowerModel { get; } = new SecurityPositionGroupBuyingPowerModel();
-
///
/// Gets the set of currently resolved position groups
///
@@ -64,6 +63,27 @@ 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
+ ///
+ protected override IEnumerable GetKeys => Groups.Keys;
+
+ ///
+ /// Gets all the available position groups
+ ///
+ 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
///
@@ -122,7 +142,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 +237,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);
+ }
}
}
diff --git a/Common/Securities/SecurityManager.cs b/Common/Securities/SecurityManager.cs
index 13ad9b6cc519..eba37583b819 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
@@ -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
{
@@ -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.
///
@@ -360,6 +371,7 @@ public override Security this[Symbol symbol]
{
if (!_completeSecuritiesCollection.TryGetValue(symbol, out security))
{
+ CheckForImplicitlyCreatedSymbol(symbol);
throw new KeyNotFoundException(Messages.SecurityManager.SymbolNotFoundInSecurities(symbol));
}
}
diff --git a/Common/Securities/SecurityPortfolioManager.cs b/Common/Securities/SecurityPortfolioManager.cs
index e0873175d639..2cbd6fee5b19 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;
@@ -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
{
@@ -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 .
///
@@ -597,8 +604,14 @@ 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
+ {
+ return Securities[symbol].Holdings;
+ }
+ set
+ {
+ Securities[symbol].Holdings = value;
+ }
}
///
diff --git a/Engine/QuantConnect.Lean.Engine.csproj b/Engine/QuantConnect.Lean.Engine.csproj
index 3f87eecfbf9b..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 e3e05e2ba5e8..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 e40043449b21..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 cdab8dd9bdea..872bbcd1d67b 100644
--- a/Research/QuantConnect.Research.csproj
+++ b/Research/QuantConnect.Research.csproj
@@ -34,7 +34,7 @@
-
+
diff --git a/Tests/Common/ExtendedDictionaryTests.cs b/Tests/Common/ExtendedDictionaryTests.cs
index 69b0b277d57d..cf9caf1afa5a 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,162 @@ public void RunPythonDictionaryFeatureRegressionAlgorithm()
parameter.ExpectedFinalStatus,
initialCash: 100000);
}
+
+ [Test]
+ public void ExtendedDictionaryBehavesAsPythonDictionary()
+ {
+ using var _ = Py.GIL();
+
+ var module = PyModule.FromString("ExtendedDictionaryBehavesAsPythonDictionary",
+ @"
+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());
+ }
+
+ [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();
+
+ public override int Count => _data.Count;
+
+ public override bool IsReadOnly => false;
+
+ public override TValue this[TKey key]
+ {
+ get => _data[key];
+ set => _data[key] = value;
+ }
+
+ protected override IEnumerable GetKeys => _data.Keys;
+
+ protected override IEnumerable GetValues => _data.Values;
+
+ public override bool TryGetValue(TKey key, out TValue value)
+ {
+ return _data.TryGetValue(key, out value);
+ }
+
+ public override bool ContainsKey(TKey key)
+ {
+ return _data.ContainsKey(key);
+ }
+
+ public override bool Remove(TKey key)
+ {
+ return _data.Remove(key);
+ }
+
+ public override IEnumerable> GetItems()
+ {
+ return _data;
+ }
+ }
+
+ private class TestSymbolDictionary : TestDictionary
+ {
+
+ }
}
}
diff --git a/Tests/QuantConnect.Tests.csproj b/Tests/QuantConnect.Tests.csproj
index acb371684612..0f0762d3a859 100644
--- a/Tests/QuantConnect.Tests.csproj
+++ b/Tests/QuantConnect.Tests.csproj
@@ -31,7 +31,7 @@
-
+