@barchart/portfolio-api-common 1.2.83 → 1.2.87

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.
@@ -39,16 +39,18 @@ module.exports = (() => {
39
39
  *
40
40
  * @public
41
41
  * @param {Array.<PositionTreeDefinition>} definitions
42
- * @param {Array.<Object>} portfolios
43
- * @param {Array.<Object>} positions
44
- * @param {Array.<Object>} summaries
42
+ * @param {Array.<Object>} portfolios - The portfolios.
43
+ * @param {Array.<Object>} positions - The positions (for all of the portfolios).
44
+ * @param {Array.<Object>} summaries - The positions summaries (for all of the positions).
45
+ * @param {PositionSummaryFrame} - If specified, locks the current (and previous) periods to a specific frame, use for reporting.
45
46
  */
46
47
  class PositionContainer {
47
- constructor(definitions, portfolios, positions, summaries) {
48
+ constructor(definitions, portfolios, positions, summaries, frame) {
48
49
  assert.argumentIsArray(definitions, 'definitions', PositionTreeDefinition, 'PositionTreeDefinition');
49
50
  assert.argumentIsArray(portfolios, 'portfolios');
50
51
  assert.argumentIsArray(positions, 'positions');
51
52
  assert.argumentIsArray(summaries, 'summaries');
53
+ assert.argumentIsOptional(frame, 'frame', PositionSummaryFrame, 'PositionSummaryFrame');
52
54
 
53
55
  this._definitions = definitions;
54
56
 
@@ -63,18 +65,24 @@ module.exports = (() => {
63
65
  return map;
64
66
  }, { });
65
67
 
66
- this._currentSummaryFrame = PositionSummaryFrame.YTD;
68
+ this._currentSummaryFrame = frame || PositionSummaryFrame.YTD;
67
69
  this._currentSummaryRange = array.last(this._currentSummaryFrame.getRecentRanges(0));
68
70
 
71
+ this._previousSummaryFrame = frame || PositionSummaryFrame.YEARLY;
72
+ this._previousSummaryRanges = this._previousSummaryFrame.getRecentRanges(3);
73
+
74
+ if (this._currentSummaryFrame === this._previousSummaryFrame) {
75
+ this._previousSummaryRanges.pop();
76
+ } else {
77
+ this._previousSummaryRanges.shift();
78
+ }
79
+
69
80
  this._summariesCurrent = summaries.reduce((map, summary) => {
70
81
  addSummaryCurrent(map, summary, this._currentSummaryFrame, this._currentSummaryRange);
71
82
 
72
83
  return map;
73
84
  }, { });
74
85
 
75
- this._previousSummaryFrame = PositionSummaryFrame.YEARLY;
76
- this._previousSummaryRanges = this._previousSummaryFrame.getRecentRanges(1);
77
-
78
86
  this._summariesPrevious = summaries.reduce((map, summary) => {
79
87
  addSummaryPrevious(map, summary, this._previousSummaryFrame, this._previousSummaryRanges);
80
88
 
@@ -275,8 +283,17 @@ module.exports = (() => {
275
283
  }
276
284
 
277
285
  const existingBarchartSymbols = this.getPositionSymbols(false);
286
+ const existingPositionItem = this._items.find(item => item.position.position === position.position);
278
287
 
279
- removePositionItem.call(this, this._items.find(item => item.position.position === position.position));
288
+ let currentQuote = null;
289
+ let previousQuote = null;
290
+
291
+ if (existingPositionItem) {
292
+ currentQuote = existingPositionItem.quote || null;
293
+ previousQuote = existingPositionItem.previousQuote || null;
294
+ }
295
+
296
+ removePositionItem.call(this, existingPositionItem);
280
297
 
281
298
  summaries.forEach((summary) => {
282
299
  addSummaryCurrent(this._summariesCurrent, summary, this._currentSummaryFrame, this._currentSummaryRange);
@@ -288,6 +305,14 @@ module.exports = (() => {
288
305
  addBarchartSymbol(this._symbols, item);
289
306
  addDisplaySymbol(this._symbolsDisplay, item);
290
307
 
308
+ if (previousQuote !== null) {
309
+ item.setQuote(previousQuote);
310
+ }
311
+
312
+ if (currentQuote !== null) {
313
+ item.setQuote(currentQuote);
314
+ }
315
+
291
316
  this._items.push(item);
292
317
 
293
318
  const createGroupOrInjectItem = (parentTree, treeDefinition, levelDefinitions) => {
@@ -397,13 +422,13 @@ module.exports = (() => {
397
422
  * @param {Object} position
398
423
  * @return {Boolean}
399
424
  */
400
- getPositionLock(portfolio, position) {
425
+ getPositionLock(position) {
401
426
  assert.argumentIsRequired(position, 'position', Object);
402
427
  assert.argumentIsRequired(position.position, 'position.position', String);
403
428
 
404
429
  const item = this._items.find(i => i.position.position === position.position);
405
430
 
406
- return is.object(item) && item.locked;
431
+ return is.object(item) && item.data.locked;
407
432
  }
408
433
 
409
434
  /**
@@ -907,7 +932,7 @@ module.exports = (() => {
907
932
  }
908
933
  }
909
934
 
910
- function createPositionItem(position) {
935
+ function createPositionItem(position, currentQuote, previousQuote) {
911
936
  const portfolio = this._portfolios[position.portfolio];
912
937
 
913
938
  let returnRef;
@@ -11,8 +11,8 @@ 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}
14
+ * A container for a single position, which handles quote changes and notifies
15
+ * observers -- which are typically parent-level {@link PositionGroup}
16
16
  * instances.
17
17
  *
18
18
  * @public
@@ -41,6 +41,7 @@ module.exports = (() => {
41
41
  this._data.basis = null;
42
42
 
43
43
  this._currentQuote = null;
44
+ this._previousQuote = null;
44
45
  this._currentPrice = null;
45
46
 
46
47
  this._data.currentPrice = null;
@@ -124,7 +125,7 @@ module.exports = (() => {
124
125
  }
125
126
 
126
127
  /**
127
- * The year-to-date summary of the encapsulated position.
128
+ * The current summary of the encapsulated position.
128
129
  *
129
130
  * @public
130
131
  * @returns {Object}
@@ -134,7 +135,7 @@ module.exports = (() => {
134
135
  }
135
136
 
136
137
  /**
137
- * Previous year's summaries for the encapsulated position.
138
+ * Previous summaries for the encapsulated position.
138
139
  *
139
140
  * @public
140
141
  * @returns {Object}
@@ -147,22 +148,32 @@ module.exports = (() => {
147
148
  * Various data regarding the encapsulated position.
148
149
  *
149
150
  * @public
150
- * @returns {*}
151
+ * @returns {Object}
151
152
  */
152
153
  get data() {
153
154
  return this._data;
154
155
  }
155
156
 
156
157
  /**
157
- * The current quote for the symbol of the encapsulated position.
158
+ * The most recent quote for the symbol of the encapsulated position.
158
159
  *
159
160
  * @public
160
- * @returns {null|{Object}}
161
+ * @returns {null|Object}
161
162
  */
162
163
  get quote() {
163
164
  return this._currentQuote;
164
165
  }
165
166
 
167
+ /**
168
+ * The second most recent quote for the symbol of the encapsulated position.
169
+ *
170
+ * @public
171
+ * @returns {null|Object}
172
+ */
173
+ get previousQuote() {
174
+ return this._previousQuote;
175
+ }
176
+
166
177
  updatePortfolio(portfolio) {
167
178
  assert.argumentIsRequired(portfolio, 'portfolio', Object);
168
179
  assert.argumentIsRequired(portfolio.portfolio, 'portfolio.portfolio', String);
@@ -200,6 +211,7 @@ module.exports = (() => {
200
211
  this._currentPricePrevious = this._currentPrice;
201
212
  this._currentPrice = quote.lastPrice;
202
213
 
214
+ this._previousQuote = this._currentQuote;
203
215
  this._currentQuote = quote;
204
216
 
205
217
  this._quoteChangedEvent.fire(this._currentQuote);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barchart/portfolio-api-common",
3
- "version": "1.2.83",
3
+ "version": "1.2.87",
4
4
  "description": "Common classes used by the Portfolio system",
5
5
  "author": {
6
6
  "name": "Bryan Ingle",
@@ -1418,16 +1418,18 @@ module.exports = (() => {
1418
1418
  *
1419
1419
  * @public
1420
1420
  * @param {Array.<PositionTreeDefinition>} definitions
1421
- * @param {Array.<Object>} portfolios
1422
- * @param {Array.<Object>} positions
1423
- * @param {Array.<Object>} summaries
1421
+ * @param {Array.<Object>} portfolios - The portfolios.
1422
+ * @param {Array.<Object>} positions - The positions (for all of the portfolios).
1423
+ * @param {Array.<Object>} summaries - The positions summaries (for all of the positions).
1424
+ * @param {PositionSummaryFrame} - If specified, locks the current (and previous) periods to a specific frame, use for reporting.
1424
1425
  */
1425
1426
  class PositionContainer {
1426
- constructor(definitions, portfolios, positions, summaries) {
1427
+ constructor(definitions, portfolios, positions, summaries, frame) {
1427
1428
  assert.argumentIsArray(definitions, 'definitions', PositionTreeDefinition, 'PositionTreeDefinition');
1428
1429
  assert.argumentIsArray(portfolios, 'portfolios');
1429
1430
  assert.argumentIsArray(positions, 'positions');
1430
1431
  assert.argumentIsArray(summaries, 'summaries');
1432
+ assert.argumentIsOptional(frame, 'frame', PositionSummaryFrame, 'PositionSummaryFrame');
1431
1433
 
1432
1434
  this._definitions = definitions;
1433
1435
 
@@ -1442,18 +1444,24 @@ module.exports = (() => {
1442
1444
  return map;
1443
1445
  }, { });
1444
1446
 
1445
- this._currentSummaryFrame = PositionSummaryFrame.YTD;
1447
+ this._currentSummaryFrame = frame || PositionSummaryFrame.YTD;
1446
1448
  this._currentSummaryRange = array.last(this._currentSummaryFrame.getRecentRanges(0));
1447
1449
 
1450
+ this._previousSummaryFrame = frame || PositionSummaryFrame.YEARLY;
1451
+ this._previousSummaryRanges = this._previousSummaryFrame.getRecentRanges(3);
1452
+
1453
+ if (this._currentSummaryFrame === this._previousSummaryFrame) {
1454
+ this._previousSummaryRanges.pop();
1455
+ } else {
1456
+ this._previousSummaryRanges.shift();
1457
+ }
1458
+
1448
1459
  this._summariesCurrent = summaries.reduce((map, summary) => {
1449
1460
  addSummaryCurrent(map, summary, this._currentSummaryFrame, this._currentSummaryRange);
1450
1461
 
1451
1462
  return map;
1452
1463
  }, { });
1453
1464
 
1454
- this._previousSummaryFrame = PositionSummaryFrame.YEARLY;
1455
- this._previousSummaryRanges = this._previousSummaryFrame.getRecentRanges(1);
1456
-
1457
1465
  this._summariesPrevious = summaries.reduce((map, summary) => {
1458
1466
  addSummaryPrevious(map, summary, this._previousSummaryFrame, this._previousSummaryRanges);
1459
1467
 
@@ -1654,8 +1662,17 @@ module.exports = (() => {
1654
1662
  }
1655
1663
 
1656
1664
  const existingBarchartSymbols = this.getPositionSymbols(false);
1665
+ const existingPositionItem = this._items.find(item => item.position.position === position.position);
1657
1666
 
1658
- removePositionItem.call(this, this._items.find(item => item.position.position === position.position));
1667
+ let currentQuote = null;
1668
+ let previousQuote = null;
1669
+
1670
+ if (existingPositionItem) {
1671
+ currentQuote = existingPositionItem.quote || null;
1672
+ previousQuote = existingPositionItem.previousQuote || null;
1673
+ }
1674
+
1675
+ removePositionItem.call(this, existingPositionItem);
1659
1676
 
1660
1677
  summaries.forEach((summary) => {
1661
1678
  addSummaryCurrent(this._summariesCurrent, summary, this._currentSummaryFrame, this._currentSummaryRange);
@@ -1667,6 +1684,14 @@ module.exports = (() => {
1667
1684
  addBarchartSymbol(this._symbols, item);
1668
1685
  addDisplaySymbol(this._symbolsDisplay, item);
1669
1686
 
1687
+ if (previousQuote !== null) {
1688
+ item.setQuote(previousQuote);
1689
+ }
1690
+
1691
+ if (currentQuote !== null) {
1692
+ item.setQuote(currentQuote);
1693
+ }
1694
+
1670
1695
  this._items.push(item);
1671
1696
 
1672
1697
  const createGroupOrInjectItem = (parentTree, treeDefinition, levelDefinitions) => {
@@ -1776,13 +1801,13 @@ module.exports = (() => {
1776
1801
  * @param {Object} position
1777
1802
  * @return {Boolean}
1778
1803
  */
1779
- getPositionLock(portfolio, position) {
1804
+ getPositionLock(position) {
1780
1805
  assert.argumentIsRequired(position, 'position', Object);
1781
1806
  assert.argumentIsRequired(position.position, 'position.position', String);
1782
1807
 
1783
1808
  const item = this._items.find(i => i.position.position === position.position);
1784
1809
 
1785
- return is.object(item) && item.locked;
1810
+ return is.object(item) && item.data.locked;
1786
1811
  }
1787
1812
 
1788
1813
  /**
@@ -2286,7 +2311,7 @@ module.exports = (() => {
2286
2311
  }
2287
2312
  }
2288
2313
 
2289
- function createPositionItem(position) {
2314
+ function createPositionItem(position, currentQuote, previousQuote) {
2290
2315
  const portfolio = this._portfolios[position.portfolio];
2291
2316
 
2292
2317
  let returnRef;
@@ -3239,8 +3264,8 @@ module.exports = (() => {
3239
3264
  'use strict';
3240
3265
 
3241
3266
  /**
3242
- * A container for a single position, which handles quote changes and
3243
- * notifies observers -- which are typically parent-level {@link PositionGroup}
3267
+ * A container for a single position, which handles quote changes and notifies
3268
+ * observers -- which are typically parent-level {@link PositionGroup}
3244
3269
  * instances.
3245
3270
  *
3246
3271
  * @public
@@ -3269,6 +3294,7 @@ module.exports = (() => {
3269
3294
  this._data.basis = null;
3270
3295
 
3271
3296
  this._currentQuote = null;
3297
+ this._previousQuote = null;
3272
3298
  this._currentPrice = null;
3273
3299
 
3274
3300
  this._data.currentPrice = null;
@@ -3352,7 +3378,7 @@ module.exports = (() => {
3352
3378
  }
3353
3379
 
3354
3380
  /**
3355
- * The year-to-date summary of the encapsulated position.
3381
+ * The current summary of the encapsulated position.
3356
3382
  *
3357
3383
  * @public
3358
3384
  * @returns {Object}
@@ -3362,7 +3388,7 @@ module.exports = (() => {
3362
3388
  }
3363
3389
 
3364
3390
  /**
3365
- * Previous year's summaries for the encapsulated position.
3391
+ * Previous summaries for the encapsulated position.
3366
3392
  *
3367
3393
  * @public
3368
3394
  * @returns {Object}
@@ -3375,22 +3401,32 @@ module.exports = (() => {
3375
3401
  * Various data regarding the encapsulated position.
3376
3402
  *
3377
3403
  * @public
3378
- * @returns {*}
3404
+ * @returns {Object}
3379
3405
  */
3380
3406
  get data() {
3381
3407
  return this._data;
3382
3408
  }
3383
3409
 
3384
3410
  /**
3385
- * The current quote for the symbol of the encapsulated position.
3411
+ * The most recent quote for the symbol of the encapsulated position.
3386
3412
  *
3387
3413
  * @public
3388
- * @returns {null|{Object}}
3414
+ * @returns {null|Object}
3389
3415
  */
3390
3416
  get quote() {
3391
3417
  return this._currentQuote;
3392
3418
  }
3393
3419
 
3420
+ /**
3421
+ * The second most recent quote for the symbol of the encapsulated position.
3422
+ *
3423
+ * @public
3424
+ * @returns {null|Object}
3425
+ */
3426
+ get previousQuote() {
3427
+ return this._previousQuote;
3428
+ }
3429
+
3394
3430
  updatePortfolio(portfolio) {
3395
3431
  assert.argumentIsRequired(portfolio, 'portfolio', Object);
3396
3432
  assert.argumentIsRequired(portfolio.portfolio, 'portfolio.portfolio', String);
@@ -3428,6 +3464,7 @@ module.exports = (() => {
3428
3464
  this._currentPricePrevious = this._currentPrice;
3429
3465
  this._currentPrice = quote.lastPrice;
3430
3466
 
3467
+ this._previousQuote = this._currentQuote;
3431
3468
  this._currentQuote = quote;
3432
3469
 
3433
3470
  this._quoteChangedEvent.fire(this._currentQuote);
@@ -17456,6 +17493,76 @@ describe('After the PositionSummaryFrame enumeration is initialized', () => {
17456
17493
  });
17457
17494
  });
17458
17495
  });
17496
+
17497
+ describe('and recent ranges are calculated', () => {
17498
+ let todayYear;
17499
+ let todayMonth;
17500
+ let todayDay;
17501
+
17502
+ beforeEach(() => {
17503
+ const today = new Date();
17504
+
17505
+ todayYear = today.getFullYear();
17506
+ todayMonth = today.getMonth() + 1;
17507
+ todayDay = today.getDate();
17508
+ });
17509
+
17510
+ describe('the most recent YTD frame', () => {
17511
+ let ranges;
17512
+
17513
+ beforeEach(() => {
17514
+ ranges = PositionSummaryFrame.YTD.getRecentRanges(0);
17515
+ });
17516
+
17517
+ it('should contain one range', () => {
17518
+ expect(ranges.length).toEqual(1);
17519
+ });
17520
+
17521
+ it('the range should begin at the end of last year', () => {
17522
+ expect(ranges[0].start.format()).toEqual(`${todayYear - 1}-12-31`);
17523
+ });
17524
+
17525
+ it('the range should end at the end of this year', () => {
17526
+ expect(ranges[0].end.format()).toEqual(`${todayYear}-12-31`);
17527
+ });
17528
+ });
17529
+
17530
+ describe('the three most recent YEARLY frames', () => {
17531
+ let ranges;
17532
+
17533
+ beforeEach(() => {
17534
+ ranges = PositionSummaryFrame.YEARLY.getRecentRanges(2);
17535
+ });
17536
+
17537
+ it('should contain three range', () => {
17538
+ expect(ranges.length).toEqual(3);
17539
+ });
17540
+
17541
+ it('the first range should begin at the end of this year (minus three years)', () => {
17542
+ expect(ranges[0].start.format()).toEqual(`${todayYear - 4}-12-31`);
17543
+ });
17544
+
17545
+ it('the first range should end at the end of this year (minus two years)', () => {
17546
+ expect(ranges[0].end.format()).toEqual(`${todayYear - 3}-12-31`);
17547
+ });
17548
+
17549
+ it('the second range should begin at the end of this year (minus two years)', () => {
17550
+ expect(ranges[1].start.format()).toEqual(`${todayYear - 3}-12-31`);
17551
+ });
17552
+
17553
+ it('the second range should end at the end of this year (minus one years)', () => {
17554
+ expect(ranges[1].end.format()).toEqual(`${todayYear - 2}-12-31`);
17555
+ });
17556
+
17557
+ it('the third range should begin at the end of the year before last', () => {
17558
+ expect(ranges[2].start.format()).toEqual(`${todayYear - 2}-12-31`);
17559
+ });
17560
+
17561
+ it('the third range should end at the end of last year', () => {
17562
+ expect(ranges[2].end.format()).toEqual(`${todayYear - 1}-12-31`);
17563
+ });
17564
+ });
17565
+ });
17459
17566
  });
17460
17567
 
17461
17568
  },{"./../../../lib/data/PositionSummaryFrame":3,"./../../../lib/data/TransactionType":4,"@barchart/common-js/lang/Day":21,"@barchart/common-js/lang/Decimal":22}],53:[function(require,module,exports){
@@ -17557,7 +17664,8 @@ describe('When requesting all the user-initiated transaction types', () => {
17557
17664
  const Currency = require('@barchart/common-js/lang/Currency'),
17558
17665
  Decimal = require('@barchart/common-js/lang/Decimal');
17559
17666
 
17560
- const InstrumentType = require('./../../../lib/data/InstrumentType');
17667
+ const InstrumentType = require('./../../../lib/data/InstrumentType'),
17668
+ PositionSummaryFrame = require('./../../../lib/data/PositionSummaryFrame');
17561
17669
 
17562
17670
  const PositionContainer = require('./../../../lib/processing/PositionContainer'),
17563
17671
  PositionLevelDefinition = require('./../../../lib/processing/definitions/PositionLevelDefinition'),
@@ -17590,6 +17698,37 @@ describe('When a position container data is gathered', () => {
17590
17698
  };
17591
17699
  }
17592
17700
 
17701
+ function getSummaries(position, frame, count) {
17702
+ const ranges = frame.getRecentRanges(count - 1);
17703
+
17704
+ return ranges.map((range) => {
17705
+ return {
17706
+ portfolio: position.portfolio,
17707
+ position: position.position,
17708
+ frame: frame,
17709
+ start: {
17710
+ date: range.start,
17711
+ open: position.snapshot.open,
17712
+ value: position.snapshot.value,
17713
+ basis: position.snapshot.basis
17714
+ },
17715
+ end: {
17716
+ date: range.end,
17717
+ open: position.snapshot.open,
17718
+ value: position.snapshot.value,
17719
+ basis: position.snapshot.basis
17720
+ },
17721
+ period: {
17722
+ buys: new Decimal(0),
17723
+ sells: new Decimal(0),
17724
+ income: new Decimal(0),
17725
+ realized: new Decimal(0),
17726
+ unrealized: new Decimal(0)
17727
+ }
17728
+ };
17729
+ });
17730
+ }
17731
+
17593
17732
  describe('for two portfolios, each with the same position, and the second portfolio with an addition position', () => {
17594
17733
  let portfolios;
17595
17734
  let positions;
@@ -17612,7 +17751,12 @@ describe('When a position container data is gathered', () => {
17612
17751
  getPosition('My Second Portfolio', 'TSLA')
17613
17752
  ];
17614
17753
 
17615
- summaries = [ ];
17754
+ summaries = positions.reduce((accumulator, position) => {
17755
+ accumulator = accumulator.concat(getSummaries(position, PositionSummaryFrame.YTD, 1));
17756
+ accumulator = accumulator.concat(getSummaries(position, PositionSummaryFrame.YEARLY, 3));
17757
+
17758
+ return accumulator;
17759
+ }, [ ]);
17616
17760
  });
17617
17761
 
17618
17762
  describe('and a container is created grouping by total, portfolio, and instrument', () => {
@@ -17659,11 +17803,49 @@ describe('When a position container data is gathered', () => {
17659
17803
  it('the "b" portfolio group should have two items', () => {
17660
17804
  expect(container.getGroup(name, [ 'totals', 'My Second Portfolio' ]).items.length).toEqual(2);
17661
17805
  });
17806
+
17807
+ describe('and an item is pulled for one of the positions', function() {
17808
+ let item;
17809
+
17810
+ let todayYear;
17811
+ let todayMonth;
17812
+ let todayDay;
17813
+
17814
+ beforeEach(() => {
17815
+ item = container.getGroup(name, [ 'totals', 'My First Portfolio' ]).items[0];
17816
+
17817
+ const today = new Date();
17818
+
17819
+ todayYear = today.getFullYear();
17820
+ todayMonth = today.getMonth() + 1;
17821
+ todayDay = today.getDate();
17822
+ });
17823
+
17824
+ it('the current summary should be a YTD summary for this year', () => {
17825
+ expect(item.currentSummary).toBe(summaries.find(s => s.position === item.position.position && s.frame === PositionSummaryFrame.YTD && s.start.date.format() === `${(todayYear - 1)}-12-31` && s.end.date.format() === `${(todayYear - 0)}-12-31`));
17826
+ });
17827
+
17828
+ it('should have two previous summaries', () => {
17829
+ expect(item.previousSummaries.length).toEqual(3);
17830
+ });
17831
+
17832
+ it('the previous (x1) summary should be a YEARLY summary for three years ago', () => {
17833
+ expect(item.previousSummaries[0]).toBe(summaries.find(s => s.position === item.position.position && s.frame === PositionSummaryFrame.YEARLY && s.start.date.format() === `${(todayYear - 4)}-12-31` && s.end.date.format() === `${(todayYear - 3)}-12-31`));
17834
+ });
17835
+
17836
+ it('the previous (x2) summary should be a YEARLY summary for the year before last', () => {
17837
+ expect(item.previousSummaries[1]).toBe(summaries.find(s => s.position === item.position.position && s.frame === PositionSummaryFrame.YEARLY && s.start.date.format() === `${(todayYear - 3)}-12-31` && s.end.date.format() === `${(todayYear - 2)}-12-31`));
17838
+ });
17839
+
17840
+ it('the previous (x3) summary should be a YEARLY summary for last year', () => {
17841
+ expect(item.previousSummaries[2]).toBe(summaries.find(s => s.position === item.position.position && s.frame === PositionSummaryFrame.YEARLY && s.start.date.format() === `${(todayYear - 2)}-12-31` && s.end.date.format() === `${(todayYear - 1)}-12-31`));
17842
+ });
17843
+ });
17662
17844
  });
17663
17845
  });
17664
17846
  });
17665
17847
 
17666
- },{"./../../../lib/data/InstrumentType":1,"./../../../lib/processing/PositionContainer":6,"./../../../lib/processing/definitions/PositionLevelDefinition":9,"./../../../lib/processing/definitions/PositionLevelType":10,"./../../../lib/processing/definitions/PositionTreeDefinition":11,"@barchart/common-js/lang/Currency":20,"@barchart/common-js/lang/Decimal":22}],55:[function(require,module,exports){
17848
+ },{"./../../../lib/data/InstrumentType":1,"./../../../lib/data/PositionSummaryFrame":3,"./../../../lib/processing/PositionContainer":6,"./../../../lib/processing/definitions/PositionLevelDefinition":9,"./../../../lib/processing/definitions/PositionLevelType":10,"./../../../lib/processing/definitions/PositionTreeDefinition":11,"@barchart/common-js/lang/Currency":20,"@barchart/common-js/lang/Decimal":22}],55:[function(require,module,exports){
17667
17849
  const Currency = require('@barchart/common-js/lang/Currency'),
17668
17850
  Day = require('@barchart/common-js/lang/Day'),
17669
17851
  Decimal = require('@barchart/common-js/lang/Decimal');
@@ -352,4 +352,74 @@ describe('After the PositionSummaryFrame enumeration is initialized', () => {
352
352
  });
353
353
  });
354
354
  });
355
+
356
+ describe('and recent ranges are calculated', () => {
357
+ let todayYear;
358
+ let todayMonth;
359
+ let todayDay;
360
+
361
+ beforeEach(() => {
362
+ const today = new Date();
363
+
364
+ todayYear = today.getFullYear();
365
+ todayMonth = today.getMonth() + 1;
366
+ todayDay = today.getDate();
367
+ });
368
+
369
+ describe('the most recent YTD frame', () => {
370
+ let ranges;
371
+
372
+ beforeEach(() => {
373
+ ranges = PositionSummaryFrame.YTD.getRecentRanges(0);
374
+ });
375
+
376
+ it('should contain one range', () => {
377
+ expect(ranges.length).toEqual(1);
378
+ });
379
+
380
+ it('the range should begin at the end of last year', () => {
381
+ expect(ranges[0].start.format()).toEqual(`${todayYear - 1}-12-31`);
382
+ });
383
+
384
+ it('the range should end at the end of this year', () => {
385
+ expect(ranges[0].end.format()).toEqual(`${todayYear}-12-31`);
386
+ });
387
+ });
388
+
389
+ describe('the three most recent YEARLY frames', () => {
390
+ let ranges;
391
+
392
+ beforeEach(() => {
393
+ ranges = PositionSummaryFrame.YEARLY.getRecentRanges(2);
394
+ });
395
+
396
+ it('should contain three range', () => {
397
+ expect(ranges.length).toEqual(3);
398
+ });
399
+
400
+ it('the first range should begin at the end of this year (minus three years)', () => {
401
+ expect(ranges[0].start.format()).toEqual(`${todayYear - 4}-12-31`);
402
+ });
403
+
404
+ it('the first range should end at the end of this year (minus two years)', () => {
405
+ expect(ranges[0].end.format()).toEqual(`${todayYear - 3}-12-31`);
406
+ });
407
+
408
+ it('the second range should begin at the end of this year (minus two years)', () => {
409
+ expect(ranges[1].start.format()).toEqual(`${todayYear - 3}-12-31`);
410
+ });
411
+
412
+ it('the second range should end at the end of this year (minus one years)', () => {
413
+ expect(ranges[1].end.format()).toEqual(`${todayYear - 2}-12-31`);
414
+ });
415
+
416
+ it('the third range should begin at the end of the year before last', () => {
417
+ expect(ranges[2].start.format()).toEqual(`${todayYear - 2}-12-31`);
418
+ });
419
+
420
+ it('the third range should end at the end of last year', () => {
421
+ expect(ranges[2].end.format()).toEqual(`${todayYear - 1}-12-31`);
422
+ });
423
+ });
424
+ });
355
425
  });
@@ -1,7 +1,8 @@
1
1
  const Currency = require('@barchart/common-js/lang/Currency'),
2
2
  Decimal = require('@barchart/common-js/lang/Decimal');
3
3
 
4
- const InstrumentType = require('./../../../lib/data/InstrumentType');
4
+ const InstrumentType = require('./../../../lib/data/InstrumentType'),
5
+ PositionSummaryFrame = require('./../../../lib/data/PositionSummaryFrame');
5
6
 
6
7
  const PositionContainer = require('./../../../lib/processing/PositionContainer'),
7
8
  PositionLevelDefinition = require('./../../../lib/processing/definitions/PositionLevelDefinition'),
@@ -34,6 +35,37 @@ describe('When a position container data is gathered', () => {
34
35
  };
35
36
  }
36
37
 
38
+ function getSummaries(position, frame, count) {
39
+ const ranges = frame.getRecentRanges(count - 1);
40
+
41
+ return ranges.map((range) => {
42
+ return {
43
+ portfolio: position.portfolio,
44
+ position: position.position,
45
+ frame: frame,
46
+ start: {
47
+ date: range.start,
48
+ open: position.snapshot.open,
49
+ value: position.snapshot.value,
50
+ basis: position.snapshot.basis
51
+ },
52
+ end: {
53
+ date: range.end,
54
+ open: position.snapshot.open,
55
+ value: position.snapshot.value,
56
+ basis: position.snapshot.basis
57
+ },
58
+ period: {
59
+ buys: new Decimal(0),
60
+ sells: new Decimal(0),
61
+ income: new Decimal(0),
62
+ realized: new Decimal(0),
63
+ unrealized: new Decimal(0)
64
+ }
65
+ };
66
+ });
67
+ }
68
+
37
69
  describe('for two portfolios, each with the same position, and the second portfolio with an addition position', () => {
38
70
  let portfolios;
39
71
  let positions;
@@ -56,7 +88,12 @@ describe('When a position container data is gathered', () => {
56
88
  getPosition('My Second Portfolio', 'TSLA')
57
89
  ];
58
90
 
59
- summaries = [ ];
91
+ summaries = positions.reduce((accumulator, position) => {
92
+ accumulator = accumulator.concat(getSummaries(position, PositionSummaryFrame.YTD, 1));
93
+ accumulator = accumulator.concat(getSummaries(position, PositionSummaryFrame.YEARLY, 3));
94
+
95
+ return accumulator;
96
+ }, [ ]);
60
97
  });
61
98
 
62
99
  describe('and a container is created grouping by total, portfolio, and instrument', () => {
@@ -103,6 +140,44 @@ describe('When a position container data is gathered', () => {
103
140
  it('the "b" portfolio group should have two items', () => {
104
141
  expect(container.getGroup(name, [ 'totals', 'My Second Portfolio' ]).items.length).toEqual(2);
105
142
  });
143
+
144
+ describe('and an item is pulled for one of the positions', function() {
145
+ let item;
146
+
147
+ let todayYear;
148
+ let todayMonth;
149
+ let todayDay;
150
+
151
+ beforeEach(() => {
152
+ item = container.getGroup(name, [ 'totals', 'My First Portfolio' ]).items[0];
153
+
154
+ const today = new Date();
155
+
156
+ todayYear = today.getFullYear();
157
+ todayMonth = today.getMonth() + 1;
158
+ todayDay = today.getDate();
159
+ });
160
+
161
+ it('the current summary should be a YTD summary for this year', () => {
162
+ expect(item.currentSummary).toBe(summaries.find(s => s.position === item.position.position && s.frame === PositionSummaryFrame.YTD && s.start.date.format() === `${(todayYear - 1)}-12-31` && s.end.date.format() === `${(todayYear - 0)}-12-31`));
163
+ });
164
+
165
+ it('should have two previous summaries', () => {
166
+ expect(item.previousSummaries.length).toEqual(3);
167
+ });
168
+
169
+ it('the previous (x1) summary should be a YEARLY summary for three years ago', () => {
170
+ expect(item.previousSummaries[0]).toBe(summaries.find(s => s.position === item.position.position && s.frame === PositionSummaryFrame.YEARLY && s.start.date.format() === `${(todayYear - 4)}-12-31` && s.end.date.format() === `${(todayYear - 3)}-12-31`));
171
+ });
172
+
173
+ it('the previous (x2) summary should be a YEARLY summary for the year before last', () => {
174
+ expect(item.previousSummaries[1]).toBe(summaries.find(s => s.position === item.position.position && s.frame === PositionSummaryFrame.YEARLY && s.start.date.format() === `${(todayYear - 3)}-12-31` && s.end.date.format() === `${(todayYear - 2)}-12-31`));
175
+ });
176
+
177
+ it('the previous (x3) summary should be a YEARLY summary for last year', () => {
178
+ expect(item.previousSummaries[2]).toBe(summaries.find(s => s.position === item.position.position && s.frame === PositionSummaryFrame.YEARLY && s.start.date.format() === `${(todayYear - 2)}-12-31` && s.end.date.format() === `${(todayYear - 1)}-12-31`));
179
+ });
180
+ });
106
181
  });
107
182
  });
108
183
  });