Files
QuanTAlib/docs/getting_started.ipynb
T
Miha Kralj 34997cd0d6 new: EQUITY_Series
Add new EQUITY_Series and updates to docs, Calculations, Indicators, Strategies, Tests, and .github/workflows
2023-04-07 16:50:19 -07:00

446 lines
15 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"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": 1,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
}
},
"outputs": [
{
"data": {
"text/html": [
"<div><div></div><div></div><div></div></div>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"index\t data\t\t sma(data)\t ema(sma(data))\t wma(ema(sma(data)))\n",
"0\t 2023-03-27\t 158.28\t\t 158.28\t\t NaN\n",
"1\t 2023-03-28\t 157.97\t\t 158.12\t\t NaN\n",
"2\t 2023-03-29\t 158.90\t\t 158.38\t\t NaN\n",
"3\t 2023-03-30\t 159.77\t\t 158.73\t\t NaN\n",
"4\t 2023-03-31\t 160.79\t\t 159.14\t\t 158.69\n",
"5\t 2023-04-03\t 162.37\t\t 160.22\t\t 159.25\n",
"6\t 2023-04-04\t 163.97\t\t 161.47\t\t 160.10\n",
"7\t 2023-04-05\t 164.56\t\t 162.50\t\t 161.07\n",
"8\t 2023-04-06\t 165.02\t\t 163.34\t\t 162.04\n"
]
}
],
"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": {},
"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": 2,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
}
},
"outputs": [
{
"data": {
"text/html": [
"<table><thead><tr><th><i>index</i></th><th>value</th></tr></thead><tbody><tr><td>0</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>(4/7/2023 12:00:00 AM, 105.3)</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Item1</td><td><span>2023-04-07 00:00:00Z</span></td></tr><tr><td>Item2</td><td><div class=\"dni-plaintext\"><pre>105.3</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>1</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>(4/7/2023 2:34:48 PM, 293.1)</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Item1</td><td><span>2023-04-07 14:34:48Z</span></td></tr><tr><td>Item2</td><td><div class=\"dni-plaintext\"><pre>293.1</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>2</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>(4/7/2023 2:34:48 PM, 0)</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Item1</td><td><span>2023-04-07 14:34:48Z</span></td></tr><tr><td>Item2</td><td><div class=\"dni-plaintext\"><pre>0</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>3</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>(4/4/2023 2:34:48 PM, 10)</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Item1</td><td><span>2023-04-04 14:34:48Z</span></td></tr><tr><td>Item2</td><td><div class=\"dni-plaintext\"><pre>10</pre></div></td></tr></tbody></table></div></details></td></tr></tbody></table><style>\r\n",
".dni-code-hint {\r\n",
" font-style: italic;\r\n",
" overflow: hidden;\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview {\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview td {\r\n",
" vertical-align: top;\r\n",
" text-align: start;\r\n",
"}\r\n",
"details.dni-treeview {\r\n",
" padding-left: 1em;\r\n",
"}\r\n",
"table td {\r\n",
" text-align: start;\r\n",
"}\r\n",
"table tr { \r\n",
" vertical-align: top; \r\n",
" margin: 0em 0px;\r\n",
"}\r\n",
"table tr td pre \r\n",
"{ \r\n",
" vertical-align: top !important; \r\n",
" margin: 0em 0px !important;\r\n",
"} \r\n",
"table th {\r\n",
" text-align: start;\r\n",
"}\r\n",
"</style>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"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": {},
"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": 3,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
}
},
"outputs": [
{
"data": {
"text/html": [
"<div class=\"dni-plaintext\"><pre>[ 105.3, 293.1, 0, 10 ]</pre></div><style>\r\n",
".dni-code-hint {\r\n",
" font-style: italic;\r\n",
" overflow: hidden;\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview {\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview td {\r\n",
" vertical-align: top;\r\n",
" text-align: start;\r\n",
"}\r\n",
"details.dni-treeview {\r\n",
" padding-left: 1em;\r\n",
"}\r\n",
"table td {\r\n",
" text-align: start;\r\n",
"}\r\n",
"table tr { \r\n",
" vertical-align: top; \r\n",
" margin: 0em 0px;\r\n",
"}\r\n",
"table tr td pre \r\n",
"{ \r\n",
" vertical-align: top !important; \r\n",
" margin: 0em 0px !important;\r\n",
"} \r\n",
"table th {\r\n",
" text-align: start;\r\n",
"}\r\n",
"</style>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"data.v"
]
},
{
"cell_type": "markdown",
"metadata": {},
"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": 4,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
}
},
"outputs": [
{
"data": {
"text/html": [
"<div class=\"dni-plaintext\"><pre>10</pre></div><style>\r\n",
".dni-code-hint {\r\n",
" font-style: italic;\r\n",
" overflow: hidden;\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview {\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview td {\r\n",
" vertical-align: top;\r\n",
" text-align: start;\r\n",
"}\r\n",
"details.dni-treeview {\r\n",
" padding-left: 1em;\r\n",
"}\r\n",
"table td {\r\n",
" text-align: start;\r\n",
"}\r\n",
"table tr { \r\n",
" vertical-align: top; \r\n",
" margin: 0em 0px;\r\n",
"}\r\n",
"table tr td pre \r\n",
"{ \r\n",
" vertical-align: top !important; \r\n",
" margin: 0em 0px !important;\r\n",
"} \r\n",
"table th {\r\n",
" text-align: start;\r\n",
"}\r\n",
"</style>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"bool IsTheSame = data.Last().v == data[^1].v;\n",
"double lastvalue = data;\n",
"\n",
"lastvalue"
]
},
{
"cell_type": "markdown",
"metadata": {},
"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": 5,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
}
},
"outputs": [
{
"data": {
"text/html": [
"<div class=\"dni-plaintext\"><pre>[ Infinity, 0.6666666666666666, 0.3333333333333333, 0.2, 0.14285714285714285, 0.1111111111111111, 0.09090909090909091, 0.07692307692307693, 0.06666666666666667, 0.058823529411764705, 0.25 ]</pre></div><style>\r\n",
".dni-code-hint {\r\n",
" font-style: italic;\r\n",
" overflow: hidden;\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview {\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview td {\r\n",
" vertical-align: top;\r\n",
" text-align: start;\r\n",
"}\r\n",
"details.dni-treeview {\r\n",
" padding-left: 1em;\r\n",
"}\r\n",
"table td {\r\n",
" text-align: start;\r\n",
"}\r\n",
"table tr { \r\n",
" vertical-align: top; \r\n",
" margin: 0em 0px;\r\n",
"}\r\n",
"table tr td pre \r\n",
"{ \r\n",
" vertical-align: top !important; \r\n",
" margin: 0em 0px !important;\r\n",
"} \r\n",
"table th {\r\n",
" text-align: start;\r\n",
"}\r\n",
"</style>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"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": {},
"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": 6,
"metadata": {
"dotnet_interactive": {
"language": "csharp"
}
},
"outputs": [
{
"data": {
"text/html": [
"<div class=\"dni-plaintext\"><pre>[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.000974358974349343, -0.03456027049873228, -0.13792617985566447, -0.4729486712049916, -0.825402881197467, -0.8902360596814031, -0.9360607784903126, -0.7333381872239422 ... (79 more) ]</pre></div><style>\r\n",
".dni-code-hint {\r\n",
" font-style: italic;\r\n",
" overflow: hidden;\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview {\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview td {\r\n",
" vertical-align: top;\r\n",
" text-align: start;\r\n",
"}\r\n",
"details.dni-treeview {\r\n",
" padding-left: 1em;\r\n",
"}\r\n",
"table td {\r\n",
" text-align: start;\r\n",
"}\r\n",
"table tr { \r\n",
" vertical-align: top; \r\n",
" margin: 0em 0px;\r\n",
"}\r\n",
"table tr td pre \r\n",
"{ \r\n",
" vertical-align: top !important; \r\n",
" margin: 0em 0px !important;\r\n",
"} \r\n",
"table th {\r\n",
" text-align: start;\r\n",
"}\r\n",
"</style>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"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"
},
"language_info": {
"name": "polyglot-notebook"
},
"polyglot_notebook": {
"kernelInfo": {
"defaultKernelName": "csharp",
"items": [
{
"aliases": [
"C#",
"c#"
],
"languageName": "C#",
"name": "csharp"
},
{
"aliases": [],
"languageName": "KQL",
"name": "kql"
},
{
"aliases": [
"frontend"
],
"name": "vscode"
}
]
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}