Skip to content

Commit 71f94b5

Browse files
author
jenkins
committed
git subrepo pull (merge) ore
subrepo: subdir: "ore" merged: "4bc8501c13" upstream: origin: "git@gitlab.acadiasoft.net:qs/ore.git" branch: "master" commit: "dbac149cfa" git-subrepo: version: "0.4.6" origin: "https://github.com/ingydotnet/git-subrepo" commit: "73a0129"
2 parents ebbc7ed + dbac149 commit 71f94b5

13 files changed

Lines changed: 92 additions & 45 deletions

Docs/ScriptedTrade/docs/language.tex

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,8 @@
466466
\item here $P_{ccy}$ is the discount factor in currency ccy, $FX$ is the FX spot from ccy to base and $N$ is the model
467467
numeraire
468468
\item $d\leq p$ must hold
469-
\item if $p$ lies on or before the evaluation date, the result is zero; $X$ is not evaluated in this case
469+
\item if $p$ lies on or before the evaluation date, the result is zero; $X$ is not evaluated in this case.
470+
Note that $X$ is evaluated in the LOGPAY function if past cashflows are included, see \ref{function_logpay}.
470471
\item avoids reading non-relevant past fixings from the index history
471472
\item if $d$ lies before (but $p$ after) the evaluation date, it is set to the evaluation date, i.e. the result is
472473
computed as of the evaluation date
@@ -514,6 +515,9 @@
514515
under leg number $2$ and flow Type ``Interest''. The same holds for Payoff3, but if any amounts were booked using the
515516
slot parameter $3$ previously they will be overwritten with the current amount.
516517

518+
Note: If IncludePastCashflows in the pricing engine config is set to true then even if $p$ lies on or before the
519+
evaluation date, a cashflow entry will be generated.
520+
517521
% ====================================================
518522
\stsubsection{Function {\tt NPV, NPVMEM}}\label{function_npv}
519523
% ====================================================

Docs/ScriptedTrade/docs/models.tex

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ \subsection{Pricing Engine Configuration}\label{pricingengine_config}
4949
<Parameter name="TimeStepsPerYear">24</Parameter>
5050
<Parameter name="Interactive">false</Parameter>
5151
<Parameter name="BootstrapTolerance">0.1</Parameter>
52+
<Parameter name="IncludePastCashflows">true</Parameter>
5253
<!-- product specific parameters -->
5354
<Parameter name="RegressionOrder_SingleAssetOption(EQ)">6</Parameter>
5455
<Parameter name="RegressionOrder_SingleAssetOption(FX)">6</Parameter>
@@ -136,6 +137,8 @@ \subsection{Pricing Engine Configuration}\label{pricingengine_config}
136137
only. The moneyness is defined as a ``standardised moneyness'' $\ln(K/F) / \sigma\sqrt{t}$ with $K$ strike, $F$ ATMF
137138
forward, $\sigma$ ATMF market vol, $t$ option time to expiry
138139
\item BootstrapTolerance: tolerance for calibration bootstrap, only applies to model = GaussianCam
140+
\item IncludePastCashflows: if true, LOGPAY() will generate cashflow information for pay dates on or before the
141+
reference date. Optional, defaults to false.
139142
\item Interactive: If true an interactive session is started on script execution for debugging purposes; should be false
140143
except for debugging purposes
141144
\item UseAD: If true and RunType in the global pricing engine parameters is SensitivityDelta, a first order pnl

OREAnalytics/orea/app/reportwriter.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,11 @@ void ReportWriter::writeCashflow(ore::data::Report& report, const std::string& b
580580
if (cf.effectiveCapVolatility != Null<Real>())
581581
capVolatility = cf.effectiveCapVolatility;
582582

583+
// to be consistent with the leg-based cf report we should do this:
584+
// if (!includePastCashflows && cf.payDate <= asof)
585+
// continue;
586+
// however, this changes a lot of results, so we output all cfs for the time being
587+
583588
report.next()
584589
.add(trade->id())
585590
.add(trade->tradeType())

OREData/ored/portfolio/builders/scriptedtrade.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,14 +320,14 @@ ScriptedTradeEngineBuilder::engine(const std::string& id, const ScriptedTrade& s
320320
engine = boost::make_shared<ScriptedInstrumentPricingEngine>(
321321
script.npv(), script.results(), model_, ast_, context, script.code(), interactive_, amcCam_ != nullptr,
322322
std::set<std::string>(script.stickyCloseOutStates().begin(), script.stickyCloseOutStates().end()),
323-
generateAdditionalResults);
323+
generateAdditionalResults, includePastCashflows_);
324324
} else if (modelCG_) {
325325
auto rt = globalParameters_.find("RunType");
326326
bool useCachedSensis = useAd_ && (rt != globalParameters_.end() && rt->second == "SensitivityDelta");
327327
bool useExternalDev = useExternalComputeDevice_ && !generateAdditionalResults && !useCachedSensis;
328328
engine = boost::make_shared<ScriptedInstrumentPricingEngineCG>(
329329
script.npv(), script.results(), modelCG_, ast_, context, mcParams_, script.code(), interactive_,
330-
generateAdditionalResults, useCachedSensis, useExternalDev);
330+
generateAdditionalResults, includePastCashflows_, useCachedSensis, useExternalDev);
331331
if (useExternalDev) {
332332
ComputeEnvironment::instance().selectContext(externalComputeDevice_);
333333
}
@@ -485,6 +485,7 @@ void ScriptedTradeEngineBuilder::populateModelParameters() {
485485
useExternalComputeDevice_ =
486486
parseBool(engineParameter("UseExternalComputeDevice", {resolvedProductTag_}, false, "false"));
487487
externalComputeDevice_ = engineParameter("ExternalComputeDevice", {}, false, "");
488+
includePastCashflows_ = parseBool(engineParameter("IncludePastCashflows", {resolvedProductTag_}, false, "false"));
488489

489490
// usage of ad or an external device implies usage of cg
490491
if (useAd_ || useExternalComputeDevice_)

OREData/ored/portfolio/builders/scriptedtrade.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ class ScriptedTradeEngineBuilder : public EngineBuilder {
161161
bool useAd_;
162162
bool useExternalComputeDevice_;
163163
std::string externalComputeDevice_;
164+
bool includePastCashflows_;
164165
};
165166

166167
} // namespace data

OREData/ored/scripting/computationgraphbuilder.cpp

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,12 @@ class ASTRunner : public AcyclicVisitor,
119119
public Visitor<LoopNode> {
120120
public:
121121
ASTRunner(ComputationGraph& g, const std::vector<std::string>& opLabels, const boost::shared_ptr<ModelCG> model,
122-
const bool generatePayLog, const std::string& script, bool& interactive, Context& context,
123-
ASTNode*& lastVisitedNode, std::set<std::size_t>& keepNodes,
122+
const bool generatePayLog, const bool includePastCashflows, const std::string& script, bool& interactive,
123+
Context& context, ASTNode*& lastVisitedNode, std::set<std::size_t>& keepNodes,
124124
std::vector<ComputationGraphBuilder::PayLogEntry>& payLogEntries)
125125
: g_(g), opLabels_(opLabels), model_(model), size_(model ? model->size() : 1), generatePayLog_(generatePayLog),
126-
script_(script), interactive_(interactive), keepNodes_(keepNodes), payLogEntries_(payLogEntries),
127-
context_(context), lastVisitedNode_(lastVisitedNode) {
126+
includePastCashflows_(includePastCashflows), script_(script), interactive_(interactive),
127+
keepNodes_(keepNodes), payLogEntries_(payLogEntries), context_(context), lastVisitedNode_(lastVisitedNode) {
128128
filter.emplace(size_, true);
129129
value.push(RandomVariable());
130130
filter_node.push(ComputationGraph::nan);
@@ -1029,7 +1029,7 @@ class ASTRunner : public AcyclicVisitor,
10291029
QL_REQUIRE(model_, "model is null");
10301030
// handle case of past payments: do not evaluate the other parameters, since not needed (e.g. past fixings)
10311031
Date pay = boost::get<EventVec>(paydate).value;
1032-
if (pay <= model_->referenceDate()) {
1032+
if (pay <= model_->referenceDate() && (!log || !includePastCashflows_)) {
10331033
value.push(RandomVariable(size_, 0.0));
10341034
std::size_t node = cg_const(g_, 0.0);
10351035
value_node.push(node);
@@ -1053,7 +1053,9 @@ class ASTRunner : public AcyclicVisitor,
10531053
QL_REQUIRE(obs <= pay, "observation date (" << obs << ") <= payment date (" << pay << ") required");
10541054
RandomVariable result; // uninitialised, since model dependent
10551055
value.push(result);
1056-
std::size_t node = model_->pay(amount_node, obs, pay, pccy);
1056+
std::size_t node =
1057+
pay <= model_->referenceDate() ? cg_const(g_, 0.0) : model_->pay(amount_node, obs, pay, pccy);
1058+
std::size_t cfnode = pay <= model_->referenceDate() ? amount_node : node;
10571059
value_node.push(node);
10581060
TRACE("pay( " << amount << " , " << obsdate << " , " << paydate << " , " << paycurr << " ) (#" << node
10591061
<< ")",
@@ -1092,10 +1094,10 @@ class ASTRunner : public AcyclicVisitor,
10921094
}
10931095
// add nodes necessary to write paylog to keepNodes set
10941096
std::size_t filterNode = filter_node.top() == ComputationGraph::nan ? cg_const(g_, 1.0) : filter_node.top();
1095-
keepNodes_.insert(node);
1097+
keepNodes_.insert(cfnode);
10961098
keepNodes_.insert(filterNode);
10971099
// add paylog entry data
1098-
payLogEntries_.push_back({node, filterNode, obs, pay, pccy, (Size)legno, cftype, (Size)slot});
1100+
payLogEntries_.push_back({cfnode, filterNode, obs, pay, pccy, (Size)legno, cftype, (Size)slot});
10991101
}
11001102
}
11011103
}
@@ -1463,6 +1465,7 @@ class ASTRunner : public AcyclicVisitor,
14631465
const boost::shared_ptr<ModelCG> model_;
14641466
const Size size_;
14651467
const bool generatePayLog_;
1468+
const bool includePastCashflows_;
14661469
const std::string script_;
14671470
bool& interactive_;
14681471
std::set<std::size_t>& keepNodes_;
@@ -1479,14 +1482,15 @@ class ASTRunner : public AcyclicVisitor,
14791482

14801483
} // namespace
14811484

1482-
void ComputationGraphBuilder::run(const bool generatePayLog, const std::string& script, bool interactive) {
1485+
void ComputationGraphBuilder::run(const bool generatePayLog, const bool includePastCashflows, const std::string& script,
1486+
bool interactive) {
14831487

14841488
keepNodes_.clear();
14851489
payLogEntries_.clear();
14861490

14871491
ASTNode* loc;
1488-
ASTRunner runner(g_, opLabels_, model_, generatePayLog, script, interactive, *context_, loc, keepNodes_,
1489-
payLogEntries_);
1492+
ASTRunner runner(g_, opLabels_, model_, generatePayLog, generatePayLog && includePastCashflows, script, interactive,
1493+
*context_, loc, keepNodes_, payLogEntries_);
14901494

14911495
randomvariable_output_pattern pattern;
14921496
if (model_ == nullptr || model_->type() == ModelCG::Type::MC) {

OREData/ored/scripting/computationgraphbuilder.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ class ComputationGraphBuilder {
4949
ComputationGraphBuilder(ComputationGraph& g, const std::vector<std::string>& opLabels, const ASTNodePtr root,
5050
const boost::shared_ptr<Context> context, const boost::shared_ptr<ModelCG> model = nullptr)
5151
: g_(g), opLabels_(opLabels), root_(root), context_(context), model_(model) {}
52-
void run(const bool generatePayLog, const std::string& script = "", bool interactive = false);
52+
void run(const bool generatePayLog, const bool includePastCashflows = false, const std::string& script = "",
53+
bool interactive = false);
5354
const std::set<std::size_t>& keepNodes() const { return keepNodes_; }
5455
const std::vector<PayLogEntry>& payLogEntries() const { return payLogEntries_; }
5556

OREData/ored/scripting/engines/scriptedinstrumentpricingengine.cpp

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,12 @@ void ScriptedInstrumentPricingEngine::calculate() const {
107107
// set up script engine and run it
108108

109109
ScriptEngine engine(ast_, workingContext, model_);
110-
auto paylog = boost::make_shared<PayLog>();
111-
engine.run(script_, interactive_, paylog);
110+
111+
boost::shared_ptr<PayLog> paylog;
112+
if (generateAdditionalResults_)
113+
paylog = boost::make_shared<PayLog>();
114+
115+
engine.run(script_, interactive_, paylog, includePastCashflows_);
112116

113117
// extract npv result and set it
114118

@@ -191,8 +195,12 @@ void ScriptedInstrumentPricingEngine::calculate() const {
191195
for (Size i = 0; i < paylog->size(); ++i) {
192196
// cashflow is written as expectation of deflated base ccy amount at T0, converted to flow ccy
193197
// with the T0 FX Spot and compounded back to the pay date on T0 curves
194-
Real fx = model_->fxSpotT0(paylog->currencies().at(i), model_->baseCcy());
195-
Real discount = model_->discount(referenceDate, paylog->dates().at(i), paylog->currencies().at(i)).at(0);
198+
Real fx = 1.0;
199+
Real discount = 1.0;
200+
if (paylog->dates().at(i) > model_->referenceDate()) {
201+
fx = model_->fxSpotT0(paylog->currencies().at(i), model_->baseCcy());
202+
discount = model_->discount(referenceDate, paylog->dates().at(i), paylog->currencies().at(i)).at(0);
203+
}
196204
cashFlowResults[i].amount = model_->extractT0Result(paylog->amounts().at(i)) / fx / discount;
197205
cashFlowResults[i].payDate = paylog->dates().at(i);
198206
cashFlowResults[i].currency = paylog->currencies().at(i);
@@ -202,10 +210,12 @@ void ScriptedInstrumentPricingEngine::calculate() const {
202210
<< cashFlowResults[i].currency << cashFlowResults[i].amount << " "
203211
<< cashFlowResults[i].currency << "-" << model_->baseCcy() << " " << fx << " discount("
204212
<< cashFlowResults[i].currency << ") " << discount);
205-
addMcErrorEstimate("cashflow_" + std::to_string(paylog->legNos().at(i)) + "_" +
206-
std::to_string(++cashflowNumber[paylog->legNos().at(i)]) + "_MCErrEst",
207-
paylog->amounts().at(i) /
208-
RandomVariable(paylog->amounts().at(i).size(), (fx * discount)));
213+
if (paylog->dates().at(i) > model_->referenceDate()) {
214+
addMcErrorEstimate("cashflow_" + std::to_string(paylog->legNos().at(i)) + "_" +
215+
std::to_string(++cashflowNumber[paylog->legNos().at(i)]) + "_MCErrEst",
216+
paylog->amounts().at(i) /
217+
RandomVariable(paylog->amounts().at(i).size(), (fx * discount)));
218+
}
209219
}
210220
results_.additionalResults["cashFlowResults"] = cashFlowResults;
211221

OREData/ored/scripting/engines/scriptedinstrumentpricingengine.hpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,18 @@ namespace data {
3434

3535
class ScriptedInstrumentPricingEngine : public QuantExt::ScriptedInstrument::engine {
3636
public:
37-
ScriptedInstrumentPricingEngine(
38-
const std::string& npv, const std::vector<std::pair<std::string, std::string>>& additionalResults,
39-
const boost::shared_ptr<Model>& model, const ASTNodePtr ast, const boost::shared_ptr<Context>& context,
40-
const std::string& script = "", const bool interactive = false,
41-
const bool amcEnabled = false,
42-
const std::set<std::string>& amcStickyCloseOutStates = {}, const bool generateAdditionalResults = false)
37+
ScriptedInstrumentPricingEngine(const std::string& npv,
38+
const std::vector<std::pair<std::string, std::string>>& additionalResults,
39+
const boost::shared_ptr<Model>& model, const ASTNodePtr ast,
40+
const boost::shared_ptr<Context>& context, const std::string& script = "",
41+
const bool interactive = false, const bool amcEnabled = false,
42+
const std::set<std::string>& amcStickyCloseOutStates = {},
43+
const bool generateAdditionalResults = false,
44+
const bool includePastCashflows = false)
4345
: npv_(npv), additionalResults_(additionalResults), model_(model), ast_(ast), context_(context),
4446
script_(script), interactive_(interactive), amcEnabled_(amcEnabled),
45-
amcStickyCloseOutStates_(amcStickyCloseOutStates), generateAdditionalResults_(generateAdditionalResults) {
47+
amcStickyCloseOutStates_(amcStickyCloseOutStates), generateAdditionalResults_(generateAdditionalResults),
48+
includePastCashflows_(includePastCashflows) {
4649
registerWith(model_);
4750
}
4851

@@ -65,6 +68,7 @@ class ScriptedInstrumentPricingEngine : public QuantExt::ScriptedInstrument::eng
6568
const bool amcEnabled_;
6669
const std::set<std::string> amcStickyCloseOutStates_;
6770
const bool generateAdditionalResults_;
71+
const bool includePastCashflows_;
6872
};
6973

7074
} // namespace data

OREData/ored/scripting/engines/scriptedinstrumentpricingenginecg.cpp

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,12 @@ ScriptedInstrumentPricingEngineCG::ScriptedInstrumentPricingEngineCG(
6868
const std::string& npv, const std::vector<std::pair<std::string, std::string>>& additionalResults,
6969
const boost::shared_ptr<ModelCG>& model, const ASTNodePtr ast, const boost::shared_ptr<Context>& context,
7070
const Model::McParams& mcParams, const std::string& script, const bool interactive,
71-
const bool generateAdditionalResults, const bool useCachedSensis, const bool useExternalComputeFramework)
71+
const bool generateAdditionalResults, const bool includePastCashflows, const bool useCachedSensis,
72+
const bool useExternalComputeFramework)
7273
: npv_(npv), additionalResults_(additionalResults), model_(model), ast_(ast), context_(context),
7374
mcParams_(mcParams), script_(script), interactive_(interactive),
74-
generateAdditionalResults_(generateAdditionalResults), useCachedSensis_(useCachedSensis),
75-
useExternalComputeFramework_(useExternalComputeFramework) {
75+
generateAdditionalResults_(generateAdditionalResults), includePastCashflows_(includePastCashflows),
76+
useCachedSensis_(useCachedSensis), useExternalComputeFramework_(useExternalComputeFramework) {
7677

7778
// register with model
7879

@@ -138,7 +139,7 @@ void ScriptedInstrumentPricingEngineCG::buildComputationGraph() const {
138139
// build graph
139140

140141
ComputationGraphBuilder cgBuilder(*g, getRandomVariableOpLabels(), ast_, workingContext_, model_);
141-
cgBuilder.run(generateAdditionalResults_, script_, interactive_);
142+
cgBuilder.run(generateAdditionalResults_, includePastCashflows_, script_, interactive_);
142143
cgVersion_ = model_->cgVersion();
143144
DLOG("Built computation graph version " << cgVersion_ << " size is " << g->size());
144145
TLOGGERSTREAM(ssaForm(*g, getRandomVariableOpLabels()));
@@ -384,8 +385,12 @@ void ScriptedInstrumentPricingEngineCG::calculate() const {
384385
for (Size i = 0; i < paylog->size(); ++i) {
385386
// cashflow is written as expectation of deflated base ccy amount at T0, converted to flow ccy
386387
// with the T0 FX Spot and compounded back to the pay date on T0 curves
387-
Real fx = model_->getDirectFxSpotT0(paylog->currencies().at(i), model_->baseCcy());
388-
Real discount = model_->getDirectDiscountT0(paylog->dates().at(i), paylog->currencies().at(i));
388+
Real fx = 1.0;
389+
Real discount = 1.0;
390+
if (paylog->dates().at(i) > model_->referenceDate()) {
391+
fx = model_->getDirectFxSpotT0(paylog->currencies().at(i), model_->baseCcy());
392+
discount = model_->getDirectDiscountT0(paylog->dates().at(i), paylog->currencies().at(i));
393+
}
389394
cashFlowResults[i].amount = model_->extractT0Result(paylog->amounts().at(i)) / fx / discount;
390395
cashFlowResults[i].payDate = paylog->dates().at(i);
391396
cashFlowResults[i].currency = paylog->currencies().at(i);

0 commit comments

Comments
 (0)