@barchart/portfolio-api-common 1.0.162 → 1.0.163
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.
|
@@ -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,127 +156,10 @@ 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
|
-
if (levelDefinitions.length === 0) {
|
|
172
|
-
return;
|
|
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];
|
|
183
|
-
|
|
184
|
-
list.push(new PositionGroup(this, parent, items, levelDefinition.currencySelector(first), key, levelDefinition.descriptionSelector(first), levelDefinition.single && items.length === 1, levelDefinition.aggregateCash));
|
|
185
|
-
|
|
186
|
-
return list;
|
|
187
|
-
}, [ ]);
|
|
188
|
-
|
|
189
|
-
const missingGroups = array.difference(levelDefinition.requiredGroups.map(group => group.key), populatedGroups.map(group => group.key))
|
|
190
|
-
.map((key) => {
|
|
191
|
-
return levelDefinition.requiredGroups.find(g => g.key === key);
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
const empty = missingGroups.map((group) => {
|
|
195
|
-
return new PositionGroup(this, parent, [ ], group.currency, group.key, group.description);
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
const compositeGroups = populatedGroups.concat(empty);
|
|
199
|
-
|
|
200
|
-
let builder;
|
|
201
|
-
|
|
202
|
-
if (levelDefinition.requiredGroups.length !== 0) {
|
|
203
|
-
const ordering = levelDefinition.requiredGroups.reduce((map, group, index) => {
|
|
204
|
-
map[group.description] = index;
|
|
205
|
-
|
|
206
|
-
return map;
|
|
207
|
-
}, { });
|
|
208
|
-
|
|
209
|
-
const getIndex = (description) => {
|
|
210
|
-
if (ordering.hasOwnProperty(description)) {
|
|
211
|
-
return ordering[description];
|
|
212
|
-
} else {
|
|
213
|
-
return Number.MAX_VALUE;
|
|
214
|
-
}
|
|
215
|
-
};
|
|
216
|
-
|
|
217
|
-
builder = ComparatorBuilder.startWith((a, b) => {
|
|
218
|
-
return comparators.compareNumbers(getIndex(a.description), getIndex(b.description));
|
|
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
|
-
}
|
|
227
|
-
|
|
228
|
-
compositeGroups.sort(builder.toComparator());
|
|
229
|
-
|
|
230
|
-
const initializeGroupObservers = (group, groupTree) => {
|
|
231
|
-
addGroupBinding(group, group.registerGroupExcludedChangeHandler((excluded, sender) => {
|
|
232
|
-
groupTree.climb((parentGroup) => {
|
|
233
|
-
if (parentGroup) {
|
|
234
|
-
let excludedItems = [];
|
|
235
|
-
|
|
236
|
-
currentTree.walk((childGroup) => {
|
|
237
|
-
if (childGroup.excluded) {
|
|
238
|
-
excludedItems = excludedItems.concat(childGroup.items);
|
|
239
|
-
}
|
|
240
|
-
}, false, false);
|
|
241
|
-
|
|
242
|
-
parentGroup.setExcludedItems(array.unique(excludedItems));
|
|
243
|
-
}
|
|
244
|
-
}, false);
|
|
245
|
-
|
|
246
|
-
if (treeDefinition.exclusionDependencies.length > 0) {
|
|
247
|
-
const dependantTrees = treeDefinition.exclusionDependencies.reduce((trees, name) => {
|
|
248
|
-
if (this._trees.hasOwnProperty(name)) {
|
|
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
|
-
};
|
|
273
|
-
|
|
274
|
-
compositeGroups.forEach((group) => {
|
|
275
|
-
const childTree = currentTree.addChild(group);
|
|
276
|
-
|
|
277
|
-
initializeGroupObservers(group, childTree);
|
|
278
|
-
|
|
279
|
-
addGroupBinding(group, group.registerMarketPercentChangeHandler(() => {
|
|
280
|
-
currentTree.walk((childGroup) => childGroup.refreshMarketPercent());
|
|
281
|
-
}));
|
|
282
|
-
|
|
283
|
-
createGroups(childTree, group.items, array.dropLeft(levelDefinitions));
|
|
284
|
-
});
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
createGroups(tree, this._items, treeDefinition.definitions);
|
|
162
|
+
createGroups.call(this, tree, tree, this._items, treeDefinition, treeDefinition.definitions);
|
|
288
163
|
|
|
289
164
|
map[treeDefinition.name] = tree;
|
|
290
165
|
|
|
@@ -293,7 +168,27 @@ module.exports = (() => {
|
|
|
293
168
|
}
|
|
294
169
|
|
|
295
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);
|
|
174
|
+
|
|
175
|
+
const key = portfolio.portfolio;
|
|
296
176
|
|
|
177
|
+
if (!this._portfolios.hasOwnProperty(key)) {
|
|
178
|
+
this._portfolios[key] = portfolio;
|
|
179
|
+
|
|
180
|
+
this._definitions.forEach((treeDefinition) => {
|
|
181
|
+
const tree = this._trees[treeDefinition.name];
|
|
182
|
+
|
|
183
|
+
treeDefinition.definitions.forEach((levelDefinition) => {
|
|
184
|
+
const requiredGroup = levelDefinition.generateRequiredGroup(portfolio);
|
|
185
|
+
|
|
186
|
+
if (requiredGroup !== null) {
|
|
187
|
+
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
}
|
|
297
192
|
}
|
|
298
193
|
|
|
299
194
|
removePortfolio(portfolio) {
|
|
@@ -539,5 +434,132 @@ module.exports = (() => {
|
|
|
539
434
|
}
|
|
540
435
|
}
|
|
541
436
|
|
|
437
|
+
function addGroupBinding(group, dispoable) {
|
|
438
|
+
const id = group.id;
|
|
439
|
+
|
|
440
|
+
if (!this._groupBindings.hasOwnProperty(id)) {
|
|
441
|
+
this._groupBindings[id] = new DisposableStack();
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
this._groupBindings[id].push(dispoable);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function createGroups(parentTree, currentTree, items, treeDefinition, levelDefinitions) {
|
|
448
|
+
if (levelDefinitions.length === 0) {
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const parent = currentTree.getValue() || null;
|
|
453
|
+
|
|
454
|
+
const levelDefinition = levelDefinitions[0];
|
|
455
|
+
|
|
456
|
+
const populatedObjects = array.groupBy(items, levelDefinition.keySelector);
|
|
457
|
+
const populatedGroups = Object.keys(populatedObjects).reduce((list, key) => {
|
|
458
|
+
const items = populatedObjects[key];
|
|
459
|
+
const first = items[0];
|
|
460
|
+
|
|
461
|
+
list.push(new PositionGroup(this, parent, items, levelDefinition.currencySelector(first), key, levelDefinition.descriptionSelector(first), levelDefinition.single && items.length === 1, levelDefinition.aggregateCash));
|
|
462
|
+
|
|
463
|
+
return list;
|
|
464
|
+
}, [ ]);
|
|
465
|
+
|
|
466
|
+
const missingGroups = array.difference(levelDefinition.requiredGroups.map(group => group.key), populatedGroups.map(group => group.key))
|
|
467
|
+
.map((key) => {
|
|
468
|
+
return levelDefinition.requiredGroups.find(g => g.key === key);
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
const empty = missingGroups.map((group) => {
|
|
472
|
+
return new PositionGroup(this, parent, [ ], group.currency, group.key, group.description);
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
const compositeGroups = populatedGroups.concat(empty);
|
|
476
|
+
|
|
477
|
+
let builder;
|
|
478
|
+
|
|
479
|
+
if (levelDefinition.requiredGroups.length !== 0) {
|
|
480
|
+
const ordering = levelDefinition.requiredGroups.reduce((map, group, index) => {
|
|
481
|
+
map[group.description] = index;
|
|
482
|
+
|
|
483
|
+
return map;
|
|
484
|
+
}, { });
|
|
485
|
+
|
|
486
|
+
const getIndex = (description) => {
|
|
487
|
+
if (ordering.hasOwnProperty(description)) {
|
|
488
|
+
return ordering[description];
|
|
489
|
+
} else {
|
|
490
|
+
return Number.MAX_VALUE;
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
builder = ComparatorBuilder.startWith((a, b) => {
|
|
495
|
+
return comparators.compareNumbers(getIndex(a.description), getIndex(b.description));
|
|
496
|
+
}).thenBy((a, b) => {
|
|
497
|
+
return comparators.compareStrings(a.description, b.description);
|
|
498
|
+
});
|
|
499
|
+
} else {
|
|
500
|
+
builder = ComparatorBuilder.startWith((a, b) => {
|
|
501
|
+
return comparators.compareStrings(a.description, b.description);
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
compositeGroups.sort(builder.toComparator());
|
|
506
|
+
|
|
507
|
+
const initializeGroupObservers = (group, groupTree) => {
|
|
508
|
+
addGroupBinding.call(this, group, group.registerGroupExcludedChangeHandler((excluded, sender) => {
|
|
509
|
+
groupTree.climb((parentGroup) => {
|
|
510
|
+
if (parentGroup) {
|
|
511
|
+
let excludedItems = [];
|
|
512
|
+
|
|
513
|
+
currentTree.walk((childGroup) => {
|
|
514
|
+
if (childGroup.excluded) {
|
|
515
|
+
excludedItems = excludedItems.concat(childGroup.items);
|
|
516
|
+
}
|
|
517
|
+
}, false, false);
|
|
518
|
+
|
|
519
|
+
parentGroup.setExcludedItems(array.unique(excludedItems));
|
|
520
|
+
}
|
|
521
|
+
}, false);
|
|
522
|
+
|
|
523
|
+
if (treeDefinition.exclusionDependencies.length > 0) {
|
|
524
|
+
const dependantTrees = treeDefinition.exclusionDependencies.reduce((trees, name) => {
|
|
525
|
+
if (this._trees.hasOwnProperty(name)) {
|
|
526
|
+
trees.push(this._trees[name]);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
return trees;
|
|
530
|
+
}, [ ]);
|
|
531
|
+
|
|
532
|
+
if (dependantTrees.length > 0) {
|
|
533
|
+
let excludedItems = [ ];
|
|
534
|
+
|
|
535
|
+
parentTree.walk((childGroup) => {
|
|
536
|
+
if (childGroup.excluded) {
|
|
537
|
+
excludedItems = excludedItems.concat(childGroup.items);
|
|
538
|
+
}
|
|
539
|
+
}, false, false);
|
|
540
|
+
|
|
541
|
+
dependantTrees.forEach((dependantTrees) => {
|
|
542
|
+
dependantTrees.walk((childGroup) => {
|
|
543
|
+
childGroup.setExcludedItems(excludedItems);
|
|
544
|
+
}, false, false);
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}));
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
compositeGroups.forEach((group) => {
|
|
552
|
+
const childTree = currentTree.addChild(group);
|
|
553
|
+
|
|
554
|
+
initializeGroupObservers(group, childTree);
|
|
555
|
+
|
|
556
|
+
addGroupBinding.call(this, group, group.registerMarketPercentChangeHandler(() => {
|
|
557
|
+
currentTree.walk((childGroup) => childGroup.refreshMarketPercent());
|
|
558
|
+
}));
|
|
559
|
+
|
|
560
|
+
createGroups.call(this, parentTree, childTree, group.items, treeDefinition, array.dropLeft(levelDefinitions));
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
|
|
542
564
|
return PositionContainer;
|
|
543
565
|
})();
|
|
@@ -20,9 +20,10 @@ module.exports = (() => {
|
|
|
20
20
|
* @param {Array.<PositionLevelDefinition~RequiredGroup>=} requiredGroups
|
|
21
21
|
* @param {Boolean=} single
|
|
22
22
|
* @param {Boolean=} aggregateCash
|
|
23
|
+
* @param {Function=} injectPositions
|
|
23
24
|
*/
|
|
24
25
|
class PositionLevelDefinition {
|
|
25
|
-
constructor(name, keySelector, descriptionSelector, currencySelector, requiredGroups, single, aggregateCash) {
|
|
26
|
+
constructor(name, keySelector, descriptionSelector, currencySelector, requiredGroups, single, aggregateCash, requiredGroupGenerator) {
|
|
26
27
|
assert.argumentIsRequired(name, 'name', String);
|
|
27
28
|
assert.argumentIsRequired(keySelector, 'keySelector', Function);
|
|
28
29
|
assert.argumentIsRequired(descriptionSelector, 'descriptionSelector', Function);
|
|
@@ -34,6 +35,7 @@ module.exports = (() => {
|
|
|
34
35
|
|
|
35
36
|
assert.argumentIsOptional(single, 'single', Boolean);
|
|
36
37
|
assert.argumentIsOptional(aggregateCash, 'aggregateCash', Boolean);
|
|
38
|
+
assert.argumentIsOptional(requiredGroupGenerator, 'requiredGroupGenerator', Function);
|
|
37
39
|
|
|
38
40
|
this._name = name;
|
|
39
41
|
|
|
@@ -42,8 +44,11 @@ module.exports = (() => {
|
|
|
42
44
|
this._currencySelector = currencySelector;
|
|
43
45
|
|
|
44
46
|
this._requiredGroups = requiredGroups || [ ];
|
|
47
|
+
|
|
45
48
|
this._single = is.boolean(single) && single;
|
|
46
49
|
this._aggregateCash = is.boolean(aggregateCash) && aggregateCash;
|
|
50
|
+
|
|
51
|
+
this._requiredGroupGenerator = requiredGroupGenerator || (input => null);
|
|
47
52
|
}
|
|
48
53
|
|
|
49
54
|
/**
|
|
@@ -120,6 +125,23 @@ module.exports = (() => {
|
|
|
120
125
|
return this._aggregateCash;
|
|
121
126
|
}
|
|
122
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
|
+
|
|
123
145
|
/**
|
|
124
146
|
* Builds a {@link PositionLevelDefinition~RequiredGroup} for a portfolio.
|
|
125
147
|
*
|
|
@@ -136,7 +158,6 @@ module.exports = (() => {
|
|
|
136
158
|
};
|
|
137
159
|
}
|
|
138
160
|
|
|
139
|
-
|
|
140
161
|
static getKeyForPortfolioGroup(portfolio) {
|
|
141
162
|
assert.argumentIsRequired(portfolio, 'portfolio', Object);
|
|
142
163
|
|
|
@@ -149,6 +170,20 @@ module.exports = (() => {
|
|
|
149
170
|
return portfolio.name;
|
|
150
171
|
}
|
|
151
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
|
+
|
|
152
187
|
/**
|
|
153
188
|
* Builds a {@link PositionLevelDefinition~RequiredGroup} for an asset class.
|
|
154
189
|
*
|
|
@@ -163,10 +198,9 @@ module.exports = (() => {
|
|
|
163
198
|
key: PositionLevelDefinition.getKeyForAssetClassGroup(type, currency),
|
|
164
199
|
description: PositionLevelDefinition.getDescriptionForAssetClassGroup(type, currency),
|
|
165
200
|
currency: currency
|
|
166
|
-
}
|
|
201
|
+
};
|
|
167
202
|
}
|
|
168
203
|
|
|
169
|
-
|
|
170
204
|
static getKeyForAssetClassGroup(type, currency) {
|
|
171
205
|
assert.argumentIsRequired(type, 'type', InstrumentType, 'InstrumentType');
|
|
172
206
|
assert.argumentIsRequired(currency, 'currency', Currency, 'Currency');
|
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,127 +872,10 @@ 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
|
-
if (levelDefinitions.length === 0) {
|
|
888
|
-
return;
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
const parent = currentTree.getValue() || null;
|
|
892
|
-
|
|
893
|
-
const levelDefinition = levelDefinitions[0];
|
|
894
|
-
|
|
895
|
-
const populatedObjects = array.groupBy(items, levelDefinition.keySelector);
|
|
896
|
-
const populatedGroups = Object.keys(populatedObjects).reduce((list, key) => {
|
|
897
|
-
const items = populatedObjects[key];
|
|
898
|
-
const first = items[0];
|
|
899
|
-
|
|
900
|
-
list.push(new PositionGroup(this, parent, items, levelDefinition.currencySelector(first), key, levelDefinition.descriptionSelector(first), levelDefinition.single && items.length === 1, levelDefinition.aggregateCash));
|
|
901
|
-
|
|
902
|
-
return list;
|
|
903
|
-
}, [ ]);
|
|
904
|
-
|
|
905
|
-
const missingGroups = array.difference(levelDefinition.requiredGroups.map(group => group.key), populatedGroups.map(group => group.key))
|
|
906
|
-
.map((key) => {
|
|
907
|
-
return levelDefinition.requiredGroups.find(g => g.key === key);
|
|
908
|
-
});
|
|
909
|
-
|
|
910
|
-
const empty = missingGroups.map((group) => {
|
|
911
|
-
return new PositionGroup(this, parent, [ ], group.currency, group.key, group.description);
|
|
912
|
-
});
|
|
913
|
-
|
|
914
|
-
const compositeGroups = populatedGroups.concat(empty);
|
|
915
|
-
|
|
916
|
-
let builder;
|
|
917
|
-
|
|
918
|
-
if (levelDefinition.requiredGroups.length !== 0) {
|
|
919
|
-
const ordering = levelDefinition.requiredGroups.reduce((map, group, index) => {
|
|
920
|
-
map[group.description] = index;
|
|
921
|
-
|
|
922
|
-
return map;
|
|
923
|
-
}, { });
|
|
924
|
-
|
|
925
|
-
const getIndex = (description) => {
|
|
926
|
-
if (ordering.hasOwnProperty(description)) {
|
|
927
|
-
return ordering[description];
|
|
928
|
-
} else {
|
|
929
|
-
return Number.MAX_VALUE;
|
|
930
|
-
}
|
|
931
|
-
};
|
|
932
|
-
|
|
933
|
-
builder = ComparatorBuilder.startWith((a, b) => {
|
|
934
|
-
return comparators.compareNumbers(getIndex(a.description), getIndex(b.description));
|
|
935
|
-
}).thenBy((a, b) => {
|
|
936
|
-
return comparators.compareStrings(a.description, b.description);
|
|
937
|
-
});
|
|
938
|
-
} else {
|
|
939
|
-
builder = ComparatorBuilder.startWith((a, b) => {
|
|
940
|
-
return comparators.compareStrings(a.description, b.description);
|
|
941
|
-
});
|
|
942
|
-
}
|
|
943
|
-
|
|
944
|
-
compositeGroups.sort(builder.toComparator());
|
|
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
|
-
};
|
|
989
|
-
|
|
990
|
-
compositeGroups.forEach((group) => {
|
|
991
|
-
const childTree = currentTree.addChild(group);
|
|
992
|
-
|
|
993
|
-
initializeGroupObservers(group, childTree);
|
|
994
|
-
|
|
995
|
-
addGroupBinding(group, group.registerMarketPercentChangeHandler(() => {
|
|
996
|
-
currentTree.walk((childGroup) => childGroup.refreshMarketPercent());
|
|
997
|
-
}));
|
|
998
|
-
|
|
999
|
-
createGroups(childTree, group.items, array.dropLeft(levelDefinitions));
|
|
1000
|
-
});
|
|
1001
|
-
};
|
|
1002
|
-
|
|
1003
|
-
createGroups(tree, this._items, treeDefinition.definitions);
|
|
878
|
+
createGroups.call(this, tree, tree, this._items, treeDefinition, treeDefinition.definitions);
|
|
1004
879
|
|
|
1005
880
|
map[treeDefinition.name] = tree;
|
|
1006
881
|
|
|
@@ -1009,7 +884,27 @@ module.exports = (() => {
|
|
|
1009
884
|
}
|
|
1010
885
|
|
|
1011
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);
|
|
890
|
+
|
|
891
|
+
const key = portfolio.portfolio;
|
|
892
|
+
|
|
893
|
+
if (!this._portfolios.hasOwnProperty(key)) {
|
|
894
|
+
this._portfolios[key] = portfolio;
|
|
895
|
+
|
|
896
|
+
this._definitions.forEach((treeDefinition) => {
|
|
897
|
+
const tree = this._trees[treeDefinition.name];
|
|
1012
898
|
|
|
899
|
+
treeDefinition.definitions.forEach((levelDefinition) => {
|
|
900
|
+
const requiredGroup = levelDefinition.generateRequiredGroup(portfolio);
|
|
901
|
+
|
|
902
|
+
if (requiredGroup !== null) {
|
|
903
|
+
|
|
904
|
+
}
|
|
905
|
+
});
|
|
906
|
+
});
|
|
907
|
+
}
|
|
1013
908
|
}
|
|
1014
909
|
|
|
1015
910
|
removePortfolio(portfolio) {
|
|
@@ -1255,6 +1150,133 @@ module.exports = (() => {
|
|
|
1255
1150
|
}
|
|
1256
1151
|
}
|
|
1257
1152
|
|
|
1153
|
+
function addGroupBinding(group, dispoable) {
|
|
1154
|
+
const id = group.id;
|
|
1155
|
+
|
|
1156
|
+
if (!this._groupBindings.hasOwnProperty(id)) {
|
|
1157
|
+
this._groupBindings[id] = new DisposableStack();
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
this._groupBindings[id].push(dispoable);
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
function createGroups(parentTree, currentTree, items, treeDefinition, levelDefinitions) {
|
|
1164
|
+
if (levelDefinitions.length === 0) {
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
const parent = currentTree.getValue() || null;
|
|
1169
|
+
|
|
1170
|
+
const levelDefinition = levelDefinitions[0];
|
|
1171
|
+
|
|
1172
|
+
const populatedObjects = array.groupBy(items, levelDefinition.keySelector);
|
|
1173
|
+
const populatedGroups = Object.keys(populatedObjects).reduce((list, key) => {
|
|
1174
|
+
const items = populatedObjects[key];
|
|
1175
|
+
const first = items[0];
|
|
1176
|
+
|
|
1177
|
+
list.push(new PositionGroup(this, parent, items, levelDefinition.currencySelector(first), key, levelDefinition.descriptionSelector(first), levelDefinition.single && items.length === 1, levelDefinition.aggregateCash));
|
|
1178
|
+
|
|
1179
|
+
return list;
|
|
1180
|
+
}, [ ]);
|
|
1181
|
+
|
|
1182
|
+
const missingGroups = array.difference(levelDefinition.requiredGroups.map(group => group.key), populatedGroups.map(group => group.key))
|
|
1183
|
+
.map((key) => {
|
|
1184
|
+
return levelDefinition.requiredGroups.find(g => g.key === key);
|
|
1185
|
+
});
|
|
1186
|
+
|
|
1187
|
+
const empty = missingGroups.map((group) => {
|
|
1188
|
+
return new PositionGroup(this, parent, [ ], group.currency, group.key, group.description);
|
|
1189
|
+
});
|
|
1190
|
+
|
|
1191
|
+
const compositeGroups = populatedGroups.concat(empty);
|
|
1192
|
+
|
|
1193
|
+
let builder;
|
|
1194
|
+
|
|
1195
|
+
if (levelDefinition.requiredGroups.length !== 0) {
|
|
1196
|
+
const ordering = levelDefinition.requiredGroups.reduce((map, group, index) => {
|
|
1197
|
+
map[group.description] = index;
|
|
1198
|
+
|
|
1199
|
+
return map;
|
|
1200
|
+
}, { });
|
|
1201
|
+
|
|
1202
|
+
const getIndex = (description) => {
|
|
1203
|
+
if (ordering.hasOwnProperty(description)) {
|
|
1204
|
+
return ordering[description];
|
|
1205
|
+
} else {
|
|
1206
|
+
return Number.MAX_VALUE;
|
|
1207
|
+
}
|
|
1208
|
+
};
|
|
1209
|
+
|
|
1210
|
+
builder = ComparatorBuilder.startWith((a, b) => {
|
|
1211
|
+
return comparators.compareNumbers(getIndex(a.description), getIndex(b.description));
|
|
1212
|
+
}).thenBy((a, b) => {
|
|
1213
|
+
return comparators.compareStrings(a.description, b.description);
|
|
1214
|
+
});
|
|
1215
|
+
} else {
|
|
1216
|
+
builder = ComparatorBuilder.startWith((a, b) => {
|
|
1217
|
+
return comparators.compareStrings(a.description, b.description);
|
|
1218
|
+
});
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
compositeGroups.sort(builder.toComparator());
|
|
1222
|
+
|
|
1223
|
+
const initializeGroupObservers = (group, groupTree) => {
|
|
1224
|
+
addGroupBinding.call(this, group, group.registerGroupExcludedChangeHandler((excluded, sender) => {
|
|
1225
|
+
groupTree.climb((parentGroup) => {
|
|
1226
|
+
if (parentGroup) {
|
|
1227
|
+
let excludedItems = [];
|
|
1228
|
+
|
|
1229
|
+
currentTree.walk((childGroup) => {
|
|
1230
|
+
if (childGroup.excluded) {
|
|
1231
|
+
excludedItems = excludedItems.concat(childGroup.items);
|
|
1232
|
+
}
|
|
1233
|
+
}, false, false);
|
|
1234
|
+
|
|
1235
|
+
parentGroup.setExcludedItems(array.unique(excludedItems));
|
|
1236
|
+
}
|
|
1237
|
+
}, false);
|
|
1238
|
+
|
|
1239
|
+
if (treeDefinition.exclusionDependencies.length > 0) {
|
|
1240
|
+
const dependantTrees = treeDefinition.exclusionDependencies.reduce((trees, name) => {
|
|
1241
|
+
if (this._trees.hasOwnProperty(name)) {
|
|
1242
|
+
trees.push(this._trees[name]);
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
return trees;
|
|
1246
|
+
}, [ ]);
|
|
1247
|
+
|
|
1248
|
+
if (dependantTrees.length > 0) {
|
|
1249
|
+
let excludedItems = [ ];
|
|
1250
|
+
|
|
1251
|
+
parentTree.walk((childGroup) => {
|
|
1252
|
+
if (childGroup.excluded) {
|
|
1253
|
+
excludedItems = excludedItems.concat(childGroup.items);
|
|
1254
|
+
}
|
|
1255
|
+
}, false, false);
|
|
1256
|
+
|
|
1257
|
+
dependantTrees.forEach((dependantTrees) => {
|
|
1258
|
+
dependantTrees.walk((childGroup) => {
|
|
1259
|
+
childGroup.setExcludedItems(excludedItems);
|
|
1260
|
+
}, false, false);
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
}));
|
|
1265
|
+
};
|
|
1266
|
+
|
|
1267
|
+
compositeGroups.forEach((group) => {
|
|
1268
|
+
const childTree = currentTree.addChild(group);
|
|
1269
|
+
|
|
1270
|
+
initializeGroupObservers(group, childTree);
|
|
1271
|
+
|
|
1272
|
+
addGroupBinding.call(this, group, group.registerMarketPercentChangeHandler(() => {
|
|
1273
|
+
currentTree.walk((childGroup) => childGroup.refreshMarketPercent());
|
|
1274
|
+
}));
|
|
1275
|
+
|
|
1276
|
+
createGroups.call(this, parentTree, childTree, group.items, treeDefinition, array.dropLeft(levelDefinitions));
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1258
1280
|
return PositionContainer;
|
|
1259
1281
|
})();
|
|
1260
1282
|
|
|
@@ -2342,9 +2364,10 @@ module.exports = (() => {
|
|
|
2342
2364
|
* @param {Array.<PositionLevelDefinition~RequiredGroup>=} requiredGroups
|
|
2343
2365
|
* @param {Boolean=} single
|
|
2344
2366
|
* @param {Boolean=} aggregateCash
|
|
2367
|
+
* @param {Function=} injectPositions
|
|
2345
2368
|
*/
|
|
2346
2369
|
class PositionLevelDefinition {
|
|
2347
|
-
constructor(name, keySelector, descriptionSelector, currencySelector, requiredGroups, single, aggregateCash) {
|
|
2370
|
+
constructor(name, keySelector, descriptionSelector, currencySelector, requiredGroups, single, aggregateCash, requiredGroupGenerator) {
|
|
2348
2371
|
assert.argumentIsRequired(name, 'name', String);
|
|
2349
2372
|
assert.argumentIsRequired(keySelector, 'keySelector', Function);
|
|
2350
2373
|
assert.argumentIsRequired(descriptionSelector, 'descriptionSelector', Function);
|
|
@@ -2356,6 +2379,7 @@ module.exports = (() => {
|
|
|
2356
2379
|
|
|
2357
2380
|
assert.argumentIsOptional(single, 'single', Boolean);
|
|
2358
2381
|
assert.argumentIsOptional(aggregateCash, 'aggregateCash', Boolean);
|
|
2382
|
+
assert.argumentIsOptional(requiredGroupGenerator, 'requiredGroupGenerator', Function);
|
|
2359
2383
|
|
|
2360
2384
|
this._name = name;
|
|
2361
2385
|
|
|
@@ -2364,8 +2388,11 @@ module.exports = (() => {
|
|
|
2364
2388
|
this._currencySelector = currencySelector;
|
|
2365
2389
|
|
|
2366
2390
|
this._requiredGroups = requiredGroups || [ ];
|
|
2391
|
+
|
|
2367
2392
|
this._single = is.boolean(single) && single;
|
|
2368
2393
|
this._aggregateCash = is.boolean(aggregateCash) && aggregateCash;
|
|
2394
|
+
|
|
2395
|
+
this._requiredGroupGenerator = requiredGroupGenerator || (input => null);
|
|
2369
2396
|
}
|
|
2370
2397
|
|
|
2371
2398
|
/**
|
|
@@ -2442,6 +2469,23 @@ module.exports = (() => {
|
|
|
2442
2469
|
return this._aggregateCash;
|
|
2443
2470
|
}
|
|
2444
2471
|
|
|
2472
|
+
/**
|
|
2473
|
+
* Given an input, potentially creates a new {@link PositionLevelDefinition~RequiredGroup}.
|
|
2474
|
+
*
|
|
2475
|
+
* @public
|
|
2476
|
+
* @param {*} input
|
|
2477
|
+
* @returns {PositionLevelDefinition~RequiredGroup|null}
|
|
2478
|
+
*/
|
|
2479
|
+
generateRequiredGroup(input) {
|
|
2480
|
+
const requiredGroup = this._requiredGroupGenerator(input);
|
|
2481
|
+
|
|
2482
|
+
if (requiredGroup !== null) {
|
|
2483
|
+
this._requiredGroups.push(requiredGroup);
|
|
2484
|
+
}
|
|
2485
|
+
|
|
2486
|
+
return requiredGroup;
|
|
2487
|
+
}
|
|
2488
|
+
|
|
2445
2489
|
/**
|
|
2446
2490
|
* Builds a {@link PositionLevelDefinition~RequiredGroup} for a portfolio.
|
|
2447
2491
|
*
|
|
@@ -2458,7 +2502,6 @@ module.exports = (() => {
|
|
|
2458
2502
|
};
|
|
2459
2503
|
}
|
|
2460
2504
|
|
|
2461
|
-
|
|
2462
2505
|
static getKeyForPortfolioGroup(portfolio) {
|
|
2463
2506
|
assert.argumentIsRequired(portfolio, 'portfolio', Object);
|
|
2464
2507
|
|
|
@@ -2471,6 +2514,20 @@ module.exports = (() => {
|
|
|
2471
2514
|
return portfolio.name;
|
|
2472
2515
|
}
|
|
2473
2516
|
|
|
2517
|
+
static getRequiredGroupGeneratorForPortfolio() {
|
|
2518
|
+
return (portfolio) => {
|
|
2519
|
+
let requiredGroup;
|
|
2520
|
+
|
|
2521
|
+
if (is.object(portfolio) && is.string(portfolio.portfolio) && is.string(portfolio.name)) {
|
|
2522
|
+
requiredGroup = PositionLevelDefinition.buildRequiredGroupForPortfolio(portfolio);
|
|
2523
|
+
} else {
|
|
2524
|
+
requiredGroup = null;
|
|
2525
|
+
}
|
|
2526
|
+
|
|
2527
|
+
return requiredGroup;
|
|
2528
|
+
};
|
|
2529
|
+
}
|
|
2530
|
+
|
|
2474
2531
|
/**
|
|
2475
2532
|
* Builds a {@link PositionLevelDefinition~RequiredGroup} for an asset class.
|
|
2476
2533
|
*
|
|
@@ -2485,10 +2542,9 @@ module.exports = (() => {
|
|
|
2485
2542
|
key: PositionLevelDefinition.getKeyForAssetClassGroup(type, currency),
|
|
2486
2543
|
description: PositionLevelDefinition.getDescriptionForAssetClassGroup(type, currency),
|
|
2487
2544
|
currency: currency
|
|
2488
|
-
}
|
|
2545
|
+
};
|
|
2489
2546
|
}
|
|
2490
2547
|
|
|
2491
|
-
|
|
2492
2548
|
static getKeyForAssetClassGroup(type, currency) {
|
|
2493
2549
|
assert.argumentIsRequired(type, 'type', InstrumentType, 'InstrumentType');
|
|
2494
2550
|
assert.argumentIsRequired(currency, 'currency', Currency, 'Currency');
|