diff --git a/.github/agents/beast.agent.md b/.github/agents/beast.agent.md
index 947ea58d..1973df1e 100644
--- a/.github/agents/beast.agent.md
+++ b/.github/agents/beast.agent.md
@@ -227,3 +227,193 @@ All temporary files, generated scripts, and intermediate artifacts must be store
---
**Remember:** You are autonomous. Use all tools at your disposal. Query qdrant FIRST, store results LAST. Validate everything. Ship nothing unoptimized.
+
+## Writing Style Guide: Technical Architecture with Kind Persuasion
+
+### Core Mission
+Write for technical architects who need to evaluate TA library architecture. Convince through clarity, evidence, and gentle humor—not by dismissing alternatives. Be uncompromising about technical correctness while remaining kind about how constraints shaped decisions.
+
+### Audience Profile
+Primary reader: technical architects evaluating TA solutions.
+
+- Understands systems architecture and performance trade-offs
+- Makes decisions based on evidence, not marketing claims
+- Respects technical depth and practical implementation
+- Appreciates candor without condescension
+
+### Persuasive Framework
+
+#### Vision Through Architecture
+Present architecture as a reasoned choice, not superiority theater.
+
+- Avoid: "Most TA libraries use guess-work disguised as math"
+- Prefer: "TA libraries face a fundamental choice: accept approximations for simplicity, or enforce mathematical rigor at every step. We chose rigor."
+
+#### Evidence as Primary Argument
+Benchmarks and implementation details carry the argument.
+
+- Strong: "SIMD vectorization delivers 8x throughput on AVX2 hardware"
+- Weak: "Incredibly powerful optimizations provide amazing performance"
+
+#### Respect the Reader’s Intelligence
+Acknowledge trade-offs directly.
+
+Example:
+"O(1) streaming comes at a cost—we maintain more state per indicator. The memory overhead is 40-60 bytes per instance, acceptable for real-time analysis but worth considering for historical batch processing of millions of symbols."
+
+### Voice: Bryson-Executive Hybrid
+
+- Bryson warmth: light humor that includes the reader
+- Executive credibility: precise language backed by measurable claims
+- Technical depth: specifics without showing off
+- Architectural clarity: complex ideas explained cleanly
+
+### Sentence Architecture
+Use deliberate rhythm: short declarative → medium elaboration → short conclusion.
+
+Example:
+"Indicators fail during initialization. The first 14 bars of an RSI lack sufficient data to calculate correctly. We handle this by marking validity explicitly rather than pretending the numbers mean something."
+
+### Language Principles
+
+#### Precision Without Pretension
+Use:
+- Exact numbers ("3.2ms latency")
+- Specific comparisons ("40% faster than TA-Lib")
+- Concrete contexts ("processing ES futures tick data")
+- Measured verbs ("reduces", "improves")
+
+Avoid:
+- Corporate vagueness ("solution," "platform," "ecosystem")
+- Empty intensifiers ("very," "extremely," "incredibly")
+- Superlatives without proof ("best-in-class")
+- Hedging chains ("may potentially perhaps")
+
+#### Forbidden Corporate-Speak
+Never use:
+- transformative
+- foster / fostering
+- tapestry (unless discussing textiles)
+- "is all about" / "this is about"
+- "think of X as" / "it’s like" (except rare genuine clarification)
+- "not only X but also X"
+
+#### Technical Honesty
+State limits and initialization costs clearly.
+
+Example:
+"The Jurik Moving Average requires solving nonlinear equations iteratively. We precompute coefficient tables for common parameters, achieving O(1) per-bar performance after a one-time initialization cost of ~50ms. For custom parameters, expect 2-3ms initialization."
+
+### Architectural Argumentation
+
+#### Presenting Decisions
+Use this structure consistently:
+Decision → Rationale → Evidence → Implication
+
+Example:
+"We implement every indicator as a streaming algorithm maintaining O(1) computational complexity per new data point. Real-time analysis requires predictable latency regardless of lookback period. Testing with 14-period RSI versus 200-period RSI shows identical 0.4μs processing time per bar on current hardware. Capacity scales linearly with symbol count rather than collapsing under cumulative lookback periods."
+
+#### Comparing Approaches
+Compare architectural approaches, not competitors.
+
+- Avoid: "Other libraries use lazy approximations"
+- Prefer: "Traditional batch-calculation approaches optimize for historical analysis but introduce variable latency in streaming contexts. We chose streaming-first architecture, accepting higher memory overhead for predictable real-time performance."
+
+#### Addressing Trade-offs
+Name the cost and why it is justified.
+
+Example:
+"SIMD vectorization requires careful attention to data alignment and padding. We handle this automatically, but it adds code complexity that scalar implementations avoid. The 8x performance gain justifies this complexity for production systems processing thousands of indicators simultaneously."
+
+### Evidence Hierarchy
+Order every claim as:
+
+1. Architectural principle (why)
+2. Implementation detail (how)
+3. Measurable outcome (proof)
+4. Practical implication (so what)
+
+### Humor Rules (The Bryson Touch)
+
+Use humor for:
+- complexity acknowledgment
+- historical context
+- universal engineering truths
+
+Do not use humor for:
+- correctness
+- security
+- performance claims
+- risk and trade-off disclosure
+
+### Structural Guidelines
+
+#### Opening
+Start with the architectural problem, not the product.
+
+Example:
+"Real-time technical analysis faces a timing problem. Calculate too slowly and you miss opportunities. Calculate incorrectly and you take bad trades. Traditional approaches optimize for one or the other. We needed both."
+
+#### Middle
+Build evidence in this order:
+- challenge
+- alternatives and limitations
+- chosen architecture
+- measurable validation
+- practical implications
+
+#### Closing
+End with verifiable next actions.
+
+Example:
+"The code is on GitHub. Run the benchmarks. Check test coverage. Compare initialization behavior against TA-Lib using sparse data. The architecture speaks for itself."
+
+### Formatting for Technical Architects
+
+- Use lists only for distinct enumerations (principles, metrics, compatibility matrices, coverage families)
+- Keep prose for architectural reasoning
+- Include code examples freely; architects trust code over adjectives
+- Present performance claims with environment, sample size, baseline, and significance when applicable
+
+### Guards Against AI Writing Patterns
+
+#### Absolutely Forbidden Phrases
+- delve into
+- it's important to note that
+- in today’s fast-paced world/landscape
+- at the end of the day
+- leverage (except mechanical context)
+- robust / holistic / synergy
+- however, it's worth noting that
+- sentence starts with: indeed, notably, significantly
+
+#### Structural Patterns to Avoid
+- formulaic lists of exactly 3 or 5 items by default
+- "on one hand / on the other hand"
+- dictionary-definition openings
+- perfectly balanced pros/cons symmetry
+- "as we embark on this journey"
+- "imagine a world where"
+
+#### Human-Writing Checks
+Prefer:
+- concrete product names (NinjaTrader, QuantConnect, Quantower)
+- specific contexts (ES futures, sparse data feeds)
+- irregular sentence rhythm
+- occasional natural asides
+- direct and testable opinions
+
+### Verification Filters (Per Section)
+
+- Proof test: every claim is backed by specifics
+- Respect test: expert architect would accept the rigor
+- Honesty test: limitations are clearly stated
+- Actionable test: reader can verify independently
+- Human test: sentence sounds naturally authored
+
+### Final Principles
+
+- Be uncompromising about standards, kind about people
+- Let architecture persuade; avoid aggressive marketing tone
+- Measure twice, claim once
+- Write like explaining to a technical colleague evaluating your logic critically
diff --git a/coverlet.runsettings b/coverlet.runsettings
deleted file mode 100644
index 54e7d92f..00000000
--- a/coverlet.runsettings
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
- opencover
- MissingAll
- false
- false
- false
- true
- Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute
-
-
-
-
-
diff --git a/temp_decompile/Skender.Stock.Indicators.decompiled.cs b/temp_decompile/Skender.Stock.Indicators.decompiled.cs
deleted file mode 100644
index 4911381e..00000000
--- a/temp_decompile/Skender.Stock.Indicators.decompiled.cs
+++ /dev/null
@@ -1,10479 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using System.Globalization;
-using System.Linq;
-using System.Reflection;
-using System.Resources;
-using System.Runtime.CompilerServices;
-using System.Runtime.Versioning;
-
-[assembly: CompilationRelaxations(8)]
-[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
-[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
-[assembly: CLSCompliant(true)]
-[assembly: InternalsVisibleTo("Tests.Indicators")]
-[assembly: InternalsVisibleTo("Tests.Performance")]
-[assembly: TargetFramework(".NETCoreApp,Version=v10.0", FrameworkDisplayName = ".NET 10.0")]
-[assembly: AssemblyCompany("Dave Skender")]
-[assembly: AssemblyConfiguration("Release")]
-[assembly: AssemblyCopyright("@2020 Dave Skender")]
-[assembly: AssemblyDescription("Stock Indicators for .NET. Transform financial market price quotes into technical analysis indicators such as MACD, Stochastic RSI, Average True Range, Parabolic SAR, etc. Nothing more.")]
-[assembly: AssemblyFileVersion("2.7.0.0")]
-[assembly: AssemblyInformationalVersion("2.7.0-43+Branch.main.Sha.e4c40d7cc048936a44d34291729c7772537f65da.e4c40d7cc048936a44d34291729c7772537f65da")]
-[assembly: AssemblyProduct("Stock Indicators for .NET")]
-[assembly: AssemblyTitle("Skender.Stock.Indicators")]
-[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/DaveSkender/Stock.Indicators")]
-[assembly: NeutralResourcesLanguage("en-US")]
-[assembly: AssemblyVersion("2.7.0.0")]
-[module: RefSafetyRules(11)]
-namespace Skender.Stock.Indicators;
-
-public static class Indicator
-{
- private static readonly CultureInfo invCulture = CultureInfo.InvariantCulture;
-
- private static readonly Calendar invCalendar = invCulture.Calendar;
-
- private static readonly CalendarWeekRule invCalendarWeekRule = invCulture.DateTimeFormat.CalendarWeekRule;
-
- private static readonly DayOfWeek invFirstDayOfWeek = invCulture.DateTimeFormat.FirstDayOfWeek;
-
- ///
- /// 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.Optional. Number of periods in the moving average of ADL.Time series of ADL values.Invalid parameter value provided.
- public static IEnumerable GetAdl(this IEnumerable quotes, int? smaPeriods = null) where TQuote : IQuote
- {
- return quotes.ToQuoteD().CalcAdl(smaPeriods);
- }
-
- internal static List CalcAdl(this List qdList, int? smaPeriods)
- {
- ValidateAdl(smaPeriods);
- List list = new List(qdList.Count);
- double num = 0.0;
- checked
- {
- for (int i = 0; i < qdList.Count; i++)
- {
- QuoteD quoteD = qdList[i];
- double num2 = ((quoteD.High == quoteD.Low) ? 0.0 : ((quoteD.Close - quoteD.Low - (quoteD.High - quoteD.Close)) / (quoteD.High - quoteD.Low)));
- double num3 = num2 * quoteD.Volume;
- double num4 = num3 + num;
- AdlResult adlResult = new AdlResult(quoteD.Date)
- {
- MoneyFlowMultiplier = num2,
- MoneyFlowVolume = num3,
- Adl = num4
- };
- list.Add(adlResult);
- num = num4;
- if (smaPeriods.HasValue && i + 1 >= smaPeriods)
- {
- double? num5 = 0.0;
- for (int j = i + 1 - smaPeriods.Value; j <= i; j++)
- {
- num5 += list[j].Adl;
- }
- adlResult.AdlSma = num5 / (double?)smaPeriods;
- }
- }
- return list;
- }
- }
-
- private static void ValidateAdl(int? smaPeriods)
- {
- if (smaPeriods.HasValue && smaPeriods.GetValueOrDefault() <= 0)
- {
- throw new ArgumentOutOfRangeException("smaPeriods", smaPeriods, "SMA periods must be greater than 0 for ADL.");
- }
- }
-
- ///
- /// Directional Movement Index (DMI) and Average Directional Movement Index (ADX) is a measure of price directional movement.
- /// It includes upward and downward indicators, and is often used to measure strength of trend.
- ///
- /// See
- /// documentation
- /// for more information.
- ///
- /// Configurable Quote type. See Guide for more information.Historical price quotes.Number of periods in the lookback window.Time series of ADX and Plus/Minus Directional values.Invalid parameter value provided.
- public static IEnumerable GetAdx(this IEnumerable quotes, int lookbackPeriods = 14) where TQuote : IQuote
- {
- return quotes.ToQuoteD().CalcAdx(lookbackPeriods);
- }
-
- internal static List CalcAdx(this List qdList, int lookbackPeriods)
- {
- ValidateAdx(lookbackPeriods);
- int count = qdList.Count;
- List list = new List(count);
- double num = 0.0;
- double num2 = 0.0;
- double num3 = 0.0;
- double num4 = 0.0;
- double num5 = 0.0;
- double num6 = 0.0;
- double num7 = 0.0;
- double num8 = 0.0;
- double num9 = 0.0;
- double num10 = 0.0;
- double num11 = 0.0;
- checked
- {
- for (int i = 0; i < count; i++)
- {
- QuoteD quoteD = qdList[i];
- AdxResult adxResult = new AdxResult(quoteD.Date);
- list.Add(adxResult);
- if (i == 0)
- {
- num = quoteD.High;
- num2 = quoteD.Low;
- num3 = quoteD.Close;
- continue;
- }
- double val = Math.Abs(quoteD.High - num3);
- double val2 = Math.Abs(quoteD.Low - num3);
- double num12 = quoteD.High - num;
- double num13 = num2 - quoteD.Low;
- double num14 = Math.Max(quoteD.High - quoteD.Low, Math.Max(val, val2));
- double num15 = ((num12 > num13) ? Math.Max(num12, 0.0) : 0.0);
- double num16 = ((num13 > num12) ? Math.Max(num13, 0.0) : 0.0);
- num = quoteD.High;
- num2 = quoteD.Low;
- num3 = quoteD.Close;
- if (i <= lookbackPeriods)
- {
- num8 += num14;
- num9 += num15;
- num10 += num16;
- }
- if (i < lookbackPeriods)
- {
- continue;
- }
- double num17;
- double num18;
- double num19;
- if (i == lookbackPeriods)
- {
- num17 = num8;
- num18 = num9;
- num19 = num10;
- }
- else
- {
- num17 = num4 - num4 / (double)lookbackPeriods + num14;
- num18 = num5 - num5 / (double)lookbackPeriods + num15;
- num19 = num6 - num6 / (double)lookbackPeriods + num16;
- }
- num4 = num17;
- num5 = num18;
- num6 = num19;
- if (num17 != 0.0)
- {
- double num20 = 100.0 * num18 / num17;
- double num21 = 100.0 * num19 / num17;
- adxResult.Pdi = num20;
- adxResult.Mdi = num21;
- double num22 = ((num20 == num21) ? 0.0 : ((num20 + num21 != 0.0) ? (100.0 * Math.Abs(num20 - num21) / (num20 + num21)) : double.NaN));
- if (i > 2 * lookbackPeriods - 1)
- {
- double num23 = (num7 * (double)(lookbackPeriods - 1) + num22) / (double)lookbackPeriods;
- adxResult.Adx = num23.NaN2Null();
- adxResult.Adxr = (num23 + list[i + 1 - lookbackPeriods].Adx).NaN2Null() / 2.0;
- num7 = num23;
- }
- else if (i == 2 * lookbackPeriods - 1)
- {
- num11 += num22;
- double num23 = num11 / (double)lookbackPeriods;
- adxResult.Adx = num23.NaN2Null();
- num7 = num23;
- }
- else
- {
- num11 += num22;
- }
- }
- }
- return list;
- }
- }
-
- private static void ValidateAdx(int lookbackPeriods)
- {
- if (lookbackPeriods <= 1)
- {
- throw new ArgumentOutOfRangeException("lookbackPeriods", lookbackPeriods, "Lookback periods must be greater than 1 for ADX.");
- }
- }
-
- /// Removes the recommended quantity of results from the beginning of the results list
- /// using a reverse-engineering approach. See
- /// documentation for more information.
- /// Indicator
- /// results to evaluate.Time
- /// series of results, pruned.
- public static IEnumerable RemoveWarmupPeriods(this IEnumerable results)
- {
- int num = results.ToList().FindIndex((AdxResult x) => x.Pdi.HasValue);
- return results.Remove(checked(2 * num + 100));
- }
-
- ///
- /// 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.
- ///
- /// Configurable Quote type. See Guide for more information.Historical price quotes.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 IEnumerable GetAlligator(this IEnumerable quotes, int jawPeriods = 13, int jawOffset = 8, int teethPeriods = 8, int teethOffset = 5, int lipsPeriods = 5, int lipsOffset = 3) where TQuote : IQuote
- {
- return quotes.ToTuple(CandlePart.HL2).CalcAlligator(jawPeriods, jawOffset, teethPeriods, teethOffset, lipsPeriods, lipsOffset);
- }
-
- public static IEnumerable GetAlligator(this IEnumerable results, int jawPeriods = 13, int jawOffset = 8, int teethPeriods = 8, int teethOffset = 5, int lipsPeriods = 5, int lipsOffset = 3)
- {
- return results.ToTuple().CalcAlligator(jawPeriods, jawOffset, teethPeriods, teethOffset, lipsPeriods, lipsOffset).SyncIndex(results, SyncType.Prepend);
- }
-
- public static IEnumerable GetAlligator(this IEnumerable<(DateTime, double)> priceTuples, int jawPeriods = 13, int jawOffset = 8, int teethPeriods = 8, int teethOffset = 5, int lipsPeriods = 5, int lipsOffset = 3)
- {
- return priceTuples.ToSortedList().CalcAlligator(jawPeriods, jawOffset, teethPeriods, teethOffset, lipsPeriods, lipsOffset);
- }
-
- internal static List CalcAlligator(this List<(DateTime Date, double Value)> tpList, int jawPeriods, int jawOffset, int teethPeriods, int teethOffset, int lipsPeriods, int lipsOffset)
- {
- ValidateAlligator(jawPeriods, jawOffset, teethPeriods, teethOffset, lipsPeriods, lipsOffset);
- int count = tpList.Count;
- double[] array = new double[count];
- List list = tpList.Select(((DateTime Date, double Value) x) => new AlligatorResult(x.Date)).ToList();
- checked
- {
- for (int num = 0; num < count; num++)
- {
- double item = tpList[num].Value;
- array[num] = item;
- if (num + jawOffset < count)
- {
- AlligatorResult alligatorResult = list[num + jawOffset];
- if (num + 1 == jawPeriods)
- {
- double num2 = 0.0;
- for (int num3 = num + 1 - jawPeriods; num3 <= num; num3++)
- {
- num2 += array[num3];
- }
- alligatorResult.Jaw = num2 / (double)jawPeriods;
- }
- else if (num + 1 > jawPeriods)
- {
- alligatorResult.Jaw = (list[num + jawOffset - 1].Jaw * (double)(jawPeriods - 1) + array[num]) / (double)jawPeriods;
- }
- alligatorResult.Jaw = alligatorResult.Jaw.NaN2Null();
- }
- if (num + teethOffset < count)
- {
- AlligatorResult alligatorResult2 = list[num + teethOffset];
- if (num + 1 == teethPeriods)
- {
- double num4 = 0.0;
- for (int num5 = num + 1 - teethPeriods; num5 <= num; num5++)
- {
- num4 += array[num5];
- }
- alligatorResult2.Teeth = num4 / (double)teethPeriods;
- }
- else if (num + 1 > teethPeriods)
- {
- alligatorResult2.Teeth = (list[num + teethOffset - 1].Teeth * (double)(teethPeriods - 1) + array[num]) / (double)teethPeriods;
- }
- alligatorResult2.Teeth = alligatorResult2.Teeth.NaN2Null();
- }
- if (num + lipsOffset >= count)
- {
- continue;
- }
- AlligatorResult alligatorResult3 = list[num + lipsOffset];
- if (num + 1 == lipsPeriods)
- {
- double num6 = 0.0;
- for (int num7 = num + 1 - lipsPeriods; num7 <= num; num7++)
- {
- num6 += array[num7];
- }
- alligatorResult3.Lips = num6 / (double)lipsPeriods;
- }
- else if (num + 1 > lipsPeriods)
- {
- alligatorResult3.Lips = (list[num + lipsOffset - 1].Lips * (double)(lipsPeriods - 1) + array[num]) / (double)lipsPeriods;
- }
- alligatorResult3.Lips = alligatorResult3.Lips.NaN2Null();
- }
- return list;
- }
- }
-
- private static void ValidateAlligator(int jawPeriods, int jawOffset, int teethPeriods, int teethOffset, int lipsPeriods, int lipsOffset)
- {
- if (jawPeriods <= teethPeriods)
- {
- throw new ArgumentOutOfRangeException("jawPeriods", jawPeriods, "Jaw lookback periods must be greater than Teeth lookback periods for Alligator.");
- }
- if (teethPeriods <= lipsPeriods)
- {
- throw new ArgumentOutOfRangeException("teethPeriods", teethPeriods, "Teeth lookback periods must be greater than Lips lookback periods for Alligator.");
- }
- if (lipsPeriods <= 0)
- {
- throw new ArgumentOutOfRangeException("lipsPeriods", lipsPeriods, "Lips lookback periods must be greater than 0 for Alligator.");
- }
- if (jawOffset <= 0)
- {
- throw new ArgumentOutOfRangeException("jawOffset", jawOffset, "Jaw offset periods must be greater than 0 for Alligator.");
- }
- if (teethOffset <= 0)
- {
- throw new ArgumentOutOfRangeException("teethOffset", teethOffset, "Jaw offset periods must be greater than 0 for Alligator.");
- }
- if (lipsOffset <= 0)
- {
- throw new ArgumentOutOfRangeException("lipsOffset", lipsOffset, "Jaw offset periods must be greater than 0 for Alligator.");
- }
- checked
- {
- if (jawPeriods + jawOffset <= teethPeriods + teethOffset)
- {
- throw new ArgumentOutOfRangeException("jawPeriods", jawPeriods, "Jaw lookback + offset are too small for Alligator.");
- }
- if (teethPeriods + teethOffset <= lipsPeriods + lipsOffset)
- {
- throw new ArgumentOutOfRangeException("teethPeriods", teethPeriods, "Teeth lookback + offset are too small for Alligator.");
- }
- }
- }
-
- /// Removes non-essential records containing null values with unique consideration for
- /// this indicator. See
- /// documentation for more information.
- /// Indicator results to evaluate.Time series of
- /// indicator results, condensed.
- public static IEnumerable Condense(this IEnumerable results)
- {
- List list = results.ToList();
- list.RemoveAll((AlligatorResult x) => !x.Jaw.HasValue && !x.Teeth.HasValue && !x.Lips.HasValue);
- return list.ToSortedList();
- }
-
- /// Removes the recommended quantity of results from the beginning of the results list
- /// using a reverse-engineering approach. See
- /// documentation for more information.
- /// Indicator
- /// results to evaluate.Time
- /// series of results, pruned.
- public static IEnumerable RemoveWarmupPeriods(this IEnumerable results)
- {
- int removePeriods = checked(results.ToList().FindIndex((AlligatorResult x) => x.Jaw.HasValue) + 251);
- return results.Remove(removePeriods);
- }
-
- ///
- /// Arnaud Legoux Moving Average (ALMA) is a Gaussian distribution
- /// weighted moving average of price over a lookback window.
- ///
- /// See
- /// documentation
- /// for more information.
- ///
- /// Configurable Quote type. See Guide for more information.Historical price quotes.Number of periods in the lookback window.Adjusts smoothness versus responsiveness.Defines the width of the Gaussian normal distribution.Time series of ALMA values.Invalid parameter value provided.
- public static IEnumerable GetAlma(this IEnumerable quotes, int lookbackPeriods = 9, double offset = 0.85, double sigma = 6.0) where TQuote : IQuote
- {
- return quotes.ToTuple(CandlePart.Close).CalcAlma(lookbackPeriods, offset, sigma);
- }
-
- public static IEnumerable GetAlma(this IEnumerable results, int lookbackPeriods = 9, double offset = 0.85, double sigma = 6.0)
- {
- return results.ToTuple().CalcAlma(lookbackPeriods, offset, sigma).SyncIndex(results, SyncType.Prepend);
- }
-
- public static IEnumerable GetAlma(this IEnumerable<(DateTime, double)> priceTuples, int lookbackPeriods = 9, double offset = 0.85, double sigma = 6.0)
- {
- return priceTuples.ToSortedList().CalcAlma(lookbackPeriods, offset, sigma);
- }
-
- internal static List CalcAlma(this List<(DateTime, double)> tpList, int lookbackPeriods, double offset, double sigma)
- {
- ValidateAlma(lookbackPeriods, offset, sigma);
- List list = new List(tpList.Count);
- checked
- {
- double num = offset * (double)(lookbackPeriods - 1);
- double num2 = (double)lookbackPeriods / sigma;
- double[] array = new double[lookbackPeriods];
- double num3 = 0.0;
- for (int i = 0; i < lookbackPeriods; i++)
- {
- num3 += (array[i] = Math.Exp((0.0 - ((double)i - num) * ((double)i - num)) / (2.0 * num2 * num2)));
- }
- for (int j = 0; j < tpList.Count; j++)
- {
- AlmaResult almaResult = new AlmaResult(tpList[j].Item1);
- list.Add(almaResult);
- if (j + 1 >= lookbackPeriods)
- {
- double? num4 = 0.0;
- int num5 = 0;
- for (int k = j + 1 - lookbackPeriods; k <= j; k++)
- {
- double item = tpList[k].Item2;
- num4 += array[num5] * item;
- num5++;
- }
- almaResult.Alma = (num4 / num3).NaN2Null();
- }
- }
- return list;
- }
- }
-
- private static void ValidateAlma(int lookbackPeriods, double offset, double sigma)
- {
- if (lookbackPeriods <= 1)
- {
- throw new ArgumentOutOfRangeException("lookbackPeriods", lookbackPeriods, "Lookback periods must be greater than 1 for ALMA.");
- }
- if ((offset < 0.0 || offset > 1.0) ? true : false)
- {
- throw new ArgumentOutOfRangeException("offset", offset, "Offset must be between 0 and 1 for ALMA.");
- }
- if (sigma <= 0.0)
- {
- throw new ArgumentOutOfRangeException("sigma", sigma, "Sigma must be greater than 0 for ALMA.");
- }
- }
-
- /// Removes the recommended quantity of results from the beginning of the results list
- /// using a reverse-engineering approach. See
- /// documentation for more information.
- /// Indicator
- /// results to evaluate.Time
- /// series of results, pruned.
- public static IEnumerable RemoveWarmupPeriods(this IEnumerable results)
- {
- int removePeriods = results.ToList().FindIndex((AlmaResult x) => x.Alma.HasValue);
- return results.Remove(removePeriods);
- }
-
- ///
- /// Aroon is a simple oscillator view of how long the new high or low price occured over a lookback window.
- ///
- /// See
- /// documentation
- /// for more information.
- ///
- /// Configurable Quote type. See Guide for more information.Historical price quotes.Number of periods in the lookback window.Time series of Aroon Up/Down and Oscillator values.Invalid parameter value provided.
- public static IEnumerable GetAroon(this IEnumerable quotes, int lookbackPeriods = 25) where TQuote : IQuote
- {
- return quotes.ToQuoteD().CalcAroon(lookbackPeriods);
- }
-
- internal static List CalcAroon(this List qdList, int lookbackPeriods)
- {
- ValidateAroon(lookbackPeriods);
- List list = new List(qdList.Count);
- checked
- {
- for (int i = 0; i < qdList.Count; i++)
- {
- AroonResult aroonResult = new AroonResult(qdList[i].Date);
- list.Add(aroonResult);
- if (i + 1 <= lookbackPeriods)
- {
- continue;
- }
- double? num = 0.0;
- double? num2 = double.MaxValue;
- int num3 = 0;
- int num4 = 0;
- for (int j = i + 1 - lookbackPeriods - 1; j <= i; j++)
- {
- QuoteD quoteD = qdList[j];
- if (quoteD.High > num)
- {
- num = quoteD.High;
- num3 = j + 1;
- }
- if (quoteD.Low < num2)
- {
- num2 = quoteD.Low;
- num4 = j + 1;
- }
- }
- aroonResult.AroonUp = 100.0 * (double)(lookbackPeriods - (i + 1 - num3)) / (double)lookbackPeriods;
- aroonResult.AroonDown = 100.0 * (double)(lookbackPeriods - (i + 1 - num4)) / (double)lookbackPeriods;
- aroonResult.Oscillator = aroonResult.AroonUp - aroonResult.AroonDown;
- }
- return list;
- }
- }
-
- private static void ValidateAroon(int lookbackPeriods)
- {
- if (lookbackPeriods <= 0)
- {
- throw new ArgumentOutOfRangeException("lookbackPeriods", lookbackPeriods, "Lookback periods must be greater than 0 for Aroon.");
- }
- }
-
- /// Removes the recommended quantity of results from the beginning of the results list
- /// using a reverse-engineering approach. See
- /// documentation for more information.
- /// Indicator
- /// results to evaluate.Time
- /// series of results, pruned.
- public static IEnumerable RemoveWarmupPeriods(this IEnumerable results)
- {
- int removePeriods = results.ToList().FindIndex((AroonResult x) => x.Oscillator.HasValue);
- return results.Remove(removePeriods);
- }
-
- ///
- /// Average True Range (ATR) is a measure of volatility that captures gaps and limits between periods.
- ///
- /// See
- /// documentation
- /// for more information.
- ///
- /// Configurable Quote type. See Guide for more information.Historical price quotes.Number of periods in the lookback window.Time series of ATR values.Invalid parameter value provided.
- public static IEnumerable GetAtr(this IEnumerable quotes, int lookbackPeriods = 14) where TQuote : IQuote
- {
- return quotes.ToQuoteD().CalcAtr(lookbackPeriods);
- }
-
- internal static List CalcAtr(this List qdList, int lookbackPeriods)
- {
- ValidateAtr(lookbackPeriods);
- List list = new List(qdList.Count);
- double num = double.NaN;
- double num2 = double.NaN;
- double num3 = 0.0;
- checked
- {
- for (int i = 0; i < qdList.Count; i++)
- {
- QuoteD quoteD = qdList[i];
- AtrResult atrResult = new AtrResult(quoteD.Date);
- list.Add(atrResult);
- if (i > 0)
- {
- double val = Math.Abs(quoteD.High - num2);
- double val2 = Math.Abs(quoteD.Low - num2);
- double num4 = Math.Max(quoteD.High - quoteD.Low, Math.Max(val, val2));
- atrResult.Tr = num4;
- if (i > lookbackPeriods)
- {
- double num5 = (num * (double)(lookbackPeriods - 1) + num4) / (double)lookbackPeriods;
- atrResult.Atr = num5;
- atrResult.Atrp = ((quoteD.Close == 0.0) ? ((double?)null) : new double?(num5 / quoteD.Close * 100.0));
- num = num5;
- }
- else if (i == lookbackPeriods)
- {
- num3 += num4;
- double num6 = num3 / (double)lookbackPeriods;
- atrResult.Atr = num6;
- atrResult.Atrp = ((quoteD.Close == 0.0) ? ((double?)null) : new double?(num6 / quoteD.Close * 100.0));
- num = num6;
- }
- else
- {
- num3 += num4;
- }
- num2 = quoteD.Close;
- }
- else
- {
- num2 = quoteD.Close;
- }
- }
- return list;
- }
- }
-
- private static void ValidateAtr(int lookbackPeriods)
- {
- if (lookbackPeriods <= 1)
- {
- throw new ArgumentOutOfRangeException("lookbackPeriods", lookbackPeriods, "Lookback periods must be greater than 1 for Average True Range.");
- }
- }
-
- /// Removes the recommended quantity of results from the beginning of the results list
- /// using a reverse-engineering approach. See
- /// documentation for more information.
- /// Indicator
- /// results to evaluate.Time
- /// series of results, pruned.
- public static IEnumerable RemoveWarmupPeriods(this IEnumerable results)
- {
- int removePeriods = results.ToList().FindIndex((AtrResult x) => x.Atr.HasValue);
- return results.Remove(removePeriods);
- }
-
- ///
- /// ATR Trailing Stop attempts to determine the primary trend of prices by using
- /// Average True Range (ATR) band thresholds. It can indicate a buy/sell signal or a
- /// trailing stop when the trend changes.
- ///
- /// See
- /// documentation
- /// for more information.
- ///
- /// Configurable Quote type. See Guide for more information.Historical price quotes.Number of periods for ATR.Multiplier sets the ATR band width.Sets basis for stop offsets (Close or High/Low).Time series of ATR Trailing Stop values.Invalid parameter value provided.
- public static IEnumerable GetAtrStop(this IEnumerable quotes, int lookbackPeriods = 21, double multiplier = 3.0, EndType endType = EndType.Close) where TQuote : IQuote
- {
- return quotes.ToQuoteD().CalcAtrStop(lookbackPeriods, multiplier, endType);
- }
-
- internal static List CalcAtrStop(this List qdList, int lookbackPeriods, double multiplier, EndType endType)
- {
- ValidateAtrStop(lookbackPeriods, multiplier);
- List list = new List(qdList.Count);
- List list2 = qdList.CalcAtr(lookbackPeriods);
- bool flag = true;
- double? num = null;
- double? num2 = null;
- checked
- {
- for (int i = 0; i < qdList.Count; i++)
- {
- QuoteD quoteD = qdList[i];
- AtrStopResult atrStopResult = new AtrStopResult(quoteD.Date);
- list.Add(atrStopResult);
- if (i >= lookbackPeriods)
- {
- double? atr = list2[i].Atr;
- QuoteD quoteD2 = qdList[i - 1];
- double? num3;
- double? num4;
- if (endType == EndType.Close)
- {
- num3 = quoteD.Close + multiplier * atr;
- num4 = quoteD.Close - multiplier * atr;
- }
- else
- {
- num3 = quoteD.High + multiplier * atr;
- num4 = quoteD.Low - multiplier * atr;
- }
- if (i == lookbackPeriods)
- {
- flag = quoteD.Close >= quoteD2.Close;
- num = num3;
- num2 = num4;
- }
- if (num3 < num || quoteD2.Close > num)
- {
- num = num3;
- }
- if (num4 > num2 || quoteD2.Close < num2)
- {
- num2 = num4;
- }
- if (quoteD.Close <= (flag ? num2 : num))
- {
- atrStopResult.AtrStop = (decimal?)num;
- atrStopResult.BuyStop = (decimal?)num;
- flag = false;
- }
- else
- {
- atrStopResult.AtrStop = (decimal?)num2;
- atrStopResult.SellStop = (decimal?)num2;
- flag = true;
- }
- }
- }
- return list;
- }
- }
-
- private static void ValidateAtrStop(int lookbackPeriods, double multiplier)
- {
- if (lookbackPeriods <= 1)
- {
- throw new ArgumentOutOfRangeException("lookbackPeriods", lookbackPeriods, "Lookback periods must be greater than 1 for ATR Trailing Stop.");
- }
- if (multiplier <= 0.0)
- {
- throw new ArgumentOutOfRangeException("multiplier", multiplier, "Multiplier must be greater than 0 for ATR Trailing Stop.");
- }
- }
-
- /// Removes non-essential records containing null values with unique consideration for
- /// this indicator. See
- /// documentation for more information.
- /// Indicator results to evaluate.Time series of
- /// indicator results, condensed.
- public static IEnumerable Condense(this IEnumerable results)
- {
- List list = results.ToList();
- list.RemoveAll((AtrStopResult x) => !x.AtrStop.HasValue);
- return list.ToSortedList();
- }
-
- /// Removes the recommended quantity of results from the beginning of the results list
- /// using a reverse-engineering approach. See
- /// documentation for more information.
- /// Indicator
- /// results to evaluate.Time
- /// series of results, pruned.
- public static IEnumerable RemoveWarmupPeriods(this IEnumerable results)
- {
- int removePeriods = results.ToList().FindIndex((AtrStopResult x) => x.AtrStop.HasValue);
- return results.Remove(removePeriods);
- }
-
- ///
- /// Awesome Oscillator (aka Super AO) is a measure of the gap between a fast and slow period modified moving average.
- ///
- /// See
- /// documentation
- /// for more information.
- ///
- /// Configurable Quote type. See Guide for more information.Historical price quotes.Number of periods in the Fast moving average.Number of periods in the Slow moving average.Time series of Awesome Oscillator values.Invalid parameter value provided.
- public static IEnumerable GetAwesome(this IEnumerable quotes, int fastPeriods = 5, int slowPeriods = 34) where TQuote : IQuote
- {
- return quotes.ToTuple(CandlePart.HL2).CalcAwesome(fastPeriods, slowPeriods);
- }
-
- public static IEnumerable GetAwesome(this IEnumerable results, int fastPeriods = 5, int slowPeriods = 34)
- {
- return results.ToTuple().CalcAwesome(fastPeriods, slowPeriods).SyncIndex(results, SyncType.Prepend);
- }
-
- public static IEnumerable GetAwesome(this IEnumerable<(DateTime, double)> priceTuples, int fastPeriods = 5, int slowPeriods = 34)
- {
- return priceTuples.ToSortedList().CalcAwesome(fastPeriods, slowPeriods);
- }
-
- internal static List CalcAwesome(this List<(DateTime, double)> tpList, int fastPeriods, int slowPeriods)
- {
- ValidateAwesome(fastPeriods, slowPeriods);
- int count = tpList.Count;
- List list = new List(count);
- double[] array = new double[count];
- checked
- {
- for (int i = 0; i < count; i++)
- {
- var (date, num) = tpList[i];
- array[i] = num;
- AwesomeResult awesomeResult = new AwesomeResult(date);
- list.Add(awesomeResult);
- if (i + 1 < slowPeriods)
- {
- continue;
- }
- double num2 = 0.0;
- double num3 = 0.0;
- for (int j = i + 1 - slowPeriods; j <= i; j++)
- {
- num2 += array[j];
- if (j >= i + 1 - fastPeriods)
- {
- num3 += array[j];
- }
- }
- awesomeResult.Oscillator = (num3 / (double)fastPeriods - num2 / (double)slowPeriods).NaN2Null();
- awesomeResult.Normalized = ((array[i] == 0.0) ? ((double?)null) : (100.0 * awesomeResult.Oscillator / array[i]));
- }
- return list;
- }
- }
-
- private static void ValidateAwesome(int fastPeriods, int slowPeriods)
- {
- if (fastPeriods <= 0)
- {
- throw new ArgumentOutOfRangeException("slowPeriods", slowPeriods, "Fast periods must be greater than 0 for Awesome Oscillator.");
- }
- if (slowPeriods <= fastPeriods)
- {
- throw new ArgumentOutOfRangeException("slowPeriods", slowPeriods, "Slow periods must be larger than Fast Periods for Awesome Oscillator.");
- }
- }
-
- /// Removes the recommended quantity of results from the beginning of the results list
- /// using a reverse-engineering approach. See
- /// documentation for more information.
- /// Indicator
- /// results to evaluate.Time
- /// series of results, pruned.
- public static IEnumerable RemoveWarmupPeriods(this IEnumerable results)
- {
- int removePeriods = results.ToList().FindIndex((AwesomeResult x) => x.Oscillator.HasValue);
- return results.Remove(removePeriods);
- }
-
- ///
- /// A simple quote transform.
- ///
- /// See
- /// documentation
- /// for more information.
- ///
- /// Configurable Quote type. See Guide for more information.Historical price quotes.The OHLCV element or simply calculated value type.Time series of Basic Quote values.Invalid candle part provided.
- public static IEnumerable GetBaseQuote(this IEnumerable quotes, CandlePart candlePart = CandlePart.Close) where TQuote : IQuote
- {
- return from q in quotes
- select q.ToBasicData(candlePart) into x
- orderby x.Date
- select x;
- }
-
- ///
- /// Beta shows how strongly one stock responds to systemic volatility of the entire market.
- ///
- /// See
- /// documentation
- /// for more information.
- ///
- /// Configurable Quote type. See Guide for more information.Historical price quotes for Evaluation.Historical price quotes for Market.Number of periods in the lookback window.Type of Beta to calculate.Time series of Beta values.Invalid parameter value provided.Invalid quotes provided.
- public static IEnumerable GetBeta(this IEnumerable quotesEval, IEnumerable quotesMarket, int lookbackPeriods, BetaType type = BetaType.Standard) where TQuote : IQuote
- {
- List<(DateTime, double)> tpListEval = quotesEval.ToTuple(CandlePart.Close);
- List<(DateTime, double)> tpListMrkt = quotesMarket.ToTuple(CandlePart.Close);
- return CalcBeta(tpListEval, tpListMrkt, lookbackPeriods, type);
- }
-
- public static IEnumerable GetBeta(this IEnumerable evalResults, IEnumerable mrktResults, int lookbackPeriods, BetaType type = BetaType.Standard)
- {
- List<(DateTime Date, double Value)> tpListEval = evalResults.ToTuple();
- List<(DateTime, double)> tpListMrkt = mrktResults.ToTuple();
- return CalcBeta(tpListEval, tpListMrkt, lookbackPeriods, type).SyncIndex(evalResults, SyncType.Prepend);
- }
-
- public static IEnumerable GetBeta(this IEnumerable<(DateTime, double)> evalTuple, IEnumerable<(DateTime, double)> mrktTuple, int lookbackPeriods, BetaType type = BetaType.Standard)
- {
- List<(DateTime, double)> tpListEval = evalTuple.ToSortedList();
- List<(DateTime, double)> tpListMrkt = mrktTuple.ToSortedList();
- return CalcBeta(tpListEval, tpListMrkt, lookbackPeriods, type);
- }
-
- internal static List CalcBeta(List<(DateTime, double)> tpListEval, List<(DateTime, double)> tpListMrkt, int lookbackPeriods, BetaType type = BetaType.Standard)
- {
- ValidateBeta(tpListEval, tpListMrkt, lookbackPeriods);
- int count = tpListEval.Count;
- List list = new List(count);
- bool flag = ((type == BetaType.Standard || type == BetaType.All) ? true : false);
- bool flag2 = flag;
- flag = ((type == BetaType.Up || type == BetaType.All) ? true : false);
- bool flag3 = flag;
- flag = (uint)(type - 2) <= 1u;
- bool flag4 = flag;
- double[] array = new double[count];
- double[] array2 = new double[count];
- double num = 0.0;
- double num2 = 0.0;
- checked
- {
- for (int i = 0; i < count; i++)
- {
- var (dateTime, num3) = tpListEval[i];
- var (dateTime2, num4) = tpListMrkt[i];
- if (dateTime != dateTime2)
- {
- throw new InvalidQuotesException("tpListEval", dateTime, "Date sequence does not match. Beta requires matching dates in provided quotes.");
- }
- array[i] = ((num != 0.0) ? (num3 / num - 1.0) : 0.0);
- array2[i] = ((num2 != 0.0) ? (num4 / num2 - 1.0) : 0.0);
- num = num3;
- num2 = num4;
- }
- for (int j = 0; j < count; j++)
- {
- BetaResult betaResult = new BetaResult(tpListEval[j].Item1)
- {
- ReturnsEval = array[j],
- ReturnsMrkt = array2[j]
- };
- list.Add(betaResult);
- if (j >= lookbackPeriods)
- {
- if (flag2)
- {
- betaResult.CalcBetaWindow(j, lookbackPeriods, array2, array, BetaType.Standard);
- }
- if (flag4)
- {
- betaResult.CalcBetaWindow(j, lookbackPeriods, array2, array, BetaType.Down);
- }
- if (flag3)
- {
- betaResult.CalcBetaWindow(j, lookbackPeriods, array2, array, BetaType.Up);
- }
- if (type == BetaType.All && betaResult.BetaUp.HasValue && betaResult.BetaDown.HasValue)
- {
- betaResult.Ratio = ((betaResult.BetaDown == 0.0) ? ((double?)null) : (betaResult.BetaUp / betaResult.BetaDown));
- betaResult.Convexity = (betaResult.BetaUp - betaResult.BetaDown) * (betaResult.BetaUp - betaResult.BetaDown);
- }
- }
- }
- return list;
- }
- }
-
- private static void CalcBetaWindow(this BetaResult r, int i, int lookbackPeriods, double[] mrktReturns, double[] evalReturns, BetaType type)
- {
- CorrResult corrResult = new CorrResult(r.Date);
- List list = new List(lookbackPeriods);
- List list2 = new List(lookbackPeriods);
- checked
- {
- for (int j = i - lookbackPeriods + 1; j <= i; j++)
- {
- double num = mrktReturns[j];
- double item = evalReturns[j];
- if (type == BetaType.Standard || (type == BetaType.Down && num < 0.0) || (type == BetaType.Up && num > 0.0))
- {
- list.Add(num);
- list2.Add(item);
- }
- }
- if (list.Count <= 0)
- {
- return;
- }
- corrResult.PeriodCorrelation(list.ToArray(), list2.ToArray());
- if (corrResult.Covariance.HasValue && corrResult.VarianceA.HasValue && corrResult.VarianceA != 0.0)
- {
- double? num2 = (corrResult.Covariance / corrResult.VarianceA).NaN2Null();
- switch (type)
- {
- case BetaType.Standard:
- r.Beta = num2;
- break;
- case BetaType.Down:
- r.BetaDown = num2;
- break;
- case BetaType.Up:
- r.BetaUp = num2;
- break;
- }
- }
- }
- }
-
- private static void ValidateBeta(List<(DateTime, double)> tpListEval, List<(DateTime, double)> tpListMrkt, int lookbackPeriods)
- {
- if (lookbackPeriods <= 0)
- {
- throw new ArgumentOutOfRangeException("lookbackPeriods", lookbackPeriods, "Lookback periods must be greater than 0 for Beta.");
- }
- if (tpListEval.Count != tpListMrkt.Count)
- {
- throw new InvalidQuotesException("tpListEval", "Eval quotes should have the same number of Market quotes for Beta.");
- }
- }
-
- /// Removes the recommended quantity of results from the beginning of the results list
- /// using a reverse-engineering approach. See
- /// documentation for more information.
- /// Indicator
- /// results to evaluate.Time
- /// series of results, pruned.
- public static IEnumerable RemoveWarmupPeriods(this IEnumerable results)
- {
- int removePeriods = results.ToList().FindIndex((BetaResult x) => x.Beta.HasValue);
- return results.Remove(removePeriods);
- }
-
- ///
- /// Bollinger Bands® depict volatility as standard deviation boundary lines from a moving average of price.
- ///
- /// See
- /// documentation
- /// for more information.
- ///
- /// Configurable Quote type. See Guide for more information.Historical price quotes.Number of periods in the lookback window.Width of bands. Number of Standard Deviations from the moving average.Time series of Bollinger Band and %B values.Invalid parameter value provided.
- public static IEnumerable GetBollingerBands(this IEnumerable quotes, int lookbackPeriods = 20, double standardDeviations = 2.0) where TQuote : IQuote
- {
- return quotes.ToTuple(CandlePart.Close).CalcBollingerBands(lookbackPeriods, standardDeviations);
- }
-
- public static IEnumerable GetBollingerBands(this IEnumerable results, int lookbackPeriods = 20, double standardDeviations = 2.0)
- {
- return results.ToTuple().CalcBollingerBands(lookbackPeriods, standardDeviations).SyncIndex(results, SyncType.Prepend);
- }
-
- public static IEnumerable GetBollingerBands(this IEnumerable<(DateTime, double)> priceTuples, int lookbackPeriods = 20, double standardDeviations = 2.0)
- {
- return priceTuples.ToSortedList().CalcBollingerBands(lookbackPeriods, standardDeviations);
- }
-
- internal static List CalcBollingerBands(this List<(DateTime, double)> tpList, int lookbackPeriods, double standardDeviations)
- {
- ValidateBollingerBands(lookbackPeriods, standardDeviations);
- List list = new List(tpList.Count);
- checked
- {
- for (int i = 0; i < tpList.Count; i++)
- {
- (DateTime, double) tuple = tpList[i];
- DateTime item = tuple.Item1;
- double item2 = tuple.Item2;
- BollingerBandsResult bollingerBandsResult = new BollingerBandsResult(item);
- list.Add(bollingerBandsResult);
- if (i + 1 >= lookbackPeriods)
- {
- double[] array = new double[lookbackPeriods];
- double num = 0.0;
- int num2 = 0;
- for (int j = i + 1 - lookbackPeriods; j <= i; j++)
- {
- num += (array[num2] = tpList[j].Item2);
- num2++;
- }
- double? num3 = (num / (double)lookbackPeriods).NaN2Null();
- double? num4 = array.StdDev().NaN2Null();
- bollingerBandsResult.Sma = num3;
- bollingerBandsResult.UpperBand = num3 + standardDeviations * num4;
- bollingerBandsResult.LowerBand = num3 - standardDeviations * num4;
- bollingerBandsResult.PercentB = ((bollingerBandsResult.UpperBand == bollingerBandsResult.LowerBand) ? ((double?)null) : ((item2 - bollingerBandsResult.LowerBand) / (bollingerBandsResult.UpperBand - bollingerBandsResult.LowerBand)));
- bollingerBandsResult.ZScore = ((num4 == 0.0) ? ((double?)null) : ((item2 - bollingerBandsResult.Sma) / num4));
- bollingerBandsResult.Width = ((num3 == 0.0) ? ((double?)null) : ((bollingerBandsResult.UpperBand - bollingerBandsResult.LowerBand) / num3));
- }
- }
- return list;
- }
- }
-
- private static void ValidateBollingerBands(int lookbackPeriods, double standardDeviations)
- {
- if (lookbackPeriods <= 1)
- {
- throw new ArgumentOutOfRangeException("lookbackPeriods", lookbackPeriods, "Lookback periods must be greater than 1 for Bollinger Bands.");
- }
- if (standardDeviations <= 0.0)
- {
- throw new ArgumentOutOfRangeException("standardDeviations", standardDeviations, "Standard Deviations must be greater than 0 for Bollinger Bands.");
- }
- }
-
- /// Removes the recommended quantity of results from the beginning of the results list
- /// using a reverse-engineering approach. See
- /// documentation for more information.
- /// Indicator
- /// results to evaluate.Time
- /// series of results, pruned.
- public static IEnumerable RemoveWarmupPeriods(this IEnumerable results)
- {
- int removePeriods = results.ToList().FindIndex((BollingerBandsResult x) => x.Width.HasValue);
- return results.Remove(removePeriods);
- }
-
- ///
- /// Balance of Power (aka Balance of Market Power) is a momentum oscillator that depicts the strength of buying and selling pressure.
- ///
- /// See
- /// documentation
- /// for more information.
- ///
- /// Configurable Quote type. See Guide for more information.Historical price quotes.Number of periods for smoothing.Time series of BOP values.Invalid parameter value provided.
- public static IEnumerable GetBop(this IEnumerable quotes, int smoothPeriods = 14) where TQuote : IQuote
- {
- return quotes.ToQuoteD().CalcBop(smoothPeriods);
- }
-
- internal static List CalcBop(this List qdList, int smoothPeriods)
- {
- ValidateBop(smoothPeriods);
- int count = qdList.Count;
- List list = new List(count);
- double[] array = qdList.Select((QuoteD x) => (x.High == x.Low) ? double.NaN : ((x.Close - x.Open) / (x.High - x.Low))).ToArray();
- checked
- {
- for (int num = 0; num < count; num++)
- {
- BopResult bopResult = new BopResult(qdList[num].Date);
- list.Add(bopResult);
- if (num >= smoothPeriods - 1)
- {
- double num2 = 0.0;
- for (int num3 = num - smoothPeriods + 1; num3 <= num; num3++)
- {
- num2 += array[num3];
- }
- bopResult.Bop = (num2 / (double)smoothPeriods).NaN2Null();
- }
- }
- return list;
- }
- }
-
- private static void ValidateBop(int smoothPeriods)
- {
- if (smoothPeriods <= 0)
- {
- throw new ArgumentOutOfRangeException("smoothPeriods", smoothPeriods, "Smoothing periods must be greater than 0 for BOP.");
- }
- }
-
- /// Removes the recommended quantity of results from the beginning of the results list
- /// using a reverse-engineering approach. See
- /// documentation for more information.
- /// Indicator
- /// results to evaluate.Time
- /// series of results, pruned.
- public static IEnumerable RemoveWarmupPeriods(this IEnumerable results)
- {
- int removePeriods = results.ToList().FindIndex((BopResult x) => x.Bop.HasValue);
- return results.Remove(removePeriods);
- }
-
- ///
- /// Commodity Channel Index (CCI) is an oscillator depicting deviation from typical price range, often used to identify cyclical trends.
- ///
- /// See
- /// documentation
- /// for more information.
- ///
- /// Configurable Quote type. See Guide for more information.Historical price quotes.Number of periods in the lookback window.Time series of CCI values.Invalid parameter value provided.
- public static IEnumerable GetCci(this IEnumerable quotes, int lookbackPeriods = 20) where TQuote : IQuote
- {
- return quotes.ToQuoteD().CalcCci(lookbackPeriods);
- }
-
- internal static List CalcCci(this List qdList, int lookbackPeriods)
- {
- ValidateCci(lookbackPeriods);
- int count = qdList.Count;
- List list = new List(count);
- double[] array = new double[count];
- checked
- {
- for (int i = 0; i < count; i++)
- {
- QuoteD quoteD = qdList[i];
- array[i] = (quoteD.High + quoteD.Low + quoteD.Close) / 3.0;
- CciResult cciResult = new CciResult(quoteD.Date);
- list.Add(cciResult);
- if (i + 1 >= lookbackPeriods)
- {
- double num = 0.0;
- for (int j = i + 1 - lookbackPeriods; j <= i; j++)
- {
- num += array[j];
- }
- num /= (double)lookbackPeriods;
- double num2 = 0.0;
- for (int k = i + 1 - lookbackPeriods; k <= i; k++)
- {
- num2 += Math.Abs(num - array[k]);
- }
- num2 /= (double)lookbackPeriods;
- cciResult.Cci = ((num2 == 0.0) ? ((double?)null) : ((array[i] - num) / (0.015 * num2)).NaN2Null());
- }
- }
- return list;
- }
- }
-
- private static void ValidateCci(int lookbackPeriods)
- {
- if (lookbackPeriods <= 0)
- {
- throw new ArgumentOutOfRangeException("lookbackPeriods", lookbackPeriods, "Lookback periods must be greater than 0 for Commodity Channel Index.");
- }
- }
-
- /// Removes the recommended quantity of results from the beginning of the results list
- /// using a reverse-engineering approach. See
- /// documentation for more information.
- /// Indicator
- /// results to evaluate.Time
- /// series of results, pruned.
- public static IEnumerable RemoveWarmupPeriods(this IEnumerable results)
- {
- int removePeriods = results.ToList().FindIndex((CciResult x) => x.Cci.HasValue);
- return results.Remove(removePeriods);
- }
-
- ///
- /// Chaikin Oscillator is the difference between fast and slow Exponential Moving Averages (EMA) of the Accumulation/Distribution Line (ADL).
- ///
- /// See
- /// documentation
- /// for more information.
- ///
- /// Configurable Quote type. See Guide for more information.Historical price quotes.Number of periods for the ADL fast EMA.Number of periods for the ADL slow EMA.Time series of Chaikin Oscillator, Money Flow Volume, and ADL values.Invalid parameter value provided.
- public static IEnumerable GetChaikinOsc(this IEnumerable quotes, int fastPeriods = 3, int slowPeriods = 10) where TQuote : IQuote
- {
- return quotes.ToQuoteD().CalcChaikinOsc(fastPeriods, slowPeriods);
- }
-
- internal static List CalcChaikinOsc(this List qdList, int fastPeriods, int slowPeriods)
- {
- ValidateChaikinOsc(fastPeriods, slowPeriods);
- List list = (from r in qdList.CalcAdl(null)
- select new ChaikinOscResult(r.Date)
- {
- MoneyFlowMultiplier = r.MoneyFlowMultiplier,
- MoneyFlowVolume = r.MoneyFlowVolume,
- Adl = r.Adl
- }).ToList();
- List<(DateTime Date, double)> tpList = list.Select((ChaikinOscResult x) => (Date: x.Date, x.Adl ?? double.NaN)).ToList();
- List list2 = tpList.CalcEma(slowPeriods);
- List list3 = tpList.CalcEma(fastPeriods);
- checked
- {
- for (int num = slowPeriods - 1; num < list.Count; num++)
- {
- ChaikinOscResult chaikinOscResult = list[num];
- EmaResult emaResult = list3[num];
- EmaResult emaResult2 = list2[num];
- chaikinOscResult.Oscillator = emaResult.Ema - emaResult2.Ema;
- }
- return list;
- }
- }
-
- private static void ValidateChaikinOsc(int fastPeriods, int slowPeriods)
- {
- if (fastPeriods <= 0)
- {
- throw new ArgumentOutOfRangeException("fastPeriods", fastPeriods, "Fast lookback periods must be greater than 0 for Chaikin Oscillator.");
- }
- if (slowPeriods <= fastPeriods)
- {
- throw new ArgumentOutOfRangeException("slowPeriods", slowPeriods, "Slow lookback periods must be greater than Fast lookback period for Chaikin Oscillator.");
- }
- }
-
- /// Removes the recommended quantity of results from the beginning of the results list
- /// using a reverse-engineering approach. See
- /// documentation for more information.
- /// Indicator
- /// results to evaluate.Time
- /// series of results, pruned.
- public static IEnumerable RemoveWarmupPeriods(this IEnumerable results)
- {
- checked
- {
- int num = results.ToList().FindIndex((ChaikinOscResult x) => x.Oscillator.HasValue) + 1;
- return results.Remove(num + 100);
- }
- }
-
- ///
- /// Chandelier Exit is typically used for stop-loss and can be computed for both long or short types.
- ///
- /// See
- /// documentation
- /// for more information.
- ///
- /// Configurable Quote type. See Guide for more information.Historical price quotes.Number of periods in the lookback window.Multiplier.Short or Long variant selection.Time series of Chandelier Exit values.Invalid parameter value provided.
- public static IEnumerable GetChandelier(this IEnumerable quotes, int lookbackPeriods = 22, double multiplier = 3.0, ChandelierType type = ChandelierType.Long) where TQuote : IQuote
- {
- return quotes.ToQuoteD().CalcChandelier(lookbackPeriods, multiplier, type);
- }
-
- internal static List CalcChandelier(this List qdList, int lookbackPeriods, double multiplier, ChandelierType type)
- {
- ValidateChandelier(lookbackPeriods, multiplier);
- int count = qdList.Count;
- List list = new List(count);
- List list2 = qdList.CalcAtr(lookbackPeriods).ToList();
- checked
- {
- for (int i = 0; i < count; i++)
- {
- ChandelierResult chandelierResult = new ChandelierResult(qdList[i].Date);
- list.Add(chandelierResult);
- if (i < lookbackPeriods)
- {
- continue;
- }
- double? atr = list2[i].Atr;
- switch (type)
- {
- case ChandelierType.Long:
- {
- double num2 = 0.0;
- for (int k = i + 1 - lookbackPeriods; k <= i; k++)
- {
- QuoteD quoteD2 = qdList[k];
- if (quoteD2.High > num2)
- {
- num2 = quoteD2.High;
- }
- }
- chandelierResult.ChandelierExit = num2 - atr * multiplier;
- break;
- }
- case ChandelierType.Short:
- {
- double num = double.MaxValue;
- for (int j = i + 1 - lookbackPeriods; j <= i; j++)
- {
- QuoteD quoteD = qdList[j];
- if (quoteD.Low < num)
- {
- num = quoteD.Low;
- }
- }
- chandelierResult.ChandelierExit = num + atr * multiplier;
- break;
- }
- default:
- throw new ArgumentOutOfRangeException("type");
- }
- }
- return list;
- }
- }
-
- private static void ValidateChandelier(int lookbackPeriods, double multiplier)
- {
- if (lookbackPeriods <= 0)
- {
- throw new ArgumentOutOfRangeException("lookbackPeriods", lookbackPeriods, "Lookback periods must be greater than 0 for Chandelier Exit.");
- }
- if (multiplier <= 0.0)
- {
- throw new ArgumentOutOfRangeException("multiplier", multiplier, "Multiplier must be greater than 0 for Chandelier Exit.");
- }
- }
-
- /// Removes the recommended quantity of results from the beginning of the results list
- /// using a reverse-engineering approach. See
- /// documentation for more information.
- /// Indicator
- /// results to evaluate.Time
- /// series of results, pruned.
- public static IEnumerable RemoveWarmupPeriods(this IEnumerable results)
- {
- int removePeriods = results.ToList().FindIndex((ChandelierResult x) => x.ChandelierExit.HasValue);
- return results.Remove(removePeriods);
- }
-
- ///
- /// Choppiness Index (CHOP) measures the trendiness or choppiness over N lookback periods
- /// on a scale of 0 to 100.
- ///
- /// See
- /// documentation
- /// for more information.
- ///
- /// Configurable Quote type. See Guide for more information.Historical price quotes.Number of periods in the lookback window.Time series of CHOP values.Invalid parameter value provided.
- public static IEnumerable GetChop(this IEnumerable quotes, int lookbackPeriods = 14) where TQuote : IQuote
- {
- return quotes.ToQuoteD().CalcChop(lookbackPeriods);
- }
-
- internal static List CalcChop(this List qdList, int lookbackPeriods)
- {
- ValidateChop(lookbackPeriods);
- int count = qdList.Count;
- List list = new List(count);
- double[] array = new double[count];
- double[] array2 = new double[count];
- double[] array3 = new double[count];
- checked
- {
- for (int i = 0; i < qdList.Count; i++)
- {
- ChopResult chopResult = new ChopResult(qdList[i].Date);
- list.Add(chopResult);
- if (i <= 0)
- {
- continue;
- }
- array[i] = Math.Max(qdList[i].High, qdList[i - 1].Close);
- array2[i] = Math.Min(qdList[i].Low, qdList[i - 1].Close);
- array3[i] = array[i] - array2[i];
- if (i >= lookbackPeriods)
- {
- double num = array3[i];
- double num2 = array[i];
- double num3 = array2[i];
- for (int j = 1; j < lookbackPeriods; j++)
- {
- num += array3[i - j];
- num2 = Math.Max(num2, array[i - j]);
- num3 = Math.Min(num3, array2[i - j]);
- }
- double num4 = num2 - num3;
- if (num4 != 0.0)
- {
- chopResult.Chop = 100.0 * (Math.Log(num / num4) / Math.Log(lookbackPeriods));
- }
- }
- }
- return list;
- }
- }
-
- private static void ValidateChop(int lookbackPeriods)
- {
- if (lookbackPeriods <= 1)
- {
- throw new ArgumentOutOfRangeException("lookbackPeriods", lookbackPeriods, "Lookback periods must be greater than 1 for CHOP.");
- }
- }
-
- /// Removes the recommended quantity of results from the beginning of the results list
- /// using a reverse-engineering approach. See
- /// documentation for more information.
- /// Indicator
- /// results to evaluate.Time
- /// series of results, pruned.
- public static IEnumerable RemoveWarmupPeriods(this IEnumerable results)
- {
- int removePeriods = results.ToList().FindIndex((ChopResult x) => x.Chop.HasValue);
- return results.Remove(removePeriods);
- }
-
- ///
- /// Chaikin Money Flow (CMF) is the simple moving average of Money Flow Volume (MFV).
- ///
- /// See
- /// documentation
- /// for more information.
- ///
- /// Configurable Quote type. See Guide for more information.Historical price quotes.Number of periods for the MFV moving average.Time series of Chaikin Money Flow and MFV values.Invalid parameter value provided.
- public static IEnumerable GetCmf(this IEnumerable quotes, int lookbackPeriods = 20) where TQuote : IQuote
- {
- return quotes.ToQuoteD().CalcCmf(lookbackPeriods);
- }
-
- internal static List CalcCmf(this List qdList, int lookbackPeriods)
- {
- List<(DateTime, double)> list = qdList.ToTuple(CandlePart.Volume);
- ValidateCmf(lookbackPeriods);
- int count = list.Count;
- List list2 = new List(count);
- List list3 = qdList.CalcAdl(null).ToList();
- checked
- {
- for (int i = 0; i < count; i++)
- {
- AdlResult adlResult = list3[i];
- CmfResult cmfResult = new CmfResult(adlResult.Date)
- {
- MoneyFlowMultiplier = adlResult.MoneyFlowMultiplier,
- MoneyFlowVolume = adlResult.MoneyFlowVolume
- };
- list2.Add(cmfResult);
- if (i >= lookbackPeriods - 1)
- {
- double? num = 0.0;
- double? num2 = 0.0;
- for (int j = i + 1 - lookbackPeriods; j <= i; j++)
- {
- num2 += list[j].Item2;
- num += list3[j].MoneyFlowVolume;
- }
- double? num3 = num / (double)lookbackPeriods;
- double? num4 = num2 / (double)lookbackPeriods;
- if (num4 != 0.0)
- {
- cmfResult.Cmf = num3 / num4;
- }
- }
- }
- return list2;
- }
- }
-
- private static void ValidateCmf(int lookbackPeriods)
- {
- if (lookbackPeriods <= 0)
- {
- throw new ArgumentOutOfRangeException("lookbackPeriods", lookbackPeriods, "Lookback periods must be greater than 0 for Chaikin Money Flow.");
- }
- }
-
- /// Removes the recommended quantity of results from the beginning of the results list
- /// using a reverse-engineering approach. See
- /// documentation for more information.
- /// Indicator
- /// results to evaluate.Time
- /// series of results, pruned.
- public static IEnumerable RemoveWarmupPeriods(this IEnumerable results)
- {
- int removePeriods = results.ToList().FindIndex((CmfResult x) => x.Cmf.HasValue);
- return results.Remove(removePeriods);
- }
-
- ///
- /// The Chande Momentum Oscillator is a momentum indicator depicting the weighted percent of higher prices in financial markets.
- ///
- /// See
- /// documentation
- /// for more information.
- ///
- /// Configurable Quote type. See Guide for more information.Historical price quotes.Number of periods in the lookback window.Time series of CMO values.Invalid parameter value provided.
- public static IEnumerable GetCmo(this IEnumerable quotes, int lookbackPeriods) where TQuote : IQuote
- {
- return quotes.ToTuple(CandlePart.Close).CalcCmo(lookbackPeriods);
- }
-
- public static IEnumerable GetCmo(this IEnumerable results, int lookbackPeriods)
- {
- return results.ToTuple().CalcCmo(lookbackPeriods).SyncIndex(results, SyncType.Prepend);
- }
-
- public static IEnumerable GetCmo(this IEnumerable<(DateTime, double)> priceTuples, int lookbackPeriods)
- {
- return priceTuples.ToSortedList().CalcCmo(lookbackPeriods);
- }
-
- internal static List CalcCmo(this List<(DateTime, double)> tpList, int lookbackPeriods)
- {
- ValidateCmo(lookbackPeriods);
- int count = tpList.Count;
- List list = new List(count);
- List<(bool?, double)> list2 = new List<(bool?, double)>(count);
- double num = double.NaN;
- if (count > 0)
- {
- list.Add(new CmoResult(tpList[0].Item1));
- list2.Add((null, double.NaN));
- num = tpList[0].Item2;
- }
- checked
- {
- for (int i = 1; i < count; i++)
- {
- (DateTime, double) tuple = tpList[i];
- DateTime item = tuple.Item1;
- double item2 = tuple.Item2;
- CmoResult cmoResult = new CmoResult(item);
- list.Add(cmoResult);
- list2.Add(((item2 > num) ? new bool?(true) : ((item2 < num) ? new bool?(false) : ((bool?)null)), Math.Abs(item2 - num)));
- if (i >= lookbackPeriods)
- {
- double num2 = 0.0;
- double num3 = 0.0;
- for (int j = i - lookbackPeriods + 1; j <= i; j++)
- {
- var (flag, num4) = list2[j];
- if (flag.HasValue)
- {
- if (flag == true)
- {
- num2 += num4;
- }
- else
- {
- num3 += num4;
- }
- }
- }
- cmoResult.Cmo = ((num2 + num3 != 0.0) ? (100.0 * (num2 - num3) / (num2 + num3)).NaN2Null() : ((double?)null));
- }
- num = item2;
- }
- return list;
- }
- }
-
- private static void ValidateCmo(int lookbackPeriods)
- {
- if (lookbackPeriods <= 0)
- {
- throw new ArgumentOutOfRangeException("lookbackPeriods", lookbackPeriods, "Lookback periods must be greater than 0 for CMO.");
- }
- }
-
- /// Removes the recommended quantity of results from the beginning of the results list
- /// using a reverse-engineering approach. See
- /// documentation for more information.
- /// Indicator
- /// results to evaluate.Time
- /// series of results, pruned.
- public static IEnumerable RemoveWarmupPeriods(this IEnumerable results)
- {
- int removePeriods = results.ToList().FindIndex((CmoResult x) => x.Cmo.HasValue);
- return results.Remove(removePeriods);
- }
-
- ///
- /// ConnorsRSI is a composite oscillator that incorporates RSI, winning/losing streaks, and percentile gain metrics on scale of 0 to 100.
- ///
- /// See
- /// documentation
- /// for more information.
- ///
- /// Configurable Quote type. See Guide for more information.Historical price quotes.Number of periods in the RSI.Number of periods for streak RSI.Number of periods for the percentile ranking.Time series of ConnorsRSI, RSI, Streak RSI, and Percent Rank values.Invalid parameter value provided.
- public static IEnumerable GetConnorsRsi(this IEnumerable quotes, int rsiPeriods = 3, int streakPeriods = 2, int rankPeriods = 100) where TQuote : IQuote
- {
- return quotes.ToTuple(CandlePart.Close).CalcConnorsRsi(rsiPeriods, streakPeriods, rankPeriods);
- }
-
- public static IEnumerable GetConnorsRsi(this IEnumerable results, int rsiPeriods = 3, int streakPeriods = 2, int rankPeriods = 100)
- {
- return results.ToTuple().CalcConnorsRsi(rsiPeriods, streakPeriods, rankPeriods).SyncIndex(results, SyncType.Prepend);
- }
-
- public static IEnumerable GetConnorsRsi(this IEnumerable<(DateTime, double)> priceTuples, int rsiPeriods = 3, int streakPeriods = 2, int rankPeriods = 100)
- {
- return priceTuples.ToSortedList().CalcConnorsRsi(rsiPeriods, streakPeriods, rankPeriods);
- }
-
- internal static List CalcConnorsRsi(this List<(DateTime, double)> tpList, int rsiPeriods, int streakPeriods, int rankPeriods)
- {
- ValidateConnorsRsi(rsiPeriods, streakPeriods, rankPeriods);
- List list = tpList.CalcStreak(rsiPeriods, rankPeriods);
- checked
- {
- int num = Math.Max(rsiPeriods, Math.Max(streakPeriods, rankPeriods)) + 2;
- int count = list.Count;
- List list2 = (from x in list.Remove(Math.Min(count, 1))
- select ((DateTime Date, double))(Date: x.Date, x.Streak)).ToList().CalcRsi(streakPeriods);
- for (int num2 = streakPeriods + 2; num2 < count; num2++)
- {
- ConnorsRsiResult connorsRsiResult = list[num2];
- RsiResult rsiResult = list2[num2 - 1];
- connorsRsiResult.RsiStreak = rsiResult.Rsi;
- if (num2 + 1 >= num)
- {
- connorsRsiResult.ConnorsRsi = (connorsRsiResult.Rsi + connorsRsiResult.RsiStreak + connorsRsiResult.PercentRank) / 3.0;
- }
- }
- return list;
- }
- }
-
- private static List CalcStreak(this List<(DateTime Date, double Streak)> tpList, int rsiPeriods, int rankPeriods)
- {
- List list = tpList.CalcRsi(rsiPeriods);
- int count = tpList.Count;
- List list2 = new List(count);
- double[] array = new double[count];
- double num = double.NaN;
- int num2 = 0;
- checked
- {
- for (int i = 0; i < count; i++)
- {
- (DateTime Date, double Streak) tuple = tpList[i];
- DateTime item = tuple.Date;
- double item2 = tuple.Streak;
- ConnorsRsiResult connorsRsiResult = new ConnorsRsiResult(item)
- {
- Rsi = list[i].Rsi
- };
- list2.Add(connorsRsiResult);
- if (i == 0)
- {
- num = item2;
- continue;
- }
- num2 = (connorsRsiResult.Streak = ((item2 != num) ? ((item2 > num) ? ((num2 < 0) ? 1 : (num2 + 1)) : ((num2 > 0) ? (-1) : (num2 - 1))) : 0));
- array[i] = ((num <= 0.0) ? double.NaN : ((item2 - num) / num));
- if (i + 1 > rankPeriods)
- {
- int num4 = 0;
- for (int j = i - rankPeriods; j <= i; j++)
- {
- if (array[j] < array[i])
- {
- num4++;
- }
- }
- unchecked
- {
- connorsRsiResult.PercentRank = checked(100 * num4) / rankPeriods;
- }
- }
- num = item2;
- }
- return list2;
- }
- }
-
- private static void ValidateConnorsRsi(int rsiPeriods, int streakPeriods, int rankPeriods)
- {
- if (rsiPeriods <= 1)
- {
- throw new ArgumentOutOfRangeException("rsiPeriods", rsiPeriods, "RSI period for Close price must be greater than 1 for ConnorsRsi.");
- }
- if (streakPeriods <= 1)
- {
- throw new ArgumentOutOfRangeException("streakPeriods", streakPeriods, "RSI period for Streak must be greater than 1 for ConnorsRsi.");
- }
- if (rankPeriods <= 1)
- {
- throw new ArgumentOutOfRangeException("rankPeriods", rankPeriods, "Percent Rank periods must be greater than 1 for ConnorsRsi.");
- }
- }
-
- /// Removes the recommended quantity of results from the beginning of the results list
- /// using a reverse-engineering approach. See
- /// documentation for more information.
- /// Indicator
- /// results to evaluate.Time
- /// series of results, pruned.
- public static IEnumerable RemoveWarmupPeriods(this IEnumerable results)
- {
- int removePeriods = results.ToList().FindIndex((ConnorsRsiResult x) => x.ConnorsRsi.HasValue);
- return results.Remove(removePeriods);
- }
-
- ///
- /// Correlation Coefficient between two quote histories, based on price.
- ///
- /// See
- /// documentation
- /// for more information.
- ///
- /// Configurable Quote type. See Guide for more information.Historical price quotes A for comparison.Historical price quotes B for comparison.Number of periods in the lookback window.
- /// Time series of Correlation Coefficient values.
- /// R², Variance, and Covariance are also included.
- /// Invalid parameter value provided.Invalid quotes provided.
- public static IEnumerable GetCorrelation(this IEnumerable quotesA, IEnumerable quotesB, int lookbackPeriods) where TQuote : IQuote
- {
- List<(DateTime, double)> tpListA = quotesA.ToTuple(CandlePart.Close);
- List<(DateTime, double)> tpListB = quotesB.ToTuple(CandlePart.Close);
- return tpListA.CalcCorrelation(tpListB, lookbackPeriods);
- }
-
- public static IEnumerable GetCorrelation(this IEnumerable quotesA, IEnumerable quotesB, int lookbackPeriods)
- {
- List<(DateTime Date, double Value)> tpListA = quotesA.ToTuple();
- List<(DateTime, double)> tpListB = quotesB.ToTuple();
- return tpListA.CalcCorrelation(tpListB, lookbackPeriods).SyncIndex(quotesA, SyncType.Prepend);
- }
-
- public static IEnumerable GetCorrelation(this IEnumerable<(DateTime, double)> tuplesA, IEnumerable<(DateTime, double)> tuplesB, int lookbackPeriods)
- {
- List<(DateTime, double)> tpListA = tuplesA.ToSortedList();
- List<(DateTime, double)> tpListB = tuplesB.ToSortedList();
- return tpListA.CalcCorrelation(tpListB, lookbackPeriods);
- }
-
- internal static List CalcCorrelation(this List<(DateTime, double)> tpListA, List<(DateTime, double)> tpListB, int lookbackPeriods)
- {
- ValidateCorrelation(tpListA, tpListB, lookbackPeriods);
- int count = tpListA.Count;
- List list = new List(count);
- checked
- {
- for (int i = 0; i < count; i++)
- {
- DateTime item = tpListA[i].Item1;
- DateTime item2 = tpListB[i].Item1;
- if (item != item2)
- {
- throw new InvalidQuotesException("tpListA", item, "Date sequence does not match. Correlation requires matching dates in provided histories.");
- }
- CorrResult corrResult = new CorrResult(item);
- list.Add(corrResult);
- if (i >= lookbackPeriods - 1)
- {
- double[] array = new double[lookbackPeriods];
- double[] array2 = new double[lookbackPeriods];
- int num = 0;
- for (int j = i + 1 - lookbackPeriods; j <= i; j++)
- {
- array[num] = tpListA[j].Item2;
- array2[num] = tpListB[j].Item2;
- num++;
- }
- corrResult.PeriodCorrelation(array, array2);
- }
- }
- return list;
- }
- }
-
- private static void PeriodCorrelation(this CorrResult r, double[] dataA, double[] dataB)
- {
- int num = dataA.Length;
- double num2 = 0.0;
- double num3 = 0.0;
- double num4 = 0.0;
- double num5 = 0.0;
- double num6 = 0.0;
- for (int i = 0; i < num; i = checked(i + 1))
- {
- double num7 = dataA[i];
- double num8 = dataB[i];
- num2 += num7;
- num3 += num8;
- num4 += num7 * num7;
- num5 += num8 * num8;
- num6 += num7 * num8;
- }
- double num9 = num2 / (double)num;
- double num10 = num3 / (double)num;
- double num11 = num4 / (double)num;
- double num12 = num5 / (double)num;
- double num13 = num6 / (double)num;
- double num14 = num11 - num9 * num9;
- double num15 = num12 - num10 * num10;
- double num16 = num13 - num9 * num10;
- double num17 = Math.Sqrt(num14 * num15);
- r.VarianceA = num14.NaN2Null();
- r.VarianceB = num15.NaN2Null();
- r.Covariance = num16.NaN2Null();
- r.Correlation = ((num17 == 0.0) ? ((double?)null) : (num16 / num17).NaN2Null());
- r.RSquared = r.Correlation * r.Correlation;
- }
-
- private static void ValidateCorrelation(List<(DateTime, double)> quotesA, List<(DateTime, double)> quotesB, int lookbackPeriods)
- {
- if (lookbackPeriods <= 0)
- {
- throw new ArgumentOutOfRangeException("lookbackPeriods", lookbackPeriods, "Lookback periods must be greater than 0 for Correlation.");
- }
- if (quotesA.Count != quotesB.Count)
- {
- throw new InvalidQuotesException("quotesB", "B quotes should have at least as many records as A quotes for Correlation.");
- }
- }
-
- /// Removes the recommended quantity of results from the beginning of the results list
- /// using a reverse-engineering approach. See
- /// documentation for more information.
- /// Indicator
- /// results to evaluate.Time
- /// series of results, pruned.
- public static IEnumerable RemoveWarmupPeriods(this IEnumerable results)
- {
- int removePeriods = results.ToList().FindIndex((CorrResult x) => x.Correlation.HasValue);
- return results.Remove(removePeriods);
- }
-
- ///
- /// Double Exponential Moving Average (DEMA) of the price.
- ///
- /// See
- /// documentation
- /// for more information.
- ///
- /// Configurable Quote type. See Guide for more information.Historical price quotes.Number of periods in the lookback window.Time series of Double EMA values.Invalid parameter value provided.
- public static IEnumerable GetDema(this IEnumerable quotes, int lookbackPeriods) where TQuote : IQuote
- {
- return quotes.ToTuple(CandlePart.Close).CalcDema(lookbackPeriods);
- }
-
- public static IEnumerable GetDema(this IEnumerable results, int lookbackPeriods)
- {
- return results.ToTuple().CalcDema(lookbackPeriods).SyncIndex(results, SyncType.Prepend);
- }
-
- public static IEnumerable GetDema(this IEnumerable<(DateTime, double)> priceTuples, int lookbackPeriods)
- {
- return priceTuples.ToSortedList().CalcDema(lookbackPeriods);
- }
-
- internal static List CalcDema(this List<(DateTime, double)> tpList, int lookbackPeriods)
- {
- ValidateDema(lookbackPeriods);
- int count = tpList.Count;
- List list = new List(count);
- checked
- {
- double num = 2.0 / (double)(lookbackPeriods + 1);
- double? num2 = 0.0;
- int num3 = Math.Min(lookbackPeriods, count);
- for (int i = 0; i < num3; i++)
- {
- num2 += tpList[i].Item2;
- }
- num2 /= (double)lookbackPeriods;
- double? num4 = num2;
- for (int j = 0; j < count; j++)
- {
- (DateTime, double) tuple = tpList[j];
- DateTime item = tuple.Item1;
- double item2 = tuple.Item2;
- DemaResult demaResult = new DemaResult(item);
- list.Add(demaResult);
- if (j > lookbackPeriods - 1)
- {
- double? num5 = num2 + num * (item2 - num2);
- double? num6 = num4 + num * (num5 - num4);
- demaResult.Dema = (2.0 * num5 - num6).NaN2Null();
- num2 = num5;
- num4 = num6;
- }
- else if (j == lookbackPeriods - 1)
- {
- demaResult.Dema = 2.0 * num2 - num4;
- }
- }
- return list;
- }
- }
-
- private static void ValidateDema(int lookbackPeriods)
- {
- if (lookbackPeriods <= 0)
- {
- throw new ArgumentOutOfRangeException("lookbackPeriods", lookbackPeriods, "Lookback periods must be greater than 0 for DEMA.");
- }
- }
-
- /// Removes the recommended quantity of results from the beginning of the results list
- /// using a reverse-engineering approach. See
- /// documentation for more information.
- /// Indicator
- /// results to evaluate.Time
- /// series of results, pruned.
- public static IEnumerable RemoveWarmupPeriods(this IEnumerable results)
- {
- checked
- {
- int num = results.ToList().FindIndex((DemaResult x) => x.Dema.HasValue) + 1;
- return results.Remove(2 * num + 100);
- }
- }
-
- ///
- /// 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 IEnumerable GetDoji(this IEnumerable quotes, double maxPriceChangePercent = 0.1) where TQuote : IQuote
- {
- return quotes.CalcDoji(maxPriceChangePercent);
- }
-
- ///
- /// 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.
- internal static List CalcDoji(this IEnumerable quotes, double maxPriceChangePercent) where TQuote : IQuote
- {
- ValidateDoji(maxPriceChangePercent);
- List list = quotes.ToCandleResults();
- maxPriceChangePercent /= 100.0;
- int count = list.Count;
- for (int i = 0; i < count; i = checked(i + 1))
- {
- CandleResult candleResult = list[i];
- if (candleResult.Candle.Open != 0m && Math.Abs((double)(candleResult.Candle.Close / candleResult.Candle.Open) - 1.0) <= maxPriceChangePercent)
- {
- candleResult.Price = candleResult.Candle.Close;
- candleResult.Match = Match.Neutral;
- }
- }
- return list;
- }
-
- private static void ValidateDoji(double maxPriceChangePercent)
- {
- if ((maxPriceChangePercent < 0.0 || maxPriceChangePercent > 0.5) ? true : false)
- {
- throw new ArgumentOutOfRangeException("maxPriceChangePercent", maxPriceChangePercent, "Maximum Percent Change must be between 0 and 0.5 for Doji (0% to 0.5%).");
- }
- }
-
- ///
- /// Donchian Channels, also called Price Channels, are derived from highest High and lowest Low values over a lookback window.
- ///
- /// See
- /// documentation
- /// for more information.
- ///
- /// Configurable Quote type. See Guide for more information.Historical price quotes.Number of periods in the lookback window.Time series of Donchian Channel values.Invalid parameter value provided.
- public static IEnumerable GetDonchian(this IEnumerable quotes, int lookbackPeriods = 20) where TQuote : IQuote
- {
- return quotes.ToSortedList().CalcDonchian(lookbackPeriods);
- }
-
- internal static List CalcDonchian(this List quotesList, int lookbackPeriods) where TQuote : IQuote
- {
- ValidateDonchian(lookbackPeriods);
- int count = quotesList.Count;
- List list = new List(count);
- checked
- {
- for (int i = 0; i < count; i++)
- {
- DonchianResult donchianResult = new DonchianResult(quotesList[i].Date);
- list.Add(donchianResult);
- if (i < lookbackPeriods)
- {
- continue;
- }
- decimal num = default(decimal);
- decimal num2 = decimal.MaxValue;
- for (int j = i - lookbackPeriods; j < i; j++)
- {
- TQuote val = quotesList[j];
- if (val.High > num)
- {
- num = val.High;
- }
- if (val.Low < num2)
- {
- num2 = val.Low;
- }
- }
- donchianResult.UpperBand = num;
- donchianResult.LowerBand = num2;
- donchianResult.Centerline = (donchianResult.UpperBand + donchianResult.LowerBand) / (decimal?)2m;
- decimal? centerline = donchianResult.Centerline;
- donchianResult.Width = (((centerline.GetValueOrDefault() == default(decimal)) & centerline.HasValue) ? ((decimal?)null) : ((donchianResult.UpperBand - donchianResult.LowerBand) / donchianResult.Centerline));
- }
- return list;
- }
- }
-
- private static void ValidateDonchian(int lookbackPeriods)
- {
- if (lookbackPeriods <= 0)
- {
- throw new ArgumentOutOfRangeException("lookbackPeriods", lookbackPeriods, "Lookback periods must be greater than 0 for Donchian Channel.");
- }
- }
-
- /// Removes non-essential records containing null values with unique consideration for
- /// this indicator. See
- /// documentation for more information.
- /// Indicator results to evaluate.Time series of
- /// indicator results, condensed.
- public static IEnumerable Condense(this IEnumerable results)
- {
- List list = results.ToList();
- list.RemoveAll((DonchianResult x) => !x.UpperBand.HasValue && !x.LowerBand.HasValue && !x.Centerline.HasValue);
- return list.ToSortedList();
- }
-
- /// Removes the recommended quantity of results from the beginning of the results list
- /// using a reverse-engineering approach. See
- /// documentation for more information.
- /// Indicator
- /// results to evaluate.Time
- /// series of results, pruned.
- public static IEnumerable RemoveWarmupPeriods(this IEnumerable results)
- {
- int removePeriods = results.ToList().FindIndex((DonchianResult x) => x.Width.HasValue);
- return results.Remove(removePeriods);
- }
-
- ///
- /// Detrended Price Oscillator (DPO) depicts the difference between price and an offset simple moving average.
- ///
- /// See
- /// documentation
- /// for more information.
- ///
- /// Configurable Quote type. See Guide for more information.Historical price quotes.Number of periods in the lookback window.Time series of DPO values.Invalid parameter value provided.
- public static IEnumerable GetDpo(this IEnumerable quotes, int lookbackPeriods) where TQuote : IQuote
- {
- return quotes.ToTuple(CandlePart.Close).CalcDpo(lookbackPeriods);
- }
-
- public static IEnumerable GetDpo(this IEnumerable results, int lookbackPeriods)
- {
- return results.ToTuple().CalcDpo(lookbackPeriods).SyncIndex(results, SyncType.Prepend);
- }
-
- public static IEnumerable GetDpo(this IEnumerable<(DateTime, double)> priceTuples, int lookbackPeriods)
- {
- return priceTuples.ToSortedList().CalcDpo(lookbackPeriods);
- }
-
- internal static List CalcDpo(this List<(DateTime, double)> tpList, int lookbackPeriods)
- {
- ValidateDpo(lookbackPeriods);
- int count = tpList.Count;
- checked
- {
- int num = unchecked(lookbackPeriods / 2) + 1;
- List list = tpList.GetSma(lookbackPeriods).ToList();
- List list2 = new List(count);
- for (int i = 0; i < count; i++)
- {
- (DateTime, double) tuple = tpList[i];
- DateTime item = tuple.Item1;
- double item2 = tuple.Item2;
- DpoResult dpoResult = new DpoResult(item);
- list2.Add(dpoResult);
- if (i >= lookbackPeriods - num - 1 && i < count - num)
- {
- SmaResult smaResult = list[i + num];
- dpoResult.Sma = smaResult.Sma;
- dpoResult.Dpo = ((!smaResult.Sma.HasValue) ? ((double?)null) : (item2 - smaResult.Sma).NaN2Null());
- }
- }
- return list2;
- }
- }
-
- private static void ValidateDpo(int lookbackPeriods)
- {
- if (lookbackPeriods <= 0)
- {
- throw new ArgumentOutOfRangeException("lookbackPeriods", lookbackPeriods, "Lookback periods must be greater than 0 for DPO.");
- }
- }
-
- ///
- /// McGinley Dynamic is a more responsive variant of exponential moving average.
- ///
- /// See
- /// documentation
- /// for more information.
- ///
- /// Configurable Quote type. See Guide for more information.Historical price quotes.Number of periods in the lookback window.Optional. Range adjustment factor.Time series of Dynamic values.Invalid parameter value provided.
- public static IEnumerable GetDynamic(this IEnumerable quotes, int lookbackPeriods, double kFactor = 0.6) where TQuote : IQuote
- {
- return quotes.ToTuple(CandlePart.Close).CalcDynamic(lookbackPeriods, kFactor);
- }
-
- public static IEnumerable GetDynamic(this IEnumerable