I'm developing a web application allowing a logged-in user to find most correlated stocks for selected stock ticker. The user chooses a ticker, period of time (10, 30, 60, 90) and type of correlation (KENDALLS, PEARSONS, SPEARMANS). Then on the server side, the 'magic' happens and the server responds to the client by providing a list of stocks sorted by correlation value.
My controller:
@RequestMapping(value = "/stock/correlation", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
@RolesAllowed(AuthoritiesConstants.USER)
public TreeSet<StockStatistic> getCorrelationForSelectedTicker(@RequestParam(value = "correlation_type", required = true) CorrelationType correlationType, @RequestParam(value = "ticker", required = true) StockTicker ticker, @RequestParam(value = "period", required = true) int period) {
return statisticService.computeCorrelation(ticker, period, correlationType);
}
Here is my service:
- In the service, I have
step
andisComputing
variables, because when the user starts computing correlation, every second I ask the server if a correlation is computing. I then calculate the percentage of progress and show it to the user via a progress bar. Is my way of informing user about progress correct, or should it be done better? - I wanted to use multithreading as I want to save time when computing a correlation. I made an inner class as you can see below. Is it correct?
@Service
public class CorrelationService {
@Inject
private StockDetailsRepository stockDetailsRepository;
private Correlation correlation;
private int step;
private boolean isComputing;
public TreeSet<StockStatistic> computeCorrelation(StockTicker correlationForTicker, int period, CorrelationType correlationType) {
correlation = getCorrelationImpl(correlationType, correlationForTicker, period, stockDetailsRepository);
ExecutorService executor = Executors.newFixedThreadPool(5);
for (StockTicker ticker : correlation.getTickersWithOutOneAnalysed()) {
executor.submit(new CorrelationStock(ticker));
}
executor.shutdown();
try {
executor.awaitTermination(1, TimeUnit.HOURS);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.step = 0;
this.isComputing = false;
return correlation.getCorrelationTreeSet();
}
private Correlation getCorrelationImpl(CorrelationType correlationType, StockTicker correlationForTicker, int period, StockDetailsRepository stockDetailsRepository2) {
switch (correlationType) {
case KENDALLS:
return new KendallsCorrelationImpl(correlationForTicker, period, stockDetailsRepository);
case PEARSONS:
return new PearsonCorrelationImpl(correlationForTicker, period, stockDetailsRepository);
case SPEARMANS:
return new SpearmansCorrelationImpl(correlationForTicker, period, stockDetailsRepository);
}
return null;
}
public int getStep() {
return step;
}
public boolean isComputing() {
return isComputing;
}
class CorrelationStock implements Runnable {
StockTicker analysedTicker;
public CorrelationStock(StockTicker analysedTicker) {
this.analysedTicker = analysedTicker;
}
@Override
public void run() {
correlation.compute(analysedTicker);
}
}
}
Here is my helper class:
public abstract class CorrelationVariables {
protected StockDetailsRepository stockDetailsRepository;
protected List<StockTicker> tickersWithOutOneAnalysed;
protected TreeSet<StockStatistic> correlationTreeSet;
protected double[] sourceClosePrices;
protected final int period;
public CorrelationVariables(StockTicker correlationForTicker, int period, StockDetailsRepository stockDetailsRepository) {
this.stockDetailsRepository = stockDetailsRepository;
this.period = period;
this.correlationTreeSet = new TreeSet<>();
List<StockDetails> stockDetailsToAnalysed = getContent(correlationForTicker);
this.sourceClosePrices = getClosePrices(stockDetailsToAnalysed);
EnumSet<StockTicker> tickersToScan = complementOf(EnumSet.of(correlationForTicker));
this.tickersWithOutOneAnalysed = new ArrayList<StockTicker>(tickersToScan);
}
public List<StockTicker> getTickersWithOutOneAnalysed() {
return tickersWithOutOneAnalysed;
}
public TreeSet<StockStatistic> getCorrelationTreeSet() {
return correlationTreeSet;
}
public void setCorrelationTreeSet(TreeSet<StockStatistic> correlationTreeSet) {
this.correlationTreeSet = correlationTreeSet;
}
public double[] getClosePricesToAnalysed() {
return sourceClosePrices;
}
protected double[] getClosePrices(List<StockDetails> stockDetails) {
double[] closePrices = new double[stockDetails.size()];
int index = 0;
for (StockDetails stds : stockDetails) {
closePrices[index++] = stds.getClosePrice().doubleValue();
}
return closePrices;
}
protected List<StockDetails> getContent(StockTicker ticker) {
return stockDetailsRepository.findByStockTickerOrderByDateDesc(ticker, new PageRequest(1, period)).getContent();
}
}
And interface:
public interface Correlation {
void compute(StockTicker ticker);
List<StockTicker> getTickersWithOutOneAnalysed();
TreeSet<StockStatistic> getCorrelationTreeSet();
}
And one of my implementation (the second and third implementation looks same but have different variables KendallsCorrelation
, kendallsCorrelation
, and SpearmansCorrelation spearmansCorrelation
):
public class PearsonCorrelationImpl extends CorrelationVariables implements Correlation {
private PearsonsCorrelation pearsonsCorrelation;
public PearsonCorrelationImpl(StockTicker correlationForTicker, int period, StockDetailsRepository stockDetailsRepository) {
super(correlationForTicker, period, stockDetailsRepository);
pearsonsCorrelation = new PearsonsCorrelation();
}
public void compute(StockTicker ticker) {
double[] closePricesToCompare = getClosePrices(getContent(ticker));
Double correlation = 0.0;
if (closePricesToCompare.length == sourceClosePrices.length) {
correlation = pearsonsCorrelation.correlation(sourceClosePrices, closePricesToCompare);
}
StockStatistic stockCorrelation = new StockStatistic(correlation, ticker);
correlationTreeSet.add(stockCorrelation);
}
}
- Is my way of computing the correlation for selected options by user fine, or should it be refactored?
- I am not sure about creating three separated classes (a lot of duplicated code) for each type of correlation, but I can't figure out another way.