├── src ├── test │ ├── resources │ │ ├── fxQuoteRequest │ │ │ ├── GBPEUR.csv │ │ │ ├── USDEUR.csv │ │ │ └── EURUSD_EURCHF.csv │ │ ├── simpleQuoteRequest │ │ │ ├── SCTY.csv │ │ │ ├── C6L.SI.csv │ │ │ ├── GOOG.csv │ │ │ ├── TSLA.csv │ │ │ ├── AIR.PA.csv │ │ │ └── INTC.csv │ │ ├── historicalQuoteRequest │ │ │ ├── AIR.PA_1Y_M.csv │ │ │ ├── INTC_1Y_M.csv │ │ │ ├── GOOG_1Y_M.csv │ │ │ ├── SCTY_1Y_W.csv │ │ │ └── TSLA_1Y_D.csv │ │ ├── multiQuoteRequest │ │ │ ├── INTC_AIR.PA.csv │ │ │ └── AIR.PA_INTC_C6L.SI.csv │ │ └── requests.yml │ └── java │ │ └── yahoofinance │ │ ├── histquotes2 │ │ └── CrumbManagerTest.java │ │ ├── mock │ │ ├── ResponseResource.java │ │ ├── MockedServersTest.java │ │ └── YahooFinanceDispatcher.java │ │ ├── FxQuoteRequestTest.java │ │ ├── QuoteRequestFlowTest.java │ │ ├── HistoricalQuoteRequestTest.java │ │ └── SimpleQuoteRequestTest.java └── main │ └── java │ └── yahoofinance │ ├── histquotes │ ├── Interval.java │ ├── HistoricalQuote.java │ └── HistQuotesRequest.java │ ├── histquotes2 │ ├── QueryInterval.java │ ├── IntervalMapper.java │ ├── HistoricalDividend.java │ ├── HistoricalSplit.java │ ├── HistDividendsRequest.java │ ├── HistSplitsRequest.java │ ├── HistQuotes2Request.java │ └── CrumbManager.java │ ├── quotes │ ├── query1v7 │ │ ├── FxQuotesQuery1V7Request.java │ │ ├── QuotesRequest.java │ │ └── StockQuotesQuery1V7Request.java │ ├── csv │ │ ├── FxQuotesRequest.java │ │ ├── QuotesRequest.java │ │ ├── QuotesProperty.java │ │ ├── StockQuotesData.java │ │ └── StockQuotesRequest.java │ ├── fx │ │ ├── FxQuote.java │ │ └── FxSymbols.java │ └── stock │ │ ├── StockDividend.java │ │ ├── StockStats.java │ │ └── StockQuote.java │ ├── util │ └── RedirectableRequest.java │ ├── query2v8 │ └── HistQuotesQuery2V8Request.java │ ├── exchanges │ └── ExchangeTimeZone.java │ └── Utils.java ├── .gitignore ├── .github ├── workflows │ ├── jacoco-badge.yml │ └── maven.yml └── badges │ └── jacoco.svg ├── LICENSE.txt ├── README.md └── pom.xml /src/test/resources/fxQuoteRequest/GBPEUR.csv: -------------------------------------------------------------------------------- 1 | "GBPEUR=X",1.1806 2 | -------------------------------------------------------------------------------- /src/test/resources/fxQuoteRequest/USDEUR.csv: -------------------------------------------------------------------------------- 1 | "USDEUR=X",0.8898 2 | -------------------------------------------------------------------------------- /src/test/resources/fxQuoteRequest/EURUSD_EURCHF.csv: -------------------------------------------------------------------------------- 1 | "EURUSD=X",1.1235 2 | "EURCHF=X",1.0954 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | 3 | .classpath 4 | .project 5 | .settings 6 | .idea 7 | /*.iml 8 | /classes/ 9 | -------------------------------------------------------------------------------- /src/test/resources/simpleQuoteRequest/SCTY.csv: -------------------------------------------------------------------------------- 1 | "SolarCity Corporation","SCTY","USD","NMS",17.60,N/A,"SCTY",100,"SCTY",16.70,N/A,"SCTY",500,"SCTY",16.77,"SCTY",35992,"SCTY","9/9/2016","4:00pm",17.32,17.44,16.73,17.49,4126443,4062750,58.87,16.31,23.27,24.04,"SCTY",100267000,"SCTY","SCTY",N/A,"SCTY",1.68B,"SCTY",70889000,"SCTY",N/A,N/A,N/A,N/A,-0.97,-9.92,-2.41,-8.79,N/A,0.02,1.96,3.25,8.88,537.69M,-546.92M,23.58,6.47 2 | -------------------------------------------------------------------------------- /src/test/resources/simpleQuoteRequest/C6L.SI.csv: -------------------------------------------------------------------------------- 1 | "SIA","C6L.SI","SGD","SES",10.89,N/A,"C6L.SI",N/A,"C6L.SI",10.88,N/A,"C6L.SI",N/A,"C6L.SI",10.88,"C6L.SI",300,"C6L.SI","8/10/2016","5:04pm",10.85,10.84,10.73,10.91,1542100,1939850,11.67,9.57,10.81,11.07,"C6L.SI",1185285000,"C6L.SI","C6L.SI",N/A,"C6L.SI",12.90B,"C6L.SI",523659000,"C6L.SI",N/A,"8/2/2016",N/A,N/A,0.82,N/A,0.00,N/A,13.19,0.00,0.95,0.85,11.44,15.15B,2.38B,N/A,0.00 2 | -------------------------------------------------------------------------------- /src/test/resources/simpleQuoteRequest/GOOG.csv: -------------------------------------------------------------------------------- 1 | "Alphabet Inc.","GOOG","USD","NMS",759.09,N/A,"GOOG",100,"GOOG",756.80,N/A,"GOOG",100,"GOOG",759.66,"GOOG",157851,"GOOG","9/9/2016","4:00pm",770.10,775.32,759.66,773.24,1885496,1465370,789.87,589.38,770.18,731.89,"GOOG",687273000,"GOOG","GOOG",N/A,"GOOG",522.09B,"GOOG",591660000,"GOOG",N/A,N/A,N/A,N/A,25.81,34.30,9.66,40.68,29.43,1.26,4.16,6.52,186.20,81.76B,26.90B,921.08,1.56 2 | -------------------------------------------------------------------------------- /src/test/resources/simpleQuoteRequest/TSLA.csv: -------------------------------------------------------------------------------- 1 | "Tesla Motors, Inc.","TSLA","USD","NMS",226.77,N/A,"TSLA",100,"TSLA",226.15,N/A,"TSLA",300,"TSLA",226.16,"TSLA",N/A,"TSLA","8/8/2016","4:00pm",228.00,230.03,226.09,229.60,2259465,4532790,271.57,141.05,219.29,214.79,"TSLA",139983000,"TSLA","TSLA",N/A,"TSLA",31.66B,"TSLA",109871000,"TSLA",N/A,N/A,N/A,N/A,-8.45,-0.24,0.74,2.10,N/A,-27.38,12.78,7.05,18.00,4.57B,-363.25M,240.08,5.23 2 | -------------------------------------------------------------------------------- /src/test/resources/simpleQuoteRequest/AIR.PA.csv: -------------------------------------------------------------------------------- 1 | "AIRBUS GROUP","AIR.PA","EUR","PAR",54.93,N/A,"AIR.PA",700,"AIR.PA",54.00,N/A,"AIR.PA",42000,"AIR.PA",50.34,"AIR.PA",813,"AIR.PA","8/8/2016","5:35pm",50.58,51.00,50.10,50.85,1460112,2211770,68.50,48.07,51.81,55.21,"AIR.PA",772397000,"AIR.PA","AIR.PA",N/A,"AIR.PA",38.88B,"AIR.PA",654166000,"AIR.PA",N/A,"5/2/2016",N/A,N/A,3.74,0.83,0.00,N/A,13.47,0.00,6.51,0.61,7.84,64.31B,4.80B,N/A,0.00 2 | -------------------------------------------------------------------------------- /src/test/resources/simpleQuoteRequest/INTC.csv: -------------------------------------------------------------------------------- 1 | "Intel Corporation","INTC","USD","NMS",35.03,N/A,"INTC",5000,"INTC",34.99,N/A,"INTC",200,"INTC",35.04,"INTC",1699919,"INTC","8/8/2016","4:00pm",34.86,34.98,34.86,35.17,19470707,22589700,35.93,24.87,33.78,31.54,"INTC",4731000000,"INTC","INTC",N/A,"INTC",165.77B,"INTC",4729060000,"INTC","6/1/2016","8/3/2016",1.04,2.97,2.06,2.50,0.74,2.73,16.99,2.80,2.70,2.92,12.98,56.61B,22.53B,37.61,2.94 2 | -------------------------------------------------------------------------------- /src/main/java/yahoofinance/histquotes/Interval.java: -------------------------------------------------------------------------------- 1 | 2 | package yahoofinance.histquotes; 3 | 4 | /** 5 | * 6 | * @author Stijn Strickx 7 | */ 8 | public enum Interval { 9 | 10 | DAILY("d"), 11 | WEEKLY("w"), 12 | MONTHLY("m"); 13 | 14 | private final String tag; 15 | 16 | Interval(String tag) { 17 | this.tag = tag; 18 | } 19 | 20 | public String getTag() { 21 | return this.tag; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/yahoofinance/histquotes2/QueryInterval.java: -------------------------------------------------------------------------------- 1 | 2 | package yahoofinance.histquotes2; 3 | 4 | /** 5 | * 6 | * @author Stijn Strickx 7 | */ 8 | public enum QueryInterval { 9 | 10 | DAILY("1d"), 11 | WEEKLY("5d"), 12 | MONTHLY("1mo"); 13 | 14 | private final String tag; 15 | 16 | QueryInterval(String tag) { 17 | this.tag = tag; 18 | } 19 | 20 | public String getTag() { 21 | return this.tag; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /.github/workflows/jacoco-badge.yml: -------------------------------------------------------------------------------- 1 | name: Jacoco Coverage 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | call-jacoco-badge: 11 | uses: sfuhrm/github_workflows/.github/workflows/jacoco-badge.yml@main 12 | with: 13 | java_version: 11 14 | artifact_name: coverage 15 | artifact_path: ./target/site/jacoco/jacoco.csv 16 | jacoco_csv_path: ./target/site/jacoco/jacoco.csv 17 | committer_email: sfuhrm@users.noreply.github.com 18 | committer_user: sfuhrm 19 | 20 | -------------------------------------------------------------------------------- /src/main/java/yahoofinance/histquotes2/IntervalMapper.java: -------------------------------------------------------------------------------- 1 | package yahoofinance.histquotes2; 2 | 3 | import yahoofinance.histquotes.Interval; 4 | 5 | /** 6 | * 7 | * @author Stijn Strickx 8 | */ 9 | public class IntervalMapper { 10 | 11 | public static QueryInterval get(Interval interval) { 12 | switch(interval) { 13 | case DAILY: return QueryInterval.DAILY; 14 | case WEEKLY: return QueryInterval.WEEKLY; 15 | case MONTHLY: return QueryInterval.MONTHLY; 16 | } 17 | return QueryInterval.MONTHLY; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/yahoofinance/histquotes2/CrumbManagerTest.java: -------------------------------------------------------------------------------- 1 | package yahoofinance.histquotes2; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertNotNull; 5 | 6 | import java.io.IOException; 7 | import org.junit.Ignore; 8 | import org.junit.Test; 9 | import yahoofinance.mock.MockedServersTest; 10 | 11 | public class CrumbManagerTest extends MockedServersTest { 12 | 13 | @Ignore("Requires internet, for manual testing") 14 | @Test 15 | public void testGetCrumb() throws IOException { 16 | assertNotNull("Crumb not set", CrumbManager.getCrumb()); 17 | assertEquals(11, CrumbManager.getCrumb().length()); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | 2 | name: Java CI with Maven 3 | 4 | on: 5 | push: 6 | branches: [ master, test ] 7 | pull_request: 8 | branches: [ master, test ] 9 | 10 | jobs: 11 | maven_version: 12 | uses: sfuhrm/github_workflows/.github/workflows/maven-expression.yml@main 13 | with: 14 | java_version: 17 15 | expression: project.version 16 | call-workflow-passing-data: 17 | needs: maven_version 18 | uses: sfuhrm/github_workflows/.github/workflows/maven.yml@main 19 | with: 20 | java_version: "[\"8\", \"11\", \"17\", \"19\" ]" 21 | artifact_name: build 22 | artifact_path: snmpman/target/snmpman-${{ needs.maven_version.outputs.expression_value }}.jar 23 | -------------------------------------------------------------------------------- /src/test/resources/historicalQuoteRequest/AIR.PA_1Y_M.csv: -------------------------------------------------------------------------------- 1 | Date,Open,High,Low,Close,Volume,Adj Close 2 | 2016-09-01,52.48,56.04,51.72,55.63,2332500,55.63 3 | 2016-08-01,52.90,53.42,49.505,52.29,1615200,52.29 4 | 2016-07-01,51.86,54.70,48.07,52.63,2262900,52.63 5 | 2016-06-01,56.00,56.38,49.51,51.73,2826600,51.73 6 | 2016-05-02,53.60,56.48,52.20,56.02,1976300,56.02 7 | 2016-04-01,57.15,59.33,54.43,54.61,2716400,53.31 8 | 2016-03-01,59.67,63.45,57.52,58.35,2329600,56.961 9 | 2016-02-01,57.82,59.88,49.96,59.78,3402200,58.357 10 | 2016-01-01,62.58,62.58,53.77,57.82,3163600,56.444 11 | 2015-12-01,68.29,68.50,61.02,62.00,2180200,60.524 12 | 2015-11-02,62.05,68.48,61.90,68.44,2207100,66.811 13 | 2015-10-01,53.99,64.50,53.14,63.36,2758300,61.852 14 | 2015-09-11,56.83,56.89,51.26,52.91,3082300,51.65 15 | -------------------------------------------------------------------------------- /src/main/java/yahoofinance/quotes/query1v7/FxQuotesQuery1V7Request.java: -------------------------------------------------------------------------------- 1 | package yahoofinance.quotes.query1v7; 2 | 3 | import com.fasterxml.jackson.databind.JsonNode; 4 | import yahoofinance.Utils; 5 | import yahoofinance.quotes.fx.FxQuote; 6 | 7 | import java.math.BigDecimal; 8 | 9 | /** 10 | * 11 | * @author Stijn Strickx 12 | */ 13 | public class FxQuotesQuery1V7Request extends QuotesRequest { 14 | 15 | public FxQuotesQuery1V7Request(String symbols) { 16 | super(symbols); 17 | } 18 | 19 | @Override 20 | protected FxQuote parseJson(JsonNode node) { 21 | String symbol = node.get("symbol").asText(); 22 | BigDecimal price = Utils.getBigDecimal(node.get("regularMarketPrice").asText()); 23 | 24 | return new FxQuote(symbol, price); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/resources/multiQuoteRequest/INTC_AIR.PA.csv: -------------------------------------------------------------------------------- 1 | "Intel Corporation","INTC","USD","NMS",35.44,N/A,"INTC",200,"INTC",35.00,N/A,"INTC",200,"INTC",35.44,"INTC",2689519,"INTC","9/9/2016","4:00pm",36.10,36.44,35.44,36.39,29457900,22272800,36.65,27.68,35.19,32.56,"INTC",4731000000,"INTC","INTC",N/A,"INTC",167.67B,"INTC",4729060000,"INTC","9/1/2016","8/3/2016",1.04,2.85,2.06,2.50,0.74,2.72,17.19,1.46,2.81,3.05,12.98,56.61B,22.53B,37.90,2.73 2 | "AIRBUS GROUP","AIR.PA","EUR","PAR",54.93,N/A,"AIR.PA",700,"AIR.PA",54.00,N/A,"AIR.PA",42000,"AIR.PA",55.63,"AIR.PA",34,"AIR.PA","9/9/2016","5:35pm",55.34,55.50,55.17,56.04,2329269,2117030,68.50,48.07,51.90,54.71,"AIR.PA",772697546000,"AIR.PA","AIR.PA",N/A,"AIR.PA",42985.17B,"AIR.PA",655394000,"AIR.PA",N/A,"5/2/2016",N/A,N/A,3.74,0.83,0.00,N/A,14.89,0.00,7.08,666.82,7.84,64.31B,4.80B,N/A,0.00 3 | -------------------------------------------------------------------------------- /src/test/resources/historicalQuoteRequest/INTC_1Y_M.csv: -------------------------------------------------------------------------------- 1 | Date,Open,High,Low,Close,Volume,Adj Close 2 | 2016-09-01,35.990002,36.650002,35.439999,35.439999,25628100,35.439999 3 | 2016-08-01,34.82,35.900002,33.990002,35.889999,17889300,35.889999 4 | 2016-07-01,32.639999,35.93,32.380001,34.860001,27435000,34.597744 5 | 2016-06-01,31.620001,33.00,30.440001,32.799999,23609900,32.553242 6 | 2016-05-02,30.450001,31.65,29.50,31.59,21645800,31.352345 7 | 2016-04-01,32.27,32.470001,30.10,30.280001,26299000,29.794836 8 | 2016-03-01,29.93,32.75,29.75,32.349998,22265300,31.831667 9 | 2016-02-01,30.719999,31.120001,27.68,29.59,27470500,29.115891 10 | 2016-01-04,33.880001,34.009998,29.209999,31.02,34409200,30.25667 11 | 2015-12-01,35.00,35.59,33.82,34.450001,20094800,33.602268 12 | 2015-11-02,33.73,35.290001,31.93,34.77,24082800,33.914391 13 | 2015-10-01,30.209999,35.029999,29.459999,33.860001,33086800,32.795761 14 | 2015-09-11,29.08,30.32,27.870001,30.139999,35115400,29.192682 15 | -------------------------------------------------------------------------------- /src/test/resources/historicalQuoteRequest/GOOG_1Y_M.csv: -------------------------------------------------------------------------------- 1 | Date,Open,High,Low,Close,Volume,Adj Close 2 | 2016-09-01,769.25,782.72998,759.659973,759.659973,1535300,759.659973 3 | 2016-08-01,761.090027,789.75,761.090027,767.049988,1191900,767.049988 4 | 2016-07-01,692.200012,778.549988,688.215027,768.789978,1687800,768.789978 5 | 2016-06-01,734.530029,737.210022,663.283997,692.099976,1861800,692.099976 6 | 2016-05-02,697.630005,739.72998,689.01001,735.719971,1767600,735.719971 7 | 2016-04-01,738.599976,769.900024,689.00,693.01001,2125700,693.01001 8 | 2016-03-01,703.619995,757.880005,685.340027,744.950012,1975500,744.950012 9 | 2016-02-01,750.460022,789.869995,663.059998,697.77002,3342400,697.77002 10 | 2016-01-04,743.00,752.00,673.26001,742.950012,2632600,742.950012 11 | 2015-12-01,747.109985,779.97998,724.169983,758.880005,2026100,758.880005 12 | 2015-11-02,711.059998,762.708008,705.849976,742.599976,1801600,742.599976 13 | 2015-10-01,608.369995,730.00,599.849976,710.809998,2333600,710.809998 14 | 2015-09-11,619.75,650.900024,589.380005,608.419983,2453900,608.419983 15 | -------------------------------------------------------------------------------- /.github/badges/jacoco.svg: -------------------------------------------------------------------------------- 1 | coverage55.6% -------------------------------------------------------------------------------- /src/main/java/yahoofinance/quotes/csv/FxQuotesRequest.java: -------------------------------------------------------------------------------- 1 | 2 | package yahoofinance.quotes.csv; 3 | 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import yahoofinance.Utils; 7 | import yahoofinance.YahooFinance; 8 | import yahoofinance.quotes.fx.FxQuote; 9 | 10 | /** 11 | * 12 | * @author Stijn Strickx 13 | */ 14 | public class FxQuotesRequest extends QuotesRequest { 15 | 16 | public static final List DEFAULT_PROPERTIES = new ArrayList<>(); 17 | static { 18 | DEFAULT_PROPERTIES.add(QuotesProperty.Symbol); 19 | DEFAULT_PROPERTIES.add(QuotesProperty.LastTradePriceOnly); 20 | } 21 | 22 | public FxQuotesRequest(String query) { 23 | super(query, FxQuotesRequest.DEFAULT_PROPERTIES); 24 | } 25 | 26 | @Override 27 | protected FxQuote parseCSVLine(String line) { 28 | String[] split = Utils.stripOverhead(line).split(YahooFinance.QUOTES_CSV_DELIMITER); 29 | if(split.length >= 2) { 30 | return new FxQuote(split[0], Utils.getBigDecimal(split[1])); 31 | } 32 | return null; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Stijn Strickx 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/test/resources/multiQuoteRequest/AIR.PA_INTC_C6L.SI.csv: -------------------------------------------------------------------------------- 1 | "AIRBUS GROUP","AIR.PA","EUR","PAR",54.93,N/A,"AIR.PA",700,"AIR.PA",54.00,N/A,"AIR.PA",42000,"AIR.PA",52.09,"AIR.PA",113,"AIR.PA","8/12/2016","2:03pm",51.84,51.84,51.72,52.19,399204,2194110,68.50,48.07,51.69,55.07,"AIR.PA",772397000,"AIR.PA","AIR.PA",N/A,"AIR.PA",40.23B,"AIR.PA",654166000,"AIR.PA",N/A,"5/2/2016",N/A,N/A,3.74,0.83,0.00,N/A,13.94,0.00,6.62,0.62,7.84,64.31B,4.80B,N/A,0.00 2 | "Intel Corporation","INTC","USD","NMS",34.73,N/A,"INTC",200,"INTC",34.60,N/A,"INTC",100,"INTC",34.68,"INTC",758534,"INTC","8/11/2016","4:00pm",34.66,34.53,34.49,34.74,16364120,22569500,35.93,24.87,34.08,31.69,"INTC",4731000000,"INTC","INTC",N/A,"INTC",164.07B,"INTC",4729060000,"INTC","6/1/2016","8/3/2016",1.04,2.97,2.06,2.50,0.74,2.73,16.82,2.76,2.66,2.89,12.98,56.61B,22.53B,37.61,2.94 3 | "SIA","C6L.SI","SGD","SES",10.77,N/A,"C6L.SI",N/A,"C6L.SI",10.74,N/A,"C6L.SI",N/A,"C6L.SI",10.77,"C6L.SI",700,"C6L.SI","8/12/2016","5:04pm",10.75,10.75,10.66,10.79,1195700,1942580,11.67,9.57,10.82,11.07,"C6L.SI",1185285000,"C6L.SI","C6L.SI",N/A,"C6L.SI",12.77B,"C6L.SI",523659000,"C6L.SI",N/A,"8/2/2016",N/A,N/A,0.82,N/A,0.00,N/A,13.05,0.00,0.94,0.84,11.44,15.15B,2.38B,N/A,0.00 4 | -------------------------------------------------------------------------------- /src/test/java/yahoofinance/mock/ResponseResource.java: -------------------------------------------------------------------------------- 1 | package yahoofinance.mock; 2 | 3 | import com.google.common.base.Charsets; 4 | import com.google.common.io.Resources; 5 | import okhttp3.mockwebserver.MockResponse; 6 | 7 | import java.io.IOException; 8 | import java.net.URL; 9 | 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | /** 14 | * 15 | * @author Stijn Strickx 16 | */ 17 | public class ResponseResource { 18 | 19 | private static final Logger log = LoggerFactory.getLogger(ResponseResource.class); 20 | 21 | private final int responseCode; 22 | private final String resource; 23 | 24 | public ResponseResource(String resource) { 25 | this(resource, 200); 26 | } 27 | 28 | public ResponseResource(String resource, int responseCode) { 29 | this.resource = resource; 30 | this.responseCode = responseCode; 31 | } 32 | 33 | public MockResponse get() { 34 | URL url = Resources.getResource(this.resource); 35 | try { 36 | String response = Resources.toString(url, Charsets.UTF_8); 37 | return new MockResponse().setBody(response).setResponseCode(this.responseCode); 38 | } catch (IOException e) { 39 | log.error("Unable to read response from resource", e); 40 | } 41 | return null; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/yahoofinance/mock/MockedServersTest.java: -------------------------------------------------------------------------------- 1 | package yahoofinance.mock; 2 | 3 | import okhttp3.mockwebserver.Dispatcher; 4 | import okhttp3.mockwebserver.MockWebServer; 5 | 6 | import org.junit.BeforeClass; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.io.IOException; 11 | 12 | /** 13 | * 14 | * @author Stijn Strickx 15 | */ 16 | public class MockedServersTest { 17 | 18 | private static final Logger log = LoggerFactory.getLogger(MockedServersTest.class); 19 | 20 | private static boolean started = false; 21 | 22 | public static MockWebServer quotesServer; 23 | public static MockWebServer histQuotesServer; 24 | 25 | @BeforeClass 26 | public static void startServers() { 27 | if(started) { 28 | return; 29 | } 30 | started = true; 31 | quotesServer = new MockWebServer(); 32 | histQuotesServer = new MockWebServer(); 33 | try { 34 | quotesServer.start(); 35 | histQuotesServer.start(); 36 | } catch (IOException e) { 37 | log.error("Unable to start mock web server", e); 38 | } 39 | String quotesBaseUrl = "http://localhost:" + quotesServer.getPort() + "/d/quotes.csv"; 40 | String histQuotesBaseUrl = "http://localhost:" + histQuotesServer.getPort() + "/table.csv"; 41 | 42 | System.setProperty("yahoofinance.baseurl.quotes", quotesBaseUrl); 43 | System.setProperty("yahoofinance.baseurl.histquotes", histQuotesBaseUrl); 44 | System.setProperty("yahoofinance.histquotes2.enabled", "false"); 45 | System.setProperty("yahoofinance.quotesquery1v7.enabled", "false"); 46 | 47 | final Dispatcher dispatcher = new YahooFinanceDispatcher(); 48 | quotesServer.setDispatcher(dispatcher); 49 | histQuotesServer.setDispatcher(dispatcher); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/yahoofinance/FxQuoteRequestTest.java: -------------------------------------------------------------------------------- 1 | package yahoofinance; 2 | 3 | import org.junit.Test; 4 | import yahoofinance.mock.MockedServersTest; 5 | import yahoofinance.quotes.fx.FxQuote; 6 | import yahoofinance.quotes.fx.FxSymbols; 7 | 8 | import java.io.IOException; 9 | import java.math.BigDecimal; 10 | import java.util.Map; 11 | 12 | import static org.junit.Assert.*; 13 | 14 | /** 15 | * 16 | * @author Stijn Strickx 17 | */ 18 | public class FxQuoteRequestTest extends MockedServersTest { 19 | 20 | @Test 21 | public void fxQuoteTest() throws IOException { 22 | FxQuote gbpeur = YahooFinance.getFx(FxSymbols.GBPEUR); 23 | assertEquals(new BigDecimal("1.1806"), gbpeur.getPrice()); 24 | assertEquals(FxSymbols.GBPEUR, gbpeur.getSymbol()); 25 | 26 | FxQuote usdeur = YahooFinance.getFx(FxSymbols.USDEUR); 27 | assertEquals(new BigDecimal("0.8898"), usdeur.getPrice()); 28 | 29 | Map fxQuotes = YahooFinance.getFx(new String[]{FxSymbols.EURUSD, FxSymbols.EURCHF}); 30 | FxQuote eurusd = fxQuotes.get(FxSymbols.EURUSD); 31 | FxQuote eurchf = fxQuotes.get(FxSymbols.EURCHF); 32 | assertEquals(new BigDecimal("1.1235"), eurusd.getPrice()); 33 | assertEquals(FxSymbols.EURUSD, eurusd.getSymbol()); 34 | assertEquals(new BigDecimal("1.0954"), eurchf.getPrice()); 35 | assertEquals(FxSymbols.EURCHF, eurchf.getSymbol()); 36 | } 37 | 38 | @Test 39 | public void fxFlowTest() throws IOException { 40 | int requestCount = MockedServersTest.quotesServer.getRequestCount(); 41 | FxQuote gbpeur = YahooFinance.getFx(FxSymbols.GBPEUR); 42 | requestCount += 1; 43 | assertEquals(requestCount, MockedServersTest.quotesServer.getRequestCount()); 44 | gbpeur.getPrice(); 45 | assertEquals(requestCount, MockedServersTest.quotesServer.getRequestCount()); 46 | gbpeur.getPrice(true); 47 | requestCount += 1; 48 | assertEquals(requestCount, MockedServersTest.quotesServer.getRequestCount()); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/yahoofinance/histquotes2/HistoricalDividend.java: -------------------------------------------------------------------------------- 1 | package yahoofinance.histquotes2; 2 | 3 | import java.math.BigDecimal; 4 | import java.text.SimpleDateFormat; 5 | import java.util.Calendar; 6 | 7 | /** 8 | * At the time of this writing Yahoo returns ADJUSTED dividends. Which means that as soon as 9 | * split occurs, all past dividends are divided by split factor. 10 | * All getters can return null in case the data is not available from Yahoo Finance. 11 | * 12 | * @author Randle McMurphy 13 | */ 14 | public class HistoricalDividend { 15 | 16 | private String symbol; 17 | 18 | private Calendar date; 19 | 20 | private BigDecimal adjDividend; 21 | 22 | public HistoricalDividend() {} 23 | 24 | public HistoricalDividend(String symbol, Calendar date, BigDecimal adjDividend) { 25 | this.symbol = symbol; 26 | this.date = date; 27 | this.adjDividend = adjDividend; 28 | } 29 | 30 | public String getSymbol() { 31 | return symbol; 32 | } 33 | 34 | public void setSymbol(String symbol) { 35 | this.symbol = symbol; 36 | } 37 | 38 | public Calendar getDate() { 39 | return date; 40 | } 41 | 42 | public void setDate(Calendar date) { 43 | this.date = date; 44 | } 45 | 46 | /** 47 | * At the time of this writing Yahoo returns ADJUSTED dividends. Which means that as soon as 48 | * split occurs, all past dividends are divided by split factor. 49 | * 50 | * @return an adjusted dividend cash 51 | */ 52 | public BigDecimal getAdjDividend() { 53 | return adjDividend; 54 | } 55 | 56 | public void setAdjDividend(BigDecimal adjDividend) { 57 | this.adjDividend = adjDividend; 58 | } 59 | 60 | @Override 61 | public String toString() { 62 | SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); 63 | String dateStr = dateFormat.format(this.date.getTime()); 64 | return "DIVIDEND: " + this.symbol + "@" + dateStr + ": " + this.adjDividend; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/yahoofinance/quotes/fx/FxQuote.java: -------------------------------------------------------------------------------- 1 | 2 | package yahoofinance.quotes.fx; 3 | 4 | import yahoofinance.quotes.csv.FxQuotesRequest; 5 | 6 | import java.io.IOException; 7 | import java.math.BigDecimal; 8 | 9 | /** 10 | * 11 | * @author Stijn Strickx 12 | */ 13 | public class FxQuote { 14 | 15 | private String symbol; 16 | private BigDecimal price; 17 | 18 | public FxQuote(String symbol) { 19 | this.symbol = symbol; 20 | this.price = BigDecimal.ZERO; 21 | } 22 | 23 | public FxQuote(String symbol, BigDecimal price) { 24 | this.symbol = symbol; 25 | this.price = price; 26 | } 27 | 28 | public String getSymbol() { 29 | return symbol; 30 | } 31 | 32 | public void setSymbol(String symbol) { 33 | this.symbol = symbol; 34 | } 35 | 36 | /** 37 | * Returns the requested FX rate. 38 | * 39 | * @return the requested FX rate 40 | */ 41 | public BigDecimal getPrice() { 42 | return price; 43 | } 44 | 45 | /** 46 | * Returns the requested FX rate. 47 | * This method will return 0 in the following situations: 48 | *
    49 | *
  • the data hasn't been loaded yet 50 | * in a previous request and refresh is set to false. 51 | *
  • refresh is true and the data cannot be retrieved from Yahoo Finance 52 | * for whatever reason (symbol not recognized, no network connection, ...) 53 | *
54 | * 55 | * @param refresh indicates whether the data should be requested again to Yahoo Finance 56 | * @return the requested FX rate 57 | * @throws java.io.IOException when there's a connection problem 58 | */ 59 | public BigDecimal getPrice(boolean refresh) throws IOException { 60 | if(refresh) { 61 | FxQuotesRequest request = new FxQuotesRequest(this.symbol); 62 | this.price = request.getSingleResult().getPrice(); 63 | } 64 | return price; 65 | } 66 | 67 | public void setPrice(BigDecimal price) { 68 | this.price = price; 69 | } 70 | 71 | @Override 72 | public String toString() { 73 | return this.symbol + ": " + this.price; 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/yahoofinance/histquotes2/HistoricalSplit.java: -------------------------------------------------------------------------------- 1 | package yahoofinance.histquotes2; 2 | 3 | import java.math.BigDecimal; 4 | import java.math.RoundingMode; 5 | import java.text.SimpleDateFormat; 6 | import java.util.Calendar; 7 | 8 | /** 9 | * All getters can return null in case the data is not available from Yahoo Finance. 10 | * 11 | * @author Randle McMurphy 12 | */ 13 | public class HistoricalSplit { 14 | 15 | private String symbol; 16 | 17 | private Calendar date; 18 | 19 | private BigDecimal numerator; 20 | private BigDecimal denominator; 21 | 22 | public HistoricalSplit() {} 23 | 24 | public HistoricalSplit(String symbol, Calendar date, BigDecimal numerator, BigDecimal denominator) { 25 | this.symbol = symbol; 26 | this.date = date; 27 | this.numerator = numerator; 28 | this.denominator = denominator; 29 | } 30 | 31 | public String getSymbol() { 32 | return symbol; 33 | } 34 | 35 | public void setSymbol(String symbol) { 36 | this.symbol = symbol; 37 | } 38 | 39 | public Calendar getDate() { 40 | return date; 41 | } 42 | 43 | public void setDate(Calendar date) { 44 | this.date = date; 45 | } 46 | 47 | public BigDecimal getNumerator() { 48 | return numerator; 49 | } 50 | 51 | public void setNumerator(BigDecimal numerator) { 52 | this.numerator = numerator; 53 | } 54 | 55 | public BigDecimal getDenominator() { 56 | return denominator; 57 | } 58 | 59 | public void setDenominator(BigDecimal denominator) { 60 | this.denominator = denominator; 61 | } 62 | 63 | /** 64 | * 65 | * @return a calculated split factor value which is equal to numerator divided by denominator 66 | */ 67 | public BigDecimal getSplitFactor() { 68 | return numerator.divide(denominator, 10, RoundingMode.HALF_UP); 69 | } 70 | 71 | @Override 72 | public String toString() { 73 | SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); 74 | String dateStr = dateFormat.format(this.date.getTime()); 75 | return "SPLIT: " + this.symbol + "@" + dateStr + ": " + this.numerator + " / " + this.denominator; 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/test/java/yahoofinance/mock/YahooFinanceDispatcher.java: -------------------------------------------------------------------------------- 1 | package yahoofinance.mock; 2 | 3 | import com.google.common.base.Charsets; 4 | import com.google.common.io.Resources; 5 | import okhttp3.mockwebserver.Dispatcher; 6 | import okhttp3.mockwebserver.MockResponse; 7 | import okhttp3.mockwebserver.RecordedRequest; 8 | 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.yaml.snakeyaml.Yaml; 12 | 13 | import java.io.IOException; 14 | import java.util.HashMap; 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | /** 19 | * 20 | * @author Stijn Strickx 21 | */ 22 | public class YahooFinanceDispatcher extends Dispatcher { 23 | 24 | private static final Logger log = LoggerFactory.getLogger(YahooFinanceDispatcher.class); 25 | 26 | private final Map pathToResponseResource; 27 | 28 | public YahooFinanceDispatcher() { 29 | this.pathToResponseResource = new HashMap<>(); 30 | this.loadRequests(); 31 | } 32 | 33 | @Override 34 | public MockResponse dispatch(RecordedRequest request) { 35 | if(this.pathToResponseResource.containsKey(request.getPath())) { 36 | return this.pathToResponseResource.get(request.getPath()).get(); 37 | } else { 38 | log.warn("Requested path not configured. Cannot provide MockResponse for " + request.getPath()); 39 | } 40 | return null; 41 | } 42 | 43 | private void loadRequests() { 44 | Yaml yaml = new Yaml(); 45 | Map>> requests; 46 | try { 47 | String requestsYaml = Resources.toString(Resources.getResource("requests.yml"), Charsets.UTF_8); 48 | requests = yaml.load(requestsYaml); 49 | } catch (IOException e) { 50 | log.warn("Unable to process requests.yml. No requests mocked.", e); 51 | return; 52 | } 53 | for(Map request : requests.get("requests")) { 54 | this.pathToResponseResource.put( 55 | (String) request.get("url"), 56 | new ResponseResource( 57 | (String) request.get("responseResource"), 58 | (Integer) request.get("responseCode") 59 | ) 60 | ); 61 | } 62 | } 63 | 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/yahoofinance/quotes/stock/StockDividend.java: -------------------------------------------------------------------------------- 1 | 2 | package yahoofinance.quotes.stock; 3 | 4 | import java.math.BigDecimal; 5 | import java.util.Calendar; 6 | 7 | /** 8 | * All getters can return null in case the data is not available from Yahoo Finance. 9 | * 10 | * @author Stijn Strickx 11 | */ 12 | public class StockDividend { 13 | 14 | private final String symbol; 15 | 16 | private Calendar payDate; 17 | private Calendar exDate; 18 | private BigDecimal annualYield; 19 | private BigDecimal annualYieldPercent; 20 | 21 | public StockDividend(String symbol) { 22 | this.symbol = symbol; 23 | } 24 | 25 | public StockDividend(String symbol, Calendar payDate, Calendar exDate, BigDecimal annualYield, BigDecimal annualYieldPercent) { 26 | this(symbol); 27 | this.payDate = payDate; 28 | this.exDate = exDate; 29 | this.annualYield = annualYield; 30 | this.annualYieldPercent = annualYieldPercent; 31 | } 32 | 33 | public String getSymbol() { 34 | return symbol; 35 | } 36 | 37 | public Calendar getPayDate() { 38 | return payDate; 39 | } 40 | 41 | public void setPayDate(Calendar payDate) { 42 | this.payDate = payDate; 43 | } 44 | 45 | public Calendar getExDate() { 46 | return exDate; 47 | } 48 | 49 | public void setExDate(Calendar exDate) { 50 | this.exDate = exDate; 51 | } 52 | 53 | public BigDecimal getAnnualYield() { 54 | return annualYield; 55 | } 56 | 57 | public void setAnnualYield(BigDecimal annualYield) { 58 | this.annualYield = annualYield; 59 | } 60 | 61 | public BigDecimal getAnnualYieldPercent() { 62 | return annualYieldPercent; 63 | } 64 | 65 | public void setAnnualYieldPercent(BigDecimal annualYieldPercent) { 66 | this.annualYieldPercent = annualYieldPercent; 67 | } 68 | 69 | @Override 70 | public String toString() { 71 | String payDateStr = "/"; 72 | String exDateStr = "/"; 73 | String annualYieldStr = "/"; 74 | if(this.payDate != null) { 75 | payDateStr = this.payDate.getTime().toString(); 76 | } 77 | if(this.exDate != null) { 78 | exDateStr = this.exDate.getTime().toString(); 79 | } 80 | if(this.annualYieldPercent != null) { 81 | annualYieldStr = this.annualYieldPercent.toString() + "%"; 82 | } 83 | return "Pay date: " + payDateStr + ", Ex date: " + exDateStr + ", Annual yield: " + annualYieldStr; 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/test/java/yahoofinance/QuoteRequestFlowTest.java: -------------------------------------------------------------------------------- 1 | package yahoofinance; 2 | 3 | import org.junit.Test; 4 | import yahoofinance.mock.MockedServersTest; 5 | 6 | import java.io.IOException; 7 | import java.math.BigDecimal; 8 | import java.util.Calendar; 9 | import java.util.Map; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * 15 | * @author Stijn Strickx 16 | */ 17 | public class QuoteRequestFlowTest extends MockedServersTest { 18 | 19 | @Test 20 | public void quoteRefreshTest() throws IOException { 21 | Stock stock = YahooFinance.get("TSLA"); 22 | 23 | assertEquals(new BigDecimal("226.16"), stock.getQuote().getPrice()); 24 | 25 | stock.getQuote().setPrice(new BigDecimal("276.34")); 26 | assertEquals(new BigDecimal("276.34"), stock.getQuote().getPrice()); 27 | stock.getQuote(false); 28 | assertEquals(new BigDecimal("276.34"), stock.getQuote().getPrice()); 29 | 30 | stock.getQuote(true); 31 | assertEquals(new BigDecimal("226.16"), stock.getQuote().getPrice()); 32 | } 33 | 34 | @Test 35 | public void statsRefreshTest() throws IOException { 36 | Stock stock = YahooFinance.get("AIR.PA"); 37 | 38 | assertEquals(new BigDecimal("13.47"), stock.getStats().getPe()); 39 | 40 | stock.getStats().setPe(new BigDecimal("10.81")); 41 | assertEquals(new BigDecimal("10.81"), stock.getStats().getPe()); 42 | stock.getStats(false); 43 | assertEquals(new BigDecimal("10.81"), stock.getStats().getPe()); 44 | 45 | stock.getStats(true); 46 | assertEquals(new BigDecimal("13.47"), stock.getStats().getPe()); 47 | } 48 | 49 | @Test 50 | public void dividendRefreshTest() throws IOException { 51 | Stock stock = YahooFinance.get("INTC"); 52 | 53 | assertEquals(new BigDecimal("1.04"), stock.getDividend().getAnnualYield()); 54 | 55 | stock.getDividend().setAnnualYield(new BigDecimal("1.32")); 56 | assertEquals(new BigDecimal("1.32"), stock.getDividend().getAnnualYield()); 57 | stock.getDividend(false); 58 | assertEquals(new BigDecimal("1.32"), stock.getDividend().getAnnualYield()); 59 | 60 | stock.getDividend(true); 61 | assertEquals(new BigDecimal("1.04"), stock.getDividend().getAnnualYield()); 62 | } 63 | 64 | @Test 65 | public void multipleQuoteRequestTest() throws IOException { 66 | Map stocks = YahooFinance.get(new String[]{"AIR.PA", "INTC", "C6L.SI"}); 67 | assertTrue(stocks.containsKey("AIR.PA")); 68 | assertTrue(stocks.containsKey("INTC")); 69 | assertTrue(stocks.containsKey("C6L.SI")); 70 | 71 | Stock airbus = stocks.get("AIR.PA"); 72 | Stock intel = stocks.get("INTC"); 73 | Stock sia = stocks.get("C6L.SI"); 74 | 75 | assertEquals(new BigDecimal("54.00"), airbus.getQuote().getBid()); 76 | assertEquals(new BigDecimal("37.61"), intel.getStats().getOneYearTargetPrice()); 77 | assertEquals(7, sia.getDividend().getExDate().get(Calendar.MONTH)); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/test/resources/historicalQuoteRequest/SCTY_1Y_W.csv: -------------------------------------------------------------------------------- 1 | Date,Open,High,Low,Close,Volume,Adj Close 2 | 2016-09-06,18.75,19.040001,16.73,16.77,4394100,16.77 3 | 2016-08-29,22.120001,22.200001,18.309999,18.48,4227800,18.48 4 | 2016-08-22,23.35,23.49,22.01,22.049999,1684700,22.049999 5 | 2016-08-15,23.85,24.43,23.200001,23.50,1445100,23.50 6 | 2016-08-08,24.50,24.85,23.469999,23.700001,2212800,23.700001 7 | 2016-08-01,25.33,25.98,23.200001,24.790001,3792900,24.790001 8 | 2016-07-25,27.07,27.60,26.40,26.700001,2741300,26.700001 9 | 2016-07-18,24.91,27.48,24.85,26.450001,3880900,26.450001 10 | 2016-07-11,24.16,25.059999,24.10,25.040001,3127800,25.040001 11 | 2016-07-05,23.77,25.15,23.25,23.799999,3738500,23.799999 12 | 2016-06-27,21.76,24.74,21.219999,23.709999,6706600,23.709999 13 | 2016-06-20,21.98,23.799999,20.50,22.200001,11037500,22.200001 14 | 2016-06-13,21.00,22.190001,20.379999,21.32,3332800,21.32 15 | 2016-06-06,21.60,24.77,21.01,21.23,6825200,21.23 16 | 2016-05-31,22.299999,23.00,21.15,21.639999,3111400,21.639999 17 | 2016-05-23,21.950001,25.200001,21.860001,22.26,6196600,22.26 18 | 2016-05-16,20.200001,22.00,19.40,21.940001,5057200,21.940001 19 | 2016-05-09,22.00,22.639999,16.50,19.60,13437300,19.60 20 | 2016-05-02,30.049999,30.25,20.809999,21.84,9068500,21.84 21 | 2016-04-25,33.00,34.439999,29.40,30.32,4978800,30.32 22 | 2016-04-18,28.50,35.23,28.35,33.34,5885000,33.34 23 | 2016-04-11,28.950001,29.67,28.10,29.040001,3297800,29.040001 24 | 2016-04-04,24.50,29.43,24.50,27.99,4482500,27.99 25 | 2016-03-28,22.219999,25.74,21.01,24.280001,3194200,24.280001 26 | 2016-03-21,26.91,27.40,21.25,22.219999,4371600,22.219999 27 | 2016-03-14,25.60,28.290001,25.110001,27.059999,3724300,27.059999 28 | 2016-03-07,22.639999,26.90,22.629999,25.690001,5954200,25.690001 29 | 2016-02-29,19.00,25.00,17.719999,22.389999,7281900,22.389999 30 | 2016-02-22,19.549999,19.780001,17.27,18.799999,4575000,18.799999 31 | 2016-02-16,18.67,22.75,17.959999,18.82,8395300,18.82 32 | 2016-02-08,29.10,30.40,16.309999,17.379999,13422500,17.379999 33 | 2016-02-01,35.200001,35.849998,28.40,29.57,3537100,29.57 34 | 2016-01-25,32.060001,38.400002,30.049999,35.650002,3665000,35.650002 35 | 2016-01-19,35.27,35.509998,27.50,32.040001,5096200,32.040001 36 | 2016-01-11,48.869999,49.389999,33.889999,35.200001,3901500,35.200001 37 | 2016-01-04,49.290001,53.610001,46.200001,48.810001,2405300,48.810001 38 | 2015-12-28,51.34,52.50,48.389999,51.02,2432000,51.02 39 | 2015-12-21,57.400002,58.50,50.299999,51.98,4008900,51.98 40 | 2015-12-14,37.099998,58.869999,37.049999,56.91,9575800,56.91 41 | 2015-12-07,35.849998,37.799999,31.299999,37.040001,3095800,37.040001 42 | 2015-11-30,29.98,37.950001,28.35,35.990002,4973400,35.990002 43 | 2015-11-23,29.00,31.57,27.68,29.870001,2772200,29.870001 44 | 2015-11-16,26.27,29.68,25.33,29.040001,5164600,29.040001 45 | 2015-11-09,29.129999,29.40,24.07,25.85,4444900,25.85 46 | 2015-11-02,29.959999,31.889999,28.120001,29.049999,6146200,29.049999 47 | 2015-10-26,39.27,39.549999,28.110001,29.65,7898400,29.65 48 | 2015-10-19,41.689999,43.389999,37.759998,39.240002,3234200,39.240002 49 | 2015-10-12,48.950001,49.00,43.900002,44.16,1687900,44.16 50 | 2015-10-05,46.560001,49.380001,45.91,48.939999,2493800,48.939999 51 | 2015-09-28,40.990002,46.490002,39.599998,46.380001,1695300,46.380001 52 | 2015-09-21,48.240002,48.880001,40.900002,41.330002,2297400,41.330002 53 | 2015-09-14,47.990002,49.98,47.02,48.09,1415700,48.09 54 | 2015-09-11,47.549999,48.950001,47.450001,48.139999,821300,48.139999 55 | -------------------------------------------------------------------------------- /src/main/java/yahoofinance/quotes/query1v7/QuotesRequest.java: -------------------------------------------------------------------------------- 1 | package yahoofinance.quotes.query1v7; 2 | 3 | import com.fasterxml.jackson.databind.JsonNode; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import yahoofinance.Utils; 8 | import yahoofinance.YahooFinance; 9 | import yahoofinance.histquotes2.CrumbManager; 10 | import yahoofinance.util.RedirectableRequest; 11 | 12 | import java.io.IOException; 13 | import java.io.InputStreamReader; 14 | import java.net.URL; 15 | import java.net.URLConnection; 16 | import java.util.ArrayList; 17 | import java.util.LinkedHashMap; 18 | import java.util.List; 19 | import java.util.Map; 20 | 21 | /** 22 | * 23 | * @author Stijn Strickx 24 | * @param Type of object that can contain the retrieved information from a 25 | * quotes request 26 | */ 27 | public abstract class QuotesRequest { 28 | 29 | private static final Logger log = LoggerFactory.getLogger(QuotesRequest.class); 30 | private static final ObjectMapper objectMapper = new ObjectMapper(); 31 | 32 | protected final String symbols; 33 | 34 | public QuotesRequest(String symbols) { 35 | this.symbols = symbols; 36 | } 37 | 38 | public String getSymbols() { 39 | return symbols; 40 | } 41 | 42 | protected abstract T parseJson(JsonNode node); 43 | 44 | public T getSingleResult() throws IOException { 45 | List results = this.getResult(); 46 | if (results.size() > 0) { 47 | return results.get(0); 48 | } 49 | return null; 50 | } 51 | 52 | /** 53 | * Sends the request to Yahoo Finance and parses the result 54 | * 55 | * @return List of parsed objects resulting from the Yahoo Finance request 56 | * @throws IOException when there's a connection problem or the request is incorrect 57 | */ 58 | public List getResult() throws IOException { 59 | List result = new ArrayList<>(); 60 | 61 | Map params = new LinkedHashMap<>(); 62 | params.put("symbols", this.symbols); 63 | 64 | String url = YahooFinance.QUOTES_QUERY1V7_BASE_URL + "?" + Utils.getURLParameters(params); 65 | if (!CrumbManager.getCrumb().isEmpty()) { 66 | url = url + "&crumb=" + CrumbManager.getCrumb(); 67 | } 68 | 69 | // Get JSON from Yahoo 70 | log.info("Sending request: {}", url); 71 | 72 | URL request = new URL(url); 73 | RedirectableRequest redirectableRequest = new RedirectableRequest(request, 5); 74 | redirectableRequest.setConnectTimeout(YahooFinance.CONNECTION_TIMEOUT); 75 | redirectableRequest.setReadTimeout(YahooFinance.CONNECTION_TIMEOUT); 76 | URLConnection connection = redirectableRequest.openConnection(); 77 | 78 | try (InputStreamReader is = new InputStreamReader(connection.getInputStream())) { 79 | JsonNode node = objectMapper.readTree(is); 80 | if (node.has("quoteResponse") && node.get("quoteResponse").has("result")) { 81 | node = node.get("quoteResponse").get("result"); 82 | for (int i = 0; i < node.size(); i++) { 83 | result.add(this.parseJson(node.get(i))); 84 | } 85 | } else { 86 | throw new IOException("Invalid response"); 87 | } 88 | } 89 | 90 | return result; 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/test/resources/requests.yml: -------------------------------------------------------------------------------- 1 | 2 | requests: 3 | - url: /d/quotes.csv?s=AIR.PA&f=nsc4xab2sa5sbb3sb6sl1sk3sd1t1opghva2kjm3m4sj2sss1sj1sf6sr1qdyee7e9e8rr5p6p5b4s6j4t8s7&e=.csv 4 | responseCode: 200 5 | responseResource: simpleQuoteRequest/AIR.PA.csv 6 | 7 | - url: /d/quotes.csv?s=TSLA&f=nsc4xab2sa5sbb3sb6sl1sk3sd1t1opghva2kjm3m4sj2sss1sj1sf6sr1qdyee7e9e8rr5p6p5b4s6j4t8s7&e=.csv 8 | responseCode: 200 9 | responseResource: simpleQuoteRequest/TSLA.csv 10 | 11 | - url: /d/quotes.csv?s=INTC&f=nsc4xab2sa5sbb3sb6sl1sk3sd1t1opghva2kjm3m4sj2sss1sj1sf6sr1qdyee7e9e8rr5p6p5b4s6j4t8s7&e=.csv 12 | responseCode: 200 13 | responseResource: simpleQuoteRequest/INTC.csv 14 | 15 | - url: /d/quotes.csv?s=C6L.SI&f=nsc4xab2sa5sbb3sb6sl1sk3sd1t1opghva2kjm3m4sj2sss1sj1sf6sr1qdyee7e9e8rr5p6p5b4s6j4t8s7&e=.csv 16 | responseCode: 200 17 | responseResource: simpleQuoteRequest/C6L.SI.csv 18 | 19 | - url: /d/quotes.csv?s=AIR.PA%2CINTC%2CC6L.SI&f=nsc4xab2sa5sbb3sb6sl1sk3sd1t1opghva2kjm3m4sj2sss1sj1sf6sr1qdyee7e9e8rr5p6p5b4s6j4t8s7&e=.csv 20 | responseCode: 200 21 | responseResource: multiQuoteRequest/AIR.PA_INTC_C6L.SI.csv 22 | 23 | - url: /d/quotes.csv?s=GOOG&f=nsc4xab2sa5sbb3sb6sl1sk3sd1t1opghva2kjm3m4sj2sss1sj1sf6sr1qdyee7e9e8rr5p6p5b4s6j4t8s7&e=.csv 24 | responseCode: 200 25 | responseResource: simpleQuoteRequest/GOOG.csv 26 | 27 | - url: /d/quotes.csv?s=SCTY&f=nsc4xab2sa5sbb3sb6sl1sk3sd1t1opghva2kjm3m4sj2sss1sj1sf6sr1qdyee7e9e8rr5p6p5b4s6j4t8s7&e=.csv 28 | responseCode: 200 29 | responseResource: simpleQuoteRequest/SCTY.csv 30 | 31 | - url: /d/quotes.csv?s=INTC%2CAIR.PA&f=nsc4xab2sa5sbb3sb6sl1sk3sd1t1opghva2kjm3m4sj2sss1sj1sf6sr1qdyee7e9e8rr5p6p5b4s6j4t8s7&e=.csv 32 | responseCode: 200 33 | responseResource: multiQuoteRequest/INTC_AIR.PA.csv 34 | 35 | # 36 | # FX QUOTE REQUESTS 37 | # 38 | - url: /d/quotes.csv?s=GBPEUR%3DX&f=sl1&e=.csv 39 | responseCode: 200 40 | responseResource: fxQuoteRequest/GBPEUR.csv 41 | 42 | - url: /d/quotes.csv?s=USDEUR%3DX&f=sl1&e=.csv 43 | responseCode: 200 44 | responseResource: fxQuoteRequest/USDEUR.csv 45 | 46 | - url: /d/quotes.csv?s=EURUSD%3DX%2CEURCHF%3DX&f=sl1&e=.csv 47 | responseCode: 200 48 | responseResource: fxQuoteRequest/EURUSD_EURCHF.csv 49 | 50 | # 51 | # HISTORICAL QUOTE REQUESTS 52 | # 53 | 54 | - url: /table.csv?s=GOOG&a=8&b=11&c=2015&d=8&e=11&f=2016&g=m&ignore=.csv 55 | responseCode: 200 56 | responseResource: historicalQuoteRequest/GOOG_1Y_M.csv 57 | 58 | - url: /table.csv?s=TSLA&a=8&b=11&c=2015&d=8&e=11&f=2016&g=d&ignore=.csv 59 | responseCode: 200 60 | responseResource: historicalQuoteRequest/TSLA_1Y_D.csv 61 | 62 | - url: /table.csv?s=SCTY&a=8&b=11&c=2015&d=8&e=11&f=2016&g=w&ignore=.csv 63 | responseCode: 200 64 | responseResource: historicalQuoteRequest/SCTY_1Y_W.csv 65 | 66 | - url: /table.csv?s=GOOG&a=8&b=11&c=2011&d=8&e=11&f=2016&g=w&ignore=.csv 67 | responseCode: 200 68 | responseResource: historicalQuoteRequest/GOOG_5Y_W.csv 69 | 70 | - url: /table.csv?s=AIR.PA&a=8&b=11&c=2015&d=8&e=11&f=2016&g=m&ignore=.csv 71 | responseCode: 200 72 | responseResource: historicalQuoteRequest/AIR.PA_1Y_M.csv 73 | 74 | - url: /table.csv?s=INTC&a=8&b=11&c=2015&d=8&e=11&f=2016&g=m&ignore=.csv 75 | responseCode: 200 76 | responseResource: historicalQuoteRequest/INTC_1Y_M.csv 77 | 78 | -------------------------------------------------------------------------------- /src/main/java/yahoofinance/histquotes/HistoricalQuote.java: -------------------------------------------------------------------------------- 1 | 2 | package yahoofinance.histquotes; 3 | 4 | import java.math.BigDecimal; 5 | import java.text.SimpleDateFormat; 6 | import java.util.Calendar; 7 | 8 | /** 9 | * All getters can return null in case the data is not available from Yahoo Finance. 10 | * 11 | * @author Stijn Strickx 12 | */ 13 | public class HistoricalQuote { 14 | 15 | private String symbol; 16 | 17 | private Calendar date; 18 | 19 | private BigDecimal open; 20 | private BigDecimal low; 21 | private BigDecimal high; 22 | private BigDecimal close; 23 | 24 | private BigDecimal adjClose; 25 | 26 | private Long volume; 27 | 28 | public HistoricalQuote() {} 29 | 30 | public HistoricalQuote(String symbol, Calendar date, BigDecimal open, BigDecimal low, BigDecimal high, BigDecimal close, BigDecimal adjClose, Long volume) { 31 | this.symbol = symbol; 32 | this.date = date; 33 | this.open = open; 34 | this.low = low; 35 | this.high = high; 36 | this.close = close; 37 | this.adjClose = adjClose; 38 | this.volume = volume; 39 | } 40 | 41 | public String getSymbol() { 42 | return symbol; 43 | } 44 | 45 | public void setSymbol(String symbol) { 46 | this.symbol = symbol; 47 | } 48 | 49 | public Calendar getDate() { 50 | return date; 51 | } 52 | 53 | public void setDate(Calendar date) { 54 | this.date = date; 55 | } 56 | 57 | public BigDecimal getOpen() { 58 | return open; 59 | } 60 | 61 | public void setOpen(BigDecimal open) { 62 | this.open = open; 63 | } 64 | 65 | /** 66 | * 67 | * @return the intra-day low 68 | */ 69 | public BigDecimal getLow() { 70 | return low; 71 | } 72 | 73 | public void setLow(BigDecimal low) { 74 | this.low = low; 75 | } 76 | 77 | /** 78 | * 79 | * @return the intra-day high 80 | */ 81 | public BigDecimal getHigh() { 82 | return high; 83 | } 84 | 85 | public void setHigh(BigDecimal high) { 86 | this.high = high; 87 | } 88 | 89 | public BigDecimal getClose() { 90 | return close; 91 | } 92 | 93 | public void setClose(BigDecimal close) { 94 | this.close = close; 95 | } 96 | 97 | /** 98 | * The adjusted closing price on a specific date 99 | * reflects all of the dividends and splits since that day. 100 | * The adjusted closing price from a date in history can be used to 101 | * calculate a close estimate of the total return, including dividends, 102 | * that an investor earned if shares were purchased on that date. 103 | * @return the adjusted close price 104 | */ 105 | public BigDecimal getAdjClose() { 106 | return adjClose; 107 | } 108 | 109 | public void setAdjClose(BigDecimal adjClose) { 110 | this.adjClose = adjClose; 111 | } 112 | 113 | public Long getVolume() { 114 | return volume; 115 | } 116 | 117 | public void setVolume(Long volume) { 118 | this.volume = volume; 119 | } 120 | 121 | @Override 122 | public String toString() { 123 | SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); 124 | String dateStr = dateFormat.format(this.date.getTime()); 125 | return this.symbol + "@" + dateStr + ": " + this.low + "-" + this.high + ", " + 126 | this.open + "->" + this.close + " (" + this.adjClose + ")"; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/yahoofinance/util/RedirectableRequest.java: -------------------------------------------------------------------------------- 1 | package yahoofinance.util; 2 | 3 | import java.io.IOException; 4 | import java.net.*; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | /** 9 | * 10 | * @author Stijn Strickx 11 | */ 12 | public class RedirectableRequest { 13 | 14 | private URL request; 15 | private int protocolRedirectLimit; 16 | 17 | private int connectTimeout = 10000; 18 | private int readTimeout = 10000; 19 | 20 | static { 21 | CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL)); 22 | } 23 | 24 | public RedirectableRequest(URL request) { 25 | this(request, 2); 26 | } 27 | 28 | public RedirectableRequest(URL request, int protocolRedirectLimit) { 29 | this.request = request; 30 | this.protocolRedirectLimit = protocolRedirectLimit; 31 | } 32 | 33 | public URLConnection openConnection() throws IOException { 34 | return openConnection(new HashMap<>()); 35 | } 36 | 37 | public URLConnection openConnection(Map requestProperties) throws IOException { 38 | int redirectCount = 0; 39 | boolean hasResponse = false; 40 | HttpURLConnection connection = null; 41 | URL currentRequest = this.request; 42 | while(!hasResponse && (redirectCount <= this.protocolRedirectLimit)) { 43 | connection = (HttpURLConnection) currentRequest.openConnection(); 44 | connection.setConnectTimeout(this.connectTimeout); 45 | connection.setReadTimeout(this.readTimeout); 46 | 47 | for(String requestProperty : requestProperties.keySet()) { 48 | connection.addRequestProperty(requestProperty, requestProperties.get(requestProperty)); 49 | } 50 | 51 | // only handle protocol redirects manually... 52 | connection.setInstanceFollowRedirects(true); 53 | 54 | switch (connection.getResponseCode()) { 55 | case HttpURLConnection.HTTP_MOVED_PERM: 56 | case HttpURLConnection.HTTP_MOVED_TEMP: 57 | redirectCount++; 58 | String location = connection.getHeaderField("Location"); 59 | currentRequest = new URL(request, location); 60 | break; 61 | default: 62 | hasResponse = true; 63 | } 64 | } 65 | 66 | if(redirectCount > this.protocolRedirectLimit) { 67 | throw new IOException("Protocol redirect count exceeded for url: " + this.request.toExternalForm()); 68 | } else if(connection == null) { 69 | throw new IOException("Unexpected error while opening connection"); 70 | } else { 71 | return connection; 72 | } 73 | } 74 | 75 | public URL getRequest() { 76 | return request; 77 | } 78 | 79 | public void setRequest(URL request) { 80 | this.request = request; 81 | } 82 | 83 | public int getProtocolRedirectLimit() { 84 | return protocolRedirectLimit; 85 | } 86 | 87 | public void setProtocolRedirectLimit(int protocolRedirectLimit) { 88 | if(protocolRedirectLimit >= 0) { 89 | this.protocolRedirectLimit = protocolRedirectLimit; 90 | } 91 | } 92 | 93 | public int getConnectTimeout() { 94 | return connectTimeout; 95 | } 96 | 97 | public void setConnectTimeout(int connectTimeout) { 98 | this.connectTimeout = connectTimeout; 99 | } 100 | 101 | public int getReadTimeout() { 102 | return readTimeout; 103 | } 104 | 105 | public void setReadTimeout(int readTimeout) { 106 | this.readTimeout = readTimeout; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/yahoofinance/quotes/csv/QuotesRequest.java: -------------------------------------------------------------------------------- 1 | package yahoofinance.quotes.csv; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStreamReader; 6 | import java.net.URL; 7 | import java.net.URLConnection; 8 | import java.util.ArrayList; 9 | import java.util.LinkedHashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | 16 | import yahoofinance.Utils; 17 | import yahoofinance.YahooFinance; 18 | import yahoofinance.util.RedirectableRequest; 19 | 20 | /** 21 | * 22 | * @author Stijn Strickx 23 | * @param Type of object that can contain the retrieved information from a 24 | * quotes request 25 | */ 26 | public abstract class QuotesRequest { 27 | 28 | private static final Logger log = LoggerFactory.getLogger(QuotesRequest.class); 29 | 30 | protected final String query; 31 | protected List properties; 32 | 33 | public QuotesRequest(String query, List properties) { 34 | this.query = query; 35 | this.properties = properties; 36 | } 37 | 38 | public String getQuery() { 39 | return query; 40 | } 41 | 42 | public List getProperties() { 43 | return properties; 44 | } 45 | 46 | public void setProperties(List properties) { 47 | this.properties = properties; 48 | } 49 | 50 | protected abstract T parseCSVLine(String line); 51 | 52 | private String getFieldsString() { 53 | StringBuilder result = new StringBuilder(); 54 | for (QuotesProperty property : this.properties) { 55 | result.append(property.getTag()); 56 | } 57 | return result.toString(); 58 | } 59 | 60 | public T getSingleResult() throws IOException { 61 | List results = this.getResult(); 62 | if (results.size() > 0) { 63 | return results.get(0); 64 | } 65 | return null; 66 | } 67 | 68 | /** 69 | * Sends the request to Yahoo Finance and parses the result 70 | * 71 | * @return List of parsed objects resulting from the Yahoo Finance request 72 | * @throws java.io.IOException when there's a connection problem or the request is incorrect 73 | */ 74 | public List getResult() throws IOException { 75 | List result = new ArrayList<>(); 76 | 77 | Map params = new LinkedHashMap<>(); 78 | params.put("s", this.query); 79 | params.put("f", this.getFieldsString()); 80 | params.put("e", ".csv"); 81 | 82 | String url = YahooFinance.QUOTES_BASE_URL + "?" + Utils.getURLParameters(params); 83 | 84 | // Get CSV from Yahoo 85 | log.info("Sending request: " + url); 86 | 87 | URL request = new URL(url); 88 | RedirectableRequest redirectableRequest = new RedirectableRequest(request, 5); 89 | redirectableRequest.setConnectTimeout(YahooFinance.CONNECTION_TIMEOUT); 90 | redirectableRequest.setReadTimeout(YahooFinance.CONNECTION_TIMEOUT); 91 | URLConnection connection = redirectableRequest.openConnection(); 92 | 93 | try ( InputStreamReader is = new InputStreamReader(connection.getInputStream()); 94 | BufferedReader br = new BufferedReader(is)) { 95 | 96 | // Parse CSV 97 | for (String line = br.readLine(); line != null; line = br.readLine()) { 98 | if (line.equals("Missing Symbols List.")) { 99 | log.error("The requested symbol was not recognized by Yahoo Finance"); 100 | } else { 101 | log.info("Parsing CSV line: " + Utils.unescape(line)); 102 | 103 | T data = this.parseCSVLine(line); 104 | result.add(data); 105 | } 106 | } 107 | } 108 | 109 | return result; 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/yahoofinance/quotes/csv/QuotesProperty.java: -------------------------------------------------------------------------------- 1 | package yahoofinance.quotes.csv; 2 | 3 | /** 4 | * 5 | * @author Stijn Strickx 6 | */ 7 | public enum QuotesProperty { 8 | 9 | 10 | 11 | AfterHoursChangeRealtime("c8"), // After Hours Change (Realtime) 12 | AnnualizedGain("g3"), // Annualized Gain 13 | Ask("a"), // Ask 14 | AskRealtime("b2"), // Ask (Realtime) 15 | AskSize("a5"), // Ask Size 16 | AverageDailyVolume("a2"), // Average Daily Volume 17 | Bid("b"), // Bid 18 | BidRealtime("b3"), // Bid (Realtime) 19 | BidSize("b6"), // Bid Size 20 | BookValuePerShare("b4"), // Book Value Per Share 21 | Change("c1"), // Change 22 | Change_ChangeInPercent("c"), // Change Change In Percent 23 | ChangeFromFiftydayMovingAverage("m7"), // Change From Fiftyday Moving Average 24 | ChangeFromTwoHundreddayMovingAverage("m5"), // Change From Two Hundredday Moving Average 25 | ChangeFromYearHigh("k4"), // Change From Year High 26 | ChangeFromYearLow("j5"), // Change From Year Low 27 | ChangeInPercent("p2"), // Change In Percent 28 | ChangeInPercentRealtime("k2"), // Change In Percent (Realtime) 29 | ChangeRealtime("c6"), // Change (Realtime) 30 | Commission("c3"), // Commission 31 | Currency("c4"), // Currency 32 | DaysHigh("h"), // Days High 33 | DaysLow("g"), // Days Low 34 | DaysRange("m"), // Days Range 35 | DaysRangeRealtime("m2"), // Days Range (Realtime) 36 | DaysValueChange("w1"), // Days Value Change 37 | DaysValueChangeRealtime("w4"), // Days Value Change (Realtime) 38 | DividendPayDate("r1"), // Dividend Pay Date 39 | TrailingAnnualDividendYield("d"), // Trailing Annual Dividend Yield 40 | TrailingAnnualDividendYieldInPercent("y"), // Trailing Annual Dividend Yield In Percent 41 | DilutedEPS("e"), // Diluted E P S 42 | EBITDA("j4"), // E B I T D A 43 | EPSEstimateCurrentYear("e7"), // E P S Estimate Current Year 44 | EPSEstimateNextQuarter("e9"), // E P S Estimate Next Quarter 45 | EPSEstimateNextYear("e8"), // E P S Estimate Next Year 46 | ExDividendDate("q"), // Ex Dividend Date 47 | FiftydayMovingAverage("m3"), // Fiftyday Moving Average 48 | SharesFloat("f6"), // Shares Float 49 | HighLimit("l2"), // High Limit 50 | HoldingsGain("g4"), // Holdings Gain 51 | HoldingsGainPercent("g1"), // Holdings Gain Percent 52 | HoldingsGainPercentRealtime("g5"), // Holdings Gain Percent (Realtime) 53 | HoldingsGainRealtime("g6"), // Holdings Gain (Realtime) 54 | HoldingsValue("v1"), // Holdings Value 55 | HoldingsValueRealtime("v7"), // Holdings Value (Realtime) 56 | LastTradeDate("d1"), // Last Trade Date 57 | LastTradePriceOnly("l1"), // Last Trade Price Only 58 | LastTradeRealtimeWithTime("k1"), // Last Trade (Realtime) With Time 59 | LastTradeSize("k3"), // Last Trade Size 60 | LastTradeTime("t1"), // Last Trade Time 61 | LastTradeWithTime("l"), // Last Trade With Time 62 | LowLimit("l3"), // Low Limit 63 | MarketCapitalization("j1"), // Market Capitalization 64 | MarketCapRealtime("j3"), // Market Cap (Realtime) 65 | MoreInfo("i"), // More Info 66 | Name("n"), // Name 67 | Notes("n4"), // Notes 68 | OneyrTargetPrice("t8"), // Oneyr Target Price 69 | Open("o"), // Open 70 | OrderBookRealtime("i5"), // Order Book (Realtime) 71 | PEGRatio("r5"), // P E G Ratio 72 | PERatio("r"), // P E Ratio 73 | PERatioRealtime("r2"), // P E Ratio (Realtime) 74 | PercentChangeFromFiftydayMovingAverage("m8"), // Percent Change From Fiftyday Moving Average 75 | PercentChangeFromTwoHundreddayMovingAverage("m6"), // Percent Change From Two Hundredday Moving Average 76 | ChangeInPercentFromYearHigh("k5"), // Change In Percent From Year High 77 | PercentChangeFromYearLow("j6"), // Percent Change From Year Low 78 | PreviousClose("p"), // Previous Close 79 | PriceBook("p6"), // Price Book 80 | PriceEPSEstimateCurrentYear("r6"), // Price E P S Estimate Current Year 81 | PriceEPSEstimateNextYear("r7"), // Price E P S Estimate Next Year 82 | PricePaid("p1"), // Price Paid 83 | PriceSales("p5"), // Price Sales 84 | Revenue("s6"), // Revenue 85 | SharesOwned("s1"), // Shares Owned 86 | SharesOutstanding("j2"), // Shares Outstanding 87 | ShortRatio("s7"), // Short Ratio 88 | StockExchange("x"), // Stock Exchange 89 | Symbol("s"), // Symbol 90 | TickerTrend("t7"), // Ticker Trend 91 | TradeDate("d2"), // Trade Date 92 | TradeLinks("t6"), // Trade Links 93 | TradeLinksAdditional("f"), // Trade Links Additional 94 | TwoHundreddayMovingAverage("m4"), // Two Hundredday Moving Average 95 | Volume("v"), // Volume 96 | YearHigh("k"), // Year High 97 | YearLow("j"), // Year Low 98 | YearRange("w"); // Year Range 99 | 100 | private final String tag; 101 | 102 | 103 | QuotesProperty(String tag) { 104 | this.tag = tag; 105 | } 106 | 107 | public String getTag() { 108 | return this.tag; 109 | } 110 | } 111 | 112 | -------------------------------------------------------------------------------- /src/main/java/yahoofinance/histquotes2/HistDividendsRequest.java: -------------------------------------------------------------------------------- 1 | package yahoofinance.histquotes2; 2 | 3 | import yahoofinance.Utils; 4 | import yahoofinance.YahooFinance; 5 | import yahoofinance.util.RedirectableRequest; 6 | 7 | import java.io.BufferedReader; 8 | import java.io.IOException; 9 | import java.io.InputStreamReader; 10 | import java.net.URL; 11 | import java.net.URLConnection; 12 | import java.net.URLEncoder; 13 | import java.util.*; 14 | 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | 18 | /** 19 | * 20 | * @author Stijn Strickx (modified by Randle McMurphy) 21 | */ 22 | public class HistDividendsRequest { 23 | 24 | 25 | private static final Logger log = LoggerFactory.getLogger(HistDividendsRequest.class); 26 | private final String symbol; 27 | 28 | private final Calendar from; 29 | private final Calendar to; 30 | 31 | public static final Calendar DEFAULT_FROM = Calendar.getInstance(); 32 | 33 | static { 34 | DEFAULT_FROM.add(Calendar.YEAR, -1); 35 | } 36 | public static final Calendar DEFAULT_TO = Calendar.getInstance(); 37 | 38 | // Interval has no meaning here and is not used here 39 | // But it's better to leave it because Yahoo's standard query URL still contains it 40 | public static final QueryInterval DEFAULT_INTERVAL = QueryInterval.DAILY; 41 | 42 | public HistDividendsRequest(String symbol) { 43 | this(symbol, DEFAULT_FROM, DEFAULT_TO); 44 | } 45 | 46 | public HistDividendsRequest(String symbol, Calendar from, Calendar to) { 47 | this.symbol = symbol; 48 | this.from = this.cleanHistCalendar(from); 49 | this.to = this.cleanHistCalendar(to); 50 | } 51 | 52 | public HistDividendsRequest(String symbol, Date from, Date to) { 53 | this(symbol); 54 | this.from.setTime(from); 55 | this.to.setTime(to); 56 | this.cleanHistCalendar(this.from); 57 | this.cleanHistCalendar(this.to); 58 | } 59 | 60 | /** 61 | * Put everything smaller than days at 0 62 | * @param cal calendar to be cleaned 63 | */ 64 | private Calendar cleanHistCalendar(Calendar cal) { 65 | cal.set(Calendar.MILLISECOND, 0); 66 | cal.set(Calendar.SECOND, 0); 67 | cal.set(Calendar.MINUTE, 0); 68 | cal.set(Calendar.HOUR, 0); 69 | return cal; 70 | } 71 | 72 | public List getResult() throws IOException { 73 | 74 | List result = new ArrayList<>(); 75 | 76 | if(this.from.after(this.to)) { 77 | log.warn("Unable to retrieve historical dividends. " 78 | + "From-date should not be after to-date. From: " 79 | + this.from.getTime() + ", to: " + this.to.getTime()); 80 | return result; 81 | } 82 | 83 | Map params = new LinkedHashMap<>(); 84 | params.put("period1", String.valueOf(this.from.getTimeInMillis() / 1000)); 85 | params.put("period2", String.valueOf(this.to.getTimeInMillis() / 1000)); 86 | 87 | // Interval has no meaning here and is not used here 88 | // But it's better to leave it because Yahoo's standard query URL still contains it 89 | params.put("interval", DEFAULT_INTERVAL.getTag()); 90 | 91 | // This will instruct Yahoo to return dividends 92 | params.put("events", "div"); 93 | 94 | params.put("crumb", CrumbManager.getCrumb()); 95 | 96 | String url = YahooFinance.HISTQUOTES2_BASE_URL + URLEncoder.encode(this.symbol , "UTF-8") + "?" + Utils.getURLParameters(params); 97 | 98 | // Get CSV from Yahoo 99 | log.info("Sending request: " + url); 100 | 101 | URL request = new URL(url); 102 | RedirectableRequest redirectableRequest = new RedirectableRequest(request, 5); 103 | redirectableRequest.setConnectTimeout(YahooFinance.CONNECTION_TIMEOUT); 104 | redirectableRequest.setReadTimeout(YahooFinance.CONNECTION_TIMEOUT); 105 | Map requestProperties = new HashMap<>(); 106 | requestProperties.put("Cookie", CrumbManager.getCookie()); 107 | URLConnection connection = redirectableRequest.openConnection(requestProperties); 108 | 109 | try ( InputStreamReader is = new InputStreamReader(connection.getInputStream()); 110 | BufferedReader br = new BufferedReader(is)) { 111 | br.readLine(); // skip the first line 112 | // Parse CSV 113 | for (String line = br.readLine(); line != null; line = br.readLine()) { 114 | 115 | log.info("Parsing CSV line: " + Utils.unescape(line)); 116 | HistoricalDividend dividend = this.parseCSVLine(line); 117 | result.add(dividend); 118 | } 119 | } 120 | return result; 121 | } 122 | 123 | private HistoricalDividend parseCSVLine(String line) { 124 | String[] data = line.split(YahooFinance.QUOTES_CSV_DELIMITER); 125 | return new HistoricalDividend(this.symbol, 126 | Utils.parseHistDate(data[0]), 127 | Utils.getBigDecimal(data[1]) 128 | ); 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/yahoofinance/quotes/fx/FxSymbols.java: -------------------------------------------------------------------------------- 1 | package yahoofinance.quotes.fx; 2 | 3 | /** 4 | * 5 | * @author Stijn Strickx 6 | */ 7 | public class FxSymbols { 8 | 9 | public static final String USDGBP = "USDGBP=X"; 10 | public static final String USDEUR = "USDEUR=X"; 11 | public static final String USDAUD = "USDAUD=X"; 12 | public static final String USDCHF = "USDCHF=X"; 13 | public static final String USDJPY = "USDJPY=X"; 14 | public static final String USDCAD = "USDCAD=X"; 15 | public static final String USDSGD = "USDSGD=X"; 16 | public static final String USDNZD = "USDNZD=X"; 17 | public static final String USDHKD = "USDHKD=X"; 18 | 19 | public static final String GBPUSD = "GBPUSD=X"; 20 | public static final String GBPEUR = "GBPEUR=X"; 21 | public static final String GBPAUD = "GBPAUD=X"; 22 | public static final String GBPCHF = "GBPCHF=X"; 23 | public static final String GBPJPY = "GBPJPY=X"; 24 | public static final String GBPCAD = "GBPCAD=X"; 25 | public static final String GBPSGD = "GBPSGD=X"; 26 | public static final String GBPNZD = "GBPNZD=X"; 27 | public static final String GBPHKD = "GBPHKD=X"; 28 | 29 | public static final String EURUSD = "EURUSD=X"; 30 | public static final String EURGBP = "EURGBP=X"; 31 | public static final String EURAUD = "EURAUD=X"; 32 | public static final String EURCHF = "EURCHF=X"; 33 | public static final String EURJPY = "EURJPY=X"; 34 | public static final String EURCAD = "EURCAD=X"; 35 | public static final String EURSGD = "EURSGD=X"; 36 | public static final String EURNZD = "EURNZD=X"; 37 | public static final String EURHKD = "EURHKD=X"; 38 | 39 | public static final String AUDUSD = "AUDUSD=X"; 40 | public static final String AUDGBP = "AUDGBP=X"; 41 | public static final String AUDEUR = "AUDEUR=X"; 42 | public static final String AUDCHF = "AUDCHF=X"; 43 | public static final String AUDJPY = "AUDJPY=X"; 44 | public static final String AUDCAD = "AUDCAD=X"; 45 | public static final String AUDSGD = "AUDSGD=X"; 46 | public static final String AUDNZD = "AUDNZD=X"; 47 | public static final String AUDHKD = "AUDHKD=X"; 48 | 49 | public static final String CHFGBP = "CHFGBP=X"; 50 | public static final String CHFEUR = "CHFEUR=X"; 51 | public static final String CHFAUD = "CHFAUD=X"; 52 | public static final String CHFJPY = "CHFJPY=X"; 53 | public static final String CHFCAD = "CHFCAD=X"; 54 | public static final String CHFSGD = "CHFSGD=X"; 55 | public static final String CHFNZD = "CHFNZD=X"; 56 | public static final String CHFHKD = "CHFHKD=X"; 57 | 58 | public static final String JPYUSD = "JPYUSD=X"; 59 | public static final String JPYGBP = "JPYGBP=X"; 60 | public static final String JPYEUR = "JPYEUR=X"; 61 | public static final String JPYAUD = "JPYAUD=X"; 62 | public static final String JPYCHF = "JPYCHF=X"; 63 | public static final String JPYCAD = "JPYCAD=X"; 64 | public static final String JPYSGD = "JPYSGD=X"; 65 | public static final String JPYNZD = "JPYNZD=X"; 66 | public static final String JPYHKD = "JPYHKD=X"; 67 | 68 | public static final String CADUSD = "CADUSD=X"; 69 | public static final String CADGBP = "CADGBP=X"; 70 | public static final String CADEUR = "CADEUR=X"; 71 | public static final String CADAUD = "CADAUD=X"; 72 | public static final String CADCHF = "CADCHF=X"; 73 | public static final String CADJPY = "CADJPY=X"; 74 | public static final String CADSGD = "CADSGD=X"; 75 | public static final String CADNZD = "CADNZD=X"; 76 | public static final String CADHKD = "CADHKD=X"; 77 | 78 | public static final String SGDUSD = "SGDUSD=X"; 79 | public static final String SGDGBP = "SGDGBP=X"; 80 | public static final String SGDEUR = "SGDEUR=X"; 81 | public static final String SGDAUD = "SGDAUD=X"; 82 | public static final String SGDCHF = "SGDCHF=X"; 83 | public static final String SGDJPY = "SGDJPY=X"; 84 | public static final String SGDCAD = "SGDCAD=X"; 85 | public static final String SGDNZD = "SGDNZD=X"; 86 | public static final String SGDHKD = "SGDHKD=X"; 87 | 88 | public static final String NZDUSD = "NZDUSD=X"; 89 | public static final String NZDGBP = "NZDGBP=X"; 90 | public static final String NZDEUR = "NZDEUR=X"; 91 | public static final String NZDAUD = "NZDAUD=X"; 92 | public static final String NZDCHF = "NZDCHF=X"; 93 | public static final String NZDJPY = "NZDJPY=X"; 94 | public static final String NZDCAD = "NZDCAD=X"; 95 | public static final String NZDSGD = "NZDSGD=X"; 96 | public static final String NZDHKD = "NZDHKD=X"; 97 | 98 | public static final String HKDUSD = "HKDUSD=X"; 99 | public static final String HKDGBP = "HKDGBP=X"; 100 | public static final String HKDEUR = "HKDEUR=X"; 101 | public static final String HKDAUD = "HKDAUD=X"; 102 | public static final String HKDCHF = "HKDCHF=X"; 103 | public static final String HKDJPY = "HKDJPY=X"; 104 | public static final String HKDCAD = "HKDCAD=X"; 105 | public static final String HKDSGD = "HKDSGD=X"; 106 | public static final String HKDNZD = "HKDNZD=X"; 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/yahoofinance/histquotes2/HistSplitsRequest.java: -------------------------------------------------------------------------------- 1 | package yahoofinance.histquotes2; 2 | 3 | import yahoofinance.Utils; 4 | import yahoofinance.YahooFinance; 5 | import yahoofinance.util.RedirectableRequest; 6 | 7 | import java.io.BufferedReader; 8 | import java.io.IOException; 9 | import java.io.InputStreamReader; 10 | import java.net.URL; 11 | import java.net.URLConnection; 12 | import java.net.URLEncoder; 13 | import java.util.*; 14 | 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | 18 | /** 19 | * 20 | * @author Stijn Strickx (modified by Randle McMurphy) 21 | */ 22 | public class HistSplitsRequest { 23 | 24 | 25 | private static final Logger log = LoggerFactory.getLogger(HistSplitsRequest.class); 26 | private final String symbol; 27 | 28 | private final Calendar from; 29 | private final Calendar to; 30 | 31 | public static final Calendar DEFAULT_FROM = Calendar.getInstance(); 32 | 33 | static { 34 | DEFAULT_FROM.add(Calendar.YEAR, -1); 35 | } 36 | public static final Calendar DEFAULT_TO = Calendar.getInstance(); 37 | 38 | // Interval has no meaning here and is not used here 39 | // But it's better to leave it because Yahoo's standard query URL still contains it 40 | public static final QueryInterval DEFAULT_INTERVAL = QueryInterval.DAILY; 41 | 42 | public HistSplitsRequest(String symbol) { 43 | this(symbol, DEFAULT_FROM, DEFAULT_TO); 44 | } 45 | 46 | public HistSplitsRequest(String symbol, Calendar from, Calendar to) { 47 | this.symbol = symbol; 48 | this.from = this.cleanHistCalendar(from); 49 | this.to = this.cleanHistCalendar(to); 50 | } 51 | 52 | public HistSplitsRequest(String symbol, Date from, Date to) { 53 | this(symbol); 54 | this.from.setTime(from); 55 | this.to.setTime(to); 56 | this.cleanHistCalendar(this.from); 57 | this.cleanHistCalendar(this.to); 58 | } 59 | 60 | /** 61 | * Put everything smaller than days at 0 62 | * @param cal calendar to be cleaned 63 | */ 64 | private Calendar cleanHistCalendar(Calendar cal) { 65 | cal.set(Calendar.MILLISECOND, 0); 66 | cal.set(Calendar.SECOND, 0); 67 | cal.set(Calendar.MINUTE, 0); 68 | cal.set(Calendar.HOUR, 0); 69 | return cal; 70 | } 71 | 72 | public List getResult() throws IOException { 73 | 74 | List result = new ArrayList<>(); 75 | 76 | if(this.from.after(this.to)) { 77 | log.warn("Unable to retrieve historical splits. " 78 | + "From-date should not be after to-date. From: " 79 | + this.from.getTime() + ", to: " + this.to.getTime()); 80 | return result; 81 | } 82 | 83 | Map params = new LinkedHashMap<>(); 84 | params.put("period1", String.valueOf(this.from.getTimeInMillis() / 1000)); 85 | params.put("period2", String.valueOf(this.to.getTimeInMillis() / 1000)); 86 | 87 | // Interval has no meaning here and is not used here 88 | // But it's better to leave it because Yahoo's standard query URL still contains it 89 | params.put("interval", DEFAULT_INTERVAL.getTag()); 90 | 91 | // This will instruct Yahoo to return splits 92 | params.put("events", "split"); 93 | 94 | params.put("crumb", CrumbManager.getCrumb()); 95 | 96 | String url = YahooFinance.HISTQUOTES2_BASE_URL + URLEncoder.encode(this.symbol , "UTF-8") + "?" + Utils.getURLParameters(params); 97 | 98 | // Get CSV from Yahoo 99 | log.info("Sending request: " + url); 100 | 101 | URL request = new URL(url); 102 | RedirectableRequest redirectableRequest = new RedirectableRequest(request, 5); 103 | redirectableRequest.setConnectTimeout(YahooFinance.CONNECTION_TIMEOUT); 104 | redirectableRequest.setReadTimeout(YahooFinance.CONNECTION_TIMEOUT); 105 | Map requestProperties = new HashMap<>(); 106 | requestProperties.put("Cookie", CrumbManager.getCookie()); 107 | URLConnection connection = redirectableRequest.openConnection(requestProperties); 108 | 109 | try ( InputStreamReader is = new InputStreamReader(connection.getInputStream()); 110 | BufferedReader br = new BufferedReader(is)) { 111 | br.readLine(); // skip the first line 112 | // Parse CSV 113 | for (String line = br.readLine(); line != null; line = br.readLine()) { 114 | 115 | log.info("Parsing CSV line: " + Utils.unescape(line)); 116 | HistoricalSplit split = this.parseCSVLine(line); 117 | result.add(split); 118 | } 119 | } 120 | return result; 121 | } 122 | 123 | private HistoricalSplit parseCSVLine(String line) { 124 | String[] data = line.split(YahooFinance.QUOTES_CSV_DELIMITER); 125 | String[] parts = data[1].split("/"); 126 | return new HistoricalSplit(this.symbol, 127 | Utils.parseHistDate(data[0]), 128 | Utils.getBigDecimal(parts[0]), 129 | Utils.getBigDecimal(parts[1]) 130 | ); 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/yahoofinance/histquotes/HistQuotesRequest.java: -------------------------------------------------------------------------------- 1 | package yahoofinance.histquotes; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStreamReader; 6 | import java.net.URL; 7 | import java.net.URLConnection; 8 | import java.util.ArrayList; 9 | import java.util.Calendar; 10 | import java.util.Date; 11 | import java.util.LinkedHashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | 18 | import yahoofinance.Utils; 19 | import yahoofinance.YahooFinance; 20 | import yahoofinance.util.RedirectableRequest; 21 | 22 | /** 23 | * 24 | * @author Stijn Strickx 25 | */ 26 | public class HistQuotesRequest { 27 | 28 | private static final Logger log = LoggerFactory.getLogger(HistQuotesRequest.class); 29 | private final String symbol; 30 | 31 | private final Calendar from; 32 | private final Calendar to; 33 | 34 | private final Interval interval; 35 | 36 | public static final Calendar DEFAULT_FROM = Calendar.getInstance(); 37 | 38 | static { 39 | DEFAULT_FROM.add(Calendar.YEAR, -1); 40 | } 41 | public static final Calendar DEFAULT_TO = Calendar.getInstance(); 42 | public static final Interval DEFAULT_INTERVAL = Interval.MONTHLY; 43 | 44 | public HistQuotesRequest(String symbol) { 45 | this(symbol, DEFAULT_INTERVAL); 46 | } 47 | 48 | public HistQuotesRequest(String symbol, Interval interval) { 49 | this(symbol, DEFAULT_FROM, DEFAULT_TO, interval); 50 | } 51 | 52 | public HistQuotesRequest(String symbol, Calendar from, Calendar to) { 53 | this(symbol, from, to, DEFAULT_INTERVAL); 54 | } 55 | 56 | public HistQuotesRequest(String symbol, Calendar from, Calendar to, Interval interval) { 57 | this.symbol = symbol; 58 | this.from = this.cleanHistCalendar(from); 59 | this.to = this.cleanHistCalendar(to); 60 | this.interval = interval; 61 | } 62 | 63 | public HistQuotesRequest(String symbol, Date from, Date to) { 64 | this(symbol, from, to, DEFAULT_INTERVAL); 65 | } 66 | 67 | public HistQuotesRequest(String symbol, Date from, Date to, Interval interval) { 68 | this(symbol, interval); 69 | this.from.setTime(from); 70 | this.to.setTime(to); 71 | this.cleanHistCalendar(this.from); 72 | this.cleanHistCalendar(this.to); 73 | } 74 | 75 | /** 76 | * Put everything smaller than days at 0 77 | * @param cal calendar to be cleaned 78 | */ 79 | private Calendar cleanHistCalendar(Calendar cal) { 80 | cal.set(Calendar.MILLISECOND, 0); 81 | cal.set(Calendar.SECOND, 0); 82 | cal.set(Calendar.MINUTE, 0); 83 | cal.set(Calendar.HOUR, 0); 84 | return cal; 85 | } 86 | 87 | public List getResult() throws IOException { 88 | 89 | List result = new ArrayList<>(); 90 | 91 | if(this.from.after(this.to)) { 92 | log.warn("Unable to retrieve historical quotes. " 93 | + "From-date should not be after to-date. From: " 94 | + this.from.getTime() + ", to: " + this.to.getTime()); 95 | return result; 96 | } 97 | 98 | Map params = new LinkedHashMap<>(); 99 | params.put("s", this.symbol); 100 | 101 | params.put("a", String.valueOf(this.from.get(Calendar.MONTH))); 102 | params.put("b", String.valueOf(this.from.get(Calendar.DAY_OF_MONTH))); 103 | params.put("c", String.valueOf(this.from.get(Calendar.YEAR))); 104 | 105 | params.put("d", String.valueOf(this.to.get(Calendar.MONTH))); 106 | params.put("e", String.valueOf(this.to.get(Calendar.DAY_OF_MONTH))); 107 | params.put("f", String.valueOf(this.to.get(Calendar.YEAR))); 108 | 109 | params.put("g", this.interval.getTag()); 110 | 111 | params.put("ignore", ".csv"); 112 | 113 | String url = YahooFinance.HISTQUOTES_BASE_URL + "?" + Utils.getURLParameters(params); 114 | 115 | // Get CSV from Yahoo 116 | log.info("Sending request: " + url); 117 | 118 | URL request = new URL(url); 119 | RedirectableRequest redirectableRequest = new RedirectableRequest(request, 5); 120 | redirectableRequest.setConnectTimeout(YahooFinance.CONNECTION_TIMEOUT); 121 | redirectableRequest.setReadTimeout(YahooFinance.CONNECTION_TIMEOUT); 122 | URLConnection connection = redirectableRequest.openConnection(); 123 | 124 | try (InputStreamReader is = new InputStreamReader(connection.getInputStream()); 125 | BufferedReader br = new BufferedReader(is)) { 126 | br.readLine(); // skip the first line 127 | // Parse CSV 128 | for (String line = br.readLine(); line != null; line = br.readLine()) { 129 | 130 | log.info("Parsing CSV line: " + Utils.unescape(line)); 131 | HistoricalQuote quote = this.parseCSVLine(line); 132 | result.add(quote); 133 | } 134 | } 135 | return result; 136 | } 137 | 138 | private HistoricalQuote parseCSVLine(String line) { 139 | String[] data = line.split(YahooFinance.QUOTES_CSV_DELIMITER); 140 | return new HistoricalQuote(this.symbol, 141 | Utils.parseHistDate(data[0]), 142 | Utils.getBigDecimal(data[1]), 143 | Utils.getBigDecimal(data[3]), 144 | Utils.getBigDecimal(data[2]), 145 | Utils.getBigDecimal(data[4]), 146 | Utils.getBigDecimal(data[6]), 147 | Utils.getLong(data[5]) 148 | ); 149 | } 150 | 151 | } 152 | -------------------------------------------------------------------------------- /src/main/java/yahoofinance/quotes/stock/StockStats.java: -------------------------------------------------------------------------------- 1 | 2 | package yahoofinance.quotes.stock; 3 | 4 | import java.math.BigDecimal; 5 | import java.util.Calendar; 6 | 7 | import yahoofinance.Utils; 8 | 9 | /** 10 | * All getters can return null in case the data is not available from Yahoo Finance. 11 | * 12 | * @author Stijn Strickx 13 | */ 14 | public class StockStats { 15 | 16 | private final String symbol; 17 | 18 | private BigDecimal marketCap; 19 | private Long sharesFloat; 20 | private Long sharesOutstanding; 21 | private Long sharesOwned; 22 | 23 | private BigDecimal eps; 24 | private BigDecimal pe; 25 | private BigDecimal peg; 26 | 27 | private BigDecimal epsEstimateCurrentYear; 28 | private BigDecimal epsEstimateNextQuarter; 29 | private BigDecimal epsEstimateNextYear; 30 | 31 | private BigDecimal priceBook; 32 | private BigDecimal priceSales; 33 | private BigDecimal bookValuePerShare; 34 | 35 | private BigDecimal revenue; // ttm 36 | private BigDecimal EBITDA; // ttm 37 | private BigDecimal oneYearTargetPrice; 38 | 39 | private BigDecimal shortRatio; 40 | 41 | private Calendar earningsAnnouncement; 42 | 43 | public StockStats(String symbol) { 44 | this.symbol = symbol; 45 | } 46 | 47 | public BigDecimal getROE() { 48 | return Utils.getPercent(this.EBITDA, this.marketCap); 49 | } 50 | 51 | public String getSymbol() { 52 | return symbol; 53 | } 54 | 55 | public BigDecimal getMarketCap() { 56 | return marketCap; 57 | } 58 | 59 | public void setMarketCap(BigDecimal marketCap) { 60 | this.marketCap = marketCap; 61 | } 62 | 63 | public Long getSharesFloat() { 64 | return sharesFloat; 65 | } 66 | 67 | public void setSharesFloat(Long sharesFloat) { 68 | this.sharesFloat = sharesFloat; 69 | } 70 | 71 | public Long getSharesOutstanding() { 72 | return sharesOutstanding; 73 | } 74 | 75 | public void setSharesOutstanding(Long sharesOutstanding) { 76 | this.sharesOutstanding = sharesOutstanding; 77 | } 78 | 79 | public Long getSharesOwned() { 80 | return sharesOwned; 81 | } 82 | 83 | public void setSharesOwned(Long sharesOwned) { 84 | this.sharesOwned = sharesOwned; 85 | } 86 | 87 | public BigDecimal getEps() { 88 | return eps; 89 | } 90 | 91 | public void setEps(BigDecimal eps) { 92 | this.eps = eps; 93 | } 94 | 95 | public BigDecimal getPe() { 96 | return pe; 97 | } 98 | 99 | public void setPe(BigDecimal pe) { 100 | this.pe = pe; 101 | } 102 | 103 | public BigDecimal getPeg() { 104 | return peg; 105 | } 106 | 107 | public void setPeg(BigDecimal peg) { 108 | this.peg = peg; 109 | } 110 | 111 | public BigDecimal getEpsEstimateCurrentYear() { 112 | return epsEstimateCurrentYear; 113 | } 114 | 115 | public void setEpsEstimateCurrentYear(BigDecimal epsEstimateCurrentYear) { 116 | this.epsEstimateCurrentYear = epsEstimateCurrentYear; 117 | } 118 | 119 | public BigDecimal getEpsEstimateNextQuarter() { 120 | return epsEstimateNextQuarter; 121 | } 122 | 123 | public void setEpsEstimateNextQuarter(BigDecimal epsEstimateNextQuarter) { 124 | this.epsEstimateNextQuarter = epsEstimateNextQuarter; 125 | } 126 | 127 | public BigDecimal getEpsEstimateNextYear() { 128 | return epsEstimateNextYear; 129 | } 130 | 131 | public void setEpsEstimateNextYear(BigDecimal epsEstimateNextYear) { 132 | this.epsEstimateNextYear = epsEstimateNextYear; 133 | } 134 | 135 | public BigDecimal getPriceBook() { 136 | return priceBook; 137 | } 138 | 139 | public void setPriceBook(BigDecimal priceBook) { 140 | this.priceBook = priceBook; 141 | } 142 | 143 | public BigDecimal getPriceSales() { 144 | return priceSales; 145 | } 146 | 147 | public void setPriceSales(BigDecimal priceSales) { 148 | this.priceSales = priceSales; 149 | } 150 | 151 | public BigDecimal getBookValuePerShare() { 152 | return bookValuePerShare; 153 | } 154 | 155 | public void setBookValuePerShare(BigDecimal bookValuePerShare) { 156 | this.bookValuePerShare = bookValuePerShare; 157 | } 158 | 159 | public BigDecimal getRevenue() { 160 | return revenue; 161 | } 162 | 163 | public void setRevenue(BigDecimal revenue) { 164 | this.revenue = revenue; 165 | } 166 | 167 | public BigDecimal getEBITDA() { 168 | return EBITDA; 169 | } 170 | 171 | public void setEBITDA(BigDecimal EBITDA) { 172 | this.EBITDA = EBITDA; 173 | } 174 | 175 | public BigDecimal getOneYearTargetPrice() { 176 | return oneYearTargetPrice; 177 | } 178 | 179 | public void setOneYearTargetPrice(BigDecimal oneYearTargetPrice) { 180 | this.oneYearTargetPrice = oneYearTargetPrice; 181 | } 182 | 183 | public BigDecimal getShortRatio() { 184 | return shortRatio; 185 | } 186 | 187 | public void setShortRatio(BigDecimal shortRatio) { 188 | this.shortRatio = shortRatio; 189 | } 190 | 191 | public Calendar getEarningsAnnouncement() { 192 | return earningsAnnouncement; 193 | } 194 | 195 | public void setEarningsAnnouncement(Calendar earningsAnnouncement) { 196 | this.earningsAnnouncement = earningsAnnouncement; 197 | } 198 | 199 | @Override 200 | public String toString() { 201 | String earningsStr = "/"; 202 | if(this.earningsAnnouncement != null) { 203 | earningsStr = this.earningsAnnouncement.getTime().toString(); 204 | } 205 | return "EPS: " + this.eps + ", PE: " + this.pe + ", Earnings announcement: " + earningsStr; 206 | } 207 | 208 | } 209 | -------------------------------------------------------------------------------- /src/main/java/yahoofinance/histquotes2/HistQuotes2Request.java: -------------------------------------------------------------------------------- 1 | package yahoofinance.histquotes2; 2 | 3 | import yahoofinance.Utils; 4 | import yahoofinance.YahooFinance; 5 | import yahoofinance.histquotes.HistoricalQuote; 6 | import yahoofinance.histquotes.Interval; 7 | import yahoofinance.util.RedirectableRequest; 8 | 9 | import java.io.BufferedReader; 10 | import java.io.IOException; 11 | import java.io.InputStreamReader; 12 | import java.net.URL; 13 | import java.net.URLConnection; 14 | import java.net.URLEncoder; 15 | import java.util.*; 16 | 17 | import org.slf4j.Logger; 18 | import org.slf4j.LoggerFactory; 19 | 20 | /** 21 | * 22 | * @author Stijn Strickx 23 | */ 24 | public class HistQuotes2Request { 25 | 26 | 27 | private static final Logger log = LoggerFactory.getLogger(HistQuotes2Request.class); 28 | private final String symbol; 29 | 30 | private final Calendar from; 31 | private final Calendar to; 32 | 33 | private final QueryInterval interval; 34 | 35 | public static final Calendar DEFAULT_FROM = Calendar.getInstance(); 36 | 37 | static { 38 | DEFAULT_FROM.add(Calendar.YEAR, -1); 39 | } 40 | public static final Calendar DEFAULT_TO = Calendar.getInstance(); 41 | public static final QueryInterval DEFAULT_INTERVAL = QueryInterval.MONTHLY; 42 | 43 | public HistQuotes2Request(String symbol) { 44 | this(symbol, DEFAULT_INTERVAL); 45 | } 46 | 47 | public HistQuotes2Request(String symbol, QueryInterval interval) { 48 | this(symbol, DEFAULT_FROM, DEFAULT_TO, interval); 49 | } 50 | 51 | 52 | public HistQuotes2Request(String symbol, Calendar from, Calendar to) { 53 | this(symbol, from, to, DEFAULT_INTERVAL); 54 | } 55 | 56 | public HistQuotes2Request(String symbol, Calendar from, Calendar to, QueryInterval interval) { 57 | this.symbol = symbol; 58 | this.from = this.cleanHistCalendar(from); 59 | this.to = this.cleanHistCalendar(to); 60 | this.interval = interval; 61 | } 62 | 63 | public HistQuotes2Request(String symbol, Date from, Date to) { 64 | this(symbol, from, to, DEFAULT_INTERVAL); 65 | } 66 | 67 | public HistQuotes2Request(String symbol, Date from, Date to, QueryInterval interval) { 68 | this(symbol, interval); 69 | this.from.setTime(from); 70 | this.to.setTime(to); 71 | this.cleanHistCalendar(this.from); 72 | this.cleanHistCalendar(this.to); 73 | } 74 | 75 | // Constructors to support the old Interval 76 | public HistQuotes2Request(String symbol, Interval interval) { 77 | this(symbol, DEFAULT_FROM, DEFAULT_TO, interval); 78 | } 79 | 80 | public HistQuotes2Request(String symbol, Calendar from, Calendar to, Interval interval) { 81 | this(symbol, from, to, IntervalMapper.get(interval)); 82 | } 83 | 84 | public HistQuotes2Request(String symbol, Date from, Date to, Interval interval) { 85 | this(symbol, from, to, IntervalMapper.get(interval)); 86 | } 87 | 88 | /** 89 | * Put everything smaller than days at 0 90 | * @param cal calendar to be cleaned 91 | */ 92 | private Calendar cleanHistCalendar(Calendar cal) { 93 | cal.set(Calendar.MILLISECOND, 0); 94 | cal.set(Calendar.SECOND, 0); 95 | cal.set(Calendar.MINUTE, 0); 96 | cal.set(Calendar.HOUR, 0); 97 | return cal; 98 | } 99 | 100 | public List getResult() throws IOException { 101 | 102 | List result = new ArrayList<>(); 103 | 104 | if(this.from.after(this.to)) { 105 | log.warn("Unable to retrieve historical quotes. " 106 | + "From-date should not be after to-date. From: " 107 | + this.from.getTime() + ", to: " + this.to.getTime()); 108 | return result; 109 | } 110 | 111 | Map params = new LinkedHashMap<>(); 112 | params.put("period1", String.valueOf(this.from.getTimeInMillis() / 1000)); 113 | params.put("period2", String.valueOf(this.to.getTimeInMillis() / 1000)); 114 | 115 | params.put("interval", this.interval.getTag()); 116 | 117 | params.put("crumb", CrumbManager.getCrumb()); 118 | 119 | String url = YahooFinance.HISTQUOTES2_BASE_URL + URLEncoder.encode(this.symbol , "UTF-8") + "?" + Utils.getURLParameters(params); 120 | 121 | // Get CSV from Yahoo 122 | log.info("Sending request: " + url); 123 | 124 | URL request = new URL(url); 125 | RedirectableRequest redirectableRequest = new RedirectableRequest(request, 5); 126 | redirectableRequest.setConnectTimeout(YahooFinance.CONNECTION_TIMEOUT); 127 | redirectableRequest.setReadTimeout(YahooFinance.CONNECTION_TIMEOUT); 128 | URLConnection connection = redirectableRequest.openConnection(); 129 | 130 | try ( InputStreamReader is = new InputStreamReader(connection.getInputStream()); 131 | BufferedReader br = new BufferedReader(is)) { 132 | br.readLine(); // skip the first line 133 | // Parse CSV 134 | for (String line = br.readLine(); line != null; line = br.readLine()) { 135 | 136 | log.info("Parsing CSV line: " + Utils.unescape(line)); 137 | HistoricalQuote quote = this.parseCSVLine(line); 138 | result.add(quote); 139 | } 140 | } 141 | return result; 142 | } 143 | 144 | private HistoricalQuote parseCSVLine(String line) { 145 | String[] data = line.split(YahooFinance.QUOTES_CSV_DELIMITER); 146 | return new HistoricalQuote(this.symbol, 147 | Utils.parseHistDate(data[0]), 148 | Utils.getBigDecimal(data[1]), 149 | Utils.getBigDecimal(data[3]), 150 | Utils.getBigDecimal(data[2]), 151 | Utils.getBigDecimal(data[4]), 152 | Utils.getBigDecimal(data[5]), 153 | Utils.getLong(data[6]) 154 | ); 155 | } 156 | 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/yahoofinance/quotes/query1v7/StockQuotesQuery1V7Request.java: -------------------------------------------------------------------------------- 1 | package yahoofinance.quotes.query1v7; 2 | 3 | import com.fasterxml.jackson.databind.JsonNode; 4 | import yahoofinance.Stock; 5 | import yahoofinance.Utils; 6 | import yahoofinance.exchanges.ExchangeTimeZone; 7 | import yahoofinance.quotes.stock.StockDividend; 8 | import yahoofinance.quotes.stock.StockQuote; 9 | import yahoofinance.quotes.stock.StockStats; 10 | 11 | import java.math.BigDecimal; 12 | import java.util.TimeZone; 13 | 14 | /** 15 | * 16 | * @author Stijn Strickx 17 | */ 18 | public class StockQuotesQuery1V7Request extends QuotesRequest { 19 | 20 | private static final BigDecimal ONE_HUNDRED = new BigDecimal(100); 21 | 22 | public StockQuotesQuery1V7Request(String symbols) { 23 | super(symbols); 24 | } 25 | 26 | @Override 27 | protected Stock parseJson(JsonNode node) { 28 | String symbol = node.get("symbol").asText(); 29 | Stock stock = new Stock(symbol); 30 | 31 | if(node.has("longName")) { 32 | stock.setName(node.get("longName").asText()); 33 | } else { 34 | stock.setName(getStringValue(node, "shortName")); 35 | } 36 | 37 | stock.setCurrency(getStringValue(node, "currency")); 38 | stock.setStockExchange(getStringValue(node, "fullExchangeName")); 39 | 40 | stock.setQuote(this.getQuote(node)); 41 | stock.setStats(this.getStats(node)); 42 | stock.setDividend(this.getDividend(node)); 43 | 44 | return stock; 45 | } 46 | 47 | private String getStringValue(JsonNode node, String field) { 48 | if(node.has(field)) { 49 | return node.get(field).asText(); 50 | } 51 | return null; 52 | } 53 | 54 | 55 | 56 | private StockQuote getQuote(JsonNode node) { 57 | String symbol = node.get("symbol").asText(); 58 | StockQuote quote = new StockQuote(symbol); 59 | 60 | quote.setPrice(Utils.getBigDecimal(getStringValue(node,"regularMarketPrice"))); 61 | // quote.setLastTradeSize(null); 62 | quote.setAsk(Utils.getBigDecimal(getStringValue(node,"ask"))); 63 | quote.setAskSize(Utils.getLong(getStringValue(node,"askSize"))); 64 | quote.setBid(Utils.getBigDecimal(getStringValue(node,"bid"))); 65 | quote.setBidSize(Utils.getLong(getStringValue(node,"bidSize"))); 66 | quote.setOpen(Utils.getBigDecimal(getStringValue(node,"regularMarketOpen"))); 67 | quote.setPreviousClose(Utils.getBigDecimal(getStringValue(node,"regularMarketPreviousClose"))); 68 | quote.setDayHigh(Utils.getBigDecimal(getStringValue(node,"regularMarketDayHigh"))); 69 | quote.setDayLow(Utils.getBigDecimal(getStringValue(node,"regularMarketDayLow"))); 70 | 71 | if(node.has("exchangeTimezoneName")) { 72 | quote.setTimeZone(TimeZone.getTimeZone(node.get("exchangeTimezoneName").asText())); 73 | } else { 74 | quote.setTimeZone(ExchangeTimeZone.getStockTimeZone(symbol)); 75 | } 76 | 77 | if(node.has("regularMarketTime")) { 78 | quote.setLastTradeTime(Utils.unixToCalendar(node.get("regularMarketTime").asLong())); 79 | } 80 | 81 | quote.setYearHigh(Utils.getBigDecimal(getStringValue(node,"fiftyTwoWeekHigh"))); 82 | quote.setYearLow(Utils.getBigDecimal(getStringValue(node,"fiftyTwoWeekLow"))); 83 | quote.setPriceAvg50(Utils.getBigDecimal(getStringValue(node,"fiftyDayAverage"))); 84 | quote.setPriceAvg200(Utils.getBigDecimal(getStringValue(node,"twoHundredDayAverage"))); 85 | 86 | quote.setVolume(Utils.getLong(getStringValue(node,"regularMarketVolume"))); 87 | quote.setAvgVolume(Utils.getLong(getStringValue(node,"averageDailyVolume3Month"))); 88 | 89 | return quote; 90 | } 91 | 92 | private StockStats getStats(JsonNode node) { 93 | String symbol = getStringValue(node,"symbol"); 94 | StockStats stats = new StockStats(symbol); 95 | 96 | stats.setMarketCap(Utils.getBigDecimal(getStringValue(node,"marketCap"))); 97 | // stats.setSharesFloat(Utils.getLong(getStringValue(node,"sharesOutstanding"))); 98 | stats.setSharesOutstanding(Utils.getLong(getStringValue(node,"sharesOutstanding"))); 99 | // stats.setSharesOwned(Utils.getLong(getStringValue(node,"symbol"))); 100 | 101 | stats.setEps(Utils.getBigDecimal(getStringValue(node,"epsTrailingTwelveMonths"))); 102 | stats.setPe(Utils.getBigDecimal(getStringValue(node,"trailingPE"))); 103 | // stats.setPeg(Utils.getBigDecimal(getStringValue(node,"symbol"))); 104 | 105 | stats.setEpsEstimateCurrentYear(Utils.getBigDecimal(getStringValue(node,"epsForward"))); 106 | 107 | stats.setPriceBook(Utils.getBigDecimal(getStringValue(node,"priceToBook"))); 108 | // stats.setPriceSales(Utils.getBigDecimal(getStringValue(node,"symbol"))); 109 | stats.setBookValuePerShare(Utils.getBigDecimal(getStringValue(node,"bookValue"))); 110 | 111 | if(node.has("earningsTimestamp")) { 112 | stats.setEarningsAnnouncement(Utils.unixToCalendar(node.get("earningsTimestamp").asLong())); 113 | } 114 | 115 | return stats; 116 | } 117 | 118 | private StockDividend getDividend(JsonNode node) { 119 | String symbol = this.getStringValue(node, "symbol"); 120 | StockDividend dividend = new StockDividend(symbol); 121 | 122 | if (node.has("dividendDate")) { 123 | long dividendTimestamp = node.get("dividendDate").asLong(); 124 | dividend.setPayDate(Utils.unixToCalendar(dividendTimestamp)); 125 | // dividend.setExDate(Utils.unixToCalendar(node.get("dividendDate").asLong())); 126 | } 127 | if (node.has("trailingAnnualDividendRate")) { 128 | dividend.setAnnualYield(Utils.getBigDecimal(this.getStringValue(node, "trailingAnnualDividendRate"))); 129 | } 130 | if (node.has("trailingAnnualDividendYield")) { 131 | BigDecimal yield = Utils.getBigDecimal(this.getStringValue(node, "trailingAnnualDividendYield")); 132 | if (yield != null) { 133 | dividend.setAnnualYieldPercent(yield.multiply(ONE_HUNDRED)); 134 | } 135 | } 136 | 137 | return dividend; 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /src/main/java/yahoofinance/quotes/csv/StockQuotesData.java: -------------------------------------------------------------------------------- 1 | package yahoofinance.quotes.csv; 2 | 3 | import yahoofinance.Stock; 4 | import yahoofinance.Utils; 5 | import yahoofinance.exchanges.ExchangeTimeZone; 6 | import yahoofinance.quotes.stock.StockDividend; 7 | import yahoofinance.quotes.stock.StockQuote; 8 | import yahoofinance.quotes.stock.StockStats; 9 | 10 | /** 11 | * 12 | * @author Stijn Strickx 13 | */ 14 | public class StockQuotesData { 15 | 16 | private final String[] data; 17 | 18 | public StockQuotesData(String[] data) { 19 | this.data = data; 20 | } 21 | 22 | public String getValue(QuotesProperty property) { 23 | int i = StockQuotesRequest.DEFAULT_PROPERTIES.indexOf(property); 24 | if(i >= 0 && i < this.data.length) { 25 | return this.data[i]; 26 | } 27 | return null; 28 | } 29 | 30 | public StockQuote getQuote() { 31 | String symbol = this.getValue(QuotesProperty.Symbol); 32 | StockQuote quote = new StockQuote(symbol); 33 | 34 | quote.setPrice(Utils.getBigDecimal(this.getValue(QuotesProperty.LastTradePriceOnly))); 35 | quote.setLastTradeSize(Utils.getLong(this.getValue(QuotesProperty.LastTradeSize))); 36 | quote.setAsk(Utils.getBigDecimal(this.getValue(QuotesProperty.AskRealtime), this.getValue(QuotesProperty.Ask))); 37 | quote.setAskSize(Utils.getLong(this.getValue(QuotesProperty.AskSize))); 38 | quote.setBid(Utils.getBigDecimal(this.getValue(QuotesProperty.BidRealtime), this.getValue(QuotesProperty.Bid))); 39 | quote.setBidSize(Utils.getLong(this.getValue(QuotesProperty.BidSize))); 40 | quote.setOpen(Utils.getBigDecimal(this.getValue(QuotesProperty.Open))); 41 | quote.setPreviousClose(Utils.getBigDecimal(this.getValue(QuotesProperty.PreviousClose))); 42 | quote.setDayHigh(Utils.getBigDecimal(this.getValue(QuotesProperty.DaysHigh))); 43 | quote.setDayLow(Utils.getBigDecimal(this.getValue(QuotesProperty.DaysLow))); 44 | 45 | quote.setTimeZone(ExchangeTimeZone.getStockTimeZone(symbol)); 46 | quote.setLastTradeDateStr(this.getValue(QuotesProperty.LastTradeDate)); 47 | quote.setLastTradeTimeStr(this.getValue(QuotesProperty.LastTradeTime)); 48 | quote.setLastTradeTime(Utils.parseDateTime(this.getValue(QuotesProperty.LastTradeDate), this.getValue(QuotesProperty.LastTradeTime), quote.getTimeZone())); 49 | 50 | quote.setYearHigh(Utils.getBigDecimal(this.getValue(QuotesProperty.YearHigh))); 51 | quote.setYearLow(Utils.getBigDecimal(this.getValue(QuotesProperty.YearLow))); 52 | quote.setPriceAvg50(Utils.getBigDecimal(this.getValue(QuotesProperty.FiftydayMovingAverage))); 53 | quote.setPriceAvg200(Utils.getBigDecimal(this.getValue(QuotesProperty.TwoHundreddayMovingAverage))); 54 | 55 | quote.setVolume(Utils.getLong(this.getValue(QuotesProperty.Volume))); 56 | quote.setAvgVolume(Utils.getLong(this.getValue(QuotesProperty.AverageDailyVolume))); 57 | 58 | return quote; 59 | } 60 | 61 | public StockStats getStats() { 62 | String symbol = this.getValue(QuotesProperty.Symbol); 63 | StockStats stats = new StockStats(symbol); 64 | 65 | stats.setMarketCap(Utils.getBigDecimal(this.getValue(QuotesProperty.MarketCapitalization))); 66 | stats.setSharesFloat(Utils.getLong(this.getValue(QuotesProperty.SharesFloat))); 67 | stats.setSharesOutstanding(Utils.getLong(this.getValue(QuotesProperty.SharesOutstanding))); 68 | stats.setSharesOwned(Utils.getLong(this.getValue(QuotesProperty.SharesOwned))); 69 | 70 | stats.setEps(Utils.getBigDecimal(this.getValue(QuotesProperty.DilutedEPS))); 71 | stats.setPe(Utils.getBigDecimal(this.getValue(QuotesProperty.PERatio))); 72 | stats.setPeg(Utils.getBigDecimal(this.getValue(QuotesProperty.PEGRatio))); 73 | 74 | stats.setEpsEstimateCurrentYear(Utils.getBigDecimal(this.getValue(QuotesProperty.EPSEstimateCurrentYear))); 75 | stats.setEpsEstimateNextQuarter(Utils.getBigDecimal(this.getValue(QuotesProperty.EPSEstimateNextQuarter))); 76 | stats.setEpsEstimateNextYear(Utils.getBigDecimal(this.getValue(QuotesProperty.EPSEstimateNextYear))); 77 | 78 | stats.setPriceBook(Utils.getBigDecimal(this.getValue(QuotesProperty.PriceBook))); 79 | stats.setPriceSales(Utils.getBigDecimal(this.getValue(QuotesProperty.PriceSales))); 80 | stats.setBookValuePerShare(Utils.getBigDecimal(this.getValue(QuotesProperty.BookValuePerShare))); 81 | 82 | stats.setOneYearTargetPrice(Utils.getBigDecimal(this.getValue(QuotesProperty.OneyrTargetPrice))); 83 | stats.setEBITDA(Utils.getBigDecimal(this.getValue(QuotesProperty.EBITDA))); 84 | stats.setRevenue(Utils.getBigDecimal(this.getValue(QuotesProperty.Revenue))); 85 | 86 | stats.setShortRatio(Utils.getBigDecimal(this.getValue(QuotesProperty.ShortRatio))); 87 | 88 | return stats; 89 | } 90 | 91 | public StockDividend getDividend() { 92 | String symbol = this.getValue(QuotesProperty.Symbol); 93 | StockDividend dividend = new StockDividend(symbol); 94 | 95 | dividend.setPayDate(Utils.parseDividendDate(this.getValue(QuotesProperty.DividendPayDate))); 96 | dividend.setExDate(Utils.parseDividendDate(this.getValue(QuotesProperty.ExDividendDate))); 97 | dividend.setAnnualYield(Utils.getBigDecimal(this.getValue(QuotesProperty.TrailingAnnualDividendYield))); 98 | dividend.setAnnualYieldPercent(Utils.getBigDecimal(this.getValue(QuotesProperty.TrailingAnnualDividendYieldInPercent))); 99 | 100 | return dividend; 101 | } 102 | 103 | public Stock getStock() { 104 | String symbol = this.getValue(QuotesProperty.Symbol); 105 | Stock stock = new Stock(symbol); 106 | 107 | stock.setName(Utils.getString(this.getValue(QuotesProperty.Name))); 108 | stock.setCurrency(Utils.getString(this.getValue(QuotesProperty.Currency))); 109 | stock.setStockExchange(Utils.getString(this.getValue(QuotesProperty.StockExchange))); 110 | 111 | stock.setQuote(this.getQuote()); 112 | stock.setStats(this.getStats()); 113 | stock.setDividend(this.getDividend()); 114 | 115 | return stock; 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /src/test/java/yahoofinance/HistoricalQuoteRequestTest.java: -------------------------------------------------------------------------------- 1 | package yahoofinance; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | import yahoofinance.histquotes.HistoricalQuote; 6 | import yahoofinance.histquotes.Interval; 7 | import yahoofinance.mock.MockedServersTest; 8 | 9 | import java.io.IOException; 10 | import java.math.BigDecimal; 11 | import java.util.Calendar; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | import static org.junit.Assert.*; 16 | 17 | /** 18 | * 19 | * @author Stijn Strickx 20 | */ 21 | public class HistoricalQuoteRequestTest extends MockedServersTest { 22 | 23 | private Calendar today; 24 | private Calendar from; 25 | 26 | @Before 27 | public void setup() { 28 | today = Calendar.getInstance(); 29 | today.set(Calendar.YEAR, 2016); 30 | today.set(Calendar.MONTH, 8); 31 | today.set(Calendar.DATE, 11); 32 | 33 | from = (Calendar) today.clone(); 34 | from.add(Calendar.YEAR, -1); 35 | } 36 | 37 | @Test 38 | public void historicalQuoteTest() throws IOException { 39 | Stock goog = YahooFinance.get("GOOG", from, today); 40 | 41 | assertNotNull(goog.getHistory()); 42 | assertEquals(13, goog.getHistory().size()); 43 | 44 | for(HistoricalQuote histQuote : goog.getHistory()) { 45 | assertEquals("GOOG", histQuote.getSymbol()); 46 | assertTrue(histQuote.getAdjClose().compareTo(BigDecimal.ZERO) > 0); 47 | assertTrue(histQuote.getClose().compareTo(BigDecimal.ZERO) > 0); 48 | assertTrue(histQuote.getHigh().compareTo(BigDecimal.ZERO) > 0); 49 | assertTrue(histQuote.getLow().compareTo(BigDecimal.ZERO) > 0); 50 | assertTrue(histQuote.getOpen().compareTo(BigDecimal.ZERO) > 0); 51 | assertTrue(histQuote.getVolume() > 0); 52 | assertNotNull(histQuote.getDate()); 53 | } 54 | 55 | HistoricalQuote histQuote = goog.getHistory().get(5); 56 | 57 | assertEquals(new BigDecimal("693.01001"), histQuote.getAdjClose()); 58 | assertEquals(new BigDecimal("693.01001"), histQuote.getClose()); 59 | assertEquals(new BigDecimal("769.900024"), histQuote.getHigh()); 60 | assertEquals(new BigDecimal("689.00"), histQuote.getLow()); 61 | assertEquals(new BigDecimal("738.599976"), histQuote.getOpen()); 62 | assertEquals(Long.valueOf(2125700), histQuote.getVolume()); 63 | assertEquals(3, histQuote.getDate().get(Calendar.MONTH)); 64 | assertEquals(1, histQuote.getDate().get(Calendar.DATE)); 65 | assertEquals(2016, histQuote.getDate().get(Calendar.YEAR)); 66 | 67 | } 68 | 69 | @Test 70 | public void intervalTest() throws IOException { 71 | Stock tsla = YahooFinance.get("TSLA", from, today, Interval.DAILY); 72 | Stock scty = YahooFinance.get("SCTY", from, today, Interval.WEEKLY); 73 | Stock goog = YahooFinance.get("GOOG", from, today, Interval.MONTHLY); 74 | 75 | assertEquals(252, tsla.getHistory().size()); 76 | assertEquals(53, scty.getHistory().size()); 77 | assertEquals(13, goog.getHistory().size()); 78 | } 79 | 80 | @Test 81 | public void multiYearTest() throws IOException { 82 | Calendar from = (Calendar) today.clone(); 83 | Calendar to = (Calendar) today.clone(); 84 | from.add(Calendar.YEAR, -5); // from 5 years ago 85 | 86 | Stock goog = YahooFinance.get("GOOG", from, to, Interval.WEEKLY); 87 | 88 | assertEquals(261, goog.getHistory().size()); 89 | 90 | HistoricalQuote histQuote = goog.getHistory().get(0); 91 | assertEquals(8, histQuote.getDate().get(Calendar.MONTH)); 92 | assertEquals(6, histQuote.getDate().get(Calendar.DATE)); 93 | assertEquals(2016, histQuote.getDate().get(Calendar.YEAR)); 94 | 95 | histQuote = goog.getHistory().get(260); 96 | assertEquals(8, histQuote.getDate().get(Calendar.MONTH)); 97 | assertEquals(12, histQuote.getDate().get(Calendar.DATE)); 98 | assertEquals(2011, histQuote.getDate().get(Calendar.YEAR)); 99 | 100 | } 101 | 102 | @Test 103 | public void multiStockTest() throws IOException { 104 | String[] symbols = new String[] {"INTC", "AIR.PA"}; 105 | Map stocks = YahooFinance.get(symbols, from, today); 106 | Stock intel = stocks.get("INTC"); 107 | Stock airbus = stocks.get("AIR.PA"); 108 | 109 | assertEquals(13, intel.getHistory().size()); 110 | assertEquals(13, airbus.getHistory().size()); 111 | assertEquals("INTC", intel.getHistory().get(3).getSymbol()); 112 | assertEquals("AIR.PA", airbus.getHistory().get(5).getSymbol()); 113 | } 114 | 115 | @Test 116 | public void historicalFlowTest() throws IOException { 117 | Stock goog = YahooFinance.get("GOOG"); 118 | int requestCount = MockedServersTest.histQuotesServer.getRequestCount(); 119 | assertNotNull(goog.getHistory(from, today)); 120 | requestCount += 1; 121 | assertEquals(requestCount, MockedServersTest.histQuotesServer.getRequestCount()); 122 | assertEquals(13, goog.getHistory().size()); 123 | assertEquals(requestCount, MockedServersTest.histQuotesServer.getRequestCount()); 124 | 125 | Calendar from = (Calendar) today.clone(); 126 | Calendar to = (Calendar) today.clone(); 127 | from.add(Calendar.YEAR, -5); // from 5 years ago 128 | assertNotNull(goog.getHistory(from, to, Interval.WEEKLY)); 129 | requestCount += 1; 130 | assertEquals(requestCount, MockedServersTest.histQuotesServer.getRequestCount()); 131 | assertEquals(261, goog.getHistory().size()); 132 | } 133 | 134 | @Test 135 | public void impossibleRequestTest() throws IOException { 136 | Calendar from = Calendar.getInstance(); 137 | Calendar to = Calendar.getInstance(); 138 | from.add(Calendar.DATE, 2); // from > to 139 | Exception reqEx = null; 140 | 141 | Stock goog = YahooFinance.get("GOOG"); 142 | List histQuotes = null; 143 | int requestCount = MockedServersTest.histQuotesServer.getRequestCount(); 144 | try { 145 | histQuotes = goog.getHistory(from, to); 146 | } catch (IOException ex) { 147 | reqEx = ex; 148 | } 149 | // Didn't send any requests since the problem was detected 150 | assertEquals(requestCount, MockedServersTest.histQuotesServer.getRequestCount()); 151 | assertNull(reqEx); 152 | assertEquals(0, histQuotes.size()); 153 | } 154 | 155 | } 156 | -------------------------------------------------------------------------------- /src/main/java/yahoofinance/query2v8/HistQuotesQuery2V8Request.java: -------------------------------------------------------------------------------- 1 | package yahoofinance.query2v8; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStreamReader; 6 | import java.math.BigDecimal; 7 | import java.net.URL; 8 | import java.net.URLConnection; 9 | import java.net.URLEncoder; 10 | import java.util.ArrayList; 11 | import java.util.Calendar; 12 | import java.util.Date; 13 | import java.util.LinkedHashMap; 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | import org.slf4j.Logger; 18 | import org.slf4j.LoggerFactory; 19 | 20 | import com.fasterxml.jackson.databind.JsonNode; 21 | import com.fasterxml.jackson.databind.ObjectMapper; 22 | 23 | import yahoofinance.Utils; 24 | import yahoofinance.YahooFinance; 25 | import yahoofinance.histquotes.HistoricalQuote; 26 | import yahoofinance.histquotes2.QueryInterval; 27 | import yahoofinance.util.RedirectableRequest; 28 | 29 | /** 30 | * @author Stijn Strickx 31 | */ 32 | public class HistQuotesQuery2V8Request { 33 | 34 | private static final Logger log = LoggerFactory.getLogger(HistQuotesQuery2V8Request.class); 35 | private static final ObjectMapper objectMapper = new ObjectMapper(); 36 | 37 | private final String symbol; 38 | private final Calendar from; 39 | private final Calendar to; 40 | private final QueryInterval interval; 41 | 42 | public static final Calendar DEFAULT_FROM = Calendar.getInstance(); 43 | 44 | static { 45 | DEFAULT_FROM.add(Calendar.YEAR, -1); 46 | } 47 | public static final Calendar DEFAULT_TO = Calendar.getInstance(); 48 | public static final QueryInterval DEFAULT_INTERVAL = QueryInterval.MONTHLY; 49 | 50 | public HistQuotesQuery2V8Request(String symbol) { 51 | this(symbol, DEFAULT_INTERVAL); 52 | } 53 | 54 | public HistQuotesQuery2V8Request(String symbol, QueryInterval interval) { 55 | this(symbol, DEFAULT_FROM, DEFAULT_TO, interval); 56 | } 57 | 58 | 59 | public HistQuotesQuery2V8Request(String symbol, Calendar from, Calendar to) { 60 | this(symbol, from, to, DEFAULT_INTERVAL); 61 | } 62 | 63 | public HistQuotesQuery2V8Request(String symbol, Calendar from, Calendar to, QueryInterval interval) { 64 | this.symbol = symbol; 65 | this.from = this.cleanHistCalendar(from); 66 | this.to = this.cleanHistCalendar(to); 67 | this.interval = interval; 68 | } 69 | 70 | public HistQuotesQuery2V8Request(String symbol, Date from, Date to) { 71 | this(symbol, from, to, DEFAULT_INTERVAL); 72 | } 73 | 74 | public HistQuotesQuery2V8Request(String symbol, Date from, Date to, QueryInterval interval) { 75 | this(symbol, interval); 76 | this.from.setTime(from); 77 | this.to.setTime(to); 78 | this.cleanHistCalendar(this.from); 79 | this.cleanHistCalendar(this.to); 80 | } 81 | 82 | /** 83 | * Put everything smaller than days at 0 84 | * @param cal calendar to be cleaned 85 | */ 86 | private Calendar cleanHistCalendar(Calendar cal) { 87 | cal.set(Calendar.MILLISECOND, 0); 88 | cal.set(Calendar.SECOND, 0); 89 | cal.set(Calendar.MINUTE, 0); 90 | cal.set(Calendar.HOUR, 0); 91 | return cal; 92 | } 93 | 94 | public List getResult() throws IOException { 95 | String json = getJson(); 96 | JsonNode resultNode = objectMapper.readTree(json).get("chart").get("result").get(0); 97 | JsonNode timestamps = resultNode.get("timestamp"); 98 | JsonNode indicators = resultNode.get("indicators"); 99 | JsonNode quotes = indicators.get("quote").get(0); 100 | JsonNode closes = quotes.get("close"); 101 | JsonNode volumes = quotes.get("volume"); 102 | JsonNode opens = quotes.get("open"); 103 | JsonNode highs = quotes.get("high"); 104 | JsonNode lows = quotes.get("low"); 105 | JsonNode adjCloses = indicators.get("adjclose").get(0).get("adjclose"); 106 | 107 | List result = new ArrayList<>(); 108 | for (int i = 0; i < timestamps.size(); i++) { 109 | long timestamp = timestamps.get(i).asLong(); 110 | Calendar calendar = Calendar.getInstance(); 111 | calendar.setTimeInMillis(timestamp * 1000); 112 | BigDecimal adjClose = adjCloses.get(i).decimalValue(); 113 | long volume = volumes.get(i).asLong(); 114 | BigDecimal open = opens.get(i).decimalValue(); 115 | BigDecimal high = highs.get(i).decimalValue(); 116 | BigDecimal low = lows.get(i).decimalValue(); 117 | BigDecimal close = closes.get(i).decimalValue(); 118 | 119 | HistoricalQuote quote = new HistoricalQuote( 120 | symbol, 121 | calendar, 122 | open, 123 | low, 124 | high, 125 | close, 126 | adjClose, 127 | volume); 128 | result.add(quote); 129 | } 130 | 131 | return result; 132 | } 133 | 134 | public String getJson() throws IOException { 135 | 136 | if(this.from.after(this.to)) { 137 | log.warn("Unable to retrieve historical quotes. " 138 | + "From-date should not be after to-date. From: " 139 | + this.from.getTime() + ", to: " + this.to.getTime()); 140 | return ""; 141 | } 142 | 143 | Map params = new LinkedHashMap<>(); 144 | params.put("period1", String.valueOf(this.from.getTimeInMillis() / 1000)); 145 | params.put("period2", String.valueOf(this.to.getTimeInMillis() / 1000)); 146 | params.put("interval", this.interval.getTag()); 147 | params.put("events", "div|split"); 148 | 149 | String url = YahooFinance.HISTQUOTES_QUERY2V8_BASE_URL + URLEncoder.encode(this.symbol , "UTF-8") + "?" + Utils.getURLParameters(params); 150 | 151 | // Get CSV from Yahoo 152 | log.info("Sending request: " + url); 153 | 154 | URL request = new URL(url); 155 | RedirectableRequest redirectableRequest = new RedirectableRequest(request, 5); 156 | redirectableRequest.setConnectTimeout(YahooFinance.CONNECTION_TIMEOUT); 157 | redirectableRequest.setReadTimeout(YahooFinance.CONNECTION_TIMEOUT); 158 | URLConnection connection = redirectableRequest.openConnection(); 159 | 160 | StringBuilder builder = new StringBuilder(); 161 | try ( InputStreamReader is = new InputStreamReader(connection.getInputStream()); 162 | BufferedReader br = new BufferedReader(is)) { 163 | for (String line = br.readLine(); line != null; line = br.readLine()) { 164 | if (builder.length() > 0) { 165 | builder.append("\n"); 166 | } 167 | builder.append(line); 168 | } 169 | } 170 | return builder.toString(); 171 | } 172 | 173 | } 174 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Finance Quotes API for Yahoo Finance (Java) 2 | 3 | [![Java CI with Maven](https://github.com/sfuhrm/yahoofinance-api/actions/workflows/maven.yml/badge.svg)](https://github.com/sfuhrm/yahoofinance-api/actions/workflows/maven.yml) 4 | [![Coverage](https://raw.githubusercontent.com/sfuhrm/yahoofinance-api/master/.github/badges/jacoco.svg)](https://github.com/sfuhrm/yahoofinance-api/actions) 5 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/de.sfuhrm/YahooFinanceAPI/badge.svg)](https://maven-badges.herokuapp.com/maven-central/de.sfuhrm/YahooFinanceAPI) 6 | [![javadoc](https://javadoc.io/badge2/de.sfuhrm/YahooFinanceAPI/javadoc.svg)](https://javadoc.io/doc/de.sfuhrm/YahooFinanceAPI) 7 | [![ReleaseDate](https://img.shields.io/github/release-date/sfuhrm/yahoofinance-api)](https://github.com/sfuhrm/yahoofinance-api/releases) 8 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 9 | 10 | 11 | ### About this fork 12 | 13 | This is a fork of sstrickx' yahoofinanceapi which can be found [here](https://github.com/sstrickx/yahoofinance-api). 14 | 15 | The original repository seems to be no longer maintained. The intention of this fork is to provide a release with 16 | bugfixes, not new features. 17 | 18 | The changes in this fork are: 19 | 20 | * Proper closing of input/output streams, thereby working properly with HTTPS sockets. 21 | * Several fixes of deprecation warnings. 22 | * Raise minimum Java required to JDK 8. 23 | * Adding/changing of some badges in the project README.md. 24 | * Change Maven group id to 'de.sfuhrm'. 25 | * Add Github actions for continuous integration. 26 | 27 | Besides these changes, the software can be used as a drop-in replacement for sstrickx' Maven artifacts. 28 | 29 | ### About the library 30 | 31 | This library provides some methods that should make it easy to communicate with the Yahoo Finance API. It allows you to request detailed information, some statistics and historical quotes on stocks. Separate functionality is available to request a simple FX quote. 32 | Please check the javadoc (available in dist directory) to get a complete overview of the available methods and to get an idea of which data is available from Yahoo Finance. 33 | 34 | > This project is not associated with nor sponsored by Yahoo! Inc. Yahoo! Inc. is the exclusive owner of all trademark and other intellectual property rights in and to the YAHOO! and Y! trademarks (the "Trademarks"), including the stylized YAHOO! and Y! logos. Yahoo! Inc. owns trademark registrations for the Trademarks. 35 | 36 | ## Add to your project as a dependency 37 | ### Maven 38 | ```xml 39 | 40 | de.sfuhrm 41 | YahooFinanceAPI 42 | x.y.z 43 | 44 | ``` 45 | ### Gradle 46 | ```groovy 47 | dependencies { 48 | compile group: 'de.sfuhrm', name: 'YahooFinanceAPI', version: 'x.y.z' 49 | } 50 | ``` 51 | ### Ivy 52 | ```xml 53 | 54 | ``` 55 | 56 | # Examples 57 | ## Single stock 58 | ```java 59 | Stock stock = YahooFinance.get("INTC"); 60 | 61 | BigDecimal price = stock.getQuote().getPrice(); 62 | BigDecimal change = stock.getQuote().getChangeInPercent(); 63 | BigDecimal peg = stock.getStats().getPeg(); 64 | BigDecimal dividend = stock.getDividend().getAnnualYieldPercent(); 65 | 66 | stock.print(); 67 | ``` 68 | Output: 69 | ``` 70 | INTC 71 | -------------------------------- 72 | symbol: INTC 73 | name: Intel Corporation 74 | currency: USD 75 | stockExchange: NasdaqNM 76 | quote: Ask: 32.25, Bid: 32.24, Price: 32.2485, Prev close: 33.62 77 | stats: EPS: 2.019, PE: 16.65, PEG: 1.74 78 | dividend: Pay date: Mon Dec 01 06:00:00 CET 2014, Ex date: Tue Aug 05 06:00:00 CEST 2014, Annual yield: 2.68% 79 | history: null 80 | -------------------------------- 81 | ``` 82 | 83 | ## Single stock, easy refresh 84 | ```java 85 | Stock stock = YahooFinance.get("INTC"); 86 | double price = stock.getQuote(true).getPrice(); 87 | ``` 88 | This will also automatically refresh the statistics and dividend data of the stock in a single request to Yahoo Finance. 89 | Please be aware that it wouldn't be a good idea to call the getQuote(true), getStats(true) or getDividend(true) too much in a short timespan as this will cost too much delay without providing any added value. There's no problem to call the versions of those methods without argument or with the argument set to false. 90 | 91 | ## Multiple stocks at once 92 | ```java 93 | String[] symbols = new String[] {"INTC", "BABA", "TSLA", "AIR.PA", "YHOO"}; 94 | Map stocks = YahooFinance.get(symbols); // single request 95 | Stock intel = stocks.get("INTC"); 96 | Stock airbus = stocks.get("AIR.PA"); 97 | ``` 98 | 99 | ## FX quote 100 | ```java 101 | FxQuote usdeur = YahooFinance.getFx(FxSymbols.USDEUR); 102 | FxQuote usdgbp = YahooFinance.getFx("USDGBP=X"); 103 | System.out.println(usdeur); 104 | System.out.println(usdgbp); 105 | ``` 106 | Output: 107 | ``` 108 | USDEUR=X: 0.7842 109 | USDGBP=X: 0.6253 110 | ``` 111 | 112 | ## Single stock, include historical quotes (1) 113 | ```java 114 | Stock tesla = YahooFinance.get("TSLA", true); 115 | System.out.println(tesla.getHistory()); 116 | ``` 117 | Output: (Symbol@Date: low-high, open->close (adjusted close)) 118 | ``` 119 | [TSLA@2014-10-01: 217.32-265.54, 242.2->229.7 (229.7), TSLA@2014-09-02: 240.12-291.42, 275.5->242.68 (242.68), ...] 120 | ``` 121 | 122 | ## Single stock, include historical quotes (2) 123 | ```java 124 | Calendar from = Calendar.getInstance(); 125 | Calendar to = Calendar.getInstance(); 126 | from.add(Calendar.YEAR, -5); // from 5 years ago 127 | 128 | Stock google = YahooFinance.get("GOOG", from, to, Interval.WEEKLY); 129 | ``` 130 | 131 | ## Multiple stocks, include historical quotes 132 | ```java 133 | String[] symbols = new String[] {"INTC", "BABA", "TSLA", "AIR.PA", "YHOO"}; 134 | // Can also be done with explicit from, to and Interval parameters 135 | Map stocks = YahooFinance.get(symbols, true); 136 | Stock intel = stocks.get("INTC"); 137 | Stock airbus = stocks.get("AIR.PA"); 138 | ``` 139 | 140 | ## Alternatives for historical quotes 141 | If the historical quotes are not yet available, the getHistory() method will automatically send a new request to Yahoo Finance. 142 | ```java 143 | Stock google = YahooFinance.get("GOOG"); 144 | List googleHistQuotes = google.getHistory(); 145 | ``` 146 | Or you could explicitly define the from, to and Interval parameters to force a new request. 147 | Check the javadoc for more variations on the getHistory method 148 | ```java 149 | Calendar from = Calendar.getInstance(); 150 | Calendar to = Calendar.getInstance(); 151 | from.add(Calendar.YEAR, -1); // from 1 year ago 152 | 153 | Stock google = YahooFinance.get("GOOG"); 154 | List googleHistQuotes = google.getHistory(from, to, Interval.DAILY); 155 | // googleHistQuotes is the same as google.getHistory() at this point 156 | // provide some parameters to the getHistory method to send a new request to Yahoo Finance 157 | ``` 158 | -------------------------------------------------------------------------------- /src/main/java/yahoofinance/quotes/csv/StockQuotesRequest.java: -------------------------------------------------------------------------------- 1 | package yahoofinance.quotes.csv; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * 8 | * @author Stijn Strickx 9 | */ 10 | public class StockQuotesRequest extends QuotesRequest { 11 | 12 | /** 13 | * Yahoo Finance is responding with formatted numbers in some cases. Because 14 | * of this, those number may contain commas. This will screw up the CSV 15 | * file. 16 | *

17 | * It's not possible to choose a different delimiter for the CSV or to 18 | * disable the number formatting 19 | *

20 | * To work around this, we surround the vulnerable values by the stock 21 | * symbol. This forces us to do manual parsing of the CSV lines instead of 22 | * using the easy String.split 23 | * 24 | */ 25 | public static final List DEFAULT_PROPERTIES = new ArrayList<>(); 26 | 27 | static { 28 | 29 | // Always keep the name and symbol in first and second place respectively! 30 | DEFAULT_PROPERTIES.add(QuotesProperty.Name); 31 | DEFAULT_PROPERTIES.add(QuotesProperty.Symbol); 32 | 33 | DEFAULT_PROPERTIES.add(QuotesProperty.Currency); 34 | DEFAULT_PROPERTIES.add(QuotesProperty.StockExchange); 35 | 36 | DEFAULT_PROPERTIES.add(QuotesProperty.Ask); 37 | DEFAULT_PROPERTIES.add(QuotesProperty.AskRealtime); 38 | DEFAULT_PROPERTIES.add(QuotesProperty.Symbol); 39 | DEFAULT_PROPERTIES.add(QuotesProperty.AskSize); 40 | DEFAULT_PROPERTIES.add(QuotesProperty.Symbol); 41 | DEFAULT_PROPERTIES.add(QuotesProperty.Bid); 42 | DEFAULT_PROPERTIES.add(QuotesProperty.BidRealtime); 43 | DEFAULT_PROPERTIES.add(QuotesProperty.Symbol); 44 | DEFAULT_PROPERTIES.add(QuotesProperty.BidSize); 45 | DEFAULT_PROPERTIES.add(QuotesProperty.Symbol); 46 | 47 | DEFAULT_PROPERTIES.add(QuotesProperty.LastTradePriceOnly); 48 | DEFAULT_PROPERTIES.add(QuotesProperty.Symbol); 49 | DEFAULT_PROPERTIES.add(QuotesProperty.LastTradeSize); 50 | DEFAULT_PROPERTIES.add(QuotesProperty.Symbol); 51 | DEFAULT_PROPERTIES.add(QuotesProperty.LastTradeDate); 52 | DEFAULT_PROPERTIES.add(QuotesProperty.LastTradeTime); 53 | 54 | DEFAULT_PROPERTIES.add(QuotesProperty.Open); 55 | DEFAULT_PROPERTIES.add(QuotesProperty.PreviousClose); 56 | DEFAULT_PROPERTIES.add(QuotesProperty.DaysLow); 57 | DEFAULT_PROPERTIES.add(QuotesProperty.DaysHigh); 58 | 59 | DEFAULT_PROPERTIES.add(QuotesProperty.Volume); 60 | DEFAULT_PROPERTIES.add(QuotesProperty.AverageDailyVolume); 61 | 62 | DEFAULT_PROPERTIES.add(QuotesProperty.YearHigh); 63 | DEFAULT_PROPERTIES.add(QuotesProperty.YearLow); 64 | 65 | DEFAULT_PROPERTIES.add(QuotesProperty.FiftydayMovingAverage); 66 | DEFAULT_PROPERTIES.add(QuotesProperty.TwoHundreddayMovingAverage); 67 | 68 | DEFAULT_PROPERTIES.add(QuotesProperty.Symbol); 69 | DEFAULT_PROPERTIES.add(QuotesProperty.SharesOutstanding); 70 | DEFAULT_PROPERTIES.add(QuotesProperty.Symbol); 71 | DEFAULT_PROPERTIES.add(QuotesProperty.Symbol); 72 | DEFAULT_PROPERTIES.add(QuotesProperty.SharesOwned); 73 | DEFAULT_PROPERTIES.add(QuotesProperty.Symbol); 74 | DEFAULT_PROPERTIES.add(QuotesProperty.MarketCapitalization); 75 | DEFAULT_PROPERTIES.add(QuotesProperty.Symbol); 76 | DEFAULT_PROPERTIES.add(QuotesProperty.SharesFloat); 77 | DEFAULT_PROPERTIES.add(QuotesProperty.Symbol); 78 | 79 | DEFAULT_PROPERTIES.add(QuotesProperty.DividendPayDate); 80 | DEFAULT_PROPERTIES.add(QuotesProperty.ExDividendDate); 81 | DEFAULT_PROPERTIES.add(QuotesProperty.TrailingAnnualDividendYield); 82 | DEFAULT_PROPERTIES.add(QuotesProperty.TrailingAnnualDividendYieldInPercent); 83 | 84 | DEFAULT_PROPERTIES.add(QuotesProperty.DilutedEPS); 85 | DEFAULT_PROPERTIES.add(QuotesProperty.EPSEstimateCurrentYear); 86 | DEFAULT_PROPERTIES.add(QuotesProperty.EPSEstimateNextQuarter); 87 | DEFAULT_PROPERTIES.add(QuotesProperty.EPSEstimateNextYear); 88 | DEFAULT_PROPERTIES.add(QuotesProperty.PERatio); 89 | DEFAULT_PROPERTIES.add(QuotesProperty.PEGRatio); 90 | 91 | DEFAULT_PROPERTIES.add(QuotesProperty.PriceBook); 92 | DEFAULT_PROPERTIES.add(QuotesProperty.PriceSales); 93 | DEFAULT_PROPERTIES.add(QuotesProperty.BookValuePerShare); 94 | 95 | DEFAULT_PROPERTIES.add(QuotesProperty.Revenue); 96 | DEFAULT_PROPERTIES.add(QuotesProperty.EBITDA); 97 | DEFAULT_PROPERTIES.add(QuotesProperty.OneyrTargetPrice); 98 | 99 | DEFAULT_PROPERTIES.add(QuotesProperty.ShortRatio); 100 | } 101 | 102 | public StockQuotesRequest(String query) { 103 | super(query, StockQuotesRequest.DEFAULT_PROPERTIES); 104 | } 105 | 106 | @Override 107 | protected StockQuotesData parseCSVLine(String line) { 108 | List parsedLine = new ArrayList<>(); 109 | 110 | // first get company name, symbol, currency and exchange 111 | // because we need the symbol and currency or exchange might be the same as the symbol! 112 | // pretty ugly code due to the bad format of the csv 113 | int pos1 = 0; 114 | int pos2; 115 | int skip = 2; 116 | 117 | if(line.startsWith("\"")) { 118 | pos1 = 1; // skip first \" 119 | pos2 = line.indexOf('\"', 1); 120 | } else { 121 | pos2 = line.indexOf(",\""); // last comma before the first symbol (hopefully) 122 | skip = 1; 123 | } 124 | 125 | String name = line.substring(pos1, pos2); 126 | pos1 = pos2 + skip; // skip \", 127 | pos2 = line.indexOf('\"', pos1 + 1); 128 | skip = 2; 129 | String fullSymbol = line.substring(pos1, pos2 + 1); 130 | String symbol = fullSymbol.substring(1, fullSymbol.length() - 1); 131 | 132 | pos1 = pos2 + skip; 133 | if (line.charAt(pos1) == '\"') { 134 | pos1 += 1; 135 | pos2 = line.indexOf('\"', pos1); 136 | skip = 2; 137 | } else { 138 | pos2 = line.indexOf(',', pos1); 139 | skip = 1; 140 | } 141 | String currency = line.substring(pos1, pos2); 142 | 143 | pos1 = pos2 + skip; 144 | if (line.charAt(pos1) == '\"') { 145 | pos1 += 1; 146 | pos2 = line.indexOf('\"', pos1); 147 | skip = 2; 148 | } else { 149 | pos2 = line.indexOf(',', pos1); 150 | skip = 1; 151 | } 152 | String exchange = line.substring(pos1, pos2); 153 | 154 | parsedLine.add(name); 155 | parsedLine.add(symbol); 156 | parsedLine.add(currency); 157 | parsedLine.add(exchange); 158 | 159 | pos1 = pos2 + skip; // skip \", 160 | for (; pos1 < line.length(); pos1++) { 161 | if (line.startsWith(fullSymbol, pos1)) { 162 | parsedLine.add(symbol); 163 | pos1 = pos1 + fullSymbol.length() + 1; // immediately skip the , as well 164 | pos2 = line.indexOf(fullSymbol, pos1) - 1; // don't include last , 165 | parsedLine.add(line.substring(pos1, pos2)); 166 | parsedLine.add(symbol); 167 | pos1 = pos2 + fullSymbol.length() + 1; 168 | } else if (line.charAt(pos1) == '\"') { 169 | pos1 += 1; 170 | pos2 = line.indexOf('\"', pos1); 171 | parsedLine.add(line.substring(pos1, pos2)); 172 | pos1 = pos2 + 1; 173 | } else if (line.charAt(pos1) != ',') { 174 | pos2 = line.indexOf(',', pos1); 175 | if (pos2 <= pos1) { 176 | pos2 = line.length(); 177 | } 178 | parsedLine.add(line.substring(pos1, pos2)); 179 | pos1 = pos2; 180 | } 181 | } 182 | return new StockQuotesData(parsedLine.toArray(new String[this.properties.size()])); 183 | } 184 | 185 | } 186 | -------------------------------------------------------------------------------- /src/test/java/yahoofinance/SimpleQuoteRequestTest.java: -------------------------------------------------------------------------------- 1 | package yahoofinance; 2 | 3 | import org.junit.Test; 4 | 5 | import yahoofinance.mock.MockedServersTest; 6 | 7 | import java.io.IOException; 8 | import java.math.BigDecimal; 9 | import java.util.Calendar; 10 | import java.util.TimeZone; 11 | 12 | import static org.junit.Assert.*; 13 | 14 | /** 15 | * 16 | * @author Stijn Strickx 17 | */ 18 | public class SimpleQuoteRequestTest extends MockedServersTest { 19 | 20 | @Test 21 | public void europeStockQuoteTest() throws IOException { 22 | Stock stock = YahooFinance.get("AIR.PA"); 23 | 24 | assertEquals("AIR.PA", stock.getSymbol()); 25 | assertEquals("AIRBUS GROUP", stock.getName()); 26 | assertEquals("EUR", stock.getCurrency()); 27 | assertEquals("PAR", stock.getStockExchange()); 28 | 29 | assertNotNull(stock.getQuote()); 30 | assertNotNull(stock.getStats()); 31 | assertNotNull(stock.getDividend()); 32 | 33 | assertEquals(new BigDecimal("54.93"), stock.getQuote().getAsk()); 34 | assertEquals(Long.valueOf(700), stock.getQuote().getAskSize()); 35 | assertEquals(new BigDecimal("54.00"), stock.getQuote().getBid()); 36 | assertEquals(Long.valueOf(42000), stock.getQuote().getBidSize()); 37 | assertEquals(new BigDecimal("50.34"), stock.getQuote().getPrice()); 38 | assertEquals(Long.valueOf(813), stock.getQuote().getLastTradeSize()); 39 | assertEquals(new BigDecimal("50.58"), stock.getQuote().getOpen()); 40 | assertEquals(new BigDecimal("51.00"), stock.getQuote().getPreviousClose()); 41 | 42 | assertEquals(new BigDecimal("50.10"), stock.getQuote().getDayLow()); 43 | assertEquals(new BigDecimal("50.85"), stock.getQuote().getDayHigh()); 44 | assertEquals(new BigDecimal("48.07"), stock.getQuote().getYearLow()); 45 | assertEquals(new BigDecimal("68.50"), stock.getQuote().getYearHigh()); 46 | assertEquals(new BigDecimal("51.81"), stock.getQuote().getPriceAvg50()); 47 | assertEquals(new BigDecimal("55.21"), stock.getQuote().getPriceAvg200()); 48 | 49 | assertEquals(Long.valueOf(1460112), stock.getQuote().getVolume()); 50 | assertEquals(Long.valueOf(2211770), stock.getQuote().getAvgVolume()); 51 | assertEquals("8/8/2016", stock.getQuote().getLastTradeDateStr()); 52 | assertEquals("5:35pm", stock.getQuote().getLastTradeTimeStr()); 53 | assertEquals(TimeZone.getTimeZone("Europe/Paris"), stock.getQuote().getTimeZone()); 54 | 55 | assertEquals(new BigDecimal("-0.66"), stock.getQuote().getChange()); 56 | assertEquals(new BigDecimal("-1.29"), stock.getQuote().getChangeInPercent()); 57 | assertEquals(new BigDecimal("-1.47"), stock.getQuote().getChangeFromAvg50()); 58 | assertEquals(new BigDecimal("-2.84"), stock.getQuote().getChangeFromAvg50InPercent()); 59 | assertEquals(new BigDecimal("-4.87"), stock.getQuote().getChangeFromAvg200()); 60 | assertEquals(new BigDecimal("-8.82"), stock.getQuote().getChangeFromAvg200InPercent()); 61 | assertEquals(new BigDecimal("-18.16"), stock.getQuote().getChangeFromYearHigh()); 62 | assertEquals(new BigDecimal("-26.51"), stock.getQuote().getChangeFromYearHighInPercent()); 63 | assertEquals(new BigDecimal("2.27"), stock.getQuote().getChangeFromYearLow()); 64 | assertEquals(new BigDecimal("4.72"), stock.getQuote().getChangeFromYearLowInPercent()); 65 | 66 | assertEquals(new BigDecimal("38880000000.00"), stock.getStats().getMarketCap()); 67 | assertEquals(Long.valueOf(654166000), stock.getStats().getSharesFloat()); 68 | assertEquals(Long.valueOf(772397000), stock.getStats().getSharesOutstanding()); 69 | assertEquals(new BigDecimal("3.74"), stock.getStats().getEps()); 70 | assertEquals(new BigDecimal("13.47"), stock.getStats().getPe()); 71 | assertEquals(new BigDecimal("0.00"), stock.getStats().getPeg()); 72 | assertEquals(new BigDecimal("0.83"), stock.getStats().getEpsEstimateCurrentYear()); 73 | assertEquals(new BigDecimal("0.00"), stock.getStats().getEpsEstimateNextQuarter()); 74 | assertNull(stock.getStats().getEpsEstimateNextYear()); 75 | assertEquals(new BigDecimal("6.51"), stock.getStats().getPriceBook()); 76 | assertEquals(new BigDecimal("0.61"), stock.getStats().getPriceSales()); 77 | assertEquals(new BigDecimal("7.84"), stock.getStats().getBookValuePerShare()); 78 | assertEquals(new BigDecimal("64310000000.00"), stock.getStats().getRevenue()); 79 | assertEquals(new BigDecimal("4800000000.00"), stock.getStats().getEBITDA()); 80 | assertNull(stock.getStats().getOneYearTargetPrice()); 81 | assertEquals(new BigDecimal("0.00"), stock.getStats().getShortRatio()); 82 | 83 | assertNull(stock.getDividend().getPayDate()); 84 | assertNull(stock.getDividend().getAnnualYield()); 85 | assertNull(stock.getDividend().getAnnualYieldPercent()); 86 | assertEquals(4, stock.getDividend().getExDate().get(Calendar.MONTH)); 87 | assertEquals(2, stock.getDividend().getExDate().get(Calendar.DAY_OF_MONTH)); 88 | } 89 | 90 | @Test 91 | public void usStockQuoteTest() throws IOException { 92 | Stock stock = YahooFinance.get("INTC"); 93 | 94 | assertEquals("INTC", stock.getSymbol()); 95 | assertEquals("Intel Corporation", stock.getName()); 96 | assertEquals("USD", stock.getCurrency()); 97 | assertEquals("NMS", stock.getStockExchange()); 98 | 99 | assertNotNull(stock.getQuote()); 100 | assertNotNull(stock.getStats()); 101 | assertNotNull(stock.getDividend()); 102 | 103 | /* 104 | * Just check a few to make sure everything is fine 105 | * Most things already tested by europeanStockQuoteTest 106 | */ 107 | assertEquals(new BigDecimal("35.03"), stock.getQuote().getAsk()); 108 | assertEquals(Long.valueOf(1699919), stock.getQuote().getLastTradeSize()); 109 | assertEquals(new BigDecimal("34.98"), stock.getQuote().getPreviousClose()); 110 | assertEquals(new BigDecimal("35.93"), stock.getQuote().getYearHigh()); 111 | 112 | assertEquals(new BigDecimal("2.80"), stock.getStats().getPeg()); 113 | assertEquals(new BigDecimal("37.61"), stock.getStats().getOneYearTargetPrice()); 114 | assertEquals(new BigDecimal("2.94"), stock.getStats().getShortRatio()); 115 | 116 | assertEquals(5, stock.getDividend().getPayDate().get(Calendar.MONTH)); 117 | assertEquals(1, stock.getDividend().getPayDate().get(Calendar.DAY_OF_MONTH)); 118 | assertEquals(7, stock.getDividend().getExDate().get(Calendar.MONTH)); 119 | assertEquals(3, stock.getDividend().getExDate().get(Calendar.DAY_OF_MONTH)); 120 | assertEquals(new BigDecimal("1.04"), stock.getDividend().getAnnualYield()); 121 | assertEquals(new BigDecimal("2.97"), stock.getDividend().getAnnualYieldPercent()); 122 | } 123 | 124 | @Test 125 | public void singaporeStockQuoteTest() throws IOException { 126 | Stock stock = YahooFinance.get("C6L.SI"); 127 | 128 | assertEquals("C6L.SI", stock.getSymbol()); 129 | assertEquals("SIA", stock.getName()); 130 | assertEquals("SGD", stock.getCurrency()); 131 | assertEquals("SES", stock.getStockExchange()); 132 | 133 | assertNotNull(stock.getQuote()); 134 | assertNotNull(stock.getStats()); 135 | assertNotNull(stock.getDividend()); 136 | 137 | assertEquals(new BigDecimal("10.89"), stock.getQuote().getAsk()); 138 | assertEquals(Long.valueOf(300), stock.getQuote().getLastTradeSize()); 139 | assertEquals(new BigDecimal("10.84"), stock.getQuote().getPreviousClose()); 140 | assertEquals(new BigDecimal("9.57"), stock.getQuote().getYearLow()); 141 | 142 | assertEquals(new BigDecimal("0.00"), stock.getStats().getPeg()); 143 | assertEquals(new BigDecimal("0.82"), stock.getStats().getEps()); 144 | assertEquals(new BigDecimal("11.44"), stock.getStats().getBookValuePerShare()); 145 | assertNull(stock.getStats().getOneYearTargetPrice()); 146 | assertEquals(new BigDecimal("0.00"), stock.getStats().getShortRatio()); 147 | 148 | assertEquals(7, stock.getDividend().getExDate().get(Calendar.MONTH)); 149 | assertEquals(2, stock.getDividend().getExDate().get(Calendar.DAY_OF_MONTH)); 150 | assertNull(stock.getDividend().getAnnualYield()); 151 | assertNull(stock.getDividend().getAnnualYieldPercent()); 152 | } 153 | 154 | } 155 | -------------------------------------------------------------------------------- /src/main/java/yahoofinance/histquotes2/CrumbManager.java: -------------------------------------------------------------------------------- 1 | package yahoofinance.histquotes2; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStreamReader; 6 | import java.io.OutputStreamWriter; 7 | import java.net.CookieHandler; 8 | import java.net.CookieManager; 9 | import java.net.CookieStore; 10 | import java.net.HttpCookie; 11 | import java.net.HttpURLConnection; 12 | import java.net.URL; 13 | import java.net.URLConnection; 14 | import java.net.URLEncoder; 15 | import java.util.HashMap; 16 | import java.util.List; 17 | import java.util.Map; 18 | import java.util.regex.Matcher; 19 | import java.util.regex.Pattern; 20 | 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | import yahoofinance.YahooFinance; 25 | import yahoofinance.util.RedirectableRequest; 26 | 27 | /** 28 | * Created by Stijn on 23/05/2017. 29 | */ 30 | public class CrumbManager { 31 | 32 | private static final Logger log = LoggerFactory.getLogger(CrumbManager.class); 33 | private static final String USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5.2 Safari/605.1.15"; 34 | 35 | private static String crumb = ""; 36 | private static String cookie = ""; 37 | 38 | private static void setCookie() throws IOException { 39 | if(YahooFinance.HISTQUOTES2_COOKIE != null && !YahooFinance.HISTQUOTES2_COOKIE.isEmpty()) { 40 | cookie = YahooFinance.HISTQUOTES2_COOKIE; 41 | log.debug("Set cookie from system property: {}", cookie); 42 | return; 43 | } 44 | 45 | URL request = new URL(YahooFinance.GET_COOKIE_URL); 46 | RedirectableRequest redirectableRequest = new RedirectableRequest(request, 5); 47 | redirectableRequest.setConnectTimeout(YahooFinance.CONNECTION_TIMEOUT); 48 | redirectableRequest.setReadTimeout(YahooFinance.CONNECTION_TIMEOUT); 49 | 50 | Map requestProperties = new HashMap<>(); 51 | requestProperties.put("Host", "fc.yahoo.com"); 52 | requestProperties.put("User-Agent", USER_AGENT); 53 | URLConnection connection = redirectableRequest.openConnection(requestProperties); 54 | 55 | Map> headerFields = connection.getHeaderFields(); 56 | List cookiesHeader = headerFields.get("Set-Cookie"); 57 | if (cookiesHeader != null) { 58 | setCookieFromHeaderValues(cookiesHeader); 59 | return; 60 | } else { 61 | log.warn("No Set-Cookie header found in the response"); 62 | } 63 | 64 | Map datas = new HashMap<>(); 65 | // If cookie is not set, we should consent to activate cookie 66 | try ( 67 | InputStreamReader is = new InputStreamReader(connection.getInputStream()); 68 | BufferedReader br = new BufferedReader(is)) { 69 | String line; 70 | Pattern patternPostForm = Pattern.compile("(.*)(action=\"/consent\")(.*)"); 71 | Pattern patternInput = Pattern.compile("(.*)()"); 72 | Matcher matcher; 73 | boolean postFind = false; 74 | // Read source to get params data for post request 75 | while ((line = br.readLine()) != null) { 76 | matcher = patternPostForm.matcher(line); 77 | if (matcher.find()) { 78 | postFind = true; 79 | } 80 | 81 | if (postFind) { 82 | matcher = patternInput.matcher(line); 83 | if (matcher.find()) { 84 | String name = matcher.group(3); 85 | String value = matcher.group(5); 86 | datas.put(name, value); 87 | } 88 | } 89 | 90 | } 91 | } 92 | // If params are not empty, send the post request 93 | if(!datas.isEmpty()){ 94 | 95 | datas.put("namespace",YahooFinance.HISTQUOTES2_COOKIE_NAMESPACE); 96 | datas.put("agree",YahooFinance.HISTQUOTES2_COOKIE_AGREE); 97 | datas.put("originalDoneUrl",YahooFinance.HISTQUOTES2_SCRAPE_URL); 98 | datas.put("doneUrl",YahooFinance.HISTQUOTES2_COOKIE_OATH_DONEURL+datas.get("sessionId")+"&inline="+datas.get("inline")+"&lang="+datas.get("locale")); 99 | 100 | URL requestOath = new URL(YahooFinance.HISTQUOTES2_COOKIE_OATH_URL); 101 | HttpURLConnection connectionOath; 102 | connectionOath = (HttpURLConnection) requestOath.openConnection(); 103 | connectionOath.setConnectTimeout(YahooFinance.CONNECTION_TIMEOUT); 104 | connectionOath.setReadTimeout(YahooFinance.CONNECTION_TIMEOUT); 105 | connectionOath.setRequestMethod( "POST" ); 106 | connectionOath.setDoOutput( true ); 107 | connectionOath.setRequestProperty("Referer", connection.getURL().toString()); 108 | connectionOath.setRequestProperty("Host",YahooFinance.HISTQUOTES2_COOKIE_OATH_HOST); 109 | connectionOath.setRequestProperty("Origin",YahooFinance.HISTQUOTES2_COOKIE_OATH_ORIGIN); 110 | connectionOath.setRequestProperty("Content-Type","application/x-www-form-urlencoded"); 111 | StringBuilder params=new StringBuilder(); 112 | 113 | for ( String key : datas.keySet() ) { 114 | if(params.length() == 0 ){ 115 | params.append(key); 116 | params.append("="); 117 | params.append(URLEncoder.encode(datas.get(key),"UTF-8")); 118 | }else{ 119 | params.append("&"); 120 | params.append(key); 121 | params.append("="); 122 | params.append(URLEncoder.encode(datas.get(key),"UTF-8")); 123 | 124 | } 125 | } 126 | 127 | 128 | log.debug("Params = "+ params.toString()); 129 | connectionOath.setRequestProperty("Content-Length",Integer.toString(params.toString().length())); 130 | try (OutputStreamWriter outputStreamWriter = new OutputStreamWriter(connectionOath.getOutputStream())) { 131 | outputStreamWriter.write(params.toString()); 132 | outputStreamWriter.flush(); 133 | connectionOath.setInstanceFollowRedirects(true); 134 | connectionOath.getResponseCode(); 135 | } 136 | } 137 | 138 | // Then Set the cookie with the cookieJar 139 | CookieStore cookieJar = ((CookieManager)CookieHandler.getDefault()).getCookieStore(); 140 | List cookies = cookieJar.getCookies(); 141 | for (HttpCookie hcookie: cookies) { 142 | if(hcookie.toString().matches("B=.*")) { 143 | cookie = hcookie.toString(); 144 | log.debug("Set cookie from http request: {}", cookie); 145 | return; 146 | } 147 | } 148 | 149 | log.warn("Failed to set cookie from http request. Historical quote requests will most likely fail"); 150 | } 151 | 152 | private static void setCookieFromHeaderValues(List cookiesHeader){ 153 | StringBuilder cookieBuilder = new StringBuilder(CrumbManager.cookie); 154 | for (String cookie : cookiesHeader) { 155 | log.debug("Set-Cookie: {}", cookie); 156 | if (cookieBuilder.length() > 0) { 157 | cookieBuilder.append("; "); 158 | } 159 | cookieBuilder.append(cookie); 160 | } 161 | CrumbManager.cookie = cookieBuilder.toString(); 162 | } 163 | 164 | private static void setCrumb() throws IOException { 165 | if(YahooFinance.HISTQUOTES2_CRUMB != null && !YahooFinance.HISTQUOTES2_CRUMB.isEmpty()) { 166 | crumb = YahooFinance.HISTQUOTES2_CRUMB; 167 | log.debug("Set crumb from system property: {}", crumb); 168 | return; 169 | } 170 | 171 | URL crumbRequest = new URL(YahooFinance.HISTQUOTES2_CRUMB_URL); 172 | RedirectableRequest redirectableCrumbRequest = new RedirectableRequest(crumbRequest, 5); 173 | redirectableCrumbRequest.setConnectTimeout(YahooFinance.CONNECTION_TIMEOUT); 174 | redirectableCrumbRequest.setReadTimeout(YahooFinance.CONNECTION_TIMEOUT); 175 | 176 | Map requestProperties = new HashMap<>(); 177 | requestProperties.put("Cookie", cookie); 178 | requestProperties.put("User-Agent", USER_AGENT); 179 | 180 | URLConnection crumbConnection = redirectableCrumbRequest.openConnection(requestProperties); 181 | try ( InputStreamReader is = new InputStreamReader(crumbConnection.getInputStream()); 182 | BufferedReader br = new BufferedReader(is)) { 183 | String crumbResult = br.readLine(); 184 | 185 | if (crumbResult != null && !crumbResult.isEmpty()) { 186 | crumb = crumbResult.trim(); 187 | log.debug("Set crumb from http request: {}", crumb); 188 | } else { 189 | log.warn("Failed to set crumb from http request. Historical quote requests will most likely fail."); 190 | } 191 | } 192 | } 193 | 194 | public static void refresh() throws IOException { 195 | setCookie(); 196 | setCrumb(); 197 | } 198 | 199 | public static synchronized String getCrumb() throws IOException { 200 | if(crumb == null || crumb.isEmpty()) { 201 | refresh(); 202 | } 203 | return crumb; 204 | } 205 | 206 | public static String getCookie() throws IOException { 207 | if(cookie == null || cookie.isEmpty()) { 208 | refresh(); 209 | } 210 | return cookie; 211 | } 212 | 213 | } 214 | -------------------------------------------------------------------------------- /src/main/java/yahoofinance/quotes/stock/StockQuote.java: -------------------------------------------------------------------------------- 1 | 2 | package yahoofinance.quotes.stock; 3 | 4 | import java.math.BigDecimal; 5 | import java.util.Calendar; 6 | import java.util.TimeZone; 7 | import yahoofinance.Utils; 8 | 9 | /** 10 | * All getters can return null in case the data is not available from Yahoo Finance. 11 | * 12 | * @author Stijn Strickx 13 | */ 14 | public class StockQuote { 15 | 16 | private final String symbol; 17 | 18 | private TimeZone timeZone; 19 | 20 | private BigDecimal ask; 21 | private Long askSize; 22 | private BigDecimal bid; 23 | private Long bidSize; 24 | private BigDecimal price; 25 | 26 | private Long lastTradeSize; 27 | private String lastTradeDateStr; 28 | private String lastTradeTimeStr; 29 | private Calendar lastTradeTime; 30 | 31 | private BigDecimal open; 32 | private BigDecimal previousClose; 33 | private BigDecimal dayLow; 34 | private BigDecimal dayHigh; 35 | 36 | private BigDecimal yearLow; 37 | private BigDecimal yearHigh; 38 | private BigDecimal priceAvg50; 39 | private BigDecimal priceAvg200; 40 | 41 | private Long volume; 42 | private Long avgVolume; 43 | 44 | public StockQuote(String symbol) { 45 | this.symbol = symbol; 46 | } 47 | 48 | /** 49 | * 50 | * @return difference between current price and previous close 51 | */ 52 | public BigDecimal getChange() { 53 | if(this.price == null || this.previousClose == null) { 54 | return null; 55 | } 56 | return this.price.subtract(this.previousClose); 57 | } 58 | 59 | /** 60 | * 61 | * @return change relative to previous close 62 | */ 63 | public BigDecimal getChangeInPercent() { 64 | return Utils.getPercent(this.getChange(), this.previousClose); 65 | } 66 | 67 | /** 68 | * 69 | * @return difference between current price and year low 70 | */ 71 | public BigDecimal getChangeFromYearLow() { 72 | if(this.price == null || this.yearLow == null) { 73 | return null; 74 | } 75 | return this.price.subtract(this.yearLow); 76 | } 77 | 78 | /** 79 | * 80 | * @return change from year low relative to year low 81 | */ 82 | public BigDecimal getChangeFromYearLowInPercent() { 83 | return Utils.getPercent(this.getChangeFromYearLow(), this.yearLow); 84 | } 85 | 86 | /** 87 | * 88 | * @return difference between current price and year high 89 | */ 90 | public BigDecimal getChangeFromYearHigh() { 91 | if(this.price == null || this.yearHigh == null) { 92 | return null; 93 | } 94 | return this.price.subtract(this.yearHigh); 95 | } 96 | 97 | /** 98 | * 99 | * @return change from year high relative to year high 100 | */ 101 | public BigDecimal getChangeFromYearHighInPercent() { 102 | return Utils.getPercent(this.getChangeFromYearHigh(), this.yearHigh); 103 | } 104 | 105 | /** 106 | * 107 | * @return difference between current price and 50 day moving average 108 | */ 109 | public BigDecimal getChangeFromAvg50() { 110 | if(this.price == null || this.priceAvg50 == null) { 111 | return null; 112 | } 113 | return this.price.subtract(this.priceAvg50); 114 | } 115 | 116 | /** 117 | * 118 | * @return change from 50 day moving average relative to 50 day moving average 119 | */ 120 | public BigDecimal getChangeFromAvg50InPercent() { 121 | return Utils.getPercent(this.getChangeFromAvg50(), this.priceAvg50); 122 | } 123 | 124 | /** 125 | * 126 | * @return difference between current price and 200 day moving average 127 | */ 128 | public BigDecimal getChangeFromAvg200() { 129 | if(this.price == null || this.priceAvg200 == null) { 130 | return null; 131 | } 132 | return this.price.subtract(this.priceAvg200); 133 | } 134 | 135 | /** 136 | * 137 | * @return change from 200 day moving average relative to 200 day moving average 138 | */ 139 | public BigDecimal getChangeFromAvg200InPercent() { 140 | return Utils.getPercent(this.getChangeFromAvg200(), this.priceAvg200); 141 | } 142 | 143 | public String getSymbol() { 144 | return symbol; 145 | } 146 | 147 | public BigDecimal getAsk() { 148 | return ask; 149 | } 150 | 151 | public void setAsk(BigDecimal ask) { 152 | this.ask = ask; 153 | } 154 | 155 | public Long getAskSize() { 156 | return askSize; 157 | } 158 | 159 | public void setAskSize(Long askSize) { 160 | this.askSize = askSize; 161 | } 162 | 163 | public BigDecimal getBid() { 164 | return bid; 165 | } 166 | 167 | public void setBid(BigDecimal bid) { 168 | this.bid = bid; 169 | } 170 | 171 | public Long getBidSize() { 172 | return bidSize; 173 | } 174 | 175 | public void setBidSize(Long bidSize) { 176 | this.bidSize = bidSize; 177 | } 178 | 179 | public BigDecimal getPrice() { 180 | return price; 181 | } 182 | 183 | public void setPrice(BigDecimal price) { 184 | this.price = price; 185 | } 186 | 187 | public Long getLastTradeSize() { 188 | return lastTradeSize; 189 | } 190 | 191 | public void setLastTradeSize(Long lastTradeSize) { 192 | this.lastTradeSize = lastTradeSize; 193 | } 194 | 195 | public String getLastTradeDateStr() { 196 | return lastTradeDateStr; 197 | } 198 | 199 | public void setLastTradeDateStr(String lastTradeDateStr) { 200 | this.lastTradeDateStr = lastTradeDateStr; 201 | } 202 | 203 | public String getLastTradeTimeStr() { 204 | return lastTradeTimeStr; 205 | } 206 | 207 | public void setLastTradeTimeStr(String lastTradeTimeStr) { 208 | this.lastTradeTimeStr = lastTradeTimeStr; 209 | } 210 | 211 | /** 212 | * Will derive the time zone from the exchange to parse the date time into a Calendar object. 213 | * This will not react to changes in the lastTradeDateStr and lastTradeTimeStr 214 | * 215 | * @return last trade date time 216 | */ 217 | public Calendar getLastTradeTime() { 218 | return lastTradeTime; 219 | } 220 | 221 | public void setLastTradeTime(Calendar lastTradeTime) { 222 | this.lastTradeTime = lastTradeTime; 223 | } 224 | 225 | /** 226 | * Will use the provided time zone to parse the date time into a Calendar object 227 | * Reacts to changes in the lastTradeDateStr and lastTradeTimeStr 228 | * 229 | * @param timeZone time zone where the stock is traded 230 | * @return last trade date time 231 | */ 232 | public Calendar getLastTradeTime(TimeZone timeZone) { 233 | return Utils.parseDateTime(this.lastTradeDateStr, this.lastTradeTimeStr, timeZone); 234 | } 235 | 236 | public TimeZone getTimeZone() { 237 | return timeZone; 238 | } 239 | 240 | public void setTimeZone(TimeZone timeZone) { 241 | this.timeZone = timeZone; 242 | } 243 | 244 | public BigDecimal getOpen() { 245 | return open; 246 | } 247 | 248 | public void setOpen(BigDecimal open) { 249 | this.open = open; 250 | } 251 | 252 | public BigDecimal getPreviousClose() { 253 | return previousClose; 254 | } 255 | 256 | public void setPreviousClose(BigDecimal previousClose) { 257 | this.previousClose = previousClose; 258 | } 259 | 260 | public BigDecimal getDayLow() { 261 | return dayLow; 262 | } 263 | 264 | public void setDayLow(BigDecimal dayLow) { 265 | this.dayLow = dayLow; 266 | } 267 | 268 | public BigDecimal getDayHigh() { 269 | return dayHigh; 270 | } 271 | 272 | public void setDayHigh(BigDecimal dayHigh) { 273 | this.dayHigh = dayHigh; 274 | } 275 | 276 | public BigDecimal getYearLow() { 277 | return yearLow; 278 | } 279 | 280 | public void setYearLow(BigDecimal yearLow) { 281 | this.yearLow = yearLow; 282 | } 283 | 284 | public BigDecimal getYearHigh() { 285 | return yearHigh; 286 | } 287 | 288 | public void setYearHigh(BigDecimal yearHigh) { 289 | this.yearHigh = yearHigh; 290 | } 291 | 292 | /** 293 | * 294 | * @return 50 day moving average 295 | */ 296 | public BigDecimal getPriceAvg50() { 297 | return priceAvg50; 298 | } 299 | 300 | public void setPriceAvg50(BigDecimal priceAvg50) { 301 | this.priceAvg50 = priceAvg50; 302 | } 303 | 304 | /** 305 | * 306 | * @return 200 day moving average 307 | */ 308 | public BigDecimal getPriceAvg200() { 309 | return priceAvg200; 310 | } 311 | 312 | public void setPriceAvg200(BigDecimal priceAvg200) { 313 | this.priceAvg200 = priceAvg200; 314 | } 315 | 316 | public Long getVolume() { 317 | return volume; 318 | } 319 | 320 | public void setVolume(Long volume) { 321 | this.volume = volume; 322 | } 323 | 324 | public Long getAvgVolume() { 325 | return avgVolume; 326 | } 327 | 328 | public void setAvgVolume(Long avgVolume) { 329 | this.avgVolume = avgVolume; 330 | } 331 | 332 | @Override 333 | public String toString() { 334 | return "Ask: " + this.ask + ", Bid: " + this.bid + ", Price: " + this.price + ", Prev close: " + this.previousClose; 335 | } 336 | 337 | } 338 | 339 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | de.sfuhrm 5 | YahooFinanceAPI 6 | 3.17.1-SNAPSHOT 7 | jar 8 | YahooFinanceAPI 9 | This library provides some methods that should make it easy 10 | to communicate with the Yahoo Finance API. 11 | It allows you to request detailed information, some statistics 12 | and historical quotes on stocks. 13 | Separate functionality is available to request a simple FX quote. 14 | Please check the javadoc to get a complete overview of the available methods 15 | and to get an idea of which data is available from Yahoo Finance. 16 | 17 | http://financequotes-api.com 18 | 19 | 20 | MIT License 21 | http://opensource.org/licenses/MIT 22 | 23 | 24 | 25 | 26 | Stijn Strickx 27 | api.yahoofinance@gmail.com 28 | YahooFinanceAPI 29 | http://financequotes-api.com 30 | 31 | 32 | Stephan Fuhrmann 33 | s@sfuhrm.de 34 | 35 | 36 | 37 | https://github.com/sstrickx/yahoofinance-api/issues 38 | GitHub Issues 39 | 40 | 41 | scm:git:git://github.com/sfuhrm/yahoofinance-api.git 42 | scm:git:git@github.com:sfuhrm/yahoofinance-api.git 43 | https://github.com/sfuhrm/yahoofinance-api 44 | HEAD 45 | 46 | 47 | UTF-8 48 | 8 49 | 8 50 | 51 | 52 | 53 | 54 | ossrh 55 | https://oss.sonatype.org/content/repositories/snapshots 56 | 57 | 58 | ossrh 59 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 60 | 61 | 62 | 63 | 64 | 65 | org.slf4j 66 | slf4j-api 67 | 1.7.36 68 | 69 | 70 | com.fasterxml.jackson.core 71 | jackson-databind 72 | 2.17.1 73 | 74 | 75 | org.slf4j 76 | slf4j-simple 77 | 1.7.36 78 | test 79 | 80 | 81 | junit 82 | junit 83 | 4.13.2 84 | test 85 | 86 | 87 | com.squareup.okhttp3 88 | mockwebserver 89 | 4.12.0 90 | test 91 | 92 | 93 | com.google.guava 94 | guava 95 | 33.2.1-jre 96 | test 97 | 98 | 99 | org.yaml 100 | snakeyaml 101 | 2.2 102 | test 103 | 104 | 105 | 106 | 107 | 108 | 109 | ${project.basedir} 110 | 111 | README.md 112 | LICENSE.txt 113 | 114 | 115 | 116 | 117 | 118 | org.apache.maven.plugins 119 | maven-release-plugin 120 | 3.1.0 121 | 122 | true 123 | release 124 | 125 | 126 | 127 | org.jacoco 128 | jacoco-maven-plugin 129 | 0.8.12 130 | 131 | 132 | 133 | prepare-agent 134 | 135 | 136 | 137 | generate-code-coverage-report 138 | test 139 | 140 | report 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | release 151 | 152 | 153 | ossrh 154 | https://oss.sonatype.org/content/repositories/snapshots 155 | 156 | 157 | 158 | 159 | 160 | org.sonatype.plugins 161 | nexus-staging-maven-plugin 162 | 1.6.13 163 | true 164 | 165 | ossrh 166 | https://oss.sonatype.org/ 167 | true 168 | 169 | 170 | 171 | org.apache.maven.plugins 172 | maven-gpg-plugin 173 | 3.2.3 174 | 175 | 9C3D5D0E75C71CE338930BF073FA841FE7DCB91F 176 | 177 | 178 | 179 | sign-artifacts 180 | verify 181 | 182 | sign 183 | 184 | 185 | 186 | 187 | 188 | org.apache.maven.plugins 189 | maven-jar-plugin 190 | 3.4.2 191 | 192 | 193 | 194 | true 195 | true 196 | 197 | 198 | ${project.version} 199 | ${basedir} 200 | 201 | 202 | 203 | 204 | 205 | org.apache.maven.plugins 206 | maven-source-plugin 207 | 3.0.1 208 | 209 | 210 | attach-sources 211 | 212 | jar 213 | 214 | 215 | 216 | 217 | 218 | org.apache.maven.plugins 219 | maven-javadoc-plugin 220 | 3.7.0 221 | 222 | 223 | attach-javadocs 224 | 225 | jar 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | -------------------------------------------------------------------------------- /src/main/java/yahoofinance/exchanges/ExchangeTimeZone.java: -------------------------------------------------------------------------------- 1 | 2 | package yahoofinance.exchanges; 3 | 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import java.util.TimeZone; 7 | 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | /** 12 | * 13 | * @author Stijn Strickx 14 | */ 15 | public class ExchangeTimeZone { 16 | 17 | private static final Logger log = LoggerFactory.getLogger(ExchangeTimeZone.class); 18 | 19 | public static final Map SUFFIX_TIMEZONES = new HashMap<>(); 20 | public static final Map INDEX_TIMEZONES = new HashMap<>(); 21 | 22 | static { 23 | SUFFIX_TIMEZONES.put("", TimeZone.getTimeZone("America/New_York")); 24 | SUFFIX_TIMEZONES.put("CBT", TimeZone.getTimeZone("America/New_York")); 25 | SUFFIX_TIMEZONES.put("CME", TimeZone.getTimeZone("America/New_York")); 26 | SUFFIX_TIMEZONES.put("NYB", TimeZone.getTimeZone("America/New_York")); 27 | SUFFIX_TIMEZONES.put("CMX", TimeZone.getTimeZone("America/New_York")); 28 | SUFFIX_TIMEZONES.put("NYM", TimeZone.getTimeZone("America/New_York")); 29 | SUFFIX_TIMEZONES.put("OB", TimeZone.getTimeZone("America/New_York")); 30 | SUFFIX_TIMEZONES.put("PK", TimeZone.getTimeZone("America/New_York")); 31 | SUFFIX_TIMEZONES.put("BA", TimeZone.getTimeZone("America/Buenos_Aires")); 32 | SUFFIX_TIMEZONES.put("VI", TimeZone.getTimeZone("Europe/Vienna")); 33 | SUFFIX_TIMEZONES.put("AX", TimeZone.getTimeZone("Australia/ACT")); 34 | SUFFIX_TIMEZONES.put("SA", TimeZone.getTimeZone("America/Sao_Paulo")); 35 | SUFFIX_TIMEZONES.put("TO", TimeZone.getTimeZone("America/Toronto")); 36 | SUFFIX_TIMEZONES.put("V", TimeZone.getTimeZone("America/Toronto")); 37 | SUFFIX_TIMEZONES.put("SN", TimeZone.getTimeZone("America/Santiago")); 38 | SUFFIX_TIMEZONES.put("SS", TimeZone.getTimeZone("Asia/Shanghai")); 39 | SUFFIX_TIMEZONES.put("SZ", TimeZone.getTimeZone("Asia/Shanghai")); 40 | SUFFIX_TIMEZONES.put("CO", TimeZone.getTimeZone("Europe/Copenhagen")); 41 | SUFFIX_TIMEZONES.put("NX", TimeZone.getTimeZone("Europe/Paris")); 42 | SUFFIX_TIMEZONES.put("PA", TimeZone.getTimeZone("Europe/Paris")); 43 | SUFFIX_TIMEZONES.put("BE", TimeZone.getTimeZone("Europe/Berlin")); 44 | SUFFIX_TIMEZONES.put("BM", TimeZone.getTimeZone("Europe/Berlin")); 45 | SUFFIX_TIMEZONES.put("DU", TimeZone.getTimeZone("Europe/Berlin")); 46 | SUFFIX_TIMEZONES.put("F", TimeZone.getTimeZone("Europe/Berlin")); 47 | SUFFIX_TIMEZONES.put("HM", TimeZone.getTimeZone("Europe/Berlin")); 48 | SUFFIX_TIMEZONES.put("HA", TimeZone.getTimeZone("Europe/Berlin")); 49 | SUFFIX_TIMEZONES.put("MU", TimeZone.getTimeZone("Europe/Berlin")); 50 | SUFFIX_TIMEZONES.put("SG", TimeZone.getTimeZone("Europe/Berlin")); 51 | SUFFIX_TIMEZONES.put("DE", TimeZone.getTimeZone("Europe/Berlin")); 52 | SUFFIX_TIMEZONES.put("IR", TimeZone.getTimeZone("Europe/Dublin")); 53 | SUFFIX_TIMEZONES.put("BR", TimeZone.getTimeZone("Europe/Brussels")); 54 | SUFFIX_TIMEZONES.put("HE", TimeZone.getTimeZone("Europe/Helsinki")); 55 | SUFFIX_TIMEZONES.put("HK", TimeZone.getTimeZone("Asia/Hong_Kong")); 56 | SUFFIX_TIMEZONES.put("BO", TimeZone.getTimeZone("Asia/Kolkata")); 57 | SUFFIX_TIMEZONES.put("NS", TimeZone.getTimeZone("Asia/Kolkata")); 58 | SUFFIX_TIMEZONES.put("JK", TimeZone.getTimeZone("Asia/Jakarta")); 59 | SUFFIX_TIMEZONES.put("TA", TimeZone.getTimeZone("Asia/Tel_Aviv")); 60 | SUFFIX_TIMEZONES.put("MI", TimeZone.getTimeZone("Europe/Rome")); 61 | SUFFIX_TIMEZONES.put("MX", TimeZone.getTimeZone("America/Mexico_City")); 62 | SUFFIX_TIMEZONES.put("AS", TimeZone.getTimeZone("Europe/Amsterdam")); 63 | SUFFIX_TIMEZONES.put("NZ", TimeZone.getTimeZone("Pacific/Auckland")); 64 | SUFFIX_TIMEZONES.put("OL", TimeZone.getTimeZone("Europe/Oslo")); 65 | SUFFIX_TIMEZONES.put("SI", TimeZone.getTimeZone("Asia/Singapore")); 66 | SUFFIX_TIMEZONES.put("KS", TimeZone.getTimeZone("Asia/Seoul")); 67 | SUFFIX_TIMEZONES.put("KQ", TimeZone.getTimeZone("Asia/Seoul")); 68 | SUFFIX_TIMEZONES.put("KL", TimeZone.getTimeZone("Asia/Kuala_Lumpur")); 69 | SUFFIX_TIMEZONES.put("BC", TimeZone.getTimeZone("Europe/Madrid")); 70 | SUFFIX_TIMEZONES.put("BI", TimeZone.getTimeZone("Europe/Madrid")); 71 | SUFFIX_TIMEZONES.put("MF", TimeZone.getTimeZone("Europe/Madrid")); 72 | SUFFIX_TIMEZONES.put("MC", TimeZone.getTimeZone("Europe/Madrid")); 73 | SUFFIX_TIMEZONES.put("MA", TimeZone.getTimeZone("Europe/Madrid")); 74 | SUFFIX_TIMEZONES.put("ST", TimeZone.getTimeZone("Europe/Stockholm")); 75 | SUFFIX_TIMEZONES.put("SW", TimeZone.getTimeZone("Europe/Zurich")); 76 | SUFFIX_TIMEZONES.put("Z", TimeZone.getTimeZone("Europe/Zurich")); 77 | SUFFIX_TIMEZONES.put("VX", TimeZone.getTimeZone("Europe/Zurich")); 78 | SUFFIX_TIMEZONES.put("TWO", TimeZone.getTimeZone("Asia/Taipei")); 79 | SUFFIX_TIMEZONES.put("TW", TimeZone.getTimeZone("Asia/Taipei")); 80 | SUFFIX_TIMEZONES.put("L", TimeZone.getTimeZone("Europe/London")); 81 | SUFFIX_TIMEZONES.put("PR", TimeZone.getTimeZone("Europe/Prague")); 82 | SUFFIX_TIMEZONES.put("ME", TimeZone.getTimeZone("Europe/Moscow")); 83 | SUFFIX_TIMEZONES.put("AT", TimeZone.getTimeZone("Europe/Athens")); 84 | SUFFIX_TIMEZONES.put("LS", TimeZone.getTimeZone("Europe/Lisbon")); 85 | 86 | INDEX_TIMEZONES.put("^FTSE", TimeZone.getTimeZone("Europe/London")); 87 | INDEX_TIMEZONES.put("^GDAXI", TimeZone.getTimeZone("Europe/Berlin")); 88 | INDEX_TIMEZONES.put("^FCHI", TimeZone.getTimeZone("Europe/Paris")); 89 | INDEX_TIMEZONES.put("^IBEX", TimeZone.getTimeZone("Europe/Madrid")); 90 | INDEX_TIMEZONES.put("^OMX", TimeZone.getTimeZone("Europe/Stockholm")); 91 | INDEX_TIMEZONES.put("^OSEAX", TimeZone.getTimeZone("Europe/Oslo")); 92 | INDEX_TIMEZONES.put("ATX", TimeZone.getTimeZone("America/New_York")); 93 | INDEX_TIMEZONES.put("^SSMI", TimeZone.getTimeZone("Europe/Zurich")); 94 | INDEX_TIMEZONES.put("^BFX", TimeZone.getTimeZone("Europe/Brussels")); 95 | INDEX_TIMEZONES.put("^DJI", TimeZone.getTimeZone("America/New_York")); 96 | INDEX_TIMEZONES.put("^OEX", TimeZone.getTimeZone("America/New_York")); 97 | INDEX_TIMEZONES.put("^NDX", TimeZone.getTimeZone("America/New_York")); 98 | INDEX_TIMEZONES.put("^BATSK", TimeZone.getTimeZone("America/New_York")); 99 | INDEX_TIMEZONES.put("^N225", TimeZone.getTimeZone("Asia/Tokyo")); 100 | INDEX_TIMEZONES.put("^HSI", TimeZone.getTimeZone("Asia/Hong_Kong")); 101 | INDEX_TIMEZONES.put("^STI", TimeZone.getTimeZone("Asia/Singapore")); 102 | INDEX_TIMEZONES.put("^AORD", TimeZone.getTimeZone("Australia/ACT")); 103 | INDEX_TIMEZONES.put("^BSESN", TimeZone.getTimeZone("Asia/Kolkata")); 104 | INDEX_TIMEZONES.put("^JKSE", TimeZone.getTimeZone("Asia/Jakarta")); 105 | INDEX_TIMEZONES.put("^KLSE", TimeZone.getTimeZone("Asia/Kuala_Lumpur")); 106 | INDEX_TIMEZONES.put("^NZ50", TimeZone.getTimeZone("Pacific/Auckland")); 107 | INDEX_TIMEZONES.put("^NSEI", TimeZone.getTimeZone("Asia/Kolkata")); 108 | INDEX_TIMEZONES.put("^KS11", TimeZone.getTimeZone("Asia/Seoul")); 109 | INDEX_TIMEZONES.put("^TWII", TimeZone.getTimeZone("Asia/Taipei")); 110 | INDEX_TIMEZONES.put("^MERV", TimeZone.getTimeZone("America/Buenos_Aires")); 111 | INDEX_TIMEZONES.put("^BVSP", TimeZone.getTimeZone("America/Sao_Paulo")); 112 | INDEX_TIMEZONES.put("^GSPTSE", TimeZone.getTimeZone("America/Toronto")); 113 | INDEX_TIMEZONES.put("^MXX", TimeZone.getTimeZone("America/Mexico_City")); 114 | INDEX_TIMEZONES.put("^GSPC", TimeZone.getTimeZone("America/New_York")); 115 | INDEX_TIMEZONES.put("^CCSI", TimeZone.getTimeZone("Africa/Cairo")); 116 | INDEX_TIMEZONES.put("^TA100", TimeZone.getTimeZone("Asia/Tel_Aviv")); 117 | INDEX_TIMEZONES.put("^FTMC", TimeZone.getTimeZone("Europe/London")); 118 | INDEX_TIMEZONES.put("^FTLC", TimeZone.getTimeZone("Europe/London")); 119 | INDEX_TIMEZONES.put("^FTAI", TimeZone.getTimeZone("Europe/London")); 120 | INDEX_TIMEZONES.put("^FTAS", TimeZone.getTimeZone("Europe/London")); 121 | INDEX_TIMEZONES.put("^FTSC", TimeZone.getTimeZone("Europe/London")); 122 | INDEX_TIMEZONES.put("^FTT1X", TimeZone.getTimeZone("Europe/London")); 123 | INDEX_TIMEZONES.put("^MID", TimeZone.getTimeZone("America/New_York")); 124 | INDEX_TIMEZONES.put("^SP600", TimeZone.getTimeZone("America/New_York")); 125 | INDEX_TIMEZONES.put("^SPSUPX", TimeZone.getTimeZone("America/New_York")); 126 | INDEX_TIMEZONES.put("^VIX", TimeZone.getTimeZone("America/New_York")); 127 | INDEX_TIMEZONES.put("^DJC", TimeZone.getTimeZone("America/New_York")); 128 | INDEX_TIMEZONES.put("^XAU", TimeZone.getTimeZone("America/New_York")); 129 | INDEX_TIMEZONES.put("^DJT", TimeZone.getTimeZone("America/New_York")); 130 | INDEX_TIMEZONES.put("^DJU", TimeZone.getTimeZone("America/New_York")); 131 | INDEX_TIMEZONES.put("^DJA", TimeZone.getTimeZone("America/New_York")); 132 | INDEX_TIMEZONES.put("^DWCF", TimeZone.getTimeZone("America/New_York")); 133 | INDEX_TIMEZONES.put("^DJU", TimeZone.getTimeZone("America/New_York")); 134 | INDEX_TIMEZONES.put("^IXIC", TimeZone.getTimeZone("America/New_York")); 135 | INDEX_TIMEZONES.put("^BANK", TimeZone.getTimeZone("America/New_York")); 136 | INDEX_TIMEZONES.put("^NBI", TimeZone.getTimeZone("America/New_York")); 137 | INDEX_TIMEZONES.put("^IXCO", TimeZone.getTimeZone("America/New_York")); 138 | INDEX_TIMEZONES.put("^IXF", TimeZone.getTimeZone("America/New_York")); 139 | INDEX_TIMEZONES.put("^INDS", TimeZone.getTimeZone("America/New_York")); 140 | INDEX_TIMEZONES.put("^INSR", TimeZone.getTimeZone("America/New_York")); 141 | INDEX_TIMEZONES.put("^OFIN", TimeZone.getTimeZone("America/New_York")); 142 | INDEX_TIMEZONES.put("^IXTC", TimeZone.getTimeZone("America/New_York")); 143 | INDEX_TIMEZONES.put("^TRAN", TimeZone.getTimeZone("America/New_York")); 144 | INDEX_TIMEZONES.put("^NYA", TimeZone.getTimeZone("America/New_York")); 145 | INDEX_TIMEZONES.put("^NYE", TimeZone.getTimeZone("America/New_York")); 146 | INDEX_TIMEZONES.put("^NYK", TimeZone.getTimeZone("America/New_York")); 147 | INDEX_TIMEZONES.put("^NYP", TimeZone.getTimeZone("America/New_York")); 148 | INDEX_TIMEZONES.put("^NYY", TimeZone.getTimeZone("America/New_York")); 149 | INDEX_TIMEZONES.put("^NYI", TimeZone.getTimeZone("America/New_York")); 150 | INDEX_TIMEZONES.put("^NY", TimeZone.getTimeZone("America/New_York")); 151 | INDEX_TIMEZONES.put("^NYL", TimeZone.getTimeZone("America/New_York")); 152 | INDEX_TIMEZONES.put("^XMI", TimeZone.getTimeZone("America/New_York")); 153 | INDEX_TIMEZONES.put("^XAX", TimeZone.getTimeZone("America/New_York")); 154 | INDEX_TIMEZONES.put("^BATSK", TimeZone.getTimeZone("America/New_York")); 155 | INDEX_TIMEZONES.put("^RUI", TimeZone.getTimeZone("America/New_York")); 156 | INDEX_TIMEZONES.put("^RUT", TimeZone.getTimeZone("America/New_York")); 157 | INDEX_TIMEZONES.put("^RUA", TimeZone.getTimeZone("America/New_York")); 158 | INDEX_TIMEZONES.put("^SOX", TimeZone.getTimeZone("America/New_York")); 159 | INDEX_TIMEZONES.put("^BKX", TimeZone.getTimeZone("America/New_York")); 160 | } 161 | 162 | /** 163 | * Get the time zone for a specific exchange suffix 164 | * 165 | * @param suffix suffix for the exchange in YahooFinance 166 | * @return time zone of the exchange 167 | */ 168 | public static TimeZone get(String suffix) { 169 | if(SUFFIX_TIMEZONES.containsKey(suffix)) { 170 | return SUFFIX_TIMEZONES.get(suffix); 171 | } 172 | log.warn("Cannot find time zone for exchange suffix: '{}'. Using default: America/New_York", suffix); 173 | return SUFFIX_TIMEZONES.get(""); 174 | } 175 | 176 | /** 177 | * Get the time zone for a specific stock or index. 178 | * For stocks, the exchange suffix is extracted from the stock symbol to retrieve the time zone. 179 | * 180 | * @param symbol stock symbol in YahooFinance 181 | * @return time zone of the exchange on which this stock is traded 182 | */ 183 | public static TimeZone getStockTimeZone(String symbol) { 184 | // First check if it's a known stock index 185 | if(INDEX_TIMEZONES.containsKey(symbol)) { 186 | return INDEX_TIMEZONES.get(symbol); 187 | } 188 | 189 | if(!symbol.contains(".")) { 190 | return ExchangeTimeZone.get(""); 191 | } 192 | String[] split = symbol.split("\\."); 193 | return ExchangeTimeZone.get(split[split.length - 1]); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/main/java/yahoofinance/Utils.java: -------------------------------------------------------------------------------- 1 | package yahoofinance; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.math.BigDecimal; 5 | import java.math.RoundingMode; 6 | import java.net.URLEncoder; 7 | import java.text.ParseException; 8 | import java.text.SimpleDateFormat; 9 | import java.util.Calendar; 10 | import java.util.Locale; 11 | import java.util.Map; 12 | import java.util.Map.Entry; 13 | import java.util.TimeZone; 14 | 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | 18 | /** 19 | * 20 | * @author Stijn Strickx 21 | */ 22 | public class Utils { 23 | 24 | private static final Logger log = LoggerFactory.getLogger(Utils.class); 25 | 26 | public static final BigDecimal HUNDRED = new BigDecimal(100); 27 | public static final BigDecimal THOUSAND = new BigDecimal(1000); 28 | public static final BigDecimal MILLION = new BigDecimal(1000000); 29 | public static final BigDecimal BILLION = new BigDecimal(1000000000); 30 | 31 | public static String join(String[] data, String d) { 32 | if (data.length == 0) { 33 | return ""; 34 | } 35 | StringBuilder sb = new StringBuilder(); 36 | int i; 37 | 38 | for (i = 0; i < (data.length - 1); i++) { 39 | sb.append(data[i]).append(d); 40 | } 41 | return sb.append(data[i]).toString(); 42 | } 43 | 44 | private static String cleanNumberString(String data) { 45 | return Utils.join(data.trim().split(","), ""); 46 | } 47 | 48 | private static boolean isParseable(String data) { 49 | return !(data == null || data.equals("N/A") || data.equals("-") 50 | || data.equals("") || data.equals("nan")); 51 | } 52 | 53 | public static String getString(String data) { 54 | if(!Utils.isParseable(data)) { 55 | return null; 56 | } 57 | return data; 58 | } 59 | 60 | public static BigDecimal getBigDecimal(String data) { 61 | BigDecimal result = null; 62 | if (!Utils.isParseable(data)) { 63 | return result; 64 | } 65 | try { 66 | data = Utils.cleanNumberString(data); 67 | char lastChar = data.charAt(data.length() - 1); 68 | BigDecimal multiplier = BigDecimal.ONE; 69 | switch (lastChar) { 70 | case 'B': 71 | data = data.substring(0, data.length() - 1); 72 | multiplier = BILLION; 73 | break; 74 | case 'M': 75 | data = data.substring(0, data.length() - 1); 76 | multiplier = MILLION; 77 | break; 78 | case 'K': 79 | data = data.substring(0, data.length() - 1); 80 | multiplier = THOUSAND; 81 | break; 82 | } 83 | result = new BigDecimal(data).multiply(multiplier); 84 | } catch (NumberFormatException e) { 85 | log.warn("Failed to parse: " + data); 86 | log.debug("Failed to parse: " + data, e); 87 | } 88 | return result; 89 | } 90 | 91 | public static BigDecimal getBigDecimal(String dataMain, String dataSub) { 92 | BigDecimal main = getBigDecimal(dataMain); 93 | BigDecimal sub = getBigDecimal(dataSub); 94 | if(main == null || main.compareTo(BigDecimal.ZERO) == 0) { 95 | return sub; 96 | } 97 | return main; 98 | } 99 | 100 | public static double getDouble(String data) { 101 | double result = Double.NaN; 102 | if (!Utils.isParseable(data)) { 103 | return result; 104 | } 105 | try { 106 | data = Utils.cleanNumberString(data); 107 | char lastChar = data.charAt(data.length() - 1); 108 | int multiplier = 1; 109 | switch (lastChar) { 110 | case 'B': 111 | data = data.substring(0, data.length() - 1); 112 | multiplier = 1000000000; 113 | break; 114 | case 'M': 115 | data = data.substring(0, data.length() - 1); 116 | multiplier = 1000000; 117 | break; 118 | case 'K': 119 | data = data.substring(0, data.length() - 1); 120 | multiplier = 1000; 121 | break; 122 | } 123 | result = Double.parseDouble(data) * multiplier; 124 | } catch (NumberFormatException e) { 125 | log.warn("Failed to parse: " + data); 126 | log.debug("Failed to parse: " + data, e); 127 | } 128 | return result; 129 | } 130 | 131 | public static Integer getInt(String data) { 132 | Integer result = null; 133 | if (!Utils.isParseable(data)) { 134 | return result; 135 | } 136 | try { 137 | data = Utils.cleanNumberString(data); 138 | result = Integer.parseInt(data); 139 | } catch (NumberFormatException e) { 140 | log.warn("Failed to parse: " + data); 141 | log.debug("Failed to parse: " + data, e); 142 | } 143 | return result; 144 | } 145 | 146 | public static Long getLong(String data) { 147 | Long result = null; 148 | if (!Utils.isParseable(data)) { 149 | return result; 150 | } 151 | try { 152 | data = Utils.cleanNumberString(data); 153 | result = Long.parseLong(data); 154 | } catch (NumberFormatException e) { 155 | log.warn("Failed to parse: " + data); 156 | log.debug("Failed to parse: " + data, e); 157 | } 158 | return result; 159 | } 160 | 161 | public static BigDecimal getPercent(BigDecimal numerator, BigDecimal denominator) { 162 | if (denominator == null || numerator == null || denominator.compareTo(BigDecimal.ZERO) == 0) { 163 | return BigDecimal.ZERO; 164 | } 165 | return numerator.divide(denominator, 4, RoundingMode.HALF_EVEN) 166 | .multiply(HUNDRED).setScale(2, RoundingMode.HALF_EVEN); 167 | } 168 | 169 | public static double getPercent(double numerator, double denominator) { 170 | if (denominator == 0) { 171 | return 0; 172 | } 173 | return (numerator / denominator) * 100; 174 | } 175 | 176 | private static String getDividendDateFormat(String date) { 177 | if (date.matches("[0-9][0-9]-...-[0-9][0-9]")) { 178 | return "dd-MMM-yy"; 179 | } else if (date.matches("[0-9]-...-[0-9][0-9]")) { 180 | return "d-MMM-yy"; 181 | } else if (date.matches("...[ ]+[0-9]+")) { 182 | return "MMM d"; 183 | } else { 184 | return "M/d/yy"; 185 | } 186 | } 187 | 188 | /** 189 | * Used to parse the dividend dates. Returns null if the date cannot be 190 | * parsed. 191 | * 192 | * @param date String received that represents the date 193 | * @return Calendar object representing the parsed date 194 | */ 195 | public static Calendar parseDividendDate(String date) { 196 | if (!Utils.isParseable(date)) { 197 | return null; 198 | } 199 | date = date.trim(); 200 | SimpleDateFormat format = new SimpleDateFormat(Utils.getDividendDateFormat(date), Locale.US); 201 | format.setTimeZone(TimeZone.getTimeZone(YahooFinance.TIMEZONE)); 202 | try { 203 | Calendar today = Calendar.getInstance(TimeZone.getTimeZone(YahooFinance.TIMEZONE)); 204 | Calendar parsedDate = Calendar.getInstance(TimeZone.getTimeZone(YahooFinance.TIMEZONE)); 205 | parsedDate.setTime(format.parse(date)); 206 | 207 | if (parsedDate.get(Calendar.YEAR) == 1970) { 208 | // Not really clear which year the dividend date is... making a reasonable guess. 209 | int monthDiff = parsedDate.get(Calendar.MONTH) - today.get(Calendar.MONTH); 210 | int year = today.get(Calendar.YEAR); 211 | if (monthDiff > 6) { 212 | year -= 1; 213 | } else if (monthDiff < -6) { 214 | year += 1; 215 | } 216 | parsedDate.set(Calendar.YEAR, year); 217 | } 218 | 219 | return parsedDate; 220 | } catch (ParseException ex) { 221 | log.warn("Failed to parse dividend date: " + date); 222 | log.debug("Failed to parse dividend date: " + date, ex); 223 | return null; 224 | } 225 | } 226 | 227 | /** 228 | * Used to parse the last trade date / time. Returns null if the date / time 229 | * cannot be parsed. 230 | * 231 | * @param date String received that represents the date 232 | * @param time String received that represents the time 233 | * @param timeZone time zone to use for parsing the date time 234 | * @return Calendar object with the parsed datetime 235 | */ 236 | public static Calendar parseDateTime(String date, String time, TimeZone timeZone) { 237 | String datetime = date + " " + time; 238 | SimpleDateFormat format = new SimpleDateFormat("M/d/yyyy h:mma", Locale.US); 239 | 240 | format.setTimeZone(timeZone); 241 | try { 242 | if (Utils.isParseable(date) && Utils.isParseable(time)) { 243 | Calendar c = Calendar.getInstance(); 244 | c.setTime(format.parse(datetime)); 245 | return c; 246 | } 247 | } catch (ParseException ex) { 248 | log.warn("Failed to parse datetime: " + datetime); 249 | log.debug("Failed to parse datetime: " + datetime, ex); 250 | } 251 | return null; 252 | } 253 | 254 | public static Calendar parseHistDate(String date) { 255 | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.US); 256 | try { 257 | if (Utils.isParseable(date)) { 258 | Calendar c = Calendar.getInstance(); 259 | c.setTime(format.parse(date)); 260 | return c; 261 | } 262 | } catch (ParseException ex) { 263 | log.warn("Failed to parse hist date: " + date); 264 | log.debug("Failed to parse hist date: " + date, ex); 265 | } 266 | return null; 267 | } 268 | 269 | public static Calendar unixToCalendar(long timestamp) { 270 | log.debug("unixToCalendar " + timestamp); 271 | Calendar calendar = Calendar.getInstance(); 272 | calendar.setTimeInMillis(timestamp * 1000); 273 | return calendar; 274 | } 275 | 276 | public static String getURLParameters(Map params) { 277 | StringBuilder sb = new StringBuilder(); 278 | 279 | for (Entry entry : params.entrySet()) { 280 | if (sb.length() > 0) { 281 | sb.append("&"); 282 | } 283 | String key = entry.getKey(); 284 | String value = entry.getValue(); 285 | try { 286 | key = URLEncoder.encode(key, "UTF-8"); 287 | value = URLEncoder.encode(value, "UTF-8"); 288 | } catch (UnsupportedEncodingException ex) { 289 | log.error(ex.getMessage(), ex); 290 | // Still try to continue with unencoded values 291 | } 292 | sb.append(String.format("%s=%s", key, value)); 293 | } 294 | return sb.toString(); 295 | } 296 | 297 | /** 298 | * Strips the unwanted chars from a line returned in the CSV 299 | * Used for parsing the FX CSV lines 300 | * 301 | * @param line the original CSV line 302 | * @return the stripped line 303 | */ 304 | public static String stripOverhead(String line) { 305 | return line.replaceAll("\"", ""); 306 | } 307 | 308 | public static String unescape(String data) { 309 | StringBuilder buffer = new StringBuilder(data.length()); 310 | for (int i = 0; i < data.length(); i++) { 311 | if ((int) data.charAt(i) > 256) { 312 | buffer.append("\\u").append(Integer.toHexString(data.charAt(i))); 313 | } else { 314 | if (data.charAt(i) == '\n') { 315 | buffer.append("\\n"); 316 | } else if (data.charAt(i) == '\t') { 317 | buffer.append("\\t"); 318 | } else if (data.charAt(i) == '\r') { 319 | buffer.append("\\r"); 320 | } else if (data.charAt(i) == '\b') { 321 | buffer.append("\\b"); 322 | } else if (data.charAt(i) == '\f') { 323 | buffer.append("\\f"); 324 | } else if (data.charAt(i) == '\'') { 325 | buffer.append("\\'"); 326 | } else if (data.charAt(i) == '\"') { 327 | buffer.append("\\\""); 328 | } else if (data.charAt(i) == '\\') { 329 | buffer.append("\\\\"); 330 | } else { 331 | buffer.append(data.charAt(i)); 332 | } 333 | } 334 | } 335 | return buffer.toString(); 336 | } 337 | 338 | } 339 | -------------------------------------------------------------------------------- /src/test/resources/historicalQuoteRequest/TSLA_1Y_D.csv: -------------------------------------------------------------------------------- 1 | Date,Open,High,Low,Close,Volume,Adj Close 2 | 2016-09-09,199.089996,199.919998,193.699997,194.470001,3742900,194.470001 3 | 2016-09-08,199.550003,199.889999,196.360001,197.360001,3370000,197.360001 4 | 2016-09-07,205.50,206.50,200.710007,201.710007,3636900,201.710007 5 | 2016-09-06,199.020004,203.25,199.00,202.830002,4383100,202.830002 6 | 2016-09-02,202.330002,203.199997,196.199997,197.779999,5962700,197.779999 7 | 2016-09-01,209.009995,211.100006,200.50,200.770004,7925100,200.770004 8 | 2016-08-31,210.429993,212.600006,208.649994,212.009995,3269400,212.009995 9 | 2016-08-30,216.110001,216.110001,210.520004,211.339996,3161200,211.339996 10 | 2016-08-29,220.149994,220.399994,215.00,215.199997,3257100,215.199997 11 | 2016-08-26,222.139999,222.860001,218.820007,219.990005,2225100,219.990005 12 | 2016-08-25,223.110001,223.800003,220.770004,220.960007,1756800,220.960007 13 | 2016-08-24,227.050003,227.149994,222.220001,222.619995,2564100,222.619995 14 | 2016-08-23,224.320007,228.490005,222.800003,224.839996,4745400,224.839996 15 | 2016-08-22,224.169998,225.110001,222.679993,222.929993,2060100,222.929993 16 | 2016-08-19,223.539993,225.169998,222.529999,225.00,1654500,225.00 17 | 2016-08-18,223.820007,225.660004,222.289993,223.509995,1699100,223.509995 18 | 2016-08-17,224.330002,224.830002,222.800003,223.240005,1770700,223.240005 19 | 2016-08-16,225.490005,227.190002,223.410004,223.610001,2247800,223.610001 20 | 2016-08-15,226.020004,229.50,224.929993,225.589996,2015800,225.589996 21 | 2016-08-12,225.410004,226.649994,224.039993,225.610001,1813500,225.610001 22 | 2016-08-11,226.169998,227.570007,223.410004,224.910004,1875800,224.910004 23 | 2016-08-10,228.240005,229.869995,224.619995,225.649994,2308400,225.649994 24 | 2016-08-09,226.820007,231.539993,226.649994,229.080002,2193900,229.080002 25 | 2016-08-08,228.00,229.600006,226.089996,226.160004,2263600,226.160004 26 | 2016-08-05,230.00,232.00,227.399994,230.029999,3205200,230.029999 27 | 2016-08-04,225.690002,230.860001,222.050003,230.610001,4147000,230.610001 28 | 2016-08-03,227.369995,229.699997,224.210007,225.789993,3887800,225.789993 29 | 2016-08-02,229.369995,229.869995,221.399994,227.199997,3934400,227.199997 30 | 2016-08-01,235.50,236.630005,229.380005,230.009995,4016300,230.009995 31 | 2016-07-29,230.699997,235.279999,230.240005,234.789993,3070800,234.789993 32 | 2016-07-28,227.949997,230.759995,226.600006,230.610001,2419100,230.610001 33 | 2016-07-27,229.339996,233.360001,226.919998,228.490005,2889000,228.490005 34 | 2016-07-26,227.690002,230.00,225.300003,229.509995,3430000,229.509995 35 | 2016-07-25,222.270004,231.389999,221.369995,230.009995,4490700,230.009995 36 | 2016-07-22,221.990005,224.50,218.880005,222.270004,2579700,222.270004 37 | 2016-07-21,226.00,227.850006,219.100006,220.50,4428700,220.50 38 | 2016-07-20,226.470001,229.800003,225.00,228.360001,2568500,228.360001 39 | 2016-07-19,225.00,229.100006,224.75,225.259995,3115100,225.259995 40 | 2016-07-18,219.639999,227.089996,218.300003,226.25,3412100,226.25 41 | 2016-07-15,222.520004,222.75,219.639999,220.399994,2234200,220.399994 42 | 2016-07-14,223.119995,224.940002,221.050003,221.529999,2675800,221.529999 43 | 2016-07-13,225.50,225.589996,220.289993,222.529999,3567100,222.529999 44 | 2016-07-12,224.100006,227.50,223.220001,224.649994,4571300,224.649994 45 | 2016-07-11,219.960007,226.779999,219.509995,224.779999,5429800,224.779999 46 | 2016-07-08,217.800003,219.809998,214.50,216.779999,4074800,216.779999 47 | 2016-07-07,213.100006,218.119995,213.009995,215.940002,3612000,215.940002 48 | 2016-07-06,210.00,215.229996,209.00,214.440002,4919900,214.440002 49 | 2016-07-05,209.729996,214.539993,208.00,213.979996,5175300,213.979996 50 | 2016-07-01,206.139999,218.240005,206.00,216.50,5400000,216.50 51 | 2016-06-30,212.970001,213.50,209.020004,212.279999,4843100,212.279999 52 | 2016-06-29,205.130005,211.779999,203.00,210.190002,5994900,210.190002 53 | 2016-06-28,201.889999,204.050003,199.410004,201.789993,6212400,201.789993 54 | 2016-06-27,190.860001,198.809998,187.869995,198.550003,7205400,198.550003 55 | 2016-06-24,190.050003,195.119995,189.729996,193.149994,7026500,193.149994 56 | 2016-06-23,195.690002,197.550003,192.130005,196.399994,10130700,196.399994 57 | 2016-06-22,199.470001,205.949997,195.75,196.660004,23742400,196.660004 58 | 2016-06-21,220.679993,222.570007,218.809998,219.610001,4529000,219.610001 59 | 2016-06-20,219.50,223.75,218.229996,219.699997,3555500,219.699997 60 | 2016-06-17,217.809998,219.990005,214.50,215.470001,3112600,215.470001 61 | 2016-06-16,217.419998,218.039993,213.50,217.929993,2440300,217.929993 62 | 2016-06-15,216.949997,221.899994,215.130005,217.699997,2908500,217.699997 63 | 2016-06-14,218.880005,222.199997,212.529999,214.960007,3580200,214.960007 64 | 2016-06-13,219.50,225.770004,217.660004,217.869995,4193000,217.869995 65 | 2016-06-10,227.389999,227.970001,218.419998,218.789993,6026600,218.789993 66 | 2016-06-09,234.979996,235.330002,227.059998,229.360001,4492100,229.360001 67 | 2016-06-08,233.800003,240.850006,232.610001,235.520004,5972000,235.520004 68 | 2016-06-07,222.240005,234.440002,221.520004,232.339996,6213600,232.339996 69 | 2016-06-06,218.00,220.899994,215.449997,220.679993,2249500,220.679993 70 | 2016-06-03,220.00,221.940002,218.009995,218.990005,2229000,218.990005 71 | 2016-06-02,219.589996,219.910004,217.110001,218.960007,2032800,218.960007 72 | 2016-06-01,221.479996,222.399994,216.889999,219.559998,2982700,219.559998 73 | 2016-05-31,223.039993,224.75,221.50,223.229996,2789000,223.229996 74 | 2016-05-27,224.990005,225.929993,220.75,223.039993,3650300,223.039993 75 | 2016-05-26,220.50,225.259995,219.050003,225.119995,4072400,225.119995 76 | 2016-05-25,217.910004,221.360001,216.509995,219.580002,3126800,219.580002 77 | 2016-05-24,216.600006,218.740005,215.179993,217.910004,3013800,217.910004 78 | 2016-05-23,219.869995,222.600006,215.860001,216.220001,5102500,216.220001 79 | 2016-05-20,216.990005,220.550003,216.350006,220.279999,9007100,220.279999 80 | 2016-05-19,213.619995,216.789993,207.300003,215.210007,6866300,215.210007 81 | 2016-05-18,209.149994,215.309998,207.75,211.169998,5617500,211.169998 82 | 2016-05-17,209.050003,209.820007,204.020004,204.660004,2843600,204.660004 83 | 2016-05-16,208.149994,213.149994,207.919998,208.289993,2949400,208.289993 84 | 2016-05-13,207.779999,211.199997,206.699997,207.610001,2822800,207.610001 85 | 2016-05-12,211.440002,211.669998,203.660004,207.279999,3650500,207.279999 86 | 2016-05-11,207.589996,215.479996,206.050003,208.960007,5161900,208.960007 87 | 2016-05-10,207.550003,209.470001,205.00,208.690002,4070600,208.690002 88 | 2016-05-09,215.720001,216.149994,206.800003,208.919998,4776400,208.919998 89 | 2016-05-06,210.869995,216.369995,208.110001,214.929993,5685200,214.929993 90 | 2016-05-05,228.460007,228.639999,209.789993,211.529999,11254800,211.529999 91 | 2016-05-04,230.289993,234.460007,220.399994,222.559998,8262500,222.559998 92 | 2016-05-03,237.360001,238.910004,231.619995,232.320007,4302200,232.320007 93 | 2016-05-02,241.50,243.190002,234.820007,241.800003,3843900,241.800003 94 | 2016-04-29,248.139999,248.429993,237.809998,240.759995,5413800,240.759995 95 | 2016-04-28,249.850006,253.429993,247.440002,247.710007,2519000,247.710007 96 | 2016-04-27,252.75,255.00,249.399994,251.470001,3205800,251.470001 97 | 2016-04-26,252.050003,255.729996,249.389999,253.740005,3223800,253.740005 98 | 2016-04-25,253.009995,257.380005,250.759995,251.820007,3670300,251.820007 99 | 2016-04-22,248.889999,254.00,245.710007,253.75,3786300,253.75 100 | 2016-04-21,248.990005,250.899994,246.910004,248.289993,2783100,248.289993 101 | 2016-04-20,246.259995,253.660004,241.50,249.970001,5194100,249.970001 102 | 2016-04-19,253.119995,254.369995,241.25,247.369995,6357500,247.369995 103 | 2016-04-18,252.229996,258.309998,251.660004,253.880005,4271400,253.880005 104 | 2016-04-15,251.309998,254.600006,249.119995,254.509995,3752400,254.509995 105 | 2016-04-14,253.00,256.839996,251.050003,251.860001,4132200,251.860001 106 | 2016-04-13,248.509995,255.50,247.330002,254.529999,4925600,254.529999 107 | 2016-04-12,249.50,251.800003,243.630005,247.820007,5763200,247.820007 108 | 2016-04-11,251.00,258.98999,245.300003,249.919998,9161700,249.919998 109 | 2016-04-08,260.50,260.820007,248.020004,250.070007,7363900,250.070007 110 | 2016-04-07,266.450012,269.339996,254.509995,257.200012,8856200,257.200012 111 | 2016-04-06,253.970001,267.73999,253.449997,265.420013,11705500,265.420013 112 | 2016-04-05,240.50,256.559998,240.00,255.470001,9948700,255.470001 113 | 2016-04-04,249.119995,252.119995,243.639999,246.990005,13475300,246.990005 114 | 2016-04-01,244.830002,247.899994,233.25,237.589996,15997500,237.589996 115 | 2016-03-31,229.339996,237.419998,225.009995,229.770004,8012900,229.770004 116 | 2016-03-30,235.089996,235.50,226.50,226.889999,4033000,226.889999 117 | 2016-03-29,229.889999,232.380005,225.330002,230.130005,4014300,230.130005 118 | 2016-03-28,231.610001,234.809998,225.00,230.259995,3925700,230.259995 119 | 2016-03-24,215.779999,228.889999,215.00,227.75,4960900,227.75 120 | 2016-03-23,232.369995,234.729996,222.029999,222.580002,4948800,222.580002 121 | 2016-03-22,237.210007,238.990005,232.559998,234.240005,4316000,234.240005 122 | 2016-03-21,235.339996,239.880005,235.00,238.320007,5307800,238.320007 123 | 2016-03-18,229.100006,234.479996,228.059998,232.740005,4711800,232.740005 124 | 2016-03-17,221.470001,228.50,220.00,226.380005,3782900,226.380005 125 | 2016-03-16,218.00,222.580002,217.020004,221.929993,3516700,221.929993 126 | 2016-03-15,214.270004,218.970001,211.50,218.339996,3180500,218.339996 127 | 2016-03-14,212.649994,216.720001,210.639999,215.149994,4065700,215.149994 128 | 2016-03-11,207.929993,209.419998,205.330002,207.50,3343100,207.50 129 | 2016-03-10,210.00,213.289993,200.669998,205.179993,5192500,205.179993 130 | 2016-03-09,204.520004,209.369995,202.789993,208.720001,3208600,208.720001 131 | 2016-03-08,203.50,207.50,202.199997,202.600006,4178700,202.600006 132 | 2016-03-07,197.679993,209.699997,197.399994,205.289993,5329400,205.289993 133 | 2016-03-04,198.00,204.029999,197.50,201.039993,6489100,201.039993 134 | 2016-03-03,188.279999,197.419998,184.220001,195.740005,4829000,195.740005 135 | 2016-03-02,183.729996,188.520004,181.50,188.339996,4862400,188.339996 136 | 2016-03-01,194.25,195.949997,182.699997,186.350006,6712200,186.350006 137 | 2016-02-29,192.399994,196.350006,189.220001,191.929993,4499000,191.929993 138 | 2016-02-26,188.699997,192.00,185.00,190.339996,6065100,190.339996 139 | 2016-02-25,178.649994,188.520004,175.199997,187.429993,5750700,187.429993 140 | 2016-02-24,172.75,179.50,167.839996,179.00,5395600,179.00 141 | 2016-02-23,176.160004,181.729996,173.679993,177.210007,5984400,177.210007 142 | 2016-02-22,170.119995,178.910004,169.850006,177.740005,5060100,177.740005 143 | 2016-02-19,163.660004,167.490005,162.50,166.580002,2959400,166.580002 144 | 2016-02-18,172.419998,172.949997,164.770004,166.770004,3887600,166.770004 145 | 2016-02-17,159.00,169.339996,156.679993,168.679993,5825200,168.679993 146 | 2016-02-16,158.699997,162.949997,154.110001,155.169998,5593800,155.169998 147 | 2016-02-12,155.00,157.009995,143.699997,151.039993,7235800,151.039993 148 | 2016-02-11,152.00,163.259995,147.00,150.470001,14252400,150.470001 149 | 2016-02-10,150.50,154.970001,141.740005,143.669998,10406500,143.669998 150 | 2016-02-09,142.320007,159.789993,141.050003,148.25,8651600,148.25 151 | 2016-02-08,157.100006,157.149994,146.00,147.990005,9313000,147.990005 152 | 2016-02-05,171.300003,173.00,157.740005,162.600006,9437600,162.600006 153 | 2016-02-04,170.699997,175.979996,166.990005,175.330002,4385400,175.330002 154 | 2016-02-03,183.589996,183.940002,170.179993,173.479996,7931400,173.479996 155 | 2016-02-02,192.419998,193.119995,180.229996,182.779999,5773600,182.779999 156 | 2016-02-01,188.759995,199.520004,182.75,196.940002,5297600,196.940002 157 | 2016-01-29,189.949997,193.740005,188.080002,191.199997,2852300,191.199997 158 | 2016-01-28,190.789993,191.279999,182.410004,189.699997,4592800,189.699997 159 | 2016-01-27,192.380005,193.259995,185.770004,188.070007,3617200,188.070007 160 | 2016-01-26,196.699997,197.820007,188.880005,193.559998,4964200,193.559998 161 | 2016-01-25,200.059998,203.570007,195.880005,196.380005,2698700,196.380005 162 | 2016-01-22,204.800003,205.50,199.029999,202.550003,3124100,202.550003 163 | 2016-01-21,201.550003,203.229996,195.020004,199.970001,3166200,199.970001 164 | 2016-01-20,199.399994,201.279999,191.25,198.699997,5838600,198.699997 165 | 2016-01-19,208.710007,210.470001,200.779999,204.720001,4038700,204.720001 166 | 2016-01-15,198.970001,205.070007,197.25,204.990005,5322200,204.990005 167 | 2016-01-14,202.210007,210.00,193.380005,206.179993,6490700,206.179993 168 | 2016-01-13,212.009995,212.649994,200.00,200.309998,4126400,200.309998 169 | 2016-01-12,211.600006,213.740005,205.309998,209.970001,3091900,209.970001 170 | 2016-01-11,214.009995,214.449997,203.00,207.850006,4091400,207.850006 171 | 2016-01-08,217.860001,220.440002,210.770004,211.00,3628100,211.00 172 | 2016-01-07,214.190002,218.440002,213.669998,215.649994,3554300,215.649994 173 | 2016-01-06,220.00,220.050003,215.979996,219.039993,3779100,219.039993 174 | 2016-01-05,226.360001,226.889999,220.00,223.429993,3186800,223.429993 175 | 2016-01-04,230.720001,231.380005,219.00,223.410004,6827100,223.410004 176 | 2015-12-31,238.509995,243.449997,238.369995,240.009995,2683200,240.009995 177 | 2015-12-30,236.600006,243.630005,235.669998,238.089996,3697900,238.089996 178 | 2015-12-29,230.059998,237.720001,229.550003,237.190002,2406300,237.190002 179 | 2015-12-28,231.490005,231.979996,225.539993,228.949997,1901300,228.949997 180 | 2015-12-24,230.559998,231.880005,228.279999,230.570007,708000,230.570007 181 | 2015-12-23,232.179993,233.449997,228.130005,229.699997,1555000,229.699997 182 | 2015-12-22,234.990005,236.550003,229.630005,229.949997,1961500,229.949997 183 | 2015-12-21,231.690002,235.830002,231.080002,232.559998,1953200,232.559998 184 | 2015-12-18,232.889999,235.899994,229.289993,230.460007,3014200,230.460007 185 | 2015-12-17,233.940002,237.759995,229.809998,233.389999,3298600,233.389999 186 | 2015-12-16,222.100006,234.880005,220.729996,234.509995,5104300,234.509995 187 | 2015-12-15,221.820007,222.220001,218.00,221.089996,2244400,221.089996 188 | 2015-12-14,217.509995,220.919998,214.869995,218.580002,2827100,218.580002 189 | 2015-12-11,225.240005,225.75,216.639999,217.020004,3268700,217.020004 190 | 2015-12-10,224.710007,228.490005,223.639999,227.070007,2067000,227.070007 191 | 2015-12-09,226.699997,227.50,220.720001,224.520004,3057800,224.520004 192 | 2015-12-08,227.520004,228.800003,224.199997,226.720001,2687600,226.720001 193 | 2015-12-07,227.699997,235.630005,226.149994,231.130005,3144200,231.130005 194 | 2015-12-04,232.460007,233.270004,227.660004,230.380005,2573600,230.380005 195 | 2015-12-03,235.479996,237.449997,230.00,232.710007,2939600,232.710007 196 | 2015-12-02,237.00,238.600006,231.229996,231.990005,2981500,231.990005 197 | 2015-12-01,231.059998,238.00,231.050003,237.190002,3734000,237.190002 198 | 2015-11-30,231.789993,234.279999,229.080002,230.259995,2659800,230.259995 199 | 2015-11-27,231.059998,232.25,227.009995,231.610001,1949400,231.610001 200 | 2015-11-25,221.339996,230.830002,220.380005,229.639999,3990800,229.639999 201 | 2015-11-24,215.369995,221.00,215.00,218.25,2480300,218.25 202 | 2015-11-23,217.350006,219.179993,214.679993,217.75,2526200,217.75 203 | 2015-11-20,223.490005,225.00,213.580002,220.009995,4400700,220.009995 204 | 2015-11-19,220.539993,226.190002,220.300003,221.800003,2504400,221.800003 205 | 2015-11-18,214.50,221.380005,212.520004,221.070007,2811900,221.070007 206 | 2015-11-17,215.199997,216.00,211.399994,214.00,2148700,214.00 207 | 2015-11-16,206.089996,214.979996,205.800003,214.309998,2925400,214.309998 208 | 2015-11-13,212.949997,212.990005,206.520004,207.190002,3430300,207.190002 209 | 2015-11-12,217.850006,219.00,212.660004,212.940002,2915900,212.940002 210 | 2015-11-11,217.770004,219.479996,213.630005,219.080002,3347800,219.080002 211 | 2015-11-10,223.479996,223.699997,216.080002,216.50,4617000,216.50 212 | 2015-11-09,232.990005,232.990005,224.309998,225.330002,3850900,225.330002 213 | 2015-11-06,230.699997,233.360001,229.50,232.360001,2445300,232.360001 214 | 2015-11-05,230.580002,234.580002,229.190002,231.770004,4496800,231.770004 215 | 2015-11-04,227.00,232.740005,225.199997,231.630005,12726400,231.630005 216 | 2015-11-03,213.850006,214.440002,207.75,208.350006,8332500,208.350006 217 | 2015-11-02,208.919998,215.800003,207.220001,213.789993,3927900,213.789993 218 | 2015-10-30,210.399994,211.630005,203.889999,206.929993,4438900,206.929993 219 | 2015-10-29,211.75,213.75,210.639999,211.630005,1805000,211.630005 220 | 2015-10-28,211.309998,213.449997,208.300003,212.960007,2728600,212.960007 221 | 2015-10-27,214.839996,217.100006,207.509995,210.350006,3519400,210.350006 222 | 2015-10-26,211.380005,215.880005,210.00,215.259995,3391400,215.259995 223 | 2015-10-23,215.00,215.350006,207.690002,209.089996,4235500,209.089996 224 | 2015-10-22,211.559998,215.75,209.399994,211.720001,2825200,211.720001 225 | 2015-10-21,211.990005,214.809998,208.800003,210.089996,4151500,210.089996 226 | 2015-10-20,227.720001,228.600006,202.00,213.029999,14863300,213.029999 227 | 2015-10-19,226.50,231.149994,224.940002,228.100006,2507900,228.100006 228 | 2015-10-16,223.039993,230.479996,222.869995,227.009995,4334500,227.009995 229 | 2015-10-15,216.429993,221.729996,213.699997,221.309998,2844200,221.309998 230 | 2015-10-14,220.669998,220.949997,215.429993,216.880005,3104400,216.880005 231 | 2015-10-13,213.279999,222.520004,211.130005,219.25,5171500,219.25 232 | 2015-10-12,222.990005,223.00,215.270004,215.580002,3836300,215.580002 233 | 2015-10-09,220.929993,224.369995,218.360001,220.690002,6158400,220.690002 234 | 2015-10-08,230.080002,230.720001,221.309998,226.720001,6133200,226.720001 235 | 2015-10-07,236.630005,237.699997,229.119995,231.960007,6814000,231.960007 236 | 2015-10-06,240.00,243.029999,235.580002,241.460007,5225200,241.460007 237 | 2015-10-05,248.839996,249.839996,244.130005,246.149994,3689900,246.149994 238 | 2015-10-02,235.600006,247.699997,234.929993,247.570007,4424000,247.570007 239 | 2015-10-01,247.509995,248.50,237.130005,239.880005,4573000,239.880005 240 | 2015-09-30,252.00,252.399994,242.339996,248.399994,4929600,248.399994 241 | 2015-09-29,250.460007,254.729996,245.460007,246.649994,3703200,246.649994 242 | 2015-09-28,257.350006,259.790009,246.610001,248.429993,4901100,248.429993 243 | 2015-09-25,266.609985,266.910004,256.149994,256.910004,3773400,256.910004 244 | 2015-09-24,259.529999,263.450012,256.209991,263.119995,3448200,263.119995 245 | 2015-09-23,261.950012,262.079987,257.579987,261.059998,2600800,261.059998 246 | 2015-09-22,259.029999,262.649994,255.869995,260.940002,3664400,260.940002 247 | 2015-09-21,263.980011,271.570007,255.800003,264.200012,6120200,264.200012 248 | 2015-09-18,257.959991,263.820007,257.50,260.619995,3763100,260.619995 249 | 2015-09-17,263.959991,265.50,260.690002,262.070007,3585800,262.070007 250 | 2015-09-16,253.039993,262.880005,252.880005,262.25,4417100,262.25 251 | 2015-09-15,252.75,254.600006,249.50,253.570007,2933500,253.570007 252 | 2015-09-14,251.100006,254.25,249.669998,253.190002,2890900,253.190002 253 | 2015-09-11,247.639999,250.240005,244.729996,250.240005,2350800,250.240005 254 | --------------------------------------------------------------------------------