@barchart/portfolio-api-common 1.0.267 → 1.0.271
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 +13 -1
- package/lib/data/TransactionValidator.js +3 -13
- package/lib/processing/PositionContainer.js +89 -119
- package/lib/processing/PositionGroup.js +1 -41
- package/package.json +1 -1
- package/test/SpecRunner.js +93 -181
- package/test/specs/data/TransactionValidatorSpec.js +0 -8
|
@@ -23,6 +23,17 @@ module.exports = (() => {
|
|
|
23
23
|
return portfolioUpdateFailedNoPortfolio;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* The portfolio does not exist.
|
|
28
|
+
*
|
|
29
|
+
* @public
|
|
30
|
+
* @static
|
|
31
|
+
* @returns {FailureType}
|
|
32
|
+
*/
|
|
33
|
+
static get PORTFOLIO_DELETE_FAILED_NO_PORTFOLIO() {
|
|
34
|
+
return portfolioDeleteFailedNoPortfolio;
|
|
35
|
+
}
|
|
36
|
+
|
|
26
37
|
/**
|
|
27
38
|
* The portfolio does not exist.
|
|
28
39
|
*
|
|
@@ -136,7 +147,8 @@ module.exports = (() => {
|
|
|
136
147
|
}
|
|
137
148
|
}
|
|
138
149
|
|
|
139
|
-
const portfolioUpdateFailedNoPortfolio = new FailureType('
|
|
150
|
+
const portfolioUpdateFailedNoPortfolio = new FailureType('PORTFOLIO_UPDATE_FAILED_NO_PORTFOLIO', 'Unable to update portfolio. The portfolio does not exist, has it been deleted?');
|
|
151
|
+
const portfolioDeleteFailedNoPortfolio = new FailureType('PORTFOLIO_DELETE_FAILED_NO_PORTFOLIO', 'Unable to delete portfolio. The portfolio does not exist, has it already been deleted?');
|
|
140
152
|
|
|
141
153
|
const positionCreateFailedNoPortfolio = new FailureType('POSITION_CREATE_FAILED_NO_PORTFOLIO', 'Unable to create transaction. The portfolio does not exist, has it been deleted?');
|
|
142
154
|
const positionUpdateFailedNoPosition = new FailureType('POSITION_UPDATE_FAILED_NO_POSITION', 'Unable to update preferences for position. The position does not exist, has it been deleted?');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const assert = require('@barchart/common-js/lang/assert'),
|
|
2
|
-
array = require('@barchart/common-js/lang/array')
|
|
2
|
+
array = require('@barchart/common-js/lang/array');
|
|
3
3
|
|
|
4
4
|
const InstrumentType = require('./InstrumentType'),
|
|
5
5
|
PositionDirection = require('./PositionDirection'),
|
|
@@ -25,22 +25,12 @@ module.exports = (() => {
|
|
|
25
25
|
* @public
|
|
26
26
|
* @static
|
|
27
27
|
* @param {Array.<Object>} transactions
|
|
28
|
-
* @param {Boolean=} partial - If true, sequence validation starts with the array's first transaction.
|
|
29
28
|
* @return {boolean}
|
|
30
29
|
*/
|
|
31
|
-
static validateOrder(transactions
|
|
30
|
+
static validateOrder(transactions) {
|
|
32
31
|
assert.argumentIsArray(transactions, 'transactions');
|
|
33
|
-
assert.argumentIsOptional(partial, 'partial', Boolean);
|
|
34
32
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (partial && transactions.length !== 0) {
|
|
38
|
-
startSequence = array.first(transactions).sequence;
|
|
39
|
-
} else {
|
|
40
|
-
startSequence = 1;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return transactions.every((t, i) => t.sequence === (i + startSequence) && (i === 0 || !t.date.getIsBefore(transactions[i - 1].date)));
|
|
33
|
+
return transactions.every((t, i) => t.sequence === (i + 1) && (i === 0 || !t.date.getIsBefore(transactions[i - 1].date)));
|
|
44
34
|
}
|
|
45
35
|
|
|
46
36
|
/**
|
|
@@ -224,11 +224,9 @@ module.exports = (() => {
|
|
|
224
224
|
assert.argumentIsRequired(portfolio, 'portfolio', Object);
|
|
225
225
|
assert.argumentIsRequired(portfolio.portfolio, 'portfolio.portfolio', String);
|
|
226
226
|
|
|
227
|
-
this.
|
|
228
|
-
getPositionItemsForPortfolio(this._items, portfolio.portfolio).forEach(item => item.updatePortfolio(portfolio));
|
|
227
|
+
getPositionItemsForPortfolio(this._items, portfolio.portfolio).forEach(item => item.updatePortfolio(portfolio));
|
|
229
228
|
|
|
230
|
-
|
|
231
|
-
});
|
|
229
|
+
updateEmptyPortfolioGroups.call(this, portfolio);
|
|
232
230
|
}
|
|
233
231
|
|
|
234
232
|
/**
|
|
@@ -243,19 +241,19 @@ module.exports = (() => {
|
|
|
243
241
|
assert.argumentIsRequired(portfolio, 'portfolio', Object);
|
|
244
242
|
assert.argumentIsRequired(portfolio.portfolio, 'portfolio.portfolio', String);
|
|
245
243
|
|
|
246
|
-
this.
|
|
247
|
-
getPositionItemsForPortfolio(this._items, portfolio.portfolio).forEach(item => removePositionItem.call(this, item));
|
|
244
|
+
getPositionItemsForPortfolio(this._items, portfolio.portfolio).forEach(item => removePositionItem.call(this, item));
|
|
248
245
|
|
|
249
|
-
|
|
246
|
+
delete this._portfolios[portfolio.portfolio];
|
|
250
247
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
});
|
|
248
|
+
Object.keys(this._trees).forEach((key) => {
|
|
249
|
+
this._trees[key].walk((group, groupNode) => {
|
|
250
|
+
if (group.definition.type === PositionLevelType.PORTFOLIO && group.key === PositionLevelDefinition.getKeyForPortfolioGroup(portfolio)) {
|
|
251
|
+
severGroupNode.call(this, groupNode);
|
|
252
|
+
}
|
|
253
|
+
}, true, false);
|
|
258
254
|
});
|
|
255
|
+
|
|
256
|
+
recalculatePercentages.call(this);
|
|
259
257
|
}
|
|
260
258
|
|
|
261
259
|
/**
|
|
@@ -276,56 +274,54 @@ module.exports = (() => {
|
|
|
276
274
|
return;
|
|
277
275
|
}
|
|
278
276
|
|
|
279
|
-
this.
|
|
280
|
-
const existingBarchartSymbols = this.getPositionSymbols(false);
|
|
277
|
+
const existingBarchartSymbols = this.getPositionSymbols(false);
|
|
281
278
|
|
|
282
|
-
|
|
279
|
+
removePositionItem.call(this, this._items.find((item) => item.position.position === position.position));
|
|
283
280
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
281
|
+
summaries.forEach((summary) => {
|
|
282
|
+
addSummaryCurrent(this._summariesCurrent, summary, this._currentSummaryFrame, this._currentSummaryRange);
|
|
283
|
+
addSummaryPrevious(this._summariesPrevious, summary, this._previousSummaryFrame, this._previousSummaryRanges);
|
|
284
|
+
});
|
|
288
285
|
|
|
289
|
-
|
|
286
|
+
const item = createPositionItem.call(this, position);
|
|
290
287
|
|
|
291
|
-
|
|
292
|
-
|
|
288
|
+
addBarchartSymbol(this._symbols, item);
|
|
289
|
+
addDisplaySymbol(this._symbolsDisplay, item);
|
|
293
290
|
|
|
294
|
-
|
|
291
|
+
this._items.push(item);
|
|
295
292
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
293
|
+
const createGroupOrInjectItem = (parentTree, treeDefinition, levelDefinitions) => {
|
|
294
|
+
if (levelDefinitions.length === 0) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
300
297
|
|
|
301
|
-
|
|
302
|
-
|
|
298
|
+
const levelDefinition = levelDefinitions[0];
|
|
299
|
+
const levelKey = levelDefinition.keySelector(item);
|
|
303
300
|
|
|
304
|
-
|
|
301
|
+
let groupTree;
|
|
305
302
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
303
|
+
if (parentTree.getChildren().length > 0) {
|
|
304
|
+
groupTree = parentTree.findChild(childGroup => childGroup.key === levelKey) || null;
|
|
305
|
+
} else {
|
|
306
|
+
groupTree = null;
|
|
307
|
+
}
|
|
311
308
|
|
|
312
|
-
|
|
313
|
-
|
|
309
|
+
if (groupTree !== null) {
|
|
310
|
+
groupTree.getValue().addItem(item);
|
|
314
311
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
312
|
+
createGroupOrInjectItem(groupTree, treeDefinition, array.dropLeft(levelDefinitions));
|
|
313
|
+
} else {
|
|
314
|
+
createGroups.call(this, parentTree, [ item ], treeDefinition, levelDefinitions, [ ]);
|
|
315
|
+
}
|
|
316
|
+
};
|
|
320
317
|
|
|
321
|
-
|
|
318
|
+
this._definitions.forEach(definition => createGroupOrInjectItem(this._trees[definition.name], definition, definition.definitions));
|
|
322
319
|
|
|
323
|
-
|
|
320
|
+
const addedBarchartSymbol = extractSymbolForBarchart(item.position);
|
|
324
321
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
});
|
|
322
|
+
if (addedBarchartSymbol !== null && !existingBarchartSymbols.some(existingBarchartSymbol => existingBarchartSymbol === addedBarchartSymbol)) {
|
|
323
|
+
this._positionSymbolAddedEvent.fire(addedBarchartSymbol);
|
|
324
|
+
}
|
|
329
325
|
|
|
330
326
|
recalculatePercentages.call(this);
|
|
331
327
|
}
|
|
@@ -376,22 +372,55 @@ module.exports = (() => {
|
|
|
376
372
|
}
|
|
377
373
|
|
|
378
374
|
/**
|
|
379
|
-
*
|
|
380
|
-
*
|
|
375
|
+
* Performs a batch update of both position quotes and forex quotes,
|
|
376
|
+
* triggering updates to position(s) and data aggregation(s).
|
|
381
377
|
*
|
|
382
378
|
* @public
|
|
383
|
-
* @param {
|
|
384
|
-
* @param {Object}
|
|
379
|
+
* @param {Array.<Object>} positionQuotes
|
|
380
|
+
* @param {Array.<Object>} forexQuotes
|
|
385
381
|
*/
|
|
386
|
-
|
|
387
|
-
assert.
|
|
388
|
-
assert.
|
|
382
|
+
setQuotes(positionQuotes, forexQuotes) {
|
|
383
|
+
assert.argumentIsArray(positionQuotes, 'positionQuotes');
|
|
384
|
+
assert.argumentIsArray(forexQuotes, 'forexQuotes');
|
|
385
|
+
|
|
386
|
+
if (positionQuotes.length !== 0) {
|
|
387
|
+
positionQuotes.forEach((quote) => {
|
|
388
|
+
const symbol = quote.symbol;
|
|
389
389
|
|
|
390
|
-
|
|
391
|
-
|
|
390
|
+
if (symbol) {
|
|
391
|
+
if (this._symbols.hasOwnProperty(symbol)) {
|
|
392
|
+
this._symbols[ symbol ].forEach(item => item.setQuote(quote));
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
});
|
|
392
396
|
}
|
|
393
397
|
|
|
394
|
-
|
|
398
|
+
if (forexQuotes.length !== 0) {
|
|
399
|
+
forexQuotes.forEach((quote) => {
|
|
400
|
+
const symbol = quote.symbol;
|
|
401
|
+
|
|
402
|
+
if (symbol) {
|
|
403
|
+
const rate = Rate.fromPair(quote.lastPrice, symbol);
|
|
404
|
+
const index = this._forexQuotes.findIndex(existing => existing.formatPair() === rate.formatPair());
|
|
405
|
+
|
|
406
|
+
if (index < 0) {
|
|
407
|
+
this._forexQuotes.push(rate);
|
|
408
|
+
} else {
|
|
409
|
+
this._forexQuotes[index] = rate;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
Object.keys(this._trees).forEach((key) => {
|
|
413
|
+
this._trees[key].walk(group => group.setForexRates(this._forexQuotes), true, false);
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
recalculatePercentages.call(this);
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (positionQuotes.length !== 0 || forexQuotes.length !== 0) {
|
|
422
|
+
recalculatePercentages.call(this);
|
|
423
|
+
}
|
|
395
424
|
}
|
|
396
425
|
|
|
397
426
|
/**
|
|
@@ -414,32 +443,6 @@ module.exports = (() => {
|
|
|
414
443
|
return this._forexQuotes;
|
|
415
444
|
}
|
|
416
445
|
|
|
417
|
-
/**
|
|
418
|
-
* Updates the forex quote for a single currency pair; causing updates to
|
|
419
|
-
* any grouping level that contains that requires translation.
|
|
420
|
-
*
|
|
421
|
-
* @public
|
|
422
|
-
* @param {String} symbol
|
|
423
|
-
* @param {Object} quote
|
|
424
|
-
*/
|
|
425
|
-
setForexQuote(symbol, quote) {
|
|
426
|
-
assert.argumentIsRequired(symbol, 'symbol', String);
|
|
427
|
-
assert.argumentIsRequired(quote, 'quote', Object);
|
|
428
|
-
|
|
429
|
-
const rate = Rate.fromPair(quote.lastPrice, symbol);
|
|
430
|
-
const index = this._forexQuotes.findIndex(existing => existing.formatPair() === rate.formatPair());
|
|
431
|
-
|
|
432
|
-
if (index < 0) {
|
|
433
|
-
this._forexQuotes.push(rate);
|
|
434
|
-
} else {
|
|
435
|
-
this._forexQuotes[index] = rate;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
Object.keys(this._trees).forEach(key => this._trees[key].walk(group => group.setForexRates(this._forexQuotes), true, false));
|
|
439
|
-
|
|
440
|
-
recalculatePercentages.call(this);
|
|
441
|
-
}
|
|
442
|
-
|
|
443
446
|
/**
|
|
444
447
|
* Updates fundamental data for a single symbol.
|
|
445
448
|
*
|
|
@@ -588,39 +591,6 @@ module.exports = (() => {
|
|
|
588
591
|
return this.getPositions(portfolio).find(p => p.position === position) || null;
|
|
589
592
|
}
|
|
590
593
|
|
|
591
|
-
/**
|
|
592
|
-
* Pauses aggregation calculations during the processing of an action.
|
|
593
|
-
*
|
|
594
|
-
* @public
|
|
595
|
-
* @param {Function} executor
|
|
596
|
-
* @param {String=|Array.<String>=} names
|
|
597
|
-
*/
|
|
598
|
-
startTransaction(executor, names) {
|
|
599
|
-
let namesToUse;
|
|
600
|
-
|
|
601
|
-
if (is.array(names)) {
|
|
602
|
-
assert.argumentIsArray(names, 'names', String);
|
|
603
|
-
|
|
604
|
-
namesToUse = names;
|
|
605
|
-
} else {
|
|
606
|
-
assert.argumentIsOptional(names, 'names', String);
|
|
607
|
-
|
|
608
|
-
if (names) {
|
|
609
|
-
namesToUse = [ names ];
|
|
610
|
-
} else {
|
|
611
|
-
namesToUse = Object.keys(this._trees);
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
assert.argumentIsRequired(executor, 'executor', Function);
|
|
616
|
-
|
|
617
|
-
namesToUse.forEach((name) => this._trees[name].walk(group => group.setSuspended(true), false, false));
|
|
618
|
-
|
|
619
|
-
executor(this);
|
|
620
|
-
|
|
621
|
-
namesToUse.forEach((name) => this._trees[name].walk(group => group.setSuspended(false), false, false));
|
|
622
|
-
}
|
|
623
|
-
|
|
624
594
|
/**
|
|
625
595
|
* Registers an observer for symbol addition (this occurs when a new position is added
|
|
626
596
|
* for a symbol that does not already exist in the container). This event only fires
|
|
@@ -54,7 +54,6 @@ module.exports = (() => {
|
|
|
54
54
|
this._aggregateCash = is.boolean(aggregateCash) && aggregateCash;
|
|
55
55
|
|
|
56
56
|
this._excluded = false;
|
|
57
|
-
this._suspended = false;
|
|
58
57
|
this._showClosedPositions = false;
|
|
59
58
|
|
|
60
59
|
this._groupExcludedChangeEvent = new Event(this);
|
|
@@ -255,10 +254,6 @@ module.exports = (() => {
|
|
|
255
254
|
return this._single;
|
|
256
255
|
}
|
|
257
256
|
|
|
258
|
-
get suspended() {
|
|
259
|
-
return this._suspended;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
257
|
/**
|
|
263
258
|
* Indicates if the group should be excluded from higher-level aggregations.
|
|
264
259
|
*
|
|
@@ -381,24 +376,6 @@ module.exports = (() => {
|
|
|
381
376
|
}
|
|
382
377
|
}
|
|
383
378
|
|
|
384
|
-
/**
|
|
385
|
-
* Stops (or starts) group-level aggregation calculations.
|
|
386
|
-
*
|
|
387
|
-
* @public
|
|
388
|
-
* @param {Boolean} value
|
|
389
|
-
*/
|
|
390
|
-
setSuspended(value) {
|
|
391
|
-
assert.argumentIsRequired(value, 'value', Boolean);
|
|
392
|
-
|
|
393
|
-
if (this._suspended !== value) {
|
|
394
|
-
this._suspended = value;
|
|
395
|
-
|
|
396
|
-
if (!this._suspended) {
|
|
397
|
-
this.refresh();
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
379
|
/**
|
|
403
380
|
* Updates the portfolio data. For example, a portfolio's name might change. This
|
|
404
381
|
* function only affects {@link PositionLevelType.PORTFOLIO} groups.
|
|
@@ -430,16 +407,11 @@ module.exports = (() => {
|
|
|
430
407
|
}
|
|
431
408
|
|
|
432
409
|
/**
|
|
433
|
-
* Causes all aggregated data to be recalculated
|
|
434
|
-
* been suspended).
|
|
410
|
+
* Causes all aggregated data to be recalculated.
|
|
435
411
|
*
|
|
436
412
|
* @public
|
|
437
413
|
*/
|
|
438
414
|
refresh() {
|
|
439
|
-
if (this._suspended) {
|
|
440
|
-
return;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
415
|
calculateStaticData(this, this._rates);
|
|
444
416
|
calculatePriceData(this, this._rates, null, true);
|
|
445
417
|
}
|
|
@@ -643,10 +615,6 @@ module.exports = (() => {
|
|
|
643
615
|
}
|
|
644
616
|
|
|
645
617
|
function calculateStaticData(group, rates) {
|
|
646
|
-
if (group.suspended) {
|
|
647
|
-
return;
|
|
648
|
-
}
|
|
649
|
-
|
|
650
618
|
const actual = group._dataActual;
|
|
651
619
|
const format = group._dataFormat;
|
|
652
620
|
|
|
@@ -743,10 +711,6 @@ module.exports = (() => {
|
|
|
743
711
|
}
|
|
744
712
|
|
|
745
713
|
function calculatePriceData(group, rates, item, forceRefresh) {
|
|
746
|
-
if (group.suspended) {
|
|
747
|
-
return;
|
|
748
|
-
}
|
|
749
|
-
|
|
750
714
|
const currency = group.currency;
|
|
751
715
|
|
|
752
716
|
const actual = group._dataActual;
|
|
@@ -832,10 +796,6 @@ module.exports = (() => {
|
|
|
832
796
|
}
|
|
833
797
|
|
|
834
798
|
function calculateMarketPercent(group, rates, parentGroup, portfolioGroup) {
|
|
835
|
-
if (group.suspended) {
|
|
836
|
-
return;
|
|
837
|
-
}
|
|
838
|
-
|
|
839
799
|
const actual = group._dataActual;
|
|
840
800
|
const format = group._dataFormat;
|
|
841
801
|
const excluded = group._excluded;
|
package/package.json
CHANGED
package/test/SpecRunner.js
CHANGED
|
@@ -957,7 +957,7 @@ module.exports = (() => {
|
|
|
957
957
|
|
|
958
958
|
},{"@barchart/common-js/lang/Enum":21,"@barchart/common-js/lang/assert":24}],5:[function(require,module,exports){
|
|
959
959
|
const assert = require('@barchart/common-js/lang/assert'),
|
|
960
|
-
array = require('@barchart/common-js/lang/array')
|
|
960
|
+
array = require('@barchart/common-js/lang/array');
|
|
961
961
|
|
|
962
962
|
const InstrumentType = require('./InstrumentType'),
|
|
963
963
|
PositionDirection = require('./PositionDirection'),
|
|
@@ -983,22 +983,12 @@ module.exports = (() => {
|
|
|
983
983
|
* @public
|
|
984
984
|
* @static
|
|
985
985
|
* @param {Array.<Object>} transactions
|
|
986
|
-
* @param {Boolean=} partial - If true, sequence validation starts with the array's first transaction.
|
|
987
986
|
* @return {boolean}
|
|
988
987
|
*/
|
|
989
|
-
static validateOrder(transactions
|
|
988
|
+
static validateOrder(transactions) {
|
|
990
989
|
assert.argumentIsArray(transactions, 'transactions');
|
|
991
|
-
assert.argumentIsOptional(partial, 'partial', Boolean);
|
|
992
990
|
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
if (partial && transactions.length !== 0) {
|
|
996
|
-
startSequence = array.first(transactions).sequence;
|
|
997
|
-
} else {
|
|
998
|
-
startSequence = 1;
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
return transactions.every((t, i) => t.sequence === (i + startSequence) && (i === 0 || !t.date.getIsBefore(transactions[i - 1].date)));
|
|
991
|
+
return transactions.every((t, i) => t.sequence === (i + 1) && (i === 0 || !t.date.getIsBefore(transactions[i - 1].date)));
|
|
1002
992
|
}
|
|
1003
993
|
|
|
1004
994
|
/**
|
|
@@ -1380,11 +1370,9 @@ module.exports = (() => {
|
|
|
1380
1370
|
assert.argumentIsRequired(portfolio, 'portfolio', Object);
|
|
1381
1371
|
assert.argumentIsRequired(portfolio.portfolio, 'portfolio.portfolio', String);
|
|
1382
1372
|
|
|
1383
|
-
this.
|
|
1384
|
-
getPositionItemsForPortfolio(this._items, portfolio.portfolio).forEach(item => item.updatePortfolio(portfolio));
|
|
1373
|
+
getPositionItemsForPortfolio(this._items, portfolio.portfolio).forEach(item => item.updatePortfolio(portfolio));
|
|
1385
1374
|
|
|
1386
|
-
|
|
1387
|
-
});
|
|
1375
|
+
updateEmptyPortfolioGroups.call(this, portfolio);
|
|
1388
1376
|
}
|
|
1389
1377
|
|
|
1390
1378
|
/**
|
|
@@ -1399,19 +1387,19 @@ module.exports = (() => {
|
|
|
1399
1387
|
assert.argumentIsRequired(portfolio, 'portfolio', Object);
|
|
1400
1388
|
assert.argumentIsRequired(portfolio.portfolio, 'portfolio.portfolio', String);
|
|
1401
1389
|
|
|
1402
|
-
this.
|
|
1403
|
-
getPositionItemsForPortfolio(this._items, portfolio.portfolio).forEach(item => removePositionItem.call(this, item));
|
|
1390
|
+
getPositionItemsForPortfolio(this._items, portfolio.portfolio).forEach(item => removePositionItem.call(this, item));
|
|
1404
1391
|
|
|
1405
|
-
|
|
1392
|
+
delete this._portfolios[portfolio.portfolio];
|
|
1406
1393
|
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
});
|
|
1394
|
+
Object.keys(this._trees).forEach((key) => {
|
|
1395
|
+
this._trees[key].walk((group, groupNode) => {
|
|
1396
|
+
if (group.definition.type === PositionLevelType.PORTFOLIO && group.key === PositionLevelDefinition.getKeyForPortfolioGroup(portfolio)) {
|
|
1397
|
+
severGroupNode.call(this, groupNode);
|
|
1398
|
+
}
|
|
1399
|
+
}, true, false);
|
|
1414
1400
|
});
|
|
1401
|
+
|
|
1402
|
+
recalculatePercentages.call(this);
|
|
1415
1403
|
}
|
|
1416
1404
|
|
|
1417
1405
|
/**
|
|
@@ -1432,56 +1420,54 @@ module.exports = (() => {
|
|
|
1432
1420
|
return;
|
|
1433
1421
|
}
|
|
1434
1422
|
|
|
1435
|
-
this.
|
|
1436
|
-
const existingBarchartSymbols = this.getPositionSymbols(false);
|
|
1423
|
+
const existingBarchartSymbols = this.getPositionSymbols(false);
|
|
1437
1424
|
|
|
1438
|
-
|
|
1425
|
+
removePositionItem.call(this, this._items.find((item) => item.position.position === position.position));
|
|
1439
1426
|
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1427
|
+
summaries.forEach((summary) => {
|
|
1428
|
+
addSummaryCurrent(this._summariesCurrent, summary, this._currentSummaryFrame, this._currentSummaryRange);
|
|
1429
|
+
addSummaryPrevious(this._summariesPrevious, summary, this._previousSummaryFrame, this._previousSummaryRanges);
|
|
1430
|
+
});
|
|
1444
1431
|
|
|
1445
|
-
|
|
1432
|
+
const item = createPositionItem.call(this, position);
|
|
1446
1433
|
|
|
1447
|
-
|
|
1448
|
-
|
|
1434
|
+
addBarchartSymbol(this._symbols, item);
|
|
1435
|
+
addDisplaySymbol(this._symbolsDisplay, item);
|
|
1449
1436
|
|
|
1450
|
-
|
|
1437
|
+
this._items.push(item);
|
|
1451
1438
|
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1439
|
+
const createGroupOrInjectItem = (parentTree, treeDefinition, levelDefinitions) => {
|
|
1440
|
+
if (levelDefinitions.length === 0) {
|
|
1441
|
+
return;
|
|
1442
|
+
}
|
|
1456
1443
|
|
|
1457
|
-
|
|
1458
|
-
|
|
1444
|
+
const levelDefinition = levelDefinitions[0];
|
|
1445
|
+
const levelKey = levelDefinition.keySelector(item);
|
|
1459
1446
|
|
|
1460
|
-
|
|
1447
|
+
let groupTree;
|
|
1461
1448
|
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1449
|
+
if (parentTree.getChildren().length > 0) {
|
|
1450
|
+
groupTree = parentTree.findChild(childGroup => childGroup.key === levelKey) || null;
|
|
1451
|
+
} else {
|
|
1452
|
+
groupTree = null;
|
|
1453
|
+
}
|
|
1467
1454
|
|
|
1468
|
-
|
|
1469
|
-
|
|
1455
|
+
if (groupTree !== null) {
|
|
1456
|
+
groupTree.getValue().addItem(item);
|
|
1470
1457
|
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1458
|
+
createGroupOrInjectItem(groupTree, treeDefinition, array.dropLeft(levelDefinitions));
|
|
1459
|
+
} else {
|
|
1460
|
+
createGroups.call(this, parentTree, [ item ], treeDefinition, levelDefinitions, [ ]);
|
|
1461
|
+
}
|
|
1462
|
+
};
|
|
1476
1463
|
|
|
1477
|
-
|
|
1464
|
+
this._definitions.forEach(definition => createGroupOrInjectItem(this._trees[definition.name], definition, definition.definitions));
|
|
1478
1465
|
|
|
1479
|
-
|
|
1466
|
+
const addedBarchartSymbol = extractSymbolForBarchart(item.position);
|
|
1480
1467
|
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
});
|
|
1468
|
+
if (addedBarchartSymbol !== null && !existingBarchartSymbols.some(existingBarchartSymbol => existingBarchartSymbol === addedBarchartSymbol)) {
|
|
1469
|
+
this._positionSymbolAddedEvent.fire(addedBarchartSymbol);
|
|
1470
|
+
}
|
|
1485
1471
|
|
|
1486
1472
|
recalculatePercentages.call(this);
|
|
1487
1473
|
}
|
|
@@ -1532,22 +1518,55 @@ module.exports = (() => {
|
|
|
1532
1518
|
}
|
|
1533
1519
|
|
|
1534
1520
|
/**
|
|
1535
|
-
*
|
|
1536
|
-
*
|
|
1521
|
+
* Performs a batch update of both position quotes and forex quotes,
|
|
1522
|
+
* triggering updates to position(s) and data aggregation(s).
|
|
1537
1523
|
*
|
|
1538
1524
|
* @public
|
|
1539
|
-
* @param {
|
|
1540
|
-
* @param {Object}
|
|
1525
|
+
* @param {Array.<Object>} positionQuotes
|
|
1526
|
+
* @param {Array.<Object>} forexQuotes
|
|
1541
1527
|
*/
|
|
1542
|
-
|
|
1543
|
-
assert.
|
|
1544
|
-
assert.
|
|
1528
|
+
setQuotes(positionQuotes, forexQuotes) {
|
|
1529
|
+
assert.argumentIsArray(positionQuotes, 'positionQuotes');
|
|
1530
|
+
assert.argumentIsArray(forexQuotes, 'forexQuotes');
|
|
1531
|
+
|
|
1532
|
+
if (positionQuotes.length !== 0) {
|
|
1533
|
+
positionQuotes.forEach((quote) => {
|
|
1534
|
+
const symbol = quote.symbol;
|
|
1545
1535
|
|
|
1546
|
-
|
|
1547
|
-
|
|
1536
|
+
if (symbol) {
|
|
1537
|
+
if (this._symbols.hasOwnProperty(symbol)) {
|
|
1538
|
+
this._symbols[ symbol ].forEach(item => item.setQuote(quote));
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
});
|
|
1548
1542
|
}
|
|
1549
1543
|
|
|
1550
|
-
|
|
1544
|
+
if (forexQuotes.length !== 0) {
|
|
1545
|
+
forexQuotes.forEach((quote) => {
|
|
1546
|
+
const symbol = quote.symbol;
|
|
1547
|
+
|
|
1548
|
+
if (symbol) {
|
|
1549
|
+
const rate = Rate.fromPair(quote.lastPrice, symbol);
|
|
1550
|
+
const index = this._forexQuotes.findIndex(existing => existing.formatPair() === rate.formatPair());
|
|
1551
|
+
|
|
1552
|
+
if (index < 0) {
|
|
1553
|
+
this._forexQuotes.push(rate);
|
|
1554
|
+
} else {
|
|
1555
|
+
this._forexQuotes[index] = rate;
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
Object.keys(this._trees).forEach((key) => {
|
|
1559
|
+
this._trees[key].walk(group => group.setForexRates(this._forexQuotes), true, false);
|
|
1560
|
+
});
|
|
1561
|
+
|
|
1562
|
+
recalculatePercentages.call(this);
|
|
1563
|
+
}
|
|
1564
|
+
});
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
if (positionQuotes.length !== 0 || forexQuotes.length !== 0) {
|
|
1568
|
+
recalculatePercentages.call(this);
|
|
1569
|
+
}
|
|
1551
1570
|
}
|
|
1552
1571
|
|
|
1553
1572
|
/**
|
|
@@ -1570,32 +1589,6 @@ module.exports = (() => {
|
|
|
1570
1589
|
return this._forexQuotes;
|
|
1571
1590
|
}
|
|
1572
1591
|
|
|
1573
|
-
/**
|
|
1574
|
-
* Updates the forex quote for a single currency pair; causing updates to
|
|
1575
|
-
* any grouping level that contains that requires translation.
|
|
1576
|
-
*
|
|
1577
|
-
* @public
|
|
1578
|
-
* @param {String} symbol
|
|
1579
|
-
* @param {Object} quote
|
|
1580
|
-
*/
|
|
1581
|
-
setForexQuote(symbol, quote) {
|
|
1582
|
-
assert.argumentIsRequired(symbol, 'symbol', String);
|
|
1583
|
-
assert.argumentIsRequired(quote, 'quote', Object);
|
|
1584
|
-
|
|
1585
|
-
const rate = Rate.fromPair(quote.lastPrice, symbol);
|
|
1586
|
-
const index = this._forexQuotes.findIndex(existing => existing.formatPair() === rate.formatPair());
|
|
1587
|
-
|
|
1588
|
-
if (index < 0) {
|
|
1589
|
-
this._forexQuotes.push(rate);
|
|
1590
|
-
} else {
|
|
1591
|
-
this._forexQuotes[index] = rate;
|
|
1592
|
-
}
|
|
1593
|
-
|
|
1594
|
-
Object.keys(this._trees).forEach(key => this._trees[key].walk(group => group.setForexRates(this._forexQuotes), true, false));
|
|
1595
|
-
|
|
1596
|
-
recalculatePercentages.call(this);
|
|
1597
|
-
}
|
|
1598
|
-
|
|
1599
1592
|
/**
|
|
1600
1593
|
* Updates fundamental data for a single symbol.
|
|
1601
1594
|
*
|
|
@@ -1744,39 +1737,6 @@ module.exports = (() => {
|
|
|
1744
1737
|
return this.getPositions(portfolio).find(p => p.position === position) || null;
|
|
1745
1738
|
}
|
|
1746
1739
|
|
|
1747
|
-
/**
|
|
1748
|
-
* Pauses aggregation calculations during the processing of an action.
|
|
1749
|
-
*
|
|
1750
|
-
* @public
|
|
1751
|
-
* @param {Function} executor
|
|
1752
|
-
* @param {String=|Array.<String>=} names
|
|
1753
|
-
*/
|
|
1754
|
-
startTransaction(executor, names) {
|
|
1755
|
-
let namesToUse;
|
|
1756
|
-
|
|
1757
|
-
if (is.array(names)) {
|
|
1758
|
-
assert.argumentIsArray(names, 'names', String);
|
|
1759
|
-
|
|
1760
|
-
namesToUse = names;
|
|
1761
|
-
} else {
|
|
1762
|
-
assert.argumentIsOptional(names, 'names', String);
|
|
1763
|
-
|
|
1764
|
-
if (names) {
|
|
1765
|
-
namesToUse = [ names ];
|
|
1766
|
-
} else {
|
|
1767
|
-
namesToUse = Object.keys(this._trees);
|
|
1768
|
-
}
|
|
1769
|
-
}
|
|
1770
|
-
|
|
1771
|
-
assert.argumentIsRequired(executor, 'executor', Function);
|
|
1772
|
-
|
|
1773
|
-
namesToUse.forEach((name) => this._trees[name].walk(group => group.setSuspended(true), false, false));
|
|
1774
|
-
|
|
1775
|
-
executor(this);
|
|
1776
|
-
|
|
1777
|
-
namesToUse.forEach((name) => this._trees[name].walk(group => group.setSuspended(false), false, false));
|
|
1778
|
-
}
|
|
1779
|
-
|
|
1780
1740
|
/**
|
|
1781
1741
|
* Registers an observer for symbol addition (this occurs when a new position is added
|
|
1782
1742
|
* for a symbol that does not already exist in the container). This event only fires
|
|
@@ -2183,7 +2143,6 @@ module.exports = (() => {
|
|
|
2183
2143
|
this._aggregateCash = is.boolean(aggregateCash) && aggregateCash;
|
|
2184
2144
|
|
|
2185
2145
|
this._excluded = false;
|
|
2186
|
-
this._suspended = false;
|
|
2187
2146
|
this._showClosedPositions = false;
|
|
2188
2147
|
|
|
2189
2148
|
this._groupExcludedChangeEvent = new Event(this);
|
|
@@ -2384,10 +2343,6 @@ module.exports = (() => {
|
|
|
2384
2343
|
return this._single;
|
|
2385
2344
|
}
|
|
2386
2345
|
|
|
2387
|
-
get suspended() {
|
|
2388
|
-
return this._suspended;
|
|
2389
|
-
}
|
|
2390
|
-
|
|
2391
2346
|
/**
|
|
2392
2347
|
* Indicates if the group should be excluded from higher-level aggregations.
|
|
2393
2348
|
*
|
|
@@ -2510,24 +2465,6 @@ module.exports = (() => {
|
|
|
2510
2465
|
}
|
|
2511
2466
|
}
|
|
2512
2467
|
|
|
2513
|
-
/**
|
|
2514
|
-
* Stops (or starts) group-level aggregation calculations.
|
|
2515
|
-
*
|
|
2516
|
-
* @public
|
|
2517
|
-
* @param {Boolean} value
|
|
2518
|
-
*/
|
|
2519
|
-
setSuspended(value) {
|
|
2520
|
-
assert.argumentIsRequired(value, 'value', Boolean);
|
|
2521
|
-
|
|
2522
|
-
if (this._suspended !== value) {
|
|
2523
|
-
this._suspended = value;
|
|
2524
|
-
|
|
2525
|
-
if (!this._suspended) {
|
|
2526
|
-
this.refresh();
|
|
2527
|
-
}
|
|
2528
|
-
}
|
|
2529
|
-
}
|
|
2530
|
-
|
|
2531
2468
|
/**
|
|
2532
2469
|
* Updates the portfolio data. For example, a portfolio's name might change. This
|
|
2533
2470
|
* function only affects {@link PositionLevelType.PORTFOLIO} groups.
|
|
@@ -2559,16 +2496,11 @@ module.exports = (() => {
|
|
|
2559
2496
|
}
|
|
2560
2497
|
|
|
2561
2498
|
/**
|
|
2562
|
-
* Causes all aggregated data to be recalculated
|
|
2563
|
-
* been suspended).
|
|
2499
|
+
* Causes all aggregated data to be recalculated.
|
|
2564
2500
|
*
|
|
2565
2501
|
* @public
|
|
2566
2502
|
*/
|
|
2567
2503
|
refresh() {
|
|
2568
|
-
if (this._suspended) {
|
|
2569
|
-
return;
|
|
2570
|
-
}
|
|
2571
|
-
|
|
2572
2504
|
calculateStaticData(this, this._rates);
|
|
2573
2505
|
calculatePriceData(this, this._rates, null, true);
|
|
2574
2506
|
}
|
|
@@ -2772,10 +2704,6 @@ module.exports = (() => {
|
|
|
2772
2704
|
}
|
|
2773
2705
|
|
|
2774
2706
|
function calculateStaticData(group, rates) {
|
|
2775
|
-
if (group.suspended) {
|
|
2776
|
-
return;
|
|
2777
|
-
}
|
|
2778
|
-
|
|
2779
2707
|
const actual = group._dataActual;
|
|
2780
2708
|
const format = group._dataFormat;
|
|
2781
2709
|
|
|
@@ -2872,10 +2800,6 @@ module.exports = (() => {
|
|
|
2872
2800
|
}
|
|
2873
2801
|
|
|
2874
2802
|
function calculatePriceData(group, rates, item, forceRefresh) {
|
|
2875
|
-
if (group.suspended) {
|
|
2876
|
-
return;
|
|
2877
|
-
}
|
|
2878
|
-
|
|
2879
2803
|
const currency = group.currency;
|
|
2880
2804
|
|
|
2881
2805
|
const actual = group._dataActual;
|
|
@@ -2961,10 +2885,6 @@ module.exports = (() => {
|
|
|
2961
2885
|
}
|
|
2962
2886
|
|
|
2963
2887
|
function calculateMarketPercent(group, rates, parentGroup, portfolioGroup) {
|
|
2964
|
-
if (group.suspended) {
|
|
2965
|
-
return;
|
|
2966
|
-
}
|
|
2967
|
-
|
|
2968
2888
|
const actual = group._dataActual;
|
|
2969
2889
|
const format = group._dataFormat;
|
|
2970
2890
|
const excluded = group._excluded;
|
|
@@ -9173,14 +9093,6 @@ describe('When validating transaction order', () => {
|
|
|
9173
9093
|
it('An array of transactions with ordered sequences, on the reversed days should not be valid', () => {
|
|
9174
9094
|
expect(TransactionValidator.validateOrder([ build(1, '2018-05-02'), build(2, '2018-05-01'), build(3, '2018-04-30') ])).toEqual(false);
|
|
9175
9095
|
});
|
|
9176
|
-
|
|
9177
|
-
it('A partial array of transactions with ordered sequences (starting after one), on the same day should be valid', () => {
|
|
9178
|
-
expect(TransactionValidator.validateOrder([ build(3, '2018-04-30'), build(4, '2018-04-30'), build(5, '2018-04-30') ], true)).toEqual(true);
|
|
9179
|
-
});
|
|
9180
|
-
|
|
9181
|
-
it('A partial array of transactions with gap in sequences (starting after one), on the same day should be not valid', () => {
|
|
9182
|
-
expect(TransactionValidator.validateOrder([ build(3, '2018-04-30'), build(5, '2018-04-30'), build(6, '2018-04-30') ], true)).toEqual(false);
|
|
9183
|
-
});
|
|
9184
9096
|
});
|
|
9185
9097
|
|
|
9186
9098
|
},{"./../../../lib/data/TransactionValidator":5,"@barchart/common-js/lang/Day":18}],37:[function(require,module,exports){
|
|
@@ -40,12 +40,4 @@ describe('When validating transaction order', () => {
|
|
|
40
40
|
it('An array of transactions with ordered sequences, on the reversed days should not be valid', () => {
|
|
41
41
|
expect(TransactionValidator.validateOrder([ build(1, '2018-05-02'), build(2, '2018-05-01'), build(3, '2018-04-30') ])).toEqual(false);
|
|
42
42
|
});
|
|
43
|
-
|
|
44
|
-
it('A partial array of transactions with ordered sequences (starting after one), on the same day should be valid', () => {
|
|
45
|
-
expect(TransactionValidator.validateOrder([ build(3, '2018-04-30'), build(4, '2018-04-30'), build(5, '2018-04-30') ], true)).toEqual(true);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('A partial array of transactions with gap in sequences (starting after one), on the same day should be not valid', () => {
|
|
49
|
-
expect(TransactionValidator.validateOrder([ build(3, '2018-04-30'), build(5, '2018-04-30'), build(6, '2018-04-30') ], true)).toEqual(false);
|
|
50
|
-
});
|
|
51
43
|
});
|