Skip to content

Commit

Permalink
Issue 981 send to market for basket trades fake oms (#986)
Browse files Browse the repository at this point in the history
* #981 Added first cut of the OmsApi into example code.

* #981 Added first cut of the OmsApi into example code.

* #981 Added first test of send basket to market

* #981 Added first cut of the OmsApi into example code.

* #981 Added small fixes for basket service

* #981 Added first working version of the happy acker

* #981 fixed bug in Happy Acker with totalFileldQty.

* #981 Added better fill model logic

* #981 disabled semgrep warning, as demo is deliberately predictable on random
  • Loading branch information
chrisjstevo authored Nov 23, 2023
1 parent 1740ebb commit 220c08e
Show file tree
Hide file tree
Showing 17 changed files with 520 additions and 85 deletions.
1 change: 1 addition & 0 deletions .semgrepignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ vuu-ui/showcase/scripts/build.mjs
vuu-ui/showcase/scripts/parse-stories.mjs
vuu-ui/showcase/scripts/utils.mjs
vuu-ui/scripts/utils.mjs
example/order/src/main/scala/org/finos/vuu/order/oms/impl/InMemOmsApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import org.finos.vuu.core.module.basket.service.{BasketService, BasketTradingCon
import org.finos.vuu.core.module.price.PriceModule
import org.finos.vuu.core.module.{DefaultModule, ModuleFactory, TableDefContainer, ViewServerModule}
import org.finos.vuu.core.table.Columns
import org.finos.vuu.order.oms.OmsApi

object BasketModule extends DefaultModule {

Expand All @@ -19,7 +20,7 @@ object BasketModule extends DefaultModule {
final val BasketTradingConstituentTable = "basketTradingConstituent"
final val BasketTradingConstituentJoin = "basketTradingConstituentJoin"

def apply()(implicit clock: Clock, lifecycle: LifecycleContainer, tableDefContainer: TableDefContainer): ViewServerModule = {
def apply(omsApi: OmsApi)(implicit clock: Clock, lifecycle: LifecycleContainer, tableDefContainer: TableDefContainer): ViewServerModule = {

import org.finos.vuu.core.module.basket.BasketModule.{BasketColumnNames => B, BasketConstituentColumnNames => BC, BasketTradingColumnNames => BT, BasketTradingConstituentColumnNames => BTC, PriceStrategy => PS}

Expand All @@ -36,15 +37,16 @@ object BasketModule extends DefaultModule {
(table, _) => new BasketProvider(table),
(table, _, _, tableContainer) => ViewPortDef(
columns = table.getTableDef.columns,
service = new BasketService(table, tableContainer)
service = new BasketService(table, tableContainer, omsApi)
)
)
.addTable(
TableDef(
name = BasketConstituentTable,
keyField = BC.RicBasketId,
columns = 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(), BC.Description.string()), // we can join to instruments and other tables to get the rest of the data.....
columns = 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(), BC.Description.string()), // we can join to instruments and other tables to get the rest of the data.....
VisualLinks(),
joinFields = BC.RicBasketId, BC.Ric
),
Expand All @@ -54,14 +56,17 @@ object BasketModule extends DefaultModule {
TableDef(
name = BasketTradingTable,
keyField = BT.InstanceId,
columns = Columns.fromNames(BT.InstanceId.string(), BT.BasketId.string(), BT.BasketName.string(), BT.Status.string(), BT.Units.int(), BT.FilledPct.double(), BT.FxRateToUsd.double(), BT.TotalNotional.double(), BT.TotalNotionalUsd.double(), BT.Side.string()), // we can join to instruments and other tables to get the rest of the data.....
columns = Columns.fromNames(BT.InstanceId.string(), BT.BasketId.string(), BT.BasketName.string(),
BT.Status.string(), BT.Units.int(), BT.FilledPct.double(), BT.FxRateToUsd.double(),
BT.TotalNotional.double(), BT.TotalNotionalUsd.double(), BT.Side.string()
), // we can join to instruments and other tables to get the rest of the data.....
VisualLinks(),
joinFields = BT.BasketId
),
(table, vs) => new BasketTradingProvider(table, vs.tableContainer),
(table, _, _, tableContainer) => ViewPortDef(
columns = table.getTableDef.columns,
service = new BasketTradingService(table, tableContainer)
service = new BasketTradingService(table, tableContainer, omsApi)
)
)
.addTable(
Expand All @@ -77,14 +82,16 @@ object BasketModule extends DefaultModule {
BTC.Algo.string(), BTC.AlgoParams.string(),
BTC.PctFilled.double(), BTC.Weighting.double(),
BTC.PriceSpread.int(),
BTC.LimitPrice.double()
BTC.LimitPrice.double(),
BTC.FilledQty.long(),
BTC.OrderStatus.string()
),// we can join to instruments and other tables to get the rest of the data.....
VisualLinks(
Link(BTC.InstanceId, BasketTradingTable, BT.InstanceId),
),
joinFields = BTC.InstanceIdRic, BTC.Ric
),
(table, vs) => new NullProvider(table),
(table, vs) => new BasketTradingConstituentProvider(table, omsApi),
(table, _, _, tableContainer) => ViewPortDef(
columns = table.getTableDef.columns,
service = new BasketTradingConstituentService(table, tableContainer)
Expand Down Expand Up @@ -192,6 +199,8 @@ object BasketModule extends DefaultModule {
final val PctFilled = "pctFilled"
final val Weighting = "weighting"
final val PriceSpread = "priceSpread"
final val OrderStatus = "orderStatus"
final val FilledQty = "filledQty"
}

object Sides{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.finos.vuu.core.module.basket.provider

import org.finos.toolbox.lifecycle.LifecycleContainer
import org.finos.toolbox.thread.LifeCycleRunner
import org.finos.toolbox.time.Clock
import org.finos.vuu.core.module.basket.service.OrderStates
import org.finos.vuu.core.table.{DataTable, RowWithData}
import org.finos.vuu.order.oms._
import org.finos.vuu.provider.DefaultProvider

class BasketTradingConstituentProvider(val table: DataTable, val omsApi: OmsApi)(implicit lifecycle: LifecycleContainer, clock: Clock) extends DefaultProvider {

private val runner = new LifeCycleRunner("TradingConsProviderRunner", runOnce, 50L)

import org.finos.vuu.core.module.basket.BasketModule.{BasketTradingConstituentColumnNames => BTC}

omsApi.addListener(new OmsListener {
override def onAck(ack: Ack): Unit = {
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 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())
}
})

def runOnce(): Unit = {
omsApi.runOnce()
}


override val lifecycleId: String = "org.finos.vuu.core.module.basket.provider.BasketTradingConstituentProvider#" + hashCode()
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ 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.{ClientSessionId, RequestContext}
import org.finos.vuu.order.oms.{NewOrder, OmsApi}
import org.finos.vuu.viewport._

import java.util.concurrent.atomic.AtomicInteger
Expand All @@ -20,9 +21,7 @@ trait BasketServiceIF{
def createBasket(basketKey: String, name: String)(ctx: RequestContext): ViewPortAction
}

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

//private val counter = new AtomicInteger(0)
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}

Expand All @@ -49,6 +48,8 @@ class BasketService(val table: DataTable, val tableContainer: TableContainer)(im
BTC.Weighting -> weighting,
BTC.PriceStrategyId -> 2,
BTC.Algo -> -1,
BTC.OrderStatus -> OrderStates.PENDING,
BTC.FilledQty -> 0
))
}

Expand All @@ -73,6 +74,7 @@ class BasketService(val table: DataTable, val tableContainer: TableContainer)(im
createBasketInternal(basketKey, name, ctx.session)
}


private def createBasketInternal(basketKey: String, name: String, sessionId: ClientSessionId): ViewPortAction = {

val constituents = getConstituentsForBasketKey(basketKey)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import org.finos.vuu.core.module.basket.BasketModule.BasketTradingConstituentCol
import org.finos.vuu.core.table.{DataTable, RowWithData, TableContainer}
import org.finos.vuu.net.ClientSessionId
import org.finos.vuu.net.rpc.{EditRpcHandler, RpcHandler}
import org.finos.vuu.order.oms.OmsApi
import org.finos.vuu.viewport._

class BasketTradingConstituentService(val table: DataTable, val tableContainer: TableContainer)(implicit clock: Clock) extends RpcHandler with EditRpcHandler with StrictLogging {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,56 @@ 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.table.{DataTable, RowWithData, TableContainer}
import org.finos.vuu.net.ClientSessionId
import org.finos.vuu.net.rpc.{EditRpcHandler, RpcHandler}
import org.finos.vuu.viewport.{ViewPort, ViewPortAddRowAction, ViewPortDeleteCellAction, ViewPortDeleteRowAction, ViewPortEditAction, ViewPortEditCellAction, ViewPortEditRowAction, ViewPortEditSuccess, ViewPortFormCloseAction, ViewPortFormSubmitAction}

trait BasketTradingServiceIF extends EditRpcHandler{
import org.finos.vuu.net.{ClientSessionId, RequestContext}
import org.finos.vuu.order.oms.{NewOrder, OmsApi}
import org.finos.vuu.viewport._

trait BasketTradingServiceIF extends EditRpcHandler {
def sendToMarket(basketInstanceId: String)(ctx: RequestContext): ViewPortAction
}

class BasketTradingService(val table: DataTable, val tableContainer: TableContainer)(implicit clock: Clock) extends RpcHandler with BasketTradingServiceIF with StrictLogging {
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
*/
override def sendToMarket(name: String)(ctx: RequestContext): ViewPortAction = {
val tableRow = table.asTable.pullRow(name)

logger.info("Sending basket to market:" + name + " (row:" + tableRow + ")")

val tradingConsTable = tableContainer.getTable(BasketModule.BasketTradingConstituentTable)

val constituents = tradingConsTable.primaryKeys.toList
.map(tradingConsTable.pullRow)
.filter(_.get(BTC.InstanceId) == name)

constituents.foreach(constituentRow => {

val quantity = constituentRow.get(BTC.Quantity).asInstanceOf[Long]
val symbol = constituentRow.get(BTC.Ric).toString
val price = constituentRow.get(BTC.LimitPrice).asInstanceOf[Double]
val instanceIdRic = constituentRow.get(BTC.InstanceIdRic).toString
val side = constituentRow.get(BTC.Side).toString

val nos = NewOrder(side, symbol, quantity, price, instanceIdRic)

import org.finos.vuu.core.module.basket.BasketModule.{BasketConstituentColumnNames => BC, BasketTradingColumnNames => BT, BasketTradingConstituentColumnNames => BTC}
logger.info(s"Sending constituent to market $nos")

omsApi.createOrder(nos)
})

table.processUpdate(name, RowWithData(name,
Map(BT.InstanceId -> name, BT.Status -> BasketStates.ON_MARKET)), clock.now())

ViewPortEditSuccess()
}

private def onEditCell(key: String, columnName: String, data: Any, vp: ViewPort, session: ClientSessionId): ViewPortEditAction = {
logger.info("Changing cell value for key:" + key + "(" + columnName + ":" + data + ")")
Expand All @@ -24,11 +61,11 @@ class BasketTradingService(val table: DataTable, val tableContainer: TableContai
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())
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)
Expand All @@ -46,18 +83,21 @@ class BasketTradingService(val table: DataTable, val tableContainer: TableContai
}

ViewPortEditSuccess()
}
}

override def deleteRowAction(): ViewPortDeleteRowAction = ???

override def deleteCellAction(): ViewPortDeleteCellAction = ???

override def addRowAction(): ViewPortAddRowAction = ???

override def editCellAction(): ViewPortEditCellAction = ViewPortEditCellAction("", onEditCell)

override def editRowAction(): ViewPortEditRowAction = ???

override def onFormSubmit(): ViewPortFormSubmitAction = ???

override def onFormClose(): ViewPortFormCloseAction = ???


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.finos.vuu.core.module.basket.service

object OrderStates {
final val PENDING = "PENDING"
final val ACKED = "ACKED"
final val CANCELLED = "CANCELLED"
final val FILLED = "FILLED"
}

object BasketStates{
final val OFF_MARKET = "OFF_MARKET"
final val ON_MARKET = "ON_MARKET"
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import org.finos.vuu.core.module.TableDefContainer
import org.finos.vuu.core.module.basket.service.{BasketServiceIF, BasketTradingServiceIF}
import org.finos.vuu.core.module.price.PriceModule
import org.finos.vuu.core.table.TableTestHelper.combineQs
import org.finos.vuu.order.oms.OmsApi
import org.finos.vuu.test.VuuServerTestCase
import org.finos.vuu.util.table.TableAsserts.assertVpEq
import org.scalatest.prop.Tables.Table
Expand All @@ -23,9 +24,11 @@ class BasketCreateTest extends VuuServerTestCase {
implicit val tableDefContainer: TableDefContainer = new TableDefContainer(Map())
implicit val metricsProvider: MetricsProvider = new MetricsProviderImpl

val omsApi = OmsApi()

import BasketModule.{BasketTradingColumnNames => BT, _}

withVuuServer(PriceModule(), BasketModule()) {
withVuuServer(PriceModule(), BasketModule(omsApi)) {
vuuServer =>

vuuServer.login("testUser", "testToken")
Expand Down
Loading

0 comments on commit 220c08e

Please sign in to comment.