@barchart/portfolio-api-common 1.0.69 → 1.0.73

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.
@@ -13,15 +13,23 @@ module.exports = (() => {
13
13
  * @param {String} alternateDescription
14
14
  * @param {String} code
15
15
  * @param {Boolean} canReinvest
16
+ * @param {Boolean} usesSymbols
16
17
  */
17
18
  class InstrumentType extends Enum {
18
- constructor(code, description, alternateDescription, canReinvest) {
19
+ constructor(code, description, alternateDescription, canReinvest, usesSymbols) {
19
20
  super(code, description);
20
21
 
21
22
  this._alternateDescription = alternateDescription;
22
23
  this._canReinvest = canReinvest;
24
+ this._usesSymbols = usesSymbols;
23
25
  }
24
26
 
27
+ /**
28
+ * A human-readable description.
29
+ *
30
+ * @public
31
+ * @return {String}
32
+ */
25
33
  get alternateDescription() {
26
34
  return this._alternateDescription;
27
35
  }
@@ -29,16 +37,28 @@ module.exports = (() => {
29
37
  /**
30
38
  * Indicates if the instrument type allows automatic reinvestment.
31
39
  *
40
+ * @public
32
41
  * @returns {Boolean}
33
42
  */
34
43
  get canReinvest() {
35
44
  return this._canReinvest;
36
45
  }
37
46
 
47
+ /**
48
+ * Indicates if an instrument of this type can be represented by a symbol.
49
+ *
50
+ * @public
51
+ * @returns {Boolean}
52
+ */
53
+ get usesSymbols() {
54
+ return this._usesSymbols;
55
+ }
56
+
38
57
  /**
39
58
  * Cash.
40
59
  *
41
60
  * @public
61
+ * @static
42
62
  * @returns {InstrumentType}
43
63
  */
44
64
  static get CASH() {
@@ -49,6 +69,7 @@ module.exports = (() => {
49
69
  * An equity issue.
50
70
  *
51
71
  * @public
72
+ * @static
52
73
  * @returns {InstrumentType}
53
74
  */
54
75
  static get EQUITY() {
@@ -59,6 +80,7 @@ module.exports = (() => {
59
80
  * A mutual fund.
60
81
  *
61
82
  * @public
83
+ * @static
62
84
  * @returns {InstrumentType}
63
85
  */
64
86
  static get FUND() {
@@ -69,6 +91,7 @@ module.exports = (() => {
69
91
  * An undefined asset (e.g. a house, or a collectible, or a salvaged alien spaceship).
70
92
  *
71
93
  * @public
94
+ * @static
72
95
  * @returns {InstrumentType}
73
96
  */
74
97
  static get OTHER() {
@@ -80,10 +103,10 @@ module.exports = (() => {
80
103
  }
81
104
  }
82
105
 
83
- const cash = new InstrumentType('CASH', 'cash', 'Cash', false);
84
- const equity = new InstrumentType('EQUITY', 'equity', 'Equities', true);
85
- const fund = new InstrumentType('FUND', 'mutual fund', 'Funds', true);
86
- const other = new InstrumentType('OTHER', 'other', 'Other', false);
106
+ const cash = new InstrumentType('CASH', 'cash', 'Cash', false, false);
107
+ const equity = new InstrumentType('EQUITY', 'equity', 'Equities', true, true);
108
+ const fund = new InstrumentType('FUND', 'mutual fund', 'Funds', true, true);
109
+ const other = new InstrumentType('OTHER', 'other', 'Other', false, false);
87
110
 
88
111
  return InstrumentType;
89
112
  })();
@@ -239,7 +239,7 @@ module.exports = (() => {
239
239
  }
240
240
 
241
241
  function getYearToDateRangeDescription(startDate, endDate) {
242
- return '';
242
+ return `${endDate.year.toString()} YTD`;
243
243
  }
244
244
 
245
245
  function getFilteredTransactions(transactions) {
@@ -6,8 +6,7 @@ const array = require('@barchart/common-js/lang/array'),
6
6
  is = require('@barchart/common-js/lang/is'),
7
7
  Tree = require('@barchart/common-js/collections/Tree');
8
8
 
9
- const InstrumentType = require('./../data/InstrumentType'),
10
- PositionSummaryFrame = require('./../data/PositionSummaryFrame');
9
+ const PositionSummaryFrame = require('./../data/PositionSummaryFrame');
11
10
 
12
11
  const PositionGroup = require('./PositionGroup'),
13
12
  PositionItem = require('./PositionItem');
@@ -24,7 +23,7 @@ module.exports = (() => {
24
23
  this._defaultCurrency = defaultCurrency || Currency.CAD;
25
24
 
26
25
  this._summaryFrame = summaryFrameType || PositionSummaryFrame.YEARLY;
27
- this._summaryRanges = this._summaryFrame.getRecentRanges(2);
26
+ this._summaryRanges = this._summaryFrame.getRecentRanges(1);
28
27
 
29
28
  this._portfolios = portfolios.reduce((map, portfolio) => {
30
29
  map[portfolio.portfolio] = portfolio;
@@ -40,7 +39,7 @@ module.exports = (() => {
40
39
  map[key] = getSummaryArray(this._summaryRanges);
41
40
  }
42
41
 
43
- const index = this._summaryRanges.findIndex(r => r.start === summary.start.date && r.end === summary.end.date);
42
+ const index = this._summaryRanges.findIndex(r => r.start.getIsEqual(summary.start.date) && r.end.getIsEqual(summary.end.date));
44
43
 
45
44
  if (!(index < 0)) {
46
45
  map[key][index] = summary;
@@ -110,13 +109,13 @@ module.exports = (() => {
110
109
  const populatedGroups = Object.keys(populatedObjects).map(key => populatedObjects[key]).map((items) => {
111
110
  const first = items[0];
112
111
 
113
- return new PositionGroup(parent, items, currentDefinition.currencySelector(first), currentDefinition.descriptionSelector(first), currentDefinition.single && items.length === 1);
112
+ return new PositionGroup(this, parent, items, currentDefinition.currencySelector(first), currentDefinition.descriptionSelector(first), currentDefinition.single && items.length === 1);
114
113
  });
115
114
 
116
115
  const missingGroups = array.difference(currentDefinition.requiredGroups.map(group => group.description), populatedGroups.map(group => group.description));
117
116
 
118
117
  const empty = missingGroups.map((description) => {
119
- return new PositionGroup(parent, [ ], currentDefinition.requiredGroups.find(group => group.description === description).currency, description);
118
+ return new PositionGroup(this, parent, [ ], currentDefinition.requiredGroups.find(group => group.description === description).currency, description);
120
119
  });
121
120
 
122
121
  const compositeGroups = populatedGroups.concat(empty);
@@ -187,10 +186,20 @@ module.exports = (() => {
187
186
  }, [ ]);
188
187
  }
189
188
 
190
- setExchangeRage(symbol, price) {
189
+ setExchangeRate(symbol, price) {
191
190
 
192
191
  }
193
192
 
193
+ startTransaction(executor) {
194
+ assert.argumentIsRequired(executor, 'executor', Function);
195
+
196
+ this._tree.walk(group => group.setSuspended(true), false, false);
197
+
198
+ executor(this);
199
+
200
+ this._tree.walk(group => group.setSuspended(false), false, false);
201
+ }
202
+
194
203
  getGroup(keys) {
195
204
  const node = keys.reduce((tree, key) => {
196
205
  tree = tree.findChild(group => group.description === key);
@@ -11,7 +11,8 @@ module.exports = (() => {
11
11
  * @public
12
12
  */
13
13
  class PositionGroup {
14
- constructor(parent, items, currency, description, single) {
14
+ constructor(container, parent, items, currency, description, single) {
15
+ this._container = container;
15
16
  this._parent = parent || null;
16
17
 
17
18
  this._items = items;
@@ -21,6 +22,9 @@ module.exports = (() => {
21
22
 
22
23
  this._single = is.boolean(single) && single;
23
24
 
25
+ this._excluded = false;
26
+ this._suspended = false;
27
+
24
28
  this._dataFormat = { };
25
29
  this._dataActual = { };
26
30
 
@@ -62,12 +66,11 @@ module.exports = (() => {
62
66
  this._dataFormat.currentPrice = null;
63
67
  }
64
68
 
65
- calculatePriceData(this, sender);
69
+ calculatePriceData(this, sender, false);
66
70
  });
67
71
  });
68
72
 
69
- calculateStaticData(this);
70
- calculatePriceData(this);
73
+ this.refresh();
71
74
  }
72
75
 
73
76
  get items() {
@@ -90,6 +93,41 @@ module.exports = (() => {
90
93
  return this._single;
91
94
  }
92
95
 
96
+ get suspended() {
97
+ return this._suspended;
98
+ }
99
+
100
+ get excluded() {
101
+ return this._excluded;
102
+ }
103
+
104
+ setExcluded(value) {
105
+ assert.argumentIsRequired(value, 'value', Boolean);
106
+
107
+ if (this._excluded !== value) {
108
+ this._container.startTransaction(() => {
109
+ this._items.forEach((item) => {
110
+ item.setExcluded(value);
111
+ });
112
+ });
113
+ }
114
+ }
115
+
116
+ setSuspended(value) {
117
+ assert.argumentIsRequired(value, 'value', Boolean);
118
+
119
+ if (this._suspended !== value) {
120
+ if (this._suspended = value) {
121
+ this.refresh();
122
+ }
123
+ }
124
+ }
125
+
126
+ refresh() {
127
+ calculateStaticData(this);
128
+ calculatePriceData(this, null, true);
129
+ }
130
+
93
131
  toString() {
94
132
  return '[PositionGroup]';
95
133
  }
@@ -116,6 +154,10 @@ module.exports = (() => {
116
154
  }
117
155
 
118
156
  function calculateStaticData(group) {
157
+ if (group.suspended) {
158
+ return;
159
+ }
160
+
119
161
  const actual = group._dataActual;
120
162
  const format = group._dataFormat;
121
163
 
@@ -152,7 +194,11 @@ module.exports = (() => {
152
194
  format.summaryTwoTotal = formatCurrency(updates.summaryTwoTotal, currency);
153
195
  }
154
196
 
155
- function calculatePriceData(group, item) {
197
+ function calculatePriceData(group, item, forceRefresh) {
198
+ if (group.suspended) {
199
+ return;
200
+ }
201
+
156
202
  const parent = group._parent;
157
203
 
158
204
  const actual = group._dataActual;
@@ -160,14 +206,11 @@ module.exports = (() => {
160
206
 
161
207
  const currency = group.currency;
162
208
 
209
+ const refresh = (is.boolean(forceRefresh) && forceRefresh) || (actual.market === null || actual.unrealizedToday === null || actual.total === null);
210
+
163
211
  let updates;
164
212
 
165
- if (actual.market !== null && actual.unrealizedToday !== null && actual.total !== null) {
166
- updates = {
167
- market: actual.market.add(item.data.marketChange),
168
- unrealizedToday: actual.unrealizedToday.add(item.data.unrealizedTodayChange)
169
- };
170
- } else {
213
+ if (refresh) {
171
214
  const items = group._items;
172
215
 
173
216
  updates = items.reduce((updates, item) => {
@@ -177,8 +220,14 @@ module.exports = (() => {
177
220
  return updates;
178
221
  }, {
179
222
  market: Decimal.ZERO,
223
+
180
224
  unrealizedToday: Decimal.ZERO
181
225
  });
226
+ } else {
227
+ updates = {
228
+ market: actual.market.add(item.data.marketChange),
229
+ unrealizedToday: actual.unrealizedToday.add(item.data.unrealizedTodayChange)
230
+ };
182
231
  }
183
232
 
184
233
  if (parent !== null) {
@@ -33,10 +33,13 @@ module.exports = (() => {
33
33
  this._data.realized = null;
34
34
  this._data.income = null;
35
35
 
36
+ this._excluded = false;
37
+
36
38
  calculateStaticData(this);
37
39
  calculatePriceData(this, null);
38
40
 
39
41
  this._priceChangeEvent = new Event(this);
42
+ this._excludedChangeEvent = new Event(this);
40
43
  }
41
44
 
42
45
  get portfolio() {
@@ -55,7 +58,13 @@ module.exports = (() => {
55
58
  return this._data;
56
59
  }
57
60
 
61
+ get excluded() {
62
+ return this._excluded;
63
+ }
64
+
58
65
  setPrice(price) {
66
+ assert.argumentIsRequired(price, 'price', Number);
67
+
59
68
  if (this._data.price !== price) {
60
69
  calculatePriceData(this, this._data.currentPrice = price);
61
70
 
@@ -63,12 +72,22 @@ module.exports = (() => {
63
72
  }
64
73
  }
65
74
 
66
- registerPriceChangeHandler(handler) {
67
- assert.argumentIsRequired(handler, 'handler', Function);
75
+ setExcluded(value) {
76
+ assert.argumentIsRequired(value, 'value', Boolean);
68
77
 
78
+ if (this._excluded !== value) {
79
+ this._excludedChangeEvent.fire(this, this._excluded = value);
80
+ }
81
+ }
82
+
83
+ registerPriceChangeHandler(handler) {
69
84
  this._priceChangeEvent.register(handler);
70
85
  }
71
86
 
87
+ registerExcludedChangeHandler(handler) {
88
+ this._excludedChangeEvent.register(handler);
89
+ }
90
+
72
91
  toString() {
73
92
  return '[PositionItem]';
74
93
  }
@@ -99,7 +118,7 @@ module.exports = (() => {
99
118
  const getSummaryTotal = (index) => {
100
119
  let summaryTotal;
101
120
 
102
- if (summaries.length > (index + 1) && summaries[index] !== null) {
121
+ if (summaries.length > index && summaries[index] !== null) {
103
122
  const period = summaries[index].period;
104
123
 
105
124
  summaryTotal = period.realized.add(period.unrealized).add(period.income);
@@ -155,6 +155,7 @@ module.exports = (() => {
155
155
  );
156
156
 
157
157
  const update = new PortfolioSchema(SchemaBuilder.withName('update')
158
+ .withField('portfolio', DataType.STRING)
158
159
  .withField('name', DataType.STRING)
159
160
  .withField('timezone', DataType.forEnum(Timezones, 'Timezone'), true)
160
161
  .withField('defaults.currency', DataType.forEnum(Currency, 'Currency'), true)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barchart/portfolio-api-common",
3
- "version": "1.0.69",
3
+ "version": "1.0.73",
4
4
  "description": "Common classes used by the Portfolio system",
5
5
  "author": {
6
6
  "name": "Bryan Ingle",
@@ -14,15 +14,23 @@ module.exports = (() => {
14
14
  * @param {String} alternateDescription
15
15
  * @param {String} code
16
16
  * @param {Boolean} canReinvest
17
+ * @param {Boolean} usesSymbols
17
18
  */
18
19
  class InstrumentType extends Enum {
19
- constructor(code, description, alternateDescription, canReinvest) {
20
+ constructor(code, description, alternateDescription, canReinvest, usesSymbols) {
20
21
  super(code, description);
21
22
 
22
23
  this._alternateDescription = alternateDescription;
23
24
  this._canReinvest = canReinvest;
25
+ this._usesSymbols = usesSymbols;
24
26
  }
25
27
 
28
+ /**
29
+ * A human-readable description.
30
+ *
31
+ * @public
32
+ * @return {String}
33
+ */
26
34
  get alternateDescription() {
27
35
  return this._alternateDescription;
28
36
  }
@@ -30,16 +38,28 @@ module.exports = (() => {
30
38
  /**
31
39
  * Indicates if the instrument type allows automatic reinvestment.
32
40
  *
41
+ * @public
33
42
  * @returns {Boolean}
34
43
  */
35
44
  get canReinvest() {
36
45
  return this._canReinvest;
37
46
  }
38
47
 
48
+ /**
49
+ * Indicates if an instrument of this type can be represented by a symbol.
50
+ *
51
+ * @public
52
+ * @returns {Boolean}
53
+ */
54
+ get usesSymbols() {
55
+ return this._usesSymbols;
56
+ }
57
+
39
58
  /**
40
59
  * Cash.
41
60
  *
42
61
  * @public
62
+ * @static
43
63
  * @returns {InstrumentType}
44
64
  */
45
65
  static get CASH() {
@@ -50,6 +70,7 @@ module.exports = (() => {
50
70
  * An equity issue.
51
71
  *
52
72
  * @public
73
+ * @static
53
74
  * @returns {InstrumentType}
54
75
  */
55
76
  static get EQUITY() {
@@ -60,6 +81,7 @@ module.exports = (() => {
60
81
  * A mutual fund.
61
82
  *
62
83
  * @public
84
+ * @static
63
85
  * @returns {InstrumentType}
64
86
  */
65
87
  static get FUND() {
@@ -70,6 +92,7 @@ module.exports = (() => {
70
92
  * An undefined asset (e.g. a house, or a collectible, or a salvaged alien spaceship).
71
93
  *
72
94
  * @public
95
+ * @static
73
96
  * @returns {InstrumentType}
74
97
  */
75
98
  static get OTHER() {
@@ -81,10 +104,10 @@ module.exports = (() => {
81
104
  }
82
105
  }
83
106
 
84
- const cash = new InstrumentType('CASH', 'cash', 'Cash', false);
85
- const equity = new InstrumentType('EQUITY', 'equity', 'Equities', true);
86
- const fund = new InstrumentType('FUND', 'mutual fund', 'Funds', true);
87
- const other = new InstrumentType('OTHER', 'other', 'Other', false);
107
+ const cash = new InstrumentType('CASH', 'cash', 'Cash', false, false);
108
+ const equity = new InstrumentType('EQUITY', 'equity', 'Equities', true, true);
109
+ const fund = new InstrumentType('FUND', 'mutual fund', 'Funds', true, true);
110
+ const other = new InstrumentType('OTHER', 'other', 'Other', false, false);
88
111
 
89
112
  return InstrumentType;
90
113
  })();
@@ -331,7 +354,7 @@ module.exports = (() => {
331
354
  }
332
355
 
333
356
  function getYearToDateRangeDescription(startDate, endDate) {
334
- return '';
357
+ return `${endDate.year.toString()} YTD`;
335
358
  }
336
359
 
337
360
  function getFilteredTransactions(transactions) {
@@ -683,8 +706,7 @@ const array = require('@barchart/common-js/lang/array'),
683
706
  is = require('@barchart/common-js/lang/is'),
684
707
  Tree = require('@barchart/common-js/collections/Tree');
685
708
 
686
- const InstrumentType = require('./../data/InstrumentType'),
687
- PositionSummaryFrame = require('./../data/PositionSummaryFrame');
709
+ const PositionSummaryFrame = require('./../data/PositionSummaryFrame');
688
710
 
689
711
  const PositionGroup = require('./PositionGroup'),
690
712
  PositionItem = require('./PositionItem');
@@ -701,7 +723,7 @@ module.exports = (() => {
701
723
  this._defaultCurrency = defaultCurrency || Currency.CAD;
702
724
 
703
725
  this._summaryFrame = summaryFrameType || PositionSummaryFrame.YEARLY;
704
- this._summaryRanges = this._summaryFrame.getRecentRanges(2);
726
+ this._summaryRanges = this._summaryFrame.getRecentRanges(1);
705
727
 
706
728
  this._portfolios = portfolios.reduce((map, portfolio) => {
707
729
  map[portfolio.portfolio] = portfolio;
@@ -717,7 +739,7 @@ module.exports = (() => {
717
739
  map[key] = getSummaryArray(this._summaryRanges);
718
740
  }
719
741
 
720
- const index = this._summaryRanges.findIndex(r => r.start === summary.start.date && r.end === summary.end.date);
742
+ const index = this._summaryRanges.findIndex(r => r.start.getIsEqual(summary.start.date) && r.end.getIsEqual(summary.end.date));
721
743
 
722
744
  if (!(index < 0)) {
723
745
  map[key][index] = summary;
@@ -787,13 +809,13 @@ module.exports = (() => {
787
809
  const populatedGroups = Object.keys(populatedObjects).map(key => populatedObjects[key]).map((items) => {
788
810
  const first = items[0];
789
811
 
790
- return new PositionGroup(parent, items, currentDefinition.currencySelector(first), currentDefinition.descriptionSelector(first), currentDefinition.single && items.length === 1);
812
+ return new PositionGroup(this, parent, items, currentDefinition.currencySelector(first), currentDefinition.descriptionSelector(first), currentDefinition.single && items.length === 1);
791
813
  });
792
814
 
793
815
  const missingGroups = array.difference(currentDefinition.requiredGroups.map(group => group.description), populatedGroups.map(group => group.description));
794
816
 
795
817
  const empty = missingGroups.map((description) => {
796
- return new PositionGroup(parent, [ ], currentDefinition.requiredGroups.find(group => group.description === description).currency, description);
818
+ return new PositionGroup(this, parent, [ ], currentDefinition.requiredGroups.find(group => group.description === description).currency, description);
797
819
  });
798
820
 
799
821
  const compositeGroups = populatedGroups.concat(empty);
@@ -864,10 +886,20 @@ module.exports = (() => {
864
886
  }, [ ]);
865
887
  }
866
888
 
867
- setExchangeRage(symbol, price) {
889
+ setExchangeRate(symbol, price) {
868
890
 
869
891
  }
870
892
 
893
+ startTransaction(executor) {
894
+ assert.argumentIsRequired(executor, 'executor', Function);
895
+
896
+ this._tree.walk(group => group.setSuspended(true), false, false);
897
+
898
+ executor(this);
899
+
900
+ this._tree.walk(group => group.setSuspended(false), false, false);
901
+ }
902
+
871
903
  getGroup(keys) {
872
904
  const node = keys.reduce((tree, key) => {
873
905
  tree = tree.findChild(group => group.description === key);
@@ -900,7 +932,7 @@ module.exports = (() => {
900
932
  return PositionContainer;
901
933
  })();
902
934
 
903
- },{"./../data/InstrumentType":1,"./../data/PositionSummaryFrame":2,"./PositionGroup":5,"./PositionItem":7,"@barchart/common-js/collections/Tree":8,"@barchart/common-js/collections/sorting/ComparatorBuilder":9,"@barchart/common-js/collections/sorting/comparators":10,"@barchart/common-js/lang/Currency":11,"@barchart/common-js/lang/array":16,"@barchart/common-js/lang/assert":17,"@barchart/common-js/lang/is":19}],5:[function(require,module,exports){
935
+ },{"./../data/PositionSummaryFrame":2,"./PositionGroup":5,"./PositionItem":7,"@barchart/common-js/collections/Tree":8,"@barchart/common-js/collections/sorting/ComparatorBuilder":9,"@barchart/common-js/collections/sorting/comparators":10,"@barchart/common-js/lang/Currency":11,"@barchart/common-js/lang/array":16,"@barchart/common-js/lang/assert":17,"@barchart/common-js/lang/is":19}],5:[function(require,module,exports){
904
936
  const assert = require('@barchart/common-js/lang/assert'),
905
937
  Currency = require('@barchart/common-js/lang/Currency'),
906
938
  Decimal = require('@barchart/common-js/lang/Decimal'),
@@ -914,7 +946,8 @@ module.exports = (() => {
914
946
  * @public
915
947
  */
916
948
  class PositionGroup {
917
- constructor(parent, items, currency, description, single) {
949
+ constructor(container, parent, items, currency, description, single) {
950
+ this._container = container;
918
951
  this._parent = parent || null;
919
952
 
920
953
  this._items = items;
@@ -924,6 +957,9 @@ module.exports = (() => {
924
957
 
925
958
  this._single = is.boolean(single) && single;
926
959
 
960
+ this._excluded = false;
961
+ this._suspended = false;
962
+
927
963
  this._dataFormat = { };
928
964
  this._dataActual = { };
929
965
 
@@ -965,12 +1001,11 @@ module.exports = (() => {
965
1001
  this._dataFormat.currentPrice = null;
966
1002
  }
967
1003
 
968
- calculatePriceData(this, sender);
1004
+ calculatePriceData(this, sender, false);
969
1005
  });
970
1006
  });
971
1007
 
972
- calculateStaticData(this);
973
- calculatePriceData(this);
1008
+ this.refresh();
974
1009
  }
975
1010
 
976
1011
  get items() {
@@ -993,6 +1028,41 @@ module.exports = (() => {
993
1028
  return this._single;
994
1029
  }
995
1030
 
1031
+ get suspended() {
1032
+ return this._suspended;
1033
+ }
1034
+
1035
+ get excluded() {
1036
+ return this._excluded;
1037
+ }
1038
+
1039
+ setExcluded(value) {
1040
+ assert.argumentIsRequired(value, 'value', Boolean);
1041
+
1042
+ if (this._excluded !== value) {
1043
+ this._container.startTransaction(() => {
1044
+ this._items.forEach((item) => {
1045
+ item.setExcluded(value);
1046
+ });
1047
+ });
1048
+ }
1049
+ }
1050
+
1051
+ setSuspended(value) {
1052
+ assert.argumentIsRequired(value, 'value', Boolean);
1053
+
1054
+ if (this._suspended !== value) {
1055
+ if (this._suspended = value) {
1056
+ this.refresh();
1057
+ }
1058
+ }
1059
+ }
1060
+
1061
+ refresh() {
1062
+ calculateStaticData(this);
1063
+ calculatePriceData(this, null, true);
1064
+ }
1065
+
996
1066
  toString() {
997
1067
  return '[PositionGroup]';
998
1068
  }
@@ -1019,6 +1089,10 @@ module.exports = (() => {
1019
1089
  }
1020
1090
 
1021
1091
  function calculateStaticData(group) {
1092
+ if (group.suspended) {
1093
+ return;
1094
+ }
1095
+
1022
1096
  const actual = group._dataActual;
1023
1097
  const format = group._dataFormat;
1024
1098
 
@@ -1055,7 +1129,11 @@ module.exports = (() => {
1055
1129
  format.summaryTwoTotal = formatCurrency(updates.summaryTwoTotal, currency);
1056
1130
  }
1057
1131
 
1058
- function calculatePriceData(group, item) {
1132
+ function calculatePriceData(group, item, forceRefresh) {
1133
+ if (group.suspended) {
1134
+ return;
1135
+ }
1136
+
1059
1137
  const parent = group._parent;
1060
1138
 
1061
1139
  const actual = group._dataActual;
@@ -1063,14 +1141,11 @@ module.exports = (() => {
1063
1141
 
1064
1142
  const currency = group.currency;
1065
1143
 
1144
+ const refresh = (is.boolean(forceRefresh) && forceRefresh) || (actual.market === null || actual.unrealizedToday === null || actual.total === null);
1145
+
1066
1146
  let updates;
1067
1147
 
1068
- if (actual.market !== null && actual.unrealizedToday !== null && actual.total !== null) {
1069
- updates = {
1070
- market: actual.market.add(item.data.marketChange),
1071
- unrealizedToday: actual.unrealizedToday.add(item.data.unrealizedTodayChange)
1072
- };
1073
- } else {
1148
+ if (refresh) {
1074
1149
  const items = group._items;
1075
1150
 
1076
1151
  updates = items.reduce((updates, item) => {
@@ -1080,8 +1155,14 @@ module.exports = (() => {
1080
1155
  return updates;
1081
1156
  }, {
1082
1157
  market: Decimal.ZERO,
1158
+
1083
1159
  unrealizedToday: Decimal.ZERO
1084
1160
  });
1161
+ } else {
1162
+ updates = {
1163
+ market: actual.market.add(item.data.marketChange),
1164
+ unrealizedToday: actual.unrealizedToday.add(item.data.unrealizedTodayChange)
1165
+ };
1085
1166
  }
1086
1167
 
1087
1168
  if (parent !== null) {
@@ -1197,10 +1278,13 @@ module.exports = (() => {
1197
1278
  this._data.realized = null;
1198
1279
  this._data.income = null;
1199
1280
 
1281
+ this._excluded = false;
1282
+
1200
1283
  calculateStaticData(this);
1201
1284
  calculatePriceData(this, null);
1202
1285
 
1203
1286
  this._priceChangeEvent = new Event(this);
1287
+ this._excludedChangeEvent = new Event(this);
1204
1288
  }
1205
1289
 
1206
1290
  get portfolio() {
@@ -1219,7 +1303,13 @@ module.exports = (() => {
1219
1303
  return this._data;
1220
1304
  }
1221
1305
 
1306
+ get excluded() {
1307
+ return this._excluded;
1308
+ }
1309
+
1222
1310
  setPrice(price) {
1311
+ assert.argumentIsRequired(price, 'price', Number);
1312
+
1223
1313
  if (this._data.price !== price) {
1224
1314
  calculatePriceData(this, this._data.currentPrice = price);
1225
1315
 
@@ -1227,12 +1317,22 @@ module.exports = (() => {
1227
1317
  }
1228
1318
  }
1229
1319
 
1230
- registerPriceChangeHandler(handler) {
1231
- assert.argumentIsRequired(handler, 'handler', Function);
1320
+ setExcluded(value) {
1321
+ assert.argumentIsRequired(value, 'value', Boolean);
1322
+
1323
+ if (this._excluded !== value) {
1324
+ this._excludedChangeEvent.fire(this, this._excluded = value);
1325
+ }
1326
+ }
1232
1327
 
1328
+ registerPriceChangeHandler(handler) {
1233
1329
  this._priceChangeEvent.register(handler);
1234
1330
  }
1235
1331
 
1332
+ registerExcludedChangeHandler(handler) {
1333
+ this._excludedChangeEvent.register(handler);
1334
+ }
1335
+
1236
1336
  toString() {
1237
1337
  return '[PositionItem]';
1238
1338
  }
@@ -1263,7 +1363,7 @@ module.exports = (() => {
1263
1363
  const getSummaryTotal = (index) => {
1264
1364
  let summaryTotal;
1265
1365
 
1266
- if (summaries.length > (index + 1) && summaries[index] !== null) {
1366
+ if (summaries.length > index && summaries[index] !== null) {
1267
1367
  const period = summaries[index].period;
1268
1368
 
1269
1369
  summaryTotal = period.realized.add(period.unrealized).add(period.income);