@barchart/portfolio-api-common 1.0.165 → 1.0.169

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.
@@ -11,7 +11,9 @@ const array = require('@barchart/common-js/lang/array'),
11
11
 
12
12
  const PositionSummaryFrame = require('./../data/PositionSummaryFrame');
13
13
 
14
- const PositionTreeDefinition = require('./definitions/PositionTreeDefinition');
14
+ const PositionLevelDefinition = require('./definitions/PositionLevelDefinition'),
15
+ PositionLevelType = require('./definitions/PositionLevelType'),
16
+ PositionTreeDefinition = require('./definitions/PositionTreeDefinition');
15
17
 
16
18
  const PositionGroup = require('./PositionGroup'),
17
19
  PositionItem = require('./PositionItem');
@@ -128,10 +130,9 @@ module.exports = (() => {
128
130
  }, { });
129
131
 
130
132
  this._currencies = this._items.reduce((map, item) => {
131
- const position = item.position;
133
+ const currency = extractCurrency(item.position);
132
134
 
133
- if (position.instrument && position.instrument.currency) {
134
- const currency = position.instrument.currency;
135
+ if (currency) {
135
136
  const code = currency.code;
136
137
 
137
138
  if (!map.hasOwnProperty(code)) {
@@ -167,6 +168,13 @@ module.exports = (() => {
167
168
  }, { });
168
169
  }
169
170
 
171
+ /**
172
+ * Adds a new portfolio to the container, injecting it into aggregation
173
+ * trees, as necessary.
174
+ *
175
+ * @public
176
+ * @param {Object} portfolio
177
+ */
170
178
  addPortfolio(portfolio) {
171
179
  assert.argumentIsRequired(portfolio, 'portfolio', Object);
172
180
  assert.argumentIsRequired(portfolio.portfolio, 'portfolio.portfolio', String);
@@ -209,7 +217,7 @@ module.exports = (() => {
209
217
  if (group.definition === parentLevelDefinition) {
210
218
  parentTrees.push(groupTree);
211
219
  }
212
- });
220
+ }, false, false);
213
221
  }
214
222
 
215
223
  const overrideRequiredGroups = [ portfolioRequiredGroup ];
@@ -222,8 +230,33 @@ module.exports = (() => {
222
230
  }
223
231
  }
224
232
 
233
+ /**
234
+ * Removes an existing portfolio, and all of it's positions, from the container. This
235
+ * also triggers removal of the portfolio and it's positions from any applicable
236
+ * aggregation trees.
237
+ *
238
+ * @public
239
+ * @param {Object} portfolio
240
+ */
225
241
  removePortfolio(portfolio) {
242
+ assert.argumentIsRequired(portfolio, 'portfolio', Object);
243
+ assert.argumentIsRequired(portfolio.portfolio, 'portfolio.portfolio', String);
244
+
245
+ this.startTransaction(() => {
246
+ getPositionItemsForPortfolio(this._items, portfolio.portfolio).forEach(item => removePositionItem.call(this, item));
247
+
248
+ delete this._portfolios[portfolio.portfolio];
249
+
250
+ Object.keys(this._trees).forEach((key) => {
251
+ const tree = this._tree[key];
226
252
 
253
+ tree.walk((group, groupNode) => {
254
+ if (group.definition.type === PositionLevelType.PORTFOLIO && group.key === PositionLevelDefinition.getKeyForPortfolioGroup(portfolio)) {
255
+ groupNode.sever();
256
+ }
257
+ }, true, false);
258
+ });
259
+ });
227
260
  }
228
261
 
229
262
  mutatePosition(position, summary) {
@@ -231,7 +264,7 @@ module.exports = (() => {
231
264
  }
232
265
 
233
266
  removePosition(position) {
234
-
267
+ removePositionItem.call(this, this._items.find((item) => item.position.position === position));
235
268
  }
236
269
 
237
270
  /**
@@ -314,7 +347,6 @@ module.exports = (() => {
314
347
  assert.argumentIsRequired(quote, 'quote', Object);
315
348
 
316
349
  const rate = Rate.fromPair(quote.lastPrice, symbol);
317
-
318
350
  const index = this._forexQuotes.findIndex(existing => existing.formatPair() === rate.formatPair());
319
351
 
320
352
  if (index < 0) {
@@ -409,13 +441,13 @@ module.exports = (() => {
409
441
  }
410
442
 
411
443
  /**
412
- * Returns all portfolios in the container
444
+ * Returns all portfolios in the container.
413
445
  *
414
446
  * @public
415
447
  * @return {Array.<Object>}
416
448
  */
417
449
  getPortfolios() {
418
- return this._portfolios;
450
+ return Object.keys(this._portfolios).map(id => this._portfolios[id]);
419
451
  }
420
452
 
421
453
  /**
@@ -426,24 +458,42 @@ module.exports = (() => {
426
458
  * @return {Array.<Object>}
427
459
  */
428
460
  getPositions(portfolio) {
429
- return this._items.reduce((positions, item) => {
430
- if (item.position.portfolio === portfolio) {
431
- positions.push(item);
432
- }
461
+ assert.argumentIsRequired(portfolio, 'portfolio', String);
433
462
 
434
- return positions;
435
- }, []);
463
+ return getPositionItemsForPortfolio(this._items, portfolio).map(item => item.position);
436
464
  }
437
465
 
438
- startTransaction(name, executor) {
439
- assert.argumentIsRequired(name, 'name', String);
466
+ /**
467
+ * Pauses aggregation calculations during the processing of an action.
468
+ *
469
+ * @public
470
+ * @param {Function} executor
471
+ * @param {String=|Array.<String>=} names
472
+ */
473
+ startTransaction(executor, names) {
474
+ let namesToUse;
475
+
476
+ if (is.array(names)) {
477
+ assert.argumentIsArray(names, 'names', String);
478
+
479
+ namesToUse = names;
480
+ } else {
481
+ assert.argumentIsOptional(names, 'names', String);
482
+
483
+ if (names) {
484
+ namesToUse = [ names ];
485
+ } else {
486
+ namesToUse = Object.keys(this._trees);
487
+ }
488
+ }
489
+
440
490
  assert.argumentIsRequired(executor, 'executor', Function);
441
491
 
442
- this._trees[name].walk(group => group.setSuspended(true), false, false);
492
+ namesToUse.forEach((name) => this._trees[name].walk(group => group.setSuspended(true), false, false));
443
493
 
444
494
  executor(this);
445
495
 
446
- this._trees[name].walk(group => group.setSuspended(false), false, false);
496
+ namesToUse.forEach((name) => this._trees[name].walk(group => group.setSuspended(false), false, false));
447
497
  }
448
498
 
449
499
  toString() {
@@ -475,6 +525,14 @@ module.exports = (() => {
475
525
  }
476
526
  }
477
527
 
528
+ function extractCurrency(position) {
529
+ if (position.instrument && position.instrument.currency) {
530
+ return position.instrument.currency;
531
+ } else {
532
+ return null;
533
+ }
534
+ }
535
+
478
536
  function addGroupBinding(group, dispoable) {
479
537
  const id = group.id;
480
538
 
@@ -604,5 +662,46 @@ module.exports = (() => {
604
662
  });
605
663
  }
606
664
 
665
+ function getPositionItemsForPortfolio(items, portfolio) {
666
+ return items.reduce((positionItems, item) => {
667
+ if (item.position.portfolio === portfolio) {
668
+ positionItems.push(item.position);
669
+ }
670
+
671
+ return positionItems;
672
+ }, [ ]);
673
+ }
674
+
675
+ function removePositionItem(positionItem) {
676
+ if (!positionItem) {
677
+ return;
678
+ }
679
+
680
+ delete this._summariesCurrent[positionItem.position.position];
681
+ delete this._summariesPrevious[positionItem.position.position];
682
+
683
+ array.remove(this._items, i => i === positionItem);
684
+
685
+ const barchartSymbol = extractSymbolForBarchart(positionItem.position);
686
+
687
+ if (this._symbols.hasOwnProperty(barchartSymbol)) {
688
+ array.remove(this._symbols[barchartSymbol], i => i === positionItem);
689
+ }
690
+
691
+ const displaySymbol = extractSymbolForDisplay(positionItem.position);
692
+
693
+ if (this._symbolsDisplay.hasOwnProperty(displaySymbol)) {
694
+ array.remove(this._symbols[displaySymbol], i => i === positionItem);
695
+ }
696
+
697
+ const currency = extractCurrency(positionItem.position);
698
+
699
+ if (currency && this._currencies.hasOwnProperty(currency.code)) {
700
+ array.remove(this._currencies[currency.code], i => i === positionItem);
701
+ }
702
+
703
+ positionItem.dispose();
704
+ }
705
+
607
706
  return PositionContainer;
608
707
  })();
@@ -2,6 +2,7 @@ const array = require('@barchart/common-js/lang/array'),
2
2
  assert = require('@barchart/common-js/lang/assert'),
3
3
  Currency = require('@barchart/common-js/lang/Currency'),
4
4
  Decimal = require('@barchart/common-js/lang/Decimal'),
5
+ Disposable = require('@barchart/common-js/lang/Disposable'),
5
6
  DisposableStack = require('@barchart/common-js/collections/specialized/DisposableStack'),
6
7
  Event = require('@barchart/common-js/messaging/Event'),
7
8
  formatter = require('@barchart/common-js/lang/formatter'),
@@ -149,7 +150,7 @@ module.exports = (() => {
149
150
  this._dataFormat.portfolioType = null;
150
151
 
151
152
  this._items.forEach((item) => {
152
- this._disposeStack.push(item.registerQuoteChangeHandler((quote, sender) => {
153
+ const quoteBinding = item.registerQuoteChangeHandler((quote, sender) => {
153
154
  if (this._single) {
154
155
  const precision = sender.position.instrument.currency.precision;
155
156
 
@@ -185,18 +186,39 @@ module.exports = (() => {
185
186
  }
186
187
 
187
188
  calculatePriceData(this, this._container.getForexQuotes(), sender, false);
188
- }));
189
+ });
190
+
191
+ let newsBinding = Disposable.getEmpty();
192
+ let fundamentalBinding = Disposable.getEmpty();
189
193
 
190
194
  if (this._single) {
191
- this._disposeStack.push(item.registerNewsExistsChangeHandler((exists, sender) => {
195
+ newsBinding = item.registerNewsExistsChangeHandler((exists, sender) => {
192
196
  this._dataActual.newsExists = exists;
193
197
  this._dataFormat.newsExists = exists;
194
- }));
198
+ });
195
199
 
196
- this._disposeStack.push(item.registerFundamentalDataChangeHandler((data, sender) => {
200
+ fundamentalBinding = item.registerFundamentalDataChangeHandler((data, sender) => {
197
201
  this._dataFormat.fundamental = data;
198
- }));
202
+ });
199
203
  }
204
+
205
+ this._disposeStack.push(quoteBinding);
206
+ this._disposeStack.push(newsBinding);
207
+ this._disposeStack.push(fundamentalBinding);
208
+
209
+ this._disposeStack.push(item.registerPositionItemDisposeHandler(() => {
210
+ quoteBinding.dispose();
211
+ newsBinding.dispose();
212
+ fundamentalBinding.dispose();
213
+
214
+ array.remove(this._items, i => i === item);
215
+ array.remove(this._excludedItems, i => i === item);
216
+ array.remove(this._consideredItems, i => i === item);
217
+
218
+ delete this._excludedItemMap[item.position.position];
219
+
220
+ this.refresh();
221
+ }));
200
222
  });
201
223
 
202
224
  this.refresh();
@@ -373,22 +395,35 @@ module.exports = (() => {
373
395
  }
374
396
  }
375
397
 
398
+ /**
399
+ * Stops (or starts) group-level aggregation calculations.
400
+ *
401
+ * @public
402
+ * @param {Boolean} value
403
+ */
376
404
  setSuspended(value) {
377
405
  assert.argumentIsRequired(value, 'value', Boolean);
378
406
 
379
407
  if (this._suspended !== value) {
380
- if (this._suspended = value) {
408
+ this._suspended = value;
409
+
410
+ if (!this._suspended) {
381
411
  this.refresh();
382
412
  }
383
413
  }
384
414
  }
385
415
 
386
416
  /**
387
- * Causes all aggregated data to be recalculated.
417
+ * Causes all aggregated data to be recalculated (assuming the group has not
418
+ * been suspended).
388
419
  *
389
420
  * @public
390
421
  */
391
422
  refresh() {
423
+ if (this._suspended) {
424
+ return;
425
+ }
426
+
392
427
  const rates = this._container.getForexQuotes();
393
428
 
394
429
  calculateStaticData(this, rates);
@@ -2,6 +2,7 @@ const array = require('@barchart/common-js/lang/array'),
2
2
  assert = require('@barchart/common-js/lang/assert'),
3
3
  Currency = require('@barchart/common-js/lang/Currency'),
4
4
  Decimal = require('@barchart/common-js/lang/Decimal'),
5
+ Disposable = require('@barchart/common-js/lang/Disposable'),
5
6
  Event = require('@barchart/common-js/messaging/Event'),
6
7
  is = require('@barchart/common-js/lang/is');
7
8
 
@@ -21,8 +22,10 @@ module.exports = (() => {
21
22
  * @param {Object} currentSummary
22
23
  * @param {Array.<Object>} previousSummaries
23
24
  */
24
- class PositionItem {
25
+ class PositionItem extends Disposable {
25
26
  constructor(portfolio, position, currentSummary, previousSummaries) {
27
+ super();
28
+
26
29
  this._portfolio = portfolio;
27
30
  this._position = position;
28
31
  this._currency = position.instrument.currency || Currency.CAD;
@@ -67,6 +70,7 @@ module.exports = (() => {
67
70
  this._quoteChangedEvent = new Event(this);
68
71
  this._newsExistsChangedEvent = new Event(this);
69
72
  this._fundamentalDataChangeEvent = new Event(this);
73
+ this._positionItemDisposeEvent = new Event(this);
70
74
  }
71
75
 
72
76
  /**
@@ -149,6 +153,10 @@ module.exports = (() => {
149
153
  setQuote(quote) {
150
154
  assert.argumentIsRequired(quote, 'quote', Object);
151
155
 
156
+ if (this.getIsDisposed()) {
157
+ return;
158
+ }
159
+
152
160
  if (this._currentPricePrevious !== quote.lastPrice) {
153
161
  calculatePriceData(this, quote.lastPrice);
154
162
 
@@ -170,6 +178,10 @@ module.exports = (() => {
170
178
  setPositionFundamentalData(data) {
171
179
  assert.argumentIsRequired(data, 'data', Object);
172
180
 
181
+ if (this.getIsDisposed()) {
182
+ return;
183
+ }
184
+
173
185
  this._fundamentalDataChangeEvent.fire(this._data.fundamental = data);
174
186
  }
175
187
 
@@ -183,6 +195,10 @@ module.exports = (() => {
183
195
  setNewsArticleExists(value) {
184
196
  assert.argumentIsRequired(value, 'value', Boolean);
185
197
 
198
+ if (this.getIsDisposed()) {
199
+ return;
200
+ }
201
+
186
202
  if (this._data.newsExists !== value) {
187
203
  this._newsExistsChangedEvent.fire(this._data.newsExists = value);
188
204
  }
@@ -222,6 +238,26 @@ module.exports = (() => {
222
238
  return this._newsExistsChangedEvent.register(handler);
223
239
  }
224
240
 
241
+ /**
242
+ * Registers an observer for object disposal.
243
+ *
244
+ * @public
245
+ * @param {Function} handler
246
+ * @returns {Disposable}
247
+ */
248
+ registerPositionItemDisposeHandler(handler) {
249
+ return this._positionItemDisposeEvent.register(handler);
250
+ }
251
+
252
+ _onDispose() {
253
+ this._positionItemDisposeEvent.fire(this);
254
+
255
+ this._quoteChangedEvent.clear();
256
+ this._newsExistsChangedEvent.clear();
257
+ this._fundamentalDataChangeEvent.clear();
258
+ this._positionItemDisposeEvent.clear();
259
+ }
260
+
225
261
  toString() {
226
262
  return '[PositionItem]';
227
263
  }
@@ -4,6 +4,8 @@ const assert = require('@barchart/common-js/lang/assert'),
4
4
 
5
5
  const InstrumentType = require('./../../data/InstrumentType');
6
6
 
7
+ const PositionLevelType = require('./PositionLevelType');
8
+
7
9
  module.exports = (() => {
8
10
  'use strict';
9
11
 
@@ -20,11 +22,12 @@ module.exports = (() => {
20
22
  * @param {Array.<PositionLevelDefinition~RequiredGroup>=} requiredGroups
21
23
  * @param {Boolean=} single
22
24
  * @param {Boolean=} aggregateCash
23
- * @param {Function=} injectPositions
25
+ * @param {Function=} requiredGroupGenerator
24
26
  */
25
27
  class PositionLevelDefinition {
26
- constructor(name, keySelector, descriptionSelector, currencySelector, requiredGroups, single, aggregateCash, requiredGroupGenerator) {
28
+ constructor(name, type, keySelector, descriptionSelector, currencySelector, requiredGroups, single, aggregateCash, requiredGroupGenerator) {
27
29
  assert.argumentIsRequired(name, 'name', String);
30
+ assert.argumentIsRequired(type, 'type', PositionLevelType, 'PositionLevelType');
28
31
  assert.argumentIsRequired(keySelector, 'keySelector', Function);
29
32
  assert.argumentIsRequired(descriptionSelector, 'descriptionSelector', Function);
30
33
  assert.argumentIsRequired(currencySelector, 'currencySelector', Function);
@@ -38,6 +41,7 @@ module.exports = (() => {
38
41
  assert.argumentIsOptional(requiredGroupGenerator, 'requiredGroupGenerator', Function);
39
42
 
40
43
  this._name = name;
44
+ this._type = type;
41
45
 
42
46
  this._keySelector = keySelector;
43
47
  this._descriptionSelector = descriptionSelector;
@@ -61,6 +65,16 @@ module.exports = (() => {
61
65
  return this._name;
62
66
  }
63
67
 
68
+ /**
69
+ * A general description of the type of items grouped together.
70
+ *
71
+ * @public
72
+ * @return {PositionLevelType}
73
+ */
74
+ get type() {
75
+ return this._type;
76
+ }
77
+
64
78
  /**
65
79
  * A function, when given a {@link PositionItem} returns a string that is used
66
80
  * to group {@link PositionItem} instances into different groups.
@@ -0,0 +1,29 @@
1
+ const Enum = require('@barchart/common-js/lang/Enum');
2
+
3
+ module.exports = (() => {
4
+ 'use strict';
5
+
6
+ class PositionLevelType extends Enum {
7
+ constructor(code) {
8
+ super(code, code);
9
+ }
10
+
11
+ static get PORTFOLIO() {
12
+ return portfolio;
13
+ }
14
+
15
+ static get POSITION() {
16
+ return position;
17
+ }
18
+
19
+ static get OTHER() {
20
+ return other;
21
+ }
22
+ }
23
+
24
+ const portfolio = new PositionLevelType('PORTFOLIO');
25
+ const position = new PositionLevelType('POSITION');
26
+ const other = new PositionLevelType('OTHER');
27
+
28
+ return PositionLevelType;
29
+ })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barchart/portfolio-api-common",
3
- "version": "1.0.165",
3
+ "version": "1.0.169",
4
4
  "description": "Common classes used by the Portfolio system",
5
5
  "author": {
6
6
  "name": "Bryan Ingle",