/*
* OpenTrader Trading Platform
* The solution for online trading, technical analysis and automated trading.
*
* Copyright (C) 2010-2011 Andrey Pudov
* Andrey Pudov <syscreat@gmail.com>
*
* http://opentrader.github.com/
*/
/*
* CDDL HEADER START
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2010 Andrey Pudov. All rights reserved.
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2010 Andrey Pudov. All rights reserved.
* Use is subject to license terms.
*
* Contributor(s):
*
* Portions Copyrighted 2010 Andrey Pudov.
*
*/
package com.external.yahooprovider;
import com.opentrader.market.feeds.DefaultExchangeProvider;
import com.opentrader.market.feeds.Historic;
import com.opentrader.market.feeds.StockExchange;
import com.opentrader.market.feeds.Symbol;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.StringTokenizer;
import java.util.logging.Logger;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* @author Andrey Pudov <syscreat@gmail.com>
* @version 0.00.00
* %name YahooProvider.java
* %pkg com.external.yahooprovider
* %date 12:00:15 PM, Nov 10, 2010
*/
public class YahooProvider implements DefaultExchangeProvider {
private static final long serialVersionUID = 1060606745559226566L;
private static final Logger LOG = Logger.getLogger("yahooprovider");
private static final int CONNECT_TIME_SECONDS = 2;
private static final int READ_TIME_SECONDS = 3;
private static final String provider = "http://finance.yahoo.com/d/quotes.csv";
private static final String historic = "http://ichart.yahoo.com/table.csv";
private static enum Format {BASIC}
/** Yahoo! Volume Leaders http://finance.yahoo.com/actives?e=us */
private ArrayList<StockExchange> stocks = new ArrayList<StockExchange>(20);
private ArrayList<Symbol> selectedSymbols = new ArrayList<Symbol>(20);
public YahooProvider() {
InputStream in = YahooProvider.class.getResourceAsStream(
"/com/external/yahooprovider/resources/volumeleaders.xml"
);
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
class StockHadler extends DefaultHandler {
private ArrayList<Symbol> symbols = new ArrayList<Symbol>(10);
private String StockExchange;
@Override
public void startElement(String uri, String localName,
String qName, Attributes attributes)
throws SAXException {
if (qName.equals("stockexchange")) {
for(int i = 0; i < attributes.getLength(); i++) {
if (attributes.getQName(i).equals("name")) {
StockExchange = attributes.getValue(i);
}
}
} else if (qName.equals("symbol")) {
String code = new String();
String description = new String();
for(int i = 0; i < attributes.getLength(); i++) {
if (attributes.getQName(i).equals("code")) {
code = attributes.getValue(i);
} else if (attributes.getQName(i).equals("description")) {
description = attributes.getValue(i);
}
}
symbols.add(new YSymbol(code, description));
//selectedSymbols.add(new YSymbol(code, description));
}
}
@Override
public void endElement(String uri, String localName,
String qName)
throws SAXException {
if (qName.equals("stockexchange")) {
stocks.add(new YStockExchange(StockExchange, symbols));
symbols = new ArrayList<Symbol>(10);
}
}
}
StockHadler handler = new StockHadler();
saxParser.parse(in, handler);
} catch(org.xml.sax.SAXException e) {
LOG.warning(e.getMessage());
} catch(javax.xml.parsers.ParserConfigurationException e) {
LOG.warning(e.getMessage());
} catch(java.io.IOException e) {
LOG.warning(e.getMessage());
}
}
public void connect() {
URL url;
URLConnection urlConn;
BufferedReader br = null;
try {
url = new URL(provider);
urlConn = url.openConnection();
urlConn.setConnectTimeout(CONNECT_TIME_SECONDS * 1000);
urlConn.setReadTimeout(READ_TIME_SECONDS * 1000);
urlConn.setDoInput(true);
urlConn.setUseCaches(false);
br = new BufferedReader(
new InputStreamReader(urlConn.getInputStream()));
String s;
while ((s = br.readLine()) != null) {
//System.out.println(s);
}
} catch (java.net.MalformedURLException e) {
LOG.warning(e.toString());
} catch (java.io.IOException e) {
LOG.warning(e.toString());
} finally {
try {
br.close();
} catch(java.io.IOException e) {
LOG.warning(e.toString());
}
}
try {
updateSymbols(selectedSymbols, Format.BASIC);
} catch (java.text.ParseException e) {
LOG.warning(e.toString());
} catch (Exception e) {
LOG.warning(e.toString());
}
}
public List<StockExchange> getStockExchanges() {
return Collections.unmodifiableList(stocks);
}
public List<Symbol> getSelectedSymbols() {
try {
updateSymbols(selectedSymbols, Format.BASIC);
} catch (java.text.ParseException e) {
LOG.warning(e.toString());
} catch (Exception e) {
LOG.warning(e.toString());
}
return Collections.unmodifiableList(selectedSymbols);
}
public List<Symbol> getSymbolsList(StockExchange stock) {
return stocks.get(stocks.indexOf(stock)).getSymbols();
}
/**
* http://ichart.finance.yahoo.com/table.csv?s=STOCK
*
* @param symbol
* STOCKĀ is the ticker symbol
* You can limit what that returns with some additional parameters:
* s - Ticker symbol. This is the only parameter that isn't optional.
* @param start
* Start date for historical prices:
* a - Month number, starting with 0 for January.
* b - Day number, eg, 1 for the first of the month.
* c - Year.
* @param end
* End date for historical prices (default is the most current
* available closing price):
* d - Month number, starting with 0 for January.
* e - Day number, eg, 1 for the first of the month.
* f - Year *
* @param ival
* And finally, the frequency of historical prices:
* g - Possible values are 'd' for daily (the default), 'w' for weekly,
* and 'm' for monthly.
*
* @return
* 'GOOG', "20100101", "20101201"
* http://ichart.yahoo.com/table.csv?s=GOOG&d=11&e=1&f=2010&g=d&a=0&b=1&c=2010&ignore=.csv
*
* 'GOOG', "20090131", "20101201"
* http://ichart.yahoo.com/table.csv?s=GOOG&d=11&e=1&f=2010&g=d&a=0&b=31&c=2009&ignore=.csv
*/
public List<Historic> getHistoricalPrices(Symbol symbol, Date start, Date end, Interval ival) {
ArrayList<Historic> list = new ArrayList<Historic>(20);
URL url;
URLConnection urlConn;
BufferedReader br = null;
try {
/* a - Month number, starting with 0 for January. */
String a = Integer.toString(Integer.parseInt(new SimpleDateFormat("MM").format(start)) - 1);
/* b - Day number, eg, 1 for the first of the month. */
String b = Integer.toString(Integer.parseInt(new SimpleDateFormat("dd").format(start)));
/* c - Year. */
String c = Integer.toString(Integer.parseInt(new SimpleDateFormat("yyyy").format(start)));
/* d - Month number, starting with 0 for January. */
String d = Integer.toString(Integer.parseInt(new SimpleDateFormat("MM").format(end)) - 1);
/* e - Day number, eg, 1 for the first of the month. */
String e = Integer.toString(Integer.parseInt(new SimpleDateFormat("dd").format(end)));
/* f - Year. */
String f = Integer.toString(Integer.parseInt(new SimpleDateFormat("yyyy").format(end)));
/* frequency of historical prices */
String g;
if (ival == Interval.DAYLY) {
g = "d";
} else if (ival == Interval.WEEKLY) {
g = "w";
} else if (ival == Interval.MONTHLY) {
g = "m";
} else {
g = "d";
}
url = new URL(
historic +
"?s=" + symbol.getCode() +
"&d=" + d + "&e=" + e + "&f=" + f +
"&g=" + g +
"&a=" + a + "&b=" + b + "&c=" + c +
"&ignore=.csv");
urlConn = url.openConnection();
urlConn.setConnectTimeout(CONNECT_TIME_SECONDS * 1000);
urlConn.setReadTimeout(READ_TIME_SECONDS * 1000);
urlConn.setDoInput(true);
urlConn.setUseCaches(false);
br = new BufferedReader(
new InputStreamReader(urlConn.getInputStream()));
YHistoric historyValue;
StringTokenizer st;
String s;
/* read title - Date,Open,High,Low,Close,Volume,Adj Close */
br.readLine();
while ((s = br.readLine()) != null) {
// 2010-12-01,563.00,571.57,562.40,564.35,7508200,564.35
historyValue = new YHistoric();
st = new StringTokenizer(s, ",");
historyValue.setDate(
new SimpleDateFormat("yyyy-MM-dd").parse(
st.nextToken()).getTime());
historyValue.setOpen(Double.parseDouble(st.nextToken()));
historyValue.setHigh(Double.parseDouble(st.nextToken()));
historyValue.setLow(Double.parseDouble(st.nextToken()));
historyValue.setClose(Double.parseDouble(st.nextToken()));
historyValue.setVolume(Double.parseDouble(st.nextToken()));
historyValue.setAdjClose(Double.parseDouble(st.nextToken()));
list.add(historyValue);
}
} catch (java.net.MalformedURLException e) {
LOG.warning(e.toString());
} catch (java.io.IOException e) {
LOG.warning(e.toString());
} catch (java.text.ParseException e) {
LOG.warning(e.toString());
}finally {
try {
br.close();
} catch(java.io.IOException e) {
LOG.warning(e.toString());
}
}
return Collections.unmodifiableList(list);
}
public List<Symbol> getPortfolio() {
return Collections.unmodifiableList(selectedSymbols);
}
public void setPortfolio(List<Symbol> selectedSymbols) {
this.selectedSymbols.addAll(selectedSymbols);
}
public static void main(String[] args) {
ArrayList <Symbol> symbols = new ArrayList<Symbol>(10);
symbols.add(new YSymbol("C", ""));
symbols.add(new YSymbol("YHOO", ""));
symbols.add(new YSymbol("MSFT", ""));
try {
YahooProvider yprovider = new YahooProvider();
yprovider.setPortfolio(symbols);
yprovider.connect();
for (Historic hvalue : yprovider.getHistoricalPrices(
new YSymbol("GOOG", ""),
new SimpleDateFormat("yyyy/MM/dd").parse("2010/01/01"),
new SimpleDateFormat("yyyy/MM/dd").parse("2010/12/01"),
Interval.MONTHLY)) {
System.out.println(hvalue.toString());
}
} catch (Exception e) {
}
}
private void updateSymbols(List<Symbol> selectedSymbols, Format format)
throws ParseException {
URL url;
URLConnection urlConn;
BufferedReader br = null;
StringBuilder sb = new StringBuilder(20);
int count = 0;
for (Symbol s : selectedSymbols) {
sb.append(s.getCode());
if (++count != selectedSymbols.size()) {
sb.append('+');
}
}
selectedSymbols.clear();
String symbols = sb.toString();
String options = "snl1d1t1c1p2vx";
switch (format) {
case BASIC:
/**
* s Symbol
* n Name
* a Ask //
* b Bid //
* l1 Last Trade (Price Only)
* d1 Last Trade Date
* t1 Last Trade Time
* c1 Change (c1 + p2 = c)
* p2 Change in Percent (c1 + p2 = c)
* v Volume
* x Stock Exchange
*/
options = "snabl1d1t1c1p2vx";
break;
default:
options = "snabl1d1t1c1p2vx";
break;
}
try {
url = new URL(provider + "?s=" + symbols + "&f=" + options);
urlConn = url.openConnection();
urlConn.setConnectTimeout(CONNECT_TIME_SECONDS * 1000);
urlConn.setReadTimeout(READ_TIME_SECONDS * 1000);
urlConn.setDoInput(true);
urlConn.setUseCaches(false);
br = new BufferedReader(
new InputStreamReader(urlConn.getInputStream()));
YSymbol symbol;
/**
* G Era designator AD
* y Year 1996; 96
* M Month in year July; Jul; 07
* w Week in year 27
* W Week in month 2
* D Day in year 189
* d Day in month 10
* F Day of week in month 2
* E Day in week Tuesday; Tue
* a Am/pm marker PM
* H Hour in day (0-23) 0
* k Hour in day (1-24) 24
* K Hour in am/pm (0-11) 0
* h Hour in am/pm (1-12) 12
* m Minute in hour 30
* s Second in minute 55
* S Millisecond 978
* z Time zone Pacific Standard Time; PST; GMT-08:00
* Z Time zone -0800
*/
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy hh:mma");
String s;
while ((s = br.readLine()) != null) {
//System.out.println(s);
switch (format) {
case BASIC:
// ""C","Citigroup, Inc. C",N/A,N/A,4.80,"2/10/2011",
// "10:30am",-0.04,"-0.83%",126972024,"NYSE""
/* Replace all Yahoo! NOVALUE with standard impl. */
s = s.replaceAll("N/A", "-1");
String otherThanQuote = " [^\"] ";
String quotedString = String.format(" \" %s* \" ", otherThanQuote);
String regex = String.format("(?x) "+ // enable comments, ignore white spaces
", "+ // match a comma
"(?= "+ // start positive look ahead
" ( "+ // start group 1
" %s* "+ // match 'otherThanQuote' zero or more times
" %s "+ // match 'quotedString'
" )* "+ // end group 1 and repeat it zero or more times
" %s* "+ // match 'otherThanQuote'
" $ "+ // match the end of the string
") ", // stop positive look ahead
otherThanQuote, quotedString, otherThanQuote);
String[] tokens = s.split(regex);
symbol = new YSymbol(
tokens[0].substring(1, tokens[0].length() - 1),
tokens[1].substring(1, tokens[1].length() - 1));
symbol.setAsk(Double.parseDouble(tokens[2]));
symbol.setBid(Double.parseDouble(tokens[3]));
symbol.setLastTradePrice(Double.parseDouble(tokens[4]));
symbol.setLastTradeDateAndTime(
sdf.parse(
tokens[5].substring(1, tokens[5].length() - 1) +
" " +
tokens[6].substring(1, tokens[6].length() - 1)
).getTime());
symbol.setChange(Double.parseDouble(tokens[7]));
symbol.setChangeInPercent(
Double.parseDouble(
tokens[8].substring(
1,
tokens[8].length() - 2)));
symbol.setVolume(Long.parseLong(tokens[9]));
symbol.setStockExchange(
tokens[10].substring(
1, tokens[10].length() - 1));
selectedSymbols.add(symbol);
break;
default:
break;
}
}
} catch (java.net.MalformedURLException e) {
LOG.warning(e.toString());
} catch (java.io.IOException e) {
LOG.warning(e.toString());
} finally {
try {
br.close();
} catch(java.io.IOException e) {
LOG.warning(e.toString());
}
}
}
}