@barchart/portfolio-api-common 1.23.0 → 1.25.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.
@@ -24,7 +24,7 @@ module.exports = (() => {
24
24
  * @param {Object} instrument
25
25
  * @param {Decimal|Number} basis
26
26
  * @param {Decimal|Number} quantity
27
- * @returns {null|Decimal}
27
+ * @returns {Decimal|null}
28
28
  */
29
29
  static calculate(instrument, basis, quantity) {
30
30
  let basisToUse = null;
@@ -34,10 +34,18 @@ module.exports = (() => {
34
34
  } else if (basis instanceof Decimal) {
35
35
  basisToUse = basis;
36
36
  }
37
+
38
+ let quantityToUse = null;
39
+
40
+ if (is.number(basis)) {
41
+ quantityToUse = new Decimal(quantity);
42
+ } else if (basis instanceof Decimal) {
43
+ quantityToUse = quantity;
44
+ }
37
45
 
38
46
  const calculator = calculators.get(instrument.type);
39
47
 
40
- return calculator(instrument, basisToUse, quantity);
48
+ return calculator(instrument, basisToUse, quantityToUse);
41
49
  }
42
50
 
43
51
  toString() {
@@ -50,11 +58,7 @@ module.exports = (() => {
50
58
  }
51
59
 
52
60
  function calculateForEquity(instrument, basis, quantity) {
53
- if (basis === null) {
54
- return null;
55
- }
56
-
57
- if (quantity === Decimal.ZERO || quantity === 0) {
61
+ if (basis === null || quantity === null || quantity.getIsZero()) {
58
62
  return null;
59
63
  }
60
64
 
@@ -62,11 +66,7 @@ module.exports = (() => {
62
66
  }
63
67
 
64
68
  function calculateForEquityOption(instrument, basis, quantity) {
65
- if (basis === null) {
66
- return null;
67
- }
68
-
69
- if (quantity === Decimal.ZERO || quantity === 0) {
69
+ if (basis === null || quantity === null || quantity.getIsZero()) {
70
70
  return null;
71
71
  }
72
72
 
@@ -76,11 +76,7 @@ module.exports = (() => {
76
76
  }
77
77
 
78
78
  function calculateForFund(instrument, basis, quantity) {
79
- if (basis === null) {
80
- return null;
81
- }
82
-
83
- if (quantity === Decimal.ZERO || quantity === 0) {
79
+ if (basis === null || quantity === null || quantity.getIsZero()) {
84
80
  return null;
85
81
  }
86
82
 
@@ -88,11 +84,7 @@ module.exports = (() => {
88
84
  }
89
85
 
90
86
  function calculateForFuture(instrument, basis, quantity) {
91
- if (basis === null) {
92
- return null;
93
- }
94
-
95
- if (quantity === Decimal.ZERO || quantity === 0) {
87
+ if (basis === null || quantity === null || quantity.getIsZero()) {
96
88
  return null;
97
89
  }
98
90
 
@@ -103,11 +95,7 @@ module.exports = (() => {
103
95
  }
104
96
 
105
97
  function calculateForFutureOption(instrument, basis, quantity) {
106
- if (basis === null) {
107
- return null;
108
- }
109
-
110
- if (quantity === Decimal.ZERO || quantity === 0) {
98
+ if (basis === null || quantity === null || quantity.getIsZero()) {
111
99
  return null;
112
100
  }
113
101
 
@@ -118,11 +106,7 @@ module.exports = (() => {
118
106
  }
119
107
 
120
108
  function calculateForOther(instrument, basis, quantity) {
121
- if (basis === null) {
122
- return null;
123
- }
124
-
125
- if (quantity === Decimal.ZERO || quantity === 0) {
109
+ if (basis === null || quantity === null || quantity.getIsZero()) {
126
110
  return null;
127
111
  }
128
112
 
@@ -10,7 +10,7 @@ module.exports = (() => {
10
10
  *
11
11
  * @public
12
12
  */
13
- class PositionSummaryFormatter{
13
+ class PositionSummaryFormatter {
14
14
  /**
15
15
  * The formatter
16
16
  *
@@ -6,6 +6,8 @@ const assert = require('@barchart/common-js/lang/assert'),
6
6
  is = require('@barchart/common-js/lang/is'),
7
7
  formatter = require('@barchart/common-js/lang/formatter');
8
8
 
9
+ formatter.numberToFraction = require('@barchart/marketdata-api-js/lib/utilities/format/fraction');
10
+
9
11
  const InstrumentType = require('./../data/InstrumentType'),
10
12
  TransactionType = require('./../data/TransactionType');
11
13
 
@@ -32,12 +34,14 @@ module.exports = (() => {
32
34
  * @param {Object[]} transactions
33
35
  * @param {Object[]} positions
34
36
  * @param {Boolean=} descending
37
+ * @param {Boolean=} fractions
35
38
  * @returns {Array}
36
39
  */
37
- static format(transactions, positions, descending) {
40
+ static format(transactions, positions, descending, fractions) {
38
41
  assert.argumentIsArray(transactions, 'transactions');
39
42
  assert.argumentIsArray(positions, 'positions');
40
43
  assert.argumentIsOptional(descending, 'descending', Boolean);
44
+ assert.argumentIsOptional(fractions, 'fractions', Boolean);
41
45
 
42
46
  const instruments = positions.reduce((map, p) => {
43
47
  const instrument = Object.assign({ }, p.instrument || { });
@@ -52,7 +56,7 @@ module.exports = (() => {
52
56
 
53
57
  if (instruments.hasOwnProperty(position)) {
54
58
  let instrument = instruments[position];
55
- let formatted = { instrument, raw: {} };
59
+ let formatted = { instrument, raw: { } };
56
60
 
57
61
  const formatterFunctions = formatters.get(transaction.type);
58
62
 
@@ -60,13 +64,21 @@ module.exports = (() => {
60
64
  formatterFunction(transaction, formatted);
61
65
  });
62
66
 
67
+ const code = instrument.code;
68
+
63
69
  Object.keys(formatted).forEach((key) => {
64
70
  const value = formatted[key];
65
71
 
66
72
  if (value instanceof Decimal) {
67
- const precision = instrument.currency.precision;
73
+ if (fractions && code && code.supportsFractions && (instrument.type === InstrumentType.FUTURE || instrument.type === InstrumentType.FUTURE_OPTION) && keys.fractions.some(k => k === key)) {
74
+ const rounded = code.roundToNearestTick(value.toFloat(), instrument.future ? instrument.future.tick : instrument.option.tick, true);
75
+
76
+ formatted[key] = formatter.numberToFraction(rounded, code.fractionFactor, code.fractionDigits, '-', true);
77
+ } else {
78
+ const precision = instrument.currency.precision;
68
79
 
69
- formatted[key] = formatter.numberToString(value.toFloat(), precision, ',');
80
+ formatted[key] = formatter.numberToString(value.toFloat(), precision, ',');
81
+ }
70
82
  }
71
83
  });
72
84
 
@@ -122,6 +134,10 @@ module.exports = (() => {
122
134
  }
123
135
  }
124
136
 
137
+ const keys = { };
138
+
139
+ keys.fractions = [ 'average', 'price' ];
140
+
125
141
  const basicFormatter = (t, f) => {
126
142
  f.date = t.date;
127
143
  f.type = t.type.display;
@@ -147,6 +163,7 @@ module.exports = (() => {
147
163
  }
148
164
 
149
165
  f.average = average;
166
+ f.raw.average = getRawForDecimal(average);
150
167
  };
151
168
 
152
169
  const buySellFormatter = (t, f) => {
@@ -9,6 +9,8 @@ const array = require('@barchart/common-js/lang/array'),
9
9
  is = require('@barchart/common-js/lang/is'),
10
10
  Rate = require('@barchart/common-js/lang/Rate');
11
11
 
12
+ const fractionFormatter = require('@barchart/marketdata-api-js/lib/utilities/format/fraction');
13
+
12
14
  const InstrumentType = require('./../data/InstrumentType');
13
15
 
14
16
  const PositionLevelDefinition = require('./definitions/PositionLevelDefinition'),
@@ -24,9 +26,9 @@ module.exports = (() => {
24
26
  * all the positions and performs currency translation, as necessary.
25
27
  *
26
28
  * @public
27
- * @param {PositionContainer} container
28
29
  * @param {PositionLevelDefinition} definition
29
30
  * @param {PositionItem[]} items
31
+ * @param {Rate[]} rates
30
32
  * @param {Currency} currency
31
33
  * @param {String} key
32
34
  * @param {String} description
@@ -78,6 +80,7 @@ module.exports = (() => {
78
80
  this._dataFormat.quantity = null;
79
81
  this._dataFormat.quantityPrevious = null;
80
82
  this._dataFormat.basisPrice = null;
83
+ this._dataFormat.unrealizedPrice = null;
81
84
 
82
85
  this._dataActual.key = this._key;
83
86
  this._dataActual.description = this._description;
@@ -85,6 +88,7 @@ module.exports = (() => {
85
88
  this._dataActual.quantity = null;
86
89
  this._dataActual.quantityPrevious = null;
87
90
  this._dataActual.basisPrice = null;
91
+ this._dataActual.unrealizedPrice = null;
88
92
 
89
93
  if (this._single && items.length === 1) {
90
94
  const item = items[0];
@@ -218,10 +222,10 @@ module.exports = (() => {
218
222
  }
219
223
 
220
224
  /**
221
- * The {@link LevelDefinition} which was used to generate this group.
225
+ * The {@link PositionLevelDefinition} which was used to generate this group.
222
226
  *
223
227
  * @public
224
- * @returns {LevelDefinition}
228
+ * @returns {PositionLevelDefinition}
225
229
  */
226
230
  get definition() {
227
231
  return this._definition;
@@ -516,10 +520,12 @@ module.exports = (() => {
516
520
  function bindItem(item) {
517
521
  const quoteBinding = item.registerQuoteChangeHandler((quote, sender) => {
518
522
  if (this._single) {
519
- const precision = sender.position.instrument.currency.precision;
523
+ const instrument = sender.position.instrument;
524
+ const currency = instrument.currency;
525
+ const precision = currency.precision;
520
526
 
521
527
  this._dataActual.currentPrice = quote.lastPrice;
522
- this._dataFormat.currentPrice = formatNumber(this._dataActual.currentPrice, precision);
528
+ this._dataFormat.currentPrice = formatFraction(this._dataActual.currentPrice, currency, instrument);
523
529
 
524
530
  this._dataActual.quoteLast = quote.previousPrice;
525
531
  this._dataActual.quoteOpen = quote.openPrice;
@@ -534,7 +540,8 @@ module.exports = (() => {
534
540
  this._dataFormat.quoteOpen = formatNumber(this._dataActual.quoteOpen, precision);
535
541
  this._dataFormat.quoteHigh = formatNumber(this._dataActual.quoteHigh, precision);
536
542
  this._dataFormat.quoteLow = formatNumber(this._dataActual.quoteLow, precision);
537
- this._dataFormat.quoteChange = formatNumber(this._dataActual.quoteChange, precision);
543
+ this._dataFormat.quoteChange = formatFraction(this._dataActual.quoteChange, currency, instrument);
544
+
538
545
  this._dataFormat.quoteChangePercent = formatPercent(new Decimal(this._dataActual.quoteChangePercent || 0), 2);
539
546
  this._dataFormat.quoteTime = this._dataActual.quoteTime;
540
547
  this._dataFormat.quoteVolume = formatNumber(this._dataActual.quoteVolume, 0);
@@ -660,6 +667,27 @@ module.exports = (() => {
660
667
  }));
661
668
  }
662
669
 
670
+ function formatFraction(value, currency, instrument) {
671
+ let decimal = value instanceof Decimal;
672
+
673
+ if (instrument && value !== null) {
674
+ const type = instrument.type;
675
+ const code = instrument.code;
676
+
677
+ if (code && code.supportsFractions && (type === InstrumentType.FUTURE || type === InstrumentType.FUTURE_OPTION)) {
678
+ const rounded = code.roundToNearestTick(decimal ? value.toFloat() : value, instrument.future ? instrument.future.tick : instrument.option.tick, true);
679
+
680
+ return fractionFormatter(rounded, code.fractionFactor, code.fractionDigits, '-', true);
681
+ }
682
+ }
683
+
684
+ if (decimal) {
685
+ return formatDecimal(value, currency.precision);
686
+ } else {
687
+ return formatNumber(value, currency.precision);
688
+ }
689
+ }
690
+
663
691
  function formatNumber(number, precision) {
664
692
  if (is.number(number)) {
665
693
  return formatter.numberToString(number, precision, ',', false);
@@ -825,6 +853,7 @@ module.exports = (() => {
825
853
 
826
854
  if (group.single && groupItems.length === 1) {
827
855
  const item = group._items[0];
856
+ const instrument = item.position.instrument;
828
857
 
829
858
  actual.quantity = item.data.quantity;
830
859
  actual.quantityPrevious = item.data.quantityPrevious;
@@ -833,8 +862,7 @@ module.exports = (() => {
833
862
  format.quantityPrevious = formatDecimal(actual.quantityPrevious, 2);
834
863
 
835
864
  actual.basisPrice = item.data.basisPrice;
836
-
837
- format.basisPrice = formatCurrency(actual.basisPrice, currency);
865
+ format.basisPrice = formatFraction(actual.basisPrice, currency, instrument);
838
866
 
839
867
  actual.periodPrice = item.data.periodPrice;
840
868
  actual.periodPricePrevious = item.data.periodPricePrevious;
@@ -842,6 +870,9 @@ module.exports = (() => {
842
870
  format.periodPrice = formatCurrency(actual.periodPrice, currency);
843
871
  format.periodPricePrevious = formatCurrency(actual.periodPricePrevious, currency);
844
872
 
873
+ actual.unrealizedPrice = item.data.unrealizedPrice;
874
+ format.unrealizedPrice = formatFraction(actual.unrealizedPrice, currency, instrument);
875
+
845
876
  format.invalid = definition.type === PositionLevelType.POSITION && item.invalid;
846
877
  format.locked = definition.type === PositionLevelType.POSITION && item.data.locked;
847
878
  format.calculating = definition.type === PositionLevelType.POSITION && item.data.calculating;
@@ -996,6 +1027,11 @@ module.exports = (() => {
996
1027
 
997
1028
  actual.periodPercent = calculateGainPercent(actual.summaryTotalCurrent, actual.periodDivisorCurrent);
998
1029
  format.periodPercent = formatPercent(actual.periodPercent, 2);
1030
+
1031
+ if (group.single && item) {
1032
+ actual.unrealizedPrice = item.data.unrealizedPrice;
1033
+ format.unrealizedPrice = formatFraction(actual.unrealizedPrice, currency, item.position.instrument);
1034
+ }
999
1035
  }
1000
1036
 
1001
1037
  function calculateMarketPercent(group, rates, parentGroup, portfolioGroup) {
@@ -8,7 +8,8 @@ const assert = require('@barchart/common-js/lang/assert'),
8
8
  const InstrumentType = require('./../data/InstrumentType'),
9
9
  PositionDirection = require('./../data/PositionDirection');
10
10
 
11
- const ValuationCalculator = require('./../calculators/ValuationCalculator');
11
+ const AveragePriceCalculator = require('./../calculators/AveragePriceCalculator'),
12
+ ValuationCalculator = require('./../calculators/ValuationCalculator');
12
13
 
13
14
  module.exports = (() => {
14
15
  'use strict';
@@ -73,7 +74,9 @@ module.exports = (() => {
73
74
 
74
75
  this._data.realized = null;
75
76
  this._data.income = null;
77
+
76
78
  this._data.basisPrice = null;
79
+ this._data.unrealizedPrice = null;
77
80
 
78
81
  this._data.periodIncome = null;
79
82
  this._data.periodRealized = null;
@@ -478,16 +481,8 @@ module.exports = (() => {
478
481
  data.periodDivisorPrevious = calculatePeriodDivisor(position.instrument.type, data.initiate, previousSummary1, previousSummary2);
479
482
  data.periodDivisorPrevious2 = calculatePeriodDivisor(position.instrument.type, data.initiate, previousSummary2, previousSummary3);
480
483
 
481
- if (snapshot.open.getIsZero()) {
482
- data.basisPrice = Decimal.ZERO;
483
- } else if (position.instrument.type === InstrumentType.FUTURE) {
484
- const minimumTick = position.instrument.future.tick;
485
- const minimumTickValue = position.instrument.future.value;
486
-
487
- data.basisPrice = basis.divide(snapshot.open).divide(minimumTickValue).multiply(minimumTick);
488
- } else {
489
- data.basisPrice = basis.divide(snapshot.open);
490
- }
484
+ data.basisPrice = AveragePriceCalculator.calculate(position.instrument, data.basis, snapshot.open) || Decimal.ZERO;
485
+ data.basisPrice = data.basisPrice.opposite();
491
486
 
492
487
  if (currentSummary && !currentSummary.end.open.getIsZero()) {
493
488
  data.periodPrice = currentSummary.end.value.divide(currentSummary.end.open);
@@ -617,6 +612,12 @@ module.exports = (() => {
617
612
 
618
613
  data.periodUnrealized = periodUnrealized;
619
614
  data.periodUnrealizedChange = periodUnrealizedChange;
615
+
616
+ if (snapshot.open.getIsZero()) {
617
+ data.unrealizedPrice = null;
618
+ } else {
619
+ data.unrealizedPrice = data.basisPrice.opposite().add(priceToUse);
620
+ }
620
621
  } else {
621
622
  data.unrealizedChange = Decimal.ZERO;
622
623
  data.periodUnrealizedChange = Decimal.ZERO;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barchart/portfolio-api-common",
3
- "version": "1.23.0",
3
+ "version": "1.25.0",
4
4
  "description": "Common JavaScript code used by Barchart's Portfolio Service",
5
5
  "author": {
6
6
  "name": "Bryan Ingle",
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "dependencies": {
18
18
  "@barchart/common-js": "^4.27.0",
19
- "@barchart/marketdata-api-js": "^6.2.0",
19
+ "@barchart/marketdata-api-js": "^6.2.1",
20
20
  "uuid": "^8.3.2"
21
21
  },
22
22
  "devDependencies": {