From d2fc0b07374bf7d8d46bedff57386ef076cd24a4 Mon Sep 17 00:00:00 2001 From: Martin-Molinero Date: Tue, 12 Mar 2024 15:36:50 -0300 Subject: [PATCH] Standarize implementation (#5) * Standarize implementation - Standarize implementation. See QuantConnect/Lean#7837 - Adjust and fix example algorithms * Minor tz tweak --- CoinGeckoUniverse.cs | 21 ++++++++++++++------- CoinGeckoUniverseSelectionAlgorithm.cs | 20 +++++++++++++++++--- CoinGeckoUniverseSelectionAlgorithm.py | 12 ++++++++++-- CoinGeckoUniverseSelectionModel.cs | 12 ++++++------ CoinGeckoUniverseSelectionModelAlgorithm.py | 2 +- QuantConnect.DataSource.csproj | 2 +- 6 files changed, 49 insertions(+), 20 deletions(-) diff --git a/CoinGeckoUniverse.cs b/CoinGeckoUniverse.cs index da41ea0..439054a 100644 --- a/CoinGeckoUniverse.cs +++ b/CoinGeckoUniverse.cs @@ -14,7 +14,9 @@ * */ +using NodaTime; using QuantConnect.Data; +using QuantConnect.Data.UniverseSelection; using System; using System.Globalization; using System.IO; @@ -24,7 +26,7 @@ namespace QuantConnect.DataSource /// /// Universe Selection Data for Coin Gecko data which contains Price, Volume, and Market Cap in USD for cryptocurrencies /// - public class CoinGeckoUniverse : CoinGecko + public class CoinGeckoUniverse : BaseDataCollection { /// /// Return the URL string source of the file. This will be converted to a stream @@ -43,7 +45,8 @@ public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, "universe", $"{date.ToStringInvariant(DateFormat.EightCharacter)}.csv" ), - SubscriptionTransportMedium.LocalFile + SubscriptionTransportMedium.LocalFile, + FileFormat.FoldingCollection ); } @@ -59,9 +62,9 @@ public override BaseData Reader(SubscriptionDataConfig config, string line, Date { var csv = line.Split(','); var coin = csv[0].ToUpperInvariant(); - var sid = SecurityIdentifier.GenerateBase(typeof(CoinGeckoUniverse), coin, Market.USA); + var sid = SecurityIdentifier.GenerateBase(typeof(CoinGecko), coin, Market.USA); - return new CoinGeckoUniverse + return new CoinGecko { Symbol = new Symbol(sid, coin), EndTime = date, @@ -82,10 +85,14 @@ public override BaseData Clone() Symbol = Symbol, Time = Time, EndTime = EndTime, - MarketCap = MarketCap, - Value = Value, - Volume = Volume + Data = Data }; } + + /// + /// Specifies the data time zone for this data type. This is useful for custom data types + /// + /// The of this data type + public override DateTimeZone DataTimeZone() => DateTimeZone.Utc; } } diff --git a/CoinGeckoUniverseSelectionAlgorithm.cs b/CoinGeckoUniverseSelectionAlgorithm.cs index d29fa83..6143b5f 100644 --- a/CoinGeckoUniverseSelectionAlgorithm.cs +++ b/CoinGeckoUniverseSelectionAlgorithm.cs @@ -38,18 +38,32 @@ public override void Initialize() SetCash(100000); // add a custom universe data source (defaults to usa-equity) - AddUniverse("CoinGeckoUniverse", Resolution.Daily, data => + var universe = AddUniverse("CoinGeckoUniverse", Resolution.Daily, data => { - foreach (var datum in data) + foreach (CoinGecko datum in data) { Debug($"{datum.Coin},{datum.MarketCap},{datum.Price}"); } // define our selection criteria - return (from d in data + return (from CoinGecko d in data orderby d.MarketCap descending select d.CreateSymbol(Market.GDAX, "USD", SecurityType.Crypto)).Take(3); }); + + var history = History(universe, 2).ToList(); + if (history.Count != 2) + { + throw new Exception($"Unexpected historical data count!"); + } + foreach (var dataForDate in history) + { + var coarseData = dataForDate.ToList(); + if (coarseData.Count < 2) + { + throw new Exception($"Unexpected historical universe data!"); + } + } } /// diff --git a/CoinGeckoUniverseSelectionAlgorithm.py b/CoinGeckoUniverseSelectionAlgorithm.py index 687d2dc..3cc0a1b 100644 --- a/CoinGeckoUniverseSelectionAlgorithm.py +++ b/CoinGeckoUniverseSelectionAlgorithm.py @@ -16,7 +16,7 @@ ### ### Example algorithm using the custom data type as a source of alpha ### -class CoinGeckoUniverseSelectionAlgorithm(QCAlgorithm): +class CoinGeckoUniverseSelectionAlgorithm(QCAlgorithm): def Initialize(self): ''' Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized. ''' @@ -28,7 +28,15 @@ def Initialize(self): self.SetCash(100000); # add a custom universe data source - self.AddUniverse(CoinGeckoUniverse, "CoinGeckoUniverse", Resolution.Daily, self.UniverseSelection) + universe = self.AddUniverse(CoinGeckoUniverse, "CoinGeckoUniverse", Resolution.Daily, self.UniverseSelection) + + history = self.History(universe, TimeSpan(2, 0, 0, 0)) + if len(history) != 2: + raise ValueError(f"Unexpected history count {len(history)}! Expected 2") + + for dataForDate in history: + if len(dataForDate) < 2: + raise ValueError(f"Unexpected historical universe data!") def UniverseSelection(self, data): ''' Selected the securities diff --git a/CoinGeckoUniverseSelectionModel.cs b/CoinGeckoUniverseSelectionModel.cs index b00ac84..d8e87b7 100644 --- a/CoinGeckoUniverseSelectionModel.cs +++ b/CoinGeckoUniverseSelectionModel.cs @@ -33,7 +33,7 @@ public class CoinGeckoUniverseSelectionModel : UniverseSelectionModel { private Universe _universe; private UniverseSettings _universeSettings; - private readonly Func, IEnumerable> _selector; + private readonly Func, IEnumerable> _selector; /// /// Initializes a new instance of the class @@ -41,7 +41,7 @@ public class CoinGeckoUniverseSelectionModel : UniverseSelectionModel /// The settings used for new subscriptions generated by this universe /// Returns the symbols that should be included in the universe public CoinGeckoUniverseSelectionModel( - Func, IEnumerable> selector, + Func, IEnumerable> selector, UniverseSettings universeSettings = null) { _universeSettings = universeSettings; @@ -57,7 +57,7 @@ public CoinGeckoUniverseSelectionModel( PyObject selector, UniverseSettings universeSettings = null ) - : this(selector.ConvertPythonUniverseFilterFunction(), universeSettings) + : this(selector.ConvertPythonUniverseFilterFunction(), universeSettings) { } @@ -66,7 +66,7 @@ public CoinGeckoUniverseSelectionModel( /// /// The settings used for new subscriptions generated by this universe public CoinGeckoUniverseSelectionModel(UniverseSettings universeSettings = null) - : this((Func, IEnumerable>)null, universeSettings) + : this((Func, IEnumerable>)null, universeSettings) { _universeSettings = universeSettings; } @@ -78,7 +78,7 @@ public CoinGeckoUniverseSelectionModel(UniverseSettings universeSettings = null) /// The CoinGecko Universe data used to perform filtering /// The Selector cannot be null /// An enumerable of symbols passing the filter - public virtual IEnumerable Selector(QCAlgorithm algorithm, IEnumerable data) + public virtual IEnumerable Selector(QCAlgorithm algorithm, IEnumerable data) { if (_selector != null) { @@ -118,7 +118,7 @@ public override IEnumerable CreateUniverses(QCAlgorithm algorithm) .SetEntryAlwaysOpen(config.Market, ticker, config.SecurityType, config.DataTimeZone); _universe = new FuncUniverse(config, _universeSettings ??= algorithm.UniverseSettings, - d => Selector(algorithm, d.OfType())); + d => Selector(algorithm, d.OfType())); yield return _universe; } diff --git a/CoinGeckoUniverseSelectionModelAlgorithm.py b/CoinGeckoUniverseSelectionModelAlgorithm.py index c3b325d..d2f281c 100644 --- a/CoinGeckoUniverseSelectionModelAlgorithm.py +++ b/CoinGeckoUniverseSelectionModelAlgorithm.py @@ -16,7 +16,7 @@ ### ### Example algorithm using the custom data type as a source of alpha ### -class CoinGeckoUniverseSelectionModelAlgorithm(QCAlgorithm): +class CoinGeckoUniverseSelectionModelAlgorithm(QCAlgorithm): def Initialize(self): ''' Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized. ''' diff --git a/QuantConnect.DataSource.csproj b/QuantConnect.DataSource.csproj index c6e16b3..b666f50 100644 --- a/QuantConnect.DataSource.csproj +++ b/QuantConnect.DataSource.csproj @@ -10,7 +10,7 @@ - +