@barchart/portfolio-api-common 1.32.0 → 2.1.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,14 +24,19 @@ const PositionGroup = require('./PositionGroup'),
|
|
|
23
24
|
module.exports = (() => {
|
|
24
25
|
'use strict';
|
|
25
26
|
|
|
26
|
-
const DEFAULT_CURRENCY = Currency.
|
|
27
|
+
const DEFAULT_CURRENCY = Currency.USD;
|
|
27
28
|
|
|
28
|
-
const
|
|
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,
|
|
38
|
+
Currency.NOK,
|
|
39
|
+
Currency.SEK,
|
|
34
40
|
Currency.USD
|
|
35
41
|
];
|
|
36
42
|
|
|
@@ -77,6 +83,7 @@ module.exports = (() => {
|
|
|
77
83
|
this._groupBindings = { };
|
|
78
84
|
|
|
79
85
|
this._reporting = reportFrame instanceof PositionSummaryFrame;
|
|
86
|
+
this._useBarchartPriceFormattingRules = false;
|
|
80
87
|
|
|
81
88
|
this._positionSymbolAddedEvent = new Event(this);
|
|
82
89
|
this._positionSymbolRemovedEvent = new Event(this);
|
|
@@ -122,7 +129,7 @@ module.exports = (() => {
|
|
|
122
129
|
}, { });
|
|
123
130
|
|
|
124
131
|
this._items = positions.reduce((items, position) => {
|
|
125
|
-
const item = createPositionItem.call(this, position, reportFrame
|
|
132
|
+
const item = createPositionItem.call(this, position, !!reportFrame);
|
|
126
133
|
|
|
127
134
|
if (item) {
|
|
128
135
|
items.push(item);
|
|
@@ -143,22 +150,6 @@ module.exports = (() => {
|
|
|
143
150
|
return map;
|
|
144
151
|
}, { });
|
|
145
152
|
|
|
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
153
|
if (is.array(currencyPairs)) {
|
|
163
154
|
currencyPairs.forEach((currencyPair) => {
|
|
164
155
|
currencyPair.sort((a, b) => comparators.compareStrings(a.code, b.code));
|
|
@@ -168,21 +159,31 @@ module.exports = (() => {
|
|
|
168
159
|
return `^${currencyPair[0].code}${currencyPair[1].code}`;
|
|
169
160
|
}));
|
|
170
161
|
} else {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (code !== DEFAULT_CURRENCY.code) {
|
|
175
|
-
symbols.push(`^${code}${DEFAULT_CURRENCY.code}`);
|
|
162
|
+
this._forexSymbols = SUPPORTED_CURRENCIES.reduce((symbols, currency) => {
|
|
163
|
+
if (currency === DEFAULT_CURRENCY || currency === Currency.GBX) {
|
|
164
|
+
return symbols;
|
|
176
165
|
}
|
|
177
166
|
|
|
167
|
+
symbols.push(`^${DEFAULT_CURRENCY.code}${currency.code}`);
|
|
168
|
+
|
|
178
169
|
return symbols;
|
|
179
170
|
}, [ ]);
|
|
171
|
+
|
|
172
|
+
this._forexSymbols.push('^GBXGBP');
|
|
180
173
|
}
|
|
181
174
|
|
|
182
|
-
this.
|
|
175
|
+
this._currencyTranslator = new CurrencyTranslator(this._forexSymbols);
|
|
176
|
+
|
|
177
|
+
const forexQuotes = this._forexSymbols.map((symbol) => {
|
|
178
|
+
if (symbol === '^GBXGBP') {
|
|
179
|
+
return Rate.fromPair(0.01, '^GBXGBP');
|
|
180
|
+
}
|
|
181
|
+
|
|
183
182
|
return Rate.fromPair(Decimal.ONE, symbol);
|
|
184
183
|
});
|
|
185
184
|
|
|
185
|
+
this._currencyTranslator.setRates(forexQuotes);
|
|
186
|
+
|
|
186
187
|
this._nodes = { };
|
|
187
188
|
|
|
188
189
|
this._trees = this._definitions.reduce((map, treeDefinition) => {
|
|
@@ -577,18 +578,6 @@ module.exports = (() => {
|
|
|
577
578
|
assert.argumentIsArray(positionQuotes, 'positionQuotes');
|
|
578
579
|
assert.argumentIsArray(forexQuotes, 'forexQuotes');
|
|
579
580
|
|
|
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
581
|
if (forexQuotes.length !== 0) {
|
|
593
582
|
forexQuotes.forEach((quote) => {
|
|
594
583
|
const symbol = quote.symbol;
|
|
@@ -596,27 +585,25 @@ module.exports = (() => {
|
|
|
596
585
|
if (symbol) {
|
|
597
586
|
const rate = Rate.fromPair(quote.lastPrice, symbol);
|
|
598
587
|
|
|
599
|
-
|
|
588
|
+
this._currencyTranslator.setRate(rate);
|
|
589
|
+
}
|
|
590
|
+
});
|
|
600
591
|
|
|
601
|
-
|
|
602
|
-
|
|
592
|
+
Object.keys(this._trees).forEach((key) => {
|
|
593
|
+
this._trees[key].walk(group => group.refreshTranslations(), true, false);
|
|
594
|
+
});
|
|
595
|
+
}
|
|
603
596
|
|
|
604
|
-
|
|
605
|
-
|
|
597
|
+
if (positionQuotes.length !== 0) {
|
|
598
|
+
positionQuotes.forEach((quote) => {
|
|
599
|
+
const symbol = quote.symbol;
|
|
606
600
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
this._forexQuotes[index] = rate;
|
|
601
|
+
if (symbol) {
|
|
602
|
+
if (this._symbols.hasOwnProperty(symbol)) {
|
|
603
|
+
this._symbols[ symbol ].forEach(item => item.setQuote(quote));
|
|
611
604
|
}
|
|
612
|
-
|
|
613
|
-
recalculatePercentages.call(this);
|
|
614
605
|
}
|
|
615
606
|
});
|
|
616
|
-
|
|
617
|
-
Object.keys(this._trees).forEach((key) => {
|
|
618
|
-
this._trees[key].walk(group => group.setForexRates(this._forexQuotes), true, false);
|
|
619
|
-
});
|
|
620
607
|
}
|
|
621
608
|
|
|
622
609
|
if (positionQuotes.length !== 0 || forexQuotes.length !== 0) {
|
|
@@ -645,18 +632,6 @@ module.exports = (() => {
|
|
|
645
632
|
return price;
|
|
646
633
|
}
|
|
647
634
|
|
|
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
635
|
/**
|
|
661
636
|
* Returns all forex symbols that are required to do currency translations.
|
|
662
637
|
*
|
|
@@ -667,16 +642,6 @@ module.exports = (() => {
|
|
|
667
642
|
return this._forexSymbols;
|
|
668
643
|
}
|
|
669
644
|
|
|
670
|
-
/**
|
|
671
|
-
* Returns all current forex quotes.
|
|
672
|
-
*
|
|
673
|
-
* @public
|
|
674
|
-
* @returns {Object[]}
|
|
675
|
-
*/
|
|
676
|
-
getForexQuotes() {
|
|
677
|
-
return this._forexQuotes;
|
|
678
|
-
}
|
|
679
|
-
|
|
680
645
|
/**
|
|
681
646
|
* Updates fundamental data for a single symbol.
|
|
682
647
|
*
|
|
@@ -851,6 +816,24 @@ module.exports = (() => {
|
|
|
851
816
|
return this._positionSymbolRemovedEvent.register(handler);
|
|
852
817
|
}
|
|
853
818
|
|
|
819
|
+
/**
|
|
820
|
+
* Changes rules for price formatting.
|
|
821
|
+
*
|
|
822
|
+
* @public
|
|
823
|
+
* @param {boolean} value
|
|
824
|
+
*/
|
|
825
|
+
setBarchartPriceFormattingRules(value) {
|
|
826
|
+
assert.argumentIsRequired(value, 'value', Boolean);
|
|
827
|
+
|
|
828
|
+
if (this._useBarchartPriceFormattingRules !== value) {
|
|
829
|
+
this._useBarchartPriceFormattingRules = value;
|
|
830
|
+
|
|
831
|
+
Object.keys(this._trees).forEach((key) => {
|
|
832
|
+
this._trees[key].walk(group => group.setBarchartPriceFormattingRules(this._useBarchartPriceFormattingRules));
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
854
837
|
toString() {
|
|
855
838
|
return '[PositionContainer]';
|
|
856
839
|
}
|
|
@@ -892,14 +875,6 @@ module.exports = (() => {
|
|
|
892
875
|
}
|
|
893
876
|
}
|
|
894
877
|
|
|
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
878
|
function addGroupBinding(group, dispoable) {
|
|
904
879
|
const id = group.id;
|
|
905
880
|
|
|
@@ -963,7 +938,7 @@ module.exports = (() => {
|
|
|
963
938
|
return;
|
|
964
939
|
}
|
|
965
940
|
|
|
966
|
-
const
|
|
941
|
+
const currencyTranslator = this._currencyTranslator;
|
|
967
942
|
|
|
968
943
|
const levelDefinition = levelDefinitions[0];
|
|
969
944
|
|
|
@@ -972,7 +947,11 @@ module.exports = (() => {
|
|
|
972
947
|
const items = populatedObjects[key];
|
|
973
948
|
const first = items[0];
|
|
974
949
|
|
|
975
|
-
|
|
950
|
+
const group = new PositionGroup(levelDefinition, items, levelDefinition.currencySelector(first), currencyTranslator, key, levelDefinition.descriptionSelector(first), levelDefinition.aggregateCash);
|
|
951
|
+
|
|
952
|
+
group.setBarchartPriceFormattingRules(this._useBarchartPriceFormattingRules);
|
|
953
|
+
|
|
954
|
+
list.push(group);
|
|
976
955
|
|
|
977
956
|
return list;
|
|
978
957
|
}, [ ]);
|
|
@@ -985,7 +964,11 @@ module.exports = (() => {
|
|
|
985
964
|
});
|
|
986
965
|
|
|
987
966
|
const empty = missingGroups.map((group) => {
|
|
988
|
-
|
|
967
|
+
const eg = new PositionGroup(levelDefinition, [ ], group.currency, currencyTranslator, group.key, group.description);
|
|
968
|
+
|
|
969
|
+
eg.setBarchartPriceFormattingRules(this._useBarchartPriceFormattingRules);
|
|
970
|
+
|
|
971
|
+
return eg;
|
|
989
972
|
});
|
|
990
973
|
|
|
991
974
|
const compositeGroups = populatedGroups.concat(empty);
|
|
@@ -1149,12 +1132,6 @@ module.exports = (() => {
|
|
|
1149
1132
|
array.remove(this._symbolsDisplay[displaySymbol], i => i === positionItem);
|
|
1150
1133
|
}
|
|
1151
1134
|
|
|
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
1135
|
Object.keys(this._trees).forEach((key) => {
|
|
1159
1136
|
this._trees[key].walk((group, groupNode) => {
|
|
1160
1137
|
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,
|
|
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.
|
|
481
|
-
calculatePriceData(this,
|
|
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.
|
|
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,36 @@ 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
|
+
|
|
516
547
|
toString() {
|
|
517
548
|
return '[PositionGroup]';
|
|
518
549
|
}
|
|
@@ -523,27 +554,28 @@ module.exports = (() => {
|
|
|
523
554
|
if (this._single) {
|
|
524
555
|
const instrument = sender.position.instrument;
|
|
525
556
|
const currency = instrument.currency;
|
|
526
|
-
const precision = currency.precision;
|
|
527
557
|
|
|
528
|
-
this._dataActual.currentPrice = quote.lastPrice;
|
|
529
|
-
this.
|
|
558
|
+
this._dataActual.currentPrice = is.number(quote.lastPrice) ? quote.lastPrice : null;
|
|
559
|
+
this._dataActual.quoteLast = is.number(quote.previousPrice) ? quote.previousPrice : null;
|
|
560
|
+
this._dataActual.quoteOpen = is.number(quote.openPrice) ? quote.openPrice : null;
|
|
561
|
+
this._dataActual.quoteHigh = is.number(quote.highPrice) ? quote.highPrice : null;
|
|
562
|
+
this._dataActual.quoteLow = is.number(quote.lowPrice) ? quote.lowPrice : null;
|
|
530
563
|
|
|
531
|
-
this.
|
|
532
|
-
this.
|
|
533
|
-
this.
|
|
534
|
-
this.
|
|
535
|
-
this.
|
|
536
|
-
this._dataActual.quoteChangePercent = quote.percentChange;
|
|
537
|
-
this._dataActual.quoteTime = quote.timeDisplay;
|
|
538
|
-
this._dataActual.quoteVolume = quote.volume;
|
|
564
|
+
this._dataFormat.currentPrice = formatFraction(this._dataActual.currentPrice, currency, instrument, this._useBarchartPriceFormattingRules);
|
|
565
|
+
this._dataFormat.quoteLast = formatFraction(this._dataActual.quoteLast, currency, instrument, this._useBarchartPriceFormattingRules);
|
|
566
|
+
this._dataFormat.quoteOpen = formatFraction(this._dataActual.quoteOpen, currency, instrument, this._useBarchartPriceFormattingRules);
|
|
567
|
+
this._dataFormat.quoteHigh = formatFraction(this._dataActual.quoteHigh, currency, instrument, this._useBarchartPriceFormattingRules);
|
|
568
|
+
this._dataFormat.quoteLow = formatFraction(this._dataActual.quoteLow, currency, instrument, this._useBarchartPriceFormattingRules);
|
|
539
569
|
|
|
540
|
-
this.
|
|
541
|
-
this.
|
|
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);
|
|
570
|
+
this._dataActual.quoteChange = is.number(quote.priceChange) ? quote.priceChange : null;
|
|
571
|
+
this._dataActual.quoteChangePercent = is.number(quote.percentChange) ? quote.percentChange : null;
|
|
545
572
|
|
|
573
|
+
this._dataFormat.quoteChange = formatFraction(this._dataActual.quoteChange, currency, instrument, this._useBarchartPriceFormattingRules);
|
|
546
574
|
this._dataFormat.quoteChangePercent = formatPercent(new Decimal(this._dataActual.quoteChangePercent || 0), 2);
|
|
575
|
+
|
|
576
|
+
this._dataActual.quoteTime = quote.timeDisplay;
|
|
577
|
+
this._dataActual.quoteVolume = is.number(quote.volume) ? quote.volume : null;
|
|
578
|
+
|
|
547
579
|
this._dataFormat.quoteTime = this._dataActual.quoteTime;
|
|
548
580
|
this._dataFormat.quoteVolume = formatNumber(this._dataActual.quoteVolume, 0);
|
|
549
581
|
|
|
@@ -558,7 +590,7 @@ module.exports = (() => {
|
|
|
558
590
|
this._dataFormat.currentPrice = null;
|
|
559
591
|
}
|
|
560
592
|
|
|
561
|
-
calculatePriceData(this,
|
|
593
|
+
calculatePriceData(this, sender, false);
|
|
562
594
|
});
|
|
563
595
|
|
|
564
596
|
let fundamentalBinding = item.registerFundamentalDataChangeHandler((data) => {
|
|
@@ -668,9 +700,43 @@ module.exports = (() => {
|
|
|
668
700
|
}));
|
|
669
701
|
}
|
|
670
702
|
|
|
671
|
-
function
|
|
703
|
+
function formatNumber(number, precision) {
|
|
704
|
+
if (!is.number(number)) {
|
|
705
|
+
return '—';
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
return formatter.numberToString(number, precision, ',', false);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
function formatDecimal(decimal, precision) {
|
|
712
|
+
if (decimal === null) {
|
|
713
|
+
return '—';
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
return formatNumber(decimal.toFloat(), precision);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
function formatPercent(decimal, precision, plus) {
|
|
720
|
+
if (decimal === null) {
|
|
721
|
+
return '—';
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
let prefix;
|
|
725
|
+
|
|
726
|
+
if (is.boolean(plus) && plus && !Decimal.getIsNegative(decimal)) {
|
|
727
|
+
prefix = '+';
|
|
728
|
+
} else {
|
|
729
|
+
prefix = '';
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
return `${prefix}${formatDecimal(decimal.multiply(100), precision)}%`;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
function formatFraction(value, currency, instrument, useBarchartPriceFormattingRules) {
|
|
672
736
|
let decimal = value instanceof Decimal;
|
|
673
737
|
|
|
738
|
+
let precision = currency.precision;
|
|
739
|
+
|
|
674
740
|
if (instrument && value !== null) {
|
|
675
741
|
const type = instrument.type;
|
|
676
742
|
const code = instrument.code;
|
|
@@ -680,101 +746,66 @@ module.exports = (() => {
|
|
|
680
746
|
|
|
681
747
|
return fractionFormatter(rounded, code.fractionFactor, code.fractionDigits, '-', true);
|
|
682
748
|
}
|
|
749
|
+
|
|
750
|
+
if (code && useBarchartPriceFormattingRules) {
|
|
751
|
+
precision = code.decimalDigits;
|
|
752
|
+
}
|
|
683
753
|
}
|
|
684
754
|
|
|
685
755
|
if (decimal) {
|
|
686
|
-
return formatDecimal(value,
|
|
756
|
+
return formatDecimal(value, precision);
|
|
687
757
|
} else {
|
|
688
|
-
return formatNumber(value,
|
|
758
|
+
return formatNumber(value, precision);
|
|
689
759
|
}
|
|
690
760
|
}
|
|
691
761
|
|
|
692
|
-
function
|
|
693
|
-
let
|
|
694
|
-
let
|
|
762
|
+
function formatCurrency(decimal, currency) {
|
|
763
|
+
let translated = decimal;
|
|
764
|
+
let desired = currency;
|
|
695
765
|
|
|
696
|
-
if (
|
|
697
|
-
|
|
766
|
+
if (desired === Currency.GBX) {
|
|
767
|
+
desired = Currency.GBP;
|
|
698
768
|
|
|
699
|
-
if (
|
|
700
|
-
|
|
701
|
-
} else if (is.number(value)) {
|
|
702
|
-
translatedValue = Rate.convert(new Decimal(value), Currency.GBX, Currency.GBP).toFloat();
|
|
703
|
-
} else {
|
|
704
|
-
translatedValue = value;
|
|
769
|
+
if (translated !== null) {
|
|
770
|
+
translated = translated.multiply(0.01);
|
|
705
771
|
}
|
|
706
|
-
} else {
|
|
707
|
-
translatedCurrency = currency;
|
|
708
|
-
translatedValue = value;
|
|
709
772
|
}
|
|
710
773
|
|
|
711
|
-
return
|
|
774
|
+
return formatDecimal(translated, desired.precision);
|
|
712
775
|
}
|
|
713
776
|
|
|
714
|
-
function
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
} else {
|
|
718
|
-
return '—';
|
|
719
|
-
}
|
|
720
|
-
}
|
|
777
|
+
function formatFractionSpecial(value, currency, instrument) {
|
|
778
|
+
let translated = value;
|
|
779
|
+
let desired = currency;
|
|
721
780
|
|
|
722
|
-
|
|
723
|
-
|
|
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;
|
|
781
|
+
if (desired === Currency.GBX) {
|
|
782
|
+
desired = Currency.GBP;
|
|
733
783
|
|
|
734
|
-
if (
|
|
735
|
-
|
|
736
|
-
} else {
|
|
737
|
-
prefix = '';
|
|
784
|
+
if (translated !== null) {
|
|
785
|
+
translated = translated.multiply(0.01);
|
|
738
786
|
}
|
|
739
|
-
|
|
740
|
-
return `${prefix}${formatDecimal(decimal.multiply(100), precision)}%`;
|
|
741
|
-
} else {
|
|
742
|
-
return '—';
|
|
743
787
|
}
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
function formatCurrency(decimal, currency) {
|
|
747
|
-
let formatted;
|
|
748
788
|
|
|
749
|
-
|
|
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;
|
|
789
|
+
return formatFraction(translated, desired, instrument);
|
|
756
790
|
}
|
|
757
791
|
|
|
758
|
-
function calculateStaticData(group,
|
|
792
|
+
function calculateStaticData(group, definition) {
|
|
759
793
|
const actual = group._dataActual;
|
|
760
794
|
const format = group._dataFormat;
|
|
761
795
|
|
|
762
796
|
const currency = group.currency;
|
|
797
|
+
const currencyTranslator = group._currencyTranslator;
|
|
763
798
|
|
|
764
799
|
const items = group._consideredItems;
|
|
765
800
|
|
|
766
801
|
group._bypassCurrencyTranslation = items.every(item => item.currency === currency);
|
|
767
802
|
|
|
768
803
|
const translate = (item, value) => {
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
if (item.currency !== currency && !value.getIsZero()) {
|
|
772
|
-
translated = Rate.convert(value, item.currency, currency, ...rates);
|
|
773
|
-
} else {
|
|
774
|
-
translated = value;
|
|
804
|
+
if (item.currency === currency || value.getIsZero()) {
|
|
805
|
+
return value;
|
|
775
806
|
}
|
|
776
807
|
|
|
777
|
-
return
|
|
808
|
+
return currencyTranslator.translate(value, item.currency, currency);
|
|
778
809
|
};
|
|
779
810
|
|
|
780
811
|
let updates = items.reduce((updates, item) => {
|
|
@@ -893,7 +924,7 @@ module.exports = (() => {
|
|
|
893
924
|
format.quantityPrevious = formatDecimal(actual.quantityPrevious, 2);
|
|
894
925
|
|
|
895
926
|
actual.basisPrice = item.data.basisPrice;
|
|
896
|
-
format.basisPrice =
|
|
927
|
+
format.basisPrice = formatFractionSpecial(actual.basisPrice, currency, instrument);
|
|
897
928
|
|
|
898
929
|
actual.periodPrice = item.data.periodPrice;
|
|
899
930
|
actual.periodPricePrevious = item.data.periodPricePrevious;
|
|
@@ -902,7 +933,7 @@ module.exports = (() => {
|
|
|
902
933
|
format.periodPricePrevious = formatCurrency(actual.periodPricePrevious, currency);
|
|
903
934
|
|
|
904
935
|
actual.unrealizedPrice = item.data.unrealizedPrice;
|
|
905
|
-
format.unrealizedPrice =
|
|
936
|
+
format.unrealizedPrice = formatFractionSpecial(actual.unrealizedPrice, currency, instrument);
|
|
906
937
|
|
|
907
938
|
format.invalid = definition.type === PositionLevelType.POSITION && item.invalid;
|
|
908
939
|
format.locked = definition.type === PositionLevelType.POSITION && item.data.locked;
|
|
@@ -925,7 +956,7 @@ module.exports = (() => {
|
|
|
925
956
|
format.portfolioType = portfolioType;
|
|
926
957
|
}
|
|
927
958
|
|
|
928
|
-
function calculatePriceData(group,
|
|
959
|
+
function calculatePriceData(group, item, forceRefresh) {
|
|
929
960
|
const currency = group.currency;
|
|
930
961
|
|
|
931
962
|
const actual = group._dataActual;
|
|
@@ -937,16 +968,14 @@ module.exports = (() => {
|
|
|
937
968
|
return;
|
|
938
969
|
}
|
|
939
970
|
|
|
940
|
-
const
|
|
941
|
-
let translated;
|
|
971
|
+
const currencyTranslator = group._currencyTranslator;
|
|
942
972
|
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
translated = value;
|
|
973
|
+
const translate = (item, value) => {
|
|
974
|
+
if (item.currency === currency || value.getIsZero()) {
|
|
975
|
+
return value;
|
|
947
976
|
}
|
|
948
977
|
|
|
949
|
-
return
|
|
978
|
+
return currencyTranslator.translate(value, item.currency, currency);
|
|
950
979
|
};
|
|
951
980
|
|
|
952
981
|
let updates;
|
|
@@ -1062,39 +1091,37 @@ module.exports = (() => {
|
|
|
1062
1091
|
|
|
1063
1092
|
if (group.single && item) {
|
|
1064
1093
|
actual.unrealizedPrice = item.data.unrealizedPrice;
|
|
1065
|
-
format.unrealizedPrice =
|
|
1094
|
+
format.unrealizedPrice = formatFractionSpecial(actual.unrealizedPrice, currency, item.position.instrument);
|
|
1066
1095
|
}
|
|
1067
1096
|
}
|
|
1068
1097
|
|
|
1069
|
-
function calculateMarketPercent(group,
|
|
1098
|
+
function calculateMarketPercent(group, parentGroup, portfolioGroup) {
|
|
1070
1099
|
const actual = group._dataActual;
|
|
1071
1100
|
const format = group._dataFormat;
|
|
1072
1101
|
const excluded = group._excluded;
|
|
1073
1102
|
|
|
1103
|
+
const currencyTranslator = group._currencyTranslator;
|
|
1104
|
+
|
|
1074
1105
|
const calculatePercent = (parent) => {
|
|
1075
|
-
|
|
1106
|
+
if (!parent || excluded) {
|
|
1107
|
+
return null;
|
|
1108
|
+
}
|
|
1076
1109
|
|
|
1077
|
-
|
|
1078
|
-
const parentData = parent._dataActual;
|
|
1110
|
+
const parentData = parent._dataActual;
|
|
1079
1111
|
|
|
1080
|
-
|
|
1081
|
-
|
|
1112
|
+
if (parentData.marketAbsolute === null || parentData.marketAbsolute.getIsApproximate(Decimal.ZERO, 4)) {
|
|
1113
|
+
return null;
|
|
1114
|
+
}
|
|
1082
1115
|
|
|
1083
|
-
|
|
1084
|
-
numerator = Rate.convert(actual.marketAbsolute, group.currency, parent.currency, ...rates);
|
|
1085
|
-
} else {
|
|
1086
|
-
numerator = actual.marketAbsolute;
|
|
1087
|
-
}
|
|
1116
|
+
let numerator;
|
|
1088
1117
|
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
marketPercent = null;
|
|
1092
|
-
}
|
|
1118
|
+
if (group.currency === parent.currency) {
|
|
1119
|
+
numerator = actual.marketAbsolute;
|
|
1093
1120
|
} else {
|
|
1094
|
-
|
|
1121
|
+
numerator = currencyTranslator.translate(actual.marketAbsolute, group.currency, parent.currency);
|
|
1095
1122
|
}
|
|
1096
1123
|
|
|
1097
|
-
return
|
|
1124
|
+
return numerator.divide(parentData.marketAbsolute);
|
|
1098
1125
|
};
|
|
1099
1126
|
|
|
1100
1127
|
actual.marketPercent = calculatePercent(parentGroup);
|
|
@@ -224,7 +224,7 @@ module.exports = (() => {
|
|
|
224
224
|
.withField('type', DataType.forEnum(TransactionType, 'TransactionType'))
|
|
225
225
|
.withField('instrument.name', DataType.STRING, true)
|
|
226
226
|
.withField('instrument.exchange', DataType.STRING, true)
|
|
227
|
-
.withField('instrument.code', DataType.NUMBER, true)
|
|
227
|
+
.withField('instrument.code', DataType.NUMBER, true) // Not intended to be the unit code. Same value as [profile] table [type] column. See `InstrumentType.fromSymbolType` function.
|
|
228
228
|
.withField('instrument.type', DataType.forEnum(InstrumentType, 'InstrumentType'), true)
|
|
229
229
|
.withField('instrument.currency', DataType.forEnum(Currency, 'Currency'), true)
|
|
230
230
|
.withField('instrument.symbol.barchart', DataType.STRING, true)
|
|
@@ -274,7 +274,7 @@ module.exports = (() => {
|
|
|
274
274
|
.withField('type', DataType.forEnum(TransactionType, 'TransactionType'))
|
|
275
275
|
.withField('instrument.name', DataType.STRING, true)
|
|
276
276
|
.withField('instrument.exchange', DataType.STRING, true)
|
|
277
|
-
.withField('instrument.code', DataType.NUMBER, true)
|
|
277
|
+
.withField('instrument.code', DataType.NUMBER, true) // Not intended to be the unit code. Same value as [profile] table [type] column. See `InstrumentType.fromSymbolType` function.
|
|
278
278
|
.withField('instrument.type', DataType.forEnum(InstrumentType, 'InstrumentType'), true)
|
|
279
279
|
.withField('instrument.currency', DataType.forEnum(Currency, 'Currency'), true)
|
|
280
280
|
.withField('instrument.symbol.barchart', DataType.STRING, true)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barchart/portfolio-api-common",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "2.1.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.
|
|
18
|
+
"@barchart/common-js": "^4.51.0",
|
|
19
19
|
"@barchart/marketdata-api-js": "^6.2.1",
|
|
20
|
-
"uuid": "
|
|
20
|
+
"uuid": "9.0.1"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@babel/core": "^7.6.2",
|