@barchart/portfolio-api-common 1.0.241 → 1.0.245

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.
@@ -132,7 +132,9 @@ module.exports = (() => {
132
132
  this._forexQuotes = this._forexSymbols.map((symbol) => {
133
133
  return Rate.fromPair(Decimal.ONE, symbol);
134
134
  });
135
-
135
+
136
+ this._nodes = { };
137
+
136
138
  this._trees = this._definitions.reduce((map, treeDefinition) => {
137
139
  const tree = new Tree();
138
140
 
@@ -247,7 +249,7 @@ module.exports = (() => {
247
249
  Object.keys(this._trees).forEach((key) => {
248
250
  this._trees[key].walk((group, groupNode) => {
249
251
  if (group.definition.type === PositionLevelType.PORTFOLIO && group.key === PositionLevelDefinition.getKeyForPortfolioGroup(portfolio)) {
250
- groupNode.sever();
252
+ severGroupNode.call(this, groupNode);
251
253
  }
252
254
  }, true, false);
253
255
  });
@@ -510,6 +512,32 @@ module.exports = (() => {
510
512
  return findNode(this._trees[name], keys).getChildren().map(node => node.getValue());
511
513
  }
512
514
 
515
+ /**
516
+ * Returns the immediate parent {@link PositionGroup} of a {@link PositionGroup}.
517
+ *
518
+ * @public
519
+ * @param {PositionGroup} position
520
+ * @returns {PositionGroup|null}
521
+ */
522
+ getParentGroup(group) {
523
+ assert.argumentIsRequired(group, 'group', PositionGroup, 'PositionGroup');
524
+
525
+ return findParentGroup.call(this, group, candidate => true);
526
+ }
527
+
528
+ /**
529
+ * Returns the a parent {@link PositionGroup} which represents a portfolio.
530
+ *
531
+ * @public
532
+ * @param {PositionGroup} position
533
+ * @returns {PositionGroup|null}
534
+ */
535
+ getParentGroupForPortfolio(group) {
536
+ assert.argumentIsRequired(group, 'group', PositionGroup, 'PositionGroup');
537
+
538
+ return findParentGroup.call(this, group, candidate => candidate.definition.type === PositionLevelType.PORTFOLIO);
539
+ }
540
+
513
541
  /**
514
542
  * Returns all portfolios in the container.
515
543
  *
@@ -536,6 +564,20 @@ module.exports = (() => {
536
564
  });
537
565
  }
538
566
 
567
+ /**
568
+ * Returns a single position for a portfolio.
569
+ *
570
+ * @public
571
+ * @param {String} portfolio
572
+ * @param {String} position
573
+ * @return {Object|null}
574
+ */
575
+ getPosition(portfolio, position) {
576
+ assert.argumentIsRequired(position, 'position', String);
577
+
578
+ return this.getPositions(portfolio).find(p => p.position === position) || null;
579
+ }
580
+
539
581
  /**
540
582
  * Pauses aggregation calculations during the processing of an action.
541
583
  *
@@ -603,6 +645,22 @@ module.exports = (() => {
603
645
  return keys.reduce((tree, key) => tree.findChild(group => group.key === key), tree);
604
646
  }
605
647
 
648
+ function findParentGroup(group, predicate) {
649
+ const groupNode = this._nodes[group.id];
650
+
651
+ let returnRef = null;
652
+
653
+ if (groupNode) {
654
+ const resultNode = groupNode.findParent((candidateGroup, candidateNode) => !candidateNode.getIsRoot() && predicate(candidateGroup));
655
+
656
+ if (resultNode) {
657
+ returnRef = resultNode.getValue();
658
+ }
659
+ }
660
+
661
+ return returnRef;
662
+ }
663
+
606
664
  function extractSymbolForBarchart(position) {
607
665
  if (position.instrument && position.instrument.symbol && position.instrument.symbol.barchart) {
608
666
  return position.instrument.symbol.barchart;
@@ -694,8 +752,6 @@ module.exports = (() => {
694
752
  return;
695
753
  }
696
754
 
697
- const parent = parentTree.getValue() || null;
698
-
699
755
  const levelDefinition = levelDefinitions[0];
700
756
 
701
757
  const populatedObjects = array.groupBy(items, levelDefinition.keySelector);
@@ -703,7 +759,7 @@ module.exports = (() => {
703
759
  const items = populatedObjects[key];
704
760
  const first = items[0];
705
761
 
706
- list.push(new PositionGroup(this, parent, levelDefinition, items, levelDefinition.currencySelector(first), key, levelDefinition.descriptionSelector(first), levelDefinition.aggregateCash));
762
+ list.push(new PositionGroup(this, levelDefinition, items, levelDefinition.currencySelector(first), key, levelDefinition.descriptionSelector(first), levelDefinition.aggregateCash));
707
763
 
708
764
  return list;
709
765
  }, [ ]);
@@ -716,7 +772,7 @@ module.exports = (() => {
716
772
  });
717
773
 
718
774
  const empty = missingGroups.map((group) => {
719
- return new PositionGroup(this, parent, levelDefinition, [ ], group.currency, group.key, group.description);
775
+ return new PositionGroup(this, levelDefinition, [ ], group.currency, group.key, group.description);
720
776
  });
721
777
 
722
778
  const compositeGroups = populatedGroups.concat(empty);
@@ -754,6 +810,8 @@ module.exports = (() => {
754
810
  compositeGroups.forEach((group) => {
755
811
  const childTree = parentTree.addChild(group);
756
812
 
813
+ this._nodes[group.id] = childTree;
814
+
757
815
  initializeGroupObservers.call(this, childTree, treeDefinition);
758
816
 
759
817
  createGroups.call(this, childTree, group.items, treeDefinition, array.dropLeft(levelDefinitions));
@@ -881,7 +939,7 @@ module.exports = (() => {
881
939
  Object.keys(this._trees).forEach((key) => {
882
940
  this._trees[key].walk((group, groupNode) => {
883
941
  if (group.definition.type === PositionLevelType.POSITION && group.key === positionItem.position.position) {
884
- groupNode.sever();
942
+ severGroupNode.call(this, groupNode);
885
943
  }
886
944
  }, true, false);
887
945
  });
@@ -889,5 +947,10 @@ module.exports = (() => {
889
947
  positionItem.dispose();
890
948
  }
891
949
 
950
+ function severGroupNode(groupNodeToSever) {
951
+ groupNodeToSever.sever();
952
+ groupNodeToSever.walk(group => delete this._nodes[group.id], false, true);
953
+ }
954
+
892
955
  return PositionContainer;
893
956
  })();
@@ -25,7 +25,6 @@ module.exports = (() => {
25
25
  *
26
26
  * @public
27
27
  * @param {PositionContainer} container
28
- * @param {PositionGroup|null} parent
29
28
  * @param {LevelDefinition} definition
30
29
  * @param {Array.<PositionItem>} items
31
30
  * @param {Currency} currency
@@ -34,12 +33,11 @@ module.exports = (() => {
34
33
  * @param {Boolean=} aggregateCash
35
34
  */
36
35
  class PositionGroup {
37
- constructor(container, parent, definition, items, currency, key, description, aggregateCash) {
36
+ constructor(container, definition, items, currency, key, description, aggregateCash) {
38
37
  this._id = counter++;
39
38
 
40
39
  this._definition = definition;
41
40
  this._container = container;
42
- this._parent = parent || null;
43
41
 
44
42
  this._items = items;
45
43
  this._currency = currency || Currency.CAD;
@@ -122,6 +120,7 @@ module.exports = (() => {
122
120
  this._dataActual.income = null;
123
121
  this._dataActual.market = null;
124
122
  this._dataActual.marketPercent = null;
123
+ this._dataActual.marketPercentPortfolio = null;
125
124
  this._dataActual.unrealized = null;
126
125
  this._dataActual.unrealizedToday = null;
127
126
  this._dataActual.total = null;
@@ -137,6 +136,7 @@ module.exports = (() => {
137
136
  this._dataFormat.income = null;
138
137
  this._dataFormat.market = null;
139
138
  this._dataFormat.marketPercent = null;
139
+ this._dataFormat.marketPercentPortfolio = null;
140
140
  this._dataFormat.marketDirection = null;
141
141
  this._dataFormat.unrealized = null;
142
142
  this._dataFormat.unrealizedPercent = null;
@@ -146,7 +146,7 @@ module.exports = (() => {
146
146
  this._dataFormat.total = null;
147
147
  this._dataFormat.totalNegative = false;
148
148
  this._dataFormat.summaryTotalCurrent = null;
149
- this._dataActual.summaryTotalCurrentNegative = false;
149
+ this._dataFormat.summaryTotalCurrentNegative = false;
150
150
  this._dataFormat.summaryTotalPrevious = null;
151
151
  this._dataFormat.summaryTotalPreviousNegative = false;
152
152
  this._dataFormat.summaryTotalPrevious2 = null;
@@ -670,7 +670,6 @@ module.exports = (() => {
670
670
  return;
671
671
  }
672
672
 
673
- const parent = group._parent;
674
673
  const currency = group.currency;
675
674
 
676
675
  const actual = group._dataActual;
@@ -761,38 +760,44 @@ module.exports = (() => {
761
760
  return;
762
761
  }
763
762
 
764
- const parent = group._parent;
765
- const excluded = group._excluded;
766
-
767
763
  const actual = group._dataActual;
768
764
  const format = group._dataFormat;
769
-
770
- let marketPercent;
771
-
772
- if (parent !== null && !excluded) {
773
- const parentData = parent._dataActual;
765
+ const excluded = group._excluded;
766
+
767
+ const portfolioParent = group._container.getParentGroupForPortfolio(group);
768
+
769
+ const calculatePercent = (parent) => {
770
+ let marketPercent;
771
+
772
+ if (parent !== null && !excluded) {
773
+ const parentData = parent._dataActual;
774
+
775
+ if (parentData.marketAbsolute !== null && !parentData.marketAbsolute.getIsZero()) {
776
+ let numerator;
774
777
 
775
- if (parentData.marketAbsolute !== null && !parentData.marketAbsolute.getIsZero()) {
776
- let numerator;
778
+ if (group.currency !== parent.currency) {
779
+ numerator = Rate.convert(actual.marketAbsolute, group.currency, parent.currency, ...rates);
780
+ } else {
781
+ numerator = actual.marketAbsolute;
782
+ }
777
783
 
778
- if (group.currency !== parent.currency) {
779
- numerator = Rate.convert(actual.marketAbsolute, group.currency, parent.currency, ...rates);
784
+ marketPercent = numerator.divide(parentData.marketAbsolute);
780
785
  } else {
781
- numerator = actual.marketAbsolute;
786
+ marketPercent = null;
782
787
  }
783
-
784
- marketPercent = numerator.divide(parentData.marketAbsolute);
785
788
  } else {
786
789
  marketPercent = null;
787
790
  }
788
- } else {
789
- marketPercent = null;
790
- }
791
791
 
792
- actual.marketPercent = marketPercent;
793
-
792
+ return marketPercent;
793
+ };
794
+
795
+ actual.marketPercent = calculatePercent(group._container.getParentGroup(group));
794
796
  format.marketPercent = formatPercent(actual.marketPercent, 2);
795
-
797
+
798
+ actual.marketPercentPortfolio = calculatePercent(group._container.getParentGroupForPortfolio(group));
799
+ format.marketPercentPortfolio = formatPercent(actual.marketPercentPortfolio, 2);
800
+
796
801
  if (!silent) {
797
802
  group._marketPercentChangeEvent.fire(group);
798
803
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barchart/portfolio-api-common",
3
- "version": "1.0.241",
3
+ "version": "1.0.245",
4
4
  "description": "Common classes used by the Portfolio system",
5
5
  "author": {
6
6
  "name": "Bryan Ingle",
@@ -931,7 +931,9 @@ module.exports = (() => {
931
931
  this._forexQuotes = this._forexSymbols.map((symbol) => {
932
932
  return Rate.fromPair(Decimal.ONE, symbol);
933
933
  });
934
-
934
+
935
+ this._nodes = { };
936
+
935
937
  this._trees = this._definitions.reduce((map, treeDefinition) => {
936
938
  const tree = new Tree();
937
939
 
@@ -1046,7 +1048,7 @@ module.exports = (() => {
1046
1048
  Object.keys(this._trees).forEach((key) => {
1047
1049
  this._trees[key].walk((group, groupNode) => {
1048
1050
  if (group.definition.type === PositionLevelType.PORTFOLIO && group.key === PositionLevelDefinition.getKeyForPortfolioGroup(portfolio)) {
1049
- groupNode.sever();
1051
+ severGroupNode.call(this, groupNode);
1050
1052
  }
1051
1053
  }, true, false);
1052
1054
  });
@@ -1309,6 +1311,32 @@ module.exports = (() => {
1309
1311
  return findNode(this._trees[name], keys).getChildren().map(node => node.getValue());
1310
1312
  }
1311
1313
 
1314
+ /**
1315
+ * Returns the immediate parent {@link PositionGroup} of a {@link PositionGroup}.
1316
+ *
1317
+ * @public
1318
+ * @param {PositionGroup} position
1319
+ * @returns {PositionGroup|null}
1320
+ */
1321
+ getParentGroup(group) {
1322
+ assert.argumentIsRequired(group, 'group', PositionGroup, 'PositionGroup');
1323
+
1324
+ return findParentGroup.call(this, group, candidate => true);
1325
+ }
1326
+
1327
+ /**
1328
+ * Returns the a parent {@link PositionGroup} which represents a portfolio.
1329
+ *
1330
+ * @public
1331
+ * @param {PositionGroup} position
1332
+ * @returns {PositionGroup|null}
1333
+ */
1334
+ getParentGroupForPortfolio(group) {
1335
+ assert.argumentIsRequired(group, 'group', PositionGroup, 'PositionGroup');
1336
+
1337
+ return findParentGroup.call(this, group, candidate => candidate.definition.type === PositionLevelType.PORTFOLIO);
1338
+ }
1339
+
1312
1340
  /**
1313
1341
  * Returns all portfolios in the container.
1314
1342
  *
@@ -1335,6 +1363,20 @@ module.exports = (() => {
1335
1363
  });
1336
1364
  }
1337
1365
 
1366
+ /**
1367
+ * Returns a single position for a portfolio.
1368
+ *
1369
+ * @public
1370
+ * @param {String} portfolio
1371
+ * @param {String} position
1372
+ * @return {Object|null}
1373
+ */
1374
+ getPosition(portfolio, position) {
1375
+ assert.argumentIsRequired(position, 'position', String);
1376
+
1377
+ return this.getPositions(portfolio).find(p => p.position === position) || null;
1378
+ }
1379
+
1338
1380
  /**
1339
1381
  * Pauses aggregation calculations during the processing of an action.
1340
1382
  *
@@ -1402,6 +1444,22 @@ module.exports = (() => {
1402
1444
  return keys.reduce((tree, key) => tree.findChild(group => group.key === key), tree);
1403
1445
  }
1404
1446
 
1447
+ function findParentGroup(group, predicate) {
1448
+ const groupNode = this._nodes[group.id];
1449
+
1450
+ let returnRef = null;
1451
+
1452
+ if (groupNode) {
1453
+ const resultNode = groupNode.findParent((candidateGroup, candidateNode) => !candidateNode.getIsRoot() && predicate(candidateGroup));
1454
+
1455
+ if (resultNode) {
1456
+ returnRef = resultNode.getValue();
1457
+ }
1458
+ }
1459
+
1460
+ return returnRef;
1461
+ }
1462
+
1405
1463
  function extractSymbolForBarchart(position) {
1406
1464
  if (position.instrument && position.instrument.symbol && position.instrument.symbol.barchart) {
1407
1465
  return position.instrument.symbol.barchart;
@@ -1493,8 +1551,6 @@ module.exports = (() => {
1493
1551
  return;
1494
1552
  }
1495
1553
 
1496
- const parent = parentTree.getValue() || null;
1497
-
1498
1554
  const levelDefinition = levelDefinitions[0];
1499
1555
 
1500
1556
  const populatedObjects = array.groupBy(items, levelDefinition.keySelector);
@@ -1502,7 +1558,7 @@ module.exports = (() => {
1502
1558
  const items = populatedObjects[key];
1503
1559
  const first = items[0];
1504
1560
 
1505
- list.push(new PositionGroup(this, parent, levelDefinition, items, levelDefinition.currencySelector(first), key, levelDefinition.descriptionSelector(first), levelDefinition.aggregateCash));
1561
+ list.push(new PositionGroup(this, levelDefinition, items, levelDefinition.currencySelector(first), key, levelDefinition.descriptionSelector(first), levelDefinition.aggregateCash));
1506
1562
 
1507
1563
  return list;
1508
1564
  }, [ ]);
@@ -1515,7 +1571,7 @@ module.exports = (() => {
1515
1571
  });
1516
1572
 
1517
1573
  const empty = missingGroups.map((group) => {
1518
- return new PositionGroup(this, parent, levelDefinition, [ ], group.currency, group.key, group.description);
1574
+ return new PositionGroup(this, levelDefinition, [ ], group.currency, group.key, group.description);
1519
1575
  });
1520
1576
 
1521
1577
  const compositeGroups = populatedGroups.concat(empty);
@@ -1553,6 +1609,8 @@ module.exports = (() => {
1553
1609
  compositeGroups.forEach((group) => {
1554
1610
  const childTree = parentTree.addChild(group);
1555
1611
 
1612
+ this._nodes[group.id] = childTree;
1613
+
1556
1614
  initializeGroupObservers.call(this, childTree, treeDefinition);
1557
1615
 
1558
1616
  createGroups.call(this, childTree, group.items, treeDefinition, array.dropLeft(levelDefinitions));
@@ -1680,7 +1738,7 @@ module.exports = (() => {
1680
1738
  Object.keys(this._trees).forEach((key) => {
1681
1739
  this._trees[key].walk((group, groupNode) => {
1682
1740
  if (group.definition.type === PositionLevelType.POSITION && group.key === positionItem.position.position) {
1683
- groupNode.sever();
1741
+ severGroupNode.call(this, groupNode);
1684
1742
  }
1685
1743
  }, true, false);
1686
1744
  });
@@ -1688,6 +1746,11 @@ module.exports = (() => {
1688
1746
  positionItem.dispose();
1689
1747
  }
1690
1748
 
1749
+ function severGroupNode(groupNodeToSever) {
1750
+ groupNodeToSever.sever();
1751
+ groupNodeToSever.walk(group => delete this._nodes[group.id], false, true);
1752
+ }
1753
+
1691
1754
  return PositionContainer;
1692
1755
  })();
1693
1756
 
@@ -1719,7 +1782,6 @@ module.exports = (() => {
1719
1782
  *
1720
1783
  * @public
1721
1784
  * @param {PositionContainer} container
1722
- * @param {PositionGroup|null} parent
1723
1785
  * @param {LevelDefinition} definition
1724
1786
  * @param {Array.<PositionItem>} items
1725
1787
  * @param {Currency} currency
@@ -1728,12 +1790,11 @@ module.exports = (() => {
1728
1790
  * @param {Boolean=} aggregateCash
1729
1791
  */
1730
1792
  class PositionGroup {
1731
- constructor(container, parent, definition, items, currency, key, description, aggregateCash) {
1793
+ constructor(container, definition, items, currency, key, description, aggregateCash) {
1732
1794
  this._id = counter++;
1733
1795
 
1734
1796
  this._definition = definition;
1735
1797
  this._container = container;
1736
- this._parent = parent || null;
1737
1798
 
1738
1799
  this._items = items;
1739
1800
  this._currency = currency || Currency.CAD;
@@ -1816,6 +1877,7 @@ module.exports = (() => {
1816
1877
  this._dataActual.income = null;
1817
1878
  this._dataActual.market = null;
1818
1879
  this._dataActual.marketPercent = null;
1880
+ this._dataActual.marketPercentPortfolio = null;
1819
1881
  this._dataActual.unrealized = null;
1820
1882
  this._dataActual.unrealizedToday = null;
1821
1883
  this._dataActual.total = null;
@@ -1831,6 +1893,7 @@ module.exports = (() => {
1831
1893
  this._dataFormat.income = null;
1832
1894
  this._dataFormat.market = null;
1833
1895
  this._dataFormat.marketPercent = null;
1896
+ this._dataFormat.marketPercentPortfolio = null;
1834
1897
  this._dataFormat.marketDirection = null;
1835
1898
  this._dataFormat.unrealized = null;
1836
1899
  this._dataFormat.unrealizedPercent = null;
@@ -1840,7 +1903,7 @@ module.exports = (() => {
1840
1903
  this._dataFormat.total = null;
1841
1904
  this._dataFormat.totalNegative = false;
1842
1905
  this._dataFormat.summaryTotalCurrent = null;
1843
- this._dataActual.summaryTotalCurrentNegative = false;
1906
+ this._dataFormat.summaryTotalCurrentNegative = false;
1844
1907
  this._dataFormat.summaryTotalPrevious = null;
1845
1908
  this._dataFormat.summaryTotalPreviousNegative = false;
1846
1909
  this._dataFormat.summaryTotalPrevious2 = null;
@@ -2364,7 +2427,6 @@ module.exports = (() => {
2364
2427
  return;
2365
2428
  }
2366
2429
 
2367
- const parent = group._parent;
2368
2430
  const currency = group.currency;
2369
2431
 
2370
2432
  const actual = group._dataActual;
@@ -2455,38 +2517,44 @@ module.exports = (() => {
2455
2517
  return;
2456
2518
  }
2457
2519
 
2458
- const parent = group._parent;
2459
- const excluded = group._excluded;
2460
-
2461
2520
  const actual = group._dataActual;
2462
2521
  const format = group._dataFormat;
2463
-
2464
- let marketPercent;
2465
-
2466
- if (parent !== null && !excluded) {
2467
- const parentData = parent._dataActual;
2522
+ const excluded = group._excluded;
2523
+
2524
+ const portfolioParent = group._container.getParentGroupForPortfolio(group);
2468
2525
 
2469
- if (parentData.marketAbsolute !== null && !parentData.marketAbsolute.getIsZero()) {
2470
- let numerator;
2526
+ const calculatePercent = (parent) => {
2527
+ let marketPercent;
2471
2528
 
2472
- if (group.currency !== parent.currency) {
2473
- numerator = Rate.convert(actual.marketAbsolute, group.currency, parent.currency, ...rates);
2529
+ if (parent !== null && !excluded) {
2530
+ const parentData = parent._dataActual;
2531
+
2532
+ if (parentData.marketAbsolute !== null && !parentData.marketAbsolute.getIsZero()) {
2533
+ let numerator;
2534
+
2535
+ if (group.currency !== parent.currency) {
2536
+ numerator = Rate.convert(actual.marketAbsolute, group.currency, parent.currency, ...rates);
2537
+ } else {
2538
+ numerator = actual.marketAbsolute;
2539
+ }
2540
+
2541
+ marketPercent = numerator.divide(parentData.marketAbsolute);
2474
2542
  } else {
2475
- numerator = actual.marketAbsolute;
2543
+ marketPercent = null;
2476
2544
  }
2477
-
2478
- marketPercent = numerator.divide(parentData.marketAbsolute);
2479
2545
  } else {
2480
2546
  marketPercent = null;
2481
2547
  }
2482
- } else {
2483
- marketPercent = null;
2484
- }
2485
2548
 
2486
- actual.marketPercent = marketPercent;
2487
-
2549
+ return marketPercent;
2550
+ };
2551
+
2552
+ actual.marketPercent = calculatePercent(group._container.getParentGroup(group));
2488
2553
  format.marketPercent = formatPercent(actual.marketPercent, 2);
2489
-
2554
+
2555
+ actual.marketPercentPortfolio = calculatePercent(group._container.getParentGroupForPortfolio(group));
2556
+ format.marketPercentPortfolio = formatPercent(actual.marketPercentPortfolio, 2);
2557
+
2490
2558
  if (!silent) {
2491
2559
  group._marketPercentChangeEvent.fire(group);
2492
2560
  }