@barchart/portfolio-api-common 1.2.120 → 1.2.125
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 +23 -23
- package/lib/formatters/TransactionFormatter.js +137 -153
- package/package.json +3 -2
- package/test/SpecRunner.js +215 -203
- package/test/specs/data/PositionSummaryFrameSpec.js +31 -115
|
@@ -64,7 +64,7 @@ module.exports = (() => {
|
|
|
64
64
|
*
|
|
65
65
|
* @public
|
|
66
66
|
* @param {Number} periods
|
|
67
|
-
* @returns {
|
|
67
|
+
* @returns {PositionSummaryRange[]}
|
|
68
68
|
*/
|
|
69
69
|
getRecentRanges(periods) {
|
|
70
70
|
const startDate = this.getStartDate(periods);
|
|
@@ -77,8 +77,8 @@ module.exports = (() => {
|
|
|
77
77
|
* Returns the ranges for the set of {@link Transaction} objects.
|
|
78
78
|
*
|
|
79
79
|
* @public
|
|
80
|
-
* @param {
|
|
81
|
-
* @returns {
|
|
80
|
+
* @param {Transaction[]} transactions
|
|
81
|
+
* @returns {PositionSummaryRange[]}
|
|
82
82
|
*/
|
|
83
83
|
getRanges(transactions) {
|
|
84
84
|
assert.argumentIsArray(transactions, 'transactions');
|
|
@@ -91,7 +91,7 @@ module.exports = (() => {
|
|
|
91
91
|
*
|
|
92
92
|
* @public
|
|
93
93
|
* @param {Day} date
|
|
94
|
-
* @return {
|
|
94
|
+
* @return {PositionSummaryRange[]}
|
|
95
95
|
*/
|
|
96
96
|
getRangesFromDate(date) {
|
|
97
97
|
assert.argumentIsRequired(date, 'date', Day, 'Day');
|
|
@@ -180,8 +180,8 @@ module.exports = (() => {
|
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
const yearly = new PositionSummaryFrame('YEARLY', 'year', false, getYearlyRanges, getYearlyStartDate, getYearlyRangeDescription);
|
|
183
|
-
const quarterly = new PositionSummaryFrame('
|
|
184
|
-
const monthly = new PositionSummaryFrame('
|
|
183
|
+
const quarterly = new PositionSummaryFrame('QUARTERLY', 'quarter', false, getQuarterlyRanges, getQuarterlyStartDate, getQuarterlyRangeDescription);
|
|
184
|
+
const monthly = new PositionSummaryFrame('MONTHLY', 'month', false, getMonthlyRanges, getMonthlyStartDate, getMonthlyRangeDescription);
|
|
185
185
|
const ytd = new PositionSummaryFrame('YTD', 'year-to-date', true, getYearToDateRanges, getYearToDateStartDate, getYearToDateRangeDescription);
|
|
186
186
|
|
|
187
187
|
/**
|
|
@@ -241,32 +241,32 @@ module.exports = (() => {
|
|
|
241
241
|
}
|
|
242
242
|
|
|
243
243
|
function getMonthlyRanges(transactions) {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
244
|
+
const ranges = [ ];
|
|
245
|
+
|
|
246
|
+
if (!transactions.length) {
|
|
247
|
+
return ranges;
|
|
248
248
|
}
|
|
249
249
|
|
|
250
250
|
const first = array.first(transactions);
|
|
251
251
|
const last = array.last(transactions);
|
|
252
252
|
|
|
253
253
|
const firstDate = first.date;
|
|
254
|
+
|
|
254
255
|
let lastDate;
|
|
255
256
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
257
|
+
if (last.snapshot.open.getIsZero()) {
|
|
258
|
+
lastDate = new Day(last.date.year, last.date.month, last.date.day).addMonths(1);
|
|
259
|
+
} else {
|
|
260
|
+
lastDate = Day.getToday();
|
|
261
|
+
}
|
|
262
|
+
|
|
259
263
|
lastDate = lastDate.getEndOfMonth();
|
|
260
264
|
|
|
261
|
-
for (
|
|
262
|
-
let end = firstDate.getEndOfMonth();
|
|
263
|
-
end.format() <= lastDate.format();
|
|
264
|
-
end = end.addMonths(1).getEndOfMonth()
|
|
265
|
-
) {
|
|
265
|
+
for (let end = firstDate.getEndOfMonth(); end.format() <= lastDate.format(); end = end.addMonths(1).getEndOfMonth()) {
|
|
266
266
|
ranges.push(getRange(end.subtractMonths(1).getEndOfMonth(), end));
|
|
267
267
|
}
|
|
268
|
-
|
|
269
|
-
|
|
268
|
+
|
|
269
|
+
return ranges;
|
|
270
270
|
}
|
|
271
271
|
|
|
272
272
|
function getYearToDateRanges(transactions) {
|
|
@@ -303,8 +303,8 @@ module.exports = (() => {
|
|
|
303
303
|
}
|
|
304
304
|
|
|
305
305
|
function getMonthlyStartDate(periods, date) {
|
|
306
|
-
|
|
307
|
-
|
|
306
|
+
const today = date || Day.getToday();
|
|
307
|
+
|
|
308
308
|
return today
|
|
309
309
|
.subtractMonths(periods)
|
|
310
310
|
.subtractDays(today.day);
|
|
@@ -323,7 +323,7 @@ module.exports = (() => {
|
|
|
323
323
|
}
|
|
324
324
|
|
|
325
325
|
function getMonthlyRangeDescription(start, end) {
|
|
326
|
-
return
|
|
326
|
+
return `Month ended ${end.format()}`;
|
|
327
327
|
}
|
|
328
328
|
|
|
329
329
|
function getYearToDateRangeDescription(start, end) {
|
|
@@ -46,32 +46,33 @@ module.exports = (() => {
|
|
|
46
46
|
return map;
|
|
47
47
|
}, { });
|
|
48
48
|
|
|
49
|
-
const
|
|
49
|
+
const list = transactions.reduce((accumulator, transaction) => {
|
|
50
50
|
const position = transaction.position;
|
|
51
51
|
|
|
52
52
|
if (instruments.hasOwnProperty(position)) {
|
|
53
53
|
let instrument = instruments[position];
|
|
54
|
-
let formatted =
|
|
54
|
+
let formatted = { instrument: instrument };
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
const formatterFunction = formatters.get(transaction.type);
|
|
58
|
-
const formattedTransaction = formatterFunction(transaction);
|
|
56
|
+
const formatterFunctions = formatters.get(transaction.type);
|
|
59
57
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
formatterFunctions.forEach((formatterFunction) => {
|
|
59
|
+
formatterFunction(transaction, formatted);
|
|
60
|
+
});
|
|
63
61
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
});
|
|
62
|
+
Object.keys(formatted).forEach((key) => {
|
|
63
|
+
const value = formatted[key];
|
|
67
64
|
|
|
68
|
-
|
|
69
|
-
|
|
65
|
+
if (value instanceof Decimal) {
|
|
66
|
+
const precision = instrument.currency.precision;
|
|
70
67
|
|
|
71
|
-
|
|
68
|
+
formatted[key] = formatter.numberToString(value.toFloat(), precision, ',');
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
accumulator.push(formatted);
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
return
|
|
75
|
+
return accumulator;
|
|
75
76
|
}, [ ]);
|
|
76
77
|
|
|
77
78
|
let comparator;
|
|
@@ -82,13 +83,13 @@ module.exports = (() => {
|
|
|
82
83
|
comparator = comparatorAscending;
|
|
83
84
|
}
|
|
84
85
|
|
|
85
|
-
|
|
86
|
+
list.sort(comparator);
|
|
86
87
|
|
|
87
|
-
|
|
88
|
+
list.forEach((t) => {
|
|
88
89
|
delete t.instrument.id;
|
|
89
90
|
});
|
|
90
91
|
|
|
91
|
-
return
|
|
92
|
+
return list;
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
/**
|
|
@@ -120,126 +121,105 @@ module.exports = (() => {
|
|
|
120
121
|
}
|
|
121
122
|
}
|
|
122
123
|
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
instrument: i,
|
|
130
|
-
position: t.position
|
|
131
|
-
};
|
|
124
|
+
const basicFormatter = (t, f) => {
|
|
125
|
+
f.date = t.date;
|
|
126
|
+
f.type = t.type.display;
|
|
127
|
+
f.code = t.type.code;
|
|
128
|
+
f.sequence = t.sequence;
|
|
129
|
+
f.position = t.position;
|
|
132
130
|
};
|
|
133
131
|
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
132
|
+
const averageCostFormatter = (t, f) => {
|
|
133
|
+
const basis = t.snapshot.basis;
|
|
134
|
+
const open = t.snapshot.open;
|
|
135
|
+
|
|
136
|
+
let average;
|
|
137
|
+
|
|
138
|
+
if (basis && open && !open.getIsZero()) {
|
|
139
|
+
average = basis.divide(open).absolute();
|
|
140
|
+
} else {
|
|
141
|
+
average = '';
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
f.average = average;
|
|
143
145
|
};
|
|
144
146
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
fee: t.fee
|
|
162
|
-
};
|
|
147
|
+
const buySellFormatter = (t, f) => {
|
|
148
|
+
f.boughtSold = t.quantity;
|
|
149
|
+
f.price = t.trade.price;
|
|
150
|
+
f.fee = t.fee;
|
|
151
|
+
f.total = t.amount;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const dividendFormatter = (t, f) => {
|
|
155
|
+
f.shares = t.snapshot.open;
|
|
156
|
+
f.total = t.dividend.amount;
|
|
157
|
+
f.rate = t.dividend.rate;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const dividendStockFormatter = (t, f) => {
|
|
161
|
+
f.boughtSold = t.quantity;
|
|
162
|
+
f.fee = t.fee;
|
|
163
163
|
|
|
164
164
|
if (t.dividend && t.dividend.rate && t.dividend.price) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
165
|
+
f.shares = t.snapshot.open.subtract(t.quantity);
|
|
166
|
+
f.price = t.dividend.price;
|
|
167
|
+
f.rate = t.dividend.rate;
|
|
168
168
|
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const dividendReinvestFormatter = (t, f) => {
|
|
172
|
+
f.boughtSold = t.quantity;
|
|
173
|
+
f.shares = t.snapshot.open.subtract(t.quantity);
|
|
174
|
+
f.price = t.dividend.price;
|
|
175
|
+
f.fee = t.fee;
|
|
176
|
+
f.rate = t.dividend.rate;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const distributionCashFormatter = (t, f) => {
|
|
180
|
+
f.shares = t.snapshot.open;
|
|
181
|
+
f.total = t.dividend.amount;
|
|
182
|
+
f.rate = t.dividend.rate;
|
|
183
|
+
};
|
|
169
184
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
formatters.set(TransactionType.DIVIDEND_REINVEST, (t) => {
|
|
174
|
-
return {
|
|
175
|
-
boughtSold: t.quantity,
|
|
176
|
-
shares: t.snapshot.open.subtract(t.quantity),
|
|
177
|
-
price: t.dividend.price,
|
|
178
|
-
fee: t.fee,
|
|
179
|
-
rate: t.dividend.rate
|
|
180
|
-
};
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
formatters.set(TransactionType.DISTRIBUTION_CASH, (t) => {
|
|
184
|
-
return {
|
|
185
|
-
shares: t.snapshot.open,
|
|
186
|
-
total: t.dividend.amount,
|
|
187
|
-
rate: t.dividend.rate
|
|
188
|
-
};
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
formatters.set(TransactionType.DISTRIBUTION_FUND, (t) => {
|
|
192
|
-
const data = {
|
|
193
|
-
boughtSold: t.quantity,
|
|
194
|
-
fee: t.fee
|
|
195
|
-
};
|
|
185
|
+
const distributionFundFormatter = (t, f) => {
|
|
186
|
+
f.boughtSold =t.quantity;
|
|
187
|
+
f.fee = t.fee;
|
|
196
188
|
|
|
197
189
|
if (t.dividend && t.dividend.rate && t.dividend.price) {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
190
|
+
f.shares = t.snapshot.open.subtract(t.quantity);
|
|
191
|
+
f.price = t.dividend.price;
|
|
192
|
+
f.rate = t.dividend.rate;
|
|
201
193
|
}
|
|
194
|
+
};
|
|
202
195
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
return {
|
|
231
|
-
boughtSold: t.quantity
|
|
232
|
-
};
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
formatters.set(TransactionType.SPLIT, (t) => {
|
|
236
|
-
return {
|
|
237
|
-
shares: t.quantity,
|
|
238
|
-
rate: t.split.numerator.divide(t.split.denominator)
|
|
239
|
-
};
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
formatters.set(TransactionType.VALUATION, (t) => {
|
|
196
|
+
const distributionReinvestFormatter = (t, f) => {
|
|
197
|
+
f.boughtSold = t.quantity;
|
|
198
|
+
f.shares = t.snapshot.open.subtract(t.quantity);
|
|
199
|
+
f.price = t.dividend.price;
|
|
200
|
+
f.fee = t.fee;
|
|
201
|
+
f.rate = t.dividend.rate;
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const incomeFormatter = (t, f) => {
|
|
205
|
+
f.total = t.income.amount;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const feeFormatter = (t, f) => {
|
|
209
|
+
f.fee = t.charge.amount;
|
|
210
|
+
f.total = t.charge.amount;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const feeUnitsFormatter = (t, f) => {
|
|
214
|
+
f.boughtSold = t.quantity;
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const splitFormatter = (t, f) => {
|
|
218
|
+
f.shares = t.quantity;
|
|
219
|
+
f.rate = t.split.numerator.divide(t.split.denominator);
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const valuationFormatter = (t, f) => {
|
|
243
223
|
let rate;
|
|
244
224
|
|
|
245
225
|
if (t.valuation.rate) {
|
|
@@ -250,39 +230,43 @@ module.exports = (() => {
|
|
|
250
230
|
rate = t.valuation.value.divide(t.snapshot.open);
|
|
251
231
|
}
|
|
252
232
|
|
|
253
|
-
|
|
254
|
-
price: rate
|
|
255
|
-
};
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
formatters.set(TransactionType.DELIST, () => {
|
|
259
|
-
return { };
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
const cashFormatter = (t) => {
|
|
263
|
-
return {
|
|
264
|
-
total: t.quantity
|
|
265
|
-
};
|
|
233
|
+
f.price = rate;
|
|
266
234
|
};
|
|
267
235
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
formatters.set(TransactionType.DEBIT, (t) => {
|
|
272
|
-
const formatted = cashFormatter(t);
|
|
273
|
-
|
|
274
|
-
formatted.description = t.description;
|
|
236
|
+
const cashFormatter = (t, f) => {
|
|
237
|
+
f.total = t.quantity;
|
|
238
|
+
};
|
|
275
239
|
|
|
276
|
-
|
|
277
|
-
|
|
240
|
+
const debitFormatter = (t, f) => {
|
|
241
|
+
f.description = t.description;
|
|
242
|
+
};
|
|
278
243
|
|
|
279
|
-
|
|
280
|
-
|
|
244
|
+
const creditFormatter = (t, f) => {
|
|
245
|
+
f.description = t.description;
|
|
246
|
+
};
|
|
281
247
|
|
|
282
|
-
|
|
248
|
+
const formatters = new Map();
|
|
283
249
|
|
|
284
|
-
|
|
285
|
-
|
|
250
|
+
formatters.set(TransactionType.BUY, [ basicFormatter, buySellFormatter, averageCostFormatter ]);
|
|
251
|
+
formatters.set(TransactionType.SELL, [ basicFormatter, buySellFormatter, averageCostFormatter ]);
|
|
252
|
+
formatters.set(TransactionType.BUY_SHORT, [ basicFormatter, buySellFormatter, averageCostFormatter ]);
|
|
253
|
+
formatters.set(TransactionType.SELL_SHORT, [ basicFormatter, buySellFormatter, averageCostFormatter ]);
|
|
254
|
+
formatters.set(TransactionType.DIVIDEND, [ basicFormatter, dividendFormatter, averageCostFormatter ]);
|
|
255
|
+
formatters.set(TransactionType.DIVIDEND_STOCK, [ basicFormatter, dividendStockFormatter, averageCostFormatter ]);
|
|
256
|
+
formatters.set(TransactionType.DIVIDEND_REINVEST, [ basicFormatter, dividendReinvestFormatter, averageCostFormatter ]);
|
|
257
|
+
formatters.set(TransactionType.DISTRIBUTION_CASH, [ basicFormatter, distributionCashFormatter, averageCostFormatter ]);
|
|
258
|
+
formatters.set(TransactionType.DISTRIBUTION_FUND, [ basicFormatter, distributionFundFormatter, averageCostFormatter ]);
|
|
259
|
+
formatters.set(TransactionType.DISTRIBUTION_REINVEST, [ basicFormatter, distributionReinvestFormatter, averageCostFormatter ]);
|
|
260
|
+
formatters.set(TransactionType.INCOME, [ basicFormatter, incomeFormatter, averageCostFormatter ]);
|
|
261
|
+
formatters.set(TransactionType.FEE, [ basicFormatter, feeFormatter, averageCostFormatter ]);
|
|
262
|
+
formatters.set(TransactionType.FEE_UNITS, [ basicFormatter, feeUnitsFormatter, averageCostFormatter ]);
|
|
263
|
+
formatters.set(TransactionType.SPLIT, [ basicFormatter, splitFormatter, averageCostFormatter ]);
|
|
264
|
+
formatters.set(TransactionType.VALUATION, [ basicFormatter, valuationFormatter, averageCostFormatter ]);
|
|
265
|
+
formatters.set(TransactionType.DELIST, [ basicFormatter, averageCostFormatter ]);
|
|
266
|
+
formatters.set(TransactionType.DEPOSIT, [ basicFormatter, cashFormatter ]);
|
|
267
|
+
formatters.set(TransactionType.WITHDRAWAL, [ basicFormatter, cashFormatter ]);
|
|
268
|
+
formatters.set(TransactionType.DEBIT, [ basicFormatter, cashFormatter, debitFormatter ]);
|
|
269
|
+
formatters.set(TransactionType.CREDIT, [ basicFormatter, cashFormatter, creditFormatter ]);
|
|
286
270
|
|
|
287
271
|
function getInstrumentTypePriority(type) {
|
|
288
272
|
if (type === InstrumentType.CASH) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barchart/portfolio-api-common",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.125",
|
|
4
4
|
"description": "Common classes used by the Portfolio system",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Bryan Ingle",
|
|
@@ -23,9 +23,10 @@
|
|
|
23
23
|
"gulp-bump": "~1.0.0",
|
|
24
24
|
"gulp-git": "~1.6.0",
|
|
25
25
|
"gulp-jasmine": "^2.2.1",
|
|
26
|
-
"gulp-jshint": "~1.
|
|
26
|
+
"gulp-jshint": "~2.1.0",
|
|
27
27
|
"gulp-util": "^3.0.7",
|
|
28
28
|
"jsdoc": "^3.5.5",
|
|
29
|
+
"jshint": "2.9.5",
|
|
29
30
|
"run-sequence": "~1.1.4",
|
|
30
31
|
"vinyl-buffer": "^1.0.0",
|
|
31
32
|
"vinyl-source-stream": "^1.1.0"
|