diff --git a/src/main/java/com/lykke/matching/engine/daos/azure/AzureAssetPair.java b/src/main/java/com/lykke/matching/engine/daos/azure/AzureAssetPair.java index f212b9dd3..161ed2a2f 100644 --- a/src/main/java/com/lykke/matching/engine/daos/azure/AzureAssetPair.java +++ b/src/main/java/com/lykke/matching/engine/daos/azure/AzureAssetPair.java @@ -11,7 +11,6 @@ public class AzureAssetPair extends TableServiceEntity { private int accuracy; private Double minVolume; private Double minInvertedVolume; - private Double maxVolume; private Double maxValue; private Double marketOrderPriceDeviationThreshold; @@ -23,7 +22,6 @@ public AzureAssetPair(String baseAssetId, int accuracy, Double minVolume, Double minInvertedVolume, - Double maxVolume, Double maxValue, Double marketOrderPriceDeviationThreshold) { super(ASSET_PAIR, baseAssetId + quotingAssetId); @@ -32,7 +30,6 @@ public AzureAssetPair(String baseAssetId, this.accuracy = accuracy; this.minVolume = minVolume; this.minInvertedVolume = minInvertedVolume; - this.maxVolume = maxVolume; this.maxValue = maxValue; this.marketOrderPriceDeviationThreshold = marketOrderPriceDeviationThreshold; } @@ -81,14 +78,6 @@ public void setMinInvertedVolume(Double minInvertedVolume) { this.minInvertedVolume = minInvertedVolume; } - public Double getMaxVolume() { - return maxVolume; - } - - public void setMaxVolume(Double maxVolume) { - this.maxVolume = maxVolume; - } - public Double getMaxValue() { return maxValue; } @@ -113,7 +102,6 @@ public String toString() { ", accuracy=" + accuracy + ", minVolume=" + minVolume + ", minInvertedVolume=" + minInvertedVolume + - ", maxVolume=" + maxVolume + ", maxValue=" + maxValue + ", marketOrderPriceDeviationThreshold=" + marketOrderPriceDeviationThreshold + ")"; diff --git a/src/main/kotlin/com/lykke/matching/engine/daos/AssetPair.kt b/src/main/kotlin/com/lykke/matching/engine/daos/AssetPair.kt index ed56ce35f..5b2e2e515 100644 --- a/src/main/kotlin/com/lykke/matching/engine/daos/AssetPair.kt +++ b/src/main/kotlin/com/lykke/matching/engine/daos/AssetPair.kt @@ -9,7 +9,6 @@ class AssetPair( val accuracy: Int, val minVolume: BigDecimal? = null, val minInvertedVolume: BigDecimal? = null, - val maxVolume: BigDecimal? = null, val maxValue: BigDecimal? = null, val marketOrderPriceDeviationThreshold: BigDecimal? = null ) { @@ -21,7 +20,6 @@ class AssetPair( "accuracy=$accuracy, " + "minVolume=$minVolume, " + "minInvertedVolume=$minInvertedVolume, " + - "maxVolume=$maxVolume, " + "maxValue=$maxValue, " + "marketOrderPriceDeviationThreshold=$marketOrderPriceDeviationThreshold" } diff --git a/src/main/kotlin/com/lykke/matching/engine/daos/order/MaxOrderVolumeInfo.kt b/src/main/kotlin/com/lykke/matching/engine/daos/order/MaxOrderVolumeInfo.kt new file mode 100644 index 000000000..fe2493804 --- /dev/null +++ b/src/main/kotlin/com/lykke/matching/engine/daos/order/MaxOrderVolumeInfo.kt @@ -0,0 +1,16 @@ +package com.lykke.matching.engine.daos.order + +import com.lykke.matching.engine.utils.NumberUtils +import java.math.BigDecimal + +class MaxOrderVolumeInfo(private val maxValue: BigDecimal, + private val midPrice: BigDecimal) { + + val maxVolume: BigDecimal = NumberUtils.divideWithMaxScale(maxValue, midPrice) + + override fun toString(): String { + return "maxValue=${NumberUtils.roundForPrint(maxValue)}, " + + "midPrice=${NumberUtils.roundForPrint(midPrice)}, " + + "maxVolume=${NumberUtils.roundForPrint(maxVolume)}" + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/lykke/matching/engine/database/azure/AzureDictionariesDatabaseAccessor.kt b/src/main/kotlin/com/lykke/matching/engine/database/azure/AzureDictionariesDatabaseAccessor.kt index d0cf6e90b..f59dee747 100644 --- a/src/main/kotlin/com/lykke/matching/engine/database/azure/AzureDictionariesDatabaseAccessor.kt +++ b/src/main/kotlin/com/lykke/matching/engine/database/azure/AzureDictionariesDatabaseAccessor.kt @@ -34,7 +34,6 @@ class AzureDictionariesDatabaseAccessor(dictsConfig: String): DictionariesDataba asset.accuracy, asset.minVolume?.toBigDecimal(), asset.minInvertedVolume?.toBigDecimal(), - asset.maxVolume?.toBigDecimal(), asset.maxValue?.toBigDecimal(), asset.marketOrderPriceDeviationThreshold?.toBigDecimal()) } @@ -57,7 +56,6 @@ class AzureDictionariesDatabaseAccessor(dictsConfig: String): DictionariesDataba assetPair.accuracy, assetPair.minVolume?.toBigDecimal(), assetPair.minInvertedVolume?.toBigDecimal(), - assetPair.maxVolume?.toBigDecimal(), assetPair.maxValue?.toBigDecimal(), assetPair.marketOrderPriceDeviationThreshold?.toBigDecimal()) } diff --git a/src/main/kotlin/com/lykke/matching/engine/matching/MatchingEngine.kt b/src/main/kotlin/com/lykke/matching/engine/matching/MatchingEngine.kt index 45d045163..09e6a5b93 100644 --- a/src/main/kotlin/com/lykke/matching/engine/matching/MatchingEngine.kt +++ b/src/main/kotlin/com/lykke/matching/engine/matching/MatchingEngine.kt @@ -22,6 +22,7 @@ import com.lykke.matching.engine.outgoing.messages.v2.builders.bigDecimalToStrin import com.lykke.matching.engine.outgoing.messages.v2.enums.TradeRole import com.lykke.matching.engine.services.GenericLimitOrderService import com.lykke.matching.engine.order.transaction.ExecutionContext +import com.lykke.matching.engine.services.validators.common.OrderValidationUtils import com.lykke.matching.engine.utils.NumberUtils import org.springframework.stereotype.Component import java.math.BigDecimal @@ -40,11 +41,12 @@ class MatchingEngine(private val genericLimitOrderService: GenericLimitOrderServ } fun match(originOrder: Order, - orderBook: PriorityBlockingQueue, - messageId: String, balance: BigDecimal? = null, priceDeviationThreshold: BigDecimal? = null, executionContext: ExecutionContext): MatchingResult { + val assetOrderBook = executionContext.orderBooksHolder.getChangedCopyOrOriginalOrderBook(originOrder.assetPairId) + val orderBook = assetOrderBook.getOrderBook(!originOrder.isBuySide()) + val balancesGetter = executionContext.walletOperationsProcessor val orderWrapper = CopyWrapper(originOrder) val order = orderWrapper.copy @@ -85,11 +87,10 @@ class MatchingEngine(private val genericLimitOrderService: GenericLimitOrderServ val marketOrderTrades = LinkedList() - val limitOrdersReport = LimitOrdersReport(messageId) + val limitOrdersReport = LimitOrdersReport(executionContext.messageId) var totalLimitVolume = BigDecimal.ZERO var matchedWithZeroLatestTrade = false - if (checkOrderBook(order, workingOrderBook)) { while (getMarketBalance(availableBalances, order, asset) >= BigDecimal.ZERO && workingOrderBook.size > 0 && !NumberUtils.equalsWithDefaultDelta(remainingVolume, BigDecimal.ZERO) @@ -316,7 +317,6 @@ class MatchingEngine(private val genericLimitOrderService: GenericLimitOrderServ totalLimitVolume += (if (order.isStraight()) marketRoundedVolume else oppositeRoundedVolume).abs() matchedOrders.add(matchedLimitOrderCopyWrapper) } - } if (isMarketOrder && remainingVolume > BigDecimal.ZERO) { if (matchedWithZeroLatestTrade) { @@ -347,15 +347,22 @@ class MatchingEngine(private val genericLimitOrderService: GenericLimitOrderServ } val executionPrice = calculateExecutionPrice(order, assetPair, totalLimitPrice, totalVolume) - if (!checkMaxVolume(order, assetPair, executionPrice)) { - order.updateStatus(OrderStatus.InvalidVolume, now) - executionContext.info("Too large volume of market order (${order.externalId}): volume=${order.volume}, price=$executionPrice, maxVolume=${assetPair.maxVolume}, straight=${order.isStraight()}") - return MatchingResult(orderWrapper, cancelledLimitOrders) - } - if (!checkMaxValue(order, assetPair, executionPrice)) { - order.updateStatus(OrderStatus.InvalidValue, now) - executionContext.info("Too large value of market order (${order.externalId}): volume=${order.volume}, price=$executionPrice, maxValue=${assetPair.maxValue}, straight=${order.isStraight()}") - return MatchingResult(orderWrapper, cancelledLimitOrders) + if (isMarketOrder) { + val maxVolumeInfo = OrderValidationUtils.calculateMaxVolume(assetPair, assetOrderBook) + if (!checkMaxVolume(order, executionPrice, maxVolumeInfo?.maxVolume)) { + order.updateStatus(OrderStatus.InvalidVolume, now) + executionContext.info("Too large volume of market order (${order.externalId}): " + + "volume=${order.volume}, price=$executionPrice, " + + "straight=${order.isStraight()}, maxVolumeInfo: $maxVolumeInfo") + return MatchingResult(orderWrapper, cancelledLimitOrders) + } + if (!checkMaxValue(order, executionPrice, assetPair.maxValue)) { + order.updateStatus(OrderStatus.InvalidValue, now) + executionContext.info("Too large value of market order (${order.externalId}): " + + "volume=${order.volume}, price=$executionPrice, " + + "straight=${order.isStraight()}, maxValue=${assetPair.maxValue}") + return MatchingResult(orderWrapper, cancelledLimitOrders) + } } if (isMarketOrder && !checkExecutionPriceDeviation(order.isBuySide(), executionPrice, bestPrice, priceDeviationThreshold)) { order.updateStatus(OrderStatus.TooHighPriceDeviation, now) @@ -394,9 +401,6 @@ class MatchingEngine(private val genericLimitOrderService: GenericLimitOrderServ false) } - private fun checkOrderBook(order: Order, orderBook: PriorityBlockingQueue): Boolean = - orderBook.isEmpty() || orderBook.peek().assetPairId == order.assetPairId && orderBook.peek().isBuySide() != order.isBuySide() - private fun getCrossVolume(volume: BigDecimal, straight: Boolean, price: BigDecimal): BigDecimal { return if (straight) volume else NumberUtils.divideWithMaxScale(volume, price) } @@ -431,22 +435,22 @@ class MatchingEngine(private val genericLimitOrderService: GenericLimitOrderServ } private fun checkMaxVolume(order: Order, - assetPair: AssetPair, - executionPrice: BigDecimal): Boolean { + executionPrice: BigDecimal, + maxVolume: BigDecimal?): Boolean { return when { - !isMarketOrder(order) || assetPair.maxVolume == null -> true - order.isStraight() -> order.getAbsVolume() <= assetPair.maxVolume - else -> order.getAbsVolume() / executionPrice <= assetPair.maxVolume + maxVolume == null -> true + order.isStraight() -> order.getAbsVolume() <= maxVolume + else -> order.getAbsVolume() / executionPrice <= maxVolume } } private fun checkMaxValue(order: Order, - assetPair: AssetPair, - executionPrice: BigDecimal): Boolean { + executionPrice: BigDecimal, + maxValue: BigDecimal?): Boolean { return when { - !isMarketOrder(order) || assetPair.maxValue == null -> true - order.isStraight() -> order.getAbsVolume() * executionPrice <= assetPair.maxValue - else -> order.getAbsVolume() <= assetPair.maxValue + maxValue == null -> true + order.isStraight() -> order.getAbsVolume() * executionPrice <= maxValue + else -> order.getAbsVolume() <= maxValue } } diff --git a/src/main/kotlin/com/lykke/matching/engine/order/process/LimitOrderProcessor.kt b/src/main/kotlin/com/lykke/matching/engine/order/process/LimitOrderProcessor.kt index 033edf862..d5e13d010 100644 --- a/src/main/kotlin/com/lykke/matching/engine/order/process/LimitOrderProcessor.kt +++ b/src/main/kotlin/com/lykke/matching/engine/order/process/LimitOrderProcessor.kt @@ -1,6 +1,7 @@ package com.lykke.matching.engine.order.process import com.lykke.matching.engine.balance.BalanceException +import com.lykke.matching.engine.daos.AssetPair import com.lykke.matching.engine.daos.LimitOrder import com.lykke.matching.engine.daos.WalletOperation import com.lykke.matching.engine.daos.order.OrderTimeInForce @@ -45,14 +46,15 @@ class LimitOrderProcessor(private val limitOrderInputValidator: LimitOrderInputV if (preProcessorValidationResult != null && !preProcessorValidationResult.isValid) { return preProcessorValidationResult } + val assetPair = orderContext.executionContext.assetPairsById[orderContext.order.assetPairId] // fixme: input validator will be moved from the business thread after multilimit order context release - val inputValidationResult = performInputValidation(orderContext) - return if (!inputValidationResult.isValid) inputValidationResult else performBusinessValidation(orderContext) + val inputValidationResult = performInputValidation(orderContext, assetPair) + return if (!inputValidationResult.isValid) inputValidationResult else performBusinessValidation(orderContext, assetPair!!) } - private fun performInputValidation(orderContext: LimitOrderExecutionContext): OrderValidationResult { + private fun performInputValidation(orderContext: LimitOrderExecutionContext, + assetPair: AssetPair?): OrderValidationResult { val order = orderContext.order - val assetPair = orderContext.executionContext.assetPairsById[order.assetPairId] val baseAsset = assetPair?.let { orderContext.executionContext.assetsById[assetPair.baseAssetId] } try { limitOrderInputValidator.validateLimitOrder(applicationSettingsHolder.isTrustedClient(order.clientId), @@ -66,13 +68,15 @@ class LimitOrderProcessor(private val limitOrderInputValidator: LimitOrderInputV return OrderValidationResult(true) } - private fun performBusinessValidation(orderContext: LimitOrderExecutionContext): OrderValidationResult { + private fun performBusinessValidation(orderContext: LimitOrderExecutionContext, + assetPair: AssetPair): OrderValidationResult { val order = orderContext.order try { limitOrderBusinessValidator.performValidation(applicationSettingsHolder.isTrustedClient(order.clientId), order, orderContext.availableLimitAssetBalance!!, orderContext.limitVolume!!, + assetPair, orderContext.executionContext.orderBooksHolder.getChangedCopyOrOriginalOrderBook(order.assetPairId), orderContext.executionContext.date, orderContext.executionContext.getOrderBookTotalSize()) @@ -129,10 +133,7 @@ class LimitOrderProcessor(private val limitOrderInputValidator: LimitOrderInputV private fun matchOrder(orderContext: LimitOrderExecutionContext): ProcessedOrder { val executionContext = orderContext.executionContext val order = orderContext.order - val orderBook = executionContext.orderBooksHolder.getChangedCopyOrOriginalOrderBook(order.assetPairId) val matchingResult = matchingEngine.match(order, - orderBook.getOrderBook(!order.isBuySide()), - executionContext.messageId, orderContext.availableLimitAssetBalance!!, applicationSettingsHolder.limitOrderPriceDeviationThreshold(order.assetPairId), executionContext = executionContext) diff --git a/src/main/kotlin/com/lykke/matching/engine/order/process/StopLimitOrderProcessor.kt b/src/main/kotlin/com/lykke/matching/engine/order/process/StopLimitOrderProcessor.kt index 0b558cc54..080c97b9f 100644 --- a/src/main/kotlin/com/lykke/matching/engine/order/process/StopLimitOrderProcessor.kt +++ b/src/main/kotlin/com/lykke/matching/engine/order/process/StopLimitOrderProcessor.kt @@ -62,6 +62,8 @@ class StopLimitOrderProcessor(private val limitOrderInputValidator: LimitOrderIn orderContext.limitVolume, orderContext.order, orderContext.executionContext.date, + orderContext.executionContext.assetPairsById[orderContext.order.assetPairId]!!, + orderContext.executionContext.orderBooksHolder.getChangedCopyOrOriginalOrderBook(orderContext.order.assetPairId), orderContext.executionContext.getOrderBookTotalSize()) } catch (e: OrderValidationException) { return OrderValidationResult(false, false, e.message, e.orderStatus) diff --git a/src/main/kotlin/com/lykke/matching/engine/services/AssetOrderBook.kt b/src/main/kotlin/com/lykke/matching/engine/services/AssetOrderBook.kt index 15bdc9836..759a9b0fb 100644 --- a/src/main/kotlin/com/lykke/matching/engine/services/AssetOrderBook.kt +++ b/src/main/kotlin/com/lykke/matching/engine/services/AssetOrderBook.kt @@ -2,6 +2,7 @@ package com.lykke.matching.engine.services import com.lykke.matching.engine.daos.LimitOrder import com.lykke.matching.engine.services.utils.AbstractAssetOrderBook +import com.lykke.matching.engine.utils.NumberUtils import java.math.BigDecimal import java.util.* import java.util.concurrent.PriorityBlockingQueue @@ -47,6 +48,13 @@ open class AssetOrderBook(assetId: String) : AbstractAssetOrderBook(assetId) { fun getAskPrice() = askOrderBook.peek()?.price ?: BigDecimal.ZERO fun getBidPrice() = bidOrderBook.peek()?.price ?: BigDecimal.ZERO + fun getMidPrice(): BigDecimal? { + return if (!NumberUtils.equalsIgnoreScale(BigDecimal.ZERO, getAskPrice()) && !NumberUtils.equalsIgnoreScale(BigDecimal.ZERO, getBidPrice())) { + NumberUtils.divideWithMaxScale(getAskPrice() + getBidPrice(), BigDecimal.valueOf(2)) + } else + null + } + fun leadToNegativeSpread(order: LimitOrder): Boolean { val book = getOrderBook(!order.isBuySide()) if (book.isEmpty()) { diff --git a/src/main/kotlin/com/lykke/matching/engine/services/MarketOrderService.kt b/src/main/kotlin/com/lykke/matching/engine/services/MarketOrderService.kt index 047562794..3b9f9dc18 100644 --- a/src/main/kotlin/com/lykke/matching/engine/services/MarketOrderService.kt +++ b/src/main/kotlin/com/lykke/matching/engine/services/MarketOrderService.kt @@ -103,8 +103,14 @@ class MarketOrderService @Autowired constructor( feeInstruction, listOfFee(feeInstruction, feeInstructions)) try { - marketOrderValidator.performValidation(order, getOrderBook(order), feeInstruction, feeInstructions) + marketOrderValidator.performValidation(order, + genericLimitOrderService.getOrderBook(order.assetPairId), + feeInstruction, + feeInstructions) } catch (e: OrderValidationException) { + val errorMessage = "Invalid market order (${order.externalId}, messageId: ${messageWrapper.messageId}): ${e.orderStatus}" + + (if (e.message.isNotEmpty()) ", ${e.message}" else "") + LOGGER.error(errorMessage) order.updateStatus(e.orderStatus, now) sendErrorNotification(messageWrapper, order, now) writeErrorResponse(messageWrapper, order, e.message) @@ -122,8 +128,6 @@ class MarketOrderService @Autowired constructor( val marketOrderExecutionContext = MarketOrderExecutionContext(order, executionContext) val matchingResult = matchingEngine.match(order, - getOrderBook(order), - messageWrapper.messageId!!, priceDeviationThreshold = assetPair.marketOrderPriceDeviationThreshold ?: applicationSettingsHolder.marketOrderPriceDeviationThreshold(assetPair.assetPairId), executionContext = executionContext) marketOrderExecutionContext.matchingResult = matchingResult @@ -162,7 +166,7 @@ class MarketOrderService @Autowired constructor( matchingResultHandlingHelper.processWalletOperations(marketOrderExecutionContext) true } catch (e: BalanceException) { - order.updateStatus(OrderStatus.NotEnoughFunds, now) + order.updateStatus(NotEnoughFunds, now) marketOrderExecutionContext.executionContext.marketOrderWithTrades = MarketOrderWithTrades(messageWrapper.messageId!!, order) LOGGER.error("$order: Unable to process wallet operations after matching: ${e.message}") false @@ -215,10 +219,6 @@ class MarketOrderService @Autowired constructor( } } - private fun getOrderBook(order: MarketOrder) = - genericLimitOrderService.getOrderBook(order.assetPairId).getOrderBook(!order.isBuySide()) - - private fun parse(array: ByteArray): ProtocolMessages.MarketOrder { return ProtocolMessages.MarketOrder.parseFrom(array) } diff --git a/src/main/kotlin/com/lykke/matching/engine/services/validators/MarketOrderValidator.kt b/src/main/kotlin/com/lykke/matching/engine/services/validators/MarketOrderValidator.kt index 65c7bcad0..5b91d4405 100644 --- a/src/main/kotlin/com/lykke/matching/engine/services/validators/MarketOrderValidator.kt +++ b/src/main/kotlin/com/lykke/matching/engine/services/validators/MarketOrderValidator.kt @@ -1,13 +1,13 @@ package com.lykke.matching.engine.services.validators -import com.lykke.matching.engine.daos.v2.FeeInstruction import com.lykke.matching.engine.daos.MarketOrder -import com.lykke.matching.engine.daos.LimitOrder import com.lykke.matching.engine.daos.fee.v2.NewFeeInstruction -import java.util.concurrent.PriorityBlockingQueue - +import com.lykke.matching.engine.daos.v2.FeeInstruction +import com.lykke.matching.engine.services.AssetOrderBook interface MarketOrderValidator { - fun performValidation(order: MarketOrder, orderBook: PriorityBlockingQueue, - feeInstruction: FeeInstruction?, feeInstructions: List?) + fun performValidation(order: MarketOrder, + orderBook: AssetOrderBook, + feeInstruction: FeeInstruction?, + feeInstructions: List?) } \ No newline at end of file diff --git a/src/main/kotlin/com/lykke/matching/engine/services/validators/business/LimitOrderBusinessValidator.kt b/src/main/kotlin/com/lykke/matching/engine/services/validators/business/LimitOrderBusinessValidator.kt index 1151cd309..899badc10 100644 --- a/src/main/kotlin/com/lykke/matching/engine/services/validators/business/LimitOrderBusinessValidator.kt +++ b/src/main/kotlin/com/lykke/matching/engine/services/validators/business/LimitOrderBusinessValidator.kt @@ -1,5 +1,6 @@ package com.lykke.matching.engine.services.validators.business +import com.lykke.matching.engine.daos.AssetPair import com.lykke.matching.engine.daos.LimitOrder import com.lykke.matching.engine.services.AssetOrderBook import java.math.BigDecimal @@ -10,6 +11,7 @@ interface LimitOrderBusinessValidator { order: LimitOrder, availableBalance: BigDecimal, limitVolume: BigDecimal, + assetPair: AssetPair, orderBook: AssetOrderBook, date: Date, currentOrderBookTotalSize: Int) diff --git a/src/main/kotlin/com/lykke/matching/engine/services/validators/business/StopOrderBusinessValidator.kt b/src/main/kotlin/com/lykke/matching/engine/services/validators/business/StopOrderBusinessValidator.kt index db5c2e343..a0cdce394 100644 --- a/src/main/kotlin/com/lykke/matching/engine/services/validators/business/StopOrderBusinessValidator.kt +++ b/src/main/kotlin/com/lykke/matching/engine/services/validators/business/StopOrderBusinessValidator.kt @@ -1,6 +1,8 @@ package com.lykke.matching.engine.services.validators.business +import com.lykke.matching.engine.daos.AssetPair import com.lykke.matching.engine.daos.LimitOrder +import com.lykke.matching.engine.services.AssetOrderBook import java.math.BigDecimal import java.util.* @@ -9,5 +11,7 @@ interface StopOrderBusinessValidator { limitVolume: BigDecimal, order: LimitOrder, orderProcessingTime: Date, + assetPair: AssetPair, + orderBook: AssetOrderBook, currentOrderBookTotalSize: Int) } \ No newline at end of file diff --git a/src/main/kotlin/com/lykke/matching/engine/services/validators/business/impl/LimitOrderBusinessValidatorImpl.kt b/src/main/kotlin/com/lykke/matching/engine/services/validators/business/impl/LimitOrderBusinessValidatorImpl.kt index 79cec45d4..990076550 100644 --- a/src/main/kotlin/com/lykke/matching/engine/services/validators/business/impl/LimitOrderBusinessValidatorImpl.kt +++ b/src/main/kotlin/com/lykke/matching/engine/services/validators/business/impl/LimitOrderBusinessValidatorImpl.kt @@ -1,5 +1,6 @@ package com.lykke.matching.engine.services.validators.business.impl +import com.lykke.matching.engine.daos.AssetPair import com.lykke.matching.engine.daos.LimitOrder import com.lykke.matching.engine.holders.OrderBookMaxTotalSizeHolder import com.lykke.matching.engine.order.OrderStatus @@ -15,9 +16,11 @@ import java.util.Date class LimitOrderBusinessValidatorImpl(private val orderBookMaxTotalSizeHolder: OrderBookMaxTotalSizeHolder) : LimitOrderBusinessValidator { - override fun performValidation(isTrustedClient: Boolean, order: LimitOrder, + override fun performValidation(isTrustedClient: Boolean, + order: LimitOrder, availableBalance: BigDecimal, limitVolume: BigDecimal, + assetPair: AssetPair, orderBook: AssetOrderBook, date: Date, currentOrderBookTotalSize: Int) { @@ -30,6 +33,7 @@ class LimitOrderBusinessValidatorImpl(private val orderBookMaxTotalSizeHolder: O validatePreviousOrderNotFound(order) validateNotEnoughFounds(order) OrderValidationUtils.validateExpiration(order, date) + OrderValidationUtils.validateMaxVolume(order, assetPair, orderBook) } private fun validatePreviousOrderNotFound(order: LimitOrder) { diff --git a/src/main/kotlin/com/lykke/matching/engine/services/validators/business/impl/StopOrderBusinessValidatorImpl.kt b/src/main/kotlin/com/lykke/matching/engine/services/validators/business/impl/StopOrderBusinessValidatorImpl.kt index 52f741110..290958dd5 100644 --- a/src/main/kotlin/com/lykke/matching/engine/services/validators/business/impl/StopOrderBusinessValidatorImpl.kt +++ b/src/main/kotlin/com/lykke/matching/engine/services/validators/business/impl/StopOrderBusinessValidatorImpl.kt @@ -1,7 +1,9 @@ package com.lykke.matching.engine.services.validators.business.impl +import com.lykke.matching.engine.daos.AssetPair import com.lykke.matching.engine.daos.LimitOrder import com.lykke.matching.engine.holders.OrderBookMaxTotalSizeHolder +import com.lykke.matching.engine.services.AssetOrderBook import com.lykke.matching.engine.services.validators.business.StopOrderBusinessValidator import com.lykke.matching.engine.services.validators.common.OrderValidationUtils import org.springframework.stereotype.Component @@ -15,9 +17,12 @@ class StopOrderBusinessValidatorImpl(private val orderBookMaxTotalSizeHolder: Or limitVolume: BigDecimal, order: LimitOrder, orderProcessingTime: Date, + assetPair: AssetPair, + orderBook: AssetOrderBook, currentOrderBookTotalSize: Int) { OrderValidationUtils.validateOrderBookTotalSize(currentOrderBookTotalSize, orderBookMaxTotalSizeHolder.get()) OrderValidationUtils.validateBalance(availableBalance, limitVolume) OrderValidationUtils.validateExpiration(order, orderProcessingTime) + OrderValidationUtils.validateMaxVolume(order, assetPair, orderBook) } } \ No newline at end of file diff --git a/src/main/kotlin/com/lykke/matching/engine/services/validators/common/OrderValidationUtils.kt b/src/main/kotlin/com/lykke/matching/engine/services/validators/common/OrderValidationUtils.kt index b76cae793..ab8ebcbef 100644 --- a/src/main/kotlin/com/lykke/matching/engine/services/validators/common/OrderValidationUtils.kt +++ b/src/main/kotlin/com/lykke/matching/engine/services/validators/common/OrderValidationUtils.kt @@ -3,7 +3,9 @@ package com.lykke.matching.engine.services.validators.common import com.lykke.matching.engine.daos.AssetPair import com.lykke.matching.engine.daos.LimitOrder import com.lykke.matching.engine.daos.Order +import com.lykke.matching.engine.daos.order.MaxOrderVolumeInfo import com.lykke.matching.engine.order.OrderStatus +import com.lykke.matching.engine.services.AssetOrderBook import com.lykke.matching.engine.services.validators.impl.OrderValidationException import com.lykke.utils.logging.MetricsLogger import java.math.BigDecimal @@ -39,5 +41,20 @@ class OrderValidationUtils { throw OrderValidationException(OrderStatus.OrderBookMaxSizeReached, errorMessage) } } + + fun validateMaxVolume(order: Order, + assetPair: AssetPair, + orderBook: AssetOrderBook) { + val maxVolumeInfo = calculateMaxVolume(assetPair, orderBook) ?: return + if (order.getAbsVolume() > maxVolumeInfo.maxVolume) { + throw OrderValidationException(OrderStatus.InvalidVolume, "volume is too large ($maxVolumeInfo)") + } + } + + fun calculateMaxVolume(assetPair: AssetPair, orderBook: AssetOrderBook): MaxOrderVolumeInfo? { + val midPrice = orderBook.getMidPrice() ?: return null + val maxValue = assetPair.maxValue ?: return null + return MaxOrderVolumeInfo(maxValue, midPrice) + } } } \ No newline at end of file diff --git a/src/main/kotlin/com/lykke/matching/engine/services/validators/impl/MarketOrderValidatorImpl.kt b/src/main/kotlin/com/lykke/matching/engine/services/validators/impl/MarketOrderValidatorImpl.kt index 9f030d0ce..a0b1fb80d 100644 --- a/src/main/kotlin/com/lykke/matching/engine/services/validators/impl/MarketOrderValidatorImpl.kt +++ b/src/main/kotlin/com/lykke/matching/engine/services/validators/impl/MarketOrderValidatorImpl.kt @@ -1,14 +1,16 @@ package com.lykke.matching.engine.services.validators.impl -import com.lykke.matching.engine.daos.v2.FeeInstruction -import com.lykke.matching.engine.daos.MarketOrder +import com.lykke.matching.engine.daos.AssetPair import com.lykke.matching.engine.daos.LimitOrder +import com.lykke.matching.engine.daos.MarketOrder import com.lykke.matching.engine.daos.fee.v2.NewFeeInstruction +import com.lykke.matching.engine.daos.v2.FeeInstruction import com.lykke.matching.engine.fee.checkFee import com.lykke.matching.engine.holders.ApplicationSettingsHolder import com.lykke.matching.engine.holders.AssetsHolder import com.lykke.matching.engine.holders.AssetsPairsHolder import com.lykke.matching.engine.order.OrderStatus +import com.lykke.matching.engine.services.AssetOrderBook import com.lykke.matching.engine.services.validators.MarketOrderValidator import com.lykke.matching.engine.services.validators.common.OrderValidationUtils import com.lykke.matching.engine.utils.NumberUtils @@ -28,13 +30,17 @@ class MarketOrderValidatorImpl private val LOGGER = LoggerFactory.getLogger(MarketOrderValidatorImpl::class.java.name) } - override fun performValidation(order: MarketOrder, orderBook: PriorityBlockingQueue, - feeInstruction: FeeInstruction?, feeInstructions: List?) { + override fun performValidation(order: MarketOrder, + orderBook: AssetOrderBook, + feeInstruction: FeeInstruction?, + feeInstructions: List?) { isAssetKnown(order) isAssetEnabled(order) - isVolumeValid(order) + val assetPair = getAssetPair(order) + isVolumeValid(order, assetPair, orderBook) + validateMaxValueForNotStraight(order, assetPair) isFeeValid(feeInstruction, feeInstructions, order) - isOrderBookValid(order, orderBook) + isOrderBookValid(order, orderBook.getOrderBook(!order.isBuySide())) isVolumeAccuracyValid(order) isPriceAccuracyValid(order) } @@ -54,7 +60,9 @@ class MarketOrderValidatorImpl } } - private fun isVolumeValid(order: MarketOrder) { + private fun isVolumeValid(order: MarketOrder, + assetPair: AssetPair, + orderBook: AssetOrderBook) { if (NumberUtils.equalsIgnoreScale(BigDecimal.ZERO, order.volume)) { val message = "volume can not be equal to zero" LOGGER.info(message) @@ -65,15 +73,25 @@ class MarketOrderValidatorImpl LOGGER.info("Too small volume for $order") throw OrderValidationException(OrderStatus.TooSmallVolume) } + validateMaxVolumeForStraight(order, assetPair, orderBook) + } - val assetPair = getAssetPair(order) - if (order.isStraight() && assetPair.maxVolume != null && order.getAbsVolume() > assetPair.maxVolume) { - LOGGER.info("Too large volume for $order") - throw OrderValidationException(OrderStatus.InvalidVolume) + private fun validateMaxVolumeForStraight(order: MarketOrder, + assetPair: AssetPair, + orderBook: AssetOrderBook) { + if (!order.isStraight()) { + return + } + OrderValidationUtils.validateMaxVolume(order, assetPair, orderBook) + } + + private fun validateMaxValueForNotStraight(order: MarketOrder, assetPair: AssetPair) { + if (order.isStraight()) { + return } - if (!order.isStraight() && assetPair.maxValue != null && order.getAbsVolume() > assetPair.maxValue) { - LOGGER.info("Too large value for $order") - throw OrderValidationException(OrderStatus.InvalidValue) + val maxValue = assetPair.maxValue ?: return + if (order.getAbsVolume() > maxValue) { + throw OrderValidationException(OrderStatus.InvalidValue, "Too large value (maxValue=$maxValue)") } } diff --git a/src/main/kotlin/com/lykke/matching/engine/services/validators/input/impl/LimitOrderInputValidatorImpl.kt b/src/main/kotlin/com/lykke/matching/engine/services/validators/input/impl/LimitOrderInputValidatorImpl.kt index 1809a23b8..7396624e6 100644 --- a/src/main/kotlin/com/lykke/matching/engine/services/validators/input/impl/LimitOrderInputValidatorImpl.kt +++ b/src/main/kotlin/com/lykke/matching/engine/services/validators/input/impl/LimitOrderInputValidatorImpl.kt @@ -43,7 +43,6 @@ class LimitOrderInputValidatorImpl(val applicationSettingsHolder: ApplicationSet validateMaxValue(order, assetPair) validatePriceAccuracy(order, assetPair) validateVolumeAccuracy(order, baseAsset!!) - validateValue(order, assetPair) } override fun validateStopOrder(singleLimitOrderParsedData: SingleLimitOrderParsedData) { @@ -111,24 +110,18 @@ class LimitOrderInputValidatorImpl(val applicationSettingsHolder: ApplicationSet } } - private fun validateValue(order: LimitOrder, assetPair: AssetPair) { - if (assetPair.maxValue != null && order.getAbsVolume() * order.price > assetPair.maxValue) { - throw OrderValidationException(OrderStatus.InvalidValue, "value is too large") - } - } - private fun validateMaxValue(limitOrder: LimitOrder, assetPair: AssetPair) { - if (assetPair.maxVolume != null && limitOrder.getAbsVolume() > assetPair.maxVolume) { - throw OrderValidationException(OrderStatus.InvalidVolume, "volume is too large") + val maxValue = assetPair.maxValue ?: return + if (limitOrder.getAbsVolume() * limitOrder.price > maxValue) { + throw OrderValidationException(OrderStatus.InvalidValue, "value is too large (maxValue=$maxValue)") } } private fun validateStopOrderMaxValue(limitOrder: LimitOrder, assetPair: AssetPair) { - validateMaxValue(limitOrder, assetPair) - - if (assetPair.maxValue != null && (limitOrder.lowerLimitPrice != null && limitOrder.getAbsVolume() * limitOrder.lowerPrice!! > assetPair.maxValue - || limitOrder.upperLimitPrice != null && limitOrder.getAbsVolume() * limitOrder.upperPrice!! > assetPair.maxValue)) { - throw OrderValidationException(OrderStatus.InvalidValue, "value is too large") + val maxValue = assetPair.maxValue ?: return + if ((limitOrder.lowerLimitPrice != null && limitOrder.getAbsVolume() * limitOrder.lowerPrice!! > maxValue + || limitOrder.upperLimitPrice != null && limitOrder.getAbsVolume() * limitOrder.upperPrice!! > maxValue)) { + throw OrderValidationException(OrderStatus.InvalidValue, "value is too large (maxValue=$maxValue)") } } diff --git a/src/test/kotlin/com/lykke/matching/engine/matching/MatchingEngineFeeTest.kt b/src/test/kotlin/com/lykke/matching/engine/matching/MatchingEngineFeeTest.kt index a55e74e86..04c3b811f 100644 --- a/src/test/kotlin/com/lykke/matching/engine/matching/MatchingEngineFeeTest.kt +++ b/src/test/kotlin/com/lykke/matching/engine/matching/MatchingEngineFeeTest.kt @@ -50,7 +50,7 @@ class MatchingEngineFeeTest : MatchingEngineTest() { ) ) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(limitOrder) assertCashMovementsEquals( listOf( @@ -91,7 +91,7 @@ class MatchingEngineFeeTest : MatchingEngineTest() { ) ) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(limitOrder) assertCashMovementsEquals( listOf( @@ -132,7 +132,7 @@ class MatchingEngineFeeTest : MatchingEngineTest() { ) ) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(limitOrder) assertCashMovementsEquals( listOf( @@ -173,7 +173,7 @@ class MatchingEngineFeeTest : MatchingEngineTest() { ) ) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(limitOrder) assertCashMovementsEquals( listOf( diff --git a/src/test/kotlin/com/lykke/matching/engine/matching/MatchingEngineLimitOrderTest.kt b/src/test/kotlin/com/lykke/matching/engine/matching/MatchingEngineLimitOrderTest.kt index a20d9e135..0cdb0896f 100644 --- a/src/test/kotlin/com/lykke/matching/engine/matching/MatchingEngineLimitOrderTest.kt +++ b/src/test/kotlin/com/lykke/matching/engine/matching/MatchingEngineLimitOrderTest.kt @@ -27,7 +27,7 @@ class MatchingEngineLimitOrderTest : MatchingEngineTest() { @Test fun testMatchLimitOrderBuyWithEmptyOrderBook() { val limitOrder = buildLimitOrder(price = 1.2, volume = 100.0) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(limitOrder) assertLimitOrderMatchingResult(matchingResult, status = OrderStatus.InOrderBook) } @@ -35,28 +35,17 @@ class MatchingEngineLimitOrderTest : MatchingEngineTest() { @Test fun testMatchLimitOrderSellWithEmptyOrderBook() { val limitOrder = buildLimitOrder(clientId = "Client2", price = 1.2, volume = -100.0) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(limitOrder) assertLimitOrderMatchingResult(matchingResult, remainingVolume = BigDecimal.valueOf( -100.0), status = OrderStatus.InOrderBook) } - @Test - fun testMatchLimitOrderWithSameOrderBookSide() { - testBalanceHolderWrapper.updateBalance("Client2", "USD", 1000.0) - testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", price = 1.2, volume = 100.0)) - - val limitOrder = buildLimitOrder(price = 1.2, volume = 100.0) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", true)) - - assertLimitOrderMatchingResult(matchingResult, orderBookSize = 1, status = OrderStatus.InOrderBook) - } - @Test fun testMatchNoLiquidityLimitOrderBuy() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", price = 1.3, volume = -100.0)) val limitOrder = buildLimitOrder(price = 1.2, volume = 100.0) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(limitOrder) assertLimitOrderMatchingResult(matchingResult, orderBookSize = 1, status = OrderStatus.InOrderBook) } @@ -66,29 +55,18 @@ class MatchingEngineLimitOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(price = 1.2, volume = 100.0)) val limitOrder = buildLimitOrder(clientId = "Client2", price = 1.3, volume = -100.0) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(limitOrder) assertLimitOrderMatchingResult(matchingResult, remainingVolume = BigDecimal.valueOf(-100.0), orderBookSize = 1, status = OrderStatus.InOrderBook) } - @Test - fun testMatchLimitOrderWithAnotherAssetPair() { - testBalanceHolderWrapper.updateBalance("Client2", "BTC", 100.0) - testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", assetId = "BTCUSD", price = 1.2, volume = -100.0)) - - val limitOrder = buildLimitOrder(price = 1.2, volume = 100.0) - val matchingResult = match(limitOrder, getOrderBook("BTCUSD", false)) - - assertLimitOrderMatchingResult(matchingResult, orderBookSize = 1, status = OrderStatus.InOrderBook) - } - @Test fun testMatchLimitOrderWithOwnLimitOrder() { testBalanceHolderWrapper.updateBalance("Client1", "EUR", 100.0) testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client1", price = 1.2, volume = -100.0)) val limitOrder = buildLimitOrder(price = 1.2, volume = 100.0) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(limitOrder) assertLimitOrderMatchingResult(matchingResult, status = OrderStatus.LeadToNegativeSpread, marketBalance = null) assertEquals(1, getOrderBook("EURUSD", false).size) @@ -100,7 +78,7 @@ class MatchingEngineLimitOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(price = 1.2, volume = 100.0)) val limitOrder = buildLimitOrder(clientId = "Client2", price = 1.2, volume = -100.0) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(limitOrder) assertLimitOrderMatchingResult(matchingResult, cancelledSize = 1, remainingVolume = BigDecimal.valueOf(-100.0), status = OrderStatus.InOrderBook) } @@ -111,7 +89,7 @@ class MatchingEngineLimitOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", price = 1.2, volume = -100.0)) val limitOrder = buildLimitOrder(price = 1.2, volume = 100.0) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(limitOrder) assertLimitOrderMatchingResult(matchingResult, cancelledSize = 1, status = OrderStatus.InOrderBook) } @@ -124,7 +102,7 @@ class MatchingEngineLimitOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", price = 1.2, volume = -100.0)) val limitOrder = buildLimitOrder(price = 1.2, volume = 100.0) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(limitOrder) assertLimitOrderMatchingResult(matchingResult, cancelledSize = 1, status = OrderStatus.InOrderBook) } @@ -136,7 +114,7 @@ class MatchingEngineLimitOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(price = 1.2, volume = 100.0)) val limitOrder = buildLimitOrder(clientId = "Client2", price = 1.2, volume = -100.0) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(limitOrder) assertLimitOrderMatchingResult(matchingResult, cancelledSize = 1, remainingVolume = BigDecimal.valueOf(-100.0), status = OrderStatus.InOrderBook) } @@ -147,7 +125,7 @@ class MatchingEngineLimitOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", price = 1.2, volume = -100.0)) val limitOrder = buildLimitOrder(price = 1.2, volume = 100.0) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(limitOrder) assertLimitOrderMatchingResult(matchingResult, status = OrderStatus.ReservedVolumeGreaterThanBalance, marketBalance = null) } @@ -158,7 +136,7 @@ class MatchingEngineLimitOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(price = 1.2, volume = 100.0)) val limitOrder = buildLimitOrder(clientId = "Client2", price = 1.2, volume = -100.0) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(limitOrder) assertLimitOrderMatchingResult(matchingResult, status = OrderStatus.ReservedVolumeGreaterThanBalance, marketBalance = null, remainingVolume = BigDecimal.valueOf(-100.0)) } @@ -170,7 +148,7 @@ class MatchingEngineLimitOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", price = 1.2, volume = -100.0)) val limitOrder = buildLimitOrder(price = 1.2, volume = 100.0) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(limitOrder) assertLimitOrderMatchingResult(matchingResult, status = OrderStatus.ReservedVolumeGreaterThanBalance, marketBalance = null) } @@ -182,7 +160,7 @@ class MatchingEngineLimitOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(price = 1.2, volume = 100.0)) val limitOrder = buildLimitOrder(clientId = "Client2", price = 1.2, volume = -100.0) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(limitOrder) assertLimitOrderMatchingResult(matchingResult, status = OrderStatus.ReservedVolumeGreaterThanBalance, marketBalance = null, remainingVolume = BigDecimal.valueOf(-100.0)) } @@ -191,7 +169,7 @@ class MatchingEngineLimitOrderTest : MatchingEngineTest() { fun testMatchLimitOrderPriceDeviation() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(price = 1.2, volume = 1.0)) val limitOrder = buildLimitOrder(clientId = "Client2", price = 1.1, volume = -100.0) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", true), priceDeviationThreshold = BigDecimal.valueOf(0.08)) + val matchingResult = match(limitOrder, priceDeviationThreshold = BigDecimal.valueOf(0.08)) assertLimitOrderMatchingResult(matchingResult, status = OrderStatus.TooHighPriceDeviation, marketBalance = null, remainingVolume = BigDecimal.valueOf(-100.0)) } @@ -200,7 +178,7 @@ class MatchingEngineLimitOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(uid = "uncompleted", clientId = "Client2", price = 1.19, volume = -100.0, reservedVolume = 100.0)) val limitOrder = buildLimitOrder(price = 1.21, volume = 91.1) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(limitOrder) assertLimitOrderMatchingResult(matchingResult, status = OrderStatus.Matched, marketBalance = BigDecimal.valueOf(891.59), @@ -226,7 +204,7 @@ class MatchingEngineLimitOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(uid = "uncompleted", price = 1.21, volume = 108.1, reservedVolume = 130.81)) val limitOrder = buildLimitOrder(clientId = "Client2", price = 1.19, volume = -100.0) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(limitOrder) assertLimitOrderMatchingResult(matchingResult, status = OrderStatus.Matched, marketBalance = BigDecimal.valueOf(900.00), @@ -254,7 +232,7 @@ class MatchingEngineLimitOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(uid = "completed", clientId = "Client2", price = 1.19, volume = -89.1, reservedVolume = 89.1)) val limitOrder = buildLimitOrder(price = 1.21, volume = 91.1) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(limitOrder) assertLimitOrderMatchingResult(matchingResult, status = OrderStatus.Processing, marketBalance = BigDecimal.valueOf(893.97), @@ -293,7 +271,7 @@ class MatchingEngineLimitOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(uid = "completed", price = 1.21, volume = 91.1, reservedVolume = 110.24)) val limitOrder = buildLimitOrder(clientId = "Client2", price = 1.19, volume = -92.2) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(limitOrder) assertLimitOrderMatchingResult(matchingResult, status = OrderStatus.Processing, marketBalance = BigDecimal.valueOf(908.9), @@ -331,7 +309,7 @@ class MatchingEngineLimitOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(uid = "completed", clientId = "Client2", price = 1.2, volume = -100.0, reservedVolume = 100.0)) val limitOrder = buildLimitOrder(price = 1.2, volume = 100.0) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(limitOrder) assertLimitOrderMatchingResult(matchingResult, status = OrderStatus.Matched, marketBalance = BigDecimal.valueOf(880.0), remainingVolume = BigDecimal.ZERO, skipSize = 0, cancelledSize = 0, lkkTradesSize = 2, cashMovementsSize = 4, marketOrderTradesSize = 1, completedLimitOrdersSize = 1, @@ -346,7 +324,7 @@ class MatchingEngineLimitOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(uid = "completed", price = 1.2, volume = 100.0, reservedVolume = 120.0)) val limitOrder = buildLimitOrder(clientId = "Client2", price = 1.2, volume = -100.0) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(limitOrder) assertLimitOrderMatchingResult(matchingResult, status = OrderStatus.Matched, marketBalance = BigDecimal.valueOf(900.0), remainingVolume = BigDecimal.ZERO, skipSize = 0, cancelledSize = 0, lkkTradesSize = 2, cashMovementsSize = 4, marketOrderTradesSize = 1, completedLimitOrdersSize = 1, @@ -362,7 +340,7 @@ class MatchingEngineLimitOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", price = 1.2, volume = -50.0, reservedVolume = 50.0)) val limitOrder = buildLimitOrder(price = 1.2, volume = 100.0) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(limitOrder) assertLimitOrderMatchingResult(matchingResult, status = OrderStatus.Matched, marketBalance = BigDecimal.valueOf(880.0), remainingVolume = BigDecimal.ZERO, skipSize = 0, cancelledSize = 0, lkkTradesSize = 4, cashMovementsSize = 8, marketOrderTradesSize = 2, completedLimitOrdersSize = 2, limitOrdersReportSize = 2) @@ -376,7 +354,7 @@ class MatchingEngineLimitOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(price = 1.2, volume = 50.0, reservedVolume = 60.0)) val limitOrder = buildLimitOrder(clientId = "Client2", price = 1.2, volume = -100.0) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(limitOrder) assertLimitOrderMatchingResult(matchingResult, status = OrderStatus.Matched, marketBalance = BigDecimal.valueOf(900.0), remainingVolume = BigDecimal.ZERO, skipSize = 0, cancelledSize = 0, lkkTradesSize = 4, cashMovementsSize = 8, marketOrderTradesSize = 2, completedLimitOrdersSize = 2, @@ -395,7 +373,7 @@ class MatchingEngineLimitOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client4", price = 1.3, volume = -40.0, reservedVolume = 40.0)) val limitOrder = buildLimitOrder(price = 1.2, volume = 100.0) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(limitOrder) assertLimitOrderMatchingResult(matchingResult, status = OrderStatus.LeadToNegativeSpread, marketBalance = null) assertEquals(4, getOrderBook("EURUSD", false).size) @@ -411,7 +389,7 @@ class MatchingEngineLimitOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client1", price = 1.1, volume = 40.0, reservedVolume = 44.0)) val limitOrder = buildLimitOrder(clientId = "Client2", price = 1.2, volume = -100.0) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(limitOrder) assertLimitOrderMatchingResult(matchingResult, status = OrderStatus.LeadToNegativeSpread, marketBalance = null, remainingVolume = BigDecimal.valueOf(-100.0)) assertEquals(4, getOrderBook("EURUSD", true).size) @@ -433,7 +411,7 @@ class MatchingEngineLimitOrderTest : MatchingEngineTest() { val limitOrder = buildLimitOrder(clientId = "Client2", assetId = "BTCUSD", price = 6110.0, volume = 30.0) Thread.sleep(100) - val matchingResult = match(limitOrder, getOrderBook("BTCUSD", false)) + val matchingResult = match(limitOrder) assertLimitOrderMatchingResult(matchingResult, status = OrderStatus.Matched, marketBalance = BigDecimal.valueOf(6999.85), @@ -472,7 +450,7 @@ class MatchingEngineLimitOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client3", assetId = "BTCUSD", volume = -0.1, price = 6110.0)) val limitOrder = buildLimitOrder(clientId = "Client2", assetId = "BTCUSD", price = 6110.0, volume = 30.0) - val matchingResult = match(limitOrder, getOrderBook("BTCUSD", false)) + val matchingResult = match(limitOrder) assertLimitOrderMatchingResult(matchingResult, status = OrderStatus.Matched, marketBalance = BigDecimal.valueOf(6999.80), @@ -497,7 +475,7 @@ class MatchingEngineLimitOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client3", price = 1.30001, volume = -52.33, reservedVolume = 52.33)) val limitOrder = buildLimitOrder(price = 1.31, volume = 100.0) - val matchingResult = match(limitOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(limitOrder) assertLimitOrderMatchingResult(matchingResult, status = OrderStatus.Matched, marketBalance = BigDecimal.valueOf(872.21), remainingVolume = BigDecimal.ZERO, skipSize = 0, cancelledSize = 0, lkkTradesSize = 4, cashMovementsSize = 8, marketOrderTradesSize = 2, completedLimitOrdersSize = 1, @@ -528,7 +506,7 @@ class MatchingEngineLimitOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", assetId = "LKK1YLKK", volume = 5500.0, price = 1.0085)) val matchingResult = match(buildLimitOrder(clientId = "Client1", assetId = "LKK1YLKK", volume = -5495.03, price = 1.0082, - fees = buildLimitOrderFeeInstructions(type = FeeType.CLIENT_FEE, takerSize = 0.0009, targetClientId = "Client5")), getOrderBook("LKK1YLKK", true)) + fees = buildLimitOrderFeeInstructions(type = FeeType.CLIENT_FEE, takerSize = 0.0009, targetClientId = "Client5"))) assertLimitOrderMatchingResult(matchingResult, status = OrderStatus.Matched, marketBalance = BigDecimal.ZERO, remainingVolume = BigDecimal.ZERO, skipSize = 0, cancelledSize = 0, lkkTradesSize = 4, cashMovementsSize = 10, marketOrderTradesSize = 2, completedLimitOrdersSize = 1, @@ -543,7 +521,7 @@ class MatchingEngineLimitOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client1", assetId = "BTCCHF", price = 0.231, volume = 1.0, reservedVolume = 0.24)) val limitOrder = buildLimitOrder(clientId = "Client2", assetId = "BTCCHF", price = 0.231, volume = -0.001) - val matchingResult = match(limitOrder, getOrderBook("BTCCHF", true)) + val matchingResult = match(limitOrder) assertLimitOrderMatchingResult(matchingResult, status = OrderStatus.InOrderBook, marketBalance = BigDecimal.valueOf(0.001), diff --git a/src/test/kotlin/com/lykke/matching/engine/matching/MatchingEngineMarketOrderTest.kt b/src/test/kotlin/com/lykke/matching/engine/matching/MatchingEngineMarketOrderTest.kt index bbe924913..00665853f 100644 --- a/src/test/kotlin/com/lykke/matching/engine/matching/MatchingEngineMarketOrderTest.kt +++ b/src/test/kotlin/com/lykke/matching/engine/matching/MatchingEngineMarketOrderTest.kt @@ -14,7 +14,6 @@ import org.springframework.test.annotation.DirtiesContext import org.springframework.test.context.junit4.SpringRunner import java.math.BigDecimal import kotlin.test.assertNotNull -import kotlin.test.assertTrue @RunWith(SpringRunner::class) @SpringBootTest(classes = [(TestApplicationContext::class), (MatchingEngineTest.Config::class)]) @@ -24,7 +23,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { @Test fun testMatchMarketOrderBuyWithEmptyOrderBook() { val marketOrder = buildMarketOrder(volume = 100.0) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult) } @@ -32,7 +31,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { @Test fun testMatchMarketOrderSellWithEmptyOrderBook() { val marketOrder = buildMarketOrder(volume = -100.0) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult) } @@ -40,7 +39,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { @Test fun testMatchMarketOrderNotStraightBuyWithEmptyOrderBook() { val marketOrder = buildMarketOrder(volume = -100.0, straight = false) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult) } @@ -48,7 +47,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { @Test fun testMatchMarketOrderNotStraightSellWithEmptyOrderBook() { val marketOrder = buildMarketOrder(volume = 100.0, straight = false) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult) } @@ -59,7 +58,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", price = 1.2, volume = 100.0)) val marketOrder = buildMarketOrder(volume = 100.0) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult) } @@ -70,7 +69,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", price = 1.2, volume = 100.0)) val marketOrder = buildMarketOrder(volume = -120.0, straight = false) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult) } @@ -80,7 +79,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", price = 1.3, volume = -100.0)) val marketOrder = buildMarketOrder(volume = 100.01) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult) } @@ -90,7 +89,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", price = 1.3, volume = -100.0)) val marketOrder = buildMarketOrder(volume = -130.01, straight = false) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult) } @@ -100,7 +99,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(price = 1.2, volume = 100.0)) val marketOrder = buildMarketOrder(clientId = "Client2", volume = -100.01) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult) } @@ -110,21 +109,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(price = 1.2, volume = 100.0)) val marketOrder = buildMarketOrder(clientId = "Client2", volume = 120.01, straight = false) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", true)) - - assertMarketOrderMatchingResult(matchingResult) - } - - @Test - fun testMatchNoLiquidityMarketOrderNotStraightSell2() { - testBalanceHolderWrapper.updateBalance("Client2", "BTC", 0.000028) - - testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client1", assetId = "BTCUSD", price = 1000.0, volume = 0.00001)) - testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client1", assetId = "BTCUSD", price = 1000.0, volume = 0.000009)) - testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client1", assetId = "BTCUSD", price = 1000.0, volume = 0.000009)) - - val marketOrder = buildMarketOrder(clientId = "Client2", assetId = "BTCUSD", volume = 0.02, straight = false) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult) } @@ -135,7 +120,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", assetId = "BTCUSD", price = 1.2, volume = -100.0)) val marketOrder = buildMarketOrder(volume = 100.0) - val matchingResult = match(marketOrder, getOrderBook("BTCUSD", false)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult) } @@ -146,7 +131,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client1", price = 1.2, volume = -100.0)) val marketOrder = buildMarketOrder(volume = 100.0) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult) assertEquals(1, getOrderBook("EURUSD", false).size) @@ -158,7 +143,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", price = 1.2, volume = -100.0)) val marketOrder = buildMarketOrder(volume = 100.0) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult, cancelledSize = 1) } @@ -169,7 +154,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", price = 1.2, volume = -100.0)) val marketOrder = buildMarketOrder(volume = -120.0, straight = false) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult, cancelledSize = 1) } @@ -180,7 +165,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(price = 1.2, volume = 100.0)) val marketOrder = buildMarketOrder(clientId = "Client2", volume = -100.0) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult, cancelledSize = 1) } @@ -192,7 +177,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", price = 1.2, volume = -100.0)) val marketOrder = buildMarketOrder(volume = 100.0) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult, cancelledSize = 1) } @@ -204,7 +189,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(price = 1.2, volume = 100.0)) val marketOrder = buildMarketOrder(clientId = "Client2", volume = -100.0) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult, cancelledSize = 1) } @@ -215,7 +200,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", price = 1.2, volume = -100.0)) val marketOrder = buildMarketOrder(volume = 100.0) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult, status = OrderStatus.NotEnoughFunds, marketBalance = null) } @@ -226,7 +211,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(price = 1.2, volume = 100.0)) val marketOrder = buildMarketOrder(clientId = "Client2", volume = -100.0) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult, status = OrderStatus.NotEnoughFunds, marketBalance = null) } @@ -237,7 +222,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(price = 1.2, volume = 100.0)) val marketOrder = buildMarketOrder(clientId = "Client2", volume = 120.0, straight = false) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult, status = OrderStatus.NotEnoughFunds, marketBalance = null) } @@ -249,7 +234,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", price = 1.2, volume = -100.0)) val marketOrder = buildMarketOrder(volume = 100.0) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult, status = OrderStatus.NotEnoughFunds, marketBalance = null) } @@ -261,7 +246,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(price = 1.2, volume = 100.0)) val marketOrder = buildMarketOrder(clientId = "Client2", volume = -100.0) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult, status = OrderStatus.NotEnoughFunds, marketBalance = null) } @@ -272,7 +257,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", price = 1.2, volume = -100.0)) val marketOrder = buildMarketOrder(volume = 100.0, reservedVolume = 120.01) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult, status = OrderStatus.ReservedVolumeGreaterThanBalance, marketBalance = null) } @@ -283,7 +268,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(price = 1.2, volume = 100.0)) val marketOrder = buildMarketOrder(clientId = "Client2", volume = -100.0, reservedVolume = 100.01) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult, status = OrderStatus.ReservedVolumeGreaterThanBalance, marketBalance = null) } @@ -295,7 +280,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(price = 1.0, volume = 1.0)) val marketOrder = buildMarketOrder(clientId = "Client2", volume = -3.0) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", true), priceDeviationThreshold = BigDecimal.valueOf(0.08)) + val matchingResult = match(marketOrder, priceDeviationThreshold = BigDecimal.valueOf(0.08)) assertMarketOrderMatchingResult(matchingResult, status = OrderStatus.TooHighPriceDeviation, marketBalance = null) } @@ -306,7 +291,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(price = 1.0, volume = 1.0)) val marketOrder = buildMarketOrder(clientId = "Client2", volume = 3.3, straight = false) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", true), priceDeviationThreshold = BigDecimal.valueOf(0.08)) + val matchingResult = match(marketOrder, priceDeviationThreshold = BigDecimal.valueOf(0.08)) assertMarketOrderMatchingResult(matchingResult, status = OrderStatus.TooHighPriceDeviation, marketBalance = null) } @@ -317,7 +302,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(uid = "uncompleted", clientId = "Client2", price = 1.19, volume = -100.0, reservedVolume = 100.0)) val marketOrder = buildMarketOrder(volume = 91.1) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult, status = OrderStatus.Matched, marketBalance = BigDecimal.valueOf(891.59), @@ -359,7 +344,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(uid = "uncompleted", price = 1.21, volume = 108.1, reservedVolume = 130.81)) val marketOrder = buildMarketOrder(clientId = "Client2", volume = -100.0) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult, status = OrderStatus.Matched, marketBalance = BigDecimal.valueOf(900.00), @@ -385,7 +370,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(uid = "uncompleted", clientId = "Client2", price = 1.19, volume = -100.0, reservedVolume = 100.0)) val marketOrder = buildMarketOrder(volume = -100.0, straight = false) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult, status = OrderStatus.Matched, marketBalance = BigDecimal.valueOf(900.00), @@ -411,7 +396,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(uid = "uncompleted", price = 1.21, volume = 108.1, reservedVolume = 130.81)) val marketOrder = buildMarketOrder(clientId = "Client2", volume = 100.0, straight = false) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult, status = OrderStatus.Matched, marketBalance = BigDecimal.valueOf(917.3553), @@ -437,7 +422,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(uid = "completed", clientId = "Client2", price = 1.2, volume = -100.0, reservedVolume = 100.0)) val marketOrder = buildMarketOrder(volume = 100.0) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult, status = OrderStatus.Matched, marketBalance = BigDecimal.valueOf(880.0), marketPrice = BigDecimal.valueOf(1.2), @@ -453,7 +438,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(uid = "completed", clientId = "Client2", price = 1.2, volume = -100.0, reservedVolume = 100.0)) val marketOrder = buildMarketOrder(volume = -120.0, straight = false) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult, status = OrderStatus.Matched, marketBalance = BigDecimal.valueOf(880.0), marketPrice = BigDecimal.valueOf(1.2), lkkTradesSize = 2, cashMovementsSize = 4, marketOrderTradesSize = 1, completedLimitOrdersSize = 1, @@ -468,7 +453,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(uid = "completed", price = 1.2, volume = 100.0, reservedVolume = 120.0)) val marketOrder = buildMarketOrder(clientId = "Client2", volume = -100.0) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult, status = OrderStatus.Matched, marketBalance = BigDecimal.valueOf(900.0), marketPrice = BigDecimal.valueOf(1.2), lkkTradesSize = 2, cashMovementsSize = 4, marketOrderTradesSize = 1, completedLimitOrdersSize = 1, @@ -482,7 +467,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(uid = "completed", price = 1.2, volume = 100.0, reservedVolume = 120.0)) val marketOrder = buildMarketOrder(clientId = "Client2", volume = 120.0, straight = false) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult, status = OrderStatus.Matched, marketBalance = BigDecimal.valueOf(900.0), marketPrice = BigDecimal.valueOf(1.2), lkkTradesSize = 2, cashMovementsSize = 4, marketOrderTradesSize = 1, completedLimitOrdersSize = 1, @@ -497,7 +482,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", price = 1.2, volume = -50.0, reservedVolume = 50.0)) val marketOrder = buildMarketOrder(volume = 100.0) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult, status = OrderStatus.Matched, marketBalance = BigDecimal.valueOf(880.0), marketPrice = BigDecimal.valueOf(1.2), skipSize = 0, cancelledSize = 0, lkkTradesSize = 4, @@ -519,7 +504,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { Thread.sleep(100) val marketOrder = buildMarketOrder(volume = 100.0) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", false)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult, status = OrderStatus.Matched, @@ -553,7 +538,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client1", price = 1.1, volume = 40.0, reservedVolume = 44.0)) val marketOrder = buildMarketOrder(clientId = "Client2", volume = 122.0, straight = false) - val matchingResult = match(marketOrder, getOrderBook("EURUSD", true)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult, status = OrderStatus.Matched, @@ -583,7 +568,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client1", assetId = "BTCUSD", volume = -0.00947867, price = 10550.0, reservedVolume = 0.00947867)) val marketOrder = buildMarketOrder(clientId = "Client3", assetId = "BTCUSD", volume = -100.0, straight = false) - match(marketOrder, getOrderBook("BTCUSD", false)) + match(marketOrder) val orders = testDatabaseAccessor.getOrders("BTCUSD", false).filter { it.clientId == "Client1" } assertEquals(1, orders.size) @@ -597,7 +582,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testBalanceHolderWrapper.updateBalance("Client4", "CHF", 1.0) val marketOrder = buildMarketOrder(clientId = "Client4", assetId = "BTCCHF", volume = -0.3772, straight = false) - val matchingResult = match(marketOrder, getOrderBook("BTCCHF", false)) + val matchingResult = match(marketOrder) assertEquals(1, matchingResult.marketOrderTrades.size) assertEquals("0.00041770", matchingResult.marketOrderTrades.first().limitVolume) @@ -613,7 +598,7 @@ class MatchingEngineMarketOrderTest : MatchingEngineTest() { testOrderBookWrapper.addLimitOrder(buildLimitOrder(assetId = "BTCCHF", price = 6007.991, volume = -0.01165173, clientId = "Client2")) val marketOrder = buildMarketOrder(clientId = "Client1", assetId = "BTCCHF", volume = 0.01665173) - val matchingResult = match(marketOrder, getOrderBook("BTCCHF", false)) + val matchingResult = match(marketOrder) assertMarketOrderMatchingResult(matchingResult, status = OrderStatus.NotEnoughFunds) } diff --git a/src/test/kotlin/com/lykke/matching/engine/matching/MatchingEngineTest.kt b/src/test/kotlin/com/lykke/matching/engine/matching/MatchingEngineTest.kt index bb6bc84ad..8d1f4f89e 100644 --- a/src/test/kotlin/com/lykke/matching/engine/matching/MatchingEngineTest.kt +++ b/src/test/kotlin/com/lykke/matching/engine/matching/MatchingEngineTest.kt @@ -14,9 +14,9 @@ import com.lykke.matching.engine.holders.BalancesHolder import com.lykke.matching.engine.messages.MessageType import com.lykke.matching.engine.order.OrderStatus import com.lykke.matching.engine.order.utils.TestOrderBookWrapper -import com.lykke.matching.engine.services.GenericLimitOrderService import com.lykke.matching.engine.order.transaction.ExecutionContext import com.lykke.matching.engine.order.transaction.ExecutionContextFactory +import com.lykke.matching.engine.services.GenericLimitOrderService import org.junit.After import org.junit.Assert.assertEquals import com.lykke.matching.engine.utils.assertEquals @@ -109,11 +109,8 @@ abstract class MatchingEngineTest { } protected fun match(order: Order, - orderBook: PriorityBlockingQueue, priceDeviationThreshold: BigDecimal? = null): MatchingResult { return matchingEngine.match(order, - orderBook, - "test", priceDeviationThreshold = priceDeviationThreshold, executionContext = executionContext) } diff --git a/src/test/kotlin/com/lykke/matching/engine/services/LimitOrderServiceTest.kt b/src/test/kotlin/com/lykke/matching/engine/services/LimitOrderServiceTest.kt index 38f15c966..eb2bce572 100644 --- a/src/test/kotlin/com/lykke/matching/engine/services/LimitOrderServiceTest.kt +++ b/src/test/kotlin/com/lykke/matching/engine/services/LimitOrderServiceTest.kt @@ -1649,10 +1649,14 @@ class LimitOrderServiceTest : AbstractTest() { fun testOrderMaxVolume() { testBalanceHolderWrapper.updateBalance("Client1", "BTC", 1.1) testDictionariesDatabaseAccessor.addAssetPair(AssetPair("BTCUSD", "BTC", "USD", 8, - maxVolume = BigDecimal.valueOf(1.0))) + maxValue = BigDecimal.valueOf(5000.0))) assetPairsCache.update() - singleLimitOrderService.processMessage(messageBuilder.buildLimitOrderWrapper(buildLimitOrder(clientId = "Client1", assetId = "BTCUSD", volume = -1.1, price = 10000.0))) + // orderBook with midPrice = 5000.0 + testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", assetId = "BTCUSD", volume = -1.0, price = 5500.0)) + testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", assetId = "BTCUSD", volume = 1.0, price = 4500.0)) + + singleLimitOrderService.processMessage(messageBuilder.buildLimitOrderWrapper(buildLimitOrder(clientId = "Client1", assetId = "BTCUSD", volume = -1.1, price = 4000.0))) assertEquals(1, clientsEventsQueue.size) val event = clientsEventsQueue.poll() as ExecutionEvent @@ -1661,7 +1665,7 @@ class LimitOrderServiceTest : AbstractTest() { assertEquals(OutgoingOrderStatus.REJECTED, eventOrder.status) assertEquals(OrderRejectReason.INVALID_VOLUME, eventOrder.rejectReason) - assertOrderBookSize("BTCUSD", false, 0) + assertOrderBookSize("BTCUSD", false, 1) } @Test diff --git a/src/test/kotlin/com/lykke/matching/engine/services/MarketOrderServiceTest.kt b/src/test/kotlin/com/lykke/matching/engine/services/MarketOrderServiceTest.kt index cf29e019e..4df578410 100644 --- a/src/test/kotlin/com/lykke/matching/engine/services/MarketOrderServiceTest.kt +++ b/src/test/kotlin/com/lykke/matching/engine/services/MarketOrderServiceTest.kt @@ -797,10 +797,17 @@ class MarketOrderServiceTest : AbstractTest() { @Test fun testStraightOrderMaxVolume() { testBalanceHolderWrapper.updateBalance("Client1", "BTC", 1.1) + testBalanceHolderWrapper.updateBalance("Client2", "BTC", 1.0) + testBalanceHolderWrapper.updateBalance("Client2", "USD", 20000.0) testDictionariesDatabaseAccessor.addAssetPair(AssetPair("BTCUSD", "BTC", "USD", 8, - maxVolume = BigDecimal.valueOf(1.0))) + maxValue = BigDecimal.valueOf(10000))) assetPairsCache.update() + // orderBook with midPrice=10000 + testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", assetId = "BTCUSD", volume = -1.0, price = 10001.0)) + testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", assetId = "BTCUSD", volume = 1.0, price = 9999.0)) + testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", assetId = "BTCUSD", volume = 1.0, price = 9998.0)) + marketOrderService.processMessage(buildMarketOrderWrapper(buildMarketOrder(clientId = "Client1", assetId = "BTCUSD", volume = -1.1))) assertEquals(1, clientsEventsQueue.size) @@ -814,11 +821,14 @@ class MarketOrderServiceTest : AbstractTest() { @Test fun testNotStraightOrderMaxVolume() { testBalanceHolderWrapper.updateBalance("Client1", "BTC", 1.1) + testBalanceHolderWrapper.updateBalance("Client2", "BTC", 1.0) testBalanceHolderWrapper.updateBalance("Client2", "USD", 11000.0) testDictionariesDatabaseAccessor.addAssetPair(AssetPair("BTCUSD", "BTC", "USD", 8, - maxVolume = BigDecimal.valueOf(1.0))) + maxValue = BigDecimal.valueOf(11000))) assetPairsCache.update() + // orderBook with midPrice=11000 + testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", assetId = "BTCUSD", volume = -1.0, price = 12000.0)) testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", assetId = "BTCUSD", volume = 1.1, price = 10000.0)) marketOrderService.processMessage(buildMarketOrderWrapper(buildMarketOrder(clientId = "Client1", assetId = "BTCUSD", volume = 11000.0, straight = false))) diff --git a/src/test/kotlin/com/lykke/matching/engine/services/MultiLimitOrderServiceTest.kt b/src/test/kotlin/com/lykke/matching/engine/services/MultiLimitOrderServiceTest.kt index 6782f05b7..58660fc9d 100644 --- a/src/test/kotlin/com/lykke/matching/engine/services/MultiLimitOrderServiceTest.kt +++ b/src/test/kotlin/com/lykke/matching/engine/services/MultiLimitOrderServiceTest.kt @@ -1331,12 +1331,16 @@ class MultiLimitOrderServiceTest: AbstractTest() { fun testOrderMaxVolume() { testBalanceHolderWrapper.updateBalance("Client1", "BTC", 1.1) testDictionariesDatabaseAccessor.addAssetPair(AssetPair("BTCUSD", "BTC", "USD", 8, - maxVolume = BigDecimal.valueOf(1.0))) + maxValue = BigDecimal.valueOf(5000.0))) assetPairsCache.update() - multiLimitOrderService.processMessage(buildMultiLimitOrderWrapper("BTCUSD", "Client1", listOf(IncomingLimitOrder(-1.1, 10000.0)))) + // orderBook with midPrice = 5000.0 + testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", assetId = "BTCUSD", volume = -1.0, price = 5500.0)) + testOrderBookWrapper.addLimitOrder(buildLimitOrder(clientId = "Client2", assetId = "BTCUSD", volume = 1.0, price = 4500.0)) - assertOrderBookSize("BTCUSD", false, 0) + multiLimitOrderService.processMessage(buildMultiLimitOrderWrapper("BTCUSD", "Client1", listOf(IncomingLimitOrder(-1.1, 4000.0)))) + + assertOrderBookSize("BTCUSD", false, 1) } @Test diff --git a/src/test/kotlin/com/lykke/matching/engine/services/StopLimitOrderTest.kt b/src/test/kotlin/com/lykke/matching/engine/services/StopLimitOrderTest.kt index ebaecfc79..f4924c422 100644 --- a/src/test/kotlin/com/lykke/matching/engine/services/StopLimitOrderTest.kt +++ b/src/test/kotlin/com/lykke/matching/engine/services/StopLimitOrderTest.kt @@ -81,7 +81,6 @@ class StopLimitOrderTest : AbstractTest() { testBalanceHolderWrapper.updateReservedBalance("Client1", "USD", 0.0) testDictionariesDatabaseAccessor.addAssetPair(AssetPair("BTCUSD", "BTC", "USD", 6)) - testDictionariesDatabaseAccessor.addAssetPair(AssetPair("BTCEUR", "BTC", "USD", 6, maxValue = BigDecimal.valueOf(8000))) initServices() } diff --git a/src/test/kotlin/com/lykke/matching/engine/services/validator/MarketOrderValidatorTest.kt b/src/test/kotlin/com/lykke/matching/engine/services/validator/MarketOrderValidatorTest.kt index 8077327de..75eee6b46 100644 --- a/src/test/kotlin/com/lykke/matching/engine/services/validator/MarketOrderValidatorTest.kt +++ b/src/test/kotlin/com/lykke/matching/engine/services/validator/MarketOrderValidatorTest.kt @@ -25,7 +25,6 @@ import org.springframework.test.annotation.DirtiesContext import org.springframework.test.context.junit4.SpringRunner import java.math.BigDecimal import java.util.* -import java.util.concurrent.PriorityBlockingQueue import kotlin.test.assertEquals @RunWith(SpringRunner::class) @@ -34,11 +33,11 @@ import kotlin.test.assertEquals class MarketOrderValidatorTest { companion object { - val CLIENT_NAME = "Client" - val OPERATION_ID = "test" - val ASSET_PAIR_ID = "EURUSD" - val BASE_ASSET_ID = "EUR" - val QUOTING_ASSET_ID = "USD" + private const val CLIENT_NAME = "Client" + private const val OPERATION_ID = "test" + private const val ASSET_PAIR_ID = "EURUSD" + private const val BASE_ASSET_ID = "EUR" + private const val QUOTING_ASSET_ID = "USD" } @TestConfiguration @@ -85,7 +84,7 @@ class MarketOrderValidatorTest { //when try { - marketOrderValidator.performValidation(order, getOrderBook(order.isBuySide()), NewFeeInstruction.create(getFeeInstruction()), null) + marketOrderValidator.performValidation(order, getOrderBook(), NewFeeInstruction.create(getFeeInstruction()), null) } catch (e: OrderValidationException) { assertEquals(OrderStatus.UnknownAsset, e.orderStatus) throw e @@ -102,7 +101,7 @@ class MarketOrderValidatorTest { //when try { - marketOrderValidator.performValidation(order, getOrderBook(order.isBuySide()), NewFeeInstruction.create(getFeeInstruction()), null) + marketOrderValidator.performValidation(order, getOrderBook(), NewFeeInstruction.create(getFeeInstruction()), null) } catch (e: OrderValidationException) { assertEquals(OrderStatus.DisabledAsset, e.orderStatus) throw e @@ -118,7 +117,7 @@ class MarketOrderValidatorTest { //when try { - marketOrderValidator.performValidation(order, getOrderBook(order.isBuySide()), NewFeeInstruction.create(getFeeInstruction()), null) + marketOrderValidator.performValidation(order, getOrderBook(), NewFeeInstruction.create(getFeeInstruction()), null) } catch (e: OrderValidationException) { assertEquals(OrderStatus.TooSmallVolume, e.orderStatus) throw e @@ -133,7 +132,7 @@ class MarketOrderValidatorTest { //when try { - marketOrderValidator.performValidation(order, getOrderBook(order.isBuySide()), + marketOrderValidator.performValidation(order, getOrderBook(), NewFeeInstruction.create(getInvalidFee()), null) } catch (e: OrderValidationException) { assertEquals(OrderStatus.InvalidFee, e.orderStatus) @@ -149,7 +148,7 @@ class MarketOrderValidatorTest { //when try { - marketOrderValidator.performValidation(order, AssetOrderBook(ASSET_PAIR_ID).getOrderBook(order.isBuySide()), + marketOrderValidator.performValidation(order, AssetOrderBook(ASSET_PAIR_ID), NewFeeInstruction.create(getFeeInstruction()), null) } catch (e: OrderValidationException) { assertEquals(OrderStatus.NoLiquidity, e.orderStatus) @@ -166,7 +165,7 @@ class MarketOrderValidatorTest { //when try { - marketOrderValidator.performValidation(order, getOrderBook(order.isBuySide()), NewFeeInstruction.create(getFeeInstruction()), null) + marketOrderValidator.performValidation(order, getOrderBook(), NewFeeInstruction.create(getFeeInstruction()), null) } catch (e: OrderValidationException) { assertEquals(OrderStatus.InvalidVolumeAccuracy, e.orderStatus) throw e @@ -182,7 +181,7 @@ class MarketOrderValidatorTest { //when try { - marketOrderValidator.performValidation(order, getOrderBook(order.isBuySide()), NewFeeInstruction.create(getFeeInstruction()), null) + marketOrderValidator.performValidation(order, getOrderBook(), NewFeeInstruction.create(getFeeInstruction()), null) } catch (e: OrderValidationException) { assertEquals(OrderStatus.InvalidPriceAccuracy, e.orderStatus) throw e @@ -196,7 +195,7 @@ class MarketOrderValidatorTest { val order = toMarketOrder(marketOrderBuilder.build()) //when - marketOrderValidator.performValidation(order, getOrderBook(order.isBuySide()), NewFeeInstruction.create(getFeeInstruction()), null) + marketOrderValidator.performValidation(order, getOrderBook(), NewFeeInstruction.create(getFeeInstruction()), null) } private fun toMarketOrder(message: ProtocolMessages.MarketOrder): MarketOrder { @@ -206,7 +205,7 @@ class MarketOrderValidatorTest { NewFeeInstruction.create(message.fee), listOf(NewFeeInstruction.create(message.fee))) } - private fun getOrderBook(isBuy: Boolean): PriorityBlockingQueue { + private fun getOrderBook(): AssetOrderBook { val assetOrderBook = AssetOrderBook(ASSET_PAIR_ID) val now = Date() assetOrderBook.addOrder(LimitOrder("test", "test", @@ -215,7 +214,13 @@ class MarketOrderValidatorTest { null, null, null, null, null, null, null, null, null, null, null, null)) - return assetOrderBook.getOrderBook(isBuy) + assetOrderBook.addOrder(LimitOrder("test", "test", + ASSET_PAIR_ID, CLIENT_NAME, BigDecimal.valueOf(-1.0), BigDecimal.valueOf(2.0), + OrderStatus.InOrderBook.name, now, now, now, BigDecimal.valueOf(1.0), now, BigDecimal.valueOf(1.0), + null, null, null, null, null, null, null, null, + null, null, null, null)) + + return assetOrderBook } private fun getDefaultMarketOrderBuilder(): ProtocolMessages.MarketOrder.Builder { diff --git a/src/test/kotlin/com/lykke/matching/engine/services/validator/business/LimitOrderBusinessValidatorTest.kt b/src/test/kotlin/com/lykke/matching/engine/services/validator/business/LimitOrderBusinessValidatorTest.kt index 2c4f7d08a..e2d669edf 100644 --- a/src/test/kotlin/com/lykke/matching/engine/services/validator/business/LimitOrderBusinessValidatorTest.kt +++ b/src/test/kotlin/com/lykke/matching/engine/services/validator/business/LimitOrderBusinessValidatorTest.kt @@ -1,5 +1,6 @@ package com.lykke.matching.engine.services.validator.business +import com.lykke.matching.engine.daos.AssetPair import com.lykke.matching.engine.daos.FeeType import com.lykke.matching.engine.daos.LimitOrder import com.lykke.matching.engine.daos.fee.v2.NewLimitOrderFeeInstruction @@ -10,6 +11,7 @@ import com.lykke.matching.engine.order.OrderStatus import com.lykke.matching.engine.services.AssetOrderBook import com.lykke.matching.engine.services.validators.business.impl.LimitOrderBusinessValidatorImpl import com.lykke.matching.engine.services.validators.impl.OrderValidationException +import com.lykke.matching.engine.utils.MessageBuilder.Companion.buildLimitOrder import org.junit.Assert.assertEquals import org.junit.Test import java.math.BigDecimal @@ -18,7 +20,15 @@ import java.util.* class LimitOrderBusinessValidatorTest { private companion object { - private const val ASSET_PAIR_ID = "BTCUSD" + private val ASSET_PAIR = AssetPair("AssetPair", + "Asset1", + "Asset2", + 2) + private val MAX_VALUE_ASSET_PAIR = AssetPair("AssetPair", + "Asset1", + "Asset2", + 2, + maxValue = BigDecimal.valueOf(5.0)) } @Test(expected = OrderValidationException::class) @@ -32,6 +42,7 @@ class LimitOrderBusinessValidatorTest { getLimitOrder(status = OrderStatus.NotFoundPrevious.name, fee = getValidFee()), BigDecimal.valueOf(12.0), BigDecimal.valueOf(11.0), + ASSET_PAIR, getValidOrderBook(), Date(), 0) @@ -53,6 +64,7 @@ class LimitOrderBusinessValidatorTest { getLimitOrder(status = OrderStatus.NotEnoughFunds.name, fee = getValidFee()), BigDecimal.valueOf(12.0), BigDecimal.valueOf(11.0), + ASSET_PAIR, getValidOrderBook(), Date(), 0) @@ -74,6 +86,7 @@ class LimitOrderBusinessValidatorTest { getLimitOrder(fee = getValidFee()), BigDecimal.valueOf(12.0), BigDecimal.valueOf(11.0), + ASSET_PAIR, getValidOrderBook(), Date(), 10) @@ -94,14 +107,39 @@ class LimitOrderBusinessValidatorTest { getLimitOrder(fee = getValidFee()), BigDecimal.valueOf(12.0), BigDecimal.valueOf(11.0), + ASSET_PAIR, getValidOrderBook(), Date(), 0) } - private fun getLimitOrder(fee: LimitOrderFeeInstruction?, + @Test(expected = OrderValidationException::class) + fun testMaxVolume() { + val validator = LimitOrderBusinessValidatorImpl(OrderBookMaxTotalSizeHolderImpl(null)) + + // orderBook with midPrice=2.0 + val orderBook = getValidOrderBook() + orderBook.addOrder(buildLimitOrder(volume = -1.0, price = 3.0)) + orderBook.addOrder(buildLimitOrder(volume = 1.0, price = 1.0)) + + try { + validator.performValidation(false, + getLimitOrder(volume = BigDecimal.valueOf(-2.6), price = BigDecimal.valueOf(1.0)), + BigDecimal.ONE, + BigDecimal.ONE, + MAX_VALUE_ASSET_PAIR, + orderBook, + Date(), + 0) + } catch (e: OrderValidationException) { + assertEquals(OrderStatus.InvalidVolume, e.orderStatus) + throw e + } + } + + private fun getLimitOrder(fee: LimitOrderFeeInstruction? = null, fees: List? = null, - assetPair: String = ASSET_PAIR_ID, + assetPair: String = ASSET_PAIR.assetPairId, price: BigDecimal = BigDecimal.valueOf(1.0), volume: BigDecimal = BigDecimal.valueOf(1.0), status: String = OrderStatus.InOrderBook.name): LimitOrder { @@ -113,11 +151,11 @@ class LimitOrderBusinessValidatorTest { childOrderExternalId = null) } - fun getValidFee(): LimitOrderFeeInstruction { + private fun getValidFee(): LimitOrderFeeInstruction { return LimitOrderFeeInstruction(FeeType.NO_FEE, null, null, null, null, null, null) } private fun getValidOrderBook(): AssetOrderBook { - return AssetOrderBook(ASSET_PAIR_ID) + return AssetOrderBook(ASSET_PAIR.assetPairId) } } \ No newline at end of file diff --git a/src/test/kotlin/com/lykke/matching/engine/services/validator/business/StopOrderBusinessValidatorTest.kt b/src/test/kotlin/com/lykke/matching/engine/services/validator/business/StopOrderBusinessValidatorTest.kt new file mode 100644 index 000000000..dee960cd8 --- /dev/null +++ b/src/test/kotlin/com/lykke/matching/engine/services/validator/business/StopOrderBusinessValidatorTest.kt @@ -0,0 +1,65 @@ +package com.lykke.matching.engine.services.validator.business + +import com.lykke.matching.engine.daos.AssetPair +import com.lykke.matching.engine.daos.LimitOrder +import com.lykke.matching.engine.daos.order.LimitOrderType +import com.lykke.matching.engine.holders.OrderBookMaxTotalSizeHolderImpl +import com.lykke.matching.engine.order.OrderStatus +import com.lykke.matching.engine.services.AssetOrderBook +import com.lykke.matching.engine.services.validators.business.impl.StopOrderBusinessValidatorImpl +import com.lykke.matching.engine.services.validators.impl.OrderValidationException +import com.lykke.matching.engine.utils.MessageBuilder.Companion.buildLimitOrder +import org.junit.Test +import java.math.BigDecimal +import java.util.Date +import kotlin.test.assertEquals + +class StopOrderBusinessValidatorTest { + + companion object { + private val MAX_VALUE_ASSET_PAIR = AssetPair("AssetPair", + "Asset1", + "Asset2", + 2, + maxValue = BigDecimal.valueOf(5.0)) + } + + private val validator = StopOrderBusinessValidatorImpl(OrderBookMaxTotalSizeHolderImpl(null)) + + @Test(expected = OrderValidationException::class) + fun testMaxVolume() { + val stopOrder = buildStopOrder(volume = -2.6, lowerLimitPrice = 1.0, lowerPrice = 1.0) + + // orderBook with midPrice=2.0 + val orderBook = AssetOrderBook(MAX_VALUE_ASSET_PAIR.assetPairId) + orderBook.addOrder(buildLimitOrder(volume = -1.0, price = 3.0)) + orderBook.addOrder(buildLimitOrder(volume = 1.0, price = 1.0)) + + try { + validator.performValidation(BigDecimal.ONE, + BigDecimal.ONE, + stopOrder, + Date(), + MAX_VALUE_ASSET_PAIR, + orderBook, + 0) + } catch (e: OrderValidationException) { + assertEquals(OrderStatus.InvalidVolume, e.orderStatus) + throw e + } + } + + private fun buildStopOrder(volume: Double, + lowerLimitPrice: Double? = null, + lowerPrice: Double? = null, + upperLimitPrice: Double? = null, + upperPrice: Double? = null): LimitOrder { + return buildLimitOrder(type = LimitOrderType.STOP_LIMIT, + volume = volume, + lowerLimitPrice = lowerLimitPrice, + lowerPrice = lowerPrice, + upperLimitPrice = upperLimitPrice, + upperPrice = upperPrice) + } + +} \ No newline at end of file