Skip to content

Commit 2337d0e

Browse files
pcaspersjenkins
authored andcommitted
QPR-12119 introduce private regression model class, to be implemented
1 parent 12222c5 commit 2337d0e

2 files changed

Lines changed: 128 additions & 128 deletions

File tree

QuantExt/qle/pricingengines/mcmultilegbaseengine.cpp

Lines changed: 74 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,6 @@ McMultiLegBaseEngine::CashflowInfo McMultiLegBaseEngine::createCashflowInfo(boos
585585
effectiveRate * fxFixing;
586586
};
587587

588-
589588
return info;
590589
}
591590

@@ -736,10 +735,15 @@ void McMultiLegBaseEngine::calculate() const {
736735
std::vector<std::vector<RandomVariable>> pathValues(
737736
simulationTimes.size(),
738737
std::vector<RandomVariable>(model_->stateProcess()->size(), RandomVariable(calibrationSamples_)));
738+
std::vector<std::vector<const RandomVariable*>> pathValuesRef(
739+
simulationTimes.size(), std::vector<const RandomVariable*>(model_->stateProcess()->size()));
739740

740-
for (auto& p : pathValues)
741-
for (auto& r : p)
742-
r.expand();
741+
for (Size i = 0; i < pathValues.size(); ++i) {
742+
for (Size j = 0; j < pathValues[i].size(); ++j) {
743+
pathValues[i][j].expand();
744+
pathValuesRef[i][j] = &pathValues[i][j];
745+
}
746+
}
743747

744748
TimeGrid timeGrid(simulationTimes.begin(), simulationTimes.end());
745749

@@ -772,19 +776,17 @@ void McMultiLegBaseEngine::calculate() const {
772776

773777
// for each xva and exercise time collect the relevant cashflow amounts and train a model on them
774778

775-
std::vector<Array> coeffsUndDirty(exerciseXvaTimes.size()); // available on xva times
776-
std::vector<Array> coeffsUndExInto(exerciseXvaTimes.size()); // available on xva and ex times
777-
std::vector<Array> coeffsContinuationValue(exerciseXvaTimes.size()); // available on ex times
778-
std::vector<Array> coeffsOption(exerciseXvaTimes.size()); // available on xva and ex times
779+
std::vector<RegressionModel> regModelUndDirty(exerciseXvaTimes.size()); // available on xva times
780+
std::vector<RegressionModel> regModelUndExInto(exerciseXvaTimes.size()); // available on xva and ex times
781+
std::vector<RegressionModel> regModelContinuationValue(exerciseXvaTimes.size()); // available on ex times
782+
std::vector<RegressionModel> regModelOption(exerciseXvaTimes.size()); // available on xva and ex times
779783

780784
auto basisFns =
781785
RandomVariableLsmBasisSystem::multiPathBasisSystem(model_->stateProcess()->size(), polynomOrder_, polynomType_);
782786

783787
enum class CfStatus { open, cached, done };
784788
std::vector<CfStatus> cfStatus(cashflowInfo.size(), CfStatus::open);
785789

786-
std::vector<const RandomVariable*> regressor(model_->stateProcess()->size());
787-
788790
RandomVariable pathValueUndDirty(calibrationSamples_);
789791
RandomVariable pathValueUndExInto(calibrationSamples_);
790792
RandomVariable pathValueOption(calibrationSamples_);
@@ -824,34 +826,41 @@ void McMultiLegBaseEngine::calculate() const {
824826
}
825827
}
826828

827-
/* possible refinement: do the regression on pairs
828-
( std::min(cashflowSimTime, t) , modelIndex state at this time )
829-
taken from the cf info sim times, model indices */
830-
831-
for (Size i = 0; i < model_->stateProcess()->size(); ++i) {
832-
regressor[i] = &pathValues[timeIndex(*t, simulationTimes)][i];
829+
if (exercise_ != nullptr) {
830+
regModelUndExInto[counter] =
831+
RegressionModel(*t, cashflowInfo, [&cfStatus](std::size_t i) { return cfStatus[i] == CfStatus::done; });
832+
regModelUndExInto[counter].train(polynomOrder_, polynomType_, pathValueUndExInto, pathValuesRef,
833+
simulationTimes);
833834
}
834835

835-
if (exercise_ != nullptr)
836-
coeffsUndExInto[counter] = regressionCoefficients(pathValueUndExInto, regressor, basisFns);
837-
838836
if (isExerciseTime) {
839-
auto exerciseValue = conditionalExpectation(regressor, basisFns, coeffsUndExInto[counter]);
840-
coeffsContinuationValue[counter] = regressionCoefficients(
841-
pathValueOption, regressor, basisFns, exerciseValue > RandomVariable(calibrationSamples_, 0));
842-
auto continuationValue = conditionalExpectation(regressor, basisFns, coeffsContinuationValue[counter]);
837+
auto exerciseValue = regModelUndExInto[counter].apply(pathValuesRef, simulationTimes);
838+
regModelContinuationValue[counter] =
839+
RegressionModel(*t, cashflowInfo, [&cfStatus](std::size_t i) { return cfStatus[i] == CfStatus::done; });
840+
regModelContinuationValue[counter].train(polynomOrder_, polynomType_, pathValueOption, pathValuesRef,
841+
simulationTimes,
842+
exerciseValue > RandomVariable(calibrationSamples_, 0));
843+
auto continuationValue = regModelContinuationValue[counter].apply(pathValuesRef, simulationTimes);
843844
pathValueOption = conditionalResult(exerciseValue > continuationValue &&
844845
exerciseValue > RandomVariable(calibrationSamples_, 0),
845846
pathValueUndExInto, pathValueOption);
846-
coeffsOption[counter] = regressionCoefficients(pathValueOption, regressor, basisFns);
847+
regModelOption[counter] =
848+
RegressionModel(*t, cashflowInfo, [&cfStatus](std::size_t i) { return cfStatus[i] == CfStatus::done; });
849+
regModelOption[counter].train(polynomOrder_, polynomType_, pathValueOption, pathValuesRef, simulationTimes);
847850
}
848851

849852
if (isXvaTime) {
850-
coeffsUndDirty[counter] = regressionCoefficients(pathValueUndDirty, regressor, basisFns);
853+
regModelUndDirty[counter] =
854+
RegressionModel(*t, cashflowInfo, [&cfStatus](std::size_t i) { return cfStatus[i] != CfStatus::open; });
855+
regModelUndDirty[counter].train(polynomOrder_, polynomType_, pathValueUndDirty, pathValuesRef,
856+
simulationTimes);
851857
}
852858

853-
if (exercise_ != nullptr)
854-
coeffsOption[counter] = regressionCoefficients(pathValueOption, regressor, basisFns);
859+
if (exercise_ != nullptr) {
860+
regModelOption[counter] =
861+
RegressionModel(*t, cashflowInfo, [&cfStatus](std::size_t i) { return cfStatus[i] == CfStatus::done; });
862+
regModelOption[counter].train(polynomOrder_, polynomType_, pathValueOption, pathValuesRef, simulationTimes);
863+
}
855864

856865
--counter;
857866
}
@@ -875,29 +884,31 @@ void McMultiLegBaseEngine::calculate() const {
875884
// construct the amc calculator
876885

877886
amcCalculator_ = boost::make_shared<MultiLegBaseAmcCalculator>(
878-
externalModelIndices_, optionSettlement_, exerciseXvaTimes, exerciseTimes, xvaTimes, coeffsUndDirty,
879-
coeffsUndExInto, coeffsContinuationValue, coeffsOption, basisFns, resultValue_,
887+
externalModelIndices_, optionSettlement_, exerciseXvaTimes, exerciseTimes, xvaTimes, regModelUndDirty,
888+
regModelUndExInto, regModelContinuationValue, regModelOption, basisFns, resultValue_,
880889
model_->stateProcess()->initialValues(), model_->irlgm1f(0)->currency());
881890
}
882891

883892
boost::shared_ptr<AmcCalculator> McMultiLegBaseEngine::amcCalculator() const { return amcCalculator_; }
884893

885-
MultiLegBaseAmcCalculator::MultiLegBaseAmcCalculator(
894+
McMultiLegBaseEngine::MultiLegBaseAmcCalculator::MultiLegBaseAmcCalculator(
886895
const std::vector<Size>& externalModelIndices, const Settlement::Type settlement,
887896
const std::set<Real>& exerciseXvaTimes, const std::set<Real>& exerciseTimes, const std::set<Real>& xvaTimes,
888-
const std::vector<Array>& coeffsUndDirty, const std::vector<Array>& coeffsUndExInto,
889-
const std::vector<Array>& coeffsContinuationValue, const std::vector<Array>& coeffsOption,
897+
const std::vector<McMultiLegBaseEngine::RegressionModel>& regModelUndDirty,
898+
const std::vector<McMultiLegBaseEngine::RegressionModel>& regModelUndExInto,
899+
const std::vector<McMultiLegBaseEngine::RegressionModel>& regModelContinuationValue,
900+
const std::vector<McMultiLegBaseEngine::RegressionModel>& regModelOption,
890901
const std::vector<std::function<RandomVariable(const std::vector<const RandomVariable*>&)>>& basisFns,
891902
const Real resultValue, const Array& initialState, const Currency& baseCurrency)
892903
: externalModelIndices_(externalModelIndices), settlement_(settlement), exerciseXvaTimes_(exerciseXvaTimes),
893-
exerciseTimes_(exerciseTimes), xvaTimes_(xvaTimes), coeffsUndDirty_(coeffsUndDirty),
894-
coeffsUndExInto_(coeffsUndExInto), coeffsContinuationValue_(coeffsContinuationValue), coeffsOption_(coeffsOption),
895-
basisFns_(basisFns), resultValue_(resultValue), initialState_(initialState), baseCurrency_(baseCurrency) {}
904+
exerciseTimes_(exerciseTimes), xvaTimes_(xvaTimes), regModelUndDirty_(regModelUndDirty),
905+
regModelUndExInto_(regModelUndExInto), regModelContinuationValue_(regModelContinuationValue),
906+
regModelOption_(regModelOption), basisFns_(basisFns), resultValue_(resultValue), initialState_(initialState),
907+
baseCurrency_(baseCurrency) {}
896908

897-
std::vector<QuantExt::RandomVariable>
898-
MultiLegBaseAmcCalculator::simulatePath(const std::vector<QuantLib::Real>& pathTimes,
899-
std::vector<std::vector<QuantExt::RandomVariable>>& paths,
900-
const std::vector<bool>& isRelevantTime, const bool stickyCloseOutRun) {
909+
std::vector<QuantExt::RandomVariable> McMultiLegBaseEngine::MultiLegBaseAmcCalculator::simulatePath(
910+
const std::vector<QuantLib::Real>& pathTimes, std::vector<std::vector<QuantExt::RandomVariable>>& paths,
911+
const std::vector<bool>& isRelevantTime, const bool stickyCloseOutRun) {
901912

902913
// check input path consistency
903914

@@ -912,31 +923,20 @@ MultiLegBaseAmcCalculator::simulatePath(const std::vector<QuantLib::Real>& pathT
912923
std::vector<std::vector<const RandomVariable*>> effPaths(
913924
xvaTimes_.size(), std::vector<const RandomVariable*>(externalModelIndices_.size()));
914925

915-
std::vector<Real> simTimes;
916926
Size timeIndex = 0;
917927
for (Size i = 0; i < pathTimes.size(); ++i) {
918928
if (isRelevantTime[i]) {
919-
920-
int ind = stickyCloseOutRun ? i - 1 : i;
921-
QL_REQUIRE(ind >= 0,
922-
"MultiLegBaseAmcCalculator: sticky close out run time index is negative - internal error.");
923-
simTimes.push_back(pathTimes[ind]);
924-
925929
for (Size j = 0; j < externalModelIndices_.size(); ++j) {
926930
effPaths[timeIndex][j] = &paths[i][externalModelIndices_[j]];
927931
}
928932
++timeIndex;
929933
}
930934
}
931935

932-
QL_REQUIRE(simTimes.size() == xvaTimes_.size(),
933-
"MultiLegBaseAmcCalculator::simulatePath(): expected input path size "
934-
<< xvaTimes_.size() << ", but got " << simTimes.size());
935-
936936
// init result vector
937937

938938
Size samples = paths.front().front().size();
939-
std::vector<RandomVariable> result(simTimes.size() + 1, RandomVariable(paths.front().front().size(), 0.0));
939+
std::vector<RandomVariable> result(xvaTimes_.size() + 1, RandomVariable(paths.front().front().size(), 0.0));
940940

941941
// simulate the path: result at first time index is simply the reference date npv
942942

@@ -960,8 +960,7 @@ MultiLegBaseAmcCalculator::simulatePath(const std::vector<QuantLib::Real>& pathT
960960
QL_REQUIRE(ind < exerciseXvaTimes_.size(),
961961
"MultiLegBaseAmcCalculator::simulatePath(): internal error, xva time "
962962
<< t << " not found in exerciseXvaTimes vector.");
963-
result[counter + 1] = conditionalExpectation(effPaths[counter], basisFns_, coeffsUndDirty_[ind]);
964-
++counter;
963+
result[++counter] = regModelUndDirty_[ind].apply(effPaths, xvaTimes_);
965964
}
966965
return result;
967966
}
@@ -982,43 +981,10 @@ MultiLegBaseAmcCalculator::simulatePath(const std::vector<QuantLib::Real>& pathT
982981
"MultiLegBaseAmcCalculator::simulatePath(): internal error, exercise time "
983982
<< t << " not found in exerciseXvaTimes vector.");
984983

985-
// find the sim times and model states before and after the exercise time
986-
987-
auto t2 = std::lower_bound(xvaTimes_.begin(), xvaTimes_.end(), t);
988-
989-
// exercise time is after last simulation time => we never exercise on such a path
990-
991-
if (t2 == xvaTimes_.end())
992-
break;
993-
994-
Real time2 = *t2;
995-
std::vector<const RandomVariable*> s2 = effPaths[std::distance(xvaTimes_.begin(), t2)];
996-
997-
Real time1;
998-
std::vector<const RandomVariable*> s1;
999-
if (t2 == xvaTimes_.begin()) {
1000-
time1 = 0.0;
1001-
s1 = initialStatePointer;
1002-
} else {
1003-
time1 = *std::next(t2, -1);
1004-
s1 = effPaths[std::distance(xvaTimes_.begin(), std::next(t2, -1))];
1005-
}
1006-
1007-
// compute the interpolated state (brownian bridge would be better)
1008-
1009-
std::vector<RandomVariable> s(externalModelIndices_.size());
1010-
std::vector<const RandomVariable*> sp(externalModelIndices_.size());
1011-
for (Size j = 0; j < externalModelIndices_.size(); ++j) {
1012-
RandomVariable alpha1(samples, (time2 - t) / (time2 - time1));
1013-
RandomVariable alpha2(samples, (t - time1) / (time2 - time1));
1014-
s[j] = alpha1 * *s1[j] + alpha2 * *s2[j];
1015-
sp[j] = &s[j];
1016-
}
1017-
1018984
// make the exercise decision
1019985

1020-
RandomVariable exerciseValue = conditionalExpectation(sp, basisFns_, coeffsUndExInto_[ind]);
1021-
RandomVariable continuationValue = conditionalExpectation(sp, basisFns_, coeffsContinuationValue_[ind]);
986+
RandomVariable exerciseValue = regModelUndExInto_[ind].apply(effPaths, xvaTimes_);
987+
RandomVariable continuationValue = regModelContinuationValue_[ind].apply(effPaths, xvaTimes_);
1022988

1023989
exercised_[counter + 1] = !exercised_[counter] && exerciseValue > continuationValue &&
1024990
exerciseValue > RandomVariable(samples, 0.0);
@@ -1045,8 +1011,7 @@ MultiLegBaseAmcCalculator::simulatePath(const std::vector<QuantLib::Real>& pathT
10451011

10461012
if (xvaTimes_.find(t) != xvaTimes_.end()) {
10471013

1048-
RandomVariable optionValue =
1049-
conditionalExpectation(effPaths[xvaCounter], basisFns_, coeffsOption_[counter]);
1014+
RandomVariable optionValue = regModelOption_[counter].apply(effPaths, xvaTimes_);
10501015

10511016
/* Exercise value is "undExInto" if we are in the period between the date on which the exercise happend and
10521017
the next exercise date after that, otherwise it is the full dirty npv. This assumes that two exercise
@@ -1062,9 +1027,8 @@ MultiLegBaseAmcCalculator::simulatePath(const std::vector<QuantLib::Real>& pathT
10621027
uses the full dirty npv at a too early time. */
10631028

10641029
RandomVariable exercisedValue =
1065-
conditionalResult(exercised_[exerciseCounter],
1066-
conditionalExpectation(effPaths[xvaCounter], basisFns_, coeffsUndExInto_[counter]),
1067-
conditionalExpectation(effPaths[xvaCounter], basisFns_, coeffsUndDirty_[counter]));
1030+
conditionalResult(exercised_[exerciseCounter], regModelUndExInto_[counter].apply(effPaths, xvaTimes_),
1031+
regModelUndDirty_[counter].apply(effPaths, xvaTimes_));
10681032

10691033
if (settlement_ == Settlement::Type::Cash) {
10701034
exercisedValue = applyInverseFilter(exercisedValue, cashExerciseValueWasAccountedForOnXvaTime);
@@ -1082,4 +1046,21 @@ MultiLegBaseAmcCalculator::simulatePath(const std::vector<QuantLib::Real>& pathT
10821046
return result;
10831047
}
10841048

1049+
McMultiLegBaseEngine::RegressionModel::RegressionModel(const Real observationTime,
1050+
const std::vector<CashflowInfo>& cashflowInfo,
1051+
const std::function<bool(std::size_t)>& cashflowRelevant)
1052+
: observationTime_(observationTime) {}
1053+
1054+
void McMultiLegBaseEngine::RegressionModel::train(const Size polynomOrder,
1055+
const LsmBasisSystem::PolynomialType polynomType,
1056+
const RandomVariable& regressand,
1057+
const std::vector<std::vector<const RandomVariable*>>& paths,
1058+
const std::set<Real>& pathTimes, const Filter& filter) {}
1059+
1060+
RandomVariable
1061+
McMultiLegBaseEngine::RegressionModel::apply(const std::vector<std::vector<const RandomVariable*>>& paths,
1062+
const std::set<Real>& pathTimes) const {
1063+
QL_FAIL("apply not implemented.");
1064+
}
1065+
10851066
} // namespace QuantExt

0 commit comments

Comments
 (0)