@barchart/portfolio-api-common 7.5.2 → 9.0.0

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.
@@ -6,6 +6,7 @@ const array = require('@barchart/common-js/lang/array'),
6
6
  CurrencyTranslator = require('@barchart/common-js/lang/CurrencyTranslator'),
7
7
  Day = require('@barchart/common-js/lang/Day'),
8
8
  Decimal = require('@barchart/common-js/lang/Decimal'),
9
+ Disposable = require('@barchart/common-js/lang/Disposable'),
9
10
  DisposableStack = require('@barchart/common-js/collections/specialized/DisposableStack'),
10
11
  Event = require('@barchart/common-js/messaging/Event'),
11
12
  is = require('@barchart/common-js/lang/is'),
@@ -30,6 +31,7 @@ module.exports = (() => {
30
31
  Currency.AUD,
31
32
  Currency.CAD,
32
33
  Currency.CHF,
34
+ Currency.DKK,
33
35
  Currency.GBP,
34
36
  Currency.GBX,
35
37
  Currency.EUR,
@@ -86,6 +88,11 @@ module.exports = (() => {
86
88
 
87
89
  this._groupBindings = { };
88
90
 
91
+ this._calculationSuspensions = new Set();
92
+
93
+ this._suspendedForexQuotes = new Map();
94
+ this._suspendedPositionQuotes = new Map();
95
+
89
96
  this._reporting = reportFrame instanceof PositionSummaryFrame;
90
97
  this._useBarchartPriceFormattingRules = false;
91
98
 
@@ -201,6 +208,48 @@ module.exports = (() => {
201
208
  recalculatePercentages.call(this);
202
209
  }
203
210
 
211
+ /**
212
+ * Suspends recalculation of aggregated position data.
213
+ *
214
+ * @public
215
+ * @returns {Disposable}
216
+ */
217
+ suspendCalculations() {
218
+ const token = { };
219
+
220
+ const disposable = Disposable.fromAction(() => {
221
+ if (this._calculationSuspensions.delete(token) && this._calculationSuspensions.size === 0) {
222
+ const positionQuotes = [ ...this._suspendedPositionQuotes.entries() ];
223
+ const forexQuotes = [ ...this._suspendedForexQuotes.entries() ];
224
+
225
+ this._suspendedPositionQuotes = new Map();
226
+ this._suspendedForexQuotes = new Map();
227
+
228
+ this.setQuotes(positionQuotes, forexQuotes);
229
+
230
+ Object.keys(this._trees).forEach((key) => {
231
+ this._trees[key].walk(group => group.resumeCalculations(), false, false);
232
+ });
233
+ }
234
+ });
235
+
236
+ this._calculationSuspensions.add(token);
237
+
238
+ if (this._calculationSuspensions.size === 1) {
239
+ Object.keys(this._trees).forEach((key) => {
240
+ this._trees[key].walk(group => group.suspendCalculations(), false, false);
241
+ });
242
+
243
+ recalculatePercentages.call(this);
244
+ }
245
+
246
+ return disposable;
247
+ }
248
+
249
+ getCalculationsSuspended() {
250
+ return this._calculationSuspensions.size !== 0;
251
+ }
252
+
204
253
  /**
205
254
  * Returns Barchart's user identifier for the container's portfolios. If
206
255
  * the container has no portfolios, a null value is returned.
@@ -209,18 +258,16 @@ module.exports = (() => {
209
258
  * @returns {String|null}
210
259
  */
211
260
  getBarchartUserId() {
212
- let returnRef = null;
213
-
214
261
  const keys = Object.keys(this._portfolios);
215
262
 
216
263
  if (keys.length > 0) {
217
264
  const firstKey = keys[0];
218
265
  const firstPortfolio = this._portfolios[firstKey];
219
266
 
220
- returnRef = firstPortfolio.user;
267
+ return firstPortfolio.user;
221
268
  }
222
269
 
223
- return returnRef;
270
+ return null;
224
271
  }
225
272
 
226
273
  /**
@@ -232,8 +279,6 @@ module.exports = (() => {
232
279
  * @returns {String|null}
233
280
  */
234
281
  getCustomerUserId() {
235
- let returnRef = null;
236
-
237
282
  const keys = Object.keys(this._portfolios);
238
283
 
239
284
  if (keys.length > 0) {
@@ -241,11 +286,11 @@ module.exports = (() => {
241
286
  const firstPortfolio = this._portfolios[firstKey];
242
287
 
243
288
  if (firstPortfolio.legacy && firstPortfolio.legacy.user) {
244
- returnRef = firstPortfolio.legacy.user;
289
+ return firstPortfolio.legacy.user;
245
290
  }
246
291
  }
247
292
 
248
- return returnRef;
293
+ return null;
249
294
  }
250
295
 
251
296
  /**
@@ -402,14 +447,13 @@ module.exports = (() => {
402
447
  return;
403
448
  }
404
449
 
405
- const existingBarchartSymbols = this.getPositionSymbols(false);
450
+ const existingBarchartSymbols = this.getPositionSymbols(false, false);
406
451
 
407
- let exchange;
452
+ let exchangeCode = extractExchangeCode(position);
453
+ let exchange = null;
408
454
 
409
- if (extractExchangeCode(position)) {
410
- const code = extractExchangeCode(position);
411
-
412
- exchange = this._exchanges[code] || null;
455
+ if (exchangeCode !== null) {
456
+ exchange = this._exchanges[exchangeCode] || null;
413
457
  }
414
458
 
415
459
  let currentQuote = null;
@@ -471,7 +515,7 @@ module.exports = (() => {
471
515
  this._positionSymbolAddedEvent.fire(addedBarchartSymbol);
472
516
  }
473
517
 
474
- if (exchange) {
518
+ if (exchange !== null) {
475
519
  item.setExchangeStatus(exchange);
476
520
  }
477
521
 
@@ -519,7 +563,7 @@ module.exports = (() => {
519
563
  *
520
564
  * @public
521
565
  * @param {Boolean} display - If true, all "display" symbols are returned; otherwise Barchart symbols are returned.
522
- * @param {Boolean} excludeExpired - If true, only non-expired symbols are filtered.
566
+ * @param {Boolean} excludeExpired - If true, only symbols for non-expired positions will be returned.
523
567
  * @returns {String[]}
524
568
  */
525
569
  getPositionSymbols(display, excludeExpired) {
@@ -634,6 +678,22 @@ module.exports = (() => {
634
678
  assert.argumentIsArray(forexQuotes, 'forexQuotes');
635
679
  assert.argumentIsOptional(force, 'force', Boolean);
636
680
 
681
+ if (this.getCalculationsSuspended()) {
682
+ forexQuotes.forEach((quote) => {
683
+ const symbol = quote.symbol;
684
+
685
+ this._suspendedForexQuotes.set(symbol, quote);
686
+ });
687
+
688
+ positionQuotes.forEach((quote) => {
689
+ const symbol = quote.symbol;
690
+
691
+ this._suspendedPositionQuotes.set(symbol, quote);
692
+ });
693
+
694
+ return;
695
+ }
696
+
637
697
  if (forexQuotes.length !== 0) {
638
698
  forexQuotes.forEach((quote) => {
639
699
  const symbol = quote.symbol;
@@ -947,41 +1007,39 @@ module.exports = (() => {
947
1007
  function findParentGroup(group, predicate) {
948
1008
  const groupNode = this._nodes[group.id];
949
1009
 
950
- let returnRef = null;
951
-
952
1010
  if (groupNode) {
953
1011
  const resultNode = groupNode.findParent((candidateGroup, candidateNode) => !candidateNode.getIsRoot() && predicate(candidateGroup));
954
1012
 
955
1013
  if (resultNode) {
956
- returnRef = resultNode.getValue();
1014
+ return resultNode.getValue();
957
1015
  }
958
1016
  }
959
1017
 
960
- return returnRef;
1018
+ return null;
961
1019
  }
962
1020
 
963
1021
  function extractSymbolForBarchart(position) {
964
1022
  if (position.instrument && position.instrument.symbol && position.instrument.symbol.barchart) {
965
1023
  return position.instrument.symbol.barchart;
966
- } else {
967
- return null;
968
1024
  }
1025
+
1026
+ return null;
969
1027
  }
970
1028
 
971
1029
  function extractSymbolForDisplay(position) {
972
1030
  if (position.instrument && position.instrument.symbol && position.instrument.symbol.display) {
973
1031
  return position.instrument.symbol.display;
974
- } else {
975
- return null;
976
1032
  }
1033
+
1034
+ return null;
977
1035
  }
978
1036
 
979
1037
  function extractExchangeCode(position) {
980
1038
  if (position.instrument && position.instrument.exchange) {
981
1039
  return position.instrument.exchange;
982
- } else {
983
- return null;
984
1040
  }
1041
+
1042
+ return null;
985
1043
  }
986
1044
 
987
1045
  function addGroupBinding(group, dispoable) {
@@ -1056,7 +1114,7 @@ module.exports = (() => {
1056
1114
  const items = populatedObjects[key];
1057
1115
  const first = items[0];
1058
1116
 
1059
- const group = new PositionGroup(levelDefinition, items, levelDefinition.currencySelector(first), currencyTranslator, key, levelDefinition.descriptionSelector(first), levelDefinition.aggregateCash);
1117
+ const group = new PositionGroup(levelDefinition, items, levelDefinition.currencySelector(first), currencyTranslator, key, levelDefinition.descriptionSelector(first), this.getCalculationsSuspended());
1060
1118
 
1061
1119
  group.setBarchartPriceFormattingRules(this._useBarchartPriceFormattingRules);
1062
1120
 
@@ -1072,15 +1130,15 @@ module.exports = (() => {
1072
1130
  return requiredGroupsToUse.find(g => g.key === key);
1073
1131
  });
1074
1132
 
1075
- const empty = missingGroups.map((group) => {
1076
- const eg = new PositionGroup(levelDefinition, [ ], group.currency, currencyTranslator, group.key, group.description);
1133
+ const emptyGroups = missingGroups.map((group) => {
1134
+ const empty = new PositionGroup(levelDefinition, [ ], group.currency, currencyTranslator, group.key, group.description, this.getCalculationsSuspended());
1077
1135
 
1078
- eg.setBarchartPriceFormattingRules(this._useBarchartPriceFormattingRules);
1136
+ empty.setBarchartPriceFormattingRules(this._useBarchartPriceFormattingRules);
1079
1137
 
1080
- return eg;
1138
+ return empty;
1081
1139
  });
1082
1140
 
1083
- const compositeGroups = populatedGroups.concat(empty);
1141
+ const compositeGroups = populatedGroups.concat(emptyGroups);
1084
1142
 
1085
1143
  let builder;
1086
1144
 
@@ -1201,22 +1259,16 @@ module.exports = (() => {
1201
1259
  function createPositionItem(position, requireCurrentSummary) {
1202
1260
  const portfolio = this._portfolios[position.portfolio];
1203
1261
 
1204
- let returnRef;
1205
-
1206
1262
  if (portfolio) {
1207
1263
  const currentSummary = this._summariesCurrent[ position.position ] || null;
1208
1264
  const previousSummaries = this._summariesPrevious[ position.position ] || getSummaryArray(this._previousSummaryRanges);
1209
1265
 
1210
1266
  if (!requireCurrentSummary || currentSummary !== null) {
1211
- returnRef = new PositionItem(portfolio, position, currentSummary, previousSummaries, this._reporting, this._reportDate);
1212
- } else {
1213
- returnRef = null;
1267
+ return new PositionItem(portfolio, position, currentSummary, previousSummaries, this._reporting, this._reportDate);
1214
1268
  }
1215
- } else {
1216
- returnRef = null;
1217
1269
  }
1218
1270
 
1219
- return returnRef;
1271
+ return null;
1220
1272
  }
1221
1273
 
1222
1274
  function removePositionItem(positionItem) {
@@ -1258,6 +1310,10 @@ module.exports = (() => {
1258
1310
  }
1259
1311
 
1260
1312
  function recalculatePercentages() {
1313
+ if (this.getCalculationsSuspended()) {
1314
+ return;
1315
+ }
1316
+
1261
1317
  Object.keys(this._trees).forEach((key) => {
1262
1318
  this._trees[key].walk(group => group.refreshMarketPercent(), false, false);
1263
1319
  });
@@ -1297,4 +1353,4 @@ module.exports = (() => {
1297
1353
  */
1298
1354
 
1299
1355
  return PositionContainer;
1300
- })();
1356
+ })();
@@ -32,10 +32,10 @@ module.exports = (() => {
32
32
  * @param {CurrencyTranslator} currencyTranslator
33
33
  * @param {String} key
34
34
  * @param {String} description
35
- * @param {Boolean=} aggregateCash
35
+ * @param {Boolean} calculationsSuspended
36
36
  */
37
37
  class PositionGroup {
38
- constructor(definition, items, currency, currencyTranslator, key, description, aggregateCash) {
38
+ constructor(definition, items, currency, currencyTranslator, key, description, calculationsSuspended) {
39
39
  this._id = counter++;
40
40
 
41
41
  this._definition = definition;
@@ -57,7 +57,7 @@ module.exports = (() => {
57
57
  this._single = this._definition.single;
58
58
  this._homogeneous = this._definition.homogeneous;
59
59
 
60
- this._aggregateCash = is.boolean(aggregateCash) && aggregateCash;
60
+ this._calculationsSuspended = calculationsSuspended;
61
61
 
62
62
  this._excluded = false;
63
63
  this._showClosedPositions = false;
@@ -342,6 +342,28 @@ module.exports = (() => {
342
342
  return this._excluded;
343
343
  }
344
344
 
345
+ /**
346
+ * Suspends recalculation of aggregated group data.
347
+ *
348
+ * @public
349
+ */
350
+ suspendCalculations() {
351
+ this._calculationsSuspended = true;
352
+ }
353
+
354
+ /**
355
+ * Resumes recalculation of aggregated group data.
356
+ *
357
+ * @public
358
+ */
359
+ resumeCalculations() {
360
+ if (this._calculationsSuspended) {
361
+ this._calculationsSuspended = false;
362
+
363
+ this.refresh();
364
+ }
365
+ }
366
+
345
367
  /**
346
368
  * Changes the group currency.
347
369
  *
@@ -512,17 +534,25 @@ module.exports = (() => {
512
534
  * @public
513
535
  */
514
536
  refresh() {
537
+ if (this._calculationsSuspended) {
538
+ return;
539
+ }
540
+
515
541
  calculateStaticData(this, this._definition);
516
542
  calculatePriceData(this,null, true);
517
543
  }
518
544
 
519
545
  /**
520
- * Causes the percent of the position, with respect to the parent container's
546
+ * Causes the percent of the group, with respect to the parent group's
521
547
  * total, to be recalculated.
522
548
  *
523
549
  * @public
524
550
  */
525
551
  refreshMarketPercent() {
552
+ if (this._calculationsSuspended) {
553
+ return;
554
+ }
555
+
526
556
  calculateMarketPercent(this, this._parentGroup, this._portfolioGroup);
527
557
  }
528
558
 
@@ -600,6 +630,10 @@ module.exports = (() => {
600
630
 
601
631
  function bindItem(item) {
602
632
  const quoteBinding = item.registerQuoteChangeHandler((quote, sender) => {
633
+ if (this._calculationsSuspended) {
634
+ return;
635
+ }
636
+
603
637
  if (this._single || (this._homogeneous && item === this._items[0])) {
604
638
  const instrument = sender.position.instrument;
605
639
  const currency = instrument.currency;
@@ -1144,6 +1178,7 @@ module.exports = (() => {
1144
1178
 
1145
1179
  if (updates.marketDirection.up || updates.marketDirection.down) {
1146
1180
  format.marketDirection = unchanged;
1181
+
1147
1182
  setTimeout(() => format.marketDirection = updates.marketDirection, 0);
1148
1183
  }
1149
1184
 
@@ -21,11 +21,10 @@ module.exports = (() => {
21
21
  * @param {PositionLevelDefinition~descriptionSelector} descriptionSelector
22
22
  * @param {PositionLevelDefinition~currencySelector} currencySelector
23
23
  * @param {PositionLevelDefinition~RequiredGroup[]=} requiredGroups
24
- * @param {Boolean=} aggregateCash
25
24
  * @param {Function=} requiredGroupGenerator
26
25
  */
27
26
  class PositionLevelDefinition {
28
- constructor(name, type, keySelector, descriptionSelector, currencySelector, requiredGroups, aggregateCash, requiredGroupGenerator) {
27
+ constructor(name, type, keySelector, descriptionSelector, currencySelector, requiredGroups, requiredGroupGenerator) {
29
28
  assert.argumentIsRequired(name, 'name', String);
30
29
  assert.argumentIsRequired(type, 'type', PositionLevelType, 'PositionLevelType');
31
30
  assert.argumentIsRequired(keySelector, 'keySelector', Function);
@@ -36,7 +35,6 @@ module.exports = (() => {
36
35
  assert.argumentIsArray(requiredGroups, 'requiredGroups', String);
37
36
  }
38
37
 
39
- assert.argumentIsOptional(aggregateCash, 'aggregateCash', Boolean);
40
38
  assert.argumentIsOptional(requiredGroupGenerator, 'requiredGroupGenerator', Function);
41
39
 
42
40
  this._name = name;
@@ -51,8 +49,6 @@ module.exports = (() => {
51
49
  this._single = type === PositionLevelType.POSITION;
52
50
  this._homogeneous = type === PositionLevelType.INSTRUMENT;
53
51
 
54
- this._aggregateCash = is.boolean(aggregateCash) && aggregateCash;
55
-
56
52
  this._requiredGroupGenerator = requiredGroupGenerator || (input => null);
57
53
  }
58
54
 
@@ -140,16 +136,6 @@ module.exports = (() => {
140
136
  return this._homogeneous;
141
137
  }
142
138
 
143
- /**
144
- * Indicates if the grouping level should aggregate cash positions.
145
- *
146
- * @public
147
- * @returns {Boolean}
148
- */
149
- get aggregateCash() {
150
- return this._aggregateCash;
151
- }
152
-
153
139
  /**
154
140
  * Given an input, potentially creates a new {@link PositionLevelDefinition~RequiredGroup}.
155
141
  *
@@ -107,7 +107,6 @@ module.exports = (() => {
107
107
  .withField('instrument.symbol.barchart', DataType.STRING, true)
108
108
  .withField('instrument.symbol.display', DataType.STRING, true)
109
109
  .withField('position', DataType.STRING)
110
- .withField('open', DataType.BOOLEAN, true)
111
110
  .withField('transaction', DataType.NUMBER)
112
111
  .withField('cash', DataType.BOOLEAN, true)
113
112
  .withField('reinvest', DataType.BOOLEAN, true)
@@ -157,7 +156,6 @@ module.exports = (() => {
157
156
  .withField('instrument.symbol.barchart', DataType.STRING, true)
158
157
  .withField('instrument.symbol.display', DataType.STRING, true)
159
158
  .withField('position', DataType.STRING)
160
- .withField('open', DataType.BOOLEAN, true)
161
159
  .withField('transaction', DataType.NUMBER)
162
160
  .withField('cash', DataType.BOOLEAN, true)
163
161
  .withField('reinvest', DataType.BOOLEAN, true)
@@ -201,7 +199,6 @@ module.exports = (() => {
201
199
  .withField('instrument.symbol.barchart', DataType.STRING, true)
202
200
  .withField('instrument.symbol.display', DataType.STRING, true)
203
201
  .withField('position', DataType.STRING)
204
- .withField('open', DataType.BOOLEAN, true)
205
202
  .schema
206
203
  );
207
204
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barchart/portfolio-api-common",
3
- "version": "7.5.2",
3
+ "version": "9.0.0",
4
4
  "description": "Common JavaScript code used by Barchart's Portfolio Service",
5
5
  "author": {
6
6
  "name": "Bryan Ingle",
@@ -22,7 +22,7 @@
22
22
  "url": "git+ssh://git@github.com/barchart/portfolio-api-common.git"
23
23
  },
24
24
  "dependencies": {
25
- "@barchart/common-js": "^4.71.0",
25
+ "@barchart/common-js": "^4.73.0",
26
26
  "@barchart/marketdata-api-js": "^6.2.1",
27
27
  "uuid": "^9.0.1"
28
28
  },