Skip to content

Commit

Permalink
add Beta+/Beta- to Beta (#597)
Browse files Browse the repository at this point in the history
  • Loading branch information
DaveSkender authored Nov 10, 2021
1 parent 24d2fcd commit cf7fe62
Show file tree
Hide file tree
Showing 16 changed files with 386 additions and 102 deletions.
28 changes: 21 additions & 7 deletions docs/_indicators/Beta.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,38 @@ layout: default

# {{ page.title }}

[Beta](https://en.wikipedia.org/wiki/Beta_(finance)) shows how strongly one stock responds to systemic volatility of the entire market.
[Beta](https://en.wikipedia.org/wiki/Beta_(finance)) shows how strongly one stock responds to systemic volatility of the entire market. [Upside Beta](https://en.wikipedia.org/wiki/Upside_beta) (Beta+) and [Downside Beta](https://en.wikipedia.org/wiki/Downside_beta) (Beta-), [popularized by Harry M. Markowitz](https://www.jstor.org/stable/j.ctt1bh4c8h), are also included.
[[Discuss] :speech_balloon:]({{site.github.repository_url}}/discussions/268 "Community discussion about this indicator")

![image]({{site.baseurl}}/assets/charts/Beta.png)

```csharp
// usage
IEnumerable<BetaResult> results =
Indicator.GetBeta(historyMarket, historyEval, lookbackPeriods);
Indicator.GetBeta(quotesMarket, quotesEval, lookbackPeriods, type);
```

## Parameters

| name | type | notes
| -- |-- |--
| `historyMarket` | IEnumerable\<[TQuote]({{site.baseurl}}/guide/#historical-quotes)\> | Historical [market] Quotes data should be at any consistent frequency (day, hour, minute, etc). This `market` quotes will be used to establish the baseline.
| `historyEval` | IEnumerable\<[TQuote]({{site.baseurl}}/guide/#historical-quotes)\> | Historical [evaluation stock] Quotes data should be at any consistent frequency (day, hour, minute, etc).
| `lookbackPeriods` | int | Number of periods (`N`) in the lookback period. Must be greater than 0 to calculate; however we suggest a larger period for statistically appropriate sample size.
| `quotesMarket` | IEnumerable\<[TQuote]({{site.baseurl}}/guide/#historical-quotes)\> | Historical [market] Quotes data should be at any consistent frequency (day, hour, minute, etc). This `market` quotes will be used to establish the baseline.
| `quotesEval` | IEnumerable\<[TQuote]({{site.baseurl}}/guide/#historical-quotes)\> | Historical [evaluation stock] Quotes data should be at any consistent frequency (day, hour, minute, etc).
| `lookbackPeriods` | int | Number of periods (`N`) in the lookback period. Must be greater than 0 to calculate; however we suggest a larger period for statistically appropriate sample size and especially when using Beta +/-.
| `type` | BetaType | Type of Beta to calculate. Default is `BetaType.Standard`. See [BetaType options](#betatype-options) below.

### Historical quotes requirements

You must have at least `N` periods of quotes. You must have at least the same matching date elements of `historyMarket`. Exception will be thrown if not matched. Historical price quotes should have a consistent frequency (day, hour, minute, etc). See [the Guide]({{site.baseurl}}/guide/#historical-quotes) for more information.
You must have at least `N` periods of quotes. You must have at least the same matching date elements of `quotesMarket`. Exception will be thrown if not matched. Historical price quotes should have a consistent frequency (day, hour, minute, etc). See [the Guide]({{site.baseurl}}/guide/#historical-quotes) for more information.

#### BetaType options

| type | description
|-- |--
| `Standard` | Standard Beta only. Uses all historical quotes.
| `Up` | Upside Beta only. Uses historical quotes from market up bars only.
| `Down` | Downside Beta only. Uses historical quotes from market down bars only.
| `All` | Returns all of the above. Use this option if you want `Ratio` and `Convexity` values returned. Note: 3× slower to calculate.

## Response

Expand All @@ -46,6 +56,10 @@ IEnumerable<BetaResult>
| -- |-- |--
| `Date` | DateTime | Date
| `Beta` | decimal | Beta coefficient based on `N` lookback periods
| `BetaUp` | decimal | Beta+ (Up Beta)
| `BetaDown` | decimal | Beta- (Down Beta)
| `Ratio` | decimal | Beta ratio is `BetaUp/BetaDown`
| `Convexity` | decimal | Beta convexity is <code>(BetaUp-BetaDown)<sup>2</sup></code>

### Utilities

Expand All @@ -59,8 +73,8 @@ See [Utilities and Helpers]({{site.baseurl}}/utilities#utilities-for-indicator-r

```csharp
// fetch historical quotes from your feed (your method)
IEnumerable<Quote> historyTSLA = GetHistoryFromFeed("TSLA");
IEnumerable<Quote> historySPX = GetHistoryFromFeed("SPX");
IEnumerable<Quote> historyTSLA = GetHistoryFromFeed("TSLA");

// calculate 20-period Beta coefficient
IEnumerable<BetaResult> results =
Expand Down
6 changes: 3 additions & 3 deletions docs/_indicators/Correlation.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,21 @@ layout: default
```csharp
// usage
IEnumerable<CorrResult> results =
historyA.GetCorr(historyB, lookbackPeriods);
quotesA.GetCorr(quotesB, lookbackPeriods);
```

## Parameters

| name | type | notes
| -- |-- |--
| `historyB` | IEnumerable\<[TQuote]({{site.baseurl}}/guide/#historical-quotes)\> | Historical quotes (B) must have at least the same matching date elements of `historyA`.
| `quotesB` | IEnumerable\<[TQuote]({{site.baseurl}}/guide/#historical-quotes)\> | Historical quotes (B) must have at least the same matching date elements of `quotesA`.
| `lookbackPeriods` | int | Number of periods (`N`) in the lookback period. Must be greater than 0 to calculate; however we suggest a larger period for statistically appropriate sample size.

### Historical quotes requirements

You must have at least `N` periods for both versions of `quotes`. Mismatch histories will produce a `BadQuotesException`. Historical price quotes should have a consistent frequency (day, hour, minute, etc).

`historyA` is an `IEnumerable<TQuote>` collection of historical price quotes. It should have a consistent frequency (day, hour, minute, etc). See [the Guide]({{site.baseurl}}/guide/#historical-quotes) for more information.
`quotesA` is an `IEnumerable<TQuote>` collection of historical price quotes. It should have a consistent frequency (day, hour, minute, etc). See [the Guide]({{site.baseurl}}/guide/#historical-quotes) for more information.

## Response

Expand Down
Binary file modified docs/assets/charts/Beta.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/Indicators.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
Relative;Strength;Index;RSI;ConnorsRSI;Stochastic;Directional;Movement;ADX;Donchian;Keltner;Channel;
Heikin-Ashi;Hilbert;Transform;Instantaneous;Trendline;Hull;HMA;Ichimoku;Cloud;On-balance;Volume;OBV;
Price;PMO;PRS;Parabolic;SAR;Stop;Reverse;PSAR;Pivot;Points;Rate;Change;ROC;Linear;Regression;R-Squared;Renko;
Schaff;Cycle;Slope;Standard;Deviation;Beta;Correlation;SuperTrend;Ulcer;Ultimate;Vortex;Williams;%R;
Schaff;Cycle;Slope;Standard;Deviation;Beta;Convexity;Correlation;SuperTrend;Ulcer;Ultimate;Vortex;Williams;%R;
Alligator;Gator;Fractal;Chaos;Choppiness;Endpoint;WMA;ZigZag;.NET;C#;Library;Package;
</PackageTags>
<PackageReadmeFile>README.md</PackageReadmeFile>
Expand Down
11 changes: 2 additions & 9 deletions src/_common/Enums.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace Skender.Stock.Indicators
{
// ENUMERATIONS
// SHARED ENUMERATIONS
// note: indicator unique ENUMS specified in indicator models

public enum CandlePart
{
Expand All @@ -17,14 +18,6 @@ public enum EndType
HighLow = 1
}

public enum PivotTrend
{
HH, // higher high
LH, // lower high
HL, // higher low
LL // lower low
}

public enum MaType
{
ALMA,
Expand Down
12 changes: 12 additions & 0 deletions src/a-d/Beta/Beta.Models.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,17 @@ namespace Skender.Stock.Indicators
public class BetaResult : ResultBase
{
public decimal? Beta { get; set; }
public decimal? BetaUp { get; set; }
public decimal? BetaDown { get; set; }
public decimal? Ratio { get; set; }
public decimal? Convexity { get; set; }
}

public enum BetaType
{
Standard,
Up,
Down,
All
}
}
142 changes: 123 additions & 19 deletions src/a-d/Beta/Beta.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,41 +10,72 @@ public static partial class Indicator
/// <include file='./info.xml' path='indicator/*' />
///
public static IEnumerable<BetaResult> GetBeta<TQuote>(
IEnumerable<TQuote> historyMarket,
IEnumerable<TQuote> historyEval,
int lookbackPeriods)
IEnumerable<TQuote> quotesMarket,
IEnumerable<TQuote> quotesEval,
int lookbackPeriods,
BetaType type = BetaType.Standard)
where TQuote : IQuote
{

// sort quotes
List<TQuote> historyEvalList = historyEval.Sort();
List<TQuote> quotesListEval = quotesEval.Sort();
List<TQuote> quotesListMrkt = quotesMarket.Sort();

// check parameter arguments
ValidateBeta(historyMarket, historyEval, lookbackPeriods);
ValidateBeta(quotesMarket, quotesEval, lookbackPeriods);

// initialize
List<BetaResult> results = new(historyEvalList.Count);
List<CorrResult> correlation = GetCorrelation(historyMarket, historyEval, lookbackPeriods).ToList();
List<BetaResult> results = new(quotesListEval.Count);
bool calcSd = type is BetaType.All or BetaType.Standard;
bool calcUp = type is BetaType.All or BetaType.Up;
bool calcDn = type is BetaType.All or BetaType.Down;

// roll through quotes
for (int i = 0; i < historyEvalList.Count; i++)
for (int i = 0; i < quotesListEval.Count; i++)
{
TQuote e = historyEvalList[i];
TQuote e = quotesListEval[i];

BetaResult result = new()
BetaResult r = new()
{
Date = e.Date
};
results.Add(r);

// calculate beta, if available
CorrResult c = correlation[i];
// skip warmup periods
if (i < lookbackPeriods - 1)
{
continue;
}

if (c.Covariance != null && c.VarianceA != null && c.VarianceA != 0)
// calculate standard beta
if (calcSd)
{
result.Beta = c.Covariance / c.VarianceA;
r.CalcBeta(
i, lookbackPeriods, quotesListMrkt, quotesListEval, BetaType.Standard);
}

results.Add(result);
// calculate up/down betas
if (i >= lookbackPeriods)
{
if (calcDn)
{
r.CalcBeta(
i, lookbackPeriods, quotesListMrkt, quotesListEval, BetaType.Down);
}

if (calcUp)
{
r.CalcBeta(
i, lookbackPeriods, quotesListMrkt, quotesListEval, BetaType.Up);
}
}

// ratio and convexity
if (type == BetaType.All && r.BetaUp != null && r.BetaDown != null)
{
r.Ratio = (r.BetaDown != 0) ? r.BetaUp / r.BetaDown : null;
r.Convexity = (r.BetaUp - r.BetaDown) * (r.BetaUp - r.BetaDown);
}
}

return results;
Expand All @@ -65,12 +96,84 @@ public static IEnumerable<BetaResult> RemoveWarmupPeriods(
}


// calculate beta
private static void CalcBeta<TQuote>(
this BetaResult r,
int index,
int lookbackPeriods,
List<TQuote> quotesListMrkt,
List<TQuote> quotesListEval,
BetaType type)
where TQuote : IQuote
{
// do not supply type==BetaType.All
if (type is BetaType.All)
{
return;
}

// initialize
CorrResult c = new();

List<decimal> dataA = new(lookbackPeriods);
List<decimal> dataB = new(lookbackPeriods);

for (int p = index - lookbackPeriods + 1; p <= index; p++)
{
decimal a = quotesListMrkt[p].Close;
decimal b = quotesListEval[p].Close;

if (type is BetaType.Standard)
{
dataA.Add(a);
dataB.Add(b);
}
else if (type is BetaType.Down
&& a < quotesListMrkt[p - 1].Close)
{
dataA.Add(a);
dataB.Add(b);
}
else if (type is BetaType.Up
&& a > quotesListMrkt[p - 1].Close)
{
dataA.Add(a);
dataB.Add(b);
}
}

if (dataA.Count > 0)
{
// calculate correlation, covariance, and variance
c.CalcCorrelation(dataA.ToArray(), dataB.ToArray());

// calculate beta
if (c.Covariance != null && c.VarianceA != null && c.VarianceA != 0)
{
decimal? beta = c.Covariance / c.VarianceA;

if (type == BetaType.Standard)
{
r.Beta = beta;
}
else if (type == BetaType.Down)
{
r.BetaDown = beta;
}
else if (type == BetaType.Up)
{
r.BetaUp = beta;
}
}
}
}

// parameter validation
private static void ValidateBeta<TQuote>(
IEnumerable<TQuote> historyMarket,
IEnumerable<TQuote> historyEval,
int lookbackPeriods)
where TQuote : IQuote
IEnumerable<TQuote> historyMarket,
IEnumerable<TQuote> historyEval,
int lookbackPeriods)
where TQuote : IQuote
{

// check parameter arguments
Expand Down Expand Up @@ -103,3 +206,4 @@ private static void ValidateBeta<TQuote>(
}
}
}

7 changes: 4 additions & 3 deletions src/a-d/Beta/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
</para>
</summary>
<typeparam name="TQuote">Configurable Quote type. See Guide for more information.</typeparam>
<param name="historyMarket">Historical price quotes for Market.</param>
<param name="historyEval">Historical price quotes for Evaluation.</param>
<param name="quotesMarket">Historical price quotes for Market.</param>
<param name="quotesEval">Historical price quotes for Evaluation.</param>
<param name="lookbackPeriods">Number of periods in the lookback window.</param>
<param name="type">Type of Beta to calculate.</param>
<returns>Time series of Beta values.</returns>
<exception cref="ArgumentOutOfRangeException">Invalid parameter value provided.</exception>
<exception cref="BadQuotesException">Insufficient quotes provided.</exception>
</indicator>
</indicator>
Loading

0 comments on commit cf7fe62

Please sign in to comment.