@barchart/portfolio-api-common 1.0.267 → 1.0.271

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.
@@ -23,6 +23,17 @@ module.exports = (() => {
23
23
  return portfolioUpdateFailedNoPortfolio;
24
24
  }
25
25
 
26
+ /**
27
+ * The portfolio does not exist.
28
+ *
29
+ * @public
30
+ * @static
31
+ * @returns {FailureType}
32
+ */
33
+ static get PORTFOLIO_DELETE_FAILED_NO_PORTFOLIO() {
34
+ return portfolioDeleteFailedNoPortfolio;
35
+ }
36
+
26
37
  /**
27
38
  * The portfolio does not exist.
28
39
  *
@@ -136,7 +147,8 @@ module.exports = (() => {
136
147
  }
137
148
  }
138
149
 
139
- const portfolioUpdateFailedNoPortfolio = new FailureType('PORTFOLIO_UPDATED_FAILED_NO_PORTFOLIO', 'Unable to update portfolio. The portfolio does not exist, has it been deleted?');
150
+ const portfolioUpdateFailedNoPortfolio = new FailureType('PORTFOLIO_UPDATE_FAILED_NO_PORTFOLIO', 'Unable to update portfolio. The portfolio does not exist, has it been deleted?');
151
+ const portfolioDeleteFailedNoPortfolio = new FailureType('PORTFOLIO_DELETE_FAILED_NO_PORTFOLIO', 'Unable to delete portfolio. The portfolio does not exist, has it already been deleted?');
140
152
 
141
153
  const positionCreateFailedNoPortfolio = new FailureType('POSITION_CREATE_FAILED_NO_PORTFOLIO', 'Unable to create transaction. The portfolio does not exist, has it been deleted?');
142
154
  const positionUpdateFailedNoPosition = new FailureType('POSITION_UPDATE_FAILED_NO_POSITION', 'Unable to update preferences for position. The position does not exist, has it been deleted?');
@@ -1,5 +1,5 @@
1
1
  const assert = require('@barchart/common-js/lang/assert'),
2
- array = require('@barchart/common-js/lang/array')
2
+ array = require('@barchart/common-js/lang/array');
3
3
 
4
4
  const InstrumentType = require('./InstrumentType'),
5
5
  PositionDirection = require('./PositionDirection'),
@@ -25,22 +25,12 @@ module.exports = (() => {
25
25
  * @public
26
26
  * @static
27
27
  * @param {Array.<Object>} transactions
28
- * @param {Boolean=} partial - If true, sequence validation starts with the array's first transaction.
29
28
  * @return {boolean}
30
29
  */
31
- static validateOrder(transactions, partial) {
30
+ static validateOrder(transactions) {
32
31
  assert.argumentIsArray(transactions, 'transactions');
33
- assert.argumentIsOptional(partial, 'partial', Boolean);
34
32
 
35
- let startSequence;
36
-
37
- if (partial && transactions.length !== 0) {
38
- startSequence = array.first(transactions).sequence;
39
- } else {
40
- startSequence = 1;
41
- }
42
-
43
- return transactions.every((t, i) => t.sequence === (i + startSequence) && (i === 0 || !t.date.getIsBefore(transactions[i - 1].date)));
33
+ return transactions.every((t, i) => t.sequence === (i + 1) && (i === 0 || !t.date.getIsBefore(transactions[i - 1].date)));
44
34
  }
45
35
 
46
36
  /**
@@ -224,11 +224,9 @@ module.exports = (() => {
224
224
  assert.argumentIsRequired(portfolio, 'portfolio', Object);
225
225
  assert.argumentIsRequired(portfolio.portfolio, 'portfolio.portfolio', String);
226
226
 
227
- this.startTransaction(() => {
228
- getPositionItemsForPortfolio(this._items, portfolio.portfolio).forEach(item => item.updatePortfolio(portfolio));
227
+ getPositionItemsForPortfolio(this._items, portfolio.portfolio).forEach(item => item.updatePortfolio(portfolio));
229
228
 
230
- updateEmptyPortfolioGroups.call(this, portfolio);
231
- });
229
+ updateEmptyPortfolioGroups.call(this, portfolio);
232
230
  }
233
231
 
234
232
  /**
@@ -243,19 +241,19 @@ module.exports = (() => {
243
241
  assert.argumentIsRequired(portfolio, 'portfolio', Object);
244
242
  assert.argumentIsRequired(portfolio.portfolio, 'portfolio.portfolio', String);
245
243
 
246
- this.startTransaction(() => {
247
- getPositionItemsForPortfolio(this._items, portfolio.portfolio).forEach(item => removePositionItem.call(this, item));
244
+ getPositionItemsForPortfolio(this._items, portfolio.portfolio).forEach(item => removePositionItem.call(this, item));
248
245
 
249
- delete this._portfolios[portfolio.portfolio];
246
+ delete this._portfolios[portfolio.portfolio];
250
247
 
251
- Object.keys(this._trees).forEach((key) => {
252
- this._trees[key].walk((group, groupNode) => {
253
- if (group.definition.type === PositionLevelType.PORTFOLIO && group.key === PositionLevelDefinition.getKeyForPortfolioGroup(portfolio)) {
254
- severGroupNode.call(this, groupNode);
255
- }
256
- }, true, false);
257
- });
248
+ Object.keys(this._trees).forEach((key) => {
249
+ this._trees[key].walk((group, groupNode) => {
250
+ if (group.definition.type === PositionLevelType.PORTFOLIO && group.key === PositionLevelDefinition.getKeyForPortfolioGroup(portfolio)) {
251
+ severGroupNode.call(this, groupNode);
252
+ }
253
+ }, true, false);
258
254
  });
255
+
256
+ recalculatePercentages.call(this);
259
257
  }
260
258
 
261
259
  /**
@@ -276,56 +274,54 @@ module.exports = (() => {
276
274
  return;
277
275
  }
278
276
 
279
- this.startTransaction(() => {
280
- const existingBarchartSymbols = this.getPositionSymbols(false);
277
+ const existingBarchartSymbols = this.getPositionSymbols(false);
281
278
 
282
- removePositionItem.call(this, this._items.find((item) => item.position.position === position.position));
279
+ removePositionItem.call(this, this._items.find((item) => item.position.position === position.position));
283
280
 
284
- summaries.forEach((summary) => {
285
- addSummaryCurrent(this._summariesCurrent, summary, this._currentSummaryFrame, this._currentSummaryRange);
286
- addSummaryPrevious(this._summariesPrevious, summary, this._previousSummaryFrame, this._previousSummaryRanges);
287
- });
281
+ summaries.forEach((summary) => {
282
+ addSummaryCurrent(this._summariesCurrent, summary, this._currentSummaryFrame, this._currentSummaryRange);
283
+ addSummaryPrevious(this._summariesPrevious, summary, this._previousSummaryFrame, this._previousSummaryRanges);
284
+ });
288
285
 
289
- const item = createPositionItem.call(this, position);
286
+ const item = createPositionItem.call(this, position);
290
287
 
291
- addBarchartSymbol(this._symbols, item);
292
- addDisplaySymbol(this._symbolsDisplay, item);
288
+ addBarchartSymbol(this._symbols, item);
289
+ addDisplaySymbol(this._symbolsDisplay, item);
293
290
 
294
- this._items.push(item);
291
+ this._items.push(item);
295
292
 
296
- const createGroupOrInjectItem = (parentTree, treeDefinition, levelDefinitions) => {
297
- if (levelDefinitions.length === 0) {
298
- return;
299
- }
293
+ const createGroupOrInjectItem = (parentTree, treeDefinition, levelDefinitions) => {
294
+ if (levelDefinitions.length === 0) {
295
+ return;
296
+ }
300
297
 
301
- const levelDefinition = levelDefinitions[0];
302
- const levelKey = levelDefinition.keySelector(item);
298
+ const levelDefinition = levelDefinitions[0];
299
+ const levelKey = levelDefinition.keySelector(item);
303
300
 
304
- let groupTree;
301
+ let groupTree;
305
302
 
306
- if (parentTree.getChildren().length > 0) {
307
- groupTree = parentTree.findChild(childGroup => childGroup.key === levelKey) || null;
308
- } else {
309
- groupTree = null;
310
- }
303
+ if (parentTree.getChildren().length > 0) {
304
+ groupTree = parentTree.findChild(childGroup => childGroup.key === levelKey) || null;
305
+ } else {
306
+ groupTree = null;
307
+ }
311
308
 
312
- if (groupTree !== null) {
313
- groupTree.getValue().addItem(item);
309
+ if (groupTree !== null) {
310
+ groupTree.getValue().addItem(item);
314
311
 
315
- createGroupOrInjectItem(groupTree, treeDefinition, array.dropLeft(levelDefinitions));
316
- } else {
317
- createGroups.call(this, parentTree, [ item ], treeDefinition, levelDefinitions, [ ]);
318
- }
319
- };
312
+ createGroupOrInjectItem(groupTree, treeDefinition, array.dropLeft(levelDefinitions));
313
+ } else {
314
+ createGroups.call(this, parentTree, [ item ], treeDefinition, levelDefinitions, [ ]);
315
+ }
316
+ };
320
317
 
321
- this._definitions.forEach(definition => createGroupOrInjectItem(this._trees[definition.name], definition, definition.definitions));
318
+ this._definitions.forEach(definition => createGroupOrInjectItem(this._trees[definition.name], definition, definition.definitions));
322
319
 
323
- const addedBarchartSymbol = extractSymbolForBarchart(item.position);
320
+ const addedBarchartSymbol = extractSymbolForBarchart(item.position);
324
321
 
325
- if (addedBarchartSymbol !== null && !existingBarchartSymbols.some(existingBarchartSymbol => existingBarchartSymbol === addedBarchartSymbol)) {
326
- this._positionSymbolAddedEvent.fire(addedBarchartSymbol);
327
- }
328
- });
322
+ if (addedBarchartSymbol !== null && !existingBarchartSymbols.some(existingBarchartSymbol => existingBarchartSymbol === addedBarchartSymbol)) {
323
+ this._positionSymbolAddedEvent.fire(addedBarchartSymbol);
324
+ }
329
325
 
330
326
  recalculatePercentages.call(this);
331
327
  }
@@ -376,22 +372,55 @@ module.exports = (() => {
376
372
  }
377
373
 
378
374
  /**
379
- * Updates the quote for a single symbol; causing updates to any grouping
380
- * level that contains the position(s) for the symbol.
375
+ * Performs a batch update of both position quotes and forex quotes,
376
+ * triggering updates to position(s) and data aggregation(s).
381
377
  *
382
378
  * @public
383
- * @param {String} symbol
384
- * @param {Object} quote
379
+ * @param {Array.<Object>} positionQuotes
380
+ * @param {Array.<Object>} forexQuotes
385
381
  */
386
- setPositionQuote(symbol, quote) {
387
- assert.argumentIsRequired(symbol, 'symbol', String);
388
- assert.argumentIsRequired(quote, 'quote', Object);
382
+ setQuotes(positionQuotes, forexQuotes) {
383
+ assert.argumentIsArray(positionQuotes, 'positionQuotes');
384
+ assert.argumentIsArray(forexQuotes, 'forexQuotes');
385
+
386
+ if (positionQuotes.length !== 0) {
387
+ positionQuotes.forEach((quote) => {
388
+ const symbol = quote.symbol;
389
389
 
390
- if (this._symbols.hasOwnProperty(symbol)) {
391
- this._symbols[symbol].forEach(item => item.setQuote(quote));
390
+ if (symbol) {
391
+ if (this._symbols.hasOwnProperty(symbol)) {
392
+ this._symbols[ symbol ].forEach(item => item.setQuote(quote));
393
+ }
394
+ }
395
+ });
392
396
  }
393
397
 
394
- recalculatePercentages.call(this);
398
+ if (forexQuotes.length !== 0) {
399
+ forexQuotes.forEach((quote) => {
400
+ const symbol = quote.symbol;
401
+
402
+ if (symbol) {
403
+ const rate = Rate.fromPair(quote.lastPrice, symbol);
404
+ const index = this._forexQuotes.findIndex(existing => existing.formatPair() === rate.formatPair());
405
+
406
+ if (index < 0) {
407
+ this._forexQuotes.push(rate);
408
+ } else {
409
+ this._forexQuotes[index] = rate;
410
+ }
411
+
412
+ Object.keys(this._trees).forEach((key) => {
413
+ this._trees[key].walk(group => group.setForexRates(this._forexQuotes), true, false);
414
+ });
415
+
416
+ recalculatePercentages.call(this);
417
+ }
418
+ });
419
+ }
420
+
421
+ if (positionQuotes.length !== 0 || forexQuotes.length !== 0) {
422
+ recalculatePercentages.call(this);
423
+ }
395
424
  }
396
425
 
397
426
  /**
@@ -414,32 +443,6 @@ module.exports = (() => {
414
443
  return this._forexQuotes;
415
444
  }
416
445
 
417
- /**
418
- * Updates the forex quote for a single currency pair; causing updates to
419
- * any grouping level that contains that requires translation.
420
- *
421
- * @public
422
- * @param {String} symbol
423
- * @param {Object} quote
424
- */
425
- setForexQuote(symbol, quote) {
426
- assert.argumentIsRequired(symbol, 'symbol', String);
427
- assert.argumentIsRequired(quote, 'quote', Object);
428
-
429
- const rate = Rate.fromPair(quote.lastPrice, symbol);
430
- const index = this._forexQuotes.findIndex(existing => existing.formatPair() === rate.formatPair());
431
-
432
- if (index < 0) {
433
- this._forexQuotes.push(rate);
434
- } else {
435
- this._forexQuotes[index] = rate;
436
- }
437
-
438
- Object.keys(this._trees).forEach(key => this._trees[key].walk(group => group.setForexRates(this._forexQuotes), true, false));
439
-
440
- recalculatePercentages.call(this);
441
- }
442
-
443
446
  /**
444
447
  * Updates fundamental data for a single symbol.
445
448
  *
@@ -588,39 +591,6 @@ module.exports = (() => {
588
591
  return this.getPositions(portfolio).find(p => p.position === position) || null;
589
592
  }
590
593
 
591
- /**
592
- * Pauses aggregation calculations during the processing of an action.
593
- *
594
- * @public
595
- * @param {Function} executor
596
- * @param {String=|Array.<String>=} names
597
- */
598
- startTransaction(executor, names) {
599
- let namesToUse;
600
-
601
- if (is.array(names)) {
602
- assert.argumentIsArray(names, 'names', String);
603
-
604
- namesToUse = names;
605
- } else {
606
- assert.argumentIsOptional(names, 'names', String);
607
-
608
- if (names) {
609
- namesToUse = [ names ];
610
- } else {
611
- namesToUse = Object.keys(this._trees);
612
- }
613
- }
614
-
615
- assert.argumentIsRequired(executor, 'executor', Function);
616
-
617
- namesToUse.forEach((name) => this._trees[name].walk(group => group.setSuspended(true), false, false));
618
-
619
- executor(this);
620
-
621
- namesToUse.forEach((name) => this._trees[name].walk(group => group.setSuspended(false), false, false));
622
- }
623
-
624
594
  /**
625
595
  * Registers an observer for symbol addition (this occurs when a new position is added
626
596
  * for a symbol that does not already exist in the container). This event only fires
@@ -54,7 +54,6 @@ module.exports = (() => {
54
54
  this._aggregateCash = is.boolean(aggregateCash) && aggregateCash;
55
55
 
56
56
  this._excluded = false;
57
- this._suspended = false;
58
57
  this._showClosedPositions = false;
59
58
 
60
59
  this._groupExcludedChangeEvent = new Event(this);
@@ -255,10 +254,6 @@ module.exports = (() => {
255
254
  return this._single;
256
255
  }
257
256
 
258
- get suspended() {
259
- return this._suspended;
260
- }
261
-
262
257
  /**
263
258
  * Indicates if the group should be excluded from higher-level aggregations.
264
259
  *
@@ -381,24 +376,6 @@ module.exports = (() => {
381
376
  }
382
377
  }
383
378
 
384
- /**
385
- * Stops (or starts) group-level aggregation calculations.
386
- *
387
- * @public
388
- * @param {Boolean} value
389
- */
390
- setSuspended(value) {
391
- assert.argumentIsRequired(value, 'value', Boolean);
392
-
393
- if (this._suspended !== value) {
394
- this._suspended = value;
395
-
396
- if (!this._suspended) {
397
- this.refresh();
398
- }
399
- }
400
- }
401
-
402
379
  /**
403
380
  * Updates the portfolio data. For example, a portfolio's name might change. This
404
381
  * function only affects {@link PositionLevelType.PORTFOLIO} groups.
@@ -430,16 +407,11 @@ module.exports = (() => {
430
407
  }
431
408
 
432
409
  /**
433
- * Causes all aggregated data to be recalculated (assuming the group has not
434
- * been suspended).
410
+ * Causes all aggregated data to be recalculated.
435
411
  *
436
412
  * @public
437
413
  */
438
414
  refresh() {
439
- if (this._suspended) {
440
- return;
441
- }
442
-
443
415
  calculateStaticData(this, this._rates);
444
416
  calculatePriceData(this, this._rates, null, true);
445
417
  }
@@ -643,10 +615,6 @@ module.exports = (() => {
643
615
  }
644
616
 
645
617
  function calculateStaticData(group, rates) {
646
- if (group.suspended) {
647
- return;
648
- }
649
-
650
618
  const actual = group._dataActual;
651
619
  const format = group._dataFormat;
652
620
 
@@ -743,10 +711,6 @@ module.exports = (() => {
743
711
  }
744
712
 
745
713
  function calculatePriceData(group, rates, item, forceRefresh) {
746
- if (group.suspended) {
747
- return;
748
- }
749
-
750
714
  const currency = group.currency;
751
715
 
752
716
  const actual = group._dataActual;
@@ -832,10 +796,6 @@ module.exports = (() => {
832
796
  }
833
797
 
834
798
  function calculateMarketPercent(group, rates, parentGroup, portfolioGroup) {
835
- if (group.suspended) {
836
- return;
837
- }
838
-
839
799
  const actual = group._dataActual;
840
800
  const format = group._dataFormat;
841
801
  const excluded = group._excluded;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barchart/portfolio-api-common",
3
- "version": "1.0.267",
3
+ "version": "1.0.271",
4
4
  "description": "Common classes used by the Portfolio system",
5
5
  "author": {
6
6
  "name": "Bryan Ingle",
@@ -957,7 +957,7 @@ module.exports = (() => {
957
957
 
958
958
  },{"@barchart/common-js/lang/Enum":21,"@barchart/common-js/lang/assert":24}],5:[function(require,module,exports){
959
959
  const assert = require('@barchart/common-js/lang/assert'),
960
- array = require('@barchart/common-js/lang/array')
960
+ array = require('@barchart/common-js/lang/array');
961
961
 
962
962
  const InstrumentType = require('./InstrumentType'),
963
963
  PositionDirection = require('./PositionDirection'),
@@ -983,22 +983,12 @@ module.exports = (() => {
983
983
  * @public
984
984
  * @static
985
985
  * @param {Array.<Object>} transactions
986
- * @param {Boolean=} partial - If true, sequence validation starts with the array's first transaction.
987
986
  * @return {boolean}
988
987
  */
989
- static validateOrder(transactions, partial) {
988
+ static validateOrder(transactions) {
990
989
  assert.argumentIsArray(transactions, 'transactions');
991
- assert.argumentIsOptional(partial, 'partial', Boolean);
992
990
 
993
- let startSequence;
994
-
995
- if (partial && transactions.length !== 0) {
996
- startSequence = array.first(transactions).sequence;
997
- } else {
998
- startSequence = 1;
999
- }
1000
-
1001
- return transactions.every((t, i) => t.sequence === (i + startSequence) && (i === 0 || !t.date.getIsBefore(transactions[i - 1].date)));
991
+ return transactions.every((t, i) => t.sequence === (i + 1) && (i === 0 || !t.date.getIsBefore(transactions[i - 1].date)));
1002
992
  }
1003
993
 
1004
994
  /**
@@ -1380,11 +1370,9 @@ module.exports = (() => {
1380
1370
  assert.argumentIsRequired(portfolio, 'portfolio', Object);
1381
1371
  assert.argumentIsRequired(portfolio.portfolio, 'portfolio.portfolio', String);
1382
1372
 
1383
- this.startTransaction(() => {
1384
- getPositionItemsForPortfolio(this._items, portfolio.portfolio).forEach(item => item.updatePortfolio(portfolio));
1373
+ getPositionItemsForPortfolio(this._items, portfolio.portfolio).forEach(item => item.updatePortfolio(portfolio));
1385
1374
 
1386
- updateEmptyPortfolioGroups.call(this, portfolio);
1387
- });
1375
+ updateEmptyPortfolioGroups.call(this, portfolio);
1388
1376
  }
1389
1377
 
1390
1378
  /**
@@ -1399,19 +1387,19 @@ module.exports = (() => {
1399
1387
  assert.argumentIsRequired(portfolio, 'portfolio', Object);
1400
1388
  assert.argumentIsRequired(portfolio.portfolio, 'portfolio.portfolio', String);
1401
1389
 
1402
- this.startTransaction(() => {
1403
- getPositionItemsForPortfolio(this._items, portfolio.portfolio).forEach(item => removePositionItem.call(this, item));
1390
+ getPositionItemsForPortfolio(this._items, portfolio.portfolio).forEach(item => removePositionItem.call(this, item));
1404
1391
 
1405
- delete this._portfolios[portfolio.portfolio];
1392
+ delete this._portfolios[portfolio.portfolio];
1406
1393
 
1407
- Object.keys(this._trees).forEach((key) => {
1408
- this._trees[key].walk((group, groupNode) => {
1409
- if (group.definition.type === PositionLevelType.PORTFOLIO && group.key === PositionLevelDefinition.getKeyForPortfolioGroup(portfolio)) {
1410
- severGroupNode.call(this, groupNode);
1411
- }
1412
- }, true, false);
1413
- });
1394
+ Object.keys(this._trees).forEach((key) => {
1395
+ this._trees[key].walk((group, groupNode) => {
1396
+ if (group.definition.type === PositionLevelType.PORTFOLIO && group.key === PositionLevelDefinition.getKeyForPortfolioGroup(portfolio)) {
1397
+ severGroupNode.call(this, groupNode);
1398
+ }
1399
+ }, true, false);
1414
1400
  });
1401
+
1402
+ recalculatePercentages.call(this);
1415
1403
  }
1416
1404
 
1417
1405
  /**
@@ -1432,56 +1420,54 @@ module.exports = (() => {
1432
1420
  return;
1433
1421
  }
1434
1422
 
1435
- this.startTransaction(() => {
1436
- const existingBarchartSymbols = this.getPositionSymbols(false);
1423
+ const existingBarchartSymbols = this.getPositionSymbols(false);
1437
1424
 
1438
- removePositionItem.call(this, this._items.find((item) => item.position.position === position.position));
1425
+ removePositionItem.call(this, this._items.find((item) => item.position.position === position.position));
1439
1426
 
1440
- summaries.forEach((summary) => {
1441
- addSummaryCurrent(this._summariesCurrent, summary, this._currentSummaryFrame, this._currentSummaryRange);
1442
- addSummaryPrevious(this._summariesPrevious, summary, this._previousSummaryFrame, this._previousSummaryRanges);
1443
- });
1427
+ summaries.forEach((summary) => {
1428
+ addSummaryCurrent(this._summariesCurrent, summary, this._currentSummaryFrame, this._currentSummaryRange);
1429
+ addSummaryPrevious(this._summariesPrevious, summary, this._previousSummaryFrame, this._previousSummaryRanges);
1430
+ });
1444
1431
 
1445
- const item = createPositionItem.call(this, position);
1432
+ const item = createPositionItem.call(this, position);
1446
1433
 
1447
- addBarchartSymbol(this._symbols, item);
1448
- addDisplaySymbol(this._symbolsDisplay, item);
1434
+ addBarchartSymbol(this._symbols, item);
1435
+ addDisplaySymbol(this._symbolsDisplay, item);
1449
1436
 
1450
- this._items.push(item);
1437
+ this._items.push(item);
1451
1438
 
1452
- const createGroupOrInjectItem = (parentTree, treeDefinition, levelDefinitions) => {
1453
- if (levelDefinitions.length === 0) {
1454
- return;
1455
- }
1439
+ const createGroupOrInjectItem = (parentTree, treeDefinition, levelDefinitions) => {
1440
+ if (levelDefinitions.length === 0) {
1441
+ return;
1442
+ }
1456
1443
 
1457
- const levelDefinition = levelDefinitions[0];
1458
- const levelKey = levelDefinition.keySelector(item);
1444
+ const levelDefinition = levelDefinitions[0];
1445
+ const levelKey = levelDefinition.keySelector(item);
1459
1446
 
1460
- let groupTree;
1447
+ let groupTree;
1461
1448
 
1462
- if (parentTree.getChildren().length > 0) {
1463
- groupTree = parentTree.findChild(childGroup => childGroup.key === levelKey) || null;
1464
- } else {
1465
- groupTree = null;
1466
- }
1449
+ if (parentTree.getChildren().length > 0) {
1450
+ groupTree = parentTree.findChild(childGroup => childGroup.key === levelKey) || null;
1451
+ } else {
1452
+ groupTree = null;
1453
+ }
1467
1454
 
1468
- if (groupTree !== null) {
1469
- groupTree.getValue().addItem(item);
1455
+ if (groupTree !== null) {
1456
+ groupTree.getValue().addItem(item);
1470
1457
 
1471
- createGroupOrInjectItem(groupTree, treeDefinition, array.dropLeft(levelDefinitions));
1472
- } else {
1473
- createGroups.call(this, parentTree, [ item ], treeDefinition, levelDefinitions, [ ]);
1474
- }
1475
- };
1458
+ createGroupOrInjectItem(groupTree, treeDefinition, array.dropLeft(levelDefinitions));
1459
+ } else {
1460
+ createGroups.call(this, parentTree, [ item ], treeDefinition, levelDefinitions, [ ]);
1461
+ }
1462
+ };
1476
1463
 
1477
- this._definitions.forEach(definition => createGroupOrInjectItem(this._trees[definition.name], definition, definition.definitions));
1464
+ this._definitions.forEach(definition => createGroupOrInjectItem(this._trees[definition.name], definition, definition.definitions));
1478
1465
 
1479
- const addedBarchartSymbol = extractSymbolForBarchart(item.position);
1466
+ const addedBarchartSymbol = extractSymbolForBarchart(item.position);
1480
1467
 
1481
- if (addedBarchartSymbol !== null && !existingBarchartSymbols.some(existingBarchartSymbol => existingBarchartSymbol === addedBarchartSymbol)) {
1482
- this._positionSymbolAddedEvent.fire(addedBarchartSymbol);
1483
- }
1484
- });
1468
+ if (addedBarchartSymbol !== null && !existingBarchartSymbols.some(existingBarchartSymbol => existingBarchartSymbol === addedBarchartSymbol)) {
1469
+ this._positionSymbolAddedEvent.fire(addedBarchartSymbol);
1470
+ }
1485
1471
 
1486
1472
  recalculatePercentages.call(this);
1487
1473
  }
@@ -1532,22 +1518,55 @@ module.exports = (() => {
1532
1518
  }
1533
1519
 
1534
1520
  /**
1535
- * Updates the quote for a single symbol; causing updates to any grouping
1536
- * level that contains the position(s) for the symbol.
1521
+ * Performs a batch update of both position quotes and forex quotes,
1522
+ * triggering updates to position(s) and data aggregation(s).
1537
1523
  *
1538
1524
  * @public
1539
- * @param {String} symbol
1540
- * @param {Object} quote
1525
+ * @param {Array.<Object>} positionQuotes
1526
+ * @param {Array.<Object>} forexQuotes
1541
1527
  */
1542
- setPositionQuote(symbol, quote) {
1543
- assert.argumentIsRequired(symbol, 'symbol', String);
1544
- assert.argumentIsRequired(quote, 'quote', Object);
1528
+ setQuotes(positionQuotes, forexQuotes) {
1529
+ assert.argumentIsArray(positionQuotes, 'positionQuotes');
1530
+ assert.argumentIsArray(forexQuotes, 'forexQuotes');
1531
+
1532
+ if (positionQuotes.length !== 0) {
1533
+ positionQuotes.forEach((quote) => {
1534
+ const symbol = quote.symbol;
1545
1535
 
1546
- if (this._symbols.hasOwnProperty(symbol)) {
1547
- this._symbols[symbol].forEach(item => item.setQuote(quote));
1536
+ if (symbol) {
1537
+ if (this._symbols.hasOwnProperty(symbol)) {
1538
+ this._symbols[ symbol ].forEach(item => item.setQuote(quote));
1539
+ }
1540
+ }
1541
+ });
1548
1542
  }
1549
1543
 
1550
- recalculatePercentages.call(this);
1544
+ if (forexQuotes.length !== 0) {
1545
+ forexQuotes.forEach((quote) => {
1546
+ const symbol = quote.symbol;
1547
+
1548
+ if (symbol) {
1549
+ const rate = Rate.fromPair(quote.lastPrice, symbol);
1550
+ const index = this._forexQuotes.findIndex(existing => existing.formatPair() === rate.formatPair());
1551
+
1552
+ if (index < 0) {
1553
+ this._forexQuotes.push(rate);
1554
+ } else {
1555
+ this._forexQuotes[index] = rate;
1556
+ }
1557
+
1558
+ Object.keys(this._trees).forEach((key) => {
1559
+ this._trees[key].walk(group => group.setForexRates(this._forexQuotes), true, false);
1560
+ });
1561
+
1562
+ recalculatePercentages.call(this);
1563
+ }
1564
+ });
1565
+ }
1566
+
1567
+ if (positionQuotes.length !== 0 || forexQuotes.length !== 0) {
1568
+ recalculatePercentages.call(this);
1569
+ }
1551
1570
  }
1552
1571
 
1553
1572
  /**
@@ -1570,32 +1589,6 @@ module.exports = (() => {
1570
1589
  return this._forexQuotes;
1571
1590
  }
1572
1591
 
1573
- /**
1574
- * Updates the forex quote for a single currency pair; causing updates to
1575
- * any grouping level that contains that requires translation.
1576
- *
1577
- * @public
1578
- * @param {String} symbol
1579
- * @param {Object} quote
1580
- */
1581
- setForexQuote(symbol, quote) {
1582
- assert.argumentIsRequired(symbol, 'symbol', String);
1583
- assert.argumentIsRequired(quote, 'quote', Object);
1584
-
1585
- const rate = Rate.fromPair(quote.lastPrice, symbol);
1586
- const index = this._forexQuotes.findIndex(existing => existing.formatPair() === rate.formatPair());
1587
-
1588
- if (index < 0) {
1589
- this._forexQuotes.push(rate);
1590
- } else {
1591
- this._forexQuotes[index] = rate;
1592
- }
1593
-
1594
- Object.keys(this._trees).forEach(key => this._trees[key].walk(group => group.setForexRates(this._forexQuotes), true, false));
1595
-
1596
- recalculatePercentages.call(this);
1597
- }
1598
-
1599
1592
  /**
1600
1593
  * Updates fundamental data for a single symbol.
1601
1594
  *
@@ -1744,39 +1737,6 @@ module.exports = (() => {
1744
1737
  return this.getPositions(portfolio).find(p => p.position === position) || null;
1745
1738
  }
1746
1739
 
1747
- /**
1748
- * Pauses aggregation calculations during the processing of an action.
1749
- *
1750
- * @public
1751
- * @param {Function} executor
1752
- * @param {String=|Array.<String>=} names
1753
- */
1754
- startTransaction(executor, names) {
1755
- let namesToUse;
1756
-
1757
- if (is.array(names)) {
1758
- assert.argumentIsArray(names, 'names', String);
1759
-
1760
- namesToUse = names;
1761
- } else {
1762
- assert.argumentIsOptional(names, 'names', String);
1763
-
1764
- if (names) {
1765
- namesToUse = [ names ];
1766
- } else {
1767
- namesToUse = Object.keys(this._trees);
1768
- }
1769
- }
1770
-
1771
- assert.argumentIsRequired(executor, 'executor', Function);
1772
-
1773
- namesToUse.forEach((name) => this._trees[name].walk(group => group.setSuspended(true), false, false));
1774
-
1775
- executor(this);
1776
-
1777
- namesToUse.forEach((name) => this._trees[name].walk(group => group.setSuspended(false), false, false));
1778
- }
1779
-
1780
1740
  /**
1781
1741
  * Registers an observer for symbol addition (this occurs when a new position is added
1782
1742
  * for a symbol that does not already exist in the container). This event only fires
@@ -2183,7 +2143,6 @@ module.exports = (() => {
2183
2143
  this._aggregateCash = is.boolean(aggregateCash) && aggregateCash;
2184
2144
 
2185
2145
  this._excluded = false;
2186
- this._suspended = false;
2187
2146
  this._showClosedPositions = false;
2188
2147
 
2189
2148
  this._groupExcludedChangeEvent = new Event(this);
@@ -2384,10 +2343,6 @@ module.exports = (() => {
2384
2343
  return this._single;
2385
2344
  }
2386
2345
 
2387
- get suspended() {
2388
- return this._suspended;
2389
- }
2390
-
2391
2346
  /**
2392
2347
  * Indicates if the group should be excluded from higher-level aggregations.
2393
2348
  *
@@ -2510,24 +2465,6 @@ module.exports = (() => {
2510
2465
  }
2511
2466
  }
2512
2467
 
2513
- /**
2514
- * Stops (or starts) group-level aggregation calculations.
2515
- *
2516
- * @public
2517
- * @param {Boolean} value
2518
- */
2519
- setSuspended(value) {
2520
- assert.argumentIsRequired(value, 'value', Boolean);
2521
-
2522
- if (this._suspended !== value) {
2523
- this._suspended = value;
2524
-
2525
- if (!this._suspended) {
2526
- this.refresh();
2527
- }
2528
- }
2529
- }
2530
-
2531
2468
  /**
2532
2469
  * Updates the portfolio data. For example, a portfolio's name might change. This
2533
2470
  * function only affects {@link PositionLevelType.PORTFOLIO} groups.
@@ -2559,16 +2496,11 @@ module.exports = (() => {
2559
2496
  }
2560
2497
 
2561
2498
  /**
2562
- * Causes all aggregated data to be recalculated (assuming the group has not
2563
- * been suspended).
2499
+ * Causes all aggregated data to be recalculated.
2564
2500
  *
2565
2501
  * @public
2566
2502
  */
2567
2503
  refresh() {
2568
- if (this._suspended) {
2569
- return;
2570
- }
2571
-
2572
2504
  calculateStaticData(this, this._rates);
2573
2505
  calculatePriceData(this, this._rates, null, true);
2574
2506
  }
@@ -2772,10 +2704,6 @@ module.exports = (() => {
2772
2704
  }
2773
2705
 
2774
2706
  function calculateStaticData(group, rates) {
2775
- if (group.suspended) {
2776
- return;
2777
- }
2778
-
2779
2707
  const actual = group._dataActual;
2780
2708
  const format = group._dataFormat;
2781
2709
 
@@ -2872,10 +2800,6 @@ module.exports = (() => {
2872
2800
  }
2873
2801
 
2874
2802
  function calculatePriceData(group, rates, item, forceRefresh) {
2875
- if (group.suspended) {
2876
- return;
2877
- }
2878
-
2879
2803
  const currency = group.currency;
2880
2804
 
2881
2805
  const actual = group._dataActual;
@@ -2961,10 +2885,6 @@ module.exports = (() => {
2961
2885
  }
2962
2886
 
2963
2887
  function calculateMarketPercent(group, rates, parentGroup, portfolioGroup) {
2964
- if (group.suspended) {
2965
- return;
2966
- }
2967
-
2968
2888
  const actual = group._dataActual;
2969
2889
  const format = group._dataFormat;
2970
2890
  const excluded = group._excluded;
@@ -9173,14 +9093,6 @@ describe('When validating transaction order', () => {
9173
9093
  it('An array of transactions with ordered sequences, on the reversed days should not be valid', () => {
9174
9094
  expect(TransactionValidator.validateOrder([ build(1, '2018-05-02'), build(2, '2018-05-01'), build(3, '2018-04-30') ])).toEqual(false);
9175
9095
  });
9176
-
9177
- it('A partial array of transactions with ordered sequences (starting after one), on the same day should be valid', () => {
9178
- expect(TransactionValidator.validateOrder([ build(3, '2018-04-30'), build(4, '2018-04-30'), build(5, '2018-04-30') ], true)).toEqual(true);
9179
- });
9180
-
9181
- it('A partial array of transactions with gap in sequences (starting after one), on the same day should be not valid', () => {
9182
- expect(TransactionValidator.validateOrder([ build(3, '2018-04-30'), build(5, '2018-04-30'), build(6, '2018-04-30') ], true)).toEqual(false);
9183
- });
9184
9096
  });
9185
9097
 
9186
9098
  },{"./../../../lib/data/TransactionValidator":5,"@barchart/common-js/lang/Day":18}],37:[function(require,module,exports){
@@ -40,12 +40,4 @@ describe('When validating transaction order', () => {
40
40
  it('An array of transactions with ordered sequences, on the reversed days should not be valid', () => {
41
41
  expect(TransactionValidator.validateOrder([ build(1, '2018-05-02'), build(2, '2018-05-01'), build(3, '2018-04-30') ])).toEqual(false);
42
42
  });
43
-
44
- it('A partial array of transactions with ordered sequences (starting after one), on the same day should be valid', () => {
45
- expect(TransactionValidator.validateOrder([ build(3, '2018-04-30'), build(4, '2018-04-30'), build(5, '2018-04-30') ], true)).toEqual(true);
46
- });
47
-
48
- it('A partial array of transactions with gap in sequences (starting after one), on the same day should be not valid', () => {
49
- expect(TransactionValidator.validateOrder([ build(3, '2018-04-30'), build(5, '2018-04-30'), build(6, '2018-04-30') ], true)).toEqual(false);
50
- });
51
43
  });