StocksRepositoryImpl.java
/**
* Copyright 2012 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.googlecode.phisix.api.repository;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.client.Client;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
import org.jboss.resteasy.client.jaxrs.engines.URLConnectionEngine;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Transaction;
import com.google.appengine.api.datastore.TransactionOptions;
import com.google.appengine.api.memcache.Expiration;
import com.google.appengine.api.memcache.MemcacheService;
import com.google.appengine.api.memcache.MemcacheServiceFactory;
import com.googlecode.phisix.api.client.PhisixClient;
import com.googlecode.phisix.api.client.PseClient;
import com.googlecode.phisix.api.client.PseFramesClient;
import com.googlecode.phisix.api.ext.StocksProvider;
import com.googlecode.phisix.api.model.Price;
import com.googlecode.phisix.api.model.Stock;
import com.googlecode.phisix.api.model.Stocks;
/**
*
* @author Edge Dalmacio
*
*/
public class StocksRepositoryImpl implements StocksRepository {
private final PseClient pseClient;
private final PhisixClient phisixClient;
private final Pattern pattern = Pattern.compile("\"listedCompany_companyId\":\"(\\d+)\".*\"securityId\":\"(\\d+)\"");
private final DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
private final MemcacheService memcache = MemcacheServiceFactory.getMemcacheService();
public StocksRepositoryImpl() {
this.pseClient = new PseFramesClient();
Client client = ((ResteasyClientBuilder) ResteasyClientBuilder.newBuilder())
.httpEngine(new URLConnectionEngine())
.register(StocksProvider.class)
.build();
ResteasyWebTarget target = (ResteasyWebTarget) client.target("https://phisix-api2.appspot.com");
this.phisixClient = target.proxy(PhisixClient.class);
}
public StocksRepositoryImpl(PseClient pseClient, PhisixClient phisixClient) {
this.pseClient = pseClient;
this.phisixClient = phisixClient;
}
@Override
public Stocks findAll() {
Stocks stocks = (Stocks) memcache.get("ALL");
if (stocks == null) {
stocks = pseClient.getSecuritiesAndIndicesForPublic();
memcache.put("ALL", stocks, Expiration.byDeltaSeconds(60));
}
return stocks;
}
@Override
public String findBySymbol(String symbol) {
String securityOrCompany = pseClient.findSecurityOrCompany(
"findSecurityOrCompany", true,
String.format("start=0&limit=1&query=%s", symbol));
Matcher matcher = pattern.matcher(securityOrCompany);
if (matcher.find()) {
return pseClient.companyInfo("fetchHeaderData", true,
String.format("company=%s&security=%s",
matcher.group(1),
matcher.group(2)));
}
return null;
}
@Override
public Stocks findBySymbolAndTradingDate(String symbol, Date tradingDate) {
Key stockKey = KeyFactory.createKey("Stock", symbol);
Entity stockEntity;
try {
stockEntity = datastore.get(stockKey);
} catch (EntityNotFoundException e) {
throw new NotFoundException();
}
Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("GMT+8"));
calendar.setTime(tradingDate);
Key historyKey = KeyFactory.createKey(stockKey, "History", calendar.getTimeInMillis());
Entity historyEntity;
try {
historyEntity = datastore.get(historyKey);
} catch (EntityNotFoundException e) {
throw new NotFoundException();
}
Price price = new Price();
price.setCurrency("PHP");
price.setAmount(new BigDecimal((String) historyEntity.getProperty("close")));
Stock stock = new Stock();
stock.setSymbol(symbol);
stock.setName((String) stockEntity.getProperty("name"));
stock.setPrice(price);
stock.setVolume((Long) historyEntity.getProperty("volume"));
Stocks stocks = new Stocks();
stocks.setAsOf(calendar);
stocks.getStocks().add(stock);
return stocks;
}
@Override
public Stocks findBySymbolAndTradingDate(String symbol, String tradingDate) {
return phisixClient.getStockByDate(symbol, tradingDate);
}
@Override
public void save(Stocks stocks) {
Calendar tradingDate = retrieveTradingDate(stocks);
for (Stock stock : stocks.getStocks()) {
Transaction txn = datastore.beginTransaction(TransactionOptions.Builder.withXG(true));
try {
Entity stockEntity;
Key key = KeyFactory.createKey("Stock", stock.getSymbol());
try {
stockEntity = datastore.get(key);
} catch (EntityNotFoundException e) {
stockEntity = new Entity(key);
stockEntity.setUnindexedProperty("name", stock.getName());
datastore.put(stockEntity);
}
Entity historyEntity = new Entity("History", tradingDate.getTimeInMillis(), key);
historyEntity.setProperty("tradingDate", tradingDate.getTime());
historyEntity.setUnindexedProperty("close", stock.getPrice().getAmount().toPlainString());
historyEntity.setUnindexedProperty("volume", stock.getVolume());
datastore.put(historyEntity);
txn.commit();
} finally {
if (txn.isActive()) {
txn.rollback();
}
}
}
}
private Calendar retrieveTradingDate(Stocks stocks) {
Calendar tradingDate = stocks.getAsOf();
tradingDate.set(Calendar.HOUR_OF_DAY, 0);
tradingDate.set(Calendar.MINUTE, 0);
tradingDate.set(Calendar.SECOND, 0);
return tradingDate;
}
}