mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-13 16:18:05 +00:00
Add unit tests for various moving average indicators
- Implement tests for HMA (Hull Moving Average) indicator to verify default settings, history depth calculations, and value computations during updates. - Create tests for KAMA (Kaufman Adaptive Moving Average) indicator, ensuring correct defaults, history depth, and value calculations. - Add tests for SMA (Simple Moving Average) indicator, checking default values, history depth, and value computations. - Develop tests for T3 (Tillson T3 Moving Average) indicator, validating defaults, history depth, and value calculations. - Implement tests for TEMA (Triple Exponential Moving Average) indicator, ensuring correct defaults and value computations. - Create tests for TRIMA (Triangular Moving Average) indicator, verifying defaults, history depth, and value calculations. - Add tests for WMA (Weighted Moving Average) indicator, checking default values, history depth, and value computations.
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
# Good Indicator Guidelines
|
||||
|
||||
This document defines the strict standards for creating high-quality technical indicators in the QuanTAlib library. All new indicators MUST adhere to these rules to ensure consistency, performance, and reliability.
|
||||
|
||||
## 1. Architecture & Design Principles
|
||||
|
||||
* **Zero Allocation:** The core calculation loop must not allocate memory on the heap. Use `stackalloc`, `Span<T>`, and pinned memory where possible.
|
||||
* **O(1) Complexity:** Streaming updates must be O(1) whenever mathematically possible. Use running sums/products or circular buffers to avoid re-iterating over history.
|
||||
* **Dual API:** Provide both a stateful object-oriented API (`Update`) and a stateless static vector API (`Calculate`).
|
||||
* **Bar Correction:** Support intra-bar updates via the `isNew` parameter. The indicator must be able to rollback the last update and apply a new value for the same timestamp.
|
||||
* **Robustness:** Handle `NaN` and `Infinity` gracefully using last-valid-value substitution. Never propagate invalid values.
|
||||
* **Reactive:** Implement `ITValuePublisher` to support event-driven architectures.
|
||||
|
||||
## 2. File Structure
|
||||
|
||||
Each indicator resides in its own directory such as `lib/trends/`, `lib/indicators/`, or `lib/oscillators/`.
|
||||
|
||||
**Directory:** `lib/[category]/[name]/`
|
||||
|
||||
| File | Purpose | Naming Convention |
|
||||
|------|---------|-------------------|
|
||||
| **Source** | Main implementation | `[Name].cs` (e.g., `Sma.cs`) |
|
||||
| **Tests** | Unit tests | `[Name].Tests.cs` |
|
||||
| **Validation** | Cross-library validation | `[Name].Validation.Tests.cs` |
|
||||
| **Docs** | User documentation | `[Name].md` |
|
||||
| **Quantower** | Quantower adapter | `[Name].Quantower.cs` |
|
||||
|
||||
## 3. Implementation Rules (`[Name].cs`)
|
||||
|
||||
### Class Definition
|
||||
|
||||
* **Namespace:** `QuanTAlib`
|
||||
* **Attributes:** `[SkipLocalsInit]` for performance.
|
||||
* **Modifiers:** `public sealed class`
|
||||
* **Interface:** Implements `ITValuePublisher`
|
||||
|
||||
### State Management
|
||||
|
||||
* Use `RingBuffer` for sliding window data.
|
||||
* Maintain separate state variables for the *current* calculation (`_sum`, `_lastVal`) and the *previous* valid state (`_p_sum`, `_p_lastVal`) to support `isNew=false` updates.
|
||||
* **Resync:** Implement a periodic full recalculation (e.g., every 1000 ticks) to prevent floating-point drift in running sums.
|
||||
|
||||
### Constructor
|
||||
|
||||
* Validate all parameters (throw `ArgumentException` for invalid values).
|
||||
* Initialize `Name` property (e.g., `$"Sma({period})"`);
|
||||
* Support chaining: `public [Name](ITValuePublisher source, ...)`
|
||||
|
||||
### Update Method
|
||||
|
||||
* **Signature:** `public TValue Update(TValue input, bool isNew = true)`
|
||||
* **Attribute:** `[MethodImpl(MethodImplOptions.AggressiveInlining)]`
|
||||
* **Logic:**
|
||||
1. **Input Validation:** Check `double.IsFinite`. If not, use `_lastValidValue`.
|
||||
2. **State Management:**
|
||||
* If `isNew=true`: Save current state to `_p_*` variables, then update.
|
||||
* If `isNew=false`: Restore state from `_p_*` variables, then update.
|
||||
3. **Calculation:** Perform the math.
|
||||
4. **Publish:** Update `Last` property, invoke `Pub` event, return `Last`.
|
||||
|
||||
### Update Method (TSeries)
|
||||
|
||||
* **Signature:** `public TSeries Update(TSeries source)`
|
||||
* **Placement:** Must be adjacent to the `Update(TValue)` method.
|
||||
* **Logic:**
|
||||
1. Create output series.
|
||||
2. Call static `Calculate(Span)` for performance.
|
||||
3. Restore internal state by replaying the last `Period` bars.
|
||||
|
||||
### Static Calculate (TSeries)
|
||||
|
||||
* Create a new instance of the indicator.
|
||||
* Iterate through the source series.
|
||||
* Return the resulting `TSeries`.
|
||||
|
||||
### Static Calculate (Span) - **Critical for Performance**
|
||||
|
||||
* **Signature:** `public static void Calculate(ReadOnlySpan<double> source, Span<double> output, ...)`
|
||||
* **Attribute:** `[MethodImpl(MethodImplOptions.AggressiveInlining)]`
|
||||
* **Optimization:**
|
||||
|
||||
* Check for SIMD support (`Avx2.IsSupported`).
|
||||
* Use `stackalloc` for small buffers (threshold ~256).
|
||||
* Implement a scalar fallback path that handles `NaN` safely.
|
||||
* Implement a SIMD path for large, clean datasets (optional but recommended for simple averages).
|
||||
|
||||
## 4. Testing Standards
|
||||
|
||||
### Unit Tests (`[Name].Tests.cs`)
|
||||
|
||||
* **Framework:** xUnit
|
||||
* **Coverage:**
|
||||
|
||||
* Constructor validation (invalid params).
|
||||
* Basic calculation correctness (compare against manual calc).
|
||||
* `isNew=true` vs `isNew=false` behavior (bar correction).
|
||||
* `Reset()` functionality.
|
||||
* `IsHot` property behavior.
|
||||
* `NaN` / `Infinity` handling (must not crash, must return finite values).
|
||||
* Consistency between Object API, Static TSeries API, and Static Span API.
|
||||
* Edge cases: Period=1, empty input, single input.
|
||||
|
||||
### Validation Tests (`[Name].Validation.Tests.cs`)
|
||||
|
||||
* **Purpose:** Verify accuracy against established libraries (Skender, TA-Lib, Tulip).
|
||||
* **Data:** Use `GBM` (Geometric Brownian Motion) to generate realistic test data.
|
||||
* **Scenarios:**
|
||||
|
||||
* Batch processing.
|
||||
* Streaming processing.
|
||||
* Span/Vector processing.
|
||||
|
||||
* **Tolerance:** Typically `1e-6` or `1e-9` depending on the algorithm.
|
||||
|
||||
## 5. Documentation Standards (`[Name].md`)
|
||||
|
||||
Follow the standard template and ensure strict adherence to Markdownlint rules, specifically:
|
||||
|
||||
* **MD030:** Ensure exactly one space after list markers (e.g., `* Item`, not `*Item` or `* Item`).
|
||||
* **MD032:** Ensure lists are surrounded by blank lines (one blank line before the first item and one after the last item).
|
||||
|
||||
Template structure:
|
||||
|
||||
1. **Title & Overview:** What is it? What does it do?
|
||||
2. **Core Concepts:** Key features (e.g., equal weighting, noise reduction).
|
||||
3. **Parameters:** Table of constructor parameters.
|
||||
4. **Formula:** LaTeX formatted math ($$...$$).
|
||||
5. **C# Implementation:** Code examples for:
|
||||
* Standard usage.
|
||||
* Span API (high performance).
|
||||
* Bar correction (`isNew`).
|
||||
* Eventing.
|
||||
6. **Interpretation:** How to use it in trading.
|
||||
7. **References:** Books or papers.
|
||||
|
||||
## 6. Performance Guidelines
|
||||
|
||||
* **Inlining:** Use `[MethodImpl(MethodImplOptions.AggressiveInlining)]` on all hot path methods (`Update`, `Calculate`).
|
||||
* **Locals Init:** Use `[SkipLocalsInit]` on the class to skip zero-initialization of locals.
|
||||
* **Loops:** Prefer `for` loops over `foreach` for arrays/spans.
|
||||
* **Math:** Use `System.Math` or `System.Numerics`. Avoid LINQ in hot paths.
|
||||
* **Memory:** **NEVER** use `new` inside the `Update` method. Pre-allocate everything in the constructor.
|
||||
|
||||
## 7. Checklist for New Indicators
|
||||
|
||||
* [ ] **File Structure:** Created all 4 required files?
|
||||
* [ ] **Constructor:** Validates inputs? Sets `Name`?
|
||||
* [ ] **Update:** Handles `isNew` correctly? Handles `NaN`? O(1)?
|
||||
* [ ] **Static API:** Implemented `Calculate(Span)`?
|
||||
* [ ] **Tests:** Unit tests pass? `NaN` tests included?
|
||||
* [ ] **Validation:** Matches external libraries (Skender/TA-Lib)?
|
||||
* [ ] **Docs:** Markdown file created with formula and examples?
|
||||
* [ ] **Quantower:** Adapter created in `[Name].Quantower.cs`?
|
||||
* [ ] **Quantower Tests:** Adapter tests created in `quantower/[category]/[Name]Indicator.Tests.cs`?
|
||||
* [ ] **Index:** Added to category `_index.md` with link and description?
|
||||
* [ ] **Performance:** No allocations in `Update`? `[SkipLocalsInit]` used?
|
||||
@@ -40,7 +40,7 @@ protected override void ManageState(bool isNew)
|
||||
}
|
||||
}
|
||||
```
|
||||
**Pattern**: Use `_p_` prefix for backup variables (e.g., `_p_lastEma`, `_p_isInit`, `_p_e`). When `isNew=false`, restore ALL stateful variables before recalculating. See `lib/averages/Ema.cs` for reference implementation.
|
||||
**Pattern**: Use `_p_` prefix for backup variables (e.g., `_p_lastEma`, `_p_isInit`, `_p_e`). When `isNew=false`, restore ALL stateful variables before recalculating. See `lib/trends/Ema.cs` for reference implementation.
|
||||
|
||||
## Development Workflow
|
||||
|
||||
@@ -79,7 +79,7 @@ dotnet clean QuanTAlib.sln
|
||||
1. **Research**: Get formula/specification. For non-trivial indicators, use Context7 to retrieve authoritative references.
|
||||
|
||||
2. **Location**: Place in appropriate `lib/` subdirectory:
|
||||
- `averages/` - Moving averages (SMA, EMA, JMA, etc.)
|
||||
- `trends/` - Trend indicators (SMA, EMA, JMA, etc.)
|
||||
- `oscillators/` - RSI, Stochastic, CCI, etc.
|
||||
- `momentum/` - MACD, ADX, ROC, etc.
|
||||
- `volatility/` - ATR, Bollinger Bands, volatility measures
|
||||
@@ -229,7 +229,7 @@ public class MyIndicator : Indicator, IWatchlistIndicator
|
||||
```
|
||||
lib/
|
||||
├── core/ # AbstractBase, CircularBuffer, TSeries, TBar, TValue, ITValue
|
||||
├── averages/ # Moving averages: SMA, EMA, DEMA, TEMA, JMA, KAMA, etc. (25+ indicators)
|
||||
├── trends/ # Trend indicators: SMA, EMA, DEMA, TEMA, JMA, KAMA, etc. (25+ indicators)
|
||||
├── oscillators/ # RSI, Stochastic, Williams %R, CCI, Fisher, CTI, etc.
|
||||
├── momentum/ # MACD, ADX, DMI, ROC, TRIX, Vortex, PMO, etc.
|
||||
├── volatility/ # ATR, Bollinger Bands, Keltner Channels, volatility measures
|
||||
|
||||
+5
-4
@@ -397,6 +397,7 @@ FodyWeavers.xsd
|
||||
# JetBrains Rider
|
||||
*.sln.iml
|
||||
.idea/
|
||||
NDependOut/
|
||||
|
||||
# SonarQube/SonarCloud
|
||||
.sonarqube/
|
||||
@@ -413,7 +414,7 @@ ilspy/
|
||||
|
||||
#Ignore insiders AI rules
|
||||
.github/instructions/codacy.instructions.md
|
||||
|
||||
|
||||
#Ignore vscode AI rules
|
||||
.github\instructions\codacy.instructions.md
|
||||
|
||||
|
||||
#Ignore vscode AI rules
|
||||
.github\instructions\codacy.instructions.md
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuanTAlib.Tests", "lib\Quan
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "quantower", "quantower", "{6CF592EE-4302-E72F-3CB4-AB1D314DD5A8}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Averages", "quantower\Averages.csproj", "{D8F03B19-F99F-475F-8951-85C9D2258B73}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Trends", "quantower\Trends.csproj", "{D8F03B19-F99F-475F-8951-85C9D2258B73}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quantower.Tests", "quantower\Quantower.Tests.csproj", "{576835AB-6453-4413-A2E7-54B6725CDF9D}"
|
||||
EndProject
|
||||
|
||||
@@ -122,7 +122,7 @@ TBar bar = csv.Next(isNew: true);
|
||||
Copy DLL files to Quantower installation:
|
||||
|
||||
```
|
||||
<Quantower_root>\Settings\Scripts\Indicators\QuanTAlib\Averages\Averages.dll
|
||||
<Quantower_root>\Settings\Scripts\Indicators\QuanTAlib\Trends\Trends.dll
|
||||
```
|
||||
|
||||
Where `<Quantower_root>` is the directory containing `Start.lnk`.
|
||||
@@ -138,7 +138,7 @@ QuanTAlib/
|
||||
│ │ ├── tbar/ # TBar struct
|
||||
│ │ ├── tbarseries/ # TBarSeries class
|
||||
│ │ └── simd/ # SIMD extensions
|
||||
│ ├── averages/
|
||||
│ ├── trends/
|
||||
│ │ └── ema/ # EMA indicator + tests + docs
|
||||
│ └── feeds/
|
||||
│ ├── csv/ # CSV file feed
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
- [**QuanTAlib**](/)
|
||||
|
||||
- **Trends**
|
||||
- [Overview](trends/)
|
||||
- [ALMA - Arnaud Legoux MA](trends/alma/Alma.md)
|
||||
- [DEMA - Double Exponential MA](trends/dema/Dema.md)
|
||||
- [EMA - Exponential MA](trends/ema/Ema.md)
|
||||
- [HMA - Hull MA](trends/hma/Hma.md)
|
||||
- [SMA - Simple MA](trends/sma/Sma.md)
|
||||
- [T3 - Tillson T3 MA](trends/t3/T3.md)
|
||||
- [TEMA - Triple Exponential MA](trends/tema/Tema.md)
|
||||
- [TRIMA - Triangular MA](trends/trima/Trima.md)
|
||||
- [WMA - Weighted MA](trends/wma/Wma.md)
|
||||
@@ -1,65 +0,0 @@
|
||||
# Averages
|
||||
|
||||
| Indicator | Name |
|
||||
| --------- | ------------------------------ |
|
||||
| ALMA | Arnaud Legoux MA |
|
||||
| BESSEL | Bessel Filter |
|
||||
| BILATERAL | Bilateral Filter |
|
||||
| BLMA | Blackman Window MA |
|
||||
| BPF | Ehlers Bandpass Filter |
|
||||
| BUTTER | Butterworth Filter |
|
||||
| BWMA | Bessel-Weighted MA |
|
||||
| CHEBY1 | Chebyshev Type I Filter |
|
||||
| CHEBY2 | Chebyshev Type II Filter |
|
||||
| CONV | Convolution MA with any kernel |
|
||||
| [DEMA](dema/Dema.cs) | Double Exponential MA |
|
||||
| DSMA | Deviation-Scaled MA |
|
||||
| DWMA | Double Weighted MA |
|
||||
| ELLIPTIC | Elliptic (Cauer) Filter |
|
||||
| [EMA](ema/Ema.cs) | Exponential MA |
|
||||
| EPMA | Endpoint MA |
|
||||
| FRAMA | Fractal Adaptive MA |
|
||||
| GAUSS | Gaussian Filter |
|
||||
| GWMA | Gaussian-Weighted MA |
|
||||
| HAMMA | Hamming Window MA |
|
||||
| HANN | Hann FIR Filter |
|
||||
| HANMA | Hanning Window MA |
|
||||
| HEMA | Hull Exponential MA |
|
||||
| [HMA](hma/Hma.cs) | Hull MA |
|
||||
| HP | Hodrick-Prescott Filter |
|
||||
| HPF | Ehlers Highpass Filter |
|
||||
| HTIT | Hilbert Transform Instantaneous Trend |
|
||||
| HWMA | Holt Weighted MA |
|
||||
| JMA | Jurik MA |
|
||||
| KAMA | Kaufman Adaptive MA |
|
||||
| KF | Kalman Filter |
|
||||
| LOESS | LOESS/LOWESS Smoothing |
|
||||
| LSMA | Least Squares MA |
|
||||
| LTMA | Linear Trend MA |
|
||||
| MAMA | MESA Adaptive MA |
|
||||
| MEDIAN | Median Filter |
|
||||
| MGDI | McGinley Dynamic Indicator |
|
||||
| MMA | Modified MA |
|
||||
| NOTCH | Notch Filter |
|
||||
| PWMA | Pascal Weighted MA |
|
||||
| QEMA | Quadruple Exponential MA |
|
||||
| REMA | Regularized Exponential MA |
|
||||
| RGMA | Recursive Gaussian MA |
|
||||
| RMA | wildeR MA (SMMA, MMA) |
|
||||
| SGF | Savitzky-Golay Filter |
|
||||
| SGMA | Savitzky-Golay MA |
|
||||
| SINEMA | Sine-weighted MA |
|
||||
| [SMA](sma/Sma.cs) | Simple MA |
|
||||
| SSF | Ehlers Super Smooth Filter |
|
||||
| [T3](t3/T3.cs) | Tillson T3 MA |
|
||||
| [TEMA](tema/Tema.cs) | Triple Exponential MA |
|
||||
| [TRIMA](trima/Trima.cs) | Triangular MA |
|
||||
| USF | Ehlers Ultrasmooth Filter |
|
||||
| VAMA | Volatility Adjusted MA |
|
||||
| VIDYA | Variable Index Dynamic Average |
|
||||
| WIENER | Wiener Filter |
|
||||
| [WMA](wma/Wma.cs) | Weighted MA |
|
||||
| YZVAMA | Yang-Zhang Volatility Adjusted MA |
|
||||
| ZLDEMA | Zero-Lag Double Exponential MA |
|
||||
| ZLEMA | Zero-Lag Exponential MA |
|
||||
| ZLTEMA | Zero-Lag Triple Exponential MA |
|
||||
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>QuanTAlib Documentation</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="description" content="Quantitative Technical Analysis Library in C#">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
|
||||
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4/lib/themes/vue.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script>
|
||||
window.$docsify = {
|
||||
name: 'QuanTAlib',
|
||||
repo: 'https://github.com/mihakralj/QuanTAlib',
|
||||
loadSidebar: true,
|
||||
subMaxLevel: 2,
|
||||
auto2top: true
|
||||
}
|
||||
</script>
|
||||
<!-- Docsify v4 -->
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
|
||||
<!-- MathJax -->
|
||||
<script src="//cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify-latex@0"></script>
|
||||
<!-- Prism for C# syntax highlighting -->
|
||||
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-csharp.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,67 @@
|
||||
# Trends
|
||||
|
||||
Trend indicators help identify the direction and strength of a market trend. Moving averages are the most common type of trend indicator, smoothing out price data to create a clearer picture of the underlying direction.
|
||||
|
||||
| Indicator | Full Name | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| [ALMA](trends/alma/Alma.md) | Arnaud Legoux MA | Uses Gaussian distribution weights to balance smoothness and responsiveness. |
|
||||
| BESSEL | Bessel Filter | |
|
||||
| BILATERAL | Bilateral Filter | |
|
||||
| BLMA | Blackman Window MA | |
|
||||
| BPF | Ehlers Bandpass Filter | |
|
||||
| BUTTER | Butterworth Filter | |
|
||||
| BWMA | Bessel-Weighted MA | |
|
||||
| CHEBY1 | Chebyshev Type I Filter | |
|
||||
| CHEBY2 | Chebyshev Type II Filter | |
|
||||
| CONV | Convolution MA with any kernel | |
|
||||
| [DEMA](trends/dema/Dema.md) | Double Exponential Moving Average | Reduces lag by placing more weight on recent data than a standard EMA. |
|
||||
| DSMA | Deviation-Scaled MA | |
|
||||
| DWMA | Double Weighted MA | |
|
||||
| ELLIPTIC | Elliptic (Cauer) Filter | |
|
||||
| [EMA](trends/ema/Ema.md) | Exponential Moving Average | Weighted average giving more importance to recent price data. |
|
||||
| EPMA | Endpoint MA | |
|
||||
| FRAMA | Fractal Adaptive MA | |
|
||||
| GAUSS | Gaussian Filter | |
|
||||
| GWMA | Gaussian-Weighted MA | |
|
||||
| HAMMA | Hamming Window MA | |
|
||||
| HANN | Hann FIR Filter | |
|
||||
| HANMA | Hanning Window MA | |
|
||||
| HEMA | Hull Exponential MA | |
|
||||
| [HMA](trends/hma/Hma.md) | Hull Moving Average | Developed by Alan Hull to reduce lag while improving smoothing. |
|
||||
| HP | Hodrick-Prescott Filter | |
|
||||
| HPF | Ehlers Highpass Filter | |
|
||||
| HTIT | Hilbert Transform Instantaneous Trend | |
|
||||
| HWMA | Holt Weighted MA | |
|
||||
| JMA | Jurik MA | |
|
||||
| [KAMA](trends/kama/Kama.md) | Kaufman Adaptive MA | Adapts to market volatility by adjusting its smoothing factor based on an Efficiency Ratio. |
|
||||
| KF | Kalman Filter | |
|
||||
| LOESS | LOESS/LOWESS Smoothing | |
|
||||
| LSMA | Least Squares MA | |
|
||||
| LTMA | Linear Trend MA | |
|
||||
| MAMA | MESA Adaptive MA | |
|
||||
| MEDIAN | Median Filter | |
|
||||
| MGDI | McGinley Dynamic Indicator | |
|
||||
| MMA | Modified MA | |
|
||||
| NOTCH | Notch Filter | |
|
||||
| PWMA | Pascal Weighted MA | |
|
||||
| QEMA | Quadruple Exponential MA | |
|
||||
| REMA | Regularized Exponential MA | |
|
||||
| RGMA | Recursive Gaussian MA | |
|
||||
| RMA | wildeR MA (SMMA, MMA) | |
|
||||
| SGF | Savitzky-Golay Filter | |
|
||||
| SGMA | Savitzky-Golay MA | |
|
||||
| SINEMA | Sine-weighted MA | |
|
||||
| [SMA](trends/sma/Sma.md) | Simple Moving Average | The unweighted mean of the previous n data. |
|
||||
| SSF | Ehlers Super Smooth Filter | |
|
||||
| [T3](trends/t3/T3.md) | Tillson T3 Moving Average | A smooth moving average that uses a smoothing factor to reduce lag. |
|
||||
| [TEMA](trends/tema/Tema.md) | Triple Exponential Moving Average | Designed to smooth price fluctuations and filter out volatility. |
|
||||
| [TRIMA](trends/trima/Trima.md) | Triangular Moving Average | A double-smoothed SMA that gives more weight to the middle of the data window. |
|
||||
| USF | Ehlers Ultrasmooth Filter | |
|
||||
| VAMA | Volatility Adjusted MA | |
|
||||
| VIDYA | Variable Index Dynamic Average | |
|
||||
| WIENER | Wiener Filter | |
|
||||
| [WMA](trends/wma/Wma.md) | Weighted Moving Average | Assigns a heavier weighting to more current data points since they are more relevant. |
|
||||
| YZVAMA | Yang-Zhang Volatility Adjusted MA | |
|
||||
| ZLDEMA | Zero-Lag Double Exponential MA | |
|
||||
| ZLEMA | Zero-Lag Exponential MA | |
|
||||
| ZLTEMA | Zero-Lag Triple Exponential MA | |
|
||||
@@ -0,0 +1,70 @@
|
||||
using System.Drawing;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class AlmaIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
public int Period { get; set; } = 9;
|
||||
|
||||
[InputParameter("Offset", sortIndex: 2, 0.0, 1.0, 0.01, 2)]
|
||||
public double Offset { get; set; } = 0.85;
|
||||
|
||||
[InputParameter("Sigma", sortIndex: 3, 0.1, 100.0, 0.1, 1)]
|
||||
public double Sigma { get; set; } = 6.0;
|
||||
|
||||
[IndicatorExtensions.DataSourceInput]
|
||||
public SourceType Source { get; set; } = SourceType.Close;
|
||||
|
||||
[InputParameter("Show cold values", sortIndex: 21)]
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Alma? ma;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private int _warmupBarIndex = -1;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"ALMA {Period}:{SourceName}";
|
||||
|
||||
public AlmaIndicator()
|
||||
{
|
||||
OnBackGround = true;
|
||||
SeparateWindow = false;
|
||||
SourceName = Source.ToString();
|
||||
Name = "ALMA - Arnaud Legoux Moving Average";
|
||||
Description = "Arnaud Legoux Moving Average";
|
||||
Series = new(name: $"ALMA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(Series);
|
||||
}
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
ma = new Alma(Period, Offset, Sigma);
|
||||
SourceName = Source.ToString();
|
||||
_warmupBarIndex = -1;
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = ma!.Update(input, isNew);
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent);
|
||||
|
||||
if (_warmupBarIndex < 0 && ma!.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
}
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
int warmupPeriod = _warmupBarIndex > 0 ? _warmupBarIndex : Count;
|
||||
this.PaintSmoothCurve(args, Series!, warmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace QuanTAlib.Tests;
|
||||
|
||||
public class AlmaTests
|
||||
{
|
||||
[Fact]
|
||||
public void Alma_Constructor_ValidatesInput()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => new Alma(0));
|
||||
Assert.Throws<ArgumentException>(() => new Alma(10, sigma: 0));
|
||||
|
||||
var alma = new Alma(10);
|
||||
Assert.NotNull(alma);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alma_Calc_ReturnsValue()
|
||||
{
|
||||
var alma = new Alma(10);
|
||||
TValue result = alma.Update(new TValue(DateTime.UtcNow, 100));
|
||||
Assert.True(result.Value > 0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alma_IsHot_BecomesTrueWhenBufferFull()
|
||||
{
|
||||
var alma = new Alma(5);
|
||||
|
||||
Assert.False(alma.IsHot);
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
alma.Update(new TValue(DateTime.UtcNow, 100));
|
||||
Assert.False(alma.IsHot);
|
||||
}
|
||||
|
||||
alma.Update(new TValue(DateTime.UtcNow, 100));
|
||||
Assert.True(alma.IsHot);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alma_StreamingMatchesBatch()
|
||||
{
|
||||
var almaStreaming = new Alma(10);
|
||||
var almaBatch = new Alma(10);
|
||||
var gbm = new GBM(startPrice: 100.0, mu: 0.05, sigma: 0.2, seed: 42);
|
||||
var series = new TSeries();
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
var bar = gbm.Next(isNew: true);
|
||||
series.Add(bar.Time, bar.Close);
|
||||
}
|
||||
|
||||
// Streaming
|
||||
var streamingResults = new TSeries();
|
||||
foreach (var item in series)
|
||||
{
|
||||
streamingResults.Add(almaStreaming.Update(item));
|
||||
}
|
||||
|
||||
// Batch
|
||||
var batchResults = almaBatch.Update(series);
|
||||
|
||||
Assert.Equal(streamingResults.Count, batchResults.Count);
|
||||
for (int i = 0; i < streamingResults.Count; i++)
|
||||
{
|
||||
Assert.Equal(streamingResults[i].Value, batchResults[i].Value, 1e-9);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alma_StaticCalculate_MatchesInstance()
|
||||
{
|
||||
var series = new TSeries();
|
||||
var gbm = new GBM(startPrice: 100.0, mu: 0.05, sigma: 0.2, seed: 42);
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
var bar = gbm.Next(isNew: true);
|
||||
series.Add(bar.Time, bar.Close);
|
||||
}
|
||||
|
||||
var instanceResults = new Alma(10).Update(series);
|
||||
var staticResults = Alma.Calculate(series, 10);
|
||||
|
||||
for (int i = 0; i < instanceResults.Count; i++)
|
||||
{
|
||||
Assert.Equal(instanceResults[i].Value, staticResults[i].Value, 1e-9);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alma_SpanCalculate_MatchesSeries()
|
||||
{
|
||||
var series = new TSeries();
|
||||
var gbm = new GBM(startPrice: 100.0, mu: 0.05, sigma: 0.2, seed: 42);
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
var bar = gbm.Next(isNew: true);
|
||||
series.Add(bar.Time, bar.Close);
|
||||
}
|
||||
|
||||
var seriesResults = Alma.Calculate(series, 10);
|
||||
|
||||
double[] input = series.Values.ToArray();
|
||||
double[] output = new double[input.Length];
|
||||
|
||||
Alma.Calculate(input.AsSpan(), output.AsSpan(), 10);
|
||||
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
Assert.Equal(seriesResults[i].Value, output[i], 1e-9);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alma_Update_IsNewFalse_CorrectsValue()
|
||||
{
|
||||
var alma = new Alma(10);
|
||||
var gbm = new GBM(startPrice: 100.0, mu: 0.05, sigma: 0.2, seed: 42);
|
||||
|
||||
// Feed initial data
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
var bar = gbm.Next(isNew: true);
|
||||
alma.Update(new TValue(bar.Time, bar.Close), isNew: true);
|
||||
}
|
||||
|
||||
// Update with isNew=false (correction)
|
||||
var newBar = gbm.Next(isNew: true);
|
||||
alma.Update(new TValue(newBar.Time, newBar.Close), isNew: true);
|
||||
|
||||
double valueAfterCommit = alma.Last.Value;
|
||||
|
||||
// Now update the SAME bar with a different value
|
||||
alma.Update(new TValue(newBar.Time, newBar.Close + 10.0), isNew: false);
|
||||
|
||||
double valueAfterCorrection = alma.Last.Value;
|
||||
|
||||
Assert.NotEqual(valueAfterCommit, valueAfterCorrection);
|
||||
|
||||
// Now restore original value
|
||||
alma.Update(new TValue(newBar.Time, newBar.Close), isNew: false);
|
||||
|
||||
Assert.Equal(valueAfterCommit, alma.Last.Value, 1e-9);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alma_NaN_Input_UsesLastValidValue()
|
||||
{
|
||||
var alma = new Alma(5);
|
||||
|
||||
alma.Update(new TValue(DateTime.UtcNow, 100));
|
||||
alma.Update(new TValue(DateTime.UtcNow, 110));
|
||||
|
||||
var resultAfterNaN = alma.Update(new TValue(DateTime.UtcNow, double.NaN));
|
||||
|
||||
Assert.True(double.IsFinite(resultAfterNaN.Value));
|
||||
Assert.NotEqual(0, resultAfterNaN.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alma_Reset_ClearsState()
|
||||
{
|
||||
var alma = new Alma(10);
|
||||
alma.Update(new TValue(DateTime.UtcNow, 100));
|
||||
alma.Update(new TValue(DateTime.UtcNow, 110));
|
||||
|
||||
Assert.True(alma.Last.Value > 0);
|
||||
|
||||
alma.Reset();
|
||||
|
||||
Assert.Equal(0, alma.Last.Value);
|
||||
Assert.False(alma.IsHot);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Skender.Stock.Indicators;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace QuanTAlib.Tests;
|
||||
|
||||
public class AlmaValidationTests
|
||||
{
|
||||
// Note: ALMA is not available in TA-Lib or Tulip, so validation is limited to Skender.Stock.Indicators.
|
||||
|
||||
private readonly TBarSeries _bars;
|
||||
private readonly TSeries _data;
|
||||
private readonly List<Quote> _skenderQuotes;
|
||||
private readonly ITestOutputHelper _output;
|
||||
|
||||
public AlmaValidationTests(ITestOutputHelper output)
|
||||
{
|
||||
_output = output;
|
||||
|
||||
// 1. Generate 1000 records using GBM feed
|
||||
var gbm = new GBM(startPrice: 100.0, mu: 0.05, sigma: 0.2, seed: 42);
|
||||
_bars = gbm.Fetch(1000, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
|
||||
// 2. Extract Close TSeries
|
||||
_data = _bars.Close;
|
||||
|
||||
// 3. Prepare data for Skender (List<Quote>)
|
||||
_skenderQuotes = new List<Quote>();
|
||||
for (int i = 0; i < _bars.Count; i++)
|
||||
{
|
||||
_skenderQuotes.Add(new Quote
|
||||
{
|
||||
Date = new DateTime(_bars.Open.Times[i], DateTimeKind.Utc),
|
||||
Open = (decimal)_bars.Open[i].Value,
|
||||
High = (decimal)_bars.High[i].Value,
|
||||
Low = (decimal)_bars.Low[i].Value,
|
||||
Close = (decimal)_bars.Close[i].Value,
|
||||
Volume = (decimal)_bars.Volume[i].Value
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_Skender_Batch()
|
||||
{
|
||||
int[] periods = { 9, 14, 20, 50 };
|
||||
double offset = 0.85;
|
||||
double sigma = 6.0;
|
||||
|
||||
foreach (var period in periods)
|
||||
{
|
||||
// Calculate QuanTAlib ALMA (batch TSeries)
|
||||
var alma = new global::QuanTAlib.Alma(period, offset, sigma);
|
||||
var qResult = alma.Update(_data);
|
||||
|
||||
// Calculate Skender ALMA
|
||||
var sResult = _skenderQuotes.GetAlma(period, offset, sigma).ToList();
|
||||
|
||||
// Compare last 100 records
|
||||
VerifyData_Skender(qResult, sResult);
|
||||
}
|
||||
_output.WriteLine("ALMA Batch(TSeries) validated successfully against Skender");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_Skender_Streaming()
|
||||
{
|
||||
int[] periods = { 9, 14, 20, 50 };
|
||||
double offset = 0.85;
|
||||
double sigma = 6.0;
|
||||
|
||||
foreach (var period in periods)
|
||||
{
|
||||
// Calculate QuanTAlib ALMA (streaming)
|
||||
var alma = new global::QuanTAlib.Alma(period, offset, sigma);
|
||||
var qResults = new List<double>();
|
||||
foreach (var item in _data)
|
||||
{
|
||||
qResults.Add(alma.Update(item).Value);
|
||||
}
|
||||
|
||||
// Calculate Skender ALMA
|
||||
var sResult = _skenderQuotes.GetAlma(period, offset, sigma).ToList();
|
||||
|
||||
// Compare last 100 records
|
||||
VerifyData_Skender_Streaming(qResults, sResult);
|
||||
}
|
||||
_output.WriteLine("ALMA Streaming validated successfully against Skender");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_Skender_Span()
|
||||
{
|
||||
int[] periods = { 9, 14, 20, 50 };
|
||||
double offset = 0.85;
|
||||
double sigma = 6.0;
|
||||
|
||||
// Prepare data for Span API
|
||||
double[] sourceData = _data.Select(x => x.Value).ToArray();
|
||||
|
||||
foreach (var period in periods)
|
||||
{
|
||||
// Calculate QuanTAlib ALMA (Span API)
|
||||
double[] qOutput = new double[sourceData.Length];
|
||||
global::QuanTAlib.Alma.Calculate(sourceData.AsSpan(), qOutput.AsSpan(), period, offset, sigma);
|
||||
|
||||
// Calculate Skender ALMA
|
||||
var sResult = _skenderQuotes.GetAlma(period, offset, sigma).ToList();
|
||||
|
||||
// Compare last 100 records
|
||||
VerifyData_Skender_Span(qOutput, sResult);
|
||||
}
|
||||
_output.WriteLine("ALMA Span validated successfully against Skender");
|
||||
}
|
||||
|
||||
private static void VerifyData_Skender(TSeries qSeries, List<AlmaResult> sSeries)
|
||||
{
|
||||
Assert.Equal(qSeries.Count, sSeries.Count);
|
||||
|
||||
int count = qSeries.Count;
|
||||
int skip = count - 100;
|
||||
|
||||
for (int i = skip; i < count; i++)
|
||||
{
|
||||
double qValue = qSeries[i].Value;
|
||||
double? sValue = sSeries[i].Alma;
|
||||
|
||||
if (!sValue.HasValue) continue;
|
||||
|
||||
Assert.Equal(sValue.Value, qValue, 1e-6);
|
||||
}
|
||||
}
|
||||
|
||||
private static void VerifyData_Skender_Streaming(List<double> qResults, List<AlmaResult> sSeries)
|
||||
{
|
||||
Assert.Equal(qResults.Count, sSeries.Count);
|
||||
|
||||
int count = qResults.Count;
|
||||
int skip = count - 100;
|
||||
|
||||
for (int i = skip; i < count; i++)
|
||||
{
|
||||
double qValue = qResults[i];
|
||||
double? sValue = sSeries[i].Alma;
|
||||
|
||||
if (!sValue.HasValue) continue;
|
||||
|
||||
Assert.Equal(sValue.Value, qValue, 1e-6);
|
||||
}
|
||||
}
|
||||
|
||||
private static void VerifyData_Skender_Span(double[] qOutput, List<AlmaResult> sSeries)
|
||||
{
|
||||
Assert.Equal(qOutput.Length, sSeries.Count);
|
||||
|
||||
int count = qOutput.Length;
|
||||
int skip = count - 100;
|
||||
|
||||
for (int i = skip; i < count; i++)
|
||||
{
|
||||
double qValue = qOutput[i];
|
||||
double? sValue = sSeries[i].Alma;
|
||||
|
||||
if (!sValue.HasValue) continue;
|
||||
|
||||
Assert.Equal(sValue.Value, qValue, 1e-6);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
/// <summary>
|
||||
/// ALMA: Arnaud Legoux Moving Average
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// ALMA uses a Gaussian distribution to determine weights for the moving average.
|
||||
/// It allows for adjusting smoothness and responsiveness via Offset and Sigma parameters.
|
||||
///
|
||||
/// Formula:
|
||||
/// Weights are calculated using the Gaussian function:
|
||||
/// W_i = exp( - (i - offset)^2 / (2 * sigma^2) )
|
||||
/// where:
|
||||
/// offset = floor(period * offset_param)
|
||||
/// sigma = period / sigma_param
|
||||
///
|
||||
/// The final ALMA is the weighted sum of the price window divided by the sum of weights.
|
||||
/// </remarks>
|
||||
[SkipLocalsInit]
|
||||
public sealed class Alma : ITValuePublisher
|
||||
{
|
||||
private readonly int _period;
|
||||
private readonly double[] _weights;
|
||||
private readonly double _weightSum;
|
||||
private readonly RingBuffer _buffer;
|
||||
private double _lastValidValue;
|
||||
|
||||
/// <summary>
|
||||
/// Display name for the indicator.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
public event Action<TValue>? Pub;
|
||||
|
||||
/// <summary>
|
||||
/// Current ALMA value.
|
||||
/// </summary>
|
||||
public TValue Last { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the ALMA has enough data to produce valid results (buffer is full).
|
||||
/// </summary>
|
||||
public bool IsHot => _buffer.IsFull;
|
||||
|
||||
/// <summary>
|
||||
/// Creates ALMA with specified parameters.
|
||||
/// </summary>
|
||||
/// <param name="period">Window size (must be > 0)</param>
|
||||
/// <param name="offset">Gaussian offset (0-1, default 0.85). Closer to 1 makes it more responsive.</param>
|
||||
/// <param name="sigma">Standard deviation (default 6). Higher values make it sharper.</param>
|
||||
public Alma(int period, double offset = 0.85, double sigma = 6.0)
|
||||
{
|
||||
if (period <= 0)
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
if (sigma <= 0)
|
||||
throw new ArgumentException("Sigma must be greater than 0", nameof(sigma));
|
||||
|
||||
_period = period;
|
||||
_buffer = new RingBuffer(period);
|
||||
_weights = new double[period];
|
||||
Name = $"Alma({period}, {offset:F2}, {sigma:F2})";
|
||||
|
||||
// Precompute weights
|
||||
double m = offset * (period - 1);
|
||||
double s = period / sigma;
|
||||
double s2 = 2 * s * s;
|
||||
double sum = 0;
|
||||
|
||||
for (int i = 0; i < period; i++)
|
||||
{
|
||||
double v = i - m;
|
||||
_weights[i] = Math.Exp(-(v * v) / s2);
|
||||
sum += _weights[i];
|
||||
}
|
||||
|
||||
_weightSum = sum;
|
||||
}
|
||||
|
||||
public Alma(ITValuePublisher source, int period, double offset = 0.85, double sigma = 6.0)
|
||||
: this(period, offset, sigma)
|
||||
{
|
||||
source.Pub += (item) => Update(item);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private double GetValidValue(double input)
|
||||
{
|
||||
if (double.IsFinite(input))
|
||||
{
|
||||
_lastValidValue = input;
|
||||
return input;
|
||||
}
|
||||
return _lastValidValue;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TValue Update(TValue input, bool isNew = true)
|
||||
{
|
||||
double val = GetValidValue(input.Value);
|
||||
_buffer.Add(val, isNew);
|
||||
|
||||
double result = 0;
|
||||
if (_buffer.Count > 0)
|
||||
{
|
||||
result = CalculateWeightedSum();
|
||||
}
|
||||
|
||||
Last = new TValue(input.Time, result);
|
||||
Pub?.Invoke(Last);
|
||||
return Last;
|
||||
}
|
||||
|
||||
public TSeries Update(TSeries source)
|
||||
{
|
||||
if (source.Count == 0) return new TSeries(new List<long>(), new List<double>());
|
||||
|
||||
int len = source.Count;
|
||||
var t = new List<long>(len);
|
||||
var v = new List<double>(len);
|
||||
CollectionsMarshal.SetCount(t, len);
|
||||
CollectionsMarshal.SetCount(v, len);
|
||||
|
||||
var tSpan = CollectionsMarshal.AsSpan(t);
|
||||
var vSpan = CollectionsMarshal.AsSpan(v);
|
||||
|
||||
Calculate(source.Values, vSpan, _period);
|
||||
source.Times.CopyTo(tSpan);
|
||||
|
||||
// Restore state
|
||||
_buffer.Clear();
|
||||
_lastValidValue = 0;
|
||||
|
||||
// Replay last part to restore buffer state
|
||||
int startIndex = Math.Max(0, len - _period);
|
||||
for (int i = startIndex; i < len; i++)
|
||||
{
|
||||
Update(source[i]);
|
||||
}
|
||||
|
||||
return new TSeries(t, v);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private double CalculateWeightedSum()
|
||||
{
|
||||
// If buffer is not full, we only use the most recent 'count' weights?
|
||||
// Standard ALMA usually waits for full period, or re-normalizes weights.
|
||||
// Here we'll re-normalize based on how many items we have.
|
||||
// But to match standard behavior, we usually just run on what we have.
|
||||
// However, the weights are designed for a specific period.
|
||||
// Using a partial window with full-period weights might be weird.
|
||||
// Let's stick to the standard: use the weights corresponding to the filled positions.
|
||||
// Since RingBuffer adds new items at 'head', and we want to apply weights
|
||||
// such that weights[period-1] applies to the newest item, etc.
|
||||
|
||||
// RingBuffer: [Oldest ... Newest]
|
||||
// Weights: [0 ... period-1]
|
||||
// We want: Sum(Buffer[i] * Weights[i]) / Sum(Weights)
|
||||
|
||||
// BUT: If buffer is not full, say count=5, period=10.
|
||||
// We have 5 items. Should we use weights[0..4] or weights[5..9]?
|
||||
// Usually, moving averages grow.
|
||||
// Let's assume we use the last 'count' weights, normalized.
|
||||
|
||||
ReadOnlySpan<double> bufferSpan = _buffer.GetSpan();
|
||||
int count = bufferSpan.Length;
|
||||
|
||||
// If not full, we need to handle it carefully.
|
||||
// For simplicity and performance, let's just iterate.
|
||||
// Optimization: If full, use SIMD.
|
||||
|
||||
if (count < _period)
|
||||
{
|
||||
double sum = 0;
|
||||
double wSum = 0;
|
||||
// Map weights to buffer:
|
||||
// Buffer[0] (oldest) -> Weights[period - count] ??
|
||||
// Actually, standard is: Weights are fixed.
|
||||
// Let's align newest with newest.
|
||||
// Buffer[count-1] (newest) <-> Weights[period-1]
|
||||
// Buffer[0] (oldest) <-> Weights[period-count]
|
||||
|
||||
int weightOffset = _period - count;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
double w = _weights[weightOffset + i];
|
||||
sum += bufferSpan[i] * w;
|
||||
wSum += w;
|
||||
}
|
||||
return wSum > 0 ? sum / wSum : 0;
|
||||
}
|
||||
|
||||
// Full buffer
|
||||
return CalculateWeightedSumSimd(bufferSpan);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private double CalculateWeightedSumSimd(ReadOnlySpan<double> buffer)
|
||||
{
|
||||
double sum = 0;
|
||||
int i = 0;
|
||||
int len = _period;
|
||||
|
||||
if (Avx2.IsSupported && len >= Vector256<double>.Count)
|
||||
{
|
||||
var vSum = Vector256<double>.Zero;
|
||||
ref double bufRef = ref MemoryMarshal.GetReference(buffer);
|
||||
ref double wRef = ref MemoryMarshal.GetReference(_weights.AsSpan());
|
||||
|
||||
for (; i <= len - Vector256<double>.Count; i += Vector256<double>.Count)
|
||||
{
|
||||
var vBuf = Vector256.LoadUnsafe(ref Unsafe.Add(ref bufRef, i));
|
||||
var vW = Vector256.LoadUnsafe(ref Unsafe.Add(ref wRef, i));
|
||||
vSum = Avx.Add(vSum, Avx.Multiply(vBuf, vW));
|
||||
}
|
||||
|
||||
// Horizontal sum
|
||||
vSum = Avx.Add(vSum, Avx2.Permute4x64(vSum.AsUInt64(), 0b_01_00_11_10).AsDouble());
|
||||
vSum = Avx.Add(vSum, Avx2.Permute4x64(vSum.AsUInt64(), 0b_00_00_00_01).AsDouble());
|
||||
sum = vSum.GetElement(0);
|
||||
}
|
||||
|
||||
// Scalar fallback
|
||||
for (; i < len; i++)
|
||||
{
|
||||
sum += buffer[i] * _weights[i];
|
||||
}
|
||||
|
||||
return sum / _weightSum;
|
||||
}
|
||||
|
||||
public static TSeries Calculate(TSeries source, int period, double offset = 0.85, double sigma = 6.0)
|
||||
{
|
||||
var alma = new Alma(period, offset, sigma);
|
||||
return alma.Update(source);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period, double offset = 0.85, double sigma = 6.0)
|
||||
{
|
||||
if (period <= 0)
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
if (source.Length != output.Length)
|
||||
throw new ArgumentException("Source and output must have the same length");
|
||||
|
||||
// Precompute weights
|
||||
double[] weights = new double[period];
|
||||
double m = offset * (period - 1);
|
||||
double s = period / sigma;
|
||||
double s2 = 2 * s * s;
|
||||
double weightSum = 0;
|
||||
|
||||
for (int i = 0; i < period; i++)
|
||||
{
|
||||
double v = i - m;
|
||||
weights[i] = Math.Exp(-(v * v) / s2);
|
||||
weightSum += weights[i];
|
||||
}
|
||||
|
||||
// Buffer for sliding window
|
||||
// Use stackalloc for small periods
|
||||
Span<double> buffer = period <= 256 ? stackalloc double[period] : new double[period];
|
||||
int bufferIdx = 0;
|
||||
int count = 0;
|
||||
double lastValid = 0;
|
||||
|
||||
for (int i = 0; i < source.Length; i++)
|
||||
{
|
||||
double val = source[i];
|
||||
if (double.IsFinite(val))
|
||||
lastValid = val;
|
||||
else
|
||||
val = lastValid;
|
||||
|
||||
// Add to circular buffer
|
||||
buffer[bufferIdx] = val;
|
||||
bufferIdx = (bufferIdx + 1) % period;
|
||||
if (count < period) count++;
|
||||
|
||||
// Calculate weighted sum
|
||||
// We need to iterate buffer from oldest to newest to match weights[0..period-1]
|
||||
// Oldest is at: (bufferIdx - count + period) % period
|
||||
// But wait, the buffer wraps.
|
||||
// Let's just iterate 0..count-1 and map to buffer index.
|
||||
|
||||
double sum = 0;
|
||||
double currentWeightSum = 0;
|
||||
|
||||
int startIdx = (bufferIdx - count + period) % period;
|
||||
int weightOffset = period - count; // Align weights to end
|
||||
|
||||
// Optimization: If full, we can use SIMD if we unwrap the buffer or handle wrapping.
|
||||
// For simplicity in static method (and since we can't easily unwrap stackalloc),
|
||||
// we'll use scalar loop with modulo.
|
||||
// Or better: copy to a temporary linear buffer? No, that's too much copying.
|
||||
|
||||
// Actually, for full period, we can do two loops (part1, part2) to avoid modulo in loop.
|
||||
|
||||
if (count == period)
|
||||
{
|
||||
// Buffer is full. startIdx is bufferIdx (which is the oldest, since we just wrote to bufferIdx-1)
|
||||
// Wait, bufferIdx points to the NEXT write position.
|
||||
// So bufferIdx is the Oldest.
|
||||
|
||||
// Part 1: bufferIdx to End
|
||||
int part1Len = period - bufferIdx;
|
||||
for (int j = 0; j < part1Len; j++)
|
||||
{
|
||||
sum += buffer[bufferIdx + j] * weights[j];
|
||||
}
|
||||
|
||||
// Part 2: 0 to bufferIdx
|
||||
for (int j = 0; j < bufferIdx; j++)
|
||||
{
|
||||
sum += buffer[j] * weights[part1Len + j];
|
||||
}
|
||||
|
||||
output[i] = sum / weightSum;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Partial buffer
|
||||
for (int j = 0; j < count; j++)
|
||||
{
|
||||
int idx = (startIdx + j) % period;
|
||||
double w = weights[weightOffset + j];
|
||||
sum += buffer[idx] * w;
|
||||
currentWeightSum += w;
|
||||
}
|
||||
output[i] = currentWeightSum > 0 ? sum / currentWeightSum : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_buffer.Clear();
|
||||
_lastValidValue = 0;
|
||||
Last = default;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
# ALMA: Arnaud Legoux Moving Average
|
||||
|
||||
## Overview and Purpose
|
||||
|
||||
The Arnaud Legoux Moving Average (ALMA) is a technical indicator that attempts to bridge the gap between responsiveness and smoothness. It uses a Gaussian distribution to determine the weights of the moving average, allowing the user to shift the peak of the weight distribution (offset) and control the width of the distribution (sigma).
|
||||
|
||||
ALMA is designed to reduce lag while maintaining smoothness, making it superior to traditional moving averages like SMA or EMA in many trend-following applications.
|
||||
|
||||
## Core Concepts
|
||||
|
||||
* **Gaussian Weighting:** Weights are distributed according to a bell curve.
|
||||
* **Offset Control:** Allows shifting the focus of the average. An offset of 0.5 is a symmetric filter (like SMA/WMA), while an offset closer to 1.0 makes it more responsive to recent prices.
|
||||
* **Sigma Control:** Controls the "sharpness" of the filter. Higher sigma values include more data points in the calculation, making it smoother but potentially introducing more lag.
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Default | Description |
|
||||
|-----------|---------|-------------|
|
||||
| Period | 9 | The window size for the moving average. |
|
||||
| Offset | 0.85 | The center of the Gaussian distribution (0.0 to 1.0). |
|
||||
| Sigma | 6.0 | The standard deviation of the Gaussian distribution. |
|
||||
|
||||
## Formula
|
||||
|
||||
The weight for the $i$-th element in the window (where $i=0$ is the oldest) is calculated as:
|
||||
|
||||
$$W_i = \exp\left(-\frac{(i - \text{offset\_idx})^2}{2\sigma_{idx}^2}\right)$$
|
||||
|
||||
Where:
|
||||
|
||||
* $\text{offset\_idx} = \lfloor \text{Period} \times \text{Offset} \rfloor$
|
||||
* $\sigma_{idx} = \text{Period} / \text{Sigma}$
|
||||
|
||||
The ALMA value is the weighted sum:
|
||||
|
||||
$$ALMA = \frac{\sum_{i=0}^{n-1} P_i \times W_i}{\sum_{i=0}^{n-1} W_i}$$
|
||||
|
||||
## C# Implementation
|
||||
|
||||
### Standard Usage
|
||||
|
||||
```csharp
|
||||
using QuanTAlib;
|
||||
|
||||
// Initialize with period 9, offset 0.85, sigma 6
|
||||
var alma = new Alma(9, offset: 0.85, sigma: 6.0);
|
||||
|
||||
// Update with new value
|
||||
TValue result = alma.Update(new TValue(time, price));
|
||||
Console.WriteLine($"ALMA: {result.Value}");
|
||||
```
|
||||
|
||||
### Zero-Allocation Span API
|
||||
|
||||
```csharp
|
||||
double[] prices = ...;
|
||||
double[] output = new double[prices.Length];
|
||||
|
||||
// Calculate ALMA for the entire array
|
||||
Alma.Calculate(prices.AsSpan(), output.AsSpan(), period: 9, offset: 0.85, sigma: 6.0);
|
||||
```
|
||||
|
||||
### Bar Correction
|
||||
|
||||
```csharp
|
||||
var alma = new Alma(9);
|
||||
|
||||
// Update with initial tick
|
||||
alma.Update(new TValue(time, 100), isNew: true);
|
||||
|
||||
// Update with correction (same bar)
|
||||
alma.Update(new TValue(time, 101), isNew: false);
|
||||
```
|
||||
|
||||
## Interpretation
|
||||
|
||||
* **Trend Following:** Like other moving averages, ALMA helps identify the trend direction.
|
||||
* **Crossovers:** Price crossing ALMA or two ALMAs crossing each other can signal trend changes.
|
||||
* **Support/Resistance:** ALMA often acts as dynamic support/resistance.
|
||||
|
||||
## References
|
||||
|
||||
* Arnaud Legoux and Dimitris Kouzis-Loukas (2009).
|
||||
@@ -23,7 +23,7 @@ public class DemaIndicator : Indicator, IWatchlistIndicator
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"DEMA {Period}:{SourceName}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/averages/dema/Dema.Quantower.cs";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/trends/dema/Dema.Quantower.cs";
|
||||
|
||||
public DemaIndicator()
|
||||
{
|
||||
@@ -23,7 +23,7 @@ public class HmaIndicator : Indicator, IWatchlistIndicator
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"HMA {Period}:{SourceName}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/averages/hma/Hma.cs";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/trends/hma/Hma.cs";
|
||||
|
||||
public HmaIndicator()
|
||||
{
|
||||
@@ -0,0 +1,70 @@
|
||||
using System.Drawing;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class KamaIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
public int Period { get; set; } = 10;
|
||||
|
||||
[InputParameter("Fast Period", sortIndex: 2, 1, 1000, 1, 0)]
|
||||
public int FastPeriod { get; set; } = 2;
|
||||
|
||||
[InputParameter("Slow Period", sortIndex: 3, 1, 1000, 1, 0)]
|
||||
public int SlowPeriod { get; set; } = 30;
|
||||
|
||||
[IndicatorExtensions.DataSourceInput]
|
||||
public SourceType Source { get; set; } = SourceType.Close;
|
||||
|
||||
[InputParameter("Show cold values", sortIndex: 21)]
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Kama? ma;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private int _warmupBarIndex = -1;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"KAMA {Period}:{SourceName}";
|
||||
|
||||
public KamaIndicator()
|
||||
{
|
||||
OnBackGround = true;
|
||||
SeparateWindow = false;
|
||||
SourceName = Source.ToString();
|
||||
Name = "KAMA - Kaufman Adaptive Moving Average";
|
||||
Description = "Kaufman Adaptive Moving Average";
|
||||
Series = new(name: $"KAMA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(Series);
|
||||
}
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
ma = new Kama(Period, FastPeriod, SlowPeriod);
|
||||
SourceName = Source.ToString();
|
||||
_warmupBarIndex = -1;
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = ma!.Update(input, isNew);
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent);
|
||||
|
||||
if (_warmupBarIndex < 0 && ma!.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
}
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
int warmupPeriod = _warmupBarIndex > 0 ? _warmupBarIndex : Count;
|
||||
this.PaintSmoothCurve(args, Series!, warmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace QuanTAlib.Tests;
|
||||
|
||||
public class KamaTests
|
||||
{
|
||||
[Fact]
|
||||
public void Kama_Constructor_ValidatesInput()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => new Kama(0));
|
||||
Assert.Throws<ArgumentException>(() => new Kama(10, fastPeriod: 0));
|
||||
Assert.Throws<ArgumentException>(() => new Kama(10, slowPeriod: 0));
|
||||
Assert.Throws<ArgumentException>(() => new Kama(10, fastPeriod: 10, slowPeriod: 5));
|
||||
|
||||
var kama = new Kama(10);
|
||||
Assert.NotNull(kama);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Kama_Calc_ReturnsValue()
|
||||
{
|
||||
var kama = new Kama(10);
|
||||
TValue result = kama.Update(new TValue(DateTime.UtcNow, 100));
|
||||
Assert.True(result.Value > 0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Kama_IsHot_BecomesTrueWhenBufferFull()
|
||||
{
|
||||
// Buffer size is period + 1
|
||||
var kama = new Kama(5);
|
||||
|
||||
Assert.False(kama.IsHot);
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
kama.Update(new TValue(DateTime.UtcNow, 100));
|
||||
Assert.False(kama.IsHot);
|
||||
}
|
||||
|
||||
kama.Update(new TValue(DateTime.UtcNow, 100));
|
||||
Assert.True(kama.IsHot);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Kama_StreamingMatchesBatch()
|
||||
{
|
||||
var kamaStreaming = new Kama(10);
|
||||
var kamaBatch = new Kama(10);
|
||||
var gbm = new GBM(startPrice: 100.0, mu: 0.05, sigma: 0.2, seed: 42);
|
||||
var series = new TSeries();
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
var bar = gbm.Next(isNew: true);
|
||||
series.Add(bar.Time, bar.Close);
|
||||
}
|
||||
|
||||
// Streaming
|
||||
var streamingResults = new TSeries();
|
||||
foreach (var item in series)
|
||||
{
|
||||
streamingResults.Add(kamaStreaming.Update(item));
|
||||
}
|
||||
|
||||
// Batch
|
||||
var batchResults = kamaBatch.Update(series);
|
||||
|
||||
Assert.Equal(streamingResults.Count, batchResults.Count);
|
||||
for (int i = 0; i < streamingResults.Count; i++)
|
||||
{
|
||||
Assert.Equal(streamingResults[i].Value, batchResults[i].Value, 1e-9);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Kama_StaticCalculate_MatchesInstance()
|
||||
{
|
||||
var series = new TSeries();
|
||||
var gbm = new GBM(startPrice: 100.0, mu: 0.05, sigma: 0.2, seed: 42);
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
var bar = gbm.Next(isNew: true);
|
||||
series.Add(bar.Time, bar.Close);
|
||||
}
|
||||
|
||||
var instanceResults = new Kama(10).Update(series);
|
||||
var staticResults = new double[series.Count];
|
||||
Kama.Calculate(series.Values.ToArray().AsSpan(), staticResults.AsSpan(), 10);
|
||||
|
||||
for (int i = 0; i < instanceResults.Count; i++)
|
||||
{
|
||||
Assert.Equal(instanceResults[i].Value, staticResults[i], 1e-9);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Kama_Update_IsNewFalse_CorrectsValue()
|
||||
{
|
||||
var kama = new Kama(10);
|
||||
var gbm = new GBM(startPrice: 100.0, mu: 0.05, sigma: 0.2, seed: 42);
|
||||
|
||||
// Feed initial data
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
var bar = gbm.Next(isNew: true);
|
||||
kama.Update(new TValue(bar.Time, bar.Close), isNew: true);
|
||||
}
|
||||
|
||||
// Update with isNew=false (correction)
|
||||
var newBar = gbm.Next(isNew: true);
|
||||
kama.Update(new TValue(newBar.Time, newBar.Close), isNew: true);
|
||||
|
||||
double valueAfterCommit = kama.Last.Value;
|
||||
|
||||
// Now update the SAME bar with a different value
|
||||
kama.Update(new TValue(newBar.Time, newBar.Close + 10.0), isNew: false);
|
||||
|
||||
double valueAfterCorrection = kama.Last.Value;
|
||||
|
||||
Assert.NotEqual(valueAfterCommit, valueAfterCorrection);
|
||||
|
||||
// Now restore original value
|
||||
kama.Update(new TValue(newBar.Time, newBar.Close), isNew: false);
|
||||
|
||||
Assert.Equal(valueAfterCommit, kama.Last.Value, 1e-9);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Kama_NaN_Input_UsesLastValidValue()
|
||||
{
|
||||
var kama = new Kama(5);
|
||||
|
||||
kama.Update(new TValue(DateTime.UtcNow, 100));
|
||||
kama.Update(new TValue(DateTime.UtcNow, 110));
|
||||
|
||||
var resultAfterNaN = kama.Update(new TValue(DateTime.UtcNow, double.NaN));
|
||||
|
||||
Assert.True(double.IsFinite(resultAfterNaN.Value));
|
||||
Assert.NotEqual(0, resultAfterNaN.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Kama_Reset_ClearsState()
|
||||
{
|
||||
var kama = new Kama(10);
|
||||
kama.Update(new TValue(DateTime.UtcNow, 100));
|
||||
kama.Update(new TValue(DateTime.UtcNow, 110));
|
||||
|
||||
Assert.True(kama.Last.Value > 0);
|
||||
|
||||
kama.Reset();
|
||||
|
||||
Assert.Equal(0, kama.Last.Value);
|
||||
Assert.False(kama.IsHot);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Kama_FlatLine_ReturnsSameValue()
|
||||
{
|
||||
var kama = new Kama(10);
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
kama.Update(new TValue(DateTime.UtcNow, 100));
|
||||
}
|
||||
|
||||
Assert.Equal(100, kama.Last.Value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Skender.Stock.Indicators;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace QuanTAlib.Tests;
|
||||
|
||||
public class KamaValidationTests
|
||||
{
|
||||
private readonly TBarSeries _bars;
|
||||
private readonly TSeries _data;
|
||||
private readonly List<Quote> _skenderQuotes;
|
||||
private readonly ITestOutputHelper _output;
|
||||
|
||||
public KamaValidationTests(ITestOutputHelper output)
|
||||
{
|
||||
_output = output;
|
||||
|
||||
// 1. Generate 1000 records using GBM feed
|
||||
var gbm = new GBM(startPrice: 100.0, mu: 0.05, sigma: 0.2, seed: 42);
|
||||
_bars = gbm.Fetch(1000, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
|
||||
// 2. Extract Close TSeries
|
||||
_data = _bars.Close;
|
||||
|
||||
// 3. Prepare data for Skender (List<Quote>)
|
||||
_skenderQuotes = new List<Quote>();
|
||||
for (int i = 0; i < _bars.Count; i++)
|
||||
{
|
||||
_skenderQuotes.Add(new Quote
|
||||
{
|
||||
Date = new DateTime(_bars.Open.Times[i], DateTimeKind.Utc),
|
||||
Open = (decimal)_bars.Open[i].Value,
|
||||
High = (decimal)_bars.High[i].Value,
|
||||
Low = (decimal)_bars.Low[i].Value,
|
||||
Close = (decimal)_bars.Close[i].Value,
|
||||
Volume = (decimal)_bars.Volume[i].Value
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_Skender_Batch()
|
||||
{
|
||||
int[] periods = { 10, 14, 20 };
|
||||
int fastPeriod = 2;
|
||||
int slowPeriod = 30;
|
||||
|
||||
foreach (var period in periods)
|
||||
{
|
||||
// Calculate QuanTAlib KAMA (batch TSeries)
|
||||
var kama = new global::QuanTAlib.Kama(period, fastPeriod, slowPeriod);
|
||||
var qResult = kama.Update(_data);
|
||||
|
||||
// Calculate Skender KAMA
|
||||
var sResult = _skenderQuotes.GetKama(period, fastPeriod, slowPeriod).ToList();
|
||||
|
||||
// Compare last 100 records
|
||||
VerifyData_Skender(qResult, sResult);
|
||||
}
|
||||
_output.WriteLine("KAMA Batch(TSeries) validated successfully against Skender");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_Skender_Streaming()
|
||||
{
|
||||
int[] periods = { 10, 14, 20 };
|
||||
int fastPeriod = 2;
|
||||
int slowPeriod = 30;
|
||||
|
||||
foreach (var period in periods)
|
||||
{
|
||||
// Calculate QuanTAlib KAMA (streaming)
|
||||
var kama = new global::QuanTAlib.Kama(period, fastPeriod, slowPeriod);
|
||||
var qResults = new List<double>();
|
||||
foreach (var item in _data)
|
||||
{
|
||||
qResults.Add(kama.Update(item).Value);
|
||||
}
|
||||
|
||||
// Calculate Skender KAMA
|
||||
var sResult = _skenderQuotes.GetKama(period, fastPeriod, slowPeriod).ToList();
|
||||
|
||||
// Compare last 100 records
|
||||
VerifyData_Skender_Streaming(qResults, sResult);
|
||||
}
|
||||
_output.WriteLine("KAMA Streaming validated successfully against Skender");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_Skender_Span()
|
||||
{
|
||||
int[] periods = { 10, 14, 20 };
|
||||
int fastPeriod = 2;
|
||||
int slowPeriod = 30;
|
||||
|
||||
// Prepare data for Span API
|
||||
double[] sourceData = _data.Select(x => x.Value).ToArray();
|
||||
|
||||
foreach (var period in periods)
|
||||
{
|
||||
// Calculate QuanTAlib KAMA (Span API)
|
||||
double[] qOutput = new double[sourceData.Length];
|
||||
global::QuanTAlib.Kama.Calculate(sourceData.AsSpan(), qOutput.AsSpan(), period, fastPeriod, slowPeriod);
|
||||
|
||||
// Calculate Skender KAMA
|
||||
var sResult = _skenderQuotes.GetKama(period, fastPeriod, slowPeriod).ToList();
|
||||
|
||||
// Compare last 100 records
|
||||
VerifyData_Skender_Span(qOutput, sResult);
|
||||
}
|
||||
_output.WriteLine("KAMA Span validated successfully against Skender");
|
||||
}
|
||||
|
||||
private static void VerifyData_Skender(TSeries qSeries, List<KamaResult> sSeries)
|
||||
{
|
||||
Assert.Equal(qSeries.Count, sSeries.Count);
|
||||
|
||||
int count = qSeries.Count;
|
||||
int skip = count - 100;
|
||||
|
||||
for (int i = skip; i < count; i++)
|
||||
{
|
||||
double qValue = qSeries[i].Value;
|
||||
double? sValue = (double?)sSeries[i].Kama;
|
||||
|
||||
if (!sValue.HasValue) continue;
|
||||
|
||||
Assert.Equal(sValue.Value, qValue, 1e-6);
|
||||
}
|
||||
}
|
||||
|
||||
private static void VerifyData_Skender_Streaming(List<double> qResults, List<KamaResult> sSeries)
|
||||
{
|
||||
Assert.Equal(qResults.Count, sSeries.Count);
|
||||
|
||||
int count = qResults.Count;
|
||||
int skip = count - 100;
|
||||
|
||||
for (int i = skip; i < count; i++)
|
||||
{
|
||||
double qValue = qResults[i];
|
||||
double? sValue = (double?)sSeries[i].Kama;
|
||||
|
||||
if (!sValue.HasValue) continue;
|
||||
|
||||
Assert.Equal(sValue.Value, qValue, 1e-6);
|
||||
}
|
||||
}
|
||||
|
||||
private static void VerifyData_Skender_Span(double[] qOutput, List<KamaResult> sSeries)
|
||||
{
|
||||
Assert.Equal(qOutput.Length, sSeries.Count);
|
||||
|
||||
int count = qOutput.Length;
|
||||
int skip = count - 100;
|
||||
|
||||
for (int i = skip; i < count; i++)
|
||||
{
|
||||
double qValue = qOutput[i];
|
||||
double? sValue = (double?)sSeries[i].Kama;
|
||||
|
||||
if (!sValue.HasValue) continue;
|
||||
|
||||
Assert.Equal(sValue.Value, qValue, 1e-6);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
/// <summary>
|
||||
/// KAMA: Kaufman's Adaptive Moving Average
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// KAMA adapts to market volatility by adjusting its smoothing factor based on an Efficiency Ratio (ER).
|
||||
/// ER is calculated as the ratio of the absolute price change over a period to the sum of absolute price changes (volatility).
|
||||
///
|
||||
/// Formula:
|
||||
/// ER = Change / Volatility
|
||||
/// Change = Abs(Price - Price[period])
|
||||
/// Volatility = Sum(Abs(Price[i] - Price[i-1]), period)
|
||||
/// SC = (ER * (fast_alpha - slow_alpha) + slow_alpha)^2
|
||||
/// KAMA = KAMA[prev] + SC * (Price - KAMA[prev])
|
||||
/// </remarks>
|
||||
[SkipLocalsInit]
|
||||
public sealed class Kama : ITValuePublisher
|
||||
{
|
||||
private readonly int _period;
|
||||
private readonly double _fastAlpha;
|
||||
private readonly double _slowAlpha;
|
||||
private readonly RingBuffer _buffer;
|
||||
private double _kama;
|
||||
private double _p_kama;
|
||||
private double _volatilitySum;
|
||||
private double _p_volatilitySum;
|
||||
private double _lastDiffOut;
|
||||
private double _lastValidValue;
|
||||
|
||||
/// <summary>
|
||||
/// Display name for the indicator.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
public event Action<TValue>? Pub;
|
||||
|
||||
/// <summary>
|
||||
/// Current KAMA value.
|
||||
/// </summary>
|
||||
public TValue Last { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the KAMA has enough data to produce valid results.
|
||||
/// </summary>
|
||||
public bool IsHot => _buffer.IsFull;
|
||||
|
||||
/// <summary>
|
||||
/// Creates KAMA with specified parameters.
|
||||
/// </summary>
|
||||
/// <param name="period">Lookback period for Efficiency Ratio (default 10).</param>
|
||||
/// <param name="fastPeriod">Fast EMA period for SC calculation (default 2).</param>
|
||||
/// <param name="slowPeriod">Slow EMA period for SC calculation (default 30).</param>
|
||||
public Kama(int period = 10, int fastPeriod = 2, int slowPeriod = 30)
|
||||
{
|
||||
if (period <= 0)
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
if (fastPeriod <= 0)
|
||||
throw new ArgumentException("Fast period must be greater than 0", nameof(fastPeriod));
|
||||
if (slowPeriod <= 0)
|
||||
throw new ArgumentException("Slow period must be greater than 0", nameof(slowPeriod));
|
||||
if (fastPeriod >= slowPeriod)
|
||||
throw new ArgumentException("Fast period must be less than slow period", nameof(fastPeriod));
|
||||
|
||||
_period = period;
|
||||
// Buffer needs to hold period + 1 values to calculate Change over 'period' bars
|
||||
// Change = Price[0] - Price[period]
|
||||
_buffer = new RingBuffer(period + 1);
|
||||
|
||||
_fastAlpha = 2.0 / (fastPeriod + 1);
|
||||
_slowAlpha = 2.0 / (slowPeriod + 1);
|
||||
|
||||
Name = $"Kama({period}, {fastPeriod}, {slowPeriod})";
|
||||
_kama = double.NaN;
|
||||
}
|
||||
|
||||
public Kama(ITValuePublisher source, int period = 10, int fastPeriod = 2, int slowPeriod = 30)
|
||||
: this(period, fastPeriod, slowPeriod)
|
||||
{
|
||||
source.Pub += (item) => Update(item);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private double GetValidValue(double input)
|
||||
{
|
||||
if (double.IsFinite(input))
|
||||
{
|
||||
_lastValidValue = input;
|
||||
return input;
|
||||
}
|
||||
return _lastValidValue;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TValue Update(TValue input, bool isNew = true)
|
||||
{
|
||||
double val = GetValidValue(input.Value);
|
||||
|
||||
if (isNew)
|
||||
{
|
||||
_p_kama = _kama;
|
||||
_p_volatilitySum = _volatilitySum;
|
||||
|
||||
double removed = _buffer.Add(val);
|
||||
|
||||
if (_buffer.IsFull)
|
||||
{
|
||||
// removed is the value that fell off (Price[period+1] relative to new state?)
|
||||
// No, removed is the value that was at index 0 (oldest).
|
||||
// The new oldest is at index 0.
|
||||
// diff_out was abs(removed - new_oldest).
|
||||
double diff_out = Math.Abs(removed - _buffer[0]);
|
||||
_lastDiffOut = diff_out;
|
||||
|
||||
double diff_in = Math.Abs(_buffer[^1] - _buffer[^2]);
|
||||
_volatilitySum += diff_in - diff_out;
|
||||
}
|
||||
else if (_buffer.Count >= 2)
|
||||
{
|
||||
double diff_in = Math.Abs(_buffer[^1] - _buffer[^2]);
|
||||
_volatilitySum += diff_in;
|
||||
_lastDiffOut = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Restore state
|
||||
_kama = _p_kama;
|
||||
_buffer.UpdateNewest(val);
|
||||
|
||||
if (_buffer.IsFull)
|
||||
{
|
||||
double diff_in = Math.Abs(_buffer[^1] - _buffer[^2]);
|
||||
_volatilitySum = _p_volatilitySum + diff_in - _lastDiffOut;
|
||||
}
|
||||
else if (_buffer.Count >= 2)
|
||||
{
|
||||
double diff_in = Math.Abs(_buffer[^1] - _buffer[^2]);
|
||||
_volatilitySum = _p_volatilitySum + diff_in;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate KAMA
|
||||
if (double.IsNaN(_kama))
|
||||
{
|
||||
_kama = val;
|
||||
_p_kama = val; // Ensure p_kama is initialized
|
||||
}
|
||||
else
|
||||
{
|
||||
double change = Math.Abs(_buffer[^1] - _buffer[0]);
|
||||
double volatility = _volatilitySum;
|
||||
|
||||
// Avoid division by zero
|
||||
double er = (volatility > double.Epsilon) ? change / volatility : 0.0;
|
||||
// Cap ER at 1.0 just in case floating point errors push it slightly over
|
||||
if (er > 1.0) er = 1.0;
|
||||
|
||||
double sc = er * (_fastAlpha - _slowAlpha) + _slowAlpha;
|
||||
sc = sc * sc;
|
||||
|
||||
_kama = _p_kama + sc * (val - _p_kama);
|
||||
}
|
||||
|
||||
Last = new TValue(input.Time, _kama);
|
||||
Pub?.Invoke(Last);
|
||||
return Last;
|
||||
}
|
||||
|
||||
public TSeries Update(TSeries source)
|
||||
{
|
||||
if (source.Count == 0) return new TSeries(new List<long>(), new List<double>());
|
||||
|
||||
int len = source.Count;
|
||||
var t = new List<long>(len);
|
||||
var v = new List<double>(len);
|
||||
|
||||
// Use static Calculate for performance
|
||||
var outputSpan = new double[len];
|
||||
Calculate(source.Values, outputSpan, _period,
|
||||
(int)(2.0/_fastAlpha - 1), (int)(2.0/_slowAlpha - 1)); // Reverse calc periods from alphas?
|
||||
// Actually better to pass alphas or periods.
|
||||
// The static method signature should match constructor params.
|
||||
|
||||
// Wait, I need to pass periods to static method.
|
||||
// fastPeriod = 2/fastAlpha - 1.
|
||||
int fastPeriod = (int)Math.Round(2.0 / _fastAlpha - 1);
|
||||
int slowPeriod = (int)Math.Round(2.0 / _slowAlpha - 1);
|
||||
|
||||
Calculate(source.Values, outputSpan, _period, fastPeriod, slowPeriod);
|
||||
|
||||
for(int i=0; i<len; i++)
|
||||
{
|
||||
t.Add(source.Times[i]);
|
||||
v.Add(outputSpan[i]);
|
||||
}
|
||||
|
||||
// Restore state by replaying last few bars
|
||||
// This is expensive but necessary to sync the object state
|
||||
Reset();
|
||||
int startIndex = Math.Max(0, len - _period - 1);
|
||||
for (int i = startIndex; i < len; i++)
|
||||
{
|
||||
Update(source[i]);
|
||||
}
|
||||
|
||||
return new TSeries(t, v);
|
||||
}
|
||||
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int period, int fastPeriod = 2, int slowPeriod = 30)
|
||||
{
|
||||
if (period <= 0) throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
if (source.Length != output.Length) throw new ArgumentException("Source and output must have the same length");
|
||||
|
||||
double fastAlpha = 2.0 / (fastPeriod + 1);
|
||||
double slowAlpha = 2.0 / (slowPeriod + 1);
|
||||
|
||||
// We need a buffer for price history to calculate ER
|
||||
// Size period + 1
|
||||
int bufSize = period + 1;
|
||||
Span<double> buffer = bufSize <= 256 ? stackalloc double[bufSize] : new double[bufSize];
|
||||
int bufferIdx = 0;
|
||||
int count = 0;
|
||||
|
||||
double volatilitySum = 0;
|
||||
double kama = 0;
|
||||
bool kamaInitialized = false;
|
||||
double lastValid = 0;
|
||||
|
||||
for (int i = 0; i < source.Length; i++)
|
||||
{
|
||||
double val = source[i];
|
||||
if (double.IsFinite(val))
|
||||
lastValid = val;
|
||||
else
|
||||
val = lastValid;
|
||||
|
||||
// Add to buffer
|
||||
double removed = buffer[bufferIdx];
|
||||
buffer[bufferIdx] = val;
|
||||
|
||||
// Update volatility
|
||||
if (count >= 1)
|
||||
{
|
||||
// diff_in = abs(val - prev)
|
||||
// prev is at bufferIdx-1 (circular)
|
||||
int prevIdx = (bufferIdx - 1 + bufSize) % bufSize;
|
||||
double diff_in = Math.Abs(val - buffer[prevIdx]);
|
||||
|
||||
volatilitySum += diff_in;
|
||||
|
||||
if (count == bufSize)
|
||||
{
|
||||
// diff_out = abs(removed - new_oldest)
|
||||
// new_oldest is at (bufferIdx + 1) % bufSize
|
||||
int oldestIdx = (bufferIdx + 1) % bufSize;
|
||||
double diff_out = Math.Abs(removed - buffer[oldestIdx]);
|
||||
volatilitySum -= diff_out;
|
||||
}
|
||||
}
|
||||
|
||||
bufferIdx = (bufferIdx + 1) % bufSize;
|
||||
if (count < bufSize) count++;
|
||||
|
||||
if (!kamaInitialized)
|
||||
{
|
||||
kama = val;
|
||||
kamaInitialized = true;
|
||||
output[i] = kama;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Calculate ER
|
||||
// Change = abs(current - oldest)
|
||||
// current = val
|
||||
// oldest:
|
||||
// if full, oldest is at bufferIdx (which is the next write pos, so it holds the oldest)
|
||||
// Wait, bufferIdx points to where we WILL write next.
|
||||
// So buffer[bufferIdx] is the oldest value (the one that will be overwritten next).
|
||||
// So Change = abs(val - buffer[bufferIdx])
|
||||
|
||||
double change = 0;
|
||||
if (count == bufSize)
|
||||
{
|
||||
change = Math.Abs(val - buffer[bufferIdx]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If not full, oldest is at 0?
|
||||
// No, we fill 0, 1, 2...
|
||||
// Oldest is at 0.
|
||||
// But bufferIdx wraps.
|
||||
// If count < bufSize, we haven't wrapped yet (except maybe once if count==bufSize?)
|
||||
// If count < bufSize, bufferIdx is the index of next write.
|
||||
// Oldest is at 0.
|
||||
change = Math.Abs(val - buffer[0]);
|
||||
}
|
||||
|
||||
double er = (volatilitySum > double.Epsilon) ? change / volatilitySum : 0.0;
|
||||
if (er > 1.0) er = 1.0;
|
||||
|
||||
double sc = er * (fastAlpha - slowAlpha) + slowAlpha;
|
||||
sc = sc * sc;
|
||||
|
||||
kama = kama + sc * (val - kama);
|
||||
output[i] = kama;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_buffer.Clear();
|
||||
_kama = double.NaN;
|
||||
_p_kama = double.NaN;
|
||||
_volatilitySum = 0;
|
||||
_p_volatilitySum = 0;
|
||||
_lastDiffOut = 0;
|
||||
_lastValidValue = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
# KAMA: Kaufman's Adaptive Moving Average
|
||||
|
||||
## Overview and Purpose
|
||||
|
||||
Kaufman's Adaptive Moving Average (KAMA) is an intelligent technical indicator that automatically adjusts its sensitivity based on market conditions. Developed by Perry Kaufman, KAMA solves the fundamental problem of traditional moving averages: their inability to adapt to changing market volatility.
|
||||
|
||||
KAMA becomes more responsive during trending markets (high efficiency) and more stable during sideways or choppy conditions (low efficiency). This self-adjusting behavior makes it valuable for traders who need a single moving average that can effectively handle different market environments without manual parameter changes.
|
||||
|
||||
## Core Concepts
|
||||
|
||||
* **Efficiency Ratio (ER):** Measures the directional movement relative to volatility.
|
||||
* **Market Adaptation:** Automatically adjusts sensitivity based on current price behavior.
|
||||
* **Non-linear Response:** Uses a squared smoothing constant to emphasize differences between trending and non-trending states.
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Default | Description |
|
||||
|-----------|---------|-------------|
|
||||
| Period | 10 | The lookback window for the Efficiency Ratio. |
|
||||
| Fast Period | 2 | The effective EMA period when the market is trending (ER = 1). |
|
||||
| Slow Period | 30 | The effective EMA period when the market is choppy (ER = 0). |
|
||||
|
||||
## Formula
|
||||
|
||||
1. **Efficiency Ratio (ER):**
|
||||
$$ER = \frac{\text{Change}}{\text{Volatility}}$$
|
||||
$$\text{Change} = |P_t - P_{t-n}|$$
|
||||
$$\text{Volatility} = \sum_{i=0}^{n-1} |P_{t-i} - P_{t-i-1}|$$
|
||||
|
||||
2. **Smoothing Constant (SC):**
|
||||
$$SC = \left(ER \times (\alpha_{fast} - \alpha_{slow}) + \alpha_{slow}\right)^2$$
|
||||
$$\alpha_{fast} = \frac{2}{\text{FastPeriod} + 1}$$
|
||||
$$\alpha_{slow} = \frac{2}{\text{SlowPeriod} + 1}$$
|
||||
|
||||
3. **KAMA:**
|
||||
$$KAMA_t = KAMA_{t-1} + SC \times (P_t - KAMA_{t-1})$$
|
||||
|
||||
## C# Implementation
|
||||
|
||||
### Standard Usage
|
||||
|
||||
```csharp
|
||||
using QuanTAlib;
|
||||
|
||||
// Initialize with period 10, fast 2, slow 30
|
||||
var kama = new Kama(10, fastPeriod: 2, slowPeriod: 30);
|
||||
|
||||
// Update with new value
|
||||
TValue result = kama.Update(new TValue(time, price));
|
||||
Console.WriteLine($"KAMA: {result.Value}");
|
||||
```
|
||||
|
||||
### Zero-Allocation Span API
|
||||
|
||||
```csharp
|
||||
double[] prices = ...;
|
||||
double[] output = new double[prices.Length];
|
||||
|
||||
// Calculate KAMA for the entire array
|
||||
Kama.Calculate(prices.AsSpan(), output.AsSpan(), period: 10, fastPeriod: 2, slowPeriod: 30);
|
||||
```
|
||||
|
||||
### Bar Correction
|
||||
|
||||
```csharp
|
||||
var kama = new Kama(10);
|
||||
|
||||
// Update with initial tick
|
||||
kama.Update(new TValue(time, 100), isNew: true);
|
||||
|
||||
// Update with correction (same bar)
|
||||
kama.Update(new TValue(time, 101), isNew: false);
|
||||
```
|
||||
|
||||
## Interpretation
|
||||
|
||||
* **Trend Identification:** When price is consistently above KAMA, it indicates an uptrend. Below indicates a downtrend.
|
||||
* **Trend Strength:** A steep KAMA slope suggests a strong trend. A flat KAMA suggests consolidation.
|
||||
* **Support/Resistance:** KAMA often acts as dynamic support or resistance, especially during pullbacks in a trend.
|
||||
* **Filter:** KAMA filters out minor fluctuations during sideways markets while remaining responsive to genuine breakouts.
|
||||
|
||||
## References
|
||||
|
||||
* Kaufman, P. (1995). *Smarter Trading*. McGraw-Hill.
|
||||
* Kaufman, P. (2013). *Trading Systems and Methods*, 5th Edition. Wiley Trading.
|
||||
@@ -23,7 +23,7 @@ public class SmaIndicator : Indicator, IWatchlistIndicator
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"SMA {Period}:{SourceName}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/averages/sma/Sma.Quantower.cs";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/trends/sma/Sma.Quantower.cs";
|
||||
|
||||
public SmaIndicator()
|
||||
{
|
||||
@@ -23,7 +23,7 @@ public class TemaIndicator : Indicator, IWatchlistIndicator
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"TEMA {Period}:{SourceName}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/averages/tema/Tema.Quantower.cs";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/trends/tema/Tema.Quantower.cs";
|
||||
|
||||
public TemaIndicator()
|
||||
{
|
||||
@@ -23,7 +23,7 @@ public class TrimaIndicator : Indicator, IWatchlistIndicator
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"TRIMA {Period}:{SourceName}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/averages/trima/Trima.Quantower.cs";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/trends/trima/Trima.Quantower.cs";
|
||||
|
||||
public TrimaIndicator()
|
||||
{
|
||||
@@ -23,7 +23,7 @@ public class WmaIndicator : Indicator, IWatchlistIndicator
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"WMA {Period}:{SourceName}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/averages/wma/Wma.Quantower.cs";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/trends/wma/Wma.Quantower.cs";
|
||||
|
||||
public WmaIndicator()
|
||||
{
|
||||
@@ -47,7 +47,7 @@ public sealed class Wma : ITValuePublisher
|
||||
if (period <= 0) throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
|
||||
_period = period;
|
||||
_divisor = period * (period + 1) * 0.5;
|
||||
_divisor = (double)period * (period + 1) * 0.5;
|
||||
_buffer = new RingBuffer(period);
|
||||
Name = $"Wma({period})";
|
||||
}
|
||||
@@ -133,7 +133,7 @@ public sealed class Wma : ITValuePublisher
|
||||
_buffer.UpdateNewest(val);
|
||||
}
|
||||
|
||||
double currentDivisor = _buffer.IsFull ? _divisor : _buffer.Count * (_buffer.Count + 1) * 0.5;
|
||||
double currentDivisor = _buffer.IsFull ? _divisor : (double)_buffer.Count * (_buffer.Count + 1) * 0.5;
|
||||
Last = new TValue(input.Time, _wsum / currentDivisor);
|
||||
Pub?.Invoke(Last);
|
||||
return Last;
|
||||
@@ -226,7 +226,7 @@ public sealed class Wma : ITValuePublisher
|
||||
private static void CalculateScalarCore(ReadOnlySpan<double> source, Span<double> output, int period)
|
||||
{
|
||||
int len = source.Length;
|
||||
double divisor = period * (period + 1) * 0.5;
|
||||
double divisor = (double)period * (period + 1) * 0.5;
|
||||
double sum = 0;
|
||||
double wsum = 0;
|
||||
double lastValid = 0;
|
||||
@@ -248,7 +248,7 @@ public sealed class Wma : ITValuePublisher
|
||||
wsum += (i + 1) * val;
|
||||
buffer[i] = val;
|
||||
|
||||
double currentDivisor = (i + 1) * (i + 2) * 0.5;
|
||||
double currentDivisor = (double)(i + 1) * (i + 2) * 0.5;
|
||||
output[i] = wsum / currentDivisor;
|
||||
}
|
||||
|
||||
@@ -304,7 +304,7 @@ public sealed class Wma : ITValuePublisher
|
||||
ref double srcRef = ref MemoryMarshal.GetReference(source);
|
||||
ref double outRef = ref MemoryMarshal.GetReference(output);
|
||||
|
||||
double divisor = period * (period + 1) * 0.5;
|
||||
double divisor = (double)period * (period + 1) * 0.5;
|
||||
double invDivisor = 1.0 / divisor;
|
||||
|
||||
int warmupEnd = Math.Min(period, len);
|
||||
@@ -315,7 +315,7 @@ public sealed class Wma : ITValuePublisher
|
||||
double val = Unsafe.Add(ref srcRef, i);
|
||||
sum += val;
|
||||
wsum += (i + 1) * val;
|
||||
double currentDivisor = (i + 1) * (i + 2) * 0.5;
|
||||
double currentDivisor = (double)(i + 1) * (i + 2) * 0.5;
|
||||
Unsafe.Add(ref outRef, i) = wsum / currentDivisor;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,529 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<NDepend AppName="quantalib" Platform="DotNet" FileWrittenByProductVersion="2026.1.1.9785">
|
||||
<OutputDir KeepXmlFiles="False">./NDependOut</OutputDir>
|
||||
<IDEFiles>
|
||||
<IDEFile FilePath="./QuanTAlib.sln" Filters="-test" Configuration="DEBUG|AnyCPU">
|
||||
<RootDirResolvingInfo Enabled="False" Hints="Debug|bin|.bin|b|AnyCPU|x64|x86|v*.*|net*" TimeOut="10">
|
||||
<RootDir>.</RootDir>
|
||||
</RootDirResolvingInfo>
|
||||
</IDEFile>
|
||||
</IDEFiles>
|
||||
<RuntimeProfileDesc>.NET 10.0</RuntimeProfileDesc>
|
||||
<Assemblies />
|
||||
<Dirs />
|
||||
<MergeCodeGeneratedByCompiler>True</MergeCodeGeneratedByCompiler>
|
||||
<CompressNDar>True</CompressNDar>
|
||||
<ZipSourceFiles>True</ZipSourceFiles>
|
||||
<ForceReturnZeroExitCode>False</ForceReturnZeroExitCode>
|
||||
<Report Kind="0" SectionsEnabled="110591" XslPath="" DefaultTheme="dark" Flags="4455424" NbItemsToUnfold="3" NbItemsPerRule="20" MaxNbSourceFiles="2000" />
|
||||
<BuildComparisonSetting ProjectMode="CurrentProject" BuildMode="NDaysAgoAnalysisResult" ProjectFileToCompareWith="" BuildFileToCompareWith="" NDaysAgo="30" />
|
||||
<BaselineInUISetting ProjectMode="CurrentProject" BuildMode="NDaysAgoAnalysisResult" ProjectFileToCompareWith="" BuildFileToCompareWith="" NDaysAgo="30" />
|
||||
<CoverageFiles CoverageDir="" CoverageDirFilter="" UncoverableAttribute="" CoverageExclusionFile="" />
|
||||
<TrendMetrics UseCustomLog="False" LogRecurrence="3" LogLabel="2" UseCustomDir="False" CustomDir="">
|
||||
<Chart Name="Size" ShowInReport="True">
|
||||
<Serie MetricName="# Lines of Code" MetricUnit="Loc" Color="#FF00BFFF" ChartType="Line" ScaleExp="0" />
|
||||
<Serie MetricName="# Lines of Code Covered" MetricUnit="Loc" Color="#FF32CD32" ChartType="Area" ScaleExp="0" />
|
||||
<Serie MetricName="# Lines of Code (NotMyCode)" MetricUnit="Loc" Color="#FFA9A9A9" ChartType="Area" ScaleExp="0" />
|
||||
<Serie MetricName="# Lines of Comments" MetricUnit="Lines" Color="#FF008000" ChartType="Line" ScaleExp="0" />
|
||||
</Chart>
|
||||
<Chart Name="% Coverage and % Debt" ShowInReport="True">
|
||||
<Serie MetricName="Percentage Code Coverage" MetricUnit="%" Color="#FF32CD32" ChartType="Area" ScaleExp="0" />
|
||||
<Serie MetricName="Percentage Debt (Metric)" MetricUnit="%" Color="#FFFF0000" ChartType="Line" ScaleExp="0" />
|
||||
</Chart>
|
||||
<Chart Name="Issues" ShowInReport="True">
|
||||
<Serie MetricName="# New Issues since Baseline" MetricUnit="issues" Color="#FFFF0000" ChartType="Line" ScaleExp="0" />
|
||||
<Serie MetricName="# Issues Fixed since Baseline" MetricUnit="issues" Color="#FF32CD32" ChartType="Line" ScaleExp="0" />
|
||||
<Serie MetricName="# Blocker/Critical/High Issues" MetricUnit="issues" Color="#FFFF8C00" ChartType="Line" ScaleExp="0" />
|
||||
<Serie MetricName="# Issues" MetricUnit="issues" Color="#FFFFD700" ChartType="Line" ScaleExp="-2" />
|
||||
</Chart>
|
||||
<Chart Name="Rules" ShowInReport="True">
|
||||
<Serie MetricName="# Rules" MetricUnit="Rules" Color="#FF66CDAA" ChartType="Line" ScaleExp="0" />
|
||||
<Serie MetricName="# Rules Violated" MetricUnit="Rules" Color="#FFFF8C00" ChartType="Area" ScaleExp="0" />
|
||||
<Serie MetricName="# Critical Rules Violated" MetricUnit="Rules" Color="#FFFF0000" ChartType="Area" ScaleExp="0" />
|
||||
</Chart>
|
||||
<Chart Name="Quality Gates" ShowInReport="True">
|
||||
<Serie MetricName="# Quality Gates Fail" MetricUnit="quality gates" Color="#FFFF0000" ChartType="Line" ScaleExp="0" />
|
||||
<Serie MetricName="# Quality Gates Warn" MetricUnit="quality gates" Color="#FFFF8C00" ChartType="Line" ScaleExp="0" />
|
||||
<Serie MetricName="# Quality Gates" MetricUnit="quality gates" Color="#FF32CD32" ChartType="Line" ScaleExp="0" />
|
||||
</Chart>
|
||||
<Chart Name="Debt" ShowInReport="True">
|
||||
<Serie MetricName="Debt (Metric)" MetricUnit="man-days" Color="#FFFF0000" ChartType="Line" ScaleExp="0" />
|
||||
<Serie MetricName="Annual Interest (Metric)" MetricUnit="man-days" Color="#FFFF8C00" ChartType="Line" ScaleExp="0" />
|
||||
</Chart>
|
||||
</TrendMetrics>
|
||||
<GitHub Account="" Repo="" />
|
||||
<IssueImport ResolveSarifInSolutionDirectories="True" />
|
||||
<HistoricAnalysisResult PersistRecurrence="3" UseCustomDir="False" CustomDir="" RetentionNDays="60" />
|
||||
<SourceFileRebasing FromPath="" ToPath="" />
|
||||
<PathVariables />
|
||||
<RuleFiles />
|
||||
<GraphFiles />
|
||||
<ProjectRules AreActive="True" QueryPersistenceKind="PersistOnlyQueryIdForUntouchedQueries" />
|
||||
<ProjectDebtSettings DebtSettingsStorage="0" SettingsFilePath="">
|
||||
<DebtSettings>
|
||||
<DebtFactor>1</DebtFactor>
|
||||
<AnnualInterestFactor>1</AnnualInterestFactor>
|
||||
<DebtDefault>0</DebtDefault>
|
||||
<AnnualInterestDefault>0</AnnualInterestDefault>
|
||||
<DebtStringFormat>$ManDay$</DebtStringFormat>
|
||||
<MoneyPerManHour>50</MoneyPerManHour>
|
||||
<Currency>USD</Currency>
|
||||
<CurrencyLocation>After</CurrencyLocation>
|
||||
<EstimatedNumberOfManDayToDevelop1000LogicalLinesOfCode>18</EstimatedNumberOfManDayToDevelop1000LogicalLinesOfCode>
|
||||
<NumberOfWorkDayPerYear>240</NumberOfWorkDayPerYear>
|
||||
<NumberOfWorkHourPerDay>8</NumberOfWorkHourPerDay>
|
||||
<A2B_RatingThreshold>5</A2B_RatingThreshold>
|
||||
<B2C_RatingThreshold>10</B2C_RatingThreshold>
|
||||
<C2D_RatingThreshold>20</C2D_RatingThreshold>
|
||||
<D2E_RatingThreshold>50</D2E_RatingThreshold>
|
||||
<Low2Medium_SeverityThreshold>1200000000</Low2Medium_SeverityThreshold>
|
||||
<Medium2High_SeverityThreshold>12000000000</Medium2High_SeverityThreshold>
|
||||
<High2Critical_SeverityThreshold>72000000000</High2Critical_SeverityThreshold>
|
||||
<Critical2Blocker_SeverityThreshold>360000000000</Critical2Blocker_SeverityThreshold>
|
||||
</DebtSettings>
|
||||
</ProjectDebtSettings>
|
||||
<Queries>
|
||||
<Group Name="Quality Gates" Active="True" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Quality Gates Evolution$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Percentage Coverage$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Percentage Coverage on New Code$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Percentage Coverage on Refactored Code$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Blocker Issues$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Critical Issues$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$New Blocker / Critical / High Issues$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Critical Rules Violated$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Treat Compiler Warnings as Error$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Percentage Debt$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Debt$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$New Debt since Baseline$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Debt Rating per Namespace$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Annual Interest$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$New Annual Interest since Baseline$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Unnecessary NDepend SuppressMessageAttribute Usage$]]></Query>
|
||||
</Group>
|
||||
<Group Name="Hot Spots" Active="True" ShownInReport="True">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Types Hot Spots$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Types to Fix Priority$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Issues to Fix Priority$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Debt and Issues per Rule$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$New Debt and Issues per Rule$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Debt and Issues per Code Element$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$New Debt and Issues per Code Element$]]></Query>
|
||||
</Group>
|
||||
<Group Name="Code Smells" Active="True" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[$ND1000$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1001$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1002$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[$ND1003$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[$ND1004$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1005$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1006$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1007$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1008$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1009$]]></Query>
|
||||
</Group>
|
||||
<Group Name="Code Smells Regression" Active="True" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1100$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1101$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1102$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1103$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1104$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1105$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1106$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1107$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1108$]]></Query>
|
||||
</Group>
|
||||
<Group Name="Object Oriented Design" Active="True" ShownInReport="True">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1200$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1201$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1202$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1203$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1204$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[$ND1205$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1206$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1207$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1208$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1209$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1210$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1211$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1212$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1213$]]></Query>
|
||||
</Group>
|
||||
<Group Name="Design" Active="True" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1300$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1301$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[$ND1302$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[$ND1303$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1304$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1305$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1306$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1307$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1308$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1309$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1310$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1311$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1312$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1313$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1314$]]></Query>
|
||||
</Group>
|
||||
<Group Name="Architecture" Active="True" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[$ND1400$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1401$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1402$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1403$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1404$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1405$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1406$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1407$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1410$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1408$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1409$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND9900$]]></Query>
|
||||
</Group>
|
||||
<Group Name="API Breaking Changes" Active="True" ShownInReport="True">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[$ND1500$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[$ND1501$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[$ND1502$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[$ND1503$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[$ND1504$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[$ND1505$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$API: New publicly visible types$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$API: New publicly visible methods$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$API: New publicly visible fields$]]></Query>
|
||||
</Group>
|
||||
<Group Name="Code Coverage" Active="True" ShownInReport="True">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1600$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1601$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1602$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1603$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1604$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1605$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1606$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1607$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1608$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1609$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1610$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Test Methods$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Methods directly called by test Methods$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Methods directly and indirectly called by test Methods$]]></Query>
|
||||
</Group>
|
||||
<Group Name="Dead Code" Active="True" ShownInReport="True">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1700$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1701$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1702$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1703$]]></Query>
|
||||
</Group>
|
||||
<Group Name="Security" Active="True" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[$ND3120$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[$ND3121$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND3100$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND3101$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND3102$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND3103$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND3104$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND3105$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND3106$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND3107$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND3108$]]></Query>
|
||||
</Group>
|
||||
<Group Name="Visibility" Active="True" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1800$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1801$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1802$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1803$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1804$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1805$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1806$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1807$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1808$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1809$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1810$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Methods that should be declared as 'public' in C#, 'Public' in VB.NET$]]></Query>
|
||||
</Group>
|
||||
<Group Name="Immutability" Active="True" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1900$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[$ND1901$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1902$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1903$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1914$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1904$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1905$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1906$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1907$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1908$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1909$]]></Query>
|
||||
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1910$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1911$]]></Query>
|
||||
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1912$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND1913$]]></Query>
|
||||
</Group>
|
||||
<Group Name="Naming Conventions" Active="True" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2000$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2001$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[$ND2002$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2003$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[$ND2004$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[$ND2005$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2006$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2007$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2008$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2009$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2010$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2011$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[$ND2012$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2013$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2014$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[$ND2015$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2016$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2017$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2018$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2019$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2020$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2021$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2022$]]></Query>
|
||||
</Group>
|
||||
<Group Name="Source Files Organization" Active="True" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2100$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2101$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2102$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2103$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2104$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2105$]]></Query>
|
||||
</Group>
|
||||
<Group Name=".NET Framework Usage" Active="True" ShownInReport="False">
|
||||
<Group Name="System" Active="True" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2200$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2201$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2202$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2203$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2204$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2205$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2206$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2207$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2208$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2209$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2210$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2211$]]></Query>
|
||||
</Group>
|
||||
<Group Name="System.Collections" Active="True" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2300$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2301$]]></Query>
|
||||
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2302$]]></Query>
|
||||
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2303$]]></Query>
|
||||
</Group>
|
||||
<Group Name="System.Runtime.InteropServices" Active="True" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2400$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2401$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2402$]]></Query>
|
||||
</Group>
|
||||
<Group Name="System.Threading" Active="True" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[$ND2500$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2501$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[$ND2502$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[$ND2503$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[$ND2504$]]></Query>
|
||||
<Query Active="False" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2505$]]></Query>
|
||||
</Group>
|
||||
<Group Name="System.Xml" Active="True" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2600$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2601$]]></Query>
|
||||
</Group>
|
||||
<Group Name="System.Globalization" Active="True" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2700$]]></Query>
|
||||
</Group>
|
||||
<Group Name="System.Reflection" Active="True" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2800$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2801$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2802$]]></Query>
|
||||
</Group>
|
||||
<Group Name="Microsoft.Contracts" Active="True" ShownInReport="False">
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND2900$]]></Query>
|
||||
</Group>
|
||||
</Group>
|
||||
<Group Name="API Usage" Active="True" ShownInReport="False">
|
||||
<Group Name="Unity" Active="True" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND3200$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND3201$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND3202$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND3203$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND3204$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND3205$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND3206$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND3207$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND3208$]]></Query>
|
||||
</Group>
|
||||
</Group>
|
||||
<Group Name="Defining JustMyCode" Active="True" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND3000$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND3001$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND3002$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND3003$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND3004$]]></Query>
|
||||
<Query Active="False" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$JustMyCode code elements$]]></Query>
|
||||
<Query Active="False" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$NotMyCode code elements$]]></Query>
|
||||
</Group>
|
||||
<Group Name="Trend Metrics" Active="True" ShownInReport="False">
|
||||
<Group Name="Issues" Active="True" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# New Issues since Baseline$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Issues Fixed since Baseline$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Issues Worsened since Baseline$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Issues with severity Blocker$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Issues with severity Critical$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Issues with severity High$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Issues with severity Medium$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Issues with severity Low$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Blocker/Critical/High Issues$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Issues$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Suppressed Issues$]]></Query>
|
||||
</Group>
|
||||
<Group Name="Rules" Active="True" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Rules$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Rules Violated$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Critical Rules Violated$]]></Query>
|
||||
</Group>
|
||||
<Group Name="Quality Gates" Active="True" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Quality Gates$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Quality Gates Warn$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Quality Gates Fail$]]></Query>
|
||||
</Group>
|
||||
<Group Name="Debt" Active="True" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Percentage Debt (Metric)$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Debt (Metric)$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$New Debt since Baseline (Metric)$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Annual Interest (Metric)$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$New Annual Interest since Baseline (Metric)$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Breaking Point$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Breaking Point of Blocker / Critical / High Issues$]]></Query>
|
||||
</Group>
|
||||
<Group Name="Code Size" Active="True" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Lines of Code$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Lines of Code (JustMyCode)$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Lines of Code (NotMyCode)$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Lines of Code Added since the Baseline$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Source Files$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Line Feed$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# IL Instructions$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# IL Instructions (NotMyCode)$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Lines of Comments$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Percentage of Comments$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Assemblies$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Namespaces$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Types$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Public Types$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Classes$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Abstract Classes$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Interfaces$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Structures$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Methods$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Abstract Methods$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Concrete Methods$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Fields$]]></Query>
|
||||
</Group>
|
||||
<Group Name="Maximum and Average" Active="True" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Max # Lines of Code for Methods (JustMyCode)$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Average # Lines of Code for Methods$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Average # Lines of Code for Methods with at least 3 Lines of Code$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Max # Lines of Code for Types (JustMyCode)$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Average # Lines of Code for Types$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Max Cyclomatic Complexity for Methods$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Average Cyclomatic Complexity for Methods$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Max IL Cyclomatic Complexity for Methods$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Average IL Cyclomatic Complexity for Methods$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Max IL Nesting Depth for Methods$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Average IL Nesting Depth for Methods$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Max # of Methods for Types$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Average # Methods for Types$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Max # of Methods for Interfaces$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Average # Methods for Interfaces$]]></Query>
|
||||
</Group>
|
||||
<Group Name="Coverage" Active="True" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Percentage Code Coverage$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Lines of Code Covered$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Lines of Code Not Covered$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Lines of Code Uncoverable$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Lines of Code in Types 100% Covered$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Lines of Code in Methods 100% Covered$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Max C.R.A.P Score$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Average C.R.A.P Score$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Percentage JustMyCode Coverage$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Lines of JustMyCode Covered$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Lines of JustMyCode Not Covered$]]></Query>
|
||||
</Group>
|
||||
<Group Name="Third-Party Usage" Active="True" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Third-Party Assemblies Used$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Third-Party Namespaces Used$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Third-Party Types Used$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Third-Party Methods Used$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Third-Party Fields Used$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$# Third-Party Code Elements Used$]]></Query>
|
||||
</Group>
|
||||
</Group>
|
||||
<Group Name="Code Diff Summary" Active="True" ShownInReport="True">
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$New assemblies$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Assemblies removed$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Assemblies where code was changed$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$New namespaces$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Namespaces removed$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Namespaces where code was changed$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$New types$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Types removed$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Types where code was changed$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Heuristic to find types moved from one namespace or assembly to another$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Types directly using one or several types changed$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Types indirectly using one or several types changed$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$New methods$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Methods removed$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Methods where code was changed$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Methods directly calling one or several methods changed$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Methods indirectly calling one or several methods changed$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$New fields$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Fields removed$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Third party types that were not used and that are now used$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Third party types that were used and that are not used anymore$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Third party methods that were not used and that are now used$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Third party methods that were used and that are not used anymore$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Third party fields that were not used and that are now used$]]></Query>
|
||||
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Third party fields that were used and that are not used anymore$]]></Query>
|
||||
</Group>
|
||||
<Group Name="Statistics" Active="False" ShownInReport="False">
|
||||
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Most used types (Rank)$]]></Query>
|
||||
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Most used methods (Rank)$]]></Query>
|
||||
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Most used assemblies (#AssembliesUsingMe)$]]></Query>
|
||||
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Most used namespaces (#NamespacesUsingMe )$]]></Query>
|
||||
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Most used types (#TypesUsingMe )$]]></Query>
|
||||
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Most used methods (#MethodsCallingMe )$]]></Query>
|
||||
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Namespaces that use many other namespaces (#NamespacesUsed )$]]></Query>
|
||||
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Types that use many other types (#TypesUsed )$]]></Query>
|
||||
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$Methods that use many other methods (#MethodsCalled )$]]></Query>
|
||||
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$High-level to low-level assemblies (Level)$]]></Query>
|
||||
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$High-level to low-level namespaces (Level)$]]></Query>
|
||||
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$High-level to low-level types (Level)$]]></Query>
|
||||
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$High-level to low-level methods (Level)$]]></Query>
|
||||
</Group>
|
||||
<Group Name="Samples of Custom rules" Active="False" ShownInReport="False">
|
||||
<Group Name="Check Architecture" Active="False" ShownInReport="False">
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND9901$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND9902$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND9903$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND9904$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND9905$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND9906$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND9907$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND9908$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND9909$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND9910$]]></Query>
|
||||
</Group>
|
||||
<Group Name="Check Coverage on particular Code Elements" Active="False" ShownInReport="False">
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND9911$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND9912$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND9913$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND9914$]]></Query>
|
||||
</Group>
|
||||
<Group Name="Custom Naming Conventions" Active="False" ShownInReport="False">
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND9915$]]></Query>
|
||||
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[$ND9916$]]></Query>
|
||||
</Group>
|
||||
</Group>
|
||||
</Queries>
|
||||
<WarnFilter />
|
||||
</NDepend>
|
||||
@@ -27,8 +27,8 @@
|
||||
<Compile Include="*.Tests.cs" />
|
||||
<!-- Include core library types -->
|
||||
<Compile Include="..\lib\core\**\*.cs" Exclude="..\lib\core\**\*.Tests.cs" />
|
||||
<!-- Include averages implementations -->
|
||||
<Compile Include="..\lib\averages\**\*.cs" Exclude="..\lib\averages\**\*.Tests.cs;..\lib\averages\**\*.Validation.Tests.cs" />
|
||||
<!-- Include trends implementations -->
|
||||
<Compile Include="..\lib\trends\**\*.cs" Exclude="..\lib\trends\**\*.Tests.cs;..\lib\trends\**\*.Validation.Tests.cs" />
|
||||
<!-- Include IndicatorExtensions -->
|
||||
<Compile Include="IndicatorExtensions.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AssemblyName>Averages</AssemblyName>
|
||||
<AssemblyName>Trends</AssemblyName>
|
||||
<AlgoType>Indicator</AlgoType>
|
||||
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
|
||||
@@ -16,7 +16,7 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="IndicatorExtensions.cs" />
|
||||
<Compile Include="..\lib\core\**\*.cs" Exclude="..\lib\core\**\*.Tests.cs" />
|
||||
<Compile Include="..\lib\averages\**\*.cs" Exclude="..\lib\averages\**\*.Tests.cs" />
|
||||
<Compile Include="..\lib\trends\**\*.cs" Exclude="..\lib\trends\**\*.Tests.cs" />
|
||||
<Reference Include="TradingPlatform.BusinessLayer">
|
||||
<HintPath>..\.github\TradingPlatform.BusinessLayer.dll</HintPath>
|
||||
</Reference>
|
||||
@@ -26,7 +26,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="CopyCustomContent" AfterTargets="AfterBuild" Condition="'$(IsLocalBuild)' == 'true' AND $([MSBuild]::IsOSPlatform('Windows'))">
|
||||
<Copy SourceFiles="$(OutputPath)\Averages.dll" DestinationFolder="$(QuantowerRoot)\Settings\Scripts\Indicators\QuanTAlib\Averages" />
|
||||
<Copy SourceFiles="$(OutputPath)\Trends.dll" DestinationFolder="$(QuantowerRoot)\Settings\Scripts\Indicators\QuanTAlib\Trends" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,170 @@
|
||||
using Xunit;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib.Tests;
|
||||
|
||||
public class AlmaIndicatorTests
|
||||
{
|
||||
[Fact]
|
||||
public void AlmaIndicator_Constructor_SetsDefaults()
|
||||
{
|
||||
var indicator = new AlmaIndicator();
|
||||
|
||||
Assert.Equal(9, indicator.Period);
|
||||
Assert.Equal(0.85, indicator.Offset);
|
||||
Assert.Equal(6.0, indicator.Sigma);
|
||||
Assert.Equal(SourceType.Close, indicator.Source);
|
||||
Assert.True(indicator.ShowColdValues);
|
||||
Assert.Equal("ALMA - Arnaud Legoux Moving Average", indicator.Name);
|
||||
Assert.False(indicator.SeparateWindow);
|
||||
Assert.True(indicator.OnBackGround);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AlmaIndicator_MinHistoryDepths_EqualsPeriod()
|
||||
{
|
||||
var indicator = new AlmaIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(20, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AlmaIndicator_ShortName_IncludesPeriodAndSource()
|
||||
{
|
||||
var indicator = new AlmaIndicator { Period = 15 };
|
||||
|
||||
Assert.Contains("ALMA", indicator.ShortName);
|
||||
Assert.Contains("15", indicator.ShortName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AlmaIndicator_Initialize_CreatesInternalAlma()
|
||||
{
|
||||
var indicator = new AlmaIndicator { Period = 10 };
|
||||
|
||||
// Initialize should not throw
|
||||
indicator.Initialize();
|
||||
|
||||
// After init, line series should exist
|
||||
Assert.Single(indicator.LinesSeries);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AlmaIndicator_ProcessUpdate_HistoricalBar_ComputesValue()
|
||||
{
|
||||
var indicator = new AlmaIndicator { Period = 3 };
|
||||
indicator.Initialize();
|
||||
|
||||
// Add historical data
|
||||
var now = DateTime.UtcNow;
|
||||
indicator.HistoricalData.AddBar(now, 100, 105, 95, 102);
|
||||
|
||||
// Process update
|
||||
var args = new UpdateArgs(UpdateReason.HistoricalBar);
|
||||
indicator.ProcessUpdate(args);
|
||||
|
||||
// Line series should have a value
|
||||
Assert.Equal(1, indicator.LinesSeries[0].Count);
|
||||
Assert.True(double.IsFinite(indicator.LinesSeries[0].GetValue(0)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AlmaIndicator_ProcessUpdate_NewBar_ComputesValue()
|
||||
{
|
||||
var indicator = new AlmaIndicator { Period = 3 };
|
||||
indicator.Initialize();
|
||||
|
||||
// Add historical data
|
||||
var now = DateTime.UtcNow;
|
||||
indicator.HistoricalData.AddBar(now, 100, 105, 95, 102);
|
||||
indicator.HistoricalData.AddBar(now.AddMinutes(1), 102, 108, 100, 106);
|
||||
|
||||
// Process first update
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.HistoricalBar));
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.NewBar));
|
||||
|
||||
// Line series should have values
|
||||
Assert.Equal(2, indicator.LinesSeries[0].Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AlmaIndicator_ProcessUpdate_NewTick_ProcessesWithoutError()
|
||||
{
|
||||
var indicator = new AlmaIndicator { Period = 3 };
|
||||
indicator.Initialize();
|
||||
|
||||
// Add historical data
|
||||
var now = DateTime.UtcNow;
|
||||
indicator.HistoricalData.AddBar(now, 100, 105, 95, 102);
|
||||
|
||||
// Process historical bar first
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.HistoricalBar));
|
||||
double firstValue = indicator.LinesSeries[0].GetValue(0);
|
||||
|
||||
// Update with new tick (same bar data - simulates intrabar update)
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.NewTick));
|
||||
double secondValue = indicator.LinesSeries[0].GetValue(0);
|
||||
|
||||
// Both values should be finite
|
||||
Assert.True(double.IsFinite(firstValue));
|
||||
Assert.True(double.IsFinite(secondValue));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AlmaIndicator_MultipleUpdates_ProducesCorrectAlmaSequence()
|
||||
{
|
||||
var indicator = new AlmaIndicator { Period = 3 };
|
||||
indicator.Initialize();
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
double[] closes = { 100, 102, 104, 103, 105, 107, 106 };
|
||||
|
||||
foreach (var close in closes)
|
||||
{
|
||||
indicator.HistoricalData.AddBar(now, close, close + 2, close - 2, close);
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.HistoricalBar));
|
||||
now = now.AddMinutes(1);
|
||||
}
|
||||
|
||||
// All values should be finite
|
||||
for (int i = 0; i < closes.Length; i++)
|
||||
{
|
||||
Assert.True(double.IsFinite(indicator.LinesSeries[0].GetValue(closes.Length - 1 - i)));
|
||||
}
|
||||
|
||||
// ALMA should be smoothing the values
|
||||
double lastAlma = indicator.LinesSeries[0].GetValue(0);
|
||||
Assert.True(lastAlma >= 100 && lastAlma <= 110);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AlmaIndicator_DifferentSourceTypes_Work()
|
||||
{
|
||||
var sources = new[] { SourceType.Open, SourceType.High, SourceType.Low, SourceType.Close, SourceType.HL2, SourceType.HLC3 };
|
||||
|
||||
foreach (var source in sources)
|
||||
{
|
||||
var indicator = new AlmaIndicator { Period = 3, Source = source };
|
||||
indicator.Initialize();
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
indicator.HistoricalData.AddBar(now, 100, 110, 90, 105);
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.HistoricalBar));
|
||||
|
||||
Assert.True(double.IsFinite(indicator.LinesSeries[0].GetValue(0)),
|
||||
$"Source {source} should produce finite value");
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AlmaIndicator_Period_CanBeChanged()
|
||||
{
|
||||
var indicator = new AlmaIndicator { Period = 5 };
|
||||
Assert.Equal(5, indicator.Period);
|
||||
|
||||
indicator.Period = 20;
|
||||
Assert.Equal(20, indicator.Period);
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
using Xunit;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib.Tests;
|
||||
|
||||
public class KamaIndicatorTests
|
||||
{
|
||||
[Fact]
|
||||
public void KamaIndicator_Constructor_SetsDefaults()
|
||||
{
|
||||
var indicator = new KamaIndicator();
|
||||
|
||||
Assert.Equal(10, indicator.Period);
|
||||
Assert.Equal(2, indicator.FastPeriod);
|
||||
Assert.Equal(30, indicator.SlowPeriod);
|
||||
Assert.Equal(SourceType.Close, indicator.Source);
|
||||
Assert.True(indicator.ShowColdValues);
|
||||
Assert.Equal("KAMA - Kaufman Adaptive Moving Average", indicator.Name);
|
||||
Assert.False(indicator.SeparateWindow);
|
||||
Assert.True(indicator.OnBackGround);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void KamaIndicator_MinHistoryDepths_EqualsPeriod()
|
||||
{
|
||||
var indicator = new KamaIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(20, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void KamaIndicator_ShortName_IncludesPeriodAndSource()
|
||||
{
|
||||
var indicator = new KamaIndicator { Period = 15 };
|
||||
|
||||
Assert.Contains("KAMA", indicator.ShortName);
|
||||
Assert.Contains("15", indicator.ShortName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void KamaIndicator_Initialize_CreatesInternalKama()
|
||||
{
|
||||
var indicator = new KamaIndicator { Period = 10 };
|
||||
|
||||
// Initialize should not throw
|
||||
indicator.Initialize();
|
||||
|
||||
// After init, line series should exist
|
||||
Assert.Single(indicator.LinesSeries);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void KamaIndicator_ProcessUpdate_HistoricalBar_ComputesValue()
|
||||
{
|
||||
var indicator = new KamaIndicator { Period = 3 };
|
||||
indicator.Initialize();
|
||||
|
||||
// Add historical data
|
||||
var now = DateTime.UtcNow;
|
||||
indicator.HistoricalData.AddBar(now, 100, 105, 95, 102);
|
||||
|
||||
// Process update
|
||||
var args = new UpdateArgs(UpdateReason.HistoricalBar);
|
||||
indicator.ProcessUpdate(args);
|
||||
|
||||
// Line series should have a value
|
||||
Assert.Equal(1, indicator.LinesSeries[0].Count);
|
||||
Assert.True(double.IsFinite(indicator.LinesSeries[0].GetValue(0)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void KamaIndicator_ProcessUpdate_NewBar_ComputesValue()
|
||||
{
|
||||
var indicator = new KamaIndicator { Period = 3 };
|
||||
indicator.Initialize();
|
||||
|
||||
// Add historical data
|
||||
var now = DateTime.UtcNow;
|
||||
indicator.HistoricalData.AddBar(now, 100, 105, 95, 102);
|
||||
indicator.HistoricalData.AddBar(now.AddMinutes(1), 102, 108, 100, 106);
|
||||
|
||||
// Process first update
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.HistoricalBar));
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.NewBar));
|
||||
|
||||
// Line series should have values
|
||||
Assert.Equal(2, indicator.LinesSeries[0].Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void KamaIndicator_ProcessUpdate_NewTick_ProcessesWithoutError()
|
||||
{
|
||||
var indicator = new KamaIndicator { Period = 3 };
|
||||
indicator.Initialize();
|
||||
|
||||
// Add historical data
|
||||
var now = DateTime.UtcNow;
|
||||
indicator.HistoricalData.AddBar(now, 100, 105, 95, 102);
|
||||
|
||||
// Process historical bar first
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.HistoricalBar));
|
||||
double firstValue = indicator.LinesSeries[0].GetValue(0);
|
||||
|
||||
// Update with new tick (same bar data - simulates intrabar update)
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.NewTick));
|
||||
double secondValue = indicator.LinesSeries[0].GetValue(0);
|
||||
|
||||
// Both values should be finite
|
||||
Assert.True(double.IsFinite(firstValue));
|
||||
Assert.True(double.IsFinite(secondValue));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void KamaIndicator_MultipleUpdates_ProducesCorrectKamaSequence()
|
||||
{
|
||||
var indicator = new KamaIndicator { Period = 3 };
|
||||
indicator.Initialize();
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
double[] closes = { 100, 102, 104, 103, 105, 107, 106 };
|
||||
|
||||
foreach (var close in closes)
|
||||
{
|
||||
indicator.HistoricalData.AddBar(now, close, close + 2, close - 2, close);
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.HistoricalBar));
|
||||
now = now.AddMinutes(1);
|
||||
}
|
||||
|
||||
// All values should be finite
|
||||
for (int i = 0; i < closes.Length; i++)
|
||||
{
|
||||
Assert.True(double.IsFinite(indicator.LinesSeries[0].GetValue(closes.Length - 1 - i)));
|
||||
}
|
||||
|
||||
// KAMA should be smoothing the values
|
||||
double lastKama = indicator.LinesSeries[0].GetValue(0);
|
||||
Assert.True(lastKama >= 100 && lastKama <= 110);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void KamaIndicator_DifferentSourceTypes_Work()
|
||||
{
|
||||
var sources = new[] { SourceType.Open, SourceType.High, SourceType.Low, SourceType.Close, SourceType.HL2, SourceType.HLC3 };
|
||||
|
||||
foreach (var source in sources)
|
||||
{
|
||||
var indicator = new KamaIndicator { Period = 3, Source = source };
|
||||
indicator.Initialize();
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
indicator.HistoricalData.AddBar(now, 100, 110, 90, 105);
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.HistoricalBar));
|
||||
|
||||
Assert.True(double.IsFinite(indicator.LinesSeries[0].GetValue(0)),
|
||||
$"Source {source} should produce finite value");
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void KamaIndicator_Period_CanBeChanged()
|
||||
{
|
||||
var indicator = new KamaIndicator { Period = 5 };
|
||||
Assert.Equal(5, indicator.Period);
|
||||
|
||||
indicator.Period = 20;
|
||||
Assert.Equal(20, indicator.Period);
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user