fix on JMA

This commit is contained in:
Miha Kralj
2023-03-19 18:33:25 -07:00
parent bbd3ccb641
commit 167f20e3a8
13 changed files with 391 additions and 433 deletions
-57
View File
@@ -1,57 +0,0 @@
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using TradingPlatform.BusinessLayer;
namespace QuanTAlib;
public class AAA_chart : Indicator {
#region Parameters
[InputParameter("Smoothing period", 0, 1, 999, 1, 1)]
private readonly int Period = 10;
#endregion Parameters
private TBars bars;
private JMA_Series ind_a;
private DWMA_Series ind_b;
public override string ShortName => $"AAA ({this.Period})";
public AAA_chart() : base()
{
this.SeparateWindow = false;
this.Name = "AAA - Test indicator";
this.Description = "Test indicator";
this.AddLineSeries("JMA", Color.RoyalBlue, 3, LineStyle.Solid);
this.AddLineSeries("DWMA", Color.OrangeRed, 3, LineStyle.Solid);
this.SeparateWindow = false;
}
protected override void OnInit()
{
this.bars = new();
this.ind_a = new(source: bars.Close, period: this.Period, useNaN: false);
this.ind_b = new(source: bars.OHLC4, period: this.Period, useNaN: false);
}
protected override void OnUpdate(UpdateArgs args)
{
bool update = !(args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar);
this.bars.Add(this.Time(),
this.GetPrice(PriceType.Open),
this.GetPrice(PriceType.High),
this.GetPrice(PriceType.Low),
this.GetPrice(PriceType.Close),
this.GetPrice(PriceType.Volume),
update);
this.SetValue(this.ind_a.v.Last(), 0);
this.SetValue(this.ind_b.v.Last(), 1);
}
}
+42 -35
View File
@@ -1,50 +1,57 @@
using System;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using TradingPlatform.BusinessLayer;
namespace QuanTAlib;
public class JMA_chart : Indicator
{
#region Parameters
public class JMA_chart : Indicator {
#region Parameters
[InputParameter("Smoothing period", 0, 1, 999, 1, 1)]
private int Period = 10;
[InputParameter("Smoothing period", 0, 1, 999, 1, 1)]
private int Period = 10;
[InputParameter("Data source", 1, variants: new object[]
{ "Open", 0, "High", 1, "Low", 2, "Close", 3, "HL2", 4, "OC2", 5,
"OHL3", 6, "HLC3", 7, "OHLC4", 8, "Weighted (HLCC4)", 9 })]
private int DataSource = 3;
[InputParameter("Data source", 1, variants: new object[]
{ "Open", 0, "High", 1, "Low", 2, "Close", 3, "HL2", 4, "OC2", 5,
"OHL3", 6, "HLC3", 7, "OHLC4", 8, "Weighted (HLCC4)", 9 })]
private int DataSource = 3
;
[InputParameter("Slope calc", 2, 2, 10, 1, 1)]
private int SlopePeriod = 3;
#endregion Parameters
private TBars bars ;
#endregion Parameters
///////
private JMA_Series indicator;
///////
private TBars bars;
public JMA_chart()
{
this.SeparateWindow = false;
this.Name = "JMA - Jurik Moving Average";
this.Description = "Jurik Moving Average description";
this.AddLineSeries("JMA", Color.RoyalBlue, 3, LineStyle.Solid);
}
///////
private JMA_Series indicator;
private LINREG_Series slope;
///////
protected override void OnInit()
{
this.bars = new();
public JMA_chart() {
this.SeparateWindow = false;
this.Name = "JMA - Jurik Moving Avg";
this.Description = "Jurik Moving Average description";
this.AddLineSeries("JMA", Color.Blue, 4, LineStyle.Solid);
}
protected override void OnInit() {
this.bars = new();
this.indicator = new(source: bars.Select(this.DataSource), period: this.Period, useNaN: false);
}
protected override void OnUpdate(UpdateArgs args)
{
bool update = !(args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar);
this.bars.Add(this.Time(), this.GetPrice(PriceType.Open),
this.GetPrice(PriceType.High), this.GetPrice(PriceType.Low),
this.GetPrice(PriceType.Close),
this.GetPrice(PriceType.Volume), update);
this.slope = new(source: this.indicator, period: this.SlopePeriod);
}
double result = this.indicator.v.Last();
this.SetValue(result);
}
protected override void OnUpdate(UpdateArgs args) {
bool update = !(args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar);
this.bars.Add(this.Time(), this.GetPrice(PriceType.Open),
this.GetPrice(PriceType.High), this.GetPrice(PriceType.Low),
this.GetPrice(PriceType.Close), this.GetPrice(PriceType.Volume), update);
double result = this.indicator[this.indicator.Count - 1].v;
this.LinesSeries[0].SetMarker(offset: 0,color: this.slope > 0 ? Color.FromArgb(0,160,0) : Color.FromArgb(255, 0, 0));
this.SetValue(result, lineIndex: 0);
}
}
+1 -1
View File
@@ -41,7 +41,7 @@
<ItemGroup>
<Reference Include="TradingPlatform.BusinessLayer">
<HintPath>C:\Quantower\TradingPlatform\v1.128.20\bin\TradingPlatform.BusinessLayer.dll</HintPath>
<HintPath>C:\Quantower\TradingPlatform\v1.129.11\bin\TradingPlatform.BusinessLayer.dll</HintPath>
</Reference>
</ItemGroup>
</Project>
+1 -1
View File
@@ -2,7 +2,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Title>QuanTAlib</Title>
<Version>0.1.27</Version>
<Version>0.1.28</Version>
<Product>Library of Technical Indicators for .NET</Product>
<Description>Quantitative Technical Analysis library for real-time (streaming) data analysis</Description>
<RepositoryType>git</RepositoryType>
+38 -32
View File
@@ -19,51 +19,57 @@ Remark:
public class DEMA_Series : Single_TSeries_Indicator
{
private readonly System.Collections.Generic.List<double> _buffer1 = new();
private readonly System.Collections.Generic.List<double> _buffer2 = new();
private readonly double _k;
private readonly double _k;
private int _len;
private readonly bool _useSMA;
private double _sum, _lastsum, _lastlastsum;
private double _lastema1, _lastlastema1;
private double _lastema2, _lastlastema2;
private double _lastema2, _lastlastema2;
public DEMA_Series(TSeries source, int period, bool useNaN = false, bool useSMA = true) : base(source, period, useNaN)
{
_k = 2.0 / (_p + 1);
_k = 2.0 / (_p + 1);
_len = 0;
_useSMA = useSMA;
_lastema1 = _lastema2 =0;
_sum = _lastema1 = _lastema2 =0;
if (_data.Count > 0) { base.Add(_data); }
}
public override void Add((DateTime t, double v) TValue, bool update)
{
if (update)
{
_lastema1 = _lastlastema1;
_lastema2 = _lastlastema2;
}
if (update) {
_lastsum = _lastlastsum;
_lastema1 = _lastlastema1;
_lastema2 = _lastlastema2;
}
else {
_lastlastsum = _lastsum;
_lastlastema1 = _lastema1;
_lastlastema2 = _lastema2;
_len++;
}
double _ema1, _ema2, _dema;
if (this.Count < _p && _useSMA)
{
Add_Replace_Trim(_buffer1, TValue.v, _p, update);
_ema1 = 0;
for (int i=0; i<_buffer1.Count; i++) { _ema1 += _buffer1[i]; }
_ema1 /= _buffer1.Count;
_ema2 = _ema1;
}
else
{
_ema1 = (TValue.v - _lastema1) * _k + _lastema1;
_ema2 = (_ema1 - _lastema2) * _k + _lastema2;
}
_dema = 2*_ema1 - _ema2;
double _ema1, _ema2, _dema;
if (this.Count == 0) {
_ema1 = _ema2 = _sum = TValue.v;
}
else if (_len <= _period && _useSMA && _period != 0) {
_sum += TValue.v;
if (_period != 0 && _len > _period) {
_sum -= (_data[base.Count - _period - (update ? 1 : 0)].v);
}
_ema1 = _sum / Math.Min(_len, _period);
_ema2 = _ema1;
}
else {
_ema1 = (TValue.v - _lastema1) * _k + _lastema1;
_ema2 = (_ema1 - _lastema2) * _k + _lastema2;
}
_dema = 2*_ema1 - _ema2;
this._lastlastema1 = this._lastema1;
this._lastlastema2 = this._lastema2;
this._lastema1 = _ema1;
this._lastema2 = _ema2;
_lastema1 = _ema1;
_lastema2 = _ema2;
base.Add((TValue.t, _dema), update, _NaN);
base.Add((TValue.t, _dema), update, _NaN);
}
}
+7 -6
View File
@@ -24,22 +24,23 @@ public class EMA_Series : Single_TSeries_Indicator {
private double _k;
private double _lastema, _lastlastema;
private double _sum, _oldsum;
private int _len, _oldlen;
private int _len;
private readonly bool _useSMA;
public EMA_Series(TSeries source, int period, bool useNaN = false, bool useSMA = true) : base(source, period, useNaN) {
this._k = 2.0 / (this._p + 1);
_k = 2.0 / (_p + 1);
_sum = _oldsum = _lastema = _lastlastema = 0;
_len = _oldlen = 0;
_len = 0;
_useSMA = useSMA;
if (this._data.Count > 0) { base.Add(this._data); }
}
public override void Add((DateTime t, double v) TValue, bool update) {
double _ema = 0;
if (update) { _lastema = _lastlastema; _sum = _oldsum; }
else { _lastlastema = _lastema; _oldsum = _sum; _len++; }
double _ema = 0;
// when period = 0, create cumulative/additive series where _k is progressively larger
if (_period == 0) { _k = 2.0 / (_len + 1); }
@@ -48,7 +49,7 @@ public class EMA_Series : Single_TSeries_Indicator {
_ema = _sum = TValue.v;
}
// if SMA is used for seeding, calculate SMA within period
else if (_len <= _period && _useSMA && _p != 0) {
else if (_len <= _period && _useSMA && _period != 0) {
_sum += TValue.v;
if (_period != 0 && _len > _period) {
_sum -= (_data[base.Count - _period - (update ? 1 : 0)].v);
@@ -65,6 +66,6 @@ public class EMA_Series : Single_TSeries_Indicator {
}
public void Reset() {
_sum = _oldsum = _lastema = _lastlastema = 0;
_len = _oldlen = 0;
_len = 0;
}
}
+2 -1
View File
@@ -45,7 +45,7 @@ public class JMA_Series : Single_TSeries_Indicator {
}
public override void Add((System.DateTime t, double v) TValue, bool update) {
if (this.Count == 0) { prev_ma1 = TValue.v; }
if (this.Count == 0) { prev_ma1 = prev_jma = TValue.v; }
if (update) {
upperBand = p_upperBand;
lowerBand = p_lowerBand;
@@ -81,6 +81,7 @@ public class JMA_Series : Single_TSeries_Indicator {
else { volty_10.Add(volty); }
if (volty_10.Count > 10) { volty_10.RemoveAt(0); }
vsum = prev_vsum + 0.1 * (volty - volty_10.First());
prev_vsum = vsum;
if (update) { vsum_buff[vsum_buff.Count - 1] = vsum; }
else { vsum_buff.Add(vsum); }
if (vsum_buff.Count > (10 * _p)) { vsum_buff.RemoveAt(0); }
+25 -24
View File
@@ -1,22 +1,23 @@
# EMA: Exponential Moving Average
period = 10
![Alt text](./img/EMA_chart.svg)
EMA needs very short history buffer and calculates the EMA value using just the previous EMA value. The weight of the new datapoint (k) is k = 2 / (period-1)
Also known as exponentially weighted moving average, as it places greater weight on the most recent data points.
EMA reacts more agressively to recent data changes and calculates the current value using just the previous EMA value and current data point. The weight applied to the new value is typically $k = 2 / (period-1)$
## Calculation
There is an adopted practice to calculate $SMA$ when $n < period$.
EMA is a rolling calculation requiring only one historical data point to calculate the current value and is denoted as ${EMA}_{p}{(data)}$ where $p$ represents the period and $data$ represents the list of data points.
Some implementations of EMA calculate a seeding value of $EMA$ as a ${SMA}_{p}$ when $n < period$ - and start the $EMA$ calculation only after the warm-up period. QuanTAlib offers an option to enable/disable SMA warm-up.
$$
EMA_n = \left\{ \begin{array}{cl}
\frac{1}{p}\left( data_{n}-data_{n-p}\right)+SMA_{n-1} & : \ n \leq period \\
{k}\times ({data_{n}} - EMA_{n-1}) + EMA_{n-1} & : \ x > period
{k}\times ({data_{n}} - EMA_{n-1}) + EMA_{n-1} & : \ n > period
\end{array} \right.
$$
## Behavior
![Alt text](./img/EMA_chart.svg)
## Reference Calculation
period = 5
@@ -27,23 +28,23 @@ EMA_Series ema_nan = new(data, 5, useNaN: true);
for (int i=0; i< data.Count; i++)
Console.WriteLine($"{i}\t{data[i].v,7:f2}\t{ema_nan[i].v,7:f3}\t{ema[i].v,7:f3}");
```
|#|input|ema_NaN|ema|
|--|:--:|:--:|:--:|
|0| 81.59| NaN| 81.590|
|1| 81.06| NaN| 81.325|
|2| 82.87| NaN| 81.840|
|3| 83.00| NaN| 82.130|
|4| 83.61| 82.426| 82.426|
|5| 83.15| 82.667| 82.667|
|6| 82.84| 82.725| 82.725|
|7| 83.99| 83.147| 83.147|
|8| 84.55| 83.614| 83.614|
|9| 84.36| 83.863| 83.863|
|10| 85.53| 84.419| 84.419|
|11| 86.54| 85.126| 85.126|
|12| 86.89| 85.714| 85.714|
|13| 87.77| 86.399| 86.399|
|14| 87.29| 86.696| 86.696|
| #| Input | **QuanTAlib** | _TA-LIB_ | _Skender_ | _Pandas-TA_ | _Tulip_ |
|--|:--:|:--:|:--:|:--:|:--:|:--:|
|0| 81.59| **81.590**| _NaN_| _NaN_| _NaN_| _NaN_|
|1| 81.06| **81.840**| _NaN_| _NaN_| _NaN_| _NaN_|
|2| 82.87| **81.840**| _NaN_| _NaN_| _NaN_| _NaN_|
|3| 83.00| **82.130**| _NaN_| _NaN_| _NaN_| _NaN_|
|4| 83.61| **82.426**| _82.426_| _82.426_| _82.426_| _82.426_|
|5| 83.15| **82.667**| _82.667_| _82.667_| _82.667_|_82.667_|
|6| 82.84| **82.725**| _82.725_| _82.725_| _82.725_|_82.725_|
|7| 83.99| **83.147**| _83.147_| _83.147_| _83.147_|_83.147_|
|8| 84.55| **83.614**| _83.614_| _83.614_| _83.614_|_83.614_|
|9| 84.36| **83.863**| _83.863_| _83.863_| _83.863_|_83.863_|
|10| 85.53| **84.419**| _84.419_| _84.419_| _84.419_|_84.419_|
|11| 86.54| **85.126**| _85.126_| _85.126_| _85.126_|_85.126_|
|12| 86.89| **85.714**| _85.714_| _85.714_| _85.714_|_85.714_|
|13| 87.77| **86.399**| _86.399_| _86.399_| _86.399_|_86.399_|
|14| 87.29| **86.696**| _86.696_| _86.696_| _86.696_|_86.696_|
## References
- https://en.wikipedia.org/wiki/Exponential_smoothing
-2
View File
@@ -21,8 +21,6 @@ $$
- `period`: optional size of a lookback window; if set to 0, SMA calculates cumulative average across the whole source
- `useNaN`: if set to _true_, SMA_Series will hide values within the initial period with NaN (for compatibility with other libraries)
[Link to source](..\Source\Trends\SMA_Series.cs)
## Behavior
![Alt text](./img/SMA_chart.svg)
## Reference Calculation & Validation
+1
View File
@@ -1,4 +1,5 @@
* [Home](/)
* [FAQ - Frequently asked questions answered](QA.md)
* [List of all Indicators](indicators.md "Indicators coverage")
+272 -272
View File
@@ -1,272 +1,272 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"source": [
"# Quick Start\n",
"\n",
"In order to use this .NET Interactive Notebook and play along with QuanTAlib (outside of making your own app or plugging QuanTAlib into Quantower platform), you will need:\n",
"\n",
"- Installed <a href=\"https://code.visualstudio.com/\" target=\"_blank\">Visual Studio Code</a>\n",
"- Installed <a href=\"https://dotnet.microsoft.com/download/dotnet/6.0\" target=\"_blank\">.NET 6 SDK</a>\n",
"- Installed <a href=\"https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-interactive-vscode\" target=\"_blank\">.NET Interactive Notebooks</a> extension\n",
"\n",
"**For impatient**, here is a simple example of calculating three moving averages - SMA(data), EMA(SMA(data)) and WMA(EMA(SMA(data))) from 10 days of AAPL stock data using QuanTAlib:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"#r \"nuget:QuanTAlib;\"\n",
"using QuanTAlib;\n",
"\n",
"Yahoo_Feed aapl = new(\"AAPL\", 10);\n",
"TSeries data = aapl.Close;\n",
"SMA_Series sma = new(source: data, period: 5, useNaN: false);\n",
"EMA_Series ema = new(sma, period: 5); // by default, indicators expose all data, no NaN values\n",
"WMA_Series wma = new(ema, 5, useNaN: true); // for the final calculation we can hide early data with NaNs\n",
"\n",
"Console.Write($\"index\\t data\\t\\t sma(data)\\t ema(sma(data))\\t wma(ema(sma(data)))\\n\");\n",
"for (int i=0; i<aapl.Count; i++)\n",
" Console.Write($\"{i}\\t {data[i].t:yyyy-MM-dd}\\t {sma[i].v:f2}\\t\\t {ema[i].v:f2}\\t\\t {wma[i].v:f2}\\n\");"
]
},
{
"cell_type": "markdown",
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"source": [
"## Understanding QuanTAlib data model\n",
"\n",
"QuanTAlib expects that every data item is a tuple (TimeDate t, double v) and TSeries is a list of (t,v) tuples. There are several helpers built into the TSeries class to simplify adding elements:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"var item1 = (DateTime.Today, 105.3); // (DateTime, Value) tuple\n",
"double item2 = 293.1; // a simple double\n",
"\n",
"TSeries data = new();\n",
"data.Add(item1); // adding tuple variable\n",
"data.Add(item2); // QuanTAlib stamps the (double) with current time\n",
"data.Add(0); // directly adding a number (stamped with current time)\n",
"data.Add((DateTime.Now.AddDays(-3), 10)); // adding a tuple with timestamp 3 days ago\n",
"\n",
"data"
]
},
{
"cell_type": "markdown",
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"source": [
"TSeries list can display only values (without timestamps) or only timestamps (without values) by using `.v` or `.t` properties"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"data.v"
]
},
{
"cell_type": "markdown",
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"source": [
"The last element on the list can be accessed by .Last() or by [^1] - and using `.t` (time) and `.v` (value) properties. Also, casting a TSeries into (double) will return the value of the last element"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"bool IsTheSame = data.Last().v == data[^1].v;\n",
"double lastvalue = data;\n",
"\n",
"lastvalue"
]
},
{
"cell_type": "markdown",
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"source": [
"All indicators are just modified TSeries classes; they get all required input during class construction (source of the datafeed, period...) and they automatically subscribe to events of the datafeed. Whenever datafeed gets a new value, indicator will calculate its own value. Indicators are also event publishers, so other indicators can subscribe to their results, chaining indicators together:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"TSeries t1 = new() {0,1,2,3,4,5,6,7,8,9}; // t1 is loaded with data and activated as a publisher\n",
"EMA_Series t2 = new(t1, 3); // t2 will auto-load all history of t1 and wait for events from t1\n",
"ADD_Series t3 = new(t1, t2); // t3 is an ADDition of t1 and t2 - will also load history and wait for t2 events\n",
"DIV_Series t4 = new(1, t3); // t4 is calculating 1/t3 - and waiting for t3 events\n",
"\n",
"TSeries t5 = new(); // a wild indicator appeared! And it is empty!\n",
"t4.Pub += t5.Sub; // let us add a manual subscription to events coming from t4 - t5 is now listening to t4\n",
"t1.Add(0); // we add one new value to t1 - and trigger the full cascade of calculation! t5 is now full!\n",
"\n",
"t5.v"
]
},
{
"cell_type": "markdown",
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"source": [
"# MACD compounded indicator\n",
"\n",
"With QuanTAlib we can chain indicators together, creating complex compounded indicators. For example, we can create Moving Average Convergence/Divergence (MACD) indicators by chaining all required operations in a sequence:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"Yahoo_Feed aapl = new(\"AAPL\", 100);\n",
"TSeries close = aapl.Close; // close will get data from history\n",
"EMA_Series slow = new(close,26); // slow gets data from slow through pub-sub eventing\n",
"EMA_Series fast = new(close,12); // fast gets data from slow (via eventing)\n",
"SUB_Series macd = new(fast,slow); // macd is a SUBtraction: fast-slow\n",
"EMA_Series signal = new(macd,9); // signal is EMA of macd\n",
"SUB_Series histogram = new(macd, signal); // histogram is SUBtraction macd-signal\n",
"\n",
"histogram.v\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".NET (C#)",
"language": "C#",
"name": ".net-csharp"
},
"polyglot_notebook": {
"kernelInfo": {
"defaultKernelName": "csharp",
"items": [
{
"aliases": [
"c#",
"C#"
],
"languageName": "C#",
"name": "csharp"
},
{
"aliases": [
"frontend"
],
"languageName": null,
"name": "vscode"
},
{
"aliases": [],
"languageName": "KQL",
"name": "kql"
}
]
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"source": [
"# Quick Start\n",
"\n",
"In order to use this .NET Interactive Notebook and play along with QuanTAlib (outside of making your own app or plugging QuanTAlib into Quantower platform), you will need:\n",
"\n",
"- Installed <a href=\"https://code.visualstudio.com/\" target=\"_blank\">Visual Studio Code</a>\n",
"- Installed <a href=\"https://dotnet.microsoft.com/download/dotnet/6.0\" target=\"_blank\">.NET 6 SDK</a>\n",
"- Installed <a href=\"https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-interactive-vscode\" target=\"_blank\">.NET Interactive Notebooks</a> extension\n",
"\n",
"**For impatient**, here is a simple example of calculating three moving averages - SMA(data), EMA(SMA(data)) and WMA(EMA(SMA(data))) from 10 days of AAPL stock data using QuanTAlib:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"#r \"nuget:QuanTAlib;\"\n",
"using QuanTAlib;\n",
"\n",
"Yahoo_Feed aapl = new(\"AAPL\", 10);\n",
"TSeries data = aapl.Close;\n",
"SMA_Series sma = new(source: data, period: 5, useNaN: false);\n",
"EMA_Series ema = new(sma, period: 5); // by default, indicators expose all data, no NaN values\n",
"WMA_Series wma = new(ema, 5, useNaN: true); // for the final calculation we can hide early data with NaNs\n",
"\n",
"Console.Write($\"index\\t data\\t\\t sma(data)\\t ema(sma(data))\\t wma(ema(sma(data)))\\n\");\n",
"for (int i=0; i<aapl.Count; i++)\n",
" Console.Write($\"{i}\\t {data[i].t:yyyy-MM-dd}\\t {sma[i].v:f2}\\t\\t {ema[i].v:f2}\\t\\t {wma[i].v:f2}\\n\");"
]
},
{
"cell_type": "markdown",
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"source": [
"## Understanding QuanTAlib data model\n",
"\n",
"QuanTAlib expects that every data item is a tuple (TimeDate t, double v) and TSeries is a list of (t,v) tuples. There are several helpers built into the TSeries class to simplify adding elements:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"var item1 = (DateTime.Today, 105.3); // (DateTime, Value) tuple\n",
"double item2 = 293.1; // a simple double\n",
"\n",
"TSeries data = new();\n",
"data.Add(item1); // adding tuple variable\n",
"data.Add(item2); // QuanTAlib stamps the (double) with current time\n",
"data.Add(0); // directly adding a number (stamped with current time)\n",
"data.Add((DateTime.Now.AddDays(-3), 10)); // adding a tuple with timestamp 3 days ago\n",
"\n",
"data"
]
},
{
"cell_type": "markdown",
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"source": [
"TSeries list can display only values (without timestamps) or only timestamps (without values) by using `.v` or `.t` properties"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"data.v"
]
},
{
"cell_type": "markdown",
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"source": [
"The last element on the list can be accessed by .Last() or by [^1] - and using `.t` (time) and `.v` (value) properties. Also, casting a TSeries into (double) will return the value of the last element"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"bool IsTheSame = data.Last().v == data[^1].v;\n",
"double lastvalue = data;\n",
"\n",
"lastvalue"
]
},
{
"cell_type": "markdown",
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"source": [
"All indicators are just modified TSeries classes; they get all required input during class construction (source of the datafeed, period...) and they automatically subscribe to events of the datafeed. Whenever datafeed gets a new value, indicator will calculate its own value. Indicators are also event publishers, so other indicators can subscribe to their results, chaining indicators together:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"TSeries t1 = new() {0,1,2,3,4,5,6,7,8,9}; // t1 is loaded with data and activated as a publisher\n",
"EMA_Series t2 = new(t1, 3); // t2 will auto-load all history of t1 and wait for events from t1\n",
"ADD_Series t3 = new(t1, t2); // t3 is an ADDition of t1 and t2 - will also load history and wait for t2 events\n",
"DIV_Series t4 = new(1, t3); // t4 is calculating 1/t3 - and waiting for t3 events\n",
"\n",
"TSeries t5 = new(); // a wild indicator appeared! And it is empty!\n",
"t4.Pub += t5.Sub; // let us add a manual subscription to events coming from t4 - t5 is now listening to t4\n",
"t1.Add(0); // we add one new value to t1 - and trigger the full cascade of calculation! t5 is now full!\n",
"\n",
"t5.v"
]
},
{
"cell_type": "markdown",
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"polyglot_notebook": {
"kernelName": "csharp"
}
},
"source": [
"# MACD compounded indicator\n",
"\n",
"With QuanTAlib we can chain indicators together, creating complex compounded indicators. For example, we can create Moving Average Convergence/Divergence (MACD) indicators by chaining all required operations in a sequence:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
},
"vscode": {
"languageId": "polyglot-notebook"
}
},
"outputs": [],
"source": [
"Yahoo_Feed aapl = new(\"AAPL\", 100);\n",
"TSeries close = aapl.Close; // close will get data from history\n",
"EMA_Series slow = new(close,26); // slow gets data from slow through pub-sub eventing\n",
"EMA_Series fast = new(close,12); // fast gets data from slow (via eventing)\n",
"SUB_Series macd = new(fast,slow); // macd is a SUBtraction: fast-slow\n",
"EMA_Series signal = new(macd,9); // signal is EMA of macd\n",
"SUB_Series histogram = new(macd, signal); // histogram is SUBtraction macd-signal\n",
"\n",
"histogram.v\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".NET (C#)",
"language": "C#",
"name": ".net-csharp"
},
"polyglot_notebook": {
"kernelInfo": {
"defaultKernelName": "csharp",
"items": [
{
"aliases": [
"c#",
"C#"
],
"languageName": "C#",
"name": "csharp"
},
{
"aliases": [
"frontend"
],
"languageName": null,
"name": "vscode"
},
{
"aliases": [],
"languageName": "KQL",
"name": "kql"
}
]
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 154 KiB

After

Width:  |  Height:  |  Size: 154 KiB

+1 -1
View File
@@ -54,7 +54,7 @@
|ALMA - Arnaud Legoux Moving Average|`ALMA_Series`||✔️GetAlma|alma|
|DEMA - Double EMA Average|`DEMA_Series`|✔️DEMA|✔️GetDema|✔️dema|✔️dema|
|DWMA - Double WMA Average|`DWMA_Series`|||||
|⭐EMA - Exponential Moving Average|`EMA_Series`|✔️EMA|✔️GetEma|✔️ema|✔️ema|
|⭐[EMA - Exponential Moving Average](EMA.md)|`EMA_Series`|✔️EMA|✔️GetEma|✔️ema|✔️ema|
|EPMA - Endpoint Moving Average|||GetEpma||
|FRAMA - Fractal Adaptive Moving Average|||||
|FMA - Fibonacci's Weighted Moving Average|`FMA_Series`|||fwma|