@barchart/portfolio-api-common 1.2.82 → 1.2.86

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
 
@@ -276,7 +284,7 @@ module.exports = (() => {
276
284
 
277
285
  const existingBarchartSymbols = this.getPositionSymbols(false);
278
286
 
279
- removePositionItem.call(this, this._items.find((item) => item.position.position === position.position));
287
+ removePositionItem.call(this, this._items.find(item => item.position.position === position.position));
280
288
 
281
289
  summaries.forEach((summary) => {
282
290
  addSummaryCurrent(this._summariesCurrent, summary, this._currentSummaryFrame, this._currentSummaryRange);
@@ -336,7 +344,7 @@ module.exports = (() => {
336
344
  assert.argumentIsRequired(position, 'position', Object);
337
345
  assert.argumentIsRequired(position.position, 'position.position', String);
338
346
 
339
- removePositionItem.call(this, this._items.find((item) => item.position.position === position.position));
347
+ removePositionItem.call(this, this._items.find(item => item.position.position === position.position));
340
348
 
341
349
  recalculatePercentages.call(this);
342
350
  }
@@ -391,17 +399,19 @@ module.exports = (() => {
391
399
  }
392
400
 
393
401
  /**
394
- * Returns all positions which are currently locked (for editing).
402
+ * Returns a position's lock status.
395
403
  *
396
404
  * @public
397
- * @returns {Array.<Object>}
405
+ * @param {Object} position
406
+ * @return {Boolean}
398
407
  */
399
- getLockedPositions() {
400
- return this._items.filter((i) => {
401
- return i.data.locked;
402
- }).map((i) => {
403
- return i.position;
404
- });
408
+ getPositionLock(position) {
409
+ assert.argumentIsRequired(position, 'position', Object);
410
+ assert.argumentIsRequired(position.position, 'position.position', String);
411
+
412
+ const item = this._items.find(i => i.position.position === position.position);
413
+
414
+ return is.object(item) && item.data.locked;
405
415
  }
406
416
 
407
417
  /**
@@ -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
@@ -124,7 +124,7 @@ module.exports = (() => {
124
124
  }
125
125
 
126
126
  /**
127
- * The year-to-date summary of the encapsulated position.
127
+ * The current summary of the encapsulated position.
128
128
  *
129
129
  * @public
130
130
  * @returns {Object}
@@ -134,7 +134,7 @@ module.exports = (() => {
134
134
  }
135
135
 
136
136
  /**
137
- * Previous year's summaries for the encapsulated position.
137
+ * Previous summaries for the encapsulated position.
138
138
  *
139
139
  * @public
140
140
  * @returns {Object}
@@ -147,7 +147,7 @@ module.exports = (() => {
147
147
  * Various data regarding the encapsulated position.
148
148
  *
149
149
  * @public
150
- * @returns {*}
150
+ * @returns {Object}
151
151
  */
152
152
  get data() {
153
153
  return this._data;
@@ -157,7 +157,7 @@ module.exports = (() => {
157
157
  * The current quote for the symbol of the encapsulated position.
158
158
  *
159
159
  * @public
160
- * @returns {null|{Object}}
160
+ * @returns {null|Object}
161
161
  */
162
162
  get quote() {
163
163
  return this._currentQuote;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barchart/portfolio-api-common",
3
- "version": "1.2.82",
3
+ "version": "1.2.86",
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
 
@@ -1655,7 +1663,7 @@ module.exports = (() => {
1655
1663
 
1656
1664
  const existingBarchartSymbols = this.getPositionSymbols(false);
1657
1665
 
1658
- removePositionItem.call(this, this._items.find((item) => item.position.position === position.position));
1666
+ removePositionItem.call(this, this._items.find(item => item.position.position === position.position));
1659
1667
 
1660
1668
  summaries.forEach((summary) => {
1661
1669
  addSummaryCurrent(this._summariesCurrent, summary, this._currentSummaryFrame, this._currentSummaryRange);
@@ -1715,7 +1723,7 @@ module.exports = (() => {
1715
1723
  assert.argumentIsRequired(position, 'position', Object);
1716
1724
  assert.argumentIsRequired(position.position, 'position.position', String);
1717
1725
 
1718
- removePositionItem.call(this, this._items.find((item) => item.position.position === position.position));
1726
+ removePositionItem.call(this, this._items.find(item => item.position.position === position.position));
1719
1727
 
1720
1728
  recalculatePercentages.call(this);
1721
1729
  }
@@ -1770,17 +1778,19 @@ module.exports = (() => {
1770
1778
  }
1771
1779
 
1772
1780
  /**
1773
- * Returns all positions which are currently locked (for editing).
1781
+ * Returns a position's lock status.
1774
1782
  *
1775
1783
  * @public
1776
- * @returns {Array.<Object>}
1784
+ * @param {Object} position
1785
+ * @return {Boolean}
1777
1786
  */
1778
- getLockedPositions() {
1779
- return this._items.filter((i) => {
1780
- return i.data.locked;
1781
- }).map((i) => {
1782
- return i.position;
1783
- });
1787
+ getPositionLock(position) {
1788
+ assert.argumentIsRequired(position, 'position', Object);
1789
+ assert.argumentIsRequired(position.position, 'position.position', String);
1790
+
1791
+ const item = this._items.find(i => i.position.position === position.position);
1792
+
1793
+ return is.object(item) && item.data.locked;
1784
1794
  }
1785
1795
 
1786
1796
  /**
@@ -3237,8 +3247,8 @@ module.exports = (() => {
3237
3247
  'use strict';
3238
3248
 
3239
3249
  /**
3240
- * A container for a single position, which handles quote changes and
3241
- * notifies observers -- which are typically parent-level {@link PositionGroup}
3250
+ * A container for a single position, which handles quote changes and notifies
3251
+ * observers -- which are typically parent-level {@link PositionGroup}
3242
3252
  * instances.
3243
3253
  *
3244
3254
  * @public
@@ -3350,7 +3360,7 @@ module.exports = (() => {
3350
3360
  }
3351
3361
 
3352
3362
  /**
3353
- * The year-to-date summary of the encapsulated position.
3363
+ * The current summary of the encapsulated position.
3354
3364
  *
3355
3365
  * @public
3356
3366
  * @returns {Object}
@@ -3360,7 +3370,7 @@ module.exports = (() => {
3360
3370
  }
3361
3371
 
3362
3372
  /**
3363
- * Previous year's summaries for the encapsulated position.
3373
+ * Previous summaries for the encapsulated position.
3364
3374
  *
3365
3375
  * @public
3366
3376
  * @returns {Object}
@@ -3373,7 +3383,7 @@ module.exports = (() => {
3373
3383
  * Various data regarding the encapsulated position.
3374
3384
  *
3375
3385
  * @public
3376
- * @returns {*}
3386
+ * @returns {Object}
3377
3387
  */
3378
3388
  get data() {
3379
3389
  return this._data;
@@ -3383,7 +3393,7 @@ module.exports = (() => {
3383
3393
  * The current quote for the symbol of the encapsulated position.
3384
3394
  *
3385
3395
  * @public
3386
- * @returns {null|{Object}}
3396
+ * @returns {null|Object}
3387
3397
  */
3388
3398
  get quote() {
3389
3399
  return this._currentQuote;
@@ -17454,6 +17464,76 @@ describe('After the PositionSummaryFrame enumeration is initialized', () => {
17454
17464
  });
17455
17465
  });
17456
17466
  });
17467
+
17468
+ describe('and recent ranges are calculated', () => {
17469
+ let todayYear;
17470
+ let todayMonth;
17471
+ let todayDay;
17472
+
17473
+ beforeEach(() => {
17474
+ const today = new Date();
17475
+
17476
+ todayYear = today.getFullYear();
17477
+ todayMonth = today.getMonth() + 1;
17478
+ todayDay = today.getDate();
17479
+ });
17480
+
17481
+ describe('the most recent YTD frame', () => {
17482
+ let ranges;
17483
+
17484
+ beforeEach(() => {
17485
+ ranges = PositionSummaryFrame.YTD.getRecentRanges(0);
17486
+ });
17487
+
17488
+ it('should contain one range', () => {
17489
+ expect(ranges.length).toEqual(1);
17490
+ });
17491
+
17492
+ it('the range should begin at the end of last year', () => {
17493
+ expect(ranges[0].start.format()).toEqual(`${todayYear - 1}-12-31`);
17494
+ });
17495
+
17496
+ it('the range should end at the end of this year', () => {
17497
+ expect(ranges[0].end.format()).toEqual(`${todayYear}-12-31`);
17498
+ });
17499
+ });
17500
+
17501
+ describe('the three most recent YEARLY frames', () => {
17502
+ let ranges;
17503
+
17504
+ beforeEach(() => {
17505
+ ranges = PositionSummaryFrame.YEARLY.getRecentRanges(2);
17506
+ });
17507
+
17508
+ it('should contain three range', () => {
17509
+ expect(ranges.length).toEqual(3);
17510
+ });
17511
+
17512
+ it('the first range should begin at the end of this year (minus three years)', () => {
17513
+ expect(ranges[0].start.format()).toEqual(`${todayYear - 4}-12-31`);
17514
+ });
17515
+
17516
+ it('the first range should end at the end of this year (minus two years)', () => {
17517
+ expect(ranges[0].end.format()).toEqual(`${todayYear - 3}-12-31`);
17518
+ });
17519
+
17520
+ it('the second range should begin at the end of this year (minus two years)', () => {
17521
+ expect(ranges[1].start.format()).toEqual(`${todayYear - 3}-12-31`);
17522
+ });
17523
+
17524
+ it('the second range should end at the end of this year (minus one years)', () => {
17525
+ expect(ranges[1].end.format()).toEqual(`${todayYear - 2}-12-31`);
17526
+ });
17527
+
17528
+ it('the third range should begin at the end of the year before last', () => {
17529
+ expect(ranges[2].start.format()).toEqual(`${todayYear - 2}-12-31`);
17530
+ });
17531
+
17532
+ it('the third range should end at the end of last year', () => {
17533
+ expect(ranges[2].end.format()).toEqual(`${todayYear - 1}-12-31`);
17534
+ });
17535
+ });
17536
+ });
17457
17537
  });
17458
17538
 
17459
17539
  },{"./../../../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){
@@ -17555,7 +17635,8 @@ describe('When requesting all the user-initiated transaction types', () => {
17555
17635
  const Currency = require('@barchart/common-js/lang/Currency'),
17556
17636
  Decimal = require('@barchart/common-js/lang/Decimal');
17557
17637
 
17558
- const InstrumentType = require('./../../../lib/data/InstrumentType');
17638
+ const InstrumentType = require('./../../../lib/data/InstrumentType'),
17639
+ PositionSummaryFrame = require('./../../../lib/data/PositionSummaryFrame');
17559
17640
 
17560
17641
  const PositionContainer = require('./../../../lib/processing/PositionContainer'),
17561
17642
  PositionLevelDefinition = require('./../../../lib/processing/definitions/PositionLevelDefinition'),
@@ -17588,6 +17669,37 @@ describe('When a position container data is gathered', () => {
17588
17669
  };
17589
17670
  }
17590
17671
 
17672
+ function getSummaries(position, frame, count) {
17673
+ const ranges = frame.getRecentRanges(count - 1);
17674
+
17675
+ return ranges.map((range) => {
17676
+ return {
17677
+ portfolio: position.portfolio,
17678
+ position: position.position,
17679
+ frame: frame,
17680
+ start: {
17681
+ date: range.start,
17682
+ open: position.snapshot.open,
17683
+ value: position.snapshot.value,
17684
+ basis: position.snapshot.basis
17685
+ },
17686
+ end: {
17687
+ date: range.end,
17688
+ open: position.snapshot.open,
17689
+ value: position.snapshot.value,
17690
+ basis: position.snapshot.basis
17691
+ },
17692
+ period: {
17693
+ buys: new Decimal(0),
17694
+ sells: new Decimal(0),
17695
+ income: new Decimal(0),
17696
+ realized: new Decimal(0),
17697
+ unrealized: new Decimal(0)
17698
+ }
17699
+ };
17700
+ });
17701
+ }
17702
+
17591
17703
  describe('for two portfolios, each with the same position, and the second portfolio with an addition position', () => {
17592
17704
  let portfolios;
17593
17705
  let positions;
@@ -17610,7 +17722,12 @@ describe('When a position container data is gathered', () => {
17610
17722
  getPosition('My Second Portfolio', 'TSLA')
17611
17723
  ];
17612
17724
 
17613
- summaries = [ ];
17725
+ summaries = positions.reduce((accumulator, position) => {
17726
+ accumulator = accumulator.concat(getSummaries(position, PositionSummaryFrame.YTD, 1));
17727
+ accumulator = accumulator.concat(getSummaries(position, PositionSummaryFrame.YEARLY, 3));
17728
+
17729
+ return accumulator;
17730
+ }, [ ]);
17614
17731
  });
17615
17732
 
17616
17733
  describe('and a container is created grouping by total, portfolio, and instrument', () => {
@@ -17657,11 +17774,49 @@ describe('When a position container data is gathered', () => {
17657
17774
  it('the "b" portfolio group should have two items', () => {
17658
17775
  expect(container.getGroup(name, [ 'totals', 'My Second Portfolio' ]).items.length).toEqual(2);
17659
17776
  });
17777
+
17778
+ describe('and an item is pulled for one of the positions', function() {
17779
+ let item;
17780
+
17781
+ let todayYear;
17782
+ let todayMonth;
17783
+ let todayDay;
17784
+
17785
+ beforeEach(() => {
17786
+ item = container.getGroup(name, [ 'totals', 'My First Portfolio' ]).items[0];
17787
+
17788
+ const today = new Date();
17789
+
17790
+ todayYear = today.getFullYear();
17791
+ todayMonth = today.getMonth() + 1;
17792
+ todayDay = today.getDate();
17793
+ });
17794
+
17795
+ it('the current summary should be a YTD summary for this year', () => {
17796
+ 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`));
17797
+ });
17798
+
17799
+ it('should have two previous summaries', () => {
17800
+ expect(item.previousSummaries.length).toEqual(3);
17801
+ });
17802
+
17803
+ it('the previous (x1) summary should be a YEARLY summary for three years ago', () => {
17804
+ 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`));
17805
+ });
17806
+
17807
+ it('the previous (x2) summary should be a YEARLY summary for the year before last', () => {
17808
+ 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`));
17809
+ });
17810
+
17811
+ it('the previous (x3) summary should be a YEARLY summary for last year', () => {
17812
+ 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`));
17813
+ });
17814
+ });
17660
17815
  });
17661
17816
  });
17662
17817
  });
17663
17818
 
17664
- },{"./../../../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){
17819
+ },{"./../../../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){
17665
17820
  const Currency = require('@barchart/common-js/lang/Currency'),
17666
17821
  Day = require('@barchart/common-js/lang/Day'),
17667
17822
  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
  });