@barchart/portfolio-api-common 1.2.114 → 1.2.118
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/api/failures/PortfolioFailureType.js +41 -1
- package/lib/data/TransactionType.js +12 -1
- package/lib/data/TransactionValidator.js +21 -1
- package/lib/formatters/TransactionFormatter.js +6 -2
- package/lib/processing/PositionGroup.js +6 -6
- package/lib/processing/PositionItem.js +0 -2
- package/lib/serialization/TransactionSchema.js +17 -2
- package/package.json +1 -1
- package/test/SpecRunner.js +89 -26
- package/test/specs/data/PositionSummaryFrameSpec.js +9 -12
- package/test/specs/data/TransactionValidatorSpec.js +23 -1
|
@@ -88,6 +88,18 @@ module.exports = (() => {
|
|
|
88
88
|
static get TRANSACTION_CREATE_FAILED_OUT_OF_SEQUENCE() {
|
|
89
89
|
return transactionCreateFailedOutOfSequence;
|
|
90
90
|
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* The transaction date is invalid.
|
|
94
|
+
* For example create opening transaction after delist date.
|
|
95
|
+
*
|
|
96
|
+
* @public
|
|
97
|
+
* @static
|
|
98
|
+
* @returns {FailureType}
|
|
99
|
+
*/
|
|
100
|
+
static get TRANSACTION_CREATE_FAILED_INVALID_DATE() {
|
|
101
|
+
return transactionCreateFailedInvalidDate;
|
|
102
|
+
}
|
|
91
103
|
|
|
92
104
|
/**
|
|
93
105
|
* @public
|
|
@@ -164,6 +176,18 @@ module.exports = (() => {
|
|
|
164
176
|
static get TRANSACTION_CREATE_FAILED_POSITION_LOCKED() {
|
|
165
177
|
return transactionCreateFailedPositionLocked;
|
|
166
178
|
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* The transaction (of this type) cannot be deleted by a user, instead,
|
|
182
|
+
* it is created and managed by the system (e.g. dividends).
|
|
183
|
+
*
|
|
184
|
+
* @public
|
|
185
|
+
* @static
|
|
186
|
+
* @returns {FailureType}
|
|
187
|
+
*/
|
|
188
|
+
static get TRANSACTION_DELETE_FAILED_TYPE_RESERVED() {
|
|
189
|
+
return transactionDeleteFailedTypeReserved;
|
|
190
|
+
}
|
|
167
191
|
|
|
168
192
|
/**
|
|
169
193
|
* Deleting any transaction except for the most recent requires
|
|
@@ -209,6 +233,18 @@ module.exports = (() => {
|
|
|
209
233
|
static get TRANSACTION_DELETE_FAILED_POSITION_LOCKED() {
|
|
210
234
|
return transactionDeleteFailedPositionLocked;
|
|
211
235
|
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* The transaction date is invalid.
|
|
239
|
+
* For example edit opening transaction after delist date.
|
|
240
|
+
*
|
|
241
|
+
* @public
|
|
242
|
+
* @static
|
|
243
|
+
* @returns {FailureType}
|
|
244
|
+
*/
|
|
245
|
+
static get TRANSACTION_EDIT_FAILED_INVALID_DATE() {
|
|
246
|
+
return transactionEditFailedInvalidDate;
|
|
247
|
+
}
|
|
212
248
|
|
|
213
249
|
/**
|
|
214
250
|
* Unable to edit, the transaction doesn't exist.
|
|
@@ -247,10 +283,12 @@ module.exports = (() => {
|
|
|
247
283
|
|
|
248
284
|
const transactionCreateFailedNoPosition = new FailureType('TRANSACTION_CREATE_FAILED_NO_POSITION', 'Unable to create transaction. The referenced position does not exist. Has it been deleted?');
|
|
249
285
|
const transactionCreateFailedOutOfSequence = new FailureType('TRANSACTION_CREATE_FAILED_OUT_OF_SEQUENCE', 'Unable to process transaction, because the transaction date is out-of-sequence. In other words, it would occur before an existing transaction. Please confirm your intent to re-write transaction history (which could take some time and alter the historical results for this position).');
|
|
286
|
+
const transactionCreateFailedInvalidDate = new FailureType('TRANSACTION_CREATE_FAILED_INVALID_DATE', 'Unable to process transaction with given date.');
|
|
250
287
|
const transactionCreateFailedTypeInvalidForInstrument = new FailureType('TRANSACTION_CREATE_FAILED_TYPE_INVALID_FOR_INSTRUMENT', 'Unable to process transaction, {L|transactionType.description} transactions cannot be used with {L|instrumentType.description} positions.');
|
|
251
288
|
const transactionCreateFailedTypeInvalidForDirection = new FailureType('TRANSACTION_CREATE_FAILED_TYPE_INVALID_FOR_DIRECTION', 'Unable to process transaction, a {L|positionDirection.description} position would be created (i.e. you would have {L|positionDirection.sign} shares/units). {u|instrumentType.description} positions cannot have {L|positionDirection.description} positions.');
|
|
252
289
|
const transactionCreateFailedInvalidDirectionSwitch = new FailureType('TRANSACTION_CREATE_FAILED_INVALID_DIRECTION_SWITCH', 'Unable to process transaction, the transaction would switch the position from {L|currentDirection.description} to {L|proposedDirection.description} (i.e. {L|currentDirection.sign} to {L|proposedDirection.sign} shares/units). This is not allowed. Please close the current position (i.e. zero it out) and then enter a second transaction.');
|
|
253
290
|
const transactionCreateFailedInvalidInitialType = new FailureType('TRANSACTION_CREATE_FAILED_INVALID_INITIAL_TYPE', 'Unable to process operation because the first transaction would to be a {U|transactionType.description}, which is not allowed -- since {U|transactionType.description} transactions cannot open a position.');
|
|
291
|
+
|
|
254
292
|
const transactionCreateFailedTypeReserved = new FailureType('TRANSACTION_CREATE_FAILED_TYPE_RESERVED', 'Unable to create {U|type.description} transaction, this type of transaction is managed by the system.');
|
|
255
293
|
const transactionCreateFailedReinvestPriceUnavailable = new FailureType('TRANSACTION_CREATE_FAILED_REINVEST_PRICE_UNAVAILABLE', 'Unable to create transaction, a dividend was paid on {L|day}; however no historical price is available for this day. To successfully create this transaction, please turn off dividend reinvestment for this position.');
|
|
256
294
|
const transactionCreateFailedPositionLocked = new FailureType('TRANSACTION_CREATE_FAILED_POSITION_LOCKED', 'Unable to create transaction, your {L|description} history is being recalculated. Please re-enter this transaction in a minute or two.');
|
|
@@ -259,9 +297,11 @@ module.exports = (() => {
|
|
|
259
297
|
const transactionDeleteFailedNoTransaction = new FailureType('TRANSACTION_DELETE_FAILED_NO_TRANSACTION', 'Unable to delete transaction. The referenced transaction does not exist.');
|
|
260
298
|
const transactionDeleteFailedDirectionSwitchOnRewrite = new FailureType('TRANSACTION_DELETE_FAILED_DIRECTION_SWITCH_ON_REWRITE', 'Deleting this transaction would cause your history to be re-written and the position to switch from long to short (i.e. positive to negative) or vice versa.');
|
|
261
299
|
const transactionDeleteFailedPositionLocked = new FailureType('TRANSACTION_DELETE_FAILED_POSITION_LOCKED', 'Unable to delete transaction, your {L|description} history is being recalculated. Please wait a minute or two and retry.');
|
|
300
|
+
const transactionDeleteFailedTypeReserved = new FailureType('TRANSACTION_DELETE_FAILED_TYPE_RESERVED', 'Unable to delete {U|type.description} transaction, this type of transaction is managed by the system.');
|
|
262
301
|
|
|
302
|
+
const transactionEditFailedInvalidDate = new FailureType('TRANSACTION_EDIT_FAILED_INVALID_DATE', 'Unable to edit transaction with given date.');
|
|
263
303
|
const transactionEditFailedNoTransaction = new FailureType('TRANSACTION_EDIT_FAILED_NO_TRANSACTION', 'Unable to edit transaction. The referenced transaction does not exist.');
|
|
264
304
|
const transactionEditFailedTypeReserved = new FailureType('TRANSACTION_EDIT_FAILED_TYPE_RESERVED', 'Unable to edit {U|type.description} transaction, this type of transaction is managed by the system.');
|
|
265
305
|
|
|
266
306
|
return PortfolioFailureType;
|
|
267
|
-
})();
|
|
307
|
+
})();
|
|
@@ -22,7 +22,6 @@ module.exports = (() => {
|
|
|
22
22
|
* @param {Boolean} corporateAction
|
|
23
23
|
* @param {Boolean} initial
|
|
24
24
|
* @param {Boolean} significant
|
|
25
|
-
* @param {Boolean} eod
|
|
26
25
|
*/
|
|
27
26
|
class TransactionType extends Enum {
|
|
28
27
|
constructor(code, description, display, sequence, purchase, sale, income, opening, closing, fee, corporateAction, initial, significant) {
|
|
@@ -352,6 +351,17 @@ module.exports = (() => {
|
|
|
352
351
|
static get DEBIT() {
|
|
353
352
|
return debit;
|
|
354
353
|
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* A system-generated transaction, indicating the security has stopped active trading.
|
|
357
|
+
*
|
|
358
|
+
* @public
|
|
359
|
+
* @static
|
|
360
|
+
* @returns {TransactionType}
|
|
361
|
+
*/
|
|
362
|
+
static get DELIST() {
|
|
363
|
+
return delist;
|
|
364
|
+
}
|
|
355
365
|
|
|
356
366
|
/**
|
|
357
367
|
* A system-generated deposit, arising from another transaction.
|
|
@@ -401,6 +411,7 @@ module.exports = (() => {
|
|
|
401
411
|
const split = new TransactionType('SP', 'Split', 'Split', 1, false, false, false, true, false, false, true, false, false);
|
|
402
412
|
const fee = new TransactionType('F', 'Fee', 'Fee', 0, false, false, false, false, false, true, false, false, false);
|
|
403
413
|
const feeUnits = new TransactionType('FU', 'Fee Units', 'Fee', 0, false, false, false, false, true, false, false, false, false);
|
|
414
|
+
const delist = new TransactionType('DL', 'Delist', 'Delist', 1, false, false, false, false, false, false, true, false, false);
|
|
404
415
|
|
|
405
416
|
const distributionCash = new TransactionType('DC', 'Distribution (Cash)', 'Cash Distribution', 1, false, false, true, false, false, false, true, false, false);
|
|
406
417
|
const distributionReinvest = new TransactionType('DY', 'Distribution (Reinvested)', 'Distribution Reinvest', 1, false, false, false, true, false, false, true, false, false);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const assert = require('@barchart/common-js/lang/assert'),
|
|
2
2
|
array = require('@barchart/common-js/lang/array'),
|
|
3
|
-
is = require('@barchart/common-js/lang/is')
|
|
3
|
+
is = require('@barchart/common-js/lang/is'),
|
|
4
|
+
Day = require('@barchart/common-js/lang/Day');
|
|
4
5
|
|
|
5
6
|
const InstrumentType = require('./InstrumentType'),
|
|
6
7
|
PositionDirection = require('./PositionDirection'),
|
|
@@ -217,6 +218,23 @@ module.exports = (() => {
|
|
|
217
218
|
static validateDirectionSwitch(instrumentType, currentDirection, proposedDirection) {
|
|
218
219
|
return currentDirection === null || instrumentType.canSwitchDirection || (currentDirection.closed || proposedDirection.closed || currentDirection.positive === proposedDirection.positive);
|
|
219
220
|
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Assuming the transaction list is ordered by sequence, validates that
|
|
224
|
+
* no opening transactions exist after delisting date.
|
|
225
|
+
*
|
|
226
|
+
* @static
|
|
227
|
+
* @public
|
|
228
|
+
* @param {Array.<Object>} transactions
|
|
229
|
+
* @returns {Boolean}
|
|
230
|
+
*/
|
|
231
|
+
static validateDelisting(transactions) {
|
|
232
|
+
assert.argumentIsArray(transactions, 'transactions');
|
|
233
|
+
|
|
234
|
+
const delistIndex = transactions.findIndex(t => t.type === TransactionType.DELIST);
|
|
235
|
+
|
|
236
|
+
return delistIndex < 0 || !transactions.some((t, i) => delistIndex < i && t.type.opening);
|
|
237
|
+
}
|
|
220
238
|
|
|
221
239
|
toString() {
|
|
222
240
|
return '[TransactionValidator]';
|
|
@@ -244,6 +262,7 @@ module.exports = (() => {
|
|
|
244
262
|
associateTypes(InstrumentType.EQUITY, TransactionType.DIVIDEND_REINVEST, false);
|
|
245
263
|
associateTypes(InstrumentType.EQUITY, TransactionType.DIVIDEND_STOCK, false);
|
|
246
264
|
associateTypes(InstrumentType.EQUITY, TransactionType.SPLIT, false);
|
|
265
|
+
associateTypes(InstrumentType.EQUITY, TransactionType.DELIST, false);
|
|
247
266
|
|
|
248
267
|
associateTypes(InstrumentType.FUND, TransactionType.BUY, true, [ PositionDirection.LONG, PositionDirection.EVEN ]);
|
|
249
268
|
associateTypes(InstrumentType.FUND, TransactionType.SELL, true, [ PositionDirection.LONG ]);
|
|
@@ -252,6 +271,7 @@ module.exports = (() => {
|
|
|
252
271
|
associateTypes(InstrumentType.FUND, TransactionType.DISTRIBUTION_CASH, false);
|
|
253
272
|
associateTypes(InstrumentType.FUND, TransactionType.DISTRIBUTION_REINVEST, false);
|
|
254
273
|
associateTypes(InstrumentType.FUND, TransactionType.DISTRIBUTION_FUND, false);
|
|
274
|
+
associateTypes(InstrumentType.FUND, TransactionType.DELIST, false);
|
|
255
275
|
|
|
256
276
|
associateTypes(InstrumentType.OTHER, TransactionType.BUY, true, [ PositionDirection.LONG, PositionDirection.EVEN ]);
|
|
257
277
|
associateTypes(InstrumentType.OTHER, TransactionType.SELL, true, [ PositionDirection.LONG ]);
|
|
@@ -216,7 +216,7 @@ module.exports = (() => {
|
|
|
216
216
|
formatters.set(TransactionType.INCOME, (t) => {
|
|
217
217
|
return {
|
|
218
218
|
total: t.income.amount
|
|
219
|
-
};
|
|
219
|
+
};
|
|
220
220
|
});
|
|
221
221
|
|
|
222
222
|
formatters.set(TransactionType.FEE, (t) => {
|
|
@@ -254,6 +254,10 @@ module.exports = (() => {
|
|
|
254
254
|
price: rate
|
|
255
255
|
};
|
|
256
256
|
});
|
|
257
|
+
|
|
258
|
+
formatters.set(TransactionType.DELIST, () => {
|
|
259
|
+
return { };
|
|
260
|
+
});
|
|
257
261
|
|
|
258
262
|
const cashFormatter = (t) => {
|
|
259
263
|
return {
|
|
@@ -298,4 +302,4 @@ module.exports = (() => {
|
|
|
298
302
|
.toComparator();
|
|
299
303
|
|
|
300
304
|
return TransactionFormatter;
|
|
301
|
-
})();
|
|
305
|
+
})();
|
|
@@ -786,9 +786,9 @@ module.exports = (() => {
|
|
|
786
786
|
actual.periodUnrealizedBasisPrevious = item.data.periodUnrealizedBasisPrevious;
|
|
787
787
|
actual.periodUnrealizedBasisPrevious2 = item.data.periodUnrealizedBasisPrevious2;
|
|
788
788
|
|
|
789
|
-
actual.periodPercent = calculatePeriodPercent(actual.
|
|
790
|
-
actual.periodPercentPrevious = calculatePeriodPercent(actual.
|
|
791
|
-
actual.periodPercentPrevious2 = calculatePeriodPercent(actual.
|
|
789
|
+
actual.periodPercent = calculatePeriodPercent(actual.summaryTotalCurrent, actual.periodRealizedBasis, actual.periodUnrealizedBasis);
|
|
790
|
+
actual.periodPercentPrevious = calculatePeriodPercent(actual.summaryTotalPrevious, actual.periodRealizedBasisPrevious, actual.periodUnrealizedBasisPrevious);
|
|
791
|
+
actual.periodPercentPrevious2 = calculatePeriodPercent(actual.summaryTotalPrevious2, actual.periodRealizedBasisPrevious2, actual.periodUnrealizedBasisPrevious2);
|
|
792
792
|
|
|
793
793
|
format.periodPercent = formatPercent(actual.periodPercent, 2);
|
|
794
794
|
format.periodPercentPrevious = formatPercent(actual.periodPercentPrevious, 2);
|
|
@@ -921,7 +921,7 @@ module.exports = (() => {
|
|
|
921
921
|
if (group.single && item) {
|
|
922
922
|
actual.periodUnrealized = item.data.periodUnrealized;
|
|
923
923
|
|
|
924
|
-
actual.periodPercent = calculatePeriodPercent(actual.
|
|
924
|
+
actual.periodPercent = calculatePeriodPercent(actual.summaryTotalCurrent, actual.periodRealizedBasis, actual.periodUnrealizedBasis);
|
|
925
925
|
format.periodPercent = formatPercent(actual.periodPercent, 2);
|
|
926
926
|
}
|
|
927
927
|
}
|
|
@@ -982,8 +982,8 @@ module.exports = (() => {
|
|
|
982
982
|
}
|
|
983
983
|
}
|
|
984
984
|
|
|
985
|
-
function calculatePeriodPercent(
|
|
986
|
-
const numerator =
|
|
985
|
+
function calculatePeriodPercent(periodSummaryTotal, realizedBasis, unrealizedBasis) {
|
|
986
|
+
const numerator = periodSummaryTotal;
|
|
987
987
|
const denominator = realizedBasis.add(unrealizedBasis);
|
|
988
988
|
|
|
989
989
|
return denominator.getIsZero() ? Decimal.ZERO : numerator.divide(denominator);
|
|
@@ -113,6 +113,10 @@ module.exports = (() => {
|
|
|
113
113
|
static get VALUATION() {
|
|
114
114
|
return valuation;
|
|
115
115
|
}
|
|
116
|
+
|
|
117
|
+
static get DELIST() {
|
|
118
|
+
return delist;
|
|
119
|
+
}
|
|
116
120
|
|
|
117
121
|
static get INCOME() {
|
|
118
122
|
return income;
|
|
@@ -322,8 +326,18 @@ module.exports = (() => {
|
|
|
322
326
|
.withField('force', DataType.BOOLEAN, true)
|
|
323
327
|
.schema
|
|
324
328
|
);
|
|
325
|
-
|
|
326
|
-
const
|
|
329
|
+
|
|
330
|
+
const delist = new TransactionSchema(SchemaBuilder.withName(TransactionType.DELIST.code)
|
|
331
|
+
.withField('portfolio', DataType.STRING)
|
|
332
|
+
.withField('position', DataType.STRING)
|
|
333
|
+
.withField('sequence', DataType.NUMBER, true)
|
|
334
|
+
.withField('type', DataType.forEnum(TransactionType, 'TransactionType'))
|
|
335
|
+
.withField('date', DataType.DAY)
|
|
336
|
+
.withField('force', DataType.BOOLEAN, true)
|
|
337
|
+
.schema
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
const income = new TransactionSchema(SchemaBuilder.withName(TransactionType.INCOME.code)
|
|
327
341
|
.withField('portfolio', DataType.STRING)
|
|
328
342
|
.withField('position', DataType.STRING)
|
|
329
343
|
.withField('sequence', DataType.NUMBER, true)
|
|
@@ -349,6 +363,7 @@ module.exports = (() => {
|
|
|
349
363
|
addSchemaToMap(TransactionType.DEPOSIT, deposit);
|
|
350
364
|
addSchemaToMap(TransactionType.WITHDRAWAL, withdrawal);
|
|
351
365
|
addSchemaToMap(TransactionType.VALUATION, valuation);
|
|
366
|
+
addSchemaToMap(TransactionType.DELIST, delist);
|
|
352
367
|
addSchemaToMap(TransactionType.INCOME, income);
|
|
353
368
|
|
|
354
369
|
return TransactionSchema;
|
package/package.json
CHANGED
package/test/SpecRunner.js
CHANGED
|
@@ -759,7 +759,6 @@ module.exports = (() => {
|
|
|
759
759
|
* @param {Boolean} corporateAction
|
|
760
760
|
* @param {Boolean} initial
|
|
761
761
|
* @param {Boolean} significant
|
|
762
|
-
* @param {Boolean} eod
|
|
763
762
|
*/
|
|
764
763
|
class TransactionType extends Enum {
|
|
765
764
|
constructor(code, description, display, sequence, purchase, sale, income, opening, closing, fee, corporateAction, initial, significant) {
|
|
@@ -1089,6 +1088,17 @@ module.exports = (() => {
|
|
|
1089
1088
|
static get DEBIT() {
|
|
1090
1089
|
return debit;
|
|
1091
1090
|
}
|
|
1091
|
+
|
|
1092
|
+
/**
|
|
1093
|
+
* A system-generated transaction, indicating the security has stopped active trading.
|
|
1094
|
+
*
|
|
1095
|
+
* @public
|
|
1096
|
+
* @static
|
|
1097
|
+
* @returns {TransactionType}
|
|
1098
|
+
*/
|
|
1099
|
+
static get DELIST() {
|
|
1100
|
+
return delist;
|
|
1101
|
+
}
|
|
1092
1102
|
|
|
1093
1103
|
/**
|
|
1094
1104
|
* A system-generated deposit, arising from another transaction.
|
|
@@ -1138,6 +1148,7 @@ module.exports = (() => {
|
|
|
1138
1148
|
const split = new TransactionType('SP', 'Split', 'Split', 1, false, false, false, true, false, false, true, false, false);
|
|
1139
1149
|
const fee = new TransactionType('F', 'Fee', 'Fee', 0, false, false, false, false, false, true, false, false, false);
|
|
1140
1150
|
const feeUnits = new TransactionType('FU', 'Fee Units', 'Fee', 0, false, false, false, false, true, false, false, false, false);
|
|
1151
|
+
const delist = new TransactionType('DL', 'Delist', 'Delist', 1, false, false, false, false, false, false, true, false, false);
|
|
1141
1152
|
|
|
1142
1153
|
const distributionCash = new TransactionType('DC', 'Distribution (Cash)', 'Cash Distribution', 1, false, false, true, false, false, false, true, false, false);
|
|
1143
1154
|
const distributionReinvest = new TransactionType('DY', 'Distribution (Reinvested)', 'Distribution Reinvest', 1, false, false, false, true, false, false, true, false, false);
|
|
@@ -1157,7 +1168,8 @@ module.exports = (() => {
|
|
|
1157
1168
|
},{"@barchart/common-js/lang/Enum":24,"@barchart/common-js/lang/assert":29}],5:[function(require,module,exports){
|
|
1158
1169
|
const assert = require('@barchart/common-js/lang/assert'),
|
|
1159
1170
|
array = require('@barchart/common-js/lang/array'),
|
|
1160
|
-
is = require('@barchart/common-js/lang/is')
|
|
1171
|
+
is = require('@barchart/common-js/lang/is'),
|
|
1172
|
+
Day = require('@barchart/common-js/lang/Day');
|
|
1161
1173
|
|
|
1162
1174
|
const InstrumentType = require('./InstrumentType'),
|
|
1163
1175
|
PositionDirection = require('./PositionDirection'),
|
|
@@ -1374,6 +1386,23 @@ module.exports = (() => {
|
|
|
1374
1386
|
static validateDirectionSwitch(instrumentType, currentDirection, proposedDirection) {
|
|
1375
1387
|
return currentDirection === null || instrumentType.canSwitchDirection || (currentDirection.closed || proposedDirection.closed || currentDirection.positive === proposedDirection.positive);
|
|
1376
1388
|
}
|
|
1389
|
+
|
|
1390
|
+
/**
|
|
1391
|
+
* Assuming the transaction list is ordered by sequence, validates that
|
|
1392
|
+
* no opening transactions exist after delisting date.
|
|
1393
|
+
*
|
|
1394
|
+
* @static
|
|
1395
|
+
* @public
|
|
1396
|
+
* @param {Array.<Object>} transactions
|
|
1397
|
+
* @returns {Boolean}
|
|
1398
|
+
*/
|
|
1399
|
+
static validateDelisting(transactions) {
|
|
1400
|
+
assert.argumentIsArray(transactions, 'transactions');
|
|
1401
|
+
|
|
1402
|
+
const delistIndex = transactions.findIndex(t => t.type === TransactionType.DELIST);
|
|
1403
|
+
|
|
1404
|
+
return delistIndex < 0 || !transactions.some((t, i) => delistIndex < i && t.type.opening);
|
|
1405
|
+
}
|
|
1377
1406
|
|
|
1378
1407
|
toString() {
|
|
1379
1408
|
return '[TransactionValidator]';
|
|
@@ -1401,6 +1430,7 @@ module.exports = (() => {
|
|
|
1401
1430
|
associateTypes(InstrumentType.EQUITY, TransactionType.DIVIDEND_REINVEST, false);
|
|
1402
1431
|
associateTypes(InstrumentType.EQUITY, TransactionType.DIVIDEND_STOCK, false);
|
|
1403
1432
|
associateTypes(InstrumentType.EQUITY, TransactionType.SPLIT, false);
|
|
1433
|
+
associateTypes(InstrumentType.EQUITY, TransactionType.DELIST, false);
|
|
1404
1434
|
|
|
1405
1435
|
associateTypes(InstrumentType.FUND, TransactionType.BUY, true, [ PositionDirection.LONG, PositionDirection.EVEN ]);
|
|
1406
1436
|
associateTypes(InstrumentType.FUND, TransactionType.SELL, true, [ PositionDirection.LONG ]);
|
|
@@ -1409,6 +1439,7 @@ module.exports = (() => {
|
|
|
1409
1439
|
associateTypes(InstrumentType.FUND, TransactionType.DISTRIBUTION_CASH, false);
|
|
1410
1440
|
associateTypes(InstrumentType.FUND, TransactionType.DISTRIBUTION_REINVEST, false);
|
|
1411
1441
|
associateTypes(InstrumentType.FUND, TransactionType.DISTRIBUTION_FUND, false);
|
|
1442
|
+
associateTypes(InstrumentType.FUND, TransactionType.DELIST, false);
|
|
1412
1443
|
|
|
1413
1444
|
associateTypes(InstrumentType.OTHER, TransactionType.BUY, true, [ PositionDirection.LONG, PositionDirection.EVEN ]);
|
|
1414
1445
|
associateTypes(InstrumentType.OTHER, TransactionType.SELL, true, [ PositionDirection.LONG ]);
|
|
@@ -1450,7 +1481,7 @@ module.exports = (() => {
|
|
|
1450
1481
|
return TransactionValidator;
|
|
1451
1482
|
})();
|
|
1452
1483
|
|
|
1453
|
-
},{"./InstrumentType":1,"./PositionDirection":2,"./TransactionType":4,"@barchart/common-js/lang/array":28,"@barchart/common-js/lang/assert":29,"@barchart/common-js/lang/is":33}],6:[function(require,module,exports){
|
|
1484
|
+
},{"./InstrumentType":1,"./PositionDirection":2,"./TransactionType":4,"@barchart/common-js/lang/Day":21,"@barchart/common-js/lang/array":28,"@barchart/common-js/lang/assert":29,"@barchart/common-js/lang/is":33}],6:[function(require,module,exports){
|
|
1454
1485
|
const array = require('@barchart/common-js/lang/array'),
|
|
1455
1486
|
assert = require('@barchart/common-js/lang/assert'),
|
|
1456
1487
|
ComparatorBuilder = require('@barchart/common-js/collections/sorting/ComparatorBuilder'),
|
|
@@ -3275,9 +3306,9 @@ module.exports = (() => {
|
|
|
3275
3306
|
actual.periodUnrealizedBasisPrevious = item.data.periodUnrealizedBasisPrevious;
|
|
3276
3307
|
actual.periodUnrealizedBasisPrevious2 = item.data.periodUnrealizedBasisPrevious2;
|
|
3277
3308
|
|
|
3278
|
-
actual.periodPercent = calculatePeriodPercent(actual.
|
|
3279
|
-
actual.periodPercentPrevious = calculatePeriodPercent(actual.
|
|
3280
|
-
actual.periodPercentPrevious2 = calculatePeriodPercent(actual.
|
|
3309
|
+
actual.periodPercent = calculatePeriodPercent(actual.summaryTotalCurrent, actual.periodRealizedBasis, actual.periodUnrealizedBasis);
|
|
3310
|
+
actual.periodPercentPrevious = calculatePeriodPercent(actual.summaryTotalPrevious, actual.periodRealizedBasisPrevious, actual.periodUnrealizedBasisPrevious);
|
|
3311
|
+
actual.periodPercentPrevious2 = calculatePeriodPercent(actual.summaryTotalPrevious2, actual.periodRealizedBasisPrevious2, actual.periodUnrealizedBasisPrevious2);
|
|
3281
3312
|
|
|
3282
3313
|
format.periodPercent = formatPercent(actual.periodPercent, 2);
|
|
3283
3314
|
format.periodPercentPrevious = formatPercent(actual.periodPercentPrevious, 2);
|
|
@@ -3410,7 +3441,7 @@ module.exports = (() => {
|
|
|
3410
3441
|
if (group.single && item) {
|
|
3411
3442
|
actual.periodUnrealized = item.data.periodUnrealized;
|
|
3412
3443
|
|
|
3413
|
-
actual.periodPercent = calculatePeriodPercent(actual.
|
|
3444
|
+
actual.periodPercent = calculatePeriodPercent(actual.summaryTotalCurrent, actual.periodRealizedBasis, actual.periodUnrealizedBasis);
|
|
3414
3445
|
format.periodPercent = formatPercent(actual.periodPercent, 2);
|
|
3415
3446
|
}
|
|
3416
3447
|
}
|
|
@@ -3471,8 +3502,8 @@ module.exports = (() => {
|
|
|
3471
3502
|
}
|
|
3472
3503
|
}
|
|
3473
3504
|
|
|
3474
|
-
function calculatePeriodPercent(
|
|
3475
|
-
const numerator =
|
|
3505
|
+
function calculatePeriodPercent(periodSummaryTotal, realizedBasis, unrealizedBasis) {
|
|
3506
|
+
const numerator = periodSummaryTotal;
|
|
3476
3507
|
const denominator = realizedBasis.add(unrealizedBasis);
|
|
3477
3508
|
|
|
3478
3509
|
return denominator.getIsZero() ? Decimal.ZERO : numerator.divide(denominator);
|
|
@@ -4134,8 +4165,6 @@ module.exports = (() => {
|
|
|
4134
4165
|
let returnRef;
|
|
4135
4166
|
|
|
4136
4167
|
if (currentSummary) {
|
|
4137
|
-
const period = currentSummary.period;
|
|
4138
|
-
|
|
4139
4168
|
returnRef = currentSummary.end.basis.absolute();
|
|
4140
4169
|
} else {
|
|
4141
4170
|
returnRef = Decimal.ZERO;
|
|
@@ -4701,6 +4730,10 @@ module.exports = (() => {
|
|
|
4701
4730
|
static get VALUATION() {
|
|
4702
4731
|
return valuation;
|
|
4703
4732
|
}
|
|
4733
|
+
|
|
4734
|
+
static get DELIST() {
|
|
4735
|
+
return delist;
|
|
4736
|
+
}
|
|
4704
4737
|
|
|
4705
4738
|
static get INCOME() {
|
|
4706
4739
|
return income;
|
|
@@ -4910,8 +4943,18 @@ module.exports = (() => {
|
|
|
4910
4943
|
.withField('force', DataType.BOOLEAN, true)
|
|
4911
4944
|
.schema
|
|
4912
4945
|
);
|
|
4913
|
-
|
|
4914
|
-
const
|
|
4946
|
+
|
|
4947
|
+
const delist = new TransactionSchema(SchemaBuilder.withName(TransactionType.DELIST.code)
|
|
4948
|
+
.withField('portfolio', DataType.STRING)
|
|
4949
|
+
.withField('position', DataType.STRING)
|
|
4950
|
+
.withField('sequence', DataType.NUMBER, true)
|
|
4951
|
+
.withField('type', DataType.forEnum(TransactionType, 'TransactionType'))
|
|
4952
|
+
.withField('date', DataType.DAY)
|
|
4953
|
+
.withField('force', DataType.BOOLEAN, true)
|
|
4954
|
+
.schema
|
|
4955
|
+
);
|
|
4956
|
+
|
|
4957
|
+
const income = new TransactionSchema(SchemaBuilder.withName(TransactionType.INCOME.code)
|
|
4915
4958
|
.withField('portfolio', DataType.STRING)
|
|
4916
4959
|
.withField('position', DataType.STRING)
|
|
4917
4960
|
.withField('sequence', DataType.NUMBER, true)
|
|
@@ -4937,6 +4980,7 @@ module.exports = (() => {
|
|
|
4937
4980
|
addSchemaToMap(TransactionType.DEPOSIT, deposit);
|
|
4938
4981
|
addSchemaToMap(TransactionType.WITHDRAWAL, withdrawal);
|
|
4939
4982
|
addSchemaToMap(TransactionType.VALUATION, valuation);
|
|
4983
|
+
addSchemaToMap(TransactionType.DELIST, delist);
|
|
4940
4984
|
addSchemaToMap(TransactionType.INCOME, income);
|
|
4941
4985
|
|
|
4942
4986
|
return TransactionSchema;
|
|
@@ -17875,10 +17919,6 @@ describe('After the PositionSummaryFrame enumeration is initialized', () => {
|
|
|
17875
17919
|
});
|
|
17876
17920
|
});
|
|
17877
17921
|
|
|
17878
|
-
|
|
17879
|
-
|
|
17880
|
-
/////
|
|
17881
|
-
|
|
17882
17922
|
describe('and month position summary ranges are processed for a transaction set that does not close', () => {
|
|
17883
17923
|
let ranges;
|
|
17884
17924
|
|
|
@@ -17903,8 +17943,8 @@ describe('After the PositionSummaryFrame enumeration is initialized', () => {
|
|
|
17903
17943
|
ranges = PositionSummaryFrame.MONTHLY.getRanges(transactions);
|
|
17904
17944
|
});
|
|
17905
17945
|
|
|
17906
|
-
it('should have
|
|
17907
|
-
expect(ranges.length).toEqual(
|
|
17946
|
+
it('should have three ranges (assuming the current year is 2018 and the current month is December)', () => {
|
|
17947
|
+
expect(ranges.length).toEqual(3);
|
|
17908
17948
|
});
|
|
17909
17949
|
|
|
17910
17950
|
it('the first range should be from 2018-09-30 to 2018-10-31', () => {
|
|
@@ -17913,14 +17953,15 @@ describe('After the PositionSummaryFrame enumeration is initialized', () => {
|
|
|
17913
17953
|
});
|
|
17914
17954
|
|
|
17915
17955
|
it('the second range should be from 2018-10-31 to 2018-11-30', () => {
|
|
17916
|
-
|
|
17917
|
-
|
|
17956
|
+
expect(ranges[1].start.format()).toEqual('2018-10-31');
|
|
17957
|
+
expect(ranges[1].end.format()).toEqual('2018-11-30');
|
|
17918
17958
|
});
|
|
17919
|
-
});
|
|
17920
|
-
|
|
17921
|
-
///////
|
|
17922
|
-
|
|
17923
17959
|
|
|
17960
|
+
it('the third range should be from 2018-10-31 to 2018-11-30', () => {
|
|
17961
|
+
expect(ranges[2].start.format()).toEqual('2018-11-30');
|
|
17962
|
+
expect(ranges[2].end.format()).toEqual('2018-12-31');
|
|
17963
|
+
});
|
|
17964
|
+
});
|
|
17924
17965
|
|
|
17925
17966
|
describe('and getting the start date for yearly frames', () => {
|
|
17926
17967
|
describe('for one year ago', function() {
|
|
@@ -18126,7 +18167,7 @@ describe('When validating transaction order', () => {
|
|
|
18126
18167
|
'use strict';
|
|
18127
18168
|
|
|
18128
18169
|
const build = (sequence, day, type) => {
|
|
18129
|
-
return { sequence: sequence, date: Day.parse(day), type: (type || TransactionType.BUY
|
|
18170
|
+
return { sequence: sequence, date: Day.parse(day), type: (type || TransactionType.BUY) };
|
|
18130
18171
|
};
|
|
18131
18172
|
|
|
18132
18173
|
it('An array of zero transactions should be valid', () => {
|
|
@@ -18198,6 +18239,28 @@ describe('When validating transaction references', () => {
|
|
|
18198
18239
|
});
|
|
18199
18240
|
});
|
|
18200
18241
|
|
|
18242
|
+
describe('When validating transactions which could include instrument delisting', () => {
|
|
18243
|
+
const build = (type) => {
|
|
18244
|
+
return { type: type };
|
|
18245
|
+
};
|
|
18246
|
+
|
|
18247
|
+
it('An array without a DELSIT transaction should be valid', () => {
|
|
18248
|
+
expect(TransactionValidator.validateDelisting([ build(TransactionType.BUY), build(TransactionType.SELL) ])).toEqual(true);
|
|
18249
|
+
});
|
|
18250
|
+
|
|
18251
|
+
it('An array with a final DELSIT transaction should be valid', () => {
|
|
18252
|
+
expect(TransactionValidator.validateDelisting([ build(TransactionType.BUY), build(TransactionType.SELL), build(TransactionType.DELIST) ])).toEqual(true);
|
|
18253
|
+
});
|
|
18254
|
+
|
|
18255
|
+
it('An array with a closing transaction after a DELIST transaction should be valid', () => {
|
|
18256
|
+
expect(TransactionValidator.validateDelisting([ build(TransactionType.BUY), build(TransactionType.SELL), build(TransactionType.DELIST), build(TransactionType.SELL) ])).toEqual(true);
|
|
18257
|
+
});
|
|
18258
|
+
|
|
18259
|
+
it('An array with an opening transaction after a DELIST transaction should not be valid', () => {
|
|
18260
|
+
expect(TransactionValidator.validateDelisting([ build(TransactionType.BUY), build(TransactionType.SELL), build(TransactionType.DELIST), build(TransactionType.BUY) ])).toEqual(false);
|
|
18261
|
+
});
|
|
18262
|
+
});
|
|
18263
|
+
|
|
18201
18264
|
describe('When requesting all the user-initiated transaction types', () => {
|
|
18202
18265
|
'use strict';
|
|
18203
18266
|
|
|
@@ -311,10 +311,6 @@ describe('After the PositionSummaryFrame enumeration is initialized', () => {
|
|
|
311
311
|
});
|
|
312
312
|
});
|
|
313
313
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
/////
|
|
317
|
-
|
|
318
314
|
describe('and month position summary ranges are processed for a transaction set that does not close', () => {
|
|
319
315
|
let ranges;
|
|
320
316
|
|
|
@@ -339,8 +335,8 @@ describe('After the PositionSummaryFrame enumeration is initialized', () => {
|
|
|
339
335
|
ranges = PositionSummaryFrame.MONTHLY.getRanges(transactions);
|
|
340
336
|
});
|
|
341
337
|
|
|
342
|
-
it('should have
|
|
343
|
-
expect(ranges.length).toEqual(
|
|
338
|
+
it('should have three ranges (assuming the current year is 2018 and the current month is December)', () => {
|
|
339
|
+
expect(ranges.length).toEqual(3);
|
|
344
340
|
});
|
|
345
341
|
|
|
346
342
|
it('the first range should be from 2018-09-30 to 2018-10-31', () => {
|
|
@@ -349,14 +345,15 @@ describe('After the PositionSummaryFrame enumeration is initialized', () => {
|
|
|
349
345
|
});
|
|
350
346
|
|
|
351
347
|
it('the second range should be from 2018-10-31 to 2018-11-30', () => {
|
|
352
|
-
|
|
353
|
-
|
|
348
|
+
expect(ranges[1].start.format()).toEqual('2018-10-31');
|
|
349
|
+
expect(ranges[1].end.format()).toEqual('2018-11-30');
|
|
354
350
|
});
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
///////
|
|
358
|
-
|
|
359
351
|
|
|
352
|
+
it('the third range should be from 2018-10-31 to 2018-11-30', () => {
|
|
353
|
+
expect(ranges[2].start.format()).toEqual('2018-11-30');
|
|
354
|
+
expect(ranges[2].end.format()).toEqual('2018-12-31');
|
|
355
|
+
});
|
|
356
|
+
});
|
|
360
357
|
|
|
361
358
|
describe('and getting the start date for yearly frames', () => {
|
|
362
359
|
describe('for one year ago', function() {
|
|
@@ -7,7 +7,7 @@ describe('When validating transaction order', () => {
|
|
|
7
7
|
'use strict';
|
|
8
8
|
|
|
9
9
|
const build = (sequence, day, type) => {
|
|
10
|
-
return { sequence: sequence, date: Day.parse(day), type: (type || TransactionType.BUY
|
|
10
|
+
return { sequence: sequence, date: Day.parse(day), type: (type || TransactionType.BUY) };
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
it('An array of zero transactions should be valid', () => {
|
|
@@ -79,6 +79,28 @@ describe('When validating transaction references', () => {
|
|
|
79
79
|
});
|
|
80
80
|
});
|
|
81
81
|
|
|
82
|
+
describe('When validating transactions which could include instrument delisting', () => {
|
|
83
|
+
const build = (type) => {
|
|
84
|
+
return { type: type };
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
it('An array without a DELSIT transaction should be valid', () => {
|
|
88
|
+
expect(TransactionValidator.validateDelisting([ build(TransactionType.BUY), build(TransactionType.SELL) ])).toEqual(true);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('An array with a final DELSIT transaction should be valid', () => {
|
|
92
|
+
expect(TransactionValidator.validateDelisting([ build(TransactionType.BUY), build(TransactionType.SELL), build(TransactionType.DELIST) ])).toEqual(true);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('An array with a closing transaction after a DELIST transaction should be valid', () => {
|
|
96
|
+
expect(TransactionValidator.validateDelisting([ build(TransactionType.BUY), build(TransactionType.SELL), build(TransactionType.DELIST), build(TransactionType.SELL) ])).toEqual(true);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('An array with an opening transaction after a DELIST transaction should not be valid', () => {
|
|
100
|
+
expect(TransactionValidator.validateDelisting([ build(TransactionType.BUY), build(TransactionType.SELL), build(TransactionType.DELIST), build(TransactionType.BUY) ])).toEqual(false);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
82
104
|
describe('When requesting all the user-initiated transaction types', () => {
|
|
83
105
|
'use strict';
|
|
84
106
|
|