Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Approvedpush 20231124 basketdemofixes #1044

Merged
merged 12 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package org.finos.vuu.core.module.basket.result

case class ErrorReason(reason: String)
Original file line number Diff line number Diff line change
Expand Up @@ -3,97 +3,100 @@ 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.service.BasketService.counter
import org.finos.vuu.core.module.basket.BasketModule.{BasketConstituentTable, Sides}
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

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{
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 {

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 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)
val instanceKey = BasketTradeId.oneNew(session.user)

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 = BasketTradeId.oneNew(ctx.session.user)
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.")
}

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.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,108 @@ 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
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
import org.finos.vuu.net.rpc.{EditRpcHandler, RpcHandler}
import org.finos.vuu.net.{ClientSessionId, RequestContext}
import org.finos.vuu.viewport._

class BasketTradingConstituentJoinService(val table: DataTable, val tableContainer: TableContainer)(implicit clock: Clock) extends RpcHandler with EditRpcHandler with StrictLogging {
import scala.util.control.NonFatal

def onDeleteRow(key: String, vp: ViewPort, session: ClientSessionId): ViewPortEditAction = {
ViewPortEditSuccess()

trait BasketTradingConstituentJoinServiceIF extends EditRpcHandler {
def setSell(selection: ViewPortSelection, session: ClientSessionId): ViewPortAction

def setBuy(selection: ViewPortSelection, session: ClientSessionId): ViewPortAction

def addConstituent(ric: String)(ctx: RequestContext): ViewPortAction
}

class BasketTradingConstituentJoinService(val table: DataTable, val tableContainer: TableContainer)(implicit clock: Clock) extends BasketTradingConstituentJoinServiceIF with RpcHandler with StrictLogging {

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)

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))
}

def onDeleteCell(key: String, column: String, vp: ViewPort, session: ClientSessionId): ViewPortEditAction = {
ViewPortEditSuccess()
def setBuy(selection: ViewPortSelection, session: ClientSessionId): ViewPortAction = {
updateSelected(selection, Map(ColumnName.Side -> Side.Buy))
}

def onAddRow(key: String, data: Map[String, Any], vp: ViewPort, session: ClientSessionId): ViewPortEditAction = {
ViewPortEditSuccess()
//this is RCP call and method name is part of contract with UI
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 tradeRow = tableContainer.getTable(BasketModule.BasketTradingTable).pullRow(tradeId)
val basketId = tradeRow.get(BTColumnName.BasketId).asInstanceOf[String]
val tradeUnit = tradeRow.get(BTColumnName.Units).asInstanceOf[Int]

val newRow = mkTradingConstituentRow(
basketTradeInstanceId = tradeId,
sourceBasketId = basketId,
tradeUnit = tradeUnit,
ric = ric)

//todo should we guard against adding row for ric that already exist?
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 = {
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(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]
}
}

Expand All @@ -41,52 +114,89 @@ 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
// 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)
}

private def onFormClose(vp: ViewPort, session: ClientSessionId): ViewPortAction = {
CloseDialogViewPortAction(vp.id)
}

private def getBaseTable(): Option[DataTable] = {
val joinTable = table.asTable.asInstanceOf[JoinTable]
val baseTableDef = joinTable.getTableDef.asInstanceOf[JoinTableDef].baseTable
joinTable.sourceTables.get(baseTableDef.name)
}

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 = {
ViewPortEditSuccess()
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())
)
Right()
case None =>
Left(ErrorReason(s"Could not find base table for ${table.name}"))
}
}

def setBuy(selection: ViewPortSelection, session: ClientSessionId): ViewPortAction = {
ViewPortEditSuccess()
private def updateSelected(selection: ViewPortSelection, updates: Map[String, Any]): ViewPortAction = {
val selectedKeys = selection.rowKeyIndex.map({ case (key, _) => key }).toList
val updateRows = selectedKeys.map(key => {
//require source table primary key for join table updates
val sourceTableKey = Map(ColumnName.InstanceIdRic -> key)
RowWithData(key, sourceTableKey ++ updates)
})

updateJoinTable(updateRows.toArray) match {
case Right(_) => NoAction()
case Left(errorReason) =>
logger.info(s"Could not update selection values${errorReason.reason}")
NoAction()
}
}

override def menuItems(): ViewPortMenu = ViewPortMenu("Direction",
new SelectionViewPortMenuItem("Set Sell", "", this.setSell, "SET_SELECTION_SELL"),
new SelectionViewPortMenuItem("Set Buy", "", this.setBuy, "SET_SELECTION_Buy")
)
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,
))
}

}
Loading
Loading