package jforex.strategies;
 
import com.dukascopy.api.Configurable;
import com.dukascopy.api.IAccount;
import com.dukascopy.api.IBar;
import com.dukascopy.api.IChart;
import com.dukascopy.api.IConsole;
import com.dukascopy.api.IContext;
import com.dukascopy.api.IEngine.OrderCommand;
import com.dukascopy.api.IEngine;
import com.dukascopy.api.IMessage;
import com.dukascopy.api.IOrder;
import com.dukascopy.api.IStrategy;
import com.dukascopy.api.ITick;
import com.dukascopy.api.Instrument;
import com.dukascopy.api.JFException;
import com.dukascopy.api.Period;
import com.dukascopy.api.drawings.ISignalDownChartObject;
import com.dukascopy.api.drawings.ISignalUpChartObject;
import java.math.BigDecimal;
import java.math.RoundingMode;
 
public class TrailingStop implements IStrategy {
 
    private IEngine engine;
    private IConsole console;
    private IContext context;
    ///@Configurable("Lock-in Pips for Breakeven (SLBE)")                            //==== Quito estas 3 variables porque tienen que ver con el breakeven más que con el trailing stop
    ///public int lockPip = 3;
    ///@Configurable("Stoploss Break Even Trigger (SLBET)")
    ///public int triggerPips = 80;
    ///@Configurable("Move stop to breakeven?")
    ///public boolean moveBE = true;
    @Configurable("Trailing stop loss?")
    public boolean doTrailingSL = true;
    @Configurable("Trailing stop loss trigger pips")
    public double trailingSLTrig = 1;
    @Configurable("Trailing stop loss pips")
    public double trailingSL = 5;
 
    public void onStart(IContext context) throws JFException {
        this.engine = context.getEngine();
        this.console = context.getConsole();
        this.context = context;
    }
 
    public void onAccount(IAccount account) throws JFException {
    }
 
    public void onMessage(IMessage message) throws JFException {
        console.getOut().println(message);
    }
 
    public void onStop() throws JFException {
    }
 
    public void onTick(Instrument instrument, ITick tick) throws JFException {
 
        for (IOrder order : engine.getOrders(instrument)) {       //=== Cada vez que se recibe un tick se mira si hay alguna orden de ese par, y por cada orden encontrada de él se gestiona su stop
            if (order.getState() == IOrder.State.FILLED) {
                boolean isLong;
                double open, newStop;
                String label = order.getLabel();
                IChart chart;
 
                open = order.getOpenPrice();
                
//===== Esta parte la quito porque es el breakeven
/*                if (order.isLong()) { // long side order
                    if (moveBE && tick.getBid() > (open + toPrice(instrument, triggerPips))) {
                        // make it breakeven trade + lock in a few pips
                        newStop = open + toPrice(instrument, lockPip);
                        order.setStopLossPrice(newStop);
                        console.getOut().println(label + ": Moved stop to breakeven");
 
                        chart = this.context.getChart(instrument);
                        drawUp(label + "_BE_UP", tick.getTime(), newStop, chart);
                    }
                } else { // short side order
                    // Move to breakeven
                    if (moveBE && tick.getAsk() < (open - toPrice(instrument, triggerPips))) { // diff is negative
                        // make it breakeven trade + lock in a few pips
                        newStop = open - toPrice(instrument, lockPip);
                        order.setStopLossPrice(newStop);
                        console.getOut().println(label + ": Moved stop to breakeven");
 
                        chart = this.context.getChart(instrument);
                        drawDown(label + "_BE_DOWN", tick.getTime(), newStop, chart);
                    }
                }
*/ 
//======
                if (doTrailingSL) {                                                               //===  Se puede hacer que gestione o no el stop marcando/desmarcando este parámetro al inicio...
                    newStop = updateTrailingStopLoss(tick, order, trailingSLTrig, trailingSL);    //<=== Aqui calcula y pone el nuevo stop loss de la orden
                    if(newStop > 0) {                                                             //<=== Esto de aqui solo sirve para dibujar, se podría quitar 
                        if (order.isLong()) {
                            chart = this.context.getChart(instrument);
                            drawUp(label + "_TS_UP", tick.getTime(), newStop, chart);
                        } else {
                            chart = this.context.getChart(instrument);
                            drawDown(label + "_TS_DOWN", tick.getTime(), newStop, chart);
                        }
                    }                                                                              //<==== hasta aqui lo de dibujar...
                }
            }
        }
    }
     
    ISignalUpChartObject upArrow = null;                                                            //<====Esto de aqui (y hasta la siguiente marca) se podría quitar si no se quiere dibujar
    ISignalDownChartObject downArrow = null;
     
    private void drawUp(String label, long time, double newStop, IChart chart) {
        if(chart != null) {
            if(upArrow == null) {
                upArrow = chart.getChartObjectFactory().createSignalUp(label, time, newStop);
                chart.addToMainChart(upArrow);
            } else {
                upArrow.move(time, newStop);
            }
        }
    }
    private void drawDown(String label, long time, double newStop, IChart chart) {
        if(chart != null) {
            if(downArrow == null) {
                downArrow = chart.getChartObjectFactory().createSignalDown(label, time, newStop);
                chart.addToMainChart(downArrow);
            } else {
                downArrow.move(time, newStop);
            }
        }
    }                                                                                             //<====Hasta aqui lo que sobra si no se quieren dibujar las flechas
 
    private double toPrice(Instrument instr, int pips) {
        return instr.getPipValue() * pips;
    }
 
    public void onBar(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException {
    }
 
    // sets stop loss to "tick.getAsk - pStopLossPips" if price is higher than "pOrder.getOpenPrice() + pTriggerPips"
    public double updateTrailingStopLoss(ITick tick, IOrder pOrder, double pTriggerPips, double pStopLossPips) throws JFException {       //<==== AQUI ES DONDE SE CALCULA Y PONE EL NUEVO STOP
 
        if (pStopLossPips > 0 && pOrder != null && pOrder.getState() == IOrder.State.FILLED) {
 
            Instrument instr = pOrder.getInstrument();
 
            double newStop;
            double openPrice = pOrder.getOpenPrice();
            double currentStopLoss = pOrder.getStopLossPrice();
 
            // (START) <span class="posthilit">trailing</span> stop loss is activated when price is higher than oper price + trailingTrigger pips
            // (<span class="posthilit">TRAILING</span> STOP) if price moves further up (for BUY order), stop loss is updated to stopLossPips
             
            if (pOrder.isLong()) { // long side order                
                if ((currentStopLoss == 0.0 || tick.getBid() > currentStopLoss + getPipPrice(pStopLossPips, instr))
                        && tick.getBid() > openPrice + getPipPrice(pTriggerPips, instr)) {
                    // <span class="posthilit">trailing</span> stop loss
                    newStop = tick.getBid() - getPipPrice(pStopLossPips, instr);
                    newStop = getRoundedPrice(newStop, instr);
 
                    if (currentStopLoss != newStop) {
                        pOrder.setStopLossPrice(newStop);
                        return newStop;
                    }
                }
 
            } else { // short side order            
                if ((currentStopLoss == 0.0 || tick.getAsk() < currentStopLoss - getPipPrice(pStopLossPips, instr))
                        && tick.getAsk() < openPrice - getPipPrice(pTriggerPips, instr)) {
                     
                    // <span class="posthilit">trailing</span> stop loss
                    newStop = tick.getAsk() + getPipPrice(pStopLossPips, instr);
                    newStop = getRoundedPrice(newStop, instr);
 
                    if (currentStopLoss != newStop) {
                        pOrder.setStopLossPrice(newStop);
                        return newStop;
                    }
                }
            }
        }
        return 0.0;
    }
 
    private double getRoundedPrice(double price, Instrument instr) {
        BigDecimal bd = new BigDecimal(price);
        bd = bd.setScale(instr.getPipScale() + 1, RoundingMode.HALF_UP);
        return bd.doubleValue();
    }
 
    private double getRoundedPipCount(double pips) {
        BigDecimal bd = new BigDecimal(pips);
        bd = bd.setScale(1, RoundingMode.HALF_UP);
        return bd.doubleValue();
    }
 
    private double getPipPrice(double pips, Instrument instr) {
        return pips * instr.getPipValue();
    }
}