From c305056e32ac065e1e59811fc4c924411d03a11a Mon Sep 17 00:00:00 2001 From: yilabs Date: Sat, 17 Jun 2023 17:06:23 -0400 Subject: [PATCH] fix input & output array length must > lookback, add unittest --- dub.json | 8 ++++---- source/talibd/talib_func.d | 6 +++--- source/talibd/talib_func.h | 2 +- source/talibd/test.d | 39 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/dub.json b/dub.json index f57a4fe..ec62890 100644 --- a/dub.json +++ b/dub.json @@ -1,11 +1,11 @@ { "authors": [ - "mw" + "mw66" ], - "copyright": "Copyright © 2020, mw", + "copyright": "Copyright © 2020, mw66", "dependencies": { - "fluent-asserts": "~>0.13.3", - "jdiutil": "~>1.1.0" + "fluent-asserts": "*", + "jdiutil": "*" }, "description": "D wrapper for https://ta-lib.org/", "lflags": [ diff --git a/source/talibd/talib_func.d b/source/talibd/talib_func.d index 7fc55db..5cb07e2 100644 --- a/source/talibd/talib_func.d +++ b/source/talibd/talib_func.d @@ -54,7 +54,7 @@ bool TA_MA(double[] inData , double[] outMA , int MA_optInTimePeriod=default_MA_ int begin, num; int lookback = TA_MA_Lookback(MA_optInTimePeriod,optInMAType,); enforce((MA_optInTimePeriod-1) == lookback); - if (lookback > inData.length) { + if (lookback >= inData.length) { return false; } outMA[0..lookback] = 0; @@ -82,7 +82,7 @@ bool TA_RSI(double[] inData , double[] outRSI , int RSI_optInTimePeriod=default_ int begin, num; int lookback = TA_RSI_Lookback(RSI_optInTimePeriod,); enforce(RSI_optInTimePeriod == lookback); - if (lookback > inData.length) { + if (lookback >= inData.length) { return false; } outRSI[0..lookback] = 0; @@ -109,7 +109,7 @@ bool TA_MACD(double[] inData , double[] outMACD, double[] outMACDSignal, double[ int begin, num; int lookback = TA_MACD_Lookback(optInFastPeriod,optInSlowPeriod,optInSignalPeriod,); enforce((optInSlowPeriod+optInSignalPeriod-2) == lookback); - if (lookback > inData.length) { + if (lookback >= inData.length) { return false; } outMACD[0..lookback] = 0; diff --git a/source/talibd/talib_func.h b/source/talibd/talib_func.h index c6e94de..02c89c8 100644 --- a/source/talibd/talib_func.h +++ b/source/talibd/talib_func.h @@ -123,7 +123,7 @@ bool TA_FUNC(double[] inData FOR_EACH(DECL_ARRAY_TYPE, FUNC_OUTS) FOR_EACH(SPLIT int begin, num; ______\ int lookback = TA_FUNC##_Lookback(FOR_EACH(SPLIT_THEN_TAKE_VAR, FUNC_INS)); ______\ enforce(expected_lookback == lookback); ______\ - if (lookback > inData.length) { ______\ + if (lookback >= inData.length) { /* the first output will be writen to outReal[lookback] */ ______\ return false; ______\ } ______\ FOR_EACH(INIT_OUTPUT, FUNC_OUTS) ______\ diff --git a/source/talibd/test.d b/source/talibd/test.d index 96d7e89..9ccee3a 100644 --- a/source/talibd/test.d +++ b/source/talibd/test.d @@ -17,6 +17,45 @@ public import talibd.oo; // D wrapper alias logger = std.experimental.logger; + +unittest { + + // NOTE: in talibd, SMA lookback and TA_RSI lookback has different relation to default_RSI_optInTimePeriod: + // The lookback function indicates how many inputs are consume before the first output can be calculated. + // Example: A simple moving average (SMA) of period 10 will have a lookback of 9. + int rsiLb = TA_RSI_Lookback(default_RSI_optInTimePeriod); + int maLb = TA_MA_Lookback( default_RSI_optInTimePeriod, TA_MAType_SMA); + Assert.equal(default_RSI_optInTimePeriod, rsiLb); + Assert.equal(default_RSI_optInTimePeriod, maLb+1); + + { + int lb = TA_RSI_Lookback(5); + Assert.equal(lb, 5); // lookback is 5! + } + + { + int lb = TA_MA_Lookback(5, TA_MAType_SMA); + Assert.equal(lb, 4); // lookback is 4, so we must have ma.length = 5 to write the 1st value + } + + // lookback must be the valid output array index + // output_sma[lookback] and output_rsi[lookback] contain the 1st output value + { + double[4] price; + double[4] ma; // not long enough to write the 1st output + bool ok = TA_MA(price, ma, 5); + Assert.equal(ok, false); + } + + { + double[5] price; + double[5] ma; // now long enough to write the 1st output + bool ok = TA_MA(price, ma, 5); + Assert.equal(ok, true); + } +} + + unittest { void main() {