/**
   * Method runStrategy. Note the current candle is just forming Enter a tier 1-3 gap in first 5min
   * bar direction, with a 3R target and stop @ 5min high/low.
   *
   * <p>TradeOrders create TradePositions that are associated to Contracts. TradeOrders are
   * associated to TradePositions and the Tradestrategy that created them. A TradePosition may have
   * TradeOrders from multiple Tradestrategies.
   *
   * <p>TradePositions are created when there is no open TradePosition and a TradeOrder is either
   * filled or partially filled.
   *
   * <p>Note TradePositions are closed when the open quantity is zero. The new TradePosition is
   * associated to the Contract with the 1 to 1 relationship from Contract to TradePosition. The
   * TradeOrder that opened the TradePosition is marked as the open order @see
   * org.trade.persistent.dao.TradeOrder.getIsOpenPosition()
   *
   * <p>TradePosition will have the Side set to either BOT/SLD i.e. Long/Short. If an open position
   * changes from Long to Short dues to an over Sell/Buy order the side will switch.
   *
   * @param candleSeries CandleSeries the series of candles that has been updated.
   * @param newBar boolean has a new bar just started.
   * @see org.trade.strategy.AbstractStrategyRule#runStrategy(CandleSeries, boolean)
   */
  @SuppressWarnings("unused")
  public void runStrategy(CandleSeries candleSeries, boolean newBar) {

    _log.info(
        "Inside BreakEvenStrategy.runStrategy::"
            + this.getSymbol()
            + "_at_"
            + this.getCurrentCandle().getPeriod().getStart());

    try {
      // Get the current candle
      CandleItem currentCandleItem = this.getCurrentCandle();
      ZonedDateTime startPeriod = currentCandleItem.getPeriod().getStart();
      //			QuantityShares = this.getTradestrategy().getCodeValues().get(0).getCodeValue();
      //			QuantityShares =
      // this.getTradestrategy().getCodeValues().get(0).getCodeAttribute().getDefaultValue();
      QuantityShares =
          ((Long) this.getTradestrategy().getValueCode("stockSharesQuantity")).intValue();

      /*
       * Position is open kill this Strategy as its job is done. In this
       * example we would manage the position with a strategy manager.
       * This strategy is just used to create the order that would open
       * the position.
       */
      if (this.isThereOpenPosition()) {
        _log.info(
            "Strategy complete open position filled symbol: "
                + getSymbol()
                + " startPeriod: "
                + startPeriod);
        /*
         * If the order is partial filled check if the risk goes beyond
         * 1 risk unit. If it does cancel the openPositionOrder this
         * will cause it to be marked as filled.
         */
        if (OrderStatus.PARTIALFILLED.equals(this.getOpenPositionOrder().getStatus())) {
          if (isRiskViolated(
              currentCandleItem.getClose(),
              this.getTradestrategy().getRiskAmount(),
              this.getOpenPositionOrder().getQuantity(),
              this.getOpenPositionOrder().getAverageFilledPrice())) {
            this.cancelOrder(this.getOpenPositionOrder());
          }
        }
        // this.cancel();
        return;
      } else

      /*
       * Open position order was cancelled kill this Strategy as its job
       * is done.
       */
      if (null != openPositionOrderKey && !this.getTradeOrder(openPositionOrderKey).isActive()) {
        _log.info(
            "Strategy complete open position cancelled symbol: "
                + getSymbol()
                + " startPeriod: "
                + startPeriod);
        updateTradestrategyStatus(TradestrategyStatus.CANCELLED);
        // this.cancel();
        return;
      } else {

        /*
         * Create code here to create orders based on your conditions/rules.
         *
         *
         * Validamos que la estrategia solo se ejecute dentro del periodo de 9:30am a 15:00pm
         */
        /*
        if (startPeriod.isAfter(this.getTradestrategy().getTradingday().getOpen().minusSeconds(1))
        		&& startPeriod.isBefore(this.getTradestrategy().getTradingday().getClose().plusSeconds(1))
        		) {	// && newBar
         */
        /*
         * Example On start of the second (9:35) candle check the 9:30
         * candle and buy over under in the direction of the bar.
         *
         *
         * Validamos que no hayan transcurrido 25min (despues de la apertura) y que la estrategia
         * no se haya cumplido
         **/
        if (startPeriod.isBefore(this.getTradestrategy().getTradingday().getOpen().plusMinutes(25))
            && !StrategyOK) {

          Candle currentCandle = currentCandleItem.getCandle();
          Candle prevDayCandle =
              getPreviousDayCandleFromDb(
                  candleSeries,
                  startPeriod); // Obtenemos el punto P, es decir el punto de apertura del día
          // anterior
          DecimalFormat df = new DecimalFormat("#.00");
          BigDecimal currentClose =
              new BigDecimal(currentCandle.getClose().doubleValue())
                  .setScale(2, RoundingMode.HALF_EVEN);
          BigDecimal preDayClose =
              new BigDecimal(prevDayCandle.getClose().doubleValue())
                  .setScale(2, RoundingMode.HALF_EVEN);

          if (currentClose.doubleValue()
              == preDayClose
                  .doubleValue()) { // Validamos que el valor actual de LOW sea igual al de P
            StrategyOK = Boolean.TRUE;

            if (!this.isThereOpenPosition()) { // Siempre que no haya una orden abierta ...
              // Money auxStopPrice = new Money(prevDayCandle.getLow());
              Money auxStopPrice = new Money(prevDayCandle.getClose()).subtract(new Money(0.04));
              Money limitPrice = new Money(prevDayCandle.getClose());
              LastAuxStopPrice = auxStopPrice.doubleValue();

              TradeOrder tradeOrder =
                  this.createOrder(
                      this.getTradestrategy().getContract(),
                      Action.BUY,
                      OrderType.STPLMT,
                      limitPrice,
                      auxStopPrice,
                      QuantityShares,
                      false,
                      true); // Creamos y transmitimos una orden BUY, STPLMT = LOW - 4c
            }

            _log.info(
                "StrategyOK::"
                    + StrategyOK
                    + ", LastAuxStopPrice::"
                    + LastAuxStopPrice
                    + ", Symbol::"
                    + this.getSymbol());

            /*
            TradeOrder tradeOrder = new TradeOrder(this.getTradestrategy(),
            		Action.BUY, OrderType.STPLMT, 100, price,
            		price.add(new BigDecimal(0.02)),
            		TradingCalendar.getDateTimeNowMarketTimeZone());
            //tradeOrder.setClientId(clientId);
            tradeOrder.setTransmit(new Boolean(true));
            //tradeOrder.setStatus(OrderStatus.UNSUBMIT);
            this.submitOrder(this.getTradestrategy().getContract(), tradeOrder);
            */
          }

        } else if (startPeriod.isAfter(
                this.getTradestrategy().getTradingday().getOpen().plusMinutes(25))
            && !StrategyOK) { // Si han pasado 25min y no se cumplio la estrategia, cancelamos la
          // ejecución
          // this.cancel();
          return;
        } else if (StrategyOK) { // Si se ejecuto la estrategia...

          // Candle prevDayCandle = getPreviousDayCandleFromDb(candleSeries, startPeriod);

          /*
           * Is the candle in the direction of the Tradestrategy side i.e.
           * a long play should have a green 5min candle
           */
          CandleItem prevCandleItem = null;
          if (getCurrentCandleCount() > 0) {
            prevCandleItem = (CandleItem) candleSeries.getDataItem(getCurrentCandleCount() - 1);
            // AbstractStrategyRule
            // .logCandle(this, prevCandleItem.getCandle());
          }

          this.reFreshPositionOrders();
          // double lastAuxStopPrice = this.getOpenPositionOrder().getAuxPrice().doubleValue();
          if (currentCandleItem.getClose() > prevCandleItem.getClose()
              && currentCandleItem.getClose() >= addAPercentToANumber(LastAuxStopPrice, 50)) {
            /*
             * Validamos que haya un incremento en LOW entre la
             * posicion actual y la posicion anterior del dia; y
             * además, que el incremento de LOW del dia sea mayor o
             * igual a un 50% de LOW del dia anterior
             */

            /*
            Money stopPrice = addPennyAndRoundStop(this
            		.getOpenPositionOrder().getAverageFilledPrice()
            		.doubleValue(), getOpenTradePosition()
            		.getSide(), Action.BUY, 0.04);
            moveStopOCAPrice(stopPrice, true);
            */

            Money auxStopPrice =
                new Money(addAPercentToANumber(LastAuxStopPrice, 50)).subtract(new Money(0.04));
            Money limitPrice = new Money(addAPercentToANumber(LastAuxStopPrice, 50));
            LastAuxStopPrice = auxStopPrice.doubleValue();

            TradeOrder tradeOrder =
                this.updateOrder(
                    this.getOpenPositionOrder().getOrderKey(),
                    Action.BUY,
                    OrderType.STPLMT,
                    limitPrice,
                    auxStopPrice,
                    QuantityShares,
                    false,
                    true); // Creamos y transmitimos una orden BUY, STPLMT = (LOW + 50%) - 4c
            this.reFreshPositionOrders();
          }
          _log.info(
              "StrategyOK::"
                  + StrategyOK
                  + ", LastAuxStopPrice::"
                  + LastAuxStopPrice
                  + ", Symbol::"
                  + this.getSymbol());
        }
      }

      /*
       * Close any opened positions with a market order at day end minus
       * one bar.
       *
      if (!currentCandleItem.getLastUpdateDate().isBefore(
      		this.getTradestrategy()
      				.getTradingday()
      				.getClose()
      				.minusMinutes(
      						this.getTradestrategy().getBarSize() / 60))) {
      	cancelOrdersClosePosition(true);
      	_log.info("Rule 15:55:00 close all open positions: "
      			+ getSymbol() + " Time: " + startPeriod);
      	this.cancel();
      }
                */
    } catch (StrategyRuleException
        | PersistentModelException
        | ClassNotFoundException
        | InstantiationException
        | IllegalAccessException
        | NoSuchMethodException
        | InvocationTargetException
        | IOException ex) {
      _log.error("Error  runRule exception: " + ex.getMessage(), ex);
      error(1, 10, "Error  runRule exception: " + ex.getMessage());
    } catch (Exception ex) {
      _log.error("Error  runRule exception: " + ex.getMessage(), ex);
      error(1, 10, "Error  runRule exception: " + ex.getMessage());
    }
  }
  /**
   * Method runStrategy. Note the current candle is just forming Enter a tier 1-3 gap in first 5min
   * bar direction, with a 3R target and stop @ 5min high/low.
   *
   * <p>TradeOrders create TradePositions that are associated to Contracts. TradeOrders are
   * associated to TradePositions and the Tradestrategy that created them. A TradePosition may have
   * TradeOrders from multiple Tradestrategies.
   *
   * <p>TradePositions are created when there is no open TradePosition and a TradeOrder is either
   * filled or partially filled.
   *
   * <p>Note TradePositions are closed when the open quantity is zero. The new TradePosition is
   * associated to the Contract with the 1 to 1 relationship from Contract to TradePosition. The
   * TradeOrder that opened the TradePosition is marked as the open order @see
   * org.trade.persistent.dao.TradeOrder.getIsOpenPosition()
   *
   * <p>TradePosition will have the Side set to either BOT/SLD i.e. Long/Short. If an open position
   * changes from Long to Short dues to an over Sell/Buy order the side will switch.
   *
   * @param candleSeries CandleSeries the series of candles that has been updated.
   * @param newBar boolean has a new bar just started.
   * @see org.trade.strategy.AbstractStrategyRule#runStrategy(CandleSeries, boolean)
   */
  public void runStrategy(CandleSeries candleSeries, boolean newBar) {

    try {
      // Get the current candle
      CandleItem currentCandleItem = this.getCurrentCandle();
      ZonedDateTime startPeriod = currentCandleItem.getPeriod().getStart();

      /*
       * Position is open kill this Strategy as its job is done. In this
       * example we would manage the position with a strategy manager.
       * This strategy is just used to create the order that would open
       * the position.
       */

      if (this.isThereOpenPosition()) {
        _log.info(
            "La posicion esta abierta para el symbol: "
                + getSymbol()
                + " startPeriod: "
                + startPeriod);
        /*
         * If the order is partial filled check if the risk goes beyond
         * 1 risk unit. If it does cancel the openPositionOrder this
         * will cause it to be marked as filled.
         */

        if (OrderStatus.PARTIALFILLED.equals(this.getOpenPositionOrder().getStatus())) {
          if (isRiskViolated(
              currentCandleItem.getClose(),
              this.getTradestrategy().getRiskAmount(),
              this.getOpenPositionOrder().getQuantity(),
              this.getOpenPositionOrder().getAverageFilledPrice())) {
            this.cancelOrder(this.getOpenPositionOrder());
          }
        }

        this.cancel();
        return;
      } else {
        try {
          CandlePivote pivote = stocksPivoting.get(getSymbol());

          if (pivote == null) {
            _log.info("PRIMER ACCESO>>>>");
            stocksPivoting.put(getSymbol(), new CandlePivote(currentCandleItem));
            _log.info(" * ******************************************** *");
            return;
          }

          CandleItem pivoteCandle = pivote.getPivote();
          _log.info(" * PIVOTE *");
          _log.info(pivoteCandle.getCandle().getContract() + " : " + pivoteCandle.getPeriod());
          _log.info("LOW: " + pivoteCandle.getLow());
          _log.info("HIGH: " + pivoteCandle.getHigh());
          _log.info("CLOSE: " + pivoteCandle.getClose());
          _log.info(" * CURRENT *");
          _log.info(getSymbol() + " : " + currentCandleItem.getPeriod());
          _log.info("LOW: " + currentCandleItem.getLow());
          _log.info("HIGH: " + currentCandleItem.getHigh());
          _log.info("CLOSE: " + currentCandleItem.getClose());
          _log.info(" ** DATA ** ");
          long diff =
              currentCandleItem.getPeriod().getFirstMillisecond()
                  - pivoteCandle.getPeriod().getFirstMillisecond();
          long diffMinutes = diff / (60 * 1000) % 60;
          // double diffLows =  Math.abs(currentCandleItem.getLow() - pivoteCandle.getLow());
          // double diffHighs =  Math.abs(currentCandleItem.getHigh() - pivoteCandle.getHigh());

          double rangoInferior = pivoteCandle.getClose() - 0.05;
          double rangoSuperior = pivoteCandle.getClose() + 0.15;

          // _log.info(getSymbol() + " :: Diff Low: "+ diffLows );
          // _log.info(getSymbol() + " :: Diff High: "+ diffHighs );
          // _log.info(getSymbol() + " :: Diff Close: "+ diffClose );
          _log.info(getSymbol() + " :: Rango Inferior: " + rangoInferior);
          _log.info(getSymbol() + " :: Rango Superior: " + rangoSuperior);
          _log.info(getSymbol() + " :: TimeElapsed: " + diffMinutes + " min. ");

          if (pivote.isCandidato()) {
            if (currentCandleItem.getClose() > rangoInferior
                && currentCandleItem.getClose() < rangoSuperior) {
              // Esta dentro del rango de valores deseados, checamos el tiempo que ha pasado
              // 180 minutos =  3 hrs
              _log.info(getSymbol() + " sigue siendo candidato");
              if (diffMinutes > 180) {
                // Si el tiempo es mayor a 3 horas, eliminamos el monitoreo
                _log.info(getSymbol() + " ya sobrepaso las 3 hrs");
                pivote.setCandidato(false);
                stocksPivoting.put(getSymbol(), pivote);
              }

            } else {

              // Salio fuera del rango, verificamos si hacemos un buy o un sell
              _log.info(getSymbol() + " es candidato y salio fuera del rango");
              double rangoInferiorSell = pivoteCandle.getClose() - 0.20;
              double rangoSuperiorBuy = pivoteCandle.getClose() + 0.20;

              if (currentCandleItem.getClose() < rangoInferiorSell) {
                // El stock sobrepaso los 20 centavos a la baja
                _log.info(getSymbol() + " sobrepaso 20 centavos a la baja. SELL");
                Money limitPrice = new Money(currentCandleItem.getLow());
                Money auxStopPrice = new Money(currentCandleItem.getLow()).add(new Money(0.10));
                _log.info(getSymbol() + " LIMIT PRICE " + limitPrice);
                _log.info(getSymbol() + " STOP PRICE " + auxStopPrice);
                int cantidadCompra =
                    ((Long) this.getTradestrategy().getValueCode("stockSharesQuantity")).intValue();
                TradeOrder tradeOrder =
                    this.createOrder(
                        this.getTradestrategy().getContract(),
                        Action.SELL,
                        OrderType.STPLMT,
                        limitPrice,
                        auxStopPrice,
                        cantidadCompra,
                        false,
                        true); // Creamos y transmitimos una orden BUY, STPLMT = LOW - 4c
                this.reFreshPositionOrders();
                _log.info(" * ******************************************** *");
                return;
              }

              if (currentCandleItem.getClose() > rangoSuperiorBuy) {
                _log.info(getSymbol() + " sobrepaso 20 centavos hacia arriba. COMPRAR");
                Money limitPrice = new Money(currentCandleItem.getHigh());
                Money auxStopPrice =
                    new Money(currentCandleItem.getHigh()).subtract(new Money(0.10));
                _log.info(getSymbol() + " LIMIT PRICE " + limitPrice);
                _log.info(getSymbol() + " STOP PRICE " + auxStopPrice);
                int cantidadCompra =
                    ((Long) this.getTradestrategy().getValueCode("stockSharesQuantity")).intValue();
                TradeOrder tradeOrder =
                    this.createOrder(
                        this.getTradestrategy().getContract(),
                        Action.BUY,
                        OrderType.STPLMT,
                        limitPrice,
                        auxStopPrice,
                        cantidadCompra,
                        false,
                        true); // Creamos y transmitimos una orden BUY, STPLMT = LOW - 4c
                this.reFreshPositionOrders();
                _log.info(" * ******************************************** *");
                return;
              }
            }
            System.out.println(" * ******************************************** *");
            return;
          }

          if (currentCandleItem.getClose() > rangoInferior
              && currentCandleItem.getClose() < rangoSuperior) {
            // Esta en el rango de valores deseados
            if (diffMinutes > 20) {
              // Ya han pasado mas de 20 minutos y el valor se sigue conservando
              // Definimos al pivote como candidato para ser comprado o vendido
              pivote.setCandidato(true);
              stocksPivoting.put(getSymbol(), pivote);
            } else {
              // Conservamos el pivote, aun no se llega al tiempo limite
              _log.info("CONSERVANDO PIVOTE, SIGUE EN OBSERVACION ");
              pivote.setCandidato(false);
              stocksPivoting.put(getSymbol(), pivote);
            }
          } else {
            // Esta fuera del rango de valores deseados, cambiamos el pivote al currentCandle
            _log.info("CAMBIANDO PIVOTE");
            pivote.setPivote(currentCandleItem);
            pivote.setCandidato(false);
            stocksPivoting.put(getSymbol(), pivote);
          }
          _log.info(" * ******************************************** *");
        } catch (Exception ex) {
          _log.error("Error  runRule exception: " + ex.getMessage(), ex);
          error(1, 10, "Error  runRule exception: " + ex.getMessage());
        }
      }

      /*
       * Create code here to create orders based on your conditions/rules.
       */

      // getCandle(startPeriod)

      //
      //			if (startPeriod.equals(this.getTradestrategy().getTradingday()
      //					.getOpen()
      //					.plusMinutes(this.getTradestrategy().getBarSize() / 60))
      //					&& newBar) {
      //
      //				/*
      //				 * Example On start of the second (9:35) candle check the 9:30
      //				 * candle and buy over under in the direction of the bar.
      //				*/
      //
      //
      //			}

      /*
       * Close any opened positions with a market order at day end minus
       * one bar.

      if (!currentCandleItem.getLastUpdateDate().isBefore(
      		this.getTradestrategy()
      				.getTradingday()
      				.getClose()
      				.minusMinutes(
      						this.getTradestrategy().getBarSize() / 60))) {
      	cancelOrdersClosePosition(true);
      	_log.info("Rule 15:55:00 close all open positions: "
      			+ getSymbol() + " Time: " + startPeriod);
      	this.cancel();
      }
       */
    } catch (StrategyRuleException ex) {
      _log.error("Error  runRule exception: " + ex.getMessage(), ex);
      error(1, 10, "Error  runRule exception: " + ex.getMessage());
    }
  }