@barchart/portfolio-api-common 1.0.263 → 1.0.267

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.
@@ -1,4 +1,5 @@
1
- const assert = require('@barchart/common-js/lang/assert');
1
+ const assert = require('@barchart/common-js/lang/assert'),
2
+ array = require('@barchart/common-js/lang/array')
2
3
 
3
4
  const InstrumentType = require('./InstrumentType'),
4
5
  PositionDirection = require('./PositionDirection'),
@@ -17,6 +18,31 @@ module.exports = (() => {
17
18
 
18
19
  }
19
20
 
21
+ /**
22
+ * Given a set of transaction, ensures that sequence numbers and dates
23
+ * are properly ordered.
24
+ *
25
+ * @public
26
+ * @static
27
+ * @param {Array.<Object>} transactions
28
+ * @param {Boolean=} partial - If true, sequence validation starts with the array's first transaction.
29
+ * @return {boolean}
30
+ */
31
+ static validateOrder(transactions, partial) {
32
+ assert.argumentIsArray(transactions, 'transactions');
33
+ assert.argumentIsOptional(partial, 'partial', Boolean);
34
+
35
+ let startSequence;
36
+
37
+ if (partial && transactions.length !== 0) {
38
+ startSequence = array.first(transactions).sequence;
39
+ } else {
40
+ startSequence = 1;
41
+ }
42
+
43
+ return transactions.every((t, i) => t.sequence === (i + startSequence) && (i === 0 || !t.date.getIsBefore(transactions[i - 1].date)));
44
+ }
45
+
20
46
  /**
21
47
  * Given an instrument type, returns all valid transaction types.
22
48
  *
@@ -146,6 +146,8 @@ module.exports = (() => {
146
146
  }, { });
147
147
 
148
148
  Object.keys(this._portfolios).forEach(key => updateEmptyPortfolioGroups.call(this, this._portfolios[key]));
149
+
150
+ recalculatePercentages.call(this);
149
151
  }
150
152
 
151
153
  /**
@@ -324,6 +326,8 @@ module.exports = (() => {
324
326
  this._positionSymbolAddedEvent.fire(addedBarchartSymbol);
325
327
  }
326
328
  });
329
+
330
+ recalculatePercentages.call(this);
327
331
  }
328
332
 
329
333
  /**
@@ -337,6 +341,8 @@ module.exports = (() => {
337
341
  assert.argumentIsRequired(position.position, 'position.position', String);
338
342
 
339
343
  removePositionItem.call(this, this._items.find((item) => item.position.position === position.position));
344
+
345
+ recalculatePercentages.call(this);
340
346
  }
341
347
 
342
348
  /**
@@ -384,6 +390,8 @@ module.exports = (() => {
384
390
  if (this._symbols.hasOwnProperty(symbol)) {
385
391
  this._symbols[symbol].forEach(item => item.setQuote(quote));
386
392
  }
393
+
394
+ recalculatePercentages.call(this);
387
395
  }
388
396
 
389
397
  /**
@@ -427,7 +435,9 @@ module.exports = (() => {
427
435
  this._forexQuotes[index] = rate;
428
436
  }
429
437
 
430
- Object.keys(this._trees).forEach(key => this._trees[key].walk(group => group.setForexRate(rate), true, false));
438
+ Object.keys(this._trees).forEach(key => this._trees[key].walk(group => group.setForexRates(this._forexQuotes), true, false));
439
+
440
+ recalculatePercentages.call(this);
431
441
  }
432
442
 
433
443
  /**
@@ -739,12 +749,6 @@ module.exports = (() => {
739
749
  }
740
750
  }
741
751
  }));
742
-
743
- addGroupBinding.call(this, group, group.registerMarketPercentChangeHandler(() => {
744
- if (!groupTree.getIsRoot()) {
745
- groupTree.getParent().walk((childGroup) => childGroup.refreshMarketPercent());
746
- }
747
- }));
748
752
  }
749
753
 
750
754
  function createGroups(parentTree, items, treeDefinition, levelDefinitions, overrideRequiredGroups) {
@@ -752,6 +756,8 @@ module.exports = (() => {
752
756
  return;
753
757
  }
754
758
 
759
+ const rates = this._forexQuotes;
760
+
755
761
  const levelDefinition = levelDefinitions[0];
756
762
 
757
763
  const populatedObjects = array.groupBy(items, levelDefinition.keySelector);
@@ -759,7 +765,7 @@ module.exports = (() => {
759
765
  const items = populatedObjects[key];
760
766
  const first = items[0];
761
767
 
762
- list.push(new PositionGroup(this, levelDefinition, items, levelDefinition.currencySelector(first), key, levelDefinition.descriptionSelector(first), levelDefinition.aggregateCash));
768
+ list.push(new PositionGroup(levelDefinition, items, rates, levelDefinition.currencySelector(first), key, levelDefinition.descriptionSelector(first), levelDefinition.aggregateCash));
763
769
 
764
770
  return list;
765
771
  }, [ ]);
@@ -772,7 +778,7 @@ module.exports = (() => {
772
778
  });
773
779
 
774
780
  const empty = missingGroups.map((group) => {
775
- return new PositionGroup(this, levelDefinition, [ ], group.currency, group.key, group.description);
781
+ return new PositionGroup(levelDefinition, [ ], rates, group.currency, group.key, group.description);
776
782
  });
777
783
 
778
784
  const compositeGroups = populatedGroups.concat(empty);
@@ -812,6 +818,9 @@ module.exports = (() => {
812
818
 
813
819
  this._nodes[group.id] = childTree;
814
820
 
821
+ group.setParentGroup(this.getParentGroup(group));
822
+ group.setPortfolioGroup(this.getParentGroupForPortfolio(group));
823
+
815
824
  initializeGroupObservers.call(this, childTree, treeDefinition);
816
825
 
817
826
  createGroups.call(this, childTree, group.items, treeDefinition, array.dropLeft(levelDefinitions));
@@ -952,5 +961,11 @@ module.exports = (() => {
952
961
  groupNodeToSever.walk(group => delete this._nodes[group.id], false, true);
953
962
  }
954
963
 
964
+ function recalculatePercentages() {
965
+ Object.keys(this._trees).forEach((key) => {
966
+ this._trees[key].walk(group => group.refreshMarketPercent(), false, false);
967
+ });
968
+ }
969
+
955
970
  return PositionContainer;
956
971
  })();
@@ -33,13 +33,17 @@ module.exports = (() => {
33
33
  * @param {Boolean=} aggregateCash
34
34
  */
35
35
  class PositionGroup {
36
- constructor(container, definition, items, currency, key, description, aggregateCash) {
36
+ constructor(definition, items, rates, currency, key, description, aggregateCash) {
37
37
  this._id = counter++;
38
38
 
39
39
  this._definition = definition;
40
- this._container = container;
41
40
 
42
41
  this._items = items;
42
+ this._rates = rates;
43
+
44
+ this._parentGroup = null;
45
+ this._portfolioGroup = null;
46
+
43
47
  this._currency = currency || Currency.CAD;
44
48
  this._bypassCurrencyTranslation = false;
45
49
 
@@ -53,7 +57,6 @@ module.exports = (() => {
53
57
  this._suspended = false;
54
58
  this._showClosedPositions = false;
55
59
 
56
- this._marketPercentChangeEvent = new Event(this);
57
60
  this._groupExcludedChangeEvent = new Event(this);
58
61
  this._showClosedPositionsChangeEvent = new Event(this);
59
62
 
@@ -266,6 +269,40 @@ module.exports = (() => {
266
269
  return this._excluded;
267
270
  }
268
271
 
272
+ /**
273
+ * Sets the immediate parent group (allowing for calculation of relative
274
+ * percentages).
275
+ *
276
+ * @public
277
+ * @param {PortfolioGroup} group
278
+ */
279
+ setParentGroup(group) {
280
+ assert.argumentIsOptional(group, 'group', PositionGroup, 'PositionGroup');
281
+
282
+ if (this._parentGroup !== null) {
283
+ throw new Error('The parent group has already been set.');
284
+ }
285
+
286
+ this._parentGroup = group;
287
+ }
288
+
289
+ /**
290
+ * Sets the nearest parent group for a portfolio (allowing for calculation
291
+ * of relative percentages).
292
+ *
293
+ * @public
294
+ * @param {PortfolioGroup} group
295
+ */
296
+ setPortfolioGroup(group) {
297
+ assert.argumentIsOptional(group, 'group', PositionGroup, 'PositionGroup');
298
+
299
+ if (this._portfolioGroup !== null) {
300
+ throw new Error('The portfolio group has already been set.');
301
+ }
302
+
303
+ this._portfolioGroup = group;
304
+ }
305
+
269
306
  /**
270
307
  * Adds a new {@link PositionItem} to the group.
271
308
  *
@@ -311,9 +348,11 @@ module.exports = (() => {
311
348
  * Causes aggregated data to be recalculated using a new exchange rate.
312
349
  *
313
350
  * @public
314
- * @param {Rate} rate
351
+ * @param {Array.<Rate>} rate
315
352
  */
316
- setForexRate(rate) {
353
+ setForexRates(rates) {
354
+ this._rates = rates;
355
+
317
356
  if (!this._bypassCurrencyTranslation) {
318
357
  this.refresh();
319
358
  }
@@ -401,10 +440,8 @@ module.exports = (() => {
401
440
  return;
402
441
  }
403
442
 
404
- const rates = this._container.getForexQuotes();
405
-
406
- calculateStaticData(this, rates);
407
- calculatePriceData(this, rates, null, true);
443
+ calculateStaticData(this, this._rates);
444
+ calculatePriceData(this, this._rates, null, true);
408
445
  }
409
446
 
410
447
  /**
@@ -414,7 +451,7 @@ module.exports = (() => {
414
451
  * @public
415
452
  */
416
453
  refreshMarketPercent() {
417
- calculateMarketPercent(this, this._container.getForexQuotes(), true);
454
+ calculateMarketPercent(this, this._rates, this._parentGroup, this._portfolioGroup);
418
455
  }
419
456
 
420
457
  /**
@@ -427,17 +464,6 @@ module.exports = (() => {
427
464
  return this._items.length === 0;
428
465
  }
429
466
 
430
- /**
431
- * Adds an observer for change in the market percentage of the group.
432
- *
433
- * @public
434
- * @param {Function} handler
435
- * @return {Disposable}
436
- */
437
- registerMarketPercentChangeHandler(handler) {
438
- return this._marketPercentChangeEvent.register(handler);
439
- }
440
-
441
467
  /**
442
468
  * Adds an observer for changes to the exclusion of the group
443
469
  * from higher level aggregations.
@@ -492,10 +518,10 @@ module.exports = (() => {
492
518
  this._dataFormat.currentPrice = null;
493
519
  }
494
520
 
495
- calculatePriceData(this, this._container.getForexQuotes(), sender, false);
521
+ calculatePriceData(this, this._rates, sender, false);
496
522
  });
497
523
 
498
- let fundamentalBinding = item.registerFundamentalDataChangeHandler((data, sender) => {
524
+ let fundamentalBinding = item.registerFundamentalDataChangeHandler((data) => {
499
525
  if (this._single) {
500
526
  this._dataFormat.fundamental = data;
501
527
  } else {
@@ -546,13 +572,13 @@ module.exports = (() => {
546
572
  let newsBinding = Disposable.getEmpty();
547
573
 
548
574
  if (this._single) {
549
- newsBinding = item.registerNewsExistsChangeHandler((exists, sender) => {
575
+ newsBinding = item.registerNewsExistsChangeHandler((exists) => {
550
576
  this._dataActual.newsExists = exists;
551
577
  this._dataFormat.newsExists = exists;
552
578
  });
553
579
  }
554
580
 
555
- this._disposeStack.push(item.registerPortfolioChangeHandler((portfolio, sender) => {
581
+ this._disposeStack.push(item.registerPortfolioChangeHandler((portfolio) => {
556
582
  const descriptionSelector = this._definition.descriptionSelector;
557
583
 
558
584
  this._description = descriptionSelector(this._items[0]);
@@ -801,12 +827,11 @@ module.exports = (() => {
801
827
 
802
828
  format.total = formatCurrency(actual.total, currency);
803
829
  format.totalNegative = actual.total.getIsNegative();
804
-
805
- calculateMarketPercent(group, rates, false);
830
+
806
831
  calculateUnrealizedPercent(group);
807
832
  }
808
833
 
809
- function calculateMarketPercent(group, rates, silent) {
834
+ function calculateMarketPercent(group, rates, parentGroup, portfolioGroup) {
810
835
  if (group.suspended) {
811
836
  return;
812
837
  }
@@ -815,12 +840,10 @@ module.exports = (() => {
815
840
  const format = group._dataFormat;
816
841
  const excluded = group._excluded;
817
842
 
818
- const portfolioParent = group._container.getParentGroupForPortfolio(group);
819
-
820
843
  const calculatePercent = (parent) => {
821
844
  let marketPercent;
822
845
 
823
- if (parent !== null && !excluded) {
846
+ if (parent && !excluded) {
824
847
  const parentData = parent._dataActual;
825
848
 
826
849
  if (parentData.marketAbsolute !== null && !parentData.marketAbsolute.getIsZero()) {
@@ -843,14 +866,15 @@ module.exports = (() => {
843
866
  return marketPercent;
844
867
  };
845
868
 
846
- actual.marketPercent = calculatePercent(group._container.getParentGroup(group));
869
+ actual.marketPercent = calculatePercent(parentGroup);
847
870
  format.marketPercent = formatPercent(actual.marketPercent, 2);
848
871
 
849
- actual.marketPercentPortfolio = calculatePercent(group._container.getParentGroupForPortfolio(group));
850
- format.marketPercentPortfolio = formatPercent(actual.marketPercentPortfolio, 2);
851
-
852
- if (!silent) {
853
- group._marketPercentChangeEvent.fire(group);
872
+ if (parentGroup === portfolioGroup) {
873
+ actual.marketPercentPortfolio = actual.marketPercent;
874
+ format.marketPercentPortfolio = format.marketPercent;
875
+ } else {
876
+ actual.marketPercentPortfolio = calculatePercent(portfolioGroup);
877
+ format.marketPercentPortfolio = formatPercent(actual.marketPercentPortfolio, 2);
854
878
  }
855
879
  }
856
880
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barchart/portfolio-api-common",
3
- "version": "1.0.263",
3
+ "version": "1.0.267",
4
4
  "description": "Common classes used by the Portfolio system",
5
5
  "author": {
6
6
  "name": "Bryan Ingle",