com.opengamma.examples.simulated.livedata.ExampleLiveDataServer.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.examples.simulated.livedata.ExampleLiveDataServer.java

Source

/**
 * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
 *
 * Please see distribution for license.
 */
package com.opengamma.examples.simulated.livedata;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

import net.sf.ehcache.CacheManager;

import org.apache.commons.io.IOUtils;
import org.fudgemsg.FudgeContext;
import org.fudgemsg.FudgeField;
import org.fudgemsg.FudgeMsg;
import org.fudgemsg.MutableFudgeMsg;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;

import au.com.bytecode.opencsv.CSVReader;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.opengamma.core.id.ExternalSchemes;
import com.opengamma.id.ExternalScheme;
import com.opengamma.livedata.server.StandardLiveDataServer;
import com.opengamma.livedata.server.Subscription;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.NamedThreadPoolFactory;
import com.opengamma.util.TerminatableJob;
import com.opengamma.util.fudgemsg.OpenGammaFudgeContext;

/**
 * An ultra-simple market data simulator, we load the initial values from a CSV file (with a header row)
 * and the format
 * <identification-scheme>, <identifier-value>, <requirement-name>, <value>
 * typically, for last price, you'd use "Market_Value" @see MarketDataRequirementNames
 */
public class ExampleLiveDataServer extends StandardLiveDataServer {

    private static final Logger s_logger = LoggerFactory.getLogger(ExampleLiveDataServer.class);

    private static final FudgeContext s_fudgeConext = OpenGammaFudgeContext.getInstance();
    private static final int NUM_FIELDS = 3;
    private static final double SCALING_FACTOR = 0.005; // i.e. 0.5% * 1SD
    private static final int MAX_MILLIS_BETWEEN_TICKS = 500;

    private final Map<String, FudgeMsg> _marketValues = Maps.newConcurrentMap();
    private final Map<String, FudgeMsg> _baseValues = Maps.newConcurrentMap();
    private volatile double _scalingFactor;
    private volatile int _maxMillisBetweenTicks;
    private final TerminatableJob _marketDataSimulatorJob = new SimulatedMarketDataJob();
    private final ExecutorService _executorService = Executors
            .newCachedThreadPool(new NamedThreadPoolFactory("ExampleLiveDataServer"));

    public ExampleLiveDataServer(final CacheManager cacheManager, final Resource initialValuesFile) {
        this(cacheManager, initialValuesFile, SCALING_FACTOR, MAX_MILLIS_BETWEEN_TICKS);
    }

    public ExampleLiveDataServer(final CacheManager cacheManager, final Resource initialValuesFile,
            final double scalingFactor, final int maxMillisBetweenTicks) {
        super(cacheManager);
        readInitialValues(initialValuesFile);
        _scalingFactor = scalingFactor;
        _maxMillisBetweenTicks = maxMillisBetweenTicks;
    }

    //-------------------------------------------------------------------------
    private void readInitialValues(final Resource initialValuesFile) {
        CSVReader reader = null;
        try {
            reader = new CSVReader(new BufferedReader(new InputStreamReader(initialValuesFile.getInputStream())));
            // Read header row
            @SuppressWarnings("unused")
            final String[] headers = reader.readNext();
            String[] line;
            int lineNum = 1;
            while ((line = reader.readNext()) != null) {
                lineNum++;
                if (line.length > 0 && line[0].startsWith("#")) {
                    continue;
                }
                if (line.length != NUM_FIELDS) {
                    s_logger.error("Not enough fields in CSV on line " + lineNum);
                } else {
                    final String identifier = line[0];
                    final String fieldName = line[1];
                    final String valueStr = line[2];
                    final Double value = Double.parseDouble(valueStr);
                    addTicks(identifier, fieldName, value);
                }
            }
        } catch (final FileNotFoundException e) {
            e.printStackTrace();
        } catch (final IOException e) {
            e.printStackTrace();
        } finally {
            IOUtils.closeQuietly(reader);
        }
    }

    /**
     * Gets the marketValues.
     * @return the marketValues
     */
    public Map<String, FudgeMsg> getMarketValues() {
        return Collections.unmodifiableMap(_marketValues);
    }

    /**
     * Gets the scalingFactor.
     * @return the scalingFactor
     */
    public double getScalingFactor() {
        return _scalingFactor;
    }

    /**
     * Sets the scalingFactor.
     * @param scalingFactor  the scalingFactor
     */
    public void setScalingFactor(final double scalingFactor) {
        _scalingFactor = scalingFactor;
    }

    /**
     * Gets the maxMillisBetweenTicks.
     * @return the maxMillisBetweenTicks
     */
    public int getMaxMillisBetweenTicks() {
        return _maxMillisBetweenTicks;
    }

    /**
     * Sets the maxMillisBetweenTicks.
     * @param maxMillisBetweenTicks  the maxMillisBetweenTicks
     */
    public void setMaxMillisBetweenTicks(final int maxMillisBetweenTicks) {
        _maxMillisBetweenTicks = maxMillisBetweenTicks;
    }

    /**
     * @param uniqueId the uniqueId, not null
     * @param fieldName the field name, not null
     * @param value the market value, not null
     */
    public void addTicks(final String uniqueId, final String fieldName, final Double value) {
        ArgumentChecker.notNull(uniqueId, "unique identifier");
        ArgumentChecker.notNull(fieldName, "field name");
        ArgumentChecker.notNull(value, "market value");

        final FudgeMsg previousTicks = _marketValues.get(uniqueId);
        MutableFudgeMsg ticks = null;
        if (previousTicks == null) {
            ticks = s_fudgeConext.newMessage();
        } else {
            ticks = s_fudgeConext.newMessage(previousTicks);
            if (ticks.hasField(fieldName)) {
                ticks.remove(fieldName);
            }
        }
        ticks.add(fieldName, value);
        _marketValues.put(uniqueId, ticks);
        _baseValues.put(uniqueId, ticks);
    }

    @Override
    protected Map<String, Object> doSubscribe(final Collection<String> uniqueIds) {
        ArgumentChecker.notNull(uniqueIds, "Subscriptions");
        s_logger.debug("doSubscribe on {}", uniqueIds);

        final Map<String, Object> result = Maps.newHashMap();
        final List<String> missingSubscriptions = Lists.newArrayList();
        for (final String uniqueId : uniqueIds) {
            if (!_marketValues.containsKey(uniqueId)) {
                missingSubscriptions.add(uniqueId);
            }
            result.put(uniqueId, new AtomicReference<String>(uniqueId));
        }
        if (!missingSubscriptions.isEmpty()) {
            s_logger.error("Could not subscribe to {}", missingSubscriptions);
        }
        return result;
    }

    @Override
    protected void doUnsubscribe(final Collection<Object> subscriptionHandles) {
        s_logger.debug("doUnsubscribe on {}", subscriptionHandles);
        // No-op; don't maintain or forward any subscription state
    }

    @Override
    protected Map<String, FudgeMsg> doSnapshot(final Collection<String> uniqueIds) {
        ArgumentChecker.notNull(uniqueIds, "Unique IDs");
        s_logger.debug("doSnapshot on {}", uniqueIds);
        if (uniqueIds.isEmpty()) {
            return Collections.emptyMap();
        }
        final Map<String, FudgeMsg> returnValue = new HashMap<String, FudgeMsg>();
        for (final String securityUniqueId : uniqueIds) {
            final FudgeMsg fieldData = _marketValues.get(securityUniqueId);
            returnValue.put(securityUniqueId, fieldData);
        }
        return returnValue;
    }

    @Override
    protected ExternalScheme getUniqueIdDomain() {
        return ExternalSchemes.OG_SYNTHETIC_TICKER;
    }

    @Override
    protected void doConnect() {
        s_logger.info("ExampleLiveDataServer connecting..");
        _executorService.submit(_marketDataSimulatorJob);
    }

    @Override
    protected void doDisconnect() {
        if (_executorService != null) {
            _executorService.shutdownNow();
            try {
                _executorService.awaitTermination(1, TimeUnit.SECONDS);
            } catch (InterruptedException ex) {
                Thread.interrupted();
            }
        }
    }

    @Override
    protected boolean snapshotOnSubscriptionStartRequired(final Subscription subscription) {
        return true;
    }

    private class SimulatedMarketDataJob extends TerminatableJob {

        private final Random _random = new Random();

        private double wiggleValue(final double value, final double centre) {
            return (9 * value + centre) / 10 + (_random.nextGaussian() * (value * _scalingFactor));
        }

        @Override
        protected void runOneCycle() {
            Set<String> activeSubscriptionIds = getActiveSubscriptionIds();
            if (!activeSubscriptionIds.isEmpty()) {
                for (String identifier : activeSubscriptionIds) {
                    final FudgeMsg lastValues = _marketValues.get(identifier);
                    final FudgeMsg baseValues = _baseValues.get(identifier);
                    final MutableFudgeMsg nextValues = s_fudgeConext.newMessage();
                    for (final FudgeField field : lastValues) {
                        final double lastValue = (Double) field.getValue();
                        final double baseValue = baseValues.getDouble(field.getName());
                        final double value = wiggleValue(lastValue, baseValue);
                        nextValues.add(field.getName(), value);
                    }
                    _marketValues.put(identifier, nextValues);
                    liveDataReceived(identifier, nextValues);
                    s_logger.debug("{} lastValues: {} nextValues: {}", identifier, lastValues, nextValues);
                }
                try {
                    Thread.sleep(_random.nextInt(_maxMillisBetweenTicks));
                } catch (final InterruptedException e) {
                    s_logger.error("Sleep interrupted, finishing");
                    Thread.interrupted();
                }
            }
        }
    }

}