Skip to content

Commit

Permalink
chore: refactor Exceptions and unit tests (#40)
Browse files Browse the repository at this point in the history
* refactor and add Exceptions tests to improve code coverage
  • Loading branch information
DaveSkender authored Jun 13, 2020
1 parent cde0f8c commit 0ea9d7d
Show file tree
Hide file tree
Showing 69 changed files with 654 additions and 283 deletions.
8 changes: 2 additions & 6 deletions Indicators/Aroon/Aroon.Models.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
using System;

namespace Skender.Stock.Indicators
namespace Skender.Stock.Indicators
{

public class AroonResult
public class AroonResult : ResultBase
{
public int Index { get; set; }
public DateTime Date { get; set; }
public decimal? AroonUp { get; set; }
public decimal? AroonDown { get; set; }
public decimal? Oscillator { get; set; }
Expand Down
33 changes: 20 additions & 13 deletions Indicators/Aroon/Aroon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,8 @@ public static IEnumerable<AroonResult> GetAroon(IEnumerable<Quote> history, int
// clean quotes
history = Cleaners.PrepareHistory(history);

// exceptions
if (lookbackPeriod <= 0)
{
throw new BadParameterException("Lookback period must be greater than 0 for Aroon.");
}

int qtyHistory = history.Count();
int minHistory = lookbackPeriod;
if (qtyHistory < minHistory)
{
throw new BadHistoryException("Insufficient history provided for Aroon. " +
string.Format("You provided {0} periods of history when {1} is required.", qtyHistory, minHistory));
}
// validate parameters
ValidateAroon(history, lookbackPeriod);

// initialize
List<AroonResult> results = new List<AroonResult>();
Expand Down Expand Up @@ -71,6 +60,24 @@ public static IEnumerable<AroonResult> GetAroon(IEnumerable<Quote> history, int
return results;
}


private static void ValidateAroon(IEnumerable<Quote> history, int lookbackPeriod)
{
// check parameters
if (lookbackPeriod <= 0)
{
throw new BadParameterException("Lookback period must be greater than 0 for Aroon.");
}

// checked history
int qtyHistory = history.Count();
int minHistory = lookbackPeriod;
if (qtyHistory < minHistory)
{
throw new BadHistoryException("Insufficient history provided for Aroon. " +
string.Format("You provided {0} periods of history when at least {1} is required.", qtyHistory, minHistory));
}
}
}

}
2 changes: 1 addition & 1 deletion Indicators/Aroon/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ IEnumerable<AroonResult> results = Indicator.GetAroon(history, lookbackPeriod);
| 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 `N` periods worth of `history`.
| `lookbackPeriod` | int | Number of periods (`N`) for the lookback evaluation. Default is 25.
| `lookbackPeriod` | int | Number of periods (`N`) for the lookback evaluation. Must be greater than 0. Default is 25.

## Response

Expand Down
8 changes: 2 additions & 6 deletions Indicators/AvgDirectional/Adx.Models.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
using System;

namespace Skender.Stock.Indicators
namespace Skender.Stock.Indicators
{

public class AdxResult
public class AdxResult : ResultBase
{
public int Index { get; set; }
public DateTime Date { get; set; }
public decimal? Pdi { get; set; }
public decimal? Mdi { get; set; }
public decimal? Adx { get; set; }
Expand Down
34 changes: 23 additions & 11 deletions Indicators/AvgDirectional/Adx.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,8 @@ public static IEnumerable<AdxResult> GetAdx(IEnumerable<Quote> history, int look
// clean quotes
history = Cleaners.PrepareHistory(history);

// check exceptions
int qtyHistory = history.Count();
int minHistory = 2 * lookbackPeriod + 1;
if (qtyHistory < minHistory)
{
throw new BadHistoryException("Insufficient history provided for ADX. " +
string.Format("You provided {0} periods of history when {1} is required. "
+ "Since this uses a smoothing technique, "
+ "we recommend you use at least 250 data points prior to the intended "
+ "usage date for maximum precision.", qtyHistory, minHistory));
}
// verify parameters
ValidateAdx(history, lookbackPeriod);

// initialize results and working variables
IEnumerable<AtrResult> atrResults = GetAtr(history, lookbackPeriod); // uses True Range value
Expand Down Expand Up @@ -146,5 +137,26 @@ public static IEnumerable<AdxResult> GetAdx(IEnumerable<Quote> history, int look
return results;
}


private static void ValidateAdx(IEnumerable<Quote> history, int lookbackPeriod)
{
// check parameters
if (lookbackPeriod <= 1)
{
throw new BadParameterException("Lookback period must be greater than 1 for ADX.");
}

// check history
int qtyHistory = history.Count();
int minHistory = 2 * lookbackPeriod + 1;
if (qtyHistory < minHistory)
{
throw new BadHistoryException("Insufficient history provided for ADX. " +
string.Format("You provided {0} periods of history when at least {1} is required. "
+ "Since this uses a smoothing technique, "
+ "we recommend you use at least 250 data points prior to the intended "
+ "usage date for maximum precision.", qtyHistory, minHistory));
}
}
}
}
2 changes: 1 addition & 1 deletion Indicators/AvgDirectional/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ IEnumerable<AdxResult> results = Indicator.GetAdx(history, lookbackPeriod);
| 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 2×`N`+1 periods of `history` to get any results; however, since this uses a smoothing technique, we recommend you use at least 250 data points prior to the intended usage date for maximum precision.
| `lookbackPeriod` | int | Number of periods (`N`) to consider. Default is 14.
| `lookbackPeriod` | int | Number of periods (`N`) to consider. Must be greater than 1. Default is 14.

## Response

Expand Down
8 changes: 2 additions & 6 deletions Indicators/AvgTrueRange/Atr.Models.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
using System;

namespace Skender.Stock.Indicators
namespace Skender.Stock.Indicators
{

public class AtrResult
public class AtrResult : ResultBase
{
public int Index { get; set; }
public DateTime Date { get; set; }
public decimal? Tr { get; set; }
public decimal? Atr { get; set; }
public decimal? Atrp { get; set; }
Expand Down
2 changes: 1 addition & 1 deletion Indicators/AvgTrueRange/Atr.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ private static void ValidateAtr(IEnumerable<Quote> history, int lookbackPeriod)
if (qtyHistory < minHistory)
{
throw new BadHistoryException("Insufficient history provided for ATR. " +
string.Format("You provided {0} periods of history when {1} is required. "
string.Format("You provided {0} periods of history when at least {1} is required. "
, qtyHistory, minHistory));
}
}
Expand Down
8 changes: 2 additions & 6 deletions Indicators/Beta/Beta.Models.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
using System;

namespace Skender.Stock.Indicators
namespace Skender.Stock.Indicators
{

public class BetaResult
public class BetaResult : ResultBase
{
public int Index { get; set; }
public DateTime Date { get; set; }
public decimal? Beta { get; set; }
}

Expand Down
38 changes: 29 additions & 9 deletions Indicators/Beta/Beta.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,8 @@ public static IEnumerable<BetaResult> GetBeta(
historyMarket = Cleaners.PrepareHistory(historyMarket);
historyEval = Cleaners.PrepareHistory(historyEval);

// check exceptions
int qtyHistory = historyEval.Count();
int minHistory = lookbackPeriod;
if (qtyHistory < minHistory)
{
throw new BadHistoryException("Insufficient history provided for Beta. " +
string.Format("You provided {0} periods of history when {1} is required."
, qtyHistory, minHistory));
}
// validate parameters
ValidateBeta(historyMarket, historyEval, lookbackPeriod);

// initialize results
List<BetaResult> results = new List<BetaResult>();
Expand Down Expand Up @@ -53,6 +46,33 @@ public static IEnumerable<BetaResult> GetBeta(
return results;
}


private static void ValidateBeta(IEnumerable<Quote> historyMarket, IEnumerable<Quote> historyEval, int lookbackPeriod)
{

// check parameters
if (lookbackPeriod <= 0)
{
throw new BadParameterException("Lookback period must be greater than 0 for Beta.");
}

// check history
int qtyHistoryMarket = historyMarket.Count();
int minHistoryMarket = lookbackPeriod;
if (qtyHistoryMarket < minHistoryMarket)
{
throw new BadHistoryException("Insufficient history provided for Beta. " +
string.Format("You provided {0} periods of history when at least {1} is required.", qtyHistoryMarket, minHistoryMarket));
}

int qtyHistoryEval = historyEval.Count();
if (qtyHistoryEval < qtyHistoryMarket)
{
throw new BadHistoryException(
"Eval history should have at least as many records as Market history for Beta.");
}

}
}

}
2 changes: 1 addition & 1 deletion Indicators/Beta/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ IEnumerable<BetaResult> results = Indicator.GetBeta(historyMarket, historyEval,
| -- |-- |--
| `historyMarket` | IEnumerable\<[Quote](/GUIDE.md#Quote)\> | Historical [market] Quotes data should be at any consistent frequency (day, hour, minute, etc). You must supply at least `N` periods of history. This `market` history will be used to establish the baseline.
| `historyEval` | IEnumerable\<[Quote](/GUIDE.md#Quote)\> | Historical [evaluation stock] Quotes data should be at any consistent frequency (day, hour, minute, etc). You must have at least the same matching date elements of `historyMarket`. Exception will be thrown if not matched.
| `lookbackPeriod` | int | Number of periods (`N`) in the lookback period.
| `lookbackPeriod` | 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.

## Response

Expand Down
8 changes: 2 additions & 6 deletions Indicators/BollingerBands/BollingerBands.Models.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
using System;

namespace Skender.Stock.Indicators
namespace Skender.Stock.Indicators
{

public class BollingerBandsResult
public class BollingerBandsResult : ResultBase
{
public int Index { get; set; }
public DateTime Date { get; set; }
public decimal? Sma { get; set; }
public decimal? UpperBand { get; set; }
public decimal? LowerBand { get; set; }
Expand Down
4 changes: 2 additions & 2 deletions Indicators/BollingerBands/BollingerBands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public static IEnumerable<BollingerBandsResult> GetBollingerBands(

if (prevUpperBand != null && prevLowerBand != null)
{
result.IsDiverging = ((decimal)result.UpperBand - (decimal)result.LowerBand)
result.IsDiverging = ((decimal)result.UpperBand - (decimal)result.LowerBand)
> ((decimal)prevUpperBand - (decimal)prevLowerBand);
}

Expand Down Expand Up @@ -84,7 +84,7 @@ private static void ValidateBollingerBands(
if (qtyHistory < minHistory)
{
throw new BadHistoryException("Insufficient history provided for Bollinger Bands. " +
string.Format("You provided {0} periods of history when {1} is required.", qtyHistory, minHistory));
string.Format("You provided {0} periods of history when at least {1} is required.", qtyHistory, minHistory));
}
}

Expand Down
8 changes: 2 additions & 6 deletions Indicators/Cci/Cci.Models.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
using System;

namespace Skender.Stock.Indicators
namespace Skender.Stock.Indicators
{

public class CciResult
public class CciResult : ResultBase
{
public int Index { get; set; }
public DateTime Date { get; set; }
internal decimal? Tp { get; set; }
public decimal? Cci { get; set; }
}
Expand Down
30 changes: 22 additions & 8 deletions Indicators/Cci/Cci.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,8 @@ public static IEnumerable<CciResult> GetCci(IEnumerable<Quote> history, int look
// clean quotes
history = Cleaners.PrepareHistory(history);

// check exceptions
int qtyHistory = history.Count();
int minHistory = lookbackPeriod + 1;
if (qtyHistory < minHistory)
{
throw new BadHistoryException("Insufficient history provided for Commodity Channel Index. " +
string.Format("You provided {0} periods of history when {1} is required.", qtyHistory, minHistory));
}
// validate parameters
ValidateCci(history, lookbackPeriod);

// initialize
List<CciResult> results = new List<CciResult>();
Expand Down Expand Up @@ -59,5 +53,25 @@ public static IEnumerable<CciResult> GetCci(IEnumerable<Quote> history, int look
return results;
}


private static void ValidateCci(IEnumerable<Quote> history, int lookbackPeriod)
{

// check parameters
if (lookbackPeriod <= 0)
{
throw new BadParameterException("Lookback period must be greater than 0 for Commodity Channel Index.");
}


// check history
int qtyHistory = history.Count();
int minHistory = lookbackPeriod + 1;
if (qtyHistory < minHistory)
{
throw new BadHistoryException("Insufficient history provided for Commodity Channel Index. " +
string.Format("You provided {0} periods of history when at least {1} is required.", qtyHistory, minHistory));
}
}
}
}
2 changes: 1 addition & 1 deletion Indicators/Cci/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ IEnumerable<CciResult> results = Indicator.GetCci(history, lookbackPeriod);
| 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+1` periods of `history`.
| `lookbackPeriod` | int | Number of periods (`N`) in the moving average. Default is 20.
| `lookbackPeriod` | int | Number of periods (`N`) in the moving average. Must be greater than 0. Default is 20.

## Response

Expand Down
8 changes: 2 additions & 6 deletions Indicators/Chandelier/Chandelier.Models.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
using System;

namespace Skender.Stock.Indicators
namespace Skender.Stock.Indicators
{

public class ChandelierResult
public class ChandelierResult : ResultBase
{
public int Index { get; set; }
public DateTime Date { get; set; }
public decimal? ChandelierExit { get; set; }
public bool? IsExitCross { get; set; }
public bool? IsCrossed { get; set; }
Expand Down
7 changes: 5 additions & 2 deletions Indicators/Chandelier/Chandelier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Skender.Stock.Indicators
{
public static partial class Indicator
{
// CHANDELIER OVERLAY
// CHANDELIER EXIT
public static IEnumerable<ChandelierResult> GetChandelier(
IEnumerable<Quote> history, int lookbackPeriod = 22, double multiplier = 3.0, string variant = "long")
{
Expand Down Expand Up @@ -75,6 +75,8 @@ public static IEnumerable<ChandelierResult> GetChandelier(
private static void ValidateChandelier(
IEnumerable<Quote> history, int lookbackPeriod, double multiplier, string variant)
{

// check parameters
if (lookbackPeriod <= 0)
{
throw new BadParameterException("Lookback period must be greater than 0 for Chandelier Exit.");
Expand All @@ -90,12 +92,13 @@ private static void ValidateChandelier(
throw new BadParameterException("Variant must be either 'long' or 'short' Chandelier Exit.");
}

// check history
int qtyHistory = history.Count();
int minHistory = lookbackPeriod + 1;
if (qtyHistory < minHistory)
{
throw new BadHistoryException("Insufficient history provided for Chandelier Exit. " +
string.Format("You provided {0} periods of history when {1} is required.", qtyHistory, minHistory));
string.Format("You provided {0} periods of history when at least {1} is required.", qtyHistory, minHistory));
}
}
}
Expand Down
Loading

0 comments on commit 0ea9d7d

Please sign in to comment.