package jforex;

import java.util.*;
import java.math.*;
import com.dukascopy.api.*;

public class RizHedge implements IStrategy {
    @Configurable("") public String Comment = "RizHedge";
    @Configurable("") public int Gap1 = 50;
    @Configurable("") public int TP1 = 50;
    @Configurable("") public double LotMultiplier1=2;
    @Configurable("") public int Set1Orders = 4;
    @Configurable("") public int Gap2 = 50;
    @Configurable("") public int TP2 = 50;
    @Configurable("") public double LotMultiplier2=2;
    @Configurable("") public int Set2Orders = 4;    
    @Configurable("") public int Gap3 = 50;
    @Configurable("") public int TP3 = 50;
    @Configurable("") public double LotMultiplier3=2;
    @Configurable("") public int Levels = 15;
    @Configurable("") public double LotSize=0.001;
    @Configurable("") public double LotAdjuster=33;
    @Configurable("") public int MAPeriod=100;
    @Configurable("") public boolean StopTrading=false;
    @Configurable("") public double PerCurrencyMargin=10000;
    
    protected static Map<Currency, Instrument> pairs = new HashMap<Currency, Instrument>();

    static {
       pairs.put(Currency.getInstance("AUD"), Instrument.AUDUSD);
       pairs.put(Currency.getInstance("CAD"), Instrument.USDCAD);
       pairs.put(Currency.getInstance("CHF"), Instrument.USDCHF);
       pairs.put(Currency.getInstance("DKK"), Instrument.USDDKK);
       pairs.put(Currency.getInstance("EUR"), Instrument.EURUSD);
       pairs.put(Currency.getInstance("GBP"), Instrument.GBPUSD);
       pairs.put(Currency.getInstance("HKD"), Instrument.USDHKD);
       pairs.put(Currency.getInstance("JPY"), Instrument.USDJPY);
       pairs.put(Currency.getInstance("MXN"), Instrument.USDMXN);
       pairs.put(Currency.getInstance("NOK"), Instrument.USDNOK);
       pairs.put(Currency.getInstance("NZD"), Instrument.NZDUSD);
       pairs.put(Currency.getInstance("SEK"), Instrument.USDSEK);
       pairs.put(Currency.getInstance("SGD"), Instrument.USDSGD);
       pairs.put(Currency.getInstance("TRY"), Instrument.USDTRY);
    }

    private IEngine engine;
    private IConsole console;
    private IHistory history;
    private IContext context;
    private IIndicators indicators;
    private IUserInterface userInterface;
    
    private int SellOrders,BuyOrders,SellStopOrders,BuyStopOrders,SellLimitOrders,BuyLimitOrders,PendingOrders,LimitOrders,StopOrders,MarketOrders,TotalOrders;
    private double lastActiveTP,totalProfit,closedOrders;
    static private Map<Instrument,Double> maxDD,cycleDD,maxInitLot,maxDDPer,maxLevels,lastTotalProfit;
    static private Map<Instrument,Double> subscribedCurrencies;
    static private Map<String,List<IOrder>> mOrders;
    static private double overAllMaxDD,overAllMaxDDPer;
    private double equity,openEquity,availEquity;
    private Currency accountCurrency;
    private IChart chart;
    private String commentStr;
    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();
        overAllMaxDD=0;
        overAllMaxDDPer=0;
        openEquity = 0;
        maxDD = new HashMap<Instrument,Double>();
        cycleDD = new HashMap<Instrument,Double>();
        maxInitLot=new HashMap<Instrument,Double>();
        maxDDPer=new HashMap<Instrument,Double>();
        maxLevels=new HashMap<Instrument,Double>();
        lastTotalProfit=new HashMap<Instrument,Double>();
        mOrders=new HashMap<String,List<IOrder>>();
        subscribedCurrencies = new HashMap<Instrument,Double>();
        for(Instrument inst:Instrument.values()){
            try{
                if (history.getLastTick(inst)==null) continue;
            }
            catch (Exception e){
                continue;
            }
            subscribedCurrencies.put(inst,0.0);
            maxDD.put(inst,0.0);
            cycleDD.put(inst,0.0);
            maxInitLot.put(inst,0.0);
            maxDDPer.put(inst,0.0);
            maxLevels.put(inst,0.0);
            lastTotalProfit.put(inst,0.0);
            mOrders.put(Comment + "_" + inst,null);
        }
    }
    public void onAccount(IAccount account) throws JFException {
        double totalSum = PerCurrencyMargin * subscribedCurrencies.size();
        availEquity = account.getEquity();
        equity = (availEquity/totalSum)*PerCurrencyMargin;
        accountCurrency = account.getCurrency();
    }

    public void onMessage(IMessage message) throws JFException {
        
    }

    public void onStop() throws JFException {
        for(Instrument inst:subscribedCurrencies.keySet()){
            console.getOut().println(inst.toString() + " : Maximum Draw Down: $" + maxDD.get(inst));
            console.getOut().println(inst.toString() + " : Maximum Draw Down: " + round(maxDDPer.get(inst)*100,2) + " %");
            console.getOut().println(inst.toString() + " : Maximum Initial Lot: " + maxInitLot.get(inst));
            console.getOut().println(inst.toString() + " : Maximum Levels Used: " + maxLevels.get(inst));
        }
        console.getOut().println("Over All Draw Down: $" + overAllMaxDD);
        console.getOut().println("Over All Draw Down: " + round(overAllMaxDDPer*100,2) + "%");
        console.getOut().println("Final Equity: $" + availEquity);
    }

    public void onTick(Instrument instrument, ITick tick) throws JFException {
        List<IOrder> orders = checkOpenOrders(instrument);
        chart = context.getChart(instrument);
        commentStr = "";
        if (MarketOrders==0 && PendingOrders==0){
            //no orders on right so place a buystop and buy limit
            
            if (StopTrading) return; //Stop Trading is on so do not open any more trades.
            openEquity = equity;
            IEngine.OrderCommand openType;
            
            if (isBuySignal(instrument)) {openType=IEngine.OrderCommand.BUY;print ("Buy Signal",instrument);}
            else if (isSellSignal(instrument)) {openType=IEngine.OrderCommand.SELL;print ("Sell Signal",instrument);}
            else {return;}
            
            cycleDD.put(instrument, new Double(0.0)); //reset the cycle drawdown
            
            if (getLotSize()>maxInitLot.get(instrument).doubleValue()) 
                maxInitLot.put(instrument,getLotSize());
            
            IOrder MainOrder = engine.submitOrder(getLabel(instrument)+"1",instrument,openType,getLotSize(),0,0,0,0);
            MainOrder.waitForUpdate(30000);
            print ("Main Order " + MainOrder.getState() + " at: " + MainOrder.getOpenPrice() + " dir: " + MainOrder.getOrderCommand(),instrument);
            
            mOrders.put(Comment + "_" + instrument,null); //reset the orders list.
            
        }
        else{
            if (closedOrders>0){
                //an order has closed this means all existing orders should close
                String labels="";
                double totLots=0;
                for (IOrder ord:orders){
                    IOrder.State ordState = ord.getState();
                    if (ordState!=IOrder.State.CANCELED && ordState!=IOrder.State.CLOSED){
                        try {print ("Closing orders because of cycle close: " + ord.getLabel() + " Lots = " + ord.getRequestedAmount(),instrument);ord.close();} catch (Exception e){
                        }
                        }
                }
                /*if (equity<openEquity){
                    if (MarketOrders==8){
                    console.getOut().println("Cycle Closed in Loss, Levels = " + MarketOrders + ", difference = $" + (equity - openEquity));
                    console.getOut().println("Labels = " + labels);}
                }
                else{
                    if (MarketOrders==8){
                    console.getOut().println("Cycle Closed in Profit, Levels = " + MarketOrders + ", difference = $" + (equity - openEquity));
                    console.getOut().println("Labels = " + labels);
                    }
                }*/
                mOrders.put(Comment + "_" + instrument,null);
            }
            else if (PendingOrders==0 && MarketOrders>0){
                if (MarketOrders==1){
                    IOrder ord = orders.get(0);
                    if (ord.getTakeProfitPrice()==0){
                        double openTP =0;
                        if (ord.getOrderCommand()==IEngine.OrderCommand.BUY){
                            openTP = ord.getOpenPrice() + (TP1*instrument.getPipValue());
                        }
                        if (ord.getOrderCommand()==IEngine.OrderCommand.SELL){
                            openTP = ord.getOpenPrice() - (TP1*instrument.getPipValue());
                        }
                        if (ord.getState() == IOrder.State.FILLED){
                            openTP = roundToHalfPip(openTP,instrument);
            
                            print ("Setting Main Order Take Profit to: " + openTP + " TP in pips is: " + (Math.abs(openTP-ord.getOpenPrice())),instrument);
                            ord.setTakeProfitPrice(openTP);
                            ord.waitForUpdate(30000);
                        }
                        return;
                    }
                }
                int i = TotalOrders +1;
                double Gap,TP,LotMultiplier,limitPrice=0,limitTP=0;

                if (i<=Set1Orders){Gap = Gap1;TP = TP1;LotMultiplier = LotMultiplier1;print ("using set 1",instrument);}
                else if (i<=(Set1Orders+Set2Orders)){Gap = Gap2;TP = TP2;LotMultiplier = LotMultiplier2;print ("using set 2",instrument);}
                else{Gap = Gap3;TP = TP3;LotMultiplier = LotMultiplier3;print ("using set 3",instrument);}
                IOrder lastOrder=null;
                //Synchronization of TP might be required
                for(IOrder ord:orders){
                    lastOrder = ord;
                    if(ord.getOrderCommand()==IEngine.OrderCommand.BUY || ord.getOrderCommand()==IEngine.OrderCommand.SELL) {
                        if (ord.getTakeProfitPrice()!=lastActiveTP){
                            print ("Changing Take Profit for Order: " + ord.getLabel() + " to " + lastActiveTP + " tp gap = " + Math.abs(ord.getOpenPrice() - lastActiveTP),instrument);
                            ord.setTakeProfitPrice(lastActiveTP);
                            ord.waitForUpdate(30000);
                        }
                    }
                }                


                IEngine.OrderCommand signal = lastOrder.getOrderCommand();
                IEngine.OrderCommand limitType=null;
                
                if (signal==IEngine.OrderCommand.BUY){
                    limitPrice = lastOrder.getOpenPrice()-(Gap*instrument.getPipValue());
                    limitTP = limitPrice + (TP*instrument.getPipValue());
                    limitType = IEngine.OrderCommand.BUYLIMIT;
                }
                else if (signal==IEngine.OrderCommand.SELL){
                    limitPrice = lastOrder.getOpenPrice()+(Gap*instrument.getPipValue());
                    limitTP = limitPrice - (TP*instrument.getPipValue());
                    limitType = IEngine.OrderCommand.SELLLIMIT;
                }
                double lot = lastOrder.getRequestedAmount() * LotMultiplier;
                //lot = round(lot,4);
                //if (lot<=lastOrder.getRequestedAmount()) lot= round(lot + 0.0005,4);
                //while ((lot%0.0005)>0.0) {lot= round(lot + 0.0001,4);if ((lot%0.0005)==0.0)break;}
                limitPrice = roundToHalfPip(limitPrice,instrument);
                limitTP = roundToHalfPip(limitTP,instrument);

                IOrder pendingOrder = engine.submitOrder(getLabel(instrument)+i,instrument,limitType,lot,limitPrice,10,0,limitTP);                
                pendingOrder.waitForUpdate(30000);
                
                print ("Pending order "+ pendingOrder.getState() + " with lots = " + lot + " price at: " + limitPrice + " TP = " + limitTP + " dir = " + limitType,instrument);
                
                orders.add(pendingOrder);
                mOrders.put(Comment + "_" + instrument,orders);
            }
        }
        if (MarketOrders>maxLevels.get(instrument).doubleValue()){
            maxLevels.put(instrument,new Double(MarketOrders));
        }
        if (totalProfit<maxDD.get(instrument).doubleValue()){
            maxDD.put(instrument, new Double(totalProfit));
        }
        
        if (totalProfit<cycleDD.get(instrument).doubleValue()){
            cycleDD.put(instrument, new Double(totalProfit));
        }
        if (totalProfit<0 && (totalProfit/(equity-totalProfit))<maxDDPer.get(instrument).doubleValue()){
            maxDDPer.put(instrument,new Double(totalProfit/(equity-totalProfit)));
        }

        lastTotalProfit.put(instrument,totalProfit);
        
        double totalLoss = 0;
        for (Instrument inst:subscribedCurrencies.keySet()){
            totalLoss += lastTotalProfit.get(inst);
        }
        if (totalLoss<overAllMaxDD){
            overAllMaxDD = totalLoss;
        }
        if (totalLoss<0 && (totalLoss/(equity-totalLoss)<overAllMaxDDPer)){
            overAllMaxDDPer = (totalLoss/(equity-totalLoss));
        }
        log ("Pips to Target: " + Math.abs((lastActiveTP-tick.getBid())/instrument.getPipValue()));
        log ("Current Cycle Profit: $" + totalProfit);
        log ("Cycle Maximum Draw Down: $" + cycleDD.get(instrument));
        log ("Maximum Absolute Draw Down: $" + maxDD.get(instrument));
        log ("Maximum Lot Size: " + maxInitLot.get(instrument));
        if (chart!=null) chart.comment(commentStr);
    }
    private List<IOrder> checkOpenOrders(Instrument instrument) throws JFException{
        
        SellOrders=0;
        BuyOrders=0;
        SellStopOrders=0;
        BuyStopOrders=0;
        SellLimitOrders=0;
        BuyLimitOrders=0;
        PendingOrders=0;
        LimitOrders=0;
        StopOrders=0;
        MarketOrders=0;
        TotalOrders=0;
        lastActiveTP=0;
        totalProfit=0;
        closedOrders=0;
        
        if (mOrders.get(Comment + "_" + instrument)==null){ //No Orders for this currency and comment exist reload all orders for this comment + instrument
            List<IOrder> ordersAll = engine.getOrders(instrument);
            List<IOrder> orders = new ArrayList();
            for(IOrder ord:ordersAll){
                String label = ord.getLabel();
                if (label.length()<Comment.length()){
                    continue;
                }
                else if (!ord.getLabel().substring(0,Comment.length()).equals(Comment)){
                    continue;
                }
                else{
                    orders.add(ord);
                }
            }
            if (orders.size()>0)
                mOrders.put(Comment + "_" + instrument,orders);
            else
                mOrders.put(Comment + "_" + instrument,null);
        }
        
        List<IOrder> orders = mOrders.get(Comment + "_" + instrument);
        if (orders==null) return (orders);
        if (orders.size()==0) return(orders);
        //Remove orders which don't belong to this Strategy
        for (IOrder ord:orders){
            IEngine.OrderCommand ordCommand = ord.getOrderCommand();
            IOrder.State ordState = ord.getState();
            if (ordState==IOrder.State.CLOSED)
                closedOrders = closedOrders + 1;
            if (ordCommand==IEngine.OrderCommand.BUY){
                BuyOrders = BuyOrders + 1;
                MarketOrders = MarketOrders + 1;
                TotalOrders = TotalOrders + 1;
                lastActiveTP = ord.getTakeProfitPrice();
                totalProfit = totalProfit + calculateProfitLoss(ord);
            }
            else if (ordCommand==IEngine.OrderCommand.SELL){
                SellOrders = SellOrders + 1;
                MarketOrders = MarketOrders + 1;
                TotalOrders = TotalOrders + 1;
                lastActiveTP = ord.getTakeProfitPrice();
                totalProfit = totalProfit + calculateProfitLoss(ord);
            }
            else if (ordCommand==IEngine.OrderCommand.SELLSTOP){
                SellStopOrders = SellStopOrders + 1;
                PendingOrders = PendingOrders + 1;
                StopOrders = StopOrders + 1;
                TotalOrders = TotalOrders + 1;
            }
            else if (ordCommand==IEngine.OrderCommand.BUYSTOP){
                BuyStopOrders = BuyStopOrders + 1;
                PendingOrders = PendingOrders + 1;
                StopOrders = StopOrders + 1;
                TotalOrders = TotalOrders + 1;
            }            
            else if (ordCommand==IEngine.OrderCommand.SELLLIMIT){
                SellLimitOrders = SellLimitOrders + 1;
                PendingOrders = PendingOrders + 1;
                LimitOrders = LimitOrders + 1;
                TotalOrders = TotalOrders + 1;
            }
            else if (ordCommand==IEngine.OrderCommand.BUYLIMIT){
                BuyLimitOrders = BuyLimitOrders + 1;
                PendingOrders = PendingOrders + 1;
                LimitOrders = LimitOrders + 1;
                TotalOrders = TotalOrders + 1;
            }            
        }
        return (orders);        
    }
    private boolean isBuySignal(Instrument instrument) throws JFException{
        double ma15_0 = indicators.ma(instrument,Period.FIFTEEN_MINS,OfferSide.BID,IIndicators.AppliedPrice.CLOSE,MAPeriod,IIndicators.MaType.SMA,0);
        double ma15_1 = indicators.ma(instrument,Period.FIFTEEN_MINS,OfferSide.BID,IIndicators.AppliedPrice.CLOSE,MAPeriod,IIndicators.MaType.SMA,1);
        
        double ma60_0 = indicators.ma(instrument,Period.ONE_HOUR,OfferSide.BID,IIndicators.AppliedPrice.CLOSE,MAPeriod,IIndicators.MaType.SMA,0);
        double ma60_1 = indicators.ma(instrument,Period.ONE_HOUR,OfferSide.BID,IIndicators.AppliedPrice.CLOSE,MAPeriod,IIndicators.MaType.SMA,1);
        
        double ma1440_0 = indicators.ma(instrument,Period.DAILY,OfferSide.BID,IIndicators.AppliedPrice.CLOSE,MAPeriod,IIndicators.MaType.SMA,0);
        double ma1440_1 = indicators.ma(instrument,Period.DAILY,OfferSide.BID,IIndicators.AppliedPrice.CLOSE,MAPeriod,IIndicators.MaType.SMA,1);
        
        IBar dailyBar = history.getBar(instrument,Period.DAILY,OfferSide.BID,0);
        
        ITick lastTick = history.getLastTick(instrument);
        double barSize = dailyBar.getHigh()-dailyBar.getLow();
        double barQuarter = barSize/4;
        double upperLevel = dailyBar.getHigh()-barQuarter;
        double lowerLevel = dailyBar.getLow()-barQuarter;
        
        //if (lastTick.getBid()<=lowerLevel || lastTick.getBid()>=upperLevel) {log ("Price is beyond acceptable daily range");return(false);}
        //if (ma15_0<=ma15_1) return (false);
        
        //if (ma60_0<=ma60_1) return (false);
        
        if (ma1440_0<=ma1440_1) return (false);
        
        return(true);
    }
    private boolean isSellSignal(Instrument instrument) throws JFException{
        double ma15_0 = indicators.ma(instrument,Period.FIFTEEN_MINS,OfferSide.BID,IIndicators.AppliedPrice.CLOSE,MAPeriod,IIndicators.MaType.SMA,0);
        double ma15_1 = indicators.ma(instrument,Period.FIFTEEN_MINS,OfferSide.BID,IIndicators.AppliedPrice.CLOSE,MAPeriod,IIndicators.MaType.SMA,1);
        
        double ma60_0 = indicators.ma(instrument,Period.ONE_HOUR,OfferSide.BID,IIndicators.AppliedPrice.CLOSE,MAPeriod,IIndicators.MaType.SMA,0);
        double ma60_1 = indicators.ma(instrument,Period.ONE_HOUR,OfferSide.BID,IIndicators.AppliedPrice.CLOSE,MAPeriod,IIndicators.MaType.SMA,1);
        
        double ma1440_0 = indicators.ma(instrument,Period.DAILY,OfferSide.BID,IIndicators.AppliedPrice.CLOSE,MAPeriod,IIndicators.MaType.SMA,0);
        double ma1440_1 = indicators.ma(instrument,Period.DAILY,OfferSide.BID,IIndicators.AppliedPrice.CLOSE,MAPeriod,IIndicators.MaType.SMA,1);
        
        IBar dailyBar = history.getBar(instrument,Period.DAILY,OfferSide.BID,0);
        
        ITick lastTick = history.getLastTick(instrument);
        double barSize = dailyBar.getHigh()-dailyBar.getLow();
        double barQuarter = barSize/4;
        double upperLevel = dailyBar.getHigh()-barQuarter;
        double lowerLevel = dailyBar.getLow()-barQuarter;
        
        //if (lastTick.getBid()<=lowerLevel || lastTick.getBid()>=upperLevel) {log ("Price is beyond acceptable daily range");return(false);}
        
        //if (ma15_0>=ma15_1) return (false);
        
        //if (ma60_0>=ma60_1) return (false);
        
        if (ma1440_0>=ma1440_1) return (false);
        
        return(true);
    }
    private String getLabel(Instrument instrument){
        return (Comment +  instrument.toString().replace("/","") + (new Date()).getTime());
        
    }
    private double getLotSize(){
        
        if (LotAdjuster==0) return (LotSize);
        double Contracts, Factor, Lotsize,LotMultiplier;
        LotMultiplier = LotMultiplier1;
        Contracts = (equity/10000);
        
        Factor = (Math.pow(LotMultiplier,Levels)-LotMultiplier) / (LotMultiplier - 1);
        
        Lotsize = (LotAdjuster*(Contracts/(1.0 + Factor)));
        Lotsize = Lotsize /10; //Dukascopy adjustment factor
        
        //Lotsize = round(Lotsize,3);
        if (Lotsize<0.001) Lotsize = 0.001;
        //LotSize = round(LotSize,4);
        //while ((LotSize%0.0005)>0.0) {LotSize+=0.0001;if ((LotSize%0.0005)==0.0)break;}
        return (Lotsize);
    }
    public static double round(double d, int decimalPlace){
        BigDecimal bd = new BigDecimal(Double.toString(d));
        bd = bd.setScale(decimalPlace,BigDecimal.ROUND_HALF_UP);
        return bd.doubleValue();
    }
    private void log (String comment){
        commentStr = commentStr + comment + "\n";
    }
    private void print (String comment,Instrument inst){
        console.getOut().println(Comment + " : " + inst.toString() + " : " + comment);
    }
    private double calculateProfitLoss(IOrder order) throws JFException {
      double closePrice;
      if (order.getState() == IOrder.State.CLOSED) {
         closePrice = order.getClosePrice();
      } else {
         ITick tick = history.getLastTick(order.getInstrument());
         if (order.getOrderCommand().isLong()) {
            closePrice = tick.getBid();
         } else {
            closePrice = tick.getAsk();
         }
      }
        BigDecimal profLossInSecondaryCCY;
        if (order.getOrderCommand().isLong()) {
            profLossInSecondaryCCY = BigDecimal.valueOf(closePrice).subtract(BigDecimal.valueOf(order.getOpenPrice()))
                    .multiply(BigDecimal.valueOf(order.getAmount()).multiply(BigDecimal.valueOf(1000000))).setScale(2, BigDecimal.ROUND_HALF_EVEN);
        } else {
            profLossInSecondaryCCY = BigDecimal.valueOf(order.getOpenPrice()).subtract(BigDecimal.valueOf(closePrice))
                    .multiply(BigDecimal.valueOf(order.getAmount()).multiply(BigDecimal.valueOf(1000000))).setScale(2, BigDecimal.ROUND_HALF_EVEN);
        }
        OfferSide side = order.getOrderCommand().isLong() ? OfferSide.ASK : OfferSide.BID;
        BigDecimal convertedProfLoss = convert(profLossInSecondaryCCY, order.getInstrument().getSecondaryCurrency(),
                accountCurrency, side).setScale(2, BigDecimal.ROUND_HALF_EVEN);
        return convertedProfLoss.doubleValue();
   }
   public BigDecimal convert(BigDecimal amount, Currency sourceCurrency, Currency targetCurrency, OfferSide side) throws JFException {
        if (targetCurrency.equals(sourceCurrency)) {
            return amount;
        }
    
        BigDecimal dollarValue;
        if (sourceCurrency.equals(Instrument.EURUSD.getSecondaryCurrency())) {
            dollarValue = amount;
        } else {
            Instrument helperSourceCurrencyPair = pairs.get(sourceCurrency);
            if (helperSourceCurrencyPair == null) {
                throw new IllegalArgumentException("No currency pair found for " + sourceCurrency);
            }
    
            BigDecimal helperSourceCurrencyPrice = getLastPrice(helperSourceCurrencyPair, side);
            if (null == helperSourceCurrencyPrice) return null;
    
            dollarValue = helperSourceCurrencyPair.toString().indexOf("USD") == 0 ?
                    amount.divide(helperSourceCurrencyPrice, 2, RoundingMode.HALF_EVEN)
                    : amount.multiply(helperSourceCurrencyPrice).setScale(2, RoundingMode.HALF_EVEN);
        }
    
        if (targetCurrency.equals(Instrument.EURUSD.getSecondaryCurrency())) {
            return dollarValue;
        }
    
        Instrument pair = pairs.get(targetCurrency);
        BigDecimal price = getLastPrice(pair, side);
        if (null == price) return null;
        
        BigDecimal result = pair.toString().indexOf("USD") == 0 ?
                dollarValue.multiply(price).setScale(2, RoundingMode.HALF_EVEN)
                : dollarValue.divide(price, 2, RoundingMode.HALF_EVEN);
    
        return result;
    }
    protected BigDecimal getLastPrice(Instrument pair, OfferSide side) throws JFException {
      ITick tick = history.getLastTick(pair);
      if (tick == null) {
         return null;
      }
      if (side == OfferSide.BID) {
         return BigDecimal.valueOf(tick.getBid());
      } else {
         return BigDecimal.valueOf(tick.getAsk());
      }
    }

    public void onBar(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException {
    }
    public static double roundToHalfPip(double d,Instrument inst){
        double pipScale = (inst.getPipValue()/10) * 5; //converts 0.0001 to 0.00005
        double roundedNum = round((d/pipScale),0) * pipScale;
        return (roundedNum);
    }
}