@barchart/portfolio-api-common 1.0.94 → 1.0.98
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 +119 -98
- package/lib/processing/PositionGroup.js +4 -4
- 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 +354 -180
- 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,101 @@ 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];
|
|
128
|
-
|
|
129
|
-
return new PositionGroup(this, parent, items, currentDefinition.currencySelector(first), currentDefinition.descriptionSelector(first), currentDefinition.single && items.length === 1);
|
|
130
|
-
});
|
|
129
|
+
const createGroups = (currentTree, items, levelDefinitions) => {
|
|
130
|
+
if (levelDefinitions.length === 0) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
131
133
|
|
|
132
|
-
|
|
134
|
+
const parent = currentTree.getValue() || null;
|
|
133
135
|
|
|
134
|
-
|
|
135
|
-
return new PositionGroup(this, parent, [ ], currentDefinition.requiredGroups.find(group => group.description === description).currency, description);
|
|
136
|
-
});
|
|
136
|
+
const levelDefinition = levelDefinitions[0];
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
const populatedObjects = array.groupBy(items, levelDefinition.keySelector);
|
|
139
|
+
const populatedGroups = Object.keys(populatedObjects).map(key => populatedObjects[key]).map((items) => {
|
|
140
|
+
const first = items[0];
|
|
139
141
|
|
|
140
|
-
|
|
142
|
+
return new PositionGroup(this, parent, items, levelDefinition.currencySelector(first), levelDefinition.descriptionSelector(first), levelDefinition.single && items.length === 1);
|
|
143
|
+
});
|
|
141
144
|
|
|
142
|
-
|
|
143
|
-
const ordering = currentDefinition.requiredGroups.reduce((map, group, index) => {
|
|
144
|
-
map[group.description] = index;
|
|
145
|
+
const missingGroups = array.difference(levelDefinition.requiredGroups.map(group => group.description), populatedGroups.map(group => group.description));
|
|
145
146
|
|
|
146
|
-
|
|
147
|
-
|
|
147
|
+
const empty = missingGroups.map((description) => {
|
|
148
|
+
return new PositionGroup(this, parent, [ ], levelDefinition.requiredGroups.find(group => group.description === description).currency, description);
|
|
149
|
+
});
|
|
148
150
|
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
151
|
+
const compositeGroups = populatedGroups.concat(empty);
|
|
152
|
+
|
|
153
|
+
let builder;
|
|
154
|
+
|
|
155
|
+
if (levelDefinition.requiredGroups.length !== 0) {
|
|
156
|
+
const ordering = levelDefinition.requiredGroups.reduce((map, group, index) => {
|
|
157
|
+
map[group.description] = index;
|
|
158
|
+
|
|
159
|
+
return map;
|
|
160
|
+
}, { });
|
|
161
|
+
|
|
162
|
+
const getIndex = (description) => {
|
|
163
|
+
if (ordering.hasOwnProperty(description)) {
|
|
164
|
+
return ordering[description];
|
|
165
|
+
} else {
|
|
166
|
+
return Number.MAX_VALUE;
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
builder = ComparatorBuilder.startWith((a, b) => {
|
|
171
|
+
return comparators.compareNumbers(getIndex(a.description), getIndex(b.description));
|
|
172
|
+
}).thenBy((a, b) => {
|
|
173
|
+
return comparators.compareStrings(a.description, b.description);
|
|
174
|
+
});
|
|
175
|
+
} else {
|
|
176
|
+
builder = ComparatorBuilder.startWith((a, b) => {
|
|
177
|
+
return comparators.compareStrings(a.description, b.description);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
156
180
|
|
|
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
|
-
}
|
|
181
|
+
compositeGroups.sort(builder.toComparator());
|
|
167
182
|
|
|
168
|
-
|
|
183
|
+
compositeGroups.forEach((group) => {
|
|
184
|
+
const childTree = currentTree.addChild(group);
|
|
169
185
|
|
|
170
|
-
|
|
171
|
-
|
|
186
|
+
group.registerMarketPercentChangeHandler(() => {
|
|
187
|
+
currentTree.walk((childGroup) => childGroup.refreshMarketPercent());
|
|
188
|
+
});
|
|
172
189
|
|
|
173
|
-
|
|
174
|
-
this._tree.walk((childGroup) => childGroup.refreshMarketPercent());
|
|
190
|
+
createGroups(childTree, group.items, array.dropLeft(levelDefinitions));
|
|
175
191
|
});
|
|
192
|
+
};
|
|
176
193
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
194
|
+
createGroups(tree, this._items, treeDefinition.definitions);
|
|
195
|
+
|
|
196
|
+
map[treeDefinition.name] = tree;
|
|
180
197
|
|
|
181
|
-
|
|
198
|
+
return map;
|
|
199
|
+
}, { });
|
|
182
200
|
}
|
|
183
201
|
|
|
184
202
|
get defaultCurrency() {
|
|
185
203
|
return this._defaultCurrency;
|
|
186
204
|
}
|
|
187
205
|
|
|
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() {
|
|
206
|
+
getPositionSymbols() {
|
|
207
207
|
return Object.keys(this._symbols);
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
-
|
|
211
|
-
|
|
210
|
+
setPositionPrice(symbol, price) {
|
|
211
|
+
assert.argumentIsOptional(symbol, 'symbol', String);
|
|
212
|
+
assert.argumentIsOptional(price, 'price', Number);
|
|
213
|
+
|
|
214
|
+
if (this._symbols.hasOwnProperty(symbol) && is.number(price)) {
|
|
212
215
|
this._symbols[symbol].forEach(item => item.setPrice(price));
|
|
213
216
|
}
|
|
214
217
|
}
|
|
215
218
|
|
|
216
|
-
|
|
219
|
+
getForexSymbols() {
|
|
217
220
|
const codes = Object.keys(this._currencies);
|
|
218
221
|
|
|
219
222
|
return codes.reduce((symbols, code) => {
|
|
@@ -225,28 +228,38 @@ module.exports = (() => {
|
|
|
225
228
|
}, [ ]);
|
|
226
229
|
}
|
|
227
230
|
|
|
228
|
-
|
|
231
|
+
setForexPrice(symbol, price) {
|
|
232
|
+
assert.argumentIsOptional(symbol, 'symbol', String);
|
|
233
|
+
assert.argumentIsOptional(price, 'price', Number);
|
|
229
234
|
|
|
235
|
+
return;
|
|
230
236
|
}
|
|
231
237
|
|
|
232
|
-
getGroup(keys) {
|
|
233
|
-
|
|
234
|
-
|
|
238
|
+
getGroup(name, keys) {
|
|
239
|
+
assert.argumentIsRequired(name, 'name', String);
|
|
240
|
+
assert.argumentIsArray(keys, 'keys', Number);
|
|
235
241
|
|
|
236
|
-
|
|
237
|
-
|
|
242
|
+
return findNode(this._trees[name], keys).getValue();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
getGroups(name, keys) {
|
|
246
|
+
assert.argumentIsRequired(name, 'name', String);
|
|
247
|
+
assert.argumentIsArray(keys, 'keys', Number);
|
|
238
248
|
|
|
239
|
-
return node.getValue();
|
|
249
|
+
return findNode(this._trees[name], keys).getChildren().map(node => node.getValue());
|
|
240
250
|
}
|
|
241
251
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
252
|
+
startTransaction(name, executor) {
|
|
253
|
+
assert.argumentIsRequired(name, 'name', String);
|
|
254
|
+
assert.argumentIsRequired(executor, 'executor', Function);
|
|
255
|
+
|
|
256
|
+
assert.argumentIsRequired(executor, 'executor', Function);
|
|
257
|
+
|
|
258
|
+
this._trees[name].walk(group => group.setSuspended(true), false, false);
|
|
245
259
|
|
|
246
|
-
|
|
247
|
-
}, this._tree);
|
|
260
|
+
executor(this);
|
|
248
261
|
|
|
249
|
-
|
|
262
|
+
this._trees[name].walk(group => group.setSuspended(false), false, false);
|
|
250
263
|
}
|
|
251
264
|
|
|
252
265
|
toString() {
|
|
@@ -254,6 +267,14 @@ module.exports = (() => {
|
|
|
254
267
|
}
|
|
255
268
|
}
|
|
256
269
|
|
|
270
|
+
function findNode(tree, keys) {
|
|
271
|
+
return keys.reduce((tree, key) => {
|
|
272
|
+
tree = tree.findChild(group => group.description === key);
|
|
273
|
+
|
|
274
|
+
return tree;
|
|
275
|
+
}, tree);
|
|
276
|
+
}
|
|
277
|
+
|
|
257
278
|
function getSummaryArray(ranges) {
|
|
258
279
|
return ranges.map(range => null);
|
|
259
280
|
}
|
|
@@ -17,7 +17,7 @@ module.exports = (() => {
|
|
|
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
22
|
this._description = description;
|
|
23
23
|
|
|
@@ -60,14 +60,13 @@ module.exports = (() => {
|
|
|
60
60
|
this._dataFormat.summaryTotalCurrent = null;
|
|
61
61
|
this._dataActual.summaryTotalCurrentNegative = false;
|
|
62
62
|
this._dataFormat.summaryTotalPrevious = null;
|
|
63
|
-
|
|
64
|
-
this._dataFormat.unrealizedTodayNegative = false;
|
|
63
|
+
this._dataFormat.summaryTotalPreviousNegative = false;
|
|
65
64
|
|
|
66
65
|
this._items.forEach((item) => {
|
|
67
66
|
item.registerPriceChangeHandler((data, sender) => {
|
|
68
67
|
if (this._single) {
|
|
69
68
|
this._dataActual.currentPrice = data.currentPrice;
|
|
70
|
-
this._dataFormat.currentPrice =
|
|
69
|
+
this._dataFormat.currentPrice = formatCurrency(data.currentPrice, sender.position.instrument.currency);
|
|
71
70
|
} else {
|
|
72
71
|
this._dataActual.currentPrice = null;
|
|
73
72
|
this._dataFormat.currentPrice = null;
|
|
@@ -211,6 +210,7 @@ module.exports = (() => {
|
|
|
211
210
|
format.income = formatCurrency(actual.income, currency);
|
|
212
211
|
format.summaryTotalCurrent = formatCurrency(updates.summaryTotalCurrent, currency);
|
|
213
212
|
format.summaryTotalPrevious = formatCurrency(updates.summaryTotalPrevious, currency);
|
|
213
|
+
format.summaryTotalPreviousNegative = updates.summaryTotalPrevious.getIsNegative();
|
|
214
214
|
}
|
|
215
215
|
|
|
216
216
|
function calculatePriceData(group, item, forceRefresh) {
|
|
@@ -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
|
+
})();
|