package Merlin.Mar2012;

import java.util.*;
import com.dukascopy.api.*;
import com.dukascopy.api.IIndicators.AppliedPrice;
import com.dukascopy.api.IEngine.OrderCommand;
import java.util.HashMap;
import java.util.Map; 
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
import java.awt.Color;
import javax.swing.SwingConstants;
import com.dukascopy.api.drawings.IOhlcChartObject;

import com.dukascopy.api.IIndicators.MaType;
import com.dukascopy.api.indicators.IIndicator;
import com.dukascopy.api.drawings.IHorizontalLineChartObject;
import java.math.BigDecimal;
import java.math.RoundingMode;

public class AL_MACDSTOCH_v5_remote implements IStrategy {

    private IEngine engine;
    private IConsole console;
    private IHistory history;
    private IIndicators indicators;
    private int counter = 0;
    private IOrder order;
    public Instrument instrument = Instrument.EURUSD;
    public Period selectedPeriod = Period.ONE_HOUR;
    public OfferSide offerSide = OfferSide.BID;
    public double slippage = 0;
    public int amountRiskPercent = 50;//2;
//  Manage amount
    private double equity = 0;
    private double leverage = 0;
    public double amount = 4.5;

    public AppliedPrice appliedPrice = AppliedPrice.CLOSE;     
    public int takeProfitPips = 0;
    public int stopLossPips = 50;
       
    public int fastMACDPeriod = 12;
    public int slowMACDPeriod = 26;
    public int signalMACDPeriod = 9;
    public double macdSell = 0.001;
    public double macdBuy = -0.001;
    
    public int fastKPeriod = 5;
    public int slowKPeriod = 3;
    public MaType slowKMaType =  MaType.SMMA;
    public int slowDPeriod = 5;
    public MaType slowDMaType = MaType.SMMA;
    public int stochBuy = 20;
    public int stochSell = 80;

    public double trailingPips = 5;
    public double trailingStopATRMult = 3;
    public int atrPeriod = 12;

    private IContext context;
    
    long lastTickTime = 0;
    DecimalFormat df = new DecimalFormat("0.00000");
         
    OrderCommand macdSign = null;
    OrderCommand stochSign = null;
   
    @SuppressWarnings("serial")
    private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") {

        {
            setTimeZone(TimeZone.getTimeZone("GMT"));
        }
    };

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

        //Subscribe to an instrument EUR/USD
            Set subscribedInstruments = new HashSet();
            subscribedInstruments.add(Instrument.EURUSD);
            context.setSubscribedInstruments(subscribedInstruments);

        double lastPrice = history.getLastTick(instrument).getBid();
        lastTickTime = history.getLastTick(instrument).getTime();

    }

    @Override
    public void onBar(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException {
        if (period != this.selectedPeriod || instrument != this.instrument) {
            return;
        }
        
    }

    public void onTick(Instrument instrument, ITick tick) throws JFException {
        if (instrument != this.instrument) {
        if(isActive(order)) {
        }
            return;
        }
 
        if(!isActive(order)){
            order = null;
        }        

        // MACD SIGNAL
        
        double[] macd0 = indicators.macd(instrument, selectedPeriod, offerSide, appliedPrice, fastMACDPeriod, slowMACDPeriod, signalMACDPeriod, 0);
        double[] macd1 = indicators.macd(instrument, selectedPeriod, offerSide, appliedPrice, fastMACDPeriod, slowMACDPeriod, signalMACDPeriod, 1);

        if(macd0[0] < macdBuy || macd0[0] > macdSell)   {
         if(macd0[0] > macd0[1] && macd1[0] > macd1[1]) {
             macdSign = OrderCommand.BUY;
         }
         if (macd0[0] < macd0[1] && macd1[0] < macd1[1]) {
            macdSign = OrderCommand.SELL;
         }
        }        
        
        // STOCH SIGNAL  
        int K = 0;
        int D = 1;        
        double[] stoch0 = indicators.stoch(instrument, selectedPeriod, offerSide, fastKPeriod, slowKPeriod, slowKMaType, slowDPeriod, slowDMaType, 0 );
        double[] stoch1 = indicators.stoch(instrument, selectedPeriod, offerSide, fastKPeriod, slowKPeriod, slowKMaType, slowDPeriod, slowDMaType, 1 );
        
        if(stoch0[K] > stoch0[D] && stoch1[K] <= stoch1[D]) {
            stochSign = OrderCommand.BUY;
            
        } else if(stoch0[K] < stoch0[D] && stoch1[K] >= stoch1[D]) {
            stochSign = OrderCommand.SELL;
        }
        
        // PLACE ORDER        
        if(stochSign == OrderCommand.BUY  &&  macdSign == OrderCommand.BUY) {
            if(order != null) {
                if(!order.isLong()) {
                    order.close();
                    order = submitOrder(OrderCommand.BUY);
                }
            } else {
                order = submitOrder(OrderCommand.BUY);
            }
            
        } else if(stochSign == OrderCommand.SELL && macdSign == OrderCommand.SELL) {
            if(order != null) {
                if(order.isLong()) {
                    order.close();
                    order = submitOrder(OrderCommand.SELL);
                }
            } else {
                order = submitOrder(OrderCommand.SELL);
            }
        }
            double atr = indicators.atr(instrument, selectedPeriod, offerSide, atrPeriod, 1);
            double stopLossPips = atr * trailingStopATRMult / instrument.getPipValue();
            updateTrailingStopLoss(tick, order, trailingPips, getRoundedPips(stopLossPips));            
    }

    public void onMessage(IMessage message) throws JFException {
    }

    public void onAccount(IAccount account) throws JFException {

//     Manage Amount of Order
        equity = account.getEquity();
        leverage = account.getLeverage();
        
        double risk = ((double)amountRiskPercent) / 100;
        double amount = equity * risk * leverage / 1000000;
    }

    public void onStop() throws JFException {
    }

    private IOrder submitOrder(OrderCommand orderCmd) throws JFException {
 
        double stopLossPrice = 0.0, takeProfitPrice = 0.0;
        ITick tick = history.getLastTick(instrument);
         
        // Calculating order price, stop loss and take profit prices
        if (orderCmd == OrderCommand.BUY) {
            if (stopLossPips > 0) {
                stopLossPrice = tick.getBid() - getPipPrice(stopLossPips);
            }
            if (takeProfitPips > 0) {
                takeProfitPrice =tick.getBid() + getPipPrice(takeProfitPips);
            }
        } else {
            if (stopLossPips > 0) {
                stopLossPrice = tick.getAsk() + getPipPrice(stopLossPips);
            }
            if (takeProfitPips > 0) {
                takeProfitPrice = tick.getAsk() - getPipPrice(takeProfitPips);
            }
        }
 
//     Manage Amount of Order
       double risk = ((double)amountRiskPercent) / 100;
       double amount = equity * risk * leverage / 1000000;

//     Submit of Order
        return engine.submitOrder(getLabel(instrument), instrument, orderCmd, amount, 0, slippage, stopLossPrice, takeProfitPrice);
    }

    private void closeOrder(IOrder order) throws JFException {
        if (order == null) {
            return;
        }
        if (order.getState() != IOrder.State.CLOSED && order.getState() != IOrder.State.CREATED && order.getState() != IOrder.State.CANCELED) {
            order.close();
            order = null;
        }
    }
    
     private boolean isActive(IOrder order) throws JFException {
        if (order != null && order.getState() != IOrder.State.CLOSED && order.getState() != IOrder.State.CREATED && order.getState() != IOrder.State.CANCELED) {
            return true;
        }
        return false;
    }

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

    public boolean updateTrailingStopLoss(ITick tick, IOrder pOrder, double pTriggerPips, double pStopLossPips) throws JFException {
        boolean triggered = false;
        if (pTriggerPips > 0 && pStopLossPips > 0
                && pOrder != null && pOrder.getState() == IOrder.State.FILLED) {
            double newStop;
            double openPrice = pOrder.getOpenPrice();
            double currentStopLoss = pOrder.getStopLossPrice();
 
            // (START) trailing stop loss is activated when price is higher than oper price + trailingTrigger pips
            // (TRAILING STOP) if price moves further up (for BUY order), stop loss is updated to stopLossPips
 
            if (pOrder.isLong()) { // long side order                
                if (tick.getBid() > currentStopLoss + getPipPrice(pStopLossPips)
                        && tick.getBid() > openPrice + getPipPrice(pTriggerPips)) {
                    // trailing stop loss
                    newStop = tick.getBid() - getPipPrice(pStopLossPips);
                    newStop = (new BigDecimal(newStop)).setScale(instrument.getPipScale(), BigDecimal.ROUND_HALF_UP).doubleValue();
 
                    if (currentStopLoss != newStop) {
                        pOrder.setStopLossPrice(newStop);
                        triggered = true;
                    }
                }
 
            } else { // short side order                
                if (tick.getAsk() < currentStopLoss - getPipPrice(pStopLossPips)
                        && tick.getAsk() < openPrice - getPipPrice(pTriggerPips)) {
                    // trailing stop loss
                    newStop = tick.getAsk() + getPipPrice(pStopLossPips);
                    newStop = (new BigDecimal(newStop)).setScale(instrument.getPipScale(), BigDecimal.ROUND_HALF_UP).doubleValue();
 
                    if (currentStopLoss != newStop) {
                        pOrder.setStopLossPrice(newStop);
                        triggered = true;
                    }
                }
            }
        }
        return triggered;
    }
    private double getRoundedPrice(double price) {
        BigDecimal bd = new BigDecimal(price);
        bd = bd.setScale(instrument.getPipScale() + 1, RoundingMode.HALF_UP);
        return bd.doubleValue();
    }
 
    private double getRoundedPips(double pips) {
        BigDecimal bd = new BigDecimal(pips);
        bd = bd.setScale(1, RoundingMode.HALF_UP);
        return bd.doubleValue();
    }

    private String getLabel(Instrument instrument) {
        String label = instrument.name();
        label = label.toLowerCase();
        label = label + "AMS_v5r_";
        label = label + (counter++);
        return label;
    }

    private void print(Object... o) {
        for (Object ob : o) {
            //console.getOut().print(ob + "  ");
            if (ob instanceof double[]) {
                print((double[]) ob);
            } else if (ob instanceof double[]) {
                print((double[][]) ob);
            } else if (ob instanceof Long) {
                print(dateToStr((Long) ob));
            } else {
                print(ob);
            }
            print(" ");
        }
        console.getOut().println();
    }

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

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

    private void print(double[] arr) {
        println(arrayToString(arr));
    }

    private void print(double[][] arr) {
        println(arrayToString(arr));
    }

    private void printIndicatorInfos(IIndicator ind) {
        for (int i = 0; i < ind.getIndicatorInfo().getNumberOfInputs(); i++) {
            println(ind.getIndicatorInfo().getName() + " Input " + ind.getInputParameterInfo(i).getName() + " " + ind.getInputParameterInfo(i).getType());
        }
        for (int i = 0; i < ind.getIndicatorInfo().getNumberOfOptionalInputs(); i++) {
            println(ind.getIndicatorInfo().getName() + " Opt Input " + ind.getOptInputParameterInfo(i).getName() + " " + ind.getOptInputParameterInfo(i).getType());
        }
        for (int i = 0; i < ind.getIndicatorInfo().getNumberOfOutputs(); i++) {
            println(ind.getIndicatorInfo().getName() + " Output " + ind.getOutputParameterInfo(i).getName() + " " + ind.getOutputParameterInfo(i).getType());
        }
        console.getOut().println();
    }

    public static String arrayToString(double[] arr) {
        String str = "";
        for (int r = 0; r < arr.length; r++) {
            str += "[" + r + "] " + (new DecimalFormat("#.#######")).format(arr[r]) + "; ";
        }
        return str;
    }

    public static String arrayToString(double[][] arr) {
        String str = "";
        if (arr == null) {
            return "null";
        }
        for (int r = 0; r < arr.length; r++) {
            for (int c = 0; c < arr[r].length; c++) {
                str += "[" + r + "][" + c + "] " + (new DecimalFormat("#.#######")).format(arr[r][c]);
            }
            str += "; ";
        }
        return str;
    }

    public String toDecimalToStr(double d) {
        return (new DecimalFormat("#.#######")).format(d);
    }

    public String dateToStr(Long time) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") {

            {
                setTimeZone(TimeZone.getTimeZone("GMT"));
            }
        };
        return sdf.format(time);
    }
}

