Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
Classes | Public Types | Protected Member Functions | Protected Attributes | Private Member Functions | Private Attributes | Static Private Attributes | List of all members
McMultiLegBaseEngine Class Reference

#include <qle/pricingengines/mcmultilegbaseengine.hpp>

+ Inheritance diagram for McMultiLegBaseEngine:
+ Collaboration diagram for McMultiLegBaseEngine:

Classes

struct  CashflowInfo
 
class  MultiLegBaseAmcCalculator
 
class  RegressionModel
 

Public Types

enum  RegressorModel { Simple , LaggedFX }
 

Protected Member Functions

 McMultiLegBaseEngine (const Handle< CrossAssetModel > &model, const SequenceType calibrationPathGenerator, const SequenceType pricingPathGenerator, const Size calibrationSamples, const Size pricingSamples, const Size calibrationSeed, const Size pricingSeed, const Size polynomOrder, const LsmBasisSystem::PolynomialType polynomType, const SobolBrownianGenerator::Ordering ordering, const SobolRsg::DirectionIntegers directionIntegers, const std::vector< Handle< YieldTermStructure > > &discountCurves=std::vector< Handle< YieldTermStructure > >(), const std::vector< Date > &simulationDates=std::vector< Date >(), const std::vector< Size > &externalModelIndices=std::vector< Size >(), const bool minimalObsDate=true, const RegressorModel regressorModel=RegressorModel::Simple, const Real regressionVarianceCutoff=Null< Real >())
 
void calculate () const
 
QuantLib::ext::shared_ptr< AmcCalculatoramcCalculator () const
 

Protected Attributes

std::vector< Leg > leg_
 
std::vector< Currency > currency_
 
std::vector< boolpayer_
 
QuantLib::ext::shared_ptr< Exercise > exercise_
 
Settlement::Type optionSettlement_ = Settlement::Physical
 
bool includeSettlementDateFlows_ = false
 
Handle< CrossAssetModelmodel_
 
SequenceType calibrationPathGenerator_
 
SequenceType pricingPathGenerator_
 
Size calibrationSamples_
 
Size pricingSamples_
 
Size calibrationSeed_
 
Size pricingSeed_
 
Size polynomOrder_
 
LsmBasisSystem::PolynomialType polynomType_
 
SobolBrownianGenerator::Ordering ordering_
 
SobolRsg::DirectionIntegers directionIntegers_
 
std::vector< Handle< YieldTermStructure > > discountCurves_
 
std::vector< Date > simulationDates_
 
std::vector< Size > externalModelIndices_
 
bool minimalObsDate_
 
RegressorModel regressorModel_
 
Real regressionVarianceCutoff_
 
QuantLib::ext::shared_ptr< AmcCalculatoramcCalculator_
 
Real resultUnderlyingNpv_
 
Real resultValue_
 

Private Member Functions

Real time (const Date &d) const
 
CashflowInfo createCashflowInfo (QuantLib::ext::shared_ptr< CashFlow > flow, const Currency &payCcy, bool payer, Size legNo, Size cfNo) const
 
Size timeIndex (const Time t, const std::set< Real > &simulationTimes) const
 
RandomVariable cashflowPathValue (const CashflowInfo &cf, const std::vector< std::vector< RandomVariable > > &pathValues, const std::set< Real > &simulationTimes) const
 

Private Attributes

Date today_
 
std::vector< LgmVectorisedlgmVectorised_
 

Static Private Attributes

static constexpr Real tinyTime = 1E-10
 

Detailed Description

Definition at line 61 of file mcmultilegbaseengine.hpp.

Member Enumeration Documentation

◆ RegressorModel

Enumerator
Simple 
LaggedFX 

Definition at line 63 of file mcmultilegbaseengine.hpp.

Constructor & Destructor Documentation

◆ McMultiLegBaseEngine()

McMultiLegBaseEngine ( const Handle< CrossAssetModel > &  model,
const SequenceType  calibrationPathGenerator,
const SequenceType  pricingPathGenerator,
const Size  calibrationSamples,
const Size  pricingSamples,
const Size  calibrationSeed,
const Size  pricingSeed,
const Size  polynomOrder,
const LsmBasisSystem::PolynomialType  polynomType,
const SobolBrownianGenerator::Ordering  ordering,
const SobolRsg::DirectionIntegers  directionIntegers,
const std::vector< Handle< YieldTermStructure > > &  discountCurves = std::vector<Handle<YieldTermStructure>>(),
const std::vector< Date > &  simulationDates = std::vector<Date>(),
const std::vector< Size > &  externalModelIndices = std::vector<Size>(),
const bool  minimalObsDate = true,
const RegressorModel  regressorModel = RegressorModel::Simple,
const Real  regressionVarianceCutoff = Null<Real>() 
)
protected

The npv is computed in the model's base currency, discounting curves are taken from the model. simulationDates are additional simulation dates. The cross asset model here must be consistent with the multi path that is the input to AmcCalculator::simulatePath().

Current limitations:

  • the parameter minimalObsDate is ignored, the corresponding optimization is not implemented yet
  • pricingSamples are ignored, the npv from the training phase is used alway

Definition at line 43 of file mcmultilegbaseengine.cpp.

51 : model_(model), calibrationPathGenerator_(calibrationPathGenerator), pricingPathGenerator_(pricingPathGenerator),
52 calibrationSamples_(calibrationSamples), pricingSamples_(pricingSamples), calibrationSeed_(calibrationSeed),
53 pricingSeed_(pricingSeed), polynomOrder_(polynomOrder), polynomType_(polynomType), ordering_(ordering),
54 directionIntegers_(directionIntegers), discountCurves_(discountCurves), simulationDates_(simulationDates),
55 externalModelIndices_(externalModelIndices), minimalObsDate_(minimalObsDate), regressorModel_(regressorModel),
56 regressionVarianceCutoff_(regressionVarianceCutoff) {
57
58 if (discountCurves_.empty())
60 else {
61 QL_REQUIRE(discountCurves_.size() == model_->components(CrossAssetModel::AssetType::IR),
62 "McMultiLegBaseEngine: " << discountCurves_.size() << " discount curves given, but model has "
63 << model_->components(CrossAssetModel::AssetType::IR) << " IR components.");
64 }
65}
SobolBrownianGenerator::Ordering ordering_
SobolRsg::DirectionIntegers directionIntegers_
LsmBasisSystem::PolynomialType polynomType_
std::vector< Handle< YieldTermStructure > > discountCurves_
Handle< CrossAssetModel > model_

Member Function Documentation

◆ calculate()

void calculate ( ) const
protected

Definition at line 686 of file mcmultilegbaseengine.cpp.

686 {
687
688 McEngineStats::instance().other_timer.resume();
689
690 // check data set by derived engines
691
692 QL_REQUIRE(currency_.size() == leg_.size(), "McMultiLegBaseEngine: number of legs ("
693 << leg_.size() << ") does not match currencies ("
694 << currency_.size() << ")");
695 QL_REQUIRE(payer_.size() == leg_.size(), "McMultiLegBaseEngine: number of legs ("
696 << leg_.size() << ") does not match payer flag (" << payer_.size()
697 << ")");
698
699 // set today's date
700
701 today_ = model_->irlgm1f(0)->termStructure()->referenceDate();
702
703 // set up lgm vectorized instances for each currency
704
705 if (lgmVectorised_.empty()) {
706 for (Size i = 0; i < model_->components(CrossAssetModel::AssetType::IR); ++i) {
707 lgmVectorised_.push_back(LgmVectorised(model_->irlgm1f(i)));
708 }
709 }
710
711 // populate the info to generate the (alive) cashflow amounts
712
713 std::vector<CashflowInfo> cashflowInfo;
714
715 Size legNo = 0;
716 for (auto const& leg : leg_) {
717 Currency currency = currency_[legNo];
718 bool payer = payer_[legNo];
719 Size cashflowNo = 0;
720 for (auto const& cashflow : leg) {
721 // we can skip cashflows that are paid
722 if (cashflow->date() < today_ || (!includeSettlementDateFlows_ && cashflow->date() == today_))
723 continue;
724 // for an alive cashflow, populate the data
725 cashflowInfo.push_back(createCashflowInfo(cashflow, currency, payer, legNo, cashflowNo));
726 // increment counter
727 ++cashflowNo;
728 }
729 ++legNo;
730 }
731
732 /* build exercise times and xva times */
733
734 std::set<Real> exerciseTimes;
735 std::set<Real> xvaTimes;
736
737 if (exercise_ != nullptr) {
738
739 QL_REQUIRE(exercise_->type() != Exercise::American,
740 "McMultiLegBaseEngine::calculate(): exercise style American is not supported yet.");
741
742 for (auto const& d : exercise_->dates()) {
743 if (d <= today_)
744 continue;
745 exerciseTimes.insert(time(d));
746 }
747 }
748
749 for (auto const& d : simulationDates_) {
750 xvaTimes.insert(time(d));
751 }
752
753 /* build cashflow generation times */
754
755 std::set<Real> cashflowGenTimes;
756
757 for (auto const& info : cashflowInfo) {
758 cashflowGenTimes.insert(info.simulationTimes.begin(), info.simulationTimes.end());
759 cashflowGenTimes.insert(info.payTime);
760 }
761
762 cashflowGenTimes.erase(0.0); // handled separately, if it is set by a cashflow
763
764 /* build combined time sets */
765
766 std::set<Real> exerciseXvaTimes; // = exercise + xva times
767 std::set<Real> simulationTimes; // = cashflowGen + exercise + xva times
768
769 exerciseXvaTimes.insert(exerciseTimes.begin(), exerciseTimes.end());
770 exerciseXvaTimes.insert(xvaTimes.begin(), xvaTimes.end());
771
772 simulationTimes.insert(cashflowGenTimes.begin(), cashflowGenTimes.end());
773 simulationTimes.insert(exerciseTimes.begin(), exerciseTimes.end());
774 simulationTimes.insert(xvaTimes.begin(), xvaTimes.end());
775
776 McEngineStats::instance().other_timer.stop();
777
778 // simulate the paths for the calibration
779
780 McEngineStats::instance().path_timer.resume();
781
782 QL_REQUIRE(!simulationTimes.empty(),
783 "McMultiLegBaseEngine::calculate(): no simulation times, this is not expected.");
784 std::vector<std::vector<RandomVariable>> pathValues(
785 simulationTimes.size(),
786 std::vector<RandomVariable>(model_->stateProcess()->size(), RandomVariable(calibrationSamples_)));
787 std::vector<std::vector<const RandomVariable*>> pathValuesRef(
788 simulationTimes.size(), std::vector<const RandomVariable*>(model_->stateProcess()->size()));
789
790 for (Size i = 0; i < pathValues.size(); ++i) {
791 for (Size j = 0; j < pathValues[i].size(); ++j) {
792 pathValues[i][j].expand();
793 pathValuesRef[i][j] = &pathValues[i][j];
794 }
795 }
796
797 TimeGrid timeGrid(simulationTimes.begin(), simulationTimes.end());
798
799 QuantLib::ext::shared_ptr<StochasticProcess> process = model_->stateProcess();
800 if (model_->dimension() == 1) {
801 // use lgm process if possible for better performance
802 auto tmp = QuantLib::ext::make_shared<IrLgm1fStateProcess>(model_->irlgm1f(0));
803 tmp->resetCache(timeGrid.size() - 1);
804 process = tmp;
805 } else if (auto tmp = QuantLib::ext::dynamic_pointer_cast<CrossAssetStateProcess>(process)) {
806 // enable cache
807 tmp->resetCache(timeGrid.size() - 1);
808 }
809
810 auto pathGenerator = makeMultiPathGenerator(calibrationPathGenerator_, process, timeGrid, calibrationSeed_,
812
813 for (Size i = 0; i < calibrationSamples_; ++i) {
814 const MultiPath& path = pathGenerator->next().value;
815 for (Size j = 0; j < simulationTimes.size(); ++j) {
816 for (Size k = 0; k < model_->stateProcess()->size(); ++k) {
817 pathValues[j][k].data()[i] = path[k][j + 1];
818 }
819 }
820 }
821
822 McEngineStats::instance().path_timer.stop();
823
824 McEngineStats::instance().calc_timer.resume();
825
826 // for each xva and exercise time collect the relevant cashflow amounts and train a model on them
827
828 std::vector<RegressionModel> regModelUndDirty(exerciseXvaTimes.size()); // available on xva times
829 std::vector<RegressionModel> regModelUndExInto(exerciseXvaTimes.size()); // available on xva and ex times
830 std::vector<RegressionModel> regModelContinuationValue(exerciseXvaTimes.size()); // available on ex times
831 std::vector<RegressionModel> regModelOption(exerciseXvaTimes.size()); // available on xva and ex times
832
833 enum class CfStatus { open, cached, done };
834 std::vector<CfStatus> cfStatus(cashflowInfo.size(), CfStatus::open);
835
836 RandomVariable pathValueUndDirty(calibrationSamples_);
837 RandomVariable pathValueUndExInto(calibrationSamples_);
838 RandomVariable pathValueOption(calibrationSamples_);
839
840 std::vector<RandomVariable> amountCache(cashflowInfo.size());
841
842 Size counter = exerciseXvaTimes.size() - 1;
843
844 for (auto t = exerciseXvaTimes.rbegin(); t != exerciseXvaTimes.rend(); ++t) {
845
846 bool isExerciseTime = exerciseTimes.find(*t) != exerciseTimes.end();
847 bool isXvaTime = xvaTimes.find(*t) != xvaTimes.end();
848
849 for (Size i = 0; i < cashflowInfo.size(); ++i) {
850
851 /* we assume here that exIntoCriterionTime > t implies payTime > t, this must be ensured by the
852 createCashflowInfo method */
853
854 if (cfStatus[i] == CfStatus::open) {
855 if (cashflowInfo[i].exIntoCriterionTime > *t) {
856 auto tmp = cashflowPathValue(cashflowInfo[i], pathValues, simulationTimes);
857 pathValueUndDirty += tmp;
858 pathValueUndExInto += tmp;
859 cfStatus[i] = CfStatus::done;
860 } else if (cashflowInfo[i].payTime > *t - (includeSettlementDateFlows_ ? tinyTime : 0.0)) {
861 auto tmp = cashflowPathValue(cashflowInfo[i], pathValues, simulationTimes);
862 pathValueUndDirty += tmp;
863 amountCache[i] = tmp;
864 cfStatus[i] = CfStatus::cached;
865 }
866 } else if (cfStatus[i] == CfStatus::cached) {
867 if (cashflowInfo[i].exIntoCriterionTime > *t) {
868 pathValueUndExInto += amountCache[i];
869 cfStatus[i] = CfStatus::done;
870 amountCache[i].clear();
871 }
872 }
873 }
874
875 if (exercise_ != nullptr) {
876 regModelUndExInto[counter] = RegressionModel(
877 *t, cashflowInfo, [&cfStatus](std::size_t i) { return cfStatus[i] == CfStatus::done; }, **model_,
879 regModelUndExInto[counter].train(polynomOrder_, polynomType_, pathValueUndExInto, pathValuesRef,
880 simulationTimes);
881 }
882
883 if (isExerciseTime) {
884 auto exerciseValue = regModelUndExInto[counter].apply(model_->stateProcess()->initialValues(),
885 pathValuesRef, simulationTimes);
886 regModelContinuationValue[counter] = RegressionModel(
887 *t, cashflowInfo, [&cfStatus](std::size_t i) { return cfStatus[i] == CfStatus::done; }, **model_,
889 regModelContinuationValue[counter].train(polynomOrder_, polynomType_, pathValueOption, pathValuesRef,
890 simulationTimes,
891 exerciseValue > RandomVariable(calibrationSamples_, 0.0));
892 auto continuationValue = regModelContinuationValue[counter].apply(model_->stateProcess()->initialValues(),
893 pathValuesRef, simulationTimes);
894 pathValueOption = conditionalResult(exerciseValue > continuationValue &&
895 exerciseValue > RandomVariable(calibrationSamples_, 0.0),
896 pathValueUndExInto, pathValueOption);
897 regModelOption[counter] = RegressionModel(
898 *t, cashflowInfo, [&cfStatus](std::size_t i) { return cfStatus[i] == CfStatus::done; }, **model_,
900 regModelOption[counter].train(polynomOrder_, polynomType_, pathValueOption, pathValuesRef, simulationTimes);
901 }
902
903 if (isXvaTime) {
904 regModelUndDirty[counter] = RegressionModel(
905 *t, cashflowInfo, [&cfStatus](std::size_t i) { return cfStatus[i] != CfStatus::open; }, **model_,
907 regModelUndDirty[counter].train(polynomOrder_, polynomType_, pathValueUndDirty, pathValuesRef,
908 simulationTimes);
909 }
910
911 if (exercise_ != nullptr) {
912 regModelOption[counter] = RegressionModel(
913 *t, cashflowInfo, [&cfStatus](std::size_t i) { return cfStatus[i] == CfStatus::done; }, **model_,
915 regModelOption[counter].train(polynomOrder_, polynomType_, pathValueOption, pathValuesRef, simulationTimes);
916 }
917
918 --counter;
919 }
920
921 // add the remaining live cashflows to get the underlying value
922
923 for (Size i = 0; i < cashflowInfo.size(); ++i) {
924 if (cfStatus[i] == CfStatus::open)
925 pathValueUndDirty += cashflowPathValue(cashflowInfo[i], pathValues, simulationTimes);
926 }
927
928 // set the result value (= underlying value if no exercise is given, otherwise option value)
929
930 resultUnderlyingNpv_ = expectation(pathValueUndDirty).at(0) * model_->numeraire(0, 0.0, 0.0, discountCurves_[0]);
931 resultValue_ = exercise_ == nullptr
933 : expectation(pathValueOption).at(0) * model_->numeraire(0, 0.0, 0.0, discountCurves_[0]);
934
935 McEngineStats::instance().calc_timer.stop();
936
937 // construct the amc calculator
938
939 amcCalculator_ = QuantLib::ext::make_shared<MultiLegBaseAmcCalculator>(
940 externalModelIndices_, optionSettlement_, exerciseXvaTimes, exerciseTimes, xvaTimes, regModelUndDirty,
941 regModelUndExInto, regModelContinuationValue, regModelOption, resultValue_,
942 model_->stateProcess()->initialValues(), model_->irlgm1f(0)->currency());
943}
std::vector< LgmVectorised > lgmVectorised_
QuantLib::ext::shared_ptr< Exercise > exercise_
RandomVariable cashflowPathValue(const CashflowInfo &cf, const std::vector< std::vector< RandomVariable > > &pathValues, const std::set< Real > &simulationTimes) const
QuantLib::ext::shared_ptr< AmcCalculator > amcCalculator_
CashflowInfo createCashflowInfo(QuantLib::ext::shared_ptr< CashFlow > flow, const Currency &payCcy, bool payer, Size legNo, Size cfNo) const
Real time(const Date &d) const
QuantLib::ext::shared_ptr< MultiPathGeneratorBase > makeMultiPathGenerator(const SequenceType s, const QuantLib::ext::shared_ptr< StochasticProcess > &process, const TimeGrid &timeGrid, const BigNatural seed, const SobolBrownianGenerator::Ordering ordering, const SobolRsg::DirectionIntegers directionIntegers)
Make function for path generators.
RandomVariable conditionalResult(const Filter &f, RandomVariable x, const RandomVariable &y)
RandomVariable expectation(const RandomVariable &r)
Real at(const Size i) const
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ amcCalculator()

QuantLib::ext::shared_ptr< AmcCalculator > amcCalculator ( ) const
protected

Definition at line 945 of file mcmultilegbaseengine.cpp.

945{ return amcCalculator_; }
+ Here is the caller graph for this function:

◆ time()

Real time ( const Date &  d) const
private

Definition at line 67 of file mcmultilegbaseengine.cpp.

67 {
68 return model_->irlgm1f(0)->termStructure()->timeFromReference(d);
69}
+ Here is the caller graph for this function:

◆ createCashflowInfo()

McMultiLegBaseEngine::CashflowInfo createCashflowInfo ( QuantLib::ext::shared_ptr< CashFlow >  flow,
const Currency &  payCcy,
bool  payer,
Size  legNo,
Size  cfNo 
) const
private

Definition at line 71 of file mcmultilegbaseengine.cpp.

73 {
74 CashflowInfo info;
75
76 // set some common info: pay time, pay ccy index in the model, payer, exercise into decision time
77
78 info.legNo = legNo;
79 info.cfNo = cfNo;
80 info.payTime = time(flow->date());
81 info.payCcyIndex = model_->ccyIndex(payCcy);
82 info.payer = payer;
83
84 if (auto cpn = QuantLib::ext::dynamic_pointer_cast<Coupon>(flow)) {
85 QL_REQUIRE(cpn->accrualStartDate() < flow->date(),
86 "McMultiLegBaseEngine::createCashflowInfo(): coupon leg "
87 << legNo << " cashflow " << cfNo << " has accrual start date (" << cpn->accrualStartDate()
88 << ") >= pay date (" << flow->date()
89 << "), which breaks an assumption in the engine. This situation is unexpected.");
90 info.exIntoCriterionTime = time(cpn->accrualStartDate()) + tinyTime;
91 } else {
92 info.exIntoCriterionTime = info.payTime;
93 }
94
95 // Handle SimpleCashflow
96 if (QuantLib::ext::dynamic_pointer_cast<SimpleCashFlow>(flow) != nullptr) {
97 info.amountCalculator = [flow](const Size n, const std::vector<std::vector<const RandomVariable*>>& states) {
98 return RandomVariable(n, flow->amount());
99 };
100 return info;
101 }
102
103 // handle fx linked fixed cashflow
104 if (auto fxl = QuantLib::ext::dynamic_pointer_cast<FXLinkedCashFlow>(flow)) {
105 Date fxLinkedFixingDate = fxl->fxFixingDate();
106 Size fxLinkedSourceCcyIdx = model_->ccyIndex(fxl->fxIndex()->sourceCurrency());
107 Size fxLinkedTargetCcyIdx = model_->ccyIndex(fxl->fxIndex()->targetCurrency());
108 if (fxLinkedFixingDate > today_) {
109 Real fxSimTime = time(fxLinkedFixingDate);
110 info.simulationTimes.push_back(fxSimTime);
111 info.modelIndices.push_back({});
112 if (fxLinkedSourceCcyIdx > 0) {
113 info.modelIndices.front().push_back(
114 model_->pIdx(CrossAssetModel::AssetType::FX, fxLinkedSourceCcyIdx - 1));
115 }
116 if (fxLinkedTargetCcyIdx > 0) {
117 info.modelIndices.front().push_back(
118 model_->pIdx(CrossAssetModel::AssetType::FX, fxLinkedTargetCcyIdx - 1));
119 }
120 }
121 info.amountCalculator = [this, fxLinkedSourceCcyIdx, fxLinkedTargetCcyIdx, fxLinkedFixingDate,
122 fxl](const Size n, const std::vector<std::vector<const RandomVariable*>>& states) {
123 if (fxLinkedFixingDate <= today_)
124 return RandomVariable(n, fxl->amount());
125 RandomVariable fxSource(n, 1.0), fxTarget(n, 1.0);
126 Size fxIdx = 0;
127 if (fxLinkedSourceCcyIdx > 0)
128 fxSource = exp(*states.at(0).at(fxIdx++));
129 if (fxLinkedTargetCcyIdx > 0)
130 fxTarget = exp(*states.at(0).at(fxIdx));
131 return RandomVariable(n, fxl->foreignAmount()) * fxSource / fxTarget;
132 };
133
134 return info;
135 }
136
137 // handle some wrapped coupon types: extract the wrapper info and continue with underlying flow
138 bool isFxLinked = false;
139 bool isFxIndexed = false;
140 Size fxLinkedSourceCcyIdx = Null<Size>();
141 Size fxLinkedTargetCcyIdx = Null<Size>();
142 Real fxLinkedFixedFxRate = Null<Real>();
143 Real fxLinkedSimTime = Null<Real>(); // if fx fixing date > today
144 Real fxLinkedForeignNominal = Null<Real>();
145 std::vector<Size> fxLinkedModelIndices;
146
147 // A Coupon could be wrapped in a FxLinkedCoupon or IndexedCoupon but not both at the same time
148 if (auto indexCpn = QuantLib::ext::dynamic_pointer_cast<IndexedCoupon>(flow)) {
149 if (auto fxIndex = QuantLib::ext::dynamic_pointer_cast<FxIndex>(indexCpn->index())) {
150 isFxIndexed = true;
151 auto fixingDate = indexCpn->fixingDate();
152 fxLinkedSourceCcyIdx = model_->ccyIndex(fxIndex->sourceCurrency());
153 fxLinkedTargetCcyIdx = model_->ccyIndex(fxIndex->targetCurrency());
154 if (fixingDate <= today_) {
155 fxLinkedFixedFxRate = fxIndex->fixing(fixingDate);
156 } else {
157 fxLinkedSimTime = time(fixingDate);
158 if (fxLinkedSourceCcyIdx > 0) {
159 fxLinkedModelIndices.push_back(
160 model_->pIdx(CrossAssetModel::AssetType::FX, fxLinkedSourceCcyIdx - 1));
161 }
162 if (fxLinkedTargetCcyIdx > 0) {
163 fxLinkedModelIndices.push_back(
164 model_->pIdx(CrossAssetModel::AssetType::FX, fxLinkedTargetCcyIdx - 1));
165 }
166 }
167 flow = indexCpn->underlying();
168 }
169 } else if (auto fxl = QuantLib::ext::dynamic_pointer_cast<FloatingRateFXLinkedNotionalCoupon>(flow)) {
170 isFxLinked = true;
171 auto fixingDate = fxl->fxFixingDate();
172 fxLinkedSourceCcyIdx = model_->ccyIndex(fxl->fxIndex()->sourceCurrency());
173 fxLinkedTargetCcyIdx = model_->ccyIndex(fxl->fxIndex()->targetCurrency());
174 if (fixingDate <= today_) {
175 fxLinkedFixedFxRate = fxl->fxIndex()->fixing(fixingDate);
176 } else {
177 fxLinkedSimTime = time(fixingDate);
178 if (fxLinkedSourceCcyIdx > 0) {
179 fxLinkedModelIndices.push_back(model_->pIdx(CrossAssetModel::AssetType::FX, fxLinkedSourceCcyIdx - 1));
180 }
181 if (fxLinkedTargetCcyIdx > 0) {
182 fxLinkedModelIndices.push_back(model_->pIdx(CrossAssetModel::AssetType::FX, fxLinkedTargetCcyIdx - 1));
183 }
184 }
185 flow = fxl->underlying();
186 fxLinkedForeignNominal = fxl->foreignAmount();
187 }
188
189 bool isCapFloored = false;
190 bool isNakedOption = false;
191 Real effCap = Null<Real>(), effFloor = Null<Real>();
192 if (auto stripped = QuantLib::ext::dynamic_pointer_cast<StrippedCappedFlooredCoupon>(flow)) {
193 isNakedOption = true;
194 flow = stripped->underlying(); // this is a CappedFlooredCoupon, handled below
195 }
196
197 if (auto cf = QuantLib::ext::dynamic_pointer_cast<CappedFlooredCoupon>(flow)) {
198 isCapFloored = true;
199 effCap = cf->effectiveCap();
200 effFloor = cf->effectiveFloor();
201 flow = cf->underlying();
202 }
203
204 // handle the coupon types
205
206 if (QuantLib::ext::dynamic_pointer_cast<FixedRateCoupon>(flow) != nullptr) {
207
208 if (fxLinkedSimTime != Null<Real>()) {
209 info.simulationTimes.push_back(fxLinkedSimTime);
210 info.modelIndices.push_back(fxLinkedModelIndices);
211 }
212
213 info.amountCalculator = [flow, isFxLinked, isFxIndexed, fxLinkedFixedFxRate,
214 fxLinkedSourceCcyIdx, fxLinkedTargetCcyIdx](const Size n, const std::vector<std::vector<const RandomVariable*>>& states) {
215 RandomVariable fxFixing(n, 1.0);
216 if (isFxLinked || isFxIndexed) {
217 if (fxLinkedFixedFxRate != Null<Real>()) {
218 fxFixing = RandomVariable(n, fxLinkedFixedFxRate);
219 } else {
220 RandomVariable fxSource(n, 1.0), fxTarget(n, 1.0);
221 Size fxIdx = 0;
222 if (fxLinkedSourceCcyIdx > 0)
223 fxSource = exp(*states.at(0).at(fxIdx++));
224 if (fxLinkedTargetCcyIdx > 0)
225 fxTarget = exp(*states.at(0).at(fxIdx));
226 fxFixing = fxSource / fxTarget;
227 }
228 }
229 return fxFixing * RandomVariable(n, flow->amount());
230 };
231 return info;
232 }
233
234 if (auto ibor = QuantLib::ext::dynamic_pointer_cast<IborCoupon>(flow)) {
235 Real fixedRate =
236 ibor->fixingDate() <= today_ ? (ibor->rate() - ibor->spread()) / ibor->gearing() : Null<Real>();
237 Size indexCcyIdx = model_->ccyIndex(ibor->index()->currency());
238 Real simTime = time(ibor->fixingDate());
239 if (ibor->fixingDate() > today_) {
240 info.simulationTimes.push_back(simTime);
241 info.modelIndices.push_back({model_->pIdx(CrossAssetModel::AssetType::IR, indexCcyIdx)});
242 }
243
244 if (fxLinkedSimTime != Null<Real>()) {
245 info.simulationTimes.push_back(fxLinkedSimTime);
246 info.modelIndices.push_back(fxLinkedModelIndices);
247 }
248
249 info.amountCalculator = [this, indexCcyIdx, ibor, simTime, fixedRate, isFxLinked, fxLinkedForeignNominal,
250 fxLinkedSourceCcyIdx, fxLinkedTargetCcyIdx, fxLinkedFixedFxRate, isCapFloored,
251 isNakedOption, effFloor, effCap, isFxIndexed](const Size n, const std::vector<std::vector<const RandomVariable*>>& states) {
252 RandomVariable fixing = fixedRate != Null<Real>()
253 ? RandomVariable(n, fixedRate)
254 : lgmVectorised_[indexCcyIdx].fixing(ibor->index(), ibor->fixingDate(), simTime,
255 *states.at(0).at(0));
256 RandomVariable fxFixing(n, 1.0);
257 if (isFxLinked || isFxIndexed) {
258 if (fxLinkedFixedFxRate != Null<Real>()) {
259 fxFixing = RandomVariable(n, fxLinkedFixedFxRate);
260 } else {
261 RandomVariable fxSource(n, 1.0), fxTarget(n, 1.0);
262 Size fxIdx = 0;
263 if (fxLinkedSourceCcyIdx > 0)
264 fxSource = exp(*states.at(1).at(fxIdx++));
265 if (fxLinkedTargetCcyIdx > 0)
266 fxTarget = exp(*states.at(1).at(fxIdx));
267 fxFixing = fxSource / fxTarget;
268 }
269 }
270
271 RandomVariable effectiveRate;
272 if (isCapFloored) {
273 RandomVariable swapletRate(n, 0.0);
274 RandomVariable floorletRate(n, 0.0);
275 RandomVariable capletRate(n, 0.0);
276 if (!isNakedOption)
277 swapletRate = RandomVariable(n, ibor->gearing()) * fixing + RandomVariable(n, ibor->spread());
278 if (effFloor != Null<Real>())
279 floorletRate = RandomVariable(n, ibor->gearing()) *
280 max(RandomVariable(n, effFloor) - fixing, RandomVariable(n, 0.0));
281 if (effCap != Null<Real>())
282 capletRate = RandomVariable(n, ibor->gearing()) *
283 max(fixing - RandomVariable(n, effCap), RandomVariable(n, 0.0)) *
284 RandomVariable(n, isNakedOption && effFloor == Null<Real>() ? -1.0 : 1.0);
285 effectiveRate = swapletRate + floorletRate - capletRate;
286 } else {
287 effectiveRate = RandomVariable(n, ibor->gearing()) * fixing + RandomVariable(n, ibor->spread());
288 }
289 return RandomVariable(n, (isFxLinked ? fxLinkedForeignNominal : ibor->nominal()) * ibor->accrualPeriod()) *
290 effectiveRate * fxFixing;
291 };
292
293 return info;
294 }
295
296 if (auto cms = QuantLib::ext::dynamic_pointer_cast<CmsCoupon>(flow)) {
297 Real fixedRate = cms->fixingDate() <= today_ ? (cms->rate() - cms->spread()) / cms->gearing() : Null<Real>();
298 Size indexCcyIdx = model_->ccyIndex(cms->index()->currency());
299 Real simTime = time(cms->fixingDate());
300 if (cms->fixingDate() > today_) {
301 info.simulationTimes.push_back(simTime);
302 info.modelIndices.push_back({model_->pIdx(CrossAssetModel::AssetType::IR, indexCcyIdx)});
303 }
304
305 if (fxLinkedSimTime != Null<Real>()) {
306 info.simulationTimes.push_back(fxLinkedSimTime);
307 info.modelIndices.push_back(fxLinkedModelIndices);
308 }
309
310 info.amountCalculator = [this, indexCcyIdx, cms, simTime, fixedRate, isFxLinked, fxLinkedForeignNominal,
311 fxLinkedSourceCcyIdx, fxLinkedTargetCcyIdx, fxLinkedFixedFxRate, isCapFloored,
312 isNakedOption, effFloor,
313 effCap, isFxIndexed](const Size n, const std::vector<std::vector<const RandomVariable*>>& states) {
314 RandomVariable fixing =
315 fixedRate != Null<Real>()
316 ? RandomVariable(n, fixedRate)
317 : lgmVectorised_[indexCcyIdx].fixing(cms->index(), cms->fixingDate(), simTime, *states.at(0).at(0));
318 RandomVariable fxFixing(n, 1.0);
319 if (isFxLinked || isFxIndexed) {
320 if (fxLinkedFixedFxRate != Null<Real>()) {
321 fxFixing = RandomVariable(n, fxLinkedFixedFxRate);
322 } else {
323 RandomVariable fxSource(n, 1.0), fxTarget(n, 1.0);
324 Size fxIdx = 0;
325 if (fxLinkedSourceCcyIdx > 0)
326 fxSource = exp(*states.at(1).at(fxIdx++));
327 if (fxLinkedTargetCcyIdx > 0)
328 fxTarget = exp(*states.at(1).at(fxIdx));
329 fxFixing = fxSource / fxTarget;
330 }
331 }
332
333 RandomVariable effectiveRate;
334 if (isCapFloored) {
335 RandomVariable swapletRate(n, 0.0);
336 RandomVariable floorletRate(n, 0.0);
337 RandomVariable capletRate(n, 0.0);
338 if (!isNakedOption)
339 swapletRate = RandomVariable(n, cms->gearing()) * fixing + RandomVariable(n, cms->spread());
340 if (effFloor != Null<Real>())
341 floorletRate = RandomVariable(n, cms->gearing()) *
342 max(RandomVariable(n, effFloor) - fixing, RandomVariable(n, 0.0));
343 if (effCap != Null<Real>())
344 capletRate = RandomVariable(n, cms->gearing()) *
345 max(fixing - RandomVariable(n, effCap), RandomVariable(n, 0.0)) *
346 RandomVariable(n, isNakedOption && effFloor == Null<Real>() ? -1.0 : 1.0);
347 effectiveRate = swapletRate + floorletRate - capletRate;
348 } else {
349 effectiveRate = RandomVariable(n, cms->gearing()) * fixing + RandomVariable(n, cms->spread());
350 }
351
352 return RandomVariable(n, (isFxLinked ? fxLinkedForeignNominal : cms->nominal()) * cms->accrualPeriod()) *
353 effectiveRate * fxFixing;
354 };
355
356 return info;
357 }
358
359 if (auto on = QuantLib::ext::dynamic_pointer_cast<OvernightIndexedCoupon>(flow)) {
360 Real simTime = std::max(0.0, time(on->valueDates().front()));
361 Size indexCcyIdx = model_->ccyIndex(on->index()->currency());
362 info.simulationTimes.push_back(simTime);
363 info.modelIndices.push_back({model_->pIdx(CrossAssetModel::AssetType::IR, indexCcyIdx)});
364
365 if (fxLinkedSimTime != Null<Real>()) {
366 info.simulationTimes.push_back(fxLinkedSimTime);
367 info.modelIndices.push_back(fxLinkedModelIndices);
368 }
369
370 info.amountCalculator = [this, indexCcyIdx, on, simTime, isFxLinked, fxLinkedForeignNominal,
371 fxLinkedSourceCcyIdx, fxLinkedTargetCcyIdx, fxLinkedFixedFxRate, isFxIndexed](
372 const Size n, const std::vector<std::vector<const RandomVariable*>>& states) {
373 RandomVariable effectiveRate = lgmVectorised_[indexCcyIdx].compoundedOnRate(
374 on->overnightIndex(), on->fixingDates(), on->valueDates(), on->dt(), on->rateCutoff(),
375 on->includeSpread(), on->spread(), on->gearing(), on->lookback(), Null<Real>(), Null<Real>(), false,
376 false, simTime, *states.at(0).at(0));
377 RandomVariable fxFixing(n, 1.0);
378 if (isFxLinked || isFxIndexed) {
379 if (fxLinkedFixedFxRate != Null<Real>()) {
380 fxFixing = RandomVariable(n, fxLinkedFixedFxRate);
381 } else {
382 RandomVariable fxSource(n, 1.0), fxTarget(n, 1.0);
383 Size fxIdx = 0;
384 if (fxLinkedSourceCcyIdx > 0)
385 fxSource = exp(*states.at(1).at(fxIdx++));
386 if (fxLinkedTargetCcyIdx > 0)
387 fxTarget = exp(*states.at(1).at(fxIdx));
388 fxFixing = fxSource / fxTarget;
389 }
390 }
391
392 return RandomVariable(n, (isFxLinked ? fxLinkedForeignNominal : on->nominal()) * on->accrualPeriod()) *
393 effectiveRate * fxFixing;
394 };
395
396 return info;
397 }
398
399 if (auto cfon = QuantLib::ext::dynamic_pointer_cast<CappedFlooredOvernightIndexedCoupon>(flow)) {
400 Real simTime = std::max(0.0, time(cfon->underlying()->valueDates().front()));
401 Size indexCcyIdx = model_->ccyIndex(cfon->underlying()->index()->currency());
402 info.simulationTimes.push_back(simTime);
403 info.modelIndices.push_back({model_->pIdx(CrossAssetModel::AssetType::IR, indexCcyIdx)});
404
405 if (fxLinkedSimTime != Null<Real>()) {
406 info.simulationTimes.push_back(fxLinkedSimTime);
407 info.modelIndices.push_back(fxLinkedModelIndices);
408 }
409
410 info.amountCalculator = [this, indexCcyIdx, cfon, simTime, isFxLinked, fxLinkedForeignNominal,
411 fxLinkedSourceCcyIdx, fxLinkedTargetCcyIdx, fxLinkedFixedFxRate, isFxIndexed](
412 const Size n, const std::vector<std::vector<const RandomVariable*>>& states) {
413 RandomVariable effectiveRate = lgmVectorised_[indexCcyIdx].compoundedOnRate(
414 cfon->underlying()->overnightIndex(), cfon->underlying()->fixingDates(),
415 cfon->underlying()->valueDates(), cfon->underlying()->dt(), cfon->underlying()->rateCutoff(),
416 cfon->underlying()->includeSpread(), cfon->underlying()->spread(), cfon->underlying()->gearing(),
417 cfon->underlying()->lookback(), cfon->cap(), cfon->floor(), cfon->localCapFloor(), cfon->nakedOption(),
418 simTime, *states.at(0).at(0));
419 RandomVariable fxFixing(n, 1.0);
420 if (isFxLinked || isFxIndexed) {
421 if (fxLinkedFixedFxRate != Null<Real>()) {
422 fxFixing = RandomVariable(n, fxLinkedFixedFxRate);
423 } else {
424 RandomVariable fxSource(n, 1.0), fxTarget(n, 1.0);
425 Size fxIdx = 0;
426 if (fxLinkedSourceCcyIdx > 0)
427 fxSource = exp(*states.at(1).at(fxIdx++));
428 if (fxLinkedTargetCcyIdx > 0)
429 fxTarget = exp(*states.at(1).at(fxIdx));
430 fxFixing = fxSource / fxTarget;
431 }
432 }
433 return RandomVariable(n, (isFxLinked ? fxLinkedForeignNominal : cfon->nominal()) * cfon->accrualPeriod()) *
434 effectiveRate * fxFixing;
435 };
436
437 return info;
438 }
439
440 if (auto av = QuantLib::ext::dynamic_pointer_cast<AverageONIndexedCoupon>(flow)) {
441 Real simTime = std::max(0.0, time(av->valueDates().front()));
442 Size indexCcyIdx = model_->ccyIndex(av->index()->currency());
443 info.simulationTimes.push_back(simTime);
444 info.modelIndices.push_back({model_->pIdx(CrossAssetModel::AssetType::IR, indexCcyIdx)});
445
446 if (fxLinkedSimTime != Null<Real>()) {
447 info.simulationTimes.push_back(fxLinkedSimTime);
448 info.modelIndices.push_back(fxLinkedModelIndices);
449 }
450
451 info.amountCalculator = [this, indexCcyIdx, av, simTime, isFxLinked, fxLinkedForeignNominal,
452 fxLinkedSourceCcyIdx, fxLinkedTargetCcyIdx, fxLinkedFixedFxRate, isFxIndexed](
453 const Size n, const std::vector<std::vector<const RandomVariable*>>& states) {
454 RandomVariable effectiveRate = lgmVectorised_[indexCcyIdx].averagedOnRate(
455 av->overnightIndex(), av->fixingDates(), av->valueDates(), av->dt(), av->rateCutoff(), false,
456 av->spread(), av->gearing(), av->lookback(), Null<Real>(), Null<Real>(), false, false, simTime,
457 *states.at(0).at(0));
458 RandomVariable fxFixing(n, 1.0);
459 if (isFxLinked || isFxIndexed) {
460 if (fxLinkedFixedFxRate != Null<Real>()) {
461 fxFixing = RandomVariable(n, fxLinkedFixedFxRate);
462 } else {
463 RandomVariable fxSource(n, 1.0), fxTarget(n, 1.0);
464 Size fxIdx = 0;
465 if (fxLinkedSourceCcyIdx > 0)
466 fxSource = exp(*states.at(1).at(fxIdx++));
467 if (fxLinkedTargetCcyIdx > 0)
468 fxTarget = exp(*states.at(1).at(fxIdx));
469 fxFixing = fxSource / fxTarget;
470 }
471 }
472 return RandomVariable(n, (isFxLinked ? fxLinkedForeignNominal : av->nominal()) * av->accrualPeriod()) *
473 effectiveRate * fxFixing;
474 };
475
476 return info;
477 }
478
479 if (auto cfav = QuantLib::ext::dynamic_pointer_cast<CappedFlooredAverageONIndexedCoupon>(flow)) {
480 Real simTime = std::max(0.0, time(cfav->underlying()->valueDates().front()));
481 Size indexCcyIdx = model_->ccyIndex(cfav->underlying()->index()->currency());
482 info.simulationTimes.push_back(simTime);
483 info.modelIndices.push_back({model_->pIdx(CrossAssetModel::AssetType::IR, indexCcyIdx)});
484
485 if (fxLinkedSimTime != Null<Real>()) {
486 info.simulationTimes.push_back(fxLinkedSimTime);
487 info.modelIndices.push_back(fxLinkedModelIndices);
488 }
489
490 info.amountCalculator = [this, indexCcyIdx, cfav, simTime, isFxLinked, fxLinkedForeignNominal,
491 fxLinkedSourceCcyIdx, fxLinkedTargetCcyIdx, fxLinkedFixedFxRate, isFxIndexed](
492 const Size n, const std::vector<std::vector<const RandomVariable*>>& states) {
493 RandomVariable effectiveRate = lgmVectorised_[indexCcyIdx].averagedOnRate(
494 cfav->underlying()->overnightIndex(), cfav->underlying()->fixingDates(),
495 cfav->underlying()->valueDates(), cfav->underlying()->dt(), cfav->underlying()->rateCutoff(),
496 cfav->includeSpread(), cfav->underlying()->spread(), cfav->underlying()->gearing(),
497 cfav->underlying()->lookback(), cfav->cap(), cfav->floor(), cfav->localCapFloor(), cfav->nakedOption(),
498 simTime, *states.at(0).at(0));
499 RandomVariable fxFixing(n, 1.0);
500 if (isFxLinked || isFxIndexed) {
501 if (fxLinkedFixedFxRate != Null<Real>()) {
502 fxFixing = RandomVariable(n, fxLinkedFixedFxRate);
503 } else {
504 RandomVariable fxSource(n, 1.0), fxTarget(n, 1.0);
505 Size fxIdx = 0;
506 if (fxLinkedSourceCcyIdx > 0)
507 fxSource = exp(*states.at(1).at(fxIdx++));
508 if (fxLinkedTargetCcyIdx > 0)
509 fxTarget = exp(*states.at(1).at(fxIdx));
510 fxFixing = fxSource / fxTarget;
511 }
512 }
513 return RandomVariable(n, (isFxLinked ? fxLinkedForeignNominal : cfav->nominal()) * cfav->accrualPeriod()) *
514 effectiveRate * fxFixing;
515 };
516
517 return info;
518 }
519
520 if (auto bma = QuantLib::ext::dynamic_pointer_cast<AverageBMACoupon>(flow)) {
521 Real simTime = std::max(0.0, time(bma->fixingDates().front()));
522 Size indexCcyIdx = model_->ccyIndex(bma->index()->currency());
523 info.simulationTimes.push_back(simTime);
524 info.modelIndices.push_back({model_->pIdx(CrossAssetModel::AssetType::IR, indexCcyIdx)});
525
526 if (fxLinkedSimTime != Null<Real>()) {
527 info.simulationTimes.push_back(fxLinkedSimTime);
528 info.modelIndices.push_back(fxLinkedModelIndices);
529 }
530 info.amountCalculator = [this, indexCcyIdx, bma, simTime, isFxLinked, fxLinkedForeignNominal,
531 fxLinkedSourceCcyIdx, fxLinkedTargetCcyIdx, fxLinkedFixedFxRate, isFxIndexed](
532 const Size n, const std::vector<std::vector<const RandomVariable*>>& states) {
533 RandomVariable effectiveRate = lgmVectorised_[indexCcyIdx].averagedBmaRate(
534 QuantLib::ext::dynamic_pointer_cast<BMAIndex>(bma->index()), bma->fixingDates(), bma->accrualStartDate(),
535 bma->accrualEndDate(), false, bma->spread(), bma->gearing(), Null<Real>(), Null<Real>(), false, simTime,
536 *states.at(0).at(0));
537 RandomVariable fxFixing(n, 1.0);
538 if (isFxLinked || isFxIndexed) {
539 if (fxLinkedFixedFxRate != Null<Real>()) {
540 fxFixing = RandomVariable(n, fxLinkedFixedFxRate);
541 } else {
542 RandomVariable fxSource(n, 1.0), fxTarget(n, 1.0);
543 Size fxIdx = 0;
544 if (fxLinkedSourceCcyIdx > 0)
545 fxSource = exp(*states.at(1).at(fxIdx++));
546 if (fxLinkedTargetCcyIdx > 0)
547 fxTarget = exp(*states.at(1).at(fxIdx));
548 fxFixing = fxSource / fxTarget;
549 }
550 }
551 return RandomVariable(n, (isFxLinked ? fxLinkedForeignNominal : bma->nominal()) * bma->accrualPeriod()) *
552 effectiveRate * fxFixing;
553 };
554
555 return info;
556 }
557
558 if (auto cfbma = QuantLib::ext::dynamic_pointer_cast<CappedFlooredAverageBMACoupon>(flow)) {
559 Real simTime = std::max(0.0, time(cfbma->underlying()->fixingDates().front()));
560 Size indexCcyIdx = model_->ccyIndex(cfbma->underlying()->index()->currency());
561 info.simulationTimes.push_back(simTime);
562 info.modelIndices.push_back({model_->pIdx(CrossAssetModel::AssetType::IR, indexCcyIdx)});
563
564 if (fxLinkedSimTime != Null<Real>()) {
565 info.simulationTimes.push_back(fxLinkedSimTime);
566 info.modelIndices.push_back(fxLinkedModelIndices);
567 }
568 info.amountCalculator = [this, indexCcyIdx, cfbma, simTime, isFxLinked, fxLinkedForeignNominal,
569 fxLinkedSourceCcyIdx, fxLinkedTargetCcyIdx, fxLinkedFixedFxRate, isFxIndexed](
570 const Size n, const std::vector<std::vector<const RandomVariable*>>& states) {
571 RandomVariable effectiveRate = lgmVectorised_[indexCcyIdx].averagedBmaRate(
572 QuantLib::ext::dynamic_pointer_cast<BMAIndex>(cfbma->underlying()->index()), cfbma->underlying()->fixingDates(),
573 cfbma->underlying()->accrualStartDate(), cfbma->underlying()->accrualEndDate(), cfbma->includeSpread(),
574 cfbma->underlying()->spread(), cfbma->underlying()->gearing(), cfbma->cap(), cfbma->floor(),
575 cfbma->nakedOption(), simTime, *states.at(0).at(0));
576 RandomVariable fxFixing(n, 1.0);
577 if (isFxLinked || isFxIndexed) {
578 if (fxLinkedFixedFxRate != Null<Real>()) {
579 fxFixing = RandomVariable(n, fxLinkedFixedFxRate);
580 } else {
581 RandomVariable fxSource(n, 1.0), fxTarget(n, 1.0);
582 Size fxIdx = 0;
583 if (fxLinkedSourceCcyIdx > 0)
584 fxSource = exp(*states.at(1).at(fxIdx++));
585 if (fxLinkedTargetCcyIdx > 0)
586 fxTarget = exp(*states.at(1).at(fxIdx));
587 fxFixing = fxSource / fxTarget;
588 }
589 }
590 return RandomVariable(n, (isFxLinked ? fxLinkedForeignNominal : cfbma->underlying()->nominal()) *
591 cfbma->underlying()->accrualPeriod()) *
592 effectiveRate * fxFixing;
593 };
594
595 return info;
596 }
597
598 if (auto sub = QuantLib::ext::dynamic_pointer_cast<SubPeriodsCoupon1>(flow)) {
599 Real simTime = std::max(0.0, time(sub->fixingDates().front()));
600 Size indexCcyIdx = model_->ccyIndex(sub->index()->currency());
601 info.simulationTimes.push_back(simTime);
602 info.modelIndices.push_back({model_->pIdx(CrossAssetModel::AssetType::IR, indexCcyIdx)});
603
604 if (fxLinkedSimTime != Null<Real>()) {
605 info.simulationTimes.push_back(fxLinkedSimTime);
606 info.modelIndices.push_back(fxLinkedModelIndices);
607 }
608
609 info.amountCalculator = [this, indexCcyIdx, sub, simTime, isFxLinked, fxLinkedForeignNominal,
610 fxLinkedSourceCcyIdx, fxLinkedTargetCcyIdx, fxLinkedFixedFxRate, isFxIndexed](
611 const Size n, const std::vector<std::vector<const RandomVariable*>>& states) {
612 RandomVariable fixing = lgmVectorised_[indexCcyIdx].subPeriodsRate(sub->index(), sub->fixingDates(),
613 simTime, *states.at(0).at(0));
614 RandomVariable fxFixing(n, 1.0);
615 if (isFxLinked || isFxIndexed) {
616 if (fxLinkedFixedFxRate != Null<Real>()) {
617 fxFixing = RandomVariable(n, fxLinkedFixedFxRate);
618 } else {
619 RandomVariable fxSource(n, 1.0), fxTarget(n, 1.0);
620 Size fxIdx = 0;
621 if (fxLinkedSourceCcyIdx > 0)
622 fxSource = exp(*states.at(1).at(fxIdx++));
623 if (fxLinkedTargetCcyIdx > 0)
624 fxTarget = exp(*states.at(1).at(fxIdx));
625 fxFixing = fxSource / fxTarget;
626 }
627 }
628 RandomVariable effectiveRate = RandomVariable(n, sub->gearing()) * fixing + RandomVariable(n, sub->spread());
629 return RandomVariable(n, (isFxLinked ? fxLinkedForeignNominal : sub->nominal()) * sub->accrualPeriod()) *
630 effectiveRate * fxFixing;
631 };
632
633 return info;
634 }
635
636 QL_FAIL("McMultiLegBaseEngine::createCashflowInfo(): unhandled coupon leg " << legNo << " cashflow " << cfNo);
637} // createCashflowInfo()
QuantLib::Date fixingDate(const QuantLib::Date &d, const QuantLib::Period obsLag, const QuantLib::Frequency freq, bool interpolated)
Definition: inflation.cpp:183
CompiledFormula exp(CompiledFormula x)
CompiledFormula max(CompiledFormula x, const CompiledFormula &y)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ timeIndex()

Size timeIndex ( const Time  t,
const std::set< Real > &  simulationTimes 
) const
private

Definition at line 639 of file mcmultilegbaseengine.cpp.

639 {
640 auto it = times.find(t);
641 QL_REQUIRE(it != times.end(), "McMultiLegBaseEngine::cashflowPathValue(): time ("
642 << t
643 << ") not found in simulation times. This is an internal error. Contact dev.");
644 return std::distance(times.begin(), it);
645}
+ Here is the caller graph for this function:

◆ cashflowPathValue()

RandomVariable cashflowPathValue ( const CashflowInfo cf,
const std::vector< std::vector< RandomVariable > > &  pathValues,
const std::set< Real > &  simulationTimes 
) const
private

Definition at line 647 of file mcmultilegbaseengine.cpp.

649 {
650
651 Size n = pathValues[0][0].size();
652 auto simTimesPayIdx = timeIndex(cf.payTime, simulationTimes);
653
654 std::vector<RandomVariable> initialValues(model_->stateProcess()->initialValues().size());
655 for (Size i = 0; i < model_->stateProcess()->initialValues().size(); ++i)
656 initialValues[i] = RandomVariable(n, model_->stateProcess()->initialValues()[i]);
657
658 std::vector<std::vector<const RandomVariable*>> states(cf.simulationTimes.size());
659 for (Size i = 0; i < cf.simulationTimes.size(); ++i) {
660 std::vector<const RandomVariable*> tmp(cf.modelIndices[i].size());
661 if (cf.simulationTimes[i] == 0.0) {
662 for (Size j = 0; j < cf.modelIndices[i].size(); ++j) {
663 tmp[j] = &initialValues[cf.modelIndices[i][j]];
664 }
665 } else {
666 auto simTimesIdx = timeIndex(cf.simulationTimes[i], simulationTimes);
667 for (Size j = 0; j < cf.modelIndices[i].size(); ++j) {
668 tmp[j] = &pathValues[simTimesIdx][cf.modelIndices[i][j]];
669 }
670 }
671 states[i] = tmp;
672 }
673
674 auto amount = cf.amountCalculator(n, states) /
675 lgmVectorised_[0].numeraire(
676 cf.payTime, pathValues[simTimesPayIdx][model_->pIdx(CrossAssetModel::AssetType::IR, 0)],
677 discountCurves_[0]);
678
679 if (cf.payCcyIndex > 0) {
680 amount *= exp(pathValues[simTimesPayIdx][model_->pIdx(CrossAssetModel::AssetType::FX, cf.payCcyIndex - 1)]);
681 }
682
683 return amount * RandomVariable(n, cf.payer ? -1.0 : 1.0);
684}
Size timeIndex(const Time t, const std::set< Real > &simulationTimes) const
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Member Data Documentation

◆ leg_

std::vector<Leg> leg_
mutableprotected

Definition at line 93 of file mcmultilegbaseengine.hpp.

◆ currency_

std::vector<Currency> currency_
mutableprotected

Definition at line 94 of file mcmultilegbaseengine.hpp.

◆ payer_

std::vector<bool> payer_
mutableprotected

Definition at line 95 of file mcmultilegbaseengine.hpp.

◆ exercise_

QuantLib::ext::shared_ptr<Exercise> exercise_
mutableprotected

Definition at line 96 of file mcmultilegbaseengine.hpp.

◆ optionSettlement_

Settlement::Type optionSettlement_ = Settlement::Physical
mutableprotected

Definition at line 97 of file mcmultilegbaseengine.hpp.

◆ includeSettlementDateFlows_

bool includeSettlementDateFlows_ = false
mutableprotected

Definition at line 98 of file mcmultilegbaseengine.hpp.

◆ model_

Handle<CrossAssetModel> model_
protected

Definition at line 101 of file mcmultilegbaseengine.hpp.

◆ calibrationPathGenerator_

SequenceType calibrationPathGenerator_
protected

Definition at line 102 of file mcmultilegbaseengine.hpp.

◆ pricingPathGenerator_

SequenceType pricingPathGenerator_
protected

Definition at line 102 of file mcmultilegbaseengine.hpp.

◆ calibrationSamples_

Size calibrationSamples_
protected

Definition at line 103 of file mcmultilegbaseengine.hpp.

◆ pricingSamples_

Size pricingSamples_
protected

Definition at line 103 of file mcmultilegbaseengine.hpp.

◆ calibrationSeed_

Size calibrationSeed_
protected

Definition at line 103 of file mcmultilegbaseengine.hpp.

◆ pricingSeed_

Size pricingSeed_
protected

Definition at line 103 of file mcmultilegbaseengine.hpp.

◆ polynomOrder_

Size polynomOrder_
protected

Definition at line 104 of file mcmultilegbaseengine.hpp.

◆ polynomType_

LsmBasisSystem::PolynomialType polynomType_
protected

Definition at line 105 of file mcmultilegbaseengine.hpp.

◆ ordering_

SobolBrownianGenerator::Ordering ordering_
protected

Definition at line 106 of file mcmultilegbaseengine.hpp.

◆ directionIntegers_

SobolRsg::DirectionIntegers directionIntegers_
protected

Definition at line 107 of file mcmultilegbaseengine.hpp.

◆ discountCurves_

std::vector<Handle<YieldTermStructure> > discountCurves_
protected

Definition at line 108 of file mcmultilegbaseengine.hpp.

◆ simulationDates_

std::vector<Date> simulationDates_
protected

Definition at line 109 of file mcmultilegbaseengine.hpp.

◆ externalModelIndices_

std::vector<Size> externalModelIndices_
protected

Definition at line 110 of file mcmultilegbaseengine.hpp.

◆ minimalObsDate_

bool minimalObsDate_
protected

Definition at line 111 of file mcmultilegbaseengine.hpp.

◆ regressorModel_

RegressorModel regressorModel_
protected

Definition at line 112 of file mcmultilegbaseengine.hpp.

◆ regressionVarianceCutoff_

Real regressionVarianceCutoff_
protected

Definition at line 113 of file mcmultilegbaseengine.hpp.

◆ amcCalculator_

QuantLib::ext::shared_ptr<AmcCalculator> amcCalculator_
mutableprotected

Definition at line 116 of file mcmultilegbaseengine.hpp.

◆ resultUnderlyingNpv_

Real resultUnderlyingNpv_
mutableprotected

Definition at line 119 of file mcmultilegbaseengine.hpp.

◆ resultValue_

Real resultValue_
protected

Definition at line 119 of file mcmultilegbaseengine.hpp.

◆ tinyTime

constexpr Real tinyTime = 1E-10
staticconstexprprivate

Definition at line 122 of file mcmultilegbaseengine.hpp.

◆ today_

Date today_
mutableprivate

Definition at line 212 of file mcmultilegbaseengine.hpp.

◆ lgmVectorised_

std::vector<LgmVectorised> lgmVectorised_
mutableprivate

Definition at line 215 of file mcmultilegbaseengine.hpp.