@barchart/portfolio-api-common 1.0.129 → 1.0.133
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.
- package/lib/processing/PositionContainer.js +108 -11
- package/lib/processing/PositionGroup.js +104 -9
- package/lib/processing/PositionItem.js +121 -20
- package/package.json +1 -1
- package/test/SpecRunner.js +333 -40
|
@@ -22,11 +22,11 @@ module.exports = (() => {
|
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* A container for positions which groups the positions into one or more
|
|
25
|
-
* trees for aggregation and display purposes. For example,
|
|
26
|
-
* grouped first by asset class then by position
|
|
25
|
+
* trees for aggregation and display purposes. For example, positions could be
|
|
26
|
+
* grouped first by asset class then by position.
|
|
27
27
|
*
|
|
28
28
|
* Furthermore, the container performs aggregation (driven primarily by price
|
|
29
|
-
* changes) for each level of grouping
|
|
29
|
+
* changes) for each level of grouping.
|
|
30
30
|
*
|
|
31
31
|
* @public
|
|
32
32
|
* @param {Array.<PositionTreeDefinition>} definitions
|
|
@@ -108,6 +108,20 @@ module.exports = (() => {
|
|
|
108
108
|
return map;
|
|
109
109
|
}, { });
|
|
110
110
|
|
|
111
|
+
this._symbolsDisplay = this._items.reduce((map, item) => {
|
|
112
|
+
const symbol = extractSymbolForDisplay(item.position);
|
|
113
|
+
|
|
114
|
+
if (symbol) {
|
|
115
|
+
if (!map.hasOwnProperty(symbol)) {
|
|
116
|
+
map[symbol] = [ ];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
map[symbol].push(item);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return map;
|
|
123
|
+
}, { });
|
|
124
|
+
|
|
111
125
|
this._currencies = this._items.reduce((map, item) => {
|
|
112
126
|
const position = item.position;
|
|
113
127
|
|
|
@@ -219,10 +233,14 @@ module.exports = (() => {
|
|
|
219
233
|
}, { });
|
|
220
234
|
}
|
|
221
235
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
236
|
+
/**
|
|
237
|
+
* Returns a distinct list of all symbols used by the positions
|
|
238
|
+
* within the container.
|
|
239
|
+
*
|
|
240
|
+
* @public
|
|
241
|
+
* @param {Boolean} display - If true, all "display" symbols are returned; otherwise Barchart symbols are returned.
|
|
242
|
+
* @returns {Array.<String>}
|
|
243
|
+
*/
|
|
226
244
|
getPositionSymbols(display) {
|
|
227
245
|
const symbols = this._items.reduce((symbols, item) => {
|
|
228
246
|
const position = item.position;
|
|
@@ -245,6 +263,14 @@ module.exports = (() => {
|
|
|
245
263
|
return array.unique(symbols);
|
|
246
264
|
}
|
|
247
265
|
|
|
266
|
+
/**
|
|
267
|
+
* Updates the quote for a single symbol; causing updates to any grouping
|
|
268
|
+
* level that contains the position(s) for the symbol.
|
|
269
|
+
*
|
|
270
|
+
* @public
|
|
271
|
+
* @param {String} symbol
|
|
272
|
+
* @param {Object} quote
|
|
273
|
+
*/
|
|
248
274
|
setPositionQuote(symbol, quote) {
|
|
249
275
|
assert.argumentIsRequired(symbol, 'symbol', String);
|
|
250
276
|
assert.argumentIsRequired(quote, 'quote', Object);
|
|
@@ -254,14 +280,34 @@ module.exports = (() => {
|
|
|
254
280
|
}
|
|
255
281
|
}
|
|
256
282
|
|
|
283
|
+
/**
|
|
284
|
+
* Returns all forex symbols that are required to do currency translations.
|
|
285
|
+
*
|
|
286
|
+
* @public
|
|
287
|
+
* @returns {Array.<String>}
|
|
288
|
+
*/
|
|
257
289
|
getForexSymbols() {
|
|
258
290
|
return this._forexSymbols;
|
|
259
291
|
}
|
|
260
292
|
|
|
293
|
+
/**
|
|
294
|
+
* Returns all current forex quotes.
|
|
295
|
+
*
|
|
296
|
+
* @public
|
|
297
|
+
* @returns {Array.<Object>}
|
|
298
|
+
*/
|
|
261
299
|
getForexQuotes() {
|
|
262
300
|
return this._forexQuotes;
|
|
263
301
|
}
|
|
264
302
|
|
|
303
|
+
/**
|
|
304
|
+
* Updates the forex quote for a single currency pair; causing updates to
|
|
305
|
+
* any grouping level that contains that requires translation.
|
|
306
|
+
*
|
|
307
|
+
* @public
|
|
308
|
+
* @param {String} symbol
|
|
309
|
+
* @param {Object} quote
|
|
310
|
+
*/
|
|
265
311
|
setForexQuote(symbol, quote) {
|
|
266
312
|
assert.argumentIsRequired(symbol, 'symbol', String);
|
|
267
313
|
assert.argumentIsRequired(quote, 'quote', Object);
|
|
@@ -279,10 +325,55 @@ module.exports = (() => {
|
|
|
279
325
|
Object.keys(this._trees).forEach(key => this._trees[key].walk(group => group.setForexRate(rate), true, false));
|
|
280
326
|
}
|
|
281
327
|
|
|
282
|
-
|
|
283
|
-
|
|
328
|
+
/**
|
|
329
|
+
* Updates fundamental data for a single symbol.
|
|
330
|
+
*
|
|
331
|
+
* @public
|
|
332
|
+
* @param {String} symbol
|
|
333
|
+
* @param {Object} data
|
|
334
|
+
*/
|
|
335
|
+
setPositionFundamentalData(symbol, data) {
|
|
336
|
+
assert.argumentIsRequired(symbol, 'symbol', String);
|
|
337
|
+
assert.argumentIsRequired(data, 'data', Object);
|
|
338
|
+
|
|
339
|
+
if (this._symbols.hasOwnProperty(symbol)) {
|
|
340
|
+
this._symbols[symbol].forEach(item => item.setPositionFundamentalData(data));
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Indicates if a news article exists for a symbol.
|
|
346
|
+
*
|
|
347
|
+
* @public
|
|
348
|
+
* @param {String} symbol
|
|
349
|
+
* @param {Boolean} display
|
|
350
|
+
* @param {Boolean} exists
|
|
351
|
+
*/
|
|
352
|
+
setNewsArticleExists(symbol, display, exists) {
|
|
353
|
+
assert.argumentIsRequired(symbol, 'symbol', String);
|
|
354
|
+
assert.argumentIsRequired(display, 'display', Boolean);
|
|
355
|
+
assert.argumentIsRequired(exists, 'exists', Boolean);
|
|
356
|
+
|
|
357
|
+
let map;
|
|
358
|
+
|
|
359
|
+
if (display) {
|
|
360
|
+
map = this._symbols;
|
|
361
|
+
} else {
|
|
362
|
+
map = this._symbolsDisplay;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (map.hasOwnProperty(symbol)) {
|
|
366
|
+
map[symbol].forEach(item => item.setNewsArticleExists(exists));
|
|
367
|
+
}
|
|
284
368
|
}
|
|
285
369
|
|
|
370
|
+
/**
|
|
371
|
+
* Returns a single level of grouping from one of the internal trees.
|
|
372
|
+
*
|
|
373
|
+
* @param {String} name
|
|
374
|
+
* @param {Array.<String> keys
|
|
375
|
+
* @returns {PositionGroup}
|
|
376
|
+
*/
|
|
286
377
|
getGroup(name, keys) {
|
|
287
378
|
assert.argumentIsRequired(name, 'name', String);
|
|
288
379
|
assert.argumentIsArray(keys, 'keys', Number);
|
|
@@ -290,6 +381,14 @@ module.exports = (() => {
|
|
|
290
381
|
return findNode(this._trees[name], keys).getValue();
|
|
291
382
|
}
|
|
292
383
|
|
|
384
|
+
/**
|
|
385
|
+
* Returns all child groups from a level of grouping within one of
|
|
386
|
+
* the internal trees.
|
|
387
|
+
*
|
|
388
|
+
* @param {String} name
|
|
389
|
+
* @param {Array.<String> keys
|
|
390
|
+
* @returns {Array.<PositionGroup>}
|
|
391
|
+
*/
|
|
293
392
|
getGroups(name, keys) {
|
|
294
393
|
assert.argumentIsRequired(name, 'name', String);
|
|
295
394
|
assert.argumentIsArray(keys, 'keys', Number);
|
|
@@ -301,8 +400,6 @@ module.exports = (() => {
|
|
|
301
400
|
assert.argumentIsRequired(name, 'name', String);
|
|
302
401
|
assert.argumentIsRequired(executor, 'executor', Function);
|
|
303
402
|
|
|
304
|
-
assert.argumentIsRequired(executor, 'executor', Function);
|
|
305
|
-
|
|
306
403
|
this._trees[name].walk(group => group.setSuspended(true), false, false);
|
|
307
404
|
|
|
308
405
|
executor(this);
|
|
@@ -10,7 +10,17 @@ module.exports = (() => {
|
|
|
10
10
|
'use strict';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
+
* A grouping of {@link PositionItem} instances. The group aggregates from across
|
|
14
|
+
* all the positions and performs currency translation, as necessary.
|
|
15
|
+
*
|
|
13
16
|
* @public
|
|
17
|
+
* @param {PositionContainer} container
|
|
18
|
+
* @param {PositionGroup|null} parent
|
|
19
|
+
* @param {Array.<PositionItem>} items
|
|
20
|
+
* @param {Currency} currency
|
|
21
|
+
* @param {String} key
|
|
22
|
+
* @param {String} description
|
|
23
|
+
* @param {Boolean=} single
|
|
14
24
|
*/
|
|
15
25
|
class PositionGroup {
|
|
16
26
|
constructor(container, parent, items, currency, key, description, single) {
|
|
@@ -30,15 +40,22 @@ module.exports = (() => {
|
|
|
30
40
|
this._suspended = false;
|
|
31
41
|
|
|
32
42
|
this._marketPercentChangeEvent = new Event(this);
|
|
43
|
+
this._excludedChangeEvent = new Event(this);
|
|
33
44
|
|
|
34
45
|
this._dataFormat = { };
|
|
35
46
|
this._dataActual = { };
|
|
36
47
|
|
|
37
48
|
this._dataFormat.key = this._key;
|
|
38
49
|
this._dataFormat.description = this._description;
|
|
39
|
-
this._dataFormat.
|
|
40
|
-
|
|
50
|
+
this._dataFormat.newsExists = false;
|
|
41
51
|
this._dataFormat.quantity = null;
|
|
52
|
+
this._dataFormat.basisPrice = null;
|
|
53
|
+
|
|
54
|
+
this._dataActual.key = this._key;
|
|
55
|
+
this._dataActual.description = this._description;
|
|
56
|
+
this._dataActual.newsExists = false;
|
|
57
|
+
this._dataActual.quantity = null;
|
|
58
|
+
this._dataActual.basisPrice = null;
|
|
42
59
|
|
|
43
60
|
if (this._single) {
|
|
44
61
|
const item = items[0];
|
|
@@ -46,10 +63,12 @@ module.exports = (() => {
|
|
|
46
63
|
this._dataFormat.portfolio = item.portfolio.portfolio;
|
|
47
64
|
this._dataFormat.position = item.position.position;
|
|
48
65
|
this._dataFormat.instrument = item.position.instrument;
|
|
66
|
+
this._dataFormat.fundamental = item.fundamental || { };
|
|
49
67
|
} else {
|
|
50
68
|
this._dataFormat.portfolio = null;
|
|
51
69
|
this._dataFormat.position = null;
|
|
52
70
|
this._dataFormat.instrument = null;
|
|
71
|
+
this._dataFormat.fundamental = { };
|
|
53
72
|
}
|
|
54
73
|
|
|
55
74
|
this._dataFormat.quoteLast = null;
|
|
@@ -117,35 +136,89 @@ module.exports = (() => {
|
|
|
117
136
|
|
|
118
137
|
calculatePriceData(this, this._container.getForexQuotes(), sender, false);
|
|
119
138
|
});
|
|
139
|
+
|
|
140
|
+
if (this._single) {
|
|
141
|
+
item.registerNewsExistsChangeHandler((exists, sender) => {
|
|
142
|
+
this._dataActual.newsExists = exists;
|
|
143
|
+
this._dataFormat.newsExists = exists;
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
item._fundamentalDataChangeEvent((data, sender) => {
|
|
147
|
+
this._dataFormat.fundamental = data;
|
|
148
|
+
});
|
|
149
|
+
}
|
|
120
150
|
});
|
|
121
151
|
|
|
122
152
|
this.refresh();
|
|
123
153
|
}
|
|
124
154
|
|
|
155
|
+
/**
|
|
156
|
+
* The key of the group.
|
|
157
|
+
*
|
|
158
|
+
* @public
|
|
159
|
+
* @returns {String}
|
|
160
|
+
*/
|
|
125
161
|
get key() {
|
|
126
162
|
return this._key;
|
|
127
163
|
}
|
|
128
164
|
|
|
165
|
+
/**
|
|
166
|
+
* The description of the group.
|
|
167
|
+
*
|
|
168
|
+
* @public
|
|
169
|
+
* @returns {String}
|
|
170
|
+
*/
|
|
129
171
|
get description() {
|
|
130
172
|
return this._description;
|
|
131
173
|
}
|
|
132
174
|
|
|
175
|
+
/**
|
|
176
|
+
* The {@link Currency} which all aggregated data is presented in.
|
|
177
|
+
*
|
|
178
|
+
* @public
|
|
179
|
+
* @returns {Currency}
|
|
180
|
+
*/
|
|
133
181
|
get currency() {
|
|
134
182
|
return this._currency;
|
|
135
183
|
}
|
|
136
184
|
|
|
185
|
+
/**
|
|
186
|
+
* The {@link PositionItem} instances which for which aggregated data is compiled.
|
|
187
|
+
*
|
|
188
|
+
* @public
|
|
189
|
+
* @returns {Currency}
|
|
190
|
+
*/
|
|
137
191
|
get items() {
|
|
138
192
|
return this._items;
|
|
139
193
|
}
|
|
140
194
|
|
|
195
|
+
/**
|
|
196
|
+
* The string-based, human-readable aggregated data for the group.
|
|
197
|
+
*
|
|
198
|
+
* @public
|
|
199
|
+
* @returns {Object}
|
|
200
|
+
*/
|
|
141
201
|
get data() {
|
|
142
202
|
return this._dataFormat;
|
|
143
203
|
}
|
|
144
204
|
|
|
205
|
+
/**
|
|
206
|
+
* The raw aggregated data for the group (typically {@link Decimal} instances).
|
|
207
|
+
*
|
|
208
|
+
* @public
|
|
209
|
+
* @returns {Object}
|
|
210
|
+
*/
|
|
145
211
|
get actual() {
|
|
146
212
|
return this._dataActual;
|
|
147
213
|
}
|
|
148
214
|
|
|
215
|
+
/**
|
|
216
|
+
* Indicates if the group will only contain one {@link PositionItem} -- that is,
|
|
217
|
+
* indicates if the group represents a single position.
|
|
218
|
+
*
|
|
219
|
+
* @public
|
|
220
|
+
* @returns {Boolean}
|
|
221
|
+
*/
|
|
149
222
|
get single() {
|
|
150
223
|
return this._single;
|
|
151
224
|
}
|
|
@@ -154,10 +227,22 @@ module.exports = (() => {
|
|
|
154
227
|
return this._suspended;
|
|
155
228
|
}
|
|
156
229
|
|
|
230
|
+
/**
|
|
231
|
+
* Indicates if the group should be excluded from higher-level aggregations.
|
|
232
|
+
*
|
|
233
|
+
* @public
|
|
234
|
+
* @returns {Boolean}
|
|
235
|
+
*/
|
|
157
236
|
get excluded() {
|
|
158
237
|
return this._excluded;
|
|
159
238
|
}
|
|
160
239
|
|
|
240
|
+
/**
|
|
241
|
+
* Causes aggregated data to be recalculated using a new exchange rate.
|
|
242
|
+
*
|
|
243
|
+
* @public
|
|
244
|
+
* @param {Rate} rate
|
|
245
|
+
*/
|
|
161
246
|
setForexRate(rate) {
|
|
162
247
|
if (!this._bypassCurrencyTranslation) {
|
|
163
248
|
this.refresh();
|
|
@@ -168,11 +253,7 @@ module.exports = (() => {
|
|
|
168
253
|
assert.argumentIsRequired(value, 'value', Boolean);
|
|
169
254
|
|
|
170
255
|
if (this._excluded !== value) {
|
|
171
|
-
this.
|
|
172
|
-
this._items.forEach((item) => {
|
|
173
|
-
item.setExcluded(value);
|
|
174
|
-
});
|
|
175
|
-
});
|
|
256
|
+
this._excludedChangeEvent(this._excluded = value);
|
|
176
257
|
}
|
|
177
258
|
}
|
|
178
259
|
|
|
@@ -186,6 +267,11 @@ module.exports = (() => {
|
|
|
186
267
|
}
|
|
187
268
|
}
|
|
188
269
|
|
|
270
|
+
/**
|
|
271
|
+
* Causes all aggregated data to be recalculated.
|
|
272
|
+
*
|
|
273
|
+
* @public
|
|
274
|
+
*/
|
|
189
275
|
refresh() {
|
|
190
276
|
const rates = this._container.getForexQuotes();
|
|
191
277
|
|
|
@@ -193,6 +279,12 @@ module.exports = (() => {
|
|
|
193
279
|
calculatePriceData(this, rates, null, true);
|
|
194
280
|
}
|
|
195
281
|
|
|
282
|
+
/**
|
|
283
|
+
* Causes the percent of the position, with respect to the parent container's
|
|
284
|
+
* total, to be recalculated.
|
|
285
|
+
*
|
|
286
|
+
* @public
|
|
287
|
+
*/
|
|
196
288
|
refreshMarketPercent() {
|
|
197
289
|
calculateMarketPercent(this, this._container.getForexQuotes(), true);
|
|
198
290
|
}
|
|
@@ -298,8 +390,11 @@ module.exports = (() => {
|
|
|
298
390
|
if (group.single) {
|
|
299
391
|
const item = group._items[0];
|
|
300
392
|
|
|
301
|
-
|
|
302
|
-
|
|
393
|
+
actual.quantity = item.position.snapshot.open;
|
|
394
|
+
actual.basisPrice = item.data.basisPrice;
|
|
395
|
+
|
|
396
|
+
format.quantity = formatDecimal(actual.quantity, 2);
|
|
397
|
+
format.basisPrice = formatCurrency(actual.basisPrice, currency);
|
|
303
398
|
}
|
|
304
399
|
}
|
|
305
400
|
|
|
@@ -11,7 +11,15 @@ module.exports = (() => {
|
|
|
11
11
|
'use strict';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
+
* A container for a single position, which handles quote changes and
|
|
15
|
+
* notifies observers -- which are typically parent-level {@link PositionGroup}
|
|
16
|
+
* instances.
|
|
17
|
+
*
|
|
14
18
|
* @public
|
|
19
|
+
* @param {Object} portfolio
|
|
20
|
+
* @param {Object} position
|
|
21
|
+
* @param {Object} currentSummary
|
|
22
|
+
* @param {Array.<Object>} previousSummaries
|
|
15
23
|
*/
|
|
16
24
|
class PositionItem {
|
|
17
25
|
constructor(portfolio, position, currentSummary, previousSummaries) {
|
|
@@ -27,10 +35,12 @@ module.exports = (() => {
|
|
|
27
35
|
this._data.basis = null;
|
|
28
36
|
|
|
29
37
|
this._currentQuote = null;
|
|
30
|
-
|
|
38
|
+
|
|
39
|
+
this._currentPrice = null;
|
|
40
|
+
this._previousPrice = null;
|
|
31
41
|
|
|
32
42
|
this._data.currentPrice = null;
|
|
33
|
-
this._data.
|
|
43
|
+
this._data.currentPricePrevious = null;
|
|
34
44
|
|
|
35
45
|
this._data.market = null;
|
|
36
46
|
this._data.marketChange = null;
|
|
@@ -40,7 +50,7 @@ module.exports = (() => {
|
|
|
40
50
|
|
|
41
51
|
this._data.unrealized = null;
|
|
42
52
|
this._data.unrealizedChange = null;
|
|
43
|
-
|
|
53
|
+
|
|
44
54
|
this._data.summaryTotalCurrent = null;
|
|
45
55
|
this._data.summaryTotalCurrentChange = null;
|
|
46
56
|
|
|
@@ -50,74 +60,165 @@ module.exports = (() => {
|
|
|
50
60
|
this._data.income = null;
|
|
51
61
|
this._data.basisPrice = null;
|
|
52
62
|
|
|
53
|
-
this.
|
|
63
|
+
this._data.newsExists = false;
|
|
64
|
+
this._data.fundamental = { };
|
|
54
65
|
|
|
55
66
|
calculateStaticData(this);
|
|
56
67
|
calculatePriceData(this, null);
|
|
57
68
|
|
|
58
69
|
this._quoteChangedEvent = new Event(this);
|
|
59
|
-
this.
|
|
70
|
+
this._newsExistsChangedEvent = new Event(this);
|
|
71
|
+
this._fundamentalDataChangeEvent = new Event(this);
|
|
60
72
|
}
|
|
61
73
|
|
|
74
|
+
/**
|
|
75
|
+
* The portfolio of the encapsulated position.
|
|
76
|
+
*
|
|
77
|
+
* @public
|
|
78
|
+
* @returns {Object}
|
|
79
|
+
*/
|
|
62
80
|
get portfolio() {
|
|
63
81
|
return this._portfolio;
|
|
64
82
|
}
|
|
65
83
|
|
|
84
|
+
/**
|
|
85
|
+
* The encapsulated position.
|
|
86
|
+
*
|
|
87
|
+
* @public
|
|
88
|
+
* @returns {Object}
|
|
89
|
+
*/
|
|
66
90
|
get position() {
|
|
67
91
|
return this._position;
|
|
68
92
|
}
|
|
69
93
|
|
|
94
|
+
/**
|
|
95
|
+
* The {@link Currency} of the encapsulated position.
|
|
96
|
+
*
|
|
97
|
+
* @public
|
|
98
|
+
* @returns {Object}
|
|
99
|
+
*/
|
|
70
100
|
get currency() {
|
|
71
101
|
return this._currency;
|
|
72
102
|
}
|
|
73
103
|
|
|
104
|
+
/**
|
|
105
|
+
* The year-to-date position summary of the encapsulated position.
|
|
106
|
+
*
|
|
107
|
+
* @public
|
|
108
|
+
* @returns {Object}
|
|
109
|
+
*/
|
|
74
110
|
get currentSummary() {
|
|
75
111
|
return this._currentSummary;
|
|
76
112
|
}
|
|
77
|
-
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Previous year's summaries for the encapsulated position.
|
|
116
|
+
*
|
|
117
|
+
* @public
|
|
118
|
+
* @returns {Object}
|
|
119
|
+
*/
|
|
78
120
|
get previousSummaries() {
|
|
79
121
|
return this._previousSummaries;
|
|
80
122
|
}
|
|
81
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Various data regarding the encapsulated position.
|
|
126
|
+
*
|
|
127
|
+
* @public
|
|
128
|
+
* @returns {*}
|
|
129
|
+
*/
|
|
82
130
|
get data() {
|
|
83
131
|
return this._data;
|
|
84
132
|
}
|
|
85
133
|
|
|
134
|
+
/**
|
|
135
|
+
* The current quote for the symbol of the encapsulated position.
|
|
136
|
+
*
|
|
137
|
+
* @public
|
|
138
|
+
* @returns {null|{Object}}
|
|
139
|
+
*/
|
|
86
140
|
get quote() {
|
|
87
141
|
return this._currentQuote;
|
|
88
142
|
}
|
|
89
143
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
144
|
+
/**
|
|
145
|
+
* Sets the current quote -- causing position-level data (e.g. market value) to
|
|
146
|
+
* be recalculated.
|
|
147
|
+
*
|
|
148
|
+
* @public
|
|
149
|
+
* @param {Object} quote
|
|
150
|
+
*/
|
|
94
151
|
setQuote(quote) {
|
|
95
152
|
assert.argumentIsRequired(quote, 'quote', Object);
|
|
96
153
|
|
|
97
|
-
if (this.
|
|
154
|
+
if (this._currentPricePrevious !== quote.lastPrice) {
|
|
98
155
|
calculatePriceData(this, quote.lastPrice);
|
|
99
156
|
|
|
100
|
-
this.
|
|
157
|
+
this._currentPricePrevious = this._currentPrice;
|
|
158
|
+
this._currentPrice = quote.lastPrice;
|
|
159
|
+
|
|
101
160
|
this._currentQuote = quote;
|
|
102
161
|
|
|
103
162
|
this._quoteChangedEvent.fire(this._currentQuote);
|
|
104
163
|
}
|
|
105
164
|
}
|
|
106
165
|
|
|
107
|
-
|
|
166
|
+
/**
|
|
167
|
+
* Sets fundamental data for the position.
|
|
168
|
+
*
|
|
169
|
+
* @public
|
|
170
|
+
* @param {Object} data
|
|
171
|
+
*/
|
|
172
|
+
setPositionFundamentalData(data) {
|
|
173
|
+
assert.argumentIsRequired(data, 'data', Object);
|
|
174
|
+
|
|
175
|
+
this._fundamentalDataChangeEvent(this._data.fundamental = data);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Sets a flag which indicates if news article(s) exist for the encapsulated position's
|
|
180
|
+
* symbol.
|
|
181
|
+
*
|
|
182
|
+
* @public
|
|
183
|
+
* @param {Boolean} value
|
|
184
|
+
*/
|
|
185
|
+
setNewsArticleExists(value) {
|
|
108
186
|
assert.argumentIsRequired(value, 'value', Boolean);
|
|
109
187
|
|
|
110
|
-
if (this.
|
|
111
|
-
this.
|
|
188
|
+
if (this._data.newsExists !== value) {
|
|
189
|
+
this._newsExistsChangedEvent.fire(this._data.newsExists = value);
|
|
112
190
|
}
|
|
113
191
|
}
|
|
114
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Registers an observer for quote changes, which is fired after internal recalculations
|
|
195
|
+
* of position data are complete.
|
|
196
|
+
*
|
|
197
|
+
* @public
|
|
198
|
+
* @param {Function} handler
|
|
199
|
+
*/
|
|
115
200
|
registerQuoteChangeHandler(handler) {
|
|
116
201
|
this._quoteChangedEvent.register(handler);
|
|
117
202
|
}
|
|
118
203
|
|
|
119
|
-
|
|
120
|
-
|
|
204
|
+
/**
|
|
205
|
+
* Registers an observer for fundamental data changes.
|
|
206
|
+
*
|
|
207
|
+
* @public
|
|
208
|
+
* @param {Function} handler
|
|
209
|
+
*/
|
|
210
|
+
registerFundamentalDataChangeHandler(handler) {
|
|
211
|
+
this._fundamentalDataChangeEvent.register(handler);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Registers an observer changes to the status of news existence.
|
|
216
|
+
*
|
|
217
|
+
* @public
|
|
218
|
+
* @param {Function} handler
|
|
219
|
+
*/
|
|
220
|
+
registerNewsExistsChangeHandler(handler) {
|
|
221
|
+
this._newsExistsChangedEvent.register(handler);
|
|
121
222
|
}
|
|
122
223
|
|
|
123
224
|
toString() {
|
|
@@ -244,10 +345,10 @@ module.exports = (() => {
|
|
|
244
345
|
data.unrealizedChange = Decimal.ZERO;
|
|
245
346
|
}
|
|
246
347
|
}
|
|
247
|
-
|
|
348
|
+
|
|
248
349
|
function calculateSummaryTotal(summary) {
|
|
249
350
|
let returnRef;
|
|
250
|
-
|
|
351
|
+
|
|
251
352
|
if (summary) {
|
|
252
353
|
const period = summary.period;
|
|
253
354
|
|
|
@@ -255,7 +356,7 @@ module.exports = (() => {
|
|
|
255
356
|
} else {
|
|
256
357
|
returnRef = Decimal.ZERO;
|
|
257
358
|
}
|
|
258
|
-
|
|
359
|
+
|
|
259
360
|
return returnRef;
|
|
260
361
|
}
|
|
261
362
|
|
package/package.json
CHANGED
package/test/SpecRunner.js
CHANGED
|
@@ -738,11 +738,11 @@ module.exports = (() => {
|
|
|
738
738
|
|
|
739
739
|
/**
|
|
740
740
|
* A container for positions which groups the positions into one or more
|
|
741
|
-
* trees for aggregation and display purposes. For example,
|
|
742
|
-
* grouped first by asset class then by position
|
|
741
|
+
* trees for aggregation and display purposes. For example, positions could be
|
|
742
|
+
* grouped first by asset class then by position.
|
|
743
743
|
*
|
|
744
744
|
* Furthermore, the container performs aggregation (driven primarily by price
|
|
745
|
-
* changes) for each level of grouping
|
|
745
|
+
* changes) for each level of grouping.
|
|
746
746
|
*
|
|
747
747
|
* @public
|
|
748
748
|
* @param {Array.<PositionTreeDefinition>} definitions
|
|
@@ -824,6 +824,20 @@ module.exports = (() => {
|
|
|
824
824
|
return map;
|
|
825
825
|
}, { });
|
|
826
826
|
|
|
827
|
+
this._symbolsDisplay = this._items.reduce((map, item) => {
|
|
828
|
+
const symbol = extractSymbolForDisplay(item.position);
|
|
829
|
+
|
|
830
|
+
if (symbol) {
|
|
831
|
+
if (!map.hasOwnProperty(symbol)) {
|
|
832
|
+
map[symbol] = [ ];
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
map[symbol].push(item);
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
return map;
|
|
839
|
+
}, { });
|
|
840
|
+
|
|
827
841
|
this._currencies = this._items.reduce((map, item) => {
|
|
828
842
|
const position = item.position;
|
|
829
843
|
|
|
@@ -935,10 +949,14 @@ module.exports = (() => {
|
|
|
935
949
|
}, { });
|
|
936
950
|
}
|
|
937
951
|
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
952
|
+
/**
|
|
953
|
+
* Returns a distinct list of all symbols used by the positions
|
|
954
|
+
* within the container.
|
|
955
|
+
*
|
|
956
|
+
* @public
|
|
957
|
+
* @param {Boolean} display - If true, all "display" symbols are returned; otherwise Barchart symbols are returned.
|
|
958
|
+
* @returns {Array.<String>}
|
|
959
|
+
*/
|
|
942
960
|
getPositionSymbols(display) {
|
|
943
961
|
const symbols = this._items.reduce((symbols, item) => {
|
|
944
962
|
const position = item.position;
|
|
@@ -961,6 +979,14 @@ module.exports = (() => {
|
|
|
961
979
|
return array.unique(symbols);
|
|
962
980
|
}
|
|
963
981
|
|
|
982
|
+
/**
|
|
983
|
+
* Updates the quote for a single symbol; causing updates to any grouping
|
|
984
|
+
* level that contains the position(s) for the symbol.
|
|
985
|
+
*
|
|
986
|
+
* @public
|
|
987
|
+
* @param {String} symbol
|
|
988
|
+
* @param {Object} quote
|
|
989
|
+
*/
|
|
964
990
|
setPositionQuote(symbol, quote) {
|
|
965
991
|
assert.argumentIsRequired(symbol, 'symbol', String);
|
|
966
992
|
assert.argumentIsRequired(quote, 'quote', Object);
|
|
@@ -970,14 +996,34 @@ module.exports = (() => {
|
|
|
970
996
|
}
|
|
971
997
|
}
|
|
972
998
|
|
|
999
|
+
/**
|
|
1000
|
+
* Returns all forex symbols that are required to do currency translations.
|
|
1001
|
+
*
|
|
1002
|
+
* @public
|
|
1003
|
+
* @returns {Array.<String>}
|
|
1004
|
+
*/
|
|
973
1005
|
getForexSymbols() {
|
|
974
1006
|
return this._forexSymbols;
|
|
975
1007
|
}
|
|
976
1008
|
|
|
1009
|
+
/**
|
|
1010
|
+
* Returns all current forex quotes.
|
|
1011
|
+
*
|
|
1012
|
+
* @public
|
|
1013
|
+
* @returns {Array.<Object>}
|
|
1014
|
+
*/
|
|
977
1015
|
getForexQuotes() {
|
|
978
1016
|
return this._forexQuotes;
|
|
979
1017
|
}
|
|
980
1018
|
|
|
1019
|
+
/**
|
|
1020
|
+
* Updates the forex quote for a single currency pair; causing updates to
|
|
1021
|
+
* any grouping level that contains that requires translation.
|
|
1022
|
+
*
|
|
1023
|
+
* @public
|
|
1024
|
+
* @param {String} symbol
|
|
1025
|
+
* @param {Object} quote
|
|
1026
|
+
*/
|
|
981
1027
|
setForexQuote(symbol, quote) {
|
|
982
1028
|
assert.argumentIsRequired(symbol, 'symbol', String);
|
|
983
1029
|
assert.argumentIsRequired(quote, 'quote', Object);
|
|
@@ -995,10 +1041,55 @@ module.exports = (() => {
|
|
|
995
1041
|
Object.keys(this._trees).forEach(key => this._trees[key].walk(group => group.setForexRate(rate), true, false));
|
|
996
1042
|
}
|
|
997
1043
|
|
|
998
|
-
|
|
999
|
-
|
|
1044
|
+
/**
|
|
1045
|
+
* Updates fundamental data for a single symbol.
|
|
1046
|
+
*
|
|
1047
|
+
* @public
|
|
1048
|
+
* @param {String} symbol
|
|
1049
|
+
* @param {Object} data
|
|
1050
|
+
*/
|
|
1051
|
+
setPositionFundamentalData(symbol, data) {
|
|
1052
|
+
assert.argumentIsRequired(symbol, 'symbol', String);
|
|
1053
|
+
assert.argumentIsRequired(data, 'data', Object);
|
|
1054
|
+
|
|
1055
|
+
if (this._symbols.hasOwnProperty(symbol)) {
|
|
1056
|
+
this._symbols[symbol].forEach(item => item.setPositionFundamentalData(data));
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
/**
|
|
1061
|
+
* Indicates if a news article exists for a symbol.
|
|
1062
|
+
*
|
|
1063
|
+
* @public
|
|
1064
|
+
* @param {String} symbol
|
|
1065
|
+
* @param {Boolean} display
|
|
1066
|
+
* @param {Boolean} exists
|
|
1067
|
+
*/
|
|
1068
|
+
setNewsArticleExists(symbol, display, exists) {
|
|
1069
|
+
assert.argumentIsRequired(symbol, 'symbol', String);
|
|
1070
|
+
assert.argumentIsRequired(display, 'display', Boolean);
|
|
1071
|
+
assert.argumentIsRequired(exists, 'exists', Boolean);
|
|
1072
|
+
|
|
1073
|
+
let map;
|
|
1074
|
+
|
|
1075
|
+
if (display) {
|
|
1076
|
+
map = this._symbols;
|
|
1077
|
+
} else {
|
|
1078
|
+
map = this._symbolsDisplay;
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
if (map.hasOwnProperty(symbol)) {
|
|
1082
|
+
map[symbol].forEach(item => item.setNewsArticleExists(exists));
|
|
1083
|
+
}
|
|
1000
1084
|
}
|
|
1001
1085
|
|
|
1086
|
+
/**
|
|
1087
|
+
* Returns a single level of grouping from one of the internal trees.
|
|
1088
|
+
*
|
|
1089
|
+
* @param {String} name
|
|
1090
|
+
* @param {Array.<String> keys
|
|
1091
|
+
* @returns {PositionGroup}
|
|
1092
|
+
*/
|
|
1002
1093
|
getGroup(name, keys) {
|
|
1003
1094
|
assert.argumentIsRequired(name, 'name', String);
|
|
1004
1095
|
assert.argumentIsArray(keys, 'keys', Number);
|
|
@@ -1006,6 +1097,14 @@ module.exports = (() => {
|
|
|
1006
1097
|
return findNode(this._trees[name], keys).getValue();
|
|
1007
1098
|
}
|
|
1008
1099
|
|
|
1100
|
+
/**
|
|
1101
|
+
* Returns all child groups from a level of grouping within one of
|
|
1102
|
+
* the internal trees.
|
|
1103
|
+
*
|
|
1104
|
+
* @param {String} name
|
|
1105
|
+
* @param {Array.<String> keys
|
|
1106
|
+
* @returns {Array.<PositionGroup>}
|
|
1107
|
+
*/
|
|
1009
1108
|
getGroups(name, keys) {
|
|
1010
1109
|
assert.argumentIsRequired(name, 'name', String);
|
|
1011
1110
|
assert.argumentIsArray(keys, 'keys', Number);
|
|
@@ -1017,8 +1116,6 @@ module.exports = (() => {
|
|
|
1017
1116
|
assert.argumentIsRequired(name, 'name', String);
|
|
1018
1117
|
assert.argumentIsRequired(executor, 'executor', Function);
|
|
1019
1118
|
|
|
1020
|
-
assert.argumentIsRequired(executor, 'executor', Function);
|
|
1021
|
-
|
|
1022
1119
|
this._trees[name].walk(group => group.setSuspended(true), false, false);
|
|
1023
1120
|
|
|
1024
1121
|
executor(this);
|
|
@@ -1072,7 +1169,17 @@ module.exports = (() => {
|
|
|
1072
1169
|
'use strict';
|
|
1073
1170
|
|
|
1074
1171
|
/**
|
|
1172
|
+
* A grouping of {@link PositionItem} instances. The group aggregates from across
|
|
1173
|
+
* all the positions and performs currency translation, as necessary.
|
|
1174
|
+
*
|
|
1075
1175
|
* @public
|
|
1176
|
+
* @param {PositionContainer} container
|
|
1177
|
+
* @param {PositionGroup|null} parent
|
|
1178
|
+
* @param {Array.<PositionItem>} items
|
|
1179
|
+
* @param {Currency} currency
|
|
1180
|
+
* @param {String} key
|
|
1181
|
+
* @param {String} description
|
|
1182
|
+
* @param {Boolean=} single
|
|
1076
1183
|
*/
|
|
1077
1184
|
class PositionGroup {
|
|
1078
1185
|
constructor(container, parent, items, currency, key, description, single) {
|
|
@@ -1092,15 +1199,22 @@ module.exports = (() => {
|
|
|
1092
1199
|
this._suspended = false;
|
|
1093
1200
|
|
|
1094
1201
|
this._marketPercentChangeEvent = new Event(this);
|
|
1202
|
+
this._excludedChangeEvent = new Event(this);
|
|
1095
1203
|
|
|
1096
1204
|
this._dataFormat = { };
|
|
1097
1205
|
this._dataActual = { };
|
|
1098
1206
|
|
|
1099
1207
|
this._dataFormat.key = this._key;
|
|
1100
1208
|
this._dataFormat.description = this._description;
|
|
1101
|
-
this._dataFormat.
|
|
1102
|
-
|
|
1209
|
+
this._dataFormat.newsExists = false;
|
|
1103
1210
|
this._dataFormat.quantity = null;
|
|
1211
|
+
this._dataFormat.basisPrice = null;
|
|
1212
|
+
|
|
1213
|
+
this._dataActual.key = this._key;
|
|
1214
|
+
this._dataActual.description = this._description;
|
|
1215
|
+
this._dataActual.newsExists = false;
|
|
1216
|
+
this._dataActual.quantity = null;
|
|
1217
|
+
this._dataActual.basisPrice = null;
|
|
1104
1218
|
|
|
1105
1219
|
if (this._single) {
|
|
1106
1220
|
const item = items[0];
|
|
@@ -1108,10 +1222,12 @@ module.exports = (() => {
|
|
|
1108
1222
|
this._dataFormat.portfolio = item.portfolio.portfolio;
|
|
1109
1223
|
this._dataFormat.position = item.position.position;
|
|
1110
1224
|
this._dataFormat.instrument = item.position.instrument;
|
|
1225
|
+
this._dataFormat.fundamental = item.fundamental || { };
|
|
1111
1226
|
} else {
|
|
1112
1227
|
this._dataFormat.portfolio = null;
|
|
1113
1228
|
this._dataFormat.position = null;
|
|
1114
1229
|
this._dataFormat.instrument = null;
|
|
1230
|
+
this._dataFormat.fundamental = { };
|
|
1115
1231
|
}
|
|
1116
1232
|
|
|
1117
1233
|
this._dataFormat.quoteLast = null;
|
|
@@ -1179,35 +1295,89 @@ module.exports = (() => {
|
|
|
1179
1295
|
|
|
1180
1296
|
calculatePriceData(this, this._container.getForexQuotes(), sender, false);
|
|
1181
1297
|
});
|
|
1298
|
+
|
|
1299
|
+
if (this._single) {
|
|
1300
|
+
item.registerNewsExistsChangeHandler((exists, sender) => {
|
|
1301
|
+
this._dataActual.newsExists = exists;
|
|
1302
|
+
this._dataFormat.newsExists = exists;
|
|
1303
|
+
});
|
|
1304
|
+
|
|
1305
|
+
item._fundamentalDataChangeEvent((data, sender) => {
|
|
1306
|
+
this._dataFormat.fundamental = data;
|
|
1307
|
+
});
|
|
1308
|
+
}
|
|
1182
1309
|
});
|
|
1183
1310
|
|
|
1184
1311
|
this.refresh();
|
|
1185
1312
|
}
|
|
1186
1313
|
|
|
1314
|
+
/**
|
|
1315
|
+
* The key of the group.
|
|
1316
|
+
*
|
|
1317
|
+
* @public
|
|
1318
|
+
* @returns {String}
|
|
1319
|
+
*/
|
|
1187
1320
|
get key() {
|
|
1188
1321
|
return this._key;
|
|
1189
1322
|
}
|
|
1190
1323
|
|
|
1324
|
+
/**
|
|
1325
|
+
* The description of the group.
|
|
1326
|
+
*
|
|
1327
|
+
* @public
|
|
1328
|
+
* @returns {String}
|
|
1329
|
+
*/
|
|
1191
1330
|
get description() {
|
|
1192
1331
|
return this._description;
|
|
1193
1332
|
}
|
|
1194
1333
|
|
|
1334
|
+
/**
|
|
1335
|
+
* The {@link Currency} which all aggregated data is presented in.
|
|
1336
|
+
*
|
|
1337
|
+
* @public
|
|
1338
|
+
* @returns {Currency}
|
|
1339
|
+
*/
|
|
1195
1340
|
get currency() {
|
|
1196
1341
|
return this._currency;
|
|
1197
1342
|
}
|
|
1198
1343
|
|
|
1344
|
+
/**
|
|
1345
|
+
* The {@link PositionItem} instances which for which aggregated data is compiled.
|
|
1346
|
+
*
|
|
1347
|
+
* @public
|
|
1348
|
+
* @returns {Currency}
|
|
1349
|
+
*/
|
|
1199
1350
|
get items() {
|
|
1200
1351
|
return this._items;
|
|
1201
1352
|
}
|
|
1202
1353
|
|
|
1354
|
+
/**
|
|
1355
|
+
* The string-based, human-readable aggregated data for the group.
|
|
1356
|
+
*
|
|
1357
|
+
* @public
|
|
1358
|
+
* @returns {Object}
|
|
1359
|
+
*/
|
|
1203
1360
|
get data() {
|
|
1204
1361
|
return this._dataFormat;
|
|
1205
1362
|
}
|
|
1206
1363
|
|
|
1364
|
+
/**
|
|
1365
|
+
* The raw aggregated data for the group (typically {@link Decimal} instances).
|
|
1366
|
+
*
|
|
1367
|
+
* @public
|
|
1368
|
+
* @returns {Object}
|
|
1369
|
+
*/
|
|
1207
1370
|
get actual() {
|
|
1208
1371
|
return this._dataActual;
|
|
1209
1372
|
}
|
|
1210
1373
|
|
|
1374
|
+
/**
|
|
1375
|
+
* Indicates if the group will only contain one {@link PositionItem} -- that is,
|
|
1376
|
+
* indicates if the group represents a single position.
|
|
1377
|
+
*
|
|
1378
|
+
* @public
|
|
1379
|
+
* @returns {Boolean}
|
|
1380
|
+
*/
|
|
1211
1381
|
get single() {
|
|
1212
1382
|
return this._single;
|
|
1213
1383
|
}
|
|
@@ -1216,10 +1386,22 @@ module.exports = (() => {
|
|
|
1216
1386
|
return this._suspended;
|
|
1217
1387
|
}
|
|
1218
1388
|
|
|
1389
|
+
/**
|
|
1390
|
+
* Indicates if the group should be excluded from higher-level aggregations.
|
|
1391
|
+
*
|
|
1392
|
+
* @public
|
|
1393
|
+
* @returns {Boolean}
|
|
1394
|
+
*/
|
|
1219
1395
|
get excluded() {
|
|
1220
1396
|
return this._excluded;
|
|
1221
1397
|
}
|
|
1222
1398
|
|
|
1399
|
+
/**
|
|
1400
|
+
* Causes aggregated data to be recalculated using a new exchange rate.
|
|
1401
|
+
*
|
|
1402
|
+
* @public
|
|
1403
|
+
* @param {Rate} rate
|
|
1404
|
+
*/
|
|
1223
1405
|
setForexRate(rate) {
|
|
1224
1406
|
if (!this._bypassCurrencyTranslation) {
|
|
1225
1407
|
this.refresh();
|
|
@@ -1230,11 +1412,7 @@ module.exports = (() => {
|
|
|
1230
1412
|
assert.argumentIsRequired(value, 'value', Boolean);
|
|
1231
1413
|
|
|
1232
1414
|
if (this._excluded !== value) {
|
|
1233
|
-
this.
|
|
1234
|
-
this._items.forEach((item) => {
|
|
1235
|
-
item.setExcluded(value);
|
|
1236
|
-
});
|
|
1237
|
-
});
|
|
1415
|
+
this._excludedChangeEvent(this._excluded = value);
|
|
1238
1416
|
}
|
|
1239
1417
|
}
|
|
1240
1418
|
|
|
@@ -1248,6 +1426,11 @@ module.exports = (() => {
|
|
|
1248
1426
|
}
|
|
1249
1427
|
}
|
|
1250
1428
|
|
|
1429
|
+
/**
|
|
1430
|
+
* Causes all aggregated data to be recalculated.
|
|
1431
|
+
*
|
|
1432
|
+
* @public
|
|
1433
|
+
*/
|
|
1251
1434
|
refresh() {
|
|
1252
1435
|
const rates = this._container.getForexQuotes();
|
|
1253
1436
|
|
|
@@ -1255,6 +1438,12 @@ module.exports = (() => {
|
|
|
1255
1438
|
calculatePriceData(this, rates, null, true);
|
|
1256
1439
|
}
|
|
1257
1440
|
|
|
1441
|
+
/**
|
|
1442
|
+
* Causes the percent of the position, with respect to the parent container's
|
|
1443
|
+
* total, to be recalculated.
|
|
1444
|
+
*
|
|
1445
|
+
* @public
|
|
1446
|
+
*/
|
|
1258
1447
|
refreshMarketPercent() {
|
|
1259
1448
|
calculateMarketPercent(this, this._container.getForexQuotes(), true);
|
|
1260
1449
|
}
|
|
@@ -1360,8 +1549,11 @@ module.exports = (() => {
|
|
|
1360
1549
|
if (group.single) {
|
|
1361
1550
|
const item = group._items[0];
|
|
1362
1551
|
|
|
1363
|
-
|
|
1364
|
-
|
|
1552
|
+
actual.quantity = item.position.snapshot.open;
|
|
1553
|
+
actual.basisPrice = item.data.basisPrice;
|
|
1554
|
+
|
|
1555
|
+
format.quantity = formatDecimal(actual.quantity, 2);
|
|
1556
|
+
format.basisPrice = formatCurrency(actual.basisPrice, currency);
|
|
1365
1557
|
}
|
|
1366
1558
|
}
|
|
1367
1559
|
|
|
@@ -1535,7 +1727,15 @@ module.exports = (() => {
|
|
|
1535
1727
|
'use strict';
|
|
1536
1728
|
|
|
1537
1729
|
/**
|
|
1730
|
+
* A container for a single position, which handles quote changes and
|
|
1731
|
+
* notifies observers -- which are typically parent-level {@link PositionGroup}
|
|
1732
|
+
* instances.
|
|
1733
|
+
*
|
|
1538
1734
|
* @public
|
|
1735
|
+
* @param {Object} portfolio
|
|
1736
|
+
* @param {Object} position
|
|
1737
|
+
* @param {Object} currentSummary
|
|
1738
|
+
* @param {Array.<Object>} previousSummaries
|
|
1539
1739
|
*/
|
|
1540
1740
|
class PositionItem {
|
|
1541
1741
|
constructor(portfolio, position, currentSummary, previousSummaries) {
|
|
@@ -1551,10 +1751,12 @@ module.exports = (() => {
|
|
|
1551
1751
|
this._data.basis = null;
|
|
1552
1752
|
|
|
1553
1753
|
this._currentQuote = null;
|
|
1554
|
-
|
|
1754
|
+
|
|
1755
|
+
this._currentPrice = null;
|
|
1756
|
+
this._previousPrice = null;
|
|
1555
1757
|
|
|
1556
1758
|
this._data.currentPrice = null;
|
|
1557
|
-
this._data.
|
|
1759
|
+
this._data.currentPricePrevious = null;
|
|
1558
1760
|
|
|
1559
1761
|
this._data.market = null;
|
|
1560
1762
|
this._data.marketChange = null;
|
|
@@ -1564,7 +1766,7 @@ module.exports = (() => {
|
|
|
1564
1766
|
|
|
1565
1767
|
this._data.unrealized = null;
|
|
1566
1768
|
this._data.unrealizedChange = null;
|
|
1567
|
-
|
|
1769
|
+
|
|
1568
1770
|
this._data.summaryTotalCurrent = null;
|
|
1569
1771
|
this._data.summaryTotalCurrentChange = null;
|
|
1570
1772
|
|
|
@@ -1574,74 +1776,165 @@ module.exports = (() => {
|
|
|
1574
1776
|
this._data.income = null;
|
|
1575
1777
|
this._data.basisPrice = null;
|
|
1576
1778
|
|
|
1577
|
-
this.
|
|
1779
|
+
this._data.newsExists = false;
|
|
1780
|
+
this._data.fundamental = { };
|
|
1578
1781
|
|
|
1579
1782
|
calculateStaticData(this);
|
|
1580
1783
|
calculatePriceData(this, null);
|
|
1581
1784
|
|
|
1582
1785
|
this._quoteChangedEvent = new Event(this);
|
|
1583
|
-
this.
|
|
1786
|
+
this._newsExistsChangedEvent = new Event(this);
|
|
1787
|
+
this._fundamentalDataChangeEvent = new Event(this);
|
|
1584
1788
|
}
|
|
1585
1789
|
|
|
1790
|
+
/**
|
|
1791
|
+
* The portfolio of the encapsulated position.
|
|
1792
|
+
*
|
|
1793
|
+
* @public
|
|
1794
|
+
* @returns {Object}
|
|
1795
|
+
*/
|
|
1586
1796
|
get portfolio() {
|
|
1587
1797
|
return this._portfolio;
|
|
1588
1798
|
}
|
|
1589
1799
|
|
|
1800
|
+
/**
|
|
1801
|
+
* The encapsulated position.
|
|
1802
|
+
*
|
|
1803
|
+
* @public
|
|
1804
|
+
* @returns {Object}
|
|
1805
|
+
*/
|
|
1590
1806
|
get position() {
|
|
1591
1807
|
return this._position;
|
|
1592
1808
|
}
|
|
1593
1809
|
|
|
1810
|
+
/**
|
|
1811
|
+
* The {@link Currency} of the encapsulated position.
|
|
1812
|
+
*
|
|
1813
|
+
* @public
|
|
1814
|
+
* @returns {Object}
|
|
1815
|
+
*/
|
|
1594
1816
|
get currency() {
|
|
1595
1817
|
return this._currency;
|
|
1596
1818
|
}
|
|
1597
1819
|
|
|
1820
|
+
/**
|
|
1821
|
+
* The year-to-date position summary of the encapsulated position.
|
|
1822
|
+
*
|
|
1823
|
+
* @public
|
|
1824
|
+
* @returns {Object}
|
|
1825
|
+
*/
|
|
1598
1826
|
get currentSummary() {
|
|
1599
1827
|
return this._currentSummary;
|
|
1600
1828
|
}
|
|
1601
|
-
|
|
1829
|
+
|
|
1830
|
+
/**
|
|
1831
|
+
* Previous year's summaries for the encapsulated position.
|
|
1832
|
+
*
|
|
1833
|
+
* @public
|
|
1834
|
+
* @returns {Object}
|
|
1835
|
+
*/
|
|
1602
1836
|
get previousSummaries() {
|
|
1603
1837
|
return this._previousSummaries;
|
|
1604
1838
|
}
|
|
1605
1839
|
|
|
1840
|
+
/**
|
|
1841
|
+
* Various data regarding the encapsulated position.
|
|
1842
|
+
*
|
|
1843
|
+
* @public
|
|
1844
|
+
* @returns {*}
|
|
1845
|
+
*/
|
|
1606
1846
|
get data() {
|
|
1607
1847
|
return this._data;
|
|
1608
1848
|
}
|
|
1609
1849
|
|
|
1850
|
+
/**
|
|
1851
|
+
* The current quote for the symbol of the encapsulated position.
|
|
1852
|
+
*
|
|
1853
|
+
* @public
|
|
1854
|
+
* @returns {null|{Object}}
|
|
1855
|
+
*/
|
|
1610
1856
|
get quote() {
|
|
1611
1857
|
return this._currentQuote;
|
|
1612
1858
|
}
|
|
1613
1859
|
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1860
|
+
/**
|
|
1861
|
+
* Sets the current quote -- causing position-level data (e.g. market value) to
|
|
1862
|
+
* be recalculated.
|
|
1863
|
+
*
|
|
1864
|
+
* @public
|
|
1865
|
+
* @param {Object} quote
|
|
1866
|
+
*/
|
|
1618
1867
|
setQuote(quote) {
|
|
1619
1868
|
assert.argumentIsRequired(quote, 'quote', Object);
|
|
1620
1869
|
|
|
1621
|
-
if (this.
|
|
1870
|
+
if (this._currentPricePrevious !== quote.lastPrice) {
|
|
1622
1871
|
calculatePriceData(this, quote.lastPrice);
|
|
1623
1872
|
|
|
1624
|
-
this.
|
|
1873
|
+
this._currentPricePrevious = this._currentPrice;
|
|
1874
|
+
this._currentPrice = quote.lastPrice;
|
|
1875
|
+
|
|
1625
1876
|
this._currentQuote = quote;
|
|
1626
1877
|
|
|
1627
1878
|
this._quoteChangedEvent.fire(this._currentQuote);
|
|
1628
1879
|
}
|
|
1629
1880
|
}
|
|
1630
1881
|
|
|
1631
|
-
|
|
1882
|
+
/**
|
|
1883
|
+
* Sets fundamental data for the position.
|
|
1884
|
+
*
|
|
1885
|
+
* @public
|
|
1886
|
+
* @param {Object} data
|
|
1887
|
+
*/
|
|
1888
|
+
setPositionFundamentalData(data) {
|
|
1889
|
+
assert.argumentIsRequired(data, 'data', Object);
|
|
1890
|
+
|
|
1891
|
+
this._fundamentalDataChangeEvent(this._data.fundamental = data);
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1894
|
+
/**
|
|
1895
|
+
* Sets a flag which indicates if news article(s) exist for the encapsulated position's
|
|
1896
|
+
* symbol.
|
|
1897
|
+
*
|
|
1898
|
+
* @public
|
|
1899
|
+
* @param {Boolean} value
|
|
1900
|
+
*/
|
|
1901
|
+
setNewsArticleExists(value) {
|
|
1632
1902
|
assert.argumentIsRequired(value, 'value', Boolean);
|
|
1633
1903
|
|
|
1634
|
-
if (this.
|
|
1635
|
-
this.
|
|
1904
|
+
if (this._data.newsExists !== value) {
|
|
1905
|
+
this._newsExistsChangedEvent.fire(this._data.newsExists = value);
|
|
1636
1906
|
}
|
|
1637
1907
|
}
|
|
1638
1908
|
|
|
1909
|
+
/**
|
|
1910
|
+
* Registers an observer for quote changes, which is fired after internal recalculations
|
|
1911
|
+
* of position data are complete.
|
|
1912
|
+
*
|
|
1913
|
+
* @public
|
|
1914
|
+
* @param {Function} handler
|
|
1915
|
+
*/
|
|
1639
1916
|
registerQuoteChangeHandler(handler) {
|
|
1640
1917
|
this._quoteChangedEvent.register(handler);
|
|
1641
1918
|
}
|
|
1642
1919
|
|
|
1643
|
-
|
|
1644
|
-
|
|
1920
|
+
/**
|
|
1921
|
+
* Registers an observer for fundamental data changes.
|
|
1922
|
+
*
|
|
1923
|
+
* @public
|
|
1924
|
+
* @param {Function} handler
|
|
1925
|
+
*/
|
|
1926
|
+
registerFundamentalDataChangeHandler(handler) {
|
|
1927
|
+
this._fundamentalDataChangeEvent.register(handler);
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
/**
|
|
1931
|
+
* Registers an observer changes to the status of news existence.
|
|
1932
|
+
*
|
|
1933
|
+
* @public
|
|
1934
|
+
* @param {Function} handler
|
|
1935
|
+
*/
|
|
1936
|
+
registerNewsExistsChangeHandler(handler) {
|
|
1937
|
+
this._newsExistsChangedEvent.register(handler);
|
|
1645
1938
|
}
|
|
1646
1939
|
|
|
1647
1940
|
toString() {
|
|
@@ -1768,10 +2061,10 @@ module.exports = (() => {
|
|
|
1768
2061
|
data.unrealizedChange = Decimal.ZERO;
|
|
1769
2062
|
}
|
|
1770
2063
|
}
|
|
1771
|
-
|
|
2064
|
+
|
|
1772
2065
|
function calculateSummaryTotal(summary) {
|
|
1773
2066
|
let returnRef;
|
|
1774
|
-
|
|
2067
|
+
|
|
1775
2068
|
if (summary) {
|
|
1776
2069
|
const period = summary.period;
|
|
1777
2070
|
|
|
@@ -1779,7 +2072,7 @@ module.exports = (() => {
|
|
|
1779
2072
|
} else {
|
|
1780
2073
|
returnRef = Decimal.ZERO;
|
|
1781
2074
|
}
|
|
1782
|
-
|
|
2075
|
+
|
|
1783
2076
|
return returnRef;
|
|
1784
2077
|
}
|
|
1785
2078
|
|