ch.algotrader.service.tt.TTFixReferenceDataServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for ch.algotrader.service.tt.TTFixReferenceDataServiceImpl.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.tt;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.time.format.TextStyle;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;

import org.apache.commons.lang.Validate;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import ch.algotrader.adapter.ExternalSessionStateHolder;
import ch.algotrader.adapter.fix.FixAdapter;
import ch.algotrader.adapter.tt.TTPendingRequests;
import ch.algotrader.adapter.tt.TTSecurityDefVO;
import ch.algotrader.adapter.tt.TTSecurityDefinitionRequestFactory;
import ch.algotrader.adapter.tt.TTSecurityDefinitionRequestIdGenerator;
import ch.algotrader.concurrent.Promise;
import ch.algotrader.concurrent.PromiseImpl;
import ch.algotrader.config.CommonConfig;
import ch.algotrader.dao.security.FutureDao;
import ch.algotrader.dao.security.OptionDao;
import ch.algotrader.dao.security.SecurityFamilyDao;
import ch.algotrader.entity.security.Future;
import ch.algotrader.entity.security.FutureFamily;
import ch.algotrader.entity.security.Option;
import ch.algotrader.entity.security.OptionFamily;
import ch.algotrader.entity.security.SecurityFamily;
import ch.algotrader.enumeration.Broker;
import ch.algotrader.enumeration.InitializingServiceType;
import ch.algotrader.enumeration.OptionType;
import ch.algotrader.future.FutureSymbol;
import ch.algotrader.option.OptionSymbol;
import ch.algotrader.service.InitializationPriority;
import ch.algotrader.service.InitializingServiceI;
import ch.algotrader.service.NoServiceResponseException;
import ch.algotrader.service.ReferenceDataService;
import ch.algotrader.service.ServiceException;
import ch.algotrader.util.DateTimeLegacy;
import ch.algotrader.util.DateTimePatterns;
import ch.algotrader.util.PriceUtil;
import quickfix.field.SecurityType;
import quickfix.fix42.SecurityDefinitionRequest;

/**
 * @author <a href="mailto:okalnichevski@algotrader.ch">Oleg Kalnichevski</a>
 */
@InitializationPriority(InitializingServiceType.BROKER_INTERFACE)
@Transactional(propagation = Propagation.SUPPORTS)
public class TTFixReferenceDataServiceImpl implements ReferenceDataService, InitializingServiceI {

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

    private static final DateTimeFormatter ICE_IPE_SYMBOL = new DateTimeFormatterBuilder()
            .appendText(ChronoField.MONTH_OF_YEAR, TextStyle.SHORT).appendValueReduced(ChronoField.YEAR, 2, 2, 2000)
            .toFormatter(Locale.ROOT);

    private final CommonConfig commonConfig;
    private final FixAdapter fixAdapter;
    private final ExternalSessionStateHolder stateHolder;
    private final TTPendingRequests pendingRequests;
    private final OptionDao optionDao;
    private final FutureDao futureDao;
    private final SecurityFamilyDao securityFamilyDao;
    private final TTSecurityDefinitionRequestIdGenerator requestIdGenerator;
    private final TTSecurityDefinitionRequestFactory requestFactory;

    public TTFixReferenceDataServiceImpl(final CommonConfig commonConfig, final FixAdapter fixAdapter,
            final ExternalSessionStateHolder stateHolder, final TTPendingRequests pendingRequests,
            final OptionDao optionDao, final FutureDao futureDao, final SecurityFamilyDao securityFamilyDao) {

        Validate.notNull(commonConfig, "CommonConfig is null");
        Validate.notNull(fixAdapter, "FixAdapter is null");
        Validate.notNull(stateHolder, "ExternalSessionStateHolder is null");
        Validate.notNull(pendingRequests, "IBPendingRequests is null");
        Validate.notNull(optionDao, "OptionDao is null");
        Validate.notNull(futureDao, "FutureDao is null");
        Validate.notNull(securityFamilyDao, "SecurityFamilyDao is null");

        this.commonConfig = commonConfig;
        this.fixAdapter = fixAdapter;
        this.stateHolder = stateHolder;
        this.pendingRequests = pendingRequests;
        this.optionDao = optionDao;
        this.futureDao = futureDao;
        this.securityFamilyDao = securityFamilyDao;
        this.requestIdGenerator = new TTSecurityDefinitionRequestIdGenerator();
        this.requestFactory = new TTSecurityDefinitionRequestFactory();
    }

    @Override
    public void init() {

        this.fixAdapter.openSession(this.stateHolder.getName());
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void retrieve(final long securityFamilyId) {

        SecurityFamily securityFamily = this.securityFamilyDao.get(securityFamilyId);
        if (securityFamily == null) {
            throw new IllegalArgumentException("Unknown security family id: " + securityFamilyId);
        }

        String requestId = this.requestIdGenerator.generateId(securityFamily);
        String securityType;
        if (securityFamily instanceof OptionFamily) {
            securityType = SecurityType.OPTION;
        } else if (securityFamily instanceof FutureFamily) {
            securityType = SecurityType.FUTURE;
        } else {
            securityType = SecurityType.NO_SECURITY_TYPE;
        }

        SecurityDefinitionRequest request = this.requestFactory.create(requestId, securityFamily, securityType);

        PromiseImpl<List<TTSecurityDefVO>> promise = new PromiseImpl<>(null);
        this.pendingRequests.addSecurityDefinitionRequest(requestId, promise);

        this.fixAdapter.sendMessage(request, this.stateHolder.getName());

        List<TTSecurityDefVO> securityDefs = getSecurityDefsBlocking(promise);
        if (securityFamily instanceof OptionFamily) {
            retrieveOptions((OptionFamily) securityFamily, securityDefs);
        } else if (securityFamily instanceof FutureFamily) {
            retrieveFutures((FutureFamily) securityFamily, securityDefs);
        } else {
            throw new ServiceException(
                    "TT does not support retrieval of security definitions for " + securityFamily.getClass());
        }
    }

    private List<TTSecurityDefVO> getSecurityDefsBlocking(final Promise<List<TTSecurityDefVO>> promise) {
        try {
            return promise.get(10, TimeUnit.SECONDS);
        } catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
            throw new ServiceException(ex);
        } catch (TimeoutException e) {
            throw new NoServiceResponseException("No response from TT after 10 seconds");
        } catch (ExecutionException ex) {
            Throwable cause = ex.getCause();
            throw new ServiceException(cause != null ? cause.getMessage() : "Unexpected exception", cause);
        }
    }

    private void retrieveOptions(final OptionFamily securityFamily, final List<TTSecurityDefVO> securityDefs) {

        // get all current options
        List<Option> allOptions = this.optionDao.findBySecurityFamily(securityFamily.getId());
        Map<String, Option> mapByTtid = allOptions.stream().filter(e -> e.getTtid() != null)
                .collect(Collectors.toMap(e -> e.getTtid(), e -> e));
        Map<String, Option> mapBySymbol = allOptions.stream().collect(Collectors.toMap(e -> e.getSymbol(), e -> e));
        for (TTSecurityDefVO securityDef : securityDefs) {

            String type = securityDef.getType();
            if (!type.equalsIgnoreCase("OPT")) {
                throw new ServiceException("Unexpected security definition type for option: " + type);
            }
            String id = securityDef.getId();
            if (!mapByTtid.containsKey(id)) {
                OptionType optionType = securityDef.getOptionType();
                BigDecimal strike = securityDef.getStrikePrice() != null
                        ? PriceUtil.normalizePrice(securityFamily, Broker.TT.name(), securityDef.getStrikePrice())
                        : null;
                LocalDate expiryDate = securityDef.getExpiryDate() != null ? securityDef.getExpiryDate()
                        : securityDef.getMaturityDate();
                String symbol = OptionSymbol.getSymbol(securityFamily, expiryDate, optionType, strike,
                        this.commonConfig.getOptionSymbolPattern());

                if (!mapBySymbol.containsKey(symbol)) {
                    String isin = securityFamily.getIsinRoot() != null
                            ? OptionSymbol.getIsin(securityFamily, expiryDate, optionType, strike)
                            : null;
                    String ric = securityFamily.getRicRoot() != null
                            ? OptionSymbol.getRic(securityFamily, expiryDate, optionType, strike)
                            : null;
                    String desc = securityDef.getDescription();

                    Option option = Option.Factory.newInstance();
                    option.setDescription(desc);
                    option.setSymbol(symbol);
                    option.setIsin(isin);
                    option.setRic(ric);
                    option.setTtid(id);
                    option.setOptionType(optionType);
                    option.setStrike(strike);
                    option.setExpiration(DateTimeLegacy.toLocalDate(expiryDate));
                    option.setSecurityFamily(securityFamily);
                    option.setUnderlying(securityFamily.getUnderlying());

                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("Creating option based on TT definition: {} {} {} {}", securityDef.getSymbol(),
                                securityDef.getOptionType(), securityDef.getMaturityDate(),
                                securityDef.getStrikePrice());
                    }
                    this.optionDao.save(option);
                } else {
                    Option option = mapBySymbol.get(symbol);
                    option.setTtid(id);

                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("Updating option based on TT definition: {}", securityDef.getSymbol());
                    }
                }
            }
        }
    }

    private void retrieveFutures(final FutureFamily securityFamily, final List<TTSecurityDefVO> securityDefs) {

        // get all current futures
        List<Future> allFutures = this.futureDao.findBySecurityFamily(securityFamily.getId());
        Map<String, Future> mapByTtid = allFutures.stream().filter(e -> e.getTtid() != null)
                .collect(Collectors.toMap(e -> e.getTtid(), e -> e));
        Map<String, Future> mapBySymbol = allFutures.stream().collect(Collectors.toMap(e -> e.getSymbol(), e -> e));

        for (TTSecurityDefVO securityDef : securityDefs) {

            String type = securityDef.getType();
            if (!type.equalsIgnoreCase("FUT")) {
                throw new ServiceException("Unexpected security definition type for future: " + type);
            }
            String id = securityDef.getId();
            if (!mapByTtid.containsKey(id)) {

                LocalDate maturityDate = securityDef.getMaturityDate();
                LocalDate expiration = maturityDate.withDayOfMonth(1);

                // IPE e-Brent has to be handled as a special case as it happens to have multiple contracts
                // with the same expiration month
                String symbol;
                if ("ICE_IPE".equalsIgnoreCase(securityDef.getExchange()) && securityDef.getAltSymbol() != null) {
                    String altSymbol = securityDef.getAltSymbol();
                    if (altSymbol.startsWith("Q") || altSymbol.startsWith("Cal")) {
                        continue;
                    } else {
                        try {
                            TemporalAccessor parsed = ICE_IPE_SYMBOL.parse(altSymbol);
                            int year = parsed.get(ChronoField.YEAR);
                            int month = parsed.get(ChronoField.MONTH_OF_YEAR);
                            expiration = LocalDate.of(year, month, 1);
                            symbol = FutureSymbol.getSymbol(securityFamily, expiration,
                                    this.commonConfig.getFutureSymbolPattern());
                        } catch (DateTimeParseException ex) {
                            throw new ServiceException(
                                    "Unable to parse IPE e-Brent expiration month / year: " + altSymbol, ex);
                        }
                    }
                } else {
                    symbol = FutureSymbol.getSymbol(securityFamily, maturityDate,
                            this.commonConfig.getFutureSymbolPattern());
                }
                if (!mapBySymbol.containsKey(symbol)) {
                    Future future = Future.Factory.newInstance();
                    String isin = securityFamily.getIsinRoot() != null
                            ? FutureSymbol.getIsin(securityFamily, maturityDate)
                            : null;
                    String ric = securityFamily.getRicRoot() != null
                            ? FutureSymbol.getRic(securityFamily, maturityDate)
                            : null;

                    future.setSymbol(symbol);
                    future.setIsin(isin);
                    future.setRic(ric);
                    future.setTtid(id);
                    future.setExpiration(DateTimeLegacy.toLocalDate(expiration));
                    future.setMonthYear(DateTimePatterns.MONTH_YEAR.format(maturityDate));
                    future.setSecurityFamily(securityFamily);
                    future.setUnderlying(securityFamily.getUnderlying());

                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("Creating future based on TT definition: {} {}", securityDef.getSymbol(),
                                securityDef.getMaturityDate());
                    }
                    this.futureDao.save(future);
                } else {
                    Future future = mapBySymbol.get(symbol);
                    future.setTtid(id);

                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("Updating future based on TT definition: {} {}", securityDef.getSymbol(),
                                securityDef.getMaturityDate());
                    }
                }
            }
        }
    }

    @Override
    public void retrieveStocks(final long securityFamilyId, final String symbol) {
        throw new ServiceException("TT does not support retrieval of stock definitions");
    }

}