From 0e4f7410c8dc56276240cf1a7ad4b641ed7c4926 Mon Sep 17 00:00:00 2001 From: Colin DuPlantis Date: Wed, 29 Mar 2023 15:48:31 -0700 Subject: [PATCH 1/4] MATP-1019 Photon market data view should allow specific exchange --- .../marketdata/AbstractMarketDataFeed.java | 5 +- .../marketdata/AbstractMarketDataModule.java | 147 +++++++++++---- .../marketdata/MarketDataClient.java | 9 + .../marketdata/service/MarketDataService.java | 9 + .../marketdata/AbstractMarketDataModule.java | 7 + .../rpc/client/MarketDataRpcClient.java | 37 +++- .../src/main/proto/rpc_marketdata.proto | 9 + .../rpc/server/MarketDataRpcService.java | 29 +++ .../rpc/MockMarketDataServiceAdapter.java | 12 +- .../MarketDataEventModuleConnector.java | 56 ++++++ .../service/MarketDataServiceImpl.java | 56 ++++++ modules/marketdata/bogus/pom.xml | 5 +- .../marketdata/bogus/BogusFeedFactory.java | 22 ++- .../bogus/BogusFeedModuleFactory.java | 35 ++++ modules/marketdata/csv/pom.xml | 4 + .../marketdata/csv/CSVFeedModuleFactory.java | 42 ++++- .../marketdata/exsim/ExsimFeedModule.java | 171 ++++++++++++++---- .../exsim/ExsimFeedModuleFactory.java | 32 ++++ .../manual/ManualFeedModuleFactory.java | 32 ++++ modules/marketdata/yahoo/pom.xml | 4 + .../yahoo/YahooFeedModuleFactory.java | 30 +++ packages/dare-package/pom.xml | 20 ++ .../service/MarketDataClientService.java | 12 ++ .../marketdata/view/MarketDataListView.java | 10 +- 24 files changed, 700 insertions(+), 95 deletions(-) diff --git a/core/src/main/java/org/marketcetera/marketdata/AbstractMarketDataFeed.java b/core/src/main/java/org/marketcetera/marketdata/AbstractMarketDataFeed.java index 1ccfc2f326..d3aa3599dd 100644 --- a/core/src/main/java/org/marketcetera/marketdata/AbstractMarketDataFeed.java +++ b/core/src/main/java/org/marketcetera/marketdata/AbstractMarketDataFeed.java @@ -16,8 +16,8 @@ import org.marketcetera.core.InMemoryIDFactory; import org.marketcetera.core.InternalID; import org.marketcetera.core.NoMoreIDsException; -import org.marketcetera.core.publisher.Subscriber; import org.marketcetera.core.publisher.PublisherEngine; +import org.marketcetera.core.publisher.Subscriber; import org.marketcetera.event.AggregateEvent; import org.marketcetera.event.Event; import org.marketcetera.event.EventTranslator; @@ -120,8 +120,7 @@ public abstract class AbstractMarketDataFeed PUBLISHING_CONDITION = ConditionsFactory.createSamplingCondition(100, "metc.metrics.marketdata.sampling.interval"); //$NON-NLS-1$ /** - * Indicates if the feed is allowed to simulate market data if the normal source is not - * available. + * Indicates if the feed is allowed to simulate market data if the normal source is not available. * * @return a boolean value */ diff --git a/core/src/main/java/org/marketcetera/marketdata/AbstractMarketDataModule.java b/core/src/main/java/org/marketcetera/marketdata/AbstractMarketDataModule.java index 1d8e554e89..169de4c0cf 100644 --- a/core/src/main/java/org/marketcetera/marketdata/AbstractMarketDataModule.java +++ b/core/src/main/java/org/marketcetera/marketdata/AbstractMarketDataModule.java @@ -37,6 +37,8 @@ import org.marketcetera.util.log.SLF4JLoggerProxy; import org.marketcetera.util.misc.ClassVersion; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.Maps; @@ -171,51 +173,57 @@ public final void requestData(DataRequest inRequest, } } else if (requestPayload instanceof MarketDataRequest) { request = (MarketDataRequest)requestPayload; + } else if(requestPayload instanceof FeedStatusRequest) { + doFeedStatusRequest((FeedStatusRequest)requestPayload, + inRequest, + inSupport); } else { throw new UnsupportedRequestParameterType(instanceURN, requestPayload); } - try { - Subscriber subscriber = new Subscriber() { - @Override - public boolean isInteresting(Object inData) - { - return inData instanceof Event; - } - @Override - public void publishTo(final Object inEvent) - { - if(inEvent instanceof Event) { - requestLock.executeRead(new Runnable() { - @Override - public void run() - { - Event event = (Event)inEvent; - Object token = event.getSource(); - RequestID requestID = requests.get(token); - event.setSource(requestID); - } - }); + if(request != null) { + try { + Subscriber subscriber = new Subscriber() { + @Override + public boolean isInteresting(Object inData) + { + return inData instanceof Event; } - ThreadedMetric.event("mdata-OUT"); //$NON-NLS-1$ - inSupport.send(inEvent); - } - }; - MarketDataFeedTokenSpec spec = MarketDataFeedTokenSpec.generateTokenSpec(request, - subscriber); - final T token = feed.execute(spec); - requestLock.executeWrite(new Runnable() { - @Override - public void run() - { - requests.put(token, - inSupport.getRequestID()); - } - }); - } catch (Exception e) { - throw new IllegalRequestParameterValue(instanceURN, - requestPayload, - e); + @Override + public void publishTo(final Object inEvent) + { + if(inEvent instanceof Event) { + requestLock.executeRead(new Runnable() { + @Override + public void run() + { + Event event = (Event)inEvent; + Object token = event.getSource(); + RequestID requestID = requests.get(token); + event.setSource(requestID); + } + }); + } + ThreadedMetric.event("mdata-OUT"); //$NON-NLS-1$ + inSupport.send(inEvent); + } + }; + MarketDataFeedTokenSpec spec = MarketDataFeedTokenSpec.generateTokenSpec(request, + subscriber); + final T token = feed.execute(spec); + requestLock.executeWrite(new Runnable() { + @Override + public void run() + { + requests.put(token, + inSupport.getRequestID()); + } + }); + } catch (Exception e) { + throw new IllegalRequestParameterValue(instanceURN, + requestPayload, + e); + } } } /* (non-Javadoc) @@ -369,6 +377,14 @@ private void setFeedStatus(FeedStatus inNewFeedStatus) String newStatusString = inNewFeedStatus.toString(); String oldStatusString = feedStatus.toString(); feedStatus = inNewFeedStatus; + for(FeedStatusRequestData feedStatusRequestData : feedStatusRequestDataByDataFlowId.asMap().values()) { + try { + feedStatusRequestData.getDataEmitterSupport().send(feedStatus); + } catch (Exception e) { + SLF4JLoggerProxy.warn(this, + e); + } + } mNotificationDelegate.sendNotification(new AttributeChangeNotification(this, mSequence.getAndIncrement(), System.currentTimeMillis(), @@ -378,6 +394,57 @@ private void setFeedStatus(FeedStatus inNewFeedStatus) oldStatusString, newStatusString)); } + /** + * Create a feed status request. + * + * @param inRequestPayload a FeedStatusRequest value + * @param inRequest a DataRequest value + * @param inSupport a DataEmitterSupport value + */ + private void doFeedStatusRequest(FeedStatusRequest inRequestPayload, + DataRequest inRequest, + DataEmitterSupport inSupport) + { + FeedStatusRequestData metaData = new FeedStatusRequestData(inSupport); + feedStatusRequestDataByDataFlowId.put(inSupport.getFlowID(), + metaData); + } + /** + * Holds data relevant to a feed status request as part of a module data flow. + * + * @author Colin DuPlantis + * @version $Id$ + * @since $Release$ + */ + private static class FeedStatusRequestData + { + /** + * Get the dataEmitterSupport value. + * + * @return a DataEmitterSupport value + */ + protected DataEmitterSupport getDataEmitterSupport() + { + return dataEmitterSupport; + } + /** + * Create a new FeedStatusRequestData instance. + * + * @param inDataEmitterSupport a DataEmitterSupport value + */ + private FeedStatusRequestData(DataEmitterSupport inDataEmitterSupport) + { + dataEmitterSupport = inDataEmitterSupport; + } + /** + * data emitter support value + */ + private final DataEmitterSupport dataEmitterSupport; + } + /** + * holds feed status requests by data flow id + */ + private final Cache feedStatusRequestDataByDataFlowId = CacheBuilder.newBuilder().build(); /** * tracks feeds by provider name as the feeds are instantiated (not started) - may not be active */ diff --git a/marketdata/marketdata-api/src/main/java/org/marketcetera/marketdata/MarketDataClient.java b/marketdata/marketdata-api/src/main/java/org/marketcetera/marketdata/MarketDataClient.java index 76089fc28a..b2efae30fd 100644 --- a/marketdata/marketdata-api/src/main/java/org/marketcetera/marketdata/MarketDataClient.java +++ b/marketdata/marketdata-api/src/main/java/org/marketcetera/marketdata/MarketDataClient.java @@ -87,4 +87,13 @@ CollectionPageResponse getSnapshot(Instrument inInstrument, * @return a Set<Capability> value */ Set getAvailableCapability(); + /** + * Gets the active providers. + * + *

Providers may or may not be connected at this time, these are the providers known + * to the system.

+ * + * @return a Set<String> value + */ + Set getProviders(); } diff --git a/marketdata/marketdata-api/src/main/java/org/marketcetera/marketdata/service/MarketDataService.java b/marketdata/marketdata-api/src/main/java/org/marketcetera/marketdata/service/MarketDataService.java index 18c36018bc..11f402d8f5 100644 --- a/marketdata/marketdata-api/src/main/java/org/marketcetera/marketdata/service/MarketDataService.java +++ b/marketdata/marketdata-api/src/main/java/org/marketcetera/marketdata/service/MarketDataService.java @@ -70,4 +70,13 @@ CollectionPageResponse getSnapshot(Instrument inInstrument, * @return a Set<Capability> value */ Set getAvailableCapability(); + /** + * Gets the active providers. + * + *

Providers may or may not be connected at this time, these are the providers known + * to the system.

+ * + * @return a Set<String> value + */ + Set getProviders(); } diff --git a/marketdata/marketdata-core/src/main/java/org/marketcetera/marketdata/AbstractMarketDataModule.java b/marketdata/marketdata-core/src/main/java/org/marketcetera/marketdata/AbstractMarketDataModule.java index ec969b36df..dde7910caa 100644 --- a/marketdata/marketdata-core/src/main/java/org/marketcetera/marketdata/AbstractMarketDataModule.java +++ b/marketdata/marketdata-core/src/main/java/org/marketcetera/marketdata/AbstractMarketDataModule.java @@ -23,6 +23,7 @@ import org.marketcetera.core.publisher.Subscriber; import org.marketcetera.event.Event; import org.marketcetera.marketdata.IFeedComponent.FeedType; +import org.marketcetera.marketdata.service.MarketDataService; import org.marketcetera.metrics.ThreadedMetric; import org.marketcetera.module.AutowiredModule; import org.marketcetera.module.DataEmitter; @@ -387,7 +388,13 @@ private void setFeedStatus(FeedStatus inNewFeedStatus) for(MarketDataStatusBroadcaster marketDataStatusPublisher : marketDataStatusBroadcasters) { marketDataStatusPublisher.reportMarketDataStatus(status); } + marketDataService.reportMarketDataStatus(status); } + /** + * provides access to market data services + */ + @Autowired + private MarketDataService marketDataService; /** * optional market data status publishers */ diff --git a/marketdata/marketdata-rpc-client/src/main/java/org/marketcetera/marketdata/rpc/client/MarketDataRpcClient.java b/marketdata/marketdata-rpc-client/src/main/java/org/marketcetera/marketdata/rpc/client/MarketDataRpcClient.java index 11b5487cf7..f79b8f3dec 100644 --- a/marketdata/marketdata-rpc-client/src/main/java/org/marketcetera/marketdata/rpc/client/MarketDataRpcClient.java +++ b/marketdata/marketdata-rpc-client/src/main/java/org/marketcetera/marketdata/rpc/client/MarketDataRpcClient.java @@ -267,7 +267,7 @@ public Void call() @Override public Set getAvailableCapability() { - return executeCall(new Callable>(){ + return executeCall(new Callable>() { @Override public Set call() throws Exception @@ -299,6 +299,41 @@ public Set call() } }); } + /* (non-Javadoc) + * @see org.marketcetera.marketdata.MarketDataClient#getProviders() + */ + @Override + public Set getProviders() + { + return executeCall(new Callable>() { + @Override + public Set call() + throws Exception + { + SLF4JLoggerProxy.trace(MarketDataRpcClient.this, + "{} getting available providers", + getSessionId()); + MarketDataRpc.GetMarketDataProvidersRequest.Builder requestBuilder = MarketDataRpc.GetMarketDataProvidersRequest.newBuilder(); + requestBuilder.setSessionId(getSessionId().getValue()); + MarketDataRpc.GetMarketDataProvidersRequest request = requestBuilder.build(); + SLF4JLoggerProxy.trace(MarketDataRpcClient.this, + "{} sending {}", + getSessionId(), + request); + MarketDataRpc.GetMarketDataProvidersResponse response = getBlockingStub().getMarketDataProviders(request); + SLF4JLoggerProxy.trace(MarketDataRpcClient.this, + "{} received {}", + getSessionId(), + response); + Set providers = Sets.newHashSet(response.getProviderList()); + SLF4JLoggerProxy.trace(MarketDataRpcClient.this, + "{} returning {}", + getSessionId(), + providers); + return providers; + } + }); + } /** * Create a new MarketDataRpcClient instance. * diff --git a/marketdata/marketdata-rpc-core/src/main/proto/rpc_marketdata.proto b/marketdata/marketdata-rpc-core/src/main/proto/rpc_marketdata.proto index 02718e942f..6a6aecb609 100644 --- a/marketdata/marketdata-rpc-core/src/main/proto/rpc_marketdata.proto +++ b/marketdata/marketdata-rpc-core/src/main/proto/rpc_marketdata.proto @@ -67,6 +67,14 @@ message RemoveMarketDataStatusListenerRequest { message RemoveMarketDataStatusListenerResponse { } +message GetMarketDataProvidersRequest { + string sessionId = 1; +} + +message GetMarketDataProvidersResponse { + repeated string provider = 1; +} + service MarketDataRpcService { rpc login(LoginRequest) returns (LoginResponse); rpc logout(LogoutRequest) returns (LogoutResponse); @@ -77,4 +85,5 @@ service MarketDataRpcService { rpc getAvailableCapability(AvailableCapabilityRequest) returns (AvailableCapabilityResponse); rpc addMarketDataStatusListener(AddMarketDataStatusListenerRequest) returns (stream MarketDataStatusListenerResponse); rpc removeMarketDataStatusListener(RemoveMarketDataStatusListenerRequest) returns (RemoveMarketDataStatusListenerResponse); + rpc getMarketDataProviders(GetMarketDataProvidersRequest) returns (GetMarketDataProvidersResponse); } diff --git a/marketdata/marketdata-rpc-server/src/main/java/org/marketcetera/marketdata/rpc/server/MarketDataRpcService.java b/marketdata/marketdata-rpc-server/src/main/java/org/marketcetera/marketdata/rpc/server/MarketDataRpcService.java index 6f229fe071..414f1341ba 100644 --- a/marketdata/marketdata-rpc-server/src/main/java/org/marketcetera/marketdata/rpc/server/MarketDataRpcService.java +++ b/marketdata/marketdata-rpc-server/src/main/java/org/marketcetera/marketdata/rpc/server/MarketDataRpcService.java @@ -15,6 +15,8 @@ import org.marketcetera.marketdata.MarketDataStatus; import org.marketcetera.marketdata.MarketDataStatusListener; import org.marketcetera.marketdata.core.rpc.MarketDataRpc; +import org.marketcetera.marketdata.core.rpc.MarketDataRpc.GetMarketDataProvidersRequest; +import org.marketcetera.marketdata.core.rpc.MarketDataRpc.GetMarketDataProvidersResponse; import org.marketcetera.marketdata.core.rpc.MarketDataRpcServiceGrpc; import org.marketcetera.marketdata.core.rpc.MarketDataRpcServiceGrpc.MarketDataRpcServiceImplBase; import org.marketcetera.marketdata.core.rpc.MarketDataTypesRpc; @@ -283,6 +285,33 @@ public void getAvailableCapability(MarketDataRpc.AvailableCapabilityRequest inRe inResponseObserver.onCompleted(); } } + /* (non-Javadoc) + * @see org.marketcetera.marketdata.core.rpc.MarketDataRpcServiceGrpc.MarketDataRpcServiceImplBase#getMarketDataProviders(org.marketcetera.marketdata.core.rpc.MarketDataRpc.GetMarketDataProvidersRequest, io.grpc.stub.StreamObserver) + */ + @Override + public void getMarketDataProviders(GetMarketDataProvidersRequest inRequest, + StreamObserver inResponseObserver) + { + try { + validateAndReturnSession(inRequest.getSessionId()); + SLF4JLoggerProxy.trace(MarketDataRpcService.this, + "Received market data providers request {}", + inRequest); + MarketDataRpc.GetMarketDataProvidersResponse.Builder responseBuilder = MarketDataRpc.GetMarketDataProvidersResponse.newBuilder(); + Set providers = marketDataService.getProviders(); + providers.forEach(provider -> responseBuilder.addProvider(provider)); + MarketDataRpc.GetMarketDataProvidersResponse response = responseBuilder.build(); + SLF4JLoggerProxy.trace(MarketDataRpcService.this, + "Sending response: {}", + response); + inResponseObserver.onNext(response); + inResponseObserver.onCompleted(); + } catch (Exception e) { + handleError(e, + inResponseObserver); + inResponseObserver.onCompleted(); + } + } /* (non-Javadoc) * @see org.marketcetera.marketdata.core.rpc.MarketDataRpcServiceGrpc.MarketDataRpcServiceImplBase#addMarketDataStatusListener(org.marketcetera.marketdata.core.rpc.MarketDataRpc.AddMarketDataStatusListenerRequest, io.grpc.stub.StreamObserver) */ diff --git a/marketdata/marketdata-rpc-server/src/test/java/org/marketcetera/marketdata/rpc/MockMarketDataServiceAdapter.java b/marketdata/marketdata-rpc-server/src/test/java/org/marketcetera/marketdata/rpc/MockMarketDataServiceAdapter.java index f10e8377ea..25e83cd4ee 100644 --- a/marketdata/marketdata-rpc-server/src/test/java/org/marketcetera/marketdata/rpc/MockMarketDataServiceAdapter.java +++ b/marketdata/marketdata-rpc-server/src/test/java/org/marketcetera/marketdata/rpc/MockMarketDataServiceAdapter.java @@ -118,6 +118,14 @@ public void reportCapability(Collection inCapabilities) { throw new UnsupportedOperationException(); // TODO } + /* (non-Javadoc) + * @see org.marketcetera.marketdata.service.MarketDataService#getProviders() + */ + @Override + public Set getProviders() + { + throw new UnsupportedOperationException(); // TODO + } /** * Get the requests value. * @@ -218,9 +226,7 @@ public Set getCapabilitiesToReturn() return capabilitiesToReturn; } /** - * - * - * + * Reset the object. */ public void reset() { diff --git a/marketdata/marketdata-server/src/main/java/org/marketcetera/marketdata/service/MarketDataEventModuleConnector.java b/marketdata/marketdata-server/src/main/java/org/marketcetera/marketdata/service/MarketDataEventModuleConnector.java index 434e3f4f1c..25e0139ee7 100644 --- a/marketdata/marketdata-server/src/main/java/org/marketcetera/marketdata/service/MarketDataEventModuleConnector.java +++ b/marketdata/marketdata-server/src/main/java/org/marketcetera/marketdata/service/MarketDataEventModuleConnector.java @@ -5,7 +5,10 @@ import org.apache.commons.lang3.exception.ExceptionUtils; import org.marketcetera.event.Event; import org.marketcetera.eventbus.EventBusService; +import org.marketcetera.marketdata.FeedStatus; +import org.marketcetera.marketdata.FeedStatusRequest; import org.marketcetera.marketdata.MarketDataRequest; +import org.marketcetera.marketdata.MarketDataStatus; import org.marketcetera.marketdata.NoMarketDataProvidersAvailable; import org.marketcetera.marketdata.cache.MarketDataCacheModuleFactory; import org.marketcetera.marketdata.core.manager.MarketDataManagerModuleFactory; @@ -122,6 +125,10 @@ public void receiveData(DataFlowID inFlowId, Object inData) throws ReceiveDataException { + SLF4JLoggerProxy.warn(this, + "Received {} for data flow id: {}", + inData, + inFlowId); if(inData instanceof Event) { String marketDataRequestId = requestsByDataFlowId.getIfPresent(inFlowId); if(marketDataRequestId == null) { @@ -134,6 +141,24 @@ public void receiveData(DataFlowID inFlowId, Event event = (Event)inData; eventBusService.post(new SimpleGeneratedMarketDataEvent(marketDataRequestId, event)); + } else if(inData instanceof MarketDataStatus) { + MarketDataStatus marketDataStatus = (MarketDataStatus)inData; + marketDataService.reportMarketDataStatus(marketDataStatus); + } else if(inData instanceof FeedStatus) { + final FeedStatus feedStatus = (FeedStatus)inData; + final String provider = requestsByDataFlowId.getIfPresent(inFlowId); + marketDataService.reportMarketDataStatus(new MarketDataStatus() { + @Override + public FeedStatus getFeedStatus() + { + return feedStatus; + } + @Override + public String getProvider() + { + return provider; + }} + ); } else { SLF4JLoggerProxy.warn(this, "Received unexpected data: {}", @@ -204,6 +229,8 @@ private void doDataRequest(MarketDataRequest inMarketDataRequest, ModuleURN inSourceUrn, String inRequestId) { + doStatusRequest(inSourceUrn, + inRequestId); DataRequest sourceRequest = new DataRequest(inSourceUrn, inMarketDataRequest); ModuleManager.startModulesIfNecessary(ModuleManager.getInstance(), @@ -222,6 +249,30 @@ private void doDataRequest(MarketDataRequest inMarketDataRequest, inSourceUrn, dataFlowId); } + /** + * Execute a market data status request. + * + * @param inSourceUrn a ModuleURN value + * @param inRequestId a String value + */ + private void doStatusRequest(ModuleURN inSourceUrn, + String inRequestId) + { + DataRequest sourceRequest = new DataRequest(inSourceUrn, + new FeedStatusRequest()); + ModuleManager.startModulesIfNecessary(ModuleManager.getInstance(), + inSourceUrn); + DataRequest targetRequest = new DataRequest(MarketDataEventModuleConnectorFactory.INSTANCE_URN); + DataFlowID dataFlowId = ModuleManager.getInstance().createDataFlow(new DataRequest[] { sourceRequest,targetRequest }); + requestsByDataFlowId.put(dataFlowId, + inSourceUrn.instanceName()); + Set dataFlows = dataFlowsByRequestId.getUnchecked(inRequestId); + dataFlows.add(dataFlowId); + SLF4JLoggerProxy.debug(this, + "Submitting feed status request to {}: {}", + inSourceUrn, + dataFlowId); + } /** * stores data flows tied to a given request id */ @@ -233,6 +284,11 @@ public Set load(String inKey) return Sets.newHashSet(); }} ); + /** + * provides market data services + */ + @Autowired + private MarketDataService marketDataService; /** * provides access to event bus services */ diff --git a/marketdata/marketdata-server/src/main/java/org/marketcetera/marketdata/service/MarketDataServiceImpl.java b/marketdata/marketdata-server/src/main/java/org/marketcetera/marketdata/service/MarketDataServiceImpl.java index 7530ed37ba..75d09e9d63 100644 --- a/marketdata/marketdata-server/src/main/java/org/marketcetera/marketdata/service/MarketDataServiceImpl.java +++ b/marketdata/marketdata-server/src/main/java/org/marketcetera/marketdata/service/MarketDataServiceImpl.java @@ -4,7 +4,9 @@ import java.util.Collections; import java.util.Deque; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.TreeSet; import javax.annotation.PostConstruct; @@ -29,6 +31,9 @@ import org.marketcetera.persist.PageResponse; import org.marketcetera.trade.Instrument; import org.marketcetera.util.log.SLF4JLoggerProxy; +import org.nocrala.tools.texttablefmt.BorderStyle; +import org.nocrala.tools.texttablefmt.ShownBorders; +import org.nocrala.tools.texttablefmt.Table; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -84,6 +89,7 @@ public void reportMarketDataStatus(MarketDataStatus inMarketDataStatus) { cachedMarketDataStatus.put(inMarketDataStatus.getProvider(), inMarketDataStatus); + logProviderData(); for(MarketDataStatusListener listener : marketDataStatusListeners) { try { listener.receiveMarketDataStatus(inMarketDataStatus); @@ -259,6 +265,21 @@ public void clear() cacheProviders.forEach(value->value.clear()); } } + /* (non-Javadoc) + * @see org.marketcetera.marketdata.service.MarketDataService#getProviders() + */ + @Override + public Set getProviders() + { + SLF4JLoggerProxy.trace(this, + "Received getProviders request"); + // develop a list of *potential* providers, may or may not be active + Set providers = new TreeSet<>(cachedMarketDataStatus.asMap().keySet()); + SLF4JLoggerProxy.trace(this, + "Returning {}", + providers); + return providers; + } /** * Receives generated market data events. * @@ -299,6 +320,37 @@ public void start() { eventBusService.register(this); } + /** + * Log market data provider status. + */ + private void logProviderData() + { + Table table = new Table(2, + BorderStyle.CLASSIC_COMPATIBLE_WIDE, + ShownBorders.ALL, + false); + table.addCell("Market Data Providers", + PlatformServices.cellStyle, + 2); + table.addCell("Provider", + PlatformServices.cellStyle); + table.addCell("Status", + PlatformServices.cellStyle); + for(Map.Entry providerEntry : cachedMarketDataStatus.asMap().entrySet()) { + table.addCell(providerEntry.getKey(), + PlatformServices.cellStyle); + table.addCell(providerEntry.getValue().getFeedStatus().name(), + PlatformServices.cellStyle); + } + String thisProviderLog = table.render(); + if(!thisProviderLog.equals(lastProviderLog)) { + SLF4JLoggerProxy.info(this, + "{}{}", + System.lineSeparator(), + thisProviderLog); + } + lastProviderLog = thisProviderLog; + } /** * Holds data about the market data request. * @@ -363,6 +415,10 @@ private RequestMetaData(MarketDataListener inListener) */ private final MarketDataListener marketDataListener; } + /** + * stores the last reported market data status to avoid reporting the same thing twice + */ + private String lastProviderLog; /** * provides access to event bus services */ diff --git a/modules/marketdata/bogus/pom.xml b/modules/marketdata/bogus/pom.xml index d2213b59b6..e8892989b3 100644 --- a/modules/marketdata/bogus/pom.xml +++ b/modules/marketdata/bogus/pom.xml @@ -32,7 +32,10 @@ ${project.groupId} core - + + ${project.groupId} + marketdata-api + diff --git a/modules/marketdata/bogus/src/main/java/org/marketcetera/marketdata/bogus/BogusFeedFactory.java b/modules/marketdata/bogus/src/main/java/org/marketcetera/marketdata/bogus/BogusFeedFactory.java index 97daf739eb..08941ba354 100644 --- a/modules/marketdata/bogus/src/main/java/org/marketcetera/marketdata/bogus/BogusFeedFactory.java +++ b/modules/marketdata/bogus/src/main/java/org/marketcetera/marketdata/bogus/BogusFeedFactory.java @@ -19,15 +19,23 @@ public class BogusFeedFactory extends AbstractMarketDataFeedFactory { - private final static BogusFeedFactory sInstance = new BogusFeedFactory(); + /** + * Get the BogusFeedFactory value. + * + * @return a BogusFeedFactory value + */ public static BogusFeedFactory getInstance() { return sInstance; } - public String getProviderName() - { - return "Marketcetera (Bogus)"; //$NON-NLS-1$ - } + /* (non-Javadoc) + * @see org.marketcetera.marketdata.IMarketDataFeedFactory#getProviderName() + */ + @Override + public String getProviderName() + { + return BogusFeedModuleFactory.IDENTIFIER; + } /* (non-Javadoc) * @see org.marketcetera.marketdata.IMarketDataFeedFactory#getMarketDataFeed() */ @@ -41,4 +49,8 @@ public BogusFeed getMarketDataFeed() throw new FeedException(e); } } + /** + * creates new {@link BogusFeed} objects + */ + private final static BogusFeedFactory sInstance = new BogusFeedFactory(); } diff --git a/modules/marketdata/bogus/src/main/java/org/marketcetera/marketdata/bogus/BogusFeedModuleFactory.java b/modules/marketdata/bogus/src/main/java/org/marketcetera/marketdata/bogus/BogusFeedModuleFactory.java index e100046985..b446e2d42a 100644 --- a/modules/marketdata/bogus/src/main/java/org/marketcetera/marketdata/bogus/BogusFeedModuleFactory.java +++ b/modules/marketdata/bogus/src/main/java/org/marketcetera/marketdata/bogus/BogusFeedModuleFactory.java @@ -2,11 +2,18 @@ import static org.marketcetera.marketdata.bogus.Messages.PROVIDER_DESCRIPTION; +import javax.annotation.PostConstruct; + import org.marketcetera.core.CoreException; +import org.marketcetera.marketdata.FeedStatus; +import org.marketcetera.marketdata.MarketDataStatus; +import org.marketcetera.marketdata.service.MarketDataService; import org.marketcetera.module.ModuleCreationException; import org.marketcetera.module.ModuleFactory; import org.marketcetera.module.ModuleURN; import org.marketcetera.util.misc.ClassVersion; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; /* $License$ */ @@ -28,6 +35,7 @@ * @version $Id$ * @since 1.0.0 */ +@Service @ClassVersion("$Id$") //$NON-NLS-1$ public class BogusFeedModuleFactory extends ModuleFactory @@ -55,6 +63,33 @@ public BogusFeedModule create(Object... inArg0) throw new ModuleCreationException(e.getI18NBoundMessage()); } } + /** + * Validate and start the object. + */ + @PostConstruct + public void start() + { + marketDataService.reportMarketDataStatus(new MarketDataStatus() { + @Override + public FeedStatus getFeedStatus() + { + return FeedStatus.OFFLINE; + } + @Override + public String getProvider() + { + return IDENTIFIER; + }} + ); + } + /** + * providers market data services + */ + @Autowired + private MarketDataService marketDataService; + /** + * uniquely identifies this provider + */ public static final String IDENTIFIER = "bogus"; //$NON-NLS-1$ /** * unique provider URN for the bogus feed market data provider diff --git a/modules/marketdata/csv/pom.xml b/modules/marketdata/csv/pom.xml index 280962d184..4e3e63ad72 100644 --- a/modules/marketdata/csv/pom.xml +++ b/modules/marketdata/csv/pom.xml @@ -17,6 +17,10 @@ ${project.groupId} core + + ${project.groupId} + marketdata-api + org.marketcetera.fork commons-csv diff --git a/modules/marketdata/csv/src/main/java/org/marketcetera/marketdata/csv/CSVFeedModuleFactory.java b/modules/marketdata/csv/src/main/java/org/marketcetera/marketdata/csv/CSVFeedModuleFactory.java index 33f84be500..930415e8ed 100644 --- a/modules/marketdata/csv/src/main/java/org/marketcetera/marketdata/csv/CSVFeedModuleFactory.java +++ b/modules/marketdata/csv/src/main/java/org/marketcetera/marketdata/csv/CSVFeedModuleFactory.java @@ -1,11 +1,19 @@ package org.marketcetera.marketdata.csv; import static org.marketcetera.marketdata.csv.Messages.PROVIDER_DESCRIPTION; -import org.marketcetera.util.misc.ClassVersion; -import org.marketcetera.module.ModuleFactory; + +import javax.annotation.PostConstruct; + +import org.marketcetera.core.CoreException; +import org.marketcetera.marketdata.FeedStatus; +import org.marketcetera.marketdata.MarketDataStatus; +import org.marketcetera.marketdata.service.MarketDataService; import org.marketcetera.module.ModuleCreationException; +import org.marketcetera.module.ModuleFactory; import org.marketcetera.module.ModuleURN; -import org.marketcetera.core.CoreException; +import org.marketcetera.util.misc.ClassVersion; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; /** * ModuleFactory implementation for the CSVFeed market data provider. @@ -15,6 +23,7 @@ * @since 2.1.0 * @version $Id$ */ +@Service @ClassVersion("$Id$") public class CSVFeedModuleFactory extends ModuleFactory @@ -42,6 +51,33 @@ public CSVFeedModule create(Object... inArg0) throw new ModuleCreationException(e.getI18NBoundMessage()); } } + /** + * Validate and start the object. + */ + @PostConstruct + public void start() + { + marketDataService.reportMarketDataStatus(new MarketDataStatus() { + @Override + public FeedStatus getFeedStatus() + { + return FeedStatus.OFFLINE; + } + @Override + public String getProvider() + { + return IDENTIFIER; + }} + ); + } + /** + * providers market data services + */ + @Autowired + private MarketDataService marketDataService; + /** + * uniquely identifies this provider + */ public static final String IDENTIFIER = "csv"; //$NON-NLS-1$ /** * unique provider URN for the CSV feed market data provider diff --git a/modules/marketdata/marketdata-exsim/src/main/java/org/marketcetera/marketdata/exsim/ExsimFeedModule.java b/modules/marketdata/marketdata-exsim/src/main/java/org/marketcetera/marketdata/exsim/ExsimFeedModule.java index a186e3be6a..8a93aa457c 100644 --- a/modules/marketdata/marketdata-exsim/src/main/java/org/marketcetera/marketdata/exsim/ExsimFeedModule.java +++ b/modules/marketdata/marketdata-exsim/src/main/java/org/marketcetera/marketdata/exsim/ExsimFeedModule.java @@ -26,14 +26,17 @@ import org.marketcetera.event.impl.MarketstatEventBuilder; import org.marketcetera.event.impl.QuoteEventBuilder; import org.marketcetera.event.impl.TradeEventBuilder; -import org.marketcetera.marketdata.MarketDataModuleMXBean; import org.marketcetera.marketdata.AssetClass; import org.marketcetera.marketdata.Capability; import org.marketcetera.marketdata.CapabilityCollection; import org.marketcetera.marketdata.FeedStatus; +import org.marketcetera.marketdata.FeedStatusRequest; +import org.marketcetera.marketdata.MarketDataModuleMXBean; import org.marketcetera.marketdata.MarketDataRequest; import org.marketcetera.marketdata.MarketDataRequestBuilder; +import org.marketcetera.marketdata.MarketDataStatus; import org.marketcetera.marketdata.OrderBook; +import org.marketcetera.marketdata.service.MarketDataService; import org.marketcetera.module.AutowiredModule; import org.marketcetera.module.DataEmitter; import org.marketcetera.module.DataEmitterSupport; @@ -62,6 +65,7 @@ import org.marketcetera.util.time.DateService; import org.springframework.beans.factory.annotation.Autowired; +import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; @@ -170,6 +174,10 @@ public void requestData(DataRequest inRequest, doMarketDataRequest((MarketDataRequest)payload, inRequest, inSupport); + } else if(payload instanceof FeedStatusRequest) { + doFeedStatusRequest((FeedStatusRequest)payload, + inRequest, + inSupport); } else { throw new RequestDataException(new I18NBoundMessage1P(Messages.UNSUPPORTED_DATA_REQUEST_PAYLOAD, payload.getClass().getSimpleName())); @@ -198,7 +206,7 @@ public void requestData(DataRequest inRequest, public void cancel(DataFlowID inFlowId, RequestID inRequestID) { - RequestData requestData = requestsByDataFlowId.remove(inFlowId); + MarketDataRequestData requestData = requestsByDataFlowId.remove(inFlowId); if(requestData == null) { Messages.DATA_FLOW_ALREADY_CANCELED.warn(this, inFlowId); @@ -221,6 +229,7 @@ public void cancel(DataFlowID inFlowId, } } } + feedStatusRequestDataByDataFlowId.invalidate(inFlowId); } /* (non-Javadoc) * @see org.marketcetera.module.Module#preStart() @@ -274,11 +283,11 @@ protected void preStart() protected void preStop() throws ModuleException { - List requestsToCancel = Lists.newArrayList(requestsByDataFlowId.values()); - for(RequestData request : requestsToCancel) { + List requestsToCancel = Lists.newArrayList(requestsByDataFlowId.values()); + for(MarketDataRequestData request : requestsToCancel) { try { - cancel(request.dataEmitterSupport.getFlowID(), - request.dataEmitterSupport.getRequestID()); + cancel(request.getDataEmitterSupport().getFlowID(), + request.getDataEmitterSupport().getRequestID()); } catch (Exception e) { SLF4JLoggerProxy.warn(this, e); @@ -320,6 +329,21 @@ public OrderBook load(OrderBookKey inKey) } }); } + /** + * Execute a feed status request with the given attributes. + * + * @param inPayload a FeedStatusRequest value + * @param inRequest a DataRequest value + * @param inSupport a DataEmitterSupport value + */ + private void doFeedStatusRequest(FeedStatusRequest inPayload, + DataRequest inRequest, + DataEmitterSupport inSupport) + { + FeedStatusRequestData metaData = new FeedStatusRequestData(inSupport); + feedStatusRequestDataByDataFlowId.put(inSupport.getFlowID(), + metaData); + } /** * Perform the market data request * @@ -356,11 +380,11 @@ private void doMarketDataRequest(MarketDataRequest inPayload, marketDataRequest, inRequest, inPayload); - RequestData requestData = new RequestData(marketDataRequest, - inSupport, - id, - inPayload, - requestedInstruments); + MarketDataRequestData requestData = new MarketDataRequestData(marketDataRequest, + inSupport, + id, + inPayload, + requestedInstruments); requestsByRequestId.put(id, requestData); requestsByDataFlowId.put(inSupport.getFlowID(), @@ -380,7 +404,7 @@ private void doMarketDataRequest(MarketDataRequest inPayload, * @throws quickfix.FieldNotFound if the market data request cancel cannot be constructed * @throws quickfix.SessionNotFound if the cancel message cannot be sent */ - private void cancelMarketDataRequest(RequestData inMarketDataRequestData) + private void cancelMarketDataRequest(MarketDataRequestData inMarketDataRequestData) throws quickfix.FieldNotFound,quickfix.SessionNotFound { quickfix.Message marketDataCancel = messageFactory.newMarketDataRequest(inMarketDataRequestData.requestId, @@ -410,6 +434,27 @@ private void updateFeedStatus(FeedStatus inNewStatus) feedStatus, inNewStatus); feedStatus = inNewStatus; + MarketDataStatus marketDataStatus = new MarketDataStatus() { + @Override + public FeedStatus getFeedStatus() + { + return feedStatus; + } + @Override + public String getProvider() + { + return ExsimFeedModuleFactory.IDENTIFIER; + } + }; + marketDataService.reportMarketDataStatus(marketDataStatus); + for(FeedStatusRequestData feedStatusRequestData : feedStatusRequestDataByDataFlowId.asMap().values()) { + try { + feedStatusRequestData.getDataEmitterSupport().send(marketDataStatus); + } catch (Exception e) { + SLF4JLoggerProxy.warn(this, + e); + } + } Messages.FEED_STATUS_UPDATE.info(this, ExsimFeedModuleFactory.IDENTIFIER.toUpperCase(), feedStatus); @@ -678,6 +723,7 @@ private List getEvents(MessageWrapper inMessageWrapper, if(marketstatBuilder == null) { marketstatBuilder = MarketstatEventBuilder.marketstat(instrument); } + marketstatBuilder.withProvider(ExsimFeedModuleFactory.IDENTIFIER); marketstatBuilder.withExchangeCode(exchange); if(openPrice != null) { marketstatBuilder.withOpenPrice(openPrice); @@ -748,7 +794,7 @@ private long getBidIdFor(OrderBook inOrderbook, private void publishEvents(Deque inEvents, String inRequestId) { - RequestData requestData = requestsByRequestId.get(inRequestId); + MarketDataRequestData requestData = requestsByRequestId.get(inRequestId); if(requestData == null) { SLF4JLoggerProxy.debug(this, "Not publishing {} to {} because it seems to have just been canceled", //$NON-NLS-1$ @@ -836,7 +882,7 @@ protected void processData(Deque inData) break; case quickfix.field.MsgType.MARKET_DATA_REQUEST_REJECT: // cancel corresponding request, unless resubmitting due to feed status change - RequestData requestData = requestsByRequestId.get(messageWrapper.getRequestId()); + MarketDataRequestData requestData = requestsByRequestId.get(messageWrapper.getRequestId()); if(requestData == null) { } else { if(!requestData.resubmitting) { @@ -1081,6 +1127,58 @@ public void fromApp(quickfix.Message inMessage, } } } + /** + * Provides common behavior for data flow requests. + * + * @author Colin DuPlantis + * @version $Id$ + * @since $Release$ + */ + private static abstract class AbstractRequestData + { + /** + * Get the dataEmitterSupport value. + * + * @return a DataEmitterSupport value + */ + protected DataEmitterSupport getDataEmitterSupport() + { + return dataEmitterSupport; + } + /** + * Create a new AbstractRequestData instance. + * + * @param inDataEmitterSupport a DataEmitterSupport value + */ + protected AbstractRequestData(DataEmitterSupport inDataEmitterSupport) + { + dataEmitterSupport = inDataEmitterSupport; + } + /** + * data emitter support value + */ + private final DataEmitterSupport dataEmitterSupport; + } + /** + * Holds data relevant to a feed status request as part of a module data flow. + * + * @author Colin DuPlantis + * @version $Id$ + * @since $Release$ + */ + private static class FeedStatusRequestData + extends AbstractRequestData + { + /** + * Create a new FeedStatusRequestData instance. + * + * @param inDataEmitterSupport a DataEmitterSupport value + */ + private FeedStatusRequestData(DataEmitterSupport inDataEmitterSupport) + { + super(inDataEmitterSupport); + } + } /** * Holds data relevant to a market data request as part of a module data flow. * @@ -1088,7 +1186,8 @@ public void fromApp(quickfix.Message inMessage, * @version $Id$ * @since $Release$ */ - private static class RequestData + private static class MarketDataRequestData + extends AbstractRequestData { /* (non-Javadoc) * @see java.lang.Object#toString() @@ -1109,16 +1208,7 @@ private quickfix.Message getRequestMessage() return requestMessage; } /** - * Get the dataEmitterSupport value. - * - * @return a DataEmitterSupport value - */ - private DataEmitterSupport getDataEmitterSupport() - { - return dataEmitterSupport; - } - /** - * Create a new RequestData instance. + * Create a new MarketDataRequestData instance. * * @param inRequestMessage a quickfix.Message value * @param inDataEmitterSupport a DataEmitterSupport value @@ -1126,15 +1216,15 @@ private DataEmitterSupport getDataEmitterSupport() * @param inMarketDataRequest a MarketDataRequest value * @param inRequestedInstruments a List<Instrument> value */ - private RequestData(quickfix.Message inRequestMessage, - DataEmitterSupport inDataEmitterSupport, - String inRequestId, - MarketDataRequest inMarketDataRequest, - List inRequestedInstruments) + private MarketDataRequestData(quickfix.Message inRequestMessage, + DataEmitterSupport inDataEmitterSupport, + String inRequestId, + MarketDataRequest inMarketDataRequest, + List inRequestedInstruments) { + super(inDataEmitterSupport); requestMessage = inRequestMessage; - dataEmitterSupport = inDataEmitterSupport; - description = RequestData.class.getSimpleName() + " [" + inDataEmitterSupport.getFlowID() + "]"; //$NON-NLS-1$ //$NON-NLS-2$ + description = MarketDataRequestData.class.getSimpleName() + " [" + inDataEmitterSupport.getFlowID() + "]"; //$NON-NLS-1$ //$NON-NLS-2$ requestId = inRequestId; requestedInstruments = inRequestedInstruments; marketDataRequest = inMarketDataRequest; @@ -1151,10 +1241,6 @@ private RequestData(quickfix.Message inRequestMessage, * original request message sent to the exchange */ private final quickfix.Message requestMessage; - /** - * information about the data flow requester - */ - private final DataEmitterSupport dataEmitterSupport; /** * request id of the request */ @@ -1261,6 +1347,11 @@ private OrderBookKey(String inRequestId, */ @Autowired private SymbolResolverService symbolResolverService; + /** + * provides access to market data services + */ + @Autowired + private MarketDataService marketDataService; /** * number of milliseconds to wait for the feed to become available if a request is made while it is offline */ @@ -1269,6 +1360,10 @@ private OrderBookKey(String inRequestId, * current status of the feed */ private volatile FeedStatus feedStatus; + /** + * holds feed status requests by data flow id + */ + private final Cache feedStatusRequestDataByDataFlowId = CacheBuilder.newBuilder().build(); /** * event order books keyed by instrument */ @@ -1280,11 +1375,11 @@ private OrderBookKey(String inRequestId, /** * holds data request info keyed by request id */ - private final Map requestsByRequestId = Maps.newHashMap(); + private final Map requestsByRequestId = Maps.newHashMap(); /** * holds market data request info by data flow id */ - private final Map requestsByDataFlowId = Maps.newHashMap(); + private final Map requestsByDataFlowId = Maps.newHashMap(); /** * supported asset classes for this provider */ diff --git a/modules/marketdata/marketdata-exsim/src/main/java/org/marketcetera/marketdata/exsim/ExsimFeedModuleFactory.java b/modules/marketdata/marketdata-exsim/src/main/java/org/marketcetera/marketdata/exsim/ExsimFeedModuleFactory.java index 957faeeb47..2c51d5d764 100644 --- a/modules/marketdata/marketdata-exsim/src/main/java/org/marketcetera/marketdata/exsim/ExsimFeedModuleFactory.java +++ b/modules/marketdata/marketdata-exsim/src/main/java/org/marketcetera/marketdata/exsim/ExsimFeedModuleFactory.java @@ -1,9 +1,16 @@ package org.marketcetera.marketdata.exsim; +import javax.annotation.PostConstruct; + +import org.marketcetera.marketdata.FeedStatus; +import org.marketcetera.marketdata.MarketDataStatus; +import org.marketcetera.marketdata.service.MarketDataService; import org.marketcetera.module.Module; import org.marketcetera.module.ModuleCreationException; import org.marketcetera.module.ModuleFactory; import org.marketcetera.module.ModuleURN; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; /* $License$ */ @@ -25,6 +32,7 @@ * @version $Id$ * @since $Release$ */ +@Service public class ExsimFeedModuleFactory extends ModuleFactory { @@ -47,6 +55,30 @@ public Module create(Object... inParameters) { return new ExsimFeedModule(INSTANCE_URN); } + /** + * Validate and start the object. + */ + @PostConstruct + public void start() + { + marketDataService.reportMarketDataStatus(new MarketDataStatus() { + @Override + public FeedStatus getFeedStatus() + { + return FeedStatus.OFFLINE; + } + @Override + public String getProvider() + { + return IDENTIFIER; + }} + ); + } + /** + * providers market data services + */ + @Autowired + private MarketDataService marketDataService; /** * market data provider identifier */ diff --git a/modules/marketdata/marketdata-manual/src/main/java/org/marketcetera/marketdata/manual/ManualFeedModuleFactory.java b/modules/marketdata/marketdata-manual/src/main/java/org/marketcetera/marketdata/manual/ManualFeedModuleFactory.java index d44c7045b5..9de2565443 100644 --- a/modules/marketdata/marketdata-manual/src/main/java/org/marketcetera/marketdata/manual/ManualFeedModuleFactory.java +++ b/modules/marketdata/marketdata-manual/src/main/java/org/marketcetera/marketdata/manual/ManualFeedModuleFactory.java @@ -1,8 +1,15 @@ package org.marketcetera.marketdata.manual; +import javax.annotation.PostConstruct; + +import org.marketcetera.marketdata.FeedStatus; +import org.marketcetera.marketdata.MarketDataStatus; +import org.marketcetera.marketdata.service.MarketDataService; import org.marketcetera.module.ModuleCreationException; import org.marketcetera.module.ModuleFactory; import org.marketcetera.module.ModuleURN; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; /* $License$ */ @@ -24,6 +31,7 @@ * @version $Id$ * @since $Release$ */ +@Service public class ManualFeedModuleFactory extends ModuleFactory { @@ -46,6 +54,30 @@ public ManualFeedModule create(Object... inParameters) { return new ManualFeedModule(INSTANCE_URN); } + /** + * Validate and start the object. + */ + @PostConstruct + public void start() + { + marketDataService.reportMarketDataStatus(new MarketDataStatus() { + @Override + public FeedStatus getFeedStatus() + { + return FeedStatus.OFFLINE; + } + @Override + public String getProvider() + { + return IDENTIFIER; + }} + ); + } + /** + * providers market data services + */ + @Autowired + private MarketDataService marketDataService; /** * market data provider identifier */ diff --git a/modules/marketdata/yahoo/pom.xml b/modules/marketdata/yahoo/pom.xml index 60169b7ee3..355bfe59cb 100644 --- a/modules/marketdata/yahoo/pom.xml +++ b/modules/marketdata/yahoo/pom.xml @@ -16,6 +16,10 @@ ${project.groupId} core + + ${project.groupId} + marketdata-api + junit junit diff --git a/modules/marketdata/yahoo/src/main/java/org/marketcetera/marketdata/yahoo/YahooFeedModuleFactory.java b/modules/marketdata/yahoo/src/main/java/org/marketcetera/marketdata/yahoo/YahooFeedModuleFactory.java index 193828553f..42d36f4ef8 100644 --- a/modules/marketdata/yahoo/src/main/java/org/marketcetera/marketdata/yahoo/YahooFeedModuleFactory.java +++ b/modules/marketdata/yahoo/src/main/java/org/marketcetera/marketdata/yahoo/YahooFeedModuleFactory.java @@ -1,11 +1,17 @@ package org.marketcetera.marketdata.yahoo; +import javax.annotation.PostConstruct; + import org.marketcetera.core.CoreException; +import org.marketcetera.marketdata.FeedStatus; +import org.marketcetera.marketdata.MarketDataStatus; +import org.marketcetera.marketdata.service.MarketDataService; import org.marketcetera.module.Module; import org.marketcetera.module.ModuleCreationException; import org.marketcetera.module.ModuleFactory; import org.marketcetera.module.ModuleURN; import org.marketcetera.util.misc.ClassVersion; +import org.springframework.beans.factory.annotation.Autowired; /* $License$ */ @@ -44,6 +50,30 @@ public Module create(Object... inArg0) throw new ModuleCreationException(e.getI18NBoundMessage()); } } + /** + * Validate and start the object. + */ + @PostConstruct + public void start() + { + marketDataService.reportMarketDataStatus(new MarketDataStatus() { + @Override + public FeedStatus getFeedStatus() + { + return FeedStatus.OFFLINE; + } + @Override + public String getProvider() + { + return IDENTIFIER; + }} + ); + } + /** + * providers market data services + */ + @Autowired + private MarketDataService marketDataService; /** * provider name of the module */ diff --git a/packages/dare-package/pom.xml b/packages/dare-package/pom.xml index e9f003129e..d3939697be 100644 --- a/packages/dare-package/pom.xml +++ b/packages/dare-package/pom.xml @@ -100,6 +100,26 @@ marketdata-exsim runtime + + ${project.groupId} + marketdata-bogus + runtime + + + ${project.groupId} + marketdata-csv + runtime + + + ${project.groupId} + marketdata-manual + runtime + + + ${project.groupId} + marketdata-yahoo + runtime + ${project.groupId} dare diff --git a/photon/src/main/java/org/marketcetera/ui/marketdata/service/MarketDataClientService.java b/photon/src/main/java/org/marketcetera/ui/marketdata/service/MarketDataClientService.java index 342e8bdddf..d705949458 100644 --- a/photon/src/main/java/org/marketcetera/ui/marketdata/service/MarketDataClientService.java +++ b/photon/src/main/java/org/marketcetera/ui/marketdata/service/MarketDataClientService.java @@ -117,6 +117,18 @@ public Set getAvailableCapability() { return marketDataClient.getAvailableCapability(); } + /** + * Gets the active providers. + * + *

Providers may or may not be connected at this time, these are the providers known + * to the system.

+ * + * @return a Set<String> value + */ + public Set getProviders() + { + return marketDataClient.getProviders(); + } /** * Get the instance value. * diff --git a/photon/src/main/java/org/marketcetera/ui/marketdata/view/MarketDataListView.java b/photon/src/main/java/org/marketcetera/ui/marketdata/view/MarketDataListView.java index 1864fa3715..14bc633ef7 100644 --- a/photon/src/main/java/org/marketcetera/ui/marketdata/view/MarketDataListView.java +++ b/photon/src/main/java/org/marketcetera/ui/marketdata/view/MarketDataListView.java @@ -217,11 +217,16 @@ private void doMarketDataRequest(String inSymbol) MarketDataItem newItem = new MarketDataItem(instrument, marketDataRequestId); marketDataTable.getItems().add(newItem); - MarketDataRequest request = MarketDataRequestBuilder.newRequest().withSymbols(inSymbol).withAssetClass(AssetClass.getFor(instrument.getSecurityType())) + MarketDataRequestBuilder requestBuilder = MarketDataRequestBuilder.newRequest(); + if(providerComboBox.valueProperty().get() != null && providerComboBox.valueProperty().get() != ALL_PROVIDERS) { + requestBuilder.withProvider(providerComboBox.valueProperty().get()); + } + requestBuilder.withSymbols(inSymbol).withAssetClass(AssetClass.getFor(instrument.getSecurityType())) .withContent(Content.LATEST_TICK,Content.TOP_OF_BOOK,Content.MARKET_STAT).withRequestId(marketDataRequestId).create(); MarketDataRowListener rowListener = new MarketDataRowListener(newItem); symbolsByRequestId.put(marketDataRequestId, inSymbol); + MarketDataRequest request = requestBuilder.create(); SLF4JLoggerProxy.debug(this, "Submitting {}", request); @@ -251,6 +256,8 @@ private void initializeAddSymbol() }); addSymbolLayout.setAlignment(Pos.CENTER_RIGHT); providerComboBox = new ComboBox<>(); + providerComboBox.getItems().add(ALL_PROVIDERS); + providerComboBox.getItems().addAll(marketdataClient.getProviders()); addSymbolLayout.getChildren().addAll(providerComboBox, addSymbolTextField, addSymbolButton); @@ -551,6 +558,7 @@ private MarketDataRowListener(MarketDataItem inMarketDataItem) */ private final MarketDataItem marketDataItem; } + private static final String ALL_PROVIDERS = ""; /** * market data item context menu */ From 90d017d1f32eb39d980678c8e84cd2eb908aa9c1 Mon Sep 17 00:00:00 2001 From: Colin DuPlantis Date: Thu, 30 Mar 2023 10:35:50 -0700 Subject: [PATCH 2/4] MATP-1019 Photon market data view should allow specific exchange --- .../service/MarketDataServiceImpl.java | 8 +- .../marketdata/bogus/BogusFeed.java | 21 +- .../marketdata/bogus/BogusFeedModule.java | 47 ++++- .../marketdata/manual/ManualFeedModule.java | 192 +++++++++++++----- packages/dare-package/pom.xml | 20 -- .../marketdata/view/MarketDataDetailView.java | 22 +- .../marketdata/view/MarketDataListView.java | 3 + 7 files changed, 221 insertions(+), 92 deletions(-) diff --git a/marketdata/marketdata-server/src/main/java/org/marketcetera/marketdata/service/MarketDataServiceImpl.java b/marketdata/marketdata-server/src/main/java/org/marketcetera/marketdata/service/MarketDataServiceImpl.java index 75d09e9d63..4f4a6e4c9c 100644 --- a/marketdata/marketdata-server/src/main/java/org/marketcetera/marketdata/service/MarketDataServiceImpl.java +++ b/marketdata/marketdata-server/src/main/java/org/marketcetera/marketdata/service/MarketDataServiceImpl.java @@ -87,6 +87,9 @@ public void removeMarketDataStatusListener(MarketDataStatusListener inMarketData @Override public void reportMarketDataStatus(MarketDataStatus inMarketDataStatus) { + SLF4JLoggerProxy.debug(this, + "Reporting {}", + inMarketDataStatus); cachedMarketDataStatus.put(inMarketDataStatus.getProvider(), inMarketDataStatus); logProviderData(); @@ -94,9 +97,8 @@ public void reportMarketDataStatus(MarketDataStatus inMarketDataStatus) try { listener.receiveMarketDataStatus(inMarketDataStatus); } catch (Exception e) { - PlatformServices.handleException(this, - "Reporting market data status", - e); + SLF4JLoggerProxy.warn(this, + e); removeMarketDataStatusListener(listener); } } diff --git a/modules/marketdata/bogus/src/main/java/org/marketcetera/marketdata/bogus/BogusFeed.java b/modules/marketdata/bogus/src/main/java/org/marketcetera/marketdata/bogus/BogusFeed.java index d76bac9c60..2af3b580fc 100644 --- a/modules/marketdata/bogus/src/main/java/org/marketcetera/marketdata/bogus/BogusFeed.java +++ b/modules/marketdata/bogus/src/main/java/org/marketcetera/marketdata/bogus/BogusFeed.java @@ -5,6 +5,7 @@ import static org.marketcetera.marketdata.AssetClass.EQUITY; import static org.marketcetera.marketdata.AssetClass.FUTURE; import static org.marketcetera.marketdata.AssetClass.OPTION; +import static org.marketcetera.marketdata.Capability.BBO10; import static org.marketcetera.marketdata.Capability.DIVIDEND; import static org.marketcetera.marketdata.Capability.EVENT_BOUNDARY; import static org.marketcetera.marketdata.Capability.LATEST_TICK; @@ -13,7 +14,6 @@ import static org.marketcetera.marketdata.Capability.OPEN_BOOK; import static org.marketcetera.marketdata.Capability.TOP_OF_BOOK; import static org.marketcetera.marketdata.Capability.TOTAL_VIEW; -import static org.marketcetera.marketdata.Capability.BBO10; import static org.marketcetera.marketdata.bogus.Messages.UNSUPPORTED_OPTION_SPECIFICATION; import java.util.ArrayList; @@ -37,6 +37,7 @@ import org.marketcetera.marketdata.ExchangeRequest; import org.marketcetera.marketdata.ExchangeRequestBuilder; import org.marketcetera.marketdata.FeedException; +import org.marketcetera.marketdata.FeedStatus; import org.marketcetera.marketdata.MarketDataFeed; import org.marketcetera.marketdata.MarketDataFeedTokenSpec; import org.marketcetera.marketdata.MarketDataRequest; @@ -67,12 +68,12 @@ */ @ClassVersion("$Id$") public class BogusFeed - extends AbstractMarketDataFeed + extends AbstractMarketDataFeed { /** * Returns an instance of BogusFeed. @@ -82,8 +83,8 @@ public class BogusFeed * @throws NoMoreIDsException if a unique identifier could not be generated to * be assigned */ - public synchronized static BogusFeed getInstance(String inProviderName) - throws NoMoreIDsException + public synchronized static BogusFeed getInstance(String inProviderName) + throws NoMoreIDsException { if(sInstance != null) { return sInstance; @@ -126,6 +127,7 @@ public synchronized void start() { exchanges.put(exchange.getCode(), exchange); } + setFeedStatus(FeedStatus.AVAILABLE); super.start(); } /* (non-Javadoc) @@ -133,6 +135,7 @@ public synchronized void start() { */ @Override public synchronized void stop() { + setFeedStatus(FeedStatus.OFFLINE); if(!getFeedStatus().isRunning()) { throw new IllegalStateException(); } diff --git a/modules/marketdata/bogus/src/main/java/org/marketcetera/marketdata/bogus/BogusFeedModule.java b/modules/marketdata/bogus/src/main/java/org/marketcetera/marketdata/bogus/BogusFeedModule.java index 70a93eacf8..483539b4a1 100644 --- a/modules/marketdata/bogus/src/main/java/org/marketcetera/marketdata/bogus/BogusFeedModule.java +++ b/modules/marketdata/bogus/src/main/java/org/marketcetera/marketdata/bogus/BogusFeedModule.java @@ -1,13 +1,21 @@ package org.marketcetera.marketdata.bogus; import org.marketcetera.core.CoreException; +import org.marketcetera.core.IFeedComponentListener; import org.marketcetera.marketdata.AbstractMarketDataModule; +import org.marketcetera.marketdata.FeedStatus; +import org.marketcetera.marketdata.IFeedComponent; +import org.marketcetera.marketdata.MarketDataStatus; +import org.marketcetera.marketdata.service.MarketDataService; +import org.marketcetera.module.AutowiredModule; +import org.marketcetera.util.log.SLF4JLoggerProxy; import org.marketcetera.util.misc.ClassVersion; +import org.springframework.beans.factory.annotation.Autowired; /* $License$ */ /** - * StrategyAgent module for {@link BogusFeed}. + * Module for {@link BogusFeed}. *

* Module Features * @@ -19,29 +27,56 @@ * @version $Id$ * @since 1.0.0 */ +@AutowiredModule @ClassVersion("$Id$") //$NON-NLS-1$ public final class BogusFeedModule extends AbstractMarketDataModule { /** - * Create a new BogusFeedEmitter instance. - * - * @throws CoreException + * Create a new BogusFeedModule instance. + * + * @throws CoreException if the module could not be created */ BogusFeedModule() - throws CoreException + throws CoreException { super(BogusFeedModuleFactory.INSTANCE_URN, BogusFeedFactory.getInstance().getMarketDataFeed()); + getFeed().addFeedComponentListener(new IFeedComponentListener() { + @Override + public void feedComponentChanged(IFeedComponent inComponent) + { + SLF4JLoggerProxy.warn(BogusFeedModule.this, + "COCO: received feed commponent: {}", + inComponent.getFeedStatus()); + marketDataService.reportMarketDataStatus(new MarketDataStatus() { + @Override + public FeedStatus getFeedStatus() + { + return inComponent.getFeedStatus(); + } + @Override + public String getProvider() + { + return BogusFeedModuleFactory.IDENTIFIER; + }} + ); + }} + ); } /* (non-Javadoc) * @see org.marketcetera.marketdata.AbstractMarketDataModule#getCredentials() */ @Override protected BogusFeedCredentials getCredentials() - throws CoreException + throws CoreException { return BogusFeedCredentials.getInstance(); } + /** + * provides market data services + */ + @Autowired + private MarketDataService marketDataService; } diff --git a/modules/marketdata/marketdata-manual/src/main/java/org/marketcetera/marketdata/manual/ManualFeedModule.java b/modules/marketdata/marketdata-manual/src/main/java/org/marketcetera/marketdata/manual/ManualFeedModule.java index c9a828c259..c938b8d699 100644 --- a/modules/marketdata/marketdata-manual/src/main/java/org/marketcetera/marketdata/manual/ManualFeedModule.java +++ b/modules/marketdata/marketdata-manual/src/main/java/org/marketcetera/marketdata/manual/ManualFeedModule.java @@ -9,11 +9,14 @@ import org.marketcetera.event.Event; import org.marketcetera.marketdata.Capability; import org.marketcetera.marketdata.FeedStatus; +import org.marketcetera.marketdata.FeedStatusRequest; import org.marketcetera.marketdata.MarketDataCapabilityBroadcaster; import org.marketcetera.marketdata.MarketDataRequest; import org.marketcetera.marketdata.MarketDataRequestBuilder; import org.marketcetera.marketdata.MarketDataStatus; import org.marketcetera.marketdata.MarketDataStatusBroadcaster; +import org.marketcetera.marketdata.service.MarketDataService; +import org.marketcetera.module.AutowiredModule; import org.marketcetera.module.DataEmitter; import org.marketcetera.module.DataEmitterSupport; import org.marketcetera.module.DataFlowID; @@ -43,6 +46,7 @@ * @version $Id$ * @since $Release$ */ +@AutowiredModule public class ManualFeedModule extends Module implements DataEmitter @@ -68,14 +72,14 @@ public void emit(String inRequestId, if(inRequestId == null) { SLF4JLoggerProxy.debug(this, "No request id specified, submitting to all data flows"); - for(RequestData request : requestsByRequestId.asMap().values()) { + for(MarketDataRequestData request : requestsByRequestId.asMap().values()) { for(Event event : inEvents) { emit(request.getDataEmitterSupport(), event); } } } else { - RequestData requestData = requestsByRequestId.getIfPresent(inRequestId); + MarketDataRequestData requestData = requestsByRequestId.getIfPresent(inRequestId); if(requestData == null) { SLF4JLoggerProxy.warn(this, "No request with id {}, cannot emit events", @@ -100,12 +104,12 @@ public void emit(String inRequestId, if(inRequestId == null) { SLF4JLoggerProxy.debug(this, "No request id specified, submitting to all data flows"); - for(RequestData request : requestsByRequestId.asMap().values()) { + for(MarketDataRequestData request : requestsByRequestId.asMap().values()) { emit(request.getDataEmitterSupport(), inEvent); } } else { - RequestData requestData = requestsByRequestId.getIfPresent(inRequestId); + MarketDataRequestData requestData = requestsByRequestId.getIfPresent(inRequestId); if(requestData == null) { SLF4JLoggerProxy.warn(this, "No request with id {}, cannot emit events", @@ -124,7 +128,7 @@ public void emit(String inRequestId, public BiMap getRequests() { BiMap requests = HashBiMap.create(); - for(Map.Entry entry : requestsByRequestId.asMap().entrySet()) { + for(Map.Entry entry : requestsByRequestId.asMap().entrySet()) { requests.put(entry.getKey(), entry.getValue().getMarketDataRequest()); } @@ -161,6 +165,10 @@ public void requestData(DataRequest inRequest, doMarketDataRequest((MarketDataRequest)payload, inRequest, inSupport); + } else if(payload instanceof FeedStatusRequest) { + doFeedStatusRequest((FeedStatusRequest)payload, + inRequest, + inSupport); } else { throw new RequestDataException(new I18NBoundMessage1P(Messages.UNSUPPORTED_DATA_REQUEST_PAYLOAD, payload.getClass().getSimpleName())); @@ -179,7 +187,7 @@ public void requestData(DataRequest inRequest, public void cancel(DataFlowID inFlowId, RequestID inRequestID) { - RequestData requestData = requestsByDataFlowId.getIfPresent(inFlowId); + MarketDataRequestData requestData = requestsByDataFlowId.getIfPresent(inFlowId); requestsByDataFlowId.invalidate(inFlowId); if(requestData != null) { SLF4JLoggerProxy.debug(this, @@ -199,21 +207,7 @@ protected void preStart() for(MarketDataCapabilityBroadcaster broadcaster : capabilityBroadcasters) { broadcaster.reportCapability(EnumSet.allOf(Capability.class)); } - MarketDataStatus status = new MarketDataStatus() { - @Override - public FeedStatus getFeedStatus() - { - return FeedStatus.AVAILABLE; - } - @Override - public String getProvider() - { - return ManualFeedModuleFactory.IDENTIFIER; - } - }; - for(MarketDataStatusBroadcaster broadcaster : statusBroadcasters) { - broadcaster.reportMarketDataStatus(status); - } + updateFeedStatus(FeedStatus.AVAILABLE); } /* (non-Javadoc) * @see org.marketcetera.module.Module#preStop() @@ -224,11 +218,39 @@ protected void preStop() { requestsByDataFlowId.invalidateAll(); requestsByRequestId.invalidateAll(); - MarketDataStatus status = new MarketDataStatus() { + updateFeedStatus(FeedStatus.OFFLINE); + } + /** + * Create a new ManualFeedModule instance. + * + * @param inUrn a ModuleURN value + */ + ManualFeedModule(ModuleURN inUrn) + { + super(inUrn, + false); + instance = this; + } + /** + * Update the feed status to the new given value. + * + * @param inNewStatus a FeedStatus value + */ + private void updateFeedStatus(FeedStatus inNewStatus) + { + if(inNewStatus == feedStatus) { + return; + } + SLF4JLoggerProxy.debug(this, + "Updating feed status from {} to {}", + feedStatus, + inNewStatus); + feedStatus = inNewStatus; + MarketDataStatus marketDataStatus = new MarketDataStatus() { @Override public FeedStatus getFeedStatus() { - return FeedStatus.OFFLINE; + return feedStatus; } @Override public String getProvider() @@ -236,20 +258,33 @@ public String getProvider() return ManualFeedModuleFactory.IDENTIFIER; } }; + marketDataService.reportMarketDataStatus(marketDataStatus); + for(FeedStatusRequestData feedStatusRequestData : feedStatusRequestDataByDataFlowId.asMap().values()) { + try { + feedStatusRequestData.getDataEmitterSupport().send(marketDataStatus); + } catch (Exception e) { + SLF4JLoggerProxy.warn(this, + e); + } + } for(MarketDataStatusBroadcaster broadcaster : statusBroadcasters) { - broadcaster.reportMarketDataStatus(status); + broadcaster.reportMarketDataStatus(marketDataStatus); } } /** - * Create a new ManualFeedModule instance. + * Execute a feed status request with the given attributes. * - * @param inUrn a ModuleURN value + * @param inPayload a FeedStatusRequest value + * @param inRequest a DataRequest value + * @param inSupport a DataEmitterSupport value */ - ManualFeedModule(ModuleURN inUrn) + private void doFeedStatusRequest(FeedStatusRequest inPayload, + DataRequest inRequest, + DataEmitterSupport inSupport) { - super(inUrn, - false); - instance = this; + FeedStatusRequestData metaData = new FeedStatusRequestData(inSupport); + feedStatusRequestDataByDataFlowId.put(inSupport.getFlowID(), + metaData); } /** * Emit the given event to the given data flow. @@ -278,7 +313,7 @@ private void doMarketDataRequest(MarketDataRequest inPayload, DataEmitterSupport inSupport) { String id = inPayload.getRequestId(); - RequestData requestData = new RequestData(inSupport, + MarketDataRequestData requestData = new MarketDataRequestData(inSupport, id, inPayload); requestsByRequestId.put(id, @@ -286,6 +321,58 @@ private void doMarketDataRequest(MarketDataRequest inPayload, requestsByDataFlowId.put(inSupport.getFlowID(), requestData); } + /** + * Provides common behavior for data flow requests. + * + * @author Colin DuPlantis + * @version $Id$ + * @since $Release$ + */ + private static abstract class AbstractRequestData + { + /** + * Get the dataEmitterSupport value. + * + * @return a DataEmitterSupport value + */ + protected DataEmitterSupport getDataEmitterSupport() + { + return dataEmitterSupport; + } + /** + * Create a new AbstractRequestData instance. + * + * @param inDataEmitterSupport a DataEmitterSupport value + */ + protected AbstractRequestData(DataEmitterSupport inDataEmitterSupport) + { + dataEmitterSupport = inDataEmitterSupport; + } + /** + * data emitter support value + */ + private final DataEmitterSupport dataEmitterSupport; + } + /** + * Holds data relevant to a feed status request as part of a module data flow. + * + * @author Colin DuPlantis + * @version $Id$ + * @since $Release$ + */ + private static class FeedStatusRequestData + extends AbstractRequestData + { + /** + * Create a new FeedStatusRequestData instance. + * + * @param inDataEmitterSupport a DataEmitterSupport value + */ + private FeedStatusRequestData(DataEmitterSupport inDataEmitterSupport) + { + super(inDataEmitterSupport); + } + } /** * Holds data relevant to a market data request as part of a module data flow. * @@ -293,7 +380,8 @@ private void doMarketDataRequest(MarketDataRequest inPayload, * @version $Id$ * @since $Release$ */ - private static class RequestData + private static class MarketDataRequestData + extends AbstractRequestData { /* (non-Javadoc) * @see java.lang.Object#toString() @@ -303,15 +391,6 @@ public String toString() { return description; } - /** - * Get the dataEmitterSupport value. - * - * @return a DataEmitterSupport value - */ - private DataEmitterSupport getDataEmitterSupport() - { - return dataEmitterSupport; - } /** * Get the requestId value. * @@ -338,12 +417,12 @@ private MarketDataRequest getMarketDataRequest() * @param inRequestId a String value * @param inMarketDataRequest a MarketDataRequest value */ - private RequestData(DataEmitterSupport inDataEmitterSupport, - String inRequestId, - MarketDataRequest inMarketDataRequest) + private MarketDataRequestData(DataEmitterSupport inDataEmitterSupport, + String inRequestId, + MarketDataRequest inMarketDataRequest) { - dataEmitterSupport = inDataEmitterSupport; - description = RequestData.class.getSimpleName() + " [" + inDataEmitterSupport.getFlowID() + "]"; //$NON-NLS-1$ //$NON-NLS-2$ + super(inDataEmitterSupport); + description = MarketDataRequestData.class.getSimpleName() + " [" + inDataEmitterSupport.getFlowID() + "]"; //$NON-NLS-1$ //$NON-NLS-2$ requestId = inRequestId; marketDataRequest = inMarketDataRequest; } @@ -351,10 +430,6 @@ private RequestData(DataEmitterSupport inDataEmitterSupport, * human-readable description of the object */ private final String description; - /** - * information about the data flow requester - */ - private final DataEmitterSupport dataEmitterSupport; /** * request id of the request */ @@ -364,6 +439,14 @@ private RequestData(DataEmitterSupport inDataEmitterSupport, */ private final MarketDataRequest marketDataRequest; } + /** + * current status of the feed + */ + private volatile FeedStatus feedStatus; + /** + * holds feed status requests by data flow id + */ + private final Cache feedStatusRequestDataByDataFlowId = CacheBuilder.newBuilder().build(); /** * receivers of capabilities of this module */ @@ -374,14 +457,19 @@ private RequestData(DataEmitterSupport inDataEmitterSupport, */ @Autowired(required=false) private Collection statusBroadcasters = Lists.newArrayList(); + /** + * provides access to market data services + */ + @Autowired + private MarketDataService marketDataService; /** * holds data request info keyed by request id */ - private final Cache requestsByRequestId = CacheBuilder.newBuilder().build(); + private final Cache requestsByRequestId = CacheBuilder.newBuilder().build(); /** * holds market data request info by data flow id */ - private final Cache requestsByDataFlowId = CacheBuilder.newBuilder().build(); + private final Cache requestsByDataFlowId = CacheBuilder.newBuilder().build(); /** * singleton reference value */ diff --git a/packages/dare-package/pom.xml b/packages/dare-package/pom.xml index d3939697be..e9f003129e 100644 --- a/packages/dare-package/pom.xml +++ b/packages/dare-package/pom.xml @@ -100,26 +100,6 @@ marketdata-exsim runtime - - ${project.groupId} - marketdata-bogus - runtime - - - ${project.groupId} - marketdata-csv - runtime - - - ${project.groupId} - marketdata-manual - runtime - - - ${project.groupId} - marketdata-yahoo - runtime - ${project.groupId} dare diff --git a/photon/src/main/java/org/marketcetera/ui/marketdata/view/MarketDataDetailView.java b/photon/src/main/java/org/marketcetera/ui/marketdata/view/MarketDataDetailView.java index 63ee9a73d2..ad3ff439a6 100644 --- a/photon/src/main/java/org/marketcetera/ui/marketdata/view/MarketDataDetailView.java +++ b/photon/src/main/java/org/marketcetera/ui/marketdata/view/MarketDataDetailView.java @@ -41,6 +41,7 @@ import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Button; +import javafx.scene.control.ComboBox; import javafx.scene.control.ContextMenu; import javafx.scene.control.Label; import javafx.scene.control.MenuItem; @@ -212,7 +213,11 @@ private void initializeAddSymbol() addSymbolTextField.setText(null); }); addSymbolLayout.setAlignment(Pos.CENTER_LEFT); - addSymbolLayout.getChildren().addAll(addSymbolTextField, + providerComboBox = new ComboBox<>(); + providerComboBox.getItems().add(ALL_PROVIDERS); + providerComboBox.getItems().addAll(marketDataClient.getProviders()); + addSymbolLayout.getChildren().addAll(providerComboBox, + addSymbolTextField, addSymbolButton); } /** @@ -329,8 +334,13 @@ private void doMarketDataRequest() asks.clear(); // TODO reset chart/top-level view depthMarketDataRequestId = UUID.randomUUID().toString(); + MarketDataRequestBuilder depthRequestBuilder = MarketDataRequestBuilder.newRequest().withRequestId(depthMarketDataRequestId).withSymbols(marketDataInstrument.getSymbol()); // TODO choose between aggregated and unaggregated? - MarketDataRequest depthRequest = MarketDataRequestBuilder.newRequest().withRequestId(depthMarketDataRequestId).withSymbols(marketDataInstrument.getSymbol()).withContent(Content.AGGREGATED_DEPTH).create(); + depthRequestBuilder = depthRequestBuilder.withContent(Content.AGGREGATED_DEPTH); + if(providerComboBox.valueProperty().get() != null && providerComboBox.valueProperty().get() != ALL_PROVIDERS) { + depthRequestBuilder.withProvider(providerComboBox.valueProperty().get()); + } + MarketDataRequest depthRequest = depthRequestBuilder.create(); marketDataClient.request(depthRequest, new MarketDataListener() { /* (non-Javadoc) @@ -479,6 +489,10 @@ private void initializeChart() // chart.getRenderers().clear(); // chart.getRenderers().add(candleStickRenderer); } + /** + * wrench value to indicate all providers are selected + */ + private static final String ALL_PROVIDERS = ""; /** * market data request id */ @@ -551,6 +565,10 @@ private void initializeChart() * add symbol control button */ private Button addSymbolButton; + /** + * allows selection of a specific market data provider + */ + private ComboBox providerComboBox; /** * bid timestamp column */ diff --git a/photon/src/main/java/org/marketcetera/ui/marketdata/view/MarketDataListView.java b/photon/src/main/java/org/marketcetera/ui/marketdata/view/MarketDataListView.java index 14bc633ef7..237bfe1594 100644 --- a/photon/src/main/java/org/marketcetera/ui/marketdata/view/MarketDataListView.java +++ b/photon/src/main/java/org/marketcetera/ui/marketdata/view/MarketDataListView.java @@ -558,6 +558,9 @@ private MarketDataRowListener(MarketDataItem inMarketDataItem) */ private final MarketDataItem marketDataItem; } + /** + * wrench value to indicate all providers are selected + */ private static final String ALL_PROVIDERS = ""; /** * market data item context menu From 81d5670ee22f5868714f8cabeb5b582091240e31 Mon Sep 17 00:00:00 2001 From: Colin DuPlantis Date: Thu, 30 Mar 2023 10:38:15 -0700 Subject: [PATCH 3/4] MATP-1019 Photon market data view should allow specific exchange --- .../marketdata/service/MarketDataEventModuleConnector.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/marketdata/marketdata-server/src/main/java/org/marketcetera/marketdata/service/MarketDataEventModuleConnector.java b/marketdata/marketdata-server/src/main/java/org/marketcetera/marketdata/service/MarketDataEventModuleConnector.java index 25e0139ee7..31e59d88ab 100644 --- a/marketdata/marketdata-server/src/main/java/org/marketcetera/marketdata/service/MarketDataEventModuleConnector.java +++ b/marketdata/marketdata-server/src/main/java/org/marketcetera/marketdata/service/MarketDataEventModuleConnector.java @@ -125,10 +125,6 @@ public void receiveData(DataFlowID inFlowId, Object inData) throws ReceiveDataException { - SLF4JLoggerProxy.warn(this, - "Received {} for data flow id: {}", - inData, - inFlowId); if(inData instanceof Event) { String marketDataRequestId = requestsByDataFlowId.getIfPresent(inFlowId); if(marketDataRequestId == null) { From 7dbeadc204face8ac6a9ed8304df14111c7436b7 Mon Sep 17 00:00:00 2001 From: Colin DuPlantis Date: Thu, 30 Mar 2023 10:40:17 -0700 Subject: [PATCH 4/4] MATP-1019 Photon market data view should allow specific exchange --- .../org/marketcetera/marketdata/bogus/BogusFeedModule.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/modules/marketdata/bogus/src/main/java/org/marketcetera/marketdata/bogus/BogusFeedModule.java b/modules/marketdata/bogus/src/main/java/org/marketcetera/marketdata/bogus/BogusFeedModule.java index 483539b4a1..3d9082253d 100644 --- a/modules/marketdata/bogus/src/main/java/org/marketcetera/marketdata/bogus/BogusFeedModule.java +++ b/modules/marketdata/bogus/src/main/java/org/marketcetera/marketdata/bogus/BogusFeedModule.java @@ -8,7 +8,6 @@ import org.marketcetera.marketdata.MarketDataStatus; import org.marketcetera.marketdata.service.MarketDataService; import org.marketcetera.module.AutowiredModule; -import org.marketcetera.util.log.SLF4JLoggerProxy; import org.marketcetera.util.misc.ClassVersion; import org.springframework.beans.factory.annotation.Autowired; @@ -47,9 +46,6 @@ public final class BogusFeedModule @Override public void feedComponentChanged(IFeedComponent inComponent) { - SLF4JLoggerProxy.warn(BogusFeedModule.this, - "COCO: received feed commponent: {}", - inComponent.getFeedStatus()); marketDataService.reportMarketDataStatus(new MarketDataStatus() { @Override public FeedStatus getFeedStatus()