@barchart/portfolio-api-common 2.2.1 → 4.0.0
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 +8 -2
- package/lib/processing/PositionContainer.js +6 -8
- package/lib/processing/PositionGroup.js +16 -0
- package/lib/processing/PositionItem.js +23 -7
- package/lib/serialization/PositionSchema.js +4 -0
- package/lib/serialization/TransactionSchema.js +2 -0
- package/package.json +1 -1
|
@@ -150,6 +150,12 @@ module.exports = (() => {
|
|
|
150
150
|
f.raw.open = getRawForDecimal(f.open);
|
|
151
151
|
};
|
|
152
152
|
|
|
153
|
+
const gainFormatter = (t, f) => {
|
|
154
|
+
f.gain = t.gain;
|
|
155
|
+
|
|
156
|
+
f.raw.gain = getRawForDecimal(f.gain);
|
|
157
|
+
};
|
|
158
|
+
|
|
153
159
|
const averageCostFormatter = (t, f) => {
|
|
154
160
|
const basis = t.snapshot.basis;
|
|
155
161
|
const open = t.snapshot.open;
|
|
@@ -495,8 +501,8 @@ module.exports = (() => {
|
|
|
495
501
|
const formatters = new Map();
|
|
496
502
|
|
|
497
503
|
formatters.set(TransactionType.BUY, [ basicFormatter, buySellFormatter, averageCostFormatter ]);
|
|
498
|
-
formatters.set(TransactionType.SELL, [ basicFormatter, buySellFormatter, averageCostFormatter ]);
|
|
499
|
-
formatters.set(TransactionType.BUY_SHORT, [ basicFormatter, buySellFormatter, averageCostFormatter ]);
|
|
504
|
+
formatters.set(TransactionType.SELL, [ basicFormatter, buySellFormatter, averageCostFormatter, gainFormatter ]);
|
|
505
|
+
formatters.set(TransactionType.BUY_SHORT, [ basicFormatter, buySellFormatter, averageCostFormatter, gainFormatter ]);
|
|
500
506
|
formatters.set(TransactionType.SELL_SHORT, [ basicFormatter, buySellFormatter, averageCostFormatter ]);
|
|
501
507
|
formatters.set(TransactionType.DIVIDEND, [ basicFormatter, dividendFormatter, averageCostFormatter ]);
|
|
502
508
|
formatters.set(TransactionType.DIVIDEND_STOCK, [ basicFormatter, dividendStockFormatter, averageCostFormatter ]);
|
|
@@ -40,6 +40,10 @@ module.exports = (() => {
|
|
|
40
40
|
Currency.USD
|
|
41
41
|
];
|
|
42
42
|
|
|
43
|
+
const STATIC_RATES = [
|
|
44
|
+
Rate.fromPair(0.01, '^GBXGBP')
|
|
45
|
+
];
|
|
46
|
+
|
|
43
47
|
/**
|
|
44
48
|
* A container for positions which groups the positions into one or more
|
|
45
49
|
* trees for aggregation and display purposes. For example, positions could be
|
|
@@ -168,21 +172,15 @@ module.exports = (() => {
|
|
|
168
172
|
|
|
169
173
|
return symbols;
|
|
170
174
|
}, [ ]);
|
|
171
|
-
|
|
172
|
-
this._forexSymbols.push('^GBXGBP');
|
|
173
175
|
}
|
|
174
176
|
|
|
175
|
-
this._currencyTranslator = new CurrencyTranslator(this._forexSymbols);
|
|
177
|
+
this._currencyTranslator = new CurrencyTranslator(this._forexSymbols.concat(STATIC_RATES.map(r => r.getSymbol())));
|
|
176
178
|
|
|
177
179
|
const forexQuotes = this._forexSymbols.map((symbol) => {
|
|
178
|
-
if (symbol === '^GBXGBP') {
|
|
179
|
-
return Rate.fromPair(0.01, '^GBXGBP');
|
|
180
|
-
}
|
|
181
|
-
|
|
182
180
|
return Rate.fromPair(Decimal.ONE, symbol);
|
|
183
181
|
});
|
|
184
182
|
|
|
185
|
-
this._currencyTranslator.setRates(forexQuotes);
|
|
183
|
+
this._currencyTranslator.setRates(forexQuotes.concat(STATIC_RATES));
|
|
186
184
|
|
|
187
185
|
this._nodes = { };
|
|
188
186
|
|
|
@@ -131,6 +131,7 @@ module.exports = (() => {
|
|
|
131
131
|
this._dataActual.basis = null;
|
|
132
132
|
this._dataActual.basis2 = null;
|
|
133
133
|
this._dataActual.realized = null;
|
|
134
|
+
this._dataActual.realizedToday = null;
|
|
134
135
|
this._dataActual.income = null;
|
|
135
136
|
this._dataActual.market = null;
|
|
136
137
|
this._dataActual.market2 = null;
|
|
@@ -138,6 +139,7 @@ module.exports = (() => {
|
|
|
138
139
|
this._dataActual.marketPercentPortfolio = null;
|
|
139
140
|
this._dataActual.unrealized = null;
|
|
140
141
|
this._dataActual.unrealizedToday = null;
|
|
142
|
+
this._dataActual.gainToday = null;
|
|
141
143
|
this._dataActual.total = null;
|
|
142
144
|
this._dataActual.summaryTotalCurrent = null;
|
|
143
145
|
this._dataActual.summaryTotalPrevious = null;
|
|
@@ -157,6 +159,7 @@ module.exports = (() => {
|
|
|
157
159
|
this._dataFormat.basis2 = null;
|
|
158
160
|
this._dataFormat.realized = null;
|
|
159
161
|
this._dataFormat.realizedPercent = null;
|
|
162
|
+
this._dataFormat.realizedToday = null;
|
|
160
163
|
this._dataFormat.income = null;
|
|
161
164
|
this._dataFormat.market = null;
|
|
162
165
|
this._dataFormat.market2 = null;
|
|
@@ -168,6 +171,8 @@ module.exports = (() => {
|
|
|
168
171
|
this._dataFormat.unrealizedNegative = false;
|
|
169
172
|
this._dataFormat.unrealizedToday = null;
|
|
170
173
|
this._dataFormat.unrealizedTodayNegative = false;
|
|
174
|
+
this._dataFormat.gainToday = null;
|
|
175
|
+
this._dataFormat.gainTodayNegative = false;
|
|
171
176
|
this._dataFormat.total = null;
|
|
172
177
|
this._dataFormat.totalNegative = false;
|
|
173
178
|
this._dataFormat.summaryTotalCurrent = null;
|
|
@@ -821,6 +826,7 @@ module.exports = (() => {
|
|
|
821
826
|
|
|
822
827
|
updates.realized = updates.realized.add(translate(item, item.data.realized));
|
|
823
828
|
updates.unrealized = updates.unrealized.add(translate(item, item.data.unrealized));
|
|
829
|
+
updates.realizedToday = updates.realizedToday.add(translate(item, item.data.realizedToday));
|
|
824
830
|
updates.income = updates.income.add(translate(item, item.data.income));
|
|
825
831
|
updates.summaryTotalCurrent = updates.summaryTotalCurrent.add(translate(item, item.data.periodGain));
|
|
826
832
|
updates.summaryTotalPrevious = updates.summaryTotalPrevious.add(translate(item, item.data.periodGainPrevious));
|
|
@@ -847,6 +853,7 @@ module.exports = (() => {
|
|
|
847
853
|
basis2: Decimal.ZERO,
|
|
848
854
|
realized: Decimal.ZERO,
|
|
849
855
|
unrealized: Decimal.ZERO,
|
|
856
|
+
realizedToday: Decimal.ZERO,
|
|
850
857
|
income: Decimal.ZERO,
|
|
851
858
|
summaryTotalCurrent: Decimal.ZERO,
|
|
852
859
|
summaryTotalPrevious: Decimal.ZERO,
|
|
@@ -867,6 +874,7 @@ module.exports = (() => {
|
|
|
867
874
|
actual.basis2 = updates.basis2;
|
|
868
875
|
actual.realized = updates.realized;
|
|
869
876
|
actual.unrealized = updates.unrealized;
|
|
877
|
+
actual.realizedToday = updates.realizedToday;
|
|
870
878
|
actual.income = updates.income;
|
|
871
879
|
actual.summaryTotalCurrent = updates.summaryTotalCurrent;
|
|
872
880
|
actual.summaryTotalPrevious = updates.summaryTotalPrevious;
|
|
@@ -886,6 +894,7 @@ module.exports = (() => {
|
|
|
886
894
|
format.basis2 = formatCurrency(actual.basis2, currency);
|
|
887
895
|
format.realized = formatCurrency(actual.realized, currency);
|
|
888
896
|
format.unrealized = formatCurrency(actual.unrealized, currency);
|
|
897
|
+
format.realizedToday = formatCurrency(actual.realizedToday, currency);
|
|
889
898
|
format.income = formatCurrency(actual.income, currency);
|
|
890
899
|
format.summaryTotalCurrent = formatCurrency(updates.summaryTotalCurrent, currency);
|
|
891
900
|
format.summaryTotalCurrentNegative = updates.summaryTotalCurrent.getIsNegative();
|
|
@@ -995,6 +1004,7 @@ module.exports = (() => {
|
|
|
995
1004
|
updates.marketAbsolute = updates.marketAbsolute.add(translate(item, item.data.marketAbsolute));
|
|
996
1005
|
updates.unrealized = updates.unrealized.add(translate(item, item.data.unrealized));
|
|
997
1006
|
updates.unrealizedToday = updates.unrealizedToday.add(translate(item, item.data.unrealizedToday));
|
|
1007
|
+
updates.gainToday = updates.gainToday.add(translate(item, item.data.unrealizedToday.add(item.data.realizedToday)));
|
|
998
1008
|
updates.summaryTotalCurrent = updates.summaryTotalCurrent.add(translate(item, item.data.periodGain));
|
|
999
1009
|
updates.periodUnrealized = updates.periodUnrealized.add(translate(item, item.data.periodUnrealized));
|
|
1000
1010
|
|
|
@@ -1006,6 +1016,7 @@ module.exports = (() => {
|
|
|
1006
1016
|
marketDirection: unchanged,
|
|
1007
1017
|
unrealized: Decimal.ZERO,
|
|
1008
1018
|
unrealizedToday: Decimal.ZERO,
|
|
1019
|
+
gainToday: Decimal.ZERO,
|
|
1009
1020
|
summaryTotalCurrent: Decimal.ZERO,
|
|
1010
1021
|
periodUnrealized: Decimal.ZERO
|
|
1011
1022
|
});
|
|
@@ -1024,6 +1035,7 @@ module.exports = (() => {
|
|
|
1024
1035
|
updates.marketDirection = { up: item.data.marketChange.getIsPositive(), down: item.data.marketChange.getIsNegative() };
|
|
1025
1036
|
updates.unrealized = actual.unrealized.add(translate(item, item.data.unrealizedChange));
|
|
1026
1037
|
updates.unrealizedToday = actual.unrealizedToday.add(translate(item, item.data.unrealizedTodayChange));
|
|
1038
|
+
updates.gainToday = actual.gainToday.add(translate(item, item.data.unrealizedTodayChange));
|
|
1027
1039
|
updates.summaryTotalCurrent = actual.summaryTotalCurrent.add(translate(item, item.data.periodGainChange));
|
|
1028
1040
|
updates.periodUnrealized = actual.periodUnrealized.add(translate(item, item.data.periodUnrealizedChange));
|
|
1029
1041
|
}
|
|
@@ -1033,6 +1045,7 @@ module.exports = (() => {
|
|
|
1033
1045
|
actual.marketAbsolute = updates.marketAbsolute;
|
|
1034
1046
|
actual.unrealized = updates.unrealized;
|
|
1035
1047
|
actual.unrealizedToday = updates.unrealizedToday;
|
|
1048
|
+
actual.gainToday = updates.gainToday;
|
|
1036
1049
|
actual.summaryTotalCurrent = updates.summaryTotalCurrent;
|
|
1037
1050
|
actual.periodUnrealized = updates.periodUnrealized;
|
|
1038
1051
|
|
|
@@ -1071,6 +1084,9 @@ module.exports = (() => {
|
|
|
1071
1084
|
format.unrealizedToday = formatCurrency(actual.unrealizedToday, currency);
|
|
1072
1085
|
format.unrealizedTodayNegative = actual.unrealizedToday.getIsNegative();
|
|
1073
1086
|
|
|
1087
|
+
format.gainToday = formatCurrency(actual.gainToday, currency);
|
|
1088
|
+
format.gainTodayNegative = actual.gainToday.getIsNegative();
|
|
1089
|
+
|
|
1074
1090
|
format.summaryTotalCurrent = formatCurrency(actual.summaryTotalCurrent, currency);
|
|
1075
1091
|
format.summaryTotalCurrentNegative = actual.summaryTotalCurrent.getIsNegative();
|
|
1076
1092
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const assert = require('@barchart/common-js/lang/assert'),
|
|
2
2
|
Currency = require('@barchart/common-js/lang/Currency'),
|
|
3
|
+
Day = require('@barchart/common-js/lang/Day'),
|
|
3
4
|
Decimal = require('@barchart/common-js/lang/Decimal'),
|
|
4
5
|
Disposable = require('@barchart/common-js/lang/Disposable'),
|
|
5
6
|
Event = require('@barchart/common-js/messaging/Event'),
|
|
@@ -43,6 +44,7 @@ module.exports = (() => {
|
|
|
43
44
|
this._previousSummaries = previousSummaries || [ ];
|
|
44
45
|
|
|
45
46
|
this._reporting = reporting;
|
|
47
|
+
this._referenceDate = referenceDate;
|
|
46
48
|
|
|
47
49
|
this._currentQuote = null;
|
|
48
50
|
this._previousQuote = null;
|
|
@@ -61,6 +63,8 @@ module.exports = (() => {
|
|
|
61
63
|
this._data.marketAbsolute = null;
|
|
62
64
|
this._data.marketAbsoluteChange = null;
|
|
63
65
|
|
|
66
|
+
this._data.realizedToday = null;
|
|
67
|
+
|
|
64
68
|
this._data.unrealizedToday = null;
|
|
65
69
|
this._data.unrealizedTodayChange = null;
|
|
66
70
|
|
|
@@ -118,8 +122,8 @@ module.exports = (() => {
|
|
|
118
122
|
this._portfolioChangedEvent = new Event(this);
|
|
119
123
|
this._positionItemDisposeEvent = new Event(this);
|
|
120
124
|
|
|
121
|
-
calculateStaticData(this);
|
|
122
|
-
calculatePriceData(this, null);
|
|
125
|
+
calculateStaticData(this, this._referenceDate);
|
|
126
|
+
calculatePriceData(this, null, null);
|
|
123
127
|
}
|
|
124
128
|
|
|
125
129
|
/**
|
|
@@ -255,7 +259,7 @@ module.exports = (() => {
|
|
|
255
259
|
this._data.previousPrice = quote.previousPrice;
|
|
256
260
|
}
|
|
257
261
|
|
|
258
|
-
calculatePriceData(this, quote.lastPrice);
|
|
262
|
+
calculatePriceData(this, quote.lastPrice, quote.lastDay instanceof Day && quote.lastDay.getIsEqual(this._referenceDate));
|
|
259
263
|
|
|
260
264
|
this._currentPricePrevious = this._currentPrice;
|
|
261
265
|
this._currentPrice = quote.lastPrice;
|
|
@@ -436,7 +440,7 @@ module.exports = (() => {
|
|
|
436
440
|
}
|
|
437
441
|
}
|
|
438
442
|
|
|
439
|
-
function calculateStaticData(item) {
|
|
443
|
+
function calculateStaticData(item, referenceDate) {
|
|
440
444
|
const position = item.position;
|
|
441
445
|
|
|
442
446
|
const currentSummary = item.currentSummary;
|
|
@@ -467,6 +471,12 @@ module.exports = (() => {
|
|
|
467
471
|
data.realized = snapshot.gain;
|
|
468
472
|
data.unrealized = Decimal.ZERO;
|
|
469
473
|
|
|
474
|
+
if (position.latest && position.latest.date && position.latest.date.getIsEqual(referenceDate) && position.latest.gain) {
|
|
475
|
+
data.realizedToday = position.latest.gain;
|
|
476
|
+
} else {
|
|
477
|
+
data.realizedToday = Decimal.ZERO;
|
|
478
|
+
}
|
|
479
|
+
|
|
470
480
|
data.income = snapshot.income;
|
|
471
481
|
|
|
472
482
|
data.marketPrevious = previousSummary1 === null ? Decimal.ZERO : previousSummary1.end.value;
|
|
@@ -503,7 +513,7 @@ module.exports = (() => {
|
|
|
503
513
|
data.totalDivisor = calculateTotalDivisor(position.instrument.type, data.initiate, position);
|
|
504
514
|
}
|
|
505
515
|
|
|
506
|
-
function calculatePriceData(item, price) {
|
|
516
|
+
function calculatePriceData(item, price, today) {
|
|
507
517
|
const position = item.position;
|
|
508
518
|
const snapshot = getSnapshot(position, item.currentSummary, item._reporting);
|
|
509
519
|
|
|
@@ -512,7 +522,7 @@ module.exports = (() => {
|
|
|
512
522
|
// 2023/11/28, BRI. Futures contracts do not have their value set to zero
|
|
513
523
|
// after expiration. At expiration, the contract would have been closed
|
|
514
524
|
// (but the price would not have been zero). On the other hand, option
|
|
515
|
-
// contracts can expire worthless and we attempt to represent that here.
|
|
525
|
+
// contracts can expire worthless, and we attempt to represent that here.
|
|
516
526
|
|
|
517
527
|
const worthless = data.expired && (position.instrument.type === InstrumentType.EQUITY_OPTION || position.instrument.type === InstrumentType.FUTURE_OPTION);
|
|
518
528
|
|
|
@@ -560,7 +570,13 @@ module.exports = (() => {
|
|
|
560
570
|
let unrealizedToday;
|
|
561
571
|
let unrealizedTodayChange;
|
|
562
572
|
|
|
563
|
-
|
|
573
|
+
// 2025/07/20, BRI. The unrealized gain should only be calculated if the position
|
|
574
|
+
// has quoted today. That means the position item is date-aware at this point. In
|
|
575
|
+
// words, the phrase "today" is literal. It does not mean the gain on the last known
|
|
576
|
+
// price change (e.g. friday, last week, sometime in the past when the instrument
|
|
577
|
+
// was delisted, etc).
|
|
578
|
+
|
|
579
|
+
if (today && data.previousPrice && price) {
|
|
564
580
|
const unrealizedTodayBase = ValuationCalculator.calculate(position.instrument, data.previousPrice, snapshot.open);
|
|
565
581
|
|
|
566
582
|
unrealizedToday = market.subtract(unrealizedTodayBase);
|
|
@@ -121,6 +121,8 @@ module.exports = (() => {
|
|
|
121
121
|
.withField('snapshot.income', DataType.DECIMAL)
|
|
122
122
|
.withField('snapshot.value', DataType.DECIMAL)
|
|
123
123
|
.withField('snapshot.initial', DataType.forEnum(PositionDirection, 'PositionDirection'), true)
|
|
124
|
+
.withField('latest.date', DataType.DAY)
|
|
125
|
+
.withField('latest.gain', DataType.DECIMAL)
|
|
124
126
|
.withField('legacy.system', DataType.STRING, true)
|
|
125
127
|
.withField('legacy.user', DataType.STRING, true)
|
|
126
128
|
.withField('legacy.portfolio', DataType.STRING, true)
|
|
@@ -168,6 +170,8 @@ module.exports = (() => {
|
|
|
168
170
|
.withField('snapshot.income', DataType.DECIMAL)
|
|
169
171
|
.withField('snapshot.value', DataType.DECIMAL)
|
|
170
172
|
.withField('snapshot.initial', DataType.forEnum(PositionDirection, 'PositionDirection'), true)
|
|
173
|
+
.withField('latest.date', DataType.DAY)
|
|
174
|
+
.withField('latest.gain', DataType.DECIMAL)
|
|
171
175
|
.withField('system.calculate.processors', DataType.NUMBER, true)
|
|
172
176
|
.withField('system.locked', DataType.BOOLEAN, true)
|
|
173
177
|
.withField('previous', DataType.NUMBER, true)
|
|
@@ -137,6 +137,7 @@ module.exports = (() => {
|
|
|
137
137
|
.withField('amount', DataType.DECIMAL)
|
|
138
138
|
.withField('quantity', DataType.DECIMAL)
|
|
139
139
|
.withField('fee', DataType.DECIMAL, true)
|
|
140
|
+
.withField('gain', DataType.DECIMAL)
|
|
140
141
|
.withField('reference.position', DataType.STRING, true)
|
|
141
142
|
.withField('reference.transaction', DataType.STRING, true)
|
|
142
143
|
.withField('snapshot.open', DataType.DECIMAL)
|
|
@@ -187,6 +188,7 @@ module.exports = (() => {
|
|
|
187
188
|
.withField('amount', DataType.DECIMAL)
|
|
188
189
|
.withField('quantity', DataType.DECIMAL)
|
|
189
190
|
.withField('fee', DataType.DECIMAL, true)
|
|
191
|
+
.withField('gain', DataType.DECIMAL)
|
|
190
192
|
.withField('reference.position', DataType.STRING, true)
|
|
191
193
|
.withField('reference.transaction', DataType.NUMBER, true)
|
|
192
194
|
.withField('snapshot.open', DataType.DECIMAL)
|