import com.dukascopy.api.*;
import com.dukascopy.api.IEngine.OrderCommand;
import com.dukascopy.api.IIndicators.AppliedPrice;
import java.util.List;
import com.dukascopy.api.util.*;

public class SMASimpleStrategy implements IStrategy {
        private IEngine engine;
        private IHistory history;
        private IConsole console;
        private IIndicators indicators;
        private int counter = 0;
        
        enum Location {
        ABOVE,
        BELOW
    }
    
    private Location priceLocation;
    private double priceLevel;

        @Configurable("Instrument")
        public Instrument instrument = Instrument.EURUSD;
        @Configurable("Period")
        public Period selectedPeriod = Period.FIFTEEN_MINS;
        @Configurable("SMA filter")
        public Filter indicatorFilter = Filter.NO_FILTER;
        @Configurable("Amount")
        public double amount = 0.02;
        @Configurable("Stop loss")
        public int stopLossPips = 10;
        @Configurable("Take profit")
        public int takeProfitPips = 90;

        public void onStart(IContext context) throws JFException {
                this.engine = context.getEngine();
                this.history = context.getHistory();
                this.console = context.getConsole(); 
                this.indicators = context.getIndicators();
        }

        public void onAccount(IAccount account) throws JFException {
        }

        public void onMessage(IMessage message) throws JFException {
        }

        public void onStop() throws JFException {
                //close all orders
                for (IOrder order : engine.getOrders()) {
                        engine.getOrder(order.getLabel()).close();
                }
        }

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

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

                if (!instrument.equals(Instrument.EURUSD) || !period.equals(Period.FIFTEEN_MINS))
                        return;

                IBar prevBar = history.getBar(instrument, selectedPeriod, OfferSide.BID, 1);

                int smaTimePeriod = 50;
                int candlesBefore = 2;
                int candlesAfter = 0;

                double sma = indicators.sma(instrument, selectedPeriod, OfferSide.BID, AppliedPrice.CLOSE, smaTimePeriod, indicatorFilter,
                                candlesBefore, prevBar.getTime(), candlesAfter)[0];

                if (prevBar.getOpen() < sma && prevBar.getClose() > sma) {
                        submitOrder(OrderCommand.BUY);
                }

                if (prevBar.getOpen() > sma && prevBar.getClose() < sma) {
                        submitOrder(OrderCommand.SELL);
                }
                
                ITick lastTick = history.getLastTick(instrument);
        
        for(IOrder order : engine.getOrders(instrument)){
            int breachCount = 0;
            int fallCount = 0;
            
            //choose as price level the order open price + 25 pips
            
            priceLevel = order.getOpenPrice() + instrument.getPipValue() * 25;
            
            List<ITick> ticks = history.getTicks(instrument, order.getFillTime(), lastTick.getTime());
            
            if(ticks == null || ticks.size() == 0){
                console.getErr().println("There are no ticks between order fill time and the last tick");
                continue;
            }
            
            priceLocation = ticks.get(0).getBid() >= priceLevel 
                    ? Location.ABOVE     //the oldest tick price is above the target price
                    : Location.BELOW;
            ticks.remove(0); //not to iterate over the already considered tick
            
            for(ITick tick : ticks){
                
                if(order != null) {
             if(priceLocation == Location.BELOW && tick.getBid() > priceLevel){
                    priceLocation = Location.ABOVE;
                    breachCount++;
                 if(order.isLong() && breachCount == 1) {
                     closeOrder(order);
                     order = null;
                 }
            }
        }

        if(order != null) {
            if(priceLocation == Location.ABOVE && tick.getBid() < priceLevel){
                    priceLocation = Location.BELOW;
                    fallCount++;
                if(!order.isLong() && fallCount == 1) {
                     closeOrder(order);
                     order = null;
                }
            }
        }
    }
            console.getOut().println(String.format(
                "For order %s since its fill at %s the price level of %.5f was breached %s and fallen under %s times.", 
                order.getLabel(), DateUtils.format(order.getFillTime()), priceLevel, breachCount, fallCount
            ));
        }            
        }

        private IOrder submitOrder(OrderCommand orderCmd) throws JFException {

                double stopLossPrice, takeProfitPrice;

                if (orderCmd == OrderCommand.BUY) {
                        stopLossPrice = history.getLastTick(this.instrument).getBid() - getPipPrice(this.stopLossPips);
                        takeProfitPrice = history.getLastTick(this.instrument).getBid() + getPipPrice(this.takeProfitPips);
                } else {
                        stopLossPrice = history.getLastTick(this.instrument).getAsk() + getPipPrice(this.stopLossPips);
                        takeProfitPrice = history.getLastTick(this.instrument).getAsk() - getPipPrice(this.takeProfitPips);
                }

                return engine.submitOrder(getLabel(instrument), this.instrument, orderCmd, this.amount, 0, 20, stopLossPrice, takeProfitPrice);
        }
        
        private void closeOrder(IOrder order) throws JFException {
        if (order != null && isActive(order)) {
            order.close();
        }
    }
    
    private boolean isActive(IOrder order) throws JFException {
        if (order != null && order.getState() != IOrder.State.CLOSED && order.getState() != IOrder.State.CANCELED) {
            return true;
        }
        return false;
    }
        
        protected String getLabel(Instrument instrument) {
                String label = instrument.name();
                label = label + (counter++);
                label = label.toUpperCase();
                return label;
        }

        private double getPipPrice(int pips) {
                return pips * this.instrument.getPipValue();
        }
}