@barchart/portfolio-api-common 1.0.71 → 1.0.75
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/data/InstrumentType.js +28 -5
- package/lib/data/PositionSummaryFrame.js +1 -1
- package/lib/processing/PositionContainer.js +13 -3
- package/lib/processing/PositionGroup.js +60 -11
- package/lib/processing/PositionItem.js +21 -2
- package/lib/serialization/PortfolioSchema.js +1 -0
- package/lib/serialization/TransactionSchema.js +0 -3
- package/package.json +1 -1
- package/test/SpecRunner.js +123 -22
|
@@ -13,15 +13,23 @@ module.exports = (() => {
|
|
|
13
13
|
* @param {String} alternateDescription
|
|
14
14
|
* @param {String} code
|
|
15
15
|
* @param {Boolean} canReinvest
|
|
16
|
+
* @param {Boolean} usesSymbols
|
|
16
17
|
*/
|
|
17
18
|
class InstrumentType extends Enum {
|
|
18
|
-
constructor(code, description, alternateDescription, canReinvest) {
|
|
19
|
+
constructor(code, description, alternateDescription, canReinvest, usesSymbols) {
|
|
19
20
|
super(code, description);
|
|
20
21
|
|
|
21
22
|
this._alternateDescription = alternateDescription;
|
|
22
23
|
this._canReinvest = canReinvest;
|
|
24
|
+
this._usesSymbols = usesSymbols;
|
|
23
25
|
}
|
|
24
26
|
|
|
27
|
+
/**
|
|
28
|
+
* A human-readable description.
|
|
29
|
+
*
|
|
30
|
+
* @public
|
|
31
|
+
* @return {String}
|
|
32
|
+
*/
|
|
25
33
|
get alternateDescription() {
|
|
26
34
|
return this._alternateDescription;
|
|
27
35
|
}
|
|
@@ -29,16 +37,28 @@ module.exports = (() => {
|
|
|
29
37
|
/**
|
|
30
38
|
* Indicates if the instrument type allows automatic reinvestment.
|
|
31
39
|
*
|
|
40
|
+
* @public
|
|
32
41
|
* @returns {Boolean}
|
|
33
42
|
*/
|
|
34
43
|
get canReinvest() {
|
|
35
44
|
return this._canReinvest;
|
|
36
45
|
}
|
|
37
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Indicates if an instrument of this type can be represented by a symbol.
|
|
49
|
+
*
|
|
50
|
+
* @public
|
|
51
|
+
* @returns {Boolean}
|
|
52
|
+
*/
|
|
53
|
+
get usesSymbols() {
|
|
54
|
+
return this._usesSymbols;
|
|
55
|
+
}
|
|
56
|
+
|
|
38
57
|
/**
|
|
39
58
|
* Cash.
|
|
40
59
|
*
|
|
41
60
|
* @public
|
|
61
|
+
* @static
|
|
42
62
|
* @returns {InstrumentType}
|
|
43
63
|
*/
|
|
44
64
|
static get CASH() {
|
|
@@ -49,6 +69,7 @@ module.exports = (() => {
|
|
|
49
69
|
* An equity issue.
|
|
50
70
|
*
|
|
51
71
|
* @public
|
|
72
|
+
* @static
|
|
52
73
|
* @returns {InstrumentType}
|
|
53
74
|
*/
|
|
54
75
|
static get EQUITY() {
|
|
@@ -59,6 +80,7 @@ module.exports = (() => {
|
|
|
59
80
|
* A mutual fund.
|
|
60
81
|
*
|
|
61
82
|
* @public
|
|
83
|
+
* @static
|
|
62
84
|
* @returns {InstrumentType}
|
|
63
85
|
*/
|
|
64
86
|
static get FUND() {
|
|
@@ -69,6 +91,7 @@ module.exports = (() => {
|
|
|
69
91
|
* An undefined asset (e.g. a house, or a collectible, or a salvaged alien spaceship).
|
|
70
92
|
*
|
|
71
93
|
* @public
|
|
94
|
+
* @static
|
|
72
95
|
* @returns {InstrumentType}
|
|
73
96
|
*/
|
|
74
97
|
static get OTHER() {
|
|
@@ -80,10 +103,10 @@ module.exports = (() => {
|
|
|
80
103
|
}
|
|
81
104
|
}
|
|
82
105
|
|
|
83
|
-
const cash = new InstrumentType('CASH', 'cash', 'Cash', false);
|
|
84
|
-
const equity = new InstrumentType('EQUITY', 'equity', 'Equities', true);
|
|
85
|
-
const fund = new InstrumentType('FUND', 'mutual fund', 'Funds', true);
|
|
86
|
-
const other = new InstrumentType('OTHER', 'other', 'Other', false);
|
|
106
|
+
const cash = new InstrumentType('CASH', 'cash', 'Cash', false, false);
|
|
107
|
+
const equity = new InstrumentType('EQUITY', 'equity', 'Equities', true, true);
|
|
108
|
+
const fund = new InstrumentType('FUND', 'mutual fund', 'Funds', true, true);
|
|
109
|
+
const other = new InstrumentType('OTHER', 'other', 'Other', false, false);
|
|
87
110
|
|
|
88
111
|
return InstrumentType;
|
|
89
112
|
})();
|
|
@@ -109,13 +109,13 @@ module.exports = (() => {
|
|
|
109
109
|
const populatedGroups = Object.keys(populatedObjects).map(key => populatedObjects[key]).map((items) => {
|
|
110
110
|
const first = items[0];
|
|
111
111
|
|
|
112
|
-
return new PositionGroup(parent, items, currentDefinition.currencySelector(first), currentDefinition.descriptionSelector(first), currentDefinition.single && items.length === 1);
|
|
112
|
+
return new PositionGroup(this, parent, items, currentDefinition.currencySelector(first), currentDefinition.descriptionSelector(first), currentDefinition.single && items.length === 1);
|
|
113
113
|
});
|
|
114
114
|
|
|
115
115
|
const missingGroups = array.difference(currentDefinition.requiredGroups.map(group => group.description), populatedGroups.map(group => group.description));
|
|
116
116
|
|
|
117
117
|
const empty = missingGroups.map((description) => {
|
|
118
|
-
return new PositionGroup(parent, [ ], currentDefinition.requiredGroups.find(group => group.description === description).currency, description);
|
|
118
|
+
return new PositionGroup(this, parent, [ ], currentDefinition.requiredGroups.find(group => group.description === description).currency, description);
|
|
119
119
|
});
|
|
120
120
|
|
|
121
121
|
const compositeGroups = populatedGroups.concat(empty);
|
|
@@ -186,10 +186,20 @@ module.exports = (() => {
|
|
|
186
186
|
}, [ ]);
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
-
|
|
189
|
+
setExchangeRate(symbol, price) {
|
|
190
190
|
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
startTransaction(executor) {
|
|
194
|
+
assert.argumentIsRequired(executor, 'executor', Function);
|
|
195
|
+
|
|
196
|
+
this._tree.walk(group => group.setSuspended(true), false, false);
|
|
197
|
+
|
|
198
|
+
executor(this);
|
|
199
|
+
|
|
200
|
+
this._tree.walk(group => group.setSuspended(false), false, false);
|
|
201
|
+
}
|
|
202
|
+
|
|
193
203
|
getGroup(keys) {
|
|
194
204
|
const node = keys.reduce((tree, key) => {
|
|
195
205
|
tree = tree.findChild(group => group.description === key);
|
|
@@ -11,7 +11,8 @@ module.exports = (() => {
|
|
|
11
11
|
* @public
|
|
12
12
|
*/
|
|
13
13
|
class PositionGroup {
|
|
14
|
-
constructor(parent, items, currency, description, single) {
|
|
14
|
+
constructor(container, parent, items, currency, description, single) {
|
|
15
|
+
this._container = container;
|
|
15
16
|
this._parent = parent || null;
|
|
16
17
|
|
|
17
18
|
this._items = items;
|
|
@@ -21,6 +22,9 @@ module.exports = (() => {
|
|
|
21
22
|
|
|
22
23
|
this._single = is.boolean(single) && single;
|
|
23
24
|
|
|
25
|
+
this._excluded = false;
|
|
26
|
+
this._suspended = false;
|
|
27
|
+
|
|
24
28
|
this._dataFormat = { };
|
|
25
29
|
this._dataActual = { };
|
|
26
30
|
|
|
@@ -62,12 +66,11 @@ module.exports = (() => {
|
|
|
62
66
|
this._dataFormat.currentPrice = null;
|
|
63
67
|
}
|
|
64
68
|
|
|
65
|
-
calculatePriceData(this, sender);
|
|
69
|
+
calculatePriceData(this, sender, false);
|
|
66
70
|
});
|
|
67
71
|
});
|
|
68
72
|
|
|
69
|
-
|
|
70
|
-
calculatePriceData(this);
|
|
73
|
+
this.refresh();
|
|
71
74
|
}
|
|
72
75
|
|
|
73
76
|
get items() {
|
|
@@ -90,6 +93,41 @@ module.exports = (() => {
|
|
|
90
93
|
return this._single;
|
|
91
94
|
}
|
|
92
95
|
|
|
96
|
+
get suspended() {
|
|
97
|
+
return this._suspended;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
get excluded() {
|
|
101
|
+
return this._excluded;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
setExcluded(value) {
|
|
105
|
+
assert.argumentIsRequired(value, 'value', Boolean);
|
|
106
|
+
|
|
107
|
+
if (this._excluded !== value) {
|
|
108
|
+
this._container.startTransaction(() => {
|
|
109
|
+
this._items.forEach((item) => {
|
|
110
|
+
item.setExcluded(value);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
setSuspended(value) {
|
|
117
|
+
assert.argumentIsRequired(value, 'value', Boolean);
|
|
118
|
+
|
|
119
|
+
if (this._suspended !== value) {
|
|
120
|
+
if (this._suspended = value) {
|
|
121
|
+
this.refresh();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
refresh() {
|
|
127
|
+
calculateStaticData(this);
|
|
128
|
+
calculatePriceData(this, null, true);
|
|
129
|
+
}
|
|
130
|
+
|
|
93
131
|
toString() {
|
|
94
132
|
return '[PositionGroup]';
|
|
95
133
|
}
|
|
@@ -116,6 +154,10 @@ module.exports = (() => {
|
|
|
116
154
|
}
|
|
117
155
|
|
|
118
156
|
function calculateStaticData(group) {
|
|
157
|
+
if (group.suspended) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
119
161
|
const actual = group._dataActual;
|
|
120
162
|
const format = group._dataFormat;
|
|
121
163
|
|
|
@@ -152,7 +194,11 @@ module.exports = (() => {
|
|
|
152
194
|
format.summaryTwoTotal = formatCurrency(updates.summaryTwoTotal, currency);
|
|
153
195
|
}
|
|
154
196
|
|
|
155
|
-
function calculatePriceData(group, item) {
|
|
197
|
+
function calculatePriceData(group, item, forceRefresh) {
|
|
198
|
+
if (group.suspended) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
156
202
|
const parent = group._parent;
|
|
157
203
|
|
|
158
204
|
const actual = group._dataActual;
|
|
@@ -160,14 +206,11 @@ module.exports = (() => {
|
|
|
160
206
|
|
|
161
207
|
const currency = group.currency;
|
|
162
208
|
|
|
209
|
+
const refresh = (is.boolean(forceRefresh) && forceRefresh) || (actual.market === null || actual.unrealizedToday === null || actual.total === null);
|
|
210
|
+
|
|
163
211
|
let updates;
|
|
164
212
|
|
|
165
|
-
if (
|
|
166
|
-
updates = {
|
|
167
|
-
market: actual.market.add(item.data.marketChange),
|
|
168
|
-
unrealizedToday: actual.unrealizedToday.add(item.data.unrealizedTodayChange)
|
|
169
|
-
};
|
|
170
|
-
} else {
|
|
213
|
+
if (refresh) {
|
|
171
214
|
const items = group._items;
|
|
172
215
|
|
|
173
216
|
updates = items.reduce((updates, item) => {
|
|
@@ -177,8 +220,14 @@ module.exports = (() => {
|
|
|
177
220
|
return updates;
|
|
178
221
|
}, {
|
|
179
222
|
market: Decimal.ZERO,
|
|
223
|
+
|
|
180
224
|
unrealizedToday: Decimal.ZERO
|
|
181
225
|
});
|
|
226
|
+
} else {
|
|
227
|
+
updates = {
|
|
228
|
+
market: actual.market.add(item.data.marketChange),
|
|
229
|
+
unrealizedToday: actual.unrealizedToday.add(item.data.unrealizedTodayChange)
|
|
230
|
+
};
|
|
182
231
|
}
|
|
183
232
|
|
|
184
233
|
if (parent !== null) {
|
|
@@ -33,10 +33,13 @@ module.exports = (() => {
|
|
|
33
33
|
this._data.realized = null;
|
|
34
34
|
this._data.income = null;
|
|
35
35
|
|
|
36
|
+
this._excluded = false;
|
|
37
|
+
|
|
36
38
|
calculateStaticData(this);
|
|
37
39
|
calculatePriceData(this, null);
|
|
38
40
|
|
|
39
41
|
this._priceChangeEvent = new Event(this);
|
|
42
|
+
this._excludedChangeEvent = new Event(this);
|
|
40
43
|
}
|
|
41
44
|
|
|
42
45
|
get portfolio() {
|
|
@@ -55,7 +58,13 @@ module.exports = (() => {
|
|
|
55
58
|
return this._data;
|
|
56
59
|
}
|
|
57
60
|
|
|
61
|
+
get excluded() {
|
|
62
|
+
return this._excluded;
|
|
63
|
+
}
|
|
64
|
+
|
|
58
65
|
setPrice(price) {
|
|
66
|
+
assert.argumentIsRequired(price, 'price', Number);
|
|
67
|
+
|
|
59
68
|
if (this._data.price !== price) {
|
|
60
69
|
calculatePriceData(this, this._data.currentPrice = price);
|
|
61
70
|
|
|
@@ -63,12 +72,22 @@ module.exports = (() => {
|
|
|
63
72
|
}
|
|
64
73
|
}
|
|
65
74
|
|
|
66
|
-
|
|
67
|
-
assert.argumentIsRequired(
|
|
75
|
+
setExcluded(value) {
|
|
76
|
+
assert.argumentIsRequired(value, 'value', Boolean);
|
|
68
77
|
|
|
78
|
+
if (this._excluded !== value) {
|
|
79
|
+
this._excludedChangeEvent.fire(this, this._excluded = value);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
registerPriceChangeHandler(handler) {
|
|
69
84
|
this._priceChangeEvent.register(handler);
|
|
70
85
|
}
|
|
71
86
|
|
|
87
|
+
registerExcludedChangeHandler(handler) {
|
|
88
|
+
this._excludedChangeEvent.register(handler);
|
|
89
|
+
}
|
|
90
|
+
|
|
72
91
|
toString() {
|
|
73
92
|
return '[PositionItem]';
|
|
74
93
|
}
|
|
@@ -155,6 +155,7 @@ module.exports = (() => {
|
|
|
155
155
|
);
|
|
156
156
|
|
|
157
157
|
const update = new PortfolioSchema(SchemaBuilder.withName('update')
|
|
158
|
+
.withField('portfolio', DataType.STRING)
|
|
158
159
|
.withField('name', DataType.STRING)
|
|
159
160
|
.withField('timezone', DataType.forEnum(Timezones, 'Timezone'), true)
|
|
160
161
|
.withField('defaults.currency', DataType.forEnum(Currency, 'Currency'), true)
|
|
@@ -388,11 +388,8 @@ module.exports = (() => {
|
|
|
388
388
|
.withField('portfolio', DataType.STRING)
|
|
389
389
|
.withField('position', DataType.STRING)
|
|
390
390
|
.withField('type', DataType.forEnum(TransactionType, 'TransactionType'))
|
|
391
|
-
.withField('instrument.name', DataType.STRING, true)
|
|
392
391
|
.withField('instrument.type', DataType.STRING, true)
|
|
393
392
|
.withField('instrument.currency', DataType.forEnum(Currency, 'Currency'), true)
|
|
394
|
-
.withField('instrument.symbol.barchart', DataType.STRING, true)
|
|
395
|
-
.withField('instrument.symbol.display', DataType.STRING, true)
|
|
396
393
|
.withField('date', DataType.DAY)
|
|
397
394
|
.withField('amount', DataType.DECIMAL)
|
|
398
395
|
.withField('fee', DataType.DECIMAL, true)
|
package/package.json
CHANGED
package/test/SpecRunner.js
CHANGED
|
@@ -14,15 +14,23 @@ module.exports = (() => {
|
|
|
14
14
|
* @param {String} alternateDescription
|
|
15
15
|
* @param {String} code
|
|
16
16
|
* @param {Boolean} canReinvest
|
|
17
|
+
* @param {Boolean} usesSymbols
|
|
17
18
|
*/
|
|
18
19
|
class InstrumentType extends Enum {
|
|
19
|
-
constructor(code, description, alternateDescription, canReinvest) {
|
|
20
|
+
constructor(code, description, alternateDescription, canReinvest, usesSymbols) {
|
|
20
21
|
super(code, description);
|
|
21
22
|
|
|
22
23
|
this._alternateDescription = alternateDescription;
|
|
23
24
|
this._canReinvest = canReinvest;
|
|
25
|
+
this._usesSymbols = usesSymbols;
|
|
24
26
|
}
|
|
25
27
|
|
|
28
|
+
/**
|
|
29
|
+
* A human-readable description.
|
|
30
|
+
*
|
|
31
|
+
* @public
|
|
32
|
+
* @return {String}
|
|
33
|
+
*/
|
|
26
34
|
get alternateDescription() {
|
|
27
35
|
return this._alternateDescription;
|
|
28
36
|
}
|
|
@@ -30,16 +38,28 @@ module.exports = (() => {
|
|
|
30
38
|
/**
|
|
31
39
|
* Indicates if the instrument type allows automatic reinvestment.
|
|
32
40
|
*
|
|
41
|
+
* @public
|
|
33
42
|
* @returns {Boolean}
|
|
34
43
|
*/
|
|
35
44
|
get canReinvest() {
|
|
36
45
|
return this._canReinvest;
|
|
37
46
|
}
|
|
38
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Indicates if an instrument of this type can be represented by a symbol.
|
|
50
|
+
*
|
|
51
|
+
* @public
|
|
52
|
+
* @returns {Boolean}
|
|
53
|
+
*/
|
|
54
|
+
get usesSymbols() {
|
|
55
|
+
return this._usesSymbols;
|
|
56
|
+
}
|
|
57
|
+
|
|
39
58
|
/**
|
|
40
59
|
* Cash.
|
|
41
60
|
*
|
|
42
61
|
* @public
|
|
62
|
+
* @static
|
|
43
63
|
* @returns {InstrumentType}
|
|
44
64
|
*/
|
|
45
65
|
static get CASH() {
|
|
@@ -50,6 +70,7 @@ module.exports = (() => {
|
|
|
50
70
|
* An equity issue.
|
|
51
71
|
*
|
|
52
72
|
* @public
|
|
73
|
+
* @static
|
|
53
74
|
* @returns {InstrumentType}
|
|
54
75
|
*/
|
|
55
76
|
static get EQUITY() {
|
|
@@ -60,6 +81,7 @@ module.exports = (() => {
|
|
|
60
81
|
* A mutual fund.
|
|
61
82
|
*
|
|
62
83
|
* @public
|
|
84
|
+
* @static
|
|
63
85
|
* @returns {InstrumentType}
|
|
64
86
|
*/
|
|
65
87
|
static get FUND() {
|
|
@@ -70,6 +92,7 @@ module.exports = (() => {
|
|
|
70
92
|
* An undefined asset (e.g. a house, or a collectible, or a salvaged alien spaceship).
|
|
71
93
|
*
|
|
72
94
|
* @public
|
|
95
|
+
* @static
|
|
73
96
|
* @returns {InstrumentType}
|
|
74
97
|
*/
|
|
75
98
|
static get OTHER() {
|
|
@@ -81,10 +104,10 @@ module.exports = (() => {
|
|
|
81
104
|
}
|
|
82
105
|
}
|
|
83
106
|
|
|
84
|
-
const cash = new InstrumentType('CASH', 'cash', 'Cash', false);
|
|
85
|
-
const equity = new InstrumentType('EQUITY', 'equity', 'Equities', true);
|
|
86
|
-
const fund = new InstrumentType('FUND', 'mutual fund', 'Funds', true);
|
|
87
|
-
const other = new InstrumentType('OTHER', 'other', 'Other', false);
|
|
107
|
+
const cash = new InstrumentType('CASH', 'cash', 'Cash', false, false);
|
|
108
|
+
const equity = new InstrumentType('EQUITY', 'equity', 'Equities', true, true);
|
|
109
|
+
const fund = new InstrumentType('FUND', 'mutual fund', 'Funds', true, true);
|
|
110
|
+
const other = new InstrumentType('OTHER', 'other', 'Other', false, false);
|
|
88
111
|
|
|
89
112
|
return InstrumentType;
|
|
90
113
|
})();
|
|
@@ -331,7 +354,7 @@ module.exports = (() => {
|
|
|
331
354
|
}
|
|
332
355
|
|
|
333
356
|
function getYearToDateRangeDescription(startDate, endDate) {
|
|
334
|
-
return
|
|
357
|
+
return `${endDate.year.toString()} YTD`;
|
|
335
358
|
}
|
|
336
359
|
|
|
337
360
|
function getFilteredTransactions(transactions) {
|
|
@@ -786,13 +809,13 @@ module.exports = (() => {
|
|
|
786
809
|
const populatedGroups = Object.keys(populatedObjects).map(key => populatedObjects[key]).map((items) => {
|
|
787
810
|
const first = items[0];
|
|
788
811
|
|
|
789
|
-
return new PositionGroup(parent, items, currentDefinition.currencySelector(first), currentDefinition.descriptionSelector(first), currentDefinition.single && items.length === 1);
|
|
812
|
+
return new PositionGroup(this, parent, items, currentDefinition.currencySelector(first), currentDefinition.descriptionSelector(first), currentDefinition.single && items.length === 1);
|
|
790
813
|
});
|
|
791
814
|
|
|
792
815
|
const missingGroups = array.difference(currentDefinition.requiredGroups.map(group => group.description), populatedGroups.map(group => group.description));
|
|
793
816
|
|
|
794
817
|
const empty = missingGroups.map((description) => {
|
|
795
|
-
return new PositionGroup(parent, [ ], currentDefinition.requiredGroups.find(group => group.description === description).currency, description);
|
|
818
|
+
return new PositionGroup(this, parent, [ ], currentDefinition.requiredGroups.find(group => group.description === description).currency, description);
|
|
796
819
|
});
|
|
797
820
|
|
|
798
821
|
const compositeGroups = populatedGroups.concat(empty);
|
|
@@ -863,10 +886,20 @@ module.exports = (() => {
|
|
|
863
886
|
}, [ ]);
|
|
864
887
|
}
|
|
865
888
|
|
|
866
|
-
|
|
889
|
+
setExchangeRate(symbol, price) {
|
|
867
890
|
|
|
868
891
|
}
|
|
869
892
|
|
|
893
|
+
startTransaction(executor) {
|
|
894
|
+
assert.argumentIsRequired(executor, 'executor', Function);
|
|
895
|
+
|
|
896
|
+
this._tree.walk(group => group.setSuspended(true), false, false);
|
|
897
|
+
|
|
898
|
+
executor(this);
|
|
899
|
+
|
|
900
|
+
this._tree.walk(group => group.setSuspended(false), false, false);
|
|
901
|
+
}
|
|
902
|
+
|
|
870
903
|
getGroup(keys) {
|
|
871
904
|
const node = keys.reduce((tree, key) => {
|
|
872
905
|
tree = tree.findChild(group => group.description === key);
|
|
@@ -913,7 +946,8 @@ module.exports = (() => {
|
|
|
913
946
|
* @public
|
|
914
947
|
*/
|
|
915
948
|
class PositionGroup {
|
|
916
|
-
constructor(parent, items, currency, description, single) {
|
|
949
|
+
constructor(container, parent, items, currency, description, single) {
|
|
950
|
+
this._container = container;
|
|
917
951
|
this._parent = parent || null;
|
|
918
952
|
|
|
919
953
|
this._items = items;
|
|
@@ -923,6 +957,9 @@ module.exports = (() => {
|
|
|
923
957
|
|
|
924
958
|
this._single = is.boolean(single) && single;
|
|
925
959
|
|
|
960
|
+
this._excluded = false;
|
|
961
|
+
this._suspended = false;
|
|
962
|
+
|
|
926
963
|
this._dataFormat = { };
|
|
927
964
|
this._dataActual = { };
|
|
928
965
|
|
|
@@ -964,12 +1001,11 @@ module.exports = (() => {
|
|
|
964
1001
|
this._dataFormat.currentPrice = null;
|
|
965
1002
|
}
|
|
966
1003
|
|
|
967
|
-
calculatePriceData(this, sender);
|
|
1004
|
+
calculatePriceData(this, sender, false);
|
|
968
1005
|
});
|
|
969
1006
|
});
|
|
970
1007
|
|
|
971
|
-
|
|
972
|
-
calculatePriceData(this);
|
|
1008
|
+
this.refresh();
|
|
973
1009
|
}
|
|
974
1010
|
|
|
975
1011
|
get items() {
|
|
@@ -992,6 +1028,41 @@ module.exports = (() => {
|
|
|
992
1028
|
return this._single;
|
|
993
1029
|
}
|
|
994
1030
|
|
|
1031
|
+
get suspended() {
|
|
1032
|
+
return this._suspended;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
get excluded() {
|
|
1036
|
+
return this._excluded;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
setExcluded(value) {
|
|
1040
|
+
assert.argumentIsRequired(value, 'value', Boolean);
|
|
1041
|
+
|
|
1042
|
+
if (this._excluded !== value) {
|
|
1043
|
+
this._container.startTransaction(() => {
|
|
1044
|
+
this._items.forEach((item) => {
|
|
1045
|
+
item.setExcluded(value);
|
|
1046
|
+
});
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
setSuspended(value) {
|
|
1052
|
+
assert.argumentIsRequired(value, 'value', Boolean);
|
|
1053
|
+
|
|
1054
|
+
if (this._suspended !== value) {
|
|
1055
|
+
if (this._suspended = value) {
|
|
1056
|
+
this.refresh();
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
refresh() {
|
|
1062
|
+
calculateStaticData(this);
|
|
1063
|
+
calculatePriceData(this, null, true);
|
|
1064
|
+
}
|
|
1065
|
+
|
|
995
1066
|
toString() {
|
|
996
1067
|
return '[PositionGroup]';
|
|
997
1068
|
}
|
|
@@ -1018,6 +1089,10 @@ module.exports = (() => {
|
|
|
1018
1089
|
}
|
|
1019
1090
|
|
|
1020
1091
|
function calculateStaticData(group) {
|
|
1092
|
+
if (group.suspended) {
|
|
1093
|
+
return;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1021
1096
|
const actual = group._dataActual;
|
|
1022
1097
|
const format = group._dataFormat;
|
|
1023
1098
|
|
|
@@ -1054,7 +1129,11 @@ module.exports = (() => {
|
|
|
1054
1129
|
format.summaryTwoTotal = formatCurrency(updates.summaryTwoTotal, currency);
|
|
1055
1130
|
}
|
|
1056
1131
|
|
|
1057
|
-
function calculatePriceData(group, item) {
|
|
1132
|
+
function calculatePriceData(group, item, forceRefresh) {
|
|
1133
|
+
if (group.suspended) {
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1058
1137
|
const parent = group._parent;
|
|
1059
1138
|
|
|
1060
1139
|
const actual = group._dataActual;
|
|
@@ -1062,14 +1141,11 @@ module.exports = (() => {
|
|
|
1062
1141
|
|
|
1063
1142
|
const currency = group.currency;
|
|
1064
1143
|
|
|
1144
|
+
const refresh = (is.boolean(forceRefresh) && forceRefresh) || (actual.market === null || actual.unrealizedToday === null || actual.total === null);
|
|
1145
|
+
|
|
1065
1146
|
let updates;
|
|
1066
1147
|
|
|
1067
|
-
if (
|
|
1068
|
-
updates = {
|
|
1069
|
-
market: actual.market.add(item.data.marketChange),
|
|
1070
|
-
unrealizedToday: actual.unrealizedToday.add(item.data.unrealizedTodayChange)
|
|
1071
|
-
};
|
|
1072
|
-
} else {
|
|
1148
|
+
if (refresh) {
|
|
1073
1149
|
const items = group._items;
|
|
1074
1150
|
|
|
1075
1151
|
updates = items.reduce((updates, item) => {
|
|
@@ -1079,8 +1155,14 @@ module.exports = (() => {
|
|
|
1079
1155
|
return updates;
|
|
1080
1156
|
}, {
|
|
1081
1157
|
market: Decimal.ZERO,
|
|
1158
|
+
|
|
1082
1159
|
unrealizedToday: Decimal.ZERO
|
|
1083
1160
|
});
|
|
1161
|
+
} else {
|
|
1162
|
+
updates = {
|
|
1163
|
+
market: actual.market.add(item.data.marketChange),
|
|
1164
|
+
unrealizedToday: actual.unrealizedToday.add(item.data.unrealizedTodayChange)
|
|
1165
|
+
};
|
|
1084
1166
|
}
|
|
1085
1167
|
|
|
1086
1168
|
if (parent !== null) {
|
|
@@ -1196,10 +1278,13 @@ module.exports = (() => {
|
|
|
1196
1278
|
this._data.realized = null;
|
|
1197
1279
|
this._data.income = null;
|
|
1198
1280
|
|
|
1281
|
+
this._excluded = false;
|
|
1282
|
+
|
|
1199
1283
|
calculateStaticData(this);
|
|
1200
1284
|
calculatePriceData(this, null);
|
|
1201
1285
|
|
|
1202
1286
|
this._priceChangeEvent = new Event(this);
|
|
1287
|
+
this._excludedChangeEvent = new Event(this);
|
|
1203
1288
|
}
|
|
1204
1289
|
|
|
1205
1290
|
get portfolio() {
|
|
@@ -1218,7 +1303,13 @@ module.exports = (() => {
|
|
|
1218
1303
|
return this._data;
|
|
1219
1304
|
}
|
|
1220
1305
|
|
|
1306
|
+
get excluded() {
|
|
1307
|
+
return this._excluded;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1221
1310
|
setPrice(price) {
|
|
1311
|
+
assert.argumentIsRequired(price, 'price', Number);
|
|
1312
|
+
|
|
1222
1313
|
if (this._data.price !== price) {
|
|
1223
1314
|
calculatePriceData(this, this._data.currentPrice = price);
|
|
1224
1315
|
|
|
@@ -1226,12 +1317,22 @@ module.exports = (() => {
|
|
|
1226
1317
|
}
|
|
1227
1318
|
}
|
|
1228
1319
|
|
|
1229
|
-
|
|
1230
|
-
assert.argumentIsRequired(
|
|
1320
|
+
setExcluded(value) {
|
|
1321
|
+
assert.argumentIsRequired(value, 'value', Boolean);
|
|
1322
|
+
|
|
1323
|
+
if (this._excluded !== value) {
|
|
1324
|
+
this._excludedChangeEvent.fire(this, this._excluded = value);
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1231
1327
|
|
|
1328
|
+
registerPriceChangeHandler(handler) {
|
|
1232
1329
|
this._priceChangeEvent.register(handler);
|
|
1233
1330
|
}
|
|
1234
1331
|
|
|
1332
|
+
registerExcludedChangeHandler(handler) {
|
|
1333
|
+
this._excludedChangeEvent.register(handler);
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1235
1336
|
toString() {
|
|
1236
1337
|
return '[PositionItem]';
|
|
1237
1338
|
}
|