├── settings.gradle.kts ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .travis.yml ├── src ├── test │ ├── kotlin │ │ └── vista │ │ │ ├── data │ │ │ └── LoaderTest.kt │ │ │ ├── indicators │ │ │ ├── SumTest.kt │ │ │ ├── StochasticRelativeStrengthIndexTest.kt │ │ │ ├── LowestTest.kt │ │ │ ├── HighestTest.kt │ │ │ ├── MomentumTest.kt │ │ │ ├── DeviationTest.kt │ │ │ ├── HullMovingAverageTest.kt │ │ │ ├── OnBalanceVolumeTest.kt │ │ │ ├── WeightedMovingAverageTest.kt │ │ │ ├── StandardDeviationTest.kt │ │ │ ├── CommodityChannelIndexTest.kt │ │ │ ├── UltimateOscillatorTest.kt │ │ │ ├── RelativeStrengthIndexTest.kt │ │ │ ├── RateOfChangeTest.kt │ │ │ ├── VolumeWeightedMovingAverageTest.kt │ │ │ ├── AwesomeOscillatorTest.kt │ │ │ ├── WilliamsPercentRangeTest.kt │ │ │ ├── AccumulationDistributionTest.kt │ │ │ ├── ChaikinOscillatorTest.kt │ │ │ ├── SimpleMovingAverageTest.kt │ │ │ ├── StochasticOscillatorTest.kt │ │ │ ├── AverageTrueRangeTest.kt │ │ │ ├── MovingAverageConvergenceDivergenceTest.kt │ │ │ ├── IchimokuCloudTest.kt │ │ │ ├── ExponentialMovingAverageTest.kt │ │ │ ├── BollingerBandTest.kt │ │ │ ├── ElderRayIndexTest.kt │ │ │ └── IndicatorTest.kt │ │ │ ├── rules │ │ │ └── RulesTest.kt │ │ │ └── Data.kt │ └── resources │ │ ├── roc.csv │ │ ├── rsi.csv │ │ ├── uo.csv │ │ ├── dev.csv │ │ ├── stdev.csv │ │ ├── cci.csv │ │ ├── ao.csv │ │ ├── wpr.csv │ │ ├── mom.csv │ │ ├── ema.csv │ │ ├── hma.csv │ │ ├── sma.csv │ │ ├── vwma.csv │ │ ├── wma.csv │ │ ├── co.csv │ │ ├── stochrsi.csv │ │ ├── obv.csv │ │ ├── stoch.csv │ │ ├── adl.csv │ │ ├── atr.csv │ │ ├── macd.csv │ │ ├── eri.csv │ │ ├── bb.csv │ │ └── ichimoku.csv └── main │ └── kotlin │ └── vista │ ├── data │ └── Loader.kt │ ├── indicators │ ├── Sum.kt │ ├── AwesomeOscillator.kt │ ├── Momentum.kt │ ├── WilliamsPercentRange.kt │ ├── ElderRayIndex.kt │ ├── UltimateOscillator.kt │ ├── Lowest.kt │ ├── Highest.kt │ ├── CommodityChannelIndex.kt │ ├── IchimokuCloud.kt │ ├── VolumeWeightedMovingAverage.kt │ ├── HullMovingAverage.kt │ ├── RateOfChange.kt │ ├── Deviation.kt │ ├── StandardDeviation.kt │ ├── BollingerBand.kt │ ├── SimpleMovingAverage.kt │ ├── ChaikinOscillator.kt │ ├── WeightedMovingAverage.kt │ ├── StochasticOscillator.kt │ ├── OnBalanceVolume.kt │ ├── AccumulationDistribution.kt │ ├── StochasticRelativeStrengthIndex.kt │ ├── Indicator.kt │ ├── MovingAverageConvergenceDivergence.kt │ ├── RelativeStrengthIndex.kt │ ├── ExponentialMovingAverage.kt │ └── AverageTrueRange.kt │ └── rules │ └── Rules.kt ├── Module.md ├── .gitignore ├── LICENSE ├── gradlew.bat └── README.md /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "vista" 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pallocchi/vista-kt/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - openjdk9 4 | before_install: 5 | - chmod +x gradlew 6 | - chmod +x gradle/wrapper/gradle-wrapper.jar 7 | script: 8 | - ./gradlew test build 9 | - ./gradlew jacocoTestReport 10 | after_success: 11 | - bash <(curl -s https://codecov.io/bash) 12 | deploy: 13 | provider: script 14 | script: ./gradlew bintrayUpload 15 | skip_cleanup: true 16 | dry-run: false 17 | on: 18 | tags: true -------------------------------------------------------------------------------- /src/test/kotlin/vista/data/LoaderTest.kt: -------------------------------------------------------------------------------- 1 | package vista.data 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.junit.jupiter.api.Test 5 | import vista.math.numOf 6 | 7 | 8 | internal class LoaderTest { 9 | 10 | @Test 11 | fun withLocalURL() { 12 | val url = javaClass.classLoader.getResource("amzn.csv") 13 | val data = dataOf(url!!.toString()) 14 | 15 | assertThat(data.open[0]).isEqualTo(numOf(2415.94)) 16 | assertThat(data.high[0]).isEqualTo(numOf(2442.37)) 17 | assertThat(data.low[0]).isEqualTo(numOf(2398.20)) 18 | assertThat(data.close[0]).isEqualTo(numOf(2442.37)) 19 | assertThat(data.volume[0]).isEqualTo(numOf(3529329)) 20 | } 21 | } -------------------------------------------------------------------------------- /src/main/kotlin/vista/data/Loader.kt: -------------------------------------------------------------------------------- 1 | package vista.data 2 | 3 | import java.net.URL 4 | 5 | /** 6 | * Loads the market data from a CSV at given [url]. 7 | * 8 | * The expected columns are date, open, high, low, close and volume. 9 | * 10 | * @param url URL where the CSV can be found. 11 | * @param reversed If bars should be reversed or not 12 | */ 13 | fun dataOf(url: String, reversed: Boolean = true): Data { 14 | val bars = mutableListOf() 15 | URL(url).readText().lines().drop(1).forEach { 16 | val split = it.split(",") 17 | val bar = Data.Bar(split[0], split[1], split[2], split[3], split[4], split[5]) 18 | bars.add(bar) 19 | } 20 | return Data(if (reversed) bars.asReversed() else bars) 21 | } -------------------------------------------------------------------------------- /Module.md: -------------------------------------------------------------------------------- 1 | # Module vista 2 | 3 | Technical Analysis for Kotlin. 4 | 5 | ## Introduction 6 | 7 | This library was inspired by [Pine Script](https://www.tradingview.com/pine-script-docs/en/v4/Introduction.html) from [Trading View](https://www.tradingview.com/). 8 | 9 | # Package vista.rules 10 | 11 | Contains the rules, such `cross`, `crossover`, `crossunder`. 12 | 13 | # Package vista.series 14 | 15 | Contains the series. 16 | 17 | # Package vista.data 18 | 19 | Contains the market data series, such `open`, `high`, `low`, and `close` prices. 20 | 21 | # Package vista.indicators 22 | 23 | Contains the indicators, such `sma`, `rsi`, `macd`. 24 | 25 | # Package vista.math 26 | 27 | Contains the number implementations and related functions, such `min` or `max`. 28 | -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/Sum.kt: -------------------------------------------------------------------------------- 1 | package vista.indicators 2 | 3 | import vista.math.Num 4 | import vista.series.Series 5 | 6 | /** 7 | * Sum helper indicator. 8 | */ 9 | internal class Sum( 10 | private val source: Series, 11 | private val n: Int 12 | ) : Indicator(source) { 13 | 14 | override val size: Int get() = source.size + 1 - n 15 | 16 | override fun calculate(i: Int): Num { 17 | var sum = Num.ZERO 18 | for (j in 0 until n) 19 | sum += source[i + j] 20 | return sum 21 | } 22 | } 23 | 24 | /** 25 | * The sum of last [n] values of [source]. 26 | * 27 | * @param source Series of values to process 28 | * @param n Number of bars (length) 29 | * @sample vista.indicators.SumTest.withIntSeries 30 | */ 31 | fun sum(source: Series, n: Int = 9): Series = if (n > 1) Sum(source, n) else source 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea/shelf 3 | /confluence/target 4 | /dependencies/repo 5 | /android.tests.dependencies 6 | /dependencies/android.tests.dependencies 7 | /dist 8 | /local 9 | /gh-pages 10 | /ideaSDK 11 | /clionSDK 12 | /android-studio/sdk 13 | out/ 14 | /tmp 15 | workspace.xml 16 | *.versionsBackup 17 | /idea/testData/debugger/tinyApp/classes* 18 | /jps-plugin/testData/kannotator 19 | /js/js.translator/testData/out/ 20 | /js/js.translator/testData/out-min/ 21 | /js/js.translator/testData/out-pir/ 22 | .gradle/ 23 | build/ 24 | !**/src/**/build 25 | !**/test/**/build 26 | *.iml 27 | !**/testData/**/*.iml 28 | .idea/ 29 | kotlin-ultimate/ 30 | node_modules/ 31 | .rpt2_cache/ 32 | libraries/tools/kotlin-test-js-runner/lib/ 33 | libraries/tools/kotlin-source-map-loader/lib/ 34 | local.properties 35 | .gradle 36 | gradle-app.setting 37 | !gradle-wrapper.jar 38 | .gradletasknamecache 39 | !.idea/dictionaries -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/AwesomeOscillator.kt: -------------------------------------------------------------------------------- 1 | package vista.indicators 2 | 3 | import vista.data.Data 4 | import vista.series.Series 5 | 6 | /** 7 | * The awesome oscillator, used to measure market momentum. 8 | * 9 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/momentum?id=awesome-oscillator-ao) 10 | * 11 | * @param high Series of high values 12 | * @param low Series of low values 13 | * @sample vista.indicators.AwesomeOscillatorTest.withIntSeries 14 | */ 15 | fun ao(high: Series, low: Series): Series { 16 | val midpoints = (high + low) / 2 17 | return sma(midpoints, 5) - sma(midpoints, 34) 18 | } 19 | 20 | /** 21 | * The awesome oscillator, used to measure market momentum. 22 | * 23 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/momentum?id=awesome-oscillator-ao) 24 | * 25 | * @sample vista.indicators.AwesomeOscillatorTest.withMarketData 26 | */ 27 | fun Data.ao() = ao(high, low) -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/Momentum.kt: -------------------------------------------------------------------------------- 1 | package vista.indicators 2 | 3 | import vista.data.Data 4 | import vista.math.change 5 | import vista.series.Series 6 | 7 | /** 8 | * The momentum indicator, which compares the current price with the previous price from [n] periods ago. 9 | * 10 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/momentum?id=momentum-indicator-mom) 11 | * 12 | * @param source Series of values to process 13 | * @param n Number of bars (length) 14 | * @sample vista.indicators.MomentumTest.withIntSeries 15 | */ 16 | fun mom(source: Series, n: Int = 10) = change(source, n) 17 | 18 | /** 19 | * The momentum indicator, which compares the current price with the previous price from [n] periods ago. 20 | * 21 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/momentum?id=momentum-indicator-mom) 22 | * 23 | * @param n Number of bars (length) 24 | * @sample vista.indicators.MomentumTest.withMarketData 25 | */ 26 | fun Data.mom(n: Int = 10) = mom(close, n) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Pablo Pallocchi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/WilliamsPercentRange.kt: -------------------------------------------------------------------------------- 1 | package vista.indicators 2 | 3 | import vista.data.Data 4 | import vista.series.Series 5 | 6 | /** 7 | * The Williams percent range (%R), which measures overbought and oversold levels. 8 | * 9 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/momentum?id=williams-r) 10 | * 11 | * @param close Series of close values 12 | * @param high Series of high values 13 | * @param low Series of low values 14 | * @param n Number of bars (length) 15 | * @sample vista.indicators.WilliamsPercentRangeTest.withIntSeries 16 | */ 17 | fun wpr(close: Series, high: Series, low: Series, n: Int = 14): Series { 18 | val highest = highest(high, n) 19 | val lowest = lowest(low, n) 20 | return (highest - close) / (highest - lowest) * -100 21 | } 22 | 23 | /** 24 | * The Williams percent range (%R), which measures overbought and oversold levels. 25 | * 26 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/momentum?id=williams-r) 27 | * 28 | * @param n Number of bars (length) 29 | * @sample vista.indicators.WilliamsPercentRangeTest.withMarketData 30 | */ 31 | fun Data.wpr(n: Int = 14) = wpr(close, high, low, n) -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/ElderRayIndex.kt: -------------------------------------------------------------------------------- 1 | package vista.indicators 2 | 3 | import vista.data.Data 4 | import vista.series.Series 5 | 6 | /** 7 | * The elder-ray index, which help traders determine the trend direction and isolate spots to enter and exit trades. 8 | * 9 | * Returns a triple of bull-bear power, bull power and bear power. 10 | * 11 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/trend?id=elder-ray-index) 12 | * 13 | * @param close Series of close values 14 | * @param high Series of high values 15 | * @param low Series of low values 16 | * @param n Number of bars (length) 17 | * @sample vista.indicators.ElderRayIndexTest.withIntSeries 18 | */ 19 | fun eri( 20 | close: Series, 21 | high: Series, 22 | low: Series, 23 | n: Int = 13 24 | ): Triple { 25 | val ema = ema(close, n) 26 | val bullPower = high - ema 27 | val bearPower = low - ema 28 | val bullBearPower = bullPower + bearPower 29 | return Triple(bullBearPower, bullPower, bearPower) 30 | } 31 | 32 | /** 33 | * The elder-ray index, which help traders determine the trend direction and isolate spots to enter and exit trades. 34 | * 35 | * Returns a triple of bull-bear power, bull power and bear power. 36 | * 37 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/trend?id=elder-ray-index) 38 | * 39 | * @param n Number of bars (length) 40 | * @sample vista.indicators.ElderRayIndexTest.withMarketData 41 | */ 42 | fun Data.eri(n: Int = 13) = eri(close, high, low, n) -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/UltimateOscillator.kt: -------------------------------------------------------------------------------- 1 | package vista.indicators 2 | 3 | import vista.data.Data 4 | import vista.math.min 5 | import vista.series.Series 6 | 7 | /** 8 | * The ultimate oscillator, which measures momentum across three varying time frames. 9 | * 10 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/momentum?id=ultimate-oscillator-uo) 11 | * 12 | * @param close Series of close values 13 | * @param high Series of high values 14 | * @param low Series of low values 15 | * @param n1 Number of bars (length) for first time frame 16 | * @param n2 Number of bars (length) for second time frame 17 | * @param n3 Number of bars (length) for third time frame 18 | * @sample vista.indicators.UltimateOscillatorTest.withIntSeries 19 | */ 20 | fun uo( 21 | close: Series, 22 | high: Series, 23 | low: Series, 24 | n1: Int = 7, 25 | n2: Int = 14, 26 | n3: Int = 28 27 | ): Series { 28 | val bp = close - min(low, close(1)) 29 | val tr = tr(close, high, low) 30 | val avg1 = sum(bp, n1) / sum(tr, n1) 31 | val avg2 = sum(bp, n2) / sum(tr, n2) 32 | val avg3 = sum(bp, n3) / sum(tr, n3) 33 | return (((avg1 * 4) + (avg2 * 2) + avg3) / 7) * 100 34 | } 35 | 36 | /** 37 | * The ultimate oscillator, which measures momentum across three varying time frames. 38 | * 39 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/momentum?id=ultimate-oscillator-uo) 40 | * 41 | * @param n1 Number of bars (length) for first time frame 42 | * @param n2 Number of bars (length) for second time frame 43 | * @param n3 Number of bars (length) for third time frame 44 | * @sample vista.indicators.UltimateOscillatorTest.withMarketData 45 | */ 46 | fun Data.uo(n1: Int = 7, n2: Int = 14, n3: Int = 28) = uo(close, high, low, n1, n2, n3) -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/SumTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.math.na 31 | import vista.math.numOf 32 | import vista.series.seriesOf 33 | 34 | internal class SumTest { 35 | 36 | @Test 37 | fun withIntSeries() { 38 | val series = seriesOf(1, 2, 3) 39 | 40 | val sum = sum(series, 2) 41 | 42 | assertThat(sum[0]).isEqualTo(numOf(5)) // current value 43 | assertThat(sum[1]).isEqualTo(numOf(3)) // previous value 44 | assertThat(sum[2]).isEqualTo(na) // oldest value 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/StochasticRelativeStrengthIndexTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.loadAmazonData 31 | import vista.loadIndicatorData 32 | 33 | internal class StochasticRelativeStrengthIndexTest { 34 | 35 | @Test 36 | fun withMarketData() { 37 | 38 | val data = loadAmazonData() 39 | val expected = loadIndicatorData("stochrsi.csv") 40 | 41 | val (k, d) = data.stochrsi() 42 | 43 | for (i in 0..99) { 44 | assertThat(k[i].round(2)).isEqualTo(expected[i][0]) 45 | assertThat(d[i].round(2)).isEqualTo(expected[i][1]) 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/LowestTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.math.na 31 | import vista.math.numOf 32 | import vista.series.seriesOf 33 | 34 | internal class LowestTest { 35 | 36 | @Test 37 | fun lowest() { 38 | val series = seriesOf(1, 3, 2) 39 | 40 | assertThat(lowest(series, 2)[0]).isEqualTo(numOf(2)) 41 | assertThat(lowest(series, 2)[1]).isEqualTo(numOf(1)) 42 | assertThat(lowest(series, 2)[2]).isEqualTo(na) 43 | 44 | assertThat(lowest(series, 3)[0]).isEqualTo(numOf(1)) 45 | assertThat(lowest(series, 3)[1]).isEqualTo(na) 46 | assertThat(lowest(series, 3)[2]).isEqualTo(na) 47 | } 48 | } -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/HighestTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.math.na 31 | import vista.math.numOf 32 | import vista.series.seriesOf 33 | 34 | internal class HighestTest { 35 | 36 | @Test 37 | fun highest() { 38 | val series = seriesOf(4, 3, 2) 39 | 40 | assertThat(highest(series, 2)[0]).isEqualTo(numOf(3)) 41 | assertThat(highest(series, 2)[1]).isEqualTo(numOf(4)) 42 | assertThat(highest(series, 2)[2]).isEqualTo(na) 43 | 44 | assertThat(highest(series, 3)[0]).isEqualTo(numOf(4)) 45 | assertThat(highest(series, 3)[1]).isEqualTo(na) 46 | assertThat(highest(series, 3)[2]).isEqualTo(na) 47 | } 48 | } -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/Lowest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import vista.math.Num 29 | import vista.math.min 30 | import vista.series.Series 31 | 32 | /** 33 | * Lowest indicator. 34 | */ 35 | internal class Lowest( 36 | private val source: Series, 37 | private val n: Int 38 | ) : CachedIndicator(source) { 39 | 40 | override val size: Int get() = source.size + 1 - n 41 | 42 | override fun calculate(i: Int): Num { 43 | var lowest = source[i] 44 | for (j in 1 until n) 45 | lowest = min(lowest, source[i + j]) 46 | return lowest 47 | } 48 | } 49 | 50 | /** 51 | * The lowest value for [n] bars back. 52 | * 53 | * @param source Series of values to process 54 | * @param n Number of bars (length) 55 | * @sample vista.indicators.LowestTest.lowest 56 | */ 57 | fun lowest(source: Series, n: Int): Series = Lowest(source, n) -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/Highest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import vista.math.Num 29 | import vista.math.max 30 | import vista.series.Series 31 | 32 | /** 33 | * Highest indicator. 34 | */ 35 | internal class Highest( 36 | private val source: Series, 37 | private val n: Int 38 | ) : CachedIndicator(source) { 39 | 40 | override val size: Int get() = source.size + 1 - n 41 | 42 | override fun calculate(i: Int): Num { 43 | var highest = source[i] 44 | for (j in 1 until n) 45 | highest = max(highest, source[i + j]) 46 | return highest 47 | } 48 | } 49 | 50 | /** 51 | * The highest value for [n] bars back. 52 | * 53 | * @param source Series of values to process 54 | * @param n Number of bars (length) 55 | * @sample vista.indicators.HighestTest.highest 56 | */ 57 | fun highest(source: Series, n: Int): Series = Highest(source, n) -------------------------------------------------------------------------------- /src/main/kotlin/vista/rules/Rules.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.rules 27 | 28 | import vista.series.Series 29 | import vista.series.cross 30 | import vista.series.crossOver 31 | import vista.series.crossUnder 32 | 33 | /** 34 | * Returns if two series has crossed each other (same as `x cross y`). 35 | * 36 | * @see Series.cross 37 | * @sample vista.rules.RulesTest.cross 38 | */ 39 | fun cross(x: Series, y: Series) = x cross y 40 | 41 | /** 42 | * Returns if [x] series has crossed over the [y] series (same as `x crossOver y`). 43 | * 44 | * @see Series.crossOver 45 | * @sample vista.rules.RulesTest.crossover 46 | */ 47 | fun crossover(x: Series, y: Series) = x crossOver y 48 | 49 | /** 50 | * Returns if [x] series has crossed under the [y] series (same as `x crossUnder y`). 51 | * 52 | * @see Series.crossUnder 53 | * @sample vista.rules.RulesTest.crossunder 54 | */ 55 | fun crossunder(x: Series, y: Series) = x crossUnder y 56 | -------------------------------------------------------------------------------- /src/test/resources/roc.csv: -------------------------------------------------------------------------------- 1 | date,roc(9) 2 | 05/29/2020,1.35 3 | 05/28/2020,0.51 4 | 05/27/2020,1.79 5 | 05/26/2020,2.75 6 | 05/22/2020,1.16 7 | 05/21/2020,2.82 8 | 05/20/2020,5.50 9 | 05/19/2020,4.17 10 | 05/18/2020,4.68 11 | 05/15/2020,4.05 12 | 05/14/2020,4.50 13 | 05/13/2020,-4.29 14 | 05/12/2020,-0.66 15 | 05/11/2020,4.10 16 | 05/08/2020,0.15 17 | 05/07/2020,-1.77 18 | 05/06/2020,-2.01 19 | 05/05/2020,-1.93 20 | 05/04/2020,-0.52 21 | 05/01/2020,-4.49 22 | 04/30/2020,4.17 23 | 04/29/2020,-1.47 24 | 04/28/2020,0.28 25 | 04/27/2020,4.06 26 | 04/24/2020,11.13 27 | 04/23/2020,17.46 28 | 04/22/2020,15.69 29 | 04/21/2020,15.73 30 | 04/20/2020,19.82 31 | 04/17/2020,24.57 32 | 04/16/2020,25.50 33 | 04/15/2020,20.97 34 | 04/14/2020,17.11 35 | 04/13/2020,10.43 36 | 04/09/2020,7.51 37 | 04/08/2020,4.48 38 | 04/07/2020,6.67 39 | 04/06/2020,2.96 40 | 04/03/2020,0.20 41 | 04/02/2020,3.94 42 | 04/01/2020,1.42 43 | 03/31/2020,6.54 44 | 03/30/2020,8.64 45 | 03/27/2020,12.49 46 | 03/26/2020,9.55 47 | 03/25/2020,12.48 48 | 03/24/2020,6.55 49 | 03/23/2020,0.58 50 | 03/20/2020,2.53 51 | 03/19/2020,-1.06 52 | 03/18/2020,-4.89 53 | 03/17/2020,-8.50 54 | 03/16/2020,-11.52 55 | 03/13/2020,-8.65 56 | 03/12/2020,-11.00 57 | 03/11/2020,-3.37 58 | 03/10/2020,-4.43 59 | 03/09/2020,-8.73 60 | 03/06/2020,-5.38 61 | 03/05/2020,-8.20 62 | 03/04/2020,-8.23 63 | 03/03/2020,-12.04 64 | 03/02/2020,-9.36 65 | 02/28/2020,-11.76 66 | 02/27/2020,-12.35 67 | 02/26/2020,-8.35 68 | 02/25/2020,-8.28 69 | 02/24/2020,-5.84 70 | 02/21/2020,0.80 71 | 02/20/2020,5.02 72 | 02/19/2020,6.39 73 | 02/18/2020,5.17 74 | 02/14/2020,6.52 75 | 02/13/2020,7.03 76 | 02/12/2020,15.47 77 | 02/11/2020,15.76 78 | 02/10/2020,15.14 79 | 02/07/2020,13.73 80 | 02/06/2020,10.13 81 | 02/05/2020,8.24 82 | 02/04/2020,8.59 83 | 02/03/2020,5.93 84 | 01/31/2020,7.72 85 | 01/30/2020,-0.39 86 | 01/29/2020,-0.22 87 | 01/28/2020,-0.87 88 | 01/27/2020,-3.33 89 | 01/24/2020,-1.14 90 | 01/23/2020,-0.87 91 | 01/22/2020,-0.24 92 | 01/21/2020,-0.78 93 | 01/17/2020,-2.01 94 | 01/16/2020,0.16 95 | 01/15/2020,-1.90 96 | 01/14/2020,1.17 97 | 01/13/2020,2.40 98 | 01/10/2020,0.71 99 | 01/09/2020,1.73 100 | 01/08/2020,5.74 101 | 01/07/2020,6.35 -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/MomentumTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.loadAmazonData 31 | import vista.loadIndicatorData 32 | import vista.math.na 33 | import vista.math.numOf 34 | import vista.series.seriesOf 35 | 36 | internal class MomentumTest { 37 | 38 | @Test 39 | fun withIntSeries() { 40 | val series = seriesOf(1, 2, 4) 41 | 42 | val sma = mom(series, 1) 43 | 44 | assertThat(sma[0]).isEqualTo(numOf(2)) // current value 45 | assertThat(sma[1]).isEqualTo(numOf(1)) // previous value 46 | assertThat(sma[2]).isEqualTo(na) // oldest value 47 | } 48 | 49 | @Test 50 | fun withMarketData() { 51 | val data = loadAmazonData() 52 | val expected = loadIndicatorData("mom.csv") 53 | 54 | val actual = data.mom(10) 55 | 56 | for (i in 0..99) 57 | assertThat(actual[i].round(2)).isEqualTo(expected[i][0]) 58 | } 59 | } -------------------------------------------------------------------------------- /src/test/resources/rsi.csv: -------------------------------------------------------------------------------- 1 | date,rsi(14) 2 | 05/29/2020,58.90 3 | 05/28/2020,54.91 4 | 05/27/2020,56.05 5 | 05/26/2020,57.41 6 | 05/22/2020,59.16 7 | 05/21/2020,60.28 8 | 05/20/2020,66.32 9 | 05/19/2020,63.06 10 | 05/18/2020,61.41 11 | 05/15/2020,60.23 12 | 05/14/2020,58.74 13 | 05/13/2020,57.26 14 | 05/12/2020,56.50 15 | 05/11/2020,61.31 16 | 05/08/2020,59.50 17 | 05/07/2020,58.77 18 | 05/06/2020,57.81 19 | 05/05/2020,55.85 20 | 05/04/2020,55.74 21 | 05/01/2020,54.10 22 | 04/30/2020,69.09 23 | 04/29/2020,64.12 24 | 04/28/2020,60.71 25 | 04/27/2020,66.94 26 | 04/24/2020,70.65 27 | 04/23/2020,70.17 28 | 04/22/2020,68.56 29 | 04/21/2020,66.93 30 | 04/20/2020,73.47 31 | 04/17/2020,72.77 32 | 04/16/2020,76.10 33 | 04/15/2020,72.57 34 | 04/14/2020,71.63 35 | 04/13/2020,66.61 36 | 04/09/2020,59.24 37 | 04/08/2020,59.26 38 | 04/07/2020,57.23 39 | 04/06/2020,56.33 40 | 04/03/2020,49.97 41 | 04/02/2020,50.90 42 | 04/01/2020,50.12 43 | 03/31/2020,53.07 44 | 03/30/2020,54.08 45 | 03/27/2020,50.15 46 | 03/26/2020,53.86 47 | 03/25/2020,49.50 48 | 03/24/2020,53.13 49 | 03/23/2020,50.83 50 | 03/20/2020,47.17 51 | 03/19/2020,49.26 52 | 03/18/2020,46.01 53 | 03/17/2020,44.58 54 | 03/16/2020,36.14 55 | 03/13/2020,40.80 56 | 03/12/2020,31.52 57 | 03/11/2020,39.09 58 | 03/10/2020,43.90 59 | 03/09/2020,34.24 60 | 03/06/2020,41.56 61 | 03/05/2020,43.54 62 | 03/04/2020,48.35 63 | 03/03/2020,40.46 64 | 03/02/2020,44.73 65 | 02/28/2020,34.75 66 | 02/27/2020,34.79 67 | 02/26/2020,44.13 68 | 02/25/2020,43.11 69 | 02/24/2020,47.40 70 | 02/21/2020,60.68 71 | 02/20/2020,73.24 72 | 02/19/2020,77.72 73 | 02/18/2020,76.59 74 | 02/14/2020,74.90 75 | 02/13/2020,78.70 76 | 02/12/2020,81.29 77 | 02/11/2020,80.76 78 | 02/10/2020,79.77 79 | 02/07/2020,76.11 80 | 02/06/2020,73.76 81 | 02/05/2020,72.88 82 | 02/04/2020,75.09 83 | 02/03/2020,71.33 84 | 01/31/2020,72.34 85 | 01/30/2020,53.83 86 | 01/29/2020,51.04 87 | 01/28/2020,49.98 88 | 01/27/2020,44.13 89 | 01/24/2020,51.63 90 | 01/23/2020,57.93 91 | 01/22/2020,58.76 92 | 01/21/2020,60.03 93 | 01/17/2020,54.56 94 | 01/16/2020,58.14 95 | 01/15/2020,54.83 96 | 01/14/2020,56.77 97 | 01/13/2020,62.87 98 | 01/10/2020,61.44 99 | 01/09/2020,66.69 100 | 01/08/2020,65.30 101 | 01/07/2020,69.75 -------------------------------------------------------------------------------- /src/test/resources/uo.csv: -------------------------------------------------------------------------------- 1 | date,uo(7,14,28) 2 | 05/29/2020,52.40 3 | 05/28/2020,47.81 4 | 05/27/2020,51.76 5 | 05/26/2020,49.74 6 | 05/22/2020,55.17 7 | 05/21/2020,57.89 8 | 05/20/2020,58.05 9 | 05/19/2020,57.29 10 | 05/18/2020,60.89 11 | 05/15/2020,58.08 12 | 05/14/2020,54.68 13 | 05/13/2020,49.43 14 | 05/12/2020,54.90 15 | 05/11/2020,48.63 16 | 05/08/2020,51.61 17 | 05/07/2020,52.42 18 | 05/06/2020,49.00 19 | 05/05/2020,45.93 20 | 05/04/2020,48.17 21 | 05/01/2020,47.01 22 | 04/30/2020,59.03 23 | 04/29/2020,49.34 24 | 04/28/2020,44.93 25 | 04/27/2020,50.35 26 | 04/24/2020,57.60 27 | 04/23/2020,57.86 28 | 04/22/2020,61.81 29 | 04/21/2020,64.51 30 | 04/20/2020,69.28 31 | 04/17/2020,74.22 32 | 04/16/2020,72.80 33 | 04/15/2020,77.53 34 | 04/14/2020,75.71 35 | 04/13/2020,72.84 36 | 04/09/2020,63.59 37 | 04/08/2020,57.08 38 | 04/07/2020,57.60 39 | 04/06/2020,54.97 40 | 04/03/2020,52.84 41 | 04/02/2020,47.79 42 | 04/01/2020,48.89 43 | 03/31/2020,53.92 44 | 03/30/2020,50.84 45 | 03/27/2020,47.77 46 | 03/26/2020,53.75 47 | 03/25/2020,52.50 48 | 03/24/2020,53.63 49 | 03/23/2020,56.29 50 | 03/20/2020,47.79 51 | 03/19/2020,50.84 52 | 03/18/2020,55.12 53 | 03/17/2020,48.90 54 | 03/16/2020,45.38 55 | 03/13/2020,44.76 56 | 03/12/2020,40.18 57 | 03/11/2020,45.15 58 | 03/10/2020,52.24 59 | 03/09/2020,49.61 60 | 03/06/2020,49.39 61 | 03/05/2020,47.61 62 | 03/04/2020,47.50 63 | 03/03/2020,39.82 64 | 03/02/2020,41.05 65 | 02/28/2020,35.33 66 | 02/27/2020,28.89 67 | 02/26/2020,36.92 68 | 02/25/2020,36.26 69 | 02/24/2020,39.46 70 | 02/21/2020,43.25 71 | 02/20/2020,49.23 72 | 02/19/2020,55.07 73 | 02/18/2020,56.71 74 | 02/14/2020,56.60 75 | 02/13/2020,55.97 76 | 02/12/2020,59.76 77 | 02/11/2020,57.71 78 | 02/10/2020,63.66 79 | 02/07/2020,61.17 80 | 02/06/2020,58.51 81 | 02/05/2020,58.71 82 | 02/04/2020,59.31 83 | 02/03/2020,54.34 84 | 01/31/2020,59.60 85 | 01/30/2020,46.92 86 | 01/29/2020,47.46 87 | 01/28/2020,47.42 88 | 01/27/2020,45.29 89 | 01/24/2020,48.97 90 | 01/23/2020,50.71 91 | 01/22/2020,51.90 92 | 01/21/2020,51.22 93 | 01/17/2020,42.64 94 | 01/16/2020,42.37 95 | 01/15/2020,44.01 96 | 01/14/2020,52.61 97 | 01/13/2020,53.33 98 | 01/10/2020,58.54 99 | 01/09/2020,63.94 100 | 01/08/2020,60.58 101 | 01/07/2020,60.16 -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/DeviationTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.loadAmazonData 31 | import vista.loadIndicatorData 32 | import vista.math.na 33 | import vista.math.numOf 34 | import vista.series.seriesOf 35 | 36 | internal class DeviationTest { 37 | 38 | @Test 39 | fun withIntSeries() { 40 | val series = seriesOf(1, 2, 3) 41 | 42 | val dev = dev(series, 2) 43 | 44 | assertThat(dev[0]).isEqualTo(numOf(.5)) // current value 45 | assertThat(dev[1]).isEqualTo(numOf(.5)) // previous value 46 | assertThat(dev[2]).isEqualTo(na) // oldest value 47 | } 48 | 49 | @Test 50 | fun withMarketData() { 51 | val data = loadAmazonData() 52 | val expected = loadIndicatorData("dev.csv") 53 | 54 | val actual = data.dev(20) 55 | 56 | for (i in 0..99) 57 | assertThat(actual[i].round(2)).isEqualTo(expected[i][0]) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/test/resources/dev.csv: -------------------------------------------------------------------------------- 1 | date,dev(20) 2 | 05/29/2020,41.56 3 | 05/28/2020,42.99 4 | 05/27/2020,43.87 5 | 05/26/2020,46.60 6 | 05/22/2020,45.36 7 | 05/21/2020,43.90 8 | 05/20/2020,41.30 9 | 05/19/2020,35.51 10 | 05/18/2020,33.83 11 | 05/15/2020,32.20 12 | 05/14/2020,30.54 13 | 05/13/2020,31.43 14 | 05/12/2020,34.67 15 | 05/11/2020,38.90 16 | 05/08/2020,47.53 17 | 05/07/2020,63.06 18 | 05/06/2020,77.09 19 | 05/05/2020,96.20 20 | 05/04/2020,115.44 21 | 05/01/2020,140.83 22 | 04/30/2020,162.40 23 | 04/29/2020,173.46 24 | 04/28/2020,181.10 25 | 04/27/2020,186.72 26 | 04/24/2020,189.81 27 | 04/23/2020,184.65 28 | 04/22/2020,178.37 29 | 04/21/2020,167.40 30 | 04/20/2020,155.78 31 | 04/17/2020,137.85 32 | 04/16/2020,118.80 33 | 04/15/2020,97.98 34 | 04/14/2020,81.10 35 | 04/13/2020,72.60 36 | 04/09/2020,64.87 37 | 04/08/2020,71.81 38 | 04/07/2020,69.63 39 | 04/06/2020,65.44 40 | 04/03/2020,64.79 41 | 04/02/2020,64.57 42 | 04/01/2020,64.77 43 | 03/31/2020,67.50 44 | 03/30/2020,65.87 45 | 03/27/2020,65.47 46 | 03/26/2020,64.82 47 | 03/25/2020,61.97 48 | 03/24/2020,65.72 49 | 03/23/2020,67.02 50 | 03/20/2020,71.28 51 | 03/19/2020,77.79 52 | 03/18/2020,90.23 53 | 03/17/2020,100.66 54 | 03/16/2020,108.17 55 | 03/13/2020,106.21 56 | 03/12/2020,107.56 57 | 03/11/2020,105.84 58 | 03/10/2020,106.79 59 | 03/09/2020,108.00 60 | 03/06/2020,99.51 61 | 03/05/2020,92.44 62 | 03/04/2020,86.26 63 | 03/03/2020,82.57 64 | 03/02/2020,77.81 65 | 02/28/2020,75.07 66 | 02/27/2020,75.72 67 | 02/26/2020,77.04 68 | 02/25/2020,83.35 69 | 02/24/2020,91.00 70 | 02/21/2020,99.86 71 | 02/20/2020,106.94 72 | 02/19/2020,109.22 73 | 02/18/2020,108.53 74 | 02/14/2020,109.23 75 | 02/13/2020,107.40 76 | 02/12/2020,102.89 77 | 02/11/2020,94.60 78 | 02/10/2020,83.23 79 | 02/07/2020,70.70 80 | 02/06/2020,59.31 81 | 02/05/2020,48.65 82 | 02/04/2020,38.38 83 | 02/03/2020,27.89 84 | 01/31/2020,21.49 85 | 01/30/2020,15.91 86 | 01/29/2020,17.16 87 | 01/28/2020,17.77 88 | 01/27/2020,16.86 89 | 01/24/2020,14.76 90 | 01/23/2020,18.62 91 | 01/22/2020,22.70 92 | 01/21/2020,27.12 93 | 01/17/2020,30.99 94 | 01/16/2020,35.85 95 | 01/15/2020,38.98 96 | 01/14/2020,43.14 97 | 01/13/2020,47.39 98 | 01/10/2020,49.71 99 | 01/09/2020,51.71 100 | 01/08/2020,51.81 101 | 01/07/2020,50.42 -------------------------------------------------------------------------------- /src/test/resources/stdev.csv: -------------------------------------------------------------------------------- 1 | date,stdev(20) 2 | 05/29/2020,50.89 3 | 05/28/2020,52.82 4 | 05/27/2020,53.03 5 | 05/26/2020,55.67 6 | 05/22/2020,55.23 7 | 05/21/2020,54.33 8 | 05/20/2020,52.63 9 | 05/19/2020,45.73 10 | 05/18/2020,43.70 11 | 05/15/2020,42.18 12 | 05/14/2020,41.17 13 | 05/13/2020,41.89 14 | 05/12/2020,43.93 15 | 05/11/2020,47.42 16 | 05/08/2020,62.06 17 | 05/07/2020,90.73 18 | 05/06/2020,110.03 19 | 05/05/2020,128.10 20 | 05/04/2020,143.86 21 | 05/01/2020,165.32 22 | 04/30/2020,181.37 23 | 04/29/2020,187.47 24 | 04/28/2020,192.44 25 | 04/27/2020,196.72 26 | 04/24/2020,200.06 27 | 04/23/2020,195.43 28 | 04/22/2020,192.22 29 | 04/21/2020,185.86 30 | 04/20/2020,181.02 31 | 04/17/2020,170.05 32 | 04/16/2020,154.11 33 | 04/15/2020,129.10 34 | 04/14/2020,110.74 35 | 04/13/2020,98.25 36 | 04/09/2020,85.94 37 | 04/08/2020,93.96 38 | 04/07/2020,88.16 39 | 04/06/2020,82.85 40 | 04/03/2020,79.06 41 | 04/02/2020,78.91 42 | 04/01/2020,79.11 43 | 03/31/2020,82.36 44 | 03/30/2020,80.76 45 | 03/27/2020,80.17 46 | 03/26/2020,79.88 47 | 03/25/2020,77.22 48 | 03/24/2020,81.41 49 | 03/23/2020,83.23 50 | 03/20/2020,88.65 51 | 03/19/2020,100.99 52 | 03/18/2020,116.81 53 | 03/17/2020,129.83 54 | 03/16/2020,137.48 55 | 03/13/2020,132.40 56 | 03/12/2020,133.01 57 | 03/11/2020,120.43 58 | 03/10/2020,117.84 59 | 03/09/2020,117.22 60 | 03/06/2020,105.72 61 | 03/05/2020,100.89 62 | 03/04/2020,96.95 63 | 03/03/2020,95.37 64 | 03/02/2020,90.16 65 | 02/28/2020,87.68 66 | 02/27/2020,89.05 67 | 02/26/2020,91.82 68 | 02/25/2020,101.11 69 | 02/24/2020,111.36 70 | 02/21/2020,118.34 71 | 02/20/2020,122.28 72 | 02/19/2020,122.60 73 | 02/18/2020,120.14 74 | 02/14/2020,118.33 75 | 02/13/2020,115.61 76 | 02/12/2020,110.80 77 | 02/11/2020,102.47 78 | 02/10/2020,91.61 79 | 02/07/2020,79.69 80 | 02/06/2020,70.96 81 | 02/05/2020,63.53 82 | 02/04/2020,55.40 83 | 02/03/2020,43.10 84 | 01/31/2020,34.28 85 | 01/30/2020,19.27 86 | 01/29/2020,20.32 87 | 01/28/2020,20.97 88 | 01/27/2020,20.36 89 | 01/24/2020,17.17 90 | 01/23/2020,25.90 91 | 01/22/2020,31.41 92 | 01/21/2020,36.11 93 | 01/17/2020,38.94 94 | 01/16/2020,42.37 95 | 01/15/2020,44.42 96 | 01/14/2020,47.90 97 | 01/13/2020,51.18 98 | 01/10/2020,52.87 99 | 01/09/2020,54.94 100 | 01/08/2020,55.67 101 | 01/07/2020,55.26 -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/HullMovingAverageTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.loadAmazonData 31 | import vista.loadIndicatorData 32 | import vista.math.na 33 | import vista.math.numOf 34 | import vista.series.seriesOf 35 | 36 | internal class HullMovingAverageTest { 37 | 38 | @Test 39 | fun withIntSeries() { 40 | 41 | val series = seriesOf(1, 2, 3) 42 | 43 | val hma = hma(series, 2) 44 | 45 | assertThat(hma[0].round(2)).isEqualTo(numOf(3.33)) // current value 46 | assertThat(hma[1].round(2)).isEqualTo(numOf(2.33)) // previous value 47 | assertThat(hma[2]).isEqualTo(na) // oldest value 48 | } 49 | 50 | @Test 51 | fun withMarketData() { 52 | 53 | val data = loadAmazonData() 54 | val expected = loadIndicatorData("hma.csv") 55 | 56 | val actual = data.hma(9) 57 | 58 | for (i in 0..99) 59 | assertThat(actual[i].round(2)).isEqualTo(expected[i][0]) 60 | } 61 | } -------------------------------------------------------------------------------- /src/test/resources/cci.csv: -------------------------------------------------------------------------------- 1 | data,cci(20) 2 | 05/29/2020,57.43 3 | 05/28/2020,18.89 4 | 05/27/2020,-10.20 5 | 05/26/2020,65.16 6 | 05/22/2020,91.37 7 | 05/21/2020,142.31 8 | 05/20/2020,193.73 9 | 05/19/2020,168.31 10 | 05/18/2020,102.76 11 | 05/15/2020,53.35 12 | 05/14/2020,23.50 13 | 05/13/2020,4.67 14 | 05/12/2020,23.79 15 | 05/11/2020,72.44 16 | 05/08/2020,39.86 17 | 05/07/2020,33.93 18 | 05/06/2020,24.45 19 | 05/05/2020,19.39 20 | 05/04/2020,11.63 21 | 05/01/2020,19.84 22 | 04/30/2020,83.69 23 | 04/29/2020,54.65 24 | 04/28/2020,49.91 25 | 04/27/2020,77.48 26 | 04/24/2020,89.48 27 | 04/23/2020,100.30 28 | 04/22/2020,101.84 29 | 04/21/2020,108.06 30 | 04/20/2020,154.97 31 | 04/17/2020,170.42 32 | 04/16/2020,234.38 33 | 04/15/2020,234.42 34 | 04/14/2020,268.45 35 | 04/13/2020,206.38 36 | 04/09/2020,137.69 37 | 04/08/2020,135.96 38 | 04/07/2020,133.32 39 | 04/06/2020,109.33 40 | 04/03/2020,47.77 41 | 04/02/2020,53.76 42 | 04/01/2020,55.37 43 | 03/31/2020,99.03 44 | 03/30/2020,89.76 45 | 03/27/2020,54.30 46 | 03/26/2020,80.35 47 | 03/25/2020,53.91 48 | 03/24/2020,72.87 49 | 03/23/2020,14.24 50 | 03/20/2020,3.81 51 | 03/19/2020,3.20 52 | 03/18/2020,-62.87 53 | 03/17/2020,-83.50 54 | 03/16/2020,-149.72 55 | 03/13/2020,-126.16 56 | 03/12/2020,-164.45 57 | 03/11/2020,-103.34 58 | 03/10/2020,-89.35 59 | 03/09/2020,-134.71 60 | 03/06/2020,-97.82 61 | 03/05/2020,-83.84 62 | 03/04/2020,-73.05 63 | 03/03/2020,-102.50 64 | 03/02/2020,-116.21 65 | 02/28/2020,-187.47 66 | 02/27/2020,-139.85 67 | 02/26/2020,-69.94 68 | 02/25/2020,-55.48 69 | 02/24/2020,-27.14 70 | 02/21/2020,46.10 71 | 02/20/2020,77.59 72 | 02/19/2020,95.94 73 | 02/18/2020,88.77 74 | 02/14/2020,91.34 75 | 02/13/2020,109.77 76 | 02/12/2020,131.38 77 | 02/11/2020,148.34 78 | 02/10/2020,148.41 79 | 02/07/2020,140.18 80 | 02/06/2020,143.45 81 | 02/05/2020,186.92 82 | 02/04/2020,240.07 83 | 02/03/2020,291.16 84 | 01/31/2020,427.78 85 | 01/30/2020,-58.23 86 | 01/29/2020,-59.14 87 | 01/28/2020,-122.40 88 | 01/27/2020,-231.35 89 | 01/24/2020,-52.80 90 | 01/23/2020,29.30 91 | 01/22/2020,62.00 92 | 01/21/2020,42.08 93 | 01/17/2020,19.66 94 | 01/16/2020,38.10 95 | 01/15/2020,23.36 96 | 01/14/2020,38.71 97 | 01/13/2020,69.40 98 | 01/10/2020,74.96 99 | 01/09/2020,101.31 100 | 01/08/2020,101.81 101 | 01/07/2020,126.52 -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/OnBalanceVolumeTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.loadAmazonData 31 | import vista.loadIndicatorData 32 | import vista.math.na 33 | import vista.math.numOf 34 | import vista.series.seriesOf 35 | 36 | internal class OnBalanceVolumeTest { 37 | 38 | @Test 39 | fun withIntSeries() { 40 | 41 | val close = seriesOf(1, 2, 1) 42 | val volume = seriesOf(1, 2, 1) 43 | 44 | val obv = obv(close, volume) 45 | 46 | assertThat(obv[0]).isEqualTo(numOf(1)) // current value 47 | assertThat(obv[1]).isEqualTo(numOf(2)) // previous value 48 | assertThat(obv[2]).isEqualTo(na) // oldest value 49 | } 50 | 51 | @Test 52 | fun withMarketData() { 53 | 54 | val data = loadAmazonData() 55 | val expected = loadIndicatorData("obv.csv") 56 | 57 | val actual = data.obv() 58 | 59 | for (i in 0..99) 60 | assertThat(actual[i]).isEqualTo(expected[i][0]) 61 | } 62 | } -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/WeightedMovingAverageTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.loadAmazonData 31 | import vista.loadIndicatorData 32 | import vista.math.na 33 | import vista.math.numOf 34 | import vista.series.seriesOf 35 | 36 | internal class WeightedMovingAverageTest { 37 | 38 | @Test 39 | fun withIntSeries() { 40 | 41 | val series = seriesOf(1, 2, 3) 42 | 43 | val wma = wma(series, 2) 44 | 45 | assertThat(wma[0].round(2)).isEqualTo(numOf(2.67)) // current value 46 | assertThat(wma[1].round(2)).isEqualTo(numOf(1.67)) // previous value 47 | assertThat(wma[2]).isEqualTo(na) // oldest value 48 | } 49 | 50 | @Test 51 | fun withMarketData() { 52 | 53 | val data = loadAmazonData() 54 | val expected = loadIndicatorData("wma.csv") 55 | 56 | val actual = data.wma(9) 57 | 58 | for (i in 0..99) 59 | assertThat(actual[i].round(2)).isEqualTo(expected[i][0]) 60 | } 61 | } -------------------------------------------------------------------------------- /src/test/resources/ao.csv: -------------------------------------------------------------------------------- 1 | date,ao 2 | 05/29/2020,43.90 3 | 05/28/2020,67.96 4 | 05/27/2020,94.35 5 | 05/26/2020,121.83 6 | 05/22/2020,129.86 7 | 05/21/2020,132.55 8 | 05/20/2020,127.13 9 | 05/19/2020,121.56 10 | 05/18/2020,121.93 11 | 05/15/2020,133.10 12 | 05/14/2020,144.43 13 | 05/13/2020,155.10 14 | 05/12/2020,161.67 15 | 05/11/2020,163.59 16 | 05/08/2020,158.34 17 | 05/07/2020,160.19 18 | 05/06/2020,189.23 19 | 05/05/2020,207.71 20 | 05/04/2020,226.19 21 | 05/01/2020,266.26 22 | 04/30/2020,301.40 23 | 04/29/2020,315.98 24 | 04/28/2020,335.42 25 | 04/27/2020,352.49 26 | 04/24/2020,372.21 27 | 04/23/2020,378.60 28 | 04/22/2020,391.33 29 | 04/21/2020,387.09 30 | 04/20/2020,376.23 31 | 04/17/2020,329.73 32 | 04/16/2020,280.12 33 | 04/15/2020,219.83 34 | 04/14/2020,174.20 35 | 04/13/2020,126.37 36 | 04/09/2020,88.92 37 | 04/08/2020,61.23 38 | 04/07/2020,35.85 39 | 04/06/2020,21.61 40 | 04/03/2020,12.04 41 | 04/02/2020,7.56 42 | 04/01/2020,3.11 43 | 03/31/2020,-4.40 44 | 03/30/2020,-18.23 45 | 03/27/2020,-38.61 46 | 03/26/2020,-49.17 47 | 03/25/2020,-59.45 48 | 03/24/2020,-88.31 49 | 03/23/2020,-122.38 50 | 03/20/2020,-161.63 51 | 03/19/2020,-196.84 52 | 03/18/2020,-229.80 53 | 03/17/2020,-223.29 54 | 03/16/2020,-208.83 55 | 03/13/2020,-188.95 56 | 03/12/2020,-161.66 57 | 03/11/2020,-123.33 58 | 03/10/2020,-102.28 59 | 03/09/2020,-85.69 60 | 03/06/2020,-67.41 61 | 03/05/2020,-74.94 62 | 03/04/2020,-74.22 63 | 03/03/2020,-64.44 64 | 03/02/2020,-52.03 65 | 02/28/2020,-31.20 66 | 02/27/2020,20.31 67 | 02/26/2020,65.85 68 | 02/25/2020,105.44 69 | 02/24/2020,138.53 70 | 02/21/2020,168.37 71 | 02/20/2020,183.26 72 | 02/19/2020,195.49 73 | 02/18/2020,202.20 74 | 02/14/2020,202.93 75 | 02/13/2020,197.14 76 | 02/12/2020,184.74 77 | 02/11/2020,172.64 78 | 02/10/2020,158.79 79 | 02/07/2020,151.22 80 | 02/06/2020,151.54 81 | 02/05/2020,123.30 82 | 02/04/2020,94.45 83 | 02/03/2020,63.85 84 | 01/31/2020,32.54 85 | 01/30/2020,9.40 86 | 01/29/2020,16.83 87 | 01/28/2020,25.63 88 | 01/27/2020,35.09 89 | 01/24/2020,46.09 90 | 01/23/2020,49.85 91 | 01/22/2020,50.54 92 | 01/21/2020,49.72 93 | 01/17/2020,54.07 94 | 01/16/2020,60.17 95 | 01/15/2020,68.96 96 | 01/14/2020,78.30 97 | 01/13/2020,88.27 98 | 01/10/2020,91.19 99 | 01/09/2020,91.84 100 | 01/08/2020,91.25 101 | 01/07/2020,84.78 -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/StandardDeviationTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.loadAmazonData 31 | import vista.loadIndicatorData 32 | import vista.math.na 33 | import vista.math.numOf 34 | import vista.series.seriesOf 35 | 36 | internal class StandardDeviationTest { 37 | 38 | @Test 39 | fun withIntSeries() { 40 | val series = seriesOf(1, 2, 4) 41 | 42 | val stdev = stdev(series, 2) 43 | 44 | assertThat(stdev[0]).isEqualTo(numOf(1)) // current value 45 | assertThat(stdev[1]).isEqualTo(numOf(.5)) // previous value 46 | assertThat(stdev[2]).isEqualTo(na) // oldest value 47 | } 48 | 49 | @Test 50 | fun withMarketData() { 51 | val data = loadAmazonData() 52 | val expected = loadIndicatorData("stdev.csv") 53 | val close = data.close 54 | 55 | val actual = stdev(close, 20) 56 | 57 | for (i in 0..99) 58 | assertThat(actual[i].round(2)).isEqualTo(expected[i][0]) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/test/resources/wpr.csv: -------------------------------------------------------------------------------- 1 | date,%R(14) 2 | 05/29/2020,-42.51 3 | 05/28/2020,-63.62 4 | 05/27/2020,-58.87 5 | 05/26/2020,-50.42 6 | 05/22/2020,-40.57 7 | 05/21/2020,-29.25 8 | 05/20/2020,-0.85 9 | 05/19/2020,-15.60 10 | 05/18/2020,-22.29 11 | 05/15/2020,-29.83 12 | 05/14/2020,-39.41 13 | 05/13/2020,-48.98 14 | 05/12/2020,-54.00 15 | 05/11/2020,-30.19 16 | 05/08/2020,-43.63 17 | 05/07/2020,-49.12 18 | 05/06/2020,-56.60 19 | 05/05/2020,-71.91 20 | 05/04/2020,-69.13 21 | 05/01/2020,-65.43 22 | 04/30/2020,-0.23 23 | 04/29/2020,-19.91 24 | 04/28/2020,-32.66 25 | 04/27/2020,-18.34 26 | 04/24/2020,-9.56 27 | 04/23/2020,-10.76 28 | 04/22/2020,-17.05 29 | 04/21/2020,-23.24 30 | 04/20/2020,-11.78 31 | 04/17/2020,-15.04 32 | 04/16/2020,-9.23 33 | 04/15/2020,-5.78 34 | 04/14/2020,-2.14 35 | 04/13/2020,-3.78 36 | 04/09/2020,-4.25 37 | 04/08/2020,-0.43 38 | 04/07/2020,-10.78 39 | 04/06/2020,-0.37 40 | 04/03/2020,-28.45 41 | 04/02/2020,-20.22 42 | 04/01/2020,-23.25 43 | 03/31/2020,-11.80 44 | 03/30/2020,-2.78 45 | 03/27/2020,-17.19 46 | 03/26/2020,-0.46 47 | 03/25/2020,-21.50 48 | 03/24/2020,-6.16 49 | 03/23/2020,-21.36 50 | 03/20/2020,-40.57 51 | 03/19/2020,-31.16 52 | 03/18/2020,-44.92 53 | 03/17/2020,-50.90 54 | 03/16/2020,-83.76 55 | 03/13/2020,-69.41 56 | 03/12/2020,-99.56 57 | 03/11/2020,-84.46 58 | 03/10/2020,-68.58 59 | 03/09/2020,-90.72 60 | 03/06/2020,-75.94 61 | 03/05/2020,-69.81 62 | 03/04/2020,-55.96 63 | 03/03/2020,-73.83 64 | 03/02/2020,-61.90 65 | 02/28/2020,-80.63 66 | 02/27/2020,-99.49 67 | 02/26/2020,-90.70 68 | 02/25/2020,-93.71 69 | 02/24/2020,-89.23 70 | 02/21/2020,-48.45 71 | 02/20/2020,-17.69 72 | 02/19/2020,-4.69 73 | 02/18/2020,-9.03 74 | 02/14/2020,-14.35 75 | 02/13/2020,-9.74 76 | 02/12/2020,-7.00 77 | 02/11/2020,-9.48 78 | 02/10/2020,-0.53 79 | 02/07/2020,-6.80 80 | 02/06/2020,-8.13 81 | 02/05/2020,-12.18 82 | 02/04/2020,-4.14 83 | 02/03/2020,-21.43 84 | 01/31/2020,-19.55 85 | 01/30/2020,-39.59 86 | 01/29/2020,-58.37 87 | 01/28/2020,-63.01 88 | 01/27/2020,-87.31 89 | 01/24/2020,-79.82 90 | 01/23/2020,-52.99 91 | 01/22/2020,-48.40 92 | 01/21/2020,-30.17 93 | 01/17/2020,-62.04 94 | 01/16/2020,-46.59 95 | 01/15/2020,-47.16 96 | 01/14/2020,-37.15 97 | 01/13/2020,-19.89 98 | 01/10/2020,-25.60 99 | 01/09/2020,-11.67 100 | 01/08/2020,-15.68 101 | 01/07/2020,-5.03 -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/CommodityChannelIndex.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import vista.data.Data 29 | import vista.series.Series 30 | 31 | /** 32 | * The commodity channel index, to evaluate overbought or oversold conditions. 33 | * 34 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/momentum?id=commodity-channel-index-cci) 35 | * 36 | * @param source Series of values to process 37 | * @param n Number of bars (length) 38 | * @sample vista.indicators.CommodityChannelIndexTest.withIntSeries 39 | */ 40 | fun cci(source: Series, n: Int = 20): Series { 41 | val movingAverage = sma(source, n) 42 | val meanDeviation = dev(source, n) 43 | return (source - movingAverage) / (meanDeviation * .015) 44 | } 45 | 46 | /** 47 | * The commodity channel index, to evaluate overbought or oversold conditions. 48 | * 49 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/momentum?id=commodity-channel-index-cci) 50 | * 51 | * @param n Number of bars (length) 52 | * @sample vista.indicators.CommodityChannelIndexTest.withMarketData 53 | */ 54 | fun Data.cci(n: Int = 20) = cci(typical, n) -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/CommodityChannelIndexTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.loadAmazonData 31 | import vista.loadIndicatorData 32 | import vista.math.na 33 | import vista.math.numOf 34 | import vista.series.seriesOf 35 | 36 | internal class CommodityChannelIndexTest { 37 | 38 | @Test 39 | fun withIntSeries() { 40 | val close = seriesOf(1..25) 41 | 42 | val cci = cci(close, 20) 43 | 44 | assertThat(cci[0].round(2)).isEqualTo(numOf(126.67)) // current value 45 | assertThat(cci[1].round(2)).isEqualTo(numOf(126.67)) // previous value 46 | assertThat(cci[5].round(2)).isEqualTo(numOf(126.67)) 47 | assertThat(cci[6]).isEqualTo(na) 48 | } 49 | 50 | @Test 51 | fun withMarketData() { 52 | val data = loadAmazonData() 53 | val expected = loadIndicatorData("cci.csv") 54 | 55 | val cci = data.cci() 56 | 57 | for (i in 0..99) 58 | assertThat(cci[i].round(2)).isEqualTo(expected[i][0]) 59 | } 60 | } -------------------------------------------------------------------------------- /src/test/resources/mom.csv: -------------------------------------------------------------------------------- 1 | date,mom(10) 2 | 05/29/2020,53.52 3 | 05/28/2020,33.18 4 | 05/27/2020,53.44 5 | 05/26/2020,12.86 6 | 05/22/2020,57.27 7 | 05/21/2020,79.13 8 | 05/20/2020,146.68 9 | 05/19/2020,131.53 10 | 05/18/2020,110.27 11 | 05/15/2020,123.74 12 | 05/14/2020,-85.15 13 | 05/13/2020,-4.79 14 | 05/12/2020,42.87 15 | 05/11/2020,33.00 16 | 05/08/2020,-30.61 17 | 05/07/2020,-31.84 18 | 05/06/2020,-12.23 19 | 05/05/2020,-10.32 20 | 05/04/2020,-77.62 21 | 05/01/2020,-88.96 22 | 04/30/2020,65.81 23 | 04/29/2020,65.03 24 | 04/28/2020,30.76 25 | 04/27/2020,207.13 26 | 04/24/2020,367.46 27 | 04/23/2020,356.45 28 | 04/22/2020,351.89 29 | 04/21/2020,330.53 30 | 04/20/2020,487.02 31 | 04/17/2020,456.17 32 | 04/16/2020,500.49 33 | 04/15/2020,357.96 34 | 04/14/2020,319.37 35 | 04/13/2020,268.77 36 | 04/09/2020,87.27 37 | 04/08/2020,157.16 38 | 04/07/2020,71.50 39 | 04/06/2020,94.76 40 | 04/03/2020,60.50 41 | 04/02/2020,37.90 42 | 04/01/2020,77.70 43 | 03/31/2020,141.88 44 | 03/30/2020,274.80 45 | 03/27/2020,115.10 46 | 03/26/2020,278.88 47 | 03/25/2020,64.98 48 | 03/24/2020,48.28 49 | 03/23/2020,102.22 50 | 03/20/2020,-55.00 51 | 03/19/2020,-43.10 52 | 03/18/2020,-145.83 53 | 03/17/2020,-101.15 54 | 03/16/2020,-264.80 55 | 03/13/2020,-98.75 56 | 03/12/2020,-207.69 57 | 03/11/2020,-158.73 58 | 03/10/2020,-80.92 59 | 03/09/2020,-208.68 60 | 03/06/2020,-194.88 61 | 03/05/2020,-229.07 62 | 03/04/2020,-194.39 63 | 03/03/2020,-246.68 64 | 03/02/2020,-180.92 65 | 02/28/2020,-266.12 66 | 02/27/2020,-275.70 67 | 02/26/2020,-171.21 68 | 02/25/2020,-161.17 69 | 02/24/2020,-69.99 70 | 02/21/2020,45.74 71 | 02/20/2020,113.23 72 | 02/19/2020,120.55 73 | 02/18/2020,151.47 74 | 02/14/2020,126.15 75 | 02/13/2020,279.19 76 | 02/12/2020,302.00 77 | 02/11/2020,297.55 78 | 02/10/2020,305.57 79 | 02/07/2020,217.64 80 | 02/06/2020,165.65 81 | 02/05/2020,152.41 82 | 02/04/2020,157.67 83 | 02/03/2020,139.48 84 | 01/31/2020,130.78 85 | 01/30/2020,8.66 86 | 01/29/2020,-11.44 87 | 01/28/2020,-38.05 88 | 01/27/2020,-54.82 89 | 01/24/2020,-39.41 90 | 01/23/2020,-7.39 91 | 01/22/2020,-19.40 92 | 01/21/2020,-10.88 93 | 01/17/2020,-10.25 94 | 01/16/2020,-20.07 95 | 01/15/2020,14.18 96 | 01/14/2020,22.55 97 | 01/13/2020,21.50 98 | 01/10/2020,14.39 99 | 01/09/2020,111.84 100 | 01/08/2020,98.97 101 | 01/07/2020,120.36 -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/UltimateOscillatorTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.loadAmazonData 31 | import vista.loadIndicatorData 32 | import vista.math.na 33 | import vista.math.numOf 34 | import vista.series.seriesOf 35 | 36 | internal class UltimateOscillatorTest { 37 | 38 | @Test 39 | fun withIntSeries() { 40 | val close = seriesOf(1..30) 41 | 42 | val high = close * 1.5 43 | val low = close * 0.5 44 | 45 | val uo = uo(close, high, low) 46 | 47 | assertThat(uo[0].round(2)).isEqualTo(numOf(50.00)) // current value 48 | assertThat(uo[1].round(2)).isEqualTo(numOf(50.00)) // previous value 49 | assertThat(uo[3]).isEqualTo(na) 50 | } 51 | 52 | @Test 53 | fun withMarketData() { 54 | val data = loadAmazonData() 55 | val expected = loadIndicatorData("uo.csv") 56 | 57 | val actual = data.uo() 58 | 59 | for (i in 0..99) 60 | assertThat(actual[i].round(2)).isEqualTo(expected[i][0]) 61 | } 62 | } -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/RelativeStrengthIndexTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.loadAmazonData 31 | import vista.loadIndicatorData 32 | import vista.math.na 33 | import vista.math.numOf 34 | import vista.series.seriesOf 35 | 36 | internal class RelativeStrengthIndexTest { 37 | 38 | @Test 39 | fun withIntSeries() { 40 | val series = seriesOf(1..20) 41 | 42 | val rsi = rsi(series, 14) 43 | 44 | assertThat(rsi[0].round(2)).isEqualTo(numOf(100)) // current value 45 | assertThat(rsi[1].round(2)).isEqualTo(numOf(100)) // previous value 46 | assertThat(rsi[5].round(2)).isEqualTo(numOf(100)) 47 | assertThat(rsi[6]).isEqualTo(na) 48 | } 49 | 50 | @Test 51 | fun withMarketData() { 52 | 53 | val data = loadAmazonData() 54 | val expected = loadIndicatorData("rsi.csv") 55 | 56 | val actual = data.rsi() 57 | 58 | for (i in 0..99) { 59 | assertThat(actual[i].round(2)).isEqualTo(expected[i][0]) 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/RateOfChangeTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.loadAmazonData 31 | import vista.loadIndicatorData 32 | import vista.math.na 33 | import vista.math.numOf 34 | import vista.series.seriesOf 35 | 36 | internal class RateOfChangeTest { 37 | 38 | @Test 39 | fun withIntSeries() { 40 | val volume = seriesOf(1..20) 41 | 42 | val roc = roc(volume, 9) 43 | 44 | assertThat(roc[0].round(2)).isEqualTo(numOf(81.82)) // current value 45 | assertThat(roc[1].round(2)).isEqualTo(numOf(90.00)) // previous value 46 | assertThat(roc[10].round(2)).isEqualTo(numOf(900.00)) 47 | assertThat(roc[11]).isEqualTo(na) // oldest value 48 | } 49 | 50 | @Test 51 | fun withMarketData() { 52 | val data = loadAmazonData() 53 | val expected = loadIndicatorData("roc.csv") 54 | 55 | val actual = data.roc(9) 56 | 57 | for (i in 0..99) 58 | assertThat(actual[i].round(2)).isEqualTo(expected[i][0]) 59 | } 60 | } -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/VolumeWeightedMovingAverageTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.loadAmazonData 31 | import vista.loadIndicatorData 32 | import vista.math.na 33 | import vista.math.numOf 34 | import vista.series.seriesOf 35 | 36 | internal class VolumeWeightedMovingAverageTest { 37 | 38 | @Test 39 | fun withIntSeries() { 40 | val close = seriesOf(1, 2, 3) 41 | val volume = seriesOf(1, 2, 3) 42 | 43 | val vwma = vwma(close, volume, 2) 44 | 45 | assertThat(vwma[0].round(2)).isEqualTo(numOf(2.60)) // current value 46 | assertThat(vwma[1].round(2)).isEqualTo(numOf(1.67)) // previous value 47 | assertThat(vwma[2]).isEqualTo(na) // oldest value 48 | } 49 | 50 | @Test 51 | fun withMarketData() { 52 | val data = loadAmazonData() 53 | val expected = loadIndicatorData("vwma.csv") 54 | 55 | val actual = data.vwma(20) 56 | 57 | for (i in 0..99) 58 | assertThat(actual[i].round(2)).isEqualTo(expected[i][0]) 59 | } 60 | } -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/AwesomeOscillatorTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.loadAmazonData 31 | import vista.loadIndicatorData 32 | import vista.math.na 33 | import vista.math.numOf 34 | import vista.series.seriesOf 35 | 36 | internal class AwesomeOscillatorTest { 37 | 38 | @Test 39 | fun withIntSeries() { 40 | val close = seriesOf(1..50) 41 | 42 | val high = close * 1.5 43 | val low = close * 0.5 44 | 45 | val ao = ao(high, low) 46 | 47 | assertThat(ao[0].round(2)).isEqualTo(numOf(14.50)) // current value 48 | assertThat(ao[1].round(2)).isEqualTo(numOf(14.50)) // previous value 49 | assertThat(ao[16].round(2)).isEqualTo(numOf(14.50)) 50 | assertThat(ao[17]).isEqualTo(na) 51 | } 52 | 53 | @Test 54 | fun withMarketData() { 55 | val data = loadAmazonData() 56 | val expected = loadIndicatorData("ao.csv") 57 | 58 | val actual = data.ao() 59 | 60 | for (i in 0..99) 61 | assertThat(actual[i].round(2)).isEqualTo(expected[i][0]) 62 | } 63 | } -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/WilliamsPercentRangeTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.loadAmazonData 31 | import vista.loadIndicatorData 32 | import vista.math.na 33 | import vista.math.numOf 34 | import vista.series.seriesOf 35 | 36 | internal class WilliamsPercentRangeTest { 37 | 38 | @Test 39 | fun withIntSeries() { 40 | val close = seriesOf(1..20) 41 | 42 | val high = close * 1.5 43 | val low = close * 0.5 44 | 45 | val r = wpr(close, high, low, 14) 46 | 47 | assertThat(r[0].round(2)).isEqualTo(numOf(-37.74)) // current value 48 | assertThat(r[1].round(2)).isEqualTo(numOf(-37.25)) // previous value 49 | assertThat(r[6].round(2)).isEqualTo(numOf(-34.15)) 50 | assertThat(r[7]).isEqualTo(na) 51 | } 52 | 53 | @Test 54 | fun withMarketData() { 55 | val data = loadAmazonData() 56 | val expected = loadIndicatorData("wpr.csv") 57 | 58 | val actual = data.wpr() 59 | 60 | for (i in 0..99) 61 | assertThat(actual[i].round(2)).isEqualTo(expected[i][0]) 62 | } 63 | } -------------------------------------------------------------------------------- /src/test/kotlin/vista/rules/RulesTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.rules 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.series.seriesOf 31 | 32 | internal class RulesTest { 33 | 34 | @Test 35 | fun cross() { 36 | 37 | val bearish = seriesOf(1, 0) 38 | val bullish = seriesOf(0, 1) 39 | 40 | assertThat(cross(bearish, bullish)).isTrue() 41 | assertThat(cross(bullish, bearish)).isTrue() 42 | assertThat(cross(bullish, bullish)).isFalse() 43 | } 44 | 45 | @Test 46 | fun crossover() { 47 | 48 | val bearish = seriesOf(1, 0) 49 | val bullish = seriesOf(0, 1) 50 | 51 | assertThat(crossover(bearish, bullish)).isFalse() 52 | assertThat(crossover(bullish, bearish)).isTrue() 53 | assertThat(crossover(bullish, bullish)).isFalse() 54 | } 55 | 56 | @Test 57 | fun crossunder() { 58 | 59 | val bearish = seriesOf(1, 0) 60 | val bullish = seriesOf(0, 1) 61 | 62 | assertThat(crossunder(bearish, bullish)).isTrue() 63 | assertThat(crossunder(bullish, bearish)).isFalse() 64 | assertThat(crossunder(bullish, bullish)).isFalse() 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/test/resources/ema.csv: -------------------------------------------------------------------------------- 1 | date,ema(9) 2 | 05/29/2020,2423.31 3 | 05/28/2020,2418.55 4 | 05/27/2020,2422.91 5 | 05/26/2020,2426.04 6 | 05/22/2020,2427.09 7 | 05/21/2020,2424.64 8 | 05/20/2020,2419.12 9 | 05/19/2020,2399.41 10 | 05/18/2020,2386.93 11 | 05/15/2020,2377.10 12 | 05/14/2020,2368.93 13 | 05/13/2020,2363.96 14 | 05/12/2020,2362.96 15 | 05/11/2020,2364.47 16 | 05/08/2020,2353.33 17 | 05/07/2020,2346.77 18 | 05/06/2020,2341.56 19 | 05/05/2020,2339.13 20 | 05/04/2020,2344.46 21 | 05/01/2020,2351.58 22 | 04/30/2020,2367.96 23 | 04/29/2020,2341.45 24 | 04/28/2020,2333.64 25 | 04/27/2020,2338.53 26 | 04/24/2020,2329.16 27 | 04/23/2020,2308.90 28 | 04/22/2020,2286.26 29 | 04/21/2020,2266.96 30 | 04/20/2020,2251.66 31 | 04/17/2020,2216.18 32 | 04/16/2020,2176.47 33 | 04/15/2020,2118.54 34 | 04/14/2020,2071.26 35 | 04/13/2020,2018.24 36 | 04/09/2020,1980.59 37 | 04/08/2020,1965.04 38 | 04/07/2020,1945.56 39 | 04/06/2020,1929.04 40 | 04/03/2020,1911.91 41 | 04/02/2020,1913.24 42 | 04/01/2020,1911.84 43 | 03/31/2020,1912.88 44 | 03/30/2020,1903.66 45 | 03/27/2020,1888.59 46 | 03/26/2020,1885.72 47 | 03/25/2020,1868.27 48 | 03/24/2020,1863.88 49 | 03/23/2020,1844.83 50 | 03/20/2020,1830.32 51 | 03/19/2020,1826.38 52 | 03/18/2020,1812.75 53 | 03/17/2020,1808.43 54 | 03/16/2020,1808.58 55 | 03/13/2020,1838.44 56 | 03/12/2020,1851.80 57 | 03/11/2020,1895.59 58 | 03/10/2020,1914.28 59 | 03/09/2020,1919.89 60 | 03/06/2020,1949.71 61 | 03/05/2020,1961.86 62 | 03/04/2020,1971.32 63 | 03/03/2020,1970.20 64 | 03/02/2020,1985.50 65 | 02/28/2020,1993.38 66 | 02/27/2020,2020.79 67 | 02/26/2020,2054.92 68 | 02/25/2020,2073.75 69 | 02/24/2020,2099.00 70 | 02/21/2020,2121.43 71 | 02/20/2020,2127.79 72 | 02/19/2020,2121.47 73 | 02/18/2020,2109.28 74 | 02/14/2020,2097.68 75 | 02/13/2020,2088.38 76 | 02/12/2020,2073.01 77 | 02/11/2020,2051.26 78 | 02/10/2020,2026.38 79 | 02/07/2020,1999.50 80 | 02/06/2020,1979.55 81 | 02/05/2020,1961.88 82 | 02/04/2020,1942.39 83 | 02/03/2020,1915.56 84 | 01/31/2020,1893.41 85 | 01/30/2020,1864.58 86 | 01/29/2020,1863.05 87 | 01/28/2020,1864.31 88 | 01/27/2020,1867.08 89 | 01/24/2020,1876.76 90 | 01/23/2020,1880.55 91 | 01/22/2020,1879.54 92 | 01/21/2020,1877.56 93 | 01/17/2020,1873.95 94 | 01/16/2020,1876.25 95 | 01/15/2020,1875.83 96 | 01/14/2020,1879.28 97 | 01/13/2020,1881.74 98 | 01/10/2020,1879.36 99 | 01/09/2020,1878.40 100 | 01/08/2020,1872.74 101 | 01/07/2020,1867.94 -------------------------------------------------------------------------------- /src/test/resources/hma.csv: -------------------------------------------------------------------------------- 1 | date,hma(9) 2 | 05/29/2020,2405.99 3 | 05/28/2020,2407.17 4 | 05/27/2020,2429.73 5 | 05/26/2020,2457.00 6 | 05/22/2020,2478.82 7 | 05/21/2020,2483.16 8 | 05/20/2020,2467.57 9 | 05/19/2020,2432.57 10 | 05/18/2020,2407.87 11 | 05/15/2020,2390.47 12 | 05/14/2020,2385.00 13 | 05/13/2020,2390.26 14 | 05/12/2020,2394.39 15 | 05/11/2020,2386.11 16 | 05/08/2020,2354.14 17 | 05/07/2020,2326.86 18 | 05/06/2020,2310.64 19 | 05/05/2020,2321.16 20 | 05/04/2020,2352.45 21 | 05/01/2020,2374.58 22 | 04/30/2020,2383.28 23 | 04/29/2020,2359.62 24 | 04/28/2020,2376.25 25 | 04/27/2020,2397.51 26 | 04/24/2020,2391.86 27 | 04/23/2020,2386.40 28 | 04/22/2020,2402.27 29 | 04/21/2020,2436.25 30 | 04/20/2020,2461.52 31 | 04/17/2020,2440.28 32 | 04/16/2020,2384.55 33 | 04/15/2020,2287.79 34 | 04/14/2020,2194.88 35 | 04/13/2020,2107.40 36 | 04/09/2020,2052.77 37 | 04/08/2020,2016.41 38 | 04/07/2020,1968.83 39 | 04/06/2020,1932.74 40 | 04/03/2020,1917.18 41 | 04/02/2020,1934.34 42 | 04/01/2020,1946.49 43 | 03/31/2020,1953.64 44 | 03/30/2020,1947.10 45 | 03/27/2020,1941.61 46 | 03/26/2020,1948.35 47 | 03/25/2020,1935.93 48 | 03/24/2020,1929.53 49 | 03/23/2020,1898.98 50 | 03/20/2020,1864.71 51 | 03/19/2020,1818.38 52 | 03/18/2020,1753.08 53 | 03/17/2020,1708.54 54 | 03/16/2020,1696.01 55 | 03/13/2020,1735.09 56 | 03/12/2020,1768.98 57 | 03/11/2020,1828.47 58 | 03/10/2020,1857.16 59 | 03/09/2020,1887.49 60 | 03/06/2020,1932.46 61 | 03/05/2020,1933.94 62 | 03/04/2020,1912.89 63 | 03/03/2020,1876.70 64 | 03/02/2020,1865.76 65 | 02/28/2020,1867.54 66 | 02/27/2020,1907.76 67 | 02/26/2020,1963.81 68 | 02/25/2020,2020.61 69 | 02/24/2020,2091.44 70 | 02/21/2020,2144.50 71 | 02/20/2020,2163.19 72 | 02/19/2020,2164.10 73 | 02/18/2020,2166.20 74 | 02/14/2020,2175.10 75 | 02/13/2020,2181.33 76 | 02/12/2020,2168.67 77 | 02/11/2020,2141.75 78 | 02/10/2020,2113.48 79 | 02/07/2020,2094.96 80 | 02/06/2020,2088.53 81 | 02/05/2020,2073.94 82 | 02/04/2020,2033.65 83 | 02/03/2020,1966.99 84 | 01/31/2020,1901.73 85 | 01/30/2020,1847.77 86 | 01/29/2020,1840.07 87 | 01/28/2020,1846.28 88 | 01/27/2020,1862.61 89 | 01/24/2020,1882.88 90 | 01/23/2020,1886.93 91 | 01/22/2020,1879.69 92 | 01/21/2020,1869.94 93 | 01/17/2020,1862.21 94 | 01/16/2020,1865.39 95 | 01/15/2020,1870.76 96 | 01/14/2020,1883.09 97 | 01/13/2020,1894.20 98 | 01/10/2020,1901.30 99 | 01/09/2020,1909.38 100 | 01/08/2020,1909.79 101 | 01/07/2020,1907.56 -------------------------------------------------------------------------------- /src/test/resources/sma.csv: -------------------------------------------------------------------------------- 1 | date,sma(9) 2 | 05/29/2020,2436.99 3 | 05/28/2020,2433.36 4 | 05/27/2020,2432.00 5 | 05/26/2020,2427.28 6 | 05/22/2020,2420.07 7 | 05/21/2020,2416.97 8 | 05/20/2020,2409.52 9 | 05/19/2020,2395.03 10 | 05/18/2020,2384.14 11 | 05/15/2020,2372.09 12 | 05/14/2020,2361.67 13 | 05/13/2020,2350.24 14 | 05/12/2020,2362.03 15 | 05/11/2020,2363.78 16 | 05/08/2020,2353.23 17 | 05/07/2020,2352.83 18 | 05/06/2020,2357.57 19 | 05/05/2020,2362.92 20 | 05/04/2020,2368.00 21 | 05/01/2020,2369.35 22 | 04/30/2020,2381.30 23 | 04/29/2020,2370.30 24 | 04/28/2020,2374.24 25 | 04/27/2020,2373.53 26 | 04/24/2020,2363.23 27 | 04/23/2020,2336.41 28 | 04/22/2020,2296.78 29 | 04/21/2020,2261.17 30 | 04/20/2020,2226.00 31 | 04/17/2020,2182.00 32 | 04/16/2020,2129.96 33 | 04/15/2020,2075.58 34 | 04/14/2020,2031.14 35 | 04/13/2020,1994.07 36 | 04/09/2020,1971.30 37 | 04/08/2020,1955.45 38 | 04/07/2020,1945.73 39 | 04/06/2020,1931.76 40 | 04/03/2020,1925.37 41 | 04/02/2020,1924.95 42 | 04/01/2020,1916.87 43 | 03/31/2020,1913.89 44 | 03/30/2020,1900.59 45 | 03/27/2020,1883.25 46 | 03/26/2020,1859.81 47 | 03/25/2020,1840.86 48 | 03/24/2020,1817.62 49 | 03/23/2020,1804.37 50 | 03/20/2020,1803.14 51 | 03/19/2020,1798.09 52 | 03/18/2020,1800.33 53 | 03/17/2020,1810.78 54 | 03/16/2020,1829.44 55 | 03/13/2020,1853.87 56 | 03/12/2020,1872.64 57 | 03/11/2020,1895.66 58 | 03/10/2020,1902.71 59 | 03/09/2020,1912.46 60 | 03/06/2020,1931.59 61 | 03/05/2020,1943.61 62 | 03/04/2020,1962.71 63 | 03/03/2020,1982.41 64 | 03/02/2020,2011.43 65 | 02/28/2020,2033.85 66 | 02/27/2020,2061.75 67 | 02/26/2020,2091.26 68 | 02/25/2020,2111.30 69 | 02/24/2020,2131.09 70 | 02/21/2020,2144.93 71 | 02/20/2020,2143.08 72 | 02/19/2020,2131.65 73 | 02/18/2020,2117.17 74 | 02/14/2020,2105.39 75 | 02/13/2020,2090.87 76 | 02/12/2020,2075.19 77 | 02/11/2020,2043.04 78 | 02/10/2020,2010.51 79 | 02/07/2020,1979.32 80 | 02/06/2020,1951.44 81 | 02/05/2020,1930.49 82 | 02/04/2020,1913.23 83 | 02/03/2020,1895.21 84 | 01/31/2020,1882.74 85 | 01/30/2020,1866.74 86 | 01/29/2020,1867.55 87 | 01/28/2020,1867.99 88 | 01/27/2020,1869.79 89 | 01/24/2020,1876.79 90 | 01/23/2020,1879.18 91 | 01/22/2020,1881.01 92 | 01/21/2020,1881.51 93 | 01/17/2020,1883.16 94 | 01/16/2020,1887.40 95 | 01/15/2020,1887.07 96 | 01/14/2020,1891.07 97 | 01/13/2020,1888.67 98 | 01/10/2020,1883.74 99 | 01/09/2020,1882.25 100 | 01/08/2020,1878.67 101 | 01/07/2020,1867.25 -------------------------------------------------------------------------------- /src/test/resources/vwma.csv: -------------------------------------------------------------------------------- 1 | date,vwma(20) 2 | 05/29/2020,2387.60 3 | 05/28/2020,2394.71 4 | 05/27/2020,2393.37 5 | 05/26/2020,2387.78 6 | 05/22/2020,2385.74 7 | 05/21/2020,2385.18 8 | 05/20/2020,2382.58 9 | 05/19/2020,2376.78 10 | 05/18/2020,2369.77 11 | 05/15/2020,2368.66 12 | 05/14/2020,2367.44 13 | 05/13/2020,2371.18 14 | 05/12/2020,2367.44 15 | 05/11/2020,2361.91 16 | 05/08/2020,2349.89 17 | 05/07/2020,2337.40 18 | 05/06/2020,2327.01 19 | 05/05/2020,2313.47 20 | 05/04/2020,2299.02 21 | 05/01/2020,2287.12 22 | 04/30/2020,2273.96 23 | 04/29/2020,2244.27 24 | 04/28/2020,2226.12 25 | 04/27/2020,2208.35 26 | 04/24/2020,2185.92 27 | 04/23/2020,2166.56 28 | 04/22/2020,2141.58 29 | 04/21/2020,2122.26 30 | 04/20/2020,2095.88 31 | 04/17/2020,2063.13 32 | 04/16/2020,2029.49 33 | 04/15/2020,1978.61 34 | 04/14/2020,1947.24 35 | 04/13/2020,1909.31 36 | 04/09/2020,1888.19 37 | 04/08/2020,1866.07 38 | 04/07/2020,1859.36 39 | 04/06/2020,1855.58 40 | 04/03/2020,1847.07 41 | 04/02/2020,1847.54 42 | 04/01/2020,1847.90 43 | 03/31/2020,1850.34 44 | 03/30/2020,1849.90 45 | 03/27/2020,1849.93 46 | 03/26/2020,1850.25 47 | 03/25/2020,1847.86 48 | 03/24/2020,1850.69 49 | 03/23/2020,1851.45 50 | 03/20/2020,1855.53 51 | 03/19/2020,1863.60 52 | 03/18/2020,1868.69 53 | 03/17/2020,1877.13 54 | 03/16/2020,1889.49 55 | 03/13/2020,1909.48 56 | 03/12/2020,1925.27 57 | 03/11/2020,1958.76 58 | 03/10/2020,1976.24 59 | 03/09/2020,1989.49 60 | 03/06/2020,2008.28 61 | 03/05/2020,2015.21 62 | 03/04/2020,2020.59 63 | 03/03/2020,2024.23 64 | 03/02/2020,2031.79 65 | 02/28/2020,2033.33 66 | 02/27/2020,2037.06 67 | 02/26/2020,2045.83 68 | 02/25/2020,2043.82 69 | 02/24/2020,2040.43 70 | 02/21/2020,2035.27 71 | 02/20/2020,2027.88 72 | 02/19/2020,2018.39 73 | 02/18/2020,2008.87 74 | 02/14/2020,1997.79 75 | 02/13/2020,1990.38 76 | 02/12/2020,1980.98 77 | 02/11/2020,1970.22 78 | 02/10/2020,1955.96 79 | 02/07/2020,1943.08 80 | 02/06/2020,1933.24 81 | 02/05/2020,1927.11 82 | 02/04/2020,1920.25 83 | 02/03/2020,1911.14 84 | 01/31/2020,1902.66 85 | 01/30/2020,1878.56 86 | 01/29/2020,1878.14 87 | 01/28/2020,1877.06 88 | 01/27/2020,1877.37 89 | 01/24/2020,1879.03 90 | 01/23/2020,1878.84 91 | 01/22/2020,1876.00 92 | 01/21/2020,1869.05 93 | 01/17/2020,1864.87 94 | 01/16/2020,1861.00 95 | 01/15/2020,1856.75 96 | 01/14/2020,1852.67 97 | 01/13/2020,1848.28 98 | 01/10/2020,1842.73 99 | 01/09/2020,1838.27 100 | 01/08/2020,1831.81 101 | 01/07/2020,1825.71 -------------------------------------------------------------------------------- /src/test/resources/wma.csv: -------------------------------------------------------------------------------- 1 | date,wma(9) 2 | 05/29/2020,2430.76 3 | 05/28/2020,2428.96 4 | 05/27/2020,2435.14 5 | 05/26/2020,2438.52 6 | 05/22/2020,2438.16 7 | 05/21/2020,2434.18 8 | 05/20/2020,2426.73 9 | 05/19/2020,2406.15 10 | 05/18/2020,2393.11 11 | 05/15/2020,2382.28 12 | 05/14/2020,2372.66 13 | 05/13/2020,2364.93 14 | 05/12/2020,2363.76 15 | 05/11/2020,2365.12 16 | 05/08/2020,2353.97 17 | 05/07/2020,2348.61 18 | 05/06/2020,2346.60 19 | 05/05/2020,2348.94 20 | 05/04/2020,2358.98 21 | 05/01/2020,2369.65 22 | 04/30/2020,2388.70 23 | 04/29/2020,2367.96 24 | 04/28/2020,2368.26 25 | 04/27/2020,2380.15 26 | 04/24/2020,2377.60 27 | 04/23/2020,2362.84 28 | 04/22/2020,2342.31 29 | 04/21/2020,2321.84 30 | 04/20/2020,2301.42 31 | 04/17/2020,2259.10 32 | 04/16/2020,2210.09 33 | 04/15/2020,2143.57 34 | 04/14/2020,2088.26 35 | 04/13/2020,2030.41 36 | 04/09/2020,1990.90 37 | 04/08/2020,1973.44 38 | 04/07/2020,1953.98 39 | 04/06/2020,1938.01 40 | 04/03/2020,1923.57 41 | 04/02/2020,1927.24 42 | 04/01/2020,1926.85 43 | 03/31/2020,1928.09 44 | 03/30/2020,1918.26 45 | 03/27/2020,1902.12 46 | 03/26/2020,1894.06 47 | 03/25/2020,1871.14 48 | 03/24/2020,1857.49 49 | 03/23/2020,1830.35 50 | 03/20/2020,1810.41 51 | 03/19/2020,1800.81 52 | 03/18/2020,1784.69 53 | 03/17/2020,1780.85 54 | 03/16/2020,1785.17 55 | 03/13/2020,1818.11 56 | 03/12/2020,1835.64 57 | 03/11/2020,1879.45 58 | 03/10/2020,1895.82 59 | 03/09/2020,1899.95 60 | 03/06/2020,1926.14 61 | 03/05/2020,1934.65 62 | 03/04/2020,1942.38 63 | 03/03/2020,1943.70 64 | 03/02/2020,1964.19 65 | 02/28/2020,1980.17 66 | 02/27/2020,2015.77 67 | 02/26/2020,2057.16 68 | 02/25/2020,2083.50 69 | 02/24/2020,2115.17 70 | 02/21/2020,2142.30 71 | 02/20/2020,2151.72 72 | 02/19/2020,2147.43 73 | 02/18/2020,2136.82 74 | 02/14/2020,2126.76 75 | 02/13/2020,2117.96 76 | 02/12/2020,2103.03 77 | 02/11/2020,2079.63 78 | 02/10/2020,2051.58 79 | 02/07/2020,2020.66 80 | 02/06/2020,1995.09 81 | 02/05/2020,1971.14 82 | 02/04/2020,1945.81 83 | 02/03/2020,1914.92 84 | 01/31/2020,1890.63 85 | 01/30/2020,1862.23 86 | 01/29/2020,1861.61 87 | 01/28/2020,1863.61 88 | 01/27/2020,1866.92 89 | 01/24/2020,1876.60 90 | 01/23/2020,1880.11 91 | 01/22/2020,1879.40 92 | 01/21/2020,1878.21 93 | 01/17/2020,1876.44 94 | 01/16/2020,1880.98 95 | 01/15/2020,1882.80 96 | 01/14/2020,1888.61 97 | 01/13/2020,1892.46 98 | 01/10/2020,1890.95 99 | 01/09/2020,1890.77 100 | 01/08/2020,1886.29 101 | 01/07/2020,1881.35 -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/AccumulationDistributionTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.loadAmazonData 31 | import vista.loadIndicatorData 32 | import vista.math.na 33 | import vista.math.numOf 34 | import vista.series.seriesOf 35 | 36 | internal class AccumulationDistributionTest { 37 | 38 | @Test 39 | fun withIntSeries() { 40 | val close = seriesOf(1..20) 41 | val volume = seriesOf(1..20) 42 | 43 | val high = close * 2 44 | val low = close * 0.5 45 | 46 | val adl = adl(close, high, low, volume) 47 | 48 | assertThat(adl[0].round(2)).isEqualTo(numOf(-70.0)) // current value 49 | assertThat(adl[1].round(2)).isEqualTo(numOf(-63.33)) // previous value 50 | assertThat(adl[19].round(2)).isEqualTo(numOf(-0.33)) 51 | assertThat(adl[20]).isEqualTo(na) // oldest value 52 | } 53 | 54 | @Test 55 | fun withMarketData() { 56 | val data = loadAmazonData() 57 | val expected = loadIndicatorData("adl.csv") 58 | 59 | val actual = data.adl() 60 | 61 | for (i in 0..99) 62 | assertThat(actual[i].round(2)).isEqualTo(expected[i][0]) 63 | } 64 | } -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/ChaikinOscillatorTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.loadAmazonData 31 | import vista.loadIndicatorData 32 | import vista.math.na 33 | import vista.math.numOf 34 | import vista.series.seriesOf 35 | 36 | internal class ChaikinOscillatorTest { 37 | 38 | @Test 39 | fun withIntSeries() { 40 | val close = seriesOf(1..20) 41 | val volume = seriesOf(1..20) 42 | 43 | val high = close * 2 44 | val low = close * 0.5 45 | 46 | val chaikin = co(close, high, low, volume) 47 | 48 | assertThat(chaikin[0].round(2)).isEqualTo(numOf(-17.29)) // current value 49 | assertThat(chaikin[1].round(2)).isEqualTo(numOf(-16.20)) // previous value 50 | assertThat(chaikin[10].round(2)).isEqualTo(numOf(-8.00)) 51 | assertThat(chaikin[11]).isEqualTo(na) // oldest value 52 | } 53 | 54 | @Test 55 | fun withMarketData() { 56 | val data = loadAmazonData() 57 | val expected = loadIndicatorData("co.csv") 58 | 59 | val actual = data.co() 60 | 61 | for (i in 0..99) 62 | assertThat(actual[i].round(2)).isEqualTo(expected[i][0]) 63 | } 64 | } -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/IchimokuCloud.kt: -------------------------------------------------------------------------------- 1 | package vista.indicators 2 | 3 | import vista.data.Data 4 | import vista.series.Series 5 | 6 | /** 7 | * The Ichimoku Cloud series. 8 | */ 9 | data class IchimokuCloud( 10 | /** Tenkan-Sen (Conversion Line) **/ 11 | val ts: Series, 12 | /** Kijun-Sen (Base Line) **/ 13 | val ks: Series, 14 | /** Senkou Span A (Leading Span A) **/ 15 | val ssa: Series, 16 | /** Senkou Span B (Leading Span B) **/ 17 | val ssb: Series 18 | ) 19 | 20 | /** 21 | * The Ichimoku Cloud, which shows support and resistance levels, as well as momentum and trend direction. 22 | * 23 | * Returns the Tenkan-Sen (Conversion Line), Kijun-Sen (Base Line), Senkou Span A (Leading Span A), and Senkou Span B (Leading Span B). 24 | * 25 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/ichimoku) 26 | * 27 | * @param high Series of high values 28 | * @param low Series of low values 29 | * @param tsLength Number of bars (length) used by Tenkan-Sen 30 | * @param ksLength Number of bars (length) used by Kijun-Sen 31 | * @param ssLength Number of bars (length) used by Senkou Span 32 | * @sample vista.indicators.IchimokuCloudTest.withIntSeries 33 | */ 34 | fun ichimoku( 35 | high: Series, 36 | low: Series, 37 | tsLength: Int = 9, 38 | ksLength: Int = 26, 39 | ssLength: Int = 52, 40 | displacement: Int = 26 41 | ): IchimokuCloud { 42 | val avg: (Int) -> Series = { (highest(high, it) + lowest(low, it)) / 2 } 43 | val ts = avg(tsLength) 44 | val ks = avg(ksLength) 45 | val ssa = (ts + ks) / 2 46 | val ssb = avg(ssLength) 47 | val d = displacement - 1 48 | return IchimokuCloud(ts, ks, ssa(d), ssb(d)) 49 | } 50 | 51 | /** 52 | * The Ichimoku Cloud, which shows support and resistance levels, as well as momentum and trend direction. 53 | * 54 | * Returns the Tenkan-Sen (Conversion Line), Kijun-Sen (Base Line), Senkou Span A (Leading Span A), and Senkou Span B (Leading Span B). 55 | * 56 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/ichimoku) 57 | * 58 | * @param tsLength Number of bars (length) used by Tenkan-Sen 59 | * @param ksLength Number of bars (length) used by Kijun-Sen 60 | * @param ssLength Number of bars (length) used by Senkou Span 61 | * @sample vista.indicators.IchimokuCloudTest.withMarketData 62 | */ 63 | fun Data.ichimoku( 64 | tsLength: Int = 9, 65 | ksLength: Int = 26, 66 | ssLength: Int = 52, 67 | displacement: Int = 26 68 | ): IchimokuCloud = ichimoku(high, low, tsLength, ksLength, ssLength, displacement) -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/VolumeWeightedMovingAverage.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import vista.data.Data 29 | import vista.series.Series 30 | 31 | /** 32 | * The volume weighted moving average of [source] for [n] bars back, which emphasizes volume 33 | * by weighing prices based on the amount of trading activity in a given period of time. 34 | * 35 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/trend?id=volume-weighted-moving-average-vwma) 36 | * 37 | * @param source Series of values to process 38 | * @param volume Series of volume values 39 | * @param n Number of bars (length) 40 | * @sample vista.indicators.VolumeWeightedMovingAverageTest.withIntSeries 41 | * @see [sma] 42 | */ 43 | fun vwma(source: Series, volume: Series, n: Int = 20) = sma(source * volume, n) / sma(volume, n) 44 | 45 | /** 46 | * The volume weighted moving average of close price for [n] bars back, which emphasizes volume 47 | * by weighing prices based on the amount of trading activity in a given period of time. 48 | * 49 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/trend?id=volume-weighted-moving-average-vwma) 50 | * 51 | * @param n Number of bars (length) 52 | * @sample vista.indicators.VolumeWeightedMovingAverageTest.withMarketData 53 | * @see [sma] 54 | */ 55 | fun Data.vwma(n: Int = 20) = vwma(close, volume, n) -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/HullMovingAverage.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import vista.data.Data 29 | import vista.series.Series 30 | import kotlin.math.sqrt 31 | 32 | /** 33 | * The hull moving average of [source] for [n] bars back, which reduces lag of traditional moving averages. 34 | * 35 | * `hma = wma(2 * wma(n/2) − wma(n)), sqrt(n))` 36 | * 37 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/trend?id=hull-moving-average-hma) 38 | * 39 | * @param source Series of values to process 40 | * @param n Number of bars (length) 41 | * @sample vista.indicators.HullMovingAverageTest.withIntSeries 42 | * @see [wma] 43 | */ 44 | fun hma(source: Series, n: Int = 9): Series { 45 | val wma1 = wma(source, n / 2) * 2 46 | val wma2 = wma(source, n) 47 | val length = sqrt(n.toDouble()).toInt() 48 | return wma(wma1 - wma2, length) 49 | } 50 | 51 | /** 52 | * The hull moving average of close price for [n] bars back, which reduces lag of traditional moving averages. 53 | * 54 | * `hma = wma(2 * wma(n/2) − wma(n)), sqrt(n))` 55 | * 56 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/trend?id=hull-moving-average-hma) 57 | * 58 | * @param n Number of bars (length) 59 | * @sample vista.indicators.HullMovingAverageTest.withMarketData 60 | * @see [wma] 61 | */ 62 | fun Data.hma(n: Int = 9) = hma(close, n) -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS="-Xmx64m" 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/SimpleMovingAverageTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.data.dataOf 31 | import vista.loadAmazonData 32 | import vista.loadIndicatorData 33 | import vista.math.na 34 | import vista.math.numOf 35 | import vista.series.seriesOf 36 | 37 | internal class SimpleMovingAverageTest { 38 | 39 | @Test 40 | fun test() { 41 | val data = dataOf("https://bulltimate.github.io/vista/amzn.csv") 42 | 43 | val ic = data.ichimoku() 44 | 45 | println("The Tenkan-Sen of last period is ${ic.ts[0]}") 46 | println("The Tenkan-Sen of prev period is ${ic.ts[1]}") 47 | 48 | 49 | } 50 | 51 | @Test 52 | fun withIntSeries() { 53 | val series = seriesOf(1, 2, 3) 54 | 55 | val sma = sma(series, 2) 56 | 57 | assertThat(sma[0]).isEqualTo(numOf(2.5)) // current value 58 | assertThat(sma[1]).isEqualTo(numOf(1.5)) // previous value 59 | assertThat(sma[2]).isEqualTo(na) // oldest value 60 | } 61 | 62 | @Test 63 | fun withMarketData() { 64 | val data = loadAmazonData() 65 | val expected = loadIndicatorData("sma.csv") 66 | 67 | val actual = data.sma(9) 68 | 69 | for (i in 0..99) 70 | assertThat(actual[i].round(2)).isEqualTo(expected[i][0]) 71 | } 72 | } -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/RateOfChange.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import vista.data.Data 29 | import vista.math.change 30 | import vista.series.Series 31 | 32 | /** 33 | * The rate of change, which is the difference between current value of [source] and the value of [source] that was [n] days ago. 34 | * 35 | * **See:** [TradingView](https://www.tradingview.com/pine-script-reference/#fun_roc) 36 | * 37 | * @param source Series of values to process 38 | * @param n Number of bars (length) 39 | * @sample vista.indicators.RateOfChangeTest.withIntSeries 40 | */ 41 | fun roc(source: Series, n: Int) = change(source, n) * 100 / source(n) 42 | 43 | /** 44 | * The rate of change, which is the difference between current close price and the value that was [n] days ago. 45 | * 46 | * **See:** [TradingView](https://www.tradingview.com/pine-script-reference/#fun_roc) 47 | * 48 | * @param n Number of bars (length) 49 | * @sample vista.indicators.RateOfChangeTest.withMarketData 50 | */ 51 | fun Data.roc(n: Int) = roc(close, n) 52 | 53 | /** 54 | * The rate of change, which is the difference between current volume and the value that was [n] days ago. 55 | * 56 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/volume?id=volume-rate-of-change-vroc) 57 | * 58 | * @param n Number of bars (length) 59 | * @sample vista.indicators.RateOfChangeTest.withMarketData 60 | */ 61 | fun Data.vroc(n: Int) = roc(volume, n) -------------------------------------------------------------------------------- /src/test/kotlin/vista/Data.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista 27 | 28 | import vista.data.Data 29 | import vista.data.dataOf 30 | import vista.math.NaN 31 | import vista.math.Num 32 | import vista.math.numOf 33 | 34 | /** 35 | * Load the market data from resources folder. 36 | */ 37 | fun loadAmazonData(): Data { 38 | val csv = Data::class.java.classLoader.getResource("amzn.csv") 39 | return dataOf(csv!!.toString()) 40 | } 41 | 42 | /** 43 | * Load the indicator values [file] from resources folder. 44 | */ 45 | fun loadIndicatorData(file: String): List> { 46 | val values = mutableListOf>() 47 | val csv = Data::class.java.classLoader.getResourceAsStream(file) 48 | csv!!.bufferedReader().useLines { lines -> 49 | var size: Int? = null 50 | for (line in lines) { 51 | if (size != null) { 52 | values.add(line2num(line, size)) 53 | } else { 54 | size = line.split(",").size 55 | } 56 | } 57 | } 58 | assert(values.size == 100) 59 | return values 60 | } 61 | 62 | /** 63 | * Returns the [Num] parsed from string [line]. 64 | */ 65 | private fun line2num(line: String, size: Int): List { 66 | val values = mutableListOf() 67 | val split = line.split(",") 68 | for (i in 1 until size) { 69 | val value = if (i < split.size) numOf(split[i]) else NaN 70 | values.add(value) 71 | } 72 | return values 73 | } -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/Deviation.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import vista.data.Data 29 | import vista.math.Num 30 | import vista.math.abs 31 | import vista.series.Series 32 | 33 | /** 34 | * Deviation indicator. 35 | */ 36 | internal class Deviation( 37 | private val source: Series, 38 | private val n: Int 39 | ) : CachedIndicator(source) { 40 | 41 | override val size: Int get() = source.size + 1 - n 42 | 43 | private val mean = sma(source, n) 44 | 45 | override fun calculate(i: Int): Num = sma(abs((source - mean[i])), n)[i] 46 | } 47 | 48 | /** 49 | * The deviation of [source] for [n] bars back. 50 | * 51 | * Measures the difference between the series and it's [sma]. 52 | * 53 | * **See:** [TradingView](https://www.tradingview.com/pine-script-reference/#fun_dev) 54 | * 55 | * @param source Series of values to process 56 | * @param n Number of bars (length) 57 | * @sample vista.indicators.DeviationTest.withIntSeries 58 | * @see [stdev] 59 | */ 60 | fun dev(source: Series, n: Int): Series = Deviation(source, n) 61 | 62 | /** 63 | * The deviation of close price for [n] bars back. 64 | * 65 | * Measures the difference between the series and it's [sma]. 66 | * 67 | * **See:** [TradingView](https://www.tradingview.com/pine-script-reference/#fun_dev) 68 | * 69 | * @param n Number of bars (length) 70 | * @sample vista.indicators.DeviationTest.withMarketData 71 | * @see [stdev] 72 | */ 73 | fun Data.dev(n: Int): Series = Deviation(close, n) -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/StandardDeviation.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import vista.data.Data 29 | import vista.math.Num 30 | import vista.math.pow 31 | import vista.series.Series 32 | 33 | /** 34 | * Standard deviation indicator. 35 | */ 36 | internal class StandardDeviation( 37 | private val source: Series, 38 | private val n: Int 39 | ) : CachedIndicator(source) { 40 | 41 | override val size: Int get() = source.size + 1 - n 42 | 43 | private val mean = sma(source, n) 44 | 45 | override fun calculate(i: Int): Num { 46 | val diff = source - mean[i] 47 | val pow = pow(diff, 2) 48 | return sma(pow, n)[i].sqrt() 49 | } 50 | } 51 | 52 | /** 53 | * The standard deviation of [source] for [n] bars back. 54 | * 55 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/volatility?id=standard-deviation) 56 | * 57 | * @param source Series of values to process 58 | * @param n Number of bars (length) 59 | * @sample vista.indicators.StandardDeviationTest.withIntSeries 60 | * @see [dev] 61 | */ 62 | fun stdev(source: Series, n: Int): Series = StandardDeviation(source, n) 63 | 64 | /** 65 | * The standard deviation of close price for [n] bars back. 66 | * 67 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/volatility?id=standard-deviation) 68 | * 69 | * @param n Number of bars (length) 70 | * @sample vista.indicators.StandardDeviationTest.withMarketData 71 | * @see [dev] 72 | */ 73 | fun Data.stdev(n: Int): Series = stdev(close, n) -------------------------------------------------------------------------------- /src/test/resources/co.csv: -------------------------------------------------------------------------------- 1 | date,chaikin(3,10) 2 | 05/29/2020,2037608.82 3 | 05/28/2020,993580.54 4 | 05/27/2020,965749.41 5 | 05/26/2020,133461.68 6 | 05/22/2020,1702266.34 7 | 05/21/2020,3286689.29 8 | 05/20/2020,4957311.07 9 | 05/19/2020,4364576.02 10 | 05/18/2020,4662209.21 11 | 05/15/2020,3435496.51 12 | 05/14/2020,2135145.96 13 | 05/13/2020,1628768.29 14 | 05/12/2020,2491685.31 15 | 05/11/2020,3532623.28 16 | 05/08/2020,3046247.79 17 | 05/07/2020,2577917.17 18 | 05/06/2020,2097486.33 19 | 05/05/2020,1750911.81 20 | 05/04/2020,2137973.46 21 | 05/01/2020,1313843.72 22 | 04/30/2020,1613680.36 23 | 04/29/2020,-1551783.89 24 | 04/28/2020,-1716701.72 25 | 04/27/2020,160006.93 26 | 04/24/2020,1594703.08 27 | 04/23/2020,1750629.34 28 | 04/22/2020,3139447.95 29 | 04/21/2020,5144684.17 30 | 04/20/2020,7528185.20 31 | 04/17/2020,9657911.03 32 | 04/16/2020,9380980.25 33 | 04/15/2020,9114986.39 34 | 04/14/2020,7953661.93 35 | 04/13/2020,5582888.33 36 | 04/09/2020,3804873.99 37 | 04/08/2020,3018262.22 38 | 04/07/2020,1947305.75 39 | 04/06/2020,1801804.37 40 | 04/03/2020,-12856.67 41 | 04/02/2020,-77510.91 42 | 04/01/2020,-392016.27 43 | 03/31/2020,735182.40 44 | 03/30/2020,1935243.75 45 | 03/27/2020,1382199.81 46 | 03/26/2020,2982831.33 47 | 03/25/2020,2079886.45 48 | 03/24/2020,4115695.64 49 | 03/23/2020,3147618.39 50 | 03/20/2020,2609961.28 51 | 03/19/2020,4914961.14 52 | 03/18/2020,4663261.73 53 | 03/17/2020,1874886.40 54 | 03/16/2020,334174.49 55 | 03/13/2020,-47157.62 56 | 03/12/2020,-1342065.46 57 | 03/11/2020,2472991.63 58 | 03/10/2020,2740035.87 59 | 03/09/2020,827589.43 60 | 03/06/2020,1159703.26 61 | 03/05/2020,346090.46 62 | 03/04/2020,442726.18 63 | 03/03/2020,-1069369.00 64 | 03/02/2020,-1103394.72 65 | 02/28/2020,-4538459.77 66 | 02/27/2020,-6737374.61 67 | 02/26/2020,-4360027.19 68 | 02/25/2020,-3702548.55 69 | 02/24/2020,-2470844.11 70 | 02/21/2020,-1929501.86 71 | 02/20/2020,-1039519.85 72 | 02/19/2020,-1230637.93 73 | 02/18/2020,-1327592.15 74 | 02/14/2020,-1749715.16 75 | 02/13/2020,-1235261.18 76 | 02/12/2020,-632026.23 77 | 02/11/2020,-62619.31 78 | 02/10/2020,-271409.17 79 | 02/07/2020,-2542223.69 80 | 02/06/2020,-3858023.59 81 | 02/05/2020,-4778897.86 82 | 02/04/2020,-4446415.95 83 | 02/03/2020,-4676290.21 84 | 01/31/2020,-1961044.37 85 | 01/30/2020,1275052.65 86 | 01/29/2020,-283515.21 87 | 01/28/2020,-77353.75 88 | 01/27/2020,-696907.50 89 | 01/24/2020,-628214.42 90 | 01/23/2020,-284331.01 91 | 01/22/2020,-560251.67 92 | 01/21/2020,-389753.60 93 | 01/17/2020,-1312085.89 94 | 01/16/2020,-773311.13 95 | 01/15/2020,-813062.86 96 | 01/14/2020,-278226.01 97 | 01/13/2020,151717.72 98 | 01/10/2020,532634.64 99 | 01/09/2020,1824106.10 100 | 01/08/2020,2874889.76 101 | 01/07/2020,3512706.66 -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/BollingerBand.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import vista.data.Data 29 | import vista.series.Series 30 | 31 | /** 32 | * The Bollinger Bands, defined as two standard deviations (positively and negatively) away from the [sma]. 33 | * 34 | * Returns a triple of middle, upper and lower lines. 35 | * 36 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/volatility?id=bollinger-bands%c2%ae) 37 | * 38 | * @param source Series of values to process 39 | * @param n Number of bars (length) 40 | * @param factor Standard deviation factor 41 | * @sample vista.indicators.BollingerBandTest.withIntSeries 42 | * @see [sma] [stdev] 43 | */ 44 | fun bb( 45 | source: Series, 46 | n: Int = 20, 47 | factor: Int = 2 48 | ): Triple { 49 | val basis = sma(source, n) 50 | val dev = stdev(source, n) * factor 51 | val upper = basis + dev 52 | val lower = basis - dev 53 | return Triple(basis, upper, lower) 54 | } 55 | 56 | /** 57 | * The Bollinger Bands, defined as two standard deviations (positively and negatively) away from the [sma]. 58 | * 59 | * Returns a triple of middle, upper and lower lines. 60 | * 61 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/volatility?id=bollinger-bands%c2%ae) 62 | * 63 | * @param n Number of bars (length) 64 | * @param factor Standard deviation factor 65 | * @sample vista.indicators.BollingerBandTest.withMarketData 66 | * @see [sma] [stdev] 67 | */ 68 | fun Data.bb(n: Int = 20, factor: Int = 2) = bb(close, n, factor) -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/SimpleMovingAverage.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import vista.data.Data 29 | import vista.math.Num 30 | import vista.math.numOf 31 | import vista.series.Series 32 | 33 | /** 34 | * Simple Moving Average (SMA) indicator. 35 | */ 36 | internal class SimpleMovingAverage( 37 | private val source: Series, 38 | private val n: Int 39 | ) : Indicator(source) { 40 | 41 | override val size: Int get() = source.size + 1 - n 42 | 43 | override fun calculate(i: Int): Num { 44 | var sum = Num.ZERO 45 | for (j in 0 until n) 46 | sum += source[i + j] 47 | return sum / numOf(n) 48 | } 49 | } 50 | 51 | /** 52 | * The simple moving average, that is the sum of last [n] values of [source], divided by [n]. 53 | * 54 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/trend?id=simple-moving-average-sma) 55 | * 56 | * @param source Series of values to process 57 | * @param n Number of bars (length) 58 | * @sample vista.indicators.SimpleMovingAverageTest.withIntSeries 59 | */ 60 | fun sma(source: Series, n: Int = 9): Series = if (n > 1) SimpleMovingAverage(source, n) else source 61 | 62 | /** 63 | * The simple moving average, that is the sum of last [n] close prices, divided by [n]. 64 | * 65 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/trend?id=simple-moving-average-sma) 66 | * 67 | * @param n Number of bars (length) 68 | * @sample vista.indicators.SimpleMovingAverageTest.withMarketData 69 | */ 70 | fun Data.sma(n: Int = 9): Series = sma(close, n) -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/StochasticOscillatorTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.loadAmazonData 31 | import vista.loadIndicatorData 32 | import vista.math.na 33 | import vista.math.numOf 34 | import vista.series.seriesOf 35 | 36 | internal class StochasticOscillatorTest { 37 | 38 | @Test 39 | fun withIntSeries() { 40 | val close = seriesOf(1..20) 41 | 42 | val high = close * 1.5 43 | val low = close * 0.5 44 | 45 | val (k, d) = stoch(close, high, low) 46 | 47 | assertThat(k[0].round(2)).isEqualTo(numOf(62.76)) // current value 48 | assertThat(k[1].round(2)).isEqualTo(numOf(63.28)) // previous value 49 | assertThat(k[4].round(2)).isEqualTo(numOf(65.14)) 50 | assertThat(k[5]).isEqualTo(na) 51 | 52 | assertThat(d[0].round(2)).isEqualTo(numOf(63.29)) // current value 53 | assertThat(d[1].round(2)).isEqualTo(numOf(63.86)) // previous value 54 | assertThat(d[2].round(2)).isEqualTo(numOf(64.48)) 55 | assertThat(d[3]).isEqualTo(na) 56 | } 57 | 58 | @Test 59 | fun withMarketData() { 60 | val data = loadAmazonData() 61 | val expected = loadIndicatorData("stoch.csv") 62 | 63 | val (k, d) = data.stoch() 64 | 65 | for (i in 0..99) { 66 | assertThat(k[i].round(2)).isEqualTo(expected[i][0]) 67 | assertThat(d[i].round(2)).isEqualTo(expected[i][1]) 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/ChaikinOscillator.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import vista.data.Data 29 | import vista.series.Series 30 | 31 | /** 32 | * The Chaikin oscillator, which measures the accumulation-distribution line of [macd]. 33 | * 34 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/volume?id=chainkin-oscillator) 35 | * 36 | * @param close Series of close prices 37 | * @param high Series of high prices 38 | * @param low Series of low prices 39 | * @param volume Series of volumes 40 | * @param fastLength Number of bars (length) used by the fast [ema] 41 | * @param slowLength Number of bars (length) used by the slow [ema] 42 | * @sample vista.indicators.ChaikinOscillatorTest.withIntSeries 43 | */ 44 | fun co( 45 | close: Series, 46 | high: Series, 47 | low: Series, 48 | volume: Series, 49 | fastLength: Int = 3, 50 | slowLength: Int = 10 51 | ): Series { 52 | val accdist = adl(close, high, low, volume) 53 | return ema(accdist, fastLength) - ema(accdist, slowLength) 54 | } 55 | 56 | /** 57 | * The Chaikin oscillator, which measures the accumulation-distribution line of [macd]. 58 | * 59 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/volume?id=chainkin-oscillator) 60 | * 61 | * @param fastLength Number of bars (length) used by the fast [ema] 62 | * @param slowLength Number of bars (length) used by the slow [ema] 63 | * @sample vista.indicators.ChaikinOscillatorTest.withMarketData 64 | */ 65 | fun Data.co(fastLength: Int = 3, slowLength: Int = 10) = co(close, high, low, volume, fastLength, slowLength) -------------------------------------------------------------------------------- /src/test/resources/stochrsi.csv: -------------------------------------------------------------------------------- 1 | date,k,d 2 | 05/29/2020,11.64,9.45 3 | 05/28/2020,3.09,14.87 4 | 05/27/2020,13.62,33.22 5 | 05/26/2020,27.90,51.19 6 | 05/22/2020,58.14,65.06 7 | 05/21/2020,67.53,62.27 8 | 05/20/2020,69.50,53.16 9 | 05/19/2020,49.79,40.10 10 | 05/18/2020,40.20,30.68 11 | 05/15/2020,30.32,25.86 12 | 05/14/2020,21.53,25.83 13 | 05/13/2020,25.72,29.80 14 | 05/12/2020,30.23,29.66 15 | 05/11/2020,33.44,25.27 16 | 05/08/2020,25.30,17.97 17 | 05/07/2020,17.08,11.25 18 | 05/06/2020,11.53,12.44 19 | 05/05/2020,5.15,17.86 20 | 05/04/2020,20.65,26.39 21 | 05/01/2020,27.79,29.41 22 | 04/30/2020,30.72,34.88 23 | 04/29/2020,29.71,46.99 24 | 04/28/2020,44.21,61.62 25 | 04/27/2020,67.05,70.59 26 | 04/24/2020,73.61,73.35 27 | 04/23/2020,71.10,75.70 28 | 04/22/2020,75.32,82.80 29 | 04/21/2020,80.69,89.61 30 | 04/20/2020,92.39,96.04 31 | 04/17/2020,95.75,98.58 32 | 04/16/2020,100.00,99.97 33 | 04/15/2020,100.00,99.95 34 | 04/14/2020,99.92,99.92 35 | 04/13/2020,99.92,99.95 36 | 04/09/2020,99.92,95.18 37 | 04/08/2020,100.00,88.44 38 | 04/07/2020,85.60,79.22 39 | 04/06/2020,79.70,79.11 40 | 04/03/2020,72.35,82.93 41 | 04/02/2020,85.26,89.81 42 | 04/01/2020,91.17,92.88 43 | 03/31/2020,92.99,92.11 44 | 03/30/2020,94.47,92.58 45 | 03/27/2020,88.87,92.56 46 | 03/26/2020,94.40,94.96 47 | 03/25/2020,94.40,95.51 48 | 03/24/2020,96.07,94.52 49 | 03/23/2020,96.07,91.80 50 | 03/20/2020,91.44,81.01 51 | 03/19/2020,87.89,68.32 52 | 03/18/2020,63.70,48.19 53 | 03/17/2020,53.36,35.12 54 | 03/16/2020,27.50,22.12 55 | 03/13/2020,24.48,17.74 56 | 03/12/2020,14.37,14.10 57 | 03/11/2020,14.37,13.34 58 | 03/10/2020,13.54,16.02 59 | 03/09/2020,12.10,18.58 60 | 03/06/2020,22.42,21.73 61 | 03/05/2020,21.23,18.01 62 | 03/04/2020,21.56,13.32 63 | 03/03/2020,11.24,6.43 64 | 03/02/2020,7.15,2.98 65 | 02/28/2020,0.89,0.89 66 | 02/27/2020,0.89,0.59 67 | 02/26/2020,0.89,2.43 68 | 02/25/2020,0.00,13.93 69 | 02/24/2020,6.39,35.11 70 | 02/21/2020,35.39,60.87 71 | 02/20/2020,63.54,77.64 72 | 02/19/2020,83.68,86.75 73 | 02/18/2020,85.70,91.42 74 | 02/14/2020,90.88,96.19 75 | 02/13/2020,97.68,99.23 76 | 02/12/2020,100.00,99.52 77 | 02/11/2020,100.00,98.24 78 | 02/10/2020,98.56,96.97 79 | 02/07/2020,96.17,96.25 80 | 02/06/2020,96.17,97.13 81 | 02/05/2020,96.42,92.65 82 | 02/04/2020,98.81,80.77 83 | 02/03/2020,82.73,59.87 84 | 01/31/2020,60.79,38.58 85 | 01/30/2020,36.10,21.20 86 | 01/29/2020,18.85,11.63 87 | 01/28/2020,8.65,10.43 88 | 01/27/2020,7.38,16.04 89 | 01/24/2020,15.25,19.62 90 | 01/23/2020,25.49,20.03 91 | 01/22/2020,18.11,13.62 92 | 01/21/2020,16.50,10.19 93 | 01/17/2020,6.26,9.83 94 | 01/16/2020,7.80,16.73 95 | 01/15/2020,15.43,29.90 96 | 01/14/2020,26.95,42.31 97 | 01/13/2020,47.32,56.38 98 | 01/10/2020,52.67,65.73 99 | 01/09/2020,69.16,74.59 100 | 01/08/2020,75.37,79.67 101 | 01/07/2020,79.24,81.22 -------------------------------------------------------------------------------- /src/test/resources/obv.csv: -------------------------------------------------------------------------------- 1 | date,obv 2 | 05/29/2020,92557338.00 3 | 05/28/2020,89028009.00 4 | 05/27/2020,92221145.00 5 | 05/26/2020,97278090.00 6 | 05/22/2020,100846243.00 7 | 05/21/2020,103713322.00 8 | 05/20/2020,108827725.00 9 | 05/19/2020,104829582.00 10 | 05/18/2020,100509084.00 11 | 05/15/2020,96142512.00 12 | 05/14/2020,91907561.00 13 | 05/13/2020,88259433.00 14 | 05/12/2020,83476514.00 15 | 05/11/2020,86551430.00 16 | 05/08/2020,83292199.00 17 | 05/07/2020,80080971.00 18 | 05/06/2020,76684560.00 19 | 05/05/2020,73566746.00 20 | 05/04/2020,70324250.00 21 | 05/01/2020,65458324.00 22 | 04/30/2020,75230929.00 23 | 04/29/2020,65696318.00 24 | 04/28/2020,61104725.00 25 | 04/27/2020,66374171.00 26 | 04/24/2020,72019816.00 27 | 04/23/2020,68188019.00 28 | 04/22/2020,63121467.00 29 | 04/21/2020,58903172.00 30 | 04/20/2020,66379851.00 31 | 04/17/2020,60609157.00 32 | 04/16/2020,68539167.00 33 | 04/15/2020,56500967.00 34 | 04/14/2020,49634400.00 35 | 04/13/2020,41547207.00 36 | 04/09/2020,34830498.00 37 | 04/08/2020,39486115.00 38 | 04/07/2020,35508802.00 39 | 04/06/2020,30394819.00 40 | 04/03/2020,24621638.00 41 | 04/02/2020,28231508.00 42 | 04/01/2020,23895523.00 43 | 03/31/2020,28017398.00 44 | 03/30/2020,33141024.00 45 | 03/27/2020,27014937.00 46 | 03/26/2020,32402834.00 47 | 03/25/2020,26167716.00 48 | 03/24/2020,32646789.00 49 | 03/23/2020,25499709.00 50 | 03/20/2020,17691220.00 51 | 03/19/2020,27509070.00 52 | 03/18/2020,17109130.00 53 | 03/17/2020,7463912.00 54 | 03/16/2020,-3453218.00 55 | 03/13/2020,5464047.00 56 | 03/12/2020,-3345678.00 57 | 03/11/2020,8000532.00 58 | 03/10/2020,13647363.00 59 | 03/09/2020,6514053.00 60 | 03/06/2020,14327285.00 61 | 03/05/2020,19600865.00 62 | 03/04/2020,24349075.00 63 | 03/03/2020,19576156.00 64 | 03/02/2020,27110647.00 65 | 02/28/2020,20348996.00 66 | 02/27/2020,29842793.00 67 | 02/26/2020,37986786.00 68 | 02/25/2020,32746384.00 69 | 02/24/2020,38965478.00 70 | 02/21/2020,45512475.00 71 | 02/20/2020,50163215.00 72 | 02/19/2020,53294557.00 73 | 02/18/2020,50733392.00 74 | 02/14/2020,47782322.00 75 | 02/13/2020,50388491.00 76 | 02/12/2020,53420282.00 77 | 02/11/2020,50086018.00 78 | 02/10/2020,44340007.00 79 | 02/07/2020,39283772.00 80 | 02/06/2020,34188425.00 81 | 02/05/2020,31005471.00 82 | 02/04/2020,35381645.00 83 | 02/03/2020,30092307.00 84 | 01/31/2020,35991401.00 85 | 01/30/2020,20424121.00 86 | 01/29/2020,14096683.00 87 | 01/28/2020,11995293.00 88 | 01/27/2020,9187253.00 89 | 01/24/2020,12715762.00 90 | 01/23/2020,16481943.00 91 | 01/22/2020,18966556.00 92 | 01/21/2020,22182813.00 93 | 01/17/2020,18475028.00 94 | 01/16/2020,22472368.00 95 | 01/15/2020,19812875.00 96 | 01/14/2020,22709467.00 97 | 01/13/2020,26155848.00 98 | 01/10/2020,23370004.00 99 | 01/09/2020,26226963.00 100 | 01/08/2020,23052001.00 101 | 01/07/2020,26563967.00 -------------------------------------------------------------------------------- /src/test/resources/stoch.csv: -------------------------------------------------------------------------------- 1 | date,%K,%D 2 | 05/29/2020,45.00,45.80 3 | 05/28/2020,42.36,50.78 4 | 05/27/2020,50.05,62.14 5 | 05/26/2020,59.92,73.71 6 | 05/22/2020,76.44,82.76 7 | 05/21/2020,84.77,83.09 8 | 05/20/2020,87.08,78.00 9 | 05/19/2020,77.42,69.17 10 | 05/18/2020,69.49,60.87 11 | 05/15/2020,60.59,56.25 12 | 05/14/2020,52.54,55.18 13 | 05/13/2020,55.61,57.34 14 | 05/12/2020,57.39,55.54 15 | 05/11/2020,59.02,50.01 16 | 05/08/2020,50.21,41.71 17 | 05/07/2020,40.79,35.36 18 | 05/06/2020,34.12,40.12 19 | 05/05/2020,31.18,52.57 20 | 05/04/2020,55.07,69.65 21 | 05/01/2020,71.47,76.74 22 | 04/30/2020,82.40,79.52 23 | 04/29/2020,76.36,81.09 24 | 04/28/2020,79.81,84.82 25 | 04/27/2020,87.11,85.88 26 | 04/24/2020,87.54,84.39 27 | 04/23/2020,82.98,82.98 28 | 04/22/2020,82.64,84.65 29 | 04/21/2020,83.31,87.09 30 | 04/20/2020,87.98,90.75 31 | 04/17/2020,89.98,93.45 32 | 04/16/2020,94.28,95.66 33 | 04/15/2020,96.10,96.63 34 | 04/14/2020,96.61,96.21 35 | 04/13/2020,97.18,96.06 36 | 04/09/2020,94.85,92.60 37 | 04/08/2020,96.14,88.87 38 | 04/07/2020,86.80,82.16 39 | 04/06/2020,83.66,80.42 40 | 04/03/2020,76.03,81.67 41 | 04/02/2020,81.58,86.13 42 | 04/01/2020,87.39,90.00 43 | 03/31/2020,89.41,89.85 44 | 03/30/2020,93.19,90.26 45 | 03/27/2020,86.95,87.08 46 | 03/26/2020,90.63,83.86 47 | 03/25/2020,83.66,76.64 48 | 03/24/2020,77.30,69.13 49 | 03/23/2020,68.97,62.59 50 | 03/20/2020,61.12,52.98 51 | 03/19/2020,57.67,43.26 52 | 03/18/2020,40.14,29.29 53 | 03/17/2020,31.98,21.09 54 | 03/16/2020,15.76,15.69 55 | 03/13/2020,15.52,16.69 56 | 03/12/2020,15.80,18.71 57 | 03/11/2020,18.75,20.50 58 | 03/10/2020,21.58,25.17 59 | 03/09/2020,21.17,29.13 60 | 03/06/2020,32.76,34.11 61 | 03/05/2020,33.47,32.48 62 | 03/04/2020,36.10,27.77 63 | 03/03/2020,27.88,18.98 64 | 03/02/2020,19.33,11.48 65 | 02/28/2020,9.73,7.96 66 | 02/27/2020,5.37,12.34 67 | 02/26/2020,8.79,26.62 68 | 02/25/2020,22.87,49.16 69 | 02/24/2020,48.21,71.38 70 | 02/21/2020,76.39,85.52 71 | 02/20/2020,89.53,89.71 72 | 02/19/2020,90.64,89.75 73 | 02/18/2020,88.96,89.95 74 | 02/14/2020,89.64,91.74 75 | 02/13/2020,91.26,93.33 76 | 02/12/2020,94.33,94.52 77 | 02/11/2020,94.40,93.40 78 | 02/10/2020,94.85,92.55 79 | 02/07/2020,90.96,90.07 80 | 02/06/2020,91.85,88.07 81 | 02/05/2020,87.41,81.84 82 | 02/04/2020,84.96,72.98 83 | 02/03/2020,73.14,60.11 84 | 01/31/2020,60.83,45.87 85 | 01/30/2020,46.35,33.35 86 | 01/29/2020,30.44,26.78 87 | 01/28/2020,23.28,29.84 88 | 01/27/2020,26.62,40.79 89 | 01/24/2020,39.60,49.63 90 | 01/23/2020,56.15,54.34 91 | 01/22/2020,53.13,51.64 92 | 01/21/2020,53.73,52.72 93 | 01/17/2020,48.07,56.57 94 | 01/16/2020,56.37,64.69 95 | 01/15/2020,65.27,72.89 96 | 01/14/2020,72.45,78.58 97 | 01/13/2020,80.95,84.17 98 | 01/10/2020,82.35,88.16 99 | 01/09/2020,89.21,91.42 100 | 01/08/2020,92.91,92.70 101 | 01/07/2020,92.12,89.23 -------------------------------------------------------------------------------- /src/test/resources/adl.csv: -------------------------------------------------------------------------------- 1 | date,accdist 2 | 05/29/2020,95938647.55 3 | 05/28/2020,92409318.55 4 | 05/27/2020,93116005.10 5 | 05/26/2020,88445077.20 6 | 05/22/2020,90852129.10 7 | 05/21/2020,92744747.72 8 | 05/20/2020,97340986.62 9 | 05/19/2020,93848412.10 10 | 05/18/2020,95028972.95 11 | 05/15/2020,91863899.02 12 | 05/14/2020,87818098.31 13 | 05/13/2020,84651798.54 14 | 05/12/2020,85313564.55 15 | 05/11/2020,88201102.86 16 | 05/08/2020,86404277.02 17 | 05/07/2020,84813529.82 18 | 05/06/2020,83149918.35 19 | 05/05/2020,81062772.90 20 | 05/04/2020,82727998.22 21 | 05/01/2020,79376988.00 22 | 04/30/2020,83928162.79 23 | 04/29/2020,74634964.91 24 | 04/28/2020,72194226.81 25 | 04/27/2020,76202128.40 26 | 04/24/2020,80055067.37 27 | 04/23/2020,78259317.59 28 | 04/22/2020,79149030.09 29 | 04/21/2020,80916790.00 30 | 04/20/2020,83518664.23 31 | 04/17/2020,87808738.96 32 | 04/16/2020,84600097.15 33 | 04/15/2020,82652966.07 34 | 04/14/2020,79778751.82 35 | 04/13/2020,73018656.56 36 | 04/09/2020,67354862.65 37 | 04/08/2020,65397237.62 38 | 04/07/2020,61662074.57 39 | 04/06/2020,63023118.87 40 | 04/03/2020,57406698.69 41 | 04/02/2020,57630009.64 42 | 04/01/2020,55304313.45 43 | 03/31/2020,57093949.71 44 | 03/30/2020,61023700.81 45 | 03/27/2020,56832693.66 46 | 03/26/2020,62171941.48 47 | 03/25/2020,56122392.47 48 | 03/24/2020,62589407.64 49 | 03/23/2020,59338833.02 50 | 03/20/2020,53939779.08 51 | 03/19/2020,60103403.26 52 | 03/18/2020,61565041.47 53 | 03/17/2020,54246809.64 54 | 03/16/2020,49799377.90 55 | 03/13/2020,50279260.70 56 | 03/12/2020,41687924.19 57 | 03/11/2020,52628192.01 58 | 03/10/2020,55143466.20 59 | 03/09/2020,48469462.63 60 | 03/06/2020,50227978.78 61 | 03/05/2020,47447781.05 62 | 03/04/2020,49569122.51 63 | 03/03/2020,45166104.73 64 | 03/02/2020,49790934.57 65 | 02/28/2020,43118894.85 66 | 02/27/2020,35076394.30 67 | 02/26/2020,42948449.98 68 | 02/25/2020,44489062.45 69 | 02/24/2020,48370077.53 70 | 02/21/2020,49478462.56 71 | 02/20/2020,52818277.79 72 | 02/19/2020,52693887.23 73 | 02/18/2020,53311215.58 74 | 02/14/2020,51823021.08 75 | 02/13/2020,53017220.39 76 | 02/12/2020,54361585.99 77 | 02/11/2020,56437485.93 78 | 02/10/2020,58778453.37 79 | 02/07/2020,54059700.09 80 | 02/06/2020,52210602.63 81 | 02/05/2020,50254349.00 82 | 02/04/2020,52865249.32 83 | 02/03/2020,49987839.93 84 | 01/31/2020,54921071.90 85 | 01/30/2020,66731234.75 86 | 01/29/2020,61648818.24 87 | 01/28/2020,63115424.44 88 | 01/27/2020,61279052.25 89 | 01/24/2020,61232298.82 90 | 01/23/2020,62749067.82 91 | 01/22/2020,61822748.69 92 | 01/21/2020,63655813.74 93 | 01/17/2020,60439226.41 94 | 01/16/2020,62404573.91 95 | 01/15/2020,61824296.18 96 | 01/14/2020,63031920.36 97 | 01/13/2020,63850073.83 98 | 01/10/2020,63234596.67 99 | 01/09/2020,65421326.24 100 | 01/08/2020,67082341.78 101 | 01/07/2020,69012779.12 -------------------------------------------------------------------------------- /src/test/resources/atr.csv: -------------------------------------------------------------------------------- 1 | date,tr(14),atr(14) 2 | 05/29/2020,44.17,64.11 3 | 05/28/2020,58.74,65.64 4 | 05/27/2020,91.86,66.17 5 | 05/26/2020,47.94,64.19 6 | 05/22/2020,39.72,65.44 7 | 05/21/2020,82.91,67.42 8 | 05/20/2020,50.68,66.23 9 | 05/19/2020,58.74,67.43 10 | 05/18/2020,48.99,68.10 11 | 05/15/2020,54.63,69.57 12 | 05/14/2020,38.16,70.72 13 | 05/13/2020,69.89,73.22 14 | 05/12/2020,64.00,73.48 15 | 05/11/2020,47.56,74.21 16 | 05/08/2020,30.24,76.26 17 | 05/07/2020,32.89,79.79 18 | 05/06/2020,39.65,83.40 19 | 05/05/2020,43.87,86.77 20 | 05/04/2020,70.60,90.07 21 | 05/01/2020,215.81,91.57 22 | 04/30/2020,102.29,82.01 23 | 04/29/2020,81.89,80.45 24 | 04/28/2020,70.00,80.34 25 | 04/27/2020,81.88,81.13 26 | 04/24/2020,38.43,81.08 27 | 04/23/2020,60.73,84.36 28 | 04/22/2020,65.88,86.17 29 | 04/21/2020,148.65,87.73 30 | 04/20/2020,69.98,83.05 31 | 04/17/2020,92.17,84.05 32 | 04/16/2020,153.32,83.43 33 | 04/15/2020,88.37,78.05 34 | 04/14/2020,123.13,77.26 35 | 04/13/2020,142.00,73.73 36 | 04/09/2020,35.34,68.48 37 | 04/08/2020,32.85,71.03 38 | 04/07/2020,38.13,73.97 39 | 04/06/2020,91.93,76.72 40 | 04/03/2020,37.18,75.55 41 | 04/02/2020,37.53,78.50 42 | 04/01/2020,56.72,81.66 43 | 03/31/2020,49.01,83.57 44 | 03/30/2020,73.53,86.23 45 | 03/27/2020,55.57,87.21 46 | 03/26/2020,70.65,89.64 47 | 03/25/2020,64.48,91.10 48 | 03/24/2020,54.66,93.15 49 | 03/23/2020,107.40,96.11 50 | 03/20/2020,136.27,95.24 51 | 03/19/2020,115.00,92.09 52 | 03/18/2020,96.66,90.33 53 | 03/17/2020,168.63,89.84 54 | 03/16/2020,158.97,83.78 55 | 03/13/2020,109.70,77.99 56 | 03/12/2020,145.86,75.56 57 | 03/11/2020,90.32,70.15 58 | 03/10/2020,93.66,68.60 59 | 03/09/2020,139.80,66.67 60 | 03/06/2020,54.53,61.04 61 | 03/05/2020,65.83,61.54 62 | 03/04/2020,69.01,61.21 63 | 03/03/2020,108.24,60.61 64 | 03/02/2020,84.51,56.95 65 | 02/28/2020,78.63,54.83 66 | 02/27/2020,96.83,53.00 67 | 02/26/2020,54.22,49.63 68 | 02/25/2020,76.18,49.27 69 | 02/24/2020,108.00,47.21 70 | 02/21/2020,65.10,42.53 71 | 02/20/2020,49.34,40.79 72 | 02/19/2020,29.43,40.14 73 | 02/18/2020,41.96,40.96 74 | 02/14/2020,33.15,40.88 75 | 02/13/2020,28.28,41.48 76 | 02/12/2020,29.45,42.49 77 | 02/11/2020,52.04,43.49 78 | 02/10/2020,56.32,42.84 79 | 02/07/2020,60.43,41.80 80 | 02/06/2020,31.50,40.37 81 | 02/05/2020,39.02,41.05 82 | 02/04/2020,55.60,41.20 83 | 02/03/2020,48.25,40.10 84 | 01/31/2020,185.04,39.47 85 | 01/30/2020,22.26,28.27 86 | 01/29/2020,21.50,28.74 87 | 01/28/2020,29.77,29.29 88 | 01/27/2020,46.30,29.26 89 | 01/24/2020,47.55,27.94 90 | 01/23/2020,17.22,26.44 91 | 01/22/2020,19.16,27.14 92 | 01/21/2020,34.27,27.76 93 | 01/17/2020,29.39,27.26 94 | 01/16/2020,23.57,27.09 95 | 01/15/2020,23.77,27.37 96 | 01/14/2020,32.75,27.64 97 | 01/13/2020,17.20,27.25 98 | 01/10/2020,26.94,28.02 99 | 01/09/2020,25.85,28.11 100 | 01/08/2020,24.56,28.28 101 | 01/07/2020,21.85,28.56 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vista, technical analysis 2 | 3 | [![][travis img]][travis] 4 | [![][codecov img]][codecov] 5 | [![][maven img]][maven] 6 | [![][license img]][license] 7 | 8 | > Vista is currently under heavy development 🛠 9 | 10 | Vista is a technical analysis library for Kotlin, inspired in [Pine Script][ps] from TradingView. 11 | 12 | ```kotlin 13 | val data = dataOf("https://pallocchi.github.io/vista/amzn.csv") 14 | 15 | val fast = data.sma(9) 16 | val slow = data.sma(30) 17 | 18 | when { 19 | fast crossOver slow -> print("I'm going long!") 20 | fast crossUnder slow -> print("I'm going short!") 21 | } 22 | ``` 23 | 24 | Full documentation is available [here](https://pallocchi.github.io/vista). 25 | 26 | ## Gradle 27 | 28 | First you need to add the Bulltimate repository: 29 | 30 | ```kts 31 | repositories { 32 | maven { 33 | setUrl("https://dl.bintray.com/bulltimate/maven/") 34 | } 35 | } 36 | ``` 37 | 38 | After that the library can be added as a usual dependency: 39 | 40 | ```kts 41 | dependencies { 42 | implementation('com.bulltimate:vista:0.2.0') 43 | } 44 | ``` 45 | 46 | ## LICENSE 47 | 48 | MIT License 49 | 50 | Copyright (c) 2020 Pablo Pallocchi 51 | 52 | Permission is hereby granted, free of charge, to any person obtaining a copy 53 | of this software and associated documentation files (the "Software"), to deal 54 | in the Software without restriction, including without limitation the rights 55 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 56 | copies of the Software, and to permit persons to whom the Software is 57 | furnished to do so, subject to the following conditions: 58 | 59 | The above copyright notice and this permission notice shall be included in all 60 | copies or substantial portions of the Software. 61 | 62 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 63 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 64 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 65 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 66 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 67 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 68 | SOFTWARE. 69 | 70 | [ps]: https://www.tradingview.com/pine-script-docs/en/v4/Introduction.html 71 | 72 | [travis]:https://travis-ci.org/bulltimate/vista-kt 73 | [travis img]:https://travis-ci.org/bulltimate/vista-kt.svg?branch=master 74 | 75 | [license]:LICENSE.txt 76 | [license img]:https://img.shields.io/github/license/mashape/apistatus.svg 77 | 78 | [maven]:https://bintray.com/bulltimate/maven/vista/_latestVersion 79 | [maven img]:https://api.bintray.com/packages/bulltimate/maven/vista/images/download.svg 80 | 81 | [codecov]:https://codecov.io/gh/bulltimate/vista-kt 82 | [codecov img]:https://codecov.io/gh/bulltimate/vista-kt/branch/master/graph/badge.svg 83 | -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/AverageTrueRangeTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.loadAmazonData 31 | import vista.loadIndicatorData 32 | import vista.math.na 33 | import vista.math.numOf 34 | import vista.series.seriesOf 35 | 36 | internal class AverageTrueRangeTest { 37 | 38 | @Test 39 | fun trWithIntSeries() { 40 | val close = seriesOf(1..9) 41 | 42 | val high = close * 1.5 43 | val low = close * 0.5 44 | 45 | val tr = tr(close, high, low) 46 | 47 | assertThat(tr[0].round(2)).isEqualTo(numOf(9)) // current value 48 | assertThat(tr[1].round(2)).isEqualTo(numOf(8)) // previous value 49 | assertThat(tr[8].round(2)).isEqualTo(numOf(1)) 50 | } 51 | 52 | @Test 53 | fun atrWithIntSeries() { 54 | val close = seriesOf(1..20) 55 | 56 | val high = close * 1.5 57 | val low = close * 0.5 58 | 59 | val atr = atr(close, high, low) 60 | 61 | assertThat(atr[0].round(2)).isEqualTo(numOf(11.17)) // current value 62 | assertThat(atr[1].round(2)).isEqualTo(numOf(10.49)) // previous value 63 | assertThat(atr[6].round(2)).isEqualTo(numOf(7.5)) 64 | assertThat(atr[7]).isEqualTo(na) 65 | } 66 | 67 | @Test 68 | fun withMarketData() { 69 | val data = loadAmazonData() 70 | val expected = loadIndicatorData("atr.csv") 71 | 72 | val tr = data.tr() 73 | val atr = data.atr(14) 74 | 75 | for (i in 0..99) { 76 | assertThat(tr[i].round(2)).isEqualTo(expected[i][0]) 77 | assertThat(atr[i].round(2)).isEqualTo(expected[i][1]) 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/WeightedMovingAverage.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import vista.data.Data 29 | import vista.math.Num 30 | import vista.series.Series 31 | 32 | /** 33 | * Weighted Moving Average (WMA) indicator. 34 | */ 35 | internal class WeightedMovingAverage( 36 | private val source: Series, 37 | private val n: Int 38 | ) : Indicator(source) { 39 | 40 | override val size: Int get() = source.size + 1 - n 41 | 42 | override fun calculate(i: Int): Num { 43 | var norm = Num.ZERO 44 | var sum = Num.ZERO 45 | for (j in 0 until n) { 46 | val weight = (n - j) * n 47 | norm += weight 48 | sum += source[i + j] * weight 49 | } 50 | return sum / norm 51 | } 52 | } 53 | 54 | /** 55 | * The weighted moving average of [source] for [n] bars back. 56 | * In wma weighting factors decrease in arithmetical progression. 57 | * 58 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/trend?id=weighted-moving-average-wma) 59 | * 60 | * @param source Series of values to process 61 | * @param n Number of bars (length) 62 | * @sample vista.indicators.WeightedMovingAverageTest.withIntSeries 63 | * @see [sma] 64 | */ 65 | fun wma(source: Series, n: Int = 9): Series = WeightedMovingAverage(source, n) 66 | 67 | /** 68 | * The weighted moving average of close price for [n] bars back. 69 | * In wma weighting factors decrease in arithmetical progression. 70 | * 71 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/trend?id=weighted-moving-average-wma) 72 | * 73 | * @param n Number of bars (length) 74 | * @sample vista.indicators.WeightedMovingAverageTest.withMarketData 75 | * @see [sma] 76 | */ 77 | fun Data.wma(n: Int = 9): Series = wma(close, n) 78 | -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/MovingAverageConvergenceDivergenceTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.loadAmazonData 31 | import vista.loadIndicatorData 32 | import vista.math.na 33 | import vista.math.numOf 34 | import vista.series.seriesOf 35 | 36 | internal class MovingAverageConvergenceDivergenceTest { 37 | 38 | @Test 39 | fun withIntSeries() { 40 | val series = seriesOf(1..50) 41 | 42 | val (macd, signal, hist) = macd(series) 43 | 44 | assertThat(macd[0].round(2)).isEqualTo(numOf(7)) // current value 45 | assertThat(macd[1].round(2)).isEqualTo(numOf(7)) // previous value 46 | assertThat(macd[49]).isEqualTo(na) // oldest value 47 | 48 | assertThat(signal[0].round(2)).isEqualTo(numOf(7)) // current value 49 | assertThat(signal[1].round(2)).isEqualTo(numOf(7)) // previous value 50 | assertThat(signal[49]).isEqualTo(na) // oldest value 51 | 52 | assertThat(hist[0].round(2)).isEqualTo(numOf(0)) // current value 53 | assertThat(hist[1].round(2)).isEqualTo(numOf(0)) // previous value 54 | assertThat(hist[49]).isEqualTo(na) // oldest value 55 | } 56 | 57 | @Test 58 | fun withMarketData() { 59 | val data = loadAmazonData() 60 | val expected = loadIndicatorData("macd.csv") 61 | 62 | val (macd, signal, hist) = data.macd() 63 | 64 | for (i in 0..99) { 65 | assertThat(macd[i].round(2)).isEqualTo(expected[i][0]) 66 | assertThat(signal[i].round(2)).isEqualTo(expected[i][1]) 67 | assertThat(hist[i].round(2)).isEqualTo(expected[i][2]) 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/IchimokuCloudTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.loadAmazonData 31 | import vista.loadIndicatorData 32 | import vista.math.numOf 33 | import vista.series.seriesOf 34 | 35 | internal class IchimokuCloudTest { 36 | 37 | @Test 38 | fun withIntSeries() { 39 | val low = seriesOf(1..100) 40 | val high = seriesOf(2..101) 41 | 42 | val ichimoku = ichimoku(high, low, 9, 26, 52, 26) 43 | 44 | assertThat(ichimoku.ts[0]).isEqualTo(numOf(96.50)) // current value 45 | assertThat(ichimoku.ts[1]).isEqualTo(numOf(95.50)) // previous value 46 | 47 | assertThat(ichimoku.ks[0]).isEqualTo(numOf(88.0)) // current value 48 | assertThat(ichimoku.ks[1]).isEqualTo(numOf(87.0)) // previous value 49 | 50 | assertThat(ichimoku.ssa[0]).isEqualTo(numOf(67.25)) // current value 51 | assertThat(ichimoku.ssa[1]).isEqualTo(numOf(66.25)) // previous value 52 | 53 | assertThat(ichimoku.ssb[0]).isEqualTo(numOf(50.0)) // current value 54 | assertThat(ichimoku.ssb[1]).isEqualTo(numOf(49.0)) // previous value 55 | } 56 | 57 | @Test 58 | fun withMarketData() { 59 | val data = loadAmazonData() 60 | val expected = loadIndicatorData("ichimoku.csv") 61 | 62 | val actual = data.ichimoku() 63 | 64 | for (i in 0..99) { 65 | assertThat(actual.ts[i].round(2)).isEqualTo(expected[i][0]) 66 | assertThat(actual.ks[i].round(2)).isEqualTo(expected[i][1]) 67 | assertThat(actual.ssa[i].round(2)).isEqualTo(expected[i][2]) 68 | assertThat(actual.ssb[i].round(2)).isEqualTo(expected[i][3]) 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/StochasticOscillator.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import vista.data.Data 29 | import vista.series.Series 30 | 31 | /** 32 | * The stochastic oscillator of [source] for [k] bars back, to evaluate overbought or oversold conditions. 33 | * 34 | * Returns a pair of %K and %D series. 35 | * 36 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/momentum?id=stochastic-oscillator) 37 | * 38 | * @param source Series of values to process 39 | * @param high Series of high values 40 | * @param low Series of low values 41 | * @param k Number of bars (length) for %K 42 | * @param d Number of bars (length) for %D moving average 43 | * @param smooth Smooth for %K 44 | * @sample vista.indicators.StochasticOscillatorTest.withIntSeries 45 | */ 46 | fun stoch( 47 | source: Series, 48 | high: Series, 49 | low: Series, 50 | k: Int = 14, 51 | d: Int = 3, 52 | smooth: Int = 3 53 | ): Pair { 54 | val lowest = lowest(low, k) 55 | val highest = highest(high, k) 56 | val kLine = ((source - lowest) / (highest - lowest)) * 100 57 | val kLineSmoothed = sma(kLine, smooth) 58 | val dLine = sma(kLineSmoothed, d) 59 | return Pair(kLineSmoothed, dLine) 60 | } 61 | 62 | /** 63 | * The stochastic oscillator of close price for [k] bars back, to evaluate overbought or oversold conditions. 64 | * 65 | * Returns a pair of %K and %D series. 66 | * 67 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/momentum?id=stochastic-oscillator) 68 | * 69 | * @param k Number of bars (length) for %K 70 | * @param d Number of bars (length) for %D moving average 71 | * @param smooth Smooth for %K 72 | * @sample vista.indicators.StochasticOscillatorTest.withMarketData 73 | */ 74 | fun Data.stoch(k: Int = 14, d: Int = 3, smooth: Int = 3) = stoch(close, high, low, k, d, smooth) -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/ExponentialMovingAverageTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.loadAmazonData 31 | import vista.loadIndicatorData 32 | import vista.math.na 33 | import vista.math.numOf 34 | import vista.series.seriesOf 35 | 36 | internal class ExponentialMovingAverageTest { 37 | 38 | @Test 39 | fun emaWithIntSeries() { 40 | 41 | val series = seriesOf(1, 2, 3) 42 | 43 | val ema = ema(series, 2) 44 | 45 | assertThat(ema[0]).isEqualTo(numOf(2.5)) // current value 46 | assertThat(ema[1]).isEqualTo(numOf(1.5)) // previous value 47 | assertThat(ema[2]).isEqualTo(na) // oldest value 48 | } 49 | 50 | @Test 51 | fun emaNestedWithIntSeries() { 52 | 53 | val series = seriesOf(1, 2, 3) 54 | 55 | val ema1 = ema(series, 2) 56 | val ema2 = ema(ema1, 2) 57 | 58 | assertThat(ema2[0]).isEqualTo(numOf(2.0)) // current value 59 | assertThat(ema2[1]).isEqualTo(na) // previous value 60 | assertThat(ema2[2]).isEqualTo(na) // oldest value 61 | } 62 | 63 | @Test 64 | fun emaWithMarketData() { 65 | 66 | val data = loadAmazonData() 67 | val expected = loadIndicatorData("ema.csv") 68 | 69 | val actual = data.ema(9) 70 | 71 | for (i in 0..99) 72 | assertThat(actual[i].round(2)).isEqualTo(expected[i][0]) 73 | } 74 | 75 | @Test 76 | fun rmaWithIntSeries() { 77 | 78 | val series = seriesOf(1, 2, 3) 79 | 80 | val rma = rma(series, 2) 81 | 82 | assertThat(rma[0]).isEqualTo(numOf(2.25)) // current value 83 | assertThat(rma[1]).isEqualTo(numOf(1.5)) // previous value 84 | assertThat(rma[2]).isEqualTo(na) // oldest value 85 | } 86 | } -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/BollingerBandTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.loadAmazonData 31 | import vista.loadIndicatorData 32 | import vista.math.na 33 | import vista.math.numOf 34 | import vista.series.seriesOf 35 | 36 | internal class BollingerBandTest { 37 | 38 | @Test 39 | fun withIntSeries() { 40 | val series = seriesOf(1..50) 41 | 42 | val (middle, upper, lower) = bb(series) 43 | 44 | assertThat(middle[0].round(2)).isEqualTo(numOf(40.50)) // current value 45 | assertThat(middle[1].round(2)).isEqualTo(numOf(39.50)) // previous value 46 | assertThat(middle[30].round(2)).isEqualTo(numOf(10.50)) 47 | assertThat(middle[31]).isEqualTo(na) 48 | 49 | assertThat(upper[0].round(2)).isEqualTo(numOf(52.03)) // current value 50 | assertThat(upper[1].round(2)).isEqualTo(numOf(51.03)) // previous value 51 | assertThat(upper[30].round(2)).isEqualTo(numOf(22.03)) 52 | assertThat(upper[31]).isEqualTo(na) 53 | 54 | assertThat(lower[0].round(2)).isEqualTo(numOf(28.97)) // current value 55 | assertThat(lower[1].round(2)).isEqualTo(numOf(27.97)) // previous value 56 | assertThat(lower[30].round(2)).isEqualTo(numOf(-1.03)) 57 | assertThat(lower[31]).isEqualTo(na) 58 | } 59 | 60 | @Test 61 | fun withMarketData() { 62 | val data = loadAmazonData() 63 | val expected = loadIndicatorData("bb.csv") 64 | 65 | val (middle, upper, lower) = data.bb() 66 | 67 | for (i in 0..99) { 68 | assertThat(middle[i].round(2)).isEqualTo(expected[i][0]) 69 | assertThat(upper[i].round(2)).isEqualTo(expected[i][1]) 70 | assertThat(lower[i].round(2)).isEqualTo(expected[i][2]) 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/OnBalanceVolume.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import vista.data.Data 29 | import vista.math.Num 30 | import vista.series.Series 31 | 32 | /** 33 | * On-Balance volume (OBV) indicator. 34 | * 35 | * Note as this indicator is an accumulation the calculated values depends on the start date, 36 | * so in order to compare the results with others, the difference between periods should be used 37 | * instead of the absolute value. 38 | */ 39 | internal class OnBalanceVolume( 40 | private val close: Series, 41 | private val volume: Series 42 | ) : CachedIndicator(close) { 43 | 44 | override val size: Int get() = close.size - 1 45 | 46 | override fun calculate(i: Int): Num { 47 | if (i == size - 1) 48 | return calculateCurrentOBV(i) 49 | return this[i + 1] + calculateCurrentOBV(i) 50 | } 51 | 52 | private fun calculateCurrentOBV(i: Int): Num { 53 | return when { 54 | close[i] > close[i + 1] -> volume[i] 55 | close[i] < close[i + 1] -> -volume[i] 56 | else -> Num.ZERO 57 | } 58 | } 59 | } 60 | 61 | /** 62 | * The on-balance volume indicator, which uses volume flow to predict changes in price. 63 | * 64 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/volume?id=on-balance-volume-obv) 65 | * 66 | * @param close Series of close prices 67 | * @param volume Series of volume values 68 | * @sample vista.indicators.OnBalanceVolumeTest.withIntSeries 69 | */ 70 | fun obv(close: Series, volume: Series): Series = OnBalanceVolume(close, volume) 71 | 72 | /** 73 | * The on-balance volume indicator, which uses volume flow to predict changes in price. 74 | * 75 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/volume?id=on-balance-volume-obv) 76 | * 77 | * @sample vista.indicators.OnBalanceVolumeTest.withMarketData 78 | */ 79 | fun Data.obv(): Series = obv(close, volume) -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/ElderRayIndexTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.loadAmazonData 31 | import vista.loadIndicatorData 32 | import vista.math.na 33 | import vista.math.numOf 34 | import vista.series.seriesOf 35 | 36 | internal class ElderRayIndexTest { 37 | 38 | @Test 39 | fun withIntSeries() { 40 | val close = seriesOf(1..20) 41 | 42 | val high = close * 1.5 43 | val low = close * 0.5 44 | 45 | val (bb, bull, bear) = eri(close, high, low, 13) 46 | 47 | assertThat(bb[0].round(2)).isEqualTo(numOf(12)) // current value 48 | assertThat(bb[1].round(2)).isEqualTo(numOf(12)) // previous value 49 | assertThat(bb[7].round(2)).isEqualTo(numOf(12)) 50 | assertThat(bb[8]).isEqualTo(na) 51 | 52 | assertThat(bull[0].round(2)).isEqualTo(numOf(16.0)) // current value 53 | assertThat(bull[1].round(2)).isEqualTo(numOf(15.5)) // previous value 54 | assertThat(bull[7].round(2)).isEqualTo(numOf(12.5)) 55 | assertThat(bull[8]).isEqualTo(na) 56 | 57 | assertThat(bear[0].round(2)).isEqualTo(numOf(-4.0)) // current value 58 | assertThat(bear[1].round(2)).isEqualTo(numOf(-3.5)) // previous value 59 | assertThat(bear[7].round(2)).isEqualTo(numOf(-0.5)) 60 | assertThat(bear[8]).isEqualTo(na) 61 | } 62 | 63 | @Test 64 | fun withMarketData() { 65 | val data = loadAmazonData() 66 | val expected = loadIndicatorData("eri.csv") 67 | 68 | val (bb, bull, bear) = data.eri() 69 | 70 | for (i in 0..99) { 71 | assertThat(bb[i].round(2)).isEqualTo(expected[i][0]) 72 | assertThat(bull[i].round(2)).isEqualTo(expected[i][1]) 73 | assertThat(bear[i].round(2)).isEqualTo(expected[i][2]) 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/AccumulationDistribution.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import vista.data.Data 29 | import vista.math.Num 30 | import vista.series.Series 31 | 32 | /** 33 | * Accumulation/Distribution (ADL) indicator. 34 | * 35 | * Note as this indicator is an accumulation the calculated values depends on the start date, 36 | * so in order to compare the results with others, the difference between periods should be used 37 | * instead of the absolute value. 38 | */ 39 | internal class AccumulationDistribution( 40 | close: Series, 41 | high: Series, 42 | low: Series, 43 | volume: Series 44 | ) : CachedIndicator(close) { 45 | 46 | override val size: Int get() = mfv.size 47 | 48 | private val mfv: Series 49 | 50 | init { 51 | //TODO: What if high == low??? 52 | val mfm = ((close - low) - (high - close)) / (high - low) 53 | mfv = mfm * volume 54 | } 55 | 56 | override fun calculate(i: Int): Num { 57 | if (i == size - 1) 58 | return mfv[i] 59 | return mfv[i] + this[i + 1] 60 | } 61 | } 62 | 63 | /** 64 | * The money flow multiplier, used by Chaikin indicators. 65 | * 66 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/volume?id=accumulationdistribution-adl) 67 | * 68 | * @param close Series of close prices 69 | * @param high Series of high prices 70 | * @param low Series of low prices 71 | * @sample vista.indicators.MovingAverageConvergenceDivergenceTest.withIntSeries 72 | */ 73 | fun adl(close: Series, high: Series, low: Series, volume: Series): Series = 74 | AccumulationDistribution(close, high, low, volume) 75 | 76 | /** 77 | * The money flow multiplier, used by Chaikin indicators. 78 | * 79 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/volume?id=accumulationdistribution-adl) 80 | * 81 | * @sample vista.indicators.MovingAverageConvergenceDivergenceTest.withMarketData 82 | */ 83 | fun Data.adl(): Series = adl(close, high, low, volume) -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/StochasticRelativeStrengthIndex.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import vista.data.Data 29 | import vista.series.Series 30 | 31 | /** 32 | * The stochastic RSI, which is primarily used for identifying overbought and oversold conditions. 33 | * 34 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/momentum?id=stochastic-rsi-stochrsi) 35 | * 36 | * @param source Series of values to process 37 | * @param k Number of bars (length) for %K 38 | * @param d Number of bars (length) for %D moving average 39 | * @param rsiLength Number of bars (length) used by the [rsi] 40 | * @param stoLength Number of bars (length) used by the stochastic oscillator 41 | * @sample vista.indicators.StochasticRelativeStrengthIndexTest.withMarketData 42 | */ 43 | fun stochrsi( 44 | source: Series, 45 | k: Int = 3, 46 | d: Int = 3, 47 | rsiLength: Int = 14, 48 | stoLength: Int = 14 49 | ): Pair { 50 | val rsi = rsi(source, rsiLength) 51 | val lowest = lowest(rsi, stoLength) 52 | val highest = highest(rsi, stoLength) 53 | val kLine = ((rsi - lowest) / (highest - lowest)) * 100 54 | val kLineSmoothed = sma(kLine, k) 55 | val dLine = sma(kLineSmoothed, d) 56 | return Pair(kLineSmoothed, dLine) 57 | } 58 | 59 | /** 60 | * The stochastic RSI, which is primarily used for identifying overbought and oversold conditions. 61 | * 62 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/momentum?id=stochastic-rsi-stochrsi) 63 | * 64 | * @param k Number of bars (length) for %K 65 | * @param d Number of bars (length) for %D moving average 66 | * @param rsiLength Number of bars (length) used by the [rsi] 67 | * @param stoLength Number of bars (length) used by the stochastic oscillator 68 | * @sample vista.indicators.StochasticRelativeStrengthIndexTest.withMarketData 69 | */ 70 | fun Data.stochrsi( 71 | k: Int = 3, 72 | d: Int = 3, 73 | rsiLength: Int = 14, 74 | stoLength: Int = 14 75 | ) = stochrsi(close, k, d, rsiLength, stoLength) -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/Indicator.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import vista.math.NaN 29 | import vista.math.Num 30 | import vista.series.Series 31 | 32 | /** 33 | * Series of indicator values. 34 | */ 35 | internal abstract class Indicator(private val source: Series) : Series { 36 | 37 | override val time: Int get() = source.time 38 | 39 | /** 40 | * Returns the calculated indicator value [i] bars from now. 41 | */ 42 | override fun get(i: Int): Num = if (i in 0 until size) calculate(i) else NaN 43 | 44 | /** 45 | * Returns the calculated indicator value [i] bars from now. 46 | */ 47 | abstract fun calculate(i: Int): Num 48 | } 49 | 50 | /** 51 | * Indicator that caches the already calculated values. 52 | */ 53 | internal abstract class CachedIndicator(private val source: Series) : Indicator(source) { 54 | 55 | /** 56 | * The last time the [cache] was updated. 57 | */ 58 | private var lastCachedTime: Int = -1 59 | 60 | /** 61 | * Holds the already calculated values. 62 | */ 63 | private val cache: MutableList = mutableListOf() 64 | 65 | /** 66 | * Returns the calculated indicator value [i] bars from now. 67 | */ 68 | override fun get(i: Int): Num { 69 | // calculate how many bars this cache is outdated 70 | val misses = time - lastCachedTime 71 | return when (i) { 72 | in misses until size -> cache[time - i] // value is in cache 73 | in 0 until misses -> calculate(misses, i) // value is missing 74 | else -> NaN // value out of range 75 | } 76 | } 77 | 78 | /** 79 | * Calculates the missing values from the [start] to the [end], and return the last one. 80 | */ 81 | private fun calculate(start: Int, end: Int): Num { 82 | var value: Num = NaN 83 | for (j in start - 1 downTo end) { 84 | value = calculate(j) 85 | cache.add(value) 86 | lastCachedTime++ 87 | } 88 | return value 89 | } 90 | } -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/MovingAverageConvergenceDivergence.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import vista.data.Data 29 | import vista.series.Series 30 | 31 | /** 32 | * The moving average convergence/divergence of [source], to evaluate strength, direction, momentum, and duration of a trend. 33 | * 34 | * Returns a triple of MACD line, signal line and histogram line. 35 | * 36 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/trend?id=moving-average-convergencedivergence-macd) 37 | * 38 | * @param source Series of values to process 39 | * @param fastLength Number of bars (length) used by the fast [sma] 40 | * @param slowLength Number of bars (length) used by the slow [sma] 41 | * @param signalLength Number of bars (length) used by the signal line 42 | * @sample vista.indicators.MovingAverageConvergenceDivergenceTest.withIntSeries 43 | * @see [sma] 44 | */ 45 | fun macd( 46 | source: Series, 47 | fastLength: Int = 12, 48 | slowLength: Int = 26, 49 | signalLength: Int = 9 50 | ): Triple { 51 | val macd = ema(source, fastLength) - ema(source, slowLength) 52 | val signal = ema(macd, signalLength) 53 | val histogram = macd - signal 54 | return Triple(macd, signal, histogram) 55 | } 56 | 57 | /** 58 | * The moving average convergence/divergence of close price, to evaluate strength, direction, momentum, and duration of a trend. 59 | * 60 | * Returns a triple of MACD line, signal line and histogram line. 61 | * 62 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/trend?id=moving-average-convergencedivergence-macd) 63 | * 64 | * @param fastLength Number of bars (length) used by the fast [sma] 65 | * @param slowLength Number of bars (length) used by the slow [sma] 66 | * @param signalLength Number of bars (length) used by the signal line 67 | * @sample vista.indicators.MovingAverageConvergenceDivergenceTest.withMarketData 68 | * @see [sma] 69 | */ 70 | fun Data.macd( 71 | fastLength: Int = 12, 72 | slowLength: Int = 26, 73 | signalLength: Int = 9 74 | ) = macd(close, fastLength, slowLength, signalLength) -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/RelativeStrengthIndex.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import vista.data.Data 29 | import vista.math.Num 30 | import vista.math.max 31 | import vista.math.min 32 | import vista.series.Series 33 | 34 | /** 35 | * Relative strength index (RSI) indicator. 36 | */ 37 | internal class RelativeStrengthIndex( 38 | source: Series, 39 | n: Int 40 | ) : CachedIndicator(source) { 41 | 42 | companion object { 43 | val MIN_VALUE = Num.ZERO 44 | val MAX_VALUE = Num.HUNDRED 45 | } 46 | 47 | override val size: Int get() = min(upward.size, downward.size) 48 | 49 | private val upward = rma(max(source - source(1), Num.ZERO), n) 50 | private val downward = rma(max(source(1) - source, Num.ZERO), n) 51 | 52 | override fun calculate(i: Int): Num { 53 | if (downward[i] == Num.ZERO) 54 | return if (upward[i] == Num.ZERO) MIN_VALUE else MAX_VALUE 55 | val rs = upward[i] / downward[i] 56 | return MAX_VALUE - MAX_VALUE / (Num.ONE + rs) 57 | } 58 | } 59 | 60 | /** 61 | * The relative strength index of [source] for [n] bars back, to evaluate overbought or oversold conditions. 62 | * 63 | * Oscillates between `0` (oversold) and `100` (overbought). 64 | * 65 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/momentum?id=relative-strength-index-rsi) 66 | * 67 | * @param source Series of values to process 68 | * @param n Number of bars (length) 69 | * @sample vista.indicators.RelativeStrengthIndexTest.withIntSeries 70 | * @see [rma] 71 | */ 72 | fun rsi(source: Series, n: Int = 14): Series = RelativeStrengthIndex(source, n) 73 | 74 | /** 75 | * The relative strength index of close price for [n] bars back, to evaluate overbought or oversold conditions. 76 | * 77 | * Oscillates between `0` (oversold) and `100` (overbought). 78 | * 79 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/momentum?id=relative-strength-index-rsi) 80 | * 81 | * @param n Number of bars (length) 82 | * @sample vista.indicators.RelativeStrengthIndexTest.withMarketData 83 | * @see [rma] 84 | */ 85 | fun Data.rsi(n: Int = 14) = rsi(close, n) 86 | -------------------------------------------------------------------------------- /src/test/kotlin/vista/indicators/IndicatorTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import org.assertj.core.api.Assertions.assertThat 29 | import org.junit.jupiter.api.Test 30 | import vista.data.Data 31 | import vista.math.Num 32 | import vista.math.numOf 33 | import vista.series.Series 34 | import vista.series.seriesOf 35 | 36 | internal class IndicatorTest { 37 | 38 | @Test 39 | fun size() { 40 | 41 | val series = seriesOf(1, 2, 3) 42 | val indicator = FoolIndicator(series) 43 | 44 | assertThat(series.size).isEqualTo(3) 45 | assertThat(indicator.size).isEqualTo(3) 46 | } 47 | 48 | @Test 49 | fun withNewValue() { 50 | 51 | // create the market data 52 | 53 | val bars = mutableListOf( 54 | Data.Bar( 55 | date = "12345678", 56 | open = 2361.01, 57 | high = 2391.37, 58 | low = 2353.21, 59 | close = 2388.85, 60 | volume = 3648128.0 61 | ) 62 | ) 63 | 64 | val data = Data(bars) 65 | 66 | // create the close price series 67 | val close = data.close 68 | 69 | // create the indicator 70 | val indicator = FoolIndicator(close) 71 | 72 | assertThat(indicator[0]).isEqualTo(numOf(2388.85)) 73 | 74 | data.add( 75 | Data.Bar( 76 | date = "12345679", 77 | open = 2368.52, 78 | high = 2411.0, 79 | low = 2356.37, 80 | close = 2409.78, 81 | volume = 4234951.0 82 | ) 83 | ) 84 | 85 | assertThat(indicator[0]).isEqualTo(numOf(2409.78)) 86 | assertThat(indicator[1]).isEqualTo(numOf(2388.85)) 87 | } 88 | 89 | /** 90 | * Indicator with no calculation. 91 | */ 92 | private class FoolIndicator( 93 | private val source: Series 94 | ) : Indicator(source) { 95 | 96 | override val size: Int get() = source.size 97 | 98 | /** 99 | * Returns the [i] value of [source]. 100 | */ 101 | override fun calculate(i: Int): Num = source[i] 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/test/resources/macd.csv: -------------------------------------------------------------------------------- 1 | date,macd,signal,histogram 2 | 05/29/2020,48.39,59.15,-10.76 3 | 05/28/2020,49.76,61.84,-12.08 4 | 05/27/2020,55.00,64.86,-9.86 5 | 05/26/2020,59.93,67.33,-7.40 6 | 05/22/2020,64.18,69.18,-4.99 7 | 05/21/2020,67.15,70.42,-3.27 8 | 05/20/2020,68.96,71.24,-2.28 9 | 05/19/2020,65.05,71.81,-6.76 10 | 05/18/2020,64.07,73.50,-9.43 11 | 05/15/2020,64.23,75.86,-11.63 12 | 05/14/2020,65.18,78.76,-13.58 13 | 05/13/2020,67.57,82.16,-14.59 14 | 05/12/2020,71.78,85.81,-14.03 15 | 05/11/2020,77.22,89.31,-12.10 16 | 05/08/2020,77.74,92.34,-14.60 17 | 05/07/2020,80.26,95.99,-15.73 18 | 05/06/2020,83.54,99.92,-16.38 19 | 05/05/2020,88.17,104.02,-15.85 20 | 05/04/2020,96.21,107.98,-11.77 21 | 05/01/2020,105.25,110.92,-5.67 22 | 04/30/2020,118.31,112.34,5.97 23 | 04/29/2020,114.12,110.85,3.27 24 | 04/28/2020,117.42,110.03,7.40 25 | 04/27/2020,125.90,108.18,17.73 26 | 04/24/2020,128.59,103.75,24.85 27 | 04/23/2020,126.67,97.54,29.13 28 | 04/22/2020,123.47,90.25,33.22 29 | 04/21/2020,121.25,81.95,39.30 30 | 04/20/2020,120.25,72.12,48.13 31 | 04/17/2020,110.57,60.09,50.48 32 | 04/16/2020,98.49,47.47,51.02 33 | 04/15/2020,78.26,34.72,43.54 34 | 04/14/2020,61.54,23.83,37.71 35 | 04/13/2020,41.78,14.40,27.37 36 | 04/09/2020,27.60,7.56,20.04 37 | 04/08/2020,21.95,2.55,19.40 38 | 04/07/2020,14.39,-2.30,16.69 39 | 04/06/2020,7.73,-6.47,14.20 40 | 04/03/2020,0.54,-10.02,10.56 41 | 04/02/2020,0.62,-12.66,13.29 42 | 04/01/2020,-0.52,-15.99,15.46 43 | 03/31/2020,-0.85,-19.85,19.00 44 | 03/30/2020,-5.53,-24.60,19.07 45 | 03/27/2020,-12.88,-29.37,16.49 46 | 03/26/2020,-15.56,-33.49,17.93 47 | 03/25/2020,-24.40,-37.97,13.57 48 | 03/24/2020,-28.24,-41.36,13.12 49 | 03/23/2020,-38.25,-44.65,6.39 50 | 03/20/2020,-46.68,-46.24,-0.44 51 | 03/19/2020,-50.98,-46.14,-4.85 52 | 03/18/2020,-59.28,-44.92,-14.35 53 | 03/17/2020,-63.82,-41.34,-22.48 54 | 03/16/2020,-66.43,-35.72,-30.71 55 | 03/13/2020,-56.71,-28.04,-28.67 56 | 03/12/2020,-53.24,-20.87,-32.37 57 | 03/11/2020,-37.00,-12.78,-24.22 58 | 03/10/2020,-30.42,-6.72,-23.70 59 | 03/09/2020,-28.80,-0.80,-28.00 60 | 03/06/2020,-17.04,6.20,-23.24 61 | 03/05/2020,-12.00,12.01,-24.01 62 | 03/04/2020,-7.73,18.02,-25.75 63 | 03/03/2020,-7.45,24.45,-31.90 64 | 03/02/2020,-0.17,32.43,-32.60 65 | 02/28/2020,4.55,40.58,-36.03 66 | 02/27/2020,17.64,49.58,-31.94 67 | 02/26/2020,34.05,57.57,-23.52 68 | 02/25/2020,44.79,63.45,-18.66 69 | 02/24/2020,58.54,68.11,-9.57 70 | 02/21/2020,71.53,70.51,1.02 71 | 02/20/2020,78.24,70.25,7.99 72 | 02/19/2020,79.84,68.25,11.59 73 | 02/18/2020,78.98,65.36,13.63 74 | 02/14/2020,78.22,61.95,16.27 75 | 02/13/2020,78.23,57.88,20.35 76 | 02/12/2020,75.58,52.79,22.79 77 | 02/11/2020,70.07,47.10,22.97 78 | 02/10/2020,62.94,41.35,21.59 79 | 02/07/2020,54.66,35.96,18.70 80 | 02/06/2020,48.85,31.28,17.56 81 | 02/05/2020,43.68,26.89,16.79 82 | 02/04/2020,37.50,22.69,14.81 83 | 02/03/2020,28.07,18.99,9.08 84 | 01/31/2020,20.26,16.72,3.54 85 | 01/30/2020,9.53,15.84,-6.31 86 | 01/29/2020,9.76,17.42,-7.66 87 | 01/28/2020,11.19,19.33,-8.14 88 | 01/27/2020,13.35,21.36,-8.01 89 | 01/24/2020,18.44,23.37,-4.93 90 | 01/23/2020,21.28,24.60,-3.32 91 | 01/22/2020,22.26,25.43,-3.16 92 | 01/21/2020,22.90,26.22,-3.32 93 | 01/17/2020,22.92,27.05,-4.13 94 | 01/16/2020,25.38,28.08,-2.70 95 | 01/15/2020,26.81,28.76,-1.95 96 | 01/14/2020,29.87,29.24,0.62 97 | 01/13/2020,32.58,29.09,3.49 98 | 01/10/2020,33.34,28.21,5.12 99 | 01/09/2020,34.65,26.93,7.72 100 | 01/08/2020,34.00,25.00,9.00 101 | 01/07/2020,33.61,22.75,10.86 -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/ExponentialMovingAverage.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import vista.data.Data 29 | import vista.math.Num 30 | import vista.math.numOf 31 | import vista.series.Series 32 | 33 | /** 34 | * Exponential Moving Average (EMA) indicator. 35 | */ 36 | internal class ExponentialMovingAverage( 37 | private val source: Series, 38 | private val n: Int, 39 | private val alpha: Num = numOf(2) / (n + 1) 40 | ) : CachedIndicator(source) { 41 | 42 | override val size: Int get() = source.size + 1 - n 43 | 44 | override fun calculate(i: Int): Num { 45 | if (i == size - 1) 46 | return sma(source, n)[i] 47 | return alpha * source[i] + (Num.ONE - alpha) * this[i + 1] 48 | } 49 | } 50 | 51 | /** 52 | * The exponential moving average, that places a greater weight and significance on the most recent data points. 53 | * 54 | * Uses the `alpha = 2 / (n + 1)`. 55 | * 56 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/trend?id=exponential-moving-average-ema) 57 | * 58 | * @param source Series of values to process 59 | * @param n Number of bars (length) 60 | * @sample vista.indicators.ExponentialMovingAverageTest.emaWithIntSeries 61 | * @see [sma] 62 | */ 63 | fun ema(source: Series, n: Int = 9): Series = ExponentialMovingAverage(source, n) 64 | 65 | /** 66 | * The exponential moving average used by RSI. 67 | * 68 | * Uses the `alpha = 1 / n`. 69 | * 70 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/trend?id=exponential-moving-average-ema) 71 | * 72 | * @param source Series of values to process 73 | * @param n Number of bars (length) 74 | * @sample vista.indicators.ExponentialMovingAverageTest.rmaWithIntSeries 75 | * @see [ema] 76 | */ 77 | fun rma(source: Series, n: Int = 9): Series = ExponentialMovingAverage(source, n, Num.ONE.div(n)) 78 | 79 | /** 80 | * The exponential moving average, that places a greater weight and significance on the most recent data points. 81 | * 82 | * Uses the `alpha = 2 / (n + 1)`. 83 | * 84 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/trend?id=exponential-moving-average-ema) 85 | * 86 | * @param n Number of bars (length) 87 | * @sample vista.indicators.ExponentialMovingAverageTest.emaWithMarketData 88 | * @see [sma] 89 | */ 90 | fun Data.ema(n: Int = 9) = ema(close, n) -------------------------------------------------------------------------------- /src/test/resources/eri.csv: -------------------------------------------------------------------------------- 1 | date,bb,bull,bear 2 | 05/29/2020,9.51,26.84,-17.33 3 | 05/28/2020,-6.91,25.91,-32.83 4 | 05/27/2020,-81.85,0.86,-82.72 5 | 05/26/2020,49.85,48.90,0.96 6 | 05/22/2020,76.69,58.21,18.49 7 | 05/21/2020,153.11,118.01,35.10 8 | 05/20/2020,165.50,99.12,66.38 9 | 05/19/2020,144.54,100.29,44.26 10 | 05/18/2020,69.12,59.06,10.07 11 | 05/15/2020,36.92,45.78,-8.85 12 | 05/14/2020,28.98,33.57,-4.59 13 | 05/13/2020,40.27,55.08,-14.81 14 | 05/12/2020,73.86,68.93,4.93 15 | 05/11/2020,93.93,70.74,23.18 16 | 05/08/2020,66.41,48.33,18.09 17 | 05/07/2020,54.85,43.87,10.98 18 | 05/06/2020,25.01,31.23,-6.22 19 | 05/05/2020,14.04,28.96,-14.91 20 | 05/04/2020,-62.14,4.23,-66.37 21 | 05/01/2020,-27.13,38.56,-65.69 22 | 04/30/2020,210.64,144.81,65.82 23 | 04/29/2020,89.46,85.67,3.78 24 | 04/28/2020,89.23,78.37,10.87 25 | 04/27/2020,223.93,152.90,71.02 26 | 04/24/2020,246.49,142.46,104.03 27 | 04/23/2020,294.44,168.29,126.15 28 | 04/22/2020,280.98,161.99,118.99 29 | 04/21/2020,287.77,218.21,69.56 30 | 04/20/2020,450.18,254.55,195.62 31 | 04/17/2020,402.89,243.44,159.46 32 | 04/16/2020,555.69,340.84,214.84 33 | 04/15/2020,434.07,261.22,172.85 34 | 04/14/2020,412.42,259.10,153.31 35 | 04/13/2020,235.68,188.84,46.84 36 | 04/09/2020,147.58,91.46,56.12 37 | 04/08/2020,159.14,95.99,63.14 38 | 04/07/2020,168.99,103.55,65.45 39 | 04/06/2020,90.67,79.58,11.08 40 | 04/03/2020,3.83,20.50,-16.68 41 | 04/02/2020,6.13,21.83,-15.70 42 | 04/01/2020,30.94,41.45,-10.51 43 | 03/31/2020,131.41,90.21,41.20 44 | 03/30/2020,95.98,78.64,17.35 45 | 03/27/2020,72.71,56.29,16.42 46 | 03/26/2020,84.31,75.76,8.56 47 | 03/25/2020,99.49,81.98,17.50 48 | 03/24/2020,124.64,89.65,34.99 49 | 03/23/2020,25.62,66.51,-40.89 50 | 03/20/2020,88.60,112.43,-23.84 51 | 03/19/2020,89.03,100.69,-11.66 52 | 03/18/2020,-89.76,3.45,-93.21 53 | 03/17/2020,-132.13,18.20,-150.34 54 | 03/16/2020,-304.25,-85.42,-218.84 55 | 03/13/2020,-274.71,-84.51,-190.20 56 | 03/12/2020,-330.24,-120.12,-210.12 57 | 03/11/2020,-166.93,-48.55,-118.37 58 | 03/10/2020,-160.31,-42.11,-118.21 59 | 03/09/2020,-263.54,-81.03,-182.51 60 | 03/06/2020,-154.96,-56.80,-98.17 61 | 03/05/2020,-86.81,-18.04,-68.76 62 | 03/04/2020,-75.77,-9.89,-65.89 63 | 03/03/2020,-95.37,6.43,-101.81 64 | 03/02/2020,-182.25,-48.87,-133.38 65 | 02/28/2020,-322.35,-121.86,-200.49 66 | 02/27/2020,-208.10,-57.93,-150.17 67 | 02/26/2020,-140.28,-43.03,-97.25 68 | 02/25/2020,-148.42,-36.12,-112.30 69 | 02/24/2020,-146.83,-47.75,-99.08 70 | 02/21/2020,32.53,44.54,-12.01 71 | 02/20/2020,102.88,76.11,26.77 72 | 02/19/2020,162.33,93.15,69.17 73 | 02/18/2020,132.38,87.17,45.21 74 | 02/14/2020,152.72,92.94,59.79 75 | 02/13/2020,202.99,115.64,87.36 76 | 02/12/2020,258.00,141.48,116.52 77 | 02/11/2020,284.81,167.38,117.43 78 | 02/10/2020,227.50,139.07,88.43 79 | 02/07/2020,189.37,124.90,64.47 80 | 02/06/2020,169.05,100.28,68.78 81 | 02/05/2020,222.37,130.70,91.68 82 | 02/04/2020,227.71,136.07,91.64 83 | 02/03/2020,243.27,145.76,97.51 84 | 01/31/2020,286.33,169.89,116.44 85 | 01/30/2020,-7.22,7.52,-14.74 86 | 01/29/2020,0.84,10.29,-9.44 87 | 01/28/2020,-42.95,-7.43,-35.52 88 | 01/27/2020,-78.84,-26.59,-52.25 89 | 01/24/2020,-5.83,20.86,-26.69 90 | 01/23/2020,10.32,13.77,-3.45 91 | 01/22/2020,36.21,27.68,8.52 92 | 01/21/2020,8.85,21.56,-12.71 93 | 01/17/2020,4.90,17.15,-12.24 94 | 01/16/2020,11.03,15.30,-4.27 95 | 01/15/2020,-4.08,9.85,-13.92 96 | 01/14/2020,5.30,16.93,-11.63 97 | 01/13/2020,38.19,27.70,10.50 98 | 01/10/2020,53.33,40.14,13.20 99 | 01/09/2020,85.46,53.74,31.72 100 | 01/08/2020,81.61,53.08,28.52 101 | 01/07/2020,101.45,61.65,39.80 -------------------------------------------------------------------------------- /src/main/kotlin/vista/indicators/AverageTrueRange.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Pablo Pallocchi 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | */ 25 | 26 | package vista.indicators 27 | 28 | import vista.data.Data 29 | import vista.math.abs 30 | import vista.math.max 31 | import vista.math.min 32 | import vista.series.Series 33 | 34 | /** 35 | * True range indicator. 36 | */ 37 | internal class TrueRange( 38 | private val close: Series, 39 | private val high: Series, 40 | private val low: Series 41 | ) : CachedIndicator(close) { 42 | 43 | override val size: Int get() = min(close.size, high.size, low.size) 44 | 45 | private val hl = high - low 46 | 47 | private val tr = max(hl, abs(high - close(1)), abs(low - close(1))) 48 | 49 | override fun calculate(i: Int) = if (i == size - 1) hl[i] else tr[i] 50 | } 51 | 52 | /** 53 | * The true range, used to calculate the [atr] indicator. 54 | * 55 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/volatility?id=average-true-range-atr) 56 | * 57 | * @param close Series of values to process 58 | * @param high Series of high values 59 | * @param low Series of low values 60 | * @sample vista.indicators.StochasticOscillatorTest.withIntSeries 61 | */ 62 | fun tr(close: Series, high: Series, low: Series): Series = TrueRange(close, high, low) 63 | 64 | /** 65 | * The true range, used to calculate the [atr] indicator. 66 | * 67 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/volatility?id=average-true-range-atr) 68 | * 69 | * @sample vista.indicators.StochasticOscillatorTest.withMarketData 70 | */ 71 | fun Data.tr(): Series = tr(close, high, low) 72 | 73 | /** 74 | * The average true range for [n] periods back. 75 | * 76 | * Returns the [rma] of [tr] (true range). 77 | * 78 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/volatility?id=average-true-range-atr) 79 | * 80 | * @param close Series of values to process 81 | * @param high Series of high values 82 | * @param low Series of low values 83 | * @sample vista.indicators.StochasticOscillatorTest.withIntSeries 84 | */ 85 | fun atr(close: Series, high: Series, low: Series, n: Int = 14) = rma(tr(close, high, low), n) 86 | 87 | /** 88 | * The average true range for [n] periods back. 89 | * 90 | * Returns the [rma] of [tr] (true range). 91 | * 92 | * **See:** [Vista Docs](https://bulltimate.github.io/vista/#/volatility?id=average-true-range-atr) 93 | * 94 | * @sample vista.indicators.StochasticOscillatorTest.withMarketData 95 | */ 96 | fun Data.atr(n: Int = 14) = atr(close, high, low, n) -------------------------------------------------------------------------------- /src/test/resources/bb.csv: -------------------------------------------------------------------------------- 1 | date,middle,upper,lower 2 | 05/29/2020,2394.18,2495.96,2292.41 3 | 05/28/2020,2395.77,2501.40,2290.13 4 | 05/27/2020,2394.35,2500.41,2288.28 5 | 05/26/2020,2389.53,2500.86,2278.20 6 | 05/22/2020,2387.24,2497.70,2276.78 7 | 05/21/2020,2385.90,2494.56,2277.24 8 | 05/20/2020,2383.54,2488.81,2278.27 9 | 05/19/2020,2376.82,2468.27,2285.37 10 | 05/18/2020,2370.76,2458.16,2283.36 11 | 05/15/2020,2369.12,2453.49,2284.76 12 | 05/14/2020,2367.39,2449.73,2285.04 13 | 05/13/2020,2368.35,2452.13,2284.58 14 | 05/12/2020,2365.34,2453.19,2277.49 15 | 05/11/2020,2361.66,2456.50,2266.82 16 | 05/08/2020,2349.65,2473.76,2225.54 17 | 05/07/2020,2332.81,2514.27,2151.35 18 | 05/06/2020,2316.58,2536.64,2096.52 19 | 05/05/2020,2299.60,2555.79,2043.40 20 | 05/04/2020,2283.59,2571.31,1995.86 21 | 05/01/2020,2263.12,2593.76,1932.47 22 | 04/30/2020,2244.76,2607.49,1882.02 23 | 04/29/2020,2216.44,2591.38,1841.50 24 | 04/28/2020,2195.29,2580.18,1810.41 25 | 04/27/2020,2177.78,2571.22,1784.35 26 | 04/24/2020,2153.99,2554.10,1753.87 27 | 04/23/2020,2131.25,2522.12,1740.39 28 | 04/22/2020,2105.57,2490.01,1721.13 29 | 04/21/2020,2084.40,2456.12,1712.68 30 | 04/20/2020,2063.14,2425.19,1701.09 31 | 04/17/2020,2035.76,2375.85,1695.67 32 | 04/16/2020,2011.06,2319.28,1702.84 33 | 04/15/2020,1982.15,2240.35,1723.94 34 | 04/14/2020,1957.16,2178.64,1735.68 35 | 04/13/2020,1927.45,2123.95,1730.95 36 | 04/09/2020,1908.26,2080.13,1736.38 37 | 04/08/2020,1889.95,2077.87,1702.03 38 | 04/07/2020,1878.84,2055.17,1702.52 39 | 04/06/2020,1872.85,2038.55,1707.16 40 | 04/03/2020,1863.00,2021.11,1704.89 41 | 04/02/2020,1862.73,2020.55,1704.90 42 | 04/01/2020,1862.99,2021.20,1704.78 43 | 03/31/2020,1866.39,2031.11,1701.68 44 | 03/30/2020,1864.36,2025.88,1702.84 45 | 03/27/2020,1863.86,2024.20,1703.52 46 | 03/26/2020,1863.04,2022.80,1703.28 47 | 03/25/2020,1859.48,2013.93,1705.03 48 | 03/24/2020,1864.17,2026.99,1701.34 49 | 03/23/2020,1865.80,2032.25,1699.35 50 | 03/20/2020,1871.12,2048.43,1693.82 51 | 03/19/2020,1883.62,2085.60,1681.63 52 | 03/18/2020,1897.23,2130.85,1663.60 53 | 03/17/2020,1914.24,2173.90,1654.58 54 | 03/16/2020,1931.63,2206.60,1656.66 55 | 03/13/2020,1953.91,2218.72,1689.11 56 | 03/12/2020,1972.16,2238.18,1706.14 57 | 03/11/2020,1996.33,2237.19,1755.46 58 | 03/10/2020,2012.82,2248.50,1777.15 59 | 03/09/2020,2024.93,2259.36,1790.49 60 | 03/06/2020,2038.86,2250.31,1827.42 61 | 03/05/2020,2046.32,2248.10,1844.53 62 | 03/04/2020,2052.11,2246.02,1858.20 63 | 03/03/2020,2055.80,2246.55,1865.06 64 | 03/02/2020,2060.56,2240.88,1880.25 65 | 02/28/2020,2063.30,2238.65,1887.95 66 | 02/27/2020,2062.65,2240.75,1884.55 67 | 02/26/2020,2061.33,2244.98,1877.68 68 | 02/25/2020,2055.02,2257.23,1852.80 69 | 02/24/2020,2047.80,2270.52,1825.07 70 | 02/21/2020,2040.41,2277.10,1803.73 71 | 02/20/2020,2029.85,2274.41,1785.28 72 | 02/19/2020,2016.56,2261.77,1771.36 73 | 02/18/2020,2002.65,2242.93,1762.38 74 | 02/14/2020,1988.10,2224.76,1751.45 75 | 02/13/2020,1975.26,2206.49,1744.03 76 | 02/12/2020,1960.87,2182.46,1739.27 77 | 02/11/2020,1946.34,2151.28,1741.40 78 | 02/10/2020,1933.36,2116.59,1750.13 79 | 02/07/2020,1920.83,2080.21,1761.44 80 | 02/06/2020,1911.91,2053.84,1769.99 81 | 02/05/2020,1904.00,2031.07,1776.93 82 | 02/04/2020,1897.35,2008.16,1786.54 83 | 02/03/2020,1890.01,1976.20,1803.82 84 | 01/31/2020,1883.55,1952.10,1815.00 85 | 01/30/2020,1878.01,1916.55,1839.48 86 | 01/29/2020,1876.87,1917.51,1836.24 87 | 01/28/2020,1876.32,1918.25,1834.38 88 | 01/27/2020,1877.14,1917.86,1836.43 89 | 01/24/2020,1879.17,1913.50,1844.83 90 | 01/23/2020,1875.54,1927.35,1823.74 91 | 01/22/2020,1870.96,1933.78,1808.15 92 | 01/21/2020,1865.92,1938.14,1793.69 93 | 01/17/2020,1860.93,1938.81,1783.05 94 | 01/16/2020,1856.90,1941.63,1772.16 95 | 01/15/2020,1852.53,1941.37,1763.69 96 | 01/14/2020,1847.89,1943.69,1752.09 97 | 01/13/2020,1842.47,1944.83,1740.10 98 | 01/10/2020,1835.92,1941.65,1730.18 99 | 01/09/2020,1829.20,1939.07,1719.32 100 | 01/08/2020,1821.10,1932.45,1709.76 101 | 01/07/2020,1813.98,1924.51,1703.45 -------------------------------------------------------------------------------- /src/test/resources/ichimoku.csv: -------------------------------------------------------------------------------- 1 | date,tenkan,kijun,senkoua,senkoub 2 | 05/29/2020,2427.73,2390.92,2176.25,2043.52 3 | 05/28/2020,2427.73,2390.92,2157.23,2043.52 4 | 05/27/2020,2427.73,2390.92,2139.80,2043.52 5 | 05/26/2020,2431.63,2390.92,2136.41,2043.52 6 | 05/22/2020,2431.63,2390.92,2119.51,2043.52 7 | 05/21/2020,2431.63,2390.92,2109.30,2043.52 8 | 05/20/2020,2418.91,2372.51,2045.48,1979.70 9 | 05/19/2020,2411.41,2335.61,2024.80,1959.02 10 | 05/18/2020,2376.50,2256.50,1968.80,1905.99 11 | 05/15/2020,2363.40,2246.33,1905.30,1905.99 12 | 05/14/2020,2338.03,2243.08,1900.80,1905.99 13 | 05/13/2020,2338.03,2236.31,1896.66,1905.99 14 | 05/12/2020,2365.69,2202.51,1877.21,1905.99 15 | 05/11/2020,2365.69,2182.08,1875.29,1905.99 16 | 05/08/2020,2365.69,2182.08,1856.85,1905.99 17 | 05/07/2020,2365.69,2182.08,1861.43,1905.99 18 | 05/06/2020,2365.69,2182.08,1866.41,1905.99 19 | 05/05/2020,2365.69,2182.08,1845.99,1905.99 20 | 05/04/2020,2365.69,2182.08,1854.21,1905.99 21 | 05/01/2020,2366.60,2182.08,1846.46,1905.99 22 | 04/30/2020,2377.33,2180.39,1848.54,1905.99 23 | 04/29/2020,2362.32,2173.39,1848.54,1905.99 24 | 04/28/2020,2370.33,2136.50,1848.54,1905.99 25 | 04/27/2020,2353.00,2136.50,1848.54,1905.99 26 | 04/24/2020,2323.61,2136.50,1845.54,1905.99 27 | 04/23/2020,2249.50,2103.00,1837.22,1905.99 28 | 04/22/2020,2239.33,2075.12,1849.68,1905.99 29 | 04/21/2020,2236.08,2043.52,1854.00,1905.99 30 | 04/20/2020,2229.31,2043.52,1883.07,1930.48 31 | 04/17/2020,2195.51,2043.52,1883.07,1930.48 32 | 04/16/2020,2175.08,2043.52,1926.22,1973.62 33 | 04/15/2020,2111.26,1979.70,1926.22,1973.62 34 | 04/14/2020,2090.58,1959.02,1930.80,1973.62 35 | 04/13/2020,2034.58,1903.02,1960.70,1984.20 36 | 04/09/2020,1971.08,1839.52,1961.88,1980.01 37 | 04/08/2020,1966.58,1835.02,1988.19,1980.01 38 | 04/07/2020,1962.44,1830.88,1996.25,1980.01 39 | 04/06/2020,1942.15,1812.28,1998.33,1971.50 40 | 04/03/2020,1939.40,1811.18,1998.33,1970.48 41 | 04/02/2020,1902.51,1811.18,2017.29,1965.70 42 | 04/01/2020,1902.51,1820.35,2036.20,1960.83 43 | 03/31/2020,1902.51,1830.32,2036.20,1960.48 44 | 03/30/2020,1859.32,1832.67,2043.80,1960.48 45 | 03/27/2020,1823.12,1885.29,2068.05,1960.48 46 | 03/26/2020,1791.52,1901.41,2056.34,1960.48 47 | 03/25/2020,1791.52,1905.57,2053.01,1960.48 48 | 03/24/2020,1791.52,1905.57,2053.01,1960.48 49 | 03/23/2020,1791.52,1905.57,2050.65,1960.48 50 | 03/20/2020,1791.52,1905.57,2046.87,1960.48 51 | 03/19/2020,1785.52,1905.57,2046.87,1960.48 52 | 03/18/2020,1768.45,1905.99,2009.46,1960.48 53 | 03/17/2020,1793.38,1905.99,1984.29,1935.30 54 | 03/16/2020,1802.02,1905.99,1960.61,1914.77 55 | 03/13/2020,1835.67,1930.48,1943.18,1900.69 56 | 03/12/2020,1835.67,1930.48,1943.18,1900.69 57 | 03/11/2020,1878.81,1973.62,1937.57,1895.08 58 | 03/10/2020,1878.81,1973.62,1931.57,1889.22 59 | 03/09/2020,1887.98,1973.62,1928.59,1889.22 60 | 03/06/2020,1922.87,1998.54,1855.04,1820.27 61 | 03/05/2020,1925.22,1998.54,1854.53,1820.27 62 | 03/04/2020,1977.84,1998.54,1852.43,1820.27 63 | 03/03/2020,1993.96,1998.54,1852.43,1820.27 64 | 03/02/2020,1998.12,1998.54,1860.46,1820.27 65 | 02/28/2020,1998.12,1998.54,1859.23,1820.27 66 | 02/27/2020,2033.93,2000.65,1861.43,1820.27 67 | 02/26/2020,2071.76,2000.65,1859.04,1820.27 68 | 02/25/2020,2071.76,2000.65,1856.61,1820.27 69 | 02/24/2020,2086.96,2000.65,1856.43,1820.27 70 | 02/21/2020,2135.46,2000.65,1856.43,1820.27 71 | 02/20/2020,2112.03,2000.65,1857.30,1820.27 72 | 02/19/2020,2105.38,2000.65,1850.72,1820.27 73 | 02/18/2020,2105.38,2000.65,1850.72,1820.27 74 | 02/14/2020,2100.66,2000.65,1850.72,1806.41 75 | 02/13/2020,2093.10,2000.65,1840.57,1804.45 76 | 02/12/2020,2093.10,2000.65,1837.59,1804.45 77 | 02/11/2020,2018.28,2000.65,1831.72,1799.35 78 | 02/10/2020,1993.11,1975.47,1830.06,1798.20 79 | 02/07/2020,1964.28,1956.94,1827.97,1798.20 80 | 02/06/2020,1943.18,1943.18,1826.97,1798.20 81 | 02/05/2020,1943.18,1943.18,1826.81,1798.20 82 | 02/04/2020,1937.57,1937.57,1822.55,1798.20 83 | 02/03/2020,1935.53,1927.61,1806.57,1782.73 84 | 01/31/2020,1935.53,1921.65,1773.95,1759.85 85 | 01/30/2020,1858.92,1851.17,1771.52,1759.85 86 | 01/29/2020,1858.92,1850.14,1771.34,1759.85 87 | 01/28/2020,1858.92,1845.94,1770.15,1759.85 88 | 01/27/2020,1858.92,1845.94,1770.15,1759.85 89 | 01/24/2020,1874.97,1845.94,1768.60,1759.85 90 | 01/23/2020,1881.02,1837.44,1767.87,1754.88 91 | 01/22/2020,1886.46,1836.41,1767.87,1754.88 92 | 01/21/2020,1886.46,1831.63,1771.99,1754.88 93 | 01/17/2020,1886.46,1826.77,1776.77,1754.88 94 | 01/16/2020,1886.46,1826.41,1776.77,1754.88 95 | 01/15/2020,1886.46,1826.41,1778.02,1754.88 96 | 01/14/2020,1888.19,1826.41,1778.02,1754.88 97 | 01/13/2020,1875.03,1826.41,1775.77,1754.88 98 | 01/10/2020,1875.03,1826.41,1775.61,1754.88 99 | 01/09/2020,1875.03,1826.41,1775.61,1757.85 100 | 01/08/2020,1856.70,1824.45,1768.69,1758.82 101 | 01/07/2020,1850.74,1824.45,1766.77,1758.82 --------------------------------------------------------------------------------