@@ -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
883892boost::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