/******************************************************************************* * Copyright (c) 2013 Luigi Sgro. All rights reserved. This * program and the accompanying materials are made available under the terms of * the Eclipse Public License v1.0 which accompanies this distribution, and is * available at http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Luigi Sgro - initial API and implementation ******************************************************************************/ package com.quantcomponents.marketdata; import java.net.ConnectException; import java.util.Date; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; import com.quantcomponents.core.exceptions.RequestFailedException; import com.quantcomponents.core.model.DataType; import com.quantcomponents.core.model.ITaskMonitor; import com.quantcomponents.marketdata.IMarketDataProvider.ITickListener; public abstract class RealTimeMarketDataManager extends MarketDataManager implements IRealTimeMarketDataManager { /** * Fills a mutable time series with real-time data */ public class RealtimeDataFeeder { private final IRealTimeMarketDataProvider provider; private boolean autoUpdate; private final IMutableTickTimeSeries timeSeries; private final DataType dataType; private ITickListener listener; private class TickListener implements IMarketDataProvider.ITickListener { @Override public void onTick(ITickPoint tick) { if (!isRunning()) { logger.log(Level.SEVERE, "Data received with feeder stopped"); return; } if (!dataType.includes(tick.getDataType())) { return; } ITickPoint lastExistingTick = (ITickPoint) timeSeries.getLast(); if (lastExistingTick != null && tick.getIndex().before(lastExistingTick.getIndex())) { logger.log(Level.WARNING, "Received old tick: " + tick.getIndex() + "; last available: " + lastExistingTick.getIndex()); } else { timeSeries.addLast(tick); } } } public RealtimeDataFeeder(IRealTimeMarketDataProvider provider, IMutableTickTimeSeries timeSeries, DataType dataType) { this.provider = provider; this.timeSeries = timeSeries; this.dataType = dataType; } public void start() throws ConnectException, RequestFailedException { if (!autoUpdate) { autoUpdate = true; listener = new TickListener(); provider.startTicks(timeSeries.getContract(), listener); } } public void stop() throws ConnectException { if (autoUpdate) { autoUpdate = false; provider.stopTicks(timeSeries.getContract(), listener); listener = null; } } public boolean isRunning() { return autoUpdate; } } private static final Logger logger = Logger.getLogger(RealTimeMarketDataManager.class.getName()); protected final Map<IStockDatabase, RealtimeDataFeeder> stockDbFeeders = new ConcurrentHashMap<IStockDatabase, RealtimeDataFeeder>(); // Overrides superclass method (same signature) but expects a parameter of type IRealTimeMarketDataProvider public void setMarketDataProvider(IMarketDataProvider marketDataProvider) { IRealTimeMarketDataProvider.class.cast(marketDataProvider); // ensure correct type of parameter super.setMarketDataProvider(marketDataProvider); } protected IRealTimeMarketDataProvider getMarketDataProvider() { return (IRealTimeMarketDataProvider) super.getMarketDataProvider(); } @Override public void removeStockDatabase(IStockDatabase stockDb) throws ConnectException, RequestFailedException { RealtimeDataFeeder feeder = stockDbFeeders.remove(stockDb); if (feeder != null) { feeder.stop(); } super.removeStockDatabase(stockDb); } @Override public void startRealtimeUpdate(IStockDatabase stockDb, boolean fillHistoricalGap, ITaskMonitor taskMonitor) throws ConnectException, RequestFailedException { RealtimeDataFeeder realtimeFeeder = null; synchronized (stockDb) { realtimeFeeder = stockDbFeeders.get(stockDb); if (realtimeFeeder == null) { realtimeFeeder = new RealtimeDataFeeder(getMarketDataProvider(), stockDb.getTickTimeSeries(), stockDb.getDataType()); stockDbFeeders.put(stockDb, realtimeFeeder); } else { if (realtimeFeeder.isRunning()) { return; } } } if (fillHistoricalGap) { if (stockDb.getVirtualTimeSeries().getLast() != null) { Date lastDataIndex = stockDb.getVirtualTimeSeries().getLast().getIndex(); fillHistoricalData(stockDb, lastDataIndex, new Date(), taskMonitor); } } realtimeFeeder.start(); } @Override public void stopRealtimeUpdate(IStockDatabase stockDb) throws ConnectException, RequestFailedException { RealtimeDataFeeder realtimeFeeder = stockDbFeeders.get(stockDb); if (realtimeFeeder == null) { throw new IllegalArgumentException("Stock DB " + stockDb + " has no realtime feeding"); } realtimeFeeder.stop(); } @Override public boolean isRealtimeUpdate(IStockDatabase stockDb) throws ConnectException, RequestFailedException { RealtimeDataFeeder realtimeFeeder = stockDbFeeders.get(stockDb); if (realtimeFeeder == null) { return false; } return realtimeFeeder.isRunning(); } }