/*  
=====================================================================
=        Version 2.0                                                                                 =
=        July 2011                                                                                =
=        Developed by Avec with thanks to LinnuxFX                       = 
=====================================================================
 */
package jforex;

//import com.dukascopy.api.drawings.IEllipseChartObject;
import com.dukascopy.api.drawings.IShortLineChartObject;
//import java.awt.Shape;
//import java.awt.Stroke;
import java.text.ParseException;
import java.util.*;
//import java.util.Date;
//import java.awt.Color;
import java.util.concurrent.TimeUnit;
import java.text.DecimalFormat;
import java.util.Arrays;

import com.dukascopy.api.*;
import com.dukascopy.api.IAccount;
import com.dukascopy.api.IBar;
import com.dukascopy.api.IConsole;
import com.dukascopy.api.IContext;
import com.dukascopy.api.IEngine;
import com.dukascopy.api.IIndicators.*;
import com.dukascopy.api.IMessage;
import com.dukascopy.api.IStrategy;
import com.dukascopy.api.ITick;
import com.dukascopy.api.Instrument;
import com.dukascopy.api.JFException;
import com.dukascopy.api.OfferSide;
import com.dukascopy.api.Period;
import com.dukascopy.api.IEngine.OrderCommand;
import com.dukascopy.api.IIndicators.MaType;
import com.dukascopy.api.drawings.IHorizontalLineChartObject;
import com.dukascopy.api.drawings.IPolyLineChartObject;
import java.awt.BasicStroke;
import java.awt.Color;
//import java.awt.Stroke;
import java.io.File;
import java.text.SimpleDateFormat;
//import com.dukascopy.api.IIndicators.AppliedPrice;

//import java.text.ParseException;
//import java.util.Date;
//import java.util.logging.Level;
//import java.util.logging.Logger;
/**
 * 
 * @author 
 */
public class _1109_2_Avec implements IStrategy {

// Strategy interfaces which are used to execute trades, request indicators, print messages
  private IContext context = null;
  private IEngine engine = null;
  private IIndicators indicators = null;
  private IConsole console = null;
//  private IHistory history = null;
  private IChart chart = null;
//  private IAccount account = null;
  private IOrder activeOrder = null;
//  private IUserInterface userInterface;
  private double orderSize = 1; // 5.5;
//  private double slippage = 5;
  private Instrument myInstrument = Instrument.EURUSD;
//  private List<Instrument> selectedInstruments = new ArrayList<Instrument>(Arrays.asList(Instrument.EURUSD)); //, Instrument.AUDUSD
//  private Period myPeriod = Period.FIVE_MINS; // Period.ONE_HOUR;//TEN
  private static int min_Stoch = 5;// for take profit
  private static int lo_Stoch = 20;// for trailling stop //15
  private final int max_Stoch = 100 - min_Stoch;
  private final int hi_Stoch = 100 - lo_Stoch;
//  private static int fastKperiod = 5;
  private static int slowKPeriod = 3;
  private static int slowDPeriod = 3;
  private static MaType maType = MaType.WMA;
  // countCash() Parameters _________________________________________
  private double diffLong = 0;
  private double diffShort = 0;
  private double totalLong = 0;
  private double totalShort = 0;
  private int tradeCount = 0;
  private int winCount = 0;
  private int lossCount = 0;
  // stopLossATR() Parameters _______________________________________
//  private int periodSpan = 90;//180
//  private double stepFactor = 2; //3.25;//2.75
//  private double favorFactor = 3.0;//4.0
//  private long nuTime = 0;
  private double step = 0.0;
  //private double pipsInFavor = 0.0;
  // Others CashStochBot Parameters _________________________________    
//  private double ccyScale = Math.pow(10, myInstrument.getPipScale());
  private boolean stopSet = false;
//  private boolean tradingAllowed;
  private boolean loggingEnabled = true;
  private String commentStr;
//  private double open, stop, diff, newStop;
//  private boolean isLong;
//  private double StopLossAtBreakEvenPips = 4;//10
  private double trailStep = 55.0;//30
//  private boolean moveTrail = false;
//  private double pipValue;
  //
  //private static String TestStartTime = "01:00:00";
  private static SimpleDateFormat TestStopFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  private static String TestStopStr = "2012-11-04 00:20:00";
  private static Date TestStopDate; // =new Date(TestStopFormat.parse(TestStopStr).getTime());
  private static int TradingHourStart = 0; // 0; // 20
  private static final int TradingHourStop = 25; //24 //0 //9
  //
  //  long BarTime;

  private class listPoint {

    long Time;
    double Price;

    public listPoint(long Time, double Price) {
      this.Time = Time;
      this.Price = Price;
    }
  }
  private List<listPoint> listAsk = new ArrayList<listPoint>(0);
//  private IPolyLineChartObject polyLineStochCurr, polyLineStochPrev, polyLineStochCurrH, polyLineStochPrevH, polyLinePeriodPrev;
  private List<listPoint> listStopLoss = new ArrayList<listPoint>(0);
  private List<listPoint> listTakeProfit = new ArrayList<listPoint>(0);
  private List<listPoint> listStepHi = new ArrayList<listPoint>(0);
  private List<listPoint> listStepLo = new ArrayList<listPoint>(0);
  private List<listPoint> listBreakEven = new ArrayList<listPoint>(0);
  private List<listPoint> listBid = new ArrayList<listPoint>(0);
//  private IPolyLineChartObject polyLinePeriodCurrONE_MIN, polyLinePeriodCurrTEN_SECS, polyLinePeriodCurrONE_SEC;
//  private IPolyLineChartObject polyLineStochCurrONE_MIN, polyLineStochCurrTEN_SECS, polyLineStochCurrONE_SEC;
  private List<List<listPoint>> listsTrend = new ArrayList<List<listPoint>>(0);
  private List<List<listPoint>> listsStopLoss = new ArrayList<List<listPoint>>(0);
  private List<List<listPoint>> listsTakeProfit = new ArrayList<List<listPoint>>(0);
  private List<List<listPoint>> listsBreakEven = new ArrayList<List<listPoint>>(0);
  private List<List<listPoint>> listsStoch = new ArrayList<List<listPoint>>(0);
  private List<List<listPoint>> listsPeriod = new ArrayList<List<listPoint>>(0);
  private List<List<listPoint>> listsFractalMaxSto = new ArrayList<List<listPoint>>(0);
  private List<List<listPoint>> listsFractalMinSto = new ArrayList<List<listPoint>>(0);
  private List<List<listPoint>> listsFractalMaxCen = new ArrayList<List<listPoint>>(0);
  private List<List<listPoint>> listsFractalMinCen = new ArrayList<List<listPoint>>(0);
//  private ArrayList<listPoint[]> listFractalMinCe = new ArrayList<listPoint[]>(0);
  private static boolean toMainChart = true;
//  private Stroke Stroke;
  private Period[] myPeriods = {
    //    Period.TICK,
    //Period.ONE_SEC,
    //Period.TWO_SECS,
    Period.TEN_SECS,
    //    Period.TWENTY_SECS,
    //    Period.THIRTY_SECS,
    Period.ONE_MIN,
    Period.FIVE_MINS, //    Period.TEN_MINS,
    Period.FIFTEEN_MINS, //    Period.TWENTY_MINS,
  //    Period.THIRTY_MINS,
  //Period.ONE_HOUR,
  //Period.FOUR_HOURS
  };
  private Color[] myColors = {
    //Color.LIGHT_GRAY,
    //Color.GRAY,
    Color.DARK_GRAY,
    new Color(0x0ffeb00),
    new Color(0x0ffbc00),
    new Color(0x0ff8900),
    //    Color.YELLOW,
    Color.ORANGE,
    Color.PINK,
    Color.CYAN,
    Color.BLUE
  };
//  private IPolyLineChartObject[][] PolyLineArr = new IPolyLineChartObject[myPeriods.length][smaMax];
  private double HTperiodMin = 100;
  private Integer[] idx = new Integer[myPeriods.length];
  private int[] isTrend = new int[myPeriods.length];
  private double[] HTperiod = new double[myPeriods.length];
  private Integer[] HTPidx = new Integer[myPeriods.length];
  private Integer[] HTPrank = new Integer[myPeriods.length];
  private double[] stoch_Prev = new double[myPeriods.length];
  private double[] stoch_Curr = new double[myPeriods.length];
  private double[] stochSign_Curr = new double[myPeriods.length];
  private double[] stochSign_Prev = new double[myPeriods.length];
  private Integer[] STidx = new Integer[myPeriods.length];
  private Integer[] STrank = new Integer[myPeriods.length];
  private double[] fractalMinSto = new double[myPeriods.length];
  private double[] fractalMaxSto = new double[myPeriods.length];
  private double[] fractalMinStoPrev = new double[myPeriods.length];
  private double[] fractalMaxStoPrev = new double[myPeriods.length];
  private double[] fractalMinCen = new double[myPeriods.length];
  private double[] fractalMaxCen = new double[myPeriods.length];
  private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd EEE HH:mm:ss");
//  private static SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
  private long TickTime;
  private double tickPriceAverage;
  private int IDsel = 0;
  private double TP = Double.NaN;
  private double SL = Double.NaN;
  private boolean moveTrail = false;
  private boolean ZeroLoss = false;
  private Calendar calendar = Calendar.getInstance();
  private double AskMax;
  private double BidMin = Double.POSITIVE_INFINITY;
  private boolean NeutralZoneTouched = false;
  private String label;
  private static int smaMin = 5;
  private static int smaMax = 100;
  private double[][] smaPrev = new double[myPeriods.length][smaMax];
  private double[][] smaCurr = new double[myPeriods.length][smaMax];
  private double[][] smaSignPrev = new double[myPeriods.length][smaMax];
  private double[][] smaSignCurr = new double[myPeriods.length][smaMax];
  private double[][] smaPeriod = new double[myPeriods.length][smaMax];
  private int ticknr = 0;
  private int plCount = 0;

//    private myPoint = { new long TickTime, new double Price };
  /**
   * 
   * @param context
   * @throws JFException
   */
  @Override
  public void onStart(IContext context) throws JFException {


    dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
//    timeFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
    calendar.setTimeZone(TimeZone.getTimeZone("GMT"));

    try {
      TestStopFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
      TestStopDate = TestStopFormat.parse(TestStopStr);
    } catch (ParseException ex) {
      //Logger.getLogger(_1109_Avec.class.getName()).log(Level.SEVERE, null, ex);
      console.getOut().println("ERROR: " + _1109_2_Avec.class.getName() + " " + ex);
    }

    // Initialize interfaces in the onStart method ____________________
    this.context = context;
    this.engine = context.getEngine();
    this.console = context.getConsole();
//    this.history = context.getHistory();
//    this.account = context.getAccount();
    this.indicators = context.getIndicators();
//    userInterface = context.getUserInterface();


//          System.exit(0);


    // Subscrive the Instruments to Trade _____________________________
    Set<Instrument> subscribedInstruments = new HashSet<Instrument>(0);
    subscribedInstruments.add(myInstrument);
    context.setSubscribedInstruments(subscribedInstruments);

    chart = context.getChart(myInstrument);
    if (chart != null) {
//     listAsk = chart.getChartObjectFactory().createPolyLine();
//      listBid = chart.getChartObjectFactory().createPolyLine();
//      polyLineStochCurr = chart.getChartObjectFactory().createPolyLine();
//      polyLineStochPrev = chart.getChartObjectFactory().createPolyLine();
//      polyLineStochCurrH = chart.getChartObjectFactory().createPolyLine();
//      polyLineStochPrevH = chart.getChartObjectFactory().createPolyLine();
//      polyLinePeriodPrev = chart.getChartObjectFactory().createPolyLine();
//      polyLinePeriodCurr = chart.getChartObjectFactory().createPolyLine();
//      listStepHi = chart.getChartObjectFactory().createPolyLine();
//      listStepLo = chart.getChartObjectFactory().createPolyLine();
//      listStopLoss = chart.getChartObjectFactory().createPolyLine();
//      listTakeProfit = chart.getChartObjectFactory().createPolyLine();

      for (int i = 0; i < myPeriods.length; i++) {
        listsTrend.add(new ArrayList<listPoint>(0)); // chart.getChartObjectFactory().createPolyLine());
        listsStoch.add(new ArrayList<listPoint>(0)); // chart.getChartObjectFactory().createPolyLine());
        listsPeriod.add(new ArrayList<listPoint>(0)); // chart.getChartObjectFactory().createPolyLine());
        listsFractalMaxSto.add(new ArrayList<listPoint>(0)); // chart.getChartObjectFactory().createPolyLine());
        listsFractalMinSto.add(new ArrayList<listPoint>(0)); // chart.getChartObjectFactory().createPolyLine());
        listsFractalMaxCen.add(new ArrayList<listPoint>(0)); // chart.getChartObjectFactory().createPolyLine());
        listsFractalMinCen.add(new ArrayList<listPoint>(0)); // chart.getChartObjectFactory().createPolyLine());

        /* for (int j = smaMin; j < smaMax; j++) {
        PolyLineArr[i][j] = chart.getChartObjectFactory().createPolyLine();
        }
        listFractalMinCe.add(PolyLineArr[i]); */
      }
      log("MAX COUNT" + com.dukascopy.api.drawings.IPolyLineChartObject.MAX_POINTS_COUNT);
    }
    File IndFile = new File(context.getFilesDir() + System.getProperty("file.separator") + "FractalLines1.jfx");
    console.getOut().println("loading indicator from file: " + IndFile.toString());
//    register custom indicator located in ...\JForex\Strategies\files folder
    indicators.registerCustomIndicator(IndFile);

    for (int i = 0; i < myPeriods.length; i++) { // we calc stoch on bars, so we need data for big bars before start
//      long BarStartTime = history.getStartTimeOfCurrentBar(myInstrument, myPeriods[i]);
//      long BarEndTime = history.getNextBarStart(myPeriods[i], BarStartTime);
//      if (myPeriods[i] == Period.FOUR_HOURS) {
//        console.getOut().println(" BarStartTime=" + dateFormat.format(BarStartTime));
//      }
      idx[i] = i;
      myCalculation(i);
      /*
      isTrend[i] = indicators.ht_trendmode(myInstrument, myPeriods[i], OfferSide.BID, AppliedPrice.MEDIAN_PRICE, 0);
      
      idx[i] = i;
      HTperiod[i] = calcPeriod(myPeriods[i]);
      stoch_Curr[i] = calcStoch(myPeriods[i], HTperiod[i]);
      stoch_Prev[i] = stoch_Curr[i];
      
      if ((stoch_Curr[i] < lo_Stoch) || (stoch_Curr[i] > hi_Stoch)) { // breakout stoch line
      fractalMaxSto[i] = (Double) indicators.calculateIndicator(myInstrument, myPeriods[i], new OfferSide[]{OfferSide.ASK}, "FractalLines", new AppliedPrice[]{AppliedPrice.MEDIAN_PRICE}, new Object[]{2}, 0)[0];
      fractalMinSto[i] = (Double) indicators.calculateIndicator(myInstrument, myPeriods[i], new OfferSide[]{OfferSide.BID}, "FractalLines", new AppliedPrice[]{AppliedPrice.MEDIAN_PRICE}, new Object[]{2}, 0)[1];
      } else {
      fractalMaxSto[i] = Double.NaN;
      fractalMinSto[i] = Double.NaN;
      }
      fractalMaxStoPrev[i] = fractalMaxSto[i];
      fractalMinStoPrev[i] = fractalMinSto[i];
       */
//      listsPeriod.get(i).addNewPoint(BarStartTime, (1.31 + HTperiod[i] / 10000.));
//      listsPeriod.get(i).addNewPoint(BarEndTime, (1.31 + HTperiod[i] / 10000.));
//      listsStoch.get(i).addNewPoint(BarStartTime, (1.32 + stoch_Curr[i] / 10000.));
//      listsStoch.get(i).addNewPoint(BarEndTime, (1.32 + stoch_Curr[i] / 10000.));
    }

    console.getOut().println("idx=");
    print((Object[]) idx);
    console.getOut().println("HTperiod=");
    print(HTperiod);

    HTPidx = mySort(idx, HTperiod);

    console.getOut().println("HTperiod sorted=");
    print((Object[]) idx);
    print((Object[]) HTPidx);
    print(HTperiod);

//    Arrays.sort(HTperiod);
//    context.stop();
    console.getOut().println("CashStochAvec Started ...");
  } // onStart

  /**
   * 
   * @param account
   * @throws JFException
   */
  @Override
  public void onAccount(IAccount account) throws JFException {
//    if (account.getUseOfLeverage() == 0) {
//      tradingAllowed = true;
//    }
  } // onAccount

  private void myCalculation(int i) throws JFException {
    // old values:
//      listsPeriod.get(i).addNewPoint(BarStartTime, (1.31 + HTperiod[i] / 10000.));
//      listsStoch.get(i).addNewPoint(BarStartTime, (1.32 + stoch_Curr[i] / 10000.));

    isTrend[i] = indicators.ht_trendmode(myInstrument, myPeriods[i], OfferSide.BID, AppliedPrice.MEDIAN_PRICE, 1); // FIXME may 1 here ?
//    log(myPeriods[i].name() + " isTrend=" + isTrend[i] + " " + (isTrend[i] == 0));

    stochSign_Prev[i] = Math.signum(stoch_Curr[i] - stoch_Prev[i]);
    stoch_Prev[i] = stoch_Curr[i];

    HTperiod[i] = calcPeriod(myPeriods[i]);
    if (HTperiod[i] < HTperiodMin) {
      HTperiodMin = HTperiod[i];
    }
    stoch_Curr[i] = calcStoch(myPeriods[i], HTperiod[i]);
    stochSign_Curr[i] = Math.signum(stoch_Curr[i] - stoch_Prev[i]);
    fractalMaxStoPrev[i] = fractalMaxSto[i];
    fractalMinStoPrev[i] = fractalMinSto[i];
    fractalMaxSto[i] = (Double) indicators.calculateIndicator(myInstrument, myPeriods[i], new OfferSide[]{OfferSide.ASK}, "FractalLines", new AppliedPrice[]{AppliedPrice.HIGH}, new Object[]{2}, 1)[0];
    fractalMinSto[i] = (Double) indicators.calculateIndicator(myInstrument, myPeriods[i], new OfferSide[]{OfferSide.BID}, "FractalLines", new AppliedPrice[]{AppliedPrice.LOW}, new Object[]{2}, 1)[1];

    /* for (int j = smaMin; j < smaMax; j++) {
    smaPrev[i][j] = smaCurr[i][j];
    smaSignPrev[i][j] = smaSignCurr[i][j];
    smaCurr[i][j] = indicators.sma(myInstrument, myPeriods[i], OfferSide.BID, AppliedPrice.MEDIAN_PRICE, j, 1);
    smaSignCurr[i][j] = Math.signum(smaCurr[i][j] - smaPrev[i][j]);
    if (smaSignCurr[i][j] * smaSignPrev[i][j] <= 0) {
    smaPeriod[i][j] = j;
    //        log("i=" + i + " j=" + j + " smaPeriod[i][j]=" + smaPeriod[i][j]);
    } else {
    smaPeriod[i][j] = Double.NaN;
    }
    }*/

    /*
    if ((stoch_Prev[i] >= lo_Stoch && stoch_Curr[i] < lo_Stoch) || (stoch_Prev[i] <= hi_Stoch && stoch_Curr[i] > hi_Stoch)) { // breakout stoch line
    fractalMaxSto[i] = (Double) indicators.calculateIndicator(myInstrument, myPeriods[i], new OfferSide[]{OfferSide.ASK}, "FractalLines", new AppliedPrice[]{AppliedPrice.MEDIAN_PRICE}, new Object[]{2}, 0)[0];
    fractalMinSto[i] = (Double) indicators.calculateIndicator(myInstrument, myPeriods[i], new OfferSide[]{OfferSide.BID}, "FractalLines", new AppliedPrice[]{AppliedPrice.MEDIAN_PRICE}, new Object[]{2}, 0)[1];
    } else {
    fractalMaxSto[i] = Double.NaN;
    fractalMinSto[i] = Double.NaN;
    }
    
    if ((stoch_Prev[i] >= 50 && stoch_Curr[i] < 50) || (stoch_Prev[i] <= 50 && stoch_Curr[i] > 50)) { // breakout stoch center line
    fractalMaxCen[i] = (Double) indicators.calculateIndicator(myInstrument, myPeriods[i], new OfferSide[]{OfferSide.ASK}, "FractalLines", new AppliedPrice[]{AppliedPrice.MEDIAN_PRICE}, new Object[]{2}, 0)[0];
    fractalMinCen[i] = (Double) indicators.calculateIndicator(myInstrument, myPeriods[i], new OfferSide[]{OfferSide.BID}, "FractalLines", new AppliedPrice[]{AppliedPrice.MEDIAN_PRICE}, new Object[]{2}, 0)[1];
    priceCen[i] = tickPriceAverage;
    } else {
    fractalMaxCen[i] = Double.NaN;
    fractalMinCen[i] = Double.NaN;
    }
    // new values:
    //      listsPeriod.get(i).addNewPoint(BarStartTime, (1.31 + HTperiod[i] / 10000.));
    //      listsStoch.get(i).addNewPoint(BarStartTime, (1.32 + stoch_Curr[i] / 10000.));
     */
  } // myCalculation

  /**
   * 
   * @param instrument
   * @param period
   * @param askbar
   * @param bidbar
   * @throws JFException
   */
  @Override
  public void onBar(Instrument instrument, Period period, IBar askbar, IBar bidbar) throws JFException {

    //if (myPeriod == period && myInstrument == instrument && tradingAllowed) {
    if (!instrument.equals(myInstrument)) { // XXX it is correct    || !period.equals(Period.FIVE_MINS)
      return;
    }

//    long BarStartTime = askbar.getTime();

    for (int i = 0; i < myPeriods.length; i++) {
      if (period != myPeriods[i]) {
        continue; // FIXME
      }
      myCalculation(i);
    }

    /*
    double[] STshift = stoch_Curr.clone();
    int MaxRank = HTperiod.length;
    
    HTPidx = mySort(idx, HTperiod);
    for (int i : idx) {
    HTPrank[HTPidx[i]] = MaxRank - i;
    STshift[i] = Math.abs(stoch_Curr[i] - 50);
    }
    STidx = mySort(idx, STshift);
    for (int i : idx) {
    if (STshift[i] > (hi_Stoch - lo_Stoch) / 2) {
    STrank[i] = MaxRank - STidx[i];
    } else {
    STrank[i] = 0;
    }
    }
    
     */
//    log(" HTperiod=" + arrayToString(HTperiod) + " HTPidx=" + arrayToString(HTPidx) + " HTPrank=" + arrayToString(HTPrank));
//    log(" stoch_Curr=" + arrayToString(stoch_Curr) + " STshift=" + arrayToString(STshift) + " STidx=" + arrayToString(STidx) + " STrank=" + arrayToString(STrank));
//    log(" onBar period=" + period.name());

    if (hasOpenedOrder()) {
//      context.stop();
    }

    IDsel = 1;

    if (!NeutralZoneTouched) {
      if ((stoch_Curr[IDsel] > lo_Stoch && stoch_Curr[IDsel] < hi_Stoch) && (Math.signum(stoch_Prev[IDsel - 1] - 50) != Math.signum(stoch_Curr[IDsel - 1] - 50))) { // stoch_Curr[IDsel - 1] > lo_Stoch && stoch_Curr[IDsel - 1] < hi_Stoch
        NeutralZoneTouched = true;
        log("NeutralZoneTouched=" + NeutralZoneTouched);
      }
      return;
    }

    if (activeOrder == null && isTradingAllowed(myInstrument, TickTime) && !isFriday(TickTime)) {

//      if (HTPrank[IDsel] > HTPrank[IDsel - 1] && STrank[0] > 0 && STrank[2] == 0) {
//      if (HTPrank[IDsel] > HTPrank[IDsel - 1]) {
//        log(dateFormat.format(TickTime) + " isTradingAllowed=" + isTradingAllowed(myInstrument, TickTime));

      //double [] this_TVS = this.indicators.tvs(myInstrument, myPeriod, OfferSide.BID, IIndicators.AppliedPrice.CLOSE, 18, Filter.ALL_FLATS, 1, thisBar.getTime(), 0);


      // Entry Trades Setup______________________________________________________________  

//        double stoch_Prev = 0;
//        double stoch_Curr = 0;

      // condition when we BUY
//        if (stoch_Curr[IDsel - 1] > stoch_Prev[IDsel - 1] && stoch_Curr[IDsel - 1] > stoch_Curr[IDsel] && stoch_Curr[IDsel] < 50 && stoch_Curr[IDsel - 1] < 50) {
//      if ((stoch_Curr[IDsel + 1] > stoch_Prev[IDsel + 1] || fractalMinSto[IDsel] > fractalMinStoPrev[IDsel]) && stoch_Curr[IDsel] > stoch_Prev[IDsel] && stoch_Curr[IDsel] <= lo_Stoch && stoch_Curr[IDsel - 1] > stoch_Prev[IDsel - 1]) { // && stoch_Curr[IDsel - 1] < stoch_Curr[IDsel - 1]
      if (isTrend[IDsel] == 0 && ((stochSign_Curr[IDsel + 1] * stochSign_Prev[IDsel + 1] <= 0 && stochSign_Curr[IDsel + 1] > 0) || fractalMinSto[IDsel] > fractalMinStoPrev[IDsel]) && stoch_Curr[IDsel] > stoch_Prev[IDsel] && stoch_Curr[IDsel] <= lo_Stoch && stoch_Curr[IDsel - 1] > stoch_Prev[IDsel - 1] && stoch_Curr[IDsel + 2] < hi_Stoch) { // && stoch_Curr[IDsel - 1] < stoch_Curr[IDsel - 1]
//          log(" HTperiod=" + arrayToString(HTperiod) + " HTPidx=" + arrayToString(HTPidx) + " HTPrank=" + arrayToString(HTPrank));
//          log(" stoch_Curr=" + arrayToString(stoch_Curr) + " STshift=" + arrayToString(STshift) + " STidx=" + arrayToString(STidx) + " STrank=" + arrayToString(STrank));
//          log(dateFormat.format(TickTime) + " " + (HTPidx[IDsel - 1] > HTPidx[IDsel]) + " " + (STrank[0] > 0) + " " + (STrank[2] == 0));
//          log(dateFormat.format(TickTime) + " " + (stoch_Curr[IDsel - 1] > stoch_Prev[IDsel - 1]) + " " + (stoch_Curr[IDsel - 1] > stoch_Curr[IDsel]));
//        double zone = Math.max(Math.abs(tickPriceAverage - fractalMaxSto[IDsel + 1]), Math.abs(tickPriceAverage - fractalMinSto[IDsel + 1]));
        TP = Double.NaN; //roundPip(tickPriceAverage + zone); //1.0002~=2pip  roundPip(2 * fractal - 1 * tickPriceAverage);
//        SL = roundPip(tickPriceAverage - zone);//roundPip(2 * tickPriceAverage - fractal);
        SL = roundPip(2 * Math.min(Math.min(Math.min(fractalMinStoPrev[IDsel - 1], fractalMinSto[IDsel - 1]), fractalMinStoPrev[IDsel]), fractalMinSto[IDsel]) - Math.max(Math.max(Math.max(fractalMaxStoPrev[IDsel - 1], fractalMaxSto[IDsel - 1]), fractalMaxStoPrev[IDsel]), fractalMaxSto[IDsel]));

//          context.stop();
        if (stochSign_Curr[IDsel + 1] * stochSign_Prev[IDsel + 1] <= 0 && stochSign_Curr[IDsel + 1] > 0) { // Stoch sign change
          label = "S";
        }
        if (fractalMinSto[IDsel] > fractalMinStoPrev[IDsel]) {
          label = "F";
        }
        log("Buy " + label + " TP=" + TP + " SL=" + SL);//  + " priceCen=" + priceCen[IDsel] + " tickPriceAverage=" + tickPriceAverage);
        submitOrder("Buy" + label, OrderCommand.BUY, 0, 2, SL, 0);
        log("OK");
        ZeroLoss = false;
        moveTrail = false;
        return;
      }

      // condition when we SELL
//      if (false && stoch_Curr[IDsel - 1] < stoch_Prev[IDsel - 1] && stoch_Curr[IDsel - 1] < stoch_Curr[IDsel] && stoch_Curr[IDsel] > 50 && stoch_Curr[IDsel - 1] > 50) {
//        } else if (stoch_Curr[IDsel] < stoch_Prev[IDsel] && stoch_Prev[IDsel] > hi_Stoch && stoch_Curr[IDsel] > hi_Stoch) {
//          double fractalMax = (Double) indicators.calculateIndicator(myInstrument, myPeriods[IDsel + 1], new OfferSide[]{OfferSide.ASK}, "FractalLines", new AppliedPrice[]{AppliedPrice.MEDIAN_PRICE}, new Object[]{2}, 0)[1];
      if (isTrend[IDsel] == 0 && ((stochSign_Curr[IDsel + 1] * stochSign_Prev[IDsel + 1] <= 0 && stochSign_Curr[IDsel + 1] < 0) || fractalMaxSto[IDsel] < fractalMaxStoPrev[IDsel]) && stoch_Curr[IDsel] < stoch_Prev[IDsel] && stoch_Curr[IDsel] >= hi_Stoch && stoch_Curr[IDsel - 1] < stoch_Prev[IDsel - 1] && stoch_Curr[IDsel + 2] > lo_Stoch) {
//        double zone = Math.max(Math.abs(tickPriceAverage - fractalMaxSto[IDsel + 1]), Math.abs(tickPriceAverage - fractalMinSto[IDsel + 1]));
        TP = Double.NaN; //roundPip(tickPriceAverage - zone);//roundPip(2 * fractal - 1 * tickPriceAverage);
//        SL = roundPip(tickPriceAverage + zone);//roundPip(2 * tickPriceAverage - fractal);
/*        log("fractalMinStoPrev[IDsel-1]" + fractalMinStoPrev[IDsel - 1]);
        log("fractalMinSto[IDsel-1]" + fractalMinSto[IDsel - 1]);
        log("fractalMinStoPrev[IDsel]" + fractalMinStoPrev[IDsel]);
        log("fractalMinSto[IDsel]" + fractalMinSto[IDsel]);
        log("fractalMaxStoPrev[IDsel - 1]" + fractalMaxStoPrev[IDsel - 1]);
        log("fractalMaxSto[IDsel - 1]" + fractalMaxSto[IDsel - 1]);
        log("fractalMaxStoPrev[IDsel]" + fractalMaxStoPrev[IDsel]);
        log("fractalMaxSto[IDsel]" + fractalMaxSto[IDsel]);
        log("Math.min(Math.min(Math.min(fractalMinStoPrev[IDsel - 1], fractalMinSto[IDsel - 1]), fractalMinStoPrev[IDsel]), fractalMinSto[IDsel])" + Math.min(Math.min(Math.min(fractalMinStoPrev[IDsel - 1], fractalMinSto[IDsel - 1]), fractalMinStoPrev[IDsel]), fractalMinSto[IDsel]));
        log("Math.max(Math.max(Math.max(fractalMaxStoPrev[IDsel - 1], fractalMaxSto[IDsel - 1]), fractalMaxStoPrev[IDsel]), fractalMaxSto[IDsel])" + Math.max(Math.max(Math.max(fractalMaxStoPrev[IDsel - 1], fractalMaxSto[IDsel - 1]), fractalMaxStoPrev[IDsel]), fractalMaxSto[IDsel]));
         */
        SL = roundPip(2 * Math.max(Math.max(Math.max(fractalMaxStoPrev[IDsel - 1], fractalMaxSto[IDsel - 1]), fractalMaxStoPrev[IDsel]), fractalMaxSto[IDsel]) - Math.min(Math.min(Math.min(fractalMinStoPrev[IDsel - 1], fractalMinSto[IDsel - 1]), fractalMinStoPrev[IDsel]), fractalMinSto[IDsel]));
        if (stochSign_Curr[IDsel + 1] * stochSign_Prev[IDsel + 1] <= 0 && stochSign_Curr[IDsel + 1] < 0) { // Stoch sign change
          label = "S";
        }
        if (fractalMaxSto[IDsel] < fractalMaxStoPrev[IDsel]) {
          label = "F";
        }
        log("Sell " + label + " TP=" + TP + " SL=" + SL);//  + " priceCen=" + priceCen[IDsel] + " tickPriceAverage=" + tickPriceAverage);
        submitOrder("Sell" + label, OrderCommand.SELL, 0, 2, SL, 0);
        log("OK");
//        context.stop();
        ZeroLoss = false;
        moveTrail = false;
        return;
      }
//      }
    }

    // IDsel croosses forward stoch 50 (breakheaven)
    if (activeOrder != null && !ZeroLoss) {
      IOrder.State orderState;
      orderState = activeOrder.getState();
      if (activeOrder.getTakeProfitPrice() == 0 && (orderState == IOrder.State.FILLED || orderState == IOrder.State.OPENED)) {
        double openPrice = activeOrder.getOpenPrice();
        double shiftPip = (activeOrder.isLong() ? 1 : -1) * myInstrument.getPipValue();
        TP = openPrice + 15 * shiftPip;
        log("TP=" + TP + " openPrice=" + openPrice + " getStopLossPrice=" + activeOrder.getStopLossPrice() + " diff=" + (openPrice - activeOrder.getStopLossPrice()));
        activeOrder.setTakeProfitPrice(TP);
        log("OK");
//        if (Math.abs(openPrice - activeOrder.getStopLossPrice()) < 20 * myInstrument.getPipValue()) {
        SL = openPrice - 10 * shiftPip;
        if (SL != activeOrder.getStopLossPrice()) {
          log("SL=" + SL + " shiftPip=" + shiftPip);
          activeOrder.setStopLossPrice(SL);
          log("OK");
        }
//        };
      }

      if (false) {
        if ((stoch_Prev[IDsel] >= 50 && stoch_Curr[IDsel] < 50) || (stoch_Prev[IDsel] <= 50 && stoch_Curr[IDsel] > 50)) { // breakout stoch line
          SL = activeOrder.getOpenPrice();
          log("SL=" + SL);
          activeOrder.setStopLossPrice(SL);
          log("OK");
//        SL = newStopLossPrice;
          ZeroLoss = true;
        }
      }
    }

    if (false) {
      /*    
      // IDsel croosses back stoch 50 (target not reached)
      if (activeOrder != null && ZeroLoss) {
      if (activeOrder.isLong() && stoch_Prev[IDsel] >= 50 && stoch_Curr[IDsel] < 50) {// breakout stoch 50 line
      double fractalMin = roundPip((Double) indicators.calculateIndicator(myInstrument, myPeriods[IDsel - 1], new OfferSide[]{OfferSide.BID}, "FractalLines", new AppliedPrice[]{AppliedPrice.MEDIAN_PRICE}, new Object[]{2}, 0)[1]);// MinMin fractal
      if (fractalMin > activeOrder.getStopLossPrice()) {
      SL = fractalMin;
      activeOrder.setStopLossPrice(SL);
      }
      }
      if (!activeOrder.isLong() && stoch_Prev[IDsel] <= 50 && stoch_Curr[IDsel] > 50) {// breakout stoch 50 line
      double fractalMax = roundPip((Double) indicators.calculateIndicator(myInstrument, myPeriods[IDsel - 1], new OfferSide[]{OfferSide.ASK}, "FractalLines", new AppliedPrice[]{AppliedPrice.MEDIAN_PRICE}, new Object[]{2}, 0)[0]);// MaxMax fractal
      if (fractalMax < activeOrder.getStopLossPrice()) {
      SL = fractalMax;
      activeOrder.setStopLossPrice(SL);
      }
      }
      }
       */

      // IDsel croosses stoch trailing line (set trailing stop for profit)
      if (activeOrder != null) {
        if (activeOrder.isLong() && stoch_Prev[IDsel] <= hi_Stoch && stoch_Curr[IDsel] > hi_Stoch) {// breakout stoch hi_Stoch line
          if (fractalMinSto[IDsel - 1] > activeOrder.getStopLossPrice()) {
            SL = fractalMinSto[IDsel - 1];
            activeOrder.setStopLossPrice(SL);
          }
        }
        if (!activeOrder.isLong() && stoch_Prev[IDsel] >= lo_Stoch && stoch_Curr[IDsel] < lo_Stoch) {// breakout stoch lo_Stoch line
//        double fractalMax = roundPip((Double) indicators.calculateIndicator(myInstrument, myPeriods[IDsel - 1], new OfferSide[]{OfferSide.ASK}, "FractalLines", new AppliedPrice[]{AppliedPrice.MEDIAN_PRICE}, new Object[]{2}, 0)[0]);// MaxMax fractal
          if (fractalMaxSto[IDsel - 1] < activeOrder.getStopLossPrice()) {
            SL = fractalMaxSto[IDsel - 1];
//          log("getStopLossPrice=" + activeOrder.getStopLossPrice() + " SL=" + SL);
            activeOrder.setStopLossPrice(SL);
          }
        }
      }

      // IDsel-1 croosses stoch trailing line (set trailing stop for profit)
      if (activeOrder != null) {
        if (activeOrder.isLong() && stoch_Curr[IDsel] > hi_Stoch && stoch_Prev[IDsel - 1] <= hi_Stoch && stoch_Curr[IDsel - 1] > hi_Stoch) {// breakout stoch hi_Stoch line
          if (fractalMinSto[IDsel - 1] > activeOrder.getStopLossPrice()) {
            SL = fractalMinSto[IDsel - 1];
            activeOrder.setStopLossPrice(SL);
          }
        }
        if (!activeOrder.isLong() && stoch_Curr[IDsel] < lo_Stoch && stoch_Prev[IDsel - 1] >= lo_Stoch && stoch_Curr[IDsel - 1] < lo_Stoch) {// breakout stoch lo_Stoch line
          double fractalMax = roundPip((Double) indicators.calculateIndicator(myInstrument, myPeriods[IDsel - 1], new OfferSide[]{OfferSide.ASK}, "FractalLines", new AppliedPrice[]{AppliedPrice.MEDIAN_PRICE}, new Object[]{2}, 0)[0]);// MaxMax fractal
          if (fractalMaxSto[IDsel - 1] < activeOrder.getStopLossPrice()) {
            SL = fractalMaxSto[IDsel - 1];
            activeOrder.setStopLossPrice(SL);
          }
        }
      }
    }
    /*    
    // IDsel-1 croosses stoch take profit line (close position for profit)
    if (activeOrder != null) {
    if (activeOrder.isLong() && stoch_Curr[IDsel] > hi_Stoch && stoch_Prev[IDsel - 1] <= max_Stoch && stoch_Curr[IDsel - 1] > max_Stoch) {
    SL = Double.NaN;
    //          activeOrder.setStopLossPrice(price, OfferSide.BID, trailStep);
    activeOrder.close();
    
    }
    if (!activeOrder.isLong() && stoch_Curr[IDsel] < lo_Stoch && stoch_Prev[IDsel - 1] >= min_Stoch && stoch_Curr[IDsel - 1] < min_Stoch) {
    SL = Double.NaN;
    activeOrder.close();
    }
    }
     */

//    CheckZeroLoss(IDsel);

    countCash();
    chart = context.getChart(instrument);
    if (chart != null && period == Period.FIVE_MINS) {
//      int printLong = Math.round((float) (totalLong * 100000)) / 100000;
//      printLong = Math.round((float) (printLong * 1000000)) / 100d;
//      int printLong = (int) Math.round(totalLong * 1E4);

//      int printShort = (int) Math.round(totalShort * 100000) / 100000;
//      printShort = (int) Math.round(printShort * 1000000) / 100d;
//      int printShort = (int) Math.round(totalShort * 1E4);
      int printTotal = (int) Math.round((totalLong + totalShort) * 1E4);
//      commentStr = "CashStochAvec\n\n";
      commentStr = "Pips long  : " + Math.round(totalLong * 1E4) + "\n";
      commentStr += "Pips short : " + Math.round(totalShort * 1E4) + "\n";
      commentStr += "Total pips : " + printTotal + "\n";
      commentStr += printTotal + " pips x " + Math.round(orderSize * 100) + " $/pip = " + Math.round((totalLong + totalShort) * 1E4 * (orderSize * 100)) + " " + " $\n";
      commentStr += "Number of positions closed: " + tradeCount + "\n";
      commentStr += "Winners    : " + winCount + "\n";
      commentStr += "Losers     : " + lossCount + "\n";
      chart.comment(commentStr);
      if (tradeCount >= 2) {
//      log("totalLong=" + totalLong);
//      log("totalLong=" + Math.round(totalLong * 1E4));
//      log(commentStr);
//        context.stop();
      }
    }

    /*
    for (int i : idx) {
    if (HTPidx[0] > 0 && STshift[i] > (hi_Stoch - lo_Stoch) / 2) {
    log(dateFormat.format(TickTime) + " i=" + i + " " + myPeriods[i].name() + " stoch_Curr=" + stoch_Curr[i] + " HTPrank=" + arrayToString(HTPrank) + " STrank?=" + arrayToString(STrank));
    //        break;
    context.stop();
    }
    }
     */

//    long BarTime = bidbar.getTime();

    /*
    if (stopSet && activeOrder.getState() == IOrder.State.CLOSED) {
    listStopLoss.addNewPoint(BarTime, listStopLoss.getPrice(listStopLoss.getPointsCount() - 1));
    listTakeProfit.addNewPoint(BarTime, listTakeProfit.getPrice(listTakeProfit.getPointsCount() - 1));
    //      listBreakEven.addNewPoint(BarTime, listBreakEven.getPrice(listBreakEven.getPointsCount() - 1));
    }*/

    //    print(" " + BarTime);
    // Collect information to trade ___________________________________________________ 
//      IBar thisBar = history.getBar(myInstrument, myPeriod, OfferSide.ASK, 0);
//      IBar prevBar = history.getBar(myInstrument, myPeriod, OfferSide.ASK, 1);
    //double stoch_Curr, stoch_Prev;

  } // onBar

  private void submitOrder(String label, OrderCommand orderCmd, double price, double slippage, double SL, double TP) throws JFException {
//        engine.submitOrder(label, selectedInstrument, IEngine.OrderCommand.BUY, lot, 0, 5, roundPip(SL), roundPip(TP));
    activeOrder = engine.submitOrder(label, myInstrument, orderCmd, orderSize, price, slippage, SL, TP);
    activeOrder.waitForUpdate(2, TimeUnit.SECONDS);

    int i = 0;
    while (activeOrder.getState() == IOrder.State.CREATED || activeOrder.getState() == IOrder.State.OPENED) {
//      log("wait: activeOrder.getState=" + activeOrder.getState().toString());
      activeOrder.waitForUpdate(2, TimeUnit.SECONDS);
      i++;
      if (i > 10) {
        log("ERROR: activeOrder.getState=" + activeOrder.getState().toString() + " wait attempt=" + i + ". Break.");
        break;
      }
    }

    if (activeOrder.getState() == IOrder.State.CANCELED) {
      activeOrder = null;
    }
//    context.stop();
  } // submitOrder

  /**
   * 
   * @param instrument
   * @param tick
   * @throws JFException
   */
  @Override
  public void onTick(Instrument instrument, ITick tick) throws JFException {

    TickTime = tick.getTime();

    if (!isTestTime(TickTime, TestStopDate)) {
      log(dateFormat.format(TickTime) + " not TestTime. context.stop()");
      context.stop();
    }

    if (chart != null) {
      //     return;
      double Ask = tick.getAsk();
      double Bid = tick.getBid();
      listAsk.add(new listPoint(TickTime, Ask)); //  .addNewPoint(TickTime, Ask);
      ticknr++;
      if (ticknr >= 155) {
//      context.stop();
      }

      if (Ask > AskMax) {
        AskMax = Ask;
      }
      listBid.add(new listPoint(TickTime, Bid));
      if (Bid < BidMin) {
        BidMin = Bid;
      }
      listStopLoss.add(new listPoint(TickTime, SL));
      listTakeProfit.add(new listPoint(TickTime, TP));

      for (int i = 0; i < myPeriods.length; i++) {
//        listPoint aa = new listPoint(TickTime, (double) isTrend[i] * 10d);
        listsTrend.get(i).add(new listPoint(TickTime, (double) isTrend[i] * 10d));
        listsPeriod.get(i).add(new listPoint(TickTime, HTperiod[i])); // (1.31 + HTperiod[i] / 10000.)
        listsStoch.get(i).add(new listPoint(TickTime, stoch_Curr[i])); // (1.32 + stoch_Curr[i] / 10000.)
        listsFractalMaxSto.get(i).add(new listPoint(TickTime, fractalMaxSto[i]));
        listsFractalMinSto.get(i).add(new listPoint(TickTime, fractalMinSto[i]));
        listsFractalMaxCen.get(i).add(new listPoint(TickTime, fractalMaxCen[i]));
        listsFractalMinCen.get(i).add(new listPoint(TickTime, fractalMinCen[i]));
        /*PolyLineArr[i] = listFractalMinCe.get(i);
        for (int j = smaMin; j < smaMax; j++) {
        PolyLineArr[i][j].addNewPoint(TickTime, smaPeriod[i][j]);
        }*/
      }
      /*
      long time = history.getStartTimeOfCurrentBar(myInstrument, myPeriod);
      if (nuTime != time) {
      //      double atr = indicators.atr(myInstrument, Period.ONE_HOUR, OfferSide.BID, periodSpan, Filter.WEEKENDS, 1, time, 0)[0]; //TODO (time or TickTime)? (0 or 1)? (60 or 1)?
      double atr = indicators.atr(myInstrument, myPeriod, OfferSide.BID, fastKperiod, Filter.WEEKENDS, 1, TickTime, 0)[0]; //TODO (time or TickTime)? (0 or 1)? (60 or 1)?
      step = atr * stepFactor;
      //      pipsInFavor = atr * favorFactor;
      nuTime = time;
      }*/


      // Set initial stop-loss / move stop-loss as rate moves in our favor ________________________
      tickPriceAverage = (tick.getBid() + tick.getAsk()) / 2;
      listStepHi.add(new listPoint(TickTime, tickPriceAverage + step)); //  * 0.525 TODO new constant here
      listStepLo.add(new listPoint(TickTime, tickPriceAverage - step));

      if (false && activeOrder != null && instrument.equals(myInstrument)) {
        if (activeOrder.getState() == IOrder.State.FILLED) {
          stopLossATR(instrument, tick);
        }
        if (stopSet && activeOrder.getState() == IOrder.State.CLOSED) {
          try {
            listStopLoss.add(new listPoint(TickTime, listStopLoss.get(listStopLoss.size() - 1).Price));
            listTakeProfit.add(new listPoint(TickTime, listTakeProfit.get(listTakeProfit.size() - 1).Price));
//          listBreakEven.addNewPoint(TickTime, listBreakEven.getPrice(listBreakEven.size() - 1));
          } catch (Exception e) {
            log("ERROR: ArrayNaN addNewPoint: " + e);
            context.stop();
          }
        }
      }
    } // onTick

    /*fastKperiod = 5;
    stoch_Curr = indicators.stoch(myInstrument, myPeriod, OfferSide.BID, fastKperiod, 3, MaType.WMA, 3, MaType.WMA, 0)[0];
    polyLineStochCurr.addNewPoint(TickTime, 1.32 + stoch_Curr / 10000);
    stoch_Prev = indicators.stoch(myInstrument, myPeriod, OfferSide.BID, fastKperiod, 3, MaType.WMA, 3, MaType.WMA, 1)[0];
    polyLineStochPrev.addNewPoint(TickTime, 1.32 + stoch_Prev / 10000);
     */
//      double force_Curr = indicators.force(myInstrument, myPeriod, OfferSide.BID, IIndicators.AppliedPrice.CLOSE, 14, MaType.SMA, 0);
//    if (false) {
//    fastKperiod = (int) indicators.ht_dcperiod(myInstrument, myPeriod, OfferSide.BID, AppliedPrice.CLOSE, 1);
//    polyLinePeriodPrev.addNewPoint(TickTime, (1.31 + (double) fastKperiod / 10000));
//    print(" fastKperiod=" + fastKperiod + " " + (1.29 + (double) fastKperiod / 10000));


//    for (int i = 0; i < myPeriods.length; i++) {    }


//    try {
//    Object[] fractal = indicators.calculateIndicator(myInstrument, myPeriod, new OfferSide[]{OfferSide.ASK}, "stoch", new AppliedPrice[]{AppliedPrice.MEDIAN_PRICE}, new Object[]{fastKperiod, 3, MaType.SMA.ordinal(), 3, MaType.SMA.ordinal()}, 0);
//    log("ASK fractal.length=" + fractal.length + " fractal[0]=" + fractal[0] + " fractal[1]=" + fractal[1]);
//    } catch (Exception e) {
//      e.printStackTrace();e.
//      log("ERROR: fractal" + e);
//    }
/*    stoch_Curr = (indicators.stoch(myInstrument, myPeriod, OfferSide.ASK, fastKperiod, 3, MaType.WMA, 3, MaType.WMA, 1)[0] + indicators.stoch(myInstrument, myPeriod, OfferSide.BID, fastKperiod, 3, MaType.WMA, 3, MaType.WMA, 1)[0]) * .5;
    //    log("stoch  st.length=" + st.length + " st[0]=" + st[0] + " st[1]=" + st[1]);
    //    st = indicators.stochF(myInstrument, myPeriod, OfferSide.BID, fastKperiod, 3, MaType.WMA, 0);
    //    log("stochF st.length=" + st.length + " st[0]=" + st[0] + " st[1]=" + st[1]);
    //    context.stop();
    
    polyLineStochCurrH.addNewPoint(TickTime, (1.32 + stoch_Curr / 10000));
    stoch_Prev = (indicators.stoch(myInstrument, myPeriod, OfferSide.ASK, fastKperiod, 3, MaType.WMA, 3, MaType.WMA, 2)[0] + indicators.stoch(myInstrument, myPeriod, OfferSide.BID, fastKperiod, 3, MaType.WMA, 3, MaType.WMA, 2)[0]) * .5;
    polyLineStochPrevH.addNewPoint(TickTime, (1.32 + stoch_Prev / 10000));
    //    print(" fastKperiod=" + fastKperiod + " stoch_Curr=" + stoch_Curr+" "+(1.3 + stoch_CurrH / 10000)+" stoch_Prev="+stoch_Prev+" "+ (1.3 + stoch_Prev / 10000));
    //    }
     */

    // Need conditions to performe trades ____________________________________________
//    if (period == this.myPeriod && askbar.getVolume() > 0 && isTradingAllowed(myInstrument, BarTime) && !isFriday(BarTime)) {

  } // onTick

  private void log(String string) {
    if (loggingEnabled) {
      console.getOut().println(dateFormat.format(TickTime) + " " + string);
    }
  } // log

  /*  private void log(String comment) {
  commentStr = commentStr + comment + "\n";
  }*/
  // count openned positions, from another trader's strategy
  private int positionsTotal(Instrument instrument) throws JFException {
    int counter = 0;
    for (IOrder order : engine.getOrders(instrument)) {
      if (order.getState() == IOrder.State.FILLED) {
        counter++;
      }
    }
    return counter;
  } // positionsTotal

  // Count pips win/lost for long/short respectively (printed in onStop). Also resets Cash.
  private void countCash() {
    if (activeOrder != null && activeOrder.getState() == IOrder.State.CLOSED) {

      double openPrice = activeOrder.getOpenPrice();
      double closePrice = activeOrder.getClosePrice();

      if (activeOrder.isLong()) {
        diffLong = closePrice - openPrice;
        totalLong += diffLong;
        if (diffLong > 0) {
          winCount++;
        } else {
          lossCount++;
        }
      } else if (!activeOrder.isLong()) {
        diffShort = openPrice - closePrice;
        totalShort += diffShort;
        if (diffShort > 0) {
          winCount++;
        } else {
          lossCount++;
        }
      }
      tradeCount++;
      activeOrder = null;
      stopSet = false;
      TP = Double.NaN;
      SL = Double.NaN;
    }
  } // countCash

  private void stopLossATR(Instrument instrument, ITick tick) throws JFException {
//    double currentStopLossPrice = activeOrder.getStopLossPrice();
//    double bid = tick.getBid();
//    double ask = tick.getAsk();
//    double moveStopLong = bid - step;
//    double moveStopShort = ask + step;
    if (true) {
      return;
    }
//    long TickTime = tick.getTime();
    //    if (activeOrder.isLong()) {
    //    double[] f;
    /*    // some code from http://www.dukascopy.com/swiss/english/forex/jforex/forum/viewtopic.php?f=6&t=17198
    int valuesBack = 2;
    //      Double fractalMax = Double.NaN;
    List<Double> fMax = new ArrayList<Double>(valuesBack);
    List<Double> fMin = new ArrayList<Double>(valuesBack);
    int maxCount = 0;
    int minCount = 0;
    int shift = 1;
    while (maxCount < valuesBack || minCount < valuesBack) {
    double[] fractal = indicators.fractal(myInstrument, myPeriod, OfferSide.BID, 2, shift);
    if (!Double.isNaN(fractal[0]) && maxCount < valuesBack) {
    fMax.add(fractal[0]);
    maxCount++;
    }
    if (!Double.isNaN(fractal[1]) && minCount < valuesBack) {
    fMin.add(fractal[1]);
    minCount++;
    }
    shift++;
    }     */
    //    double[] fractal = indicators.fractalLines(myInstrument, myPeriod, OfferSide.BID, 2, 1);
    Double fractalMax = (Double) indicators.calculateIndicator(myInstrument, myPeriods[0], new OfferSide[]{OfferSide.ASK}, "FractalLines", new AppliedPrice[]{AppliedPrice.MEDIAN_PRICE}, new Object[]{2}, 0)[0];
    Double fractalMin = (Double) indicators.calculateIndicator(myInstrument, myPeriods[0], new OfferSide[]{OfferSide.BID}, "FractalLines", new AppliedPrice[]{AppliedPrice.MEDIAN_PRICE}, new Object[]{2}, 0)[1];
//    Double fractalMax = (Double) fractal[0];
//    Double fractalMin = (Double) fractal[1];
//    log("fractalMin=" + fractalMin + " fractalMax=" + fractalMax);


//OK      double[][] fractal = indicators.fractal(myInstrument, myPeriod, OfferSide.BID, 2, Filter.NO_FILTER, 1, BarTime-shift*300000, 0);
//OK      double[][] fractal = indicators.fractal(myInstrument, myPeriod, OfferSide.BID, 2, BarTime-shift*300000, BarTime); // 2:3,11 
//    double pipValue = myInstrument.getPipValue();
    tickPriceAverage = (tick.getBid() + tick.getAsk()) / 2;
    double fillPrice = activeOrder.getOpenPrice();
    double StopLossPrice = activeOrder.getStopLossPrice();
    double TakeProfitPrice = activeOrder.getTakeProfitPrice();
    double newStopLossPrice = roundPip(activeOrder.isLong() ? tickPriceAverage - step : tickPriceAverage + step);// TODO new constant here
    double newTakeProfitPrice = roundPip(activeOrder.isLong() ? fractalMax : fractalMin);
//    log(" " + fractalMin + " " + fMin.get(1));
    if (StopLossPrice == 0) {
//    context.stop();

      //    log(" LimitPriceLo="+LimitPriceLo+" LimitPriceHi="+LimitPriceHi);
//      double LimitPriceLo = fillPrice - step * 0.525; //0.325  0.75)); // TODO ne4w constant here
//      double LimitPriceHi = fillPrice + (step * 0.525); // TODO new constant here
//      newStopLossPrice = activeOrder.isLong() ? Math.min(fillPrice - step, newStopLossPrice) : Math.max(fillPrice + step, newStopLossPrice);
      activeOrder.setStopLossPrice(newStopLossPrice);
      if (!stopSet) {
        listStopLoss = new ArrayList<listPoint>(0);
        listsStopLoss.add(listStopLoss);
        listStopLoss.add(new listPoint(TickTime, newStopLossPrice));
        newTakeProfitPrice = activeOrder.isLong() ? fillPrice + step : fillPrice - step; // TODO we can try to calculate TP here 
        activeOrder.setTakeProfitPrice(newTakeProfitPrice);
        listTakeProfit = new ArrayList<listPoint>(0);
        listsTakeProfit.add(listTakeProfit);
        listTakeProfit.add(new listPoint(TickTime, newTakeProfitPrice));
        listBreakEven = new ArrayList<listPoint>(0);
        listsBreakEven.add(listBreakEven);
        stopSet = true;
      }
      log("stopSet=" + stopSet);
    } else {
      listStopLoss.add(new listPoint(TickTime, StopLossPrice));
      listTakeProfit.add(new listPoint(TickTime, TakeProfitPrice));
//      if (activeOrder.getState().equals(IOrder.State.FILLED)) { // State.FILLED 
      if (activeOrder.isLong() && newStopLossPrice > StopLossPrice && newStopLossPrice < tick.getBid()) { // TODO  - 0.00155 new constant here
        activeOrder.setStopLossPrice(newStopLossPrice);
        listStopLoss.add(new listPoint(TickTime, newStopLossPrice));
//        context.stop();
      }
      if (!activeOrder.isLong() && newStopLossPrice < StopLossPrice && newStopLossPrice > tick.getAsk()) { // TODO  + 0.00155 new constant here
        activeOrder.setStopLossPrice(newStopLossPrice);
        listStopLoss.add(new listPoint(TickTime, newStopLossPrice));
//        context.stop();
      }
      if (activeOrder.isLong() && stoch_Curr[0] > hi_Stoch && newTakeProfitPrice < TakeProfitPrice) { // TODO  + 0.00155 new constant here  && newTakeProfitPrice > tick.getBid()
        activeOrder.setTakeProfitPrice(newTakeProfitPrice);
        listTakeProfit.add(new listPoint(TickTime, newTakeProfitPrice));
      }
      if (!activeOrder.isLong() && stoch_Curr[0] < lo_Stoch && newTakeProfitPrice > TakeProfitPrice && newTakeProfitPrice < tick.getBid()) { // TODO  + 0.00155 new constant here
        activeOrder.setTakeProfitPrice(newTakeProfitPrice);
        listTakeProfit.add(new listPoint(TickTime, newTakeProfitPrice));
      }

      /*        
      if (!moveTrail) {
      
      //        for (IOrder orderInMarket : engine.getOrders()) 
      //        fillPrice = activeOrder.getOpenPrice();
      diff = (fillPrice - stop) * 0.5; // TODO constant: what this is? 0.35
      isLong = activeOrder.isLong();
      //        if (!moveTrail) {
      listBreakEven.addNewPoint(TickTime, fillPrice + diff);
      
      // ___ Move to Breakeven ___
      if (isLong && diff > 0 && tick.getBid() > (fillPrice + diff)) {
      newStop = fillPrice + (instrument.getPipValue() * StopLossAtBreakEvenPips);
      activeOrder.setStopLossPrice(newStop);
      log(new java.text.SimpleDateFormat("HH:mm:ss").format(TickTime) + " " + activeOrder.getLabel() + ": Moved STOP to breakeven");
      listBreakEven.addNewPoint(TickTime, newStop);
      listStopLoss.addNewPoint(TickTime, newStop);
      moveTrail = true;
      //          context.stop();
      } else if (!isLong && diff < 0 && tick.getAsk() < (fillPrice + diff)) {
      newStop = fillPrice - (instrument.getPipValue() * StopLossAtBreakEvenPips);
      activeOrder.setStopLossPrice(newStop);
      log(new java.text.SimpleDateFormat("HH:mm:ss").format(TickTime) + " " + activeOrder.getLabel() + ": Moved STOP to breakeven");
      listBreakEven.addNewPoint(TickTime, newStop);
      listStopLoss.addNewPoint(TickTime, newStop);
      moveTrail = true;
      }
      
      } else {
      updateStopLoss(activeOrder, tick);
      }*/
    }
    //if (currentStopLossPrice + pipsInFavor < moveStopLong) {
    //activeOrder.setStopLossPrice(moveStopLong);
    //}
/*    } else if (!activeOrder.isLong()) {
    if (!stopSet) {
    //    log(" LimitPriceLo="+LimitPriceLo+" LimitPriceHi="+LimitPriceHi);
    activeOrder.setStopLossPrice(activeOrder.isLong() ? LimitPriceHi:LimitPriceLo);
    listStopLoss.addNewPoint(TickTime, activeOrder.isLong() ? LimitPriceHi:LimitPriceLo);
    activeOrder.setTakeProfitPrice(activeOrder.isLong() ? LimitPriceLo:LimitPriceHi);
    stopSet = true;
    log("stopSet="+stopSet);
    listTakeProfit.addNewPoint(TickTime, activeOrder.isLong() ? LimitPriceLo:LimitPriceHi);
    } else if (moveTrail == true) {
    updateStopLoss(activeOrder, tick);
    }
    //if (currentStopLossPrice - pipsInFavor > moveStopShort) {
    //activeOrder.setStopLossPrice(moveStopShort);
    //}
    }*/

//    log("stopLossATR done.");
  } // stopLossATR

  //Update stoploss value of open position:
  private void updateStopLoss(IOrder order, ITick tick) throws JFException {
    double newStopLoss = 0.0;
//    long TickTime = tick.getTime();

    if (order.getState().equals(IOrder.State.FILLED)) { //Order can already be close here, need to check order's state

      if (order.isLong()) {
        newStopLoss = roundPip(tick.getAsk() - order.getInstrument().getPipValue() * trailStep);

        if (newStopLoss > order.getStopLossPrice() && newStopLoss < tick.getAsk() - 0.00155) { // TODO new constant here
          order.setStopLossPrice(newStopLoss);
          listStopLoss.add(new listPoint(TickTime, newStopLoss));
        }
      }

      if (!order.isLong()) {
        newStopLoss = roundPip(tick.getBid() + order.getInstrument().getPipValue() * trailStep);

        if (newStopLoss < order.getStopLossPrice() && newStopLoss > tick.getAsk() + 0.00155) { // TODO new constant here
          order.setStopLossPrice(newStopLoss);
          listStopLoss.add(new listPoint(TickTime, newStopLoss));
        }
      }
    }
  } // updateStopLoss

  // rounding to nearest half, 0, 0.5, or 1
  private double roundPip(double value) {
    int pipsMultiplier = value <= 20 ? 10000 : 100;
    int rounded = (int) (value * pipsMultiplier * 10 + 0.5);
    rounded *= 2;
    rounded = (int) ((rounded) / 10d + 0.5d);
    value = (rounded) / 2d;
    value /= pipsMultiplier;
    return value;
  } // roundPip

  private boolean isFriday(long barTime) {

    Calendar c = Calendar.getInstance();
    c.setTimeZone(TimeZone.getTimeZone("GMT"));
    c.setTimeInMillis(barTime);
    int day_of_week = c.get(Calendar.DAY_OF_WEEK);
    int hour = c.get(Calendar.HOUR_OF_DAY);

    if (day_of_week == Calendar.FRIDAY && hour > 13) {
      return true;
    } else {
      return false;
    }
  } // isFriday

  private boolean isTradingAllowed(Instrument instrument, long timeNow) throws JFException {
    calendar.setTimeInMillis(timeNow);
    long hourNow = calendar.get(Calendar.HOUR_OF_DAY);
    if (((TradingHourStart >= TradingHourStop && (hourNow >= TradingHourStart || hourNow < TradingHourStop)) || (TradingHourStart < TradingHourStop && hourNow >= TradingHourStart && hourNow < TradingHourStop)) && !hasOpenedOrder()) { // TODO new constants 
      return true;
    } else {
      NeutralZoneTouched = false;
      return false;
    }
  } // isTradingAllowed

  private static boolean isTestTime(long timeNow, Date StopDateTime) {
    Date tickTime = new Date(timeNow);
    return tickTime.before(StopDateTime);
  } // isTestTime

  private boolean erase_isTestTime(long timeNow) throws JFException {
    if (true) {
      return true;
    }
    calendar.setTimeInMillis(timeNow);
    long hourNow = calendar.get(Calendar.HOUR_OF_DAY);
//    if (((TradingHourStart >= TradingHourStop && (hourNow >= TradingHourStart || hourNow < TradingHourStop)) || (TradingHourStart < TradingHourStop && hourNow >= TradingHourStart && hourNow < TradingHourStop))) { // TODO new constants 
    if (hourNow < TradingHourStop) { // TODO new constants
      return true;
    } else {
      return false;
    }
  }// closes isTradingAllowed()

  private boolean hasOpenedOrder() throws JFException {
    List<IOrder> orders = engine.getOrders();
    if (orders.size() > 0) {
      return true;
    } else {
      return false;
    }
  } // hasOpenedOrder

  /*
  private String getLabel(Instrument instrument) {
  String label = instrument.name();
  label = label.substring(0, 2) + label.substring(3, 5);
  label = "CashStochAvec." + label;
  return label;
  }// closes getLabel()
   */
  /**
   * 
   * @param message
   * @throws JFException
   */
  @Override
  public void onMessage(IMessage message) throws JFException {
  } // onMessage

  /**
   * 
   * @throws JFException
   */
  @Override
  public void onStop() throws JFException {

    if (chart != null) {
      double bias = Math.round(BidMin * 100) / 100d;

      for (int level : new int[]{max_Stoch, hi_Stoch, 50, lo_Stoch, min_Stoch}) {
        IHorizontalLineChartObject hl = chart.getChartObjectFactory().createHorizontalLine();
        hl.setColor(myColors[0]);
        hl.setText("hl " + level);
        hl.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[]{5}, 0)); // {5} dashed line
        hl.setPrice(0, (bias - 1E-2 + (double) level / 1E4d));
        chart.addToMainChart(hl);
      }

      for (int level : new int[]{0, 3, 60}) {
        IHorizontalLineChartObject hl = chart.getChartObjectFactory().createHorizontalLine();
        hl.setColor(myColors[0]);
        hl.setText("hl " + level);
        hl.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[]{5}, 0)); // {5} dashed line
        hl.setPrice(0, (bias - 2E-2 + (double) level / 1E4d));
        chart.addToMainChart(hl);
      }

      log(" listStopLoss PointsCount=" + listStopLoss.size());
      FixAddToMainChartPolyLine(0, 1, listStopLoss, toMainChart, Color.RED, 1, "StopLoss");
      log(" listTakeProfit PointsCount=" + listTakeProfit.size());
      FixAddToMainChartPolyLine(0, 1, listTakeProfit, toMainChart, Color.BLUE, 1, "TakeProfit");

      for (int i = 0; i < myPeriods.length; i++) { //myPeriods.length
        log(" listTrend " + myPeriods[i].name() + " PointsCount=" + listsStoch.get(i).size());
        FixAddToMainChartPolyLine(bias, 1E4d, listsTrend.get(i), toMainChart, myColors[i], 1, "polyLinesTrend " + i + " " + myPeriods[i].toString());
        log(" listsStoch " + myPeriods[i].name() + " PointsCount=" + listsStoch.get(i).size());
        FixAddToMainChartPolyLine(bias - 1E-2d, 1E4d, listsStoch.get(i), toMainChart, myColors[i], 1, "polyLinesStoch " + i + " " + myPeriods[i].toString());
//        log(" listsPeriod " + myPeriods[i].name() + " PointsCount=" + listsPeriod.get(i).size());
//        FixAddToMainChartPolyLine(bias - 2E-2, 1E4d, listsPeriod.get(i), toMainChart, myColors[i], 1, myPeriods[i].toString());
//        log(" listsFractalMaxCen " + myPeriods[i].name() + " PointsCount=" + listsFractalMaxCen.get(i).size());
//        FixAddToMainChartPolyLine(0, 1, listsFractalMaxCen.get(i), toMainChart, myColors[i], 1, myPeriods[i].toString());
//        log(" listsFractalMinCen " + myPeriods[i].name() + " PointsCount=" + listsFractalMinCen.get(i).size());
//        FixAddToMainChartPolyLine(0, 1, listsFractalMinCen.get(i), toMainChart, myColors[i], 1, myPeriods[i].toString());

        /* PolyLineArr[i] = listFractalMinCe.get(i);
        for (int j = smaMin; j < smaMax; j++) {
        //          log("bias=" + bias + " 3E-2d=" + (3E-2d) + " j/2E4d=" + (j / 1E4d) + " i/1E5d=" + (i / 1E5d) + " =" + (bias - 3E-2d + (((double) j) / 1E4d) + (((double) i) / 1E5d)));
        FixAddToMainChartPolyLine(bias - 3E-2d + (((double) i) / 5E4d), 1E4d, PolyLineArr[i][j], toMainChart, myColors[i], 1, myPeriods[i].toString());
        }*/

      }

      for (int i = 1; i < myPeriods.length; i++) { //myPeriods.length
        log(" listsFractalMaxSto " + myPeriods[i].name() + " PointsCount=" + listsFractalMaxSto.get(i).size());
        FixAddToMainChartPolyLine(0, 1, listsFractalMaxSto.get(i), toMainChart, myColors[i], 1, "polyLinesFractalMaxSto " + i + " " + myPeriods[i].toString());
        log(" listsFractalMinSto " + myPeriods[i].name() + " PointsCount=" + listsFractalMinSto.get(i).size());
        FixAddToMainChartPolyLine(0, 1, listsFractalMinSto.get(i), toMainChart, myColors[i], 1, "polyLinesFractalMinSto " + i + " " + myPeriods[i].toString());
      }
    }

    if (false) {
      return;
    }

    for (List<listPoint> myList : listsStopLoss) {
      log(" listStopLoss PointsCount=" + myList.size());
      FixAddToMainChartPolyLine(0, 1, myList, toMainChart, Color.RED, 1, "StopLoss");
    }

    for (List<listPoint> myList : listsTakeProfit) {
      log(" listTakeProfit PointsCount=" + myList.size());
      FixAddToMainChartPolyLine(0, 1, myList, toMainChart, Color.BLUE, 1, "TakeProfit");
    }

    for (List<listPoint> myList : listsBreakEven) {
      log(" listBreakEven PointsCount=" + myList.size());
      FixAddToMainChartPolyLine(0, 1, myList, toMainChart, Color.MAGENTA, 1, "BreakEven");
    }

    log(" listStepHi PointsCount=" + listStepHi.size());
    FixAddToMainChartPolyLine(0, 1, listStepHi, toMainChart, Color.MAGENTA, 0.2, "StepHi");
    log(" listStepLo PointsCount=" + listStepLo.size());
    FixAddToMainChartPolyLine(0, 1, listStepLo, toMainChart, Color.MAGENTA, 0.2, "StepLo");

//    FixAddToMainChartPolyLine(polyLinePeriodCurr, toMainChart, Color.BLUE, 1, "PeriodCurr");
//    FixAddToMainChartPolyLine(polyLinePeriodPrev, toMainChart, Color.MAGENTA, 1, "PeriodPrev");

    log(" listAsk PointsCount=" + listStepHi.size());
    FixAddToMainChartPolyLine(0, 1, listAsk, toMainChart, Color.RED, 1, "Ask");
    log(" listBid PointsCount=" + listStepHi.size());
    FixAddToMainChartPolyLine(0, 1, listBid, toMainChart, Color.BLUE, 1, "Bid");
    log("HTperiodMin=" + HTperiodMin);
    log("CashStochAvec Stopped ...");
  } // onStop

  /**
   * 
   * @param period
   * @return
   * @throws JFException
   */
  public double calcPeriod(Period period) throws JFException {
    double HT_period = Double.NaN;
    double htp = (indicators.ht_dcperiod(myInstrument, period, OfferSide.BID, AppliedPrice.CLOSE, 1) - 9.) * 4. + 3.;
//      double htp = (indicators.ht_dcperiod(myInstrument, period, OfferSide.BID, AppliedPrice.CLOSE, Filter.WEEKENDS, 1, BarStartTime, 0)[0] - 10) * 2 + 3;
    if (!Double.isNaN(htp)) {
      HT_period = Math.round(htp);
      if (HT_period < 3) {
        log("ERROR: " + period.name() + " htp=" + htp + " HT_period=" + HT_period);
        context.stop();
      }
    }
    return HT_period;
  } // calcPeriod

  /**
   * 
   * @param period
   * @param fastKPeriod
   * @return
   * @throws JFException
   */
  public double calcStoch(Period period, double fastKPeriod) throws JFException {
    double stoch = Double.NaN;
    if (!Double.isNaN(fastKPeriod)) {

//      if (HTperiod < 3) {
//        log("ERROR: " + period.name() + " htp=" + htp + " HTperiod=" + HTperiod);
//        context.stop();
//      }

//        stoch = (indicators.stoch(myInstrument, period, OfferSide.BID, (int) HTperiod, 3, MaType.KAMA, 3, MaType.KAMA, Filter.WEEKENDS, 1, BarStartTime, 0)[0][0] + indicators.stoch(myInstrument, period, OfferSide.ASK, (int) HTperiod, 3, MaType.KAMA, 3, MaType.KAMA, Filter.WEEKENDS, 1, BarStartTime, 0)[0][0]) * .5;
      stoch = (indicators.stoch(myInstrument, period, OfferSide.ASK, (int) fastKPeriod, slowKPeriod, maType, slowDPeriod, maType, 1)[0] + indicators.stoch(myInstrument, period, OfferSide.BID, (int) fastKPeriod, slowKPeriod, maType, slowDPeriod, maType, 1)[0]) * .5;
//        stoch_Curr = (indicators.stochRsi(myInstrument, myPeriods[i], OfferSide.ASK, AppliedPrice.CLOSE, (int) HTperiod, 3, 3, MaType.KAMA, 1)[0] + indicators.stochRsi(myInstrument, myPeriods[i], OfferSide.BID, AppliedPrice.CLOSE, (int) HTperiod, 3, 3, MaType.KAMA, 1)[0]) * .5;
    }
    return stoch;
  } // calcStoch

  private void FixAddToMainChartPolyLine(double bias, double div, List<listPoint> ArrayNaN, boolean toMainChart, Color color, double Opacity, String str) { // 20111004

    if (ArrayNaN == null || ArrayNaN.isEmpty()) {
      log("FixAddToMainChartPolyLine: null=" + (ArrayNaN == null) + " or zero=" + (ArrayNaN.isEmpty()) + " length. return ");
      return;
    }

    int PointsCount = ArrayNaN.size();
    int nanCnt = 0;
    /*
    for (int j = 0; j < ArrayNaN.getPointsCount(); j++) {
    if (Double.isNaN(ArrayNaN.getPrice(j))) {
    nanCnt++;
    }
    }
    log(" chart.addToMainChart (ArrayNaN) " + str + " PointsCount=" + PointsCount + " nanCnt=" + nanCnt);
     */

    List<listPoint> tmpArray = new ArrayList<listPoint>(0);
    List<List<listPoint>> tmpArrays = new ArrayList<List<listPoint>>(0);

    int lastId = PointsCount - 1;
    for (int i = 0; i < lastId; i++) {
      if (Double.isNaN(ArrayNaN.get(i).Price)) {
        nanCnt++;
        continue;
      }
      tmpArray.add(new listPoint(ArrayNaN.get(i).Time, bias + ArrayNaN.get(i).Price / div));
      if (Double.isNaN(ArrayNaN.get(i + 1).Price)) {
        tmpArrays.add(tmpArray);
        tmpArray = new ArrayList<listPoint>(0);; //chart.getChartObjectFactory().createPolyLine();
      }
    }
    if (!Double.isNaN(ArrayNaN.get(lastId).Price)) {
      tmpArray.add(new listPoint(ArrayNaN.get(lastId).Time, bias + ArrayNaN.get(lastId).Price / div));
    } else {
      nanCnt++;
    }
    if (tmpArray.size() > 0) {
      tmpArrays.add(tmpArray);
    } else {
      log("WARN: FixAddToMainChartPolyLine: [" + str + "] PointsCount=" + tmpArray.size() + " nanCnt=" + nanCnt + " Color=" + color + " Opacity=" + Opacity + ". return");
      return;
    }
//    log(" chart.addToMainChart " + str + " tmpArrays=" + tmpArrays.size() + " last tmpArray PointsCount=" + tmpArrays.get(tmpArrays.size() - 1).getPointsCount());

    List<List<listPoint>> tmpArraysUnique = new ArrayList<List<listPoint>>(0);
    for (List<listPoint> myArray : tmpArrays) {
      List<listPoint> tmpArrayUnique = new ArrayList<listPoint>(0);
      tmpArrayUnique.add(new listPoint(myArray.get(0).Time, myArray.get(0).Price));
      lastId = myArray.size() - 1;
      for (int i = 1; i < lastId; i++) {
        double CurrentValue = myArray.get(i).Price;
        if (Double.compare(CurrentValue, myArray.get(i - 1).Price) != 0 || Double.compare(CurrentValue, myArray.get(i + 1).Price) != 0) {
          tmpArrayUnique.add(new listPoint(myArray.get(i).Time, CurrentValue));
        }
      }
      tmpArrayUnique.add(new listPoint(myArray.get(lastId).Time, myArray.get(lastId).Price));
      tmpArraysUnique.add(tmpArrayUnique);
    }
//    log(" chart.addToMainChart " + str + " tmpArraysUnique=" + tmpArrays.size());

    List<IPolyLineChartObject> polyLinesShort = new ArrayList<IPolyLineChartObject>(0);
    for (List<listPoint> myArray : tmpArraysUnique) {
      PointsCount = myArray.size();
      IPolyLineChartObject polyLineShort = chart.getChartObjectFactory().createPolyLine();
      int breakAt = 1 + (int) Math.floor(PointsCount / Math.ceil(PointsCount / (IPolyLineChartObject.MAX_POINTS_COUNT - 1))); // 149.
      lastId = myArray.size() - 1;
      for (int i = 0; i < lastId; i++) {
        polyLineShort.addNewPoint(myArray.get(i).Time, myArray.get(i).Price);
        if ((i + 1) % breakAt == 0) {
          polyLinesShort.add(polyLineShort);
          polyLineShort = chart.getChartObjectFactory().createPolyLine();
          polyLineShort.addNewPoint(myArray.get(i).Time, myArray.get(i).Price);
        }
      }
      polyLineShort.addNewPoint(myArray.get(lastId).Time, myArray.get(lastId).Price);
      polyLinesShort.add(polyLineShort);
    }
//    log(" chart.addToMainChart " + str + " polyLinesShort=" + polyLinesShort.size());

    for (IPolyLineChartObject myPL : polyLinesShort) {
      PointsCount = myPL.getPointsCount();
      /*
      nanCnt = 0;
      for (int j = 0; j < myPL.getPointsCount(); j++) {
      if (Double.isNaN(myPL.getPrice(j))) {
      nanCnt++;
      }
      }
      log(" chart.addToMainChart (polyLineShort) " + str + " PointsCount=" + PointsCount + " nanCnt=" + nanCnt);*/
      if (PointsCount < 2) {
        log("WARN: FixAddToMainChartPolyLine: polyLineShort [" + str + "] to short. PointsCount=" + PointsCount + " Color=" + color + " Opacity=" + Opacity + ".");
        continue;
      }
      if (PointsCount == 2) {
        if (Double.compare(myPL.getTime(0), myPL.getTime(1)) == 0) {
          log("WARN: FixAddToMainChartPolyLine: [" + str + "] time index equal. " + dateFormat.format(myPL.getTime(0)) + " PointsCount=" + PointsCount + " Color=" + color + " Opacity=" + Opacity + ". return");
          continue;
        }

        IShortLineChartObject ShortLine = chart.getChartObjectFactory().createShortLine();
        ShortLine.setPrice(0, myPL.getPrice(0));
        ShortLine.setTime(0, myPL.getTime(0));
        ShortLine.setPrice(1, myPL.getPrice(1));
        ShortLine.setTime(1, myPL.getTime(1));
        ShortLine.setColor(color);
        ShortLine.setOpacity((float) Opacity);
        if (toMainChart) {
          chart.addToMainChart(ShortLine);
//          plCount++;
        } else {
          //            chart.addToSubChart(ChId, indId, polyLineShort);
        }
        log("FixAddToMainChartPolyLine: " + " ShortLine [" + str + "] added. " + ShortLine.getPrice(0) + " " + ShortLine.getPrice(1));
      } else {
        if (toMainChart) {
          myPL.setColor(color);
          //myPL.setText("myPL " + str);
          myPL.setOpacity((float) Opacity);
          chart.addToMainChart(myPL);
          plCount++;
        } else {
          //            chart.addToSubChart(ChId, indId, polyLineShort);
        }
        nanCnt = 0;
        for (int j = 0; j < myPL.getPointsCount(); j++) {
          if (Double.isNaN(myPL.getPrice(j))) {
            nanCnt++;
          }
        }
        log(" " + plCount + " chart.addToMainChart(polyLineShort) [" + str + "] added. PointsCount=" + myPL.getPointsCount() + " nanCnt=" + nanCnt);
      }
    }
  } // FixAddToMainChartPolyLine

// Support: Some helper methods for printing
// http://www.swfx-marketplace.com/swiss/english/forex/jforex/forum/viewtopic.php?f=7&t=42704&sid=47d5107e8bf94d059852be3b2dfd9835
  private void print(Object... o) {
    for (Object ob : o) {
      console.getOut().print(ob + "  ");
    }
    console.getOut().println();
  }

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

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

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

  /*
  private void printIndicatorInfos(IIndicator ind) {
  for (int i = 0; i < ind.getIndicatorInfo().getNumberOfInputs(); i++) {
  print(ind.getIndicatorInfo().getName() + " Input " + ind.getInputParameterInfo(i).getName() + " " + ind.getInputParameterInfo(i).getType());
  }
  for (int i = 0; i < ind.getIndicatorInfo().getNumberOfOptionalInputs(); i++) {
  print(ind.getIndicatorInfo().getName() + " Opt Input " + ind.getOptInputParameterInfo(i).getName() + " " + ind.getOptInputParameterInfo(i).getType());
  }
  for (int i = 0; i < ind.getIndicatorInfo().getNumberOfOutputs(); i++) {
  print(ind.getIndicatorInfo().getName() + " Output " + ind.getOutputParameterInfo(i).getName() + " " + ind.getOutputParameterInfo(i).getType());
  }
  }*/
  /**
   * 
   * @param arr
   * @return
   */
  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;
  }

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

  /**
   * 
   * @param arr
   * @return
   */
  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;
  }

  /**
   * 
   * @param id
   * @param arr
   * @return
   * @throws JFException
   */
  public Integer[] mySort(Integer[] id, final double[] arr) throws JFException {
// http://stackoverflow.com/questions/951848/java-array-sort-quick-way-to-get-a-sorted-list-of-indices-of-an-array
    Integer[] idClone = id.clone();
    Arrays.sort(idClone, new Comparator<Integer>() {

      @Override
      public int compare(final Integer o1, final Integer o2) {
        return Double.compare(arr[o2], arr[o1]);
      }
    });
    return idClone;
  }
} // mySort
