package com.hadooparchitecturebook.movingavg.topology;
import backtype.storm.task.OutputCollector;
import backtype.storm.task.TopologyContext;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.topology.base.BaseRichBolt;
import backtype.storm.tuple.Tuple;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
/**
* Calculate a moving average over stock tick data.
*
* SMA algo adapted from
* http://rosettacode.org/wiki/Averages/Simple_moving_average#Java.
* See http://www.gnu.org/licenses/fdl-1.2.html
*/
public class CalcMovingAvgBolt extends BaseRichBolt {
private OutputCollector outputCollector;
// Create map to hold tuples for unique tickers:
private Map<String, LinkedList<Double>> windowMap;
private final int period = 5;
@Override
public void prepare(Map config,
TopologyContext topologyContext,
OutputCollector collector) {
outputCollector = collector;
windowMap = new HashMap<String, LinkedList<Double>>();
}
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
// Since we're not actually emitting any values from this bolt we don't declare any output fields.
}
/**
* For each ticker in input stream, calculate the moving average.
*/
@Override
public void execute(Tuple tuple) {
String ticker = tuple.getStringByField("ticker");
String quote = tuple.getStringByField("price");
Double num = Double.parseDouble(quote);
LinkedList<Double> window = (LinkedList)getQuotesForTicker(ticker);
window.add(num);
// Print to System.out for test purposes. In a real implementation this
// would go to a downstream bolt for further processing, or persisted, etc.
System.out.println("----------------------------------------");
System.out.println("moving average for ticker " + ticker + "=" + getAvg(window));
System.out.println("----------------------------------------");
}
/**
* Return the current window of prices for a ticker.
*/
private LinkedList<Double> getQuotesForTicker(String ticker) {
LinkedList<Double> window = windowMap.get(ticker);
if (window == null) {
window = new LinkedList<Double>();
windowMap.put(ticker, window);
}
return window;
}
/**
* Return the average for the current window.
*/
public double getAvg(LinkedList<Double> window) {
if (window.isEmpty()) {
return 0;
}
// Drop the oldest price from the window:
if (window.size() > period) {
window.remove();
}
double sum = 0;
for (Double price : window) {
sum += price.doubleValue();
}
return sum / window.size();
}
}