@barchart/portfolio-api-common 1.0.174 → 1.0.178

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.
@@ -44,12 +44,6 @@ module.exports = (() => {
44
44
  assert.argumentIsArray(positions, 'positions');
45
45
  assert.argumentIsArray(summaries, 'summaries');
46
46
 
47
- const previousSummaryFrame = PositionSummaryFrame.YEARLY;
48
- const previousSummaryRanges = previousSummaryFrame.getRecentRanges(0);
49
-
50
- const currentSummaryFrame = PositionSummaryFrame.YTD;
51
- const currentSummaryRange = array.last(currentSummaryFrame.getRecentRanges(0));
52
-
53
47
  this._definitions = definitions;
54
48
 
55
49
  this._groupBindings = { };
@@ -60,71 +54,42 @@ module.exports = (() => {
60
54
  return map;
61
55
  }, { });
62
56
 
63
- this._summariesCurrent = summaries.reduce((map, summary) => {
64
- if (summary.frame === currentSummaryFrame && currentSummaryRange.start.getIsEqual(summary.start.date) && currentSummaryRange.end.getIsEqual(summary.end.date)) {
65
- const key = summary.position;
57
+ this._currentSummaryFrame = PositionSummaryFrame.YTD;
58
+ this._currentSummaryRange = array.last(this._currentSummaryFrame.getRecentRanges(0));
66
59
 
67
- map[key] = summary;
68
- }
60
+ this._summariesCurrent = summaries.reduce((map, summary) => {
61
+ addSummaryCurrent(map, summary, this._currentSummaryFrame, this._currentSummaryRange);
69
62
 
70
63
  return map;
71
64
  }, { });
72
-
73
- this._summariesPrevious = summaries.reduce((map, summary) => {
74
- if (summary.frame === previousSummaryFrame) {
75
- const key = summary.position;
76
-
77
- if (!map.hasOwnProperty(key)) {
78
- map[key] = getSummaryArray(previousSummaryRanges);
79
- }
80
65
 
81
- const index = previousSummaryRanges.findIndex(r => r.start.getIsEqual(summary.start.date) && r.end.getIsEqual(summary.end.date));
66
+ this._previousSummaryFrame = PositionSummaryFrame.YEARLY;
67
+ this._previousSummaryRanges = this._previousSummaryFrame.getRecentRanges(0);
82
68
 
83
- if (!(index < 0)) {
84
- map[key][index] = summary;
85
- }
86
- }
69
+ this._summariesPrevious = summaries.reduce((map, summary) => {
70
+ addSummaryPrevious(map, summary, this._previousSummaryFrame, this._previousSummaryRanges);
87
71
 
88
72
  return map;
89
73
  }, { });
90
74
 
91
75
  this._items = positions.reduce((items, position) => {
92
- const portfolio = this._portfolios[position.portfolio];
76
+ const item = createPositionItem.call(this, position);
93
77
 
94
- if (position) {
95
- const currentSummary = this._summariesCurrent[position.position] || null;
96
- const previousSummaries = this._summariesPrevious[position.position] || getSummaryArray(previousSummaryRanges);
97
-
98
- items.push(new PositionItem(portfolio, position, currentSummary, previousSummaries));
78
+ if (item) {
79
+ items.push(item);
99
80
  }
100
81
 
101
82
  return items;
102
83
  }, [ ]);
103
84
 
104
85
  this._symbols = this._items.reduce((map, item) => {
105
- const symbol = extractSymbolForBarchart(item.position);
106
-
107
- if (symbol) {
108
- if (!map.hasOwnProperty(symbol)) {
109
- map[symbol] = [ ];
110
- }
111
-
112
- map[symbol].push(item);
113
- }
86
+ addBarchartSymbol(map, item);
114
87
 
115
88
  return map;
116
89
  }, { });
117
90
 
118
91
  this._symbolsDisplay = this._items.reduce((map, item) => {
119
- const symbol = extractSymbolForDisplay(item.position);
120
-
121
- if (symbol) {
122
- if (!map.hasOwnProperty(symbol)) {
123
- map[symbol] = [ ];
124
- }
125
-
126
- map[symbol].push(item);
127
- }
92
+ addDisplaySymbol(map, item);
128
93
 
129
94
  return map;
130
95
  }, { });
@@ -160,7 +125,7 @@ module.exports = (() => {
160
125
  this._trees = this._definitions.reduce((map, treeDefinition) => {
161
126
  const tree = new Tree();
162
127
 
163
- createGroups.call(this, tree, tree, this._items, treeDefinition, treeDefinition.definitions);
128
+ createGroups.call(this, tree, this._items, treeDefinition, treeDefinition.definitions);
164
129
 
165
130
  map[treeDefinition.name] = tree;
166
131
 
@@ -223,7 +188,7 @@ module.exports = (() => {
223
188
  const overrideRequiredGroups = [ portfolioRequiredGroup ];
224
189
 
225
190
  parentTrees.forEach((t) => {
226
- createGroups.call(this, tree, t, [ ], treeDefinition, levelDefinitions.slice(portfolioLevelDefinitionIndex), overrideRequiredGroups);
191
+ createGroups.call(this, t, [ ], treeDefinition, levelDefinitions.slice(portfolioLevelDefinitionIndex), overrideRequiredGroups);
227
192
  });
228
193
  }
229
194
  });
@@ -257,12 +222,75 @@ module.exports = (() => {
257
222
  });
258
223
  }
259
224
 
260
- mutatePosition(position, summary) {
225
+ /**
226
+ * Adds a new position to the container or updates an existing position already
227
+ * in the container.
228
+ *
229
+ * @public
230
+ * @param {Object} position
231
+ * @param {Array.<Object>} summaries
232
+ */
233
+ updatePosition(position, summaries) {
234
+ assert.argumentIsRequired(position, 'position', Object);
235
+ assert.argumentIsRequired(position.position, 'position.position', String);
236
+ assert.argumentIsRequired(position.portfolio, 'position.portfolio', String);
237
+ assert.argumentIsArray(summaries, 'summaries');
238
+
239
+ if (!this._portfolios.hasOwnProperty(position.portfolio)) {
240
+ return;
241
+ }
242
+
243
+ this.startTransaction(() => {
244
+ this.removePosition(position);
245
+
246
+ summaries.forEach((summary) => {
247
+ addSummaryCurrent(this._summariesCurrent, summary, this._currentSummaryFrame, this._currentSummaryRange);
248
+ addSummaryPrevious(this._summariesPrevious, summary, this._previousSummaryFrame, this._previousSummaryRanges);
249
+ });
261
250
 
251
+ const item = createPositionItem(position);
252
+
253
+ addBarchartSymbol(this._symbols, item);
254
+ addDisplaySymbol(this._symbolsDisplay, item);
255
+
256
+ this._items.push(item);
257
+
258
+ const createGroupOrInjectItem = (parentTree, treeDefinition, levelDefinitions) => {
259
+ const levelDefinition = levelDefinitions[0];
260
+ const levelKey = levelDefinition.keySelector(item);
261
+
262
+ let groupTree;
263
+
264
+ if (parentTree.getChildren().length > 0) {
265
+ groupTree = parentTree.getChildren().findChild(childGroup => childGroup.key === levelKey) || null;
266
+ } else {
267
+ groupTree = null;
268
+ }
269
+
270
+ if (groupTree !== null) {
271
+ groupTree.addItem(item);
272
+
273
+ createGroupOrInjectItem(groupTree, treeDefinition, array.dropLeft(levelDefinitions));
274
+ } else {
275
+ createGroups.call(this, parentTree, [ item ], treeDefinition, levelDefinitions, [ ]);
276
+ }
277
+ };
278
+
279
+ this._definitions.forEach(definition => createGroupOrInjectItem(this._trees[definition.name], definition, definition.definitions));
280
+ });
262
281
  }
263
282
 
283
+ /**
284
+ * Removes a single position from the container.
285
+ *
286
+ * @public
287
+ * @param {Object} position
288
+ */
264
289
  removePosition(position) {
265
- removePositionItem.call(this, this._items.find((item) => item.position.position === position));
290
+ assert.argumentIsRequired(position, 'position', Object);
291
+ assert.argumentIsRequired(position.position, 'position.position', String);
292
+
293
+ removePositionItem.call(this, this._items.find((item) => item.position.position === position.position));
266
294
  }
267
295
 
268
296
  /**
@@ -412,7 +440,7 @@ module.exports = (() => {
412
440
  *
413
441
  * @public
414
442
  * @param {String} name
415
- * @param {Array.<String> keys
443
+ * @param {Array.<String>} keys
416
444
  * @returns {PositionGroup}
417
445
  */
418
446
  getGroup(name, keys) {
@@ -428,7 +456,7 @@ module.exports = (() => {
428
456
  *
429
457
  * @public
430
458
  * @param {String} name
431
- * @param {Array.<String> keys
459
+ * @param {Array.<String>} keys
432
460
  * @returns {Array.<PositionGroup>}
433
461
  */
434
462
  getGroups(name, keys) {
@@ -503,10 +531,6 @@ module.exports = (() => {
503
531
  return keys.reduce((tree, key) => tree.findChild(group => group.key === key), tree);
504
532
  }
505
533
 
506
- function getSummaryArray(ranges) {
507
- return ranges.map(range => null);
508
- }
509
-
510
534
  function extractSymbolForBarchart(position) {
511
535
  if (position.instrument && position.instrument.symbol && position.instrument.symbol.barchart) {
512
536
  return position.instrument.symbol.barchart;
@@ -541,12 +565,62 @@ module.exports = (() => {
541
565
  this._groupBindings[id].push(dispoable);
542
566
  }
543
567
 
544
- function createGroups(parentTree, currentTree, items, treeDefinition, levelDefinitions, overrideRequiredGroups) {
568
+ function initializeGroupObservers(groupTree, treeDefinition) {
569
+ const group = groupTree.getValue();
570
+
571
+ addGroupBinding.call(this, group, group.registerGroupExcludedChangeHandler(() => {
572
+ groupTree.climb((parentGroup, parentTree) => {
573
+ if (parentGroup) {
574
+ let excludedItems = [];
575
+
576
+ parentTree.walk((childGroup) => {
577
+ if (childGroup.excluded) {
578
+ excludedItems = excludedItems.concat(childGroup.items);
579
+ }
580
+ }, false, false);
581
+
582
+ parentGroup.setExcludedItems(array.unique(excludedItems));
583
+ }
584
+ }, false);
585
+
586
+ if (treeDefinition.exclusionDependencies.length > 0) {
587
+ const dependantTrees = treeDefinition.exclusionDependencies.reduce((trees, name) => {
588
+ if (this._trees.hasOwnProperty(name)) {
589
+ trees.push(this._trees[name]);
590
+ }
591
+
592
+ return trees;
593
+ }, [ ]);
594
+
595
+ if (dependantTrees.length > 0) {
596
+ let excludedItems = [ ];
597
+
598
+ groupTree.getRoot().walk((childGroup) => {
599
+ if (childGroup.excluded) {
600
+ excludedItems = excludedItems.concat(childGroup.items);
601
+ }
602
+ }, false, false);
603
+
604
+ dependantTrees.forEach((dependantTrees) => {
605
+ dependantTrees.walk((childGroup) => {
606
+ childGroup.setExcludedItems(excludedItems);
607
+ }, false, false);
608
+ });
609
+ }
610
+ }
611
+ }));
612
+
613
+ addGroupBinding.call(this, group, group.registerMarketPercentChangeHandler(() => {
614
+ groupTree.getParent().walk((childGroup) => childGroup.refreshMarketPercent());
615
+ }));
616
+ }
617
+
618
+ function createGroups(parentTree, items, treeDefinition, levelDefinitions, overrideRequiredGroups) {
545
619
  if (levelDefinitions.length === 0) {
546
620
  return;
547
621
  }
548
622
 
549
- const parent = currentTree.getValue() || null;
623
+ const parent = parentTree.getValue() || null;
550
624
 
551
625
  const levelDefinition = levelDefinitions[0];
552
626
 
@@ -603,71 +677,92 @@ module.exports = (() => {
603
677
 
604
678
  compositeGroups.sort(builder.toComparator());
605
679
 
606
- const initializeGroupObservers = (group, groupTree) => {
607
- addGroupBinding.call(this, group, group.registerGroupExcludedChangeHandler((excluded, sender) => {
608
- groupTree.climb((parentGroup) => {
609
- if (parentGroup) {
610
- let excludedItems = [];
680
+ compositeGroups.forEach((group) => {
681
+ const childTree = parentTree.addChild(group);
611
682
 
612
- currentTree.walk((childGroup) => {
613
- if (childGroup.excluded) {
614
- excludedItems = excludedItems.concat(childGroup.items);
615
- }
616
- }, false, false);
683
+ initializeGroupObservers.call(this, childTree, treeDefinition);
617
684
 
618
- parentGroup.setExcludedItems(array.unique(excludedItems));
619
- }
620
- }, false);
685
+ createGroups.call(this, childTree, group.items, treeDefinition, array.dropLeft(levelDefinitions));
686
+ });
687
+ }
621
688
 
622
- if (treeDefinition.exclusionDependencies.length > 0) {
623
- const dependantTrees = treeDefinition.exclusionDependencies.reduce((trees, name) => {
624
- if (this._trees.hasOwnProperty(name)) {
625
- trees.push(this._trees[name]);
626
- }
689
+ function getPositionItemsForPortfolio(items, portfolio) {
690
+ return items.reduce((positionItems, item) => {
691
+ if (item.position.portfolio === portfolio) {
692
+ positionItems.push(item.position);
693
+ }
627
694
 
628
- return trees;
629
- }, [ ]);
695
+ return positionItems;
696
+ }, [ ]);
697
+ }
630
698
 
631
- if (dependantTrees.length > 0) {
632
- let excludedItems = [ ];
699
+ function getSummaryArray(ranges) {
700
+ return ranges.map(range => null);
701
+ }
633
702
 
634
- parentTree.walk((childGroup) => {
635
- if (childGroup.excluded) {
636
- excludedItems = excludedItems.concat(childGroup.items);
637
- }
638
- }, false, false);
703
+ function addSummaryCurrent(map, summary, currentSummaryFrame, currentSummaryRange) {
704
+ if (summary.frame === currentSummaryFrame && currentSummaryRange.start.getIsEqual(summary.start.date) && currentSummaryRange.end.getIsEqual(summary.end.date)) {
705
+ const key = summary.position;
639
706
 
640
- dependantTrees.forEach((dependantTrees) => {
641
- dependantTrees.walk((childGroup) => {
642
- childGroup.setExcludedItems(excludedItems);
643
- }, false, false);
644
- });
645
- }
646
- }
647
- }));
648
- };
707
+ map[key] = summary;
708
+ }
709
+ }
649
710
 
650
- compositeGroups.forEach((group) => {
651
- const childTree = currentTree.addChild(group);
711
+ function addSummaryPrevious(map, summary, previousSummaryFrame, previousSummaryRanges) {
712
+ if (summary.frame === previousSummaryFrame) {
713
+ const key = summary.position;
652
714
 
653
- initializeGroupObservers(group, childTree);
715
+ if (!map.hasOwnProperty(key)) {
716
+ map[key] = getSummaryArray(previousSummaryRanges);
717
+ }
654
718
 
655
- addGroupBinding.call(this, group, group.registerMarketPercentChangeHandler(() => {
656
- currentTree.walk((childGroup) => childGroup.refreshMarketPercent());
657
- }));
719
+ const index = previousSummaryRanges.findIndex(r => r.start.getIsEqual(summary.start.date) && r.end.getIsEqual(summary.end.date));
658
720
 
659
- createGroups.call(this, parentTree, childTree, group.items, treeDefinition, array.dropLeft(levelDefinitions));
660
- });
721
+ if (!(index < 0)) {
722
+ map[key][index] = summary;
723
+ }
724
+ }
661
725
  }
662
726
 
663
- function getPositionItemsForPortfolio(items, portfolio) {
664
- return items.reduce((positionItems, item) => {
665
- if (item.position.portfolio === portfolio) {
666
- positionItems.push(item.position);
727
+ function addBarchartSymbol(map, item) {
728
+ const barchartSymbol = extractSymbolForBarchart(item.position);
729
+
730
+ if (barchartSymbol) {
731
+ if (!map.hasOwnProperty(barchartSymbol)) {
732
+ map[barchartSymbol] = [ ];
667
733
  }
668
734
 
669
- return positionItems;
670
- }, [ ]);
735
+ map[barchartSymbol].push(item);
736
+ }
737
+ }
738
+
739
+ function addDisplaySymbol(map, item) {
740
+ const displaySymbol = extractSymbolForDisplay(item.position);
741
+
742
+ if (displaySymbol) {
743
+ if (!map.hasOwnProperty(displaySymbol)) {
744
+ map[displaySymbol] = [ ];
745
+ }
746
+
747
+ map[displaySymbol].push(item);
748
+ }
749
+ }
750
+
751
+ function createPositionItem(position) {
752
+ const portfolio = this._portfolios[position.portfolio];
753
+
754
+ let returnRef;
755
+
756
+ if (portfolio) {
757
+ const currentSummary = this._summariesCurrent[ position.position ] || null;
758
+ const previousSummaries = this._summariesPrevious[ position.position ] || getSummaryArray(this._previousSummaryRanges);
759
+
760
+ returnRef = new PositionItem(portfolio, position, currentSummary, previousSummaries);
761
+ } else {
762
+ returnRef = null;
763
+ }
764
+
765
+ return returnRef;
671
766
  }
672
767
 
673
768
  function removePositionItem(positionItem) {
@@ -689,7 +784,7 @@ module.exports = (() => {
689
784
  const displaySymbol = extractSymbolForDisplay(positionItem.position);
690
785
 
691
786
  if (this._symbolsDisplay.hasOwnProperty(displaySymbol)) {
692
- array.remove(this._symbols[displaySymbol], i => i === positionItem);
787
+ array.remove(this._symbolsDisplay[displaySymbol], i => i === positionItem);
693
788
  }
694
789
 
695
790
  const currency = extractCurrency(positionItem.position);
@@ -698,6 +793,12 @@ module.exports = (() => {
698
793
  array.remove(this._currencies[currency.code], i => i === positionItem);
699
794
  }
700
795
 
796
+ this._trees[key].walk((group, groupNode) => {
797
+ if (group.definition.type === PositionLevelType.POSITION && group.key === positionItem.position.position) {
798
+ groupNode.sever();
799
+ }
800
+ }, true, false);
801
+
701
802
  positionItem.dispose();
702
803
  }
703
804
 
@@ -329,12 +329,13 @@ module.exports = (() => {
329
329
  return this._excluded;
330
330
  }
331
331
 
332
- addItems(items) {
333
-
334
- this.refresh();
335
- }
336
-
337
- removeItems(items) {
332
+ /**
333
+ * Adds a new {@link PositionItem} to the group.
334
+ *
335
+ * @public
336
+ * @param {PositionItem} item
337
+ */
338
+ addItem(item) {
338
339
 
339
340
  this.refresh();
340
341
  }
@@ -341,8 +341,8 @@ module.exports = (() => {
341
341
  marketAbsoluteChange = marketAbsolute.subtract(data.marketAbsolute);
342
342
  }
343
343
 
344
- data.marketAbsolute = market;
345
- data.marketAbsoluteChange = marketChange;
344
+ data.marketAbsolute = marketAbsolute;
345
+ data.marketAbsoluteChange = marketAbsoluteChange;
346
346
 
347
347
  let unrealizedToday;
348
348
  let unrealizedTodayChange;
@@ -240,7 +240,7 @@ module.exports = (() => {
240
240
  *
241
241
  * @public
242
242
  * @callback PositionLevelDefinition~keySelector
243
- * @param {PositionItem} session
243
+ * @param {PositionItem} item
244
244
  * @returns {String}
245
245
  */
246
246
 
@@ -250,7 +250,7 @@ module.exports = (() => {
250
250
  *
251
251
  * @public
252
252
  * @callback PositionLevelDefinition~descriptionSelector
253
- * @param {PositionItem} session
253
+ * @param {PositionItem} item
254
254
  * @returns {String}
255
255
  */
256
256
 
@@ -260,7 +260,7 @@ module.exports = (() => {
260
260
  *
261
261
  * @public
262
262
  * @callback PositionLevelDefinition~currencySelector
263
- * @param {PositionItem} session
263
+ * @param {PositionItem} item
264
264
  * @returns {Currency}
265
265
  */
266
266
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barchart/portfolio-api-common",
3
- "version": "1.0.174",
3
+ "version": "1.0.178",
4
4
  "description": "Common classes used by the Portfolio system",
5
5
  "author": {
6
6
  "name": "Bryan Ingle",
@@ -760,12 +760,6 @@ module.exports = (() => {
760
760
  assert.argumentIsArray(positions, 'positions');
761
761
  assert.argumentIsArray(summaries, 'summaries');
762
762
 
763
- const previousSummaryFrame = PositionSummaryFrame.YEARLY;
764
- const previousSummaryRanges = previousSummaryFrame.getRecentRanges(0);
765
-
766
- const currentSummaryFrame = PositionSummaryFrame.YTD;
767
- const currentSummaryRange = array.last(currentSummaryFrame.getRecentRanges(0));
768
-
769
763
  this._definitions = definitions;
770
764
 
771
765
  this._groupBindings = { };
@@ -776,71 +770,42 @@ module.exports = (() => {
776
770
  return map;
777
771
  }, { });
778
772
 
779
- this._summariesCurrent = summaries.reduce((map, summary) => {
780
- if (summary.frame === currentSummaryFrame && currentSummaryRange.start.getIsEqual(summary.start.date) && currentSummaryRange.end.getIsEqual(summary.end.date)) {
781
- const key = summary.position;
773
+ this._currentSummaryFrame = PositionSummaryFrame.YTD;
774
+ this._currentSummaryRange = array.last(this._currentSummaryFrame.getRecentRanges(0));
782
775
 
783
- map[key] = summary;
784
- }
776
+ this._summariesCurrent = summaries.reduce((map, summary) => {
777
+ addSummaryCurrent(map, summary, this._currentSummaryFrame, this._currentSummaryRange);
785
778
 
786
779
  return map;
787
780
  }, { });
788
-
789
- this._summariesPrevious = summaries.reduce((map, summary) => {
790
- if (summary.frame === previousSummaryFrame) {
791
- const key = summary.position;
792
-
793
- if (!map.hasOwnProperty(key)) {
794
- map[key] = getSummaryArray(previousSummaryRanges);
795
- }
796
781
 
797
- const index = previousSummaryRanges.findIndex(r => r.start.getIsEqual(summary.start.date) && r.end.getIsEqual(summary.end.date));
782
+ this._previousSummaryFrame = PositionSummaryFrame.YEARLY;
783
+ this._previousSummaryRanges = this._previousSummaryFrame.getRecentRanges(0);
798
784
 
799
- if (!(index < 0)) {
800
- map[key][index] = summary;
801
- }
802
- }
785
+ this._summariesPrevious = summaries.reduce((map, summary) => {
786
+ addSummaryPrevious(map, summary, this._previousSummaryFrame, this._previousSummaryRanges);
803
787
 
804
788
  return map;
805
789
  }, { });
806
790
 
807
791
  this._items = positions.reduce((items, position) => {
808
- const portfolio = this._portfolios[position.portfolio];
792
+ const item = createPositionItem.call(this, position);
809
793
 
810
- if (position) {
811
- const currentSummary = this._summariesCurrent[position.position] || null;
812
- const previousSummaries = this._summariesPrevious[position.position] || getSummaryArray(previousSummaryRanges);
813
-
814
- items.push(new PositionItem(portfolio, position, currentSummary, previousSummaries));
794
+ if (item) {
795
+ items.push(item);
815
796
  }
816
797
 
817
798
  return items;
818
799
  }, [ ]);
819
800
 
820
801
  this._symbols = this._items.reduce((map, item) => {
821
- const symbol = extractSymbolForBarchart(item.position);
822
-
823
- if (symbol) {
824
- if (!map.hasOwnProperty(symbol)) {
825
- map[symbol] = [ ];
826
- }
827
-
828
- map[symbol].push(item);
829
- }
802
+ addBarchartSymbol(map, item);
830
803
 
831
804
  return map;
832
805
  }, { });
833
806
 
834
807
  this._symbolsDisplay = this._items.reduce((map, item) => {
835
- const symbol = extractSymbolForDisplay(item.position);
836
-
837
- if (symbol) {
838
- if (!map.hasOwnProperty(symbol)) {
839
- map[symbol] = [ ];
840
- }
841
-
842
- map[symbol].push(item);
843
- }
808
+ addDisplaySymbol(map, item);
844
809
 
845
810
  return map;
846
811
  }, { });
@@ -876,7 +841,7 @@ module.exports = (() => {
876
841
  this._trees = this._definitions.reduce((map, treeDefinition) => {
877
842
  const tree = new Tree();
878
843
 
879
- createGroups.call(this, tree, tree, this._items, treeDefinition, treeDefinition.definitions);
844
+ createGroups.call(this, tree, this._items, treeDefinition, treeDefinition.definitions);
880
845
 
881
846
  map[treeDefinition.name] = tree;
882
847
 
@@ -939,7 +904,7 @@ module.exports = (() => {
939
904
  const overrideRequiredGroups = [ portfolioRequiredGroup ];
940
905
 
941
906
  parentTrees.forEach((t) => {
942
- createGroups.call(this, tree, t, [ ], treeDefinition, levelDefinitions.slice(portfolioLevelDefinitionIndex), overrideRequiredGroups);
907
+ createGroups.call(this, t, [ ], treeDefinition, levelDefinitions.slice(portfolioLevelDefinitionIndex), overrideRequiredGroups);
943
908
  });
944
909
  }
945
910
  });
@@ -973,12 +938,75 @@ module.exports = (() => {
973
938
  });
974
939
  }
975
940
 
976
- mutatePosition(position, summary) {
941
+ /**
942
+ * Adds a new position to the container or updates an existing position already
943
+ * in the container.
944
+ *
945
+ * @public
946
+ * @param {Object} position
947
+ * @param {Array.<Object>} summaries
948
+ */
949
+ updatePosition(position, summaries) {
950
+ assert.argumentIsRequired(position, 'position', Object);
951
+ assert.argumentIsRequired(position.position, 'position.position', String);
952
+ assert.argumentIsRequired(position.portfolio, 'position.portfolio', String);
953
+ assert.argumentIsArray(summaries, 'summaries');
954
+
955
+ if (!this._portfolios.hasOwnProperty(position.portfolio)) {
956
+ return;
957
+ }
958
+
959
+ this.startTransaction(() => {
960
+ this.removePosition(position);
961
+
962
+ summaries.forEach((summary) => {
963
+ addSummaryCurrent(this._summariesCurrent, summary, this._currentSummaryFrame, this._currentSummaryRange);
964
+ addSummaryPrevious(this._summariesPrevious, summary, this._previousSummaryFrame, this._previousSummaryRanges);
965
+ });
966
+
967
+ const item = createPositionItem(position);
968
+
969
+ addBarchartSymbol(this._symbols, item);
970
+ addDisplaySymbol(this._symbolsDisplay, item);
971
+
972
+ this._items.push(item);
977
973
 
974
+ const createGroupOrInjectItem = (parentTree, treeDefinition, levelDefinitions) => {
975
+ const levelDefinition = levelDefinitions[0];
976
+ const levelKey = levelDefinition.keySelector(item);
977
+
978
+ let groupTree;
979
+
980
+ if (parentTree.getChildren().length > 0) {
981
+ groupTree = parentTree.getChildren().findChild(childGroup => childGroup.key === levelKey) || null;
982
+ } else {
983
+ groupTree = null;
984
+ }
985
+
986
+ if (groupTree !== null) {
987
+ groupTree.addItem(item);
988
+
989
+ createGroupOrInjectItem(groupTree, treeDefinition, array.dropLeft(levelDefinitions));
990
+ } else {
991
+ createGroups.call(this, parentTree, [ item ], treeDefinition, levelDefinitions, [ ]);
992
+ }
993
+ };
994
+
995
+ this._definitions.forEach(definition => createGroupOrInjectItem(this._trees[definition.name], definition, definition.definitions));
996
+ });
978
997
  }
979
998
 
999
+ /**
1000
+ * Removes a single position from the container.
1001
+ *
1002
+ * @public
1003
+ * @param {Object} position
1004
+ */
980
1005
  removePosition(position) {
981
- removePositionItem.call(this, this._items.find((item) => item.position.position === position));
1006
+ assert.argumentIsRequired(position, 'position', Object);
1007
+ assert.argumentIsRequired(position.position, 'position.position', String);
1008
+
1009
+ removePositionItem.call(this, this._items.find((item) => item.position.position === position.position));
982
1010
  }
983
1011
 
984
1012
  /**
@@ -1128,7 +1156,7 @@ module.exports = (() => {
1128
1156
  *
1129
1157
  * @public
1130
1158
  * @param {String} name
1131
- * @param {Array.<String> keys
1159
+ * @param {Array.<String>} keys
1132
1160
  * @returns {PositionGroup}
1133
1161
  */
1134
1162
  getGroup(name, keys) {
@@ -1144,7 +1172,7 @@ module.exports = (() => {
1144
1172
  *
1145
1173
  * @public
1146
1174
  * @param {String} name
1147
- * @param {Array.<String> keys
1175
+ * @param {Array.<String>} keys
1148
1176
  * @returns {Array.<PositionGroup>}
1149
1177
  */
1150
1178
  getGroups(name, keys) {
@@ -1219,10 +1247,6 @@ module.exports = (() => {
1219
1247
  return keys.reduce((tree, key) => tree.findChild(group => group.key === key), tree);
1220
1248
  }
1221
1249
 
1222
- function getSummaryArray(ranges) {
1223
- return ranges.map(range => null);
1224
- }
1225
-
1226
1250
  function extractSymbolForBarchart(position) {
1227
1251
  if (position.instrument && position.instrument.symbol && position.instrument.symbol.barchart) {
1228
1252
  return position.instrument.symbol.barchart;
@@ -1257,12 +1281,62 @@ module.exports = (() => {
1257
1281
  this._groupBindings[id].push(dispoable);
1258
1282
  }
1259
1283
 
1260
- function createGroups(parentTree, currentTree, items, treeDefinition, levelDefinitions, overrideRequiredGroups) {
1284
+ function initializeGroupObservers(groupTree, treeDefinition) {
1285
+ const group = groupTree.getValue();
1286
+
1287
+ addGroupBinding.call(this, group, group.registerGroupExcludedChangeHandler(() => {
1288
+ groupTree.climb((parentGroup, parentTree) => {
1289
+ if (parentGroup) {
1290
+ let excludedItems = [];
1291
+
1292
+ parentTree.walk((childGroup) => {
1293
+ if (childGroup.excluded) {
1294
+ excludedItems = excludedItems.concat(childGroup.items);
1295
+ }
1296
+ }, false, false);
1297
+
1298
+ parentGroup.setExcludedItems(array.unique(excludedItems));
1299
+ }
1300
+ }, false);
1301
+
1302
+ if (treeDefinition.exclusionDependencies.length > 0) {
1303
+ const dependantTrees = treeDefinition.exclusionDependencies.reduce((trees, name) => {
1304
+ if (this._trees.hasOwnProperty(name)) {
1305
+ trees.push(this._trees[name]);
1306
+ }
1307
+
1308
+ return trees;
1309
+ }, [ ]);
1310
+
1311
+ if (dependantTrees.length > 0) {
1312
+ let excludedItems = [ ];
1313
+
1314
+ groupTree.getRoot().walk((childGroup) => {
1315
+ if (childGroup.excluded) {
1316
+ excludedItems = excludedItems.concat(childGroup.items);
1317
+ }
1318
+ }, false, false);
1319
+
1320
+ dependantTrees.forEach((dependantTrees) => {
1321
+ dependantTrees.walk((childGroup) => {
1322
+ childGroup.setExcludedItems(excludedItems);
1323
+ }, false, false);
1324
+ });
1325
+ }
1326
+ }
1327
+ }));
1328
+
1329
+ addGroupBinding.call(this, group, group.registerMarketPercentChangeHandler(() => {
1330
+ groupTree.getParent().walk((childGroup) => childGroup.refreshMarketPercent());
1331
+ }));
1332
+ }
1333
+
1334
+ function createGroups(parentTree, items, treeDefinition, levelDefinitions, overrideRequiredGroups) {
1261
1335
  if (levelDefinitions.length === 0) {
1262
1336
  return;
1263
1337
  }
1264
1338
 
1265
- const parent = currentTree.getValue() || null;
1339
+ const parent = parentTree.getValue() || null;
1266
1340
 
1267
1341
  const levelDefinition = levelDefinitions[0];
1268
1342
 
@@ -1319,71 +1393,92 @@ module.exports = (() => {
1319
1393
 
1320
1394
  compositeGroups.sort(builder.toComparator());
1321
1395
 
1322
- const initializeGroupObservers = (group, groupTree) => {
1323
- addGroupBinding.call(this, group, group.registerGroupExcludedChangeHandler((excluded, sender) => {
1324
- groupTree.climb((parentGroup) => {
1325
- if (parentGroup) {
1326
- let excludedItems = [];
1396
+ compositeGroups.forEach((group) => {
1397
+ const childTree = parentTree.addChild(group);
1327
1398
 
1328
- currentTree.walk((childGroup) => {
1329
- if (childGroup.excluded) {
1330
- excludedItems = excludedItems.concat(childGroup.items);
1331
- }
1332
- }, false, false);
1399
+ initializeGroupObservers.call(this, childTree, treeDefinition);
1333
1400
 
1334
- parentGroup.setExcludedItems(array.unique(excludedItems));
1335
- }
1336
- }, false);
1401
+ createGroups.call(this, childTree, group.items, treeDefinition, array.dropLeft(levelDefinitions));
1402
+ });
1403
+ }
1337
1404
 
1338
- if (treeDefinition.exclusionDependencies.length > 0) {
1339
- const dependantTrees = treeDefinition.exclusionDependencies.reduce((trees, name) => {
1340
- if (this._trees.hasOwnProperty(name)) {
1341
- trees.push(this._trees[name]);
1342
- }
1405
+ function getPositionItemsForPortfolio(items, portfolio) {
1406
+ return items.reduce((positionItems, item) => {
1407
+ if (item.position.portfolio === portfolio) {
1408
+ positionItems.push(item.position);
1409
+ }
1343
1410
 
1344
- return trees;
1345
- }, [ ]);
1411
+ return positionItems;
1412
+ }, [ ]);
1413
+ }
1346
1414
 
1347
- if (dependantTrees.length > 0) {
1348
- let excludedItems = [ ];
1415
+ function getSummaryArray(ranges) {
1416
+ return ranges.map(range => null);
1417
+ }
1349
1418
 
1350
- parentTree.walk((childGroup) => {
1351
- if (childGroup.excluded) {
1352
- excludedItems = excludedItems.concat(childGroup.items);
1353
- }
1354
- }, false, false);
1419
+ function addSummaryCurrent(map, summary, currentSummaryFrame, currentSummaryRange) {
1420
+ if (summary.frame === currentSummaryFrame && currentSummaryRange.start.getIsEqual(summary.start.date) && currentSummaryRange.end.getIsEqual(summary.end.date)) {
1421
+ const key = summary.position;
1355
1422
 
1356
- dependantTrees.forEach((dependantTrees) => {
1357
- dependantTrees.walk((childGroup) => {
1358
- childGroup.setExcludedItems(excludedItems);
1359
- }, false, false);
1360
- });
1361
- }
1362
- }
1363
- }));
1364
- };
1423
+ map[key] = summary;
1424
+ }
1425
+ }
1365
1426
 
1366
- compositeGroups.forEach((group) => {
1367
- const childTree = currentTree.addChild(group);
1427
+ function addSummaryPrevious(map, summary, previousSummaryFrame, previousSummaryRanges) {
1428
+ if (summary.frame === previousSummaryFrame) {
1429
+ const key = summary.position;
1368
1430
 
1369
- initializeGroupObservers(group, childTree);
1431
+ if (!map.hasOwnProperty(key)) {
1432
+ map[key] = getSummaryArray(previousSummaryRanges);
1433
+ }
1370
1434
 
1371
- addGroupBinding.call(this, group, group.registerMarketPercentChangeHandler(() => {
1372
- currentTree.walk((childGroup) => childGroup.refreshMarketPercent());
1373
- }));
1435
+ const index = previousSummaryRanges.findIndex(r => r.start.getIsEqual(summary.start.date) && r.end.getIsEqual(summary.end.date));
1374
1436
 
1375
- createGroups.call(this, parentTree, childTree, group.items, treeDefinition, array.dropLeft(levelDefinitions));
1376
- });
1437
+ if (!(index < 0)) {
1438
+ map[key][index] = summary;
1439
+ }
1440
+ }
1377
1441
  }
1378
1442
 
1379
- function getPositionItemsForPortfolio(items, portfolio) {
1380
- return items.reduce((positionItems, item) => {
1381
- if (item.position.portfolio === portfolio) {
1382
- positionItems.push(item.position);
1443
+ function addBarchartSymbol(map, item) {
1444
+ const barchartSymbol = extractSymbolForBarchart(item.position);
1445
+
1446
+ if (barchartSymbol) {
1447
+ if (!map.hasOwnProperty(barchartSymbol)) {
1448
+ map[barchartSymbol] = [ ];
1383
1449
  }
1384
1450
 
1385
- return positionItems;
1386
- }, [ ]);
1451
+ map[barchartSymbol].push(item);
1452
+ }
1453
+ }
1454
+
1455
+ function addDisplaySymbol(map, item) {
1456
+ const displaySymbol = extractSymbolForDisplay(item.position);
1457
+
1458
+ if (displaySymbol) {
1459
+ if (!map.hasOwnProperty(displaySymbol)) {
1460
+ map[displaySymbol] = [ ];
1461
+ }
1462
+
1463
+ map[displaySymbol].push(item);
1464
+ }
1465
+ }
1466
+
1467
+ function createPositionItem(position) {
1468
+ const portfolio = this._portfolios[position.portfolio];
1469
+
1470
+ let returnRef;
1471
+
1472
+ if (portfolio) {
1473
+ const currentSummary = this._summariesCurrent[ position.position ] || null;
1474
+ const previousSummaries = this._summariesPrevious[ position.position ] || getSummaryArray(this._previousSummaryRanges);
1475
+
1476
+ returnRef = new PositionItem(portfolio, position, currentSummary, previousSummaries);
1477
+ } else {
1478
+ returnRef = null;
1479
+ }
1480
+
1481
+ return returnRef;
1387
1482
  }
1388
1483
 
1389
1484
  function removePositionItem(positionItem) {
@@ -1405,7 +1500,7 @@ module.exports = (() => {
1405
1500
  const displaySymbol = extractSymbolForDisplay(positionItem.position);
1406
1501
 
1407
1502
  if (this._symbolsDisplay.hasOwnProperty(displaySymbol)) {
1408
- array.remove(this._symbols[displaySymbol], i => i === positionItem);
1503
+ array.remove(this._symbolsDisplay[displaySymbol], i => i === positionItem);
1409
1504
  }
1410
1505
 
1411
1506
  const currency = extractCurrency(positionItem.position);
@@ -1414,6 +1509,12 @@ module.exports = (() => {
1414
1509
  array.remove(this._currencies[currency.code], i => i === positionItem);
1415
1510
  }
1416
1511
 
1512
+ this._trees[key].walk((group, groupNode) => {
1513
+ if (group.definition.type === PositionLevelType.POSITION && group.key === positionItem.position.position) {
1514
+ groupNode.sever();
1515
+ }
1516
+ }, true, false);
1517
+
1417
1518
  positionItem.dispose();
1418
1519
  }
1419
1520
 
@@ -1752,12 +1853,13 @@ module.exports = (() => {
1752
1853
  return this._excluded;
1753
1854
  }
1754
1855
 
1755
- addItems(items) {
1756
-
1757
- this.refresh();
1758
- }
1759
-
1760
- removeItems(items) {
1856
+ /**
1857
+ * Adds a new {@link PositionItem} to the group.
1858
+ *
1859
+ * @public
1860
+ * @param {PositionItem} item
1861
+ */
1862
+ addItem(item) {
1761
1863
 
1762
1864
  this.refresh();
1763
1865
  }
@@ -2509,8 +2611,8 @@ module.exports = (() => {
2509
2611
  marketAbsoluteChange = marketAbsolute.subtract(data.marketAbsolute);
2510
2612
  }
2511
2613
 
2512
- data.marketAbsolute = market;
2513
- data.marketAbsoluteChange = marketChange;
2614
+ data.marketAbsolute = marketAbsolute;
2615
+ data.marketAbsoluteChange = marketAbsoluteChange;
2514
2616
 
2515
2617
  let unrealizedToday;
2516
2618
  let unrealizedTodayChange;
@@ -2827,7 +2929,7 @@ module.exports = (() => {
2827
2929
  *
2828
2930
  * @public
2829
2931
  * @callback PositionLevelDefinition~keySelector
2830
- * @param {PositionItem} session
2932
+ * @param {PositionItem} item
2831
2933
  * @returns {String}
2832
2934
  */
2833
2935
 
@@ -2837,7 +2939,7 @@ module.exports = (() => {
2837
2939
  *
2838
2940
  * @public
2839
2941
  * @callback PositionLevelDefinition~descriptionSelector
2840
- * @param {PositionItem} session
2942
+ * @param {PositionItem} item
2841
2943
  * @returns {String}
2842
2944
  */
2843
2945
 
@@ -2847,7 +2949,7 @@ module.exports = (() => {
2847
2949
  *
2848
2950
  * @public
2849
2951
  * @callback PositionLevelDefinition~currencySelector
2850
- * @param {PositionItem} session
2952
+ * @param {PositionItem} item
2851
2953
  * @returns {Currency}
2852
2954
  */
2853
2955