@barchart/portfolio-api-common 1.0.63 → 1.0.67

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.
@@ -91,6 +91,8 @@ module.exports = (() => {
91
91
  return;
92
92
  }
93
93
 
94
+ const parent = tree.getValue() || null;
95
+
94
96
  const currentDefinition = definitions[0];
95
97
  const additionalDefinitions = array.dropLeft(definitions);
96
98
 
@@ -98,13 +100,13 @@ module.exports = (() => {
98
100
  const populatedGroups = Object.keys(populatedObjects).map(key => populatedObjects[key]).map((items) => {
99
101
  const first = items[0];
100
102
 
101
- return new PositionGroup(items, currentDefinition.currencySelector(first), currentDefinition.descriptionSelector(first), currentDefinition.single && items.length === 1);
103
+ return new PositionGroup(parent, items, currentDefinition.currencySelector(first), currentDefinition.descriptionSelector(first), currentDefinition.single && items.length === 1);
102
104
  });
103
105
 
104
106
  const missingGroups = array.difference(currentDefinition.requiredGroups.map(group => group.description), populatedGroups.map(group => group.description));
105
107
 
106
108
  const empty = missingGroups.map((description) => {
107
- return new PositionGroup([ ], currentDefinition.requiredGroups.find(group => group.description === description).currency, description);
109
+ return new PositionGroup(parent, [ ], currentDefinition.requiredGroups.find(group => group.description === description).currency, description);
108
110
  });
109
111
 
110
112
  const compositeGroups = populatedGroups.concat(empty);
@@ -11,7 +11,9 @@ module.exports = (() => {
11
11
  * @public
12
12
  */
13
13
  class PositionGroup {
14
- constructor(items, currency, description, single) {
14
+ constructor(parent, items, currency, description, single) {
15
+ this._parent = parent || null;
16
+
15
17
  this._items = items;
16
18
  this._currency = currency;
17
19
 
@@ -20,33 +22,37 @@ module.exports = (() => {
20
22
  this._single = is.boolean(single) && single;
21
23
 
22
24
  this._dataFormat = { };
23
- this._dataRaw = { };
25
+ this._dataActual = { };
24
26
 
25
27
  this._dataFormat.description = this._description;
26
28
 
27
- this._dataRaw.current = null;
28
- this._dataRaw.previous = null;
29
-
30
- this._dataFormat.current = null;
31
- this._dataFormat.previous = null;
32
-
33
- this._dataRaw.basis = null;
34
- this._dataRaw.market = null;
29
+ this._dataActual.currentPrice = null;
30
+ this._dataActual.previousPrice = null;
31
+ this._dataActual.basis = null;
32
+ this._dataActual.market = null;
33
+ this._dataActual.marketPercent = null;
34
+ this._dataActual.unrealizedToday = null;
35
35
 
36
+ this._dataFormat.currentPrice = null;
37
+ this._dataFormat.previousPrice = null;
36
38
  this._dataFormat.basis = null;
37
39
  this._dataFormat.market = null;
40
+ this._dataFormat.marketPercent = null;
41
+ this._dataFormat.unrealizedToday = null;
42
+
43
+ this._dataFormat.unrealizedTodayNegative = false;
38
44
 
39
45
  this._items.forEach((item) => {
40
46
  item.registerPriceChangeHandler((data, sender) => {
41
47
  if (this._single) {
42
- this._dataRaw.current = data.current;
43
- this._dataFormat.current = format(data.current, sender.position.instrument.currency);
48
+ this._dataActual.currentPrice = data.currentPrice;
49
+ this._dataFormat.currentPrice = format(data.currentPrice, sender.position.instrument.currency);
44
50
  } else {
45
- this._dataRaw.current = null;
46
- this._dataFormat.current = null;
51
+ this._dataActual.currentPrice = null;
52
+ this._dataFormat.currentPrice = null;
47
53
  }
48
54
 
49
- calculatePriceData(this, item);
55
+ calculatePriceData(this, sender);
50
56
  });
51
57
  });
52
58
 
@@ -79,64 +85,96 @@ module.exports = (() => {
79
85
  }
80
86
  }
81
87
 
82
- function format(decimal, currency) {
83
- return formatter.numberToString(decimal.toFloat(), currency.precision, ',', false);
88
+ function formatNumber(decimal, precision) {
89
+ if (decimal !== null) {
90
+ return formatter.numberToString(decimal.toFloat(), precision, ',', false);
91
+ } else {
92
+ return '—';
93
+ }
84
94
  }
85
95
 
86
- function calculateStaticData(group) {
87
- const items = group._items;
88
-
89
- let updates;
96
+ function formatPercent(decimal, precision) {
97
+ if (decimal !== null) {
98
+ return formatNumber(decimal.multiply(100), precision);
99
+ } else {
100
+ return '—';
101
+ }
102
+ }
90
103
 
91
- if (group.single) {
92
- const item = items[0];
104
+ function formatCurrency(decimal, currency) {
105
+ return formatNumber(decimal, currency.precision);
106
+ }
93
107
 
94
- updates = { };
108
+ function calculateStaticData(group) {
109
+ const actual = group._dataActual;
110
+ const format = group._dataFormat;
95
111
 
96
- updates.basis = item.basis;
97
- } else {
98
- updates = items.reduce(function(updates, item) {
99
- updates.basis = updates.basis.add(item.data.basis);
112
+ const currency = group.currency;
113
+
114
+ const items = group._items;
100
115
 
101
- return updates;
102
- }, {
103
- basis: Decimal.ZERO
104
- });
105
- }
116
+ let updates = items.reduce((updates, item) => {
117
+ updates.basis = updates.basis.add(item.data.basis);
106
118
 
107
- const raw = group._dataRaw;
108
- const formatted = group._dataFormat;
119
+ return updates;
120
+ }, {
121
+ basis: Decimal.ZERO
122
+ });
109
123
 
110
- raw.basis = updates.basis;
111
- formatted.basis = format(updates.basis, Currency.USD);
124
+ actual.basis = updates.basis;
125
+
126
+ format.basis = formatCurrency(updates.basis, currency);
112
127
  }
113
128
 
114
- function calculatePriceData(group) {
115
- const items = group._items;
129
+ function calculatePriceData(group, item) {
130
+ const parent = group._parent;
116
131
 
117
- let updates;
132
+ const actual = group._dataActual;
133
+ const format = group._dataFormat;
118
134
 
119
- if (group.single) {
120
- updates = { };
135
+ const currency = group.currency;
121
136
 
122
- let item = items[0];
137
+ let updates;
123
138
 
124
- updates.market = item.market;
125
- } else {
126
- updates = items.reduce(function(updates, item) {
139
+ if (actual.market === null || actual.unrealizedToday === null) {
140
+ const items = group._items;
141
+
142
+ updates = items.reduce((updates, item) => {
127
143
  updates.market = updates.market.add(item.data.market);
144
+ updates.unrealizedToday = updates.unrealizedToday.add(item.data.unrealizedToday);
128
145
 
129
146
  return updates;
130
147
  }, {
131
- market: Decimal.ZERO
148
+ market: Decimal.ZERO,
149
+ unrealizedToday: Decimal.ZERO
132
150
  });
151
+ } else {
152
+ updates = {
153
+ market: actual.market.add(item.data.marketChange),
154
+ unrealizedToday: actual.unrealizedToday.add(item.data.unrealizedTodayChange)
155
+ };
133
156
  }
134
-
135
- const raw = group._dataRaw;
136
- const formatted = group._dataFormat;
137
-
138
- raw.market = updates.market;
139
- formatted.market = format(updates.market, Currency.USD);
157
+
158
+ if (parent !== null) {
159
+ const parentData = parent._dataActual;
160
+
161
+ if (parentData.market !== null && !parentData.market.getIsZero()) {
162
+ updates.marketPercent = updates.market.divide(parentData.market);
163
+ } else {
164
+ updates.marketPercent = null;
165
+ }
166
+ } else {
167
+ updates.marketPercent = null;
168
+ }
169
+
170
+ actual.market = updates.market;
171
+ actual.marketPercent = updates.marketPercent;
172
+ actual.unrealizedToday = updates.unrealizedToday;
173
+
174
+ format.market = formatCurrency(updates.market, currency);
175
+ format.marketPercent = formatPercent(updates.marketPercent, 2);
176
+ format.unrealizedToday = formatCurrency(updates.unrealizedToday, currency);
177
+ format.unrealizedTodayNegative = actual.unrealizedToday.getIsNegative();
140
178
  }
141
179
 
142
180
  return PositionGroup;
@@ -19,10 +19,17 @@ module.exports = (() => {
19
19
 
20
20
  this._data = { };
21
21
 
22
- this._data.current = null;
23
- this._data.previous = null;
24
22
  this._data.basis = null;
25
23
 
24
+ this._data.currentPrice = null;
25
+ this._data.previousPrice = null;
26
+
27
+ this._data.market = null;
28
+ this._data.marketChange = null;
29
+
30
+ this._data.unrealizedToday = null;
31
+ this._data.unrealizedTodayChange = null;
32
+
26
33
  calculateStaticData(this);
27
34
  calculatePriceData(this, null);
28
35
 
@@ -47,7 +54,7 @@ module.exports = (() => {
47
54
 
48
55
  setPrice(price) {
49
56
  if (this._data.price !== price) {
50
- calculatePriceData(this, this._data.price = price);
57
+ calculatePriceData(this, this._data.currentPrice = price);
51
58
 
52
59
  this._priceChangeEvent.fire(this._data);
53
60
  }
@@ -70,7 +77,7 @@ module.exports = (() => {
70
77
 
71
78
  const data = item._data;
72
79
 
73
- data.previous = position.previous || null;
80
+ data.previousPrice = position.previousPrice || null;
74
81
 
75
82
  let basis;
76
83
 
@@ -103,7 +110,35 @@ module.exports = (() => {
103
110
  }
104
111
  }
105
112
 
113
+ let marketChange;
114
+
115
+ if (data.market === null) {
116
+ marketChange = market;
117
+ } else {
118
+ marketChange = market.subtract(data.market);
119
+ }
120
+
106
121
  data.market = market;
122
+ data.marketChange = marketChange;
123
+
124
+ let unrealizedToday;
125
+ let unrealizedTodayChange;
126
+
127
+ if (data.previousPrice && price) {
128
+ unrealizedToday = market.subtract(snapshot.open.multiply(data.previousPrice));
129
+
130
+ if (data.unrealizedToday !== null) {
131
+ unrealizedTodayChange = unrealizedToday.subtract(data.unrealizedToday);
132
+ } else {
133
+ unrealizedTodayChange = unrealizedToday;
134
+ }
135
+ } else {
136
+ unrealizedToday = Decimal.ZERO;
137
+ unrealizedTodayChange = Decimal.ZERO;
138
+ }
139
+
140
+ data.unrealizedToday = unrealizedToday;
141
+ data.unrealizedTodayChange = unrealizedTodayChange;
107
142
  }
108
143
 
109
144
  return PositionItem;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barchart/portfolio-api-common",
3
- "version": "1.0.63",
3
+ "version": "1.0.67",
4
4
  "description": "Common classes used by the Portfolio system",
5
5
  "author": {
6
6
  "name": "Bryan Ingle",
@@ -697,6 +697,8 @@ module.exports = (() => {
697
697
  return;
698
698
  }
699
699
 
700
+ const parent = tree.getValue() || null;
701
+
700
702
  const currentDefinition = definitions[0];
701
703
  const additionalDefinitions = array.dropLeft(definitions);
702
704
 
@@ -704,13 +706,13 @@ module.exports = (() => {
704
706
  const populatedGroups = Object.keys(populatedObjects).map(key => populatedObjects[key]).map((items) => {
705
707
  const first = items[0];
706
708
 
707
- return new PositionGroup(items, currentDefinition.currencySelector(first), currentDefinition.descriptionSelector(first), currentDefinition.single && items.length === 1);
709
+ return new PositionGroup(parent, items, currentDefinition.currencySelector(first), currentDefinition.descriptionSelector(first), currentDefinition.single && items.length === 1);
708
710
  });
709
711
 
710
712
  const missingGroups = array.difference(currentDefinition.requiredGroups.map(group => group.description), populatedGroups.map(group => group.description));
711
713
 
712
714
  const empty = missingGroups.map((description) => {
713
- return new PositionGroup([ ], currentDefinition.requiredGroups.find(group => group.description === description).currency, description);
715
+ return new PositionGroup(parent, [ ], currentDefinition.requiredGroups.find(group => group.description === description).currency, description);
714
716
  });
715
717
 
716
718
  const compositeGroups = populatedGroups.concat(empty);
@@ -827,7 +829,9 @@ module.exports = (() => {
827
829
  * @public
828
830
  */
829
831
  class PositionGroup {
830
- constructor(items, currency, description, single) {
832
+ constructor(parent, items, currency, description, single) {
833
+ this._parent = parent || null;
834
+
831
835
  this._items = items;
832
836
  this._currency = currency;
833
837
 
@@ -836,33 +840,37 @@ module.exports = (() => {
836
840
  this._single = is.boolean(single) && single;
837
841
 
838
842
  this._dataFormat = { };
839
- this._dataRaw = { };
843
+ this._dataActual = { };
840
844
 
841
845
  this._dataFormat.description = this._description;
842
846
 
843
- this._dataRaw.current = null;
844
- this._dataRaw.previous = null;
845
-
846
- this._dataFormat.current = null;
847
- this._dataFormat.previous = null;
848
-
849
- this._dataRaw.basis = null;
850
- this._dataRaw.market = null;
847
+ this._dataActual.currentPrice = null;
848
+ this._dataActual.previousPrice = null;
849
+ this._dataActual.basis = null;
850
+ this._dataActual.market = null;
851
+ this._dataActual.marketPercent = null;
852
+ this._dataActual.unrealizedToday = null;
851
853
 
854
+ this._dataFormat.currentPrice = null;
855
+ this._dataFormat.previousPrice = null;
852
856
  this._dataFormat.basis = null;
853
857
  this._dataFormat.market = null;
858
+ this._dataFormat.marketPercent = null;
859
+ this._dataFormat.unrealizedToday = null;
860
+
861
+ this._dataFormat.unrealizedTodayNegative = false;
854
862
 
855
863
  this._items.forEach((item) => {
856
864
  item.registerPriceChangeHandler((data, sender) => {
857
865
  if (this._single) {
858
- this._dataRaw.current = data.current;
859
- this._dataFormat.current = format(data.current, sender.position.instrument.currency);
866
+ this._dataActual.currentPrice = data.currentPrice;
867
+ this._dataFormat.currentPrice = format(data.currentPrice, sender.position.instrument.currency);
860
868
  } else {
861
- this._dataRaw.current = null;
862
- this._dataFormat.current = null;
869
+ this._dataActual.currentPrice = null;
870
+ this._dataFormat.currentPrice = null;
863
871
  }
864
872
 
865
- calculatePriceData(this, item);
873
+ calculatePriceData(this, sender);
866
874
  });
867
875
  });
868
876
 
@@ -895,64 +903,96 @@ module.exports = (() => {
895
903
  }
896
904
  }
897
905
 
898
- function format(decimal, currency) {
899
- return formatter.numberToString(decimal.toFloat(), currency.precision, ',', false);
906
+ function formatNumber(decimal, precision) {
907
+ if (decimal !== null) {
908
+ return formatter.numberToString(decimal.toFloat(), precision, ',', false);
909
+ } else {
910
+ return '—';
911
+ }
900
912
  }
901
913
 
902
- function calculateStaticData(group) {
903
- const items = group._items;
904
-
905
- let updates;
914
+ function formatPercent(decimal, precision) {
915
+ if (decimal !== null) {
916
+ return formatNumber(decimal.multiply(100), precision);
917
+ } else {
918
+ return '—';
919
+ }
920
+ }
906
921
 
907
- if (group.single) {
908
- const item = items[0];
922
+ function formatCurrency(decimal, currency) {
923
+ return formatNumber(decimal, currency.precision);
924
+ }
909
925
 
910
- updates = { };
926
+ function calculateStaticData(group) {
927
+ const actual = group._dataActual;
928
+ const format = group._dataFormat;
911
929
 
912
- updates.basis = item.basis;
913
- } else {
914
- updates = items.reduce(function(updates, item) {
915
- updates.basis = updates.basis.add(item.data.basis);
930
+ const currency = group.currency;
931
+
932
+ const items = group._items;
916
933
 
917
- return updates;
918
- }, {
919
- basis: Decimal.ZERO
920
- });
921
- }
934
+ let updates = items.reduce((updates, item) => {
935
+ updates.basis = updates.basis.add(item.data.basis);
922
936
 
923
- const raw = group._dataRaw;
924
- const formatted = group._dataFormat;
937
+ return updates;
938
+ }, {
939
+ basis: Decimal.ZERO
940
+ });
925
941
 
926
- raw.basis = updates.basis;
927
- formatted.basis = format(updates.basis, Currency.USD);
942
+ actual.basis = updates.basis;
943
+
944
+ format.basis = formatCurrency(updates.basis, currency);
928
945
  }
929
946
 
930
- function calculatePriceData(group) {
931
- const items = group._items;
947
+ function calculatePriceData(group, item) {
948
+ const parent = group._parent;
932
949
 
933
- let updates;
950
+ const actual = group._dataActual;
951
+ const format = group._dataFormat;
952
+
953
+ const currency = group.currency;
934
954
 
935
- if (group.single) {
936
- updates = { };
955
+ let updates;
937
956
 
938
- let item = items[0];
957
+ if (actual.market === null || actual.unrealizedToday === null) {
958
+ const items = group._items;
939
959
 
940
- updates.market = item.market;
941
- } else {
942
- updates = items.reduce(function(updates, item) {
960
+ updates = items.reduce((updates, item) => {
943
961
  updates.market = updates.market.add(item.data.market);
962
+ updates.unrealizedToday = updates.unrealizedToday.add(item.data.unrealizedToday);
944
963
 
945
964
  return updates;
946
965
  }, {
947
- market: Decimal.ZERO
966
+ market: Decimal.ZERO,
967
+ unrealizedToday: Decimal.ZERO
948
968
  });
969
+ } else {
970
+ updates = {
971
+ market: actual.market.add(item.data.marketChange),
972
+ unrealizedToday: actual.unrealizedToday.add(item.data.unrealizedTodayChange)
973
+ };
949
974
  }
975
+
976
+ if (parent !== null) {
977
+ const parentData = parent._dataActual;
950
978
 
951
- const raw = group._dataRaw;
952
- const formatted = group._dataFormat;
953
-
954
- raw.market = updates.market;
955
- formatted.market = format(updates.market, Currency.USD);
979
+ if (parentData.market !== null && !parentData.market.getIsZero()) {
980
+ updates.marketPercent = updates.market.divide(parentData.market);
981
+ } else {
982
+ updates.marketPercent = null;
983
+ }
984
+ } else {
985
+ updates.marketPercent = null;
986
+ }
987
+
988
+ actual.market = updates.market;
989
+ actual.marketPercent = updates.marketPercent;
990
+ actual.unrealizedToday = updates.unrealizedToday;
991
+
992
+ format.market = formatCurrency(updates.market, currency);
993
+ format.marketPercent = formatPercent(updates.marketPercent, 2);
994
+ format.unrealizedToday = formatCurrency(updates.unrealizedToday, currency);
995
+ format.unrealizedTodayNegative = actual.unrealizedToday.getIsNegative();
956
996
  }
957
997
 
958
998
  return PositionGroup;
@@ -1030,10 +1070,17 @@ module.exports = (() => {
1030
1070
 
1031
1071
  this._data = { };
1032
1072
 
1033
- this._data.current = null;
1034
- this._data.previous = null;
1035
1073
  this._data.basis = null;
1036
1074
 
1075
+ this._data.currentPrice = null;
1076
+ this._data.previousPrice = null;
1077
+
1078
+ this._data.market = null;
1079
+ this._data.marketChange = null;
1080
+
1081
+ this._data.unrealizedToday = null;
1082
+ this._data.unrealizedTodayChange = null;
1083
+
1037
1084
  calculateStaticData(this);
1038
1085
  calculatePriceData(this, null);
1039
1086
 
@@ -1058,7 +1105,7 @@ module.exports = (() => {
1058
1105
 
1059
1106
  setPrice(price) {
1060
1107
  if (this._data.price !== price) {
1061
- calculatePriceData(this, this._data.price = price);
1108
+ calculatePriceData(this, this._data.currentPrice = price);
1062
1109
 
1063
1110
  this._priceChangeEvent.fire(this._data);
1064
1111
  }
@@ -1081,7 +1128,7 @@ module.exports = (() => {
1081
1128
 
1082
1129
  const data = item._data;
1083
1130
 
1084
- data.previous = position.previous || null;
1131
+ data.previousPrice = position.previousPrice || null;
1085
1132
 
1086
1133
  let basis;
1087
1134
 
@@ -1114,7 +1161,35 @@ module.exports = (() => {
1114
1161
  }
1115
1162
  }
1116
1163
 
1164
+ let marketChange;
1165
+
1166
+ if (data.market === null) {
1167
+ marketChange = market;
1168
+ } else {
1169
+ marketChange = market.subtract(data.market);
1170
+ }
1171
+
1117
1172
  data.market = market;
1173
+ data.marketChange = marketChange;
1174
+
1175
+ let unrealizedToday;
1176
+ let unrealizedTodayChange;
1177
+
1178
+ if (data.previousPrice && price) {
1179
+ unrealizedToday = market.subtract(snapshot.open.multiply(data.previousPrice));
1180
+
1181
+ if (data.unrealizedToday !== null) {
1182
+ unrealizedTodayChange = unrealizedToday.subtract(data.unrealizedToday);
1183
+ } else {
1184
+ unrealizedTodayChange = unrealizedToday;
1185
+ }
1186
+ } else {
1187
+ unrealizedToday = Decimal.ZERO;
1188
+ unrealizedTodayChange = Decimal.ZERO;
1189
+ }
1190
+
1191
+ data.unrealizedToday = unrealizedToday;
1192
+ data.unrealizedTodayChange = unrealizedTodayChange;
1118
1193
  }
1119
1194
 
1120
1195
  return PositionItem;