package com.jbooktrader.strategy;

import com.jbooktrader.indicator.velocity.*;
import com.jbooktrader.platform.indicator.*;
import com.jbooktrader.platform.model.*;
import com.jbooktrader.platform.optimizer.*;
import com.jbooktrader.platform.util.*;
import com.jbooktrader.strategy.base.*;

import java.util.*;

/**
 *
 */
public class SimilarityFinder extends StrategyES {
    private static final int MIN_NEIGHBORS = 100;
    private static final int LOOK_AHEAD_PERIOD = 6000;

    private final LinkedList<Long> balanceVelocityHistory;
    private final LinkedList<Double> priceHistory;
    private final Map<Long, Forecast> forecasts;

    // Strategy parameters names
    private static final String FAST_PERIOD = "FastPeriod";
    private static final String SLOW_PERIOD = "SlowPeriod";
    private static final String ENTRY = "Entry";

    // Technical indicators
    private final Indicator balanceVelocityInd;

    private double entry;

    public SimilarityFinder(StrategyParams optimizationParams) throws JBookTraderException {
        super(optimizationParams);

        balanceVelocityHistory = new LinkedList<Long>();
        priceHistory = new LinkedList<Double>();
        forecasts = new HashMap<Long, Forecast>();

        entry = getParam(ENTRY);
        balanceVelocityInd = new BalanceVelocity(getParam(FAST_PERIOD), getParam(SLOW_PERIOD));
        addIndicator(balanceVelocityInd);
    }

    @Override
    public void setParams() {
        addParam(FAST_PERIOD, 1, 130, 10, 32);
        addParam(SLOW_PERIOD, 200, 1200, 10, 715);
        addParam(ENTRY, 1, 10, 1, 3);
    }

    /**
     * Framework invokes this method when a new snapshot of the limit order book is taken
     * and the technical indicators are recalculated. This is where the strategy itself
     * (i.e., its entry and exit conditions) should be defined.
     */
    @Override
    public void onBookSnapshot() {
        long balanceVelocity = Math.round(balanceVelocityInd.getValue());
        double price = getMarketBook().getSnapshot().getPrice();
        balanceVelocityHistory.add(balanceVelocity);
        priceHistory.add(price);

        if (priceHistory.size() > LOOK_AHEAD_PERIOD) {
            long pastBalanceVelocity = balanceVelocityHistory.removeFirst();
            double pastPrice = priceHistory.removeFirst();

            Forecast forecast = forecasts.get(pastBalanceVelocity);
            if (forecast == null) {
                forecast = new Forecast();
                forecasts.put(pastBalanceVelocity, forecast);
            }
            forecast.update(price - pastPrice);

            Forecast currentForecast = forecasts.get(balanceVelocity);
            if (currentForecast != null && currentForecast.getNeighbors() >= MIN_NEIGHBORS) {
                double forecastValue = currentForecast.getValue();
                if (forecastValue >= entry) {
                    setPosition(1);
                } else if (forecastValue <= -entry) {
                    setPosition(-1);
                }
            }
        }
    }
}
