/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Linq; using QuantConnect.Securities; using System.Collections.Generic; namespace QuantConnect.Brokerages { /// /// Provides the mapping between Lean symbols and brokerage symbols using the symbol properties database /// public class SymbolPropertiesDatabaseSymbolMapper : ISymbolMapper { private readonly string _market; // map Lean symbols to symbol properties private Dictionary _symbolPropertiesMap; // map brokerage symbols to Lean symbols we do it per security type because they could overlap, for example binance futures and spot private Dictionary> _symbolMap; // Timestamp of the last successful reload private DateTime _lastReloadTime; // Minimum time between reloads private static readonly TimeSpan _minReloadInterval = TimeSpan.FromMinutes(15); /// /// Creates a new instance of the class. /// /// The Lean market public SymbolPropertiesDatabaseSymbolMapper(string market) { _market = market; TryRefreshMappings(); } /// /// Attempts to refresh the mappings if the minimum reload interval has passed /// /// True if the mappings were refreshed, false otherwise private bool TryRefreshMappings() { // Check if enough time has passed since the last reload if (DateTime.UtcNow - _lastReloadTime < _minReloadInterval) { return false; } var symbolPropertiesList = SymbolPropertiesDatabase .FromDataFolder() .GetSymbolPropertiesList(_market) .Where(x => !string.IsNullOrWhiteSpace(x.Value.MarketTicker)) .ToList(); var symbolPropertiesMap = symbolPropertiesList .ToDictionary( x => Symbol.Create(x.Key.Symbol, x.Key.SecurityType, x.Key.Market), x => x.Value); var symbolMap = new Dictionary>(); foreach (var group in symbolPropertiesMap.GroupBy(x => x.Key.SecurityType)) { symbolMap[group.Key] = group.ToDictionary( x => x.Value.MarketTicker, x => x.Key); } _symbolPropertiesMap = symbolPropertiesMap; _symbolMap = symbolMap; // Update the last reload time _lastReloadTime = DateTime.UtcNow; return true; } /// /// Converts a Lean symbol instance to a brokerage symbol /// /// A Lean symbol instance /// The brokerage symbol public string GetBrokerageSymbol(Symbol symbol) { if (symbol == null || string.IsNullOrWhiteSpace(symbol.Value)) { throw new ArgumentException($"Invalid symbol: {(symbol == null ? "null" : symbol.Value)}"); } if (symbol.ID.Market != _market) { throw new ArgumentException($"Invalid market: {symbol.ID.Market}"); } // First attempt with current mappings if (!_symbolPropertiesMap.TryGetValue(symbol, out var symbolProperties)) { // If not found, try to refresh the mappings and check again if (!TryRefreshMappings() || !_symbolPropertiesMap.TryGetValue(symbol, out symbolProperties)) { throw new ArgumentException($"Unknown symbol: {symbol.Value}/{symbol.SecurityType}/{symbol.ID.Market}"); } } if (string.IsNullOrWhiteSpace(symbolProperties.MarketTicker)) { throw new ArgumentException($"MarketTicker not found in database for symbol: {symbol.Value}"); } return symbolProperties.MarketTicker; } /// /// Converts a brokerage symbol to a Lean symbol instance /// /// The brokerage symbol /// The security type /// The market /// Expiration date of the security(if applicable) /// The strike of the security (if applicable) /// The option right of the security (if applicable) /// A new Lean Symbol instance public Symbol GetLeanSymbol(string brokerageSymbol, SecurityType securityType, string market, DateTime expirationDate = default(DateTime), decimal strike = 0, OptionRight optionRight = OptionRight.Call) { if (string.IsNullOrWhiteSpace(brokerageSymbol)) { throw new ArgumentException($"Invalid brokerage symbol: {brokerageSymbol}"); } if (market != _market) { throw new ArgumentException($"Invalid market: {market}"); } // First attempt with current mappings if (!TryGetLeanSymbol(brokerageSymbol, securityType, out var symbol)) { // If not found, try to refresh and check again if (!TryRefreshMappings() || !TryGetLeanSymbol(brokerageSymbol, securityType, out symbol)) { throw new ArgumentException($"Unknown brokerage symbol: {brokerageSymbol}/{securityType}"); } } return symbol; } private bool TryGetLeanSymbol(string brokerageSymbol, SecurityType securityType, out Symbol symbol) { symbol = null; return _symbolMap.TryGetValue(securityType, out var symbols) && symbols.TryGetValue(brokerageSymbol, out symbol); } /// /// Checks if the Lean symbol is supported by the brokerage /// /// The Lean symbol /// True if the brokerage supports the symbol public bool IsKnownLeanSymbol(Symbol symbol) { if (string.IsNullOrWhiteSpace(symbol?.Value)) { return false; } // First check current mappings if (_symbolPropertiesMap.ContainsKey(symbol)) { return true; } // If not found, try to refresh and check again return TryRefreshMappings() && _symbolPropertiesMap.ContainsKey(symbol); } /// /// Returns the security type for a brokerage symbol /// /// The brokerage symbol /// The security type public SecurityType GetBrokerageSecurityType(string brokerageSymbol) { if (string.IsNullOrWhiteSpace(brokerageSymbol)) { throw new ArgumentException($"Invalid brokerage symbol: {brokerageSymbol}"); } // First attempt with current mappings var result = GetMatchingSymbols(brokerageSymbol); if (result.Count == 0) { // If not found, try to refresh and check again if (!TryRefreshMappings() || (result = GetMatchingSymbols(brokerageSymbol)).Count == 0) { throw new ArgumentException($"Unknown brokerage symbol: {brokerageSymbol}"); } } if (result.Count > 1) { throw new ArgumentException($"Found multiple brokerage symbols: {string.Join(",", result)}"); } return result[0].SecurityType; } private List GetMatchingSymbols(string brokerageSymbol) { return _symbolMap.Select(kvp => { kvp.Value.TryGetValue(brokerageSymbol, out var symbol); return symbol; }).Where(s => s != null).ToList(); } /// /// Checks if the symbol is supported by the brokerage /// /// The brokerage symbol /// True if the brokerage supports the symbol public bool IsKnownBrokerageSymbol(string brokerageSymbol) { if (string.IsNullOrWhiteSpace(brokerageSymbol)) { return false; } if (_symbolMap.Any(kvp => kvp.Value.ContainsKey(brokerageSymbol))) { return true; } // If not found, try to refresh and check again return TryRefreshMappings() && _symbolMap.Any(kvp => kvp.Value.ContainsKey(brokerageSymbol)); } } }