package jforex;

import java.util.*;
import java.awt.*;
import java.util.List;
import java.util.Set;

import com.dukascopy.api.*;
import com.dukascopy.api.indicators.*;
import com.dukascopy.api.IIndicators.AppliedPrice;
import com.dukascopy.api.indicators.OutputParameterInfo.*;
import com.dukascopy.api.feed.*;
import com.dukascopy.api.feed.util.*;
import java.util.TimeZone;
import java.text.SimpleDateFormat;
import java.text.DateFormat;
import java.util.Date;
import java.text.ParseException;


@CustomIndicators("SSL.jfx")


public class TestStrategyMultiInstr_v1 implements IStrategy, IFeedListener {
    private IEngine engine;
    private IConsole console;
    private IHistory history;
    private IContext context;
    private IIndicators indicators;
    private IUserInterface userInterface;
    private IOrder order;
    private IAccount account;
    private IDataService dataService;
    
    private double[][] outputs = new double[1][];
    
    IIndicator SSLIndicator;
    IIndicator T3Indicator;
    IIndicator KAMAIndicator;
    IIndicator ATRIndicator;
    IIndicator AROONIndicator;
    IIndicator REGSLOPEIndicator;
    
    private int uniqueOrderCounter = 1;
    
    //Indicator Settings
    @Configurable("SSL time period")
    public int sslTimePeriod = 12;
    
    @Configurable("ATR time period")
    public int atrTimePeriod = 14;
    
    @Configurable("Risk")
    public int riskEquity = 1;

    int sslSignal = 0;


    //private ITimedData curDailyBar;
    private IFeedDescriptor myFeedDescriptor;
    private IFeedDescriptor feed;
    private HashSet<Instrument> instSet;
    private HashSet<FeedDescriptor> feedSet;
    private double equity;
    
    
    long offlineStart;
    long offlineEnd;
    
    String indName;
    
    
    
    private Period myPeriod = Period.createCustomPeriod(Unit.Day, 1, JFTimeZone.EET);
    public String timeZone = "CET";
    
    

    
    public void onStart(IContext context) throws JFException {
        this.engine = context.getEngine();
        this.console = context.getConsole();
        this.history = context.getHistory();
        this.context = context;
        this.indicators = context.getIndicators();
        this.userInterface = context.getUserInterface();
        this.dataService = context.getDataService();
        
        String indPath = getClass().getAnnotation(CustomIndicators.class).value(); 
            //for multiple indicators split the path with File.pathSeparator 
        IIndicator indicator = indicators.getIndicatorByPath(indPath);
        if(indicator == null){
            console.getErr().println("Indicator by path "+indPath+" not registered!");
            context.stop();
        }
        indName = indicator.getIndicatorInfo().getName();
        console.getErr().println("Indicator path "+indPath+" & name "+ indName);
        
        account = context.getAccount();

        
        
        //subscribe to Instruments
        instSet = new HashSet<Instrument>();
        instSet.add(Instrument.GBPUSD);
        //instSet.add(Instrument.EURUSD);
        //instSet.add(Instrument.AUDUSD);
        //instSet.add(Instrument.USDJPY);
        //instSet.add(Instrument.CHFJPY);
        //instSet.add(Instrument.USDCAD);
        //instSet.add(Instrument.NZDUSD);
        instSet.add(Instrument.CADCHF);
        context.setSubscribedInstruments(instSet);
        
        
        //subscribe to Feed
        feedSet = new HashSet<FeedDescriptor>();
        for(Instrument instr : instSet) {
            feedSet.add(new TimePeriodAggregationFeedDescriptor(instr, myPeriod, OfferSide.BID, Filter.ALL_FLATS));
        }
        for(FeedDescriptor feed : feedSet) {
            context.subscribeToFeed(feed, this);
        }
        
        //get chart
        IChart chart = context.getChart(Instrument.EURUSD);
        
        console.getOut().println("subscribed instruments " + context.getSubscribedInstruments());
    }
    

    public void onAccount(IAccount account) throws JFException {
    }

    public void onMessage(IMessage message) throws JFException {
    }

    public void onStop() throws JFException {
    }

    public void onTick(Instrument instrument, ITick tick) throws JFException {
    }
    
    public void onBar(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException {
        //check instrument & if period = 15mins
        if (!instSet.contains(instrument) || !period.equals(Period.FIFTEEN_MINS)) {
            return;
        }

        //create Feed for instrument
        for(FeedDescriptor feed : feedSet) {
            if(feed.getInstrument() == instrument) {
                myFeedDescriptor = feed;
            }
        }
        
        //build daily candle in 15min chunks
        ITimedData curDailyBar = history.getFeedData(myFeedDescriptor, 0);


        //check if current 15min period is the last before daily candle close
        long cur15MBarTime = bidBar.getTime() + period.getInterval();
        
        long nextDailyBarTime = curDailyBar.getTime() + myPeriod.getInterval();
        
        
        if (cur15MBarTime != nextDailyBarTime - period.getInterval()) {
            return;
            
        }
        
        
        //code below is executed at 15min before daily close

        try {
            double[] atr = atrPlatformCalc(bidBar.getTime(), myFeedDescriptor);
            
            double[] ssl = sslCalc(bidBar.getTime(), myFeedDescriptor);

   
            int shiftact = 0;
            IBar actBar = history.getBar(instrument, myPeriod, OfferSide.BID, shiftact);
            double actClose = actBar.getClose();

            
            int shiftprev = 1;
            IBar prevBar = history.getBar(instrument, myPeriod, OfferSide.BID, shiftprev);
            double prevClose = prevBar.getClose();

            //calculate ATR in Pips
            int multiplier = 10000;
            if (instrument.getName().contains("JPY") ) {
                multiplier = 100;
            }
            
            double atrPips = atr[0] * multiplier;

            //calculate SL & TP values from ATR
            double SL = Double.parseDouble(String.format(Locale.ENGLISH, "%.1f", atrPips * 1.5));
            double TP = Double.parseDouble(String.format(Locale.ENGLISH, "%.1f", atrPips * 1));

            //print calculated values
            console.getOut().println("--------------------------------------");
            console.getOut().println("Instrument: " + instrument);
            console.getOut().println("feed time: " + toStr(cur15MBarTime));
            console.getOut().println("ATR: " + atr[0]);
            console.getOut().println("ssl[1]: " + ssl[1]);
            console.getOut().println("ssl[0]: " + ssl[0]);
            console.getOut().println("prevClose: " + prevClose);
            console.getOut().println("actClose: " + actClose);
            console.getOut().println("--------------------------------------");

            
            //check if SSL gives signal
            if (prevClose < ssl[0] && actClose > ssl[1]) {
                sslSignal = 1;                                      //Price crossed above SSL => buy signal
            } else if (prevClose > ssl[0] && actClose < ssl[1]) {
                sslSignal = -1;                                      //Price crossed below SSL => sell signal
            } else {
                sslSignal = 0;
            }
            //console.getOut().println("Instrument: " + instrument);
            //console.getOut().println("SSL signal: " + sslSignal);
            
            
            //create order signals
            IEngine.OrderCommand myCommand = null;
            
            if(sslSignal == 1) {
                console.getOut().println("BUY!!!" + instrument);
                myCommand = IEngine.OrderCommand.BUY;
            } else if(sslSignal == -1) {
                console.getOut().println("SELL!!!" + instrument);
                myCommand = IEngine.OrderCommand.SELL;
            } else {
                myCommand = null;
                return;
            }
            
            
            //calculate SL & TP prices
            double lastTickBid = history.getLastTick(instrument).getBid();
            double lastTickAsk = history.getLastTick(instrument).getAsk();
            
            double stopLossValueForLong = instrument.getPipValue() * SL;
            double takeProfitValueForShort = instrument.getPipValue() * TP;
            
            double stopLossPrice = myCommand.isLong() ? (lastTickBid - stopLossValueForLong) : (lastTickAsk + stopLossValueForLong);
            double takeProfitPrice = myCommand.isLong() ? (lastTickBid + takeProfitValueForShort) : (lastTickAsk - takeProfitValueForShort);
            
            
            
            //calculate order size
            equity = account.getEquity();
            double amountAtRisk = equity / 100 * riskEquity;
            
            double pipValue = instrument.getPipValue();
            
            //if secondary Curr == account currency => get exchange rate of isntrument
            
            //if secondary Curr != account currency => get instrument with secondary/account currency
            //if account curr == secondary => get exchange rate
            //if account curr == primary   => get 1 / exchange rate

            
            Instrument exchangeRateCurr = Instrument.USDCHF;
            double exchangeRate = 0;
            boolean invert = false;
            if (instrument.getSecondaryCurrency() == account.getCurrency()) {
                exchangeRate = myCommand.isLong() ? lastTickAsk : lastTickBid;
            } else if (instrument.getSecondaryCurrency() != account.getCurrency()) {
                switch(instrument.getSecondaryCurrency().getSymbol()) {
                    case "USD":
                        exchangeRateCurr = Instrument.USDCHF;
                        break;
                    case "EUR":
                        exchangeRateCurr = Instrument.EURCHF;
                        break;
                    case "GBP":
                        exchangeRateCurr = Instrument.GBPCHF;
                        break;
                    case "CAD":
                        exchangeRateCurr = Instrument.CADCHF;
                        break;
                    case "AUD":
                        exchangeRateCurr = Instrument.AUDCHF;
                        break;
                    case "NZD":
                        exchangeRateCurr = Instrument.NZDCHF;
                        break;
                    case "JPY":
                        exchangeRateCurr = Instrument.CHFJPY;
                        invert = true;
                        break;
                }
                exchangeRate = myCommand.isLong() ? history.getLastTick(exchangeRateCurr).getAsk() : history.getLastTick(exchangeRateCurr).getBid();
                if (invert) {
                    exchangeRate = 1 / exchangeRate;
                }
            }

  
            
            //calc order size
            double orderSize = amountAtRisk / (SL * pipValue * exchangeRate * 1000000);  // order size in millions

            
            //generate order
            String instrumentlabel = instrument.getName().replace("/", "");
            IOrder newOrder = engine.submitOrder("Order" + instrumentlabel + uniqueOrderCounter++, instrument, myCommand, orderSize, 0, 1, stopLossPrice, takeProfitPrice);
            //newOrder.waitForUpdate(2000);
            
            console.getOut().println("Orders: " + engine.getOrders());
            console.getOut().println("Order Label: " + newOrder.getLabel());
            console.getOut().println("Order ID: " + newOrder.getId());

        } catch (JFException e) {
            console.getErr().println(e);
        }
    }
    
    public void onFeedData(IFeedDescriptor feedDescriptor, ITimedData feedData) {
    }
    
    
    //HELPER FUNCTIONS
    
    public String toStr(long time) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") {
            {
            setTimeZone(TimeZone.getTimeZone(timeZone));
            }
        };
        return sdf.format(time);
    }
    
    
    
    //INDICATOR CALCULATION
    
    public double[] atrPlatformCalc(long timeFrom, IFeedDescriptor myFeedDescriptor) throws JFException {
        Object[] atrFeed = indicators.calculateIndicator(
            myFeedDescriptor, 
            new OfferSide[] { OfferSide.BID }, 
            "ATR",
            new IIndicators.AppliedPrice[] { IIndicators.AppliedPrice.CLOSE }, 
            new Object[] { atrTimePeriod }, 
            1, 
            timeFrom,
            0);
        double[] atr = (double[]) atrFeed[0];
        return atr;
    }
    
    public double[] sslCalc(long timeFrom, IFeedDescriptor myFeedDescriptor) throws JFException {
        Object[] sslFeed = indicators.calculateIndicator(
            myFeedDescriptor,
            new OfferSide[] {OfferSide.BID},
            indName,
            new IIndicators.AppliedPrice[] { IIndicators.AppliedPrice.CLOSE }, 
            new Object[] {sslTimePeriod},
            2,
            timeFrom,
            0);
        double[] ssl = (double[]) sslFeed[0];
        return ssl;
    }
}

    
