@barchart/portfolio-api-common 1.0.96 → 1.0.100
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 +124 -97
- package/lib/processing/PositionGroup.js +15 -9
- package/lib/processing/definitions/PositionLevelDefinition.js +154 -0
- package/lib/processing/definitions/PositionTreeDefinition.js +52 -0
- package/package.json +1 -1
- package/test/SpecRunner.js +382 -185
- 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,107 @@ 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
|
-
|
|
144
|
-
|
|
148
|
+
const missingGroups = array.difference(levelDefinition.requiredGroups.map(group => group.key), populatedGroups.map(group => group.key))
|
|
149
|
+
.map((key) => {
|
|
150
|
+
return levelDefinition.requiredGroups.find(g => g.key === key);
|
|
151
|
+
});
|
|
145
152
|
|
|
146
|
-
|
|
147
|
-
|
|
153
|
+
const empty = missingGroups.map((group) => {
|
|
154
|
+
return new PositionGroup(this, parent, [ ], group.currency, group.key, group.description);
|
|
155
|
+
});
|
|
148
156
|
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
157
|
+
const compositeGroups = populatedGroups.concat(empty);
|
|
158
|
+
|
|
159
|
+
let builder;
|
|
160
|
+
|
|
161
|
+
if (levelDefinition.requiredGroups.length !== 0) {
|
|
162
|
+
const ordering = levelDefinition.requiredGroups.reduce((map, group, index) => {
|
|
163
|
+
map[group.description] = index;
|
|
164
|
+
|
|
165
|
+
return map;
|
|
166
|
+
}, { });
|
|
167
|
+
|
|
168
|
+
const getIndex = (description) => {
|
|
169
|
+
if (ordering.hasOwnProperty(description)) {
|
|
170
|
+
return ordering[description];
|
|
171
|
+
} else {
|
|
172
|
+
return Number.MAX_VALUE;
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
builder = ComparatorBuilder.startWith((a, b) => {
|
|
177
|
+
return comparators.compareNumbers(getIndex(a.description), getIndex(b.description));
|
|
178
|
+
}).thenBy((a, b) => {
|
|
179
|
+
return comparators.compareStrings(a.description, b.description);
|
|
180
|
+
});
|
|
181
|
+
} else {
|
|
182
|
+
builder = ComparatorBuilder.startWith((a, b) => {
|
|
183
|
+
return comparators.compareStrings(a.description, b.description);
|
|
184
|
+
});
|
|
185
|
+
}
|
|
156
186
|
|
|
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
|
-
}
|
|
187
|
+
compositeGroups.sort(builder.toComparator());
|
|
167
188
|
|
|
168
|
-
|
|
189
|
+
compositeGroups.forEach((group) => {
|
|
190
|
+
const childTree = currentTree.addChild(group);
|
|
169
191
|
|
|
170
|
-
|
|
171
|
-
|
|
192
|
+
group.registerMarketPercentChangeHandler(() => {
|
|
193
|
+
currentTree.walk((childGroup) => childGroup.refreshMarketPercent());
|
|
194
|
+
});
|
|
172
195
|
|
|
173
|
-
|
|
174
|
-
this._tree.walk((childGroup) => childGroup.refreshMarketPercent());
|
|
196
|
+
createGroups(childTree, group.items, array.dropLeft(levelDefinitions));
|
|
175
197
|
});
|
|
198
|
+
};
|
|
176
199
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
200
|
+
createGroups(tree, this._items, treeDefinition.definitions);
|
|
201
|
+
|
|
202
|
+
map[treeDefinition.name] = tree;
|
|
180
203
|
|
|
181
|
-
|
|
204
|
+
return map;
|
|
205
|
+
}, { });
|
|
182
206
|
}
|
|
183
207
|
|
|
184
208
|
get defaultCurrency() {
|
|
185
209
|
return this._defaultCurrency;
|
|
186
210
|
}
|
|
187
211
|
|
|
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() {
|
|
212
|
+
getPositionSymbols() {
|
|
207
213
|
return Object.keys(this._symbols);
|
|
208
214
|
}
|
|
209
215
|
|
|
210
|
-
|
|
211
|
-
|
|
216
|
+
setPositionPrice(symbol, price) {
|
|
217
|
+
assert.argumentIsOptional(symbol, 'symbol', String);
|
|
218
|
+
assert.argumentIsOptional(price, 'price', Number);
|
|
219
|
+
|
|
220
|
+
if (this._symbols.hasOwnProperty(symbol) && is.number(price)) {
|
|
212
221
|
this._symbols[symbol].forEach(item => item.setPrice(price));
|
|
213
222
|
}
|
|
214
223
|
}
|
|
215
224
|
|
|
216
|
-
|
|
225
|
+
getForexSymbols() {
|
|
217
226
|
const codes = Object.keys(this._currencies);
|
|
218
227
|
|
|
219
228
|
return codes.reduce((symbols, code) => {
|
|
@@ -225,28 +234,38 @@ module.exports = (() => {
|
|
|
225
234
|
}, [ ]);
|
|
226
235
|
}
|
|
227
236
|
|
|
228
|
-
|
|
237
|
+
setForexPrice(symbol, price) {
|
|
238
|
+
assert.argumentIsOptional(symbol, 'symbol', String);
|
|
239
|
+
assert.argumentIsOptional(price, 'price', Number);
|
|
229
240
|
|
|
241
|
+
return;
|
|
230
242
|
}
|
|
231
243
|
|
|
232
|
-
getGroup(keys) {
|
|
233
|
-
|
|
234
|
-
|
|
244
|
+
getGroup(name, keys) {
|
|
245
|
+
assert.argumentIsRequired(name, 'name', String);
|
|
246
|
+
assert.argumentIsArray(keys, 'keys', Number);
|
|
235
247
|
|
|
236
|
-
|
|
237
|
-
|
|
248
|
+
return findNode(this._trees[name], keys).getValue();
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
getGroups(name, keys) {
|
|
252
|
+
assert.argumentIsRequired(name, 'name', String);
|
|
253
|
+
assert.argumentIsArray(keys, 'keys', Number);
|
|
238
254
|
|
|
239
|
-
return node.getValue();
|
|
255
|
+
return findNode(this._trees[name], keys).getChildren().map(node => node.getValue());
|
|
240
256
|
}
|
|
241
257
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
258
|
+
startTransaction(name, executor) {
|
|
259
|
+
assert.argumentIsRequired(name, 'name', String);
|
|
260
|
+
assert.argumentIsRequired(executor, 'executor', Function);
|
|
261
|
+
|
|
262
|
+
assert.argumentIsRequired(executor, 'executor', Function);
|
|
263
|
+
|
|
264
|
+
this._trees[name].walk(group => group.setSuspended(true), false, false);
|
|
245
265
|
|
|
246
|
-
|
|
247
|
-
}, this._tree);
|
|
266
|
+
executor(this);
|
|
248
267
|
|
|
249
|
-
|
|
268
|
+
this._trees[name].walk(group => group.setSuspended(false), false, false);
|
|
250
269
|
}
|
|
251
270
|
|
|
252
271
|
toString() {
|
|
@@ -254,6 +273,14 @@ module.exports = (() => {
|
|
|
254
273
|
}
|
|
255
274
|
}
|
|
256
275
|
|
|
276
|
+
function findNode(tree, keys) {
|
|
277
|
+
return keys.reduce((tree, key) => {
|
|
278
|
+
tree = tree.findChild(group => group.description === key);
|
|
279
|
+
|
|
280
|
+
return tree;
|
|
281
|
+
}, tree);
|
|
282
|
+
}
|
|
283
|
+
|
|
257
284
|
function getSummaryArray(ranges) {
|
|
258
285
|
return ranges.map(range => null);
|
|
259
286
|
}
|
|
@@ -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;
|
|
@@ -30,9 +31,10 @@ module.exports = (() => {
|
|
|
30
31
|
|
|
31
32
|
this._dataFormat = { };
|
|
32
33
|
this._dataActual = { };
|
|
33
|
-
|
|
34
|
+
|
|
35
|
+
this._dataFormat.key = this._key;
|
|
34
36
|
this._dataFormat.description = this._description;
|
|
35
|
-
|
|
37
|
+
|
|
36
38
|
this._dataActual.currentPrice = null;
|
|
37
39
|
this._dataActual.previousPrice = null;
|
|
38
40
|
this._dataActual.basis = null;
|
|
@@ -66,7 +68,7 @@ module.exports = (() => {
|
|
|
66
68
|
item.registerPriceChangeHandler((data, sender) => {
|
|
67
69
|
if (this._single) {
|
|
68
70
|
this._dataActual.currentPrice = data.currentPrice;
|
|
69
|
-
this._dataFormat.currentPrice =
|
|
71
|
+
this._dataFormat.currentPrice = formatCurrency(data.currentPrice, sender.position.instrument.currency);
|
|
70
72
|
} else {
|
|
71
73
|
this._dataActual.currentPrice = null;
|
|
72
74
|
this._dataFormat.currentPrice = null;
|
|
@@ -79,16 +81,20 @@ module.exports = (() => {
|
|
|
79
81
|
this.refresh();
|
|
80
82
|
}
|
|
81
83
|
|
|
82
|
-
get
|
|
83
|
-
return this.
|
|
84
|
+
get key() {
|
|
85
|
+
return this._key;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
get description() {
|
|
89
|
+
return this._description;
|
|
84
90
|
}
|
|
85
91
|
|
|
86
92
|
get currency() {
|
|
87
93
|
return this._currency;
|
|
88
94
|
}
|
|
89
95
|
|
|
90
|
-
get
|
|
91
|
-
return this.
|
|
96
|
+
get items() {
|
|
97
|
+
return this._items;
|
|
92
98
|
}
|
|
93
99
|
|
|
94
100
|
get data() {
|
|
@@ -0,0 +1,154 @@
|
|
|
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.<PositionLevelDefinition~RequiredGroup>=} 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
|
+
/**
|
|
143
|
+
* The data required to construct a group.
|
|
144
|
+
*
|
|
145
|
+
* @public
|
|
146
|
+
* @typedef PositionLevelDefinition~RequiredGroup
|
|
147
|
+
* @type {Object}
|
|
148
|
+
* @property {String} key
|
|
149
|
+
* @property {String} description
|
|
150
|
+
* @property {Currency} currency
|
|
151
|
+
*/
|
|
152
|
+
|
|
153
|
+
return PositionLevelDefinition;
|
|
154
|
+
})();
|
|
@@ -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
|
+
})();
|