@barchart/portfolio-api-common 1.17.0 → 1.18.0
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 +25 -0
- package/lib/data/CorporateActionType.js +10 -0
- package/lib/data/InstrumentType.js +47 -14
- package/lib/data/PositionDirection.js +10 -0
- package/lib/data/PositionSummaryFrame.js +10 -0
- package/lib/data/TransactionType.js +10 -0
- package/lib/data/TransactionValidator.js +9 -0
- package/lib/data/ValuationType.js +11 -11
- package/lib/formatters/TransactionFormatter.js +9 -2
- package/lib/processing/PositionGroup.js +26 -0
- package/lib/processing/PositionItem.js +49 -8
- package/lib/serialization/PositionSchema.js +10 -0
- package/package.json +2 -1
|
@@ -199,6 +199,29 @@ module.exports = (() => {
|
|
|
199
199
|
return transactionCreateFailedAfterTermination;
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
+
/**
|
|
203
|
+
* A transaction cannot be created because the price is not a valid
|
|
204
|
+
* multiple of the minimum tick size.
|
|
205
|
+
*
|
|
206
|
+
* @public
|
|
207
|
+
* @static
|
|
208
|
+
* @returns {FailureType}
|
|
209
|
+
*/
|
|
210
|
+
static get TRANSACTION_CREATE_FAILED_PRICE_INVALID_FOR_INSTRUMENT() {
|
|
211
|
+
return transactionCreateFailedInvalidPriceForInstrument;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* A transaction cannot be created after the instrument's expiration date.
|
|
216
|
+
*
|
|
217
|
+
* @public
|
|
218
|
+
* @static
|
|
219
|
+
* @returns {FailureType}
|
|
220
|
+
*/
|
|
221
|
+
static get TRANSACTION_CREATE_FAILED_INSTRUMENT_EXPIRED() {
|
|
222
|
+
return transactionCreateFailedInstrumentExpired;
|
|
223
|
+
}
|
|
224
|
+
|
|
202
225
|
/**
|
|
203
226
|
* The transaction (of this type) cannot be created by a user, instead,
|
|
204
227
|
* it is created and managed by the system (e.g. dividends).
|
|
@@ -418,6 +441,8 @@ module.exports = (() => {
|
|
|
418
441
|
const transactionCreateFailedValuationNegative = new FailureType('TRANSACTION_CREATE_FAILED_VALUATION_NEGATIVE', 'Unable to process operation, valuations cannot be negative.', false);
|
|
419
442
|
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);
|
|
420
443
|
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);
|
|
444
|
+
const transactionCreateFailedInvalidPriceForInstrument = new FailureType('TRANSACTION_CREATE_FAILED_PRICE_INVALID_FOR_INSTRUMENT', 'Unable to process transaction, the trade price for {U|symbol} must be a multiple of {tick} (the minimum tick increment).', false);
|
|
445
|
+
const transactionCreateFailedInstrumentExpired = new FailureType('TRANSACTION_CREATE_FAILED_INSTRUMENT_EXPIRED', 'Unable to process transaction, the transaction date {date} is after the expiration date {expiration}.', false);
|
|
421
446
|
|
|
422
447
|
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.');
|
|
423
448
|
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.');
|
|
@@ -105,6 +105,16 @@ module.exports = (() => {
|
|
|
105
105
|
return spinoff;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
/**
|
|
109
|
+
* @public
|
|
110
|
+
* @static
|
|
111
|
+
* @param {String} code
|
|
112
|
+
* @returns {CorporateActionType|null}
|
|
113
|
+
*/
|
|
114
|
+
static parse(code) {
|
|
115
|
+
return Enum.fromCode(CorporateActionType, code);
|
|
116
|
+
}
|
|
117
|
+
|
|
108
118
|
toString() {
|
|
109
119
|
return `[CorporateActionType (code=${this.code})]`;
|
|
110
120
|
}
|
|
@@ -20,13 +20,14 @@ module.exports = (() => {
|
|
|
20
20
|
* @param {Boolean} canSwitchDirection
|
|
21
21
|
* @param {Boolean} usesSymbols
|
|
22
22
|
* @param {Boolean} hasCorporateActions
|
|
23
|
+
* @param {Boolean} allowFractional
|
|
23
24
|
* @param {Boolean} closeFractional
|
|
24
25
|
* @param {Boolean} roundQuantity
|
|
25
26
|
* @param {Boolean} strictOrdering
|
|
26
27
|
* @param {Function} generator
|
|
27
28
|
*/
|
|
28
29
|
class InstrumentType extends Enum {
|
|
29
|
-
constructor(code, description, alternateDescription, canExistEmpty, canReinvest, canShort, canSwitchDirection, usesSymbols, hasCorporateActions, closeFractional, roundQuantity, strictOrdering, generator) {
|
|
30
|
+
constructor(code, description, alternateDescription, canExistEmpty, canReinvest, canShort, canSwitchDirection, usesSymbols, hasCorporateActions, allowFractional, closeFractional, roundQuantity, strictOrdering, generator) {
|
|
30
31
|
super(code, description);
|
|
31
32
|
|
|
32
33
|
assert.argumentIsRequired(alternateDescription, 'alternateDescription', String);
|
|
@@ -36,9 +37,10 @@ module.exports = (() => {
|
|
|
36
37
|
assert.argumentIsRequired(canSwitchDirection, 'canSwitchDirection', Boolean);
|
|
37
38
|
assert.argumentIsRequired(usesSymbols, 'usesSymbols', Boolean);
|
|
38
39
|
assert.argumentIsRequired(hasCorporateActions, 'hasCorporateActions', Boolean);
|
|
40
|
+
assert.argumentIsRequired(allowFractional, 'allowFractional', Boolean);
|
|
39
41
|
assert.argumentIsRequired(closeFractional, 'closeFractional', Boolean);
|
|
40
42
|
assert.argumentIsRequired(roundQuantity, 'roundQuantity', Boolean);
|
|
41
|
-
assert.argumentIsRequired(
|
|
43
|
+
assert.argumentIsRequired(strictOrdering, 'strictOrdering', Boolean);
|
|
42
44
|
assert.argumentIsRequired(generator, 'generator', Function);
|
|
43
45
|
|
|
44
46
|
this._alternateDescription = alternateDescription;
|
|
@@ -49,6 +51,7 @@ module.exports = (() => {
|
|
|
49
51
|
this._canSwitchDirection = canSwitchDirection;
|
|
50
52
|
this._usesSymbols = usesSymbols;
|
|
51
53
|
this._hasCorporateActions = hasCorporateActions;
|
|
54
|
+
this._allowFractional = allowFractional;
|
|
52
55
|
this._closeFractional = closeFractional;
|
|
53
56
|
this._roundQuantity = roundQuantity;
|
|
54
57
|
this._strictOrdering = strictOrdering;
|
|
@@ -127,6 +130,17 @@ module.exports = (() => {
|
|
|
127
130
|
return this._hasCorporateActions;
|
|
128
131
|
}
|
|
129
132
|
|
|
133
|
+
/**
|
|
134
|
+
* Indicates if a position can have a fractional value; otherwise, only
|
|
135
|
+
* integer values are allowed.
|
|
136
|
+
*
|
|
137
|
+
* @public
|
|
138
|
+
* @returns {Boolean}
|
|
139
|
+
*/
|
|
140
|
+
get allowFractional() {
|
|
141
|
+
return this._allowFractional;
|
|
142
|
+
}
|
|
143
|
+
|
|
130
144
|
/**
|
|
131
145
|
* Indicates if fractional shares should be closed when the position
|
|
132
146
|
* size is less than one (or some of the fractional shares are closed).
|
|
@@ -209,6 +223,17 @@ module.exports = (() => {
|
|
|
209
223
|
return fund;
|
|
210
224
|
}
|
|
211
225
|
|
|
226
|
+
/**
|
|
227
|
+
* A futures contract.
|
|
228
|
+
*
|
|
229
|
+
* @public
|
|
230
|
+
* @static
|
|
231
|
+
* @returns {InstrumentType}
|
|
232
|
+
*/
|
|
233
|
+
static get FUTURE() {
|
|
234
|
+
return future;
|
|
235
|
+
}
|
|
236
|
+
|
|
212
237
|
/**
|
|
213
238
|
* An undefined asset (e.g. a house, or a collectible, or a salvaged alien spaceship).
|
|
214
239
|
*
|
|
@@ -220,6 +245,16 @@ module.exports = (() => {
|
|
|
220
245
|
return other;
|
|
221
246
|
}
|
|
222
247
|
|
|
248
|
+
/**
|
|
249
|
+
* @public
|
|
250
|
+
* @static
|
|
251
|
+
* @param {String} code
|
|
252
|
+
* @returns {InstrumentType|null}
|
|
253
|
+
*/
|
|
254
|
+
static parse(code) {
|
|
255
|
+
return Enum.fromCode(InstrumentType, code);
|
|
256
|
+
}
|
|
257
|
+
|
|
223
258
|
/**
|
|
224
259
|
* Generates an identifier for the instrument.
|
|
225
260
|
*
|
|
@@ -229,7 +264,9 @@ module.exports = (() => {
|
|
|
229
264
|
* @returns {String}
|
|
230
265
|
*/
|
|
231
266
|
static generateIdentifier(instrument) {
|
|
232
|
-
|
|
267
|
+
const type = Enum.fromCode(InstrumentType, instrument.type.code);
|
|
268
|
+
|
|
269
|
+
return type.generateIdentifier(instrument);
|
|
233
270
|
}
|
|
234
271
|
|
|
235
272
|
/**
|
|
@@ -246,6 +283,8 @@ module.exports = (() => {
|
|
|
246
283
|
return InstrumentType.EQUITY;
|
|
247
284
|
} else if (code === 5 || code == 15) {
|
|
248
285
|
return InstrumentType.FUND;
|
|
286
|
+
} else if (code === 2) {
|
|
287
|
+
return InstrumentType.FUTURE;
|
|
249
288
|
} else {
|
|
250
289
|
throw new Error(`Unable to determine InstrumentType for [ ${code} ]`);
|
|
251
290
|
}
|
|
@@ -256,17 +295,11 @@ module.exports = (() => {
|
|
|
256
295
|
}
|
|
257
296
|
}
|
|
258
297
|
|
|
259
|
-
const cash = new InstrumentType('CASH', 'cash', 'Cash', true, false, false, true, false, false, false, false, false,
|
|
260
|
-
const equity = new InstrumentType('EQUITY', 'equity', 'Equities', false, true, true, false, true, true, true, true, true,
|
|
261
|
-
const fund = new InstrumentType('FUND', 'mutual fund', 'Funds', false, true, false, false, true, true, false, true, true,
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
const map = { };
|
|
265
|
-
|
|
266
|
-
map[cash.code] = cash;
|
|
267
|
-
map[equity.code] = equity;
|
|
268
|
-
map[fund.code] = fund;
|
|
269
|
-
map[other.code] = other;
|
|
298
|
+
const cash = new InstrumentType('CASH', 'cash', 'Cash', true, false, false, true, false, false, true, false, false, false, instrument => `BARCHART-${instrument.type.code}-${instrument.currency.code}`);
|
|
299
|
+
const equity = new InstrumentType('EQUITY', 'equity', 'Equities', false, true, true, false, true, true, true, true, true, true, instrument => `BARCHART-${instrument.type.code}-${instrument.symbol.barchart}`);
|
|
300
|
+
const fund = new InstrumentType('FUND', 'mutual fund', 'Funds', false, true, false, false, true, true, true,false, true, true, instrument => `BARCHART-${instrument.type.code}-${instrument.symbol.barchart}`);
|
|
301
|
+
const future = new InstrumentType('FUTURE', 'futures contract', 'Futures', false, false, true, false, true, false, false, false, false, true, instrument => `BARCHART-${instrument.type.code}-${instrument.symbol.barchart}`);
|
|
302
|
+
const other = new InstrumentType('OTHER', 'other', 'Other', false, false, false, false, false, false, true,false, true, true, instrument => `BARCHART-${instrument.type.code}-${uuid.v4()}`);
|
|
270
303
|
|
|
271
304
|
return InstrumentType;
|
|
272
305
|
})();
|
|
@@ -98,6 +98,16 @@ module.exports = (() => {
|
|
|
98
98
|
return even;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
/**
|
|
102
|
+
* @public
|
|
103
|
+
* @static
|
|
104
|
+
* @param {String} code
|
|
105
|
+
* @returns {PositionDirection|null}
|
|
106
|
+
*/
|
|
107
|
+
static parse(code) {
|
|
108
|
+
return Enum.fromCode(PositionDirection, code);
|
|
109
|
+
}
|
|
110
|
+
|
|
101
111
|
/**
|
|
102
112
|
* Given an open quantity, returns a {@link PositionDirection} that
|
|
103
113
|
* describes the quantity.
|
|
@@ -174,6 +174,16 @@ module.exports = (() => {
|
|
|
174
174
|
return ytd;
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
+
/**
|
|
178
|
+
* @public
|
|
179
|
+
* @static
|
|
180
|
+
* @param {String} code
|
|
181
|
+
* @returns {PositionSummaryFrame|null}
|
|
182
|
+
*/
|
|
183
|
+
static parse(code) {
|
|
184
|
+
return Enum.fromCode(PositionSummaryFrame, code);
|
|
185
|
+
}
|
|
186
|
+
|
|
177
187
|
toString() {
|
|
178
188
|
return `[PositionSummaryFrame (code=${this.code})]`;
|
|
179
189
|
}
|
|
@@ -493,6 +493,16 @@ module.exports = (() => {
|
|
|
493
493
|
return spinoffOpen;
|
|
494
494
|
}
|
|
495
495
|
|
|
496
|
+
/**
|
|
497
|
+
* @public
|
|
498
|
+
* @static
|
|
499
|
+
* @param {String} code
|
|
500
|
+
* @returns {TransactionType|null}
|
|
501
|
+
*/
|
|
502
|
+
static parse(code) {
|
|
503
|
+
return Enum.fromCode(TransactionType, code);
|
|
504
|
+
}
|
|
505
|
+
|
|
496
506
|
toString() {
|
|
497
507
|
return `[TransactionType (code=${this.code})]`;
|
|
498
508
|
}
|
|
@@ -263,6 +263,11 @@ module.exports = (() => {
|
|
|
263
263
|
associateTypes(InstrumentType.FUND, TransactionType.SPINOFF, false);
|
|
264
264
|
associateTypes(InstrumentType.FUND, TransactionType.SPINOFF_OPEN, false);
|
|
265
265
|
|
|
266
|
+
associateTypes(InstrumentType.FUTURE, TransactionType.BUY, true, [ PositionDirection.LONG, PositionDirection.EVEN ]);
|
|
267
|
+
associateTypes(InstrumentType.FUTURE, TransactionType.SELL, true, [ PositionDirection.LONG ]);
|
|
268
|
+
associateTypes(InstrumentType.FUTURE, TransactionType.SELL_SHORT, true, [ PositionDirection.SHORT, PositionDirection.EVEN ]);
|
|
269
|
+
associateTypes(InstrumentType.FUTURE, TransactionType.BUY_SHORT, true, [ PositionDirection.SHORT ]);
|
|
270
|
+
|
|
266
271
|
associateTypes(InstrumentType.OTHER, TransactionType.BUY, true, [ PositionDirection.LONG, PositionDirection.EVEN ]);
|
|
267
272
|
associateTypes(InstrumentType.OTHER, TransactionType.SELL, true, [ PositionDirection.LONG ]);
|
|
268
273
|
associateTypes(InstrumentType.OTHER, TransactionType.INCOME, true, [ PositionDirection.LONG ]);
|
|
@@ -293,6 +298,10 @@ module.exports = (() => {
|
|
|
293
298
|
associateDirections(InstrumentType.FUND, PositionDirection.EVEN);
|
|
294
299
|
associateDirections(InstrumentType.FUND, PositionDirection.LONG);
|
|
295
300
|
|
|
301
|
+
associateDirections(InstrumentType.FUTURE, PositionDirection.EVEN);
|
|
302
|
+
associateDirections(InstrumentType.FUTURE, PositionDirection.LONG);
|
|
303
|
+
associateDirections(InstrumentType.FUTURE, PositionDirection.SHORT);
|
|
304
|
+
|
|
296
305
|
associateDirections(InstrumentType.OTHER, PositionDirection.EVEN);
|
|
297
306
|
associateDirections(InstrumentType.OTHER, PositionDirection.LONG);
|
|
298
307
|
|
|
@@ -16,17 +16,6 @@ module.exports = (() => {
|
|
|
16
16
|
super(code, description);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
/**
|
|
20
|
-
* Given a code, returns the enumeration item.
|
|
21
|
-
*
|
|
22
|
-
* @public
|
|
23
|
-
* @param {String} code
|
|
24
|
-
* @returns {ValuationType|null}
|
|
25
|
-
*/
|
|
26
|
-
static parse(code) {
|
|
27
|
-
return Enum.fromCode(ValuationType, code);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
19
|
/**
|
|
31
20
|
* A valuation method that uses average costing.
|
|
32
21
|
*
|
|
@@ -47,6 +36,17 @@ module.exports = (() => {
|
|
|
47
36
|
return fifo;
|
|
48
37
|
}
|
|
49
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Given a code, returns the enumeration item.
|
|
41
|
+
*
|
|
42
|
+
* @public
|
|
43
|
+
* @param {String} code
|
|
44
|
+
* @returns {ValuationType|null}
|
|
45
|
+
*/
|
|
46
|
+
static parse(code) {
|
|
47
|
+
return Enum.fromCode(ValuationType, code);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
50
|
toString() {
|
|
51
51
|
return `[ValuationType (code=${this.code})]`;
|
|
52
52
|
}
|
|
@@ -50,7 +50,7 @@ module.exports = (() => {
|
|
|
50
50
|
|
|
51
51
|
if (instruments.hasOwnProperty(position)) {
|
|
52
52
|
let instrument = instruments[position];
|
|
53
|
-
let formatted = { instrument
|
|
53
|
+
let formatted = { instrument, raw: {} };
|
|
54
54
|
|
|
55
55
|
const formatterFunctions = formatters.get(transaction.type);
|
|
56
56
|
|
|
@@ -139,7 +139,14 @@ module.exports = (() => {
|
|
|
139
139
|
let average;
|
|
140
140
|
|
|
141
141
|
if (basis && open && !open.getIsZero()) {
|
|
142
|
-
|
|
142
|
+
if (f.instrument.type === InstrumentType.FUTURE) {
|
|
143
|
+
const minimumTick = f.instrument.future.tick;
|
|
144
|
+
const minimumTickValue = f.instrument.future.value;
|
|
145
|
+
|
|
146
|
+
average = basis.divide(open).multiply(minimumTick).divide(minimumTickValue).absolute();
|
|
147
|
+
} else {
|
|
148
|
+
average = basis.divide(open).absolute();
|
|
149
|
+
}
|
|
143
150
|
} else {
|
|
144
151
|
average = '';
|
|
145
152
|
}
|
|
@@ -304,6 +304,22 @@ module.exports = (() => {
|
|
|
304
304
|
return this._excluded;
|
|
305
305
|
}
|
|
306
306
|
|
|
307
|
+
/**
|
|
308
|
+
* Changes the group currency.
|
|
309
|
+
*
|
|
310
|
+
* @public
|
|
311
|
+
* @param {Currency} currency
|
|
312
|
+
*/
|
|
313
|
+
changeCurrency(currency) {
|
|
314
|
+
assert.argumentIsRequired(currency, 'currency', Currency, 'Currency');
|
|
315
|
+
|
|
316
|
+
if (this._currency !== currency) {
|
|
317
|
+
this._currency = currency;
|
|
318
|
+
|
|
319
|
+
this.refresh();
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
307
323
|
/**
|
|
308
324
|
* Sets the immediate parent group (allowing for calculation of relative
|
|
309
325
|
* percentages).
|
|
@@ -442,6 +458,8 @@ module.exports = (() => {
|
|
|
442
458
|
}
|
|
443
459
|
|
|
444
460
|
this._dataFormat.portfolioType = portfolioType;
|
|
461
|
+
|
|
462
|
+
this.changeCurrency(this._definition.currencySelector({ portfolio }));
|
|
445
463
|
}
|
|
446
464
|
|
|
447
465
|
/**
|
|
@@ -605,6 +623,14 @@ module.exports = (() => {
|
|
|
605
623
|
|
|
606
624
|
this._dataActual.description = this._description;
|
|
607
625
|
this._dataFormat.description = this._description;
|
|
626
|
+
|
|
627
|
+
if (this._definition.type !== PositionLevelType.PORTFOLIO || this._key !== PositionLevelDefinition.getKeyForPortfolioGroup(portfolio)) {
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
const currencySelector = this._definition.currencySelector;
|
|
632
|
+
|
|
633
|
+
this.changeCurrency(currencySelector({ portfolio }));
|
|
608
634
|
}));
|
|
609
635
|
|
|
610
636
|
this._disposeStack.push(fundamentalBinding);
|
|
@@ -464,9 +464,9 @@ module.exports = (() => {
|
|
|
464
464
|
data.marketPrevious2 = previousSummary2 === null ? Decimal.ZERO : previousSummary2.end.value;
|
|
465
465
|
data.quantityPrevious = previousSummary1 === null ? Decimal.ZERO : previousSummary1.end.open;
|
|
466
466
|
|
|
467
|
-
data.periodGain = calculatePeriodGain(position.instrument
|
|
468
|
-
data.periodGainPrevious = calculatePeriodGain(position.instrument
|
|
469
|
-
data.periodGainPrevious2 = calculatePeriodGain(position.instrument
|
|
467
|
+
data.periodGain = calculatePeriodGain(position.instrument, data.initiate, currentSummary, previousSummary1);
|
|
468
|
+
data.periodGainPrevious = calculatePeriodGain(position.instrument, data.initiate, previousSummary1, previousSummary2);
|
|
469
|
+
data.periodGainPrevious2 = calculatePeriodGain(position.instrument, data.initiate, previousSummary2, previousSummary3);
|
|
470
470
|
|
|
471
471
|
data.periodIncome = currentSummary !== null ? currentSummary.period.income : Decimal.ZERO;
|
|
472
472
|
data.periodRealized = currentSummary !== null ? currentSummary.period.realized : Decimal.ZERO;
|
|
@@ -478,6 +478,11 @@ module.exports = (() => {
|
|
|
478
478
|
|
|
479
479
|
if (snapshot.open.getIsZero()) {
|
|
480
480
|
data.basisPrice = Decimal.ZERO;
|
|
481
|
+
} else if (position.instrument.type === InstrumentType.FUTURE) {
|
|
482
|
+
const minimumTick = position.instrument.future.tick;
|
|
483
|
+
const minimumTickValue = position.instrument.future.value;
|
|
484
|
+
|
|
485
|
+
data.basisPrice = basis.divide(snapshot.open).divide(minimumTickValue).multiply(minimumTick);
|
|
481
486
|
} else {
|
|
482
487
|
data.basisPrice = basis.divide(snapshot.open);
|
|
483
488
|
}
|
|
@@ -509,6 +514,8 @@ module.exports = (() => {
|
|
|
509
514
|
market = snapshot.value;
|
|
510
515
|
} else if (position.instrument.type === InstrumentType.CASH) {
|
|
511
516
|
market = snapshot.open;
|
|
517
|
+
} else if (position.instrument.type === InstrumentType.FUTURE) {
|
|
518
|
+
market = getFuturesValue(position.instrument, snapshot.open, price) || snapshot.value;
|
|
512
519
|
} else {
|
|
513
520
|
if (price) {
|
|
514
521
|
market = snapshot.open.multiply(price);
|
|
@@ -544,7 +551,15 @@ module.exports = (() => {
|
|
|
544
551
|
let unrealizedTodayChange;
|
|
545
552
|
|
|
546
553
|
if (data.previousPrice && price) {
|
|
547
|
-
|
|
554
|
+
let unrealizedTodayBase;
|
|
555
|
+
|
|
556
|
+
if (position.instrument.type === InstrumentType.FUTURE) {
|
|
557
|
+
unrealizedTodayBase = getFuturesValue(position.instrument, snapshot.open, data.previousPrice);
|
|
558
|
+
} else {
|
|
559
|
+
unrealizedTodayBase = snapshot.open.multiply(data.previousPrice);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
unrealizedToday = market.subtract(unrealizedTodayBase);
|
|
548
563
|
|
|
549
564
|
if (data.unrealizedToday !== null) {
|
|
550
565
|
unrealizedTodayChange = unrealizedToday.subtract(data.unrealizedToday);
|
|
@@ -576,7 +591,14 @@ module.exports = (() => {
|
|
|
576
591
|
}
|
|
577
592
|
|
|
578
593
|
if (priceToUse !== null) {
|
|
579
|
-
let unrealized
|
|
594
|
+
let unrealized;
|
|
595
|
+
|
|
596
|
+
if (position.instrument.type === InstrumentType.FUTURE) {
|
|
597
|
+
unrealized = getFuturesValue(position.instrument, currentSummary.end.open, priceToUse).add(currentSummary.end.basis);
|
|
598
|
+
} else {
|
|
599
|
+
unrealized = currentSummary.end.open.multiply(priceToUse).add(currentSummary.end.basis);
|
|
600
|
+
}
|
|
601
|
+
|
|
580
602
|
let unrealizedChange;
|
|
581
603
|
|
|
582
604
|
if (data.unrealized !== null) {
|
|
@@ -588,7 +610,7 @@ module.exports = (() => {
|
|
|
588
610
|
data.unrealized = unrealized;
|
|
589
611
|
data.unrealizedChange = unrealizedChange;
|
|
590
612
|
|
|
591
|
-
let periodGain = calculatePeriodGain(position.instrument
|
|
613
|
+
let periodGain = calculatePeriodGain(position.instrument, data.initiate, currentSummary, previousSummary, priceToUse);
|
|
592
614
|
let periodGainChange;
|
|
593
615
|
|
|
594
616
|
if (data.periodGain !== null) {
|
|
@@ -643,9 +665,11 @@ module.exports = (() => {
|
|
|
643
665
|
return direction || PositionDirection.LONG;
|
|
644
666
|
}
|
|
645
667
|
|
|
646
|
-
function calculatePeriodGain(
|
|
668
|
+
function calculatePeriodGain(instrument, direction, currentSummary, previousSummary, overridePrice) {
|
|
647
669
|
let returnRef;
|
|
648
670
|
|
|
671
|
+
const type = instrument.type;
|
|
672
|
+
|
|
649
673
|
if (currentSummary && type !== InstrumentType.CASH) {
|
|
650
674
|
let startValue;
|
|
651
675
|
|
|
@@ -658,7 +682,11 @@ module.exports = (() => {
|
|
|
658
682
|
let endValue;
|
|
659
683
|
|
|
660
684
|
if (overridePrice) {
|
|
661
|
-
|
|
685
|
+
if (type === InstrumentType.FUTURE) {
|
|
686
|
+
endValue = getFuturesValue(instrument, currentSummary.end.open, overridePrice);
|
|
687
|
+
} else {
|
|
688
|
+
endValue = currentSummary.end.open.multiply(overridePrice);
|
|
689
|
+
}
|
|
662
690
|
} else {
|
|
663
691
|
endValue = currentSummary.end.value;
|
|
664
692
|
}
|
|
@@ -779,5 +807,18 @@ module.exports = (() => {
|
|
|
779
807
|
return snapshot;
|
|
780
808
|
}
|
|
781
809
|
|
|
810
|
+
function getFuturesValue(instrument, contracts, price) {
|
|
811
|
+
if (price || price === 0) {
|
|
812
|
+
const priceDecimal = new Decimal(price);
|
|
813
|
+
|
|
814
|
+
const minimumTick = instrument.future.tick;
|
|
815
|
+
const minimumTickValue = instrument.future.value;
|
|
816
|
+
|
|
817
|
+
return priceDecimal.divide(minimumTick).multiply(minimumTickValue).multiply(contracts);
|
|
818
|
+
} else {
|
|
819
|
+
return null;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
782
823
|
return PositionItem;
|
|
783
824
|
})();
|
|
@@ -91,6 +91,11 @@ module.exports = (() => {
|
|
|
91
91
|
.withField('instrument.type', DataType.forEnum(InstrumentType, 'InstrumentType'))
|
|
92
92
|
.withField('instrument.currency', DataType.forEnum(Currency, 'Currency'))
|
|
93
93
|
.withField('instrument.delist', DataType.DAY, true)
|
|
94
|
+
.withField('instrument.future.expiration', DataType.DAY, true)
|
|
95
|
+
.withField('instrument.future.tick', DataType.DECIMAL, true)
|
|
96
|
+
.withField('instrument.future.value', DataType.DECIMAL, true)
|
|
97
|
+
.withField('instrument.option.expiration', DataType.DAY, true)
|
|
98
|
+
.withField('instrument.option.strike', DataType.DECIMAL, true)
|
|
94
99
|
.withField('instrument.symbol.barchart', DataType.STRING, true)
|
|
95
100
|
.withField('instrument.symbol.display', DataType.STRING, true)
|
|
96
101
|
.withField('position', DataType.STRING)
|
|
@@ -127,6 +132,11 @@ module.exports = (() => {
|
|
|
127
132
|
.withField('instrument.type', DataType.forEnum(InstrumentType, 'InstrumentType'))
|
|
128
133
|
.withField('instrument.currency', DataType.forEnum(Currency, 'Currency'))
|
|
129
134
|
.withField('instrument.delist', DataType.DAY, true)
|
|
135
|
+
.withField('instrument.future.expiration', DataType.DAY, true)
|
|
136
|
+
.withField('instrument.future.tick', DataType.DECIMAL, true)
|
|
137
|
+
.withField('instrument.future.value', DataType.DECIMAL, true)
|
|
138
|
+
.withField('instrument.option.expiration', DataType.DAY, true)
|
|
139
|
+
.withField('instrument.option.strike', DataType.DECIMAL, true)
|
|
130
140
|
.withField('instrument.symbol.barchart', DataType.STRING, true)
|
|
131
141
|
.withField('instrument.symbol.display', DataType.STRING, true)
|
|
132
142
|
.withField('position', DataType.STRING)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barchart/portfolio-api-common",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.18.0",
|
|
4
4
|
"description": "Common JavaScript code used by Barchart's Portfolio Service",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Bryan Ingle",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@barchart/common-js": "^4.27.0",
|
|
19
|
+
"@barchart/marketdata-api-js": "^6.1.0",
|
|
19
20
|
"uuid": "^8.3.2"
|
|
20
21
|
},
|
|
21
22
|
"devDependencies": {
|