@barchart/portfolio-api-common 1.0.63 → 1.0.64

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,35 @@ 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;
38
42
 
39
43
  this._items.forEach((item) => {
40
44
  item.registerPriceChangeHandler((data, sender) => {
41
45
  if (this._single) {
42
- this._dataRaw.current = data.current;
43
- this._dataFormat.current = format(data.current, sender.position.instrument.currency);
46
+ this._dataActual.currentPrice = data.currentPrice;
47
+ this._dataFormat.currentPrice = format(data.currentPrice, sender.position.instrument.currency);
44
48
  } else {
45
- this._dataRaw.current = null;
46
- this._dataFormat.current = null;
49
+ this._dataActual.currentPrice = null;
50
+ this._dataFormat.currentPrice = null;
47
51
  }
48
52
 
49
- calculatePriceData(this, item);
53
+ calculatePriceData(this, sender);
50
54
  });
51
55
  });
52
56
 
@@ -79,64 +83,91 @@ module.exports = (() => {
79
83
  }
80
84
  }
81
85
 
82
- function format(decimal, currency) {
83
- return formatter.numberToString(decimal.toFloat(), currency.precision, ',', false);
86
+ function formatNumber(decimal, precision) {
87
+ if (decimal !== null) {
88
+ return formatter.numberToString(decimal.toFloat(), precision, ',', false);
89
+ } else {
90
+ return '--';
91
+ }
84
92
  }
85
93
 
86
- function calculateStaticData(group) {
87
- const items = group._items;
88
-
89
- let updates;
94
+ function formatPercent(decimal, precision) {
95
+ if (decimal !== null) {
96
+ return formatNumber(decimal.multiply(100));
97
+ } else {
98
+ return '--';
99
+ }
100
+ }
90
101
 
91
- if (group.single) {
92
- const item = items[0];
102
+ function formatCurrency(decimal, currency) {
103
+ return formatNumber(decimal, currency.precision);
104
+ }
93
105
 
94
- updates = { };
106
+ function calculateStaticData(group) {
107
+ const actual = group._dataActual;
108
+ const format = group._dataFormat;
95
109
 
96
- updates.basis = item.basis;
97
- } else {
98
- updates = items.reduce(function(updates, item) {
99
- updates.basis = updates.basis.add(item.data.basis);
110
+ const currency = group.currency;
111
+
112
+ const items = group._items;
100
113
 
101
- return updates;
102
- }, {
103
- basis: Decimal.ZERO
104
- });
105
- }
114
+ let updates = items.reduce((updates, item) => {
115
+ updates.basis = updates.basis.add(item.data.basis);
106
116
 
107
- const raw = group._dataRaw;
108
- const formatted = group._dataFormat;
117
+ return updates;
118
+ }, {
119
+ basis: Decimal.ZERO
120
+ });
109
121
 
110
- raw.basis = updates.basis;
111
- formatted.basis = format(updates.basis, Currency.USD);
122
+ actual.basis = updates.basis;
123
+
124
+ format.basis = formatCurrency(updates.basis, currency);
112
125
  }
113
126
 
114
- function calculatePriceData(group) {
115
- const items = group._items;
127
+ function calculatePriceData(group, item) {
128
+ const parent = group._parent;
116
129
 
117
- let updates;
130
+ const actual = group._dataActual;
131
+ const format = group._dataFormat;
132
+
133
+ const currency = group.currency;
118
134
 
119
- if (group.single) {
120
- updates = { };
135
+ let updates;
121
136
 
122
- let item = items[0];
137
+ if (actual.market === null || actual.unrealizedToday === null) {
138
+ const items = group._items;
123
139
 
124
- updates.market = item.market;
125
- } else {
126
- updates = items.reduce(function(updates, item) {
140
+ updates = items.reduce((updates, item) => {
127
141
  updates.market = updates.market.add(item.data.market);
142
+ updates.unrealizedToday = updates.unrealizedToday.add(item.data.unrealizedToday);
128
143
 
129
144
  return updates;
130
145
  }, {
131
- market: Decimal.ZERO
146
+ market: Decimal.ZERO,
147
+ unrealizedToday: Decimal.ZERO
132
148
  });
149
+ } else {
150
+ updates = {
151
+ market: actual.market.add(item.data.marketChange),
152
+ unrealizedToday: actual.unrealizedToday.add(item.data.unrealizedTodayChange)
153
+ };
133
154
  }
155
+
156
+ if (parent !== null) {
157
+ const parentData = parent._dataActual;
134
158
 
135
- const raw = group._dataRaw;
136
- const formatted = group._dataFormat;
137
-
138
- raw.market = updates.market;
139
- formatted.market = format(updates.market, Currency.USD);
159
+ if (parentData.market !== null && !parentData.market.getIsZero()) {
160
+ updates.marketPercent = updates.market.divide(parentData.market);
161
+ }
162
+ }
163
+
164
+ actual.market = updates.market;
165
+ actual.marketPercent = updates.marketPercent;
166
+ actual.unrealizedToday = updates.unrealizedToday;
167
+
168
+ format.market = formatCurrency(updates.market, currency);
169
+ format.marketPercent = formatPercent(updates.unrealizedToday, 2);
170
+ format.unrealizedToday = formatCurrency(updates.unrealizedToday, currency);
140
171
  }
141
172
 
142
173
  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.64",
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,35 @@ 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;
854
860
 
855
861
  this._items.forEach((item) => {
856
862
  item.registerPriceChangeHandler((data, sender) => {
857
863
  if (this._single) {
858
- this._dataRaw.current = data.current;
859
- this._dataFormat.current = format(data.current, sender.position.instrument.currency);
864
+ this._dataActual.currentPrice = data.currentPrice;
865
+ this._dataFormat.currentPrice = format(data.currentPrice, sender.position.instrument.currency);
860
866
  } else {
861
- this._dataRaw.current = null;
862
- this._dataFormat.current = null;
867
+ this._dataActual.currentPrice = null;
868
+ this._dataFormat.currentPrice = null;
863
869
  }
864
870
 
865
- calculatePriceData(this, item);
871
+ calculatePriceData(this, sender);
866
872
  });
867
873
  });
868
874
 
@@ -895,64 +901,91 @@ module.exports = (() => {
895
901
  }
896
902
  }
897
903
 
898
- function format(decimal, currency) {
899
- return formatter.numberToString(decimal.toFloat(), currency.precision, ',', false);
904
+ function formatNumber(decimal, precision) {
905
+ if (decimal !== null) {
906
+ return formatter.numberToString(decimal.toFloat(), precision, ',', false);
907
+ } else {
908
+ return '--';
909
+ }
900
910
  }
901
911
 
902
- function calculateStaticData(group) {
903
- const items = group._items;
904
-
905
- let updates;
912
+ function formatPercent(decimal, precision) {
913
+ if (decimal !== null) {
914
+ return formatNumber(decimal.multiply(100));
915
+ } else {
916
+ return '--';
917
+ }
918
+ }
906
919
 
907
- if (group.single) {
908
- const item = items[0];
920
+ function formatCurrency(decimal, currency) {
921
+ return formatNumber(decimal, currency.precision);
922
+ }
909
923
 
910
- updates = { };
924
+ function calculateStaticData(group) {
925
+ const actual = group._dataActual;
926
+ const format = group._dataFormat;
911
927
 
912
- updates.basis = item.basis;
913
- } else {
914
- updates = items.reduce(function(updates, item) {
915
- updates.basis = updates.basis.add(item.data.basis);
928
+ const currency = group.currency;
929
+
930
+ const items = group._items;
916
931
 
917
- return updates;
918
- }, {
919
- basis: Decimal.ZERO
920
- });
921
- }
932
+ let updates = items.reduce((updates, item) => {
933
+ updates.basis = updates.basis.add(item.data.basis);
922
934
 
923
- const raw = group._dataRaw;
924
- const formatted = group._dataFormat;
935
+ return updates;
936
+ }, {
937
+ basis: Decimal.ZERO
938
+ });
925
939
 
926
- raw.basis = updates.basis;
927
- formatted.basis = format(updates.basis, Currency.USD);
940
+ actual.basis = updates.basis;
941
+
942
+ format.basis = formatCurrency(updates.basis, currency);
928
943
  }
929
944
 
930
- function calculatePriceData(group) {
931
- const items = group._items;
945
+ function calculatePriceData(group, item) {
946
+ const parent = group._parent;
932
947
 
933
- let updates;
948
+ const actual = group._dataActual;
949
+ const format = group._dataFormat;
950
+
951
+ const currency = group.currency;
934
952
 
935
- if (group.single) {
936
- updates = { };
953
+ let updates;
937
954
 
938
- let item = items[0];
955
+ if (actual.market === null || actual.unrealizedToday === null) {
956
+ const items = group._items;
939
957
 
940
- updates.market = item.market;
941
- } else {
942
- updates = items.reduce(function(updates, item) {
958
+ updates = items.reduce((updates, item) => {
943
959
  updates.market = updates.market.add(item.data.market);
960
+ updates.unrealizedToday = updates.unrealizedToday.add(item.data.unrealizedToday);
944
961
 
945
962
  return updates;
946
963
  }, {
947
- market: Decimal.ZERO
964
+ market: Decimal.ZERO,
965
+ unrealizedToday: Decimal.ZERO
948
966
  });
967
+ } else {
968
+ updates = {
969
+ market: actual.market.add(item.data.marketChange),
970
+ unrealizedToday: actual.unrealizedToday.add(item.data.unrealizedTodayChange)
971
+ };
949
972
  }
973
+
974
+ if (parent !== null) {
975
+ const parentData = parent._dataActual;
950
976
 
951
- const raw = group._dataRaw;
952
- const formatted = group._dataFormat;
953
-
954
- raw.market = updates.market;
955
- formatted.market = format(updates.market, Currency.USD);
977
+ if (parentData.market !== null && !parentData.market.getIsZero()) {
978
+ updates.marketPercent = updates.market.divide(parentData.market);
979
+ }
980
+ }
981
+
982
+ actual.market = updates.market;
983
+ actual.marketPercent = updates.marketPercent;
984
+ actual.unrealizedToday = updates.unrealizedToday;
985
+
986
+ format.market = formatCurrency(updates.market, currency);
987
+ format.marketPercent = formatPercent(updates.unrealizedToday, 2);
988
+ format.unrealizedToday = formatCurrency(updates.unrealizedToday, currency);
956
989
  }
957
990
 
958
991
  return PositionGroup;
@@ -1030,10 +1063,17 @@ module.exports = (() => {
1030
1063
 
1031
1064
  this._data = { };
1032
1065
 
1033
- this._data.current = null;
1034
- this._data.previous = null;
1035
1066
  this._data.basis = null;
1036
1067
 
1068
+ this._data.currentPrice = null;
1069
+ this._data.previousPrice = null;
1070
+
1071
+ this._data.market = null;
1072
+ this._data.marketChange = null;
1073
+
1074
+ this._data.unrealizedToday = null;
1075
+ this._data.unrealizedTodayChange = null;
1076
+
1037
1077
  calculateStaticData(this);
1038
1078
  calculatePriceData(this, null);
1039
1079
 
@@ -1058,7 +1098,7 @@ module.exports = (() => {
1058
1098
 
1059
1099
  setPrice(price) {
1060
1100
  if (this._data.price !== price) {
1061
- calculatePriceData(this, this._data.price = price);
1101
+ calculatePriceData(this, this._data.currentPrice = price);
1062
1102
 
1063
1103
  this._priceChangeEvent.fire(this._data);
1064
1104
  }
@@ -1081,7 +1121,7 @@ module.exports = (() => {
1081
1121
 
1082
1122
  const data = item._data;
1083
1123
 
1084
- data.previous = position.previous || null;
1124
+ data.previousPrice = position.previousPrice || null;
1085
1125
 
1086
1126
  let basis;
1087
1127
 
@@ -1114,7 +1154,35 @@ module.exports = (() => {
1114
1154
  }
1115
1155
  }
1116
1156
 
1157
+ let marketChange;
1158
+
1159
+ if (data.market === null) {
1160
+ marketChange = market;
1161
+ } else {
1162
+ marketChange = market.subtract(data.market);
1163
+ }
1164
+
1117
1165
  data.market = market;
1166
+ data.marketChange = marketChange;
1167
+
1168
+ let unrealizedToday;
1169
+ let unrealizedTodayChange;
1170
+
1171
+ if (data.previousPrice && price) {
1172
+ unrealizedToday = market.subtract(snapshot.open.multiply(data.previousPrice));
1173
+
1174
+ if (data.unrealizedToday !== null) {
1175
+ unrealizedTodayChange = unrealizedToday.subtract(data.unrealizedToday);
1176
+ } else {
1177
+ unrealizedTodayChange = unrealizedToday;
1178
+ }
1179
+ } else {
1180
+ unrealizedToday = Decimal.ZERO;
1181
+ unrealizedTodayChange = Decimal.ZERO;
1182
+ }
1183
+
1184
+ data.unrealizedToday = unrealizedToday;
1185
+ data.unrealizedTodayChange = unrealizedTodayChange;
1118
1186
  }
1119
1187
 
1120
1188
  return PositionItem;