@barchart/portfolio-api-common 1.0.95 → 1.0.99
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/formatters/TransactionFormatter.js +44 -32
- package/lib/processing/PositionContainer.js +121 -97
- package/lib/processing/PositionGroup.js +12 -7
- package/lib/processing/definitions/PositionLevelDefinition.js +143 -0
- package/lib/processing/definitions/PositionTreeDefinition.js +52 -0
- package/lib/serialization/TransactionSchema.js +10 -10
- package/package.json +1 -1
- package/test/SpecRunner.js +365 -183
- package/test/specs/processing/PositionContainerSpec.js +15 -11
- package/lib/processing/PositionGroupDefinition.js +0 -48
|
@@ -8,7 +8,7 @@ module.exports = (() => {
|
|
|
8
8
|
'use strict';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* Static
|
|
11
|
+
* Static utility for formatting transaction data in human-readable form.
|
|
12
12
|
*
|
|
13
13
|
* @public
|
|
14
14
|
*/
|
|
@@ -18,54 +18,66 @@ module.exports = (() => {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
21
|
+
* Maps transaction objects into new objects whose properties are human-readable or,
|
|
22
|
+
* optionally returns the original objects with a "formatted" property appended to
|
|
23
|
+
* each transaction.
|
|
23
24
|
*
|
|
24
25
|
* @public
|
|
25
26
|
* @static
|
|
26
|
-
* @param {Array} transactions
|
|
27
|
-
* @param {Array} positions
|
|
28
|
-
* @param {
|
|
29
|
-
*
|
|
27
|
+
* @param {Array<Object>} transactions
|
|
28
|
+
* @param {Array<Object>} positions
|
|
29
|
+
* @param {Boolean=} append
|
|
30
30
|
* @returns {Array}
|
|
31
31
|
*/
|
|
32
|
-
static format(transactions, positions,
|
|
33
|
-
assert.
|
|
34
|
-
assert.
|
|
32
|
+
static format(transactions, positions, append) {
|
|
33
|
+
assert.argumentIsArray(transactions, 'transactions');
|
|
34
|
+
assert.argumentIsArray(positions, 'positions');
|
|
35
|
+
assert.argumentIsOptional(append, 'append', Boolean);
|
|
35
36
|
|
|
36
|
-
const
|
|
37
|
+
const instruments = positions.reduce((map, p) => {
|
|
38
|
+
map[p.position] = p.instrument;
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
return map;
|
|
41
|
+
}, { });
|
|
39
42
|
|
|
40
|
-
return transactions.
|
|
41
|
-
|
|
43
|
+
return transactions.reduce((list, transaction) => {
|
|
44
|
+
const position = transaction.position;
|
|
42
45
|
|
|
43
|
-
|
|
46
|
+
if (instruments.hasOwnProperty(position)) {
|
|
47
|
+
transaction.instrument = instruments[position];
|
|
44
48
|
|
|
45
|
-
|
|
46
|
-
const formatterFunction = formatters.get(transaction.type);
|
|
49
|
+
let formatted = getBasicTransaction(transaction);
|
|
47
50
|
|
|
48
|
-
|
|
51
|
+
if (formatters.has(transaction.type)) {
|
|
52
|
+
const formatterFunction = formatters.get(transaction.type);
|
|
53
|
+
const formattedTransaction = formatterFunction(transaction);
|
|
49
54
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
55
|
+
Object.keys(formattedTransaction).map((key) => {
|
|
56
|
+
if (!is.undefined(formattedTransaction[key]) && is.fn(formattedTransaction[key].toFloat)) {
|
|
57
|
+
const precision = transaction.instrument.currency.precision;
|
|
53
58
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
formattedTransaction[key] = formatter.numberToString(formattedTransaction[key].toFloat(), precision, ',');
|
|
60
|
+
}
|
|
61
|
+
});
|
|
57
62
|
|
|
58
|
-
|
|
59
|
-
|
|
63
|
+
formatted = Object.assign({}, formatted, formattedTransaction);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let transactionToInsert;
|
|
60
67
|
|
|
61
|
-
|
|
62
|
-
|
|
68
|
+
if (append) {
|
|
69
|
+
transaction.formatted = formatted;
|
|
63
70
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
71
|
+
transactionToInsert = transaction;
|
|
72
|
+
} else {
|
|
73
|
+
transactionToInsert = formatted;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
list.push(transactionToInsert);
|
|
67
77
|
}
|
|
68
|
-
|
|
78
|
+
|
|
79
|
+
return list;
|
|
80
|
+
}, [ ]);
|
|
69
81
|
}
|
|
70
82
|
|
|
71
83
|
toString() {
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
const array = require('@barchart/common-js/lang/array'),
|
|
2
|
+
assert = require('@barchart/common-js/lang/assert'),
|
|
2
3
|
ComparatorBuilder = require('@barchart/common-js/collections/sorting/ComparatorBuilder'),
|
|
3
4
|
comparators = require('@barchart/common-js/collections/sorting/comparators'),
|
|
4
5
|
Currency = require('@barchart/common-js/lang/Currency'),
|
|
5
|
-
assert = require('@barchart/common-js/lang/assert'),
|
|
6
6
|
is = require('@barchart/common-js/lang/is'),
|
|
7
7
|
Tree = require('@barchart/common-js/collections/Tree');
|
|
8
8
|
|
|
9
9
|
const PositionSummaryFrame = require('./../data/PositionSummaryFrame');
|
|
10
10
|
|
|
11
|
+
const PositionLevelDefinition = require('./definitions/PositionLevelDefinition'),
|
|
12
|
+
PositionTreeDefinition = require('./definitions/PositionTreeDefinition');
|
|
13
|
+
|
|
11
14
|
const PositionGroup = require('./PositionGroup'),
|
|
12
15
|
PositionItem = require('./PositionItem');
|
|
13
16
|
|
|
@@ -15,22 +18,32 @@ module.exports = (() => {
|
|
|
15
18
|
'use strict';
|
|
16
19
|
|
|
17
20
|
/**
|
|
21
|
+
* A container for positions which groups the positions into one or more
|
|
22
|
+
* trees for aggregation and display purposes. For example, perhaps a positions
|
|
23
|
+
* grouped first by asset class then by position is desired.
|
|
24
|
+
*
|
|
25
|
+
* Furthermore, the container performs aggregation (driven primarily by price
|
|
26
|
+
* changes) for each level of grouping in the internal tree(s).
|
|
27
|
+
*
|
|
18
28
|
* @public
|
|
29
|
+
* @param {Array.<PositionTreeDefinition>} definitions
|
|
30
|
+
* @param {Array.<Object>} portfolios
|
|
31
|
+
* @param {Array.<Object>} positions
|
|
32
|
+
* @param {Array.<Object>} summaries
|
|
19
33
|
*/
|
|
20
34
|
class PositionContainer {
|
|
21
|
-
constructor(portfolios, positions, summaries
|
|
22
|
-
|
|
23
|
-
|
|
35
|
+
constructor(definitions, portfolios, positions, summaries) {
|
|
36
|
+
assert.argumentIsArray(definitions, 'definitions', PositionTreeDefinition, 'PositionTreeDefinition');
|
|
37
|
+
assert.argumentIsArray(portfolios, 'portfolios');
|
|
38
|
+
assert.argumentIsArray(positions, 'positions');
|
|
39
|
+
assert.argumentIsArray(summaries, 'summaries');
|
|
24
40
|
|
|
25
|
-
const previousSummaryFrame =
|
|
41
|
+
const previousSummaryFrame = PositionSummaryFrame.YEARLY;
|
|
26
42
|
const previousSummaryRanges = previousSummaryFrame.getRecentRanges(0);
|
|
27
43
|
|
|
28
44
|
const currentSummaryFrame = PositionSummaryFrame.YTD;
|
|
29
45
|
const currentSummaryRange = array.last(currentSummaryFrame.getRecentRanges(0));
|
|
30
46
|
|
|
31
|
-
this._summaryDescriptionCurrent = previousSummaryFrame.describeRange(array.last(previousSummaryRanges));
|
|
32
|
-
this._summaryDescriptionPrevious = currentSummaryFrame.describeRange(currentSummaryRange);
|
|
33
|
-
|
|
34
47
|
this._portfolios = portfolios.reduce((map, portfolio) => {
|
|
35
48
|
map[portfolio.portfolio] = portfolio;
|
|
36
49
|
|
|
@@ -109,111 +122,104 @@ module.exports = (() => {
|
|
|
109
122
|
|
|
110
123
|
return map;
|
|
111
124
|
}, { });
|
|
125
|
+
|
|
126
|
+
this._trees = definitions.reduce((map, treeDefinition) => {
|
|
127
|
+
const tree = new Tree();
|
|
112
128
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const parent = tree.getValue() || null;
|
|
121
|
-
|
|
122
|
-
const currentDefinition = definitions[0];
|
|
123
|
-
const additionalDefinitions = array.dropLeft(definitions);
|
|
124
|
-
|
|
125
|
-
const populatedObjects = array.groupBy(items, currentDefinition.keySelector);
|
|
126
|
-
const populatedGroups = Object.keys(populatedObjects).map(key => populatedObjects[key]).map((items) => {
|
|
127
|
-
const first = items[0];
|
|
129
|
+
const createGroups = (currentTree, items, levelDefinitions) => {
|
|
130
|
+
if (levelDefinitions.length === 0) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
128
133
|
|
|
129
|
-
|
|
130
|
-
});
|
|
134
|
+
const parent = currentTree.getValue() || null;
|
|
131
135
|
|
|
132
|
-
|
|
136
|
+
const levelDefinition = levelDefinitions[0];
|
|
133
137
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
138
|
+
const populatedObjects = array.groupBy(items, levelDefinition.keySelector);
|
|
139
|
+
const populatedGroups = Object.keys(populatedObjects).reduce((list, key) => {
|
|
140
|
+
const items = populatedObjects[key];
|
|
141
|
+
const first = items[0];
|
|
137
142
|
|
|
138
|
-
|
|
143
|
+
list.push(new PositionGroup(this, parent, items, levelDefinition.currencySelector(first), key, levelDefinition.descriptionSelector(first), levelDefinition.single && items.length === 1));
|
|
139
144
|
|
|
140
|
-
|
|
145
|
+
return list;
|
|
146
|
+
}, [ ]);
|
|
141
147
|
|
|
142
|
-
|
|
143
|
-
const ordering = currentDefinition.requiredGroups.reduce((map, group, index) => {
|
|
144
|
-
map[group.description] = index;
|
|
148
|
+
const missingGroups = array.difference(levelDefinition.requiredGroups.map(group => group.description), populatedGroups.map(group => group.description));
|
|
145
149
|
|
|
146
|
-
|
|
147
|
-
|
|
150
|
+
const empty = missingGroups.map((description) => {
|
|
151
|
+
return new PositionGroup(this, parent, [ ], levelDefinition.requiredGroups.find(group => group.description === description).currency, null, description);
|
|
152
|
+
});
|
|
148
153
|
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
154
|
+
const compositeGroups = populatedGroups.concat(empty);
|
|
155
|
+
|
|
156
|
+
let builder;
|
|
157
|
+
|
|
158
|
+
if (levelDefinition.requiredGroups.length !== 0) {
|
|
159
|
+
const ordering = levelDefinition.requiredGroups.reduce((map, group, index) => {
|
|
160
|
+
map[group.description] = index;
|
|
161
|
+
|
|
162
|
+
return map;
|
|
163
|
+
}, { });
|
|
164
|
+
|
|
165
|
+
const getIndex = (description) => {
|
|
166
|
+
if (ordering.hasOwnProperty(description)) {
|
|
167
|
+
return ordering[description];
|
|
168
|
+
} else {
|
|
169
|
+
return Number.MAX_VALUE;
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
builder = ComparatorBuilder.startWith((a, b) => {
|
|
174
|
+
return comparators.compareNumbers(getIndex(a.description), getIndex(b.description));
|
|
175
|
+
}).thenBy((a, b) => {
|
|
176
|
+
return comparators.compareStrings(a.description, b.description);
|
|
177
|
+
});
|
|
178
|
+
} else {
|
|
179
|
+
builder = ComparatorBuilder.startWith((a, b) => {
|
|
180
|
+
return comparators.compareStrings(a.description, b.description);
|
|
181
|
+
});
|
|
182
|
+
}
|
|
156
183
|
|
|
157
|
-
builder
|
|
158
|
-
return comparators.compareNumbers(getIndex(a.description), getIndex(b.description));
|
|
159
|
-
}).thenBy((a, b) => {
|
|
160
|
-
return comparators.compareStrings(a.description, b.description);
|
|
161
|
-
});
|
|
162
|
-
} else {
|
|
163
|
-
builder = ComparatorBuilder.startWith((a, b) => {
|
|
164
|
-
return comparators.compareStrings(a.description, b.description);
|
|
165
|
-
});
|
|
166
|
-
}
|
|
184
|
+
compositeGroups.sort(builder.toComparator());
|
|
167
185
|
|
|
168
|
-
|
|
186
|
+
compositeGroups.forEach((group) => {
|
|
187
|
+
const childTree = currentTree.addChild(group);
|
|
169
188
|
|
|
170
|
-
|
|
171
|
-
|
|
189
|
+
group.registerMarketPercentChangeHandler(() => {
|
|
190
|
+
currentTree.walk((childGroup) => childGroup.refreshMarketPercent());
|
|
191
|
+
});
|
|
172
192
|
|
|
173
|
-
|
|
174
|
-
this._tree.walk((childGroup) => childGroup.refreshMarketPercent());
|
|
193
|
+
createGroups(childTree, group.items, array.dropLeft(levelDefinitions));
|
|
175
194
|
});
|
|
195
|
+
};
|
|
176
196
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
197
|
+
createGroups(tree, this._items, treeDefinition.definitions);
|
|
198
|
+
|
|
199
|
+
map[treeDefinition.name] = tree;
|
|
180
200
|
|
|
181
|
-
|
|
201
|
+
return map;
|
|
202
|
+
}, { });
|
|
182
203
|
}
|
|
183
204
|
|
|
184
205
|
get defaultCurrency() {
|
|
185
206
|
return this._defaultCurrency;
|
|
186
207
|
}
|
|
187
208
|
|
|
188
|
-
|
|
189
|
-
return this._summaryDescriptionCurrent;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
getPreviousSummaryDescription() {
|
|
193
|
-
return this._summaryDescriptionPrevious;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
startTransaction(executor) {
|
|
197
|
-
assert.argumentIsRequired(executor, 'executor', Function);
|
|
198
|
-
|
|
199
|
-
this._tree.walk(group => group.setSuspended(true), false, false);
|
|
200
|
-
|
|
201
|
-
executor(this);
|
|
202
|
-
|
|
203
|
-
this._tree.walk(group => group.setSuspended(false), false, false);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
getSymbols() {
|
|
209
|
+
getPositionSymbols() {
|
|
207
210
|
return Object.keys(this._symbols);
|
|
208
211
|
}
|
|
209
212
|
|
|
210
|
-
|
|
211
|
-
|
|
213
|
+
setPositionPrice(symbol, price) {
|
|
214
|
+
assert.argumentIsOptional(symbol, 'symbol', String);
|
|
215
|
+
assert.argumentIsOptional(price, 'price', Number);
|
|
216
|
+
|
|
217
|
+
if (this._symbols.hasOwnProperty(symbol) && is.number(price)) {
|
|
212
218
|
this._symbols[symbol].forEach(item => item.setPrice(price));
|
|
213
219
|
}
|
|
214
220
|
}
|
|
215
221
|
|
|
216
|
-
|
|
222
|
+
getForexSymbols() {
|
|
217
223
|
const codes = Object.keys(this._currencies);
|
|
218
224
|
|
|
219
225
|
return codes.reduce((symbols, code) => {
|
|
@@ -225,28 +231,38 @@ module.exports = (() => {
|
|
|
225
231
|
}, [ ]);
|
|
226
232
|
}
|
|
227
233
|
|
|
228
|
-
|
|
234
|
+
setForexPrice(symbol, price) {
|
|
235
|
+
assert.argumentIsOptional(symbol, 'symbol', String);
|
|
236
|
+
assert.argumentIsOptional(price, 'price', Number);
|
|
229
237
|
|
|
238
|
+
return;
|
|
230
239
|
}
|
|
231
240
|
|
|
232
|
-
getGroup(keys) {
|
|
233
|
-
|
|
234
|
-
|
|
241
|
+
getGroup(name, keys) {
|
|
242
|
+
assert.argumentIsRequired(name, 'name', String);
|
|
243
|
+
assert.argumentIsArray(keys, 'keys', Number);
|
|
235
244
|
|
|
236
|
-
|
|
237
|
-
|
|
245
|
+
return findNode(this._trees[name], keys).getValue();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
getGroups(name, keys) {
|
|
249
|
+
assert.argumentIsRequired(name, 'name', String);
|
|
250
|
+
assert.argumentIsArray(keys, 'keys', Number);
|
|
238
251
|
|
|
239
|
-
return node.getValue();
|
|
252
|
+
return findNode(this._trees[name], keys).getChildren().map(node => node.getValue());
|
|
240
253
|
}
|
|
241
254
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
255
|
+
startTransaction(name, executor) {
|
|
256
|
+
assert.argumentIsRequired(name, 'name', String);
|
|
257
|
+
assert.argumentIsRequired(executor, 'executor', Function);
|
|
258
|
+
|
|
259
|
+
assert.argumentIsRequired(executor, 'executor', Function);
|
|
260
|
+
|
|
261
|
+
this._trees[name].walk(group => group.setSuspended(true), false, false);
|
|
245
262
|
|
|
246
|
-
|
|
247
|
-
}, this._tree);
|
|
263
|
+
executor(this);
|
|
248
264
|
|
|
249
|
-
|
|
265
|
+
this._trees[name].walk(group => group.setSuspended(false), false, false);
|
|
250
266
|
}
|
|
251
267
|
|
|
252
268
|
toString() {
|
|
@@ -254,6 +270,14 @@ module.exports = (() => {
|
|
|
254
270
|
}
|
|
255
271
|
}
|
|
256
272
|
|
|
273
|
+
function findNode(tree, keys) {
|
|
274
|
+
return keys.reduce((tree, key) => {
|
|
275
|
+
tree = tree.findChild(group => group.description === key);
|
|
276
|
+
|
|
277
|
+
return tree;
|
|
278
|
+
}, tree);
|
|
279
|
+
}
|
|
280
|
+
|
|
257
281
|
function getSummaryArray(ranges) {
|
|
258
282
|
return ranges.map(range => null);
|
|
259
283
|
}
|
|
@@ -12,13 +12,14 @@ module.exports = (() => {
|
|
|
12
12
|
* @public
|
|
13
13
|
*/
|
|
14
14
|
class PositionGroup {
|
|
15
|
-
constructor(container, parent, items, currency, description, single) {
|
|
15
|
+
constructor(container, parent, items, currency, key, description, single) {
|
|
16
16
|
this._container = container;
|
|
17
17
|
this._parent = parent || null;
|
|
18
18
|
|
|
19
19
|
this._items = items;
|
|
20
|
-
this._currency = currency;
|
|
20
|
+
this._currency = currency || Currency.CAD;
|
|
21
21
|
|
|
22
|
+
this._key = key;
|
|
22
23
|
this._description = description;
|
|
23
24
|
|
|
24
25
|
this._single = is.boolean(single) && single;
|
|
@@ -66,7 +67,7 @@ module.exports = (() => {
|
|
|
66
67
|
item.registerPriceChangeHandler((data, sender) => {
|
|
67
68
|
if (this._single) {
|
|
68
69
|
this._dataActual.currentPrice = data.currentPrice;
|
|
69
|
-
this._dataFormat.currentPrice =
|
|
70
|
+
this._dataFormat.currentPrice = formatCurrency(data.currentPrice, sender.position.instrument.currency);
|
|
70
71
|
} else {
|
|
71
72
|
this._dataActual.currentPrice = null;
|
|
72
73
|
this._dataFormat.currentPrice = null;
|
|
@@ -79,16 +80,20 @@ module.exports = (() => {
|
|
|
79
80
|
this.refresh();
|
|
80
81
|
}
|
|
81
82
|
|
|
82
|
-
get
|
|
83
|
-
return this.
|
|
83
|
+
get key() {
|
|
84
|
+
return this._key;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
get description() {
|
|
88
|
+
return this._description;
|
|
84
89
|
}
|
|
85
90
|
|
|
86
91
|
get currency() {
|
|
87
92
|
return this._currency;
|
|
88
93
|
}
|
|
89
94
|
|
|
90
|
-
get
|
|
91
|
-
return this.
|
|
95
|
+
get items() {
|
|
96
|
+
return this._items;
|
|
92
97
|
}
|
|
93
98
|
|
|
94
99
|
get data() {
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
const assert = require('@barchart/common-js/lang/assert'),
|
|
2
|
+
is = require('@barchart/common-js/lang/is');
|
|
3
|
+
|
|
4
|
+
module.exports = (() => {
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Defines a grouping level within a tree of positions. A level could represent a
|
|
9
|
+
* group of multiple positions (e.g. all equities or all positions for a portfolio).
|
|
10
|
+
* Alternately, a level could also represent a single position.
|
|
11
|
+
*
|
|
12
|
+
* @public
|
|
13
|
+
* @param {String} name
|
|
14
|
+
* @param {PositionLevelDefinition~keySelector} keySelector
|
|
15
|
+
* @param {PositionLevelDefinition~descriptionSelector} descriptionSelector
|
|
16
|
+
* @param {PositionLevelDefinition~currencySelector} currencySelector
|
|
17
|
+
* @param {Array.<String>=} requiredGroups
|
|
18
|
+
* @param {Boolean=} single
|
|
19
|
+
*/
|
|
20
|
+
class PositionLevelDefinition {
|
|
21
|
+
constructor(name, keySelector, descriptionSelector, currencySelector, requiredGroups, single) {
|
|
22
|
+
assert.argumentIsRequired(name, 'name', String);
|
|
23
|
+
assert.argumentIsRequired(keySelector, 'keySelector', Function);
|
|
24
|
+
assert.argumentIsRequired(descriptionSelector, 'descriptionSelector', Function);
|
|
25
|
+
assert.argumentIsRequired(currencySelector, 'currencySelector', Function);
|
|
26
|
+
|
|
27
|
+
if (requiredGroups) {
|
|
28
|
+
assert.argumentIsArray(requiredGroups, 'requiredGroups', String);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
assert.argumentIsOptional(single, 'single', Boolean);
|
|
32
|
+
|
|
33
|
+
this._name = name;
|
|
34
|
+
|
|
35
|
+
this._keySelector = keySelector;
|
|
36
|
+
this._descriptionSelector = descriptionSelector;
|
|
37
|
+
this._currencySelector = currencySelector;
|
|
38
|
+
|
|
39
|
+
this._requiredGroups = requiredGroups || [ ];
|
|
40
|
+
this._single = is.boolean(single) && single;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The name of the grouping level.
|
|
45
|
+
*
|
|
46
|
+
* @public
|
|
47
|
+
* @returns {String}
|
|
48
|
+
*/
|
|
49
|
+
get name() {
|
|
50
|
+
return this._name;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* A function, when given a {@link PositionItem} returns a string that is used
|
|
55
|
+
* to group {@link PositionItem} instances into different groups.
|
|
56
|
+
*
|
|
57
|
+
* @public
|
|
58
|
+
* @returns {PositionLevelDefinition~keySelector}
|
|
59
|
+
*/
|
|
60
|
+
get keySelector() {
|
|
61
|
+
return this._keySelector;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* A function, when given a {@link PositionItem} returns a string used to describe the
|
|
66
|
+
* group.
|
|
67
|
+
*
|
|
68
|
+
* @public
|
|
69
|
+
* @returns {PositionLevelDefinition~descriptionSelector}
|
|
70
|
+
*/
|
|
71
|
+
get descriptionSelector() {
|
|
72
|
+
return this._descriptionSelector;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* A function, when given a {@link PositionItem} returns the {@link Currency} used to
|
|
77
|
+
* display values for the group.
|
|
78
|
+
*
|
|
79
|
+
* @public
|
|
80
|
+
* @returns {PositionLevelDefinition~currencySelector}
|
|
81
|
+
*/
|
|
82
|
+
get currencySelector() {
|
|
83
|
+
return this._currencySelector;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Indicates the required groups (i.e. descriptions). The allows for the creation of empty
|
|
88
|
+
* groups.
|
|
89
|
+
*
|
|
90
|
+
* @public
|
|
91
|
+
* @returns {Array<String>}
|
|
92
|
+
*/
|
|
93
|
+
get requiredGroups() {
|
|
94
|
+
return this._requiredGroups;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Indicates if the grouping level is meant to only contain a single item.
|
|
99
|
+
*
|
|
100
|
+
* @public
|
|
101
|
+
* @returns {Boolean}
|
|
102
|
+
*/
|
|
103
|
+
get single() {
|
|
104
|
+
return this._single;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
toString() {
|
|
108
|
+
return '[PositionLevelDefinition]';
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* A callback used to determine the eligibility for membership of a {@link PositionItem}
|
|
114
|
+
* within a group.
|
|
115
|
+
*
|
|
116
|
+
* @public
|
|
117
|
+
* @callback PositionLevelDefinition~keySelector
|
|
118
|
+
* @param {PositionItem} session
|
|
119
|
+
* @returns {String}
|
|
120
|
+
*/
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* A callback used to determine the human-readable name of a group. This function should
|
|
124
|
+
* return the same value for any {@link PositionItem} in the group.
|
|
125
|
+
*
|
|
126
|
+
* @public
|
|
127
|
+
* @callback PositionLevelDefinition~descriptionSelector
|
|
128
|
+
* @param {PositionItem} session
|
|
129
|
+
* @returns {String}
|
|
130
|
+
*/
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* A callback used to determine the display {@link Currency} for the group. This function should
|
|
134
|
+
* return the same value for any {@link PositionItem} in the group.
|
|
135
|
+
*
|
|
136
|
+
* @public
|
|
137
|
+
* @callback PositionLevelDefinition~currencySelector
|
|
138
|
+
* @param {PositionItem} session
|
|
139
|
+
* @returns {Currency}
|
|
140
|
+
*/
|
|
141
|
+
|
|
142
|
+
return PositionLevelDefinition;
|
|
143
|
+
})();
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const assert = require('@barchart/common-js/lang/assert');
|
|
2
|
+
|
|
3
|
+
const PositionLevelDefinition = require('./PositionLevelDefinition');
|
|
4
|
+
|
|
5
|
+
module.exports = (() => {
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Defines the structure for a tree of positions.
|
|
10
|
+
*
|
|
11
|
+
* @public
|
|
12
|
+
* @param {String} name
|
|
13
|
+
* @param {Array.<PositionLevelDefinition>} definitions
|
|
14
|
+
*/
|
|
15
|
+
class PositionTreeDefinitions {
|
|
16
|
+
constructor(name, definitions) {
|
|
17
|
+
assert.argumentIsRequired(name, 'name', String);
|
|
18
|
+
assert.argumentIsArray(definitions, 'definitions', PositionLevelDefinition, 'PositionLevelDefinition');
|
|
19
|
+
|
|
20
|
+
this._name = name;
|
|
21
|
+
this._definitions = definitions;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The name of the tree.
|
|
26
|
+
*
|
|
27
|
+
* @returns {String}
|
|
28
|
+
*/
|
|
29
|
+
get name() {
|
|
30
|
+
return this._name;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* An ordered list of {@link PositionLevelDefinitions} that describes the
|
|
35
|
+
* levels of the tree. The first item represents the top-most level of the
|
|
36
|
+
* tree (i.e. the children of the root node) and the last item represents the
|
|
37
|
+
* bottom-most level of the tree (i.e. leaf nodes).
|
|
38
|
+
*
|
|
39
|
+
* @public
|
|
40
|
+
* @returns {Array.<PositionTreeDefinition>}
|
|
41
|
+
*/
|
|
42
|
+
get definitions() {
|
|
43
|
+
return this._definitions;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
toString() {
|
|
47
|
+
return '[PositionTreeDefinitions]';
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return PositionTreeDefinitions;
|
|
52
|
+
})();
|