├── .idea
├── .name
├── vcs.xml
├── modules.xml
├── scala_compiler.xml
├── misc.xml
└── compiler.xml
├── .travis.yml
├── src
├── data
│ ├── txn-no_headers.csv
│ └── sjnk.csv
└── main
│ └── scala
│ └── com
│ └── quant
│ ├── Parse
│ ├── IFTTTParse.scala
│ ├── GoogleFinanceParse.scala
│ └── CsvUtil.scala
│ ├── TechAnalysis
│ ├── Average.scala
│ └── Fibo.scala
│ ├── Tree
│ ├── Sort.scala
│ └── Tree.scala
│ └── StockEtfData.scala
├── LICENSE
├── .gitignore
├── README.md
├── ParseIFTTStockEtf.iml
└── pom.xml
/.idea/.name:
--------------------------------------------------------------------------------
1 | ParseIFTTStockEtf
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | install: mvn install
2 | language: scala
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/scala_compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/data/txn-no_headers.csv:
--------------------------------------------------------------------------------
1 | 5/1/2017,79.25,79.5,78.4,79.29,5065496
2 | 4/28/2017,81.04,81.04,78.65,79.18,10522503
3 | 4/27/2017,80.75,81.27,80.36,80.8,5762688
4 | 4/26/2017,82.17,82.92,80.79,81.11,6763868
5 | 4/25/2017,81.3,82.43,81.12,82.36,7604096
6 | 4/24/2017,81.02,81.51,80.85,81.08,5129198
7 | 4/21/2017,79.58,80.41,79.24,79.81,6314436
8 | 4/20/2017,79.72,80.89,79.51,80.76,3586302
9 | 4/19/2017,79.96,80.66,79.28,79.37,4673843
10 | 4/18/2017,78.51,79.84,78.35,79.61,4529140
11 | 4/17/2017,78.3,78.75,78.12,78.72,4462363
12 | 4/13/2017,78.47,79.33,77.83,77.84,4658269
13 |
--------------------------------------------------------------------------------
/src/main/scala/com/quant/Parse/IFTTTParse.scala:
--------------------------------------------------------------------------------
1 | package com.quant.Parse
2 |
3 | import scala.collection.mutable.ArrayBuffer
4 |
5 | /**
6 | * Created by Frank Cash on 5/2/17.
7 | */
8 | object IFTTTParse {
9 |
10 |
11 | /**
12 | *
13 | * @param rows Rows from CSVParse Output
14 | * @return ArrayBuffer with Closing Costs of the Stock/ETF
15 | */
16 | def StripClosingPriceIFTTT(rows: ArrayBuffer[Array[String]]): ArrayBuffer[Double] ={
17 | val closingCosts:ArrayBuffer[Double] = new ArrayBuffer[Double]
18 | rows.map(row => closingCosts.append(row(3).toDouble))
19 | closingCosts
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/scala/com/quant/Parse/GoogleFinanceParse.scala:
--------------------------------------------------------------------------------
1 | package com.quant.Parse
2 |
3 | import scala.collection.mutable.ArrayBuffer
4 |
5 | /**
6 | * Created by Frank Cash on 5/2/17.
7 | */
8 | object GoogleFinanceParse {
9 |
10 | /**
11 | *
12 | * @param rows Rows from CSVParse Output
13 | * @return ArrayBuffer with Closing Costs of the Stock/ETF
14 | */
15 | def StripClosingPriceGoog(rows: ArrayBuffer[Array[String]]): ArrayBuffer[Double] ={
16 | val closingCosts:ArrayBuffer[Double] = new ArrayBuffer[Double]
17 | rows.map(row => closingCosts.append(row(4).toDouble))
18 | closingCosts
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/main/scala/com/quant/Parse/CsvUtil.scala:
--------------------------------------------------------------------------------
1 | package com.quant.Parse
2 |
3 | import scala.collection.mutable.ArrayBuffer
4 |
5 | /**
6 | * Created by Frank Cash on 5/2/17.
7 | */
8 | object CsvUtil {
9 | /**
10 | *
11 | * @param path Path to CSV File
12 | * @return ArrayBuffer
13 | */
14 | def CSVParseIFTTT(path: String): ArrayBuffer[Array[String]] ={
15 | val rows = ArrayBuffer[Array[String]]()
16 | val bufferedSource = io.Source.fromFile(path)
17 | for(line <- bufferedSource.getLines()){
18 | rows += line.split(",").map(_.trim)
19 | }
20 | bufferedSource.close()
21 | rows
22 | }
23 |
24 | /**
25 | *
26 | * @param xs List
27 | * @param n Step size
28 | * @tparam A
29 | * @return Returns a list of stepped lists
30 | */
31 | def split[A](xs:List[A], n:Int): List[List[A]] ={
32 | if(xs.isEmpty) Nil
33 | else (xs take n) :: split(xs drop n, n)
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Frank Cash
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.
--------------------------------------------------------------------------------
/src/main/scala/com/quant/TechAnalysis/Average.scala:
--------------------------------------------------------------------------------
1 | package com.quant.TechAnalysis
2 |
3 | /**
4 | * Created by Frank Cash on 5/6/17.
5 | */
6 | object Average {
7 |
8 | /**
9 | * Reference on Resistance
10 | * @param data List of List Double. Takes return from split(xs, n).
11 | * @return Returns the avg resistance
12 | */
13 | def avgResistance(data:List[List[Double]]): Double = {
14 | var runTotal = 0.0;
15 | for(n <- data){
16 | runTotal += (n.max)
17 | }
18 | return runTotal./(data.length)
19 | }
20 |
21 |
22 | /**
23 | * Reference on Support
24 | * @param data List of List Double. Takes return from split(xs, n)
25 | * @return Returns the avg support
26 | */
27 | def avgSupport(data:List[List[Double]]): Double ={
28 | var runTotal = 0.0
29 | for(n <- data){
30 | runTotal += n.min
31 | }
32 | return runTotal./(data.length)
33 | }
34 |
35 |
36 | /**
37 | * Reference on Moving Average
38 | * @param data List[Double] of prices to include in moving average
39 | * @return Moving Average
40 | */
41 | def movingAvg(movingAvgSize:Double, data:List[Double] ): Double ={
42 | var avg = 0.0
43 | avg = data.sum / movingAvgSize
44 | return avg
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/scala/com/quant/TechAnalysis/Fibo.scala:
--------------------------------------------------------------------------------
1 | package com.quant.TechAnalysis
2 |
3 | import scala.collection.mutable.ArrayBuffer
4 |
5 | /**
6 | * Created by Frank Cash on 5/6/17.
7 | */
8 | object Fibo {
9 | /**
10 | * Fibonacci Retracement Math
11 | * High minus Low multiplied by the given ratio
12 | * Reference on Fibonacci Retracement
13 | * @param h The high value
14 | * @param l The low value
15 | * @param ratio The ratio to use, as a decimal
16 | * @return the amount for the fibonacci retracement at the given ratio
17 | */
18 | def fibonacciRetracement(h:Double, l:Double, ratio:Double): Double={
19 | val fib = (h - l) * ratio
20 | val res = h - fib
21 | res
22 | }
23 |
24 | /**
25 | * Reference on Fibonacci Retracement
26 | * @param high
27 | * @param low
28 | * @return List of fibonacci retracement values for [23.6%, 38.2%, 50.0%, 0.618%, 100%]
29 | */
30 | def fibRetracementValues(high:Double, low:Double): List[Double]={
31 | val fib:ArrayBuffer[Double] = new ArrayBuffer[Double]
32 | fib.append(fibonacciRetracement(high, low, 0.236))
33 | fib.append(fibonacciRetracement(high, low, 0.382))
34 | fib.append(fibonacciRetracement(high, low, 0.500))
35 | fib.append(fibonacciRetracement(high, low, 0.618))
36 | fib.append(fibonacciRetracement(high, low, 1.000))
37 | fib.toList
38 |
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Scala Specific
2 | *.class
3 | *.log
4 |
5 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
6 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
7 |
8 | # User-specific stuff:
9 | .idea/**/workspace.xml
10 | .idea/**/tasks.xml
11 | .idea/dictionaries
12 | .idea/kotlinc.xml
13 | # Sensitive or high-churn files:
14 | .idea/**/dataSources/
15 | .idea/**/dataSources.ids
16 | .idea/**/dataSources.xml
17 | .idea/**/dataSources.local.xml
18 | .idea/**/sqlDataSources.xml
19 | .idea/**/dynamic.xml
20 | .idea/**/uiDesigner.xml
21 |
22 | # Gradle:
23 | .idea/**/gradle.xml
24 | .idea/**/libraries
25 |
26 | #Maven
27 | target/
28 |
29 | # Mongo Explorer plugin:
30 | .idea/**/mongoSettings.xml
31 |
32 | ## File-based project format:
33 | *.iws
34 |
35 | target/
36 |
37 | ## Plugin-specific files:
38 |
39 | # IntelliJ
40 | /out/
41 |
42 | # mpeltonen/sbt-idea plugin
43 | .idea_modules/
44 |
45 | # JIRA plugin
46 | atlassian-ide-plugin.xml
47 |
48 | # Cursive Clojure plugin
49 | .idea/replstate.xml
50 |
51 | # Crashlytics plugin (for Android Studio and IntelliJ)
52 | com_crashlytics_export_strings.xml
53 | crashlytics.properties
54 | crashlytics-build.properties
55 | fabric.properties
56 |
57 | # Windows thumbnail cache files
58 | Thumbs.db
59 | ehthumbs.db
60 | ehthumbs_vista.db
61 |
62 | # Folder config file
63 | Desktop.ini
64 |
65 | # Recycle Bin used on file shares
66 | $RECYCLE.BIN/
67 |
68 | # Windows Installer files
69 | *.cab
70 | *.msi
71 | *.msm
72 | *.msp
73 |
74 | # Windows shortcuts
75 | *.lnk
76 |
77 | /lib
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Scala Quant
2 |
3 | This is a project to explore market data.
4 |
5 | This project is designed to work with a specific IFTTT plugin: [Keep track of a particular stock's daily closing price in a spreadsheet](https://ifttt.com/applets/117304p-keep-track-of-a-particular-stock-s-daily-closing-price-in-a-spreadsheet)
6 |
7 | This project is also designed to work with a CSV of historical data downloaded from Google Finance.
8 |
9 | ## Utilizing
10 |
11 | Download the `CSV` file for your desired Stock/ETF.
12 |
13 |
14 | ## Functions
15 |
16 | `avgResistance(data:List[List[Double]]): Double`
17 |
18 | Calculates the [average resistance](http://www.investopedia.com/articles/technical/061801.asp) based local maxes from split lists. Takes maximum values from the split lists and then generates an average using the amount of split lists.
19 |
20 | `avgSupport(data:List[List[Double]]): Double`
21 |
22 | Calculates the [average support](http://www.investopedia.com/articles/technical/061801.asp) based local maxes from split lists. Takes minimum values from the split lists and then generates an average using the amount of split lists.
23 |
24 | `movingAvg(movingAvgSize:Double, data:List[Double] ): Double`
25 |
26 | Calculates [moving average](http://www.investopedia.com/terms/m/movingaverage.asp). The amount of days is pre-defined to 10.
27 |
28 | `fibRetracementValues(high:Double, low:Double): List[Double]`
29 |
30 | Calculates [Fibonacci Retracement](http://www.investopedia.com/ask/answers/05/fibonacciretracement.asp) values for the given high and low. Predefined ratios for the retracement values are: 23.6%, 38.2%, 50.0%, 0.618%, 100%.
31 |
32 | ## Configuration
33 |
34 | It is also able to configure how many days should be grouped into the lists for calculating the average resistance and support. Edit `final val step = 5`.
35 |
36 | The moving average size can be changed. Edit ` final val movingAvgSize = 10`
37 |
38 | ### License
39 |
40 | MIT
41 |
--------------------------------------------------------------------------------
/ParseIFTTStockEtf.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/main/scala/com/quant/Tree/Sort.scala:
--------------------------------------------------------------------------------
1 | /**MIT License
2 | *Copyright (c) 2017 Frank Cash
3 | *Permission is hereby granted, free of charge, to any person obtaining a copy
4 | *of this software and associated documentation files (the "Software"), to deal
5 | *in the Software without restriction, including without limitation the rights
6 | *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | *copies of the Software, and to permit persons to whom the Software is
8 | *furnished to do so, subject to the following conditions:
9 | *The above copyright notice and this permission notice shall be included in all
10 | *copies or substantial portions of the Software.
11 | *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 | *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 | *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14 | *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15 | *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16 | *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17 | *SOFTWARE.
18 | */
19 |
20 | package Tree
21 |
22 |
23 | /**
24 | * Created by Frank Cash on 4/16/17.
25 | */
26 |
27 | object Sort{
28 |
29 | /**
30 | *
31 | * @param left List[Double]
32 | * @param right List[Double]
33 | * @return List[Double]
34 | */
35 | def merge(left: List[Double], right: List[Double]):List[Double]= (left, right)match{
36 | case(left, Nil) => left
37 | case(Nil, right) => right
38 | case(leftHead :: leftTail, rightHead :: rightTail) =>
39 | if(leftHead < rightHead) leftHead::merge(leftTail, right)
40 | else rightHead :: merge(left, rightTail)
41 | }
42 |
43 | /**
44 | * Merge Sort
45 | * @param list[Double] Input
46 | * @return Returns a sorted List[Double]
47 | */
48 | def mergeSort(list: List[Double]): List[Double] ={
49 | val n = list.length / 2
50 | if (n == 0) list
51 | else{
52 | val(left, right) = list.splitAt(n)
53 | merge(mergeSort(left), mergeSort(right))
54 | }
55 | }
56 |
57 |
58 | }
59 |
60 |
--------------------------------------------------------------------------------
/src/data/sjnk.csv:
--------------------------------------------------------------------------------
1 | "February 24 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,28.11,http://finance.yahoo.com/q?s=SJNK,"February 24, 2017"
2 | "February 27 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,28.13,http://finance.yahoo.com/q?s=SJNK,"February 27, 2017"
3 | "February 28 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,28.14,http://finance.yahoo.com/q?s=SJNK,"February 28, 2017"
4 | "March 01 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,28.09,http://finance.yahoo.com/q?s=SJNK,"March 01, 2017"
5 | "March 02 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,28.04,http://finance.yahoo.com/q?s=SJNK,"March 02, 2017"
6 | "March 03 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,28.06,http://finance.yahoo.com/q?s=SJNK,"March 03, 2017"
7 | "March 06 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,27.97,http://finance.yahoo.com/q?s=SJNK,"March 06, 2017"
8 | "March 07 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,27.88,http://finance.yahoo.com/q?s=SJNK,"March 07, 2017"
9 | "March 08 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,27.77,http://finance.yahoo.com/q?s=SJNK,"March 08, 2017"
10 | "March 09 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,27.69,http://finance.yahoo.com/q?s=SJNK,"March 09, 2017"
11 | "March 10 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,27.695,http://finance.yahoo.com/q?s=SJNK,"March 10, 2017"
12 | "March 13 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,27.71,http://finance.yahoo.com/q?s=SJNK,"March 13, 2017"
13 | "March 14 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,27.65,http://finance.yahoo.com/q?s=SJNK,"March 14, 2017"
14 | "March 15 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,27.89,http://finance.yahoo.com/q?s=SJNK,"March 15, 2017"
15 | "March 16 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,27.785,http://finance.yahoo.com/q?s=SJNK,"March 16, 2017"
16 | "March 17 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,27.8,http://finance.yahoo.com/q?s=SJNK,"March 17, 2017"
17 | "March 20 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,27.785,http://finance.yahoo.com/q?s=SJNK,"March 20, 2017"
18 | "March 21 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,27.6556,http://finance.yahoo.com/q?s=SJNK,"March 21, 2017"
19 | "March 22 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,27.715,http://finance.yahoo.com/q?s=SJNK,"March 22, 2017"
20 | "March 23 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,27.695,http://finance.yahoo.com/q?s=SJNK,"March 23, 2017"
21 | "March 24 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,27.775,http://finance.yahoo.com/q?s=SJNK,"March 24, 2017"
22 | "March 27 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,27.79,http://finance.yahoo.com/q?s=SJNK,"March 27, 2017"
23 | "March 28 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,27.87,http://finance.yahoo.com/q?s=SJNK,"March 28, 2017"
24 | "March 29 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,27.89,http://finance.yahoo.com/q?s=SJNK,"March 29, 2017"
25 | "March 30 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,27.975,http://finance.yahoo.com/q?s=SJNK,"March 30, 2017"
26 | "March 31 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,27.95,http://finance.yahoo.com/q?s=SJNK,"March 31, 2017"
27 | "April 03 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,27.82,http://finance.yahoo.com/q?s=SJNK,"April 03, 2017"
28 | "April 04 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,27.84,http://finance.yahoo.com/q?s=SJNK,"April 04, 2017"
29 | "April 05 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,27.82,http://finance.yahoo.com/q?s=SJNK,"April 05, 2017"
30 | "April 06 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,27.8545,http://finance.yahoo.com/q?s=SJNK,"April 06, 2017"
31 | "April 07 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,27.8557,http://finance.yahoo.com/q?s=SJNK,"April 07, 2017"
32 | "April 10 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,27.89,http://finance.yahoo.com/q?s=SJNK,"April 10, 2017"
33 | "April 11 2017",SPDR Blmbg BarclaysST HY Bd ETF,SJNK,27.88,http://finance.yahoo.com/q?s=SJNK,"April 11, 2017"
--------------------------------------------------------------------------------
/src/main/scala/com/quant/StockEtfData.scala:
--------------------------------------------------------------------------------
1 | /**MIT License
2 | *Copyright (c) 2017 Frank Cash
3 | *Permission is hereby granted, free of charge, to any person obtaining a copy
4 | *of this software and associated documentation files (the "Software"), to deal
5 | *in the Software without restriction, including without limitation the rights
6 | *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | *copies of the Software, and to permit persons to whom the Software is
8 | *furnished to do so, subject to the following conditions:
9 | *The above copyright notice and this permission notice shall be included in all
10 | *copies or substantial portions of the Software.
11 | *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 | *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 | *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14 | *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15 | *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16 | *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17 | *SOFTWARE.
18 | */
19 |
20 | /**
21 | * Created by Frank Cash on 4/12/2017.
22 | * Made to work CSV output from: https://ifttt.com/applets/117304p-keep-track-of-a-particular-stock-s-daily-closing-price-in-a-spreadsheet
23 | */
24 |
25 | import com.quant.TechAnalysis.{Average, Fibo}
26 | import Tree.{Sort, Tree}
27 | import com.quant.Parse.{CsvUtil, GoogleFinanceParse, IFTTTParse}
28 |
29 |
30 | object StockEtfData {
31 |
32 | /**
33 | * Step for calculating segmentation of price data when calculating local min and max for historical support and resistance
34 | */
35 | final val step = 5
36 | /**
37 | * Amount of days to use for Moving Average
38 | */
39 | final val movingAvgSize = 10
40 |
41 |
42 |
43 | def IFTTT(path:String ): Unit= {
44 | println("hello World")
45 | val rows = CsvUtil.CSVParseIFTTT(path)
46 | val closingPrices = IFTTTParse.StripClosingPriceIFTTT(rows)
47 | val sorted = Sort.mergeSort(closingPrices.toList)
48 | val myTree = Tree.fromSortedList(sorted)
49 | val steppedClosingPrices = CsvUtil.split(closingPrices.toList, step)
50 | val mvAvg = Average.movingAvg(movingAvgSize.toDouble, closingPrices.takeRight(movingAvgSize.toInt).toList)
51 | val retracementLevels = Fibo.fibRetracementValues(myTree.max, myTree.min)
52 | println("Moving Average is: "+ mvAvg)
53 |
54 | println("Average Support: " + Average.avgSupport(steppedClosingPrices))
55 | println("Average Resistance: " + Average.avgResistance(steppedClosingPrices))
56 |
57 |
58 | println("Historical Low: " + myTree.min)
59 | println("Historical Max: " + myTree.max)
60 | retracementLevels.map(l => println("Retracement: " + l))
61 |
62 |
63 | }
64 |
65 | def Goog(path:String ): Unit= {
66 | val rows = CsvUtil.CSVParseIFTTT(path)
67 | val closingPrices = GoogleFinanceParse.StripClosingPriceGoog(rows)
68 | val sorted = Sort.mergeSort(closingPrices.toList)
69 | val myTree = Tree.fromSortedList(sorted)
70 | val steppedClosingPrices = CsvUtil.split(closingPrices.toList, step)
71 | val mvAvg = Average.movingAvg(movingAvgSize.toDouble, closingPrices.takeRight(movingAvgSize.toInt).toList)
72 | val retracementLevels = Fibo.fibRetracementValues(myTree.max, myTree.min)
73 | println("Moving Average is: "+ mvAvg)
74 |
75 | println("Average Support: " + Average.avgSupport(steppedClosingPrices))
76 | println("Average Resistance: " + Average.avgResistance(steppedClosingPrices))
77 |
78 |
79 | println("Historical Low: " + myTree.min)
80 | println("Historical Max: " + myTree.max)
81 | retracementLevels.map(l => println("Retracement: " + l))
82 | }
83 |
84 | def main(args: Array[String]): Unit= {
85 | IFTTT("/Users/majora/Code/Scala-Quant/src/data/sjnk.csv")
86 | Goog("/Users/majora/Code/Scala-Quant/src/data/txn-no_headers.csv")
87 | }
88 |
89 |
90 | }
91 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 | quant.com.scalastock
7 | ParseIFTTStockEtf
8 | 1.0-SNAPSHOT
9 |
10 | 2.11.8
11 | 2.2.0
12 | 1.7
13 | 1.7
14 |
15 |
16 |
17 | org.scalatest
18 | scalatest_2.10
19 | ${scalaTest.version}
20 | test
21 |
22 |
23 | org.scala-lang
24 | scala-library
25 | ${scala.version}
26 |
27 |
28 | org.pegdown
29 | pegdown
30 | 1.2.1
31 | test
32 |
33 |
34 | junit
35 | junit
36 | 4.8.1
37 | test
38 |
39 |
40 |
41 |
42 |
43 | org.scala-tools
44 | maven-scala-plugin
45 | 2.15.2
46 |
47 |
48 | scala-compile
49 |
50 | compile
51 | testCompile
52 |
53 |
54 |
55 | -dependencyfile
56 | ${project.build.directory}/.scala_dependencies
57 |
58 |
59 |
60 |
61 |
62 |
76 |
77 | org.scalatest
78 | scalatest-maven-plugin
79 | 1.0-M2
80 |
81 | ${project.build.directory}/surefire-reports
82 | W
83 |
84 |
85 |
86 | scala-test
87 |
88 | test
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 | net.alchim31.maven
99 | scala-maven-plugin
100 |
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/src/main/scala/com/quant/Tree/Tree.scala:
--------------------------------------------------------------------------------
1 | /**MIT License
2 | *Copyright (c) 2017 Frank Cash
3 | *Permission is hereby granted, free of charge, to any person obtaining a copy
4 | *of this software and associated documentation files (the "Software"), to deal
5 | *in the Software without restriction, including without limitation the rights
6 | *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | *copies of the Software, and to permit persons to whom the Software is
8 | *furnished to do so, subject to the following conditions:
9 | *The above copyright notice and this permission notice shall be included in all
10 | *copies or substantial portions of the Software.
11 | *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 | *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 | *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14 | *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15 | *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16 | *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17 | *SOFTWARE.
18 | */
19 |
20 | package Tree
21 |
22 | /**
23 | * Frank Cash
24 | * Implementation based upon Scalacaster BST
25 | */
26 |
27 | abstract sealed class Tree[+A <% Ordered[A]] {
28 |
29 | /**
30 | * The value of this tree.
31 | */
32 | def value: A
33 |
34 | /**
35 | * The left child of this tree.
36 | */
37 | def left: Tree[A]
38 |
39 | /**
40 | * The right child of this tree.
41 | */
42 | def right: Tree[A]
43 |
44 | /**
45 | * The size of this tree.
46 | */
47 | def size: Int
48 |
49 | /**
50 | * Checks whether this tree is empty or not.
51 | */
52 | def isEmpty: Boolean
53 |
54 |
55 | def isValid: Boolean =
56 | if (isEmpty) true
57 | else if (left.isEmpty && right.isEmpty) true
58 | else if (left.isEmpty) right.value >= value && right.isValid
59 | else if (right.isEmpty) left.value <= value && left.isValid
60 | else left.value <= value && right.value >= value && left.isValid && right.isValid
61 |
62 |
63 | def isBalanced: Boolean = {
64 | def loop(t: Tree[A]): Int =
65 | if (t.isEmpty) 0
66 | else {
67 | val l = loop(t.left)
68 | if (l == -1) -1
69 | else {
70 | val r = loop(t.right)
71 | if (r == -1) -1
72 | else if (math.abs(l - r) > 1) -1
73 | else 1 + math.max(l, r)
74 | }
75 | }
76 |
77 | !(loop(this) == -1)
78 | }
79 |
80 |
81 | def add[B >: A <% Ordered[B]](x: B): Tree[B] =
82 | if (isEmpty) Tree.make(x)
83 | else if (x < value) Tree.make(value, left.add(x), right)
84 | else if (x > value) Tree.make(value, left, right.add(x))
85 | else this
86 |
87 |
88 |
89 |
90 |
91 | def contains[B >: A <% Ordered[B]](x: B): Boolean = {
92 | def loop(t: Tree[A], c: Option[A]): Boolean =
93 | if (t.isEmpty) check(c)
94 | else if (x < t.value) loop(t.left, c)
95 | else loop(t.right, Some(t.value))
96 |
97 | def check(c: Option[A]): Boolean = c match {
98 | case Some(y) if y == x => true
99 | case _ => false
100 | }
101 |
102 | loop(this, None)
103 | }
104 |
105 |
106 | def foreach(f: (A) => Unit): Unit =
107 | if (!isEmpty) {
108 | left.foreach(f)
109 | f(value)
110 | right.foreach(f)
111 | }
112 |
113 | def map[B <% Ordered[B]](f: (A) => B): Tree[B] =
114 | if (isEmpty) Tree.empty
115 | else Tree.make(f(value), left.map(f), right.map(f))
116 |
117 |
118 | /**
119 | * Searches for the minimal element of this tree.
120 | */
121 | def min: A = {
122 | def loop(t: Tree[A], m: A): A =
123 | if (t.isEmpty) m
124 | else loop(t.left, t.value)
125 |
126 | if (isEmpty) fail("An empty tree.")
127 | else loop(left, value)
128 | }
129 |
130 | /**
131 | * Searches for the maximal element of this tree.
132 | */
133 | def max: A = {
134 | def loop(t: Tree[A], m: A): A =
135 | if (t.isEmpty) m
136 | else loop(t.right, t.value)
137 |
138 | if (isEmpty) fail("An empty tree.")
139 | else loop(right, value)
140 | }
141 |
142 | /**
143 | * Calculates the height of this tree.
144 | */
145 | def height: Int =
146 | if (isEmpty) 0
147 | else 1 + math.max(left.height, right.height)
148 |
149 |
150 |
151 | /**
152 | * Searches for the successor of given element 'x'.
153 | */
154 | def successor[B >: A <% Ordered[B]](x: B): A = {
155 | def forward(t: Tree[A], p: List[Tree[A]]): A =
156 | if (t.isEmpty) fail("Can't find " + x + " in this tree.")
157 | else if (x < t.value) forward(t.left, t :: p)
158 | else if (x > t.value) forward(t.right, t :: p)
159 | else if (!t.right.isEmpty) t.right.min
160 | else backward(t, p)
161 |
162 | def backward(t: Tree[A], p: List[Tree[A]]): A =
163 | if (p.isEmpty) fail("The " + x + " doesn't have an successor.")
164 | else if (t == p.head.right) backward(p.head, p.tail)
165 | else p.head.value
166 |
167 | forward(this, Nil)
168 | }
169 |
170 |
171 |
172 | /**
173 | * Searches for the lower bound element of given 'x'.
174 | */
175 | def lowerBound[B >: A <% Ordered[B]](x: B): A =
176 | if (isEmpty) fail("Tree is empty.")
177 | else if (x < value)
178 | if (!left.isEmpty) left.lowerBound(x)
179 | else value
180 | else if (x > value)
181 | if (!right.isEmpty) { val v = right.lowerBound(x); if (v > x) value else v }
182 | else value
183 | else value
184 |
185 | /**
186 | * Calculates the number of elements that less or equal to given 'x'.
187 | */
188 | def rank[B >: A <% Ordered[B]](x: B): Int =
189 | if (isEmpty) 0
190 | else if (x < value) left.rank(x)
191 | else if (x > value) 1 + left.size + right.rank(x)
192 | else left.size
193 |
194 | /**
195 | * Searches for the upper bound element of given 'x'.
196 | */
197 | def upperBound[B >: A <% Ordered[B]](x: B): A =
198 | if (isEmpty) fail("Tree is empty.")
199 | else if (x < value)
200 | if (!left.isEmpty) { val v = left.upperBound(x); if (v < x) value else v }
201 | else value
202 | else if (x > value)
203 | if (!right.isEmpty) right.upperBound(x)
204 | else value
205 | else value
206 |
207 | /**
208 | * Calculates the path for given element 'x'.
209 | */
210 | def path[B >: A <% Ordered[B]](x: B): List[Tree[A]] =
211 | if (isEmpty) fail("Can't find " + x + " in this tree.")
212 | else if (x < value) this :: left.path(x)
213 | else if (x > value) this :: right.path(x)
214 | else List(this)
215 |
216 |
217 | /**
218 | * Performs the DFS and dumps values to the list.
219 | */
220 | def valuesByDepth: List[A] = {
221 | def loop(s: List[Tree[A]]): List[A] =
222 | if (s.isEmpty) Nil
223 | else if (s.head.isEmpty) loop(s.tail)
224 | else s.head.value :: loop(s.head.right :: s.head.left :: s.tail)
225 |
226 | loop(List(this))
227 | }
228 |
229 | /**
230 | * Fails with given message 'm'.
231 | */
232 | def fail(m: String) = throw new NoSuchElementException(m)
233 | }
234 |
235 | case object Leaf extends Tree[Nothing] {
236 | def value: Nothing = fail("An empty tree.")
237 | def left: Tree[Nothing] = fail("An empty tree.")
238 | def right: Tree[Nothing] = fail("An empty tree.")
239 | def size: Int = 0
240 |
241 | def isEmpty: Boolean = true
242 | }
243 |
244 | case class Branch[A <% Ordered[A]](value: A,
245 | left: Tree[A],
246 | right: Tree[A],
247 | size: Int) extends Tree[A] {
248 | def isEmpty: Boolean = false
249 | }
250 |
251 | object Tree {
252 |
253 | /**
254 | * An empty tree.
255 | */
256 | def empty[A]: Tree[A] = Leaf
257 |
258 | /**
259 | * A smart constructor for tree's branch.
260 | */
261 | def make[A <% Ordered[A]](x: A, l: Tree[A] = Leaf, r: Tree[A] = Leaf): Tree[A] =
262 | Branch(x, l, r, l.size + r.size + 1)
263 |
264 | /**
265 | * Creates a new tree from given sequence 'xs'.
266 | */
267 | def apply[A <% Ordered[A]](xs: A*): Tree[A] = {
268 | var r: Tree[A] = Tree.empty
269 | for (x <- xs) r = r.add(x)
270 | r
271 | }
272 |
273 | /**
274 | * Creates a new balanced tree from given sorted array 'a'.
275 | */
276 | def fromSortedArray[A <% Ordered[A]](a: Array[A]): Tree[A] = {
277 | def loop(l: Int, r: Int): Tree[A] =
278 | if (l == r) Tree.empty
279 | else {
280 | val p = (l + r) / 2
281 | Tree.make(a(p), loop(l, p), loop(p + 1, r))
282 | }
283 |
284 | loop(0, a.length)
285 | }
286 |
287 | /**
288 | * Creates a new balanced tree from given sorted list 'l'.
289 | *
290 | * http://www.geeksforgeeks.org/sorted-linked-list-to-balanced-bst/
291 | */
292 | def fromSortedList[A <% Ordered[A]](l: List[A]): Tree[A] = {
293 | def loop(ll: List[A], n: Int): (List[A], Tree[A]) =
294 | if (n == 0) (ll, Tree.empty)
295 | else {
296 | val (lt, left) = loop(ll, n / 2)
297 | val (rt, right) = loop(lt.tail, n - 1 - n / 2)
298 | (rt, Tree.make(lt.head, left, right))
299 | }
300 |
301 | loop(l, l.length)._2
302 | }
303 |
304 | }
305 |
--------------------------------------------------------------------------------