Here is problem to be solved
TradingAlgorithm employs a trading algorithm which, based on the prices it receives, will return a trade to execute. The trading algorithm must implement the following interface:
public interface TradingAlgorithm { Trade buildTrades(Price price); }
A Price is made up of a product name and a numerical price.
A Trade is made up of a product name, a direction (buy or sell), a numerical price and a quantity.
Write an implementation of the TradingAlgorithm interface that satisfies the following:
Accepts an array of product names (String[]) at the time of construction. These are the products that can be traded. Returns a buy trade for a quantity of 1000 at the newest price, if the simple average of the last 4 prices is greater than the oldest price in that collection of 4 prices e.g. {1,2,3,4} will result in a trade, as will {4,5,6,4} but {9,4,2,1} will not. In other words a trade will be made when the simple average of a moving window of prices has an upward trend. The implementation should be thread-safe, and performant.
Here is my implementation.
public class Price {
private String product;
private BigDecimal price;
// getters and setters
}
public class Trade {
private String product;
private String direction;
private BigDecimal price;
private Integer quantity;
// getters and setters
}
public class DirectionAlgorithmImpl implements DirectionAlgorithm {
private static final int PRICE_SAMPLES_SIZE = 4;
private static final String DIRECTION_BUY = "buy";
private static final String DIRECTION_SELL = "sell";
private final Queue<BigDecimal> priceQueue = new LinkedList<>();
private BigDecimal movingSum = BigDecimal.ZERO;
private final Object lock = new Object();
@Override
public String getDirection(BigDecimal newPrice) {
synchronized (lock) {
priceQueue.add(newPrice);
movingSum = movingSum.add(newPrice);
if (priceQueue.size() > PRICE_SAMPLES_SIZE) {
BigDecimal removedPrice = priceQueue.remove();
BigDecimal oldestPrice = priceQueue.peek();
movingSum = movingSum.subtract(removedPrice);
BigDecimal sma = movingSum.divide(BigDecimal.valueOf(PRICE_SAMPLES_SIZE));
return getDirection(sma, oldestPrice);
} else if (priceQueue.size() == PRICE_SAMPLES_SIZE) {
BigDecimal oldestPrice = priceQueue.peek();
BigDecimal sma = movingSum.divide(BigDecimal.valueOf(PRICE_SAMPLES_SIZE));
return getDirection(sma, oldestPrice);
}
return null;
}
}
private String getDirection(BigDecimal sma, BigDecimal oldestPrice) {
return sma.compareTo(oldestPrice) > 0 ? DIRECTION_BUY : DIRECTION_SELL;
}
}
public class TradingAlgorithmImpl implements TradingAlgorithm {
private static final Integer TRADE_QUANTITY = 1000;
private final ConcurrentMap<String, DirectionAlgorithm> productToDirectionAlgoMap = new ConcurrentHashMap<>();
public TradingAlgorithmImpl(String[] productNames) {
if (productNames == null || productNames.length == 0) {
throw new IllegalArgumentException("product names cannot be null or empty");
}
for (String productName : productNames) {
productToDirectionAlgoMap.put(productName, new DirectionAlgorithmImpl());
}
}
@Override
public Trade buildTrades(Price price) {
String product = price.getProduct();
BigDecimal newPrice = price.getPrice();
// time complexity: O(1)
DirectionAlgorithm directionAlgorithm = productToDirectionAlgoMap.get(product);
if (directionAlgorithm != null) {
// time complexity: O(1)
String direction = directionAlgorithm.getDirection(newPrice);
if (direction != null) {
return new Trade(product, direction, newPrice, TRADE_QUANTITY);
}
}
return null;
}
}
Questions
- Is this code thread-safe?
- Is there any way its time complexity can be improved?