Guide and Pro tips
Getting started
Installation and setup
Find and install the Skender.Stock.Indicators NuGet package into your Project. See more help for installing packages.
# dotnet CLI example
dotnet add package Skender.Stock.Indicators
# package manager example
Install-Package Skender.Stock.Indicators
Prerequisite data
Most indicators require that you provide historical quote data and additional configuration parameters.
You must get historical quotes from your own market data provider. For clarification, the GetQuotesFromFeed()
method shown in the example below is not part of this library, but rather an example to represent your own acquisition of historical quotes.
Historical price data can be provided as a List
, IEnumerable
, or ICollection
of the Quote
class (see below); however, it can also be supplied as a generic custom TQuote type if you prefer to use your own quote model.
For additional configuration parameters, default values are provided when there is an industry standard. You can, of course, override these and provide your own values.
Example usage
All indicator methods will produce all possible results for the provided historical quotes as a time series dataset – it is not just a single data point returned. For example, if you provide 3 years worth of historical quotes for the SMA method, you’ll get 3 years of SMA result values.
using Skender.Stock.Indicators;
[..]
// fetch historical quotes from your feed (your method)
IEnumerable<Quote> quotes = GetQuotesFromFeed("MSFT");
// calculate 20-period SMA
IEnumerable<SmaResult> results = quotes
.GetSma(20);
// use results as needed for your use case (example only)
foreach (SmaResult r in results)
{
Console.WriteLine($"SMA on {r.Date:d} was ${r.Sma:N4}");
}
SMA on 4/19/2018 was $255.0590
SMA on 4/20/2018 was $255.2015
SMA on 4/23/2018 was $255.6135
SMA on 4/24/2018 was $255.5105
SMA on 4/25/2018 was $255.6570
SMA on 4/26/2018 was $255.9705
..
See individual indicator pages for specific usage guidance.
More examples available:
- Example usage code in a simple working console application
- Demo site (a stock chart)
Historical quotes
You must provide historical price quotes to the library in the standard OHLCV IEnumerable<Quote>
or a compatible List
or ICollection
format. It should have a consistent period frequency (day, hour, minute, etc). See using custom quote classes if you prefer to use your own quote class.
name | type | notes |
---|---|---|
Date | DateTime | Date |
Open | decimal | Open price |
High | decimal | High price |
Low | decimal | Low price |
Close | decimal | Close price |
Volume | decimal | Volume |
Where can I get historical quote data?
There are many places to get financial market data. Check with your brokerage or other commercial sites. If you’re looking for a free developer API, see our ongoing discussion on market data for ideas.
How much historical quote data do I need?
Each indicator will need different amounts of price quotes
to calculate. You can find guidance on the individual indicator documentation pages for minimum requirements; however, most use cases will require that you provide more than the minimum. As a general rule of thumb, you will be safe if you provide 750 points of historical quote data (e.g. 3 years of daily data).
🚩 IMPORTANT! Applying the minimum amount of quote history as possible is NOT a good way to optimize your system. Some indicators use a smoothing technique that converges to better precision over time. While you can calculate these with the minimum amount of quote data, the precision to two decimal points often requires 250 or more preceding historical records.
For example, if you are using daily data and want one year of precise EMA(250) data, you need to provide 3 years of historical quotes (1 extra year for the lookback period and 1 extra year for convergence); thereafter, you would discard or not use the first two years of results. Occasionally, even more is required for optimal precision.
See discussion on warmup and convergence for more information.
Using custom quote classes
If you would like to use your own custom MyCustomQuote
class, to avoid needing to transpose into the library Quote
class, you only need to add the IQuote
interface.
using Skender.Stock.Indicators;
[..]
public class MyCustomQuote : IQuote
{
// required base properties
public DateTime Date { get; set; }
public decimal Open { get; set; }
public decimal High { get; set; }
public decimal Low { get; set; }
public decimal Close { get; set; }
public decimal Volume { get; set; }
// custom properties
public int MyOtherProperty { get; set; }
}
// fetch historical quotes from your favorite feed
IEnumerable<MyCustomQuote> myQuotes = GetQuotesFromFeed("MSFT");
// example: get 20-period simple moving average
IEnumerable<SmaResult> results = myQuotes.GetSma(20);
Using custom quote property names
If you have a model that has different properties names, but the same meaning, you only need to map them. For example, if your class has a property called CloseDate
instead of Date
, it could be represented like this:
public class MyCustomQuote : IQuote // + ISeries
{
// required base properties
DateTime ISeries.Date => CloseDate;
public decimal Open { get; set; }
public decimal High { get; set; }
public decimal Low { get; set; }
public decimal Close { get; set; }
decimal IQuote.Volume => Vol;
// custom properties
public int MyOtherProperty { get; set; }
public DateTime CloseDate { get; set; }
public decimal Vol { get; set; }
}
Note the use of explicit interface (property declaration is ISeries.Date
), this is because having two properties that expose the same information can be confusing, this way Date
property is only accessible when working with the included Quote
type, while if you are working with a MyCustomQuote
the Date
property will be hidden, avoiding confusion.
For more information on explicit interfaces, refer to the C# Programming Guide.
Using custom results classes
The indicator result classes can be customized in your code. There are many ways to do this, but the benefit of using derived ResultBase
is that your custom class will inherit all of the utility results extension methods. Here’s one example:
// your custom class with an EMA profile
public class MyEma : ResultBase
{
// my properties
public int MyId { get; set; }
public double? Ema { get; set; }
}
public void MyClass(){
// fetch historical quotes from your feed (your method)
IEnumerable<Quote> quotes = GetQuotesFromFeed("SPY");
// compute indicator
INumerable<EmaResult> emaResults = quotes.GetEma(14);
// convert to my Ema class list [using LINQ]
List<MyEma> myEmaResults = emaResults
.Select(e => new MyEma
{
MyId = 123,
Date = e.Date,
Ema = e.Ema
})
.ToList();
// randomly selecting first record from the
// collection here for the example
MyEma r = myEmaResults.FirstOrDefault();
// use your custom quote data
Console.WriteLine($"On {r.Date}, EMA was {r.Ema} for my EMA ID {r.MyId}.");
}
Generating indicator of indicators
If you want to compute an indicator of indicators, such as an SMA of an ADX or an RSI of an OBV, use chaining to calculate an indicator from prior results. Example:
// fetch historical quotes from your feed (your method)
IEnumerable<Quote> quotes = GetQuotesFromFeed("SPY");
// calculate RSI of OBV
IEnumerable<RsiResult> results
= quotes
.GetObv()
.GetRsi(14);
// or with two separate operations
IEnumerable<ObvResult> obvResults = quotes.GetObv();
IEnumerable<RsiResult> rsiOfObv = obvResults.GetRsi(14);
Candlestick patterns
Candlestick Patterns are a unique form of indicator and have a common output model.
CandleResult
Date
DateTime
- Date
Price
decimal
- Price of the most relevant OHLC candle element when a signal is present
Match
Match
- Indicates a matching signal type for this candlestick pattern
Candle
CandleProperties
- Details of the candle properties
Match
When a candlestick pattern is recognized, it produces a matching signal. In some cases, an intrinsic confirmation is also available after the signal. In cases where previous bars were used to identify a pattern, they are indicated as the basis for the signal. This enum
can also be referenced as an int
value. Documentation for each candlestick pattern will indicate whether confirmation and/or basis information is produced.
type | int | description |
---|---|---|
Match.BullConfirmed | 200 | Confirmation of a prior bull signal |
Match.BullSignal | 100 | Bullish signal |
Match.BullBasis | 10 | Bars supporting a bullish signal |
Match.Neutral | 1 | Signal for non-directional patterns |
Match.None | 0 | No match |
Match.BearBasis | -10 | Bars supporting a bearish signal |
Match.BearSignal | -100 | Bearish signal |
Match.BearConfirmed | -200 | Confirmation of a prior bear signal |
Candle
The CandleProperties
class is an extended version of Quote
, and contains additional calculated properties. TQuote
classes can be converted to CandleProperties
with the .ToCandle()
utility, and further used as the basis for calculating indicators.
CandleProperties
name | type | notes |
---|---|---|
Date | DateTime | Date |
Open | decimal | Open price |
High | decimal | High price |
Low | decimal | Low price |
Close | decimal | Close price |
Volume | decimal | Volume |
Size | decimal | High-Low |
Body | decimal | |Open-Close| |
UpperWick | decimal | Upper wick size |
LowerWick | decimal | Lower wick size |
BodyPct | double | Body/Size |
UpperWickPct | double | UpperWick/Size |
LowerWickPct | double | LowerWick/Size |
IsBullish | bool | Close>Open direction |
IsBearish | bool | Close<Open direction |
Utilities
See Utilities and helper functions for additional tools.