@barchart/portfolio-api-common 1.0.68 → 1.0.69
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 +22 -8
- package/lib/processing/PositionGroup.js +42 -14
- package/lib/processing/PositionItem.js +24 -0
- package/package.json +1 -1
- package/test/SpecRunner.js +169 -30
- 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,8 @@ 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 InstrumentType = require('./../data/InstrumentType')
|
|
9
|
+
const InstrumentType = require('./../data/InstrumentType'),
|
|
10
|
+
PositionSummaryFrame = require('./../data/PositionSummaryFrame');
|
|
10
11
|
|
|
11
12
|
const PositionGroup = require('./PositionGroup'),
|
|
12
13
|
PositionItem = require('./PositionItem');
|
|
@@ -18,10 +19,13 @@ module.exports = (() => {
|
|
|
18
19
|
* @public
|
|
19
20
|
*/
|
|
20
21
|
class PositionContainer {
|
|
21
|
-
constructor(portfolios, positions, summaries, definitions, defaultCurrency) {
|
|
22
|
+
constructor(portfolios, positions, summaries, definitions, defaultCurrency, summaryFrameType) {
|
|
22
23
|
this._definitions = definitions;
|
|
23
24
|
this._defaultCurrency = defaultCurrency || Currency.CAD;
|
|
24
25
|
|
|
26
|
+
this._summaryFrame = summaryFrameType || PositionSummaryFrame.YEARLY;
|
|
27
|
+
this._summaryRanges = this._summaryFrame.getRecentRanges(2);
|
|
28
|
+
|
|
25
29
|
this._portfolios = portfolios.reduce((map, portfolio) => {
|
|
26
30
|
map[portfolio.portfolio] = portfolio;
|
|
27
31
|
|
|
@@ -29,13 +33,19 @@ module.exports = (() => {
|
|
|
29
33
|
}, { });
|
|
30
34
|
|
|
31
35
|
this._summaries = summaries.reduce((map, summary) => {
|
|
32
|
-
|
|
36
|
+
if (this._summaryFrame === summary.frame) {
|
|
37
|
+
const key = summary.position;
|
|
33
38
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
if (!map.hasOwnProperty(key)) {
|
|
40
|
+
map[key] = getSummaryArray(this._summaryRanges);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const index = this._summaryRanges.findIndex(r => r.start === summary.start.date && r.end === summary.end.date);
|
|
37
44
|
|
|
38
|
-
|
|
45
|
+
if (!(index < 0)) {
|
|
46
|
+
map[key][index] = summary;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
39
49
|
|
|
40
50
|
return map;
|
|
41
51
|
}, { });
|
|
@@ -44,7 +54,7 @@ module.exports = (() => {
|
|
|
44
54
|
const portfolio = this._portfolios[position.portfolio];
|
|
45
55
|
|
|
46
56
|
if (position) {
|
|
47
|
-
const summaries = this._summaries[position.position] ||
|
|
57
|
+
const summaries = this._summaries[position.position] || getSummaryArray(this._summaryRanges);
|
|
48
58
|
|
|
49
59
|
items.push(new PositionItem(portfolio, position, summaries));
|
|
50
60
|
}
|
|
@@ -206,5 +216,9 @@ module.exports = (() => {
|
|
|
206
216
|
}
|
|
207
217
|
}
|
|
208
218
|
|
|
219
|
+
function getSummaryArray(ranges) {
|
|
220
|
+
return ranges.map(range => null);
|
|
221
|
+
}
|
|
222
|
+
|
|
209
223
|
return PositionContainer;
|
|
210
224
|
})();
|
|
@@ -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;
|
|
@@ -30,6 +30,9 @@ module.exports = (() => {
|
|
|
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,6 +77,7 @@ 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
|
|
|
@@ -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 + 1) && 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;
|
|
120
125
|
}
|
|
121
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 ]);
|
|
152
|
+
}
|
|
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,8 @@ 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 InstrumentType = require('./../data/InstrumentType')
|
|
686
|
+
const InstrumentType = require('./../data/InstrumentType'),
|
|
687
|
+
PositionSummaryFrame = require('./../data/PositionSummaryFrame');
|
|
616
688
|
|
|
617
689
|
const PositionGroup = require('./PositionGroup'),
|
|
618
690
|
PositionItem = require('./PositionItem');
|
|
@@ -624,10 +696,13 @@ module.exports = (() => {
|
|
|
624
696
|
* @public
|
|
625
697
|
*/
|
|
626
698
|
class PositionContainer {
|
|
627
|
-
constructor(portfolios, positions, summaries, definitions, defaultCurrency) {
|
|
699
|
+
constructor(portfolios, positions, summaries, definitions, defaultCurrency, summaryFrameType) {
|
|
628
700
|
this._definitions = definitions;
|
|
629
701
|
this._defaultCurrency = defaultCurrency || Currency.CAD;
|
|
630
702
|
|
|
703
|
+
this._summaryFrame = summaryFrameType || PositionSummaryFrame.YEARLY;
|
|
704
|
+
this._summaryRanges = this._summaryFrame.getRecentRanges(2);
|
|
705
|
+
|
|
631
706
|
this._portfolios = portfolios.reduce((map, portfolio) => {
|
|
632
707
|
map[portfolio.portfolio] = portfolio;
|
|
633
708
|
|
|
@@ -635,13 +710,19 @@ module.exports = (() => {
|
|
|
635
710
|
}, { });
|
|
636
711
|
|
|
637
712
|
this._summaries = summaries.reduce((map, summary) => {
|
|
638
|
-
|
|
713
|
+
if (this._summaryFrame === summary.frame) {
|
|
714
|
+
const key = summary.position;
|
|
639
715
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
716
|
+
if (!map.hasOwnProperty(key)) {
|
|
717
|
+
map[key] = getSummaryArray(this._summaryRanges);
|
|
718
|
+
}
|
|
643
719
|
|
|
644
|
-
|
|
720
|
+
const index = this._summaryRanges.findIndex(r => r.start === summary.start.date && r.end === summary.end.date);
|
|
721
|
+
|
|
722
|
+
if (!(index < 0)) {
|
|
723
|
+
map[key][index] = summary;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
645
726
|
|
|
646
727
|
return map;
|
|
647
728
|
}, { });
|
|
@@ -650,7 +731,7 @@ module.exports = (() => {
|
|
|
650
731
|
const portfolio = this._portfolios[position.portfolio];
|
|
651
732
|
|
|
652
733
|
if (position) {
|
|
653
|
-
const summaries = this._summaries[position.position] ||
|
|
734
|
+
const summaries = this._summaries[position.position] || getSummaryArray(this._summaryRanges);
|
|
654
735
|
|
|
655
736
|
items.push(new PositionItem(portfolio, position, summaries));
|
|
656
737
|
}
|
|
@@ -812,10 +893,14 @@ module.exports = (() => {
|
|
|
812
893
|
}
|
|
813
894
|
}
|
|
814
895
|
|
|
896
|
+
function getSummaryArray(ranges) {
|
|
897
|
+
return ranges.map(range => null);
|
|
898
|
+
}
|
|
899
|
+
|
|
815
900
|
return PositionContainer;
|
|
816
901
|
})();
|
|
817
902
|
|
|
818
|
-
},{"./../data/InstrumentType":1,"./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){
|
|
903
|
+
},{"./../data/InstrumentType":1,"./../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
904
|
const assert = require('@barchart/common-js/lang/assert'),
|
|
820
905
|
Currency = require('@barchart/common-js/lang/Currency'),
|
|
821
906
|
Decimal = require('@barchart/common-js/lang/Decimal'),
|
|
@@ -843,20 +928,30 @@ module.exports = (() => {
|
|
|
843
928
|
this._dataActual = { };
|
|
844
929
|
|
|
845
930
|
this._dataFormat.description = this._description;
|
|
846
|
-
|
|
931
|
+
|
|
847
932
|
this._dataActual.currentPrice = null;
|
|
848
933
|
this._dataActual.previousPrice = null;
|
|
849
934
|
this._dataActual.basis = null;
|
|
935
|
+
this._dataActual.realized = null;
|
|
936
|
+
this._dataActual.income = null;
|
|
850
937
|
this._dataActual.market = null;
|
|
851
938
|
this._dataActual.marketPercent = null;
|
|
852
939
|
this._dataActual.unrealizedToday = null;
|
|
940
|
+
this._dataActual.total = null;
|
|
941
|
+
this._dataActual.summaryOneTotal = null;
|
|
942
|
+
this._dataActual.summaryTwoTotal = null;
|
|
853
943
|
|
|
854
944
|
this._dataFormat.currentPrice = null;
|
|
855
945
|
this._dataFormat.previousPrice = null;
|
|
856
946
|
this._dataFormat.basis = null;
|
|
947
|
+
this._dataFormat.realized = null;
|
|
948
|
+
this._dataFormat.income = null;
|
|
857
949
|
this._dataFormat.market = null;
|
|
858
950
|
this._dataFormat.marketPercent = null;
|
|
859
951
|
this._dataFormat.unrealizedToday = null;
|
|
952
|
+
this._dataFormat.total = null;
|
|
953
|
+
this._dataFormat.summaryOneTotal = null;
|
|
954
|
+
this._dataFormat.summaryTwoTotal = null;
|
|
860
955
|
|
|
861
956
|
this._dataFormat.unrealizedTodayNegative = false;
|
|
862
957
|
|
|
@@ -933,15 +1028,31 @@ module.exports = (() => {
|
|
|
933
1028
|
|
|
934
1029
|
let updates = items.reduce((updates, item) => {
|
|
935
1030
|
updates.basis = updates.basis.add(item.data.basis);
|
|
1031
|
+
updates.realized = updates.realized.add(item.data.realized);
|
|
1032
|
+
updates.income = updates.income.add(item.data.income);
|
|
1033
|
+
updates.summaryOneTotal = updates.summaryOneTotal.add(item.data.summaryOneTotal);
|
|
1034
|
+
updates.summaryTwoTotal = updates.summaryTwoTotal.add(item.data.summaryTwoTotal);
|
|
936
1035
|
|
|
937
1036
|
return updates;
|
|
938
1037
|
}, {
|
|
939
|
-
basis: Decimal.ZERO
|
|
1038
|
+
basis: Decimal.ZERO,
|
|
1039
|
+
realized: Decimal.ZERO,
|
|
1040
|
+
income: Decimal.ZERO,
|
|
1041
|
+
summaryOneTotal: Decimal.ZERO,
|
|
1042
|
+
summaryTwoTotal: Decimal.ZERO
|
|
940
1043
|
});
|
|
941
1044
|
|
|
942
1045
|
actual.basis = updates.basis;
|
|
943
|
-
|
|
944
|
-
|
|
1046
|
+
actual.realized = updates.realized;
|
|
1047
|
+
actual.income = updates.income;
|
|
1048
|
+
actual.summaryOneTotal = updates.summaryOneTotal;
|
|
1049
|
+
actual.summaryTwoTotal = updates.summaryTwoTotal;
|
|
1050
|
+
|
|
1051
|
+
format.basis = formatCurrency(actual.basis, currency);
|
|
1052
|
+
format.realized = formatCurrency(actual.basis, currency);
|
|
1053
|
+
format.income = formatCurrency(actual.income, currency);
|
|
1054
|
+
format.summaryOneTotal = formatCurrency(updates.summaryOneTotal, currency);
|
|
1055
|
+
format.summaryTwoTotal = formatCurrency(updates.summaryTwoTotal, currency);
|
|
945
1056
|
}
|
|
946
1057
|
|
|
947
1058
|
function calculatePriceData(group, item) {
|
|
@@ -954,7 +1065,12 @@ module.exports = (() => {
|
|
|
954
1065
|
|
|
955
1066
|
let updates;
|
|
956
1067
|
|
|
957
|
-
if (actual.market
|
|
1068
|
+
if (actual.market !== null && actual.unrealizedToday !== null && actual.total !== null) {
|
|
1069
|
+
updates = {
|
|
1070
|
+
market: actual.market.add(item.data.marketChange),
|
|
1071
|
+
unrealizedToday: actual.unrealizedToday.add(item.data.unrealizedTodayChange)
|
|
1072
|
+
};
|
|
1073
|
+
} else {
|
|
958
1074
|
const items = group._items;
|
|
959
1075
|
|
|
960
1076
|
updates = items.reduce((updates, item) => {
|
|
@@ -966,11 +1082,6 @@ module.exports = (() => {
|
|
|
966
1082
|
market: Decimal.ZERO,
|
|
967
1083
|
unrealizedToday: Decimal.ZERO
|
|
968
1084
|
});
|
|
969
|
-
} else {
|
|
970
|
-
updates = {
|
|
971
|
-
market: actual.market.add(item.data.marketChange),
|
|
972
|
-
unrealizedToday: actual.unrealizedToday.add(item.data.unrealizedTodayChange)
|
|
973
|
-
};
|
|
974
1085
|
}
|
|
975
1086
|
|
|
976
1087
|
if (parent !== null) {
|
|
@@ -984,15 +1095,17 @@ module.exports = (() => {
|
|
|
984
1095
|
} else {
|
|
985
1096
|
updates.marketPercent = null;
|
|
986
1097
|
}
|
|
987
|
-
|
|
1098
|
+
|
|
988
1099
|
actual.market = updates.market;
|
|
989
1100
|
actual.marketPercent = updates.marketPercent;
|
|
990
1101
|
actual.unrealizedToday = updates.unrealizedToday;
|
|
1102
|
+
actual.total = updates.unrealizedToday.add(actual.realized).add(actual.income);
|
|
991
1103
|
|
|
992
|
-
format.market = formatCurrency(
|
|
993
|
-
format.marketPercent = formatPercent(
|
|
994
|
-
format.unrealizedToday = formatCurrency(
|
|
1104
|
+
format.market = formatCurrency(actual.market, currency);
|
|
1105
|
+
format.marketPercent = formatPercent(actual.marketPercent, 2);
|
|
1106
|
+
format.unrealizedToday = formatCurrency(actual.unrealizedToday, currency);
|
|
995
1107
|
format.unrealizedTodayNegative = actual.unrealizedToday.getIsNegative();
|
|
1108
|
+
format.total = formatCurrency(actual.total, currency);
|
|
996
1109
|
}
|
|
997
1110
|
|
|
998
1111
|
return PositionGroup;
|
|
@@ -1081,6 +1194,9 @@ module.exports = (() => {
|
|
|
1081
1194
|
this._data.unrealizedToday = null;
|
|
1082
1195
|
this._data.unrealizedTodayChange = null;
|
|
1083
1196
|
|
|
1197
|
+
this._data.realized = null;
|
|
1198
|
+
this._data.income = null;
|
|
1199
|
+
|
|
1084
1200
|
calculateStaticData(this);
|
|
1085
1201
|
calculatePriceData(this, null);
|
|
1086
1202
|
|
|
@@ -1125,6 +1241,7 @@ module.exports = (() => {
|
|
|
1125
1241
|
function calculateStaticData(item) {
|
|
1126
1242
|
const position = item.position;
|
|
1127
1243
|
const snapshot = item.position.snapshot;
|
|
1244
|
+
const summaries = item.summaries;
|
|
1128
1245
|
|
|
1129
1246
|
const data = item._data;
|
|
1130
1247
|
|
|
@@ -1139,6 +1256,26 @@ module.exports = (() => {
|
|
|
1139
1256
|
}
|
|
1140
1257
|
|
|
1141
1258
|
data.basis = basis;
|
|
1259
|
+
|
|
1260
|
+
data.realized = snapshot.gain;
|
|
1261
|
+
data.income = snapshot.income;
|
|
1262
|
+
|
|
1263
|
+
const getSummaryTotal = (index) => {
|
|
1264
|
+
let summaryTotal;
|
|
1265
|
+
|
|
1266
|
+
if (summaries.length > (index + 1) && summaries[index] !== null) {
|
|
1267
|
+
const period = summaries[index].period;
|
|
1268
|
+
|
|
1269
|
+
summaryTotal = period.realized.add(period.unrealized).add(period.income);
|
|
1270
|
+
} else {
|
|
1271
|
+
summaryTotal = Decimal.ZERO;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
return summaryTotal;
|
|
1275
|
+
};
|
|
1276
|
+
|
|
1277
|
+
data.summaryOneTotal = getSummaryTotal(0);
|
|
1278
|
+
data.summaryTwoTotal = getSummaryTotal(1);
|
|
1142
1279
|
}
|
|
1143
1280
|
|
|
1144
1281
|
function calculatePriceData(item, price) {
|
|
@@ -5605,7 +5742,9 @@ describe('When a position container data is gathered', () => {
|
|
|
5605
5742
|
snapshot: {
|
|
5606
5743
|
basis: new Decimal(123),
|
|
5607
5744
|
value: new Decimal(456),
|
|
5608
|
-
open: new Decimal(1)
|
|
5745
|
+
open: new Decimal(1),
|
|
5746
|
+
income: new Decimal(0),
|
|
5747
|
+
gain: new Decimal(0)
|
|
5609
5748
|
}
|
|
5610
5749
|
}
|
|
5611
5750
|
}
|