@vuu-ui/vuu-data-test 4.0.0-alpha.21 → 4.0.0-alpha.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +36 -0
  2. package/package.json +7 -7
  3. package/src/basket/BasketModule.js +105 -40
  4. package/src/basket/basket-schemas.js +16 -0
  5. package/src/basket/reference-data/constituents.js +8 -76
  6. package/src/basket/reference-data/prices.js +9 -3
  7. package/src/core/table/TableContainer.js +51 -8
  8. package/types/basket/BasketModule.d.ts +2 -0
  9. package/types/basket/reference-data/constituents.d.ts +3 -3
  10. package/types/basket/reference-data/prices.d.ts +2 -1
  11. package/types/core/table/TableContainer.d.ts +1 -1
  12. package/src/basket/reference-data/ftse100.js +0 -686
  13. package/src/basket/reference-data/hsi.js +0 -1047
  14. package/src/basket/reference-data/nasdaq100.js +0 -812
  15. package/src/basket/reference-data/sp500.js +0 -315
  16. package/types/src/ArrayProxy.d.ts +0 -8
  17. package/types/src/RuntimeVisualLink.d.ts +0 -9
  18. package/types/src/Table.d.ts +0 -34
  19. package/types/src/TickingArrayDataSource.d.ts +0 -44
  20. package/types/src/UpdateGenerator.d.ts +0 -18
  21. package/types/src/basket/BasketModule.d.ts +0 -55
  22. package/types/src/basket/basket-schemas.d.ts +0 -9
  23. package/types/src/basket/index.d.ts +0 -2
  24. package/types/src/basket/reference-data/constituents.d.ts +0 -3
  25. package/types/src/basket/reference-data/ftse100.d.ts +0 -2
  26. package/types/src/basket/reference-data/hsi.d.ts +0 -2
  27. package/types/src/basket/reference-data/nasdaq100.d.ts +0 -2
  28. package/types/src/basket/reference-data/prices.d.ts +0 -24
  29. package/types/src/basket/reference-data/sp500.d.ts +0 -2
  30. package/types/src/core/module/ModuleContainer.d.ts +0 -11
  31. package/types/src/core/module/VuuModule.d.ts +0 -81
  32. package/types/src/core/table/TableContainer.d.ts +0 -16
  33. package/types/src/data-utils.d.ts +0 -7
  34. package/types/src/default-column-definitions.d.ts +0 -2
  35. package/types/src/index.d.ts +0 -12
  36. package/types/src/local-datasource-provider/LocalDatasourceProvider.d.ts +0 -4
  37. package/types/src/makeSuggestions.d.ts +0 -3
  38. package/types/src/rowUpdates.d.ts +0 -15
  39. package/types/src/schemas.d.ts +0 -8
  40. package/types/src/session-table-utils.d.ts +0 -6
  41. package/types/src/simul/SimulModule.d.ts +0 -24
  42. package/types/src/simul/index.d.ts +0 -2
  43. package/types/src/simul/reference-data/accounts.d.ts +0 -1
  44. package/types/src/simul/reference-data/algos.d.ts +0 -1
  45. package/types/src/simul/reference-data/currencies.d.ts +0 -1
  46. package/types/src/simul/reference-data/index.d.ts +0 -5
  47. package/types/src/simul/reference-data/instrument-prices.d.ts +0 -60
  48. package/types/src/simul/reference-data/instruments-extended.d.ts +0 -1
  49. package/types/src/simul/reference-data/instruments.d.ts +0 -35
  50. package/types/src/simul/reference-data/isin-generator/convert-string-to-base-ten-number.d.ts +0 -1
  51. package/types/src/simul/reference-data/isin-generator/fixtures/alphabet.d.ts +0 -1
  52. package/types/src/simul/reference-data/isin-generator/fixtures/alphanumeric-values.d.ts +0 -1
  53. package/types/src/simul/reference-data/isin-generator/fixtures/base-ten-numbers.d.ts +0 -1
  54. package/types/src/simul/reference-data/isin-generator/generate-cusip-check-code.d.ts +0 -1
  55. package/types/src/simul/reference-data/isin-generator/generate-cusip-without-check-code.d.ts +0 -1
  56. package/types/src/simul/reference-data/isin-generator/generate-cusip.d.ts +0 -1
  57. package/types/src/simul/reference-data/isin-generator/generate-random-alphanumeric.d.ts +0 -1
  58. package/types/src/simul/reference-data/isin-generator/generate-random-base-ten-number.d.ts +0 -1
  59. package/types/src/simul/reference-data/isin-generator/generate-random-string-of-alphanumeric-chars.d.ts +0 -1
  60. package/types/src/simul/reference-data/isin-generator/generate-random-string-of-base-ten-chars.d.ts +0 -1
  61. package/types/src/simul/reference-data/isin-generator/index.d.ts +0 -1
  62. package/types/src/simul/reference-data/isin-generator/is-odd.d.ts +0 -1
  63. package/types/src/simul/reference-data/locations.d.ts +0 -4
  64. package/types/src/simul/reference-data/lotsizes.d.ts +0 -1
  65. package/types/src/simul/reference-data/orderStatus.d.ts +0 -1
  66. package/types/src/simul/reference-data/orders.d.ts +0 -36
  67. package/types/src/simul/reference-data/parent-child-orders.d.ts +0 -4
  68. package/types/src/simul/reference-data/priceStrategies.d.ts +0 -1
  69. package/types/src/simul/reference-data/prices.d.ts +0 -34
  70. package/types/src/simul/reference-data/sides.d.ts +0 -1
  71. package/types/src/simul/simul-schemas.d.ts +0 -9
  72. package/types/src/test/TestModule.d.ts +0 -16
  73. package/types/src/test/index.d.ts +0 -1
  74. package/types/src/vuu-row-generator.d.ts +0 -17
package/README.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # `@vuu-ui/vuu-data-test`
2
2
 
3
+ ## Local basket trading
4
+
5
+ Constituent reference data is generated from the server's four CSV files in
6
+ `example/main/src/main/resources/static/`. From `vuu-ui`, run
7
+ `npm run generate:basket-data` after editing those files and commit the resulting
8
+ `src/basket/reference-data/constituents.json`. Use
9
+ `npm run generate:basket-data -- --check` to verify it is up to date.
10
+ The converter preserves source weights (without normalizing their units),
11
+ maps the differing price/change column names, and rejects invalid CSV records.
12
+ The browser loads only the generated JSON, not the CSV parser.
13
+
14
+ The `BASKET` module provides its own simulated constituent prices, including
15
+ updates for all basket instruments. Its constituent join retains
16
+ `instanceIdRic` as the row key, so multiple trading baskets can contain the
17
+ same instrument. Direct edits through the join update the underlying
18
+ constituent table; subsequent price ticks preserve those edits.
19
+ Changing the basket side flips each constituent's current side, preserving
20
+ mixed BUY/SELL positions, as on the Scala server. Unchanged side edits do
21
+ nothing, and other basket instances are unaffected.
22
+ Editing basket units recalculates its constituent quantities as
23
+ `weighting * units`, truncated to whole units, matching the Scala service.
24
+
25
+ New baskets retain the `basketTradeName` supplied by the creation dialog.
26
+ Constituents default to the Limit price strategy (`priceStrategyId = 2`),
27
+ matching the Scala server.
28
+ Dropping an instrument from the Add constituent context panel adds it to the
29
+ requested basket instance. Manually added constituents start with weight `0.1`,
30
+ BUY side and Limit strategy; quantity is calculated from the current basket
31
+ units. Unknown instruments, duplicates, missing baskets and on-market baskets
32
+ are rejected without changing existing rows.
33
+ The Add constituent list excludes RICs already in the active basket using a
34
+ separate full-membership subscription, including rows outside the visible grid.
35
+ Sending a basket to market and taking it off market update its constituent
36
+ statuses; live rows include `orderStatus` and an initially zero `filledQty`.
37
+ Execution fills are not simulated.
38
+
3
39
  ## Admin test module dependencies
4
40
 
5
41
  The `USER_ADMIN` and `MODULE_DISCOVERY` test modules use the published
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.0.0-alpha.21",
2
+ "version": "4.0.0-alpha.23",
3
3
  "main": "./src/index.js",
4
4
  "type": "module",
5
5
  "author": "heswell",
@@ -12,14 +12,14 @@
12
12
  "dependencies": {
13
13
  "@heswell/module-admin": "1.1.0",
14
14
  "@heswell/user-admin": "1.0.1",
15
- "@vuu-ui/vuu-data-editing": "4.0.0-alpha.21",
16
- "@vuu-ui/vuu-data-local": "4.0.0-alpha.21",
17
- "@vuu-ui/vuu-utils": "4.0.0-alpha.21"
15
+ "@vuu-ui/vuu-data-editing": "4.0.0-alpha.23",
16
+ "@vuu-ui/vuu-data-local": "4.0.0-alpha.23",
17
+ "@vuu-ui/vuu-utils": "4.0.0-alpha.23"
18
18
  },
19
19
  "devDependencies": {
20
- "@vuu-ui/vuu-data-types": "4.0.0-alpha.21",
21
- "@vuu-ui/vuu-protocol-types": "4.0.0-alpha.21",
22
- "@vuu-ui/vuu-table-types": "4.0.0-alpha.21"
20
+ "@vuu-ui/vuu-protocol-types": "4.0.0-alpha.23",
21
+ "@vuu-ui/vuu-data-types": "4.0.0-alpha.23",
22
+ "@vuu-ui/vuu-table-types": "4.0.0-alpha.23"
23
23
  },
24
24
  "sideEffects": false,
25
25
  "files": [
@@ -2,6 +2,7 @@ import { VuuModule } from "../core/module/VuuModule.js";
2
2
  import TableContainer from "../core/table/TableContainer.js";
3
3
  import { schemas } from "./basket-schemas.js";
4
4
  import reference_data_constituents from "./reference-data/constituents.js";
5
+ import prices from "./reference-data/prices.js";
5
6
  const { createTable: createTable } = TableContainer;
6
7
  let basketIncrement = 1;
7
8
  const createBasketTradingRow = (basketId, basketName, side = "BUY", status = "OFF MARKET")=>[
@@ -98,10 +99,7 @@ class BasketModule extends VuuModule {
98
99
  }, {
99
100
  module: "BASKET",
100
101
  table: "basketTradingConstituent"
101
- }, {
102
- module: "SIMUL",
103
- table: "prices"
104
- }, "ric"),
102
+ }, prices, "ric"),
105
103
  priceStrategyType: createTable(schemas.priceStrategyType, [
106
104
  [
107
105
  "Peg to Near Touch",
@@ -123,6 +121,22 @@ class BasketModule extends VuuModule {
123
121
  };
124
122
  constructor(){
125
123
  super("BASKET");
124
+ const table = this.tables.basketTrading;
125
+ const update = table.update.bind(table);
126
+ table.update = (key, column, value)=>{
127
+ if ("side" !== column && "units" !== column) return update(key, column, value);
128
+ const basket = table.findByKey(key);
129
+ if (!basket) throw Error(`[BasketModule] basket ${key} not found`);
130
+ if (basket[table.map[column]] === value) return;
131
+ if ("units" === column && ("number" != typeof value || !Number.isInteger(value))) throw Error("[BasketModule] units must be an integer");
132
+ update(key, column, value);
133
+ const constituents = this.tables.basketTradingConstituent;
134
+ for (const row of constituents.data)if (row[constituents.map.instanceId] === key) {
135
+ const constituentKey = String(row[constituents.map.instanceIdRic]);
136
+ if ("side" === column) constituents.update(constituentKey, "side", "BUY" === row[constituents.map.side] ? "SELL" : "BUY");
137
+ else if ("number" == typeof value) constituents.update(constituentKey, "quantity", Math.trunc(Number(row[constituents.map.weighting]) * value));
138
+ }
139
+ };
126
140
  }
127
141
  get menus() {
128
142
  return {
@@ -196,45 +210,55 @@ class BasketModule extends VuuModule {
196
210
  const { basketId: key } = buildDataColumnMap("basketConstituent");
197
211
  const constituents = this.tables.basketConstituent.data.filter((c)=>c[key] === basketId);
198
212
  const { instanceId } = tableMaps.basketTrading;
199
- constituents.forEach(([, , description, , ric, , , quantity, weighting])=>{
200
- const algo = "";
201
- const algoParams = "";
202
- const limitPrice = 95;
203
- const notionalLocal = 0;
204
- const notionalUsd = 0;
205
- const pctFilled = 0;
206
- const priceSpread = 0;
207
- const priceStrategyId = "";
208
- const side = "BUY";
209
- const venue = "venue";
210
- const basketInstanceId = basketTradingRow[instanceId];
211
- const basketTradingConstituentRow = [
212
- algo,
213
- algoParams,
214
- basketId,
215
- description,
216
- basketInstanceId,
217
- `${basketInstanceId}-${ric}`,
218
- limitPrice,
219
- notionalLocal,
220
- notionalUsd,
221
- pctFilled,
222
- priceSpread,
223
- priceStrategyId,
224
- quantity,
225
- ric,
226
- side,
227
- venue,
228
- weighting
229
- ];
230
- basketTradingConstituent.insert(basketTradingConstituentRow);
213
+ constituents.forEach(([, , description, , ric, , , , weighting])=>{
214
+ this.insertTradingConstituent(basketTradingRow, String(ric), String(description), Number(weighting));
231
215
  });
232
216
  return basketTradingRow[instanceId];
233
217
  }
218
+ insertTradingConstituent(basketTradingRow, ric, description, weighting) {
219
+ const { basketId, instanceId } = tableMaps.basketTrading;
220
+ const quantity = Math.trunc(Number(weighting) * Number(basketTradingRow[tableMaps.basketTrading.units]));
221
+ const algo = "";
222
+ const algoParams = "";
223
+ const limitPrice = 95;
224
+ const notionalLocal = 0;
225
+ const notionalUsd = 0;
226
+ const pctFilled = 0;
227
+ const priceSpread = 0;
228
+ const priceStrategyId = 2;
229
+ const side = "BUY";
230
+ const status = "OFF-MARKET";
231
+ const venue = "venue";
232
+ const basketInstanceId = basketTradingRow[instanceId];
233
+ const basketTradingConstituentRow = [
234
+ algo,
235
+ algoParams,
236
+ basketTradingRow[basketId],
237
+ description,
238
+ basketInstanceId,
239
+ `${basketInstanceId}-${ric}`,
240
+ limitPrice,
241
+ notionalLocal,
242
+ notionalUsd,
243
+ pctFilled,
244
+ priceSpread,
245
+ priceStrategyId,
246
+ quantity,
247
+ ric,
248
+ side,
249
+ status,
250
+ venue,
251
+ weighting,
252
+ "PENDING",
253
+ 0
254
+ ];
255
+ basketTradingConstituent.insert(basketTradingConstituentRow);
256
+ return `${basketInstanceId}-${ric}`;
257
+ }
234
258
  createNewBasket = async (rpcRequest)=>{
235
259
  if ("VIEWPORT_CONTEXT" === rpcRequest.context.type) {
236
- const { sourceBasketId, tradeBasketName } = rpcRequest.params;
237
- const key = this.createTradingBasket(sourceBasketId, tradeBasketName);
260
+ const { sourceBasketId, basketTradeName } = rpcRequest.params;
261
+ const key = this.createTradingBasket(sourceBasketId, basketTradeName);
238
262
  return {
239
263
  type: "SUCCESS_RESULT",
240
264
  data: key
@@ -246,6 +270,7 @@ class BasketModule extends VuuModule {
246
270
  if ("VIEWPORT_CONTEXT" === rpcRequest.context.type) {
247
271
  const { basketInstanceId } = rpcRequest.params;
248
272
  this.tables.basketTrading.update(basketInstanceId, "status", "ON_MARKET");
273
+ this.updateConstituentStatus(basketInstanceId, "ON_MARKET", "ACKED");
249
274
  return {
250
275
  type: "SUCCESS_RESULT",
251
276
  data: void 0
@@ -257,6 +282,7 @@ class BasketModule extends VuuModule {
257
282
  if ("VIEWPORT_CONTEXT" === rpcRequest.context.type) {
258
283
  const { basketInstanceId } = rpcRequest.params;
259
284
  this.tables.basketTrading.update(basketInstanceId, "status", "OFF-MARKET");
285
+ this.updateConstituentStatus(basketInstanceId, "OFF-MARKET", "CANCELLED");
260
286
  return {
261
287
  type: "SUCCESS_RESULT",
262
288
  data: void 0
@@ -264,9 +290,48 @@ class BasketModule extends VuuModule {
264
290
  }
265
291
  throw Error("[BasketModule] createNewBasket invalid request type");
266
292
  };
267
- addConstituent = async ()=>{
268
- throw Error("addConstituent not implemented");
293
+ addConstituent = async (request)=>{
294
+ const { params } = request;
295
+ const ric = "ric" in params ? params.ric : void 0;
296
+ const basketInstanceId = "basketInstanceId" in params ? params.basketInstanceId : void 0;
297
+ if ("VIEWPORT_CONTEXT" !== request.context.type || "string" != typeof ric || "string" != typeof basketInstanceId) return {
298
+ type: "ERROR_RESULT",
299
+ errorMessage: "Invalid add constituent request"
300
+ };
301
+ const basket = this.tables.basketTrading.findByKey(basketInstanceId);
302
+ if (!basket) return {
303
+ type: "ERROR_RESULT",
304
+ errorMessage: "Basket not found"
305
+ };
306
+ if ("ON_MARKET" === basket[tableMaps.basketTrading.status]) return {
307
+ type: "ERROR_RESULT",
308
+ errorMessage: "Take the basket off market before adding constituents"
309
+ };
310
+ const source = this.tables.basketConstituent;
311
+ const reference = source.data.find((row)=>row[source.map.ric] === ric);
312
+ if (!reference) return {
313
+ type: "ERROR_RESULT",
314
+ errorMessage: `Unknown constituent ${ric}`
315
+ };
316
+ const key = `${basketInstanceId}-${ric}`;
317
+ if (this.tables.basketTradingConstituent.findByKey(key)) return {
318
+ type: "ERROR_RESULT",
319
+ errorMessage: `${ric} is already in this basket`
320
+ };
321
+ this.insertTradingConstituent(basket, ric, String(reference[source.map.description]), 0.1);
322
+ return {
323
+ type: "SUCCESS_RESULT",
324
+ data: key
325
+ };
269
326
  };
327
+ updateConstituentStatus(basketInstanceId, status, orderStatus) {
328
+ const table = this.tables.basketTradingConstituent;
329
+ for (const row of table.data)if (row[table.map.instanceId] === basketInstanceId) {
330
+ const key = String(row[table.map.instanceIdRic]);
331
+ table.update(key, "status", status);
332
+ table.update(key, "orderStatus", orderStatus);
333
+ }
334
+ }
270
335
  }
271
336
  const basketModule = new BasketModule();
272
337
  export { BasketModule, basketModule, createBasketTradingRow };
@@ -216,6 +216,14 @@ const schemas = {
216
216
  {
217
217
  name: "weighting",
218
218
  serverDataType: "double"
219
+ },
220
+ {
221
+ name: "orderStatus",
222
+ serverDataType: "string"
223
+ },
224
+ {
225
+ name: "filledQty",
226
+ serverDataType: "long"
219
227
  }
220
228
  ],
221
229
  key: "instanceIdRic",
@@ -334,6 +342,14 @@ const schemas = {
334
342
  {
335
343
  name: "weighting",
336
344
  serverDataType: "double"
345
+ },
346
+ {
347
+ name: "orderStatus",
348
+ serverDataType: "string"
349
+ },
350
+ {
351
+ name: "filledQty",
352
+ serverDataType: "long"
337
353
  }
338
354
  ],
339
355
  key: "instanceIdRic",
@@ -1,76 +1,8 @@
1
- import ftse100 from "./ftse100.js";
2
- import nasdaq100 from "./nasdaq100.js";
3
- import sp500 from "./sp500.js";
4
- import hsi from "./hsi.js";
5
- const basketConstituentData = [];
6
- for (const row of ftse100){
7
- const [ric, name, lastTrade, change, volume] = row;
8
- const basketId = ".FTSE100";
9
- const side = "BUY";
10
- const weighting = 1;
11
- basketConstituentData.push([
12
- basketId,
13
- change,
14
- name,
15
- lastTrade,
16
- ric,
17
- `${ric}-${basketId}`,
18
- side,
19
- volume,
20
- weighting
21
- ]);
22
- }
23
- for (const row of hsi){
24
- const [name, ric, lastTrade, change, , volume] = row;
25
- const basketId = ".HSI";
26
- const side = "BUY";
27
- const weighting = 1;
28
- basketConstituentData.push([
29
- basketId,
30
- change,
31
- name,
32
- lastTrade,
33
- ric,
34
- `${ric}-${basketId}`,
35
- side,
36
- volume,
37
- weighting
38
- ]);
39
- }
40
- for (const row of nasdaq100){
41
- const [name, ric, weighting, lastTrade, change] = row;
42
- const basketId = ".NASDAQ100";
43
- const side = "BUY";
44
- const volume = 1000;
45
- basketConstituentData.push([
46
- basketId,
47
- change,
48
- name,
49
- lastTrade,
50
- ric,
51
- `${ric}-${basketId}`,
52
- side,
53
- volume,
54
- weighting
55
- ]);
56
- }
57
- for (const row of sp500){
58
- const [name, ric, weighting, , change] = row;
59
- const basketId = ".SP500";
60
- const side = "BUY";
61
- const volume = 1000;
62
- const lastTrade = 0;
63
- basketConstituentData.push([
64
- basketId,
65
- change,
66
- name,
67
- lastTrade,
68
- ric,
69
- `${ric}-${basketId}`,
70
- side,
71
- volume,
72
- weighting
73
- ]);
74
- }
75
- var constituents = basketConstituentData;
76
- export default constituents;
1
+ import constituents from "./constituents.js";
2
+ import { schemas } from "../basket-schemas.js";
3
+ const basketConstituentData = constituents.map((constituent)=>{
4
+ const values = constituent;
5
+ return schemas.basketConstituent.columns.map(({ name })=>values[name]);
6
+ });
7
+ var reference_data_constituents = basketConstituentData;
8
+ export default reference_data_constituents;
@@ -1,9 +1,8 @@
1
- import { buildDataColumnMap } from "../../Table.js";
1
+ import { Table, buildDataColumnMap } from "../../Table.js";
2
2
  import { BaseUpdateGenerator } from "../../UpdateGenerator.js";
3
3
  import { schemas } from "../../simul/simul-schemas.js";
4
4
  import constituents from "./constituents.js";
5
5
  import { initBidAsk, nextRandomDouble, random } from "../../data-utils.js";
6
- import TableContainer from "../../core/table/TableContainer.js";
7
6
  const { bid: prices_bid, bidSize: prices_bidSize, ask: prices_ask, askSize: prices_askSize, last: prices_last } = buildDataColumnMap(schemas, "prices");
8
7
  const pricesUpdateGenerator = new BaseUpdateGenerator({
9
8
  bid: prices_bid,
@@ -13,9 +12,12 @@ const pricesUpdateGenerator = new BaseUpdateGenerator({
13
12
  last: prices_last
14
13
  });
15
14
  const prices = [];
15
+ const seen = new Set();
16
16
  for (const [, , , lastTrade, ric] of constituents){
17
+ if (seen.has(String(ric))) continue;
17
18
  const priceSeed = parseFloat(String(lastTrade));
18
19
  if (!isNaN(priceSeed)) {
20
+ seen.add(String(ric));
19
21
  const [bid, ask] = initBidAsk(5, nextRandomDouble);
20
22
  const askSize = random(1000, 3000);
21
23
  const bidSize = random(1000, 3000);
@@ -38,6 +40,10 @@ for (const [, , , lastTrade, ric] of constituents){
38
40
  ]);
39
41
  }
40
42
  }
41
- const pricesTable = TableContainer.createTable(schemas.prices, prices, buildDataColumnMap(schemas, "prices"), pricesUpdateGenerator);
43
+ const pricesTable = new Table(schemas.prices, prices, buildDataColumnMap(schemas, "prices"), pricesUpdateGenerator);
44
+ pricesUpdateGenerator.setRange({
45
+ from: 0,
46
+ to: prices.length
47
+ });
42
48
  var reference_data_prices = pricesTable;
43
49
  export default reference_data_prices;
@@ -15,9 +15,9 @@ class TableContainer {
15
15
  this.addTable(table);
16
16
  return table;
17
17
  };
18
- createJoinTable(joinTable, { table: t1 }, { table: t2 }, joinColumn) {
19
- const table1 = this.getTable(t1);
20
- const table2 = this.getTable(t2);
18
+ createJoinTable(joinTable, source1, source2, joinColumn) {
19
+ const table1 = source1 instanceof Table ? source1 : this.getTable(source1.table);
20
+ const table2 = source2 instanceof Table ? source2 : this.getTable(source2.table);
21
21
  const { map: m1, schema: schema1 } = table1;
22
22
  const { map: m2, schema: schema2 } = table2;
23
23
  const k1 = m1[joinColumn];
@@ -27,7 +27,7 @@ class TableContainer {
27
27
  ...schema2.columns
28
28
  ].map((col)=>col.name).sort());
29
29
  const combinedSchema = {
30
- key: joinColumn,
30
+ key: schema1.key,
31
31
  table: joinTable,
32
32
  rangeLimits: DEFAULT_RANGE_LIMITS,
33
33
  columns: Array.from(combinedColumns).map((columnName)=>({
@@ -53,6 +53,18 @@ class TableContainer {
53
53
  }
54
54
  }
55
55
  const newTable = new Table(combinedSchema, data, combinedColumnMap);
56
+ const joinedKeys = new Map();
57
+ const indexJoinedRow = (row)=>{
58
+ const value = String(row[combinedColumnMap[joinColumn]]);
59
+ const key = String(row[combinedColumnMap[schema1.key]]);
60
+ let keys = joinedKeys.get(value);
61
+ if (!keys) {
62
+ keys = new Set();
63
+ joinedKeys.set(value, keys);
64
+ }
65
+ keys.add(key);
66
+ };
67
+ data.forEach(indexJoinedRow);
56
68
  table1.on("insert", (row)=>{
57
69
  const row2 = table2.findByKey(String(row[k1]));
58
70
  if (row2) {
@@ -65,18 +77,49 @@ class TableContainer {
65
77
  const value = row2[m2[column.name]];
66
78
  out[combinedColumnMap[column.name]] = value;
67
79
  }
80
+ indexJoinedRow(out);
68
81
  newTable.insert(out);
69
82
  }
70
83
  });
71
- table2.on("update", (row)=>{
72
- const keyValue = row[k2];
73
- const targetRow = newTable.findByKey(keyValue);
84
+ table1.on("update", (row)=>{
85
+ const key = String(row[m1[schema1.key]]);
86
+ const targetRow = newTable.findByKey(key);
74
87
  if (targetRow) {
75
88
  const updatedRow = targetRow.slice();
76
- for (const { name } of table2.schema.columns)if (row[m2[name]] !== updatedRow[combinedColumnMap[name]]) updatedRow[combinedColumnMap[name]] = row[m2[name]];
89
+ for (const { name } of schema1.columns)updatedRow[combinedColumnMap[name]] = row[m1[name]];
90
+ newTable.updateRow(updatedRow);
91
+ }
92
+ });
93
+ table1.on("delete", (key)=>{
94
+ const row = newTable.findByKey(key);
95
+ if (row) {
96
+ const value = String(row[combinedColumnMap[joinColumn]]);
97
+ const keys = joinedKeys.get(value);
98
+ keys?.delete(key);
99
+ if (keys?.size === 0) joinedKeys.delete(value);
100
+ newTable.delete(key);
101
+ }
102
+ });
103
+ table2.on("update", (row)=>{
104
+ const keyValue = String(row[k2]);
105
+ for (const key of joinedKeys.get(keyValue) ?? []){
106
+ const targetRow = newTable.findByKey(key);
107
+ if (!targetRow) continue;
108
+ const updatedRow = targetRow.slice();
109
+ for (const { name } of table2.schema.columns)if (!(name in m1)) {
110
+ if (row[m2[name]] !== updatedRow[combinedColumnMap[name]]) updatedRow[combinedColumnMap[name]] = row[m2[name]];
111
+ }
77
112
  newTable.updateRow(updatedRow);
78
113
  }
79
114
  });
115
+ newTable.update = (key, columnName, value)=>{
116
+ const row = newTable.findByKey(key);
117
+ if (!row) throw Error(`[TableContainer] join row ${key} not found`);
118
+ if (columnName === schema1.key || columnName === joinColumn) throw Error(`[TableContainer] cannot edit join key ${columnName}`);
119
+ if (columnName in m1) table1.update(key, columnName, value);
120
+ else if (columnName in m2) table2.update(String(row[combinedColumnMap[schema2.key]]), columnName, value);
121
+ else throw Error(`[TableContainer] unknown join column ${columnName}`);
122
+ };
80
123
  this.addTable(newTable);
81
124
  return newTable;
82
125
  }
@@ -47,9 +47,11 @@ export declare class BasketModule extends VuuModule<BasketsTableName> {
47
47
  }[];
48
48
  };
49
49
  private createTradingBasket;
50
+ private insertTradingConstituent;
50
51
  private createNewBasket;
51
52
  private sendToMarket;
52
53
  private takeOffMarket;
53
54
  private addConstituent;
55
+ private updateConstituentStatus;
54
56
  }
55
57
  export declare const basketModule: BasketModule;
@@ -1,3 +1,3 @@
1
- import { VuuRowDataItemType } from "@vuu-ui/vuu-protocol-types";
2
- declare const _default: VuuRowDataItemType[][];
3
- export default _default;
1
+ import type { VuuRowDataItemType } from "@vuu-ui/vuu-protocol-types";
2
+ declare const basketConstituentData: VuuRowDataItemType[][];
3
+ export default basketConstituentData;
@@ -1,3 +1,4 @@
1
+ import { Table } from "../../Table";
1
2
  export type ask = number;
2
3
  export type askSize = number;
3
4
  export type bid = number;
@@ -20,5 +21,5 @@ export type PricesDataRow = [
20
21
  ric,
21
22
  scenario
22
23
  ];
23
- declare const pricesTable: import("../..").Table;
24
+ declare const pricesTable: Table;
24
25
  export default pricesTable;
@@ -8,7 +8,7 @@ declare class TableContainer {
8
8
  private constructor();
9
9
  static get instance(): TableContainer;
10
10
  createTable: (schema: TableSchema, data?: Array<Array<VuuRowDataItemType | bigint>>, dataMap?: ColumnMap, updateGenerator?: UpdateGenerator) => Table;
11
- createJoinTable(joinTable: VuuTable, { table: t1 }: VuuTable, { table: t2 }: VuuTable, joinColumn: string): Table;
11
+ createJoinTable(joinTable: VuuTable, source1: VuuTable | Table, source2: VuuTable | Table, joinColumn: string): Table;
12
12
  addTable(table: Table): void;
13
13
  getTable<T = Table>(tableName: string): NonNullable<T>;
14
14
  }