@barchart/portfolio-api-common 1.0.160 → 1.0.164
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/processing/PositionContainer.js +184 -113
- package/lib/processing/PositionGroup.js +24 -1
- package/lib/processing/definitions/PositionLevelDefinition.js +99 -2
- package/lib/processing/definitions/PositionTreeDefinition.js +1 -1
- package/package.json +1 -1
- package/test/SpecRunner.js +309 -118
|
@@ -48,17 +48,9 @@ module.exports = (() => {
|
|
|
48
48
|
const currentSummaryFrame = PositionSummaryFrame.YTD;
|
|
49
49
|
const currentSummaryRange = array.last(currentSummaryFrame.getRecentRanges(0));
|
|
50
50
|
|
|
51
|
-
this.
|
|
52
|
-
|
|
53
|
-
const addGroupBinding = (group, dispoable) => {
|
|
54
|
-
const id = group.id;
|
|
55
|
-
|
|
56
|
-
if (!this._groupBindings.hasOwnProperty(id)) {
|
|
57
|
-
this._groupBindings[id] = new DisposableStack();
|
|
58
|
-
}
|
|
51
|
+
this._definitions = definitions;
|
|
59
52
|
|
|
60
|
-
|
|
61
|
-
};
|
|
53
|
+
this._groupBindings = { };
|
|
62
54
|
|
|
63
55
|
this._portfolios = portfolios.reduce((map, portfolio) => {
|
|
64
56
|
map[portfolio.portfolio] = portfolio;
|
|
@@ -164,132 +156,82 @@ module.exports = (() => {
|
|
|
164
156
|
return Rate.fromPair(Decimal.ONE, symbol);
|
|
165
157
|
});
|
|
166
158
|
|
|
167
|
-
this._trees =
|
|
159
|
+
this._trees = this._definitions.reduce((map, treeDefinition) => {
|
|
168
160
|
const tree = new Tree();
|
|
169
161
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const parent = currentTree.getValue() || null;
|
|
176
|
-
|
|
177
|
-
const levelDefinition = levelDefinitions[0];
|
|
178
|
-
|
|
179
|
-
const populatedObjects = array.groupBy(items, levelDefinition.keySelector);
|
|
180
|
-
const populatedGroups = Object.keys(populatedObjects).reduce((list, key) => {
|
|
181
|
-
const items = populatedObjects[key];
|
|
182
|
-
const first = items[0];
|
|
162
|
+
createGroups.call(this, tree, tree, this._items, treeDefinition, treeDefinition.definitions);
|
|
163
|
+
|
|
164
|
+
map[treeDefinition.name] = tree;
|
|
183
165
|
|
|
184
|
-
|
|
166
|
+
return map;
|
|
167
|
+
}, { });
|
|
168
|
+
}
|
|
185
169
|
|
|
186
|
-
|
|
187
|
-
|
|
170
|
+
addPortfolio(portfolio) {
|
|
171
|
+
assert.argumentIsRequired(portfolio, 'portfolio', Object);
|
|
172
|
+
assert.argumentIsRequired(portfolio.portfolio, 'portfolio.portfolio', String);
|
|
173
|
+
assert.argumentIsRequired(portfolio.name, 'portfolio.name', String);
|
|
188
174
|
|
|
189
|
-
|
|
190
|
-
.map((key) => {
|
|
191
|
-
return levelDefinition.requiredGroups.find(g => g.key === key);
|
|
192
|
-
});
|
|
175
|
+
const key = portfolio.portfolio;
|
|
193
176
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
});
|
|
177
|
+
if (!this._portfolios.hasOwnProperty(key)) {
|
|
178
|
+
this._portfolios[key] = portfolio;
|
|
197
179
|
|
|
198
|
-
|
|
180
|
+
this._definitions.forEach((treeDefinition) => {
|
|
181
|
+
const tree = this._trees[treeDefinition.name];
|
|
182
|
+
const levelDefinitions = treeDefinition.definitions;
|
|
199
183
|
|
|
200
|
-
let
|
|
184
|
+
let portfolioRequiredGroup = null;
|
|
201
185
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
map[group.description] = index;
|
|
186
|
+
let portfolioLevelDefinition = null;
|
|
187
|
+
let portfolioLevelDefinitionIndex = null;
|
|
205
188
|
|
|
206
|
-
|
|
207
|
-
|
|
189
|
+
levelDefinitions.forEach((levelDefinition, i) => {
|
|
190
|
+
if (portfolioRequiredGroup === null) {
|
|
191
|
+
portfolioRequiredGroup = levelDefinition.generateRequiredGroup(portfolio);
|
|
208
192
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
} else {
|
|
213
|
-
return Number.MAX_VALUE;
|
|
193
|
+
if (portfolioRequiredGroup !== null) {
|
|
194
|
+
portfolioLevelDefinition = levelDefinition;
|
|
195
|
+
portfolioLevelDefinitionIndex = i;
|
|
214
196
|
}
|
|
215
|
-
}
|
|
197
|
+
}
|
|
198
|
+
});
|
|
216
199
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}).thenBy((a, b) => {
|
|
220
|
-
return comparators.compareStrings(a.description, b.description);
|
|
221
|
-
});
|
|
222
|
-
} else {
|
|
223
|
-
builder = ComparatorBuilder.startWith((a, b) => {
|
|
224
|
-
return comparators.compareStrings(a.description, b.description);
|
|
225
|
-
});
|
|
226
|
-
}
|
|
200
|
+
if (portfolioRequiredGroup !== null) {
|
|
201
|
+
let parentTrees = [ ];
|
|
227
202
|
|
|
228
|
-
|
|
203
|
+
if (portfolioLevelDefinitionIndex === 0) {
|
|
204
|
+
parentTrees.push(tree);
|
|
205
|
+
} else {
|
|
206
|
+
const parentLevelDefinition = levelDefinitions[ portfolioLevelDefinitionIndex - 1 ];
|
|
229
207
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
208
|
+
tree.walk((group, groupTree) => {
|
|
209
|
+
if (group.definition === parentLevelDefinition) {
|
|
210
|
+
parentTrees.push(groupTree);
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
235
214
|
|
|
236
|
-
|
|
237
|
-
if (childGroup.excluded) {
|
|
238
|
-
excludedItems = excludedItems.concat(childGroup.items);
|
|
239
|
-
}
|
|
240
|
-
}, false, false);
|
|
215
|
+
const overrideRequiredGroups = [ portfolioRequiredGroup ];
|
|
241
216
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
trees.push(this._trees[name]);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
return trees;
|
|
253
|
-
}, [ ]);
|
|
254
|
-
|
|
255
|
-
if (dependantTrees.length > 0) {
|
|
256
|
-
let excludedItems = [ ];
|
|
257
|
-
|
|
258
|
-
tree.walk((childGroup) => {
|
|
259
|
-
if (childGroup.excluded) {
|
|
260
|
-
excludedItems = excludedItems.concat(childGroup.items);
|
|
261
|
-
}
|
|
262
|
-
}, false, false);
|
|
263
|
-
|
|
264
|
-
dependantTrees.forEach((dependantTrees) => {
|
|
265
|
-
dependantTrees.walk((childGroup) => {
|
|
266
|
-
childGroup.setExcludedItems(excludedItems);
|
|
267
|
-
}, false, false);
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}));
|
|
272
|
-
};
|
|
217
|
+
parentTrees.forEach((t) => {
|
|
218
|
+
createGroups.call(this, tree, t, [ ], treeDefinition, levelDefinitions.slice(portfolioLevelDefinitionIndex), overrideRequiredGroups);
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
273
224
|
|
|
274
|
-
|
|
275
|
-
const childTree = currentTree.addChild(group);
|
|
225
|
+
removePortfolio(portfolio) {
|
|
276
226
|
|
|
277
|
-
|
|
227
|
+
}
|
|
278
228
|
|
|
279
|
-
|
|
280
|
-
currentTree.walk((childGroup) => childGroup.refreshMarketPercent());
|
|
281
|
-
}));
|
|
229
|
+
mutatePosition(position, summary) {
|
|
282
230
|
|
|
283
|
-
|
|
284
|
-
});
|
|
285
|
-
};
|
|
231
|
+
}
|
|
286
232
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
map[treeDefinition.name] = tree;
|
|
233
|
+
removePosition(position) {
|
|
290
234
|
|
|
291
|
-
return map;
|
|
292
|
-
}, { });
|
|
293
235
|
}
|
|
294
236
|
|
|
295
237
|
/**
|
|
@@ -523,5 +465,134 @@ module.exports = (() => {
|
|
|
523
465
|
}
|
|
524
466
|
}
|
|
525
467
|
|
|
468
|
+
function addGroupBinding(group, dispoable) {
|
|
469
|
+
const id = group.id;
|
|
470
|
+
|
|
471
|
+
if (!this._groupBindings.hasOwnProperty(id)) {
|
|
472
|
+
this._groupBindings[id] = new DisposableStack();
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
this._groupBindings[id].push(dispoable);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function createGroups(parentTree, currentTree, items, treeDefinition, levelDefinitions, overrideRequiredGroups) {
|
|
479
|
+
if (levelDefinitions.length === 0) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const parent = currentTree.getValue() || null;
|
|
484
|
+
|
|
485
|
+
const levelDefinition = levelDefinitions[0];
|
|
486
|
+
|
|
487
|
+
const populatedObjects = array.groupBy(items, levelDefinition.keySelector);
|
|
488
|
+
const populatedGroups = Object.keys(populatedObjects).reduce((list, key) => {
|
|
489
|
+
const items = populatedObjects[key];
|
|
490
|
+
const first = items[0];
|
|
491
|
+
|
|
492
|
+
list.push(new PositionGroup(this, parent, levelDefinition, items, levelDefinition.currencySelector(first), key, levelDefinition.descriptionSelector(first), levelDefinition.single && items.length === 1, levelDefinition.aggregateCash));
|
|
493
|
+
|
|
494
|
+
return list;
|
|
495
|
+
}, [ ]);
|
|
496
|
+
|
|
497
|
+
const requiredGroupsToUse = overrideRequiredGroups || levelDefinition.requiredGroups;
|
|
498
|
+
|
|
499
|
+
const missingGroups = array.difference(requiredGroupsToUse.map(group => group.key), populatedGroups.map(group => group.key))
|
|
500
|
+
.map((key) => {
|
|
501
|
+
return requiredGroupsToUse.find(g => g.key === key);
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
const empty = missingGroups.map((group) => {
|
|
505
|
+
return new PositionGroup(this, parent, levelDefinition, [ ], group.currency, group.key, group.description);
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
const compositeGroups = populatedGroups.concat(empty);
|
|
509
|
+
|
|
510
|
+
let builder;
|
|
511
|
+
|
|
512
|
+
if (requiredGroupsToUse.length !== 0) {
|
|
513
|
+
const ordering = requiredGroupsToUse.reduce((map, group, index) => {
|
|
514
|
+
map[group.description] = index;
|
|
515
|
+
|
|
516
|
+
return map;
|
|
517
|
+
}, { });
|
|
518
|
+
|
|
519
|
+
const getIndex = (description) => {
|
|
520
|
+
if (ordering.hasOwnProperty(description)) {
|
|
521
|
+
return ordering[description];
|
|
522
|
+
} else {
|
|
523
|
+
return Number.MAX_VALUE;
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
builder = ComparatorBuilder.startWith((a, b) => {
|
|
528
|
+
return comparators.compareNumbers(getIndex(a.description), getIndex(b.description));
|
|
529
|
+
}).thenBy((a, b) => {
|
|
530
|
+
return comparators.compareStrings(a.description, b.description);
|
|
531
|
+
});
|
|
532
|
+
} else {
|
|
533
|
+
builder = ComparatorBuilder.startWith((a, b) => {
|
|
534
|
+
return comparators.compareStrings(a.description, b.description);
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
compositeGroups.sort(builder.toComparator());
|
|
539
|
+
|
|
540
|
+
const initializeGroupObservers = (group, groupTree) => {
|
|
541
|
+
addGroupBinding.call(this, group, group.registerGroupExcludedChangeHandler((excluded, sender) => {
|
|
542
|
+
groupTree.climb((parentGroup) => {
|
|
543
|
+
if (parentGroup) {
|
|
544
|
+
let excludedItems = [];
|
|
545
|
+
|
|
546
|
+
currentTree.walk((childGroup) => {
|
|
547
|
+
if (childGroup.excluded) {
|
|
548
|
+
excludedItems = excludedItems.concat(childGroup.items);
|
|
549
|
+
}
|
|
550
|
+
}, false, false);
|
|
551
|
+
|
|
552
|
+
parentGroup.setExcludedItems(array.unique(excludedItems));
|
|
553
|
+
}
|
|
554
|
+
}, false);
|
|
555
|
+
|
|
556
|
+
if (treeDefinition.exclusionDependencies.length > 0) {
|
|
557
|
+
const dependantTrees = treeDefinition.exclusionDependencies.reduce((trees, name) => {
|
|
558
|
+
if (this._trees.hasOwnProperty(name)) {
|
|
559
|
+
trees.push(this._trees[name]);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
return trees;
|
|
563
|
+
}, [ ]);
|
|
564
|
+
|
|
565
|
+
if (dependantTrees.length > 0) {
|
|
566
|
+
let excludedItems = [ ];
|
|
567
|
+
|
|
568
|
+
parentTree.walk((childGroup) => {
|
|
569
|
+
if (childGroup.excluded) {
|
|
570
|
+
excludedItems = excludedItems.concat(childGroup.items);
|
|
571
|
+
}
|
|
572
|
+
}, false, false);
|
|
573
|
+
|
|
574
|
+
dependantTrees.forEach((dependantTrees) => {
|
|
575
|
+
dependantTrees.walk((childGroup) => {
|
|
576
|
+
childGroup.setExcludedItems(excludedItems);
|
|
577
|
+
}, false, false);
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}));
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
compositeGroups.forEach((group) => {
|
|
585
|
+
const childTree = currentTree.addChild(group);
|
|
586
|
+
|
|
587
|
+
initializeGroupObservers(group, childTree);
|
|
588
|
+
|
|
589
|
+
addGroupBinding.call(this, group, group.registerMarketPercentChangeHandler(() => {
|
|
590
|
+
currentTree.walk((childGroup) => childGroup.refreshMarketPercent());
|
|
591
|
+
}));
|
|
592
|
+
|
|
593
|
+
createGroups.call(this, parentTree, childTree, group.items, treeDefinition, array.dropLeft(levelDefinitions));
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
|
|
526
597
|
return PositionContainer;
|
|
527
598
|
})();
|
|
@@ -22,6 +22,7 @@ module.exports = (() => {
|
|
|
22
22
|
* @public
|
|
23
23
|
* @param {PositionContainer} container
|
|
24
24
|
* @param {PositionGroup|null} parent
|
|
25
|
+
* @param {LevelDefinition} definition
|
|
25
26
|
* @param {Array.<PositionItem>} items
|
|
26
27
|
* @param {Currency} currency
|
|
27
28
|
* @param {String} key
|
|
@@ -30,8 +31,10 @@ module.exports = (() => {
|
|
|
30
31
|
* @param {Boolean=} aggregateCash
|
|
31
32
|
*/
|
|
32
33
|
class PositionGroup {
|
|
33
|
-
constructor(container, parent, items, currency, key, description, single, aggregateCash) {
|
|
34
|
+
constructor(container, parent, definition, items, currency, key, description, single, aggregateCash) {
|
|
34
35
|
this._id = counter++;
|
|
36
|
+
|
|
37
|
+
this._definition = definition;
|
|
35
38
|
this._container = container;
|
|
36
39
|
this._parent = parent || null;
|
|
37
40
|
|
|
@@ -209,6 +212,16 @@ module.exports = (() => {
|
|
|
209
212
|
return this._id;
|
|
210
213
|
}
|
|
211
214
|
|
|
215
|
+
/**
|
|
216
|
+
* The {@link LevelDefinition} which was used to generate this group.
|
|
217
|
+
*
|
|
218
|
+
* @public
|
|
219
|
+
* @returns {LevelDefinition}
|
|
220
|
+
*/
|
|
221
|
+
get definition() {
|
|
222
|
+
return this._definition;
|
|
223
|
+
}
|
|
224
|
+
|
|
212
225
|
/**
|
|
213
226
|
* The key of the group.
|
|
214
227
|
*
|
|
@@ -294,6 +307,16 @@ module.exports = (() => {
|
|
|
294
307
|
return this._excluded;
|
|
295
308
|
}
|
|
296
309
|
|
|
310
|
+
addItems(items) {
|
|
311
|
+
|
|
312
|
+
this.refresh();
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
removeItems(items) {
|
|
316
|
+
|
|
317
|
+
this.refresh();
|
|
318
|
+
}
|
|
319
|
+
|
|
297
320
|
/**
|
|
298
321
|
* Sets the list of items which are excluded from group aggregation calculations.
|
|
299
322
|
*
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
const assert = require('@barchart/common-js/lang/assert'),
|
|
2
|
+
Currency = require('@barchart/common-js/lang/Currency'),
|
|
2
3
|
is = require('@barchart/common-js/lang/is');
|
|
3
4
|
|
|
5
|
+
const InstrumentType = require('./../../data/InstrumentType');
|
|
6
|
+
|
|
4
7
|
module.exports = (() => {
|
|
5
8
|
'use strict';
|
|
6
9
|
|
|
@@ -15,12 +18,12 @@ module.exports = (() => {
|
|
|
15
18
|
* @param {PositionLevelDefinition~descriptionSelector} descriptionSelector
|
|
16
19
|
* @param {PositionLevelDefinition~currencySelector} currencySelector
|
|
17
20
|
* @param {Array.<PositionLevelDefinition~RequiredGroup>=} requiredGroups
|
|
18
|
-
* @param {Array.<PositionLevelDefinition~RequiredGroup>=} requiredGroups
|
|
19
21
|
* @param {Boolean=} single
|
|
20
22
|
* @param {Boolean=} aggregateCash
|
|
23
|
+
* @param {Function=} injectPositions
|
|
21
24
|
*/
|
|
22
25
|
class PositionLevelDefinition {
|
|
23
|
-
constructor(name, keySelector, descriptionSelector, currencySelector, requiredGroups, single, aggregateCash) {
|
|
26
|
+
constructor(name, keySelector, descriptionSelector, currencySelector, requiredGroups, single, aggregateCash, requiredGroupGenerator) {
|
|
24
27
|
assert.argumentIsRequired(name, 'name', String);
|
|
25
28
|
assert.argumentIsRequired(keySelector, 'keySelector', Function);
|
|
26
29
|
assert.argumentIsRequired(descriptionSelector, 'descriptionSelector', Function);
|
|
@@ -32,6 +35,7 @@ module.exports = (() => {
|
|
|
32
35
|
|
|
33
36
|
assert.argumentIsOptional(single, 'single', Boolean);
|
|
34
37
|
assert.argumentIsOptional(aggregateCash, 'aggregateCash', Boolean);
|
|
38
|
+
assert.argumentIsOptional(requiredGroupGenerator, 'requiredGroupGenerator', Function);
|
|
35
39
|
|
|
36
40
|
this._name = name;
|
|
37
41
|
|
|
@@ -40,8 +44,11 @@ module.exports = (() => {
|
|
|
40
44
|
this._currencySelector = currencySelector;
|
|
41
45
|
|
|
42
46
|
this._requiredGroups = requiredGroups || [ ];
|
|
47
|
+
|
|
43
48
|
this._single = is.boolean(single) && single;
|
|
44
49
|
this._aggregateCash = is.boolean(aggregateCash) && aggregateCash;
|
|
50
|
+
|
|
51
|
+
this._requiredGroupGenerator = requiredGroupGenerator || (input => null);
|
|
45
52
|
}
|
|
46
53
|
|
|
47
54
|
/**
|
|
@@ -118,6 +125,96 @@ module.exports = (() => {
|
|
|
118
125
|
return this._aggregateCash;
|
|
119
126
|
}
|
|
120
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Given an input, potentially creates a new {@link PositionLevelDefinition~RequiredGroup}.
|
|
130
|
+
*
|
|
131
|
+
* @public
|
|
132
|
+
* @param {*} input
|
|
133
|
+
* @returns {PositionLevelDefinition~RequiredGroup|null}
|
|
134
|
+
*/
|
|
135
|
+
generateRequiredGroup(input) {
|
|
136
|
+
const requiredGroup = this._requiredGroupGenerator(input);
|
|
137
|
+
|
|
138
|
+
if (requiredGroup !== null) {
|
|
139
|
+
this._requiredGroups.push(requiredGroup);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return requiredGroup;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Builds a {@link PositionLevelDefinition~RequiredGroup} for a portfolio.
|
|
147
|
+
*
|
|
148
|
+
* @public
|
|
149
|
+
* @static
|
|
150
|
+
* @param {Object} portfolio
|
|
151
|
+
* @return {PositionLevelDefinition~RequiredGroup}
|
|
152
|
+
*/
|
|
153
|
+
static buildRequiredGroupForPortfolio(portfolio) {
|
|
154
|
+
return {
|
|
155
|
+
key: PositionLevelDefinition.getKeyForPortfolioGroup(portfolio),
|
|
156
|
+
description: PositionLevelDefinition.getDescriptionForPortfolioGroup(portfolio),
|
|
157
|
+
currency: Currency.CAD
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
static getKeyForPortfolioGroup(portfolio) {
|
|
162
|
+
assert.argumentIsRequired(portfolio, 'portfolio', Object);
|
|
163
|
+
|
|
164
|
+
return portfolio.portfolio;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
static getDescriptionForPortfolioGroup(portfolio) {
|
|
168
|
+
assert.argumentIsRequired(portfolio, 'portfolio', Object);
|
|
169
|
+
|
|
170
|
+
return portfolio.name;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
static getRequiredGroupGeneratorForPortfolio() {
|
|
174
|
+
return (portfolio) => {
|
|
175
|
+
let requiredGroup;
|
|
176
|
+
|
|
177
|
+
if (is.object(portfolio) && is.string(portfolio.portfolio) && is.string(portfolio.name)) {
|
|
178
|
+
requiredGroup = PositionLevelDefinition.buildRequiredGroupForPortfolio(portfolio);
|
|
179
|
+
} else {
|
|
180
|
+
requiredGroup = null;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return requiredGroup;
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Builds a {@link PositionLevelDefinition~RequiredGroup} for an asset class.
|
|
189
|
+
*
|
|
190
|
+
* @public
|
|
191
|
+
* @static
|
|
192
|
+
* @param {InstrumentType} type
|
|
193
|
+
* @param {Currency} currency
|
|
194
|
+
* @return {PositionLevelDefinition~RequiredGroup}
|
|
195
|
+
*/
|
|
196
|
+
static buildRequiredGroupForAssetClass(type, currency) {
|
|
197
|
+
return {
|
|
198
|
+
key: PositionLevelDefinition.getKeyForAssetClassGroup(type, currency),
|
|
199
|
+
description: PositionLevelDefinition.getDescriptionForAssetClassGroup(type, currency),
|
|
200
|
+
currency: currency
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
static getKeyForAssetClassGroup(type, currency) {
|
|
205
|
+
assert.argumentIsRequired(type, 'type', InstrumentType, 'InstrumentType');
|
|
206
|
+
assert.argumentIsRequired(currency, 'currency', Currency, 'Currency');
|
|
207
|
+
|
|
208
|
+
return `${type.code}|${currency.code}`;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
static getDescriptionForAssetClassGroup(type, currency) {
|
|
212
|
+
assert.argumentIsRequired(type, 'type', InstrumentType, 'InstrumentType');
|
|
213
|
+
assert.argumentIsRequired(currency, 'currency', Currency, 'Currency');
|
|
214
|
+
|
|
215
|
+
return `${type.alternateDescription}${currency.code === 'CAD' ? '' : ` (${currency.alternateDescription})`}`;
|
|
216
|
+
}
|
|
217
|
+
|
|
121
218
|
toString() {
|
|
122
219
|
return '[PositionLevelDefinition]';
|
|
123
220
|
}
|
package/package.json
CHANGED
package/test/SpecRunner.js
CHANGED
|
@@ -764,17 +764,9 @@ module.exports = (() => {
|
|
|
764
764
|
const currentSummaryFrame = PositionSummaryFrame.YTD;
|
|
765
765
|
const currentSummaryRange = array.last(currentSummaryFrame.getRecentRanges(0));
|
|
766
766
|
|
|
767
|
-
this.
|
|
768
|
-
|
|
769
|
-
const addGroupBinding = (group, dispoable) => {
|
|
770
|
-
const id = group.id;
|
|
771
|
-
|
|
772
|
-
if (!this._groupBindings.hasOwnProperty(id)) {
|
|
773
|
-
this._groupBindings[id] = new DisposableStack();
|
|
774
|
-
}
|
|
767
|
+
this._definitions = definitions;
|
|
775
768
|
|
|
776
|
-
|
|
777
|
-
};
|
|
769
|
+
this._groupBindings = { };
|
|
778
770
|
|
|
779
771
|
this._portfolios = portfolios.reduce((map, portfolio) => {
|
|
780
772
|
map[portfolio.portfolio] = portfolio;
|
|
@@ -880,132 +872,82 @@ module.exports = (() => {
|
|
|
880
872
|
return Rate.fromPair(Decimal.ONE, symbol);
|
|
881
873
|
});
|
|
882
874
|
|
|
883
|
-
this._trees =
|
|
875
|
+
this._trees = this._definitions.reduce((map, treeDefinition) => {
|
|
884
876
|
const tree = new Tree();
|
|
885
877
|
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
}
|
|
878
|
+
createGroups.call(this, tree, tree, this._items, treeDefinition, treeDefinition.definitions);
|
|
879
|
+
|
|
880
|
+
map[treeDefinition.name] = tree;
|
|
890
881
|
|
|
891
|
-
|
|
882
|
+
return map;
|
|
883
|
+
}, { });
|
|
884
|
+
}
|
|
892
885
|
|
|
893
|
-
|
|
886
|
+
addPortfolio(portfolio) {
|
|
887
|
+
assert.argumentIsRequired(portfolio, 'portfolio', Object);
|
|
888
|
+
assert.argumentIsRequired(portfolio.portfolio, 'portfolio.portfolio', String);
|
|
889
|
+
assert.argumentIsRequired(portfolio.name, 'portfolio.name', String);
|
|
894
890
|
|
|
895
|
-
|
|
896
|
-
const populatedGroups = Object.keys(populatedObjects).reduce((list, key) => {
|
|
897
|
-
const items = populatedObjects[key];
|
|
898
|
-
const first = items[0];
|
|
891
|
+
const key = portfolio.portfolio;
|
|
899
892
|
|
|
900
|
-
|
|
893
|
+
if (!this._portfolios.hasOwnProperty(key)) {
|
|
894
|
+
this._portfolios[key] = portfolio;
|
|
901
895
|
|
|
902
|
-
|
|
903
|
-
|
|
896
|
+
this._definitions.forEach((treeDefinition) => {
|
|
897
|
+
const tree = this._trees[treeDefinition.name];
|
|
898
|
+
const levelDefinitions = treeDefinition.definitions;
|
|
904
899
|
|
|
905
|
-
|
|
906
|
-
.map((key) => {
|
|
907
|
-
return levelDefinition.requiredGroups.find(g => g.key === key);
|
|
908
|
-
});
|
|
900
|
+
let portfolioRequiredGroup = null;
|
|
909
901
|
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
});
|
|
902
|
+
let portfolioLevelDefinition = null;
|
|
903
|
+
let portfolioLevelDefinitionIndex = null;
|
|
913
904
|
|
|
914
|
-
|
|
905
|
+
levelDefinitions.forEach((levelDefinition, i) => {
|
|
906
|
+
if (portfolioRequiredGroup === null) {
|
|
907
|
+
portfolioRequiredGroup = levelDefinition.generateRequiredGroup(portfolio);
|
|
915
908
|
|
|
916
|
-
|
|
909
|
+
if (portfolioRequiredGroup !== null) {
|
|
910
|
+
portfolioLevelDefinition = levelDefinition;
|
|
911
|
+
portfolioLevelDefinitionIndex = i;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
});
|
|
917
915
|
|
|
918
|
-
if (
|
|
919
|
-
|
|
920
|
-
map[group.description] = index;
|
|
916
|
+
if (portfolioRequiredGroup !== null) {
|
|
917
|
+
let parentTrees = [ ];
|
|
921
918
|
|
|
922
|
-
|
|
923
|
-
|
|
919
|
+
if (portfolioLevelDefinitionIndex === 0) {
|
|
920
|
+
parentTrees.push(tree);
|
|
921
|
+
} else {
|
|
922
|
+
const parentLevelDefinition = levelDefinitions[ portfolioLevelDefinitionIndex - 1 ];
|
|
924
923
|
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
};
|
|
924
|
+
tree.walk((group, groupTree) => {
|
|
925
|
+
if (group.definition === parentLevelDefinition) {
|
|
926
|
+
parentTrees.push(groupTree);
|
|
927
|
+
}
|
|
928
|
+
});
|
|
929
|
+
}
|
|
932
930
|
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
});
|
|
938
|
-
} else {
|
|
939
|
-
builder = ComparatorBuilder.startWith((a, b) => {
|
|
940
|
-
return comparators.compareStrings(a.description, b.description);
|
|
931
|
+
const overrideRequiredGroups = [ portfolioRequiredGroup ];
|
|
932
|
+
|
|
933
|
+
parentTrees.forEach((t) => {
|
|
934
|
+
createGroups.call(this, tree, t, [ ], treeDefinition, levelDefinitions.slice(portfolioLevelDefinitionIndex), overrideRequiredGroups);
|
|
941
935
|
});
|
|
942
936
|
}
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
}
|
|
943
940
|
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
const initializeGroupObservers = (group, groupTree) => {
|
|
947
|
-
addGroupBinding(group, group.registerGroupExcludedChangeHandler((excluded, sender) => {
|
|
948
|
-
groupTree.climb((parentGroup) => {
|
|
949
|
-
if (parentGroup) {
|
|
950
|
-
let excludedItems = [];
|
|
951
|
-
|
|
952
|
-
currentTree.walk((childGroup) => {
|
|
953
|
-
if (childGroup.excluded) {
|
|
954
|
-
excludedItems = excludedItems.concat(childGroup.items);
|
|
955
|
-
}
|
|
956
|
-
}, false, false);
|
|
957
|
-
|
|
958
|
-
parentGroup.setExcludedItems(array.unique(excludedItems));
|
|
959
|
-
}
|
|
960
|
-
}, false);
|
|
961
|
-
|
|
962
|
-
if (treeDefinition.exclusionDependencies.length > 0) {
|
|
963
|
-
const dependantTrees = treeDefinition.exclusionDependencies.reduce((trees, name) => {
|
|
964
|
-
if (this._trees.hasOwnProperty(name)) {
|
|
965
|
-
trees.push(this._trees[name]);
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
return trees;
|
|
969
|
-
}, [ ]);
|
|
970
|
-
|
|
971
|
-
if (dependantTrees.length > 0) {
|
|
972
|
-
let excludedItems = [ ];
|
|
973
|
-
|
|
974
|
-
tree.walk((childGroup) => {
|
|
975
|
-
if (childGroup.excluded) {
|
|
976
|
-
excludedItems = excludedItems.concat(childGroup.items);
|
|
977
|
-
}
|
|
978
|
-
}, false, false);
|
|
979
|
-
|
|
980
|
-
dependantTrees.forEach((dependantTrees) => {
|
|
981
|
-
dependantTrees.walk((childGroup) => {
|
|
982
|
-
childGroup.setExcludedItems(excludedItems);
|
|
983
|
-
}, false, false);
|
|
984
|
-
});
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
}));
|
|
988
|
-
};
|
|
941
|
+
removePortfolio(portfolio) {
|
|
989
942
|
|
|
990
|
-
|
|
991
|
-
const childTree = currentTree.addChild(group);
|
|
943
|
+
}
|
|
992
944
|
|
|
993
|
-
|
|
945
|
+
mutatePosition(position, summary) {
|
|
994
946
|
|
|
995
|
-
|
|
996
|
-
currentTree.walk((childGroup) => childGroup.refreshMarketPercent());
|
|
997
|
-
}));
|
|
947
|
+
}
|
|
998
948
|
|
|
999
|
-
|
|
1000
|
-
});
|
|
1001
|
-
};
|
|
949
|
+
removePosition(position) {
|
|
1002
950
|
|
|
1003
|
-
createGroups(tree, this._items, treeDefinition.definitions);
|
|
1004
|
-
|
|
1005
|
-
map[treeDefinition.name] = tree;
|
|
1006
|
-
|
|
1007
|
-
return map;
|
|
1008
|
-
}, { });
|
|
1009
951
|
}
|
|
1010
952
|
|
|
1011
953
|
/**
|
|
@@ -1239,6 +1181,135 @@ module.exports = (() => {
|
|
|
1239
1181
|
}
|
|
1240
1182
|
}
|
|
1241
1183
|
|
|
1184
|
+
function addGroupBinding(group, dispoable) {
|
|
1185
|
+
const id = group.id;
|
|
1186
|
+
|
|
1187
|
+
if (!this._groupBindings.hasOwnProperty(id)) {
|
|
1188
|
+
this._groupBindings[id] = new DisposableStack();
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
this._groupBindings[id].push(dispoable);
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
function createGroups(parentTree, currentTree, items, treeDefinition, levelDefinitions, overrideRequiredGroups) {
|
|
1195
|
+
if (levelDefinitions.length === 0) {
|
|
1196
|
+
return;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
const parent = currentTree.getValue() || null;
|
|
1200
|
+
|
|
1201
|
+
const levelDefinition = levelDefinitions[0];
|
|
1202
|
+
|
|
1203
|
+
const populatedObjects = array.groupBy(items, levelDefinition.keySelector);
|
|
1204
|
+
const populatedGroups = Object.keys(populatedObjects).reduce((list, key) => {
|
|
1205
|
+
const items = populatedObjects[key];
|
|
1206
|
+
const first = items[0];
|
|
1207
|
+
|
|
1208
|
+
list.push(new PositionGroup(this, parent, levelDefinition, items, levelDefinition.currencySelector(first), key, levelDefinition.descriptionSelector(first), levelDefinition.single && items.length === 1, levelDefinition.aggregateCash));
|
|
1209
|
+
|
|
1210
|
+
return list;
|
|
1211
|
+
}, [ ]);
|
|
1212
|
+
|
|
1213
|
+
const requiredGroupsToUse = overrideRequiredGroups || levelDefinition.requiredGroups;
|
|
1214
|
+
|
|
1215
|
+
const missingGroups = array.difference(requiredGroupsToUse.map(group => group.key), populatedGroups.map(group => group.key))
|
|
1216
|
+
.map((key) => {
|
|
1217
|
+
return requiredGroupsToUse.find(g => g.key === key);
|
|
1218
|
+
});
|
|
1219
|
+
|
|
1220
|
+
const empty = missingGroups.map((group) => {
|
|
1221
|
+
return new PositionGroup(this, parent, levelDefinition, [ ], group.currency, group.key, group.description);
|
|
1222
|
+
});
|
|
1223
|
+
|
|
1224
|
+
const compositeGroups = populatedGroups.concat(empty);
|
|
1225
|
+
|
|
1226
|
+
let builder;
|
|
1227
|
+
|
|
1228
|
+
if (requiredGroupsToUse.length !== 0) {
|
|
1229
|
+
const ordering = requiredGroupsToUse.reduce((map, group, index) => {
|
|
1230
|
+
map[group.description] = index;
|
|
1231
|
+
|
|
1232
|
+
return map;
|
|
1233
|
+
}, { });
|
|
1234
|
+
|
|
1235
|
+
const getIndex = (description) => {
|
|
1236
|
+
if (ordering.hasOwnProperty(description)) {
|
|
1237
|
+
return ordering[description];
|
|
1238
|
+
} else {
|
|
1239
|
+
return Number.MAX_VALUE;
|
|
1240
|
+
}
|
|
1241
|
+
};
|
|
1242
|
+
|
|
1243
|
+
builder = ComparatorBuilder.startWith((a, b) => {
|
|
1244
|
+
return comparators.compareNumbers(getIndex(a.description), getIndex(b.description));
|
|
1245
|
+
}).thenBy((a, b) => {
|
|
1246
|
+
return comparators.compareStrings(a.description, b.description);
|
|
1247
|
+
});
|
|
1248
|
+
} else {
|
|
1249
|
+
builder = ComparatorBuilder.startWith((a, b) => {
|
|
1250
|
+
return comparators.compareStrings(a.description, b.description);
|
|
1251
|
+
});
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
compositeGroups.sort(builder.toComparator());
|
|
1255
|
+
|
|
1256
|
+
const initializeGroupObservers = (group, groupTree) => {
|
|
1257
|
+
addGroupBinding.call(this, group, group.registerGroupExcludedChangeHandler((excluded, sender) => {
|
|
1258
|
+
groupTree.climb((parentGroup) => {
|
|
1259
|
+
if (parentGroup) {
|
|
1260
|
+
let excludedItems = [];
|
|
1261
|
+
|
|
1262
|
+
currentTree.walk((childGroup) => {
|
|
1263
|
+
if (childGroup.excluded) {
|
|
1264
|
+
excludedItems = excludedItems.concat(childGroup.items);
|
|
1265
|
+
}
|
|
1266
|
+
}, false, false);
|
|
1267
|
+
|
|
1268
|
+
parentGroup.setExcludedItems(array.unique(excludedItems));
|
|
1269
|
+
}
|
|
1270
|
+
}, false);
|
|
1271
|
+
|
|
1272
|
+
if (treeDefinition.exclusionDependencies.length > 0) {
|
|
1273
|
+
const dependantTrees = treeDefinition.exclusionDependencies.reduce((trees, name) => {
|
|
1274
|
+
if (this._trees.hasOwnProperty(name)) {
|
|
1275
|
+
trees.push(this._trees[name]);
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
return trees;
|
|
1279
|
+
}, [ ]);
|
|
1280
|
+
|
|
1281
|
+
if (dependantTrees.length > 0) {
|
|
1282
|
+
let excludedItems = [ ];
|
|
1283
|
+
|
|
1284
|
+
parentTree.walk((childGroup) => {
|
|
1285
|
+
if (childGroup.excluded) {
|
|
1286
|
+
excludedItems = excludedItems.concat(childGroup.items);
|
|
1287
|
+
}
|
|
1288
|
+
}, false, false);
|
|
1289
|
+
|
|
1290
|
+
dependantTrees.forEach((dependantTrees) => {
|
|
1291
|
+
dependantTrees.walk((childGroup) => {
|
|
1292
|
+
childGroup.setExcludedItems(excludedItems);
|
|
1293
|
+
}, false, false);
|
|
1294
|
+
});
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
}));
|
|
1298
|
+
};
|
|
1299
|
+
|
|
1300
|
+
compositeGroups.forEach((group) => {
|
|
1301
|
+
const childTree = currentTree.addChild(group);
|
|
1302
|
+
|
|
1303
|
+
initializeGroupObservers(group, childTree);
|
|
1304
|
+
|
|
1305
|
+
addGroupBinding.call(this, group, group.registerMarketPercentChangeHandler(() => {
|
|
1306
|
+
currentTree.walk((childGroup) => childGroup.refreshMarketPercent());
|
|
1307
|
+
}));
|
|
1308
|
+
|
|
1309
|
+
createGroups.call(this, parentTree, childTree, group.items, treeDefinition, array.dropLeft(levelDefinitions));
|
|
1310
|
+
});
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1242
1313
|
return PositionContainer;
|
|
1243
1314
|
})();
|
|
1244
1315
|
|
|
@@ -1267,6 +1338,7 @@ module.exports = (() => {
|
|
|
1267
1338
|
* @public
|
|
1268
1339
|
* @param {PositionContainer} container
|
|
1269
1340
|
* @param {PositionGroup|null} parent
|
|
1341
|
+
* @param {LevelDefinition} definition
|
|
1270
1342
|
* @param {Array.<PositionItem>} items
|
|
1271
1343
|
* @param {Currency} currency
|
|
1272
1344
|
* @param {String} key
|
|
@@ -1275,8 +1347,10 @@ module.exports = (() => {
|
|
|
1275
1347
|
* @param {Boolean=} aggregateCash
|
|
1276
1348
|
*/
|
|
1277
1349
|
class PositionGroup {
|
|
1278
|
-
constructor(container, parent, items, currency, key, description, single, aggregateCash) {
|
|
1350
|
+
constructor(container, parent, definition, items, currency, key, description, single, aggregateCash) {
|
|
1279
1351
|
this._id = counter++;
|
|
1352
|
+
|
|
1353
|
+
this._definition = definition;
|
|
1280
1354
|
this._container = container;
|
|
1281
1355
|
this._parent = parent || null;
|
|
1282
1356
|
|
|
@@ -1454,6 +1528,16 @@ module.exports = (() => {
|
|
|
1454
1528
|
return this._id;
|
|
1455
1529
|
}
|
|
1456
1530
|
|
|
1531
|
+
/**
|
|
1532
|
+
* The {@link LevelDefinition} which was used to generate this group.
|
|
1533
|
+
*
|
|
1534
|
+
* @public
|
|
1535
|
+
* @returns {LevelDefinition}
|
|
1536
|
+
*/
|
|
1537
|
+
get definition() {
|
|
1538
|
+
return this._definition;
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1457
1541
|
/**
|
|
1458
1542
|
* The key of the group.
|
|
1459
1543
|
*
|
|
@@ -1539,6 +1623,16 @@ module.exports = (() => {
|
|
|
1539
1623
|
return this._excluded;
|
|
1540
1624
|
}
|
|
1541
1625
|
|
|
1626
|
+
addItems(items) {
|
|
1627
|
+
|
|
1628
|
+
this.refresh();
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
removeItems(items) {
|
|
1632
|
+
|
|
1633
|
+
this.refresh();
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1542
1636
|
/**
|
|
1543
1637
|
* Sets the list of items which are excluded from group aggregation calculations.
|
|
1544
1638
|
*
|
|
@@ -2295,8 +2389,11 @@ module.exports = (() => {
|
|
|
2295
2389
|
|
|
2296
2390
|
},{"./../data/InstrumentType":1,"@barchart/common-js/lang/Currency":14,"@barchart/common-js/lang/Decimal":16,"@barchart/common-js/lang/array":20,"@barchart/common-js/lang/assert":21,"@barchart/common-js/lang/is":23,"@barchart/common-js/messaging/Event":25}],7:[function(require,module,exports){
|
|
2297
2391
|
const assert = require('@barchart/common-js/lang/assert'),
|
|
2392
|
+
Currency = require('@barchart/common-js/lang/Currency'),
|
|
2298
2393
|
is = require('@barchart/common-js/lang/is');
|
|
2299
2394
|
|
|
2395
|
+
const InstrumentType = require('./../../data/InstrumentType');
|
|
2396
|
+
|
|
2300
2397
|
module.exports = (() => {
|
|
2301
2398
|
'use strict';
|
|
2302
2399
|
|
|
@@ -2311,12 +2408,12 @@ module.exports = (() => {
|
|
|
2311
2408
|
* @param {PositionLevelDefinition~descriptionSelector} descriptionSelector
|
|
2312
2409
|
* @param {PositionLevelDefinition~currencySelector} currencySelector
|
|
2313
2410
|
* @param {Array.<PositionLevelDefinition~RequiredGroup>=} requiredGroups
|
|
2314
|
-
* @param {Array.<PositionLevelDefinition~RequiredGroup>=} requiredGroups
|
|
2315
2411
|
* @param {Boolean=} single
|
|
2316
2412
|
* @param {Boolean=} aggregateCash
|
|
2413
|
+
* @param {Function=} injectPositions
|
|
2317
2414
|
*/
|
|
2318
2415
|
class PositionLevelDefinition {
|
|
2319
|
-
constructor(name, keySelector, descriptionSelector, currencySelector, requiredGroups, single, aggregateCash) {
|
|
2416
|
+
constructor(name, keySelector, descriptionSelector, currencySelector, requiredGroups, single, aggregateCash, requiredGroupGenerator) {
|
|
2320
2417
|
assert.argumentIsRequired(name, 'name', String);
|
|
2321
2418
|
assert.argumentIsRequired(keySelector, 'keySelector', Function);
|
|
2322
2419
|
assert.argumentIsRequired(descriptionSelector, 'descriptionSelector', Function);
|
|
@@ -2328,6 +2425,7 @@ module.exports = (() => {
|
|
|
2328
2425
|
|
|
2329
2426
|
assert.argumentIsOptional(single, 'single', Boolean);
|
|
2330
2427
|
assert.argumentIsOptional(aggregateCash, 'aggregateCash', Boolean);
|
|
2428
|
+
assert.argumentIsOptional(requiredGroupGenerator, 'requiredGroupGenerator', Function);
|
|
2331
2429
|
|
|
2332
2430
|
this._name = name;
|
|
2333
2431
|
|
|
@@ -2336,8 +2434,11 @@ module.exports = (() => {
|
|
|
2336
2434
|
this._currencySelector = currencySelector;
|
|
2337
2435
|
|
|
2338
2436
|
this._requiredGroups = requiredGroups || [ ];
|
|
2437
|
+
|
|
2339
2438
|
this._single = is.boolean(single) && single;
|
|
2340
2439
|
this._aggregateCash = is.boolean(aggregateCash) && aggregateCash;
|
|
2440
|
+
|
|
2441
|
+
this._requiredGroupGenerator = requiredGroupGenerator || (input => null);
|
|
2341
2442
|
}
|
|
2342
2443
|
|
|
2343
2444
|
/**
|
|
@@ -2414,6 +2515,96 @@ module.exports = (() => {
|
|
|
2414
2515
|
return this._aggregateCash;
|
|
2415
2516
|
}
|
|
2416
2517
|
|
|
2518
|
+
/**
|
|
2519
|
+
* Given an input, potentially creates a new {@link PositionLevelDefinition~RequiredGroup}.
|
|
2520
|
+
*
|
|
2521
|
+
* @public
|
|
2522
|
+
* @param {*} input
|
|
2523
|
+
* @returns {PositionLevelDefinition~RequiredGroup|null}
|
|
2524
|
+
*/
|
|
2525
|
+
generateRequiredGroup(input) {
|
|
2526
|
+
const requiredGroup = this._requiredGroupGenerator(input);
|
|
2527
|
+
|
|
2528
|
+
if (requiredGroup !== null) {
|
|
2529
|
+
this._requiredGroups.push(requiredGroup);
|
|
2530
|
+
}
|
|
2531
|
+
|
|
2532
|
+
return requiredGroup;
|
|
2533
|
+
}
|
|
2534
|
+
|
|
2535
|
+
/**
|
|
2536
|
+
* Builds a {@link PositionLevelDefinition~RequiredGroup} for a portfolio.
|
|
2537
|
+
*
|
|
2538
|
+
* @public
|
|
2539
|
+
* @static
|
|
2540
|
+
* @param {Object} portfolio
|
|
2541
|
+
* @return {PositionLevelDefinition~RequiredGroup}
|
|
2542
|
+
*/
|
|
2543
|
+
static buildRequiredGroupForPortfolio(portfolio) {
|
|
2544
|
+
return {
|
|
2545
|
+
key: PositionLevelDefinition.getKeyForPortfolioGroup(portfolio),
|
|
2546
|
+
description: PositionLevelDefinition.getDescriptionForPortfolioGroup(portfolio),
|
|
2547
|
+
currency: Currency.CAD
|
|
2548
|
+
};
|
|
2549
|
+
}
|
|
2550
|
+
|
|
2551
|
+
static getKeyForPortfolioGroup(portfolio) {
|
|
2552
|
+
assert.argumentIsRequired(portfolio, 'portfolio', Object);
|
|
2553
|
+
|
|
2554
|
+
return portfolio.portfolio;
|
|
2555
|
+
}
|
|
2556
|
+
|
|
2557
|
+
static getDescriptionForPortfolioGroup(portfolio) {
|
|
2558
|
+
assert.argumentIsRequired(portfolio, 'portfolio', Object);
|
|
2559
|
+
|
|
2560
|
+
return portfolio.name;
|
|
2561
|
+
}
|
|
2562
|
+
|
|
2563
|
+
static getRequiredGroupGeneratorForPortfolio() {
|
|
2564
|
+
return (portfolio) => {
|
|
2565
|
+
let requiredGroup;
|
|
2566
|
+
|
|
2567
|
+
if (is.object(portfolio) && is.string(portfolio.portfolio) && is.string(portfolio.name)) {
|
|
2568
|
+
requiredGroup = PositionLevelDefinition.buildRequiredGroupForPortfolio(portfolio);
|
|
2569
|
+
} else {
|
|
2570
|
+
requiredGroup = null;
|
|
2571
|
+
}
|
|
2572
|
+
|
|
2573
|
+
return requiredGroup;
|
|
2574
|
+
};
|
|
2575
|
+
}
|
|
2576
|
+
|
|
2577
|
+
/**
|
|
2578
|
+
* Builds a {@link PositionLevelDefinition~RequiredGroup} for an asset class.
|
|
2579
|
+
*
|
|
2580
|
+
* @public
|
|
2581
|
+
* @static
|
|
2582
|
+
* @param {InstrumentType} type
|
|
2583
|
+
* @param {Currency} currency
|
|
2584
|
+
* @return {PositionLevelDefinition~RequiredGroup}
|
|
2585
|
+
*/
|
|
2586
|
+
static buildRequiredGroupForAssetClass(type, currency) {
|
|
2587
|
+
return {
|
|
2588
|
+
key: PositionLevelDefinition.getKeyForAssetClassGroup(type, currency),
|
|
2589
|
+
description: PositionLevelDefinition.getDescriptionForAssetClassGroup(type, currency),
|
|
2590
|
+
currency: currency
|
|
2591
|
+
};
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2594
|
+
static getKeyForAssetClassGroup(type, currency) {
|
|
2595
|
+
assert.argumentIsRequired(type, 'type', InstrumentType, 'InstrumentType');
|
|
2596
|
+
assert.argumentIsRequired(currency, 'currency', Currency, 'Currency');
|
|
2597
|
+
|
|
2598
|
+
return `${type.code}|${currency.code}`;
|
|
2599
|
+
}
|
|
2600
|
+
|
|
2601
|
+
static getDescriptionForAssetClassGroup(type, currency) {
|
|
2602
|
+
assert.argumentIsRequired(type, 'type', InstrumentType, 'InstrumentType');
|
|
2603
|
+
assert.argumentIsRequired(currency, 'currency', Currency, 'Currency');
|
|
2604
|
+
|
|
2605
|
+
return `${type.alternateDescription}${currency.code === 'CAD' ? '' : ` (${currency.alternateDescription})`}`;
|
|
2606
|
+
}
|
|
2607
|
+
|
|
2417
2608
|
toString() {
|
|
2418
2609
|
return '[PositionLevelDefinition]';
|
|
2419
2610
|
}
|
|
@@ -2463,7 +2654,7 @@ module.exports = (() => {
|
|
|
2463
2654
|
return PositionLevelDefinition;
|
|
2464
2655
|
})();
|
|
2465
2656
|
|
|
2466
|
-
},{"@barchart/common-js/lang/assert":21,"@barchart/common-js/lang/is":23}],8:[function(require,module,exports){
|
|
2657
|
+
},{"./../../data/InstrumentType":1,"@barchart/common-js/lang/Currency":14,"@barchart/common-js/lang/assert":21,"@barchart/common-js/lang/is":23}],8:[function(require,module,exports){
|
|
2467
2658
|
const assert = require('@barchart/common-js/lang/assert');
|
|
2468
2659
|
|
|
2469
2660
|
const PositionLevelDefinition = require('./PositionLevelDefinition');
|
|
@@ -2509,7 +2700,7 @@ module.exports = (() => {
|
|
|
2509
2700
|
* bottom-most level of the tree (i.e. leaf nodes).
|
|
2510
2701
|
*
|
|
2511
2702
|
* @public
|
|
2512
|
-
* @returns {Array.<
|
|
2703
|
+
* @returns {Array.<PositionLevelDefinitions>}
|
|
2513
2704
|
*/
|
|
2514
2705
|
get definitions() {
|
|
2515
2706
|
return this._definitions;
|