Skip to content

Commit

Permalink
modify Beta, add return values (#744)
Browse files Browse the repository at this point in the history
  • Loading branch information
DaveSkender authored Mar 13, 2022
1 parent 57cd86d commit 66ab9ed
Show file tree
Hide file tree
Showing 9 changed files with 16,387 additions and 80 deletions.
14 changes: 9 additions & 5 deletions docs/_indicators/Beta.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ layout: indicator

# {{ page.title }}

[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.
[Beta](https://en.wikipedia.org/wiki/Beta_(finance)) shows how strongly one asset's price 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)
Expand All @@ -25,7 +25,7 @@ IEnumerable<BetaResult> results =
| -- |-- |--
| `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 +/-.
| `lookbackPeriods` | int | Number of periods (`N`) in the lookback window. 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
Expand Down Expand Up @@ -62,6 +62,10 @@ IEnumerable<BetaResult>
| `BetaDown` | double | Beta- (Down Beta)
| `Ratio` | double | Beta ratio is `BetaUp/BetaDown`
| `Convexity` | double | Beta convexity is <code>(BetaUp-BetaDown)<sup>2</sup></code>
| `ReturnsEval` | double | Returns of evaluated quotes (`R`)
| `ReturnsMrkt` | double | Returns of market quotes (`Rm`)

**Tip:** [Alpha](https://en.wikipedia.org/wiki/Alpha_(finance)) is calculated as `R – Rf – Beta (Rm - Rf)`, where `Rf` is the risk-free rate.

### Utilities

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

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

// calculate 20-period Beta coefficient
IEnumerable<BetaResult> results =
Indicator.GetBeta(historySPX,historyTSLA,20);
Indicator.GetBeta(quotesSPX,quotesTSLA,20);
```
2 changes: 2 additions & 0 deletions src/a-d/Beta/Beta.Models.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ public class BetaResult : ResultBase
public double? BetaDown { get; set; }
public double? Ratio { get; set; }
public double? Convexity { get; set; }
public double? ReturnsEval { get; set; }
public double? ReturnsMrkt { get; set; }
}

public enum BetaType
Expand Down
98 changes: 60 additions & 38 deletions src/a-d/Beta/Beta.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,56 +13,80 @@ public static IEnumerable<BetaResult> GetBeta<TQuote>(
where TQuote : IQuote
{
// convert quotes
List<BasicD> bdListEval = quotesEval.ConvertToBasic(CandlePart.Close);
List<BasicD> bdListMrkt = quotesMarket.ConvertToBasic(CandlePart.Close);
List<TQuote>? evalQuotesList = quotesEval.SortToList();
List<TQuote>? mrktQuotesList = quotesMarket.SortToList();

// check parameter arguments
ValidateBeta(quotesMarket, quotesEval, lookbackPeriods);
ValidateBeta(mrktQuotesList, evalQuotesList, lookbackPeriods);

// initialize
List<BetaResult> results = new(bdListEval.Count);
int size = evalQuotesList.Count;
List<BetaResult> results = new(size);

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;

// convert quotes to returns
double[] evalReturns = new double[size];
double[] mrktReturns = new double[size];
decimal? prevE = 0;
decimal? prevM = 0;

for (int i = 0; i < size; i++)
{
TQuote? e = evalQuotesList[i];
TQuote? m = mrktQuotesList[i];

if (e.Date != m.Date)
{
throw new InvalidQuotesException(nameof(quotesEval), e.Date,
"Date sequence does not match. Beta requires matching dates in provided quotes.");
}

evalReturns[i] = (double)(prevE != 0 ? (e.Close / prevE) - 1m : 0);
mrktReturns[i] = (double)(prevM != 0 ? (m.Close / prevM) - 1m : 0);

prevE = e.Close;
prevM = m.Close;
}

// roll through quotes
for (int i = 0; i < bdListEval.Count; i++)
for (int i = 0; i < size; i++)
{
BasicD e = bdListEval[i];
TQuote? q = evalQuotesList[i];

BetaResult r = new()
{
Date = e.Date
Date = q.Date,
ReturnsEval = evalReturns[i],
ReturnsMrkt = mrktReturns[i]
};
results.Add(r);

// skip warmup periods
if (i < lookbackPeriods - 1)
if (i < lookbackPeriods)
{
continue;
}

// calculate standard beta
// calculate beta variants
if (calcSd)
{
r.CalcBeta(
i, lookbackPeriods, bdListMrkt, bdListEval, BetaType.Standard);
i, lookbackPeriods, mrktReturns, evalReturns, BetaType.Standard);
}

// calculate up/down betas
if (i >= lookbackPeriods)
if (calcDn)
{
if (calcDn)
{
r.CalcBeta(
i, lookbackPeriods, bdListMrkt, bdListEval, BetaType.Down);
}
r.CalcBeta(
i, lookbackPeriods, mrktReturns, evalReturns, BetaType.Down);
}

if (calcUp)
{
r.CalcBeta(
i, lookbackPeriods, bdListMrkt, bdListEval, BetaType.Up);
}
if (calcUp)
{
r.CalcBeta(
i, lookbackPeriods, mrktReturns, evalReturns, BetaType.Up);
}

// ratio and convexity
Expand All @@ -79,10 +103,10 @@ public static IEnumerable<BetaResult> GetBeta<TQuote>(
// calculate beta
private static void CalcBeta(
this BetaResult r,
int index,
int i,
int lookbackPeriods,
List<BasicD> bdListMrkt,
List<BasicD> bdListEval,
double[] mrktReturns,
double[] evalReturns,
BetaType type)
{
// note: BetaType.All is ineligible for this method
Expand All @@ -93,24 +117,22 @@ private static void CalcBeta(
List<double> dataA = new(lookbackPeriods);
List<double> dataB = new(lookbackPeriods);

for (int p = index - lookbackPeriods + 1; p <= index; p++)
for (int p = i - lookbackPeriods + 1; p <= i; p++)
{
double a = bdListMrkt[p].Value;
double b = bdListEval[p].Value;
double a = mrktReturns[p];
double b = evalReturns[p];

if (type is BetaType.Standard)
{
dataA.Add(a);
dataB.Add(b);
}
else if (type is BetaType.Down
&& a < bdListMrkt[p - 1].Value)
else if (type is BetaType.Down && a < 0)
{
dataA.Add(a);
dataB.Add(b);
}
else if (type is BetaType.Up
&& a > bdListMrkt[p - 1].Value)
else if (type is BetaType.Up && a > 0)
{
dataA.Add(a);
dataB.Add(b);
Expand Down Expand Up @@ -145,10 +167,10 @@ private static void CalcBeta(

// parameter validation
private static void ValidateBeta<TQuote>(
IEnumerable<TQuote> quotesMarket,
IEnumerable<TQuote> quotesEval,
int lookbackPeriods)
where TQuote : IQuote
List<TQuote> quotesMarket,
List<TQuote> quotesEval,
int lookbackPeriods)
where TQuote : IQuote
{
// check parameter arguments
if (lookbackPeriods <= 0)
Expand All @@ -158,11 +180,11 @@ private static void ValidateBeta<TQuote>(
}

// check quotes
if (quotesEval.Count() < quotesMarket.Count())
if (quotesEval.Count != quotesMarket.Count)
{
throw new InvalidQuotesException(
nameof(quotesEval),
"Eval quotes should have at least as many records as Market quotes for Beta.");
"Eval quotes should have the same number of Market quotes for Beta.");
}
}
}
33 changes: 20 additions & 13 deletions tests/indicators/Tests.Indicators.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,6 @@
</ItemGroup>

<ItemGroup>
<None Update="a-d\Dpo\Dpo.Data.csv">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="s-z\ZigZag\data.ethusdt.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="s-z\ZigZag\data.issue632.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="s-z\ZigZag\data.schrodinger.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="_common\data\bad.csv">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
Expand Down Expand Up @@ -71,15 +59,34 @@
<None Update="_common\data\mismatch.csv">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="_common\data\msft.csv">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="_common\data\penny.csv">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="s-z\ZigZag\zigzag.csv">
<None Update="_common\data\spx.csv">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="_common\data\toobig.csv">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>

<None Update="a-d\Dpo\Dpo.Data.csv">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="s-z\ZigZag\data.ethusdt.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="s-z\ZigZag\data.issue632.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="s-z\ZigZag\data.schrodinger.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="s-z\ZigZag\zigzag.csv">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
22 changes: 22 additions & 0 deletions tests/indicators/_common/Helper.Importer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -179,4 +179,26 @@ internal static IEnumerable<Quote> GetMismatch()
.Select(v => Importer.QuoteFromCsv(v))
.ToList();
}

// SPX, 30 years, daily
internal static IEnumerable<Quote> GetSpx(int days = 8111)
{
return File.ReadAllLines("_common/data/spx.csv")
.Skip(1)
.Select(v => Importer.QuoteFromCsv(v))
.OrderByDescending(x => x.Date)
.Take(days)
.ToList();
}

// MSFT, 30 years, daily
internal static IEnumerable<Quote> GetMsft(int days = 8111)
{
return File.ReadAllLines("_common/data/msft.csv")
.Skip(1)
.Select(v => Importer.QuoteFromCsv(v))
.OrderByDescending(x => x.Date)
.Take(days)
.ToList();
}
}
Loading

0 comments on commit 66ab9ed

Please sign in to comment.