From 71f49e88155d2934893fbd922d1b2082b6238843 Mon Sep 17 00:00:00 2001 From: Miha Kralj Date: Tue, 24 Sep 2024 20:37:25 -0700 Subject: [PATCH 1/5] benchmarks --- QuanTAlib.sln | 9 +++- benchmark/benchmark.csproj | 28 ++++++++++++ benchmark/growthBench.cs | 84 +++++++++++++++++++++++++++++++++++ docs/indicators/indicators.md | 2 +- 4 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 benchmark/benchmark.csproj create mode 100644 benchmark/growthBench.cs diff --git a/QuanTAlib.sln b/QuanTAlib.sln index a6ac4ee3..7b1d3247 100644 --- a/QuanTAlib.sln +++ b/QuanTAlib.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31903.59 @@ -15,6 +15,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "quantower", "quantower", "{ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Statistics", "quantower\Statistics\Statistics.csproj", "{B6D3EB11-63B6-430F-B526-E1981B3D8214}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "benchmark", "benchmark\benchmark.csproj", "{6629C6D2-FC34-4BDF-AEF4-A5859E05981E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -44,9 +46,12 @@ Global {B6D3EB11-63B6-430F-B526-E1981B3D8214}.Debug|Any CPU.Build.0 = Debug|Any CPU {B6D3EB11-63B6-430F-B526-E1981B3D8214}.Release|Any CPU.ActiveCfg = Release|Any CPU {B6D3EB11-63B6-430F-B526-E1981B3D8214}.Release|Any CPU.Build.0 = Release|Any CPU + {6629C6D2-FC34-4BDF-AEF4-A5859E05981E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6629C6D2-FC34-4BDF-AEF4-A5859E05981E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6629C6D2-FC34-4BDF-AEF4-A5859E05981E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6629C6D2-FC34-4BDF-AEF4-A5859E05981E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {B6D3EB11-63B6-430F-B526-E1981B3D8214} = {A8D9AE68-24E3-476C-BB98-244541BB4B43} EndGlobalSection EndGlobal - diff --git a/benchmark/benchmark.csproj b/benchmark/benchmark.csproj new file mode 100644 index 00000000..0940953e --- /dev/null +++ b/benchmark/benchmark.csproj @@ -0,0 +1,28 @@ + + + + Exe + net8.0 + false + enable + latest + false + + + + + + + + + + + + true + AnyCPU + pdbonly + true + true + + + \ No newline at end of file diff --git a/benchmark/growthBench.cs b/benchmark/growthBench.cs new file mode 100644 index 00000000..c01cdd8f --- /dev/null +++ b/benchmark/growthBench.cs @@ -0,0 +1,84 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Running; + +namespace QuanTAlib; + +public class Program +{ + public static void Main(string[] args) + { + var config = DefaultConfig.Instance + .WithOption(ConfigOptions.DisableOptimizationsValidator, true); + BenchmarkRunner.Run(config); + } +} + +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 3, iterationCount: 5)] +public class EmaBenchmark +{ + private const int Period = 10; + private const int Length = 100_000; + private GbmFeed gbm = null!; + private TSeries inputs = null!; + + [GlobalSetup] + public void Setup() + { + gbm = new GbmFeed(); + inputs = new(); + + for (int i = 0; i < Length; i++) + { + TBar item = gbm.Generate(DateTime.Now); + inputs.Add(new TValue(item.Time, item.Close, true, true)); + } + } + + [Benchmark] + public void Afirma_bench() + { + Afirma ma1 = new(Period); + for (int i = 0; i < Length; i++) + { + TValue item = gbm.Generate(DateTime.Now).Close; + ma1.Calc(item); + } + + } + + [Benchmark] + public void Alma_bench() + { + Alma ma1 = new(Period); + for (int i = 0; i < Length; i++) + { + TValue item = gbm.Generate(DateTime.Now).Close; + ma1.Calc(item); + } + } + + [Benchmark] + public void Dema_bench() + { + Dema ma1 = new(Period); + for (int i = 0; i < Length; i++) + { + TValue item = gbm.Generate(DateTime.Now).Close; + ma1.Calc(item); + } + } + + [Benchmark] + public void Ema_bench() + { + Ema ma1 = new(Period); + for (int i = 0; i < Length; i++) + { + TValue item = gbm.Generate(DateTime.Now).Close; + ma1.Calc(item); + } + } +} \ No newline at end of file diff --git a/docs/indicators/indicators.md b/docs/indicators/indicators.md index 8de0cd95..e26a8a65 100644 --- a/docs/indicators/indicators.md +++ b/docs/indicators/indicators.md @@ -14,7 +14,7 @@ |HLCC4 - Weighted Price|`️.HLCC4`||`WclPrice`|| |
|||| |**AVERAGES & TRENDS**|**QuanTALib**|Skender.Stock|TALib.NETCore|Tulip.NETCore|Trady| -|AFIRMA - Autoregressive Finite Impulse Response Moving Average|`✔️`|||| +|AFIRMA - Autoregressive Finite Impulse Response Moving Average|`Afirma`|||| |ALMA - Arnaud Legoux Moving Average|`Alma`|`✔️`||| |⭐DEMA - Double EMA Average|`Dema`|`⭐`|`⭐`|`⭐`|| |DSMA - Deviation Scaled Moving Average|`Dsma`|||| From 148f0ea8469178a46ef2973dc2a4bfa42554dae2 Mon Sep 17 00:00:00 2001 From: Miha Kralj Date: Thu, 26 Sep 2024 10:44:09 -0700 Subject: [PATCH 2/5] dependabot --- benchmark/growthBench.cs | 15 +- docs/_sidebar.md | 35 +- docs/essays/realtime.md | 43 +++ docs/indicators/averages/afirma/afirma.md | 46 +-- docs/indicators/averages/afirma/analysis.md | 60 +++ docs/indicators/averages/afirma/calc.md | 67 ++++ docs/indicators/averages/afirma/charts.dib | 14 +- docs/indicators/averages/afirma/charts.md | 4 +- .../averages/afirma/img/AFIRMA6_B.svg | 330 ++++++++++++++++ .../{AFIRMA10_Chirp.svg => AFIRMA6_Chirp.svg} | 158 ++++---- ...AFIRMA10_ChirpG.svg => AFIRMA6_ChirpG.svg} | 158 ++++---- ...IRMA10_Complex.svg => AFIRMA6_Complex.svg} | 158 ++++---- .../averages/afirma/img/AFIRMA6_Gauss.svg | 327 ++++++++++++++++ .../averages/afirma/img/AFIRMA6_HF.svg | 330 ++++++++++++++++ ...IRMA10_Impulse.svg => AFIRMA6_Impulse.svg} | 62 +-- .../averages/afirma/img/AFIRMA6_ImpulseHF.svg | 320 +++++++++++++++ .../averages/afirma/img/AFIRMA6_Market.svg | 357 +++++++++++++++++ ...MA10_Sawtooth.svg => AFIRMA6_Sawtooth.svg} | 114 +++--- ..._SawtoothHF.svg => AFIRMA6_SawtoothHF.svg} | 158 ++++---- .../{AFIRMA10_Sine.svg => AFIRMA6_Sine.svg} | 158 ++++---- .../{AFIRMA10_SineG.svg => AFIRMA6_SineG.svg} | 158 ++++---- .../{AFIRMA10_Spike.svg => AFIRMA6_Spike.svg} | 64 +-- ...MA10_Triangle.svg => AFIRMA6_Triangle.svg} | 158 ++++---- .../averages/afirma/img/AFIRMA6_White.svg | 335 ++++++++++++++++ docs/indicators/averages/alma/analysis.md | 51 +++ docs/indicators/averages/alma/calc.md | 45 +++ docs/indicators/averages/dema/analysis.md | 65 ++++ docs/indicators/averages/dema/calc.md | 48 +++ docs/indicators/averages/dsma/analysis.md | 0 docs/indicators/averages/dsma/calc.md | 0 docs/indicators/averages/dwma/analysis.md | 0 docs/indicators/averages/dwma/calc.md | 0 docs/indicators/averages/ema/analysis.md | 0 docs/indicators/averages/ema/calc.md | 0 docs/indicators/averages/sma/calc.md | 0 docs/indicators/averages/tema/analysis.md | 0 docs/indicators/averages/tema/calc.md | 52 +++ docs/indicators/averages/tema/charts.dib | 60 +++ docs/indicators/averages/tema/charts.md | 3 + .../AFIRMA10_B.svg => tema/img/TEMA10_B.svg} | 158 ++++---- .../averages/tema/img/TEMA10_Chirp.svg | 334 ++++++++++++++++ .../averages/tema/img/TEMA10_ChirpG.svg | 352 +++++++++++++++++ .../averages/tema/img/TEMA10_Complex.svg | 334 ++++++++++++++++ .../img/TEMA10_Gauss.svg} | 158 ++++---- .../img/TEMA10_HF.svg} | 158 ++++---- .../averages/tema/img/TEMA10_Impulse.svg | 346 +++++++++++++++++ .../img/TEMA10_ImpulseHF.svg} | 158 ++++---- .../img/TEMA10_Market.svg} | 158 ++++---- .../averages/tema/img/TEMA10_Sawtooth.svg | 364 ++++++++++++++++++ .../averages/tema/img/TEMA10_SawtoothHF.svg | 336 ++++++++++++++++ .../averages/tema/img/TEMA10_Sine.svg | 334 ++++++++++++++++ .../averages/tema/img/TEMA10_SineG.svg | 347 +++++++++++++++++ .../averages/tema/img/TEMA10_Spike.svg | 339 ++++++++++++++++ .../averages/tema/img/TEMA10_Triangle.svg | 355 +++++++++++++++++ .../img/TEMA10_White.svg} | 158 ++++---- docs/readme.md | 24 +- lib/averages/Afirma.cs | 233 +++++++---- lib/averages/Ama.cs | 89 +++++ quantower/Averages/AfirmaIndicator.cs | 28 +- quantower/Averages/AmaIndicator.cs | 25 ++ 60 files changed, 7437 insertions(+), 1304 deletions(-) create mode 100644 docs/essays/realtime.md create mode 100644 docs/indicators/averages/afirma/analysis.md create mode 100644 docs/indicators/averages/afirma/calc.md create mode 100644 docs/indicators/averages/afirma/img/AFIRMA6_B.svg rename docs/indicators/averages/afirma/img/{AFIRMA10_Chirp.svg => AFIRMA6_Chirp.svg} (78%) rename docs/indicators/averages/afirma/img/{AFIRMA10_ChirpG.svg => AFIRMA6_ChirpG.svg} (79%) rename docs/indicators/averages/afirma/img/{AFIRMA10_Complex.svg => AFIRMA6_Complex.svg} (79%) create mode 100644 docs/indicators/averages/afirma/img/AFIRMA6_Gauss.svg create mode 100644 docs/indicators/averages/afirma/img/AFIRMA6_HF.svg rename docs/indicators/averages/afirma/img/{AFIRMA10_Impulse.svg => AFIRMA6_Impulse.svg} (89%) create mode 100644 docs/indicators/averages/afirma/img/AFIRMA6_ImpulseHF.svg create mode 100644 docs/indicators/averages/afirma/img/AFIRMA6_Market.svg rename docs/indicators/averages/afirma/img/{AFIRMA10_Sawtooth.svg => AFIRMA6_Sawtooth.svg} (84%) rename docs/indicators/averages/afirma/img/{AFIRMA10_SawtoothHF.svg => AFIRMA6_SawtoothHF.svg} (79%) rename docs/indicators/averages/afirma/img/{AFIRMA10_Sine.svg => AFIRMA6_Sine.svg} (79%) rename docs/indicators/averages/afirma/img/{AFIRMA10_SineG.svg => AFIRMA6_SineG.svg} (79%) rename docs/indicators/averages/afirma/img/{AFIRMA10_Spike.svg => AFIRMA6_Spike.svg} (89%) rename docs/indicators/averages/afirma/img/{AFIRMA10_Triangle.svg => AFIRMA6_Triangle.svg} (80%) create mode 100644 docs/indicators/averages/afirma/img/AFIRMA6_White.svg create mode 100644 docs/indicators/averages/alma/analysis.md create mode 100644 docs/indicators/averages/alma/calc.md create mode 100644 docs/indicators/averages/dema/analysis.md create mode 100644 docs/indicators/averages/dema/calc.md create mode 100644 docs/indicators/averages/dsma/analysis.md create mode 100644 docs/indicators/averages/dsma/calc.md create mode 100644 docs/indicators/averages/dwma/analysis.md create mode 100644 docs/indicators/averages/dwma/calc.md create mode 100644 docs/indicators/averages/ema/analysis.md create mode 100644 docs/indicators/averages/ema/calc.md create mode 100644 docs/indicators/averages/sma/calc.md create mode 100644 docs/indicators/averages/tema/analysis.md create mode 100644 docs/indicators/averages/tema/calc.md create mode 100644 docs/indicators/averages/tema/charts.dib create mode 100644 docs/indicators/averages/tema/charts.md rename docs/indicators/averages/{afirma/img/AFIRMA10_B.svg => tema/img/TEMA10_B.svg} (79%) create mode 100644 docs/indicators/averages/tema/img/TEMA10_Chirp.svg create mode 100644 docs/indicators/averages/tema/img/TEMA10_ChirpG.svg create mode 100644 docs/indicators/averages/tema/img/TEMA10_Complex.svg rename docs/indicators/averages/{afirma/img/AFIRMA10_Gauss.svg => tema/img/TEMA10_Gauss.svg} (79%) rename docs/indicators/averages/{afirma/img/AFIRMA10_HF.svg => tema/img/TEMA10_HF.svg} (78%) create mode 100644 docs/indicators/averages/tema/img/TEMA10_Impulse.svg rename docs/indicators/averages/{afirma/img/AFIRMA10_ImpulseHF.svg => tema/img/TEMA10_ImpulseHF.svg} (78%) rename docs/indicators/averages/{afirma/img/AFIRMA10_Market.svg => tema/img/TEMA10_Market.svg} (80%) create mode 100644 docs/indicators/averages/tema/img/TEMA10_Sawtooth.svg create mode 100644 docs/indicators/averages/tema/img/TEMA10_SawtoothHF.svg create mode 100644 docs/indicators/averages/tema/img/TEMA10_Sine.svg create mode 100644 docs/indicators/averages/tema/img/TEMA10_SineG.svg create mode 100644 docs/indicators/averages/tema/img/TEMA10_Spike.svg create mode 100644 docs/indicators/averages/tema/img/TEMA10_Triangle.svg rename docs/indicators/averages/{afirma/img/AFIRMA10_White.svg => tema/img/TEMA10_White.svg} (80%) create mode 100644 lib/averages/Ama.cs create mode 100644 quantower/Averages/AmaIndicator.cs diff --git a/benchmark/growthBench.cs b/benchmark/growthBench.cs index c01cdd8f..6dd123e4 100644 --- a/benchmark/growthBench.cs +++ b/benchmark/growthBench.cs @@ -38,9 +38,9 @@ public class EmaBenchmark } [Benchmark] - public void Afirma_bench() + public void Ema_bench() { - Afirma ma1 = new(Period); + Ema ma1 = new(Period); for (int i = 0; i < Length; i++) { TValue item = gbm.Generate(DateTime.Now).Close; @@ -70,15 +70,4 @@ public class EmaBenchmark ma1.Calc(item); } } - - [Benchmark] - public void Ema_bench() - { - Ema ma1 = new(Period); - for (int i = 0; i < Length; i++) - { - TValue item = gbm.Generate(DateTime.Now).Close; - ma1.Calc(item); - } - } } \ No newline at end of file diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 2bdd1409..cc1650b9 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -1,20 +1,36 @@ * [QuanTAlib](/) + * [Historical vs Real-time analysis](essays/realtime.md) * [Indicators](indicators/indicators.md) * Averages & Trends * [AFIRMA - Adaptive Filtering Integrated Recursive Moving Average](indicators/averages/afirma/afirma.md) - * [AFIRMA Charts](indicators/averages/afirma/charts.md) + * [Calculation](indicators/averages/afirma/calc.md) + * [Analysis](indicators/averages/afirma/analysis.md) + * [Charts](indicators/averages/afirma/charts.md) * [ALMA - Arnaud Legoux Moving Average](indicators/averages/alma/alma.md) - * [ALMA Charts](indicators/averages/alma/charts.md) + * [Calculation](indicators/averages/alma/calc.md) + * [Analysis](indicators/averages/alma/analysis.md) + * [Charts](indicators/averages/alma/charts.md) + * [AMA - Adaptive Moving Average](indicators/averages/ama/ama.md) + * [Calculation](indicators/averages/ama/calc.md) + * [Analysis](indicators/averages/ama/analysis.md) + * [Charts](indicators/averages/ama/charts.md) + * [Convolutiuon] * [DEMA - Double Exponential Moving Average](indicators/averages/dema/dema.md) - * [DEMA Charts](indicators/averages/dema/charts.md) + * [Calculation](indicators/averages/dema/calc.md) + * [Analysis](indicators/averages/dema/analysis.md) + * [Charts](indicators/averages/dema/charts.md) * [DSMA - Deviation Scaled Moving Average](indicators/averages/dsma/dsma.md) - * [DSMA Charts](indicators/averages/dsma/charts.md) - * DWMA - Double Weighted Moving Average - * [DWMA Charts](indicators/averages/dwma/charts.md) + * [Calculation](indicators/averages/dsma/calculation.md) + * [Quality](indicators/averages/dsma/quality.md) + * [Charts](indicators/averages/dsma/charts.md) + * [DWMA - Double Weighted Moving Average](indicators/averages/dwma/calculation.md) + * [Calculation](indicators/averages/dwma/calculation.md) + * [Quality](indicators/averages/dwma/quality.md) + * [Charts](indicators/averages/dwma/charts.md) * [EMA - Exponential Moving Average](indicators/averages/ema/ema.md) * [Calculation](indicators/averages/ema/calculation.md) * [Quality](indicators/averages/ema/quality.md) - * [EMA Charts](indicators/averages/ema/charts.md) + * [Charts](indicators/averages/ema/charts.md) * EPMA - Endpoint Moving Average * FRAMA - Fractal Adaptive Moving Average * FWMA - Fibonacci-Weighted Moving Average @@ -37,7 +53,10 @@ * [Charts](indicators/averages/sma/charts.md) * SMMA - Smoothed Moving Average * T3 - Tillson T3 Moving Average - * TEMA - Triple Exponential Moving Average + * [TEMA - Triple Exponential Moving Average](indicators/averages/tema/tema.md) + * [Calculation](indicators/averages/tema/calc.md) + * [Analysis](indicators/averages/tema/analysis.md) + * [Charts](indicators/averages/tema/charts.md) * TRIMA - Triangular Moving Average * VIDYA - Variable Index Dynamic Average * WMA - Weighted Moving Average diff --git a/docs/essays/realtime.md b/docs/essays/realtime.md new file mode 100644 index 00000000..13a3f46f --- /dev/null +++ b/docs/essays/realtime.md @@ -0,0 +1,43 @@ +# Historical vs. Real-time Indicators: A Tale of Two Approaches + +**Indicators for historical analysis** are like long automation trains. They zoom through a complete set of provided historical data, crunching numbers faster in the series than you can say "bullish pattern." These indicators have the luxury of seeing the big picture all at once, from the oldest to the most current data point. That is allowing them to make end-to-end calculations with a bird's-eye view of market trends. + +On the flip side, **real-time indicators** are more like surfers riding the wave of not-yet-known incoming data. They process information as it arrives, often dealing with updates and corrections to the most recent data point. + +"*Currently the Close value of the bar is at \$3.10. Actually, it is at \$3.20. No, scrape that, it is at \$3.25, which also makes a new High of the current bar.*" + +It's a bit like trying to predict the ocean's next move while you're already on the wave – exciting, but challenging! Unknown upcoming data trends alongside with the constant possiblity of corrections of the last provided value - that makes historical analysis indicators practically useless; they are fine-tuned to calculate an output on a well-known array of all known and valid historical inputs. + + +### The High-Frequency Data Dilemma + +Imagine you attach your system to an active forex or crypto ticker, and you're receiving up to 200 updates per second to form a single one-second bar. 200 updates per second is not uncommon during an active trading rally of the day, sometimes exceeding 500 updates/second. That's a lot of data to process in real-time, right? Let's break it down: + +- In one second: Up to 200 updates +- In one minute: 12,000 updates +- In one hour: 720,000 updates +- In 24 hours: 17,280,000 updates + +Now, if we're talking about gathering 24 hours of one-second bars, we're looking at `86,400` data points (60 seconds * 60 minutes * 24 hours). And every single time we receive a new update (or a signal that a new bar started so the last bar is now sealed), we need to crunch through nearly 100,000 datapoints. And do that 200 times per second. That's the calculation demand that will make even the most hard-core historical analysis indicator choke and give up. + +### The Great Calculation Showdown + +Let's compare how our historical and real-time approaches would handle this data tsunami: + +**Historical Analysis Approach:** + +- Imagine recalculating the entire history with each new or updated data point. It's like rewriting the entire encyclopedia every time you learn a new fact. With 17,280,000 updates in a day , you'd be needing: +- `17,280,000 * 86,400 = 1,492,992,000,000` calculations. +- That's nearly 1.5 trillion calculations! Your poor computer might just decide to pack its bags and go on vacation. + +**Real-time Analysis Approach:** + +- Our real-time indicator doesn't need to recalculate the entire history. It just processes each new (or updated) data point as it arrives, and spits out the result. So, we're looking at a mere 17,280,000 calculations per day, one single calculation per each update. + +### Why This Matters + +This enormous difference in calculation requirements isn't just about saving your computer from a meltdown. It's about providing traders with insights when they use tens of indicators with many parameter variations across hundreds of tracked symbols. Real-time indicators allow for quicker decision-making, more responsive trading strategies, and the ability to catch market movements as they happen. + +So, the next time someone tells you that fine-tuned historical indicators are basically faster than performance of real-time indicators, you can wow them with your newfound knowledge. Just remember, in the world of technical analysis, being real-time isn't just a feature – it's a superpower! + +**Real-time analysis is like having a super-efficient personal assistant who only tells you what's new, while historical analysis is like that friend who insists on retelling you their entire life story every time you meet up for coffee.** \ No newline at end of file diff --git a/docs/indicators/averages/afirma/afirma.md b/docs/indicators/averages/afirma/afirma.md index ec8a714e..db119ae2 100644 --- a/docs/indicators/averages/afirma/afirma.md +++ b/docs/indicators/averages/afirma/afirma.md @@ -1,39 +1,27 @@ -## AFIRMA: Adaptive Filtering Integrated Recursive Moving Average +# AFIRMA: Autoregressive Finite Impulse Response Moving Average -### Concept +## Concept -AFIRMA combines elements of simple moving averages (`SMA`) with adaptive filtering techniques from signal processing. It aims to provide a more responsive indicator that can quickly adjust to market changes while maintaining stability. +AFIRMA indicator is a hybrid moving average that combines the benefits of digital filtering and cubic spline fitting to provide a smooth and accurate representation of price movement without significant time lag. -### Origin +## Origin -AFIRMA (Adaptive Filtering Integrated Recursive Moving Average) was developed by Clive Bowsher and Roland Meeks in their 2008 paper titled "*The Dynamics of Economic Functions: Modeling and Forecasting the Yield Curve.*" It was created as an improvement over traditional moving averages, designed to adapt more quickly to changes in financial time series data. +The AFIRMA indicator is based on the principles of digital signal processing and curve fitting. It was developed to address the limitations of traditional moving averages, which often suffer from time lag or fail to accurately track price movements. -### Key Features +## Key Features -1. **Adaptive Nature**: AFIRMA adjusts its behavior based on recent price movements, allowing it to respond more quickly to significant changes. -2. **Error-based Adaptation**: It uses the error between its last output and the current input to determine how much to adapt. -3. **Customizable Sensitivity**: The alpha parameter allows fine-tuning the indicator's responsiveness. +- **Digital Filter**: The AFIRMA indicator uses a digital filter to smooth out price movements. +- **Cubic Spline Fitting**: The latest candlesticks are smoothed using cubic spline fitting with the least square method to ensure a seamless transition. +- **Combined Moving Average**: The indicator combines the digital filter and cubic spline fitting to create a smooth moving average that accurately tracks prices without time lag. +- **Customizable Parameters**: The AFIRMA indicator allows users to adjust the Periods, Taps, and Window parameters to fine-tune the indicator's performance. -### Usage +## Advantages -1. **Trend Identification**: AFIRMA can help identify the start or end of trends more quickly than traditional moving averages. -2. **Signal Generation**: Traders may use crossovers of AFIRMA with price or other indicators to generate buy/sell signals. -3. **Dynamic Support/Resistance**: The AFIRMA line can act as a dynamic support or resistance level. -4. **Volatility Analysis**: The adaptive nature of AFIRMA can provide insights into market volatility. +- **Accurate Price Tracking**: The AFIRMA indicator provides a smooth and accurate representation of price movement without time lag. +- **Hybrid Approach**: The combination of digital filtering and cubic spline fitting provides a unique and effective approach to moving average calculation. -### Advantages +## Considerations -- More responsive to market changes compared to traditional moving averages. -- Reduces lag typically associated with moving averages. -- Customizable through its `alpha` parameter to suit different market conditions or trading styles. - -### Considerations - -- **Adaptive Factor**: Alpha serves as the base adaptive factor. It has a range between 0.0 and 1.0 that determines how quickly the AFIRMA responds to changes in the input data. -- **Error Sensitivity**: Alpha is used in calculating the adaptive factor, which is based on the error between the current input and the last AFIRMA value. -- **Balancing Stability and Responsiveness**: A smaller alpha (closer to 0.0) makes the AFIRMA more stable but less responsive to recent changes. -A larger alpha (closer to 1) makes the AFIRMA more responsive to recent changes but potentially more volatile. -- **Fine-tuning the Indicator**: - - In trending markets, a higher alpha might be preferred to capture price movements more quickly. - - In ranging markets, a lower alpha might be better to reduce false signals from price noise. -- **Adaptive Nature**: The use of alpha allows AFIRMA to adapt its behavior based on recent price movements. When there are significant changes (large errors), the adaptive factor increases, allowing AFIRMA to adjust more quickly. \ No newline at end of file +**Complexity**: The AFIRMA indicator is a complex filter that requires some understanding of digital signal processing and curve fitting to use it right. +- **Parameter Optimization**: Finding the optimal parameters for the AFIRMA indicator may require some experimentation and testing. +- **Computational Resources**: The AFIRMA indicator is computationally more intensive than traditional moving averages due to the use of cubic spline fitting. \ No newline at end of file diff --git a/docs/indicators/averages/afirma/analysis.md b/docs/indicators/averages/afirma/analysis.md new file mode 100644 index 00000000..8392e240 --- /dev/null +++ b/docs/indicators/averages/afirma/analysis.md @@ -0,0 +1,60 @@ +# AFIRMA: Benchmark Analysis + +This analysis evaluates the Autoregressive Finite Impulse Response Moving Average (AFIRMA) across four core benchmarks: accuracy, timeliness, overshooting, and smoothness. These benchmarks provide a comprehensive view of AFIRMA's performance characteristics and serve as a basis for comparison with other moving averages. + +## Accuracy (closeness to the original data) + +AFIRMA generally exhibits high accuracy in representing the original price data due to its sophisticated approach combining digital filtering and cubic spline fitting. + +- **Strengths**: + - The digital filter component helps to reduce noise while preserving important price trends. + - Cubic spline fitting for recent candlesticks ensures that the most current price movements are accurately represented. + +- **Considerations**: + - Accuracy can vary based on parameter settings. Incorrect parameter selection might lead to over-smoothing or under-smoothing, potentially reducing accuracy. + - In highly volatile markets, AFIRMA may sacrifice some accuracy for smoothness, especially if the parameters are set to prioritize noise reduction. + +## Timeliness (amount of lag) + +AFIRMA is designed to minimize lag, which is one of its key advantages over traditional moving averages. + +- **Strengths**: + - The combination of ARMA modeling and FIR filtering allows AFIRMA to respond quickly to price changes. + - Cubic spline fitting of recent data points further reduces lag for the most current price movements. + +- **Considerations**: + - While AFIRMA generally has less lag than traditional MAs, it's not entirely lag-free. Some minimal lag may still be present, especially with longer period settings. + - The amount of lag can be influenced by parameter settings. Optimizing for minimal lag might come at the cost of increased noise sensitivity. + +## Overshooting (overcompensation during reversals) + +AFIRMA's design helps to mitigate overshooting during price reversals, but the extent can vary based on settings and market conditions. + +- **Strengths**: + - The digital filtering component helps to dampen extreme price movements, reducing the likelihood of significant overshooting. + - Cubic spline fitting allows for smoother transitions during reversals, potentially minimizing overshoot. + +- **Considerations**: + - Overshooting can still occur, especially in markets with sudden, sharp reversals. + - The degree of overshooting can be influenced by parameter settings. More aggressive settings might increase responsiveness but also the risk of overshooting. + +## Smoothness (continuous 2nd derivative, less jagged flow) + +AFIRMA generally produces a smoother line than many traditional moving averages, which is one of its defining characteristics. + +- **Strengths**: + - The digital filtering component effectively smooths out minor price fluctuations and noise. + - Cubic spline fitting ensures a smooth transition between historical and current data points. + - The combination of these techniques results in a visually smooth line that can make trend identification easier. + +- **Considerations**: + - The degree of smoothness can be adjusted through parameter settings. Excessive smoothing might lead to a loss of responsiveness to genuine price changes. + - In some cases, the smooth line might mask short-term volatility that could be relevant for certain trading strategies. + +## Conclusion + +AFIRMA demonstrates strong performance across all four benchmarks, particularly excelling in accuracy, timeliness, and smoothness. Its complex approach allows it to balance these often competing characteristics more effectively than many traditional moving averages. + +However, it's important to note that AFIRMA's performance can be significantly influenced by its parameter settings. Optimal use of AFIRMA requires careful tuning of these parameters to balance accuracy, timeliness, overshooting resistance, and smoothness for the specific asset and timeframe being analyzed. + +When compared to other moving averages, AFIRMA generally offers superior or comparable performance across these benchmarks. However, this comes at the cost of increased complexity and computational requirements. Traders and analysts should weigh these factors when deciding whether to incorporate AFIRMA into their technical analysis toolkit. \ No newline at end of file diff --git a/docs/indicators/averages/afirma/calc.md b/docs/indicators/averages/afirma/calc.md new file mode 100644 index 00000000..fcc2255d --- /dev/null +++ b/docs/indicators/averages/afirma/calc.md @@ -0,0 +1,67 @@ +# The Math Behind AFIRMA + +## Components of AFIRMA + +AFIRMA is a hybrid beast, combining two main components: + +- Autoregressive Moving Average (ARMA) +- Finite Impulse Response (FIR) filter + +### ARMA Component + +$ X_t = c + \epsilon_t + \sum_{i=1}^p \phi_i X_{t-i} + \sum_{j=1}^q \theta_j \epsilon_{t-j} $ + +Where: +- $X_t$ is the time series value at time $t$
+- $c$ is a constant
+- $\phi_i$ are the parameters of the autoregressive term
+- $\theta_j$ are the parameters of the moving average term
+- $\epsilon_t$ is white noise
+ +### FIR Component + +$ y[n] = \sum_{i=0}^{N-1} b_i \cdot x[n-i] $ + +Where: +- $y[n]$ is the output signal +- $x[n]$ is the input signal +- $b_i$ are the filter coefficients +- $N$ is the filter order + +### AFIRMA: Putting It All Together + +AFIRMA combines these components and adds cubic spline fitting to the mix. The general form can be expressed as: + +$ AFIRMA_t = ARMA_t + FIR_t + CS_t $ + +Where: +- $ARMA_t$ is the ARMA component at time $t$ +- $FIR_t$ is the FIR component at time $t$ +- $CS_t$ is the cubic spline fitting component at time $t$ + +### Digital Filtering Process + +- The price data is passed through the digital filter to smooth out fluctuations. +- The filter coefficients are optimized based on the specified parameters (Periods, Taps, Window). + +### Cubic Spline Fitting + +For the most recent bars: + +- A cubic spline is fitted to the data points using the least squares method. +- This ensures a smooth transition between the filtered data and the most recent price movements. + +### Parameter Definitions + +The AFIRMA indicator allows for the adjustment of three main parameters: + +- **Periods**: Affects the overall smoothness of the indicator. +- *Taps*: Influences the complexity of the digital filter. +- *Window*: Determines the number of recent bars to which the cubic spline fitting is applied. + +### Computational Process + +- Apply the ARMA model to the price data. +- Pass the result through the FIR filter. +- Apply cubic spline fitting to the most recent data points. +- Combine the results to produce the final AFIRMA value. diff --git a/docs/indicators/averages/afirma/charts.dib b/docs/indicators/averages/afirma/charts.dib index d1c8c640..7d93d388 100644 --- a/docs/indicators/averages/afirma/charts.dib +++ b/docs/indicators/averages/afirma/charts.dib @@ -42,20 +42,22 @@ Dictionary Data = new Dictionary #!csharp String Name = "AFIRMA"; -int p = 10; -double alpha = 0.5; -Func Indicator = period => new Afirma(period,alpha); +int taps = 6; +int periods = 6; +Afirma.WindowType window = Afirma.WindowType.BlackmanHarris; + +Func Indicator = (taps, periods, windows) => new Afirma(taps: taps, periods: periods, window: window); foreach (var item in Data) { string Signal = item.Key; double[] Input = item.Value; TSeries Output = new(); - var ma = Indicator(p); + var ma = Indicator(taps, periods, window); foreach (var value in Input) { Output.Add(ma.Calc(value)); } Plot plt = new(); var p1a = plt.Add.Signal(Input[24..]); p1a.Color = ScottPlot.Colors.Red; p1a.LineWidth = 2; var p1b = plt.Add.Signal(Output.v.ToArray()[24..]); p1b.Color = ScottPlot.Colors.Blue; p1b.LineWidth = 4; - plt.Title($"{Signal} - {Name}({p}, {alpha})"); + plt.Title($"{Signal} - {Name}({taps}, {periods}, {window.ToString()})"); plt.Display(); - plt.SaveSvg($"img/{Name}{p}_{Signal}.svg", 450, 300); + plt.SaveSvg($"img/{Name}{taps}_{Signal}.svg", 450, 300); } diff --git a/docs/indicators/averages/afirma/charts.md b/docs/indicators/averages/afirma/charts.md index d3b95b46..d4b19d30 100644 --- a/docs/indicators/averages/afirma/charts.md +++ b/docs/indicators/averages/afirma/charts.md @@ -1,3 +1,3 @@ -# AFIRMA Charts +# AFIRMA: Charts -![](img/AFIRMA10_Spike.svg) ![](img/AFIRMA10_Impulse.svg) ![](img/AFIRMA10_Triangle.svg) ![](img/AFIRMA10_Sawtooth.svg) ![](img/AFIRMA10_Sine.svg) ![](img/AFIRMA10_Chirp.svg) ![](img/AFIRMA10_White.svg) ![](img/AFIRMA10_Gauss.svg) ![](img/AFIRMA10_B.svg) ![](img/AFIRMA10_HF.svg) ![](img/AFIRMA10_ImpulseHF.svg) ![](img/AFIRMA10_SawtoothHF.svg) ![](img/AFIRMA10_SineG.svg) ![](img/AFIRMA10_ChirpG.svg) ![](img/AFIRMA10_Complex.svg) ![](img/AFIRMA10_Market.svg) +![](img/AFIRMA6_Spike.svg) ![](img/AFIRMA6_Impulse.svg) ![](img/AFIRMA6_Triangle.svg) ![](img/AFIRMA6_Sawtooth.svg) ![](img/AFIRMA6_Sine.svg) ![](img/AFIRMA6_Chirp.svg) ![](img/AFIRMA6_White.svg) ![](img/AFIRMA6_Gauss.svg) ![](img/AFIRMA6_B.svg) ![](img/AFIRMA6_HF.svg) ![](img/AFIRMA6_ImpulseHF.svg) ![](img/AFIRMA6_SawtoothHF.svg) ![](img/AFIRMA6_SineG.svg) ![](img/AFIRMA6_ChirpG.svg) ![](img/AFIRMA6_Complex.svg) ![](img/AFIRMA6_Market.svg) diff --git a/docs/indicators/averages/afirma/img/AFIRMA6_B.svg b/docs/indicators/averages/afirma/img/AFIRMA6_B.svg new file mode 100644 index 00000000..43dcf35a --- /dev/null +++ b/docs/indicators/averages/afirma/img/AFIRMA6_B.svg @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + B - AFIRMA(6, 6, BlackmanHarris) + + diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_Chirp.svg b/docs/indicators/averages/afirma/img/AFIRMA6_Chirp.svg similarity index 78% rename from docs/indicators/averages/afirma/img/AFIRMA10_Chirp.svg rename to docs/indicators/averages/afirma/img/AFIRMA6_Chirp.svg index 1c8e44c7..b1826d0d 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_Chirp.svg +++ b/docs/indicators/averages/afirma/img/AFIRMA6_Chirp.svg @@ -3,10 +3,10 @@ - + - + @@ -81,83 +81,83 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -324,7 +324,7 @@ - - Chirp - AFIRMA(10, 0.5) + + Chirp - AFIRMA(6, 6, BlackmanHarris) diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_ChirpG.svg b/docs/indicators/averages/afirma/img/AFIRMA6_ChirpG.svg similarity index 79% rename from docs/indicators/averages/afirma/img/AFIRMA10_ChirpG.svg rename to docs/indicators/averages/afirma/img/AFIRMA6_ChirpG.svg index 62dd210d..2c61faa3 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_ChirpG.svg +++ b/docs/indicators/averages/afirma/img/AFIRMA6_ChirpG.svg @@ -3,10 +3,10 @@ - + - + @@ -81,83 +81,83 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -342,7 +342,7 @@ - - ChirpG - AFIRMA(10, 0.5) + + ChirpG - AFIRMA(6, 6, BlackmanHarris) diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_Complex.svg b/docs/indicators/averages/afirma/img/AFIRMA6_Complex.svg similarity index 79% rename from docs/indicators/averages/afirma/img/AFIRMA10_Complex.svg rename to docs/indicators/averages/afirma/img/AFIRMA6_Complex.svg index ed8b1c42..4a1b92bc 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_Complex.svg +++ b/docs/indicators/averages/afirma/img/AFIRMA6_Complex.svg @@ -3,10 +3,10 @@ - + - + @@ -81,83 +81,83 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -327,7 +327,7 @@ - - Complex - AFIRMA(10, 0.5) + + Complex - AFIRMA(6, 6, BlackmanHarris) diff --git a/docs/indicators/averages/afirma/img/AFIRMA6_Gauss.svg b/docs/indicators/averages/afirma/img/AFIRMA6_Gauss.svg new file mode 100644 index 00000000..6e6747fb --- /dev/null +++ b/docs/indicators/averages/afirma/img/AFIRMA6_Gauss.svg @@ -0,0 +1,327 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + + + + + + + + + + + + + + + + + + + + + Gauss - AFIRMA(6, 6, BlackmanHarris) + + diff --git a/docs/indicators/averages/afirma/img/AFIRMA6_HF.svg b/docs/indicators/averages/afirma/img/AFIRMA6_HF.svg new file mode 100644 index 00000000..ec93b5b8 --- /dev/null +++ b/docs/indicators/averages/afirma/img/AFIRMA6_HF.svg @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + HF - AFIRMA(6, 6, BlackmanHarris) + + diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_Impulse.svg b/docs/indicators/averages/afirma/img/AFIRMA6_Impulse.svg similarity index 89% rename from docs/indicators/averages/afirma/img/AFIRMA10_Impulse.svg rename to docs/indicators/averages/afirma/img/AFIRMA6_Impulse.svg index 45ddbb94..878244c4 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_Impulse.svg +++ b/docs/indicators/averages/afirma/img/AFIRMA6_Impulse.svg @@ -3,10 +3,10 @@ - + - + @@ -81,26 +81,26 @@ - + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + @@ -125,15 +125,15 @@ - - - - - - - - - + + + + + + + + + @@ -332,7 +332,7 @@ - - Impulse - AFIRMA(10, 0.5) + + Impulse - AFIRMA(6, 6, BlackmanHarris) diff --git a/docs/indicators/averages/afirma/img/AFIRMA6_ImpulseHF.svg b/docs/indicators/averages/afirma/img/AFIRMA6_ImpulseHF.svg new file mode 100644 index 00000000..bff64afa --- /dev/null +++ b/docs/indicators/averages/afirma/img/AFIRMA6_ImpulseHF.svg @@ -0,0 +1,320 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + ImpulseHF - AFIRMA(6, 6, BlackmanHarris) + + diff --git a/docs/indicators/averages/afirma/img/AFIRMA6_Market.svg b/docs/indicators/averages/afirma/img/AFIRMA6_Market.svg new file mode 100644 index 00000000..6f006235 --- /dev/null +++ b/docs/indicators/averages/afirma/img/AFIRMA6_Market.svg @@ -0,0 +1,357 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 64 + + + + 66 + + + + 68 + + + + 70 + + + + 72 + + + + 74 + + + + 76 + + + + 78 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Market - AFIRMA(6, 6, BlackmanHarris) + + diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_Sawtooth.svg b/docs/indicators/averages/afirma/img/AFIRMA6_Sawtooth.svg similarity index 84% rename from docs/indicators/averages/afirma/img/AFIRMA10_Sawtooth.svg rename to docs/indicators/averages/afirma/img/AFIRMA6_Sawtooth.svg index ec5cadb8..a4d68182 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_Sawtooth.svg +++ b/docs/indicators/averages/afirma/img/AFIRMA6_Sawtooth.svg @@ -3,10 +3,10 @@ - + - + @@ -81,61 +81,61 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -349,7 +349,7 @@ - - Sawtooth - AFIRMA(10, 0.5) + + Sawtooth - AFIRMA(6, 6, BlackmanHarris) diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_SawtoothHF.svg b/docs/indicators/averages/afirma/img/AFIRMA6_SawtoothHF.svg similarity index 79% rename from docs/indicators/averages/afirma/img/AFIRMA10_SawtoothHF.svg rename to docs/indicators/averages/afirma/img/AFIRMA6_SawtoothHF.svg index 392508d4..7cdb3f2b 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_SawtoothHF.svg +++ b/docs/indicators/averages/afirma/img/AFIRMA6_SawtoothHF.svg @@ -3,10 +3,10 @@ - + - + @@ -81,83 +81,83 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -326,7 +326,7 @@ - - SawtoothHF - AFIRMA(10, 0.5) + + SawtoothHF - AFIRMA(6, 6, BlackmanHarris) diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_Sine.svg b/docs/indicators/averages/afirma/img/AFIRMA6_Sine.svg similarity index 79% rename from docs/indicators/averages/afirma/img/AFIRMA10_Sine.svg rename to docs/indicators/averages/afirma/img/AFIRMA6_Sine.svg index 5d391a00..c08c7024 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_Sine.svg +++ b/docs/indicators/averages/afirma/img/AFIRMA6_Sine.svg @@ -3,10 +3,10 @@ - + - + @@ -81,83 +81,83 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -324,7 +324,7 @@ - - Sine - AFIRMA(10, 0.5) + + Sine - AFIRMA(6, 6, BlackmanHarris) diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_SineG.svg b/docs/indicators/averages/afirma/img/AFIRMA6_SineG.svg similarity index 79% rename from docs/indicators/averages/afirma/img/AFIRMA10_SineG.svg rename to docs/indicators/averages/afirma/img/AFIRMA6_SineG.svg index 4f8b6d2b..d731a839 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_SineG.svg +++ b/docs/indicators/averages/afirma/img/AFIRMA6_SineG.svg @@ -3,10 +3,10 @@ - + - + @@ -81,83 +81,83 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -340,7 +340,7 @@ - - SineG - AFIRMA(10, 0.5) + + SineG - AFIRMA(6, 6, BlackmanHarris) diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_Spike.svg b/docs/indicators/averages/afirma/img/AFIRMA6_Spike.svg similarity index 89% rename from docs/indicators/averages/afirma/img/AFIRMA10_Spike.svg rename to docs/indicators/averages/afirma/img/AFIRMA6_Spike.svg index ef3d9095..004c96d0 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_Spike.svg +++ b/docs/indicators/averages/afirma/img/AFIRMA6_Spike.svg @@ -3,10 +3,10 @@ - + - + @@ -81,26 +81,26 @@ - + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + @@ -125,16 +125,16 @@ - - - - - - - - - - + + + + + + + + + + @@ -332,7 +332,7 @@ - - Spike - AFIRMA(10, 0.5) + + Spike - AFIRMA(6, 6, BlackmanHarris) diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_Triangle.svg b/docs/indicators/averages/afirma/img/AFIRMA6_Triangle.svg similarity index 80% rename from docs/indicators/averages/afirma/img/AFIRMA10_Triangle.svg rename to docs/indicators/averages/afirma/img/AFIRMA6_Triangle.svg index f4fc099c..e675197d 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_Triangle.svg +++ b/docs/indicators/averages/afirma/img/AFIRMA6_Triangle.svg @@ -3,10 +3,10 @@ - + - + @@ -81,83 +81,83 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -349,7 +349,7 @@ - - Triangle - AFIRMA(10, 0.5) + + Triangle - AFIRMA(6, 6, BlackmanHarris) diff --git a/docs/indicators/averages/afirma/img/AFIRMA6_White.svg b/docs/indicators/averages/afirma/img/AFIRMA6_White.svg new file mode 100644 index 00000000..b649a062 --- /dev/null +++ b/docs/indicators/averages/afirma/img/AFIRMA6_White.svg @@ -0,0 +1,335 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -0.4 + + + + -0.2 + + + + 0 + + + + 0.2 + + + + 0.4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + White - AFIRMA(6, 6, BlackmanHarris) + + diff --git a/docs/indicators/averages/alma/analysis.md b/docs/indicators/averages/alma/analysis.md new file mode 100644 index 00000000..87bf847d --- /dev/null +++ b/docs/indicators/averages/alma/analysis.md @@ -0,0 +1,51 @@ +# ALMA: Benchmark Analysis + +This analysis evaluates the Arnaud Legoux Moving Average (ALMA) across four core benchmarks: accuracy, timeliness, overshooting, and smoothness. These benchmarks provide a comprehensive view of ALMA's performance characteristics and serve as a basis for comparison with other moving averages. + +## Accuracy (closeness to the original data) + +ALMA generally exhibits good accuracy in representing the original price data due to its Gaussian distribution-based weighting system. + +- **Strengths**: + - The Gaussian distribution weighting helps to reduce noise while preserving important price trends. + - The offset parameter allows for fine-tuning of the balance between recent and historical data representation. + +- **Considerations**: + - Accuracy can vary based on parameter settings. Incorrect parameter selection might lead to over-smoothing or under-smoothing, potentially reducing accuracy. + - In highly volatile markets, ALMA may sacrifice some accuracy for smoothness, especially if the sigma parameter is set to prioritize noise reduction. + +## Timeliness (amount of lag) + +ALMA is designed to minimize lag, which is one of its key advantages over traditional moving averages. + +- **Strengths**: + - The offset parameter allows ALMA to be more responsive to recent price changes, potentially reducing lag. + - The ability to adjust the window size provides flexibility in balancing timeliness and stability. + +- **Considerations**: + - While ALMA generally has less lag than traditional MAs, it's not entirely lag-free. Some minimal lag may still be present, especially with larger window sizes. + - The amount of lag can be influenced by parameter settings. Optimizing for minimal lag might come at the cost of increased noise sensitivity. + +## Overshooting (overcompensation during reversals) + +ALMA's design helps to mitigate overshooting during price reversals, but the extent can vary based on settings and market conditions. + +- **Strengths**: + - The Gaussian distribution weighting helps to dampen extreme price movements, reducing the likelihood of significant overshooting. + - The sigma parameter allows for control over the smoothness of transitions, potentially minimizing overshoot. + +- **Considerations**: + - Overshooting can still occur, especially in markets with sudden, sharp reversals. + - The degree of overshooting can be influenced by parameter settings. More aggressive settings (lower sigma, higher offset) might increase responsiveness but also the risk of overshooting. + +## Smoothness (continuous 2nd derivative, less jagged flow) + +ALMA generally produces a smoother line than many traditional moving averages, which is one of its defining characteristics. + +- **Strengths**: + - The Gaussian distribution weighting effectively smooths out minor price fluctuations and noise. + - The sigma parameter provides direct control over the smoothness of the line. + - The resulting smooth line can make trend identification easier. + +- **Considerations**: + - The degree of smoothness can be adjusted through parameter settings. \ No newline at end of file diff --git a/docs/indicators/averages/alma/calc.md b/docs/indicators/averages/alma/calc.md new file mode 100644 index 00000000..7c9bc48b --- /dev/null +++ b/docs/indicators/averages/alma/calc.md @@ -0,0 +1,45 @@ +# The Math Behind ALMA + +## Components of ALMA + +ALMA is a single-formula moving average that incorporates elements of several advanced techniques: + +- Gaussian distribution +- Weighted moving average +- Offset parameter + +### ALMA Formula + +$ ALMA_t = \sum_{i=0}^{n-1} w_i \cdot P_{t-i} $ + +Where: +- $ALMA_t$ is the ALMA value at time $t$ +- $n$ is the window size (number of periods) +- $P_{t-i}$ is the price at time $t-i$ +- $w_i$ are the weights + +### Weight Calculation + +The weights $w_i$ are calculated using a Gaussian distribution function with an offset: + +$ w_i = \exp\left(-\frac{(i - m)^2}{2s^2}\right) $ + +Where: +- $i$ is the position of the price in the window (0 to $n-1$) +- $m$ is the offset of the Gaussian distribution, calculated as $m = \text{floor}(offset \cdot (n - 1))$ +- $s$ is the standard deviation of the Gaussian distribution, calculated as $s = \frac{n}{sigma}$ + +### Parameter Definitions + +ALMA uses three main parameters: + +- **Window size** ($n$): Affects the overall reactivity of the indicator. +- **Offset**: Influences the lag of the moving average. Lower values reduce lag but may increase noise. +- **Sigma**: Controls the smoothness of the indicator. Higher values increase smoothness but may increase lag. + +### Computational Process + +For each new data point: +- Calculate the weights for the entire window. +- Apply these weights to the most recent $n$ prices. +- Sum the weighted prices to produce the final ALMA value. diff --git a/docs/indicators/averages/dema/analysis.md b/docs/indicators/averages/dema/analysis.md new file mode 100644 index 00000000..ee4ada70 --- /dev/null +++ b/docs/indicators/averages/dema/analysis.md @@ -0,0 +1,65 @@ +# DEMA: Benchmark Analysis + +This analysis evaluates the Double Exponential Moving Average (DEMA) across four core benchmarks: accuracy, timeliness, overshooting, and smoothness. + +## Accuracy (closeness to the original data) + +DEMA generally provides a good balance between accuracy and smoothing. + +- **Strengths**: + - The double smoothing process helps to reduce noise while preserving important price trends. + - More accurate than a simple EMA, especially during trend changes. + +- **Considerations**: + - In highly volatile markets, DEMA may sacrifice some accuracy for smoothness. + - Accuracy can vary based on the period setting. Shorter periods increase accuracy but may introduce more noise. + +## Timeliness (amount of lag) + +DEMA is designed to reduce lag compared to traditional moving averages, which is one of its key advantages. + +- **Strengths**: + - The double smoothing formula effectively reduces lag compared to standard EMAs. + - Responds more quickly to price changes than simple or exponential moving averages. + +- **Considerations**: + - While DEMA has less lag than traditional MAs, it's not entirely lag-free. + - Shorter periods reduce lag but may increase sensitivity to noise. + +## Overshooting (overcompensation during reversals) + +DEMA is known for significant overshooting during price reversals, which is one of its main drawbacks. + +- **Weaknesses**: + - Prone to substantial overshooting, especially during sharp price reversals. + - The double exponential smoothing, while reducing lag, can exaggerate price movements during trend changes. + +- **Considerations**: + - Overshooting is particularly pronounced in volatile markets or during sudden trend reversals. + - Shorter periods may further increase the risk and magnitude of overshooting. + - This characteristic can lead to false signals or exaggerated price projections, potentially misleading traders. + +## Smoothness (continuous 2nd derivative, less jagged flow) + +DEMA produces a relatively smooth line, balancing smoothness with responsiveness. + +- **Strengths**: + - Smoother than a standard EMA, making trend identification easier. + - The double smoothing process effectively reduces minor fluctuations. + +- **Considerations**: + - Less smooth than higher-order moving averages or those with explicit smoothing parameters. + - The degree of smoothness is primarily controlled by the period setting, offering less flexibility than some advanced moving averages. + +## Conclusion + +DEMA demonstrates mixed performance across the four benchmarks. It excels in reducing lag and maintains a good degree of smoothness, but its tendency to overshoot significantly during price reversals is a major drawback. + +DEMA's performance is influenced by its single parameter (the period). While this simplicity is an advantage for ease of use, it also means there's less flexibility to mitigate its overshooting tendency. + +Compared to more complex moving averages like AFIRMA or ALMA, DEMA offers simplicity and excellent lag reduction. However, its proneness to overshooting can make it less reliable during volatile market conditions or during trend reversals. + +Traders and analysts should carefully consider DEMA's strengths and weaknesses. While it offers improved lag reduction over simple moving averages, its overshooting characteristic can lead to false signals. This makes it potentially risky to use on its own, especially in volatile markets. +DEMA might be most effectively used in conjunction with other indicators that can help confirm signals and mitigate the risk of false readings due to overshooting. It may be particularly useful in strongly trending markets where its lag reduction is beneficial and the risk of reversal (and thus overshooting) is lower. + +In summary, DEMA's simplicity and lag reduction make it an interesting tool, but its tendency to overshoot means it should be used with caution and preferably as part of a broader analytical approach rather than as a standalone indicator. \ No newline at end of file diff --git a/docs/indicators/averages/dema/calc.md b/docs/indicators/averages/dema/calc.md new file mode 100644 index 00000000..bf9035dc --- /dev/null +++ b/docs/indicators/averages/dema/calc.md @@ -0,0 +1,48 @@ +# The Math Behind DEMA + +## Components of DEMA + +DEMA is composed of two main components: + +1. Exponential Moving Average (EMA) +2. A "double smoothing" factor + +Let's break these down: + +### EMA Calculation + +The Exponential Moving Average (EMA) is calculated as: + +$ EMA_t = \alpha \cdot P_t + (1 - \alpha) \cdot EMA_{t-1} $ + +Where: +- $EMA_t$ is the EMA value at time $t$ +- $P_t$ is the price at time $t$ +- $\alpha$ is the smoothing factor, calculated as $\frac{2}{n+1}$ +- $n$ is the number of periods + +### DEMA Formula + +The DEMA is then calculated using the following formula: + +$ DEMA_t = 2 \cdot EMA_t - EMA(EMA_t) $ + +Where: +- $DEMA_t$ is the DEMA value at time $t$ +- $EMA_t$ is the EMA of the price +- $EMA(EMA_t)$ is the EMA of the EMA + +## Calculation Process + +1. Calculate the EMA of the price series. +2. Calculate another EMA on the result of step 1. +3. Multiply the first EMA by 2. +4. Subtract the second EMA from the result of step 3. + +This process effectively reduces lag while maintaining smoothness. + +## Parameter + +DEMA uses a single parameter: + +- **Period** ($n$): Determines the number of periods used in the EMA calculations. This affects the overall reactivity and smoothness of the indicator. diff --git a/docs/indicators/averages/dsma/analysis.md b/docs/indicators/averages/dsma/analysis.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/indicators/averages/dsma/calc.md b/docs/indicators/averages/dsma/calc.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/indicators/averages/dwma/analysis.md b/docs/indicators/averages/dwma/analysis.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/indicators/averages/dwma/calc.md b/docs/indicators/averages/dwma/calc.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/indicators/averages/ema/analysis.md b/docs/indicators/averages/ema/analysis.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/indicators/averages/ema/calc.md b/docs/indicators/averages/ema/calc.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/indicators/averages/sma/calc.md b/docs/indicators/averages/sma/calc.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/indicators/averages/tema/analysis.md b/docs/indicators/averages/tema/analysis.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/indicators/averages/tema/calc.md b/docs/indicators/averages/tema/calc.md new file mode 100644 index 00000000..631b5647 --- /dev/null +++ b/docs/indicators/averages/tema/calc.md @@ -0,0 +1,52 @@ +# The Math Behind TEMA + +## Components of TEMA + +TEMA is composed of three main components: + +1. Exponential Moving Average (EMA) +2. A "triple smoothing" factor + +Let's break these down: + +### EMA Calculation + +The Exponential Moving Average (EMA) is calculated as: + +$ EMA_t = \alpha \cdot P_t + (1 - \alpha) \cdot EMA_{t-1} $ + +Where: +- $EMA_t$ is the EMA value at time $t$ +- $P_t$ is the price at time $t$ +- $\alpha$ is the smoothing factor, calculated as $\frac{2}{n+1}$ +- $n$ is the number of periods + +### TEMA Formula + +The TEMA is then calculated using the following formula: + +$ TEMA_t = 3 \cdot EMA_t - 3 \cdot EMA(EMA_t) + EMA(EMA(EMA_t)) $ + +Where: +- $TEMA_t$ is the TEMA value at time $t$ +- $EMA_t$ is the EMA of the price +- $EMA(EMA_t)$ is the EMA of the EMA +- $EMA(EMA(EMA_t))$ is the EMA of the EMA of the EMA + +## Calculation Process + +1. Calculate the EMA of the price series (EMA1). +2. Calculate another EMA on the result of step 1 (EMA2). +3. Calculate a third EMA on the result of step 2 (EMA3). +4. Multiply EMA1 by 3. +5. Multiply EMA2 by 3. +6. Subtract EMA2 * 3 from EMA1 * 3. +7. Add EMA3 to the result. + +This process effectively reduces lag while maintaining smoothness and attempting to minimize overshooting. + +## Parameter + +TEMA uses a single parameter: + +- **Period** ($n$): Determines the number of periods used in the EMA calculations. This affects the overall reactivity and smoothness of the indicator. \ No newline at end of file diff --git a/docs/indicators/averages/tema/charts.dib b/docs/indicators/averages/tema/charts.dib new file mode 100644 index 00000000..956337cb --- /dev/null +++ b/docs/indicators/averages/tema/charts.dib @@ -0,0 +1,60 @@ +#!meta + +{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"name":"csharp"}]}} + +#!csharp + +#r "..\..\..\..\lib\obj\Debug\QuanTAlib.dll" + +#r "nuget: ScottPlot" + +using QuanTAlib; +using ScottPlot; +using Microsoft.DotNet.Interactive.Formatting; + +QuanTAlib.Formatters.Initialize(); +Formatter.Register(typeof(ScottPlot.Plot), (p, w) => + w.Write(((ScottPlot.Plot)p).GetSvgXml(600, 300)), HtmlFormatter.MimeType); + +#!csharp + +Dictionary Data = new Dictionary +{ + { "Spike", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { "Impulse", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 } }, + { "Triangle", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2 } }, + { "Sawtooth", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }, + { "Sine", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0.39,0.56,0.72,0.84,0.93,0.99,1,0.97,0.91,0.81,0.68,0.52,0.33,0.14,-0.06,-0.26,-0.44,-0.61,-0.76,-0.87,-0.95,-0.99,-1,-0.96,-0.88,-0.77,-0.63,-0.46,-0.28,-0.08,0.12,0.31,0.49,0.66,0.79,0.9,0.97,1,0.99,0.94,0.85,0.73,0.58,0.41,0.22,0.02,-0.17,-0.37,-0.54,-0.7,-0.83,-0.92,-0.98,-1,-0.98,-0.92,-0.82,-0.69,-0.54,-0.36,-0.17,0.03,0.23,0.42,0.59,0.74 } }, + { "Chirp", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0.93,0.27,-0.59,-1,-0.71,0.05,0.75,1,0.67,0,-0.67,-0.99,-0.85,-0.34,0.31,0.81,1,0.82,0.35,-0.22,-0.71,-0.98,-0.95,-0.66,-0.2,0.31,0.72,0.96,0.98,0.78,0.43,-0.01,-0.43,-0.77,-0.96,-0.99,-0.85,-0.58,-0.23,0.16,0.51,0.79,0.95,1,0.92,0.73,0.47,0.15,-0.17,-0.47,-0.72,-0.9,-0.99,-0.99,-0.9,-0.74,-0.52,-0.26,0.01,0.28,0.53,0.73,0.88,0.97,1,0.97 } }, + { "White", new double[] { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,0.03,-0.4,-0.47,0.19,-0.4,-0.23,0.31,0.41,0.19,0.16,-0.5,-0.31,-0.21,0.25,0.18,-0.48,-0.1,0.38,0.29,-0.38,-0.08,-0.21,0.34,0.01,-0.46,0.28,-0.48,0.11,0.02,-0.37,0.19,-0.2,0.1,0.24,0.08,-0.22,-0.12,0.15,0.36,-0.43,-0.03,-0.32,0.45,-0.5,-0.04,-0.04,-0.08,-0.18,0.13,-0.33,-0.19,0.36,-0.39,0.2,-0.31,0.28,-0.13,-0.07,-0.29,0.37,0.03,-0.25,-0.06,-0.3,-0.08,-0.09 } }, + { "Gauss", new double[] { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,0,0.03,0.11,-0.1,-0.43,-0.08,0.36,-0.04,-0.04,-0.21,-0.3,0.26,0.2,0.28,0.2,0.27,-0.01,-0.1,-0.23,-0.13,-0.41,-0.23,-0.07,-0.21,0.32,-0.18,-0.48,0.3,0.46,-0.2,0.52,-0.81,-0.25,-0.21,-0.12,-0.18,0.18,0.52,0.29,0.44,0.18,-1.2,0.38,0.24,0.06,0.28,0.34,0.3,-0.13,0.19,-0.5,0.59,-0.36,0.22,-0.23,0.24,0.39,0.13,-0.33,-0.57,-0.23,0.49,-0.13,0.76,0.59,0.61 } }, + { "B", new double[] { -0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0.4,-0.4,0,-0.28,0.41,-0.54,0.65,-0.75,0.84,-0.91,0.96,-0.99,1,-0.99,0.96,-0.92,0.85,-0.77,0.67,-0.56,0.44,-0.3,0.17,-0.03,-0.11,0.25,-0.39,0.51,-0.63,0.73,-0.82,0.89,-0.95,0.98,-1,0.99,-0.97,0.93,-0.86,0.78,-0.69,0.58,-0.46,0.33,-0.19,0.05,0.09,-0.23,0.36,-0.49,0.61,-0.71,0.81,-0.88,0.94,-0.98,1,-1,0.98,-0.94,0.88,-0.8,0.71,-0.6,0.48,-0.35,0.22,-0.08,-0.06 } }, + { "HF", new double[] { -0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,-0.6,0.6,0,0.14,-0.76,-0.96,-0.28,0.66,0.99,0.41,-0.54,-1,-0.54,0.42,0.99,0.65,-0.29,-0.96,-0.75,0.15,0.91,0.84,-0.01,-0.85,-0.91,-0.13,0.76,0.96,0.27,-0.66,-0.99,-0.4,0.55,1,0.53,-0.43,-0.99,-0.64,0.3,0.96,0.75,-0.16,-0.92,-0.83,0.02,0.85,0.9,0.12,-0.77,-0.95,-0.26,0.67,0.99,0.4,-0.56,-1,-0.52,0.44,0.99,0.64,-0.3,-0.97,-0.74,0.17,0.92,0.83,-0.03,-0.86 } }, + { "ImpulseHF", new double[] { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,0.05,-0.25,-0.32,-0.09,0.22,0.33,0.14,-0.18,-0.33,-0.18,0.14,0.33,0.22,-0.1,-0.32,-0.25,0.05,0.3,0.28,0,-0.28,-0.3,-0.04,0.25,0.32,0.09,-0.22,-0.33,-0.13,0.18,0.33,0.18,0.86,0.67,0.79,1.1,1.32,1.25,0.95,0.69,0.72,1.01,1.28,1.3,1.04,0.74,0.68,0.91,1.22,1.33,1.13,0.81,0.67,0.83,1.15,1.33,1.21,0.9,0.68,0.75,1.06,1.31,1.28,0.99,0.71 } }, + { "SawtoothHF", new double[] { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,2.7,-0.8,-0.8,3.6,9.3,11.95,10.05,6.3,5,8.3,14.1,17.95,17.25,13.55,11.2,13.25,18.75,23.55,24.2,20.95,17.75,18.45,23.35,28.8,30.8,28.35,24.7,24.05,28,33.75,37,35.65,31.85,28.05,-3.2,1.5,4.8,3.75,-0.8,-4.6,-4.15,0.1,4.25,4.5,0.6,-3.85,-4.75,-1.3,3.35,4.95,2,-2.8,-5,-2.6,2.2,4.95,3.2,-1.5,-4.85,-3.7,0.85,4.6,4.15,-0.15,-4.3} }, + { "SineG", new double[] { -0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0.2,-0.2,0,0,0.59,0.83,0.74,0.5,0.91,1.36,0.93,0.87,0.6,0.38,0.78,0.53,0.42,0.14,0.01,-0.45,-0.71,-0.99,-1,-1.36,-1.22,-1.07,-1.17,-0.56,-0.95,-1.11,-0.16,0.18,-0.28,0.64,-0.5,0.24,0.45,0.67,0.72,1.15,1.52,1.28,1.38,1.03,-0.47,0.96,0.65,0.28,0.3,0.17,-0.07,-0.67,-0.51,-1.33,-0.33,-1.34,-0.78,-1.21,-0.68,-0.43,-0.56,-0.87,-0.93,-0.4,0.52,0.1,1.18,1.18,1.35} }, + { "ChirpG", new double[] { 0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,0,0.01,1.3,0.3,-0.48,-1.1,-1.14,-0.03,1.11,0.96,0.63,-0.21,-0.97,-0.73,-0.65,-0.06,0.51,1.08,0.99,0.72,0.12,-0.35,-1.12,-1.21,-1.02,-0.87,0.12,0.13,0.24,1.26,1.44,0.58,0.95,-0.82,-0.68,-0.98,-1.08,-1.17,-0.67,-0.06,0.06,0.6,0.69,-0.41,1.33,1.24,0.98,1.01,0.81,0.45,-0.3,-0.28,-1.22,-0.31,-1.35,-0.77,-1.13,-0.5,-0.13,-0.13,-0.32,-0.29,0.3,1.22,0.75,1.73,1.59,1.58} }, + { "Complex", new double[] { 175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.1,175.6,175.44,176.27,176.04,176.99,175.49,175.68,174.34,176.4,174.05,174.4,174.2,176.16,175,177.72,174.33,176.96,174.62,174.76,170.9,171.12,171.05,170.01,169.24,172.64,171.96,175.72,174.16,175.81,177.3,178.38,176.75,177.19,175.55,178.49,176.52,178.45,178.04,178.25,177.8,176.97,172.94,174.92,173.98,172.29,171.19,172.54,172.11,175.32,175.63,176.65,173.8,176.04,172.74,175.24,171.84,171.54,172.17,171.85,172.38,170.78,173.49,173.69,171.71,174.38,173.99,174.83} }, + { "Market", new double[] { 68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,68.75,68.25,67.75,67.75,72.75,74.75,72.25,71.25,71.75,72.75,77.75,76,76,76,74.75,75.5,74.75,73.75,74,74.75,72.25,72.5,72.25,74.5,74.75,75.75,75.75,75.75,74.25,73.75,74.75,72,71.75,72.5,72.25,71,72,71.75,71.75,73.25,72.5,73.75,74,76.75,75.75,75,75.75,74.5,74.25,73.5,71.75,70.5,69,70.5,70,68.75,67.25,68.5,70.75,70,70.5,68.25,68.25,68.25,63.75,64.25} } + +}; + +#!csharp + +String Name = "TEMA"; +int p = 10; +Func Indicator = period => new Tema(period); + +foreach (var item in Data) { + string Signal = item.Key; + double[] Input = item.Value; + TSeries Output = new(); + var ma = Indicator(p); + foreach (var value in Input) { Output.Add(ma.Calc(value)); } + Plot plt = new(); + var p1a = plt.Add.Signal(Input[24..]); p1a.Color = ScottPlot.Colors.Red; p1a.LineWidth = 2; + var p1b = plt.Add.Signal(Output.v.ToArray()[24..]); p1b.Color = ScottPlot.Colors.Blue; p1b.LineWidth = 4; + plt.Title($"{Signal} - {Name}({p})"); + plt.Display(); + plt.SaveSvg($"img/{Name}{p}_{Signal}.svg", 450, 300); +} diff --git a/docs/indicators/averages/tema/charts.md b/docs/indicators/averages/tema/charts.md new file mode 100644 index 00000000..a7309bfd --- /dev/null +++ b/docs/indicators/averages/tema/charts.md @@ -0,0 +1,3 @@ +# TEMA Charts + +![](img/TEMA10_Spike.svg) ![](img/TEMA10_Impulse.svg) ![](img/TEMA10_Triangle.svg) ![](img/TEMA10_Sawtooth.svg) ![](img/TEMA10_Sine.svg) ![](img/TEMA10_Chirp.svg) ![](img/TEMA10_White.svg) ![](img/TEMA10_Gauss.svg) ![](img/TEMA10_B.svg) ![](img/TEMA10_HF.svg) ![](img/TEMA10_ImpulseHF.svg) ![](img/TEMA10_SawtoothHF.svg) ![](img/TEMA10_SineG.svg) ![](img/TEMA10_ChirpG.svg) ![](img/TEMA10_Complex.svg) ![](img/TEMA10_Market.svg) diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_B.svg b/docs/indicators/averages/tema/img/TEMA10_B.svg similarity index 79% rename from docs/indicators/averages/afirma/img/AFIRMA10_B.svg rename to docs/indicators/averages/tema/img/TEMA10_B.svg index 08fbe8bf..e0b2fac7 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_B.svg +++ b/docs/indicators/averages/tema/img/TEMA10_B.svg @@ -3,10 +3,10 @@ - + - + @@ -81,83 +81,83 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -324,7 +324,7 @@ - - B - AFIRMA(10, 0.5) + + B - TEMA(10) diff --git a/docs/indicators/averages/tema/img/TEMA10_Chirp.svg b/docs/indicators/averages/tema/img/TEMA10_Chirp.svg new file mode 100644 index 00000000..88cd9d6e --- /dev/null +++ b/docs/indicators/averages/tema/img/TEMA10_Chirp.svg @@ -0,0 +1,334 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Chirp - TEMA(10) + + diff --git a/docs/indicators/averages/tema/img/TEMA10_ChirpG.svg b/docs/indicators/averages/tema/img/TEMA10_ChirpG.svg new file mode 100644 index 00000000..0cb96318 --- /dev/null +++ b/docs/indicators/averages/tema/img/TEMA10_ChirpG.svg @@ -0,0 +1,352 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1.5 + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + 1.5 + + + + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ChirpG - TEMA(10) + + diff --git a/docs/indicators/averages/tema/img/TEMA10_Complex.svg b/docs/indicators/averages/tema/img/TEMA10_Complex.svg new file mode 100644 index 00000000..fa50d5dd --- /dev/null +++ b/docs/indicators/averages/tema/img/TEMA10_Complex.svg @@ -0,0 +1,334 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 170 + + + + 172 + + + + 174 + + + + 176 + + + + 178 + + + + + + + + + + + + + + + + + + + + + + + + + + + Complex - TEMA(10) + + diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_Gauss.svg b/docs/indicators/averages/tema/img/TEMA10_Gauss.svg similarity index 79% rename from docs/indicators/averages/afirma/img/AFIRMA10_Gauss.svg rename to docs/indicators/averages/tema/img/TEMA10_Gauss.svg index 98190bd9..f56510ae 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_Gauss.svg +++ b/docs/indicators/averages/tema/img/TEMA10_Gauss.svg @@ -3,10 +3,10 @@ - + - + @@ -81,83 +81,83 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -321,7 +321,7 @@ - - Gauss - AFIRMA(10, 0.5) + + Gauss - TEMA(10) diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_HF.svg b/docs/indicators/averages/tema/img/TEMA10_HF.svg similarity index 78% rename from docs/indicators/averages/afirma/img/AFIRMA10_HF.svg rename to docs/indicators/averages/tema/img/TEMA10_HF.svg index c2259c57..6227f0fc 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_HF.svg +++ b/docs/indicators/averages/tema/img/TEMA10_HF.svg @@ -3,10 +3,10 @@ - + - + @@ -81,83 +81,83 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -324,7 +324,7 @@ - - HF - AFIRMA(10, 0.5) + + HF - TEMA(10) diff --git a/docs/indicators/averages/tema/img/TEMA10_Impulse.svg b/docs/indicators/averages/tema/img/TEMA10_Impulse.svg new file mode 100644 index 00000000..e4d9cf92 --- /dev/null +++ b/docs/indicators/averages/tema/img/TEMA10_Impulse.svg @@ -0,0 +1,346 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 0.2 + + + + 0.4 + + + + 0.6 + + + + 0.8 + + + + 1 + + + + 1.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Impulse - TEMA(10) + + diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_ImpulseHF.svg b/docs/indicators/averages/tema/img/TEMA10_ImpulseHF.svg similarity index 78% rename from docs/indicators/averages/afirma/img/AFIRMA10_ImpulseHF.svg rename to docs/indicators/averages/tema/img/TEMA10_ImpulseHF.svg index 4a5c8479..90c19c1d 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_ImpulseHF.svg +++ b/docs/indicators/averages/tema/img/TEMA10_ImpulseHF.svg @@ -3,10 +3,10 @@ - + - + @@ -81,83 +81,83 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -314,7 +314,7 @@ - - ImpulseHF - AFIRMA(10, 0.5) + + ImpulseHF - TEMA(10) diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_Market.svg b/docs/indicators/averages/tema/img/TEMA10_Market.svg similarity index 80% rename from docs/indicators/averages/afirma/img/AFIRMA10_Market.svg rename to docs/indicators/averages/tema/img/TEMA10_Market.svg index cf860443..f3b8bef4 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_Market.svg +++ b/docs/indicators/averages/tema/img/TEMA10_Market.svg @@ -3,10 +3,10 @@ - + - + @@ -81,83 +81,83 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -351,7 +351,7 @@ - - Market - AFIRMA(10, 0.5) + + Market - TEMA(10) diff --git a/docs/indicators/averages/tema/img/TEMA10_Sawtooth.svg b/docs/indicators/averages/tema/img/TEMA10_Sawtooth.svg new file mode 100644 index 00000000..4481c708 --- /dev/null +++ b/docs/indicators/averages/tema/img/TEMA10_Sawtooth.svg @@ -0,0 +1,364 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -5 + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sawtooth - TEMA(10) + + diff --git a/docs/indicators/averages/tema/img/TEMA10_SawtoothHF.svg b/docs/indicators/averages/tema/img/TEMA10_SawtoothHF.svg new file mode 100644 index 00000000..9a6da825 --- /dev/null +++ b/docs/indicators/averages/tema/img/TEMA10_SawtoothHF.svg @@ -0,0 +1,336 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -10 + + + + 0 + + + + 10 + + + + 20 + + + + 30 + + + + 40 + + + + + + + + + + + + + + + + + + + + + + + + + SawtoothHF - TEMA(10) + + diff --git a/docs/indicators/averages/tema/img/TEMA10_Sine.svg b/docs/indicators/averages/tema/img/TEMA10_Sine.svg new file mode 100644 index 00000000..9c3950fa --- /dev/null +++ b/docs/indicators/averages/tema/img/TEMA10_Sine.svg @@ -0,0 +1,334 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Sine - TEMA(10) + + diff --git a/docs/indicators/averages/tema/img/TEMA10_SineG.svg b/docs/indicators/averages/tema/img/TEMA10_SineG.svg new file mode 100644 index 00000000..ac43a37a --- /dev/null +++ b/docs/indicators/averages/tema/img/TEMA10_SineG.svg @@ -0,0 +1,347 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1.5 + + + + -1 + + + + -0.5 + + + + 0 + + + + 0.5 + + + + 1 + + + + 1.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SineG - TEMA(10) + + diff --git a/docs/indicators/averages/tema/img/TEMA10_Spike.svg b/docs/indicators/averages/tema/img/TEMA10_Spike.svg new file mode 100644 index 00000000..de7a15bf --- /dev/null +++ b/docs/indicators/averages/tema/img/TEMA10_Spike.svg @@ -0,0 +1,339 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 0.2 + + + + 0.4 + + + + 0.6 + + + + 0.8 + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + Spike - TEMA(10) + + diff --git a/docs/indicators/averages/tema/img/TEMA10_Triangle.svg b/docs/indicators/averages/tema/img/TEMA10_Triangle.svg new file mode 100644 index 00000000..1a6e063e --- /dev/null +++ b/docs/indicators/averages/tema/img/TEMA10_Triangle.svg @@ -0,0 +1,355 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + 40 + + + + 45 + + + + 50 + + + + 55 + + + + 60 + + + + 65 + + + + 70 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + 5 + + + + 10 + + + + 15 + + + + 20 + + + + 25 + + + + 30 + + + + 35 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Triangle - TEMA(10) + + diff --git a/docs/indicators/averages/afirma/img/AFIRMA10_White.svg b/docs/indicators/averages/tema/img/TEMA10_White.svg similarity index 80% rename from docs/indicators/averages/afirma/img/AFIRMA10_White.svg rename to docs/indicators/averages/tema/img/TEMA10_White.svg index d23c6dd3..69601237 100644 --- a/docs/indicators/averages/afirma/img/AFIRMA10_White.svg +++ b/docs/indicators/averages/tema/img/TEMA10_White.svg @@ -3,10 +3,10 @@ - + - + @@ -81,83 +81,83 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -329,7 +329,7 @@ - - White - AFIRMA(10, 0.5) + + White - TEMA(10) diff --git a/docs/readme.md b/docs/readme.md index 6364377f..32247e64 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -1,7 +1,3 @@ -# QuanTAlib - quantitative technical indicators for Quantower - -### (and other C#-based trading platorms) - [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=mihakralj_QuanTAlib&metric=ncloc)](https://sonarcloud.io/summary/overall?id=mihakralj_QuanTAlib) [![Codacy grade](https://img.shields.io/codacy/grade/b1f9109222234c87bce45f1fd4c63aee?style=flat-square)](https://app.codacy.com/gh/mihakralj/QuanTAlib/dashboard) [![codecov](https://codecov.io/gh/mihakralj/QuanTAlib/branch/main/graph/badge.svg?style=flat-square&token=YNMJRGKMTJ?style=flat-square)](https://codecov.io/gh/mihakralj/QuanTAlib) @@ -14,14 +10,24 @@ [![GitHub watchers](https://img.shields.io/github/watchers/mihakralj/QuanTAlib?style=flat-square)](https://github.com/mihakralj/QuanTAlib/watchers) [![.NET8.0](https://img.shields.io/badge/.NET-8.0-blue?style=flat-square)](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) +![Alt text](./img/quotes.gif) + +# QuanTAlib - quantitative technical indicators for Quantower + **Quan**titative **TA** **lib**rary (QuanTAlib) is a C# library of classess and methods for quantitative technical analysis useful for analyzing quotes with [Quantower](https://www.quantower.com/) and other C#-based trading platforms. -**QuanTAlib** is written with some specific design criteria in mind - why there is '_yet another C# TA library_': +[**Visit documentation pages**](https://mihakralj.github.io/QuanTAlib/#/) -- Prioritize **real-time data analysis**: As new data items arrives, indicators don't have to re-calculate the entire history and can generate a result directly from the last item +**QuanTAlib** is a C# library written with some specific design criteria in mind. Here is why there is '_yet another C# TA library_': + +- QuanTAlib focuses on **[real-time data analysis](essays/realtime.md)**: As new data items arrives, indicators don't have to re-calculate the entire history and can generate a result directly from the last item - **Allow updates/corrections** of the last quote - QuanTAlib is re-calculating the last value as many times as required before continuing to the new bar - **Calculate early data right** - calculated data is as valid as mathematically possible from the first value onwards - no blackout or warming-up periods. All indicators return data from the first bar, alongside with a flag `isHot` - defining if calculation is already stable. +### Coverage + +[List of indicators - implemented and planned](indicators/indicators.md) + ## Installation to Quantower - `` is the directory where Quantower is installed - where `Start.lnk` launcher is @@ -29,14 +35,10 @@ - Copy `Statistics.dll` from Releases to `\Settings\Scripts\Indicators\Statistics\Statistics.dll` - Copy `SyntheticVendor.dll` from Releases to `\Settings\Scripts\Vendors\SyntheticVendor\SyntheticVendor.dll` -![Alt text](./img/quotes.gif) + QuanTAlib is intended for developers and users of Quantower, therefore it does not focus on privind sources of OHLCV quotes. There are some very basic data feeds available to use in the learning process: `GBM_Feed` for Random (Geometric Brownian Motion) data, and `SyntheticVendor` data generator for Quantower. -### Coverage - -[List of indicators - implemented and planned](indicators/indicators.md) - ### Validation QuanTAlib uses validation tests with four other TA libraries to assure accuracy and validity of results: diff --git a/lib/averages/Afirma.cs b/lib/averages/Afirma.cs index e2771a61..2bbb5b35 100644 --- a/lib/averages/Afirma.cs +++ b/lib/averages/Afirma.cs @@ -1,89 +1,162 @@ -namespace QuanTAlib; +using System; -public class Afirma : AbstractBase +namespace QuanTAlib { - private readonly int Period; - private readonly CircularBuffer _buffer; - private readonly double _alpha; // Adaptive factor - private double _lastAfirma, _p_lastAfirma; - private double _lastError, _p_lastError; - public Afirma(int period, double alpha = 0.1) + public class Afirma : AbstractBase { - if (period < 1) + + public enum WindowType { - throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + Rectangular, + Hanning1, + Hanning2, + Blackman, + BlackmanHarris } - if (alpha <= 0 || alpha >= 1) + + private readonly int Periods; + private readonly int Taps; + private readonly WindowType Window; + private readonly CircularBuffer _buffer; + private readonly double[] _weights; + private readonly double _wsum; + private readonly double[] _armaBuffer; + private readonly int _n; + private readonly double _sx2, _sx3, _sx4, _sx5, _sx6, _den; + + public Afirma(int periods, int taps, WindowType window) { - throw new ArgumentOutOfRangeException(nameof(alpha), "Alpha must be between 0 and 1 (exclusive)."); + if (periods < 1) + { + throw new ArgumentOutOfRangeException(nameof(periods), "Periods must be greater than or equal to 1."); + } + if (taps < 1) + { + throw new ArgumentOutOfRangeException(nameof(taps), "Taps must be greater than or equal to 1."); + } + Periods = periods; + Taps = taps; + Window = window; + WarmupPeriod = taps; + _buffer = new CircularBuffer(taps); + _weights = new double[taps]; + _wsum = CalculateWeights(); + _armaBuffer = new double[taps]; + _n = (Taps - 1) / 2; + + // Calculate least squares coefficients in the constructor + _sx2 = (2 * _n + 1) / 3.0; + _sx3 = _n * (_n + 1) / 2.0; + _sx4 = _sx2 * (3 * _n * _n + 3 * _n - 1) / 5.0; + _sx5 = _sx3 * (2 * _n * _n + 2 * _n - 1) / 3.0; + _sx6 = _sx2 * (3 * Math.Pow(_n, 3) * (_n + 2) - 3 * _n + 1) / 7.0; + _den = _sx6 * _sx4 / _sx5 - _sx5; + + Name = "Afirma"; + Init(); } - Period = period; - WarmupPeriod = period; - _buffer = new CircularBuffer(period); - _alpha = alpha; - Name = "Afirma"; - WarmupPeriod = period; - Init(); + + public Afirma(object source, int periods, int taps, WindowType window) : this(periods, taps, window) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(IsNew); + _buffer.Add(Input.Value, Input.IsNew); + + if (_index >= Taps) + { + double a0 = _buffer[_n]; + double a1 = _buffer[_n] - _buffer[_n + 1]; + double sx2y = 0.0; + double sx3y = 0.0; + + for (int i = 0; i <= _n; i++) + { + sx2y += i * i * _buffer[_n - i]; + sx3y += i * i * i * _buffer[_n - i]; + } + + sx2y = 2.0 * sx2y / _n / (_n + 1); + sx3y = 2.0 * sx3y / _n / (_n + 1); + double p = sx2y - a0 * _sx2 - a1 * _sx3; + double q = sx3y - a0 * _sx3 - a1 * _sx4; + double a2 = (p * _sx6 / _sx5 - q) / _den; + double a3 = (q * _sx4 / _sx5 - p) / _den; + + for (int k = 0; k <= _n; k++) + { + _armaBuffer[_n - k] = a0 + k * a1 + k * k * a2 + k * k * k * a3; + } + } + + double result = 0.0; + for (int k = 0; k < Taps; k++) + { + result += _buffer[k] * _weights[k] / _wsum; + } + + IsHot = _index >= WarmupPeriod; + return result; + } + + private double CalculateWeights() + { + double wsum = 0.0; + double centerTap = (Taps - 1) / 2.0; + for (int k = 0; k < Taps; k++) + { + double windowWeight; + switch (Window) + { + case WindowType.Rectangular: + windowWeight = 1.0; + break; + case WindowType.Hanning1: + windowWeight = 0.50 - 0.50 * Math.Cos(2.0 * Math.PI * k / (Taps - 1)); + break; + case WindowType.Hanning2: + windowWeight = 0.54 - 0.46 * Math.Cos(2.0 * Math.PI * k / (Taps - 1)); + break; + case WindowType.Blackman: + windowWeight = 0.42 - 0.50 * Math.Cos(2.0 * Math.PI * k / (Taps - 1)) + 0.08 * Math.Cos(4.0 * Math.PI * k / (Taps - 1)); + break; + case WindowType.BlackmanHarris: + windowWeight = 0.35875 - 0.48829 * Math.Cos(2.0 * Math.PI * k / (Taps - 1)) + 0.14128 * Math.Cos(4.0 * Math.PI * k / (Taps - 1)) - 0.01168 * Math.Cos(6.0 * Math.PI * k / (Taps - 1)); + break; + default: + windowWeight = 1.0; + break; + } + + double sincWeight; + if (Math.Abs(k - centerTap) < 1e-10) + { + sincWeight = 1.0; + } + else + { + sincWeight = Math.Sin(Math.PI * (k - centerTap) / Periods) / (Math.PI * (k - centerTap) / Periods); + } + + _weights[k] = windowWeight * sincWeight; + wsum += _weights[k]; + } + return wsum; + } + } - - public Afirma(object source, int period, double alpha = 0.1) : this(period: period, alpha: alpha) - { - var pubEvent = source.GetType().GetEvent("Pub"); - pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); - } - - public override void Init() - { - base.Init(); - _lastAfirma = 0; - _lastError = 0; - } - - protected override void ManageState(bool isNew) - { - if (isNew) - { - _lastValidValue = Input.Value; - _index++; - _p_lastAfirma = _lastAfirma; - _p_lastError = _lastError; - } - else - { - _lastAfirma = _p_lastAfirma; - _lastError = _p_lastError; - } - } - - /// - /// Core AFIRMA calculation - /// - protected override double Calculation() - { - double result; - ManageState(IsNew); - _buffer.Add(Input.Value, Input.IsNew); - - if (_index < Period) - { - // Use simple average during warmup period - result = _buffer.Average(); - } - else - { - // AFIRMA calculation - double sma = _buffer.Average(); - double error = Input.Value - _lastAfirma; - double denominator = Math.Abs(error) + Math.Abs(_lastError); - double adaptiveFactor = denominator != 0 ? _alpha * Math.Abs(error) / denominator : _alpha; - result = sma + adaptiveFactor * (Input.Value - sma); - - _lastError = error; - } - - _lastAfirma = result; - IsHot = _index >= WarmupPeriod; - return result; - } -} \ No newline at end of file +} diff --git a/lib/averages/Ama.cs b/lib/averages/Ama.cs new file mode 100644 index 00000000..78beb01e --- /dev/null +++ b/lib/averages/Ama.cs @@ -0,0 +1,89 @@ +namespace QuanTAlib; + +public class Ama : AbstractBase +{ + private readonly int Period; + private readonly CircularBuffer _buffer; + private readonly double _alpha; // Adaptive factor + private double _lastAfirma, _p_lastAfirma; + private double _lastError, _p_lastError; + + public Ama(int period, double alpha = 0.1) + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + if (alpha <= 0 || alpha >= 1) + { + throw new ArgumentOutOfRangeException(nameof(alpha), "Alpha must be between 0 and 1 (exclusive)."); + } + Period = period; + WarmupPeriod = period; + _buffer = new CircularBuffer(period); + _alpha = alpha; + Name = "Afirma"; + WarmupPeriod = period; + Init(); + } + + public Ama(object source, int period, double alpha = 0.1) : this(period: period, alpha: alpha) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _lastAfirma = 0; + _lastError = 0; + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + _p_lastAfirma = _lastAfirma; + _p_lastError = _lastError; + } + else + { + _lastAfirma = _p_lastAfirma; + _lastError = _p_lastError; + } + } + + /// + /// Core AFIRMA calculation + /// + protected override double Calculation() + { + double result; + ManageState(IsNew); + _buffer.Add(Input.Value, Input.IsNew); + + if (_index < Period) + { + // Use simple average during warmup period + result = _buffer.Average(); + } + else + { + // AFIRMA calculation + double sma = _buffer.Average(); + double error = Input.Value - _lastAfirma; + double denominator = Math.Abs(error) + Math.Abs(_lastError); + double adaptiveFactor = denominator != 0 ? _alpha * Math.Abs(error) / denominator : _alpha; + result = sma + adaptiveFactor * (Input.Value - sma); + + _lastError = error; + } + + _lastAfirma = result; + IsHot = _index >= WarmupPeriod; + return result; + } +} \ No newline at end of file diff --git a/quantower/Averages/AfirmaIndicator.cs b/quantower/Averages/AfirmaIndicator.cs index b7fb250d..44449832 100644 --- a/quantower/Averages/AfirmaIndicator.cs +++ b/quantower/Averages/AfirmaIndicator.cs @@ -3,23 +3,35 @@ namespace QuanTAlib; public class AfirmaIndicator : IndicatorBase { - [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] - public int Period { get; set; } = 10; + [InputParameter("Taps (number of weights)", sortIndex: 1, 1, 2000, 1, 0)] + public int Taps { get; set; } = 6; + + [InputParameter("Periods for lowpass cutoff", sortIndex: 2, 1, 2000, 1, 0)] + public int Periods { get; set; } = 6; + + + + [InputParameter("Window Type", sortIndex: 3, variants: [ + "Rectangular", Afirma.WindowType.Rectangular, + "Hanning", Afirma.WindowType.Hanning1, + "Hamming", Afirma.WindowType.Hanning2, + "Blackman", Afirma.WindowType.Blackman, + "Blackman-Harris", Afirma.WindowType.BlackmanHarris + ])] + public Afirma.WindowType Window { get; set; } = Afirma.WindowType.Hanning1; - [InputParameter("Alpha", sortIndex: 2, 0.01, 0.99, 0.01, 2)] - public double Alpha { get; set; } = 0.1; private Afirma? ma; protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"AFIRMA {Period} : {SourceName}"; + public override string ShortName => $"AFIRMA {Taps}:{Periods}:{Window} : {SourceName}"; public AfirmaIndicator() { - Name = "AFIRMA - Adaptive Filtering Integrated Recursive Moving Average"; - Description = "Adaptive Filtering Integrated Recursive Moving Average"; + Name = "AFIRMA - Adaptive Finite Impulse Response Moving Average"; + Description = "Adaptive Finite Impulse Response Moving Average with ARMA component"; } protected override void InitIndicator() { - ma = new Afirma(period: Period, alpha: Alpha); + ma = new Afirma(periods: Periods, taps: Taps, window: Window); } } \ No newline at end of file diff --git a/quantower/Averages/AmaIndicator.cs b/quantower/Averages/AmaIndicator.cs new file mode 100644 index 00000000..417700b5 --- /dev/null +++ b/quantower/Averages/AmaIndicator.cs @@ -0,0 +1,25 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class AmaIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 2, 2000, 1, 0)] + public int Period { get; set; } = 10; + + [InputParameter("Alpha", sortIndex: 2, minimum: -0.01, maximum: 1.0, increment: 0.01, decimalPlaces: 2)] + public double Alpha { get; set; } = 0.1; + private Ama? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"AMA {Period} : {Alpha} : {SourceName}"; + + + public AmaIndicator() + { + Name = "AMA - Adaptive Moving Average"; + } + + protected override void InitIndicator() + { + ma = new Ama(period: Period, alpha: Alpha); + } +} From bdc01bff4aabe4d26848e11f663f0704c4d846b5 Mon Sep 17 00:00:00 2001 From: Miha Kralj Date: Mon, 30 Sep 2024 06:46:07 -0700 Subject: [PATCH 3/5] Atr --- .sonarlint/QuanTAlib.json | 4 - .vscode/settings.json | 13 + Directory.Build.props | 1 + QuanTAlib.sln | 81 +- SyntheticVendor/SyntheticVendor.cs | 5 +- Tests/test_consistency.cs | 857 ---------------------- Tests/test_iTBar.cs | 58 ++ Tests/test_iTValue.cs | 97 +++ Tests/test_skender.stock.cs | 20 + docs/Progress.md | 5 + docs/index.html | 34 +- docs/readme.md | 7 +- lib/averages/Ama.cs | 89 --- lib/averages/Mama.cs | 1 - lib/averages/T3.cs | 40 +- lib/core/AbstractBarBase.cs | 71 ++ lib/core/tbar.cs | 3 +- lib/core/tvalue.cs | 2 +- lib/quantalib.csproj | 1 - lib/volatility/Atr.cs | 71 ++ notebooks/Skender.dib | 45 ++ quantower/Averages/AmaIndicator.cs | 25 - quantower/Averages/Averages.csproj | 1 - quantower/Statistics/Statistics.csproj | 1 - quantower/Statistics/_IndicatorBarBase.cs | 136 ++++ quantower/Volatility/AtrIndicator.cs | 23 + quantower/Volatility/Volatility.csproj | 29 + quantower/Volatility/_IndicatorBarBase.cs | 136 ++++ 28 files changed, 803 insertions(+), 1053 deletions(-) delete mode 100644 .sonarlint/QuanTAlib.json create mode 100644 .vscode/settings.json delete mode 100644 Tests/test_consistency.cs create mode 100644 Tests/test_iTBar.cs create mode 100644 Tests/test_iTValue.cs create mode 100644 docs/Progress.md delete mode 100644 lib/averages/Ama.cs create mode 100644 lib/core/AbstractBarBase.cs create mode 100644 lib/volatility/Atr.cs create mode 100644 notebooks/Skender.dib delete mode 100644 quantower/Averages/AmaIndicator.cs create mode 100644 quantower/Statistics/_IndicatorBarBase.cs create mode 100644 quantower/Volatility/AtrIndicator.cs create mode 100644 quantower/Volatility/Volatility.csproj create mode 100644 quantower/Volatility/_IndicatorBarBase.cs diff --git a/.sonarlint/QuanTAlib.json b/.sonarlint/QuanTAlib.json deleted file mode 100644 index 482e31c0..00000000 --- a/.sonarlint/QuanTAlib.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "SonarCloudOrganization": "mihakralj", - "ProjectKey": "mihakralj_QuanTAlib" -} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..e77d093f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,13 @@ +{ + "sonarlint.connectedMode.connections.sonarcloud": [ + { + "organizationKey": "mihakralj", + "token": "6df7cd62a17dc4e1c5532df1da2f49d5a977dd50", + "connectionId": "mihakralj" + } + ], + "sonarlint.connectedMode.project": { + "connectionId": "mihakralj", + "projectKey": "mihakralj_QuanTAlib" + } +} \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props index 48962b26..9aa0211e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -35,6 +35,7 @@ all runtime; build; native; contentfiles; analyzers + diff --git a/QuanTAlib.sln b/QuanTAlib.sln index 7b1d3247..8727aad2 100644 --- a/QuanTAlib.sln +++ b/QuanTAlib.sln @@ -1,57 +1,68 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 -VisualStudioVersion = 17.0.31903.59 +VisualStudioVersion = 17.5.002.0 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "quantalib", "lib\quantalib.csproj", "{584E06A9-CEB4-476A-85CC-6A8FF3974AE2}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "benchmark", "benchmark\benchmark.csproj", "{DBB674D9-43AA-4383-A4D0-E791847C7145}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{D85FEBB4-B651-466F-85CC-FD902378D4D2}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "quantalib", "lib\quantalib.csproj", "{D2F742C0-9E2A-4B07-8D9F-0DF0BA3DDA31}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MovingAverages", "quantower\Averages\Averages.csproj", "{32CC09CC-26E3-4FCE-8932-C0513C4AD766}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SyntheticVendor", "SyntheticVendor\SyntheticVendor.csproj", "{1777C7BB-F67A-4F00-99BB-EE48178A7129}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SyntheticVendor", "SyntheticVendor\SyntheticVendor.csproj", "{20B1B5F1-8C36-4668-B0AE-951C13AE197B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "tests\Tests.csproj", "{A89D1A5D-9DB9-4E5D-A7D6-E153509430CD}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "quantower", "quantower", "{A8D9AE68-24E3-476C-BB98-244541BB4B43}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "quantower", "quantower", "{1EC1B338-6AD9-4E3D-82C3-6591CC148DD5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Statistics", "quantower\Statistics\Statistics.csproj", "{B6D3EB11-63B6-430F-B526-E1981B3D8214}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Averages", "quantower\Averages\Averages.csproj", "{61AFF1E5-9E2B-47F4-9665-D8358C5BDD6F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "benchmark", "benchmark\benchmark.csproj", "{6629C6D2-FC34-4BDF-AEF4-A5859E05981E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Statistics", "quantower\Statistics\Statistics.csproj", "{7EBB6ECD-15FA-44DD-B231-FED97E0B710D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volatility", "quantower\Volatility\Volatility.csproj", "{9190CFDD-9FD8-4E14-9269-79325B03EC6C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DBB674D9-43AA-4383-A4D0-E791847C7145}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DBB674D9-43AA-4383-A4D0-E791847C7145}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DBB674D9-43AA-4383-A4D0-E791847C7145}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DBB674D9-43AA-4383-A4D0-E791847C7145}.Release|Any CPU.Build.0 = Release|Any CPU + {D2F742C0-9E2A-4B07-8D9F-0DF0BA3DDA31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D2F742C0-9E2A-4B07-8D9F-0DF0BA3DDA31}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D2F742C0-9E2A-4B07-8D9F-0DF0BA3DDA31}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D2F742C0-9E2A-4B07-8D9F-0DF0BA3DDA31}.Release|Any CPU.Build.0 = Release|Any CPU + {1777C7BB-F67A-4F00-99BB-EE48178A7129}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1777C7BB-F67A-4F00-99BB-EE48178A7129}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1777C7BB-F67A-4F00-99BB-EE48178A7129}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1777C7BB-F67A-4F00-99BB-EE48178A7129}.Release|Any CPU.Build.0 = Release|Any CPU + {A89D1A5D-9DB9-4E5D-A7D6-E153509430CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A89D1A5D-9DB9-4E5D-A7D6-E153509430CD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A89D1A5D-9DB9-4E5D-A7D6-E153509430CD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A89D1A5D-9DB9-4E5D-A7D6-E153509430CD}.Release|Any CPU.Build.0 = Release|Any CPU + {61AFF1E5-9E2B-47F4-9665-D8358C5BDD6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {61AFF1E5-9E2B-47F4-9665-D8358C5BDD6F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {61AFF1E5-9E2B-47F4-9665-D8358C5BDD6F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {61AFF1E5-9E2B-47F4-9665-D8358C5BDD6F}.Release|Any CPU.Build.0 = Release|Any CPU + {7EBB6ECD-15FA-44DD-B231-FED97E0B710D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7EBB6ECD-15FA-44DD-B231-FED97E0B710D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7EBB6ECD-15FA-44DD-B231-FED97E0B710D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7EBB6ECD-15FA-44DD-B231-FED97E0B710D}.Release|Any CPU.Build.0 = Release|Any CPU + {9190CFDD-9FD8-4E14-9269-79325B03EC6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9190CFDD-9FD8-4E14-9269-79325B03EC6C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9190CFDD-9FD8-4E14-9269-79325B03EC6C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9190CFDD-9FD8-4E14-9269-79325B03EC6C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {584E06A9-CEB4-476A-85CC-6A8FF3974AE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {584E06A9-CEB4-476A-85CC-6A8FF3974AE2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {584E06A9-CEB4-476A-85CC-6A8FF3974AE2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {584E06A9-CEB4-476A-85CC-6A8FF3974AE2}.Release|Any CPU.Build.0 = Release|Any CPU - {D85FEBB4-B651-466F-85CC-FD902378D4D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D85FEBB4-B651-466F-85CC-FD902378D4D2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D85FEBB4-B651-466F-85CC-FD902378D4D2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D85FEBB4-B651-466F-85CC-FD902378D4D2}.Release|Any CPU.Build.0 = Release|Any CPU - {32CC09CC-26E3-4FCE-8932-C0513C4AD766}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {32CC09CC-26E3-4FCE-8932-C0513C4AD766}.Debug|Any CPU.Build.0 = Debug|Any CPU - {32CC09CC-26E3-4FCE-8932-C0513C4AD766}.Release|Any CPU.ActiveCfg = Release|Any CPU - {32CC09CC-26E3-4FCE-8932-C0513C4AD766}.Release|Any CPU.Build.0 = Release|Any CPU - {20B1B5F1-8C36-4668-B0AE-951C13AE197B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {20B1B5F1-8C36-4668-B0AE-951C13AE197B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {20B1B5F1-8C36-4668-B0AE-951C13AE197B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {20B1B5F1-8C36-4668-B0AE-951C13AE197B}.Release|Any CPU.Build.0 = Release|Any CPU - {B6D3EB11-63B6-430F-B526-E1981B3D8214}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B6D3EB11-63B6-430F-B526-E1981B3D8214}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B6D3EB11-63B6-430F-B526-E1981B3D8214}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B6D3EB11-63B6-430F-B526-E1981B3D8214}.Release|Any CPU.Build.0 = Release|Any CPU - {6629C6D2-FC34-4BDF-AEF4-A5859E05981E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6629C6D2-FC34-4BDF-AEF4-A5859E05981E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6629C6D2-FC34-4BDF-AEF4-A5859E05981E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6629C6D2-FC34-4BDF-AEF4-A5859E05981E}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection GlobalSection(NestedProjects) = preSolution - {B6D3EB11-63B6-430F-B526-E1981B3D8214} = {A8D9AE68-24E3-476C-BB98-244541BB4B43} + {61AFF1E5-9E2B-47F4-9665-D8358C5BDD6F} = {1EC1B338-6AD9-4E3D-82C3-6591CC148DD5} + {7EBB6ECD-15FA-44DD-B231-FED97E0B710D} = {1EC1B338-6AD9-4E3D-82C3-6591CC148DD5} + {9190CFDD-9FD8-4E14-9269-79325B03EC6C} = {1EC1B338-6AD9-4E3D-82C3-6591CC148DD5} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {080D10E4-B741-4A85-B37B-08845A5C78D9} EndGlobalSection EndGlobal diff --git a/SyntheticVendor/SyntheticVendor.cs b/SyntheticVendor/SyntheticVendor.cs index a770055d..03d49548 100644 --- a/SyntheticVendor/SyntheticVendor.cs +++ b/SyntheticVendor/SyntheticVendor.cs @@ -540,7 +540,10 @@ namespace SyntheticVendorNamespace openValue = 0.0001; closeValue = 100; } - else { } + else + { + // No action + } return new HistoryItemBar { diff --git a/Tests/test_consistency.cs b/Tests/test_consistency.cs deleted file mode 100644 index ea6ae40d..00000000 --- a/Tests/test_consistency.cs +++ /dev/null @@ -1,857 +0,0 @@ -using Xunit; -namespace QuanTAlib; - -public class Consistency -{ - Random rnd; - int series_len = 1000; - int corrections = 100; - - public Consistency() - { //constructor - rnd = new((int)DateTime.Now.Ticks); - } - - - [Fact] - public void CanUpdate() - { - - GbmFeed gbm = new(); - TSeries input = new(gbm.Close); - TSeries output = new(input); - - gbm.Add(10000); - - Assert.Equal(input.Count, output.Count); - for (int i = 0; i < input.Count; i++) - { - Assert.Equal(input[i].v, output[i].v); - } - } - - [Fact] - public void Alma_isNew() - { - int p = (int)rnd.Next(2, 100); - double offset = rnd.Next(); - double sigma = rnd.Next(1, 100); - Alma ma1 = new(period: p, offset: offset, sigma: sigma); - Alma ma2 = new(period: p, offset: offset, sigma: sigma); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Convolution_isNew() - { - Convolution ma1 = new(new double[] { 1.0, 2, 3, 2, 1 }); - Convolution ma2 = new(new double[] { 1.0, 2, 3, 2, 1 }); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Dema_isNew() - { - int p = (int)rnd.Next(2, 100); - Dema ma1 = new(p); - Dema ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Dsma_isNew() - { - int p = (int)rnd.Next(2, 100); - Dsma ma1 = new(p); - Dsma ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Dwma_isNew() - { - int p = (int)rnd.Next(2, 100); - Dwma ma1 = new(p); - Dwma ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void EmaSma_isNew() - { - int p = (int)rnd.Next(2, 100); - Ema ma1 = new(p, useSma: true); - Ema ma2 = new(p, useSma: true); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Ema_isNew() - { - int p = (int)rnd.Next(2, 100); - Ema ma1 = new(p, useSma: false); - Ema ma2 = new(p, useSma: false); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Sma_isNew() - { - int p = (int)rnd.Next(2, 100); - Sma ma1 = new(p); - Sma ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Epma_isNew() - { - int p = (int)rnd.Next(2, 100); - Epma ma1 = new(p); - Epma ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Frama_isNew() - { - int p = (int)rnd.Next(2, 100); - Frama ma1 = new(p); - Frama ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Fwma_isNew() - { - int p = (int)rnd.Next(2, 100); - Fwma ma1 = new(p); - Fwma ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Gma_isNew() - { - int p = (int)rnd.Next(2, 100); - Gma ma1 = new(p); - Gma ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Hma_isNew() - { - int p = (int)rnd.Next(2, 100); - Hma ma1 = new(p); - Hma ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - - [Fact] - public void Hwma_isNew() - { - int p = (int)rnd.Next(2, 100); - Hwma ma1 = new(p); - Hwma ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Kama_isNew() - { - int p = (int)rnd.Next(2, 100); - Kama ma1 = new(p); - Kama ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Ltma_isNew() - { - int p = rnd.Next(0, 1); - Ltma ma1 = new(p); - Ltma ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Mama_isNew() - { - int p = rnd.Next(0, 1); - Mama ma1 = new(p, p * 0.1); - Mama ma2 = new(p, p * 0.1); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - - Assert.True(ma1.Value == ma2.Value, $"Assertion failed for p={p}, i={i}. Expected {ma1.Value} but got {ma2.Value}."); - } - } - - [Fact] - public void Mgdi_isNew() - { - int p = (int)rnd.Next(2, 100); - Mgdi ma1 = new(p); - Mgdi ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Mma_isNew() - { - int p = (int)rnd.Next(2, 100); - Mma ma1 = new(p); - Mma ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Qema_isNew() - { - Qema ma1 = new(); - Qema ma2 = new(); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Rema_isNew() - { - int p = (int)rnd.Next(2, 100); - Rema ma1 = new(p); - Rema ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Rma_isNew() - { - int p = (int)rnd.Next(2, 100); - Rma ma1 = new(p); - Rma ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Sinema_isNew() - { - int p = (int)rnd.Next(2, 100); - Sinema ma1 = new(p); - Sinema ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Smma_isNew() - { - int p = (int)rnd.Next(2, 100); - Smma ma1 = new(p); - Smma ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void T3_isNew() - { - int p = (int)rnd.Next(2, 100); - T3 ma1 = new(p); - T3 ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Tema_isNew() - { - int p = (int)rnd.Next(2, 100); - Tema ma1 = new(p); - Tema ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - - [Fact] - public void Trima_isNew() - { - int p = (int)rnd.Next(2, 100); - Trima ma1 = new(p); - Trima ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - - [Fact] - public void Vidya_isNew() - { - int p = (int)rnd.Next(2, 100); - Vidya ma1 = new(p); - Vidya ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Wma_isNew() - { - int p = (int)rnd.Next(2, 100); - Wma ma1 = new(p); - Wma ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - [Fact] - public void Zlema_isNew() - { - int p = (int)rnd.Next(2, 100); - Zlema ma1 = new(p); - Zlema ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Entropy_isNew() - { - int p = (int)rnd.Next(2, 100); - Entropy ma1 = new(p); - Entropy ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Kurtosis_isNew() - { - int p = (int)rnd.Next(5, 100); - Kurtosis ma1 = new(p); - Kurtosis ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Max_isNew() - { - int p = (int)rnd.Next(2, 100); - Max ma1 = new(p, 0.01); - Max ma2 = new(p, 0.01); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Min_isNew() - { - int p = (int)rnd.Next(2, 100); - Min ma1 = new(p, 0.01); - Min ma2 = new(p, 0.01); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Med_isNew() - { - int p = (int)rnd.Next(2, 100); - Median ma1 = new(p); - Median ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Mode_isNew() - { - int p = (int)rnd.Next(2, 100); - Mode ma1 = new(p); - Mode ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Percentile_isNew() - { - int p = (int)rnd.Next(2, 100); - Percentile ma1 = new(p, 50); - Percentile ma2 = new(p, 50); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Skew_isNew() - { - int p = (int)rnd.Next(2, 100); - Skew ma1 = new(p); - Skew ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Stddev_isNew() - { - int p = (int)rnd.Next(2, 100); - Stddev ma1 = new(p); - Stddev ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Variance_isNew() - { - int p = (int)rnd.Next(2, 100); - Variance ma1 = new(p); - Variance ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - - [Fact] - public void Zscore_isNew() - { - int p = (int)rnd.Next(2, 100); - Zscore ma1 = new(p); - Zscore ma2 = new(p); - for (int i = 0; i < series_len; i++) - { - TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); - ma1.Calc(item1); - for (int j = 0; j < corrections; j++) - { - item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); - ma1.Calc(item1); - } - ma2.Calc(new TValue(item1.Time, item1.Value, IsNew: true)); - Assert.Equal(ma1.Value, ma2.Value); - } - } - -} diff --git a/Tests/test_iTBar.cs b/Tests/test_iTBar.cs new file mode 100644 index 00000000..4f21c69a --- /dev/null +++ b/Tests/test_iTBar.cs @@ -0,0 +1,58 @@ +using Xunit; +using System.Reflection; + +namespace QuanTAlib +{ + public class BarIndicatorTests + { + private Random rnd; + private const int SeriesLen = 1000; + private const int Corrections = 100; + + public BarIndicatorTests() + { + rnd = new Random((int)DateTime.Now.Ticks); + } + + private static readonly iTValue[] indicators = new iTValue[] + { + new Atr(period: 14), + }; + + [Theory] + [MemberData(nameof(GetIndicators))] + public void IndicatorIsNew(iTValue indicator) + { + var indicator1 = indicator; + var indicator2 = indicator; + + MethodInfo calcMethod = indicator.GetType().GetMethod("Calc")!; + if (calcMethod == null) + { + throw new Exception($"Calc method not found for indicator type: {indicator.GetType().Name}"); + } + + for (int i = 0; i < SeriesLen; i++) + { + TBar item1 = new(Time: DateTime.Now, Open: rnd.Next(-100, 100), High: rnd.Next(-100, 100), Low: rnd.Next(-100, 100), Close: rnd.Next(-100, 100), Volume: rnd.Next(-1000, 1000), IsNew: true); + calcMethod.Invoke(indicator1, new object[] { item1 }); + + for (int j = 0; j < Corrections; j++) + { + item1 = new(Time: DateTime.Now, Open: rnd.Next(-100, 100), High: rnd.Next(-100, 100), Low: rnd.Next(-100, 100), Close: rnd.Next(-100, 100), Volume: rnd.Next(-1000, 1000), IsNew: false); + calcMethod.Invoke(indicator1, new object[] { item1 }); + } + + var item2 = new TBar (item1.Time, item1.Open, item1.High, item1.Low, item1.Close, item1.Volume , IsNew: true); + calcMethod.Invoke(indicator2, new object[] { item2 }); + + Assert.Equal(indicator1.Value, indicator2.Value); + } + } + + public static IEnumerable GetIndicators() + { + return indicators.Select(indicator => new object[] { indicator }); + } + } +} \ No newline at end of file diff --git a/Tests/test_iTValue.cs b/Tests/test_iTValue.cs new file mode 100644 index 00000000..3f68ae4c --- /dev/null +++ b/Tests/test_iTValue.cs @@ -0,0 +1,97 @@ +using Xunit; +using System.Reflection; + +namespace QuanTAlib +{ + public class IndicatorTests + { + private Random rnd; + private const int SeriesLen = 1000; + private const int Corrections = 100; + + public IndicatorTests() + { + rnd = new Random((int)DateTime.Now.Ticks); + } + + private static readonly iTValue[] indicators = + [ + new Ema(period: 10, useSma: true), + new Alma(period: 14, offset: 0.85, sigma: 6), + new Convolution(new double[] { 1.0, 2, 3, 2, 1 }), + new Dema(period: 14), + new Dsma(period: 14), + new Dwma(period: 14), + new Epma(period: 14), + new Frama(period: 14), + new Fwma(period: 14), + new Gma(period: 14), + new Hma(period: 14), + new Hwma(period: 14), + new Kama(period: 14), + new Mama(fastLimit: 0.5, slowLimit: 0.05), + new Mgdi(period: 14), + new Mma(period: 14), + new Qema(), + new Rema(period: 14), + new Rma(period: 14), + new Sinema(period: 14), + new Sma(period: 14), + new Smma(period: 14), + new T3(period: 14), + new Tema(period: 14), + new Trima(period: 14), + new Vidya(shortPeriod: 14, longPeriod: 30, alpha: 0.2), + new Wma(period: 14), + new Zlema(period: 14), + new Entropy(period: 14), + new Kurtosis(period: 14), + new Max(period: 14, decay: 0.01), + new Min(period: 14, decay: 0.01), + new Median(period: 14), + new Mode(period: 14), + new Percentile(period: 14, percent: 50), + new Skew(period: 14), + new Stddev(period: 14), + new Variance(period: 14), + new Zscore(period: 14) + + ]; + + [Theory] + [MemberData(nameof(GetIndicators))] + public void IndicatorIsNew(iTValue indicator) + { + var indicator1 = indicator; + var indicator2 = indicator; + + MethodInfo calcMethod = indicator.GetType().GetMethod("Calc")!; + if (calcMethod == null) + { + throw new Exception($"Calc method not found for indicator type: {indicator.GetType().Name}"); + } + + for (int i = 0; i < SeriesLen; i++) + { + TValue item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: true); + calcMethod.Invoke(indicator1, new object[] { item1 }); + + for (int j = 0; j < Corrections; j++) + { + item1 = new(Time: DateTime.Now, Value: rnd.Next(-100, 100), IsNew: false); + calcMethod.Invoke(indicator1, new object[] { item1 }); + } + + var item2 = new TValue(item1.Time, item1.Value, IsNew: true); + calcMethod.Invoke(indicator2, new object[] { item2 }); + + Assert.Equal(indicator1.Value, indicator2.Value); + } + } + + public static IEnumerable GetIndicators() + { + return indicators.Select(indicator => new object[] { indicator }); + } + } +} \ No newline at end of file diff --git a/Tests/test_skender.stock.cs b/Tests/test_skender.stock.cs index 9602b2d5..e652c773 100644 --- a/Tests/test_skender.stock.cs +++ b/Tests/test_skender.stock.cs @@ -320,4 +320,24 @@ public class SkenderTests } } + [Fact] + public void ATR() + { + for (int run = 0; run < iterations; run++) + { + period = rnd.Next(50) + 5; + Atr ma = new(period: period); + TSeries QL = new(); + foreach (TBar item in bars) { QL.Add(ma.Calc(item)); } + + var SK = quotes.GetAtr(lookbackPeriods: period).Select(i => i.Atr.Null2NaN()!); + Assert.Equal(QL.Length, QL.Length); + + for (int i = QL.Length - 1; i > period +500; i--) + { + Assert.InRange(SK.ElementAt(i) - QL[i].Value, -range, range); + } + } + } + } \ No newline at end of file diff --git a/docs/Progress.md b/docs/Progress.md new file mode 100644 index 00000000..5847f8dc --- /dev/null +++ b/docs/Progress.md @@ -0,0 +1,5 @@ +# Backlog and done + +|**QT**|**Cht**|Cmnt|Docs|isNew|Valid| +|--|:--:|:--:|:--:|:--:|:--:| +|AFIRMA|✔️||||| \ No newline at end of file diff --git a/docs/index.html b/docs/index.html index 5ff7bd52..4c1158b0 100644 --- a/docs/index.html +++ b/docs/index.html @@ -8,6 +8,7 @@ + +
@@ -40,10 +68,10 @@ window.$docsify = { homepage: 'readme.md', loadSidebar: true, - subMaxLevel: 1, + subMaxLevel: 4, // Increased to allow for level 4+ items name: '', repo: '', - sidebarDisplayLevel: 2, + sidebarDisplayLevel: 3, // Changed to keep levels 1, 2, and 3 expanded themeable: { readyTransition: true, responsiveTables: true diff --git a/docs/readme.md b/docs/readme.md index 32247e64..aa548a91 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -16,7 +16,8 @@ **Quan**titative **TA** **lib**rary (QuanTAlib) is a C# library of classess and methods for quantitative technical analysis useful for analyzing quotes with [Quantower](https://www.quantower.com/) and other C#-based trading platforms. -[**Visit documentation pages**](https://mihakralj.github.io/QuanTAlib/#/) +[**Visit documentation pages**](https://mihakralj.github.io/QuanTAlib/#/)
+[**List of indicators - implemented and planned**](indicators/indicators.md) **QuanTAlib** is a C# library written with some specific design criteria in mind. Here is why there is '_yet another C# TA library_': @@ -24,10 +25,6 @@ - **Allow updates/corrections** of the last quote - QuanTAlib is re-calculating the last value as many times as required before continuing to the new bar - **Calculate early data right** - calculated data is as valid as mathematically possible from the first value onwards - no blackout or warming-up periods. All indicators return data from the first bar, alongside with a flag `isHot` - defining if calculation is already stable. -### Coverage - -[List of indicators - implemented and planned](indicators/indicators.md) - ## Installation to Quantower - `` is the directory where Quantower is installed - where `Start.lnk` launcher is diff --git a/lib/averages/Ama.cs b/lib/averages/Ama.cs deleted file mode 100644 index 78beb01e..00000000 --- a/lib/averages/Ama.cs +++ /dev/null @@ -1,89 +0,0 @@ -namespace QuanTAlib; - -public class Ama : AbstractBase -{ - private readonly int Period; - private readonly CircularBuffer _buffer; - private readonly double _alpha; // Adaptive factor - private double _lastAfirma, _p_lastAfirma; - private double _lastError, _p_lastError; - - public Ama(int period, double alpha = 0.1) - { - if (period < 1) - { - throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); - } - if (alpha <= 0 || alpha >= 1) - { - throw new ArgumentOutOfRangeException(nameof(alpha), "Alpha must be between 0 and 1 (exclusive)."); - } - Period = period; - WarmupPeriod = period; - _buffer = new CircularBuffer(period); - _alpha = alpha; - Name = "Afirma"; - WarmupPeriod = period; - Init(); - } - - public Ama(object source, int period, double alpha = 0.1) : this(period: period, alpha: alpha) - { - var pubEvent = source.GetType().GetEvent("Pub"); - pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); - } - - public override void Init() - { - base.Init(); - _lastAfirma = 0; - _lastError = 0; - } - - protected override void ManageState(bool isNew) - { - if (isNew) - { - _lastValidValue = Input.Value; - _index++; - _p_lastAfirma = _lastAfirma; - _p_lastError = _lastError; - } - else - { - _lastAfirma = _p_lastAfirma; - _lastError = _p_lastError; - } - } - - /// - /// Core AFIRMA calculation - /// - protected override double Calculation() - { - double result; - ManageState(IsNew); - _buffer.Add(Input.Value, Input.IsNew); - - if (_index < Period) - { - // Use simple average during warmup period - result = _buffer.Average(); - } - else - { - // AFIRMA calculation - double sma = _buffer.Average(); - double error = Input.Value - _lastAfirma; - double denominator = Math.Abs(error) + Math.Abs(_lastError); - double adaptiveFactor = denominator != 0 ? _alpha * Math.Abs(error) / denominator : _alpha; - result = sma + adaptiveFactor * (Input.Value - sma); - - _lastError = error; - } - - _lastAfirma = result; - IsHot = _index >= WarmupPeriod; - return result; - } -} \ No newline at end of file diff --git a/lib/averages/Mama.cs b/lib/averages/Mama.cs index b090e52d..5372fd35 100644 --- a/lib/averages/Mama.cs +++ b/lib/averages/Mama.cs @@ -40,7 +40,6 @@ public class Mama : AbstractBase public override void Init() { Fama = new TValue(); - base.Init(); } protected override void ManageState(bool isNew) diff --git a/lib/averages/T3.cs b/lib/averages/T3.cs index c2b216d7..1007b943 100644 --- a/lib/averages/T3.cs +++ b/lib/averages/T3.cs @@ -1,7 +1,6 @@ namespace QuanTAlib; -public class T3 : AbstractBase -{ +public class T3 : AbstractBase { private readonly int _period; private readonly bool _useSma; private readonly double _k, _c1, _c2, _c3, _c4; @@ -9,10 +8,8 @@ public class T3 : AbstractBase private double _lastEma1, _lastEma2, _lastEma3, _lastEma4, _lastEma5, _lastEma6; private double _p_lastEma1, _p_lastEma2, _p_lastEma3, _p_lastEma4, _p_lastEma5, _p_lastEma6; - public T3(int period, double vfactor = 0.7, bool useSma = true) - { - if (period < 1) - { + public T3(int period, double vfactor = 0.7, bool useSma = true) { + if (period < 1) { throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); } _period = period; @@ -37,15 +34,12 @@ public class T3 : AbstractBase Init(); } - public T3(object source, int period, double vfactor = 0.7, bool useSma = true) : this(period, vfactor, useSma) - { + public T3(object source, int period, double vfactor = 0.7, bool useSma = true) : this(period, vfactor, useSma) { var pubEvent = source.GetType().GetEvent("Pub"); pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); } - public override void Init() - { - base.Init(); + public override void Init() { _lastEma1 = _lastEma2 = _lastEma3 = _lastEma4 = _lastEma5 = _lastEma6 = 0; _buffer1.Clear(); _buffer2.Clear(); @@ -55,10 +49,8 @@ public class T3 : AbstractBase _buffer6.Clear(); } - protected override void ManageState(bool isNew) - { - if (isNew) - { + protected override void ManageState(bool isNew) { + if (isNew) { _lastValidValue = Input.Value; _index++; _p_lastEma1 = _lastEma1; @@ -67,9 +59,7 @@ public class T3 : AbstractBase _p_lastEma4 = _lastEma4; _p_lastEma5 = _lastEma5; _p_lastEma6 = _lastEma6; - } - else - { + } else { _lastEma1 = _p_lastEma1; _lastEma2 = _p_lastEma2; _lastEma3 = _p_lastEma3; @@ -80,18 +70,14 @@ public class T3 : AbstractBase } - protected override double Calculation() - { + protected override double Calculation() { ManageState(Input.IsNew); double ema1, ema2, ema3, ema4, ema5, ema6; - if (_index == 1) - { + if (_index == 1) { ema1 = ema2 = ema3 = ema4 = ema5 = ema6 = Input.Value; - } - else if (_index <= _period && _useSma) - { + } else if (_index <= _period && _useSma) { _buffer1.Add(Input.Value, Input.IsNew); ema1 = _buffer1.Average(); _buffer2.Add(ema1, Input.IsNew); @@ -104,9 +90,7 @@ public class T3 : AbstractBase ema5 = _buffer5.Average(); _buffer6.Add(ema5, Input.IsNew); ema6 = _buffer6.Average(); - } - else - { + } else { ema1 = _k * (Input.Value - _lastEma1) + _lastEma1; ema2 = _k * (ema1 - _lastEma2) + _lastEma2; ema3 = _k * (ema2 - _lastEma3) + _lastEma3; diff --git a/lib/core/AbstractBarBase.cs b/lib/core/AbstractBarBase.cs new file mode 100644 index 00000000..cd643345 --- /dev/null +++ b/lib/core/AbstractBarBase.cs @@ -0,0 +1,71 @@ +namespace QuanTAlib; + +/// +/// Provides a base implementation for financial indicators in the QuanTAlib library. +/// This abstract class implements the iTValue interface and defines common properties +/// and methods used by inheriting indicator types. +/// +public abstract class AbstractBarBase : iTValue +{ + public DateTime Time { get; set; } + public double Value { get; set; } + public bool IsNew { get; set; } + public bool IsHot { get; set; } + + public TBar Input { get; set; } + public String Name { get; set; } = ""; + public int WarmupPeriod { get; set; } + + public TValue Tick => new(Time, Value, IsNew, IsHot); // Stores the current value of indicator + public event ValueSignal Pub = delegate { }; // Publisher of generated values + + protected int _index; //tracking the position of output + protected double _lastValidValue; + // other _internal vars defined here + + protected AbstractBarBase() + { //add parameters into constructor + } + + public void Sub(object source, in TBarEventArgs args) => Calc(args.Bar); + + public virtual void Init() + { + _index = 0; + _lastValidValue = 0; + } + + public virtual TValue Calc(TBar input) + { + Input = input; + if (double.IsNaN(input.Close) || double.IsInfinity(input.Close)) + { + return Process(new TValue(Time: input.Time, Value: GetLastValid(), IsNew: input.IsNew, IsHot: true)); + } + this.Value = Calculation(); + return Process(new TValue(Time: Input.Time, Value: this.Value, IsNew: Input.IsNew, IsHot: this.IsHot)); + } + + protected virtual double GetLastValid() + { + return this.Value; + } + protected abstract void ManageState(bool isNew); + protected abstract double Calculation(); + + /// + /// Processes the calculated value, updates the indicator's own state, + /// and publishes the result through an event. + /// + /// The calculated TValue to process. + /// The processed TValue. + protected virtual TValue Process(TValue value) + { + this.Time = value.Time; + this.Value = value.Value; + this.IsNew = value.IsNew; + this.IsHot = value.IsHot; + Pub?.Invoke(this, new ValueEventArgs(value)); + return value; + } +} diff --git a/lib/core/tbar.cs b/lib/core/tbar.cs index 0326b043..677db00c 100644 --- a/lib/core/tbar.cs +++ b/lib/core/tbar.cs @@ -84,7 +84,8 @@ public class TBarSeries : List public new virtual void Add(TBar bar) { - if (bar.IsNew) { base.Add(bar); } else { this[^1] = bar; } + if (bar.IsNew || base.Count == 0) { base.Add(bar); } + else { this[^1] = bar; } Pub?.Invoke(this, new TBarEventArgs(bar)); Open.Add(bar.Time, bar.Open, IsNew: bar.IsNew, IsHot: true); diff --git a/lib/core/tvalue.cs b/lib/core/tvalue.cs index fa06f0f5..d72c8cd5 100644 --- a/lib/core/tvalue.cs +++ b/lib/core/tvalue.cs @@ -60,7 +60,7 @@ public class TSeries : List public new virtual void Add(TValue tick) { - if (tick.IsNew) { base.Add(tick); } + if (tick.IsNew || base.Count==0) { base.Add(tick); } else { this[^1] = tick; } Pub?.Invoke(this, new ValueEventArgs(tick)); } diff --git a/lib/quantalib.csproj b/lib/quantalib.csproj index 53b82781..c342f1cd 100644 --- a/lib/quantalib.csproj +++ b/lib/quantalib.csproj @@ -23,7 +23,6 @@ - diff --git a/lib/volatility/Atr.cs b/lib/volatility/Atr.cs new file mode 100644 index 00000000..1ab0299a --- /dev/null +++ b/lib/volatility/Atr.cs @@ -0,0 +1,71 @@ +namespace QuanTAlib; + +public class Atr : AbstractBarBase +{ + private readonly int _period; + private readonly Ema _ma; + private double _prevClose, _p_prevClose; + + public Atr(int period) : base() + { + if (period < 1) + { + throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1."); + } + _period = period; + _ma = new(1.0/period); + WarmupPeriod = _ma.WarmupPeriod; + Name = $"ATR({_period})"; + } + + public Atr(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new BarSignal(Sub)); + } + + public override void Init() + { + base.Init(); + _ma.Init(); + _prevClose = double.NaN; + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _index++; + _p_prevClose = _prevClose; + } + else + { + _prevClose = _p_prevClose; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + double trueRange = Math.Max( + Math.Max( + Input.High - Input.Low, + Math.Abs(Input.High - _prevClose) + ), + Math.Abs(Input.Low - _prevClose) + ); + if (_index < 2) + { + trueRange = Input.High - Input.Low; + } + + TValue emaTrueRange = _ma.Calc(new TValue(Input.Time, trueRange, Input.IsNew)); + IsHot = _ma.IsHot; + _prevClose = Input.Close; + + return emaTrueRange.Value; + } + +} + diff --git a/notebooks/Skender.dib b/notebooks/Skender.dib new file mode 100644 index 00000000..52eab105 --- /dev/null +++ b/notebooks/Skender.dib @@ -0,0 +1,45 @@ +#!meta + +{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"name":"csharp"}]}} + +#!csharp + +#r "nuget:Skender.Stock.Indicators" +#r "..\lib\obj\Debug\QuanTAlib.dll" + +#!csharp + +using Skender.Stock.Indicators; +using QuanTAlib; + +GbmFeed gbm = new(); +Atr atr = new(gbm, 5); +TSeries res = new(atr); +gbm.Add(100); + +IEnumerable quotes = gbm.Select(item => new Quote { Date = item.Time, Open = (decimal)item.Open, + High = (decimal)item.High, Low = (decimal)item.Low, Close = (decimal)item.Close, Volume = (decimal)item.Volume }); +var SkResults = quotes.GetAtr(5).Select(i => i.Atr.Null2NaN()!); +for (int i=0; i< gbm.Length; i++) { + Console.WriteLine($"{gbm.High[i].Value,6:F2} {gbm.Low[i].Value,6:F2} {gbm.Close[i].Value,6:F2}\t\t{res[i].Value,10:F4} {SkResults.ElementAt(i),10:F4}"); +} + +#!csharp + +Random rnd = new((int)DateTime.Now.Ticks); +GbmFeed feed = new(sigma: 0.5, mu: 0.0); +TBarSeries bars = new(feed); +feed.Add(20); +IEnumerable quotes; + +int period = rnd.Next(5) + 2; +Atr ma = new(period: period); +TSeries QL = new(); +foreach (TBar item in bars) { + Console.WriteLine($"{ma.Calc(item)}"); + //QL.Add(ma.Calc(item)); +} + +#!csharp + +bars diff --git a/quantower/Averages/AmaIndicator.cs b/quantower/Averages/AmaIndicator.cs deleted file mode 100644 index 417700b5..00000000 --- a/quantower/Averages/AmaIndicator.cs +++ /dev/null @@ -1,25 +0,0 @@ -using TradingPlatform.BusinessLayer; -namespace QuanTAlib; - -public class AmaIndicator : IndicatorBase -{ - [InputParameter("Period", sortIndex: 1, 2, 2000, 1, 0)] - public int Period { get; set; } = 10; - - [InputParameter("Alpha", sortIndex: 2, minimum: -0.01, maximum: 1.0, increment: 0.01, decimalPlaces: 2)] - public double Alpha { get; set; } = 0.1; - private Ama? ma; - protected override AbstractBase QuanTAlib => ma!; - public override string ShortName => $"AMA {Period} : {Alpha} : {SourceName}"; - - - public AmaIndicator() - { - Name = "AMA - Adaptive Moving Average"; - } - - protected override void InitIndicator() - { - ma = new Ama(period: Period, alpha: Alpha); - } -} diff --git a/quantower/Averages/Averages.csproj b/quantower/Averages/Averages.csproj index 3fb41cb2..77c81333 100644 --- a/quantower/Averages/Averages.csproj +++ b/quantower/Averages/Averages.csproj @@ -8,7 +8,6 @@
- lib\%(RecursiveDir)%(Filename)%(Extension) diff --git a/quantower/Statistics/Statistics.csproj b/quantower/Statistics/Statistics.csproj index 36fadba0..262800f0 100644 --- a/quantower/Statistics/Statistics.csproj +++ b/quantower/Statistics/Statistics.csproj @@ -8,7 +8,6 @@ - lib\%(RecursiveDir)%(Filename)%(Extension) diff --git a/quantower/Statistics/_IndicatorBarBase.cs b/quantower/Statistics/_IndicatorBarBase.cs new file mode 100644 index 00000000..a5fae449 --- /dev/null +++ b/quantower/Statistics/_IndicatorBarBase.cs @@ -0,0 +1,136 @@ +using System.Drawing; +using TradingPlatform.BusinessLayer; +using TradingPlatform.BusinessLayer.Chart; +using System.Runtime.CompilerServices; +using System.Drawing.Drawing2D; +using System.Collections; +using TradingPlatform.BusinessLayer.TimeSync; + +namespace QuanTAlib; + +#pragma warning disable CA1416 // Validate platform compatibility +public abstract class IndicatorBarBase : Indicator, IWatchlistIndicator +{ + + [InputParameter("Show cold values", sortIndex: 20)] + public bool ShowColdValues { get; set; } = true; + public int MinHistoryDepths { get; set; } + + // LineSeries.LineSeries(string, Color, int, LineStyle)' + + protected LineSeries? Series; + protected abstract AbstractBarBase QuanTAlib { get; } + + int IWatchlistIndicator.MinHistoryDepths => 0; + + protected IndicatorBarBase() + { + OnBackGround = true; + SeparateWindow = false; + Series = new(name: $"{Name}", color: Color.RoyalBlue, width: 2, style: LineStyle.Solid); + + AddLineSeries(Series); + } + + protected abstract void InitIndicator(); + + protected override void OnInit() + { + InitIndicator(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TBar bar = new(Time: Time(), + Open: GetPrice(PriceType.Open), + High: GetPrice(PriceType.High), + Low: GetPrice(PriceType.Low), + Close: GetPrice(PriceType.Close), + Volume: GetPrice(PriceType.Volume), + IsNew: args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar); + + TValue result = QuanTAlib.Calc(bar); + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); + + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + List allPoints = new List(); + if (CurrentChart == null) { return; } + + Graphics gr = args.Graphics; + + var mainWindow = this.CurrentChart.Windows[args.WindowIndex]; + var converter = mainWindow.CoordinatesConverter; + var clientRect = mainWindow.ClientRectangle; + + gr.SetClip(clientRect); + DateTime leftTime = new[] { converter.GetTime(clientRect.Left), Time(this.Count - 1) }.Max(); + DateTime rightTime = new[] { converter.GetTime(clientRect.Right), Time(0) }.Min(); + + int leftIndex = (int)HistoricalData.GetIndexByTime(leftTime.Ticks) + 1; + int rightIndex = (int)HistoricalData.GetIndexByTime(rightTime.Ticks); + + for (int i = rightIndex; i < leftIndex; i++) + { + int barX = (int)converter.GetChartX(Time(i)); + int barY = (int)converter.GetChartY(Series![i]); + int halfBarWidth = CurrentChart.BarsWidth / 2; + Point point = new Point(barX + halfBarWidth, barY); + allPoints.Add(point); + } + + if (allPoints.Count > 1) + { + DrawSmoothCombinedCurve(gr, allPoints, this.Count - QuanTAlib.WarmupPeriod - rightIndex); + } + } + + private void DrawSmoothCombinedCurve(Graphics gr, List allPoints, int hotCount) + { + if (allPoints.Count < 2) { return; } + + using (Pen defaultPen = new(Series!.Color, Series.Width) { DashStyle = ConvertLineStyleToDashStyle(Series.Style) }) + using (Pen coldPen = new(Series!.Color, Series.Width) { DashStyle = DashStyle.Dot }) + { + // Draw the hot part + if (hotCount > 0) + { + var hotPoints = allPoints.Take(Math.Min(hotCount + 1, allPoints.Count)).ToArray(); + gr.DrawCurve(defaultPen, hotPoints, 0, hotPoints.Length - 1, (float)0.1); + } + + // Draw the cold part + if (ShowColdValues && hotCount < allPoints.Count) + { + var coldPoints = allPoints.Skip(Math.Max(0, hotCount)).ToArray(); + gr.DrawCurve(coldPen, coldPoints, 0, coldPoints.Length - 1, (float)0.1); + } + } + } + private static DashStyle ConvertLineStyleToDashStyle(LineStyle lineStyle) + { + return lineStyle switch + { + LineStyle.Solid => DashStyle.Solid, + LineStyle.Dash => DashStyle.Dash, + LineStyle.Dot => DashStyle.Dot, + LineStyle.DashDot => DashStyle.DashDot, + _ => DashStyle.Solid, + }; + } + protected static void DrawText(Graphics gr, string text, Rectangle clientRect) + { + Font font = new Font("Inter", 8); + SizeF textSize = gr.MeasureString(text, font); + RectangleF textRect = new RectangleF(clientRect.Left + 5, + clientRect.Bottom - textSize.Height - 10, + textSize.Width + 10, textSize.Height + 10); + gr.FillRectangle(SystemBrushes.ControlDarkDark, textRect); + gr.DrawString(text, font, Brushes.White, new PointF(textRect.X + 6, textRect.Y + 5)); + } +} \ No newline at end of file diff --git a/quantower/Volatility/AtrIndicator.cs b/quantower/Volatility/AtrIndicator.cs new file mode 100644 index 00000000..fe1b0738 --- /dev/null +++ b/quantower/Volatility/AtrIndicator.cs @@ -0,0 +1,23 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class AtrIndicator : IndicatorBarBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 20; + + private Atr? atr; + protected override AbstractBarBase QuanTAlib => atr!; + public override string ShortName => $"ATR {Period}"; + public AtrIndicator() + { + Name = "ATR - Average True Range"; + SeparateWindow = true; + } + + protected override void InitIndicator() + { + atr = new(Period); + MinHistoryDepths = atr!.WarmupPeriod; + } +} \ No newline at end of file diff --git a/quantower/Volatility/Volatility.csproj b/quantower/Volatility/Volatility.csproj new file mode 100644 index 00000000..7247b808 --- /dev/null +++ b/quantower/Volatility/Volatility.csproj @@ -0,0 +1,29 @@ + + + Indicator + bin\$(Configuration)\ + true + true + true + + + + + lib\%(RecursiveDir)%(Filename)%(Extension) + + + + + + + + + + ..\..\.github\TradingPlatform.BusinessLayer.dll + + + TradingPlatform.BusinessLayer.xml + + + + \ No newline at end of file diff --git a/quantower/Volatility/_IndicatorBarBase.cs b/quantower/Volatility/_IndicatorBarBase.cs new file mode 100644 index 00000000..a5fae449 --- /dev/null +++ b/quantower/Volatility/_IndicatorBarBase.cs @@ -0,0 +1,136 @@ +using System.Drawing; +using TradingPlatform.BusinessLayer; +using TradingPlatform.BusinessLayer.Chart; +using System.Runtime.CompilerServices; +using System.Drawing.Drawing2D; +using System.Collections; +using TradingPlatform.BusinessLayer.TimeSync; + +namespace QuanTAlib; + +#pragma warning disable CA1416 // Validate platform compatibility +public abstract class IndicatorBarBase : Indicator, IWatchlistIndicator +{ + + [InputParameter("Show cold values", sortIndex: 20)] + public bool ShowColdValues { get; set; } = true; + public int MinHistoryDepths { get; set; } + + // LineSeries.LineSeries(string, Color, int, LineStyle)' + + protected LineSeries? Series; + protected abstract AbstractBarBase QuanTAlib { get; } + + int IWatchlistIndicator.MinHistoryDepths => 0; + + protected IndicatorBarBase() + { + OnBackGround = true; + SeparateWindow = false; + Series = new(name: $"{Name}", color: Color.RoyalBlue, width: 2, style: LineStyle.Solid); + + AddLineSeries(Series); + } + + protected abstract void InitIndicator(); + + protected override void OnInit() + { + InitIndicator(); + base.OnInit(); + } + + protected override void OnUpdate(UpdateArgs args) + { + TBar bar = new(Time: Time(), + Open: GetPrice(PriceType.Open), + High: GetPrice(PriceType.High), + Low: GetPrice(PriceType.Low), + Close: GetPrice(PriceType.Close), + Volume: GetPrice(PriceType.Volume), + IsNew: args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar); + + TValue result = QuanTAlib.Calc(bar); + Series!.SetValue(result.Value); + Series!.SetMarker(0, Color.Transparent); + + } + + public override void OnPaintChart(PaintChartEventArgs args) + { + base.OnPaintChart(args); + List allPoints = new List(); + if (CurrentChart == null) { return; } + + Graphics gr = args.Graphics; + + var mainWindow = this.CurrentChart.Windows[args.WindowIndex]; + var converter = mainWindow.CoordinatesConverter; + var clientRect = mainWindow.ClientRectangle; + + gr.SetClip(clientRect); + DateTime leftTime = new[] { converter.GetTime(clientRect.Left), Time(this.Count - 1) }.Max(); + DateTime rightTime = new[] { converter.GetTime(clientRect.Right), Time(0) }.Min(); + + int leftIndex = (int)HistoricalData.GetIndexByTime(leftTime.Ticks) + 1; + int rightIndex = (int)HistoricalData.GetIndexByTime(rightTime.Ticks); + + for (int i = rightIndex; i < leftIndex; i++) + { + int barX = (int)converter.GetChartX(Time(i)); + int barY = (int)converter.GetChartY(Series![i]); + int halfBarWidth = CurrentChart.BarsWidth / 2; + Point point = new Point(barX + halfBarWidth, barY); + allPoints.Add(point); + } + + if (allPoints.Count > 1) + { + DrawSmoothCombinedCurve(gr, allPoints, this.Count - QuanTAlib.WarmupPeriod - rightIndex); + } + } + + private void DrawSmoothCombinedCurve(Graphics gr, List allPoints, int hotCount) + { + if (allPoints.Count < 2) { return; } + + using (Pen defaultPen = new(Series!.Color, Series.Width) { DashStyle = ConvertLineStyleToDashStyle(Series.Style) }) + using (Pen coldPen = new(Series!.Color, Series.Width) { DashStyle = DashStyle.Dot }) + { + // Draw the hot part + if (hotCount > 0) + { + var hotPoints = allPoints.Take(Math.Min(hotCount + 1, allPoints.Count)).ToArray(); + gr.DrawCurve(defaultPen, hotPoints, 0, hotPoints.Length - 1, (float)0.1); + } + + // Draw the cold part + if (ShowColdValues && hotCount < allPoints.Count) + { + var coldPoints = allPoints.Skip(Math.Max(0, hotCount)).ToArray(); + gr.DrawCurve(coldPen, coldPoints, 0, coldPoints.Length - 1, (float)0.1); + } + } + } + private static DashStyle ConvertLineStyleToDashStyle(LineStyle lineStyle) + { + return lineStyle switch + { + LineStyle.Solid => DashStyle.Solid, + LineStyle.Dash => DashStyle.Dash, + LineStyle.Dot => DashStyle.Dot, + LineStyle.DashDot => DashStyle.DashDot, + _ => DashStyle.Solid, + }; + } + protected static void DrawText(Graphics gr, string text, Rectangle clientRect) + { + Font font = new Font("Inter", 8); + SizeF textSize = gr.MeasureString(text, font); + RectangleF textRect = new RectangleF(clientRect.Left + 5, + clientRect.Bottom - textSize.Height - 10, + textSize.Width + 10, textSize.Height + 10); + gr.FillRectangle(SystemBrushes.ControlDarkDark, textRect); + gr.DrawString(text, font, Brushes.White, new PointF(textRect.X + 6, textRect.Y + 5)); + } +} \ No newline at end of file From d5bf378f69d30a0d4d77e901226f4fcdcfc4c2bf Mon Sep 17 00:00:00 2001 From: Miha Kralj Date: Mon, 30 Sep 2024 07:21:15 -0700 Subject: [PATCH 4/5] sln file --- QuanTAlib.sln | 83 ++++++++++++++++++++------------------------------- 1 file changed, 33 insertions(+), 50 deletions(-) diff --git a/QuanTAlib.sln b/QuanTAlib.sln index 8727aad2..aadc2553 100644 --- a/QuanTAlib.sln +++ b/QuanTAlib.sln @@ -1,68 +1,51 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 -VisualStudioVersion = 17.5.002.0 +VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "benchmark", "benchmark\benchmark.csproj", "{DBB674D9-43AA-4383-A4D0-E791847C7145}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "quantalib", "lib\quantalib.csproj", "{QUANTALIB_GUID}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "quantalib", "lib\quantalib.csproj", "{D2F742C0-9E2A-4B07-8D9F-0DF0BA3DDA31}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "tests\Tests.csproj", "{TESTS_GUID}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SyntheticVendor", "SyntheticVendor\SyntheticVendor.csproj", "{1777C7BB-F67A-4F00-99BB-EE48178A7129}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SyntheticVendor", "SyntheticVendor\SyntheticVendor.csproj", "{SYNTHETICVENDOR_GUID}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "tests\Tests.csproj", "{A89D1A5D-9DB9-4E5D-A7D6-E153509430CD}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Averages", "quantower\Averages\Averages.csproj", "{AVERAGES_GUID}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "quantower", "quantower", "{1EC1B338-6AD9-4E3D-82C3-6591CC148DD5}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Statistics", "quantower\Statistics\Statistics.csproj", "{STATISTICS_GUID}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Averages", "quantower\Averages\Averages.csproj", "{61AFF1E5-9E2B-47F4-9665-D8358C5BDD6F}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Statistics", "quantower\Statistics\Statistics.csproj", "{7EBB6ECD-15FA-44DD-B231-FED97E0B710D}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volatility", "quantower\Volatility\Volatility.csproj", "{9190CFDD-9FD8-4E14-9269-79325B03EC6C}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volatility", "quantower\Volatility\Volatility.csproj", "{VOLATILITY_GUID}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {DBB674D9-43AA-4383-A4D0-E791847C7145}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DBB674D9-43AA-4383-A4D0-E791847C7145}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DBB674D9-43AA-4383-A4D0-E791847C7145}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DBB674D9-43AA-4383-A4D0-E791847C7145}.Release|Any CPU.Build.0 = Release|Any CPU - {D2F742C0-9E2A-4B07-8D9F-0DF0BA3DDA31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D2F742C0-9E2A-4B07-8D9F-0DF0BA3DDA31}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D2F742C0-9E2A-4B07-8D9F-0DF0BA3DDA31}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D2F742C0-9E2A-4B07-8D9F-0DF0BA3DDA31}.Release|Any CPU.Build.0 = Release|Any CPU - {1777C7BB-F67A-4F00-99BB-EE48178A7129}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1777C7BB-F67A-4F00-99BB-EE48178A7129}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1777C7BB-F67A-4F00-99BB-EE48178A7129}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1777C7BB-F67A-4F00-99BB-EE48178A7129}.Release|Any CPU.Build.0 = Release|Any CPU - {A89D1A5D-9DB9-4E5D-A7D6-E153509430CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A89D1A5D-9DB9-4E5D-A7D6-E153509430CD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A89D1A5D-9DB9-4E5D-A7D6-E153509430CD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A89D1A5D-9DB9-4E5D-A7D6-E153509430CD}.Release|Any CPU.Build.0 = Release|Any CPU - {61AFF1E5-9E2B-47F4-9665-D8358C5BDD6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {61AFF1E5-9E2B-47F4-9665-D8358C5BDD6F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {61AFF1E5-9E2B-47F4-9665-D8358C5BDD6F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {61AFF1E5-9E2B-47F4-9665-D8358C5BDD6F}.Release|Any CPU.Build.0 = Release|Any CPU - {7EBB6ECD-15FA-44DD-B231-FED97E0B710D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7EBB6ECD-15FA-44DD-B231-FED97E0B710D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7EBB6ECD-15FA-44DD-B231-FED97E0B710D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7EBB6ECD-15FA-44DD-B231-FED97E0B710D}.Release|Any CPU.Build.0 = Release|Any CPU - {9190CFDD-9FD8-4E14-9269-79325B03EC6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9190CFDD-9FD8-4E14-9269-79325B03EC6C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9190CFDD-9FD8-4E14-9269-79325B03EC6C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9190CFDD-9FD8-4E14-9269-79325B03EC6C}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {61AFF1E5-9E2B-47F4-9665-D8358C5BDD6F} = {1EC1B338-6AD9-4E3D-82C3-6591CC148DD5} - {7EBB6ECD-15FA-44DD-B231-FED97E0B710D} = {1EC1B338-6AD9-4E3D-82C3-6591CC148DD5} - {9190CFDD-9FD8-4E14-9269-79325B03EC6C} = {1EC1B338-6AD9-4E3D-82C3-6591CC148DD5} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {080D10E4-B741-4A85-B37B-08845A5C78D9} + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {QUANTALIB_GUID}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {QUANTALIB_GUID}.Debug|Any CPU.Build.0 = Debug|Any CPU + {QUANTALIB_GUID}.Release|Any CPU.ActiveCfg = Release|Any CPU + {QUANTALIB_GUID}.Release|Any CPU.Build.0 = Release|Any CPU + {TESTS_GUID}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {TESTS_GUID}.Debug|Any CPU.Build.0 = Debug|Any CPU + {TESTS_GUID}.Release|Any CPU.ActiveCfg = Release|Any CPU + {TESTS_GUID}.Release|Any CPU.Build.0 = Release|Any CPU + {SYNTHETICVENDOR_GUID}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {SYNTHETICVENDOR_GUID}.Debug|Any CPU.Build.0 = Debug|Any CPU + {SYNTHETICVENDOR_GUID}.Release|Any CPU.ActiveCfg = Release|Any CPU + {SYNTHETICVENDOR_GUID}.Release|Any CPU.Build.0 = Release|Any CPU + {AVERAGES_GUID}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AVERAGES_GUID}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AVERAGES_GUID}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AVERAGES_GUID}.Release|Any CPU.Build.0 = Release|Any CPU + {STATISTICS_GUID}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {STATISTICS_GUID}.Debug|Any CPU.Build.0 = Debug|Any CPU + {STATISTICS_GUID}.Release|Any CPU.ActiveCfg = Release|Any CPU + {STATISTICS_GUID}.Release|Any CPU.Build.0 = Release|Any CPU + {VOLATILITY_GUID}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {VOLATILITY_GUID}.Debug|Any CPU.Build.0 = Debug|Any CPU + {VOLATILITY_GUID}.Release|Any CPU.ActiveCfg = Release|Any CPU + {VOLATILITY_GUID}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal From 060567b1f0ced18455ca61f172f09ab0c8a8f06b Mon Sep 17 00:00:00 2001 From: Miha Kralj Date: Mon, 30 Sep 2024 07:21:15 -0700 Subject: [PATCH 5/5] sln file --- QuanTAlib.sln | 83 ++++++++++++++++++++------------------------------- 1 file changed, 33 insertions(+), 50 deletions(-) diff --git a/QuanTAlib.sln b/QuanTAlib.sln index 8727aad2..07695eaf 100644 --- a/QuanTAlib.sln +++ b/QuanTAlib.sln @@ -1,68 +1,51 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 -VisualStudioVersion = 17.5.002.0 +VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "benchmark", "benchmark\benchmark.csproj", "{DBB674D9-43AA-4383-A4D0-E791847C7145}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "quantalib", "lib\quantalib.csproj", "{A1B2C3D4-E5F6-47G8-H9I0-J1K2L3M4N5O6}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "quantalib", "lib\quantalib.csproj", "{D2F742C0-9E2A-4B07-8D9F-0DF0BA3DDA31}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "tests\Tests.csproj", "{B2C3D4E5-F6G7-48H9-I0J1-K2L3M4N5O6P7}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SyntheticVendor", "SyntheticVendor\SyntheticVendor.csproj", "{1777C7BB-F67A-4F00-99BB-EE48178A7129}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SyntheticVendor", "SyntheticVendor\SyntheticVendor.csproj", "{C3D4E5F6-G7H8-49I0-J1K2-L3M4N5O6P7Q8}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "tests\Tests.csproj", "{A89D1A5D-9DB9-4E5D-A7D6-E153509430CD}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Averages", "quantower\Averages\Averages.csproj", "{D4E5F6G7-H8I9-50J1-K2L3-M4N5O6P7Q8R9}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "quantower", "quantower", "{1EC1B338-6AD9-4E3D-82C3-6591CC148DD5}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Statistics", "quantower\Statistics\Statistics.csproj", "{E5F6G7H8-I9J0-51K2-L3M4-N5O6P7Q8R9S0}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Averages", "quantower\Averages\Averages.csproj", "{61AFF1E5-9E2B-47F4-9665-D8358C5BDD6F}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Statistics", "quantower\Statistics\Statistics.csproj", "{7EBB6ECD-15FA-44DD-B231-FED97E0B710D}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volatility", "quantower\Volatility\Volatility.csproj", "{9190CFDD-9FD8-4E14-9269-79325B03EC6C}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volatility", "quantower\Volatility\Volatility.csproj", "{F6G7H8I9-J0K1-52L3-M4N5-O6P7Q8R9S0T1}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {DBB674D9-43AA-4383-A4D0-E791847C7145}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DBB674D9-43AA-4383-A4D0-E791847C7145}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DBB674D9-43AA-4383-A4D0-E791847C7145}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DBB674D9-43AA-4383-A4D0-E791847C7145}.Release|Any CPU.Build.0 = Release|Any CPU - {D2F742C0-9E2A-4B07-8D9F-0DF0BA3DDA31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D2F742C0-9E2A-4B07-8D9F-0DF0BA3DDA31}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D2F742C0-9E2A-4B07-8D9F-0DF0BA3DDA31}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D2F742C0-9E2A-4B07-8D9F-0DF0BA3DDA31}.Release|Any CPU.Build.0 = Release|Any CPU - {1777C7BB-F67A-4F00-99BB-EE48178A7129}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1777C7BB-F67A-4F00-99BB-EE48178A7129}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1777C7BB-F67A-4F00-99BB-EE48178A7129}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1777C7BB-F67A-4F00-99BB-EE48178A7129}.Release|Any CPU.Build.0 = Release|Any CPU - {A89D1A5D-9DB9-4E5D-A7D6-E153509430CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A89D1A5D-9DB9-4E5D-A7D6-E153509430CD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A89D1A5D-9DB9-4E5D-A7D6-E153509430CD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A89D1A5D-9DB9-4E5D-A7D6-E153509430CD}.Release|Any CPU.Build.0 = Release|Any CPU - {61AFF1E5-9E2B-47F4-9665-D8358C5BDD6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {61AFF1E5-9E2B-47F4-9665-D8358C5BDD6F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {61AFF1E5-9E2B-47F4-9665-D8358C5BDD6F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {61AFF1E5-9E2B-47F4-9665-D8358C5BDD6F}.Release|Any CPU.Build.0 = Release|Any CPU - {7EBB6ECD-15FA-44DD-B231-FED97E0B710D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7EBB6ECD-15FA-44DD-B231-FED97E0B710D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7EBB6ECD-15FA-44DD-B231-FED97E0B710D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7EBB6ECD-15FA-44DD-B231-FED97E0B710D}.Release|Any CPU.Build.0 = Release|Any CPU - {9190CFDD-9FD8-4E14-9269-79325B03EC6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9190CFDD-9FD8-4E14-9269-79325B03EC6C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9190CFDD-9FD8-4E14-9269-79325B03EC6C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9190CFDD-9FD8-4E14-9269-79325B03EC6C}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {61AFF1E5-9E2B-47F4-9665-D8358C5BDD6F} = {1EC1B338-6AD9-4E3D-82C3-6591CC148DD5} - {7EBB6ECD-15FA-44DD-B231-FED97E0B710D} = {1EC1B338-6AD9-4E3D-82C3-6591CC148DD5} - {9190CFDD-9FD8-4E14-9269-79325B03EC6C} = {1EC1B338-6AD9-4E3D-82C3-6591CC148DD5} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {080D10E4-B741-4A85-B37B-08845A5C78D9} + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A1B2C3D4-E5F6-47G8-H9I0-J1K2L3M4N5O6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1B2C3D4-E5F6-47G8-H9I0-J1K2L3M4N5O6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1B2C3D4-E5F6-47G8-H9I0-J1K2L3M4N5O6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1B2C3D4-E5F6-47G8-H9I0-J1K2L3M4N5O6}.Release|Any CPU.Build.0 = Release|Any CPU + {B2C3D4E5-F6G7-48H9-I0J1-K2L3M4N5O6P7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B2C3D4E5-F6G7-48H9-I0J1-K2L3M4N5O6P7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B2C3D4E5-F6G7-48H9-I0J1-K2L3M4N5O6P7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B2C3D4E5-F6G7-48H9-I0J1-K2L3M4N5O6P7}.Release|Any CPU.Build.0 = Release|Any CPU + {C3D4E5F6-G7H8-49I0-J1K2-L3M4N5O6P7Q8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C3D4E5F6-G7H8-49I0-J1K2-L3M4N5O6P7Q8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C3D4E5F6-G7H8-49I0-J1K2-L3M4N5O6P7Q8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C3D4E5F6-G7H8-49I0-J1K2-L3M4N5O6P7Q8}.Release|Any CPU.Build.0 = Release|Any CPU + {D4E5F6G7-H8I9-50J1-K2L3-M4N5O6P7Q8R9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D4E5F6G7-H8I9-50J1-K2L3-M4N5O6P7Q8R9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D4E5F6G7-H8I9-50J1-K2L3-M4N5O6P7Q8R9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D4E5F6G7-H8I9-50J1-K2L3-M4N5O6P7Q8R9}.Release|Any CPU.Build.0 = Release|Any CPU + {E5F6G7H8-I9J0-51K2-L3M4-N5O6P7Q8R9S0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E5F6G7H8-I9J0-51K2-L3M4-N5O6P7Q8R9S0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E5F6G7H8-I9J0-51K2-L3M4-N5O6P7Q8R9S0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E5F6G7H8-I9J0-51K2-L3M4-N5O6P7Q8R9S0}.Release|Any CPU.Build.0 = Release|Any CPU + {F6G7H8I9-J0K1-52L3-M4N5-O6P7Q8R9S0T1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F6G7H8I9-J0K1-52L3-M4N5-O6P7Q8R9S0T1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F6G7H8I9-J0K1-52L3-M4N5-O6P7Q8R9S0T1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F6G7H8I9-J0K1-52L3-M4N5-O6P7Q8R9S0T1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal