├── stokker ├── .gitignore ├── .settings │ ├── microservice-metadata.prefs │ └── .gitignore ├── src │ ├── main │ │ ├── java │ │ │ └── org │ │ │ │ └── vferrer │ │ │ │ └── stokker │ │ │ │ ├── elk │ │ │ │ ├── StockQuotationRepository.java │ │ │ │ ├── StockQuotationJPARepository.java │ │ │ │ ├── converter │ │ │ │ │ ├── IStockQuotationConverter.java │ │ │ │ │ └── CSVStockQuotationConverter.java │ │ │ │ ├── StockQuotationJPA.java │ │ │ │ ├── controller │ │ │ │ │ └── StockPriceController.java │ │ │ │ ├── StockQuotation.java │ │ │ │ └── ELKClient.java │ │ │ │ ├── feeder │ │ │ │ └── csv │ │ │ │ │ ├── CSVLineSplitter.java │ │ │ │ │ ├── HistoricalCSVLineSplitter.java │ │ │ │ │ └── HistoricalCSVRequestCreator.java │ │ │ │ └── StokkerApplication.java │ │ ├── resources │ │ │ ├── application.properties │ │ │ ├── application.yml │ │ │ ├── META-INF │ │ │ │ ├── feederFlow.xml │ │ │ │ ├── liveQuotations.xml │ │ │ │ └── historicalQuotations.xml │ │ │ └── stockTickers.csv │ │ └── docker │ │ │ └── Dockerfile │ └── test │ │ └── java │ │ └── org │ │ └── vferrer │ │ └── stokker │ │ ├── StokkerApplicationTests.java │ │ └── feeder │ │ └── csv │ │ └── StockQuotationConverterTest.java └── pom.xml ├── deployment_model.PNG ├── sample dashboard.PNG ├── Procfile ├── .travis.yml ├── pom.xml └── README.md /stokker/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.classpath 3 | /.project 4 | /data/ 5 | -------------------------------------------------------------------------------- /deployment_model.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victor-ferrer/stokker/HEAD/deployment_model.PNG -------------------------------------------------------------------------------- /sample dashboard.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victor-ferrer/stokker/HEAD/sample dashboard.PNG -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: java $JAVA_OPTS -Dserver.port=$PORT -Dspring.profiles.active=heroku -jar stokker/target/*.jar 2 | -------------------------------------------------------------------------------- /stokker/.settings/microservice-metadata.prefs: -------------------------------------------------------------------------------- 1 | default.request-mapping.path= 2 | eclipse.preferences.version=1 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | before_script: 2 | - "echo $JAVA_OPTS" 3 | - "export JAVA_OPTS=-Xmx512m" 4 | - "export JAVA_OPTS=-XX:PermSize=256m" 5 | language: java 6 | jdk: 7 | - oraclejdk8 8 | deploy: 9 | provider: heroku 10 | api-key: acf393bc-f94f-4124-8d19-a2101c1acfe2 11 | -------------------------------------------------------------------------------- /stokker/.settings/.gitignore: -------------------------------------------------------------------------------- 1 | /com.springsource.sts.config.flow.prefs 2 | /org.eclipse.core.resources.prefs 3 | /org.eclipse.jdt.core.prefs 4 | /org.eclipse.m2e.core.prefs 5 | /org.eclipse.wst.common.project.facet.core.xml 6 | /.jsdtscope 7 | /org.eclipse.wst.common.component 8 | /org.eclipse.wst.jsdt.ui.superType.container 9 | /org.eclipse.wst.jsdt.ui.superType.name 10 | /org.eclipse.wst.validation.prefs 11 | /org.springframework.ide.eclipse.boot.prefs 12 | -------------------------------------------------------------------------------- /stokker/src/main/java/org/vferrer/stokker/elk/StockQuotationRepository.java: -------------------------------------------------------------------------------- 1 | package org.vferrer.stokker.elk; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | public interface StockQuotationRepository extends ElasticsearchRepository 10 | { 11 | 12 | public List findByStock(String stock); 13 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /stokker/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | #ELK properties 2 | spring.data.elasticsearch.properties.http.enabled=true 3 | spring.data.elasticsearch.cluster-name=stokker 4 | 5 | elasticsearch.index.name=stockquotations 6 | 7 | #Targeted stocks and start of the historical period to be loaded 8 | stokker.target.stocks=SAN.MC,MAP.MC,REP.MC,IBE.MC,OHI,REE.MC,BME.MC,ABE.MC,ACS.MC,T 9 | stokker.historicaldata.start.year=2012 10 | stokker.historicaldata.start.month=1 11 | stokker.historicaldata.start.day=1 -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.vferrer 7 | parent 8 | 0.0.1-SNAPSHOT 9 | pom 10 | 11 | stokker 12 | Stokker - Stock Quotations 13 | 14 | 15 | stokker 16 | 17 | -------------------------------------------------------------------------------- /stokker/src/main/java/org/vferrer/stokker/elk/StockQuotationJPARepository.java: -------------------------------------------------------------------------------- 1 | package org.vferrer.stokker.elk; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.data.repository.PagingAndSortingRepository; 6 | import org.springframework.data.repository.query.Param; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | public interface StockQuotationJPARepository extends PagingAndSortingRepository 11 | { 12 | 13 | public List findValueByStock(@Param("ticker") String ticker); 14 | 15 | 16 | public StockQuotationJPA findTopByStockOrderByTimestampDesc(@Param("ticker") String ticker); 17 | } 18 | -------------------------------------------------------------------------------- /stokker/src/test/java/org/vferrer/stokker/StokkerApplicationTests.java: -------------------------------------------------------------------------------- 1 | package org.vferrer.stokker; 2 | 3 | import org.junit.Ignore; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.springframework.boot.test.SpringApplicationConfiguration; 7 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 8 | import org.springframework.test.context.web.WebAppConfiguration; 9 | 10 | @RunWith(SpringJUnit4ClassRunner.class) 11 | @SpringApplicationConfiguration(classes = StokkerApplication.class) 12 | @WebAppConfiguration 13 | @Ignore 14 | public class StokkerApplicationTests { 15 | 16 | @Test 17 | public void contextLoads() { 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /stokker/src/main/java/org/vferrer/stokker/feeder/csv/CSVLineSplitter.java: -------------------------------------------------------------------------------- 1 | package org.vferrer.stokker.feeder.csv; 2 | 3 | import java.util.Arrays; 4 | 5 | import org.springframework.integration.splitter.AbstractMessageSplitter; 6 | import org.springframework.messaging.Message; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | public class CSVLineSplitter extends AbstractMessageSplitter { 11 | 12 | @Override 13 | protected Object splitMessage(Message message) 14 | { 15 | String payload = message.getPayload().toString(); 16 | 17 | String[] chunks = payload.split("\\r?\\n"); 18 | 19 | return Arrays.asList(chunks); 20 | 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /stokker/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: stokker 4 | server: 5 | port: 8989 6 | 7 | --- 8 | 9 | spring: 10 | profiles: default 11 | eureka: 12 | password: user 13 | client: 14 | serviceUrl: 15 | defaultZone: http://user:${eureka.password}@localhost:9999/eureka/ 16 | --- 17 | spring: 18 | profiles: remote-es-node 19 | data: 20 | elasticsearch: 21 | cluster-nodes: localhost:9300 22 | --- 23 | spring: 24 | profiles: heroku 25 | eureka: 26 | password: user 27 | client: 28 | serviceUrl: 29 | defaultZone: https://user:${eureka.password}@stokker-portfolio-manager.herokuapp.com/eureka/ 30 | -------------------------------------------------------------------------------- /stokker/src/main/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # This image will be based on an image configured with Java 8 2 | FROM java:8 3 | 4 | MAINTAINER Victor Ferrer 5 | 6 | # This is the directory used by Tomcat to deploy the app 7 | VOLUME /tmp 8 | 9 | # This will add our jar as executable with the alias "app.jar" 10 | ADD stokker-0.0.1-SNAPSHOT.jar app.jar 11 | 12 | # Done to update the file timestamp 13 | RUN bash -c 'touch /app.jar' 14 | 15 | # urandom property modified for a faster startup (!!??) 16 | ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] 17 | 18 | # This will expose the internal 8080 port to the outside if -P flag is provided 19 | EXPOSE 8080 9200 20 | 21 | -------------------------------------------------------------------------------- /stokker/src/main/java/org/vferrer/stokker/elk/converter/IStockQuotationConverter.java: -------------------------------------------------------------------------------- 1 | package org.vferrer.stokker.elk.converter; 2 | 3 | import org.vferrer.stokker.elk.StockQuotation; 4 | 5 | public interface IStockQuotationConverter 6 | { 7 | public StockQuotation convertLiveCSVToStockQuotation(T input); 8 | 9 | /** 10 | * Data from Yahoo Finance comes in this CSV format: 11 | * - Date 12 | * - Open 13 | * - High 14 | * - Low 15 | * - Close 16 | * - Volume 17 | * - Adjusted Close 18 | * 19 | * Note: Ticker field comes in the message header 20 | * @param input 21 | * @return 22 | */ 23 | public StockQuotation convertHistoricalCSVToStockQuotation(String ticker, T input); 24 | } 25 | -------------------------------------------------------------------------------- /stokker/src/main/java/org/vferrer/stokker/feeder/csv/HistoricalCSVLineSplitter.java: -------------------------------------------------------------------------------- 1 | package org.vferrer.stokker.feeder.csv; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | import org.springframework.integration.splitter.AbstractMessageSplitter; 8 | import org.springframework.messaging.Message; 9 | import org.springframework.stereotype.Component; 10 | 11 | @Component 12 | public class HistoricalCSVLineSplitter extends AbstractMessageSplitter { 13 | 14 | @Override 15 | protected Object splitMessage(Message message) 16 | { 17 | String payload = message.getPayload().toString(); 18 | 19 | String[] chunks = payload.split("\\r?\\n"); 20 | 21 | List toReturn = Arrays.asList(chunks); 22 | 23 | // Remove headers 24 | if (!toReturn.isEmpty()){ 25 | toReturn = new ArrayList(toReturn.subList(1, toReturn.size() - 1)); 26 | } 27 | return toReturn; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /stokker/src/main/java/org/vferrer/stokker/StokkerApplication.java: -------------------------------------------------------------------------------- 1 | package org.vferrer.stokker; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 6 | import org.springframework.context.annotation.ImportResource; 7 | 8 | @SpringBootApplication 9 | @ImportResource(value = {"classpath:/META-INF/feederFlow.xml","classpath:/META-INF/historicalQuotations.xml","classpath:/META-INF/liveQuotations.xml"}) 10 | @EnableEurekaClient 11 | // https://jira.spring.io/browse/DATAES-137 12 | // This bug prevents Spring Data REST to work with ElasticSearchRepositories 13 | // Fix Version is 1.3.0 GA (Gosling) 14 | //@EnableElasticsearchRepositories(repositoryFactoryBeanClass = RestElasticsearchRepositoryFactoryBean.class) 15 | public class StokkerApplication { 16 | 17 | public static void main(String[] args) { 18 | SpringApplication.run(StokkerApplication.class, args); 19 | } 20 | 21 | 22 | } 23 | -------------------------------------------------------------------------------- /stokker/src/main/resources/META-INF/feederFlow.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | 18 | 19 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /stokker/src/main/java/org/vferrer/stokker/elk/StockQuotationJPA.java: -------------------------------------------------------------------------------- 1 | package org.vferrer.stokker.elk; 2 | 3 | import java.util.Date; 4 | 5 | import javax.persistence.Column; 6 | import javax.persistence.Entity; 7 | import javax.persistence.GeneratedValue; 8 | import javax.persistence.GenerationType; 9 | import javax.persistence.Id; 10 | 11 | @Entity 12 | public class StockQuotationJPA 13 | { 14 | 15 | @Id 16 | @GeneratedValue(strategy=GenerationType.AUTO) 17 | private Long id; 18 | 19 | @Column 20 | private String stock; 21 | 22 | @Column 23 | private Double value; 24 | 25 | @Column 26 | private Date timestamp; 27 | 28 | public String getStock() { 29 | return stock; 30 | } 31 | public void setStock(String stock) { 32 | this.stock = stock; 33 | } 34 | public Double getValue() { 35 | return value; 36 | } 37 | public void setValue(Double value) { 38 | this.value = value; 39 | } 40 | public Long getId() { 41 | return id; 42 | } 43 | public void setId(Long id) { 44 | this.id = id; 45 | } 46 | public Date getTimestamp() { 47 | return timestamp; 48 | } 49 | public void setTimestamp(Date timestamp) { 50 | this.timestamp = timestamp; 51 | } 52 | 53 | public static StockQuotationJPA fromElasticSearchStockQuotation(StockQuotation input){ 54 | StockQuotationJPA toReturn = new StockQuotationJPA(); 55 | toReturn.setStock(input.getStock()); 56 | toReturn.setValue(input.getValue()); 57 | toReturn.setTimestamp(input.getTimestamp().getTime()); 58 | 59 | return toReturn; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /stokker/src/main/java/org/vferrer/stokker/elk/controller/StockPriceController.java: -------------------------------------------------------------------------------- 1 | package org.vferrer.stokker.elk.controller; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.joda.time.DateTime; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | import org.vferrer.stokker.elk.ELKClient; 11 | import org.vferrer.stokker.elk.StockQuotation; 12 | 13 | // TODO: This should be refactored in order to use a more RESTful approach 14 | // However, we have to wait until the bug with the elastic search repositories is fixed in Spring data REST 15 | @RestController 16 | public class StockPriceController 17 | { 18 | 19 | @Autowired 20 | private ELKClient elkClient; 21 | 22 | 23 | // TODO: This should be refactored in order to use a more RESTful approach 24 | // However, we have to wait until the bug with the elastic search repositories is fixed in Spring data REST 25 | @RequestMapping(value = "/getPrices", produces = "application/json") 26 | public List getDailyPrices(String ticker, String date) 27 | { 28 | List toReturn = new ArrayList<>(); 29 | toReturn.add(buildQuotation(ticker,elkClient.getAvgPriceByStockAndDate(ticker, DateTime.parse(date)))); 30 | return toReturn; 31 | } 32 | 33 | private StockQuotation buildQuotation(String stock, Double value) 34 | { 35 | StockQuotation quotation = new StockQuotation(); 36 | quotation.setValue(value); 37 | quotation.setStock(stock); 38 | return quotation; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /stokker/src/main/java/org/vferrer/stokker/feeder/csv/HistoricalCSVRequestCreator.java: -------------------------------------------------------------------------------- 1 | package org.vferrer.stokker.feeder.csv; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | import javax.annotation.PostConstruct; 7 | 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.beans.factory.annotation.Qualifier; 10 | import org.springframework.beans.factory.annotation.Value; 11 | import org.springframework.context.ApplicationEvent; 12 | import org.springframework.context.ApplicationListener; 13 | import org.springframework.context.event.ContextRefreshedEvent; 14 | import org.springframework.messaging.MessageChannel; 15 | import org.springframework.messaging.support.MessageBuilder; 16 | import org.springframework.stereotype.Component; 17 | 18 | @SuppressWarnings("rawtypes") 19 | @Component 20 | public class HistoricalCSVRequestCreator implements ApplicationListener { 21 | 22 | private List tickers; 23 | 24 | @Autowired 25 | @Qualifier("historical.trigger.split.channel") 26 | private MessageChannel channel; 27 | 28 | @Value("${stokker.target.stocks}") 29 | private String tickersConfig; 30 | 31 | @PostConstruct 32 | public void init() 33 | { 34 | tickers = Arrays.asList(tickersConfig.split(",")); 35 | } 36 | 37 | @Override 38 | public void onApplicationEvent(ApplicationEvent event) { 39 | 40 | if (event instanceof ContextRefreshedEvent) 41 | { 42 | System.out.println("Building request for tickers: " + tickersConfig); 43 | 44 | for (String ticker : tickers) { 45 | 46 | if (!ticker.trim().isEmpty()){ 47 | MessageBuilder builder = MessageBuilder.withPayload(""); 48 | builder.setHeaderIfAbsent("ticker", ticker.trim()); 49 | channel.send(builder.build()); 50 | } 51 | } 52 | } 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /stokker/src/test/java/org/vferrer/stokker/feeder/csv/StockQuotationConverterTest.java: -------------------------------------------------------------------------------- 1 | package org.vferrer.stokker.feeder.csv; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import java.text.ParseException; 6 | import java.text.SimpleDateFormat; 7 | 8 | import org.junit.Test; 9 | import org.vferrer.stokker.elk.StockQuotation; 10 | import org.vferrer.stokker.elk.converter.CSVStockQuotationConverter; 11 | 12 | public class StockQuotationConverterTest { 13 | 14 | @Test 15 | public void testLiveConversion() throws ParseException { 16 | 17 | String csvInput = "REP.MC,23/07/2015 17:36:00,\"16,155\""; 18 | 19 | SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"); 20 | 21 | CSVStockQuotationConverter converter = new CSVStockQuotationConverter(); 22 | StockQuotation result = converter.convertLiveCSVToStockQuotation(csvInput); 23 | 24 | assertEquals("Invalid parsed value",16.155d,result.getValue(), 0.0001d); 25 | assertEquals("Invalid parsed stock","REP.MC",result.getStock()); 26 | assertEquals("Invalid parsed date", sdf.parse("23/07/2015 17:36:00"), result.getTimestamp().getTime()); 27 | 28 | } 29 | 30 | @Test 31 | public void testHistoricalConversionWithHeader() throws ParseException { 32 | 33 | // URL for retrieving the data: 34 | // http://ichart.finance.yahoo.com/table.csv?s=T&a=1&b=1&c=2015&g=ds 35 | 36 | String csvInput = "2015-10-21,33.880001,33.939999,33.330002,33.599998,27215000,33.599998"; 37 | 38 | SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"); 39 | 40 | CSVStockQuotationConverter converter = new CSVStockQuotationConverter(); 41 | StockQuotation result = converter.convertHistoricalCSVToStockQuotation("REP.MC",csvInput); 42 | 43 | assertEquals("Invalid parsed value",33.59999,result.getValue(), 0.0001d); 44 | assertEquals("Invalid parsed stock","REP.MC",result.getStock()); 45 | assertEquals("Invalid parsed date", sdf.parse("21/10/2015 00:00:00"), result.getTimestamp().getTime()); 46 | 47 | } 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /stokker/src/main/resources/META-INF/liveQuotations.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | 21 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 30 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /stokker/src/main/resources/META-INF/historicalQuotations.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 26 | 27 | 28 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 42 | 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Stokker - Stock screener based on Spring-Boot/Integration + Elastic Search 2 | 3 | [![Travis CI Status](https://travis-ci.org/victor-ferrer/stokker.svg?branch=master)](https://travis-ci.org/victor-ferrer/stokker) 4 | 5 | *NOTE*: The source code corresponding to the Portfolio Manager has been moved to its own repository: https://github.com/victor-ferrer/stokker-portfolio-manager 6 | 7 | 8 | ## Summary 9 | 10 | This a personal learning project whose functionality, for the moment, is to: 11 | - Gather historical stock quotations from Yahoo Finance 12 | - Gather "live" stock quotations from Google Finance 13 | - Index everything in a Elasticsearch node so queries, graphs and alarms can be generated. 14 | - Provide an AngularJS web interface wrapping the Kibana dashboards. 15 | - Provide portfolio management functionalities using the Stock prices stored in ES. 16 | 17 | By "stock quotations", I mean: 18 | - High, low, open and close values (daily values) 19 | - Volume (daily) 20 | - Ticker 21 | - Timestamp of the current value 22 | 23 | 24 | Desired functionality 25 | - Be able to perform some studies in the indexed data in ES (specially, I´m waiting for some ES 2.0 features such as the moving average). This is a sample dashboard created with the information stored in ES and embedded in the AngularJS interface: 26 | 27 | ![Sample Kibana Dashboard](https://raw.githubusercontent.com/victor-ferrer/stokker/master/sample%20dashboard.PNG) 28 | 29 | - Use the module "Watcher" in order to define several types of alerts based on market conditions. 30 | 31 | 32 | On technical side: 33 | - The latest Spring Boot version is to be used 34 | - Application contains a DockerFile and can create an Docker image from it: 35 | -- This image can be associated with another images running more ElasticSearch nodes and the Kibana server 36 | - Introduce OAuth2 as authentication system 37 | 38 | This is how the deployment looks like: 39 | 40 | ![Deployment diagram](https://raw.githubusercontent.com/victor-ferrer/stokker/master/deployment_model.PNG) 41 | 42 | - Stokker will read the stock information from the Web and will index it in an ElasticSearch cluster. 43 | - The ElasticSearch cluster will be deployed either in its own Docker container or along with the Stokker JVM. 44 | - Another Docker container will host Kibana 45 | - Portfolio manager will host a local database with stock portolios, whose valuations will be calculated based on what is stored in the ES cluster. 46 | - An AngularJS web interface will be shown to the user combining data from the local database and ES. 47 | 48 | ## How to use it 49 | Check [this Wiki Page](https://github.com/victor-ferrer/stokker/wiki/How-to-run-the-Stokker-services) on how to run the project and its related tools. 50 | 51 | ## Running example 52 | There is a running instance of the application deployed in Heroku: https://stokker-portfolio-manager.herokuapp.com 53 | 54 | My intention is that everytime that a succesfull CI build is finished, a new version is deployed to Heroku. 55 | 56 | **Please note that the Kibana functionality is not yet working in this environment.** 57 | 58 | ## Pending improvements 59 | - Improve error reporting 60 | - Provide a GUI with embedded Kibana visualizations 61 | - And many more... 62 | 63 | ## More info 64 | I´m writing about this development experience in my blog: http://victorferrerjava.blogspot.com.es/ 65 | Feel free to contribute. 66 | 67 | 68 | -------------------------------------------------------------------------------- /stokker/src/main/java/org/vferrer/stokker/elk/StockQuotation.java: -------------------------------------------------------------------------------- 1 | package org.vferrer.stokker.elk; 2 | 3 | import java.util.Calendar; 4 | 5 | import javax.persistence.GeneratedValue; 6 | import javax.persistence.GenerationType; 7 | import javax.persistence.Id; 8 | 9 | import org.springframework.data.elasticsearch.annotations.DateFormat; 10 | import org.springframework.data.elasticsearch.annotations.Document; 11 | import org.springframework.data.elasticsearch.annotations.Field; 12 | import org.springframework.data.elasticsearch.annotations.FieldIndex; 13 | import org.springframework.data.elasticsearch.annotations.FieldType; 14 | 15 | @Document(indexName = "stockquotations") 16 | public class StockQuotation 17 | { 18 | private static final String DATE_FORMAT = "dd-MM-yyyy HH:mm:ss"; 19 | 20 | @Id 21 | @Field 22 | @GeneratedValue(strategy=GenerationType.AUTO) 23 | private Long id; 24 | 25 | @Field (type = FieldType.String, store = true, index = FieldIndex.analyzed, searchAnalyzer = "standard", indexAnalyzer = "standard") 26 | private String stock; 27 | 28 | @Field(type=FieldType.Double, store = true, index = FieldIndex.analyzed, searchAnalyzer = "standard", indexAnalyzer = "standard") 29 | private Double value; 30 | 31 | @Field(type = FieldType.Date, format = DateFormat.custom, pattern = DATE_FORMAT, store = true, index = FieldIndex.analyzed, searchAnalyzer = "standard", indexAnalyzer = "standard") 32 | private Calendar timestamp; 33 | 34 | @Field(type=FieldType.Double, store = true, index = FieldIndex.analyzed, searchAnalyzer = "standard", indexAnalyzer = "standard") 35 | private Double openValue; 36 | 37 | @Field(type=FieldType.Double, store = true, index = FieldIndex.analyzed, searchAnalyzer = "standard", indexAnalyzer = "standard") 38 | private Double highValue; 39 | 40 | @Field(type=FieldType.Double, store = true, index = FieldIndex.analyzed, searchAnalyzer = "standard", indexAnalyzer = "standard") 41 | private Double lowValue; 42 | 43 | @Field(type=FieldType.Double, store = true, index = FieldIndex.analyzed, searchAnalyzer = "standard", indexAnalyzer = "standard") 44 | private Double volume; 45 | 46 | 47 | public String getStock() { 48 | return stock; 49 | } 50 | public void setStock(String stock) { 51 | this.stock = stock; 52 | } 53 | public Double getValue() { 54 | return value; 55 | } 56 | public void setValue(Double value) { 57 | this.value = value; 58 | } 59 | public Calendar getTimestamp() { 60 | return timestamp; 61 | } 62 | public void setTimestamp(Calendar timestamp) { 63 | this.timestamp = timestamp; 64 | } 65 | public Long getId() { 66 | return id; 67 | } 68 | public void setId(Long id) { 69 | this.id = id; 70 | } 71 | 72 | public Double getOpenValue() { 73 | return openValue; 74 | } 75 | 76 | public void setOpenValue(Double openValue) { 77 | this.openValue = openValue; 78 | } 79 | 80 | public Double getHighValue() { 81 | return highValue; 82 | } 83 | 84 | public void setHighValue(Double highValue) { 85 | this.highValue = highValue; 86 | } 87 | 88 | public Double getLowValue() { 89 | return lowValue; 90 | } 91 | 92 | public void setLowValue(Double lowValue) { 93 | this.lowValue = lowValue; 94 | } 95 | 96 | public Double getVolume() { 97 | return volume; 98 | } 99 | 100 | public void setVolume(Double volume) { 101 | this.volume = volume; 102 | } 103 | 104 | 105 | @Override 106 | public String toString() 107 | { 108 | return String.format("[%s]@%s (%s) H(%s)/L(%s)/O(%s)", this.stock, this.value, this.timestamp.getTime().toString(), this.highValue, this.lowValue,this.openValue); 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /stokker/src/main/java/org/vferrer/stokker/elk/converter/CSVStockQuotationConverter.java: -------------------------------------------------------------------------------- 1 | package org.vferrer.stokker.elk.converter; 2 | 3 | import java.util.Calendar; 4 | import java.util.GregorianCalendar; 5 | 6 | import org.joda.time.DateTime; 7 | import org.joda.time.format.DateTimeFormatter; 8 | import org.joda.time.format.DateTimeFormatterBuilder; 9 | import org.springframework.stereotype.Component; 10 | import org.vferrer.stokker.elk.StockQuotation; 11 | 12 | @Component 13 | public class CSVStockQuotationConverter implements IStockQuotationConverter { 14 | 15 | private DateTimeFormatter liveInputFormatter, historicalnputFormatter; 16 | 17 | private static final int STOCK_COLUMN = 0; 18 | private static final int DATE_COLUMN = 1; 19 | private static final int PRICE_COLUMN = 2; 20 | 21 | /* Data from Yahoo Finance comes in this CSV format: 22 | * - Date 23 | * - Open 24 | * - High 25 | * - Low 26 | * - Close 27 | * - Volume 28 | * - Adjusted Close 29 | */ 30 | private static final int HISTORICAL_DATE_COLUMN = 0; 31 | private static final int HISTORICAL_OPEN_COLUMN = 1; 32 | private static final int HISTORICAL_HIGH_COLUMN = 2; 33 | private static final int HISTORICAL_LOW_COLUMN = 3; 34 | private static final int HISTORICAL_CLOSE_COLUMN = 4; 35 | private static final int HISTORICAL_VOLUME_COLUMN = 5; 36 | 37 | 38 | public CSVStockQuotationConverter() 39 | { 40 | DateTimeFormatterBuilder formatterBuilder = new DateTimeFormatterBuilder(); 41 | liveInputFormatter = formatterBuilder.appendPattern("dd/MM/yyyy HH:mm:ss").toFormatter(); 42 | formatterBuilder.clear(); 43 | historicalnputFormatter = formatterBuilder.appendPattern("yy-MM-dd").toFormatter(); 44 | } 45 | 46 | @Override 47 | public StockQuotation convertLiveCSVToStockQuotation(String input) { 48 | String[] chunks = input.split(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)", -1); 49 | 50 | if (chunks.length != 3) 51 | { 52 | throw new IllegalArgumentException("Invalid CSV stock quotation: " + input); 53 | } 54 | 55 | StockQuotation quotation = new StockQuotation(); 56 | quotation.setStock(chunks[STOCK_COLUMN].replaceAll("\"", "")); 57 | quotation.setValue(Double.parseDouble(chunks[PRICE_COLUMN].replaceAll("\"", "").replaceAll(",", ".") + "d")); 58 | 59 | DateTime time = DateTime.parse(chunks[DATE_COLUMN].replaceAll("\"", ""),liveInputFormatter); 60 | Calendar calendar = new GregorianCalendar(time.getYear(), time.getMonthOfYear() -1, time.getDayOfMonth(), time.getHourOfDay(), time.getMinuteOfHour(), time.getSecondOfMinute()); 61 | quotation.setTimestamp(calendar); 62 | 63 | return quotation; 64 | } 65 | 66 | /* 67 | * (non-Javadoc) 68 | * @see org.vferrer.stokker.elk.converter.IStockQuotationConverter#convertHistoricalCSVToStockQuotation(java.lang.Object) 69 | */ 70 | @Override 71 | public StockQuotation convertHistoricalCSVToStockQuotation(String ticker, String input) 72 | { 73 | String[] chunks = input.split(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)", -1); 74 | 75 | if (chunks.length != 7) 76 | { 77 | throw new IllegalArgumentException("Invalid CSV stock quotation: " + input); 78 | } 79 | 80 | StockQuotation quotation = new StockQuotation(); 81 | quotation.setStock(ticker); 82 | quotation.setValue(Double.parseDouble(chunks[HISTORICAL_CLOSE_COLUMN].replaceAll("\"", "").replaceAll(",", ".") + "d")); 83 | 84 | quotation.setHighValue(Double.parseDouble(chunks[HISTORICAL_HIGH_COLUMN].replaceAll("\"", "").replaceAll(",", ".") + "d")); 85 | quotation.setLowValue(Double.parseDouble(chunks[HISTORICAL_LOW_COLUMN].replaceAll("\"", "").replaceAll(",", ".") + "d")); 86 | quotation.setOpenValue(Double.parseDouble(chunks[HISTORICAL_OPEN_COLUMN].replaceAll("\"", "").replaceAll(",", ".") + "d")); 87 | quotation.setVolume(Double.parseDouble(chunks[HISTORICAL_VOLUME_COLUMN].replaceAll("\"", "").replaceAll(",", ".") + "d")); 88 | 89 | DateTime time = DateTime.parse(chunks[HISTORICAL_DATE_COLUMN].replaceAll("\"", ""),historicalnputFormatter); 90 | Calendar calendar = new GregorianCalendar(time.getYear(), time.getMonthOfYear() -1, time.getDayOfMonth(), time.getHourOfDay(), time.getMinuteOfHour(), time.getSecondOfMinute()); 91 | quotation.setTimestamp(calendar); 92 | 93 | return quotation; 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /stokker/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.vferrer 7 | stokker 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | stokker 12 | Microservice for Stock data publication 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.3.3.RELEASE 18 | 19 | 20 | 21 | 22 | 23 | 24 | org.springframework.cloud 25 | spring-cloud-starter-parent 26 | Brixton.M4 27 | pom 28 | import 29 | 30 | 31 | 32 | 33 | 34 | UTF-8 35 | 1.8 36 | org.vferrer 37 | 38 | 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-data-elasticsearch 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter-integration 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-starter-web 51 | 52 | 53 | org.springframework.boot 54 | spring-boot-starter-actuator 55 | 56 | 57 | org.springframework.boot 58 | spring-boot-starter-data-jpa 59 | 60 | 61 | org.springframework.boot 62 | spring-boot-starter-data-rest 63 | 64 | 65 | org.springframework.cloud 66 | spring-cloud-starter-eureka 67 | 68 | 69 | com.h2database 70 | h2 71 | 72 | 73 | 74 | org.springframework.boot 75 | spring-boot-starter-test 76 | test 77 | 78 | 79 | org.elasticsearch 80 | elasticsearch 81 | 1.7.0 82 | 83 | 84 | org.springframework.integration 85 | spring-integration-file 86 | 87 | 88 | 89 | 90 | 91 | 92 | org.springframework.boot 93 | spring-boot-maven-plugin 94 | 95 | 96 | 97 | com.spotify 98 | docker-maven-plugin 99 | 0.2.3 100 | 101 | ${docker.image.prefix}/${project.artifactId} 102 | src/main/docker 103 | 104 | 105 | ${project.build.directory} 106 | ${project.build.finalName}.jar 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | spring-snapshots 118 | Spring Snapshots 119 | https://repo.spring.io/snapshot 120 | 121 | true 122 | 123 | 124 | 125 | spring-milestones 126 | Spring Milestones 127 | https://repo.spring.io/milestone 128 | 129 | false 130 | 131 | 132 | 133 | 134 | 135 | spring-snapshots 136 | Spring Snapshots 137 | https://repo.spring.io/snapshot 138 | 139 | true 140 | 141 | 142 | 143 | spring-milestones 144 | Spring Milestones 145 | https://repo.spring.io/milestone 146 | 147 | false 148 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /stokker/src/main/java/org/vferrer/stokker/elk/ELKClient.java: -------------------------------------------------------------------------------- 1 | package org.vferrer.stokker.elk; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import javax.annotation.PostConstruct; 7 | 8 | import org.elasticsearch.action.search.SearchResponse; 9 | import org.elasticsearch.index.query.MatchQueryBuilder; 10 | import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogram; 11 | import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogram.Bucket; 12 | import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogram.Interval; 13 | import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramBuilder; 14 | import org.elasticsearch.search.aggregations.metrics.avg.Avg; 15 | import org.elasticsearch.search.aggregations.metrics.avg.AvgBuilder; 16 | import org.joda.time.DateTime; 17 | import org.springframework.beans.factory.annotation.Autowired; 18 | import org.springframework.beans.factory.annotation.Value; 19 | import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; 20 | import org.springframework.data.elasticsearch.core.ResultsExtractor; 21 | import org.springframework.data.elasticsearch.core.query.IndexQuery; 22 | import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; 23 | import org.springframework.stereotype.Component; 24 | 25 | @Component 26 | public class ELKClient { 27 | 28 | // This template is configured by default in order to connect to a local 29 | // ES node and injected in the application context 30 | @Autowired 31 | private ElasticsearchTemplate template; 32 | 33 | @Value("${elasticsearch.index.name}") 34 | public String indexName; 35 | 36 | @Autowired 37 | private StockQuotationRepository stockRepo; 38 | 39 | @Autowired 40 | private StockQuotationJPARepository stockJPARepo; 41 | 42 | @PostConstruct 43 | public void initIndex() 44 | { 45 | // Create an index if necessary 46 | if (!template.indexExists(indexName)){ 47 | template.createIndex(indexName); 48 | } 49 | 50 | // Tell ELK to consider StockQuotation as a entity to use 51 | template.putMapping(StockQuotation.class); 52 | template.refresh(indexName, true); 53 | 54 | } 55 | 56 | /** 57 | * Indexes a single StockQuotation 58 | * @param quotation 59 | * @throws Exception 60 | */ 61 | public void pushToELK(StockQuotation quotation) throws Exception 62 | { 63 | // Temporary store quotations in H2 until rest for ES works 64 | stockJPARepo.save(StockQuotationJPA.fromElasticSearchStockQuotation(quotation)); 65 | 66 | stockRepo.save(quotation); 67 | } 68 | 69 | /** 70 | * Bulk indexing of a list of stock quotations for better performance 71 | * @param quotationList 72 | * @throws Exception 73 | */ 74 | public void pushToELK(List quotationList) throws Exception 75 | { 76 | List queries = new ArrayList<>(); 77 | 78 | for (StockQuotation quotation : quotationList){ 79 | 80 | IndexQuery indexQuery = new IndexQuery(); 81 | indexQuery.setIndexName(indexName); 82 | indexQuery.setObject(quotation); 83 | queries.add(indexQuery); 84 | 85 | // Temporary store quotations in H2 until rest for ES works 86 | stockJPARepo.save(StockQuotationJPA.fromElasticSearchStockQuotation(quotation)); 87 | } 88 | 89 | template.bulkIndex(queries); 90 | } 91 | 92 | /* 93 | 94 | { 95 | "query": { 96 | "match": { 97 | "stock": "MAP.MC" 98 | } 99 | }, 100 | 101 | "size": 0, 102 | 103 | "aggs": { 104 | "per_day": { 105 | "date_histogram": { 106 | "field": "timestamp", 107 | "interval": "day", 108 | "format": "yyyy-MM-dd" 109 | }, 110 | 111 | "aggs": { 112 | "total": { 113 | avg": { 114 | "field": "value" 115 | } 116 | } 117 | } 118 | } 119 | } 120 | * */ 121 | public Double getAvgPriceByStockAndDate(String stock, final DateTime date) 122 | { 123 | MatchQueryBuilder queryBuilder = new MatchQueryBuilder("stock", stock); 124 | 125 | NativeSearchQuery query = new NativeSearchQuery(queryBuilder); 126 | 127 | DateHistogramBuilder perDayAggregation = new DateHistogramBuilder("per_day"); 128 | perDayAggregation.field("timestamp"); 129 | perDayAggregation.interval(Interval.DAY); 130 | perDayAggregation.format("yyyy-MM-dd"); 131 | 132 | AvgBuilder avg = new AvgBuilder("total"); 133 | avg.field("value"); 134 | perDayAggregation.subAggregation(avg); 135 | 136 | query.addAggregation(perDayAggregation); 137 | 138 | 139 | Double value = template.query(query, new ResultsExtractor() { 140 | 141 | @Override 142 | public Double extract(SearchResponse response) 143 | { 144 | DateHistogram dateHisto = (DateHistogram) response.getAggregations().asMap().get("per_day"); 145 | 146 | Bucket bucketByKey = dateHisto.getBucketByKey(date.toString("yyyy-MM-dd")); 147 | 148 | if (bucketByKey == null){ 149 | return null; 150 | } 151 | 152 | Avg avg = (Avg)bucketByKey.getAggregations().asMap().get("total"); 153 | return avg.getValue(); 154 | } 155 | }); 156 | 157 | return value; 158 | } 159 | 160 | } -------------------------------------------------------------------------------- /stokker/src/main/resources/stockTickers.csv: -------------------------------------------------------------------------------- 1 | SAN.MC;Banco Santander;FInanacials 2 | MAP.MC;Mapfre;Insurers 3 | REP.MC;Repsol;Petroleum 4 | IBE.MC;Iberdrola;Electric 5 | OHI;Omega Healthcare Investors;HealthCare 6 | REE.MC;Red Electrica Corporacion;Electric 7 | BME.MC;Bolsas y Mercados;Financials 8 | ABE.MC;Abertis Infraestructuras;Infrastructures 9 | ACS.MC;ACS Construcciones;Constructions 10 | MMM;3M Co;Industrials 11 | ABT;Abbott Laboratories;Health Care 12 | ABBV;AbbVie;Health Care 13 | ACN;Accenture Plc;Information Technology 14 | ACE;ACE Limited;Financials 15 | ADBE;Adobe Systems;Information Technology 16 | ADT;ADT Corp (The);Industrials 17 | AES;AES Corp;Utilities 18 | AET;Aetna;Health Care 19 | AMG;Affiliated Managers Group;Financials 20 | AFL;AFLAC;Financials 21 | A;Agilent Technologies;Health Care 22 | GAS;AGL Resources;Utilities 23 | APD;Air Products & Chemicals;Materials 24 | ARG;Airgas;Materials 25 | AKAM;Akamai Technologies;Information Technology 26 | AA;Alcoa;Materials 27 | ALXN;Alexion Pharmaceuticals;Health Care 28 | ATI;Allegheny Technologies;Materials 29 | ALLE;Allegion Plc;Industrials 30 | AGN;Allergan plc;Health Care 31 | ADS;Alliance Data Systems;Information Technology 32 | ALL;Allstate Corp;Financials 33 | ALTR;Altera Corp;Information Technology 34 | MO;Altria Group;Consumer Staples 35 | AMZN;Amazon.com;Consumer Discretionary 36 | AEE;Ameren Corp;Utilities 37 | AAL;American Airlines Group;Industrials 38 | AEP;American Electric Power;Utilities 39 | AXP;American Express;Financials 40 | AIG;American International Group;Financials 41 | AMT;American Tower;Financials 42 | AMP;Ameriprise Financial;Financials 43 | ABC;AmerisourceBergen Corp;Health Care 44 | AME;AMETEK;Industrials 45 | AMGN;Amgen;Health Care 46 | APH;Amphenol Corp A;Information Technology 47 | APC;Anadarko Petroleum;Energy 48 | ADI;Analog Devices;Information Technology 49 | ANTM;Anthem;Health Care 50 | AON;Aon Plc;Financials 51 | APA;Apache Corp;Energy 52 | AIV;Apartment Investment & Mgmt;Financials 53 | AAPL;Apple;Information Technology 54 | AMAT;Applied Materials;Information Technology 55 | ADM;Archer-Daniels-Midland;Consumer Staples 56 | AIZ;Assurant;Financials 57 | T;AT&T;Telecommunication Services 58 | ADSK;Autodesk;Information Technology 59 | ADP;Automatic Data Processing;Information Technology 60 | AN;AutoNation;Consumer Discretionary 61 | AZO;AutoZone;Consumer Discretionary 62 | AVGO;Avago Technologies;Information Technology 63 | AVB;AvalonBay Communities;Financials 64 | AVY;Avery Dennison Corp;Materials 65 | BHI;Baker Hughes;Energy 66 | BLL;Ball Corp;Materials 67 | BAC;Bank of America;Financials 68 | BK;Bank of New York Mellon;Financials 69 | BCR;Bard (C.R.);Health Care 70 | BAX;Baxter International;Health Care 71 | BBT;BB&T Corp;Financials 72 | BDX;Becton Dickinson;Health Care 73 | BBBY;Bed Bath & Beyond;Consumer Discretionary 74 | BRK-B;Berkshire Hathaway;Financials 75 | BBY;Best Buy;Consumer Discretionary 76 | BIIB;Biogen;Health Care 77 | BLK;BlackRock;Financials 78 | BA;Boeing Co;Industrials 79 | BWA;Borg Warner;Consumer Discretionary 80 | BXP;Boston Properties;Financials 81 | BSX;Boston Scientific;Health Care 82 | BMY;Bristol-Myers Squibb;Health Care 83 | BRCM;Broadcom Corp;Information Technology 84 | BF-B;Brown-Forman;Consumer Staples 85 | CHRW;C.H. Robinson Worldwide;Industrials 86 | CA;CA;Information Technology 87 | CVC;Cablevision Systems;Consumer Discretionary 88 | COG;Cabot Oil & Gas;Energy 89 | CAM;Cameron International;Energy 90 | CPB;Campbell Soup;Consumer Staples 91 | COF;Capital One Financial;Financials 92 | CAH;Cardinal Health;Health Care 93 | KMX;CarMax;Consumer Discretionary 94 | CCL;Carnival Corp;Consumer Discretionary 95 | CAT;Caterpillar;Industrials 96 | CBG;CBRE Group;Financials 97 | CBS;CBS Corp;Consumer Discretionary 98 | CELG;Celgene Corp;Health Care 99 | CNP;CenterPoint Energy;Utilities 100 | CTL;CenturyLink;Telecommunication Services 101 | CERN;Cerner Corp;Health Care 102 | CF;CF Industries Holdings;Materials 103 | CHK;Chesapeake Energy;Energy 104 | CVX;Chevron Corp;Energy 105 | CMG;Chipotle Mexican Grill;Consumer Discretionary 106 | CB;Chubb Corp;Financials 107 | CI;Cigna Corp;Health Care 108 | XEC;Cimarex Energy;Energy 109 | CINF;Cincinnati Financial;Financials 110 | CTAS;Cintas Corp;Industrials 111 | CSCO;Cisco Systems;Information Technology 112 | C;Citigroup;Financials 113 | CTXS;Citrix Systems;Information Technology 114 | CLX;Clorox Co;Consumer Staples 115 | CME;CME Group;Financials 116 | CMS;CMS Energy;Utilities 117 | COH;Coach;Consumer Discretionary 118 | KO;Coca-Cola Co;Consumer Staples 119 | CCE;Coca-Cola Enterprises;Consumer Staples 120 | CTSH;Cognizant Technology Solutions;Information Technology 121 | CL;Colgate-Palmolive;Consumer Staples 122 | CMCSA;Comcast Corp;Consumer Discretionary 123 | CMA;Comerica;Financials 124 | CSC;Computer Sciences;Information Technology 125 | CAG;ConAgra Foods;Consumer Staples 126 | COP;ConocoPhillips;Energy 127 | CNX;CONSOL Energy;Energy 128 | ED;Consolidated Edison;Utilities 129 | STZ;Constellation Brands;Consumer Staples 130 | GLW;Corning;Information Technology 131 | COST;Costco Wholesale;Consumer Staples 132 | CCI;Crown Castle International;Financials 133 | CSX;CSX Corp;Industrials 134 | CMI;Cummins;Industrials 135 | CVS;CVS Health Corp;Consumer Staples 136 | DHI;D.R.Horton;Consumer Discretionary 137 | DHR;Danaher Corp;Industrials 138 | DRI;Darden Restaurants;Consumer Discretionary 139 | DVA;DaVita HealthCare Partner;Health Care 140 | DE;Deere & Co;Industrials 141 | DLPH;Delphi Automotive Plc;Consumer Discretionary 142 | DAL;Delta Air Lines;Industrials 143 | XRAY;DENTSPLY International;Health Care 144 | DVN;Devon Energy;Energy 145 | DO;Diamond Offshore Drilling;Energy 146 | DFS;Discover Financial Services;Financials 147 | DISCA;Discovery Communications;Consumer Discretionary 148 | DG;Dollar General;Consumer Discretionary 149 | DLTR;Dollar Tree;Consumer Discretionary 150 | D;Dominion Resources;Utilities 151 | DOV;Dover Corp;Industrials 152 | DOW;Dow Chemical;Materials 153 | DPS;Dr. Pepper Snapple Group;Consumer Staples 154 | DTE;DTE Energy;Utilities 155 | DD;Du Pont (E.I.);Materials 156 | DUK;Duke Energy;Utilities 157 | DNB;Dun & Bradstreet;Industrials 158 | ETFC;E Trade Financial;Financials 159 | EMN;Eastman Chemical;Materials 160 | ETN;Eaton Corp Plc;Industrials 161 | EBAY;eBay;Information Technology 162 | ECL;Ecolab;Materials 163 | EIX;Edison International;Utilities 164 | EW;Edwards Lifesciences;Health Care 165 | EA;Electronic Arts;Information Technology 166 | EMC;EMC Corp;Information Technology 167 | EMR;Emerson Electric;Industrials 168 | ENDP;Endo International plc;Health Care 169 | ESV;ENSCO Plc;Energy 170 | ETR;Entergy Corp;Utilities 171 | EOG;EOG Resources;Energy 172 | EQT;EQT Corp;Energy 173 | EFX;Equifax;Industrials 174 | EQIX;Equinix;Financials 175 | EQR;Equity Residential;Financials 176 | ESS;Essex Property Trust;Financials 177 | EL;Estee Lauder Co;Consumer Staples 178 | ES;Eversource Energy;Utilities 179 | EXC;Exelon Corp;Utilities 180 | EXPE;Expedia;Consumer Discretionary 181 | EXPD;Expeditors International,Wash;Industrials 182 | ESRX;Express Scripts Holding;Health Care 183 | XOM;Exxon Mobil;Energy 184 | FFIV;F5 Networks;Information Technology 185 | FB;Facebook Cl;Information Technology 186 | FDO;Family Dollar Stores;Consumer Discretionary 187 | FAST;Fastenal Co;Industrials 188 | FDX;FedEx Corp;Industrials 189 | FIS;Fidelity National Information Services;Information Technology 190 | FITB;Fifth Third Bancorp;Financials 191 | FSLR;First Solar;Information Technology 192 | FE;FirstEnergy Corp;Utilities 193 | FISV;Fiserv;Information Technology 194 | FLIR;FLIR Systems;Information Technology 195 | FLS;Flowserve Corp;Industrials 196 | FLR;Fluor Corp;Industrials 197 | FMC;FMC Corp;Materials 198 | FTI;FMC Technologies;Energy 199 | F;Ford Motor;Consumer Discretionary 200 | FOSL;Fossil Group;Consumer Discretionary 201 | BEN;Franklin Resources;Financials 202 | FCX;Freeport-McMoRan;Materials 203 | FTR;Frontier Communications;Telecommunication Services 204 | GME;GameStop Corp;Consumer Discretionary 205 | GPS;Gap;Consumer Discretionary 206 | GRMN;Garmin Ltd;Consumer Discretionary 207 | GM;General Motors;Consumer Discretionary 208 | GD;Genl Dynamics;Industrials 209 | GE;Genl Electric;Industrials 210 | GGP;Genl Growth Properties;Financials 211 | GIS;Genl Mills;Consumer Staples 212 | GPC;Genuine Parts;Consumer Discretionary 213 | GNW;Genworth Financial;Financials 214 | GILD;Gilead Sciences;Health Care 215 | GS;Goldman Sachs Group;Financials 216 | GT;Goodyear Tire & Rub;Consumer Discretionary 217 | GOOGL;Google;Information Technology 218 | GWW;Grainger (W.W.);Industrials 219 | HRB;H & R Block;Consumer Discretionary 220 | HAL;Halliburton Co;Energy 221 | HBI;Hanesbrands;Consumer Discretionary 222 | HOG;Harley-Davidson;Consumer Discretionary 223 | HAR;Harman International;Consumer Discretionary 224 | HRS;Harris Corp;Information Technology 225 | HIG;Hartford Finl Services Gp;Financials 226 | HAS;Hasbro;Consumer Discretionary 227 | HCA;HCA Holdings;Health Care 228 | HCP;HCP;Financials 229 | HCN;Health Care REIT;Financials 230 | HP;Helmerich & Payne;Energy 231 | HSY;Hershey Co;Consumer Staples 232 | HES;Hess Corp;Energy 233 | HPQ;Hewlett-Packard;Information Technology 234 | HD;Home Depot;Consumer Discretionary 235 | HON;Honeywell International;Industrials 236 | HRL;Hormel Foods;Consumer Staples 237 | HSP;Hospira;Health Care 238 | HST;Host Hotels & Resorts;Financials 239 | HCBK;Hudson City Bancorp;Financials 240 | HUM;Humana;Health Care 241 | HBAN;Huntington Bancshares;Financials 242 | ITW;Illinois Tool Works;Industrials 243 | IR;Ingersoll-Rand Plc;Industrials 244 | INTC;Intel Corp;Information Technology 245 | ICE;Intercontinental Exchange;Financials 246 | IBM;International Bus. Machines;Information Technology 247 | IFF;International Flavors/Fragr;Materials 248 | IP;International Paper;Materials 249 | IPG;Interpublic GroupCompanies;Consumer Discretionary 250 | INTU;Intuit;Information Technology 251 | ISRG;Intuitive Surgical;Health Care 252 | IVZ;INVESCO Ltd;Financials 253 | IRM;Iron Mountain;Financials 254 | JEC;Jacobs Engineering Group;Industrials 255 | JNJ;Johnson & Johnson;Health Care 256 | JCI;Johnson Controls;Consumer Discretionary 257 | JOY;Joy Global;Industrials 258 | JPM;JPMorgan Chase & Co;Financials 259 | JNPR;Juniper Networks;Information Technology 260 | KSU;Kansas City Southern;Industrials 261 | K;Kellogg Co;Consumer Staples 262 | GMCR;Keurig Green Mountain;Consumer Staples 263 | KEY;KeyCorp;Financials 264 | KMB;Kimberly-Clark;Consumer Staples 265 | KIM;Kimco Realty;Financials 266 | KMI;Kinder Morgan;Energy 267 | KLAC;KLA-Tencor Corp;Information Technology 268 | KSS;Kohl's Corp;Consumer Discretionary 269 | KRFT;Kraft Foods Group;Consumer Staples 270 | KR;Kroger Co;Consumer Staples 271 | LB;L Brands;Consumer Discretionary 272 | LLL;L-3 Communications Holdings;Industrials 273 | LH;Laboratory Corp American Holdings;Health Care 274 | LRCX;Lam Research;Information Technology 275 | LM;Legg Mason;Financials 276 | LEG;Leggett & Platt;Consumer Discretionary 277 | LEN;Lennar Corp;Consumer Discretionary 278 | LUK;Leucadia National;Financials 279 | LVLT;Level 3 Communications;Telecommunication Services 280 | LLY;Lilly (Eli);Health Care 281 | LNC;Lincoln National;Financials 282 | LLTC;Linear Technology Corp;Information Technology 283 | LMT;Lockheed Martin;Industrials 284 | L;Loews Corp;Financials 285 | LOW;Lowe'sCompanies;Consumer Discretionary 286 | LYB;LyondellBasell Industries;Materials 287 | MTB;M&T Bank;Financials 288 | MAC;Macerich Co;Financials 289 | M;Macy's;Consumer Discretionary 290 | MNK;Mallinckrodt plc;Health Care 291 | MRO;Marathon Oil;Energy 292 | MPC;Marathon Petroleum;Energy 293 | MAR;Marriott International;Consumer Discretionary 294 | MMC;Marsh & McLennan;Financials 295 | MLM;Martin Marietta Materials;Materials 296 | MAS;Masco Corp;Industrials 297 | MA;MasterCard;Information Technology 298 | MAT;Mattel;Consumer Discretionary 299 | MKC;McCormick & Co;Consumer Staples 300 | MCD;McDonald's Corp;Consumer Discretionary 301 | MHFI;McGraw Hill Financial;Financials 302 | MCK;McKesson Corp;Health Care 303 | MJN;Mead Johnson Nutrition;Consumer Staples 304 | MDT;Medtronic Plc;Health Care 305 | MRK;Merck & Co;Health Care 306 | MET;MetLife;Financials 307 | KORS;Michael Kors Holdings;Consumer Discretionary 308 | MCHP;Microchip Technology;Information Technology 309 | MU;Micron Technology;Information Technology 310 | MSFT;Microsoft Corp;Information Technology 311 | MHK;Mohawk Industries;Consumer Discretionary 312 | TAP;Molson Coors Brewing;Consumer Staples 313 | MDLZ;Mondelez International;Consumer Staples 314 | MON;Monsanto Co;Materials 315 | MNST;Monster Beverage;Consumer Staples 316 | MCO;Moody's Corp;Financials 317 | MS;Morgan Stanley;Financials 318 | MOS;Mosaic Co;Materials 319 | MSI;Motorola Solutions;Information Technology 320 | MUR;Murphy Oil;Energy 321 | MYL;Mylan NV;Health Care 322 | NDAQ;Nasdaq OMX Group;Financials 323 | NOV;Natl Oilwell Varco;Energy 324 | NAVI;Navient Corp;Financials 325 | NTAP;NetApp;Information Technology 326 | NFLX;NetFlix;Consumer Discretionary 327 | NWL;Newell Rubbermaid;Consumer Discretionary 328 | NFX;Newfield Exploration;Energy 329 | NEM;Newmont Mining;Materials 330 | NWSA;News Corporation;Consumer Discretionary 331 | NEE;NextEra Energy;Utilities 332 | NKE;NIKE;Consumer Discretionary 333 | NI;NiSource;Utilities 334 | NE;Noble Corp;Energy 335 | NBL;Noble Energy;Energy 336 | JWN;Nordstrom;Consumer Discretionary 337 | NSC;Norfolk Southern;Industrials 338 | NTRS;Northern Trust;Financials 339 | NOC;Northrop Grumman;Industrials 340 | NRG;NRG Energy;Utilities 341 | NUE;Nucor Corp;Materials 342 | NVDA;NVIDIA Corp;Information Technology 343 | ORLY;O'Reilly Automotive;Consumer Discretionary 344 | OXY;Occidental Petroleum;Energy 345 | OMC;Omnicom Group;Consumer Discretionary 346 | OKE;ONEOK;Energy 347 | ORCL;Oracle Corp;Information Technology 348 | OI;Owens-Illinois;Materials 349 | PCAR;PACCAR;Industrials 350 | PH;Parker-Hannifin;Industrials 351 | PDCO;PattersonCompanies;Health Care 352 | PAYX;Paychex;Information Technology 353 | PNR;Pentair plc;Industrials 354 | PBCT;People's United Financial;Financials 355 | POM;Pepco Holdings;Utilities 356 | PEP;PepsiCo;Consumer Staples 357 | PKI;PerkinElmer;Health Care 358 | PRGO;Perrigo;Health Care 359 | PFE;Pfizer;Health Care 360 | PCG;PG&E Corp;Utilities 361 | PM;Philip Morris International;Consumer Staples 362 | PSX;Phillips 66;Energy 363 | PNW;Pinnacle West Capital;Utilities 364 | PXD;Pioneer Natural Resources;Energy 365 | PBI;Pitney Bowes;Industrials 366 | PCL;Plum Creek Timber;Financials 367 | PNC;PNC Financial Services;Financials 368 | PPG;PPG Indus;Materials 369 | PPL;PPL Corp;Utilities 370 | PX;Praxair;Materials 371 | PCP;Precision Castparts;Industrials 372 | PCLN;Priceline Group (The);Consumer Discretionary 373 | PFG;Principal Financial Group;Financials 374 | PG;Procter & Gamble;Consumer Staples 375 | PGR;Progressive Corp,Ohio;Financials 376 | PLD;Prologis;Financials 377 | PRU;Prudential Financial;Financials 378 | PSA;Public Storage;Financials 379 | PEG;Public Svc Enterprises;Utilities 380 | PHM;PulteGroup;Consumer Discretionary 381 | PVH;PVH Corp;Consumer Discretionary 382 | QEP;QEP Resources;Energy 383 | QRVO;Qorvo;Information Technology 384 | QCOM;QUALCOMM;Information Technology 385 | PWR;Quanta Services;Industrials 386 | DGX;Quest Diagnostics;Health Care 387 | RL;Ralph Lauren Corp;Consumer Discretionary 388 | RRC;Range Resources;Energy 389 | RTN;Raytheon Co;Industrials 390 | O;Realtyome;Financials 391 | RHT;Red Hat;Information Technology 392 | REGN;Regeneron Pharmaceuticals;Health Care 393 | RF;Regions Financial;Financials 394 | RSG;Republic Services;Industrials 395 | RAI;Reynolds American;Consumer Staples 396 | RHI;Robert Half International;Industrials 397 | ROK;Rockwell Automation;Industrials 398 | COL;Rockwell Collins;Industrials 399 | ROP;Roper Technologies;Industrials 400 | ROST;Ross Stores;Consumer Discretionary 401 | RCL;Royal Caribbean Cruises;Consumer Discretionary 402 | R;Ryder System;Industrials 403 | SNDK;SanDisk Corp;Information Technology 404 | SCG;SCANA Corp;Utilities 405 | HSIC;Schein (Henry);Health Care 406 | SLB;Schlumberger Ltd;Energy 407 | SCHW;Schwab(Charles)Corp;Financials 408 | SNI;Scripps Networks Interact;Consumer Discretionary 409 | STX;Seagate Technology Plc;Information Technology 410 | SEE;Sealed Air;Materials 411 | SRE;Sempra Energy;Utilities 412 | SHW;Sherwin-Williams;Materials 413 | SIAL;Sigma-Aldrich;Materials 414 | SPG;Simon Property Group;Financials 415 | SWKS;Skyworks Solutions;Information Technology 416 | SLG;SL Green Realty;Financials 417 | SJM;Smucker (J.M.);Consumer Staples 418 | SNA;Snap-On;Industrials 419 | SO;Southern Co;Utilities 420 | LUV;Southwest Airlines;Industrials 421 | SWN;Southwestern Energy;Energy 422 | SE;Spectra Energy;Energy 423 | STJ;St. Jude Medical;Health Care 424 | SWK;Stanley Black & Decker;Industrials 425 | SPLS;Staples;Consumer Discretionary 426 | SBUX;Starbucks Corp;Consumer Discretionary 427 | HOT;Starwood Hotels&Res World;Consumer Discretionary 428 | STT;State Street Corp;Financials 429 | SRCL;Stericycle;Industrials 430 | SYK;Stryker Corp;Health Care 431 | STI;SunTrust Banks;Financials 432 | SYMC;Symantec Corp;Information Technology 433 | SYY;Sysco Corp;Consumer Staples 434 | TROW;T.Rowe Price Group;Financials 435 | TGT;Target Corp;Consumer Discretionary 436 | TEL;TE Connectivity;Information Technology 437 | TE;TECO Energy;Utilities 438 | TGNA;TEGNA;Consumer Discretionary 439 | THC;Tenet Healthcare;Health Care 440 | TDC;Teradata Corp;Information Technology 441 | TSO;Tesoro Petroleum Co;Energy 442 | TXN;Texas Instruments;Information Technology 443 | TXT;Textron;Industrials 444 | TMO;Thermo Fisher Scientific;Health Care 445 | TIF;Tiffany & Co;Consumer Discretionary 446 | TWX;Time Warner;Consumer Discretionary 447 | TWC;Time Warner Cable;Consumer Discretionary 448 | TJX;TJX Companies;Consumer Discretionary 449 | TMK;Torchmark Corp;Financials 450 | TSS;Total System Services;Information Technology 451 | TSCO;Tractor Supply;Consumer Discretionary 452 | RIG;Transocean Ltd;Energy 453 | TRV;TravelersCompanies;Financials 454 | TRIP;TripAdvisor;Consumer Discretionary 455 | FOXA;Twenty-First Century Fox;Consumer Discretionary 456 | TYC;Tyco International;Industrials 457 | TSN;Tyson Foods;Consumer Staples 458 | USB;U.S. Bancorp;Financials 459 | UA;Under Armour;Consumer Discretionary 460 | UNP;Union Pacific;Industrials 461 | UPS;United Parcel Service;Industrials 462 | URI;United Rentals;Industrials 463 | UTX;United Technologies;Industrials 464 | UNH;UnitedHealth Group;Health Care 465 | UHS;Univl Health Services;Health Care 466 | UNM;Unum Group;Financials 467 | URBN;Urban Outfitters;Consumer Discretionary 468 | VLO;Valero Energy;Energy 469 | VAR;Varian Medical Systems;Health Care 470 | VTR;Ventas;Financials 471 | VRSN;VeriSign;Information Technology 472 | VZ;Verizon Communications;Telecommunication Services 473 | VRTX;Vertex Pharmaceuticals;Health Care 474 | VFC;VF Corp;Consumer Discretionary 475 | VIAB;Viacom;Consumer Discretionary 476 | V;Visa;Information Technology 477 | VNO;Vornado Realty Trust;Financials 478 | VMC;Vulcan Materials;Materials 479 | WMT;Wal-Mart Stores;Consumer Staples 480 | WBA;Walgreens Boots Alliance;Consumer Staples 481 | DIS;Walt Disney Co;Consumer Discretionary 482 | WM;Waste Management;Industrials 483 | WAT;Waters Corp;Health Care 484 | WEC;WEC Energy Group;Utilities 485 | WFC;Wells Fargo;Financials 486 | WDC;Western Digital;Information Technology 487 | WU;Western Union;Information Technology 488 | WY;Weyerhaeuser Co;Financials 489 | WHR;Whirlpool Corp;Consumer Discretionary 490 | WFM;Whole Foods Market;Consumer Staples 491 | WMB;WilliamsCompanies;Energy 492 | WYN;Wyndham Worldwide;Consumer Discretionary 493 | WYNN;Wynn Resorts;Consumer Discretionary 494 | XEL;Xcel Energy;Utilities 495 | XRX;Xerox Corp;Information Technology 496 | XLNX;Xilinx;Information Technology 497 | XL;XL Group Plc;Financials 498 | XYL;Xylem;Industrials 499 | YHOO;Yahoo;Information Technology 500 | YUM;Yum Brands;Consumer Discretionary 501 | ZBH;Zimmer Biomet Holdings;Health Care 502 | ZTS;Zoetis;Health Care 503 | --------------------------------------------------------------------------------