@barchart/portfolio-api-common 1.3.18 → 1.3.22
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/.jshintrc +3 -1
- package/README.md +29 -1
- package/gulpfile.js +29 -49
- package/lib/api/failures/PortfolioFailureType.js +53 -3
- package/lib/data/CorporateActionType.js +24 -0
- package/lib/data/PositionSummaryFrame.js +1 -3
- package/lib/data/TransactionType.js +128 -26
- package/lib/data/TransactionValidator.js +9 -1
- package/lib/formatters/TransactionFormatter.js +36 -0
- package/lib/serialization/TransactionSchema.js +11 -3
- package/package.json +9 -11
- package/test/SpecRunner.js +4526 -5328
- package/test/specs/data/PositionSummaryFrameSpec.js +1 -1
package/.jshintrc
CHANGED
package/README.md
CHANGED
|
@@ -1,2 +1,30 @@
|
|
|
1
1
|
# @barchart/portfolio-api-common
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
A *private* library of shared JavaScript code pertaining to the paper-trading portfolio system.
|
|
4
|
+
|
|
5
|
+
### Overview
|
|
6
|
+
|
|
7
|
+
Simply put, this project contains code that runs on both the servers (i.e. Serverless applications) and clients (e.g. browser, mobile, etc).
|
|
8
|
+
|
|
9
|
+
#### [lib/serialization](https://github.com/barchart/portfolio-api-common/tree/master/lib/serialization)
|
|
10
|
+
|
|
11
|
+
Data is passed between client and server in JSON format. However, the code works with more complex types. For example, [Decimal](https://github.com/barchart/barchart-common-js/blob/master/lang/Decimal.js) instances are used in place of [native JavaScript floats](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number). [Day](https://github.com/barchart/barchart-common-js/blob/master/lang/Day.js) instances are used instead of [native JavaScript Dates](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date).
|
|
12
|
+
|
|
13
|
+
So, before data is exchanged, it must be converted to pure JSON. Conversely, when data is received, as pure JSON, its translated into more complex types before use. This is facilitated by the [Schema](https://github.com/barchart/barchart-common-js/blob/master/serialization/json/Schema.js) definitions which build custom "reviver" functions for [JSON parsing](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse).
|
|
14
|
+
|
|
15
|
+
#### [lib/processing](https://github.com/barchart/portfolio-api-common/tree/master/lib/processing)
|
|
16
|
+
|
|
17
|
+
For reporting and display purposes, positions can be grouped together (by asset class, by portfolio, by user, etc). This code supports aggregation and gain/loss calculation. It is used by server-generated reports and dynamic user interfaces.
|
|
18
|
+
|
|
19
|
+
### Notable Consumers
|
|
20
|
+
|
|
21
|
+
* [aws-lambda-portfolio](https://github.com/barchart/aws-lambda-portfolio) - Serverless applications (i.e. the servers)
|
|
22
|
+
* [portfolio-client-js](https://github.com/barchart/portfolio-client-js) - JavaScript client SDK for communicating with server API's.
|
|
23
|
+
* [tgam-portfolio-ui-js](https://github.com/barchart/tgam-portfolio-ui-js) - A dynamic, single-page HTML UI.
|
|
24
|
+
|
|
25
|
+
### Package Managers
|
|
26
|
+
|
|
27
|
+
This library has been published as a *private* module to NPM as [@barchart/portfolio-api-common](https://www.npmjs.com/package/@barchart/portfolio-api-common).
|
|
28
|
+
|
|
29
|
+
> npm login
|
|
30
|
+
> npm install @barchart/portfolio-api-common -S
|
package/gulpfile.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const gulp = require('gulp');
|
|
2
2
|
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
|
|
3
5
|
const browserify = require('browserify'),
|
|
4
6
|
buffer = require('vinyl-buffer'),
|
|
5
7
|
bump = require('gulp-bump'),
|
|
@@ -9,31 +11,29 @@ const browserify = require('browserify'),
|
|
|
9
11
|
glob = require('glob'),
|
|
10
12
|
jasmine = require('gulp-jasmine'),
|
|
11
13
|
jshint = require('gulp-jshint'),
|
|
12
|
-
|
|
13
|
-
source = require('vinyl-source-stream'),
|
|
14
|
-
util = require('gulp-util');
|
|
15
|
-
|
|
16
|
-
const fs = require('fs');
|
|
14
|
+
source = require('vinyl-source-stream');
|
|
17
15
|
|
|
18
16
|
function getVersionFromPackage() {
|
|
19
17
|
return JSON.parse(fs.readFileSync('./package.json', 'utf8')).version;
|
|
20
18
|
}
|
|
21
19
|
|
|
22
|
-
gulp.task('ensure-clean-working-directory', () => {
|
|
23
|
-
gitStatus(
|
|
20
|
+
gulp.task('ensure-clean-working-directory', (cb) => {
|
|
21
|
+
gitStatus((err, status) => {
|
|
24
22
|
if (err, !status.clean) {
|
|
25
23
|
throw new Error('Unable to proceed, your working directory is not clean.');
|
|
26
24
|
}
|
|
25
|
+
|
|
26
|
+
cb();
|
|
27
27
|
});
|
|
28
28
|
});
|
|
29
29
|
|
|
30
30
|
gulp.task('bump-version', () => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
return gulp.src([ './package.json' ])
|
|
32
|
+
.pipe(bump({ type: 'patch' }))
|
|
33
|
+
.pipe(gulp.dest('./'));
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
-
gulp.task('document',
|
|
36
|
+
gulp.task('document', (cb) => {
|
|
37
37
|
exec('jsdoc . -c jsdoc.json -r -d docs', (error, stdout, stderr) => {
|
|
38
38
|
console.log(stdout);
|
|
39
39
|
console.log(stderr);
|
|
@@ -55,7 +55,7 @@ gulp.task('push-changes', (cb) => {
|
|
|
55
55
|
gulp.task('create-tag', (cb) => {
|
|
56
56
|
const version = getVersionFromPackage();
|
|
57
57
|
|
|
58
|
-
git.tag(version, 'Release ' + version,
|
|
58
|
+
git.tag(version, 'Release ' + version, (error) => {
|
|
59
59
|
if (error) {
|
|
60
60
|
return cb(error);
|
|
61
61
|
}
|
|
@@ -82,41 +82,21 @@ gulp.task('execute-node-tests', () => {
|
|
|
82
82
|
.pipe(jasmine());
|
|
83
83
|
});
|
|
84
84
|
|
|
85
|
-
gulp.task('execute-tests', (
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
gulp.task('release', (cb) => {
|
|
101
|
-
runSequence(
|
|
102
|
-
'ensure-clean-working-directory',
|
|
103
|
-
'execute-tests',
|
|
104
|
-
'document',
|
|
105
|
-
'bump-version',
|
|
106
|
-
'commit-changes',
|
|
107
|
-
'push-changes',
|
|
108
|
-
'create-tag',
|
|
109
|
-
|
|
110
|
-
function (error) {
|
|
111
|
-
if (error) {
|
|
112
|
-
console.log(error.message);
|
|
113
|
-
} else {
|
|
114
|
-
console.log('Release complete');
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
cb(error);
|
|
118
|
-
});
|
|
119
|
-
});
|
|
85
|
+
gulp.task('execute-tests', gulp.series(
|
|
86
|
+
'build-test-bundle',
|
|
87
|
+
'execute-browser-tests',
|
|
88
|
+
'execute-node-tests'
|
|
89
|
+
));
|
|
90
|
+
|
|
91
|
+
gulp.task('release', gulp.series(
|
|
92
|
+
'ensure-clean-working-directory',
|
|
93
|
+
'execute-tests',
|
|
94
|
+
'document',
|
|
95
|
+
'bump-version',
|
|
96
|
+
'commit-changes',
|
|
97
|
+
'push-changes',
|
|
98
|
+
'create-tag'
|
|
99
|
+
));
|
|
120
100
|
|
|
121
101
|
gulp.task('lint', () => {
|
|
122
102
|
return gulp.src([ './**/*.js', './test/specs/**/*.js', '!./node_modules/**', '!./docs/**', '!./test/SpecRunner.js' ])
|
|
@@ -124,6 +104,6 @@ gulp.task('lint', () => {
|
|
|
124
104
|
.pipe(jshint.reporter('default'));
|
|
125
105
|
});
|
|
126
106
|
|
|
127
|
-
gulp.task('test',
|
|
107
|
+
gulp.task('test', gulp.series('execute-tests'));
|
|
128
108
|
|
|
129
|
-
gulp.task('default',
|
|
109
|
+
gulp.task('default', gulp.series('lint'));
|
|
@@ -88,7 +88,7 @@ module.exports = (() => {
|
|
|
88
88
|
static get TRANSACTION_CREATE_FAILED_OUT_OF_SEQUENCE() {
|
|
89
89
|
return transactionCreateFailedOutOfSequence;
|
|
90
90
|
}
|
|
91
|
-
|
|
91
|
+
|
|
92
92
|
/**
|
|
93
93
|
* The transaction date is invalid.
|
|
94
94
|
* For example create opening transaction after delist date.
|
|
@@ -101,6 +101,18 @@ module.exports = (() => {
|
|
|
101
101
|
return transactionCreateFailedInvalidDate;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
/**
|
|
105
|
+
* The target instrument is invalid.
|
|
106
|
+
* Can be used in Merger and Acquisition.
|
|
107
|
+
*
|
|
108
|
+
* @public
|
|
109
|
+
* @static
|
|
110
|
+
* @returns {FailureType}
|
|
111
|
+
*/
|
|
112
|
+
static get TRANSACTION_CREATE_FAILED_TARGET_INSTRUMENT_MISSING() {
|
|
113
|
+
return transactionCreateFailedTargetInstrumentMissing;
|
|
114
|
+
}
|
|
115
|
+
|
|
104
116
|
/**
|
|
105
117
|
* @public
|
|
106
118
|
* @static
|
|
@@ -142,6 +154,40 @@ module.exports = (() => {
|
|
|
142
154
|
return transactionCreateFailedInvalidInitialType;
|
|
143
155
|
}
|
|
144
156
|
|
|
157
|
+
/**
|
|
158
|
+
* A valuation transaction cannot have a negative rate (or amount).
|
|
159
|
+
*
|
|
160
|
+
* @public
|
|
161
|
+
* @static
|
|
162
|
+
* @returns {FailureType}
|
|
163
|
+
*/
|
|
164
|
+
static get TRANSACTION_CREATE_FAILED_VALUATION_NEGATIVE() {
|
|
165
|
+
return transactionCreateFailedValuationNegative;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* A "terminal" transaction must be the last transaction is the history.
|
|
170
|
+
*
|
|
171
|
+
* @public
|
|
172
|
+
* @static
|
|
173
|
+
* @returns {FailureType}
|
|
174
|
+
*/
|
|
175
|
+
static get TRANSACTION_CREATE_FAILED_INVALID_TERMINATION() {
|
|
176
|
+
return transactionCreateFailedInvalidTermination;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* A transaction cannot be created after the termination date of
|
|
181
|
+
* the instrument.
|
|
182
|
+
*
|
|
183
|
+
* @public
|
|
184
|
+
* @static
|
|
185
|
+
* @returns {FailureType}
|
|
186
|
+
*/
|
|
187
|
+
static get TRANSACTION_CREATE_FAILED_AFTER_TERMINATION() {
|
|
188
|
+
return transactionCreateFailedAfterTermination;
|
|
189
|
+
}
|
|
190
|
+
|
|
145
191
|
/**
|
|
146
192
|
* The transaction (of this type) cannot be created by a user, instead,
|
|
147
193
|
* it is created and managed by the system (e.g. dividends).
|
|
@@ -244,7 +290,7 @@ module.exports = (() => {
|
|
|
244
290
|
static get TRANSACTION_DELETE_FAILED_POSITION_LOCKED() {
|
|
245
291
|
return transactionDeleteFailedPositionLocked;
|
|
246
292
|
}
|
|
247
|
-
|
|
293
|
+
|
|
248
294
|
/**
|
|
249
295
|
* The transaction date is invalid.
|
|
250
296
|
* For example edit opening transaction after delist date.
|
|
@@ -295,10 +341,14 @@ module.exports = (() => {
|
|
|
295
341
|
const transactionCreateFailedNoPosition = new FailureType('TRANSACTION_CREATE_FAILED_NO_POSITION', 'Unable to create transaction. The referenced position does not exist. Has it been deleted?', false);
|
|
296
342
|
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).');
|
|
297
343
|
const transactionCreateFailedInvalidDate = new FailureType('TRANSACTION_CREATE_FAILED_INVALID_DATE', 'Unable to process transaction with given date.');
|
|
344
|
+
const transactionCreateFailedTargetInstrumentMissing = new FailureType('TRANSACTION_CREATE_FAILED_TARGET_INSTRUMENT_MISSING', 'Unable to create transaction. The target instrument does not exist.');
|
|
298
345
|
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.');
|
|
299
346
|
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.', false);
|
|
300
347
|
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.', false);
|
|
301
|
-
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.');
|
|
348
|
+
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.', false);
|
|
349
|
+
const transactionCreateFailedValuationNegative = new FailureType('TRANSACTION_CREATE_FAILED_VALUATION_NEGATIVE', 'Unable to process operation, valuations cannot be negative.', false);
|
|
350
|
+
const transactionCreateFailedInvalidTermination = new FailureType('TRANSACTION_CREATE_FAILED_INVALID_TERMINATION', 'Unable to process operation, a {U|transactionType.description} must be the final transaction in the position history.', false);
|
|
351
|
+
const transactionCreateFailedAfterTermination = new FailureType('TRANSACTION_CREATE_FAILED_AFTER_TERMINATION', 'Unable to process operation, one or more transactions would exist after {L|termination}, the final day of trading for this instrument', false);
|
|
302
352
|
|
|
303
353
|
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.');
|
|
304
354
|
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.');
|
|
@@ -82,6 +82,28 @@ module.exports = (() => {
|
|
|
82
82
|
static get DELIST() {
|
|
83
83
|
return delist;
|
|
84
84
|
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* A merging.
|
|
88
|
+
*
|
|
89
|
+
* @public
|
|
90
|
+
* @static
|
|
91
|
+
* @returns {CorporateActionType}
|
|
92
|
+
*/
|
|
93
|
+
static get MERGER() {
|
|
94
|
+
return merger;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* A spinoff.
|
|
99
|
+
*
|
|
100
|
+
* @public
|
|
101
|
+
* @static
|
|
102
|
+
* @returns {CorporateActionType}
|
|
103
|
+
*/
|
|
104
|
+
static get SPINOFF() {
|
|
105
|
+
return spinoff;
|
|
106
|
+
}
|
|
85
107
|
}
|
|
86
108
|
|
|
87
109
|
const split = new CorporateActionType('SPLIT', 'Split', false);
|
|
@@ -90,6 +112,8 @@ module.exports = (() => {
|
|
|
90
112
|
const symbolChange = new CorporateActionType('SYMBOL_CHANGE', 'Symbol Change', false);
|
|
91
113
|
const nameChange = new CorporateActionType('NAME_CHANGE', 'Name Change', false);
|
|
92
114
|
const delist = new CorporateActionType('DELIST', 'Delist', false);
|
|
115
|
+
const merger = new CorporateActionType('MERGER', 'Merger', false);
|
|
116
|
+
const spinoff = new CorporateActionType('SPINOFF', 'Spinoff', false);
|
|
93
117
|
|
|
94
118
|
return CorporateActionType;
|
|
95
119
|
})();
|
|
@@ -218,7 +218,6 @@ module.exports = (() => {
|
|
|
218
218
|
const last = array.last(transactions);
|
|
219
219
|
|
|
220
220
|
const firstDate = first.date;
|
|
221
|
-
const lastDate = last.date;
|
|
222
221
|
|
|
223
222
|
let lastYear;
|
|
224
223
|
|
|
@@ -277,13 +276,12 @@ module.exports = (() => {
|
|
|
277
276
|
const ranges = [ ];
|
|
278
277
|
|
|
279
278
|
if (transactions.length !== 0) {
|
|
280
|
-
const first = array.first(transactions);
|
|
281
279
|
const last = array.last(transactions);
|
|
282
280
|
|
|
283
281
|
const currentYear = Day.getToday().year;
|
|
284
282
|
|
|
285
283
|
if (!last.snapshot.open.getIsZero() || last.date.year === currentYear) {
|
|
286
|
-
let end = new Day(
|
|
284
|
+
let end = new Day(currentYear, 12, 31);
|
|
287
285
|
let start = end.subtractYears(1);
|
|
288
286
|
|
|
289
287
|
ranges.push(getRange(start, end));
|
|
@@ -21,11 +21,14 @@ module.exports = (() => {
|
|
|
21
21
|
* @param {Boolean} fee
|
|
22
22
|
* @param {Boolean} corporateAction
|
|
23
23
|
* @param {Boolean} initial
|
|
24
|
+
* @param {Boolean} terminal
|
|
24
25
|
* @param {Boolean} significant
|
|
25
|
-
* @param {Boolean}
|
|
26
|
+
* @param {Boolean} chaining
|
|
27
|
+
* @param {Boolean} chained
|
|
28
|
+
* @param {Boolean} transfer
|
|
26
29
|
*/
|
|
27
30
|
class TransactionType extends Enum {
|
|
28
|
-
constructor(code, description, display, sequence, purchase, sale, income, opening, closing, fee, corporateAction, initial, significant) {
|
|
31
|
+
constructor(code, description, display, sequence, purchase, sale, income, opening, closing, fee, corporateAction, initial, terminal, significant, chaining, chained, transfer) {
|
|
29
32
|
super(code, description);
|
|
30
33
|
|
|
31
34
|
assert.argumentIsRequired(display, 'display', String);
|
|
@@ -38,7 +41,11 @@ module.exports = (() => {
|
|
|
38
41
|
assert.argumentIsRequired(fee, 'fee', Boolean);
|
|
39
42
|
assert.argumentIsRequired(corporateAction, 'corporateAction', Boolean);
|
|
40
43
|
assert.argumentIsRequired(initial, 'initial', Boolean);
|
|
44
|
+
assert.argumentIsRequired(terminal, 'terminal', Boolean);
|
|
41
45
|
assert.argumentIsRequired(significant, 'significant', Boolean);
|
|
46
|
+
assert.argumentIsRequired(chaining, 'chaining', Boolean);
|
|
47
|
+
assert.argumentIsRequired(chained, 'chained', Boolean);
|
|
48
|
+
assert.argumentIsRequired(transfer, 'transfer', Boolean);
|
|
42
49
|
|
|
43
50
|
this._display = display;
|
|
44
51
|
this._sequence = sequence;
|
|
@@ -50,7 +57,11 @@ module.exports = (() => {
|
|
|
50
57
|
this._fee = fee;
|
|
51
58
|
this._corporateAction = corporateAction;
|
|
52
59
|
this._initial = initial;
|
|
60
|
+
this._terminal = terminal;
|
|
53
61
|
this._significant = significant;
|
|
62
|
+
this._chaining = chaining;
|
|
63
|
+
this._chained = chained;
|
|
64
|
+
this._transfer = transfer;
|
|
54
65
|
}
|
|
55
66
|
|
|
56
67
|
/**
|
|
@@ -167,6 +178,16 @@ module.exports = (() => {
|
|
|
167
178
|
return this._initial;
|
|
168
179
|
}
|
|
169
180
|
|
|
181
|
+
/**
|
|
182
|
+
* Indicates if the transaction must be the last
|
|
183
|
+
*
|
|
184
|
+
* @public
|
|
185
|
+
* @returns {Boolean}
|
|
186
|
+
*/
|
|
187
|
+
get terminal() {
|
|
188
|
+
return this._terminal;
|
|
189
|
+
}
|
|
190
|
+
|
|
170
191
|
/**
|
|
171
192
|
* Significant transactions cannot be discarded during transaction re-write.
|
|
172
193
|
*
|
|
@@ -177,6 +198,37 @@ module.exports = (() => {
|
|
|
177
198
|
return this._significant;
|
|
178
199
|
}
|
|
179
200
|
|
|
201
|
+
/**
|
|
202
|
+
* Chain transactions cause another position to be created.
|
|
203
|
+
*
|
|
204
|
+
* @public
|
|
205
|
+
* @returns {Boolean}
|
|
206
|
+
*/
|
|
207
|
+
get chaining() {
|
|
208
|
+
return this._chaining;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Chained transactions are created from another position.
|
|
213
|
+
*
|
|
214
|
+
* @public
|
|
215
|
+
* @returns {Boolean}
|
|
216
|
+
*/
|
|
217
|
+
get chained() {
|
|
218
|
+
return this._chained;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Indicates if the transaction should cause gains and losses to be
|
|
223
|
+
* transferred from the original (chaining) position.
|
|
224
|
+
*
|
|
225
|
+
* @public
|
|
226
|
+
* @returns {Boolean}
|
|
227
|
+
*/
|
|
228
|
+
get transfer() {
|
|
229
|
+
return this._transfer;
|
|
230
|
+
}
|
|
231
|
+
|
|
180
232
|
/**
|
|
181
233
|
* A purchase.
|
|
182
234
|
*
|
|
@@ -352,7 +404,7 @@ module.exports = (() => {
|
|
|
352
404
|
static get DEBIT() {
|
|
353
405
|
return debit;
|
|
354
406
|
}
|
|
355
|
-
|
|
407
|
+
|
|
356
408
|
/**
|
|
357
409
|
* A system-generated transaction, indicating the security has stopped active trading.
|
|
358
410
|
*
|
|
@@ -397,34 +449,84 @@ module.exports = (() => {
|
|
|
397
449
|
return income;
|
|
398
450
|
}
|
|
399
451
|
|
|
452
|
+
/**
|
|
453
|
+
* A merger opening.
|
|
454
|
+
*
|
|
455
|
+
* @public
|
|
456
|
+
* @static
|
|
457
|
+
* @returns {TransactionType}
|
|
458
|
+
*/
|
|
459
|
+
static get MERGER_OPEN() {
|
|
460
|
+
return mergerOpen;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* A merger closing.
|
|
465
|
+
*
|
|
466
|
+
* @public
|
|
467
|
+
* @static
|
|
468
|
+
* @returns {TransactionType}
|
|
469
|
+
*/
|
|
470
|
+
static get MERGER_CLOSE() {
|
|
471
|
+
return mergerClose;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* A spin-off.
|
|
476
|
+
*
|
|
477
|
+
* @public
|
|
478
|
+
* @static
|
|
479
|
+
* @returns {TransactionType}
|
|
480
|
+
*/
|
|
481
|
+
static get SPINOFF() {
|
|
482
|
+
return spinoff;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* A spin-off opening.
|
|
487
|
+
*
|
|
488
|
+
* @public
|
|
489
|
+
* @static
|
|
490
|
+
* @returns {TransactionType}
|
|
491
|
+
*/
|
|
492
|
+
static get SPINOFF_OPEN() {
|
|
493
|
+
return spinoffOpen;
|
|
494
|
+
}
|
|
495
|
+
|
|
400
496
|
toString() {
|
|
401
497
|
return '[TransactionType]';
|
|
402
498
|
}
|
|
403
499
|
}
|
|
404
500
|
|
|
405
|
-
const buy = new TransactionType('B', 'Buy', 'Buy', 0, true, false, false, true, false, false, false, true, true);
|
|
406
|
-
const sell = new TransactionType('S', 'Sell', 'Sell', 0, false, true, false, false, true, false, false, false, true);
|
|
407
|
-
const buyShort = new TransactionType('BS', 'Buy To Cover', 'Buy To Cover', 0, true, false, false, false, true, false, false, false, true);
|
|
408
|
-
const sellShort = new TransactionType('SS', 'Sell Short', 'Sell Short', 0, false, true, false, true, false, false, false, true, true);
|
|
409
|
-
const dividend = new TransactionType('DV', 'Dividend', 'Dividend', 1, false, false, true, false, false, false, true, false, false);
|
|
410
|
-
const dividendReinvest = new TransactionType('DX', 'Dividend (Reinvested)', 'Dividend Reinvest', 1, false, false, false, true, false, false, true, false, false);
|
|
411
|
-
const dividendStock = new TransactionType('DS', 'Dividend (Stock)', 'Dividend Stock', 1, false, false, false, true, false, false, true, false, false);
|
|
412
|
-
const split = new TransactionType('SP', 'Split', 'Split', 1, false, false, false, true, false, false, true, false, false);
|
|
413
|
-
const fee = new TransactionType('F', 'Fee', 'Fee', 0, false, false, false, false, false, true, false, false, false);
|
|
414
|
-
const feeUnits = new TransactionType('FU', 'Fee Units', 'Fee', 0, false, false, false, false, true, false, false, false, false);
|
|
415
|
-
const delist = new TransactionType('DL', 'Delist', 'Delist', 1, false, false, false, false, false, false, true, false, false);
|
|
416
|
-
|
|
417
|
-
const
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
const
|
|
422
|
-
|
|
423
|
-
const
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
const
|
|
501
|
+
const buy = new TransactionType('B', 'Buy', 'Buy', 0, true, false, false, true, false, false, false, true, false, true, false, false, false);
|
|
502
|
+
const sell = new TransactionType('S', 'Sell', 'Sell', 0, false, true, false, false, true, false, false, false, false, true, false, false, false);
|
|
503
|
+
const buyShort = new TransactionType('BS', 'Buy To Cover', 'Buy To Cover', 0, true, false, false, false, true, false, false, false, false, true, false, false, false);
|
|
504
|
+
const sellShort = new TransactionType('SS', 'Sell Short', 'Sell Short', 0, false, true, false, true, false, false, false, true, false, true, false, false, false);
|
|
505
|
+
const dividend = new TransactionType('DV', 'Dividend', 'Dividend', 1, false, false, true, false, false, false, true, false, false, false, false, false, false);
|
|
506
|
+
const dividendReinvest = new TransactionType('DX', 'Dividend (Reinvested)', 'Dividend Reinvest', 1, false, false, false, true, false, false, true, false, false, false, false, false, false);
|
|
507
|
+
const dividendStock = new TransactionType('DS', 'Dividend (Stock)', 'Dividend Stock', 1, false, false, false, true, false, false, true, false, false, false, false, false, false);
|
|
508
|
+
const split = new TransactionType('SP', 'Split', 'Split', 1, false, false, false, true, false, false, true, false, false, false, false, false, false);
|
|
509
|
+
const fee = new TransactionType('F', 'Fee', 'Fee', 0, false, false, false, false, false, true, false, false, false, false, false, false, false);
|
|
510
|
+
const feeUnits = new TransactionType('FU', 'Fee Units', 'Fee', 0, false, false, false, false, true, false, false, false, false, false, false, false, false);
|
|
511
|
+
const delist = new TransactionType('DL', 'Delist', 'Delist', 1, false, false, false, false, false, false, true, false, true, false, false, false, false);
|
|
512
|
+
|
|
513
|
+
const mergerOpen = new TransactionType('MO', 'Merger Open', 'Merger Open', 1, false, false, false, true, false, false, true, true, false, true, false, true, true);
|
|
514
|
+
const mergerClose = new TransactionType('MC', 'Merger Close', 'Merger Close', 1, false, false, false, false, true, false, true, false, true, false, true, false, false);
|
|
515
|
+
|
|
516
|
+
const spinoff = new TransactionType('SPF', 'Spinoff', 'Spinoff', 1, false, false, false, false, false, false, true, false, false, false, true, false, false);
|
|
517
|
+
const spinoffOpen = new TransactionType('SPFO', 'Spinoff Open', 'Spinoff Open', 1, false, false, false, true, false, false, true, true, false, true, false, false, false);
|
|
518
|
+
|
|
519
|
+
const distributionCash = new TransactionType('DC', 'Distribution (Cash)', 'Cash Distribution', 1, false, false, true, false, false, false, true, false, false, false, false, false, false);
|
|
520
|
+
const distributionReinvest = new TransactionType('DY', 'Distribution (Reinvested)', 'Distribution Reinvest', 1, false, false, false, true, false, false, true, false, false, false, false, false, false);
|
|
521
|
+
const distributionFund = new TransactionType('DF', 'Distribution (Units)', 'Unit Distribution', 1, false, false, false, true, false, false, true, false, false, false, false, false, false);
|
|
522
|
+
|
|
523
|
+
const deposit = new TransactionType('D', 'Deposit', 'Deposit', 0, false, false, false, false, false, false, false, true, false, true, false, false, false);
|
|
524
|
+
const withdrawal = new TransactionType('W', 'Withdrawal', 'Withdrawal', 0, false, false, false, false, false, false, false, true, false, true, false, false, false);
|
|
525
|
+
const debit = new TransactionType('DR', 'Debit', 'Debit', 0, false, false, false, false, false, false, false, true, false, true, false, false, false);
|
|
526
|
+
const credit = new TransactionType('CR', 'Credit', 'Credit', 0, false, false, false, false, false, false, false, true, false, true, false, false, false);
|
|
527
|
+
|
|
528
|
+
const valuation = new TransactionType('V', 'Valuation', 'Valuation', 0, false, false, false, false, false, false, false, false, false, false, false, false, false);
|
|
529
|
+
const income = new TransactionType('I', 'Income', 'Income', 0, false, false, true, false, false, false, false, false, false, false, false, false, false);
|
|
428
530
|
|
|
429
531
|
return TransactionType;
|
|
430
532
|
})();
|
|
@@ -186,7 +186,7 @@ module.exports = (() => {
|
|
|
186
186
|
static validateInitialTransactionType(transactionType) {
|
|
187
187
|
return transactionType.initial;
|
|
188
188
|
}
|
|
189
|
-
|
|
189
|
+
|
|
190
190
|
/**
|
|
191
191
|
* Determines if a position for a given instrument type can exist in
|
|
192
192
|
* the given direction.
|
|
@@ -246,6 +246,10 @@ module.exports = (() => {
|
|
|
246
246
|
associateTypes(InstrumentType.EQUITY, TransactionType.DIVIDEND_STOCK, false);
|
|
247
247
|
associateTypes(InstrumentType.EQUITY, TransactionType.SPLIT, false);
|
|
248
248
|
associateTypes(InstrumentType.EQUITY, TransactionType.DELIST, false);
|
|
249
|
+
associateTypes(InstrumentType.EQUITY, TransactionType.MERGER_OPEN, false);
|
|
250
|
+
associateTypes(InstrumentType.EQUITY, TransactionType.MERGER_CLOSE, false);
|
|
251
|
+
associateTypes(InstrumentType.EQUITY, TransactionType.SPINOFF, false);
|
|
252
|
+
associateTypes(InstrumentType.EQUITY, TransactionType.SPINOFF_OPEN, false);
|
|
249
253
|
|
|
250
254
|
associateTypes(InstrumentType.FUND, TransactionType.BUY, true, [ PositionDirection.LONG, PositionDirection.EVEN ]);
|
|
251
255
|
associateTypes(InstrumentType.FUND, TransactionType.SELL, true, [ PositionDirection.LONG ]);
|
|
@@ -255,6 +259,10 @@ module.exports = (() => {
|
|
|
255
259
|
associateTypes(InstrumentType.FUND, TransactionType.DISTRIBUTION_REINVEST, false);
|
|
256
260
|
associateTypes(InstrumentType.FUND, TransactionType.DISTRIBUTION_FUND, false);
|
|
257
261
|
associateTypes(InstrumentType.FUND, TransactionType.DELIST, false);
|
|
262
|
+
associateTypes(InstrumentType.FUND, TransactionType.MERGER_OPEN, false);
|
|
263
|
+
associateTypes(InstrumentType.FUND, TransactionType.MERGER_CLOSE, false);
|
|
264
|
+
associateTypes(InstrumentType.FUND, TransactionType.SPINOFF, false);
|
|
265
|
+
associateTypes(InstrumentType.FUND, TransactionType.SPINOFF_OPEN, false);
|
|
258
266
|
|
|
259
267
|
associateTypes(InstrumentType.OTHER, TransactionType.BUY, true, [ PositionDirection.LONG, PositionDirection.EVEN ]);
|
|
260
268
|
associateTypes(InstrumentType.OTHER, TransactionType.SELL, true, [ PositionDirection.LONG ]);
|
|
@@ -326,6 +326,38 @@ module.exports = (() => {
|
|
|
326
326
|
f.description = t.description;
|
|
327
327
|
};
|
|
328
328
|
|
|
329
|
+
const mergerFormatter = (t, f) => {
|
|
330
|
+
f.boughtSold = t.quantity;
|
|
331
|
+
|
|
332
|
+
let rate;
|
|
333
|
+
|
|
334
|
+
if (!t.merger.denominator.getIsZero()) {
|
|
335
|
+
rate = t.merger.numerator.divide(t.merger.denominator);
|
|
336
|
+
} else {
|
|
337
|
+
rate = '';
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
f.rate = rate;
|
|
341
|
+
|
|
342
|
+
f.shares = t.snapshot.open.subtract(t.quantity);
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
const spinoffFormatter = (t, f) => {
|
|
346
|
+
f.boughtSold = t.quantity;
|
|
347
|
+
|
|
348
|
+
let rate;
|
|
349
|
+
|
|
350
|
+
if (!t.spinoff.denominator.getIsZero()) {
|
|
351
|
+
rate = t.spinoff.numerator.divide(t.spinoff.denominator);
|
|
352
|
+
} else {
|
|
353
|
+
rate = '';
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
f.rate = rate;
|
|
357
|
+
|
|
358
|
+
f.shares = t.snapshot.open.subtract(t.quantity);
|
|
359
|
+
};
|
|
360
|
+
|
|
329
361
|
const formatters = new Map();
|
|
330
362
|
|
|
331
363
|
formatters.set(TransactionType.BUY, [ basicFormatter, buySellFormatter, averageCostFormatter ]);
|
|
@@ -348,6 +380,10 @@ module.exports = (() => {
|
|
|
348
380
|
formatters.set(TransactionType.WITHDRAWAL, [ basicFormatter, cashFormatter ]);
|
|
349
381
|
formatters.set(TransactionType.DEBIT, [ basicFormatter, cashFormatter, debitFormatter ]);
|
|
350
382
|
formatters.set(TransactionType.CREDIT, [ basicFormatter, cashFormatter, creditFormatter ]);
|
|
383
|
+
formatters.set(TransactionType.MERGER_OPEN, [ basicFormatter ]);
|
|
384
|
+
formatters.set(TransactionType.MERGER_CLOSE, [ basicFormatter, mergerFormatter ]);
|
|
385
|
+
formatters.set(TransactionType.SPINOFF, [ basicFormatter, spinoffFormatter ]);
|
|
386
|
+
formatters.set(TransactionType.SPINOFF_OPEN, [ basicFormatter ]);
|
|
351
387
|
|
|
352
388
|
function getInstrumentTypePriority(type) {
|
|
353
389
|
if (type === InstrumentType.CASH) {
|
|
@@ -113,7 +113,7 @@ module.exports = (() => {
|
|
|
113
113
|
static get VALUATION() {
|
|
114
114
|
return valuation;
|
|
115
115
|
}
|
|
116
|
-
|
|
116
|
+
|
|
117
117
|
static get DELIST() {
|
|
118
118
|
return delist;
|
|
119
119
|
}
|
|
@@ -162,6 +162,10 @@ module.exports = (() => {
|
|
|
162
162
|
.withField('split.denominator', DataType.DECIMAL, true)
|
|
163
163
|
.withField('split.effective', DataType.DAY, true)
|
|
164
164
|
.withField('split.reference', DataType.STRING, true)
|
|
165
|
+
.withField('merger.numerator', DataType.DECIMAL, true)
|
|
166
|
+
.withField('merger.denominator', DataType.DECIMAL, true)
|
|
167
|
+
.withField('spinoff.numerator', DataType.DECIMAL, true)
|
|
168
|
+
.withField('spinoff.denominator', DataType.DECIMAL, true)
|
|
165
169
|
.withField('charge.amount', DataType.DECIMAL, true)
|
|
166
170
|
.withField('income.amount', DataType.DECIMAL, true)
|
|
167
171
|
.withField('valuation.rate', DataType.DECIMAL, true)
|
|
@@ -201,6 +205,10 @@ module.exports = (() => {
|
|
|
201
205
|
.withField('split.denominator', DataType.DECIMAL, true)
|
|
202
206
|
.withField('split.effective', DataType.DAY, true)
|
|
203
207
|
.withField('split.reference', DataType.STRING, true)
|
|
208
|
+
.withField('merger.numerator', DataType.DECIMAL, true)
|
|
209
|
+
.withField('merger.denominator', DataType.DECIMAL, true)
|
|
210
|
+
.withField('spinoff.numerator', DataType.DECIMAL, true)
|
|
211
|
+
.withField('spinoff.denominator', DataType.DECIMAL, true)
|
|
204
212
|
.withField('charge.amount', DataType.DECIMAL, true)
|
|
205
213
|
.withField('income.amount', DataType.DECIMAL, true)
|
|
206
214
|
.withField('valuation.rate', DataType.DECIMAL, true)
|
|
@@ -326,7 +334,7 @@ module.exports = (() => {
|
|
|
326
334
|
.withField('force', DataType.BOOLEAN, true)
|
|
327
335
|
.schema
|
|
328
336
|
);
|
|
329
|
-
|
|
337
|
+
|
|
330
338
|
const delist = new TransactionSchema(SchemaBuilder.withName(TransactionType.DELIST.code)
|
|
331
339
|
.withField('portfolio', DataType.STRING)
|
|
332
340
|
.withField('position', DataType.STRING)
|
|
@@ -336,7 +344,7 @@ module.exports = (() => {
|
|
|
336
344
|
.withField('force', DataType.BOOLEAN, true)
|
|
337
345
|
.schema
|
|
338
346
|
);
|
|
339
|
-
|
|
347
|
+
|
|
340
348
|
const income = new TransactionSchema(SchemaBuilder.withName(TransactionType.INCOME.code)
|
|
341
349
|
.withField('portfolio', DataType.STRING)
|
|
342
350
|
.withField('position', DataType.STRING)
|