diff --git a/ma.go b/ma.go index cd28e25..7065193 100644 --- a/ma.go +++ b/ma.go @@ -8,22 +8,29 @@ import ( // Data expected to be older->newer, fair for result as well func SMA(data []float64, period int) (result []float64, err error) { if len(data) == 0 { - return result, errors.New("input parameter 'data' is empty") + return result, errors.New("SMA: input parameter 'data' is empty") } if period <= 0 { - return result, errors.New("Invalid period") + return result, errors.New("SMA: Invalid period") } var interm float64 for i := 0; i < len(data); i++ { - interm += data[i] - if (i + 1) < period { + if math.IsNaN(data[i]) { result = append(result, math.NaN()) + interm = 0 } else { - result = append(result, interm/float64(period)) - interm -= data[i+1-period] + interm += data[i] + if (i + 1) < period { + result = append(result, math.NaN()) + } else { + result = append(result, interm/float64(period)) + if !math.IsNaN(data[i+1-period]) { + interm -= data[i+1-period] + } + } } } return result, nil @@ -82,26 +89,33 @@ func MACD(data []float64, fastperiod, slowperiod, signalperiod int) (macd, macds } macd = make([]float64, len(fast_ema)) - macdsignal = make([]float64, 0) - diff := make([]float64, 0) + //macdsignal = make([]float64, 0) + macdsignal = make([]float64, len(fast_ema)) + //diff := make([]float64, 0) for k, fast := range fast_ema { if math.IsNaN(fast) || math.IsNaN(slow_ema[k]) { macd[k] = math.NaN() - macdsignal = append(macdsignal, math.NaN()) + //macdsignal = append(macdsignal, math.NaN()) + macdsignal[k] = math.NaN() } else { macd[k] = fast - slow_ema[k] - diff = append(diff, macd[k]) + //diff = append(diff, macd[k]) + macdsignal[k] = macd[k] } } - diff_ema, err := EMA(diff, signalperiod) + /*diff_ema, err := EMA(diff, signalperiod) if err != nil { return } macdsignal = append(macdsignal, diff_ema...) - + */ + macdsignal, err = EMA(macdsignal, signalperiod) + if err != nil { + return + } macdhist = make([]float64, len(macd)) for k, ms := range macdsignal { diff --git a/rsi.go b/rsi.go index 095e18b..8a2a2ba 100644 --- a/rsi.go +++ b/rsi.go @@ -38,5 +38,3 @@ func RSI(data []float64, period int) (result []float64, err error) { } return } - - diff --git a/stoch.go b/stoch.go index 18ec503..bc853ae 100644 --- a/stoch.go +++ b/stoch.go @@ -1,51 +1,44 @@ package ta import ( + "fmt" "math" ) func STOCH(high, low, closes []float64, fastk_period, slowk_period, slowd_period int) (fastk, slowd []float64, err error) { - fastk_dummy := make([]float64, fastk_period-1) - fastk_vals := make([]float64, len(closes)-fastk_period+1) - + fastk = make([]float64, len(closes)) for i := 0; i < len(closes); i++ { - if (i + 1) < fastk_period { - fastk_dummy[i] = math.NaN() + fastk[i] = math.NaN() } else { lower_bound := i + 1 - fastk_period upper_bound := i + 1 curr_low := SliceMin(low[lower_bound:upper_bound]) curr_high := SliceMax(high[lower_bound:upper_bound]) - - fastk_vals[i+1-fastk_period] = ((closes[i] - curr_low) / (curr_high - curr_low)) * 100 + fastk[i] = ((closes[i] - curr_low) / (curr_high - curr_low)) * 100 } } - fastk_ema, err := EMA(fastk_vals, slowk_period) - + fastk, err = EMA(fastk, slowk_period) if err != nil { return } - fastk = append(fastk_dummy, fastk_ema...) - slowd_dummy := make([]float64, 0) - var offset int - for _, v := range fastk { - if math.IsNaN(v) { - slowd_dummy = append(slowd_dummy, math.NaN()) - offset++ - } else { - break - } + slowd, err = EMA(fastk, slowd_period) + if err != nil { + return } - slowd_ema, err := EMA(fastk[offset:], slowd_period) + return +} + +func STOCHRSI(data []float64, period, fastk_period, fastd_period int) (fastk, slowd []float64, err error) { + rsi, err := RSI(data, period) if err != nil { return } - slowd = append(slowd_dummy, slowd_ema...) + fmt.Println(rsi) - return + return STOCH(rsi, rsi, rsi, period, fastk_period, fastd_period) } diff --git a/tests/ma_test.go b/tests/ma_test.go index 12442f6..9ffd94e 100644 --- a/tests/ma_test.go +++ b/tests/ma_test.go @@ -2,6 +2,7 @@ package tests import ( "math" + "strings" "testing" "../../ta" @@ -13,7 +14,7 @@ func TestEmptyData(t *testing.T) { res, err := ta.SMA(data, period) if err != nil { - if err.Error() != "input parameter 'data' is empty" { + if !strings.Contains(err.Error(), "'data' is empty") { t.Errorf("Unexpected error %s", err.Error()) } } else { @@ -30,7 +31,7 @@ func TestZeroPeriod(t *testing.T) { res, err := ta.SMA(data, period) if err != nil { - if err.Error() != "Invalid period" { + if !strings.Contains(err.Error(), "Invalid period") { t.Errorf("Unexpected error %s", err.Error()) } } else { @@ -46,7 +47,7 @@ func TestNegativePeriod(t *testing.T) { res, err := ta.SMA(data, period) if err != nil { - if err.Error() != "Invalid period" { + if !strings.Contains(err.Error(), "Invalid period") { t.Errorf("Unexpected error %s", err.Error()) } } else { @@ -62,7 +63,7 @@ func TestEmaPeriod(t *testing.T) { res, err := ta.EMA(data, period) if err != nil { - if err.Error() != "Invalid period" { + if !strings.Contains(err.Error(), "Invalid period") { t.Errorf("Unexpected error %s", err.Error()) } } else { @@ -173,7 +174,7 @@ func TestEMACalculation(t *testing.T) { test_worker(t, tests, ta.EMA, 2) } -func test_workerCD(t *testing.T) { +func TestMACD(t *testing.T) { type MACDTestItem struct { Input []float64 @@ -228,48 +229,28 @@ func test_workerCD(t *testing.T) { -0.21748933, -0.04349606, 0.17309122, 0.42058164, 0.68946351, }, ExpectedMACDSignal: []float64{ - math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), - math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), - math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), - math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), - math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), - math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), - math.NaN(), math.NaN(), math.NaN(), 0.16148066, 0.08007288, - 0.03721329, 0.03663134, 0.07844962, 0.16042697, 0.27886664, - 0.26972604, 0.19643942, 0.10229581, 0.01573588, -0.0456146, - -0.07188979, -0.05868482, -0.00533851, 0.0863377, 0.21299016, - 0.21085054, 0.14357255, 0.05462109, -0.02742197, -0.08481723, - -0.10760724, -0.09131299, -0.03521311, 0.05892999, 0.18780281, - 0.18767003, 0.12221268, 0.03491841, -0.04561188, -0.10162274, - -0.12314307, -0.10568219, -0.04850869, 0.04662376, 0.1764093, - 0.17711933, 0.11244085, 0.02586679, -0.05399716, -0.10939127, - -0.13034056, -0.11235082, -0.05468742, 0.04089892, 0.17110502, - 0.17220477, 0.10788744, 0.02164809, -0.05790567, -0.11301231, - -0.13369517, -0.11545853, -0.05756632, 0.03823205, 0.16863464, - 0.16991646, 0.10576784, 0.01968481, -0.05972412, -0.11469657, - -0.13525512, -0.11690331, -0.0589044, 0.0369928, 0.16748694, + math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), + math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), + math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), -0.00716444, 0.01465165, 0.07390009, 0.16929050, 0.29790203, + 0.29632517, 0.22851044, 0.13816764, 0.05408012, -0.00584755, -0.03152305, -0.01835773, 0.03445836, 0.12523397, 0.25071224, + 0.24720252, 0.17842060, 0.08788076, 0.00420383, -0.05484023, -0.07927030, -0.06458923, -0.01006204, 0.08255888, 0.20996711, + 0.20843210, 0.14163779, 0.05307328, -0.02866021, -0.08580783, -0.10839972, -0.09194697, -0.03572029, 0.05852424, 0.18747821, + 0.18741035, 0.12200494, 0.03475222, -0.04574484, -0.10172910, -0.12322816, -0.10575027, -0.04856315, 0.04658020, 0.17637445, + 0.17709145, 0.11241854, 0.02584895, -0.05401144, -0.10940269, -0.13034970, -0.11235813, -0.05469327, 0.04089424, 0.17110127, + 0.17220177, 0.10788504, 0.02164617, -0.05790721, -0.11301353, -0.13369616, -0.11545932, -0.05756695, 0.03823155, 0.16863424, + 0.16991614, 0.10576758, 0.01968460, -0.05972429, -0.11469670, -0.13525523, -0.11690339, -0.05890447, 0.03699275, 0.16748690, }, ExpectedHist: []float64{ - math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), - math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), - math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), - math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), - math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), - math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), - math.NaN(), math.NaN(), math.NaN(), -0.44375062, -0.32563113, - -0.17143834, -0.00232781, 0.16727312, 0.32790942, 0.47375867, - -0.03656241, -0.29314649, -0.37657443, -0.34623974, -0.24540189, - -0.10510078, 0.05281987, 0.21338525, 0.36670484, 0.50660986, - -0.00855848, -0.26911198, -0.35580584, -0.32817224, -0.22958104, - -0.09116004, 0.06517701, 0.22439951, 0.37657239, 0.51549127, - -0.00053111, -0.26182939, -0.34917708, -0.32212117, -0.22404342, - -0.08608132, 0.06984351, 0.22869401, 0.38052981, 0.51914216, - 0.00284013, -0.25871396, -0.34629622, -0.31945582, -0.22157643, - -0.08379716, 0.07195896, 0.23065361, 0.38234534, 0.5208244, - 0.00439901, -0.25726932, -0.3449574, -0.31821505, -0.22042653, - -0.08273148, 0.07294657, 0.23156885, 0.38319349, 0.52161035, - 0.00512729, -0.25659449, -0.34433213, -0.31763573, -0.21988979, - -0.08223421, 0.07340725, 0.23199562, 0.38358883, 0.52197657, + math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), + math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), + math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), -0.05731555, 0.08726436, 0.23699379, 0.38156163, 0.51444613, + -0.00630745, -0.27125893, -0.36137117, -0.33635008, -0.23971070, -0.10270200, 0.05266126, 0.21126438, 0.36310243, 0.50191308, + -0.01403888, -0.27512767, -0.36215937, -0.33470770, -0.23617625, -0.09772027, 0.05872427, 0.21810877, 0.37048366, 0.50963291, + -0.00614003, -0.26717723, -0.35425803, -0.32693400, -0.22859045, -0.09036757, 0.06581099, 0.22490670, 0.37697814, 0.51581587, + -0.00027143, -0.26162165, -0.34901089, -0.32198821, -0.22393706, -0.08599623, 0.06991158, 0.22874846, 0.38057338, 0.51917701, + 0.00286802, -0.25869165, -0.34627837, -0.31944154, -0.22156501, -0.08378802, 0.07196627, 0.23065946, 0.38235002, 0.52082814, + 0.00440200, -0.25726692, -0.34495548, -0.31821352, -0.22042530, -0.08273049, 0.07294736, 0.23156948, 0.38319399, 0.52161075, + 0.00512761, -0.25659424, -0.34433193, -0.31763556, -0.21988966, -0.08223411, 0.07340733, 0.23199569, 0.38358889, 0.52197661, }, }, } @@ -281,8 +262,8 @@ func test_workerCD(t *testing.T) { if err != nil { t.Errorf(err.Error()) } - compare_inp_exp(t, macd, test.ExpectedMACD, 8) - compare_inp_exp(t, macdsignal, test.ExpectedMACDSignal, 8) - compare_inp_exp(t, macdhist, test.ExpectedHist, 8) + compare_inp_exp(t, macd, test.ExpectedMACD, 8, "MACD") + compare_inp_exp(t, macdsignal, test.ExpectedMACDSignal, 8, "MACDSignal") + compare_inp_exp(t, macdhist, test.ExpectedHist, 8, "MACDHIST") } } diff --git a/tests/misc.go b/tests/misc.go index 38b972a..1ebcaaa 100644 --- a/tests/misc.go +++ b/tests/misc.go @@ -14,25 +14,26 @@ type testItem struct { type test_func func([]float64, int) ([]float64, error) func test_worker(t *testing.T, tests []*testItem, f test_func, round_floats float64) { - for _, test := range tests { - + for test_num, test := range tests { + comment := "TEST_" + string(test_num) res, err := f(test.Input, test.Period) if err != nil { - t.Errorf("Unexpected error %s", err.Error()) + t.Errorf("%s: Unexpected error %s", comment, err.Error()) } else { - compare_inp_exp(t, res, test.Expected, round_floats) + compare_inp_exp(t, res, test.Expected, round_floats, comment) } } } -func compare_inp_exp(t *testing.T, res, expected []float64, round_floats float64) { +func compare_inp_exp(t *testing.T, res, expected []float64, round_floats float64, comment string) { if len(res) != len(expected) { t.Errorf(` + TEST COMMENT %s: Different sizes: result %d, expected %d res: %v, expected: %v - `, len(res), len(expected), res, expected, + `, comment, len(res), len(expected), res, expected, ) } @@ -44,11 +45,12 @@ func compare_inp_exp(t *testing.T, res, expected []float64, round_floats float64 (math.IsNaN(rounded_e) && !math.IsNaN(rounded_v)) || (math.IsNaN(rounded_v) && !math.IsNaN(rounded_e)) { t.Errorf(` + TEST COMMENT %s Expected result %#v Got result %#v %0.8f instead of %0.8f (%0.8f instead of %0.8f) - `, expected, res, rounded_v, rounded_e, v, expected[k]) + `, comment, expected, res, rounded_v, rounded_e, v, expected[k]) break } } diff --git a/tests/stoch_test.go b/tests/stoch_test.go index 68107cb..df7777d 100644 --- a/tests/stoch_test.go +++ b/tests/stoch_test.go @@ -8,7 +8,7 @@ import ( ) func TestSTOCHCalculation(t *testing.T) { - var close []float64 = []float64{ + var closes []float64 = []float64{ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, @@ -48,10 +48,10 @@ func TestSTOCHCalculation(t *testing.T) { } var expected_slowk []float64 = []float64{ - math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), 72.80551296, 68.93194648, 64.09801434, 67.01261748, - 33.87313046, 21.50238050, 19.51599905, 22.74694285, 43.61632131, 54.89057317, 60.33474072, 59.71617774, 64.11716958, 72.58908543, - 37.99000927, 25.28654979, 24.62579350, 29.35369057, 63.11142986, 63.87264353, 69.30238698, 65.84761110, 70.49064827, 68.92086282, - 34.87326225, 21.86676276, 19.38081379, 22.15514010, 46.33345430, 66.57208246, 63.34893407, 68.54159402, 61.79572053, 62.86587247, + math.NaN(), math.NaN(), math.NaN(), math.NaN(), 27.87890027, 50.85070959, 55.90401438, 60.48119718, 59.87263969, 64.89993016, + 32.81678680, 20.97420866, 19.25191313, 22.61489989, 43.55029983, 54.85756243, 60.31823535, 59.70792505, 64.11304324, 72.58702226, + 37.98897768, 25.28603400, 24.62553560, 29.35356162, 63.11136538, 63.87261130, 69.30237086, 65.84760304, 70.49064424, 68.92086080, + 34.87326125, 21.86676225, 19.38081354, 22.15513997, 46.33345424, 66.57208242, 63.34893405, 68.54159402, 61.79572053, 62.86587247, 31.49596171, 19.89307521, 18.17370084, 21.39608255, 51.51021174, 74.10528969, 72.14488542, 75.60062262, 73.75777263, 62.58372971, 32.65246736, 21.17584260, 18.92653665, 21.29089011, 49.79981076, 68.46652608, 69.25782069, 62.59550901, 65.61605476, 61.53837599, 31.35469526, 19.87697835, 17.75224334, 20.30399929, 43.69896224, 56.11384973, 67.48331110, 81.28271989, 72.41915990, 70.25530869, @@ -61,11 +61,11 @@ func TestSTOCHCalculation(t *testing.T) { } var expected_slowd []float64 = []float64{ - math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), 68.61182459, 67.81222104, - 50.84267575, 36.17252812, 27.84426359, 25.29560322, 34.45596226, 44.67326771, 52.50400422, 56.11009098, 60.11363028, 66.35135785, - 52.17068356, 38.72861668, 31.67720509, 30.51544783, 46.81343884, 55.34304119, 62.32271408, 64.08516259, 67.28790543, 68.10438412, - 51.48882319, 36.67779297, 28.02930338, 25.09222174, 35.71283802, 51.14246024, 57.24569715, 62.89364559, 62.34468306, 62.60527777, - 47.05061974, 33.47184747, 25.82277416, 23.60942835, 37.55982005, 55.83255487, 63.98872014, 69.79467138, 71.77622201, 67.17997586, + math.NaN(), math.NaN(), math.NaN(), math.NaN(), 9.29296676, 30.07183817, 42.98792627, 51.73456173, 55.80360071, 60.35176543, + 46.58427612, 33.77924239, 26.51557776, 24.56523883, 34.05776933, 44.45766588, 52.38795061, 56.04793783, 60.08049053, 66.33375640, + 52.16136704, 38.72370052, 31.67461806, 30.51408984, 46.81272761, 55.34266945, 62.32252016, 64.08506160, 67.28785292, 68.10435686, + 51.48880905, 36.67778565, 28.02929960, 25.09221978, 35.71283701, 51.14245972, 57.24569689, 62.89364545, 62.34468299, 62.60527773, + 47.05061972, 33.47184746, 25.82277415, 23.60942835, 37.55982004, 55.83255487, 63.98872014, 69.79467138, 71.77622201, 67.17997586, 49.91622161, 35.54603211, 27.23628438, 24.26358724, 37.03169900, 52.74911254, 61.00346662, 61.79948782, 63.70777129, 62.62307364, 46.98888445, 33.43293140, 25.59258737, 22.94829333, 33.32362779, 44.71873876, 56.10102493, 68.69187241, 70.55551616, 70.40541242, 53.50126411, 38.72966911, 30.27913856, 29.22228955, 40.71899777, 54.97240137, 66.19989971, 71.78736728, 71.18366337, 71.45039074, @@ -73,10 +73,10 @@ func TestSTOCHCalculation(t *testing.T) { 61.89501833, 45.14304756, 35.40187571, 32.52161407, 45.83112306, 61.43166889, 71.11482286, 75.29755176, 74.06191255, 74.38415717, } - slowk, slowd, err := ta.STOCH(high, low, close, 5, 3, 3) + slowk, slowd, err := ta.STOCH(high, low, closes, 5, 3, 3) if err != nil { t.Errorf(err.Error()) } - compare_inp_exp(t, slowk, expected_slowk, 8) - compare_inp_exp(t, slowd, expected_slowd, 8) + compare_inp_exp(t, slowk, expected_slowk, 8, "SLOWK") + compare_inp_exp(t, slowd, expected_slowd, 8, "SLOWD") }