private void validateTicksize(Instrument instrument, double price) {
    TickType ts = instrument.getTickscale();

    if (ts.canVerifyPrice()) {
      if (!ts.isValid(price)) {

        delim().append(INVALID_PRICE);

        ts.writeError(price, _err);
      }
    } else {
      ReusableString msg = TLC.instance().pop();
      msg.append(MISSING_TICK).append(instrument.getRIC());
      _log.warn(msg);
      TLC.instance().pushback(msg);
    }
  }
  private void commonValidation(Exchange ex, OrderRequest req, Order order, long now) {
    if (req.getClOrdId().length() == 0) addError(MISSING_CLORDID);

    validateHandlingInstruction(req.getHandlInst());
    validateAge(req.getTransactTime(), now);

    Instrument inst = req.getInstrument();

    final ViewString exDest = req.getExDest();
    final ViewString secEx = req.getSecurityExchange();
    final double price = order.getPendingVersion().getMarketPrice();

    if (exDest.length() > 0 && !ex.getRecCode().equals(exDest)) {
      delim()
          .append(EXDEST_MISMATCH)
          .append(exDest)
          .append(Strings.EXPECTED)
          .append(ex.getRecCode());
    }

    if (secEx.length() > 0 && !ex.getRecCode().equals(secEx)) {
      delim().append(SECEX_MISMATCH).append(secEx).append(Strings.EXPECTED).append(ex.getRecCode());
    }

    validateTicksize(inst, price);

    if (inst.isRestricted()
        && !canTradeRestricted(req.getClient(), req.getBookingType(), req.getOrderCapacity())) {
      delim()
          .append(RESTRICTED)
          .append(req.getBookingType())
          .append(Strings.ORDCAP)
          .append(req.getOrderCapacity());
    }

    if (!inst.isEnabled()) {
      delim().append(INSTRUMENT_DISABLED).append(inst.getRIC());
    }

    TradingRange band = inst.getValidTradingRange();

    band.valid(price, req.getSide().getIsBuySide(), _err);
  }
  /** dont do validation that the exchange validator is doing */
  @Override
  public boolean validate(NewOrderSingle msg, Order order) {
    reset();

    final Instrument inst = msg.getInstrument();
    final Exchange ex = inst.getExchange();

    if (!inst.isTestInstrument()) { // dont validate test instruments at ALL
      final long now = System.currentTimeMillis();

      commonValidation(ex, msg, order, now);

      final OMExchangeValidator exchangeValidator =
          (OMExchangeValidator) ex.getExchangeEventValidator();

      // qty validation done in exchangevalidator

      if (exchangeValidator != null) exchangeValidator.validate(msg, _err, now);
    }

    return _err.length() == 0;
  }
  @Override
  public boolean validate(CancelReplaceRequest newVersion, Order order) {
    reset();

    final OrderVersion lastAcc = order.getLastAckedVerion();
    final OrderRequest previous = (OrderRequest) lastAcc.getBaseOrderRequest();
    final int cumQty = order.getLastAckedVerion().getCumQty();

    final Instrument inst = newVersion.getInstrument();
    final Exchange ex = inst.getExchange();

    if (!inst.isTestInstrument()) { // dont validate test instruments at ALL
      final long now = System.currentTimeMillis();
      final int newOrdQty = newVersion.getOrderQty();

      commonValidation(ex, newVersion, order, now);

      if (newVersion.getCurrency() != previous.getCurrency()) { // CANT CHANGE CCY
        delim()
            .append(UNABLE_TO_CHANGE_CCY)
            .append(Strings.FROM)
            .append(previous.getCurrency().toString())
            .append(Strings.TO)
            .append(newVersion.getCurrency().toString());
      }

      if (newVersion.getOrdType() != previous.getOrdType()) {
        delim()
            .append(UNABLE_TO_CHANGE_ORDTYPE)
            .append(Strings.FROM)
            .append(previous.getOrdType().toString())
            .append(Strings.TO)
            .append(newVersion.getOrdType().toString());
      }

      if (newVersion.getSide() != previous.getSide()) {
        delim()
            .append(UNABLE_TO_CHANGE_SIDE)
            .append(Strings.FROM)
            .append(previous.getSide().toString())
            .append(Strings.TO)
            .append(newVersion.getSide().toString());
      }

      if (!newVersion.getSymbol().equals(previous.getSymbol())) {
        delim()
            .append(UNABLE_TO_CHANGE_SYM)
            .append(Strings.FROM)
            .append(previous.getSymbol())
            .append(Strings.TO)
            .append(newVersion.getSymbol());
      }

      if (newVersion.getPrice() == previous.getPrice()
          && newOrdQty == previous.getOrderQty()
          && newVersion.getTimeInForce() == previous.getTimeInForce()) {
        delim().append(MAJOR_FIELDS_UNCHANGED);
      }

      if (newOrdQty < cumQty) {
        delim()
            .append(CANNOT_AMEND_BELOW_CQTY)
            .append(newOrdQty)
            .append(Strings.CUMQTY)
            .append(cumQty);
      }

      final OMExchangeValidator exchangeValidator =
          (OMExchangeValidator) ex.getExchangeEventValidator();
      if (exchangeValidator != null) {
        exchangeValidator.validate(newVersion, _err, now);
      }
    }

    return _err.length() == 0;
  }