@barchart/portfolio-api-common 7.0.0 → 7.0.1
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/data/FilterMode.js +67 -0
- package/lib/data/OptionsValuationType.js +61 -0
- package/lib/data/SnapTradeLinkStatus.js +72 -0
- package/lib/data/TransactionValidator.js +1 -0
- package/lib/data/ValuationType.js +1 -1
- package/lib/formatters/TransactionFormatter.js +28 -14
- package/lib/processing/PositionContainer.js +66 -36
- package/lib/processing/PositionGroup.js +87 -19
- package/lib/processing/PositionItem.js +17 -4
- package/lib/processing/definitions/PositionLevelDefinition.js +12 -0
- package/lib/processing/definitions/PositionLevelType.js +15 -2
- package/lib/serialization/PortfolioSchema.js +22 -6
- package/package.json +1 -1
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
const Enum = require('@barchart/common-js/lang/Enum');
|
|
2
|
+
|
|
3
|
+
module.exports = (() => {
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* An enumeration item that describes how positions should be filtered.
|
|
8
|
+
*
|
|
9
|
+
* @public
|
|
10
|
+
* @extends {Enum}
|
|
11
|
+
* @param {String} code
|
|
12
|
+
* @param {String} description
|
|
13
|
+
*/
|
|
14
|
+
class FilterMode extends Enum {
|
|
15
|
+
constructor(code, description) {
|
|
16
|
+
super(code, description);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Show open positions only.
|
|
21
|
+
*
|
|
22
|
+
* @returns {FilterMode}
|
|
23
|
+
*/
|
|
24
|
+
static get OPEN() {
|
|
25
|
+
return open;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Show closed positions only.
|
|
30
|
+
*
|
|
31
|
+
* @returns {FilterMode}
|
|
32
|
+
*/
|
|
33
|
+
static get CLOSED() {
|
|
34
|
+
return closed;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Show open and closed positions.
|
|
39
|
+
*
|
|
40
|
+
* @returns {FilterMode}
|
|
41
|
+
*/
|
|
42
|
+
static get ALL() {
|
|
43
|
+
return all;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Given a code, returns the enumeration item.
|
|
48
|
+
*
|
|
49
|
+
* @public
|
|
50
|
+
* @param {String} code
|
|
51
|
+
* @returns {FilterMode|null}
|
|
52
|
+
*/
|
|
53
|
+
static parse(code) {
|
|
54
|
+
return Enum.fromCode(FilterMode, code);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
toString() {
|
|
58
|
+
return `[FilterMode (code=${this.code})]`;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const open = new FilterMode('OPEN', 'Open only');
|
|
63
|
+
const closed = new FilterMode('CLOSED', 'Closed only');
|
|
64
|
+
const all = new FilterMode('ALL', 'Open and Closed');
|
|
65
|
+
|
|
66
|
+
return FilterMode;
|
|
67
|
+
})();
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const Enum = require('@barchart/common-js/lang/Enum');
|
|
2
|
+
|
|
3
|
+
module.exports = (() => {
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Defines the method used to value options in a portfolio.
|
|
8
|
+
*
|
|
9
|
+
* @public
|
|
10
|
+
* @extends {Enum}
|
|
11
|
+
* @param {String} code
|
|
12
|
+
* @param {String} description
|
|
13
|
+
*/
|
|
14
|
+
class OptionsValuationType extends Enum {
|
|
15
|
+
constructor(code, description) {
|
|
16
|
+
super(code, description);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Value based on the midpoint between Bid and Ask prices.
|
|
21
|
+
* Default behavior.
|
|
22
|
+
*
|
|
23
|
+
* @public
|
|
24
|
+
* @static
|
|
25
|
+
* @returns {OptionsValuationType}
|
|
26
|
+
*/
|
|
27
|
+
static get MIDPOINT() {
|
|
28
|
+
return MIDPOINT;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Value based on the Last Traded price.
|
|
33
|
+
*
|
|
34
|
+
* @public
|
|
35
|
+
* @static
|
|
36
|
+
* @returns {OptionsValuationType}
|
|
37
|
+
*/
|
|
38
|
+
static get LAST_TRADE() {
|
|
39
|
+
return LAST_TRADE;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @public
|
|
44
|
+
* @static
|
|
45
|
+
* @param {String} code
|
|
46
|
+
* @returns {OptionsValuationType|null}
|
|
47
|
+
*/
|
|
48
|
+
static parse(code) {
|
|
49
|
+
return Enum.fromCode(OptionsValuationType, code);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
toString() {
|
|
53
|
+
return `[OptionsValuationType (code=${this.code})]`;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const MIDPOINT = new OptionsValuationType('MIDPOINT', 'Bid/Ask Midpoint');
|
|
58
|
+
const LAST_TRADE = new OptionsValuationType('LAST_TRADE', 'Last Trade');
|
|
59
|
+
|
|
60
|
+
return OptionsValuationType;
|
|
61
|
+
})();
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const assert = require('@barchart/common-js/lang/assert'),
|
|
2
|
+
Enum = require('@barchart/common-js/lang/Enum');
|
|
3
|
+
|
|
4
|
+
module.exports = (() => {
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* An enumeration item that describes a strategy for calculating basis.
|
|
9
|
+
*
|
|
10
|
+
* @public
|
|
11
|
+
* @extends {Enum}
|
|
12
|
+
* @param {String} code
|
|
13
|
+
* @param {String} description
|
|
14
|
+
* @param {Boolean} processing
|
|
15
|
+
*/
|
|
16
|
+
class SnapTradeLinkStatus extends Enum {
|
|
17
|
+
constructor(code, description, processing) {
|
|
18
|
+
super(code, description);
|
|
19
|
+
|
|
20
|
+
assert.argumentIsRequired(processing, 'processing', Boolean);
|
|
21
|
+
|
|
22
|
+
this._processing = processing;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
get processing() {
|
|
26
|
+
return this._processing;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static get WAITING() {
|
|
30
|
+
return waiting;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static get LINKING() {
|
|
34
|
+
return linking;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static get LINKED() {
|
|
38
|
+
return linked;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static get REFRESHING() {
|
|
42
|
+
return refreshing;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static get FAILED() {
|
|
46
|
+
return failed;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Given a code, returns the enumeration item.
|
|
51
|
+
*
|
|
52
|
+
* @public
|
|
53
|
+
* @param {String} code
|
|
54
|
+
* @returns {SnapTradeLinkStatus|null}
|
|
55
|
+
*/
|
|
56
|
+
static parse(code) {
|
|
57
|
+
return Enum.fromCode(SnapTradeLinkStatus, code);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
toString() {
|
|
61
|
+
return `[SnapTradeLinkStatus (code=${this.code})]`;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const waiting = new SnapTradeLinkStatus('WAITING', 'Waiting', false);
|
|
66
|
+
const linking = new SnapTradeLinkStatus('LINKING', 'Linking', true);
|
|
67
|
+
const linked = new SnapTradeLinkStatus('LINKED', 'Linked', false);
|
|
68
|
+
const refreshing = new SnapTradeLinkStatus('REFRESHING', 'Refreshing', true);
|
|
69
|
+
const failed = new SnapTradeLinkStatus('FAILED', 'Failed', false);
|
|
70
|
+
|
|
71
|
+
return SnapTradeLinkStatus;
|
|
72
|
+
})();
|
|
@@ -351,6 +351,7 @@ module.exports = (() => {
|
|
|
351
351
|
associateTypes(InstrumentType.FUND, TransactionType.DISTRIBUTION_CASH, false);
|
|
352
352
|
associateTypes(InstrumentType.FUND, TransactionType.DISTRIBUTION_REINVEST, false);
|
|
353
353
|
associateTypes(InstrumentType.FUND, TransactionType.DISTRIBUTION_FUND, false);
|
|
354
|
+
associateTypes(InstrumentType.FUND, TransactionType.SPLIT, false);
|
|
354
355
|
associateTypes(InstrumentType.FUND, TransactionType.DELIST, false);
|
|
355
356
|
associateTypes(InstrumentType.FUND, TransactionType.MERGER_OPEN, false);
|
|
356
357
|
associateTypes(InstrumentType.FUND, TransactionType.MERGER_CLOSE, false);
|
|
@@ -44,9 +44,7 @@ module.exports = (() => {
|
|
|
44
44
|
assert.argumentIsOptional(fractions, 'fractions', Boolean);
|
|
45
45
|
|
|
46
46
|
const instruments = positions.reduce((map, p) => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
map[p.position] = instrument;
|
|
47
|
+
map[p.position] = Object.assign({ }, p.instrument || { });
|
|
50
48
|
|
|
51
49
|
return map;
|
|
52
50
|
}, { });
|
|
@@ -177,6 +175,11 @@ module.exports = (() => {
|
|
|
177
175
|
f.price = t.trade.price;
|
|
178
176
|
f.fee = t.fee;
|
|
179
177
|
f.total = t.amount;
|
|
178
|
+
|
|
179
|
+
if (t.description) {
|
|
180
|
+
f.description = t.description;
|
|
181
|
+
}
|
|
182
|
+
|
|
180
183
|
f.raw.total = getRawForDecimal(f.total);
|
|
181
184
|
f.raw.price = getRawForDecimal(f.price);
|
|
182
185
|
f.raw.boughtSold = getRawForDecimal(f.boughtSold);
|
|
@@ -184,7 +187,14 @@ module.exports = (() => {
|
|
|
184
187
|
|
|
185
188
|
const dividendFormatter = (t, f) => {
|
|
186
189
|
f.total = t.dividend.amount;
|
|
190
|
+
f.raw.total = getRawForDecimal(f.total);
|
|
191
|
+
|
|
192
|
+
if (!t.dividend.rate) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
187
196
|
f.rate = t.dividend.rate;
|
|
197
|
+
f.raw.rate = getRawForDecimal(f.rate);
|
|
188
198
|
|
|
189
199
|
let shares;
|
|
190
200
|
|
|
@@ -213,6 +223,7 @@ module.exports = (() => {
|
|
|
213
223
|
}
|
|
214
224
|
|
|
215
225
|
f.shares = shares;
|
|
226
|
+
f.raw.shares = getRawForDecimal(f.shares);
|
|
216
227
|
|
|
217
228
|
if (t.dividend.currency) {
|
|
218
229
|
f.currency = t.dividend.currency;
|
|
@@ -222,15 +233,18 @@ module.exports = (() => {
|
|
|
222
233
|
f.native = t.dividend.native;
|
|
223
234
|
f.raw.native = getRawForDecimal(f.native);
|
|
224
235
|
}
|
|
225
|
-
|
|
226
|
-
f.raw.rate = getRawForDecimal(f.rate);
|
|
227
|
-
f.raw.shares = getRawForDecimal(f.shares);
|
|
228
|
-
f.raw.total = getRawForDecimal(f.total);
|
|
229
236
|
};
|
|
230
237
|
|
|
231
238
|
const distributionCashFormatter = (t, f) => {
|
|
232
239
|
f.total = t.dividend.amount;
|
|
240
|
+
f.raw.total = getRawForDecimal(f.total);
|
|
241
|
+
|
|
242
|
+
if (!t.dividend.rate) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
233
246
|
f.rate = t.dividend.rate;
|
|
247
|
+
f.raw.rate = getRawForDecimal(f.rate);
|
|
234
248
|
|
|
235
249
|
let shares;
|
|
236
250
|
|
|
@@ -259,6 +273,7 @@ module.exports = (() => {
|
|
|
259
273
|
}
|
|
260
274
|
|
|
261
275
|
f.shares = shares;
|
|
276
|
+
f.raw.shares = getRawForDecimal(f.shares);
|
|
262
277
|
|
|
263
278
|
if (t.dividend.currency) {
|
|
264
279
|
f.currency = t.dividend.currency;
|
|
@@ -268,10 +283,6 @@ module.exports = (() => {
|
|
|
268
283
|
f.native = t.dividend.native;
|
|
269
284
|
f.raw.native = getRawForDecimal(f.native);
|
|
270
285
|
}
|
|
271
|
-
|
|
272
|
-
f.raw.rate = getRawForDecimal(f.rate);
|
|
273
|
-
f.raw.shares = getRawForDecimal(f.shares);
|
|
274
|
-
f.raw.total = getRawForDecimal(f.total);
|
|
275
286
|
};
|
|
276
287
|
|
|
277
288
|
const dividendReinvestFormatter = (t, f) => {
|
|
@@ -408,6 +419,11 @@ module.exports = (() => {
|
|
|
408
419
|
|
|
409
420
|
const splitFormatter = (t, f) => {
|
|
410
421
|
f.boughtSold = t.quantity;
|
|
422
|
+
f.raw.boughtSold = getRawForDecimal(f.boughtSold);
|
|
423
|
+
|
|
424
|
+
if (!t.split || !t.split.numerator) {
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
411
427
|
|
|
412
428
|
let rate;
|
|
413
429
|
|
|
@@ -418,12 +434,10 @@ module.exports = (() => {
|
|
|
418
434
|
}
|
|
419
435
|
|
|
420
436
|
f.rate = rate;
|
|
437
|
+
f.raw.rate = getRawForDecimal(f.rate);
|
|
421
438
|
|
|
422
439
|
f.shares = t.snapshot.open.subtract(t.quantity);
|
|
423
|
-
|
|
424
|
-
f.raw.rate = getRawForDecimal(f.rate);
|
|
425
440
|
f.raw.shares = getRawForDecimal(f.shares);
|
|
426
|
-
f.raw.boughtSold = getRawForDecimal(f.boughtSold);
|
|
427
441
|
};
|
|
428
442
|
|
|
429
443
|
const valuationFormatter = (t, f) => {
|
|
@@ -248,6 +248,22 @@ module.exports = (() => {
|
|
|
248
248
|
return returnRef;
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
+
/**
|
|
252
|
+
* Indicates if a portfolio has been added to the container.
|
|
253
|
+
*
|
|
254
|
+
* @public
|
|
255
|
+
* @param {Object} portfolio
|
|
256
|
+
* @returns {boolean}
|
|
257
|
+
*/
|
|
258
|
+
hasPortfolio(portfolio) {
|
|
259
|
+
assert.argumentIsRequired(portfolio, 'portfolio', Object);
|
|
260
|
+
assert.argumentIsRequired(portfolio.portfolio, 'portfolio.portfolio', String);
|
|
261
|
+
|
|
262
|
+
const key = portfolio.portfolio;
|
|
263
|
+
|
|
264
|
+
return this._portfolios.hasOwnProperty(key);
|
|
265
|
+
}
|
|
266
|
+
|
|
251
267
|
/**
|
|
252
268
|
* Adds a new portfolio to the container, injecting it into aggregation
|
|
253
269
|
* trees, as necessary.
|
|
@@ -260,56 +276,58 @@ module.exports = (() => {
|
|
|
260
276
|
assert.argumentIsRequired(portfolio.portfolio, 'portfolio.portfolio', String);
|
|
261
277
|
assert.argumentIsRequired(portfolio.name, 'portfolio.name', String);
|
|
262
278
|
|
|
279
|
+
if (this.hasPortfolio(portfolio)) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
263
283
|
const key = portfolio.portfolio;
|
|
264
284
|
|
|
265
|
-
|
|
266
|
-
this._portfolios = Object.assign({}, this._portfolios, { [key]: portfolio });
|
|
285
|
+
this._portfolios = Object.assign({}, this._portfolios, { [key]: portfolio });
|
|
267
286
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
287
|
+
this._definitions.forEach((treeDefinition) => {
|
|
288
|
+
const tree = this._trees[treeDefinition.name];
|
|
289
|
+
const levelDefinitions = treeDefinition.definitions;
|
|
271
290
|
|
|
272
|
-
|
|
291
|
+
let portfolioRequiredGroup = null;
|
|
273
292
|
|
|
274
|
-
|
|
275
|
-
|
|
293
|
+
let portfolioLevelDefinition = null;
|
|
294
|
+
let portfolioLevelDefinitionIndex = null;
|
|
276
295
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
296
|
+
levelDefinitions.forEach((levelDefinition, i) => {
|
|
297
|
+
if (portfolioRequiredGroup === null) {
|
|
298
|
+
portfolioRequiredGroup = levelDefinition.generateRequiredGroup(portfolio);
|
|
280
299
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
}
|
|
300
|
+
if (portfolioRequiredGroup !== null) {
|
|
301
|
+
portfolioLevelDefinition = levelDefinition;
|
|
302
|
+
portfolioLevelDefinitionIndex = i;
|
|
285
303
|
}
|
|
286
|
-
}
|
|
304
|
+
}
|
|
305
|
+
});
|
|
287
306
|
|
|
288
|
-
|
|
289
|
-
|
|
307
|
+
if (portfolioRequiredGroup !== null) {
|
|
308
|
+
let parentTrees = [ ];
|
|
290
309
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
310
|
+
if (portfolioLevelDefinitionIndex === 0) {
|
|
311
|
+
parentTrees.push(tree);
|
|
312
|
+
} else {
|
|
313
|
+
const parentLevelDefinition = levelDefinitions[ portfolioLevelDefinitionIndex - 1 ];
|
|
295
314
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
315
|
+
tree.walk((group, groupTree) => {
|
|
316
|
+
if (group.definition === parentLevelDefinition) {
|
|
317
|
+
parentTrees.push(groupTree);
|
|
318
|
+
}
|
|
319
|
+
}, false, false);
|
|
320
|
+
}
|
|
302
321
|
|
|
303
|
-
|
|
322
|
+
const overrideRequiredGroups = [ portfolioRequiredGroup ];
|
|
304
323
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
324
|
+
parentTrees.forEach((t) => {
|
|
325
|
+
createGroups.call(this, t, [ ], treeDefinition, levelDefinitions.slice(portfolioLevelDefinitionIndex), overrideRequiredGroups);
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
});
|
|
310
329
|
|
|
311
|
-
|
|
312
|
-
}
|
|
330
|
+
updateEmptyPortfolioGroups.call(this, portfolio);
|
|
313
331
|
}
|
|
314
332
|
|
|
315
333
|
/**
|
|
@@ -322,6 +340,12 @@ module.exports = (() => {
|
|
|
322
340
|
assert.argumentIsRequired(portfolio, 'portfolio', Object);
|
|
323
341
|
assert.argumentIsRequired(portfolio.portfolio, 'portfolio.portfolio', String);
|
|
324
342
|
|
|
343
|
+
if (!this.hasPortfolio(portfolio)) {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
this._portfolios[portfolio.portfolio] = portfolio;
|
|
348
|
+
|
|
325
349
|
getPositionItemsForPortfolio(this._items, portfolio.portfolio).forEach(item => item.updatePortfolio(portfolio));
|
|
326
350
|
|
|
327
351
|
updateEmptyPortfolioGroups.call(this, portfolio);
|
|
@@ -339,6 +363,10 @@ module.exports = (() => {
|
|
|
339
363
|
assert.argumentIsRequired(portfolio, 'portfolio', Object);
|
|
340
364
|
assert.argumentIsRequired(portfolio.portfolio, 'portfolio.portfolio', String);
|
|
341
365
|
|
|
366
|
+
if (!this.hasPortfolio(portfolio)) {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
342
370
|
getPositionItemsForPortfolio(this._items, portfolio.portfolio).forEach(item => removePositionItem.call(this, item));
|
|
343
371
|
|
|
344
372
|
delete this._portfolios[portfolio.portfolio];
|
|
@@ -1253,6 +1281,8 @@ module.exports = (() => {
|
|
|
1253
1281
|
* @property {number} highPrice
|
|
1254
1282
|
* @property {number} lowPrice
|
|
1255
1283
|
* @property {number} volume
|
|
1284
|
+
* @property {string} askPrice
|
|
1285
|
+
* @property {string} bidPrice
|
|
1256
1286
|
* @property {string} timeDisplay
|
|
1257
1287
|
* @property {Day|null} lastDay
|
|
1258
1288
|
*/
|
|
@@ -10,7 +10,8 @@ const array = require('@barchart/common-js/lang/array'),
|
|
|
10
10
|
|
|
11
11
|
const fractionFormatter = require('@barchart/marketdata-api-js/lib/utilities/format/fraction');
|
|
12
12
|
|
|
13
|
-
const InstrumentType = require('./../data/InstrumentType')
|
|
13
|
+
const InstrumentType = require('./../data/InstrumentType'),
|
|
14
|
+
FilterMode = require('./../data/FilterMode');
|
|
14
15
|
|
|
15
16
|
const PositionLevelDefinition = require('./definitions/PositionLevelDefinition'),
|
|
16
17
|
PositionLevelType = require('./definitions/PositionLevelType');
|
|
@@ -54,13 +55,19 @@ module.exports = (() => {
|
|
|
54
55
|
this._description = description;
|
|
55
56
|
|
|
56
57
|
this._single = this._definition.single;
|
|
58
|
+
this._homogeneous = this._definition.homogeneous;
|
|
59
|
+
|
|
57
60
|
this._aggregateCash = is.boolean(aggregateCash) && aggregateCash;
|
|
58
61
|
|
|
59
62
|
this._excluded = false;
|
|
60
63
|
this._showClosedPositions = false;
|
|
64
|
+
this._showOpenedPositions = false;
|
|
61
65
|
|
|
62
66
|
this._groupExcludedChangeEvent = new Event(this);
|
|
63
67
|
this._showClosedPositionsChangeEvent = new Event(this);
|
|
68
|
+
this._showOpenedPositionsChangeEvent = new Event(this);
|
|
69
|
+
|
|
70
|
+
this._filterMode = FilterMode.OPEN;
|
|
64
71
|
|
|
65
72
|
this._excludedItems = [ ];
|
|
66
73
|
this._excludedItemMap = { };
|
|
@@ -104,6 +111,13 @@ module.exports = (() => {
|
|
|
104
111
|
this._dataFormat.fundamental = { };
|
|
105
112
|
}
|
|
106
113
|
|
|
114
|
+
if (this._homogeneous && items.length !== 0) {
|
|
115
|
+
const item = items[0];
|
|
116
|
+
|
|
117
|
+
this._dataFormat.instrument = item.position.instrument;
|
|
118
|
+
this._dataFormat.fundamental = item.fundamental || { };
|
|
119
|
+
}
|
|
120
|
+
|
|
107
121
|
this._dataActual.quoteLast = null;
|
|
108
122
|
this._dataActual.quoteOpen = null;
|
|
109
123
|
this._dataActual.quoteHigh = null;
|
|
@@ -307,6 +321,17 @@ module.exports = (() => {
|
|
|
307
321
|
return this._single;
|
|
308
322
|
}
|
|
309
323
|
|
|
324
|
+
/**
|
|
325
|
+
* Indicates if the group will only contain {@link PositionItem} instances
|
|
326
|
+
* that share the same instrument.
|
|
327
|
+
*
|
|
328
|
+
* @public
|
|
329
|
+
* @returns {Boolean}
|
|
330
|
+
*/
|
|
331
|
+
get homogeneous() {
|
|
332
|
+
return this._homogeneous;
|
|
333
|
+
}
|
|
334
|
+
|
|
310
335
|
/**
|
|
311
336
|
* Indicates if the group should be excluded from higher-level aggregations.
|
|
312
337
|
*
|
|
@@ -431,6 +456,26 @@ module.exports = (() => {
|
|
|
431
456
|
}
|
|
432
457
|
}
|
|
433
458
|
|
|
459
|
+
setShowOpenedPositions(value) {
|
|
460
|
+
assert.argumentIsRequired(value, 'value', Boolean);
|
|
461
|
+
|
|
462
|
+
if (this._showOpenedPositions !== value) {
|
|
463
|
+
this._showOpenedPositionsChangeEvent.fire(this._showOpenedPositions = value);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
setFilterMode(mode) {
|
|
468
|
+
assert.argumentIsRequired(mode, 'mode', FilterMode);
|
|
469
|
+
|
|
470
|
+
const showClosed = mode !== FilterMode.OPEN;
|
|
471
|
+
const showOpen = mode !== FilterMode.CLOSED;
|
|
472
|
+
|
|
473
|
+
this._filterMode = mode;
|
|
474
|
+
|
|
475
|
+
this.setShowClosedPositions(showClosed);
|
|
476
|
+
this.setShowOpenedPositions(showOpen);
|
|
477
|
+
}
|
|
478
|
+
|
|
434
479
|
/**
|
|
435
480
|
* Updates the portfolio data. For example, a portfolio's name might change. This
|
|
436
481
|
* function only affects {@link PositionLevelType.PORTFOLIO} groups.
|
|
@@ -525,24 +570,26 @@ module.exports = (() => {
|
|
|
525
570
|
setBarchartPriceFormattingRules(value) {
|
|
526
571
|
assert.argumentIsRequired(value, 'value', Boolean);
|
|
527
572
|
|
|
528
|
-
if (this._useBarchartPriceFormattingRules
|
|
529
|
-
|
|
573
|
+
if (this._useBarchartPriceFormattingRules === value) {
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
530
576
|
|
|
531
|
-
|
|
532
|
-
const item = this._items[0];
|
|
577
|
+
this._useBarchartPriceFormattingRules = value;
|
|
533
578
|
|
|
534
|
-
|
|
535
|
-
|
|
579
|
+
if ((this._single || this._homogeneous) && this._dataActual.currentPrice) {
|
|
580
|
+
const item = this._items[0];
|
|
536
581
|
|
|
537
|
-
|
|
582
|
+
const instrument = item.position.instrument;
|
|
583
|
+
const currency = instrument.currency;
|
|
538
584
|
|
|
539
|
-
|
|
540
|
-
this._dataFormat.quoteOpen = formatFraction(this._dataActual.quoteOpen, currency, instrument, this._useBarchartPriceFormattingRules);
|
|
541
|
-
this._dataFormat.quoteHigh = formatFraction(this._dataActual.quoteHigh, currency, instrument, this._useBarchartPriceFormattingRules);
|
|
542
|
-
this._dataFormat.quoteLow = formatFraction(this._dataActual.quoteLow, currency, instrument, this._useBarchartPriceFormattingRules);
|
|
585
|
+
this._dataFormat.currentPrice = formatFraction(this._dataActual.currentPrice, currency, instrument, this._useBarchartPriceFormattingRules);
|
|
543
586
|
|
|
544
|
-
|
|
545
|
-
|
|
587
|
+
this._dataFormat.quoteLast = formatFraction(this._dataActual.quoteLast, currency, instrument, this._useBarchartPriceFormattingRules);
|
|
588
|
+
this._dataFormat.quoteOpen = formatFraction(this._dataActual.quoteOpen, currency, instrument, this._useBarchartPriceFormattingRules);
|
|
589
|
+
this._dataFormat.quoteHigh = formatFraction(this._dataActual.quoteHigh, currency, instrument, this._useBarchartPriceFormattingRules);
|
|
590
|
+
this._dataFormat.quoteLow = formatFraction(this._dataActual.quoteLow, currency, instrument, this._useBarchartPriceFormattingRules);
|
|
591
|
+
|
|
592
|
+
this._dataFormat.quoteChange = formatFraction(this._dataActual.quoteChange, currency, instrument, this._useBarchartPriceFormattingRules);
|
|
546
593
|
}
|
|
547
594
|
}
|
|
548
595
|
|
|
@@ -553,7 +600,7 @@ module.exports = (() => {
|
|
|
553
600
|
|
|
554
601
|
function bindItem(item) {
|
|
555
602
|
const quoteBinding = item.registerQuoteChangeHandler((quote, sender) => {
|
|
556
|
-
if (this._single) {
|
|
603
|
+
if (this._single || (this._homogeneous && item === this._items[0])) {
|
|
557
604
|
const instrument = sender.position.instrument;
|
|
558
605
|
const currency = instrument.currency;
|
|
559
606
|
|
|
@@ -596,7 +643,7 @@ module.exports = (() => {
|
|
|
596
643
|
});
|
|
597
644
|
|
|
598
645
|
const fundamentalBinding = item.registerFundamentalDataChangeHandler((data) => {
|
|
599
|
-
if (this._single) {
|
|
646
|
+
if (this._single || this._homogeneous) {
|
|
600
647
|
this._dataFormat.fundamental = data;
|
|
601
648
|
|
|
602
649
|
return;
|
|
@@ -649,12 +696,14 @@ module.exports = (() => {
|
|
|
649
696
|
let lockedBinding = Disposable.getEmpty();
|
|
650
697
|
let calculatingBinding = Disposable.getEmpty();
|
|
651
698
|
|
|
652
|
-
if (this._single) {
|
|
699
|
+
if (this._single || this._homogeneous) {
|
|
653
700
|
newsBinding = item.registerNewsExistsChangeHandler((exists) => {
|
|
654
701
|
this._dataActual.newsExists = exists;
|
|
655
702
|
this._dataFormat.newsExists = exists;
|
|
656
703
|
});
|
|
704
|
+
}
|
|
657
705
|
|
|
706
|
+
if (this._single) {
|
|
658
707
|
lockedBinding = item.registerLockChangeHandler((locked) => {
|
|
659
708
|
this._dataFormat.locked = locked;
|
|
660
709
|
});
|
|
@@ -847,6 +896,10 @@ module.exports = (() => {
|
|
|
847
896
|
updates.periodDivisorPrevious = updates.periodDivisorPrevious.add(translate(item, item.data.periodDivisorPrevious));
|
|
848
897
|
updates.periodDivisorPrevious2 = updates.periodDivisorPrevious2.add(translate(item, item.data.periodDivisorPrevious2));
|
|
849
898
|
|
|
899
|
+
if (group.homogeneous) {
|
|
900
|
+
updates.quantity = updates.quantity.add(item.data.quantity);
|
|
901
|
+
}
|
|
902
|
+
|
|
850
903
|
return updates;
|
|
851
904
|
}, {
|
|
852
905
|
basis: Decimal.ZERO,
|
|
@@ -867,7 +920,8 @@ module.exports = (() => {
|
|
|
867
920
|
totalDivisor: Decimal.ZERO,
|
|
868
921
|
periodDivisorCurrent: Decimal.ZERO,
|
|
869
922
|
periodDivisorPrevious: Decimal.ZERO,
|
|
870
|
-
periodDivisorPrevious2: Decimal.ZERO
|
|
923
|
+
periodDivisorPrevious2: Decimal.ZERO,
|
|
924
|
+
quantity: Decimal.ZERO
|
|
871
925
|
});
|
|
872
926
|
|
|
873
927
|
actual.basis = updates.basis;
|
|
@@ -890,6 +944,10 @@ module.exports = (() => {
|
|
|
890
944
|
actual.periodDivisorPrevious = updates.periodDivisorPrevious;
|
|
891
945
|
actual.periodDivisorPrevious2 = updates.periodDivisorPrevious2;
|
|
892
946
|
|
|
947
|
+
if (group.homogeneous) {
|
|
948
|
+
actual.quantity = updates.quantity;
|
|
949
|
+
}
|
|
950
|
+
|
|
893
951
|
format.basis = formatCurrency(actual.basis, currency);
|
|
894
952
|
format.basis2 = formatCurrency(actual.basis2, currency);
|
|
895
953
|
format.realized = formatCurrency(actual.realized, currency);
|
|
@@ -909,6 +967,10 @@ module.exports = (() => {
|
|
|
909
967
|
format.periodUnrealized = formatCurrency(updates.periodUnrealized, currency);
|
|
910
968
|
format.cashTotal = formatCurrency(updates.cashTotal, currency);
|
|
911
969
|
|
|
970
|
+
if (group.homogeneous) {
|
|
971
|
+
format.quantity = formatDecimal(actual.quantity, 2);
|
|
972
|
+
}
|
|
973
|
+
|
|
912
974
|
calculateRealizedPercent(group);
|
|
913
975
|
calculateUnrealizedPercent(group);
|
|
914
976
|
|
|
@@ -923,7 +985,7 @@ module.exports = (() => {
|
|
|
923
985
|
const groupItems = group._items;
|
|
924
986
|
|
|
925
987
|
if (group.single && groupItems.length === 1) {
|
|
926
|
-
const item =
|
|
988
|
+
const item = groupItems[0];
|
|
927
989
|
const instrument = item.position.instrument;
|
|
928
990
|
|
|
929
991
|
actual.quantity = item.data.quantity;
|
|
@@ -950,6 +1012,12 @@ module.exports = (() => {
|
|
|
950
1012
|
format.expired = definition.type === PositionLevelType.POSITION && item.data.expired;
|
|
951
1013
|
}
|
|
952
1014
|
|
|
1015
|
+
if (group.homogeneous && groupItems.length !== 0) {
|
|
1016
|
+
const item = groupItems[0];
|
|
1017
|
+
|
|
1018
|
+
format.expired = item.data.expired;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
953
1021
|
let portfolioType = null;
|
|
954
1022
|
|
|
955
1023
|
if (groupItems.length > 0) {
|
|
@@ -7,7 +7,8 @@ const assert = require('@barchart/common-js/lang/assert'),
|
|
|
7
7
|
is = require('@barchart/common-js/lang/is');
|
|
8
8
|
|
|
9
9
|
const InstrumentType = require('./../data/InstrumentType'),
|
|
10
|
-
PositionDirection = require('./../data/PositionDirection')
|
|
10
|
+
PositionDirection = require('./../data/PositionDirection'),
|
|
11
|
+
OptionsValuationType = require('./../data/OptionsValuationType');
|
|
11
12
|
|
|
12
13
|
const AveragePriceCalculator = require('./../calculators/AveragePriceCalculator'),
|
|
13
14
|
ValuationCalculator = require('./../calculators/ValuationCalculator');
|
|
@@ -242,6 +243,10 @@ module.exports = (() => {
|
|
|
242
243
|
if (this._portfolio !== portfolio) {
|
|
243
244
|
this._portfolioChangedEvent.fire(this._portfolio = portfolio);
|
|
244
245
|
}
|
|
246
|
+
|
|
247
|
+
if (this._currentQuote) {
|
|
248
|
+
this.setQuote(this._currentQuote, true);
|
|
249
|
+
}
|
|
245
250
|
}
|
|
246
251
|
|
|
247
252
|
/**
|
|
@@ -260,14 +265,22 @@ module.exports = (() => {
|
|
|
260
265
|
return;
|
|
261
266
|
}
|
|
262
267
|
|
|
263
|
-
|
|
268
|
+
let priceToUse;
|
|
269
|
+
|
|
270
|
+
if (this.portfolio.miscellany && this.portfolio.miscellany.data && this.portfolio.miscellany.data.optionsValuation === OptionsValuationType.MIDPOINT.code && quote.askPrice && quote.bidPrice) {
|
|
271
|
+
priceToUse = (quote.askPrice + quote.bidPrice) / 2;
|
|
272
|
+
} else {
|
|
273
|
+
priceToUse = quote.lastPrice;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (this._currentPrice !== priceToUse || force) {
|
|
264
277
|
if (quote.previousPrice) {
|
|
265
278
|
this._data.previousPrice = quote.previousPrice;
|
|
266
279
|
}
|
|
267
280
|
|
|
268
|
-
calculatePriceData(this,
|
|
281
|
+
calculatePriceData(this, priceToUse, calculateQuoteDay(quote), this._today);
|
|
269
282
|
|
|
270
|
-
this._currentPrice =
|
|
283
|
+
this._currentPrice = priceToUse;
|
|
271
284
|
|
|
272
285
|
this._previousQuote = this._currentQuote;
|
|
273
286
|
this._currentQuote = quote;
|
|
@@ -49,6 +49,8 @@ module.exports = (() => {
|
|
|
49
49
|
this._requiredGroups = requiredGroups || [ ];
|
|
50
50
|
|
|
51
51
|
this._single = type === PositionLevelType.POSITION;
|
|
52
|
+
this._homogeneous = type === PositionLevelType.INSTRUMENT;
|
|
53
|
+
|
|
52
54
|
this._aggregateCash = is.boolean(aggregateCash) && aggregateCash;
|
|
53
55
|
|
|
54
56
|
this._requiredGroupGenerator = requiredGroupGenerator || (input => null);
|
|
@@ -128,6 +130,16 @@ module.exports = (() => {
|
|
|
128
130
|
return this._single;
|
|
129
131
|
}
|
|
130
132
|
|
|
133
|
+
/**
|
|
134
|
+
* Indicates if the grouping level only contains items for the same instrument.
|
|
135
|
+
*
|
|
136
|
+
* @public
|
|
137
|
+
* @return {Boolean}
|
|
138
|
+
*/
|
|
139
|
+
get homogeneous() {
|
|
140
|
+
return this._homogeneous;
|
|
141
|
+
}
|
|
142
|
+
|
|
131
143
|
/**
|
|
132
144
|
* Indicates if the grouping level should aggregate cash positions.
|
|
133
145
|
*
|
|
@@ -15,6 +15,17 @@ module.exports = (() => {
|
|
|
15
15
|
super(code, code);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* A level of grouping for positions which share the same instrument.
|
|
20
|
+
*
|
|
21
|
+
* @public
|
|
22
|
+
* @static
|
|
23
|
+
* @returns {PositionLevelType}
|
|
24
|
+
*/
|
|
25
|
+
static get INSTRUMENT() {
|
|
26
|
+
return instrument;
|
|
27
|
+
}
|
|
28
|
+
|
|
18
29
|
/**
|
|
19
30
|
* A level of grouping that represents an entire portfolio's contents.
|
|
20
31
|
*
|
|
@@ -39,8 +50,9 @@ module.exports = (() => {
|
|
|
39
50
|
}
|
|
40
51
|
|
|
41
52
|
/**
|
|
42
|
-
* A level of grouping that
|
|
43
|
-
* intermediate level of grouping (e.g. an asset
|
|
53
|
+
* A level of grouping that doesn't fit into any other explicitly defined
|
|
54
|
+
* category. This could be an intermediate level of grouping (e.g. an asset
|
|
55
|
+
* class within a portfolio).
|
|
44
56
|
*
|
|
45
57
|
* @public
|
|
46
58
|
* @static
|
|
@@ -51,6 +63,7 @@ module.exports = (() => {
|
|
|
51
63
|
}
|
|
52
64
|
}
|
|
53
65
|
|
|
66
|
+
const instrument = new PositionLevelType('INSTRUMENT');
|
|
54
67
|
const portfolio = new PositionLevelType('PORTFOLIO');
|
|
55
68
|
const position = new PositionLevelType('POSITION');
|
|
56
69
|
const other = new PositionLevelType('OTHER');
|
|
@@ -4,7 +4,8 @@ const Currency = require('@barchart/common-js/lang/Currency'),
|
|
|
4
4
|
SchemaBuilder = require('@barchart/common-js/serialization/json/builders/SchemaBuilder'),
|
|
5
5
|
Timezones = require('@barchart/common-js/lang/Timezones');
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const SnapTradeLinkStatus = require('./../data/SnapTradeLinkStatus'),
|
|
8
|
+
ValuationType = require('./../data/ValuationType');
|
|
8
9
|
|
|
9
10
|
module.exports = (() => {
|
|
10
11
|
'use strict';
|
|
@@ -103,6 +104,15 @@ module.exports = (() => {
|
|
|
103
104
|
.withField('defaults.currency', DataType.forEnum(Currency, 'Currency'))
|
|
104
105
|
.withField('defaults.reinvest', DataType.BOOLEAN, true)
|
|
105
106
|
.withField('defaults.valuation', DataType.forEnum(ValuationType, 'ValuationType'))
|
|
107
|
+
.withField('snaptrade.connection', DataType.STRING, true)
|
|
108
|
+
.withField('snaptrade.account', DataType.STRING, true)
|
|
109
|
+
.withField('snaptrade.broken', DataType.BOOLEAN, true)
|
|
110
|
+
.withField('snaptrade.timestamp', DataType.TIMESTAMP, true)
|
|
111
|
+
.withField('snaptrade.link.status', DataType.forEnum(SnapTradeLinkStatus, 'SnapTradeLinkStatus'), true)
|
|
112
|
+
.withField('snaptrade.link.progress', DataType.NUMBER, true)
|
|
113
|
+
.withField('snaptrade.link.timestamp', DataType.TIMESTAMP, true)
|
|
114
|
+
.withField('snaptrade.brokerage.display', DataType.STRING, true)
|
|
115
|
+
.withField('snaptrade.brokerage.logo', DataType.STRING, true)
|
|
106
116
|
.withField('legacy.system', DataType.STRING, true)
|
|
107
117
|
.withField('legacy.user', DataType.STRING, true)
|
|
108
118
|
.withField('legacy.portfolio', DataType.STRING, true)
|
|
@@ -110,7 +120,6 @@ module.exports = (() => {
|
|
|
110
120
|
.withField('legacy.drops', DataType.NUMBER, true)
|
|
111
121
|
.withField('miscellany', DataType.AD_HOC, true)
|
|
112
122
|
.withField('email', DataType.AD_HOC, true)
|
|
113
|
-
.withField('linked', DataType.BOOLEAN)
|
|
114
123
|
.withField('system.calculate.processors', DataType.NUMBER, true)
|
|
115
124
|
.withField('system.sequence', DataType.NUMBER)
|
|
116
125
|
.withField('system.version', DataType.STRING)
|
|
@@ -129,6 +138,15 @@ module.exports = (() => {
|
|
|
129
138
|
.withField('defaults.currency', DataType.forEnum(Currency, 'Currency'))
|
|
130
139
|
.withField('defaults.reinvest', DataType.BOOLEAN, true)
|
|
131
140
|
.withField('defaults.valuation', DataType.forEnum(ValuationType, 'ValuationType'))
|
|
141
|
+
.withField('snaptrade.connection', DataType.STRING, true)
|
|
142
|
+
.withField('snaptrade.account', DataType.STRING, true)
|
|
143
|
+
.withField('snaptrade.broken', DataType.BOOLEAN, true)
|
|
144
|
+
.withField('snaptrade.timestamp', DataType.TIMESTAMP, true)
|
|
145
|
+
.withField('snaptrade.link.status', DataType.forEnum(SnapTradeLinkStatus, 'SnapTradeLinkStatus'), true)
|
|
146
|
+
.withField('snaptrade.link.progress', DataType.NUMBER, true)
|
|
147
|
+
.withField('snaptrade.link.timestamp', DataType.TIMESTAMP, true)
|
|
148
|
+
.withField('snaptrade.brokerage.display', DataType.STRING, true)
|
|
149
|
+
.withField('snaptrade.brokerage.logo', DataType.STRING, true)
|
|
132
150
|
.withField('legacy.system', DataType.STRING, true)
|
|
133
151
|
.withField('legacy.user', DataType.STRING, true)
|
|
134
152
|
.withField('legacy.portfolio', DataType.STRING, true)
|
|
@@ -136,7 +154,6 @@ module.exports = (() => {
|
|
|
136
154
|
.withField('legacy.drops', DataType.NUMBER, true)
|
|
137
155
|
.withField('miscellany', DataType.AD_HOC, true)
|
|
138
156
|
.withField('email', DataType.AD_HOC, true)
|
|
139
|
-
.withField('linked', DataType.BOOLEAN)
|
|
140
157
|
.withField('system.calculate.processors', DataType.NUMBER, true)
|
|
141
158
|
.schema
|
|
142
159
|
);
|
|
@@ -157,8 +174,7 @@ module.exports = (() => {
|
|
|
157
174
|
.withField('defaults.valuation', DataType.forEnum(ValuationType, 'ValuationType'), true)
|
|
158
175
|
.withField('miscellany', DataType.AD_HOC, true)
|
|
159
176
|
.withField('email', DataType.AD_HOC, true)
|
|
160
|
-
|
|
161
|
-
.schema
|
|
177
|
+
.schema
|
|
162
178
|
);
|
|
163
179
|
|
|
164
180
|
const update = new PortfolioSchema(SchemaBuilder.withName('update')
|
|
@@ -170,7 +186,7 @@ module.exports = (() => {
|
|
|
170
186
|
.withField('defaults.reinvest', DataType.BOOLEAN, true)
|
|
171
187
|
.withField('miscellany', DataType.AD_HOC, true)
|
|
172
188
|
.withField('email', DataType.AD_HOC, true)
|
|
173
|
-
|
|
189
|
+
.schema
|
|
174
190
|
);
|
|
175
191
|
|
|
176
192
|
return PortfolioSchema;
|