diff --git a/momentum_indicators.go b/momentum_indicators.go index b021ed8..b62652b 100644 --- a/momentum_indicators.go +++ b/momentum_indicators.go @@ -29,7 +29,7 @@ func AwesomeOscillator(low, high []float64) []float64 { // CO = Ema(fastPeriod, AD) - Ema(slowPeriod, AD) // // Returns co, ad. -func ChaikinOscillator(fastPeriod, slowPeriod int, low, high, closing []float64, volume []int64) ([]float64, []float64) { +func ChaikinOscillator(fastPeriod, slowPeriod int, low, high, closing, volume []float64) ([]float64, []float64) { ad := AccumulationDistribution(high, low, closing, volume) co := subtract(Ema(fastPeriod, ad), Ema(slowPeriod, ad)) @@ -41,7 +41,7 @@ func ChaikinOscillator(fastPeriod, slowPeriod int, low, high, closing []float64, // periods, 3 and 10. // // Returns co, ad. -func DefaultChaikinOscillator(low, high, closing []float64, volume []int64) ([]float64, []float64) { +func DefaultChaikinOscillator(low, high, closing, volume []float64) ([]float64, []float64) { return ChaikinOscillator(3, 10, low, high, closing, volume) } @@ -102,10 +102,9 @@ func DefaultPercentagePriceOscillator(price []float64) ([]float64, []float64, [] // Histogram = PVO - Signal // // Returns pvo, signal, histogram -func PercentageVolumeOscillator(fastPeriod, slowPeriod, signalPeriod int, volume []int64) ([]float64, []float64, []float64) { - volumeAsFloat := asFloat64(volume) - fastEma := Ema(fastPeriod, volumeAsFloat) - slowEma := Ema(slowPeriod, volumeAsFloat) +func PercentageVolumeOscillator(fastPeriod, slowPeriod, signalPeriod int, volume []float64) ([]float64, []float64, []float64) { + fastEma := Ema(fastPeriod, volume) + slowEma := Ema(slowPeriod, volume) pvo := multiplyBy(divide(subtract(fastEma, slowEma), slowEma), 100) signal := Ema(signalPeriod, pvo) histogram := subtract(pvo, signal) @@ -116,7 +115,7 @@ func PercentageVolumeOscillator(fastPeriod, slowPeriod, signalPeriod int, volume // Default Percentage Volume Oscillator calculates it with the default periods of 12, 26, 9. // // Returns pvo, signal, histogram -func DefaultPercentageVolumeOscillator(volume []int64) ([]float64, []float64, []float64) { +func DefaultPercentageVolumeOscillator(volume []float64) ([]float64, []float64, []float64) { return PercentageVolumeOscillator(12, 26, 9, volume) } diff --git a/momentum_indicators_test.go b/momentum_indicators_test.go index e9b4190..fb9fc1a 100644 --- a/momentum_indicators_test.go +++ b/momentum_indicators_test.go @@ -13,7 +13,7 @@ func TestChaikinOscillator(t *testing.T) { high := []float64{10, 11, 12, 13, 14, 15, 16, 17} low := []float64{1, 2, 3, 4, 5, 6, 7, 8} closing := []float64{5, 6, 7, 8, 9, 10, 11, 12} - volume := []int64{100, 200, 300, 400, 500, 600, 700, 800} + volume := []float64{100, 200, 300, 400, 500, 600, 700, 800} expected := []float64{0, -7.41, -18.52, -31.69, -46.09, -61.27, -76.95, -92.97} actual, _ := ChaikinOscillator(2, 5, low, high, closing, volume) @@ -76,7 +76,7 @@ func TestPercentagePriceOscillator(t *testing.T) { } func TestPercentageVolumeOscillator(t *testing.T) { - volume := []int64{ + volume := []float64{ 6954, 4511, 4474, 4126, 4572, 3936, 3192, 3090, 3476, 3852, 3107, 3604, 4145, 5192, 3560, 3961, 4322, 3901, 3392, 4278, 4212, 4428, 3846, 3824, 4142, 4964, 4683, 4630, 4746, 4254, 4197, 4236, 3877, 4474, 3943, 3969, diff --git a/strategy.go b/strategy.go index 8f759c2..6c86977 100644 --- a/strategy.go +++ b/strategy.go @@ -25,7 +25,7 @@ type Asset struct { Closing []float64 High []float64 Low []float64 - Volume []int64 + Volume []float64 } // Strategy function. It takes an Asset and returns diff --git a/strategy.md b/strategy.md index c5e17c4..629aee0 100644 --- a/strategy.md +++ b/strategy.md @@ -21,7 +21,7 @@ type Asset struct { Closing []float64 High []float64 Low []float64 - Volume []int64 + Volume []float64 } ``` diff --git a/trend_indicators.go b/trend_indicators.go index 89255c7..de1483d 100644 --- a/trend_indicators.go +++ b/trend_indicators.go @@ -267,12 +267,12 @@ func Min(period int, values []float64) []float64 { // PSAR = PSAR[i - 1] - ((PSAR[i - 1] - EP) * AF) // // If the trend is Falling: -// - PSAR is the maximum of PSAR or the previous two high values. -// - If the current high is greather than or equals to PSAR, use EP. +// - PSAR is the maximum of PSAR or the previous two high values. +// - If the current high is greather than or equals to PSAR, use EP. // // If the trend is Rising: -// - PSAR is the minimum of PSAR or the previous two low values. -// - If the current low is less than or equals to PSAR, use EP. +// - PSAR is the minimum of PSAR or the previous two low values. +// - If the current low is less than or equals to PSAR, use EP. // // If PSAR is greater than the closing, trend is falling, and the EP // is set to the minimum of EP or the low. @@ -361,9 +361,10 @@ func Qstick(period int, opening, closing []float64) []float64 { // crosses above 80%, and oversold when they crosses below // 20%. The J line represents the divergence. // -// // RSV = ((Closing - Min(Low, rPeriod)) -// / (Max(High, rPeriod) - Min(Low, rPeriod))) * 100 +// +// / (Max(High, rPeriod) - Min(Low, rPeriod))) * 100 +// // K = Sma(RSV, kPeriod) // D = Sma(K, dPeriod) // J = (3 * K) - (2 * D) @@ -498,9 +499,12 @@ func Tema(period int, values []float64) []float64 { // Trima function calculates the Triangular Moving Average (TRIMA). // // If period is even: -// TRIMA = SMA(period / 2, SMA((period / 2) + 1, values)) +// +// TRIMA = SMA(period / 2, SMA((period / 2) + 1, values)) +// // If period is odd: -// TRIMA = SMA((period + 1) / 2, SMA((period + 1) / 2, values)) +// +// TRIMA = SMA((period + 1) / 2, SMA((period + 1) / 2, values)) // // Returns trima. func Trima(period int, values []float64) []float64 { @@ -627,14 +631,13 @@ func Vortex(high, low, closing []float64) ([]float64, []float64) { // VWMA = Sum(Price * Volume) / Sum(Volume) for a given Period. // // Returns vwma -func Vwma(period int, closing []float64, volume []int64) []float64 { - floatVolume := asFloat64(volume) - vwma := divide(Sum(period, multiply(closing, floatVolume)), Sum(period, floatVolume)) +func Vwma(period int, closing, volume []float64) []float64 { + vwma := divide(Sum(period, multiply(closing, volume)), Sum(period, volume)) return vwma } // The DefaultVwma function calculates VWMA with a period of 20. -func DefaultVwma(closing []float64, volume []int64) []float64 { +func DefaultVwma(closing, volume []float64) []float64 { return Vwma(20, closing, volume) } diff --git a/trend_indicators_test.go b/trend_indicators_test.go index 9080eab..2027c53 100644 --- a/trend_indicators_test.go +++ b/trend_indicators_test.go @@ -295,7 +295,7 @@ func TestVortex(t *testing.T) { func TestVwma(t *testing.T) { closing := []float64{20, 21, 21, 19, 16} - volume := []int64{100, 50, 40, 50, 100} + volume := []float64{100, 50, 40, 50, 100} expected := []float64{20, 20.33, 20.47, 20.29, 17.84} period := 3 @@ -305,7 +305,7 @@ func TestVwma(t *testing.T) { func TestDefaultVwma(t *testing.T) { closing := []float64{20, 21, 21, 19, 16} - volume := []int64{100, 50, 40, 50, 100} + volume := []float64{100, 50, 40, 50, 100} expected := []float64{20, 20.33, 20.47, 20.17, 18.94} actual := DefaultVwma(closing, volume) diff --git a/trend_strategies_test.go b/trend_strategies_test.go index 9330cbb..da8f549 100644 --- a/trend_strategies_test.go +++ b/trend_strategies_test.go @@ -27,7 +27,7 @@ func TestTrendStrategy(t *testing.T) { Low: []float64{ 0, 0, 0, 0, 0, }, - Volume: []int64{ + Volume: []float64{ 0, 0, 0, 0, 0, }, } @@ -57,7 +57,7 @@ func TestVwmaStrategy(t *testing.T) { Low: []float64{ 0, 0, 0, 0, 0, }, - Volume: []int64{ + Volume: []float64{ 100, 50, 40, 50, 100, }, } @@ -90,7 +90,7 @@ func TestDefaultVwmaStrategy(t *testing.T) { Low: []float64{ 0, 0, 0, 0, 0, }, - Volume: []int64{ + Volume: []float64{ 100, 50, 40, 50, 100, }, } diff --git a/volume_indicators.go b/volume_indicators.go index 2c19f04..aeb4a2f 100644 --- a/volume_indicators.go +++ b/volume_indicators.go @@ -20,7 +20,7 @@ const CMF_DEFAULT_PERIOD = 20 // AD = Previous AD + CMFV // // Returns ad. -func AccumulationDistribution(high, low, closing []float64, volume []int64) []float64 { +func AccumulationDistribution(high, low, closing, volume []float64) []float64 { checkSameSize(high, low, closing) ad := make([]float64, len(closing)) @@ -39,17 +39,19 @@ func AccumulationDistribution(high, low, closing []float64, volume []int64) []fl // On-Balance Volume (OBV). It is a technical trading momentum indicator that // uses volume flow to predict changes in stock price. // -// volume, if Closing > Closing-Prev +// volume, if Closing > Closing-Prev +// // OBV = OBV-Prev + 0, if Closing = Closing-Prev -// -volume, if Closing < Closing-Prev +// +// -volume, if Closing < Closing-Prev // // Returns obv -func Obv(closing []float64, volume []int64) []int64 { +func Obv(closing, volume []float64) []float64 { if len(closing) != len(volume) { panic("not all same size") } - obv := make([]int64, len(volume)) + obv := make([]float64, len(volume)) for i := 1; i < len(obv); i++ { obv[i] = obv[i-1] @@ -73,9 +75,9 @@ func Obv(closing []float64, volume []int64) []int64 { // Money Flow Index = 100 - (100 / (1 + Money Ratio)) // // Retruns money flow index values. -func MoneyFlowIndex(period int, high, low, closing []float64, volume []int64) []float64 { +func MoneyFlowIndex(period int, high, low, closing, volume []float64) []float64 { typicalPrice, _ := TypicalPrice(low, high, closing) - rawMoneyFlow := multiply(typicalPrice, asFloat64(volume)) + rawMoneyFlow := multiply(typicalPrice, volume) signs := extractSign(diff(rawMoneyFlow, 1)) moneyFlow := multiply(signs, rawMoneyFlow) @@ -93,7 +95,7 @@ func MoneyFlowIndex(period int, high, low, closing []float64, volume []int64) [] } // Default money flow index with period 14. -func DefaultMoneyFlowIndex(high, low, closing []float64, volume []int64) []float64 { +func DefaultMoneyFlowIndex(high, low, closing, volume []float64) []float64 { return MoneyFlowIndex(14, high, low, closing, volume) } @@ -103,12 +105,12 @@ func DefaultMoneyFlowIndex(high, low, closing []float64, volume []int64) []float // Force Index = EMA(period, (Current - Previous) * Volume) // // Returns force index. -func ForceIndex(period int, closing []float64, volume []int64) []float64 { - return Ema(period, multiply(diff(closing, 1), asFloat64(volume))) +func ForceIndex(period int, closing, volume []float64) []float64 { + return Ema(period, multiply(diff(closing, 1), volume)) } // The default Force Index (FI) with window size of 13. -func DefaultForceIndex(closing []float64, volume []int64) []float64 { +func DefaultForceIndex(closing, volume []float64) []float64 { return ForceIndex(13, closing, volume) } @@ -121,15 +123,15 @@ func DefaultForceIndex(closing []float64, volume []int64) []float64 { // EMV(14) = SMA(14, EMV(1)) // // Returns ease of movement values. -func EaseOfMovement(period int, high, low []float64, volume []int64) []float64 { +func EaseOfMovement(period int, high, low, volume []float64) []float64 { distanceMoved := diff(divideBy(add(high, low), 2), 1) - boxRatio := divide(divideBy(asFloat64(volume), float64(100000000)), subtract(high, low)) + boxRatio := divide(divideBy(volume, float64(100000000)), subtract(high, low)) emv := Sma(period, divide(distanceMoved, boxRatio)) return emv } // The default Ease of Movement with the default period of 14. -func DefaultEaseOfMovement(high, low []float64, volume []int64) []float64 { +func DefaultEaseOfMovement(high, low, volume []float64) []float64 { return EaseOfMovement(14, high, low, volume) } @@ -139,9 +141,9 @@ func DefaultEaseOfMovement(high, low []float64, volume []int64) []float64 { // VPT = Previous VPT + (Volume * (Current Closing - Previous Closing) / Previous Closing) // // Returns volume price trend values. -func VolumePriceTrend(closing []float64, volume []int64) []float64 { +func VolumePriceTrend(closing, volume []float64) []float64 { previousClosing := shiftRightAndFillBy(1, closing[0], closing) - vpt := multiply(asFloat64(volume), divide(subtract(closing, previousClosing), previousClosing)) + vpt := multiply(volume, divide(subtract(closing, previousClosing), previousClosing)) return Sum(len(vpt), vpt) } @@ -151,13 +153,12 @@ func VolumePriceTrend(closing []float64, volume []int64) []float64 { // VWAP = Sum(Closing * Volume) / Sum(Volume) // // Returns vwap values. -func VolumeWeightedAveragePrice(period int, closing []float64, volume []int64) []float64 { - v := asFloat64(volume) - return divide(Sum(period, multiply(closing, v)), Sum(period, v)) +func VolumeWeightedAveragePrice(period int, closing, volume []float64) []float64 { + return divide(Sum(period, multiply(closing, volume)), Sum(period, volume)) } // Default volume weighted average price with period of 14. -func DefaultVolumeWeightedAveragePrice(closing []float64, volume []int64) []float64 { +func DefaultVolumeWeightedAveragePrice(closing, volume []float64) []float64 { return VolumeWeightedAveragePrice(14, closing, volume) } @@ -166,14 +167,14 @@ func DefaultVolumeWeightedAveragePrice(closing []float64, volume []int64) []floa // // If Volume is greather than Previous Volume: // -// NVI = Previous NVI +// NVI = Previous NVI // // Otherwise: // -// NVI = Previous NVI + (((Closing - Previous Closing) / Previous Closing) * Previous NVI) +// NVI = Previous NVI + (((Closing - Previous Closing) / Previous Closing) * Previous NVI) // // Returns nvi values. -func NegativeVolumeIndex(closing []float64, volume []int64) []float64 { +func NegativeVolumeIndex(closing, volume []float64) []float64 { if len(closing) != len(volume) { panic("not all same size") } @@ -199,17 +200,16 @@ func NegativeVolumeIndex(closing []float64, volume []int64) []float64 { // Money Flow Multiplier = ((Closing - Low) - (High - Closing)) / (High - Low) // Money Flow Volume = Money Flow Multiplier * Volume // Chaikin Money Flow = Sum(20, Money Flow Volume) / Sum(20, Volume) -// -func ChaikinMoneyFlow(high, low, closing []float64, volume []int64) []float64 { +func ChaikinMoneyFlow(high, low, closing, volume []float64) []float64 { moneyFlowMultiplier := divide( subtract(subtract(closing, low), subtract(high, closing)), subtract(high, low)) - moneyFlowVolume := multiply(moneyFlowMultiplier, asFloat64(volume)) + moneyFlowVolume := multiply(moneyFlowMultiplier, volume) cmf := divide( Sum(CMF_DEFAULT_PERIOD, moneyFlowVolume), - Sum(CMF_DEFAULT_PERIOD, asFloat64(volume))) + Sum(CMF_DEFAULT_PERIOD, volume)) return cmf } diff --git a/volume_indicators_test.go b/volume_indicators_test.go index bb4d7b2..153e2e2 100644 --- a/volume_indicators_test.go +++ b/volume_indicators_test.go @@ -11,7 +11,7 @@ func TestMoneyFlowIndex(t *testing.T) { high := []float64{10, 9, 12, 14, 12} low := []float64{6, 7, 9, 12, 10} closing := []float64{9, 11, 7, 10, 8} - volume := []int64{100, 110, 80, 120, 90} + volume := []float64{100, 110, 80, 120, 90} expected := []float64{100, 100, 57.01, 65.85, 61.54} period := 2 @@ -23,7 +23,7 @@ func TestMoneyFlowIndex2(t *testing.T) { high := []float64{2390.9, 2386.3, 2395.33, 2399.0, 2402.46, 2401.15, 2421.98, 2430.31, 2426.33, 2434.93, 2470.83, 2483.36, 2467.19, 2450.72} low := []float64{2373.15, 2370.0, 2380.77, 2384.28, 2387.46, 2385.02, 2383.18, 2408.39, 2410.59, 2420.89, 2428.92, 2456.77, 2437.65, 2440.87} closing := []float64{2373.39, 2382.47, 2394.4, 2387.51, 2395.64, 2389.47, 2410.24, 2425.37, 2422.33, 2430.29, 2465.76, 2466.27, 2440.07, 2445.85} - volume := []int64{1621, 1387, 1444, 1298, 1629, 1598, 2311, 2934, 2128, 1823, 5078, 6693, 3960, 1927} + volume := []float64{1621, 1387, 1444, 1298, 1629, 1598, 2311, 2934, 2128, 1823, 5078, 6693, 3960, 1927} expected := []float64{100, 53.884998, 68.888204, 53.300054, 63.645476, 52.296425, 62.119198, 70.011701, 60.826077, 54.659774, 64.728457, 72.749002, 64.185176, 60.710993} period := 14 @@ -33,7 +33,7 @@ func TestMoneyFlowIndex2(t *testing.T) { func TestForceIndex(t *testing.T) { closing := []float64{9, 11, 7, 10, 8} - volume := []int64{100, 110, 80, 120, 90} + volume := []float64{100, 110, 80, 120, 90} expected := []float64{900, 220, -320, 360, -180} period := 1 @@ -43,7 +43,7 @@ func TestForceIndex(t *testing.T) { func TestDefaultForceIndex(t *testing.T) { closing := []float64{9, 11, 7, 10, 8} - volume := []int64{100, 110, 80, 120, 90} + volume := []float64{100, 110, 80, 120, 90} expected := []float64{900, 802.86, 642.45, 602.1, 490.37} actual := roundDigitsAll(DefaultForceIndex(closing, volume), 2) @@ -53,7 +53,7 @@ func TestDefaultForceIndex(t *testing.T) { func TestDefaultEaseOfMovement(t *testing.T) { high := []float64{10, 9, 12, 14, 12} low := []float64{6, 7, 9, 12, 10} - volume := []int64{100, 110, 80, 120, 90} + volume := []float64{100, 110, 80, 120, 90} expected := []float64{32000000, 16000000, 13791666.67, 11385416.67, 8219444.44} actual := roundDigitsAll(DefaultEaseOfMovement(high, low, volume), 2) @@ -62,7 +62,7 @@ func TestDefaultEaseOfMovement(t *testing.T) { func TestVolumePriceTrend(t *testing.T) { closing := []float64{9, 11, 7, 10, 8} - volume := []int64{100, 110, 80, 120, 90} + volume := []float64{100, 110, 80, 120, 90} expected := []float64{0, 24.44, -4.65, 46.78, 28.78} actual := roundDigitsAll(VolumePriceTrend(closing, volume), 2) @@ -71,7 +71,7 @@ func TestVolumePriceTrend(t *testing.T) { func TestVolumeWeightedAveragePrice(t *testing.T) { closing := []float64{9, 11, 7, 10, 8} - volume := []int64{100, 110, 80, 120, 90} + volume := []float64{100, 110, 80, 120, 90} period := 2 expected := []float64{9, 10.05, 9.32, 8.8, 9.14} @@ -81,7 +81,7 @@ func TestVolumeWeightedAveragePrice(t *testing.T) { func TestDefaultVolumeWeightedAveragePrice(t *testing.T) { closing := []float64{9, 11, 7, 10, 8} - volume := []int64{100, 110, 80, 120, 90} + volume := []float64{100, 110, 80, 120, 90} expected := []float64{9, 10.05, 9.21, 9.44, 9.18} actual := roundDigitsAll(DefaultVolumeWeightedAveragePrice(closing, volume), 2) @@ -90,7 +90,7 @@ func TestDefaultVolumeWeightedAveragePrice(t *testing.T) { func TestNegativeVolumeIndex(t *testing.T) { closing := []float64{9, 11, 7, 10, 8} - volume := []int64{100, 110, 80, 120, 90} + volume := []float64{100, 110, 80, 120, 90} expected := []float64{1000, 1000, 636.36, 636.36, 509.09} actual := roundDigitsAll(NegativeVolumeIndex(closing, volume), 2) @@ -100,7 +100,7 @@ func TestNegativeVolumeIndex(t *testing.T) { func TestChaikinMoneyFlow(t *testing.T) { high := []float64{10, 9, 12, 14, 12} low := []float64{6, 7, 9, 12, 10} - volume := []int64{100, 110, 80, 120, 90} + volume := []float64{100, 110, 80, 120, 90} closing := []float64{9, 11, 7, 10, 8} expected := []float64{0.5, 1.81, 0.67, -0.41, -0.87}