@barchart/portfolio-api-common 2.2.1 → 4.1.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 +20 -8
- package/lib/processing/PositionGroup.js +67 -46
- package/lib/processing/PositionItem.js +68 -10
- 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
|
|
|
@@ -625,6 +623,20 @@ module.exports = (() => {
|
|
|
625
623
|
}
|
|
626
624
|
}
|
|
627
625
|
|
|
626
|
+
/**
|
|
627
|
+
* Sets the reference date (today).
|
|
628
|
+
*
|
|
629
|
+
* @public
|
|
630
|
+
* @param {Day} referenceDate
|
|
631
|
+
*/
|
|
632
|
+
setReferenceDate(referenceDate) {
|
|
633
|
+
this._referenceDate = referenceDate;
|
|
634
|
+
|
|
635
|
+
this._items.forEach((item) => {
|
|
636
|
+
item.setReferenceDate(this._referenceDate);
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
|
|
628
640
|
/**
|
|
629
641
|
* Returns current price for symbol provided.
|
|
630
642
|
*
|
|
@@ -4,7 +4,6 @@ const array = require('@barchart/common-js/lang/array'),
|
|
|
4
4
|
CurrencyTranslator = require('@barchart/common-js/lang/CurrencyTranslator'),
|
|
5
5
|
Decimal = require('@barchart/common-js/lang/Decimal'),
|
|
6
6
|
Disposable = require('@barchart/common-js/lang/Disposable'),
|
|
7
|
-
DisposableStack = require('@barchart/common-js/collections/specialized/DisposableStack'),
|
|
8
7
|
Event = require('@barchart/common-js/messaging/Event'),
|
|
9
8
|
formatter = require('@barchart/common-js/lang/formatter'),
|
|
10
9
|
is = require('@barchart/common-js/lang/is');
|
|
@@ -63,8 +62,6 @@ module.exports = (() => {
|
|
|
63
62
|
this._groupExcludedChangeEvent = new Event(this);
|
|
64
63
|
this._showClosedPositionsChangeEvent = new Event(this);
|
|
65
64
|
|
|
66
|
-
this._disposeStack = new DisposableStack();
|
|
67
|
-
|
|
68
65
|
this._excludedItems = [ ];
|
|
69
66
|
this._excludedItemMap = { };
|
|
70
67
|
this._consideredItems = this._items.slice(0);
|
|
@@ -131,6 +128,7 @@ module.exports = (() => {
|
|
|
131
128
|
this._dataActual.basis = null;
|
|
132
129
|
this._dataActual.basis2 = null;
|
|
133
130
|
this._dataActual.realized = null;
|
|
131
|
+
this._dataActual.realizedToday = null;
|
|
134
132
|
this._dataActual.income = null;
|
|
135
133
|
this._dataActual.market = null;
|
|
136
134
|
this._dataActual.market2 = null;
|
|
@@ -138,6 +136,7 @@ module.exports = (() => {
|
|
|
138
136
|
this._dataActual.marketPercentPortfolio = null;
|
|
139
137
|
this._dataActual.unrealized = null;
|
|
140
138
|
this._dataActual.unrealizedToday = null;
|
|
139
|
+
this._dataActual.gainToday = null;
|
|
141
140
|
this._dataActual.total = null;
|
|
142
141
|
this._dataActual.summaryTotalCurrent = null;
|
|
143
142
|
this._dataActual.summaryTotalPrevious = null;
|
|
@@ -157,6 +156,7 @@ module.exports = (() => {
|
|
|
157
156
|
this._dataFormat.basis2 = null;
|
|
158
157
|
this._dataFormat.realized = null;
|
|
159
158
|
this._dataFormat.realizedPercent = null;
|
|
159
|
+
this._dataFormat.realizedToday = null;
|
|
160
160
|
this._dataFormat.income = null;
|
|
161
161
|
this._dataFormat.market = null;
|
|
162
162
|
this._dataFormat.market2 = null;
|
|
@@ -168,6 +168,8 @@ module.exports = (() => {
|
|
|
168
168
|
this._dataFormat.unrealizedNegative = false;
|
|
169
169
|
this._dataFormat.unrealizedToday = null;
|
|
170
170
|
this._dataFormat.unrealizedTodayNegative = false;
|
|
171
|
+
this._dataFormat.gainToday = null;
|
|
172
|
+
this._dataFormat.gainTodayNegative = false;
|
|
171
173
|
this._dataFormat.total = null;
|
|
172
174
|
this._dataFormat.totalNegative = false;
|
|
173
175
|
this._dataFormat.summaryTotalCurrent = null;
|
|
@@ -593,52 +595,54 @@ module.exports = (() => {
|
|
|
593
595
|
calculatePriceData(this, sender, false);
|
|
594
596
|
});
|
|
595
597
|
|
|
596
|
-
|
|
598
|
+
const fundamentalBinding = item.registerFundamentalDataChangeHandler((data) => {
|
|
597
599
|
if (this._single) {
|
|
598
600
|
this._dataFormat.fundamental = data;
|
|
599
|
-
} else {
|
|
600
|
-
const fundamentalFields = [ 'percentChange1m', 'percentChange1y', 'percentChange3m', 'percentChangeYtd' ];
|
|
601
601
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
const fundamentalFields = [ 'percentChange1m', 'percentChange1y', 'percentChange3m', 'percentChangeYtd' ];
|
|
605
606
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
607
|
+
const fundamentalData = this.items.reduce((sums, item, i) => {
|
|
608
|
+
if (item.data && item.data.fundamental && item.data.fundamental.raw) {
|
|
609
|
+
const fundamental = item.data.fundamental.raw;
|
|
609
610
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
}
|
|
611
|
+
fundamentalFields.forEach((fieldName) => {
|
|
612
|
+
const summary = sums[fieldName];
|
|
613
|
+
const value = fundamental[fieldName];
|
|
614
614
|
|
|
615
|
-
|
|
616
|
-
|
|
615
|
+
if (is.number(value)) {
|
|
616
|
+
summary.total = sums[fieldName].total + value;
|
|
617
|
+
summary.count = sums[fieldName].count + 1;
|
|
618
|
+
}
|
|
617
619
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
} else {
|
|
621
|
-
averageFormat = '—';
|
|
622
|
-
}
|
|
620
|
+
if ((i + 1) == this.items.length) {
|
|
621
|
+
let averageFormat;
|
|
623
622
|
|
|
624
|
-
|
|
623
|
+
if (summary.count > 0) {
|
|
624
|
+
averageFormat = formatPercent(new Decimal(summary.total / summary.count), 2, true);
|
|
625
|
+
} else {
|
|
626
|
+
averageFormat = '—';
|
|
625
627
|
}
|
|
626
|
-
});
|
|
627
|
-
}
|
|
628
628
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
629
|
+
summary.averageFormat = averageFormat;
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
}
|
|
632
633
|
|
|
633
|
-
|
|
634
|
-
|
|
634
|
+
return sums;
|
|
635
|
+
}, fundamentalFields.reduce((sums, fieldName) => {
|
|
636
|
+
sums[fieldName] = { total: 0, count: 0, averageFormat: '—' };
|
|
635
637
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
+
return sums;
|
|
639
|
+
}, { }));
|
|
638
640
|
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
641
|
+
this._dataFormat.fundamental = fundamentalFields.reduce((sums, fieldName) => {
|
|
642
|
+
sums[fieldName] = fundamentalData[fieldName].averageFormat;
|
|
643
|
+
|
|
644
|
+
return sums;
|
|
645
|
+
}, { });
|
|
642
646
|
});
|
|
643
647
|
|
|
644
648
|
let newsBinding = Disposable.getEmpty();
|
|
@@ -660,7 +664,7 @@ module.exports = (() => {
|
|
|
660
664
|
});
|
|
661
665
|
}
|
|
662
666
|
|
|
663
|
-
|
|
667
|
+
const portfolioChangeBinding = item.registerPortfolioChangeHandler((portfolio) => {
|
|
664
668
|
const descriptionSelector = this._definition.descriptionSelector;
|
|
665
669
|
|
|
666
670
|
this._description = descriptionSelector(this._items[0]);
|
|
@@ -675,21 +679,27 @@ module.exports = (() => {
|
|
|
675
679
|
const currencySelector = this._definition.currencySelector;
|
|
676
680
|
|
|
677
681
|
this.changeCurrency(currencySelector({ portfolio }));
|
|
678
|
-
})
|
|
682
|
+
});
|
|
679
683
|
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
this._disposeStack.push(calculatingBinding);
|
|
684
|
-
this._disposeStack.push(newsBinding);
|
|
684
|
+
const referenceDateBinding = item.registerReferenceDateChangeHandler(() => {
|
|
685
|
+
this.refresh();
|
|
686
|
+
});
|
|
685
687
|
|
|
686
|
-
|
|
687
|
-
|
|
688
|
+
let disposalBinding = null;
|
|
689
|
+
|
|
690
|
+
disposalBinding = item.registerPositionItemDisposeHandler(() => {
|
|
688
691
|
quoteBinding.dispose();
|
|
692
|
+
fundamentalBinding.dispose();
|
|
693
|
+
|
|
689
694
|
newsBinding.dispose();
|
|
690
695
|
lockedBinding.dispose();
|
|
691
696
|
calculatingBinding.dispose();
|
|
692
697
|
|
|
698
|
+
portfolioChangeBinding.dispose();
|
|
699
|
+
referenceDateBinding.dispose();
|
|
700
|
+
|
|
701
|
+
disposalBinding.dispose();
|
|
702
|
+
|
|
693
703
|
array.remove(this._items, i => i === item);
|
|
694
704
|
array.remove(this._excludedItems, i => i === item);
|
|
695
705
|
array.remove(this._consideredItems, i => i === item);
|
|
@@ -697,7 +707,7 @@ module.exports = (() => {
|
|
|
697
707
|
delete this._excludedItemMap[item.position.position];
|
|
698
708
|
|
|
699
709
|
this.refresh();
|
|
700
|
-
})
|
|
710
|
+
});
|
|
701
711
|
}
|
|
702
712
|
|
|
703
713
|
function formatNumber(number, precision) {
|
|
@@ -821,6 +831,7 @@ module.exports = (() => {
|
|
|
821
831
|
|
|
822
832
|
updates.realized = updates.realized.add(translate(item, item.data.realized));
|
|
823
833
|
updates.unrealized = updates.unrealized.add(translate(item, item.data.unrealized));
|
|
834
|
+
updates.realizedToday = updates.realizedToday.add(translate(item, item.data.realizedToday));
|
|
824
835
|
updates.income = updates.income.add(translate(item, item.data.income));
|
|
825
836
|
updates.summaryTotalCurrent = updates.summaryTotalCurrent.add(translate(item, item.data.periodGain));
|
|
826
837
|
updates.summaryTotalPrevious = updates.summaryTotalPrevious.add(translate(item, item.data.periodGainPrevious));
|
|
@@ -847,6 +858,7 @@ module.exports = (() => {
|
|
|
847
858
|
basis2: Decimal.ZERO,
|
|
848
859
|
realized: Decimal.ZERO,
|
|
849
860
|
unrealized: Decimal.ZERO,
|
|
861
|
+
realizedToday: Decimal.ZERO,
|
|
850
862
|
income: Decimal.ZERO,
|
|
851
863
|
summaryTotalCurrent: Decimal.ZERO,
|
|
852
864
|
summaryTotalPrevious: Decimal.ZERO,
|
|
@@ -867,6 +879,7 @@ module.exports = (() => {
|
|
|
867
879
|
actual.basis2 = updates.basis2;
|
|
868
880
|
actual.realized = updates.realized;
|
|
869
881
|
actual.unrealized = updates.unrealized;
|
|
882
|
+
actual.realizedToday = updates.realizedToday;
|
|
870
883
|
actual.income = updates.income;
|
|
871
884
|
actual.summaryTotalCurrent = updates.summaryTotalCurrent;
|
|
872
885
|
actual.summaryTotalPrevious = updates.summaryTotalPrevious;
|
|
@@ -886,6 +899,7 @@ module.exports = (() => {
|
|
|
886
899
|
format.basis2 = formatCurrency(actual.basis2, currency);
|
|
887
900
|
format.realized = formatCurrency(actual.realized, currency);
|
|
888
901
|
format.unrealized = formatCurrency(actual.unrealized, currency);
|
|
902
|
+
format.realizedToday = formatCurrency(actual.realizedToday, currency);
|
|
889
903
|
format.income = formatCurrency(actual.income, currency);
|
|
890
904
|
format.summaryTotalCurrent = formatCurrency(updates.summaryTotalCurrent, currency);
|
|
891
905
|
format.summaryTotalCurrentNegative = updates.summaryTotalCurrent.getIsNegative();
|
|
@@ -995,6 +1009,7 @@ module.exports = (() => {
|
|
|
995
1009
|
updates.marketAbsolute = updates.marketAbsolute.add(translate(item, item.data.marketAbsolute));
|
|
996
1010
|
updates.unrealized = updates.unrealized.add(translate(item, item.data.unrealized));
|
|
997
1011
|
updates.unrealizedToday = updates.unrealizedToday.add(translate(item, item.data.unrealizedToday));
|
|
1012
|
+
updates.gainToday = updates.gainToday.add(translate(item, item.data.unrealizedToday.add(item.data.realizedToday)));
|
|
998
1013
|
updates.summaryTotalCurrent = updates.summaryTotalCurrent.add(translate(item, item.data.periodGain));
|
|
999
1014
|
updates.periodUnrealized = updates.periodUnrealized.add(translate(item, item.data.periodUnrealized));
|
|
1000
1015
|
|
|
@@ -1006,6 +1021,7 @@ module.exports = (() => {
|
|
|
1006
1021
|
marketDirection: unchanged,
|
|
1007
1022
|
unrealized: Decimal.ZERO,
|
|
1008
1023
|
unrealizedToday: Decimal.ZERO,
|
|
1024
|
+
gainToday: Decimal.ZERO,
|
|
1009
1025
|
summaryTotalCurrent: Decimal.ZERO,
|
|
1010
1026
|
periodUnrealized: Decimal.ZERO
|
|
1011
1027
|
});
|
|
@@ -1024,6 +1040,7 @@ module.exports = (() => {
|
|
|
1024
1040
|
updates.marketDirection = { up: item.data.marketChange.getIsPositive(), down: item.data.marketChange.getIsNegative() };
|
|
1025
1041
|
updates.unrealized = actual.unrealized.add(translate(item, item.data.unrealizedChange));
|
|
1026
1042
|
updates.unrealizedToday = actual.unrealizedToday.add(translate(item, item.data.unrealizedTodayChange));
|
|
1043
|
+
updates.gainToday = actual.gainToday.add(translate(item, item.data.unrealizedTodayChange));
|
|
1027
1044
|
updates.summaryTotalCurrent = actual.summaryTotalCurrent.add(translate(item, item.data.periodGainChange));
|
|
1028
1045
|
updates.periodUnrealized = actual.periodUnrealized.add(translate(item, item.data.periodUnrealizedChange));
|
|
1029
1046
|
}
|
|
@@ -1033,6 +1050,7 @@ module.exports = (() => {
|
|
|
1033
1050
|
actual.marketAbsolute = updates.marketAbsolute;
|
|
1034
1051
|
actual.unrealized = updates.unrealized;
|
|
1035
1052
|
actual.unrealizedToday = updates.unrealizedToday;
|
|
1053
|
+
actual.gainToday = updates.gainToday;
|
|
1036
1054
|
actual.summaryTotalCurrent = updates.summaryTotalCurrent;
|
|
1037
1055
|
actual.periodUnrealized = updates.periodUnrealized;
|
|
1038
1056
|
|
|
@@ -1071,6 +1089,9 @@ module.exports = (() => {
|
|
|
1071
1089
|
format.unrealizedToday = formatCurrency(actual.unrealizedToday, currency);
|
|
1072
1090
|
format.unrealizedTodayNegative = actual.unrealizedToday.getIsNegative();
|
|
1073
1091
|
|
|
1092
|
+
format.gainToday = formatCurrency(actual.gainToday, currency);
|
|
1093
|
+
format.gainTodayNegative = actual.gainToday.getIsNegative();
|
|
1094
|
+
|
|
1074
1095
|
format.summaryTotalCurrent = formatCurrency(actual.summaryTotalCurrent, currency);
|
|
1075
1096
|
format.summaryTotalCurrentNegative = actual.summaryTotalCurrent.getIsNegative();
|
|
1076
1097
|
|
|
@@ -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
|
|
|
@@ -116,10 +120,11 @@ module.exports = (() => {
|
|
|
116
120
|
this._lockChangedEvent = new Event(this);
|
|
117
121
|
this._calculatingChangedEvent = new Event(this);
|
|
118
122
|
this._portfolioChangedEvent = new Event(this);
|
|
123
|
+
this._referenceDateChangedEvent = new Event(this);
|
|
119
124
|
this._positionItemDisposeEvent = new Event(this);
|
|
120
125
|
|
|
121
|
-
calculateStaticData(this);
|
|
122
|
-
calculatePriceData(this, null);
|
|
126
|
+
calculateStaticData(this, this._referenceDate);
|
|
127
|
+
calculatePriceData(this, null, null);
|
|
123
128
|
}
|
|
124
129
|
|
|
125
130
|
/**
|
|
@@ -250,14 +255,13 @@ module.exports = (() => {
|
|
|
250
255
|
return;
|
|
251
256
|
}
|
|
252
257
|
|
|
253
|
-
if (this.
|
|
258
|
+
if (this._currentPrice !== quote.lastPrice || force) {
|
|
254
259
|
if (quote.previousPrice) {
|
|
255
260
|
this._data.previousPrice = quote.previousPrice;
|
|
256
261
|
}
|
|
257
262
|
|
|
258
|
-
calculatePriceData(this, quote.lastPrice);
|
|
263
|
+
calculatePriceData(this, quote.lastPrice, getQuoteIsToday(quote, this._referenceDate));
|
|
259
264
|
|
|
260
|
-
this._currentPricePrevious = this._currentPrice;
|
|
261
265
|
this._currentPrice = quote.lastPrice;
|
|
262
266
|
|
|
263
267
|
this._previousQuote = this._currentQuote;
|
|
@@ -342,6 +346,31 @@ module.exports = (() => {
|
|
|
342
346
|
}
|
|
343
347
|
}
|
|
344
348
|
|
|
349
|
+
/**
|
|
350
|
+
* Sets the reference date (today).
|
|
351
|
+
*
|
|
352
|
+
* @public
|
|
353
|
+
* @param {Day} referenceDate
|
|
354
|
+
*/
|
|
355
|
+
setReferenceDate(referenceDate) {
|
|
356
|
+
assert.argumentIsRequired(referenceDate, 'referenceDate', Day, 'Day');
|
|
357
|
+
|
|
358
|
+
if (this.getIsDisposed()) {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (this._referenceDate.getIsEqual(referenceDate)) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
this._referenceDate = referenceDate;
|
|
367
|
+
|
|
368
|
+
calculateStaticData(this, this._referenceDate);
|
|
369
|
+
calculatePriceData(this, this._currentPrice, getQuoteIsToday(this._currentQuote, this._referenceDate));
|
|
370
|
+
|
|
371
|
+
this._referenceDateChangedEvent.fire(this._referenceDate);
|
|
372
|
+
}
|
|
373
|
+
|
|
345
374
|
/**
|
|
346
375
|
* Registers an observer for quote changes, which is fired after internal recalculations
|
|
347
376
|
* of position data are complete.
|
|
@@ -399,7 +428,7 @@ module.exports = (() => {
|
|
|
399
428
|
}
|
|
400
429
|
|
|
401
430
|
/**
|
|
402
|
-
* Registers an observer changes to portfolio metadata.
|
|
431
|
+
* Registers an observer for changes to portfolio metadata.
|
|
403
432
|
*
|
|
404
433
|
* @public
|
|
405
434
|
* @param {Function} handler
|
|
@@ -409,6 +438,17 @@ module.exports = (() => {
|
|
|
409
438
|
return this._portfolioChangedEvent.register(handler);
|
|
410
439
|
}
|
|
411
440
|
|
|
441
|
+
/**
|
|
442
|
+
* Registers an observer for changes to the reference date (today).
|
|
443
|
+
*
|
|
444
|
+
* @public
|
|
445
|
+
* @param {Function} handler
|
|
446
|
+
* @returns {Disposable}
|
|
447
|
+
*/
|
|
448
|
+
registerReferenceDateChangeHandler(handler) {
|
|
449
|
+
return this._referenceDateChangedEvent.register(handler);
|
|
450
|
+
}
|
|
451
|
+
|
|
412
452
|
/**
|
|
413
453
|
* Registers an observer for object disposal.
|
|
414
454
|
*
|
|
@@ -427,7 +467,9 @@ module.exports = (() => {
|
|
|
427
467
|
this._newsExistsChangedEvent.clear();
|
|
428
468
|
this._fundamentalDataChangedEvent.clear();
|
|
429
469
|
this._lockChangedEvent.clear();
|
|
470
|
+
this._calculatingChangedEvent.clear();
|
|
430
471
|
this._portfolioChangedEvent.clear();
|
|
472
|
+
this._referenceDateChangedEvent.clear();
|
|
431
473
|
this._positionItemDisposeEvent.clear();
|
|
432
474
|
}
|
|
433
475
|
|
|
@@ -436,7 +478,7 @@ module.exports = (() => {
|
|
|
436
478
|
}
|
|
437
479
|
}
|
|
438
480
|
|
|
439
|
-
function calculateStaticData(item) {
|
|
481
|
+
function calculateStaticData(item, referenceDate) {
|
|
440
482
|
const position = item.position;
|
|
441
483
|
|
|
442
484
|
const currentSummary = item.currentSummary;
|
|
@@ -467,6 +509,12 @@ module.exports = (() => {
|
|
|
467
509
|
data.realized = snapshot.gain;
|
|
468
510
|
data.unrealized = Decimal.ZERO;
|
|
469
511
|
|
|
512
|
+
if (position.latest && position.latest.date && position.latest.date.getIsEqual(referenceDate) && position.latest.gain) {
|
|
513
|
+
data.realizedToday = position.latest.gain;
|
|
514
|
+
} else {
|
|
515
|
+
data.realizedToday = Decimal.ZERO;
|
|
516
|
+
}
|
|
517
|
+
|
|
470
518
|
data.income = snapshot.income;
|
|
471
519
|
|
|
472
520
|
data.marketPrevious = previousSummary1 === null ? Decimal.ZERO : previousSummary1.end.value;
|
|
@@ -503,7 +551,7 @@ module.exports = (() => {
|
|
|
503
551
|
data.totalDivisor = calculateTotalDivisor(position.instrument.type, data.initiate, position);
|
|
504
552
|
}
|
|
505
553
|
|
|
506
|
-
function calculatePriceData(item, price) {
|
|
554
|
+
function calculatePriceData(item, price, today) {
|
|
507
555
|
const position = item.position;
|
|
508
556
|
const snapshot = getSnapshot(position, item.currentSummary, item._reporting);
|
|
509
557
|
|
|
@@ -512,7 +560,7 @@ module.exports = (() => {
|
|
|
512
560
|
// 2023/11/28, BRI. Futures contracts do not have their value set to zero
|
|
513
561
|
// after expiration. At expiration, the contract would have been closed
|
|
514
562
|
// (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.
|
|
563
|
+
// contracts can expire worthless, and we attempt to represent that here.
|
|
516
564
|
|
|
517
565
|
const worthless = data.expired && (position.instrument.type === InstrumentType.EQUITY_OPTION || position.instrument.type === InstrumentType.FUTURE_OPTION);
|
|
518
566
|
|
|
@@ -560,7 +608,13 @@ module.exports = (() => {
|
|
|
560
608
|
let unrealizedToday;
|
|
561
609
|
let unrealizedTodayChange;
|
|
562
610
|
|
|
563
|
-
|
|
611
|
+
// 2025/07/20, BRI. The unrealized gain should only be calculated if the position
|
|
612
|
+
// has quoted today. That means the position item is date-aware at this point. In
|
|
613
|
+
// words, the phrase "today" is literal. It does not mean the gain on the last known
|
|
614
|
+
// price change (e.g. friday, last week, sometime in the past when the instrument
|
|
615
|
+
// was delisted, etc).
|
|
616
|
+
|
|
617
|
+
if (today && data.previousPrice && price) {
|
|
564
618
|
const unrealizedTodayBase = ValuationCalculator.calculate(position.instrument, data.previousPrice, snapshot.open);
|
|
565
619
|
|
|
566
620
|
unrealizedToday = market.subtract(unrealizedTodayBase);
|
|
@@ -830,5 +884,9 @@ module.exports = (() => {
|
|
|
830
884
|
return snapshot;
|
|
831
885
|
}
|
|
832
886
|
|
|
887
|
+
function getQuoteIsToday(quote, referenceDate) {
|
|
888
|
+
return quote && quote.lastDay instanceof Day && referenceDate instanceof Day && quote.lastDay.getIsEqual(referenceDate);
|
|
889
|
+
}
|
|
890
|
+
|
|
833
891
|
return PositionItem;
|
|
834
892
|
})();
|
|
@@ -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)
|