@barchart/portfolio-api-common 1.0.239 → 1.0.243

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.
@@ -37,7 +37,7 @@ module.exports = (() => {
37
37
  this._income = income;
38
38
  this._opening = opening;
39
39
  this._closing = closing;
40
- this._fee = fee
40
+ this._fee = fee;
41
41
  }
42
42
 
43
43
  /**
@@ -118,7 +118,7 @@ module.exports = (() => {
118
118
  formatters.set(TransactionType.DIVIDEND, (t) => {
119
119
  return {
120
120
  shares: t.snapshot.open,
121
- total: t.dividend.amout,
121
+ total: t.dividend.amount,
122
122
  rate: t.dividend.rate
123
123
  };
124
124
  });
@@ -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
  *
@@ -603,6 +631,22 @@ module.exports = (() => {
603
631
  return keys.reduce((tree, key) => tree.findChild(group => group.key === key), tree);
604
632
  }
605
633
 
634
+ function findParentGroup(group, predicate) {
635
+ const groupNode = this._nodes[group.id];
636
+
637
+ let returnRef = null;
638
+
639
+ if (groupNode) {
640
+ const resultNode = groupNode.findParent(candidate => predicate(candidate));
641
+
642
+ if (resultNode) {
643
+ returnRef = resultNode.getValue();
644
+ }
645
+ }
646
+
647
+ return returnRef;
648
+ }
649
+
606
650
  function extractSymbolForBarchart(position) {
607
651
  if (position.instrument && position.instrument.symbol && position.instrument.symbol.barchart) {
608
652
  return position.instrument.symbol.barchart;
@@ -694,8 +738,6 @@ module.exports = (() => {
694
738
  return;
695
739
  }
696
740
 
697
- const parent = parentTree.getValue() || null;
698
-
699
741
  const levelDefinition = levelDefinitions[0];
700
742
 
701
743
  const populatedObjects = array.groupBy(items, levelDefinition.keySelector);
@@ -703,7 +745,7 @@ module.exports = (() => {
703
745
  const items = populatedObjects[key];
704
746
  const first = items[0];
705
747
 
706
- list.push(new PositionGroup(this, parent, levelDefinition, items, levelDefinition.currencySelector(first), key, levelDefinition.descriptionSelector(first), levelDefinition.aggregateCash));
748
+ list.push(new PositionGroup(this, levelDefinition, items, levelDefinition.currencySelector(first), key, levelDefinition.descriptionSelector(first), levelDefinition.aggregateCash));
707
749
 
708
750
  return list;
709
751
  }, [ ]);
@@ -716,7 +758,7 @@ module.exports = (() => {
716
758
  });
717
759
 
718
760
  const empty = missingGroups.map((group) => {
719
- return new PositionGroup(this, parent, levelDefinition, [ ], group.currency, group.key, group.description);
761
+ return new PositionGroup(this, levelDefinition, [ ], group.currency, group.key, group.description);
720
762
  });
721
763
 
722
764
  const compositeGroups = populatedGroups.concat(empty);
@@ -754,6 +796,8 @@ module.exports = (() => {
754
796
  compositeGroups.forEach((group) => {
755
797
  const childTree = parentTree.addChild(group);
756
798
 
799
+ this._nodes[group.id] = childTree;
800
+
757
801
  initializeGroupObservers.call(this, childTree, treeDefinition);
758
802
 
759
803
  createGroups.call(this, childTree, group.items, treeDefinition, array.dropLeft(levelDefinitions));
@@ -881,7 +925,7 @@ module.exports = (() => {
881
925
  Object.keys(this._trees).forEach((key) => {
882
926
  this._trees[key].walk((group, groupNode) => {
883
927
  if (group.definition.type === PositionLevelType.POSITION && group.key === positionItem.position.position) {
884
- groupNode.sever();
928
+ severGroupNode.call(this, groupNode);
885
929
  }
886
930
  }, true, false);
887
931
  });
@@ -889,5 +933,10 @@ module.exports = (() => {
889
933
  positionItem.dispose();
890
934
  }
891
935
 
936
+ function severGroupNode(groupNodeToSever) {
937
+ groupNodeToSever.sever();
938
+ groupNodeToSever.walk(group => delete this._nodes[group.id], false, true);
939
+ }
940
+
892
941
  return PositionContainer;
893
942
  })();
@@ -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.239",
3
+ "version": "1.0.243",
4
4
  "description": "Common classes used by the Portfolio system",
5
5
  "author": {
6
6
  "name": "Bryan Ingle",
@@ -482,7 +482,7 @@ module.exports = (() => {
482
482
  this._income = income;
483
483
  this._opening = opening;
484
484
  this._closing = closing;
485
- this._fee = fee
485
+ this._fee = fee;
486
486
  }
487
487
 
488
488
  /**
@@ -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
  *
@@ -1402,6 +1430,22 @@ module.exports = (() => {
1402
1430
  return keys.reduce((tree, key) => tree.findChild(group => group.key === key), tree);
1403
1431
  }
1404
1432
 
1433
+ function findParentGroup(group, predicate) {
1434
+ const groupNode = this._nodes[group.id];
1435
+
1436
+ let returnRef = null;
1437
+
1438
+ if (groupNode) {
1439
+ const resultNode = groupNode.findParent(candidate => predicate(candidate));
1440
+
1441
+ if (resultNode) {
1442
+ returnRef = resultNode.getValue();
1443
+ }
1444
+ }
1445
+
1446
+ return returnRef;
1447
+ }
1448
+
1405
1449
  function extractSymbolForBarchart(position) {
1406
1450
  if (position.instrument && position.instrument.symbol && position.instrument.symbol.barchart) {
1407
1451
  return position.instrument.symbol.barchart;
@@ -1493,8 +1537,6 @@ module.exports = (() => {
1493
1537
  return;
1494
1538
  }
1495
1539
 
1496
- const parent = parentTree.getValue() || null;
1497
-
1498
1540
  const levelDefinition = levelDefinitions[0];
1499
1541
 
1500
1542
  const populatedObjects = array.groupBy(items, levelDefinition.keySelector);
@@ -1502,7 +1544,7 @@ module.exports = (() => {
1502
1544
  const items = populatedObjects[key];
1503
1545
  const first = items[0];
1504
1546
 
1505
- list.push(new PositionGroup(this, parent, levelDefinition, items, levelDefinition.currencySelector(first), key, levelDefinition.descriptionSelector(first), levelDefinition.aggregateCash));
1547
+ list.push(new PositionGroup(this, levelDefinition, items, levelDefinition.currencySelector(first), key, levelDefinition.descriptionSelector(first), levelDefinition.aggregateCash));
1506
1548
 
1507
1549
  return list;
1508
1550
  }, [ ]);
@@ -1515,7 +1557,7 @@ module.exports = (() => {
1515
1557
  });
1516
1558
 
1517
1559
  const empty = missingGroups.map((group) => {
1518
- return new PositionGroup(this, parent, levelDefinition, [ ], group.currency, group.key, group.description);
1560
+ return new PositionGroup(this, levelDefinition, [ ], group.currency, group.key, group.description);
1519
1561
  });
1520
1562
 
1521
1563
  const compositeGroups = populatedGroups.concat(empty);
@@ -1553,6 +1595,8 @@ module.exports = (() => {
1553
1595
  compositeGroups.forEach((group) => {
1554
1596
  const childTree = parentTree.addChild(group);
1555
1597
 
1598
+ this._nodes[group.id] = childTree;
1599
+
1556
1600
  initializeGroupObservers.call(this, childTree, treeDefinition);
1557
1601
 
1558
1602
  createGroups.call(this, childTree, group.items, treeDefinition, array.dropLeft(levelDefinitions));
@@ -1680,7 +1724,7 @@ module.exports = (() => {
1680
1724
  Object.keys(this._trees).forEach((key) => {
1681
1725
  this._trees[key].walk((group, groupNode) => {
1682
1726
  if (group.definition.type === PositionLevelType.POSITION && group.key === positionItem.position.position) {
1683
- groupNode.sever();
1727
+ severGroupNode.call(this, groupNode);
1684
1728
  }
1685
1729
  }, true, false);
1686
1730
  });
@@ -1688,6 +1732,11 @@ module.exports = (() => {
1688
1732
  positionItem.dispose();
1689
1733
  }
1690
1734
 
1735
+ function severGroupNode(groupNodeToSever) {
1736
+ groupNodeToSever.sever();
1737
+ groupNodeToSever.walk(group => delete this._nodes[group.id], false, true);
1738
+ }
1739
+
1691
1740
  return PositionContainer;
1692
1741
  })();
1693
1742
 
@@ -1719,7 +1768,6 @@ module.exports = (() => {
1719
1768
  *
1720
1769
  * @public
1721
1770
  * @param {PositionContainer} container
1722
- * @param {PositionGroup|null} parent
1723
1771
  * @param {LevelDefinition} definition
1724
1772
  * @param {Array.<PositionItem>} items
1725
1773
  * @param {Currency} currency
@@ -1728,12 +1776,11 @@ module.exports = (() => {
1728
1776
  * @param {Boolean=} aggregateCash
1729
1777
  */
1730
1778
  class PositionGroup {
1731
- constructor(container, parent, definition, items, currency, key, description, aggregateCash) {
1779
+ constructor(container, definition, items, currency, key, description, aggregateCash) {
1732
1780
  this._id = counter++;
1733
1781
 
1734
1782
  this._definition = definition;
1735
1783
  this._container = container;
1736
- this._parent = parent || null;
1737
1784
 
1738
1785
  this._items = items;
1739
1786
  this._currency = currency || Currency.CAD;
@@ -1816,6 +1863,7 @@ module.exports = (() => {
1816
1863
  this._dataActual.income = null;
1817
1864
  this._dataActual.market = null;
1818
1865
  this._dataActual.marketPercent = null;
1866
+ this._dataActual.marketPercentPortfolio = null;
1819
1867
  this._dataActual.unrealized = null;
1820
1868
  this._dataActual.unrealizedToday = null;
1821
1869
  this._dataActual.total = null;
@@ -1831,6 +1879,7 @@ module.exports = (() => {
1831
1879
  this._dataFormat.income = null;
1832
1880
  this._dataFormat.market = null;
1833
1881
  this._dataFormat.marketPercent = null;
1882
+ this._dataFormat.marketPercentPortfolio = null;
1834
1883
  this._dataFormat.marketDirection = null;
1835
1884
  this._dataFormat.unrealized = null;
1836
1885
  this._dataFormat.unrealizedPercent = null;
@@ -1840,7 +1889,7 @@ module.exports = (() => {
1840
1889
  this._dataFormat.total = null;
1841
1890
  this._dataFormat.totalNegative = false;
1842
1891
  this._dataFormat.summaryTotalCurrent = null;
1843
- this._dataActual.summaryTotalCurrentNegative = false;
1892
+ this._dataFormat.summaryTotalCurrentNegative = false;
1844
1893
  this._dataFormat.summaryTotalPrevious = null;
1845
1894
  this._dataFormat.summaryTotalPreviousNegative = false;
1846
1895
  this._dataFormat.summaryTotalPrevious2 = null;
@@ -2364,7 +2413,6 @@ module.exports = (() => {
2364
2413
  return;
2365
2414
  }
2366
2415
 
2367
- const parent = group._parent;
2368
2416
  const currency = group.currency;
2369
2417
 
2370
2418
  const actual = group._dataActual;
@@ -2455,38 +2503,44 @@ module.exports = (() => {
2455
2503
  return;
2456
2504
  }
2457
2505
 
2458
- const parent = group._parent;
2459
- const excluded = group._excluded;
2460
-
2461
2506
  const actual = group._dataActual;
2462
2507
  const format = group._dataFormat;
2463
-
2464
- let marketPercent;
2465
-
2466
- if (parent !== null && !excluded) {
2467
- const parentData = parent._dataActual;
2508
+ const excluded = group._excluded;
2509
+
2510
+ const portfolioParent = group._container.getParentGroupForPortfolio(group);
2511
+
2512
+ const calculatePercent = (parent) => {
2513
+ let marketPercent;
2514
+
2515
+ if (parent !== null && !excluded) {
2516
+ const parentData = parent._dataActual;
2468
2517
 
2469
- if (parentData.marketAbsolute !== null && !parentData.marketAbsolute.getIsZero()) {
2470
- let numerator;
2518
+ if (parentData.marketAbsolute !== null && !parentData.marketAbsolute.getIsZero()) {
2519
+ let numerator;
2471
2520
 
2472
- if (group.currency !== parent.currency) {
2473
- numerator = Rate.convert(actual.marketAbsolute, group.currency, parent.currency, ...rates);
2521
+ if (group.currency !== parent.currency) {
2522
+ numerator = Rate.convert(actual.marketAbsolute, group.currency, parent.currency, ...rates);
2523
+ } else {
2524
+ numerator = actual.marketAbsolute;
2525
+ }
2526
+
2527
+ marketPercent = numerator.divide(parentData.marketAbsolute);
2474
2528
  } else {
2475
- numerator = actual.marketAbsolute;
2529
+ marketPercent = null;
2476
2530
  }
2477
-
2478
- marketPercent = numerator.divide(parentData.marketAbsolute);
2479
2531
  } else {
2480
2532
  marketPercent = null;
2481
2533
  }
2482
- } else {
2483
- marketPercent = null;
2484
- }
2485
2534
 
2486
- actual.marketPercent = marketPercent;
2487
-
2535
+ return marketPercent;
2536
+ };
2537
+
2538
+ actual.marketPercent = calculatePercent(group._container.getParentGroup(group));
2488
2539
  format.marketPercent = formatPercent(actual.marketPercent, 2);
2489
-
2540
+
2541
+ actual.marketPercentPortfolio = calculatePercent(group._container.getParentGroupForPortfolio(group));
2542
+ format.marketPercentPortfolio = formatPercent(actual.marketPercentPortfolio, 2);
2543
+
2490
2544
  if (!silent) {
2491
2545
  group._marketPercentChangeEvent.fire(group);
2492
2546
  }