@barchart/portfolio-api-common 1.0.67 → 1.0.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/data/PositionSummaryFrame.js +76 -5
- package/lib/processing/PositionContainer.js +21 -8
- package/lib/processing/PositionGroup.js +42 -14
- package/lib/processing/PositionItem.js +26 -2
- package/package.json +1 -1
- package/test/SpecRunner.js +170 -32
- package/test/specs/processing/PositionContainerSpec.js +3 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const array = require('@barchart/common-js/lang/array'),
|
|
2
2
|
assert = require('@barchart/common-js/lang/assert'),
|
|
3
3
|
Day = require('@barchart/common-js/lang/Day'),
|
|
4
|
+
Decimal = require('@barchart/common-js/lang/Decimal'),
|
|
4
5
|
Enum = require('@barchart/common-js/lang/Enum'),
|
|
5
6
|
is = require('@barchart/common-js/lang/is');
|
|
6
7
|
|
|
@@ -16,23 +17,68 @@ module.exports = (() => {
|
|
|
16
17
|
* @param {String} description
|
|
17
18
|
* @param {Function} rangeCalculator
|
|
18
19
|
* @param {Function} startDateCalculator
|
|
20
|
+
* @param {Function} descriptionCalculator
|
|
19
21
|
*/
|
|
20
22
|
class PositionSummaryFrame extends Enum {
|
|
21
|
-
constructor(code, description, rangeCalculator, startDateCalculator) {
|
|
23
|
+
constructor(code, description, rangeCalculator, startDateCalculator, descriptionCalculator) {
|
|
22
24
|
super(code, description);
|
|
23
25
|
|
|
24
26
|
assert.argumentIsRequired(rangeCalculator, 'rangeCalculator', Function);
|
|
27
|
+
assert.argumentIsRequired(startDateCalculator, 'startDateCalculator', Function);
|
|
28
|
+
assert.argumentIsRequired(descriptionCalculator, 'descriptionCalculator', Function);
|
|
25
29
|
|
|
26
30
|
this._rangeCalculator = rangeCalculator;
|
|
27
31
|
this._startDateCalculator = startDateCalculator;
|
|
32
|
+
this._descriptionCalculator = descriptionCalculator;
|
|
28
33
|
}
|
|
29
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Returns a human-readable description of the frame, given
|
|
37
|
+
* start and end dates.
|
|
38
|
+
*
|
|
39
|
+
* @public
|
|
40
|
+
* @param {Day} startDate
|
|
41
|
+
* @param {Day} endDate
|
|
42
|
+
* @return {String}
|
|
43
|
+
*/
|
|
44
|
+
describeRange(startDate, endDate) {
|
|
45
|
+
return this._descriptionCalculator(startDate, endDate);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Returns the most recent ranges for the frame.
|
|
50
|
+
*
|
|
51
|
+
* @public
|
|
52
|
+
* @param {Number} periods
|
|
53
|
+
* @returns {Array.<PositionSummaryRange>}
|
|
54
|
+
*/
|
|
55
|
+
getRecentRanges(periods) {
|
|
56
|
+
const startDate = this.getStartDate(periods);
|
|
57
|
+
const transaction = { date: startDate, snapshot: { open: Decimal.ONE } };
|
|
58
|
+
|
|
59
|
+
return this.getRanges([ transaction ]);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Returns the ranges for the set of {@link Transaction} objects.
|
|
64
|
+
*
|
|
65
|
+
* @public
|
|
66
|
+
* @param {Array.<Transaction>} transactions
|
|
67
|
+
* @returns {Array.<PositionSummaryRange>}
|
|
68
|
+
*/
|
|
30
69
|
getRanges(transactions) {
|
|
31
70
|
assert.argumentIsArray(transactions, 'transactions');
|
|
32
71
|
|
|
33
72
|
return this._rangeCalculator(getFilteredTransactions(transactions));
|
|
34
73
|
}
|
|
35
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Returns the start date for a frame, a given number of periods ago.
|
|
77
|
+
*
|
|
78
|
+
* @public
|
|
79
|
+
* @param {Number} periods
|
|
80
|
+
* @returns {Day}
|
|
81
|
+
*/
|
|
36
82
|
getStartDate(periods) {
|
|
37
83
|
assert.argumentIsRequired(periods, 'periods', Number);
|
|
38
84
|
|
|
@@ -84,10 +130,19 @@ module.exports = (() => {
|
|
|
84
130
|
}
|
|
85
131
|
}
|
|
86
132
|
|
|
87
|
-
const yearly = new PositionSummaryFrame('YEARLY', 'year', getYearlyRanges, getYearlyStartDate);
|
|
88
|
-
const quarterly = new PositionSummaryFrame('QUARTER', 'quarter', getQuarterlyRanges, getQuarterlyStartDate);
|
|
89
|
-
const monthly = new PositionSummaryFrame('MONTH', 'month', getMonthlyRanges, getMonthlyStartDate);
|
|
90
|
-
const ytd = new PositionSummaryFrame('YTD', 'year-to-date', getYearToDateRanges, getYearToDateStartDate);
|
|
133
|
+
const yearly = new PositionSummaryFrame('YEARLY', 'year', getYearlyRanges, getYearlyStartDate, getYearlyRangeDescription);
|
|
134
|
+
const quarterly = new PositionSummaryFrame('QUARTER', 'quarter', getQuarterlyRanges, getQuarterlyStartDate, getQuarterlyRangeDescription);
|
|
135
|
+
const monthly = new PositionSummaryFrame('MONTH', 'month', getMonthlyRanges, getMonthlyStartDate, getMonthlyRangeDescription);
|
|
136
|
+
const ytd = new PositionSummaryFrame('YTD', 'year-to-date', getYearToDateRanges, getYearToDateStartDate, getYearToDateRangeDescription);
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* The start and and date for a {@link PositionSummaryFrame}
|
|
140
|
+
*
|
|
141
|
+
* @typedef PositionSummaryRange
|
|
142
|
+
* @type {Object}
|
|
143
|
+
* @property {Day} start
|
|
144
|
+
* @property {Day} end
|
|
145
|
+
*/
|
|
91
146
|
|
|
92
147
|
function getRange(start, end) {
|
|
93
148
|
return {
|
|
@@ -171,6 +226,22 @@ module.exports = (() => {
|
|
|
171
226
|
return null;
|
|
172
227
|
}
|
|
173
228
|
|
|
229
|
+
function getYearlyRangeDescription(startDate, endDate) {
|
|
230
|
+
return endDate.year.toString();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function getQuarterlyRangeDescription(startDate, endDate) {
|
|
234
|
+
return '';
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function getMonthlyRangeDescription(startDate, endDate) {
|
|
238
|
+
return '';
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function getYearToDateRangeDescription(startDate, endDate) {
|
|
242
|
+
return '';
|
|
243
|
+
}
|
|
244
|
+
|
|
174
245
|
function getFilteredTransactions(transactions) {
|
|
175
246
|
return transactions.reduce((filtered, transaction) => {
|
|
176
247
|
if (!transaction.snapshot.open.getIsZero() || transaction.type.closing) {
|
|
@@ -6,7 +6,7 @@ const array = require('@barchart/common-js/lang/array'),
|
|
|
6
6
|
is = require('@barchart/common-js/lang/is'),
|
|
7
7
|
Tree = require('@barchart/common-js/collections/Tree');
|
|
8
8
|
|
|
9
|
-
const
|
|
9
|
+
const PositionSummaryFrame = require('./../data/PositionSummaryFrame');
|
|
10
10
|
|
|
11
11
|
const PositionGroup = require('./PositionGroup'),
|
|
12
12
|
PositionItem = require('./PositionItem');
|
|
@@ -18,10 +18,13 @@ module.exports = (() => {
|
|
|
18
18
|
* @public
|
|
19
19
|
*/
|
|
20
20
|
class PositionContainer {
|
|
21
|
-
constructor(portfolios, positions, summaries, definitions, defaultCurrency) {
|
|
21
|
+
constructor(portfolios, positions, summaries, definitions, defaultCurrency, summaryFrameType) {
|
|
22
22
|
this._definitions = definitions;
|
|
23
23
|
this._defaultCurrency = defaultCurrency || Currency.CAD;
|
|
24
24
|
|
|
25
|
+
this._summaryFrame = summaryFrameType || PositionSummaryFrame.YEARLY;
|
|
26
|
+
this._summaryRanges = this._summaryFrame.getRecentRanges(1);
|
|
27
|
+
|
|
25
28
|
this._portfolios = portfolios.reduce((map, portfolio) => {
|
|
26
29
|
map[portfolio.portfolio] = portfolio;
|
|
27
30
|
|
|
@@ -29,13 +32,19 @@ module.exports = (() => {
|
|
|
29
32
|
}, { });
|
|
30
33
|
|
|
31
34
|
this._summaries = summaries.reduce((map, summary) => {
|
|
32
|
-
|
|
35
|
+
if (this._summaryFrame === summary.frame) {
|
|
36
|
+
const key = summary.position;
|
|
33
37
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
if (!map.hasOwnProperty(key)) {
|
|
39
|
+
map[key] = getSummaryArray(this._summaryRanges);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const index = this._summaryRanges.findIndex(r => r.start.getIsEqual(summary.start.date) && r.end.getIsEqual(summary.end.date));
|
|
37
43
|
|
|
38
|
-
|
|
44
|
+
if (!(index < 0)) {
|
|
45
|
+
map[key][index] = summary;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
39
48
|
|
|
40
49
|
return map;
|
|
41
50
|
}, { });
|
|
@@ -44,7 +53,7 @@ module.exports = (() => {
|
|
|
44
53
|
const portfolio = this._portfolios[position.portfolio];
|
|
45
54
|
|
|
46
55
|
if (position) {
|
|
47
|
-
const summaries = this._summaries[position.position] ||
|
|
56
|
+
const summaries = this._summaries[position.position] || getSummaryArray(this._summaryRanges);
|
|
48
57
|
|
|
49
58
|
items.push(new PositionItem(portfolio, position, summaries));
|
|
50
59
|
}
|
|
@@ -206,5 +215,9 @@ module.exports = (() => {
|
|
|
206
215
|
}
|
|
207
216
|
}
|
|
208
217
|
|
|
218
|
+
function getSummaryArray(ranges) {
|
|
219
|
+
return ranges.map(range => null);
|
|
220
|
+
}
|
|
221
|
+
|
|
209
222
|
return PositionContainer;
|
|
210
223
|
})();
|
|
@@ -25,20 +25,30 @@ module.exports = (() => {
|
|
|
25
25
|
this._dataActual = { };
|
|
26
26
|
|
|
27
27
|
this._dataFormat.description = this._description;
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
this._dataActual.currentPrice = null;
|
|
30
30
|
this._dataActual.previousPrice = null;
|
|
31
31
|
this._dataActual.basis = null;
|
|
32
|
+
this._dataActual.realized = null;
|
|
33
|
+
this._dataActual.income = null;
|
|
32
34
|
this._dataActual.market = null;
|
|
33
35
|
this._dataActual.marketPercent = null;
|
|
34
36
|
this._dataActual.unrealizedToday = null;
|
|
37
|
+
this._dataActual.total = null;
|
|
38
|
+
this._dataActual.summaryOneTotal = null;
|
|
39
|
+
this._dataActual.summaryTwoTotal = null;
|
|
35
40
|
|
|
36
41
|
this._dataFormat.currentPrice = null;
|
|
37
42
|
this._dataFormat.previousPrice = null;
|
|
38
43
|
this._dataFormat.basis = null;
|
|
44
|
+
this._dataFormat.realized = null;
|
|
45
|
+
this._dataFormat.income = null;
|
|
39
46
|
this._dataFormat.market = null;
|
|
40
47
|
this._dataFormat.marketPercent = null;
|
|
41
48
|
this._dataFormat.unrealizedToday = null;
|
|
49
|
+
this._dataFormat.total = null;
|
|
50
|
+
this._dataFormat.summaryOneTotal = null;
|
|
51
|
+
this._dataFormat.summaryTwoTotal = null;
|
|
42
52
|
|
|
43
53
|
this._dataFormat.unrealizedTodayNegative = false;
|
|
44
54
|
|
|
@@ -115,15 +125,31 @@ module.exports = (() => {
|
|
|
115
125
|
|
|
116
126
|
let updates = items.reduce((updates, item) => {
|
|
117
127
|
updates.basis = updates.basis.add(item.data.basis);
|
|
128
|
+
updates.realized = updates.realized.add(item.data.realized);
|
|
129
|
+
updates.income = updates.income.add(item.data.income);
|
|
130
|
+
updates.summaryOneTotal = updates.summaryOneTotal.add(item.data.summaryOneTotal);
|
|
131
|
+
updates.summaryTwoTotal = updates.summaryTwoTotal.add(item.data.summaryTwoTotal);
|
|
118
132
|
|
|
119
133
|
return updates;
|
|
120
134
|
}, {
|
|
121
|
-
basis: Decimal.ZERO
|
|
135
|
+
basis: Decimal.ZERO,
|
|
136
|
+
realized: Decimal.ZERO,
|
|
137
|
+
income: Decimal.ZERO,
|
|
138
|
+
summaryOneTotal: Decimal.ZERO,
|
|
139
|
+
summaryTwoTotal: Decimal.ZERO
|
|
122
140
|
});
|
|
123
141
|
|
|
124
142
|
actual.basis = updates.basis;
|
|
125
|
-
|
|
126
|
-
|
|
143
|
+
actual.realized = updates.realized;
|
|
144
|
+
actual.income = updates.income;
|
|
145
|
+
actual.summaryOneTotal = updates.summaryOneTotal;
|
|
146
|
+
actual.summaryTwoTotal = updates.summaryTwoTotal;
|
|
147
|
+
|
|
148
|
+
format.basis = formatCurrency(actual.basis, currency);
|
|
149
|
+
format.realized = formatCurrency(actual.basis, currency);
|
|
150
|
+
format.income = formatCurrency(actual.income, currency);
|
|
151
|
+
format.summaryOneTotal = formatCurrency(updates.summaryOneTotal, currency);
|
|
152
|
+
format.summaryTwoTotal = formatCurrency(updates.summaryTwoTotal, currency);
|
|
127
153
|
}
|
|
128
154
|
|
|
129
155
|
function calculatePriceData(group, item) {
|
|
@@ -136,7 +162,12 @@ module.exports = (() => {
|
|
|
136
162
|
|
|
137
163
|
let updates;
|
|
138
164
|
|
|
139
|
-
if (actual.market
|
|
165
|
+
if (actual.market !== null && actual.unrealizedToday !== null && actual.total !== null) {
|
|
166
|
+
updates = {
|
|
167
|
+
market: actual.market.add(item.data.marketChange),
|
|
168
|
+
unrealizedToday: actual.unrealizedToday.add(item.data.unrealizedTodayChange)
|
|
169
|
+
};
|
|
170
|
+
} else {
|
|
140
171
|
const items = group._items;
|
|
141
172
|
|
|
142
173
|
updates = items.reduce((updates, item) => {
|
|
@@ -148,11 +179,6 @@ module.exports = (() => {
|
|
|
148
179
|
market: Decimal.ZERO,
|
|
149
180
|
unrealizedToday: Decimal.ZERO
|
|
150
181
|
});
|
|
151
|
-
} else {
|
|
152
|
-
updates = {
|
|
153
|
-
market: actual.market.add(item.data.marketChange),
|
|
154
|
-
unrealizedToday: actual.unrealizedToday.add(item.data.unrealizedTodayChange)
|
|
155
|
-
};
|
|
156
182
|
}
|
|
157
183
|
|
|
158
184
|
if (parent !== null) {
|
|
@@ -166,15 +192,17 @@ module.exports = (() => {
|
|
|
166
192
|
} else {
|
|
167
193
|
updates.marketPercent = null;
|
|
168
194
|
}
|
|
169
|
-
|
|
195
|
+
|
|
170
196
|
actual.market = updates.market;
|
|
171
197
|
actual.marketPercent = updates.marketPercent;
|
|
172
198
|
actual.unrealizedToday = updates.unrealizedToday;
|
|
199
|
+
actual.total = updates.unrealizedToday.add(actual.realized).add(actual.income);
|
|
173
200
|
|
|
174
|
-
format.market = formatCurrency(
|
|
175
|
-
format.marketPercent = formatPercent(
|
|
176
|
-
format.unrealizedToday = formatCurrency(
|
|
201
|
+
format.market = formatCurrency(actual.market, currency);
|
|
202
|
+
format.marketPercent = formatPercent(actual.marketPercent, 2);
|
|
203
|
+
format.unrealizedToday = formatCurrency(actual.unrealizedToday, currency);
|
|
177
204
|
format.unrealizedTodayNegative = actual.unrealizedToday.getIsNegative();
|
|
205
|
+
format.total = formatCurrency(actual.total, currency);
|
|
178
206
|
}
|
|
179
207
|
|
|
180
208
|
return PositionGroup;
|
|
@@ -26,10 +26,13 @@ module.exports = (() => {
|
|
|
26
26
|
|
|
27
27
|
this._data.market = null;
|
|
28
28
|
this._data.marketChange = null;
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
this._data.unrealizedToday = null;
|
|
31
31
|
this._data.unrealizedTodayChange = null;
|
|
32
32
|
|
|
33
|
+
this._data.realized = null;
|
|
34
|
+
this._data.income = null;
|
|
35
|
+
|
|
33
36
|
calculateStaticData(this);
|
|
34
37
|
calculatePriceData(this, null);
|
|
35
38
|
|
|
@@ -74,10 +77,11 @@ module.exports = (() => {
|
|
|
74
77
|
function calculateStaticData(item) {
|
|
75
78
|
const position = item.position;
|
|
76
79
|
const snapshot = item.position.snapshot;
|
|
80
|
+
const summaries = item.summaries;
|
|
77
81
|
|
|
78
82
|
const data = item._data;
|
|
79
83
|
|
|
80
|
-
data.previousPrice = position.
|
|
84
|
+
data.previousPrice = position.previous || null;
|
|
81
85
|
|
|
82
86
|
let basis;
|
|
83
87
|
|
|
@@ -88,6 +92,26 @@ module.exports = (() => {
|
|
|
88
92
|
}
|
|
89
93
|
|
|
90
94
|
data.basis = basis;
|
|
95
|
+
|
|
96
|
+
data.realized = snapshot.gain;
|
|
97
|
+
data.income = snapshot.income;
|
|
98
|
+
|
|
99
|
+
const getSummaryTotal = (index) => {
|
|
100
|
+
let summaryTotal;
|
|
101
|
+
|
|
102
|
+
if (summaries.length > index && summaries[index] !== null) {
|
|
103
|
+
const period = summaries[index].period;
|
|
104
|
+
|
|
105
|
+
summaryTotal = period.realized.add(period.unrealized).add(period.income);
|
|
106
|
+
} else {
|
|
107
|
+
summaryTotal = Decimal.ZERO;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return summaryTotal;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
data.summaryOneTotal = getSummaryTotal(0);
|
|
114
|
+
data.summaryTwoTotal = getSummaryTotal(1);
|
|
91
115
|
}
|
|
92
116
|
|
|
93
117
|
function calculatePriceData(item, price) {
|
package/package.json
CHANGED
package/test/SpecRunner.js
CHANGED
|
@@ -93,6 +93,7 @@ module.exports = (() => {
|
|
|
93
93
|
const array = require('@barchart/common-js/lang/array'),
|
|
94
94
|
assert = require('@barchart/common-js/lang/assert'),
|
|
95
95
|
Day = require('@barchart/common-js/lang/Day'),
|
|
96
|
+
Decimal = require('@barchart/common-js/lang/Decimal'),
|
|
96
97
|
Enum = require('@barchart/common-js/lang/Enum'),
|
|
97
98
|
is = require('@barchart/common-js/lang/is');
|
|
98
99
|
|
|
@@ -108,23 +109,68 @@ module.exports = (() => {
|
|
|
108
109
|
* @param {String} description
|
|
109
110
|
* @param {Function} rangeCalculator
|
|
110
111
|
* @param {Function} startDateCalculator
|
|
112
|
+
* @param {Function} descriptionCalculator
|
|
111
113
|
*/
|
|
112
114
|
class PositionSummaryFrame extends Enum {
|
|
113
|
-
constructor(code, description, rangeCalculator, startDateCalculator) {
|
|
115
|
+
constructor(code, description, rangeCalculator, startDateCalculator, descriptionCalculator) {
|
|
114
116
|
super(code, description);
|
|
115
117
|
|
|
116
118
|
assert.argumentIsRequired(rangeCalculator, 'rangeCalculator', Function);
|
|
119
|
+
assert.argumentIsRequired(startDateCalculator, 'startDateCalculator', Function);
|
|
120
|
+
assert.argumentIsRequired(descriptionCalculator, 'descriptionCalculator', Function);
|
|
117
121
|
|
|
118
122
|
this._rangeCalculator = rangeCalculator;
|
|
119
123
|
this._startDateCalculator = startDateCalculator;
|
|
124
|
+
this._descriptionCalculator = descriptionCalculator;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Returns a human-readable description of the frame, given
|
|
129
|
+
* start and end dates.
|
|
130
|
+
*
|
|
131
|
+
* @public
|
|
132
|
+
* @param {Day} startDate
|
|
133
|
+
* @param {Day} endDate
|
|
134
|
+
* @return {String}
|
|
135
|
+
*/
|
|
136
|
+
describeRange(startDate, endDate) {
|
|
137
|
+
return this._descriptionCalculator(startDate, endDate);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Returns the most recent ranges for the frame.
|
|
142
|
+
*
|
|
143
|
+
* @public
|
|
144
|
+
* @param {Number} periods
|
|
145
|
+
* @returns {Array.<PositionSummaryRange>}
|
|
146
|
+
*/
|
|
147
|
+
getRecentRanges(periods) {
|
|
148
|
+
const startDate = this.getStartDate(periods);
|
|
149
|
+
const transaction = { date: startDate, snapshot: { open: Decimal.ONE } };
|
|
150
|
+
|
|
151
|
+
return this.getRanges([ transaction ]);
|
|
120
152
|
}
|
|
121
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Returns the ranges for the set of {@link Transaction} objects.
|
|
156
|
+
*
|
|
157
|
+
* @public
|
|
158
|
+
* @param {Array.<Transaction>} transactions
|
|
159
|
+
* @returns {Array.<PositionSummaryRange>}
|
|
160
|
+
*/
|
|
122
161
|
getRanges(transactions) {
|
|
123
162
|
assert.argumentIsArray(transactions, 'transactions');
|
|
124
163
|
|
|
125
164
|
return this._rangeCalculator(getFilteredTransactions(transactions));
|
|
126
165
|
}
|
|
127
166
|
|
|
167
|
+
/**
|
|
168
|
+
* Returns the start date for a frame, a given number of periods ago.
|
|
169
|
+
*
|
|
170
|
+
* @public
|
|
171
|
+
* @param {Number} periods
|
|
172
|
+
* @returns {Day}
|
|
173
|
+
*/
|
|
128
174
|
getStartDate(periods) {
|
|
129
175
|
assert.argumentIsRequired(periods, 'periods', Number);
|
|
130
176
|
|
|
@@ -176,10 +222,19 @@ module.exports = (() => {
|
|
|
176
222
|
}
|
|
177
223
|
}
|
|
178
224
|
|
|
179
|
-
const yearly = new PositionSummaryFrame('YEARLY', 'year', getYearlyRanges, getYearlyStartDate);
|
|
180
|
-
const quarterly = new PositionSummaryFrame('QUARTER', 'quarter', getQuarterlyRanges, getQuarterlyStartDate);
|
|
181
|
-
const monthly = new PositionSummaryFrame('MONTH', 'month', getMonthlyRanges, getMonthlyStartDate);
|
|
182
|
-
const ytd = new PositionSummaryFrame('YTD', 'year-to-date', getYearToDateRanges, getYearToDateStartDate);
|
|
225
|
+
const yearly = new PositionSummaryFrame('YEARLY', 'year', getYearlyRanges, getYearlyStartDate, getYearlyRangeDescription);
|
|
226
|
+
const quarterly = new PositionSummaryFrame('QUARTER', 'quarter', getQuarterlyRanges, getQuarterlyStartDate, getQuarterlyRangeDescription);
|
|
227
|
+
const monthly = new PositionSummaryFrame('MONTH', 'month', getMonthlyRanges, getMonthlyStartDate, getMonthlyRangeDescription);
|
|
228
|
+
const ytd = new PositionSummaryFrame('YTD', 'year-to-date', getYearToDateRanges, getYearToDateStartDate, getYearToDateRangeDescription);
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* The start and and date for a {@link PositionSummaryFrame}
|
|
232
|
+
*
|
|
233
|
+
* @typedef PositionSummaryRange
|
|
234
|
+
* @type {Object}
|
|
235
|
+
* @property {Day} start
|
|
236
|
+
* @property {Day} end
|
|
237
|
+
*/
|
|
183
238
|
|
|
184
239
|
function getRange(start, end) {
|
|
185
240
|
return {
|
|
@@ -263,6 +318,22 @@ module.exports = (() => {
|
|
|
263
318
|
return null;
|
|
264
319
|
}
|
|
265
320
|
|
|
321
|
+
function getYearlyRangeDescription(startDate, endDate) {
|
|
322
|
+
return endDate.year.toString();
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function getQuarterlyRangeDescription(startDate, endDate) {
|
|
326
|
+
return '';
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function getMonthlyRangeDescription(startDate, endDate) {
|
|
330
|
+
return '';
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function getYearToDateRangeDescription(startDate, endDate) {
|
|
334
|
+
return '';
|
|
335
|
+
}
|
|
336
|
+
|
|
266
337
|
function getFilteredTransactions(transactions) {
|
|
267
338
|
return transactions.reduce((filtered, transaction) => {
|
|
268
339
|
if (!transaction.snapshot.open.getIsZero() || transaction.type.closing) {
|
|
@@ -276,7 +347,7 @@ module.exports = (() => {
|
|
|
276
347
|
return PositionSummaryFrame;
|
|
277
348
|
})();
|
|
278
349
|
|
|
279
|
-
},{"@barchart/common-js/lang/Day":12,"@barchart/common-js/lang/Enum":15,"@barchart/common-js/lang/array":16,"@barchart/common-js/lang/assert":17,"@barchart/common-js/lang/is":19}],3:[function(require,module,exports){
|
|
350
|
+
},{"@barchart/common-js/lang/Day":12,"@barchart/common-js/lang/Decimal":13,"@barchart/common-js/lang/Enum":15,"@barchart/common-js/lang/array":16,"@barchart/common-js/lang/assert":17,"@barchart/common-js/lang/is":19}],3:[function(require,module,exports){
|
|
280
351
|
const assert = require('@barchart/common-js/lang/assert'),
|
|
281
352
|
Enum = require('@barchart/common-js/lang/Enum');
|
|
282
353
|
|
|
@@ -612,7 +683,7 @@ const array = require('@barchart/common-js/lang/array'),
|
|
|
612
683
|
is = require('@barchart/common-js/lang/is'),
|
|
613
684
|
Tree = require('@barchart/common-js/collections/Tree');
|
|
614
685
|
|
|
615
|
-
const
|
|
686
|
+
const PositionSummaryFrame = require('./../data/PositionSummaryFrame');
|
|
616
687
|
|
|
617
688
|
const PositionGroup = require('./PositionGroup'),
|
|
618
689
|
PositionItem = require('./PositionItem');
|
|
@@ -624,10 +695,13 @@ module.exports = (() => {
|
|
|
624
695
|
* @public
|
|
625
696
|
*/
|
|
626
697
|
class PositionContainer {
|
|
627
|
-
constructor(portfolios, positions, summaries, definitions, defaultCurrency) {
|
|
698
|
+
constructor(portfolios, positions, summaries, definitions, defaultCurrency, summaryFrameType) {
|
|
628
699
|
this._definitions = definitions;
|
|
629
700
|
this._defaultCurrency = defaultCurrency || Currency.CAD;
|
|
630
701
|
|
|
702
|
+
this._summaryFrame = summaryFrameType || PositionSummaryFrame.YEARLY;
|
|
703
|
+
this._summaryRanges = this._summaryFrame.getRecentRanges(1);
|
|
704
|
+
|
|
631
705
|
this._portfolios = portfolios.reduce((map, portfolio) => {
|
|
632
706
|
map[portfolio.portfolio] = portfolio;
|
|
633
707
|
|
|
@@ -635,13 +709,19 @@ module.exports = (() => {
|
|
|
635
709
|
}, { });
|
|
636
710
|
|
|
637
711
|
this._summaries = summaries.reduce((map, summary) => {
|
|
638
|
-
|
|
712
|
+
if (this._summaryFrame === summary.frame) {
|
|
713
|
+
const key = summary.position;
|
|
639
714
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
715
|
+
if (!map.hasOwnProperty(key)) {
|
|
716
|
+
map[key] = getSummaryArray(this._summaryRanges);
|
|
717
|
+
}
|
|
643
718
|
|
|
644
|
-
|
|
719
|
+
const index = this._summaryRanges.findIndex(r => r.start.getIsEqual(summary.start.date) && r.end.getIsEqual(summary.end.date));
|
|
720
|
+
|
|
721
|
+
if (!(index < 0)) {
|
|
722
|
+
map[key][index] = summary;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
645
725
|
|
|
646
726
|
return map;
|
|
647
727
|
}, { });
|
|
@@ -650,7 +730,7 @@ module.exports = (() => {
|
|
|
650
730
|
const portfolio = this._portfolios[position.portfolio];
|
|
651
731
|
|
|
652
732
|
if (position) {
|
|
653
|
-
const summaries = this._summaries[position.position] ||
|
|
733
|
+
const summaries = this._summaries[position.position] || getSummaryArray(this._summaryRanges);
|
|
654
734
|
|
|
655
735
|
items.push(new PositionItem(portfolio, position, summaries));
|
|
656
736
|
}
|
|
@@ -812,10 +892,14 @@ module.exports = (() => {
|
|
|
812
892
|
}
|
|
813
893
|
}
|
|
814
894
|
|
|
895
|
+
function getSummaryArray(ranges) {
|
|
896
|
+
return ranges.map(range => null);
|
|
897
|
+
}
|
|
898
|
+
|
|
815
899
|
return PositionContainer;
|
|
816
900
|
})();
|
|
817
901
|
|
|
818
|
-
},{"./../data/
|
|
902
|
+
},{"./../data/PositionSummaryFrame":2,"./PositionGroup":5,"./PositionItem":7,"@barchart/common-js/collections/Tree":8,"@barchart/common-js/collections/sorting/ComparatorBuilder":9,"@barchart/common-js/collections/sorting/comparators":10,"@barchart/common-js/lang/Currency":11,"@barchart/common-js/lang/array":16,"@barchart/common-js/lang/assert":17,"@barchart/common-js/lang/is":19}],5:[function(require,module,exports){
|
|
819
903
|
const assert = require('@barchart/common-js/lang/assert'),
|
|
820
904
|
Currency = require('@barchart/common-js/lang/Currency'),
|
|
821
905
|
Decimal = require('@barchart/common-js/lang/Decimal'),
|
|
@@ -843,20 +927,30 @@ module.exports = (() => {
|
|
|
843
927
|
this._dataActual = { };
|
|
844
928
|
|
|
845
929
|
this._dataFormat.description = this._description;
|
|
846
|
-
|
|
930
|
+
|
|
847
931
|
this._dataActual.currentPrice = null;
|
|
848
932
|
this._dataActual.previousPrice = null;
|
|
849
933
|
this._dataActual.basis = null;
|
|
934
|
+
this._dataActual.realized = null;
|
|
935
|
+
this._dataActual.income = null;
|
|
850
936
|
this._dataActual.market = null;
|
|
851
937
|
this._dataActual.marketPercent = null;
|
|
852
938
|
this._dataActual.unrealizedToday = null;
|
|
939
|
+
this._dataActual.total = null;
|
|
940
|
+
this._dataActual.summaryOneTotal = null;
|
|
941
|
+
this._dataActual.summaryTwoTotal = null;
|
|
853
942
|
|
|
854
943
|
this._dataFormat.currentPrice = null;
|
|
855
944
|
this._dataFormat.previousPrice = null;
|
|
856
945
|
this._dataFormat.basis = null;
|
|
946
|
+
this._dataFormat.realized = null;
|
|
947
|
+
this._dataFormat.income = null;
|
|
857
948
|
this._dataFormat.market = null;
|
|
858
949
|
this._dataFormat.marketPercent = null;
|
|
859
950
|
this._dataFormat.unrealizedToday = null;
|
|
951
|
+
this._dataFormat.total = null;
|
|
952
|
+
this._dataFormat.summaryOneTotal = null;
|
|
953
|
+
this._dataFormat.summaryTwoTotal = null;
|
|
860
954
|
|
|
861
955
|
this._dataFormat.unrealizedTodayNegative = false;
|
|
862
956
|
|
|
@@ -933,15 +1027,31 @@ module.exports = (() => {
|
|
|
933
1027
|
|
|
934
1028
|
let updates = items.reduce((updates, item) => {
|
|
935
1029
|
updates.basis = updates.basis.add(item.data.basis);
|
|
1030
|
+
updates.realized = updates.realized.add(item.data.realized);
|
|
1031
|
+
updates.income = updates.income.add(item.data.income);
|
|
1032
|
+
updates.summaryOneTotal = updates.summaryOneTotal.add(item.data.summaryOneTotal);
|
|
1033
|
+
updates.summaryTwoTotal = updates.summaryTwoTotal.add(item.data.summaryTwoTotal);
|
|
936
1034
|
|
|
937
1035
|
return updates;
|
|
938
1036
|
}, {
|
|
939
|
-
basis: Decimal.ZERO
|
|
1037
|
+
basis: Decimal.ZERO,
|
|
1038
|
+
realized: Decimal.ZERO,
|
|
1039
|
+
income: Decimal.ZERO,
|
|
1040
|
+
summaryOneTotal: Decimal.ZERO,
|
|
1041
|
+
summaryTwoTotal: Decimal.ZERO
|
|
940
1042
|
});
|
|
941
1043
|
|
|
942
1044
|
actual.basis = updates.basis;
|
|
943
|
-
|
|
944
|
-
|
|
1045
|
+
actual.realized = updates.realized;
|
|
1046
|
+
actual.income = updates.income;
|
|
1047
|
+
actual.summaryOneTotal = updates.summaryOneTotal;
|
|
1048
|
+
actual.summaryTwoTotal = updates.summaryTwoTotal;
|
|
1049
|
+
|
|
1050
|
+
format.basis = formatCurrency(actual.basis, currency);
|
|
1051
|
+
format.realized = formatCurrency(actual.basis, currency);
|
|
1052
|
+
format.income = formatCurrency(actual.income, currency);
|
|
1053
|
+
format.summaryOneTotal = formatCurrency(updates.summaryOneTotal, currency);
|
|
1054
|
+
format.summaryTwoTotal = formatCurrency(updates.summaryTwoTotal, currency);
|
|
945
1055
|
}
|
|
946
1056
|
|
|
947
1057
|
function calculatePriceData(group, item) {
|
|
@@ -954,7 +1064,12 @@ module.exports = (() => {
|
|
|
954
1064
|
|
|
955
1065
|
let updates;
|
|
956
1066
|
|
|
957
|
-
if (actual.market
|
|
1067
|
+
if (actual.market !== null && actual.unrealizedToday !== null && actual.total !== null) {
|
|
1068
|
+
updates = {
|
|
1069
|
+
market: actual.market.add(item.data.marketChange),
|
|
1070
|
+
unrealizedToday: actual.unrealizedToday.add(item.data.unrealizedTodayChange)
|
|
1071
|
+
};
|
|
1072
|
+
} else {
|
|
958
1073
|
const items = group._items;
|
|
959
1074
|
|
|
960
1075
|
updates = items.reduce((updates, item) => {
|
|
@@ -966,11 +1081,6 @@ module.exports = (() => {
|
|
|
966
1081
|
market: Decimal.ZERO,
|
|
967
1082
|
unrealizedToday: Decimal.ZERO
|
|
968
1083
|
});
|
|
969
|
-
} else {
|
|
970
|
-
updates = {
|
|
971
|
-
market: actual.market.add(item.data.marketChange),
|
|
972
|
-
unrealizedToday: actual.unrealizedToday.add(item.data.unrealizedTodayChange)
|
|
973
|
-
};
|
|
974
1084
|
}
|
|
975
1085
|
|
|
976
1086
|
if (parent !== null) {
|
|
@@ -984,15 +1094,17 @@ module.exports = (() => {
|
|
|
984
1094
|
} else {
|
|
985
1095
|
updates.marketPercent = null;
|
|
986
1096
|
}
|
|
987
|
-
|
|
1097
|
+
|
|
988
1098
|
actual.market = updates.market;
|
|
989
1099
|
actual.marketPercent = updates.marketPercent;
|
|
990
1100
|
actual.unrealizedToday = updates.unrealizedToday;
|
|
1101
|
+
actual.total = updates.unrealizedToday.add(actual.realized).add(actual.income);
|
|
991
1102
|
|
|
992
|
-
format.market = formatCurrency(
|
|
993
|
-
format.marketPercent = formatPercent(
|
|
994
|
-
format.unrealizedToday = formatCurrency(
|
|
1103
|
+
format.market = formatCurrency(actual.market, currency);
|
|
1104
|
+
format.marketPercent = formatPercent(actual.marketPercent, 2);
|
|
1105
|
+
format.unrealizedToday = formatCurrency(actual.unrealizedToday, currency);
|
|
995
1106
|
format.unrealizedTodayNegative = actual.unrealizedToday.getIsNegative();
|
|
1107
|
+
format.total = formatCurrency(actual.total, currency);
|
|
996
1108
|
}
|
|
997
1109
|
|
|
998
1110
|
return PositionGroup;
|
|
@@ -1077,10 +1189,13 @@ module.exports = (() => {
|
|
|
1077
1189
|
|
|
1078
1190
|
this._data.market = null;
|
|
1079
1191
|
this._data.marketChange = null;
|
|
1080
|
-
|
|
1192
|
+
|
|
1081
1193
|
this._data.unrealizedToday = null;
|
|
1082
1194
|
this._data.unrealizedTodayChange = null;
|
|
1083
1195
|
|
|
1196
|
+
this._data.realized = null;
|
|
1197
|
+
this._data.income = null;
|
|
1198
|
+
|
|
1084
1199
|
calculateStaticData(this);
|
|
1085
1200
|
calculatePriceData(this, null);
|
|
1086
1201
|
|
|
@@ -1125,10 +1240,11 @@ module.exports = (() => {
|
|
|
1125
1240
|
function calculateStaticData(item) {
|
|
1126
1241
|
const position = item.position;
|
|
1127
1242
|
const snapshot = item.position.snapshot;
|
|
1243
|
+
const summaries = item.summaries;
|
|
1128
1244
|
|
|
1129
1245
|
const data = item._data;
|
|
1130
1246
|
|
|
1131
|
-
data.previousPrice = position.
|
|
1247
|
+
data.previousPrice = position.previous || null;
|
|
1132
1248
|
|
|
1133
1249
|
let basis;
|
|
1134
1250
|
|
|
@@ -1139,6 +1255,26 @@ module.exports = (() => {
|
|
|
1139
1255
|
}
|
|
1140
1256
|
|
|
1141
1257
|
data.basis = basis;
|
|
1258
|
+
|
|
1259
|
+
data.realized = snapshot.gain;
|
|
1260
|
+
data.income = snapshot.income;
|
|
1261
|
+
|
|
1262
|
+
const getSummaryTotal = (index) => {
|
|
1263
|
+
let summaryTotal;
|
|
1264
|
+
|
|
1265
|
+
if (summaries.length > index && summaries[index] !== null) {
|
|
1266
|
+
const period = summaries[index].period;
|
|
1267
|
+
|
|
1268
|
+
summaryTotal = period.realized.add(period.unrealized).add(period.income);
|
|
1269
|
+
} else {
|
|
1270
|
+
summaryTotal = Decimal.ZERO;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
return summaryTotal;
|
|
1274
|
+
};
|
|
1275
|
+
|
|
1276
|
+
data.summaryOneTotal = getSummaryTotal(0);
|
|
1277
|
+
data.summaryTwoTotal = getSummaryTotal(1);
|
|
1142
1278
|
}
|
|
1143
1279
|
|
|
1144
1280
|
function calculatePriceData(item, price) {
|
|
@@ -5605,7 +5741,9 @@ describe('When a position container data is gathered', () => {
|
|
|
5605
5741
|
snapshot: {
|
|
5606
5742
|
basis: new Decimal(123),
|
|
5607
5743
|
value: new Decimal(456),
|
|
5608
|
-
open: new Decimal(1)
|
|
5744
|
+
open: new Decimal(1),
|
|
5745
|
+
income: new Decimal(0),
|
|
5746
|
+
gain: new Decimal(0)
|
|
5609
5747
|
}
|
|
5610
5748
|
}
|
|
5611
5749
|
}
|