ch.algotrader.service.MarketDataCacheServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for ch.algotrader.service.MarketDataCacheServiceImpl.java

Source

/***********************************************************************************
 * AlgoTrader Enterprise Trading Framework
 *
 * Copyright (C) 2015 AlgoTrader GmbH - All rights reserved
 *
 * All information contained herein is, and remains the property of AlgoTrader GmbH.
 * The intellectual and technical concepts contained herein are proprietary to
 * AlgoTrader GmbH. Modification, translation, reverse engineering, decompilation,
 * disassembly or reproduction of this material is strictly forbidden unless prior
 * written permission is obtained from AlgoTrader GmbH
 *
 * Fur detailed terms and conditions consult the file LICENSE.txt or contact
 *
 * AlgoTrader GmbH
 * Aeschstrasse 6
 * 8834 Schindellegi
 ***********************************************************************************/
package ch.algotrader.service;

import java.math.BigDecimal;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.apache.commons.lang.Validate;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import ch.algotrader.entity.marketData.BarVO;
import ch.algotrader.entity.marketData.MarketDataEventVO;
import ch.algotrader.entity.marketData.Tick;
import ch.algotrader.entity.marketData.TickVO;
import ch.algotrader.entity.security.Forex;
import ch.algotrader.entity.security.Security;
import ch.algotrader.enumeration.Currency;
import ch.algotrader.esper.EngineManager;
import ch.algotrader.event.listener.BarEventListener;
import ch.algotrader.event.listener.TickEventListener;

/**
 * Lookup for market data cached locally in the current VM.
 *
 * @author <a href="mailto:aflury@algotrader.ch">Andy Flury</a>
 */
public class MarketDataCacheServiceImpl implements MarketDataCacheService, TickEventListener, BarEventListener {

    private static final Logger LOGGER = LogManager.getLogger(MarketDataCacheServiceImpl.class);

    private final EngineManager engineManager;
    private final LookupService lookupService;
    private final Currency baseCurrency;
    private final int lookBackDays;

    private final ConcurrentMap<Long, MarketDataEventVO> lastMarketDataEventBySecurityId;
    private final ConcurrentMap<EnumSet<Currency>, Forex> forexMap;

    public MarketDataCacheServiceImpl(final EngineManager engineManager, final LookupService lookupService,
            final Currency baseCurrency, final int lookBackDays) {

        Validate.notNull(engineManager, "EngineManager is null");
        Validate.notNull(lookupService, "LookupService is null");
        Validate.notNull(baseCurrency, "BaseCurrency is null");

        this.engineManager = engineManager;
        this.lookupService = lookupService;
        this.baseCurrency = baseCurrency;
        this.lookBackDays = lookBackDays;
        this.lastMarketDataEventBySecurityId = new ConcurrentHashMap<>();
        this.forexMap = new ConcurrentHashMap<>();
    }

    @Override
    public Map<Long, MarketDataEventVO> getCurrentMarketDataEvents() {

        return new HashMap<>(lastMarketDataEventBySecurityId);
    }

    @Override
    public MarketDataEventVO getCurrentMarketDataEvent(long securityId) {

        MarketDataEventVO last = lastMarketDataEventBySecurityId.get(securityId);
        if (last != null) {
            return last;
        }
        Tick entity = this.lookupService.getLastTick(securityId, this.engineManager.getCurrentEPTime(),
                this.lookBackDays);
        if (entity != null) {
            onMarketDataEvent(Tick.Converter.INSTANCE.convert(entity));
            // The market data may have changed in the meantime. Get the latest value.
            return lastMarketDataEventBySecurityId.get(securityId);
        }
        return null;
    }

    @Override
    public BigDecimal getCurrentValue(long securityId) {

        MarketDataEventVO marketDataEvent = getCurrentMarketDataEvent(securityId);
        if (marketDataEvent == null) {
            return null;
        }
        return marketDataEvent.getCurrentValue();
    }

    @Override
    public double getForexRate(Currency baseCurrency, Currency transactionCurrency) {

        Validate.notNull(baseCurrency, "Base currency is null");
        Validate.notNull(transactionCurrency, "Transaction currency is null");

        if (baseCurrency.equals(transactionCurrency)) {
            return 1.0;
        }

        final EnumSet<Currency> key = EnumSet.of(baseCurrency, transactionCurrency);
        Forex forex = this.forexMap.get(key);
        if (forex == null) {
            forex = this.lookupService.getForex(baseCurrency, transactionCurrency);
            if (forex == null) {
                throw new ForexAvailabilityException(
                        "Forex does not exist: " + baseCurrency + "." + transactionCurrency);
            }
            this.forexMap.put(key, forex);//we may replace an existing forex entry due to racing but that's ok
        }

        final MarketDataEventVO marketDataEvent = getCurrentMarketDataEvent(forex.getId());

        if (marketDataEvent == null) {
            throw new ForexAvailabilityException(
                    "No exchange rate available for " + baseCurrency + "." + transactionCurrency);
        }

        if (forex.getBaseCurrency().equals(baseCurrency)) {
            // expected case
            return marketDataEvent.getCurrentValueDouble();
        } else {
            // reverse case
            return 1.0 / marketDataEvent.getCurrentValueDouble();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double getForexRateBase(final Currency baseCurrency) {

        Validate.notNull(baseCurrency, "Base currency is null");

        return getForexRate(baseCurrency, this.baseCurrency);

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double getForexRate(long securityId, Currency transactionCurrency) {

        final Security security = this.lookupService.getSecurityInclUnderlyingFamilyAndExchange(securityId);

        return getForexRate(security, transactionCurrency);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double getForexRate(Security security, Currency transactionCurrency) {
        Validate.notNull(security, "Security is null");

        return getForexRate(security.getSecurityFamily().getCurrency(), transactionCurrency);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double getForexRateBase(long securityId) {

        return getForexRate(securityId, this.baseCurrency);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double getForexRateBase(Security security) {

        return getForexRate(security, this.baseCurrency);
    }

    @Override
    public boolean hasCurrentMarketDataEvents() {
        return !lastMarketDataEventBySecurityId.isEmpty();
    }

    @Override
    public void flush() {
        lastMarketDataEventBySecurityId.clear();
    }

    @Override
    public void flush(long securityId) {
        lastMarketDataEventBySecurityId.remove(securityId);
    }

    private void onMarketDataEvent(MarketDataEventVO event) {

        if (event.getDateTime() == null) {
            if (LOGGER.isWarnEnabled()) {
                LOGGER.warn("Market data event with missing timestamp: {}", event);
            }
            return;
        }
        lastMarketDataEventBySecurityId.merge(event.getSecurityId(), event, (lastEvent, newEvent) -> {
            if (lastEvent == null) {
                return newEvent;
            } else {
                return newEvent.getDateTime().compareTo(lastEvent.getDateTime()) >= 0 ? newEvent : lastEvent;
            }
        });
    }

    @Override
    public void onTick(TickVO tick) {
        onMarketDataEvent(tick);
    }

    @Override
    public void onBar(BarVO bar) {
        onMarketDataEvent(bar);
    }
}