diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 921dda23d..3b7f7d542 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -23,7 +23,7 @@ body: render: csharp placeholder: | // example (put your own code here) - IEnumerable results = quotes.GetEma(14); + IReadOnlyList results = quotes.GetEma(14); validations: required: false - type: textarea diff --git a/.github/workflows/test-performance.yml b/.github/workflows/test-performance.yml index 857f71949..6682f6b06 100644 --- a/.github/workflows/test-performance.yml +++ b/.github/workflows/test-performance.yml @@ -47,7 +47,7 @@ jobs: run: dotnet run -c Release - name: Save test results - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: test-summaries path: tests/performance/BenchmarkDotNet.Artifacts/results @@ -62,3 +62,9 @@ jobs: echo "## Stream indicators (with Quote caching)" >> $GITHUB_STEP_SUMMARY cat Performance.StreamIndicators-report-github.md >> $GITHUB_STEP_SUMMARY + + echo "## Incremental indicators (with buffer)" >> $GITHUB_STEP_SUMMARY + cat Performance.Incrementals-report-github.md >> $GITHUB_STEP_SUMMARY + + echo "## Utilities" >> $GITHUB_STEP_SUMMARY + cat Performance.Utility-report-github.md >> $GITHUB_STEP_SUMMARY diff --git a/.gitignore b/.gitignore index 7a97cd5dc..a517802a1 100644 --- a/.gitignore +++ b/.gitignore @@ -111,12 +111,13 @@ $tf/ # Guidance Automation Toolkit *.gpState -# ReSharper is a .NET coding add-in +# ReSharper IDE extension _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user +*.DotSettings -# JustCode is a .NET coding add-in +# JustCode IDE extension .JustCode # TeamCity is a build add-in @@ -218,7 +219,7 @@ ClientBin/ *.publishsettings orleans.codegen.cs -# Including strong name files can present a security risk +# Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk @@ -314,7 +315,7 @@ __pycache__/ # OpenCover UI analysis results OpenCover/ -# Azure Stream Analytics local run output +# Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log @@ -323,11 +324,11 @@ ASALocalRun/ # NVidia Nsight GPU debugger configuration file *.nvuser -# MFractors (Xamarin productivity tool) working folder +# MFractors (Xamarin productivity tool) working folder .mfractor/ # Jekyll site _site/ # zip artifacts -.DS_Store \ No newline at end of file +.DS_Store diff --git a/docs/_data/aliases.yml b/docs/_data/aliases.yml index 9950409f0..5d1bf4b87 100644 --- a/docs/_data/aliases.yml +++ b/docs/_data/aliases.yml @@ -59,23 +59,23 @@ type: price-characteristic - title: HL2 - permalink: /indicators/BasicQuote/ + permalink: /indicators/quotepart/ type: price-transform - title: HLC3 - permalink: /indicators/BasicQuote/ + permalink: /indicators/quotepart/ type: price-transform - title: OC2 - permalink: /indicators/BasicQuote/ + permalink: /indicators/quotepart/ type: price-transform - title: OHL3 - permalink: /indicators/BasicQuote/ + permalink: /indicators/quotepart/ type: price-transform - title: OHLC4 - permalink: /indicators/BasicQuote/ + permalink: /indicators/quotepart/ type: price-transform - title: Price Channels diff --git a/docs/_indicators/AtrStop.md b/docs/_indicators/AtrStop.md index e2ef0271e..c1db90504 100644 --- a/docs/_indicators/AtrStop.md +++ b/docs/_indicators/AtrStop.md @@ -57,11 +57,11 @@ IReadOnlyList **`Timestamp`** _`DateTime`_ - date from evaluated `TQuote` -**`AtrStop`** _`decimal`_ - ATR Trailing Stop line contains both Upper and Lower segments +**`AtrStop`** _`double`_ - ATR Trailing Stop line contains both Upper and Lower segments -**`BuyStop`** _`decimal`_ - Upper band only (green) +**`BuyStop`** _`double`_ - Upper band only (green) -**`SellStop`** _`decimal`_ - Lower band only (red) +**`SellStop`** _`double`_ - Lower band only (red) **`Atr`** _`double`_ - Average True Range diff --git a/docs/_indicators/Beta.md b/docs/_indicators/Beta.md index 3dc49393e..b41d716cb 100644 --- a/docs/_indicators/Beta.md +++ b/docs/_indicators/Beta.md @@ -22,7 +22,7 @@ IReadOnlyList results = quotesEval ## Parameters -**`quotesMarket`** _`IEnumerable`_ - [Historical quotes]({{site.baseurl}}/guide/#historical-quotes) market data should be at any consistent frequency (day, hour, minute, etc). This `market` quotes will be used to establish the baseline. +**`quotesMarket`** _`IReadOnlyList`_ - [Historical quotes]({{site.baseurl}}/guide/#historical-quotes) market data should be at any consistent frequency (day, hour, minute, etc). This `market` quotes will be used to establish the baseline. **`lookbackPeriods`** _`int`_ - Number of periods (`N`) in the lookback window. Must be greater than 0 to calculate; however we suggest a larger period for statistically appropriate sample size and especially when using Beta +/-. diff --git a/docs/_indicators/Correlation.md b/docs/_indicators/Correlation.md index 70ef31ce4..740374667 100644 --- a/docs/_indicators/Correlation.md +++ b/docs/_indicators/Correlation.md @@ -30,7 +30,7 @@ IReadOnlyList results = You must have at least `N` periods for both versions of `quotes` to cover the warmup periods. Mismatch histories will produce a `InvalidQuotesException`. Historical price quotes should have a consistent frequency (day, hour, minute, etc). -`quotesA` is an `IEnumerable` collection of historical price quotes. It should have a consistent frequency (day, hour, minute, etc). See [the Guide]({{site.baseurl}}/guide/#historical-quotes) for more information. +`quotesA` is an `IReadOnlyList` collection of historical price quotes. It should have a consistent frequency (day, hour, minute, etc). See [the Guide]({{site.baseurl}}/guide/#historical-quotes) for more information. ## Response diff --git a/docs/_indicators/Use.md b/docs/_indicators/QuotePart.md similarity index 84% rename from docs/_indicators/Use.md rename to docs/_indicators/QuotePart.md index 236eb9ad8..d4f7491c8 100644 --- a/docs/_indicators/Use.md +++ b/docs/_indicators/QuotePart.md @@ -1,19 +1,26 @@ --- -title: Basic quote transform +title: Quote parts and basic transforms description: Basic quote transforms (e.g. HL2, OHL3, etc.) and isolation of individual price quote candle parts from a full OHLCV quote. -permalink: /indicators/Use/ +permalink: /indicators/quotepart/ +redirect-from: + - /indicators/Use/ + - /indicators/BasicQuote/ type: price-transform layout: indicator --- # {{ page.title }} -Returns a reusable (chainable) basic quote transform (e.g. HL2, OHL3, etc.) by isolating a single value or calculated value from the full OHLCV quote candle parts. +Returns a reusable (chainable) basic quote transform (e.g. HL2, OHL3, etc.) by isolating a single component part value or calculated value from the full OHLCV quote candle parts. ```csharp // C# usage syntax IReadOnlyList results = quotes.Use(candlePart); + +// alternate syntax +IReadOnlyList results = + quotes.GetQuotePart(candlePart); ``` ## Parameters diff --git a/docs/examples/CustomIndicators/AtrWma.cs b/docs/examples/CustomIndicators/AtrWma.cs index e1cf570cd..9414070b2 100644 --- a/docs/examples/CustomIndicators/AtrWma.cs +++ b/docs/examples/CustomIndicators/AtrWma.cs @@ -18,7 +18,7 @@ public static class CustomIndicators { // Custom ATR WMA calculation public static IReadOnlyList GetAtrWma( - this IEnumerable quotes, + this IReadOnlyList quotes, int lookbackPeriods) where TQuote : IQuote { diff --git a/docs/pages/guide.md b/docs/pages/guide.md index 6344988c6..40b1adad4 100644 --- a/docs/pages/guide.md +++ b/docs/pages/guide.md @@ -43,7 +43,7 @@ Most indicators require that you provide historical quote data and additional co You must get historical quotes from your own market data provider. For clarification, the `GetQuotesFromFeed()` method shown in the example below **is not part of this library**, but rather an example to represent your own acquisition of historical quotes. -Historical price data can be provided as a `List`, `IEnumerable`, or `ICollection` of the `Quote` class ([see below](#historical-quotes)); however, it can also be supplied as a generic [custom TQuote type](#using-custom-quote-classes) if you prefer to use your own quote model. +Historical price data can be provided as a `List`, `IReadOnlyList`, or `ICollection` of the `Quote` class ([see below](#historical-quotes)); however, it can also be supplied as a generic [custom TQuote type](#using-custom-quote-classes) if you prefer to use your own quote model. For additional configuration parameters, default values are provided when there is an industry standard. You can, of course, override these and provide your own values. @@ -57,7 +57,7 @@ using Skender.Stock.Indicators; [..] // fetch historical quotes from your feed (your method) -IEnumerable quotes = GetQuotesFromFeed("MSFT"); +IReadOnlyList quotes = GetQuotesFromFeed("MSFT"); // calculate 20-period SMA IReadOnlyList results = quotes @@ -89,7 +89,7 @@ More examples available: ## Historical quotes -You must provide historical price quotes to the library in the standard OHLCV `IEnumerable` or a compatible `List` or `ICollection` format. It should have a consistent period frequency (day, hour, minute, etc). See [using custom quote classes](#using-custom-quote-classes) if you prefer to use your own quote class. +You must provide historical price quotes to the library in the standard OHLCV `IReadOnlyList` or a compatible `List` or `ICollection` format. It should have a consistent period frequency (day, hour, minute, etc). See [using custom quote classes](#using-custom-quote-classes) if you prefer to use your own quote class. | name | type | notes | -- |-- |-- @@ -194,10 +194,10 @@ public class MyCustomQuote : IQuote ```csharp // USAGE // fetch historical quotes from your favorite feed -IEnumerable myQuotes = GetQuotesFromFeed("MSFT"); +IReadOnlyList myQuotes = GetQuotesFromFeed("MSFT"); // example: get 20-period simple moving average -IEnumerable results = myQuotes.GetSma(20); +IReadOnlyList results = myQuotes.GetSma(20); ``` #### Using custom quote property names @@ -247,7 +247,7 @@ Example: ```csharp // fetch historical quotes from your feed (your method) -IEnumerable quotes = GetQuotesFromFeed("SPY"); +IReadOnlyList quotes = GetQuotesFromFeed("SPY"); // calculate RSI of OBV IReadOnlyList results diff --git a/docs/pages/utilities.md b/docs/pages/utilities.md index 6159b938e..e86794df1 100644 --- a/docs/pages/utilities.md +++ b/docs/pages/utilities.md @@ -30,7 +30,7 @@ var results = quotes ### Sort quotes -`quotes.ToSortedCollection()` sorts any collection of `TQuote` or `ISeries` and returns it as a `Collection` sorted by ascending `Timestamp`. You do not need to sort quotes before using library indicators; however, if you are creating [custom indicators]({{site.baseurl}}/custom-indicators/#content) it's important to analyze `quotes` in a proper sequence. +`quotes.ToSortedList()` sorts any collection of `TQuote` or `ISeries` and returns it as a `IReadOnlyList` sorted by ascending `Timestamp`. You **do need to sort quotes** before using library indicators. ### Resize quote history @@ -38,7 +38,7 @@ var results = quotes ```csharp // aggregate into larger bars -IEnumerable dayBarQuotes = +IReadOnlyList dayBarQuotes = minuteBarQuotes.Aggregate(PeriodSize.Day); ``` @@ -46,7 +46,7 @@ An alternate version of this utility is provided where you can use any native `T ```csharp // alternate usage with TimeSpan -IEnumerable dayBarQuotes = +IReadOnlyList dayBarQuotes = minuteBarQuotes.Aggregate(TimeSpan timeSpan); ``` @@ -87,7 +87,7 @@ IReadOnlyList candles = quotes.ToCandles(); ```csharp // advanced validation -IEnumerable validatedQuotes = quotes.Validate(); +IReadOnlyList validatedQuotes = quotes.Validate(); // and can be used inline with chaining var results = quotes @@ -148,7 +148,7 @@ See [individual indicator pages]({{site.baseurl}}/indicators/#content) for infor ### Sort results -`results.ToSortedCollection()` sorts any collection of indicator results and returns it as a `Collection` sorted by ascending `Timestamp`. Results from the library indicators are already sorted, so you'd only potentially need this if you're creating [custom indicators]({{site.baseurl}}/custom-indicators/#content). +`results.ToSortedList()` sorts any collection of indicator results and returns it as a `IReadOnlyList` sorted by ascending `Timestamp`. Results from the library indicators are already sorted, so you'd only potentially need this if you're creating [custom indicators]({{site.baseurl}}/custom-indicators/#content). ## Utilities for numerical analysis diff --git a/src/_common/BinarySettings.cs b/src/_common/BinarySettings.cs new file mode 100644 index 000000000..6912d3f11 --- /dev/null +++ b/src/_common/BinarySettings.cs @@ -0,0 +1,110 @@ +namespace Skender.Stock.Indicators; + +/// +/// Binary on/off switches for high performance access +/// to behaviors and characteristics. +/// +/// +/// Initializes a new instance of the struct. +/// The Mask parameter is optional and defaults to 0b11111111 where all bits +/// pass through to "combinor" sets. +/// +/// Example of accessing a specific bit: +/// +/// BinarySettings settings = new(0b00000001); // bit 0 is set to 1 +/// bool isBit0Set = settings[0]; // true +/// bool isBit1Set = settings[1]; // false +/// +/// +/// Example of re/setting a specific bit: +/// +/// BinarySettings settings = new(0); +/// settings = settings with { [0] = true }; // set bit 0 to true +/// settings = settings with { [1] = false }; // set bit 1 to false +/// +/// +/// +/// Binary settings. +/// Default is 0b00000000 (binary literal of 0). +/// +/// +/// Mask for settings inheritence. +/// Default is 0b11111111 (binary literal of 255). +/// +public readonly struct BinarySettings( + byte settings, + byte mask = 0b11111111) : IEquatable +{ + public byte Settings { get; } = settings; + public byte Mask { get; } = mask; + + // use default settings (none) and mask + // important: this explicit parameterless ctor required for struct + public BinarySettings() : this(settings: 0b00000000) { } + + /// + /// Gets the value of the bit at the specified index. + /// + /// The index of the bit to get. + /// True if the bit is set; otherwise, false. + public bool this[short index] + => (Settings & (1 << index)) != 0; + + /// + /// Combines the current settings with another instance + /// using a bitwise OR operation, excluding the bits masked by the parent settings. + /// + /// The parent instance to combine with. + /// + /// A new instance with combined settings. + /// Notably, it does not modify the current read-only instance. + /// + /// + /// + /// The mask is used to determine which bits from the parent settings should be excluded + /// during the combination. By default, the mask is set to 0b11111111, meaning all bits + /// are included. If a different mask is provided, the corresponding bits in the parent + /// settings will be excluded based on the mask. + /// + /// In other words, the mask you provide on instantiation will determine which bits are + /// genetic material passed on to the "combinor" child settings. The child settings will inherit the + /// bits from the parent settings that the parent decides to pass along. + /// + /// + /// Usage example (default mask): + /// + /// BinarySettings srcSettings = new(0b01101001); + /// BinarySettings defSettings = new(0b00000010); + /// BinarySettings newSettings = defSettings.Combine(srcSettings); // result: 0b01101011 + /// + /// Using a custom mask: + /// + /// BinarySettings customMaskSettings = new(0b01101001, 0b11111110); // do not pass 0th bit value + /// BinarySettings newSettingsWithCustomMask = defSettings.Combine(customMaskSettings); // result: 0b01101010 + /// + /// + public BinarySettings Combine(BinarySettings parentSettings) + { + // add parent bits according to their mask template + byte maskedParentSettings = (byte)(parentSettings.Settings & parentSettings.Mask); + + // combine the settings + return new BinarySettings((byte)(Settings | maskedParentSettings), parentSettings.Mask); + } + + public override bool Equals(object? obj) + => obj is BinarySettings other && Equals(other); + + public bool Equals(BinarySettings other) + => Settings == other.Settings && Mask == other.Mask; + + public override int GetHashCode() + => HashCode.Combine(Settings, Mask); + + public static bool operator ==(BinarySettings left, BinarySettings right) + => left.Equals(right); + + public static bool operator !=(BinarySettings left, BinarySettings right) + => !(left == right); +} + diff --git a/src/_common/Candles/Candles.Utilities.cs b/src/_common/Candles/Candles.Utilities.cs index 579c4b25b..82d278637 100644 --- a/src/_common/Candles/Candles.Utilities.cs +++ b/src/_common/Candles/Candles.Utilities.cs @@ -3,7 +3,7 @@ namespace Skender.Stock.Indicators; public static partial class Utility { public static IReadOnlyList Condense( - this IEnumerable candleResults) => candleResults + this IReadOnlyList candleResults) => candleResults .Where(candle => candle.Match != Match.None) .ToList(); @@ -19,7 +19,7 @@ public static CandleProperties ToCandle( // convert/sort quotes into candles list public static IReadOnlyList ToCandles( - this IEnumerable quotes) + this IReadOnlyList quotes) where TQuote : IQuote => quotes .Select(x => x.ToCandle()) .OrderBy(x => x.Timestamp) diff --git a/src/_common/Enums.cs b/src/_common/Enums.cs index a1e497564..f254baaea 100644 --- a/src/_common/Enums.cs +++ b/src/_common/Enums.cs @@ -1,50 +1,32 @@ namespace Skender.Stock.Indicators; // SHARED ENUMERATIONS -// note: indicator unique ENUMS specified in indicator models +// note: indicator unique ENUMS filed with their models /// /// Cache action instruction or outcome /// -public enum Act +internal enum Act { /// - /// Adds to end of cache + /// Adds item to end of cache or rebuild if older. /// - AddNew, + Add, /// - /// Adds new item to middle of cache + /// Does nothing to cache (aborted). /// - AddOld, + Ignore, /// - /// Updates existing item in cache + /// Insert item without rebuilding cache. /// - Update, + Insert, /// - /// Deletes existing item in cache + /// Reset and rebuild from marker position. /// - Delete, - - /// - /// Does nothing to cache (aborted) - /// - DoNothing, - - /// - /// Delete from first position of cache - /// without rebuilding or recalculating; - /// as part of the auto-pruning process - /// to maintain maximum cache size. - /// - AutoPrune, // TODO: implement. May also have some integrity checks. - - /// - /// Instruction has not yet been determined - /// - Unknown + Rebuild } /// diff --git a/src/_common/Generics/Pruning.cs b/src/_common/Generics/Pruning.cs index c6d58f089..997957a91 100644 --- a/src/_common/Generics/Pruning.cs +++ b/src/_common/Generics/Pruning.cs @@ -6,7 +6,7 @@ public static partial class Utility { // REMOVE SPECIFIC PERIODS public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable series, + this IReadOnlyList series, int removePeriods) => removePeriods < 0 ? throw new ArgumentOutOfRangeException(nameof(removePeriods), removePeriods, @@ -15,7 +15,7 @@ public static IReadOnlyList RemoveWarmupPeriods( // REMOVE PERIODS internal static List Remove( - this IEnumerable series, + this IReadOnlyList series, int removePeriods) { List seriesList = series.ToList(); diff --git a/src/_common/Generics/Sorting.cs b/src/_common/Generics/Sorting.cs index 6e5ffedf0..56ed632c6 100644 --- a/src/_common/Generics/Sorting.cs +++ b/src/_common/Generics/Sorting.cs @@ -1,28 +1,10 @@ -using System.Collections.ObjectModel; - namespace Skender.Stock.Indicators; // SORTED of SERIES public static partial class Utility { - public static Collection ToSortedCollection( - this IEnumerable series) - where TSeries : ISeries - => series - .OrderBy(x => x.Timestamp) - .ToCollection(); - - // TODO: need it? is it a useful public utility? - [Obsolete("new, never implemented")] - public static IReadOnlyList ToSortedReadOnlyList( - this IEnumerable series) - where TSeries : ISeries - => series - .OrderBy(x => x.Timestamp) - .ToList(); - - internal static List ToSortedList( + public static IReadOnlyList ToSortedList( this IEnumerable series) where TSeries : ISeries => series diff --git a/src/_common/Generics/Transforms.cs b/src/_common/Generics/Transforms.cs index eb2f63415..11e6b3ffd 100644 --- a/src/_common/Generics/Transforms.cs +++ b/src/_common/Generics/Transforms.cs @@ -9,10 +9,7 @@ public static partial class Utility // TO COLLECTION internal static Collection ToCollection(this IEnumerable source) { - if (source is null) - { - throw new ArgumentNullException(nameof(source)); - } + ArgumentNullException.ThrowIfNull(source); Collection collection = [.. source]; diff --git a/src/_common/Globals.cs b/src/_common/Globals.cs index 072c2ce43..620494b62 100644 --- a/src/_common/Globals.cs +++ b/src/_common/Globals.cs @@ -1,24 +1,5 @@ -using System.Globalization; using System.Runtime.CompilerServices; [assembly: CLSCompliant(true)] [assembly: InternalsVisibleTo("Tests.Indicators")] // these test internals [assembly: InternalsVisibleTo("Tests.Performance")] - -namespace Skender.Stock.Indicators; - -/// Technical indicators and overlays. See -/// -/// the Guide for more information. -public static partial class Indicator -{ - private static readonly CultureInfo invCulture = CultureInfo.InvariantCulture; - private static readonly Calendar invCalendar = invCulture.Calendar; - - // Gets the DTFI properties required by GetWeekOfYear. - private static readonly CalendarWeekRule invCalendarWeekRule - = invCulture.DateTimeFormat.CalendarWeekRule; - - private static readonly DayOfWeek invFirstDayOfWeek - = invCulture.DateTimeFormat.FirstDayOfWeek; -} diff --git a/src/_common/Incrementals/IIncremental.cs b/src/_common/Incrementals/IIncremental.cs index f96393d45..90de40106 100644 --- a/src/_common/Incrementals/IIncremental.cs +++ b/src/_common/Incrementals/IIncremental.cs @@ -1,44 +1,52 @@ namespace Skender.Stock.Indicators; -public interface IIncrementalPrice - where TQuote : IQuote +public interface IAddReusable { /// - /// Converts incremental price into + /// Converts an incremental value into /// the next incremental indicator value /// and added it to the list. /// /// Date context - /// Next price value - void Add(DateTime timestamp, double price); + /// Next value + void Add(DateTime timestamp, double value); /// - /// Converts incremental quotes into + /// Converts an incremental reusable value into /// the next incremental indicator value /// and added it to the list. /// - /// Next quote value - void Add(TQuote quote); + /// Next value + void Add(IReusable value); - // TODO: convert ToStreamHub(); + /// + /// Converts batch of reusable values into + /// the next incremental indicator values + /// and added them to the list. + /// + /// + /// Chronologically ordered batch of IReusable info + /// + void Add(IReadOnlyList values); } -public interface IIncrementalQuote - where TQuote : IQuote +public interface IAddQuote { - /// - void Add(TQuote quote); -} + /// + /// Converts an incremental quote into + /// the next incremental indicator value + /// and added it to the list. + /// + /// Next quote value + void Add(IQuote quote); -/// -/// This produces the same results as the equivalent -/// time-series indicator, but is optimized for array-based operations. -/// Since it does not retain a date context, -/// all new values provided to the -/// method are expected to be in chronological order. -/// -public interface IIncrementalValue -{ - /// - void Add(double price); + /// + /// Converts batch of quotes into + /// the next incremental indicator values + /// and added them to the list. + /// + /// + /// Chronologically ordered batch of quotes + /// + void Add(IReadOnlyList quotes); } diff --git a/src/_common/Math/Numerical.cs b/src/_common/Math/Numerical.cs index b9e68e69b..bba184344 100644 --- a/src/_common/Math/Numerical.cs +++ b/src/_common/Math/Numerical.cs @@ -5,13 +5,8 @@ public static class Numerical // STANDARD DEVIATION public static double StdDev(this double[] values) { - // validate parameters - if (values is null) - { - throw new ArgumentNullException( - nameof(values), - "StdDev values cannot be null."); - } + ArgumentNullException.ThrowIfNull( + values, "StdDev values cannot be null."); int n = values.Length; @@ -43,17 +38,8 @@ public static double StdDev(this double[] values) public static double Slope(double[] x, double[] y) { // validate parameters - if (x is null) - { - throw new ArgumentNullException( - nameof(x), "Slope X values cannot be null."); - } - - if (y is null) - { - throw new ArgumentNullException( - nameof(y), "Slope Y values cannot be null."); - } + ArgumentNullException.ThrowIfNull(x, "Slope X values cannot be null."); + ArgumentNullException.ThrowIfNull(y, "Slope Y values cannot be null."); if (x.Length != y.Length) { diff --git a/src/_common/Observables/IStreamCache.cs b/src/_common/Observables/IStreamCache.cs deleted file mode 100644 index 95af2aec6..000000000 --- a/src/_common/Observables/IStreamCache.cs +++ /dev/null @@ -1,138 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STREAM CACHE INTERFACES - -/// -/// Stored value cache for streaming hubs -/// -public interface IStreamCache - where TSeries : ISeries -{ - /// - /// Read-only list of the stored values cache. - /// - IReadOnlyList Results { get; } - - /// - /// The cache and provider failed and is no longer operational. - /// - /// - /// This occurs when there is an overflow condition - /// from a circular chain or - /// when there were too many sequential duplicates. - /// - /// Use - /// to remove this flag. - /// - /// - bool IsFaulted { get; } - - /// - /// Resets the flag and - /// overflow counter. Use this after recovering - /// from an error. - /// - /// - /// You may also need to - /// , - /// , or - /// before resuming. - /// - void ResetFault(); - - /// - /// Try to find index position of the provided timestamp - /// - /// Timestamp to seek - /// - /// Index of timestamp or -1 when not found - /// - /// True if found - bool TryFindIndex(DateTime timestamp, out int index); - - /// - /// Get the cache index based on item equality. - /// - /// - /// Timeseries object to find in cache - /// - /// - /// Disable exception when item is not found - /// - /// Index position - /// - /// When items is not found (should never happen). - /// - int GetIndex(TSeries cachedItem, bool noException); - - /// - /// Get the cache index based on a timestamp. - /// - /// - /// Only use this when you are looking for a point in time - /// without a matching item for context. In most cases - /// is more appropriate. - /// - /// - /// Timestamp of cached item - /// - /// - /// Disable exception when item is not found - /// - /// Index position - /// - /// When timestamp is not found (should never happen). - /// - int GetIndex(DateTime timestamp, bool noException); - - /// - /// Get the first cache index on or after a timestamp. - /// - /// - /// Only use this when you are looking for a point in time - /// without a matching item for context. In most cases - /// is more appropriate. - /// - /// - /// Timestamp of cached item - /// - /// First index position or -1 if not found - int GetInsertIndex(DateTime timestamp); - - /// - /// Deletes all cached time-series records, - /// without restore. When applicable, - /// it will cascade delete commands to subscribers. - /// - /// - /// For observers, if your intention is to rebuild from a provider, - /// use alternate . - /// - void ClearCache(); - - /// - /// Deletes newer cached records from point in time, - /// without restore. When applicable, it will cascade delete - /// commands to subscribers. - /// - /// - /// For observers, if your intention is to rebuild from a provider, - /// use alternate . - /// - /// - /// All periods (inclusive) after this DateTime will be removed. - /// - void ClearCache(DateTime fromTimestamp); - - /// - /// Deletes newer cached records from an index position (inclusive), - /// without restore. When applicable, it will cascade delete - /// commands to subscribers. - /// - /// - /// For observers, if your intention is to rebuild from a provider, - /// use alternate . - /// - /// From index, inclusive - void ClearCache(int fromIndex); -} diff --git a/src/_common/Observables/IStreamHub.cs b/src/_common/Observables/IStreamHub.cs index 245c99346..f73b1250d 100644 --- a/src/_common/Observables/IStreamHub.cs +++ b/src/_common/Observables/IStreamHub.cs @@ -1,123 +1,123 @@ namespace Skender.Stock.Indicators; -// STREAM HUB INTERFACES - -#region hub variants - -public interface IQuoteHub - : IStreamHub, IQuoteProvider, IChainProvider - where TIn : IQuote - where TOut : IQuote; - -/// -public interface IReusableHub - : IStreamHub, IChainProvider - where TIn : ISeries - where TOut : IReusable; - -/// -public interface IResultHub - : IStreamHub - where TIn : ISeries - where TOut : ISeries; -#endregion +// STREAM HUB INTERFACE /// -/// Streaming hub (observer and observable provider). +/// Streaming hub: management of observer +/// and observable indicator data /// -public interface IStreamHub - : IObserver<(Act, TIn, int?)>, IStreamProvider +/// +/// Type of inbound provider data. +/// +/// +/// Type of outbound indicator data. +/// +public interface IStreamHub where TIn : ISeries - where TOut : ISeries { /// - /// Current state of subscription to provider. + /// Read-only list of the stored cache values. /// - bool IsSubscribed { get; } + IReadOnlyList Results { get; } /// - /// Unsubscribe from the data provider. - /// - void Unsubscribe(); - - /// - /// Full reset of the provider subscription. + /// The cache and provider failed and is no longer operational. /// /// - /// This unsubscribes from the provider, - /// clears cache, cascading deletes to subscribers, - /// then re-subscribes to the provider (with rebuild). + /// This occurs when there is an overflow condition + /// from a circular chain or + /// when there were too many sequential duplicates. /// - /// This is also used on startup to invoke provider - /// . + /// Use + /// to remove this flag. /// /// - void Reinitialize(); + bool IsFaulted { get; } /// - /// Reset the entire results cache - /// and rebuild it from provider sources, - /// with cascading updates to subscribers. + /// Resets the flag and + /// overflow counter. Use this after recovering + /// from an error. /// /// - /// This is different from . - /// It does not reset the provider subscription. + /// You may also need to + /// , or + /// . /// - void RebuildCache(); + void ResetFault(); /// - /// Reset the entire results cache from a point in time - /// and rebuilds it from provider sources, - /// with cascading updates to subscribers. + /// Add a single new item. + /// We'll determine if it's new or an update. /// - /// - /// All periods (inclusive) after this date/time will - /// be removed and recalculated. + /// + /// New item to add /// - void RebuildCache(DateTime fromTimestamp); + void Add(TIn newIn); /// - /// Resets the results cache from an index position - /// and rebuilds it from provider sources, - /// with cascading updates to subscribers. + /// Add a batch of new items. + /// We'll determine if they're new or updated. /// - /// - /// All periods (inclusive) after this index position will - /// be removed and recalculated. + /// + /// Batch of new items to add /// - void RebuildCache(int fromIndex); + void Add(IEnumerable batchIn); /// - /// Add a single new observed item. - /// We'll determine if it's new or an update. + /// Insert a new item without rebuilding the cache. /// - /// - /// Observed item to add or update - /// - void Add(TIn newIn); - - /// - /// Add a batch of observed items. - /// We'll determine if they're new or updated. - /// - /// - /// Batch of observed items to add or update + /// + /// This is used in situations when inserting an older item + /// and where newer cache entries do not need to be rebuilt. + /// Typically, this is only used for provider-only hubs. + /// + /// + /// Item to insert /// - void Add(IEnumerable newIn); + void Insert(TIn newIn); /// /// Delete an item from the cache. /// /// Cached item to delete - /// Action taken (outcome) - Act Remove(TOut cachedItem); + /// + void Remove(TOut cachedItem); /// - /// Delete an item from the cache. + /// Delete an item from the cache, from a specific position. /// /// Position in cache to delete - /// Action taken (outcome) - Act RemoveAt(int cacheIndex); + /// + void RemoveAt(int cacheIndex); + + /// + /// Deletes newer cached records from point in time (inclusive). + /// + /// + /// For observers, if your intention is to rebuild from a provider, + /// use alternate . + /// + /// + /// All periods (inclusive) after this DateTime will be removed. + /// + /// + /// Notify subscribers of the delete point. + /// + void RemoveRange(DateTime fromTimestamp, bool notify); + + /// + /// Deletes newer cached records from an index position (inclusive). + /// + /// + /// For observers, if your intention is to rebuild from a provider, + /// use alternate . + /// + /// From index, inclusive + /// + /// Notify subscribers of the delete position. + /// + void RemoveRange(int fromIndex, bool notify); /// /// Returns a short text label for the hub diff --git a/src/_common/Observables/IStreamObservable.cs b/src/_common/Observables/IStreamObservable.cs new file mode 100644 index 000000000..394678c6f --- /dev/null +++ b/src/_common/Observables/IStreamObservable.cs @@ -0,0 +1,108 @@ +namespace Skender.Stock.Indicators; + +// STREAM (OBSERVABLE) INTERFACE + +#region chain and quote variants + +/// +public interface IQuoteProvider : IChainProvider + where T : IQuote +{ + IReadOnlyList Quotes { get; } +} + +/// +public interface IChainProvider : IStreamObservable + where T : IReusable; +#endregion + +/// +/// Provider of data + management of and notification to observing subscribers. +/// +/// +/// The object that provides notification information. +/// +public interface IStreamObservable +{ + /// + /// Hub observable properties and behaviors. + /// + /// + /// This struct holds cumulative overrides for + /// a streaming hub. Observer hubs inherit these values cumulatively when + /// instantiated as a , except where masked. + /// + /// Default settings are a binary set of 0b00000000, where 1 values represent + /// exceptional (atypical) behaviors. + /// + /// + /// + /// 0 + /// + /// Disable observer: a non-observing observable (e.g. base provider). + /// + /// + /// + /// 1 + /// + /// Allow duplicates: + /// bypass rebuild analysis and duplicate prevention when caching new results. + /// + /// + /// + /// 2-7 + /// [unused positions] + /// + /// + /// + BinarySettings Properties { get; } + + /// + /// Current number of subscribers + /// + int ObserverCount { get; } + + /// + /// Provider currently has subscribers + /// + bool HasObservers { get; } + + /// + /// Checks if a specific observer is subscribed + /// + /// + /// Subscriber IStreamObserver reference + /// + /// True if subscribed/registered + bool HasSubscriber(IStreamObserver observer); + + /// + /// Notifies the provider that an observer is to receive notifications. + /// + /// + /// The object that is to receive notifications. + /// + /// + /// A reference to an interface that allows observers + /// to stop receiving notifications before the provider + /// has finished sending them. + /// + IDisposable Subscribe(IStreamObserver observer); + + /// + /// Unsubscribe from the data provider. + /// + /// + bool Unsubscribe(IStreamObserver observer); + + /// + /// Unsubscribe all observers (subscribers) + /// + void EndTransmission(); + + /// + /// Get a readonly reference of the observable cache. + /// + /// Read-only list of cached items. + IReadOnlyList GetCacheRef(); +} diff --git a/src/_common/Observables/IStreamObserver.cs b/src/_common/Observables/IStreamObserver.cs new file mode 100644 index 000000000..32398e668 --- /dev/null +++ b/src/_common/Observables/IStreamObserver.cs @@ -0,0 +1,117 @@ +namespace Skender.Stock.Indicators; + +// STREAM (OBSERVER) INTERFACE + +/// +/// Management of observing + processing of streamed inbound data. +/// +/// +/// The object that provides notification information. +/// +public interface IStreamObserver +{ + /// + /// Current state of subscription to provider. + /// + bool IsSubscribed { get; } + + /// + /// Unsubscribe from the data provider. + /// + void Unsubscribe(); + + /// + /// Provides the observer with new data. + /// + /// + /// The current notification information. + /// + /// + /// Notify subscribers of the new item. + /// + /// + /// Provider index hint, if known. + /// + void OnAdd(T item, bool notify, int? indexHint); + + /// + /// Provides the observer with starting point in timeline + /// to rebuild and cascade to all its own subscribers. + /// + /// + /// Starting point in timeline to rebuild. + /// + void OnChange(DateTime fromTimestamp); + + /// + /// Provides the observer with errors from the provider + /// that have produced a faulted state. + /// + /// + /// An exception with additional information about the error. + /// + void OnError(Exception exception); + + /// + /// Provides the observer with final notice that the data + /// provider has finished sending push-based notifications. + /// + /// + /// Completion indicates that publisher will never send + /// additional data. This is only used for finite data + /// streams; and is different from faulted OnError(). + /// + void OnCompleted(); + + /// + /// Full reset of the provider subscription. + /// + /// + /// This unsubscribes from the provider, + /// rebuilds the cache, resets faulted states, + /// and then re-subscribes to the provider. + /// + /// This is done automatically on hub + /// instantiation, so it's only needed if you + /// want to manually reset the hub. + /// + /// + /// If you only need to rebuild the cache, + /// use instead. + /// + /// + void Reinitialize(); + + /// + /// Resets the entire results cache + /// and rebuilds it from provider sources, + /// with cascading updates to subscribers. + /// + /// + /// This is different from . + /// It does not reset the provider subscription. + /// + void Rebuild(); + + /// + /// Resets the results cache from a point in time + /// and rebuilds it from provider sources, + /// with cascading updates to subscribers. + /// + /// + /// All periods (inclusive) after this date/time + /// will be removed and recalculated. + /// + void Rebuild(DateTime fromTimestamp); + + /// + /// Resets the results cache from an index position + /// and rebuilds it from provider sources, + /// with cascading updates to subscribers. + /// + /// + /// All periods (inclusive) after this index position + /// will be removed and recalculated. + /// + void Rebuild(int fromIndex); +} diff --git a/src/_common/Observables/IStreamProvider.cs b/src/_common/Observables/IStreamProvider.cs deleted file mode 100644 index d6b7a71ac..000000000 --- a/src/_common/Observables/IStreamProvider.cs +++ /dev/null @@ -1,46 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STREAM PROVIDER INTERFACES - -/// -public interface IQuoteProvider - : IChainProvider - where TQuote : IQuote; - -/// -public interface IChainProvider - : IStreamProvider - where TReusable : IReusable; - -/// -/// Streaming provider (observable cache) -/// -public interface IStreamProvider - : IObservable<(Act, TSeries, int?)>, IStreamCache - where TSeries : ISeries -{ - /// - /// Currently has subscribers - /// - bool HasSubscribers { get; } - - /// - /// Current number of subscribers - /// - int SubscriberCount { get; } - - /// - /// Checks if a specific observer is subscribed - /// - /// - /// Subscriber IObserver reference - /// - /// True if subscribed/registered - bool HasSubscriber( - IObserver<(Act, TSeries, int?)> observer); - - /// - /// Unsubscribe all observers (subscribers) - /// - void EndTransmission(); -} diff --git a/src/_common/Observables/StreamCache.Utilities.cs b/src/_common/Observables/StreamCache.Utilities.cs deleted file mode 100644 index 1f0727567..000000000 --- a/src/_common/Observables/StreamCache.Utilities.cs +++ /dev/null @@ -1,173 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STREAM CACHE (UTILITIES) - -/// -public abstract partial class StreamCache - : IStreamCache - where TSeries : ISeries -{ - // clear cache without restore - /// - public void ClearCache() => ClearCache(0); - public abstract void ClearCache(DateTime fromTimestamp); - public abstract void ClearCache(int fromIndex); - - // reset fault flag and condition - /// - public void ResetFault() - { - OverflowCount = 0; - IsFaulted = false; - } - - // try/get the cache index based on a timestamp - /// - public bool TryFindIndex(DateTime timestamp, out int index) - { - index = GetIndex(timestamp, true); - return index != -1; - } - - // get the cache index based on item equality - /// - public int GetIndex(TSeries cachedItem, bool noException) - { - int low = 0; - int high = Cache.Count - 1; - int firstMatchIndex = -1; - - while (low <= high) - { - int mid = low + ((high - low) / 2); - int comparison = Cache[mid].Timestamp.CompareTo(cachedItem.Timestamp); - - if (comparison == 0) - { - // Found a match by Timestamp, - // store the index of the first match - if (firstMatchIndex == -1) - { - firstMatchIndex = mid; - } - - // Verify with Equals for an exact match - if (Cache[mid].Equals(cachedItem)) - { - return mid; // exact match found - } - - // Continue searching to the left for - // the first occurrence - high = mid - 1; - } - else if (comparison < 0) - { - low = mid + 1; - } - else - { - high = mid - 1; - } - } - - // If a timestamp match was found but no exact - // match, try to find an exact match in the range - // of duplicate timestamps (e.g. Renko bricks), - // biased towards later duplicats. - if (firstMatchIndex != -1) - { - // Find the last occurrence of the matching timestamp - int lastMatchIndex = firstMatchIndex; - for (int i = firstMatchIndex + 1; i < Cache.Count && Cache[i].Timestamp == cachedItem.Timestamp; i++) - { - lastMatchIndex = i; - } - - // Search for an exact match starting from the last occurrence - for (int i = lastMatchIndex; i >= firstMatchIndex; i--) - { - if (Cache[i].Equals(cachedItem)) - { - return i; // exact match found among duplicates - } - } - } - - if (noException) - { - return -1; - } - else - { - // not found - throw new ArgumentException( - "Matching source history not found.", nameof(cachedItem)); - } - } - - // get the cache index based on a timestamp - /// - public int GetIndex(DateTime timestamp, bool noException) - { - int low = 0; - int high = Cache.Count - 1; - - while (low <= high) - { - int mid = low + ((high - low) / 2); - DateTime midTimestamp = Cache[mid].Timestamp; - - if (midTimestamp == timestamp) - { - return mid; - } - else if (midTimestamp < timestamp) - { - low = mid + 1; - } - else - { - high = mid - 1; - } - } - - if (noException) - { - return -1; - } - else - { - // not found - throw new ArgumentException( - "Matching source history not found.", nameof(timestamp)); - } - } - - // get first cache index at or greater than timestamp - /// - public int GetInsertIndex(DateTime timestamp) - { - int low = 0; - int high = Cache.Count; - while (low < high) - { - int mid = low + ((high - low) / 2); - if (Cache[mid].Timestamp < timestamp) - { - low = mid + 1; - } - else - { - high = mid; - } - } - - // At this point, low is the index of the first - // element that is greater than or equal to timestamp - // or Cache.Count if all elements are less than timestamp. - // If low is equal to Cache.Count, it means there are - // no elements greater than or equal to timestamp. - return low < Cache.Count ? low : -1; - } -} diff --git a/src/_common/Observables/StreamCache.cs b/src/_common/Observables/StreamCache.cs deleted file mode 100644 index dc7e773ae..000000000 --- a/src/_common/Observables/StreamCache.cs +++ /dev/null @@ -1,320 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STREAM CACHE (BASE) - -/// -public abstract partial class StreamCache - : IStreamCache - where TSeries : ISeries -{ - #region PROPERTIES - - /// - public IReadOnlyList Results => Cache; - - /// - public bool IsFaulted { get; private set; } - - /// - /// Cache of stored values (base). - /// - internal List Cache { get; private set; } = []; - - /// - /// Most recent arrival to cache. - /// - internal TSeries? LastArrival { get; private set; } - - /// - /// Current count of repeated arrivals. - /// An overflow condition is triggered after 100. - /// - internal byte OverflowCount { get; private set; } - #endregion - - // CACHE MODIFICATION - - /// - /// Analyze new arrival to determine caching instruction; - /// then follow-on with caching action. - /// - /// Cacheable time-series object - /// Action taken (outcome) - /// - /// Item to modify is not found. - /// - /// - /// Too many sequential duplicates were detected. - /// - internal Act Modify(TSeries item) - { - // check overflow - if (CheckOverflow(item) is Act.DoNothing) - { - // duplicate found - return Act.DoNothing; - } - - // DETERMINE ACTion INSTRUCTION - - Act act; - int length = Cache.Count; - int? index = null; - - // first - if (length == 0) - { - act = Act.AddNew; - return Modify(act, item, index); - } - - TSeries last = Cache[length - 1]; - - // newer - if (item.Timestamp > last.Timestamp) - { - act = Act.AddNew; - } - - // repeat or late arrival - else - { - // seek duplicate - index = GetIndex(item.Timestamp, true); - - // replace duplicate - if (index >= 0) - { - act = Act.Update; - } - else - { - act = Act.AddOld; - index = null; - } - } - - // perform actual modification, return final action - return Modify(act, item, index); - } - - /// - /// Update cache, per "act" instruction, without analysis. - /// - /// - /// Since this does not analyze the action, it is not - /// recommended for use outside of the cache management system. - /// For example, it will not prevent duplicates or overflow. - /// - /// Caching instruction - /// Cacheable time-series object - /// Index, if already known (optional) - /// Action taken (outcome) - /// - /// Item to modify is not found. - /// - /// - /// Too many sequential duplicates were detected. - /// - /// - /// Action type is unknown. - /// - protected Act Modify(Act act, TSeries item, int? index) - { - // execute action - switch (act) - { - case Act.AddNew: - - Cache.Add(item); - - break; - - case Act.AddOld: - - // find - int ao = index ?? GetInsertIndex(item.Timestamp); - - // insert - if (ao != -1) - { - Cache.Insert(ao, item); - } - - // failure to find newer index - else - { - Cache.Add(item); - } - - break; - - case Act.Update: - - // find - int uo = index ?? GetIndex(item.Timestamp, false); - - // duplicate - if (item.Equals(Cache[uo])) - { - return Act.DoNothing; - } - - // replace - Cache[uo] = item; - - break; - - case Act.Delete: - - // find - int d = index ?? GetIndex(item.Timestamp, false); - - // delete - Cache.RemoveAt(d); - - break; - - case Act.DoNothing: - - break; - - case Act.Unknown: - - return Modify(item); - - // should never get here - default: - - throw new InvalidOperationException( - "Undefined cache action."); - } - - IsFaulted = false; - return act; - } - - /// - /// Analyze and DELETE new arrivals from cache, - /// after validating best instruction. - /// - /// Cacheable time-series object - /// Action taken (outcome) - /// - /// Too many sequential duplicates were detected. - /// - protected Act Purge(TSeries item) - { - // check format and overflow - if (CheckOverflow(item) is Act.DoNothing) - { - return Act.DoNothing; - } - - // find position - int index = GetIndex(item, true); - - if (index < 0) - { - // not found - return Act.DoNothing; - } - - // delete - return Modify(Act.Delete, item, index); - } - - /// - /// DELETE item from cache at index position, - /// without validating or analyzing the action - /// - /// Action taken (outcome) - /// - /// Index is out of range (not found). - /// - protected Act Purge(int index) - { - if (index < 0 || index >= Cache.Count) - { - // not found - return Act.DoNothing; - } - - TSeries item = Cache[index]; - - // delete - return Modify(Act.Delete, item, index); - } - - /// - /// Validate inbound item and compare to prior arrivals - /// to gracefully manage and prevent overflow conditions. - /// - /// Cacheable time-series object - /// - /// A "do nothing" act instruction if duplicate or 'null' - /// when no overflow condition is detected. - /// - /// - /// Too many sequential duplicates were detected. - /// - private Act? CheckOverflow(TSeries item) - { - Act? act = null; - - // skip first arrival - if (LastArrival is null) - { - LastArrival = item; - return act; - } - - // check for overflow condition - if (item.Timestamp == LastArrival.Timestamp) - { - // note: we have a better IsEqual() comparison method below, - // but it is too expensive as an initial quick evaluation. - - OverflowCount++; - - if (OverflowCount > 100) - { - const string msg = """ - A repeated stream update exceeded the 100 attempt threshold. - Check and remove circular chains or check your stream provider. - Provider terminated. - """; - - IsFaulted = true; - - throw new OverflowException(msg); - - // note: overflow exception is also further handled by providers, - // where it will EndTransmission(); and then throw error to user. - } - - // aggressive property value comparison - // TODO: not handling add-back after delete, registers as dup - if (item.Equals(LastArrival)) - { - // to prevent propogation - // of identical cache entry - act = Act.DoNothing; - } - - // same date with different values - // continues as an update - else - { - LastArrival = item; - } - } - else - { - OverflowCount = 0; - LastArrival = item; - } - - return act; - } -} diff --git a/src/_common/Observables/StreamHub.AddRemove.cs b/src/_common/Observables/StreamHub.AddRemove.cs deleted file mode 100644 index 014108567..000000000 --- a/src/_common/Observables/StreamHub.AddRemove.cs +++ /dev/null @@ -1,52 +0,0 @@ - -namespace Skender.Stock.Indicators; - -// STREAM HUB (ADD/REMOVE) - -public abstract partial class StreamHub -{ - public void Add(TIn newIn) - => OnNext((Act.Unknown, newIn, null)); - - public void Add(IEnumerable newIn) - { - foreach (TIn quote in newIn.ToSortedList()) - { - OnNext((Act.Unknown, quote, null)); - } - } - - public virtual Act Remove(TOut cachedItem) - => RemoveAt(GetIndex(cachedItem, false)); - - public Act RemoveAt(int cacheIndex) - { - TOut thisItem = Cache[cacheIndex]; - - try - { - Act act = Purge(cacheIndex); - NotifyObservers(act, thisItem, cacheIndex); - return act; - } - catch (OverflowException) - { - EndTransmission(); - throw; - } - } - - /// - /// Builds incremental indicator and adds to cache. - /// - /// - /// Caching instruction hint from provider - /// - /// - /// New inbound item from provider - /// - /// - /// Index position of item in provider cache - /// - internal abstract void Add(Act act, TIn item, int? index); -} diff --git a/src/_common/Observables/StreamHub.Observable.cs b/src/_common/Observables/StreamHub.Observable.cs new file mode 100644 index 000000000..5f6ec2269 --- /dev/null +++ b/src/_common/Observables/StreamHub.Observable.cs @@ -0,0 +1,119 @@ +namespace Skender.Stock.Indicators; + +// STREAM HUB (OBSERVABLE) + +public abstract partial class StreamHub : IStreamObservable +{ + private readonly HashSet> _observers = new(); + + /// + public bool HasObservers => _observers.Count > 0; + + /// + public int ObserverCount => _observers.Count; + + /// + public IReadOnlyList ReadCache => Cache; + + /// + public virtual BinarySettings Properties { get; init; } = new(0); // default 0b00000000 + + #region SUBSCRIPTION SERVICES + + /// + public IDisposable Subscribe(IStreamObserver observer) + { + _observers.Add(observer); + return new Unsubscriber(_observers, observer); + } + + /// + public bool Unsubscribe(IStreamObserver observer) + => _observers.Remove(observer); + + /// + public bool HasSubscriber(IStreamObserver observer) + => _observers.Contains(observer); + + /// + /// A disposable subscription to the stream provider. + /// Unsubscribed with + /// + /// + /// Registry of all subscribers (by ref) + /// + /// + /// Your unique subscription as provided. + /// + private class Unsubscriber( + ISet> observers, + IStreamObserver observer) : IDisposable + { + private readonly ISet> _observers = observers; + private readonly IStreamObserver _observer = observer; + + /// + /// Remove single observer. + /// + public void Dispose() => _observers.Remove(_observer); + } + + /// + public void EndTransmission() + { + foreach (IStreamObserver observer + in _observers.ToArray()) + { + if (_observers.Contains(observer)) + { + // subscriber removes itself + observer.OnCompleted(); + } + } + + _observers.Clear(); + } + #endregion + + #region SUBSCRIBER NOTIFICATIONS + + /// + /// Sends new TSeries item to subscribers. + /// + /// TSeries item to send. + /// Provider index hint. + private void NotifyObserversOnAdd(TOut item, int? indexHint) + { + // send to subscribers + foreach (IStreamObserver o in _observers.ToArray()) + { + o.OnAdd(item, notify: true, indexHint); + } + } + + /// + /// Sends rebuilds point in time to all subscribers. + /// + /// Rebuild starting positions. + private void NotifyObserversOnChange(DateTime fromTimestamp) + { + foreach (IStreamObserver o in _observers.ToArray()) + { + o.OnChange(fromTimestamp); + } + } + + /// + /// Sends error (exception) to all subscribers. + /// + /// The exception to send. + private void NotifyObserversOnError(Exception exception) + { + // send to subscribers + foreach (IStreamObserver o in _observers.ToArray()) + { + o.OnError(exception); + } + } + #endregion +} diff --git a/src/_common/Observables/StreamHub.Observer.cs b/src/_common/Observables/StreamHub.Observer.cs new file mode 100644 index 000000000..c2b374336 --- /dev/null +++ b/src/_common/Observables/StreamHub.Observer.cs @@ -0,0 +1,66 @@ +namespace Skender.Stock.Indicators; + +// STREAM HUB (OBSERVER) + +public abstract partial class StreamHub : IStreamObserver +{ + /// + public bool IsSubscribed => Provider.HasSubscriber(this); + + /// + /// Data provider that this observer subscribes to. + /// + protected IStreamObservable Provider { get; init; } + + /// + /// Subscription token for managing the subscription lifecycle. + /// + private IDisposable? Subscription { get; set; } + + /// + /// Lock object to ensure thread safety during unsubscription. + /// + private readonly object _unsubscribeLock = new(); + + // Observer methods + + /// + public virtual void OnAdd(TIn item, bool notify, int? indexHint) + { + // Convert the input item to the output type and append it to the cache. + // Override this method if the input and output types are not indexed 1:1. + + (TOut result, int _) = ToIndicator(item, indexHint); // TODO: make this return array, loop appendation? + AppendCache(result, notify); + } + + /// + public void OnChange(DateTime fromTimestamp) + => Rebuild(fromTimestamp); + + /// + public void OnError(Exception exception) + => throw exception; + + /// + public void OnCompleted() + => Unsubscribe(); + + /// + public void Unsubscribe() + { + // Ensure thread-safety for EndTransmission > OnCompleted-type race conditions + // see https://learn.microsoft.com/en-us/dotnet/standard/events/observer-design-pattern-best-practices + + lock (_unsubscribeLock) + { + if (IsSubscribed) + { + Provider.Unsubscribe(this); + } + + Subscription?.Dispose(); + Subscription = null; // ensure the ref is cleared + } + } +} diff --git a/src/_common/Observables/StreamHub.Rebuild.cs b/src/_common/Observables/StreamHub.Rebuild.cs deleted file mode 100644 index 6ff357022..000000000 --- a/src/_common/Observables/StreamHub.Rebuild.cs +++ /dev/null @@ -1,90 +0,0 @@ - -namespace Skender.Stock.Indicators; - -// STREAM HUB (REBUILD CACHE) - -public abstract partial class StreamHub -{ - // full reset - /// - public void Reinitialize() - { - Unsubscribe(); - ResetFault(); - ClearCache(); - Subscription = Provider.Subscribe(this); - RebuildCache(); - } - - // rebuild cache - /// - public void RebuildCache() => RebuildCache(0); - - // rebuild cache from timestamp - /// - public void RebuildCache( - DateTime fromTimestamp) - { - int fromIndex = GetInsertIndex(fromTimestamp); - - // nothing to rebuild - if (fromIndex < 0) - { - return; - } - - int provIndex = Provider.GetInsertIndex(fromTimestamp); - - // nothing to restore - if (provIndex < 0) - { - provIndex = int.MaxValue; - } - - RebuildCache(fromIndex, provIndex); - } - - // rebuild cache from index - /// - public void RebuildCache(int fromIndex) - { - // get equivalent provider index - int provIndex; - - if (fromIndex <= 0) - { - provIndex = 0; - } - else - { - TOut item = Cache[fromIndex]; - - provIndex = Provider.GetInsertIndex(item.Timestamp); - - if (provIndex < 0) - { - // nothing to restore - provIndex = int.MaxValue; - } - } - - RebuildCache(fromIndex, provIndex); - } - - /// - /// Rebuild cache from index and provider index positions. - /// - /// Cache starting position to purge. - /// Provider starting position to add back. - internal void RebuildCache(int thisIndex, int provIndex) - { - // clear outdated cache - ClearCache(thisIndex); - - // rebuild cache from provider - for (int i = provIndex; i < Provider.Results.Count; i++) - { - OnNext((Act.AddNew, Provider.Results[i], i)); - } - } -} diff --git a/src/_common/Observables/StreamHub.Utilities.cs b/src/_common/Observables/StreamHub.Utilities.cs new file mode 100644 index 000000000..041d9e3cf --- /dev/null +++ b/src/_common/Observables/StreamHub.Utilities.cs @@ -0,0 +1,202 @@ +namespace Skender.Stock.Indicators; + +// STREAM HUB (STATIC UTILITIES) + +public static class StreamHub +{ + /// + /// Try to find index position of the provided timestamp + /// + /// + /// Timestamp to seek + /// + /// Index of timestamp or -1 when not found + /// + /// True if found + internal static bool TryFindIndex( + this IReadOnlyList cache, + DateTime timestamp, + out int index) + where T : ISeries + { + index = cache.GetIndex(timestamp, false); + return index != -1; + } + + /// + /// Get the cache index based on item equality. + /// + /// + /// + /// Time-series object to find in cache + /// + /// + /// Throw exception when item is not found + /// + /// Index position + /// + /// When items is not found (should never happen). + /// + internal static int GetIndex( + this IReadOnlyList cache, + T cachedItem, + bool throwOnFail) + where T : ISeries + { + int low = 0; + int high = cache.Count - 1; + int firstMatchIndex = -1; + + while (low <= high) + { + int mid = low + ((high - low) / 2); + int comparison = cache[mid].Timestamp.CompareTo(cachedItem.Timestamp); + + if (comparison == 0) + { + // Found a match by Timestamp, + // store the index of the first match + if (firstMatchIndex == -1) + { + firstMatchIndex = mid; + } + + // Verify with Equals for an exact match + if (cache[mid].Equals(cachedItem)) + { + return mid; // exact match found + } + + // Continue searching to the left for + // the first occurrence + high = mid - 1; + } + else if (comparison < 0) + { + low = mid + 1; + } + else + { + high = mid - 1; + } + } + + // If a timestamp match was found but no exact + // match, try to find an exact match in the range + // of duplicate timestamps (e.g. Renko bricks), + // biased towards later duplicates. + if (firstMatchIndex != -1) + { + // Find the last occurrence of the matching timestamp + for (int i = cache.Count - 1; i >= firstMatchIndex; i--) + { + if (cache[i].Timestamp == cachedItem.Timestamp + && cache[i].Equals(cachedItem)) + { + return i; // exact match found among duplicates + } + } + } + + // not found + return throwOnFail + ? throw new ArgumentException( + "Matching source history not found.", nameof(cachedItem)) + : -1; + } + + /// + /// Get the cache index based on a timestamp. + /// + /// + /// Only use this when you are looking for a point in time + /// without a matching item for context. In most cases + /// is more appropriate. + /// + /// + /// + /// Timestamp of cached item + /// + /// + /// Throw exception when timestamp is not found + /// + /// Index position + /// + /// When timestamp is not found (should never happen). + /// + internal static int GetIndex( + this IReadOnlyList cache, + DateTime timestamp, + bool throwOnFail) + where T : ISeries + { + int low = 0; + int high = cache.Count - 1; + + while (low <= high) + { + int mid = low + ((high - low) / 2); + DateTime midTimestamp = cache[mid].Timestamp; + + if (midTimestamp == timestamp) + { + return mid; + } + else if (midTimestamp < timestamp) + { + low = mid + 1; + } + else + { + high = mid - 1; + } + } + + // not found + return throwOnFail + ? throw new ArgumentException( + "Matching source history not found.", nameof(timestamp)) + : -1; + } + + /// + /// Get the first cache index on or after a timestamp. + /// + /// + /// Only use this when you are looking for a point in time + /// without a matching item for context. In most cases + /// is more appropriate. + /// + /// + /// + /// Timestamp of cached item + /// + /// First index position or -1 if not found + internal static int GetIndexGte( + this IReadOnlyList cache, + DateTime timestamp) + where T : ISeries + { + int low = 0; + int high = cache.Count; + while (low < high) + { + int mid = low + ((high - low) / 2); + if (cache[mid].Timestamp < timestamp) + { + low = mid + 1; + } + else + { + high = mid; + } + } + + // At this point, low is the index of the first + // element that is greater than or equal to timestamp + // or Cache.Count if all elements are less than timestamp. + // If low is equal to Cache.Count, it means there are + // no elements greater than or equal to timestamp. + return low < cache.Count ? low : -1; + } +} diff --git a/src/_common/Observables/StreamHub.cs b/src/_common/Observables/StreamHub.cs index 5b3d806cf..53c6302f4 100644 --- a/src/_common/Observables/StreamHub.cs +++ b/src/_common/Observables/StreamHub.cs @@ -1,61 +1,408 @@ - namespace Skender.Stock.Indicators; -// STREAM HUB (OBSERVER BASE) - -# region hub observer variants - -public abstract class QuoteObserver( - IQuoteProvider provider -) : StreamHub(provider) - where TIn : IQuote - where TOut : ISeries; - -public abstract class ReusableObserver( - IChainProvider provider -) : StreamHub(provider) - where TIn : IReusable - where TOut : ISeries; -#endregion +// STREAM HUB (BASE/CACHE) -/// -/// Streaming hub (abstract observer/provider) -/// -public abstract partial class StreamHub( - IStreamProvider provider -) : StreamProvider, IStreamHub +/// +public abstract partial class StreamHub : IStreamHub where TIn : ISeries where TOut : ISeries { - public bool IsSubscribed => Provider.HasSubscriber(this); + #region constructor + + /// + /// Streaming data provider + /// + private protected StreamHub(IStreamObservable provider) + { + // store provider reference + Provider = provider; + + // set provider cache reference + ProviderCache = provider.GetCacheRef(); + + // inherit settings (reinstantiate struct on heap) + Properties = Properties.Combine(provider.Properties); + } + + #endregion + + #region PROPERTIES + + /// + public IReadOnlyList Results => Cache; + + /// + public bool IsFaulted { get; private set; } - protected internal IDisposable? Subscription { get; set; } + /// + /// Cache of stored values (base). + /// + internal List Cache { get; } = new(); - protected IStreamProvider Provider => provider; + /// + /// Current count of repeated caching attempts. + /// An overflow condition is triggered after 100. + /// + internal byte OverflowCount { get; private set; } - // observer methods + /// + /// Reference to this hub's provider's cache. + /// + protected IReadOnlyList ProviderCache { get; } - public virtual void OnNext((Act, TIn, int?) value) + /// + /// Most recent item saved to cache. + /// + private TOut? LastItem { get; set; } + + #endregion + + // reset fault flag and condition + /// + public void ResetFault() { - (Act act, TIn item, int? index) = value; + OverflowCount = 0; + IsFaulted = false; + } + + // fetch cache reference + /// + public IReadOnlyList GetCacheRef() => Cache; + + public abstract override string ToString(); + + /// + /// Converts incremental value into + /// an indicator candidate and cache position. + /// + /// New item from provider + /// Provider index hint + /// Cacheable item candidate and index hint + protected abstract (TOut result, int index) + ToIndicator(TIn item, int? indexHint); + + #region ADD & ANALYZE - if (act is Act.Unknown or Act.AddNew) + public void Add(TIn newIn) + => OnAdd(newIn, notify: true, null); + + public void Add(IEnumerable batchIn) + { + foreach (TIn newIn in batchIn.OrderBy(x => x.Timestamp)) + { + OnAdd(newIn, notify: true, null); + } + } + + public void Insert(TIn newIn) + { + // note: should only be used when newer timestamps + // are not impacted by the insertion of an older item + + // generate candidate result + (TOut result, int index) = ToIndicator(newIn, null); + + // insert, then rebuild observers (no self-rebuild) + if (index > 0) + { + // check overflow/duplicates + if (IsOverflowing(result)) + { + return; // duplicate found + } + + Cache.Insert(index, result); + NotifyObserversOnChange(result.Timestamp); + } + + // normal add + else + { + AppendCache(result, notify: true); + } + } + + /// + /// Perform appropriate caching action after analysis. + /// It will add if new, ignore if duplicate, or rebuild if late-arrival. + /// + /// TSeries item to cache. + /// + /// Notify subscribers of change (send to observers). + /// This is disabled for bulk operations like rebuild. + /// + protected void AppendCache(TOut result, bool notify) + { + // check overflow/duplicates + if (IsOverflowing(result)) { - Add(act, item, index); return; } - // TODO: handle revision/recursion differently - // for different indicators; and may also need - // to breakout OnDeleted(TIn deleted), etc. - RebuildCache(item.Timestamp); + bool bypassRebuild = Properties[1]; // forced add/caching w/o rebuild + + // consider timeline + Act act = bypassRebuild || Cache.Count == 0 || result.Timestamp > Cache[^1].Timestamp + ? Act.Add + : Act.Rebuild; + + // fulfill action + switch (act) + { + // add to cache + case Act.Add: + Add(result, notify); + break; + + // rebuild cache + case Act.Rebuild: + Rebuild(result.Timestamp); + break; + + // would never happen + default: + throw new InvalidOperationException(); + } } - public void OnError(Exception error) => throw error; + /// + /// Add item to cache and notify observers. + /// + /// Item to add to end of cache + /// Inherited notification instructions. + private void Add(TOut item, bool notify) + { + // notes: + // 1. Should only be called from AppendCache() + // 2. Notify has to be disabled for bulk operations, like rebuild. + // 3. Forced caching (rebuild analysis bypass) is inherited property. - public void OnCompleted() => Unsubscribe(); + // add to cache + Cache.Add(item); + IsFaulted = false; - public void Unsubscribe() => Subscription?.Dispose(); + // notify subscribers + if (notify) + { + NotifyObserversOnAdd(item, Cache.Count - 1); + } + } - public abstract override string ToString(); + /// + /// Validate outbound item and compare to prior cached item, + /// to gracefully manage and prevent overflow conditions. + /// + /// Cacheable time-series object + /// + /// True if item is repeating and duplicate was suppressed. + /// + /// + /// Too many sequential duplicates were detected. + /// + private bool IsOverflowing(TOut item) + { + // skip first arrival + if (LastItem is null) + { + LastItem = item; + return false; + } + + // track/check for overflow condition + if (item.Timestamp == LastItem.Timestamp && item.Equals(LastItem)) + { + // ^^ using progressive check to avoid Equals() on every item + + OverflowCount++; + + // handle overflow + if (OverflowCount > 100) + { + const string msg = """ + A repeated stream update exceeded the 100 attempt threshold. + Check and remove circular chains or check your stream provider. + Provider terminated. + """; + + IsFaulted = true; + + // emit error + OverflowException exception = new(msg); + NotifyObserversOnError(exception); + throw exception; + } + + // bypass duplicate prevention + // when forced caching is enabled + if (Properties[1]) + { + return false; + + // note: will still overflow + // when the 100 limit is reached + } + + return true; + } + + // not repeating + OverflowCount = 0; + LastItem = item; + return false; + } + #endregion + + #region REMOVE & REMOVE RANGE + + /// remove cached item + /// + public void Remove(TOut cachedItem) + { + Cache.Remove(cachedItem); + NotifyObserversOnChange(cachedItem.Timestamp); + } + + /// remove cached item at index position + /// + public void RemoveAt(int cacheIndex) + { + TOut cachedItem = Cache[cacheIndex]; + Cache.RemoveAt(cacheIndex); + NotifyObserversOnChange(cachedItem.Timestamp); + } + + /// remove cache range from timestamp + /// + public void RemoveRange(DateTime fromTimestamp, bool notify) + { + // rollback internal state + RollbackState(fromTimestamp); + + // remove cache entries + Cache.RemoveAll(c => c.Timestamp >= fromTimestamp); + + // notify observers + if (notify) + { + NotifyObserversOnChange(fromTimestamp); + } + } + + /// remove cache range from index + /// + public void RemoveRange(int fromIndex, bool notify) + { + // nothing to do + if (Cache.Count == 0 || fromIndex >= Cache.Count) + { + return; + } + + // remove cache entries + DateTime fromTimestamp = fromIndex <= 0 + ? DateTime.MinValue + : Cache[fromIndex].Timestamp; + + RemoveRange(fromTimestamp, notify); + } + #endregion + + #region REBUILD & REINITIALIZE + + // full reset + /// + public void Reinitialize() + { + Unsubscribe(); + ResetFault(); + Rebuild(); + Subscription = Provider.Subscribe(this); + + // TODO: make reinitialization abstract, + // and build initial Cache from faster static method + + // TODO: evaluate race condition between rebuild + // and subscribe; will it miss any high frequency data? + } + + // rebuild cache + /// + public void Rebuild() + => Rebuild(DateTime.MinValue); + + // rebuild cache from timestamp + /// + public void Rebuild(DateTime fromTimestamp) + { + // clear cache + RemoveRange(fromTimestamp, notify: false); + + // get provider position + int provIndex = ProviderCache.GetIndexGte(fromTimestamp); + + // rebuild + if (provIndex >= 0) + { + for (int i = provIndex; i < ProviderCache.Count; i++) + { + OnAdd(ProviderCache[i], notify: false, i); + } + } + + // notify observers + NotifyObserversOnChange(fromTimestamp); + } + + // rebuild cache from index + /// + public void Rebuild(int fromIndex) + { + // find timestamp + DateTime fromTimestamp = fromIndex <= 0 || Cache.Count == 0 + ? DateTime.MinValue + : Cache[fromIndex].Timestamp; + + // rebuild & notify + Rebuild(fromTimestamp); + } + + /// + /// Rollback internal state to a point in time. + /// Behavior varies by indicator. + /// + /// + /// Override when indicator needs to rollback state to a + /// point in time (e.g. when rebuilding cache). Example: + /// + /// + /// + /// Point in time to restore. + /// + protected virtual void RollbackState(DateTime timestamp) + { + // note: override when rollback is needed + // default: do nothing + // see AtrStopHub() for example + } + #endregion } + +#region chain and quote variants + +/// +public abstract class QuoteProvider( + IStreamObservable provider +) : StreamHub(provider), IQuoteProvider + where TIn : IReusable + where TOut : IQuote +{ + public IReadOnlyList Quotes => Cache; +}; + +/// +public abstract class ChainProvider( + IStreamObservable provider +) : StreamHub(provider), IChainProvider + where TIn : IReusable + where TOut : IReusable; +#endregion diff --git a/src/_common/Observables/StreamProvider.Motify.cs b/src/_common/Observables/StreamProvider.Motify.cs deleted file mode 100644 index c76f92cfc..000000000 --- a/src/_common/Observables/StreamProvider.Motify.cs +++ /dev/null @@ -1,91 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STREAM PROVIDER: MODIFY CACHE + NOTIFY SUBSCRIBERS (MOTIFY) - -public abstract partial class StreamProvider -{ - /// clear cache without restore, from timestamp - /// - public override void ClearCache(DateTime fromTimestamp) - { - // start of range - int fromIndex = GetInsertIndex(fromTimestamp); - - // something to do - if (fromIndex != -1) - { - ClearCache(fromIndex); - } - } - - /// clear cache without restore, from index - /// - public override void ClearCache(int fromIndex) - => ClearCache(fromIndex, toIndex: Cache.Count - 1); - - /// - /// Modify cache and notify observers. - /// - /// Caching instruction - /// TSeries item to send - /// Cached index position - protected void Motify(Act act, TSeries result, int? index) - { - Act actTaken = Modify(act, result, index); - NotifyObservers(actTaken, result, index); - } - - /// - /// Sends TSeries item to all subscribers - /// - /// Caching instruction - /// TSeries item to send - /// Provider index hint - protected void NotifyObservers(Act act, TSeries? item, int? index) - { - // do not propogate "do nothing" acts - if (act == Act.DoNothing || item is null) - { - return; - } - - // send to subscribers - foreach (IObserver<(Act, TSeries, int?)> obs - in _subscribers.ToArray()) - { - obs.OnNext((act, item, index)); - } - } - - /// - /// Deletes cache entries between index range values. - /// - /// - /// This is implemented in inheriting (provider) class - /// due to unique requirement to notify subscribers. - /// - /// First element to delete - /// Last element to delete - /// clears cache segment - /// - private void ClearCache( - int fromIndex, int toIndex) - { - // nothing to do - if (Cache.Count is 0) - { - return; - } - - // determine in-range start/end indices - int fr = Math.Max(0, Math.Min(fromIndex, toIndex)); - int to = Math.Min(Cache.Count - 1, Math.Max(fromIndex, toIndex)); - - // delete and deliver instruction in reverse - // order to prevent recursive recompositions - for (int i = to; i >= fr; i--) - { - Motify(Act.Delete, Cache[i], i); - } - } -} diff --git a/src/_common/Observables/StreamProvider.cs b/src/_common/Observables/StreamProvider.cs deleted file mode 100644 index aa3598906..000000000 --- a/src/_common/Observables/StreamProvider.cs +++ /dev/null @@ -1,64 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STREAM PROVIDER (OBSERVABLE BASE) - -/// -public abstract partial class StreamProvider - : StreamCache, IStreamProvider - where TSeries : ISeries -{ - private readonly HashSet> _subscribers = []; - - public bool HasSubscribers => _subscribers.Count > 0; - - public int SubscriberCount => _subscribers.Count; - - public IReadOnlyList ReadCache => Cache; - - // SUBSCRIPTION SERVICES - - // subscribe observer - public IDisposable Subscribe(IObserver<(Act, TSeries, int?)> observer) - { - _subscribers.Add(observer); - return new Subscription(_subscribers, observer); - } - - // check if observer is subscribed - public bool HasSubscriber( - IObserver<(Act, TSeries, int?)> observer) - => _subscribers.Contains(observer); - - /// - /// A disposable subscription to the stream provider. - /// Unsubscribed with - /// - /// - /// Registry of all subscribers (by ref) - /// - /// - /// Your unique subscription as provided. - /// - private class Subscription( - ISet> subscribers, - IObserver<(Act, TSeries, int?)> subscriber) : IDisposable - { - // remove single observer - public void Dispose() => subscribers.Remove(subscriber); - } - - // unsubscribe all observers - public void EndTransmission() - { - foreach (IObserver<(Act, TSeries, int?)> subscriber - in _subscribers.ToArray()) - { - if (_subscribers.Contains(subscriber)) - { - subscriber.OnCompleted(); - } - } - - _subscribers.Clear(); - } -} diff --git a/src/_common/ObsoleteV3.cs b/src/_common/ObsoleteV3.cs index 54bf03653..de35c808b 100644 --- a/src/_common/ObsoleteV3.cs +++ b/src/_common/ObsoleteV3.cs @@ -8,8 +8,10 @@ namespace Skender.Stock.Indicators; // OBSOLETE IN v3 public static partial class Indicator { + // GENERAL INDICATOR METHODS + [ExcludeFromCodeCoverage] - [Obsolete("Use alternate 'GetX' variant.", false)] // v3.0.0 + [Obsolete("Use alternate 'GetX' variant. Tuple arguments were removed.", false)] // v3.0.0 public static IEnumerable GetAlligator( this IEnumerable<(DateTime d, double v)> priceTuples, int jawPeriods = 13, @@ -21,67 +23,85 @@ public static IEnumerable GetAlligator( => priceTuples .Select(t => new QuotePart(t.d, t.v)) .ToList() - .CalcAlligator( + .ToAlligator( jawPeriods, jawOffset, teethPeriods, teethOffset, lipsPeriods, lipsOffset); [ExcludeFromCodeCoverage] - [Obsolete("Use a chained `results.GetSma(smaPeriods)` to generate a moving average.", true)] // v3.0.0 - public static IEnumerable GetAdl( - this IEnumerable quotes, int smaPeriods) + [Obsolete("Replace `GetEma(..)` with `ToEma(..)`", false)] // v3.0.0 + public static IEnumerable GetEma( + this IReadOnlyList quotes, int lookbackPeriods) where TQuote : IQuote - => quotes.ToSortedList().CalcAdl(); + => quotes.ToSortedList().ToEma(lookbackPeriods); + + // REMOVAL OF INTEGRATED SMAs (evaluates to ERRORs) [ExcludeFromCodeCoverage] - [Obsolete("Replace `GetEma(..)` with `ToEma(..)`", true)] // v3.0.0 - public static IEnumerable GetEma( - this IEnumerable quotes, int lookbackPeriods) + [Obsolete("Use a chained `results.GetSma(smaPeriods)` to generate a moving average.", true)] // v3.0.0 + public static IEnumerable GetAdl( + this IReadOnlyList quotes, int smaPeriods) where TQuote : IQuote - => quotes.ToEma(lookbackPeriods); + => quotes.ToSortedList().ToAdl(); [ExcludeFromCodeCoverage] [Obsolete("Use a chained `results.GetSma(smaPeriods)` to generate a moving average.", true)] // v3.0.0 public static IEnumerable GetObv( - this IEnumerable quotes, int smaPeriods) + this IReadOnlyList quotes, int smaPeriods) where TQuote : IQuote - => quotes.GetObv(); + => quotes.ToObv(); [ExcludeFromCodeCoverage] [Obsolete("Use a chained `results.GetSma(smaPeriods)` to generate a moving average.", true)] // v3.0.0 public static IEnumerable GetPrs( this IEnumerable quotesEval, IEnumerable quotesBase, int lookbackPeriods, int smaPeriods) where TQuote : IQuote - => quotesEval.Use(CandlePart.Close).GetPrs(quotesBase.Use(CandlePart.Close), lookbackPeriods); + => quotesEval + .ToSortedList() + .Use(CandlePart.Close) + .ToPrs( + quotesBase.ToSortedList() + .Use(CandlePart.Close), lookbackPeriods); [ExcludeFromCodeCoverage] [Obsolete("Use a chained `results.GetSma(smaPeriods)` to generate a moving average.", true)] // v3.0.0 public static IEnumerable GetRoc( - this IEnumerable quotes, int lookbackPeriods, int smaPeriods) + this IReadOnlyList quotes, int lookbackPeriods, int smaPeriods) where TQuote : IQuote - => quotes.Use(CandlePart.Close).GetRoc(lookbackPeriods); + => quotes.Use(CandlePart.Close).ToRoc(lookbackPeriods); [ExcludeFromCodeCoverage] [Obsolete("Use a chained `results.GetSma(smaPeriods)` to generate a moving average.", true)] // v3.0.0 public static IEnumerable GetStdDev( - this IEnumerable quotes, int lookbackPeriods, int smaPeriods) + this IReadOnlyList quotes, int lookbackPeriods, int smaPeriods) where TQuote : IQuote - => quotes.Use(CandlePart.Close).ToList().CalcStdDev(lookbackPeriods); + => quotes.Use(CandlePart.Close).ToStdDev(lookbackPeriods); [ExcludeFromCodeCoverage] [Obsolete("Use a chained `results.GetSma(smaPeriods)` to generate a moving average.", true)] // v3.0.0 public static IEnumerable GetTrix( - this IEnumerable quotes, int lookbackPeriods, int smaPeriods) + this IReadOnlyList quotes, int lookbackPeriods, int smaPeriods) where TQuote : IQuote - => quotes.Use(CandlePart.Close).ToList().CalcTrix(lookbackPeriods); + => quotes.Use(CandlePart.Close).ToTrix(lookbackPeriods); + + // UTILITIES [ExcludeFromCodeCoverage] [Obsolete("This method no longer defaults to Close. Rename Use() to Use(CandlePart.Close) for an explicit conversion.", false)] // v3.0.0 public static IEnumerable<(DateTime Timestamp, double Value)> Use( - this IEnumerable quotes) + this IReadOnlyList quotes) where TQuote : IQuote => quotes.Select(x => (x.Timestamp, x.Value)); + [ExcludeFromCodeCoverage] + [Obsolete("Refactor to use `ToSortedList()`", true)] // v3.0.0 + public static Collection ToSortedCollection( + this IReadOnlyList series) + where TSeries : ISeries + => series + .OrderBy(x => x.Timestamp) + .ToCollection(); + [ExcludeFromCodeCoverage] [Obsolete("Refactor to use `ToReusable()`", true)] // v3.0.0 public static Collection<(DateTime Timestamp, double Value)> ToTupleChainable( @@ -89,7 +109,6 @@ public static IEnumerable GetTrix( where TResult : IReusable => reusable.Select(x => (x.Timestamp, x.Value)).OrderBy(x => x.Timestamp).ToCollection(); - [ExcludeFromCodeCoverage] [Obsolete("Refactor to use `List.First(c => c.Timestamp == lookupDate)`", false)] // v3.0.0 public static TSeries Find(this IEnumerable series, DateTime lookupDate) @@ -101,6 +120,8 @@ public static int FindIndex(this List series, DateTime lookupD where TSeries : ISeries => series?.FindIndex(x => x.Timestamp == lookupDate) ?? -1; } +// CLASSES AND INTERFACES + [Obsolete("Rename `IReusableResult` to `IReusable`", true)] // v3.0.0 public interface IReusableResult : IReusable; diff --git a/src/_common/ObsoleteV3.md b/src/_common/ObsoleteV3.md index 4d21cf0be..9732c8d59 100644 --- a/src/_common/ObsoleteV3.md +++ b/src/_common/ObsoleteV3.md @@ -98,6 +98,8 @@ Items marked with 🚩 require special attention since they will not produc - `GetBaseQuote()` indicator and related `BasicData` return types were removed since they are redundant to the `Use()` method and `QuotePart` return types, respectively. +- `AtrStopResult` values were changed from `decimal` to `double` numeric return types. + - `SyncSeries()` utility function and related `SyncType` enum were removed. This was primarily an internal utility, but was part of the public API to support user who wanted to build custom indicator development. Internally, we've refactored indicators to auto-initialize and heal, so they no longer require re-sizing to support explicit warmup periods. - `ToTupleCollection()` utility method was deprecated. This was available to support custom indicator development, but is no longer needed. We've discontinued using _tuples_ as an interface to chainable indicators. diff --git a/src/_common/Quotes/Quote.Aggregates.cs b/src/_common/Quotes/Quote.Aggregates.cs index a06f93554..8af1b3ccd 100644 --- a/src/_common/Quotes/Quote.Aggregates.cs +++ b/src/_common/Quotes/Quote.Aggregates.cs @@ -2,13 +2,13 @@ namespace Skender.Stock.Indicators; // QUOTE UTILITIES -public static partial class Utility +public static partial class Quotes { // aggregation (quantization) /// /// public static IReadOnlyList Aggregate( - this IEnumerable quotes, + this IReadOnlyList quotes, PeriodSize newSize) where TQuote : IQuote { @@ -40,7 +40,7 @@ public static IReadOnlyList Aggregate( /// /// public static IReadOnlyList Aggregate( - this IEnumerable quotes, + this IReadOnlyList quotes, TimeSpan timeSpan) where TQuote : IQuote { diff --git a/src/_common/Quotes/Quote.Api.cs b/src/_common/Quotes/Quote.Api.cs deleted file mode 100644 index de8fb2a29..000000000 --- a/src/_common/Quotes/Quote.Api.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Skender.Stock.Indicators; - -// QUOTE (API) - -public static partial class Utility -{ - // OBSERVER, from Quote Provider (redistribution) - public static QuoteHub ToQuote( - this IQuoteProvider quoteProvider) - where TQuote : IQuote => new(quoteProvider); -} diff --git a/src/_common/Quotes/Quote.Converters.cs b/src/_common/Quotes/Quote.Converters.cs index a2e664209..0a03e5e13 100644 --- a/src/_common/Quotes/Quote.Converters.cs +++ b/src/_common/Quotes/Quote.Converters.cs @@ -1,17 +1,14 @@ -using System.Globalization; - namespace Skender.Stock.Indicators; // QUOTE UTILITIES (CONVERTERS) -public static partial class Utility +public static partial class Quotes { - /* LISTS */ // convert TQuote type list to built-in Quote type list public static IReadOnlyList ToQuoteList( - this IEnumerable quotes) + this IReadOnlyList quotes) where TQuote : IQuote => quotes @@ -21,11 +18,10 @@ public static IReadOnlyList ToQuoteList( // convert TQuote type list to QuoteD type list internal static List ToQuoteDList( - this IEnumerable quotes) + this IReadOnlyList quotes) where TQuote : IQuote => quotes - .OrderBy(x => x.Timestamp) .Select(x => x.ToQuoteD()) .ToList(); diff --git a/src/_common/Quotes/Quote.Models.cs b/src/_common/Quotes/Quote.Models.cs index d8bbdea23..5d094449d 100644 --- a/src/_common/Quotes/Quote.Models.cs +++ b/src/_common/Quotes/Quote.Models.cs @@ -16,12 +16,6 @@ namespace Skender.Stock.Indicators; /// /// double IReusable.Value => (double)Close; /// -/// or inherit the record class -/// -/// public record MyQuote(DateTime MyTimestamp, decimal MyClosePrice, ...) -/// : Reusable(MyTimestamp, (double)MyClosePrice), IQuote -/// { ... } -/// /// /// /// TIP: If you do not need a custom quote type, @@ -86,9 +80,9 @@ public record Quote decimal Low, decimal Close, decimal Volume -) : Reusable(Timestamp), IQuote +) : IQuote { - public override double Value => (double)Close; + public double Value => (double)Close; // TODO: add [Obsolete] auto-getter/setter for 'Date' property // but only for a short transition period. See if there can be @@ -108,7 +102,7 @@ internal record QuoteD double Low, double Close, double Volume -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Close; + public double Value => Close; } diff --git a/src/_common/Quotes/Quote.StreamHub.cs b/src/_common/Quotes/Quote.StreamHub.cs index b73fe4745..6f43feb02 100644 --- a/src/_common/Quotes/Quote.StreamHub.cs +++ b/src/_common/Quotes/Quote.StreamHub.cs @@ -1,13 +1,23 @@ namespace Skender.Stock.Indicators; +#region hub initializer + +public static partial class Quotes +{ + public static QuoteHub ToQuote( + this IQuoteProvider quoteProvider) + where TQuote : IQuote => new(quoteProvider); +} +#endregion + /// /// Quote provider (abstract base) /// -public class QuoteHub : QuoteObserver, - IQuoteHub +public class QuoteHub + : QuoteProvider where TQuote : IQuote { - public QuoteHub() : base(new QuoteProvider()) { } + public QuoteHub() : base(new EmptyQuoteProvider()) { } public QuoteHub( IQuoteProvider provider) @@ -16,22 +26,15 @@ public QuoteHub( Reinitialize(); } - public IReadOnlyList Quotes => Cache; - // METHODS - internal override void Add(Act act, TQuote newIn, int? index) + protected override (TQuote result, int index) + ToIndicator(TQuote item, int? indexHint) { - try - { - // save and send - Motify(act, newIn, index); - } - catch (OverflowException) - { - EndTransmission(); - throw; - } + int index = indexHint + ?? Cache.GetIndexGte(item.Timestamp); + + return (item, index == -1 ? Cache.Count : index); } public override string ToString() @@ -39,9 +42,31 @@ public override string ToString() } /// -/// Empty quote provider for parent-less QuoteHub +/// Empty quote provider for base Quote Hub initialization. /// +/// Internal use only. Do not use directly. /// -internal class QuoteProvider - : StreamProvider, IQuoteProvider - where TQuote : IQuote; +public class EmptyQuoteProvider + : IQuoteProvider + where TQuote : IQuote +{ + /// + /// Default quote provider is parent-less Quote Hub. + /// It does not transfer its setting to its children. + /// + public BinarySettings Properties { get; } = new(0b00000001, 0b11111110); + public int ObserverCount => 0; + public bool HasObservers => false; + public IReadOnlyList Quotes { get; } = Array.Empty(); + public IReadOnlyList GetCacheRef() => Array.Empty(); + public bool HasSubscriber(IStreamObserver observer) => false; + + public IDisposable Subscribe(IStreamObserver observer) + => throw new InvalidOperationException(); + + public bool Unsubscribe(IStreamObserver observer) + => throw new InvalidOperationException(); + + public void EndTransmission() + => throw new InvalidOperationException(); +} diff --git a/src/_common/Quotes/Quote.Validation.cs b/src/_common/Quotes/Quote.Validation.cs index 76caa4865..5713b6105 100644 --- a/src/_common/Quotes/Quote.Validation.cs +++ b/src/_common/Quotes/Quote.Validation.cs @@ -2,35 +2,60 @@ namespace Skender.Stock.Indicators; -// QUOTE UTILITIES +// QUOTE UTILITIES: VALIDATION -public static partial class Utility +public static partial class Quotes { - private static readonly CultureInfo invCulture = CultureInfo.InvariantCulture; + private static readonly CultureInfo invariantCulture + = CultureInfo.InvariantCulture; - // VALIDATION - /// - /// + /// + /// Check that quotes are valid and in ascending order. + /// + /// IQuote type + /// List of quotes + /// Valid list of quotes + /// + /// List of quotes cannot be a null reference. + /// + /// + /// Duplicate or out of sequence quotes found. + /// public static IReadOnlyList Validate( - this IEnumerable quotes) + this IReadOnlyList quotes) where TQuote : IQuote { - // we cannot rely on date consistency when looking back, so we force sort - List quotesList = quotes.ToSortedList(); + ArgumentNullException.ThrowIfNull(quotes); - // check for duplicates - DateTime lastDate = DateTime.MinValue; - foreach (TQuote q in quotesList) + if (quotes.Count == 0) { - if (lastDate == q.Timestamp) + return quotes; + } + + DateTime lastDate = quotes[0].Timestamp; + for (int i = 1; i < quotes.Count; i++) + { + DateTime currentDate = quotes[i].Timestamp; + + if (lastDate == currentDate) { - throw new InvalidQuotesException( - $"Duplicate date found on {q.Timestamp.ToString("o", invCulture)}."); + string msg = + $"Duplicate date found on {currentDate.ToString("o", invariantCulture)}."; + + throw new InvalidQuotesException(nameof(quotes), msg); + } + + if (lastDate > currentDate) + { + string msg = + $"Quotes are out of sequence on {currentDate.ToString("o", invariantCulture)}."; + + throw new InvalidQuotesException(nameof(quotes), msg); } - lastDate = q.Timestamp; + lastDate = currentDate; } - return quotesList; + return quotes; } } diff --git a/src/_common/Quotes/info.xml b/src/_common/Quotes/info.xml index d172a60d0..721479e5b 100644 --- a/src/_common/Quotes/info.xml +++ b/src/_common/Quotes/info.xml @@ -2,21 +2,6 @@ - - - Validate historical quotes. - - See - documentation - for more information. - - - Configurable Quote type. See Guide for more information. - Historical price quotes. - Time series of historical quote values. - Validation check failed. - - Converts historical quotes into larger bar sizes. @@ -49,4 +34,4 @@ Invalid parameter value provided. - \ No newline at end of file + diff --git a/src/_common/Reusable/Reusable.Models.cs b/src/_common/Reusable/Reusable.Models.cs deleted file mode 100644 index 201f94a32..000000000 --- a/src/_common/Reusable/Reusable.Models.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Skender.Stock.Indicators; - -/// -/// Reusable base result model for chainable indicators. -/// -/// -public abstract record Reusable( - DateTime Timestamp -) : IReusable -{ - // we've done it this way to avoid - // having to seal the inherited classes - // to enable inheritance, but without - // instantiating the Value property - - public abstract double Value { get; } -} diff --git a/src/_common/Reusable/Reusable.Utilities.cs b/src/_common/Reusable/Reusable.Utilities.cs index ddef2bb6f..d0439f591 100644 --- a/src/_common/Reusable/Reusable.Utilities.cs +++ b/src/_common/Reusable/Reusable.Utilities.cs @@ -2,11 +2,11 @@ namespace Skender.Stock.Indicators; // REUSABLE TYPE UTILITIES -public static partial class Utility +public static partial class Reusable { // convert IQuote type list to IReusable list public static IReadOnlyList ToReusableList( - this IEnumerable quotes, + this IReadOnlyList quotes, CandlePart candlePart) where TQuote : IQuote @@ -17,16 +17,12 @@ public static IReadOnlyList ToReusableList( /// /// Removes non-essential records containing null or NaN values. - /// See - /// documentation for more information. - /// /// /// Any reusable result type. /// Indicator results to evaluate. /// Time series of indicator results, condensed. public static IReadOnlyList Condense( - this IEnumerable results) + this IReadOnlyList results) where T : IReusable { List resultsList = results @@ -42,16 +38,12 @@ public static IReadOnlyList Condense( /// /// Removes the recommended quantity of results from the beginning /// of the results list using a reverse-engineering approach. - /// See - /// - /// documentation for more information. - /// /// /// Any reusable result type. /// Indicator results to evaluate. /// Time series of results, pruned. internal static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) where T : IReusable { // this is the default implementation, it will @@ -62,6 +54,10 @@ internal static IReadOnlyList RemoveWarmupPeriods( .FindIndex(x => !double.IsNaN(x.Value)); return results.Remove(removePeriods); + + // TODO: remove specific indicator 'RemoveWarmupPeriods()' methods + // that are now redundant to this generic method (not all are). + // Note: Some or all of these may already be removed. } // convert TQuote element to a basic chainable class diff --git a/src/_common/Use (QuotePart)/QuotePart.Api.cs b/src/_common/Use (QuotePart)/QuotePart.Api.cs deleted file mode 100644 index 8f60682c2..000000000 --- a/src/_common/Use (QuotePart)/QuotePart.Api.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace Skender.Stock.Indicators; - -// QUOTEPART (API) - -public static partial class Utility -{ - // SERIES, from Quotes - /// - /// Converts to - /// an list. - /// - /// - /// Use this conversion if indicator needs to - /// use something other than the default Close price. - /// - /// - /// List of IQuote or IReusable items - /// - /// List of IReusable items - public static IReadOnlyList ToQuotePart( - this IEnumerable quotes, - CandlePart candlePart) - where TQuote : IQuote - => quotes - .OrderBy(q => q.Timestamp) - .Select(q => q.ToQuotePart(candlePart)) - .ToList(); - - // OBSERVER, from Quote Provider - public static QuotePartHub ToQuotePart( - this IQuoteProvider quoteProvider, - CandlePart candlePart) - where TIn : IQuote - => new(quoteProvider, candlePart); -} diff --git a/src/_common/Use (QuotePart)/QuotePart.Models.cs b/src/_common/Use (QuotePart)/QuotePart.Models.cs index ff857c126..60627a28a 100644 --- a/src/_common/Use (QuotePart)/QuotePart.Models.cs +++ b/src/_common/Use (QuotePart)/QuotePart.Models.cs @@ -7,12 +7,12 @@ public record QuotePart ( DateTime Timestamp, double Value -) : Reusable(Timestamp) +) : IReusable { public QuotePart(IReusable reusable) : this(reusable?.Timestamp ?? default, reusable?.Value ?? default) { } - public override double Value { get; } = Value; + public double Value { get; } = Value; } diff --git a/src/_common/Use (QuotePart)/QuotePart.StaticSeries.cs b/src/_common/Use (QuotePart)/QuotePart.StaticSeries.cs new file mode 100644 index 000000000..53821b9f0 --- /dev/null +++ b/src/_common/Use (QuotePart)/QuotePart.StaticSeries.cs @@ -0,0 +1,45 @@ +namespace Skender.Stock.Indicators; + +// USE / QUOTE CONVERTER (SERIES) + +public static partial class QuoteParts +{ + /// + /// Converts to + /// an list. + /// + /// + /// Use this conversion if indicator needs to + /// use something other than the default Close price. + /// + /// + /// Sorted list of IQuote or IReusable items + /// + /// List of IReusable items + public static IReadOnlyList ToQuotePart( + this IReadOnlyList quotes, + CandlePart candlePart) + where TQuote : IQuote + { + ArgumentNullException.ThrowIfNull(quotes); + int length = quotes.Count; + List result = new(length); + + for (int i = 0; i < length; i++) + { + result.Add(quotes[i].ToQuotePart(candlePart)); + } + return result; + } + + // QuotePart alias + /// + public static IReadOnlyList Use( + this IReadOnlyList quotes, + CandlePart candlePart) + where TQuote : IQuote + => ToQuotePart(quotes, candlePart); + + // TODO: should we deprecate Use in favor of "ToQuotePart"? + // Probably not, this is a fairly simple alias. +} diff --git a/src/_common/Use (QuotePart)/Use.StreamHub.cs b/src/_common/Use (QuotePart)/QuotePart.StreamHub.cs similarity index 57% rename from src/_common/Use (QuotePart)/Use.StreamHub.cs rename to src/_common/Use (QuotePart)/QuotePart.StreamHub.cs index 0a044fe53..4e7448fbe 100644 --- a/src/_common/Use (QuotePart)/Use.StreamHub.cs +++ b/src/_common/Use (QuotePart)/QuotePart.StreamHub.cs @@ -2,17 +2,26 @@ namespace Skender.Stock.Indicators; // USE / QUOTE CONVERTER (STREAM HUB) -#region hub interface +#region hub interface and initializer public interface IQuotePartHub { CandlePart CandlePartSelection { get; } // TODO: consider renaming to IBarPartHub, with IQuote to IBar } + +public static partial class QuoteParts +{ + public static QuotePartHub ToQuotePart( + this IQuoteProvider quoteProvider, + CandlePart candlePart) + where TIn : IQuote + => new(quoteProvider, candlePart); +} #endregion -public class QuotePartHub : QuoteObserver, - IReusableHub, IQuotePartHub +public class QuotePartHub + : ChainProvider, IQuotePartHub where TQuote : IQuote { #region constructors @@ -32,14 +41,14 @@ internal QuotePartHub( // METHODS - internal override void Add(Act act, TQuote newIn, int? index) + protected override (QuotePart result, int index) + ToIndicator(TQuote item, int? indexHint) { // candidate result QuotePart r - = newIn.ToQuotePart(CandlePartSelection); + = item.ToQuotePart(CandlePartSelection); - // save and send - Motify(act, r, index); + return (r, indexHint ?? Cache.Count); } public override string ToString() diff --git a/src/_common/Use (QuotePart)/QuotePart.Utilities.cs b/src/_common/Use (QuotePart)/QuotePart.Utilities.cs index 398daa587..487cc7173 100644 --- a/src/_common/Use (QuotePart)/QuotePart.Utilities.cs +++ b/src/_common/Use (QuotePart)/QuotePart.Utilities.cs @@ -2,7 +2,7 @@ namespace Skender.Stock.Indicators; // QUOTEPART TYPE UTILITIES -public static partial class Utility +public static partial class QuoteParts { // convert TQuote element to a basic QuotePart class internal static QuotePart ToQuotePart(this IQuote q, CandlePart candlePart) @@ -60,19 +60,19 @@ internal static double QuotePartOrValue( /// List of IQuote or IReusable items /// /// List of IReusable items - internal static List ToSortedList( - this IEnumerable items, CandlePart candlePart) - where T : IReusable - - => typeof(IQuote).IsAssignableFrom(typeof(T)) - - ? items - .Cast() - .Use(candlePart) - .Cast() - .ToSortedList() + internal static IReadOnlyList ToPreferredList( + this IReadOnlyList items, CandlePart candlePart) + where T : IReusable + { + ArgumentNullException.ThrowIfNull(items); - : items - .Cast() - .ToSortedList(); + if (items is IReadOnlyList quotes) + { + return quotes.ToQuotePart(candlePart); + } + else + { + return items.Cast().ToList(); + } + } } diff --git a/src/_common/Use (QuotePart)/Use.Api.cs b/src/_common/Use (QuotePart)/Use.Api.cs deleted file mode 100644 index 04147e4f9..000000000 --- a/src/_common/Use (QuotePart)/Use.Api.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace Skender.Stock.Indicators; - -// USE (API) - -public static partial class Utility -{ - // SERIES, from Quotes - public static IReadOnlyList Use( - this IEnumerable quotes, - CandlePart candlePart) - => quotes - .OrderBy(q => q.Timestamp) - .Select(q => q.ToQuotePart(candlePart)) - .ToList(); - - // SERIES, from Quotes - public static IReadOnlyList Use( - this IEnumerable quotes, - CandlePart candlePart) - where TQuote : IQuote - => quotes - .OrderBy(q => q.Timestamp) - .Select(q => q.ToQuotePart(candlePart)) - .ToList(); -} diff --git a/src/a-d/Adl/Adl.Api.cs b/src/a-d/Adl/Adl.Api.cs deleted file mode 100644 index e7a08f051..000000000 --- a/src/a-d/Adl/Adl.Api.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ACCUMULATION/DISTRIBUTION LINE (API) - -public static partial class Adl -{ - // SERIES, from TQuote - /// - ///Accumulation / Distribution Line(ADL) is a rolling accumulation of Chaikin Money Flow Volume. - /// - ///See - /// documentation - ///for more information. - /// - /// - /// Configurable Quote type. See Guide for more information. - ///Historical price quotes. - ///Time series of ADL values. - public static IReadOnlyList GetAdl( - this IEnumerable quotes) - where TQuote : IQuote - => quotes - .ToSortedList() - .CalcAdl(); - - // OBSERVER, from Quote Provider - public static AdlHub ToAdl( - this IQuoteProvider quoteProvider) - where TIn : IQuote - => new(quoteProvider); -} diff --git a/src/a-d/Adl/Adl.Models.cs b/src/a-d/Adl/Adl.Models.cs index d17d41031..a5fd38014 100644 --- a/src/a-d/Adl/Adl.Models.cs +++ b/src/a-d/Adl/Adl.Models.cs @@ -6,7 +6,7 @@ public record AdlResult double Adl, double? MoneyFlowMultiplier = null, double? MoneyFlowVolume = null -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Adl; + public double Value => Adl; } diff --git a/src/a-d/Adl/Adl.StaticSeries.cs b/src/a-d/Adl/Adl.StaticSeries.cs index 8b7c17cea..10f833ed4 100644 --- a/src/a-d/Adl/Adl.StaticSeries.cs +++ b/src/a-d/Adl/Adl.StaticSeries.cs @@ -4,10 +4,12 @@ namespace Skender.Stock.Indicators; public static partial class Adl { - internal static List CalcAdl( - this List source) + public static IReadOnlyList ToAdl( + this IReadOnlyList source) where TQuote : IQuote { + ArgumentNullException.ThrowIfNull(source); + // initialize int length = source.Count; List results = new(length); diff --git a/src/a-d/Adl/Adl.StreamHub.cs b/src/a-d/Adl/Adl.StreamHub.cs index 50870273a..e336e51b3 100644 --- a/src/a-d/Adl/Adl.StreamHub.cs +++ b/src/a-d/Adl/Adl.StreamHub.cs @@ -2,8 +2,18 @@ namespace Skender.Stock.Indicators; // ACCUMULATION/DISTRIBUTION LINE (STREAM HUB) -public class AdlHub : QuoteObserver, - IReusableHub +#region hub initializer + +public static partial class Adl +{ + public static AdlHub ToAdl( + this IQuoteProvider quoteProvider) + where TIn : IQuote + => new(quoteProvider); +} +#endregion + +public class AdlHub : ChainProvider where TIn : IQuote { #region constructors @@ -17,30 +27,22 @@ internal AdlHub(IQuoteProvider provider) // METHODS - internal override void Add(Act act, TIn newIn, int? index) + protected override (AdlResult result, int index) + ToIndicator(TIn item, int? indexHint) { - int i = index ?? Provider.GetIndex(newIn, false); + int i = indexHint ?? ProviderCache.GetIndex(item, true); // candidate result AdlResult r = Adl.Increment( - newIn.Timestamp, - newIn.High, - newIn.Low, - newIn.Close, - newIn.Volume, + item.Timestamp, + item.High, + item.Low, + item.Close, + item.Volume, i > 0 ? Cache[i - 1].Value : 0); - // save and send - Motify(act, r, i); + return (r, i); } - public override string ToString() - { - if (Cache.Count == 0) - { - return "ADL"; - } - - return $"ADL({Cache[0].Timestamp:d})"; - } + public override string ToString() => Cache.Count == 0 ? "ADL" : $"ADL({Cache[0].Timestamp:d})"; } diff --git a/src/a-d/Adl/Adl.Utilities.cs b/src/a-d/Adl/Adl.Utilities.cs index f54bf9c4a..d964a392a 100644 --- a/src/a-d/Adl/Adl.Utilities.cs +++ b/src/a-d/Adl/Adl.Utilities.cs @@ -1,6 +1,6 @@ namespace Skender.Stock.Indicators; -// ACCUMULATION/DISTRIBUTION LINE (COMMON) +// ACCUMULATION/DISTRIBUTION LINE (UTILITIES) /// /// See the @@ -10,10 +10,8 @@ public static partial class Adl { /// /// Get the next incremental Accumulation/Distribution Line(ADL) value. - /// See documentation - /// for more information. /// - /// Timestamp + /// timestamp /// High price, current period /// Low price, current period /// Close price, current period diff --git a/src/a-d/Adx/Adx.Api.cs b/src/a-d/Adx/Adx.Api.cs deleted file mode 100644 index c578dad40..000000000 --- a/src/a-d/Adx/Adx.Api.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Skender.Stock.Indicators; - -// AVERAGE DIRECTIONAL INDEX (API) - -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetAdx( - this IEnumerable quotes, - int lookbackPeriods = 14) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcAdx(lookbackPeriods); -} diff --git a/src/a-d/Adx/Adx.Common.cs b/src/a-d/Adx/Adx.Common.cs deleted file mode 100644 index deb21a614..000000000 --- a/src/a-d/Adx/Adx.Common.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Skender.Stock.Indicators; - -// AVERAGE DIRECTIONAL INDEX (COMMON) - -public static class Adx -{ - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 1 for ADX."); - } - } -} diff --git a/src/a-d/Adx/Adx.Models.cs b/src/a-d/Adx/Adx.Models.cs index 3673d8acc..609131564 100644 --- a/src/a-d/Adx/Adx.Models.cs +++ b/src/a-d/Adx/Adx.Models.cs @@ -7,7 +7,7 @@ public record AdxResult double? Mdi = null, double? Adx = null, double? Adxr = null -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Adx.Null2NaN(); + public double Value => Adx.Null2NaN(); } diff --git a/src/a-d/Adx/Adx.StaticSeries.cs b/src/a-d/Adx/Adx.StaticSeries.cs index 13b89108a..45839098a 100644 --- a/src/a-d/Adx/Adx.StaticSeries.cs +++ b/src/a-d/Adx/Adx.StaticSeries.cs @@ -2,14 +2,21 @@ namespace Skender.Stock.Indicators; // AVERAGE DIRECTIONAL INDEX (SERIES) -public static partial class Indicator +public static partial class Adx { + public static IReadOnlyList ToAdx( + this IReadOnlyList quotes, + int lookbackPeriods = 14) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcAdx(lookbackPeriods); + private static List CalcAdx( - this List source, - int lookbackPeriods) + this IReadOnlyList source, + int lookbackPeriods = 14) { // check parameter arguments - Adx.Validate(lookbackPeriods); + Validate(lookbackPeriods); // initialize int length = source.Count; @@ -96,7 +103,7 @@ private static List CalcAdx( prevPdm = pdm; prevMdm = mdm; - if (trs is 0) + if (trs == 0) { results.Add(new(Timestamp: q.Timestamp)); continue; diff --git a/src/a-d/Adx/Adx.Utilities.cs b/src/a-d/Adx/Adx.Utilities.cs index 67e984948..1611769cd 100644 --- a/src/a-d/Adx/Adx.Utilities.cs +++ b/src/a-d/Adx/Adx.Utilities.cs @@ -1,16 +1,30 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// AVERAGE DIRECTIONAL INDEX (UTILITIES) + +public static partial class Adx { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int n = results .ToList() .FindIndex(x => x.Pdi != null); - return results.Remove(2 * n + 100); + return results.Remove((2 * n) + 100); + } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 1 for ADX."); + } } } diff --git a/src/a-d/Alligator/Alligator.Api.cs b/src/a-d/Alligator/Alligator.Api.cs deleted file mode 100644 index 6d0c047eb..000000000 --- a/src/a-d/Alligator/Alligator.Api.cs +++ /dev/null @@ -1,69 +0,0 @@ -namespace Skender.Stock.Indicators; - -// WILLIAMS ALLIGATOR (API) - -public static partial class Alligator -{ - // SERIES, from CHAIN - /// - /// Williams Alligator is an indicator that transposes multiple moving averages, - /// showing chart patterns that creator Bill Williams compared to an alligator's - /// feeding habits when describing market movement. - /// - /// See - /// documentation - /// for more information. - /// - /// - /// - /// T must be or type - /// - /// Time-series values to transform. - /// Lookback periods for the Jaw line. - /// Offset periods for the Jaw line. - /// Lookback periods for the Teeth line. - /// Offset periods for the Teeth line. - /// Lookback periods for the Lips line. - /// Offset periods for the Lips line. - /// Time series of Alligator values. - /// - /// Invalid parameter value provided. - /// - public static IReadOnlyList GetAlligator( - this IEnumerable source, - int jawPeriods = 13, - int jawOffset = 8, - int teethPeriods = 8, - int teethOffset = 5, - int lipsPeriods = 5, - int lipsOffset = 3) - where T : IReusable - => source - .ToSortedList(CandlePart.HL2) - .CalcAlligator( - jawPeriods, - jawOffset, - teethPeriods, - teethOffset, - lipsPeriods, - lipsOffset); - - // HUB, from Chain Provider - public static AlligatorHub ToAlligator( - this IChainProvider chainProvider, - int jawPeriods = 13, - int jawOffset = 8, - int teethPeriods = 8, - int teethOffset = 5, - int lipsPeriods = 5, - int lipsOffset = 3) - where TIn : IReusable - => new( - chainProvider, - jawPeriods, - jawOffset, - teethPeriods, - teethOffset, - lipsPeriods, - lipsOffset); -} diff --git a/src/a-d/Alligator/Alligator.StaticSeries.cs b/src/a-d/Alligator/Alligator.StaticSeries.cs index fffcf84e0..f35217b70 100644 --- a/src/a-d/Alligator/Alligator.StaticSeries.cs +++ b/src/a-d/Alligator/Alligator.StaticSeries.cs @@ -4,14 +4,34 @@ namespace Skender.Stock.Indicators; public static partial class Alligator { - internal static List CalcAlligator( - this List source, - int jawPeriods, - int jawOffset, - int teethPeriods, - int teethOffset, - int lipsPeriods, - int lipsOffset) + // SERIES, from CHAIN + /// + /// Williams Alligator is an indicator that transposes multiple moving averages, + /// showing chart patterns that creator Bill Williams compared to an alligator's + /// feeding habits when describing market movement. + /// + /// + /// T must be or type + /// + /// Time-series values to transform. + /// Lookback periods for the Jaw line. + /// Offset periods for the Jaw line. + /// Lookback periods for the Teeth line. + /// Offset periods for the Teeth line. + /// Lookback periods for the Lips line. + /// Offset periods for the Lips line. + /// Time series of Alligator values. + /// + /// Invalid parameter value provided. + /// + public static IReadOnlyList ToAlligator( + this IReadOnlyList source, + int jawPeriods = 13, + int jawOffset = 8, + int teethPeriods = 8, + int teethOffset = 5, + int lipsPeriods = 5, + int lipsOffset = 3) where T : IReusable { // check parameter arguments @@ -23,8 +43,12 @@ internal static List CalcAlligator( lipsPeriods, lipsOffset); + // prefer HL2 when IQuote + IReadOnlyList values + = source.ToPreferredList(CandlePart.HL2); + // initialize - int length = source.Count; + int length = values.Count; List results = new(length); // roll through source values @@ -37,15 +61,13 @@ internal static List CalcAlligator( // calculate alligator's jaw, when in range if (i >= jawPeriods + jawOffset - 1) { - double prevJaw = results[i - 1].Jaw.Null2NaN(); - // first/reset value: calculate SMA - if (double.IsNaN(prevJaw)) + if (results[i - 1].Jaw is null) { double sum = 0; for (int p = i - jawPeriods - jawOffset + 1; p <= i - jawOffset; p++) { - sum += source[p].Value; + sum += values[p].Value; } jaw = sum / jawPeriods; @@ -54,22 +76,22 @@ internal static List CalcAlligator( // remaining values: SMMA else { - jaw = ((prevJaw * (jawPeriods - 1)) + source[i - jawOffset].Value) / jawPeriods; + double prevJaw = results[i - 1].Jaw.Null2NaN(); + + jaw = ((prevJaw * (jawPeriods - 1)) + values[i - jawOffset].Value) / jawPeriods; } } // calculate alligator's teeth, when in range if (i >= teethPeriods + teethOffset - 1) { - double prevTooth = results[i - 1].Teeth.Null2NaN(); - // first/reset value: calculate SMA - if (double.IsNaN(prevTooth)) + if (results[i - 1].Teeth is null) { double sum = 0; for (int p = i - teethPeriods - teethOffset + 1; p <= i - teethOffset; p++) { - sum += source[p].Value; + sum += values[p].Value; } teeth = sum / teethPeriods; @@ -78,22 +100,22 @@ internal static List CalcAlligator( // remaining values: SMMA else { - teeth = ((prevTooth * (teethPeriods - 1)) + source[i - teethOffset].Value) / teethPeriods; + double prevTooth = results[i - 1].Teeth.Null2NaN(); + + teeth = ((prevTooth * (teethPeriods - 1)) + values[i - teethOffset].Value) / teethPeriods; } } // calculate alligator's lips, when in range if (i >= lipsPeriods + lipsOffset - 1) { - double prevLips = results[i - 1].Lips.Null2NaN(); - // first/reset value: calculate SMA - if (double.IsNaN(prevLips)) + if (results[i - 1].Lips is null) { double sum = 0; for (int p = i - lipsPeriods - lipsOffset + 1; p <= i - lipsOffset; p++) { - sum += source[p].Value; + sum += values[p].Value; } lips = sum / lipsPeriods; @@ -102,13 +124,18 @@ internal static List CalcAlligator( // remaining values: SMMA else { - lips = ((prevLips * (lipsPeriods - 1)) + source[i - lipsOffset].Value) / lipsPeriods; + double prevLips = results[i - 1].Lips.Null2NaN(); + + lips = ((prevLips * (lipsPeriods - 1)) + values[i - lipsOffset].Value) / lipsPeriods; } } // result results.Add(new AlligatorResult( - source[i].Timestamp, jaw.NaN2Null(), teeth.NaN2Null(), lips.NaN2Null())); + values[i].Timestamp, + jaw.NaN2Null(), + teeth.NaN2Null(), + lips.NaN2Null())); } return results; diff --git a/src/a-d/Alligator/Alligator.StreamHub.cs b/src/a-d/Alligator/Alligator.StreamHub.cs index 5f6d65baa..8ade9b1ac 100644 --- a/src/a-d/Alligator/Alligator.StreamHub.cs +++ b/src/a-d/Alligator/Alligator.StreamHub.cs @@ -2,7 +2,7 @@ namespace Skender.Stock.Indicators; // WILLIAMS ALLIGATOR (STREAM HUB) -#region hub interface +#region hub interface and initializer public interface IAlligatorHub { @@ -13,10 +13,32 @@ public interface IAlligatorHub int LipsPeriods { get; } int LipsOffset { get; } } + +public static partial class Alligator +{ + // HUB, from Chain Provider + public static AlligatorHub ToAlligator( + this IChainProvider chainProvider, + int jawPeriods = 13, + int jawOffset = 8, + int teethPeriods = 8, + int teethOffset = 5, + int lipsPeriods = 5, + int lipsOffset = 3) + where TIn : IReusable + => new( + chainProvider, + jawPeriods, + jawOffset, + teethPeriods, + teethOffset, + lipsPeriods, + lipsOffset); +} #endregion -public class AlligatorHub : ReusableObserver, - IResultHub, IAlligatorHub +public class AlligatorHub + : StreamHub, IAlligatorHub where TIn : IReusable { #region constructors @@ -59,27 +81,25 @@ internal AlligatorHub( public override string ToString() => hubName; - internal override void Add(Act act, TIn newIn, int? index) + protected override (AlligatorResult result, int index) + ToIndicator(TIn item, int? indexHint) { double jaw = double.NaN; double lips = double.NaN; double teeth = double.NaN; - int i = index ?? Provider.GetIndex(newIn, false); + int i = indexHint ?? ProviderCache.GetIndex(item, true); // calculate alligator's jaw, when in range if (i >= JawPeriods + JawOffset - 1) { - AlligatorResult prev = Cache[i - 1]; - double prevJaw = prev.Jaw.Null2NaN(); - // first/reset value: calculate SMA - if (double.IsNaN(prevJaw)) + if (Cache[i - 1].Jaw is null) { double sum = 0; for (int p = i - JawPeriods - JawOffset + 1; p <= i - JawOffset; p++) { - sum += Provider.Results[p].Hl2OrValue(); + sum += ProviderCache[p].Hl2OrValue(); } jaw = sum / JawPeriods; @@ -88,7 +108,9 @@ internal override void Add(Act act, TIn newIn, int? index) // remaining values: SMMA else { - double newVal = Provider.Results[i - JawOffset].Hl2OrValue(); + double prevJaw = Cache[i - 1].Jaw.Null2NaN(); + double newVal = ProviderCache[i - JawOffset].Hl2OrValue(); + jaw = ((prevJaw * (JawPeriods - 1)) + newVal) / JawPeriods; } } @@ -96,17 +118,13 @@ internal override void Add(Act act, TIn newIn, int? index) // calculate alligator's teeth, when in range if (i >= TeethPeriods + TeethOffset - 1) { - AlligatorResult prev = Cache[i - 1]; - - double prevTooth = prev.Teeth.Null2NaN(); - // first/reset value: calculate SMA - if (double.IsNaN(prevTooth)) + if (Cache[i - 1].Teeth is null) { double sum = 0; for (int p = i - TeethPeriods - TeethOffset + 1; p <= i - TeethOffset; p++) { - sum += Provider.Results[p].Hl2OrValue(); + sum += ProviderCache[p].Hl2OrValue(); } teeth = sum / TeethPeriods; @@ -115,7 +133,9 @@ internal override void Add(Act act, TIn newIn, int? index) // remaining values: SMMA else { - double newVal = Provider.Results[i - TeethOffset].Hl2OrValue(); + double prevTooth = Cache[i - 1].Teeth.Null2NaN(); + double newVal = ProviderCache[i - TeethOffset].Hl2OrValue(); + teeth = ((prevTooth * (TeethPeriods - 1)) + newVal) / TeethPeriods; } } @@ -123,18 +143,13 @@ internal override void Add(Act act, TIn newIn, int? index) // calculate alligator's lips, when in range if (i >= LipsPeriods + LipsOffset - 1) { - AlligatorResult prev = Cache[i - 1]; - - double prevLips = prev.Lips.Null2NaN(); - // first/reset value: calculate SMA - if (double.IsNaN(prevLips)) + if (Cache[i - 1].Lips is null) { - // TODO: refactor - add offset to, and use Sma.Increment(...,offset) double sum = 0; for (int p = i - LipsPeriods - LipsOffset + 1; p <= i - LipsOffset; p++) { - sum += Provider.Results[p].Hl2OrValue(); + sum += ProviderCache[p].Hl2OrValue(); } lips = sum / LipsPeriods; @@ -143,16 +158,20 @@ internal override void Add(Act act, TIn newIn, int? index) // remaining values: SMMA else { - double newVal = Provider.Results[i - LipsOffset].Hl2OrValue(); + double prevLips = Cache[i - 1].Lips.Null2NaN(); + double newVal = ProviderCache[i - LipsOffset].Hl2OrValue(); + lips = ((prevLips * (LipsPeriods - 1)) + newVal) / LipsPeriods; } } // candidate result AlligatorResult r = new( - newIn.Timestamp, jaw.NaN2Null(), teeth.NaN2Null(), lips.NaN2Null()); + item.Timestamp, + jaw.NaN2Null(), + teeth.NaN2Null(), + lips.NaN2Null()); - // save and send - Motify(act, r, i); + return (r, i); } } diff --git a/src/a-d/Alligator/Alligator.Utilities.cs b/src/a-d/Alligator/Alligator.Utilities.cs index 59cae1a79..03050cb34 100644 --- a/src/a-d/Alligator/Alligator.Utilities.cs +++ b/src/a-d/Alligator/Alligator.Utilities.cs @@ -1,5 +1,7 @@ namespace Skender.Stock.Indicators; +// WILLIAMS ALLIGATOR (UTILITIES) + public static partial class Alligator { // CONDENSE (REMOVE null results) @@ -18,7 +20,7 @@ public static IReadOnlyList Condense( // remove recommended periods public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int removePeriods = results .ToList() diff --git a/src/a-d/Alma/Alma.Api.cs b/src/a-d/Alma/Alma.Api.cs deleted file mode 100644 index 64b49785a..000000000 --- a/src/a-d/Alma/Alma.Api.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ARNAUD LEGOUX MOVING AVERAGE (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetAlma( - this IEnumerable results, - int lookbackPeriods = 9, - double offset = 0.85, - double sigma = 6) - where T : IReusable - => results - .ToSortedList() - .CalcAlma(lookbackPeriods, offset, sigma); -} diff --git a/src/a-d/Alma/Alma.Common.cs b/src/a-d/Alma/Alma.Common.cs deleted file mode 100644 index 8648271b5..000000000 --- a/src/a-d/Alma/Alma.Common.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ARNAUD LEGOUX MOVING AVERAGE (COMMON) - -public static class Alma -{ - // parameter validation - internal static void Validate( - int lookbackPeriods, - double offset, - double sigma) - { - // check parameter arguments - if (lookbackPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 1 for ALMA."); - } - - if (offset is < 0 or > 1) - { - throw new ArgumentOutOfRangeException(nameof(offset), offset, - "Offset must be between 0 and 1 for ALMA."); - } - - if (sigma <= 0) - { - throw new ArgumentOutOfRangeException(nameof(sigma), sigma, - "Sigma must be greater than 0 for ALMA."); - } - } - -} diff --git a/src/a-d/Alma/Alma.Models.cs b/src/a-d/Alma/Alma.Models.cs index e14e5b8df..c245ac967 100644 --- a/src/a-d/Alma/Alma.Models.cs +++ b/src/a-d/Alma/Alma.Models.cs @@ -4,7 +4,7 @@ public record AlmaResult ( DateTime Timestamp, double? Alma -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Alma.Null2NaN(); + public double Value => Alma.Null2NaN(); } diff --git a/src/a-d/Alma/Alma.StaticSeries.cs b/src/a-d/Alma/Alma.StaticSeries.cs index 34ee39a8e..372f68c9e 100644 --- a/src/a-d/Alma/Alma.StaticSeries.cs +++ b/src/a-d/Alma/Alma.StaticSeries.cs @@ -2,17 +2,18 @@ namespace Skender.Stock.Indicators; // ARNAUD LEGOUX MOVING AVERAGE (SERIES) -public static partial class Indicator +public static partial class Alma { - private static List CalcAlma( - this List source, - int lookbackPeriods, - double offset, - double sigma) + public static IReadOnlyList ToAlma( + this IReadOnlyList source, + int lookbackPeriods = 9, + double offset = 0.85, + double sigma = 6) where T : IReusable { // check parameter arguments - Alma.Validate(lookbackPeriods, offset, sigma); + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods, offset, sigma); // initialize int length = source.Count; diff --git a/src/a-d/Alma/Alma.Utilities.cs b/src/a-d/Alma/Alma.Utilities.cs index bdf32d6ab..da7f11eb8 100644 --- a/src/a-d/Alma/Alma.Utilities.cs +++ b/src/a-d/Alma/Alma.Utilities.cs @@ -1,16 +1,32 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// ARNAUD LEGOUX MOVING AVERAGE (UTILITIES) + +public static partial class Alma { - // remove recommended periods - /// - public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods, + double offset, + double sigma) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Alma != null); + // check parameter arguments + if (lookbackPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 1 for ALMA."); + } + + if (offset is < 0 or > 1) + { + throw new ArgumentOutOfRangeException(nameof(offset), offset, + "Offset must be between 0 and 1 for ALMA."); + } - return results.Remove(removePeriods); + if (sigma <= 0) + { + throw new ArgumentOutOfRangeException(nameof(sigma), sigma, + "Sigma must be greater than 0 for ALMA."); + } } } diff --git a/src/a-d/Aroon/Aroon.Api.cs b/src/a-d/Aroon/Aroon.Api.cs deleted file mode 100644 index 9abeee793..000000000 --- a/src/a-d/Aroon/Aroon.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// AROON OSCILLATOR (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetAroon( - this IEnumerable quotes, - int lookbackPeriods = 25) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcAroon(lookbackPeriods); -} diff --git a/src/a-d/Aroon/Aroon.Common.cs b/src/a-d/Aroon/Aroon.Common.cs deleted file mode 100644 index 4c0c30bf8..000000000 --- a/src/a-d/Aroon/Aroon.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// AROON OSCILLATOR (COMMON) - -public static class Aroon -{ - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Aroon."); - } - } - -} diff --git a/src/a-d/Aroon/Aroon.Models.cs b/src/a-d/Aroon/Aroon.Models.cs index 7413d6cfd..1e4712be0 100644 --- a/src/a-d/Aroon/Aroon.Models.cs +++ b/src/a-d/Aroon/Aroon.Models.cs @@ -6,7 +6,7 @@ public record AroonResult double? AroonUp, double? AroonDown, double? Oscillator -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Oscillator.Null2NaN(); + public double Value => Oscillator.Null2NaN(); } diff --git a/src/a-d/Aroon/Aroon.StaticSeries.cs b/src/a-d/Aroon/Aroon.StaticSeries.cs index a8af83f1e..4fcdbe3be 100644 --- a/src/a-d/Aroon/Aroon.StaticSeries.cs +++ b/src/a-d/Aroon/Aroon.StaticSeries.cs @@ -2,14 +2,21 @@ namespace Skender.Stock.Indicators; // AROON OSCILLATOR (SERIES) -public static partial class Indicator +public static partial class Aroon { + public static IReadOnlyList ToAroon( + this IReadOnlyList quotes, + int lookbackPeriods = 25) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcAroon(lookbackPeriods); + private static List CalcAroon( - this List source, + this IReadOnlyList source, int lookbackPeriods) { // check parameter arguments - Aroon.Validate(lookbackPeriods); + Validate(lookbackPeriods); // initialize int length = source.Count; diff --git a/src/a-d/Aroon/Aroon.Utilities.cs b/src/a-d/Aroon/Aroon.Utilities.cs index d9819d84f..32f51769d 100644 --- a/src/a-d/Aroon/Aroon.Utilities.cs +++ b/src/a-d/Aroon/Aroon.Utilities.cs @@ -1,16 +1,16 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +public static partial class Aroon { - // remove recommended periods - /// - public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Oscillator != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Aroon."); + } } } diff --git a/src/a-d/Atr/Atr.Api.cs b/src/a-d/Atr/Atr.Api.cs deleted file mode 100644 index a10d67c21..000000000 --- a/src/a-d/Atr/Atr.Api.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Skender.Stock.Indicators; - -// AVERAGE TRUE RANGE (API) - -public static partial class Atr -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetAtr( - this IEnumerable quotes, - int lookbackPeriods = 14) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcAtr(lookbackPeriods); - - // OBSERVER, from Quote Provider - public static AtrHub ToAtr( - this IQuoteProvider quoteProvider, - int lookbackPeriods = 14) - where TIn : IQuote - => new(quoteProvider, lookbackPeriods); -} diff --git a/src/a-d/Atr/Atr.Models.cs b/src/a-d/Atr/Atr.Models.cs index bee607e79..900a749f6 100644 --- a/src/a-d/Atr/Atr.Models.cs +++ b/src/a-d/Atr/Atr.Models.cs @@ -6,7 +6,7 @@ public record AtrResult double? Tr = null, double? Atr = null, double? Atrp = null -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Atrp.Null2NaN(); + public double Value => Atrp.Null2NaN(); } diff --git a/src/a-d/Atr/Atr.StaticSeries.cs b/src/a-d/Atr/Atr.StaticSeries.cs index b517be680..10db80315 100644 --- a/src/a-d/Atr/Atr.StaticSeries.cs +++ b/src/a-d/Atr/Atr.StaticSeries.cs @@ -4,13 +4,19 @@ namespace Skender.Stock.Indicators; public static partial class Atr { - // calculate series + public static IReadOnlyList ToAtr( + this IReadOnlyList quotes, + int lookbackPeriods = 14) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcAtr(lookbackPeriods); + internal static List CalcAtr( - this List source, + this IReadOnlyList source, int lookbackPeriods) { // check parameter arguments - Atr.Validate(lookbackPeriods); + Validate(lookbackPeriods); // initialize int length = source.Count; diff --git a/src/a-d/Atr/Atr.StreamHub.cs b/src/a-d/Atr/Atr.StreamHub.cs index 09660d74c..d37d45062 100644 --- a/src/a-d/Atr/Atr.StreamHub.cs +++ b/src/a-d/Atr/Atr.StreamHub.cs @@ -2,16 +2,25 @@ namespace Skender.Stock.Indicators; // AVERAGE TRUE RANGE (STREAM HUB) -#region hub interface +#region hub interface and initializer public interface IAtrHub { int LookbackPeriods { get; } } + +public static partial class Atr +{ + public static AtrHub ToAtr( + this IQuoteProvider quoteProvider, + int lookbackPeriods = 14) + where TIn : IQuote + => new(quoteProvider, lookbackPeriods); +} #endregion -public class AtrHub : QuoteObserver, - IReusableHub, IAtrHub +public class AtrHub + : ChainProvider, IAtrHub where TIn : IQuote { #region constructors @@ -36,15 +45,15 @@ internal AtrHub(IQuoteProvider provider, public override string ToString() => hubName; - internal override void Add(Act act, TIn newIn, int? index) + protected override (AtrResult result, int index) + ToIndicator(TIn item, int? indexHint) { - int i = index ?? Provider.GetIndex(newIn, false); + int i = indexHint ?? ProviderCache.GetIndex(item, true); // skip incalculable periods if (i == 0) { - Motify(act, new AtrResult(newIn.Timestamp), i); - return; + return (new AtrResult(item.Timestamp), i); } AtrResult r; @@ -58,9 +67,9 @@ internal override void Add(Act act, TIn newIn, int? index) for (int p = i - LookbackPeriods + 1; p <= i; p++) { tr = Tr.Increment( - (double)Provider.Results[p].High, - (double)Provider.Results[p].Low, - (double)Provider.Results[p - 1].Close); + (double)ProviderCache[p].High, + (double)ProviderCache[p].Low, + (double)ProviderCache[p - 1].Close); sumTr += tr; } @@ -68,10 +77,10 @@ internal override void Add(Act act, TIn newIn, int? index) double atr = sumTr / LookbackPeriods; r = new AtrResult( - newIn.Timestamp, + item.Timestamp, tr, atr, - atr / (double)newIn.Close * 100); + atr / (double)item.Close * 100); } // calculate ATR (normally) @@ -79,12 +88,11 @@ internal override void Add(Act act, TIn newIn, int? index) { r = Atr.Increment( LookbackPeriods, - newIn, - (double)Provider.Results[i - 1].Close, + item, + (double)ProviderCache[i - 1].Close, Cache[i - 1].Atr); } - // save and send - Motify(act, r, i); + return (r, i); } } diff --git a/src/a-d/Atr/Atr.Utilities.cs b/src/a-d/Atr/Atr.Utilities.cs index 14239d69a..febeb0aa6 100644 --- a/src/a-d/Atr/Atr.Utilities.cs +++ b/src/a-d/Atr/Atr.Utilities.cs @@ -39,17 +39,6 @@ public static AtrResult Increment( atrp.NaN2Null()); } - // remove recommended periods - public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) - { - int removePeriods = results - .ToList() - .FindIndex(x => x.Atr != null); - - return results.Remove(removePeriods); - } - // parameter validation internal static void Validate( int lookbackPeriods) diff --git a/src/a-d/AtrStop/AtrStop.Api.cs b/src/a-d/AtrStop/AtrStop.Api.cs deleted file mode 100644 index f985cd541..000000000 --- a/src/a-d/AtrStop/AtrStop.Api.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ATR TRAILING STOP (API) - -public static partial class AtrStop -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetAtrStop( - this IEnumerable quotes, - int lookbackPeriods = 21, - double multiplier = 3, - EndType endType = EndType.Close) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcAtrStop(lookbackPeriods, multiplier, endType); - - // OBSERVER, from Quote Provider - public static AtrStopHub ToAtrStop( - this IQuoteProvider quoteProvider, - int lookbackPeriods = 21, - double multiplier = 3, - EndType endType = EndType.Close) - where TIn : IQuote - => new(quoteProvider, lookbackPeriods, multiplier, endType); -} diff --git a/src/a-d/AtrStop/AtrStop.Models.cs b/src/a-d/AtrStop/AtrStop.Models.cs index 69c7b1b46..5966930c6 100644 --- a/src/a-d/AtrStop/AtrStop.Models.cs +++ b/src/a-d/AtrStop/AtrStop.Models.cs @@ -20,8 +20,8 @@ namespace Skender.Stock.Indicators; /// public record AtrStopResult( DateTime Timestamp, - decimal? AtrStop = null, - decimal? BuyStop = null, - decimal? SellStop = null, + double? AtrStop = null, + double? BuyStop = null, + double? SellStop = null, double? Atr = null ) : ISeries; diff --git a/src/a-d/AtrStop/AtrStop.StaticSeries.cs b/src/a-d/AtrStop/AtrStop.StaticSeries.cs index 70521e8dd..b4b840f48 100644 --- a/src/a-d/AtrStop/AtrStop.StaticSeries.cs +++ b/src/a-d/AtrStop/AtrStop.StaticSeries.cs @@ -4,8 +4,17 @@ namespace Skender.Stock.Indicators; public static partial class AtrStop { + public static IReadOnlyList ToAtrStop( + this IReadOnlyList quotes, + int lookbackPeriods = 21, + double multiplier = 3, + EndType endType = EndType.Close) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcAtrStop(lookbackPeriods, multiplier, endType); + private static List CalcAtrStop( - this List source, + this IReadOnlyList source, int lookbackPeriods, double multiplier, EndType endType) @@ -84,8 +93,8 @@ private static List CalcAtrStop( r = new( Timestamp: q.Timestamp, - AtrStop: (decimal)upperBand, - BuyStop: (decimal)upperBand, + AtrStop: upperBand, + BuyStop: upperBand, SellStop: null, Atr: atr); } @@ -97,9 +106,9 @@ private static List CalcAtrStop( r = new( Timestamp: q.Timestamp, - AtrStop: (decimal)lowerBand, + AtrStop: lowerBand, BuyStop: null, - SellStop: (decimal)lowerBand, + SellStop: lowerBand, Atr: atr); } diff --git a/src/a-d/AtrStop/AtrStop.StreamHub.cs b/src/a-d/AtrStop/AtrStop.StreamHub.cs index 808e3f069..20c0e7d0e 100644 --- a/src/a-d/AtrStop/AtrStop.StreamHub.cs +++ b/src/a-d/AtrStop/AtrStop.StreamHub.cs @@ -2,7 +2,7 @@ namespace Skender.Stock.Indicators; // ATR TRAILING STOP (STREAM HUB) -#region hub interface +#region hub interface and initializer public interface IAtrStopHub { @@ -10,10 +10,21 @@ public interface IAtrStopHub double Multiplier { get; } EndType EndType { get; } } + +public static partial class AtrStop +{ + public static AtrStopHub ToAtrStop( + this IQuoteProvider quoteProvider, + int lookbackPeriods = 21, + double multiplier = 3, + EndType endType = EndType.Close) + where TIn : IQuote + => new(quoteProvider, lookbackPeriods, multiplier, endType); +} #endregion -public class AtrStopHub : QuoteObserver, - IResultHub, IAtrStopHub +public class AtrStopHub + : StreamHub, IAtrStopHub where TIn : IQuote { #region constructors @@ -50,67 +61,26 @@ internal AtrStopHub( public override string ToString() => hubName; - // overridden to handle non-standard arrival scenarios - public override void OnNext((Act, TIn, int?) value) - { - (Act act, TIn item, int? index) = value; - - // add next value - if (act is Act.AddNew) - { - Add(act, item, index); - return; - } - - // find last synchronized band position (before deviance) - int lastSyncIndex = Cache.FindLastIndex( - x => x.Timestamp < item.Timestamp - && x.AtrStop == (IsBullish ? x.SellStop : x.BuyStop)); - - // rebuild from last know reversal point - if (lastSyncIndex > LookbackPeriods) - { - AtrStopResult lastSyncPoint = Cache[lastSyncIndex]; - - // reset prevailing direction and bands - IsBullish = lastSyncPoint.AtrStop == lastSyncPoint.SellStop; - UpperBand = (double?)lastSyncPoint.BuyStop ?? default; - LowerBand = (double?)lastSyncPoint.SellStop ?? default; - - // rebuild cache AFTER last sync point - RebuildCache(lastSyncIndex + 1, lastSyncIndex + 1); - } - - // full rebuild if no prior reversal - else - { - IsBullish = default; - UpperBand = default; - LowerBand = default; - RebuildCache(); - } - } - - internal override void Add(Act act, TIn newIn, int? index) + protected override (AtrStopResult result, int index) + ToIndicator(TIn item, int? indexHint) { - // reminder: should only processes "new" instructions + // reminder: should only process "new" instructions - int i = index ?? Provider.GetIndex(newIn, false); + int i = indexHint ?? ProviderCache.GetIndex(item, true); // handle warmup periods if (i < LookbackPeriods) { - Motify(act, new(newIn.Timestamp), null); - return; + return (new AtrStopResult(item.Timestamp), i); } - QuoteD newQ = newIn.ToQuoteD(); - QuoteD prevQ = Provider.Results[i - 1].ToQuoteD(); + QuoteD newQ = item.ToQuoteD(); + double prevClose = (double)ProviderCache[i - 1].Close; // initialize direction on first evaluation if (i == LookbackPeriods) { - IsBullish = newQ.Close >= prevQ.Close; + IsBullish = newQ.Close >= prevClose; } // calculate ATR @@ -122,7 +92,7 @@ internal override void Add(Act act, TIn newIn, int? index) LookbackPeriods, newQ.High, newQ.Low, - prevQ.Close, + prevClose, Cache[i - 1].Atr ?? double.NaN); } @@ -134,9 +104,9 @@ internal override void Add(Act act, TIn newIn, int? index) for (int p = i - LookbackPeriods + 1; p <= i; p++) { sumTr += Tr.Increment( - (double)Provider.Results[p].High, - (double)Provider.Results[p].Low, - (double)Provider.Results[p - 1].Close); + (double)ProviderCache[p].High, + (double)ProviderCache[p].Low, + (double)ProviderCache[p - 1].Close); } atr = sumTr / LookbackPeriods; @@ -161,13 +131,13 @@ internal override void Add(Act act, TIn newIn, int? index) } // new upper band: can only go down, or reverse - if (upperEval < UpperBand || prevQ.Close > UpperBand) + if (upperEval < UpperBand || prevClose > UpperBand) { UpperBand = upperEval; } // new lower band: can only go up, or reverse - if (lowerEval > LowerBand || prevQ.Close < LowerBand) + if (lowerEval > LowerBand || prevClose < LowerBand) { LowerBand = lowerEval; } @@ -181,10 +151,10 @@ internal override void Add(Act act, TIn newIn, int? index) { IsBullish = false; - r = new( + r = new AtrStopResult( Timestamp: newQ.Timestamp, - AtrStop: (decimal)UpperBand, - BuyStop: (decimal)UpperBand, + AtrStop: UpperBand, + BuyStop: UpperBand, SellStop: null, Atr: atr); } @@ -194,15 +164,42 @@ internal override void Add(Act act, TIn newIn, int? index) { IsBullish = true; - r = new( + r = new AtrStopResult( Timestamp: newQ.Timestamp, - AtrStop: (decimal)LowerBand, + AtrStop: LowerBand, BuyStop: null, - SellStop: (decimal)LowerBand, + SellStop: LowerBand, Atr: atr); } - // save and send - Motify(act, r, null); + return (r, i); + } + + /// + /// Restore prior ATR Stop + /// + /// + protected override void RollbackState(DateTime timestamp) + { + int i = ProviderCache.GetIndexGte(timestamp); + + // restore prior stop point + if (i > LookbackPeriods) + { + AtrStopResult resetStop = Cache[i - 1]; + + // prevailing direction and bands + IsBullish = resetStop.AtrStop >= resetStop.SellStop; + UpperBand = resetStop.BuyStop ?? default; + LowerBand = resetStop.SellStop ?? default; + } + + // or reset if no prior stop found + else + { + IsBullish = default; + UpperBand = default; + LowerBand = default; + } } } diff --git a/src/a-d/AtrStop/AtrStop.Utilities.cs b/src/a-d/AtrStop/AtrStop.Utilities.cs index a96b27fd5..be667403f 100644 --- a/src/a-d/AtrStop/AtrStop.Utilities.cs +++ b/src/a-d/AtrStop/AtrStop.Utilities.cs @@ -5,9 +5,9 @@ namespace Skender.Stock.Indicators; public static partial class AtrStop { // CONDENSE (REMOVE null results) - /// + /// public static IReadOnlyList Condense( - this IEnumerable results) + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -18,9 +18,9 @@ public static IReadOnlyList Condense( } // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int removePeriods = results .ToList() diff --git a/src/a-d/Awesome/Awesome.Api.cs b/src/a-d/Awesome/Awesome.Api.cs deleted file mode 100644 index ccf389b14..000000000 --- a/src/a-d/Awesome/Awesome.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// AWESOME OSCILLATOR (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetAwesome( - this IEnumerable source, - int fastPeriods = 5, - int slowPeriods = 34) - where T : IReusable - => source - .ToSortedList(CandlePart.HL2) - .CalcAwesome(fastPeriods, slowPeriods); -} diff --git a/src/a-d/Awesome/Awesome.Common.cs b/src/a-d/Awesome/Awesome.Common.cs deleted file mode 100644 index a8c194923..000000000 --- a/src/a-d/Awesome/Awesome.Common.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Skender.Stock.Indicators; - -// AWESOME OSCILLATOR (COMMON) - -public static class Awesome -{ - // parameter validation - internal static void Validate( - int fastPeriods, - int slowPeriods) - { - // check parameter arguments - if (fastPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, - "Fast periods must be greater than 0 for Awesome Oscillator."); - } - - if (slowPeriods <= fastPeriods) - { - throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, - "Slow periods must be larger than Fast Periods for Awesome Oscillator."); - } - } - -} diff --git a/src/a-d/Awesome/Awesome.Models.cs b/src/a-d/Awesome/Awesome.Models.cs index 7101206ae..007a14e6c 100644 --- a/src/a-d/Awesome/Awesome.Models.cs +++ b/src/a-d/Awesome/Awesome.Models.cs @@ -5,7 +5,7 @@ public record AwesomeResult DateTime Timestamp, double? Oscillator, double? Normalized -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Oscillator.Null2NaN(); + public double Value => Oscillator.Null2NaN(); } diff --git a/src/a-d/Awesome/Awesome.StaticSeries.cs b/src/a-d/Awesome/Awesome.StaticSeries.cs index a2ca091fc..b8f38f542 100644 --- a/src/a-d/Awesome/Awesome.StaticSeries.cs +++ b/src/a-d/Awesome/Awesome.StaticSeries.cs @@ -2,26 +2,30 @@ namespace Skender.Stock.Indicators; // AWESOME OSCILLATOR (SERIES) -public static partial class Indicator +public static partial class Awesome { - private static List CalcAwesome( - this List source, - int fastPeriods, - int slowPeriods) + public static IReadOnlyList ToAwesome( + this IReadOnlyList source, + int fastPeriods = 5, + int slowPeriods = 34) where T : IReusable { // check parameter arguments - Awesome.Validate(fastPeriods, slowPeriods); + Validate(fastPeriods, slowPeriods); + + // prefer HL2 when IQuote + IReadOnlyList values + = source.ToPreferredList(CandlePart.HL2); // initialize - int length = source.Count; + int length = values.Count; List results = new(length); double[] pr = new double[length]; // roll through source values for (int i = 0; i < length; i++) { - IReusable s = source[i]; + IReusable s = values[i]; pr[i] = s.Value; double? oscillator = null; diff --git a/src/a-d/Awesome/Awesome.Utilities.cs b/src/a-d/Awesome/Awesome.Utilities.cs index 63333ebeb..95396cb3d 100644 --- a/src/a-d/Awesome/Awesome.Utilities.cs +++ b/src/a-d/Awesome/Awesome.Utilities.cs @@ -1,16 +1,23 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +public static partial class Awesome { - // remove recommended periods - /// - public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int fastPeriods, + int slowPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Oscillator != null); + // check parameter arguments + if (fastPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, + "Fast periods must be greater than 0 for Awesome Oscillator."); + } - return results.Remove(removePeriods); + if (slowPeriods <= fastPeriods) + { + throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, + "Slow periods must be larger than Fast Periods for Awesome Oscillator."); + } } } diff --git a/src/a-d/Beta/Beta.Api.cs b/src/a-d/Beta/Beta.Api.cs deleted file mode 100644 index 989c47cac..000000000 --- a/src/a-d/Beta/Beta.Api.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Skender.Stock.Indicators; - -// BETA COEFFICIENT (API) -public static partial class Indicator -{ - // SERIES, from CHAINS (both inputs reusable) - public static IReadOnlyList GetBeta( - this IEnumerable evalSource, - IEnumerable mrktSource, - int lookbackPeriods, - BetaType type = BetaType.Standard) - where T : IReusable - { - List listEval - = evalSource.ToSortedList(); - - List listMrkt - = mrktSource.ToSortedList(); - - return CalcBeta(listEval, listMrkt, lookbackPeriods, type); - } -} diff --git a/src/a-d/Beta/Beta.Common.cs b/src/a-d/Beta/Beta.Common.cs deleted file mode 100644 index 384b86fc5..000000000 --- a/src/a-d/Beta/Beta.Common.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Skender.Stock.Indicators; - -// BETA COEFFICIENT (COMMON) - -public static class Beta -{ - // parameter validation - internal static void Validate( - List sourceEval, - List sourceMrkt, - int lookbackPeriods) - where T : ISeries - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Beta."); - } - - // check quotes - if (sourceEval.Count != sourceMrkt.Count) - { - throw new InvalidQuotesException( - nameof(sourceEval), - "Eval quotes should have the same number of Market quotes for Beta."); - } - } - -} diff --git a/src/a-d/Beta/Beta.Models.cs b/src/a-d/Beta/Beta.Models.cs index cd020fa54..49339ebf9 100644 --- a/src/a-d/Beta/Beta.Models.cs +++ b/src/a-d/Beta/Beta.Models.cs @@ -9,9 +9,9 @@ public record BetaResult( double? Convexity = null, double? ReturnsEval = null, double? ReturnsMrkt = null -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Beta.Null2NaN(); + public double Value => Beta.Null2NaN(); } public enum BetaType diff --git a/src/a-d/Beta/Beta.StaticSeries.cs b/src/a-d/Beta/Beta.StaticSeries.cs index f584c9f83..d8e3280a6 100644 --- a/src/a-d/Beta/Beta.StaticSeries.cs +++ b/src/a-d/Beta/Beta.StaticSeries.cs @@ -2,18 +2,19 @@ namespace Skender.Stock.Indicators; // BETA COEFFICIENT (SERIES) -public static partial class Indicator +public static partial class Beta { - // NOTE: sequence swapped from API - private static List CalcBeta( - List sourceEval, - List sourceMrkt, + public static IReadOnlyList ToBeta( + this IReadOnlyList sourceEval, + IReadOnlyList sourceMrkt, int lookbackPeriods, BetaType type = BetaType.Standard) where T : IReusable { // check parameter arguments - Beta.Validate(sourceEval, sourceMrkt, lookbackPeriods); + ArgumentNullException.ThrowIfNull(sourceEval); + ArgumentNullException.ThrowIfNull(sourceMrkt); + Validate(sourceEval, sourceMrkt, lookbackPeriods); // initialize int length = sourceEval.Count; @@ -146,7 +147,7 @@ private static List CalcBeta( } // calculate correlation, covariance, and variance - CorrResult c = PeriodCorrelation( + CorrResult c = Correlation.PeriodCorrelation( default, [.. dataA], [.. dataB]); diff --git a/src/a-d/Beta/Beta.Utilities.cs b/src/a-d/Beta/Beta.Utilities.cs index ac1a1434c..45a096dcf 100644 --- a/src/a-d/Beta/Beta.Utilities.cs +++ b/src/a-d/Beta/Beta.Utilities.cs @@ -1,16 +1,27 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +public static partial class Beta { - // remove recommended periods - /// - public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + IReadOnlyList sourceEval, + IReadOnlyList sourceMrkt, + int lookbackPeriods) + where T : ISeries { - int removePeriods = results - .ToList() - .FindIndex(x => x.Beta != null); + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Beta."); + } - return results.Remove(removePeriods); + // check quotes + if (sourceEval.Count != sourceMrkt.Count) + { + throw new InvalidQuotesException( + nameof(sourceEval), + "Eval quotes should have the same number of Market quotes for Beta."); + } } } diff --git a/src/a-d/BollingerBands/BollingerBands.Api.cs b/src/a-d/BollingerBands/BollingerBands.Api.cs deleted file mode 100644 index 28fa8dda8..000000000 --- a/src/a-d/BollingerBands/BollingerBands.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// BOLLINGER BANDS (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetBollingerBands( - this IEnumerable results, - int lookbackPeriods = 20, - double standardDeviations = 2) - where T : IReusable - => results - .ToSortedList() - .CalcBollingerBands(lookbackPeriods, standardDeviations); -} diff --git a/src/a-d/BollingerBands/BollingerBands.Common.cs b/src/a-d/BollingerBands/BollingerBands.Common.cs deleted file mode 100644 index a29ad0bf7..000000000 --- a/src/a-d/BollingerBands/BollingerBands.Common.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Skender.Stock.Indicators; - -// BOLLINGER BANDS (COMMON) - -public static class BollingerBands -{ - // parameter validation - internal static void Validate( - int lookbackPeriods, - double standardDeviations) - { - // check parameter arguments - if (lookbackPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 1 for Bollinger Bands."); - } - - if (standardDeviations <= 0) - { - throw new ArgumentOutOfRangeException(nameof(standardDeviations), standardDeviations, - "Standard Deviations must be greater than 0 for Bollinger Bands."); - } - } - -} diff --git a/src/a-d/BollingerBands/BollingerBands.Models.cs b/src/a-d/BollingerBands/BollingerBands.Models.cs index 49b42b90c..a3c6f1d0a 100644 --- a/src/a-d/BollingerBands/BollingerBands.Models.cs +++ b/src/a-d/BollingerBands/BollingerBands.Models.cs @@ -9,7 +9,7 @@ public record BollingerBandsResult double? PercentB = null, double? ZScore = null, double? Width = null -) : Reusable(Timestamp) +) : IReusable { - public override double Value => PercentB.Null2NaN(); + public double Value => PercentB.Null2NaN(); } diff --git a/src/a-d/BollingerBands/BollingerBands.StaticSeries.cs b/src/a-d/BollingerBands/BollingerBands.StaticSeries.cs index cfdf3dc9d..8945cb590 100644 --- a/src/a-d/BollingerBands/BollingerBands.StaticSeries.cs +++ b/src/a-d/BollingerBands/BollingerBands.StaticSeries.cs @@ -2,16 +2,17 @@ namespace Skender.Stock.Indicators; // BOLLINGER BANDS (SERIES) -public static partial class Indicator +public static partial class BollingerBands { - private static List CalcBollingerBands( - this List source, - int lookbackPeriods, - double standardDeviations) + public static IReadOnlyList ToBollingerBands( + this IReadOnlyList source, + int lookbackPeriods = 20, + double standardDeviations = 2) where T : IReusable { // check parameter arguments - BollingerBands.Validate(lookbackPeriods, standardDeviations); + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods, standardDeviations); // initialize int length = source.Count; diff --git a/src/a-d/BollingerBands/BollingerBands.Utilities.cs b/src/a-d/BollingerBands/BollingerBands.Utilities.cs index 167fee2b7..a90db9fe1 100644 --- a/src/a-d/BollingerBands/BollingerBands.Utilities.cs +++ b/src/a-d/BollingerBands/BollingerBands.Utilities.cs @@ -1,11 +1,11 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +public static partial class BollingerBands { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -13,4 +13,23 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + int lookbackPeriods, + double standardDeviations) + { + // check parameter arguments + if (lookbackPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 1 for Bollinger Bands."); + } + + if (standardDeviations <= 0) + { + throw new ArgumentOutOfRangeException(nameof(standardDeviations), standardDeviations, + "Standard Deviations must be greater than 0 for Bollinger Bands."); + } + } } diff --git a/src/a-d/Bop/Bop.Api.cs b/src/a-d/Bop/Bop.Api.cs deleted file mode 100644 index 0cc2e8413..000000000 --- a/src/a-d/Bop/Bop.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// BALANCE OF POWER (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetBop( - this IEnumerable quotes, - int smoothPeriods = 14) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcBop(smoothPeriods); -} diff --git a/src/a-d/Bop/Bop.Common.cs b/src/a-d/Bop/Bop.Common.cs deleted file mode 100644 index 67c58d608..000000000 --- a/src/a-d/Bop/Bop.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// BALANCE OF POWER (COMMON) - -public static class Bop -{ - // parameter validation - internal static void Validate( - int smoothPeriods) - { - // check parameter arguments - if (smoothPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(smoothPeriods), smoothPeriods, - "Smoothing periods must be greater than 0 for BOP."); - } - } - -} diff --git a/src/a-d/Bop/Bop.Models.cs b/src/a-d/Bop/Bop.Models.cs index 5283a18c2..11d564184 100644 --- a/src/a-d/Bop/Bop.Models.cs +++ b/src/a-d/Bop/Bop.Models.cs @@ -4,7 +4,7 @@ public record BopResult ( DateTime Timestamp, double? Bop -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Bop.Null2NaN(); + public double Value => Bop.Null2NaN(); } diff --git a/src/a-d/Bop/Bop.StaticSeries.cs b/src/a-d/Bop/Bop.StaticSeries.cs index 35d59bdb0..37a1c7b7c 100644 --- a/src/a-d/Bop/Bop.StaticSeries.cs +++ b/src/a-d/Bop/Bop.StaticSeries.cs @@ -2,14 +2,21 @@ namespace Skender.Stock.Indicators; // BALANCE OF POWER (SERIES) -public static partial class Indicator +public static partial class Bop { + public static IReadOnlyList ToBop( + this IReadOnlyList quotes, + int smoothPeriods = 14) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcBop(smoothPeriods); + private static List CalcBop( - this List source, + this IReadOnlyList source, int smoothPeriods) { // check parameter arguments - Bop.Validate(smoothPeriods); + Validate(smoothPeriods); // initialize int length = source.Count; diff --git a/src/a-d/Bop/Bop.Utilities.cs b/src/a-d/Bop/Bop.Utilities.cs index 08a059ba9..488440947 100644 --- a/src/a-d/Bop/Bop.Utilities.cs +++ b/src/a-d/Bop/Bop.Utilities.cs @@ -1,16 +1,18 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// BALANCE OF POWER (UTILITIES) + +public static partial class Bop { - // remove recommended periods - /// - public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int smoothPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Bop != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (smoothPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(smoothPeriods), smoothPeriods, + "Smoothing periods must be greater than 0 for BOP."); + } } } diff --git a/src/a-d/Cci/Cci.Api.cs b/src/a-d/Cci/Cci.Api.cs deleted file mode 100644 index 1d262caac..000000000 --- a/src/a-d/Cci/Cci.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// COMMODITY CHANNEL INDEX (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetCci( - this IEnumerable quotes, - int lookbackPeriods = 20) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcCci(lookbackPeriods); -} diff --git a/src/a-d/Cci/Cci.Common.cs b/src/a-d/Cci/Cci.Common.cs deleted file mode 100644 index 1f8501688..000000000 --- a/src/a-d/Cci/Cci.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// COMMODITY CHANNEL INDEX (COMMON) - -public static class Cci -{ - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Commodity Channel Index."); - } - } - -} diff --git a/src/a-d/Cci/Cci.Models.cs b/src/a-d/Cci/Cci.Models.cs index 3e3d2d046..1b448e730 100644 --- a/src/a-d/Cci/Cci.Models.cs +++ b/src/a-d/Cci/Cci.Models.cs @@ -4,7 +4,7 @@ public record CciResult ( DateTime Timestamp, double? Cci -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Cci.Null2NaN(); + public double Value => Cci.Null2NaN(); } diff --git a/src/a-d/Cci/Cci.StaticSeries.cs b/src/a-d/Cci/Cci.StaticSeries.cs index 5f9ff1479..e854ce386 100644 --- a/src/a-d/Cci/Cci.StaticSeries.cs +++ b/src/a-d/Cci/Cci.StaticSeries.cs @@ -2,14 +2,21 @@ namespace Skender.Stock.Indicators; // COMMODITY CHANNEL INDEX (SERIES) -public static partial class Indicator +public static partial class Cci { + public static IReadOnlyList ToCci( + this IReadOnlyList quotes, + int lookbackPeriods = 20) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcCci(lookbackPeriods); + private static List CalcCci( - this List source, + this IReadOnlyList source, int lookbackPeriods) { // check parameter arguments - Cci.Validate(lookbackPeriods); + Validate(lookbackPeriods); // initialize int length = source.Count; diff --git a/src/a-d/Cci/Cci.Utilities.cs b/src/a-d/Cci/Cci.Utilities.cs index e71456947..34bce451c 100644 --- a/src/a-d/Cci/Cci.Utilities.cs +++ b/src/a-d/Cci/Cci.Utilities.cs @@ -1,16 +1,19 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// COMMODITY CHANNEL INDEX (UTILITIES) + +public static partial class Cci { - // remove recommended periods - /// - public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Cci != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Commodity Channel Index."); + } } + } diff --git a/src/a-d/ChaikinOsc/ChaikinOsc.Api.cs b/src/a-d/ChaikinOsc/ChaikinOsc.Api.cs deleted file mode 100644 index 8871af1ed..000000000 --- a/src/a-d/ChaikinOsc/ChaikinOsc.Api.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CHAIKIN OSCILLATOR -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetChaikinOsc( - this IEnumerable quotes, - int fastPeriods = 3, - int slowPeriods = 10) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcChaikinOsc(fastPeriods, slowPeriods); -} diff --git a/src/a-d/ChaikinOsc/ChaikinOsc.Common.cs b/src/a-d/ChaikinOsc/ChaikinOsc.Common.cs deleted file mode 100644 index 0b328680c..000000000 --- a/src/a-d/ChaikinOsc/ChaikinOsc.Common.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CHAIKIN OSCILLATOR (COMMON) - -public static class ChaikinOsc -{ - // parameter validation - internal static void Validate( - int fastPeriods, - int slowPeriods) - { - // check parameter arguments - if (fastPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(fastPeriods), fastPeriods, - "Fast lookback periods must be greater than 0 for Chaikin Oscillator."); - } - - if (slowPeriods <= fastPeriods) - { - throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, - "Slow lookback periods must be greater than Fast lookback period for Chaikin Oscillator."); - } - } - -} diff --git a/src/a-d/ChaikinOsc/ChaikinOsc.Models.cs b/src/a-d/ChaikinOsc/ChaikinOsc.Models.cs index fa5adf0db..62a508717 100644 --- a/src/a-d/ChaikinOsc/ChaikinOsc.Models.cs +++ b/src/a-d/ChaikinOsc/ChaikinOsc.Models.cs @@ -7,7 +7,7 @@ public record ChaikinOscResult double? MoneyFlowVolume, double? Adl, double? Oscillator -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Oscillator.Null2NaN(); + public double Value => Oscillator.Null2NaN(); } diff --git a/src/a-d/ChaikinOsc/ChaikinOsc.StaticSeries.cs b/src/a-d/ChaikinOsc/ChaikinOsc.StaticSeries.cs index 00b2ea54a..f3d1b3b6c 100644 --- a/src/a-d/ChaikinOsc/ChaikinOsc.StaticSeries.cs +++ b/src/a-d/ChaikinOsc/ChaikinOsc.StaticSeries.cs @@ -2,27 +2,28 @@ namespace Skender.Stock.Indicators; // CHAIKIN OSCILLATOR (SERIES) -public static partial class Indicator +public static partial class ChaikinOsc { - private static List CalcChaikinOsc( - this List source, - int fastPeriods, - int slowPeriods) + public static IReadOnlyList ToChaikinOsc( + this IReadOnlyList quotes, + int fastPeriods = 3, + int slowPeriods = 10) where TQuote : IQuote { // check parameter arguments - ChaikinOsc.Validate(fastPeriods, slowPeriods); + ArgumentNullException.ThrowIfNull(quotes); + Validate(fastPeriods, slowPeriods); // initialize - int length = source.Count; + int length = quotes.Count; List results = new(length); // money flow - List adlResults = source.CalcAdl(); + IReadOnlyList adlResults = quotes.ToAdl(); // fast/slow EMA of ADL - List adlEmaSlow = adlResults.CalcEma(slowPeriods); - List adlEmaFast = adlResults.CalcEma(fastPeriods); + IReadOnlyList adlEmaSlow = adlResults.ToEma(slowPeriods); + IReadOnlyList adlEmaFast = adlResults.ToEma(fastPeriods); // roll through source values for (int i = 0; i < length; i++) diff --git a/src/a-d/ChaikinOsc/ChaikinOsc.Utilities.cs b/src/a-d/ChaikinOsc/ChaikinOsc.Utilities.cs index 6f11abc34..3e21a64c0 100644 --- a/src/a-d/ChaikinOsc/ChaikinOsc.Utilities.cs +++ b/src/a-d/ChaikinOsc/ChaikinOsc.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// CHAIKIN OSCILLATOR (UTILITIES) + +public static partial class ChaikinOsc { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int s = results .ToList() @@ -13,4 +15,23 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(s + 100); } + + // parameter validation + internal static void Validate( + int fastPeriods, + int slowPeriods) + { + // check parameter arguments + if (fastPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(fastPeriods), fastPeriods, + "Fast lookback periods must be greater than 0 for Chaikin Oscillator."); + } + + if (slowPeriods <= fastPeriods) + { + throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, + "Slow lookback periods must be greater than Fast lookback period for Chaikin Oscillator."); + } + } } diff --git a/src/a-d/Chandelier/Chandelier.Api.cs b/src/a-d/Chandelier/Chandelier.Api.cs deleted file mode 100644 index 60f36c875..000000000 --- a/src/a-d/Chandelier/Chandelier.Api.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CHANDELIER EXIT (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetChandelier( - this IEnumerable quotes, - int lookbackPeriods = 22, - double multiplier = 3, - ChandelierType type = ChandelierType.Long) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcChandelier(lookbackPeriods, multiplier, type); -} diff --git a/src/a-d/Chandelier/Chandelier.Common.cs b/src/a-d/Chandelier/Chandelier.Common.cs deleted file mode 100644 index 4f5d8020f..000000000 --- a/src/a-d/Chandelier/Chandelier.Common.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CHANDELIER EXIT (COMMON) - -public static class Chandelier -{ - // parameter validation - internal static void Validate( - int lookbackPeriods, - double multiplier) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Chandelier Exit."); - } - - if (multiplier <= 0) - { - throw new ArgumentOutOfRangeException(nameof(multiplier), multiplier, - "Multiplier must be greater than 0 for Chandelier Exit."); - } - } - -} diff --git a/src/a-d/Chandelier/Chandelier.Models.cs b/src/a-d/Chandelier/Chandelier.Models.cs index 7d90ea11f..b6bd6af2b 100644 --- a/src/a-d/Chandelier/Chandelier.Models.cs +++ b/src/a-d/Chandelier/Chandelier.Models.cs @@ -4,9 +4,9 @@ public record ChandelierResult ( DateTime Timestamp, double? ChandelierExit -) : Reusable(Timestamp) +) : IReusable { - public override double Value => ChandelierExit.Null2NaN(); + public double Value => ChandelierExit.Null2NaN(); } public enum ChandelierType diff --git a/src/a-d/Chandelier/Chandelier.StaticSeries.cs b/src/a-d/Chandelier/Chandelier.StaticSeries.cs index cfe7f18d3..8f13ebeb8 100644 --- a/src/a-d/Chandelier/Chandelier.StaticSeries.cs +++ b/src/a-d/Chandelier/Chandelier.StaticSeries.cs @@ -2,16 +2,25 @@ namespace Skender.Stock.Indicators; // CHANDELIER EXIT (SERIES) -public static partial class Indicator +public static partial class Chandelier { + public static IReadOnlyList ToChandelier( + this IReadOnlyList quotes, + int lookbackPeriods = 22, + double multiplier = 3, + ChandelierType type = ChandelierType.Long) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcChandelier(lookbackPeriods, multiplier, type); + private static List CalcChandelier( - this List source, + this IReadOnlyList source, int lookbackPeriods, double multiplier, ChandelierType type) { // check parameter arguments - Chandelier.Validate(lookbackPeriods, multiplier); + Validate(lookbackPeriods, multiplier); // initialize int length = source.Count; diff --git a/src/a-d/Chandelier/Chandelier.Utilities.cs b/src/a-d/Chandelier/Chandelier.Utilities.cs index a7ff84bcb..f61fe73ec 100644 --- a/src/a-d/Chandelier/Chandelier.Utilities.cs +++ b/src/a-d/Chandelier/Chandelier.Utilities.cs @@ -1,16 +1,25 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// CHANDELIER EXIT (UTILITIES) + +public static partial class Chandelier { - // remove recommended periods - /// - public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods, + double multiplier) { - int removePeriods = results - .ToList() - .FindIndex(x => x.ChandelierExit != null); + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Chandelier Exit."); + } - return results.Remove(removePeriods); + if (multiplier <= 0) + { + throw new ArgumentOutOfRangeException(nameof(multiplier), multiplier, + "Multiplier must be greater than 0 for Chandelier Exit."); + } } } diff --git a/src/a-d/Chop/Chop.Api.cs b/src/a-d/Chop/Chop.Api.cs deleted file mode 100644 index e834dfec5..000000000 --- a/src/a-d/Chop/Chop.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CHOPPINESS INDEX (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetChop( - this IEnumerable quotes, - int lookbackPeriods = 14) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcChop(lookbackPeriods); -} diff --git a/src/a-d/Chop/Chop.Common.cs b/src/a-d/Chop/Chop.Common.cs deleted file mode 100644 index 3bf4cc474..000000000 --- a/src/a-d/Chop/Chop.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CHOPPINESS INDEX (COMMON) - -public static class Chop -{ - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 1 for CHOP."); - } - } - -} diff --git a/src/a-d/Chop/Chop.Models.cs b/src/a-d/Chop/Chop.Models.cs index 51ef88f86..d5a3cbc66 100644 --- a/src/a-d/Chop/Chop.Models.cs +++ b/src/a-d/Chop/Chop.Models.cs @@ -4,7 +4,7 @@ public record ChopResult ( DateTime Timestamp, double? Chop -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Chop.Null2NaN(); + public double Value => Chop.Null2NaN(); } diff --git a/src/a-d/Chop/Chop.StaticSeries.cs b/src/a-d/Chop/Chop.StaticSeries.cs index a68a278f8..2237f86c1 100644 --- a/src/a-d/Chop/Chop.StaticSeries.cs +++ b/src/a-d/Chop/Chop.StaticSeries.cs @@ -2,14 +2,21 @@ namespace Skender.Stock.Indicators; // CHOPPINESS INDEX (SERIES) -public static partial class Indicator +public static partial class Chop { + public static IReadOnlyList ToChop( + this IReadOnlyList quotes, + int lookbackPeriods = 14) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcChop(lookbackPeriods); + private static List CalcChop( - this List source, + this IReadOnlyList source, int lookbackPeriods) { // check parameter arguments - Chop.Validate(lookbackPeriods); + Validate(lookbackPeriods); // initialize int length = source.Count; diff --git a/src/a-d/Chop/Chop.Utilities.cs b/src/a-d/Chop/Chop.Utilities.cs index 7b93a5d31..08bdb70dc 100644 --- a/src/a-d/Chop/Chop.Utilities.cs +++ b/src/a-d/Chop/Chop.Utilities.cs @@ -1,16 +1,18 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// CHOPPINESS INDEX (UTILITIES) + +public static partial class Chop { - // remove recommended periods - /// - public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Chop != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (lookbackPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 1 for CHOP."); + } } } diff --git a/src/a-d/Cmf/Cmf.Api.cs b/src/a-d/Cmf/Cmf.Api.cs deleted file mode 100644 index 96f8c5961..000000000 --- a/src/a-d/Cmf/Cmf.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CHAIKIN MONEY FLOW (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetCmf( - this IEnumerable quotes, - int lookbackPeriods = 20) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcCmf(lookbackPeriods); -} diff --git a/src/a-d/Cmf/Cmf.Common.cs b/src/a-d/Cmf/Cmf.Common.cs deleted file mode 100644 index 9731b7bd6..000000000 --- a/src/a-d/Cmf/Cmf.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CHAIKIN MONEY FLOW (COMMON) - -public static class Cmf -{ - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Chaikin Money Flow."); - } - } - -} diff --git a/src/a-d/Cmf/Cmf.Models.cs b/src/a-d/Cmf/Cmf.Models.cs index 35e5700db..f8de46088 100644 --- a/src/a-d/Cmf/Cmf.Models.cs +++ b/src/a-d/Cmf/Cmf.Models.cs @@ -6,7 +6,7 @@ public record CmfResult double? MoneyFlowMultiplier, double? MoneyFlowVolume, double? Cmf -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Cmf.Null2NaN(); + public double Value => Cmf.Null2NaN(); } diff --git a/src/a-d/Cmf/Cmf.StaticSeries.cs b/src/a-d/Cmf/Cmf.StaticSeries.cs index 6da645c0d..063d60b5b 100644 --- a/src/a-d/Cmf/Cmf.StaticSeries.cs +++ b/src/a-d/Cmf/Cmf.StaticSeries.cs @@ -2,10 +2,17 @@ namespace Skender.Stock.Indicators; // CHAIKIN MONEY FLOW (SERIES) -public static partial class Indicator +public static partial class Cmf { + public static IReadOnlyList ToCmf( + this IReadOnlyList quotes, + int lookbackPeriods = 20) + where TQuote : IQuote => quotes + .ToSortedList() + .CalcCmf(lookbackPeriods); + private static List CalcCmf( - this List source, + this IReadOnlyList source, int lookbackPeriods) where TQuote : IQuote { @@ -14,12 +21,12 @@ double[] volume = source.Select(v => (double)v.Volume).ToArray(); // check parameter arguments - Cmf.Validate(lookbackPeriods); + Validate(lookbackPeriods); // initialize int length = volume.Length; List results = new(length); - List adlResults = source.CalcAdl(); + IReadOnlyList adlResults = source.ToAdl(); // roll through source values for (int i = 0; i < length; i++) diff --git a/src/a-d/Cmf/Cmf.Utilities.cs b/src/a-d/Cmf/Cmf.Utilities.cs index 02954b3d6..360263a04 100644 --- a/src/a-d/Cmf/Cmf.Utilities.cs +++ b/src/a-d/Cmf/Cmf.Utilities.cs @@ -1,16 +1,19 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// CHAIKIN MONEY FLOW (UTILITIES) + +public static partial class Cmf { - // remove recommended periods - /// - public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Cmf != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Chaikin Money Flow."); + } } + } diff --git a/src/a-d/Cmo/Cmo.Api.cs b/src/a-d/Cmo/Cmo.Api.cs deleted file mode 100644 index cd9e4c85b..000000000 --- a/src/a-d/Cmo/Cmo.Api.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CHANDE MOMENTUM OSCILLATOR (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetCmo( - this IEnumerable source, - int lookbackPeriods) - where T : IReusable - => source - .ToSortedList() - .CalcCmo(lookbackPeriods); -} diff --git a/src/a-d/Cmo/Cmo.Common.cs b/src/a-d/Cmo/Cmo.Common.cs deleted file mode 100644 index a54d46479..000000000 --- a/src/a-d/Cmo/Cmo.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CHANDE MOMENTUM OSCILLATOR (COMMON) - -public static class Cmo -{ - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for CMO."); - } - } - -} diff --git a/src/a-d/Cmo/Cmo.Models.cs b/src/a-d/Cmo/Cmo.Models.cs index 87e2ce136..708bbe262 100644 --- a/src/a-d/Cmo/Cmo.Models.cs +++ b/src/a-d/Cmo/Cmo.Models.cs @@ -4,7 +4,7 @@ public record CmoResult ( DateTime Timestamp, double? Cmo = null -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Cmo.Null2NaN(); + public double Value => Cmo.Null2NaN(); } diff --git a/src/a-d/Cmo/Cmo.StaticSeries.cs b/src/a-d/Cmo/Cmo.StaticSeries.cs index 3ec0801d5..8cd1d448f 100644 --- a/src/a-d/Cmo/Cmo.StaticSeries.cs +++ b/src/a-d/Cmo/Cmo.StaticSeries.cs @@ -2,15 +2,16 @@ namespace Skender.Stock.Indicators; // CHANDE MOMENTUM OSCILLATOR (SERIES) -public static partial class Indicator +public static partial class Cmo { - private static List CalcCmo( - this List source, + public static IReadOnlyList ToCmo( + this IReadOnlyList source, int lookbackPeriods) where T : IReusable { // check parameter arguments - Cmo.Validate(lookbackPeriods); + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); // initialize int length = source.Count; @@ -29,7 +30,7 @@ private static List CalcCmo( results.Add(new(source[0].Timestamp)); ticks.Add((null, double.NaN)); - // roll through remaining prices + // roll through remaining values for (int i = 1; i < length; i++) { T s = source[i]; @@ -38,10 +39,9 @@ private static List CalcCmo( // determine tick direction and size (bool? isUp, double value) tick = (null, Math.Abs(s.Value - prevValue)); - tick.isUp = double.IsNaN(tick.value) ? null - : (s.Value > prevValue ? true - : (s.Value < prevValue ? false - : null)); + tick.isUp = double.IsNaN(tick.value) || s.Value == prevValue + ? null + : s.Value > prevValue; ticks.Add(tick); diff --git a/src/a-d/Cmo/Cmo.Utilities.cs b/src/a-d/Cmo/Cmo.Utilities.cs index 3c9b23628..5ed18a20a 100644 --- a/src/a-d/Cmo/Cmo.Utilities.cs +++ b/src/a-d/Cmo/Cmo.Utilities.cs @@ -1,16 +1,18 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// CHANDE MOMENTUM OSCILLATOR (UTILITIES) + +public static partial class Cmo { - // remove recommended periods - /// - public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Cmo != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for CMO."); + } } } diff --git a/src/a-d/ConnorsRsi/ConnorsRsi.Api.cs b/src/a-d/ConnorsRsi/ConnorsRsi.Api.cs deleted file mode 100644 index 5cb943377..000000000 --- a/src/a-d/ConnorsRsi/ConnorsRsi.Api.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CONNORS RSI (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetConnorsRsi( - this IEnumerable results, - int rsiPeriods = 3, - int streakPeriods = 2, - int rankPeriods = 100) - where T : IReusable - => results - .ToSortedList() - .CalcConnorsRsi(rsiPeriods, streakPeriods, rankPeriods); -} diff --git a/src/a-d/ConnorsRsi/ConnorsRsi.Common.cs b/src/a-d/ConnorsRsi/ConnorsRsi.Common.cs deleted file mode 100644 index 85da8081f..000000000 --- a/src/a-d/ConnorsRsi/ConnorsRsi.Common.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CONNORS RSI (COMMON) - -public static class ConnorsRsi -{ - // parameter validation - internal static void Validate( - int rsiPeriods, - int streakPeriods, - int rankPeriods) - { - // check parameter arguments - if (rsiPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(rsiPeriods), rsiPeriods, - "RSI period for Close price must be greater than 1 for ConnorsRsi."); - } - - if (streakPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(streakPeriods), streakPeriods, - "RSI period for Streak must be greater than 1 for ConnorsRsi."); - } - - if (rankPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(rankPeriods), rankPeriods, - "Percent Rank periods must be greater than 1 for ConnorsRsi."); - } - } - -} diff --git a/src/a-d/ConnorsRsi/ConnorsRsi.Models.cs b/src/a-d/ConnorsRsi/ConnorsRsi.Models.cs index dae912427..b7b9b31fc 100644 --- a/src/a-d/ConnorsRsi/ConnorsRsi.Models.cs +++ b/src/a-d/ConnorsRsi/ConnorsRsi.Models.cs @@ -8,7 +8,7 @@ public record ConnorsRsiResult double? RsiStreak = null, double? PercentRank = null, double? ConnorsRsi = null -) : Reusable(Timestamp) +) : IReusable { - public override double Value => ConnorsRsi.Null2NaN(); + public double Value => ConnorsRsi.Null2NaN(); } diff --git a/src/a-d/ConnorsRsi/ConnorsRsi.StaticSeries.cs b/src/a-d/ConnorsRsi/ConnorsRsi.StaticSeries.cs index 9fde8d627..9f224edca 100644 --- a/src/a-d/ConnorsRsi/ConnorsRsi.StaticSeries.cs +++ b/src/a-d/ConnorsRsi/ConnorsRsi.StaticSeries.cs @@ -2,17 +2,18 @@ namespace Skender.Stock.Indicators; // CONNORS RSI (SERIES) -public static partial class Indicator +public static partial class ConnorsRsi { - private static List CalcConnorsRsi( - this List source, - int rsiPeriods, - int streakPeriods, - int rankPeriods) + public static IReadOnlyList ToConnorsRsi( + this IReadOnlyList source, + int rsiPeriods = 3, + int streakPeriods = 2, + int rankPeriods = 100) where T : IReusable { // check parameter arguments - ConnorsRsi.Validate(rsiPeriods, streakPeriods, rankPeriods); + ArgumentNullException.ThrowIfNull(source); + Validate(rsiPeriods, streakPeriods, rankPeriods); // initialize int length = source.Count; @@ -28,7 +29,7 @@ IReadOnlyList streakInfo IReadOnlyList rsiStreak = streakInfo .Select(si => new QuotePart(si.Timestamp, si.Streak)) .ToList() - .CalcRsi(streakPeriods); + .ToRsi(streakPeriods); // compose final results for (int i = 0; i < length; i++) @@ -64,13 +65,13 @@ IReadOnlyList streakInfo // calculate baseline streak and rank private static List CalcStreak( - this List source, + this IReadOnlyList source, int rsiPeriods, int rankPeriods) where T : IReusable { // initialize - List rsiResults = CalcRsi(source, rsiPeriods); + IReadOnlyList rsiResults = source.ToRsi(rsiPeriods); int length = source.Count; List results = new(length); diff --git a/src/a-d/ConnorsRsi/ConnorsRsi.Utilities.cs b/src/a-d/ConnorsRsi/ConnorsRsi.Utilities.cs index b4d79d2d1..6fe17591f 100644 --- a/src/a-d/ConnorsRsi/ConnorsRsi.Utilities.cs +++ b/src/a-d/ConnorsRsi/ConnorsRsi.Utilities.cs @@ -1,16 +1,32 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// CONNORS RSI (UTILITIES) + +public static partial class ConnorsRsi { - // remove recommended periods - /// - public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int rsiPeriods, + int streakPeriods, + int rankPeriods) { - int n = results - .ToList() - .FindIndex(x => x.ConnorsRsi != null); + // check parameter arguments + if (rsiPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(rsiPeriods), rsiPeriods, + "RSI period for Close price must be greater than 1 for ConnorsRsi."); + } + + if (streakPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(streakPeriods), streakPeriods, + "RSI period for Streak must be greater than 1 for ConnorsRsi."); + } - return results.Remove(n); + if (rankPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(rankPeriods), rankPeriods, + "Percent Rank periods must be greater than 1 for ConnorsRsi."); + } } } diff --git a/src/a-d/Correlation/Correlation.Api.cs b/src/a-d/Correlation/Correlation.Api.cs deleted file mode 100644 index cfd99f390..000000000 --- a/src/a-d/Correlation/Correlation.Api.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CORRELATION COEFFICIENT (API) -public static partial class Indicator -{ - // SERIES, from CHAINS (both inputs reusable) - public static IReadOnlyList GetCorrelation( - this IEnumerable sourceA, - IEnumerable sourceB, - int lookbackPeriods) - where T : IReusable - => CalcCorrelation( - sourceA.ToSortedList(), - sourceB.ToSortedList(), - lookbackPeriods); -} diff --git a/src/a-d/Correlation/Correlation.Common.cs b/src/a-d/Correlation/Correlation.Common.cs deleted file mode 100644 index 043d2d37a..000000000 --- a/src/a-d/Correlation/Correlation.Common.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Skender.Stock.Indicators; - -// CORRELATION COEFFICIENT (COMMON) - -public static class Correlation -{ - // parameter validation - internal static void Validate( - List sourceA, - List sourceB, - int lookbackPeriods) - where T : ISeries - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Correlation."); - } - - // check quotes - if (sourceA.Count != sourceB.Count) - { - throw new InvalidQuotesException( - nameof(sourceB), - "B quotes should have at least as many records as A quotes for Correlation."); - } - } - -} diff --git a/src/a-d/Correlation/Correlation.Models.cs b/src/a-d/Correlation/Correlation.Models.cs index 559972c6c..33a1a4f51 100644 --- a/src/a-d/Correlation/Correlation.Models.cs +++ b/src/a-d/Correlation/Correlation.Models.cs @@ -8,7 +8,7 @@ public record CorrResult double? Covariance = null, double? Correlation = null, double? RSquared = null -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Correlation.Null2NaN(); + public double Value => Correlation.Null2NaN(); } diff --git a/src/a-d/Correlation/Correlation.StaticSeries.cs b/src/a-d/Correlation/Correlation.StaticSeries.cs index 5a3677c3a..e82a4df70 100644 --- a/src/a-d/Correlation/Correlation.StaticSeries.cs +++ b/src/a-d/Correlation/Correlation.StaticSeries.cs @@ -2,16 +2,18 @@ namespace Skender.Stock.Indicators; // CORRELATION COEFFICIENT (SERIES) -public static partial class Indicator +public static partial class Correlation { - private static List CalcCorrelation( - this List sourceA, - List sourceB, + public static IReadOnlyList ToCorrelation( + this IReadOnlyList sourceA, + IReadOnlyList sourceB, int lookbackPeriods) where T : IReusable { // check parameter arguments - Correlation.Validate(sourceA, sourceB, lookbackPeriods); + ArgumentNullException.ThrowIfNull(sourceA); + ArgumentNullException.ThrowIfNull(sourceB); + Validate(sourceA, sourceB, lookbackPeriods); // initialize int length = sourceA.Count; @@ -25,8 +27,10 @@ private static List CalcCorrelation( if (a.Timestamp != b.Timestamp) { - throw new InvalidQuotesException(nameof(sourceA), a.Timestamp, - "Timestamp sequence does not match. Correlation requires matching dates in provided histories."); + throw new InvalidQuotesException( + nameof(sourceA), a.Timestamp, + "Timestamp sequence does not match. " + + "Correlation requires matching dates in provided histories."); } CorrResult r; @@ -60,7 +64,7 @@ private static List CalcCorrelation( } // calculate correlation - private static CorrResult PeriodCorrelation( + internal static CorrResult PeriodCorrelation( DateTime timestamp, double[] dataA, double[] dataB) diff --git a/src/a-d/Correlation/Correlation.Utilities.cs b/src/a-d/Correlation/Correlation.Utilities.cs index a6be6eb7e..00df00337 100644 --- a/src/a-d/Correlation/Correlation.Utilities.cs +++ b/src/a-d/Correlation/Correlation.Utilities.cs @@ -1,16 +1,27 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +public static partial class Correlation { - // remove recommended periods - /// - public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + IReadOnlyList sourceA, + IReadOnlyList sourceB, + int lookbackPeriods) + where T : ISeries { - int removePeriods = results - .ToList() - .FindIndex(x => x.Correlation != null); + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Correlation."); + } - return results.Remove(removePeriods); + // check quotes + if (sourceA.Count != sourceB.Count) + { + throw new InvalidQuotesException( + nameof(sourceB), + "B quotes should have at least as many records as A quotes for Correlation."); + } } } diff --git a/src/a-d/Dema/Dema.Api.cs b/src/a-d/Dema/Dema.Api.cs deleted file mode 100644 index fdbfca629..000000000 --- a/src/a-d/Dema/Dema.Api.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Skender.Stock.Indicators; - -// DOUBLE EXPONENTIAL MOVING AVERAGE - DEMA (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetDema( - this IEnumerable results, - int lookbackPeriods) - where T : IReusable - => results - .ToSortedList() - .CalcDema(lookbackPeriods); -} diff --git a/src/a-d/Dema/Dema.Common.cs b/src/a-d/Dema/Dema.Common.cs deleted file mode 100644 index f948ca4e0..000000000 --- a/src/a-d/Dema/Dema.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// DOUBLE EXPONENTIAL MOVING AVERAGE (COMMON) - -public static class Dema -{ - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for DEMA."); - } - } - -} diff --git a/src/a-d/Dema/Dema.Models.cs b/src/a-d/Dema/Dema.Models.cs index 5dde861e7..41d5a330e 100644 --- a/src/a-d/Dema/Dema.Models.cs +++ b/src/a-d/Dema/Dema.Models.cs @@ -4,7 +4,7 @@ public record DemaResult ( DateTime Timestamp, double? Dema = null -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Dema.Null2NaN(); + public double Value => Dema.Null2NaN(); } diff --git a/src/a-d/Dema/Dema.StaticSeries.cs b/src/a-d/Dema/Dema.StaticSeries.cs index c7653fd28..0a10cb5a9 100644 --- a/src/a-d/Dema/Dema.StaticSeries.cs +++ b/src/a-d/Dema/Dema.StaticSeries.cs @@ -2,15 +2,16 @@ namespace Skender.Stock.Indicators; // DOUBLE EXPONENTIAL MOVING AVERAGE (SERIES) -public static partial class Indicator +public static partial class Dema { - private static List CalcDema( - this List source, + public static IReadOnlyList ToDema( + this IReadOnlyList source, int lookbackPeriods) where T : IReusable { // check parameter arguments - Dema.Validate(lookbackPeriods); + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); // initialize int length = source.Count; diff --git a/src/a-d/Dema/Dema.Utilities.cs b/src/a-d/Dema/Dema.Utilities.cs index 60f63a390..e04ddcead 100644 --- a/src/a-d/Dema/Dema.Utilities.cs +++ b/src/a-d/Dema/Dema.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// DOUBLE EXPONENTIAL MOVING AVERAGE (UTILITIES) + +public static partial class Dema { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int n = results .ToList() @@ -13,4 +15,16 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(2 * n + 100); } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for DEMA."); + } + } } diff --git a/src/a-d/Doji/Doji.Api.cs b/src/a-d/Doji/Doji.Api.cs deleted file mode 100644 index feeb2ce96..000000000 --- a/src/a-d/Doji/Doji.Api.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Skender.Stock.Indicators; - -// DOJI (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// Doji is a single candlestick pattern where open and close price are virtually identical, representing market indecision. - /// - /// See - /// documentation - /// for more information. - /// - /// - /// Configurable Quote type. See Guide for more information. - /// Historical price quotes. - /// Optional.Maximum absolute percent difference in open and close price. - /// Time series of Doji values. - /// Invalid parameter value provided. - public static IReadOnlyList GetDoji( - this IEnumerable quotes, - double maxPriceChangePercent = 0.1) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcDoji(maxPriceChangePercent); -} diff --git a/src/a-d/Doji/Doji.StaticSeries.cs b/src/a-d/Doji/Doji.StaticSeries.cs index e51870665..982d4f3eb 100644 --- a/src/a-d/Doji/Doji.StaticSeries.cs +++ b/src/a-d/Doji/Doji.StaticSeries.cs @@ -2,18 +2,33 @@ namespace Skender.Stock.Indicators; // DOJI (SERIES) -public static partial class Indicator +public static partial class Doji { - private static List CalcDoji( - this List quotesList, - double maxPriceChangePercent) + /// + /// Doji is a single candlestick pattern where open and close price + /// are virtually identical, representing market indecision. + /// + /// Configurable Quote type. + /// See Guide for more information. + /// Historical price quotes. + /// + /// Optional.Maximum absolute percent difference in open and close price. + /// + /// Time series of Doji values. + /// + /// Invalid parameter value provided. + /// + public static IReadOnlyList ToDoji( + this IReadOnlyList quotes, + double maxPriceChangePercent = 0.1) where TQuote : IQuote { // check parameter arguments - Doji.Validate(maxPriceChangePercent); + ArgumentNullException.ThrowIfNull(quotes); + Validate(maxPriceChangePercent); // initialize - int length = quotesList.Count; + int length = quotes.Count; List results = new(length); maxPriceChangePercent /= 100; @@ -21,7 +36,7 @@ private static List CalcDoji( // roll through candles for (int i = 0; i < length; i++) { - TQuote q = quotesList[i]; + TQuote q = quotes[i]; decimal? matchPrice = null; Match matchType = Match.None; diff --git a/src/a-d/Doji/Doji.Common.cs b/src/a-d/Doji/Doji.Utilities.cs similarity index 89% rename from src/a-d/Doji/Doji.Common.cs rename to src/a-d/Doji/Doji.Utilities.cs index 9c57180d0..eb8b3988c 100644 --- a/src/a-d/Doji/Doji.Common.cs +++ b/src/a-d/Doji/Doji.Utilities.cs @@ -1,8 +1,8 @@ namespace Skender.Stock.Indicators; -// DOJI (COMMON) +// DOJI (UTILITIES) -public static class Doji +public static partial class Doji { // parameter validation internal static void Validate( @@ -15,5 +15,4 @@ internal static void Validate( "Maximum Percent Change must be between 0 and 0.5 for Doji (0% to 0.5%)."); } } - } diff --git a/src/a-d/Donchian/Donchian.Api.cs b/src/a-d/Donchian/Donchian.Api.cs deleted file mode 100644 index 1a3d7db7d..000000000 --- a/src/a-d/Donchian/Donchian.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// DONCHIAN CHANNEL (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetDonchian( - this IEnumerable quotes, - int lookbackPeriods = 20) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcDonchian(lookbackPeriods); -} diff --git a/src/a-d/Donchian/Donchian.Common.cs b/src/a-d/Donchian/Donchian.Common.cs deleted file mode 100644 index ca6c80020..000000000 --- a/src/a-d/Donchian/Donchian.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// DONCHIAN CHANNEL (COMMON) - -public static class Donchian -{ - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Donchian Channel."); - } - } - -} diff --git a/src/a-d/Donchian/Donchian.StaticSeries.cs b/src/a-d/Donchian/Donchian.StaticSeries.cs index 92a6ad431..483ac4a34 100644 --- a/src/a-d/Donchian/Donchian.StaticSeries.cs +++ b/src/a-d/Donchian/Donchian.StaticSeries.cs @@ -2,24 +2,25 @@ namespace Skender.Stock.Indicators; // DONCHIAN CHANNEL (SERIES) -public static partial class Indicator +public static partial class Donchian { - private static List CalcDonchian( - this List quotesList, - int lookbackPeriods) + public static IReadOnlyList ToDonchian( + this IReadOnlyList quotes, + int lookbackPeriods = 20) where TQuote : IQuote { // check parameter arguments - Donchian.Validate(lookbackPeriods); + ArgumentNullException.ThrowIfNull(quotes); + Validate(lookbackPeriods); // initialize - int length = quotesList.Count; + int length = quotes.Count; List results = new(length); // roll through source values for (int i = 0; i < length; i++) { - TQuote q = quotesList[i]; + TQuote q = quotes[i]; if (i >= lookbackPeriods) { @@ -29,7 +30,7 @@ private static List CalcDonchian( // high/low over prior periods for (int p = i - lookbackPeriods; p < i; p++) { - TQuote d = quotesList[p]; + TQuote d = quotes[p]; if (d.High > highHigh) { diff --git a/src/a-d/Donchian/Donchian.Utilities.cs b/src/a-d/Donchian/Donchian.Utilities.cs index bc74a5da2..5cd392a2e 100644 --- a/src/a-d/Donchian/Donchian.Utilities.cs +++ b/src/a-d/Donchian/Donchian.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// DONCHIAN CHANNEL (UTILITIES) + +public static partial class Donchian { // CONDENSE (REMOVE null results) - /// + /// public static IReadOnlyList Condense( - this IEnumerable results) + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -18,9 +20,9 @@ public static IReadOnlyList Condense( } // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -28,4 +30,16 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Donchian Channel."); + } + } } diff --git a/src/a-d/Dpo/Dpo.Api.cs b/src/a-d/Dpo/Dpo.Api.cs deleted file mode 100644 index 38a81aea1..000000000 --- a/src/a-d/Dpo/Dpo.Api.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Skender.Stock.Indicators; - -// DETRENDED PRICE OSCILLATOR (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetDpo( - this IEnumerable results, - int lookbackPeriods) - where T : IReusable - => results - .ToSortedList() - .CalcDpo(lookbackPeriods); -} diff --git a/src/a-d/Dpo/Dpo.Models.cs b/src/a-d/Dpo/Dpo.Models.cs index 8a9d3bfeb..ff0ded498 100644 --- a/src/a-d/Dpo/Dpo.Models.cs +++ b/src/a-d/Dpo/Dpo.Models.cs @@ -5,7 +5,7 @@ public record DpoResult DateTime Timestamp, double? Dpo = null, double? Sma = null - ) : Reusable(Timestamp) + ) : IReusable { - public override double Value => Dpo.Null2NaN(); + public double Value => Dpo.Null2NaN(); } diff --git a/src/a-d/Dpo/Dpo.StaticSeries.cs b/src/a-d/Dpo/Dpo.StaticSeries.cs index 6f55dd35a..1298baca3 100644 --- a/src/a-d/Dpo/Dpo.StaticSeries.cs +++ b/src/a-d/Dpo/Dpo.StaticSeries.cs @@ -2,16 +2,16 @@ namespace Skender.Stock.Indicators; // DETRENDED PRICE OSCILLATOR (SERIES) -public static partial class Indicator +public static partial class Dpo { - // calculate series - private static List CalcDpo( - this List source, + public static IReadOnlyList ToDpo( + this IReadOnlyList source, int lookbackPeriods) where T : IReusable { // check parameter arguments - Dpo.Validate(lookbackPeriods); + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); // initialize int length = source.Count; @@ -20,7 +20,7 @@ private static List CalcDpo( int offset = (lookbackPeriods / 2) + 1; IReadOnlyList sma - = source.CalcSma(lookbackPeriods); + = source.ToSma(lookbackPeriods); // roll through source values for (int i = 0; i < length; i++) diff --git a/src/a-d/Dpo/Dpo.Common.cs b/src/a-d/Dpo/Dpo.Utilities.cs similarity index 84% rename from src/a-d/Dpo/Dpo.Common.cs rename to src/a-d/Dpo/Dpo.Utilities.cs index 06ae8a441..d6170d70c 100644 --- a/src/a-d/Dpo/Dpo.Common.cs +++ b/src/a-d/Dpo/Dpo.Utilities.cs @@ -1,8 +1,8 @@ namespace Skender.Stock.Indicators; -// DETRENDED PRICE OSCILLATOR (COMMON) +// DETRENDED PRICE OSCILLATOR (UTILITIES) -public static class Dpo +public static partial class Dpo { // parameter validation internal static void Validate( diff --git a/src/a-d/Dynamic/Dynamic.Api.cs b/src/a-d/Dynamic/Dynamic.Api.cs deleted file mode 100644 index 34e906f2f..000000000 --- a/src/a-d/Dynamic/Dynamic.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// McGINLEY DYNAMIC -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetDynamic( - this IEnumerable results, - int lookbackPeriods, - double kFactor = 0.6) - where T : IReusable - => results - .ToSortedList() - .CalcDynamic(lookbackPeriods, kFactor); -} diff --git a/src/a-d/Dynamic/Dynamic.Models.cs b/src/a-d/Dynamic/Dynamic.Models.cs index d6707e31b..83d73b23f 100644 --- a/src/a-d/Dynamic/Dynamic.Models.cs +++ b/src/a-d/Dynamic/Dynamic.Models.cs @@ -4,7 +4,7 @@ public record DynamicResult ( DateTime Timestamp, double? Dynamic -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Dynamic.Null2NaN(); + public double Value => Dynamic.Null2NaN(); } diff --git a/src/a-d/Dynamic/Dynamic.StaticSeries.cs b/src/a-d/Dynamic/Dynamic.StaticSeries.cs index 87ef22fee..03c56c40e 100644 --- a/src/a-d/Dynamic/Dynamic.StaticSeries.cs +++ b/src/a-d/Dynamic/Dynamic.StaticSeries.cs @@ -2,48 +2,41 @@ namespace Skender.Stock.Indicators; // McGINLEY DYNAMIC (SERIES) -public static partial class Indicator +public static partial class MgDynamic { - private static List CalcDynamic( - this List source, + public static IReadOnlyList ToDynamic( + this IReadOnlyList source, int lookbackPeriods, - double kFactor) + double kFactor = 0.6) where T : IReusable { // check parameter arguments - MgDynamic.Validate(lookbackPeriods, kFactor); + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods, kFactor); // initialize int length = source.Count; List results = new(length); - double prevDyn = double.NaN; - - // roll through source values, to get preliminary data - for (int i = 0; i < length; i++) + // skip first period + if (length > 0) { - T s = source[i]; - double dyn; - - // re/initialize - if (double.IsNaN(prevDyn)) - { - dyn = double.NaN; - prevDyn = s.Value; - } - - // normal Dynamic - else - { - dyn = prevDyn + (s.Value - prevDyn) / - (kFactor * lookbackPeriods * Math.Pow(s.Value / prevDyn, 4)); - - prevDyn = dyn; - } + results.Add(new(source[0].Timestamp, null)); + } - results.Add(new( - Timestamp: s.Timestamp, - Dynamic: dyn.NaN2Null())); + // roll through source values + for (int i = 1; i < length; i++) + { + double? dyn = Increment( + lookbackPeriods, + kFactor, + newVal: source[i].Value, + prevDyn: results[i - 1].Dynamic ?? source[i - 1].Value + ).NaN2Null(); + + results.Add(new DynamicResult( + Timestamp: source[i].Timestamp, + Dynamic: dyn)); } return results; diff --git a/src/a-d/Dynamic/Dynamic.Common.cs b/src/a-d/Dynamic/Dynamic.Utilities.cs similarity index 63% rename from src/a-d/Dynamic/Dynamic.Common.cs rename to src/a-d/Dynamic/Dynamic.Utilities.cs index cb71fd2a6..5228d3c4d 100644 --- a/src/a-d/Dynamic/Dynamic.Common.cs +++ b/src/a-d/Dynamic/Dynamic.Utilities.cs @@ -1,9 +1,19 @@ namespace Skender.Stock.Indicators; -// McGINLEY DYNAMIC (COMMON) +// McGINLEY DYNAMIC (UTILITIES) -public static class MgDynamic +public static partial class MgDynamic { + // increment calculation + public static double Increment( + int lookbackPeriods, + double kFactor, + double newVal, + double prevDyn) + => prevDyn + ( + (newVal - prevDyn) + / (kFactor * lookbackPeriods * Math.Pow(newVal / prevDyn, 4))); + // parameter validation internal static void Validate( int lookbackPeriods, diff --git a/src/e-k/ElderRay/ElderRay.Api.cs b/src/e-k/ElderRay/ElderRay.Api.cs deleted file mode 100644 index 4c437b693..000000000 --- a/src/e-k/ElderRay/ElderRay.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ELDER-RAY (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetElderRay( - this IEnumerable quotes, - int lookbackPeriods = 13) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcElderRay(lookbackPeriods); -} diff --git a/src/e-k/ElderRay/ElderRay.Common.cs b/src/e-k/ElderRay/ElderRay.Common.cs deleted file mode 100644 index 76e73e10a..000000000 --- a/src/e-k/ElderRay/ElderRay.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ELDER-RAY (COMMON) - -public static class ElderRay -{ - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Elder-ray Index."); - } - } - -} diff --git a/src/e-k/ElderRay/ElderRay.Models.cs b/src/e-k/ElderRay/ElderRay.Models.cs index 06cde6fc9..7fb38fb86 100644 --- a/src/e-k/ElderRay/ElderRay.Models.cs +++ b/src/e-k/ElderRay/ElderRay.Models.cs @@ -6,7 +6,7 @@ public record ElderRayResult double? Ema, double? BullPower, double? BearPower -) : Reusable(Timestamp) +) : IReusable { - public override double Value => (BullPower + BearPower).Null2NaN(); + public double Value => (BullPower + BearPower).Null2NaN(); } diff --git a/src/e-k/ElderRay/ElderRay.StaticSeries.cs b/src/e-k/ElderRay/ElderRay.StaticSeries.cs index 0400056bb..785223db6 100644 --- a/src/e-k/ElderRay/ElderRay.StaticSeries.cs +++ b/src/e-k/ElderRay/ElderRay.StaticSeries.cs @@ -2,22 +2,29 @@ namespace Skender.Stock.Indicators; // ELDER-RAY (SERIES) -public static partial class Indicator +public static partial class ElderRay { + public static IReadOnlyList ToElderRay( + this IReadOnlyList quotes, + int lookbackPeriods = 13) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcElderRay(lookbackPeriods); + private static List CalcElderRay( - this List source, + this IReadOnlyList source, int lookbackPeriods) { // check parameter arguments - ElderRay.Validate(lookbackPeriods); + Validate(lookbackPeriods); // initialize int length = source.Count; List results = new(length); // EMA - List emaResults - = source.CalcEma(lookbackPeriods); + IReadOnlyList emaResults + = source.ToEma(lookbackPeriods); // roll through source values for (int i = 0; i < length; i++) diff --git a/src/e-k/ElderRay/ElderRay.Utilities.cs b/src/e-k/ElderRay/ElderRay.Utilities.cs index e38e79590..5e789103b 100644 --- a/src/e-k/ElderRay/ElderRay.Utilities.cs +++ b/src/e-k/ElderRay/ElderRay.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// ELDER-RAY (UTILITIES) + +public static partial class ElderRay { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int n = results .ToList() @@ -13,4 +15,16 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(n + 100); } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Elder-ray Index."); + } + } } diff --git a/src/e-k/Ema/Ema.Api.cs b/src/e-k/Ema/Ema.Api.cs deleted file mode 100644 index 3d78301b9..000000000 --- a/src/e-k/Ema/Ema.Api.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace Skender.Stock.Indicators; - -// EXPONENTIAL MOVING AVERAGE (API) - -public static partial class Api -{ - // SERIES, from CHAIN - public static IReadOnlyList ToEma( - this IEnumerable results, - int lookbackPeriods) - where T : IReusable - => results - .ToSortedList() - .CalcEma(lookbackPeriods); - - // HUB, from Chain Provider - public static EmaHub ToEma( - this IChainProvider chainProvider, - int lookbackPeriods) - where T : IReusable - => new(chainProvider, lookbackPeriods); -} - -public interface IEma -{ - int LookbackPeriods { get; } - double K { get; } -} diff --git a/src/e-k/Ema/Ema.Increments.cs b/src/e-k/Ema/Ema.Increments.cs index 8ef01a65c..0df4706d5 100644 --- a/src/e-k/Ema/Ema.Increments.cs +++ b/src/e-k/Ema/Ema.Increments.cs @@ -2,10 +2,14 @@ namespace Skender.Stock.Indicators; // EXPONENTIAL MOVING AVERAGE (INCREMENTING LIST) -public class EmaList : List, IEma, IIncrementalPrice - where TQuote : IQuote +/// +/// Exponential Moving Average (EMA) +/// from incremental reusable values. +/// +public class EmaList : List, IEma, IAddQuote, IAddReusable { - private readonly List _buffer; + private readonly Queue _buffer; + private double _bufferSum; public EmaList(int lookbackPeriods) { @@ -14,20 +18,21 @@ public EmaList(int lookbackPeriods) K = 2d / (lookbackPeriods + 1); _buffer = new(lookbackPeriods); + _bufferSum = 0; } public int LookbackPeriods { get; init; } public double K { get; init; } - public void Add(DateTime timestamp, double price) + public void Add(DateTime timestamp, double value) { // update buffer - _buffer.Add(price); - - if (_buffer.Count > LookbackPeriods) + if (_buffer.Count == LookbackPeriods) { - _buffer.RemoveAt(0); + _bufferSum -= _buffer.Dequeue(); } + _buffer.Enqueue(value); + _bufferSum += value; // add nulls for incalculable periods if (Count < LookbackPeriods - 1) @@ -36,83 +41,50 @@ public void Add(DateTime timestamp, double price) return; } - // re/initialize + // re/initialize as SMA if (this[^1].Ema is null) { - double sum = 0; - for (int i = 0; i < LookbackPeriods; i++) - { - sum += _buffer[i]; - } - base.Add(new EmaResult( timestamp, - sum / LookbackPeriods)); - + _bufferSum / LookbackPeriods)); return; } // calculate EMA normally base.Add(new EmaResult( timestamp, - Ema.Increment(K, this[^1].Ema, price))); + Ema.Increment(K, this[^1].Ema, value))); } - public void Add(TQuote quote) - => Add(quote.Timestamp, quote.Value); -} - -/// -/// Exponential Moving Average (EMA) without date context (array-based). -/// -/// -public class EmaArray : List, IEma, IIncrementalValue -{ - private readonly List _buffer; - - public EmaArray(int lookbackPeriods) + public void Add(IReusable value) { - Ema.Validate(lookbackPeriods); - LookbackPeriods = lookbackPeriods; - K = 2d / (lookbackPeriods + 1); - - _buffer = new(lookbackPeriods); + ArgumentNullException.ThrowIfNull(value); + Add(value.Timestamp, value.Value); } - public int LookbackPeriods { get; init; } - public double K { get; init; } - - public void Add(double price) + public void Add(IReadOnlyList values) { - // update buffer - _buffer.Add(price); + ArgumentNullException.ThrowIfNull(values); - if (_buffer.Count > LookbackPeriods) + for (int i = 0; i < values.Count; i++) { - _buffer.RemoveAt(0); + Add(values[i].Timestamp, values[i].Value); } + } - // add nulls for incalculable periods - if (Count < LookbackPeriods - 1) - { - Add(null); - return; - } + public void Add(IQuote quote) + { + ArgumentNullException.ThrowIfNull(quote); + Add(quote.Timestamp, quote.Value); + } - // re/initialize - if (this[^1] is null) - { - double sum = 0; - for (int i = 0; i < LookbackPeriods; i++) - { - sum += _buffer[i]; - } + public void Add(IReadOnlyList quotes) + { + ArgumentNullException.ThrowIfNull(quotes); - base.Add(sum / LookbackPeriods); - return; + for (int i = 0; i < quotes.Count; i++) + { + Add(quotes[i]); } - - // calculate EMA normally - base.Add(Ema.Increment(K, this[^1], price)); } } diff --git a/src/e-k/Ema/Ema.Models.cs b/src/e-k/Ema/Ema.Models.cs index f66860fc4..c999dbaad 100644 --- a/src/e-k/Ema/Ema.Models.cs +++ b/src/e-k/Ema/Ema.Models.cs @@ -4,7 +4,7 @@ public record EmaResult ( DateTime Timestamp, double? Ema = null -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Ema.Null2NaN(); + public double Value => Ema.Null2NaN(); } diff --git a/src/e-k/Ema/Ema.StaticSeries.cs b/src/e-k/Ema/Ema.StaticSeries.cs index c072fc51b..49ad66637 100644 --- a/src/e-k/Ema/Ema.StaticSeries.cs +++ b/src/e-k/Ema/Ema.StaticSeries.cs @@ -4,17 +4,18 @@ namespace Skender.Stock.Indicators; public static partial class Ema { - internal static List CalcEma( - this List source, + public static IReadOnlyList ToEma( + this IReadOnlyList source, int lookbackPeriods) where T : IReusable { // check parameter arguments + ArgumentNullException.ThrowIfNull(source); Validate(lookbackPeriods); // initialize int length = source.Count; - List results = new(length); + EmaResult[] results = new EmaResult[length]; double lastEma = double.NaN; double k = 2d / (lookbackPeriods + 1); @@ -27,40 +28,25 @@ internal static List CalcEma( // skip incalculable periods if (i < lookbackPeriods - 1) { - results.Add(new(Timestamp: s.Timestamp)); + results[i] = new EmaResult(Timestamp: s.Timestamp); continue; } - double ema; + double ema = !double.IsNaN(lastEma) - // when no prior EMA, reset as SMA - if (double.IsNaN(lastEma)) - { - double sum = 0; - for (int p = i - lookbackPeriods + 1; p <= i; p++) - { - T ps = source[p]; - sum += ps.Value; - } - - ema = sum / lookbackPeriods; - } + // calculate EMA (normally) + ? Ema.Increment(k, lastEma, s.Value) - // normal EMA - else - { - ema = Increment(k, lastEma, s.Value); - } + // when no prior EMA, reset as SMA + : Sma.Increment(source, lookbackPeriods, i); - EmaResult r = new( + results[i] = new EmaResult( Timestamp: s.Timestamp, Ema: ema.NaN2Null()); - results.Add(r); - lastEma = ema; } - return results; + return new List(results); } } diff --git a/src/e-k/Ema/Ema.StreamHub.cs b/src/e-k/Ema/Ema.StreamHub.cs index 701bdb7b5..c66697259 100644 --- a/src/e-k/Ema/Ema.StreamHub.cs +++ b/src/e-k/Ema/Ema.StreamHub.cs @@ -2,14 +2,33 @@ namespace Skender.Stock.Indicators; // EXPONENTIAL MOVING AVERAGE (STREAM HUB) -public class EmaHub : ReusableObserver, - IReusableHub, IEma +#region hub interface and initializer + +public interface IEma +{ + int LookbackPeriods { get; } + double K { get; } +} + +public static partial class Ema +{ + // HUB, from Chain Provider + public static EmaHub ToEma( + this IChainProvider chainProvider, + int lookbackPeriods) + where T : IReusable + => new(chainProvider, lookbackPeriods); +} +#endregion + +public class EmaHub + : ChainProvider, IEma where TIn : IReusable { #region constructors private readonly string hubName; - + internal EmaHub( IChainProvider provider, int lookbackPeriods) : base(provider) @@ -30,37 +49,28 @@ internal EmaHub( public override string ToString() => hubName; - internal override void Add(Act act, TIn newIn, int? index) + protected override (EmaResult result, int index) + ToIndicator(TIn item, int? indexHint) { - double ema; - - int i = index ?? Provider.GetIndex(newIn, false); - - if (i >= LookbackPeriods - 1) - { - IReusable last = Cache[i - 1]; + int i = indexHint ?? ProviderCache.GetIndex(item, true); - ema = !double.IsNaN(last.Value) + double ema = i >= LookbackPeriods - 1 + ? Cache[i - 1].Ema is not null // normal - ? Ema.Increment(K, last.Value, newIn.Value) + ? Ema.Increment(K, Cache[i - 1].Value, item.Value) - // re/initialize - : Sma.Increment(Provider.Results, i, LookbackPeriods); - } + // re/initialize as SMA + : Sma.Increment(ProviderCache, LookbackPeriods, i) - // warmup periods are never calculable - else - { - ema = double.NaN; - } + // warmup periods are never calculable + : double.NaN; // candidate result EmaResult r = new( - Timestamp: newIn.Timestamp, + Timestamp: item.Timestamp, Ema: ema.NaN2Null()); - // save and send - Motify(act, r, i); + return (r, i); } } diff --git a/src/e-k/Ema/Ema.Utilities.cs b/src/e-k/Ema/Ema.Utilities.cs index eb922d370..6a8f8f1f0 100644 --- a/src/e-k/Ema/Ema.Utilities.cs +++ b/src/e-k/Ema/Ema.Utilities.cs @@ -27,7 +27,7 @@ public static double Increment( // remove recommended periods public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int n = results .ToList() diff --git a/src/e-k/Epma/Epma.Api.cs b/src/e-k/Epma/Epma.Api.cs deleted file mode 100644 index 932ccea70..000000000 --- a/src/e-k/Epma/Epma.Api.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ENDPOINT MOVING AVERAGE (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetEpma( - this IEnumerable results, - int lookbackPeriods) - where T : IReusable - => results - .ToSortedList() - .CalcEpma(lookbackPeriods); -} diff --git a/src/e-k/Epma/Epma.Common.cs b/src/e-k/Epma/Epma.Common.cs deleted file mode 100644 index 84cdab2ac..000000000 --- a/src/e-k/Epma/Epma.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ENDPOINT MOVING AVERAGE (COMMON) - -public static class Epma -{ - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Epma."); - } - } - -} diff --git a/src/e-k/Epma/Epma.Models.cs b/src/e-k/Epma/Epma.Models.cs index 9e5fb96d8..9392c5e26 100644 --- a/src/e-k/Epma/Epma.Models.cs +++ b/src/e-k/Epma/Epma.Models.cs @@ -4,7 +4,7 @@ public record EpmaResult ( DateTime Timestamp, double? Epma -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Epma.Null2NaN(); + public double Value => Epma.Null2NaN(); } diff --git a/src/e-k/Epma/Epma.StaticSeries.cs b/src/e-k/Epma/Epma.StaticSeries.cs index 0319b6d7b..131d601be 100644 --- a/src/e-k/Epma/Epma.StaticSeries.cs +++ b/src/e-k/Epma/Epma.StaticSeries.cs @@ -2,23 +2,23 @@ namespace Skender.Stock.Indicators; // ENDPOINT MOVING AVERAGE (SERIES) -public static partial class Indicator +public static partial class Epma { - // calculate series - private static List CalcEpma( - this List source, + public static IReadOnlyList ToEpma( + this IReadOnlyList source, int lookbackPeriods) where T : IReusable { // check parameter arguments - Epma.Validate(lookbackPeriods); + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); // initialize int length = source.Count; List results = new(length); IReadOnlyList slope - = source.CalcSlope(lookbackPeriods); + = source.ToSlope(lookbackPeriods); // roll through source values for (int i = 0; i < length; i++) diff --git a/src/e-k/Epma/Epma.Utilities.cs b/src/e-k/Epma/Epma.Utilities.cs index 1376bb215..49bfc8370 100644 --- a/src/e-k/Epma/Epma.Utilities.cs +++ b/src/e-k/Epma/Epma.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// ENDPOINT MOVING AVERAGE (UTILITIES) + +public static partial class Epma { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -13,4 +15,16 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Epma."); + } + } } diff --git a/src/e-k/Fcb/Fcb.Api.cs b/src/e-k/Fcb/Fcb.Api.cs deleted file mode 100644 index 0b48e1fde..000000000 --- a/src/e-k/Fcb/Fcb.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// FRACTAL CHAOS BANDS (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetFcb( - this IEnumerable quotes, - int windowSpan = 2) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcFcb(windowSpan); -} diff --git a/src/e-k/Fcb/Fcb.Common.cs b/src/e-k/Fcb/Fcb.Common.cs deleted file mode 100644 index 4535c746a..000000000 --- a/src/e-k/Fcb/Fcb.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// FRACTAL CHAOS BANDS (COMMON) - -public static class Fcb -{ - // parameter validation - internal static void Validate( - int windowSpan) - { - // check parameter arguments - if (windowSpan < 2) - { - throw new ArgumentOutOfRangeException(nameof(windowSpan), windowSpan, - "Window span must be at least 2 for FCB."); - } - } - -} diff --git a/src/e-k/Fcb/Fcb.StaticSeries.cs b/src/e-k/Fcb/Fcb.StaticSeries.cs index 0eac52d52..36ca6553e 100644 --- a/src/e-k/Fcb/Fcb.StaticSeries.cs +++ b/src/e-k/Fcb/Fcb.StaticSeries.cs @@ -2,22 +2,23 @@ namespace Skender.Stock.Indicators; // FRACTAL CHAOS BANDS (SERIES) -public static partial class Indicator +public static partial class Fcb { - private static List CalcFcb( - this List quotesList, - int windowSpan) + public static IReadOnlyList ToFcb( + this IReadOnlyList quotes, + int windowSpan = 2) where TQuote : IQuote { // check parameter arguments - Fcb.Validate(windowSpan); + ArgumentNullException.ThrowIfNull(quotes); + Validate(windowSpan); // initialize - int length = quotesList.Count; + int length = quotes.Count; List results = new(length); - List fractals = quotesList - .CalcFractal(windowSpan, windowSpan, EndType.HighLow); + IReadOnlyList fractals = quotes + .ToFractal(windowSpan, windowSpan, EndType.HighLow); decimal? upperLine = null; decimal? lowerLine = null; diff --git a/src/e-k/Fcb/Fcb.Utilities.cs b/src/e-k/Fcb/Fcb.Utilities.cs index 989961b6d..6a3dc52b4 100644 --- a/src/e-k/Fcb/Fcb.Utilities.cs +++ b/src/e-k/Fcb/Fcb.Utilities.cs @@ -1,11 +1,12 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// FRACTAL CHAOS BANDS (UTILITIES) + +public static partial class Fcb { - // CONDENSE (REMOVE null results) - /// + /// public static IReadOnlyList Condense( - this IEnumerable results) + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -17,10 +18,9 @@ public static IReadOnlyList Condense( return resultsList.ToSortedList(); } - // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -28,4 +28,16 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + int windowSpan) + { + // check parameter arguments + if (windowSpan < 2) + { + throw new ArgumentOutOfRangeException(nameof(windowSpan), windowSpan, + "Window span must be at least 2 for FCB."); + } + } } diff --git a/src/e-k/FisherTransform/FisherTransform.Api.cs b/src/e-k/FisherTransform/FisherTransform.Api.cs deleted file mode 100644 index 0a8ba4f0c..000000000 --- a/src/e-k/FisherTransform/FisherTransform.Api.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Skender.Stock.Indicators; - -// FISHER TRANSFORM (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetFisherTransform( - this IEnumerable results, - int lookbackPeriods = 10) - where T : IReusable - => results - .ToSortedList(CandlePart.HL2) - .CalcFisherTransform(lookbackPeriods); -} diff --git a/src/e-k/FisherTransform/FisherTransform.Models.cs b/src/e-k/FisherTransform/FisherTransform.Models.cs index 17ec99011..0f983ffe0 100644 --- a/src/e-k/FisherTransform/FisherTransform.Models.cs +++ b/src/e-k/FisherTransform/FisherTransform.Models.cs @@ -5,7 +5,7 @@ public record FisherTransformResult DateTime Timestamp, double? Fisher, double? Trigger -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Fisher.Null2NaN(); + public double Value => Fisher.Null2NaN(); } diff --git a/src/e-k/FisherTransform/FisherTransform.StaticSeries.cs b/src/e-k/FisherTransform/FisherTransform.StaticSeries.cs index daa6afd92..a9f542ba8 100644 --- a/src/e-k/FisherTransform/FisherTransform.StaticSeries.cs +++ b/src/e-k/FisherTransform/FisherTransform.StaticSeries.cs @@ -2,18 +2,22 @@ namespace Skender.Stock.Indicators; // FISHER TRANSFORM (SERIES) -public static partial class Indicator +public static partial class FisherTransform { - private static List CalcFisherTransform( - this List source, - int lookbackPeriods) + public static IReadOnlyList ToFisherTransform( + this IReadOnlyList source, + int lookbackPeriods = 10) where T : IReusable { // check parameter arguments - FisherTransform.Validate(lookbackPeriods); + Validate(lookbackPeriods); + + // prefer HL2 when IQuote + IReadOnlyList values + = source.ToPreferredList(CandlePart.HL2); // initialize - int length = source.Count; + int length = values.Count; double[] pr = new double[length]; // median price double[] xv = new double[length]; // price transform "value" List results = new(length); @@ -21,7 +25,7 @@ private static List CalcFisherTransform( // roll through source values for (int i = 0; i < length; i++) { - IReusable s = source[i]; + IReusable s = values[i]; pr[i] = s.Value; double minPrice = pr[i]; diff --git a/src/e-k/FisherTransform/FisherTransform.Common.cs b/src/e-k/FisherTransform/FisherTransform.Utilities.cs similarity index 84% rename from src/e-k/FisherTransform/FisherTransform.Common.cs rename to src/e-k/FisherTransform/FisherTransform.Utilities.cs index 71e6f016c..8d192b5db 100644 --- a/src/e-k/FisherTransform/FisherTransform.Common.cs +++ b/src/e-k/FisherTransform/FisherTransform.Utilities.cs @@ -1,8 +1,8 @@ namespace Skender.Stock.Indicators; -// FISHER TRANSFORM (COMMON) +// FISHER TRANSFORM (UTILITIES) -public static class FisherTransform +public static partial class FisherTransform { // parameter validation internal static void Validate( @@ -15,5 +15,4 @@ internal static void Validate( "Lookback periods must be greater than 0 for Fisher Transform."); } } - } diff --git a/src/e-k/ForceIndex/ForceIndex.Api.cs b/src/e-k/ForceIndex/ForceIndex.Api.cs deleted file mode 100644 index f095cf270..000000000 --- a/src/e-k/ForceIndex/ForceIndex.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// FORCE INDEX (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetForceIndex( - this IEnumerable quotes, - int lookbackPeriods = 2) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcForceIndex(lookbackPeriods); -} diff --git a/src/e-k/ForceIndex/ForceIndex.Models.cs b/src/e-k/ForceIndex/ForceIndex.Models.cs index 9724707ea..08a51bdb3 100644 --- a/src/e-k/ForceIndex/ForceIndex.Models.cs +++ b/src/e-k/ForceIndex/ForceIndex.Models.cs @@ -4,7 +4,7 @@ public record ForceIndexResult ( DateTime Timestamp, double? ForceIndex = null -) : Reusable(Timestamp) +) : IReusable { - public override double Value => ForceIndex.Null2NaN(); + public double Value => ForceIndex.Null2NaN(); } diff --git a/src/e-k/ForceIndex/ForceIndex.StaticSeries.cs b/src/e-k/ForceIndex/ForceIndex.StaticSeries.cs index 8374def9c..1bea16987 100644 --- a/src/e-k/ForceIndex/ForceIndex.StaticSeries.cs +++ b/src/e-k/ForceIndex/ForceIndex.StaticSeries.cs @@ -2,14 +2,21 @@ namespace Skender.Stock.Indicators; // FORCE INDEX (SERIES) -public static partial class Indicator +public static partial class ForceIndex { + public static IReadOnlyList ToForceIndex( + this IReadOnlyList quotes, + int lookbackPeriods = 2) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcForceIndex(lookbackPeriods); + private static List CalcForceIndex( - this List source, + this IReadOnlyList source, int lookbackPeriods) { // check parameter arguments - ForceIndex.Validate(lookbackPeriods); + Validate(lookbackPeriods); // initialize int length = source.Count; diff --git a/src/e-k/ForceIndex/ForceIndex.Utilities.cs b/src/e-k/ForceIndex/ForceIndex.Utilities.cs index 428b8dcfc..025c244ca 100644 --- a/src/e-k/ForceIndex/ForceIndex.Utilities.cs +++ b/src/e-k/ForceIndex/ForceIndex.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// FORCE INDEX (UTILITIES) + +public static partial class ForceIndex { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int n = results .ToList() @@ -13,4 +15,16 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(n + 100); } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Force Index."); + } + } } diff --git a/src/e-k/ForceIndex/Kvo.Common (1).cs b/src/e-k/ForceIndex/Kvo.Common (1).cs deleted file mode 100644 index a694b6633..000000000 --- a/src/e-k/ForceIndex/Kvo.Common (1).cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// FORCE INDEX (COMMON) - -public static class ForceIndex -{ - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Force Index."); - } - } - -} diff --git a/src/e-k/Fractal/Fractal.Api.cs b/src/e-k/Fractal/Fractal.Api.cs deleted file mode 100644 index 01972eeb4..000000000 --- a/src/e-k/Fractal/Fractal.Api.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Skender.Stock.Indicators; - -// WILLIAMS FRACTAL (API) -public static partial class Indicator -{ - /// - /// - public static IReadOnlyList GetFractal( - this IEnumerable quotes, - int windowSpan = 2, - EndType endType = EndType.HighLow) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcFractal(windowSpan, windowSpan, endType); - - // more configurable version (undocumented) - /// - /// - public static IReadOnlyList GetFractal( - this IEnumerable quotes, - int leftSpan, - int rightSpan, - EndType endType = EndType.HighLow) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcFractal(leftSpan, rightSpan, endType); -} diff --git a/src/e-k/Fractal/Fractal.Common.cs b/src/e-k/Fractal/Fractal.Common.cs deleted file mode 100644 index 33f2f69ea..000000000 --- a/src/e-k/Fractal/Fractal.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// WILLIAMS FRACTAL (COMMON) - -public static class Fractal -{ - // parameter validation - internal static void Validate( - int windowSpan) - { - // check parameter arguments - if (windowSpan < 2) - { - throw new ArgumentOutOfRangeException(nameof(windowSpan), windowSpan, - "Window span must be at least 2 for Fractal."); - } - } - -} diff --git a/src/e-k/Fractal/Fractal.StaticSeries.cs b/src/e-k/Fractal/Fractal.StaticSeries.cs index 9d15456b7..2dd3b1615 100644 --- a/src/e-k/Fractal/Fractal.StaticSeries.cs +++ b/src/e-k/Fractal/Fractal.StaticSeries.cs @@ -2,30 +2,38 @@ namespace Skender.Stock.Indicators; // WILLIAMS FRACTAL (SERIES) -public static partial class Indicator +public static partial class Fractal { - private static List CalcFractal( - this List quotesList, + public static IReadOnlyList ToFractal( + this IReadOnlyList quotes, + int windowSpan = 2, + EndType endType = EndType.HighLow) + where TQuote : IQuote => quotes + .ToFractal(windowSpan, windowSpan, endType); + + public static IReadOnlyList ToFractal( + this IReadOnlyList quotes, int leftSpan, int rightSpan, - EndType endType) + EndType endType = EndType.HighLow) where TQuote : IQuote { // check parameter arguments - Fractal.Validate(Math.Min(leftSpan, rightSpan)); + ArgumentNullException.ThrowIfNull(quotes); + Validate(Math.Min(leftSpan, rightSpan)); // initialize - int length = quotesList.Count; + int length = quotes.Count; List results = new(length); // roll through source values for (int i = 0; i < length; i++) { - TQuote q = quotesList[i]; + TQuote q = quotes[i]; decimal? fractalBear = null; decimal? fractalBull = null; - if (i + 1 > leftSpan && i + 1 <= quotesList.Count - rightSpan) + if (i + 1 > leftSpan && i + 1 <= length - rightSpan) { bool isHigh = true; bool isLow = true; @@ -46,7 +54,7 @@ private static List CalcFractal( } // evaluate wing periods - TQuote wing = quotesList[p]; + TQuote wing = quotes[p]; decimal wingHigh = endType == EndType.Close ? wing.Close : wing.High; diff --git a/src/e-k/Fractal/Fractal.Utilities.cs b/src/e-k/Fractal/Fractal.Utilities.cs index 5926ae46a..9e36b3105 100644 --- a/src/e-k/Fractal/Fractal.Utilities.cs +++ b/src/e-k/Fractal/Fractal.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// WILLIAMS FRACTAL (UTILITIES) + +public static partial class Fractal { // CONDENSE (REMOVE null results) - /// + /// public static IReadOnlyList Condense( - this IEnumerable results) + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -16,4 +18,16 @@ public static IReadOnlyList Condense( return resultsList.ToSortedList(); } + + // parameter validation + internal static void Validate( + int windowSpan) + { + // check parameter arguments + if (windowSpan < 2) + { + throw new ArgumentOutOfRangeException(nameof(windowSpan), windowSpan, + "Window span must be at least 2 for Fractal."); + } + } } diff --git a/src/e-k/Gator/Gator.Api.cs b/src/e-k/Gator/Gator.Api.cs deleted file mode 100644 index 0682aaf92..000000000 --- a/src/e-k/Gator/Gator.Api.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace Skender.Stock.Indicators; - -// GATOR OSCILLATOR (API) -public static partial class Indicator -{ - // SERIES, from CHAIN (or QUOTES) - /// - /// Gator Oscillator is an expanded view of Williams Alligator. - /// - /// See - /// documentation - /// for more information. - /// - /// - /// - /// - /// - /// T must be or type - /// - /// Time-series values to transform. - /// Time series of Gator values. - - // See Alligator API for explanation of unusual setup. - public static IReadOnlyList GetGator( - this IEnumerable source) - where T : IReusable - => source - .GetAlligator() - .GetGator(); - - // SERIES, from [custom] Alligator - public static IReadOnlyList GetGator( - this IEnumerable alligator) - => alligator - .ToList() - .CalcGator(); -} diff --git a/src/e-k/Gator/Gator.StaticSeries.cs b/src/e-k/Gator/Gator.StaticSeries.cs index 12d502632..66fe5353b 100644 --- a/src/e-k/Gator/Gator.StaticSeries.cs +++ b/src/e-k/Gator/Gator.StaticSeries.cs @@ -2,11 +2,31 @@ namespace Skender.Stock.Indicators; // GATOR OSCILLATOR (SERIES) -public static partial class Indicator +public static partial class Gator { - private static List CalcGator( - this List alligator) + /// + /// Gator Oscillator is an expanded view of Williams Alligator. + /// + /// + /// + /// + /// T must be or type + /// + /// Time-series values to transform. + /// Time series of Gator values. + public static IReadOnlyList ToGator( + this IReadOnlyList source) + where T : IReusable + => source + .ToAlligator() + .ToGator(); + + // from [custom] Alligator + public static IReadOnlyList ToGator( + this IReadOnlyList alligator) { + ArgumentNullException.ThrowIfNull(alligator); + // initialize int length = alligator.Count; List results = []; diff --git a/src/e-k/Gator/Gator.Utilities.cs b/src/e-k/Gator/Gator.Utilities.cs index e8be0f046..a9b97545a 100644 --- a/src/e-k/Gator/Gator.Utilities.cs +++ b/src/e-k/Gator/Gator.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// GATOR OSCILLATOR (UTILITIES) + +public static partial class Gator { // CONDENSE (REMOVE null results) - /// + /// public static IReadOnlyList Condense( - this IEnumerable results) + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -18,7 +20,7 @@ public static IReadOnlyList Condense( } // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) => results.Remove(150); + this IReadOnlyList results) => results.Remove(150); } diff --git a/src/e-k/HeikinAshi/HeikinAshi.Api.cs b/src/e-k/HeikinAshi/HeikinAshi.Api.cs deleted file mode 100644 index d17a73c29..000000000 --- a/src/e-k/HeikinAshi/HeikinAshi.Api.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Skender.Stock.Indicators; - -// HEIKIN-ASHI (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetHeikinAshi( - this IEnumerable quotes) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcHeikinAshi(); -} diff --git a/src/e-k/HeikinAshi/HeikinAshi.StaticSeries.cs b/src/e-k/HeikinAshi/HeikinAshi.StaticSeries.cs index 295303089..946cb94eb 100644 --- a/src/e-k/HeikinAshi/HeikinAshi.StaticSeries.cs +++ b/src/e-k/HeikinAshi/HeikinAshi.StaticSeries.cs @@ -2,14 +2,16 @@ namespace Skender.Stock.Indicators; // HEIKIN-ASHI (SERIES) -public static partial class Indicator +public static class HeikinAshi { - private static List CalcHeikinAshi( - this List quotesList) + public static IReadOnlyList ToHeikinAshi( + this IReadOnlyList quotes) where TQuote : IQuote { + ArgumentNullException.ThrowIfNull(quotes); + // initialize - int length = quotesList.Count; + int length = quotes.Count; List results = new(length); decimal prevOpen = decimal.MinValue; @@ -17,7 +19,7 @@ private static List CalcHeikinAshi( if (length > 0) { - TQuote q = quotesList[0]; + TQuote q = quotes[0]; prevOpen = q.Open; prevClose = q.Close; } @@ -25,7 +27,7 @@ private static List CalcHeikinAshi( // roll through source values for (int i = 0; i < length; i++) { - TQuote q = quotesList[i]; + TQuote q = quotes[i]; // close decimal close = (q.Open + q.High + q.Low + q.Close) / 4; diff --git a/src/e-k/Hma/Hma.Api.cs b/src/e-k/Hma/Hma.Api.cs deleted file mode 100644 index 2ea9d317d..000000000 --- a/src/e-k/Hma/Hma.Api.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Skender.Stock.Indicators; - -// HULL MOVING AVERAGE (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetHma( - this IEnumerable results, - int lookbackPeriods) - where T : IReusable - => results - .ToSortedList() - .CalcHma(lookbackPeriods); -} diff --git a/src/e-k/Hma/Hma.Common.cs b/src/e-k/Hma/Hma.Common.cs deleted file mode 100644 index d3d98190f..000000000 --- a/src/e-k/Hma/Hma.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// HULL MOVING AVERAGE (COMMON) - -public static class Hma -{ - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 1 for HMA."); - } - } - -} diff --git a/src/e-k/Hma/Hma.Models.cs b/src/e-k/Hma/Hma.Models.cs index 3f908c113..af738a356 100644 --- a/src/e-k/Hma/Hma.Models.cs +++ b/src/e-k/Hma/Hma.Models.cs @@ -4,7 +4,7 @@ public record HmaResult ( DateTime Timestamp, double? Hma = null -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Hma.Null2NaN(); + public double Value => Hma.Null2NaN(); } diff --git a/src/e-k/Hma/Hma.StaticSeries.cs b/src/e-k/Hma/Hma.StaticSeries.cs index d06a74c98..a4c02ba66 100644 --- a/src/e-k/Hma/Hma.StaticSeries.cs +++ b/src/e-k/Hma/Hma.StaticSeries.cs @@ -2,16 +2,16 @@ namespace Skender.Stock.Indicators; // HULL MOVING AVERAGE (SERIES) -public static partial class Indicator +public static partial class Hma { - // calculate series - private static List CalcHma( - this List source, + public static IReadOnlyList ToHma( + this IReadOnlyList source, int lookbackPeriods) where T : IReusable { // check parameter arguments - Hma.Validate(lookbackPeriods); + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); // initialize int length = source.Count; @@ -19,10 +19,10 @@ private static List CalcHma( List synthHistory = []; IReadOnlyList wmaN1 - = source.CalcWma(lookbackPeriods); + = source.ToWma(lookbackPeriods); IReadOnlyList wmaN2 - = source.CalcWma(lookbackPeriods / 2); + = source.ToWma(lookbackPeriods / 2); // roll through source values, to get interim synthetic quotes for (int i = 0; i < length; i++) @@ -53,7 +53,7 @@ IReadOnlyList wmaN2 .ToList(); // calculate final HMA = WMA with period SQRT(n) - List hmaResults = synthHistory.CalcWma(sqN) + List hmaResults = synthHistory.ToWma(sqN) .Select(x => new HmaResult( Timestamp: x.Timestamp, Hma: x.Wma diff --git a/src/e-k/Hma/Hma.Utilities.cs b/src/e-k/Hma/Hma.Utilities.cs index d22ef9ffe..2cb778625 100644 --- a/src/e-k/Hma/Hma.Utilities.cs +++ b/src/e-k/Hma/Hma.Utilities.cs @@ -1,16 +1,16 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +public static partial class Hma { - // remove recommended periods - /// - public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Hma != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (lookbackPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 1 for HMA."); + } } } diff --git a/src/e-k/HtTrendline/HtTrendline.Api.cs b/src/e-k/HtTrendline/HtTrendline.Api.cs deleted file mode 100644 index 96d163808..000000000 --- a/src/e-k/HtTrendline/HtTrendline.Api.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Skender.Stock.Indicators; - -// HILBERT TRANSFORM - INSTANTANEOUS TRENDLINE (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - /// - /// Hilbert Transform Instantaneous Trendline(HTL) is a 5-period trendline of high/low price that uses signal processing to reduce noise. - /// - /// See - /// documentation - /// for more information. - /// - /// - /// - /// T must be or type - /// - /// Time-series values to transform. - /// Time series of HTL values and smoothed price. - public static IReadOnlyList GetHtTrendline( - this IEnumerable source) - where T : IReusable - => source - .ToSortedList(CandlePart.HL2) - .CalcHtTrendline(); -} diff --git a/src/e-k/HtTrendline/HtTrendline.Common.cs b/src/e-k/HtTrendline/HtTrendline.Common.cs deleted file mode 100644 index e81fee576..000000000 --- a/src/e-k/HtTrendline/HtTrendline.Common.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Skender.Stock.Indicators; - -// Htl (COMMON) - -public static class Htl -{ - // parameter validation - internal static void Validate() => throw new NotImplementedException(); - -} diff --git a/src/e-k/HtTrendline/HtTrendline.Models.cs b/src/e-k/HtTrendline/HtTrendline.Models.cs index 5e2d7a88a..42bd3aaee 100644 --- a/src/e-k/HtTrendline/HtTrendline.Models.cs +++ b/src/e-k/HtTrendline/HtTrendline.Models.cs @@ -6,7 +6,7 @@ public record HtlResult int? DcPeriods, double? Trendline, double? SmoothPrice -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Trendline.Null2NaN(); + public double Value => Trendline.Null2NaN(); } diff --git a/src/e-k/HtTrendline/HtTrendline.StaticSeries.cs b/src/e-k/HtTrendline/HtTrendline.StaticSeries.cs index a27f2b747..0788b5181 100644 --- a/src/e-k/HtTrendline/HtTrendline.StaticSeries.cs +++ b/src/e-k/HtTrendline/HtTrendline.StaticSeries.cs @@ -1,14 +1,29 @@ namespace Skender.Stock.Indicators; // HILBERT TRANSFORM - INSTANTANEOUS TRENDLINE (SERIES) -public static partial class Indicator + +public static partial class HtTrendline { - private static List CalcHtTrendline( - this List source) + // SERIES, from CHAIN + /// + /// Hilbert Transform Instantaneous Trendline(HTL) is a 5-period trendline + /// of high/low price that uses signal processing to reduce noise. + /// + /// + /// T must be type + /// + /// Time-series values to transform. + /// Time series of HTL values and smoothed price. + public static IReadOnlyList ToHtTrendline( + this IReadOnlyList source) where T : IReusable { + // prefer HL2 when IQuote + IReadOnlyList values + = source.ToPreferredList(CandlePart.HL2); + // initialize - int length = source.Count; + int length = values.Count; List results = new(length); double[] pr = new double[length]; // price @@ -31,7 +46,7 @@ private static List CalcHtTrendline( // roll through source values for (int i = 0; i < length; i++) { - IReusable s = source[i]; + IReusable s = values[i]; pr[i] = s.Value; if (i > 5) diff --git a/src/e-k/HtTrendline/HtTrendline.Utilities.cs b/src/e-k/HtTrendline/HtTrendline.Utilities.cs index b4fe28e15..6ca015ee2 100644 --- a/src/e-k/HtTrendline/HtTrendline.Utilities.cs +++ b/src/e-k/HtTrendline/HtTrendline.Utilities.cs @@ -1,9 +1,12 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// HILBERT TRANSFORM - INSTANTANEOUS TRENDLINE (UTILITIES) + +public static partial class HtTrendline { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) => results.Remove(100); + this IReadOnlyList results) + => results.Remove(100); } diff --git a/src/e-k/Hurst/Hurst.Api.cs b/src/e-k/Hurst/Hurst.Api.cs deleted file mode 100644 index 18789abed..000000000 --- a/src/e-k/Hurst/Hurst.Api.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Skender.Stock.Indicators; - -// HURST EXPONENT (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetHurst( - this IEnumerable results, - int lookbackPeriods = 100) - where T : IReusable - => results - .ToSortedList() - .CalcHurst(lookbackPeriods); -} diff --git a/src/e-k/Hurst/Hurst.Common.cs b/src/e-k/Hurst/Hurst.Common.cs deleted file mode 100644 index 523e1f2d4..000000000 --- a/src/e-k/Hurst/Hurst.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// HURST EXPONENT (COMMON) - -public static class Hurst -{ - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods < 20) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be at least 20 for Hurst Exponent."); - } - } - -} diff --git a/src/e-k/Hurst/Hurst.Models.cs b/src/e-k/Hurst/Hurst.Models.cs index 42064930d..bb5cb2aec 100644 --- a/src/e-k/Hurst/Hurst.Models.cs +++ b/src/e-k/Hurst/Hurst.Models.cs @@ -4,7 +4,7 @@ public record HurstResult ( DateTime Timestamp, double? HurstExponent -) : Reusable(Timestamp) +) : IReusable { - public override double Value => HurstExponent.Null2NaN(); + public double Value => HurstExponent.Null2NaN(); } diff --git a/src/e-k/Hurst/Hurst.StaticSeries.cs b/src/e-k/Hurst/Hurst.StaticSeries.cs index 940e25452..3b2ff650d 100644 --- a/src/e-k/Hurst/Hurst.StaticSeries.cs +++ b/src/e-k/Hurst/Hurst.StaticSeries.cs @@ -2,15 +2,16 @@ namespace Skender.Stock.Indicators; // HURST EXPONENT (SERIES) -public static partial class Indicator +public static partial class Hurst { - private static List CalcHurst( - this List source, - int lookbackPeriods) + public static IReadOnlyList ToHurst( + this IReadOnlyList source, + int lookbackPeriods = 100) where T : IReusable { // check parameter arguments - Hurst.Validate(lookbackPeriods); + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); // initialize int length = source.Count; diff --git a/src/e-k/Hurst/Hurst.Utilities.cs b/src/e-k/Hurst/Hurst.Utilities.cs index 827f95467..92fc16415 100644 --- a/src/e-k/Hurst/Hurst.Utilities.cs +++ b/src/e-k/Hurst/Hurst.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// HURST EXPONENT (UTILITIES) + +public static partial class Hurst { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -13,4 +15,16 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods < 20) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be at least 20 for Hurst Exponent."); + } + } } diff --git a/src/e-k/Ichimoku/Ichimoku.Api.cs b/src/e-k/Ichimoku/Ichimoku.Api.cs deleted file mode 100644 index d0d80c0c3..000000000 --- a/src/e-k/Ichimoku/Ichimoku.Api.cs +++ /dev/null @@ -1,56 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ICHIMOKU CLOUD (API) -public static partial class Indicator -{ - /// - /// - public static IReadOnlyList GetIchimoku( - this IEnumerable quotes, - int tenkanPeriods = 9, - int kijunPeriods = 26, - int senkouBPeriods = 52) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcIchimoku( - tenkanPeriods, - kijunPeriods, - senkouBPeriods, - kijunPeriods, - kijunPeriods); - - /// - /// - public static IReadOnlyList GetIchimoku( - this IEnumerable quotes, - int tenkanPeriods, - int kijunPeriods, - int senkouBPeriods, - int offsetPeriods) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcIchimoku( - tenkanPeriods, - kijunPeriods, - senkouBPeriods, - offsetPeriods, - offsetPeriods); - - /// - /// - public static IReadOnlyList GetIchimoku( - this IEnumerable quotes, - int tenkanPeriods, - int kijunPeriods, - int senkouBPeriods, - int senkouOffset, - int chikouOffset) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcIchimoku( - tenkanPeriods, - kijunPeriods, - senkouBPeriods, - senkouOffset, - chikouOffset); -} diff --git a/src/e-k/Ichimoku/Ichimoku.Common.cs b/src/e-k/Ichimoku/Ichimoku.Common.cs deleted file mode 100644 index 2430de584..000000000 --- a/src/e-k/Ichimoku/Ichimoku.Common.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ICHIMOKU CLOUD (COMMON) - -public static class Ichimoku -{ - internal static void Validate( - int tenkanPeriods, - int kijunPeriods, - int senkouBPeriods, - int senkouOffset, - int chikouOffset) - { - // check parameter arguments - if (tenkanPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(tenkanPeriods), tenkanPeriods, - "Tenkan periods must be greater than 0 for Ichimoku Cloud."); - } - - if (kijunPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(kijunPeriods), kijunPeriods, - "Kijun periods must be greater than 0 for Ichimoku Cloud."); - } - - if (senkouBPeriods <= kijunPeriods) - { - throw new ArgumentOutOfRangeException(nameof(senkouBPeriods), senkouBPeriods, - "Senkou B periods must be greater than Kijun periods for Ichimoku Cloud."); - } - - if (senkouOffset < 0 || chikouOffset < 0) - { - throw new ArgumentOutOfRangeException(nameof(senkouOffset), senkouOffset, - "Senkou and Chikou offset periods must be non-negative for Ichimoku Cloud."); - } - } - -} diff --git a/src/e-k/Ichimoku/Ichimoku.StaticSeries.cs b/src/e-k/Ichimoku/Ichimoku.StaticSeries.cs index da334180d..f370ff6a2 100644 --- a/src/e-k/Ichimoku/Ichimoku.StaticSeries.cs +++ b/src/e-k/Ichimoku/Ichimoku.StaticSeries.cs @@ -2,10 +2,55 @@ namespace Skender.Stock.Indicators; // ICHIMOKU CLOUD (SERIES) -public static partial class Indicator +public static partial class Ichimoku { + public static IReadOnlyList ToIchimoku( + this IReadOnlyList quotes, + int tenkanPeriods = 9, + int kijunPeriods = 26, + int senkouBPeriods = 52) + where TQuote : IQuote => quotes + .ToSortedList() + .CalcIchimoku( + tenkanPeriods, + kijunPeriods, + senkouBPeriods, + kijunPeriods, + kijunPeriods); + + public static IReadOnlyList GetIchimoku( + this IReadOnlyList quotes, + int tenkanPeriods, + int kijunPeriods, + int senkouBPeriods, + int offsetPeriods) + where TQuote : IQuote => quotes + .ToSortedList() + .CalcIchimoku( + tenkanPeriods, + kijunPeriods, + senkouBPeriods, + offsetPeriods, + offsetPeriods); + + public static IReadOnlyList GetIchimoku( + this IReadOnlyList quotes, + int tenkanPeriods, + int kijunPeriods, + int senkouBPeriods, + int senkouOffset, + int chikouOffset) + where TQuote : IQuote => quotes + .ToSortedList() + .CalcIchimoku( + tenkanPeriods, + kijunPeriods, + senkouBPeriods, + senkouOffset, + chikouOffset); + private static List CalcIchimoku( - this List quotesList, + this IReadOnlyList quotes, int tenkanPeriods, int kijunPeriods, int senkouBPeriods, @@ -14,7 +59,7 @@ private static List CalcIchimoku( where TQuote : IQuote { // check parameter arguments - Ichimoku.Validate( + Validate( tenkanPeriods, kijunPeriods, senkouBPeriods, @@ -22,7 +67,7 @@ private static List CalcIchimoku( chikouOffset); // initialize - int length = quotesList.Count; + int length = quotes.Count; List results = new(length); int senkouStartPeriod = Math.Max( @@ -32,15 +77,15 @@ private static List CalcIchimoku( // roll through source values for (int i = 0; i < length; i++) { - TQuote q = quotesList[i]; + TQuote q = quotes[i]; // tenkan-sen conversion line decimal? tenkanSen = CalcIchimokuTenkanSen( - i, quotesList, tenkanPeriods); + i, quotes, tenkanPeriods); // kijun-sen base line decimal? kijunSen = CalcIchimokuKijunSen( - i, quotesList, kijunPeriods); + i, quotes, kijunPeriods); // senkou span A decimal? senkouSpanA = null; @@ -60,14 +105,14 @@ private static List CalcIchimoku( // senkou span B decimal? senkouSpanB = CalcIchimokuSenkouB( - i, quotesList, senkouOffset, senkouBPeriods); + i, quotes, senkouOffset, senkouBPeriods); // chikou line decimal? chikouSpan = null; - if (i + chikouOffset < quotesList.Count) + if (i + chikouOffset < quotes.Count) { - chikouSpan = quotesList[i + chikouOffset].Close; + chikouSpan = quotes[i + chikouOffset].Close; } results.Add(new( @@ -83,7 +128,7 @@ private static List CalcIchimoku( } private static decimal? CalcIchimokuTenkanSen( - int i, List quotesList, int tenkanPeriods) + int i, IReadOnlyList quotes, int tenkanPeriods) where TQuote : IQuote { if (i < tenkanPeriods - 1) @@ -96,7 +141,7 @@ private static List CalcIchimoku( for (int p = i - tenkanPeriods + 1; p <= i; p++) { - TQuote d = quotesList[p]; + TQuote d = quotes[p]; if (d.High > max) { @@ -115,7 +160,7 @@ private static List CalcIchimoku( private static decimal? CalcIchimokuKijunSen( int i, - List quotesList, + IReadOnlyList quotes, int kijunPeriods) where TQuote : IQuote { @@ -129,7 +174,7 @@ private static List CalcIchimoku( for (int p = i - kijunPeriods + 1; p <= i; p++) { - TQuote d = quotesList[p]; + TQuote d = quotes[p]; if (d.High > max) { @@ -147,7 +192,7 @@ private static List CalcIchimoku( private static decimal? CalcIchimokuSenkouB( int i, - List quotesList, + IReadOnlyList quotes, int senkouOffset, int senkouBPeriods) where TQuote : IQuote @@ -163,7 +208,7 @@ private static List CalcIchimoku( for (int p = i - senkouOffset - senkouBPeriods + 1; p <= i - senkouOffset; p++) { - TQuote d = quotesList[p]; + TQuote d = quotes[p]; if (d.High > max) { diff --git a/src/e-k/Ichimoku/Ichimoku.Utilities.cs b/src/e-k/Ichimoku/Ichimoku.Utilities.cs index 0e5f7891e..5a09101db 100644 --- a/src/e-k/Ichimoku/Ichimoku.Utilities.cs +++ b/src/e-k/Ichimoku/Ichimoku.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// ICHIMOKU CLOUD (UTILITIES) + +public static partial class Ichimoku { - // CONDENSE (REMOVE null results) - /// + // remove null results + /// public static IReadOnlyList Condense( - this IEnumerable results) + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -20,4 +22,37 @@ x.TenkanSen is null return resultsList.ToSortedList(); } + + // validate parameters + internal static void Validate( + int tenkanPeriods, + int kijunPeriods, + int senkouBPeriods, + int senkouOffset, + int chikouOffset) + { + if (tenkanPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(tenkanPeriods), tenkanPeriods, + "Tenkan periods must be greater than 0 for Ichimoku Cloud."); + } + + if (kijunPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(kijunPeriods), kijunPeriods, + "Kijun periods must be greater than 0 for Ichimoku Cloud."); + } + + if (senkouBPeriods <= kijunPeriods) + { + throw new ArgumentOutOfRangeException(nameof(senkouBPeriods), senkouBPeriods, + "Senkou B periods must be greater than Kijun periods for Ichimoku Cloud."); + } + + if (senkouOffset < 0 || chikouOffset < 0) + { + throw new ArgumentOutOfRangeException(nameof(senkouOffset), senkouOffset, + "Senkou and Chikou offset periods must be non-negative for Ichimoku Cloud."); + } + } } diff --git a/src/e-k/Kama/Kama.Api.cs b/src/e-k/Kama/Kama.Api.cs deleted file mode 100644 index b499a392f..000000000 --- a/src/e-k/Kama/Kama.Api.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Skender.Stock.Indicators; - -// KAUFMAN's ADAPTIVE MOVING AVERAGE (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetKama( - this IEnumerable source, - int erPeriods = 10, - int fastPeriods = 2, - int slowPeriods = 30) - where T : IReusable - => source - .ToSortedList() - .CalcKama(erPeriods, fastPeriods, slowPeriods); -} diff --git a/src/e-k/Kama/Kama.Common.cs b/src/e-k/Kama/Kama.Common.cs deleted file mode 100644 index 7cc5bf300..000000000 --- a/src/e-k/Kama/Kama.Common.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Skender.Stock.Indicators; - -// KAUFMAN's ADAPTIVE MOVING AVERAGE (COMMON) - -public static class Kama -{ - // parameter validation - internal static void Validate( - int erPeriods, - int fastPeriods, - int slowPeriods) - { - // check parameter arguments - if (erPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(erPeriods), erPeriods, - "Efficiency Ratio periods must be greater than 0 for KAMA."); - } - - if (fastPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(fastPeriods), fastPeriods, - "Fast EMA periods must be greater than 0 for KAMA."); - } - - if (slowPeriods <= fastPeriods) - { - throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, - "Slow EMA periods must be greater than Fast EMA period for KAMA."); - } - } - -} diff --git a/src/e-k/Kama/Kama.Models.cs b/src/e-k/Kama/Kama.Models.cs index 23789bfda..74a7509ec 100644 --- a/src/e-k/Kama/Kama.Models.cs +++ b/src/e-k/Kama/Kama.Models.cs @@ -5,7 +5,7 @@ public record KamaResult DateTime Timestamp, double? Er = null, double? Kama = null -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Kama.Null2NaN(); + public double Value => Kama.Null2NaN(); } diff --git a/src/e-k/Kama/Kama.StaticSeries.cs b/src/e-k/Kama/Kama.StaticSeries.cs index 460717c9a..b321d4cbf 100644 --- a/src/e-k/Kama/Kama.StaticSeries.cs +++ b/src/e-k/Kama/Kama.StaticSeries.cs @@ -2,17 +2,18 @@ namespace Skender.Stock.Indicators; // KAUFMAN's ADAPTIVE MOVING AVERAGE (SERIES) -public static partial class Indicator +public static partial class Kama { - private static List CalcKama( - this List source, - int erPeriods, - int fastPeriods, - int slowPeriods) + public static IReadOnlyList ToKama( + this IReadOnlyList source, + int erPeriods = 10, + int fastPeriods = 2, + int slowPeriods = 30) where T : IReusable { // check parameter arguments - Kama.Validate(erPeriods, fastPeriods, slowPeriods); + ArgumentNullException.ThrowIfNull(source); + Validate(erPeriods, fastPeriods, slowPeriods); // initialize int length = source.Count; @@ -26,27 +27,22 @@ private static List CalcKama( // roll through source values for (int i = 0; i < length; i++) { - T s = source[i]; - // skip incalculable periods if (i < erPeriods - 1) { - results.Add(new(s.Timestamp)); + results.Add(new(source[i].Timestamp)); continue; } double er; double kama; - if (double.IsNaN(prevKama)) - { - er = double.NaN; - kama = s.Value; - } - else + if (results[i - 1].Kama is not null) { + double newVal = source[i].Value; + // ER period change - double change = Math.Abs(s.Value - source[i - erPeriods].Value); + double change = Math.Abs(newVal - source[i - erPeriods].Value); // volatility double sumPv = 0; @@ -64,19 +60,26 @@ private static List CalcKama( double sc = (er * (scFast - scSlow)) + scSlow; // squared later // kama calculation - kama = prevKama + (sc * sc * (s.Value - prevKama)); + kama = prevKama + (sc * sc * (newVal - prevKama)); } // handle flatline case else { er = 0; - kama = s.Value; + kama = source[i].Value; } } + // re/initialize + else + { + er = double.NaN; + kama = source[i].Value; + } + results.Add(new KamaResult( - Timestamp: s.Timestamp, + Timestamp: source[i].Timestamp, Er: er.NaN2Null(), Kama: kama.NaN2Null())); diff --git a/src/e-k/Kama/Kama.Utilities.cs b/src/e-k/Kama/Kama.Utilities.cs index bb7cc3086..93af9a74a 100644 --- a/src/e-k/Kama/Kama.Utilities.cs +++ b/src/e-k/Kama/Kama.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// KAUFMAN's ADAPTIVE MOVING AVERAGE (UTILITIES) + +public static partial class Kama { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int erPeriods = results .ToList() @@ -13,4 +15,30 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(Math.Max(erPeriods + 100, 10 * erPeriods)); } + + // parameter validation + internal static void Validate( + int erPeriods, + int fastPeriods, + int slowPeriods) + { + // check parameter arguments + if (erPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(erPeriods), erPeriods, + "Efficiency Ratio periods must be greater than 0 for KAMA."); + } + + if (fastPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(fastPeriods), fastPeriods, + "Fast EMA periods must be greater than 0 for KAMA."); + } + + if (slowPeriods <= fastPeriods) + { + throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, + "Slow EMA periods must be greater than Fast EMA period for KAMA."); + } + } } diff --git a/src/e-k/Keltner/Keltner.Api.cs b/src/e-k/Keltner/Keltner.Api.cs deleted file mode 100644 index 7bffd9e49..000000000 --- a/src/e-k/Keltner/Keltner.Api.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Skender.Stock.Indicators; - -// KELTNER CHANNELS (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetKeltner( - this IEnumerable quotes, - int emaPeriods = 20, - double multiplier = 2, - int atrPeriods = 10) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcKeltner(emaPeriods, multiplier, atrPeriods); -} diff --git a/src/e-k/Keltner/Keltner.Common.cs b/src/e-k/Keltner/Keltner.Common.cs deleted file mode 100644 index 3238ee6d5..000000000 --- a/src/e-k/Keltner/Keltner.Common.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Skender.Stock.Indicators; - -// KELTNER CHANNELS (COMMON) - -public static class Keltner -{ - // parameter validation - internal static void Validate( - int emaPeriods, - double multiplier, - int atrPeriods) - { - // check parameter arguments - if (emaPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(emaPeriods), emaPeriods, - "EMA periods must be greater than 1 for Keltner Channel."); - } - - if (atrPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(atrPeriods), atrPeriods, - "ATR periods must be greater than 1 for Keltner Channel."); - } - - if (multiplier <= 0) - { - throw new ArgumentOutOfRangeException(nameof(multiplier), multiplier, - "Multiplier must be greater than 0 for Keltner Channel."); - } - } - -} diff --git a/src/e-k/Keltner/Keltner.StaticSeries.cs b/src/e-k/Keltner/Keltner.StaticSeries.cs index 922d44401..baa1ba676 100644 --- a/src/e-k/Keltner/Keltner.StaticSeries.cs +++ b/src/e-k/Keltner/Keltner.StaticSeries.cs @@ -2,23 +2,32 @@ namespace Skender.Stock.Indicators; // KELTNER CHANNELS (SERIES) -public static partial class Indicator +public static partial class Keltner { + public static IReadOnlyList ToKeltner( + this IReadOnlyList quotes, + int emaPeriods = 20, + double multiplier = 2, + int atrPeriods = 10) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcKeltner(emaPeriods, multiplier, atrPeriods); + private static List CalcKeltner( - this List source, + this IReadOnlyList source, int emaPeriods, double multiplier, int atrPeriods) { // check parameter arguments - Keltner.Validate(emaPeriods, multiplier, atrPeriods); + Validate(emaPeriods, multiplier, atrPeriods); // initialize int length = source.Count; List results = new(length); IReadOnlyList emaResults - = source.CalcEma(emaPeriods); + = source.ToEma(emaPeriods); IReadOnlyList atrResults = source.CalcAtr(atrPeriods); diff --git a/src/e-k/Keltner/Keltner.Utilities.cs b/src/e-k/Keltner/Keltner.Utilities.cs index b97b5e68e..992bf4f5e 100644 --- a/src/e-k/Keltner/Keltner.Utilities.cs +++ b/src/e-k/Keltner/Keltner.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// KELTNER CHANNELS (UTILITIES) + +public static partial class Keltner { // CONDENSE (REMOVE null results) - /// + /// public static IReadOnlyList Condense( - this IEnumerable results) + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -18,9 +20,9 @@ public static IReadOnlyList Condense( } // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int n = results .ToList() @@ -28,4 +30,30 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(Math.Max(2 * n, n + 100)); } + + // parameter validation + internal static void Validate( + int emaPeriods, + double multiplier, + int atrPeriods) + { + // check parameter arguments + if (emaPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(emaPeriods), emaPeriods, + "EMA periods must be greater than 1 for Keltner Channel."); + } + + if (atrPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(atrPeriods), atrPeriods, + "ATR periods must be greater than 1 for Keltner Channel."); + } + + if (multiplier <= 0) + { + throw new ArgumentOutOfRangeException(nameof(multiplier), multiplier, + "Multiplier must be greater than 0 for Keltner Channel."); + } + } } diff --git a/src/e-k/Kvo/Kvo.Api.cs b/src/e-k/Kvo/Kvo.Api.cs deleted file mode 100644 index b00bc2768..000000000 --- a/src/e-k/Kvo/Kvo.Api.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Skender.Stock.Indicators; - -// KLINGER VOLUME OSCILLATOR (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetKvo( - this IEnumerable quotes, - int fastPeriods = 34, - int slowPeriods = 55, - int signalPeriods = 13) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcKvo(fastPeriods, slowPeriods, signalPeriods); -} diff --git a/src/e-k/Kvo/Kvo.Common.cs b/src/e-k/Kvo/Kvo.Common.cs deleted file mode 100644 index 74ca3645e..000000000 --- a/src/e-k/Kvo/Kvo.Common.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Skender.Stock.Indicators; - -// KLINGER VOLUME OSCILLATOR (COMMON) - -public static class Kvo -{ - // parameter validation - internal static void Validate( - int fastPeriods, - int slowPeriods, - int signalPeriods) - { - // check parameter arguments - if (fastPeriods <= 2) - { - throw new ArgumentOutOfRangeException(nameof(fastPeriods), fastPeriods, - "Fast (short) Periods must be greater than 2 for Klinger Oscillator."); - } - - if (slowPeriods <= fastPeriods) - { - throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, - "Slow (long) Periods must be greater than Fast Periods for Klinger Oscillator."); - } - - if (signalPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, - "Signal Periods must be greater than 0 for Klinger Oscillator."); - } - } - -} diff --git a/src/e-k/Kvo/Kvo.Models.cs b/src/e-k/Kvo/Kvo.Models.cs index 2a9bdeed1..259119208 100644 --- a/src/e-k/Kvo/Kvo.Models.cs +++ b/src/e-k/Kvo/Kvo.Models.cs @@ -5,7 +5,7 @@ public record KvoResult DateTime Timestamp, double? Oscillator = null, double? Signal = null -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Oscillator.Null2NaN(); + public double Value => Oscillator.Null2NaN(); } diff --git a/src/e-k/Kvo/Kvo.StaticSeries.cs b/src/e-k/Kvo/Kvo.StaticSeries.cs index 94af60d03..1b735fe2d 100644 --- a/src/e-k/Kvo/Kvo.StaticSeries.cs +++ b/src/e-k/Kvo/Kvo.StaticSeries.cs @@ -2,16 +2,25 @@ namespace Skender.Stock.Indicators; // KLINGER VOLUME OSCILLATOR (SERIES) -public static partial class Indicator +public static partial class Kvo { + public static IReadOnlyList ToKvo( + this IReadOnlyList quotes, + int fastPeriods = 34, + int slowPeriods = 55, + int signalPeriods = 13) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcKvo(fastPeriods, slowPeriods, signalPeriods); + private static List CalcKvo( - this List source, + this IReadOnlyList source, int fastPeriods, int slowPeriods, int signalPeriods) { // check parameter arguments - Kvo.Validate(fastPeriods, slowPeriods, signalPeriods); + Validate(fastPeriods, slowPeriods, signalPeriods); // initialize int length = source.Count; diff --git a/src/e-k/Kvo/Kvo.Utilities.cs b/src/e-k/Kvo/Kvo.Utilities.cs index b57303ac8..022893924 100644 --- a/src/e-k/Kvo/Kvo.Utilities.cs +++ b/src/e-k/Kvo/Kvo.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// KLINGER VOLUME OSCILLATOR (UTILIITES) + +public static partial class Kvo { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int l = results .ToList() @@ -13,4 +15,30 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(l + 150); } + + // parameter validation + internal static void Validate( + int fastPeriods, + int slowPeriods, + int signalPeriods) + { + // check parameter arguments + if (fastPeriods <= 2) + { + throw new ArgumentOutOfRangeException(nameof(fastPeriods), fastPeriods, + "Fast (short) Periods must be greater than 2 for Klinger Oscillator."); + } + + if (slowPeriods <= fastPeriods) + { + throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, + "Slow (long) Periods must be greater than Fast Periods for Klinger Oscillator."); + } + + if (signalPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, + "Signal Periods must be greater than 0 for Klinger Oscillator."); + } + } } diff --git a/src/m-r/MaEnvelopes/MaEnvelopes.Api.cs b/src/m-r/MaEnvelopes/MaEnvelopes.Api.cs deleted file mode 100644 index 477c7006c..000000000 --- a/src/m-r/MaEnvelopes/MaEnvelopes.Api.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Skender.Stock.Indicators; - -// MOVING AVERAGE ENVELOPES (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetMaEnvelopes( - this IEnumerable results, - int lookbackPeriods, - double percentOffset = 2.5, - MaType movingAverageType = MaType.SMA) - where T : IReusable - => results - .ToSortedList() - .CalcMaEnvelopes(lookbackPeriods, percentOffset, movingAverageType); -} diff --git a/src/m-r/MaEnvelopes/MaEnvelopes.Common.cs b/src/m-r/MaEnvelopes/MaEnvelopes.Common.cs deleted file mode 100644 index c894dc624..000000000 --- a/src/m-r/MaEnvelopes/MaEnvelopes.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// MOVING AVERAGE ENVELOPES (COMMON) - -public static class MaEnvelopes -{ - // parameter validation - internal static void Validate( - double percentOffset) - { - // check parameter arguments - if (percentOffset <= 0) - { - throw new ArgumentOutOfRangeException(nameof(percentOffset), percentOffset, - "Percent Offset must be greater than 0 for Moving Average Envelopes."); - } - } - -} diff --git a/src/m-r/MaEnvelopes/MaEnvelopes.StaticSeries.cs b/src/m-r/MaEnvelopes/MaEnvelopes.StaticSeries.cs index cf9b7257b..52942bbbd 100644 --- a/src/m-r/MaEnvelopes/MaEnvelopes.StaticSeries.cs +++ b/src/m-r/MaEnvelopes/MaEnvelopes.StaticSeries.cs @@ -1,20 +1,22 @@ +using System.Globalization; + namespace Skender.Stock.Indicators; // MOVING AVERAGE ENVELOPES (SERIES) -public static partial class Indicator +public static partial class MaEnvelopes { // calculate series - private static List CalcMaEnvelopes( - this List source, + public static IReadOnlyList ToMaEnvelopes( + this IReadOnlyList source, int lookbackPeriods, - double percentOffset, - MaType movingAverageType) + double percentOffset = 2.5, + MaType movingAverageType = MaType.SMA) where T : IReusable { // check parameter arguments // note: most validations are done in variant methods - MaEnvelopes.Validate(percentOffset); + Validate(percentOffset); // initialize double offsetRatio = percentOffset / 100d; @@ -35,7 +37,7 @@ private static List CalcMaEnvelopes( _ => throw new ArgumentOutOfRangeException( nameof(movingAverageType), movingAverageType, string.Format( - invCulture, + CultureInfo.InvariantCulture, "Moving Average Envelopes does not support {0}.", Enum.GetName(typeof(MaType), movingAverageType))) }; @@ -44,11 +46,11 @@ private static List CalcMaEnvelopes( } private static IEnumerable MaEnvAlma( - this List source, + this IReadOnlyList source, int lookbackPeriods, double offsetRatio) where T : IReusable - => source.CalcAlma(lookbackPeriods, offset: 0.85, sigma: 6) + => source.ToAlma(lookbackPeriods, offset: 0.85, sigma: 6) .Select(x => new MaEnvelopeResult( Timestamp: x.Timestamp, Centerline: x.Alma, @@ -56,11 +58,11 @@ private static IEnumerable MaEnvAlma( LowerEnvelope: x.Alma - (x.Alma * offsetRatio))); private static IEnumerable MaEnvDema( - this List source, + this IReadOnlyList source, int lookbackPeriods, double offsetRatio) where T : IReusable - => source.CalcDema(lookbackPeriods) + => source.ToDema(lookbackPeriods) .Select(x => new MaEnvelopeResult( Timestamp: x.Timestamp, Centerline: x.Dema, @@ -68,11 +70,11 @@ private static IEnumerable MaEnvDema( LowerEnvelope: x.Dema - (x.Dema * offsetRatio))); private static IEnumerable MaEnvEma( - this List source, + this IReadOnlyList source, int lookbackPeriods, double offsetRatio) where T : IReusable - => source.CalcEma(lookbackPeriods) + => source.ToEma(lookbackPeriods) .Select(x => new MaEnvelopeResult( Timestamp: x.Timestamp, Centerline: x.Ema, @@ -80,11 +82,11 @@ private static IEnumerable MaEnvEma( LowerEnvelope: x.Ema - (x.Ema * offsetRatio))); private static IEnumerable MaEnvEpma( - this List source, + this IReadOnlyList source, int lookbackPeriods, double offsetRatio) where T : IReusable - => source.CalcEpma(lookbackPeriods) + => source.ToEpma(lookbackPeriods) .Select(x => new MaEnvelopeResult( Timestamp: x.Timestamp, Centerline: x.Epma, @@ -92,11 +94,11 @@ private static IEnumerable MaEnvEpma( LowerEnvelope: x.Epma - (x.Epma * offsetRatio))); private static IEnumerable MaEnvHma( - this List source, + this IReadOnlyList source, int lookbackPeriods, double offsetRatio) where T : IReusable - => source.CalcHma(lookbackPeriods) + => source.ToHma(lookbackPeriods) .Select(x => new MaEnvelopeResult( Timestamp: x.Timestamp, Centerline: x.Hma, @@ -104,11 +106,11 @@ private static IEnumerable MaEnvHma( LowerEnvelope: x.Hma - (x.Hma * offsetRatio))); private static IEnumerable MaEnvSma( - this List source, + this IReadOnlyList source, int lookbackPeriods, double offsetRatio) where T : IReusable - => source.CalcSma(lookbackPeriods) + => source.ToSma(lookbackPeriods) .Select(x => new MaEnvelopeResult( Timestamp: x.Timestamp, Centerline: x.Sma, @@ -116,11 +118,11 @@ private static IEnumerable MaEnvSma( LowerEnvelope: x.Sma - (x.Sma * offsetRatio))); private static IEnumerable MaEnvSmma( - this List source, + this IReadOnlyList source, int lookbackPeriods, double offsetRatio) where T : IReusable - => source.CalcSmma(lookbackPeriods) + => source.ToSmma(lookbackPeriods) .Select(x => new MaEnvelopeResult( Timestamp: x.Timestamp, Centerline: x.Smma, @@ -128,11 +130,11 @@ private static IEnumerable MaEnvSmma( LowerEnvelope: x.Smma - (x.Smma * offsetRatio))); private static IEnumerable MaEnvTema( - this List source, + this IReadOnlyList source, int lookbackPeriods, double offsetRatio) where T : IReusable - => source.CalcTema(lookbackPeriods) + => source.ToTema(lookbackPeriods) .Select(x => new MaEnvelopeResult( Timestamp: x.Timestamp, Centerline: x.Tema, @@ -140,11 +142,11 @@ private static IEnumerable MaEnvTema( LowerEnvelope: x.Tema - (x.Tema * offsetRatio))); private static IEnumerable MaEnvWma( - this List source, + this IReadOnlyList source, int lookbackPeriods, double offsetRatio) where T : IReusable - => source.CalcWma(lookbackPeriods) + => source.ToWma(lookbackPeriods) .Select(x => new MaEnvelopeResult( Timestamp: x.Timestamp, Centerline: x.Wma, diff --git a/src/m-r/MaEnvelopes/MaEnvelopes.Utilities.cs b/src/m-r/MaEnvelopes/MaEnvelopes.Utilities.cs index 55937c355..07b314c46 100644 --- a/src/m-r/MaEnvelopes/MaEnvelopes.Utilities.cs +++ b/src/m-r/MaEnvelopes/MaEnvelopes.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// MOVING AVERAGE ENVELOPES (UTILITIES) + +public static partial class MaEnvelopes { // CONDENSE (REMOVE null results) - /// + /// public static IReadOnlyList Condense( - this IEnumerable results) + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -16,4 +18,16 @@ public static IReadOnlyList Condense( return resultsList.ToSortedList(); } + + // parameter validation + internal static void Validate( + double percentOffset) + { + // check parameter arguments + if (percentOffset <= 0) + { + throw new ArgumentOutOfRangeException(nameof(percentOffset), percentOffset, + "Percent Offset must be greater than 0 for Moving Average Envelopes."); + } + } } diff --git a/src/m-r/Macd/Macd.Common.cs b/src/m-r/Macd/Macd.Common.cs deleted file mode 100644 index f01a1a63e..000000000 --- a/src/m-r/Macd/Macd.Common.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Skender.Stock.Indicators; - -// MOVING AVERAGE CONVERGENCE/DIVERGENCE (MACD) OSCILLATOR (COMMON) - -public static class Macd -{ - // parameter validation - internal static void Validate( - int fastPeriods, - int slowPeriods, - int signalPeriods) - { - // check parameter arguments - if (fastPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(fastPeriods), fastPeriods, - "Fast periods must be greater than 0 for MACD."); - } - - if (signalPeriods < 0) - { - throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, - "Signal periods must be greater than or equal to 0 for MACD."); - } - - if (slowPeriods <= fastPeriods) - { - throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, - "Slow periods must be greater than the fast period for MACD."); - } - } - -} diff --git a/src/m-r/Macd/Macd.Models.cs b/src/m-r/Macd/Macd.Models.cs index ca62b0997..c6b48ff1e 100644 --- a/src/m-r/Macd/Macd.Models.cs +++ b/src/m-r/Macd/Macd.Models.cs @@ -7,11 +7,11 @@ public record MacdResult double? Signal, double? Histogram, - // extra interim data + // extra/interim data double? FastEma, double? SlowEma -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Macd.Null2NaN(); + public double Value => Macd.Null2NaN(); } diff --git a/src/m-r/Macd/Macd.StaticSeries.cs b/src/m-r/Macd/Macd.StaticSeries.cs index e9ee2a55e..45c4628d6 100644 --- a/src/m-r/Macd/Macd.StaticSeries.cs +++ b/src/m-r/Macd/Macd.StaticSeries.cs @@ -2,17 +2,18 @@ namespace Skender.Stock.Indicators; // MOVING AVERAGE CONVERGENCE/DIVERGENCE (MACD) OSCILLATOR (SERIES) -public static partial class Indicator +public static partial class Macd { - private static List CalcMacd( - this List source, - int fastPeriods, - int slowPeriods, - int signalPeriods) + public static IReadOnlyList ToMacd( + this IReadOnlyList source, + int fastPeriods = 12, + int slowPeriods = 26, + int signalPeriods = 9) where T : IReusable { // check parameter arguments - Macd.Validate(fastPeriods, slowPeriods, signalPeriods); + ArgumentNullException.ThrowIfNull(source); + Validate(fastPeriods, slowPeriods, signalPeriods); // initialize int length = source.Count; @@ -29,57 +30,30 @@ private static List CalcMacd( // roll through source values for (int i = 0; i < length; i++) { - T s = source[i]; - - // re-initialize Fast EMA - double emaFast; - - if (double.IsNaN(lastEmaFast) && i >= fastPeriods - 1) - { - double sum = 0; - for (int p = i - fastPeriods + 1; p <= i; p++) - { - T ps = source[p]; - sum += ps.Value; - } - - emaFast = sum / fastPeriods; - } - else - { - emaFast = Ema.Increment(kFast, lastEmaFast, s.Value); - } - - // re-initialize Slow EMA - double emaSlow; - - if (double.IsNaN(lastEmaSlow) && i >= slowPeriods - 1) - { - double sum = 0; - for (int p = i - slowPeriods + 1; p <= i; p++) - { - T ps = source[p]; - sum += ps.Value; - } - - emaSlow = sum / slowPeriods; - } - else - { - emaSlow = Ema.Increment(kSlow, lastEmaSlow, s.Value); - } - + // Fast EMA + double emaFast + = i >= fastPeriods - 1 && results[i - 1].FastEma is null + ? Sma.Increment(source, fastPeriods, i) + : Ema.Increment(kFast, lastEmaFast, source[i].Value); + + // Slow EMA + double emaSlow + = i >= slowPeriods - 1 && results[i - 1].SlowEma is null + ? Sma.Increment(source, slowPeriods, i) + : Ema.Increment(kSlow, lastEmaSlow, source[i].Value); + + // MACD double macd = emaFast - emaSlow; - // re-initialize Signal EMA + // Signal double signal; - if (double.IsNaN(lastEmaMacd) && i >= signalPeriods + slowPeriods - 2) + if (i >= signalPeriods + slowPeriods - 2 && results[i - 1].Signal is null) { double sum = macd; for (int p = i - signalPeriods + 1; p < i; p++) { - sum += ((IReusable)results[p]).Value; + sum += results[p].Value; } signal = sum / signalPeriods; @@ -89,9 +63,9 @@ private static List CalcMacd( signal = Ema.Increment(kMacd, lastEmaMacd, macd); } - // write results - results.Add(new( - Timestamp: s.Timestamp, + // results + results.Add(new MacdResult( + Timestamp: source[i].Timestamp, Macd: macd.NaN2Null(), Signal: signal.NaN2Null(), Histogram: (macd - signal).NaN2Null(), diff --git a/src/m-r/Macd/Macd.Utilities.cs b/src/m-r/Macd/Macd.Utilities.cs index 53c0a5227..4326aac2d 100644 --- a/src/m-r/Macd/Macd.Utilities.cs +++ b/src/m-r/Macd/Macd.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// MOVING AVERAGE CONVERGENCE/DIVERGENCE (MACD) OSCILLATOR (UTILITIES) + +public static partial class Macd { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int n = results .ToList() @@ -13,4 +15,30 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(n + 250); } + + // parameter validation + internal static void Validate( + int fastPeriods, + int slowPeriods, + int signalPeriods) + { + // check parameter arguments + if (fastPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(fastPeriods), fastPeriods, + "Fast periods must be greater than 0 for MACD."); + } + + if (signalPeriods < 0) + { + throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, + "Signal periods must be greater than or equal to 0 for MACD."); + } + + if (slowPeriods <= fastPeriods) + { + throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, + "Slow periods must be greater than the fast period for MACD."); + } + } } diff --git a/src/m-r/Macd/MacdApi.cs b/src/m-r/Macd/MacdApi.cs deleted file mode 100644 index db9759211..000000000 --- a/src/m-r/Macd/MacdApi.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Skender.Stock.Indicators; - -// MOVING AVERAGE CONVERGENCE/DIVERGENCE (MACD) OSCILLATOR (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetMacd( - this IEnumerable results, - int fastPeriods = 12, - int slowPeriods = 26, - int signalPeriods = 9) - where T : IReusable - => results - .ToSortedList() - .CalcMacd(fastPeriods, slowPeriods, signalPeriods); -} diff --git a/src/m-r/Mama/Mama.Api.cs b/src/m-r/Mama/Mama.Api.cs deleted file mode 100644 index 5b4d98e0a..000000000 --- a/src/m-r/Mama/Mama.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// MOTHER of ADAPTIVE MOVING AVERAGES - MAMA (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetMama( - this IEnumerable results, - double fastLimit = 0.5, - double slowLimit = 0.05) - where T : IReusable - => results - .ToSortedList(CandlePart.HL2) - .CalcMama(fastLimit, slowLimit); -} diff --git a/src/m-r/Mama/Mama.Common.cs b/src/m-r/Mama/Mama.Common.cs deleted file mode 100644 index 9fca3e44f..000000000 --- a/src/m-r/Mama/Mama.Common.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Skender.Stock.Indicators; - -// MOTHER of ADAPTIVE MOVING AVERAGES (COMMON) - -public static class Mama -{ - // parameter validation - internal static void Validate( - double fastLimit, - double slowLimit) - { - // check parameter arguments - if (fastLimit <= slowLimit || fastLimit >= 1) - { - throw new ArgumentOutOfRangeException(nameof(fastLimit), fastLimit, - "Fast Limit must be greater than Slow Limit and less than 1 for MAMA."); - } - - if (slowLimit <= 0) - { - throw new ArgumentOutOfRangeException(nameof(slowLimit), slowLimit, - "Slow Limit must be greater than 0 for MAMA."); - } - } - -} diff --git a/src/m-r/Mama/Mama.Models.cs b/src/m-r/Mama/Mama.Models.cs index 44ef0bb24..789019c78 100644 --- a/src/m-r/Mama/Mama.Models.cs +++ b/src/m-r/Mama/Mama.Models.cs @@ -5,7 +5,7 @@ public record MamaResult DateTime Timestamp, double? Mama = null, double? Fama = null -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Mama.Null2NaN(); + public double Value => Mama.Null2NaN(); } diff --git a/src/m-r/Mama/Mama.StaticSeries.cs b/src/m-r/Mama/Mama.StaticSeries.cs index 0aa73ce54..3110c5702 100644 --- a/src/m-r/Mama/Mama.StaticSeries.cs +++ b/src/m-r/Mama/Mama.StaticSeries.cs @@ -1,19 +1,24 @@ namespace Skender.Stock.Indicators; // MOTHER of ADAPTIVE MOVING AVERAGES (SERIES) -public static partial class Indicator + +public static partial class Mama { - private static List CalcMama( - this List source, - double fastLimit, - double slowLimit) + public static IReadOnlyList ToMama( + this IReadOnlyList source, + double fastLimit = 0.5, + double slowLimit = 0.05) where T : IReusable { // check parameter arguments - Mama.Validate(fastLimit, slowLimit); + Validate(fastLimit, slowLimit); + + // prefer HL2 when IQuote + IReadOnlyList values + = source.ToPreferredList(CandlePart.HL2); // initialize - int length = source.Count; + int length = values.Count; List results = new(length); double prevMama = double.NaN; @@ -38,7 +43,7 @@ private static List CalcMama( // roll through source values for (int i = 0; i < length; i++) { - IReusable s = source[i]; + IReusable s = values[i]; pr[i] = s.Value; // skip incalculable periods diff --git a/src/m-r/Mama/Mama.Utilities.cs b/src/m-r/Mama/Mama.Utilities.cs index 3789e8d72..2b9a21126 100644 --- a/src/m-r/Mama/Mama.Utilities.cs +++ b/src/m-r/Mama/Mama.Utilities.cs @@ -1,9 +1,30 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// MOTHER of ADAPTIVE MOVING AVERAGES (UTILITIES) + +public static partial class Mama { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) => results.Remove(50); + this IReadOnlyList results) => results.Remove(50); + + // parameter validation + internal static void Validate( + double fastLimit, + double slowLimit) + { + // check parameter arguments + if (fastLimit <= slowLimit || fastLimit >= 1) + { + throw new ArgumentOutOfRangeException(nameof(fastLimit), fastLimit, + "Fast Limit must be greater than Slow Limit and less than 1 for MAMA."); + } + + if (slowLimit <= 0) + { + throw new ArgumentOutOfRangeException(nameof(slowLimit), slowLimit, + "Slow Limit must be greater than 0 for MAMA."); + } + } } diff --git a/src/m-r/Marubozu/Marubozu.Api.cs b/src/m-r/Marubozu/Marubozu.Api.cs deleted file mode 100644 index b0315cc7c..000000000 --- a/src/m-r/Marubozu/Marubozu.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// MARUBOZU (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetMarubozu( - this IEnumerable quotes, - double minBodyPercent = 95) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcMarubozu(minBodyPercent); -} diff --git a/src/m-r/Marubozu/Marubozu.StaticSeries.cs b/src/m-r/Marubozu/Marubozu.StaticSeries.cs index 59f3ca219..6cf34e122 100644 --- a/src/m-r/Marubozu/Marubozu.StaticSeries.cs +++ b/src/m-r/Marubozu/Marubozu.StaticSeries.cs @@ -2,18 +2,19 @@ namespace Skender.Stock.Indicators; // MARUBOZU (SERIES) -public static partial class Indicator +public static partial class Marubozu { - private static List CalcMarubozu( - this List quotesList, - double minBodyPercent) + public static IReadOnlyList ToMarubozu( + this IReadOnlyList quotes, + double minBodyPercent = 95) where TQuote : IQuote { // check parameter arguments - Marubozu.Validate(minBodyPercent); + ArgumentNullException.ThrowIfNull(quotes); + Validate(minBodyPercent); // initialize - int length = quotesList.Count; + int length = quotes.Count; List results = new(length); minBodyPercent /= 100; @@ -21,7 +22,7 @@ private static List CalcMarubozu( // roll through candles for (int i = 0; i < length; i++) { - TQuote q = quotesList[i]; + TQuote q = quotes[i]; decimal? matchPrice = null; Match matchType = Match.None; CandleProperties candle = q.ToCandle(); diff --git a/src/m-r/Marubozu/Marubozu.Common.cs b/src/m-r/Marubozu/Marubozu.Utilities.cs similarity index 92% rename from src/m-r/Marubozu/Marubozu.Common.cs rename to src/m-r/Marubozu/Marubozu.Utilities.cs index 1b882334c..7d859c14c 100644 --- a/src/m-r/Marubozu/Marubozu.Common.cs +++ b/src/m-r/Marubozu/Marubozu.Utilities.cs @@ -1,8 +1,8 @@ namespace Skender.Stock.Indicators; -// MARUBOZU (COMMON) +// MARUBOZU (UTILITIES) -public static class Marubozu +public static partial class Marubozu { // parameter validation internal static void Validate( @@ -25,5 +25,4 @@ internal static void Validate( "for Marubozu and is usually greater than 90 (90%)."); } } - } diff --git a/src/m-r/Mfi/Mfi.Api.cs b/src/m-r/Mfi/Mfi.Api.cs deleted file mode 100644 index 6dc4d0bab..000000000 --- a/src/m-r/Mfi/Mfi.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// MONEY FLOW INDEX (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetMfi( - this IEnumerable quotes, - int lookbackPeriods = 14) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcMfi(lookbackPeriods); -} diff --git a/src/m-r/Mfi/Mfi.Common.cs b/src/m-r/Mfi/Mfi.Common.cs deleted file mode 100644 index b1f6b8ad5..000000000 --- a/src/m-r/Mfi/Mfi.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// MONEY FLOW INDEX (COMMON) - -public static class Mfi -{ - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 1 for MFI."); - } - } - -} diff --git a/src/m-r/Mfi/Mfi.Models.cs b/src/m-r/Mfi/Mfi.Models.cs index 601598009..3fed3c72d 100644 --- a/src/m-r/Mfi/Mfi.Models.cs +++ b/src/m-r/Mfi/Mfi.Models.cs @@ -4,7 +4,7 @@ public record MfiResult ( DateTime Timestamp, double? Mfi -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Mfi.Null2NaN(); + public double Value => Mfi.Null2NaN(); } diff --git a/src/m-r/Mfi/Mfi.StaticSeries.cs b/src/m-r/Mfi/Mfi.StaticSeries.cs index e6d434aea..2d9043a7c 100644 --- a/src/m-r/Mfi/Mfi.StaticSeries.cs +++ b/src/m-r/Mfi/Mfi.StaticSeries.cs @@ -2,14 +2,21 @@ namespace Skender.Stock.Indicators; // MONEY FLOW INDEX (SERIES) -public static partial class Indicator +public static partial class Mfi { + public static IReadOnlyList ToMfi( + this IReadOnlyList quotes, + int lookbackPeriods = 14) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcMfi(lookbackPeriods); + private static List CalcMfi( - this List source, + this IReadOnlyList source, int lookbackPeriods) { // check parameter arguments - Mfi.Validate(lookbackPeriods); + Validate(lookbackPeriods); // initialize int length = source.Count; diff --git a/src/m-r/Mfi/Mfi.Utilities.cs b/src/m-r/Mfi/Mfi.Utilities.cs index 07a1ba3c9..e4768b450 100644 --- a/src/m-r/Mfi/Mfi.Utilities.cs +++ b/src/m-r/Mfi/Mfi.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// MONEY FLOW INDEX (UTILITIES) + +public static partial class Mfi { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -13,4 +15,16 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 1 for MFI."); + } + } } diff --git a/src/m-r/Obv/Obv.Api.cs b/src/m-r/Obv/Obv.Api.cs deleted file mode 100644 index 8eefcd0b8..000000000 --- a/src/m-r/Obv/Obv.Api.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ON-BALANCE VOLUME (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetObv( - this IEnumerable quotes) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcObv(); -} diff --git a/src/m-r/Obv/Obv.Models.cs b/src/m-r/Obv/Obv.Models.cs index 74f25129f..fdb191089 100644 --- a/src/m-r/Obv/Obv.Models.cs +++ b/src/m-r/Obv/Obv.Models.cs @@ -4,7 +4,7 @@ public record ObvResult ( DateTime Timestamp, double Obv -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Obv; + public double Value => Obv; } diff --git a/src/m-r/Obv/Obv.StaticSeries.cs b/src/m-r/Obv/Obv.StaticSeries.cs index 9c9754eec..48456b11d 100644 --- a/src/m-r/Obv/Obv.StaticSeries.cs +++ b/src/m-r/Obv/Obv.StaticSeries.cs @@ -2,10 +2,16 @@ namespace Skender.Stock.Indicators; // ON-BALANCE VOLUME (SERIES) -public static partial class Indicator +public static partial class Obv { + public static IReadOnlyList ToObv( + this IReadOnlyList quotes) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcObv(); + private static List CalcObv( - this List source) + this IReadOnlyList source) { // initialize int length = source.Count; diff --git a/src/m-r/ParabolicSar/ParabolicSar.Api.cs b/src/m-r/ParabolicSar/ParabolicSar.Api.cs deleted file mode 100644 index 56da5dbdd..000000000 --- a/src/m-r/ParabolicSar/ParabolicSar.Api.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace Skender.Stock.Indicators; - -// PARABOLIC SAR (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetParabolicSar( - this IEnumerable quotes, - double accelerationStep = 0.02, - double maxAccelerationFactor = 0.2) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcParabolicSar( - accelerationStep, - maxAccelerationFactor, - accelerationStep); - - // SERIES, from TQuote (alt) - /// - /// - public static IReadOnlyList GetParabolicSar( - this IEnumerable quotes, - double accelerationStep, - double maxAccelerationFactor, - double initialFactor) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcParabolicSar( - accelerationStep, - maxAccelerationFactor, - initialFactor); -} diff --git a/src/m-r/ParabolicSar/ParabolicSar.Common.cs b/src/m-r/ParabolicSar/ParabolicSar.Common.cs deleted file mode 100644 index e6a51ab8a..000000000 --- a/src/m-r/ParabolicSar/ParabolicSar.Common.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Globalization; - -namespace Skender.Stock.Indicators; - -// PARABOLIC SAR (COMMON) - -public static class ParabolicSar -{ - private static readonly CultureInfo EnglishCulture = new("en-US", false); - - // parameter validation - internal static void Validate( - double accelerationStep, - double maxAccelerationFactor, - double initialFactor) - { - // check parameter arguments - if (accelerationStep <= 0) - { - throw new ArgumentOutOfRangeException(nameof(accelerationStep), accelerationStep, - "Acceleration Step must be greater than 0 for Parabolic SAR."); - } - - if (maxAccelerationFactor <= 0) - { - throw new ArgumentOutOfRangeException(nameof(maxAccelerationFactor), maxAccelerationFactor, - "Max Acceleration Factor must be greater than 0 for Parabolic SAR."); - } - - if (accelerationStep > maxAccelerationFactor) - { - string message = string.Format( - EnglishCulture, - "Acceleration Step cannot be larger than the Max Acceleration Factor ({0}) for Parabolic SAR.", - maxAccelerationFactor); - - throw new ArgumentOutOfRangeException(nameof(accelerationStep), accelerationStep, message); - } - - if (initialFactor <= 0 || initialFactor > maxAccelerationFactor) - { - throw new ArgumentOutOfRangeException(nameof(initialFactor), initialFactor, - "Initial Factor must be greater than 0 and not larger than Max Acceleration Factor for Parabolic SAR."); - } - } - -} diff --git a/src/m-r/ParabolicSar/ParabolicSar.Models.cs b/src/m-r/ParabolicSar/ParabolicSar.Models.cs index 12dadd669..1c49a0538 100644 --- a/src/m-r/ParabolicSar/ParabolicSar.Models.cs +++ b/src/m-r/ParabolicSar/ParabolicSar.Models.cs @@ -5,7 +5,7 @@ public record ParabolicSarResult DateTime Timestamp, double? Sar = null, bool? IsReversal = null -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Sar.Null2NaN(); + public double Value => Sar.Null2NaN(); } diff --git a/src/m-r/ParabolicSar/ParabolicSar.StaticSeries.cs b/src/m-r/ParabolicSar/ParabolicSar.StaticSeries.cs index 4016fea11..f19618e1c 100644 --- a/src/m-r/ParabolicSar/ParabolicSar.StaticSeries.cs +++ b/src/m-r/ParabolicSar/ParabolicSar.StaticSeries.cs @@ -2,16 +2,39 @@ namespace Skender.Stock.Indicators; // PARABOLIC SAR (SERIES) -public static partial class Indicator +public static partial class ParabolicSar { + public static IReadOnlyList ToParabolicSar( + this IReadOnlyList quotes, + double accelerationStep = 0.02, + double maxAccelerationFactor = 0.2) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcParabolicSar( + accelerationStep, + maxAccelerationFactor, + accelerationStep); + + public static IReadOnlyList GetParabolicSar( + this IReadOnlyList quotes, + double accelerationStep, + double maxAccelerationFactor, + double initialFactor) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcParabolicSar( + accelerationStep, + maxAccelerationFactor, + initialFactor); + private static List CalcParabolicSar( - this List source, + this IReadOnlyList source, double accelerationStep, double maxAccelerationFactor, double initialFactor) { // check parameter arguments - ParabolicSar.Validate( + Validate( accelerationStep, maxAccelerationFactor, initialFactor); // initialize diff --git a/src/m-r/ParabolicSar/ParabolicSar.Utilities.cs b/src/m-r/ParabolicSar/ParabolicSar.Utilities.cs index 4c9db1b4b..afa732021 100644 --- a/src/m-r/ParabolicSar/ParabolicSar.Utilities.cs +++ b/src/m-r/ParabolicSar/ParabolicSar.Utilities.cs @@ -1,16 +1,46 @@ +using System.Globalization; + namespace Skender.Stock.Indicators; -public static partial class Indicator +// PARABOLIC SAR (UTILITIES) + +public static partial class ParabolicSar { - // remove recommended periods - /// - public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + private static readonly CultureInfo invariantCulture = CultureInfo.InvariantCulture; + + // parameter validation + internal static void Validate( + double accelerationStep, + double maxAccelerationFactor, + double initialFactor) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Sar != null); + // check parameter arguments + if (accelerationStep <= 0) + { + throw new ArgumentOutOfRangeException(nameof(accelerationStep), accelerationStep, + "Acceleration Step must be greater than 0 for Parabolic SAR."); + } + + if (maxAccelerationFactor <= 0) + { + throw new ArgumentOutOfRangeException(nameof(maxAccelerationFactor), maxAccelerationFactor, + "Max Acceleration Factor must be greater than 0 for Parabolic SAR."); + } + + if (accelerationStep > maxAccelerationFactor) + { + string message = string.Format( + invariantCulture, + "Acceleration Step cannot be larger than the Max Acceleration Factor ({0}) for Parabolic SAR.", + maxAccelerationFactor); + + throw new ArgumentOutOfRangeException(nameof(accelerationStep), accelerationStep, message); + } - return results.Remove(removePeriods); + if (initialFactor <= 0 || initialFactor > maxAccelerationFactor) + { + throw new ArgumentOutOfRangeException(nameof(initialFactor), initialFactor, + "Initial Factor must be greater than 0 and not larger than Max Acceleration Factor for Parabolic SAR."); + } } } diff --git a/src/m-r/PivotPoints/PivotPoints.Api.cs b/src/m-r/PivotPoints/PivotPoints.Api.cs deleted file mode 100644 index be099965a..000000000 --- a/src/m-r/PivotPoints/PivotPoints.Api.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Skender.Stock.Indicators; - -// PIVOT POINTS (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetPivotPoints( - this IEnumerable quotes, - PeriodSize windowSize, - PivotPointType pointType = PivotPointType.Standard) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcPivotPoints(windowSize, pointType); -} diff --git a/src/m-r/PivotPoints/PivotPoints.StaticSeries.cs b/src/m-r/PivotPoints/PivotPoints.StaticSeries.cs index 058649e93..7b540b12f 100644 --- a/src/m-r/PivotPoints/PivotPoints.StaticSeries.cs +++ b/src/m-r/PivotPoints/PivotPoints.StaticSeries.cs @@ -2,16 +2,18 @@ namespace Skender.Stock.Indicators; // PIVOT POINTS (SERIES) -public static partial class Indicator +public static partial class PivotPoints { - private static List CalcPivotPoints( - this List quotesList, + public static IReadOnlyList ToPivotPoints( + this IReadOnlyList quotes, PeriodSize windowSize, - PivotPointType pointType) + PivotPointType pointType = PivotPointType.Standard) where TQuote : IQuote { + ArgumentNullException.ThrowIfNull(quotes); + // initialize - int length = quotesList.Count; + int length = quotes.Count; List results = new(length); WindowPoint windowPoint = new(); @@ -21,7 +23,7 @@ private static List CalcPivotPoints( return results; } - TQuote h0 = quotesList[0]; + TQuote h0 = quotes[0]; int windowId = GetWindowNumber(h0.Timestamp, windowSize); @@ -35,7 +37,7 @@ private static List CalcPivotPoints( // roll through source values for (int i = 0; i < length; i++) { - TQuote q = quotesList[i]; + TQuote q = quotes[i]; // new window evaluation int windowEval = GetWindowNumber(q.Timestamp, windowSize); @@ -97,7 +99,43 @@ PivotPointsResult r return results; } - // internals + // pivot point lookup + internal static WindowPoint GetPivotPoint( + PivotPointType pointType, decimal open, decimal high, decimal low, decimal close) + => pointType switch { + + PivotPointType.Standard => GetPivotPointStandard(high, low, close), + PivotPointType.Camarilla => GetPivotPointCamarilla(high, low, close), + PivotPointType.Demark => GetPivotPointDemark(open, high, low, close), + PivotPointType.Fibonacci => GetPivotPointFibonacci(high, low, close), + PivotPointType.Woodie => GetPivotPointWoodie(open, high, low), + + _ => throw new ArgumentOutOfRangeException( + nameof(pointType), pointType, "Invalid pointType provided.") + }; + + // window size lookup + private static int GetWindowNumber(DateTime d, PeriodSize windowSize) + => windowSize switch { + + PeriodSize.Month => d.Month, + + PeriodSize.Week => calendar.GetWeekOfYear( + d, calendarWeekRule, firstDayOfWeek), + + PeriodSize.Day => d.Day, + PeriodSize.OneHour => d.Hour, + + _ => throw new ArgumentOutOfRangeException( + nameof(windowSize), windowSize, + string.Format( + invariantCulture, + "Pivot Points does not support PeriodSize of {0}. " + + "See documentation for valid options.", + Enum.GetName(typeof(PeriodSize), windowSize))) + }; + + // pivot point variants private static WindowPoint GetPivotPointStandard( decimal high, decimal low, decimal close) { @@ -105,12 +143,12 @@ private static WindowPoint GetPivotPointStandard( return new() { PP = pp, - S1 = pp * 2 - high, + S1 = (pp * 2) - high, S2 = pp - (high - low), - S3 = low - 2 * (high - pp), - R1 = pp * 2 - low, + S3 = low - (2 * (high - pp)), + R1 = (pp * 2) - low, R2 = pp + (high - low), - R3 = high + 2 * (pp - low) + R3 = high + (2 * (pp - low)) }; } @@ -118,29 +156,29 @@ private static WindowPoint GetPivotPointCamarilla( decimal high, decimal low, decimal close) => new() { PP = close, - S1 = close - 1.1m / 12 * (high - low), - S2 = close - 1.1m / 6 * (high - low), - S3 = close - 1.1m / 4 * (high - low), - S4 = close - 1.1m / 2 * (high - low), - R1 = close + 1.1m / 12 * (high - low), - R2 = close + 1.1m / 6 * (high - low), - R3 = close + 1.1m / 4 * (high - low), - R4 = close + 1.1m / 2 * (high - low) + S1 = close - (1.1m / 12 * (high - low)), + S2 = close - (1.1m / 6 * (high - low)), + S3 = close - (1.1m / 4 * (high - low)), + S4 = close - (1.1m / 2 * (high - low)), + R1 = close + (1.1m / 12 * (high - low)), + R2 = close + (1.1m / 6 * (high - low)), + R3 = close + (1.1m / 4 * (high - low)), + R4 = close + (1.1m / 2 * (high - low)) }; internal static WindowPoint GetPivotPointDemark( decimal open, decimal high, decimal low, decimal close) { decimal x = close < open - ? high + 2 * low + close + ? high + (2 * low) + close : close > open - ? 2 * high + low + close - : high + low + 2 * close; + ? (2 * high) + low + close + : high + low + (2 * close); return new() { PP = x / 4, - S1 = x / 2 - high, - R1 = x / 2 - low + S1 = (x / 2) - high, + R1 = (x / 2) - low }; } @@ -151,64 +189,28 @@ private static WindowPoint GetPivotPointFibonacci( return new() { PP = pp, - S1 = pp - 0.382m * (high - low), - S2 = pp - 0.618m * (high - low), - S3 = pp - 1.000m * (high - low), - R1 = pp + 0.382m * (high - low), - R2 = pp + 0.618m * (high - low), - R3 = pp + 1.000m * (high - low) + S1 = pp - (0.382m * (high - low)), + S2 = pp - (0.618m * (high - low)), + S3 = pp - (1.000m * (high - low)), + R1 = pp + (0.382m * (high - low)), + R2 = pp + (0.618m * (high - low)), + R3 = pp + (1.000m * (high - low)) }; } private static WindowPoint GetPivotPointWoodie( decimal currentOpen, decimal high, decimal low) { - decimal pp = (high + low + 2 * currentOpen) / 4; + decimal pp = (high + low + (2 * currentOpen)) / 4; return new() { PP = pp, - S1 = pp * 2 - high, + S1 = (pp * 2) - high, S2 = pp - high + low, - S3 = low - 2 * (high - pp), - R1 = pp * 2 - low, + S3 = low - (2 * (high - pp)), + R1 = (pp * 2) - low, R2 = pp + high - low, - R3 = high + 2 * (pp - low) + R3 = high + (2 * (pp - low)) }; } - - // pivot type lookup - private static WindowPoint GetPivotPoint( - PivotPointType pointType, decimal open, decimal high, decimal low, decimal close) - => pointType switch { - - PivotPointType.Standard => GetPivotPointStandard(high, low, close), - PivotPointType.Camarilla => GetPivotPointCamarilla(high, low, close), - PivotPointType.Demark => GetPivotPointDemark(open, high, low, close), - PivotPointType.Fibonacci => GetPivotPointFibonacci(high, low, close), - PivotPointType.Woodie => GetPivotPointWoodie(open, high, low), - - _ => throw new ArgumentOutOfRangeException( - nameof(pointType), pointType, "Invalid pointType provided.") - }; - - // window size lookup - private static int GetWindowNumber(DateTime d, PeriodSize windowSize) - => windowSize switch { - - PeriodSize.Month => d.Month, - - PeriodSize.Week => invCalendar.GetWeekOfYear( - d, invCalendarWeekRule, invFirstDayOfWeek), - - PeriodSize.Day => d.Day, - PeriodSize.OneHour => d.Hour, - - _ => throw new ArgumentOutOfRangeException( - nameof(windowSize), windowSize, - string.Format( - invCulture, - "Pivot Points does not support PeriodSize of {0}. " + - "See documentation for valid options.", - Enum.GetName(typeof(PeriodSize), windowSize))) - }; } diff --git a/src/m-r/PivotPoints/PivotPoints.Utilities.cs b/src/m-r/PivotPoints/PivotPoints.Utilities.cs index 0de695e83..c562b0da9 100644 --- a/src/m-r/PivotPoints/PivotPoints.Utilities.cs +++ b/src/m-r/PivotPoints/PivotPoints.Utilities.cs @@ -1,15 +1,29 @@ +using System.Globalization; + namespace Skender.Stock.Indicators; // PIVOT POINTS (UTILITIES) -public static partial class Indicator + +public static partial class PivotPoints { + private static readonly CultureInfo invariantCulture = CultureInfo.InvariantCulture; + + private static readonly Calendar calendar = invariantCulture.Calendar; + + // Gets the DTFI properties required by GetWeekOfYear. + private static readonly CalendarWeekRule calendarWeekRule + = invariantCulture.DateTimeFormat.CalendarWeekRule; + + private static readonly DayOfWeek firstDayOfWeek + = invariantCulture.DateTimeFormat.FirstDayOfWeek; + // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int removePeriods = results - .ToList() + .ToList() // TODO: is there a no-copy way to do this? Many places. .FindIndex(x => x.PP != null); return results.Remove(removePeriods); diff --git a/src/m-r/Pivots/Pivots.Api.cs b/src/m-r/Pivots/Pivots.Api.cs deleted file mode 100644 index d2157f35a..000000000 --- a/src/m-r/Pivots/Pivots.Api.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Skender.Stock.Indicators; - -// PIVOTS (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetPivots( - this IEnumerable quotes, - int leftSpan = 2, - int rightSpan = 2, - int maxTrendPeriods = 20, - EndType endType = EndType.HighLow) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcPivots(leftSpan, rightSpan, maxTrendPeriods, endType); -} diff --git a/src/m-r/Pivots/Pivots.Common.cs b/src/m-r/Pivots/Pivots.Common.cs deleted file mode 100644 index b244c836a..000000000 --- a/src/m-r/Pivots/Pivots.Common.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace Skender.Stock.Indicators; - -// PIVOTS (COMMON) - -public static class Pivots -{ - // parameter validation - internal static void Validate( - int leftSpan, - int rightSpan, - int maxTrendPeriods, - string caller = "Pivots") - { - // check parameter arguments - if (rightSpan < 2) - { - throw new ArgumentOutOfRangeException(nameof(rightSpan), rightSpan, - $"Right span must be at least 2 for {caller}."); - } - - if (leftSpan < 2) - { - throw new ArgumentOutOfRangeException(nameof(leftSpan), leftSpan, - $"Left span must be at least 2 for {caller}."); - } - - if (maxTrendPeriods <= leftSpan) - { - throw new ArgumentOutOfRangeException(nameof(leftSpan), leftSpan, - $"Lookback periods must be greater than the Left window span for {caller}."); - } - } - -} diff --git a/src/m-r/Pivots/Pivots.StaticSeries.cs b/src/m-r/Pivots/Pivots.StaticSeries.cs index 6a2672123..00a833b10 100644 --- a/src/m-r/Pivots/Pivots.StaticSeries.cs +++ b/src/m-r/Pivots/Pivots.StaticSeries.cs @@ -2,21 +2,22 @@ namespace Skender.Stock.Indicators; // PIVOTS (SERIES) -public static partial class Indicator +public static partial class Pivots { - private static List CalcPivots( - this List quotesList, - int leftSpan, - int rightSpan, - int maxTrendPeriods, - EndType endType) + public static IReadOnlyList ToPivots( + this IReadOnlyList quotes, + int leftSpan = 2, + int rightSpan = 2, + int maxTrendPeriods = 20, + EndType endType = EndType.HighLow) where TQuote : IQuote { // check parameter arguments - Pivots.Validate(leftSpan, rightSpan, maxTrendPeriods); + ArgumentNullException.ThrowIfNull(quotes); + Validate(leftSpan, rightSpan, maxTrendPeriods); // initialize - int length = quotesList.Count; + int length = quotes.Count; decimal?[] highLine = new decimal?[length]; PivotTrend?[] highTrend = new PivotTrend?[length]; @@ -24,9 +25,9 @@ private static List CalcPivots( decimal?[] lowLine = new decimal?[length]; PivotTrend?[] lowTrend = new PivotTrend?[length]; - List<(decimal? highPoint, decimal? lowPoint)> fractals - = quotesList - .CalcFractal(leftSpan, rightSpan, endType) + IReadOnlyList<(decimal? highPoint, decimal? lowPoint)> fractals + = quotes + .ToFractal(leftSpan, rightSpan, endType) .Select(f => (f.FractalBear, f.FractalBull)) .ToList(); @@ -117,7 +118,7 @@ private static List CalcPivots( for (int i = 0; i < length; i++) { - TQuote q = quotesList[i]; + TQuote q = quotes[i]; (decimal? highPoint, decimal? lowPoint) = fractals[i]; decimal? hl = highLine[i]; diff --git a/src/m-r/Pivots/Pivots.Utilities.cs b/src/m-r/Pivots/Pivots.Utilities.cs index 65417f95a..4030ac0a0 100644 --- a/src/m-r/Pivots/Pivots.Utilities.cs +++ b/src/m-r/Pivots/Pivots.Utilities.cs @@ -1,11 +1,12 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// PIVOTS (UTILITIES) + +public static partial class Pivots { - // CONDENSE (REMOVE null results) - /// + /// public static IReadOnlyList Condense( - this IEnumerable results) + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -16,4 +17,31 @@ public static IReadOnlyList Condense( return resultsList.ToSortedList(); } + + // parameter validation + internal static void Validate( + int leftSpan, + int rightSpan, + int maxTrendPeriods, + string caller = "Pivots") + { + // check parameter arguments + if (rightSpan < 2) + { + throw new ArgumentOutOfRangeException(nameof(rightSpan), rightSpan, + $"Right span must be at least 2 for {caller}."); + } + + if (leftSpan < 2) + { + throw new ArgumentOutOfRangeException(nameof(leftSpan), leftSpan, + $"Left span must be at least 2 for {caller}."); + } + + if (maxTrendPeriods <= leftSpan) + { + throw new ArgumentOutOfRangeException(nameof(leftSpan), leftSpan, + $"Lookback periods must be greater than the Left window span for {caller}."); + } + } } diff --git a/src/m-r/Pmo/Pmo.Api.cs b/src/m-r/Pmo/Pmo.Api.cs deleted file mode 100644 index 6dd0e9481..000000000 --- a/src/m-r/Pmo/Pmo.Api.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Skender.Stock.Indicators; - -// PRICE MOMENTUM OSCILLATOR (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetPmo( - this IEnumerable results, - int timePeriods = 35, - int smoothPeriods = 20, - int signalPeriods = 10) - where T : IReusable - => results - .ToSortedList() - .CalcPmo(timePeriods, smoothPeriods, signalPeriods); -} diff --git a/src/m-r/Pmo/Pmo.Common.cs b/src/m-r/Pmo/Pmo.Common.cs deleted file mode 100644 index 1fab8c121..000000000 --- a/src/m-r/Pmo/Pmo.Common.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Skender.Stock.Indicators; - -// PRICE MOMENTUM OSCILLATOR (COMMON) - -public static class Pmo -{ - // parameter validation - internal static void Validate( - int timePeriods, - int smoothPeriods, - int signalPeriods) - { - // check parameter arguments - if (timePeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(timePeriods), timePeriods, - "Time periods must be greater than 1 for PMO."); - } - - if (smoothPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(smoothPeriods), smoothPeriods, - "Smoothing periods must be greater than 0 for PMO."); - } - - if (signalPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, - "Signal periods must be greater than 0 for PMO."); - } - } - -} diff --git a/src/m-r/Pmo/Pmo.Models.cs b/src/m-r/Pmo/Pmo.Models.cs index f4807d81f..3bf28a820 100644 --- a/src/m-r/Pmo/Pmo.Models.cs +++ b/src/m-r/Pmo/Pmo.Models.cs @@ -5,7 +5,7 @@ public record PmoResult DateTime Timestamp, double? Pmo, double? Signal -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Pmo.Null2NaN(); + public double Value => Pmo.Null2NaN(); } diff --git a/src/m-r/Pmo/Pmo.StaticSeries.cs b/src/m-r/Pmo/Pmo.StaticSeries.cs index adaffec49..feaea3a64 100644 --- a/src/m-r/Pmo/Pmo.StaticSeries.cs +++ b/src/m-r/Pmo/Pmo.StaticSeries.cs @@ -2,17 +2,18 @@ namespace Skender.Stock.Indicators; // PRICE MOMENTUM OSCILLATOR (SERIES) -public static partial class Indicator +public static partial class Pmo { - private static List CalcPmo( - this List source, - int timePeriods, - int smoothPeriods, - int signalPeriods) + public static IReadOnlyList ToPmo( + this IReadOnlyList source, + int timePeriods = 35, + int smoothPeriods = 20, + int signalPeriods = 10) where T : IReusable { // check parameter arguments - Pmo.Validate(timePeriods, smoothPeriods, signalPeriods); + ArgumentNullException.ThrowIfNull(source); + Validate(timePeriods, smoothPeriods, signalPeriods); // initialize int length = source.Count; @@ -36,7 +37,7 @@ private static List CalcPmo( T s = source[i]; // rate of change (ROC) - rc[i] = prevPrice == 0 ? double.NaN : 100 * (s.Value / prevPrice - 1); + rc[i] = prevPrice == 0 ? double.NaN : 100 * ((s.Value / prevPrice) - 1); prevPrice = s.Value; // ROC smoothed moving average @@ -53,7 +54,7 @@ private static List CalcPmo( } else { - rocEma = prevRocEma + smoothingConstant2 * (rc[i] - prevRocEma); + rocEma = prevRocEma + (smoothingConstant2 * (rc[i] - prevRocEma)); } re[i] = rocEma * 10; @@ -73,7 +74,7 @@ private static List CalcPmo( } else { - pmo = prevPmo + smoothingConstant1 * (re[i] - prevPmo); + pmo = prevPmo + (smoothingConstant1 * (re[i] - prevPmo)); } prevPmo = pm[i] = pmo; diff --git a/src/m-r/Pmo/Pmo.Utilities.cs b/src/m-r/Pmo/Pmo.Utilities.cs index 388388b4c..3ca068f94 100644 --- a/src/m-r/Pmo/Pmo.Utilities.cs +++ b/src/m-r/Pmo/Pmo.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// PRICE MOMENTUM OSCILLATOR (UTILITIES) + +public static partial class Pmo { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int ts = results .ToList() @@ -13,4 +15,30 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(ts + 250); } + + // parameter validation + internal static void Validate( + int timePeriods, + int smoothPeriods, + int signalPeriods) + { + // check parameter arguments + if (timePeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(timePeriods), timePeriods, + "Time periods must be greater than 1 for PMO."); + } + + if (smoothPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(smoothPeriods), smoothPeriods, + "Smoothing periods must be greater than 0 for PMO."); + } + + if (signalPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, + "Signal periods must be greater than 0 for PMO."); + } + } } diff --git a/src/m-r/Prs/Prs.Api.cs b/src/m-r/Prs/Prs.Api.cs deleted file mode 100644 index f169896d3..000000000 --- a/src/m-r/Prs/Prs.Api.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Skender.Stock.Indicators; - -// PRICE RELATIVE STRENGTH (API) -public static partial class Indicator -{ - // SERIES, from CHAINS (both inputs reusable) - public static IReadOnlyList GetPrs( - this IEnumerable quotesEval, - IEnumerable quotesBase, - int? lookbackPeriods = null) - where T : IReusable - => CalcPrs( - quotesEval.ToSortedList(), - quotesBase.ToSortedList(), - lookbackPeriods); -} diff --git a/src/m-r/Prs/Prs.Models.cs b/src/m-r/Prs/Prs.Models.cs index bcb721a71..8170f040b 100644 --- a/src/m-r/Prs/Prs.Models.cs +++ b/src/m-r/Prs/Prs.Models.cs @@ -5,7 +5,7 @@ public record PrsResult DateTime Timestamp, double? Prs, double? PrsPercent -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Prs.Null2NaN(); + public double Value => Prs.Null2NaN(); } diff --git a/src/m-r/Prs/Prs.StaticSeries.cs b/src/m-r/Prs/Prs.StaticSeries.cs index 197590345..40a6ab70d 100644 --- a/src/m-r/Prs/Prs.StaticSeries.cs +++ b/src/m-r/Prs/Prs.StaticSeries.cs @@ -2,31 +2,33 @@ namespace Skender.Stock.Indicators; // PRICE RELATIVE STRENGTH (SERIES) -public static partial class Indicator +public static partial class Prs { - private static List CalcPrs( - List listEval, - List listBase, + public static IReadOnlyList ToPrs( + this IReadOnlyList sourceEval, + IReadOnlyList sourceBase, int? lookbackPeriods = null) where T : IReusable { // check parameter arguments - Prs.Validate(listEval, listBase, lookbackPeriods); + ArgumentNullException.ThrowIfNull(sourceEval); + ArgumentNullException.ThrowIfNull(sourceBase); + Validate(sourceEval, sourceBase, lookbackPeriods); // initialize - int length = listEval.Count; + int length = sourceEval.Count; List results = new(length); // roll through source values for (int i = 0; i < length; i++) { - T b = listBase[i]; - T e = listEval[i]; + T b = sourceBase[i]; + T e = sourceEval[i]; if (e.Timestamp != b.Timestamp) { throw new InvalidQuotesException( - nameof(listEval), e.Timestamp, + nameof(sourceEval), e.Timestamp, "Timestamp sequence does not match. " + "Price Relative requires matching dates in provided histories."); } @@ -35,8 +37,8 @@ private static List CalcPrs( if (lookbackPeriods is not null && i > lookbackPeriods - 1) { - T bo = listBase[i - (int)lookbackPeriods]; - T eo = listEval[i - (int)lookbackPeriods]; + T bo = sourceBase[i - (int)lookbackPeriods]; + T eo = sourceEval[i - (int)lookbackPeriods]; if (bo.Value != 0 && eo.Value != 0) { diff --git a/src/m-r/Prs/Prs.Common.cs b/src/m-r/Prs/Prs.Utilities.cs similarity index 80% rename from src/m-r/Prs/Prs.Common.cs rename to src/m-r/Prs/Prs.Utilities.cs index 55e71e8ee..497f2e649 100644 --- a/src/m-r/Prs/Prs.Common.cs +++ b/src/m-r/Prs/Prs.Utilities.cs @@ -2,18 +2,18 @@ namespace Skender.Stock.Indicators; -// PRICE RELATIVE STRENGTH (COMMON) +// PRICE RELATIVE STRENGTH (UTILITIES) -public static class Prs +public static partial class Prs { - private static readonly CultureInfo EnglishCulture = new("en-US", false); + private static readonly CultureInfo invariantCulture = CultureInfo.InvariantCulture; // parameter validation internal static void Validate( - List quotesEval, - List quotesBase, + IReadOnlyList quotesEval, + IReadOnlyList quotesBase, int? lookbackPeriods) - where T : ISeries + where T : IReusable { // check parameter arguments if (lookbackPeriods is <= 0) @@ -31,7 +31,7 @@ internal static void Validate( { string message = "Insufficient quotes provided for Price Relative Strength. " + string.Format( - EnglishCulture, + invariantCulture, "You provided {0} periods of quotes when at least {1} are required.", qtyHistoryEval, lookbackPeriods); diff --git a/src/m-r/Pvo/Pvo.Api.cs b/src/m-r/Pvo/Pvo.Api.cs deleted file mode 100644 index 0c24ea386..000000000 --- a/src/m-r/Pvo/Pvo.Api.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Skender.Stock.Indicators; - -// PRICE VOLUME OSCILLATOR (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetPvo( - this IEnumerable quotes, - int fastPeriods = 12, - int slowPeriods = 26, - int signalPeriods = 9) - where TQuote : IQuote => quotes - .Use(CandlePart.Volume) - .ToList() - .CalcPvo(fastPeriods, slowPeriods, signalPeriods); - - // given that this is volume-based, other chaining is moot -} diff --git a/src/m-r/Pvo/Pvo.Common.cs b/src/m-r/Pvo/Pvo.Common.cs deleted file mode 100644 index 8119da987..000000000 --- a/src/m-r/Pvo/Pvo.Common.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Skender.Stock.Indicators; - -// PRICE VOLUME OSCILLATOR (COMMON) - -public static class Pvo -{ - // parameter validation - internal static void Validate( - int fastPeriods, - int slowPeriods, - int signalPeriods) - { - // check parameter arguments - if (fastPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(fastPeriods), fastPeriods, - "Fast periods must be greater than 0 for PVO."); - } - - if (signalPeriods < 0) - { - throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, - "Signal periods must be greater than or equal to 0 for PVO."); - } - - if (slowPeriods <= fastPeriods) - { - throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, - "Slow periods must be greater than the fast period for PVO."); - } - } - -} diff --git a/src/m-r/Pvo/Pvo.Models.cs b/src/m-r/Pvo/Pvo.Models.cs index 49e18ec66..7d6c0828c 100644 --- a/src/m-r/Pvo/Pvo.Models.cs +++ b/src/m-r/Pvo/Pvo.Models.cs @@ -6,7 +6,7 @@ public record PvoResult double? Pvo, double? Signal, double? Histogram -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Pvo.Null2NaN(); + public double Value => Pvo.Null2NaN(); } diff --git a/src/m-r/Pvo/Pvo.StaticSeries.cs b/src/m-r/Pvo/Pvo.StaticSeries.cs index eff18d84b..4f02630e7 100644 --- a/src/m-r/Pvo/Pvo.StaticSeries.cs +++ b/src/m-r/Pvo/Pvo.StaticSeries.cs @@ -2,17 +2,26 @@ namespace Skender.Stock.Indicators; // PRICE VOLUME OSCILLATOR (SERIES) -public static partial class Indicator +public static partial class Pvo { + public static IReadOnlyList ToPvo( + this IReadOnlyList quotes, + int fastPeriods = 12, + int slowPeriods = 26, + int signalPeriods = 9) + where TQuote : IQuote => quotes + .Use(CandlePart.Volume) + .CalcPvo(fastPeriods, slowPeriods, signalPeriods); + private static List CalcPvo( - this List source, // volume + this IReadOnlyList source, // volume int fastPeriods, int slowPeriods, int signalPeriods) where T : IReusable { // check parameter arguments - Pvo.Validate(fastPeriods, slowPeriods, signalPeriods); + Validate(fastPeriods, slowPeriods, signalPeriods); // initialize int length = source.Count; diff --git a/src/m-r/Pvo/Pvo.Utilities.cs b/src/m-r/Pvo/Pvo.Utilities.cs index 05729ea59..3731ccb5d 100644 --- a/src/m-r/Pvo/Pvo.Utilities.cs +++ b/src/m-r/Pvo/Pvo.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// PRICE VOLUME OSCILLATOR (UTILITIES) + +public static partial class Pvo { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int n = results .ToList() @@ -13,4 +15,30 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(n + 250); } + + // parameter validation + internal static void Validate( + int fastPeriods, + int slowPeriods, + int signalPeriods) + { + // check parameter arguments + if (fastPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(fastPeriods), fastPeriods, + "Fast periods must be greater than 0 for PVO."); + } + + if (signalPeriods < 0) + { + throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, + "Signal periods must be greater than or equal to 0 for PVO."); + } + + if (slowPeriods <= fastPeriods) + { + throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, + "Slow periods must be greater than the fast period for PVO."); + } + } } diff --git a/src/m-r/Renko/Renko.Api.cs b/src/m-r/Renko/Renko.Api.cs deleted file mode 100644 index 9f2b1c5df..000000000 --- a/src/m-r/Renko/Renko.Api.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Skender.Stock.Indicators; - -// RENKO CHART (API) - -public static partial class Renko -{ - // SERIES, from TQuote - public static IReadOnlyList GetRenko( - this IEnumerable quotes, - decimal brickSize, - EndType endType = EndType.Close) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcRenko(brickSize, endType); - - // OBSERVER, from Quote Provider - public static RenkoHub ToRenko( - this IQuoteProvider quoteProvider, - decimal brickSize, - EndType endType = EndType.Close) - where TIn : IQuote - => new(quoteProvider, brickSize, endType); -} diff --git a/src/m-r/Renko/Renko.StaticSeries.cs b/src/m-r/Renko/Renko.StaticSeries.cs index 89c95cd8d..6aaa45bc5 100644 --- a/src/m-r/Renko/Renko.StaticSeries.cs +++ b/src/m-r/Renko/Renko.StaticSeries.cs @@ -4,17 +4,18 @@ namespace Skender.Stock.Indicators; public static partial class Renko { - internal static List CalcRenko( - this List quotesList, + public static IReadOnlyList ToRenko( + this IReadOnlyList quotes, decimal brickSize, - EndType endType) + EndType endType = EndType.Close) where TQuote : IQuote { // check parameter arguments + ArgumentNullException.ThrowIfNull(quotes); Validate(brickSize); // initialize - int length = quotesList.Count; + int length = quotes.Count; List results = new(length); if (length == 0) @@ -23,7 +24,7 @@ internal static List CalcRenko( } // first brick baseline - TQuote q0 = quotesList[0]; + TQuote q0 = quotes[0]; int decimals = brickSize.GetDecimalPlaces(); decimal baseline = Math.Round(q0.Close, Math.Max(decimals - 1, 0)); @@ -41,7 +42,7 @@ internal static List CalcRenko( // roll through source values for (int i = 1; i < length; i++) { - TQuote q = quotesList[i]; + TQuote q = quotes[i]; // track high/low/volume between bricks h = Math.Max(h, q.High); diff --git a/src/m-r/Renko/Renko.StreamHub.cs b/src/m-r/Renko/Renko.StreamHub.cs index 53f061a54..8486bf51f 100644 --- a/src/m-r/Renko/Renko.StreamHub.cs +++ b/src/m-r/Renko/Renko.StreamHub.cs @@ -2,23 +2,37 @@ namespace Skender.Stock.Indicators; // RENKO CHART (STREAM HUB) -#region hub interface +#region hub interface and initializer public interface IRenkoHub { decimal BrickSize { get; } EndType EndType { get; } } + +public static partial class Renko +{ + public static RenkoHub ToRenko( + this IQuoteProvider quoteProvider, + decimal brickSize, + EndType endType = EndType.Close) + where TIn : IQuote + => new(quoteProvider, brickSize, endType); +} #endregion -public class RenkoHub : QuoteObserver, - IQuoteHub, IRenkoHub +public class RenkoHub + : QuoteProvider, IRenkoHub where TIn : IQuote { #region constructors private readonly string hubName; + private RenkoResult lastBrick + = new(default, default, default, + default, default, default, default); + internal RenkoHub( IQuoteProvider provider, decimal brickSize, @@ -33,50 +47,105 @@ internal RenkoHub( } #endregion + /// + /// Renko hub settings. Since it can produce 0 or many bricks per quote, + /// the default 1:1 in/out is not used and must be skipped to prevent + /// same-date triggerred rebuilds when caching. + /// + public override BinarySettings Properties { get; init; } = new(0b00000010); // custom + + /// + /// Standard brick size for Renko chart. + /// public decimal BrickSize { get; } + + /// + /// Close or High/Low price used to determine when threshold + /// is met to generate new bricks. + /// public EndType EndType { get; } // METHODS - + public override string ToString() => hubName; - - internal override void Add(Act act, TIn newIn, int? index) - { - // get last brick - RenkoResult lastBrick; + public override void OnAdd(TIn item, bool notify, int? indexHint) + => ToIndicator(item, notify, indexHint); + + protected override (RenkoResult result, int index) + ToIndicator(TIn item, int? indexHint) + => throw new InvalidOperationException(); // not used + + // TODO: see if returning array of results is possible ^^ + // for all indicators, so we don't have to do this goofy override + + /// + /// Restore last brick marker. + /// + /// + protected override void RollbackState(DateTime timestamp) + { + // restore last brick marker if (Cache.Count != 0) { lastBrick = Cache - .Where(c => c.Timestamp <= newIn.Timestamp) - .Last(); + .Last(c => c.Timestamp <= timestamp); + + return; } - else // no bricks yet + + // skip first quote + if (ProviderCache.Count <= 1) { - // skip first quote - if (Provider.Results.Count <= 1) - { - return; - } + return; + } - int decimals = BrickSize.GetDecimalPlaces(); + SetBaselineBrick(); + } + + // re/initialize last brick marker + private void SetBaselineBrick() + { + int decimals = BrickSize.GetDecimalPlaces(); + + TIn q0 = ProviderCache[0]; + + decimal baseline + = Math.Round(q0.Close, + Math.Max(decimals - 1, 0)); + + lastBrick = new( + q0.Timestamp, + Open: baseline, + High: 0, + Low: 0, + Close: baseline, + Volume: 0, + IsUp: false); + } - TIn q0 = Provider.Results[0]; + // custom: build 0 to many bricks per quote + private void ToIndicator(TIn item, bool notify, int? indexHint) + { + int providerIndex = indexHint + ?? throw new InvalidOperationException($"{nameof(indexHint)} cannot be empty"); - decimal baseline - = Math.Round(q0.Close, - Math.Max(decimals - 1, 0)); + // nothing to do + if (providerIndex <= 0) + { + return; + } - lastBrick = new( - q0.Timestamp, - Open: baseline, 0, 0, - Close: baseline, 0, false); + // establish baseline brick + if (providerIndex == 1) + { + SetBaselineBrick(); } // determine new brick quantity int newBrickQty = Renko.GetNewBrickQuantity( - newIn, lastBrick, BrickSize, EndType); + item, lastBrick, BrickSize, EndType); int absBrickQty = Math.Abs(newBrickQty); bool isUp = newBrickQty >= 0; @@ -90,12 +159,11 @@ int newBrickQty decimal sumV = 0; // cumulative // by aggregating provider cache range - int inboundIndex = index ?? Provider.GetIndex(newIn, false); - int lastBrickIndex = Provider.GetIndex(lastBrick.Timestamp, false); + int lastBrickIndex = ProviderCache.GetIndex(lastBrick.Timestamp, true); - for (int w = lastBrickIndex + 1; w <= inboundIndex; w++) + for (int w = lastBrickIndex + 1; w <= providerIndex; w++) { - TIn pq = Provider.Results[w]; + TIn pq = ProviderCache[w]; h = Math.Max(h, pq.High); l = Math.Min(l, pq.Low); @@ -106,28 +174,26 @@ int newBrickQty for (int b = 0; b < absBrickQty; b++) { - decimal o; - decimal c; - - if (isUp) - { - o = Math.Max(lastBrick.Open, lastBrick.Close); - c = o + BrickSize; - } - else - { - o = Math.Min(lastBrick.Open, lastBrick.Close); - c = o - BrickSize; - } + decimal o = isUp + ? Math.Max(lastBrick.Open, lastBrick.Close) + : Math.Min(lastBrick.Open, lastBrick.Close); + + decimal c = isUp + ? o + BrickSize + : o - BrickSize; // candidate result RenkoResult r - = new(newIn.Timestamp, o, h, l, c, v, isUp); + = new(item.Timestamp, o, h, l, c, v, isUp); lastBrick = r; // save and send - Motify(act, r, null); + AppendCache(r, notify); + + // note: bypass rebuild bit set in Properties to allow + // sequential bricks with duplicate dates that would + // normally trigger rebuild, causing stack overflow. } } } diff --git a/src/m-r/RenkoAtr/RenkoAtr.Api.cs b/src/m-r/RenkoAtr/RenkoAtr.Api.cs deleted file mode 100644 index 2c3cf775c..000000000 --- a/src/m-r/RenkoAtr/RenkoAtr.Api.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Skender.Stock.Indicators; - -// RENKO CHART - ATR (API) -public static partial class RenkoAtr -{ - public static IReadOnlyList GetRenkoAtr( - this IEnumerable quotes, - int atrPeriods, - EndType endType = EndType.Close) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcRenkoAtr(atrPeriods, endType); -} diff --git a/src/m-r/RenkoAtr/RenkoAtr.StaticSeries.cs b/src/m-r/RenkoAtr/RenkoAtr.StaticSeries.cs index 21729ad36..479278122 100644 --- a/src/m-r/RenkoAtr/RenkoAtr.StaticSeries.cs +++ b/src/m-r/RenkoAtr/RenkoAtr.StaticSeries.cs @@ -2,24 +2,24 @@ namespace Skender.Stock.Indicators; // RENKO CHART - ATR (SERIES) -public static partial class RenkoAtr +public static partial class Renko { - private static List CalcRenkoAtr( - this List quotesList, + public static IReadOnlyList GetRenkoAtr( + this IReadOnlyList quotes, int atrPeriods, EndType endType = EndType.Close) where TQuote : IQuote { // initialize - List atrResults = quotesList + List atrResults = quotes .ToQuoteDList() .CalcAtr(atrPeriods); AtrResult? last = atrResults.LastOrDefault(); decimal brickSize = (decimal?)last?.Atr ?? 0; - return brickSize is 0 + return brickSize == 0 ? [] - : quotesList.CalcRenko(brickSize, endType); + : quotes.ToRenko(brickSize, endType); } } diff --git a/src/m-r/Roc/Roc.Api.cs b/src/m-r/Roc/Roc.Api.cs deleted file mode 100644 index f087095af..000000000 --- a/src/m-r/Roc/Roc.Api.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Skender.Stock.Indicators; - -// RATE OF CHANGE (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetRoc( - this IEnumerable results, - int lookbackPeriods) - where T : IReusable - => results - .ToSortedList() - .CalcRoc(lookbackPeriods); -} diff --git a/src/m-r/Roc/Roc.Common.cs b/src/m-r/Roc/Roc.Common.cs deleted file mode 100644 index 3c3131afc..000000000 --- a/src/m-r/Roc/Roc.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// RATE OF CHANGE (COMMON) - -public static class Roc -{ - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for ROC."); - } - } - -} diff --git a/src/m-r/Roc/Roc.Models.cs b/src/m-r/Roc/Roc.Models.cs index d6bb41bcc..78837b215 100644 --- a/src/m-r/Roc/Roc.Models.cs +++ b/src/m-r/Roc/Roc.Models.cs @@ -5,7 +5,7 @@ public record RocResult DateTime Timestamp, double? Momentum, double? Roc -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Roc.Null2NaN(); + public double Value => Roc.Null2NaN(); } diff --git a/src/m-r/Roc/Roc.StaticSeries.cs b/src/m-r/Roc/Roc.StaticSeries.cs index 570fbdc44..aacfbf368 100644 --- a/src/m-r/Roc/Roc.StaticSeries.cs +++ b/src/m-r/Roc/Roc.StaticSeries.cs @@ -2,15 +2,16 @@ namespace Skender.Stock.Indicators; // RATE OF CHANGE (SERIES) -public static partial class Indicator +public static partial class Roc { - private static List CalcRoc( - this List source, + public static IReadOnlyList ToRoc( + this IReadOnlyList source, int lookbackPeriods) where T : IReusable { // check parameter arguments - Roc.Validate(lookbackPeriods); + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); // initialize int length = source.Count; diff --git a/src/m-r/Roc/Roc.Utilities.cs b/src/m-r/Roc/Roc.Utilities.cs index 132642fe7..2256c42ec 100644 --- a/src/m-r/Roc/Roc.Utilities.cs +++ b/src/m-r/Roc/Roc.Utilities.cs @@ -1,16 +1,18 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// RATE OF CHANGE (UTILITIES) + +public static partial class Roc { - // remove recommended periods - /// - public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Roc != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for ROC."); + } } } diff --git a/src/m-r/RocWb/RocWb.Api.cs b/src/m-r/RocWb/RocWb.Api.cs deleted file mode 100644 index a32ec46f0..000000000 --- a/src/m-r/RocWb/RocWb.Api.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Skender.Stock.Indicators; - -// RATE OF CHANGE (ROC) WITH BANDS (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetRocWb( - this IEnumerable results, - int lookbackPeriods, - int emaPeriods, - int stdDevPeriods) - where T : IReusable - => results - .ToSortedList() - .CalcRocWb(lookbackPeriods, emaPeriods, stdDevPeriods); -} diff --git a/src/m-r/RocWb/RocWb.Common.cs b/src/m-r/RocWb/RocWb.Common.cs deleted file mode 100644 index 6178f926b..000000000 --- a/src/m-r/RocWb/RocWb.Common.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Skender.Stock.Indicators; - -// RocWb (COMMON) - -public static class RocWb -{ - // parameter validation - internal static void Validate( - int lookbackPeriods, - int emaPeriods, - int stdDevPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for ROC with Bands."); - } - - if (emaPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(emaPeriods), emaPeriods, - "EMA periods must be greater than 0 for ROC."); - } - - if (stdDevPeriods <= 0 || stdDevPeriods > lookbackPeriods) - { - throw new ArgumentOutOfRangeException(nameof(stdDevPeriods), stdDevPeriods, - "Standard Deviation periods must be greater than 0 and less than lookback period for ROC with Bands."); - } - } - -} diff --git a/src/m-r/RocWb/RocWb.Models.cs b/src/m-r/RocWb/RocWb.Models.cs index e00443252..f6856c952 100644 --- a/src/m-r/RocWb/RocWb.Models.cs +++ b/src/m-r/RocWb/RocWb.Models.cs @@ -7,7 +7,7 @@ public record RocWbResult double? RocEma, double? UpperBand, double? LowerBand -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Roc.Null2NaN(); + public double Value => Roc.Null2NaN(); } diff --git a/src/m-r/RocWb/RocWb.StaticSeries.cs b/src/m-r/RocWb/RocWb.StaticSeries.cs index eee59d16b..5fa46b88f 100644 --- a/src/m-r/RocWb/RocWb.StaticSeries.cs +++ b/src/m-r/RocWb/RocWb.StaticSeries.cs @@ -1,17 +1,19 @@ namespace Skender.Stock.Indicators; // RATE OF CHANGE (ROC) WITH BANDS (SERIES) -public static partial class Indicator + +public static partial class RocWb { - private static List CalcRocWb( - this List source, + public static IReadOnlyList ToRocWb( + this IReadOnlyList source, int lookbackPeriods, int emaPeriods, int stdDevPeriods) where T : IReusable { // check parameter arguments - RocWb.Validate(lookbackPeriods, emaPeriods, stdDevPeriods); + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods, emaPeriods, stdDevPeriods); // initialize int length = source.Count; @@ -20,10 +22,8 @@ private static List CalcRocWb( double k = 2d / (emaPeriods + 1); double prevEma = double.NaN; - List ogRoc = source - .CalcRoc(lookbackPeriods) - .Cast() - .ToSortedList(); + IReadOnlyList ogRoc = source + .ToRoc(lookbackPeriods); double[] rocSq = ogRoc .Select(x => x.Value * x.Value) diff --git a/src/m-r/RocWb/RocWb.Utilities.cs b/src/m-r/RocWb/RocWb.Utilities.cs index 60cbc8c64..3b94d7257 100644 --- a/src/m-r/RocWb/RocWb.Utilities.cs +++ b/src/m-r/RocWb/RocWb.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// RATE OF CHANGE (ROC) WITH BANDS (UTILITIES) + +public static partial class RocWb { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int n = results .ToList() @@ -13,4 +15,30 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(n + 100); } + + // parameter validation + internal static void Validate( + int lookbackPeriods, + int emaPeriods, + int stdDevPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for ROC with Bands."); + } + + if (emaPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(emaPeriods), emaPeriods, + "EMA periods must be greater than 0 for ROC."); + } + + if (stdDevPeriods <= 0 || stdDevPeriods > lookbackPeriods) + { + throw new ArgumentOutOfRangeException(nameof(stdDevPeriods), stdDevPeriods, + "Standard Deviation periods must be greater than 0 and less than lookback period for ROC with Bands."); + } + } } diff --git a/src/m-r/RollingPivots/RollingPivots.Api.cs b/src/m-r/RollingPivots/RollingPivots.Api.cs deleted file mode 100644 index e7759d288..000000000 --- a/src/m-r/RollingPivots/RollingPivots.Api.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ROLLING PIVOT POINTS (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetRollingPivots( - this IEnumerable quotes, - int windowPeriods, - int offsetPeriods, - PivotPointType pointType = PivotPointType.Standard) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcRollingPivots(windowPeriods, offsetPeriods, pointType); -} diff --git a/src/m-r/RollingPivots/RollingPivots.Common.cs b/src/m-r/RollingPivots/RollingPivots.Common.cs deleted file mode 100644 index d0a3c8fa4..000000000 --- a/src/m-r/RollingPivots/RollingPivots.Common.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ROLLING PIVOT POINTS (COMMON) - -public static class RollingPivots -{ - // parameter validation - internal static void Validate( - int windowPeriods, - int offsetPeriods) - { - // check parameter arguments - if (windowPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(windowPeriods), windowPeriods, - "Window periods must be greater than 0 for Rolling Pivot Points."); - } - - if (offsetPeriods < 0) - { - throw new ArgumentOutOfRangeException(nameof(offsetPeriods), offsetPeriods, - "Offset periods must be greater than or equal to 0 for Rolling Pivot Points."); - } - } - -} diff --git a/src/m-r/RollingPivots/RollingPivots.StaticSeries.cs b/src/m-r/RollingPivots/RollingPivots.StaticSeries.cs index 75ba6df3b..efbc07fde 100644 --- a/src/m-r/RollingPivots/RollingPivots.StaticSeries.cs +++ b/src/m-r/RollingPivots/RollingPivots.StaticSeries.cs @@ -2,26 +2,27 @@ namespace Skender.Stock.Indicators; // ROLLING PIVOT POINTS (SERIES) -public static partial class Indicator +public static partial class RollingPivots { - private static List CalcRollingPivots( - this List quotesList, + public static IReadOnlyList ToRollingPivots( + this IReadOnlyList quotes, int windowPeriods, int offsetPeriods, - PivotPointType pointType) + PivotPointType pointType = PivotPointType.Standard) where TQuote : IQuote { // check parameter arguments - RollingPivots.Validate(windowPeriods, offsetPeriods); + ArgumentNullException.ThrowIfNull(quotes); + Validate(windowPeriods, offsetPeriods); // initialize - int length = quotesList.Count; + int length = quotes.Count; List results = new(length); // roll through source values for (int i = 0; i < length; i++) { - TQuote q = quotesList[i]; + TQuote q = quotes[i]; RollingPivotsResult r; @@ -29,21 +30,21 @@ private static List CalcRollingPivots( { // window values int s = i - windowPeriods - offsetPeriods; - TQuote hi = quotesList[s]; + TQuote hi = quotes[s]; decimal windowHigh = hi.High; decimal windowLow = hi.Low; - decimal windowClose = quotesList[i - offsetPeriods - 1].Close; + decimal windowClose = quotes[i - offsetPeriods - 1].Close; for (int p = s; p <= i - offsetPeriods - 1; p++) { - TQuote d = quotesList[p]; + TQuote d = quotes[p]; windowHigh = d.High > windowHigh ? d.High : windowHigh; windowLow = d.Low < windowLow ? d.Low : windowLow; } // pivot points - WindowPoint wp = GetPivotPoint( + WindowPoint wp = PivotPoints.GetPivotPoint( pointType, q.Open, windowHigh, windowLow, windowClose); r = new() { diff --git a/src/m-r/RollingPivots/RollingPivots.Utilities.cs b/src/m-r/RollingPivots/RollingPivots.Utilities.cs index 420fdd5c7..95a88b44b 100644 --- a/src/m-r/RollingPivots/RollingPivots.Utilities.cs +++ b/src/m-r/RollingPivots/RollingPivots.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// ROLLING PIVOT POINTS (UTILITIES) + +public static partial class RollingPivots { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -13,4 +15,23 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + int windowPeriods, + int offsetPeriods) + { + // check parameter arguments + if (windowPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(windowPeriods), windowPeriods, + "Window periods must be greater than 0 for Rolling Pivot Points."); + } + + if (offsetPeriods < 0) + { + throw new ArgumentOutOfRangeException(nameof(offsetPeriods), offsetPeriods, + "Offset periods must be greater than or equal to 0 for Rolling Pivot Points."); + } + } } diff --git a/src/m-r/Rsi/Rsi.Api.cs b/src/m-r/Rsi/Rsi.Api.cs deleted file mode 100644 index a12cfeea7..000000000 --- a/src/m-r/Rsi/Rsi.Api.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Skender.Stock.Indicators; - -// RELATIVE STRENGTH INDEX (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetRsi( - this IEnumerable results, - int lookbackPeriods = 14) - where T : IReusable - => results - .ToSortedList() - .CalcRsi(lookbackPeriods); -} diff --git a/src/m-r/Rsi/Rsi.Common.cs b/src/m-r/Rsi/Rsi.Common.cs deleted file mode 100644 index 70d11e5cb..000000000 --- a/src/m-r/Rsi/Rsi.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// RELATIVE STRENGTH INDEX (COMMON) - -public static class Rsi -{ - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods < 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for RSI."); - } - } - -} diff --git a/src/m-r/Rsi/Rsi.Models.cs b/src/m-r/Rsi/Rsi.Models.cs index 1cdd40ded..de6d97113 100644 --- a/src/m-r/Rsi/Rsi.Models.cs +++ b/src/m-r/Rsi/Rsi.Models.cs @@ -4,7 +4,7 @@ public record RsiResult ( DateTime Timestamp, double? Rsi = null -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Rsi.Null2NaN(); + public double Value => Rsi.Null2NaN(); } diff --git a/src/m-r/Rsi/Rsi.StaticSeries.cs b/src/m-r/Rsi/Rsi.StaticSeries.cs index 71c88f55f..fe0163cdf 100644 --- a/src/m-r/Rsi/Rsi.StaticSeries.cs +++ b/src/m-r/Rsi/Rsi.StaticSeries.cs @@ -2,15 +2,16 @@ namespace Skender.Stock.Indicators; // RELATIVE STRENGTH INDEX (SERIES) -public static partial class Indicator +public static partial class Rsi { - private static List CalcRsi( - this List source, - int lookbackPeriods) + public static IReadOnlyList ToRsi( + this IReadOnlyList source, + int lookbackPeriods = 14) where T : IReusable { // check parameter arguments - Rsi.Validate(lookbackPeriods); + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); // initialize int length = source.Count; @@ -46,7 +47,7 @@ private static List CalcRsi( double? rsi = null; prevValue = s.Value; - // initialize average gain + // re/initialize average gain if (i >= lookbackPeriods && (double.IsNaN(avgGain) || double.IsNaN(avgLoss))) { double sumGain = 0; diff --git a/src/m-r/Rsi/Rsi.Utilities.cs b/src/m-r/Rsi/Rsi.Utilities.cs index ed81fd4a4..c82bab854 100644 --- a/src/m-r/Rsi/Rsi.Utilities.cs +++ b/src/m-r/Rsi/Rsi.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// RELATIVE STRENGTH INDEX (UTILITIES) + +public static partial class Rsi { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int n = results .ToList() @@ -13,4 +15,16 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(10 * n); } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods < 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for RSI."); + } + } } diff --git a/src/s-z/Slope/Slope.Api.cs b/src/s-z/Slope/Slope.Api.cs deleted file mode 100644 index 5b3a7ec7e..000000000 --- a/src/s-z/Slope/Slope.Api.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Skender.Stock.Indicators; - -// SLOPE AND LINEAR REGRESSION (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetSlope( - this IEnumerable results, - int lookbackPeriods) - where T : IReusable - => results - .ToSortedList() - .CalcSlope(lookbackPeriods); -} diff --git a/src/s-z/Slope/Slope.Common.cs b/src/s-z/Slope/Slope.Common.cs deleted file mode 100644 index 375aa15f4..000000000 --- a/src/s-z/Slope/Slope.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// SLOPE AND LINEAR REGRESSION (COMMON) - -public static class Slope -{ - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 1 for Slope/Linear Regression."); - } - } - -} diff --git a/src/s-z/Slope/Slope.Models.cs b/src/s-z/Slope/Slope.Models.cs index f0c6c91a1..b2ea358a0 100644 --- a/src/s-z/Slope/Slope.Models.cs +++ b/src/s-z/Slope/Slope.Models.cs @@ -8,7 +8,7 @@ public record SlopeResult double? StdDev = null, double? RSquared = null, decimal? Line = null // last line segment only -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Slope.Null2NaN(); + public double Value => Slope.Null2NaN(); } diff --git a/src/s-z/Slope/Slope.StaticSeries.cs b/src/s-z/Slope/Slope.StaticSeries.cs index f50694867..6082405ed 100644 --- a/src/s-z/Slope/Slope.StaticSeries.cs +++ b/src/s-z/Slope/Slope.StaticSeries.cs @@ -2,16 +2,16 @@ namespace Skender.Stock.Indicators; // SLOPE AND LINEAR REGRESSION (SERIES) -public static partial class Indicator +public static partial class Slope { - // calculate series - private static List CalcSlope( - this List source, + public static IReadOnlyList ToSlope( + this IReadOnlyList source, int lookbackPeriods) where T : IReusable { // check parameter arguments - Slope.Validate(lookbackPeriods); + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); // initialize int length = source.Count; diff --git a/src/s-z/Slope/Slope.Utilities.cs b/src/s-z/Slope/Slope.Utilities.cs index bbe498ed9..f04ed9b5e 100644 --- a/src/s-z/Slope/Slope.Utilities.cs +++ b/src/s-z/Slope/Slope.Utilities.cs @@ -1,16 +1,18 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// SLOPE AND LINEAR REGRESSION (UTILITIES) + +public static partial class Slope { - // remove recommended periods - /// - public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Slope != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (lookbackPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 1 for Slope/Linear Regression."); + } } } diff --git a/src/s-z/Sma/Sma.Api.cs b/src/s-z/Sma/Sma.Api.cs deleted file mode 100644 index d8b39b6fc..000000000 --- a/src/s-z/Sma/Sma.Api.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Skender.Stock.Indicators; - -// SIMPLE MOVING AVERAGE (API) - -public static partial class Sma -{ - // SERIES, from CHAIN - public static IReadOnlyList GetSma( - this IEnumerable source, - int lookbackPeriods) - where T : IReusable - => source - .ToSortedList() - .CalcSma(lookbackPeriods); - - // OBSERVER, from Chain Provider - public static SmaHub ToSma( - this IChainProvider chainProvider, - int lookbackPeriods) - where TIn : IReusable - => new(chainProvider, lookbackPeriods); -} diff --git a/src/s-z/Sma/Sma.Models.cs b/src/s-z/Sma/Sma.Models.cs index ffdb800bf..44a8885c4 100644 --- a/src/s-z/Sma/Sma.Models.cs +++ b/src/s-z/Sma/Sma.Models.cs @@ -3,7 +3,7 @@ namespace Skender.Stock.Indicators; public record SmaResult( DateTime Timestamp, double? Sma -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Sma.Null2NaN(); + public double Value => Sma.Null2NaN(); } diff --git a/src/s-z/Sma/Sma.StaticSeries.cs b/src/s-z/Sma/Sma.StaticSeries.cs index f06a39124..a0b7a5c39 100644 --- a/src/s-z/Sma/Sma.StaticSeries.cs +++ b/src/s-z/Sma/Sma.StaticSeries.cs @@ -4,17 +4,24 @@ namespace Skender.Stock.Indicators; public static partial class Sma { - internal static List CalcSma( - this List source, + public static IReadOnlyList ToSma( + this IReadOnlyList source, int lookbackPeriods) where T : IReusable { // check parameter arguments - Sma.Validate(lookbackPeriods); + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); // initialize int length = source.Count; - List results = new(length); + SmaResult[] results = new SmaResult[length]; + double[] values = new double[length]; + + for (int i = 0; i < length; i++) + { + values[i] = source[i].Value; + } // roll through source values for (int i = 0; i < length; i++) @@ -25,27 +32,27 @@ internal static List CalcSma( if (i >= lookbackPeriods - 1) { - double sumSma = 0; - for (int p = i - lookbackPeriods + 1; p <= i; p++) + double sum = 0; + int end = i + 1; + int start = end - lookbackPeriods; + + for (int p = start; p < end; p++) { - T ps = source[p]; - sumSma += ps.Value; + sum += source[p].Value; } - sma = sumSma / lookbackPeriods; + sma = sum / lookbackPeriods; } else { sma = double.NaN; } - SmaResult result = new( + results[i] = new SmaResult( Timestamp: s.Timestamp, Sma: sma.NaN2Null()); - - results.Add(result); } - return results; + return new List(results); } } diff --git a/src/s-z/Sma/Sma.StreamHub.cs b/src/s-z/Sma/Sma.StreamHub.cs index 2c0497ae2..150609bb2 100644 --- a/src/s-z/Sma/Sma.StreamHub.cs +++ b/src/s-z/Sma/Sma.StreamHub.cs @@ -2,21 +2,31 @@ namespace Skender.Stock.Indicators; // SIMPLE MOVING AVERAGE (STREAM HUB) -#region hub interface +#region hub interface and initializer + public interface ISmaHub { int LookbackPeriods { get; } } + +public static partial class Sma +{ + public static SmaHub ToSma( + this IChainProvider chainProvider, + int lookbackPeriods) + where TIn : IReusable + => new(chainProvider, lookbackPeriods); +} #endregion -public class SmaHub : ReusableObserver, - IReusableHub, ISmaHub +public class SmaHub + : ChainProvider, ISmaHub where TIn : IReusable { #region constructors private readonly string hubName; - + internal SmaHub( IChainProvider provider, int lookbackPeriods) : base(provider) @@ -35,16 +45,16 @@ internal SmaHub( public override string ToString() => hubName; - internal override void Add(Act act, TIn newIn, int? index) + protected override (SmaResult result, int index) + ToIndicator(TIn item, int? indexHint) { - int i = index ?? Provider.GetIndex(newIn, false); + int i = indexHint ?? ProviderCache.GetIndex(item, true); // candidate result SmaResult r = new( - Timestamp: newIn.Timestamp, - Sma: Sma.Increment(Provider.Results, i, LookbackPeriods).NaN2Null()); + Timestamp: item.Timestamp, + Sma: Sma.Increment(ProviderCache, LookbackPeriods, i).NaN2Null()); - // save and send - Motify(act, r, i); + return (r, i); } } diff --git a/src/s-z/Sma/Sma.Utilities.cs b/src/s-z/Sma/Sma.Utilities.cs index 1880cdd1f..f8700d185 100644 --- a/src/s-z/Sma/Sma.Utilities.cs +++ b/src/s-z/Sma/Sma.Utilities.cs @@ -1,29 +1,104 @@ +using System.Numerics; + namespace Skender.Stock.Indicators; +// SIMPLE MOVING AVERAGE (UTILITIES) + public static partial class Sma { - internal static double Increment( + /// + /// Simple moving average calculation + /// + /// List of chainable values + /// + /// Window to evaluate, prior to 'endIndex' + /// + /// + /// Index position to evaluate or last position when . + /// + /// IReusable (chainable) type + /// + /// Simple moving average or + /// if incalculable + /// values are in range. + /// + internal static double? Average( this IReadOnlyList values, - int endIndex, - int lookbackPeriods) + int lookbackPeriods, + int? endIndex = null) where T : IReusable - { - int offset = lookbackPeriods - 1; - if (endIndex < offset || endIndex >= values.Count) + // TODO: unused SMA utility, either make public or remove + + => Increment( + values, + lookbackPeriods, + endIndex ?? values.Count - 1) + .NaN2Null(); + + /// + /// Simple moving average calculation + /// + /// List of chainable values + /// Window to evaluate, prior to 'endIndex' + /// Index position to evaluate. + /// IReusable (chainable) type + /// + /// Simple moving average or + /// when incalculable. + /// + internal static double Increment( + IReadOnlyList source, + int lookbackPeriods, + int endIndex) + where T : IReusable + { + if (endIndex < lookbackPeriods - 1 || endIndex >= source.Count) { return double.NaN; } double sum = 0; - int startIndex = endIndex - offset; - - for (int i = startIndex; i <= endIndex; i++) + for (int i = endIndex - lookbackPeriods + 1; i <= endIndex; i++) { - sum += values[i].Value; + sum += source[i].Value; } return sum / lookbackPeriods; + + // TODO: apply this SMA increment method more widely in other indicators (see EMA example) + } + + internal static double[] Increment(this double[] prices, int period) + { + // TODO: is this used (probably just an experiment, has rounding errors) + + int count = prices.Length - period + 1; + double[] sma = new double[count]; + + int simdWidth = Vector.Count; + for (int i = 0; i < count; i++) + { + Vector sumVector = Vector.Zero; + + int j; + for (j = 0; j <= period - simdWidth; j += simdWidth) + { + Vector priceVector = new(prices, i + j); + sumVector += priceVector; + } + + double sum = 0; + for (; j < period; j++) // remainder loop + { + sum += prices[i + j]; + } + sum += Vector.Dot(sumVector, Vector.One); + + sma[i] = sum / period; + } + + return sma; } // parameter validation diff --git a/src/s-z/SmaAnalysis/SmaAnalysis.Api.cs b/src/s-z/SmaAnalysis/SmaAnalysis.Api.cs deleted file mode 100644 index 0dfe24f95..000000000 --- a/src/s-z/SmaAnalysis/SmaAnalysis.Api.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Skender.Stock.Indicators; - -// SIMPLE MOVING AVERAGE (API) -// with extended analysis - -public static partial class Indicator -{ - // ANALYSIS, from CHAIN - public static IReadOnlyList GetSmaAnalysis( - this IEnumerable source, - int lookbackPeriods) - where T : IReusable - => source - .ToSortedList() - .CalcSmaAnalysis(lookbackPeriods); -} diff --git a/src/s-z/SmaAnalysis/SmaAnalysis.StaticSeries.cs b/src/s-z/SmaAnalysis/SmaAnalysis.StaticSeries.cs index cf9486374..d43770795 100644 --- a/src/s-z/SmaAnalysis/SmaAnalysis.StaticSeries.cs +++ b/src/s-z/SmaAnalysis/SmaAnalysis.StaticSeries.cs @@ -1,16 +1,17 @@ namespace Skender.Stock.Indicators; // SIMPLE MOVING AVERAGE (ANALYSIS) -public static partial class Indicator + +public static partial class Sma { - private static List CalcSmaAnalysis( - this List source, + public static IReadOnlyList ToSmaAnalysis( + this IReadOnlyList source, int lookbackPeriods) where T : IReusable { // initialize List results = source - .CalcSma(lookbackPeriods) + .ToSma(lookbackPeriods) .Select(s => new SmaAnalysis(s.Timestamp, s.Sma)) .ToList(); diff --git a/src/s-z/Smi/Smi.Api.cs b/src/s-z/Smi/Smi.Api.cs deleted file mode 100644 index 6bfefa7e3..000000000 --- a/src/s-z/Smi/Smi.Api.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STOCHASTIC MOMENTUM INDEX (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetSmi( - this IEnumerable quotes, - int lookbackPeriods = 13, - int firstSmoothPeriods = 25, - int secondSmoothPeriods = 2, - int signalPeriods = 3) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcSmi( - lookbackPeriods, - firstSmoothPeriods, - secondSmoothPeriods, - signalPeriods); -} diff --git a/src/s-z/Smi/Smi.Common.cs b/src/s-z/Smi/Smi.Common.cs deleted file mode 100644 index 0549703fe..000000000 --- a/src/s-z/Smi/Smi.Common.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STOCHASTIC MOMENTUM INDEX (COMMON) - -public static class Smi -{ - // parameter validation - internal static void Validate( - int lookbackPeriods, - int firstSmoothPeriods, - int secondSmoothPeriods, - int signalPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for SMI."); - } - - if (firstSmoothPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(firstSmoothPeriods), firstSmoothPeriods, - "Smoothing periods must be greater than 0 for SMI."); - } - - if (secondSmoothPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(secondSmoothPeriods), secondSmoothPeriods, - "Smoothing periods must be greater than 0 for SMI."); - } - - if (signalPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, - "Signal periods must be greater than 0 for SMI."); - } - } - -} diff --git a/src/s-z/Smi/Smi.Models.cs b/src/s-z/Smi/Smi.Models.cs index 8291a51ac..406f4af07 100644 --- a/src/s-z/Smi/Smi.Models.cs +++ b/src/s-z/Smi/Smi.Models.cs @@ -5,7 +5,7 @@ public record SmiResult DateTime Timestamp, double? Smi, double? Signal -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Smi.Null2NaN(); + public double Value => Smi.Null2NaN(); } diff --git a/src/s-z/Smi/Smi.StaticSeries.cs b/src/s-z/Smi/Smi.StaticSeries.cs index bd7fe49ef..9d80f0be2 100644 --- a/src/s-z/Smi/Smi.StaticSeries.cs +++ b/src/s-z/Smi/Smi.StaticSeries.cs @@ -2,17 +2,31 @@ namespace Skender.Stock.Indicators; // STOCHASTIC MOMENTUM INDEX (SERIES) -public static partial class Indicator +public static partial class Smi { + public static IReadOnlyList ToSmi( + this IReadOnlyList quotes, + int lookbackPeriods = 13, + int firstSmoothPeriods = 25, + int secondSmoothPeriods = 2, + int signalPeriods = 3) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcSmi( + lookbackPeriods, + firstSmoothPeriods, + secondSmoothPeriods, + signalPeriods); + private static List CalcSmi( - this List source, + this IReadOnlyList source, int lookbackPeriods, int firstSmoothPeriods, int secondSmoothPeriods, int signalPeriods) { // check parameter arguments - Smi.Validate( + Validate( lookbackPeriods, firstSmoothPeriods, secondSmoothPeriods, diff --git a/src/s-z/Smi/Smi.Utilities.cs b/src/s-z/Smi/Smi.Utilities.cs index 77b41759e..b22632495 100644 --- a/src/s-z/Smi/Smi.Utilities.cs +++ b/src/s-z/Smi/Smi.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// STOCHASTIC MOMENTUM INDEX (UTILITIES) + +public static partial class Smi { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -13,4 +15,37 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(removePeriods + 2 + 100); } + + // parameter validation + internal static void Validate( + int lookbackPeriods, + int firstSmoothPeriods, + int secondSmoothPeriods, + int signalPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for SMI."); + } + + if (firstSmoothPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(firstSmoothPeriods), firstSmoothPeriods, + "Smoothing periods must be greater than 0 for SMI."); + } + + if (secondSmoothPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(secondSmoothPeriods), secondSmoothPeriods, + "Smoothing periods must be greater than 0 for SMI."); + } + + if (signalPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, + "Signal periods must be greater than 0 for SMI."); + } + } } diff --git a/src/s-z/Smma/Smma.Api.cs b/src/s-z/Smma/Smma.Api.cs deleted file mode 100644 index d614d06f9..000000000 --- a/src/s-z/Smma/Smma.Api.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Skender.Stock.Indicators; - -// SMOOTHED MOVING AVERAGE (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetSmma( - this IEnumerable results, - int lookbackPeriods) - where T : IReusable - => results - .ToSortedList() - .CalcSmma(lookbackPeriods); -} diff --git a/src/s-z/Smma/Smma.Common.cs b/src/s-z/Smma/Smma.Common.cs deleted file mode 100644 index af39121b2..000000000 --- a/src/s-z/Smma/Smma.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// SMOOTHED MOVING AVERAGE (COMMON) - -public static class Smma -{ - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for SMMA."); - } - } - -} diff --git a/src/s-z/Smma/Smma.Models.cs b/src/s-z/Smma/Smma.Models.cs index c4e926828..882392ea4 100644 --- a/src/s-z/Smma/Smma.Models.cs +++ b/src/s-z/Smma/Smma.Models.cs @@ -4,7 +4,7 @@ public record SmmaResult ( DateTime Timestamp, double? Smma = null -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Smma.Null2NaN(); + public double Value => Smma.Null2NaN(); } diff --git a/src/s-z/Smma/Smma.StaticSeries.cs b/src/s-z/Smma/Smma.StaticSeries.cs index 65c91e02d..e2503ef6c 100644 --- a/src/s-z/Smma/Smma.StaticSeries.cs +++ b/src/s-z/Smma/Smma.StaticSeries.cs @@ -2,16 +2,16 @@ namespace Skender.Stock.Indicators; // SMOOTHED MOVING AVERAGE (SERIES) -public static partial class Indicator +public static partial class Smma { - // calculate series - private static List CalcSmma( - this List source, + public static IReadOnlyList ToSmma( + this IReadOnlyList source, int lookbackPeriods) where T : IReusable { // check parameter arguments - Smma.Validate(lookbackPeriods); + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); // initialize int length = source.Count; diff --git a/src/s-z/Smma/Smma.Utilities.cs b/src/s-z/Smma/Smma.Utilities.cs index 5681a3e18..5b5336023 100644 --- a/src/s-z/Smma/Smma.Utilities.cs +++ b/src/s-z/Smma/Smma.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// SMOOTHED MOVING AVERAGE (UTILITIES) + +public static partial class Smma { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int n = results .ToList() @@ -13,4 +15,16 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(n + 100); } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for SMMA."); + } + } } diff --git a/src/s-z/StarcBands/StarcBands.Api.cs b/src/s-z/StarcBands/StarcBands.Api.cs deleted file mode 100644 index 572e2fddb..000000000 --- a/src/s-z/StarcBands/StarcBands.Api.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STARC BANDS (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetStarcBands( - this IEnumerable quotes, - int smaPeriods, - double multiplier = 2, - int atrPeriods = 10) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcStarcBands(smaPeriods, multiplier, atrPeriods); -} diff --git a/src/s-z/StarcBands/StarcBands.Common.cs b/src/s-z/StarcBands/StarcBands.Common.cs deleted file mode 100644 index 861f3d783..000000000 --- a/src/s-z/StarcBands/StarcBands.Common.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STARC BANDS (COMMON) - -public static class StarcBands -{ - // parameter validation - internal static void Validate( - int smaPeriods, - double multiplier, - int atrPeriods) - { - // check parameter arguments - if (smaPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(smaPeriods), smaPeriods, - "EMA periods must be greater than 1 for STARC Bands."); - } - - if (atrPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(atrPeriods), atrPeriods, - "ATR periods must be greater than 1 for STARC Bands."); - } - - if (multiplier <= 0) - { - throw new ArgumentOutOfRangeException(nameof(multiplier), multiplier, - "Multiplier must be greater than 0 for STARC Bands."); - } - } - -} diff --git a/src/s-z/StarcBands/StarcBands.StaticSeries.cs b/src/s-z/StarcBands/StarcBands.StaticSeries.cs index 92bb409f2..3a28837e4 100644 --- a/src/s-z/StarcBands/StarcBands.StaticSeries.cs +++ b/src/s-z/StarcBands/StarcBands.StaticSeries.cs @@ -2,22 +2,31 @@ namespace Skender.Stock.Indicators; // STARC BANDS (SERIES) -public static partial class Indicator +public static partial class StarcBands { + public static IReadOnlyList ToStarcBands( + this IReadOnlyList quotes, + int smaPeriods, + double multiplier = 2, + int atrPeriods = 10) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcStarcBands(smaPeriods, multiplier, atrPeriods); + private static List CalcStarcBands( - this List source, + this IReadOnlyList source, int smaPeriods, double multiplier, int atrPeriods) { // check parameter arguments - StarcBands.Validate(smaPeriods, multiplier, atrPeriods); + Validate(smaPeriods, multiplier, atrPeriods); // initialize int length = source.Count; List results = new(length); List atrResults = source.CalcAtr(atrPeriods); - List smaResults = source.CalcSma(smaPeriods); + IReadOnlyList smaResults = source.ToSma(smaPeriods); // roll through source values for (int i = 0; i < length; i++) @@ -28,8 +37,8 @@ private static List CalcStarcBands( results.Add(new( Timestamp: s.Timestamp, Centerline: s.Sma, - UpperBand: s.Sma + multiplier * a.Atr, - LowerBand: s.Sma - multiplier * a.Atr)); + UpperBand: s.Sma + (multiplier * a.Atr), + LowerBand: s.Sma - (multiplier * a.Atr))); } return results; diff --git a/src/s-z/StarcBands/StarcBands.Utilities.cs b/src/s-z/StarcBands/StarcBands.Utilities.cs index 8eb9c85c1..c30e4d801 100644 --- a/src/s-z/StarcBands/StarcBands.Utilities.cs +++ b/src/s-z/StarcBands/StarcBands.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// STARC BANDS (UTILITIES) + +public static partial class StarcBands { // CONDENSE (REMOVE null results) - /// + /// public static IReadOnlyList Condense( - this IEnumerable results) + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -18,9 +20,9 @@ public static IReadOnlyList Condense( } // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int n = results .ToList() @@ -28,4 +30,30 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(n + 150); } + + // parameter validation + internal static void Validate( + int smaPeriods, + double multiplier, + int atrPeriods) + { + // check parameter arguments + if (smaPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(smaPeriods), smaPeriods, + "EMA periods must be greater than 1 for STARC Bands."); + } + + if (atrPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(atrPeriods), atrPeriods, + "ATR periods must be greater than 1 for STARC Bands."); + } + + if (multiplier <= 0) + { + throw new ArgumentOutOfRangeException(nameof(multiplier), multiplier, + "Multiplier must be greater than 0 for STARC Bands."); + } + } } diff --git a/src/s-z/Stc/Stc.Api.cs b/src/s-z/Stc/Stc.Api.cs deleted file mode 100644 index 199a5c519..000000000 --- a/src/s-z/Stc/Stc.Api.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Skender.Stock.Indicators; - -// SCHAFF TREND CYCLE (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetStc( - this IEnumerable results, - int cyclePeriods = 10, - int fastPeriods = 23, - int slowPeriods = 50) - where T : IReusable - => results - .ToSortedList() - .CalcStc(cyclePeriods, fastPeriods, slowPeriods); -} diff --git a/src/s-z/Stc/Stc.Common.cs b/src/s-z/Stc/Stc.Common.cs deleted file mode 100644 index 762caa2e4..000000000 --- a/src/s-z/Stc/Stc.Common.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Skender.Stock.Indicators; - -// SCHAFF TREND CYCLE (COMMON) - -public static class Stc -{ - // parameter validation - internal static void Validate( - int cyclePeriods, - int fastPeriods, - int slowPeriods) - { - // check parameter arguments - if (cyclePeriods < 0) - { - throw new ArgumentOutOfRangeException(nameof(cyclePeriods), cyclePeriods, - "Trend Cycle periods must be greater than or equal to 0 for STC."); - } - - if (fastPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(fastPeriods), fastPeriods, - "Fast periods must be greater than 0 for STC."); - } - - if (slowPeriods <= fastPeriods) - { - throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, - "Slow periods must be greater than the fast period for STC."); - } - } - -} diff --git a/src/s-z/Stc/Stc.Models.cs b/src/s-z/Stc/Stc.Models.cs index b9d8a4577..3a3b78ee8 100644 --- a/src/s-z/Stc/Stc.Models.cs +++ b/src/s-z/Stc/Stc.Models.cs @@ -4,7 +4,7 @@ public record StcResult ( DateTime Timestamp, double? Stc -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Stc.Null2NaN(); + public double Value => Stc.Null2NaN(); } diff --git a/src/s-z/Stc/Stc.StaticSeries.cs b/src/s-z/Stc/Stc.StaticSeries.cs index 5873af1a9..bb103d5bb 100644 --- a/src/s-z/Stc/Stc.StaticSeries.cs +++ b/src/s-z/Stc/Stc.StaticSeries.cs @@ -2,17 +2,18 @@ namespace Skender.Stock.Indicators; // SCHAFF TREND CYCLE (SERIES) -public static partial class Indicator +public static partial class Stc { - private static List CalcStc( - this List source, - int cyclePeriods, - int fastPeriods, - int slowPeriods) + public static IReadOnlyList ToStc( + this IReadOnlyList source, + int cyclePeriods = 10, + int fastPeriods = 23, + int slowPeriods = 50) where T : IReusable { // check parameter arguments - Stc.Validate(cyclePeriods, fastPeriods, slowPeriods); + ArgumentNullException.ThrowIfNull(source); + Validate(cyclePeriods, fastPeriods, slowPeriods); // initialize results int length = source.Count; @@ -20,7 +21,7 @@ private static List CalcStc( // get stochastic of macd IReadOnlyList stochMacd = source - .CalcMacd(fastPeriods, slowPeriods, 1) + .ToMacd(fastPeriods, slowPeriods, 1) .Select(x => new QuoteD( x.Timestamp, 0, x.Macd.Null2NaN(), diff --git a/src/s-z/Stc/Stc.Utilities.cs b/src/s-z/Stc/Stc.Utilities.cs index 96763e7a9..ecc8a0004 100644 --- a/src/s-z/Stc/Stc.Utilities.cs +++ b/src/s-z/Stc/Stc.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// SCHAFF TREND CYCLE (UTILITIES) + +public static partial class Stc { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int n = results .ToList() @@ -13,4 +15,30 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(n + 250); } + + // parameter validation + internal static void Validate( + int cyclePeriods, + int fastPeriods, + int slowPeriods) + { + // check parameter arguments + if (cyclePeriods < 0) + { + throw new ArgumentOutOfRangeException(nameof(cyclePeriods), cyclePeriods, + "Trend Cycle periods must be greater than or equal to 0 for STC."); + } + + if (fastPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(fastPeriods), fastPeriods, + "Fast periods must be greater than 0 for STC."); + } + + if (slowPeriods <= fastPeriods) + { + throw new ArgumentOutOfRangeException(nameof(slowPeriods), slowPeriods, + "Slow periods must be greater than the fast period for STC."); + } + } } diff --git a/src/s-z/StdDev/StdDev.Api.cs b/src/s-z/StdDev/StdDev.Api.cs deleted file mode 100644 index 3488c082a..000000000 --- a/src/s-z/StdDev/StdDev.Api.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STANDARD DEVIATION (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetStdDev( - this IEnumerable results, - int lookbackPeriods) - where T : IReusable - => results - .ToSortedList() - .CalcStdDev(lookbackPeriods); -} diff --git a/src/s-z/StdDev/StdDev.Common.cs b/src/s-z/StdDev/StdDev.Common.cs deleted file mode 100644 index 2224d9f4c..000000000 --- a/src/s-z/StdDev/StdDev.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STANDARD DEVIATION (COMMON) - -public static class StdDev -{ - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 1 for Standard Deviation."); - } - } - -} diff --git a/src/s-z/StdDev/StdDev.Models.cs b/src/s-z/StdDev/StdDev.Models.cs index 4258f9777..3cd6e455c 100644 --- a/src/s-z/StdDev/StdDev.Models.cs +++ b/src/s-z/StdDev/StdDev.Models.cs @@ -6,7 +6,7 @@ public record StdDevResult double? StdDev, double? Mean, double? ZScore -) : Reusable(Timestamp) +) : IReusable { - public override double Value => StdDev.Null2NaN(); + public double Value => StdDev.Null2NaN(); } diff --git a/src/s-z/StdDev/StdDev.StaticSeries.cs b/src/s-z/StdDev/StdDev.StaticSeries.cs index 6bc8bc173..f1a9aba47 100644 --- a/src/s-z/StdDev/StdDev.StaticSeries.cs +++ b/src/s-z/StdDev/StdDev.StaticSeries.cs @@ -2,15 +2,16 @@ namespace Skender.Stock.Indicators; // STANDARD DEVIATION (SERIES) -public static partial class Indicator +public static partial class StdDev { - private static List CalcStdDev( - this List source, + public static IReadOnlyList ToStdDev( + this IReadOnlyList source, int lookbackPeriods) where T : IReusable { // check parameter arguments - StdDev.Validate(lookbackPeriods); + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); // initialize int length = source.Count; diff --git a/src/s-z/StdDev/StdDev.Utilities.cs b/src/s-z/StdDev/StdDev.Utilities.cs index 90648f99b..bd42630e0 100644 --- a/src/s-z/StdDev/StdDev.Utilities.cs +++ b/src/s-z/StdDev/StdDev.Utilities.cs @@ -1,16 +1,18 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// STANDARD DEVIATION (UTILITIES) + +public static partial class StdDev { - // remove recommended periods - /// - public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.StdDev != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (lookbackPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 1 for Standard Deviation."); + } } } diff --git a/src/s-z/StdDevChannels/StdDevChannels.Api.cs b/src/s-z/StdDevChannels/StdDevChannels.Api.cs deleted file mode 100644 index d063b151b..000000000 --- a/src/s-z/StdDevChannels/StdDevChannels.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STANDARD DEVIATION CHANNELS (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetStdDevChannels( - this IEnumerable results, - int? lookbackPeriods = 20, - double stdDeviations = 2) - where T : IReusable - => results - .ToSortedList() - .CalcStdDevChannels(lookbackPeriods, stdDeviations); -} diff --git a/src/s-z/StdDevChannels/StdDevChannels.Common.cs b/src/s-z/StdDevChannels/StdDevChannels.Common.cs deleted file mode 100644 index bf56037f5..000000000 --- a/src/s-z/StdDevChannels/StdDevChannels.Common.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STANDARD DEVIATION CHANNELS (COMMON) - -public static class StdDevChannels -{ - // parameter validation - internal static void Validate( - int? lookbackPeriods, - double stdDeviations) - { - // check parameter arguments - if (lookbackPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 1 for Standard Deviation Channels."); - } - - if (stdDeviations <= 0) - { - throw new ArgumentOutOfRangeException(nameof(stdDeviations), stdDeviations, - "Standard Deviations must be greater than 0 for Standard Deviation Channels."); - } - } - -} diff --git a/src/s-z/StdDevChannels/StdDevChannels.StaticSeries.cs b/src/s-z/StdDevChannels/StdDevChannels.StaticSeries.cs index 2e8bece73..067c32127 100644 --- a/src/s-z/StdDevChannels/StdDevChannels.StaticSeries.cs +++ b/src/s-z/StdDevChannels/StdDevChannels.StaticSeries.cs @@ -1,26 +1,25 @@ namespace Skender.Stock.Indicators; -// STANDARD DEVIATION CHANNELS +// STANDARD DEVIATION CHANNELS (SERIES) -public static partial class Indicator +public static partial class StdDevChannels { - private static List CalcStdDevChannels( - this List source, - int? lookbackPeriods, - double stdDeviations) + public static IReadOnlyList ToStdDevChannels( + this IReadOnlyList source, + int? lookbackPeriods = 20, + double stdDeviations = 2) where T : IReusable { - // assume whole quotes when lookback is null - lookbackPeriods ??= source.Count; - // check parameter arguments - StdDevChannels.Validate(lookbackPeriods, stdDeviations); + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods, stdDeviations); // initialize + lookbackPeriods ??= source.Count; // assume whole quotes when null int length = source.Count; - List slopeResults = source - .CalcSlope((int)lookbackPeriods); + IReadOnlyList slopeResults = source + .ToSlope((int)lookbackPeriods); List results = slopeResults .Select(x => new StdDevChannelsResult(x.Timestamp)) diff --git a/src/s-z/StdDevChannels/StdDevChannels.Utilities.cs b/src/s-z/StdDevChannels/StdDevChannels.Utilities.cs index 881f7381c..70d382c48 100644 --- a/src/s-z/StdDevChannels/StdDevChannels.Utilities.cs +++ b/src/s-z/StdDevChannels/StdDevChannels.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// STANDARD DEVIATION CHANNELS (UTILITIES) + +public static partial class StdDevChannels { // CONDENSE (REMOVE null results) - /// + /// public static IReadOnlyList Condense( - this IEnumerable results) + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -21,9 +23,9 @@ x.UpperChannel is null } // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -31,4 +33,23 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + int? lookbackPeriods, + double stdDeviations) + { + // check parameter arguments + if (lookbackPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 1 for Standard Deviation Channels."); + } + + if (stdDeviations <= 0) + { + throw new ArgumentOutOfRangeException(nameof(stdDeviations), stdDeviations, + "Standard Deviations must be greater than 0 for Standard Deviation Channels."); + } + } } diff --git a/src/s-z/Stoch/Stoch.Api.cs b/src/s-z/Stoch/Stoch.Api.cs deleted file mode 100644 index 6b4d434e9..000000000 --- a/src/s-z/Stoch/Stoch.Api.cs +++ /dev/null @@ -1,41 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STOCHASTIC OSCILLATOR (API) -public static partial class Indicator -{ - // SERIES, from TQuote (standard) - /// - /// - public static IReadOnlyList GetStoch( - this IEnumerable quotes, - int lookbackPeriods = 14, - int signalPeriods = 3, - int smoothPeriods = 3) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcStoch( - lookbackPeriods, - signalPeriods, - smoothPeriods, 3, 2, MaType.SMA); - - // SERIES, from TQuote (extended) - /// - /// - public static IReadOnlyList GetStoch( - this IEnumerable quotes, - int lookbackPeriods, - int signalPeriods, - int smoothPeriods, - double kFactor, - double dFactor, - MaType movingAverageType) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcStoch( - lookbackPeriods, - signalPeriods, - smoothPeriods, - kFactor, - dFactor, - movingAverageType); -} diff --git a/src/s-z/Stoch/Stoch.Common.cs b/src/s-z/Stoch/Stoch.Common.cs deleted file mode 100644 index dbe45afd3..000000000 --- a/src/s-z/Stoch/Stoch.Common.cs +++ /dev/null @@ -1,54 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STOCHASTIC OSCILLATOR (COMMON) - -public static class Stoch -{ - // parameter validation - internal static void Validate( - int lookbackPeriods, - int signalPeriods, - int smoothPeriods, - double kFactor, - double dFactor, - MaType movingAverageType) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Stochastic."); - } - - if (signalPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, - "Signal periods must be greater than 0 for Stochastic."); - } - - if (smoothPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(smoothPeriods), smoothPeriods, - "Smooth periods must be greater than 0 for Stochastic."); - } - - if (kFactor <= 0) - { - throw new ArgumentOutOfRangeException(nameof(kFactor), kFactor, - "kFactor must be greater than 0 for Stochastic."); - } - - if (dFactor <= 0) - { - throw new ArgumentOutOfRangeException(nameof(dFactor), dFactor, - "dFactor must be greater than 0 for Stochastic."); - } - - if (movingAverageType is not MaType.SMA and not MaType.SMMA) - { - throw new ArgumentOutOfRangeException(nameof(dFactor), dFactor, - "Stochastic only supports SMA and SMMA moving average types."); - } - } - -} diff --git a/src/s-z/Stoch/Stoch.Models.cs b/src/s-z/Stoch/Stoch.Models.cs index 41e20f00a..4e060a287 100644 --- a/src/s-z/Stoch/Stoch.Models.cs +++ b/src/s-z/Stoch/Stoch.Models.cs @@ -1,16 +1,14 @@ namespace Skender.Stock.Indicators; -/// -/// public record StochResult ( DateTime Timestamp, double? Oscillator, double? Signal, double? PercentJ -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Oscillator.Null2NaN(); + public double Value => Oscillator.Null2NaN(); // aliases public double? K => Oscillator; diff --git a/src/s-z/Stoch/Stoch.StaticSeries.cs b/src/s-z/Stoch/Stoch.StaticSeries.cs index 83f3e441d..b272dfb4a 100644 --- a/src/s-z/Stoch/Stoch.StaticSeries.cs +++ b/src/s-z/Stoch/Stoch.StaticSeries.cs @@ -2,10 +2,40 @@ namespace Skender.Stock.Indicators; // STOCHASTIC OSCILLATOR (SERIES) -public static partial class Indicator +public static partial class Stoch { - private static List CalcStoch( - this List source, + public static IReadOnlyList ToStoch( + this IReadOnlyList quotes, + int lookbackPeriods = 14, + int signalPeriods = 3, + int smoothPeriods = 3) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcStoch( + lookbackPeriods, + signalPeriods, + smoothPeriods, 3, 2, MaType.SMA); + + public static IReadOnlyList ToStoch( + this IReadOnlyList quotes, + int lookbackPeriods, + int signalPeriods, + int smoothPeriods, + double kFactor, + double dFactor, + MaType movingAverageType) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcStoch( + lookbackPeriods, + signalPeriods, + smoothPeriods, + kFactor, + dFactor, + movingAverageType); + + internal static List CalcStoch( + this IReadOnlyList source, int lookbackPeriods, int signalPeriods, int smoothPeriods, @@ -14,7 +44,7 @@ private static List CalcStoch( MaType movingAverageType) { // check parameter arguments - Stoch.Validate( + Validate( lookbackPeriods, signalPeriods, smoothPeriods, kFactor, dFactor, movingAverageType); @@ -109,7 +139,7 @@ private static List CalcStoch( prevK = o[i]; } - k[i] = (prevK * (smoothPeriods - 1) + o[i]) / smoothPeriods; + k[i] = ((prevK * (smoothPeriods - 1)) + o[i]) / smoothPeriods; prevK = k[i]; break; } @@ -160,7 +190,7 @@ private static List CalcStoch( prevD = k[i]; } - double d = (prevD * (signalPeriods - 1) + k[i]) / signalPeriods; + double d = ((prevD * (signalPeriods - 1)) + k[i]) / signalPeriods; signal = d; prevD = d; break; @@ -179,7 +209,7 @@ private static List CalcStoch( Timestamp: q.Timestamp, Oscillator: oscillator.NaN2Null(), Signal: signal.NaN2Null(), - PercentJ: (kFactor * oscillator - dFactor * signal).NaN2Null())); + PercentJ: ((kFactor * oscillator) - (dFactor * signal)).NaN2Null())); } return results; } diff --git a/src/s-z/Stoch/Stoch.Utilities.cs b/src/s-z/Stoch/Stoch.Utilities.cs index 61df7f848..bb1fcc708 100644 --- a/src/s-z/Stoch/Stoch.Utilities.cs +++ b/src/s-z/Stoch/Stoch.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// STOCHASTIC OSCILLATOR (UTILITIES) + +public static partial class Stoch { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -13,4 +15,51 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + int lookbackPeriods, + int signalPeriods, + int smoothPeriods, + double kFactor, + double dFactor, + MaType movingAverageType) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Stochastic."); + } + + if (signalPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, + "Signal periods must be greater than 0 for Stochastic."); + } + + if (smoothPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(smoothPeriods), smoothPeriods, + "Smooth periods must be greater than 0 for Stochastic."); + } + + if (kFactor <= 0) + { + throw new ArgumentOutOfRangeException(nameof(kFactor), kFactor, + "kFactor must be greater than 0 for Stochastic."); + } + + if (dFactor <= 0) + { + throw new ArgumentOutOfRangeException(nameof(dFactor), dFactor, + "dFactor must be greater than 0 for Stochastic."); + } + + if (movingAverageType is not MaType.SMA and not MaType.SMMA) + { + throw new ArgumentOutOfRangeException(nameof(dFactor), dFactor, + "Stochastic only supports SMA and SMMA moving average types."); + } + } } diff --git a/src/s-z/StochRsi/StochRsi.Api.cs b/src/s-z/StochRsi/StochRsi.Api.cs deleted file mode 100644 index 83c3985ae..000000000 --- a/src/s-z/StochRsi/StochRsi.Api.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STOCHASTIC RSI (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetStochRsi( - this IEnumerable results, - int rsiPeriods, - int stochPeriods, - int signalPeriods, - int smoothPeriods = 1) - where T : IReusable - => results - .ToSortedList() - .CalcStochRsi( - rsiPeriods, - stochPeriods, - signalPeriods, - smoothPeriods); -} diff --git a/src/s-z/StochRsi/StochRsi.Common.cs b/src/s-z/StochRsi/StochRsi.Common.cs deleted file mode 100644 index 3f138708b..000000000 --- a/src/s-z/StochRsi/StochRsi.Common.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace Skender.Stock.Indicators; - -// STOCHASTIC RSI (COMMON) - -public static class StochRsi -{ - // parameter validation - internal static void Validate( - int rsiPeriods, - int stochPeriods, - int signalPeriods, - int smoothPeriods) - { - // check parameter arguments - if (rsiPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(rsiPeriods), rsiPeriods, - "RSI periods must be greater than 0 for Stochastic RSI."); - } - - if (stochPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(stochPeriods), stochPeriods, - "STOCH periods must be greater than 0 for Stochastic RSI."); - } - - if (signalPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, - "Signal periods must be greater than 0 for Stochastic RSI."); - } - - if (smoothPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(smoothPeriods), smoothPeriods, - "Smooth periods must be greater than 0 for Stochastic RSI."); - } - } - -} diff --git a/src/s-z/StochRsi/StochRsi.Models.cs b/src/s-z/StochRsi/StochRsi.Models.cs index ec2670859..cad4f480a 100644 --- a/src/s-z/StochRsi/StochRsi.Models.cs +++ b/src/s-z/StochRsi/StochRsi.Models.cs @@ -5,7 +5,7 @@ public record StochRsiResult DateTime Timestamp, double? StochRsi = null, double? Signal = null -) : Reusable(Timestamp) +) : IReusable { - public override double Value => StochRsi.Null2NaN(); + public double Value => StochRsi.Null2NaN(); } diff --git a/src/s-z/StochRsi/StochRsi.StaticSeries.cs b/src/s-z/StochRsi/StochRsi.StaticSeries.cs index 9afe0b389..85837f5bf 100644 --- a/src/s-z/StochRsi/StochRsi.StaticSeries.cs +++ b/src/s-z/StochRsi/StochRsi.StaticSeries.cs @@ -2,18 +2,19 @@ namespace Skender.Stock.Indicators; // STOCHASTIC RSI (SERIES) -public static partial class Indicator +public static partial class StochRsi { - private static List CalcStochRsi( - this List source, + public static IReadOnlyList ToStochRsi( + this IReadOnlyList source, int rsiPeriods, int stochPeriods, int signalPeriods, - int smoothPeriods) + int smoothPeriods = 1) where T : IReusable { // check parameter arguments - StochRsi.Validate(rsiPeriods, stochPeriods, signalPeriods, smoothPeriods); + ArgumentNullException.ThrowIfNull(source); + Validate(rsiPeriods, stochPeriods, signalPeriods, smoothPeriods); // initialize results int length = source.Count; @@ -30,8 +31,8 @@ private static List CalcStochRsi( // get Stochastic of RSI List stoResults = source - .CalcRsi(rsiPeriods) - .Remove(Math.Min(rsiPeriods, length)) // TODO: still need to Remove() here? + .ToRsi(rsiPeriods) + .Remove(Math.Min(rsiPeriods, length)) // TODO: still need to Remove() here, or auto-healing? .Select(x => new QuoteD( Timestamp: x.Timestamp, Open: 0, diff --git a/src/s-z/StochRsi/StochRsi.Utilities.cs b/src/s-z/StochRsi/StochRsi.Utilities.cs index 03ace36c6..d7d71e061 100644 --- a/src/s-z/StochRsi/StochRsi.Utilities.cs +++ b/src/s-z/StochRsi/StochRsi.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// STOCHASTIC RSI (UTILITIES) + +public static partial class StochRsi { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int n = results .ToList() @@ -13,4 +15,37 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(n + 100); } + + // parameter validation + internal static void Validate( + int rsiPeriods, + int stochPeriods, + int signalPeriods, + int smoothPeriods) + { + // check parameter arguments + if (rsiPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(rsiPeriods), rsiPeriods, + "RSI periods must be greater than 0 for Stochastic RSI."); + } + + if (stochPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(stochPeriods), stochPeriods, + "STOCH periods must be greater than 0 for Stochastic RSI."); + } + + if (signalPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, + "Signal periods must be greater than 0 for Stochastic RSI."); + } + + if (smoothPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(smoothPeriods), smoothPeriods, + "Smooth periods must be greater than 0 for Stochastic RSI."); + } + } } diff --git a/src/s-z/SuperTrend/SuperTrend.Api.cs b/src/s-z/SuperTrend/SuperTrend.Api.cs deleted file mode 100644 index fba7807fd..000000000 --- a/src/s-z/SuperTrend/SuperTrend.Api.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Skender.Stock.Indicators; - -// SUPERTREND (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetSuperTrend( - this IEnumerable quotes, - int lookbackPeriods = 10, - double multiplier = 3) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcSuperTrend(lookbackPeriods, multiplier); -} diff --git a/src/s-z/SuperTrend/SuperTrend.Common.cs b/src/s-z/SuperTrend/SuperTrend.Common.cs deleted file mode 100644 index 3125b29cb..000000000 --- a/src/s-z/SuperTrend/SuperTrend.Common.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Skender.Stock.Indicators; - -// SUPERTREND (COMMON) - -public static class SuperTrend -{ - // parameter validation - internal static void Validate( - int lookbackPeriods, - double multiplier) - { - // check parameter arguments - if (lookbackPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 1 for SuperTrend."); - } - - if (multiplier <= 0) - { - throw new ArgumentOutOfRangeException(nameof(multiplier), multiplier, - "Multiplier must be greater than 0 for SuperTrend."); - } - } - -} diff --git a/src/s-z/SuperTrend/SuperTrend.StaticSeries.cs b/src/s-z/SuperTrend/SuperTrend.StaticSeries.cs index 9bb2f8265..98e6fbee6 100644 --- a/src/s-z/SuperTrend/SuperTrend.StaticSeries.cs +++ b/src/s-z/SuperTrend/SuperTrend.StaticSeries.cs @@ -2,15 +2,23 @@ namespace Skender.Stock.Indicators; // SUPERTREND (SERIES) -public static partial class Indicator +public static partial class SuperTrend { + public static IReadOnlyList ToSuperTrend( + this IReadOnlyList quotes, + int lookbackPeriods = 10, + double multiplier = 3) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcSuperTrend(lookbackPeriods, multiplier); + private static List CalcSuperTrend( - this List source, + this IReadOnlyList source, int lookbackPeriods, double multiplier) { // check parameter arguments - SuperTrend.Validate(lookbackPeriods, multiplier); + Validate(lookbackPeriods, multiplier); // initialize int length = source.Count; diff --git a/src/s-z/SuperTrend/SuperTrend.Utilities.cs b/src/s-z/SuperTrend/SuperTrend.Utilities.cs index 749f35339..052d097b9 100644 --- a/src/s-z/SuperTrend/SuperTrend.Utilities.cs +++ b/src/s-z/SuperTrend/SuperTrend.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// SUPERTREND (UTILITIES) + +public static partial class SuperTrend { // CONDENSE (REMOVE null results) - /// + /// public static IReadOnlyList Condense( - this IEnumerable results) + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -18,9 +20,9 @@ public static IReadOnlyList Condense( } // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -28,4 +30,23 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + int lookbackPeriods, + double multiplier) + { + // check parameter arguments + if (lookbackPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 1 for SuperTrend."); + } + + if (multiplier <= 0) + { + throw new ArgumentOutOfRangeException(nameof(multiplier), multiplier, + "Multiplier must be greater than 0 for SuperTrend."); + } + } } diff --git a/src/s-z/T3/T3.Api.cs b/src/s-z/T3/T3.Api.cs deleted file mode 100644 index 610f3008b..000000000 --- a/src/s-z/T3/T3.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// TILLSON T3 MOVING AVERAGE (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetT3( - this IEnumerable results, - int lookbackPeriods = 5, - double volumeFactor = 0.7) - where T : IReusable - => results - .ToSortedList() - .CalcT3(lookbackPeriods, volumeFactor); -} diff --git a/src/s-z/T3/T3.Models.cs b/src/s-z/T3/T3.Models.cs index b65c223ff..b1aebebb8 100644 --- a/src/s-z/T3/T3.Models.cs +++ b/src/s-z/T3/T3.Models.cs @@ -4,7 +4,7 @@ public record T3Result ( DateTime Timestamp, double? T3 -) : Reusable(Timestamp) +) : IReusable { - public override double Value => T3.Null2NaN(); + public double Value => T3.Null2NaN(); } diff --git a/src/s-z/T3/T3.StaticSeries.cs b/src/s-z/T3/T3.StaticSeries.cs index f34fc01ed..a85bf64ac 100644 --- a/src/s-z/T3/T3.StaticSeries.cs +++ b/src/s-z/T3/T3.StaticSeries.cs @@ -2,16 +2,17 @@ namespace Skender.Stock.Indicators; // TILLSON T3 MOVING AVERAGE (SERIES) -public static partial class Indicator +public static partial class T3 { - private static List CalcT3( - this List source, - int lookbackPeriods, - double volumeFactor) + public static IReadOnlyList ToT3( + this IReadOnlyList source, + int lookbackPeriods = 5, + double volumeFactor = 0.7) where T : IReusable { // check parameter arguments - T3.Validate(lookbackPeriods, volumeFactor); + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods, volumeFactor); // initialize int length = source.Count; diff --git a/src/s-z/T3/T3.Common.cs b/src/s-z/T3/T3.Utilities.cs similarity index 89% rename from src/s-z/T3/T3.Common.cs rename to src/s-z/T3/T3.Utilities.cs index 797dee7b6..70c4264aa 100644 --- a/src/s-z/T3/T3.Common.cs +++ b/src/s-z/T3/T3.Utilities.cs @@ -1,8 +1,8 @@ namespace Skender.Stock.Indicators; -// TILLSON T3 MOVING AVERAGE (COMMON) +// TILLSON T3 MOVING AVERAGE (UTILITIES) -public static class T3 +public static partial class T3 { // parameter validation internal static void Validate( @@ -22,5 +22,4 @@ internal static void Validate( "Volume Factor must be greater than 0 for T3."); } } - } diff --git a/src/s-z/Tema/Tema.Api.cs b/src/s-z/Tema/Tema.Api.cs deleted file mode 100644 index 5cbb96ad7..000000000 --- a/src/s-z/Tema/Tema.Api.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Skender.Stock.Indicators; - -// TRIPLE EXPONENTIAL MOVING AVERAGE - TEMA (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetTema( - this IEnumerable results, - int lookbackPeriods) - where T : IReusable - => results - .ToSortedList() - .CalcTema(lookbackPeriods); -} diff --git a/src/s-z/Tema/Tema.Common.cs b/src/s-z/Tema/Tema.Common.cs deleted file mode 100644 index 8cf6417b6..000000000 --- a/src/s-z/Tema/Tema.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// TRIPLE EXPONENTIAL MOVING AVERAGE (COMMON) - -public static class Tema -{ - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for TEMA."); - } - } - -} diff --git a/src/s-z/Tema/Tema.Models.cs b/src/s-z/Tema/Tema.Models.cs index f5c01e949..36293757c 100644 --- a/src/s-z/Tema/Tema.Models.cs +++ b/src/s-z/Tema/Tema.Models.cs @@ -4,7 +4,7 @@ public record TemaResult ( DateTime Timestamp, double? Tema = null -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Tema.Null2NaN(); + public double Value => Tema.Null2NaN(); } diff --git a/src/s-z/Tema/Tema.StaticSeries.cs b/src/s-z/Tema/Tema.StaticSeries.cs index 29514baef..f14a28512 100644 --- a/src/s-z/Tema/Tema.StaticSeries.cs +++ b/src/s-z/Tema/Tema.StaticSeries.cs @@ -2,16 +2,17 @@ namespace Skender.Stock.Indicators; // TRIPLE EXPONENTIAL MOVING AVERAGE (SERIES) -public static partial class Indicator +public static partial class Tema { // calculate series - private static List CalcTema( - this List source, + public static IReadOnlyList ToTema( + this IReadOnlyList source, int lookbackPeriods) where T : IReusable { // check parameter arguments - Tema.Validate(lookbackPeriods); + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); // initialize int length = source.Count; diff --git a/src/s-z/Tema/Tema.Utilities.cs b/src/s-z/Tema/Tema.Utilities.cs index 594228578..963479cb1 100644 --- a/src/s-z/Tema/Tema.Utilities.cs +++ b/src/s-z/Tema/Tema.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// TRIPLE EXPONENTIAL MOVING AVERAGE (UTILITIES) + +public static partial class Tema { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int n = results .ToList() @@ -13,4 +15,16 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(3 * n + 100); } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for TEMA."); + } + } } diff --git a/src/s-z/Tr/Tr.Api.cs b/src/s-z/Tr/Tr.Api.cs deleted file mode 100644 index a3596c016..000000000 --- a/src/s-z/Tr/Tr.Api.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Skender.Stock.Indicators; - -// TRUE RANGE (API) - -public static partial class Tr -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetTr( - this IEnumerable quotes) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcTr(); - - // OBSERVER, from Quote Provider - public static TrHub ToTr( - this IQuoteProvider quoteProvider) - where TIn : IQuote - => new(quoteProvider); -} diff --git a/src/s-z/Tr/Tr.Models.cs b/src/s-z/Tr/Tr.Models.cs index bd88886d1..c6cbd373a 100644 --- a/src/s-z/Tr/Tr.Models.cs +++ b/src/s-z/Tr/Tr.Models.cs @@ -3,7 +3,7 @@ namespace Skender.Stock.Indicators; public record TrResult( DateTime Timestamp, double? Tr -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Tr.Null2NaN(); + public double Value => Tr.Null2NaN(); } diff --git a/src/s-z/Tr/Tr.StaticSeries.cs b/src/s-z/Tr/Tr.StaticSeries.cs index 2ab91b4fd..79fa0dbaf 100644 --- a/src/s-z/Tr/Tr.StaticSeries.cs +++ b/src/s-z/Tr/Tr.StaticSeries.cs @@ -4,19 +4,23 @@ namespace Skender.Stock.Indicators; public static partial class Tr { - // calculate series + public static IReadOnlyList ToTr( + this IReadOnlyList quotes) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcTr(); + private static List CalcTr( - this List source) + this IReadOnlyList source) { // initialize int length = source.Count; - List results = new(length); + TrResult[] results = new TrResult[length]; // skip first period if (length > 0) { - results.Add( - new(source[0].Timestamp, null)); + results[0] = new TrResult(source[0].Timestamp, null); } // roll through source values @@ -24,11 +28,11 @@ private static List CalcTr( { QuoteD q = source[i]; - results.Add(new TrResult( + results[i] = new TrResult( Timestamp: q.Timestamp, - Tr: Increment(q.High, q.Low, source[i - 1].Close))); + Tr: Increment(q.High, q.Low, source[i - 1].Close)); } - return results; + return new List(results); } } diff --git a/src/s-z/Tr/Tr.StreamHub.cs b/src/s-z/Tr/Tr.StreamHub.cs index f1f768405..ea0803e9b 100644 --- a/src/s-z/Tr/Tr.StreamHub.cs +++ b/src/s-z/Tr/Tr.StreamHub.cs @@ -2,8 +2,19 @@ namespace Skender.Stock.Indicators; // TRUE RANGE (STREAM HUB) -public class TrHub : QuoteObserver, - IReusableHub +#region initializer + +public static partial class Tr +{ + public static TrHub ToTr( + this IQuoteProvider quoteProvider) + where TIn : IQuote + => new(quoteProvider); +} +#endregion + +public class TrHub + : ChainProvider where TIn : IQuote { #region constructors @@ -21,28 +32,27 @@ internal TrHub(IQuoteProvider provider) public override string ToString() => hubName; - internal override void Add(Act act, TIn newIn, int? index) + protected override (TrResult result, int index) + ToIndicator(TIn item, int? indexHint) { - int i = index ?? Provider.GetIndex(newIn, false); + int i = indexHint ?? ProviderCache.GetIndex(item, true); // skip first period if (i == 0) { - Motify(act, new TrResult(newIn.Timestamp, null), i); - return; + return (new TrResult(item.Timestamp, null), i); } - TIn prev = Provider.Results[i - 1]; + TIn prev = ProviderCache[i - 1]; // candidate result TrResult r = new( - newIn.Timestamp, + item.Timestamp, Tr.Increment( - (double)newIn.High, - (double)newIn.Low, + (double)item.High, + (double)item.Low, (double)prev.Close)); - // save and send - Motify(act, r, i); + return (r, i); } } diff --git a/src/s-z/Tr/Tr.Utilities.cs b/src/s-z/Tr/Tr.Utilities.cs index 5e53022cb..d6f9f4d09 100644 --- a/src/s-z/Tr/Tr.Utilities.cs +++ b/src/s-z/Tr/Tr.Utilities.cs @@ -2,13 +2,8 @@ namespace Skender.Stock.Indicators; // TRUE RANGE (UTILITIES) -/// See the -/// Stock Indicators for .NET online guide for more information. public static partial class Tr { - // increment calculation - /// - /// public static double Increment( double high, double low, diff --git a/src/s-z/Trix/Trix.Api.cs b/src/s-z/Trix/Trix.Api.cs deleted file mode 100644 index a08e53bb5..000000000 --- a/src/s-z/Trix/Trix.Api.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Skender.Stock.Indicators; - -// TRIPLE EMA OSCILLATOR - TRIX (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetTrix( - this IEnumerable results, - int lookbackPeriods) - where T : IReusable - => results - .ToSortedList() - .CalcTrix(lookbackPeriods); -} diff --git a/src/s-z/Trix/Trix.Common.cs b/src/s-z/Trix/Trix.Common.cs deleted file mode 100644 index c9028974f..000000000 --- a/src/s-z/Trix/Trix.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// TRIPLE EMA OSCILLATOR - TRIX (COMMON) - -public static class Trix -{ - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for TRIX."); - } - } - -} diff --git a/src/s-z/Trix/Trix.Models.cs b/src/s-z/Trix/Trix.Models.cs index 46479f029..9f7cf0114 100644 --- a/src/s-z/Trix/Trix.Models.cs +++ b/src/s-z/Trix/Trix.Models.cs @@ -5,7 +5,7 @@ public record TrixResult DateTime Timestamp, double? Ema3 = null, double? Trix = null -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Trix.Null2NaN(); + public double Value => Trix.Null2NaN(); } diff --git a/src/s-z/Trix/Trix.StaticSeries.cs b/src/s-z/Trix/Trix.StaticSeries.cs index 0d8f70380..a94818133 100644 --- a/src/s-z/Trix/Trix.StaticSeries.cs +++ b/src/s-z/Trix/Trix.StaticSeries.cs @@ -2,15 +2,16 @@ namespace Skender.Stock.Indicators; // TRIPLE EMA OSCILLATOR - TRIX (SERIES) -public static partial class Indicator +public static partial class Trix { - private static List CalcTrix( - this List source, + public static IReadOnlyList ToTrix( + this IReadOnlyList source, int lookbackPeriods) where T : IReusable { // check parameter arguments - Trix.Validate(lookbackPeriods); + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); // initialize int length = source.Count; diff --git a/src/s-z/Trix/Trix.Utilities.cs b/src/s-z/Trix/Trix.Utilities.cs index 97f041a6d..ce869af82 100644 --- a/src/s-z/Trix/Trix.Utilities.cs +++ b/src/s-z/Trix/Trix.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// TRIPLE EMA OSCILLATOR - TRIX (UTILITIES) + +public static partial class Trix { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int n = results .ToList() @@ -13,4 +15,16 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(3 * n + 100); } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for TRIX."); + } + } } diff --git a/src/s-z/Tsi/Tsi.Api.cs b/src/s-z/Tsi/Tsi.Api.cs deleted file mode 100644 index 029928648..000000000 --- a/src/s-z/Tsi/Tsi.Api.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Skender.Stock.Indicators; - -// TRUE STRENGTH INDEX (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetTsi( - this IEnumerable results, - int lookbackPeriods = 25, - int smoothPeriods = 13, - int signalPeriods = 7) - where T : IReusable - => results - .ToSortedList() - .CalcTsi(lookbackPeriods, smoothPeriods, signalPeriods); -} diff --git a/src/s-z/Tsi/Tsi.Common.cs b/src/s-z/Tsi/Tsi.Common.cs deleted file mode 100644 index 862d388db..000000000 --- a/src/s-z/Tsi/Tsi.Common.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Skender.Stock.Indicators; - -// TRUE STRENGTH INDEX (COMMON) - -public static class Tsi -{ - // parameter validation - internal static void Validate( - int lookbackPeriods, - int smoothPeriods, - int signalPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for TSI."); - } - - if (smoothPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(smoothPeriods), smoothPeriods, - "Smoothing periods must be greater than 0 for TSI."); - } - - if (signalPeriods < 0) - { - throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, - "Signal periods must be greater than or equal to 0 for TSI."); - } - } - -} diff --git a/src/s-z/Tsi/Tsi.Models.cs b/src/s-z/Tsi/Tsi.Models.cs index b19317c7f..68804399f 100644 --- a/src/s-z/Tsi/Tsi.Models.cs +++ b/src/s-z/Tsi/Tsi.Models.cs @@ -5,7 +5,7 @@ public record TsiResult DateTime Timestamp, double? Tsi = null, double? Signal = null -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Tsi.Null2NaN(); + public double Value => Tsi.Null2NaN(); } diff --git a/src/s-z/Tsi/Tsi.StaticSeries.cs b/src/s-z/Tsi/Tsi.StaticSeries.cs index ff76ac646..578f6ab88 100644 --- a/src/s-z/Tsi/Tsi.StaticSeries.cs +++ b/src/s-z/Tsi/Tsi.StaticSeries.cs @@ -2,17 +2,18 @@ namespace Skender.Stock.Indicators; // TRUE STRENGTH INDEX (SERIES) -public static partial class Indicator +public static partial class Tsi { - private static List CalcTsi( - this List source, - int lookbackPeriods, - int smoothPeriods, - int signalPeriods) + public static IReadOnlyList ToTsi( + this IReadOnlyList source, + int lookbackPeriods = 25, + int smoothPeriods = 13, + int signalPeriods = 7) where T : IReusable { // check parameter arguments - Tsi.Validate(lookbackPeriods, smoothPeriods, signalPeriods); + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods, smoothPeriods, signalPeriods); // initialize int length = source.Count; diff --git a/src/s-z/Tsi/Tsi.Utilities.cs b/src/s-z/Tsi/Tsi.Utilities.cs index 04d622781..16b08c8ef 100644 --- a/src/s-z/Tsi/Tsi.Utilities.cs +++ b/src/s-z/Tsi/Tsi.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// TRUE STRENGTH INDEX (UTILITIES) + +public static partial class Tsi { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int nm = results .ToList() @@ -13,4 +15,30 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(nm + 250); } + + // parameter validation + internal static void Validate( + int lookbackPeriods, + int smoothPeriods, + int signalPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for TSI."); + } + + if (smoothPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(smoothPeriods), smoothPeriods, + "Smoothing periods must be greater than 0 for TSI."); + } + + if (signalPeriods < 0) + { + throw new ArgumentOutOfRangeException(nameof(signalPeriods), signalPeriods, + "Signal periods must be greater than or equal to 0 for TSI."); + } + } } diff --git a/src/s-z/UlcerIndex/UlcerIndex.Api.cs b/src/s-z/UlcerIndex/UlcerIndex.Api.cs deleted file mode 100644 index ed6fab72a..000000000 --- a/src/s-z/UlcerIndex/UlcerIndex.Api.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ULCER INDEX (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetUlcerIndex( - this IEnumerable results, - int lookbackPeriods = 14) - where T : IReusable - => results - .ToSortedList() - .CalcUlcerIndex(lookbackPeriods); -} diff --git a/src/s-z/UlcerIndex/UlcerIndex.Common.cs b/src/s-z/UlcerIndex/UlcerIndex.Common.cs deleted file mode 100644 index 7e203219e..000000000 --- a/src/s-z/UlcerIndex/UlcerIndex.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ULCER INDEX (COMMON) - -public static class UlcerIndex -{ - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Ulcer Index."); - } - } - -} diff --git a/src/s-z/UlcerIndex/UlcerIndex.Models.cs b/src/s-z/UlcerIndex/UlcerIndex.Models.cs index e14aa2a01..33be09d19 100644 --- a/src/s-z/UlcerIndex/UlcerIndex.Models.cs +++ b/src/s-z/UlcerIndex/UlcerIndex.Models.cs @@ -4,9 +4,9 @@ public record UlcerIndexResult ( DateTime Timestamp, double? UlcerIndex -) : Reusable(Timestamp) +) : IReusable { - public override double Value => UlcerIndex.Null2NaN(); + public double Value => UlcerIndex.Null2NaN(); [Obsolete("Rename UI to UlcerIndex")] // v3.0.0 public double? UI => UlcerIndex; diff --git a/src/s-z/UlcerIndex/UlcerIndex.StaticSeries.cs b/src/s-z/UlcerIndex/UlcerIndex.StaticSeries.cs index 819a60ebf..29f7a3bc3 100644 --- a/src/s-z/UlcerIndex/UlcerIndex.StaticSeries.cs +++ b/src/s-z/UlcerIndex/UlcerIndex.StaticSeries.cs @@ -2,15 +2,16 @@ namespace Skender.Stock.Indicators; // ULCER INDEX (SERIES) -public static partial class Indicator +public static partial class UlcerIndex { - private static List CalcUlcerIndex( - this List source, - int lookbackPeriods) + public static IReadOnlyList ToUlcerIndex( + this IReadOnlyList source, + int lookbackPeriods = 14) where T : IReusable { // check parameter arguments - UlcerIndex.Validate(lookbackPeriods); + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); // initialize int length = source.Count; diff --git a/src/s-z/UlcerIndex/UlcerIndex.Utilities.cs b/src/s-z/UlcerIndex/UlcerIndex.Utilities.cs index da03ac1a7..26679c7a6 100644 --- a/src/s-z/UlcerIndex/UlcerIndex.Utilities.cs +++ b/src/s-z/UlcerIndex/UlcerIndex.Utilities.cs @@ -1,16 +1,18 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// ULCER INDEX (UTILITIES) + +public static partial class UlcerIndex { - // remove recommended periods - /// - public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.UlcerIndex != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Ulcer Index."); + } } } diff --git a/src/s-z/Ultimate/Ultimate.Api.cs b/src/s-z/Ultimate/Ultimate.Api.cs deleted file mode 100644 index e76a7cad1..000000000 --- a/src/s-z/Ultimate/Ultimate.Api.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ULTIMATE OSCILLATOR (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetUltimate( - this IEnumerable quotes, - int shortPeriods = 7, - int middlePeriods = 14, - int longPeriods = 28) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcUltimate(shortPeriods, middlePeriods, longPeriods); -} diff --git a/src/s-z/Ultimate/Ultimate.Common.cs b/src/s-z/Ultimate/Ultimate.Common.cs deleted file mode 100644 index 115b0fd1b..000000000 --- a/src/s-z/Ultimate/Ultimate.Common.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ULTIMATE OSCILLATOR (COMMON) - -public static class Ultimate -{ - // parameter validation - internal static void Validate( - int shortPeriods, - int middleAverage, - int longPeriods) - { - // check parameter arguments - if (shortPeriods <= 0 || middleAverage <= 0 || longPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(longPeriods), longPeriods, - "Average periods must be greater than 0 for Ultimate Oscillator."); - } - - if (shortPeriods >= middleAverage || middleAverage >= longPeriods) - { - throw new ArgumentOutOfRangeException(nameof(middleAverage), middleAverage, - "Average periods must be increasingly larger than each other for Ultimate Oscillator."); - } - } - -} diff --git a/src/s-z/Ultimate/Ultimate.Models.cs b/src/s-z/Ultimate/Ultimate.Models.cs index 575919d1f..0286a6ab4 100644 --- a/src/s-z/Ultimate/Ultimate.Models.cs +++ b/src/s-z/Ultimate/Ultimate.Models.cs @@ -4,7 +4,7 @@ public record UltimateResult ( DateTime Timestamp, double? Ultimate -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Ultimate.Null2NaN(); + public double Value => Ultimate.Null2NaN(); } diff --git a/src/s-z/Ultimate/Ultimate.StaticSeries.cs b/src/s-z/Ultimate/Ultimate.StaticSeries.cs index e477adaf5..a4978492b 100644 --- a/src/s-z/Ultimate/Ultimate.StaticSeries.cs +++ b/src/s-z/Ultimate/Ultimate.StaticSeries.cs @@ -2,16 +2,25 @@ namespace Skender.Stock.Indicators; // ULTIMATE OSCILLATOR (SERIES) -public static partial class Indicator +public static partial class Ultimate { + public static IReadOnlyList ToUltimate( + this IReadOnlyList quotes, + int shortPeriods = 7, + int middlePeriods = 14, + int longPeriods = 28) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcUltimate(shortPeriods, middlePeriods, longPeriods); + private static List CalcUltimate( - this List source, + this IReadOnlyList source, int shortPeriods, int middlePeriods, int longPeriods) { // check parameter arguments - Ultimate.Validate(shortPeriods, middlePeriods, longPeriods); + Validate(shortPeriods, middlePeriods, longPeriods); // initialize int length = source.Count; diff --git a/src/s-z/Ultimate/Ultimate.Utilities.cs b/src/s-z/Ultimate/Ultimate.Utilities.cs index bb1ce4389..63c6da0ff 100644 --- a/src/s-z/Ultimate/Ultimate.Utilities.cs +++ b/src/s-z/Ultimate/Ultimate.Utilities.cs @@ -1,16 +1,26 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// ULTIMATE OSCILLATOR (UTILITIES) + +public static partial class Ultimate { - // remove recommended periods - /// - public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int shortPeriods, + int middleAverage, + int longPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Ultimate != null); + // check parameter arguments + if (shortPeriods <= 0 || middleAverage <= 0 || longPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(longPeriods), longPeriods, + "Average periods must be greater than 0 for Ultimate Oscillator."); + } - return results.Remove(removePeriods); + if (shortPeriods >= middleAverage || middleAverage >= longPeriods) + { + throw new ArgumentOutOfRangeException(nameof(middleAverage), middleAverage, + "Average periods must be increasingly larger than each other for Ultimate Oscillator."); + } } } diff --git a/src/s-z/VolatilityStop/VolatilityStop.Api.cs b/src/s-z/VolatilityStop/VolatilityStop.Api.cs deleted file mode 100644 index e2dec1829..000000000 --- a/src/s-z/VolatilityStop/VolatilityStop.Api.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Skender.Stock.Indicators; - -// VOLATILITY SYSTEM/STOP (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetVolatilityStop( - this IEnumerable quotes, - int lookbackPeriods = 7, - double multiplier = 3) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcVolatilityStop(lookbackPeriods, multiplier); -} diff --git a/src/s-z/VolatilityStop/VolatilityStop.Common.cs b/src/s-z/VolatilityStop/VolatilityStop.Common.cs deleted file mode 100644 index 2beb0f8d1..000000000 --- a/src/s-z/VolatilityStop/VolatilityStop.Common.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Skender.Stock.Indicators; - -// VOLATILITY SYSTEM/STOP (COMMON) - -public static class VolatilityStop -{ - // parameter validation - internal static void Validate( - int lookbackPeriods, - double multiplier) - { - // check parameter arguments - if (lookbackPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 1 for Volatility Stop."); - } - - if (multiplier <= 0) - { - throw new ArgumentOutOfRangeException(nameof(multiplier), multiplier, - "ATR Multiplier must be greater than 0 for Volatility Stop."); - } - } - -} diff --git a/src/s-z/VolatilityStop/VolatilityStop.Models.cs b/src/s-z/VolatilityStop/VolatilityStop.Models.cs index 101c876a9..19467860f 100644 --- a/src/s-z/VolatilityStop/VolatilityStop.Models.cs +++ b/src/s-z/VolatilityStop/VolatilityStop.Models.cs @@ -10,7 +10,7 @@ public record VolatilityStopResult double? UpperBand = null, double? LowerBand = null -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Sar.Null2NaN(); + public double Value => Sar.Null2NaN(); } diff --git a/src/s-z/VolatilityStop/VolatilityStop.StaticSeries.cs b/src/s-z/VolatilityStop/VolatilityStop.StaticSeries.cs index bb82e67bb..9b30ea8ea 100644 --- a/src/s-z/VolatilityStop/VolatilityStop.StaticSeries.cs +++ b/src/s-z/VolatilityStop/VolatilityStop.StaticSeries.cs @@ -2,10 +2,18 @@ namespace Skender.Stock.Indicators; // VOLATILITY SYSTEM/STOP (SERIES) -public static partial class Indicator +public static partial class VolatilityStop { + public static IReadOnlyList ToVolatilityStop( + this IReadOnlyList quotes, + int lookbackPeriods = 7, + double multiplier = 3) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcVolatilityStop(lookbackPeriods, multiplier); + private static List CalcVolatilityStop( - this List source, + this IReadOnlyList source, int lookbackPeriods, double multiplier) { @@ -15,7 +23,7 @@ private static List CalcVolatilityStop( .ToList(); // check parameter arguments - VolatilityStop.Validate(lookbackPeriods, multiplier); + Validate(lookbackPeriods, multiplier); // initialize int length = source.Count; diff --git a/src/s-z/VolatilityStop/VolatilityStop.Utilities.cs b/src/s-z/VolatilityStop/VolatilityStop.Utilities.cs index 6713b93ee..ec65d0404 100644 --- a/src/s-z/VolatilityStop/VolatilityStop.Utilities.cs +++ b/src/s-z/VolatilityStop/VolatilityStop.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// VOLATILITY SYSTEM/STOP (UTILITIES) + +public static partial class VolatilityStop { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -15,4 +17,23 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + int lookbackPeriods, + double multiplier) + { + // check parameter arguments + if (lookbackPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 1 for Volatility Stop."); + } + + if (multiplier <= 0) + { + throw new ArgumentOutOfRangeException(nameof(multiplier), multiplier, + "ATR Multiplier must be greater than 0 for Volatility Stop."); + } + } } diff --git a/src/s-z/Vortex/Vortex.Api.cs b/src/s-z/Vortex/Vortex.Api.cs deleted file mode 100644 index 8396c507d..000000000 --- a/src/s-z/Vortex/Vortex.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// VORTEX INDICATOR (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetVortex( - this IEnumerable quotes, - int lookbackPeriods) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcVortex(lookbackPeriods); -} diff --git a/src/s-z/Vortex/Vortex.Common.cs b/src/s-z/Vortex/Vortex.Common.cs deleted file mode 100644 index f5d0a7702..000000000 --- a/src/s-z/Vortex/Vortex.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// VORTEX INDICATOR (COMMON) - -public static class Vortex -{ - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 1) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 1 for VI."); - } - } - -} diff --git a/src/s-z/Vortex/Vortex.StaticSeries.cs b/src/s-z/Vortex/Vortex.StaticSeries.cs index 0e95ecfb9..2d6ce4549 100644 --- a/src/s-z/Vortex/Vortex.StaticSeries.cs +++ b/src/s-z/Vortex/Vortex.StaticSeries.cs @@ -2,14 +2,21 @@ namespace Skender.Stock.Indicators; // VORTEX INDICATOR (SERIES) -public static partial class Indicator +public static partial class Vortex { + public static IReadOnlyList ToVortex( + this IReadOnlyList quotes, + int lookbackPeriods) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcVortex(lookbackPeriods); + private static List CalcVortex( - this List source, + this IReadOnlyList source, int lookbackPeriods) { // check parameter arguments - Vortex.Validate(lookbackPeriods); + Validate(lookbackPeriods); // initialize int length = source.Count; diff --git a/src/s-z/Vortex/Vortex.Utilities.cs b/src/s-z/Vortex/Vortex.Utilities.cs index 0b49878f1..22e449b50 100644 --- a/src/s-z/Vortex/Vortex.Utilities.cs +++ b/src/s-z/Vortex/Vortex.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// VORTEX INDICATOR (UTILITIES) + +public static partial class Vortex { // CONDENSE (REMOVE null results) - /// + /// public static IReadOnlyList Condense( - this IEnumerable results) + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -18,9 +20,9 @@ public static IReadOnlyList Condense( } // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -28,4 +30,16 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + int lookbackPeriods) + { + // check parameter arguments + if (lookbackPeriods <= 1) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 1 for VI."); + } + } } diff --git a/src/s-z/Vwap/Vwap.Api.cs b/src/s-z/Vwap/Vwap.Api.cs deleted file mode 100644 index 4caa822f2..000000000 --- a/src/s-z/Vwap/Vwap.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// VOLUME WEIGHTED AVERAGE PRICE (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetVwap( - this IEnumerable quotes, - DateTime? startDate = null) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcVwap(startDate); -} diff --git a/src/s-z/Vwap/Vwap.Common.cs b/src/s-z/Vwap/Vwap.Common.cs deleted file mode 100644 index f6755a855..000000000 --- a/src/s-z/Vwap/Vwap.Common.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Skender.Stock.Indicators; - -// VOLUME WEIGHTED AVERAGE PRICE (COMMON) - -public static class Vwap -{ - // parameter validation - internal static void Validate( - List quotesList, - DateTime? startDate) - { - // nothing to do for 0 length - if (quotesList.Count == 0) - { - return; - } - - // check parameter arguments (intentionally after quotes check) - if (startDate < quotesList[0].Timestamp) - { - throw new ArgumentOutOfRangeException(nameof(startDate), startDate, - "Start Timestamp must be within the quotes range for VWAP."); - } - } - -} diff --git a/src/s-z/Vwap/Vwap.Models.cs b/src/s-z/Vwap/Vwap.Models.cs index c7b24df2e..168f55103 100644 --- a/src/s-z/Vwap/Vwap.Models.cs +++ b/src/s-z/Vwap/Vwap.Models.cs @@ -4,7 +4,7 @@ public record VwapResult ( DateTime Timestamp, double? Vwap -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Vwap.Null2NaN(); + public double Value => Vwap.Null2NaN(); } diff --git a/src/s-z/Vwap/Vwap.StaticSeries.cs b/src/s-z/Vwap/Vwap.StaticSeries.cs index a4a1e8f5c..0f31b1505 100644 --- a/src/s-z/Vwap/Vwap.StaticSeries.cs +++ b/src/s-z/Vwap/Vwap.StaticSeries.cs @@ -2,14 +2,21 @@ namespace Skender.Stock.Indicators; // VOLUME WEIGHTED AVERAGE PRICE (SERIES) -public static partial class Indicator +public static partial class Vwap { + public static IReadOnlyList ToVwap( + this IReadOnlyList quotes, + DateTime? startDate = null) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcVwap(startDate); + private static List CalcVwap( - this List source, + this IReadOnlyList source, DateTime? startDate = null) { // check parameter arguments - Vwap.Validate(source, startDate); + Validate(source, startDate); // initialize int length = source.Count; diff --git a/src/s-z/Vwap/Vwap.Utilities.cs b/src/s-z/Vwap/Vwap.Utilities.cs index 10c8cc564..bf24c9fea 100644 --- a/src/s-z/Vwap/Vwap.Utilities.cs +++ b/src/s-z/Vwap/Vwap.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// VOLUME WEIGHTED AVERAGE PRICE (UTILITIES) + +public static partial class Vwap { // remove recommended periods - /// + /// public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + this IReadOnlyList results) { int removePeriods = results .ToList() @@ -13,4 +15,23 @@ public static IReadOnlyList RemoveWarmupPeriods( return results.Remove(removePeriods); } + + // parameter validation + internal static void Validate( + IReadOnlyList quotes, + DateTime? startDate) + { + // nothing to do for 0 length + if (quotes.Count == 0) + { + return; + } + + // check parameter arguments (intentionally after quotes check) + if (startDate < quotes[0].Timestamp) + { + throw new ArgumentOutOfRangeException(nameof(startDate), startDate, + "Start Timestamp must be within the quotes range for VWAP."); + } + } } diff --git a/src/s-z/Vwma/Vwma.Api.cs b/src/s-z/Vwma/Vwma.Api.cs deleted file mode 100644 index 99845d0c5..000000000 --- a/src/s-z/Vwma/Vwma.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// VOLUME WEIGHTED MOVING AVERAGE (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetVwma( - this IEnumerable quotes, - int lookbackPeriods) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcVwma(lookbackPeriods); -} diff --git a/src/s-z/Vwma/Vwma.Common.cs b/src/s-z/Vwma/Vwma.Common.cs deleted file mode 100644 index a99db7fce..000000000 --- a/src/s-z/Vwma/Vwma.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// VOLUME WEIGHTED MOVING AVERAGE (COMMON) - -public static class Vwma -{ - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for Vwma."); - } - } - -} diff --git a/src/s-z/Vwma/Vwma.Models.cs b/src/s-z/Vwma/Vwma.Models.cs index 1de241f44..a145b314c 100644 --- a/src/s-z/Vwma/Vwma.Models.cs +++ b/src/s-z/Vwma/Vwma.Models.cs @@ -4,7 +4,7 @@ public record VwmaResult ( DateTime Timestamp, double? Vwma -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Vwma.Null2NaN(); + public double Value => Vwma.Null2NaN(); } diff --git a/src/s-z/Vwma/Vwma.StaticSeries.cs b/src/s-z/Vwma/Vwma.StaticSeries.cs index c7ae21839..20c4c689e 100644 --- a/src/s-z/Vwma/Vwma.StaticSeries.cs +++ b/src/s-z/Vwma/Vwma.StaticSeries.cs @@ -2,14 +2,21 @@ namespace Skender.Stock.Indicators; // VOLUME WEIGHTED MOVING AVERAGE (SERIES) -public static partial class Indicator +public static partial class Vwma { + public static IReadOnlyList ToVwma( + this IReadOnlyList quotes, + int lookbackPeriods) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcVwma(lookbackPeriods); + private static List CalcVwma( - this List source, + this IReadOnlyList source, int lookbackPeriods) { // check parameter arguments - Vwma.Validate(lookbackPeriods); + Validate(lookbackPeriods); // initialize int length = source.Count; diff --git a/src/s-z/Vwma/Vwma.Utilities.cs b/src/s-z/Vwma/Vwma.Utilities.cs index aeee25851..dd43909b3 100644 --- a/src/s-z/Vwma/Vwma.Utilities.cs +++ b/src/s-z/Vwma/Vwma.Utilities.cs @@ -1,16 +1,18 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// VOLUME WEIGHTED MOVING AVERAGE (UTILITIES) + +public static partial class Vwma { - // remove recommended periods - /// - public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Vwma != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for Vwma."); + } } } diff --git a/src/s-z/WilliamsR/WilliamsR.Api.cs b/src/s-z/WilliamsR/WilliamsR.Api.cs deleted file mode 100644 index d8c8ef219..000000000 --- a/src/s-z/WilliamsR/WilliamsR.Api.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Skender.Stock.Indicators; - -// WILLIAM %R OSCILLATOR (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetWilliamsR( - this IEnumerable quotes, - int lookbackPeriods = 14) - where TQuote : IQuote => quotes - .ToQuoteDList() - .CalcWilliamsR(lookbackPeriods); -} diff --git a/src/s-z/WilliamsR/WilliamsR.Common.cs b/src/s-z/WilliamsR/WilliamsR.Common.cs deleted file mode 100644 index 8ef455e8a..000000000 --- a/src/s-z/WilliamsR/WilliamsR.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// WILLIAM %R OSCILLATOR (COMMON) - -public static class WilliamsR -{ - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for William %R."); - } - } - -} diff --git a/src/s-z/WilliamsR/WilliamsR.Models.cs b/src/s-z/WilliamsR/WilliamsR.Models.cs index a260ae42e..7eacca043 100644 --- a/src/s-z/WilliamsR/WilliamsR.Models.cs +++ b/src/s-z/WilliamsR/WilliamsR.Models.cs @@ -4,7 +4,7 @@ public record WilliamsResult ( DateTime Timestamp, double? WilliamsR -) : Reusable(Timestamp) +) : IReusable { - public override double Value => WilliamsR.Null2NaN(); + public double Value => WilliamsR.Null2NaN(); } diff --git a/src/s-z/WilliamsR/WilliamsR.StaticSeries.cs b/src/s-z/WilliamsR/WilliamsR.StaticSeries.cs index bece3f912..23becb293 100644 --- a/src/s-z/WilliamsR/WilliamsR.StaticSeries.cs +++ b/src/s-z/WilliamsR/WilliamsR.StaticSeries.cs @@ -2,14 +2,21 @@ namespace Skender.Stock.Indicators; // WILLIAM %R OSCILLATOR (SERIES) -public static partial class Indicator +public static partial class WilliamsR { + public static IReadOnlyList ToWilliamsR( + this IReadOnlyList quotes, + int lookbackPeriods = 14) + where TQuote : IQuote => quotes + .ToQuoteDList() + .CalcWilliamsR(lookbackPeriods); + private static List CalcWilliamsR( - this List source, + this IReadOnlyList source, int lookbackPeriods) { // check parameter arguments - WilliamsR.Validate(lookbackPeriods); + Validate(lookbackPeriods); // convert Fast Stochastic to William %R return source.CalcStoch(lookbackPeriods, 1, 1, 3, 2, MaType.SMA) diff --git a/src/s-z/WilliamsR/WilliamsR.Utilities.cs b/src/s-z/WilliamsR/WilliamsR.Utilities.cs index a7cdaab47..49f8d8af1 100644 --- a/src/s-z/WilliamsR/WilliamsR.Utilities.cs +++ b/src/s-z/WilliamsR/WilliamsR.Utilities.cs @@ -1,16 +1,18 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// WILLIAM %R OSCILLATOR (UTILITIES) + +public static partial class WilliamsR { - // remove recommended periods - /// - public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.WilliamsR != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for William %R."); + } } } diff --git a/src/s-z/Wma/Wma.Api.cs b/src/s-z/Wma/Wma.Api.cs deleted file mode 100644 index f3af400ea..000000000 --- a/src/s-z/Wma/Wma.Api.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Skender.Stock.Indicators; - -// WEIGHTED MOVING AVERAGE (API) -public static partial class Indicator -{ - // SERIES, from CHAIN - public static IReadOnlyList GetWma( - this IEnumerable results, - int lookbackPeriods) - where T : IReusable - => results - .ToSortedList() - .CalcWma(lookbackPeriods); -} diff --git a/src/s-z/Wma/Wma.Common.cs b/src/s-z/Wma/Wma.Common.cs deleted file mode 100644 index fd0d6b64a..000000000 --- a/src/s-z/Wma/Wma.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// WEIGHTED MOVING AVERAGE (COMMON) - -public static class Wma -{ - // parameter validation - internal static void Validate( - int lookbackPeriods) - { - // check parameter arguments - if (lookbackPeriods <= 0) - { - throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, - "Lookback periods must be greater than 0 for WMA."); - } - } - -} diff --git a/src/s-z/Wma/Wma.Models.cs b/src/s-z/Wma/Wma.Models.cs index 9231177fa..ae37a2bc6 100644 --- a/src/s-z/Wma/Wma.Models.cs +++ b/src/s-z/Wma/Wma.Models.cs @@ -4,7 +4,7 @@ public record WmaResult ( DateTime Timestamp, double? Wma -) : Reusable(Timestamp) +) : IReusable { - public override double Value => Wma.Null2NaN(); + public double Value => Wma.Null2NaN(); } diff --git a/src/s-z/Wma/Wma.StaticSeries.cs b/src/s-z/Wma/Wma.StaticSeries.cs index 19e3efd4b..95fcc8aac 100644 --- a/src/s-z/Wma/Wma.StaticSeries.cs +++ b/src/s-z/Wma/Wma.StaticSeries.cs @@ -2,15 +2,16 @@ namespace Skender.Stock.Indicators; // WEIGHTED MOVING AVERAGE (SERIES) -public static partial class Indicator +public static partial class Wma { - private static List CalcWma( - this List source, + public static IReadOnlyList ToWma( + this IReadOnlyList source, int lookbackPeriods) where T : IReusable { // check parameter arguments - Wma.Validate(lookbackPeriods); + ArgumentNullException.ThrowIfNull(source); + Validate(lookbackPeriods); // initialize int length = source.Count; diff --git a/src/s-z/Wma/Wma.Utilities.cs b/src/s-z/Wma/Wma.Utilities.cs index d9225ffde..cd7b65c13 100644 --- a/src/s-z/Wma/Wma.Utilities.cs +++ b/src/s-z/Wma/Wma.Utilities.cs @@ -1,16 +1,18 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// WEIGHTED MOVING AVERAGE (UTILITIES) + +public static partial class Wma { - // remove recommended periods - /// - public static IReadOnlyList RemoveWarmupPeriods( - this IEnumerable results) + // parameter validation + internal static void Validate( + int lookbackPeriods) { - int removePeriods = results - .ToList() - .FindIndex(x => x.Wma != null); - - return results.Remove(removePeriods); + // check parameter arguments + if (lookbackPeriods <= 0) + { + throw new ArgumentOutOfRangeException(nameof(lookbackPeriods), lookbackPeriods, + "Lookback periods must be greater than 0 for WMA."); + } } } diff --git a/src/s-z/ZigZag/ZigZag.Api.cs b/src/s-z/ZigZag/ZigZag.Api.cs deleted file mode 100644 index 1bfbf81fa..000000000 --- a/src/s-z/ZigZag/ZigZag.Api.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ZIG ZAG (API) -public static partial class Indicator -{ - // SERIES, from TQuote - /// - /// - public static IReadOnlyList GetZigZag( - this IEnumerable quotes, - EndType endType = EndType.Close, - decimal percentChange = 5) - where TQuote : IQuote => quotes - .ToSortedList() - .CalcZigZag(endType, percentChange); -} diff --git a/src/s-z/ZigZag/ZigZag.Common.cs b/src/s-z/ZigZag/ZigZag.Common.cs deleted file mode 100644 index fdcd0bc0a..000000000 --- a/src/s-z/ZigZag/ZigZag.Common.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Skender.Stock.Indicators; - -// ZIG ZAG (COMMON) - -public static class ZigZag -{ - // parameter validation - internal static void Validate( - decimal percentChange) - { - // check parameter arguments - if (percentChange <= 0) - { - throw new ArgumentOutOfRangeException(nameof(percentChange), percentChange, - "Percent change must be greater than 0 for ZIGZAG."); - } - } - -} diff --git a/src/s-z/ZigZag/ZigZag.Models.cs b/src/s-z/ZigZag/ZigZag.Models.cs index 6ebd5db44..de7fce44f 100644 --- a/src/s-z/ZigZag/ZigZag.Models.cs +++ b/src/s-z/ZigZag/ZigZag.Models.cs @@ -7,9 +7,9 @@ public record ZigZagResult string? PointType = null, // indicates a specific point and type e.g. H or L decimal? RetraceHigh = null, // zig zag retrace high line decimal? RetraceLow = null // zig zag retrace low line -) : Reusable(Timestamp) +) : IReusable { - public override double Value => ZigZag.Null2NaN(); + public double Value => ZigZag.Null2NaN(); } internal class ZigZagEval diff --git a/src/s-z/ZigZag/ZigZag.StaticSeries.cs b/src/s-z/ZigZag/ZigZag.StaticSeries.cs index 39397a269..8d249e916 100644 --- a/src/s-z/ZigZag/ZigZag.StaticSeries.cs +++ b/src/s-z/ZigZag/ZigZag.StaticSeries.cs @@ -2,19 +2,20 @@ namespace Skender.Stock.Indicators; // ZIG ZAG (SERIES) -public static partial class Indicator +public static partial class ZigZag { - private static List CalcZigZag( - this List quotesList, + public static IReadOnlyList ToZigZag( + this IReadOnlyList quotes, EndType endType = EndType.Close, decimal percentChange = 5) where TQuote : IQuote { // check parameter arguments - ZigZag.Validate(percentChange); + ArgumentNullException.ThrowIfNull(quotes); + Validate(percentChange); // initialize - int length = quotesList.Count; + int length = quotes.Count; List results = new(length); if (length == 0) @@ -22,7 +23,7 @@ private static List CalcZigZag( return results; } - TQuote q0 = quotesList[0]; + TQuote q0 = quotes[0]; ZigZagEval eval = GetZigZagEval(endType, 1, q0); decimal changeThreshold = percentChange / 100m; @@ -48,7 +49,7 @@ private static List CalcZigZag( // roll through source values, to find initial trend for (int i = 0; i < length; i++) { - TQuote q = quotesList[i]; + TQuote q = quotes[i]; int index = i + 1; eval = GetZigZagEval(endType, index, q); @@ -88,12 +89,12 @@ private static List CalcZigZag( while (lastPoint.Index < length) { ZigZagPoint nextPoint = EvaluateNextPoint( - quotesList, endType, changeThreshold, lastPoint); + quotes, endType, changeThreshold, lastPoint); string lastDirection = lastPoint.PointType; // draw line (and reset last point) - DrawZigZagLine(results, quotesList, lastPoint, nextPoint); + DrawZigZagLine(results, quotes, lastPoint, nextPoint); // draw retrace line (and reset last high/low point) DrawRetraceLine(results, lastDirection, lastLowPoint, @@ -105,7 +106,7 @@ private static List CalcZigZag( // internals private static ZigZagPoint EvaluateNextPoint( - List quotesList, + IReadOnlyList quotesList, EndType endType, decimal changeThreshold, ZigZagPoint lastPoint) @@ -183,7 +184,7 @@ private static ZigZagPoint EvaluateNextPoint( } private static void DrawZigZagLine( - List results, List quotesList, + List results, IReadOnlyList quotes, ZigZagPoint lastPoint, ZigZagPoint nextPoint) where TQuote : IQuote { @@ -196,7 +197,7 @@ private static void DrawZigZagLine( // add new line segment for (int i = lastPoint.Index; i < nextPoint.Index; i++) { - TQuote q = quotesList[i]; + TQuote q = quotes[i]; int index = i + 1; ZigZagResult result = new( diff --git a/src/s-z/ZigZag/ZigZag.Utilities.cs b/src/s-z/ZigZag/ZigZag.Utilities.cs index c35be15d0..b3af14695 100644 --- a/src/s-z/ZigZag/ZigZag.Utilities.cs +++ b/src/s-z/ZigZag/ZigZag.Utilities.cs @@ -1,11 +1,13 @@ namespace Skender.Stock.Indicators; -public static partial class Indicator +// ZIG ZAG (UTILITIES) + +public static partial class ZigZag { // CONDENSE (REMOVE null results) - /// + /// public static IReadOnlyList Condense( - this IEnumerable results) + this IReadOnlyList results) { List resultsList = results .ToList(); @@ -16,4 +18,16 @@ public static IReadOnlyList Condense( return resultsList.ToSortedList(); } + + // parameter validation + internal static void Validate( + decimal percentChange) + { + // check parameter arguments + if (percentChange <= 0) + { + throw new ArgumentOutOfRangeException(nameof(percentChange), percentChange, + "Percent change must be greater than 0 for ZIGZAG."); + } + } } diff --git a/tests/application/Program.cs b/tests/application/Program.cs index 2b126c4ba..620267b46 100644 --- a/tests/application/Program.cs +++ b/tests/application/Program.cs @@ -206,7 +206,7 @@ private static void SendToConsole( /// private static void Timewarp(int quotesPerMinute = 0) { - if (quotesPerMinute is 0) + if (quotesPerMinute == 0) { return; } diff --git a/tests/indicators/TestBase.cs b/tests/indicators/TestBase.cs index e34f90f0c..5486c5f0e 100644 --- a/tests/indicators/TestBase.cs +++ b/tests/indicators/TestBase.cs @@ -6,12 +6,13 @@ [assembly: CLSCompliant(true)] [assembly: InternalsVisibleTo("Tests.Other")] // these use test data [assembly: InternalsVisibleTo("Tests.Performance")] +[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)] + namespace Test.Data; -[TestClass] public abstract class TestBase // base for all tests { - internal static readonly CultureInfo englishCulture = new("en-US", false); + internal static readonly CultureInfo invariantCulture = CultureInfo.InvariantCulture; internal static readonly IReadOnlyList Quotes = Data.GetDefault(); internal static readonly IReadOnlyList OtherQuotes = Data.GetCompare(); @@ -28,48 +29,38 @@ namespace Test.Data; protected static readonly double DoublePrecision = 1E-13; protected static readonly DateTime EvalDate - = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", englishCulture); + = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", invariantCulture); } /// /// Base tests that all series indicators should have. /// -[TestClass] public abstract class StaticSeriesTestBase : TestBase { - [TestMethod] public abstract void Standard(); - [TestMethod] public abstract void BadData(); - [TestMethod] public abstract void NoQuotes(); } /// /// Base tests that all static indicators (series) should have. /// -[TestClass] public abstract class IncrementsTestBase : TestBase { - [TestMethod] - public abstract void Standard(); + public abstract void FromQuote(); - [TestMethod] - public abstract void ValueBased(); + public abstract void FromQuoteBatch(); } /// /// Base tests that all streamed indicators should have. /// -[TestClass] public abstract class StreamHubTestBase : TestBase // default: quote observer { - [TestMethod] public abstract void QuoteObserver(); - [TestMethod] public abstract void CustomToString(); } @@ -78,7 +69,6 @@ public abstract class StreamHubTestBase : TestBase // default: quote observer /// public interface ITestChainObserver { - [TestMethod] void ChainObserver(); } @@ -87,6 +77,5 @@ public interface ITestChainObserver /// public interface ITestChainProvider { - [TestMethod] void ChainProvider(); } diff --git a/tests/indicators/Tests.Indicators.csproj b/tests/indicators/Tests.Indicators.csproj index f98f83383..7f6a08c48 100644 --- a/tests/indicators/Tests.Indicators.csproj +++ b/tests/indicators/Tests.Indicators.csproj @@ -33,6 +33,7 @@ + Always diff --git a/tests/indicators/_common/Candles/Candles.StaticSeries.Tests.cs b/tests/indicators/_common/Candles/Candles.Tests.cs similarity index 96% rename from tests/indicators/_common/Candles/Candles.StaticSeries.Tests.cs rename to tests/indicators/_common/Candles/Candles.Tests.cs index 85d6fe6e3..a23c94202 100644 --- a/tests/indicators/_common/Candles/Candles.StaticSeries.Tests.cs +++ b/tests/indicators/_common/Candles/Candles.Tests.cs @@ -16,13 +16,13 @@ public void SortCandles() Assert.AreEqual(502, candles.Count); // sample values - DateTime firstDate = DateTime.ParseExact("01/18/2016", "MM/dd/yyyy", englishCulture); + DateTime firstDate = DateTime.ParseExact("01/18/2016", "MM/dd/yyyy", invariantCulture); Assert.AreEqual(firstDate, candles[0].Timestamp); - DateTime lastDate = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", englishCulture); + DateTime lastDate = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", invariantCulture); Assert.AreEqual(lastDate, candles[^1].Timestamp); - DateTime spotDate = DateTime.ParseExact("03/16/2017", "MM/dd/yyyy", englishCulture); + DateTime spotDate = DateTime.ParseExact("03/16/2017", "MM/dd/yyyy", invariantCulture); Assert.AreEqual(spotDate, candles[50].Timestamp); } diff --git a/tests/indicators/_common/Generics/BinarySettingsTests.cs b/tests/indicators/_common/Generics/BinarySettingsTests.cs new file mode 100644 index 000000000..aa2b01b78 --- /dev/null +++ b/tests/indicators/_common/Generics/BinarySettingsTests.cs @@ -0,0 +1,73 @@ +namespace Utilities; + +[TestClass] +public class BinarySettingsTests : TestBase +{ + // see Renko Hub tests for inheritance + + [TestMethod] + public void InitializationDefault() + { + BinarySettings sut = new(); + sut.Settings.Should().Be(0); + sut.Mask.Should().Be(0b11111111); + } + + [TestMethod] + public void InitializationPartial() + { + BinarySettings sut = new(0); + sut.Settings.Should().Be(0); + sut.Mask.Should().Be(0b11111111); + } + + [TestMethod] + public void InitializationCustom() + { + BinarySettings sut = new(0b10101010, 0b11001100); + sut.Settings.Should().Be(0b10101010); + sut.Mask.Should().Be(0b11001100); + } + + [TestMethod] + public void AccessBit() + { + BinarySettings sut = new(0b00010001); + + // positions: 76543210 + sut[0].Should().BeTrue(); + sut[1].Should().BeFalse(); + sut[2].Should().BeFalse(); + sut[3].Should().BeFalse(); + sut[4].Should().BeTrue(); + sut[5].Should().BeFalse(); + sut[6].Should().BeFalse(); + sut[7].Should().BeFalse(); + } + + [TestMethod] + public void CombineDefaultMask() + { + BinarySettings srcSettings = new(0b01101001); + BinarySettings defSettings = new(0b00000010); + BinarySettings newSettings = defSettings.Combine(srcSettings); + newSettings.Settings.Should().Be(0b01101011); + } + + [TestMethod] + public void CombineCustomMask() + { + BinarySettings srcSettings = new(0b01101001, 0b11111110); + BinarySettings defSettings = new(0b00000010); + BinarySettings newSettings = defSettings.Combine(srcSettings); + newSettings.Settings.Should().Be(0b01101010); + } + + [TestMethod] + public void Equality() + { + BinarySettings sut = new(); + Assert.AreEqual(0b00000000, sut.Settings); + Assert.AreEqual(0b11111111, sut.Mask); + } +} diff --git a/tests/indicators/_common/Generics/RemoveWarmup.StaticSeries.Tests.cs b/tests/indicators/_common/Generics/RemoveWarmup.Tests.cs similarity index 84% rename from tests/indicators/_common/Generics/RemoveWarmup.StaticSeries.Tests.cs rename to tests/indicators/_common/Generics/RemoveWarmup.Tests.cs index 3f19c57bc..99abb398b 100644 --- a/tests/indicators/_common/Generics/RemoveWarmup.StaticSeries.Tests.cs +++ b/tests/indicators/_common/Generics/RemoveWarmup.Tests.cs @@ -8,14 +8,14 @@ public void Standard() { // specific periods IReadOnlyList results = Quotes - .GetHeikinAshi() + .ToHeikinAshi() .RemoveWarmupPeriods(102); Assert.AreEqual(400, results.Count); // bad remove period Assert.ThrowsException(() - => Quotes.GetAdx().RemoveWarmupPeriods(-1)); + => Quotes.ToAdx().RemoveWarmupPeriods(-1)); } [TestMethod] @@ -23,7 +23,7 @@ public void TooMany() { // more than available IReadOnlyList results = Quotes - .GetHeikinAshi() + .ToHeikinAshi() .RemoveWarmupPeriods(600); Assert.AreEqual(0, results.Count); diff --git a/tests/indicators/_common/Generics/Sorting.StaticSeries.Tests.cs b/tests/indicators/_common/Generics/Sorting.StaticSeries.Tests.cs deleted file mode 100644 index 77aa0c223..000000000 --- a/tests/indicators/_common/Generics/Sorting.StaticSeries.Tests.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Collections.ObjectModel; - -namespace Utilities; - -[TestClass] -public class Sorting : TestBase -{ - [TestMethod] - public void ToSortedCollection() - { - // baseline for comparison - IReadOnlyList baseline = - [ - new(Timestamp: DateTime.Parse("1/1/2000", englishCulture), Sma: null), - new(Timestamp: DateTime.Parse("1/2/2000", englishCulture), Sma: null), - new(Timestamp: DateTime.Parse("1/9/2000", englishCulture), Sma: null), - new(Timestamp: DateTime.Parse("1/3/2000", englishCulture), Sma: 3), - new(Timestamp: DateTime.Parse("1/4/2000", englishCulture), Sma: 4), - new(Timestamp: DateTime.Parse("1/5/2000", englishCulture), Sma: 5), - new(Timestamp: DateTime.Parse("1/6/2000", englishCulture), Sma: 6), - new(Timestamp: DateTime.Parse("1/7/2000", englishCulture), Sma: 7), - new(Timestamp: DateTime.Parse("1/8/2000", englishCulture), Sma: double.NaN) - ]; - - // PUBLIC VARIANT, generic sorted Collection - Collection sortResults = baseline - .ToSortedCollection(); - - Assert.AreEqual(5, sortResults[4].Sma); - Assert.AreEqual(DateTime.Parse("1/9/2000", englishCulture), sortResults.LastOrDefault().Timestamp); - } -} diff --git a/tests/indicators/_common/Generics/Sorting.Tests.cs b/tests/indicators/_common/Generics/Sorting.Tests.cs new file mode 100644 index 000000000..4bb16536b --- /dev/null +++ b/tests/indicators/_common/Generics/Sorting.Tests.cs @@ -0,0 +1,32 @@ +using System.Collections.ObjectModel; + +namespace Utilities; + +[TestClass] +public class Sorting : TestBase +{ + [TestMethod] + public void ToSortedList() + { + // baseline for comparison + IReadOnlyList baseline = + [ + new(Timestamp: DateTime.Parse("1/1/2000", invariantCulture), Sma: null), + new(Timestamp: DateTime.Parse("1/2/2000", invariantCulture), Sma: null), + new(Timestamp: DateTime.Parse("1/9/2000", invariantCulture), Sma: null), + new(Timestamp: DateTime.Parse("1/3/2000", invariantCulture), Sma: 3), + new(Timestamp: DateTime.Parse("1/4/2000", invariantCulture), Sma: 4), + new(Timestamp: DateTime.Parse("1/5/2000", invariantCulture), Sma: 5), + new(Timestamp: DateTime.Parse("1/6/2000", invariantCulture), Sma: 6), + new(Timestamp: DateTime.Parse("1/7/2000", invariantCulture), Sma: 7), + new(Timestamp: DateTime.Parse("1/8/2000", invariantCulture), Sma: double.NaN) + ]; + + // PUBLIC VARIANT, generic sorted list + IReadOnlyList sortResults = baseline + .ToSortedList(); + + Assert.AreEqual(5, sortResults[4].Sma); + Assert.AreEqual(DateTime.Parse("1/9/2000", invariantCulture), sortResults[^1].Timestamp); + } +} diff --git a/tests/indicators/_common/Generics/Transforms.StaticSeries.Tests.cs b/tests/indicators/_common/Generics/Transforms.Tests.cs similarity index 89% rename from tests/indicators/_common/Generics/Transforms.StaticSeries.Tests.cs rename to tests/indicators/_common/Generics/Transforms.Tests.cs index f6105ea87..60a8da66b 100644 --- a/tests/indicators/_common/Generics/Transforms.StaticSeries.Tests.cs +++ b/tests/indicators/_common/Generics/Transforms.Tests.cs @@ -14,7 +14,7 @@ public void ToCollection() Assert.IsNotNull(collection); Assert.AreEqual(502, collection.Count); - Assert.AreEqual(collection.LastOrDefault().Close, 245.28m); + Assert.AreEqual(245.28m, collection.LastOrDefault().Close); } // null ToCollection diff --git a/tests/indicators/_common/Math/Numerical.StaticSeries.Tests.cs b/tests/indicators/_common/Math/Numerical.Tests.cs similarity index 74% rename from tests/indicators/_common/Math/Numerical.StaticSeries.Tests.cs rename to tests/indicators/_common/Math/Numerical.Tests.cs index 8863435ae..e116fe11f 100644 --- a/tests/indicators/_common/Math/Numerical.StaticSeries.Tests.cs +++ b/tests/indicators/_common/Math/Numerical.Tests.cs @@ -7,8 +7,8 @@ public class Numericals : TestBase .Select(x => (double)x.Close) .ToArray(); - private readonly double[] _x = [1, 2, 3, 4, 5]; - private readonly double[] _y = [0, 0, 0, 0]; + private readonly double[] _x = { 1, 2, 3, 4, 5 }; + private readonly double[] _y = { 0, 0, 0, 0 }; [TestMethod] public void StdDev() @@ -19,8 +19,10 @@ public void StdDev() } [TestMethod] - [ExpectedException(typeof(ArgumentNullException), "Null parameter.")] - public void StdDevNull() => Numerical.StdDev(null); + public void StdDevNull() + { + Assert.ThrowsException(() => Numerical.StdDev(null)); + } [TestMethod] public void Slope() @@ -31,25 +33,31 @@ public void Slope() } [TestMethod] - [ExpectedException(typeof(ArgumentNullException), "Null X parameter.")] - public void SlopeXnull() => Numerical.Slope(null, _x); + public void SlopeXnull() + { + Assert.ThrowsException(() => Numerical.Slope(null, _x)); + } [TestMethod] - [ExpectedException(typeof(ArgumentNullException), "Null Y parameter.")] - public void SlopeYnull() => Numerical.Slope(_x, null); + public void SlopeYnull() + { + Assert.ThrowsException(() => Numerical.Slope(_x, null)); + } [TestMethod] - [ExpectedException(typeof(ArgumentException), "X and Y different lengths.")] - public void SlopeMismatch() => Numerical.Slope(_x, _y); + public void SlopeMismatch() + { + Assert.ThrowsException(() => Numerical.Slope(_x, _y)); + } [TestMethod] public void RoundDownDate() { TimeSpan interval = PeriodSize.OneHour.ToTimeSpan(); - DateTime evDate = DateTime.Parse("2020-12-15 09:35:45", englishCulture); + DateTime evDate = DateTime.Parse("2020-12-15 09:35:45", invariantCulture); DateTime rnDate = evDate.RoundDown(interval); - DateTime exDate = DateTime.Parse("2020-12-15 09:00:00", englishCulture); + DateTime exDate = DateTime.Parse("2020-12-15 09:00:00", invariantCulture); Assert.AreEqual(exDate, rnDate); } diff --git a/tests/indicators/_common/Observables/Cache.Manager.StaticSeries.Tests.cs b/tests/indicators/_common/Observables/StreamHub.CacheMgmt.Tests.cs similarity index 93% rename from tests/indicators/_common/Observables/Cache.Manager.StaticSeries.Tests.cs rename to tests/indicators/_common/Observables/StreamHub.CacheMgmt.Tests.cs index 69e46d569..2ebd45acf 100644 --- a/tests/indicators/_common/Observables/Cache.Manager.StaticSeries.Tests.cs +++ b/tests/indicators/_common/Observables/StreamHub.CacheMgmt.Tests.cs @@ -10,7 +10,7 @@ public class CacheManagement : TestBase public void ModifyWithAct() => Assert.Inconclusive("test not implemented"); [TestMethod] - public void Purge() + public void Remove() { QuoteHub provider = new(); SmaHub observer = provider.ToSma(20); @@ -52,7 +52,7 @@ public void ActAddOld() // late arrival } // add late - provider.Add(Quotes[100]); + provider.Insert(Quotes[100]); // assert same as original for (int i = 0; i < length; i++) @@ -62,7 +62,7 @@ public void ActAddOld() // late arrival // compare quote to result cache r.Timestamp.Should().Be(q.Timestamp); - r.Value.Should().Be((double)q.Close); + r.Value.Should().Be(q.Value); } // close observations @@ -98,7 +98,7 @@ public void Overflowing() observer.Results.Should().HaveCount(1); provider.IsFaulted.Should().BeFalse(); provider.OverflowCount.Should().Be(100); - provider.HasSubscribers.Should().BeTrue(); + provider.HasObservers.Should().BeTrue(); provider.EndTransmission(); } @@ -135,7 +135,7 @@ public void OverflowedAndReset() observer.Results.Should().HaveCount(1); provider.IsFaulted.Should().BeTrue(); provider.OverflowCount.Should().Be(101); - provider.HasSubscribers.Should().BeFalse(); + provider.HasObservers.Should().BeTrue(); // act: reset @@ -152,7 +152,7 @@ public void OverflowedAndReset() observer.Results.Should().HaveCount(1); provider.IsFaulted.Should().BeFalse(); provider.OverflowCount.Should().Be(100); - provider.HasSubscribers.Should().BeFalse(); // expected + provider.HasObservers.Should().BeTrue(); // not lost provider.EndTransmission(); } diff --git a/tests/indicators/_common/Observables/StreamProvider.StaticSeries.Tests.cs b/tests/indicators/_common/Observables/StreamHub.Observable.Tests.cs similarity index 84% rename from tests/indicators/_common/Observables/StreamProvider.StaticSeries.Tests.cs rename to tests/indicators/_common/Observables/StreamHub.Observable.Tests.cs index af8fedb6c..8dcb6f73c 100644 --- a/tests/indicators/_common/Observables/StreamProvider.StaticSeries.Tests.cs +++ b/tests/indicators/_common/Observables/StreamHub.Observable.Tests.cs @@ -1,7 +1,7 @@ namespace Observables; [TestClass] -public class StreamProviders : TestBase, ITestChainProvider +public class StreamObservables : TestBase, ITestChainProvider { [TestMethod] public void Prefill() @@ -49,32 +49,32 @@ QuotePartHub observer = provider.ToQuotePart(CandlePart.OHLC4); // assert: subscribed - provider.SubscriberCount.Should().Be(1); - provider.HasSubscribers.Should().BeTrue(); + provider.ObserverCount.Should().Be(1); + provider.HasObservers.Should().BeTrue(); observer.IsSubscribed.Should().BeTrue(); // act: unsubscribe observer.Unsubscribe(); // assert: not subscribed - provider.SubscriberCount.Should().Be(0); - provider.HasSubscribers.Should().BeFalse(); + provider.ObserverCount.Should().Be(0); + provider.HasObservers.Should().BeFalse(); observer.IsSubscribed.Should().BeFalse(); // act: resubscribe provider.Subscribe(observer); // assert: subscribed - provider.SubscriberCount.Should().Be(1); - provider.HasSubscribers.Should().BeTrue(); + provider.ObserverCount.Should().Be(1); + provider.HasObservers.Should().BeTrue(); observer.IsSubscribed.Should().BeTrue(); // act: end all subscriptions provider.EndTransmission(); // assert: not subscribed - provider.SubscriberCount.Should().Be(0); - provider.HasSubscribers.Should().BeFalse(); + provider.ObserverCount.Should().Be(0); + provider.HasObservers.Should().BeFalse(); observer.IsSubscribed.Should().BeFalse(); } diff --git a/tests/indicators/_common/Observables/StreamObserver.StaticSeries.Tests.cs b/tests/indicators/_common/Observables/StreamHub.Observer.Tests.cs similarity index 91% rename from tests/indicators/_common/Observables/StreamObserver.StaticSeries.Tests.cs rename to tests/indicators/_common/Observables/StreamHub.Observer.Tests.cs index d0a914a18..794749b3b 100644 --- a/tests/indicators/_common/Observables/StreamObserver.StaticSeries.Tests.cs +++ b/tests/indicators/_common/Observables/StreamHub.Observer.Tests.cs @@ -37,20 +37,20 @@ IReadOnlyList quotesList Quote q1000modified = quotesList[1000] with { Close = 12345m }; QuotePart r1000modified = q1000modified.ToQuotePart(CandlePart.Close); - observer.Modify(r1000modified); // add directly to observer + observer.Cache.Insert(1000, r1000modified); // add directly to cache IReadOnlyList modified = observer.Results.ToList(); // precondition: prefilled, modified provider.Cache.Should().HaveCount(length); - observer.Cache.Should().HaveCount(length); + observer.Cache.Should().HaveCount(length + 1); observer.Cache[1000].Value.Should().Be(12345); observer.Cache.Should().NotBeEquivalentTo(original); observer.Cache.Should().BeEquivalentTo(modified); // act: Rebuild() - observer.RebuildCache(); + observer.Rebuild(); // assert: restored to original observer.Results.Should().HaveCount(length); diff --git a/tests/indicators/_common/Observables/Stackoverflow.StaticSeries.Tests.cs b/tests/indicators/_common/Observables/StreamHub.Stackoverflow.Tests.cs similarity index 68% rename from tests/indicators/_common/Observables/Stackoverflow.StaticSeries.Tests.cs rename to tests/indicators/_common/Observables/StreamHub.Stackoverflow.Tests.cs index 8409d1b32..0e61b8fcc 100644 --- a/tests/indicators/_common/Observables/Stackoverflow.StaticSeries.Tests.cs +++ b/tests/indicators/_common/Observables/StreamHub.Stackoverflow.Tests.cs @@ -12,16 +12,16 @@ public void FatLongStack() int qtyQuotes = 20000; // setup: many random quotes (massive) - IReadOnlyList quotesList - = Data.GetRandom(qtyQuotes); + IReadOnlyList quotesList = Data.GetRandom(qtyQuotes); QuoteHub provider = new(); // setup: define ~10 subscribers (flat) - List<(string label, IEnumerable results, bool irregular)> subscribers - = new() { - HubRef(provider.ToAdl()), - HubRef(provider.ToEma(14)) }; + List<(string label, IReadOnlyList results, bool irregular)> subscribers = new() + { + HubRef(provider.ToAdl()), + HubRef(provider.ToEma(14)) + }; // all USEs foreach (CandlePart candlePart in Enum.GetValues()) @@ -35,9 +35,7 @@ IReadOnlyList quotesList provider.Add(quotesList[i]); } - subscribers.Insert(0, new( - provider.ToString(), - provider.Quotes.Cast(), false)); + subscribers.Insert(0, new(provider.ToString(), provider.Quotes, false)); // assert: this just has to not fail, really @@ -45,49 +43,37 @@ IReadOnlyList quotesList Console.WriteLine("--------------------"); // assert: all non-irregular subscribers have the same count - foreach ((string label, IEnumerable results, bool irregular) - in subscribers) + foreach ((string label, IReadOnlyList results, bool irregular) in subscribers) { - int resultQty = results.Count(); - + int resultQty = results.Count; Console.WriteLine($"Hub: {resultQty} - {label}"); - if (irregular) { continue; } - resultQty.Should().Be(qtyQuotes); } // assert: [last subscriber] has the same dates - - IReadOnlyList lastSubscriber - = subscribers[^1].results.ToList(); - + IReadOnlyList lastSubscriber = subscribers[^1].results.ToList(); for (int i = 0; i < qtyQuotes; i++) { Quote q = quotesList[i]; ISeries r = lastSubscriber[i]; - r.Timestamp.Should().Be(q.Timestamp); } // act: clear provider cache (cascades to subscribers) int cutoff = qtyQuotes / 2; - provider.ClearCache(cutoff); + provider.RemoveRange(cutoff, notify: true); provider.Quotes.Count.Should().Be(cutoff); Console.WriteLine("--------------------"); // assert: all have same count - foreach ((string label, IEnumerable results, bool irregular) - in subscribers) + foreach ((string label, IReadOnlyList results, bool irregular) in subscribers) { - int resultQty = results.Count(); - + int resultQty = results.Count; Console.WriteLine($"Cut: {resultQty} - {label}"); - if (irregular) { continue; } - resultQty.Should().Be(cutoff); } } @@ -103,14 +89,12 @@ public void ManyChainDepths() int chainDepth = 500; // setup: many random quotes (massive) - IReadOnlyList quotesList - = Data.GetRandom(qtyQuotes); + IReadOnlyList quotesList = Data.GetRandom(qtyQuotes); QuoteHub provider = new(); // setup: subscribe a large chain depth - List<(string label, IEnumerable results, bool irregular)> subscribers - = new(chainDepth + 2); + List<(string label, IReadOnlyList results, bool irregular)> subscribers = new(chainDepth + 2); SmaHub init = provider.ToSma(1); SmaHub sma = init.ToSma(2); @@ -135,9 +119,7 @@ IReadOnlyList quotesList provider.Add(quotesList[i]); } - subscribers.Insert(0, - new(provider.ToString(), - provider.Quotes.Cast(), false)); + subscribers.Insert(0, new(provider.ToString(), provider.Quotes, false)); Console.WriteLine($"Subscribers: {subscribers.Count}"); Console.WriteLine("--------------------"); @@ -145,47 +127,35 @@ IReadOnlyList quotesList // assert: this just has to not fail, really // assert: all non-irregular subscribers have the same count - foreach ((string label, IEnumerable results, bool irregular) - in subscribers) + foreach ((string label, IReadOnlyList results, bool irregular) in subscribers) { - int resultQty = results.Count(); + int resultQty = results.Count; Console.WriteLine($"Hub: {resultQty} - {label}"); if (irregular) { continue; } resultQty.Should().Be(qtyQuotes); } // assert: [last subscriber] has the same dates - - IReadOnlyList lastSubscriber - = subscribers[^1].results.ToList(); - + IReadOnlyList lastSubscriber = subscribers[^1].results.ToList(); for (int i = 0; i < qtyQuotes; i++) { Quote q = quotesList[i]; ISeries r = lastSubscriber[i]; - r.Timestamp.Should().Be(q.Timestamp); } // act: clear provider cache (cascades to subscribers) int cutoff = qtyQuotes / 2; - provider.ClearCache(cutoff); + provider.RemoveRange(cutoff, notify: true); provider.Quotes.Count.Should().Be(cutoff); // assert: all have same count - foreach ((string label, IEnumerable results, bool irregular) - in subscribers) + foreach ((string label, IReadOnlyList results, bool irregular) in subscribers) { - int resultQty = results.Count(); - + int resultQty = results.Count; Console.WriteLine($"Cut: {resultQty} - {label}"); - - if (irregular) - { - continue; - } - + if (irregular) { continue; } resultQty.Should().Be(cutoff); } } @@ -200,21 +170,20 @@ public void ManySubscribers() int qtyQuotes = 5000; // setup: many random quotes - IReadOnlyList quotesList - = Data.GetRandom(qtyQuotes); + IReadOnlyList quotesList = Data.GetRandom(qtyQuotes); QuoteHub provider = new(); // setup: define all possible subscribers // TODO: add to this as more Hubs come online - List<(string label, IEnumerable results, bool irregular)> subscribers - = new() { - HubRef(provider.ToAdl()), - HubRef(provider.ToAlligator()), - HubRef(provider.ToEma(14)), - HubRef(provider.ToRenko(2.1m), irregular: true), - HubRef(provider.ToQuote()) - }; + List<(string label, IReadOnlyList results, bool irregular)> subscribers = new() + { + HubRef(provider.ToAdl()), + HubRef(provider.ToAlligator()), + HubRef(provider.ToEma(14)), + //HubRef(provider.ToRenko(2.1m), irregular: true), + HubRef(provider.ToQuote()) + }; // all QuoteParts foreach (CandlePart candlePart in Enum.GetValues()) @@ -234,9 +203,7 @@ IReadOnlyList quotesList provider.Add(quotesList[i]); } - subscribers.Insert(0, new( - provider.ToString(), - provider.Quotes.Cast(), false)); + subscribers.Insert(0, new(provider.ToString(), provider.Quotes, false)); // assert: this just has to not fail, really @@ -244,49 +211,37 @@ IReadOnlyList quotesList Console.WriteLine("--------------------"); // assert: all non-irregular subscribers have the same count - foreach ((string label, IEnumerable results, bool irregular) - in subscribers) + foreach ((string label, IReadOnlyList results, bool irregular) in subscribers) { - int resultQty = results.Count(); - + int resultQty = results.Count; Console.WriteLine($"Hub: {resultQty} - {label}"); - if (irregular) { continue; } - resultQty.Should().Be(qtyQuotes); } // assert: [last subscriber] has the same dates - - IReadOnlyList lastSubscriber - = subscribers[^1].results.ToList(); - + IReadOnlyList lastSubscriber = subscribers[^1].results.ToList(); for (int i = 0; i < qtyQuotes; i++) { Quote q = quotesList[i]; ISeries r = lastSubscriber[i]; - r.Timestamp.Should().Be(q.Timestamp); } // act: clear provider cache (cascades to subscribers) int cutoff = qtyQuotes / 2; - provider.ClearCache(cutoff); + provider.RemoveRange(cutoff, notify: true); provider.Quotes.Count.Should().Be(cutoff); Console.WriteLine("--------------------"); // assert: all have same count - foreach ((string label, IEnumerable results, bool irregular) - in subscribers) + foreach ((string label, IReadOnlyList results, bool irregular) in subscribers) { - int resultQty = results.Count(); - + int resultQty = results.Count; Console.WriteLine($"Cut: {resultQty} - {label}"); - if (irregular) { continue; } - resultQty.Should().Be(cutoff); } } @@ -294,13 +249,12 @@ IReadOnlyList lastSubscriber /// /// Utility to get references to a hub's results. /// - private static (string, IEnumerable, bool) HubRef( + private static (string, IReadOnlyList, bool) HubRef( StreamHub hub, bool irregular = false) where TIn : ISeries where TOut : ISeries { - IEnumerable results = hub.Cache.Cast(); + IReadOnlyList results = hub.Cache; return (hub.ToString(), results, irregular); } } - diff --git a/tests/indicators/_common/Observables/Cache.Utilities.StaticSeries.Tests.cs b/tests/indicators/_common/Observables/StreamHub.Utilities.StaticSeries.Tests.cs similarity index 74% rename from tests/indicators/_common/Observables/Cache.Utilities.StaticSeries.Tests.cs rename to tests/indicators/_common/Observables/StreamHub.Utilities.StaticSeries.Tests.cs index 3030607a3..17e87a1dd 100644 --- a/tests/indicators/_common/Observables/Cache.Utilities.StaticSeries.Tests.cs +++ b/tests/indicators/_common/Observables/StreamHub.Utilities.StaticSeries.Tests.cs @@ -3,36 +3,6 @@ namespace Observables; [TestClass] public class CacheUtilities : TestBase { - [TestMethod] - public void ClearCache() - { - // setup quote provider - - List quotesList = Quotes - .ToSortedList() - .Take(10) - .ToList(); - - int length = quotesList.Count; - - QuoteHub provider = new(); - - QuotePartHub observer = provider - .ToQuotePart(CandlePart.Close); - - for (int i = 0; i < length; i++) - { - provider.Add(quotesList[i]); - } - - // act: clear cache - observer.ClearCache(); - - // assert: cache is empty - observer.Cache.Should().BeEmpty(); - provider.Cache.Should().HaveCount(10); - } - [TestMethod] public void ClearCacheByTimestamp() { @@ -58,7 +28,7 @@ public void ClearCacheByTimestamp() Quote q3 = quotesList[3]; // act: clear cache - observer.ClearCache(q3.Timestamp); + observer.RemoveRange(q3.Timestamp, notify: false); // assert: cache is empty observer.Cache.Should().HaveCount(3); @@ -79,7 +49,6 @@ List cacheUndr [TestMethod] public void ClearCacheByIndex() { - // setup quote provider List quotesList = Quotes @@ -102,7 +71,7 @@ public void ClearCacheByIndex() Quote q3 = quotesList[3]; // act: clear cache - observer.ClearCache(3); + observer.RemoveRange(3, notify: true); // assert: cache is empty observer.Cache.Should().HaveCount(3); @@ -141,8 +110,8 @@ public void GetIndex() // find position of quote Quote q = quotesList[4]; - int itemIndexEx = provider.GetIndex(q, false); - int timeIndexEx = provider.GetIndex(q.Timestamp, false); + int itemIndexEx = provider.Cache.GetIndex(q, true); + int timeIndexEx = provider.Cache.GetIndex(q.Timestamp, true); // assert: same index itemIndexEx.Should().Be(4); @@ -152,22 +121,22 @@ public void GetIndex() Quote o = Quotes[10]; Assert.ThrowsException(() => { - provider.GetIndex(o, false); + provider.Cache.GetIndex(o, true); }); Assert.ThrowsException(() => { - provider.GetIndex(o.Timestamp, false); + provider.Cache.GetIndex(o.Timestamp, true); }); // out of range (no exceptions) - int itemIndexNo = provider.GetIndex(o, true); - int timeIndexNo = provider.GetIndex(o.Timestamp, true); + int itemIndexNo = provider.Cache.GetIndex(o, false); + int timeIndexNo = provider.Cache.GetIndex(o.Timestamp, false); itemIndexNo.Should().Be(-1); timeIndexNo.Should().Be(-1); - int timeInsertOut = provider.GetInsertIndex(o.Timestamp); - int timeInsertIn = provider.GetInsertIndex(quotesList[2].Timestamp); + int timeInsertOut = provider.Cache.GetIndexGte(o.Timestamp); + int timeInsertIn = provider.Cache.GetIndexGte(quotesList[2].Timestamp); timeInsertOut.Should().Be(-1); timeInsertIn.Should().Be(2); @@ -198,7 +167,7 @@ public void TryFindIndex() // act: find index of quote // assert: correct index - if (provider.TryFindIndex(q.Timestamp, out int goodIndex)) + if (provider.Cache.TryFindIndex(q.Timestamp, out int goodIndex)) { goodIndex.Should().Be(4); } @@ -208,7 +177,7 @@ public void TryFindIndex() } // assert: out of range - if (provider.TryFindIndex(DateTime.MaxValue, out int badIndex)) + if (provider.Cache.TryFindIndex(DateTime.MaxValue, out int badIndex)) { Assert.Fail("unexpected index found"); } diff --git a/tests/indicators/_common/Quotes/Quote.Aggregates.Tests.cs b/tests/indicators/_common/Quotes/Quote.Aggregates.Tests.cs index 23df6d46f..8fa697629 100644 --- a/tests/indicators/_common/Quotes/Quote.Aggregates.Tests.cs +++ b/tests/indicators/_common/Quotes/Quote.Aggregates.Tests.cs @@ -18,7 +18,7 @@ public void Aggregate() // sample values Quote r0 = results[0]; - Assert.AreEqual(DateTime.Parse("2020-12-15 09:30", englishCulture), r0.Timestamp); + Assert.AreEqual(DateTime.Parse("2020-12-15 09:30", TestBase.invariantCulture), r0.Timestamp); Assert.AreEqual(367.40m, r0.Open); Assert.AreEqual(367.775m, r0.High); Assert.AreEqual(367.02m, r0.Low); @@ -26,7 +26,7 @@ public void Aggregate() Assert.AreEqual(2401786m, r0.Volume); Quote r1 = results[1]; - Assert.AreEqual(DateTime.Parse("2020-12-15 09:45", englishCulture), r1.Timestamp); + Assert.AreEqual(DateTime.Parse("2020-12-15 09:45", TestBase.invariantCulture), r1.Timestamp); Assert.AreEqual(367.25m, r1.Open); Assert.AreEqual(367.44m, r1.High); Assert.AreEqual(366.69m, r1.Low); @@ -34,7 +34,7 @@ public void Aggregate() Assert.AreEqual(1669983m, r1.Volume); Quote r2 = results[2]; - Assert.AreEqual(DateTime.Parse("2020-12-15 10:00", englishCulture), r2.Timestamp); + Assert.AreEqual(DateTime.Parse("2020-12-15 10:00", TestBase.invariantCulture), r2.Timestamp); Assert.AreEqual(366.85m, r2.Open); Assert.AreEqual(367.17m, r2.High); Assert.AreEqual(366.57m, r2.Low); @@ -43,7 +43,7 @@ public void Aggregate() // no history scenario IReadOnlyList noQuotes = []; - IEnumerable noResults = noQuotes.Aggregate(PeriodSize.Day); + IReadOnlyList noResults = noQuotes.Aggregate(PeriodSize.Day); Assert.IsFalse(noResults.Any()); } @@ -61,7 +61,7 @@ public void AggregateTimeSpan() // sample values Quote r0 = results[0]; - Assert.AreEqual(DateTime.Parse("2020-12-15 09:30", englishCulture), r0.Timestamp); + Assert.AreEqual(DateTime.Parse("2020-12-15 09:30", TestBase.invariantCulture), r0.Timestamp); Assert.AreEqual(367.40m, r0.Open); Assert.AreEqual(367.775m, r0.High); Assert.AreEqual(367.02m, r0.Low); @@ -69,7 +69,7 @@ public void AggregateTimeSpan() Assert.AreEqual(2401786m, r0.Volume); Quote r1 = results[1]; - Assert.AreEqual(DateTime.Parse("2020-12-15 09:45", englishCulture), r1.Timestamp); + Assert.AreEqual(DateTime.Parse("2020-12-15 09:45", TestBase.invariantCulture), r1.Timestamp); Assert.AreEqual(367.25m, r1.Open); Assert.AreEqual(367.44m, r1.High); Assert.AreEqual(366.69m, r1.Low); @@ -77,7 +77,7 @@ public void AggregateTimeSpan() Assert.AreEqual(1669983m, r1.Volume); Quote r2 = results[2]; - Assert.AreEqual(DateTime.Parse("2020-12-15 10:00", englishCulture), r2.Timestamp); + Assert.AreEqual(DateTime.Parse("2020-12-15 10:00", TestBase.invariantCulture), r2.Timestamp); Assert.AreEqual(366.85m, r2.Open); Assert.AreEqual(367.17m, r2.High); Assert.AreEqual(366.57m, r2.Low); @@ -86,7 +86,7 @@ public void AggregateTimeSpan() // no history scenario IReadOnlyList noQuotes = []; - IEnumerable noResults = noQuotes.Aggregate(TimeSpan.FromDays(1)); + IReadOnlyList noResults = noQuotes.Aggregate(TimeSpan.FromDays(1)); Assert.IsFalse(noResults.Any()); } @@ -102,7 +102,7 @@ public void AggregateMonth() // sample values Quote r0 = results[0]; - Assert.AreEqual(DateTime.Parse("2017-01-01", englishCulture), r0.Timestamp); + Assert.AreEqual(DateTime.Parse("2017-01-01", TestBase.invariantCulture), r0.Timestamp); Assert.AreEqual(212.61m, r0.Open); Assert.AreEqual(217.02m, r0.High); Assert.AreEqual(211.52m, r0.Low); @@ -110,7 +110,7 @@ public void AggregateMonth() Assert.AreEqual(1569087580m, r0.Volume); Quote r1 = results[1]; - Assert.AreEqual(DateTime.Parse("2017-02-01", englishCulture), r1.Timestamp); + Assert.AreEqual(DateTime.Parse("2017-02-01", TestBase.invariantCulture), r1.Timestamp); Assert.AreEqual(215.65m, r1.Open); Assert.AreEqual(224.20m, r1.High); Assert.AreEqual(214.29m, r1.Low); @@ -118,7 +118,7 @@ public void AggregateMonth() Assert.AreEqual(1444958340m, r1.Volume); Quote r23 = results[23]; - Assert.AreEqual(DateTime.Parse("2018-12-01", englishCulture), r23.Timestamp); + Assert.AreEqual(DateTime.Parse("2018-12-01", TestBase.invariantCulture), r23.Timestamp); Assert.AreEqual(273.47m, r23.Open); Assert.AreEqual(273.59m, r23.High); Assert.AreEqual(229.42m, r23.Low); @@ -126,10 +126,8 @@ public void AggregateMonth() Assert.AreEqual(3173255968m, r23.Volume); } - [TestMethod] - [ExpectedException(typeof(ArgumentOutOfRangeException), "Bad aggregation size.")] - public void AggregateBadSize() => - - // bad period size - Quotes.Aggregate(TimeSpan.Zero); + [TestMethod] // bad period size + public void AggregateBadSize() + => Assert.ThrowsException(() + => Quotes.Aggregate(TimeSpan.Zero)); } diff --git a/tests/indicators/_common/Quotes/Quote.Converters.Tests.cs b/tests/indicators/_common/Quotes/Quote.Converters.Tests.cs index 6e10aadc9..8d837fe98 100644 --- a/tests/indicators/_common/Quotes/Quote.Converters.Tests.cs +++ b/tests/indicators/_common/Quotes/Quote.Converters.Tests.cs @@ -7,29 +7,6 @@ namespace Utilities; [TestClass] public partial class Quotes : TestBase { - [TestMethod] - public void ToSortedCollection() - { - IReadOnlyList quotes = Data.GetMismatch(); - - Collection h = quotes.ToSortedCollection(); - - // proper quantities - Assert.AreEqual(502, h.Count); - - // check first date - DateTime firstDate = DateTime.ParseExact("01/18/2016", "MM/dd/yyyy", englishCulture); - Assert.AreEqual(firstDate, h[0].Timestamp); - - // check last date - DateTime lastDate = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", englishCulture); - Assert.AreEqual(lastDate, h.LastOrDefault().Timestamp); - - // spot check an out of sequence date - DateTime spotDate = DateTime.ParseExact("03/16/2017", "MM/dd/yyyy", englishCulture); - Assert.AreEqual(spotDate, h[50].Timestamp); - } - [TestMethod] public void ToSortedList() { @@ -41,15 +18,15 @@ public void ToSortedList() Assert.AreEqual(502, h.Count); // check first date - DateTime firstDate = DateTime.ParseExact("01/18/2016", "MM/dd/yyyy", englishCulture); + DateTime firstDate = DateTime.ParseExact("01/18/2016", "MM/dd/yyyy", TestBase.invariantCulture); Assert.AreEqual(firstDate, h[0].Timestamp); // check last date - DateTime lastDate = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", englishCulture); + DateTime lastDate = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", TestBase.invariantCulture); Assert.AreEqual(lastDate, h[^1].Timestamp); // spot check an out of sequence date - DateTime spotDate = DateTime.ParseExact("03/16/2017", "MM/dd/yyyy", englishCulture); + DateTime spotDate = DateTime.ParseExact("03/16/2017", "MM/dd/yyyy", TestBase.invariantCulture); Assert.AreEqual(spotDate, h[50].Timestamp); } } diff --git a/tests/indicators/_common/Quotes/Quote.Exceptions.Tests.cs b/tests/indicators/_common/Quotes/Quote.Exceptions.Tests.cs index 986ff7d5b..48ba52573 100644 --- a/tests/indicators/_common/Quotes/Quote.Exceptions.Tests.cs +++ b/tests/indicators/_common/Quotes/Quote.Exceptions.Tests.cs @@ -5,17 +5,17 @@ namespace Utilities; public partial class Quotes : TestBase { [TestMethod] - [ExpectedException(typeof(InvalidQuotesException), "Bad quotes without message.")] public void InvalidQuotesExceptionThrow() - => throw new InvalidQuotesException(); + => Assert.ThrowsException(() + => throw new InvalidQuotesException()); [TestMethod] - [ExpectedException(typeof(InvalidQuotesException), "Bad quotes with message.")] public void InvalidQuotesExceptionThrowWithMessage() - => throw new InvalidQuotesException("This is a quotes exception."); + => Assert.ThrowsException(() + => throw new InvalidQuotesException("This is a quotes exception.")); [TestMethod] - [ExpectedException(typeof(InvalidQuotesException), "Bad quotes with inner Exception.")] public void InvalidQuotesExceptionThrowWithInner() - => throw new InvalidQuotesException("This has an inner Exception.", new ArgumentException()); + => Assert.ThrowsException(() + => throw new InvalidQuotesException("This has an inner Exception.", new ArgumentException())); } diff --git a/tests/indicators/_common/Quotes/Quote.StreamHub.Tests.cs b/tests/indicators/_common/Quotes/Quote.StreamHub.Tests.cs index c9871b9cc..babf4c1c3 100644 --- a/tests/indicators/_common/Quotes/Quote.StreamHub.Tests.cs +++ b/tests/indicators/_common/Quotes/Quote.StreamHub.Tests.cs @@ -10,8 +10,7 @@ public override void QuoteObserver() { // tests quote redistribution - List quotesList = Quotes - .ToSortedList(); + List quotesList = Quotes.ToList(); int length = Quotes.Count; @@ -43,8 +42,7 @@ public void ChainProvider() { int smaPeriods = 8; - List quotesList = Quotes - .ToSortedList(); + List quotesList = Quotes.ToList(); int length = quotesList.Count; @@ -73,7 +71,7 @@ IReadOnlyList streamList // time-series, for comparison IReadOnlyList seriesList = quotesList - .GetSma(smaPeriods); + .ToSma(smaPeriods); // assert, should equal series streamList.Should().HaveCount(length - 1); @@ -102,8 +100,7 @@ public void AddQuote() { // covers both single and batch add - List quotesList = Quotes - .ToSortedList(); + List quotesList = Quotes.ToList(); int length = Quotes.Count; diff --git a/tests/indicators/_common/Quotes/Quote.Validation.Tests.cs b/tests/indicators/_common/Quotes/Quote.Validation.Tests.cs index 30e7a01c3..c1c54cac1 100644 --- a/tests/indicators/_common/Quotes/Quote.Validation.Tests.cs +++ b/tests/indicators/_common/Quotes/Quote.Validation.Tests.cs @@ -1,3 +1,5 @@ +using System.Globalization; + namespace Utilities; // quote validation @@ -15,10 +17,10 @@ public void Validate() Assert.AreEqual(502, h.Count); // sample values - DateTime lastDate = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", englishCulture); + DateTime lastDate = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", invariantCulture); Assert.AreEqual(lastDate, h[501].Timestamp); - DateTime spotDate = DateTime.ParseExact("02/01/2017", "MM/dd/yyyy", englishCulture); + DateTime spotDate = DateTime.ParseExact("02/01/2017", "MM/dd/yyyy", invariantCulture); Assert.AreEqual(spotDate, h[20].Timestamp); } @@ -31,7 +33,7 @@ public void ValidateLong() Assert.AreEqual(5285, h.Count); // sample values - DateTime lastDate = DateTime.ParseExact("09/04/2020", "MM/dd/yyyy", englishCulture); + DateTime lastDate = DateTime.ParseExact("09/04/2020", "MM/dd/yyyy", invariantCulture); Assert.AreEqual(lastDate, h[5284].Timestamp); } @@ -48,7 +50,7 @@ public void ValidateCut() Assert.AreEqual(200, h.Count); // should be 20 results and no index corruption - IReadOnlyList r1 = h.TakeLast(20).GetSma(14).ToList(); + IReadOnlyList r1 = h.TakeLast(20).ToList().ToSma(14).ToList(); Assert.AreEqual(20, r1.Count); for (int i = 1; i < r1.Count; i++) @@ -57,7 +59,7 @@ public void ValidateCut() } // should be 50 results and no index corruption - IReadOnlyList r2 = h.TakeLast(50).GetSma(14).ToList(); + IReadOnlyList r2 = h.TakeLast(50).ToList().ToSma(14).ToList(); Assert.AreEqual(50, r2.Count); for (int i = 1; i < r2.Count; i++) @@ -77,19 +79,39 @@ public void ValidateCut() [TestMethod] public void ValidateDuplicates() { - IReadOnlyList badHistory = - [ - new(Timestamp: DateTime.ParseExact("2017-01-03", "yyyy-MM-dd", englishCulture), Open: 214.86m, High: 220.33m, Low: 210.96m, Close: 216.99m, Volume: 5923254), - new(Timestamp: DateTime.ParseExact("2017-01-04", "yyyy-MM-dd", englishCulture), Open: 214.75m, High: 228.00m, Low: 214.31m, Close: 226.99m, Volume: 11213471), - new(Timestamp: DateTime.ParseExact("2017-01-05", "yyyy-MM-dd", englishCulture), Open: 226.42m, High: 227.48m, Low: 221.95m, Close: 226.75m, Volume: 5911695), - new(Timestamp: DateTime.ParseExact("2017-01-06", "yyyy-MM-dd", englishCulture), Open: 226.93m, High: 230.31m, Low: 225.45m, Close: 229.01m, Volume: 5527893), - new(Timestamp: DateTime.ParseExact("2017-01-06", "yyyy-MM-dd", englishCulture), Open: 228.97m, High: 231.92m, Low: 228.00m, Close: 231.28m, Volume: 3979484) - ]; - - InvalidQuotesException ex = - Assert.ThrowsException(() - => badHistory.Validate()); - - ex.Message.Should().Contain("Duplicate date found on 2017-01-06T00:00:00.0000000."); + IReadOnlyList dupQuotes = new List + { + new(Timestamp: DateTime.ParseExact("2017-01-03", "yyyy-MM-dd", invariantCulture), Open: 214.86m, High: 220.33m, Low: 210.96m, Close: 216.99m, Volume: 5923254), + new(Timestamp: DateTime.ParseExact("2017-01-04", "yyyy-MM-dd", invariantCulture), Open: 214.75m, High: 228.00m, Low: 214.31m, Close: 226.99m, Volume: 11213471), + new(Timestamp: DateTime.ParseExact("2017-01-05", "yyyy-MM-dd", invariantCulture), Open: 226.42m, High: 227.48m, Low: 221.95m, Close: 226.75m, Volume: 5911695), + new(Timestamp: DateTime.ParseExact("2017-01-06", "yyyy-MM-dd", invariantCulture), Open: 226.93m, High: 230.31m, Low: 225.45m, Close: 229.01m, Volume: 5527893), + new(Timestamp: DateTime.ParseExact("2017-01-06", "yyyy-MM-dd", invariantCulture), Open: 228.97m, High: 231.92m, Low: 228.00m, Close: 231.28m, Volume: 3979484) + }; + + InvalidQuotesException dx + = Assert.ThrowsException( + () => dupQuotes.Validate()); + + dx.Message.Should().Contain("Duplicate date found on 2017-01-06T00:00:00.0000000."); + } + + [TestMethod] + public void ValidateOutOfSequence() + { + IReadOnlyList unorderedQuotes = new List + { + new(Timestamp: DateTime.ParseExact("2017-01-03", "yyyy-MM-dd", invariantCulture), Open: 214.86m, High: 220.33m, Low: 210.96m, Close: 216.99m, Volume: 5923254), + new(Timestamp: DateTime.ParseExact("2017-01-04", "yyyy-MM-dd", invariantCulture), Open: 214.75m, High: 228.00m, Low: 214.31m, Close: 226.99m, Volume: 11213471), + new(Timestamp: DateTime.ParseExact("2017-01-06", "yyyy-MM-dd", invariantCulture), Open: 228.97m, High: 231.92m, Low: 228.00m, Close: 231.28m, Volume: 3979484), + new(Timestamp: DateTime.ParseExact("2017-01-05", "yyyy-MM-dd", invariantCulture), Open: 226.42m, High: 227.48m, Low: 221.95m, Close: 226.75m, Volume: 5911695), + new(Timestamp: DateTime.ParseExact("2017-01-06", "yyyy-MM-dd", invariantCulture), Open: 226.93m, High: 230.31m, Low: 225.45m, Close: 229.01m, Volume: 5527893) + }; + + InvalidQuotesException dx + = Assert.ThrowsException( + () => unorderedQuotes.Validate()); + + dx.Message.Should() + .Contain("Quotes are out of sequence on 2017-01-05T00:00:00.0000000."); } } diff --git a/tests/indicators/_common/Reusable/Reusable.Utilities.StaticSeries.Tests.cs b/tests/indicators/_common/Reusable/Reusable.Utilities.Tests.cs similarity index 95% rename from tests/indicators/_common/Reusable/Reusable.Utilities.StaticSeries.Tests.cs rename to tests/indicators/_common/Reusable/Reusable.Utilities.Tests.cs index c209a5871..f8bba07d7 100644 --- a/tests/indicators/_common/Reusable/Reusable.Utilities.StaticSeries.Tests.cs +++ b/tests/indicators/_common/Reusable/Reusable.Utilities.Tests.cs @@ -7,7 +7,7 @@ public class Reusable : TestBase public void Condense() { List original = Quotes - .GetAdx() + .ToAdx() .ToList(); // make a few more in the middle null and NaN @@ -35,13 +35,13 @@ public void ToReusableList() Assert.IsNotNull(reusableList); Assert.AreEqual(502, reusableList.Count); - Assert.AreEqual(reusableList[^1].Value, 245.28d); + Assert.AreEqual(245.28d, reusableList[^1].Value); } [TestMethod] public void QuoteToReusable() { - DateTime t = DateTime.Parse("5/5/2055", englishCulture); + DateTime t = DateTime.Parse("5/5/2055", invariantCulture); decimal l = 111111111111111m; decimal o = 222222222222222m; diff --git a/tests/indicators/_common/Use (QuotePart)/Use.StaticSeries.Tests.cs b/tests/indicators/_common/Use (QuotePart)/QuotePart.StaticSeries.Tests.cs similarity index 96% rename from tests/indicators/_common/Use (QuotePart)/Use.StaticSeries.Tests.cs rename to tests/indicators/_common/Use (QuotePart)/QuotePart.StaticSeries.Tests.cs index 8acf42ac5..72c57b1a2 100644 --- a/tests/indicators/_common/Use (QuotePart)/Use.StaticSeries.Tests.cs +++ b/tests/indicators/_common/Use (QuotePart)/QuotePart.StaticSeries.Tests.cs @@ -1,7 +1,7 @@ namespace StaticSeries; [TestClass] -public class Use : StaticSeriesTestBase +public class QuoteParts : StaticSeriesTestBase { [TestMethod] public override void Standard() @@ -34,7 +34,7 @@ public override void Standard() QuotePart rohlc = ohlc[501]; // proper last date - DateTime lastDate = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", englishCulture); + DateTime lastDate = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", invariantCulture); Assert.AreEqual(lastDate, rc.Timestamp); // last values should be correct @@ -56,7 +56,7 @@ public void Chainor() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetSma(10); + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(493, results.Count(x => x.Sma != null)); diff --git a/tests/indicators/_common/Use (QuotePart)/QuotePart.StreamHub.Tests.cs b/tests/indicators/_common/Use (QuotePart)/QuotePart.StreamHub.Tests.cs index 4dc2ac8a1..9a5bbb284 100644 --- a/tests/indicators/_common/Use (QuotePart)/QuotePart.StreamHub.Tests.cs +++ b/tests/indicators/_common/Use (QuotePart)/QuotePart.StreamHub.Tests.cs @@ -8,8 +8,7 @@ public override void QuoteObserver() { CandlePart candlePart = CandlePart.HLC3; - List quotesList = Quotes - .ToSortedList(); + List quotesList = Quotes.ToList(); int length = quotesList.Count; @@ -50,7 +49,7 @@ IReadOnlyList streamList } // late arrival - provider.Add(quotesList[80]); + provider.Insert(quotesList[80]); // delete provider.Remove(quotesList[400]); @@ -84,8 +83,7 @@ public void ChainProvider() int smaPeriods = 8; CandlePart candlePart = CandlePart.OHLC4; - List quotesList = Quotes - .ToSortedList(); + List quotesList = Quotes.ToList(); int length = quotesList.Count; @@ -116,7 +114,7 @@ IReadOnlyList streamList IReadOnlyList seriesList = quotesList .Use(candlePart) - .GetSma(smaPeriods); + .ToSma(smaPeriods); // assert, should equal series for (int i = 0; i < length - 1; i++) diff --git a/tests/indicators/_common/Use (QuotePart)/QuotePart.Utilities.StaticSeries.Tests.cs b/tests/indicators/_common/Use (QuotePart)/QuotePart.Utilities.Tests.cs similarity index 98% rename from tests/indicators/_common/Use (QuotePart)/QuotePart.Utilities.StaticSeries.Tests.cs rename to tests/indicators/_common/Use (QuotePart)/QuotePart.Utilities.Tests.cs index ed7fe3524..bbf643f4b 100644 --- a/tests/indicators/_common/Use (QuotePart)/QuotePart.Utilities.StaticSeries.Tests.cs +++ b/tests/indicators/_common/Use (QuotePart)/QuotePart.Utilities.Tests.cs @@ -22,7 +22,7 @@ public void ConvertQuote() QuotePart ohlc = Quotes[501].ToQuotePart(CandlePart.OHLC4); // proper last date - DateTime lastDate = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", englishCulture); + DateTime lastDate = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", invariantCulture); Assert.AreEqual(lastDate, c.Timestamp); // last values should be correct @@ -70,7 +70,7 @@ public void ConvertList() QuotePart rohlc = ohlc[501]; // proper last date - DateTime lastDate = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", englishCulture); + DateTime lastDate = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", invariantCulture); Assert.AreEqual(lastDate, rc.Timestamp); // last values should be correct diff --git a/tests/indicators/_testdata/TestData.Getter.cs b/tests/indicators/_testdata/TestData.Getter.cs index f049d5485..69d763678 100644 --- a/tests/indicators/_testdata/TestData.Getter.cs +++ b/tests/indicators/_testdata/TestData.Getter.cs @@ -64,7 +64,7 @@ internal static IReadOnlyList GetIntraday(int days = 1564) .Take(days) .ToSortedList(); - // LONGEST DATA ~62 years of S&P 500 daily data + // LONGEST DATA ~62 years of S&P 500 daily data (15,821) internal static IReadOnlyList GetLongest() => File.ReadAllLines("_testdata/data/longest.csv") .Skip(1) diff --git a/tests/indicators/_testdata/TestData.Tests.cs b/tests/indicators/_testdata/TestData.Tests.cs new file mode 100644 index 000000000..7fcf9406f --- /dev/null +++ b/tests/indicators/_testdata/TestData.Tests.cs @@ -0,0 +1,77 @@ +namespace TestOfTests; + +[TestClass] +public class TestData : TestBase +{ + // Test the test data to + // ensure it meets the expected format + + [TestMethod] + public void QuotesIsValid() + { + Quotes.Should().HaveCount(502); + Quotes.Validate(); + } + + [TestMethod] + public void OtherQuotesIsValid() + { + OtherQuotes.Should().HaveCount(502); + OtherQuotes.Validate(); + } + + [TestMethod] + public void BadQuotesIsInvalid() + { + BadQuotes.Should().HaveCount(502); + + // duplicates + Assert.ThrowsException( + () => BadQuotes.Validate()); + } + + [TestMethod] + public void BigQuotesIsValid() + { + BigQuotes.Should().HaveCount(1246); + BigQuotes.Validate(); + } + + [TestMethod] + public void LongishQuotesIsValid() + { + LongishQuotes.Should().HaveCount(5285); + LongishQuotes.Validate(); + } + + [TestMethod] + public void LongestQuotesIsValid() + { + LongestQuotes.Should().HaveCount(15821); + LongestQuotes.Validate(); + } + + [TestMethod] + public void MismatchQuotesIsValid() + { + MismatchQuotes.Should().HaveCount(502); + + // out of sequence + Assert.ThrowsException( + () => MismatchQuotes.Validate()); + } + + [TestMethod] + public void RandomQuotesIsValid() + { + RandomQuotes.Should().HaveCount(1000); + RandomQuotes.Validate(); + } + + [TestMethod] + public void ZeroesQuotesIsValid() + { + ZeroesQuotes.Should().HaveCount(200); + ZeroesQuotes.Validate(); + } +} diff --git a/tests/indicators/a-d/Adl/Adl.StaticSeries.Tests.cs b/tests/indicators/a-d/Adl/Adl.StaticSeries.Tests.cs index 94f245bc7..1c98e391d 100644 --- a/tests/indicators/a-d/Adl/Adl.StaticSeries.Tests.cs +++ b/tests/indicators/a-d/Adl/Adl.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Adl : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetAdl(); + .ToAdl(); // proper quantities Assert.AreEqual(502, results.Count); @@ -28,8 +28,8 @@ public override void Standard() public void Chainor() { IReadOnlyList results = Quotes - .GetAdl() - .GetSma(10); + .ToAdl() + .ToSma(10); // assertions @@ -42,7 +42,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetAdl(); + .ToAdl(); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => double.IsNaN(x.Adl))); @@ -52,7 +52,7 @@ public override void BadData() public void BigData() { IReadOnlyList r = BigQuotes - .GetAdl(); + .ToAdl(); Assert.AreEqual(1246, r.Count); } @@ -61,7 +61,7 @@ public void BigData() public void RandomData() { IReadOnlyList r = RandomQuotes - .GetAdl(); + .ToAdl(); Assert.AreEqual(1000, r.Count); } @@ -70,12 +70,12 @@ public void RandomData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetAdl(); + .ToAdl(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetAdl(); + .ToAdl(); Assert.AreEqual(1, r1.Count); } diff --git a/tests/indicators/a-d/Adl/Adl.StreamHub.Tests.cs b/tests/indicators/a-d/Adl/Adl.StreamHub.Tests.cs index 2714bae73..0b2d3e66b 100644 --- a/tests/indicators/a-d/Adl/Adl.StreamHub.Tests.cs +++ b/tests/indicators/a-d/Adl/Adl.StreamHub.Tests.cs @@ -6,8 +6,7 @@ public class AdlHub : StreamHubTestBase, ITestChainProvider [TestMethod] public override void QuoteObserver() { - List quotesList = Quotes - .ToSortedList(); + List quotesList = Quotes.ToList(); int length = quotesList.Count; @@ -48,7 +47,7 @@ IReadOnlyList streamList } // late arrival - provider.Add(quotesList[80]); + provider.Insert(quotesList[80]); // delete provider.Remove(quotesList[400]); @@ -56,7 +55,7 @@ IReadOnlyList streamList // time-series, for comparison IReadOnlyList seriesList = quotesList - .GetAdl(); + .ToAdl(); // assert, should equal series streamList.Should().HaveCount(length - 1); @@ -69,10 +68,9 @@ IReadOnlyList streamList [TestMethod] public void ChainProvider() { - int smaPeriods = 8; + const int smaPeriods = 8; - List quotesList = Quotes - .ToSortedList(); + List quotesList = Quotes.ToList(); int length = quotesList.Count; @@ -80,7 +78,7 @@ public void ChainProvider() QuoteHub provider = new(); // initialize observer - IChainProvider adlHub = provider + AdlHub adlHub = provider .ToAdl(); SmaHub observer = adlHub @@ -102,8 +100,8 @@ IReadOnlyList streamList // time-series, for comparison IReadOnlyList seriesList = quotesList - .GetAdl() - .GetSma(smaPeriods); + .ToAdl() + .ToSma(smaPeriods); // assert, should equal series streamList.Should().HaveCount(length - 1); diff --git a/tests/indicators/a-d/Adx/Adx.StaticSeries.Tests.cs b/tests/indicators/a-d/Adx/Adx.StaticSeries.Tests.cs index fdd6e89c1..ca0a5c213 100644 --- a/tests/indicators/a-d/Adx/Adx.StaticSeries.Tests.cs +++ b/tests/indicators/a-d/Adx/Adx.StaticSeries.Tests.cs @@ -6,7 +6,7 @@ public class Adx : StaticSeriesTestBase [TestMethod] public override void Standard() { - IReadOnlyList results = Quotes.GetAdx(); + IReadOnlyList results = Quotes.ToAdx(); // proper quantities Assert.AreEqual(502, results.Count); @@ -46,8 +46,8 @@ public override void Standard() public void Chainor() { IReadOnlyList results = Quotes - .GetAdx() - .GetSma(10); + .ToAdx() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(466, results.Count(x => x.Sma != null)); @@ -56,7 +56,7 @@ public void Chainor() [TestMethod] public override void BadData() { - IReadOnlyList r = BadQuotes.GetAdx(20); + IReadOnlyList r = BadQuotes.ToAdx(20); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Adx is double.NaN)); @@ -65,7 +65,7 @@ public override void BadData() [TestMethod] public void BigData() { - IReadOnlyList r = BigQuotes.GetAdx(200); + IReadOnlyList r = BigQuotes.ToAdx(200); Assert.AreEqual(1246, r.Count); } @@ -73,11 +73,11 @@ public void BigData() [TestMethod] public override void NoQuotes() { - IReadOnlyList r0 = Noquotes.GetAdx(5); + IReadOnlyList r0 = Noquotes.ToAdx(5); Assert.AreEqual(0, r0.Count); - IReadOnlyList r1 = Onequote.GetAdx(5); + IReadOnlyList r1 = Onequote.ToAdx(5); Assert.AreEqual(1, r1.Count); } @@ -85,12 +85,13 @@ public override void NoQuotes() [TestMethod] public void Issue859() { - IOrderedEnumerable test859 = File.ReadAllLines("a-d/Adx/issue859quotes.csv") + List test859 = File.ReadAllLines("a-d/Adx/issue859quotes.csv") .Skip(1) .Select(Imports.QuoteFromCsv) - .OrderByDescending(x => x.Timestamp); + .OrderByDescending(x => x.Timestamp) + .ToList(); - IReadOnlyList r = test859.GetAdx(); + IReadOnlyList r = test859.ToAdx(); Assert.AreEqual(0, r.Count(x => x.Adx is double.NaN)); Assert.AreEqual(595, r.Count); @@ -99,7 +100,7 @@ public void Issue859() [TestMethod] public void Zeroes() { - IReadOnlyList r = ZeroesQuotes.GetAdx(); + IReadOnlyList r = ZeroesQuotes.ToAdx(); Assert.AreEqual(0, r.Count(x => x.Adx is double.NaN)); Assert.AreEqual(200, r.Count); @@ -109,7 +110,7 @@ public void Zeroes() public void Removed() { IReadOnlyList results = Quotes - .GetAdx() + .ToAdx() .RemoveWarmupPeriods(); // assertions @@ -126,5 +127,5 @@ public void Exceptions() => // bad lookback period Assert.ThrowsException(() => - Quotes.GetAdx(1)); + Quotes.ToAdx(1)); } diff --git a/tests/indicators/a-d/Alligator/Alligator.StaticSeries.Tests.cs b/tests/indicators/a-d/Alligator/Alligator.StaticSeries.Tests.cs index ab84e4578..e8c682b40 100644 --- a/tests/indicators/a-d/Alligator/Alligator.StaticSeries.Tests.cs +++ b/tests/indicators/a-d/Alligator/Alligator.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Alligator : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetAlligator(); + .ToAlligator(); // proper quantities Assert.AreEqual(502, results.Count); @@ -46,8 +46,8 @@ public override void Standard() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetAlligator(); + .ToSma(2) + .ToAlligator(); Assert.AreEqual(502, results.Count); Assert.AreEqual(481, results.Count(x => x.Jaw != null)); @@ -57,7 +57,7 @@ public void Chainee() public override void BadData() { IReadOnlyList r = BadQuotes - .GetAlligator(3, 3, 2, 1, 1, 1); + .ToAlligator(3, 3, 2, 1, 1, 1); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Jaw is double.NaN)); @@ -67,12 +67,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetAlligator(); + .ToAlligator(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetAlligator(); + .ToAlligator(); Assert.AreEqual(1, r1.Count); } @@ -81,7 +81,7 @@ public override void NoQuotes() public void Condense() { IReadOnlyList results = Quotes - .GetAlligator() + .ToAlligator() .Condense(); Assert.AreEqual(495, results.Count); @@ -96,7 +96,7 @@ public void Condense() public void Removed() { IReadOnlyList results = Quotes - .GetAlligator() + .ToAlligator() .RemoveWarmupPeriods(); Assert.AreEqual(502 - 21 - 250, results.Count); @@ -123,34 +123,34 @@ public void Exceptions() { // bad jaw lookback periods Assert.ThrowsException(() => - Quotes.GetAlligator(13, 8, 13)); + Quotes.ToAlligator(13, 8, 13)); // bad teeth lookback periods Assert.ThrowsException(() => - Quotes.GetAlligator(13, 8, 8, 5, 8)); + Quotes.ToAlligator(13, 8, 8, 5, 8)); // bad lips lookback periods Assert.ThrowsException(() => - Quotes.GetAlligator(13, 8, 8, 5, 0)); + Quotes.ToAlligator(13, 8, 8, 5, 0)); // bad jaw offset periods Assert.ThrowsException(() => - Quotes.GetAlligator(13, 0)); + Quotes.ToAlligator(13, 0)); // bad teeth offset periods Assert.ThrowsException(() => - Quotes.GetAlligator(13, 8, 8, 0)); + Quotes.ToAlligator(13, 8, 8, 0)); // bad lips offset periods Assert.ThrowsException(() => - Quotes.GetAlligator(13, 8, 8, 5, 5, 0)); + Quotes.ToAlligator(13, 8, 8, 5, 5, 0)); // bad jaw + offset periods Assert.ThrowsException(() => - Quotes.GetAlligator(13, 8, 12, 11)); + Quotes.ToAlligator(13, 8, 12, 11)); // bad teeth + offset periods Assert.ThrowsException(() => - Quotes.GetAlligator(13, 8, 8, 5, 7, 7)); + Quotes.ToAlligator(13, 8, 8, 5, 7, 7)); } } diff --git a/tests/indicators/a-d/Alligator/Alligator.StreamHub.Tests.cs b/tests/indicators/a-d/Alligator/Alligator.StreamHub.Tests.cs index f88c3b7ef..f6db7e617 100644 --- a/tests/indicators/a-d/Alligator/Alligator.StreamHub.Tests.cs +++ b/tests/indicators/a-d/Alligator/Alligator.StreamHub.Tests.cs @@ -6,8 +6,7 @@ public class AlligatorHub : StreamHubTestBase, ITestChainObserver [TestMethod] public override void QuoteObserver() { - List quotesList = Quotes - .ToSortedList(); + List quotesList = Quotes.ToList(); int length = quotesList.Count; @@ -48,7 +47,7 @@ IReadOnlyList streamList } // late arrival - provider.Add(quotesList[80]); + provider.Insert(quotesList[80]); // delete provider.Remove(quotesList[400]); @@ -57,7 +56,7 @@ IReadOnlyList streamList // time-series, for comparison IReadOnlyList seriesList = quotesList - .GetAlligator(); + .ToAlligator(); // assert, should equal series streamList.Should().HaveCount(length - 1); @@ -70,8 +69,7 @@ IReadOnlyList seriesList [TestMethod] public void ChainObserver() { - List quotesList = Quotes - .ToSortedList(); + List quotesList = Quotes.ToList(); int length = quotesList.Count; @@ -104,7 +102,7 @@ public void ChainObserver() } // late arrival - provider.Add(quotesList[80]); + provider.Insert(quotesList[80]); // delete provider.Remove(quotesList[400]); @@ -117,8 +115,8 @@ IReadOnlyList streamList // time-series, for comparison IReadOnlyList seriesList = quotesList - .GetSma(10) - .GetAlligator(); + .ToSma(10) + .ToAlligator(); // assert, should equal series streamList.Should().HaveCount(length - 1); diff --git a/tests/indicators/a-d/Alma/Alma.StaticSeries.Tests.cs b/tests/indicators/a-d/Alma/Alma.StaticSeries.Tests.cs index 75a77bb66..d9ccb1884 100644 --- a/tests/indicators/a-d/Alma/Alma.StaticSeries.Tests.cs +++ b/tests/indicators/a-d/Alma/Alma.StaticSeries.Tests.cs @@ -11,7 +11,7 @@ public override void Standard() double sigma = 6; IReadOnlyList results = Quotes - .GetAlma(lookbackPeriods, offset, sigma); + .ToAlma(lookbackPeriods, offset, sigma); // proper quantities Assert.AreEqual(502, results.Count); @@ -42,7 +42,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetAlma(10); + .ToAlma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(493, results.Count(x => x.Alma != null)); @@ -55,8 +55,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetAlma(10); + .ToSma(2) + .ToAlma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(492, results.Count(x => x.Alma != null)); @@ -70,8 +70,8 @@ public void Chainor() double sigma = 6; IReadOnlyList results = Quotes - .GetAlma(lookbackPeriods, offset, sigma) - .GetSma(10); + .ToAlma(lookbackPeriods, offset, sigma) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(484, results.Count(x => x.Sma != null)); @@ -81,12 +81,12 @@ public void Chainor() public void NaN() { IReadOnlyList r1 - = Data.GetBtcUsdNan().GetAlma(); + = Data.GetBtcUsdNan().ToAlma(); Assert.AreEqual(0, r1.Count(x => x.Alma is double.NaN)); IReadOnlyList r2 - = Data.GetBtcUsdNan().GetAlma(20); + = Data.GetBtcUsdNan().ToAlma(20); Assert.AreEqual(0, r2.Count(x => x.Alma is double.NaN)); } @@ -94,7 +94,7 @@ IReadOnlyList r2 [TestMethod] public override void BadData() { - IReadOnlyList r = BadQuotes.GetAlma(14, 0.5, 3); + IReadOnlyList r = BadQuotes.ToAlma(14, 0.5, 3); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Alma is double.NaN)); @@ -103,11 +103,11 @@ public override void BadData() [TestMethod] public override void NoQuotes() { - IReadOnlyList r0 = Noquotes.GetAlma(); + IReadOnlyList r0 = Noquotes.ToAlma(); Assert.AreEqual(0, r0.Count); - IReadOnlyList r1 = Onequote.GetAlma(); + IReadOnlyList r1 = Onequote.ToAlma(); Assert.AreEqual(1, r1.Count); } @@ -116,7 +116,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetAlma(10) + .ToAlma(10) .RemoveWarmupPeriods(); // assertions @@ -131,14 +131,14 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() => - Quotes.GetAlma(0, 1, 5)); + Quotes.ToAlma(0, 1, 5)); // bad offset Assert.ThrowsException(() => - Quotes.GetAlma(15, 1.1, 3)); + Quotes.ToAlma(15, 1.1, 3)); // bad sigma Assert.ThrowsException(() => - Quotes.GetAlma(10, 0.5, 0)); + Quotes.ToAlma(10, 0.5, 0)); } } diff --git a/tests/indicators/a-d/Aroon/Aroon.StaticSeries.Tests.cs b/tests/indicators/a-d/Aroon/Aroon.StaticSeries.Tests.cs index 4a193d5c6..db6dcba18 100644 --- a/tests/indicators/a-d/Aroon/Aroon.StaticSeries.Tests.cs +++ b/tests/indicators/a-d/Aroon/Aroon.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Aroon : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetAroon(); + .ToAroon(); // proper quantities Assert.AreEqual(502, results.Count); @@ -46,8 +46,8 @@ public override void Standard() public void Chainor() { IReadOnlyList results = Quotes - .GetAroon() - .GetSma(10); + .ToAroon() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(468, results.Count(x => x.Sma != null)); @@ -57,7 +57,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetAroon(20); + .ToAroon(20); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Oscillator is double.NaN)); @@ -67,12 +67,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetAroon(); + .ToAroon(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetAroon(); + .ToAroon(); Assert.AreEqual(1, r1.Count); } @@ -81,7 +81,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetAroon() + .ToAroon() .RemoveWarmupPeriods(); // assertions @@ -97,5 +97,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetAroon(0)); + => Quotes.ToAroon(0)); } diff --git a/tests/indicators/a-d/Atr/Atr.StaticSeries.Tests.cs b/tests/indicators/a-d/Atr/Atr.StaticSeries.Tests.cs index 1fe4b8944..0682f0fc3 100644 --- a/tests/indicators/a-d/Atr/Atr.StaticSeries.Tests.cs +++ b/tests/indicators/a-d/Atr/Atr.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Atr : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetAtr(); + .ToAtr(); // proper quantities Assert.AreEqual(502, results.Count); @@ -44,10 +44,10 @@ public override void Standard() public void MatchingTrueRange() { IReadOnlyList resultsAtr = Quotes - .GetAtr(14); + .ToAtr(14); IReadOnlyList resultsTr = Quotes - .GetTr(); + .ToTr(); for (int i = 0; i < Quotes.Count; i++) { @@ -65,8 +65,8 @@ public void MatchingTrueRange() public void Chainor() { IReadOnlyList results = Quotes - .GetAtr(10) - .GetSma(10); + .ToAtr(10) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(502 - 19, results.Count(x => x.Sma != null)); @@ -76,7 +76,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetAtr(20); + .ToAtr(20); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Atr is double.NaN)); @@ -86,12 +86,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetAtr(); + .ToAtr(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetAtr(); + .ToAtr(); Assert.AreEqual(1, r1.Count); } @@ -100,7 +100,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetAtr() + .ToAtr() .RemoveWarmupPeriods(); // assertions @@ -116,5 +116,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() => - Quotes.GetAtr(1)); + Quotes.ToAtr(1)); } diff --git a/tests/indicators/a-d/Atr/Atr.StreamHub.Tests.cs b/tests/indicators/a-d/Atr/Atr.StreamHub.Tests.cs index 43374ed83..0f3e5e419 100644 --- a/tests/indicators/a-d/Atr/Atr.StreamHub.Tests.cs +++ b/tests/indicators/a-d/Atr/Atr.StreamHub.Tests.cs @@ -6,8 +6,7 @@ public class AtrHub : StreamHubTestBase, ITestChainProvider [TestMethod] public override void QuoteObserver() { - List quotesList = Quotes - .ToSortedList(); + List quotesList = Quotes.ToList(); int length = quotesList.Count; @@ -48,7 +47,7 @@ IReadOnlyList streamList } // late arrival - provider.Add(quotesList[80]); + provider.Insert(quotesList[80]); // delete provider.Remove(quotesList[400]); @@ -56,7 +55,7 @@ IReadOnlyList streamList // time-series, for comparison IReadOnlyList seriesList = quotesList - .GetAtr(14); + .ToAtr(14); // assert, should equal series streamList.Should().HaveCount(length - 1); @@ -71,8 +70,7 @@ public void ChainProvider() { int smaPeriods = 8; - List quotesList = Quotes - .ToSortedList(); + List quotesList = Quotes.ToList(); int length = quotesList.Count; @@ -102,8 +100,8 @@ IReadOnlyList streamList // time-series, for comparison IReadOnlyList seriesList = quotesList - .GetAtr(14) - .GetSma(smaPeriods); + .ToAtr(14) + .ToSma(smaPeriods); // assert, should equal series streamList.Should().HaveCount(length - 1); diff --git a/tests/indicators/a-d/AtrStop/AtrStop.StaticSeries.Tests.cs b/tests/indicators/a-d/AtrStop/AtrStop.StaticSeries.Tests.cs index 657543678..091b89486 100644 --- a/tests/indicators/a-d/AtrStop/AtrStop.StaticSeries.Tests.cs +++ b/tests/indicators/a-d/AtrStop/AtrStop.StaticSeries.Tests.cs @@ -10,7 +10,7 @@ public override void Standard() double multiplier = 3; IReadOnlyList results = Quotes - .GetAtrStop(lookbackPeriods, multiplier); + .ToAtrStop(lookbackPeriods, multiplier); // proper quantities Assert.AreEqual(502, results.Count); @@ -23,27 +23,27 @@ public override void Standard() Assert.AreEqual(null, r20.SellStop); AtrStopResult r21 = results[21]; - Assert.AreEqual(211.13m, r21.AtrStop.Round(4)); + Assert.AreEqual(211.13, r21.AtrStop.Round(4)); Assert.AreEqual(null, r21.BuyStop); Assert.AreEqual(r21.AtrStop, r21.SellStop); AtrStopResult r151 = results[151]; - Assert.AreEqual(232.7861m, r151.AtrStop.Round(4)); + Assert.AreEqual(232.7861, r151.AtrStop.Round(4)); Assert.AreEqual(null, r151.BuyStop); Assert.AreEqual(r151.AtrStop, r151.SellStop); AtrStopResult r152 = results[152]; - Assert.AreEqual(236.3913m, r152.AtrStop.Round(4)); + Assert.AreEqual(236.3913, r152.AtrStop.Round(4)); Assert.AreEqual(r152.AtrStop, r152.BuyStop); Assert.AreEqual(null, r152.SellStop); AtrStopResult r249 = results[249]; - Assert.AreEqual(253.8863m, r249.AtrStop.Round(4)); + Assert.AreEqual(253.8863, r249.AtrStop.Round(4)); Assert.AreEqual(null, r249.BuyStop); Assert.AreEqual(r249.AtrStop, r249.SellStop); AtrStopResult r501 = results[501]; - Assert.AreEqual(246.3232m, r501.AtrStop.Round(4)); + Assert.AreEqual(246.3232, r501.AtrStop.Round(4)); Assert.AreEqual(r501.AtrStop, r501.BuyStop); Assert.AreEqual(null, r501.SellStop); } @@ -55,7 +55,7 @@ public void HighLow() double multiplier = 3; IReadOnlyList results = Quotes - .GetAtrStop(lookbackPeriods, multiplier, EndType.HighLow); + .ToAtrStop(lookbackPeriods, multiplier, EndType.HighLow); // proper quantities Assert.AreEqual(502, results.Count); @@ -68,27 +68,27 @@ public void HighLow() Assert.AreEqual(null, r20.SellStop); AtrStopResult r21 = results[21]; - Assert.AreEqual(210.23m, r21.AtrStop.Round(4)); + Assert.AreEqual(210.23, r21.AtrStop.Round(4)); Assert.AreEqual(null, r21.BuyStop); Assert.AreEqual(r21.AtrStop, r21.SellStop); AtrStopResult r69 = results[69]; - Assert.AreEqual(221.0594m, r69.AtrStop.Round(4)); + Assert.AreEqual(221.0594, r69.AtrStop.Round(4)); Assert.AreEqual(null, r69.BuyStop); Assert.AreEqual(r69.AtrStop, r69.SellStop); AtrStopResult r70 = results[70]; - Assert.AreEqual(226.4624m, r70.AtrStop.Round(4)); + Assert.AreEqual(226.4624, r70.AtrStop.Round(4)); Assert.AreEqual(r70.AtrStop, r70.BuyStop); Assert.AreEqual(null, r70.SellStop); AtrStopResult r249 = results[249]; - Assert.AreEqual(253.4863m, r249.AtrStop.Round(4)); + Assert.AreEqual(253.4863, r249.AtrStop.Round(4)); Assert.AreEqual(null, r249.BuyStop); Assert.AreEqual(r249.AtrStop, r249.SellStop); AtrStopResult r501 = results[501]; - Assert.AreEqual(252.6932m, r501.AtrStop.Round(4)); + Assert.AreEqual(252.6932, r501.AtrStop.Round(4)); Assert.AreEqual(r501.AtrStop, r501.BuyStop); Assert.AreEqual(null, r501.SellStop); } @@ -97,7 +97,7 @@ public void HighLow() public override void BadData() { IReadOnlyList r = BadQuotes - .GetAtrStop(7); + .ToAtrStop(7); Assert.AreEqual(502, r.Count); } @@ -106,12 +106,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetAtrStop(); + .ToAtrStop(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetAtrStop(); + .ToAtrStop(); Assert.AreEqual(1, r1.Count); } @@ -123,14 +123,14 @@ public void Condense() double multiplier = 3; IReadOnlyList results = Quotes - .GetAtrStop(lookbackPeriods, multiplier) + .ToAtrStop(lookbackPeriods, multiplier) .Condense(); // assertions Assert.AreEqual(481, results.Count); AtrStopResult last = results[^1]; - Assert.AreEqual(246.3232m, last.AtrStop.Round(4)); + Assert.AreEqual(246.3232, last.AtrStop.Round(4)); Assert.AreEqual(last.AtrStop, last.BuyStop); Assert.AreEqual(null, last.SellStop); } @@ -142,14 +142,14 @@ public void Removed() double multiplier = 3; IReadOnlyList results = Quotes - .GetAtrStop(lookbackPeriods, multiplier) + .ToAtrStop(lookbackPeriods, multiplier) .RemoveWarmupPeriods(); // assertions Assert.AreEqual(481, results.Count); AtrStopResult last = results[^1]; - Assert.AreEqual(246.3232m, last.AtrStop.Round(4)); + Assert.AreEqual(246.3232, last.AtrStop.Round(4)); Assert.AreEqual(last.AtrStop, last.BuyStop); Assert.AreEqual(null, last.SellStop); } @@ -159,10 +159,10 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() - => Quotes.GetAtrStop(1)); + => Quotes.ToAtrStop(1)); // bad multiplier Assert.ThrowsException(() - => Quotes.GetAtrStop(7, 0)); + => Quotes.ToAtrStop(7, 0)); } } diff --git a/tests/indicators/a-d/AtrStop/AtrStop.StreamHub.Tests.cs b/tests/indicators/a-d/AtrStop/AtrStop.StreamHub.Tests.cs index 404a402b7..539fc18ec 100644 --- a/tests/indicators/a-d/AtrStop/AtrStop.StreamHub.Tests.cs +++ b/tests/indicators/a-d/AtrStop/AtrStop.StreamHub.Tests.cs @@ -6,8 +6,7 @@ public class AtrStop : StreamHubTestBase [TestMethod] public override void QuoteObserver() { - List quotesList = Quotes - .ToSortedList(); + List quotesList = Quotes.ToList(); int length = quotesList.Count; @@ -47,17 +46,17 @@ IReadOnlyList streamList } // late arrivals - provider.Add(quotesList[30]); // rebuilds complete series - provider.Add(quotesList[80]); // rebuilds from last reversal + provider.Insert(quotesList[30]); // rebuilds complete series + provider.Insert(quotesList[80]); // rebuilds from last reversal // delete provider.Remove(quotesList[400]); quotesList.RemoveAt(400); // time-series, for comparison - IEnumerable seriesList + IReadOnlyList seriesList = quotesList - .GetAtrStop(); + .ToAtrStop(); // assert, should equal series streamList.Should().HaveCount(length - 1); @@ -87,9 +86,9 @@ IReadOnlyList streamList = observer.Results; // time-series, for comparison - IEnumerable seriesList + IReadOnlyList seriesList = Quotes - .GetAtrStop(endType: EndType.HighLow); + .ToAtrStop(endType: EndType.HighLow); // assert, should equal series streamList.Should().HaveCount(Quotes.Count); diff --git a/tests/indicators/a-d/Awesome/Awesome.StaticSeries.Tests.cs b/tests/indicators/a-d/Awesome/Awesome.StaticSeries.Tests.cs index e08f291d9..92982f51e 100644 --- a/tests/indicators/a-d/Awesome/Awesome.StaticSeries.Tests.cs +++ b/tests/indicators/a-d/Awesome/Awesome.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Awesome : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetAwesome(); + .ToAwesome(); // proper quantities Assert.AreEqual(502, results.Count); @@ -36,7 +36,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetAwesome(); + .ToAwesome(); Assert.AreEqual(502, results.Count); Assert.AreEqual(469, results.Count(x => x.Oscillator != null)); @@ -46,8 +46,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetAwesome(); + .ToSma(2) + .ToAwesome(); Assert.AreEqual(502, results.Count); Assert.AreEqual(468, results.Count(x => x.Oscillator != null)); @@ -57,8 +57,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetAwesome() - .GetSma(10); + .ToAwesome() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(460, results.Count(x => x.Sma != null)); @@ -68,7 +68,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetAwesome(); + .ToAwesome(); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Oscillator is double.NaN)); @@ -78,12 +78,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetAwesome(); + .ToAwesome(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetAwesome(); + .ToAwesome(); Assert.AreEqual(1, r1.Count); } @@ -92,7 +92,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetAwesome() + .ToAwesome() .RemoveWarmupPeriods(); // assertions @@ -108,10 +108,10 @@ public void Exceptions() { // bad fast period Assert.ThrowsException(() => - Quotes.GetAwesome(0)); + Quotes.ToAwesome(0)); // bad slow period Assert.ThrowsException(() => - Quotes.GetAwesome(25, 25)); + Quotes.ToAwesome(25, 25)); } } diff --git a/tests/indicators/a-d/Beta/Beta.StaticSeries.Tests.cs b/tests/indicators/a-d/Beta/Beta.StaticSeries.Tests.cs index 49a660630..c8214d024 100644 --- a/tests/indicators/a-d/Beta/Beta.StaticSeries.Tests.cs +++ b/tests/indicators/a-d/Beta/Beta.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Beta : StaticSeriesTestBase public void All() { IReadOnlyList results = OtherQuotes - .GetBeta(Quotes, 20, BetaType.All); + .ToBeta(Quotes, 20, BetaType.All); // proper quantities Assert.AreEqual(502, results.Count); @@ -51,7 +51,7 @@ public void All() public override void Standard() { IReadOnlyList results = OtherQuotes - .GetBeta(Quotes, 20); + .ToBeta(Quotes, 20); // proper quantities Assert.AreEqual(502, results.Count); @@ -66,7 +66,7 @@ public override void Standard() public void Up() { IReadOnlyList results = OtherQuotes - .GetBeta(Quotes, 20, BetaType.Up); + .ToBeta(Quotes, 20, BetaType.Up); // proper quantities Assert.AreEqual(502, results.Count); @@ -81,7 +81,7 @@ public void Up() public void Down() { IReadOnlyList results = OtherQuotes - .GetBeta(Quotes, 20, BetaType.Down); + .ToBeta(Quotes, 20, BetaType.Down); // proper quantities Assert.AreEqual(502, results.Count); @@ -97,7 +97,7 @@ public void UseReusable() { IReadOnlyList results = OtherQuotes .Use(CandlePart.Close) - .GetBeta(Quotes.Use(CandlePart.Close), 20); + .ToBeta(Quotes.Use(CandlePart.Close), 20); Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.Beta != null)); @@ -107,8 +107,8 @@ public void UseReusable() public void Chainor() { IReadOnlyList results = OtherQuotes - .GetBeta(Quotes, 20) - .GetSma(10); + .ToBeta(Quotes, 20) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(473, results.Count(x => x.Sma != null)); @@ -118,8 +118,8 @@ public void Chainor() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetBeta(OtherQuotes.GetSma(2), 20); + .ToSma(2) + .ToBeta(OtherQuotes.ToSma(2), 20); Assert.AreEqual(502, results.Count); Assert.AreEqual(481, results.Count(x => x.Beta != null)); @@ -130,19 +130,19 @@ public void Chainee() public override void BadData() { IReadOnlyList r1 = BadQuotes - .GetBeta(BadQuotes, 15); + .ToBeta(BadQuotes, 15); Assert.AreEqual(502, r1.Count); Assert.AreEqual(0, r1.Count(x => x.Beta is double.NaN)); IReadOnlyList r2 = BadQuotes - .GetBeta(BadQuotes, 15, BetaType.Up); + .ToBeta(BadQuotes, 15, BetaType.Up); Assert.AreEqual(502, r2.Count); Assert.AreEqual(0, r2.Count(x => x.BetaUp is double.NaN)); IReadOnlyList r3 = BadQuotes - .GetBeta(BadQuotes, 15, BetaType.Down); + .ToBeta(BadQuotes, 15, BetaType.Down); Assert.AreEqual(502, r3.Count); Assert.AreEqual(0, r3.Count(x => x.BetaDown is double.NaN)); @@ -152,7 +152,7 @@ public override void BadData() public void BigData() { IReadOnlyList r = BigQuotes - .GetBeta(BigQuotes, 150, BetaType.All); + .ToBeta(BigQuotes, 150, BetaType.All); Assert.AreEqual(1246, r.Count); } @@ -173,7 +173,7 @@ public void BetaMsft() IReadOnlyList results = evalQuotes .Aggregate(PeriodSize.Month) - .GetBeta(mktQuotes.Aggregate(PeriodSize.Month), 60); + .ToBeta(mktQuotes.Aggregate(PeriodSize.Month), 60); Assert.AreEqual(0.91, results[385].Beta.Round(2)); } @@ -182,7 +182,7 @@ public void BetaMsft() public void Removed() { IReadOnlyList results = OtherQuotes - .GetBeta(Quotes, 20) + .ToBeta(Quotes, 20) .RemoveWarmupPeriods(); // assertions @@ -197,7 +197,7 @@ public void SameSame() { // Beta should be 1 if evaluating against self IReadOnlyList results = Quotes - .GetBeta(Quotes, 20); + .ToBeta(Quotes, 20); // proper quantities Assert.AreEqual(502, results.Count); @@ -212,12 +212,12 @@ public void SameSame() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetBeta(Noquotes, 5); + .ToBeta(Noquotes, 5); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetBeta(Onequote, 5); + .ToBeta(Onequote, 5); Assert.AreEqual(1, r1.Count); } @@ -227,32 +227,32 @@ public void NoMatch() { IReadOnlyList quoteA = [ - new(DateTime.Parse("1/1/2020", englishCulture), 0, 0, 0, 1234, 0), - new(DateTime.Parse("1/2/2020", englishCulture), 0, 0, 0, 1234, 0), - new(DateTime.Parse("1/3/2020", englishCulture), 0, 0, 0, 1234, 0), - new(DateTime.Parse("1/4/2020", englishCulture), 0, 0, 0, 1234, 0), - new(DateTime.Parse("1/5/2020", englishCulture), 0, 0, 0, 1234, 0), - new(DateTime.Parse("1/6/2020", englishCulture), 0, 0, 0, 1234, 0), - new(DateTime.Parse("1/7/2020", englishCulture), 0, 0, 0, 1234, 0), - new(DateTime.Parse("1/8/2020", englishCulture), 0, 0, 0, 1234, 0), - new(DateTime.Parse("1/9/2020", englishCulture), 0, 0, 0, 1234, 0) + new(DateTime.Parse("1/1/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/2/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/3/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/4/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/5/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/6/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/7/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/8/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/9/2020", invariantCulture), 0, 0, 0, 1234, 0) ]; IReadOnlyList quoteB = [ - new(DateTime.Parse("1/1/2020", englishCulture), 0, 0, 0, 1234, 0), - new(DateTime.Parse("1/2/2020", englishCulture), 0, 0, 0, 1234, 0), - new(DateTime.Parse("1/3/2020", englishCulture), 0, 0, 0, 1234, 0), - new(DateTime.Parse("2/4/2020", englishCulture), 0, 0, 0, 1234, 0), // abberrant - new(DateTime.Parse("1/5/2020", englishCulture), 0, 0, 0, 1234, 0), - new(DateTime.Parse("1/6/2020", englishCulture), 0, 0, 0, 1234, 0), - new(DateTime.Parse("1/7/2020", englishCulture), 0, 0, 0, 1234, 0), - new(DateTime.Parse("1/8/2020", englishCulture), 0, 0, 0, 1234, 0), - new(DateTime.Parse("1/9/2020", englishCulture), 0, 0, 0, 1234, 0) + new(DateTime.Parse("1/1/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/2/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/3/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("2/4/2020", invariantCulture), 0, 0, 0, 1234, 0), // abberrant + new(DateTime.Parse("1/5/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/6/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/7/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/8/2020", invariantCulture), 0, 0, 0, 1234, 0), + new(DateTime.Parse("1/9/2020", invariantCulture), 0, 0, 0, 1234, 0) ]; Assert.ThrowsException(() - => quoteA.GetBeta(quoteB, 3)); + => quoteA.ToBeta(quoteB, 3)); } [TestMethod] @@ -260,12 +260,12 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() - => Quotes.GetBeta(OtherQuotes, 0)); + => Quotes.ToBeta(OtherQuotes, 0)); // bad evaluation quotes IReadOnlyList eval = Data.GetCompare(300).ToList(); Assert.ThrowsException(() - => Quotes.GetBeta(eval, 30)); + => Quotes.ToBeta(eval, 30)); } } diff --git a/tests/indicators/a-d/BollingerBands/BollingerBands.StaticSeries.Tests.cs b/tests/indicators/a-d/BollingerBands/BollingerBands.StaticSeries.Tests.cs index dac11b87c..a0fa51e21 100644 --- a/tests/indicators/a-d/BollingerBands/BollingerBands.StaticSeries.Tests.cs +++ b/tests/indicators/a-d/BollingerBands/BollingerBands.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class BollingerBands : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = - Quotes.GetBollingerBands(); + Quotes.ToBollingerBands(); // proper quantities Assert.AreEqual(502, results.Count); @@ -41,7 +41,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetBollingerBands(); + .ToBollingerBands(); Assert.AreEqual(502, results.Count); Assert.AreEqual(483, results.Count(x => x.Sma != null)); @@ -51,8 +51,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetBollingerBands(); + .ToSma(2) + .ToBollingerBands(); Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.UpperBand != null)); @@ -62,8 +62,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetBollingerBands() - .GetSma(10); + .ToBollingerBands() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(474, results.Count(x => x.Sma != null)); @@ -73,7 +73,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetBollingerBands(15, 3); + .ToBollingerBands(15, 3); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.UpperBand is double.NaN)); @@ -83,12 +83,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetBollingerBands(); + .ToBollingerBands(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetBollingerBands(); + .ToBollingerBands(); Assert.AreEqual(1, r1.Count); } @@ -97,7 +97,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetBollingerBands() + .ToBollingerBands() .RemoveWarmupPeriods(); // assertions @@ -117,10 +117,10 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() => - Quotes.GetBollingerBands(1)); + Quotes.ToBollingerBands(1)); // bad standard deviation Assert.ThrowsException(() => - Quotes.GetBollingerBands(2, 0)); + Quotes.ToBollingerBands(2, 0)); } } diff --git a/tests/indicators/a-d/Bop/Bop.StaticSeries.Tests.cs b/tests/indicators/a-d/Bop/Bop.StaticSeries.Tests.cs index b9e1dd932..7169a2238 100644 --- a/tests/indicators/a-d/Bop/Bop.StaticSeries.Tests.cs +++ b/tests/indicators/a-d/Bop/Bop.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Bop : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetBop(); + .ToBop(); // proper quantities Assert.AreEqual(502, results.Count); @@ -34,8 +34,8 @@ public override void Standard() public void Chainor() { IReadOnlyList results = Quotes - .GetBop() - .GetSma(10); + .ToBop() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(480, results.Count(x => x.Sma != null)); @@ -44,8 +44,8 @@ public void Chainor() [TestMethod] public void NaN() { - IEnumerable r = Data.GetBtcUsdNan() - .GetBop(50); + IReadOnlyList r = Data.GetBtcUsdNan() + .ToBop(50); Assert.AreEqual(0, r.Count(x => x.Bop is double.NaN)); } @@ -54,7 +54,7 @@ public void NaN() public override void BadData() { IReadOnlyList r = BadQuotes - .GetBop(); + .ToBop(); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Bop is double.NaN)); @@ -64,11 +64,11 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetBop(); + .ToBop(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetBop(); + .ToBop(); Assert.AreEqual(1, r1.Count); } @@ -76,7 +76,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetBop() + .ToBop() .RemoveWarmupPeriods(); // assertions @@ -90,5 +90,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetBop(0)); + => Quotes.ToBop(0)); } diff --git a/tests/indicators/a-d/Cci/Cci.StaticSeries.Tests.cs b/tests/indicators/a-d/Cci/Cci.StaticSeries.Tests.cs index afcacf1fe..c4550ed6a 100644 --- a/tests/indicators/a-d/Cci/Cci.StaticSeries.Tests.cs +++ b/tests/indicators/a-d/Cci/Cci.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Cci : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetCci(); + .ToCci(); // proper quantities Assert.AreEqual(502, results.Count); @@ -22,8 +22,8 @@ public override void Standard() public void Chainor() { IReadOnlyList results = Quotes - .GetCci() - .GetSma(10); + .ToCci() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(474, results.Count(x => x.Sma != null)); @@ -33,7 +33,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetCci(15); + .ToCci(15); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Cci is double.NaN)); @@ -43,12 +43,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetCci(); + .ToCci(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetCci(); + .ToCci(); Assert.AreEqual(1, r1.Count); } @@ -57,7 +57,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetCci() + .ToCci() .RemoveWarmupPeriods(); // assertions @@ -71,5 +71,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetCci(0)); + => Quotes.ToCci(0)); } diff --git a/tests/indicators/a-d/ChaikinOsc/ChaikinOsc.StaticSeries.Tests.cs b/tests/indicators/a-d/ChaikinOsc/ChaikinOsc.StaticSeries.Tests.cs index c55f4df4a..613c34cb6 100644 --- a/tests/indicators/a-d/ChaikinOsc/ChaikinOsc.StaticSeries.Tests.cs +++ b/tests/indicators/a-d/ChaikinOsc/ChaikinOsc.StaticSeries.Tests.cs @@ -10,7 +10,7 @@ public override void Standard() int slowPeriods = 10; IReadOnlyList results = Quotes - .GetChaikinOsc(fastPeriods, slowPeriods); + .ToChaikinOsc(fastPeriods, slowPeriods); // proper quantities Assert.AreEqual(502, results.Count); @@ -28,8 +28,8 @@ public override void Standard() public void Chainor() { IReadOnlyList results = Quotes - .GetChaikinOsc() - .GetSma(10); + .ToChaikinOsc() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(484, results.Count(x => x.Sma != null)); @@ -39,7 +39,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetChaikinOsc(5, 15); + .ToChaikinOsc(5, 15); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Oscillator is double.NaN)); @@ -49,12 +49,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetChaikinOsc(); + .ToChaikinOsc(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetChaikinOsc(); + .ToChaikinOsc(); Assert.AreEqual(1, r1.Count); } @@ -66,7 +66,7 @@ public void Removed() int slowPeriods = 10; IReadOnlyList results = Quotes - .GetChaikinOsc(fastPeriods, slowPeriods) + .ToChaikinOsc(fastPeriods, slowPeriods) .RemoveWarmupPeriods(); // assertions @@ -84,10 +84,10 @@ public void Exceptions() { // bad fast lookback Assert.ThrowsException(() => - Quotes.GetChaikinOsc(0)); + Quotes.ToChaikinOsc(0)); // bad slow lookback Assert.ThrowsException(() => - Quotes.GetChaikinOsc(10, 5)); + Quotes.ToChaikinOsc(10, 5)); } } diff --git a/tests/indicators/a-d/Chandelier/Chandelier.StaticSeries.Tests.cs b/tests/indicators/a-d/Chandelier/Chandelier.StaticSeries.Tests.cs index 8c3de54ec..92b8663d0 100644 --- a/tests/indicators/a-d/Chandelier/Chandelier.StaticSeries.Tests.cs +++ b/tests/indicators/a-d/Chandelier/Chandelier.StaticSeries.Tests.cs @@ -9,7 +9,7 @@ public override void Standard() int lookbackPeriods = 22; IReadOnlyList longResult = - Quotes.GetChandelier(lookbackPeriods); + Quotes.ToChandelier(lookbackPeriods); // proper quantities Assert.AreEqual(502, longResult.Count); @@ -24,7 +24,7 @@ public override void Standard() // short IReadOnlyList shortResult = - Quotes.GetChandelier(lookbackPeriods, 3, ChandelierType.Short); + Quotes.ToChandelier(lookbackPeriods, 3, ChandelierType.Short); ChandelierResult c = shortResult[501]; Assert.AreEqual(246.4240, c.ChandelierExit.Round(4)); @@ -34,8 +34,8 @@ public override void Standard() public void Chainor() { IReadOnlyList results = Quotes - .GetChandelier() - .GetSma(10); + .ToChandelier() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(471, results.Count(x => x.Sma != null)); @@ -45,7 +45,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetChandelier(15, 2); + .ToChandelier(15, 2); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.ChandelierExit is double.NaN)); @@ -55,12 +55,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetChandelier(); + .ToChandelier(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetChandelier(); + .ToChandelier(); Assert.AreEqual(1, r1.Count); } @@ -69,7 +69,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetChandelier() + .ToChandelier() .RemoveWarmupPeriods(); // assertions @@ -84,14 +84,14 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() => - Quotes.GetChandelier(0)); + Quotes.ToChandelier(0)); // bad multiplier Assert.ThrowsException(() => - Quotes.GetChandelier(25, 0)); + Quotes.ToChandelier(25, 0)); // bad type Assert.ThrowsException(() => - Quotes.GetChandelier(25, 2, (ChandelierType)int.MaxValue)); + Quotes.ToChandelier(25, 2, (ChandelierType)int.MaxValue)); } } diff --git a/tests/indicators/a-d/Chop/Chop.StaticSeries.Tests.cs b/tests/indicators/a-d/Chop/Chop.StaticSeries.Tests.cs index 871e0f5d5..0563157d0 100644 --- a/tests/indicators/a-d/Chop/Chop.StaticSeries.Tests.cs +++ b/tests/indicators/a-d/Chop/Chop.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Chop : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetChop(); + .ToChop(); // proper quantities Assert.AreEqual(502, results.Count); @@ -31,8 +31,8 @@ public override void Standard() public void Chainor() { IReadOnlyList results = Quotes - .GetChop() - .GetSma(10); + .ToChop() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(479, results.Count(x => x.Sma != null)); @@ -43,7 +43,7 @@ public void SmallLookback() { int lookbackPeriods = 2; IReadOnlyList results = Quotes - .GetChop(lookbackPeriods); + .ToChop(lookbackPeriods); // proper quantities Assert.AreEqual(502, results.Count); @@ -54,7 +54,7 @@ public void SmallLookback() public override void BadData() { IReadOnlyList r = BadQuotes - .GetChop(20); + .ToChop(20); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Chop is double.NaN)); @@ -64,12 +64,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetChop(); + .ToChop(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetChop(); + .ToChop(); Assert.AreEqual(1, r1.Count); } @@ -78,7 +78,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetChop() + .ToChop() .RemoveWarmupPeriods(); // assertions @@ -92,5 +92,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetChop(1)); + => Quotes.ToChop(1)); } diff --git a/tests/indicators/a-d/Cmf/Cmf.StaticSeries.Tests.cs b/tests/indicators/a-d/Cmf/Cmf.StaticSeries.Tests.cs index e8f3b80b7..f6c5814c0 100644 --- a/tests/indicators/a-d/Cmf/Cmf.StaticSeries.Tests.cs +++ b/tests/indicators/a-d/Cmf/Cmf.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Cmf : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetCmf(); + .ToCmf(); // proper quantities Assert.AreEqual(502, results.Count); @@ -34,8 +34,8 @@ public override void Standard() public void Chainor() { IReadOnlyList results = Quotes - .GetCmf() - .GetSma(10); + .ToCmf() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(474, results.Count(x => x.Sma != null)); @@ -45,7 +45,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetCmf(15); + .ToCmf(15); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Cmf is double.NaN)); @@ -55,7 +55,7 @@ public override void BadData() public void BigData() { IReadOnlyList r = BigQuotes - .GetCmf(150); + .ToCmf(150); Assert.AreEqual(1246, r.Count); } @@ -64,12 +64,12 @@ public void BigData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetCmf(); + .ToCmf(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetCmf(); + .ToCmf(); Assert.AreEqual(1, r1.Count); } @@ -78,7 +78,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetCmf() + .ToCmf() .RemoveWarmupPeriods(); // assertions @@ -94,5 +94,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetCmf(0)); + => Quotes.ToCmf(0)); } diff --git a/tests/indicators/a-d/Cmo/Cmo.StaticSeries.Tests.cs b/tests/indicators/a-d/Cmo/Cmo.StaticSeries.Tests.cs index cbbef590a..866f2b762 100644 --- a/tests/indicators/a-d/Cmo/Cmo.StaticSeries.Tests.cs +++ b/tests/indicators/a-d/Cmo/Cmo.StaticSeries.Tests.cs @@ -3,11 +3,14 @@ namespace StaticSeries; [TestClass] public class Cmo : StaticSeriesTestBase { + // TODO: test for CMO isUp works as expected + // when there’s no price change + [TestMethod] public override void Standard() { IReadOnlyList results = Quotes - .GetCmo(14); + .ToCmo(14); // proper quantities Assert.AreEqual(502, results.Count); @@ -32,7 +35,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetCmo(14); + .ToCmo(14); Assert.AreEqual(502, results.Count); Assert.AreEqual(488, results.Count(x => x.Cmo != null)); @@ -42,8 +45,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetCmo(20); + .ToSma(2) + .ToCmo(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(481, results.Count(x => x.Cmo != null)); @@ -53,8 +56,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetCmo(20) - .GetSma(10); + .ToCmo(20) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(473, results.Count(x => x.Sma != null)); @@ -64,7 +67,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetCmo(35); + .ToCmo(35); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Cmo is double.NaN)); @@ -74,12 +77,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetCmo(5); + .ToCmo(5); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetCmo(5); + .ToCmo(5); Assert.AreEqual(1, r1.Count); } @@ -88,7 +91,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetCmo(14) + .ToCmo(14) .RemoveWarmupPeriods(); // assertions @@ -102,5 +105,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetCmo(0)); + => Quotes.ToCmo(0)); } diff --git a/tests/indicators/a-d/ConnorsRsi/ConnorsRsi.StaticSeries.Tests.cs b/tests/indicators/a-d/ConnorsRsi/ConnorsRsi.StaticSeries.Tests.cs index 6839702c4..3e1c6e07c 100644 --- a/tests/indicators/a-d/ConnorsRsi/ConnorsRsi.StaticSeries.Tests.cs +++ b/tests/indicators/a-d/ConnorsRsi/ConnorsRsi.StaticSeries.Tests.cs @@ -12,7 +12,7 @@ public override void Standard() int startPeriod = Math.Max(rsiPeriods, Math.Max(streakPeriods, rankPeriods)) + 2; IReadOnlyList results1 = Quotes - .GetConnorsRsi(rsiPeriods, streakPeriods, rankPeriods); + .ToConnorsRsi(rsiPeriods, streakPeriods, rankPeriods); // proper quantities Assert.AreEqual(502, results1.Count); @@ -26,7 +26,7 @@ public override void Standard() Assert.AreEqual(74.7662, r1.ConnorsRsi.Round(4)); // different parameters - IReadOnlyList results2 = Quotes.GetConnorsRsi(14, 20, 10).ToList(); + IReadOnlyList results2 = Quotes.ToConnorsRsi(14, 20, 10).ToList(); ConnorsRsiResult r2 = results2[501]; Assert.AreEqual(42.0773, r2.Rsi.Round(4)); Assert.AreEqual(52.7386, r2.RsiStreak.Round(4)); @@ -39,7 +39,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetConnorsRsi(); + .ToConnorsRsi(); Assert.AreEqual(502, results.Count); Assert.AreEqual(401, results.Count(x => x.ConnorsRsi != null)); @@ -49,8 +49,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetConnorsRsi(); + .ToSma(2) + .ToConnorsRsi(); Assert.AreEqual(502, results.Count); Assert.AreEqual(400, results.Count(x => x.ConnorsRsi != null)); @@ -60,8 +60,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetConnorsRsi() - .GetSma(10); + .ToConnorsRsi() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(392, results.Count(x => x.Sma != null)); @@ -71,7 +71,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetConnorsRsi(4, 3, 25); + .ToConnorsRsi(4, 3, 25); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Rsi is double.NaN)); @@ -81,12 +81,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetConnorsRsi(); + .ToConnorsRsi(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetConnorsRsi(); + .ToConnorsRsi(); Assert.AreEqual(1, r1.Count); } @@ -102,7 +102,7 @@ public void Removed() int removePeriods = Math.Max(rsiPeriods, Math.Max(streakPeriods, rankPeriods)) + 2; IReadOnlyList results = Quotes - .GetConnorsRsi(rsiPeriods, streakPeriods, rankPeriods) + .ToConnorsRsi(rsiPeriods, streakPeriods, rankPeriods) .RemoveWarmupPeriods(); // assertions @@ -120,14 +120,14 @@ public void Exceptions() { // bad RSI period Assert.ThrowsException(() => - Quotes.GetConnorsRsi(1)); + Quotes.ToConnorsRsi(1)); // bad Streak period Assert.ThrowsException(() => - Quotes.GetConnorsRsi(3, 1)); + Quotes.ToConnorsRsi(3, 1)); // bad Rank period Assert.ThrowsException(() => - Quotes.GetConnorsRsi(3, 2, 1)); + Quotes.ToConnorsRsi(3, 2, 1)); } } diff --git a/tests/indicators/a-d/Correlation/Correlation.StaticSeries.Tests.cs b/tests/indicators/a-d/Correlation/Correlation.StaticSeries.Tests.cs index 7c97e4d1a..5502f29db 100644 --- a/tests/indicators/a-d/Correlation/Correlation.StaticSeries.Tests.cs +++ b/tests/indicators/a-d/Correlation/Correlation.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Correlation : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetCorrelation(OtherQuotes, 20); + .ToCorrelation(OtherQuotes, 20); // proper quantities // should always be the same number of results as there is quotes @@ -37,7 +37,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetCorrelation(OtherQuotes.Use(CandlePart.Close), 20); + .ToCorrelation(OtherQuotes.Use(CandlePart.Close), 20); Assert.AreEqual(502, results.Count); Assert.AreEqual(483, results.Count(x => x.Correlation != null)); @@ -47,8 +47,8 @@ public void UseReusable() public void Chainor() { IReadOnlyList results = Quotes - .GetCorrelation(OtherQuotes, 20) - .GetSma(10); + .ToCorrelation(OtherQuotes, 20) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(474, results.Count(x => x.Sma != null)); @@ -58,8 +58,8 @@ public void Chainor() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetCorrelation(OtherQuotes.GetSma(2), 20); + .ToSma(2) + .ToCorrelation(OtherQuotes.ToSma(2), 20); Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.Correlation != null)); @@ -70,7 +70,7 @@ public void Chainee() public override void BadData() { IReadOnlyList r = BadQuotes - .GetCorrelation(BadQuotes, 15); + .ToCorrelation(BadQuotes, 15); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Correlation is double.NaN)); @@ -80,7 +80,7 @@ public override void BadData() public void BigData() { IReadOnlyList r = BigQuotes - .GetCorrelation(BigQuotes, 150); + .ToCorrelation(BigQuotes, 150); Assert.AreEqual(1246, r.Count); } @@ -89,12 +89,12 @@ public void BigData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetCorrelation(Noquotes, 10); + .ToCorrelation(Noquotes, 10); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetCorrelation(Onequote, 10); + .ToCorrelation(Onequote, 10); Assert.AreEqual(1, r1.Count); } @@ -103,7 +103,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetCorrelation(OtherQuotes, 20) + .ToCorrelation(OtherQuotes, 20) .RemoveWarmupPeriods(); // assertions @@ -119,15 +119,15 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() => - Quotes.GetCorrelation(OtherQuotes, 0)); + Quotes.ToCorrelation(OtherQuotes, 0)); // bad eval quotes - IEnumerable eval = Data.GetCompare(300); + IReadOnlyList eval = Data.GetCompare(300); Assert.ThrowsException(() => - Quotes.GetCorrelation(eval, 30)); + Quotes.ToCorrelation(eval, 30)); // mismatched quotes Assert.ThrowsException(() => - MismatchQuotes.GetCorrelation(OtherQuotes, 20)); + MismatchQuotes.ToCorrelation(OtherQuotes, 20)); } } diff --git a/tests/indicators/a-d/Dema/Dema.StaticSeries.Tests.cs b/tests/indicators/a-d/Dema/Dema.StaticSeries.Tests.cs index 683f8531f..b98a3b5f7 100644 --- a/tests/indicators/a-d/Dema/Dema.StaticSeries.Tests.cs +++ b/tests/indicators/a-d/Dema/Dema.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Dema : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetDema(20); + .ToDema(20); // proper quantities Assert.AreEqual(502, results.Count); @@ -32,7 +32,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetDema(20); + .ToDema(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(483, results.Count(x => x.Dema != null)); @@ -42,8 +42,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetDema(20); + .ToSma(2) + .ToDema(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.Dema != null)); @@ -53,8 +53,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetDema(20) - .GetSma(10); + .ToDema(20) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(474, results.Count(x => x.Sma != null)); @@ -64,7 +64,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetDema(15); + .ToDema(15); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Dema is double.NaN)); @@ -74,12 +74,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetDema(5); + .ToDema(5); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetDema(5); + .ToDema(5); Assert.AreEqual(1, r1.Count); } @@ -88,7 +88,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetDema(20) + .ToDema(20) .RemoveWarmupPeriods(); // assertions @@ -102,5 +102,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetDema(0)); + => Quotes.ToDema(0)); } diff --git a/tests/indicators/a-d/Doji/Doji.StaticSeries.Tests.cs b/tests/indicators/a-d/Doji/Doji.StaticSeries.Tests.cs index 0bbd6614a..6b0622535 100644 --- a/tests/indicators/a-d/Doji/Doji.StaticSeries.Tests.cs +++ b/tests/indicators/a-d/Doji/Doji.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Doji : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetDoji(); + .ToDoji(); // proper quantities Assert.AreEqual(502, results.Count); @@ -43,7 +43,7 @@ public override void Standard() public override void BadData() { IReadOnlyList r = BadQuotes - .GetDoji(); + .ToDoji(); Assert.AreEqual(502, r.Count); } @@ -52,12 +52,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetDoji(); + .ToDoji(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetDoji(); + .ToDoji(); Assert.AreEqual(1, r1.Count); } @@ -66,7 +66,7 @@ public override void NoQuotes() public void Condense() { IReadOnlyList results = Quotes - .GetDoji() + .ToDoji() .Condense(); Assert.AreEqual(112, results.Count); @@ -77,9 +77,9 @@ public void Exceptions() { // bad maximum change value Assert.ThrowsException(() => - Quotes.GetDoji(-0.00001)); + Quotes.ToDoji(-0.00001)); Assert.ThrowsException(() => - Quotes.GetDoji(0.50001)); + Quotes.ToDoji(0.50001)); } } diff --git a/tests/indicators/a-d/Donchian/Donchian.StaticSeries.Tests.cs b/tests/indicators/a-d/Donchian/Donchian.StaticSeries.Tests.cs index 362476c98..c16cee7b4 100644 --- a/tests/indicators/a-d/Donchian/Donchian.StaticSeries.Tests.cs +++ b/tests/indicators/a-d/Donchian/Donchian.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Donchian : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetDonchian(); + .ToDonchian(); // proper quantities Assert.AreEqual(502, results.Count); @@ -52,7 +52,7 @@ public override void Standard() public override void BadData() { IReadOnlyList r = BadQuotes - .GetDonchian(15); + .ToDonchian(15); Assert.AreEqual(502, r.Count); } @@ -61,12 +61,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetDonchian(); + .ToDonchian(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetDonchian(); + .ToDonchian(); Assert.AreEqual(1, r1.Count); } @@ -75,7 +75,7 @@ public override void NoQuotes() public void Condense() { IReadOnlyList results = Quotes - .GetDonchian() + .ToDonchian() .Condense(); // assertions @@ -92,7 +92,7 @@ public void Condense() public void Removed() { IReadOnlyList results = Quotes - .GetDonchian() + .ToDonchian() .RemoveWarmupPeriods(); // assertions @@ -109,5 +109,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetDonchian(0)); + => Quotes.ToDonchian(0)); } diff --git a/tests/indicators/a-d/Dpo/Dpo.StaticSeries.Tests.cs b/tests/indicators/a-d/Dpo/Dpo.StaticSeries.Tests.cs index f279d237b..08628d406 100644 --- a/tests/indicators/a-d/Dpo/Dpo.StaticSeries.Tests.cs +++ b/tests/indicators/a-d/Dpo/Dpo.StaticSeries.Tests.cs @@ -17,14 +17,14 @@ public override void Standard() for (int i = 0; i < csvData.Count; i++) { string[] csv = csvData[i].Split(","); - DateTime date = Convert.ToDateTime(csv[1], englishCulture); + DateTime date = Convert.ToDateTime(csv[1], invariantCulture); qot.Add(new Quote(date, 0, 0, 0, Close: csv[5].ToDecimal(), 0)); exp.Add(new(date, csv[7].ToDoubleNull(), csv[6].ToDoubleNull())); } // calculate actual data - IReadOnlyList act = qot.GetDpo(14); + IReadOnlyList act = qot.ToDpo(14); // assertions Assert.AreEqual(exp.Count, act.Count); @@ -46,7 +46,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetDpo(14); + .ToDpo(14); Assert.AreEqual(502, results.Count); Assert.AreEqual(489, results.Count(x => x.Dpo != null)); @@ -56,8 +56,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetDpo(14); + .ToSma(2) + .ToDpo(14); Assert.AreEqual(502, results.Count); Assert.AreEqual(488, results.Count(x => x.Dpo != null)); @@ -67,8 +67,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetDpo(14) - .GetSma(10); + .ToDpo(14) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(480, results.Count(x => x.Sma is not null and not double.NaN)); @@ -78,7 +78,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetDpo(5); + .ToDpo(5); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Dpo is double.NaN)); @@ -88,12 +88,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetDpo(5); + .ToDpo(5); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetDpo(5); + .ToDpo(5); Assert.AreEqual(1, r1.Count); } @@ -102,5 +102,5 @@ public override void NoQuotes() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetDpo(0)); + => Quotes.ToDpo(0)); } diff --git a/tests/indicators/a-d/Dynamic/Dynamic.StaticSeries.Tests.cs b/tests/indicators/a-d/Dynamic/Dynamic.StaticSeries.Tests.cs index 5fbd7b8b6..6f2a47247 100644 --- a/tests/indicators/a-d/Dynamic/Dynamic.StaticSeries.Tests.cs +++ b/tests/indicators/a-d/Dynamic/Dynamic.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class McGinleyDynamic : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetDynamic(14); + .ToDynamic(14); // assertions Assert.AreEqual(502, results.Count); @@ -32,7 +32,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetDynamic(20); + .ToDynamic(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(501, results.Count(x => x.Dynamic != null)); @@ -43,8 +43,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(10) - .GetDynamic(14); + .ToSma(10) + .ToDynamic(14); Assert.AreEqual(502, results.Count); Assert.AreEqual(492, results.Count(x => x.Dynamic != null)); @@ -54,8 +54,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetDynamic(14) - .GetSma(10); + .ToDynamic(14) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(492, results.Count(x => x.Sma != null)); @@ -65,7 +65,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetDynamic(15); + .ToDynamic(15); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Dynamic is double.NaN)); @@ -75,12 +75,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetDynamic(14); + .ToDynamic(14); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetDynamic(14); + .ToDynamic(14); Assert.AreEqual(1, r1.Count); } @@ -90,10 +90,10 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() - => Quotes.GetDynamic(0)); + => Quotes.ToDynamic(0)); // bad k-factor Assert.ThrowsException(() - => Quotes.GetDynamic(14, 0)); + => Quotes.ToDynamic(14, 0)); } } diff --git a/tests/indicators/e-k/ElderRay/ElderRay.StaticSeries.Tests.cs b/tests/indicators/e-k/ElderRay/ElderRay.StaticSeries.Tests.cs index 8bef0f2d7..f857c4df9 100644 --- a/tests/indicators/e-k/ElderRay/ElderRay.StaticSeries.Tests.cs +++ b/tests/indicators/e-k/ElderRay/ElderRay.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class ElderRay : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetElderRay(); + .ToElderRay(); // proper quantities Assert.AreEqual(502, results.Count); @@ -50,8 +50,8 @@ public override void Standard() public void Chainor() { IReadOnlyList results = Quotes - .GetElderRay() - .GetSma(10); + .ToElderRay() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(481, results.Count(x => x.Sma != null)); @@ -61,7 +61,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetElderRay(); + .ToElderRay(); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.BullPower is double.NaN)); @@ -71,12 +71,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetElderRay(); + .ToElderRay(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetElderRay(); + .ToElderRay(); Assert.AreEqual(1, r1.Count); } @@ -85,7 +85,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetElderRay() + .ToElderRay() .RemoveWarmupPeriods(); // assertions @@ -101,5 +101,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetElderRay(0)); + => Quotes.ToElderRay(0)); } diff --git a/tests/indicators/e-k/Ema/Ema.Increments.Tests.cs b/tests/indicators/e-k/Ema/Ema.Increments.Tests.cs index 8f698717f..b19ce4c69 100644 --- a/tests/indicators/e-k/Ema/Ema.Increments.Tests.cs +++ b/tests/indicators/e-k/Ema/Ema.Increments.Tests.cs @@ -3,37 +3,68 @@ namespace Increments; [TestClass] public class Ema : IncrementsTestBase { + private const int lookbackPeriods = 14; + + private static readonly IReadOnlyList reusables + = Quotes + .Cast() + .ToList(); + + private static readonly IReadOnlyList series + = Quotes.ToEma(lookbackPeriods); + [TestMethod] - public override void Standard() + public void FromReusableSplit() { - EmaList sut = new(14); + EmaList sut = new(lookbackPeriods); - foreach (Quote quote in Quotes) + foreach (IReusable item in reusables) { - sut.Add(quote); + sut.Add(item.Timestamp, item.Value); } - IReadOnlyList series - = Quotes.ToEma(14); + sut.Should().HaveCount(Quotes.Count); + sut.Should().BeEquivalentTo(series); + } + + [TestMethod] + public void FromReusableItem() + { + EmaList sut = new(lookbackPeriods); + + foreach (IReusable item in reusables) { sut.Add(item); } sut.Should().HaveCount(Quotes.Count); sut.Should().BeEquivalentTo(series); } [TestMethod] - public override void ValueBased() + public void FromReusableBatch() { - EmaArray sut = new(14); + EmaList sut = new(lookbackPeriods) { reusables }; - foreach (Quote quote in Quotes) - { - sut.Add(quote.Value); - } + sut.Should().HaveCount(Quotes.Count); + sut.Should().BeEquivalentTo(series); + } + + [TestMethod] + public override void FromQuote() + { + EmaList sut = new(lookbackPeriods); + + foreach (Quote q in Quotes) { sut.Add(q); } + + sut.Should().HaveCount(Quotes.Count); + sut.Should().BeEquivalentTo(series); + } - List series = Quotes - .ToEma(14) - .Select(x => x.Ema) - .ToList(); + [TestMethod] + public override void FromQuoteBatch() + { + EmaList sut = new(lookbackPeriods) { Quotes }; + + IReadOnlyList series + = Quotes.ToEma(lookbackPeriods); sut.Should().HaveCount(Quotes.Count); sut.Should().BeEquivalentTo(series); diff --git a/tests/indicators/e-k/Ema/Ema.StaticSeries.Tests.cs b/tests/indicators/e-k/Ema/Ema.StaticSeries.Tests.cs index 6136b75d2..39a670a7f 100644 --- a/tests/indicators/e-k/Ema/Ema.StaticSeries.Tests.cs +++ b/tests/indicators/e-k/Ema/Ema.StaticSeries.Tests.cs @@ -14,8 +14,7 @@ public void Increment() [TestMethod] public override void Standard() { - IReadOnlyList results = Quotes - .ToEma(20); + IReadOnlyList results = Quotes.ToEma(20); // proper quantities Assert.AreEqual(502, results.Count); @@ -73,7 +72,7 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) + .ToSma(2) .ToEma(20); Assert.AreEqual(502, results.Count); @@ -86,7 +85,7 @@ public void Chainor() { IReadOnlyList results = Quotes .ToEma(20) - .GetSma(10); + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(474, results.Count(x => x.Sma != null)); @@ -97,7 +96,7 @@ public void Chainor() public void ChaineeMore() { IReadOnlyList results = Quotes - .GetRsi() + .ToRsi() .ToEma(20); // assertions @@ -122,8 +121,7 @@ public void ChaineeMore() [TestMethod] public override void BadData() { - IReadOnlyList r = BadQuotes - .ToEma(15); + IReadOnlyList r = BadQuotes.ToEma(15); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Ema is double.NaN)); @@ -132,13 +130,11 @@ public override void BadData() [TestMethod] public override void NoQuotes() { - IReadOnlyList r0 = Noquotes - .ToEma(10); + IReadOnlyList r0 = Noquotes.ToEma(10); Assert.AreEqual(0, r0.Count); - IReadOnlyList r1 = Onequote - .ToEma(10); + IReadOnlyList r1 = Onequote.ToEma(10); Assert.AreEqual(1, r1.Count); } @@ -146,8 +142,7 @@ public override void NoQuotes() [TestMethod] public void Removed() { - IReadOnlyList results = Quotes - .ToEma(20) + IReadOnlyList results = Quotes.ToEma(20) .RemoveWarmupPeriods(); // assertions diff --git a/tests/indicators/e-k/Ema/Ema.StreamHub.Tests.cs b/tests/indicators/e-k/Ema/Ema.StreamHub.Tests.cs index 552b2f2e7..60354ebfb 100644 --- a/tests/indicators/e-k/Ema/Ema.StreamHub.Tests.cs +++ b/tests/indicators/e-k/Ema/Ema.StreamHub.Tests.cs @@ -6,8 +6,7 @@ public class EmaHub : StreamHubTestBase, ITestChainObserver, ITestChainProvider [TestMethod] public override void QuoteObserver() { - List quotesList = Quotes - .ToSortedList(); + List quotesList = Quotes.ToList(); int length = quotesList.Count; @@ -48,15 +47,14 @@ IReadOnlyList streamList } // late arrival - provider.Add(quotesList[80]); + provider.Insert(quotesList[80]); // delete provider.Remove(quotesList[400]); quotesList.RemoveAt(400); // time-series, for comparison - IReadOnlyList seriesList = quotesList - .ToEma(5); + IReadOnlyList seriesList = quotesList.ToEma(5); // assert, should equal series streamList.Should().HaveCount(length - 1); @@ -72,8 +70,7 @@ public void ChainObserver() int emaPeriods = 12; int smaPeriods = 8; - List quotesList = Quotes - .ToSortedList(); + List quotesList = Quotes.ToList(); int length = quotesList.Count; @@ -98,7 +95,7 @@ IReadOnlyList streamList // time-series, for comparison IReadOnlyList seriesList = quotesList - .GetSma(smaPeriods) + .ToSma(smaPeriods) .ToEma(emaPeriods); // assert, should equal series @@ -115,8 +112,7 @@ public void ChainProvider() int emaPeriods = 20; int smaPeriods = 10; - List quotesList = Quotes - .ToSortedList(); + List quotesList = Quotes.ToList(); int length = quotesList.Count; @@ -148,7 +144,7 @@ public void ChainProvider() } // late arrival - provider.Add(quotesList[80]); + provider.Insert(quotesList[80]); // delete provider.Remove(quotesList[400]); @@ -160,9 +156,8 @@ IReadOnlyList streamList // time-series, for comparison IReadOnlyList seriesList - = quotesList - .ToEma(emaPeriods) - .GetSma(smaPeriods); + = quotesList.ToEma(emaPeriods) + .ToSma(smaPeriods); // assert, should equal series streamList.Should().HaveCount(length - 1); diff --git a/tests/indicators/e-k/Epma/Epma.StaticSeries.Tests.cs b/tests/indicators/e-k/Epma/Epma.StaticSeries.Tests.cs index 98be869cd..6756a5159 100644 --- a/tests/indicators/e-k/Epma/Epma.StaticSeries.Tests.cs +++ b/tests/indicators/e-k/Epma/Epma.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Epma : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetEpma(20); + .ToEpma(20); // proper quantities Assert.AreEqual(502, results.Count); @@ -35,7 +35,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetEpma(20); + .ToEpma(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(483, results.Count(x => x.Epma != null)); @@ -45,8 +45,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetEpma(20); + .ToSma(2) + .ToEpma(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.Epma != null)); @@ -56,8 +56,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetEpma(20) - .GetSma(10); + .ToEpma(20) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(474, results.Count(x => x.Sma != null)); @@ -67,7 +67,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetEpma(15); + .ToEpma(15); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Epma is double.NaN)); @@ -77,12 +77,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetEpma(5); + .ToEpma(5); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetEpma(5); + .ToEpma(5); Assert.AreEqual(1, r1.Count); } @@ -91,7 +91,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetEpma(20) + .ToEpma(20) .RemoveWarmupPeriods(); // assertions @@ -105,5 +105,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetEpma(0)); + => Quotes.ToEpma(0)); } diff --git a/tests/indicators/e-k/Fcb/Fcb.StaticSeries.Tests.cs b/tests/indicators/e-k/Fcb/Fcb.StaticSeries.Tests.cs index 09ca48d44..e0742cacd 100644 --- a/tests/indicators/e-k/Fcb/Fcb.StaticSeries.Tests.cs +++ b/tests/indicators/e-k/Fcb/Fcb.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Fcb : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetFcb(); + .ToFcb(); // proper quantities Assert.AreEqual(502, results.Count); @@ -44,7 +44,7 @@ public override void Standard() public override void BadData() { IReadOnlyList r = BadQuotes - .GetFcb(); + .ToFcb(); Assert.AreEqual(502, r.Count); } @@ -53,12 +53,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetFcb(); + .ToFcb(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetFcb(); + .ToFcb(); Assert.AreEqual(1, r1.Count); } @@ -67,7 +67,7 @@ public override void NoQuotes() public void Condense() { IReadOnlyList results = Quotes - .GetFcb() + .ToFcb() .Condense(); // assertions @@ -82,7 +82,7 @@ public void Condense() public void Removed() { IReadOnlyList results = Quotes - .GetFcb() + .ToFcb() .RemoveWarmupPeriods(); // assertions @@ -97,5 +97,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetFcb(1)); + => Quotes.ToFcb(1)); } diff --git a/tests/indicators/e-k/FisherTransform/FisherTransform.StaticSeries.Tests.cs b/tests/indicators/e-k/FisherTransform/FisherTransform.StaticSeries.Tests.cs index 8ed431cb4..e826b2b5d 100644 --- a/tests/indicators/e-k/FisherTransform/FisherTransform.StaticSeries.Tests.cs +++ b/tests/indicators/e-k/FisherTransform/FisherTransform.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class FisherTransform : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetFisherTransform(); + .ToFisherTransform(); // proper quantities Assert.AreEqual(502, results.Count); @@ -50,7 +50,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetFisherTransform(); + .ToFisherTransform(); Assert.AreEqual(502, results.Count); Assert.AreEqual(501, results.Count(x => x.Fisher != 0)); @@ -60,8 +60,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetFisherTransform(); + .ToSma(2) + .ToFisherTransform(); Assert.AreEqual(502, results.Count); Assert.AreEqual(501, results.Count(x => x.Fisher != 0)); @@ -71,8 +71,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetFisherTransform() - .GetSma(10); + .ToFisherTransform() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(493, results.Count(x => x.Sma != null)); @@ -82,7 +82,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetFisherTransform(9); + .ToFisherTransform(9); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Fisher is double.NaN)); @@ -92,12 +92,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetFisherTransform(); + .ToFisherTransform(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetFisherTransform(); + .ToFisherTransform(); Assert.AreEqual(1, r1.Count); } @@ -106,5 +106,5 @@ public override void NoQuotes() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetFisherTransform(0)); + => Quotes.ToFisherTransform(0)); } diff --git a/tests/indicators/e-k/ForceIndex/ForceIndex.StaticSeries.Tests.cs b/tests/indicators/e-k/ForceIndex/ForceIndex.StaticSeries.Tests.cs index 48ef7077d..0e9d54eed 100644 --- a/tests/indicators/e-k/ForceIndex/ForceIndex.StaticSeries.Tests.cs +++ b/tests/indicators/e-k/ForceIndex/ForceIndex.StaticSeries.Tests.cs @@ -6,7 +6,7 @@ public class ForceIndex : StaticSeriesTestBase [TestMethod] public override void Standard() { - IReadOnlyList r = Quotes.GetForceIndex(13).ToList(); + IReadOnlyList r = Quotes.ToForceIndex(13).ToList(); // proper quantities Assert.AreEqual(502, r.Count); @@ -26,8 +26,8 @@ public override void Standard() public void Chainor() { IReadOnlyList results = Quotes - .GetForceIndex(13) - .GetSma(10); + .ToForceIndex(13) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(480, results.Count(x => x.Sma != null)); @@ -37,7 +37,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetForceIndex(); + .ToForceIndex(); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.ForceIndex is double.NaN)); @@ -47,12 +47,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetForceIndex(5); + .ToForceIndex(5); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetForceIndex(5); + .ToForceIndex(5); Assert.AreEqual(1, r1.Count); } @@ -61,7 +61,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetForceIndex(13) + .ToForceIndex(13) .RemoveWarmupPeriods(); // assertions @@ -75,5 +75,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetForceIndex(0)); + => Quotes.ToForceIndex(0)); } diff --git a/tests/indicators/e-k/Fractal/Fractal.StaticSeries.Tests.cs b/tests/indicators/e-k/Fractal/Fractal.StaticSeries.Tests.cs index 9f9dfc51c..6085d67b7 100644 --- a/tests/indicators/e-k/Fractal/Fractal.StaticSeries.Tests.cs +++ b/tests/indicators/e-k/Fractal/Fractal.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Fractal : StaticSeriesTestBase public override void Standard() // Span 2 { IReadOnlyList results = Quotes - .GetFractal(); + .ToFractal(); // proper quantities Assert.AreEqual(502, results.Count); @@ -44,7 +44,7 @@ public override void Standard() // Span 2 public void StandardSpan4() { IReadOnlyList results = Quotes - .GetFractal(4, 4); + .ToFractal(4, 4); // proper quantities Assert.AreEqual(502, results.Count); @@ -81,7 +81,7 @@ public void StandardSpan4() public override void BadData() { IReadOnlyList r = BadQuotes - .GetFractal(); + .ToFractal(); Assert.AreEqual(502, r.Count); } @@ -90,12 +90,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetFractal(); + .ToFractal(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetFractal(); + .ToFractal(); Assert.AreEqual(1, r1.Count); } @@ -104,7 +104,7 @@ public override void NoQuotes() public void Condense() { IReadOnlyList results = Quotes - .GetFractal() + .ToFractal() .Condense(); Assert.AreEqual(129, results.Count); @@ -114,5 +114,5 @@ public void Condense() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetFractal(1)); + => Quotes.ToFractal(1)); } diff --git a/tests/indicators/e-k/Gator/Gator.StaticSeries.Tests.cs b/tests/indicators/e-k/Gator/Gator.StaticSeries.Tests.cs index 7f433e3ab..3c448b971 100644 --- a/tests/indicators/e-k/Gator/Gator.StaticSeries.Tests.cs +++ b/tests/indicators/e-k/Gator/Gator.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Gator : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetGator(); + .ToGator(); // proper quantities Assert.AreEqual(502, results.Count); @@ -76,8 +76,8 @@ public override void Standard() public void FromAlligator() { IReadOnlyList results = Quotes - .GetAlligator() - .GetGator(); + .ToAlligator() + .ToGator(); // proper quantities Assert.AreEqual(502, results.Count); @@ -147,7 +147,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetGator(); + .ToGator(); Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.Upper != null)); @@ -157,8 +157,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetGator(); + .ToSma(2) + .ToGator(); Assert.AreEqual(502, results.Count); Assert.AreEqual(481, results.Count(x => x.Upper != null)); @@ -168,7 +168,7 @@ public void Chainee() public override void BadData() { IReadOnlyList r = BadQuotes - .GetGator(); + .ToGator(); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Upper is double.NaN)); @@ -178,12 +178,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetGator(); + .ToGator(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetGator(); + .ToGator(); Assert.AreEqual(1, r1.Count); } @@ -192,7 +192,7 @@ public override void NoQuotes() public void Condense() { IReadOnlyList results = Quotes - .GetGator() + .ToGator() .Condense(); // assertions @@ -209,7 +209,7 @@ public void Condense() public void Removed() { IReadOnlyList results = Quotes - .GetGator() + .ToGator() .RemoveWarmupPeriods(); // assertions diff --git a/tests/indicators/e-k/HeikinAshi/HeikinAshi.StaticSeries.Tests.cs b/tests/indicators/e-k/HeikinAshi/HeikinAshi.StaticSeries.Tests.cs index 26e3816dc..6d452026e 100644 --- a/tests/indicators/e-k/HeikinAshi/HeikinAshi.StaticSeries.Tests.cs +++ b/tests/indicators/e-k/HeikinAshi/HeikinAshi.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class HeikinAshi : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetHeikinAshi(); + .ToHeikinAshi(); // proper quantities Assert.AreEqual(502, results.Count); @@ -24,8 +24,8 @@ public override void Standard() [TestMethod] public void UseAsQuotes() { - IEnumerable haQuotes = Quotes.GetHeikinAshi(); - IEnumerable haSma = haQuotes.GetSma(5); + IReadOnlyList haQuotes = Quotes.ToHeikinAshi(); + IReadOnlyList haSma = haQuotes.ToSma(5); Assert.AreEqual(498, haSma.Count(x => x.Sma != null)); } @@ -33,7 +33,7 @@ public void UseAsQuotes() public override void BadData() { IReadOnlyList r = BadQuotes - .GetHeikinAshi(); + .ToHeikinAshi(); Assert.AreEqual(502, r.Count); } @@ -42,12 +42,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetHeikinAshi(); + .ToHeikinAshi(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetHeikinAshi(); + .ToHeikinAshi(); Assert.AreEqual(1, r1.Count); } diff --git a/tests/indicators/e-k/Hma/Hma.StaticSeries.Tests.cs b/tests/indicators/e-k/Hma/Hma.StaticSeries.Tests.cs index 9bceb5d16..a0bfafe45 100644 --- a/tests/indicators/e-k/Hma/Hma.StaticSeries.Tests.cs +++ b/tests/indicators/e-k/Hma/Hma.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Hma : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetHma(20); + .ToHma(20); // proper quantities Assert.AreEqual(502, results.Count); @@ -26,7 +26,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetHma(20); + .ToHma(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(480, results.Count(x => x.Hma != null)); @@ -36,8 +36,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetHma(19); + .ToSma(2) + .ToHma(19); Assert.AreEqual(502, results.Count); Assert.AreEqual(480, results.Count(x => x.Hma != null)); @@ -47,8 +47,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetHma(20) - .GetSma(10); + .ToHma(20) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(471, results.Count(x => x.Sma != null)); @@ -58,7 +58,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetHma(15); + .ToHma(15); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Hma is double.NaN)); @@ -68,12 +68,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetHma(5); + .ToHma(5); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetHma(5); + .ToHma(5); Assert.AreEqual(1, r1.Count); } @@ -82,7 +82,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetHma(20) + .ToHma(20) .RemoveWarmupPeriods(); // assertions @@ -96,5 +96,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetHma(1)); + => Quotes.ToHma(1)); } diff --git a/tests/indicators/e-k/HtTrendline/HtTrendline.StaticSeries.Tests.cs b/tests/indicators/e-k/HtTrendline/HtTrendline.StaticSeries.Tests.cs index 463b781f3..e15b2268c 100644 --- a/tests/indicators/e-k/HtTrendline/HtTrendline.StaticSeries.Tests.cs +++ b/tests/indicators/e-k/HtTrendline/HtTrendline.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class HtTrendline : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetHtTrendline(); + .ToHtTrendline(); // proper quantities // should always be the same number of results as there is quotes @@ -61,7 +61,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetHtTrendline(); + .ToHtTrendline(); Assert.AreEqual(502, results.Count); Assert.AreEqual(502, results.Count(x => x.Trendline != null)); @@ -71,8 +71,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetHtTrendline(); + .ToSma(2) + .ToHtTrendline(); Assert.AreEqual(502, results.Count); Assert.AreEqual(501, results.Count(x => x.Trendline != null)); @@ -82,8 +82,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetHtTrendline() - .GetSma(10); + .ToHtTrendline() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(493, results.Count(x => x.Sma != null)); @@ -93,7 +93,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetHtTrendline(); + .ToHtTrendline(); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Trendline is double.NaN)); @@ -103,7 +103,7 @@ public override void BadData() public void Removed() { IReadOnlyList results = Quotes - .GetHtTrendline() + .ToHtTrendline() .RemoveWarmupPeriods(); // assertions @@ -117,10 +117,10 @@ public void Removed() [TestMethod] public void PennyData() { - IEnumerable penny = Data.GetPenny(); + IReadOnlyList penny = Data.GetPenny(); IReadOnlyList r = penny - .GetHtTrendline(); + .ToHtTrendline(); Assert.AreEqual(533, r.Count); } @@ -129,12 +129,12 @@ public void PennyData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetHtTrendline(); + .ToHtTrendline(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetHtTrendline(); + .ToHtTrendline(); Assert.AreEqual(1, r1.Count); } diff --git a/tests/indicators/e-k/Hurst/Hurst.StaticSeries.Tests.cs b/tests/indicators/e-k/Hurst/Hurst.StaticSeries.Tests.cs index db69cc2b5..f596df5c5 100644 --- a/tests/indicators/e-k/Hurst/Hurst.StaticSeries.Tests.cs +++ b/tests/indicators/e-k/Hurst/Hurst.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Hurst : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = LongestQuotes - .GetHurst(LongestQuotes.Count - 1); + .ToHurst(LongestQuotes.Count - 1); // assertions @@ -25,7 +25,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetHurst(); + .ToHurst(); Assert.AreEqual(502, results.Count); Assert.AreEqual(402, results.Count(x => x.HurstExponent != null)); @@ -35,8 +35,8 @@ public void UseReusable() public void Chainor() { IReadOnlyList results = Quotes - .GetHurst() - .GetSma(10); + .ToHurst() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(393, results.Count(x => x.Sma != null)); @@ -46,8 +46,8 @@ public void Chainor() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(10) - .GetHurst(); + .ToSma(10) + .ToHurst(); Assert.AreEqual(502, results.Count); Assert.AreEqual(393, results.Count(x => x.HurstExponent != null)); @@ -57,7 +57,7 @@ public void Chainee() public override void BadData() { IReadOnlyList r = BadQuotes - .GetHurst(150); + .ToHurst(150); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.HurstExponent is double.NaN)); @@ -67,12 +67,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetHurst(); + .ToHurst(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetHurst(); + .ToHurst(); Assert.AreEqual(1, r1.Count); } @@ -80,7 +80,7 @@ public override void NoQuotes() [TestMethod] public void Removed() { - IReadOnlyList results = LongestQuotes.GetHurst(LongestQuotes.Count - 1) + IReadOnlyList results = LongestQuotes.ToHurst(LongestQuotes.Count - 1) .RemoveWarmupPeriods(); // assertions @@ -94,5 +94,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetHurst(19)); + => Quotes.ToHurst(19)); } diff --git a/tests/indicators/e-k/Ichimoku/Ichimoku.StaticSeries.Tests.cs b/tests/indicators/e-k/Ichimoku/Ichimoku.StaticSeries.Tests.cs index 15b047ec1..fd077faa0 100644 --- a/tests/indicators/e-k/Ichimoku/Ichimoku.StaticSeries.Tests.cs +++ b/tests/indicators/e-k/Ichimoku/Ichimoku.StaticSeries.Tests.cs @@ -11,7 +11,7 @@ public override void Standard() int senkouBPeriods = 52; IReadOnlyList results = Quotes - .GetIchimoku(tenkanPeriods, kijunPeriods, senkouBPeriods); + .ToIchimoku(tenkanPeriods, kijunPeriods, senkouBPeriods); // proper quantities Assert.AreEqual(502, results.Count); @@ -64,7 +64,7 @@ public void Extended() public override void BadData() { IReadOnlyList r = BadQuotes - .GetIchimoku(); + .ToIchimoku(); Assert.AreEqual(502, r.Count); } @@ -73,12 +73,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetIchimoku(); + .ToIchimoku(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetIchimoku(); + .ToIchimoku(); Assert.AreEqual(1, r1.Count); } @@ -87,7 +87,7 @@ public override void NoQuotes() public void Condense() { IReadOnlyList results = Quotes - .GetIchimoku() + .ToIchimoku() .Condense(); Assert.AreEqual(502, results.Count); @@ -98,15 +98,15 @@ public void Exceptions() { // bad signal period Assert.ThrowsException(() => - Quotes.GetIchimoku(0)); + Quotes.ToIchimoku(0)); // bad short span period Assert.ThrowsException(() => - Quotes.GetIchimoku(9, 0)); + Quotes.ToIchimoku(9, 0)); // bad long span period Assert.ThrowsException(() => - Quotes.GetIchimoku(9, 26, 26)); + Quotes.ToIchimoku(9, 26, 26)); // invalid offsets Assert.ThrowsException(() => diff --git a/tests/indicators/e-k/Kama/Kama.StaticSeries.Tests.cs b/tests/indicators/e-k/Kama/Kama.StaticSeries.Tests.cs index 1a03ba4fd..bbd19115c 100644 --- a/tests/indicators/e-k/Kama/Kama.StaticSeries.Tests.cs +++ b/tests/indicators/e-k/Kama/Kama.StaticSeries.Tests.cs @@ -11,7 +11,7 @@ public override void Standard() int slowPeriods = 30; IReadOnlyList results = Quotes - .GetKama(erPeriods, fastPeriods, slowPeriods); + .ToKama(erPeriods, fastPeriods, slowPeriods); // proper quantities Assert.AreEqual(502, results.Count); @@ -53,7 +53,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetKama(); + .ToKama(); Assert.AreEqual(502, results.Count); Assert.AreEqual(493, results.Count(x => x.Kama != null)); @@ -63,8 +63,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetKama(); + .ToSma(2) + .ToKama(); Assert.AreEqual(502, results.Count); Assert.AreEqual(492, results.Count(x => x.Kama != null)); @@ -74,8 +74,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetKama() - .GetSma(10); + .ToKama() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(484, results.Count(x => x.Sma != null)); @@ -85,7 +85,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetKama(); + .ToKama(); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Kama is double.NaN)); @@ -95,12 +95,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetKama(); + .ToKama(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetKama(); + .ToKama(); Assert.AreEqual(1, r1.Count); } @@ -113,7 +113,7 @@ public void Removed() int slowPeriods = 30; IReadOnlyList results = Quotes - .GetKama(erPeriods, fastPeriods, slowPeriods) + .ToKama(erPeriods, fastPeriods, slowPeriods) .RemoveWarmupPeriods(); // assertions @@ -129,14 +129,14 @@ public void Exceptions() { // bad ER period Assert.ThrowsException(() => - Quotes.GetKama(0)); + Quotes.ToKama(0)); // bad fast period Assert.ThrowsException(() => - Quotes.GetKama(10, 0)); + Quotes.ToKama(10, 0)); // bad slow period Assert.ThrowsException(() => - Quotes.GetKama(10, 5, 5)); + Quotes.ToKama(10, 5, 5)); } } diff --git a/tests/indicators/e-k/Keltner/Keltner.StaticSeries.Tests.cs b/tests/indicators/e-k/Keltner/Keltner.StaticSeries.Tests.cs index 2fd7625ce..ce3dc3c2f 100644 --- a/tests/indicators/e-k/Keltner/Keltner.StaticSeries.Tests.cs +++ b/tests/indicators/e-k/Keltner/Keltner.StaticSeries.Tests.cs @@ -11,7 +11,7 @@ public override void Standard() int atrPeriods = 10; IReadOnlyList results = Quotes - .GetKeltner(emaPeriods, multiplier, atrPeriods); + .ToKeltner(emaPeriods, multiplier, atrPeriods); // proper quantities Assert.AreEqual(502, results.Count); @@ -40,7 +40,7 @@ public override void Standard() public override void BadData() { IReadOnlyList r = BadQuotes - .GetKeltner(10, 3, 15); + .ToKeltner(10, 3, 15); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.UpperBand is double.NaN)); @@ -50,12 +50,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetKeltner(); + .ToKeltner(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetKeltner(); + .ToKeltner(); Assert.AreEqual(1, r1.Count); } @@ -68,7 +68,7 @@ public void Condense() int atrPeriods = 10; IReadOnlyList results = Quotes - .GetKeltner(emaPeriods, multiplier, atrPeriods) + .ToKeltner(emaPeriods, multiplier, atrPeriods) .Condense(); // assertions @@ -90,7 +90,7 @@ public void Removed() int n = Math.Max(emaPeriods, atrPeriods); IReadOnlyList results = Quotes - .GetKeltner(emaPeriods, multiplier, atrPeriods) + .ToKeltner(emaPeriods, multiplier, atrPeriods) .RemoveWarmupPeriods(); // assertions @@ -108,14 +108,14 @@ public void Exceptions() { // bad EMA period Assert.ThrowsException(() => - Quotes.GetKeltner(1)); + Quotes.ToKeltner(1)); // bad ATR period Assert.ThrowsException(() => - Quotes.GetKeltner(20, 2, 1)); + Quotes.ToKeltner(20, 2, 1)); // bad multiplier Assert.ThrowsException(() => - Quotes.GetKeltner(20, 0)); + Quotes.ToKeltner(20, 0)); } } diff --git a/tests/indicators/e-k/Kvo/Kvo.StaticSeries.Tests.cs b/tests/indicators/e-k/Kvo/Kvo.StaticSeries.Tests.cs index 2f9f24172..34dccb79f 100644 --- a/tests/indicators/e-k/Kvo/Kvo.StaticSeries.Tests.cs +++ b/tests/indicators/e-k/Kvo/Kvo.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Klinger : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = - Quotes.GetKvo(); + Quotes.ToKvo(); // proper quantities Assert.AreEqual(502, results.Count); @@ -48,8 +48,8 @@ public override void Standard() public void Chainor() { IReadOnlyList results = Quotes - .GetKvo() - .GetSma(10); + .ToKvo() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(437, results.Count(x => x.Sma != null)); @@ -59,7 +59,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetKvo(); + .ToKvo(); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Oscillator is double.NaN)); @@ -69,12 +69,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetKvo(); + .ToKvo(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetKvo(); + .ToKvo(); Assert.AreEqual(1, r1.Count); } @@ -83,7 +83,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetKvo() + .ToKvo() .RemoveWarmupPeriods(); // assertions @@ -99,14 +99,14 @@ public void Exceptions() { // bad fast period Assert.ThrowsException(() => - Quotes.GetKvo(2)); + Quotes.ToKvo(2)); // bad slow period Assert.ThrowsException(() => - Quotes.GetKvo(20, 20)); + Quotes.ToKvo(20, 20)); // bad signal period Assert.ThrowsException(() => - Quotes.GetKvo(34, 55, 0)); + Quotes.ToKvo(34, 55, 0)); } } diff --git a/tests/indicators/m-r/MaEnvelopes/MaEnvelopes.StaticSeries.Tests.cs b/tests/indicators/m-r/MaEnvelopes/MaEnvelopes.StaticSeries.Tests.cs index de453ae75..13aa3ddd7 100644 --- a/tests/indicators/m-r/MaEnvelopes/MaEnvelopes.StaticSeries.Tests.cs +++ b/tests/indicators/m-r/MaEnvelopes/MaEnvelopes.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class MaEnvelopes : StaticSeriesTestBase public override void Standard() // SMA { IReadOnlyList results = - Quotes.GetMaEnvelopes(20); + Quotes.ToMaEnvelopes(20); // proper quantities Assert.AreEqual(502, results.Count); @@ -34,7 +34,7 @@ public override void Standard() // SMA public void Alma() { IReadOnlyList results = - Quotes.GetMaEnvelopes(10, 2.5, MaType.ALMA); + Quotes.ToMaEnvelopes(10, 2.5, MaType.ALMA); // proper quantities Assert.AreEqual(502, results.Count); @@ -61,7 +61,7 @@ public void Alma() public void Dema() { IReadOnlyList results = - Quotes.GetMaEnvelopes(20, 2.5, MaType.DEMA); + Quotes.ToMaEnvelopes(20, 2.5, MaType.DEMA); // proper quantities Assert.AreEqual(502, results.Count); @@ -88,7 +88,7 @@ public void Dema() public void Epma() { IReadOnlyList results = - Quotes.GetMaEnvelopes(20, 2.5, MaType.EPMA); + Quotes.ToMaEnvelopes(20, 2.5, MaType.EPMA); // proper quantities Assert.AreEqual(502, results.Count); @@ -115,7 +115,7 @@ public void Epma() public void Ema() { IReadOnlyList results = - Quotes.GetMaEnvelopes(20, 2.5, MaType.EMA); + Quotes.ToMaEnvelopes(20, 2.5, MaType.EMA); // proper quantities Assert.AreEqual(502, results.Count); @@ -142,7 +142,7 @@ public void Ema() public void Hma() { IReadOnlyList results = - Quotes.GetMaEnvelopes(20, 2.5, MaType.HMA); + Quotes.ToMaEnvelopes(20, 2.5, MaType.HMA); // proper quantities Assert.AreEqual(502, results.Count); @@ -164,7 +164,7 @@ public void Hma() public void Smma() { IReadOnlyList results = - Quotes.GetMaEnvelopes(20, 2.5, MaType.SMMA); + Quotes.ToMaEnvelopes(20, 2.5, MaType.SMMA); // proper quantities Assert.AreEqual(502, results.Count); @@ -191,7 +191,7 @@ public void Smma() public void Tema() { IReadOnlyList results = - Quotes.GetMaEnvelopes(20, 2.5, MaType.TEMA); + Quotes.ToMaEnvelopes(20, 2.5, MaType.TEMA); // proper quantities Assert.AreEqual(502, results.Count); @@ -218,7 +218,7 @@ public void Tema() public void Wma() { IReadOnlyList results = - Quotes.GetMaEnvelopes(20, 2.5, MaType.WMA); + Quotes.ToMaEnvelopes(20, 2.5, MaType.WMA); // proper quantities Assert.AreEqual(502, results.Count); @@ -241,7 +241,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetMaEnvelopes(10); + .ToMaEnvelopes(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(493, results.Count(x => x.Centerline != null)); @@ -251,8 +251,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetMaEnvelopes(10); + .ToSma(2) + .ToMaEnvelopes(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(492, results.Count(x => x.Centerline != null)); @@ -262,42 +262,42 @@ public void Chainee() public override void BadData() { IReadOnlyList a = BadQuotes - .GetMaEnvelopes(5, 2.5, MaType.ALMA); + .ToMaEnvelopes(5, 2.5, MaType.ALMA); Assert.AreEqual(502, a.Count); IReadOnlyList d = BadQuotes - .GetMaEnvelopes(5, 2.5, MaType.DEMA); + .ToMaEnvelopes(5, 2.5, MaType.DEMA); Assert.AreEqual(502, d.Count); IReadOnlyList p = BadQuotes - .GetMaEnvelopes(5, 2.5, MaType.EPMA); + .ToMaEnvelopes(5, 2.5, MaType.EPMA); Assert.AreEqual(502, p.Count); IReadOnlyList e = BadQuotes - .GetMaEnvelopes(5, 2.5, MaType.EMA); + .ToMaEnvelopes(5, 2.5, MaType.EMA); Assert.AreEqual(502, e.Count); IReadOnlyList h = BadQuotes - .GetMaEnvelopes(5, 2.5, MaType.HMA); + .ToMaEnvelopes(5, 2.5, MaType.HMA); Assert.AreEqual(502, h.Count); IReadOnlyList s = BadQuotes - .GetMaEnvelopes(5); + .ToMaEnvelopes(5); Assert.AreEqual(502, s.Count); IReadOnlyList t = BadQuotes - .GetMaEnvelopes(5, 2.5, MaType.TEMA); + .ToMaEnvelopes(5, 2.5, MaType.TEMA); Assert.AreEqual(502, t.Count); IReadOnlyList w = BadQuotes - .GetMaEnvelopes(5, 2.5, MaType.WMA); + .ToMaEnvelopes(5, 2.5, MaType.WMA); Assert.AreEqual(502, w.Count); } @@ -306,12 +306,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetMaEnvelopes(10); + .ToMaEnvelopes(10); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetMaEnvelopes(10); + .ToMaEnvelopes(10); Assert.AreEqual(1, r1.Count); } @@ -320,7 +320,7 @@ public override void NoQuotes() public void Condense() { IReadOnlyList results = Quotes - .GetMaEnvelopes(20) + .ToMaEnvelopes(20) .Condense(); Assert.AreEqual(483, results.Count); @@ -331,11 +331,11 @@ public void Exceptions() { // bad offset period Assert.ThrowsException(() => - Quotes.GetMaEnvelopes(14, 0)); + Quotes.ToMaEnvelopes(14, 0)); // bad MA period Assert.ThrowsException(() => - Quotes.GetMaEnvelopes(14, 5, MaType.KAMA)); + Quotes.ToMaEnvelopes(14, 5, MaType.KAMA)); // note: insufficient quotes is tested elsewhere } diff --git a/tests/indicators/m-r/Macd/Macd.StaticSeries.Tests.cs b/tests/indicators/m-r/Macd/Macd.StaticSeries.Tests.cs index 1cdcbcfdf..63f78539e 100644 --- a/tests/indicators/m-r/Macd/Macd.StaticSeries.Tests.cs +++ b/tests/indicators/m-r/Macd/Macd.StaticSeries.Tests.cs @@ -11,7 +11,7 @@ public override void Standard() int signalPeriods = 9; IReadOnlyList results = - Quotes.GetMacd(fastPeriods, slowPeriods, signalPeriods); + Quotes.ToMacd(fastPeriods, slowPeriods, signalPeriods); // proper quantities Assert.AreEqual(502, results.Count); @@ -47,7 +47,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetMacd(); + .ToMacd(); Assert.AreEqual(502, results.Count); Assert.AreEqual(477, results.Count(x => x.Macd != null)); @@ -57,8 +57,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetMacd(); + .ToSma(2) + .ToMacd(); Assert.AreEqual(502, results.Count); Assert.AreEqual(476, results.Count(x => x.Macd != null)); @@ -68,8 +68,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetMacd() - .GetSma(10); + .ToMacd() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(468, results.Count(x => x.Sma != null)); @@ -79,7 +79,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetMacd(10, 20, 5); + .ToMacd(10, 20, 5); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Macd is double.NaN)); @@ -89,12 +89,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetMacd(); + .ToMacd(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetMacd(); + .ToMacd(); Assert.AreEqual(1, r1.Count); } @@ -107,7 +107,7 @@ public void Removed() int signalPeriods = 9; IReadOnlyList results = Quotes - .GetMacd(fastPeriods, slowPeriods, signalPeriods) + .ToMacd(fastPeriods, slowPeriods, signalPeriods) .RemoveWarmupPeriods(); // assertions @@ -124,14 +124,14 @@ public void Exceptions() { // bad fast period Assert.ThrowsException(() => - Quotes.GetMacd(0)); + Quotes.ToMacd(0)); // bad slow periods must be larger than faster period Assert.ThrowsException(() => - Quotes.GetMacd(12, 12)); + Quotes.ToMacd(12, 12)); // bad signal period Assert.ThrowsException(() => - Quotes.GetMacd(12, 26, -1)); + Quotes.ToMacd(12, 26, -1)); } } diff --git a/tests/indicators/m-r/Mama/Mama.StaticSeries.Tests.cs b/tests/indicators/m-r/Mama/Mama.StaticSeries.Tests.cs index 2866f9b7d..bdb748645 100644 --- a/tests/indicators/m-r/Mama/Mama.StaticSeries.Tests.cs +++ b/tests/indicators/m-r/Mama/Mama.StaticSeries.Tests.cs @@ -10,7 +10,7 @@ public override void Standard() double slowLimit = 0.05; IReadOnlyList results = Quotes - .GetMama(fastLimit, slowLimit); + .ToMama(fastLimit, slowLimit); // proper quantities Assert.AreEqual(502, results.Count); @@ -51,7 +51,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetMama(); + .ToMama(); Assert.AreEqual(502, results.Count); Assert.AreEqual(497, results.Count(x => x.Mama != null)); @@ -61,8 +61,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetMama(); + .ToSma(2) + .ToMama(); Assert.AreEqual(502, results.Count); Assert.AreEqual(496, results.Count(x => x.Mama != null)); @@ -72,8 +72,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetMama() - .GetSma(10); + .ToMama() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(488, results.Count(x => x.Sma != null)); @@ -83,7 +83,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetMama(); + .ToMama(); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Mama is double.NaN)); @@ -92,11 +92,11 @@ public override void BadData() [TestMethod] public override void NoQuotes() { - IReadOnlyList r0 = Noquotes.GetMama(); + IReadOnlyList r0 = Noquotes.ToMama(); Assert.AreEqual(0, r0.Count); - IReadOnlyList r1 = Onequote.GetMama(); + IReadOnlyList r1 = Onequote.ToMama(); Assert.AreEqual(1, r1.Count); } @@ -108,7 +108,7 @@ public void Removed() double slowLimit = 0.05; IReadOnlyList results = Quotes - .GetMama(fastLimit, slowLimit) + .ToMama(fastLimit, slowLimit) .RemoveWarmupPeriods(); // assertions @@ -124,14 +124,14 @@ public void Exceptions() { // bad fast period (same as slow period) Assert.ThrowsException(() => - Quotes.GetMama(0.5, 0.5)); + Quotes.ToMama(0.5, 0.5)); // bad fast period (cannot be 1 or more) Assert.ThrowsException(() => - Quotes.GetMama(1, 0.5)); + Quotes.ToMama(1, 0.5)); // bad slow period Assert.ThrowsException(() => - Quotes.GetMama(0.5, 0)); + Quotes.ToMama(0.5, 0)); } } diff --git a/tests/indicators/m-r/Marubozu/Marubozu.StaticSeries.Tests.cs b/tests/indicators/m-r/Marubozu/Marubozu.StaticSeries.Tests.cs index 98e858f49..66ba37050 100644 --- a/tests/indicators/m-r/Marubozu/Marubozu.StaticSeries.Tests.cs +++ b/tests/indicators/m-r/Marubozu/Marubozu.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Marubozu : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetMarubozu(); + .ToMarubozu(); // proper quantities Assert.AreEqual(502, results.Count); @@ -43,7 +43,7 @@ public override void Standard() public override void BadData() { IReadOnlyList r = BadQuotes - .GetMarubozu(); + .ToMarubozu(); Assert.AreEqual(502, r.Count); } @@ -52,12 +52,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetMarubozu(); + .ToMarubozu(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetMarubozu(); + .ToMarubozu(); Assert.AreEqual(1, r1.Count); } @@ -66,7 +66,7 @@ public override void NoQuotes() public void Condense() { IReadOnlyList results = Quotes - .GetMarubozu() + .ToMarubozu() .Condense(); Assert.AreEqual(6, results.Count); @@ -77,9 +77,9 @@ public void Exceptions() { // bad minimum body percent values Assert.ThrowsException(() => - Quotes.GetMarubozu(79.9)); + Quotes.ToMarubozu(79.9)); Assert.ThrowsException(() => - Quotes.GetMarubozu(100.1)); + Quotes.ToMarubozu(100.1)); } } diff --git a/tests/indicators/m-r/Mfi/Mfi.StaticSeries.Tests.cs b/tests/indicators/m-r/Mfi/Mfi.StaticSeries.Tests.cs index 34fec7d95..bd5be8d19 100644 --- a/tests/indicators/m-r/Mfi/Mfi.StaticSeries.Tests.cs +++ b/tests/indicators/m-r/Mfi/Mfi.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Mfi : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetMfi(); + .ToMfi(); // proper quantities Assert.AreEqual(502, results.Count); @@ -25,8 +25,8 @@ public override void Standard() public void Chainor() { IReadOnlyList results = Quotes - .GetMfi() - .GetSma(10); + .ToMfi() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(479, results.Count(x => x.Sma != null)); @@ -38,7 +38,7 @@ public void SmallLookback() int lookbackPeriods = 4; IReadOnlyList results = Quotes - .GetMfi(lookbackPeriods); + .ToMfi(lookbackPeriods); // proper quantities Assert.AreEqual(502, results.Count); @@ -56,7 +56,7 @@ public void SmallLookback() public override void BadData() { IReadOnlyList r = BadQuotes - .GetMfi(15); + .ToMfi(15); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Mfi is double.NaN)); @@ -66,12 +66,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetMfi(); + .ToMfi(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetMfi(); + .ToMfi(); Assert.AreEqual(1, r1.Count); } @@ -82,7 +82,7 @@ public void Removed() int lookbackPeriods = 14; IReadOnlyList results = Quotes - .GetMfi(lookbackPeriods) + .ToMfi(lookbackPeriods) .RemoveWarmupPeriods(); // assertions @@ -96,5 +96,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetMfi(1)); + => Quotes.ToMfi(1)); } diff --git a/tests/indicators/m-r/Obv/Obv.StaticSeries.Tests.cs b/tests/indicators/m-r/Obv/Obv.StaticSeries.Tests.cs index b919ed5af..a0127f678 100644 --- a/tests/indicators/m-r/Obv/Obv.StaticSeries.Tests.cs +++ b/tests/indicators/m-r/Obv/Obv.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Obv : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetObv(); + .ToObv(); // proper quantities Assert.AreEqual(502, results.Count); @@ -24,8 +24,8 @@ public override void Standard() public void Chainor() { IReadOnlyList results = Quotes - .GetObv() - .GetSma(10); + .ToObv() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(493, results.Count(x => x.Sma != null)); @@ -35,7 +35,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetObv(); + .ToObv(); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => double.IsNaN(x.Obv))); @@ -45,7 +45,7 @@ public override void BadData() public void BigData() { IReadOnlyList r = BigQuotes - .GetObv(); + .ToObv(); Assert.AreEqual(1246, r.Count); } @@ -54,12 +54,12 @@ public void BigData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetObv(); + .ToObv(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetObv(); + .ToObv(); Assert.AreEqual(1, r1.Count); } diff --git a/tests/indicators/m-r/ParabolicSar/ParabolicSar.StaticSeries.Tests.cs b/tests/indicators/m-r/ParabolicSar/ParabolicSar.StaticSeries.Tests.cs index b5ed0025d..e8eea6afa 100644 --- a/tests/indicators/m-r/ParabolicSar/ParabolicSar.StaticSeries.Tests.cs +++ b/tests/indicators/m-r/ParabolicSar/ParabolicSar.StaticSeries.Tests.cs @@ -9,8 +9,8 @@ public override void Standard() double acclerationStep = 0.02; double maxAccelerationFactor = 0.2; - IReadOnlyList results = - Quotes.GetParabolicSar(acclerationStep, maxAccelerationFactor) + List results = + Quotes.ToParabolicSar(acclerationStep, maxAccelerationFactor) .ToList(); // proper quantities @@ -42,7 +42,7 @@ public void Extended() double maxAccelerationFactor = 0.2; double initialStep = 0.01; - IReadOnlyList results = + List results = Quotes.GetParabolicSar( acclerationStep, maxAccelerationFactor, initialStep) .ToList(); @@ -77,8 +77,8 @@ public void Extended() public void Chainor() { IReadOnlyList results = Quotes - .GetParabolicSar() - .GetSma(10); + .ToParabolicSar() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(479, results.Count(x => x.Sma != null)); @@ -90,13 +90,14 @@ public void InsufficientQuotes() double acclerationStep = 0.02; double maxAccelerationFactor = 0.2; - IEnumerable insufficientQuotes = Data.GetDefault() + List insufficientQuotes = Data.GetDefault() .OrderBy(x => x.Timestamp) - .Take(10); + .Take(10) + .ToList(); IReadOnlyList results = - insufficientQuotes.GetParabolicSar(acclerationStep, maxAccelerationFactor) - .ToList(); + insufficientQuotes + .ToParabolicSar(acclerationStep, maxAccelerationFactor); // assertions @@ -119,12 +120,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetParabolicSar(); + .ToParabolicSar(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetParabolicSar(); + .ToParabolicSar(); Assert.AreEqual(1, r1.Count); } @@ -136,7 +137,7 @@ public void Removed() double maxAccelerationFactor = 0.2; IReadOnlyList results = Quotes - .GetParabolicSar(acclerationStep, maxAccelerationFactor) + .ToParabolicSar(acclerationStep, maxAccelerationFactor) .RemoveWarmupPeriods(); // assertions @@ -152,15 +153,15 @@ public void Exceptions() { // bad acceleration step Assert.ThrowsException(() => - Quotes.GetParabolicSar(0, 1)); + Quotes.ToParabolicSar(0, 1)); // insufficient acceleration step Assert.ThrowsException(() => - Quotes.GetParabolicSar(0.02, 0)); + Quotes.ToParabolicSar(0.02, 0)); // step larger than factor Assert.ThrowsException(() => - Quotes.GetParabolicSar(6, 2)); + Quotes.ToParabolicSar(6, 2)); // insufficient initial factor Assert.ThrowsException(() => diff --git a/tests/indicators/m-r/PivotPoints/PivotPoints.StaticSeries.Tests.cs b/tests/indicators/m-r/PivotPoints/PivotPoints.StaticSeries.Tests.cs index 7277120fa..56ad3e87d 100644 --- a/tests/indicators/m-r/PivotPoints/PivotPoints.StaticSeries.Tests.cs +++ b/tests/indicators/m-r/PivotPoints/PivotPoints.StaticSeries.Tests.cs @@ -1,7 +1,7 @@ namespace StaticSeries; [TestClass] -public class PivotPoints : StaticSeriesTestBase +public class PivotPointz : StaticSeriesTestBase { [TestMethod] public override void Standard() @@ -10,7 +10,7 @@ public override void Standard() PivotPointType pointType = PivotPointType.Standard; IReadOnlyList results = Quotes - .GetPivotPoints(periodSize, pointType); + .ToPivotPoints(periodSize, pointType); // proper quantities Assert.AreEqual(502, results.Count); @@ -90,8 +90,9 @@ public void Camarilla() PeriodSize periodSize = PeriodSize.Week; PivotPointType pointType = PivotPointType.Camarilla; - IEnumerable h = Data.GetDefault(38); - IReadOnlyList results = h.GetPivotPoints(periodSize, pointType); + IReadOnlyList h = Data.GetDefault(38); + IReadOnlyList results + = h.ToPivotPoints(periodSize, pointType); // proper quantities Assert.AreEqual(38, results.Count); @@ -161,7 +162,7 @@ public void Demark() PivotPointType pointType = PivotPointType.Demark; IReadOnlyList results = Quotes - .GetPivotPoints(periodSize, pointType); + .ToPivotPoints(periodSize, pointType); // proper quantities Assert.AreEqual(502, results.Count); @@ -235,7 +236,7 @@ public void Demark() Assert.AreEqual(null, r6.S4); // special Demark case: test close = open - WindowPoint d1 = Indicator.GetPivotPointDemark(125, 200, 100, 125); + WindowPoint d1 = PivotPoints.GetPivotPointDemark(125, 200, 100, 125); Assert.AreEqual(550m / 4, d1.PP); } @@ -245,8 +246,9 @@ public void Fibonacci() PeriodSize periodSize = PeriodSize.OneHour; PivotPointType pointType = PivotPointType.Fibonacci; - IEnumerable h = Data.GetIntraday(300); - IReadOnlyList results = h.GetPivotPoints(periodSize, pointType); + IReadOnlyList h = Data.GetIntraday(300); + IReadOnlyList results + = h.ToPivotPoints(periodSize, pointType); // proper quantities Assert.AreEqual(300, results.Count); @@ -316,8 +318,9 @@ public void Woodie() PeriodSize periodSize = PeriodSize.Day; PivotPointType pointType = PivotPointType.Woodie; - IEnumerable h = Data.GetIntraday(); - IReadOnlyList results = h.GetPivotPoints(periodSize, pointType); + IReadOnlyList h = Data.GetIntraday(); + IReadOnlyList results + = h.ToPivotPoints(periodSize, pointType); // proper quantities Assert.AreEqual(1564, results.Count); @@ -376,7 +379,7 @@ public void Woodie() public override void BadData() { IReadOnlyList r = BadQuotes - .GetPivotPoints(PeriodSize.Week); + .ToPivotPoints(PeriodSize.Week); Assert.AreEqual(502, r.Count); } @@ -385,12 +388,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetPivotPoints(PeriodSize.Week); + .ToPivotPoints(PeriodSize.Week); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetPivotPoints(PeriodSize.Week); + .ToPivotPoints(PeriodSize.Week); Assert.AreEqual(1, r1.Count); } @@ -402,7 +405,7 @@ public void Removed() PivotPointType pointType = PivotPointType.Standard; IReadOnlyList results = Quotes - .GetPivotPoints(periodSize, pointType) + .ToPivotPoints(periodSize, pointType) .RemoveWarmupPeriods(); // assertions @@ -426,11 +429,11 @@ public void Exceptions() // bad pointtype size Assert.ThrowsException(() => Quotes - .GetPivotPoints(PeriodSize.Week, (PivotPointType)999)); + .ToPivotPoints(PeriodSize.Week, (PivotPointType)999)); // bad window size Assert.ThrowsException(() => Quotes - .GetPivotPoints(PeriodSize.ThreeMinutes)); + .ToPivotPoints(PeriodSize.ThreeMinutes)); } } diff --git a/tests/indicators/m-r/Pivots/Pivots.StaticSeries.Tests.cs b/tests/indicators/m-r/Pivots/Pivots.StaticSeries.Tests.cs index c3cb6298f..ef3c9385f 100644 --- a/tests/indicators/m-r/Pivots/Pivots.StaticSeries.Tests.cs +++ b/tests/indicators/m-r/Pivots/Pivots.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Pivots : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetPivots(4, 4); + .ToPivots(4, 4); // proper quantities Assert.AreEqual(502, results.Count); @@ -88,7 +88,7 @@ public override void Standard() public override void BadData() { IReadOnlyList r = BadQuotes - .GetPivots(); + .ToPivots(); Assert.AreEqual(502, r.Count); } @@ -97,12 +97,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetPivots(); + .ToPivots(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetPivots(); + .ToPivots(); Assert.AreEqual(1, r1.Count); } @@ -111,7 +111,7 @@ public override void NoQuotes() public void Condense() { IReadOnlyList results = Quotes - .GetPivots(4, 4) + .ToPivots(4, 4) .Condense(); Assert.AreEqual(67, results.Count); @@ -122,14 +122,14 @@ public void Exceptions() { // bad left span Assert.ThrowsException(() => - Quotes.GetPivots(1)); + Quotes.ToPivots(1)); // bad right span Assert.ThrowsException(() => - Quotes.GetPivots(2, 1)); + Quotes.ToPivots(2, 1)); // bad lookback window Assert.ThrowsException(() => - Quotes.GetPivots(20, 10, 20, EndType.Close)); + Quotes.ToPivots(20, 10, 20, EndType.Close)); } } diff --git a/tests/indicators/m-r/Pmo/Pmo.StaticSeries.Tests.cs b/tests/indicators/m-r/Pmo/Pmo.StaticSeries.Tests.cs index 5b30416d7..97f15b910 100644 --- a/tests/indicators/m-r/Pmo/Pmo.StaticSeries.Tests.cs +++ b/tests/indicators/m-r/Pmo/Pmo.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Pmo : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetPmo(); + .ToPmo(); // proper quantities Assert.AreEqual(502, results.Count); @@ -29,7 +29,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetPmo(); + .ToPmo(); Assert.AreEqual(502, results.Count); Assert.AreEqual(448, results.Count(x => x.Pmo != null)); @@ -39,8 +39,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetPmo(); + .ToSma(2) + .ToPmo(); Assert.AreEqual(502, results.Count); Assert.AreEqual(447, results.Count(x => x.Pmo != null)); @@ -50,8 +50,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetPmo() - .GetSma(10); + .ToPmo() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(439, results.Count(x => x.Sma != null)); @@ -61,7 +61,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetPmo(25, 15, 5); + .ToPmo(25, 15, 5); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Pmo is double.NaN)); @@ -71,12 +71,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetPmo(); + .ToPmo(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetPmo(); + .ToPmo(); Assert.AreEqual(1, r1.Count); } @@ -85,7 +85,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetPmo() + .ToPmo() .RemoveWarmupPeriods(); // assertions @@ -101,14 +101,14 @@ public void Exceptions() { // bad time period Assert.ThrowsException(() => - Quotes.GetPmo(1)); + Quotes.ToPmo(1)); // bad smoothing period Assert.ThrowsException(() => - Quotes.GetPmo(5, 0)); + Quotes.ToPmo(5, 0)); // bad signal period Assert.ThrowsException(() => - Quotes.GetPmo(5, 5, 0)); + Quotes.ToPmo(5, 5, 0)); } } diff --git a/tests/indicators/m-r/Prs/Prs.StaticSeries.Tests.cs b/tests/indicators/m-r/Prs/Prs.StaticSeries.Tests.cs index 3e15b23f6..debcc99da 100644 --- a/tests/indicators/m-r/Prs/Prs.StaticSeries.Tests.cs +++ b/tests/indicators/m-r/Prs/Prs.StaticSeries.Tests.cs @@ -9,7 +9,7 @@ public override void Standard() int lookbackPeriods = 30; IReadOnlyList results = OtherQuotes - .GetPrs(Quotes, lookbackPeriods); + .ToPrs(Quotes, lookbackPeriods); // proper quantities Assert.AreEqual(502, results.Count); @@ -34,7 +34,7 @@ public void UseReusable() { IReadOnlyList results = OtherQuotes .Use(CandlePart.Close) - .GetPrs(Quotes.Use(CandlePart.Close), 20); + .ToPrs(Quotes.Use(CandlePart.Close), 20); Assert.AreEqual(502, results.Count); Assert.AreEqual(502, results.Count(x => x.Prs != null)); @@ -44,8 +44,8 @@ public void UseReusable() public void Chainor() { IReadOnlyList results = OtherQuotes - .GetPrs(Quotes, 20) - .GetSma(10); + .ToPrs(Quotes, 20) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(493, results.Count(x => x.Sma != null)); @@ -55,8 +55,8 @@ public void Chainor() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetPrs(OtherQuotes.GetSma(2), 20); + .ToSma(2) + .ToPrs(OtherQuotes.ToSma(2), 20); Assert.AreEqual(502, results.Count); Assert.AreEqual(501, results.Count(x => x.Prs != null)); @@ -67,7 +67,7 @@ public void Chainee() public override void BadData() { IReadOnlyList r = BadQuotes - .GetPrs(BadQuotes, 15); + .ToPrs(BadQuotes, 15); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Prs is double.NaN)); @@ -77,12 +77,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetPrs(Noquotes); + .ToPrs(Noquotes); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetPrs(Onequote); + .ToPrs(Onequote); Assert.AreEqual(1, r1.Count); } @@ -92,18 +92,18 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() => - OtherQuotes.GetPrs(Quotes, 0)); + OtherQuotes.ToPrs(Quotes, 0)); // insufficient quotes Assert.ThrowsException(() => - Data.GetCompare(13).GetPrs(Quotes, 14)); + Data.GetCompare(13).ToPrs(Quotes, 14)); // insufficient eval quotes Assert.ThrowsException(() => - Data.GetCompare(300).GetPrs(Quotes, 14)); + Data.GetCompare(300).ToPrs(Quotes, 14)); // mismatch quotes Assert.ThrowsException(() => - OtherQuotes.GetPrs(MismatchQuotes, 14)); + OtherQuotes.ToPrs(MismatchQuotes, 14)); } } diff --git a/tests/indicators/m-r/Pvo/Pvo.StaticSeries.Tests.cs b/tests/indicators/m-r/Pvo/Pvo.StaticSeries.Tests.cs index a871a561e..6b5e8123d 100644 --- a/tests/indicators/m-r/Pvo/Pvo.StaticSeries.Tests.cs +++ b/tests/indicators/m-r/Pvo/Pvo.StaticSeries.Tests.cs @@ -11,7 +11,7 @@ public override void Standard() int signalPeriods = 9; IReadOnlyList results = - Quotes.GetPvo(fastPeriods, slowPeriods, signalPeriods); + Quotes.ToPvo(fastPeriods, slowPeriods, signalPeriods); // proper quantities Assert.AreEqual(502, results.Count); @@ -50,8 +50,8 @@ public override void Standard() public void Chainor() { IReadOnlyList results = Quotes - .GetPvo() - .GetSma(10); + .ToPvo() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(468, results.Count(x => x.Sma != null)); @@ -61,7 +61,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetPvo(10, 20, 5); + .ToPvo(10, 20, 5); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Pvo is double.NaN)); @@ -71,12 +71,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetPvo(); + .ToPvo(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetPvo(); + .ToPvo(); Assert.AreEqual(1, r1.Count); } @@ -89,7 +89,7 @@ public void Removed() int signalPeriods = 9; IReadOnlyList results = Quotes - .GetPvo(fastPeriods, slowPeriods, signalPeriods) + .ToPvo(fastPeriods, slowPeriods, signalPeriods) .RemoveWarmupPeriods(); // assertions @@ -106,14 +106,14 @@ public void Exceptions() { // bad fast period Assert.ThrowsException(() => - Quotes.GetPvo(0)); + Quotes.ToPvo(0)); // bad slow periods must be larger than faster period Assert.ThrowsException(() => - Quotes.GetPvo(12, 12)); + Quotes.ToPvo(12, 12)); // bad signal period Assert.ThrowsException(() => - Quotes.GetPvo(12, 26, -1)); + Quotes.ToPvo(12, 26, -1)); } } diff --git a/tests/indicators/m-r/Renko/Renko.StaticSeries.Tests.cs b/tests/indicators/m-r/Renko/Renko.StaticSeries.Tests.cs index a0d4c9f52..1a2ed70ac 100644 --- a/tests/indicators/m-r/Renko/Renko.StaticSeries.Tests.cs +++ b/tests/indicators/m-r/Renko/Renko.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Renko : StaticSeriesTestBase public override void Standard() // close { IReadOnlyList results = Quotes - .GetRenko(2.5m); + .ToRenko(2.5m); // assertions @@ -45,7 +45,7 @@ public override void Standard() // close public void StandardHighLow() { IReadOnlyList results = Quotes - .GetRenko(2.5m, EndType.HighLow); + .ToRenko(2.5m, EndType.HighLow); // assertions @@ -107,8 +107,8 @@ public void Atr() [TestMethod] public void UseAsQuotes() { - IEnumerable renkoQuotes = Quotes.GetRenko(2.5m); - IEnumerable renkoSma = renkoQuotes.GetSma(5); + IReadOnlyList renkoQuotes = Quotes.ToRenko(2.5m); + IReadOnlyList renkoSma = renkoQuotes.ToSma(5); Assert.AreEqual(108, renkoSma.Count(x => x.Sma != null)); } @@ -116,7 +116,7 @@ public void UseAsQuotes() public override void BadData() { IReadOnlyList r = BadQuotes - .GetRenko(100m); + .ToRenko(100m); Assert.AreNotEqual(0, r.Count); } @@ -125,7 +125,7 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetRenko(0.01m); + .ToRenko(0.01m); Assert.AreEqual(0, r0.Count); } @@ -135,10 +135,10 @@ public void Exceptions() { // bad arguments Assert.ThrowsException(() - => Quotes.GetRenko(0)); + => Quotes.ToRenko(0)); // bad end type Assert.ThrowsException(() - => Quotes.GetRenko(2, (EndType)int.MaxValue)); + => Quotes.ToRenko(2, (EndType)int.MaxValue)); } } diff --git a/tests/indicators/m-r/Renko/Renko.StreamHub.Tests.cs b/tests/indicators/m-r/Renko/Renko.StreamHub.Tests.cs index 287cc469e..9e859a39d 100644 --- a/tests/indicators/m-r/Renko/Renko.StreamHub.Tests.cs +++ b/tests/indicators/m-r/Renko/Renko.StreamHub.Tests.cs @@ -7,10 +7,9 @@ public class RenkoHub : StreamHubTestBase, ITestChainProvider public override void QuoteObserver() { decimal brickSize = 2.5m; - EndType endType = EndType.Close; + EndType endType = EndType.HighLow; - List quotesList = Quotes - .ToSortedList(); + List quotesList = Quotes.ToList(); int length = quotesList.Count; @@ -51,7 +50,7 @@ IReadOnlyList streamList } // late arrival - provider.Add(quotesList[80]); + provider.Insert(quotesList[80]); // delete provider.Remove(quotesList[400]); @@ -59,11 +58,11 @@ IReadOnlyList streamList // time-series, for comparison IReadOnlyList seriesList = quotesList - .GetRenko(brickSize, endType); + .ToRenko(brickSize, endType); // assert, should equal series - streamList.Should().HaveCount(112); streamList.Should().BeEquivalentTo(seriesList); + streamList.Should().HaveCount(159); observer.Unsubscribe(); provider.EndTransmission(); @@ -74,10 +73,9 @@ public void ChainProvider() { decimal brickSize = 2.5m; EndType endType = EndType.Close; - int smaPeriods = 8; + int smaPeriods = 50; - List quotesList = Quotes - .ToSortedList(); + List quotesList = Quotes.ToList(); int length = quotesList.Count; @@ -105,12 +103,12 @@ IReadOnlyList streamList // time-series, for comparison IReadOnlyList seriesList = quotesList - .GetRenko(brickSize, endType) - .GetSma(smaPeriods); + .ToRenko(brickSize, endType) + .ToSma(smaPeriods); // assert, should equal series - streamList.Should().HaveCount(112); streamList.Should().BeEquivalentTo(seriesList); + streamList.Should().HaveCount(112); observer.Unsubscribe(); provider.EndTransmission(); @@ -122,4 +120,28 @@ public override void CustomToString() RenkoHub hub = new(new QuoteHub(), 2.5m, EndType.Close); hub.ToString().Should().Be("RENKO(2.5,CLOSE)"); } + + [TestMethod] + public void SettingsInheritance() + { + // setup quote hub (1st level) + QuoteHub quoteHub = new(); + + // setup renko hub (2nd level) + RenkoHub renkoHub = quoteHub + .ToRenko(brickSize: 2.5m, endType: EndType.Close); + + // setup child hub (3rd level) + SmaHub childHub = renkoHub + .ToSma(lookbackPeriods: 5); + + // note: dispite `quoteHub` being parentless, + // it has default properties; it should not + // inherit its own empty provider settings + + // assert + quoteHub.Properties.Settings.Should().Be(0b00000000, "is has default settings, not inherited"); + renkoHub.Properties.Settings.Should().Be(0b00000010, "it has custom Renko properties"); + childHub.Properties.Settings.Should().Be(0b00000010, "it inherits Renko properties"); + } } diff --git a/tests/indicators/m-r/Roc/Roc.StaticSeries.Tests.cs b/tests/indicators/m-r/Roc/Roc.StaticSeries.Tests.cs index dd8179ad8..1042bca94 100644 --- a/tests/indicators/m-r/Roc/Roc.StaticSeries.Tests.cs +++ b/tests/indicators/m-r/Roc/Roc.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Roc : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetRoc(20); + .ToRoc(20); // proper quantities Assert.AreEqual(502, results.Count); @@ -33,7 +33,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetRoc(20); + .ToRoc(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.Roc != null)); @@ -43,8 +43,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetRoc(20); + .ToSma(2) + .ToRoc(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(481, results.Count(x => x.Roc != null)); @@ -54,8 +54,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetRoc(20) - .GetSma(10); + .ToRoc(20) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(473, results.Count(x => x.Sma != null)); @@ -65,7 +65,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetRoc(35); + .ToRoc(35); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Roc is double.NaN)); @@ -75,12 +75,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetRoc(5); + .ToRoc(5); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetRoc(5); + .ToRoc(5); Assert.AreEqual(1, r1.Count); } @@ -89,7 +89,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetRoc(20) + .ToRoc(20) .RemoveWarmupPeriods(); // assertions @@ -103,5 +103,5 @@ public void Removed() public void Exceptions() => // bad lookback period Assert.ThrowsException(() => - Quotes.GetRoc(0)); + Quotes.ToRoc(0)); } diff --git a/tests/indicators/m-r/RocWb/RocWb.StaticSeries.Tests.cs b/tests/indicators/m-r/RocWb/RocWb.StaticSeries.Tests.cs index d4838b8bd..65bd3dd2e 100644 --- a/tests/indicators/m-r/RocWb/RocWb.StaticSeries.Tests.cs +++ b/tests/indicators/m-r/RocWb/RocWb.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class RocWb : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetRocWb(20, 3, 20); + .ToRocWb(20, 3, 20); // proper quantities Assert.AreEqual(502, results.Count); @@ -71,7 +71,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetRocWb(20, 3, 20); + .ToRocWb(20, 3, 20); Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.Roc != null)); @@ -81,8 +81,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetRocWb(20, 3, 20); + .ToSma(2) + .ToRocWb(20, 3, 20); Assert.AreEqual(502, results.Count); Assert.AreEqual(481, results.Count(x => x.Roc != null)); @@ -92,8 +92,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetRocWb(20, 3, 20) - .GetSma(10); + .ToRocWb(20, 3, 20) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(473, results.Count(x => x.Sma != null)); @@ -103,7 +103,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetRocWb(35, 3, 35); + .ToRocWb(35, 3, 35); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Roc is double.NaN)); @@ -113,12 +113,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetRocWb(5, 3, 2); + .ToRocWb(5, 3, 2); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetRocWb(5, 3, 2); + .ToRocWb(5, 3, 2); Assert.AreEqual(1, r1.Count); } @@ -127,7 +127,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetRocWb(20, 3, 20) + .ToRocWb(20, 3, 20) .RemoveWarmupPeriods(); // assertions @@ -145,14 +145,14 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() => - Quotes.GetRocWb(0, 3, 12)); + Quotes.ToRocWb(0, 3, 12)); // bad EMA period Assert.ThrowsException(() => - Quotes.GetRocWb(14, 0, 14)); + Quotes.ToRocWb(14, 0, 14)); // bad STDDEV period Assert.ThrowsException(() => - Quotes.GetRocWb(15, 3, 16)); + Quotes.ToRocWb(15, 3, 16)); } } diff --git a/tests/indicators/m-r/RollingPivots/RollingPivots.StaticSeries.Tests.cs b/tests/indicators/m-r/RollingPivots/RollingPivots.StaticSeries.Tests.cs index b5735dda2..7874751ca 100644 --- a/tests/indicators/m-r/RollingPivots/RollingPivots.StaticSeries.Tests.cs +++ b/tests/indicators/m-r/RollingPivots/RollingPivots.StaticSeries.Tests.cs @@ -11,7 +11,7 @@ public override void Standard() PivotPointType pointType = PivotPointType.Standard; IReadOnlyList results = - Quotes.GetRollingPivots(windowPeriods, offsetPeriods, pointType); + Quotes.ToRollingPivots(windowPeriods, offsetPeriods, pointType); // proper quantities Assert.AreEqual(502, results.Count); @@ -81,10 +81,10 @@ public void Camarilla() int offsetPeriods = 0; PivotPointType pointType = PivotPointType.Camarilla; - IEnumerable h = Data.GetDefault(38); + IReadOnlyList h = Data.GetDefault(38); IReadOnlyList results = h - .GetRollingPivots(windowPeriods, offsetPeriods, pointType); + .ToRollingPivots(windowPeriods, offsetPeriods, pointType); // proper quantities Assert.AreEqual(38, results.Count); @@ -155,7 +155,7 @@ public void Demark() PivotPointType pointType = PivotPointType.Demark; IReadOnlyList results = Quotes - .GetRollingPivots(windowPeriods, offsetPeriods, pointType); + .ToRollingPivots(windowPeriods, offsetPeriods, pointType); // proper quantities Assert.AreEqual(502, results.Count); @@ -236,10 +236,10 @@ public void Fibonacci() int offsetPeriods = 15; PivotPointType pointType = PivotPointType.Fibonacci; - IEnumerable h = Data.GetIntraday(300); + IReadOnlyList h = Data.GetIntraday(300); IReadOnlyList results = - h.GetRollingPivots(windowPeriods, offsetPeriods, pointType); + h.ToRollingPivots(windowPeriods, offsetPeriods, pointType); // proper quantities Assert.AreEqual(300, results.Count); @@ -310,10 +310,10 @@ public void Woodie() int offsetPeriods = 16; PivotPointType pointType = PivotPointType.Woodie; - IEnumerable h = Data.GetIntraday(); + IReadOnlyList h = Data.GetIntraday(); IReadOnlyList results = h - .GetRollingPivots(windowPeriods, offsetPeriods, pointType); + .ToRollingPivots(windowPeriods, offsetPeriods, pointType); // proper quantities Assert.AreEqual(1564, results.Count); @@ -372,7 +372,7 @@ public void Woodie() public override void BadData() { IReadOnlyList r = BadQuotes - .GetRollingPivots(5, 5); + .ToRollingPivots(5, 5); Assert.AreEqual(502, r.Count); } @@ -381,12 +381,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetRollingPivots(5, 2); + .ToRollingPivots(5, 2); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetRollingPivots(5, 2); + .ToRollingPivots(5, 2); Assert.AreEqual(1, r1.Count); } @@ -399,7 +399,7 @@ public void Removed() PivotPointType pointType = PivotPointType.Standard; IReadOnlyList results = Quotes - .GetRollingPivots(windowPeriods, offsetPeriods, pointType) + .ToRollingPivots(windowPeriods, offsetPeriods, pointType) .RemoveWarmupPeriods(); // assertions @@ -422,10 +422,10 @@ public void Exceptions() { // bad window period Assert.ThrowsException(() => - Quotes.GetRollingPivots(0, 10)); + Quotes.ToRollingPivots(0, 10)); // bad offset period Assert.ThrowsException(() => - Quotes.GetRollingPivots(10, -1)); + Quotes.ToRollingPivots(10, -1)); } } diff --git a/tests/indicators/m-r/Rsi/Rsi.StaticSeries.Tests.cs b/tests/indicators/m-r/Rsi/Rsi.StaticSeries.Tests.cs index 416bf9f88..f70a73693 100644 --- a/tests/indicators/m-r/Rsi/Rsi.StaticSeries.Tests.cs +++ b/tests/indicators/m-r/Rsi/Rsi.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Rsi : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetRsi(); + .ToRsi(); // proper quantities Assert.AreEqual(502, results.Count); @@ -32,7 +32,7 @@ public void SmallLookback() { int lookbackPeriods = 1; IReadOnlyList results = Quotes - .GetRsi(lookbackPeriods); + .ToRsi(lookbackPeriods); // proper quantities Assert.AreEqual(502, results.Count); @@ -49,10 +49,10 @@ public void SmallLookback() [TestMethod] public void CryptoData() { - IEnumerable btc = Data.GetBitcoin(); + IReadOnlyList btc = Data.GetBitcoin(); IReadOnlyList r = btc - .GetRsi(1); + .ToRsi(1); Assert.AreEqual(1246, r.Count); } @@ -62,7 +62,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetRsi(); + .ToRsi(); Assert.AreEqual(502, results.Count); Assert.AreEqual(488, results.Count(x => x.Rsi != null)); @@ -72,8 +72,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetRsi(); + .ToSma(2) + .ToRsi(); Assert.AreEqual(502, results.Count); Assert.AreEqual(487, results.Count(x => x.Rsi != null)); @@ -83,8 +83,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetRsi() - .GetSma(10); + .ToRsi() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(479, results.Count(x => x.Sma != null)); @@ -93,8 +93,8 @@ public void Chainor() [TestMethod] public void NaN() { - IEnumerable r = Data.GetBtcUsdNan() - .GetRsi(); + IReadOnlyList r = Data.GetBtcUsdNan() + .ToRsi(); Assert.AreEqual(0, r.Count(x => x.Rsi is double.NaN)); } @@ -103,7 +103,7 @@ public void NaN() public override void BadData() { IReadOnlyList r = BadQuotes - .GetRsi(20); + .ToRsi(20); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Rsi is double.NaN)); @@ -113,12 +113,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetRsi(); + .ToRsi(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetRsi(); + .ToRsi(); Assert.AreEqual(1, r1.Count); } @@ -127,11 +127,11 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetRsi() + .ToRsi() .RemoveWarmupPeriods(); // assertions - Assert.AreEqual(502 - 10 * 14, results.Count); + Assert.AreEqual(502 - (10 * 14), results.Count); RsiResult last = results[^1]; Assert.AreEqual(42.0773, last.Rsi.Round(4)); @@ -141,5 +141,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetRsi(0)); + => Quotes.ToRsi(0)); } diff --git a/tests/indicators/s-z/Slope/Slope.StaticSeries.Tests.cs b/tests/indicators/s-z/Slope/Slope.StaticSeries.Tests.cs index 60e32f9ab..9fc6af0f4 100644 --- a/tests/indicators/s-z/Slope/Slope.StaticSeries.Tests.cs +++ b/tests/indicators/s-z/Slope/Slope.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Slope : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetSlope(20); + .ToSlope(20); // proper quantities Assert.AreEqual(502, results.Count); @@ -43,7 +43,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetSlope(20); + .ToSlope(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(483, results.Count(x => x.Slope != null)); @@ -53,8 +53,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetSlope(20); + .ToSma(2) + .ToSlope(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.Slope != null)); @@ -64,8 +64,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetSlope(20) - .GetSma(10); + .ToSlope(20) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(474, results.Count(x => x.Sma != null)); @@ -75,7 +75,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetSlope(15); + .ToSlope(15); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Slope is double.NaN)); @@ -85,7 +85,7 @@ public override void BadData() public void BigData() { IReadOnlyList r = BigQuotes - .GetSlope(250); + .ToSlope(250); Assert.AreEqual(1246, r.Count); } @@ -94,12 +94,12 @@ public void BigData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetSlope(5); + .ToSlope(5); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetSlope(5); + .ToSlope(5); Assert.AreEqual(1, r1.Count); } @@ -108,7 +108,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetSlope(20) + .ToSlope(20) .RemoveWarmupPeriods(); // assertions @@ -126,5 +126,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetSlope(1)); + => Quotes.ToSlope(1)); } diff --git a/tests/indicators/s-z/Sma/Sma.StaticSeries.Tests.cs b/tests/indicators/s-z/Sma/Sma.StaticSeries.Tests.cs index f811a88c6..d66fe61ff 100644 --- a/tests/indicators/s-z/Sma/Sma.StaticSeries.Tests.cs +++ b/tests/indicators/s-z/Sma/Sma.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Sma : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetSma(20); + .ToSma(20); // proper quantities Assert.AreEqual(502, results.Count); @@ -27,7 +27,7 @@ public void CandlePartOpen() { IReadOnlyList results = Quotes .Use(CandlePart.Open) - .GetSma(20); + .ToSma(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(483, results.Count(x => x.Sma != null)); @@ -46,7 +46,7 @@ public void CandlePartVolume() { IReadOnlyList results = Quotes .Use(CandlePart.Volume) - .GetSma(20); + .ToSma(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(483, results.Count(x => x.Sma != null)); @@ -59,7 +59,7 @@ public void CandlePartVolume() Assert.AreEqual(157958070.8, r290.Sma); SmaResult r501 = results[501]; - Assert.AreEqual(DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", englishCulture), r501.Timestamp); + Assert.AreEqual(DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", invariantCulture), r501.Timestamp); Assert.AreEqual(163695200, r501.Sma); } @@ -67,7 +67,7 @@ public void CandlePartVolume() public void Chainor() { IReadOnlyList results = Quotes - .GetSma(10) + .ToSma(10) .ToEma(10); Assert.AreEqual(502, results.Count); @@ -78,7 +78,7 @@ public void Chainor() public void NaN() { IReadOnlyList r = Data.GetBtcUsdNan() - .GetSma(50); + .ToSma(50); Assert.AreEqual(0, r.Count(x => x.Sma is double.NaN)); } @@ -87,7 +87,7 @@ public void NaN() public override void BadData() { IReadOnlyList r = BadQuotes - .GetSma(15); + .ToSma(15); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Sma is double.NaN)); @@ -97,12 +97,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetSma(5); + .ToSma(5); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetSma(5); + .ToSma(5); Assert.AreEqual(1, r1.Count); } @@ -111,7 +111,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetSma(20) + .ToSma(20) .RemoveWarmupPeriods(); // assertions @@ -145,5 +145,5 @@ public void Equality() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetSma(0)); + => Quotes.ToSma(0)); } diff --git a/tests/indicators/s-z/Sma/Sma.StreamHub.Tests.cs b/tests/indicators/s-z/Sma/Sma.StreamHub.Tests.cs index 939d3ee52..c09661948 100644 --- a/tests/indicators/s-z/Sma/Sma.StreamHub.Tests.cs +++ b/tests/indicators/s-z/Sma/Sma.StreamHub.Tests.cs @@ -6,8 +6,7 @@ public class SmaHub : StreamHubTestBase, ITestChainObserver, ITestChainProvider [TestMethod] public override void QuoteObserver() { - List quotesList = Quotes - .ToSortedList(); + List quotesList = Quotes.ToList(); int length = quotesList.Count; @@ -48,7 +47,7 @@ IReadOnlyList streamList } // late arrival - provider.Add(quotesList[80]); + provider.Insert(quotesList[80]); // delete provider.Remove(quotesList[400]); @@ -57,7 +56,7 @@ IReadOnlyList streamList // time-series, for comparison IReadOnlyList seriesList = quotesList - .GetSma(5); + .ToSma(5); // assert, should equal series streamList.Should().HaveCount(length - 1); @@ -70,8 +69,7 @@ IReadOnlyList seriesList [TestMethod] public void ChainObserver() { - List quotesList = Quotes - .ToSortedList(); + List quotesList = Quotes.ToList(); int length = quotesList.Count; @@ -102,7 +100,7 @@ public void ChainObserver() IReadOnlyList seriesList = quotesList .Use(CandlePart.OC2) - .GetSma(11); + .ToSma(11); // assert, should equal series streamList.Should().HaveCount(length); @@ -118,8 +116,7 @@ public void ChainProvider() int emaPeriods = 12; int smaPeriods = 8; - List quotesList = Quotes - .ToSortedList(); + List quotesList = Quotes.ToList(); int length = quotesList.Count; @@ -149,7 +146,7 @@ IReadOnlyList streamList // time-series, for comparison IReadOnlyList seriesList = quotesList - .GetSma(smaPeriods) + .ToSma(smaPeriods) .ToEma(emaPeriods); // assert, should equal series diff --git a/tests/indicators/s-z/SmaAnalysis/SmaAnalysis.StaticSeries.Tests.cs b/tests/indicators/s-z/SmaAnalysis/SmaAnalysis.StaticSeries.Tests.cs index 0c483fe5f..1b41ece3d 100644 --- a/tests/indicators/s-z/SmaAnalysis/SmaAnalysis.StaticSeries.Tests.cs +++ b/tests/indicators/s-z/SmaAnalysis/SmaAnalysis.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class SmaAnalyses : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetSmaAnalysis(20); + .ToSmaAnalysis(20); // proper quantities Assert.AreEqual(502, results.Count); @@ -26,7 +26,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetSmaAnalysis(20); + .ToSmaAnalysis(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(483, results.Count(x => x.Sma != null)); @@ -36,8 +36,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetSmaAnalysis(20); + .ToSma(2) + .ToSmaAnalysis(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.Sma != null)); @@ -47,7 +47,7 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetSmaAnalysis(10) + .ToSmaAnalysis(10) .ToEma(10); Assert.AreEqual(502, results.Count); @@ -58,7 +58,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetSmaAnalysis(15); + .ToSmaAnalysis(15); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Mape is double.NaN)); @@ -68,12 +68,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetSmaAnalysis(6); + .ToSmaAnalysis(6); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetSmaAnalysis(6); + .ToSmaAnalysis(6); Assert.AreEqual(1, r1.Count); } @@ -82,7 +82,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetSmaAnalysis(20) + .ToSmaAnalysis(20) .RemoveWarmupPeriods(); // assertions @@ -94,5 +94,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetSmaAnalysis(0)); + => Quotes.ToSmaAnalysis(0)); } diff --git a/tests/indicators/s-z/Smi/Smi.StaticSeries.Tests.cs b/tests/indicators/s-z/Smi/Smi.StaticSeries.Tests.cs index c9955776d..5dc184c73 100644 --- a/tests/indicators/s-z/Smi/Smi.StaticSeries.Tests.cs +++ b/tests/indicators/s-z/Smi/Smi.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Smi : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetSmi(14, 20, 5); + .ToSmi(14, 20, 5); // proper quantities Assert.AreEqual(502, results.Count); @@ -48,8 +48,8 @@ public override void Standard() public void Chainor() { IReadOnlyList results = Quotes - .GetSmi(14, 20, 5) - .GetSma(10); + .ToSmi(14, 20, 5) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(480, results.Count(x => x.Sma != null)); @@ -59,7 +59,7 @@ public void Chainor() public void NoSignal() { IReadOnlyList results = Quotes - .GetSmi(5, 20, 20, 1); + .ToSmi(5, 20, 20, 1); // signal equals oscillator SmiResult r1 = results[487]; @@ -73,7 +73,7 @@ public void NoSignal() public void SmallPeriods() { IReadOnlyList results = Quotes - .GetSmi(1, 1, 1, 5); + .ToSmi(1, 1, 1, 5); // sample values SmiResult r51 = results[51]; @@ -93,7 +93,7 @@ public void SmallPeriods() public override void BadData() { IReadOnlyList r = BadQuotes - .GetSmi(5, 5, 1, 5); + .ToSmi(5, 5, 1, 5); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Smi is double.NaN)); @@ -103,12 +103,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetSmi(5, 5); + .ToSmi(5, 5); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetSmi(5, 3, 3); + .ToSmi(5, 3, 3); Assert.AreEqual(1, r1.Count); } @@ -117,7 +117,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetSmi(14, 20, 5) + .ToSmi(14, 20, 5) .RemoveWarmupPeriods(); // assertions @@ -133,18 +133,18 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() => - Quotes.GetSmi(0, 5, 5, 5)); + Quotes.ToSmi(0, 5, 5, 5)); // bad first smooth period Assert.ThrowsException(() => - Quotes.GetSmi(14, 0, 5, 5)); + Quotes.ToSmi(14, 0, 5, 5)); // bad second smooth period Assert.ThrowsException(() => - Quotes.GetSmi(14, 3, 0, 5)); + Quotes.ToSmi(14, 3, 0, 5)); // bad signal Assert.ThrowsException(() => - Quotes.GetSmi(9, 3, 1, 0)); + Quotes.ToSmi(9, 3, 1, 0)); } } diff --git a/tests/indicators/s-z/Smma/Smma.StaticSeries.Tests.cs b/tests/indicators/s-z/Smma/Smma.StaticSeries.Tests.cs index 49a25790e..b2370be85 100644 --- a/tests/indicators/s-z/Smma/Smma.StaticSeries.Tests.cs +++ b/tests/indicators/s-z/Smma/Smma.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Smma : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetSmma(20); + .ToSmma(20); // proper quantities Assert.AreEqual(502, results.Count); @@ -30,7 +30,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetSmma(20); + .ToSmma(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(483, results.Count(x => x.Smma != null)); @@ -40,8 +40,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetSmma(20); + .ToSma(2) + .ToSmma(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.Smma != null)); @@ -51,8 +51,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetSmma(20) - .GetSma(10); + .ToSmma(20) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(474, results.Count(x => x.Sma != null)); @@ -62,7 +62,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetSmma(15); + .ToSmma(15); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Smma is double.NaN)); @@ -72,12 +72,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetSmma(5); + .ToSmma(5); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetSmma(5); + .ToSmma(5); Assert.AreEqual(1, r1.Count); } @@ -86,7 +86,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetSmma(20) + .ToSmma(20) .RemoveWarmupPeriods(); // assertions @@ -98,5 +98,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetSmma(0)); + => Quotes.ToSmma(0)); } diff --git a/tests/indicators/s-z/StarcBands/StarcBands.StaticSeries.Tests.cs b/tests/indicators/s-z/StarcBands/StarcBands.StaticSeries.Tests.cs index 7467d3585..86030fed3 100644 --- a/tests/indicators/s-z/StarcBands/StarcBands.StaticSeries.Tests.cs +++ b/tests/indicators/s-z/StarcBands/StarcBands.StaticSeries.Tests.cs @@ -11,7 +11,7 @@ public override void Standard() int atrPeriods = 14; IReadOnlyList results = Quotes - .GetStarcBands(smaPeriods, multiplier, atrPeriods); + .ToStarcBands(smaPeriods, multiplier, atrPeriods); // proper quantities Assert.AreEqual(502, results.Count); @@ -50,7 +50,7 @@ public override void Standard() public override void BadData() { IReadOnlyList r = BadQuotes - .GetStarcBands(10, 3, 15); + .ToStarcBands(10, 3, 15); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.UpperBand is double.NaN)); @@ -60,12 +60,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetStarcBands(10); + .ToStarcBands(10); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetStarcBands(10); + .ToStarcBands(10); Assert.AreEqual(1, r1.Count); } @@ -79,7 +79,7 @@ public void Condense() int lookbackPeriods = Math.Max(smaPeriods, atrPeriods); IReadOnlyList results = Quotes - .GetStarcBands(smaPeriods, multiplier, atrPeriods) + .ToStarcBands(smaPeriods, multiplier, atrPeriods) .Condense(); // assertions @@ -100,7 +100,7 @@ public void Removed() int lookbackPeriods = Math.Max(smaPeriods, atrPeriods); IReadOnlyList results = Quotes - .GetStarcBands(smaPeriods, multiplier, atrPeriods) + .ToStarcBands(smaPeriods, multiplier, atrPeriods) .RemoveWarmupPeriods(); // assertions @@ -117,14 +117,14 @@ public void Exceptions() { // bad EMA period Assert.ThrowsException(() => - Quotes.GetStarcBands(1)); + Quotes.ToStarcBands(1)); // bad ATR period Assert.ThrowsException(() => - Quotes.GetStarcBands(20, 2, 1)); + Quotes.ToStarcBands(20, 2, 1)); // bad multiplier Assert.ThrowsException(() => - Quotes.GetStarcBands(20, 0)); + Quotes.ToStarcBands(20, 0)); } } diff --git a/tests/indicators/s-z/Stc/Stc.StaticSeries.Tests.cs b/tests/indicators/s-z/Stc/Stc.StaticSeries.Tests.cs index 287e5d03d..9cf0fbb6b 100644 --- a/tests/indicators/s-z/Stc/Stc.StaticSeries.Tests.cs +++ b/tests/indicators/s-z/Stc/Stc.StaticSeries.Tests.cs @@ -11,7 +11,7 @@ public override void Standard() int slowPeriods = 26; IReadOnlyList results = Quotes - .GetStc(cyclePeriods, fastPeriods, slowPeriods); + .ToStc(cyclePeriods, fastPeriods, slowPeriods); // proper quantities Assert.AreEqual(502, results.Count); @@ -39,7 +39,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetStc(9, 12, 26); + .ToStc(9, 12, 26); Assert.AreEqual(502, results.Count); Assert.AreEqual(467, results.Count(x => x.Stc != null)); @@ -49,8 +49,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetStc(9, 12, 26); + .ToSma(2) + .ToStc(9, 12, 26); Assert.AreEqual(502, results.Count); Assert.AreEqual(466, results.Count(x => x.Stc != null)); @@ -60,8 +60,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetStc(9, 12, 26) - .GetSma(10); + .ToStc(9, 12, 26) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(458, results.Count(x => x.Sma != null)); @@ -71,7 +71,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetStc(); + .ToStc(); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Stc is double.NaN)); @@ -81,12 +81,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetStc(); + .ToStc(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetStc(); + .ToStc(); Assert.AreEqual(1, r1.Count); } @@ -99,7 +99,7 @@ public void Issue1107() RandomGbm quotes = new(58); IReadOnlyList results = quotes - .GetStc(); + .ToStc(); Assert.AreEqual(58, results.Count); } @@ -112,7 +112,7 @@ public void Removed() int slowPeriods = 26; IReadOnlyList results = Quotes - .GetStc(cyclePeriods, fastPeriods, slowPeriods) + .ToStc(cyclePeriods, fastPeriods, slowPeriods) .RemoveWarmupPeriods(); // assertions @@ -127,14 +127,14 @@ public void Exceptions() { // bad fast period Assert.ThrowsException(() => - Quotes.GetStc(9, 0, 26)); + Quotes.ToStc(9, 0, 26)); // bad slow periods must be larger than faster period Assert.ThrowsException(() => - Quotes.GetStc(9, 12, 12)); + Quotes.ToStc(9, 12, 12)); // bad signal period Assert.ThrowsException(() => - Quotes.GetStc(-1, 12, 26)); + Quotes.ToStc(-1, 12, 26)); } } diff --git a/tests/indicators/s-z/StdDev/StdDev.StaticSeries.Tests.cs b/tests/indicators/s-z/StdDev/StdDev.StaticSeries.Tests.cs index fe2a399e6..c4fbc30b0 100644 --- a/tests/indicators/s-z/StdDev/StdDev.StaticSeries.Tests.cs +++ b/tests/indicators/s-z/StdDev/StdDev.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class StdDev : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetStdDev(10); + .ToStdDev(10); // proper quantities Assert.AreEqual(502, results.Count); @@ -41,7 +41,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetStdDev(10); + .ToStdDev(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(493, results.Count(x => x.StdDev != null)); @@ -51,8 +51,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetStdDev(10); + .ToSma(2) + .ToStdDev(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(492, results.Count(x => x.StdDev != null)); @@ -62,8 +62,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetStdDev(10) - .GetSma(10); + .ToStdDev(10) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(484, results.Count(x => x.Sma != null)); @@ -73,7 +73,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetStdDev(15); + .ToStdDev(15); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.StdDev is double.NaN)); @@ -83,7 +83,7 @@ public override void BadData() public void BigData() { IReadOnlyList r = BigQuotes - .GetStdDev(200); + .ToStdDev(200); Assert.AreEqual(1246, r.Count); } @@ -92,12 +92,12 @@ public void BigData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetStdDev(10); + .ToStdDev(10); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetStdDev(10); + .ToStdDev(10); Assert.AreEqual(1, r1.Count); } @@ -106,7 +106,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetStdDev(10) + .ToStdDev(10) .RemoveWarmupPeriods(); // assertions @@ -123,5 +123,5 @@ public void Exceptions() => // bad lookback period Assert.ThrowsException(() => - Quotes.GetStdDev(1)); + Quotes.ToStdDev(1)); } diff --git a/tests/indicators/s-z/StdDevChannels/StdDevChannels.StaticSeries.Tests.cs b/tests/indicators/s-z/StdDevChannels/StdDevChannels.StaticSeries.Tests.cs index 26e4f21fc..dc145f82b 100644 --- a/tests/indicators/s-z/StdDevChannels/StdDevChannels.StaticSeries.Tests.cs +++ b/tests/indicators/s-z/StdDevChannels/StdDevChannels.StaticSeries.Tests.cs @@ -10,7 +10,7 @@ public override void Standard() double standardDeviations = 2; IReadOnlyList results = - Quotes.GetStdDevChannels(lookbackPeriods, standardDeviations); + Quotes.ToStdDevChannels(lookbackPeriods, standardDeviations); // proper quantities Assert.AreEqual(502, results.Count); @@ -68,7 +68,7 @@ public void FullHistory() // null provided for lookback period IReadOnlyList results = - Quotes.GetStdDevChannels(null); + Quotes.ToStdDevChannels(null); // proper quantities Assert.AreEqual(502, results.Count); @@ -100,7 +100,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetStdDevChannels(); + .ToStdDevChannels(); Assert.AreEqual(502, results.Count); Assert.AreEqual(500, results.Count(x => x.Centerline != null)); @@ -110,8 +110,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetStdDevChannels(); + .ToSma(2) + .ToStdDevChannels(); Assert.AreEqual(502, results.Count); Assert.AreEqual(500, results.Count(x => x.Centerline != null)); @@ -121,7 +121,7 @@ public void Chainee() public override void BadData() { IReadOnlyList r = BadQuotes - .GetStdDevChannels(); + .ToStdDevChannels(); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.UpperChannel is double.NaN)); @@ -131,12 +131,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetStdDevChannels(); + .ToStdDevChannels(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetStdDevChannels(); + .ToStdDevChannels(); Assert.AreEqual(1, r1.Count); } @@ -148,7 +148,7 @@ public void Condense() double standardDeviations = 2; IReadOnlyList results = Quotes - .GetStdDevChannels(lookbackPeriods, standardDeviations) + .ToStdDevChannels(lookbackPeriods, standardDeviations) .Condense(); // assertions @@ -167,7 +167,7 @@ public void Removed() double standardDeviations = 2; IReadOnlyList results = Quotes - .GetStdDevChannels(lookbackPeriods, standardDeviations) + .ToStdDevChannels(lookbackPeriods, standardDeviations) .RemoveWarmupPeriods(); // assertions @@ -184,10 +184,10 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() => - Quotes.GetStdDevChannels(0)); + Quotes.ToStdDevChannels(0)); // bad standard deviations Assert.ThrowsException(() => - Quotes.GetStdDevChannels(20, 0)); + Quotes.ToStdDevChannels(20, 0)); } } diff --git a/tests/indicators/s-z/Stoch/Stoch.StaticSeries.Tests.cs b/tests/indicators/s-z/Stoch/Stoch.StaticSeries.Tests.cs index f871e1a7e..6e8fe7a1b 100644 --- a/tests/indicators/s-z/Stoch/Stoch.StaticSeries.Tests.cs +++ b/tests/indicators/s-z/Stoch/Stoch.StaticSeries.Tests.cs @@ -11,7 +11,7 @@ public override void Standard() // Slow int smoothPeriods = 3; IReadOnlyList results = Quotes - .GetStoch(lookbackPeriods, signalPeriods, smoothPeriods); + .ToStoch(lookbackPeriods, signalPeriods, smoothPeriods); // proper quantities Assert.AreEqual(502, results.Count); @@ -72,7 +72,7 @@ public override void Standard() // Slow public void Extended() // with extra parameters { IReadOnlyList results = - Quotes.GetStoch(9, 3, 3, 5, 4, MaType.SMMA); + Quotes.ToStoch(9, 3, 3, 5, 4, MaType.SMMA); // proper quantities Assert.AreEqual(502, results.Count); @@ -115,8 +115,8 @@ public void Extended() // with extra parameters public void Chainor() { IReadOnlyList results = Quotes - .GetStoch() - .GetSma(10); + .ToStoch() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(478, results.Count(x => x.Sma != null)); @@ -130,7 +130,7 @@ public void NoSignal() const int smoothPeriods = 3; IReadOnlyList results = Quotes - .GetStoch(lookbackPeriods, signalPeriods, smoothPeriods); + .ToStoch(lookbackPeriods, signalPeriods, smoothPeriods); // signal equals oscillator StochResult r1 = results[487]; @@ -148,7 +148,7 @@ public void Fast() int smoothPeriods = 1; IReadOnlyList results = Quotes - .GetStoch(lookbackPeriods, signalPeriods, smoothPeriods); + .ToStoch(lookbackPeriods, signalPeriods, smoothPeriods); // sample values StochResult r1 = results[487]; @@ -168,7 +168,7 @@ public void FastSmall() int smoothPeriods = 1; IReadOnlyList results = Quotes - .GetStoch(lookbackPeriods, signalPeriods, smoothPeriods); + .ToStoch(lookbackPeriods, signalPeriods, smoothPeriods); // sample values StochResult r1 = results[70]; @@ -182,7 +182,7 @@ public void FastSmall() public override void BadData() { IReadOnlyList r = BadQuotes - .GetStoch(15); + .ToStoch(15); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Oscillator is double.NaN)); @@ -192,12 +192,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetStoch(); + .ToStoch(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetStoch(); + .ToStoch(); Assert.AreEqual(1, r1.Count); } @@ -210,7 +210,7 @@ public void Removed() int smoothPeriods = 3; IReadOnlyList results = Quotes - .GetStoch(lookbackPeriods, signalPeriods, smoothPeriods) + .ToStoch(lookbackPeriods, signalPeriods, smoothPeriods) .RemoveWarmupPeriods(); // assertions @@ -231,7 +231,7 @@ public void Boundary() IReadOnlyList results = Data .GetRandom(2500) - .GetStoch(lookbackPeriods, signalPeriods, smoothPeriods); + .ToStoch(lookbackPeriods, signalPeriods, smoothPeriods); // test boundary condition @@ -264,26 +264,26 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() => - Quotes.GetStoch(0)); + Quotes.ToStoch(0)); // bad signal period Assert.ThrowsException(() => - Quotes.GetStoch(14, 0)); + Quotes.ToStoch(14, 0)); // bad smoothing period Assert.ThrowsException(() => - Quotes.GetStoch(14, 3, 0)); + Quotes.ToStoch(14, 3, 0)); // bad kFactor Assert.ThrowsException(() => - Quotes.GetStoch(9, 3, 1, 0, 2, MaType.SMA)); + Quotes.ToStoch(9, 3, 1, 0, 2, MaType.SMA)); // bad dFactor Assert.ThrowsException(() => - Quotes.GetStoch(9, 3, 1, 3, 0, MaType.SMA)); + Quotes.ToStoch(9, 3, 1, 3, 0, MaType.SMA)); // bad MA type Assert.ThrowsException(() => - Quotes.GetStoch(9, 3, 3, 3, 2, MaType.ALMA)); + Quotes.ToStoch(9, 3, 3, 3, 2, MaType.ALMA)); } } diff --git a/tests/indicators/s-z/StochRsi/StochRsi.StaticSeries.Tests.cs b/tests/indicators/s-z/StochRsi/StochRsi.StaticSeries.Tests.cs index 856009de1..2efbd0983 100644 --- a/tests/indicators/s-z/StochRsi/StochRsi.StaticSeries.Tests.cs +++ b/tests/indicators/s-z/StochRsi/StochRsi.StaticSeries.Tests.cs @@ -12,7 +12,7 @@ public override void Standard() // Fast RSI int smoothPeriods = 1; IReadOnlyList results = - Quotes.GetStochRsi(rsiPeriods, stochPeriods, signalPeriods, smoothPeriods); + Quotes.ToStochRsi(rsiPeriods, stochPeriods, signalPeriods, smoothPeriods); // assertions @@ -48,7 +48,7 @@ public void SlowRsi() int smoothPeriods = 3; IReadOnlyList results = - Quotes.GetStochRsi(rsiPeriods, stochPeriods, signalPeriods, smoothPeriods); + Quotes.ToStochRsi(rsiPeriods, stochPeriods, signalPeriods, smoothPeriods); // assertions @@ -80,7 +80,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetStochRsi(14, 14, 3); + .ToStochRsi(14, 14, 3); Assert.AreEqual(502, results.Count); Assert.AreEqual(475, results.Count(x => x.StochRsi != null)); @@ -91,8 +91,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetStochRsi(14, 14, 3); + .ToSma(2) + .ToStochRsi(14, 14, 3); Assert.AreEqual(502, results.Count); Assert.AreEqual(474, results.Count(x => x.StochRsi != null)); @@ -102,8 +102,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetStochRsi(14, 14, 3, 3) - .GetSma(10); + .ToStochRsi(14, 14, 3, 3) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(464, results.Count(x => x.Sma != null)); @@ -113,7 +113,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetStochRsi(15, 20, 3, 2); + .ToStochRsi(15, 20, 3, 2); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.StochRsi is double.NaN)); @@ -123,12 +123,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetStochRsi(10, 20, 3); + .ToStochRsi(10, 20, 3); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetStochRsi(8, 13, 2); + .ToStochRsi(8, 13, 2); Assert.AreEqual(1, r1.Count); } @@ -142,7 +142,7 @@ public void Removed() int smoothPeriods = 3; IReadOnlyList results = Quotes - .GetStochRsi(rsiPeriods, stochPeriods, signalPeriods, smoothPeriods) + .ToStochRsi(rsiPeriods, stochPeriods, signalPeriods, smoothPeriods) .RemoveWarmupPeriods(); // assertions @@ -159,18 +159,18 @@ public void Exceptions() { // bad RSI period Assert.ThrowsException(() => - Quotes.GetStochRsi(0, 14, 3)); + Quotes.ToStochRsi(0, 14, 3)); // bad STO period Assert.ThrowsException(() => - Quotes.GetStochRsi(14, 0, 3, 3)); + Quotes.ToStochRsi(14, 0, 3, 3)); // bad STO signal period Assert.ThrowsException(() => - Quotes.GetStochRsi(14, 14, 0)); + Quotes.ToStochRsi(14, 14, 0)); // bad STO smoothing period Assert.ThrowsException(() => - Quotes.GetStochRsi(14, 14, 3, 0)); + Quotes.ToStochRsi(14, 14, 3, 0)); } } diff --git a/tests/indicators/s-z/SuperTrend/SuperTrend.StaticSeries.Tests.cs b/tests/indicators/s-z/SuperTrend/SuperTrend.StaticSeries.Tests.cs index 31860967a..3b652b756 100644 --- a/tests/indicators/s-z/SuperTrend/SuperTrend.StaticSeries.Tests.cs +++ b/tests/indicators/s-z/SuperTrend/SuperTrend.StaticSeries.Tests.cs @@ -10,7 +10,7 @@ public override void Standard() const double multiplier = 3; IReadOnlyList results = Quotes - .GetSuperTrend(lookbackPeriods, multiplier); + .ToSuperTrend(lookbackPeriods, multiplier); // proper quantities Assert.AreEqual(502, results.Count); @@ -51,10 +51,10 @@ public override void Standard() [TestMethod] public void Bitcoin() { - IEnumerable h = Data.GetBitcoin(); + IReadOnlyList h = Data.GetBitcoin(); IReadOnlyList results = h - .GetSuperTrend(); + .ToSuperTrend(); Assert.AreEqual(1246, results.Count); @@ -66,7 +66,7 @@ public void Bitcoin() public override void BadData() { IReadOnlyList r = BadQuotes - .GetSuperTrend(7); + .ToSuperTrend(7); Assert.AreEqual(502, r.Count); } @@ -75,12 +75,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetSuperTrend(); + .ToSuperTrend(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetSuperTrend(); + .ToSuperTrend(); Assert.AreEqual(1, r1.Count); } @@ -92,7 +92,7 @@ public void Condense() double multiplier = 3; IReadOnlyList results = Quotes - .GetSuperTrend(lookbackPeriods, multiplier) + .ToSuperTrend(lookbackPeriods, multiplier) .Condense(); // assertions @@ -111,7 +111,7 @@ public void Removed() double multiplier = 3; IReadOnlyList results = Quotes - .GetSuperTrend(lookbackPeriods, multiplier) + .ToSuperTrend(lookbackPeriods, multiplier) .RemoveWarmupPeriods(); // assertions @@ -128,10 +128,10 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() => - Quotes.GetSuperTrend(1)); + Quotes.ToSuperTrend(1)); // bad multiplier Assert.ThrowsException(() => - Quotes.GetSuperTrend(7, 0)); + Quotes.ToSuperTrend(7, 0)); } } diff --git a/tests/indicators/s-z/T3/T3.StaticSeries.Tests.cs b/tests/indicators/s-z/T3/T3.StaticSeries.Tests.cs index 6b90394fc..5cb40775b 100644 --- a/tests/indicators/s-z/T3/T3.StaticSeries.Tests.cs +++ b/tests/indicators/s-z/T3/T3.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class T3 : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetT3(); + .ToT3(); // proper quantities Assert.AreEqual(502, results.Count); @@ -38,7 +38,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetT3(); + .ToT3(); Assert.AreEqual(502, results.Count); Assert.AreEqual(502, results.Count(x => x.T3 != null)); @@ -48,8 +48,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetT3(); + .ToSma(2) + .ToT3(); Assert.AreEqual(502, results.Count); Assert.AreEqual(501, results.Count(x => x.T3 != null)); @@ -59,8 +59,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetT3() - .GetSma(10); + .ToT3() + .ToSma(10); Assert.AreEqual(502, results.Count); } @@ -69,7 +69,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetT3(); + .ToT3(); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.T3 is double.NaN)); @@ -79,12 +79,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetT3(); + .ToT3(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetT3(); + .ToT3(); Assert.AreEqual(1, r1.Count); } @@ -94,10 +94,10 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() => - Quotes.GetT3(0)); + Quotes.ToT3(0)); // bad volume factor Assert.ThrowsException(() => - Quotes.GetT3(25, 0)); + Quotes.ToT3(25, 0)); } } diff --git a/tests/indicators/s-z/Tema/Tema.StaticSeries.Tests.cs b/tests/indicators/s-z/Tema/Tema.StaticSeries.Tests.cs index a2e2f483c..23e39b123 100644 --- a/tests/indicators/s-z/Tema/Tema.StaticSeries.Tests.cs +++ b/tests/indicators/s-z/Tema/Tema.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Tema : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetTema(20); + .ToTema(20); // proper quantities Assert.AreEqual(502, results.Count); @@ -32,7 +32,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetTema(20); + .ToTema(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(483, results.Count(x => x.Tema != null)); @@ -42,8 +42,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetTema(20); + .ToSma(2) + .ToTema(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.Tema != null)); @@ -53,8 +53,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetTema(20) - .GetSma(10); + .ToTema(20) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(474, results.Count(x => x.Sma != null)); @@ -64,7 +64,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetTema(15); + .ToTema(15); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Tema is double.NaN)); @@ -74,12 +74,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetTema(5); + .ToTema(5); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetTema(5); + .ToTema(5); Assert.AreEqual(1, r1.Count); } @@ -88,7 +88,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetTema(20) + .ToTema(20) .RemoveWarmupPeriods(); // assertions @@ -102,5 +102,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetTema(0)); + => Quotes.ToTema(0)); } diff --git a/tests/indicators/s-z/Tr/Tr.StaticSeries.Tests.cs b/tests/indicators/s-z/Tr/Tr.StaticSeries.Tests.cs index 1e5a2b71d..e44f90c03 100644 --- a/tests/indicators/s-z/Tr/Tr.StaticSeries.Tests.cs +++ b/tests/indicators/s-z/Tr/Tr.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Tr : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetTr(); + .ToTr(); // proper quantities Assert.AreEqual(502, results.Count); @@ -41,11 +41,11 @@ public void Chainor() { // same as ATR IReadOnlyList results = Quotes - .GetTr() - .GetSmma(14); + .ToTr() + .ToSmma(14); IReadOnlyList atrResults = Quotes - .GetAtr(); + .ToAtr(); for (int i = 0; i < results.Count; i++) { @@ -61,7 +61,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetTr(); + .ToTr(); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Tr is double.NaN)); @@ -71,12 +71,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetTr(); + .ToTr(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetTr(); + .ToTr(); Assert.AreEqual(1, r1.Count); } diff --git a/tests/indicators/s-z/Tr/Tr.StreamHub.Tests.cs b/tests/indicators/s-z/Tr/Tr.StreamHub.Tests.cs index 86ac56307..b67bcc866 100644 --- a/tests/indicators/s-z/Tr/Tr.StreamHub.Tests.cs +++ b/tests/indicators/s-z/Tr/Tr.StreamHub.Tests.cs @@ -6,8 +6,7 @@ public class TrHub : StreamHubTestBase, ITestChainProvider [TestMethod] public override void QuoteObserver() { - List quotesList = Quotes - .ToSortedList(); + List quotesList = Quotes.ToList(); int length = quotesList.Count; @@ -48,7 +47,7 @@ IReadOnlyList streamList } // late arrival - provider.Add(quotesList[80]); + provider.Insert(quotesList[80]); // delete provider.Remove(quotesList[400]); @@ -56,7 +55,7 @@ IReadOnlyList streamList // time-series, for comparison IReadOnlyList seriesList = quotesList - .GetTr(); + .ToTr(); // assert, should equal series streamList.Should().HaveCount(length - 1); @@ -71,8 +70,7 @@ public void ChainProvider() { int smaPeriods = 8; - List quotesList = Quotes - .ToSortedList(); + List quotesList = Quotes.ToList(); int length = quotesList.Count; @@ -102,8 +100,8 @@ IReadOnlyList streamList // time-series, for comparison IReadOnlyList seriesList = quotesList - .GetTr() - .GetSma(smaPeriods); + .ToTr() + .ToSma(smaPeriods); // assert, should equal series streamList.Should().HaveCount(length - 1); diff --git a/tests/indicators/s-z/Trix/Trix.StaticSeries.Tests.cs b/tests/indicators/s-z/Trix/Trix.StaticSeries.Tests.cs index da1623b0d..626d544b5 100644 --- a/tests/indicators/s-z/Trix/Trix.StaticSeries.Tests.cs +++ b/tests/indicators/s-z/Trix/Trix.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Trix : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetTrix(20); + .ToTrix(20); // proper quantities Assert.AreEqual(502, results.Count); @@ -37,7 +37,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetTrix(20); + .ToTrix(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.Trix != null)); @@ -47,8 +47,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetTrix(20); + .ToSma(2) + .ToTrix(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(481, results.Count(x => x.Trix != null)); @@ -58,8 +58,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetTrix(20) - .GetSma(10); + .ToTrix(20) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(473, results.Count(x => x.Sma != null)); @@ -69,7 +69,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetTrix(15); + .ToTrix(15); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Trix is double.NaN)); @@ -79,12 +79,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetTrix(5); + .ToTrix(5); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetTrix(5); + .ToTrix(5); Assert.AreEqual(1, r1.Count); } @@ -93,7 +93,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetTrix(20) + .ToTrix(20) .RemoveWarmupPeriods(); // assertions @@ -108,5 +108,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetTrix(0)); + => Quotes.ToTrix(0)); } diff --git a/tests/indicators/s-z/Tsi/Tsi.StaticSeries.Tests.cs b/tests/indicators/s-z/Tsi/Tsi.StaticSeries.Tests.cs index 4b13e4f66..ec96ba655 100644 --- a/tests/indicators/s-z/Tsi/Tsi.StaticSeries.Tests.cs +++ b/tests/indicators/s-z/Tsi/Tsi.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Tsi : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetTsi(); + .ToTsi(); // proper quantities Assert.AreEqual(502, results.Count); @@ -45,7 +45,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetTsi(); + .ToTsi(); Assert.AreEqual(502, results.Count); Assert.AreEqual(465, results.Count(x => x.Tsi != null)); @@ -55,8 +55,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetTsi(); + .ToSma(2) + .ToTsi(); Assert.AreEqual(502, results.Count); Assert.AreEqual(464, results.Count(x => x.Tsi != null)); @@ -66,8 +66,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetTsi() - .GetSma(10); + .ToTsi() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(456, results.Count(x => x.Sma != null)); @@ -77,7 +77,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetTsi(); + .ToTsi(); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Tsi is double.NaN)); @@ -87,7 +87,7 @@ public override void BadData() public void BigData() { IReadOnlyList r = BigQuotes - .GetTsi(); + .ToTsi(); Assert.AreEqual(1246, r.Count); } @@ -96,12 +96,12 @@ public void BigData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetTsi(); + .ToTsi(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetTsi(); + .ToTsi(); Assert.AreEqual(1, r1.Count); } @@ -110,7 +110,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetTsi() + .ToTsi() .RemoveWarmupPeriods(); // assertions @@ -126,14 +126,14 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() => - Quotes.GetTsi(0)); + Quotes.ToTsi(0)); // bad smoothing period Assert.ThrowsException(() => - Quotes.GetTsi(25, 0)); + Quotes.ToTsi(25, 0)); // bad signal period Assert.ThrowsException(() => - Quotes.GetTsi(25, 13, -1)); + Quotes.ToTsi(25, 13, -1)); } } diff --git a/tests/indicators/s-z/UlcerIndex/UlcerIndex.StaticSeries.Tests.cs b/tests/indicators/s-z/UlcerIndex/UlcerIndex.StaticSeries.Tests.cs index bd37fb6d6..8e08fa5f7 100644 --- a/tests/indicators/s-z/UlcerIndex/UlcerIndex.StaticSeries.Tests.cs +++ b/tests/indicators/s-z/UlcerIndex/UlcerIndex.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class UlcerIndex : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetUlcerIndex(); + .ToUlcerIndex(); // proper quantities Assert.AreEqual(502, results.Count); @@ -23,7 +23,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetUlcerIndex(); + .ToUlcerIndex(); Assert.AreEqual(502, results.Count); Assert.AreEqual(489, results.Count(x => x.UlcerIndex != null)); @@ -33,8 +33,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetUlcerIndex(); + .ToSma(2) + .ToUlcerIndex(); Assert.AreEqual(502, results.Count); Assert.AreEqual(488, results.Count(x => x.UlcerIndex != null)); @@ -44,8 +44,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetUlcerIndex() - .GetSma(10); + .ToUlcerIndex() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(480, results.Count(x => x.Sma != null)); @@ -55,7 +55,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetUlcerIndex(15); + .ToUlcerIndex(15); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.UlcerIndex is double.NaN)); @@ -65,12 +65,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetUlcerIndex(); + .ToUlcerIndex(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetUlcerIndex(); + .ToUlcerIndex(); Assert.AreEqual(1, r1.Count); } @@ -79,7 +79,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetUlcerIndex() + .ToUlcerIndex() .RemoveWarmupPeriods(); // assertions @@ -93,5 +93,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetUlcerIndex(0)); + => Quotes.ToUlcerIndex(0)); } diff --git a/tests/indicators/s-z/Ultimate/Ultimate.StaticSeries.Tests.cs b/tests/indicators/s-z/Ultimate/Ultimate.StaticSeries.Tests.cs index 4d1e5532b..8571195da 100644 --- a/tests/indicators/s-z/Ultimate/Ultimate.StaticSeries.Tests.cs +++ b/tests/indicators/s-z/Ultimate/Ultimate.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Ultimate : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetUltimate(); + .ToUltimate(); // proper quantities Assert.AreEqual(502, results.Count); @@ -28,8 +28,8 @@ public override void Standard() public void Chainor() { IReadOnlyList results = Quotes - .GetUltimate() - .GetSma(10); + .ToUltimate() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(465, results.Count(x => x.Sma != null)); @@ -39,7 +39,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetUltimate(1, 2, 3); + .ToUltimate(1, 2, 3); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Ultimate is double.NaN)); @@ -49,12 +49,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetUltimate(); + .ToUltimate(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetUltimate(); + .ToUltimate(); Assert.AreEqual(1, r1.Count); } @@ -63,7 +63,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetUltimate() + .ToUltimate() .RemoveWarmupPeriods(); // assertions @@ -78,14 +78,14 @@ public void Exceptions() { // bad short period Assert.ThrowsException(() => - Quotes.GetUltimate(0)); + Quotes.ToUltimate(0)); // bad middle period Assert.ThrowsException(() => - Quotes.GetUltimate(7, 6)); + Quotes.ToUltimate(7, 6)); // bad long period Assert.ThrowsException(() => - Quotes.GetUltimate(7, 14, 11)); + Quotes.ToUltimate(7, 14, 11)); } } diff --git a/tests/indicators/s-z/VolatilityStop/VolatilityStop.StaticSeries.Tests.cs b/tests/indicators/s-z/VolatilityStop/VolatilityStop.StaticSeries.Tests.cs index 7cb17f5be..66687b868 100644 --- a/tests/indicators/s-z/VolatilityStop/VolatilityStop.StaticSeries.Tests.cs +++ b/tests/indicators/s-z/VolatilityStop/VolatilityStop.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class VolatilityStop : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = - Quotes.GetVolatilityStop(14); + Quotes.ToVolatilityStop(14); // proper quantities Assert.AreEqual(502, results.Count); @@ -64,8 +64,8 @@ public override void Standard() public void Chainor() { IReadOnlyList results = Quotes - .GetVolatilityStop() - .GetSma(10); + .ToVolatilityStop() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(439, results.Count(x => x.Sma != null)); @@ -75,7 +75,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetVolatilityStop(); + .ToVolatilityStop(); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Sar is double.NaN)); @@ -85,12 +85,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetVolatilityStop(); + .ToVolatilityStop(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetVolatilityStop(); + .ToVolatilityStop(); Assert.AreEqual(1, r1.Count); } @@ -99,7 +99,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetVolatilityStop(14) + .ToVolatilityStop(14) .RemoveWarmupPeriods(); // assertions @@ -115,10 +115,10 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() => - Quotes.GetVolatilityStop(1)); + Quotes.ToVolatilityStop(1)); // bad multiplier Assert.ThrowsException(() => - Quotes.GetVolatilityStop(20, 0)); + Quotes.ToVolatilityStop(20, 0)); } } diff --git a/tests/indicators/s-z/Vortex/Vortex.StaticSeries.Tests.cs b/tests/indicators/s-z/Vortex/Vortex.StaticSeries.Tests.cs index 8ef219ea8..389863567 100644 --- a/tests/indicators/s-z/Vortex/Vortex.StaticSeries.Tests.cs +++ b/tests/indicators/s-z/Vortex/Vortex.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Vortex : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetVortex(14); + .ToVortex(14); // proper quantities Assert.AreEqual(502, results.Count); @@ -39,7 +39,7 @@ public override void Standard() public override void BadData() { IReadOnlyList r = BadQuotes - .GetVortex(20); + .ToVortex(20); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Pvi is double.NaN)); @@ -49,12 +49,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetVortex(5); + .ToVortex(5); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetVortex(5); + .ToVortex(5); Assert.AreEqual(1, r1.Count); } @@ -63,7 +63,7 @@ public override void NoQuotes() public void Condense() { IReadOnlyList results = Quotes - .GetVortex(14) + .ToVortex(14) .Condense(); // assertions @@ -78,7 +78,7 @@ public void Condense() public void Removed() { IReadOnlyList results = Quotes - .GetVortex(14) + .ToVortex(14) .RemoveWarmupPeriods(); // assertions @@ -93,5 +93,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetVortex(1)); + => Quotes.ToVortex(1)); } diff --git a/tests/indicators/s-z/Vwap/Vwap.StaticSeries.Tests.cs b/tests/indicators/s-z/Vwap/Vwap.StaticSeries.Tests.cs index aa31fe8d5..cb07abc66 100644 --- a/tests/indicators/s-z/Vwap/Vwap.StaticSeries.Tests.cs +++ b/tests/indicators/s-z/Vwap/Vwap.StaticSeries.Tests.cs @@ -3,14 +3,15 @@ namespace StaticSeries; [TestClass] public class Vwap : StaticSeriesTestBase { - private readonly IEnumerable intraday = Data.GetIntraday() + private static readonly IReadOnlyList intraday = Data.GetIntraday() .OrderBy(x => x.Timestamp) - .Take(391); + .Take(391) + .ToList(); [TestMethod] public override void Standard() { - IReadOnlyList results = intraday.GetVwap(); + IReadOnlyList results = intraday.ToVwap(); // proper quantities Assert.AreEqual(391, results.Count); @@ -34,10 +35,10 @@ public override void Standard() public void WithStartDate() { DateTime startDate = - DateTime.ParseExact("2020-12-15 10:00", "yyyy-MM-dd h:mm", englishCulture); + DateTime.ParseExact("2020-12-15 10:00", "yyyy-MM-dd h:mm", invariantCulture); IReadOnlyList results = intraday - .GetVwap(startDate); + .ToVwap(startDate); // proper quantities Assert.AreEqual(391, results.Count); @@ -61,8 +62,8 @@ public void WithStartDate() public void Chainor() { IReadOnlyList results = Quotes - .GetVwap() - .GetSma(10); + .ToVwap() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(493, results.Count(x => x.Sma != null)); @@ -72,7 +73,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetVwap(); + .ToVwap(); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Vwap is double.NaN)); @@ -82,12 +83,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetVwap(); + .ToVwap(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetVwap(); + .ToVwap(); Assert.AreEqual(1, r1.Count); } @@ -97,7 +98,7 @@ public void Removed() { // no start date IReadOnlyList results = intraday - .GetVwap() + .ToVwap() .RemoveWarmupPeriods(); // assertions @@ -108,10 +109,10 @@ public void Removed() // with start date DateTime startDate = - DateTime.ParseExact("2020-12-15 10:00", "yyyy-MM-dd h:mm", englishCulture); + DateTime.ParseExact("2020-12-15 10:00", "yyyy-MM-dd h:mm", invariantCulture); IReadOnlyList sdResults = intraday - .GetVwap(startDate) + .ToVwap(startDate) .RemoveWarmupPeriods(); // assertions @@ -126,9 +127,9 @@ public void Exceptions() { // bad SMA period DateTime startDate = - DateTime.ParseExact("2000-12-15", "yyyy-MM-dd", englishCulture); + DateTime.ParseExact("2000-12-15", "yyyy-MM-dd", invariantCulture); Assert.ThrowsException(() => - Quotes.GetVwap(startDate)); + Quotes.ToVwap(startDate)); } } diff --git a/tests/indicators/s-z/Vwma/Vwma.StaticSeries.Tests.cs b/tests/indicators/s-z/Vwma/Vwma.StaticSeries.Tests.cs index c3fed2674..8cfc11cc0 100644 --- a/tests/indicators/s-z/Vwma/Vwma.StaticSeries.Tests.cs +++ b/tests/indicators/s-z/Vwma/Vwma.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Vwma : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetVwma(10); + .ToVwma(10); // proper quantities Assert.AreEqual(502, results.Count); @@ -28,8 +28,8 @@ public override void Standard() public void Chainor() { IReadOnlyList results = Quotes - .GetVwma(10) - .GetSma(10); + .ToVwma(10) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(484, results.Count(x => x.Sma != null)); @@ -39,7 +39,7 @@ public void Chainor() public override void BadData() { IReadOnlyList r = BadQuotes - .GetVwma(15); + .ToVwma(15); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Vwma is double.NaN)); @@ -49,12 +49,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetVwma(4); + .ToVwma(4); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetVwma(4); + .ToVwma(4); Assert.AreEqual(1, r1.Count); } @@ -63,7 +63,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetVwma(10) + .ToVwma(10) .RemoveWarmupPeriods(); // assertions @@ -77,5 +77,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetVwma(0)); + => Quotes.ToVwma(0)); } diff --git a/tests/indicators/s-z/WilliamsR/WilliamsR.StaticSeries.Tests.cs b/tests/indicators/s-z/WilliamsR/WilliamsR.StaticSeries.Tests.cs index 36c855f79..c0af8b3e2 100644 --- a/tests/indicators/s-z/WilliamsR/WilliamsR.StaticSeries.Tests.cs +++ b/tests/indicators/s-z/WilliamsR/WilliamsR.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class WilliamsR : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetWilliamsR(); + .ToWilliamsR(); // proper quantities Assert.AreEqual(502, results.Count); @@ -37,8 +37,8 @@ public override void Standard() public void Chainor() { IReadOnlyList results = Quotes - .GetWilliamsR() - .GetSma(10); + .ToWilliamsR() + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(480, results.Count(x => x.Sma != null)); @@ -48,7 +48,7 @@ public void Chainor() public override void BadData() { IReadOnlyList results = BadQuotes - .GetWilliamsR(20); + .ToWilliamsR(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(0, results.Count(x => x.WilliamsR is double.NaN)); @@ -58,12 +58,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetWilliamsR(); + .ToWilliamsR(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetWilliamsR(); + .ToWilliamsR(); Assert.AreEqual(1, r1.Count); } @@ -72,7 +72,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetWilliamsR() + .ToWilliamsR() .RemoveWarmupPeriods(); // assertions @@ -87,7 +87,7 @@ public void Boundary() { IReadOnlyList results = Data .GetRandom(2500) - .GetWilliamsR(); + .ToWilliamsR(); // analyze boundary for (int i = 0; i < results.Count; i++) @@ -116,7 +116,7 @@ public void Issue1127() // get indicators IReadOnlyList resultsList = quotesList - .GetWilliamsR(); + .ToWilliamsR(); Console.WriteLine($"%R from {length} quotes."); @@ -140,5 +140,5 @@ public void Issue1127() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetWilliamsR(0)); + => Quotes.ToWilliamsR(0)); } diff --git a/tests/indicators/s-z/Wma/Wma.StaticSeries.Tests.cs b/tests/indicators/s-z/Wma/Wma.StaticSeries.Tests.cs index 014820658..842b28399 100644 --- a/tests/indicators/s-z/Wma/Wma.StaticSeries.Tests.cs +++ b/tests/indicators/s-z/Wma/Wma.StaticSeries.Tests.cs @@ -7,7 +7,7 @@ public class Wma : StaticSeriesTestBase public override void Standard() { IReadOnlyList results = Quotes - .GetWma(20); + .ToWma(20); // proper quantities Assert.AreEqual(502, results.Count); @@ -26,7 +26,7 @@ public void UseReusable() { IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetWma(20); + .ToWma(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(483, results.Count(x => x.Wma != null)); @@ -36,8 +36,8 @@ public void UseReusable() public void Chainee() { IReadOnlyList results = Quotes - .GetSma(2) - .GetWma(20); + .ToSma(2) + .ToWma(20); Assert.AreEqual(502, results.Count); Assert.AreEqual(482, results.Count(x => x.Wma != null)); @@ -47,8 +47,8 @@ public void Chainee() public void Chainor() { IReadOnlyList results = Quotes - .GetWma(20) - .GetSma(10); + .ToWma(20) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(474, results.Count(x => x.Sma != null)); @@ -58,11 +58,11 @@ public void Chainor() public void Chaining() { IReadOnlyList standard = Quotes - .GetWma(17); + .ToWma(17); IReadOnlyList results = Quotes .Use(CandlePart.Close) - .GetWma(17); + .ToWma(17); // assertions for (int i = 0; i < results.Count; i++) @@ -79,7 +79,7 @@ public void Chaining() public override void BadData() { IReadOnlyList r = BadQuotes - .GetWma(15); + .ToWma(15); Assert.AreEqual(502, r.Count); Assert.AreEqual(0, r.Count(x => x.Wma is double.NaN)); @@ -89,12 +89,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetWma(5); + .ToWma(5); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetWma(5); + .ToWma(5); Assert.AreEqual(1, r1.Count); } @@ -103,7 +103,7 @@ public override void NoQuotes() public void Removed() { IReadOnlyList results = Quotes - .GetWma(20) + .ToWma(20) .RemoveWarmupPeriods(); // assertions @@ -117,5 +117,5 @@ public void Removed() [TestMethod] public void Exceptions() => Assert.ThrowsException(() - => Quotes.GetWma(0)); + => Quotes.ToWma(0)); } diff --git a/tests/indicators/s-z/ZigZag/ZigZag.StaticSeries.Tests.cs b/tests/indicators/s-z/ZigZag/ZigZag.StaticSeries.Tests.cs index abdedebde..30c5e0300 100644 --- a/tests/indicators/s-z/ZigZag/ZigZag.StaticSeries.Tests.cs +++ b/tests/indicators/s-z/ZigZag/ZigZag.StaticSeries.Tests.cs @@ -9,7 +9,7 @@ public class ZigZag : StaticSeriesTestBase public override void Standard() // on Close { IReadOnlyList results = - Quotes.GetZigZag(EndType.Close, 3); + Quotes.ToZigZag(EndType.Close, 3); // proper quantities Assert.AreEqual(502, results.Count); @@ -60,7 +60,7 @@ public override void Standard() // on Close public void StandardHighLow() { IReadOnlyList results = - Quotes.GetZigZag(EndType.HighLow, 3); + Quotes.ToZigZag(EndType.HighLow, 3); // proper quantities Assert.AreEqual(502, results.Count); @@ -111,8 +111,8 @@ public void StandardHighLow() public void Chainor() { IReadOnlyList results = Quotes - .GetZigZag(EndType.Close, 3) - .GetSma(10); + .ToZigZag(EndType.Close, 3) + .ToSma(10); Assert.AreEqual(502, results.Count); Assert.AreEqual(225, results.Count(x => x.Sma != null)); @@ -124,11 +124,12 @@ public void NoEntry() // thresholds are never met string json = File.ReadAllText("./s-z/ZigZag/data.ethusdt.json"); - IReadOnlyCollection quotes = JsonConvert - .DeserializeObject>(json); + IReadOnlyList quotes = JsonConvert + .DeserializeObject>(json) + .ToList(); IReadOnlyList results = quotes - .GetZigZag(); + .ToZigZag(); Assert.AreEqual(0, results.Count(x => x.PointType != null)); } @@ -139,11 +140,12 @@ public void Issue632() // thresholds are never met string json = File.ReadAllText("./s-z/ZigZag/data.issue632.json"); - IReadOnlyCollection quotesList = JsonConvert - .DeserializeObject>(json); + IReadOnlyList quotesList = JsonConvert + .DeserializeObject>(json) + .ToList(); IReadOnlyList resultsList = quotesList - .GetZigZag(); + .ToZigZag(); Assert.AreEqual(17, resultsList.Count); } @@ -152,12 +154,12 @@ public void Issue632() public override void BadData() { IReadOnlyList r1 = BadQuotes - .GetZigZag(); + .ToZigZag(); Assert.AreEqual(502, r1.Count); IReadOnlyList r2 = BadQuotes - .GetZigZag(EndType.HighLow); + .ToZigZag(EndType.HighLow); Assert.AreEqual(502, r2.Count); } @@ -166,12 +168,12 @@ public override void BadData() public override void NoQuotes() { IReadOnlyList r0 = Noquotes - .GetZigZag(); + .ToZigZag(); Assert.AreEqual(0, r0.Count); IReadOnlyList r1 = Onequote - .GetZigZag(); + .ToZigZag(); Assert.AreEqual(1, r1.Count); } @@ -180,7 +182,7 @@ public override void NoQuotes() public void Condense() { IReadOnlyList results = Quotes - .GetZigZag(EndType.Close, 3) + .ToZigZag(EndType.Close, 3) .Condense(); // assertions @@ -192,16 +194,17 @@ public void SchrodingerScenario() { string json = File.ReadAllText("./s-z/ZigZag/data.schrodinger.json"); - IOrderedEnumerable h = JsonConvert + IReadOnlyList h = JsonConvert .DeserializeObject>(json) - .OrderBy(x => x.Timestamp); + .OrderBy(x => x.Timestamp) + .ToList(); - IReadOnlyList r1 = h.GetZigZag(EndType.Close, 0.25m).ToList(); + IReadOnlyList r1 = h.ToZigZag(EndType.Close, 0.25m).ToList(); Assert.AreEqual(342, r1.Count); // first period has High/Low that exceeds threhold // where it is both a H and L pivot simultaenously - IReadOnlyList r2 = h.GetZigZag(EndType.HighLow, 3).ToList(); + IReadOnlyList r2 = h.ToZigZag(EndType.HighLow, 3).ToList(); Assert.AreEqual(342, r2.Count); } @@ -210,10 +213,10 @@ public void Exceptions() { // bad lookback period Assert.ThrowsException(() - => Quotes.GetZigZag(EndType.Close, 0)); + => Quotes.ToZigZag(EndType.Close, 0)); // bad end type Assert.ThrowsException(() - => Quotes.GetZigZag((EndType)int.MaxValue, 2)); + => Quotes.ToZigZag((EndType)int.MaxValue, 2)); } } diff --git a/tests/other/Convergence.Tests.cs b/tests/other/Convergence.Tests.cs index c47c7dd16..541bb7cca 100644 --- a/tests/other/Convergence.Tests.cs +++ b/tests/other/Convergence.Tests.cs @@ -11,11 +11,11 @@ public void Adx() { foreach (int qty in QuotesQuantities) { - IEnumerable qts = Data.GetLongish(qty); - IEnumerable r = qts.GetAdx(); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToAdx(); - AdxResult l = r.LastOrDefault(); - Console.WriteLine($"ADX(14) on {l.Timestamp:d} with {qts.Count(),4} historical qts: {l.Adx:N8}"); + AdxResult l = r[^1]; + Console.WriteLine($"ADX(14) on {l.Timestamp:d} with {qts.Count,4} historical qts: {l.Adx:N8}"); } } @@ -24,13 +24,13 @@ public void Alligator() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = Data.GetLongish(qty); - IEnumerable r = quotes.GetAlligator(); + IReadOnlyList quotes = Data.GetLongish(qty); + IReadOnlyList r = quotes.ToAlligator(); - AlligatorResult l = r.LastOrDefault(); + AlligatorResult l = r[^1]; Console.WriteLine( "ALLIGATOR(13,8,5) on {0:d} with {1,4} periods: Jaw {2:N8}", - l.Timestamp, quotes.Count(), l.Jaw); + l.Timestamp, quotes.Count, l.Jaw); } } @@ -39,11 +39,11 @@ public void Atr() { foreach (int qty in QuotesQuantities) { - IEnumerable qts = Data.GetLongish(qty); - IEnumerable r = qts.GetAtr(); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToAtr(); - AtrResult l = r.LastOrDefault(); - Console.WriteLine($"ATR(14) on {l.Timestamp:d} with {qts.Count(),4} periods: {l.Atr:N8}"); + AtrResult l = r[^1]; + Console.WriteLine($"ATR(14) on {l.Timestamp:d} with {qts.Count,4} periods: {l.Atr:N8}"); } } @@ -52,11 +52,11 @@ public void ChaikinOsc() { foreach (int qty in QuotesQuantities) { - IEnumerable qts = Data.GetLongish(qty); - IEnumerable r = qts.GetChaikinOsc(); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToChaikinOsc(); - ChaikinOscResult l = r.LastOrDefault(); - Console.WriteLine($"CHAIKIN OSC on {l.Timestamp:d} with {qts.Count(),4} periods: {l.Oscillator:N8}"); + ChaikinOscResult l = r[^1]; + Console.WriteLine($"CHAIKIN OSC on {l.Timestamp:d} with {qts.Count,4} periods: {l.Oscillator:N8}"); } } @@ -65,11 +65,11 @@ public void ConnorsRsi() { foreach (int qty in QuotesQuantities) { - IEnumerable qts = Data.GetLongish(qty); - IEnumerable r = qts.GetConnorsRsi(3, 2, 10); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToConnorsRsi(3, 2, 10); - ConnorsRsiResult l = r.LastOrDefault(); - Console.WriteLine($"CRSI on {l.Timestamp:d} with {qts.Count(),4} periods: {l.ConnorsRsi:N8}"); + ConnorsRsiResult l = r[^1]; + Console.WriteLine($"CRSI on {l.Timestamp:d} with {qts.Count,4} periods: {l.ConnorsRsi:N8}"); } } @@ -78,11 +78,11 @@ public void Dema() { foreach (int qty in QuotesQuantities) { - IEnumerable qts = Data.GetLongish(qty); - IEnumerable r = qts.GetDema(15); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToDema(15); - DemaResult l = r.LastOrDefault(); - Console.WriteLine($"DEMA(15) on {l.Timestamp:d} with {qts.Count(),4} periods: {l.Dema:N8}"); + DemaResult l = r[^1]; + Console.WriteLine($"DEMA(15) on {l.Timestamp:d} with {qts.Count,4} periods: {l.Dema:N8}"); } } @@ -91,11 +91,11 @@ public void Dynamic() { foreach (int qty in QuotesQuantities) { - IEnumerable qts = Data.GetLongish(qty); - IEnumerable r = qts.GetDynamic(100); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToDynamic(100); - DynamicResult l = r.LastOrDefault(); - Console.WriteLine($"DYNAMIC(15) on {l.Timestamp:d} with {qts.Count(),4} periods: {l.Dynamic:N8}"); + DynamicResult l = r[^1]; + Console.WriteLine($"DYNAMIC(15) on {l.Timestamp:d} with {qts.Count,4} periods: {l.Dynamic:N8}"); } } @@ -104,11 +104,11 @@ public void Ema() { foreach (int qty in QuotesQuantities) { - IEnumerable qts = Data.GetLongish(qty); - IEnumerable r = qts.ToEma(15); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToEma(15); - EmaResult l = r.LastOrDefault(); - Console.WriteLine($"EMA(15) on {l.Timestamp:d} with {qts.Count(),4} periods: {l.Ema:N8}"); + EmaResult l = r[^1]; + Console.WriteLine($"EMA(15) on {l.Timestamp:d} with {qts.Count,4} periods: {l.Ema:N8}"); } } @@ -117,11 +117,11 @@ public void FisherTransform() { foreach (int qty in QuotesQuantities) { - IEnumerable qts = Data.GetLongish(qty); - IEnumerable r = qts.GetFisherTransform(); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToFisherTransform(); - FisherTransformResult l = r.LastOrDefault(); - Console.WriteLine($"FT(10) on {l.Timestamp:d} with {qts.Count(),4} periods: {l.Fisher:N8}"); + FisherTransformResult l = r[^1]; + Console.WriteLine($"FT(10) on {l.Timestamp:d} with {qts.Count,4} periods: {l.Fisher:N8}"); } } @@ -130,13 +130,13 @@ public void Gator() { foreach (int qty in QuotesQuantities) { - IEnumerable quotes = Data.GetLongish(qty); - IEnumerable r = quotes.GetGator(); + IReadOnlyList quotes = Data.GetLongish(qty); + IReadOnlyList r = quotes.ToGator(); - GatorResult l = r.LastOrDefault(); + GatorResult l = r[^1]; Console.WriteLine( "GATOR() on {0:d} with {1,4} periods: Upper {2:N8} Lower {3:N8}", - l.Timestamp, quotes.Count(), l.Upper, l.Lower); + l.Timestamp, quotes.Count, l.Upper, l.Lower); } } @@ -145,11 +145,11 @@ public void HtTrendline() { foreach (int qty in QuotesQuantities) { - IEnumerable qts = Data.GetLongish(qty); - IEnumerable r = qts.GetHtTrendline(); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToHtTrendline(); - HtlResult l = r.LastOrDefault(); - Console.WriteLine($"HTL on {l.Timestamp:d} with {qts.Count(),4} periods: {l.Trendline:N8}"); + HtlResult l = r[^1]; + Console.WriteLine($"HTL on {l.Timestamp:d} with {qts.Count,4} periods: {l.Trendline:N8}"); } } @@ -158,11 +158,11 @@ public void Kama() { foreach (int qty in QuotesQuantities) { - IEnumerable qts = Data.GetLongish(qty); - IEnumerable r = qts.GetKama(); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToKama(); - KamaResult l = r.LastOrDefault(); - Console.WriteLine($"KAMA(10) on {l.Timestamp:d} with {qts.Count(),4} periods: {l.Kama:N8}"); + KamaResult l = r[^1]; + Console.WriteLine($"KAMA(10) on {l.Timestamp:d} with {qts.Count,4} periods: {l.Kama:N8}"); } } @@ -171,11 +171,11 @@ public void Keltner() { foreach (int qty in QuotesQuantities) { - IEnumerable qts = Data.GetLongish(qty); - IEnumerable r = qts.GetKeltner(100); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToKeltner(100); - KeltnerResult l = r.LastOrDefault(); - Console.WriteLine($"KC-UP on {l.Timestamp:d} with {qts.Count(),4} periods: {l.UpperBand:N8}"); + KeltnerResult l = r[^1]; + Console.WriteLine($"KC-UP on {l.Timestamp:d} with {qts.Count,4} periods: {l.UpperBand:N8}"); } } @@ -184,11 +184,11 @@ public void Macd() { foreach (int qty in QuotesQuantities) { - IEnumerable qts = Data.GetLongish(15 + qty); - IEnumerable r = qts.GetMacd(); + IReadOnlyList qts = Data.GetLongish(15 + qty); + IReadOnlyList r = qts.ToMacd(); - MacdResult l = r.LastOrDefault(); - Console.WriteLine($"MACD on {l.Timestamp:d} with {qts.Count(),4} periods: {l.Macd:N8}"); + MacdResult l = r[^1]; + Console.WriteLine($"MACD on {l.Timestamp:d} with {qts.Count,4} periods: {l.Macd:N8}"); } } @@ -197,11 +197,11 @@ public void Mama() { foreach (int qty in QuotesQuantities) { - IEnumerable qts = Data.GetLongish(qty); - IEnumerable r = qts.GetMama(); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToMama(); - MamaResult l = r.LastOrDefault(); - Console.WriteLine($"MAMA on {l.Timestamp:d} with {qts.Count(),4} periods: {l.Mama:N8}"); + MamaResult l = r[^1]; + Console.WriteLine($"MAMA on {l.Timestamp:d} with {qts.Count,4} periods: {l.Mama:N8}"); } } @@ -210,11 +210,11 @@ public void Pmo() { foreach (int qty in QuotesQuantities) { - IEnumerable qts = Data.GetLongish(qty); - IEnumerable r = qts.GetPmo(); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToPmo(); - PmoResult l = r.LastOrDefault(); - Console.WriteLine($"PMO on {l.Timestamp:d} with {qts.Count(),4} periods: {l.Pmo:N8}"); + PmoResult l = r[^1]; + Console.WriteLine($"PMO on {l.Timestamp:d} with {qts.Count,4} periods: {l.Pmo:N8}"); } } @@ -223,11 +223,11 @@ public void Pvo() { foreach (int qty in QuotesQuantities) { - IEnumerable qts = Data.GetLongish(qty); - IEnumerable r = qts.GetPvo(); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToPvo(); - PvoResult l = r.LastOrDefault(); - Console.WriteLine($"PVO on {l.Timestamp:d} with {qts.Count(),4} periods: {l.Pvo:N8}"); + PvoResult l = r[^1]; + Console.WriteLine($"PVO on {l.Timestamp:d} with {qts.Count,4} periods: {l.Pvo:N8}"); } } @@ -236,11 +236,11 @@ public void Rsi() { foreach (int qty in QuotesQuantities) { - IEnumerable qts = Data.GetLongish(qty); - IEnumerable r = qts.GetRsi(); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToRsi(); - RsiResult l = r.LastOrDefault(); - Console.WriteLine($"RSI(14) on {l.Timestamp:d} with {qts.Count(),4} periods: {l.Rsi:N8}"); + RsiResult l = r[^1]; + Console.WriteLine($"RSI(14) on {l.Timestamp:d} with {qts.Count,4} periods: {l.Rsi:N8}"); } } @@ -249,11 +249,11 @@ public void Smi() { foreach (int qty in QuotesQuantities) { - IEnumerable qts = Data.GetDefault(qty); - IEnumerable r = qts.GetSmi(14, 20, 5); + IReadOnlyList qts = Data.GetDefault(qty); + IReadOnlyList r = qts.ToSmi(14, 20, 5); - SmiResult l = r.LastOrDefault(); - Console.WriteLine($"SMI on {l.Timestamp:d} with {qts.Count(),4} periods: {l.Smi:N8}"); + SmiResult l = r[^1]; + Console.WriteLine($"SMI on {l.Timestamp:d} with {qts.Count,4} periods: {l.Smi:N8}"); } } @@ -262,11 +262,11 @@ public void Smma() { foreach (int qty in QuotesQuantities) { - IEnumerable qts = Data.GetLongish(qty); - IEnumerable r = qts.GetSmma(15); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToSmma(15); - SmmaResult l = r.LastOrDefault(); - Console.WriteLine($"SMMA(15) on {l.Timestamp:d} with {qts.Count(),4} periods: {l.Smma:N8}"); + SmmaResult l = r[^1]; + Console.WriteLine($"SMMA(15) on {l.Timestamp:d} with {qts.Count,4} periods: {l.Smma:N8}"); } } @@ -275,11 +275,11 @@ public void StarcBands() { foreach (int qty in QuotesQuantities) { - IEnumerable qts = Data.GetLongish(qty); - IEnumerable r = qts.GetStarcBands(20); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToStarcBands(20); - StarcBandsResult l = r.LastOrDefault(); - Console.WriteLine($"STARC UPPER on {l.Timestamp:d} with {qts.Count(),4} periods: {l.UpperBand:N8}"); + StarcBandsResult l = r[^1]; + Console.WriteLine($"STARC UPPER on {l.Timestamp:d} with {qts.Count,4} periods: {l.UpperBand:N8}"); } } @@ -288,11 +288,11 @@ public void StochRsi() { foreach (int qty in QuotesQuantities.Where(x => x <= 502)) { - IEnumerable qts = Data.GetDefault(qty); - IEnumerable r = qts.GetStochRsi(14, 14, 3); + IReadOnlyList qts = Data.GetDefault(qty); + IReadOnlyList r = qts.ToStochRsi(14, 14, 3); - StochRsiResult l = r.LastOrDefault(); - Console.WriteLine($"SRSI on {l.Timestamp:d} with {qts.Count(),4} periods: {l.StochRsi:N8}"); + StochRsiResult l = r[^1]; + Console.WriteLine($"SRSI on {l.Timestamp:d} with {qts.Count,4} periods: {l.StochRsi:N8}"); } } @@ -301,11 +301,11 @@ public void T3() { foreach (int qty in QuotesQuantities) { - IEnumerable qts = Data.GetLongish(qty); - IEnumerable r = qts.GetT3(20); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToT3(20); - T3Result l = r.LastOrDefault(); - Console.WriteLine($"T3 on {l.Timestamp:d} with {qts.Count(),4} periods: {l.T3:N8}"); + T3Result l = r[^1]; + Console.WriteLine($"T3 on {l.Timestamp:d} with {qts.Count,4} periods: {l.T3:N8}"); } } @@ -314,11 +314,11 @@ public void Tema() { foreach (int qty in QuotesQuantities) { - IEnumerable qts = Data.GetLongish(qty); - IEnumerable r = qts.GetTema(15); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToTema(15); - TemaResult l = r.LastOrDefault(); - Console.WriteLine($"TEMA on {l.Timestamp:d} with {qts.Count(),4} periods: {l.Tema:N8}"); + TemaResult l = r[^1]; + Console.WriteLine($"TEMA on {l.Timestamp:d} with {qts.Count,4} periods: {l.Tema:N8}"); } } @@ -327,11 +327,11 @@ public void Trix() { foreach (int qty in QuotesQuantities) { - IEnumerable qts = Data.GetLongish(qty); - IEnumerable r = qts.GetTrix(15); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToTrix(15); - TrixResult l = r.LastOrDefault(); - Console.WriteLine($"TRIX on {l.Timestamp:d} with {qts.Count(),4} periods: {l.Trix:N8}"); + TrixResult l = r[^1]; + Console.WriteLine($"TRIX on {l.Timestamp:d} with {qts.Count,4} periods: {l.Trix:N8}"); } } @@ -340,11 +340,11 @@ public void Tsi() { foreach (int qty in QuotesQuantities) { - IEnumerable qts = Data.GetLongish(20 + qty); - IEnumerable r = qts.GetTsi(); + IReadOnlyList qts = Data.GetLongish(20 + qty); + IReadOnlyList r = qts.ToTsi(); - TsiResult l = r.LastOrDefault(); - Console.WriteLine($"TSI on {l.Timestamp:d} with {qts.Count(),4} periods: {l.Tsi:N8}"); + TsiResult l = r[^1]; + Console.WriteLine($"TSI on {l.Timestamp:d} with {qts.Count,4} periods: {l.Tsi:N8}"); } } @@ -353,11 +353,11 @@ public void Vortex() { foreach (int qty in QuotesQuantities) { - IEnumerable qts = Data.GetLongish(qty); - IEnumerable r = qts.GetVortex(14); + IReadOnlyList qts = Data.GetLongish(qty); + IReadOnlyList r = qts.ToVortex(14); - VortexResult l = r.LastOrDefault(); - Console.WriteLine($"VI+ on {l.Timestamp:d} with {qts.Count(),4} periods: {l.Pvi:N8}"); + VortexResult l = r[^1]; + Console.WriteLine($"VI+ on {l.Timestamp:d} with {qts.Count,4} periods: {l.Pvi:N8}"); } } } diff --git a/tests/other/Custom.Indicator.Tests.cs b/tests/other/Custom.Indicator.Tests.cs index 261064fe7..133d4d488 100644 --- a/tests/other/Custom.Indicator.Tests.cs +++ b/tests/other/Custom.Indicator.Tests.cs @@ -90,9 +90,9 @@ public void Chainor() [TestMethod] public void QuoteToSortedList() { - IEnumerable mismatch = Data.GetMismatch(); + IReadOnlyList mismatch = Data.GetMismatch(); - Collection h = mismatch.ToSortedCollection(); + IReadOnlyList h = mismatch.ToSortedList(); // proper quantities Assert.AreEqual(502, h.Count); @@ -103,7 +103,7 @@ public void QuoteToSortedList() // check last date DateTime lastDate = DateTime.ParseExact("12/31/2018", "MM/dd/yyyy", EnglishCulture); - Assert.AreEqual(lastDate, h.LastOrDefault().Timestamp); + Assert.AreEqual(lastDate, h[^1].Timestamp); // spot check an out of sequence date DateTime spotDate = DateTime.ParseExact("03/16/2017", "MM/dd/yyyy", EnglishCulture); diff --git a/tests/other/Custom.Quotes.Tests.cs b/tests/other/Custom.Quotes.Tests.cs index 4a2aeaa06..3b50b542d 100644 --- a/tests/other/Custom.Quotes.Tests.cs +++ b/tests/other/Custom.Quotes.Tests.cs @@ -33,8 +33,7 @@ public void CustomQuoteSeries() }) .ToList(); - IReadOnlyList results = myGenericHistory - .ToEma(20); + IReadOnlyList results = Ema.ToEma(myGenericHistory, 20); // proper quantities Assert.AreEqual(502, results.Count); @@ -160,8 +159,7 @@ public void CustomQuoteInheritedSeries() MyOtherProperty: 123456)) .ToList(); - IReadOnlyList results = myGenericHistory - .ToEma(20); + IReadOnlyList results = myGenericHistory.ToEma(20); // proper quantities Assert.AreEqual(502, results.Count); diff --git a/tests/other/Custom.Results.Tests.cs b/tests/other/Custom.Results.Tests.cs index a45bd6264..d7b4c7f60 100644 --- a/tests/other/Custom.Results.Tests.cs +++ b/tests/other/Custom.Results.Tests.cs @@ -29,7 +29,7 @@ public void CustomSeriesClass() [TestMethod] public void CustomSeriesClassLinq() { - IEnumerable emaResults = quotes.ToEma(14); + IReadOnlyList emaResults = quotes.ToEma(14); // can use a derive Indicator class using Linq diff --git a/tests/other/PublicApi.Interface.Tests.cs b/tests/other/PublicApi.Interface.Tests.cs index 0ec736e34..9dad62942 100644 --- a/tests/other/PublicApi.Interface.Tests.cs +++ b/tests/other/PublicApi.Interface.Tests.cs @@ -1,4 +1,6 @@ [assembly: CLSCompliant(true)] +[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)] + namespace PublicApi; // PUBLIC API (INTERFACES) @@ -7,15 +9,34 @@ namespace PublicApi; public class UserInterface { private static readonly IReadOnlyList quotes = Data.GetDefault(); + private static readonly IReadOnlyList quotesBad = Data.GetBad(); [TestMethod] public void QuoteValidation() { - IEnumerable enumerable = quotes; + IReadOnlyList clean = quotes; + + clean.Validate(); + clean.ToSma(6); + clean.ToEma(5); + + IReadOnlyList reverse = quotes + .OrderByDescending(x => x.Timestamp) + .ToList(); + + // has duplicates + InvalidQuotesException dx + = Assert.ThrowsException( + () => quotesBad.Validate()); + + dx.Message.Should().Contain("Duplicate date found"); + + // out of order + InvalidQuotesException sx + = Assert.ThrowsException( + () => reverse.Validate()); - enumerable.Validate(); - enumerable.GetSma(6); - enumerable.ToEma(5); + sx.Message.Should().Contain("Quotes are out of sequence"); } [TestMethod] @@ -78,20 +99,20 @@ public void StreamMany() // from quote provider } // late arrival - provider.Add(quotes[80]); + provider.Insert(quotes[80]); // end all observations provider.EndTransmission(); // get static equivalents for comparison - IEnumerable staticAdl = quotes.GetAdl(); - IReadOnlyList staticAtr = quotes.GetAtr(); - IReadOnlyList staticAtrStop = quotes.GetAtrStop(); - IEnumerable staticAlligator = quotes.GetAlligator(); - IEnumerable staticEma = quotes.ToEma(20); - IEnumerable staticQuotePart = quotes.Use(CandlePart.OHL3); - IEnumerable staticSma = quotes.GetSma(20); - IReadOnlyList staticTr = quotes.GetTr(); + IReadOnlyList staticAdl = quotes.ToAdl(); + IReadOnlyList staticAtr = quotes.ToAtr(); + IReadOnlyList staticAtrStop = quotes.ToAtrStop(); + IReadOnlyList staticAlligator = quotes.ToAlligator(); + IReadOnlyList staticEma = quotes.ToEma(20); + IReadOnlyList staticQuotePart = quotes.Use(CandlePart.OHL3); + IReadOnlyList staticSma = quotes.ToSma(20); + IReadOnlyList staticTr = quotes.ToTr(); // final results should persist in scope IReadOnlyList streamAdl = adlHub.Results; diff --git a/tests/other/Sut.CustomItems.cs b/tests/other/Sut.CustomItems.cs index 2761f64db..c070ca3ab 100644 --- a/tests/other/Sut.CustomItems.cs +++ b/tests/other/Sut.CustomItems.cs @@ -51,9 +51,9 @@ double IReusable.Value public sealed record CustomReusableInherited( DateTime Timestamp, double? Sma - ) : Reusable(Timestamp) + ) : IReusable { - public override double Value + public double Value => Sma.Null2NaN(); } @@ -80,15 +80,15 @@ public static class CustomIndicator { // SERIES, from CHAIN public static IReadOnlyList GetIndicator( - this IEnumerable source, + this IReadOnlyList source, int lookbackPeriods) where T : IReusable => source - .ToSortedCollection() + .ToSortedList() .CalcIndicator(lookbackPeriods); private static List CalcIndicator( - this Collection source, + this IReadOnlyList source, int lookbackPeriods) where T : IReusable { diff --git a/tests/performance/Perf.Increments.cs b/tests/performance/Perf.Increments.cs index 91833f894..cb4ffdef1 100644 --- a/tests/performance/Perf.Increments.cs +++ b/tests/performance/Perf.Increments.cs @@ -5,8 +5,14 @@ namespace Performance; [ShortRunJob] public class Incrementals { - private static readonly IReadOnlyList quotes = Data.GetDefault(); - private static readonly double[] values = Data.GetDefault().Select(x => x.Value).ToArray(); + private static readonly IReadOnlyList quotes + = Data.GetDefault(); + + private static readonly IReadOnlyList reusables + = quotes + .Cast() + .ToList(); + private readonly QuoteHub provider = new(); [GlobalSetup] @@ -16,51 +22,74 @@ public class Incrementals public void Cleanup() { provider.EndTransmission(); - provider.ClearCache(); + provider.Cache.Clear(); } [Benchmark] - public object EmaListQ() + public object EmaIncRusBatch() { - EmaList sut = new(14); + EmaList sut = new(14) { reusables }; + return sut; + } - for (int i = 0; i < quotes.Count; i++) + [Benchmark] + public object EmaIncRusItem() + { + EmaList sut = new(14); + + for (int i = 0; i < reusables.Count; i++) { - sut.Add(quotes[i]); + sut.Add(reusables[i]); } return sut; } [Benchmark] - public object EmaListP() + public object EmaIncRusSplit() { - EmaList sut = new(14); + EmaList sut = new(14); - for (int i = 0; i < quotes.Count; i++) + for (int i = 0; i < reusables.Count; i++) { - sut.Add(quotes[i].Timestamp, quotes[i].Value); + sut.Add(reusables[i].Timestamp, reusables[i].Value); } return sut; } [Benchmark] - public object EmaArray() + public object EmaIncQotBatch() { - EmaArray sut = new(14); + EmaList sut = new(14) { quotes }; + return sut; + } - for (int i = 0; i < values.Length; i++) + [Benchmark] + public object EmaIncQot() + { + EmaList sut = new(14); + + for (int i = 0; i < quotes.Count; i++) { - sut.Add(values[i]); + sut.Add(quotes[i]); } return sut; } + // TIME-SERIES EQUIVALENTS + [Benchmark] public object EmaSeries() => quotes.ToEma(14); - // [Benchmark] - // public object EmaStream() => provider.ToEma(14).Results; + [Benchmark] + public object EmaIncrem() + { + EmaList ema = new(14) { quotes }; + return ema; + } + + [Benchmark] + public object EmaStream() => provider.ToEma(14).Results; } diff --git a/tests/performance/Perf.StaticSeries.cs b/tests/performance/Perf.StaticSeries.cs index ac86db83a..6892ef6e4 100644 --- a/tests/performance/Perf.StaticSeries.cs +++ b/tests/performance/Perf.StaticSeries.cs @@ -9,269 +9,269 @@ public class SeriesIndicators private static readonly IReadOnlyList o = Data.GetCompare(); [Benchmark] - public object GetAdl() => q.GetAdl(); + public object ToAdl() => q.ToAdl(); [Benchmark] - public object GetAdx() => q.GetAdx(); + public object ToAdx() => q.ToAdx(); [Benchmark] - public object GetAlligator() => q.GetAlligator(); + public object ToAlligator() => q.ToAlligator(); [Benchmark] - public object GetAlma() => q.GetAlma(); + public object ToAlma() => q.ToAlma(); [Benchmark] - public object GetAroon() => q.GetAroon(); + public object ToAroon() => q.ToAroon(); [Benchmark] - public object GetAtr() => q.GetAtr(); + public object ToAtr() => q.ToAtr(); [Benchmark] - public object GetAtrStop() => q.GetAtrStop(); + public object ToAtrStop() => q.ToAtrStop(); [Benchmark] - public object GetAwesome() => q.GetAwesome(); + public object ToAwesome() => q.ToAwesome(); [Benchmark] - public object GetBeta() => Indicator.GetBeta(q, o, 20, BetaType.Standard); + public object ToBeta() => Beta.ToBeta(q, o, 20, BetaType.Standard); [Benchmark] - public object GetBetaUp() => Indicator.GetBeta(q, o, 20, BetaType.Up); + public object ToBetaUp() => Beta.ToBeta(q, o, 20, BetaType.Up); [Benchmark] - public object GetBetaDown() => Indicator.GetBeta(q, o, 20, BetaType.Down); + public object ToBetaDown() => Beta.ToBeta(q, o, 20, BetaType.Down); [Benchmark] - public object GetBetaAll() => Indicator.GetBeta(q, o, 20, BetaType.All); + public object ToBetaAll() => Beta.ToBeta(q, o, 20, BetaType.All); [Benchmark] - public object GetBollingerBands() => q.GetBollingerBands(); + public object ToBollingerBands() => q.ToBollingerBands(); [Benchmark] - public object GetBop() => q.GetBop(); + public object ToBop() => q.ToBop(); [Benchmark] - public object GetCci() => q.GetCci(); + public object ToCci() => q.ToCci(); [Benchmark] - public object GetChaikinOsc() => q.GetChaikinOsc(); + public object ToChaikinOsc() => q.ToChaikinOsc(); [Benchmark] - public object GetChandelier() => q.GetChandelier(); + public object ToChandelier() => q.ToChandelier(); [Benchmark] - public object GetChop() => q.GetChop(); + public object ToChop() => q.ToChop(); [Benchmark] - public object GetCmf() => q.GetCmf(); + public object ToCmf() => q.ToCmf(); [Benchmark] - public object GetCmo() => q.GetCmo(14); + public object ToCmo() => q.ToCmo(14); [Benchmark] - public object GetConnorsRsi() => q.GetConnorsRsi(); + public object ToConnorsRsi() => q.ToConnorsRsi(); [Benchmark] - public object GetCorrelation() => q.GetCorrelation(o, 20); + public object ToCorrelation() => q.ToCorrelation(o, 20); [Benchmark] - public object GetDema() => q.GetDema(14); + public object ToDema() => q.ToDema(14); [Benchmark] - public object GetDoji() => q.GetDoji(); + public object ToDoji() => q.ToDoji(); [Benchmark] - public object GetDonchian() => q.GetDonchian(); + public object ToDonchian() => q.ToDonchian(); [Benchmark] - public object GetDpo() => q.GetDpo(14); + public object ToDpo() => q.ToDpo(14); [Benchmark] - public object GetDynamic() => q.GetDynamic(20); + public object ToDynamic() => q.ToDynamic(20); [Benchmark] - public object GetElderRay() => q.GetElderRay(); + public object ToElderRay() => q.ToElderRay(); [Benchmark] - public object GetEma() => q.ToEma(14); + public object ToEma() => q.ToEma(14); [Benchmark] - public object GetEpma() => q.GetEpma(14); + public object ToEpma() => q.ToEpma(14); [Benchmark] - public object GetFcb() => q.GetFcb(14); + public object ToFcb() => q.ToFcb(14); [Benchmark] - public object GetFisherTransform() => q.GetFisherTransform(10); + public object ToFisherTransform() => q.ToFisherTransform(10); [Benchmark] - public object GetForceIndex() => q.GetForceIndex(13); + public object ToForceIndex() => q.ToForceIndex(13); [Benchmark] - public object GetFractal() => q.GetFractal(); + public object ToFractal() => q.ToFractal(); [Benchmark] - public object GetGator() => q.GetGator(); + public object ToGator() => q.ToGator(); [Benchmark] - public object GetHeikinAshi() => q.GetHeikinAshi(); + public object ToHeikinAshi() => q.ToHeikinAshi(); [Benchmark] - public object GetHma() => q.GetHma(14); + public object ToHma() => q.ToHma(14); [Benchmark] - public object GetHtTrendline() => q.GetHtTrendline(); + public object ToHtTrendline() => q.ToHtTrendline(); [Benchmark] - public object GetHurst() => q.GetHurst(); + public object ToHurst() => q.ToHurst(); [Benchmark] - public object GetIchimoku() => q.GetIchimoku(); + public object ToIchimoku() => q.ToIchimoku(); [Benchmark] - public object GetKama() => q.GetKama(); + public object ToKama() => q.ToKama(); [Benchmark] - public object GetKlinger() => q.GetKvo(); + public object ToKlinger() => q.ToKvo(); [Benchmark] - public object GetKeltner() => q.GetKeltner(); + public object ToKeltner() => q.ToKeltner(); [Benchmark] - public object GetKvo() => q.GetKvo(); + public object ToKvo() => q.ToKvo(); [Benchmark] - public object GetMacd() => q.GetMacd(); + public object ToMacd() => q.ToMacd(); [Benchmark] - public object GetMaEnvelopes() => q.GetMaEnvelopes(20, 2.5, MaType.SMA); + public object ToMaEnvelopes() => q.ToMaEnvelopes(20, 2.5, MaType.SMA); [Benchmark] - public object GetMama() => q.GetMama(); + public object ToMama() => q.ToMama(); [Benchmark] - public object GetMarubozu() => q.GetMarubozu(); + public object ToMarubozu() => q.ToMarubozu(); [Benchmark] - public object GetMfi() => q.GetMfi(); + public object ToMfi() => q.ToMfi(); [Benchmark] - public object GetObv() => q.GetObv(); + public object ToObv() => q.ToObv(); [Benchmark] - public object GetParabolicSar() => q.GetParabolicSar(); + public object ToParabolicSar() => q.ToParabolicSar(); [Benchmark] - public object GetPivotPoints() => q.GetPivotPoints(PeriodSize.Month, PivotPointType.Standard); + public object ToPivotPoints() => q.ToPivotPoints(PeriodSize.Month, PivotPointType.Standard); [Benchmark] - public object GetPivots() => q.GetPivots(); + public object ToPivots() => q.ToPivots(); [Benchmark] - public object GetPmo() => q.GetPmo(); + public object ToPmo() => q.ToPmo(); [Benchmark] - public object GetPrs() => q.GetPrs(o); + public object ToPrs() => q.ToPrs(o); [Benchmark] - public object GetPvo() => q.GetPvo(); + public object ToPvo() => q.ToPvo(); [Benchmark] - public object GetRenko() => q.GetRenko(2.5m); + public object ToRenko() => q.ToRenko(2.5m); [Benchmark] - public object GetRenkoAtr() => q.GetRenko(14); + public object ToRenkoAtr() => q.ToRenko(14); [Benchmark] - public object GetRoc() => q.GetRoc(20); + public object ToRoc() => q.ToRoc(20); [Benchmark] - public object GetRocWb() => q.GetRocWb(12, 3, 12); + public object ToRocWb() => q.ToRocWb(12, 3, 12); [Benchmark] - public object GetRollingPivots() => q.GetRollingPivots(14, 1); + public object ToRollingPivots() => q.ToRollingPivots(14, 1); [Benchmark] - public object GetRsi() => q.GetRsi(); + public object ToRsi() => q.ToRsi(); [Benchmark] - public object GetSlope() => q.GetSlope(20); + public object ToSlope() => q.ToSlope(20); [Benchmark] - public object GetSma() => q.GetSma(10); + public object ToSma() => q.ToSma(10); [Benchmark] - public object GetSmaAnalysis() => q.GetSmaAnalysis(10); + public object ToSmaAnalysis() => q.ToSmaAnalysis(10); [Benchmark] - public object GetSmi() => q.GetSmi(5, 20, 5, 3); + public object ToSmi() => q.ToSmi(5, 20, 5, 3); [Benchmark] - public object GetSmma() => q.GetSmma(10); + public object ToSmma() => q.ToSmma(10); [Benchmark] - public object GetStarcBands() => q.GetStarcBands(10); + public object ToStarcBands() => q.ToStarcBands(10); [Benchmark] - public object GetStc() => q.GetStc(); + public object ToStc() => q.ToStc(); [Benchmark] - public object GetStdDev() => q.GetStdDev(20); + public object ToStdDev() => q.ToStdDev(20); [Benchmark] - public object GetStdDevChannels() => q.GetStdDevChannels(); + public object ToStdDevChannels() => q.ToStdDevChannels(); [Benchmark] - public object GetStoch() => q.GetStoch(); + public object ToStoch() => q.ToStoch(); [Benchmark] - public object GetStochSMMA() => q.GetStoch(9, 3, 3, 3, 2, MaType.SMMA); + public object ToStochSMMA() => q.ToStoch(9, 3, 3, 3, 2, MaType.SMMA); [Benchmark] - public object GetStochRsi() => q.GetStochRsi(14, 14, 3); + public object ToStochRsi() => q.ToStochRsi(14, 14, 3); [Benchmark] - public object GetSuperTrend() => q.GetSuperTrend(); + public object ToSuperTrend() => q.ToSuperTrend(); [Benchmark] - public object GetT3() => q.GetT3(); + public object ToT3() => q.ToT3(); [Benchmark] - public object GetTema() => q.GetTema(14); + public object ToTema() => q.ToTema(14); [Benchmark] - public object GetTr() => q.GetTr(); + public object ToTr() => q.ToTr(); [Benchmark] - public object GetTrix() => q.GetTrix(14); + public object ToTrix() => q.ToTrix(14); [Benchmark] - public object GetTsi() => q.GetTsi(); + public object ToTsi() => q.ToTsi(); [Benchmark] - public object GetUlcerIndex() => q.GetUlcerIndex(); + public object ToUlcerIndex() => q.ToUlcerIndex(); [Benchmark] - public object GetUltimate() => q.GetUltimate(); + public object ToUltimate() => q.ToUltimate(); [Benchmark] - public object GetVolatilityStop() => q.GetVolatilityStop(); + public object ToVolatilityStop() => q.ToVolatilityStop(); [Benchmark] - public object GetVortex() => q.GetVortex(14); + public object ToVortex() => q.ToVortex(14); [Benchmark] - public object GetVwap() => q.GetVwap(); + public object ToVwap() => q.ToVwap(); [Benchmark] - public object GetVwma() => q.GetVwma(14); + public object ToVwma() => q.ToVwma(14); [Benchmark] - public object GetWilliamsR() => q.GetWilliamsR(); + public object ToWilliamsR() => q.ToWilliamsR(); [Benchmark] - public object GetWma() => q.GetWma(14); + public object ToWma() => q.ToWma(14); [Benchmark] - public object GetZigZag() => q.GetZigZag(); + public object ToZigZag() => q.ToZigZag(); } diff --git a/tests/performance/Perf.StreamHub.Externals.cs b/tests/performance/Perf.StreamHub.Externals.cs index aa6706217..3a2dd4eb0 100644 --- a/tests/performance/Perf.StreamHub.Externals.cs +++ b/tests/performance/Perf.StreamHub.Externals.cs @@ -5,7 +5,9 @@ namespace Performance; [ShortRunJob] public class StreamExternal { - private static readonly IReadOnlyList quotes = Data.GetDefault(); + private static readonly IReadOnlyList quotes + = Data.GetDefault(); + private readonly QuoteHub provider = new(); /* SETUP/CLEANUP - runs before and after each. @@ -25,7 +27,7 @@ public class StreamExternal public void Cleanup() { provider.EndTransmission(); - provider.ClearCache(); + provider.Cache.Clear(); } // BENCHMARKS @@ -33,8 +35,8 @@ public void Cleanup() // TODO: replace with external data cache model, when available [Benchmark(Baseline = true)] - public object GetEma() => quotes.ToEma(14); + public object EmaSeries() => quotes.ToEma(14); [Benchmark] - public object EmaHub() => provider.ToEma(14).Results; + public object EmaStream() => provider.ToEma(14).Results; } diff --git a/tests/performance/Perf.StreamHub.cs b/tests/performance/Perf.StreamHub.cs index cc3dffb85..6ad618641 100644 --- a/tests/performance/Perf.StreamHub.cs +++ b/tests/performance/Perf.StreamHub.cs @@ -5,8 +5,11 @@ namespace Performance; [ShortRunJob] public class StreamIndicators { - private static readonly IReadOnlyList quotes = Data.GetDefault(); - private readonly QuoteHub provider = new(); + private static readonly IReadOnlyList quotes + = Data.GetDefault(); + + private readonly QuoteHub provider = new(); // prepopulated + private readonly QuoteHub supplier = new(); // empty /* SETUP/CLEANUP - runs before and after each. * @@ -25,7 +28,7 @@ public class StreamIndicators public void Cleanup() { provider.EndTransmission(); - provider.ClearCache(); + provider.Cache.Clear(); } // BENCHMARKS @@ -45,6 +48,19 @@ public void Cleanup() [Benchmark] public object EmaHub() => provider.ToEma(14).Results; + [Benchmark] + public object EmaHub2() + { + EmaHub observer = supplier.ToEma(14); + + for (int i = 0; i < quotes.Count; i++) + { + observer.OnAdd(quotes[i], notify: false, i); + } + + return observer.Results; + } + [Benchmark] public object QuoteHub() => provider.ToQuote().Results; diff --git a/tests/performance/Perf.Utility.cs b/tests/performance/Perf.Utility.cs index 7dfb9cea5..eb1401be1 100644 --- a/tests/performance/Perf.Utility.cs +++ b/tests/performance/Perf.Utility.cs @@ -11,9 +11,6 @@ public class Utility [Benchmark] public object ToSortedList() => q.ToSortedList(); - [Benchmark] - public object ToSortedCollection() => q.ToSortedCollection(); - [Benchmark] public object ToListQuoteD() => q.ToQuoteDList(); diff --git a/tests/performance/Tests.Performance.csproj b/tests/performance/Tests.Performance.csproj index a610fde50..43bc26589 100644 --- a/tests/performance/Tests.Performance.csproj +++ b/tests/performance/Tests.Performance.csproj @@ -5,6 +5,10 @@ Exe Performance.Program + false + None + true + enable true