@barchart/portfolio-api-common 1.32.0 → 2.0.0

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.
@@ -3,6 +3,7 @@ const array = require('@barchart/common-js/lang/array'),
3
3
  ComparatorBuilder = require('@barchart/common-js/collections/sorting/ComparatorBuilder'),
4
4
  comparators = require('@barchart/common-js/collections/sorting/comparators'),
5
5
  Currency = require('@barchart/common-js/lang/Currency'),
6
+ CurrencyTranslator = require('@barchart/common-js/lang/CurrencyTranslator'),
6
7
  Day = require('@barchart/common-js/lang/Day'),
7
8
  Decimal = require('@barchart/common-js/lang/Decimal'),
8
9
  DisposableStack = require('@barchart/common-js/collections/specialized/DisposableStack'),
@@ -23,11 +24,14 @@ const PositionGroup = require('./PositionGroup'),
23
24
  module.exports = (() => {
24
25
  'use strict';
25
26
 
26
- const DEFAULT_CURRENCY = Currency.CAD;
27
+ const DEFAULT_CURRENCY = Currency.USD;
27
28
 
28
- const REQUIRED_CURRENCIES = [
29
+ const SUPPORTED_CURRENCIES = [
30
+ Currency.AUD,
29
31
  Currency.CAD,
30
32
  Currency.CHF,
33
+ Currency.GBP,
34
+ Currency.GBX,
31
35
  Currency.EUR,
32
36
  Currency.HKD,
33
37
  Currency.JPY,
@@ -77,6 +81,7 @@ module.exports = (() => {
77
81
  this._groupBindings = { };
78
82
 
79
83
  this._reporting = reportFrame instanceof PositionSummaryFrame;
84
+ this._useBarchartPriceFormattingRules = false;
80
85
 
81
86
  this._positionSymbolAddedEvent = new Event(this);
82
87
  this._positionSymbolRemovedEvent = new Event(this);
@@ -122,7 +127,7 @@ module.exports = (() => {
122
127
  }, { });
123
128
 
124
129
  this._items = positions.reduce((items, position) => {
125
- const item = createPositionItem.call(this, position, reportFrame ? true : false);
130
+ const item = createPositionItem.call(this, position, !!reportFrame);
126
131
 
127
132
  if (item) {
128
133
  items.push(item);
@@ -143,22 +148,6 @@ module.exports = (() => {
143
148
  return map;
144
149
  }, { });
145
150
 
146
- this._currencies = this._items.reduce((map, item) => {
147
- const currency = extractCurrency(item.position);
148
-
149
- if (currency) {
150
- const code = currency.code;
151
-
152
- if (!map.hasOwnProperty(code)) {
153
- map[code] = [ ];
154
- }
155
-
156
- map[code].push(item);
157
- }
158
-
159
- return map;
160
- }, { });
161
-
162
151
  if (is.array(currencyPairs)) {
163
152
  currencyPairs.forEach((currencyPair) => {
164
153
  currencyPair.sort((a, b) => comparators.compareStrings(a.code, b.code));
@@ -168,21 +157,31 @@ module.exports = (() => {
168
157
  return `^${currencyPair[0].code}${currencyPair[1].code}`;
169
158
  }));
170
159
  } else {
171
- const forexCurrencyCodes = array.unique(Object.keys(this._currencies).concat(REQUIRED_CURRENCIES.map(c => c.code)));
172
-
173
- this._forexSymbols = forexCurrencyCodes.reduce((symbols, code) => {
174
- if (code !== DEFAULT_CURRENCY.code) {
175
- symbols.push(`^${code}${DEFAULT_CURRENCY.code}`);
160
+ this._forexSymbols = SUPPORTED_CURRENCIES.reduce((symbols, currency) => {
161
+ if (currency === DEFAULT_CURRENCY || currency === Currency.GBX) {
162
+ return symbols;
176
163
  }
177
164
 
165
+ symbols.push(`^${DEFAULT_CURRENCY.code}${currency.code}`);
166
+
178
167
  return symbols;
179
168
  }, [ ]);
169
+
170
+ this._forexSymbols.push('^GBXGBP');
180
171
  }
181
172
 
182
- this._forexQuotes = this._forexSymbols.map((symbol) => {
173
+ this._currencyTranslator = new CurrencyTranslator(this._forexSymbols);
174
+
175
+ const forexQuotes = this._forexSymbols.map((symbol) => {
176
+ if (symbol === '^GBXGBP') {
177
+ return Rate.fromPair(0.01, '^GBXGBP');
178
+ }
179
+
183
180
  return Rate.fromPair(Decimal.ONE, symbol);
184
181
  });
185
182
 
183
+ this._currencyTranslator.setRates(forexQuotes);
184
+
186
185
  this._nodes = { };
187
186
 
188
187
  this._trees = this._definitions.reduce((map, treeDefinition) => {
@@ -577,18 +576,6 @@ module.exports = (() => {
577
576
  assert.argumentIsArray(positionQuotes, 'positionQuotes');
578
577
  assert.argumentIsArray(forexQuotes, 'forexQuotes');
579
578
 
580
- if (positionQuotes.length !== 0) {
581
- positionQuotes.forEach((quote) => {
582
- const symbol = quote.symbol;
583
-
584
- if (symbol) {
585
- if (this._symbols.hasOwnProperty(symbol)) {
586
- this._symbols[ symbol ].forEach(item => item.setQuote(quote));
587
- }
588
- }
589
- });
590
- }
591
-
592
579
  if (forexQuotes.length !== 0) {
593
580
  forexQuotes.forEach((quote) => {
594
581
  const symbol = quote.symbol;
@@ -596,27 +583,25 @@ module.exports = (() => {
596
583
  if (symbol) {
597
584
  const rate = Rate.fromPair(quote.lastPrice, symbol);
598
585
 
599
- let index = this._forexQuotes.findIndex(existing => existing.formatPair() === rate.formatPair());
586
+ this._currencyTranslator.setRate(rate);
587
+ }
588
+ });
600
589
 
601
- if (index < 0) {
602
- const inverted = rate.invert();
590
+ Object.keys(this._trees).forEach((key) => {
591
+ this._trees[key].walk(group => group.refreshTranslations(), true, false);
592
+ });
593
+ }
603
594
 
604
- index = this._forexQuotes.findIndex(existing => existing.formatPair() === inverted.formatPair());
605
- }
595
+ if (positionQuotes.length !== 0) {
596
+ positionQuotes.forEach((quote) => {
597
+ const symbol = quote.symbol;
606
598
 
607
- if (index < 0) {
608
- this._forexQuotes.push(rate);
609
- } else {
610
- this._forexQuotes[index] = rate;
599
+ if (symbol) {
600
+ if (this._symbols.hasOwnProperty(symbol)) {
601
+ this._symbols[ symbol ].forEach(item => item.setQuote(quote));
611
602
  }
612
-
613
- recalculatePercentages.call(this);
614
603
  }
615
604
  });
616
-
617
- Object.keys(this._trees).forEach((key) => {
618
- this._trees[key].walk(group => group.setForexRates(this._forexQuotes), true, false);
619
- });
620
605
  }
621
606
 
622
607
  if (positionQuotes.length !== 0 || forexQuotes.length !== 0) {
@@ -645,18 +630,6 @@ module.exports = (() => {
645
630
  return price;
646
631
  }
647
632
 
648
- /**
649
- * Sets a historical forex quote.
650
- *
651
- * @public
652
- * @param {Object} forexQuote
653
- * @param {Day} date
654
- */
655
- setHistoricalForexQuote(forexQuote, date) {
656
- assert.argumentIsRequired(forexQuote, 'forexQuote', Object);
657
- assert.argumentIsRequired(date, 'date', Day, 'Day');
658
- }
659
-
660
633
  /**
661
634
  * Returns all forex symbols that are required to do currency translations.
662
635
  *
@@ -667,16 +640,6 @@ module.exports = (() => {
667
640
  return this._forexSymbols;
668
641
  }
669
642
 
670
- /**
671
- * Returns all current forex quotes.
672
- *
673
- * @public
674
- * @returns {Object[]}
675
- */
676
- getForexQuotes() {
677
- return this._forexQuotes;
678
- }
679
-
680
643
  /**
681
644
  * Updates fundamental data for a single symbol.
682
645
  *
@@ -851,6 +814,24 @@ module.exports = (() => {
851
814
  return this._positionSymbolRemovedEvent.register(handler);
852
815
  }
853
816
 
817
+ /**
818
+ * Changes rules for price formatting.
819
+ *
820
+ * @public
821
+ * @param {boolean} value
822
+ */
823
+ setBarchartPriceFormattingRules(value) {
824
+ assert.argumentIsRequired(value, 'value', Boolean);
825
+
826
+ if (this._useBarchartPriceFormattingRules !== value) {
827
+ this._useBarchartPriceFormattingRules = value;
828
+
829
+ Object.keys(this._trees).forEach((key) => {
830
+ this._trees[key].walk(group => group.setBarchartPriceFormattingRules(this._useBarchartPriceFormattingRules));
831
+ });
832
+ }
833
+ }
834
+
854
835
  toString() {
855
836
  return '[PositionContainer]';
856
837
  }
@@ -892,14 +873,6 @@ module.exports = (() => {
892
873
  }
893
874
  }
894
875
 
895
- function extractCurrency(position) {
896
- if (position.instrument && position.instrument.currency) {
897
- return position.instrument.currency;
898
- } else {
899
- return null;
900
- }
901
- }
902
-
903
876
  function addGroupBinding(group, dispoable) {
904
877
  const id = group.id;
905
878
 
@@ -963,7 +936,7 @@ module.exports = (() => {
963
936
  return;
964
937
  }
965
938
 
966
- const rates = this._forexQuotes;
939
+ const currencyTranslator = this._currencyTranslator;
967
940
 
968
941
  const levelDefinition = levelDefinitions[0];
969
942
 
@@ -972,7 +945,11 @@ module.exports = (() => {
972
945
  const items = populatedObjects[key];
973
946
  const first = items[0];
974
947
 
975
- list.push(new PositionGroup(levelDefinition, items, rates, levelDefinition.currencySelector(first), key, levelDefinition.descriptionSelector(first), levelDefinition.aggregateCash));
948
+ const group = new PositionGroup(levelDefinition, items, levelDefinition.currencySelector(first), currencyTranslator, key, levelDefinition.descriptionSelector(first), levelDefinition.aggregateCash);
949
+
950
+ group.setBarchartPriceFormattingRules(this._useBarchartPriceFormattingRules);
951
+
952
+ list.push(group);
976
953
 
977
954
  return list;
978
955
  }, [ ]);
@@ -985,7 +962,11 @@ module.exports = (() => {
985
962
  });
986
963
 
987
964
  const empty = missingGroups.map((group) => {
988
- return new PositionGroup(levelDefinition, [ ], rates, group.currency, group.key, group.description);
965
+ const eg = new PositionGroup(levelDefinition, [ ], group.currency, currencyTranslator, group.key, group.description);
966
+
967
+ eg.setBarchartPriceFormattingRules(this._useBarchartPriceFormattingRules);
968
+
969
+ return eg;
989
970
  });
990
971
 
991
972
  const compositeGroups = populatedGroups.concat(empty);
@@ -1149,12 +1130,6 @@ module.exports = (() => {
1149
1130
  array.remove(this._symbolsDisplay[displaySymbol], i => i === positionItem);
1150
1131
  }
1151
1132
 
1152
- const currency = extractCurrency(positionItem.position);
1153
-
1154
- if (currency && this._currencies.hasOwnProperty(currency.code)) {
1155
- array.remove(this._currencies[currency.code], i => i === positionItem);
1156
- }
1157
-
1158
1133
  Object.keys(this._trees).forEach((key) => {
1159
1134
  this._trees[key].walk((group, groupNode) => {
1160
1135
  if (group.definition.type === PositionLevelType.POSITION && group.key === positionItem.position.position) {
@@ -1,13 +1,13 @@
1
1
  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
+ CurrencyTranslator = require('@barchart/common-js/lang/CurrencyTranslator'),
4
5
  Decimal = require('@barchart/common-js/lang/Decimal'),
5
6
  Disposable = require('@barchart/common-js/lang/Disposable'),
6
7
  DisposableStack = require('@barchart/common-js/collections/specialized/DisposableStack'),
7
8
  Event = require('@barchart/common-js/messaging/Event'),
8
9
  formatter = require('@barchart/common-js/lang/formatter'),
9
- is = require('@barchart/common-js/lang/is'),
10
- Rate = require('@barchart/common-js/lang/Rate');
10
+ is = require('@barchart/common-js/lang/is');
11
11
 
12
12
  const fractionFormatter = require('@barchart/marketdata-api-js/lib/utilities/format/fraction');
13
13
 
@@ -28,27 +28,29 @@ module.exports = (() => {
28
28
  * @public
29
29
  * @param {PositionLevelDefinition} definition
30
30
  * @param {PositionItem[]} items
31
- * @param {Rate[]} rates
32
31
  * @param {Currency} currency
32
+ * @param {CurrencyTranslator} currencyTranslator
33
33
  * @param {String} key
34
34
  * @param {String} description
35
35
  * @param {Boolean=} aggregateCash
36
36
  */
37
37
  class PositionGroup {
38
- constructor(definition, items, rates, currency, key, description, aggregateCash) {
38
+ constructor(definition, items, currency, currencyTranslator, key, description, aggregateCash) {
39
39
  this._id = counter++;
40
40
 
41
41
  this._definition = definition;
42
42
 
43
43
  this._items = items;
44
- this._rates = rates;
45
44
 
46
45
  this._parentGroup = null;
47
46
  this._portfolioGroup = null;
48
47
 
49
48
  this._currency = currency || Currency.CAD;
49
+ this._currencyTranslator = currencyTranslator;
50
50
  this._bypassCurrencyTranslation = false;
51
51
 
52
+ this._useBarchartPriceFormattingRules = false;
53
+
52
54
  this._key = key;
53
55
  this._description = description;
54
56
 
@@ -404,20 +406,6 @@ module.exports = (() => {
404
406
  this.refresh();
405
407
  }
406
408
 
407
- /**
408
- * Causes aggregated data to be recalculated using a new exchange rate.
409
- *
410
- * @public
411
- * @param {Rate[]} rates
412
- */
413
- setForexRates(rates) {
414
- this._rates = rates;
415
-
416
- if (!this._bypassCurrencyTranslation) {
417
- this.refresh();
418
- }
419
- }
420
-
421
409
  /**
422
410
  * Set a flag to indicate if parent groups should exclude this group's
423
411
  * items from their calculations.
@@ -477,8 +465,8 @@ module.exports = (() => {
477
465
  * @public
478
466
  */
479
467
  refresh() {
480
- calculateStaticData(this, this._rates, this._definition);
481
- calculatePriceData(this, this._rates, null, true);
468
+ calculateStaticData(this, this._definition);
469
+ calculatePriceData(this,null, true);
482
470
  }
483
471
 
484
472
  /**
@@ -488,7 +476,20 @@ module.exports = (() => {
488
476
  * @public
489
477
  */
490
478
  refreshMarketPercent() {
491
- calculateMarketPercent(this, this._rates, this._parentGroup, this._portfolioGroup);
479
+ calculateMarketPercent(this, this._parentGroup, this._portfolioGroup);
480
+ }
481
+
482
+ /**
483
+ * Causes aggregated data to be recalculated using a new exchange rate.
484
+ *
485
+ * @public
486
+ */
487
+ refreshTranslations() {
488
+ if (this._bypassCurrencyTranslation) {
489
+ return;
490
+ }
491
+
492
+ this.refresh();
492
493
  }
493
494
 
494
495
  /**
@@ -513,6 +514,37 @@ module.exports = (() => {
513
514
  return this._groupExcludedChangeEvent.register(handler);
514
515
  }
515
516
 
517
+ /**
518
+ * Changes rules for price formatting.
519
+ *
520
+ * @public
521
+ * @param {boolean} value
522
+ */
523
+ setBarchartPriceFormattingRules(value) {
524
+ assert.argumentIsRequired(value, 'value', Boolean);
525
+
526
+ if (this._useBarchartPriceFormattingRules !== value) {
527
+ this._useBarchartPriceFormattingRules = value;
528
+
529
+ if (this._single && this._dataActual.currentPrice) {
530
+ const item = this._items[0];
531
+
532
+ const instrument = item.position.instrument;
533
+ const currency = instrument.currency;
534
+
535
+ this._dataFormat.currentPrice = formatFraction(this._dataActual.currentPrice, currency, instrument, this._useBarchartPriceFormattingRules);
536
+
537
+ this._dataFormat.quoteLast = formatFraction(this._dataActual.quoteLast, currency, instrument, this._useBarchartPriceFormattingRules);
538
+ this._dataFormat.quoteOpen = formatFraction(this._dataActual.quoteOpen, currency, instrument, this._useBarchartPriceFormattingRules);
539
+ this._dataFormat.quoteHigh = formatFraction(this._dataActual.quoteHigh, currency, instrument, this._useBarchartPriceFormattingRules);
540
+ this._dataFormat.quoteLow = formatFraction(this._dataActual.quoteLow, currency, instrument, this._useBarchartPriceFormattingRules);
541
+
542
+ this._dataFormat.quoteChange = formatFraction(this._dataActual.quoteChange, currency, instrument, this._useBarchartPriceFormattingRules);
543
+
544
+ }
545
+ }
546
+ }
547
+
516
548
  toString() {
517
549
  return '[PositionGroup]';
518
550
  }
@@ -523,27 +555,28 @@ module.exports = (() => {
523
555
  if (this._single) {
524
556
  const instrument = sender.position.instrument;
525
557
  const currency = instrument.currency;
526
- const precision = currency.precision;
527
558
 
528
- this._dataActual.currentPrice = quote.lastPrice;
529
- this._dataFormat.currentPrice = formatFraction(this._dataActual.currentPrice, currency, instrument);
559
+ this._dataActual.currentPrice = is.number(quote.lastPrice) ? quote.lastPrice : null;
560
+ this._dataActual.quoteLast = is.number(quote.previousPrice) ? quote.previousPrice : null;
561
+ this._dataActual.quoteOpen = is.number(quote.openPrice) ? quote.openPrice : null;
562
+ this._dataActual.quoteHigh = is.number(quote.highPrice) ? quote.highPrice : null;
563
+ this._dataActual.quoteLow = is.number(quote.lowPrice) ? quote.lowPrice : null;
530
564
 
531
- this._dataActual.quoteLast = quote.previousPrice;
532
- this._dataActual.quoteOpen = quote.openPrice;
533
- this._dataActual.quoteHigh = quote.highPrice;
534
- this._dataActual.quoteLow = quote.lowPrice;
535
- this._dataActual.quoteChange = quote.priceChange;
536
- this._dataActual.quoteChangePercent = quote.percentChange;
537
- this._dataActual.quoteTime = quote.timeDisplay;
538
- this._dataActual.quoteVolume = quote.volume;
565
+ this._dataFormat.currentPrice = formatFraction(this._dataActual.currentPrice, currency, instrument, this._useBarchartPriceFormattingRules);
566
+ this._dataFormat.quoteLast = formatFraction(this._dataActual.quoteLast, currency, instrument, this._useBarchartPriceFormattingRules);
567
+ this._dataFormat.quoteOpen = formatFraction(this._dataActual.quoteOpen, currency, instrument, this._useBarchartPriceFormattingRules);
568
+ this._dataFormat.quoteHigh = formatFraction(this._dataActual.quoteHigh, currency, instrument, this._useBarchartPriceFormattingRules);
569
+ this._dataFormat.quoteLow = formatFraction(this._dataActual.quoteLow, currency, instrument, this._useBarchartPriceFormattingRules);
539
570
 
540
- this._dataFormat.quoteLast = formatNumber(this._dataActual.quoteLast , precision);
541
- this._dataFormat.quoteOpen = formatNumber(this._dataActual.quoteOpen, precision);
542
- this._dataFormat.quoteHigh = formatNumber(this._dataActual.quoteHigh, precision);
543
- this._dataFormat.quoteLow = formatNumber(this._dataActual.quoteLow, precision);
544
- this._dataFormat.quoteChange = formatFraction(this._dataActual.quoteChange, currency, instrument);
571
+ this._dataActual.quoteChange = is.number(quote.priceChange) ? quote.priceChange : null;
572
+ this._dataActual.quoteChangePercent = is.number(quote.percentChange) ? quote.percentChange : null;
545
573
 
574
+ this._dataFormat.quoteChange = formatFraction(this._dataActual.quoteChange, currency, instrument, this._useBarchartPriceFormattingRules);
546
575
  this._dataFormat.quoteChangePercent = formatPercent(new Decimal(this._dataActual.quoteChangePercent || 0), 2);
576
+
577
+ this._dataActual.quoteTime = quote.timeDisplay;
578
+ this._dataActual.quoteVolume = is.number(quote.volume) ? quote.volume : null;
579
+
547
580
  this._dataFormat.quoteTime = this._dataActual.quoteTime;
548
581
  this._dataFormat.quoteVolume = formatNumber(this._dataActual.quoteVolume, 0);
549
582
 
@@ -558,7 +591,7 @@ module.exports = (() => {
558
591
  this._dataFormat.currentPrice = null;
559
592
  }
560
593
 
561
- calculatePriceData(this, this._rates, sender, false);
594
+ calculatePriceData(this, sender, false);
562
595
  });
563
596
 
564
597
  let fundamentalBinding = item.registerFundamentalDataChangeHandler((data) => {
@@ -668,9 +701,43 @@ module.exports = (() => {
668
701
  }));
669
702
  }
670
703
 
671
- function formatFraction(value, currency, instrument) {
704
+ function formatNumber(number, precision) {
705
+ if (!is.number(number)) {
706
+ return '—';
707
+ }
708
+
709
+ return formatter.numberToString(number, precision, ',', false);
710
+ }
711
+
712
+ function formatDecimal(decimal, precision) {
713
+ if (decimal === null) {
714
+ return '—';
715
+ }
716
+
717
+ return formatNumber(decimal.toFloat(), precision);
718
+ }
719
+
720
+ function formatPercent(decimal, precision, plus) {
721
+ if (decimal === null) {
722
+ return '—';
723
+ }
724
+
725
+ let prefix;
726
+
727
+ if (is.boolean(plus) && plus && !Decimal.getIsNegative(decimal)) {
728
+ prefix = '+';
729
+ } else {
730
+ prefix = '';
731
+ }
732
+
733
+ return `${prefix}${formatDecimal(decimal.multiply(100), precision)}%`;
734
+ }
735
+
736
+ function formatFraction(value, currency, instrument, useBarchartPriceFormattingRules) {
672
737
  let decimal = value instanceof Decimal;
673
738
 
739
+ let precision = currency.precision;
740
+
674
741
  if (instrument && value !== null) {
675
742
  const type = instrument.type;
676
743
  const code = instrument.code;
@@ -680,101 +747,66 @@ module.exports = (() => {
680
747
 
681
748
  return fractionFormatter(rounded, code.fractionFactor, code.fractionDigits, '-', true);
682
749
  }
750
+
751
+ if (code && useBarchartPriceFormattingRules) {
752
+ precision = code.decimalDigits;
753
+ }
683
754
  }
684
755
 
685
756
  if (decimal) {
686
- return formatDecimal(value, currency.precision);
757
+ return formatDecimal(value, precision);
687
758
  } else {
688
- return formatNumber(value, currency.precision);
759
+ return formatNumber(value, precision);
689
760
  }
690
761
  }
691
762
 
692
- function formatFractionAndTranslate(value, currency, instrument) {
693
- let translatedCurrency;
694
- let translatedValue;
763
+ function formatCurrency(decimal, currency) {
764
+ let translated = decimal;
765
+ let desired = currency;
695
766
 
696
- if (currency === Currency.GBX) {
697
- translatedCurrency = Currency.GBP;
767
+ if (desired === Currency.GBX) {
768
+ desired = Currency.GBP;
698
769
 
699
- if (value instanceof Decimal) {
700
- translatedValue = Rate.convert(value, Currency.GBX, Currency.GBP);
701
- } else if (is.number(value)) {
702
- translatedValue = Rate.convert(new Decimal(value), Currency.GBX, Currency.GBP).toFloat();
703
- } else {
704
- translatedValue = value;
770
+ if (translated !== null) {
771
+ translated = translated.multiply(0.01);
705
772
  }
706
- } else {
707
- translatedCurrency = currency;
708
- translatedValue = value;
709
773
  }
710
774
 
711
- return formatFraction(translatedValue, translatedCurrency, instrument);
775
+ return formatDecimal(translated, desired.precision);
712
776
  }
713
777
 
714
- function formatNumber(number, precision) {
715
- if (is.number(number)) {
716
- return formatter.numberToString(number, precision, ',', false);
717
- } else {
718
- return '—';
719
- }
720
- }
778
+ function formatFractionSpecial(value, currency, instrument) {
779
+ let translated = value;
780
+ let desired = currency;
721
781
 
722
- function formatDecimal(decimal, precision) {
723
- if (decimal !== null) {
724
- return formatNumber(decimal.toFloat(), precision);
725
- } else {
726
- return '—';
727
- }
728
- }
729
-
730
- function formatPercent(decimal, precision, plus) {
731
- if (decimal !== null) {
732
- let prefix;
782
+ if (desired === Currency.GBX) {
783
+ desired = Currency.GBP;
733
784
 
734
- if (is.boolean(plus) && plus && !Decimal.getIsNegative(decimal)) {
735
- prefix = '+';
736
- } else {
737
- prefix = '';
785
+ if (translated !== null) {
786
+ translated = translated.multiply(0.01);
738
787
  }
739
-
740
- return `${prefix}${formatDecimal(decimal.multiply(100), precision)}%`;
741
- } else {
742
- return '—';
743
788
  }
744
- }
745
-
746
- function formatCurrency(decimal, currency) {
747
- let formatted;
748
789
 
749
- if (decimal === null || currency !== Currency.GBX) {
750
- formatted = formatDecimal(decimal, currency.precision);
751
- } else {
752
- formatted = formatDecimal(Rate.convert(decimal, Currency.GBX, Currency.GBP), Currency.GBP.precision);
753
- }
754
-
755
- return formatted;
790
+ return formatFraction(translated, desired, instrument);
756
791
  }
757
792
 
758
- function calculateStaticData(group, rates, definition) {
793
+ function calculateStaticData(group, definition) {
759
794
  const actual = group._dataActual;
760
795
  const format = group._dataFormat;
761
796
 
762
797
  const currency = group.currency;
798
+ const currencyTranslator = group._currencyTranslator;
763
799
 
764
800
  const items = group._consideredItems;
765
801
 
766
802
  group._bypassCurrencyTranslation = items.every(item => item.currency === currency);
767
803
 
768
804
  const translate = (item, value) => {
769
- let translated;
770
-
771
- if (item.currency !== currency && !value.getIsZero()) {
772
- translated = Rate.convert(value, item.currency, currency, ...rates);
773
- } else {
774
- translated = value;
805
+ if (item.currency === currency || value.getIsZero()) {
806
+ return value;
775
807
  }
776
808
 
777
- return translated;
809
+ return currencyTranslator.translate(value, item.currency, currency);
778
810
  };
779
811
 
780
812
  let updates = items.reduce((updates, item) => {
@@ -893,7 +925,7 @@ module.exports = (() => {
893
925
  format.quantityPrevious = formatDecimal(actual.quantityPrevious, 2);
894
926
 
895
927
  actual.basisPrice = item.data.basisPrice;
896
- format.basisPrice = formatFractionAndTranslate(actual.basisPrice, currency, instrument);
928
+ format.basisPrice = formatFractionSpecial(actual.basisPrice, currency, instrument);
897
929
 
898
930
  actual.periodPrice = item.data.periodPrice;
899
931
  actual.periodPricePrevious = item.data.periodPricePrevious;
@@ -902,7 +934,7 @@ module.exports = (() => {
902
934
  format.periodPricePrevious = formatCurrency(actual.periodPricePrevious, currency);
903
935
 
904
936
  actual.unrealizedPrice = item.data.unrealizedPrice;
905
- format.unrealizedPrice = formatFractionAndTranslate(actual.unrealizedPrice, currency, instrument);
937
+ format.unrealizedPrice = formatFractionSpecial(actual.unrealizedPrice, currency, instrument);
906
938
 
907
939
  format.invalid = definition.type === PositionLevelType.POSITION && item.invalid;
908
940
  format.locked = definition.type === PositionLevelType.POSITION && item.data.locked;
@@ -925,7 +957,7 @@ module.exports = (() => {
925
957
  format.portfolioType = portfolioType;
926
958
  }
927
959
 
928
- function calculatePriceData(group, rates, item, forceRefresh) {
960
+ function calculatePriceData(group, item, forceRefresh) {
929
961
  const currency = group.currency;
930
962
 
931
963
  const actual = group._dataActual;
@@ -937,16 +969,14 @@ module.exports = (() => {
937
969
  return;
938
970
  }
939
971
 
940
- const translate = (item, value) => {
941
- let translated;
972
+ const currencyTranslator = group._currencyTranslator;
942
973
 
943
- if (item.currency !== currency && !value.getIsZero()) {
944
- translated = Rate.convert(value, item.currency, currency, ...rates);
945
- } else {
946
- translated = value;
974
+ const translate = (item, value) => {
975
+ if (item.currency === currency || value.getIsZero()) {
976
+ return value;
947
977
  }
948
978
 
949
- return translated;
979
+ return currencyTranslator.translate(value, item.currency, currency);
950
980
  };
951
981
 
952
982
  let updates;
@@ -1062,39 +1092,37 @@ module.exports = (() => {
1062
1092
 
1063
1093
  if (group.single && item) {
1064
1094
  actual.unrealizedPrice = item.data.unrealizedPrice;
1065
- format.unrealizedPrice = formatFractionAndTranslate(actual.unrealizedPrice, currency, item.position.instrument);
1095
+ format.unrealizedPrice = formatFractionSpecial(actual.unrealizedPrice, currency, item.position.instrument);
1066
1096
  }
1067
1097
  }
1068
1098
 
1069
- function calculateMarketPercent(group, rates, parentGroup, portfolioGroup) {
1099
+ function calculateMarketPercent(group, parentGroup, portfolioGroup) {
1070
1100
  const actual = group._dataActual;
1071
1101
  const format = group._dataFormat;
1072
1102
  const excluded = group._excluded;
1073
1103
 
1104
+ const currencyTranslator = group._currencyTranslator;
1105
+
1074
1106
  const calculatePercent = (parent) => {
1075
- let marketPercent;
1107
+ if (!parent || excluded) {
1108
+ return null;
1109
+ }
1076
1110
 
1077
- if (parent && !excluded) {
1078
- const parentData = parent._dataActual;
1111
+ const parentData = parent._dataActual;
1079
1112
 
1080
- if (parentData.marketAbsolute !== null && !parentData.marketAbsolute.getIsApproximate(Decimal.ZERO, 4)) {
1081
- let numerator;
1113
+ if (parentData.marketAbsolute === null || parentData.marketAbsolute.getIsApproximate(Decimal.ZERO, 4)) {
1114
+ return null;
1115
+ }
1082
1116
 
1083
- if (group.currency !== parent.currency) {
1084
- numerator = Rate.convert(actual.marketAbsolute, group.currency, parent.currency, ...rates);
1085
- } else {
1086
- numerator = actual.marketAbsolute;
1087
- }
1117
+ let numerator;
1088
1118
 
1089
- marketPercent = numerator.divide(parentData.marketAbsolute);
1090
- } else {
1091
- marketPercent = null;
1092
- }
1119
+ if (group.currency === parent.currency) {
1120
+ numerator = actual.marketAbsolute;
1093
1121
  } else {
1094
- marketPercent = null;
1122
+ numerator = currencyTranslator.translate(actual.marketAbsolute, group.currency, parent.currency);
1095
1123
  }
1096
1124
 
1097
- return marketPercent;
1125
+ return numerator.divide(parentData.marketAbsolute);
1098
1126
  };
1099
1127
 
1100
1128
  actual.marketPercent = calculatePercent(parentGroup);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barchart/portfolio-api-common",
3
- "version": "1.32.0",
3
+ "version": "2.0.0",
4
4
  "description": "Common JavaScript code used by Barchart's Portfolio Service",
5
5
  "author": {
6
6
  "name": "Bryan Ingle",
@@ -15,9 +15,9 @@
15
15
  "url": "git+ssh://git@github.com/barchart/portfolio-api-common.git"
16
16
  },
17
17
  "dependencies": {
18
- "@barchart/common-js": "^4.43.0",
18
+ "@barchart/common-js": "^4.46.1",
19
19
  "@barchart/marketdata-api-js": "^6.2.1",
20
- "uuid": "^8.3.2"
20
+ "uuid": "9.0.1"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@babel/core": "^7.6.2",