Skip to content

Commit

Permalink
Showcase baskets (finos#1047)
Browse files Browse the repository at this point in the history
* build index on tables

* enhancements to ContextPanel, InstrumentSearch

* add proce generation logic from Vuu

* handle constituent drop in basket edit table

* connect takeOffMarket
  • Loading branch information
heswell authored Dec 5, 2023
1 parent 69509a4 commit 70fdd01
Show file tree
Hide file tree
Showing 38 changed files with 3,120 additions and 309 deletions.
2 changes: 1 addition & 1 deletion vuu-ui/.vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@
"typescript.tsdk": "node_modules/typescript/lib",
"css.customData": [
"./.vscode/custom-css.json"
]
]
}
39 changes: 34 additions & 5 deletions vuu-ui/packages/vuu-data-test/src/Table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export class Table extends EventEmitter<TableEvents> {
#data: VuuRowDataItemType[][];
#dataMap: ColumnMap;
#indexOfKey: number;
#index = new Map<string, number>();
#schema: TableSchema;

constructor(
Expand All @@ -26,8 +27,16 @@ export class Table extends EventEmitter<TableEvents> {
this.#dataMap = dataMap;
this.#schema = schema;
this.#indexOfKey = dataMap[schema.key];
this.buildIndex();
updateGenerator?.setTable(this);
updateGenerator?.setRange({ from: 0, to: 20 });
updateGenerator?.setRange({ from: 0, to: 100 });
}

private buildIndex() {
for (let i = 0; i < this.#data.length; i++) {
const key = this.#data[i][this.#indexOfKey] as string;
this.#index.set(key, i);
}
}

get data() {
Expand All @@ -43,12 +52,16 @@ export class Table extends EventEmitter<TableEvents> {
}

insert(row: VuuRowDataItemType[]) {
const index = this.#data.length;
this.#data.push(row);
const key = row[this.#indexOfKey] as string;
this.#index.set(key, index);
this.emit("insert", row);
}

findByKey(key: string) {
return this.#data.find((d) => (d[this.#indexOfKey] = key));
const index = this.#index.get(key) ?? -1;
return this.#data[index];
}

update(key: string, columnName: string, value: VuuRowDataItemType) {
Expand Down Expand Up @@ -134,7 +147,7 @@ export const joinTables = (

const data: VuuRowDataItemType[][] = [];
const combinedColumnMap = buildDataColumnMap(combinedSchema);
const start = performance.now();
// const start = performance.now();
for (const row of table1.data) {
const row2 = table2.findByKey(String(row[k1]));
if (row2) {
Expand All @@ -151,11 +164,27 @@ export const joinTables = (
data.push(out);
}
}
const end = performance.now();
console.log(`took ${end - start} ms to create join table ${joinTable.table}`);
// const end = performance.now();
// console.log(`took ${end - start} ms to create join table ${joinTable.table}`);

const newTable = new Table(combinedSchema, data, combinedColumnMap);

table1.on("insert", (row) => {
const row2 = table2.findByKey(String(row[k1]));
if (row2) {
const out = [];
for (const column of table1.schema.columns) {
const value = row[m1[column.name]];
out[combinedColumnMap[column.name]] = value;
}
for (const column of table2.schema.columns) {
const value = row2[m2[column.name]];
out[combinedColumnMap[column.name]] = value;
}
newTable.insert(out);
}
});

table2.on("update", (row) => {
const keyValue = row[k2] as string;
const targetRow = newTable.findByKey(keyValue);
Expand Down
2 changes: 2 additions & 0 deletions vuu-ui/packages/vuu-data-test/src/TickingArrayDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
ArrayDataSource,
ArrayDataSourceConstructorProps,
MenuRpcResponse,
RpcResponse,
SubscribeCallback,
SubscribeProps,
VuuUIMessageInRPCEditReject,
Expand Down Expand Up @@ -104,6 +105,7 @@ export class TickingArrayDataSource extends ArrayDataSource {
columnName: string,
value: VuuRowDataItemType
): Promise<true> {
console.log(`applyEdit ${columnName} ${value}`);
const key = row[metadataKeys.KEY];
this.#table?.update(key, columnName, value);
return Promise.resolve(true);
Expand Down
31 changes: 26 additions & 5 deletions vuu-ui/packages/vuu-data-test/src/UpdateGenerator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { VuuRange } from "@finos/vuu-protocol-types";
import { random } from "./simul/reference-data";
import { generateNextBidAsk, nextRandomDouble, random } from "./data-utils";
import type { UpdateGenerator } from "./rowUpdates";
import { Table } from "./Table";

Expand All @@ -14,9 +14,9 @@ export class BaseUpdateGenerator implements UpdateGenerator {
private range: VuuRange | undefined;
private updating = false;
private timer: number | undefined;
private tickingColumns: number[];
private tickingColumns: { [key: string]: number };

constructor(tickingColumns: number[]) {
constructor(tickingColumns: { [key: string]: number }) {
this.tickingColumns = tickingColumns;
}

Expand Down Expand Up @@ -47,6 +47,7 @@ export class BaseUpdateGenerator implements UpdateGenerator {
update = () => {
if (this.range && this.table) {
const data = this.table?.data;
const { bid, ask, last, ...rest } = this.tickingColumns;
if (data && data?.length > 0) {
const maxRange = Math.min(this.range.to, data.length);
for (let rowIndex = this.range.from; rowIndex < maxRange; rowIndex++) {
Expand All @@ -57,7 +58,27 @@ export class BaseUpdateGenerator implements UpdateGenerator {
// @ts-ignore
const rowUpdates = this.table.data[rowIndex];
const row = data[rowIndex];
for (const colIdx of this.tickingColumns) {

if (bid !== undefined && ask !== undefined) {
const { [bid]: currentBid, [ask]: currentAsk } = row as number[];
const [newBid, newAsk] = generateNextBidAsk(
currentBid,
currentAsk,
10,
5,
nextRandomDouble
);
rowUpdates[ask] = newAsk;
rowUpdates[bid] = newBid;
if (last !== undefined) {
const newLast =
Math.round((currentAsk + (newAsk - currentAsk) / 2) * 100) /
100.0;
rowUpdates[last] = newLast;
}
}

for (const colIdx of Object.values(rest)) {
const shallUpdateColumn = random(0, 10) < 5;
if (shallUpdateColumn) {
updateCount += 1;
Expand All @@ -76,7 +97,7 @@ export class BaseUpdateGenerator implements UpdateGenerator {
}

if (this.updating) {
this.timer = window.setTimeout(this.update, 200);
this.timer = window.setTimeout(this.update, 500);
}
};
}
144 changes: 40 additions & 104 deletions vuu-ui/packages/vuu-data-test/src/basket/basket-module.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import { VuuModule } from "../vuu-modules";
import { ColumnMap, metadataKeys } from "@finos/vuu-utils";
import { BasketsTableName } from "./basket-schemas";
import { TickingArrayDataSource } from "../TickingArrayDataSource";
import { schemas } from "./basket-schemas";
import ftse from "./reference-data/ftse100";
import nasdaq from "./reference-data/nasdaq100";
import sp500 from "./reference-data/sp500";
import hsi from "./reference-data/hsi";
import {
ClientToServerViewportRpcCall,
VuuMenu,
VuuRowDataItemType,
} from "@finos/vuu-protocol-types";
import { Table } from "../Table";
import { ColumnMap, metadataKeys } from "@finos/vuu-utils";
import pricesTable from "./reference-data/prices";
import { joinTables, Table } from "../Table";
import { TickingArrayDataSource } from "../TickingArrayDataSource";
import { VuuModule } from "../vuu-modules";
import { BasketsTableName, schemas } from "./basket-schemas";
import basketConstituentData from "./reference-data/constituents";

// This is a 'local' columnMap
const buildDataColumnMap = (tableName: BasketsTableName) =>
Expand All @@ -36,56 +33,12 @@ const tableMaps: Record<BasketsTableName, ColumnMap> = {
priceStrategyType: buildDataColumnMap("priceStrategyType"),
};

//---------------

const { KEY } = metadataKeys;

/**
* BasketConstituent
*/

const basketConstituentData = [];
for (const row of ftse) {
// prettier-ignore
const [ric, name, lastTrade, change, volume] = row;
const basketId = ".FTSE100";
const side = "BUY";
const weighting = 1;
// prettier-ignore
basketConstituentData.push([ basketId, change, name, lastTrade, ric, `${ric}-${basketId}`, side, volume, weighting ]);
}

for (const row of hsi) {
// prettier-ignore
const [name, ric, lastTrade, change, , volume] = row;
const basketId = ".HSI";
const side = "BUY";
const weighting = 1;
// prettier-ignore
basketConstituentData.push([basketId,change,name, lastTrade,ric,`${ric}-${basketId}`,side,volume,weighting ]);
}

for (const row of nasdaq) {
// prettier-ignore
const [name, ric, weighting, lastTrade, change] = row;
const basketId = ".NASDAQ100";
const side = "BUY";
const volume = 1000;
// prettier-ignore
basketConstituentData.push([ basketId, change, name, lastTrade, ric, `${ric}-${basketId}`, side, volume, weighting ]);
}

for (const row of sp500) {
// prettier-ignore
const [name, ric, weighting,,change] = row;
const basketId = ".SP500";
const side = "BUY";
const volume = 1000;
const lastTrade = 0;
// prettier-ignore
basketConstituentData.push([ basketId, change, name, lastTrade, ric, `${ric}-${basketId}`, side, volume, weighting ]);
}

const basketConstituent = new Table(
schemas.basketConstituent,
basketConstituentData,
Expand All @@ -110,11 +63,6 @@ const basketTradingConstituent = new Table(
[],
tableMaps.basketTradingConstituent
);
const basketTradingConstituentJoin = new Table(
schemas.basketTradingConstituentJoin,
[],
tableMaps.basketTradingConstituentJoin
);

// export as convenience for showcase examples
export const createBasketTradingRow = (
Expand Down Expand Up @@ -179,51 +127,20 @@ function createTradingBasket(basketId: string, basketName: string) {
weighting,
];
basketTradingConstituent.insert(basketTradingConstituentRow);

const ask = 0;
const askSize = 0;
const bid = 0;
const bidSize = 0;
const close = 0;
const last = 0;
const open = 0;
const phase = "market";
const scenario = "scenario";
const status = "on market";

const basketTradingConstituentJoinRow = [
algo,
algoParams,
ask,
askSize,
basketId,
bid,
bidSize,
close,
description,
basketInstanceId,
`${basketInstanceId}-${ric}`,
last,
limitPrice,
notionalLocal,
notionalUsd,
open,
pctFilled,
phase,
priceSpread,
priceStrategyId,
quantity,
ric,
scenario,
side,
status,
venue,
weighting,
];
basketTradingConstituentJoin.insert(basketTradingConstituentJoinRow);
});
}

async function addConstituent(rpcRequest: ClientToServerViewportRpcCall) {
console.log(`RPC call erceived ${rpcRequest.rpcName}`);
}
async function sendToMarket(rpcRequest: ClientToServerViewportRpcCall) {
const [basketInstanceId] = rpcRequest.params;
console.log(`RPC call erceived ${rpcRequest.rpcName} ${basketInstanceId}`);
}
async function takeOffMarket(rpcRequest: ClientToServerViewportRpcCall) {
console.log(`RPC call erceived ${rpcRequest.rpcName}`);
}

async function createNewBasket(rpcRequest: ClientToServerViewportRpcCall) {
const {
params: [basketId, basketName],
Expand Down Expand Up @@ -258,7 +175,12 @@ export const tables: Record<BasketsTableName, Table> = {
basketConstituent,
basketTrading,
basketTradingConstituent,
basketTradingConstituentJoin,
basketTradingConstituentJoin: joinTables(
{ module: "BASKET", table: "basketTradingConstituentJoin" },
basketTradingConstituent,
pricesTable,
"ric"
),
priceStrategyType: new Table(
schemas.priceStrategyType,
[
Expand Down Expand Up @@ -305,9 +227,23 @@ const services: Record<BasketsTableName, RpcService[] | undefined> = {
},
],
basketConstituent: undefined,
basketTrading: undefined,
basketTrading: [
{
rpcName: "sendToMarket",
service: sendToMarket,
},
{
rpcName: "takeOffMarket",
service: takeOffMarket,
},
],
basketTradingConstituent: undefined,
basketTradingConstituentJoin: undefined,
basketTradingConstituentJoin: [
{
rpcName: "addConstituent",
service: addConstituent,
},
],
priceStrategyType: undefined,
};

Expand Down
Loading

0 comments on commit 70fdd01

Please sign in to comment.