From 436c889d2c1c4fd3d0b03f9bea9676c71e9ac3c4 Mon Sep 17 00:00:00 2001 From: Na Lee Ha Date: Mon, 27 Nov 2023 09:26:12 +0000 Subject: [PATCH 01/21] #966 when updating basket trade cell value, only update if different from current value --- .../basket/service/BasketTradingService.scala | 73 +++++++++++-------- .../basket/BasketMutateOffMarketTest.scala | 70 +++++++++++++++--- 2 files changed, 102 insertions(+), 41 deletions(-) diff --git a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingService.scala b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingService.scala index 488935d19..a738b2887 100644 --- a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingService.scala +++ b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingService.scala @@ -4,20 +4,21 @@ import com.typesafe.scalalogging.StrictLogging import org.finos.toolbox.time.Clock import org.finos.vuu.core.module.basket.BasketModule import org.finos.vuu.core.module.basket.BasketModule.{BasketTradingConstituentTable, Sides} -import org.finos.vuu.core.table.{DataTable, RowWithData, TableContainer} +import org.finos.vuu.core.table.{DataTable, RowWithData, TableContainer, ViewPortColumnCreator} import org.finos.vuu.net.rpc.{EditRpcHandler, RpcHandler} import org.finos.vuu.net.{ClientSessionId, RequestContext} import org.finos.vuu.order.oms.{NewOrder, OmsApi} import org.finos.vuu.viewport._ - -trait BasketTradingServiceIF extends EditRpcHandler { +trait BasketTradingServiceIF extends EditRpcHandler{ def sendToMarket(basketInstanceId: String)(ctx: RequestContext): ViewPortAction } + class BasketTradingService(val table: DataTable, val tableContainer: TableContainer, val omsApi: OmsApi)(implicit clock: Clock) extends RpcHandler with BasketTradingServiceIF with StrictLogging { import org.finos.vuu.core.module.basket.BasketModule.{BasketTradingColumnNames => BT, BasketTradingConstituentColumnNames => BTC} + /** * Send basket to market */ @@ -54,37 +55,47 @@ class BasketTradingService(val table: DataTable, val tableContainer: TableContai } private def onEditCell(key: String, columnName: String, data: Any, vp: ViewPort, session: ClientSessionId): ViewPortEditAction = { - logger.info("Changing cell value for key:" + key + "(" + columnName + ":" + data + ")") - table.processUpdate(key, RowWithData(key, Map(BT.InstanceId -> key, columnName -> data)), clock.now()) - - columnName match { - case BT.Units => - val constituentTable = tableContainer.getTable(BasketTradingConstituentTable) - val constituents = constituentTable.primaryKeys.map(key => constituentTable.pullRow(key)).filter(_.get(BTC.InstanceId) == key) - constituents.foreach(row => { - val unitsAsInt = data.asInstanceOf[Int] - val weighting = row.get(BTC.Weighting) - val quantity = (weighting.asInstanceOf[Double] * unitsAsInt).toLong - constituentTable.processUpdate(row.key(), RowWithData(row.key(), Map(BTC.InstanceIdRic -> row.key(), BTC.Quantity -> quantity)), clock.now()) - }) - case BT.Side => - val constituentTable = tableContainer.getTable(BasketTradingConstituentTable) - val constituents = constituentTable.primaryKeys.map(key => constituentTable.pullRow(key)).filter(_.get(BTC.InstanceId) == key) - val side = data.asInstanceOf[String] - constituents.foreach(row => { - val newSide = row.get(BTC.Side) match { - case Sides.Buy => Sides.Sell - case _ => Sides.Buy - } - constituentTable.processUpdate(row.key(), RowWithData(row.key(), Map(BTC.InstanceIdRic -> row.key(), BTC.Side -> newSide)), clock.now()) - }) - - case _ => - } + logger.info("Change requested for cell value for key:" + key + "(" + columnName + ":" + data + ")") + val currentData = getRowData(key, columnName) + if (currentData == data) { + logger.info("Current cell value is same and therefore skipping update for key:" + key + "(" + columnName + ":" + data + ")") + } + else { + logger.info("Changing cell value for key:" + key + "(" + columnName + ":" + data + ")") + table.processUpdate(key, RowWithData(key, Map(BT.InstanceId -> key, columnName -> data)), clock.now()) + + columnName match { + case BT.Units => + val constituentTable = tableContainer.getTable(BasketTradingConstituentTable) + val constituents = constituentTable.primaryKeys.map(key => constituentTable.pullRow(key)).filter(_.get(BTC.InstanceId) == key) + constituents.foreach(row => { + val unitsAsInt = data.asInstanceOf[Int] + val weighting = row.get(BTC.Weighting) + val quantity = (weighting.asInstanceOf[Double] * unitsAsInt).toLong + constituentTable.processUpdate(row.key(), RowWithData(row.key(), Map(BTC.InstanceIdRic -> row.key(), BTC.Quantity -> quantity)), clock.now()) + }) + case BT.Side => + val constituentTable = tableContainer.getTable(BasketTradingConstituentTable) + val constituents = constituentTable.primaryKeys.map(key => constituentTable.pullRow(key)).filter(_.get(BTC.InstanceId) == key) + constituents.foreach(row => { + val newSide = row.get(BTC.Side) match { + case Sides.Buy => Sides.Sell + case _ => Sides.Buy + } + constituentTable.processUpdate(row.key(), RowWithData(row.key(), Map(BTC.InstanceIdRic -> row.key(), BTC.Side -> newSide)), clock.now()) + }) + case _ => + } + } ViewPortEditSuccess() } + private def getRowData(rowKey: String, columnName: String): Any = { + val row = table.pullRow(rowKey, ViewPortColumnCreator.create(table, List(columnName))) + row.get(columnName) + } + override def deleteRowAction(): ViewPortDeleteRowAction = ??? override def deleteCellAction(): ViewPortDeleteCellAction = ??? @@ -98,6 +109,4 @@ class BasketTradingService(val table: DataTable, val tableContainer: TableContai override def onFormSubmit(): ViewPortFormSubmitAction = ??? override def onFormClose(): ViewPortFormCloseAction = ??? - - } diff --git a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala index ead49b52c..b281cd311 100644 --- a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala +++ b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala @@ -5,10 +5,11 @@ import org.finos.toolbox.lifecycle.LifecycleContainer import org.finos.toolbox.time.{Clock, TestFriendlyClock} import org.finos.vuu.api.ViewPortDef import org.finos.vuu.core.module.TableDefContainer +import org.finos.vuu.core.module.basket.BasketModule.{BasketColumnNames => B, BasketConstituentColumnNames => BC} import org.finos.vuu.core.module.basket.service.{BasketServiceIF, BasketTradingServiceIF} import org.finos.vuu.core.module.price.PriceModule import org.finos.vuu.order.oms.OmsApi -import org.finos.vuu.test.VuuServerTestCase +import org.finos.vuu.test.{TestVuuServer, VuuServerTestCase} import org.finos.vuu.util.table.TableAsserts.assertVpEq import org.scalatest.prop.Tables.Table @@ -17,17 +18,68 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { import BasketTestCaseHelper._ Feature("Basket Service Test Case") { + implicit val clock: Clock = new TestFriendlyClock(10001L) + implicit val lifecycle: LifecycleContainer = new LifecycleContainer() + implicit val tableDefContainer: TableDefContainer = new TableDefContainer(Map()) + implicit val metricsProvider: MetricsProvider = new MetricsProviderImpl + import BasketModule._ - Scenario("Check the creation of the baskets and constituents") { + val omsApi = OmsApi() + + Scenario("Check updating trade basket side with no change does not update constituents side") { + withVuuServer(PriceModule(), BasketModule(omsApi)) { + vuuServer => + + vuuServer.login("testUser", "testToken") + + GivenBasketTradeExist(vuuServer, ".FTSE", "chris-001") + + val vpBasketTrading = vuuServer.createViewPort(BasketModule.NAME, BasketTradingTable) + val vpBasketTradingCons = vuuServer.createViewPort(BasketModule.NAME, BasketTradingConstituentTable) + val basketTradingService = vuuServer.getViewPortRpcServiceProxy[BasketTradingServiceIF](vpBasketTrading) - implicit val clock: Clock = new TestFriendlyClock(10001L) - implicit val lifecycle: LifecycleContainer = new LifecycleContainer() - implicit val tableDefContainer: TableDefContainer = new TableDefContainer(Map()) - implicit val metricsProvider: MetricsProvider = new MetricsProviderImpl + When("we edit the side of the parent basket to same side as current value") + basketTradingService.editCellAction().func("chris-001", "side", "Buy", vpBasketTrading, vuuServer.session) + vuuServer.runOnce() - val omsApi = OmsApi() + Then("get all the updates that have occurred for all view ports from the outbound queue") + val updates = combineQs(vpBasketTrading) - import BasketModule._ + And("assert the basket trading table has not changed side....") + assertVpEq(filterByVp(vpBasketTrading, updates)) { + Table( + ("instanceId", "basketId", "basketName", "status", "units", "filledPct", "fxRateToUsd", "totalNotional", "totalNotionalUsd", "side"), + ("chris-001", ".FTSE", "chris-001", "OFF-MARKET", 1, null, null, null, null, "Buy") + ) + } + + And("assert the basket trading constituent table has not changed sides") + assertVpEq(filterByVp(vpBasketTradingCons, updates)) { + Table( + ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId", "filledQty","orderStatus"), + (10L, "Buy", "chris-001", "chris-001.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (10L, "Sell", "chris-001", "chris-001.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (10L, "Buy", "chris-001", "chris-001.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") + ) + } + } + } + + def GivenBasketTradeExist(vuuServer: TestVuuServer, basketId: String, basketTradeName: String): Unit = { + val basketProvider = vuuServer.getProvider(BasketModule.NAME, BasketTable) + basketProvider.tick(".FTSE", Map(B.Id -> ".FTSE", B.Name -> ".FTSE 100", B.NotionalValue -> 1000001, B.NotionalValueUsd -> 1500001)) + + val constituentProvider = vuuServer.getProvider(BasketModule.NAME, BasketConstituentTable) + constituentProvider.tick("VOD.L.FTSE", Map(BC.RicBasketId -> "VOD.L.FTSE", BC.Ric -> "VOD.L", BC.BasketId -> basketId, BC.Weighting -> 0.1, BC.Side -> "Buy", BC.Description -> "Vodafone")) + constituentProvider.tick("BT.L.FTSE", Map(BC.RicBasketId -> "BT.L.FTSE", BC.Ric -> "BT.L", BC.BasketId -> basketId, BC.Weighting -> 0.1, BC.Side -> "Sell", BC.Description -> "British Telecom")) + constituentProvider.tick("BP.L.FTSE", Map(BC.RicBasketId -> "BP.L.FTSE", BC.Ric -> "BP.L", BC.BasketId -> basketId, BC.Weighting -> 0.1, BC.Side -> "Buy", BC.Description -> "Beyond Petroleum")) + + val vpBasket = vuuServer.createViewPort(BasketModule.NAME, BasketTable) + val basketService = vuuServer.getViewPortRpcServiceProxy[BasketServiceIF](vpBasket) + basketService.createBasket(basketId, basketTradeName)(vuuServer.requestContext) + } + + Scenario("Check the creation of the baskets and constituents") { withVuuServer(PriceModule(), BasketModule(omsApi)) { vuuServer => @@ -119,7 +171,7 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { //vuuServer.runOnce() And("assert the basket trading constituent table has flipped sides also") - assertVpEq(filterByVp(vpBasketTradingCons,updates)) { + assertVpEq(filterByVp(vpBasketTradingCons, updates)) { Table( ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId", "filledQty", "orderStatus"), (10L, "Sell", "chris-001", "chris-001.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), From c4c6afb754f03ff89d044723d682468caea09f17 Mon Sep 17 00:00:00 2001 From: Na Lee Ha Date: Mon, 27 Nov 2023 09:36:38 +0000 Subject: [PATCH 02/21] #982 initial implementation of editing basket trade constituent side --- .../BasketTradingConstituentJoinService.scala | 71 +++++--- .../basket/BasketMutateOffMarketTest.scala | 165 +++++++++++++++++- .../basket/TestHelper/TestDataFactory.scala | 65 +++++++ .../org/finos/vuu/test/TestVuuServer.scala | 1 + 4 files changed, 273 insertions(+), 29 deletions(-) create mode 100644 example/basket/src/test/scala/org/finos/vuu/core/module/basket/TestHelper/TestDataFactory.scala diff --git a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala index 6a6685641..96e637d80 100644 --- a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala +++ b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala @@ -3,13 +3,22 @@ package org.finos.vuu.core.module.basket.service import com.typesafe.scalalogging.StrictLogging import org.finos.toolbox.time.Clock import org.finos.vuu.api.JoinTableDef -import org.finos.vuu.core.module.basket.BasketModule.BasketTradingConstituentColumnNames.InstanceIdRic +import org.finos.vuu.core.module.basket.BasketConstants.Side +import org.finos.vuu.core.module.basket.BasketModule.{BasketTradingConstituentColumnNames => ColumnName} import org.finos.vuu.core.table.{DataTable, JoinTable, RowWithData, TableContainer} import org.finos.vuu.net.ClientSessionId import org.finos.vuu.net.rpc.{EditRpcHandler, RpcHandler} import org.finos.vuu.viewport._ -class BasketTradingConstituentJoinService(val table: DataTable, val tableContainer: TableContainer)(implicit clock: Clock) extends RpcHandler with EditRpcHandler with StrictLogging { +trait BasketTradingConstituentJoinServiceIF extends EditRpcHandler { + def setSell(selection: ViewPortSelection, session: ClientSessionId): ViewPortAction + + def setBuy(selection: ViewPortSelection, session: ClientSessionId): ViewPortAction +} + +object BasketTradingConstituentJoinService {} + +class BasketTradingConstituentJoinService(val table: DataTable, val tableContainer: TableContainer)(implicit clock: Clock) extends BasketTradingConstituentJoinServiceIF with RpcHandler with StrictLogging { def onDeleteRow(key: String, vp: ViewPort, session: ClientSessionId): ViewPortEditAction = { ViewPortEditSuccess() @@ -28,7 +37,7 @@ class BasketTradingConstituentJoinService(val table: DataTable, val tableContain val baseTableDef = joinTable.getTableDef.asInstanceOf[JoinTableDef].baseTable joinTable.sourceTables.get(baseTableDef.name) match { case Some(table: DataTable) => - table.processUpdate(key, RowWithData(key, Map(InstanceIdRic -> key, columnName -> data)), clock.now()) + table.processUpdate(key, RowWithData(key, Map(ColumnName.InstanceIdRic -> key, columnName -> data)), clock.now()) ViewPortEditSuccess() case None => ViewPortEditFailure("Could not find base table") @@ -42,18 +51,18 @@ class BasketTradingConstituentJoinService(val table: DataTable, val tableContain } private def onFormSubmit(vp: ViewPort, session: ClientSessionId): ViewPortAction = { -// val table = vp.table.asTable -// val primaryKeys = table.primaryKeys -// val headKey = primaryKeys.head -// val sequencerNumber = table.pullRow(headKey).get("sequenceNumber").asInstanceOf[Int].toLong -// -// if (sequencerNumber > 0) { -// logger.info("I would now send this fix seq to a fix engine to reset, we're all good:" + sequencerNumber) -// CloseDialogViewPortAction(vp.id) -// } else { -// logger.error("Seq number not set, returning error") -// ViewPortEditFailure("Sequencer number has not been set.") -// } + // val table = vp.table.asTable + // val primaryKeys = table.primaryKeys + // val headKey = primaryKeys.head + // val sequencerNumber = table.pullRow(headKey).get("sequenceNumber").asInstanceOf[Int].toLong + // + // if (sequencerNumber > 0) { + // logger.info("I would now send this fix seq to a fix engine to reset, we're all good:" + sequencerNumber) + // CloseDialogViewPortAction(vp.id) + // } else { + // logger.error("Seq number not set, returning error") + // ViewPortEditFailure("Sequencer number has not been set.") + // } CloseDialogViewPortAction(vp.id) } @@ -61,7 +70,6 @@ class BasketTradingConstituentJoinService(val table: DataTable, val tableContain CloseDialogViewPortAction(vp.id) } - override def deleteRowAction(): ViewPortDeleteRowAction = ViewPortDeleteRowAction("", this.onDeleteRow) override def deleteCellAction(): ViewPortDeleteCellAction = ViewPortDeleteCellAction("", this.onDeleteCell) @@ -77,16 +85,39 @@ class BasketTradingConstituentJoinService(val table: DataTable, val tableContain override def onFormClose(): ViewPortFormCloseAction = ViewPortFormCloseAction("", this.onFormClose) def setSell(selection: ViewPortSelection, session: ClientSessionId): ViewPortAction = { - ViewPortEditSuccess() + updateSelected(selection, Map(ColumnName.Side -> Side.Sell)) } def setBuy(selection: ViewPortSelection, session: ClientSessionId): ViewPortAction = { + updateSelected(selection, Map(ColumnName.Side -> Side.Buy)) + } + + private def updateSelected(selection: ViewPortSelection, updates: Map[String, Any]): ViewPortAction = { + val selectedKeys = selection.rowKeyIndex.map({ case (key, _) => key }).toList + selectedKeys.foreach(key => { + //require source table primary key for join table updates + val sourceTableKey = Map(ColumnName.InstanceIdRic -> key) + update(key, sourceTableKey ++ updates) + }) + //todo handle error + //todo care about checking current value? simpler not to ViewPortEditSuccess() } + //todo can update multiple rows in one go? + private def update(key: String, keyValuesToUpdate: Map[String, Any]): Unit = { + val joinTable = table.asTable.asInstanceOf[JoinTable] + val baseTableDef = joinTable.getTableDef.asInstanceOf[JoinTableDef].baseTable + joinTable.sourceTables.get(baseTableDef.name) match { + case Some(table: DataTable) => + table.processUpdate(key, RowWithData(key, keyValuesToUpdate), clock.now()) + ViewPortEditSuccess() + case None => + ViewPortEditFailure("Could not find base table") + } + } override def menuItems(): ViewPortMenu = ViewPortMenu("Direction", - new SelectionViewPortMenuItem("Set Sell", "", this.setSell, "SET_SELECTION_SELL"), - new SelectionViewPortMenuItem("Set Buy", "", this.setBuy, "SET_SELECTION_Buy") + new SelectionViewPortMenuItem("Set Sell", "", this.setSell, "SET_SELECTION_SELL"), + new SelectionViewPortMenuItem("Set Buy", "", this.setBuy, "SET_SELECTION_Buy") ) - } diff --git a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala index b281cd311..4d900fa24 100644 --- a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala +++ b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala @@ -3,27 +3,30 @@ package org.finos.vuu.core.module.basket import org.finos.toolbox.jmx.{MetricsProvider, MetricsProviderImpl} import org.finos.toolbox.lifecycle.LifecycleContainer import org.finos.toolbox.time.{Clock, TestFriendlyClock} -import org.finos.vuu.api.ViewPortDef +import org.finos.vuu.api.{JoinTableDef, ViewPortDef} import org.finos.vuu.core.module.TableDefContainer import org.finos.vuu.core.module.basket.BasketModule.{BasketColumnNames => B, BasketConstituentColumnNames => BC} -import org.finos.vuu.core.module.basket.service.{BasketServiceIF, BasketTradingServiceIF} +import org.finos.vuu.core.module.basket.TestHelper.TestDataFactory +import org.finos.vuu.core.module.basket.service.{BasketServiceIF, BasketTradingConstituentJoinServiceIF, BasketTradingServiceIF} import org.finos.vuu.core.module.price.PriceModule import org.finos.vuu.order.oms.OmsApi +import org.finos.vuu.core.table.{DataTable, JoinTable, RowWithData, TableContainer} import org.finos.vuu.test.{TestVuuServer, VuuServerTestCase} import org.finos.vuu.util.table.TableAsserts.assertVpEq +import org.finos.vuu.viewport.ViewPortSelection import org.scalatest.prop.Tables.Table class BasketMutateOffMarketTest extends VuuServerTestCase { import BasketTestCaseHelper._ + import BasketModule._ - Feature("Basket Service Test Case") { - implicit val clock: Clock = new TestFriendlyClock(10001L) - implicit val lifecycle: LifecycleContainer = new LifecycleContainer() - implicit val tableDefContainer: TableDefContainer = new TableDefContainer(Map()) - implicit val metricsProvider: MetricsProvider = new MetricsProviderImpl - import BasketModule._ + implicit val clock: Clock = new TestFriendlyClock(10001L) + implicit val lifecycle: LifecycleContainer = new LifecycleContainer() + implicit val tableDefContainer: TableDefContainer = new TableDefContainer(Map()) + implicit val metricsProvider: MetricsProvider = new MetricsProviderImpl + Feature("Basket Service Test Case") { val omsApi = OmsApi() Scenario("Check updating trade basket side with no change does not update constituents side") { @@ -56,7 +59,7 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { And("assert the basket trading constituent table has not changed sides") assertVpEq(filterByVp(vpBasketTradingCons, updates)) { Table( - ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId", "filledQty","orderStatus"), + ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId", "filledQty", "orderStatus"), (10L, "Buy", "chris-001", "chris-001.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), (10L, "Sell", "chris-001", "chris-001.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), (10L, "Buy", "chris-001", "chris-001.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") @@ -194,5 +197,149 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { } } } + + Scenario("Check updating trade basket side with no change does not update constituents side") { + withVuuServer(PriceModule(), BasketModule()) { + vuuServer => + + vuuServer.login("testUser", "testToken") + + GivenBasketTradeExist(vuuServer, ".FTSE", "chris-001") + + val vpBasketTrading = vuuServer.createViewPort(BasketModule.NAME, BasketTradingTable) + val vpBasketTradingCons = vuuServer.createViewPort(BasketModule.NAME, BasketTradingConstituentTable) + val basketTradingService = vuuServer.getViewPortRpcServiceProxy[BasketTradingServiceIF](vpBasketTrading) + + When("we edit the side of the parent basket to same side as current value") + basketTradingService.editCellAction().func("chris-001", "side", "Buy", vpBasketTrading, vuuServer.session) + vuuServer.runOnce() + + Then("get all the updates that have occurred for all view ports from the outbound queue") + val updates = combineQs(vpBasketTrading) + + And("assert the basket trading table has not changed side....") + assertVpEq(filterByVp(vpBasketTrading, updates)) { + Table( + ("instanceId", "basketId", "basketName", "status", "units", "filledPct", "fxRateToUsd", "totalNotional", "totalNotionalUsd", "side"), + ("chris-001", ".FTSE", "chris-001", "OFF-MARKET", 1, null, null, null, null, "Buy") + ) + } + + And("assert the basket trading constituent table has not changed sides") + assertVpEq(filterByVp(vpBasketTradingCons, updates)) { + Table( + ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId"), + (10L, "Buy", "chris-001", "chris-001.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2), + (10L, "Sell", "chris-001", "chris-001.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2), + (10L, "Buy", "chris-001", "chris-001.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2) + ) + } + } + } + } + + Feature("Basket Trading Constituent Join Service Test Case") { + Scenario("Update selected trade constituent sides when menu action is triggered") { + withVuuServer(PriceModule(), BasketModule()) { + vuuServer => + + vuuServer.login("testUser", "testToken") + + val basketName = "test_basket1" + val tradeId = GivenBasketTrade(vuuServer.tableContainer, basketName, "Buy") + GivenPrices(vuuServer.tableContainer, List(("VOD.L", 1.1, 1.4), ("BP.L", 2.1, 2.4))) + GivenBasketTradeConstituentsJoin(vuuServer.tableContainer, tradeId, Map(("VOD.L" -> "Buy"), ("BP.L" -> "Sell"))) + + val vpBasketTrading = vuuServer.createViewPort(BasketModule.NAME, BasketTradingTable) + val vpBasketTradingConsJoin = vuuServer.createViewPort(BasketModule.NAME, BasketTradingConstituentJoin) + vuuServer.runOnce() + + When("we select multiple constituent rows ") + vpBasketTradingConsJoin.setSelection(Array(1, 2)) + + And("select set sell context menu") + val basketTradingConstituentJoinService = vuuServer.getViewPortRpcServiceProxy[BasketTradingConstituentJoinServiceIF](vpBasketTradingConsJoin) + val selection = vpBasketTradingConsJoin.getSelection + val vpSelection = ViewPortSelection(selection, vpBasketTradingConsJoin) + basketTradingConstituentJoinService.setSell(vpSelection, vuuServer.session) + vuuServer.runOnce() + + Then("get all the updates that have occurred for all view ports from the outbound queue") + val updates = combineQs(vpBasketTrading) + + And("assert only selected constituent has changed set side to sell") + assertVpEq(filterByVp(vpBasketTradingConsJoin, updates)) { + Table( + ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId", "bid", "bidSize", "ask", "askSize"), + (10L, "Buy", "chris-001", "chris-001.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 2.1, 1, 2.4, 1), + (10L, "Sell", "chris-001", "chris-001.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, null, null, null, null), + (10L, "Sell", "chris-001", "chris-001.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 1.1, 1, 1.4, 1) + ) + } + } + } + } + + def uuid = java.util.UUID.randomUUID.toString + def GivenBasketTradeExist(vuuServer: TestVuuServer, basketId: String, basketTradeName: String): Unit = { + val basketProvider = vuuServer.getProvider(BasketModule.NAME, BasketTable) + basketProvider.tick(".FTSE", Map(B.Id -> ".FTSE", B.Name -> ".FTSE 100", B.NotionalValue -> 1000001, B.NotionalValueUsd -> 1500001)) + + val constituentProvider = vuuServer.getProvider(BasketModule.NAME, BasketConstituentTable) + constituentProvider.tick("VOD.L.FTSE", Map(BC.RicBasketId -> "VOD.L.FTSE", BC.Ric -> "VOD.L", BC.BasketId -> basketId, BC.Weighting -> 0.1, BC.Side -> "Buy", BC.Description -> "Vodafone")) + constituentProvider.tick("BT.L.FTSE", Map(BC.RicBasketId -> "BT.L.FTSE", BC.Ric -> "BT.L", BC.BasketId -> basketId, BC.Weighting -> 0.1, BC.Side -> "Sell", BC.Description -> "British Telecom")) + constituentProvider.tick("BP.L.FTSE", Map(BC.RicBasketId -> "BP.L.FTSE", BC.Ric -> "BP.L", BC.BasketId -> basketId, BC.Weighting -> 0.1, BC.Side -> "Buy", BC.Description -> "Beyond Petroleum")) + + val vpBasket = vuuServer.createViewPort(BasketModule.NAME, BasketTable) + val basketService = vuuServer.getViewPortRpcServiceProxy[BasketServiceIF](vpBasket) + basketService.createBasket(basketId, basketTradeName)(vuuServer.requestContext) } + + def GivenBasketTrade(tableContainer: TableContainer, basketName: String, side: String): String = { + val table = tableContainer.getTable(BasketModule.BasketTradingTable) + val rowKey = s"$uuid.$basketName" + table.processUpdate(rowKey, TestDataFactory.createBasketTradingRow(rowKey, basketName, side), clock.now()) + rowKey + } + + def GivenBasketTradeConstituents(tableContainer: TableContainer, tradeId: String, ricToSide: Map[String, String]): Unit = { + val table = tableContainer.getTable(BasketModule.BasketTradingConstituentTable) + ricToSide.foreach(item => { + val (ric, side) = item + val row = TestDataFactory.createBasketTradingConstituentRow(tradeId, ric, side) + table.processUpdate(row.key, row, clock.now()) + }) + } + + def GivenBasketTradeConstituentsJoin(tableContainer: TableContainer, tradeId: String, ricToSide: Map[String, String]): Unit = { + val table = tableContainer.getTable(BasketModule.BasketTradingConstituentJoin) + ricToSide.foreach(item => { + val (ric, side) = item + val row = TestDataFactory.createBasketTradingConstituentJoinRow(tradeId, ric, side) + updateJoinTable(table, row) + }) + } + + def updateJoinTable(table: DataTable, row: RowWithData): Unit = { + val joinTable = table.asTable.asInstanceOf[JoinTable] + val baseTableDef = joinTable.getTableDef.asInstanceOf[JoinTableDef].baseTable + joinTable.sourceTables.get(baseTableDef.name) match { + case Some(table: DataTable) => + table.processUpdate(row.key, row, clock.now()) + case None => + //log and throw? + } + } + + def GivenPrices(tableContainer: TableContainer, prices: List[(String, Double, Double)]) = { + val table = tableContainer.getTable("prices") + for ((ric, bid, ask) <- prices) { + val rowKey = s"$uuid" + table.processUpdate(rowKey, TestDataFactory.createPricesRow(rowKey, ric, bid, ask), clock.now()) + } + } + + //todo having issue importing side constant + //todo introduce case class BasketTrading? + } \ No newline at end of file diff --git a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/TestHelper/TestDataFactory.scala b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/TestHelper/TestDataFactory.scala new file mode 100644 index 000000000..a7ce889b1 --- /dev/null +++ b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/TestHelper/TestDataFactory.scala @@ -0,0 +1,65 @@ +package org.finos.vuu.core.module.basket.TestHelper + +import org.finos.vuu.core.table.RowWithData +import org.finos.vuu.core.module.basket.BasketModule.{Sides, BasketColumnNames => B, BasketConstituentColumnNames => BC, BasketTradingColumnNames => BT, BasketTradingConstituentColumnNames => BTC} + +object TestDataFactory { + def uuid = java.util.UUID.randomUUID.toString + def createBasketTradingRow(rowKey: String, basketName: String, side: String): RowWithData = + RowWithData(rowKey, Map(BT.InstanceId -> rowKey, BT.Status -> "OFF-MARKET", BT.BasketId -> ".FTSE", BT.BasketName -> basketName, BT.Side -> side, BT.Units -> 1)) + + def createBasketTradingConstituentRow(basketTradeInstanceId: String, constituentRic: String, side: String): RowWithData = { + RowWithData( + key = uuid, + data = Map( + BTC.BasketId -> "someBasketId", + BTC.Ric -> constituentRic, + BTC.InstanceId -> basketTradeInstanceId, + BTC.Quantity -> 40 , + BTC.InstanceIdRic -> s"$basketTradeInstanceId.$constituentRic", + BTC.Description -> "some instrument description", + BTC.Side -> side, + BTC.Weighting -> 0.4, + BTC.PriceStrategyId -> 2, + BTC.Algo -> -1, + ) + ) + } + + + //todo does row key need to match instance id (primary key) + def createBasketTradingConstituentJoinRow(basketTradeInstanceId: String, constituentRic: String, side: String): RowWithData = { + RowWithData( + key = uuid, + data = Map( + BTC.BasketId -> "someBasketId", + BTC.Ric -> constituentRic, + BTC.InstanceId -> basketTradeInstanceId, + BTC.Quantity -> 40, + BTC.InstanceIdRic -> s"$basketTradeInstanceId.$constituentRic", + BTC.Description -> "some instrument description", + BTC.Side -> side, + BTC.Weighting -> 0.4, + BTC.PriceStrategyId -> 2, + BTC.Algo -> -1, + "bid" -> 1.1, + "bidSize" -> 1, + "ask" -> 1.5, + "askSize" -> 1, + ) + ) + } + + def createPricesRow(rowKey: String, ric: String, bid: Double, ask: Double): RowWithData = { + RowWithData( + key = rowKey, + data = Map( + "ric" -> ric, + "bid" -> bid, + "bidSize" -> 1, + "ask" -> ask, + "askSize" -> 1, + ) + ) + } +} diff --git a/vuu/src/test/scala/org/finos/vuu/test/TestVuuServer.scala b/vuu/src/test/scala/org/finos/vuu/test/TestVuuServer.scala index 012ad3216..23bb1779a 100644 --- a/vuu/src/test/scala/org/finos/vuu/test/TestVuuServer.scala +++ b/vuu/src/test/scala/org/finos/vuu/test/TestVuuServer.scala @@ -103,4 +103,5 @@ trait TestVuuServer { def overrideViewPortDef(table: String, vpDefFunc: (DataTable, Provider, ProviderContainer, TableContainer) => ViewPortDef): Unit def getViewPortRpcServiceProxy[TYPE: _root_.scala.reflect.ClassTag](viewport: ViewPort): TYPE def requestContext: RequestContext + def tableContainer: TableContainer } From 6463da7a91cb2c13f4d16638e569d76017f1ea60 Mon Sep 17 00:00:00 2001 From: Na Lee Ha Date: Mon, 27 Nov 2023 09:41:54 +0000 Subject: [PATCH 03/21] #979 using unique instanceId for the basket trade separate from basket trade name entered by user --- .../module/basket/service/BasketService.scala | 73 ++++++++++--------- 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketService.scala b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketService.scala index a83ad5de8..0f815215a 100644 --- a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketService.scala +++ b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketService.scala @@ -3,12 +3,12 @@ package org.finos.vuu.core.module.basket.service import com.typesafe.scalalogging.StrictLogging import org.finos.toolbox.time.Clock import org.finos.vuu.core.module.basket.BasketModule -import org.finos.vuu.core.module.basket.BasketModule.{BasketConstituentTable, PriceStrategy, Sides} +import org.finos.vuu.core.module.basket.BasketModule.{BasketConstituentTable, Sides} import org.finos.vuu.core.module.basket.service.BasketService.counter import org.finos.vuu.core.table.{DataTable, RowData, RowWithData, TableContainer} -import org.finos.vuu.net.rpc.{EditRpcHandler, RpcHandler} +import org.finos.vuu.net.rpc.RpcHandler import org.finos.vuu.net.{ClientSessionId, RequestContext} -import org.finos.vuu.order.oms.{NewOrder, OmsApi} +import org.finos.vuu.order.oms.OmsApi import org.finos.vuu.viewport._ import java.util.concurrent.atomic.AtomicInteger @@ -18,7 +18,7 @@ object BasketService{ } trait BasketServiceIF{ - def createBasket(basketKey: String, name: String)(ctx: RequestContext): ViewPortAction + def createBasket(basketId: String, name: String)(ctx: RequestContext): ViewPortAction } class BasketService(val table: DataTable, val tableContainer: TableContainer, val omsApi: OmsApi)(implicit clock: Clock) extends RpcHandler with BasketServiceIF with StrictLogging { @@ -30,58 +30,61 @@ class BasketService(val table: DataTable, val tableContainer: TableContainer, va session.user + "-" + "".padTo(5 - counterValue.toString.length, "0").mkString + counterValue } - private def getConstituentsForBasketKey(key: String): List[RowData] = { + private def getConstituentsForSourceBasket(basketId: String): List[RowData] = { val table = tableContainer.getTable(BasketConstituentTable) val keys = table.primaryKeys.toList - keys.map( key => table.pullRow(key) ).filter(_.get(BC.BasketId).toString == key) + keys.map( key => table.pullRow(key) ).filter(_.get(BC.BasketId).toString == basketId) } - private def mkTradingConstituentRow(side: String, basketKey: String, instanceKey: String, - constituentKey: String, quantity: Long, weighting: Double, basketConsRow: RowData): RowWithData = { - RowWithData(constituentKey, Map(BTC.BasketId -> basketKey, - BTC.Ric -> basketConsRow.get(BC.Ric), - BTC.InstanceId -> instanceKey, - BTC.Quantity -> quantity, - BTC.InstanceIdRic -> constituentKey, - BTC.Description -> basketConsRow.get(BC.Description), - BTC.Side -> side, - BTC.Weighting -> weighting, - BTC.PriceStrategyId -> 2, - BTC.Algo -> -1, - BTC.OrderStatus -> OrderStates.PENDING, - BTC.FilledQty -> 0 - )) + private def mkTradingConstituentRow(side: String, sourceBasketId: String, basketTradeInstanceId: String, constituentKey: String, + quantity: Long, weighting: Double, basketConsRow: RowData): RowWithData = { + RowWithData( + constituentKey, + Map( + BTC.Ric -> basketConsRow.get(BC.Ric), + BTC.BasketId -> sourceBasketId, + BTC.InstanceId -> basketTradeInstanceId, + BTC.InstanceIdRic -> constituentKey, + BTC.Quantity -> quantity, + BTC.Description -> basketConsRow.get(BC.Description), + BTC.Side -> side, + BTC.Weighting -> weighting, + BTC.PriceStrategyId -> 2, + BTC.Algo -> -1, + BTC.OrderStatus -> OrderStates.PENDING, + BTC.FilledQty -> 0 + )) } - private def mkTradingBasketRow(instanceKey: String, basketKey: String): RowWithData = { - RowWithData(instanceKey, Map(BT.InstanceId -> instanceKey, BT.Status -> "OFF-MARKET", BT.BasketId -> basketKey, BT.BasketName -> instanceKey, BT.Side -> Sides.Buy, BT.Units -> 1)) + private def mkTradingBasketRow(sourceBasketId: String, basketTradeName: String, basketTradeInstanceId: String) = { + RowWithData(basketTradeInstanceId, Map(BT.InstanceId -> basketTradeInstanceId, BT.Status -> "OFF-MARKET", BT.BasketId -> sourceBasketId, BT.BasketName -> basketTradeName, BT.Side -> Sides.Buy, BT.Units -> 1)) } - def createBasketFromRpc(basketKey: String, name: String)(ctx: RequestContext): ViewPortAction = { - createBasket(basketKey, name)(ctx) + def createBasketFromRpc(basketId: String, name: String)(ctx: RequestContext): ViewPortAction = { + createBasket(basketId, name)(ctx) } def createBasket(selection: ViewPortSelection, session: ClientSessionId): ViewPortAction = { - val basketKey = selection.rowKeyIndex.map({ case (key, _) => key }).toList.head + val basketId = selection.rowKeyIndex.map({ case (key, _) => key }).toList.head val instanceKey = getAndPadCounter(session) - createBasketInternal(basketKey, instanceKey, session) + createBasketInternal(basketId, instanceKey, instanceKey, session) } - def createBasket(basketKey: String, name: String)(ctx: RequestContext): ViewPortAction = { - createBasketInternal(basketKey, name, ctx.session) + def createBasket(basketId: String, name: String)(ctx: RequestContext): ViewPortAction = { + val basketTradeId = getAndPadCounter(ctx.session) + createBasketInternal(basketId, name, basketTradeId, ctx.session) } + private def createBasketInternal(sourceBasketId: String, basketTradeName: String, basketTradeId: String, sessionId: ClientSessionId) = { - private def createBasketInternal(basketKey: String, name: String, sessionId: ClientSessionId): ViewPortAction = { - - val constituents = getConstituentsForBasketKey(basketKey) + val constituents = getConstituentsForSourceBasket(sourceBasketId) tableContainer.getTable(BasketModule.BasketTradingTable) match { case table: DataTable => - table.processUpdate(name, mkTradingBasketRow(name, basketKey), clock.now()) + table.processUpdate(basketTradeId, mkTradingBasketRow(sourceBasketId, basketTradeName, basketTradeId), clock.now()) case null => logger.error("Cannot find the Basket Trading table.") } @@ -89,11 +92,11 @@ class BasketService(val table: DataTable, val tableContainer: TableContainer, va tableContainer.getTable(BasketModule.BasketTradingConstituentTable) match { case table: DataTable => constituents.foreach( rowData => { - val constituentKey = name + "." + rowData.get(BTC.Ric) + val constituentKey = basketTradeId + "." + rowData.get(BTC.Ric) val weighting = rowData.get(BTC.Weighting).asInstanceOf[Double] val quantity = (weighting * 100).asInstanceOf[Long] val side = rowData.get(BTC.Side).toString - table.processUpdate(constituentKey, mkTradingConstituentRow(side, basketKey, name, constituentKey, quantity, weighting, rowData), clock.now()) + table.processUpdate(constituentKey, mkTradingConstituentRow(side, sourceBasketId, basketTradeId, constituentKey, quantity, weighting, rowData), clock.now()) }) case null => logger.error("Cannot find the Basket Trading Constituent.") From e0d905db47c714c00f84fb6690223b0df58948e3 Mon Sep 17 00:00:00 2001 From: Na Lee Ha Date: Fri, 24 Nov 2023 16:18:53 +0000 Subject: [PATCH 04/21] #980 adding basket trading constituent row - using menu for testing --- .../BasketTradingConstituentJoinService.scala | 75 ++++++++++++++++++- 1 file changed, 71 insertions(+), 4 deletions(-) diff --git a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala index 96e637d80..6b2526c4d 100644 --- a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala +++ b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala @@ -4,9 +4,10 @@ import com.typesafe.scalalogging.StrictLogging import org.finos.toolbox.time.Clock import org.finos.vuu.api.JoinTableDef import org.finos.vuu.core.module.basket.BasketConstants.Side -import org.finos.vuu.core.module.basket.BasketModule.{BasketTradingConstituentColumnNames => ColumnName} -import org.finos.vuu.core.table.{DataTable, JoinTable, RowWithData, TableContainer} -import org.finos.vuu.net.ClientSessionId +import org.finos.vuu.core.module.basket.BasketModule +import org.finos.vuu.core.module.basket.BasketModule.{BasketTradingTable, BasketTradingColumnNames => BTColumnName, BasketTradingConstituentColumnNames => ColumnName} +import org.finos.vuu.core.table.{DataTable, JoinTable, RowData, RowWithData, TableContainer} +import org.finos.vuu.net.{ClientSessionId, RequestContext} import org.finos.vuu.net.rpc.{EditRpcHandler, RpcHandler} import org.finos.vuu.viewport._ @@ -21,6 +22,7 @@ object BasketTradingConstituentJoinService {} class BasketTradingConstituentJoinService(val table: DataTable, val tableContainer: TableContainer)(implicit clock: Clock) extends BasketTradingConstituentJoinServiceIF with RpcHandler with StrictLogging { def onDeleteRow(key: String, vp: ViewPort, session: ClientSessionId): ViewPortEditAction = { + ViewPortEditSuccess() } @@ -32,6 +34,70 @@ class BasketTradingConstituentJoinService(val table: DataTable, val tableContain ViewPortEditSuccess() } + def testAddConstituent(selection: ViewPortSelection, session: ClientSessionId): ViewPortAction = { + addConstituents(Array("VOD.L", "BP.L")) + //todo handle failure + } + def addConstituents(rics: Array[String]): ViewPortEditAction = { + //def addConstituents(rics: Array[String])(ctx: RequestContext): ViewPortEditAction = { + //todo handle if no rows + if(table.size() == 0) + ViewPortEditFailure(s"Failed to add constituents to ${table.name} as adding row to empty table is currently not supported") + + val existingConstituentRow = table.pullRow(table.primaryKeys.head) + val tradeId = existingConstituentRow.get(ColumnName.InstanceId).asInstanceOf[String] + + val tradeRow = tableContainer.getTable(BasketModule.BasketTradingTable).pullRow(tradeId) + val basketId = tradeRow.get(BTColumnName.BasketId).asInstanceOf[String] + val tradeUnit = tradeRow.get(BTColumnName.Units).asInstanceOf[Int] + + rics.foreach(ric => { + + //todo should we guard against adding row for ric that already exist? + val newRow = mkTradingConstituentRow( + basketTradeInstanceId = tradeId, + sourceBasketId = basketId, + tradeUnit = tradeUnit, + ric = ric) + + updateJoinTable(table, newRow) + + logger.info(s"added constituent $ric") + }) + + ViewPortEditSuccess() + + } + + private def mkTradingConstituentRow(basketTradeInstanceId: String, sourceBasketId:String, tradeUnit:Int, ric: String): RowWithData = { + val constituentKey = s"$basketTradeInstanceId.$ric" + val weighting: Double = 0.1 + RowWithData( + constituentKey, + Map( + ColumnName.Ric -> ric, + ColumnName.BasketId -> sourceBasketId, + ColumnName.InstanceId -> basketTradeInstanceId, + ColumnName.InstanceIdRic -> constituentKey, + ColumnName.Quantity -> (weighting * 100).asInstanceOf[Long], + ColumnName.Description -> "", //todo look up description from instrument table + ColumnName.Side -> Side.Buy, + ColumnName.Weighting -> weighting, + ColumnName.PriceStrategyId -> 2, + ColumnName.Algo -> -1, + )) + } + + def updateJoinTable(table: DataTable, row: RowWithData): Unit = { + val joinTable = table.asTable.asInstanceOf[JoinTable] + val baseTableDef = joinTable.getTableDef.asInstanceOf[JoinTableDef].baseTable + joinTable.sourceTables.get(baseTableDef.name) match { + case Some(table: DataTable) => + table.processUpdate(row.key, row, clock.now()) + case None => + //log and throw? + } + } private def onEditCell(key: String, columnName: String, data: Any, vp: ViewPort, session: ClientSessionId): ViewPortEditAction = { val joinTable = vp.table.asTable.asInstanceOf[JoinTable] val baseTableDef = joinTable.getTableDef.asInstanceOf[JoinTableDef].baseTable @@ -118,6 +184,7 @@ class BasketTradingConstituentJoinService(val table: DataTable, val tableContain } override def menuItems(): ViewPortMenu = ViewPortMenu("Direction", new SelectionViewPortMenuItem("Set Sell", "", this.setSell, "SET_SELECTION_SELL"), - new SelectionViewPortMenuItem("Set Buy", "", this.setBuy, "SET_SELECTION_Buy") + new SelectionViewPortMenuItem("Set Buy", "", this.setBuy, "SET_SELECTION_Buy"), + new SelectionViewPortMenuItem("Add Row", "", this.testAddConstituent, "TEST_ADD_ROW"), ) } From 9d23e743a94f53d6402e2cc02cf6c67e6eddf586 Mon Sep 17 00:00:00 2001 From: Na Lee Ha Date: Fri, 24 Nov 2023 17:23:32 +0000 Subject: [PATCH 05/21] #980 removed the add row menu that was for testing, clean up and error handling around adding row --- .../BasketTradingConstituentJoinService.scala | 179 ++++++++---------- 1 file changed, 78 insertions(+), 101 deletions(-) diff --git a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala index 6b2526c4d..7c6f955f7 100644 --- a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala +++ b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala @@ -5,8 +5,8 @@ import org.finos.toolbox.time.Clock import org.finos.vuu.api.JoinTableDef import org.finos.vuu.core.module.basket.BasketConstants.Side import org.finos.vuu.core.module.basket.BasketModule -import org.finos.vuu.core.module.basket.BasketModule.{BasketTradingTable, BasketTradingColumnNames => BTColumnName, BasketTradingConstituentColumnNames => ColumnName} -import org.finos.vuu.core.table.{DataTable, JoinTable, RowData, RowWithData, TableContainer} +import org.finos.vuu.core.module.basket.BasketModule.{BasketTradingColumnNames => BTColumnName, BasketTradingConstituentColumnNames => ColumnName} +import org.finos.vuu.core.table.{DataTable, JoinTable, RowWithData, TableContainer} import org.finos.vuu.net.{ClientSessionId, RequestContext} import org.finos.vuu.net.rpc.{EditRpcHandler, RpcHandler} import org.finos.vuu.viewport._ @@ -21,26 +21,32 @@ object BasketTradingConstituentJoinService {} class BasketTradingConstituentJoinService(val table: DataTable, val tableContainer: TableContainer)(implicit clock: Clock) extends BasketTradingConstituentJoinServiceIF with RpcHandler with StrictLogging { - def onDeleteRow(key: String, vp: ViewPort, session: ClientSessionId): ViewPortEditAction = { + override def menuItems(): ViewPortMenu = ViewPortMenu("Direction", + new SelectionViewPortMenuItem("Set Sell", "", this.setSell, "SET_SELECTION_SELL"), + new SelectionViewPortMenuItem("Set Buy", "", this.setBuy, "SET_SELECTION_Buy"), + ) + override def deleteRowAction(): ViewPortDeleteRowAction = ViewPortDeleteRowAction("", this.onDeleteRow) - ViewPortEditSuccess() - } + override def deleteCellAction(): ViewPortDeleteCellAction = ViewPortDeleteCellAction("", this.onDeleteCell) - def onDeleteCell(key: String, column: String, vp: ViewPort, session: ClientSessionId): ViewPortEditAction = { - ViewPortEditSuccess() - } + override def addRowAction(): ViewPortAddRowAction = ViewPortAddRowAction("", this.onAddRow) - def onAddRow(key: String, data: Map[String, Any], vp: ViewPort, session: ClientSessionId): ViewPortEditAction = { - ViewPortEditSuccess() + override def editCellAction(): ViewPortEditCellAction = ViewPortEditCellAction("", this.onEditCell) + + override def editRowAction(): ViewPortEditRowAction = ViewPortEditRowAction("", this.onEditRow) + + override def onFormSubmit(): ViewPortFormSubmitAction = ViewPortFormSubmitAction("", this.onFormSubmit) + + override def onFormClose(): ViewPortFormCloseAction = ViewPortFormCloseAction("", this.onFormClose) + + def setSell(selection: ViewPortSelection, session: ClientSessionId): ViewPortAction = { + updateSelected(selection, Map(ColumnName.Side -> Side.Sell)) } - def testAddConstituent(selection: ViewPortSelection, session: ClientSessionId): ViewPortAction = { - addConstituents(Array("VOD.L", "BP.L")) - //todo handle failure + def setBuy(selection: ViewPortSelection, session: ClientSessionId): ViewPortAction = { + updateSelected(selection, Map(ColumnName.Side -> Side.Buy)) } - def addConstituents(rics: Array[String]): ViewPortEditAction = { - //def addConstituents(rics: Array[String])(ctx: RequestContext): ViewPortEditAction = { - //todo handle if no rows + def addConstituents(rics: Array[String])(ctx: RequestContext): ViewPortEditAction = { if(table.size() == 0) ViewPortEditFailure(s"Failed to add constituents to ${table.name} as adding row to empty table is currently not supported") @@ -51,59 +57,21 @@ class BasketTradingConstituentJoinService(val table: DataTable, val tableContain val basketId = tradeRow.get(BTColumnName.BasketId).asInstanceOf[String] val tradeUnit = tradeRow.get(BTColumnName.Units).asInstanceOf[Int] - rics.foreach(ric => { - - //todo should we guard against adding row for ric that already exist? - val newRow = mkTradingConstituentRow( + val newRows = rics.map(ric => + mkTradingConstituentRow( basketTradeInstanceId = tradeId, sourceBasketId = basketId, tradeUnit = tradeUnit, - ric = ric) - - updateJoinTable(table, newRow) - - logger.info(s"added constituent $ric") - }) - - ViewPortEditSuccess() + ric = ric)) + //todo should we guard against adding row for ric that already exist? + updateJoinTable(newRows) } - private def mkTradingConstituentRow(basketTradeInstanceId: String, sourceBasketId:String, tradeUnit:Int, ric: String): RowWithData = { - val constituentKey = s"$basketTradeInstanceId.$ric" - val weighting: Double = 0.1 - RowWithData( - constituentKey, - Map( - ColumnName.Ric -> ric, - ColumnName.BasketId -> sourceBasketId, - ColumnName.InstanceId -> basketTradeInstanceId, - ColumnName.InstanceIdRic -> constituentKey, - ColumnName.Quantity -> (weighting * 100).asInstanceOf[Long], - ColumnName.Description -> "", //todo look up description from instrument table - ColumnName.Side -> Side.Buy, - ColumnName.Weighting -> weighting, - ColumnName.PriceStrategyId -> 2, - ColumnName.Algo -> -1, - )) - } - - def updateJoinTable(table: DataTable, row: RowWithData): Unit = { - val joinTable = table.asTable.asInstanceOf[JoinTable] - val baseTableDef = joinTable.getTableDef.asInstanceOf[JoinTableDef].baseTable - joinTable.sourceTables.get(baseTableDef.name) match { - case Some(table: DataTable) => - table.processUpdate(row.key, row, clock.now()) - case None => - //log and throw? - } - } private def onEditCell(key: String, columnName: String, data: Any, vp: ViewPort, session: ClientSessionId): ViewPortEditAction = { - val joinTable = vp.table.asTable.asInstanceOf[JoinTable] - val baseTableDef = joinTable.getTableDef.asInstanceOf[JoinTableDef].baseTable - joinTable.sourceTables.get(baseTableDef.name) match { - case Some(table: DataTable) => - table.processUpdate(key, RowWithData(key, Map(ColumnName.InstanceIdRic -> key, columnName -> data)), clock.now()) + getBaseTable() match { + case Some(baseTable: DataTable) => + baseTable.processUpdate(key, RowWithData(key, Map(ColumnName.InstanceIdRic -> key, columnName -> data)), clock.now()) ViewPortEditSuccess() case None => ViewPortEditFailure("Could not find base table") @@ -116,6 +84,19 @@ class BasketTradingConstituentJoinService(val table: DataTable, val tableContain ViewPortEditSuccess() } + private def onDeleteRow(key: String, vp: ViewPort, session: ClientSessionId): ViewPortEditAction = { + + ViewPortEditSuccess() + } + + private def onDeleteCell(key: String, column: String, vp: ViewPort, session: ClientSessionId): ViewPortEditAction = { + ViewPortEditSuccess() + } + + private def onAddRow(key: String, data: Map[String, Any], vp: ViewPort, session: ClientSessionId): ViewPortEditAction = { + ViewPortEditSuccess() + } + private def onFormSubmit(vp: ViewPort, session: ClientSessionId): ViewPortAction = { // val table = vp.table.asTable // val primaryKeys = table.primaryKeys @@ -136,55 +117,51 @@ class BasketTradingConstituentJoinService(val table: DataTable, val tableContain CloseDialogViewPortAction(vp.id) } - override def deleteRowAction(): ViewPortDeleteRowAction = ViewPortDeleteRowAction("", this.onDeleteRow) - - override def deleteCellAction(): ViewPortDeleteCellAction = ViewPortDeleteCellAction("", this.onDeleteCell) - - override def addRowAction(): ViewPortAddRowAction = ViewPortAddRowAction("", this.onAddRow) - - override def editCellAction(): ViewPortEditCellAction = ViewPortEditCellAction("", this.onEditCell) - - override def editRowAction(): ViewPortEditRowAction = ViewPortEditRowAction("", this.onEditRow) - - override def onFormSubmit(): ViewPortFormSubmitAction = ViewPortFormSubmitAction("", this.onFormSubmit) - - override def onFormClose(): ViewPortFormCloseAction = ViewPortFormCloseAction("", this.onFormClose) - - def setSell(selection: ViewPortSelection, session: ClientSessionId): ViewPortAction = { - updateSelected(selection, Map(ColumnName.Side -> Side.Sell)) + private def getBaseTable(): Option[DataTable] = { + val joinTable = table.asTable.asInstanceOf[JoinTable] + val baseTableDef = joinTable.getTableDef.asInstanceOf[JoinTableDef].baseTable + joinTable.sourceTables.get(baseTableDef.name) } - def setBuy(selection: ViewPortSelection, session: ClientSessionId): ViewPortAction = { - updateSelected(selection, Map(ColumnName.Side -> Side.Buy)) + private def updateJoinTable(rows: Array[RowWithData]): ViewPortEditAction = { + getBaseTable() match { + case Some(baseTable: DataTable) => + rows.foreach(row => + baseTable.processUpdate(row.key, row, clock.now()) + ) + ViewPortEditSuccess() + case None => + ViewPortEditFailure(s"Could not find base table for ${table.name}") + } } private def updateSelected(selection: ViewPortSelection, updates: Map[String, Any]): ViewPortAction = { val selectedKeys = selection.rowKeyIndex.map({ case (key, _) => key }).toList - selectedKeys.foreach(key => { + val updateRows = selectedKeys.map(key => { //require source table primary key for join table updates val sourceTableKey = Map(ColumnName.InstanceIdRic -> key) - update(key, sourceTableKey ++ updates) + RowWithData(key, sourceTableKey ++ updates) }) - //todo handle error - //todo care about checking current value? simpler not to - ViewPortEditSuccess() + updateJoinTable(updateRows.toArray) } - //todo can update multiple rows in one go? - private def update(key: String, keyValuesToUpdate: Map[String, Any]): Unit = { - val joinTable = table.asTable.asInstanceOf[JoinTable] - val baseTableDef = joinTable.getTableDef.asInstanceOf[JoinTableDef].baseTable - joinTable.sourceTables.get(baseTableDef.name) match { - case Some(table: DataTable) => - table.processUpdate(key, RowWithData(key, keyValuesToUpdate), clock.now()) - ViewPortEditSuccess() - case None => - ViewPortEditFailure("Could not find base table") - } + private def mkTradingConstituentRow(basketTradeInstanceId: String, sourceBasketId: String, tradeUnit: Int, ric: String): RowWithData = { + val constituentKey = s"$basketTradeInstanceId.$ric" + val weighting: Double = 0.1 + RowWithData( + constituentKey, + Map( + ColumnName.Ric -> ric, + ColumnName.BasketId -> sourceBasketId, + ColumnName.InstanceId -> basketTradeInstanceId, + ColumnName.InstanceIdRic -> constituentKey, + ColumnName.Quantity -> (weighting * 100).asInstanceOf[Long], + ColumnName.Description -> "", //todo look up description from instrument table + ColumnName.Side -> Side.Buy, + ColumnName.Weighting -> weighting, + ColumnName.PriceStrategyId -> 2, + ColumnName.Algo -> -1, + )) } - override def menuItems(): ViewPortMenu = ViewPortMenu("Direction", - new SelectionViewPortMenuItem("Set Sell", "", this.setSell, "SET_SELECTION_SELL"), - new SelectionViewPortMenuItem("Set Buy", "", this.setBuy, "SET_SELECTION_Buy"), - new SelectionViewPortMenuItem("Add Row", "", this.testAddConstituent, "TEST_ADD_ROW"), - ) + } From 79fd6a2c9b64daeb59621ea4ac79a298b8f663dd Mon Sep 17 00:00:00 2001 From: Na Lee Ha Date: Fri, 24 Nov 2023 19:32:30 +0000 Subject: [PATCH 06/21] #980 returning no action for menu calls and rcp success or failure for rcp call --- .../module/basket/result/ErrorReason.scala | 3 ++ .../BasketTradingConstituentJoinService.scala | 29 ++++++++++++++----- 2 files changed, 24 insertions(+), 8 deletions(-) create mode 100644 example/basket/src/main/scala/org/finos/vuu/core/module/basket/result/ErrorReason.scala diff --git a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/result/ErrorReason.scala b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/result/ErrorReason.scala new file mode 100644 index 000000000..e4a453405 --- /dev/null +++ b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/result/ErrorReason.scala @@ -0,0 +1,3 @@ +package org.finos.vuu.core.module.basket.result + +case class ErrorReason(reason: String) diff --git a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala index 7c6f955f7..1c76a099a 100644 --- a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala +++ b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala @@ -6,11 +6,13 @@ import org.finos.vuu.api.JoinTableDef import org.finos.vuu.core.module.basket.BasketConstants.Side import org.finos.vuu.core.module.basket.BasketModule import org.finos.vuu.core.module.basket.BasketModule.{BasketTradingColumnNames => BTColumnName, BasketTradingConstituentColumnNames => ColumnName} +import org.finos.vuu.core.module.basket.result.ErrorReason import org.finos.vuu.core.table.{DataTable, JoinTable, RowWithData, TableContainer} -import org.finos.vuu.net.{ClientSessionId, RequestContext} import org.finos.vuu.net.rpc.{EditRpcHandler, RpcHandler} +import org.finos.vuu.net.{ClientSessionId, RequestContext} import org.finos.vuu.viewport._ + trait BasketTradingConstituentJoinServiceIF extends EditRpcHandler { def setSell(selection: ViewPortSelection, session: ClientSessionId): ViewPortAction @@ -46,7 +48,9 @@ class BasketTradingConstituentJoinService(val table: DataTable, val tableContain def setBuy(selection: ViewPortSelection, session: ClientSessionId): ViewPortAction = { updateSelected(selection, Map(ColumnName.Side -> Side.Buy)) } - def addConstituents(rics: Array[String])(ctx: RequestContext): ViewPortEditAction = { + + //this is RCP call and method name is part of contract with UI + def addConstituents(rics: Array[String])(ctx: RequestContext): ViewPortAction = { if(table.size() == 0) ViewPortEditFailure(s"Failed to add constituents to ${table.name} as adding row to empty table is currently not supported") @@ -65,7 +69,11 @@ class BasketTradingConstituentJoinService(val table: DataTable, val tableContain ric = ric)) //todo should we guard against adding row for ric that already exist? - updateJoinTable(newRows) + updateJoinTable(newRows) match { + case Right(_) => ViewPortRpcSuccess() + case Left(errorReason) => + ViewPortRpcFailure(errorReason.reason) + } } private def onEditCell(key: String, columnName: String, data: Any, vp: ViewPort, session: ClientSessionId): ViewPortEditAction = { @@ -85,7 +93,6 @@ class BasketTradingConstituentJoinService(val table: DataTable, val tableContain } private def onDeleteRow(key: String, vp: ViewPort, session: ClientSessionId): ViewPortEditAction = { - ViewPortEditSuccess() } @@ -123,15 +130,15 @@ class BasketTradingConstituentJoinService(val table: DataTable, val tableContain joinTable.sourceTables.get(baseTableDef.name) } - private def updateJoinTable(rows: Array[RowWithData]): ViewPortEditAction = { + private def updateJoinTable(rows: Array[RowWithData]): Either[ErrorReason, Unit] = { getBaseTable() match { case Some(baseTable: DataTable) => rows.foreach(row => baseTable.processUpdate(row.key, row, clock.now()) ) - ViewPortEditSuccess() + Right() case None => - ViewPortEditFailure(s"Could not find base table for ${table.name}") + Left(ErrorReason(s"Could not find base table for ${table.name}")) } } @@ -142,7 +149,13 @@ class BasketTradingConstituentJoinService(val table: DataTable, val tableContain val sourceTableKey = Map(ColumnName.InstanceIdRic -> key) RowWithData(key, sourceTableKey ++ updates) }) - updateJoinTable(updateRows.toArray) + + updateJoinTable(updateRows.toArray) match { + case Right(_) => NoAction() + case Left(errorReason) => + logger.info(s"Could not update selection values${errorReason.reason}") + NoAction() + } } private def mkTradingConstituentRow(basketTradeInstanceId: String, sourceBasketId: String, tradeUnit: Int, ric: String): RowWithData = { From a6d317315781805df6b618117eb8ccef13f95d37 Mon Sep 17 00:00:00 2001 From: Na Lee Ha Date: Mon, 27 Nov 2023 09:44:28 +0000 Subject: [PATCH 07/21] #1010 registring ViewPortRpcSuccess --- vuu/src/main/scala/org/finos/vuu/viewport/ViewPortAction.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortAction.scala b/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortAction.scala index 4d557c7b2..6f33691fc 100644 --- a/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortAction.scala +++ b/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortAction.scala @@ -26,5 +26,7 @@ case class CloseDialogViewPortAction(vpId: String) extends ViewPortAction new Type(value = classOf[ViewPortEditSuccess], name = "VP_EDIT_SUCCESS"), new Type(value = classOf[ViewPortEditFailure], name = "VP_EDIT_FAIL"), new Type(value = classOf[ViewPortRpcFailure], name = "VP_RPC_FAILURE") + new Type(value = classOf[ViewPortRpcSuccess], name = "VP_RCP_SUCCESS"), + new Type(value = classOf[ViewPortRpcFailure], name = "VP_RCP_FAIL"), )) trait ViewPortActionMixin {} From 24418628d4f449fbc122e5a9c51dd561d646b621 Mon Sep 17 00:00:00 2001 From: Na Lee Ha Date: Mon, 27 Nov 2023 10:37:04 +0000 Subject: [PATCH 08/21] #979 updated test to use basket trade instance id seperate from basket name. Disabling tests that are in progress --- .../basket/BasketConstituentMutateTest.scala | 113 +++++++ .../basket/BasketMutateOffMarketTest.scala | 287 +++++------------- .../finos/vuu/viewport/ViewPortAction.scala | 1 - .../vuu/test/impl/TestVuuServerImpl.scala | 3 +- 4 files changed, 185 insertions(+), 219 deletions(-) create mode 100644 example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketConstituentMutateTest.scala diff --git a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketConstituentMutateTest.scala b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketConstituentMutateTest.scala new file mode 100644 index 000000000..6fe045421 --- /dev/null +++ b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketConstituentMutateTest.scala @@ -0,0 +1,113 @@ +package org.finos.vuu.core.module.basket + +import org.finos.toolbox.jmx.{MetricsProvider, MetricsProviderImpl} +import org.finos.toolbox.lifecycle.LifecycleContainer +import org.finos.toolbox.time.{Clock, TestFriendlyClock} +import org.finos.vuu.api.JoinTableDef +import org.finos.vuu.core.module.TableDefContainer +import org.finos.vuu.core.module.basket.TestHelper.TestDataFactory +import org.finos.vuu.core.module.basket.service.BasketTradingConstituentJoinServiceIF +import org.finos.vuu.core.module.price.PriceModule +import org.finos.vuu.core.table.{DataTable, JoinTable, RowWithData, TableContainer} +import org.finos.vuu.order.oms.OmsApi +import org.finos.vuu.test.VuuServerTestCase +import org.finos.vuu.util.table.TableAsserts.assertVpEq +import org.finos.vuu.viewport.ViewPortSelection +import org.scalatest.Ignore +import org.scalatest.prop.Tables.Table + +@Ignore //working progress +class BasketConstituentMutateTest extends VuuServerTestCase { + + import BasketModule._ + implicit val clock: Clock = new TestFriendlyClock(10001L) + implicit val lifecycle: LifecycleContainer = new LifecycleContainer() + implicit val tableDefContainer: TableDefContainer = new TableDefContainer(Map()) + implicit val metricsProvider: MetricsProvider = new MetricsProviderImpl + val omsApi = OmsApi() + Feature("Basket Trading Constituent Join Service Test Case") { + Scenario("Update selected trade constituent sides when menu action is triggered") { + withVuuServer(PriceModule(), BasketModule(omsApi)) { + vuuServer => + + vuuServer.login("testUser", "testToken") + + val basketName = "test_basket1" + val tradeId = GivenBasketTrade(vuuServer.tableContainer, basketName, "Buy") + GivenPrices(vuuServer.tableContainer, List(("VOD.L", 1.1, 1.4), ("BP.L", 2.1, 2.4))) + GivenBasketTradeConstituentsJoin(vuuServer.tableContainer, tradeId, Map(("VOD.L" -> "Buy"), ("BP.L" -> "Sell"))) + + val vpBasketTrading = vuuServer.createViewPort(BasketModule.NAME, BasketTradingTable) + val vpBasketTradingConsJoin = vuuServer.createViewPort(BasketModule.NAME, BasketTradingConstituentJoin) + vuuServer.runOnce() + + When("we select multiple constituent rows ") + vpBasketTradingConsJoin.setSelection(Array(1, 2)) + + And("select set sell context menu") + val basketTradingConstituentJoinService = vuuServer.getViewPortRpcServiceProxy[BasketTradingConstituentJoinServiceIF](vpBasketTradingConsJoin) + val selection = vpBasketTradingConsJoin.getSelection + val vpSelection = ViewPortSelection(selection, vpBasketTradingConsJoin) + basketTradingConstituentJoinService.setSell(vpSelection, vuuServer.session) + vuuServer.runOnce() + + Then("get all the updates that have occurred for all view ports from the outbound queue") + val updates = combineQs(vpBasketTrading) + + And("assert only selected constituent has changed set side to sell") + + + assertVpEq(filterByVp(vpBasketTradingConsJoin, updates)) { + Table( + ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId", "bid", "ask", "filledQty", "orderStatus"), + (10L, "Buy", "testUser-00001", "testUser-00001.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 2.1, 2.4, 0, "PENDING"), + (10L, "Sell", "testUser-00001", "testUser-00001.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, null, null, 0, "PENDING"), + (10L, "Sell", "testUser-00001", "testUser-00001.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 1.1, 1.4, 0, "PENDING") + ) + } + } + } + } + + def uuid = java.util.UUID.randomUUID.toString + + def GivenBasketTrade(tableContainer: TableContainer, basketName: String, side: String): String = { + val table = tableContainer.getTable(BasketModule.BasketTradingTable) + val rowKey = s"$uuid.$basketName" + table.processUpdate(rowKey, TestDataFactory.createBasketTradingRow(rowKey, basketName, side), clock.now()) + rowKey + } + + def GivenBasketTradeConstituentsJoin(tableContainer: TableContainer, tradeId: String, ricToSide: Map[String, String]): Unit = { + val table = tableContainer.getTable(BasketModule.BasketTradingConstituentJoin) + ricToSide.foreach(item => { + val (ric, side) = item + val row = TestDataFactory.createBasketTradingConstituentJoinRow(tradeId, ric, side) + updateJoinTable(table, row) + }) + } + + def updateJoinTable(table: DataTable, row: RowWithData): Unit = { + val joinTable = table.asTable.asInstanceOf[JoinTable] + val baseTableDef = joinTable.getTableDef.asInstanceOf[JoinTableDef].baseTable + joinTable.sourceTables.get(baseTableDef.name) match { + case Some(table: DataTable) => + table.processUpdate(row.key, row, clock.now()) + case None => + //log and throw? + } + } + + def GivenPrices(tableContainer: TableContainer, prices: List[(String, Double, Double)]) = { + val table = tableContainer.getTable("prices") + for ((ric, bid, ask) <- prices) { + val rowKey = s"$uuid" + table.processUpdate(rowKey, TestDataFactory.createPricesRow(rowKey, ric, bid, ask), clock.now()) + } + } + + //todo having issue importing side constant + //todo introduce case class BasketTrading? + + +} diff --git a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala index 4d900fa24..c46c500af 100644 --- a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala +++ b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala @@ -3,87 +3,27 @@ package org.finos.vuu.core.module.basket import org.finos.toolbox.jmx.{MetricsProvider, MetricsProviderImpl} import org.finos.toolbox.lifecycle.LifecycleContainer import org.finos.toolbox.time.{Clock, TestFriendlyClock} -import org.finos.vuu.api.{JoinTableDef, ViewPortDef} +import org.finos.vuu.api.ViewPortDef import org.finos.vuu.core.module.TableDefContainer import org.finos.vuu.core.module.basket.BasketModule.{BasketColumnNames => B, BasketConstituentColumnNames => BC} -import org.finos.vuu.core.module.basket.TestHelper.TestDataFactory -import org.finos.vuu.core.module.basket.service.{BasketServiceIF, BasketTradingConstituentJoinServiceIF, BasketTradingServiceIF} +import org.finos.vuu.core.module.basket.service.{BasketServiceIF, BasketTradingServiceIF} import org.finos.vuu.core.module.price.PriceModule import org.finos.vuu.order.oms.OmsApi -import org.finos.vuu.core.table.{DataTable, JoinTable, RowWithData, TableContainer} import org.finos.vuu.test.{TestVuuServer, VuuServerTestCase} import org.finos.vuu.util.table.TableAsserts.assertVpEq -import org.finos.vuu.viewport.ViewPortSelection import org.scalatest.prop.Tables.Table class BasketMutateOffMarketTest extends VuuServerTestCase { - import BasketTestCaseHelper._ - import BasketModule._ - - implicit val clock: Clock = new TestFriendlyClock(10001L) - implicit val lifecycle: LifecycleContainer = new LifecycleContainer() - implicit val tableDefContainer: TableDefContainer = new TableDefContainer(Map()) - implicit val metricsProvider: MetricsProvider = new MetricsProviderImpl - Feature("Basket Service Test Case") { - val omsApi = OmsApi() - - Scenario("Check updating trade basket side with no change does not update constituents side") { - withVuuServer(PriceModule(), BasketModule(omsApi)) { - vuuServer => - - vuuServer.login("testUser", "testToken") - - GivenBasketTradeExist(vuuServer, ".FTSE", "chris-001") - - val vpBasketTrading = vuuServer.createViewPort(BasketModule.NAME, BasketTradingTable) - val vpBasketTradingCons = vuuServer.createViewPort(BasketModule.NAME, BasketTradingConstituentTable) - val basketTradingService = vuuServer.getViewPortRpcServiceProxy[BasketTradingServiceIF](vpBasketTrading) - - When("we edit the side of the parent basket to same side as current value") - basketTradingService.editCellAction().func("chris-001", "side", "Buy", vpBasketTrading, vuuServer.session) - vuuServer.runOnce() - - Then("get all the updates that have occurred for all view ports from the outbound queue") - val updates = combineQs(vpBasketTrading) - - And("assert the basket trading table has not changed side....") - assertVpEq(filterByVp(vpBasketTrading, updates)) { - Table( - ("instanceId", "basketId", "basketName", "status", "units", "filledPct", "fxRateToUsd", "totalNotional", "totalNotionalUsd", "side"), - ("chris-001", ".FTSE", "chris-001", "OFF-MARKET", 1, null, null, null, null, "Buy") - ) - } - - And("assert the basket trading constituent table has not changed sides") - assertVpEq(filterByVp(vpBasketTradingCons, updates)) { - Table( - ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId", "filledQty", "orderStatus"), - (10L, "Buy", "chris-001", "chris-001.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), - (10L, "Sell", "chris-001", "chris-001.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), - (10L, "Buy", "chris-001", "chris-001.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") - ) - } - } - } - - def GivenBasketTradeExist(vuuServer: TestVuuServer, basketId: String, basketTradeName: String): Unit = { - val basketProvider = vuuServer.getProvider(BasketModule.NAME, BasketTable) - basketProvider.tick(".FTSE", Map(B.Id -> ".FTSE", B.Name -> ".FTSE 100", B.NotionalValue -> 1000001, B.NotionalValueUsd -> 1500001)) - - val constituentProvider = vuuServer.getProvider(BasketModule.NAME, BasketConstituentTable) - constituentProvider.tick("VOD.L.FTSE", Map(BC.RicBasketId -> "VOD.L.FTSE", BC.Ric -> "VOD.L", BC.BasketId -> basketId, BC.Weighting -> 0.1, BC.Side -> "Buy", BC.Description -> "Vodafone")) - constituentProvider.tick("BT.L.FTSE", Map(BC.RicBasketId -> "BT.L.FTSE", BC.Ric -> "BT.L", BC.BasketId -> basketId, BC.Weighting -> 0.1, BC.Side -> "Sell", BC.Description -> "British Telecom")) - constituentProvider.tick("BP.L.FTSE", Map(BC.RicBasketId -> "BP.L.FTSE", BC.Ric -> "BP.L", BC.BasketId -> basketId, BC.Weighting -> 0.1, BC.Side -> "Buy", BC.Description -> "Beyond Petroleum")) - - val vpBasket = vuuServer.createViewPort(BasketModule.NAME, BasketTable) - val basketService = vuuServer.getViewPortRpcServiceProxy[BasketServiceIF](vpBasket) - basketService.createBasket(basketId, basketTradeName)(vuuServer.requestContext) - } - Scenario("Check the creation of the baskets and constituents") { - + import BasketModule._ + import BasketTestCaseHelper._ + implicit val clock: Clock = new TestFriendlyClock(10001L) + implicit val lifecycle: LifecycleContainer = new LifecycleContainer() + implicit val tableDefContainer: TableDefContainer = new TableDefContainer(Map()) + implicit val metricsProvider: MetricsProvider = new MetricsProviderImpl + val omsApi = OmsApi() withVuuServer(PriceModule(), BasketModule(omsApi)) { vuuServer => @@ -128,7 +68,7 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { Then("Get the Basket RPC Service and call create basket") val basketService = vuuServer.getViewPortRpcServiceProxy[BasketServiceIF](vpBasket) - basketService.createBasket(".FTSE", "chris-001")(vuuServer.requestContext) + basketService.createBasket(".FTSE", "MyCustomBasket")(vuuServer.requestContext) val vpBasketTrading = vuuServer.createViewPort(BasketModule.NAME, BasketTradingTable) @@ -139,7 +79,7 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { assertVpEq(combineQsForVp(vpBasketTrading)) { Table( ("instanceId", "basketId", "basketName", "status", "units", "filledPct", "fxRateToUsd", "totalNotional", "totalNotionalUsd", "side"), - ("chris-001", ".FTSE", "chris-001", "OFF-MARKET", 1, null, null, null, null, "Buy") + ("testUser-00001", ".FTSE", "MyCustomBasket", "OFF-MARKET", 1, null, null, null, null, "Buy") ) } @@ -150,16 +90,16 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { assertVpEq(combineQsForVp(vpBasketTradingCons)) { Table( ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId", "filledQty", "orderStatus"), - (10L, "Buy", "chris-001", "chris-001.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), - (10L, "Sell", "chris-001", "chris-001.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), - (10L, "Buy", "chris-001", "chris-001.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") + (10L, "Buy", "testUser-00001", "testUser-00001.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (10L, "Sell", "testUser-00001", "testUser-00001.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (10L, "Buy", "testUser-00001", "testUser-00001.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") ) } val basketTradingService = vuuServer.getViewPortRpcServiceProxy[BasketTradingServiceIF](vpBasketTrading) When("we edit the side of the parent basket") - basketTradingService.editCellAction().func("chris-001", "side", "Sell", vpBasketTrading, vuuServer.session) + basketTradingService.editCellAction().func("testUser-00001", "side", "Sell", vpBasketTrading, vuuServer.session) Then("get all the updates that have occurred for all view ports from the outbound queue") val updates = combineQs(vpBasketTrading) @@ -168,7 +108,7 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { assertVpEq(filterByVp(vpBasketTrading, updates)) { Table( ("instanceId", "basketId", "basketName", "status", "units", "filledPct", "fxRateToUsd", "totalNotional", "totalNotionalUsd", "side"), - ("chris-001", ".FTSE", "chris-001", "OFF-MARKET", 1, null, null, null, null, "Sell") + ("testUser-00001", ".FTSE", "MyCustomBasket", "OFF-MARKET", 1, null, null, null, null, "Sell") ) } @@ -177,169 +117,84 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { assertVpEq(filterByVp(vpBasketTradingCons, updates)) { Table( ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId", "filledQty", "orderStatus"), - (10L, "Sell", "chris-001", "chris-001.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), - (10L, "Buy", "chris-001", "chris-001.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), - (10L, "Sell", "chris-001", "chris-001.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") + (10L, "Sell", "testUser-00001", "testUser-00001.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (10L, "Buy", "testUser-00001", "testUser-00001.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (10L, "Sell", "testUser-00001", "testUser-00001.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") ) } When("we edit the units of the parent basket") - basketTradingService.editCellAction().func("chris-001", "units", 1000L.asInstanceOf[Object], vpBasketTrading, vuuServer.session) + basketTradingService.editCellAction().func("testUser-00001", "units", 1000L.asInstanceOf[Object], vpBasketTrading, vuuServer.session) And("assert the basket trading constituent table has increased the units") assertVpEq(filterByVp(vpBasketTradingCons, combineQs(vpBasketTrading))) { Table( ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId", "filledQty", "orderStatus"), - (100L, "Sell", "chris-001", "chris-001.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), - (100L, "Buy", "chris-001", "chris-001.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), - (100L, "Sell", "chris-001", "chris-001.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") + (100L, "Sell", "testUser-00001", "testUser-00001.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (100L, "Buy", "testUser-00001", "testUser-00001.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (100L, "Sell", "testUser-00001", "testUser-00001.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") ) } } } - Scenario("Check updating trade basket side with no change does not update constituents side") { - withVuuServer(PriceModule(), BasketModule()) { - vuuServer => - - vuuServer.login("testUser", "testToken") - - GivenBasketTradeExist(vuuServer, ".FTSE", "chris-001") - - val vpBasketTrading = vuuServer.createViewPort(BasketModule.NAME, BasketTradingTable) - val vpBasketTradingCons = vuuServer.createViewPort(BasketModule.NAME, BasketTradingConstituentTable) - val basketTradingService = vuuServer.getViewPortRpcServiceProxy[BasketTradingServiceIF](vpBasketTrading) - - When("we edit the side of the parent basket to same side as current value") - basketTradingService.editCellAction().func("chris-001", "side", "Buy", vpBasketTrading, vuuServer.session) - vuuServer.runOnce() - - Then("get all the updates that have occurred for all view ports from the outbound queue") - val updates = combineQs(vpBasketTrading) - - And("assert the basket trading table has not changed side....") - assertVpEq(filterByVp(vpBasketTrading, updates)) { - Table( - ("instanceId", "basketId", "basketName", "status", "units", "filledPct", "fxRateToUsd", "totalNotional", "totalNotionalUsd", "side"), - ("chris-001", ".FTSE", "chris-001", "OFF-MARKET", 1, null, null, null, null, "Buy") - ) - } - - And("assert the basket trading constituent table has not changed sides") - assertVpEq(filterByVp(vpBasketTradingCons, updates)) { - Table( - ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId"), - (10L, "Buy", "chris-001", "chris-001.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2), - (10L, "Sell", "chris-001", "chris-001.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2), - (10L, "Buy", "chris-001", "chris-001.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2) - ) - } - } - } + //todo this only works if running one test at a time. BasketService singleton object is used to create the basket trade id and doesnt get reset between tests +// Scenario("Check updating trade basket side with no change does not update constituents side") { +// import BasketModule._ +// implicit val clock: Clock = new TestFriendlyClock(10001L) +// implicit val lifecycle: LifecycleContainer = new LifecycleContainer() +// implicit val tableDefContainer: TableDefContainer = new TableDefContainer(Map()) +// implicit val metricsProvider: MetricsProvider = new MetricsProviderImpl +// +// withVuuServer(PriceModule(), BasketModule()) { +// vuuServer => +// +// vuuServer.login("testUser", "testToken2") +// +// GivenBasketTradeExist(vuuServer, ".FTSE", "MyCustomBasket") +// +// val vpBasketTrading = vuuServer.createViewPort(BasketModule.NAME, BasketTradingTable) +// val vpBasketTradingCons = vuuServer.createViewPort(BasketModule.NAME, BasketTradingConstituentTable) +// val basketTradingService = vuuServer.getViewPortRpcServiceProxy[BasketTradingServiceIF](vpBasketTrading) +// +// When("we edit the side of the parent basket to same side as current value") +// basketTradingService.editCellAction().func("testUser-00001", "side", "Buy", vpBasketTrading, vuuServer.session) +// vuuServer.runOnce() +// +// Then("get all the updates that have occurred for all view ports from the outbound queue") +// val updates2 = combineQs(vpBasketTrading) +// +// And("assert the basket trading table has not changed side....") +// assertVpEq(filterByVp(vpBasketTrading, updates2)) { +// Table( +// ("instanceId", "basketId", "basketName", "status", "units", "filledPct", "fxRateToUsd", "totalNotional", "totalNotionalUsd", "side"), +// ("testUser-00001", ".FTSE", "MyCustomBasket", "OFF-MARKET", 1, null, null, null, null, "Buy") +// ) +// } +// +// And("assert the basket trading constituent table has not changed sides") +// assertVpEq(filterByVp(vpBasketTradingCons, updates2)) { +// Table( +// ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId"), +// (10L, "Buy", "testUser-00001", "testUser-00001.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2), +// (10L, "Sell", "testUser-00001", "testUser-00001.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2), +// (10L, "Buy", "testUser-00001", "testUser-00001.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2) +// ) +// } +// } +// } } - - Feature("Basket Trading Constituent Join Service Test Case") { - Scenario("Update selected trade constituent sides when menu action is triggered") { - withVuuServer(PriceModule(), BasketModule()) { - vuuServer => - - vuuServer.login("testUser", "testToken") - - val basketName = "test_basket1" - val tradeId = GivenBasketTrade(vuuServer.tableContainer, basketName, "Buy") - GivenPrices(vuuServer.tableContainer, List(("VOD.L", 1.1, 1.4), ("BP.L", 2.1, 2.4))) - GivenBasketTradeConstituentsJoin(vuuServer.tableContainer, tradeId, Map(("VOD.L" -> "Buy"), ("BP.L" -> "Sell"))) - - val vpBasketTrading = vuuServer.createViewPort(BasketModule.NAME, BasketTradingTable) - val vpBasketTradingConsJoin = vuuServer.createViewPort(BasketModule.NAME, BasketTradingConstituentJoin) - vuuServer.runOnce() - - When("we select multiple constituent rows ") - vpBasketTradingConsJoin.setSelection(Array(1, 2)) - - And("select set sell context menu") - val basketTradingConstituentJoinService = vuuServer.getViewPortRpcServiceProxy[BasketTradingConstituentJoinServiceIF](vpBasketTradingConsJoin) - val selection = vpBasketTradingConsJoin.getSelection - val vpSelection = ViewPortSelection(selection, vpBasketTradingConsJoin) - basketTradingConstituentJoinService.setSell(vpSelection, vuuServer.session) - vuuServer.runOnce() - - Then("get all the updates that have occurred for all view ports from the outbound queue") - val updates = combineQs(vpBasketTrading) - - And("assert only selected constituent has changed set side to sell") - assertVpEq(filterByVp(vpBasketTradingConsJoin, updates)) { - Table( - ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId", "bid", "bidSize", "ask", "askSize"), - (10L, "Buy", "chris-001", "chris-001.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 2.1, 1, 2.4, 1), - (10L, "Sell", "chris-001", "chris-001.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, null, null, null, null), - (10L, "Sell", "chris-001", "chris-001.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 1.1, 1, 1.4, 1) - ) - } - } - } - } - - def uuid = java.util.UUID.randomUUID.toString def GivenBasketTradeExist(vuuServer: TestVuuServer, basketId: String, basketTradeName: String): Unit = { - val basketProvider = vuuServer.getProvider(BasketModule.NAME, BasketTable) + val basketProvider = vuuServer.getProvider(BasketModule.NAME, BasketModule.BasketTable) basketProvider.tick(".FTSE", Map(B.Id -> ".FTSE", B.Name -> ".FTSE 100", B.NotionalValue -> 1000001, B.NotionalValueUsd -> 1500001)) - val constituentProvider = vuuServer.getProvider(BasketModule.NAME, BasketConstituentTable) + val constituentProvider = vuuServer.getProvider(BasketModule.NAME, BasketModule.BasketConstituentTable) constituentProvider.tick("VOD.L.FTSE", Map(BC.RicBasketId -> "VOD.L.FTSE", BC.Ric -> "VOD.L", BC.BasketId -> basketId, BC.Weighting -> 0.1, BC.Side -> "Buy", BC.Description -> "Vodafone")) constituentProvider.tick("BT.L.FTSE", Map(BC.RicBasketId -> "BT.L.FTSE", BC.Ric -> "BT.L", BC.BasketId -> basketId, BC.Weighting -> 0.1, BC.Side -> "Sell", BC.Description -> "British Telecom")) constituentProvider.tick("BP.L.FTSE", Map(BC.RicBasketId -> "BP.L.FTSE", BC.Ric -> "BP.L", BC.BasketId -> basketId, BC.Weighting -> 0.1, BC.Side -> "Buy", BC.Description -> "Beyond Petroleum")) - val vpBasket = vuuServer.createViewPort(BasketModule.NAME, BasketTable) + val vpBasket = vuuServer.createViewPort(BasketModule.NAME, BasketModule.BasketTable) val basketService = vuuServer.getViewPortRpcServiceProxy[BasketServiceIF](vpBasket) basketService.createBasket(basketId, basketTradeName)(vuuServer.requestContext) } - - def GivenBasketTrade(tableContainer: TableContainer, basketName: String, side: String): String = { - val table = tableContainer.getTable(BasketModule.BasketTradingTable) - val rowKey = s"$uuid.$basketName" - table.processUpdate(rowKey, TestDataFactory.createBasketTradingRow(rowKey, basketName, side), clock.now()) - rowKey - } - - def GivenBasketTradeConstituents(tableContainer: TableContainer, tradeId: String, ricToSide: Map[String, String]): Unit = { - val table = tableContainer.getTable(BasketModule.BasketTradingConstituentTable) - ricToSide.foreach(item => { - val (ric, side) = item - val row = TestDataFactory.createBasketTradingConstituentRow(tradeId, ric, side) - table.processUpdate(row.key, row, clock.now()) - }) - } - - def GivenBasketTradeConstituentsJoin(tableContainer: TableContainer, tradeId: String, ricToSide: Map[String, String]): Unit = { - val table = tableContainer.getTable(BasketModule.BasketTradingConstituentJoin) - ricToSide.foreach(item => { - val (ric, side) = item - val row = TestDataFactory.createBasketTradingConstituentJoinRow(tradeId, ric, side) - updateJoinTable(table, row) - }) - } - - def updateJoinTable(table: DataTable, row: RowWithData): Unit = { - val joinTable = table.asTable.asInstanceOf[JoinTable] - val baseTableDef = joinTable.getTableDef.asInstanceOf[JoinTableDef].baseTable - joinTable.sourceTables.get(baseTableDef.name) match { - case Some(table: DataTable) => - table.processUpdate(row.key, row, clock.now()) - case None => - //log and throw? - } - } - - def GivenPrices(tableContainer: TableContainer, prices: List[(String, Double, Double)]) = { - val table = tableContainer.getTable("prices") - for ((ric, bid, ask) <- prices) { - val rowKey = s"$uuid" - table.processUpdate(rowKey, TestDataFactory.createPricesRow(rowKey, ric, bid, ask), clock.now()) - } - } - - //todo having issue importing side constant - //todo introduce case class BasketTrading? - } \ No newline at end of file diff --git a/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortAction.scala b/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortAction.scala index 6f33691fc..43f9f7c72 100644 --- a/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortAction.scala +++ b/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortAction.scala @@ -25,7 +25,6 @@ case class CloseDialogViewPortAction(vpId: String) extends ViewPortAction new Type(value = classOf[NoAction], name = "NO_ACTION"), new Type(value = classOf[ViewPortEditSuccess], name = "VP_EDIT_SUCCESS"), new Type(value = classOf[ViewPortEditFailure], name = "VP_EDIT_FAIL"), - new Type(value = classOf[ViewPortRpcFailure], name = "VP_RPC_FAILURE") new Type(value = classOf[ViewPortRpcSuccess], name = "VP_RCP_SUCCESS"), new Type(value = classOf[ViewPortRpcFailure], name = "VP_RCP_FAIL"), )) diff --git a/vuu/src/test/scala/org/finos/vuu/test/impl/TestVuuServerImpl.scala b/vuu/src/test/scala/org/finos/vuu/test/impl/TestVuuServerImpl.scala index e65baca45..bd1863165 100644 --- a/vuu/src/test/scala/org/finos/vuu/test/impl/TestVuuServerImpl.scala +++ b/vuu/src/test/scala/org/finos/vuu/test/impl/TestVuuServerImpl.scala @@ -165,7 +165,7 @@ class TestVuuServerImpl(val modules: List[ViewServerModule])(implicit clock: Clo override def login(user: String, token: String): Unit = { handler = factory.create() - val packet = serializer.serialize(JsonViewServerMessage(RequestId.oneNew(), "", "", "", LoginRequest("TOKEN", "AAAA"))) + val packet = serializer.serialize(JsonViewServerMessage(RequestId.oneNew(), "", "", "", LoginRequest("TOKEN", user))) handler.handle(packet, channel) channel.popMsg match { @@ -173,7 +173,6 @@ class TestVuuServerImpl(val modules: List[ViewServerModule])(implicit clock: Clo val msg = serializer.deserialize(msgPacket) clientSessionId = ClientSessionId(msg.sessionId, user) } - } override def overrideViewPortDef(table: String, vpDefFunc: (DataTable, Provider, ProviderContainer, TableContainer) => ViewPortDef): Unit = { From 351a5b2f4c8913cb1e1fe5f19136411be775d110 Mon Sep 17 00:00:00 2001 From: Na Lee Ha Date: Mon, 27 Nov 2023 10:53:08 +0000 Subject: [PATCH 09/21] #1010 re-naming to VP_EDIT_FAILURE and VP_RCP_FAILURE for consistency --- .../main/scala/org/finos/vuu/viewport/ViewPortAction.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortAction.scala b/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortAction.scala index 43f9f7c72..a91273908 100644 --- a/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortAction.scala +++ b/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortAction.scala @@ -24,8 +24,8 @@ case class CloseDialogViewPortAction(vpId: String) extends ViewPortAction new Type(value = classOf[CloseDialogViewPortAction], name = "CLOSE_DIALOG_ACTION"), new Type(value = classOf[NoAction], name = "NO_ACTION"), new Type(value = classOf[ViewPortEditSuccess], name = "VP_EDIT_SUCCESS"), - new Type(value = classOf[ViewPortEditFailure], name = "VP_EDIT_FAIL"), + new Type(value = classOf[ViewPortEditFailure], name = "VP_EDIT_FAILURE"), new Type(value = classOf[ViewPortRpcSuccess], name = "VP_RCP_SUCCESS"), - new Type(value = classOf[ViewPortRpcFailure], name = "VP_RCP_FAIL"), + new Type(value = classOf[ViewPortRpcFailure], name = "VP_RCP_FAILURE"), )) trait ViewPortActionMixin {} From f5a41e6ca192eaf572c6b0237e615105aab7d3b3 Mon Sep 17 00:00:00 2001 From: Na Lee Ha Date: Mon, 27 Nov 2023 18:39:25 +0000 Subject: [PATCH 10/21] #1019 converting weighting and limit price field to a double as json serialisation parse it as BigDecimal or Integer --- .../BasketTradingConstituentJoinService.scala | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala index 1c76a099a..aa1811d6a 100644 --- a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala +++ b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala @@ -12,6 +12,8 @@ import org.finos.vuu.net.rpc.{EditRpcHandler, RpcHandler} import org.finos.vuu.net.{ClientSessionId, RequestContext} import org.finos.vuu.viewport._ +import scala.util.control.NonFatal + trait BasketTradingConstituentJoinServiceIF extends EditRpcHandler { def setSell(selection: ViewPortSelection, session: ClientSessionId): ViewPortAction @@ -77,12 +79,30 @@ class BasketTradingConstituentJoinService(val table: DataTable, val tableContain } private def onEditCell(key: String, columnName: String, data: Any, vp: ViewPort, session: ClientSessionId): ViewPortEditAction = { - getBaseTable() match { - case Some(baseTable: DataTable) => - baseTable.processUpdate(key, RowWithData(key, Map(ColumnName.InstanceIdRic -> key, columnName -> data)), clock.now()) - ViewPortEditSuccess() - case None => - ViewPortEditFailure("Could not find base table") + try { + getBaseTable() match { + case Some(baseTable: DataTable) => + columnName match { + case ColumnName.Weighting | ColumnName.LimitPrice => + val doubleValue = convertToDouble(data) + baseTable.processUpdate(key, RowWithData(key, Map(ColumnName.InstanceIdRic -> key, columnName -> doubleValue)), clock.now()) + case _ => baseTable.processUpdate(key, RowWithData(key, Map(ColumnName.InstanceIdRic -> key, columnName -> data)), clock.now()) + } + ViewPortEditSuccess() + case None => + ViewPortEditFailure("Could not find base table for basket trading constituent join ") + } + } catch { + case NonFatal(t) => ViewPortEditFailure(s"Could not update $columnName. $t") + } + } + private def convertToDouble(data:Any): Double = { + data match { + case decimalValue: java.math.BigDecimal => + decimalValue.doubleValue + case integer: java.lang.Integer => integer.toDouble + case int: Int => int.toDouble + case _ => data.asInstanceOf[Double] } } From b2cae8a5590f1c1a984433df82f3609d532843b2 Mon Sep 17 00:00:00 2001 From: Na Lee Ha Date: Fri, 1 Dec 2023 19:05:44 +0000 Subject: [PATCH 11/21] #979 moved logic for generating unique basket id to the static class, using that class to get generated basket trade if in tests --- .../module/basket/service/BasketService.scala | 22 ++-- .../core/module/basket/BasketCreateTest.scala | 8 +- .../basket/BasketMutateOffMarketTest.scala | 121 +++++++++--------- .../basket/BasketSendToMarketTest.scala | 9 +- 4 files changed, 82 insertions(+), 78 deletions(-) diff --git a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketService.scala b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketService.scala index 0f815215a..b720d80ad 100644 --- a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketService.scala +++ b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketService.scala @@ -4,17 +4,22 @@ import com.typesafe.scalalogging.StrictLogging import org.finos.toolbox.time.Clock import org.finos.vuu.core.module.basket.BasketModule import org.finos.vuu.core.module.basket.BasketModule.{BasketConstituentTable, Sides} -import org.finos.vuu.core.module.basket.service.BasketService.counter import org.finos.vuu.core.table.{DataTable, RowData, RowWithData, TableContainer} import org.finos.vuu.net.rpc.RpcHandler import org.finos.vuu.net.{ClientSessionId, RequestContext} import org.finos.vuu.order.oms.OmsApi import org.finos.vuu.viewport._ - import java.util.concurrent.atomic.AtomicInteger -object BasketService{ - val counter = new AtomicInteger(0) +object BasketTradeId { + + private val counter: AtomicInteger = new AtomicInteger(0) + var current:String = "NoneInitalised" //this is for testing but only works if tests that use this doesnt run in parallel + def oneNew(user:String): String = { + val counterValue = counter.incrementAndGet() + current = user + "-" + "".padTo(5 - counterValue.toString.length, "0").mkString + counterValue + current + } } trait BasketServiceIF{ @@ -25,11 +30,6 @@ class BasketService(val table: DataTable, val tableContainer: TableContainer, va import org.finos.vuu.core.module.basket.BasketModule.{BasketConstituentColumnNames => BC, BasketTradingColumnNames => BT, BasketTradingConstituentColumnNames => BTC} - private def getAndPadCounter(session: ClientSessionId): String = { - val counterValue = counter.incrementAndGet() - session.user + "-" + "".padTo(5 - counterValue.toString.length, "0").mkString + counterValue - } - private def getConstituentsForSourceBasket(basketId: String): List[RowData] = { val table = tableContainer.getTable(BasketConstituentTable) val keys = table.primaryKeys.toList @@ -68,13 +68,13 @@ class BasketService(val table: DataTable, val tableContainer: TableContainer, va val basketId = selection.rowKeyIndex.map({ case (key, _) => key }).toList.head - val instanceKey = getAndPadCounter(session) + val instanceKey = BasketTradeId.oneNew(session.user) createBasketInternal(basketId, instanceKey, instanceKey, session) } def createBasket(basketId: String, name: String)(ctx: RequestContext): ViewPortAction = { - val basketTradeId = getAndPadCounter(ctx.session) + val basketTradeId = BasketTradeId.oneNew(ctx.session.user) createBasketInternal(basketId, name, basketTradeId, ctx.session) } diff --git a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketCreateTest.scala b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketCreateTest.scala index c0a31d6d2..f847d55e6 100644 --- a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketCreateTest.scala +++ b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketCreateTest.scala @@ -5,7 +5,7 @@ import org.finos.toolbox.lifecycle.LifecycleContainer import org.finos.toolbox.time.{Clock, TestFriendlyClock} import org.finos.vuu.api.ViewPortDef import org.finos.vuu.core.module.TableDefContainer -import org.finos.vuu.core.module.basket.service.{BasketServiceIF, BasketTradingServiceIF} +import org.finos.vuu.core.module.basket.service.{BasketServiceIF, BasketTradeId, BasketTradingServiceIF} import org.finos.vuu.core.module.price.PriceModule import org.finos.vuu.core.table.TableTestHelper.combineQs import org.finos.vuu.order.oms.OmsApi @@ -54,21 +54,21 @@ class BasketCreateTest extends VuuServerTestCase { val basketService = vuuServer.getViewPortRpcServiceProxy[BasketServiceIF](viewportBasket) - val action = basketService.createBasket(".FTSE", "chris-001")(vuuServer.requestContext) + val action = basketService.createBasket(".FTSE", "TestBasket")(vuuServer.requestContext) val viewportBasketTrading = vuuServer.createViewPort(BasketModule.NAME, BasketTradingTable) val basketTradingService = vuuServer.getViewPortRpcServiceProxy[BasketTradingServiceIF](viewportBasketTrading) //CJS: I don't like this forced cast, need to look at that a bit - basketTradingService.editCellAction().func("chris-001", BT.Units, 100.asInstanceOf[Object], viewportBasketTrading, vuuServer.session) + basketTradingService.editCellAction().func(BasketTradeId.current, BT.Units, 100.asInstanceOf[Object], viewportBasketTrading, vuuServer.session) vuuServer.runOnce() assertVpEq(combineQsForVp(viewportBasketTrading)) { Table( ("basketId", "instanceId", "basketName", "units", "status", "filledPct", "totalNotionalUsd", "totalNotional", "fxRateToUsd", "side"), - (".FTSE", "chris-001", "chris-001", 100, "OFF-MARKET", null, null, null, null, "Buy") + (".FTSE", BasketTradeId.current, "TestBasket", 100, "OFF-MARKET", null, null, null, null, "Buy") ) } } diff --git a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala index c46c500af..8c9489cbd 100644 --- a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala +++ b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala @@ -6,7 +6,7 @@ import org.finos.toolbox.time.{Clock, TestFriendlyClock} import org.finos.vuu.api.ViewPortDef import org.finos.vuu.core.module.TableDefContainer import org.finos.vuu.core.module.basket.BasketModule.{BasketColumnNames => B, BasketConstituentColumnNames => BC} -import org.finos.vuu.core.module.basket.service.{BasketServiceIF, BasketTradingServiceIF} +import org.finos.vuu.core.module.basket.service.{BasketServiceIF, BasketTradeId, BasketTradingServiceIF} import org.finos.vuu.core.module.price.PriceModule import org.finos.vuu.order.oms.OmsApi import org.finos.vuu.test.{TestVuuServer, VuuServerTestCase} @@ -69,6 +69,7 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { Then("Get the Basket RPC Service and call create basket") val basketService = vuuServer.getViewPortRpcServiceProxy[BasketServiceIF](vpBasket) basketService.createBasket(".FTSE", "MyCustomBasket")(vuuServer.requestContext) + val basketTradeInstanceId = BasketTradeId.current val vpBasketTrading = vuuServer.createViewPort(BasketModule.NAME, BasketTradingTable) @@ -79,7 +80,7 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { assertVpEq(combineQsForVp(vpBasketTrading)) { Table( ("instanceId", "basketId", "basketName", "status", "units", "filledPct", "fxRateToUsd", "totalNotional", "totalNotionalUsd", "side"), - ("testUser-00001", ".FTSE", "MyCustomBasket", "OFF-MARKET", 1, null, null, null, null, "Buy") + (basketTradeInstanceId, ".FTSE", "MyCustomBasket", "OFF-MARKET", 1, null, null, null, null, "Buy") ) } @@ -90,16 +91,16 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { assertVpEq(combineQsForVp(vpBasketTradingCons)) { Table( ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId", "filledQty", "orderStatus"), - (10L, "Buy", "testUser-00001", "testUser-00001.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), - (10L, "Sell", "testUser-00001", "testUser-00001.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), - (10L, "Buy", "testUser-00001", "testUser-00001.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") + (10L, "Buy", basketTradeInstanceId, s"$basketTradeInstanceId.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (10L, "Sell", basketTradeInstanceId, s"$basketTradeInstanceId.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (10L, "Buy", basketTradeInstanceId, s"$basketTradeInstanceId.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") ) } val basketTradingService = vuuServer.getViewPortRpcServiceProxy[BasketTradingServiceIF](vpBasketTrading) When("we edit the side of the parent basket") - basketTradingService.editCellAction().func("testUser-00001", "side", "Sell", vpBasketTrading, vuuServer.session) + basketTradingService.editCellAction().func(basketTradeInstanceId, "side", "Sell", vpBasketTrading, vuuServer.session) Then("get all the updates that have occurred for all view ports from the outbound queue") val updates = combineQs(vpBasketTrading) @@ -108,7 +109,7 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { assertVpEq(filterByVp(vpBasketTrading, updates)) { Table( ("instanceId", "basketId", "basketName", "status", "units", "filledPct", "fxRateToUsd", "totalNotional", "totalNotionalUsd", "side"), - ("testUser-00001", ".FTSE", "MyCustomBasket", "OFF-MARKET", 1, null, null, null, null, "Sell") + (basketTradeInstanceId, ".FTSE", "MyCustomBasket", "OFF-MARKET", 1, null, null, null, null, "Sell") ) } @@ -117,73 +118,75 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { assertVpEq(filterByVp(vpBasketTradingCons, updates)) { Table( ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId", "filledQty", "orderStatus"), - (10L, "Sell", "testUser-00001", "testUser-00001.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), - (10L, "Buy", "testUser-00001", "testUser-00001.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), - (10L, "Sell", "testUser-00001", "testUser-00001.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") + (10L, "Sell", basketTradeInstanceId, s"$basketTradeInstanceId.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (10L, "Buy", basketTradeInstanceId, s"$basketTradeInstanceId.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (10L, "Sell", basketTradeInstanceId, s"$basketTradeInstanceId.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") ) } When("we edit the units of the parent basket") - basketTradingService.editCellAction().func("testUser-00001", "units", 1000L.asInstanceOf[Object], vpBasketTrading, vuuServer.session) + basketTradingService.editCellAction().func(basketTradeInstanceId, "units", 1000L.asInstanceOf[Object], vpBasketTrading, vuuServer.session) And("assert the basket trading constituent table has increased the units") assertVpEq(filterByVp(vpBasketTradingCons, combineQs(vpBasketTrading))) { Table( ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId", "filledQty", "orderStatus"), - (100L, "Sell", "testUser-00001", "testUser-00001.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), - (100L, "Buy", "testUser-00001", "testUser-00001.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), - (100L, "Sell", "testUser-00001", "testUser-00001.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") + (100L, "Sell", basketTradeInstanceId, s"$basketTradeInstanceId.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (100L, "Buy", basketTradeInstanceId, s"$basketTradeInstanceId.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (100L, "Sell", basketTradeInstanceId, s"$basketTradeInstanceId.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") ) } } } - //todo this only works if running one test at a time. BasketService singleton object is used to create the basket trade id and doesnt get reset between tests -// Scenario("Check updating trade basket side with no change does not update constituents side") { -// import BasketModule._ -// implicit val clock: Clock = new TestFriendlyClock(10001L) -// implicit val lifecycle: LifecycleContainer = new LifecycleContainer() -// implicit val tableDefContainer: TableDefContainer = new TableDefContainer(Map()) -// implicit val metricsProvider: MetricsProvider = new MetricsProviderImpl -// -// withVuuServer(PriceModule(), BasketModule()) { -// vuuServer => -// -// vuuServer.login("testUser", "testToken2") -// -// GivenBasketTradeExist(vuuServer, ".FTSE", "MyCustomBasket") -// -// val vpBasketTrading = vuuServer.createViewPort(BasketModule.NAME, BasketTradingTable) -// val vpBasketTradingCons = vuuServer.createViewPort(BasketModule.NAME, BasketTradingConstituentTable) -// val basketTradingService = vuuServer.getViewPortRpcServiceProxy[BasketTradingServiceIF](vpBasketTrading) -// -// When("we edit the side of the parent basket to same side as current value") -// basketTradingService.editCellAction().func("testUser-00001", "side", "Buy", vpBasketTrading, vuuServer.session) -// vuuServer.runOnce() -// -// Then("get all the updates that have occurred for all view ports from the outbound queue") -// val updates2 = combineQs(vpBasketTrading) -// -// And("assert the basket trading table has not changed side....") -// assertVpEq(filterByVp(vpBasketTrading, updates2)) { -// Table( -// ("instanceId", "basketId", "basketName", "status", "units", "filledPct", "fxRateToUsd", "totalNotional", "totalNotionalUsd", "side"), -// ("testUser-00001", ".FTSE", "MyCustomBasket", "OFF-MARKET", 1, null, null, null, null, "Buy") -// ) -// } -// -// And("assert the basket trading constituent table has not changed sides") -// assertVpEq(filterByVp(vpBasketTradingCons, updates2)) { -// Table( -// ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId"), -// (10L, "Buy", "testUser-00001", "testUser-00001.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2), -// (10L, "Sell", "testUser-00001", "testUser-00001.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2), -// (10L, "Buy", "testUser-00001", "testUser-00001.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2) -// ) -// } -// } -// } + Scenario("Check updating trade basket side with no change does not update constituents side") { + import BasketModule._ + implicit val clock: Clock = new TestFriendlyClock(10001L) + implicit val lifecycle: LifecycleContainer = new LifecycleContainer() + implicit val tableDefContainer: TableDefContainer = new TableDefContainer(Map()) + implicit val metricsProvider: MetricsProvider = new MetricsProviderImpl + + val omsApi = OmsApi() + withVuuServer(PriceModule(), BasketModule(omsApi)) { + vuuServer => + + vuuServer.login("testUser", "testToken2") + + GivenBasketTradeExist(vuuServer, ".FTSE", "MyCustomBasket") + val basketTradeInstanceId = BasketTradeId.current + + val vpBasketTrading = vuuServer.createViewPort(BasketModule.NAME, BasketTradingTable) + val vpBasketTradingCons = vuuServer.createViewPort(BasketModule.NAME, BasketTradingConstituentTable) + val basketTradingService = vuuServer.getViewPortRpcServiceProxy[BasketTradingServiceIF](vpBasketTrading) + + When("we edit the side of the parent basket to same side as current value") + basketTradingService.editCellAction().func(basketTradeInstanceId, "side", "Buy", vpBasketTrading, vuuServer.session) + vuuServer.runOnce() + + Then("get all the updates that have occurred for all view ports from the outbound queue") + val updates2 = combineQs(vpBasketTrading) + + And("assert the basket trading table has not changed side....") + assertVpEq(filterByVp(vpBasketTrading, updates2)) { + Table( + ("instanceId", "basketId", "basketName", "status", "units", "filledPct", "fxRateToUsd", "totalNotional", "totalNotionalUsd", "side"), + (basketTradeInstanceId, ".FTSE", "MyCustomBasket", "OFF-MARKET", 1, null, null, null, null, "Buy") + ) + } + + And("assert the basket trading constituent table has not changed sides") + assertVpEq(filterByVp(vpBasketTradingCons, updates2)) { + Table( + ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId", "filledQty", "orderStatus"), + (10L, "Buy", basketTradeInstanceId, s"$basketTradeInstanceId.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (10L, "Sell", basketTradeInstanceId, s"$basketTradeInstanceId.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (10L, "Buy", basketTradeInstanceId, s"$basketTradeInstanceId.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") + ) + } + } + } } + def GivenBasketTradeExist(vuuServer: TestVuuServer, basketId: String, basketTradeName: String): Unit = { val basketProvider = vuuServer.getProvider(BasketModule.NAME, BasketModule.BasketTable) basketProvider.tick(".FTSE", Map(B.Id -> ".FTSE", B.Name -> ".FTSE 100", B.NotionalValue -> 1000001, B.NotionalValueUsd -> 1500001)) diff --git a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketSendToMarketTest.scala b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketSendToMarketTest.scala index 315e5cb88..36fc23848 100644 --- a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketSendToMarketTest.scala +++ b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketSendToMarketTest.scala @@ -6,7 +6,7 @@ import org.finos.toolbox.time.{Clock, TestFriendlyClock} import org.finos.vuu.api.ViewPortDef import org.finos.vuu.core.module.TableDefContainer import org.finos.vuu.core.module.basket.BasketTestCaseHelper.{tickBasketDef, tickConstituentsDef, tickPrices} -import org.finos.vuu.core.module.basket.service.{BasketServiceIF, BasketTradingServiceIF} +import org.finos.vuu.core.module.basket.service.{BasketServiceIF, BasketTradeId, BasketTradingServiceIF} import org.finos.vuu.core.module.price.PriceModule import org.finos.vuu.order.oms.OmsApi import org.finos.vuu.test.VuuServerTestCase @@ -50,7 +50,8 @@ class BasketSendToMarketTest extends VuuServerTestCase { Then("Get the Basket RPC Service and call create basket") val basketService = vuuServer.getViewPortRpcServiceProxy[BasketServiceIF](vpBasket) - basketService.createBasket(".FTSE", "chris-001")(vuuServer.requestContext) + basketService.createBasket(".FTSE", "TestBasket")(vuuServer.requestContext) + val basketTradeInstanceId = BasketTradeId.current val vpBasketTrading = vuuServer.createViewPort(BasketModule.NAME, BasketTradingTable) @@ -59,7 +60,7 @@ class BasketSendToMarketTest extends VuuServerTestCase { val tradingService = vuuServer.getViewPortRpcServiceProxy[BasketTradingServiceIF](vpBasketTrading) And("send the basket to market") - tradingService.sendToMarket("chris-001")(vuuServer.requestContext) + tradingService.sendToMarket(basketTradeInstanceId)(vuuServer.requestContext) vuuServer.runOnce() @@ -67,7 +68,7 @@ class BasketSendToMarketTest extends VuuServerTestCase { assertVpEq(combineQsForVp(vpBasketTrading)) { Table( ("instanceId", "basketId", "basketName", "status", "units", "filledPct", "fxRateToUsd", "totalNotional", "totalNotionalUsd", "side"), - ("chris-001", ".FTSE", "chris-001", "ON_MARKET", 1, null, null, null, null, "Buy") + (basketTradeInstanceId, ".FTSE", "TestBasket", "ON_MARKET", 1, null, null, null, null, "Buy") ) } } From 90eaa3117cd40d252adbd6aa042d2bce1fe2b30d Mon Sep 17 00:00:00 2001 From: Na Lee Ha Date: Tue, 5 Dec 2023 12:01:09 +0000 Subject: [PATCH 12/21] #980 making basket trade add constituent take in single ric as parsing arrays on rcp message is not supported --- .../BasketTradingConstituentJoinService.scala | 36 ++++++------- .../basket/BasketMutateOffMarketTest.scala | 50 ++++++++++++++++++- 2 files changed, 68 insertions(+), 18 deletions(-) diff --git a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala index aa1811d6a..66a33d5b3 100644 --- a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala +++ b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala @@ -19,9 +19,9 @@ trait BasketTradingConstituentJoinServiceIF extends EditRpcHandler { def setSell(selection: ViewPortSelection, session: ClientSessionId): ViewPortAction def setBuy(selection: ViewPortSelection, session: ClientSessionId): ViewPortAction -} -object BasketTradingConstituentJoinService {} + def addConstituent(ric: String)(ctx: RequestContext): ViewPortAction +} class BasketTradingConstituentJoinService(val table: DataTable, val tableContainer: TableContainer)(implicit clock: Clock) extends BasketTradingConstituentJoinServiceIF with RpcHandler with StrictLogging { @@ -29,6 +29,7 @@ class BasketTradingConstituentJoinService(val table: DataTable, val tableContain new SelectionViewPortMenuItem("Set Sell", "", this.setSell, "SET_SELECTION_SELL"), new SelectionViewPortMenuItem("Set Buy", "", this.setBuy, "SET_SELECTION_Buy"), ) + override def deleteRowAction(): ViewPortDeleteRowAction = ViewPortDeleteRowAction("", this.onDeleteRow) override def deleteCellAction(): ViewPortDeleteCellAction = ViewPortDeleteCellAction("", this.onDeleteCell) @@ -52,30 +53,30 @@ class BasketTradingConstituentJoinService(val table: DataTable, val tableContain } //this is RCP call and method name is part of contract with UI - def addConstituents(rics: Array[String])(ctx: RequestContext): ViewPortAction = { - if(table.size() == 0) + def addConstituent(ric: String)(ctx: RequestContext): ViewPortAction = { + if (table.size() == 0) ViewPortEditFailure(s"Failed to add constituents to ${table.name} as adding row to empty table is currently not supported") + else { + val existingConstituentRow = table.pullRow(table.primaryKeys.head) + val tradeId = existingConstituentRow.get(ColumnName.InstanceId).asInstanceOf[String] - val existingConstituentRow = table.pullRow(table.primaryKeys.head) - val tradeId = existingConstituentRow.get(ColumnName.InstanceId).asInstanceOf[String] + val tradeRow = tableContainer.getTable(BasketModule.BasketTradingTable).pullRow(tradeId) + val basketId = tradeRow.get(BTColumnName.BasketId).asInstanceOf[String] + val tradeUnit = tradeRow.get(BTColumnName.Units).asInstanceOf[Int] - val tradeRow = tableContainer.getTable(BasketModule.BasketTradingTable).pullRow(tradeId) - val basketId = tradeRow.get(BTColumnName.BasketId).asInstanceOf[String] - val tradeUnit = tradeRow.get(BTColumnName.Units).asInstanceOf[Int] - - val newRows = rics.map(ric => - mkTradingConstituentRow( + val newRow = mkTradingConstituentRow( basketTradeInstanceId = tradeId, sourceBasketId = basketId, tradeUnit = tradeUnit, - ric = ric)) + ric = ric) //todo should we guard against adding row for ric that already exist? - updateJoinTable(newRows) match { + updateJoinTable(Array(newRow)) match { case Right(_) => ViewPortRpcSuccess() case Left(errorReason) => ViewPortRpcFailure(errorReason.reason) } + } } private def onEditCell(key: String, columnName: String, data: Any, vp: ViewPort, session: ClientSessionId): ViewPortEditAction = { @@ -84,7 +85,7 @@ class BasketTradingConstituentJoinService(val table: DataTable, val tableContain case Some(baseTable: DataTable) => columnName match { case ColumnName.Weighting | ColumnName.LimitPrice => - val doubleValue = convertToDouble(data) + val doubleValue = convertToDouble(data) baseTable.processUpdate(key, RowWithData(key, Map(ColumnName.InstanceIdRic -> key, columnName -> doubleValue)), clock.now()) case _ => baseTable.processUpdate(key, RowWithData(key, Map(ColumnName.InstanceIdRic -> key, columnName -> data)), clock.now()) } @@ -93,10 +94,11 @@ class BasketTradingConstituentJoinService(val table: DataTable, val tableContain ViewPortEditFailure("Could not find base table for basket trading constituent join ") } } catch { - case NonFatal(t) => ViewPortEditFailure(s"Could not update $columnName. $t") + case NonFatal(t) => ViewPortEditFailure(s"Could not update $columnName. $t") } } - private def convertToDouble(data:Any): Double = { + + private def convertToDouble(data: Any): Double = { data match { case decimalValue: java.math.BigDecimal => decimalValue.doubleValue diff --git a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala index 8c9489cbd..6ede3b3ef 100644 --- a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala +++ b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala @@ -6,7 +6,7 @@ import org.finos.toolbox.time.{Clock, TestFriendlyClock} import org.finos.vuu.api.ViewPortDef import org.finos.vuu.core.module.TableDefContainer import org.finos.vuu.core.module.basket.BasketModule.{BasketColumnNames => B, BasketConstituentColumnNames => BC} -import org.finos.vuu.core.module.basket.service.{BasketServiceIF, BasketTradeId, BasketTradingServiceIF} +import org.finos.vuu.core.module.basket.service.{BasketServiceIF, BasketTradeId, BasketTradingConstituentJoinServiceIF, BasketTradingServiceIF} import org.finos.vuu.core.module.price.PriceModule import org.finos.vuu.order.oms.OmsApi import org.finos.vuu.test.{TestVuuServer, VuuServerTestCase} @@ -185,6 +185,54 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { } } } + + //TODO oin table cannot be tested currently as it doesnt get updated when underlying table gets updated +// Scenario("Adding new constituents by ric should add it to basket trading") { +// import BasketModule._ +// implicit val clock: Clock = new TestFriendlyClock(10001L) +// implicit val lifecycle: LifecycleContainer = new LifecycleContainer() +// implicit val tableDefContainer: TableDefContainer = new TableDefContainer(Map()) +// implicit val metricsProvider: MetricsProvider = new MetricsProviderImpl +// +// val omsApi = OmsApi() +// withVuuServer(PriceModule(), BasketModule(omsApi)) { +// vuuServer => +// +// vuuServer.login("testUser", "testToken2") +// +// GivenBasketTradeExist(vuuServer, ".FTSE", "MyCustomBasket") +// val basketTradeInstanceId = BasketTradeId.current +// +// val vpBasketTrading = vuuServer.createViewPort(BasketModule.NAME, BasketTradingTable) +// val vpBasketTradingCons = vuuServer.createViewPort(BasketModule.NAME, BasketTradingConstituentTable) +// val vpBasketTradingConsJoin = vuuServer.createViewPort(BasketModule.NAME, BasketTradingConstituentJoin) +// val basketTradingConstituentJoinService = vuuServer.getViewPortRpcServiceProxy[BasketTradingConstituentJoinServiceIF](vpBasketTradingConsJoin) +// +// vuuServer.runOnce() +// +// When("we edit the side of the parent basket to same side as current value") +// basketTradingConstituentJoinService.addConstituent("0001.HK")(vuuServer.requestContext) +// vuuServer.runOnce() +// +// Then("get all the updates that have occurred for all view ports from the outbound queue") +// val updates = combineQs(vpBasketTradingConsJoin) +// +// +// //todo map description +// //todo should basketid be where the stock was sourced from? in this case .HSI? +// +// And("assert the basket trading constituent table has not changed sides") +// assertVpEq(filterByVp(vpBasketTradingCons, updates)) { +// Table( +// ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId", "filledQty", "orderStatus"), +// (10L, "Buy", basketTradeInstanceId, s"$basketTradeInstanceId.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), +// (10L, "Sell", basketTradeInstanceId, s"$basketTradeInstanceId.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), +// (10L, "Buy", basketTradeInstanceId, s"$basketTradeInstanceId.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), +// (10L, "Buy", basketTradeInstanceId, s"$basketTradeInstanceId.0001.HK", ".FTSE", "0001.HK", "", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") +// ) +// } +// } +// } } def GivenBasketTradeExist(vuuServer: TestVuuServer, basketId: String, basketTradeName: String): Unit = { From 6390950c3e53ab8ab8415991e853802d92253a2f Mon Sep 17 00:00:00 2001 From: Na Lee Ha Date: Tue, 28 Nov 2023 11:58:30 +0000 Subject: [PATCH 13/21] #1025 updating internal order state's state when processing fill --- .../scala/org/finos/vuu/order/oms/impl/InMemOmsApi.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/example/order/src/main/scala/org/finos/vuu/order/oms/impl/InMemOmsApi.scala b/example/order/src/main/scala/org/finos/vuu/order/oms/impl/InMemOmsApi.scala index 1805f5a9b..daa11a7ed 100644 --- a/example/order/src/main/scala/org/finos/vuu/order/oms/impl/InMemOmsApi.scala +++ b/example/order/src/main/scala/org/finos/vuu/order/oms/impl/InMemOmsApi.scala @@ -58,9 +58,11 @@ class InMemOmsApi(implicit val clock: Clock) extends OmsApi { orderstate.copy(state = States.ACKED, nextEventTime = clock.now() + random.between(1000, MaxTimes.MAX_FILL_TIME_MS), orderId = orderId) case 'A' => val remainingQty = orderstate.qty - orderstate.filledQty - val fillQty = if(remainingQty > 1) random.between(1, orderstate.qty - orderstate.filledQty) else 1 - listeners.foreach(_.onFill(Fill(orderstate.orderId, fillQty, orderstate.price, orderstate.clientOrderId, orderstate.filledQty + fillQty, orderstate.qty))) - orderstate.copy(filledQty = orderstate.filledQty + fillQty, nextEventTime = clock.now() + random.between(1000, 5000)) + val fillQty = if(remainingQty > 1) random.between(1, remainingQty) else 1 + val totalFilledQty = orderstate.filledQty + fillQty + val state = if( orderstate.qty == totalFilledQty) States.FILLED else States.ACKED + listeners.foreach(_.onFill(Fill(orderstate.orderId, fillQty, orderstate.price, orderstate.clientOrderId, totalFilledQty, orderstate.qty))) + orderstate.copy(state = state, filledQty = totalFilledQty, nextEventTime = clock.now() + random.between(1000, 5000)) case _ => orderstate } From 5bf65d0e2abe03b3fbe536065991aaef16ffd7db Mon Sep 17 00:00:00 2001 From: Na Lee Ha Date: Tue, 28 Nov 2023 15:04:39 +0000 Subject: [PATCH 14/21] #1025 implementing taking basket trade off the market --- .../BasketTradingConstituentProvider.scala | 8 ++- .../basket/service/BasketTradingService.scala | 58 ++++++++++++++----- .../org/finos/vuu/order/oms/OmsApi.scala | 1 + .../vuu/order/oms/impl/InMemOmsApi.scala | 23 ++++++-- .../org/finos/vuu/order/oms/OmsApiTest.scala | 31 +++++++++- 5 files changed, 99 insertions(+), 22 deletions(-) diff --git a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/provider/BasketTradingConstituentProvider.scala b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/provider/BasketTradingConstituentProvider.scala index 2cc9d691d..74fb2dd71 100644 --- a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/provider/BasketTradingConstituentProvider.scala +++ b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/provider/BasketTradingConstituentProvider.scala @@ -19,14 +19,16 @@ class BasketTradingConstituentProvider(val table: DataTable, val omsApi: OmsApi) table.processUpdate(ack.clientOrderId, RowWithData(ack.clientOrderId, Map[String, Any](BTC.InstanceIdRic -> ack.clientOrderId, BTC.OrderStatus -> OrderStates.ACKED)),clock.now()) } - override def onCancelAck(ack: CancelAck): Unit = ??? + override def onCancelAck(ack: CancelAck): Unit = { + table.processUpdate(ack.clientOrderId, RowWithData(ack.clientOrderId, Map[String, Any](BTC.InstanceIdRic -> ack.clientOrderId, + BTC.OrderStatus -> OrderStates.CANCELLED)), clock.now()) + } override def onReplaceAck(ack: ReplaceAck): Unit = ??? override def onFill(fill: Fill): Unit = { val state = if(fill.orderQty == fill.totalFilledQty) OrderStates.FILLED else OrderStates.ACKED table.processUpdate(fill.clientOrderId, RowWithData(fill.clientOrderId, Map[String, Any](BTC.InstanceIdRic -> fill.clientOrderId, - BTC.FilledQty -> fill.totalFilledQty, BTC.OrderStatus -> state)) - ,clock.now()) + BTC.FilledQty -> fill.totalFilledQty, BTC.OrderStatus -> state)),clock.now()) } }) diff --git a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingService.scala b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingService.scala index a738b2887..29470b65d 100644 --- a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingService.scala +++ b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingService.scala @@ -4,13 +4,16 @@ import com.typesafe.scalalogging.StrictLogging import org.finos.toolbox.time.Clock import org.finos.vuu.core.module.basket.BasketModule import org.finos.vuu.core.module.basket.BasketModule.{BasketTradingConstituentTable, Sides} -import org.finos.vuu.core.table.{DataTable, RowWithData, TableContainer, ViewPortColumnCreator} +import org.finos.vuu.core.table.{DataTable, RowData, RowWithData, TableContainer, ViewPortColumnCreator} import org.finos.vuu.net.rpc.{EditRpcHandler, RpcHandler} import org.finos.vuu.net.{ClientSessionId, RequestContext} -import org.finos.vuu.order.oms.{NewOrder, OmsApi} +import org.finos.vuu.order.oms.{CancelOrder, NewOrder, OmsApi} import org.finos.vuu.viewport._ -trait BasketTradingServiceIF extends EditRpcHandler{ + +trait BasketTradingServiceIF extends EditRpcHandler { def sendToMarket(basketInstanceId: String)(ctx: RequestContext): ViewPortAction + + def takeOffMarket(basketInstanceId: String)(ctx: RequestContext): ViewPortAction } @@ -20,18 +23,14 @@ class BasketTradingService(val table: DataTable, val tableContainer: TableContai /** - * Send basket to market + * Send basket to market rpc call */ - override def sendToMarket(name: String)(ctx: RequestContext): ViewPortAction = { - val tableRow = table.asTable.pullRow(name) - - logger.info("Sending basket to market:" + name + " (row:" + tableRow + ")") + override def sendToMarket(basketInstanceId: String)(ctx: RequestContext): ViewPortAction = { + val tableRow = table.asTable.pullRow(basketInstanceId) - val tradingConsTable = tableContainer.getTable(BasketModule.BasketTradingConstituentTable) + logger.info("Sending basket to market:" + basketInstanceId + " (row:" + tableRow + ")") - val constituents = tradingConsTable.primaryKeys.toList - .map(tradingConsTable.pullRow) - .filter(_.get(BTC.InstanceId) == name) + val constituents = getConstituents(basketInstanceId) constituents.foreach(constituentRow => { @@ -48,12 +47,43 @@ class BasketTradingService(val table: DataTable, val tableContainer: TableContai omsApi.createOrder(nos) }) - table.processUpdate(name, RowWithData(name, - Map(BT.InstanceId -> name, BT.Status -> BasketStates.ON_MARKET)), clock.now()) + updateBasketTradeStatus(basketInstanceId, state = BasketStates.ON_MARKET) ViewPortEditSuccess() } + /** + * Take basket off market rpc call + */ + override def takeOffMarket(basketInstanceId: String)(ctx: RequestContext): ViewPortAction = { + val tableRow = table.asTable.pullRow(basketInstanceId) + + logger.info("Tasking basket off market:" + basketInstanceId + " (row:" + tableRow + ")") + + updateBasketTradeStatus(basketInstanceId, BasketStates.OFF_MARKET) + + getConstituents(basketInstanceId) + .flatMap(c => omsApi.getOrderId(clientOrderId = c.get(BTC.InstanceIdRic).toString)) + .foreach(orderId => omsApi.cancelOrder(CancelOrder(orderId))) + + ViewPortEditSuccess() + } + + private def getConstituents(basketInstanceId: String): List[RowData] = { + val tradingConsTable = tableContainer.getTable(BasketModule.BasketTradingConstituentTable) + + tradingConsTable.primaryKeys.toList + .map(tradingConsTable.pullRow) + .filter(_.get(BTC.InstanceId) == basketInstanceId) + } + + private def updateBasketTradeStatus(basketInstanceId: String, state: String): Unit = { + table.processUpdate( + basketInstanceId, + RowWithData(basketInstanceId, Map(BT.InstanceId -> basketInstanceId, BT.Status -> state)), + clock.now()) + } + private def onEditCell(key: String, columnName: String, data: Any, vp: ViewPort, session: ClientSessionId): ViewPortEditAction = { logger.info("Change requested for cell value for key:" + key + "(" + columnName + ":" + data + ")") diff --git a/example/order/src/main/scala/org/finos/vuu/order/oms/OmsApi.scala b/example/order/src/main/scala/org/finos/vuu/order/oms/OmsApi.scala index 12a15fdac..a780c854a 100644 --- a/example/order/src/main/scala/org/finos/vuu/order/oms/OmsApi.scala +++ b/example/order/src/main/scala/org/finos/vuu/order/oms/OmsApi.scala @@ -23,6 +23,7 @@ trait OmsApi { def addListener(omsListener: OmsListener): Unit def runOnce(): Unit def containsOrder(clientOrderId: String): Boolean + def getOrderId(clientOrderId: String): Option[Int] } trait OmsListener{ diff --git a/example/order/src/main/scala/org/finos/vuu/order/oms/impl/InMemOmsApi.scala b/example/order/src/main/scala/org/finos/vuu/order/oms/impl/InMemOmsApi.scala index daa11a7ed..94899cce8 100644 --- a/example/order/src/main/scala/org/finos/vuu/order/oms/impl/InMemOmsApi.scala +++ b/example/order/src/main/scala/org/finos/vuu/order/oms/impl/InMemOmsApi.scala @@ -42,7 +42,13 @@ class InMemOmsApi(implicit val clock: Clock) extends OmsApi { override def replaceOrder(replaceOrder: ReplaceOrder): Unit = ??? - override def cancelOrder(cancelOrder: CancelOrder): Unit = ??? + override def cancelOrder(cancelOrder: CancelOrder): Unit = { + orders = orders.map(orderState => + if (orderState.orderId == cancelOrder.orderId) + orderState.copy(state = States.CANCELLED, nextEventTime = clock.now()) + else orderState + ) + } override def addListener(omsListener: OmsListener): Unit = listeners = listeners ++ List(omsListener) @@ -60,9 +66,12 @@ class InMemOmsApi(implicit val clock: Clock) extends OmsApi { val remainingQty = orderstate.qty - orderstate.filledQty val fillQty = if(remainingQty > 1) random.between(1, remainingQty) else 1 val totalFilledQty = orderstate.filledQty + fillQty - val state = if( orderstate.qty == totalFilledQty) States.FILLED else States.ACKED + val nextState = if( orderstate.qty == totalFilledQty) States.FILLED else States.ACKED listeners.foreach(_.onFill(Fill(orderstate.orderId, fillQty, orderstate.price, orderstate.clientOrderId, totalFilledQty, orderstate.qty))) - orderstate.copy(state = state, filledQty = totalFilledQty, nextEventTime = clock.now() + random.between(1000, 5000)) + orderstate.copy(state = nextState, filledQty = totalFilledQty, nextEventTime = clock.now() + random.between(1000, 5000)) + case 'X' => + listeners.foreach(_.onCancelAck(CancelAck(orderstate.orderId, orderstate.clientOrderId))) + orderstate case _ => orderstate } @@ -72,8 +81,14 @@ class InMemOmsApi(implicit val clock: Clock) extends OmsApi { } }) - orders = orders.filter(os => os.filledQty != os.qty) + orders = orders.filter(os => os.state != States.FILLED && os.state != States.CANCELLED) } + override def getOrderId(clientOrderId: String): Option[Int] = { + orders.find(order => order.clientOrderId == clientOrderId) match { + case Some(o) => Some(o.orderId) + case None => None + } + } } diff --git a/example/order/src/test/scala/org/finos/vuu/order/oms/OmsApiTest.scala b/example/order/src/test/scala/org/finos/vuu/order/oms/OmsApiTest.scala index e3e2b54f5..164bb1686 100644 --- a/example/order/src/test/scala/org/finos/vuu/order/oms/OmsApiTest.scala +++ b/example/order/src/test/scala/org/finos/vuu/order/oms/OmsApiTest.scala @@ -74,7 +74,7 @@ class OmsApiTest extends AnyFeatureSpec with GivenWhenThen with Matchers { omsApi.containsOrder("clOrdId1") should equal(true) - listener.getOrderState("clOrdId1").get.filledQty should be > (0L) + listener.getOrderState("clOrdId1").get.filledQty should be > 0L for (n <- 1 to 1000) { clock.sleep(MAX_FILL_TIME_MS) @@ -84,5 +84,34 @@ class OmsApiTest extends AnyFeatureSpec with GivenWhenThen with Matchers { omsApi.containsOrder("clOrdId1") should equal(false) } + + Scenario("Check we can submit order and cancel") { + + implicit val clock: Clock = new TestFriendlyClock(1000L) + + val omsApi = OmsApi() + + val listener = new TestListener() + + omsApi.addListener(listener) + + omsApi.createOrder(NewOrder("Buy", "VOD.L", 1000L, 100.01, "clOrdId1")) + omsApi.createOrder(NewOrder("Buy", "BP.L", 1000L, 150.01, "clOrdId2")) + + clock.sleep(MAX_ACK_TIME_MS) + omsApi.runOnce() + + listener.getOrderState("clOrdId1").get.state should equal("ACKED") + + val orderId = omsApi.getOrderId("clOrdId1").get + omsApi.cancelOrder(CancelOrder(orderId)) + omsApi.runOnce() + + omsApi.containsOrder("clOrdId1") should equal(false) + listener.getOrderState("clOrdId1").get.state should equal("CANCELLED") + + omsApi.containsOrder("clOrdId2") should equal(true) + + } } } From b8980424d986d5cb285463ff63d662f518243565 Mon Sep 17 00:00:00 2001 From: Na Lee Ha Date: Tue, 28 Nov 2023 15:08:36 +0000 Subject: [PATCH 15/21] #1025 using MAX_FILL_TIME_MS when generating random next event time --- .../scala/org/finos/vuu/order/oms/impl/InMemOmsApi.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/example/order/src/main/scala/org/finos/vuu/order/oms/impl/InMemOmsApi.scala b/example/order/src/main/scala/org/finos/vuu/order/oms/impl/InMemOmsApi.scala index 94899cce8..09b4ac4c2 100644 --- a/example/order/src/main/scala/org/finos/vuu/order/oms/impl/InMemOmsApi.scala +++ b/example/order/src/main/scala/org/finos/vuu/order/oms/impl/InMemOmsApi.scala @@ -68,7 +68,7 @@ class InMemOmsApi(implicit val clock: Clock) extends OmsApi { val totalFilledQty = orderstate.filledQty + fillQty val nextState = if( orderstate.qty == totalFilledQty) States.FILLED else States.ACKED listeners.foreach(_.onFill(Fill(orderstate.orderId, fillQty, orderstate.price, orderstate.clientOrderId, totalFilledQty, orderstate.qty))) - orderstate.copy(state = nextState, filledQty = totalFilledQty, nextEventTime = clock.now() + random.between(1000, 5000)) + orderstate.copy(state = nextState, filledQty = totalFilledQty, nextEventTime = clock.now() + random.between(1000, MaxTimes.MAX_FILL_TIME_MS)) case 'X' => listeners.foreach(_.onCancelAck(CancelAck(orderstate.orderId, orderstate.clientOrderId))) orderstate @@ -86,8 +86,8 @@ class InMemOmsApi(implicit val clock: Clock) extends OmsApi { } override def getOrderId(clientOrderId: String): Option[Int] = { - orders.find(order => order.clientOrderId == clientOrderId) match { - case Some(o) => Some(o.orderId) + orders.find(os => os.clientOrderId == clientOrderId) match { + case Some(os) => Some(os.orderId) case None => None } } From dc35263416ada4d4d42f68db1a5ab35a255b6bfc Mon Sep 17 00:00:00 2001 From: Na Lee Ha Date: Fri, 1 Dec 2023 19:47:16 +0000 Subject: [PATCH 16/21] #1025 updated basket send to market test to include taking off the market --- .../basket/BasketSendToMarketTest.scala | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketSendToMarketTest.scala b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketSendToMarketTest.scala index 36fc23848..8f0abcf56 100644 --- a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketSendToMarketTest.scala +++ b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketSendToMarketTest.scala @@ -54,9 +54,20 @@ class BasketSendToMarketTest extends VuuServerTestCase { val basketTradeInstanceId = BasketTradeId.current val vpBasketTrading = vuuServer.createViewPort(BasketModule.NAME, BasketTradingTable) + val vpBasketTradingCons = vuuServer.createViewPort(BasketModule.NAME, BasketTradingConstituentTable) vuuServer.runOnce() + And("Check the trading constituents are created") + assertVpEq(combineQsForVp(vpBasketTradingCons)) { + Table( + ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId", "filledQty", "orderStatus"), + (10L, "Buy", basketTradeInstanceId, s"$basketTradeInstanceId.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (10L, "Sell", basketTradeInstanceId, s"$basketTradeInstanceId.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (10L, "Buy", basketTradeInstanceId, s"$basketTradeInstanceId.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") + ) + } + val tradingService = vuuServer.getViewPortRpcServiceProxy[BasketTradingServiceIF](vpBasketTrading) And("send the basket to market") @@ -71,6 +82,19 @@ class BasketSendToMarketTest extends VuuServerTestCase { (basketTradeInstanceId, ".FTSE", "TestBasket", "ON_MARKET", 1, null, null, null, null, "Buy") ) } + + Then("Take the basket off the market") + tradingService.takeOffMarket(basketTradeInstanceId)(vuuServer.requestContext) + + vuuServer.runOnce() + + And("verify basket is on market") + assertVpEq(combineQsForVp(vpBasketTrading)) { + Table( + ("instanceId", "basketId", "basketName", "status", "units", "filledPct", "fxRateToUsd", "totalNotional", "totalNotionalUsd", "side"), + (basketTradeInstanceId, ".FTSE", "TestBasket", "OFF_MARKET", 1, null, null, null, null, "Buy") + ) + } } } } From b83cfbb5f009f45c3bf96ab13cf6fb3debcdcea0 Mon Sep 17 00:00:00 2001 From: Na Lee Ha Date: Tue, 28 Nov 2023 18:06:08 +0000 Subject: [PATCH 17/21] #1027 fix for where basket constituent is not sorting --- .../core/module/basket/provider/BasketConstituentProvider.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/provider/BasketConstituentProvider.scala b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/provider/BasketConstituentProvider.scala index 4b1ec3d06..a87c400ed 100644 --- a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/provider/BasketConstituentProvider.scala +++ b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/provider/BasketConstituentProvider.scala @@ -34,7 +34,7 @@ class BasketConstituentProvider(val table: DataTable)(implicit lifecycle: Lifecy val weighting = row("Weighting") val side = BasketConstants.Side.Buy val ricBasketId = symbol + "." + basketId - table.processUpdate(ricBasketId, RowWithData(symbol, Map( + table.processUpdate(ricBasketId, RowWithData(ricBasketId, Map( Ric -> symbol, BasketId -> basketId, RicBasketId -> ricBasketId, From ec58b408a1f25bd5698805643eafe37c7153f9ea Mon Sep 17 00:00:00 2001 From: Na Lee Ha Date: Tue, 5 Dec 2023 10:04:15 +0000 Subject: [PATCH 18/21] #1036 adding equals and hashcode override methods to be able to compare ViewPortColumns --- .../finos/vuu/viewport/ViewPortColumns.scala | 59 ++++++++++++------- .../vuu/viewport/ViewPortColumnsTests.scala | 55 +++++++++++++++++ 2 files changed, 92 insertions(+), 22 deletions(-) create mode 100644 vuu/src/test/scala/org/finos/vuu/viewport/ViewPortColumnsTests.scala diff --git a/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortColumns.scala b/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortColumns.scala index 02775ed7c..eca0bd55b 100644 --- a/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortColumns.scala +++ b/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortColumns.scala @@ -2,40 +2,55 @@ package org.finos.vuu.viewport import org.finos.vuu.core.table.{CalculatedColumn, Column, RowData, RowWithData} -class ViewPortColumns(sourceColumns: List[Column]){ +class ViewPortColumns(sourceColumns: List[Column]) { - @volatile private var columns: List[Column] = sourceColumns + @volatile private var columns: List[Column] = sourceColumns - def addColumn(column: Column): Unit = { - columns = columns ++ List(column) - } + def canEqual(a: Any): Boolean = a.isInstanceOf[ViewPortColumns] - def columnExists(name: String): Boolean = { - columns.exists(_.name == name) + override def equals(that: Any): Boolean = + that match { + case that: ViewPortColumns => + that.canEqual(this) && + this.columns.size == that.columns.size && + 0 == this.columns.sortBy(c => c.name) + .zip(that.columns.sortBy(c => c.name)) + .count(columnPair => columnPair._1.name != columnPair._2.name || columnPair._1.dataType != columnPair._2.dataType) + case _ => false } - def getColumns(): List[Column] = columns + override def hashCode(): Int = columns.sortBy(c => c.name).map(_.hashCode()).hashCode() - def getColumnForName(name: String): Option[Column] = { - columns.find(_.name == name) - } + def addColumn(column: Column): Unit = { + columns = columns ++ List(column) + } - def count(): Int = columns.size + def columnExists(name: String): Boolean = { + columns.exists(_.name == name) + } - private lazy val hasCalculatedColumn = columns.exists(c => c.isInstanceOf[CalculatedColumn]) + def getColumns(): List[Column] = columns - def pullRow(key: String, row: RowData): RowData = { + def getColumnForName(name: String): Option[Column] = { + columns.find(_.name == name) + } - if(!hasCalculatedColumn){ - row - }else{ - val rowData = this.getColumns().map(c => c.name -> row.get(c)).toMap - RowWithData(key, rowData) - } - } + def count(): Int = columns.size - def pullRowAlwaysFilter(key: String, row: RowData): RowData = { + private lazy val hasCalculatedColumn = columns.exists(c => c.isInstanceOf[CalculatedColumn]) + + def pullRow(key: String, row: RowData): RowData = { + + if (!hasCalculatedColumn) { + row + } else { val rowData = this.getColumns().map(c => c.name -> row.get(c)).toMap RowWithData(key, rowData) + } + } + + def pullRowAlwaysFilter(key: String, row: RowData): RowData = { + val rowData = this.getColumns().map(c => c.name -> row.get(c)).toMap + RowWithData(key, rowData) } } diff --git a/vuu/src/test/scala/org/finos/vuu/viewport/ViewPortColumnsTests.scala b/vuu/src/test/scala/org/finos/vuu/viewport/ViewPortColumnsTests.scala new file mode 100644 index 000000000..ff678c1bf --- /dev/null +++ b/vuu/src/test/scala/org/finos/vuu/viewport/ViewPortColumnsTests.scala @@ -0,0 +1,55 @@ +package org.finos.vuu.viewport + +import org.finos.vuu.core.table.Columns +import org.scalatest.featurespec.AnyFeatureSpec + +class ViewPortColumnsTests extends AnyFeatureSpec { + + Feature("compare two view port columns") { + + Scenario("when all the columns names are same equality check should return true") { + val sourceColumn = Columns.fromNames("firstColumn:String", "secondColumn:String") + val oldVPColumns = new ViewPortColumns(sourceColumn.toList) + val sourceColumn2 = Columns.fromNames("firstColumn:String", "secondColumn:String") + val newVPColumns = new ViewPortColumns(sourceColumn2.toList) + + assert(oldVPColumns == newVPColumns) + } + + Scenario("when all the columns names are same in different order equality check should return true") { + val sourceColumn = Columns.fromNames("firstColumn:String", "secondColumn:String") + val oldVPColumns = new ViewPortColumns(sourceColumn.toList) + val sourceColumn2 = Columns.fromNames("secondColumn:String", "firstColumn:String") + val newVPColumns = new ViewPortColumns(sourceColumn2.toList) + + assert(oldVPColumns == newVPColumns) + } + + Scenario("when one if the column has different type equality check should return false") { + val sourceColumn = Columns.fromNames("firstColumn:String", "secondColumn:String") + val oldVPColumns = new ViewPortColumns(sourceColumn.toList) + val sourceColumn2 = Columns.fromNames("firstColumn:String", "secondColumn:Int") + val newVPColumns = new ViewPortColumns(sourceColumn2.toList) + + assert(oldVPColumns != newVPColumns) + } + + Scenario("when one if the column has different name equality check should return false") { + val sourceColumn = Columns.fromNames("firstColumn:String", "secondColumn:String") + val oldVPColumns = new ViewPortColumns(sourceColumn.toList) + val sourceColumn2 = Columns.fromNames("firstColumn:String", "someOtherColumn:String") + val newVPColumns = new ViewPortColumns(sourceColumn2.toList) + + assert(oldVPColumns != newVPColumns) + } + + Scenario("when all the columns not same equality check should return false") { + val sourceColumn = Columns.fromNames("firstColumn:String", "secondColumn:String") + val oldVPColumns = new ViewPortColumns(sourceColumn.toList) + val sourceColumn2 = Columns.fromNames("firstColumn:String") + val newVPColumns = new ViewPortColumns(sourceColumn2.toList) + + assert(oldVPColumns != newVPColumns) + } + } +} From c57daf3550a679b219d9b242dbc8d7e3a9ff967a Mon Sep 17 00:00:00 2001 From: Na Lee Ha Date: Wed, 6 Dec 2023 09:31:04 +0000 Subject: [PATCH 19/21] #1010 correcting name of RPC response message --- .../main/scala/org/finos/vuu/viewport/ViewPortAction.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortAction.scala b/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortAction.scala index a91273908..3575beab6 100644 --- a/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortAction.scala +++ b/vuu/src/main/scala/org/finos/vuu/viewport/ViewPortAction.scala @@ -25,7 +25,7 @@ case class CloseDialogViewPortAction(vpId: String) extends ViewPortAction new Type(value = classOf[NoAction], name = "NO_ACTION"), new Type(value = classOf[ViewPortEditSuccess], name = "VP_EDIT_SUCCESS"), new Type(value = classOf[ViewPortEditFailure], name = "VP_EDIT_FAILURE"), - new Type(value = classOf[ViewPortRpcSuccess], name = "VP_RCP_SUCCESS"), - new Type(value = classOf[ViewPortRpcFailure], name = "VP_RCP_FAILURE"), + new Type(value = classOf[ViewPortRpcSuccess], name = "VP_RPC_SUCCESS"), + new Type(value = classOf[ViewPortRpcFailure], name = "VP_RPC_FAILURE"), )) trait ViewPortActionMixin {} From f6de41628eae9beb4c9a216f68e86fd4b537e6b4 Mon Sep 17 00:00:00 2001 From: Na Lee Ha Date: Wed, 6 Dec 2023 17:14:54 +0000 Subject: [PATCH 20/21] #980 adding description to the new constituent on basket trading --- .../BasketTradingConstituentJoinService.scala | 23 +++++++++++++++---- .../basket/BasketMutateOffMarketTest.scala | 8 +++---- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala index 66a33d5b3..41d113792 100644 --- a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala +++ b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingConstituentJoinService.scala @@ -5,9 +5,9 @@ import org.finos.toolbox.time.Clock import org.finos.vuu.api.JoinTableDef import org.finos.vuu.core.module.basket.BasketConstants.Side import org.finos.vuu.core.module.basket.BasketModule -import org.finos.vuu.core.module.basket.BasketModule.{BasketTradingColumnNames => BTColumnName, BasketTradingConstituentColumnNames => ColumnName} +import org.finos.vuu.core.module.basket.BasketModule.{BasketConstituentColumnNames => BCColumnName, BasketTradingColumnNames => BTColumnName, BasketTradingConstituentColumnNames => ColumnName} import org.finos.vuu.core.module.basket.result.ErrorReason -import org.finos.vuu.core.table.{DataTable, JoinTable, RowWithData, TableContainer} +import org.finos.vuu.core.table.{DataTable, JoinTable, RowData, RowWithData, TableContainer} import org.finos.vuu.net.rpc.{EditRpcHandler, RpcHandler} import org.finos.vuu.net.{ClientSessionId, RequestContext} import org.finos.vuu.viewport._ @@ -64,11 +64,18 @@ class BasketTradingConstituentJoinService(val table: DataTable, val tableContain val basketId = tradeRow.get(BTColumnName.BasketId).asInstanceOf[String] val tradeUnit = tradeRow.get(BTColumnName.Units).asInstanceOf[Int] + val basketConstituentRows = getConstituentsWith(ric) //todo what to do when multiple result? + val description = + if(basketConstituentRows.nonEmpty) + basketConstituentRows.head.get(BCColumnName.Description).asInstanceOf[String] + else "" + val newRow = mkTradingConstituentRow( basketTradeInstanceId = tradeId, sourceBasketId = basketId, tradeUnit = tradeUnit, - ric = ric) + ric = ric, + description = description) //todo should we guard against adding row for ric that already exist? updateJoinTable(Array(newRow)) match { @@ -79,6 +86,12 @@ class BasketTradingConstituentJoinService(val table: DataTable, val tableContain } } + private def getConstituentsWith(ric: String): List[RowData] = { + val table = tableContainer.getTable(BasketModule.BasketConstituentTable) + val keys = table.primaryKeys.toList + keys.map(key => table.pullRow(key)).filter(_.get(BCColumnName.Ric).toString == ric) + } + private def onEditCell(key: String, columnName: String, data: Any, vp: ViewPort, session: ClientSessionId): ViewPortEditAction = { try { getBaseTable() match { @@ -180,7 +193,7 @@ class BasketTradingConstituentJoinService(val table: DataTable, val tableContain } } - private def mkTradingConstituentRow(basketTradeInstanceId: String, sourceBasketId: String, tradeUnit: Int, ric: String): RowWithData = { + private def mkTradingConstituentRow(basketTradeInstanceId: String, sourceBasketId: String, tradeUnit: Int, ric: String, description: String): RowWithData = { val constituentKey = s"$basketTradeInstanceId.$ric" val weighting: Double = 0.1 RowWithData( @@ -191,7 +204,7 @@ class BasketTradingConstituentJoinService(val table: DataTable, val tableContain ColumnName.InstanceId -> basketTradeInstanceId, ColumnName.InstanceIdRic -> constituentKey, ColumnName.Quantity -> (weighting * 100).asInstanceOf[Long], - ColumnName.Description -> "", //todo look up description from instrument table + ColumnName.Description -> description, ColumnName.Side -> Side.Buy, ColumnName.Weighting -> weighting, ColumnName.PriceStrategyId -> 2, diff --git a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala index 6ede3b3ef..f788d16e3 100644 --- a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala +++ b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala @@ -186,7 +186,7 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { } } - //TODO oin table cannot be tested currently as it doesnt get updated when underlying table gets updated + //TODO join table cannot be tested currently as it doesnt get updated when underlying table gets updated // Scenario("Adding new constituents by ric should add it to basket trading") { // import BasketModule._ // implicit val clock: Clock = new TestFriendlyClock(10001L) @@ -217,11 +217,9 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { // Then("get all the updates that have occurred for all view ports from the outbound queue") // val updates = combineQs(vpBasketTradingConsJoin) // +// //todo should basketid be where the stock was sourced from? in this case .HSI? // -// //todo map description -// //todo should basketid be where the stock was sourced from? in this case .HSI? -// -// And("assert the basket trading constituent table has not changed sides") +// And("assert the basket trading constituent table has added row") // assertVpEq(filterByVp(vpBasketTradingCons, updates)) { // Table( // ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId", "filledQty", "orderStatus"), From 71cf56103f1dcd87f3fe4c5133fcaf4d8aa28f2d Mon Sep 17 00:00:00 2001 From: Na Lee Ha Date: Thu, 7 Dec 2023 13:42:45 +0000 Subject: [PATCH 21/21] #1059 removed the redundant sides constants and made buy and sell all upper case everywhere --- .../core/module/basket/BasketConstants.scala | 2 +- .../vuu/core/module/basket/BasketModule.scala | 5 -- .../module/basket/service/BasketService.scala | 5 +- .../basket/service/BasketTradingService.scala | 7 +-- .../basket/BasketConstituentMutateTest.scala | 10 ++-- .../core/module/basket/BasketCreateTest.scala | 2 +- .../basket/BasketMutateOffMarketTest.scala | 54 +++++++++---------- .../basket/BasketSendToMarketTest.scala | 10 ++-- .../module/basket/BasketTestCaseHelper.scala | 6 +-- .../provider/ParentChildOrdersModel.scala | 2 +- .../org/finos/vuu/order/oms/OmsApiTest.scala | 6 +-- 11 files changed, 53 insertions(+), 56 deletions(-) diff --git a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/BasketConstants.scala b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/BasketConstants.scala index 6de498283..8e8baf046 100644 --- a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/BasketConstants.scala +++ b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/BasketConstants.scala @@ -2,7 +2,7 @@ package org.finos.vuu.core.module.basket object BasketConstants { - object Side{ + object Side { final val Buy = "BUY" final val Sell = "SELL" } diff --git a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/BasketModule.scala b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/BasketModule.scala index 70a5683f9..8630bcb8e 100644 --- a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/BasketModule.scala +++ b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/BasketModule.scala @@ -202,9 +202,4 @@ object BasketModule extends DefaultModule { final val OrderStatus = "orderStatus" final val FilledQty = "filledQty" } - - object Sides{ - final val Buy = "Buy" - final val Sell = "Sell" - } } diff --git a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketService.scala b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketService.scala index b720d80ad..991aa248d 100644 --- a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketService.scala +++ b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketService.scala @@ -2,8 +2,9 @@ package org.finos.vuu.core.module.basket.service import com.typesafe.scalalogging.StrictLogging import org.finos.toolbox.time.Clock +import org.finos.vuu.core.module.basket.BasketConstants.Side import org.finos.vuu.core.module.basket.BasketModule -import org.finos.vuu.core.module.basket.BasketModule.{BasketConstituentTable, Sides} +import org.finos.vuu.core.module.basket.BasketModule.{BasketConstituentTable} import org.finos.vuu.core.table.{DataTable, RowData, RowWithData, TableContainer} import org.finos.vuu.net.rpc.RpcHandler import org.finos.vuu.net.{ClientSessionId, RequestContext} @@ -57,7 +58,7 @@ class BasketService(val table: DataTable, val tableContainer: TableContainer, va } private def mkTradingBasketRow(sourceBasketId: String, basketTradeName: String, basketTradeInstanceId: String) = { - RowWithData(basketTradeInstanceId, Map(BT.InstanceId -> basketTradeInstanceId, BT.Status -> "OFF-MARKET", BT.BasketId -> sourceBasketId, BT.BasketName -> basketTradeName, BT.Side -> Sides.Buy, BT.Units -> 1)) + RowWithData(basketTradeInstanceId, Map(BT.InstanceId -> basketTradeInstanceId, BT.Status -> "OFF-MARKET", BT.BasketId -> sourceBasketId, BT.BasketName -> basketTradeName, BT.Side -> Side.Buy, BT.Units -> 1)) } def createBasketFromRpc(basketId: String, name: String)(ctx: RequestContext): ViewPortAction = { diff --git a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingService.scala b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingService.scala index 29470b65d..8a805e9bc 100644 --- a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingService.scala +++ b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/service/BasketTradingService.scala @@ -3,7 +3,8 @@ package org.finos.vuu.core.module.basket.service import com.typesafe.scalalogging.StrictLogging import org.finos.toolbox.time.Clock import org.finos.vuu.core.module.basket.BasketModule -import org.finos.vuu.core.module.basket.BasketModule.{BasketTradingConstituentTable, Sides} +import org.finos.vuu.core.module.basket.BasketConstants.Side +import org.finos.vuu.core.module.basket.BasketModule.{BasketTradingConstituentTable} import org.finos.vuu.core.table.{DataTable, RowData, RowWithData, TableContainer, ViewPortColumnCreator} import org.finos.vuu.net.rpc.{EditRpcHandler, RpcHandler} import org.finos.vuu.net.{ClientSessionId, RequestContext} @@ -110,8 +111,8 @@ class BasketTradingService(val table: DataTable, val tableContainer: TableContai val constituents = constituentTable.primaryKeys.map(key => constituentTable.pullRow(key)).filter(_.get(BTC.InstanceId) == key) constituents.foreach(row => { val newSide = row.get(BTC.Side) match { - case Sides.Buy => Sides.Sell - case _ => Sides.Buy + case Side.Buy => Side.Sell + case _ => Side.Buy } constituentTable.processUpdate(row.key(), RowWithData(row.key(), Map(BTC.InstanceIdRic -> row.key(), BTC.Side -> newSide)), clock.now()) }) diff --git a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketConstituentMutateTest.scala b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketConstituentMutateTest.scala index 6fe045421..e3493813b 100644 --- a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketConstituentMutateTest.scala +++ b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketConstituentMutateTest.scala @@ -33,9 +33,9 @@ class BasketConstituentMutateTest extends VuuServerTestCase { vuuServer.login("testUser", "testToken") val basketName = "test_basket1" - val tradeId = GivenBasketTrade(vuuServer.tableContainer, basketName, "Buy") + val tradeId = GivenBasketTrade(vuuServer.tableContainer, basketName, "BUY") GivenPrices(vuuServer.tableContainer, List(("VOD.L", 1.1, 1.4), ("BP.L", 2.1, 2.4))) - GivenBasketTradeConstituentsJoin(vuuServer.tableContainer, tradeId, Map(("VOD.L" -> "Buy"), ("BP.L" -> "Sell"))) + GivenBasketTradeConstituentsJoin(vuuServer.tableContainer, tradeId, Map(("VOD.L" -> "BUY"), ("BP.L" -> "SELL"))) val vpBasketTrading = vuuServer.createViewPort(BasketModule.NAME, BasketTradingTable) val vpBasketTradingConsJoin = vuuServer.createViewPort(BasketModule.NAME, BasketTradingConstituentJoin) @@ -60,9 +60,9 @@ class BasketConstituentMutateTest extends VuuServerTestCase { assertVpEq(filterByVp(vpBasketTradingConsJoin, updates)) { Table( ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId", "bid", "ask", "filledQty", "orderStatus"), - (10L, "Buy", "testUser-00001", "testUser-00001.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 2.1, 2.4, 0, "PENDING"), - (10L, "Sell", "testUser-00001", "testUser-00001.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, null, null, 0, "PENDING"), - (10L, "Sell", "testUser-00001", "testUser-00001.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 1.1, 1.4, 0, "PENDING") + (10L, "BUY", "testUser-00001", "testUser-00001.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 2.1, 2.4, 0, "PENDING"), + (10L, "SELL", "testUser-00001", "testUser-00001.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, null, null, 0, "PENDING"), + (10L, "SELL", "testUser-00001", "testUser-00001.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 1.1, 1.4, 0, "PENDING") ) } } diff --git a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketCreateTest.scala b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketCreateTest.scala index f847d55e6..9451fa78f 100644 --- a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketCreateTest.scala +++ b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketCreateTest.scala @@ -68,7 +68,7 @@ class BasketCreateTest extends VuuServerTestCase { assertVpEq(combineQsForVp(viewportBasketTrading)) { Table( ("basketId", "instanceId", "basketName", "units", "status", "filledPct", "totalNotionalUsd", "totalNotional", "fxRateToUsd", "side"), - (".FTSE", BasketTradeId.current, "TestBasket", 100, "OFF-MARKET", null, null, null, null, "Buy") + (".FTSE", BasketTradeId.current, "TestBasket", 100, "OFF-MARKET", null, null, null, null, "BUY") ) } } diff --git a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala index f788d16e3..c60cb88d1 100644 --- a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala +++ b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketMutateOffMarketTest.scala @@ -60,9 +60,9 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { assertVpEq(combineQsForVp(vpConstituent)) { Table( ("ricBasketId", "ric", "basketId", "weighting", "lastTrade", "change", "volume", "description", "side"), - ("BP.L.FTSE", "BP.L", ".FTSE", 0.1, null, null, null, "Beyond Petroleum", "Buy"), - ("BT.L.FTSE", "BT.L", ".FTSE", 0.1, null, null, null, "British Telecom", "Sell"), - ("VOD.L.FTSE", "VOD.L", ".FTSE", 0.1, null, null, null, "Vodafone", "Buy") + ("BP.L.FTSE", "BP.L", ".FTSE", 0.1, null, null, null, "Beyond Petroleum", "BUY"), + ("BT.L.FTSE", "BT.L", ".FTSE", 0.1, null, null, null, "British Telecom", "SELL"), + ("VOD.L.FTSE", "VOD.L", ".FTSE", 0.1, null, null, null, "Vodafone", "BUY") ) } @@ -80,7 +80,7 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { assertVpEq(combineQsForVp(vpBasketTrading)) { Table( ("instanceId", "basketId", "basketName", "status", "units", "filledPct", "fxRateToUsd", "totalNotional", "totalNotionalUsd", "side"), - (basketTradeInstanceId, ".FTSE", "MyCustomBasket", "OFF-MARKET", 1, null, null, null, null, "Buy") + (basketTradeInstanceId, ".FTSE", "MyCustomBasket", "OFF-MARKET", 1, null, null, null, null, "BUY") ) } @@ -91,16 +91,16 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { assertVpEq(combineQsForVp(vpBasketTradingCons)) { Table( ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId", "filledQty", "orderStatus"), - (10L, "Buy", basketTradeInstanceId, s"$basketTradeInstanceId.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), - (10L, "Sell", basketTradeInstanceId, s"$basketTradeInstanceId.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), - (10L, "Buy", basketTradeInstanceId, s"$basketTradeInstanceId.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") + (10L, "BUY", basketTradeInstanceId, s"$basketTradeInstanceId.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (10L, "SELL", basketTradeInstanceId, s"$basketTradeInstanceId.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (10L, "BUY", basketTradeInstanceId, s"$basketTradeInstanceId.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") ) } val basketTradingService = vuuServer.getViewPortRpcServiceProxy[BasketTradingServiceIF](vpBasketTrading) When("we edit the side of the parent basket") - basketTradingService.editCellAction().func(basketTradeInstanceId, "side", "Sell", vpBasketTrading, vuuServer.session) + basketTradingService.editCellAction().func(basketTradeInstanceId, "side", "SELL", vpBasketTrading, vuuServer.session) Then("get all the updates that have occurred for all view ports from the outbound queue") val updates = combineQs(vpBasketTrading) @@ -109,7 +109,7 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { assertVpEq(filterByVp(vpBasketTrading, updates)) { Table( ("instanceId", "basketId", "basketName", "status", "units", "filledPct", "fxRateToUsd", "totalNotional", "totalNotionalUsd", "side"), - (basketTradeInstanceId, ".FTSE", "MyCustomBasket", "OFF-MARKET", 1, null, null, null, null, "Sell") + (basketTradeInstanceId, ".FTSE", "MyCustomBasket", "OFF-MARKET", 1, null, null, null, null, "SELL") ) } @@ -118,9 +118,9 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { assertVpEq(filterByVp(vpBasketTradingCons, updates)) { Table( ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId", "filledQty", "orderStatus"), - (10L, "Sell", basketTradeInstanceId, s"$basketTradeInstanceId.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), - (10L, "Buy", basketTradeInstanceId, s"$basketTradeInstanceId.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), - (10L, "Sell", basketTradeInstanceId, s"$basketTradeInstanceId.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") + (10L, "SELL", basketTradeInstanceId, s"$basketTradeInstanceId.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (10L, "BUY", basketTradeInstanceId, s"$basketTradeInstanceId.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (10L, "SELL", basketTradeInstanceId, s"$basketTradeInstanceId.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") ) } @@ -131,9 +131,9 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { assertVpEq(filterByVp(vpBasketTradingCons, combineQs(vpBasketTrading))) { Table( ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId", "filledQty", "orderStatus"), - (100L, "Sell", basketTradeInstanceId, s"$basketTradeInstanceId.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), - (100L, "Buy", basketTradeInstanceId, s"$basketTradeInstanceId.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), - (100L, "Sell", basketTradeInstanceId, s"$basketTradeInstanceId.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") + (100L, "SELL", basketTradeInstanceId, s"$basketTradeInstanceId.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (100L, "BUY", basketTradeInstanceId, s"$basketTradeInstanceId.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (100L, "SELL", basketTradeInstanceId, s"$basketTradeInstanceId.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") ) } } @@ -160,7 +160,7 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { val basketTradingService = vuuServer.getViewPortRpcServiceProxy[BasketTradingServiceIF](vpBasketTrading) When("we edit the side of the parent basket to same side as current value") - basketTradingService.editCellAction().func(basketTradeInstanceId, "side", "Buy", vpBasketTrading, vuuServer.session) + basketTradingService.editCellAction().func(basketTradeInstanceId, "side", "BUY", vpBasketTrading, vuuServer.session) vuuServer.runOnce() Then("get all the updates that have occurred for all view ports from the outbound queue") @@ -170,7 +170,7 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { assertVpEq(filterByVp(vpBasketTrading, updates2)) { Table( ("instanceId", "basketId", "basketName", "status", "units", "filledPct", "fxRateToUsd", "totalNotional", "totalNotionalUsd", "side"), - (basketTradeInstanceId, ".FTSE", "MyCustomBasket", "OFF-MARKET", 1, null, null, null, null, "Buy") + (basketTradeInstanceId, ".FTSE", "MyCustomBasket", "OFF-MARKET", 1, null, null, null, null, "BUY") ) } @@ -178,9 +178,9 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { assertVpEq(filterByVp(vpBasketTradingCons, updates2)) { Table( ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId", "filledQty", "orderStatus"), - (10L, "Buy", basketTradeInstanceId, s"$basketTradeInstanceId.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), - (10L, "Sell", basketTradeInstanceId, s"$basketTradeInstanceId.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), - (10L, "Buy", basketTradeInstanceId, s"$basketTradeInstanceId.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") + (10L, "BUY", basketTradeInstanceId, s"$basketTradeInstanceId.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (10L, "SELL", basketTradeInstanceId, s"$basketTradeInstanceId.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (10L, "BUY", basketTradeInstanceId, s"$basketTradeInstanceId.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") ) } } @@ -223,10 +223,10 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { // assertVpEq(filterByVp(vpBasketTradingCons, updates)) { // Table( // ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId", "filledQty", "orderStatus"), -// (10L, "Buy", basketTradeInstanceId, s"$basketTradeInstanceId.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), -// (10L, "Sell", basketTradeInstanceId, s"$basketTradeInstanceId.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), -// (10L, "Buy", basketTradeInstanceId, s"$basketTradeInstanceId.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), -// (10L, "Buy", basketTradeInstanceId, s"$basketTradeInstanceId.0001.HK", ".FTSE", "0001.HK", "", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") +// (10L, "BUY", basketTradeInstanceId, s"$basketTradeInstanceId.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), +// (10L, "SELL", basketTradeInstanceId, s"$basketTradeInstanceId.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), +// (10L, "BUY", basketTradeInstanceId, s"$basketTradeInstanceId.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), +// (10L, "BUY", basketTradeInstanceId, s"$basketTradeInstanceId.0001.HK", ".FTSE", "0001.HK", "", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") // ) // } // } @@ -238,9 +238,9 @@ class BasketMutateOffMarketTest extends VuuServerTestCase { basketProvider.tick(".FTSE", Map(B.Id -> ".FTSE", B.Name -> ".FTSE 100", B.NotionalValue -> 1000001, B.NotionalValueUsd -> 1500001)) val constituentProvider = vuuServer.getProvider(BasketModule.NAME, BasketModule.BasketConstituentTable) - constituentProvider.tick("VOD.L.FTSE", Map(BC.RicBasketId -> "VOD.L.FTSE", BC.Ric -> "VOD.L", BC.BasketId -> basketId, BC.Weighting -> 0.1, BC.Side -> "Buy", BC.Description -> "Vodafone")) - constituentProvider.tick("BT.L.FTSE", Map(BC.RicBasketId -> "BT.L.FTSE", BC.Ric -> "BT.L", BC.BasketId -> basketId, BC.Weighting -> 0.1, BC.Side -> "Sell", BC.Description -> "British Telecom")) - constituentProvider.tick("BP.L.FTSE", Map(BC.RicBasketId -> "BP.L.FTSE", BC.Ric -> "BP.L", BC.BasketId -> basketId, BC.Weighting -> 0.1, BC.Side -> "Buy", BC.Description -> "Beyond Petroleum")) + constituentProvider.tick("VOD.L.FTSE", Map(BC.RicBasketId -> "VOD.L.FTSE", BC.Ric -> "VOD.L", BC.BasketId -> basketId, BC.Weighting -> 0.1, BC.Side -> "BUY", BC.Description -> "Vodafone")) + constituentProvider.tick("BT.L.FTSE", Map(BC.RicBasketId -> "BT.L.FTSE", BC.Ric -> "BT.L", BC.BasketId -> basketId, BC.Weighting -> 0.1, BC.Side -> "SELL", BC.Description -> "British Telecom")) + constituentProvider.tick("BP.L.FTSE", Map(BC.RicBasketId -> "BP.L.FTSE", BC.Ric -> "BP.L", BC.BasketId -> basketId, BC.Weighting -> 0.1, BC.Side -> "BUY", BC.Description -> "Beyond Petroleum")) val vpBasket = vuuServer.createViewPort(BasketModule.NAME, BasketModule.BasketTable) val basketService = vuuServer.getViewPortRpcServiceProxy[BasketServiceIF](vpBasket) diff --git a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketSendToMarketTest.scala b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketSendToMarketTest.scala index 8f0abcf56..7bc7511ea 100644 --- a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketSendToMarketTest.scala +++ b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketSendToMarketTest.scala @@ -62,9 +62,9 @@ class BasketSendToMarketTest extends VuuServerTestCase { assertVpEq(combineQsForVp(vpBasketTradingCons)) { Table( ("quantity", "side", "instanceId", "instanceIdRic", "basketId", "ric", "description", "notionalUsd", "notionalLocal", "venue", "algo", "algoParams", "pctFilled", "weighting", "priceSpread", "limitPrice", "priceStrategyId", "filledQty", "orderStatus"), - (10L, "Buy", basketTradeInstanceId, s"$basketTradeInstanceId.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), - (10L, "Sell", basketTradeInstanceId, s"$basketTradeInstanceId.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), - (10L, "Buy", basketTradeInstanceId, s"$basketTradeInstanceId.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") + (10L, "BUY", basketTradeInstanceId, s"$basketTradeInstanceId.BP.L", ".FTSE", "BP.L", "Beyond Petroleum", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (10L, "SELL", basketTradeInstanceId, s"$basketTradeInstanceId.BT.L", ".FTSE", "BT.L", "British Telecom", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING"), + (10L, "BUY", basketTradeInstanceId, s"$basketTradeInstanceId.VOD.L", ".FTSE", "VOD.L", "Vodafone", null, null, null, -1, null, null, 0.1, null, null, 2, 0, "PENDING") ) } @@ -79,7 +79,7 @@ class BasketSendToMarketTest extends VuuServerTestCase { assertVpEq(combineQsForVp(vpBasketTrading)) { Table( ("instanceId", "basketId", "basketName", "status", "units", "filledPct", "fxRateToUsd", "totalNotional", "totalNotionalUsd", "side"), - (basketTradeInstanceId, ".FTSE", "TestBasket", "ON_MARKET", 1, null, null, null, null, "Buy") + (basketTradeInstanceId, ".FTSE", "TestBasket", "ON_MARKET", 1, null, null, null, null, "BUY") ) } @@ -92,7 +92,7 @@ class BasketSendToMarketTest extends VuuServerTestCase { assertVpEq(combineQsForVp(vpBasketTrading)) { Table( ("instanceId", "basketId", "basketName", "status", "units", "filledPct", "fxRateToUsd", "totalNotional", "totalNotionalUsd", "side"), - (basketTradeInstanceId, ".FTSE", "TestBasket", "OFF_MARKET", 1, null, null, null, null, "Buy") + (basketTradeInstanceId, ".FTSE", "TestBasket", "OFF_MARKET", 1, null, null, null, null, "BUY") ) } } diff --git a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketTestCaseHelper.scala b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketTestCaseHelper.scala index 540cb1d2d..657af5767 100644 --- a/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketTestCaseHelper.scala +++ b/example/basket/src/test/scala/org/finos/vuu/core/module/basket/BasketTestCaseHelper.scala @@ -20,8 +20,8 @@ object BasketTestCaseHelper { //symbol + "." + basketId //Columns.fromNames(BC.RicBasketId.string(), BC.Ric.string(), BC.BasketId.string(), BC.Weighting.double(), BC.LastTrade.string(), BC.Change.string(), // BC.Volume.string(), BC.Side.string()) - provider.tick("VOD.L.FTSE", Map(BC.RicBasketId -> "VOD.L.FTSE", BC.Ric -> "VOD.L", BC.BasketId -> ".FTSE", BC.Weighting -> 0.1, BC.Side -> "Buy", BC.Description -> "Vodafone")) - provider.tick("BT.L.FTSE", Map(BC.RicBasketId -> "BT.L.FTSE", BC.Ric -> "BT.L", BC.BasketId -> ".FTSE", BC.Weighting -> 0.1, BC.Side -> "Sell", BC.Description -> "British Telecom")) - provider.tick("BP.L.FTSE", Map(BC.RicBasketId -> "BP.L.FTSE", BC.Ric -> "BP.L", BC.BasketId -> ".FTSE", BC.Weighting -> 0.1, BC.Side -> "Buy", BC.Description -> "Beyond Petroleum")) + provider.tick("VOD.L.FTSE", Map(BC.RicBasketId -> "VOD.L.FTSE", BC.Ric -> "VOD.L", BC.BasketId -> ".FTSE", BC.Weighting -> 0.1, BC.Side -> "BUY", BC.Description -> "Vodafone")) + provider.tick("BT.L.FTSE", Map(BC.RicBasketId -> "BT.L.FTSE", BC.Ric -> "BT.L", BC.BasketId -> ".FTSE", BC.Weighting -> 0.1, BC.Side -> "SELL", BC.Description -> "British Telecom")) + provider.tick("BP.L.FTSE", Map(BC.RicBasketId -> "BP.L.FTSE", BC.Ric -> "BP.L", BC.BasketId -> ".FTSE", BC.Weighting -> 0.1, BC.Side -> "BUY", BC.Description -> "Beyond Petroleum")) } } diff --git a/example/order/src/main/scala/org/finos/vuu/core/module/simul/provider/ParentChildOrdersModel.scala b/example/order/src/main/scala/org/finos/vuu/core/module/simul/provider/ParentChildOrdersModel.scala index 5d08aee37..9b0be7bbf 100644 --- a/example/order/src/main/scala/org/finos/vuu/core/module/simul/provider/ParentChildOrdersModel.scala +++ b/example/order/src/main/scala/org/finos/vuu/core/module/simul/provider/ParentChildOrdersModel.scala @@ -291,7 +291,7 @@ class ParentChildOrdersModel(implicit clock: Clock, lifecycleContainer: Lifecycl val instIdx = randomNumbers.seededRand(0, instruments.length - 1) val instrument = instruments(instIdx) val quantity = randomNumbers.seededRand(0, 30) * 100 + randomNumbers.seededRand(0, 100) - val side = if (randomNumbers.seededRand(0, 10) > 8) "Buy" else "Sell" + val side = if (randomNumbers.seededRand(0, 10) > 8) "BUY" else "SELL" val account = accounts(randomNumbers.seededRand(0, accounts.length - 1)) val algo = algos(randomNumbers.seededRand(0, algos.length - 1)) val volLimit = randomNumbers.seededRand(0, 10) * 10 diff --git a/example/order/src/test/scala/org/finos/vuu/order/oms/OmsApiTest.scala b/example/order/src/test/scala/org/finos/vuu/order/oms/OmsApiTest.scala index 164bb1686..eb2ed2454 100644 --- a/example/order/src/test/scala/org/finos/vuu/order/oms/OmsApiTest.scala +++ b/example/order/src/test/scala/org/finos/vuu/order/oms/OmsApiTest.scala @@ -62,7 +62,7 @@ class OmsApiTest extends AnyFeatureSpec with GivenWhenThen with Matchers { omsApi.addListener(listener) - omsApi.createOrder(NewOrder("Buy","VOD.L", 1000L, 100.01, "clOrdId1")) + omsApi.createOrder(NewOrder("BUY","VOD.L", 1000L, 100.01, "clOrdId1")) clock.sleep(MAX_ACK_TIME_MS) omsApi.runOnce() @@ -95,8 +95,8 @@ class OmsApiTest extends AnyFeatureSpec with GivenWhenThen with Matchers { omsApi.addListener(listener) - omsApi.createOrder(NewOrder("Buy", "VOD.L", 1000L, 100.01, "clOrdId1")) - omsApi.createOrder(NewOrder("Buy", "BP.L", 1000L, 150.01, "clOrdId2")) + omsApi.createOrder(NewOrder("BUY", "VOD.L", 1000L, 100.01, "clOrdId1")) + omsApi.createOrder(NewOrder("BUY", "BP.L", 1000L, 150.01, "clOrdId2")) clock.sleep(MAX_ACK_TIME_MS) omsApi.runOnce()