ch.algotrader.service.PortfolioServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for ch.algotrader.service.PortfolioServiceImpl.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.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeSet;

import org.apache.commons.collections15.keyvalue.MultiKey;
import org.apache.commons.lang.Validate;
import org.apache.commons.lang.time.DateUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.SessionFactory;
import org.springframework.scheduling.support.CronSequenceGenerator;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import ch.algotrader.config.CommonConfig;
import ch.algotrader.config.CoreConfig;
import ch.algotrader.dao.GenericDao;
import ch.algotrader.dao.HibernateInitializer;
import ch.algotrader.dao.NamedParam;
import ch.algotrader.dao.PositionDao;
import ch.algotrader.dao.TransactionDao;
import ch.algotrader.dao.marketData.TickDao;
import ch.algotrader.dao.security.ForexDao;
import ch.algotrader.dao.strategy.CashBalanceDao;
import ch.algotrader.dao.strategy.PortfolioValueDao;
import ch.algotrader.dao.strategy.PortfolioValueVOProducer;
import ch.algotrader.dao.strategy.StrategyDao;
import ch.algotrader.entity.Position;
import ch.algotrader.entity.Transaction;
import ch.algotrader.entity.marketData.MarketDataEventVO;
import ch.algotrader.entity.marketData.Tick;
import ch.algotrader.entity.security.Forex;
import ch.algotrader.entity.security.Security;
import ch.algotrader.entity.strategy.CashBalance;
import ch.algotrader.entity.strategy.PortfolioValue;
import ch.algotrader.entity.strategy.PortfolioValueI;
import ch.algotrader.entity.strategy.Strategy;
import ch.algotrader.entity.strategy.StrategyImpl;
import ch.algotrader.enumeration.Currency;
import ch.algotrader.enumeration.QueryType;
import ch.algotrader.esper.EngineManager;
import ch.algotrader.report.PortfolioReport;
import ch.algotrader.util.RoundUtil;
import ch.algotrader.util.collection.DoubleMap;
import ch.algotrader.vo.BalanceVO;
import ch.algotrader.vo.CurrencyAmountVO;
import ch.algotrader.vo.FxExposureVO;
import ch.algotrader.vo.PortfolioValueVO;

/**
 * @author <a href="mailto:aflury@algotrader.ch">Andy Flury</a>
 */
@Transactional(propagation = Propagation.SUPPORTS)
public class PortfolioServiceImpl implements PortfolioService {

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

    private final CommonConfig commonConfig;

    private final CoreConfig coreConfig;

    private final SessionFactory sessionFactory;

    private final MarketDataCacheService marketDataCacheService;

    private final GenericDao genericDao;

    private final StrategyDao strategyDao;

    private final TransactionDao transactionDao;

    private final PositionDao positionDao;

    private final CashBalanceDao cashBalanceDao;

    private final PortfolioValueDao portfolioValueDao;

    private final TickDao tickDao;

    private final ForexDao forexDao;

    private Comparator<Currency> currencyComparator = new Comparator<Currency>() {

        @Override
        public int compare(Currency currency1, Currency currency2) {
            if (currency1 == currency2) {
                return 0;
            } else if (currency1 == PortfolioServiceImpl.this.commonConfig.getPortfolioBaseCurrency()) {
                return Integer.MIN_VALUE;
            } else if (currency2 == PortfolioServiceImpl.this.commonConfig.getPortfolioBaseCurrency()) {
                return Integer.MAX_VALUE;
            } else {
                return currency1.compareTo(currency2);
            }
        }
    };

    private final EngineManager engineManager;

    private volatile PortfolioReport portfolioReport;

    public PortfolioServiceImpl(final CommonConfig commonConfig, final CoreConfig coreConfig,
            final SessionFactory sessionFactory, final MarketDataCacheService marketDataCacheService,
            final GenericDao genericDao, final StrategyDao strategyDao, final TransactionDao transactionDao,
            final PositionDao positionDao, final CashBalanceDao cashBalanceDao,
            final PortfolioValueDao portfolioValueDao, final TickDao tickDao, final ForexDao forexDao,
            final EngineManager engineManager) {

        Validate.notNull(commonConfig, "CommonConfig is null");
        Validate.notNull(coreConfig, "CoreConfig is null");
        Validate.notNull(sessionFactory, "SessionFactory is null");
        Validate.notNull(marketDataCacheService, "MarketDataCacheService is null");
        Validate.notNull(genericDao, "GenericDao is null");
        Validate.notNull(strategyDao, "StrategyDao is null");
        Validate.notNull(transactionDao, "TransactionDao is null");
        Validate.notNull(positionDao, "PositionDao is null");
        Validate.notNull(cashBalanceDao, "CashBalanceDao is null");
        Validate.notNull(portfolioValueDao, "PortfolioValueDao is null");
        Validate.notNull(tickDao, "TickDao is null");
        Validate.notNull(forexDao, "ForexDao is null");
        Validate.notNull(engineManager, "EngineManager is null");

        this.commonConfig = commonConfig;
        this.coreConfig = coreConfig;
        this.sessionFactory = sessionFactory;
        this.marketDataCacheService = marketDataCacheService;
        this.genericDao = genericDao;
        this.strategyDao = strategyDao;
        this.transactionDao = transactionDao;
        this.positionDao = positionDao;
        this.cashBalanceDao = cashBalanceDao;
        this.portfolioValueDao = portfolioValueDao;
        this.tickDao = tickDao;
        this.forexDao = forexDao;
        this.engineManager = engineManager;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Collection<Transaction> getTransactionsByFilter(final String filter, final Date date,
            final NamedParam... namedParams) {

        Validate.notEmpty(filter, "Filter is empty");
        Validate.notNull(namedParams, "Named parameters is null");
        Validate.notNull(date, "Date is null");

        // add maxDate
        NamedParam[] copy = new NamedParam[namedParams.length + 1];
        System.arraycopy(namedParams, 0, copy, 0, namedParams.length);
        copy[namedParams.length] = new NamedParam("maxDate", date);

        //@formatter:off
        String query = "from TransactionImpl as t " + "left join fetch t.strategy " + "join fetch t.security "
                + "join fetch t.position " + "join fetch t.account " + "where " + filter + " "
                + "and t.dateTime <= :maxDate";
        //@formatter:on
        return this.genericDao.find(Transaction.class, query, QueryType.HQL, copy);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public List<Position> getOpenPositionsByFilter(final String filter, final Date date,
            final NamedParam... namedParams) {

        Validate.notEmpty(filter, "Filter is empty");
        Validate.notNull(namedParams, "Named parameters is null");
        Validate.notNull(date, "Date is null");

        //@formatter:off
        String queryString = "select new Position(sum(t.quantity), s) " + "from TransactionImpl as t "
                + "join t.security as s " + "where s != null " + "and t.dateTime <= :date " + "and " + filter + " "
                + "group by s.id " + "having sum(t.quantity) != 0 " + "order by s.id";
        //@formatter:on

        // add maxDate
        NamedParam[] copy = new NamedParam[namedParams.length + 1];
        System.arraycopy(namedParams, 0, copy, 0, namedParams.length);
        copy[namedParams.length] = new NamedParam("date", date);

        List<Position> positions = this.genericDao.find(Position.class, queryString, QueryType.HQL, copy);

        positions.stream().forEach((p) -> p.initializeSecurity(HibernateInitializer.INSTANCE));

        return positions;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public BigDecimal getCashBalance() {

        return RoundUtil.getBigDecimal(getCashBalanceDouble());

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public BigDecimal getCashBalance(final String strategyName) {

        Validate.notEmpty(strategyName, "Strategy name is nempty");

        return RoundUtil.getBigDecimal(getCashBalanceDouble(strategyName));

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public BigDecimal getCashBalance(final Date date) {

        Validate.notNull(date, "Date is null");

        return RoundUtil.getBigDecimal(getCashBalanceDouble(date));

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public BigDecimal getCashBalance(final String strategyName, final Date date) {

        Validate.notEmpty(strategyName, "Strategy name is empty");
        Validate.notNull(date, "Date is null");

        return RoundUtil.getBigDecimal(getCashBalanceDouble(strategyName, date));

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public BigDecimal getCashBalance(final String filter, final Date date, final NamedParam... namedParams) {

        Validate.notEmpty(filter, "Filter is empty");
        Validate.notNull(namedParams, "Named parameters is null");
        Validate.notNull(date, "Date is null");

        return RoundUtil.getBigDecimal(getCashBalanceDouble(filter, date, namedParams));

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double getCashBalanceDouble() {

        Collection<CashBalance> cashBalances = this.cashBalanceDao.loadAll();

        List<Position> positions = this.positionDao.findOpenFXPositionsAggregated();

        return getCashBalanceDoubleInternal(cashBalances, positions);

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double getCashBalanceDouble(final String strategyName) {

        Validate.notEmpty(strategyName, "Strategy name is null");

        Collection<CashBalance> cashBalances = this.cashBalanceDao.findCashBalancesByStrategy(strategyName);

        List<Position> positions = this.positionDao.findOpenFXPositionsByStrategy(strategyName);

        return getCashBalanceDoubleInternal(cashBalances, positions);

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double getCashBalanceDouble(final Date date) {

        Validate.notNull(date, "Date is null");

        Collection<Transaction> transactions = this.transactionDao.findByMaxDate(date);

        Collection<Position> openPositions = this.positionDao.findOpenPositionsByMaxDateAggregated(date);

        return getCashBalanceDoubleInternal(transactions, openPositions, date);

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double getCashBalanceDouble(final String strategyName, final Date date) {

        Validate.notEmpty(strategyName, "Strategy name is null");
        Validate.notNull(date, "Date is null");

        Collection<Transaction> transactions = this.transactionDao.findByStrategyAndMaxDate(strategyName, date);

        Collection<Position> openPositions = this.positionDao.findOpenPositionsByStrategyAndMaxDate(strategyName,
                date);

        return getCashBalanceDoubleInternal(transactions, openPositions, date);

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double getCashBalanceDouble(final String filter, final Date date, final NamedParam... namedParams) {

        return getCashBalanceDoubleInternal(getTransactionsByFilter(filter, date, namedParams), new ArrayList<>(),
                date);

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public BigDecimal getMarketValue() {

        return RoundUtil.getBigDecimal(getMarketValueDouble());

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public BigDecimal getMarketValue(final String strategyName) {

        Validate.notEmpty(strategyName, "Strategy name is empty");

        return RoundUtil.getBigDecimal(getMarketValueDouble(strategyName));

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public BigDecimal getMarketValue(final Date date) {

        Validate.notNull(date, "Date is null");

        return RoundUtil.getBigDecimal(getMarketValueDouble(date));

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public BigDecimal getMarketValue(final String strategyName, final Date date) {

        Validate.notEmpty(strategyName, "Strategy name is empty");
        Validate.notNull(date, "Date is null");

        return RoundUtil.getBigDecimal(getMarketValueDouble(strategyName, date));

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public BigDecimal getMarketValue(final String filter, final Date date, final NamedParam... namedParams) {

        Validate.notEmpty(filter, "Filter is empty");
        Validate.notNull(namedParams, "Named parameters is null");
        Validate.notNull(date, "Date is null");

        return RoundUtil.getBigDecimal(getMarketValueDouble(filter, date, namedParams));

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double getMarketValueDouble() {

        Collection<Position> openPositions = this.positionDao.findOpenTradeablePositionsAggregated();

        return getMarketValueDoubleInternal(openPositions);

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double getMarketValueDouble(final String strategyName) {

        Validate.notEmpty(strategyName, "Strategy name is empty");

        List<Position> openPositions = this.positionDao.findOpenTradeablePositionsByStrategy(strategyName);

        return getMarketValueDoubleInternal(openPositions);

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double getMarketValueDouble(final Date date) {

        Validate.notNull(date, "Date is null");

        Collection<Position> openPositions = this.positionDao.findOpenPositionsByMaxDateAggregated(date);

        return getMarketValueDoubleInternal(openPositions, date);

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double getMarketValueDouble(final String strategyName, final Date date) {

        Validate.notEmpty(strategyName, "Strategy name is empty");
        Validate.notNull(date, "Date is null");

        Collection<Position> openPositions = this.positionDao.findOpenPositionsByStrategyAndMaxDate(strategyName,
                date);

        return getMarketValueDoubleInternal(openPositions, date);

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double getMarketValueDouble(final String filter, final Date date, final NamedParam... namedParams) {

        List<Position> openPositions = getOpenPositionsByFilter(filter, date, namedParams);

        return getMarketValueDoubleInternal(openPositions, date);

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public BigDecimal getRealizedPL() {

        return RoundUtil.getBigDecimal(getRealizedPLDouble());

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public BigDecimal getRealizedPL(final String strategyName) {

        Validate.notEmpty(strategyName, "Strategy name is empty");

        return RoundUtil.getBigDecimal(getRealizedPLDouble(strategyName));

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double getRealizedPLDouble() {

        Collection<Position> openPositions = this.positionDao.loadAll();

        return getRealizePLDoubleInternal(openPositions);

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double getRealizedPLDouble(final String strategyName) {

        Validate.notEmpty(strategyName, "Strategy name is empty");

        List<Position> openPositions = this.positionDao.findByStrategy(strategyName);

        return getRealizePLDoubleInternal(openPositions);

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public BigDecimal getUnrealizedPL() {

        return RoundUtil.getBigDecimal(getUnrealizedPLDouble());

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public BigDecimal getUnrealizedPL(final String strategyName) {

        Validate.notEmpty(strategyName, "Strategy name is empty");

        return RoundUtil.getBigDecimal(getUnrealizedPLDouble(strategyName));

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double getUnrealizedPLDouble() {

        Collection<Position> openPositions = this.positionDao.findOpenTradeablePositionsAggregated();

        return getUnrealizePLDoubleInternal(openPositions);

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double getUnrealizedPLDouble(final String strategyName) {

        Validate.notEmpty(strategyName, "Strategy name is empty");

        List<Position> openPositions = this.positionDao.findOpenTradeablePositionsByStrategy(strategyName);

        return getUnrealizePLDoubleInternal(openPositions);

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public BigDecimal getNetLiqValue() {

        return RoundUtil.getBigDecimal(getNetLiqValueDouble());

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public BigDecimal getNetLiqValue(final String strategyName) {

        Validate.notEmpty(strategyName, "Strategy name is empty");

        return RoundUtil.getBigDecimal(getNetLiqValueDouble(strategyName));

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double getNetLiqValueDouble() {

        return getCashBalanceDouble() + getMarketValueDouble();

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double getNetLiqValueDouble(final String strategyName) {

        Validate.notEmpty(strategyName, "Strategy name is empty");

        return getCashBalanceDouble(strategyName) + getMarketValueDouble(strategyName);

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getOpenPositions() {

        Collection<Position> openPositions = this.positionDao.findOpenTradeablePositionsAggregated();

        return openPositions.size();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getOpenPositions(String strategyName) {

        Validate.notEmpty(strategyName, "Strategy name is empty");

        List<Position> openPositions = this.positionDao.findOpenTradeablePositionsByStrategy(strategyName);

        return openPositions.size();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getOpenPositions(Date date) {

        Collection<Position> openPositions = this.positionDao.findOpenPositionsByMaxDateAggregated(date);

        return openPositions.size();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getOpenPositions(String strategyName, Date date) {

        Validate.notEmpty(strategyName, "Strategy name is empty");

        List<Position> openPositions = this.positionDao.findOpenPositionsByStrategyAndMaxDate(strategyName, date);

        return openPositions.size();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double getLeverage() {

        double exposure = 0.0;
        Collection<Position> positions = this.positionDao.findOpenTradeablePositions();
        for (Position position : positions) {
            MarketDataEventVO marketDataEvent = this.marketDataCacheService
                    .getCurrentMarketDataEvent(position.getSecurity().getId());
            MarketDataEventVO underlyingMarketDataEvent = null;
            if (position.getSecurity().getUnderlying() != null) {
                underlyingMarketDataEvent = this.marketDataCacheService
                        .getCurrentMarketDataEvent(position.getSecurity().getUnderlying().getId());
            }
            position.initializeSecurity(HibernateInitializer.INSTANCE);
            exposure += position
                    .getExposure(marketDataEvent, underlyingMarketDataEvent, this.engineManager.getCurrentEPTime())
                    .doubleValue();
        }
        return exposure / getNetLiqValueDouble();

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double getLeverage(final String strategyName) {

        Validate.notEmpty(strategyName, "Strategy name is empty");

        double exposure = 0.0;
        List<Position> positions = this.positionDao.findOpenTradeablePositionsByStrategy(strategyName);
        for (Position position : positions) {
            MarketDataEventVO marketDataEvent = this.marketDataCacheService
                    .getCurrentMarketDataEvent(position.getSecurity().getId());
            MarketDataEventVO underlyingMarketDataEvent = null;
            if (position.getSecurity().getUnderlying() != null) {
                underlyingMarketDataEvent = this.marketDataCacheService
                        .getCurrentMarketDataEvent(position.getSecurity().getUnderlying().getId());
            }
            position.initializeSecurity(HibernateInitializer.INSTANCE);
            exposure += position
                    .getExposure(marketDataEvent, underlyingMarketDataEvent, this.engineManager.getCurrentEPTime())
                    .doubleValue();
        }

        return exposure / getNetLiqValueDouble(strategyName);

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double getPerformance() {

        return getPerformance(StrategyImpl.SERVER);

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public double getPerformance(final String strategyName) {

        Validate.notEmpty(strategyName, "Strategy name is empty");

        Date date = DateUtils.truncate(new Date(), Calendar.MONTH);
        List<PortfolioValueVO> portfolioValues = (List<PortfolioValueVO>) getPortfolioValuesInclPerformanceSinceDate(
                strategyName, date);

        // the performance of the last portfolioValue represents the performance of the entire timeperiod
        if (portfolioValues.size() > 0) {
            return portfolioValues.get(portfolioValues.size() - 1).getPerformance();
        } else {
            return Double.NaN;
        }

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public PortfolioValue getPortfolioValue() {

        return getPortfolioValue(StrategyImpl.SERVER);

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public PortfolioValue getPortfolioValue(final String strategyName) {

        Validate.notEmpty(strategyName, "Strategy name is empty");

        Strategy strategy = this.strategyDao.findByName(strategyName);

        BigDecimal cashBalance;
        BigDecimal marketValue;
        BigDecimal realizedPL;
        BigDecimal unrealizedPL;
        int openPositions;
        if (strategy.isServer()) {
            cashBalance = getCashBalance();
            marketValue = getMarketValue();
            realizedPL = getRealizedPL();
            unrealizedPL = getUnrealizedPL();
            openPositions = getOpenPositions();
        } else {
            cashBalance = getCashBalance(strategyName);
            marketValue = getMarketValue(strategyName);
            realizedPL = getRealizedPL(strategyName);
            unrealizedPL = getUnrealizedPL(strategyName);
            openPositions = getOpenPositions(strategyName);
        }

        PortfolioValue portfolioValue = PortfolioValue.Factory.newInstance();

        portfolioValue.setStrategy(strategy);
        portfolioValue.setDateTime(this.engineManager.getCurrentEPTime());
        portfolioValue.setNetLiqValue(marketValue != null ? cashBalance.add(marketValue) : null);
        portfolioValue.setCashBalance(cashBalance);
        portfolioValue.setMarketValue(marketValue);
        portfolioValue.setRealizedPL(realizedPL);
        portfolioValue.setUnrealizedPL(unrealizedPL);
        portfolioValue.setOpenPositions(openPositions);

        return portfolioValue;

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public PortfolioValue getPortfolioValue(final String strategyName, final Date date) {

        Validate.notEmpty(strategyName, "Strategy name is empty");
        Validate.notNull(date, "Date is null");

        Strategy strategy = this.strategyDao.findByName(strategyName);

        BigDecimal cashBalance;
        BigDecimal marketValue;
        int positions;
        if (strategy.isServer()) {
            cashBalance = getCashBalance(date);
            marketValue = getMarketValue(date);
            positions = getOpenPositions(date);
        } else {
            cashBalance = getCashBalance(strategyName, date);
            marketValue = getMarketValue(strategyName, date);
            positions = getOpenPositions(strategyName, date);
        }

        PortfolioValue portfolioValue = PortfolioValue.Factory.newInstance();

        portfolioValue.setStrategy(strategy);
        portfolioValue.setDateTime(date);
        portfolioValue.setNetLiqValue(marketValue != null ? cashBalance.add(marketValue) : null);
        portfolioValue.setCashBalance(cashBalance);
        portfolioValue.setMarketValue(marketValue);
        portfolioValue.setOpenPositions(positions);

        return portfolioValue;

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Collection<PortfolioValueVO> getPortfolioValuesInclPerformanceSinceDate(final String strategyName,
            final Date date) {

        Validate.notEmpty(strategyName, "Strategy name is empty");
        Validate.notNull(date, "Date is null");

        Collection<PortfolioValueVO> portfolioValues = this.portfolioValueDao.findByStrategyAndMinDate(strategyName,
                date, PortfolioValueVOProducer.INSTANCE);

        // calculate the performance
        double lastNetLiqValue = 0;
        double lastDayNetLiqValue = 0;
        double performance = 1.0;
        double dailyPerformance = 1.0;
        for (PortfolioValueVO portfolioValue : portfolioValues) {

            // for AlgoTrader Server reset performance at the 24:00 based on NetLiqValue of prior day
            if (StrategyImpl.SERVER.equals(strategyName)
                    && DateUtils.getFragmentInHours(portfolioValue.getDateTime(), Calendar.DAY_OF_YEAR) == 0) {
                if (lastDayNetLiqValue != 0) {
                    dailyPerformance = dailyPerformance * (portfolioValue.getNetLiqValue().doubleValue()
                            / (lastDayNetLiqValue + (portfolioValue.getCashFlow() != null
                                    ? portfolioValue.getCashFlow().doubleValue()
                                    : 0)));
                    performance = dailyPerformance;
                    portfolioValue.setPerformance(performance - 1.0);
                }

                lastDayNetLiqValue = portfolioValue.getNetLiqValue().doubleValue();
                lastNetLiqValue = portfolioValue.getNetLiqValue().doubleValue();

            } else {
                if (lastNetLiqValue != 0) {
                    performance = performance * (portfolioValue.getNetLiqValue().doubleValue() / (lastNetLiqValue
                            + (portfolioValue.getCashFlow() != null ? portfolioValue.getCashFlow().doubleValue()
                                    : 0)));
                    portfolioValue.setPerformance(performance - 1.0);
                }

                lastNetLiqValue = portfolioValue.getNetLiqValue().doubleValue();
            }
        }

        return portfolioValues;

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Collection<BalanceVO> getBalances() {

        Collection<CashBalance> cashBalances = this.cashBalanceDao.loadAll();
        Collection<Position> positions = this.positionDao.findOpenTradeablePositionsAggregated();

        return getBalances(cashBalances, positions);

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Collection<BalanceVO> getBalances(final String strategyName) {

        Validate.notEmpty(strategyName, "Strategy name is empty");

        Collection<CashBalance> cashBalances = this.cashBalanceDao.findCashBalancesByStrategy(strategyName);
        Collection<Position> positions = this.positionDao.findOpenTradeablePositionsByStrategy(strategyName);

        return getBalances(cashBalances, positions);

    }

    @Override
    public Collection<FxExposureVO> getFxExposure() {

        List<Position> positions = this.positionDao.findOpenFXPositionsAggregated();
        return getFxExposure(positions);
    }

    @Override
    public Collection<FxExposureVO> getFxExposure(String strategyName) {

        List<Position> positions = this.positionDao.findOpenFXPositionsByStrategy(strategyName);
        return getFxExposure(positions);
    }

    private double getCashBalanceDoubleInternal(Collection<CashBalance> cashBalances, List<Position> positions) {

        // sum of all cashBalances
        double amount = 0.0;
        for (CashBalance cashBalance : cashBalances) {
            double forexRateBase = this.marketDataCacheService.getForexRateBase(cashBalance.getCurrency());
            amount += cashBalance.getAmount().doubleValue() * forexRateBase;
        }

        // sum of all FX positions
        for (Position position : positions) {
            MarketDataEventVO marketDataEvent = this.marketDataCacheService
                    .getCurrentMarketDataEvent(position.getSecurity().getId());
            double forexRateBase = this.marketDataCacheService.getForexRateBase(position.getSecurity());
            amount += position.getMarketValue(marketDataEvent).doubleValue() * forexRateBase;
        }

        return amount;
    }

    private double getCashBalanceDoubleInternal(Collection<Transaction> transactions,
            Collection<Position> openPositions, Date date) {

        // sum of all transactions
        DoubleMap<Currency> map = new DoubleMap<>();
        for (Transaction transaction : transactions) {

            transaction.initializeSecurity(HibernateInitializer.INSTANCE);

            // process all currenyAmounts
            for (CurrencyAmountVO currencyAmount : transaction.getAttributions()) {
                map.increment(currencyAmount.getCurrency(), currencyAmount.getAmount().doubleValue());
            }
        }

        // sum of all FX positions
        for (Position openPosition : openPositions) {

            openPosition.initializeSecurity(HibernateInitializer.INSTANCE);

            Security security = openPosition.getSecurity();
            if (security instanceof Forex) {
                int intervalDays = this.coreConfig.getIntervalDays();
                List<Tick> ticks = this.tickDao.findTicksBySecurityAndMaxDate(1, security.getId(), date,
                        intervalDays);
                if (ticks.isEmpty()) {
                    ticks = this.tickDao.findTicksBySecurityAndMinDate(1, security.getId(), date, intervalDays);
                    if (ticks.isEmpty()) {
                        if (LOGGER.isWarnEnabled()) {
                            LOGGER.warn("no tick available for {} on {}", security, date);
                        }
                        continue;
                    }
                    if (LOGGER.isInfoEnabled()) {
                        LOGGER.info("no prior tick available on {} next tick is {} days later for {}", date,
                                ((ticks.get(0).getDateTime().getTime() - date.getTime()) / 86400000.0), security);
                    }
                }

                double amount = openPosition.getQuantity()
                        * ticks.get(0).getMarketValueDouble(openPosition.getDirection());
                map.increment(security.getSecurityFamily().getCurrency(), amount);
            }
        }

        // convert non baseCurrencies
        double amount = 0.0;
        for (Map.Entry<Currency, Double> entry : map.entrySet()) {
            double fxRate = getRateDoubleByDate(entry.getKey(), date);
            amount += entry.getValue() * fxRate;
        }
        return amount;
    }

    private double getMarketValueDoubleInternal(Collection<Position> openPositions) {

        // sum of all non-FX positions (FX counts as cash)
        double amount = 0.0;
        for (Position openPosition : openPositions) {

            openPosition.initializeSecurity(HibernateInitializer.INSTANCE);

            Security security = openPosition.getSecurity();
            if (!(security instanceof Forex)) {
                MarketDataEventVO marketDataEvent = this.marketDataCacheService
                        .getCurrentMarketDataEvent(security.getId());
                double forexRateBase = this.marketDataCacheService.getForexRateBase(openPosition.getSecurity());
                amount += openPosition.getMarketValue(marketDataEvent).doubleValue() * forexRateBase;
            }
        }
        return amount;
    }

    private double getMarketValueDoubleInternal(Collection<Position> openPositions, Date date) {

        // sum of all non-FX positions (FX counts as cash)
        DoubleMap<Currency> map = new DoubleMap<>();

        for (Position openPosition : openPositions) {

            openPosition.initializeSecurity(HibernateInitializer.INSTANCE);

            Security security = openPosition.getSecurity();
            if (!(security instanceof Forex)) {
                int intervalDays = this.coreConfig.getIntervalDays();
                List<Tick> ticks = this.tickDao.findTicksBySecurityAndMaxDate(1, security.getId(), date,
                        intervalDays);
                if (ticks.isEmpty()) {
                    ticks = this.tickDao.findTicksBySecurityAndMinDate(1, security.getId(), date, intervalDays);
                    if (ticks.isEmpty()) {
                        if (LOGGER.isWarnEnabled()) {
                            LOGGER.warn("no tick available for {} on {}", security, date);
                        }
                        continue;
                    }
                    if (LOGGER.isInfoEnabled()) {
                        LOGGER.info("no prior tick available on {} next tick is {} days later for {}", date,
                                ((ticks.get(0).getDateTime().getTime() - date.getTime()) / 86400000.0), security);
                    }
                }

                double marketValue = openPosition.getQuantity()
                        * ticks.get(0).getMarketValueDouble(openPosition.getDirection())
                        * security.getSecurityFamily().getContractSize();
                map.increment(security.getSecurityFamily().getCurrency(), marketValue);
            }
        }

        double amount = 0.0;
        for (Map.Entry<Currency, Double> entry : map.entrySet()) {
            double fxRate = getRateDoubleByDate(entry.getKey(), date);
            amount += entry.getValue() * fxRate;
        }
        return amount;
    }

    private double getRateDoubleByDate(final Currency ccy, final Date date) {
        Double fxRate = this.forexDao.getRateDoubleByDate(ccy, this.commonConfig.getPortfolioBaseCurrency(), date);
        if (fxRate == null) {
            throw new ForexAvailabilityException("No exchange rate available for " + ccy + "."
                    + this.commonConfig.getPortfolioBaseCurrency() + " for " + date);
        }
        return fxRate;
    }

    private double getUnrealizePLDoubleInternal(Collection<Position> openPositions) {

        // sum of all positions
        double amount = 0.0;
        for (Position openPosition : openPositions) {
            MarketDataEventVO marketDataEvent = this.marketDataCacheService
                    .getCurrentMarketDataEvent(openPosition.getSecurity().getId());
            double forexRateBase = this.marketDataCacheService.getForexRateBase(openPosition.getSecurity());
            amount += openPosition.getUnrealizedPL(marketDataEvent).doubleValue() * forexRateBase;
        }
        return amount;
    }

    private double getRealizePLDoubleInternal(Collection<Position> openPositions) {

        // sum of all positions
        double amount = 0.0;
        for (Position openPosition : openPositions) {
            double forexRateBase = this.marketDataCacheService.getForexRateBase(openPosition.getSecurity());
            amount += openPosition.getRealizedPL().doubleValue() * forexRateBase;
        }
        return amount;
    }

    private List<BalanceVO> getBalances(Collection<CashBalance> cashBalances, Collection<Position> positions) {

        DoubleMap<Currency> cashMap = new DoubleMap<>();
        DoubleMap<Currency> securitiesMap = new DoubleMap<>();
        DoubleMap<Currency> unrealizedPLMap = new DoubleMap<>();

        // sum of all cashBalances
        for (CashBalance cashBalance : cashBalances) {
            Currency currency = cashBalance.getCurrency();
            cashMap.increment(currency, cashBalance.getAmount().doubleValue());
        }

        // sum of all positions
        for (Position position : positions) {

            position.initializeSecurity(HibernateInitializer.INSTANCE);

            MarketDataEventVO marketDataEvent = this.marketDataCacheService
                    .getCurrentMarketDataEvent(position.getSecurity().getId());
            CurrencyAmountVO currencyAmount = position.getAttribution(marketDataEvent);

            if (currencyAmount.getAmount() != null) {
                if (position.isCashPosition()) {
                    cashMap.increment(currencyAmount.getCurrency(), currencyAmount.getAmount().doubleValue());
                } else {
                    securitiesMap.increment(currencyAmount.getCurrency(), currencyAmount.getAmount().doubleValue());
                }
                unrealizedPLMap.increment(position.getSecurity().getSecurityFamily().getCurrency(),
                        position.getUnrealizedPL(marketDataEvent).doubleValue());
            }
        }

        Set<Currency> currencies = new TreeSet<Currency>(this.currencyComparator);
        currencies.addAll(cashMap.keySet());
        currencies.addAll(securitiesMap.keySet());

        List<BalanceVO> balances = new ArrayList<>();
        for (Currency currency : currencies) {

            double cash = cashMap.containsKey(currency) ? cashMap.get(currency) : 0.0;
            double securities = securitiesMap.containsKey(currency) ? securitiesMap.get(currency) : 0.0;
            double netLiqValue = cash + securities;
            double exchangeRate = this.marketDataCacheService.getForexRate(currency,
                    this.commonConfig.getPortfolioBaseCurrency());
            double unrealizedPL = unrealizedPLMap.containsKey(currency) ? unrealizedPLMap.get(currency) : 0.0;
            double cashBase = cash * exchangeRate;
            double securitiesBase = securities * exchangeRate;
            double netLiqValueBase = netLiqValue * exchangeRate;
            double unrealizedPLBase = unrealizedPL * exchangeRate;

            BalanceVO balance = new BalanceVO();
            balance.setCurrency(currency);
            balance.setCash(RoundUtil.getBigDecimal(cash));
            balance.setSecurities(RoundUtil.getBigDecimal(securities));
            balance.setUnrealizedPL(RoundUtil.getBigDecimal(unrealizedPL));
            balance.setNetLiqValue(RoundUtil.getBigDecimal(netLiqValue));
            balance.setCashBase(RoundUtil.getBigDecimal(cashBase));
            balance.setSecuritiesBase(RoundUtil.getBigDecimal(securitiesBase));
            balance.setUnrealizedPLBase(RoundUtil.getBigDecimal(unrealizedPLBase));
            balance.setNetLiqValueBase(RoundUtil.getBigDecimal(netLiqValueBase));
            balance.setExchangeRate(exchangeRate);

            balances.add(balance);
        }
        return balances;
    }

    private Collection<FxExposureVO> getFxExposure(List<Position> positions) {

        DoubleMap<Currency> currencyMap = new DoubleMap<Currency>();

        // sum of all positions
        for (Position position : positions) {

            position.initializeSecurity(HibernateInitializer.INSTANCE);

            MarketDataEventVO marketDataEvent = this.marketDataCacheService
                    .getCurrentMarketDataEvent(position.getSecurity().getId());
            CurrencyAmountVO currencyAmount = position.getAttribution(marketDataEvent);
            if (currencyAmount.getAmount() != null) {
                currencyMap.increment(currencyAmount.getCurrency(), currencyAmount.getAmount().doubleValue());
                currencyMap.increment(position.getSecurity().getSecurityFamily().getCurrency(),
                        -position.getMarketValue(marketDataEvent).doubleValue());
            }
        }

        Set<Currency> currencies = new TreeSet<Currency>(this.currencyComparator);
        currencies.addAll(currencyMap.keySet());

        List<FxExposureVO> exposures = new ArrayList<FxExposureVO>();
        for (Currency currency : currencies) {

            double amount = currencyMap.getDouble(currency);
            double exchangeRate = this.marketDataCacheService.getForexRate(currency,
                    this.commonConfig.getPortfolioBaseCurrency());
            double amountBase = amount * exchangeRate;

            FxExposureVO exposure = new FxExposureVO();
            exposure.setCurrency(currency);
            exposure.setAmount(RoundUtil.getBigDecimal(amount));
            exposure.setAmountBase(RoundUtil.getBigDecimal(amountBase));
            exposure.setExchangeRate(exchangeRate);

            exposures.add(exposure);
        }
        return exposures;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void savePortfolioValue(final Transaction transaction) {

        // do not save PortfolioValue in simulation
        if (this.commonConfig.isSimulation()) {
            return;
        }

        // only process performanceRelevant transactions
        if (transaction.isPerformanceRelevant()) {

            // check if there is an existing portfolio value
            Collection<PortfolioValue> portfolioValues = this.portfolioValueDao
                    .findByStrategyAndMinDate(transaction.getStrategy().getName(), transaction.getDateTime());

            if (portfolioValues.size() > 0) {

                LOGGER.warn("transaction date is in the past, please restore portfolio values");

            } else {

                // create and save the portfolio value
                PortfolioValue portfolioValue = getPortfolioValue(transaction.getStrategy().getName());

                portfolioValue.setCashFlow(transaction.getGrossValue());

                this.portfolioValueDao.save(portfolioValue);
            }
        }

    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void savePortfolioValues() {

        for (Strategy strategy : this.strategyDao.findAutoActivateStrategies()) {

            PortfolioValue portfolioValue = getPortfolioValue(strategy.getName());

            // truncate Date to hour
            portfolioValue.setDateTime(DateUtils.truncate(portfolioValue.getDateTime(), Calendar.HOUR));

            this.portfolioValueDao.save(portfolioValue);
        }

    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void restorePortfolioValues(final Strategy strategy, final Date fromDate, final Date toDate) {

        Validate.notNull(strategy, "Strategy is null");
        Validate.notNull(fromDate, "From date is null");
        Validate.notNull(toDate, "To date is null");

        // delete existing portfolio values;
        List<PortfolioValue> portfolioValues = this.portfolioValueDao.findByStrategyAndMinDate(strategy.getName(),
                fromDate);

        if (portfolioValues.size() > 0) {

            this.portfolioValueDao.deleteAll(portfolioValues);

            // need to flush since new portfoliovalues will be created with same date and strategy
            this.sessionFactory.getCurrentSession().flush();
        }

        // init cron
        CronSequenceGenerator cron = new CronSequenceGenerator("0 0 * * * 1-5", TimeZone.getDefault());

        // group PortfolioValues by strategyId and date
        Map<MultiKey<Long>, PortfolioValue> portfolioValueMap = new HashMap<>();

        // create portfolioValues for all cron time slots
        Date date = cron.next(DateUtils.addHours(fromDate, -1));
        while (date.compareTo(toDate) <= 0) {

            PortfolioValue portfolioValue = getPortfolioValue(strategy.getName(), date);
            if (portfolioValue.getNetLiqValueDouble() == 0) {
                date = cron.next(date);
                continue;
            } else {
                MultiKey<Long> key = new MultiKey<>(strategy.getId(), date.getTime());
                portfolioValueMap.put(key, portfolioValue);

                if (LOGGER.isInfoEnabled()) {
                    LOGGER.info("processed portfolioValue for {} {}", strategy.getName(), date);
                }

                date = cron.next(date);
            }
        }

        // save values for all cashFlows
        List<Transaction> transactions = this.transactionDao.findCashflowsByStrategyAndMinDate(strategy.getName(),
                fromDate);
        for (Transaction transaction : transactions) {

            // only process performanceRelevant transactions
            if (!transaction.isPerformanceRelevant()) {
                continue;
            }

            // do not save before fromDate
            if (transaction.getDateTime().compareTo(fromDate) < 0) {
                continue;
            }

            // if there is an existing PortfolioValue, add the cashFlow
            MultiKey<Long> key = new MultiKey<>(transaction.getStrategy().getId(),
                    transaction.getDateTime().getTime());
            if (portfolioValueMap.containsKey(key)) {
                PortfolioValue portfolioValue = portfolioValueMap.get(key);
                if (portfolioValue.getCashFlow() != null) {
                    portfolioValue.setCashFlow(portfolioValue.getCashFlow().add(transaction.getGrossValue()));
                } else {
                    portfolioValue.setCashFlow(transaction.getGrossValue());
                }
            } else {
                PortfolioValue portfolioValue = getPortfolioValue(transaction.getStrategy().getName(),
                        transaction.getDateTime());
                portfolioValue.setCashFlow(transaction.getGrossValue());
                portfolioValueMap.put(key, portfolioValue);
            }

            if (LOGGER.isInfoEnabled()) {
                LOGGER.info("processed portfolioValue for {} {} cashflow {}", transaction.getStrategy().getName(),
                        transaction.getDateTime(), transaction.getGrossValue());
            }
        }

        // perisist the PortfolioValues
        this.portfolioValueDao.saveAll(portfolioValueMap.values());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void printPortfolioValue(final PortfolioValueI portfolioValue) {

        if (!this.commonConfig.isDisableReports()) {
            synchronized (this) {
                try {
                    if (this.portfolioReport == null) {

                        File reportLocation = this.commonConfig.getReportLocation();
                        File reportFile = new File(reportLocation != null ? reportLocation : new File("."),
                                "PortfolioReport.csv");
                        this.portfolioReport = PortfolioReport
                                .create(this.commonConfig.getSimulationInitialBalance());
                    }
                    this.portfolioReport.write(this.engineManager.getCurrentEPTime(), portfolioValue);
                } catch (IOException ex) {
                    LOGGER.error(ex.getMessage(), ex);
                }
            }
        }
    }

}