Custom indicators
At some point in your journey, you may want to create your own custom indicators. The following is an example of how you’d create your own. This example is also included in our example usage code.
STEP 1: Create the Results class
Create your results class by extending the library ResultBase
class. This will allow you to inherit many of the utility functions, such as RemoveWarmupPeriods()
.
using Skender.Stock.Indicators;
namespace Custom.Stock.Indicators;
// custom results class
public class AtrWmaResult : ResultBase, IReusableResult
{
// date property is inherited here,
// so you only need to add custom items
public double? AtrWma { get; set; }
// to enable further chaining
double? IReusableResult.Value => AtrWma;
}
STEP 2: Create your custom indicator
Create your custom indicator algorithm in the same style as our main library so the API functions identically.
using System.Collections.ObjectModel;
using Skender.Stock.Indicators;
namespace Custom.Stock.Indicators;
public static class CustomIndicator
{
// Custom ATR WMA calculation
public static IEnumerable<AtrWmaResult> GetAtrWma<TQuote>(
this IEnumerable<TQuote> quotes,
int lookbackPeriods)
where TQuote : IQuote
{
// sort quotes and convert to collection or list
Collection<TQuote> quotesList = quotes
.ToSortedCollection();
// initialize results
List<AtrWmaResult> results = new(quotesList.Count);
// perform pre-requisite calculations to get ATR values
List<AtrResult> atrResults = quotes
.GetAtr(lookbackPeriods)
.ToList();
// roll through quotes
for (int i = 0; i < quotesList.Count; i++)
{
TQuote q = quotesList[i];
AtrWmaResult r = new()
{
Date = q.Date
};
// only do calculations after incalculable periods
if (i >= lookbackPeriods - 1)
{
double? sumWma = 0;
double? sumAtr = 0;
for (int p = i - lookbackPeriods + 1; p <= i; p++)
{
double close = (double)quotesList[p].Close;
double? atr = atrResults[p]?.Atr;
sumWma += atr * close;
sumAtr += atr;
}
r.AtrWma = sumWma / sumAtr;
}
// add record to results
results.Add(r);
}
return results;
}
}
STEP 3: Use your indicator just like the others
using Skender.Stock.Indicators;
using Custom.Stock.Indicators; // your custom library
[..]
// fetch historical quotes from your feed (your method)
IEnumerable<Quote> quotes = GetQuotesFromFeed("MSFT");
// calculate 10-period ATR WMA
IEnumerable<AtrWmaResult> results = quotes.GetAtrWma(10);
// use results as needed for your use case (example only)
foreach (AtrWmaResult r in results)
{
Console.WriteLine($"ATR WMA on {r.Date:d} was ${r.AtrWma:N4}");
}
ATR WMA on 4/19/2018 was $255.0590
ATR WMA on 4/20/2018 was $255.2015
ATR WMA on 4/23/2018 was $255.6135
ATR WMA on 4/24/2018 was $255.5105
ATR WMA on 4/25/2018 was $255.6570
ATR WMA on 4/26/2018 was $255.9705
..