@barchart/portfolio-api-common 1.0.69 → 1.0.73
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 +16 -7
- package/lib/processing/PositionGroup.js +60 -11
- package/lib/processing/PositionItem.js +22 -3
- package/lib/serialization/PortfolioSchema.js +1 -0
- package/package.json +1 -1
- package/test/SpecRunner.js +128 -28
|
@@ -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
|
})();
|
|
@@ -6,8 +6,7 @@ const array = require('@barchart/common-js/lang/array'),
|
|
|
6
6
|
is = require('@barchart/common-js/lang/is'),
|
|
7
7
|
Tree = require('@barchart/common-js/collections/Tree');
|
|
8
8
|
|
|
9
|
-
const
|
|
10
|
-
PositionSummaryFrame = require('./../data/PositionSummaryFrame');
|
|
9
|
+
const PositionSummaryFrame = require('./../data/PositionSummaryFrame');
|
|
11
10
|
|
|
12
11
|
const PositionGroup = require('./PositionGroup'),
|
|
13
12
|
PositionItem = require('./PositionItem');
|
|
@@ -24,7 +23,7 @@ module.exports = (() => {
|
|
|
24
23
|
this._defaultCurrency = defaultCurrency || Currency.CAD;
|
|
25
24
|
|
|
26
25
|
this._summaryFrame = summaryFrameType || PositionSummaryFrame.YEARLY;
|
|
27
|
-
this._summaryRanges = this._summaryFrame.getRecentRanges(
|
|
26
|
+
this._summaryRanges = this._summaryFrame.getRecentRanges(1);
|
|
28
27
|
|
|
29
28
|
this._portfolios = portfolios.reduce((map, portfolio) => {
|
|
30
29
|
map[portfolio.portfolio] = portfolio;
|
|
@@ -40,7 +39,7 @@ module.exports = (() => {
|
|
|
40
39
|
map[key] = getSummaryArray(this._summaryRanges);
|
|
41
40
|
}
|
|
42
41
|
|
|
43
|
-
const index = this._summaryRanges.findIndex(r => r.start
|
|
42
|
+
const index = this._summaryRanges.findIndex(r => r.start.getIsEqual(summary.start.date) && r.end.getIsEqual(summary.end.date));
|
|
44
43
|
|
|
45
44
|
if (!(index < 0)) {
|
|
46
45
|
map[key][index] = summary;
|
|
@@ -110,13 +109,13 @@ module.exports = (() => {
|
|
|
110
109
|
const populatedGroups = Object.keys(populatedObjects).map(key => populatedObjects[key]).map((items) => {
|
|
111
110
|
const first = items[0];
|
|
112
111
|
|
|
113
|
-
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);
|
|
114
113
|
});
|
|
115
114
|
|
|
116
115
|
const missingGroups = array.difference(currentDefinition.requiredGroups.map(group => group.description), populatedGroups.map(group => group.description));
|
|
117
116
|
|
|
118
117
|
const empty = missingGroups.map((description) => {
|
|
119
|
-
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);
|
|
120
119
|
});
|
|
121
120
|
|
|
122
121
|
const compositeGroups = populatedGroups.concat(empty);
|
|
@@ -187,10 +186,20 @@ module.exports = (() => {
|
|
|
187
186
|
}, [ ]);
|
|
188
187
|
}
|
|
189
188
|
|
|
190
|
-
|
|
189
|
+
setExchangeRate(symbol, price) {
|
|
191
190
|
|
|
192
191
|
}
|
|
193
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
|
+
|
|
194
203
|
getGroup(keys) {
|
|
195
204
|
const node = keys.reduce((tree, key) => {
|
|
196
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
|
}
|
|
@@ -99,7 +118,7 @@ module.exports = (() => {
|
|
|
99
118
|
const getSummaryTotal = (index) => {
|
|
100
119
|
let summaryTotal;
|
|
101
120
|
|
|
102
|
-
if (summaries.length >
|
|
121
|
+
if (summaries.length > index && summaries[index] !== null) {
|
|
103
122
|
const period = summaries[index].period;
|
|
104
123
|
|
|
105
124
|
summaryTotal = period.realized.add(period.unrealized).add(period.income);
|
|
@@ -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)
|
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) {
|
|
@@ -683,8 +706,7 @@ const array = require('@barchart/common-js/lang/array'),
|
|
|
683
706
|
is = require('@barchart/common-js/lang/is'),
|
|
684
707
|
Tree = require('@barchart/common-js/collections/Tree');
|
|
685
708
|
|
|
686
|
-
const
|
|
687
|
-
PositionSummaryFrame = require('./../data/PositionSummaryFrame');
|
|
709
|
+
const PositionSummaryFrame = require('./../data/PositionSummaryFrame');
|
|
688
710
|
|
|
689
711
|
const PositionGroup = require('./PositionGroup'),
|
|
690
712
|
PositionItem = require('./PositionItem');
|
|
@@ -701,7 +723,7 @@ module.exports = (() => {
|
|
|
701
723
|
this._defaultCurrency = defaultCurrency || Currency.CAD;
|
|
702
724
|
|
|
703
725
|
this._summaryFrame = summaryFrameType || PositionSummaryFrame.YEARLY;
|
|
704
|
-
this._summaryRanges = this._summaryFrame.getRecentRanges(
|
|
726
|
+
this._summaryRanges = this._summaryFrame.getRecentRanges(1);
|
|
705
727
|
|
|
706
728
|
this._portfolios = portfolios.reduce((map, portfolio) => {
|
|
707
729
|
map[portfolio.portfolio] = portfolio;
|
|
@@ -717,7 +739,7 @@ module.exports = (() => {
|
|
|
717
739
|
map[key] = getSummaryArray(this._summaryRanges);
|
|
718
740
|
}
|
|
719
741
|
|
|
720
|
-
const index = this._summaryRanges.findIndex(r => r.start
|
|
742
|
+
const index = this._summaryRanges.findIndex(r => r.start.getIsEqual(summary.start.date) && r.end.getIsEqual(summary.end.date));
|
|
721
743
|
|
|
722
744
|
if (!(index < 0)) {
|
|
723
745
|
map[key][index] = summary;
|
|
@@ -787,13 +809,13 @@ module.exports = (() => {
|
|
|
787
809
|
const populatedGroups = Object.keys(populatedObjects).map(key => populatedObjects[key]).map((items) => {
|
|
788
810
|
const first = items[0];
|
|
789
811
|
|
|
790
|
-
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);
|
|
791
813
|
});
|
|
792
814
|
|
|
793
815
|
const missingGroups = array.difference(currentDefinition.requiredGroups.map(group => group.description), populatedGroups.map(group => group.description));
|
|
794
816
|
|
|
795
817
|
const empty = missingGroups.map((description) => {
|
|
796
|
-
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);
|
|
797
819
|
});
|
|
798
820
|
|
|
799
821
|
const compositeGroups = populatedGroups.concat(empty);
|
|
@@ -864,10 +886,20 @@ module.exports = (() => {
|
|
|
864
886
|
}, [ ]);
|
|
865
887
|
}
|
|
866
888
|
|
|
867
|
-
|
|
889
|
+
setExchangeRate(symbol, price) {
|
|
868
890
|
|
|
869
891
|
}
|
|
870
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
|
+
|
|
871
903
|
getGroup(keys) {
|
|
872
904
|
const node = keys.reduce((tree, key) => {
|
|
873
905
|
tree = tree.findChild(group => group.description === key);
|
|
@@ -900,7 +932,7 @@ module.exports = (() => {
|
|
|
900
932
|
return PositionContainer;
|
|
901
933
|
})();
|
|
902
934
|
|
|
903
|
-
},{"./../data/
|
|
935
|
+
},{"./../data/PositionSummaryFrame":2,"./PositionGroup":5,"./PositionItem":7,"@barchart/common-js/collections/Tree":8,"@barchart/common-js/collections/sorting/ComparatorBuilder":9,"@barchart/common-js/collections/sorting/comparators":10,"@barchart/common-js/lang/Currency":11,"@barchart/common-js/lang/array":16,"@barchart/common-js/lang/assert":17,"@barchart/common-js/lang/is":19}],5:[function(require,module,exports){
|
|
904
936
|
const assert = require('@barchart/common-js/lang/assert'),
|
|
905
937
|
Currency = require('@barchart/common-js/lang/Currency'),
|
|
906
938
|
Decimal = require('@barchart/common-js/lang/Decimal'),
|
|
@@ -914,7 +946,8 @@ module.exports = (() => {
|
|
|
914
946
|
* @public
|
|
915
947
|
*/
|
|
916
948
|
class PositionGroup {
|
|
917
|
-
constructor(parent, items, currency, description, single) {
|
|
949
|
+
constructor(container, parent, items, currency, description, single) {
|
|
950
|
+
this._container = container;
|
|
918
951
|
this._parent = parent || null;
|
|
919
952
|
|
|
920
953
|
this._items = items;
|
|
@@ -924,6 +957,9 @@ module.exports = (() => {
|
|
|
924
957
|
|
|
925
958
|
this._single = is.boolean(single) && single;
|
|
926
959
|
|
|
960
|
+
this._excluded = false;
|
|
961
|
+
this._suspended = false;
|
|
962
|
+
|
|
927
963
|
this._dataFormat = { };
|
|
928
964
|
this._dataActual = { };
|
|
929
965
|
|
|
@@ -965,12 +1001,11 @@ module.exports = (() => {
|
|
|
965
1001
|
this._dataFormat.currentPrice = null;
|
|
966
1002
|
}
|
|
967
1003
|
|
|
968
|
-
calculatePriceData(this, sender);
|
|
1004
|
+
calculatePriceData(this, sender, false);
|
|
969
1005
|
});
|
|
970
1006
|
});
|
|
971
1007
|
|
|
972
|
-
|
|
973
|
-
calculatePriceData(this);
|
|
1008
|
+
this.refresh();
|
|
974
1009
|
}
|
|
975
1010
|
|
|
976
1011
|
get items() {
|
|
@@ -993,6 +1028,41 @@ module.exports = (() => {
|
|
|
993
1028
|
return this._single;
|
|
994
1029
|
}
|
|
995
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
|
+
|
|
996
1066
|
toString() {
|
|
997
1067
|
return '[PositionGroup]';
|
|
998
1068
|
}
|
|
@@ -1019,6 +1089,10 @@ module.exports = (() => {
|
|
|
1019
1089
|
}
|
|
1020
1090
|
|
|
1021
1091
|
function calculateStaticData(group) {
|
|
1092
|
+
if (group.suspended) {
|
|
1093
|
+
return;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1022
1096
|
const actual = group._dataActual;
|
|
1023
1097
|
const format = group._dataFormat;
|
|
1024
1098
|
|
|
@@ -1055,7 +1129,11 @@ module.exports = (() => {
|
|
|
1055
1129
|
format.summaryTwoTotal = formatCurrency(updates.summaryTwoTotal, currency);
|
|
1056
1130
|
}
|
|
1057
1131
|
|
|
1058
|
-
function calculatePriceData(group, item) {
|
|
1132
|
+
function calculatePriceData(group, item, forceRefresh) {
|
|
1133
|
+
if (group.suspended) {
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1059
1137
|
const parent = group._parent;
|
|
1060
1138
|
|
|
1061
1139
|
const actual = group._dataActual;
|
|
@@ -1063,14 +1141,11 @@ module.exports = (() => {
|
|
|
1063
1141
|
|
|
1064
1142
|
const currency = group.currency;
|
|
1065
1143
|
|
|
1144
|
+
const refresh = (is.boolean(forceRefresh) && forceRefresh) || (actual.market === null || actual.unrealizedToday === null || actual.total === null);
|
|
1145
|
+
|
|
1066
1146
|
let updates;
|
|
1067
1147
|
|
|
1068
|
-
if (
|
|
1069
|
-
updates = {
|
|
1070
|
-
market: actual.market.add(item.data.marketChange),
|
|
1071
|
-
unrealizedToday: actual.unrealizedToday.add(item.data.unrealizedTodayChange)
|
|
1072
|
-
};
|
|
1073
|
-
} else {
|
|
1148
|
+
if (refresh) {
|
|
1074
1149
|
const items = group._items;
|
|
1075
1150
|
|
|
1076
1151
|
updates = items.reduce((updates, item) => {
|
|
@@ -1080,8 +1155,14 @@ module.exports = (() => {
|
|
|
1080
1155
|
return updates;
|
|
1081
1156
|
}, {
|
|
1082
1157
|
market: Decimal.ZERO,
|
|
1158
|
+
|
|
1083
1159
|
unrealizedToday: Decimal.ZERO
|
|
1084
1160
|
});
|
|
1161
|
+
} else {
|
|
1162
|
+
updates = {
|
|
1163
|
+
market: actual.market.add(item.data.marketChange),
|
|
1164
|
+
unrealizedToday: actual.unrealizedToday.add(item.data.unrealizedTodayChange)
|
|
1165
|
+
};
|
|
1085
1166
|
}
|
|
1086
1167
|
|
|
1087
1168
|
if (parent !== null) {
|
|
@@ -1197,10 +1278,13 @@ module.exports = (() => {
|
|
|
1197
1278
|
this._data.realized = null;
|
|
1198
1279
|
this._data.income = null;
|
|
1199
1280
|
|
|
1281
|
+
this._excluded = false;
|
|
1282
|
+
|
|
1200
1283
|
calculateStaticData(this);
|
|
1201
1284
|
calculatePriceData(this, null);
|
|
1202
1285
|
|
|
1203
1286
|
this._priceChangeEvent = new Event(this);
|
|
1287
|
+
this._excludedChangeEvent = new Event(this);
|
|
1204
1288
|
}
|
|
1205
1289
|
|
|
1206
1290
|
get portfolio() {
|
|
@@ -1219,7 +1303,13 @@ module.exports = (() => {
|
|
|
1219
1303
|
return this._data;
|
|
1220
1304
|
}
|
|
1221
1305
|
|
|
1306
|
+
get excluded() {
|
|
1307
|
+
return this._excluded;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1222
1310
|
setPrice(price) {
|
|
1311
|
+
assert.argumentIsRequired(price, 'price', Number);
|
|
1312
|
+
|
|
1223
1313
|
if (this._data.price !== price) {
|
|
1224
1314
|
calculatePriceData(this, this._data.currentPrice = price);
|
|
1225
1315
|
|
|
@@ -1227,12 +1317,22 @@ module.exports = (() => {
|
|
|
1227
1317
|
}
|
|
1228
1318
|
}
|
|
1229
1319
|
|
|
1230
|
-
|
|
1231
|
-
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
|
+
}
|
|
1232
1327
|
|
|
1328
|
+
registerPriceChangeHandler(handler) {
|
|
1233
1329
|
this._priceChangeEvent.register(handler);
|
|
1234
1330
|
}
|
|
1235
1331
|
|
|
1332
|
+
registerExcludedChangeHandler(handler) {
|
|
1333
|
+
this._excludedChangeEvent.register(handler);
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1236
1336
|
toString() {
|
|
1237
1337
|
return '[PositionItem]';
|
|
1238
1338
|
}
|
|
@@ -1263,7 +1363,7 @@ module.exports = (() => {
|
|
|
1263
1363
|
const getSummaryTotal = (index) => {
|
|
1264
1364
|
let summaryTotal;
|
|
1265
1365
|
|
|
1266
|
-
if (summaries.length >
|
|
1366
|
+
if (summaries.length > index && summaries[index] !== null) {
|
|
1267
1367
|
const period = summaries[index].period;
|
|
1268
1368
|
|
|
1269
1369
|
summaryTotal = period.realized.add(period.unrealized).add(period.income);
|