package jforex.feed.test;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;

import com.dukascopy.api.*;
import com.dukascopy.api.IIndicators.AppliedPrice;
import com.dukascopy.api.feed.*;
import com.dukascopy.api.feed.util.*;
import com.dukascopy.api.indicators.IIndicator;
import com.dukascopy.api.DataType;



/**
 * The example strategy shows how to work with an arbitrary feed:
 * - subscribe and print thelatest completed feed data
 * - retrieve history by feed descriptor
 * - calculate an indicator by feed descriptor
 * - open a chart of the feed
 * 
 */
public class FeedEmaMacdMinMax implements IStrategy {

    private IConsole console;
    private IIndicators indicators;

    @Configurable("Feed data count for indicator calculation")
    public int dataCount = 10;
    @Configurable("Add to chart?")
    public boolean addToChart = true;
    @Configurable("")
    public int minMaxTimePeriod = 5;
    @Configurable("")
    public int emaTimePeriod = 12;
    @Configurable("")
    public int macdSignalPeriod = 9;
    @Configurable("")
    public int macdSlowPeriod = 26;
    @Configurable("")
    public int macdFastPeriod = 12;

    @Configurable("feed type")
    public MyFeeds feedDataType = MyFeeds.RENKOS_1;
    @Configurable("open chart")
    public boolean openChart = false;
    
    private static int MAX = 0;
    private static int MIN = 1;

    private static int EMA_LINE = 0;

    private static int MACD = 0;
    private static int MACD_SIGNAL = 1;
    private static int MACD_HIST = 2;
    
    private static int timePeriod = 14;    
    private static double nbDevDn = 2;
    private static double nbDevUp = 2;
    

    enum MyFeeds {
        RANGE_BARS_2(new RangeBarFeedDescriptor(Instrument.EURUSD, PriceRange.TWO_PIPS, OfferSide.ASK)),
        RANGE_BARS_10(new RangeBarFeedDescriptor(Instrument.EURUSD, PriceRange.valueOf(10), OfferSide.ASK)),
        RENKOS_1(new RenkoFeedDescriptor(Instrument.EURUSD, PriceRange.ONE_PIP, OfferSide.ASK)),
        RENKOS_10(new RenkoFeedDescriptor(Instrument.EURUSD, PriceRange.valueOf(10), OfferSide.ASK)),
        TICK_BARS_20(new TickBarFeedDescriptor(Instrument.EURUSD, TickBarSize.valueOf(20), OfferSide.ASK)),
        PF_2_2(new PointAndFigureFeedDescriptor(Instrument.EURUSD, PriceRange.TWO_PIPS, ReversalAmount.TWO, OfferSide.ASK)),
        CANDLESTICKS_10_SEC(new TimePeriodAggregationFeedDescriptor(Instrument.EURUSD, Period.TEN_SECS, OfferSide.ASK, Filter.NO_FILTER)),
        CANDLESTICKS_1_MIN(new TimePeriodAggregationFeedDescriptor(Instrument.EURUSD, Period.ONE_MIN, OfferSide.ASK, Filter.NO_FILTER));
        //TICKS(new TicksFeedDescriptor(Instrument.EURUSD)), disabled till tick bar feed support from IFeedListener   

        private final IFeedDescriptor feedDescriptor;

        MyFeeds(IFeedDescriptor feedDescriptor) {
            this.feedDescriptor = feedDescriptor;
        }

        public IFeedDescriptor getFeedDescriptor() {
            return feedDescriptor;
        }
    }

    @Override
    public void onStart(IContext context) throws JFException {

        console = context.getConsole();
        indicators = context.getIndicators();

        IFeedDescriptor feedDescriptor = feedDataType.getFeedDescriptor();
        context.setSubscribedInstruments(new HashSet<Instrument>(Arrays.asList(new Instrument[] { feedDescriptor.getInstrument() })), true);

        if (openChart) {
            context.openChart(feedDescriptor);
        }

        print("subscribe to feed=" + feedDescriptor);
        context.subscribeToFeed(feedDescriptor, new IFeedListener() {
            @Override
            public void onFeedData(IFeedDescriptor feedDescriptor, ITimedData feedData) {
                console.getInfo().println("completed " + feedData + " of feed: " + feedDescriptor);
                try {
                    
                    //calculate MINMAX by shift     
                    Object[] minMaxFeed = indicators.calculateIndicator(feedDescriptor, new OfferSide[] { OfferSide.BID }, "MINMAX",
                        new AppliedPrice[] { AppliedPrice.CLOSE }, new Object[] { minMaxTimePeriod }, 1);
                    double max = (Double) minMaxFeed[MAX], min = (Double) minMaxFeed[MIN];
                    print("Previous %s max=%.5f min=%.5f", feedDescriptor.getDataType(), max, min);
                    
                    //calculate EMA by shift  
                    Object[] emaFeedShift = indicators.calculateIndicator(feedDescriptor, new OfferSide[] { OfferSide.BID }, "EMA",
                            new AppliedPrice[] { AppliedPrice.CLOSE }, new Object[] { emaTimePeriod }, 1);
                    double emaPrev = (Double) emaFeedShift[EMA_LINE];
                    print("Previous %s ema=%.5f", feedDescriptor.getDataType(), emaPrev);
                    
                    // calculate BBBands
                    Object[] bbandsUniversal = indicators.calculateIndicator(feedDescriptor, new  OfferSide[] {OfferSide.BID}, "BBANDS",
                                new IIndicators.AppliedPrice[] {IIndicators.AppliedPrice.CLOSE},
                                new Object[]{timePeriod, nbDevUp, nbDevDn, MaType.EMA.ordinal()}, 1);
                    
                    

                    // calculate EMA for the last 10 feed data
                    Object[] emaFeed = indicators.calculateIndicator(feedDescriptor, new OfferSide[] { OfferSide.BID }, "EMA",
                            new AppliedPrice[] { AppliedPrice.CLOSE }, new Object[] { emaTimePeriod }, dataCount, feedData.getTime(), 0);
                    double[] ema = (double[]) emaFeed[EMA_LINE]; // ema has just 1 output
                    print("ema output line for the last %s %s: %s", dataCount, feedDescriptor.getDataType(), arrayToString(ema));

                    // calculate MACD for the last 10 feed data
                    Object[] macdFeed = indicators.calculateIndicator(feedDescriptor, new OfferSide[] { OfferSide.BID }, "MACD",
                            new AppliedPrice[] { AppliedPrice.CLOSE }, new Object[] { macdFastPeriod, macdSlowPeriod, macdSignalPeriod },
                            dataCount, feedData.getTime(), 0);
                    double[][] macd = { (double[]) macdFeed[MACD], (double[]) macdFeed[MACD_SIGNAL], (double[]) macdFeed[MACD_HIST] };

                    IIndicator macdIndicator = indicators.getIndicator("MACD");
                    for (int i = 0; i < macdIndicator.getIndicatorInfo().getNumberOfOutputs(); i++) {
                        String outputName = macdIndicator.getOutputParameterInfo(i).getName();
                        print("macd output %s for the last %s %s: %s", outputName, dataCount, feedDescriptor.getDataType(), arrayToString(macd[i]));
                    }
                } catch (JFException e) {
                    console.getErr().println(e);
                    e.printStackTrace();
                }
            }
        });

    }

    private void print(Object o) {
        console.getOut().println(o);
    }

    private void print(String format, Object... args) {
        print(String.format(format, args));
    }
    
    private static String arrayToString(double[] arr) {
        String str = "";
        for (int r = 0; r < arr.length; r++) {
            str += String.format("[%s] %.5f; ", r, arr[r]);
        }
        return str;
    }

    @Override
    public void onTick(Instrument instrument, ITick tick) throws JFException {
    }

    @Override
    public void onBar(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException {
    }

    @Override
    public void onMessage(IMessage message) throws JFException {
    }

    @Override
    public void onAccount(IAccount account) throws JFException {
    }

    @Override
    public void onStop() throws JFException {
    }

}
