@Test public void testSell() throws Exception { final CurrencyPair btc = new CurrencyPair("BTC"); final BigMoney total = BigMoney.of(btc.getCounterCurrencyUnit(), 100); final BigMoney reserved = BigMoney.of(btc.getCounterCurrencyUnit(), 80); final BigMoney available = BigMoney.of(btc.getBaseCurrencyUnit(), 10); BuyOrder buyOrder = new BuyOrder(); SellOrder sellOrder = new SellOrder(); OrderBookEntry orderBookEntry = new OrderBookEntry(); PortfolioEntry portfolioEntry = new PortfolioEntry(); portfolioEntry.setAmountOfMoney(total); portfolioEntry.setReservedAmountOfMoney(reserved); portfolioEntry.createItem("BTC", "Bitcoin"); portfolioEntry.addItemInPossession("BTC", available); portfolioEntry.setPrimaryKey(new PortfolioId().toString()); when(userServiceFacade.obtainPortfolioForUser()).thenReturn(portfolioEntry); when(userServiceFacade.isWithdrawPasswordSet()).thenReturn(true); when(userServiceFacade.isWithdrawPasswordMatched(eq("password123"))).thenReturn(true); when(tradeServiceFacade. prepareSellOrder(eq(btc.getBaseCurrency()), eq(TradeController.DEFAULT_CCY_PAIR), eq(orderBookEntry), eq(portfolioEntry))) .thenReturn(sellOrder); when(tradeServiceFacade. prepareBuyOrder(eq(btc.getBaseCurrency()), eq(TradeController.DEFAULT_CCY_PAIR), eq(orderBookEntry), eq(portfolioEntry))) .thenReturn(buyOrder); CoinEntry coinEntry = new CoinEntry(); when(tradeServiceFacade.loadCoin(eq(btc.getBaseCurrency()))).thenReturn(coinEntry); when(userServiceFacade.obtainPortfolioForUser()).thenReturn(portfolioEntry); when(tradeServiceFacade.loadOrderBookByCurrencyPair(eq(btc))).thenReturn(orderBookEntry); when(tradeServiceFacade.calculateSellOrderEffectiveAmount(any(SellOrder.class))) .thenReturn(BigMoney.of(CurrencyUnit.of(btc.getBaseCurrency()), BigDecimal.valueOf(1.3))); final BuyOrder buyOrderPara = new BuyOrder(); buyOrderPara.setAmountCcy(btc.getBaseCurrency()); buyOrderPara.setPriceCcy(btc.getCounterCurrency()); buyOrderPara.setItemPrice(BigDecimal.valueOf(32.988)); buyOrderPara.setTradeAmount(BigDecimal.valueOf(1.2)); mockMvc.perform(post("/sell/" + btc.getBaseCurrency()) .param("itemPrice", "32.988") .param("tradeAmount", "1.2") .param("tradingPassword", "password123") ).andExpect(status().isFound()); verify(tradeServiceFacade) .sellOrder(any(TransactionId.class), eq("BTC"), eq(btc), anyString(), anyString(), eq(BigMoney.of(btc.getBaseCurrencyUnit(), 1.2).toMoney().toBigMoney()), eq(BigMoney.of(btc.getCounterCurrencyUnit(), 32.988))); verify(userServiceFacade).isWithdrawPasswordMatched(eq("password123")); }
@RequestMapping(value = "/sell/{coinId}", method = RequestMethod.POST) public String sell(@PathVariable String coinId, @ModelAttribute("sellOrder") @Valid SellOrder order, BindingResult bindingResult, Model model) { CurrencyPair currencyPair = new CurrencyPair(coinId); CurrencyUnit priceCcy = currencyPair.getCounterCurrencyUnit(); CurrencyUnit coinCcy = currencyPair.getBaseCurrencyUnit(); OrderBookEntry orderBookEntry = tradeServiceFacade.loadOrderBookByCurrencyPair(currencyPair); PortfolioEntry portfolioEntry = userServiceFacade.obtainPortfolioForUser(); if (hasErrors(order, bindingResult, portfolioEntry)) { fillFailedToSellModels(coinId, currencyPair, model, orderBookEntry, portfolioEntry); return "/index"; } final BigDecimal tradeAmount = order.getTradeAmount(); final BigDecimal itemPrice = order.getItemPrice(); final Money price = Money.of(priceCcy, itemPrice, RoundingMode.HALF_EVEN); final Money btcAmount = Money.of(coinCcy, tradeAmount, RoundingMode.HALF_EVEN); final BigMoney totalMoney = tradeServiceFacade.calculateSellOrderEffectiveAmount(order); BigMoney availableCoin = portfolioEntry.obtainAmountOfAvailableItemFor(coinId, coinCcy); if (availableCoin == null || availableCoin.isLessThan(totalMoney)) { bindingResult.rejectValue("tradeAmount", "error.order.sell.notenoughcoins", "Not enough items available to create sell order."); logger.info("rejected a sell order with price {}, amount {}, total money {}: {}.", price, btcAmount, totalMoney, order); fillFailedToSellModels(coinId, currencyPair, model, orderBookEntry, portfolioEntry); return "/index"; } final TransactionId transactionId = new TransactionId(); logger.info("placing a sell transaction {} with price {}, amount {}: {}.", transactionId, price, btcAmount, order); tradeServiceFacade.sellOrder(transactionId, coinId, currencyPair, orderBookEntry.getPrimaryKey(), portfolioEntry.getPrimaryKey(), btcAmount.toBigMoney(), price.toBigMoney()); logger.info("Sell order {} dispatched... ", order); return "redirect:/"; }
TradeController { @RequestMapping(value = "/sell/{coinId}", method = RequestMethod.POST) public String sell(@PathVariable String coinId, @ModelAttribute("sellOrder") @Valid SellOrder order, BindingResult bindingResult, Model model) { CurrencyPair currencyPair = new CurrencyPair(coinId); CurrencyUnit priceCcy = currencyPair.getCounterCurrencyUnit(); CurrencyUnit coinCcy = currencyPair.getBaseCurrencyUnit(); OrderBookEntry orderBookEntry = tradeServiceFacade.loadOrderBookByCurrencyPair(currencyPair); PortfolioEntry portfolioEntry = userServiceFacade.obtainPortfolioForUser(); if (hasErrors(order, bindingResult, portfolioEntry)) { fillFailedToSellModels(coinId, currencyPair, model, orderBookEntry, portfolioEntry); return "/index"; } final BigDecimal tradeAmount = order.getTradeAmount(); final BigDecimal itemPrice = order.getItemPrice(); final Money price = Money.of(priceCcy, itemPrice, RoundingMode.HALF_EVEN); final Money btcAmount = Money.of(coinCcy, tradeAmount, RoundingMode.HALF_EVEN); final BigMoney totalMoney = tradeServiceFacade.calculateSellOrderEffectiveAmount(order); BigMoney availableCoin = portfolioEntry.obtainAmountOfAvailableItemFor(coinId, coinCcy); if (availableCoin == null || availableCoin.isLessThan(totalMoney)) { bindingResult.rejectValue("tradeAmount", "error.order.sell.notenoughcoins", "Not enough items available to create sell order."); logger.info("rejected a sell order with price {}, amount {}, total money {}: {}.", price, btcAmount, totalMoney, order); fillFailedToSellModels(coinId, currencyPair, model, orderBookEntry, portfolioEntry); return "/index"; } final TransactionId transactionId = new TransactionId(); logger.info("placing a sell transaction {} with price {}, amount {}: {}.", transactionId, price, btcAmount, order); tradeServiceFacade.sellOrder(transactionId, coinId, currencyPair, orderBookEntry.getPrimaryKey(), portfolioEntry.getPrimaryKey(), btcAmount.toBigMoney(), price.toBigMoney()); logger.info("Sell order {} dispatched... ", order); return "redirect:/"; } }
TradeController { @RequestMapping(value = "/sell/{coinId}", method = RequestMethod.POST) public String sell(@PathVariable String coinId, @ModelAttribute("sellOrder") @Valid SellOrder order, BindingResult bindingResult, Model model) { CurrencyPair currencyPair = new CurrencyPair(coinId); CurrencyUnit priceCcy = currencyPair.getCounterCurrencyUnit(); CurrencyUnit coinCcy = currencyPair.getBaseCurrencyUnit(); OrderBookEntry orderBookEntry = tradeServiceFacade.loadOrderBookByCurrencyPair(currencyPair); PortfolioEntry portfolioEntry = userServiceFacade.obtainPortfolioForUser(); if (hasErrors(order, bindingResult, portfolioEntry)) { fillFailedToSellModels(coinId, currencyPair, model, orderBookEntry, portfolioEntry); return "/index"; } final BigDecimal tradeAmount = order.getTradeAmount(); final BigDecimal itemPrice = order.getItemPrice(); final Money price = Money.of(priceCcy, itemPrice, RoundingMode.HALF_EVEN); final Money btcAmount = Money.of(coinCcy, tradeAmount, RoundingMode.HALF_EVEN); final BigMoney totalMoney = tradeServiceFacade.calculateSellOrderEffectiveAmount(order); BigMoney availableCoin = portfolioEntry.obtainAmountOfAvailableItemFor(coinId, coinCcy); if (availableCoin == null || availableCoin.isLessThan(totalMoney)) { bindingResult.rejectValue("tradeAmount", "error.order.sell.notenoughcoins", "Not enough items available to create sell order."); logger.info("rejected a sell order with price {}, amount {}, total money {}: {}.", price, btcAmount, totalMoney, order); fillFailedToSellModels(coinId, currencyPair, model, orderBookEntry, portfolioEntry); return "/index"; } final TransactionId transactionId = new TransactionId(); logger.info("placing a sell transaction {} with price {}, amount {}: {}.", transactionId, price, btcAmount, order); tradeServiceFacade.sellOrder(transactionId, coinId, currencyPair, orderBookEntry.getPrimaryKey(), portfolioEntry.getPrimaryKey(), btcAmount.toBigMoney(), price.toBigMoney()); logger.info("Sell order {} dispatched... ", order); return "redirect:/"; }  }
TradeController { @RequestMapping(value = "/sell/{coinId}", method = RequestMethod.POST) public String sell(@PathVariable String coinId, @ModelAttribute("sellOrder") @Valid SellOrder order, BindingResult bindingResult, Model model) { CurrencyPair currencyPair = new CurrencyPair(coinId); CurrencyUnit priceCcy = currencyPair.getCounterCurrencyUnit(); CurrencyUnit coinCcy = currencyPair.getBaseCurrencyUnit(); OrderBookEntry orderBookEntry = tradeServiceFacade.loadOrderBookByCurrencyPair(currencyPair); PortfolioEntry portfolioEntry = userServiceFacade.obtainPortfolioForUser(); if (hasErrors(order, bindingResult, portfolioEntry)) { fillFailedToSellModels(coinId, currencyPair, model, orderBookEntry, portfolioEntry); return "/index"; } final BigDecimal tradeAmount = order.getTradeAmount(); final BigDecimal itemPrice = order.getItemPrice(); final Money price = Money.of(priceCcy, itemPrice, RoundingMode.HALF_EVEN); final Money btcAmount = Money.of(coinCcy, tradeAmount, RoundingMode.HALF_EVEN); final BigMoney totalMoney = tradeServiceFacade.calculateSellOrderEffectiveAmount(order); BigMoney availableCoin = portfolioEntry.obtainAmountOfAvailableItemFor(coinId, coinCcy); if (availableCoin == null || availableCoin.isLessThan(totalMoney)) { bindingResult.rejectValue("tradeAmount", "error.order.sell.notenoughcoins", "Not enough items available to create sell order."); logger.info("rejected a sell order with price {}, amount {}, total money {}: {}.", price, btcAmount, totalMoney, order); fillFailedToSellModels(coinId, currencyPair, model, orderBookEntry, portfolioEntry); return "/index"; } final TransactionId transactionId = new TransactionId(); logger.info("placing a sell transaction {} with price {}, amount {}: {}.", transactionId, price, btcAmount, order); tradeServiceFacade.sellOrder(transactionId, coinId, currencyPair, orderBookEntry.getPrimaryKey(), portfolioEntry.getPrimaryKey(), btcAmount.toBigMoney(), price.toBigMoney()); logger.info("Sell order {} dispatched... ", order); return "redirect:/"; }  @Autowired void setTradeServiceFacade(TradeServiceFacade tradeServiceFacade); @Autowired void setUserServiceFacade(UserServiceFacade userServiceFacade); @SuppressWarnings("unchecked") @RequestMapping(value = "index", method = RequestMethod.GET) String get(Model model); @SuppressWarnings("unchecked") @RequestMapping(value = "refresh", method = RequestMethod.GET) String refresh(); @RequestMapping(value = "/coin/{coinId}", method = RequestMethod.GET) String details(@PathVariable String coinId, Model model); @RequestMapping(value = "/sell/{coinId}", method = RequestMethod.POST) String sell(@PathVariable String coinId,
                       @ModelAttribute("sellOrder") @Valid SellOrder order,
                       BindingResult bindingResult, Model model); @RequestMapping(value = "/buy/{coinId}", method = RequestMethod.POST) String buy(@PathVariable String coinId,
                      @ModelAttribute("buyOrder") @Valid BuyOrder order,
                      BindingResult bindingResult, Model model); }
TradeController { @RequestMapping(value = "/sell/{coinId}", method = RequestMethod.POST) public String sell(@PathVariable String coinId, @ModelAttribute("sellOrder") @Valid SellOrder order, BindingResult bindingResult, Model model) { CurrencyPair currencyPair = new CurrencyPair(coinId); CurrencyUnit priceCcy = currencyPair.getCounterCurrencyUnit(); CurrencyUnit coinCcy = currencyPair.getBaseCurrencyUnit(); OrderBookEntry orderBookEntry = tradeServiceFacade.loadOrderBookByCurrencyPair(currencyPair); PortfolioEntry portfolioEntry = userServiceFacade.obtainPortfolioForUser(); if (hasErrors(order, bindingResult, portfolioEntry)) { fillFailedToSellModels(coinId, currencyPair, model, orderBookEntry, portfolioEntry); return "/index"; } final BigDecimal tradeAmount = order.getTradeAmount(); final BigDecimal itemPrice = order.getItemPrice(); final Money price = Money.of(priceCcy, itemPrice, RoundingMode.HALF_EVEN); final Money btcAmount = Money.of(coinCcy, tradeAmount, RoundingMode.HALF_EVEN); final BigMoney totalMoney = tradeServiceFacade.calculateSellOrderEffectiveAmount(order); BigMoney availableCoin = portfolioEntry.obtainAmountOfAvailableItemFor(coinId, coinCcy); if (availableCoin == null || availableCoin.isLessThan(totalMoney)) { bindingResult.rejectValue("tradeAmount", "error.order.sell.notenoughcoins", "Not enough items available to create sell order."); logger.info("rejected a sell order with price {}, amount {}, total money {}: {}.", price, btcAmount, totalMoney, order); fillFailedToSellModels(coinId, currencyPair, model, orderBookEntry, portfolioEntry); return "/index"; } final TransactionId transactionId = new TransactionId(); logger.info("placing a sell transaction {} with price {}, amount {}: {}.", transactionId, price, btcAmount, order); tradeServiceFacade.sellOrder(transactionId, coinId, currencyPair, orderBookEntry.getPrimaryKey(), portfolioEntry.getPrimaryKey(), btcAmount.toBigMoney(), price.toBigMoney()); logger.info("Sell order {} dispatched... ", order); return "redirect:/"; }  @Autowired void setTradeServiceFacade(TradeServiceFacade tradeServiceFacade); @Autowired void setUserServiceFacade(UserServiceFacade userServiceFacade); @SuppressWarnings("unchecked") @RequestMapping(value = "index", method = RequestMethod.GET) String get(Model model); @SuppressWarnings("unchecked") @RequestMapping(value = "refresh", method = RequestMethod.GET) String refresh(); @RequestMapping(value = "/coin/{coinId}", method = RequestMethod.GET) String details(@PathVariable String coinId, Model model); @RequestMapping(value = "/sell/{coinId}", method = RequestMethod.POST) String sell(@PathVariable String coinId,
                       @ModelAttribute("sellOrder") @Valid SellOrder order,
                       BindingResult bindingResult, Model model); @RequestMapping(value = "/buy/{coinId}", method = RequestMethod.POST) String buy(@PathVariable String coinId,
                      @ModelAttribute("buyOrder") @Valid BuyOrder order,
                      BindingResult bindingResult, Model model);  }