@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.
@@ -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);
@@ -8,8 +8,8 @@ module.exports = (() => {
8
8
  *
9
9
  * @public
10
10
  * @extends {Enum}
11
- * @param {String} description
12
11
  * @param {String} code
12
+ * @param {String} description
13
13
  */
14
14
  class ValuationType extends Enum {
15
15
  constructor(code, description) {
@@ -44,9 +44,7 @@ module.exports = (() => {
44
44
  assert.argumentIsOptional(fractions, 'fractions', Boolean);
45
45
 
46
46
  const instruments = positions.reduce((map, p) => {
47
- const instrument = Object.assign({ }, p.instrument || { });
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
- if (!this._portfolios.hasOwnProperty(key)) {
266
- this._portfolios = Object.assign({}, this._portfolios, { [key]: portfolio });
285
+ this._portfolios = Object.assign({}, this._portfolios, { [key]: portfolio });
267
286
 
268
- this._definitions.forEach((treeDefinition) => {
269
- const tree = this._trees[treeDefinition.name];
270
- const levelDefinitions = treeDefinition.definitions;
287
+ this._definitions.forEach((treeDefinition) => {
288
+ const tree = this._trees[treeDefinition.name];
289
+ const levelDefinitions = treeDefinition.definitions;
271
290
 
272
- let portfolioRequiredGroup = null;
291
+ let portfolioRequiredGroup = null;
273
292
 
274
- let portfolioLevelDefinition = null;
275
- let portfolioLevelDefinitionIndex = null;
293
+ let portfolioLevelDefinition = null;
294
+ let portfolioLevelDefinitionIndex = null;
276
295
 
277
- levelDefinitions.forEach((levelDefinition, i) => {
278
- if (portfolioRequiredGroup === null) {
279
- portfolioRequiredGroup = levelDefinition.generateRequiredGroup(portfolio);
296
+ levelDefinitions.forEach((levelDefinition, i) => {
297
+ if (portfolioRequiredGroup === null) {
298
+ portfolioRequiredGroup = levelDefinition.generateRequiredGroup(portfolio);
280
299
 
281
- if (portfolioRequiredGroup !== null) {
282
- portfolioLevelDefinition = levelDefinition;
283
- portfolioLevelDefinitionIndex = i;
284
- }
300
+ if (portfolioRequiredGroup !== null) {
301
+ portfolioLevelDefinition = levelDefinition;
302
+ portfolioLevelDefinitionIndex = i;
285
303
  }
286
- });
304
+ }
305
+ });
287
306
 
288
- if (portfolioRequiredGroup !== null) {
289
- let parentTrees = [ ];
307
+ if (portfolioRequiredGroup !== null) {
308
+ let parentTrees = [ ];
290
309
 
291
- if (portfolioLevelDefinitionIndex === 0) {
292
- parentTrees.push(tree);
293
- } else {
294
- const parentLevelDefinition = levelDefinitions[ portfolioLevelDefinitionIndex - 1 ];
310
+ if (portfolioLevelDefinitionIndex === 0) {
311
+ parentTrees.push(tree);
312
+ } else {
313
+ const parentLevelDefinition = levelDefinitions[ portfolioLevelDefinitionIndex - 1 ];
295
314
 
296
- tree.walk((group, groupTree) => {
297
- if (group.definition === parentLevelDefinition) {
298
- parentTrees.push(groupTree);
299
- }
300
- }, false, false);
301
- }
315
+ tree.walk((group, groupTree) => {
316
+ if (group.definition === parentLevelDefinition) {
317
+ parentTrees.push(groupTree);
318
+ }
319
+ }, false, false);
320
+ }
302
321
 
303
- const overrideRequiredGroups = [ portfolioRequiredGroup ];
322
+ const overrideRequiredGroups = [ portfolioRequiredGroup ];
304
323
 
305
- parentTrees.forEach((t) => {
306
- createGroups.call(this, t, [ ], treeDefinition, levelDefinitions.slice(portfolioLevelDefinitionIndex), overrideRequiredGroups);
307
- });
308
- }
309
- });
324
+ parentTrees.forEach((t) => {
325
+ createGroups.call(this, t, [ ], treeDefinition, levelDefinitions.slice(portfolioLevelDefinitionIndex), overrideRequiredGroups);
326
+ });
327
+ }
328
+ });
310
329
 
311
- updateEmptyPortfolioGroups.call(this, portfolio);
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 !== value) {
529
- this._useBarchartPriceFormattingRules = value;
573
+ if (this._useBarchartPriceFormattingRules === value) {
574
+ return;
575
+ }
530
576
 
531
- if (this._single && this._dataActual.currentPrice) {
532
- const item = this._items[0];
577
+ this._useBarchartPriceFormattingRules = value;
533
578
 
534
- const instrument = item.position.instrument;
535
- const currency = instrument.currency;
579
+ if ((this._single || this._homogeneous) && this._dataActual.currentPrice) {
580
+ const item = this._items[0];
536
581
 
537
- this._dataFormat.currentPrice = formatFraction(this._dataActual.currentPrice, currency, instrument, this._useBarchartPriceFormattingRules);
582
+ const instrument = item.position.instrument;
583
+ const currency = instrument.currency;
538
584
 
539
- this._dataFormat.quoteLast = formatFraction(this._dataActual.quoteLast, currency, instrument, this._useBarchartPriceFormattingRules);
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
- this._dataFormat.quoteChange = formatFraction(this._dataActual.quoteChange, currency, instrument, this._useBarchartPriceFormattingRules);
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 = group._items[0];
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
- if (this._currentPrice !== quote.lastPrice || force) {
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, quote.lastPrice, calculateQuoteDay(quote), this._today);
281
+ calculatePriceData(this, priceToUse, calculateQuoteDay(quote), this._today);
269
282
 
270
- this._currentPrice = quote.lastPrice;
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 is neither a portfolio or a position. This could be an
43
- * intermediate level of grouping (e.g. an asset class within a portfolio).
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 ValuationType = require('./../data/ValuationType');
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
- .withField('linked', DataType.BOOLEAN)
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
- .schema
189
+ .schema
174
190
  );
175
191
 
176
192
  return PortfolioSchema;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barchart/portfolio-api-common",
3
- "version": "7.0.0",
3
+ "version": "7.0.1",
4
4
  "description": "Common JavaScript code used by Barchart's Portfolio Service",
5
5
  "author": {
6
6
  "name": "Bryan Ingle",