@barchart/portfolio-api-common 1.19.0 → 1.21.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.
@@ -0,0 +1,99 @@
1
+ const Decimal = require('@barchart/common-js/lang/Decimal'),
2
+ is = require('@barchart/common-js/lang/is');
3
+
4
+ const InstrumentType = require('./../data/InstrumentType');
5
+
6
+ module.exports = (() => {
7
+ 'use strict';
8
+
9
+ /**
10
+ * Static utilities for calculating the value of a position.
11
+ *
12
+ * @public
13
+ */
14
+ class ValuationCalculator {
15
+ constructor() {
16
+
17
+ }
18
+
19
+ /**
20
+ * Calculates the value of a position.
21
+ *
22
+ * @public
23
+ * @static
24
+ * @param {Object} instrument
25
+ * @param {Decimal|Number} price
26
+ * @param {Decimal|Number} quantity
27
+ * @returns {null|Decimal}
28
+ */
29
+ static calculate(instrument, price, quantity) {
30
+ let priceToUse = null;
31
+
32
+ if (is.number(price)) {
33
+ priceToUse = new Decimal(price);
34
+ } else if (price instanceof Decimal) {
35
+ priceToUse = price;
36
+ }
37
+
38
+ if (priceToUse === null) {
39
+ return null;
40
+ }
41
+
42
+ const calculator = calculators.get(instrument.type);
43
+
44
+ return calculator(instrument, priceToUse, quantity);
45
+ }
46
+
47
+ toString() {
48
+ return `[ValuationCalculator]`;
49
+ }
50
+ }
51
+
52
+ function calculateForCash(instrument, price, quantity) {
53
+ return new Decimal(quantity);
54
+ }
55
+
56
+ function calculateForEquity(instrument, price, quantity) {
57
+ return price.multiply(quantity);
58
+ }
59
+
60
+ function calculateForEquityOption(instrument, price, quantity) {
61
+ const priceMultiplier = instrument.option.multiplier;
62
+
63
+ return price.multiply(priceMultiplier).multiply(quantity);
64
+ }
65
+
66
+ function calculateForFund(instrument, price, quantity) {
67
+ return price.multiply(quantity);
68
+ }
69
+
70
+ function calculateForFuture(instrument, price, quantity) {
71
+ const minimumTick = instrument.future.tick;
72
+ const minimumTickValue = instrument.future.value;
73
+
74
+ return price.divide(minimumTick).multiply(minimumTickValue).multiply(quantity);
75
+ }
76
+
77
+ function calculateForFutureOption(instrument, price, quantity) {
78
+ const minimumTick = instrument.option.tick;
79
+ const minimumTickValue = instrument.option.value;
80
+
81
+ return price.divide(minimumTick).multiply(minimumTickValue).multiply(quantity);
82
+ }
83
+
84
+ function calculateForOther(instrument, price, quantity) {
85
+ return price.multiply(quantity);
86
+ }
87
+
88
+ const calculators = new Map();
89
+
90
+ calculators.set(InstrumentType.CASH, calculateForCash);
91
+ calculators.set(InstrumentType.EQUITY, calculateForEquity);
92
+ calculators.set(InstrumentType.EQUITY_OPTION, calculateForEquityOption);
93
+ calculators.set(InstrumentType.FUND, calculateForFund);
94
+ calculators.set(InstrumentType.FUTURE, calculateForFuture);
95
+ calculators.set(InstrumentType.FUTURE_OPTION, calculateForFutureOption);
96
+ calculators.set(InstrumentType.OTHER, calculateForOther);
97
+
98
+ return ValuationCalculator;
99
+ })();
@@ -122,9 +122,11 @@ module.exports = (() => {
122
122
 
123
123
  this._dataActual.currentPrice = null;
124
124
  this._dataActual.basis = null;
125
+ this._dataActual.basis2 = null;
125
126
  this._dataActual.realized = null;
126
127
  this._dataActual.income = null;
127
128
  this._dataActual.market = null;
129
+ this._dataActual.market2 = null;
128
130
  this._dataActual.marketPercent = null;
129
131
  this._dataActual.marketPercentPortfolio = null;
130
132
  this._dataActual.unrealized = null;
@@ -145,10 +147,12 @@ module.exports = (() => {
145
147
 
146
148
  this._dataFormat.currentPrice = null;
147
149
  this._dataFormat.basis = null;
150
+ this._dataFormat.basis2 = null;
148
151
  this._dataFormat.realized = null;
149
152
  this._dataFormat.realizedPercent = null;
150
153
  this._dataFormat.income = null;
151
154
  this._dataFormat.market = null;
155
+ this._dataFormat.market2 = null;
152
156
  this._dataFormat.marketPercent = null;
153
157
  this._dataFormat.marketPercentPortfolio = null;
154
158
  this._dataFormat.marketDirection = null;
@@ -716,6 +720,15 @@ module.exports = (() => {
716
720
 
717
721
  let updates = items.reduce((updates, item) => {
718
722
  updates.basis = updates.basis.add(translate(item, item.data.basis));
723
+
724
+ if (item.position.instrument.type === InstrumentType.FUTURE) {
725
+ if (group.single) {
726
+ updates.basis2 = null;
727
+ }
728
+ } else {
729
+ updates.basis2 = updates.basis2.add(translate(item, item.data.basis));
730
+ }
731
+
719
732
  updates.realized = updates.realized.add(translate(item, item.data.realized));
720
733
  updates.unrealized = updates.unrealized.add(translate(item, item.data.unrealized));
721
734
  updates.income = updates.income.add(translate(item, item.data.income));
@@ -741,6 +754,7 @@ module.exports = (() => {
741
754
  return updates;
742
755
  }, {
743
756
  basis: Decimal.ZERO,
757
+ basis2: Decimal.ZERO,
744
758
  realized: Decimal.ZERO,
745
759
  unrealized: Decimal.ZERO,
746
760
  income: Decimal.ZERO,
@@ -760,6 +774,7 @@ module.exports = (() => {
760
774
  });
761
775
 
762
776
  actual.basis = updates.basis;
777
+ actual.basis2 = updates.basis2;
763
778
  actual.realized = updates.realized;
764
779
  actual.unrealized = updates.unrealized;
765
780
  actual.income = updates.income;
@@ -778,6 +793,7 @@ module.exports = (() => {
778
793
  actual.periodDivisorPrevious2 = updates.periodDivisorPrevious2;
779
794
 
780
795
  format.basis = formatCurrency(actual.basis, currency);
796
+ format.basis2 = formatCurrency(actual.basis2, currency);
781
797
  format.realized = formatCurrency(actual.realized, currency);
782
798
  format.unrealized = formatCurrency(actual.unrealized, currency);
783
799
  format.income = formatCurrency(actual.income, currency);
@@ -877,6 +893,13 @@ module.exports = (() => {
877
893
 
878
894
  updates = items.reduce((updates, item) => {
879
895
  updates.market = updates.market.add(translate(item, item.data.market));
896
+
897
+ if (item.position.instrument.type === InstrumentType.FUTURE) {
898
+ updates.market2 = updates.market2.add(translate(item, item.data.unrealized));
899
+ } else {
900
+ updates.market2 = updates.market2.add(translate(item, item.data.market));
901
+ }
902
+
880
903
  updates.marketAbsolute = updates.marketAbsolute.add(translate(item, item.data.marketAbsolute));
881
904
  updates.unrealized = updates.unrealized.add(translate(item, item.data.unrealized));
882
905
  updates.unrealizedToday = updates.unrealizedToday.add(translate(item, item.data.unrealizedToday));
@@ -886,6 +909,7 @@ module.exports = (() => {
886
909
  return updates;
887
910
  }, {
888
911
  market: Decimal.ZERO,
912
+ market2: Decimal.ZERO,
889
913
  marketAbsolute: Decimal.ZERO,
890
914
  marketDirection: unchanged,
891
915
  unrealized: Decimal.ZERO,
@@ -894,18 +918,26 @@ module.exports = (() => {
894
918
  periodUnrealized: Decimal.ZERO
895
919
  });
896
920
  } else {
897
- updates = {
898
- market: actual.market.add(translate(item, item.data.marketChange)),
899
- marketAbsolute: actual.marketAbsolute.add(translate(item, item.data.marketAbsoluteChange)),
900
- marketDirection: { up: item.data.marketChange.getIsPositive(), down: item.data.marketChange.getIsNegative() },
901
- unrealized: actual.unrealized.add(translate(item, item.data.unrealizedChange)),
902
- unrealizedToday: actual.unrealizedToday.add(translate(item, item.data.unrealizedTodayChange)),
903
- summaryTotalCurrent: actual.summaryTotalCurrent.add(translate(item, item.data.periodGainChange)),
904
- periodUnrealized: actual.periodUnrealized.add(translate(item, item.data.periodUnrealizedChange))
905
- };
921
+ updates = { };
922
+
923
+ updates.market = actual.market.add(translate(item, item.data.marketChange));
924
+
925
+ if (item.position.instrument.type === InstrumentType.FUTURE) {
926
+ updates.market2 = actual.market2.add(translate(item, item.data.unrealizedChange));
927
+ } else {
928
+ updates.market2 = actual.market2.add(translate(item, item.data.marketChange));
929
+ }
930
+
931
+ updates.marketAbsolute = actual.marketAbsolute.add(translate(item, item.data.marketAbsoluteChange));
932
+ updates.marketDirection = { up: item.data.marketChange.getIsPositive(), down: item.data.marketChange.getIsNegative() };
933
+ updates.unrealized = actual.unrealized.add(translate(item, item.data.unrealizedChange));
934
+ updates.unrealizedToday = actual.unrealizedToday.add(translate(item, item.data.unrealizedTodayChange));
935
+ updates.summaryTotalCurrent = actual.summaryTotalCurrent.add(translate(item, item.data.periodGainChange));
936
+ updates.periodUnrealized = actual.periodUnrealized.add(translate(item, item.data.periodUnrealizedChange));
906
937
  }
907
938
 
908
939
  actual.market = updates.market;
940
+ actual.market2 = updates.market2;
909
941
  actual.marketAbsolute = updates.marketAbsolute;
910
942
  actual.unrealized = updates.unrealized;
911
943
  actual.unrealizedToday = updates.unrealizedToday;
@@ -934,6 +966,7 @@ module.exports = (() => {
934
966
  actual.marketChangePercent = marketChangePercent;
935
967
 
936
968
  format.market = formatCurrency(actual.market, currency);
969
+ format.market2 = formatCurrency(actual.market2, currency);
937
970
 
938
971
  if (updates.marketDirection.up || updates.marketDirection.down) {
939
972
  format.marketDirection = unchanged;
@@ -8,6 +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');
12
+
11
13
  module.exports = (() => {
12
14
  'use strict';
13
15
 
@@ -514,18 +516,8 @@ module.exports = (() => {
514
516
  market = snapshot.value;
515
517
  } else if (position.instrument.type === InstrumentType.CASH) {
516
518
  market = snapshot.open;
517
- } else if (position.instrument.type === InstrumentType.FUTURE) {
518
- market = getFuturesValue(position.instrument, snapshot.open, price) || snapshot.value;
519
- } else if (position.instrument.type === InstrumentType.FUTURE_OPTION) {
520
- market = getFuturesOptionValue(position.instrument, snapshot.open, price) || snapshot.value;
521
- } else if (position.instrument.type === InstrumentType.EQUITY_OPTION) {
522
- market = getEquityOptionValue(position.instrument, snapshot.open, price) || snapshot.value;
523
519
  } else {
524
- if (price) {
525
- market = snapshot.open.multiply(price);
526
- } else {
527
- market = snapshot.value;
528
- }
520
+ market = ValuationCalculator.calculate(position.instrument, price, snapshot.open) || snapshot.value;
529
521
  }
530
522
 
531
523
  let marketChange;
@@ -555,17 +547,7 @@ module.exports = (() => {
555
547
  let unrealizedTodayChange;
556
548
 
557
549
  if (data.previousPrice && price) {
558
- let unrealizedTodayBase;
559
-
560
- if (position.instrument.type === InstrumentType.FUTURE) {
561
- unrealizedTodayBase = getFuturesValue(position.instrument, snapshot.open, data.previousPrice);
562
- } else if (position.instrument.type === InstrumentType.FUTURE_OPTION) {
563
- unrealizedTodayBase = getFuturesOptionValue(position.instrument, snapshot.open, data.previousPrice);
564
- } else if (position.instrument.type === InstrumentType.EQUITY_OPTION) {
565
- unrealizedTodayBase = getEquityOptionValue(position.instrument, snapshot.open, data.previousPrice);
566
- } else {
567
- unrealizedTodayBase = snapshot.open.multiply(data.previousPrice);
568
- }
550
+ const unrealizedTodayBase = ValuationCalculator.calculate(position.instrument, data.previousPrice, snapshot.open);
569
551
 
570
552
  unrealizedToday = market.subtract(unrealizedTodayBase);
571
553
 
@@ -599,17 +581,7 @@ module.exports = (() => {
599
581
  }
600
582
 
601
583
  if (priceToUse !== null) {
602
- let unrealized;
603
-
604
- if (position.instrument.type === InstrumentType.FUTURE) {
605
- unrealized = getFuturesValue(position.instrument, currentSummary.end.open, priceToUse).add(currentSummary.end.basis);
606
- } else if (position.instrument.type === InstrumentType.FUTURE_OPTION) {
607
- unrealized = getFuturesOptionValue(position.instrument, currentSummary.end.open, priceToUse).add(currentSummary.end.basis);
608
- } else if (position.instrument.type === InstrumentType.EQUITY_OPTION) {
609
- unrealized = getEquityOptionValue(position.instrument, currentSummary.end.open, priceToUse).add(currentSummary.end.basis);
610
- } else {
611
- unrealized = currentSummary.end.open.multiply(priceToUse).add(currentSummary.end.basis);
612
- }
584
+ const unrealized = ValuationCalculator.calculate(position.instrument, priceToUse, currentSummary.end.open).add(currentSummary.end.basis);
613
585
 
614
586
  let unrealizedChange;
615
587
 
@@ -694,15 +666,7 @@ module.exports = (() => {
694
666
  let endValue;
695
667
 
696
668
  if (overridePrice) {
697
- if (type === InstrumentType.FUTURE) {
698
- endValue = getFuturesValue(instrument, currentSummary.end.open, overridePrice);
699
- } else if (type === InstrumentType.FUTURE_OPTION) {
700
- endValue = getFuturesOptionValue(instrument, currentSummary.end.open, overridePrice);
701
- } else if (type === InstrumentType.EQUITY_OPTION) {
702
- endValue = getEquityOptionValue(instrument, currentSummary.end.open, overridePrice);
703
- } else {
704
- endValue = currentSummary.end.open.multiply(overridePrice);
705
- }
669
+ endValue = ValuationCalculator.calculate(instrument, overridePrice, currentSummary.end.open);
706
670
  } else {
707
671
  endValue = currentSummary.end.value;
708
672
  }
@@ -823,44 +787,5 @@ module.exports = (() => {
823
787
  return snapshot;
824
788
  }
825
789
 
826
- function getFuturesValue(instrument, contracts, price) {
827
- if (price || price === 0) {
828
- const priceDecimal = new Decimal(price);
829
-
830
- const minimumTick = instrument.future.tick;
831
- const minimumTickValue = instrument.future.value;
832
-
833
- return priceDecimal.divide(minimumTick).multiply(minimumTickValue).multiply(contracts);
834
- } else {
835
- return null;
836
- }
837
- }
838
-
839
- function getFuturesOptionValue(instrument, contracts, price) {
840
- if (price || price === 0) {
841
- const priceDecimal = new Decimal(price);
842
-
843
- const minimumTick = instrument.option.tick;
844
- const minimumTickValue = instrument.option.value;
845
-
846
- const multiplier = instrument.option.multiplier;
847
-
848
- return priceDecimal.divide(minimumTick).multiply(minimumTickValue).multiply(multiplier).multiply(contracts);
849
- } else {
850
- return null;
851
- }
852
- }
853
-
854
- function getEquityOptionValue(instrument, contracts, price) {
855
- if (price || price === 0) {
856
- const priceDecimal = new Decimal(price);
857
- const multiplier = instrument.option.multiplier;
858
-
859
- return priceDecimal.multiply(contracts).multiply(multiplier);
860
- } else {
861
- return null;
862
- }
863
- }
864
-
865
790
  return PositionItem;
866
791
  })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barchart/portfolio-api-common",
3
- "version": "1.19.0",
3
+ "version": "1.21.0",
4
4
  "description": "Common JavaScript code used by Barchart's Portfolio Service",
5
5
  "author": {
6
6
  "name": "Bryan Ingle",