-
Notifications
You must be signed in to change notification settings - Fork 246
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
17e019f
commit 17fbbbb
Showing
5 changed files
with
186 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# [Linear] Weighted Moving Average (WMA) | ||
|
||
Weighted moving average is the linear weighted average of `Close` price over `N` lookback periods. This also called Linear Weighted Moving Average (LWMA). | ||
[More info ...](https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/wma) | ||
|
||
```csharp | ||
// usage | ||
IEnumerable<WmaResult> results = Indicator.GetWma(history, lookbackPeriod); | ||
``` | ||
|
||
## Parameters | ||
|
||
| name | type | notes | ||
| -- |-- |-- | ||
| `history` | IEnumerable\<[Quote](../../GUIDE.md#quote)\> | Historical Quotes data should be at any consistent frequency (day, hour, minute, etc). You must supply at least `N` periods of `history`. | ||
| `lookbackPeriod` | int | Number of periods (`N`) in the moving average. Must be greater than 0. | ||
|
||
## Response | ||
|
||
```csharp | ||
IEnumerable<WmaResult> | ||
``` | ||
|
||
The first `N-1` periods will have `null` values since there's not enough data to calculate. We always return the same number of elements as there are in the historical quotes. | ||
|
||
### WmaResult | ||
|
||
| name | type | notes | ||
| -- |-- |-- | ||
| `Index` | int | Sequence of dates | ||
| `Date` | DateTime | Date | ||
| `Wma` | decimal | Weighted moving average for `N` lookback periods | ||
|
||
## Example | ||
|
||
```csharp | ||
// fetch historical quotes from your favorite feed, in Quote format | ||
IEnumerable<Quote> history = GetHistoryFromFeed("MSFT"); | ||
|
||
// calculate 20-period WMA | ||
IEnumerable<WmaResult> results = Indicator.GetWma(history,20); | ||
|
||
// use results as needed | ||
DateTime evalDate = DateTime.Parse("12/31/2018"); | ||
WmaResult result = results.Where(x=>x.Date==evalDate).FirstOrDefault(); | ||
Console.WriteLine("WMA on {0} was ${1}", result.Date, result.Wma); | ||
``` | ||
|
||
```bash | ||
WMA on 12/31/2018 was $235.53 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
using System; | ||
|
||
namespace Skender.Stock.Indicators | ||
{ | ||
[Serializable] | ||
public class WmaResult : ResultBase | ||
{ | ||
public decimal? Wma { get; set; } // weighted moving average | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
|
||
namespace Skender.Stock.Indicators | ||
{ | ||
public static partial class Indicator | ||
{ | ||
// WEIGHTED MOVING AVERAGE | ||
public static IEnumerable<WmaResult> GetWma(IEnumerable<Quote> history, int lookbackPeriod) | ||
{ | ||
|
||
// clean quotes | ||
history = Cleaners.PrepareHistory(history); | ||
|
||
// check parameters | ||
ValidateWma(history, lookbackPeriod); | ||
|
||
// initialize | ||
List<WmaResult> results = new List<WmaResult>(); | ||
decimal divisor = (lookbackPeriod * (lookbackPeriod + 1)) / 2m; | ||
|
||
// roll through history | ||
foreach (Quote h in history) | ||
{ | ||
|
||
WmaResult result = new WmaResult | ||
{ | ||
Index = (int)h.Index, | ||
Date = h.Date | ||
}; | ||
|
||
if (h.Index >= lookbackPeriod) | ||
{ | ||
List<Quote> period = history | ||
.Where(x => x.Index > (h.Index - lookbackPeriod) && x.Index <= h.Index) | ||
.ToList(); | ||
|
||
result.Wma = period | ||
.Select(x => x.Close * (lookbackPeriod - (h.Index - x.Index)) / divisor) | ||
.Sum(); | ||
} | ||
|
||
results.Add(result); | ||
} | ||
|
||
return results; | ||
} | ||
|
||
|
||
private static void ValidateWma(IEnumerable<Quote> history, int lookbackPeriod) | ||
{ | ||
|
||
// check parameters | ||
if (lookbackPeriod <= 0) | ||
{ | ||
throw new BadParameterException("Lookback period must be greater than 0 for WMA."); | ||
} | ||
|
||
// check history | ||
int qtyHistory = history.Count(); | ||
int minHistory = lookbackPeriod; | ||
if (qtyHistory < minHistory) | ||
{ | ||
throw new BadHistoryException("Insufficient history provided for WMA. " + | ||
string.Format(cultureProvider, | ||
"You provided {0} periods of history when at least {1} is required.", | ||
qtyHistory, minHistory)); | ||
} | ||
|
||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
using Microsoft.VisualStudio.TestTools.UnitTesting; | ||
using Skender.Stock.Indicators; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
|
||
namespace StockIndicators.Tests | ||
{ | ||
[TestClass] | ||
public class WmaTests : TestBase | ||
{ | ||
|
||
[TestMethod()] | ||
public void GetWmaTest() | ||
{ | ||
int lookbackPeriod = 20; | ||
IEnumerable<WmaResult> results = Indicator.GetWma(history, lookbackPeriod); | ||
|
||
// assertions | ||
|
||
// proper quantities | ||
// should always be the same number of results as there is history | ||
Assert.AreEqual(502, results.Count()); | ||
Assert.AreEqual(502 - lookbackPeriod + 1, results.Where(x => x.Wma != null).Count()); | ||
|
||
// sample values | ||
WmaResult r1 = results.Where(x => x.Index == 502).FirstOrDefault(); | ||
Assert.AreEqual(246.5110m, Math.Round((decimal)r1.Wma,4)); | ||
|
||
WmaResult r2 = results.Where(x => x.Index == 150).FirstOrDefault(); | ||
Assert.AreEqual(235.5253m, Math.Round((decimal)r2.Wma, 4)); | ||
} | ||
|
||
|
||
/* EXCEPTIONS */ | ||
|
||
[TestMethod()] | ||
[ExpectedException(typeof(BadParameterException), "Bad lookback.")] | ||
public void BadLookback() | ||
{ | ||
Indicator.GetWma(history, 0); | ||
} | ||
|
||
[TestMethod()] | ||
[ExpectedException(typeof(BadHistoryException), "Insufficient history.")] | ||
public void InsufficientHistory() | ||
{ | ||
Indicator.GetWma(history.Where(x => x.Index < 10), 10); | ||
} | ||
} | ||
} |