diff --git a/Source/Trends/FMA_Series.cs b/Source/Trends/FMA_Series.cs
new file mode 100644
index 00000000..4e15155c
--- /dev/null
+++ b/Source/Trends/FMA_Series.cs
@@ -0,0 +1,59 @@
+namespace QuanTAlib;
+using System;
+
+/*
+FMA: Fibonacci Moving Average
+ FMA calculates the average across multiple EMAs with periods following Fibonacci sequence
+ (skipping initial Fibonacci numbers of 1, 1, 2) 3, 5, 8, 13, 21, 34...
+
+ FMA(n) = Average(EMA(3), EMA(5), EMA(8), ema(13), ... EMA(n-th Fib))
+
+Sources:
+ https://kaabar-sofien.medium.com/the-fibonacci-moving-average-the-full-guide-60e718117595
+ https://www.tradingview.com/script/6pxgp2vh-Fibonacci-Moving-Average-FMA/
+
+ */
+
+public class FMA_Series : Single_TSeries_Indicator {
+ double[,] fib;
+ double _oldsum;
+ int _len;
+
+ public FMA_Series(TSeries source, int period) : base(source, period, false) {
+ _len = period;
+ fib = new double[_len, 4];
+ int a = 3;
+ int b = 5;
+ int f = 0;
+ fib[0, 0] = 2 / ((double)a - 1);
+ if (_len > 1) { fib[1, 0] = 2 / ((double)b - 1); }
+ if (_len > 2) {
+ for (int i = 2; i < _len; i++) {
+ f = a + b;
+ a = b;
+ b = f;
+ fib[i, 0] = 2 / ((double)f - 1);
+ }
+ }
+ _oldsum = 0;
+ if (this._data.Count > 0) { base.Add(this._data); }
+ }
+
+ public override void Add((DateTime t, double v) TValue, bool update) {
+ double _sum = 0;
+ for (int i = 0; i < _len; i++) {
+ if (update) { fib[i, 1] = fib[i, 3]; _sum = _oldsum; }
+ else { fib[i, 3] = fib[i, 1]; _oldsum = _sum; }
+
+ if (this.Count == 0) { fib[i, 1] = TValue.v; }
+ else {
+ fib[i, 2] = fib[i, 0] * (TValue.v - fib[i, 1]) + fib[i, 1];
+ fib[i, 1] = fib[i, 2];
+ }
+ _sum += fib[i, 1];
+ }
+
+ double _fma = _sum / _len;
+ base.Add((TValue.t, _fma), update, _NaN);
+ }
+}
\ No newline at end of file
diff --git a/Source/Trends/JMA_Series.cs b/Source/Trends/JMA_Series.cs
index 0a8b201d..96e5bb0e 100644
--- a/Source/Trends/JMA_Series.cs
+++ b/Source/Trends/JMA_Series.cs
@@ -35,8 +35,8 @@ public class JMA_Series : Single_TSeries_Indicator {
upperBand = lowerBand = prev_ma1 = prev_det0 = prev_det1 = prev_vsum = prev_jma = Kv = del1 = del2 = 0.0;
Kv = 0;
pr = (phase * 0.01) + 1.5;
- if (phase < -100) pr = 0.5;
- if (phase > 100) pr = 2.5;
+ if (phase < -100) { pr = 0.5; }
+ if (phase > 100) { pr = 2.5; }
mma1 = new();
mma2 = new();
@@ -83,8 +83,7 @@ public class JMA_Series : Single_TSeries_Indicator {
vsum = prev_vsum + 0.1 * (volty - volty_10.First());
if (update) { vsum_buff[vsum_buff.Count - 1] = vsum; }
else { vsum_buff.Add(vsum); }
- if (vsum_buff.Count > (10*_p))
- vsum_buff.RemoveAt(0);
+ if (vsum_buff.Count > (10 * _p)) { vsum_buff.RemoveAt(0); }
double avolty = 0;
for (int i = 0; i < vsum_buff.Count; i++) { avolty += vsum_buff[i]; }
avolty /= vsum_buff.Count;
@@ -94,10 +93,8 @@ public class JMA_Series : Single_TSeries_Indicator {
double len1 = (Math.Log(Math.Sqrt(2.0 * _p)) / Math.Log(2.0)) + 2;
if (len1 < 0) len1 = 0;
double pow1 = Math.Max(len1 - 2.0, 0.5);
- if (rvolty > Math.Pow(len1, 1.0 / pow1))
- rvolty = Math.Pow(len1, 1.0 / pow1);
- if (rvolty < 1)
- rvolty = 1;
+ if (rvolty > Math.Pow(len1, 1.0 / pow1)) { rvolty = Math.Pow(len1, 1.0 / pow1); }
+ if (rvolty < 1) { rvolty = 1; }
//// from rvolty to second smoothing
double pow2 = Math.Pow(rvolty, pow1);
diff --git a/Tests/MovingAvg/FMA_Test.cs b/Tests/MovingAvg/FMA_Test.cs
new file mode 100644
index 00000000..2369784d
--- /dev/null
+++ b/Tests/MovingAvg/FMA_Test.cs
@@ -0,0 +1,31 @@
+using Xunit;
+using System;
+using QuanTAlib;
+
+namespace MovingAvg;
+public class FMA_Test
+{
+ [Fact]
+ public void Add_Test()
+ {
+ TSeries a = new() { 0, 1, 2, 3, 4, 5 };
+ FMA_Series c = new(a, 3);
+ Assert.Equal(6, c.Count);
+ a.Add(5);
+ Assert.Equal(a.Count, c.Count);
+ a.Add(0, update: true);
+ Assert.Equal(a.Count, c.Count);
+ }
+
+ [Fact]
+ public void Edge_Test()
+ {
+ TSeries a = new() { double.NaN, double.Epsilon, double.PositiveInfinity, double.MaxValue };
+ FMA_Series c = new(a, 3);
+ Assert.Equal(a.Count, c.Count);
+ a.Add(double.NaN);
+ Assert.Equal(a.Count, c.Count);
+ a.Add(double.PositiveInfinity);
+ Assert.Equal(a.Count, c.Count);
+ }
+}
diff --git a/Tests/Series/Update.cs b/Tests/Series/Update.cs
index e477da9f..bef582c7 100644
--- a/Tests/Series/Update.cs
+++ b/Tests/Series/Update.cs
@@ -155,7 +155,18 @@ public class Update {
Assert.Equal(lastLen, QL.Count); // same size
Assert.Equal(lastCalc, QL.Last()); // same data
}
- [Fact] public void HEMA() {
+ [Fact]
+ public void FMA() {
+ FMA_Series QL = new(source: bars.Close, period: period);
+ var lastData = bars.Close.Last();
+ var lastCalc = QL.Last();
+ int lastLen = QL.Count;
+ QL.Add((DateTime.Today, 0), update: true);
+ QL.Add(lastData, update: true);
+ Assert.Equal(lastLen, QL.Count); // same size
+ Assert.Equal(lastCalc, QL.Last()); // same data
+ }
+ [Fact] public void HEMA() {
HEMA_Series QL = new(source: bars.Close, period: period);
var lastData = bars.Close.Last();
var lastCalc = QL.Last();
diff --git a/docs/FMA.md b/docs/FMA.md
new file mode 100644
index 00000000..50b42426
--- /dev/null
+++ b/docs/FMA.md
@@ -0,0 +1,4 @@
+# FMA: Fibonacci Moving Average
+period = 6
+
+
\ No newline at end of file
diff --git a/docs/_sidebar.md b/docs/_sidebar.md
index f59cf77c..a2bf563e 100644
--- a/docs/_sidebar.md
+++ b/docs/_sidebar.md
@@ -9,6 +9,7 @@
* [SMMA - Smoothed Moving Average](SMMA.md)
* [TRIMA - Triangular Moving Average](TRIMA.md)
* [DWMA - Double Weighted Moving Average](DWMA.md)
+ * [FMA - Fibonacci Moving Average](FMA.md)
* [DEMA - Double Exponential MA](DEMA.md)
* [TEMA - Triple Exponential MA](TEMA.md)
* [ALMA - Arnaud Legoux Moving Average](ALMA.md)
diff --git a/docs/img/FMA_chart.svg b/docs/img/FMA_chart.svg
new file mode 100644
index 00000000..93d2997e
--- /dev/null
+++ b/docs/img/FMA_chart.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/img/quotes.gif b/docs/img/quotes.gif
new file mode 100644
index 00000000..f7ffdd1b
Binary files /dev/null and b/docs/img/quotes.gif differ
diff --git a/docs/indicators.md b/docs/indicators.md
index 514e1c35..8cfe9399 100644
--- a/docs/indicators.md
+++ b/docs/indicators.md
@@ -57,7 +57,7 @@
|⭐EMA - Exponential Moving Average|`EMA_Series`|✔️EMA|✔️GetEma|✔️ema|✔️ema|
|EPMA - Endpoint Moving Average|||GetEpma||
|FRAMA - Fractal Adaptive Moving Average|||||
-|FWMA - Fibonacci's Weighted Moving Average||||fwma|
+|FMA - Fibonacci's Weighted Moving Average|`FMA_Series`|||fwma|
|HILO - Gann High-Low Activator||||hilo|
|HEMA - Hull/EMA Average|`HEMA_Series`||||
|Hilbert Transform Instantaneous Trendline||HT_TRENDLINE|GetHtTrendline||
diff --git a/docs/readme.md b/docs/readme.md
index 67b099e8..582856d4 100644
--- a/docs/readme.md
+++ b/docs/readme.md
@@ -12,30 +12,24 @@
[](https://github.com/mihakralj/QuanTAlib/watchers)
[](https://dotnet.microsoft.com/en-us/download/dotnet/7.0)
-**Quan**titative **TA** **lib**rary (QuanTAlib) is a C# library of classess and methods for quantitative technical analysis useful for trading securities with [Quantower](https://www.quantower.com/) and other C#-based trading platforms.
+**Quan**titative **TA** **lib**rary (QuanTAlib) is a C# library of classess and methods for quantitative technical analysis useful for analyzing quotes with [Quantower](https://www.quantower.com/) and other C#-based trading platforms.
-**QuanTAlib** is written with some specific design criteria in mind - some reasons why there is '_yet another C# TA library_':
+**QuanTAlib** is written with some specific design criteria in mind - why there is '_yet another C# TA library_':
-- Supports both **historical data analysis** (working on bulk of historical arrays) and **real-time streaming analysis** (adding one data item at the time without the need to re-calculate the whole history)
-- **Calculate early data right** - no hiding of incomplete calculations with NaN values (unless explicitly requested), data is as valid as mathematically possible from the first value
-- Usage of events for communication between indicators - each data series is an event publisher, each indicator can be a subscriber - this allows easy and seamless data flow between indicators
+- Prioritize **real-time data analysis** (series can add new data and indicator doesn't have to re-calculate the whole history)
+- **Allow updates** to the last quote and adjusting the calculation to the still-forming bar
+- **Calculate early data right** - output data is as valid as mathematically possible from the first value onwards
+
+
If not obvious, QuanTAlib is intended for developers, and it does not focus on sources of OHLCV quotes. There are some very basic data feeds available to use in the learning process: `RND_Feed` and `GBM_Feed` for random data, `Yahoo_Feed` and `Alphavantage_Feed` for a quick grab of daily data of US stock market.
-See [Getting Started](https://github.com/mihakralj/QuanTAlib/blob/main/Docs/getting_started.ipynb) .NET interactive notebook to get a feel how library works. Developers can use QuanTAlib in .NET interactive or in console apps, but the best usage of the library is withing C#-enabled trading platforms - see **QuanTower_Charts** folder for Quantower examples.
+See [Getting Started](https://github.com/mihakralj/QuanTAlib/blob/main/Docs/getting_started.ipynb) .NET interactive notebook to get a feel how library works. Developers can use QuanTAlib in [Polyglot Notebooks](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-interactive-vscode) or in console apps, but the best usage of the library is with C#-enabled trading platforms - see **QuanTower_Charts** folder for Quantower examples and check **Releases** for compiled Quantower DLL.
### Coverage
[List of all indicators - current and planned](indicators.md)
-- **Basic calculations:** ADD, DIV, MAX, MIDPOINT, MIDPRICE, MIN, MUL, SUB, SUM, ZL
-- **Momentum:** CCI
-- **Statistics:** BIAS, VORR, COVAR, ENTROPY, KURTOSIS, LINREG, MAD, MAPE, MEDIAN, MSE, SDEV, SMAPE, SSDEV, SVAR, VAR, WMAPE, ZSCORE
-- **Trends:** ALMA, DEMA, DWMA, 3EMA, HEMA, HMA, JMA, KAMA, MACD, MAMA, RMA, SMA, SMMA, T3, TEMA, TRIMA, TRIX, WMA, ZLEMA
-- **Volatility:** ADL, ADOSC, ATR, ATRP, BBANDS, CMO, RSI
-- **Volume:** OBV
-- **Feeds:** GBM, RND, Yahoo, Alphavantage
-
### Validation
QuanTAlib uses validation tests with four other TA libraries to assure accuracy and validity of results:
@@ -43,4 +37,14 @@ QuanTAlib uses validation tests with four other TA libraries to assure accuracy
- [TA-LIB](https://www.ta-lib.org/function.html)
- [Skender Stock Indicators](https://dotnet.stockindicators.dev/)
- [Pandas-TA](https://twopirllc.github.io/pandas-ta/)
-- [Tulip Indicators](https://tulipindicators.org/)
\ No newline at end of file
+- [Tulip Indicators](https://tulipindicators.org/)
+
+### Performance
+
+Is QuanTAlib fast? Well, _no_, but actually *yes*. QuanTAlib works on an additive principle, meaning that even when served a full list of quotes, QuanTAlib will process one item at the time, rolling forward throug time series of data.
+- If the last bar is still forming (parameter `update: true`), QuanTAlib easily recalculates the last entry as often as needed without any need to recalculate the history.
+- If a new bar is added to the input, QuanTAlib will process that one item (default parameter `update: false`) and add that one result to the List. No recalculation of the history needed.
+
+If you feed QuanTAlib 500 historical bars and calculate EMA(20) on it, the performance of QuanTAlib will be dead last compared to all other TA libraries.
+
+But when system uses 10,000 historical bars, does updates to the current/last bar every tick, and adds a new bar every minute, QuanTAlib has no rivals; all other libraries need to re-calculate the full length of the array on each update/addition to the time series. Longer the series and more updates/additions it gets, more advantage for QuanTAlib.
\ No newline at end of file