/*
* Copyright (c) 2004-2011 Marco Maccaferri and others.
* 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:
* Marco Maccaferri - initial API and implementation
*/
package org.eclipsetrader.core.markets;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.core.internal.runtime.AdapterManager;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.Status;
import org.eclipsetrader.core.feed.Bar;
import org.eclipsetrader.core.feed.IBar;
import org.eclipsetrader.core.feed.IBarOpen;
import org.eclipsetrader.core.feed.IBook;
import org.eclipsetrader.core.feed.IConnectorOverride;
import org.eclipsetrader.core.feed.IFeedConnector;
import org.eclipsetrader.core.feed.IFeedConnector2;
import org.eclipsetrader.core.feed.IFeedIdentifier;
import org.eclipsetrader.core.feed.IFeedSubscription;
import org.eclipsetrader.core.feed.IFeedSubscription2;
import org.eclipsetrader.core.feed.ILastClose;
import org.eclipsetrader.core.feed.IPrice;
import org.eclipsetrader.core.feed.IPricingEnvironment;
import org.eclipsetrader.core.feed.IPricingListener;
import org.eclipsetrader.core.feed.IQuote;
import org.eclipsetrader.core.feed.ISubscriptionListener;
import org.eclipsetrader.core.feed.ITodayOHL;
import org.eclipsetrader.core.feed.ITrade;
import org.eclipsetrader.core.feed.PricingDelta;
import org.eclipsetrader.core.feed.PricingEvent;
import org.eclipsetrader.core.feed.QuoteDelta;
import org.eclipsetrader.core.feed.QuoteEvent;
import org.eclipsetrader.core.feed.TimeSpan;
import org.eclipsetrader.core.instruments.ISecurity;
import org.eclipsetrader.core.internal.CoreActivator;
/**
* Pricing environment implementation based on markets.
*
* <p>Feed connectors are choosen based on the market that holds a security.
* If securities don't belong to a market, the default feed connector is used.</p>
*
* @since 1.0
*/
public class MarketPricingEnvironment implements IPricingEnvironment {
private IMarketService marketService;
class PricingStatus {
ITrade trade;
IQuote quote;
ITodayOHL todayOHL;
ILastClose lastClose;
IBook book;
IBarOpen todayBarOpen;
IBar todayBar;
List<PricingDelta> deltas = new ArrayList<PricingDelta>();
}
class SubscriptionStatus {
Map<IFeedConnector, IFeedSubscription> subscriptions = new HashMap<IFeedConnector, IFeedSubscription>();
List<ISecurity> securities = new ArrayList<ISecurity>();
}
class SubscriptionStatus2 {
Map<IFeedConnector2, IFeedSubscription2> subscriptions = new HashMap<IFeedConnector2, IFeedSubscription2>();
List<ISecurity> securities = new ArrayList<ISecurity>();
}
private Map<ISecurity, PricingStatus> securitiesMap = new HashMap<ISecurity, PricingStatus>();
private Map<IFeedIdentifier, SubscriptionStatus> identifiersMap = new HashMap<IFeedIdentifier, SubscriptionStatus>();
private Map<IFeedIdentifier, SubscriptionStatus2> identifiersMap2 = new HashMap<IFeedIdentifier, SubscriptionStatus2>();
private ListenerList listeners = new ListenerList(ListenerList.IDENTITY);
private ISubscriptionListener listener = new ISubscriptionListener() {
@Override
public void quoteUpdate(QuoteEvent event) {
processUpdateQuotes(event.getIdentifier(), event.getDelta());
}
};
private PropertyChangeListener propertyChangeListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getSource() instanceof IMarket) {
handleMarketChanges((IMarket) evt.getSource(), evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
}
if (evt.getSource() instanceof IFeedIdentifier) {
for (Iterator<Entry<IFeedIdentifier, SubscriptionStatus>> iter = identifiersMap.entrySet().iterator(); iter.hasNext();) {
Entry<IFeedIdentifier, SubscriptionStatus> entry = iter.next();
if (entry.getKey() == evt.getSource()) {
iter.remove();
identifiersMap.put((IFeedIdentifier) evt.getSource(), entry.getValue());
break;
}
}
}
}
};
private IMarketStatusListener marketStatusListener = new IMarketStatusListener() {
@Override
public void marketStatusChanged(MarketStatusEvent event) {
IMarket market = event.getMarket();
if (!market.isOpen()) {
// fireTodayBarCloseEvent();
}
}
};
protected MarketPricingEnvironment() {
}
public MarketPricingEnvironment(IMarketService marketService) {
this(marketService, null);
}
public MarketPricingEnvironment(IMarketService marketService, ISecurity[] securities) {
this.marketService = marketService;
marketService.addMarketStatusListener(marketStatusListener);
for (IMarket market : marketService.getMarkets()) {
PropertyChangeSupport propertyChangeSupport = (PropertyChangeSupport) market.getAdapter(PropertyChangeSupport.class);
if (propertyChangeSupport != null) {
propertyChangeSupport.addPropertyChangeListener(propertyChangeListener);
}
}
if (securities != null) {
addSecurities(securities);
}
}
protected IMarket getMarketsForSecurity(ISecurity security) {
if (marketService != null) {
for (IMarket market : marketService.getMarkets()) {
if (market.hasMember(security)) {
return market;
}
}
}
return null;
}
public void addSecurity(ISecurity security) {
IFeedConnector connector = getDefaultConnector();
IMarket market = getMarketsForSecurity(security);
if (market != null && market.getLiveFeedConnector() != null) {
connector = market.getLiveFeedConnector();
}
IConnectorOverride override = (IConnectorOverride) AdapterManager.getDefault().getAdapter(security, IConnectorOverride.class);
if (override != null) {
if (override.getLiveFeedConnector() != null) {
connector = override.getLiveFeedConnector();
}
}
if (connector != null) {
subscribeSecurity(security, connector);
}
}
public void addSecurities(ISecurity[] securities) {
for (ISecurity security : securities) {
addSecurity(security);
}
}
public void addLevel2Security(ISecurity security) {
IFeedConnector2 connector = null;
IMarket market = getMarketsForSecurity(security);
if (market != null && market.getLiveFeedConnector() instanceof IFeedConnector2) {
connector = (IFeedConnector2) market.getLiveFeedConnector();
}
if (connector != null) {
subscribeSecurity2(security, connector);
}
}
protected IFeedConnector getDefaultConnector() {
if (CoreActivator.getDefault() == null) {
return null;
}
return CoreActivator.getDefault().getDefaultConnector();
}
protected void subscribeSecurity(ISecurity security, IFeedConnector connector) {
IFeedIdentifier identifier = (IFeedIdentifier) security.getAdapter(IFeedIdentifier.class);
PricingStatus pricingStatus;
synchronized (securitiesMap) {
pricingStatus = securitiesMap.get(security);
if (pricingStatus == null) {
pricingStatus = new PricingStatus();
securitiesMap.put(security, pricingStatus);
}
}
if (identifier != null) {
SubscriptionStatus subscriptionStatus = identifiersMap.get(identifier);
if (subscriptionStatus == null) {
subscriptionStatus = new SubscriptionStatus();
identifiersMap.put(identifier, subscriptionStatus);
PropertyChangeSupport propertyChangeSupport = (PropertyChangeSupport) identifier.getAdapter(PropertyChangeSupport.class);
if (propertyChangeSupport != null) {
propertyChangeSupport.addPropertyChangeListener(propertyChangeListener);
}
}
subscriptionStatus.securities.add(security);
IFeedSubscription subscription = subscriptionStatus.subscriptions.get(connector);
if (subscription == null) {
subscription = connector.subscribe(identifier);
subscriptionStatus.subscriptions.put(connector, subscription);
subscription.addSubscriptionListener(listener);
}
pricingStatus.trade = subscription.getTrade();
if (pricingStatus.trade != null) {
pricingStatus.deltas.add(new PricingDelta(null, pricingStatus.trade));
}
pricingStatus.quote = subscription.getQuote();
if (pricingStatus.quote != null) {
pricingStatus.deltas.add(new PricingDelta(null, pricingStatus.quote));
}
pricingStatus.todayOHL = subscription.getTodayOHL();
if (pricingStatus.todayOHL != null) {
pricingStatus.deltas.add(new PricingDelta(null, pricingStatus.todayOHL));
}
pricingStatus.lastClose = subscription.getLastClose();
if (pricingStatus.lastClose != null) {
pricingStatus.deltas.add(new PricingDelta(null, pricingStatus.lastClose));
}
}
notifyListeners();
}
protected void subscribeSecurity2(ISecurity security, IFeedConnector2 connector) {
IFeedIdentifier identifier = (IFeedIdentifier) security.getAdapter(IFeedIdentifier.class);
PricingStatus pricingStatus;
synchronized (securitiesMap) {
pricingStatus = securitiesMap.get(security);
if (pricingStatus == null) {
pricingStatus = new PricingStatus();
securitiesMap.put(security, pricingStatus);
}
}
if (identifier != null) {
SubscriptionStatus2 subscriptionStatus = identifiersMap2.get(identifier);
if (subscriptionStatus == null) {
subscriptionStatus = new SubscriptionStatus2();
identifiersMap2.put(identifier, subscriptionStatus);
PropertyChangeSupport propertyChangeSupport = (PropertyChangeSupport) identifier.getAdapter(PropertyChangeSupport.class);
if (propertyChangeSupport != null) {
propertyChangeSupport.addPropertyChangeListener(propertyChangeListener);
}
}
subscriptionStatus.securities.add(security);
IFeedSubscription2 subscription = subscriptionStatus.subscriptions.get(connector);
if (subscription == null) {
subscription = connector.subscribeLevel2(identifier);
subscriptionStatus.subscriptions.put(connector, subscription);
subscription.addSubscriptionListener(listener);
}
pricingStatus.trade = subscription.getTrade();
pricingStatus.quote = subscription.getQuote();
pricingStatus.todayOHL = subscription.getTodayOHL();
pricingStatus.lastClose = subscription.getLastClose();
pricingStatus.book = subscription.getBook();
}
}
public void removeSecurity(ISecurity security) {
IFeedIdentifier identifier = (IFeedIdentifier) security.getAdapter(IFeedIdentifier.class);
if (identifier != null) {
SubscriptionStatus subscriptionStatus = identifiersMap.get(identifier);
if (subscriptionStatus != null) {
subscriptionStatus.securities.remove(security);
if (subscriptionStatus.securities.size() == 0) {
for (IFeedSubscription subscription : subscriptionStatus.subscriptions.values()) {
subscription.removeSubscriptionListener(listener);
subscription.dispose();
}
identifiersMap.remove(identifier);
PropertyChangeSupport propertyChangeSupport = (PropertyChangeSupport) identifier.getAdapter(PropertyChangeSupport.class);
if (propertyChangeSupport != null) {
propertyChangeSupport.removePropertyChangeListener(propertyChangeListener);
}
}
}
}
synchronized (securitiesMap) {
if (!identifiersMap.containsKey(identifier) && !identifiersMap2.containsKey(identifier)) {
securitiesMap.remove(security);
}
}
}
public void removeLevel2Security(ISecurity security) {
IFeedIdentifier identifier = (IFeedIdentifier) security.getAdapter(IFeedIdentifier.class);
if (identifier != null) {
SubscriptionStatus2 subscriptionStatus2 = identifiersMap2.get(identifier);
if (subscriptionStatus2 != null) {
subscriptionStatus2.securities.remove(security);
if (subscriptionStatus2.securities.size() == 0) {
for (IFeedSubscription2 subscription : subscriptionStatus2.subscriptions.values()) {
subscription.removeSubscriptionListener(listener);
subscription.dispose();
}
identifiersMap2.remove(identifier);
PropertyChangeSupport propertyChangeSupport = (PropertyChangeSupport) identifier.getAdapter(PropertyChangeSupport.class);
if (propertyChangeSupport != null) {
propertyChangeSupport.removePropertyChangeListener(propertyChangeListener);
}
}
}
}
synchronized (securitiesMap) {
if (!identifiersMap.containsKey(identifier) && !identifiersMap2.containsKey(identifier)) {
securitiesMap.remove(security);
}
}
}
public void removeSecurities(ISecurity[] securities) {
for (ISecurity security : securities) {
removeSecurity(security);
}
}
protected void handleMarketChanges(IMarket market, String propertyName, Object oldValue, Object newValue) {
if (IMarket.PROP_LIVE_FEED_CONNECTOR.equals(propertyName)) {
IFeedConnector oldConnector = (IFeedConnector) oldValue;
IFeedConnector newConnector = (IFeedConnector) newValue;
if (newConnector == null) {
newConnector = getDefaultConnector();
}
for (ISecurity security : market.getMembers()) {
if (securitiesMap.containsKey(security)) {
IFeedIdentifier identifier = (IFeedIdentifier) security.getAdapter(IFeedIdentifier.class);
if (identifier != null) {
SubscriptionStatus subscriptionStatus = identifiersMap.get(identifier);
if (subscriptionStatus != null) {
IFeedSubscription subscription = subscriptionStatus.subscriptions.get(oldConnector);
if (subscription != null) {
subscription.removeSubscriptionListener(listener);
subscription.dispose();
}
}
if (newConnector != null) {
IFeedSubscription subscription = newConnector.subscribe(identifier);
subscriptionStatus.subscriptions.put(newConnector, subscription);
subscription.addSubscriptionListener(listener);
PricingStatus pricingStatus = securitiesMap.get(security);
pricingStatus.deltas.add(new PricingDelta(pricingStatus.trade, subscription.getTrade()));
pricingStatus.deltas.add(new PricingDelta(pricingStatus.quote, subscription.getQuote()));
pricingStatus.deltas.add(new PricingDelta(pricingStatus.todayOHL, subscription.getTodayOHL()));
pricingStatus.deltas.add(new PricingDelta(pricingStatus.lastClose, subscription.getLastClose()));
pricingStatus.trade = subscription.getTrade();
pricingStatus.quote = subscription.getQuote();
pricingStatus.todayOHL = subscription.getTodayOHL();
pricingStatus.lastClose = subscription.getLastClose();
}
}
}
}
notifyListeners();
}
}
/* (non-Javadoc)
* @see org.eclipsetrader.core.feed.IPricingEnvironment#addPricingListener(org.eclipsetrader.core.feed.IPricingListener)
*/
@Override
public void addPricingListener(IPricingListener listener) {
listeners.add(listener);
}
/* (non-Javadoc)
* @see org.eclipsetrader.core.feed.IPricingEnvironment#removePricingListener(org.eclipsetrader.core.feed.IPricingListener)
*/
@Override
public void removePricingListener(IPricingListener listener) {
listeners.remove(listener);
}
/* (non-Javadoc)
* @see org.eclipsetrader.core.feed.IPricingEnvironment#dispose()
*/
@Override
public void dispose() {
marketService.removeMarketStatusListener(marketStatusListener);
for (IMarket market : marketService.getMarkets()) {
PropertyChangeSupport propertyChangeSupport = (PropertyChangeSupport) market.getAdapter(PropertyChangeSupport.class);
if (propertyChangeSupport != null) {
propertyChangeSupport.removePropertyChangeListener(propertyChangeListener);
}
}
listeners.clear();
for (Iterator<Entry<IFeedIdentifier, SubscriptionStatus>> iter = identifiersMap.entrySet().iterator(); iter.hasNext();) {
Entry<IFeedIdentifier, SubscriptionStatus> entry = iter.next();
for (IFeedSubscription subscription : entry.getValue().subscriptions.values()) {
subscription.removeSubscriptionListener(listener);
subscription.dispose();
}
PropertyChangeSupport propertyChangeSupport = (PropertyChangeSupport) entry.getKey().getAdapter(PropertyChangeSupport.class);
if (propertyChangeSupport != null) {
propertyChangeSupport.removePropertyChangeListener(propertyChangeListener);
}
}
identifiersMap.clear();
securitiesMap.clear();
}
/* (non-Javadoc)
* @see org.eclipsetrader.core.feed.IPricingEnvironment#getTrade(org.eclipsetrader.core.instruments.ISecurity)
*/
@Override
public ITrade getTrade(ISecurity security) {
return securitiesMap.get(security) != null ? securitiesMap.get(security).trade : null;
}
/* (non-Javadoc)
* @see org.eclipsetrader.core.feed.IPricingEnvironment#getQuote(org.eclipsetrader.core.instruments.ISecurity)
*/
@Override
public IQuote getQuote(ISecurity security) {
return securitiesMap.get(security) != null ? securitiesMap.get(security).quote : null;
}
/* (non-Javadoc)
* @see org.eclipsetrader.core.feed.IPricingEnvironment#getTodayOHL(org.eclipsetrader.core.instruments.ISecurity)
*/
@Override
public ITodayOHL getTodayOHL(ISecurity security) {
return securitiesMap.get(security) != null ? securitiesMap.get(security).todayOHL : null;
}
/* (non-Javadoc)
* @see org.eclipsetrader.core.feed.IPricingEnvironment#getLastClose(org.eclipsetrader.core.instruments.ISecurity)
*/
@Override
public ILastClose getLastClose(ISecurity security) {
return securitiesMap.get(security) != null ? securitiesMap.get(security).lastClose : null;
}
/* (non-Javadoc)
* @see org.eclipsetrader.core.feed.IPricingEnvironment#getBook(org.eclipsetrader.core.instruments.ISecurity)
*/
@Override
public IBook getBook(ISecurity security) {
return securitiesMap.get(security) != null ? securitiesMap.get(security).book : null;
}
protected void processUpdateQuotes(IFeedIdentifier identifier, QuoteDelta[] delta) {
SubscriptionStatus subscriptionStatus = identifiersMap.get(identifier);
if (subscriptionStatus != null) {
for (ISecurity security : subscriptionStatus.securities) {
PricingStatus pricingStatus = securitiesMap.get(security);
if (pricingStatus != null) {
for (int i = 0; i < delta.length; i++) {
if (delta[i].getNewValue() instanceof ITrade) {
Object oldValue = pricingStatus.trade;
pricingStatus.trade = (ITrade) delta[i].getNewValue();
pricingStatus.deltas.add(new PricingDelta(oldValue, delta[i].getNewValue()));
}
if (delta[i].getNewValue() instanceof IQuote) {
Object oldValue = pricingStatus.quote;
pricingStatus.quote = (IQuote) delta[i].getNewValue();
pricingStatus.deltas.add(new PricingDelta(oldValue, delta[i].getNewValue()));
}
if (delta[i].getNewValue() instanceof ITodayOHL) {
Object oldValue = pricingStatus.todayOHL;
pricingStatus.todayOHL = (ITodayOHL) delta[i].getNewValue();
pricingStatus.deltas.add(new PricingDelta(oldValue, delta[i].getNewValue()));
}
if (delta[i].getNewValue() instanceof ILastClose) {
Object oldValue = pricingStatus.lastClose;
pricingStatus.lastClose = (ILastClose) delta[i].getNewValue();
pricingStatus.deltas.add(new PricingDelta(oldValue, delta[i].getNewValue()));
}
if (delta[i].getNewValue() instanceof IBook) {
Object oldValue = pricingStatus.book;
pricingStatus.book = (IBook) delta[i].getNewValue();
pricingStatus.deltas.add(new PricingDelta(oldValue, delta[i].getNewValue()));
}
if (delta[i].getNewValue() instanceof IBarOpen) {
pricingStatus.todayBarOpen = (IBarOpen) delta[i].getNewValue();
pricingStatus.deltas.add(new PricingDelta(null, delta[i].getNewValue()));
}
if (delta[i].getNewValue() instanceof IBar) {
pricingStatus.todayBar = (IBar) delta[i].getNewValue();
pricingStatus.deltas.add(new PricingDelta(null, delta[i].getNewValue()));
}
if (delta[i].getNewValue() instanceof IPrice) {
pricingStatus.deltas.add(new PricingDelta(null, delta[i].getNewValue()));
}
}
}
}
notifyListeners();
}
}
protected void notifyListeners() {
Object[] l = listeners.getListeners();
ISecurity[] securities;
synchronized (securitiesMap) {
Set<ISecurity> set = securitiesMap.keySet();
securities = set.toArray(new ISecurity[set.size()]);
}
for (ISecurity security : securities) {
PricingStatus pricingStatus = securitiesMap.get(security);
if (pricingStatus == null || pricingStatus.deltas.size() == 0) {
continue;
}
PricingEvent event = new PricingEvent(security, pricingStatus.deltas.toArray(new PricingDelta[pricingStatus.deltas.size()]));
for (int i = 0; i < l.length; i++) {
try {
((IPricingListener) l[i]).pricingUpdate(event);
} catch (Exception e) {
Status status = new Status(IStatus.ERROR, CoreActivator.PLUGIN_ID, 0, "Error running pricing environment listener", e); //$NON-NLS-1$
CoreActivator.log(status);
} catch (LinkageError e) {
Status status = new Status(IStatus.ERROR, CoreActivator.PLUGIN_ID, 0, "Error running pricing environment listener", e); //$NON-NLS-1$
CoreActivator.log(status);
}
}
pricingStatus.deltas.clear();
}
}
PricingStatus getPricingStatus(ISecurity security) {
return securitiesMap.get(security);
}
SubscriptionStatus getSubscriptionStatus(ISecurity security) {
IFeedIdentifier identifier = (IFeedIdentifier) security.getAdapter(IFeedIdentifier.class);
return identifier != null ? identifiersMap.get(identifier) : null;
}
protected void fireTodayBarCloseEvent() {
Calendar c = Calendar.getInstance();
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
for (ISecurity security : securitiesMap.keySet()) {
PricingStatus pricingStatus = securitiesMap.get(security);
if (pricingStatus != null && pricingStatus.todayOHL != null && pricingStatus.trade != null) {
IBar newValue = new Bar(c.getTime(), TimeSpan.days(1),
pricingStatus.todayOHL.getOpen(), pricingStatus.todayOHL.getHigh(), pricingStatus.todayOHL.getLow(),
pricingStatus.trade.getPrice(), pricingStatus.trade.getVolume());
if (!newValue.equals(pricingStatus.todayBar)) {
pricingStatus.todayBar = newValue;
System.out.println(String.format("%s: %s", security.getName(), newValue));
pricingStatus.deltas.add(new PricingDelta(null, newValue));
}
}
}
notifyListeners();
}
}