From b5dfe1418a8d72636960f45cfe7be7399097609f Mon Sep 17 00:00:00 2001 From: Danilo <115167567+calderinisystemorph@users.noreply.github.com> Date: Tue, 24 Jan 2023 11:03:51 +0100 Subject: [PATCH 1/9] Yield curve import triggers calculations (#161) * equality comparer and queries updated * data model, importers and importstorage updated * scenario avalinade for datanodes * scenario test improved * firsts comments resolved * all comments resolved * scenario test works * estimate type fix * more scenario tests * revert calculation engine notebook * improved union of target/default variables * full union + save zeros for scenarios * scenario tests are green * fix equality comparer * best estimate all green * ifrs variables scenario new benchmark * openings importer cleanups * first implementation of yield curve importer * fix report benchmark scenario * cleanup * preparation before pull develop * scenario for GOC removed * YieldCurves new format added. * more readable relaxed query util method * warning triggering multiple calculation * cleanup the importer * comments on warning implemented * test clean up * YieldCurve Importer now trigger calculation * fix report variables benchmark * test coverage improved * some comments resolved * validation added * new args datamodel, bm with standard digits * Useless Zeros not saved, code cleanup * comments on EqualityComparer resolved, BM updated * Test Improved * comments resolved * refactoring of Main Parser, all tests green * typo fixing * importer and test for yield curve improved * code improvment and cleanup * code cleanup * Name property in YieldCurve Implemented * bug fix, all test green * commit to DB only with succeeded import * yield curve updated * new yield curves files created * comment resolved * some comments resolved * renames and markdown and use of .Any * code improvments * test debug * cleanup merge errors * code cleanup * typo fix, test and importer improved * best * implement feedback * feedback v2: consistency and readability up * add toggle * commit partition renaming * improved toggle + better relaxed queries * make args and storage initializer more robust * pv cleanups Co-authored-by: Andrea Muolo Co-authored-by: Davide Colleoni --- .../PresentValue - Episode 2.ipynb | 12 +- .../PresentValue - Episode 3.ipynb | 35 +- .../Files/Parameters/YieldCurve_2020_1.csv | 2 +- .../Files/Parameters/YieldCurve_2020_12.csv | 2 +- .../Test/ScenarioDataImportTest.ipynb | 133 +++-- .../Test/ScenarioYieldCurveImportTest.ipynb | 513 ++++++++++++++++++ ifrs17-template/Test/Tests.ipynb | 13 +- ifrs17/Constants/Consts.ipynb | 9 + ifrs17/Constants/Validations.ipynb | 5 +- ifrs17/Import/ImportStorage.ipynb | 2 +- ifrs17/Import/Importers.ipynb | 226 +++++--- ifrs17/Utils/Queries.ipynb | 20 +- 12 files changed, 806 insertions(+), 166 deletions(-) create mode 100644 ifrs17-template/Test/ScenarioYieldCurveImportTest.ipynb diff --git a/PresentValueSeries/PresentValue - Episode 2.ipynb b/PresentValueSeries/PresentValue - Episode 2.ipynb index 37e9ce42..804597e7 100644 --- a/PresentValueSeries/PresentValue - Episode 2.ipynb +++ b/PresentValueSeries/PresentValue - Episode 2.ipynb @@ -168,10 +168,7 @@ { "cell_type": "code", "source": [ - "await Import.FromFile(\"YieldCurve.xlsx\")", - "\n .WithType()", - "\n .WithTarget(DataSource)", - "\n .ExecuteAsync()" + "await Import.FromFile(\"YieldCurve.xlsx\").WithType().WithTarget(DataSource).ExecuteAsync()" ], "metadata": {}, "execution_count": 0, @@ -180,10 +177,7 @@ { "cell_type": "code", "source": [ - "await Import.FromFile(\"Cashflows.xlsx\")", - "\n .WithFormat(\"Cashflow\")", - "\n .WithTarget(DataSource)", - "\n .ExecuteAsync()" + "await Import.FromFile(\"Cashflows.xlsx\").WithFormat(\"Cashflow\").WithTarget(DataSource).ExecuteAsync()" ], "metadata": {}, "execution_count": 0, @@ -507,4 +501,4 @@ "outputs": [] } ] -} +} \ No newline at end of file diff --git a/PresentValueSeries/PresentValue - Episode 3.ipynb b/PresentValueSeries/PresentValue - Episode 3.ipynb index 16605c69..3463cc41 100644 --- a/PresentValueSeries/PresentValue - Episode 3.ipynb +++ b/PresentValueSeries/PresentValue - Episode 3.ipynb @@ -168,10 +168,7 @@ { "cell_type": "code", "source": [ - "await Import.FromFile(\"DataNodes_CH.xlsx\")", - "\n .WithFormat(\"DataNode\")", - "\n .WithTarget(DataSource)", - "\n .ExecuteAsync()" + "await Import.FromFile(\"DataNodes_CH.xlsx\").WithFormat(\"DataNode\").WithTarget(DataSource).ExecuteAsync()" ], "metadata": {}, "execution_count": 0, @@ -180,10 +177,7 @@ { "cell_type": "code", "source": [ - "await Import.FromFile(\"DataNodes_DE.xlsx\")", - "\n .WithFormat(\"DataNode\")", - "\n .WithTarget(DataSource)", - "\n .ExecuteAsync()" + "await Import.FromFile(\"DataNodes_DE.xlsx\").WithFormat(\"DataNode\").WithTarget(DataSource).ExecuteAsync()" ], "metadata": {}, "execution_count": 0, @@ -192,10 +186,7 @@ { "cell_type": "code", "source": [ - "await Import.FromFile(\"YieldCurve.xlsx\")", - "\n .WithType()", - "\n .WithTarget(DataSource)", - "\n .ExecuteAsync()" + "await Import.FromFile(\"YieldCurve.xlsx\").WithType().WithTarget(DataSource).ExecuteAsync()" ], "metadata": {}, "execution_count": 0, @@ -204,10 +195,7 @@ { "cell_type": "code", "source": [ - "await Import.FromFile(\"Cashflows.xlsx\")", - "\n .WithFormat(\"Cashflow\")", - "\n .WithTarget(DataSource)", - "\n .ExecuteAsync()" + "await Import.FromFile(\"Cashflows.xlsx\").WithFormat(\"Cashflow\").WithTarget(DataSource).ExecuteAsync()" ], "metadata": {}, "execution_count": 0, @@ -216,10 +204,7 @@ { "cell_type": "code", "source": [ - "await Import.FromFile(\"CF_CH_2021_12.xlsx\")", - "\n .WithFormat(\"Cashflow\")", - "\n .WithTarget(DataSource)", - "\n .ExecuteAsync()" + "await Import.FromFile(\"CF_CH_2021_12.xlsx\").WithFormat(\"Cashflow\").WithTarget(DataSource).ExecuteAsync()" ], "metadata": {}, "execution_count": 0, @@ -228,10 +213,7 @@ { "cell_type": "code", "source": [ - "await Import.FromFile(\"CF_DE_2021_12.xlsx\")", - "\n .WithFormat(\"Cashflow\")", - "\n .WithTarget(DataSource)", - "\n .ExecuteAsync()" + "await Import.FromFile(\"CF_DE_2021_12.xlsx\").WithFormat(\"Cashflow\").WithTarget(DataSource).ExecuteAsync()" ], "metadata": {}, "execution_count": 0, @@ -240,10 +222,7 @@ { "cell_type": "code", "source": [ - "await Import.FromFile(\"CF_DE_2022_12.xlsx\")", - "\n .WithFormat(\"Cashflow\")", - "\n .WithTarget(DataSource)", - "\n .ExecuteAsync()" + "await Import.FromFile(\"CF_DE_2022_12.xlsx\").WithFormat(\"Cashflow\").WithTarget(DataSource).ExecuteAsync()" ], "metadata": {}, "execution_count": 0, diff --git a/ifrs17-template/Files/Parameters/YieldCurve_2020_1.csv b/ifrs17-template/Files/Parameters/YieldCurve_2020_1.csv index c94e347e..c97a6549 100644 --- a/ifrs17-template/Files/Parameters/YieldCurve_2020_1.csv +++ b/ifrs17-template/Files/Parameters/YieldCurve_2020_1.csv @@ -1,6 +1,6 @@ @@Main Year,Month -2020,12 +2020,1 @@YieldCurve Currency,Values0,Values1,Values2,Values3 USD,0.002,0.002,0.002,0.002 diff --git a/ifrs17-template/Files/Parameters/YieldCurve_2020_12.csv b/ifrs17-template/Files/Parameters/YieldCurve_2020_12.csv index 48e9669d..b6fcb54c 100644 --- a/ifrs17-template/Files/Parameters/YieldCurve_2020_12.csv +++ b/ifrs17-template/Files/Parameters/YieldCurve_2020_12.csv @@ -1,6 +1,6 @@ @@Main Year,Month -2020,1 +2020,12 @@YieldCurve Currency,Values0,Values1,Values2,Values3 USD,0.002,0.002,0.002,0.002 diff --git a/ifrs17-template/Test/ScenarioDataImportTest.ipynb b/ifrs17-template/Test/ScenarioDataImportTest.ipynb index c372ff33..e6603fef 100644 --- a/ifrs17-template/Test/ScenarioDataImportTest.ipynb +++ b/ifrs17-template/Test/ScenarioDataImportTest.ipynb @@ -42,16 +42,49 @@ "execution_count": 0, "outputs": [] }, + { + "cell_type": "markdown", + "source": [ + "# Setting Constants" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, { "cell_type": "code", "source": [ "var ws = Workspace.CreateNew();", - "\nws.InitializeFrom(DataSource);", - "\nvar argsBestEstimate = new ImportArgs (\"CH\", 2020, 12, Periodicity.Quarterly, null, ImportFormats.Cashflow);", - "\nvar argsScenario = new ImportArgs (\"CH\", 2020, 12, Periodicity.Quarterly, \"Test\", ImportFormats.Cashflow);", - "\nvar initStorage = new ImportStorage(argsBestEstimate, DataSource, ws);", - "\nawait initStorage.InitializeAsync();", - "\nvar actualETs = initStorage.EstimateTypesByImportFormat[ImportFormats.Actual];", + "\nws.InitializeFrom(DataSource);" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var argsBestEstimate = new ImportArgs (\"CH\", 2020, 12, Periodicity.Quarterly, null, ImportFormats.Cashflow);", + "\nvar argsScenario = new ImportArgs (\"CH\", 2020, 12, Periodicity.Quarterly, \"Test\", ImportFormats.Cashflow);" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var initStorage = new ImportStorage(argsBestEstimate, DataSource, ws);", + "\nawait initStorage.InitializeAsync();" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var actualETs = initStorage.EstimateTypesByImportFormat[ImportFormats.Actual];", "\nvar cashflowETs = initStorage.EstimateTypesByImportFormat[ImportFormats.Cashflow];", "\nvar actualOrCashflowETs = actualETs.Intersect(cashflowETs);", "\nvar onlyActualETs = actualETs.Except(cashflowETs);", @@ -192,9 +225,11 @@ { "cell_type": "code", "source": [ - "actualVars.Length.Should().Be(0);", - "\ncashflowVars.Length.Should().NotBe(0);", - "\ndiffs.Except(actualVars.Union(cashflowVars).Union(actualOrCashflowVars)).ToArray().Length.Should().Be(0);" + "if(EnableScenario) {", + "\n actualVars.Length.Should().Be(0);", + "\n cashflowVars.Length.Should().NotBe(0);", + "\n diffs.Except(actualVars.Union(cashflowVars).Union(actualOrCashflowVars)).ToArray().Length.Should().Be(0);", + "\n}" ], "metadata": {}, "execution_count": 0, @@ -250,9 +285,11 @@ { "cell_type": "code", "source": [ - "actualVars.Length.Should().NotBe(0);", - "\ncashflowVars.Length.Should().Be(0);", - "\ndiffs.Except(actualVars.Union(cashflowVars).Union(actualOrCashflowVars)).ToArray().Length.Should().Be(0);" + "if(EnableScenario) {", + "\n actualVars.Length.Should().NotBe(0);", + "\n cashflowVars.Length.Should().Be(0);", + "\n diffs.Except(actualVars.Union(cashflowVars).Union(actualOrCashflowVars)).ToArray().Length.Should().Be(0);", + "\n}" ], "metadata": {}, "execution_count": 0, @@ -317,9 +354,11 @@ { "cell_type": "code", "source": [ - "actualVars.Length.Should().Be(0);", - "\ncashflowVars.Length.Should().NotBe(0);", - "\ndiffs.Except(actualVars.Union(cashflowVars).Union(actualOrCashflowVars)).ToArray().Length.Should().Be(0);" + "if(EnableScenario) {", + "\n actualVars.Length.Should().Be(0);", + "\n cashflowVars.Length.Should().NotBe(0);", + "\n diffs.Except(actualVars.Union(cashflowVars).Union(actualOrCashflowVars)).ToArray().Length.Should().Be(0);", + "\n}" ], "metadata": {}, "execution_count": 0, @@ -376,9 +415,11 @@ { "cell_type": "code", "source": [ - "diffs.Where(x => x.EstimateType == EstimateTypes.DA).ToArray().Length.Should().NotBe(0);", - "\ndiffs.Where(x => x.EstimateType == EstimateTypes.F).ToArray().Length.Should().Be(0);", - "\ndiffs.Except(actualVars.Union(cashflowVars).Union(actualOrCashflowVars)).ToArray().Length.Should().Be(0);" + "if(EnableScenario) {", + "\n diffs.Where(x => x.EstimateType == EstimateTypes.DA).ToArray().Length.Should().NotBe(0);", + "\n diffs.Where(x => x.EstimateType == EstimateTypes.F).ToArray().Length.Should().Be(0);", + "\n diffs.Except(actualVars.Union(cashflowVars).Union(actualOrCashflowVars)).ToArray().Length.Should().Be(0);", + "\n}" ], "metadata": {}, "execution_count": 0, @@ -441,9 +482,11 @@ { "cell_type": "code", "source": [ - "diffs.Where(x => x.EstimateType == EstimateTypes.DA).ToArray().Length.Should().NotBe(0);", - "\nivsScenarioActualNoACAAEA.Where(x => x.EstimateType == EstimateTypes.F).Count().Should().NotBe(0);", - "\nivsScenarioActualNoACAAEA.Where(x => x.EstimateType == EstimateTypes.DA && Math.Abs(x.Value) > Precision).Count().Should().Be(0);" + "if(EnableScenario) {", + "\n diffs.Where(x => x.EstimateType == EstimateTypes.DA).ToArray().Length.Should().NotBe(0);", + "\n ivsScenarioActualNoACAAEA.Where(x => x.EstimateType == EstimateTypes.F).Count().Should().NotBe(0);", + "\n ivsScenarioActualNoACAAEA.Where(x => x.EstimateType == EstimateTypes.DA && Math.Abs(x.Value) > Precision).Count().Should().Be(0);", + "\n}" ], "metadata": {}, "execution_count": 0, @@ -525,7 +568,7 @@ { "cell_type": "code", "source": [ - "diffs.Count().Should().Be(0);" + "if(EnableScenario) diffs.Count().Should().Be(0);" ], "metadata": {}, "execution_count": 0, @@ -619,8 +662,10 @@ { "cell_type": "code", "source": [ - "diffs.Where(x => onlyCashflowETs.Contains(x.EstimateType) && x.EstimateType != EstimateTypes.F && Math.Abs(x.Value) > Precision).Count().Should().Be(0);", - "\ndiffs.Where(x => onlyActualETs.Contains(x.EstimateType) && Math.Abs(x.Value) > Precision).Count().Should().Be(0);" + "if(EnableScenario) {", + "\n diffs.Where(x => onlyCashflowETs.Contains(x.EstimateType) && x.EstimateType != EstimateTypes.F && Math.Abs(x.Value) > Precision).Count().Should().Be(0);", + "\n diffs.Where(x => onlyActualETs.Contains(x.EstimateType) && Math.Abs(x.Value) > Precision).Count().Should().Be(0);", + "\n}" ], "metadata": {}, "execution_count": 0, @@ -680,9 +725,11 @@ { "cell_type": "code", "source": [ - "cashflowVars.Length.Should().NotBe(0);", - "\nactualVars.Length.Should().Be(0);", - "\ndiffs.Except(actualVars.Union(cashflowVars).Union(actualOrCashflowVars)).ToArray().Length.Should().Be(0);" + "if(EnableScenario) {", + "\n cashflowVars.Length.Should().NotBe(0);", + "\n actualVars.Length.Should().Be(0);", + "\n diffs.Except(actualVars.Union(cashflowVars).Union(actualOrCashflowVars)).ToArray().Length.Should().Be(0);", + "\n}" ], "metadata": {}, "execution_count": 0, @@ -762,9 +809,11 @@ { "cell_type": "code", "source": [ - "actualVars.Length.Should().Be(0);", - "\ncashflowVars.Length.Should().NotBe(0);", - "\ncashflowVars.Where(x => x.EstimateType == EstimateTypes.DA).Count().Should().Be(0);" + "if(EnableScenario) {", + "\n actualVars.Length.Should().Be(0);", + "\n cashflowVars.Length.Should().NotBe(0);", + "\n cashflowVars.Where(x => x.EstimateType == EstimateTypes.DA).Count().Should().Be(0);", + "\n}" ], "metadata": {}, "execution_count": 0, @@ -877,8 +926,28 @@ { "cell_type": "code", "source": [ - "actualVars.Length.Should().Be(0);", - "\ncashflowVars.Length.Should().NotBe(0);" + "if(EnableScenario) {", + "\n actualVars.Length.Should().Be(0);", + "\n cashflowVars.Length.Should().NotBe(0);", + "\n}" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "" ], "metadata": {}, "execution_count": 0, diff --git a/ifrs17-template/Test/ScenarioYieldCurveImportTest.ipynb b/ifrs17-template/Test/ScenarioYieldCurveImportTest.ipynb new file mode 100644 index 00000000..002c811c --- /dev/null +++ b/ifrs17-template/Test/ScenarioYieldCurveImportTest.ipynb @@ -0,0 +1,513 @@ +{ + "metadata": { + "authors": [], + "kernelspec": { + "display_name": "Formula Framework", + "language": "C#", + "name": "C#" + }, + "language_info": { + "file_extension": ".cs", + "mimetype": "text/plain", + "name": "C#" + } + }, + "nbformat": 4, + "nbformat_minor": 5, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "

Scenario Yield Curve Import Test

" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "#!eval-notebook \"../Initialization/InitSystemorphBaseToMemory\"" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "DataSource.Reset(x => x.ResetType().ResetType().ResetType());" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Setting Constants" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var ws = Workspace.CreateNew();", + "\nws.InitializeFrom(DataSource);" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var argsBestEstimate = new ImportArgs (\"CH\", 2020, 12, Periodicity.Quarterly, null, ImportFormats.Cashflow);", + "\nvar argsScenarioYieldCurve = new ImportArgs (\"CH\", 2020, 12, Periodicity.Quarterly, \"YCUP1.0pct\", ImportFormats.Cashflow);", + "\nvar argsScenarioTransactionalData = new ImportArgs (\"CH\", 2020, 12, Periodicity.Quarterly, \"SRUP1.0pct\", ImportFormats.Cashflow);" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var initStorage = new ImportStorage(argsBestEstimate, DataSource, ws);", + "\nawait initStorage.InitializeAsync();" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var actualEstimateTypes = initStorage.EstimateTypesByImportFormat[ImportFormats.Actual];", + "\nvar cashflowEstimateTypes = initStorage.EstimateTypesByImportFormat[ImportFormats.Cashflow];", + "\nvar actualAndCashflowEstimateTypes = actualEstimateTypes.Intersect(cashflowEstimateTypes);", + "\nvar onlyActualEstimateTypes = actualEstimateTypes.Except(cashflowEstimateTypes);", + "\nvar onlyCashflowEstimateTypes = cashflowEstimateTypes.Except(actualEstimateTypes);" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Best Estimate Initialization" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var basicYieldCurve = @\"", + "\n@@Main", + "\nYear,Month,Scenario", + "\n2020,1,", + "\n@@YieldCurve", + "\nCurrency,Values0,Values1,Values2,Values3", + "\nUSD,0.002,0.002,0.002,0.002\";" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "await Import.FromString(basicYieldCurve).WithFormat(ImportFormats.YieldCurve).WithTarget(DataSource).ExecuteAsync()" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var cashflowBestEstimateInit = @\"", + "\n@@Main", + "\nReportingNode,Year,Month,Scenario", + "\nCH,2020,12,", + "\n@@Cashflow", + "\nDataNode,AmountType,EstimateType,AocType,Novelty,AccidentYear,Values0,Values1,Values2,Values3,Values4,Values5,Values6,Values7,Values8,Values9,Values10,Values11,Values12,Values13,Values14,Values15,Values16,Values17,Values18,Values19,Values20,Values21,Values22,Values23", + "\nDT1.1,PR,BE,CL,C,,100,0,0,100,0,0,100,0,0,100,0,0,0,100,0,0,100,0,0,100,0,0,100,0", + "\nDT1.1,NIC,BE,CL,C,,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25", + "\nDT1.1,,CU,CL,C,,-10,-10,-10,-10,-10,-10,-10,-10,-10,-10,-10,-10,-10,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-3", + "\nDT1.1,,RA,CL,C,,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5", + "\nDTR1.1,PR,BE,CL,C,,50,0,0,50,0,0,50,0,0,50,0,0,50,0,0,50,0,0,50,0,0,50,0,0", + "\nDTR1.1,NIC,BE,CL,C,,12.5,12.5,12.5,12.5,12.5,12.5,12.5,12.5,12.5,12.5,12.5,12.5,12.5,12.5,12.5,12.5,12.5,12.5,12.5,12.5,12.5,12.5,12.5,12.5", + "\nDTR1.1,,CU,CL,C,,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5", + "\nDTR1.1,,RA,CL,C,,1.25,1.25,1.25,1.25,1.25,1.25,1.25,1.25,1.25,1.25,1.25,1.25,1.25,1.25,1.25,1.25,1.25,1.25,1.25,1.25,1.25,1.25,1.25,1.25\";" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var actualsBestEstimateInit = @\"", + "\n@@Main", + "\nReportingNode,Year,Month,Scenario", + "\nCH,2020,12,", + "\n@@Actual", + "\nDataNode,AocType,AmountType,EstimateType,AccidentYear,Value", + "\nDT1.1,CF,ACA,A,,-10", + "\nDT1.1,CF,AEA,A,,-5", + "\nDT1.1,CF,NIC,A,,-280", + "\nDT1.1,CF,PR,A,,400", + "\nDTR1.1,CF,PR,A,,-200", + "\nDTR1.1,CF,NIC,A,,140\";" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "await Import.FromString(cashflowBestEstimateInit).WithFormat(ImportFormats.Cashflow).WithTarget(DataSource).ExecuteAsync()" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "await Import.FromString(actualsBestEstimateInit).WithFormat(ImportFormats.Actual).WithTarget(DataSource).ExecuteAsync()" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "await DataSource.Partition.SetAsync(argsBestEstimate);", + "\nvar ivsBestEstimateInit = await DataSource.Query().ToArrayAsync();" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Scenario Yield Curve Import" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var yieldCurveScenario = @\"", + "\n@@Main", + "\nYear,Month,Scenario", + "\n2020,12,YCUP1.0pct", + "\n@@YieldCurve", + "\nCurrency,Values0,Values1,Values2,Values3", + "\nEUR,0.108,0.108,0.118,0.119", + "\nCHF,0.102,0.102,0.102,0.102", + "\nUSD,0.102,0.102,0.102,0.102\";" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "await Import.FromString(yieldCurveScenario).WithFormat(ImportFormats.YieldCurve).WithTarget(DataSource).ExecuteAsync()" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "await DataSource.Partition.SetAsync(argsScenarioYieldCurve);", + "\nvar ivsScenarioYieldCurve = await DataSource.Query().ToArrayAsync();" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var diffs = ivsScenarioYieldCurve.Where(x => Math.Abs(x.Value) > Precision).Except(ivsBestEstimateInit, IfrsVariableComparer.Instance());", + "\nvar actualVars = diffs.Where(x => onlyActualEstimateTypes.Contains(x.EstimateType)).ToArray();", + "\nvar cashflowVars = diffs.Where(x => onlyCashflowEstimateTypes.Contains(x.EstimateType)).ToArray();" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "if(EnableScenario) {", + "\n actualVars.Length.Should().Be(0);", + "\n cashflowVars.Length.Should().NotBe(0);", + "\n}" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Re-Import of the same Yield Curve does not trigger IFRS17 Calculations" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "await Import.FromString(yieldCurveScenario).WithFormat(ImportFormats.YieldCurve).WithTarget(DataSource).ExecuteAsync()" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "await DataSource.Partition.SetAsync(argsScenarioYieldCurve);", + "\nvar ivsScenarioYieldCurveReimport = await DataSource.Query().ToArrayAsync();" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var diffs = ivsScenarioYieldCurve.Except(ivsScenarioYieldCurve, IfrsVariableComparer.Instance());" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "if(EnableScenario) diffs.Count().Should().Be(0);" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Multiple Scenarios ", + "\n", + "\nIn this example, a new Cashflow for a different Scenario - SRUP1.0pct - is imported. ", + "\nThe aim is to check how the Yield Curve importer behaves when a new Best Estimate Yield Curve is imported. ", + "\nWe can identify the following two cases: ", + "\n - a Scenario including a dedicated Yield Curve: only the Best Estimate variables are re-calculated;", + "\n - a Scenario including only transactional data: re-calculation is needed to account for the updated Best Estimate Yield Curve. " + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var cashflowScenario = @\"", + "\n@@Main", + "\nReportingNode,Year,Month,Scenario", + "\nCH,2020,12,SRUP1.0pct", + "\n@@Cashflow", + "\nDataNode,AmountType,EstimateType,AocType,Novelty,AccidentYear,Values0,Values1,Values2,Values3,Values4,Values5,Values6,Values7,Values8,Values9,Values10,Values11,Values12,Values13,Values14,Values15,Values16,Values17,Values18,Values19,Values20,Values21,Values22,Values23", + "\nDT1.1,PR,BE,CL,C,,110,0,0,110,0,0,110,0,0,110,0,0,0,110,0,0,110,0,0,110,0,0,110,0", + "\nDT1.1,NIC,BE,CL,C,,-27.5,-27.5,-27.5,-27.5,-27.5,-27.5,-27.5,-27.5,-27.5,-27.5,-27.5,-27.5,-27.5,-27.5,-27.5,-27.5,-27.5,-27.5,-27.5,-27.5,-27.5,-27.5,-27.5,-27.5", + "\nDT1.1,,RA,CL,C,,-2.75,-2.75,-2.75,-2.75,-2.75,-2.75,-2.75,-2.75,-2.75,-2.75,-2.75,-2.75,-2.75,-2.75,-2.75,-2.75,-2.75,-2.75,-2.75,-2.75,-2.75,-2.75,-2.75,-2.75\";" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "await Import.FromString(cashflowScenario).WithFormat(ImportFormats.Cashflow).WithTarget(DataSource).ExecuteAsync()" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "await DataSource.Partition.SetAsync(argsScenarioTransactionalData);", + "\nvar ivsScenarioTransactionalData = await DataSource.Query().ToArrayAsync();" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var bestEstimateYieldCurve = @\"", + "\n@@Main", + "\nYear,Month", + "\n2020,12", + "\n@@YieldCurve", + "\nCurrency,Values0,Values1,Values2,Values3", + "\nEUR,0.108,0.108,0.118,0.119", + "\nCHF,0.103,0.103,0.103,0.103", + "\nUSD,0.903,0.103,0.103,0.103\";" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "await Import.FromString(bestEstimateYieldCurve).WithFormat(ImportFormats.YieldCurve).WithTarget(DataSource).ExecuteAsync()" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Best Estimate variables are re-calculated" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "await DataSource.Partition.SetAsync(argsBestEstimate);", + "\nvar ivsBestEstimateUpdated = await DataSource.Query().ToArrayAsync();" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var diffs = ivsBestEstimateUpdated.Except(ivsBestEstimateInit, IfrsVariableComparer.Instance());" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "if(EnableScenario) if(EnableScenario) diffs.Count().Should().NotBe(0);" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Scenario with dedicated Yield Curve does not change" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "await DataSource.Partition.SetAsync(argsScenarioYieldCurve);", + "\nvar ivsScenarioYieldCurveUpdated = await DataSource.Query().ToArrayAsync();" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var diffs = ivsScenarioYieldCurveUpdated.Where(x => Math.Abs(x.Value) > Precision).Except(ivsScenarioYieldCurve, IfrsVariableComparer.Instance());" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "if(EnableScenario) diffs.Count().Should().Be(0);" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Scenario including Transactional Data solely is re-calculated" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "await DataSource.Partition.SetAsync(argsScenarioTransactionalData);", + "\nvar ivsScenarioTransactionalDataUpdated = await DataSource.Query().ToArrayAsync();" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var diffs = ivsScenarioTransactionalDataUpdated.Where(x => Math.Abs(x.Value) > Precision).Except(ivsScenarioTransactionalData, IfrsVariableComparer.Instance());" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "if(EnableScenario) diffs.Count().Should().NotBe(0);" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + } + ] +} \ No newline at end of file diff --git a/ifrs17-template/Test/Tests.ipynb b/ifrs17-template/Test/Tests.ipynb index c493097e..3a29c378 100644 --- a/ifrs17-template/Test/Tests.ipynb +++ b/ifrs17-template/Test/Tests.ipynb @@ -30,7 +30,7 @@ "cell_type": "markdown", "source": [ "Comprehensive collection of tests executed on top of the Systemorph use cases (initialization).", - "\n
Execute this Notebook using at least 16Gb RAM." + "\n
Execute this Notebook using at least 20Gb RAM." ], "metadata": {}, "execution_count": 0, @@ -99,6 +99,15 @@ "execution_count": 0, "outputs": [] }, + { + "cell_type": "code", + "source": [ + "#!eval-notebook \"ScenarioYieldCurveImportTest\"" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, { "cell_type": "markdown", "source": [ @@ -154,4 +163,4 @@ "outputs": [] } ] -} \ No newline at end of file +} diff --git a/ifrs17/Constants/Consts.ipynb b/ifrs17/Constants/Consts.ipynb index 08842d5a..23d90c66 100644 --- a/ifrs17/Constants/Consts.ipynb +++ b/ifrs17/Constants/Consts.ipynb @@ -27,6 +27,15 @@ "execution_count": 0, "outputs": [] }, + { + "cell_type": "code", + "source": [ + "public const bool EnableScenario = false;" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, { "cell_type": "markdown", "source": [ diff --git a/ifrs17/Constants/Validations.ipynb b/ifrs17/Constants/Validations.ipynb index 5f22aca2..b68ea84e 100644 --- a/ifrs17/Constants/Validations.ipynb +++ b/ifrs17/Constants/Validations.ipynb @@ -158,7 +158,7 @@ "\n (Error.CreditDefaultRateNotFound , 1) => $\"Credit Default Rate not found for rating {s[0]}.\",", "\n (Error.MissingPremiumAllocation , 1) => $\"Premium Allocation Rate not found for Group of Contract {s[0]}.\", // TODO: this is now a warning to be produced by a validation in the importers (default is 1)", "\n (Error.ReinsuranceCoverage , 1) => $\"Reinsurance Allocation Rate not found for Group of Insurance Contract {s[0]}.\",", - "\n (Error.YieldCurveNotFound , 3) => $\"Yield Curve not found for currency {s[0]}, year {s[1]}, and month {s[2]}.\",", + "\n (Error.YieldCurveNotFound , 5) => $\"Yield Curve not found for Currency {s[0]}, Year {s[1]}, Month {s[2]}, Scenario {(s[3] == null ? \"Best Estimate\" : s[3])} and Name {s[4]}.\",", "\n (Error.YieldCurvePeriodNotApplicable , 2) => $\"YieldCurve period NotApplicable not valid for AoC Step with AoC Type {s[0]} and Novelty {s[1]}.\",", "\n (Error.EconomicBasisNotFound , 1) => $\"EconomicBasis not valid for DataNode {s[0]}.\",", "\n (Error.AccountingVariableTypeNotFound , 1) => $\"AccountingVariableType {s[0]} not found.\",", @@ -184,7 +184,8 @@ "public static string Get (Warning w, params string[] s) => (w, s.Length) switch {", "\n // Import", "\n (Warning.ActiveDataNodeWithCashflowBOPI , 1) => $\"Cash flow with AoC Type: {AocTypes.BOP} and Novelty: {Novelties.I} for Group of Contract {s[0]} is not allowed because previous period data are available.\",", - "\n (Warning.ScenarioReCalculations , 1) => $\"The import of the current file for the Best Estimate scenario makes the result of dependent Scenarios out of date. Hence, the following Scenarios are recalculated: {s[0]}.\", ", + "\n (Warning.VariablesAlreadyImported , 0) => $\"The import of the current file does not contain any new data. Hence, no data will be saved or calculations will be performed.\",", + "\n (Warning.ScenarioReCalculations , 1) => $\"The present Best Estimate import makes the result of dependent Scenarios out of date. Hence, the following Scenarios are re-calculated: {s[0]}.\", ", "\n // Default", "\n (Warning.Generic , _) => $\"{s[0]}\",", "\n (_ , _) => $\"Warning not found.\"", diff --git a/ifrs17/Import/ImportStorage.ipynb b/ifrs17/Import/ImportStorage.ipynb index f6fca951..e5136298 100644 --- a/ifrs17/Import/ImportStorage.ipynb +++ b/ifrs17/Import/ImportStorage.ipynb @@ -145,7 +145,7 @@ "\n await hierarchyCache.InitializeAsync();", "\n ", "\n //EstimateType to load and to update", - "\n EstimateTypesByImportFormat = new InputSource[] { InputSource.Opening, InputSource.Actual, InputSource.Cashflow,}", + "\n EstimateTypesByImportFormat = new InputSource[] { InputSource.Opening, InputSource.Actual, InputSource.Cashflow }", "\n .ToDictionary(x => x.ToString(), ", "\n x => estimateTypes", "\n .Where(et => et.InputSource.Contains(x))", diff --git a/ifrs17/Import/Importers.ipynb b/ifrs17/Import/Importers.ipynb index 52c5a764..e4f2bcda 100644 --- a/ifrs17/Import/Importers.ipynb +++ b/ifrs17/Import/Importers.ipynb @@ -100,7 +100,8 @@ "\n await workspace.Partition.SetAsync(TargetPartitionByReportingNode.Id);", "\n await dataSource.Partition.SetAsync(TargetPartitionByReportingNode.Id);", "\n ", - "\n if(args.Year != default(int) && args.Month != default(int))", + "\n if(args.ImportFormat == ImportFormats.Cashflow || args.ImportFormat == ImportFormats.Actual || ", + "\n args.ImportFormat == ImportFormats.SimpleValue || args.ImportFormat == ImportFormats.Opening)", "\n {", "\n TargetPartitionByReportingNodeAndPeriod = (await workspace.Query()", "\n .Where(p => p.ReportingNode == args.ReportingNode &&", @@ -135,10 +136,8 @@ "\n ImportFormats.Opening => aocConfigurationByAocStep.Where(x => x.InputSource.Contains(InputSource.Opening) && x.DataType == DataType.Optional).GroupBy(x => new AocStep(x.AocType, x.Novelty), (k,v) => k).ToHashSet(),", "\n ImportFormats.SimpleValue => aocConfigurationByAocStep.GroupBy(x => new AocStep(x.AocType, x.Novelty), (k,v) => k).Concat((await dataSource.Query().ToArrayAsync())", "\n .Select(vt => new AocStep(vt.SystemName,null))).ToHashSet(),", - "\n _ => Enumerable.Empty().ToHashSet(),", + "\n _ => Enumerable.Empty().ToHashSet(),", "\n };", - "\n ", - "\n //DataNodes", "\n DataNodeDataBySystemName = args.ImportFormat == ImportFormats.Opening ", "\n ? (await LoadDataNodesAsync(dataSource, args)).Where(kvp => kvp.Value.Year == args.Year).ToDictionary(kvp => kvp.Key, kvp => kvp.Value)", "\n : await LoadDataNodesAsync(dataSource, args);", @@ -326,10 +325,10 @@ "\n if(ApplicationMessage.HasErrors()) return null;", "\n", "\n var main = mainTab.Rows.First();", - "\n var reportingNode = mainTab.Columns.Any(x => x.ColumnName == nameof(Args.ReportingNode)) ? (string)main[nameof(ReportingNode)] : default(string);", - "\n var scenario = mainTab.Columns.Any(x => x.ColumnName == nameof(Args.Scenario)) ? (string)main[nameof(Scenario)] : default(string);", - "\n var year = mainTab.Columns.Any(x => x.ColumnName == nameof(Args.Year)) ? (int)Convert.ChangeType(main[nameof(Args.Year)], typeof(int)) : default(int);", - "\n var month = mainTab.Columns.Any(x => x.ColumnName == nameof(Args.Month)) ? (int)Convert.ChangeType(main[nameof(Args.Month)], typeof(int)) : default(int);", + "\n var reportingNode = mainTab.Columns.Any(x => x.ColumnName == nameof(Args.ReportingNode)) && main[nameof(Args.ReportingNode)] != null ? (string)main[nameof(ReportingNode)] : default(string);", + "\n var scenario = mainTab.Columns.Any(x => x.ColumnName == nameof(Args.Scenario)) && main[nameof(Args.Scenario)] != null ? (string)main[nameof(Scenario)] : default(string);", + "\n var year = mainTab.Columns.Any(x => x.ColumnName == nameof(Args.Year)) && main[nameof(Args.Year)] != null ? (int)Convert.ChangeType(main[nameof(Args.Year)], typeof(int)) : default(int);", + "\n var month = mainTab.Columns.Any(x => x.ColumnName == nameof(Args.Month)) && main[nameof(Args.Month)] != null ? (int)Convert.ChangeType(main[nameof(Args.Month)], typeof(int)) : default(int);", "\n", "\n return new ImportArgs(reportingNode, year, month, default(Periodicity), scenario, default(string));", "\n}" @@ -362,9 +361,57 @@ { "cell_type": "markdown", "source": [ - "## Get Args and commit Partition", + "## Create Partition", + "\n", + "\nThese are the methods used to create the partition if not already existing in the DataSource. " + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "public async Task CommitPartitionAsync(ImportArgs args, params IDataSource[] dataSources)", + "\n{", + "\n foreach (var dataSource in dataSources) {", + "\n switch(typeof(IPartition).Name) {", + "\n case nameof(PartitionByReportingNode) : {", + "\n await dataSource.UpdateAsync( new[] { new PartitionByReportingNode { ", + "\n Id = (Guid)(await DataSource.Partition.GetKeyForInstanceAsync(args)),", + "\n ReportingNode = args.ReportingNode } } );", + "\n break;", + "\n }", + "\n case nameof(PartitionByReportingNodeAndPeriod) : {", + "\n args.ValidateArgsForPeriod();", + "\n if(ApplicationMessage.HasErrors()) return;", + "\n await dataSource.UpdateAsync( new[]{ new PartitionByReportingNodeAndPeriod { ", + "\n Id = (Guid)(await DataSource.Partition.GetKeyForInstanceAsync(args)),", + "\n Year = args.Year,", + "\n Month = args.Month,", + "\n ReportingNode = args.ReportingNode, ", + "\n Scenario = args.Scenario } } );", + "\n break;", + "\n }", + "\n default : {", + "\n ApplicationMessage.Log(Error.PartitionTypeNotFound, typeof(IPartition).Name); ", + "\n return;", + "\n }", + "\n }", + "\n await dataSource.CommitAsync();", + "\n }", + "\n}" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Get Args and create Partition", "\n", - "\nThis is the main method to get Args which reference a specific data partition. All validations are triggered after parsing and if the partition is not already existing in the DataSource it is created and committed. " + "\nThis is the main method to get Args which reference a specific data partition. All validations are triggered after parsing and if the partition is not already existing in the DataSource it is created. " ], "metadata": {}, "execution_count": 0, @@ -378,32 +425,7 @@ "\n var args = GetArgsFromMain(dataSet);", "\n if(ApplicationMessage.HasErrors()) return null;", "\n if(args.ReportingNode == default(string)) { ApplicationMessage.Log(Error.ReportingNodeInMainNotFound); return null; }", - "\n", - "\n switch(typeof(IPartition).Name) {", - "\n case nameof(PartitionByReportingNode) : {", - "\n await DataSource.UpdateAsync( new[] { new PartitionByReportingNode { ", - "\n Id = (Guid)(await DataSource.Partition.GetKeyForInstanceAsync(args)),", - "\n ReportingNode = args.ReportingNode } } );", - "\n break;", - "\n }", - "\n case nameof(PartitionByReportingNodeAndPeriod) : {", - "\n args.ValidateArgsForPeriod();", - "\n if(ApplicationMessage.HasErrors()) return null;", - "\n await DataSource.UpdateAsync( new[]{ new PartitionByReportingNodeAndPeriod { ", - "\n Id = (Guid)(await DataSource.Partition.GetKeyForInstanceAsync(args)),", - "\n Year = args.Year,", - "\n Month = args.Month,", - "\n ReportingNode = args.ReportingNode, ", - "\n Scenario = args.Scenario } } );", - "\n break;", - "\n }", - "\n default : {", - "\n ApplicationMessage.Log(Error.PartitionTypeNotFound, typeof(IPartition).Name); ", - "\n return null;", - "\n }", - "\n }", - "\n ", - "\n await DataSource.CommitAsync();", + "\n await CommitPartitionAsync(args, DataSource);", "\n return args;", "\n}" ], @@ -481,6 +503,7 @@ "\nThe following methods are used in the different importers to compute the [IfrsVariables](../DataModel/DataStructure#ifrs-variable).", "\n", "\nGetAllArgsAsync retrieves the partitions or Args that require computation. They are the union of the primary args (the one read from the main tab of the imported file) with the secondary args (senarios which depends on the best estimate data).", + "\n", "\nComputeAsync computes the IfrsVariables for a given partition (identified by its ImportArgs) and stores the results in a disposable workspace. This then serves as DataSource in the calculation of the secondary partitions (identified by the secondary args)." ], "metadata": {}, @@ -490,19 +513,42 @@ { "cell_type": "code", "source": [ - "public async Task GetAllArgsAsync(ImportArgs primaryArgs)", + "public async Task GetAllArgsAsync(ImportArgs args, IDataSource dataSource, string format)", "\n{", - "\n if (primaryArgs.Scenario != null)", - "\n return primaryArgs.RepeatOnce().ToArray();", - "\n", - "\n var secondaryArgs = await DataSource.Query() ", - "\n .Where(x => x.ReportingNode == primaryArgs.ReportingNode && x.Year == primaryArgs.Year && x.Month == primaryArgs.Month && x.Scenario != null)", - "\n .Select(x => new ImportArgs(x.ReportingNode, x.Year, x.Month, default(Periodicity), x.Scenario, ImportFormats.Cashflow)).ToArrayAsync();", + "\n ImportArgs[] allArgs;", + "\n switch(format)", + "\n {", + "\n case ImportFormats.YieldCurve : ", + "\n {", + "\n if(args.Scenario == null) {", + "\n var scenariosWithYieldCurves = await dataSource.Query().Where(x => x.Year == args.Year && x.Month == args.Month && x.Scenario != null)", + "\n .Select(x => x.Scenario).Distinct().ToArrayAsync();", + "\n var targetPartitions = await dataSource.Query()", + "\n .Where(x => x.Year == args.Year && x.Month == args.Month && !scenariosWithYieldCurves.Contains(x.Scenario)).OrderBy(x => x.Scenario).ToArrayAsync();", + "\n var targetScenarios = targetPartitions.Where(x => x.Scenario != null).Select(x => x.Scenario);", + "\n if(targetScenarios.Any()) ApplicationMessage.Log(Warning.ScenarioReCalculations, String.Join(\", \", targetScenarios));", + "\n allArgs = targetPartitions.Select(x => new ImportArgs(x.ReportingNode, x.Year, x.Month, default(Periodicity), x.Scenario, ImportFormats.Cashflow)).ToArray();", + "\n }", + "\n else {", + "\n allArgs = (await dataSource.Query()", + "\n .Where(x => x.Year == args.Year && x.Month == args.Month && x.Scenario == null).ToArrayAsync())", + "\n .Select(x => new ImportArgs(x.ReportingNode, x.Year, x.Month, default(Periodicity), args.Scenario, ImportFormats.Cashflow)).ToArray();", + "\n }", + "\n break;", + "\n }", + "\n default : ", + "\n {", + "\n if(args.Scenario != null) return args.RepeatOnce().ToArray();", + "\n var secondaryArgs = await DataSource.Query() ", + "\n .Where(x => x.ReportingNode == args.ReportingNode && x.Year == args.Year && x.Month == args.Month && x.Scenario != null)", + "\n .Select(x => new ImportArgs(x.ReportingNode, x.Year, x.Month, default(Periodicity), x.Scenario, ImportFormats.Cashflow)).ToArrayAsync();", "\n ", - "\n if (secondaryArgs.Any())", - "\n ApplicationMessage.Log(Warning.ScenarioReCalculations, String.Join(\", \", secondaryArgs.Select(x => x.Scenario)));", - "\n ", - "\n return primaryArgs.RepeatOnce().Concat(secondaryArgs).ToArray();", + "\n if(secondaryArgs.Any()) ApplicationMessage.Log(Warning.ScenarioReCalculations, String.Join(\", \", secondaryArgs.Select(x => x.Scenario)));", + "\n allArgs = args.RepeatOnce().Concat(secondaryArgs).ToArray();", + "\n break;", + "\n }", + "\n }", + "\n return allArgs.Where(x => (!EnableScenario && x.Scenario == null) || EnableScenario).ToArray();", "\n}" ], "metadata": {}, @@ -525,7 +571,7 @@ "\n if(Activity.HasErrors()) return Activity.Finish();", "\n", "\n if(storage.DefaultPartition != storage.TargetPartition){", - "\n var bestEstimateIvs = await DataSource.LoadPartitionedDataAsync(storage.DefaultPartition, storage.TargetPartition);", + "\n var bestEstimateIvs = await DataSource.LoadPartitionedDataAsync(storage.DefaultPartition);", "\n ivs = ivs.Where(iv => Math.Abs(iv.Value) >= Precision).ToArray().Concat(", "\n ivs.Where(iv => Math.Abs(iv.Value) < Precision).Intersect(bestEstimateIvs, EqualityComparer.Instance).Select(iv => iv with {Value = 0.0}).ToArray());", "\n }", @@ -678,44 +724,64 @@ "source": [ "Import.DefineFormat(ImportFormats.YieldCurve, async (options, dataSet) => {", "\n Activity.Start();", - "\n var args = GetArgsFromMain(dataSet) with {ImportFormat = ImportFormats.YieldCurve};", - "\n args.ValidateArgsForPeriod();", - "\n if(ApplicationMessage.HasErrors()) return Activity.Finish();", - "\n", + "\n var primaryArgs = GetArgsFromMain(dataSet) with {ImportFormat = ImportFormats.YieldCurve};", + "\n primaryArgs.ValidateArgsForPeriod();", + "\n if(ApplicationMessage.HasErrors()) return Activity.Finish();", "\n var workspace = Workspace.CreateNew();", - "\n workspace.Initialize(x => x.FromSource(DataSource).DisableInitialization().DisableInitialization());", + "\n workspace.Initialize(x => x.FromSource(options.TargetDataSource).DisableInitialization()", + "\n .DisableInitialization().DisableInitialization());", "\n", - "\n var committedYieldCurves = await DataSource.Query().ToArrayAsync();", + "\n var committedYieldCurves = await options.TargetDataSource.Query().ToArrayAsync();", "\n var hasNameColumn = dataSet.Tables[ImportFormats.YieldCurve].Columns.Any(x => x.ColumnName == nameof(YieldCurve.Name));", - "\n var importLog = await Import.FromDataSet(dataSet).WithType((dataset, datarow) => { ", - "\n var yieldCurve = new YieldCurve {", - "\n Currency = datarow.Field(nameof(YieldCurve.Currency)),", - "\n Year = args.Year,", - "\n Month = args.Month, ", - "\n Scenario = args.Scenario,", - "\n Values = datarow.Table.Columns.Where(c => c.ColumnName.StartsWith(nameof(YieldCurve.Values))).OrderBy(c => c.ColumnName.Length).ThenBy(c => c.ColumnName)", - "\n .Select(x => datarow.Field(x.ColumnName).CheckStringForExponentialAndConvertToDouble()).ToArray(),", - "\n Name = hasNameColumn", - "\n ? datarow.Field(nameof(YieldCurve.Name))", - "\n : default(string)", - "\n };", - "\n return committedYieldCurves.Contains(yieldCurve, YieldCurveComparer.Instance())", - "\n ? null", - "\n : yieldCurve;", - "\n }", - "\n ).WithTarget(workspace).ExecuteAsync(); ", - "\n ", + "\n var importLog = await Import.FromDataSet(dataSet).WithType((dataset, datarow) => { ", + "\n var yieldCurve = new YieldCurve { ", + "\n Currency = datarow.Field(nameof(YieldCurve.Currency)),", + "\n Year = primaryArgs.Year,", + "\n Month = primaryArgs.Month, ", + "\n Scenario = primaryArgs.Scenario,", + "\n Values = datarow.Table.Columns.Where(c => c.ColumnName.StartsWith(nameof(YieldCurve.Values))).OrderBy(c => c.ColumnName.Length).ThenBy(c => c.ColumnName)", + "\n .Select(x => datarow.Field(x.ColumnName).CheckStringForExponentialAndConvertToDouble()).ToArray(),", + "\n Name = hasNameColumn ? datarow.Field(nameof(YieldCurve.Name)) : default(string)", + "\n };", + "\n return committedYieldCurves.Contains(yieldCurve, YieldCurveComparer.Instance()) ? null : yieldCurve;", + "\n }).WithTarget(workspace).ExecuteAsync(); ", + "\n", "\n if(importLog.Errors.Any()) return Activity.Finish().Merge(importLog); ", - "\n ", - "\n var toCommitYieldCurves = await workspace.Query().ToArrayAsync();", - "\n if (!toCommitYieldCurves.Any()){", + "\n var toCommitYieldCurves = await workspace.Query().ToArrayAsync();", + "\n if (!toCommitYieldCurves.Any()) {", "\n ApplicationMessage.Log(Warning.VariablesAlreadyImported); ", "\n return Activity.Finish().Merge(importLog);", "\n }", - "\n ", - "\n await DataSource.UpdateAsync(toCommitYieldCurves);", - "\n await DataSource.CommitAsync();", + "\n", + "\n var allArgs = await GetAllArgsAsync(primaryArgs, options.TargetDataSource, ImportFormats.YieldCurve);", + "\n var updatedCurrencies = toCommitYieldCurves.Select(x => x.Currency).Distinct();", + "\n var dataNodesToUpdate = await workspace.Query().Where(x => updatedCurrencies.Contains(x.ContractualCurrency)).Select(x => x.SystemName).ToArrayAsync();", + "\n var workspaceToCompute = Workspace.CreateNew();", + "\n workspaceToCompute.Initialize(x => x.FromSource(options.TargetDataSource));", "\n ", + "\n foreach (var args in allArgs) {", + "\n await CommitPartitionAsync(args, workspace, workspaceToCompute);", + "\n var targetPartition = (Guid)(await options.TargetDataSource.Partition.GetKeyForInstanceAsync(args));", + "\n var defaultPartition = (Guid)(await options.TargetDataSource.Partition.GetKeyForInstanceAsync(", + "\n new ImportArgs(args.ReportingNode, args.Year, args.Month, default(Periodicity), null, ImportFormats.Cashflow) ));", + "\n if(ApplicationMessage.HasErrors()) return Activity.Finish().Merge(importLog);", + "\n ", + "\n // Avoid starting the computation if no best estimate cash flow has ever been imported ", + "\n if(args.Scenario == null) {", + "\n await options.TargetDataSource.Partition.SetAsync(null);", + "\n if(!(await options.TargetDataSource.Query().Where(x => x.Partition == targetPartition).Take(1).ToArrayAsync()).Any()) continue;", + "\n }", + "\n", + "\n // Remove data nodes which are unaffected by the updated yield curves", + "\n await workspaceToCompute.DeleteAsync( await workspaceToCompute.Query()", + "\n .Where(x => !(dataNodesToUpdate.Contains(x.DataNode) && (x.Partition == targetPartition || x.Partition == defaultPartition))).ToArrayAsync() );", + "\n", + "\n importLog = importLog.Merge(await ComputeAsync(args, workspace, workspaceToCompute, false));", + "\n if(importLog.Errors.Any()) return Activity.Finish().Merge(importLog);", + "\n }", + "\n", + "\n await workspaceToCompute.UpdateAsync(toCommitYieldCurves);", + "\n await workspaceToCompute.CommitToTargetAsync(options.TargetDataSource);", "\n return Activity.Finish().Merge(importLog);", "\n});" ], @@ -1160,7 +1226,7 @@ "\n var primaryArgs = await GetArgsAndCommitPartitionAsync(dataSet) with {ImportFormat = ImportFormats.Cashflow};", "\n if(Activity.HasErrors()) return Activity.Finish();", "\n", - "\n var allArgs = await GetAllArgsAsync(primaryArgs);", + "\n var allArgs = await GetAllArgsAsync(primaryArgs, options.TargetDataSource, ImportFormats.Cashflow);", "\n await DataNodeFactoryAsync(dataSet, ImportFormats.Cashflow, primaryArgs);", "\n if(Activity.HasErrors()) return Activity.Finish();", "\n ", @@ -1261,7 +1327,7 @@ "\n var primaryArgs = await GetArgsAndCommitPartitionAsync(dataSet) with {ImportFormat = ImportFormats.Actual};", "\n if(Activity.HasErrors()) return Activity.Finish();", "\n", - "\n var allArgs = await GetAllArgsAsync(primaryArgs);", + "\n var allArgs = await GetAllArgsAsync(primaryArgs, options.TargetDataSource, ImportFormats.Actual);", "\n await DataNodeFactoryAsync(dataSet, ImportFormats.Actual, primaryArgs);", "\n if(Activity.HasErrors()) return Activity.Finish();", "\n", diff --git a/ifrs17/Utils/Queries.ipynb b/ifrs17/Utils/Queries.ipynb index 2a102c0f..6fa652ed 100644 --- a/ifrs17/Utils/Queries.ipynb +++ b/ifrs17/Utils/Queries.ipynb @@ -193,7 +193,7 @@ "\n var loadedYc = (await querySource.LoadCurrentParameterAsync(argsNew, x => x.Currency, x => x.Currency == dn.ContractualCurrency && x.Name == dn.YieldCurveName));", "\n ", "\n if (!loadedYc.TryGetValue(dn.ContractualCurrency, out var lockedYc))", - "\n ApplicationMessage.Log(Error.YieldCurveNotFound, dn.ContractualCurrency, argsNew.Year.ToString(), argsNew.Month.ToString());", + "\n ApplicationMessage.Log(Error.YieldCurveNotFound, dn.ContractualCurrency, argsNew.Year.ToString(), argsNew.Month.ToString(), argsNew.Scenario, dn.YieldCurveName);", "\n ", "\n lockedInYieldCurveByGoc[dn.DataNode] = lockedYc;", "\n }", @@ -235,7 +235,7 @@ "\n : true));", "\n ", "\n if (!loadedYc.TryGetValue(key.ContractualCurrency, out var currentYcDict))", - "\n ApplicationMessage.Log(Error.YieldCurveNotFound, key.ContractualCurrency, args.Year.ToString(), args.Month.ToString());", + "\n ApplicationMessage.Log(Error.YieldCurveNotFound, key.ContractualCurrency, args.Year.ToString(), args.Month.ToString(), args.Scenario, key.YieldCurveName);", "\n", "\n foreach(var dn in dnByValAppContrCurrYcName[key])", "\n {", @@ -432,15 +432,15 @@ { "cell_type": "code", "source": [ - "public static async Task LoadPartitionedDataAsync(this IDataSource querySource, Guid partition, Guid? partitionBackup = null)", + "public static async Task LoadPartitionedDataAsync(this IDataSource querySource, Guid partition)", "\n where T : IPartitioned", "\n where P : IPartition", - "\n", "\n{", + "\n var partitionBackup = (Guid)querySource.Partition.GetCurrent(typeof(P).Name);", "\n await querySource.Partition.SetAsync

(partition);", "\n // Temporary workaround for physical database: where clause is necessary", "\n var data = await querySource.Query().Where(x => x.Partition == partition).ToArrayAsync();", - "\n if(partitionBackup != null) await querySource.Partition.SetAsync

(partitionBackup);", + "\n await querySource.Partition.SetAsync

(partitionBackup);", "\n return data;", "\n}" ], @@ -455,19 +455,19 @@ "\n where T : IPartitioned", "\n where P : IPartition", "\n{", - "\n var isRelaxed = targetPartition != defaultPartition &&", - "\n ((format != ImportFormats.Cashflow && typeof(T).Name == nameof(IfrsVariable)) ||", + "\n var isRelaxed = ((format != ImportFormats.Cashflow && typeof(T).Name == nameof(IfrsVariable)) ||", "\n (format == ImportFormats.Cashflow && typeof(T).Name == nameof(RawVariable)));", "\n", "\n var variablesFromWorkspace = await workspace.LoadPartitionedDataAsync(targetPartition);", "\n if(!isRelaxed || variablesFromWorkspace.Any()) return variablesFromWorkspace;", "\n", - "\n // For scenario re-calculation", + "\n // This is for scenario re-calculation", "\n var variablesFromDataSource = await dataSource.LoadPartitionedDataAsync(targetPartition);", "\n if(variablesFromDataSource.Any()) return variablesFromDataSource;", "\n", - "\n // For scenarios related to parameters solely", - "\n return await dataSource.LoadPartitionedDataAsync(defaultPartition, targetPartition);", + "\n // This is for scenarios affecting parameters solely", + "\n // And for the best estimate when parameters are updated", + "\n return await dataSource.LoadPartitionedDataAsync(defaultPartition);", "\n}" ], "metadata": {}, From 2b02150884db1ca1b7e0de7d7dd1f791fb695e9a Mon Sep 17 00:00:00 2001 From: nnikolopoulos <114566025+nnikolopoulos@users.noreply.github.com> Date: Tue, 24 Jan 2023 13:27:22 +0100 Subject: [PATCH 2/9] Place importFormat string outside of variable in GetSign (#192) Place importFormat string outside of variable --- ifrs17/Import/Importers.ipynb | 6 +++--- ifrs17/Utils/ImportCalculationMethods.ipynb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ifrs17/Import/Importers.ipynb b/ifrs17/Import/Importers.ipynb index e4f2bcda..5d680c6c 100644 --- a/ifrs17/Import/Importers.ipynb +++ b/ifrs17/Import/Importers.ipynb @@ -1204,7 +1204,7 @@ "\n ? accidentYear", "\n : (int?)null,", "\n Partition = parsingStorage.TargetPartitionByReportingNodeAndPeriod.Id,", - "\n Values = Multiply(GetSign((ImportFormats.Cashflow, aocType, valueType.AmountType, valueType.EstimateType, dataNodeData.IsReinsurance), parsingStorage.HierarchyCache), values)", + "\n Values = Multiply(GetSign(ImportFormats.Cashflow, (aocType, valueType.AmountType, valueType.EstimateType, dataNodeData.IsReinsurance), parsingStorage.HierarchyCache), values)", "\n };", "\n return item;", "\n }, ImportFormats.Cashflow", @@ -1305,7 +1305,7 @@ "\n AmountType = valueType.AmountType,", "\n EstimateType = valueType.EstimateType,", "\n Partition = parsingStorage.TargetPartitionByReportingNodeAndPeriod.Id,", - "\n Value = GetSign((ImportFormats.Actual, aocType, valueType.AmountType, valueType.EstimateType, dataNodeData.IsReinsurance), parsingStorage.HierarchyCache) * datarow.Field(nameof(IfrsVariable.Value)).CheckStringForExponentialAndConvertToDouble()", + "\n Value = GetSign(ImportFormats.Actual, (aocType, valueType.AmountType, valueType.EstimateType, dataNodeData.IsReinsurance), parsingStorage.HierarchyCache) * datarow.Field(nameof(IfrsVariable.Value)).CheckStringForExponentialAndConvertToDouble()", "\n };", "\n return item;", "\n }, ImportFormats.Actual", @@ -1401,7 +1401,7 @@ "\n EstimateType = estimateType,", "\n EconomicBasis = economicBasis,", "\n Partition = parsingStorage.TargetPartitionByReportingNodeAndPeriod.Id,", - "\n Value = GetSign((importFormat, aocStep.AocType, amountType, estimateType, parsingStorage.IsDataNodeReinsurance(dataNode)), parsingStorage.HierarchyCache) * datarow.Field(nameof(IfrsVariable.Value)).CheckStringForExponentialAndConvertToDouble()", + "\n Value = GetSign(importFormat, (aocStep.AocType, amountType, estimateType, parsingStorage.IsDataNodeReinsurance(dataNode)), parsingStorage.HierarchyCache) * datarow.Field(nameof(IfrsVariable.Value)).CheckStringForExponentialAndConvertToDouble()", "\n };", "\n return iv;", "\n }, importFormat // This should indicate the table name, not the input format", diff --git a/ifrs17/Utils/ImportCalculationMethods.ipynb b/ifrs17/Utils/ImportCalculationMethods.ipynb index a380baa8..4e8270a2 100644 --- a/ifrs17/Utils/ImportCalculationMethods.ipynb +++ b/ifrs17/Utils/ImportCalculationMethods.ipynb @@ -219,7 +219,7 @@ { "cell_type": "code", "source": [ - "public static int GetSign((string importFormat, string AocType, string AmountType, string EstimateType, bool IsReinsurance) variable, Systemorph.Vertex.Hierarchies.IHierarchicalDimensionCache hierarchyCache) => 1;" + "public static int GetSign(string importFormat, (string AocType, string AmountType, string EstimateType, bool IsReinsurance) variable, Systemorph.Vertex.Hierarchies.IHierarchicalDimensionCache hierarchyCache) => 1;" ], "metadata": {}, "execution_count": 0, From 7cb34cc40e4d575909a3e0d7b23e67d3aa4f9396 Mon Sep 17 00:00:00 2001 From: Davide Colleoni <103409906+dcolleoni@users.noreply.github.com> Date: Tue, 24 Jan 2023 16:24:25 +0100 Subject: [PATCH 3/9] Current YieldCurve query fix on YcName filter (#194) * fix and test * fix YC query bm and refine locked int month defition + add a tests --- ifrs17/Test/QueriesTest.ipynb | 115 +++++++++++++++++++++++++++++++--- ifrs17/Utils/Queries.ipynb | 5 +- 2 files changed, 110 insertions(+), 10 deletions(-) diff --git a/ifrs17/Test/QueriesTest.ipynb b/ifrs17/Test/QueriesTest.ipynb index d48841bd..7e87e8b6 100644 --- a/ifrs17/Test/QueriesTest.ipynb +++ b/ifrs17/Test/QueriesTest.ipynb @@ -571,6 +571,43 @@ "execution_count": 0, "outputs": [] }, + { + "cell_type": "code", + "source": [ + "var args = new Args(\"CH\",2020,12,Periodicity.Monthly,null);", + "\nvar testData = new YieldCurve[] {", + "\n new YieldCurve{ Currency = \"EUR\", Year = 2020, Month = 12, Name = \"A\", Values = new double[]{7.1,7.2,7.3,7.4,7.5,7.6} },", + "\n new YieldCurve{ Currency = \"EUR\", Year = 2020, Month = 12, Values = new double[]{0.1,0.2,0.3,0.4,0.5,0.6} }, ", + "\n ", + "\n };", + "\n", + "\n(int year , int month, string yieldCurveName, string valuationApproach) dataNodeTestData = (2020, 12, \"A\", \"BBA\");", + "\nvar activity = await CheckLoadYieldCurveAsync(args, testData, dataNodeTestData, 0.1, 0.1, 7.1);", + "\nactivity" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var args = new Args(\"CH\",2020,9,Periodicity.Monthly,null);", + "\nvar testData = new YieldCurve[] {", + "\n new YieldCurve{ Currency = \"EUR\", Year = 2020, Month = 12, Name = \"A\", Values = new double[]{7.1,7.2,7.3,7.4,7.5,7.6} },", + "\n new YieldCurve{ Currency = \"EUR\", Year = 2020, Month = 12, Values = new double[]{0.1,0.2,0.3,0.4,0.5,0.6} }, ", + "\n new YieldCurve{ Currency = \"EUR\", Year = 2020, Month = 9, Name = \"A\", Values = new double[]{6.1,6.2,6.3,6.4,6.5,6.6} },", + "\n new YieldCurve{ Currency = \"EUR\", Year = 2020, Month = 9, Values = new double[]{0.01,0.02,0.03,0.04,0.05,0.06} },", + "\n };", + "\n", + "\n(int year , int month, string yieldCurveName, string valuationApproach) dataNodeTestData = (2020, 1, \"A\", \"BBA\");", + "\nvar activity = await CheckLoadYieldCurveAsync(args, testData, dataNodeTestData, 0.01, 0.01, 6.1);", + "\nactivity" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, { "cell_type": "code", "source": [ @@ -613,7 +650,7 @@ "\n new YieldCurve{ Currency = \"EUR\", Year = 2016, Month = 3, Name = \"B\", Values = new double[]{7.1,7.2,7.3,7.4,7.5,7.6} }};", "\n", "\n(int year , int month, string yieldCurveName, string valuationApproach) dataNodeTestData = (2016, 6, \"A\", \"BBA\");", - "\nvar activity = await CheckLoadYieldCurveAsync(args, testData, dataNodeTestData, 6.1, 0.1, 6.1);", + "\nvar activity = await CheckLoadYieldCurveAsync(args, testData, dataNodeTestData, 9.1, 1.1, 6.1);", "\nactivity" ], "metadata": {}, @@ -634,20 +671,29 @@ "source": [ "var args = new Args(\"CH\",2020,9,Periodicity.Monthly,null);", "\nvar testData = new YieldCurve[] {new YieldCurve{ Currency = \"EUR\", Year = 2020, Month = 12, Name = \"A\", Values = new double[]{11.1,11.2,11.3,11.4,11.5,11.6} }, ", + "\n new YieldCurve{ Currency = \"EUR\", Year = 2020, Month = 12, Values = new double[]{100.1,100.2,100.3,100.4,100.5,100.6} }, ", "\n new YieldCurve{ Currency = \"EUR\", Year = 2020, Month = 9, Name = \"A\", Values = new double[]{12.1,12.2,12.3,12.4,12.5,12.6} },", + "\n new YieldCurve{ Currency = \"EUR\", Year = 2020, Month = 9, Values = new double[]{90.1,90.2,90.3,90.4,90.5,90.6} }, ", "\n new YieldCurve{ Currency = \"EUR\", Year = 2020, Month = 6, Name = \"A\", Values = new double[]{13.1,13.2,13.3,13.4,13.5,13.6} },", + "\n new YieldCurve{ Currency = \"EUR\", Year = 2020, Month = 6, Values = new double[]{80.1,80.2,80.3,80.4,80.5,80.6} }, ", "\n new YieldCurve{ Currency = \"EUR\", Year = 2020, Month = 3, Name = \"A\", Values = new double[]{1.1,1.2,1.3,1.4,1.5,1.6} }, ", - "\n new YieldCurve{ Currency = \"EUR\", Year = 2019, Month = 12, Name = \"A\", Values = new double[]{9.1,9.2,9.3,9.4,9.5,9.6} }, ", + "\n new YieldCurve{ Currency = \"EUR\", Year = 2020, Month = 3, Values = new double[]{70.1,70.2,70.3,70.4,70.5,70.6} }, ", + "\n new YieldCurve{ Currency = \"EUR\", Year = 2019, Month = 12, Name = \"A\", Values = new double[]{9.1,9.2,9.3,9.4,9.5,9.6} },", + "\n new YieldCurve{ Currency = \"EUR\", Year = 2019, Month = 12, Values = new double[]{60.1,60.2,60.3,60.4,60.5,60.6} },", "\n new YieldCurve{ Currency = \"EUR\", Year = 2019, Month = 9, Name = \"A\", Values = new double[]{10.1,10.2,10.3,10.4,10.5,10.6} }, ", - "\n new YieldCurve{ Currency = \"EUR\", Year = 2019, Month = 6, Name = \"A\", Values = new double[]{2.1,2.2,2.3,2.4,2.5,2.6} }, ", - "\n new YieldCurve{ Currency = \"EUR\", Year = 2019, Month = 3, Name = \"A\", Values = new double[]{7.1,7.2,7.3,7.4,7.5,7.6} }, ", + "\n new YieldCurve{ Currency = \"EUR\", Year = 2019, Month = 9, Values = new double[]{50.1,50.2,50.3,50.4,50.5,50.6} },", + "\n new YieldCurve{ Currency = \"EUR\", Year = 2019, Month = 6, Name = \"A\", Values = new double[]{2.1,2.2,2.3,2.4,2.5,2.6} },", + "\n new YieldCurve{ Currency = \"EUR\", Year = 2019, Month = 6, Values = new double[]{40.1,40.2,40.3,40.4,40.5,40.6} },", + "\n new YieldCurve{ Currency = \"EUR\", Year = 2019, Month = 3, Name = \"A\", Values = new double[]{7.1,7.2,7.3,7.4,7.5,7.6} },", + "\n new YieldCurve{ Currency = \"EUR\", Year = 2019, Month = 3, Values = new double[]{30.1,30.2,30.3,30.4,30.5,30.6} },", "\n new YieldCurve{ Currency = \"EUR\", Year = 2018, Month = 6, Name = \"A\", Values = new double[]{3.1,3.2,3.3,3.4,3.5,3.6} },", + "\n new YieldCurve{ Currency = \"EUR\", Year = 2018, Month = 6, Values = new double[]{20.1,20.2,20.3,20.4,20.5,20.6} },", "\n new YieldCurve{ Currency = \"EUR\", Year = 2017, Month = 6, Name = \"A\", Values = new double[]{4.1,4.2,4.3,4.4,4.5,4.6} }, ", "\n new YieldCurve{ Currency = \"EUR\", Year = 2016, Month = 12, Name = \"A\", Values = new double[]{8.1,8.2,8.3,8.4,8.5,8.6} },", "\n new YieldCurve{ Currency = \"EUR\", Year = 2016, Month = 3, Name = \"A\", Values = new double[]{6.1,6.2,6.3,6.4,6.5,6.6} }};", "\n", "\n(int year , int month, string yieldCurveName, string valuationApproach) dataNodeTestData = (2016, 6, \"A\", \"BBA\");", - "\nvar activity = await CheckLoadYieldCurveAsync(args, testData, dataNodeTestData, 9.1, 12.1, 8.1);", + "\nvar activity = await CheckLoadYieldCurveAsync(args, testData, dataNodeTestData, 60.1, 90.1, 8.1);", "\nactivity" ], "metadata": {}, @@ -693,14 +739,56 @@ "cell_type": "code", "source": [ "var args = new Args(\"CH\",2020,9,Periodicity.Monthly,null);", - "\nvar testData = new YieldCurve[] {new YieldCurve{ Currency = \"EUR\", Year = 2020, Month = 6, Name = \"B\", Values = new double[]{0.1,0.2,0.3,0.4,0.5,0.6} }, ", + "\nvar testData = new YieldCurve[] {new YieldCurve{ Currency = \"EUR\", Year = 2020, Month = 6, Name = \"B\", Values = new double[]{0.1,0.2,0.3,0.4,0.5,0.6} },", + "\n new YieldCurve{ Currency = \"EUR\", Year = 2020, Month = 6, Values = new double[]{10.1,10.2,10.3,10.4,10.5,10.6} }, ", + "\n new YieldCurve{ Currency = \"EUR\", Year = 2020, Month = 6, Name = \"B\", Values = new double[]{2.1,2.2,2.3,2.4,2.5,2.6} }, ", + "\n new YieldCurve{ Currency = \"EUR\", Year = 2016, Month = 3, Name = \"C\", Values = new double[]{4.1,4.2,4.3,4.4,4.5,4.6} },", + "\n new YieldCurve{ Currency = \"EUR\", Year = 2016, Month = 3, Name = \"B\", Values = new double[]{6.1,6.2,6.3,6.4,6.5,6.6} }};", + "\n", + "\n(int year , int month, string yieldCurveName, string valuationApproach) dataNodeTestData = (2016, 2, \"A\", \"BBA\");", + "\n", + "\nvar activity = await CheckLoadYieldCurveAsync(args, testData, dataNodeTestData, 10.1, 10.1);", + "\nactivity" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "activity.Status.Should().Be(ActivityLogStatus.Failed);", + "\nactivity.Errors.Count().Should().Be(1)" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var expectedErrorMessage = Get(Error.YieldCurveNotFound, \"EUR\",\"2016\",\"12\",\"Best Estimate\",\"A\");", + "\nvar errorMessage = activity.Errors.First().ToString();", + "\nerrorMessage.Contains(expectedErrorMessage).Should().Be(true)" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var args = new Args(\"CH\",2016,9,Periodicity.Monthly,null);", + "\nvar testData = new YieldCurve[] {new YieldCurve{ Currency = \"EUR\", Year = 2020, Month = 6, Name = \"B\", Values = new double[]{0.1,0.2,0.3,0.4,0.5,0.6} },", + "\n new YieldCurve{ Currency = \"EUR\", Year = 2016, Month = 6, Values = new double[]{10.1,10.2,10.3,10.4,10.5,10.6} }, ", "\n new YieldCurve{ Currency = \"EUR\", Year = 2020, Month = 6, Name = \"B\", Values = new double[]{2.1,2.2,2.3,2.4,2.5,2.6} }, ", "\n new YieldCurve{ Currency = \"EUR\", Year = 2016, Month = 3, Name = \"C\", Values = new double[]{4.1,4.2,4.3,4.4,4.5,4.6} },", "\n new YieldCurve{ Currency = \"EUR\", Year = 2016, Month = 3, Name = \"B\", Values = new double[]{6.1,6.2,6.3,6.4,6.5,6.6} }};", "\n", "\n(int year , int month, string yieldCurveName, string valuationApproach) dataNodeTestData = (2016, 2, \"A\", \"BBA\");", "\n", - "\nvar activity = await CheckLoadYieldCurveAsync(args, testData, dataNodeTestData);" + "\nvar activity = await CheckLoadYieldCurveAsync(args, testData, dataNodeTestData, 10.1, 10.1);", + "\nactivity" ], "metadata": {}, "execution_count": 0, @@ -710,7 +798,18 @@ "cell_type": "code", "source": [ "activity.Status.Should().Be(ActivityLogStatus.Failed);", - "\nactivity.Errors.Select(x => x.ToString().Contains(Get(Error.YieldCurveNotFound)).Should().Be(true));" + "\nactivity.Errors.Count().Should().Be(1)" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var expectedErrorMessage = Get(Error.YieldCurveNotFound, \"EUR\",\"2016\",\"9\",\"Best Estimate\",\"A\");", + "\nvar errorMessage = activity.Errors.First().ToString();", + "\nerrorMessage.Contains(expectedErrorMessage).Should().Be(true)" ], "metadata": {}, "execution_count": 0, diff --git a/ifrs17/Utils/Queries.ipynb b/ifrs17/Utils/Queries.ipynb index 6fa652ed..e82f52b9 100644 --- a/ifrs17/Utils/Queries.ipynb +++ b/ifrs17/Utils/Queries.ipynb @@ -189,7 +189,8 @@ "\n var lockedInYieldCurveByGoc = new Dictionary();", "\n foreach (var dn in dataNodes.Where(x => x.ValuationApproach == ValuationApproaches.BBA))", "\n {", - "\n var argsNew = args with {Year = dn.Year, Month = MonthInAYear, Scenario = args.Scenario};", + "\n var monthUpperLimit = args.Year == dn.Year ? args.Month : MonthInAYear;", + "\n var argsNew = args with {Year = dn.Year, Month = monthUpperLimit, Scenario = args.Scenario};", "\n var loadedYc = (await querySource.LoadCurrentParameterAsync(argsNew, x => x.Currency, x => x.Currency == dn.ContractualCurrency && x.Name == dn.YieldCurveName));", "\n ", "\n if (!loadedYc.TryGetValue(dn.ContractualCurrency, out var lockedYc))", @@ -232,7 +233,7 @@ "\n x => x.Currency == key.ContractualCurrency ", "\n && (key.ValuationApproach == ValuationApproaches.VFA", "\n ? x.Name == key.YieldCurveName", - "\n : true));", + "\n : x.Name == (string)null));", "\n ", "\n if (!loadedYc.TryGetValue(key.ContractualCurrency, out var currentYcDict))", "\n ApplicationMessage.Log(Error.YieldCurveNotFound, key.ContractualCurrency, args.Year.ToString(), args.Month.ToString(), args.Scenario, key.YieldCurveName);", From 34b2a9ef2c09599616bc171fb3a29e39f0044414 Mon Sep 17 00:00:00 2001 From: Teo Kukuljan <116903719+tkukuljan@users.noreply.github.com> Date: Wed, 25 Jan 2023 14:31:43 +0100 Subject: [PATCH 4/9] Moving tests to engine (#143) * Adapting the tests (AocStructureTest, ImportStorageTest, ReportStorageTest, TechnicalMarginTest) so that they can be executed in the Engine. * Cleanup. * Enhancements - reducing import string sizes. * Optimizing data. * Bug fix, and updating the data to the newest changes in the develop. * Davide's feedback. * More Davide's feedback. * Importing entire ProjectionConfiguration instead. * Proper import of the CE * Solving an issue with yield curve changes. * better way of solving the issue with the yield curve change. * Removing commits, as the tests now run in memory. * Fixing a bugg in ReportStorageTest and making the tests run in the tests file * Fixing the amount types --- ifrs17-template/Test/AocStructureTest.ipynb | 152 +++---- ifrs17-template/Test/ImportStorageTest.ipynb | 133 +++--- ifrs17-template/Test/ReportStorageTest.ipynb | 46 +- .../Test/TechnicalMarginTest.ipynb | 96 ++++- ifrs17-template/Test/TestData.ipynb | 408 ++++++++++++++++++ 5 files changed, 688 insertions(+), 147 deletions(-) create mode 100644 ifrs17-template/Test/TestData.ipynb diff --git a/ifrs17-template/Test/AocStructureTest.ipynb b/ifrs17-template/Test/AocStructureTest.ipynb index b52e9153..c13eee40 100644 --- a/ifrs17-template/Test/AocStructureTest.ipynb +++ b/ifrs17-template/Test/AocStructureTest.ipynb @@ -29,7 +29,7 @@ { "cell_type": "code", "source": [ - "#!eval-notebook \"../Initialization/InitSystemorphBaseToMemory\"" + "#!import \"TestData\"" ], "metadata": {}, "execution_count": 0, @@ -47,18 +47,50 @@ { "cell_type": "code", "source": [ - "Workspace.Initialize(x => x.FromSource(DataSource)", - "\n .DisableInitialization()", - "\n .DisableInitialization());" + "await DataSource.SetAsync();", + "\nDataSource.Reset(x => x.ResetCurrentPartitions());" ], "metadata": {}, "execution_count": 0, "outputs": [] }, { - "cell_type": "markdown", + "cell_type": "code", "source": [ - "# Test" + "await Import.FromString(novelties).WithType().WithTarget(DataSource).ExecuteAsync();", + "\nawait Import.FromString(canonicalAocTypes).WithType().WithTarget(DataSource).ExecuteAsync();", + "\nawait Import.FromString(canonicalAocConfig).WithFormat(ImportFormats.AocConfiguration).WithTarget(DataSource).ExecuteAsync();" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "await DataSource.UpdateAsync(dt1.RepeatOnce());", + "\nawait DataSource.UpdateAsync(dtr1.RepeatOnce());", + "\nawait DataSource.UpdateAsync(new [] {dt11});", + "\nawait DataSource.UpdateAsync(new [] {dtr11});" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "await DataSource.UpdateAsync(new [ ] {dt11State,dtr11State});", + "\nawait DataSource.UpdateAsync(new [ ] {dt11Inter});" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "await Import.FromString(estimateType).WithType().WithTarget(DataSource).ExecuteAsync();" ], "metadata": {}, "execution_count": 0, @@ -67,8 +99,7 @@ { "cell_type": "code", "source": [ - "var reportingNode = \"CH\";", - "\nvar scenario = (string)null;" + "await DataSource.UpdateAsync(new [ ] {yieldCurvePrevious});" ], "metadata": {}, "execution_count": 0, @@ -77,15 +108,26 @@ { "cell_type": "code", "source": [ - "//Define partition", - "\nvar args = new ImportArgs(reportingNode, 2021, 3, Periodicity.Quarterly, scenario, ImportFormats.Actual);", - "\nvar partition = new PartitionByReportingNodeAndPeriod { Id = (Guid)(await DataSource.Partition.GetKeyForInstanceAsync(args)),", - "\n ReportingNode = reportingNode, ", - "\n Scenario = scenario, ", - "\n Year = args.Year,", - "\n Month = args.Month };", - "\nawait DataSource.UpdateAsync(new[]{partition});", - "\nawait DataSource.CommitAsync();" + "Workspace.Initialize(x => x.FromSource(DataSource).DisableInitialization().DisableInitialization());" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "await DataSource.UpdateAsync(new[]{partition, previousPeriodPartition});", + "\nawait DataSource.UpdateAsync(new[]{partitionReportingNode});" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Test" ], "metadata": {}, "execution_count": 0, @@ -259,24 +301,15 @@ "execution_count": 0, "outputs": [] }, - { - "cell_type": "code", - "source": [ - "var groupOfContract = \"DT1.1\";" - ], - "metadata": {}, - "execution_count": 0, - "outputs": [] - }, { "cell_type": "code", "source": [ "var inputRawVariables = new RawVariable[]{", - "\n new RawVariable{Partition = partition.Id, Values = new []{1.0}, DataNode = groupOfContract, AocType = \"BOP\", Novelty = \"I\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\"},", - "\n new RawVariable{Partition = partition.Id, Values = new []{1.0}, DataNode = groupOfContract, AocType = \"MC\", Novelty = \"I\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\"},", - "\n new RawVariable{Partition = partition.Id, Values = new []{1.0}, DataNode = groupOfContract, AocType = \"BOP\", Novelty = \"N\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\"},", - "\n new RawVariable{Partition = partition.Id, Values = new []{1.0}, DataNode = groupOfContract, AocType = \"EV\", Novelty = \"N\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\"},", - "\n new RawVariable{Partition = partition.Id, Values = new []{1.0}, DataNode = groupOfContract, AocType = \"CL\", Novelty = \"C\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\"},", + "\n new RawVariable{Partition = partition.Id, Values = new []{1.0}, DataNode = groupOfInsuranceContracts, AocType = \"BOP\", Novelty = \"I\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\"},", + "\n new RawVariable{Partition = partition.Id, Values = new []{1.0}, DataNode = groupOfInsuranceContracts, AocType = \"MC\", Novelty = \"I\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\"},", + "\n new RawVariable{Partition = partition.Id, Values = new []{1.0}, DataNode = groupOfInsuranceContracts, AocType = \"BOP\", Novelty = \"N\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\"},", + "\n new RawVariable{Partition = partition.Id, Values = new []{1.0}, DataNode = groupOfInsuranceContracts, AocType = \"EV\", Novelty = \"N\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\"},", + "\n new RawVariable{Partition = partition.Id, Values = new []{1.0}, DataNode = groupOfInsuranceContracts, AocType = \"CL\", Novelty = \"C\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\"},", "\n};" ], "metadata": {}, @@ -354,7 +387,9 @@ "\n new AocStep(\"EA\",\"C\"), new AocStep(\"AM\",\"C\"), new AocStep(\"CL\",\"C\"),", "\n }},", "\n //{new AocStep(\"CF\",\"C\"), Enumerable.Empty()},", - "\n };" + "\n };", + "\nvar activity = await CheckAocStepStructureAsync(inputRawVariables, parentBm, referenceBm, fullAocBm);", + "\nactivity" ], "metadata": {}, "execution_count": 0, @@ -363,7 +398,7 @@ { "cell_type": "code", "source": [ - "await CheckAocStepStructureAsync(inputRawVariables, parentBm, referenceBm, fullAocBm)" + "activity.Status.Should().Be(ActivityLogStatus.Succeeded);" ], "metadata": {}, "execution_count": 0, @@ -378,24 +413,15 @@ "execution_count": 0, "outputs": [] }, - { - "cell_type": "code", - "source": [ - "groupOfContract = \"DTR1.1\";" - ], - "metadata": {}, - "execution_count": 0, - "outputs": [] - }, { "cell_type": "code", "source": [ "inputRawVariables = new RawVariable[]{", - "\n new RawVariable{Partition = partition.Id, Values = new []{1.0}, DataNode = groupOfContract, AocType = \"BOP\", Novelty = \"I\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\"},", - "\n new RawVariable{Partition = partition.Id, Values = new []{1.0}, DataNode = groupOfContract, AocType = \"MC\", Novelty = \"I\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\"},", - "\n new RawVariable{Partition = partition.Id, Values = new []{1.0}, DataNode = groupOfContract, AocType = \"BOP\", Novelty = \"N\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\"},", - "\n new RawVariable{Partition = partition.Id, Values = new []{1.0}, DataNode = groupOfContract, AocType = \"EV\", Novelty = \"N\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\"},", - "\n new RawVariable{Partition = partition.Id, Values = new []{1.0}, DataNode = groupOfContract, AocType = \"CL\", Novelty = \"C\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\"},", + "\n new RawVariable{Partition = partition.Id, Values = new []{1.0}, DataNode = groupOfReinsuranceContracts, AocType = \"BOP\", Novelty = \"I\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\"},", + "\n new RawVariable{Partition = partition.Id, Values = new []{1.0}, DataNode = groupOfReinsuranceContracts, AocType = \"MC\", Novelty = \"I\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\"},", + "\n new RawVariable{Partition = partition.Id, Values = new []{1.0}, DataNode = groupOfReinsuranceContracts, AocType = \"BOP\", Novelty = \"N\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\"},", + "\n new RawVariable{Partition = partition.Id, Values = new []{1.0}, DataNode = groupOfReinsuranceContracts, AocType = \"EV\", Novelty = \"N\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\"},", + "\n new RawVariable{Partition = partition.Id, Values = new []{1.0}, DataNode = groupOfReinsuranceContracts, AocType = \"CL\", Novelty = \"C\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\"},", "\n};" ], "metadata": {}, @@ -529,22 +555,13 @@ "execution_count": 0, "outputs": [] }, - { - "cell_type": "code", - "source": [ - "var groupOfContract = \"DT1.1\";" - ], - "metadata": {}, - "execution_count": 0, - "outputs": [] - }, { "cell_type": "code", "source": [ "inputRawVariables = new RawVariable[]{", - "\n new RawVariable{Partition = partition.Id, Values = new []{1.0}, DataNode = groupOfContract, AocType = \"BOP\", Novelty = \"I\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\"},", - "\n new RawVariable{Partition = partition.Id, Values = new []{1.0}, DataNode = groupOfContract, AocType = \"MC\", Novelty = \"I\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\"},", - "\n new RawVariable{Partition = partition.Id, Values = new []{1.0}, DataNode = groupOfContract, AocType = \"CL\", Novelty = \"C\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\"},", + "\n new RawVariable{Partition = partition.Id, Values = new []{1.0}, DataNode = groupOfInsuranceContracts, AocType = \"BOP\", Novelty = \"I\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\"},", + "\n new RawVariable{Partition = partition.Id, Values = new []{1.0}, DataNode = groupOfInsuranceContracts, AocType = \"MC\", Novelty = \"I\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\"},", + "\n new RawVariable{Partition = partition.Id, Values = new []{1.0}, DataNode = groupOfInsuranceContracts, AocType = \"CL\", Novelty = \"C\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\"},", "\n};" ], "metadata": {}, @@ -641,23 +658,14 @@ "execution_count": 0, "outputs": [] }, - { - "cell_type": "code", - "source": [ - "var groupOfContract = \"DT1.1\";" - ], - "metadata": {}, - "execution_count": 0, - "outputs": [] - }, { "cell_type": "code", "source": [ "var inputIfrsVariables = new IfrsVariable[]{", - "\n new IfrsVariable{Partition = partition.Id, Value = 1.0, DataNode = groupOfContract, AocType = \"BOP\", Novelty = \"C\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"AA\"},", - "\n new IfrsVariable{Partition = partition.Id, Value = 1.0, DataNode = groupOfContract, AocType = \"CF\", Novelty = \"C\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"AA\"},", - "\n new IfrsVariable{Partition = partition.Id, Value = 1.0, DataNode = groupOfContract, AocType = \"CF\", Novelty = \"C\", AccidentYear = null, AmountType = \"ACA\", EstimateType = \"A\"},", - "\n new IfrsVariable{Partition = partition.Id, Value = 1.0, DataNode = groupOfContract, AocType = \"WO\", Novelty = \"C\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"OA\"},", + "\n new IfrsVariable{Partition = partition.Id, Value = 1.0, DataNode = groupOfInsuranceContracts, AocType = \"BOP\", Novelty = \"C\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"AA\"},", + "\n new IfrsVariable{Partition = partition.Id, Value = 1.0, DataNode = groupOfInsuranceContracts, AocType = \"CF\", Novelty = \"C\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"AA\"},", + "\n new IfrsVariable{Partition = partition.Id, Value = 1.0, DataNode = groupOfInsuranceContracts, AocType = \"CF\", Novelty = \"C\", AccidentYear = null, AmountType = \"ACA\", EstimateType = \"A\"},", + "\n new IfrsVariable{Partition = partition.Id, Value = 1.0, DataNode = groupOfInsuranceContracts, AocType = \"WO\", Novelty = \"C\", AccidentYear = null, AmountType = \"PR\", EstimateType = \"OA\"},", "\n};" ], "metadata": {}, diff --git a/ifrs17-template/Test/ImportStorageTest.ipynb b/ifrs17-template/Test/ImportStorageTest.ipynb index 73c83161..985ef965 100644 --- a/ifrs17-template/Test/ImportStorageTest.ipynb +++ b/ifrs17-template/Test/ImportStorageTest.ipynb @@ -29,7 +29,7 @@ { "cell_type": "code", "source": [ - "#!eval-notebook \"../Initialization/InitSystemorphBaseToMemory\"" + "#!import \"TestData\"" ], "metadata": {}, "execution_count": 0, @@ -47,18 +47,19 @@ { "cell_type": "code", "source": [ - "Workspace.Initialize(x => x.FromSource(DataSource)", - "\n .DisableInitialization()", - "\n .DisableInitialization());" + "await DataSource.SetAsync();", + "\nDataSource.Reset(x => x.ResetCurrentPartitions());" ], "metadata": {}, "execution_count": 0, "outputs": [] }, { - "cell_type": "markdown", + "cell_type": "code", "source": [ - "# Test IfrsVariables Query" + "await Import.FromString(novelties).WithType().WithTarget(DataSource).ExecuteAsync();", + "\nawait Import.FromString(canonicalAocTypes).WithType().WithTarget(DataSource).ExecuteAsync();", + "\nawait Import.FromString(canonicalAocConfig).WithFormat(ImportFormats.AocConfiguration).WithTarget(DataSource).ExecuteAsync();" ], "metadata": {}, "execution_count": 0, @@ -67,10 +68,10 @@ { "cell_type": "code", "source": [ - "var gic = \"DT1.1\";", - "\nvar gric = \"DTR1.1\";", - "\nvar reportingNode = \"CH\";", - "\nvar scenario = (string)null;" + "await DataSource.UpdateAsync(dt1.RepeatOnce());", + "\nawait DataSource.UpdateAsync(dtr1.RepeatOnce());", + "\nawait DataSource.UpdateAsync(new [] {dt11});", + "\nawait DataSource.UpdateAsync(new [] {dtr11});" ], "metadata": {}, "execution_count": 0, @@ -79,21 +80,54 @@ { "cell_type": "code", "source": [ - "//Define partition", - "\nvar args = new ImportArgs(reportingNode, 2021, 3, Periodicity.Quarterly, scenario, ImportFormats.Actual);", - "\nvar previousArgs = new ImportArgs(reportingNode, 2020, 12, Periodicity.Quarterly, scenario, ImportFormats.Actual);", - "\nvar partition = new PartitionByReportingNodeAndPeriod { Id = (Guid)(await DataSource.Partition.GetKeyForInstanceAsync(args)),", - "\n ReportingNode = reportingNode, ", - "\n Scenario = scenario, ", - "\n Year = args.Year,", - "\n Month = args.Month };", - "\nvar previousPeriodPartition = new PartitionByReportingNodeAndPeriod { Id = (Guid)(await DataSource.Partition.GetKeyForInstanceAsync(previousArgs)),", - "\n ReportingNode = reportingNode, ", - "\n Scenario = scenario, ", - "\n Year = previousArgs.Year,", - "\n Month = previousArgs.Month };", - "\nawait DataSource.UpdateAsync(new[]{partition, previousPeriodPartition});", - "\nawait DataSource.CommitAsync();" + "await DataSource.UpdateAsync(new [ ] {dt11State, dtr11State});", + "\nawait DataSource.UpdateAsync(new [ ] {dt11Inter});" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "await DataSource.UpdateAsync(new [ ] {yieldCurve, yieldCurvePrevious});" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "await Import.FromString(estimateType).WithType().WithTarget(DataSource).ExecuteAsync();" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "Workspace.Initialize(x => x.FromSource(DataSource).DisableInitialization().DisableInitialization());" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "await DataSource.UpdateAsync(new[]{partition, previousPeriodPartition});", + "\nawait DataSource.UpdateAsync(new[]{partitionReportingNode});" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Test" ], "metadata": {}, "execution_count": 0, @@ -144,7 +178,7 @@ "cell_type": "code", "source": [ "//There is a previous year, and it's the first time Actuals are imported for the current year", - "\nvar basicIfrsVariable = new IfrsVariable{Partition = partition.Id, DataNode = gic, AccidentYear = null, AmountType = AmountTypes.PR, Novelty = Novelties.C, EstimateType = EstimateTypes.AA};", + "\nvar basicIfrsVariable = new IfrsVariable{Partition = partition.Id, DataNode = groupOfInsuranceContracts, AccidentYear = null, AmountType = AmountTypes.PR, Novelty = Novelties.C, EstimateType = EstimateTypes.AA};", "\nvar inputDataSetForDataSource = new IfrsVariable[]{", "\n basicIfrsVariable with {Partition = previousPeriodPartition.Id, AocType = AocTypes.EOP, Value = 100.0},", "\n basicIfrsVariable with {Partition = previousPeriodPartition.Id, AocType = AocTypes.EOP, EstimateType = EstimateTypes.DA, Value = 1000.0},", @@ -161,7 +195,6 @@ "\n basicIfrsVariable with {AocType = AocTypes.CF, Value = -10.0},", "\n basicIfrsVariable with {AocType = AocTypes.WO, Value = 15.0},", "\n };", - "\n", "\nvar activity = await CheckIfrsVariablesFromImportStorageAsync(ImportFormats.Actual, inputDataSetForWorkspace, inputDataSetForDataSource, ivsBenchmark);", "\nactivity" ], @@ -182,7 +215,7 @@ "cell_type": "code", "source": [ "//There is a previous year, and it's the second time Actuals are imported for the current year (reimport)", - "\nvar basicIfrsVariable = new IfrsVariable{Partition = partition.Id, DataNode = gic, AccidentYear = null, AmountType = AmountTypes.PR, Novelty = Novelties.C, EstimateType = EstimateTypes.AA};", + "\nvar basicIfrsVariable = new IfrsVariable{Partition = partition.Id, DataNode = groupOfInsuranceContracts, AccidentYear = null, AmountType = AmountTypes.PR, Novelty = Novelties.C, EstimateType = EstimateTypes.AA};", "\nvar inputDataSetForDataSource = new IfrsVariable[]{", "\n basicIfrsVariable with {Partition = previousPeriodPartition.Id, AocType = AocTypes.EOP, Value = 100.0},", "\n basicIfrsVariable with {Partition = previousPeriodPartition.Id, AocType = AocTypes.EOP, EstimateType = EstimateTypes.DA, Value = 1000.0},", @@ -225,7 +258,7 @@ "cell_type": "code", "source": [ "//There is a previous year, Cash flows were already imported and Actuals are imported for the first time", - "\nvar basicIfrsVariable = new IfrsVariable{Partition = partition.Id, DataNode = gic, AccidentYear = null, AmountType = AmountTypes.PR, Novelty = Novelties.I, EstimateType = EstimateTypes.BE};", + "\nvar basicIfrsVariable = new IfrsVariable{Partition = partition.Id, DataNode = groupOfInsuranceContracts, AccidentYear = null, AmountType = AmountTypes.PR, Novelty = Novelties.I, EstimateType = EstimateTypes.BE};", "\nvar inputDataSetForDataSource = new IfrsVariable[]{", "\n basicIfrsVariable with {Partition = previousPeriodPartition.Id, AocType = AocTypes.EOP, EstimateType = EstimateTypes.AA, Novelty = Novelties.C, Value = 100.0},", "\n basicIfrsVariable with {Partition = previousPeriodPartition.Id, AocType = AocTypes.EOP, Value = 1000.0},", @@ -270,8 +303,8 @@ "cell_type": "code", "source": [ "//There is a previous year, Cash flows and Actuals were already imported and Actuals are imported again", - "\nvar basicAdvanceActualIfrsVariable = new IfrsVariable{Partition = partition.Id, DataNode = gic, AccidentYear = null, AmountType = AmountTypes.PR, Novelty = Novelties.C, EstimateType = EstimateTypes.AA};", - "\nvar basicBeIfrsVariable = new IfrsVariable{Partition = partition.Id, DataNode = gic, AccidentYear = null, AmountType = AmountTypes.PR, Novelty = Novelties.I, EstimateType = EstimateTypes.BE};", + "\nvar basicAdvanceActualIfrsVariable = new IfrsVariable{Partition = partition.Id, DataNode = groupOfInsuranceContracts, AccidentYear = null, AmountType = AmountTypes.PR, Novelty = Novelties.C, EstimateType = EstimateTypes.AA};", + "\nvar basicBeIfrsVariable = new IfrsVariable{Partition = partition.Id, DataNode = groupOfInsuranceContracts, AccidentYear = null, AmountType = AmountTypes.PR, Novelty = Novelties.I, EstimateType = EstimateTypes.BE};", "\nvar inputDataSetForDataSource = new IfrsVariable[]{", "\n basicBeIfrsVariable with {Partition = previousPeriodPartition.Id, AocType = AocTypes.EOP, Value = 1000.0},", "\n basicBeIfrsVariable with {AocType = AocTypes.BOP, Novelty = Novelties.I, Value = 1000.0},", @@ -326,19 +359,19 @@ "\n//There is a previous and current year for Secondary Scope (GIC10), ", "\n//It's the first time Actuals are imported for the current year", "\n", - "\nvar basicIfrsVariable = new IfrsVariable{Partition = partition.Id, DataNode = gric, AccidentYear = null, AmountType = AmountTypes.PR, Novelty = Novelties.C, EstimateType = EstimateTypes.AA};", + "\nvar basicIfrsVariable = new IfrsVariable{Partition = partition.Id, DataNode = groupOfReinsuranceContracts, AccidentYear = null, AmountType = AmountTypes.PR, Novelty = Novelties.C, EstimateType = EstimateTypes.AA};", "\nvar inputDataSetForDataSource = new IfrsVariable[]{", "\n //Year", "\n basicIfrsVariable with {Partition = previousPeriodPartition.Id, AocType = AocTypes.EOP, Value = 100.0},", "\n basicIfrsVariable with {Partition = previousPeriodPartition.Id, AocType = AocTypes.EOP, EstimateType = EstimateTypes.DA, Value = 1000.0},", - "\n basicIfrsVariable with {Partition = previousPeriodPartition.Id, AocType = AocTypes.EOP, DataNode = gic, EstimateType = EstimateTypes.DA, Value = 1000.0},", - "\n basicIfrsVariable with {Partition = previousPeriodPartition.Id, AocType = AocTypes.EOP, DataNode = gic, EstimateType = EstimateTypes.BE, EconomicBasis = EconomicBases.C, Value = 1000.0},", - "\n basicIfrsVariable with {Partition = previousPeriodPartition.Id, AocType = AocTypes.CL, DataNode = gic, EstimateType = EstimateTypes.BE, EconomicBasis = EconomicBases.C, Value = 666.0},", - "\n basicIfrsVariable with {Partition = previousPeriodPartition.Id, AocType = AocTypes.EOP, DataNode = gic, EstimateType = EstimateTypes.BE, EconomicBasis = EconomicBases.L, Value = 1000.0},", - "\n basicIfrsVariable with {Partition = previousPeriodPartition.Id, AocType = AocTypes.CL, DataNode = gic, EstimateType = EstimateTypes.BE, EconomicBasis = EconomicBases.L, Value = 666.0},", + "\n basicIfrsVariable with {Partition = previousPeriodPartition.Id, AocType = AocTypes.EOP, DataNode = groupOfInsuranceContracts, EstimateType = EstimateTypes.DA, Value = 1000.0},", + "\n basicIfrsVariable with {Partition = previousPeriodPartition.Id, AocType = AocTypes.EOP, DataNode = groupOfInsuranceContracts, EstimateType = EstimateTypes.BE, EconomicBasis = EconomicBases.C, Value = 1000.0},", + "\n basicIfrsVariable with {Partition = previousPeriodPartition.Id, AocType = AocTypes.CL, DataNode = groupOfInsuranceContracts, EstimateType = EstimateTypes.BE, EconomicBasis = EconomicBases.C, Value = 666.0},", + "\n basicIfrsVariable with {Partition = previousPeriodPartition.Id, AocType = AocTypes.EOP, DataNode = groupOfInsuranceContracts, EstimateType = EstimateTypes.BE, EconomicBasis = EconomicBases.L, Value = 1000.0},", + "\n basicIfrsVariable with {Partition = previousPeriodPartition.Id, AocType = AocTypes.CL, DataNode = groupOfInsuranceContracts, EstimateType = EstimateTypes.BE, EconomicBasis = EconomicBases.L, Value = 666.0},", "\n //Year -1", - "\n basicIfrsVariable with {AocType = AocTypes.IA, Novelty = Novelties.I, DataNode = gic, EstimateType = EstimateTypes.BE, EconomicBasis = EconomicBases.L, Value = 10.0},", - "\n basicIfrsVariable with {AocType = AocTypes.CF, Novelty = Novelties.I, DataNode = gic, EstimateType = EstimateTypes.BE, EconomicBasis = EconomicBases.L, Value = 10.0},", + "\n basicIfrsVariable with {AocType = AocTypes.IA, Novelty = Novelties.I, DataNode = groupOfInsuranceContracts, EstimateType = EstimateTypes.BE, EconomicBasis = EconomicBases.L, Value = 10.0},", + "\n basicIfrsVariable with {AocType = AocTypes.CF, Novelty = Novelties.I, DataNode = groupOfInsuranceContracts, EstimateType = EstimateTypes.BE, EconomicBasis = EconomicBases.L, Value = 10.0},", "\n };", "\n", "\nvar inputDataSetForWorkspace = new IfrsVariable[]{", @@ -351,15 +384,15 @@ "\n //Actuals", "\n basicIfrsVariable with {AocType = AocTypes.BOP, Novelty = Novelties.I, EstimateType = EstimateTypes.DA, Value = 1000.0},", "\n basicIfrsVariable with {AocType = AocTypes.BOP, Novelty = Novelties.I, Value = 100.0},", - "\n basicIfrsVariable with {AocType = AocTypes.BOP, Novelty = Novelties.I, DataNode = gic, EstimateType = EstimateTypes.DA, Value = 1000.0},", + "\n basicIfrsVariable with {AocType = AocTypes.BOP, Novelty = Novelties.I, DataNode = groupOfInsuranceContracts, EstimateType = EstimateTypes.DA, Value = 1000.0},", "\n //Cash flow", - "\n basicIfrsVariable with {AocType = AocTypes.BOP, Novelty = Novelties.I, DataNode = gic, EstimateType = EstimateTypes.BE, EconomicBasis = EconomicBases.L, Value = 1000.0},", - "\n basicIfrsVariable with {AocType = AocTypes.BOP, Novelty = Novelties.I, DataNode = gic, EstimateType = EstimateTypes.BE, EconomicBasis = EconomicBases.C, Value = 1000.0},", + "\n basicIfrsVariable with {AocType = AocTypes.BOP, Novelty = Novelties.I, DataNode = groupOfInsuranceContracts, EstimateType = EstimateTypes.BE, EconomicBasis = EconomicBases.L, Value = 1000.0},", + "\n basicIfrsVariable with {AocType = AocTypes.BOP, Novelty = Novelties.I, DataNode = groupOfInsuranceContracts, EstimateType = EstimateTypes.BE, EconomicBasis = EconomicBases.C, Value = 1000.0},", "\n ", "\n //From current Period", "\n //from DB", - "\n basicIfrsVariable with {AocType = AocTypes.IA, Novelty = Novelties.I, DataNode = gic, EstimateType = EstimateTypes.BE, EconomicBasis = EconomicBases.L, Value = 10.0},", - "\n basicIfrsVariable with {AocType = AocTypes.CF, Novelty = Novelties.I, DataNode = gic, EstimateType = EstimateTypes.BE, EconomicBasis = EconomicBases.L, Value = 10.0},", + "\n basicIfrsVariable with {AocType = AocTypes.IA, Novelty = Novelties.I, DataNode = groupOfInsuranceContracts, EstimateType = EstimateTypes.BE, EconomicBasis = EconomicBases.L, Value = 10.0},", + "\n basicIfrsVariable with {AocType = AocTypes.CF, Novelty = Novelties.I, DataNode = groupOfInsuranceContracts, EstimateType = EstimateTypes.BE, EconomicBasis = EconomicBases.L, Value = 10.0},", "\n ", "\n //from workspace", "\n basicIfrsVariable with {AocType = AocTypes.CF, Value = -10.0},", @@ -427,15 +460,15 @@ "cell_type": "code", "source": [ "var sampleGic = new GroupOfInsuranceContract(){Portfolio = \"P1\"};", - "\nvar inputDataGic = new GroupOfInsuranceContract[]{ sampleGic with {SystemName = \"Gross1\", LiabilityType = LiabilityTypes.LRC, ContractualCurrency = \"CHF\", ValuationApproach = \"BBA\"},", - "\n sampleGic with {SystemName = \"Gross2\", LiabilityType = LiabilityTypes.LRC, ContractualCurrency = \"CHF\", ValuationApproach = \"BBA\"},", - "\n sampleGic with {SystemName = \"Gross3\", LiabilityType = LiabilityTypes.LRC, ContractualCurrency = \"CHF\", ValuationApproach = \"BBA\"},", + "\nvar inputDataGic = new GroupOfInsuranceContract[]{ sampleGic with {SystemName = \"Gross1\", LiabilityType = LiabilityTypes.LRC, ContractualCurrency = \"USD\", ValuationApproach = \"BBA\"},", + "\n sampleGic with {SystemName = \"Gross2\", LiabilityType = LiabilityTypes.LRC, ContractualCurrency = \"USD\", ValuationApproach = \"BBA\"},", + "\n sampleGic with {SystemName = \"Gross3\", LiabilityType = LiabilityTypes.LRC, ContractualCurrency = \"USD\", ValuationApproach = \"BBA\"},", "\n };", "\n", "\nvar sampleGric = new GroupOfReinsuranceContract(){Portfolio = \"ReP1\"};", - "\nvar inputDataGric = new GroupOfReinsuranceContract[]{ sampleGric with {SystemName = \"Reins1\", LiabilityType = LiabilityTypes.LRC, ContractualCurrency = \"CHF\", ValuationApproach = \"BBA\"},", - "\n sampleGric with {SystemName = \"Reins2\", LiabilityType = LiabilityTypes.LRC, ContractualCurrency = \"CHF\", ValuationApproach = \"BBA\"},", - "\n sampleGric with {SystemName = \"Reins3\", LiabilityType = LiabilityTypes.LRC, ContractualCurrency = \"CHF\", ValuationApproach = \"BBA\"},", + "\nvar inputDataGric = new GroupOfReinsuranceContract[]{ sampleGric with {SystemName = \"Reins1\", LiabilityType = LiabilityTypes.LRC, ContractualCurrency = \"USD\", ValuationApproach = \"BBA\"},", + "\n sampleGric with {SystemName = \"Reins2\", LiabilityType = LiabilityTypes.LRC, ContractualCurrency = \"USD\", ValuationApproach = \"BBA\"},", + "\n sampleGric with {SystemName = \"Reins3\", LiabilityType = LiabilityTypes.LRC, ContractualCurrency = \"USD\", ValuationApproach = \"BBA\"},", "\n };", "\nvar sampleDnState = new DataNodeState {Partition = reportingNodePartition.Id, Year = args.Year, Month = args.Month, State = State.Active};", "\nvar inputDataState = new DataNodeState[]{ sampleDnState with {DataNode = \"Gross1\"} ,", diff --git a/ifrs17-template/Test/ReportStorageTest.ipynb b/ifrs17-template/Test/ReportStorageTest.ipynb index 0f49a5d2..21d410fb 100644 --- a/ifrs17-template/Test/ReportStorageTest.ipynb +++ b/ifrs17-template/Test/ReportStorageTest.ipynb @@ -20,7 +20,7 @@ { "cell_type": "code", "source": [ - "#!eval-notebook \"../Initialization/InitSystemorphBaseToMemory\"" + "#!import \"TestData\"" ], "metadata": {}, "execution_count": 0, @@ -29,7 +29,7 @@ { "cell_type": "markdown", "source": [ - "# Test Exchange Rate Query" + "# Initialization" ], "metadata": {}, "execution_count": 0, @@ -38,8 +38,35 @@ { "cell_type": "code", "source": [ - "Workspace.Initialize(x => x.FromSource(DataSource)", - "\n .DisableInitialization());" + "await DataSource.SetAsync();", + "\nDataSource.Reset(x => x.ResetCurrentPartitions());" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "await DataSource.UpdateAsync(reportingNodeG.RepeatOnce());" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "Workspace.Initialize(x => x.FromSource(DataSource).DisableInitialization());" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Test" ], "metadata": {}, "execution_count": 0, @@ -86,7 +113,16 @@ "\n new ExchangeRate{ Currency = \"USD\", Year = 2020, Month = 12, FxType = FxType.Spot, FxToGroupCurrency = 5 },", "\n new ExchangeRate{ Currency = \"USD\", Year = 2021, Month = 6, FxType = FxType.Average, FxToGroupCurrency = 2 },", "\n new ExchangeRate{ Currency = \"USD\", Year = 2021, Month = 6, FxType = FxType.Spot, FxToGroupCurrency = 0.5 }};", - "\nvar activity = await CheckGetFx(\"EUR\", \"USD\", 2021, 6, testData, 2, 10, 60);", + "\n" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var activity = await CheckGetFx(\"EUR\", \"USD\", 2021, 6, testData, 2, 10, 60);", "\nactivity" ], "metadata": {}, diff --git a/ifrs17-template/Test/TechnicalMarginTest.ipynb b/ifrs17-template/Test/TechnicalMarginTest.ipynb index 8d0d4f34..537208b5 100644 --- a/ifrs17-template/Test/TechnicalMarginTest.ipynb +++ b/ifrs17-template/Test/TechnicalMarginTest.ipynb @@ -29,7 +29,7 @@ { "cell_type": "code", "source": [ - "#!eval-notebook \"../Initialization/InitSystemorphBaseToMemory\"" + "#!import \"TestData\"" ], "metadata": {}, "execution_count": 0, @@ -47,18 +47,31 @@ { "cell_type": "code", "source": [ - "Workspace.Initialize(x => x.FromSource(DataSource)", - "\n .DisableInitialization()", - "\n .DisableInitialization());" + "await DataSource.SetAsync();", + "\nDataSource.Reset(x => x.ResetCurrentPartitions());" ], "metadata": {}, "execution_count": 0, "outputs": [] }, { - "cell_type": "markdown", + "cell_type": "code", + "source": [ + "await Import.FromString(novelties).WithType().WithTarget(DataSource).ExecuteAsync();", + "\nawait Import.FromString(canonicalAocTypes).WithType().WithTarget(DataSource).ExecuteAsync();", + "\nawait Import.FromString(canonicalAocConfig).WithFormat(ImportFormats.AocConfiguration).WithTarget(DataSource).ExecuteAsync();" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", "source": [ - "# Test switch logic" + "await DataSource.UpdateAsync(dt1.RepeatOnce());", + "\nawait DataSource.UpdateAsync(new[]{dt11});", + "\nawait DataSource.UpdateAsync(new[]{dt11State});", + "\nawait DataSource.UpdateAsync(new[]{dt11SingleParameter});" ], "metadata": {}, "execution_count": 0, @@ -67,8 +80,8 @@ { "cell_type": "code", "source": [ - "//define group of contract", - "\nvar groupOfContract = \"DT1.1\";" + "await Import.FromString(amountType).WithType().WithTarget(DataSource).ExecuteAsync();", + "\nawait Import.FromString(estimateType).WithType().WithTarget(DataSource).ExecuteAsync();" ], "metadata": {}, "execution_count": 0, @@ -77,11 +90,44 @@ { "cell_type": "code", "source": [ - "//Define partition", - "\n var args = new ImportArgs(\"CH\", 2020, 12, Periodicity.Quarterly, null, ImportFormats.Actual);", - "\n var partition = Workspace.Query().FirstOrDefault(x => x.ReportingNode == args.ReportingNode && x.Year == args.Year && ", - "\n x.Month == args.Month && x.Scenario == args.Scenario);", - "\n if(partition == null) ApplicationMessage.Log(Error.PartitionNotFound);" + "await Import.FromString(projectionConfiguration).WithType().WithTarget(DataSource).ExecuteAsync();" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "await DataSource.UpdateAsync(new [ ] {yieldCurve, yieldCurvePrevious});" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "Workspace.Initialize(x => x.FromSource(DataSource).DisableInitialization().DisableInitialization());" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "await DataSource.UpdateAsync(previousPeriodPartition.RepeatOnce());", + "\nawait DataSource.UpdateAsync(partitionReportingNode.RepeatOnce());" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Test" ], "metadata": {}, "execution_count": 0, @@ -97,11 +143,11 @@ "\n await Workspace.UpdateAsync(inputDataSet);", "\n ", "\n //Set up import storage and test universe", - "\n var testStorage = new ImportStorage(args, DataSource, Workspace);", + "\n var testStorage = new ImportStorage(previousArgs, DataSource, Workspace);", "\n await testStorage.InitializeAsync();", "\n var testUniverse = Scopes.ForStorage(testStorage).ToScope();", "\n ", - "\n var identities = testUniverse.GetScopes(testStorage.DataNodesByImportScope[ImportScope.Primary].Where(dn => dn == groupOfContract)).SelectMany(s => s.Identities);", + "\n var identities = testUniverse.GetScopes(testStorage.DataNodesByImportScope[ImportScope.Primary].Where(dn => dn == groupOfInsuranceContracts)).SelectMany(s => s.Identities);", "\n var csm = testUniverse.GetScopes(identities, o => o.WithContext(\"C\")).Where(x => Math.Abs(x.Value) > Precision);", "\n var lc = testUniverse.GetScopes(identities, o => o.WithContext(\"L\")).Where(x => Math.Abs(x.Value) > Precision);", "\n //Clean up Workspace", @@ -165,7 +211,7 @@ { "cell_type": "code", "source": [ - "var basicIfrsVariable = new IfrsVariable{Partition = partition.Id, DataNode = groupOfContract, AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\", EconomicBasis = \"L\"};", + "var basicIfrsVariable = new IfrsVariable{Partition = previousPeriodPartition.Id, DataNode = groupOfInsuranceContracts, AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\", EconomicBasis = \"L\"};", "\nvar inputDataSet = new IfrsVariable[]{", "\n basicIfrsVariable with {AocType = \"BOP\", Novelty = \"N\", Value = 100.0},", "\n basicIfrsVariable with {AocType = \"CF\", Novelty = \"N\", Value = -10.0},", @@ -186,7 +232,17 @@ "\n {new AocStep(\"EOP\",\"C\"),(0d, 153.5)},", "\n };", "\n", - "\nawait CheckSwitchLogicAsync(inputDataSet, csmLcSwitch_benchmark);" + "\nvar activity = await CheckSwitchLogicAsync(inputDataSet, csmLcSwitch_benchmark);", + "\nactivity" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "activity.Status.Should().Be(ActivityLogStatus.Succeeded);" ], "metadata": {}, "execution_count": 0, @@ -195,7 +251,7 @@ { "cell_type": "code", "source": [ - "var basicIfrsVariable = new IfrsVariable{Partition = partition.Id, DataNode = groupOfContract, AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\", EconomicBasis = \"L\"};", + "var basicIfrsVariable = new IfrsVariable{Partition = previousPeriodPartition.Id, DataNode = groupOfInsuranceContracts, AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\", EconomicBasis = \"L\"};", "\nvar inputDataSet = new IfrsVariable[]{", "\n basicIfrsVariable with {AocType = \"BOP\", Novelty = \"N\", Value = 100.0},", "\n basicIfrsVariable with {AocType = \"CF\", Novelty = \"N\", Value = -10.0},", @@ -235,7 +291,7 @@ { "cell_type": "code", "source": [ - "var basicIfrsVariable = new IfrsVariable{Partition = partition.Id, DataNode = groupOfContract, AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\", EconomicBasis = \"L\"};", + "var basicIfrsVariable = new IfrsVariable{Partition = previousPeriodPartition.Id, DataNode = groupOfInsuranceContracts, AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\", EconomicBasis = \"L\"};", "\nvar inputDataSet = new IfrsVariable[]{", "\n basicIfrsVariable with {AocType = \"BOP\", Novelty = \"N\", Value = 100.0},", "\n basicIfrsVariable with {AocType = \"CF\", Novelty = \"N\", Value = -10.0},", @@ -271,7 +327,7 @@ { "cell_type": "code", "source": [ - "var basicIfrsVariable = new IfrsVariable{Partition = partition.Id, DataNode = groupOfContract, AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\", EconomicBasis = \"L\"};", + "var basicIfrsVariable = new IfrsVariable{Partition = previousPeriodPartition.Id, DataNode = groupOfInsuranceContracts, AccidentYear = null, AmountType = \"PR\", EstimateType = \"BE\", EconomicBasis = \"L\"};", "\nvar inputDataSet = new IfrsVariable[]{", "\n basicIfrsVariable with {AocType = \"BOP\", Novelty = \"I\", Value = 5010.0, EstimateType = \"L\", AmountType = null},", "\n basicIfrsVariable with {AocType = \"MC\", Novelty = \"I\", Value = -10.0},", diff --git a/ifrs17-template/Test/TestData.ipynb b/ifrs17-template/Test/TestData.ipynb new file mode 100644 index 00000000..66b22324 --- /dev/null +++ b/ifrs17-template/Test/TestData.ipynb @@ -0,0 +1,408 @@ +{ + "metadata": { + "authors": [], + "kernelspec": { + "display_name": "Formula Framework", + "language": "C#", + "name": "C#" + }, + "language_info": { + "file_extension": ".cs", + "mimetype": "text/plain", + "name": "C#" + } + }, + "nbformat": 4, + "nbformat_minor": 5, + "cells": [ + { + "cell_type": "code", + "source": [ + "#!import \"../Constants/CalculationEngine\"", + "\n#!eval calculationEngine" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# AoC Structure" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "string novelties = ", + "\n@\"@@Novelty", + "\nSystemName,DisplayName,Parent,Order", + "\nI,In Force,,1", + "\nN,New Business,,10", + "\nC,Combined,,20\";", + "\n", + "\nstring canonicalAocTypes = ", + "\n@\"@@AocType,,,,,,,,,,,", + "\nSystemName,DisplayName,Parent,Order,,,,,,,,", + "\nBOP,Opening Balance,,10,,,,,,,,", + "\nMC,Model Correction,,20,,,,,,,,", + "\nPC,Portfolio Changes,,30,,,,,,,,", + "\nRCU,Reinsurance Coverage Update,PC,40,,,,,,,,", + "\nCF,Cash flow,,50,,,,,,,,", + "\nIA,Interest Accretion,,60,,,,,,,,", + "\nAU,Assumption Update,,70,,,,,,,,", + "\nFAU,Financial Assumption Update,,80,,,,,,,,", + "\nYCU,Yield Curve Update,FAU,90,,,,,,,,", + "\nCRU,Credit Risk Update,FAU,100,,,,,,,,", + "\nEV,Experience Variance,,110,,,,,,,,", + "\nWO,Write-Off,,120,,,,,,,,", + "\nCL,Combined Liabilities,,130,,,,,,,,", + "\nEA,Experience Adjustment,,140,,,,,,,,", + "\nAM,Amortization,,150,,,,,,,,", + "\nFX,FX Impact,,160,,,,,,,,", + "\nEOP,Closing Balance,,170,,,,,,,,\";", + "\n", + "\nstring canonicalAocConfig = ", + "\n@\"@@AocConfiguration,,,,,,,,,,,", + "\nAocType,Novelty,DataType,InputSource,FxPeriod,YcPeriod,CdrPeriod,ValuationPeriod,RcPeriod,Order,Year,Month", + "\nBOP,I,Optional,7,BeginningOfPeriod,BeginningOfPeriod,BeginningOfPeriod,BeginningOfPeriod,BeginningOfPeriod,10,1900,1", + "\nMC,I,Optional,4,BeginningOfPeriod,BeginningOfPeriod,BeginningOfPeriod,BeginningOfPeriod,BeginningOfPeriod,20,1900,1", + "\nRCU,I,Calculated,4,BeginningOfPeriod,BeginningOfPeriod,BeginningOfPeriod,BeginningOfPeriod,EndOfPeriod,30,1900,1", + "\nCF,I,Calculated,4,Average,NotApplicable,BeginningOfPeriod,Delta,EndOfPeriod,40,1900,1", + "\nIA,I,Calculated,5,Average,BeginningOfPeriod,BeginningOfPeriod,Delta,EndOfPeriod,50,1900,1", + "\nAU,I,Optional,4,EndOfPeriod,BeginningOfPeriod,BeginningOfPeriod,EndOfPeriod,EndOfPeriod,60,1900,1", + "\nYCU,I,CalculatedTelescopic,4,EndOfPeriod,EndOfPeriod,BeginningOfPeriod,EndOfPeriod,EndOfPeriod,70,1900,1", + "\nCRU,I,CalculatedTelescopic,4,EndOfPeriod,EndOfPeriod,EndOfPeriod,EndOfPeriod,EndOfPeriod,80,1900,1", + "\nEV,I,Optional,4,EndOfPeriod,EndOfPeriod,EndOfPeriod,EndOfPeriod,EndOfPeriod,90,1900,1", + "\nBOP,N,Optional,4,Average,EndOfPeriod,EndOfPeriod,BeginningOfPeriod,EndOfPeriod,95,1900,1", + "\nCF,N,Calculated,4,Average,NotApplicable,EndOfPeriod,Delta,EndOfPeriod,110,1900,1", + "\nIA,N,Calculated,4,Average,EndOfPeriod,EndOfPeriod,Delta,EndOfPeriod,120,1900,1", + "\nAU,N,Optional,4,EndOfPeriod,EndOfPeriod,EndOfPeriod,EndOfPeriod,EndOfPeriod,130,1900,1", + "\nEV,N,Optional,4,EndOfPeriod,EndOfPeriod,EndOfPeriod,EndOfPeriod,EndOfPeriod,140,1900,1", + "\nCF,C,Optional,2,Average,NotApplicable,NotApplicable,NotApplicable,NotApplicable,160,1900,1", + "\nWO,C,Optional,2,Average,NotApplicable,NotApplicable,NotApplicable,NotApplicable,170,1900,1", + "\nCL,C,Mandatory,4,EndOfPeriod,EndOfPeriod,EndOfPeriod,EndOfPeriod,EndOfPeriod,180,1900,1", + "\nEA,C,Calculated,4,EndOfPeriod,NotApplicable,NotApplicable,NotApplicable,EndOfPeriod,190,1900,1", + "\nAM,C,Calculated,6,EndOfPeriod,NotApplicable,NotApplicable,NotApplicable,EndOfPeriod,200,1900,1", + "\nFX,C,Calculated,0,NotApplicable,NotApplicable,NotApplicable,NotApplicable,NotApplicable,210,1900,1", + "\nEOP,C,Calculated,6,EndOfPeriod,EndOfPeriod,EndOfPeriod,EndOfPeriod,EndOfPeriod,220,1900,1\";" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Estimate Type" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var estimateType = ", + "\n@\"@@EstimateType,,,,,,,,,,,", + "\nSystemName,DisplayName,Order,StructureType,InputSource,PeriodType,ExternalId0,ExternalId1,ExternalId2,ExternalId3,ExternalId4,", + "\nBE,Best Estimate of Present Value,1,AoC,4,EndOfPeriod,,,,,,", + "\nAA,Advance Actuals,60,AoC,3,NotApplicable,PayablePR,ReceivableNIC,ReceivableICO,RiReceivablePR,RiPayableNIC,", + "\nOA,Overdue Actuals,70,AoC,3,NotApplicable,ReceivablePR,PayableNIC,PayableICO,RiPayablePR,RiReceivableNIC,", + "\nDA,Deferrable Actuals,80,AoC,7,NotApplicable,,,,,,", + "\nA,Actuals,90,None,2,NotApplicable,,,,,,\";" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Projection Configuration" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var projectionConfiguration = ", + "\n@\"@@ProjectionConfiguration,,,,,,,,,,,", + "\nSystemName,DisplayName,Shift,TimeStep,,,,,,,,", + "\nP0,End of January,0,1,,,,,,,,", + "\nP1,End of February,0,2,,,,,,,,", + "\nP2,End of March,0,3,,,,,,,,", + "\nP3,End of April,0,4,,,,,,,,", + "\nP4,End of May,0,5,,,,,,,,", + "\nP5,End of June,0,6,,,,,,,,", + "\nP6,End of July,0,7,,,,,,,,", + "\nP7,End of August,0,8,,,,,,,,", + "\nP8,End of September,0,9,,,,,,,,", + "\nP9,End of October,0,10,,,,,,,,", + "\nP10,End of November,0,11,,,,,,,,", + "\nP11,End of December,0,12,,,,,,,,", + "\nP12,End of Year+1,12,12,,,,,,,,", + "\nP13,End of Year+2,24,12,,,,,,,,", + "\nP14,End of Year+3,36,12,,,,,,,,", + "\nP15,End of Year+4,48,12,,,,,,,,", + "\nP16,Year+5 to Year+10,60,60,,,,,,,,", + "\nP17,Year+10 to Year+15,120,60,,,,,,,,", + "\nP18,Year+15 to Year+20,180,60,,,,,,,,", + "\nP19,Years Over +20,240,9999,,,,,,,,\";" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Amount Type" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var amountType = ", + "\n@\"@@AmountType,,,,,,,,,,,", + "\nSystemName,DisplayName,Parent,Order,PeriodType,,,,,,,", + "\nPR,Premiums,,10,BeginningOfPeriod,,,,,,,", + "\nCL,Claims,,20,EndOfPeriod,,,,,,,", + "\nNIC,Non Investment Component,CL,30,EndOfPeriod,,,,,,,", + "\nICO,Investment Component,CL,40,EndOfPeriod,,,,,,,", + "\nCDR,Credit Default Risk,CL,50,EndOfPeriod,,,,,,,", + "\nCE,Claim Expenses,CL,200,EndOfPeriod,,,,,,,", + "\nALE,Allocated Loss Adjustment Expenses,CE,210,EndOfPeriod,,,,,,,", + "\nULE,Unallocated Loss Adjustment Expenses,CE,220,EndOfPeriod,,,,,,,", + "\nAE,Attributable Expenses,,80,BeginningOfPeriod,,,,,,,", + "\nAEA,Aquisition,AE,90,BeginningOfPeriod,,,,,,,", + "\nAEM,Maintenance,AE,100,BeginningOfPeriod,,,,,,,", + "\nNE,Non Attributable Expenses,,110,BeginningOfPeriod,,,,,,,", + "\nAC,Attributable Commission,,120,BeginningOfPeriod,,,,,,,", + "\nACA,Aquisition,AC,130,BeginningOfPeriod,,,,,,,", + "\nACM,Maitenance,AC,140,BeginningOfPeriod,,,,,,,\";" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Reporting Nodes" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var reportingNodeG = new ReportingNode() {", + "\n SystemName = \"G\",", + "\n DisplayName = \"Group\",", + "\n Currency = \"CHF\"", + "\n};" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Variables" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var groupOfInsuranceContracts = \"DT1.1\";", + "\nvar groupOfReinsuranceContracts = \"DTR1.1\";", + "\nvar reportingNode = \"CH\";", + "\nvar scenario = (string)null;" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var args = new ImportArgs(reportingNode, 2021, 3, Periodicity.Quarterly, scenario, ImportFormats.Actual);", + "\nvar previousArgs = new ImportArgs(reportingNode, 2020, 12, Periodicity.Quarterly, scenario, ImportFormats.Actual);" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Partitions" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var partition = new PartitionByReportingNodeAndPeriod { Id = (Guid)(await DataSource.Partition.GetKeyForInstanceAsync(args)),", + "\n ReportingNode = reportingNode, ", + "\n Scenario = scenario, ", + "\n Year = args.Year,", + "\n Month = args.Month };", + "\nvar previousPeriodPartition = new PartitionByReportingNodeAndPeriod { Id = (Guid)(await DataSource.Partition.GetKeyForInstanceAsync(previousArgs)),", + "\n ReportingNode = reportingNode, ", + "\n Scenario = scenario, ", + "\n Year = previousArgs.Year,", + "\n Month = previousArgs.Month };", + "\nvar partitionReportingNode = new PartitionByReportingNode { Id = (Guid)(await DataSource.Partition.GetKeyForInstanceAsync(args)),", + "\n ReportingNode = args.ReportingNode};", + "\n" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# DataNodes and DataNodeData" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var dt1 = new Portfolio(){", + "\n Partition = partitionReportingNode.Id,", + "\n ContractualCurrency = \"USD\",", + "\n LineOfBusiness = \"ANN\",", + "\n ValuationApproach =\"BBA\",", + "\n OciType = \"Default\",", + "\n SystemName = \"DT1\",", + "\n DisplayName = \"DT1 OCI\"", + "\n};", + "\n", + "\nvar dtr1 = new Portfolio(){", + "\n Partition = partitionReportingNode.Id,", + "\n ContractualCurrency = \"USD\",", + "\n LineOfBusiness = \"ANN\",", + "\n ValuationApproach =\"BBA\",", + "\n OciType = \"Default\",", + "\n SystemName = \"DTR1\",", + "\n DisplayName = \"DTR1 OCI\"", + "\n};", + "\n", + "\nvar dt11 = new GroupOfInsuranceContract(){", + "\n Portfolio = \"DT1\",", + "\n Profitability = \"P\",", + "\n LiabilityType = \"LRC\",", + "\n AnnualCohort = 2020,", + "\n Partition = partitionReportingNode.Id,", + "\n ContractualCurrency = \"USD\",", + "\n LineOfBusiness = \"ANN\",", + "\n ValuationApproach = \"BBA\",", + "\n OciType =\"Default\" ,", + "\n SystemName = \"DT1.1\",", + "\n DisplayName = \"DT1.1 OCI LRC PA 0.8\"", + "\n};", + "\n", + "\nvar dtr11 = new GroupOfReinsuranceContract(){", + "\n Portfolio = \"DTR1\",", + "\n Profitability = \"P\",", + "\n LiabilityType = \"LRC\",", + "\n AnnualCohort = 2020,", + "\n Partition = partitionReportingNode.Id,", + "\n ContractualCurrency = \"USD\",", + "\n LineOfBusiness = \"ANN\",", + "\n ValuationApproach = \"BBA\",", + "\n OciType =\"Default\" ,", + "\n SystemName = \"DTR1.1\",", + "\n DisplayName = \"DTR1.1 OCI LRC PA 0.8\"", + "\n};", + "\nvar dt11State = new DataNodeState {", + "\n DataNode = \"DT1.1\",", + "\n State = State.Active,", + "\n Year = previousArgs.Year,", + "\n Month = previousArgs.Month,", + "\n Partition = partitionReportingNode.Id", + "\n };", + "\n", + "\nvar dtr11State = new DataNodeState {", + "\n DataNode = \"DTR1.1\",", + "\n State = State.Active,", + "\n Year = previousArgs.Year,", + "\n Month = previousArgs.Month,", + "\n Partition = partitionReportingNode.Id", + "\n };", + "\n", + "\nvar dt11SingleParameter = new SingleDataNodeParameter {", + "\n Year = previousArgs.Year,", + "\n Month = previousArgs.Month,", + "\n DataNode = \"DT1.1\",", + "\n PremiumAllocation = .8,", + "\n Partition = partitionReportingNode.Id", + "\n };", + "\n", + "\nvar dt11Inter = new InterDataNodeParameter{", + "\n LinkedDataNode = \"DTR1.1\",", + "\n\tReinsuranceCoverage = 1,", + "\n\tYear = args.Year,", + "\n\tMonth = args.Month,", + "\n\tDataNode = \"DT1.1\",", + "\n\tScenario = args.Scenario,", + "\n Partition = partitionReportingNode.Id", + "\n};" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# YieldCurve" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var yieldCurve = new YieldCurve(){", + "\n Currency = \"USD\",", + "\n Year = 2021,", + "\n Month = 3,", + "\n Values = new []{0.005,0.005,0.005,0.005}", + "\n};", + "\n", + "\nvar yieldCurvePrevious = new YieldCurve(){", + "\n Currency = \"USD\",", + "\n Year = 2020,", + "\n Month = 12,", + "\n Values = new []{0.002, 0.002, 0.002, 0.002}", + "\n};" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + } + ] +} \ No newline at end of file From 756320c65d09c9f614bd8f9e0f8698efd393128b Mon Sep 17 00:00:00 2001 From: Teo Kukuljan <116903719+tkukuljan@users.noreply.github.com> Date: Wed, 25 Jan 2023 15:40:41 +0100 Subject: [PATCH 5/9] Moving tests part 2 (#198) Moving the tests, adapting the tests notebooks and removing the unnecessary code for the DataSource reset. Also the imports are suitabler --- ifrs17-template/Test/Tests.ipynb | 40 +------------------ .../Test/AocStructureTest.ipynb | 13 +----- .../Test/ImportStorageTest.ipynb | 13 +----- .../Test/ReportStorageTest.ipynb | 13 +----- .../Test/TechnicalMarginTest.ipynb | 13 +----- .../Test/TestData.ipynb | 10 ----- ifrs17/Test/Tests.ipynb | 36 +++++++++++++++++ 7 files changed, 46 insertions(+), 92 deletions(-) rename {ifrs17-template => ifrs17}/Test/AocStructureTest.ipynb (99%) rename {ifrs17-template => ifrs17}/Test/ImportStorageTest.ipynb (99%) rename {ifrs17-template => ifrs17}/Test/ReportStorageTest.ipynb (94%) rename {ifrs17-template => ifrs17}/Test/TechnicalMarginTest.ipynb (98%) rename {ifrs17-template => ifrs17}/Test/TestData.ipynb (98%) diff --git a/ifrs17-template/Test/Tests.ipynb b/ifrs17-template/Test/Tests.ipynb index 3a29c378..2a3d2bed 100644 --- a/ifrs17-template/Test/Tests.ipynb +++ b/ifrs17-template/Test/Tests.ipynb @@ -30,7 +30,7 @@ "cell_type": "markdown", "source": [ "Comprehensive collection of tests executed on top of the Systemorph use cases (initialization).", - "\n
Execute this Notebook using at least 20Gb RAM." + "\n
Execute this Notebook using at least 12Gb RAM." ], "metadata": {}, "execution_count": 0, @@ -45,42 +45,6 @@ "execution_count": 0, "outputs": [] }, - { - "cell_type": "code", - "source": [ - "#!eval-notebook \"ImportStorageTest\"" - ], - "metadata": {}, - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "source": [ - "#!eval-notebook \"ReportStorageTest\"" - ], - "metadata": {}, - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "source": [ - "#!eval-notebook \"AocStructureTest\"" - ], - "metadata": {}, - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "source": [ - "#!eval-notebook \"TechnicalMarginTest\"" - ], - "metadata": {}, - "execution_count": 0, - "outputs": [] - }, { "cell_type": "code", "source": [ @@ -163,4 +127,4 @@ "outputs": [] } ] -} +} \ No newline at end of file diff --git a/ifrs17-template/Test/AocStructureTest.ipynb b/ifrs17/Test/AocStructureTest.ipynb similarity index 99% rename from ifrs17-template/Test/AocStructureTest.ipynb rename to ifrs17/Test/AocStructureTest.ipynb index c13eee40..a230366e 100644 --- a/ifrs17-template/Test/AocStructureTest.ipynb +++ b/ifrs17/Test/AocStructureTest.ipynb @@ -29,7 +29,8 @@ { "cell_type": "code", "source": [ - "#!import \"TestData\"" + "#!import \"../Import/Importers\"", + "\n#!import \"TestData\"" ], "metadata": {}, "execution_count": 0, @@ -44,16 +45,6 @@ "execution_count": 0, "outputs": [] }, - { - "cell_type": "code", - "source": [ - "await DataSource.SetAsync();", - "\nDataSource.Reset(x => x.ResetCurrentPartitions());" - ], - "metadata": {}, - "execution_count": 0, - "outputs": [] - }, { "cell_type": "code", "source": [ diff --git a/ifrs17-template/Test/ImportStorageTest.ipynb b/ifrs17/Test/ImportStorageTest.ipynb similarity index 99% rename from ifrs17-template/Test/ImportStorageTest.ipynb rename to ifrs17/Test/ImportStorageTest.ipynb index 985ef965..67a9871f 100644 --- a/ifrs17-template/Test/ImportStorageTest.ipynb +++ b/ifrs17/Test/ImportStorageTest.ipynb @@ -29,7 +29,8 @@ { "cell_type": "code", "source": [ - "#!import \"TestData\"" + "#!import \"../Import/Importers\"", + "\n#!import \"TestData\"" ], "metadata": {}, "execution_count": 0, @@ -44,16 +45,6 @@ "execution_count": 0, "outputs": [] }, - { - "cell_type": "code", - "source": [ - "await DataSource.SetAsync();", - "\nDataSource.Reset(x => x.ResetCurrentPartitions());" - ], - "metadata": {}, - "execution_count": 0, - "outputs": [] - }, { "cell_type": "code", "source": [ diff --git a/ifrs17-template/Test/ReportStorageTest.ipynb b/ifrs17/Test/ReportStorageTest.ipynb similarity index 94% rename from ifrs17-template/Test/ReportStorageTest.ipynb rename to ifrs17/Test/ReportStorageTest.ipynb index 21d410fb..277fca1c 100644 --- a/ifrs17-template/Test/ReportStorageTest.ipynb +++ b/ifrs17/Test/ReportStorageTest.ipynb @@ -20,7 +20,8 @@ { "cell_type": "code", "source": [ - "#!import \"TestData\"" + "#!import \"../Report/ReportStorage\"", + "\n#!import \"TestData\"" ], "metadata": {}, "execution_count": 0, @@ -35,16 +36,6 @@ "execution_count": 0, "outputs": [] }, - { - "cell_type": "code", - "source": [ - "await DataSource.SetAsync();", - "\nDataSource.Reset(x => x.ResetCurrentPartitions());" - ], - "metadata": {}, - "execution_count": 0, - "outputs": [] - }, { "cell_type": "code", "source": [ diff --git a/ifrs17-template/Test/TechnicalMarginTest.ipynb b/ifrs17/Test/TechnicalMarginTest.ipynb similarity index 98% rename from ifrs17-template/Test/TechnicalMarginTest.ipynb rename to ifrs17/Test/TechnicalMarginTest.ipynb index 537208b5..1e60b92a 100644 --- a/ifrs17-template/Test/TechnicalMarginTest.ipynb +++ b/ifrs17/Test/TechnicalMarginTest.ipynb @@ -29,7 +29,8 @@ { "cell_type": "code", "source": [ - "#!import \"TestData\"" + "#!import \"../Import/Importers\"", + "\n#!import \"TestData\"" ], "metadata": {}, "execution_count": 0, @@ -44,16 +45,6 @@ "execution_count": 0, "outputs": [] }, - { - "cell_type": "code", - "source": [ - "await DataSource.SetAsync();", - "\nDataSource.Reset(x => x.ResetCurrentPartitions());" - ], - "metadata": {}, - "execution_count": 0, - "outputs": [] - }, { "cell_type": "code", "source": [ diff --git a/ifrs17-template/Test/TestData.ipynb b/ifrs17/Test/TestData.ipynb similarity index 98% rename from ifrs17-template/Test/TestData.ipynb rename to ifrs17/Test/TestData.ipynb index 66b22324..1326997d 100644 --- a/ifrs17-template/Test/TestData.ipynb +++ b/ifrs17/Test/TestData.ipynb @@ -15,16 +15,6 @@ "nbformat": 4, "nbformat_minor": 5, "cells": [ - { - "cell_type": "code", - "source": [ - "#!import \"../Constants/CalculationEngine\"", - "\n#!eval calculationEngine" - ], - "metadata": {}, - "execution_count": 0, - "outputs": [] - }, { "cell_type": "markdown", "source": [ diff --git a/ifrs17/Test/Tests.ipynb b/ifrs17/Test/Tests.ipynb index 7a5e9882..a52dc9e6 100644 --- a/ifrs17/Test/Tests.ipynb +++ b/ifrs17/Test/Tests.ipynb @@ -59,6 +59,42 @@ "metadata": {}, "execution_count": 0, "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "#!eval-notebook \"AocStructureTest\"" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "#!eval-notebook \"ImportStorageTest\"" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "#!eval-notebook \"ReportStorageTest\"" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "#!eval-notebook \"TechnicalMarginTest\"" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] } ] } \ No newline at end of file From d16fc06bfbf3ca87519cacbeabdf3eb974f3d7d3 Mon Sep 17 00:00:00 2001 From: Teo Kukuljan <116903719+tkukuljan@users.noreply.github.com> Date: Thu, 26 Jan 2023 10:48:22 +0100 Subject: [PATCH 6/9] Adding mandatory AoC step exception in the filter (#151) * Adding mandatory AoC step exception in the filter and adding warnings for not importing mandatory AoC steps- * Including Andreas feedback * Adding a warning for missing aoc steps import. * Removing bugs * Aesthetics * Andreas feedback implementation. * Working on Expressions. * Making the reader function work. * Implementing into Importers. * Reverting the changes from the merge. * Cleanup. * Andreas feedback implementation * Adding imports. * Adaptations and aesthetics. * Simplifying the diff. * Simplifying the diff. * A fix. * Corrected merging conflicts. * Cleaning the diff (a bit). * Reseting a change. * Implementing Daniels feedback. * Reverting some change. * Improvement. * Replacing ToString for ToIdentityString. * ToIdentityString update * Proposition 1 * Improvement * some refactors --- ifrs17/Constants/Validations.ipynb | 3 +- ifrs17/Import/Importers.ipynb | 151 ++++++++++++++++++++--------- ifrs17/Utils/Extensions.ipynb | 27 +++++- 3 files changed, 132 insertions(+), 49 deletions(-) diff --git a/ifrs17/Constants/Validations.ipynb b/ifrs17/Constants/Validations.ipynb index b68ea84e..3fdcf95e 100644 --- a/ifrs17/Constants/Validations.ipynb +++ b/ifrs17/Constants/Validations.ipynb @@ -51,7 +51,7 @@ "source": [ "public enum Warning {", "\n // Import", - "\n ActiveDataNodeWithCashflowBOPI, VariablesAlreadyImported, VariablesAlreadyCalculated, ScenarioReCalculations,", + "\n ActiveDataNodeWithCashflowBOPI, VariablesAlreadyImported, VariablesAlreadyCalculated, ScenarioReCalculations, MandatoryAocStepMissing,", "\n // Default", "\n Generic", "\n}; " @@ -185,6 +185,7 @@ "\n // Import", "\n (Warning.ActiveDataNodeWithCashflowBOPI , 1) => $\"Cash flow with AoC Type: {AocTypes.BOP} and Novelty: {Novelties.I} for Group of Contract {s[0]} is not allowed because previous period data are available.\",", "\n (Warning.VariablesAlreadyImported , 0) => $\"The import of the current file does not contain any new data. Hence, no data will be saved or calculations will be performed.\",", + "\n (Warning.MandatoryAocStepMissing , 3) => $\"The AoC step ({s[0]}, {s[1]}) is not imported for ({s[2]}).\",", "\n (Warning.ScenarioReCalculations , 1) => $\"The present Best Estimate import makes the result of dependent Scenarios out of date. Hence, the following Scenarios are re-calculated: {s[0]}.\", ", "\n // Default", "\n (Warning.Generic , _) => $\"{s[0]}\",", diff --git a/ifrs17/Import/Importers.ipynb b/ifrs17/Import/Importers.ipynb index 5d680c6c..93b6296e 100644 --- a/ifrs17/Import/Importers.ipynb +++ b/ifrs17/Import/Importers.ipynb @@ -69,6 +69,7 @@ "\n // Dimensions", "\n public Dictionary EstimateType;", "\n public Dictionary AmountType; ", + "\n public HashSet MandatoryAocSteps;", "\n public HashSet AocTypeMap;", "\n private HashSet estimateTypes;", "\n private HashSet amountTypes;", @@ -125,6 +126,7 @@ "\n ReportingNode = reportingNodes.First();", "\n", "\n var aocConfigurationByAocStep = await dataSource.LoadAocStepConfigurationAsync(args.Year, args.Month);", + "\n MandatoryAocSteps = aocConfigurationByAocStep.Where(x => x.DataType == DataType.Mandatory).Select(x => new AocStep(x.AocType, x.Novelty)).ToHashSet();", "\n AocTypeMap = args.ImportFormat switch {", "\n ImportFormats.Cashflow => aocConfigurationByAocStep.Where(x => x.InputSource.Contains(InputSource.Cashflow) &&", "\n !new DataType[]{DataType.Calculated, DataType.CalculatedTelescopic}.Contains(x.DataType) )", @@ -471,30 +473,6 @@ "execution_count": 0, "outputs": [] }, - { - "cell_type": "markdown", - "source": [ - "## Validation for Active Data Node States" - ], - "metadata": {}, - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "source": [ - "public async Task ValidateForDataNodeStateActiveAsync(IWorkspace workspace, Dictionary dataNodes) where T : BaseDataRecord", - "\n{ ", - "\n foreach(var item in (await workspace.Query().ToArrayAsync()).GroupBy(x => x.DataNode))", - "\n if(!dataNodes.ContainsKey(item.First().DataNode))", - "\n ApplicationMessage.Log(Error.InactiveDataNodeState, item.First().DataNode);", - "\n}", - "\n" - ], - "metadata": {}, - "execution_count": 0, - "outputs": [] - }, { "cell_type": "markdown", "source": [ @@ -593,6 +571,102 @@ "execution_count": 0, "outputs": [] }, + { + "cell_type": "markdown", + "source": [ + "# Validations" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Validation for Active Data Node States" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "public async static Task ValidateForDataNodeStateActiveAsync(this IWorkspace workspace, Dictionary dataNodes) where T : BaseDataRecord", + "\n{ ", + "\n foreach(var item in (await workspace.Query().ToArrayAsync()).GroupBy(x => x.DataNode))", + "\n if(!dataNodes.ContainsKey(item.First().DataNode))", + "\n ApplicationMessage.Log(Error.InactiveDataNodeState, item.First().DataNode);", + "\n}", + "\n" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Validate for Data Node States Logic" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "public async static Task ValidateDataNodeStatesAsync(this IWorkspace workspace, Dictionary persistentDataNodeByDataNode)", + "\n{", + "\n foreach(var importedDataNodeState in await workspace.Query().ToArrayAsync())", + "\n {", + "\n if(persistentDataNodeByDataNode.TryGetValue(importedDataNodeState.DataNode, out var currentPersistentDataNode))", + "\n {", + "\n if(importedDataNodeState.State < currentPersistentDataNode.State)", + "\n ApplicationMessage.Log(Error.ChangeDataNodeState, importedDataNodeState.DataNode, ", + "\n currentPersistentDataNode.State.ToString(), ", + "\n importedDataNodeState.State.ToString());", + "\n", + "\n if(importedDataNodeState.State == currentPersistentDataNode.State)", + "\n await workspace.DeleteAsync(importedDataNodeState);", + "\n }", + "\n }", + "\n}" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Validate for Mandatory Aoc Steps" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "using static Systemorph.Vertex.Equality.IdentityPropertyExtensions;", + "\npublic async static Task ValidateForMandatoryAocSteps(this IWorkspace workspace, IDataSet dataSet, HashSet mandatoryAocSteps)", + "\n{ ", + "\n var excludedProperties = new[]{nameof(AocType), nameof(Novelty)};", + "\n var includingProperties = typeof(RawVariable).GetIdentityProperties().Select(x=>x.Name).Except(excludedProperties).ToArray();", + "\n var missingAocStepsByIdentityProperties = (await workspace.Query().ToListAsync())", + "\n .GroupBy(x => x.ToStringWith(properties: includingProperties),", + "\n x => new AocStep(x.AocType, x.Novelty),", + "\n (properties, parsedAocSteps) => (properties, mandatoryAocSteps.Except(parsedAocSteps))", + "\n );", + "\n foreach((var properties, var missingSteps) in missingAocStepsByIdentityProperties) ", + "\n foreach(var missingStep in missingSteps) ApplicationMessage.Log(Warning.MandatoryAocStepMissing, missingStep.AocType, missingStep.Novelty, properties);", + "\n}" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, { "cell_type": "markdown", "source": [ @@ -946,22 +1020,7 @@ { "cell_type": "code", "source": [ - "public async static Task ValidateDataNodeStatesAsync(this IWorkspace workspace, Dictionary persistentDataNodeByDataNode)", - "\n{", - "\n foreach(var importedDataNodeState in await workspace.Query().ToArrayAsync())", - "\n {", - "\n if(persistentDataNodeByDataNode.TryGetValue(importedDataNodeState.DataNode, out var currentPersistentDataNode))", - "\n {", - "\n if(importedDataNodeState.State < currentPersistentDataNode.State)", - "\n ApplicationMessage.Log(Error.ChangeDataNodeState, importedDataNodeState.DataNode, ", - "\n currentPersistentDataNode.State.ToString(), ", - "\n importedDataNodeState.State.ToString());", - "\n", - "\n if(importedDataNodeState.State == currentPersistentDataNode.State)", - "\n await workspace.DeleteAsync(importedDataNodeState);", - "\n }", - "\n }", - "\n}" + "" ], "metadata": {}, "execution_count": 0, @@ -1187,11 +1246,10 @@ "\n var values = datarow.Table.Columns.Where(c => c.ColumnName.StartsWith(nameof(RawVariable.Values))).OrderBy(c => c.ColumnName.Length).ThenBy(c => c.ColumnName)", "\n .Select(x => datarow.Field(x.ColumnName).CheckStringForExponentialAndConvertToDouble()).ToArray();", "\n ", - "\n // Filter out empty raw variables for AocType != CL", - "\n //TODO: extend this check for all mandatory step and not just for CL", + "\n // Filter out empty raw variables for AocStep \\not\\in MandatoryAocSteps", "\n if(args.Scenario == null) {", "\n values = values.Prune();", - "\n if(values.Length == 0 && aocType != AocTypes.CL) return null;", + "\n if(values.Length == 0 && !parsingStorage.MandatoryAocSteps.Contains(new AocStep(aocType, novelty))) return null;", "\n }", "\n ", "\n var item = new RawVariable {", @@ -1210,7 +1268,8 @@ "\n }, ImportFormats.Cashflow", "\n ).WithTarget(workspace).ExecuteAsync();", "\n ", - "\n await ValidateForDataNodeStateActiveAsync(workspace, parsingStorage.DataNodeDataBySystemName);", + "\n await workspace.ValidateForMandatoryAocSteps(dataSet, parsingStorage.MandatoryAocSteps);", + "\n await workspace.ValidateForDataNodeStateActiveAsync(parsingStorage.DataNodeDataBySystemName);", "\n return Activity.Finish().Merge(importLog);", "\n}" ], @@ -1311,7 +1370,7 @@ "\n }, ImportFormats.Actual", "\n ).WithTarget(workspace).ExecuteAsync();", "\n ", - "\n await ValidateForDataNodeStateActiveAsync(workspace, parsingStorage.DataNodeDataBySystemName);", + "\n await workspace.ValidateForDataNodeStateActiveAsync(parsingStorage.DataNodeDataBySystemName);", "\n return Activity.Finish().Merge(importLog);", "\n}" ], @@ -1419,7 +1478,7 @@ "\n foreach (var iv in invalidVariables)", "\n ApplicationMessage.Log(Error.MultipleTechnicalMarginOpening, $\"{iv.DataNode},{iv.AocType},{iv.Novelty}\");", "\n ", - "\n await ValidateForDataNodeStateActiveAsync(workspace, parsingStorage.DataNodeDataBySystemName);", + "\n await workspace.ValidateForDataNodeStateActiveAsync(parsingStorage.DataNodeDataBySystemName);", "\n targetPartitionByReportingNodeAndPeriodId = parsingStorage.TargetPartitionByReportingNodeAndPeriod.Id;", "\n return Activity.Finish().Merge(importLog);", "\n}" diff --git a/ifrs17/Utils/Extensions.ipynb b/ifrs17/Utils/Extensions.ipynb index 9e335a22..d23cd61e 100644 --- a/ifrs17/Utils/Extensions.ipynb +++ b/ifrs17/Utils/Extensions.ipynb @@ -207,12 +207,12 @@ "cell_type": "code", "source": [ "using System.Text;", - "\npublic static string ToIdentityString(this T v) where T : class", + "\npublic static string ToIdentityString(this T v, params string[] excludingProperties) where T : class", "\n{", "\n StringBuilder sb = new StringBuilder();", "\n var propertyInfos = v.GetType()", "\n .GetProperties()", - "\n .Where(x => Attribute.IsDefined(x, typeof(IdentityPropertyAttribute)))", + "\n .Where(x => Attribute.IsDefined(x, typeof(IdentityPropertyAttribute)) && !excludingProperties.Contains(x.Name))", "\n .OrderByDescending(x => x.PropertyType.Name).ThenByDescending(x => x.Name)", "\n .Select(x => sb.Append(x.Name).Append(\":\").Append(v.GetType().GetProperty(x.Name)?.GetValue(v, null)).Append(\", \")).ToArray();", "\n return propertyInfos.Count() == 0? v.ToString() : propertyInfos.Select(p => p.ToString()).ToArray().Last();", @@ -222,6 +222,29 @@ "execution_count": 0, "outputs": [] }, + { + "cell_type": "markdown", + "source": [ + "# ToString with including properties" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "public static string ToStringWith(this T variable, string[] properties) where T : BaseVariableIdentity", + "\n{", + "\n var propertiesRead = variable.ToString().Split('{', '}')[1].Split(',');", + "\n var propertiesFiltered = propertiesRead.Where(x=> properties.Contains(x.Split('=')[0].Trim()));", + "\n return string.Join(\", \", propertiesFiltered).Trim();", + "\n}" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, { "cell_type": "code", "source": [ From 61562ce6374df34c3e8aa777b4df8c1bdef8fcc2 Mon Sep 17 00:00:00 2001 From: Davide Colleoni <103409906+dcolleoni@users.noreply.github.com> Date: Thu, 26 Jan 2023 18:01:30 +0100 Subject: [PATCH 7/9] Fix opening importer (#199) * improving import: fix opening and Fix CommitToAsync to deal with physical db * clean up * clean up * add test for cf * rename file and correct memory usage --- ...ominalCashflows_CH_2020_12_DT1.1NoPrem.csv | 11 + .../Test/ReimportWithDifferentScopeTest.ipynb | 198 ++++++++++++++++++ ifrs17-template/Test/Tests.ipynb | 11 +- ifrs17/Import/Importers.ipynb | 23 +- 4 files changed, 232 insertions(+), 11 deletions(-) create mode 100644 ifrs17-template/Test/Data/NominalCashflows_CH_2020_12_DT1.1NoPrem.csv create mode 100644 ifrs17-template/Test/ReimportWithDifferentScopeTest.ipynb diff --git a/ifrs17-template/Test/Data/NominalCashflows_CH_2020_12_DT1.1NoPrem.csv b/ifrs17-template/Test/Data/NominalCashflows_CH_2020_12_DT1.1NoPrem.csv new file mode 100644 index 00000000..885890c3 --- /dev/null +++ b/ifrs17-template/Test/Data/NominalCashflows_CH_2020_12_DT1.1NoPrem.csv @@ -0,0 +1,11 @@ +@@Main,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +ReportingNode,Year,Month,Scenario,,,,,,,,,,,,,,,,,,,,,,,,,, +CH,2020,12,,,,,,,,,,,,,,,,,,,,,,,,,,, +@@Cashflow,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +DataNode,AmountType,EstimateType,AocType,Novelty,AccidentYear,Values0,Values1,Values2,Values3,Values4,Values5,Values6,Values7,Values8,Values9,Values10,Values11,Values12,Values13,Values14,Values15,Values16,Values17,Values18,Values19,Values20,Values21,Values22,Values23 +DT1.1,NIC,BE,BOP,N,,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25 +DT1.1,,CU,BOP,N,,-10,-10,-10,-10,-10,-10,-10,-10,-10,-10,-10,-10,-10,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-3 +DT1.1,,RA,BOP,N,,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5 +DT1.1,NIC,BE,CL,C,,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25 +DT1.1,,CU,CL,C,,-10,-10,-10,-10,-10,-10,-10,-10,-10,-10,-10,-10,-10,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-3 +DT1.1,,RA,CL,C,,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5,-2.5 diff --git a/ifrs17-template/Test/ReimportWithDifferentScopeTest.ipynb b/ifrs17-template/Test/ReimportWithDifferentScopeTest.ipynb new file mode 100644 index 00000000..7e911369 --- /dev/null +++ b/ifrs17-template/Test/ReimportWithDifferentScopeTest.ipynb @@ -0,0 +1,198 @@ +{ + "metadata": { + "authors": [], + "kernelspec": { + "display_name": "Formula Framework", + "language": "C#", + "name": "C#" + }, + "language_info": { + "file_extension": ".cs", + "mimetype": "text/plain", + "name": "C#" + } + }, + "nbformat": 4, + "nbformat_minor": 5, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "

Reimport With Different Scope Test

" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "#!eval-notebook \"../Initialization/InitSystemorphBaseToMemory\"" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Cash flow Import", + "\n", + "\nImporting a file with N GICs and GRICs." + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "await Import.FromFile(\"../Files/TransactionalData/NominalCashflows_CH_2020_12.csv\")", + "\n .WithFormat(ImportFormats.Cashflow)", + "\n .WithTarget(DataSource)", + "\n .WithActivityLog(Session, DataSource, DataSetReader)", + "\n .ExecuteAsync()" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var ifrsVars1 = await DataSource.Query().ToArrayAsync();", + "\nvar dn1 = ifrsVars1.Select(x => x.DataNode).ToHashSet();", + "\nvar dt11Prem1 = ifrsVars1.Where(x => x.DataNode == \"DT1.1\" && x.AmountType == \"PR\");", + "\n(ifrsVars1.Count(), dn1.Count(), dt11Prem1.Count())" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Cash flow Import - restatement", + "\nImporting a file for the same period as previous import but it only contains 1 GIC: DT1.1 where one amount type (Premium) has been removed" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "await Import.FromFile(\"Data/NominalCashflows_CH_2020_12_DT1.1NoPrem.csv\")", + "\n .WithFormat(ImportFormats.Cashflow)", + "\n .WithTarget(DataSource)", + "\n .WithActivityLog(Session, DataSource, DataSetReader)", + "\n .ExecuteAsync()" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "var ifrsVars2 = await DataSource.Query().ToArrayAsync();", + "\nvar dn2 = ifrsVars2.Select(x => x.DataNode).ToHashSet();", + "\nvar dt11Prem2 = ifrsVars2.Where(x => x.DataNode == \"DT1.1\" && x.AmountType == \"PR\");", + "\n(ifrsVars2.Count(), dn2.Count(), dt11Prem2.Count())" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Test" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Import generate variables" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "ifrsVars1.Count().Should().NotBe(0);" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "ifrsVars2.Count().Should().NotBe(0);" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Count of data nodes does not change" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "dn2.Count().Should().Be(dn1.Count());" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Premiums are present with the first import and then deleted" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "dt11Prem1.Count().Should().NotBe(0);" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "dt11Prem2.Count().Should().Be(0);" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + } + ] +} \ No newline at end of file diff --git a/ifrs17-template/Test/Tests.ipynb b/ifrs17-template/Test/Tests.ipynb index 2a3d2bed..cf5f7611 100644 --- a/ifrs17-template/Test/Tests.ipynb +++ b/ifrs17-template/Test/Tests.ipynb @@ -30,7 +30,7 @@ "cell_type": "markdown", "source": [ "Comprehensive collection of tests executed on top of the Systemorph use cases (initialization).", - "\n
Execute this Notebook using at least 12Gb RAM." + "\n
Execute this Notebook using at least 14Gb RAM." ], "metadata": {}, "execution_count": 0, @@ -54,6 +54,15 @@ "execution_count": 0, "outputs": [] }, + { + "cell_type": "code", + "source": [ + "#!eval-notebook \"ReimportWithDifferentScopeTest\"" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, { "cell_type": "code", "source": [ diff --git a/ifrs17/Import/Importers.ipynb b/ifrs17/Import/Importers.ipynb index 93b6296e..fe8f34ec 100644 --- a/ifrs17/Import/Importers.ipynb +++ b/ifrs17/Import/Importers.ipynb @@ -254,9 +254,11 @@ { "cell_type": "code", "source": [ - "public static async Task CleanAsync (this IDataSource dataSource, Expression> filter = null) where T : class", + "public static async Task CleanAsync (this IDataSource dataSource, Guid partitionId = default, Expression> filter = null) where T : class, IPartitioned", "\n{", - "\n var loadData = await dataSource.Query().Where(filter?? (Expression>)(x => true)).ToListAsync();", + "\n var loadData = partitionId != (Guid)default", + "\n ? await dataSource.Query().Where(x => x.Partition == partitionId ).Where(filter?? (Expression>)(x => true)).ToListAsync()", + "\n : await dataSource.Query().Where(filter ?? (Expression>)(x => true)).ToListAsync();", "\n await dataSource.DeleteAsync(loadData);", "\n}" ], @@ -282,11 +284,11 @@ "\nwhere TData : class, IPartitioned", "\nwhere TPartition : IfrsPartition", "\n{", - "\n if(partitionId != new Guid()) {", + "\n if(partitionId != (Guid)default) {", "\n await target.Partition.SetAsync(partitionId);", "\n await source.Partition.SetAsync(partitionId);", "\n }", - "\n if(snapshot) await CleanAsync(target, filter);", + "\n if(snapshot) await CleanAsync(target, partitionId, filter);", "\n await target.UpdateAsync( await source.Query().ToArrayAsync() );", "\n await target.CommitAsync();", "\n}" @@ -847,8 +849,10 @@ "\n }", "\n", "\n // Remove data nodes which are unaffected by the updated yield curves", - "\n await workspaceToCompute.DeleteAsync( await workspaceToCompute.Query()", - "\n .Where(x => !(dataNodesToUpdate.Contains(x.DataNode) && (x.Partition == targetPartition || x.Partition == defaultPartition))).ToArrayAsync() );", + "\n // TODO : Reintroduce this functionality. Note all UpdateAsync/DeleteAsync performed to the workspaceToCompute are then trasferred to the DataSource.", + "\n // This is way this functionality should be written in a different way. ", + "\n // await workspaceToCompute.DeleteAsync( await workspaceToCompute.Query()", + "\n // .Where(x => !(dataNodesToUpdate.Contains(x.DataNode) && (x.Partition == targetPartition || x.Partition == defaultPartition))).ToArrayAsync() );", "\n", "\n importLog = importLog.Merge(await ComputeAsync(args, workspace, workspaceToCompute, false));", "\n if(importLog.Errors.Any()) return Activity.Finish().Merge(importLog);", @@ -1400,7 +1404,7 @@ "\n log = log.Merge(await ComputeAsync(args, workspace, workspaceToCompute, false)); ", "\n if(log.Errors.Any()) return Activity.Finish().Merge(log);", "\n }", - "\n if (!log.Errors.Any()) await workspaceToCompute.CommitToTargetAsync(DataSource);", + "\n await workspaceToCompute.CommitToTargetAsync(DataSource);", "\n return Activity.Finish().Merge(log);", "\n});" ], @@ -1507,7 +1511,6 @@ "\n var targetDataNodes = workspace.Query().Select(v => v.DataNode).Distinct().ToArray();", "\n await workspace.CommitToAsync(DataSource, partitionId, snapshot : true,", "\n filter : x => targetDataNodes.Contains(x.DataNode));", - "\n ", "\n return Activity.Finish().Merge(parsingLog);", "\n});" ], @@ -1545,9 +1548,9 @@ "\n workspaceToCompute.Initialize(x => x.FromSource(DataSource));", "\n var calculationLog = await ComputeAsync(args, workspace, workspaceToCompute, false); ", "\n if(calculationLog.Errors.Any()) return Activity.Finish().Merge(calculationLog);", - "\n await workspaceToCompute.CommitToAsync(DataSource, partitionId);", + "\n await workspaceToCompute.CommitToTargetAsync(DataSource, x => x.SnapshotMode());", "\n return Activity.Finish().Merge(parsingLog).Merge(calculationLog);", - "\n});" + "\n})" ], "metadata": {}, "execution_count": 0, From ccac4556e6c2877924641c85a255db123da31e8a Mon Sep 17 00:00:00 2001 From: Davide Colleoni <103409906+dcolleoni@users.noreply.github.com> Date: Sat, 28 Jan 2023 22:44:01 +0100 Subject: [PATCH 8/9] Improve documentation (#203) * param report and queries nb * update template readme * update overviews * v100 to v110 links update * typos * refer to v1.1.0 --- PresentValueSeries/InitializeData.ipynb | 2 +- .../PresentValue - Episode 2.ipynb | 2 +- .../PresentValue - Episode 3.ipynb | 2 +- .../Constants/CalculationEngine.ipynb | 2 +- ifrs17-template/Export/MapTemplate.ipynb | 6 +- .../Import/CloseImportTemplate.ipynb | 8 +-- ifrs17-template/InputFormatDescription.ipynb | 34 +++++----- ifrs17-template/OverviewIFRS17Template.ipynb | 10 +-- ifrs17-template/README.md | 24 +++++++ ifrs17-template/Report/ParameterReports.ipynb | 65 ++++++++++++++----- ifrs17-template/Report/Reports.ipynb | 32 ++++----- ifrs17/OverviewCalculationEngine.ipynb | 12 +--- ifrs17/Report/ParameterReportsQueries.ipynb | 28 ++++++-- 13 files changed, 146 insertions(+), 81 deletions(-) diff --git a/PresentValueSeries/InitializeData.ipynb b/PresentValueSeries/InitializeData.ipynb index f7baba98..eacf5385 100644 --- a/PresentValueSeries/InitializeData.ipynb +++ b/PresentValueSeries/InitializeData.ipynb @@ -40,7 +40,7 @@ { "cell_type": "code", "source": [ - "#!import \"//ifrs17/v1.0.0/CalculationEngine\"" + "#!import \"//ifrs17/v1.1.0/CalculationEngine\"" ], "metadata": {}, "execution_count": 0, diff --git a/PresentValueSeries/PresentValue - Episode 2.ipynb b/PresentValueSeries/PresentValue - Episode 2.ipynb index 804597e7..70a4c51a 100644 --- a/PresentValueSeries/PresentValue - Episode 2.ipynb +++ b/PresentValueSeries/PresentValue - Episode 2.ipynb @@ -99,7 +99,7 @@ { "cell_type": "code", "source": [ - "#!import \"//ifrs17/v1.0.0/CalculationEngine\"" + "#!import \"//ifrs17/v1.1.0/CalculationEngine\"" ], "metadata": {}, "execution_count": 0, diff --git a/PresentValueSeries/PresentValue - Episode 3.ipynb b/PresentValueSeries/PresentValue - Episode 3.ipynb index 3463cc41..0c3d4211 100644 --- a/PresentValueSeries/PresentValue - Episode 3.ipynb +++ b/PresentValueSeries/PresentValue - Episode 3.ipynb @@ -99,7 +99,7 @@ { "cell_type": "code", "source": [ - "#!import \"//ifrs17/v1.0.0/CalculationEngine\"" + "#!import \"//ifrs17/v1.1.0/CalculationEngine\"" ], "metadata": {}, "execution_count": 0, diff --git a/ifrs17-template/Constants/CalculationEngine.ipynb b/ifrs17-template/Constants/CalculationEngine.ipynb index af978b92..3b0bef01 100644 --- a/ifrs17-template/Constants/CalculationEngine.ipynb +++ b/ifrs17-template/Constants/CalculationEngine.ipynb @@ -19,7 +19,7 @@ "cell_type": "code", "source": [ "var projectName = \"ifrs17\";", - "\nvar environmentName = \"v1.0.0\";", + "\nvar environmentName = \"v1.1.0\";", "\nvar notebookName = \"CalculationEngine\";", "\nvar calculationEngine = $\"#!import \\\"//{projectName}/{environmentName}/{notebookName}\\\"\";" ], diff --git a/ifrs17-template/Export/MapTemplate.ipynb b/ifrs17-template/Export/MapTemplate.ipynb index dc792dba..eb986486 100644 --- a/ifrs17-template/Export/MapTemplate.ipynb +++ b/ifrs17-template/Export/MapTemplate.ipynb @@ -111,7 +111,7 @@ "source": [ "# Map Template: Data Node", "\n", - "\n[DataNodes](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#data-node) defines the properties of [Portfolios](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#portfolios) and [Group of Contracts](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#group-of-contracts)." + "\n[DataNodes](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#data-node) defines the properties of [Portfolios](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#portfolios) and [Group of Contracts](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#group-of-contracts)." ], "metadata": {}, "execution_count": 0, @@ -167,7 +167,7 @@ "source": [ "# Map Template: Data Node State", "\n", - "\n[Data Node State](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#data-node-state) defines whether the instance is active (used in import/output) or inactive (present in the DataSource but not used in input/output operations)." + "\n[Data Node State](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#data-node-state) defines whether the instance is active (used in import/output) or inactive (present in the DataSource but not used in input/output operations)." ], "metadata": {}, "execution_count": 0, @@ -253,7 +253,7 @@ "source": [ "# Map Template: Data Node Parameter", "\n", - "\n[Data Node Parameters](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#data-node-parameters) are defined at the Group of Contract level and are used during the import calculation. " + "\n[Data Node Parameters](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#data-node-parameters) are defined at the Group of Contract level and are used during the import calculation. " ], "metadata": {}, "execution_count": 0, diff --git a/ifrs17-template/Import/CloseImportTemplate.ipynb b/ifrs17-template/Import/CloseImportTemplate.ipynb index fc78fc44..4070701a 100644 --- a/ifrs17-template/Import/CloseImportTemplate.ipynb +++ b/ifrs17-template/Import/CloseImportTemplate.ipynb @@ -38,7 +38,7 @@ "source": [ "All imports are triggered via the Import command. Several options can be fluently appended to customize the import action:", "\n- FromFile() : accepts a string as input (eg. \"../Files/TransactionalData/Openings_CH_2020_12.csv\"). It corresponds the file to be imported complete with the path", - "\n- WithFormat() : accepts a string as input (eg. \"Cashflow\" or \"Actual\"). It is used to trigger the desired import logic for the current file. Full list of avalaible ImportFormat can be found [here](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/Constants/Consts#import-formats)", + "\n- WithFormat() : accepts a string as input (eg. \"Cashflow\" or \"Actual\"). It is used to trigger the desired import logic for the current file. Full list of avalaible ImportFormats can be found [here](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/Constants/Consts#import-formats)", "\n- WithFileStorage() : defined the drive from where the input files is retrieved. In this example we use the project file storage (new files have to be uploaded into the project), other options are SharePoint and OneDrive. ", "\n- WithTarget() : target drive where the results are stored. In this example we use our in-memory set up", "\n" @@ -88,9 +88,9 @@ "\nNew Data Nodes and Parameters are imported here.", "\n
To easily generate these files, please refer to the **Map Template** files exported [here](../Export/MapTemplate). Edit them at your wish and import them back into the solution through the following cells. ", "\n
For the **ImportFormat**, the following options are expected:", - "\n- ImportFormats.DataNode : [Portfolios](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#portfolios) and [Group of Contracts](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#group-of-contracts)", - "\n- ImportFormats.DataNodeState : the [state of a data node](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#data-node-state) can be either active or inactive.", - "\n- ImportFormats.DataNodeParameter : parameters are described [here](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#data-node-parameters). For **Group of Insurance Contracts** a default [Premium Allocation factor](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/Import/ImportScopeCalculation#experience-adjustment-on-premium) of 1 is applied if the parameter is not imported.", + "\n- ImportFormats.DataNode : [Portfolios](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#portfolios) and [Group of Contracts](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#group-of-contracts)", + "\n- ImportFormats.DataNodeState : the [state of a data node](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#data-node-state) can be either active or inactive.", + "\n- ImportFormats.DataNodeParameter : parameters are described [here](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#data-node-parameters). For **Group of Insurance Contracts** a default [Premium Allocation factor](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/Import/ImportScopeCalculation#experience-adjustment-on-premium) of 1 is applied if the parameter is not imported.", "\n" ], "metadata": {}, diff --git a/ifrs17-template/InputFormatDescription.ipynb b/ifrs17-template/InputFormatDescription.ipynb index 42f72726..1087fa85 100644 --- a/ifrs17-template/InputFormatDescription.ipynb +++ b/ifrs17-template/InputFormatDescription.ipynb @@ -39,18 +39,18 @@ "source": [ "A cash flow file is named **NominalCashflows** *\\_* *\\_* *\\_* and is composed of two sections: **Main** and **Cashflow**.", "\n", - "\nThe **Main** section contains information such as: Reporting Node, Year, Month and Scenario for which the data is being imported. This information is used to define the [partition](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#partitions) to which the data belongs.", + "\nThe **Main** section contains information such as: Reporting Node, Year, Month and Scenario for which the data is being imported. This information is used to define the [partition](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#partitions) to which the data belongs.", "\n", "\nThe **Cashflow** section contains the cash flow data. Several pieces of information are required to characterize a cash flow correctly:", - "\n- [DataNode](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#group-of-contracts) : lowest granularity of the Data Node, it is entered with its SystemName,", - "\n- [AmountType](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#amount-type) : entered with its SystemName,", - "\n- [EstimateType](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#estimate-type) : entered with its SystemName,", - "\n- [AocType](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#aoc-type) : entered with its SystemName,", - "\n- [Novelty](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#novelty) : entered with its SystemName,", + "\n- [DataNode](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#group-of-contracts) : lowest granularity of the Data Node, it is entered with its SystemName,", + "\n- [AmountType](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#amount-type) : entered with its SystemName,", + "\n- [EstimateType](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#estimate-type) : entered with its SystemName,", + "\n- [AocType](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#aoc-type) : entered with its SystemName,", + "\n- [Novelty](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#novelty) : entered with its SystemName,", "\n- AccidentYear : relevant for Data Node with Liability Type: Liability for Incurred Claims.", "\n", - "\nAll valid combinations of AocType and Novelty can be found in [AocConfiguration](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#aoc-configuration) with DataType Optional or Mandatory.", - "\n
The cash flow is entered in the columns named ValuesN where N goes from 0 to the lenght of cash flow. The Value0 corresponds to January of the Year entered in the **Main** section for every combination of AocType and Novelty. " + "\nAll valid combinations of AocType and Novelty can be found in [AocConfiguration](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#aoc-configuration) with DataType Optional or Mandatory.", + "\n
The cash flow is entered in the columns named ValuesN where N goes from 0 to the length of cash flow. The Value0 corresponds to January of the Year entered in the **Main** section for every combination of AocType and Novelty. " ], "metadata": {}, "execution_count": 0, @@ -74,13 +74,13 @@ "source": [ "An Actuals file is named **Actuals** *\\_* *\\_* *\\_* and is composed of two sections: **Main** and **Actual**.", "\n", - "\nThe **Main** section contains information such as: Reporting Node, Year, Month for which the data is being imported. This information is used to define the [partition](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#partition) to which the data belong.", + "\nThe **Main** section contains information such as: Reporting Node, Year, Month for which the data is being imported. This information is used to define the [partition](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#partition) to which the data belong.", "\n", "\nThe **Actual** section contains the actuals data. Several pieces of information are required to to characterize an Actuals amount correctly:", - "\n- [DataNode](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#group-of-contracts) : lowest granularity of the Data Node, it is entered with its SystemName,", - "\n- [AocType](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#aoc-type) : entered with its SystemName,", - "\n- [AmountType](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#amount-type) : entered with its SystemName,", - "\n- [EstimateType](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#estimate-type) : entered with its SystemName,", + "\n- [DataNode](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#group-of-contracts) : lowest granularity of the Data Node, it is entered with its SystemName,", + "\n- [AocType](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#aoc-type) : entered with its SystemName,", + "\n- [AmountType](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#amount-type) : entered with its SystemName,", + "\n- [EstimateType](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#estimate-type) : entered with its SystemName,", "\n- AccidentYear : relevant for Data Node with Liability Type: Liability for Incurred Claims." ], "metadata": {}, @@ -105,12 +105,12 @@ "source": [ "An Openings file is named **Openings** *\\_* *\\_* *\\_* and is composed of two sections: **Main** and **Opening**. In an AoC for one period, we only need one file with the year/month combination of the beginning of the period. Some GICs do not need any entries in Openings.", "\n", - "\nThe **Main** section contains information such as: Reporting Node, Year, Month for which the data is being imported. This information is used to define the [partition](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#partition) to which the data belong.", + "\nThe **Main** section contains information such as: Reporting Node, Year, Month for which the data is being imported. This information is used to define the [partition](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#partition) to which the data belong.", "\n", "\nThe **Opening** section contains values at opening of an analysis. Several pieces of information are required to to characterize an Opening amount correctly:", - "\n- [DataNode](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#group-of-contracts) : lowest granularity of the Data Node, it is entered with its SystemName,", - "\n- [AmountType](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#amount-type) : entered with its SystemName,", - "\n- [EstimateType](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#estimate-type) : entered with its SystemName,", + "\n- [DataNode](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#group-of-contracts) : lowest granularity of the Data Node, it is entered with its SystemName,", + "\n- [AmountType](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#amount-type) : entered with its SystemName,", + "\n- [EstimateType](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#estimate-type) : entered with its SystemName,", "\n- AccidentYear : relevant for Data Node with Liability Type: Liability for Incurred Claims." ], "metadata": {}, diff --git a/ifrs17-template/OverviewIFRS17Template.ipynb b/ifrs17-template/OverviewIFRS17Template.ipynb index 8afb4069..95a010d7 100644 --- a/ifrs17-template/OverviewIFRS17Template.ipynb +++ b/ifrs17-template/OverviewIFRS17Template.ipynb @@ -61,7 +61,7 @@ "\n", "\nIf the entered cash flows are nominal (the usual case), the the present value (PV) of each cash flow is computed using proper yield curves for discounting.", "\n", - "\nA central element of IFRS 17 is the Analysis of Change (AoC). The present value of the business (future cash flows) changes from the beginning to the end of the accounting period due to different effects, each of them constituting a modeled AoC step.", + "\nA central element of IFRS 17 is the Analysis of Change (AoC). The present value of the business (future cash flows) changes from the beginning to the end of the accounting period due to different effects, each of them constituting a modeled AoC Step.", "\n", "\nIn IFRS 17, the value of future profitable business has a component called the Contractual Service Margin (CSM), defined at GIC level. The profit measured as CSM is recognized slowly over time rather than immediately in financial performance statements. On the other hand, a GIC can be onerous and produce a Loss Component (LC) instead, which needs to be recognized immediately.", "\n", @@ -151,13 +151,13 @@ "\n## Data input: Groups of insurance and reinsurance contracts", "\n", "\nSee folder Files/TransactionalData.", - "\n- **Actuals** *\\_* *\\_* *\\_*: List of all actual amounts for those GICs and GRICs that are used by the given reporting node/year/month combination, for different amount types, AoC types and other attributes; we need at least two such files for the two dates of the Analysis of Change", + "\n- **Actuals** *\\_* *\\_* *\\_*: List of all actual amounts for those GICs and GRICs that are used by the given reporting node/year/month combination, for different amount types, AoC Types and other attributes; we need at least two such files for the two dates of the Analysis of Change", "\n$$", "\n$$", "\n- **Openings** *\\_* *\\_* *\\_*: List of the opening amounts for some GICs that are used by the given reporting node. The year/month combination corresponds to the beginning of the planned Analysis of Change period, so only one such file is required. The entries are by EstimateType and AmountType.", "\n$$", "\n$$", - "\n- **NominalCash flows** *\\_* *\\_* *\\_*: List of all projected cash flows for those GICs and GRICs that are used by the given reporting node/year/month combination, for different amount types, AoC types, novelty types and other attributes, where the cash flow amounts are given in a regular sequence extending as far as needed; we need at least two such files for the two dates of the Analysis of Change", + "\n- **NominalCash flows** *\\_* *\\_* *\\_*: List of all projected cash flows for those GICs and GRICs that are used by the given reporting node/year/month combination, for different amount types, AoC Types, novelty types and other attributes, where the cash flow amounts are given in a regular sequence extending as far as needed; we need at least two such files for the two dates of the Analysis of Change", "\n", "\nThe structure of the [Actuals](./InputFormatDescription#actual), [Openings](./InputFormatDescription#opening) and [Nominal Cash flows](./InputFormatDescription#cashflow) files is explained in a special [notebook](./InputFormatDescription).", "\n", @@ -203,7 +203,9 @@ "", "\n# Reports", "\n", - "\nAll the reports are produced by running a notebook such as **[Reports](./Report/Reports#report-production)**." + "\nAll the reports of the calculated IFRS 17 figures are produced by running a notebook such as **[Reports](./Report/Reports#report-production)**.", + "\n", + "\nAll the reports of the paramaters used in the calculation of the IFRS 17 figures are produced by running a notebook such as **[ParameterReports](./Report/ParameterReports)**." ], "metadata": {}, "execution_count": 0, diff --git a/ifrs17-template/README.md b/ifrs17-template/README.md index 5e994ebe..e6033e6a 100644 --- a/ifrs17-template/README.md +++ b/ifrs17-template/README.md @@ -59,6 +59,30 @@ Follow step by step our [Get Started](https://youtu.be/WQFn58gFhaM) video and ex For more information on our IFRS 17 initiative check out our [IFRS 17 page](https://systemorph.com/). +## Practical Use Cases + +Interact with our IFRS 17 practical use cases. Analyze your data and create new use cases. + +
+ +
+ +### Actuals outside the period + +Compare use-cases of [actuals](./PracticalUseCases/ActualsOutsideThePeriod/ActualsUseCaseReports) paid outside the reporting period. + +
+
+ +### Contractual service margin : single vs multiple switch + +Compare the effects of modelling the [CSM - LC switch](./PracticalUseCases/SingleVsMultipleCsmSwitch/CsmSwitchReports) with a single vs multiple switch logic. + +
+ +
+ +
## Got Questions? diff --git a/ifrs17-template/Report/ParameterReports.ipynb b/ifrs17-template/Report/ParameterReports.ipynb index b1823e77..8b1c6d9c 100644 --- a/ifrs17-template/Report/ParameterReports.ipynb +++ b/ifrs17-template/Report/ParameterReports.ipynb @@ -18,16 +18,36 @@ { "cell_type": "markdown", "source": [ - "# Data Initialization" + "", + "\n

Parameter Reports

" ], "metadata": {}, "execution_count": 0, "outputs": [] }, { - "cell_type": "code", + "cell_type": "markdown", "source": [ - "#!eval-notebook \"../Initialization/InitSystemorphBaseToMemory\"" + "For demonstration purposes we import here data for some *Group of Insurance Contract* (GIC) and *Group of Reinsurance Contract* (GRIC) - the import is triggered in the [Set up data and configuration](#set-up-data-and-configuration) section.", + "\n
The imported data set consists of cash flows, actuals, and parameters.", + "\n
Input files can be found in the **File** directory. You are invited to change them or upload your own or add new data in the [CloseImportTemplate](../Import/CloseImportTemplate) notebook. ", + "\n", + "\nIn this notebook we show the parameters (provided to the calculation engine as inputs) used to performe the calculation of the reports shown in [Reports](Reports) notebook." + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Set up data and configuration", + "\n", + "\nChoose to run the Reports notebook either with the set of Systemorph data in memory or with the data present in the Database: ", + "\n- #!eval-notebook \"../Database/Configure\" : connects to a physical Database", + "\n- #!eval-notebook \"../Import/CloseImportTemplate\" : uses the in-memory set up", + "\n", + "\nWe use here the in-memory set up." ], "metadata": {}, "execution_count": 0, @@ -36,7 +56,7 @@ { "cell_type": "code", "source": [ - "Workspace.Reset(x => x.ResetInitializationRules().ResetCurrentPartitions());" + "#!eval-notebook \"../Import/CloseImportTemplate\"" ], "metadata": {}, "execution_count": 0, @@ -54,7 +74,9 @@ { "cell_type": "markdown", "source": [ - "# Args" + "# Args", + "\n", + "\nDefine the reporting node, year, month, and scenario for which the parameter reports should be loaded. " ], "metadata": {}, "execution_count": 0, @@ -63,7 +85,11 @@ { "cell_type": "code", "source": [ - "var args = new ImportArgs(\"CH\", 2021, 3, default, default , default);" + "var reportinNode = \"CH\";", + "\nvar year = 2021;", + "\nvar month = 3;", + "\nvar scenario = (string)default;", + "\nvar args = new ImportArgs(reportinNode, year, month, default, scenario, default);" ], "metadata": {}, "execution_count": 0, @@ -72,7 +98,10 @@ { "cell_type": "markdown", "source": [ - "# Data Node" + "# Retrieve data ", + "\n", + "\nThe parameters used for calculation of the provided period are here retrieved through queries.", + "\nThe queries and the data model of the reported objects can be foud in the [ParameterReportQueries](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/Report/ParameterReportsQueries#queries)" ], "metadata": {}, "execution_count": 0, @@ -99,7 +128,7 @@ { "cell_type": "code", "source": [ - "var allYieldCurves = await Workspace.GetYieldCurveReportParametersAsync(args);;" + "var allYieldCurves = await Workspace.GetYieldCurveReportParametersAsync(args);" ], "metadata": {}, "execution_count": 0, @@ -108,7 +137,7 @@ { "cell_type": "code", "source": [ - "var singleDataNodeParameters = await Workspace.GetSingleDataNodeReportParametersAsync(args);;" + "var singleDataNodeParameters = await Workspace.GetSingleDataNodeReportParametersAsync(args);" ], "metadata": {}, "execution_count": 0, @@ -117,7 +146,7 @@ { "cell_type": "code", "source": [ - "var interDataNodeParameters = await Workspace.GetInterDataNodeParametersAsync(args);;" + "var interDataNodeParameters = await Workspace.GetInterDataNodeParametersAsync(args);" ], "metadata": {}, "execution_count": 0, @@ -135,7 +164,7 @@ { "cell_type": "code", "source": [ - "var partnerDefaultRates = await Workspace.GetCreditDefaultRatesReportParametersAsync(args);;" + "var partnerDefaultRates = await Workspace.GetCreditDefaultRatesReportParametersAsync(args);" ], "metadata": {}, "execution_count": 0, @@ -154,7 +183,7 @@ "cell_type": "markdown", "source": [ "## Data node", - "\nProperties of the Group of Contracts and corresponding Portfolios are merged to provide a full description of the [Data Node](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructureDataStructure#data-node)" + "\nProperties of the Group of Contracts and corresponding Portfolios are merged to provide a full description of the [Data Node](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructureDataStructure#data-node)" ], "metadata": {}, "execution_count": 0, @@ -178,7 +207,7 @@ "source": [ "## Data node state", "\n", - "\nCurrent and previous period [data node state](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#data-node-state)." + "\nCurrent and previous period [data node state](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#data-node-state)." ], "metadata": {}, "execution_count": 0, @@ -203,7 +232,7 @@ "cell_type": "markdown", "source": [ "## Yield curve", - "\n[Yield Curve](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#yield-curve) used for locked-in discounting and current rating discouning (curret period and previous period) are shown." + "\n[Yield Curve](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#yield-curve) used for locked-in discounting and current rating discouning (curret period and previous period) are shown." ], "metadata": {}, "execution_count": 0, @@ -227,7 +256,7 @@ "cell_type": "markdown", "source": [ "## Single data node parameter", - "\n[Single data node parameters](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#data-node-parameters) for current and previous period. " + "\n[Single data node parameters](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#data-node-parameters) for current and previous period. " ], "metadata": {}, "execution_count": 0, @@ -251,7 +280,7 @@ "cell_type": "markdown", "source": [ "## Inter data node parameter", - "\n[Inter data node parameters](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#data-node-parameters) for current and previous period. " + "\n[Inter data node parameters](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#data-node-parameters) for current and previous period. " ], "metadata": {}, "execution_count": 0, @@ -275,7 +304,7 @@ "cell_type": "markdown", "source": [ "## Partner ratings", - "\n[Partner ratings](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#partner-rating) for current and previous period." + "\n[Partner ratings](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#partner-rating) for current and previous period." ], "metadata": {}, "execution_count": 0, @@ -299,7 +328,7 @@ "cell_type": "markdown", "source": [ "## Partner default rates", - "\n[Partner default rates](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/DataModel/DataStructure#credit-default-rate) for current and previous period." + "\n[Partner default rates](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/DataModel/DataStructure#credit-default-rate) for current and previous period." ], "metadata": {}, "execution_count": 0, diff --git a/ifrs17-template/Report/Reports.ipynb b/ifrs17-template/Report/Reports.ipynb index 12250b90..d22f25e8 100644 --- a/ifrs17-template/Report/Reports.ipynb +++ b/ifrs17-template/Report/Reports.ipynb @@ -30,7 +30,7 @@ { "cell_type": "markdown", "source": [ - "For demonstration purposes we import here data for some *Group of Insurance Contract* (GIC) and *Group of Reinsurance Contract* (GRIC) - the import is triggered in the [Set up data and configuration](#set-up-data-and-configuration).", + "For demonstration purposes we import here data for some *Group of Insurance Contract* (GIC) and *Group of Reinsurance Contract* (GRIC) - the import is triggered in the [Set up data and configuration](#set-up-data-and-configuration) section.", "\n
The imported data set consists of cash flows, actuals, and parameters.", "\n
Input files can be found in the **File** directory. You are invited to change them or upload your own or add new data in the [CloseImportTemplate](../Import/CloseImportTemplate) notebook. ", "\n
For simplicity, we import similar transactional data for all GICs and GRICs. Each *Group of Contracts* produces different figures due to differences in parameters such as *Liability Type*, *Oci type* or *Premium allocation factor* to Contractual Service Margin.", @@ -80,7 +80,7 @@ "source": [ "# Best Estimate", "\n", - "\nPresent values of the [best-estimate](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/Report/ReportScopes#best-estimate) future cash flows are shown here in an Analysis of Change report.", + "\nPresent values of the [best-estimate](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/Report/ReportScopes#best-estimate) future cash flows are shown here in an Analysis of Change report.", "\n", "\nThe granularity of the reported figures can be modified by changing the Column Slices options.", "\nFor example one can add \"GroupOfContract\" to separate the contributions of the individual Group of Contracts.", @@ -111,7 +111,7 @@ "source": [ "# Risk Adjustment", "\n", - "\nPresent values of the [risk adjustment](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/Report/ReportScopes#risk-adjustment) future cash flows are shown here.", + "\nPresent values of the [risk adjustment](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/Report/ReportScopes#risk-adjustment) future cash flows are shown here.", "\n", "\nThe additional ColumnSlices are added to the view as the inner column. This can dicrease the readability of the report. For example, adding the slice by \"GroupOfContract\" leaves the lock-in and current rate contributions far apart and difficult to compare. The re-order of default slices with custom slices is achieved by esplicitly add the default slice among the custom slices. In our case, you can try entering both \"GroupOfContract\" and \"EconomicBasis\" separated by a comma in the ColumnSlices." ], @@ -138,7 +138,7 @@ "source": [ "# Written Actuals", "\n", - "\n[Written Actuals](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/Report/ReportScopes#written-accrual-deferral) are shown here. ", + "\n[Written Actuals](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/Report/ReportScopes#written-accrual-deferral) are shown here. ", "\n", "\nIn this case, the analysis of change view is replaced with a default slice by the **AmountTypes**. Only the amount type with non zero value are displayed. ", "\n
Filters can be applied to reports in order to isolate a sub-set of the data. They are specified by the name of the dimension to filter and the system name of the desired value. For example, to investigate the contribution of a single Group of Contract the following filter can be applied: new [] {(\"GroupOfContract\", \"DT1.1\")}." @@ -166,8 +166,8 @@ "source": [ "## Advance, Overdue Actuals", "\n", - "\nActuals payed in [Advance](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/Report/ReportScopes#written-accrual-deferral)", - "\nor [Overdue](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/Report/ReportScopes#written-accrual-deferral) are shown here together in a simplified Analysis of Change. ", + "\nActuals payed in [Advance](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/Report/ReportScopes#written-accrual-deferral)", + "\nor [Overdue](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/Report/ReportScopes#written-accrual-deferral) are shown here together in a simplified Analysis of Change. ", "\n", "\nCombining Filters and ColumnSlices facilitate the report analysis. For example, you can select \"GroupOfContract\" as column slices with a Filter on EstimateType \"AA\" to analyse the Advance Actuals for all Group of Contract. " ], @@ -194,7 +194,7 @@ "source": [ "## Deferrable Actuals", "\n", - "\n[Deferrable Actuals](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/Report/ReportScopes#written-accrual-deferral) are shown here. Amortization of the deferrable amount is computed using the Coverage Unit pattern. " + "\n[Deferrable Actuals](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/Report/ReportScopes#written-accrual-deferral) are shown here. Amortization of the deferrable amount is computed using the Coverage Unit pattern. " ], "metadata": {}, "execution_count": 0, @@ -219,7 +219,7 @@ "source": [ "# Fulfilment Cash flow", "\n", - "\nPresent Value of the [Fulfilment Cash flow](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/Report/ReportScopes#fulfillment-cash-flows) are shown here. ", + "\nPresent Value of the [Fulfilment Cash flow](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/Report/ReportScopes#fulfillment-cash-flows) are shown here. ", "\n
The individual contributions from Best Estimate and Risk Adjustment can be visualized slicing by **EstimateType**.", "\n", "\nFilters can be applied to report to isolate a sub-set of the data. For example you can Filter by a specific Group of Contract using its system name using : new [] {(\"GroupOfContract\", \"DT1.1\")}.", @@ -248,7 +248,7 @@ "source": [ "# Actuarial Experience Adjustment", "\n", - "\nA comparison between [Written Actual](#written-actual) and the Releases of the [Best Estimate](#present-value) is reported in the [Actuarial Experience Adjustment](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/Report/ReportScopes#experience-adjustment)." + "\nA comparison between [Written Actual](#written-actual) and the Releases of the [Best Estimate](#present-value) is reported in the [Actuarial Experience Adjustment](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/Report/ReportScopes#experience-adjustment)." ], "metadata": {}, "execution_count": 0, @@ -273,7 +273,7 @@ "source": [ "# LRC Technical Margin", "\n", - "\nIn the [Technical Margin](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/Report/ReportScopes#technical-margin) report we present a unified view on the figures that are allocated to either Contractual Service Margin or to Loss Component. ", + "\nIn the [Technical Margin](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/Report/ReportScopes#technical-margin) report we present a unified view on the figures that are allocated to either Contractual Service Margin or to Loss Component. ", "\n
The Analysis of Change is expanded with few more steps such as **Experience Adjustment** and **Amortization**." ], "metadata": {}, @@ -299,7 +299,7 @@ "source": [ "# Contractual Service Margin / Loss Component / Loss Recovery Component", "\n", - "\nThe Contractual Service Margin (CSM) / Loss Component (LC) / Loss Recovery Component (LR) [report](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/Report/ReportScopes#technical-margin) are here shown side by side as the allocation to profit or loss is done at each step of the Analysis of Change. ", + "\nThe Contractual Service Margin (CSM) / Loss Component (LC) / Loss Recovery Component (LR) [report](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/Report/ReportScopes#technical-margin) are here shown side by side as the allocation to profit or loss is done at each step of the Analysis of Change. ", "\n", "\nA default slice by EstimateType - which distinguish between CSM, LC and LR contributions - is automatically enforced. " ], @@ -326,7 +326,7 @@ "source": [ "# LRC Actuarial", "\n", - "\nThe [Actuarial Liability for Remaining Coverage](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/Report/ReportScopes#lrc-actuarial-actuarial-liability-for-remaining-coverage) report shows figures from Fulfilment Cash flow discounted with current yield curve, and the allocated techinical margin. " + "\nThe [Actuarial Liability for Remaining Coverage](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/Report/ReportScopes#lrc-actuarial-actuarial-liability-for-remaining-coverage) report shows figures from Fulfilment Cash flow discounted with current yield curve, and the allocated techinical margin. " ], "metadata": {}, "execution_count": 0, @@ -351,7 +351,7 @@ "source": [ "# LRC", "\n", - "\nThe [Liability for Remaining Coverage](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/Report/ReportScopes#lrc-liability-for-remaining-coverage) report adds to the [Actuarial Liability for Remaining Coverage](#lrc-actuarial) the contribution of and accrual actuals. A simplified AoC Chain is used to allow comparison of the balance change between Actuals and Present Values. " + "\nThe [Liability for Remaining Coverage](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/Report/ReportScopes#lrc-liability-for-remaining-coverage) report adds to the [Actuarial Liability for Remaining Coverage](#lrc-actuarial) the contribution of and accrual actuals. A simplified AoC Chain is used to allow comparison of the balance change between Actuals and Present Values. " ], "metadata": {}, "execution_count": 0, @@ -376,7 +376,7 @@ "source": [ "# LIC Actuarial", "\n", - "\nThe [Actuarial Liability of Incurred Claims](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/Report/ReportScopes#lic-actuarial-actuarial-liability-for-incurred-claims) report shows figures from Fulfilment Cash flow discounted with current yield curve. " + "\nThe [Actuarial Liability of Incurred Claims](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/Report/ReportScopes#lic-actuarial-actuarial-liability-for-incurred-claims) report shows figures from Fulfilment Cash flow discounted with current yield curve. " ], "metadata": {}, "execution_count": 0, @@ -401,7 +401,7 @@ "source": [ "# LIC", "\n", - "\nThe [Liability for Incurred Claims](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/Report/ReportScopes#lic-liability-for-incurred-claims) adds to the [Actuarial Liability for Incurred Claims](#lic-actuarial) the contribution of and accrual actuals." + "\nThe [Liability for Incurred Claims](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/Report/ReportScopes#lic-liability-for-incurred-claims) adds to the [Actuarial Liability for Incurred Claims](#lic-actuarial) the contribution of and accrual actuals." ], "metadata": {}, "execution_count": 0, @@ -426,7 +426,7 @@ "source": [ "# Financial Performance", "\n", - "\nThe [Financial Performance](https://portal.systemorph.cloud/project/ifrs17/env/v1.0.0/Report/ReportScopes#ifrs-17-financial-performance) report discloses the Change in Estimate of the IFRS 17 balance sheet items ([LRC](#lrc) and [LIC](#lic)) and the relevant incurred cash flows (Premiums, Claims, Expenses, etc...) for the given period.", + "\nThe [Financial Performance](https://portal.systemorph.cloud/project/ifrs17/env/v1.1.0/Report/ReportScopes#ifrs-17-financial-performance) report discloses the Change in Estimate of the IFRS 17 balance sheet items ([LRC](#lrc) and [LIC](#lic)) and the relevant incurred cash flows (Premiums, Claims, Expenses, etc...) for the given period.", "\n", "\nUse the expand and collapse buttons in the report rows to change the granularity of the figures displayed." ], diff --git a/ifrs17/OverviewCalculationEngine.ipynb b/ifrs17/OverviewCalculationEngine.ipynb index c4d85c77..482b71d8 100644 --- a/ifrs17/OverviewCalculationEngine.ipynb +++ b/ifrs17/OverviewCalculationEngine.ipynb @@ -189,7 +189,8 @@ "\n", "\nSome reports of ReportScopes just present existing data from the database, namely the imported data and the results already calculated using the methods of [ImportScopes](#model-calc).", "\n", - "\nSome other reports related to **[Financial Performance](./Report/ReportScopes#ifrs-17-financial-performance)** (profit & loss) require further calculations provided by ReportScopes. An IFRS 17 financial performance report has at least four sections: Insurance Revenue, Insurance Service Expense, Insurance Finance Income/Expense and Other Comprehensive Income. Formulas are provided [here](./Report/ReportScopes#ifrs-17-financial-performance)." + "\nSome other reports related to **[Financial Performance](./Report/ReportScopes#ifrs-17-financial-performance)** (profit & loss) require further calculations provided by ReportScopes. An IFRS 17 financial performance report has at least four sections: Insurance Revenue, Insurance Service Expense, Insurance Finance Income/Expense and Other Comprehensive Income. Formulas are provided [here](./Report/ReportScopes#ifrs-17-financial-performance).", + "\n" ], "metadata": {}, "execution_count": 0, @@ -220,15 +221,6 @@ "metadata": {}, "execution_count": 0, "outputs": [] - }, - { - "cell_type": "markdown", - "source": [ - "" - ], - "metadata": {}, - "execution_count": 0, - "outputs": [] } ] } \ No newline at end of file diff --git a/ifrs17/Report/ParameterReportsQueries.ipynb b/ifrs17/Report/ParameterReportsQueries.ipynb index 8f0d6f4a..229d5ae6 100644 --- a/ifrs17/Report/ParameterReportsQueries.ipynb +++ b/ifrs17/Report/ParameterReportsQueries.ipynb @@ -15,6 +15,15 @@ "nbformat": 4, "nbformat_minor": 5, "cells": [ + { + "cell_type": "markdown", + "source": [ + "

Parameter Report Queries

" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, { "cell_type": "markdown", "source": [ @@ -167,7 +176,16 @@ { "cell_type": "markdown", "source": [ - "# Data Node" + "# Queries" + ], + "metadata": {}, + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Data Node" ], "metadata": {}, "execution_count": 0, @@ -208,7 +226,7 @@ { "cell_type": "markdown", "source": [ - "# YieldCurve" + "## YieldCurve" ], "metadata": {}, "execution_count": 0, @@ -266,7 +284,7 @@ { "cell_type": "markdown", "source": [ - "# Single Data Node Parameters" + "## Single Data Node Parameters" ], "metadata": {}, "execution_count": 0, @@ -295,7 +313,7 @@ { "cell_type": "markdown", "source": [ - "# Inter Data Node Parameters" + "## Inter Data Node Parameters" ], "metadata": {}, "execution_count": 0, @@ -329,7 +347,7 @@ { "cell_type": "markdown", "source": [ - "# Partner Default Rates" + "## Partner Default Rates" ], "metadata": {}, "execution_count": 0, From 644314383b2307630a92b87a01f980e3e7adf1b0 Mon Sep 17 00:00:00 2001 From: Davide Colleoni <103409906+dcolleoni@users.noreply.github.com> Date: Mon, 30 Jan 2023 15:11:29 +0100 Subject: [PATCH 9/9] Fix pv ep2 (#207) remove parent from RN in Dimensions.xlsx --- PresentValueSeries/Dimensions.xlsx | Bin 55843 -> 55837 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/PresentValueSeries/Dimensions.xlsx b/PresentValueSeries/Dimensions.xlsx index 0fd16d7d7d77e15c8448c872ad09f6b6f82f5017..44ba3f6dd910ca2fdbdf260a0e1641ba892543ea 100644 GIT binary patch delta 24129 zcmZ^qWmH_<(yfu;8iG5)HH`!(xVyW%y9C<=cXxLP5ZpC51c%@noZu2%Zj<+%^POvd zdhF4?T1KzhRdZH7tDqh_s~);K84h8hS$?`21_A;zAqNf@(6%q&#Prp-e1SH-O#c-h zsPoeVQ)p7fif6Tcd2uo22MqOjvVlpiEL-}^r$HT*Lh1BxS~QIl?w!uwkBbg>pSvy# z)klSdp1xN2D48NWpt^>2rTqZ-7FOdJg5F-}ZYz?=??9vOOw_FdZQcb(J89E62oR4B zboK=_sWejifXz8-P>zM4&%UN|qwrha+9(WIWO9xbBf?`|QFT?}MY7Ei+Md8Hf5Ca{ zO@j#TbJPb##GdMq_qil?lIXh@JbReUL`EMRCtdfMAVSSIDYSzn5=^{ zJZ<<=Z+>R|J*T)wbb?P%y>~X6GcwqP$Op=Tl5#avA5g}UEiW`!AMLi3@Co{*-m_P2 z&>zXFi3gLc=x_P^MQkRwK3*$L;9^{a0CUCVvs()lv9FA@x{#nlgqwFUujYjUE~74n zp*tPguei=%wUGHnjs^pL0eU{5Iq_N+`X6`Ek`M%jZi7SgPN>yXCd2*Km|j!RiEbz8 zODM(_Xh8FEw(P~#x~Po*OI3w+u0xF87xchmvp=3N#0_f+r`2KCSG~j&jJ>JWy5H6b z+#Zt4++}6w+wL7qGNiQ$@OJm9wpxYZTP|W!r`{em*SR@p{|Fd=!s*q?aG$U{Ubo+&#gkT9zSm17u6U$SF;pCX1R(;mffBsJBs4+asaJAgv9QOypSiD6iC(Yd&;ICgS0tqH7MIEiN+zLgo0_t^2h#K{ z%>tt<^AL`DCdD7n<-3z`-4^^96}EKcyse ze*FegVG8rl=Bl(;coxJX707V)20nu%vBJA`-X(@>Mq!73HZ=K}rSpyi(9Lj!zd;YI zA0-&oGGynCZ-)6ZZ}P5ABp62c(`t}m?Wjon2n5{%h3`$dXaF+&jvpR?c~3x*cMY2Vl~Ah6RJ zi}v6knP9R`i7~i*Q^Z97*|qR%`OZrehTVV0?8{LW;{^vUTXAh_|Gi&pg)IIrZ9d;t zfIsl<+NyIm`Uoc+ZaYZ&p`GQo@Cj#$!^*U{^4JK_ zXhgRPpRWh4EcJEh`~@Rie_RU>d>vAKGW+2^#msa6hH0reOC*?_g~ni)(99Qc)ZcKS z=yTc`QTKXgLJaJ{U|oEK@T||8)~=}S7$E(HBA({U#b`bB$HVjC!k@iEDduCH5z+n5 zcCMb^kk{clqxfRVR~_M`PYf0ha<(>t@29RgZSj{kU5-TGLpt(nre)m)Q$OYM_{C2eoW@@Q6@x zRF=OoD5=hQ*0tymda+a}JkctY+Ps)o<0mdQT(p1oS}QwDH6jNjJ^5U8QqD|y{ly$w zES;Z-6y-QiU|$_HAs^v3*V(5L`(fa4LPOb=^QfBx4{ka48fkyyw8;Ux=V6~aGQJIy z70TXt*TAH#qh{@RIeqom+PJ|wU95|z`e@CdR5N1d`>uV-S$E)+pS*zzK!#npAA+B@8I2@8KAwYEaVo66tkOU%O6M#fe=)T%x= zM47#nm|w%ypm+^l*7*bA%+TPz4WU*f6f;`z;wnLX10~ta!#v#?j)#2)z7Yr-T-~M|Fi4}O9m75R%zxZ-)ONBCqh~594g$RUe1y1 z_!5`%m~r!@KBANI_t?MvKMmX&i7a|!-|wXac6hyeH=({R_~p!xn3>YYt1@!&3MuD& z3O$U8VLwdAqa^?%knsPi@+fJFv{r=FGmE?)b*xf)QK&xB%`8PMyLX5(EUq%gf(~#CTcM9#V9% zs%Zy^yKm{i@ft1gf*9(+ET+<*>*#^WFD{1e-yg7J?!*SI;fGtdBeU&d%zh(K%eJ>M z_k2|@{Oq*j?f!WY-3M1$enGCj&*Eh`n-p38?Ma!fMMTHOtX3?!o9F)aPw8wiMP^w)Sb!+Zx&!wRA1#?tBH+n%b;5?2zjzM;ph zYjmErTK9q5o0Mv?P(etAJbW=5cuwOZRuOhHsQ76sS0E^XP%o5HtGjo#7h?)4>f>HZ zZl7Y@zJgEwG)~evdc9<0gR%jSp_MVS)WT#u8jg?I1idO4L7&7P&g1>j+KdOf{p$T} zH77DKDC4*|{f>0i9AOHlUk+z{`Vnt7~0FNeS21bq7|$tR&lhP+s##b921iUKi3k z%F*v~*KoJN;r#PTY^}AM91CX&)#q4q_9??aW9{g{ddX%BZg4+EYT%4$Y@xhlHGD|{ zsc-hcxlx>q*wiga)`V0T^@_YGvI>{3i00d(#4+&Z1Vam610BIFGo_|Ir{t3ryHlii3;boTY7(<35Hyc&)fy$IRw-+ zh>@zHh{A&4YDy4SNuhE?hM(t!#Loj_yfjjLAkG@26m!ZB(Z&I`w69){g)SlEpi6S;25Hn|Yao%0$H3Pg!qH_!xDq%p zrBX|+KEWQ7Vfa5cvRzb&!d$fd%1XZRG=I2#lKV+>=%pC&^Ji|E)!{XT;zHPu4@^3T zX76X2OYaw2`6Ls~^Do2(8lpvhniqE{Apt|fRTGMSN@#iq3g#=CbXS!; zYmtovn#C+eLR#PJYYHUwyb!)Mf7g~Hg~ZSRSFNWL9IrmrNWhbMNslnkAQ!jowBN#u znfUjYCCe&NuMvD^=>7#F5?tF4&ny}d8QTpOaByawJ1>y`m97L|)0OCJx}pUV=L+*E z=>8DDkm)W2qy7<1rkd{4DyHF2_X9dF=a z0o^Bc`upzyo3yECbuWJ-jc|~@%(puuwj02WDee1_5=&SbW7_*D@};?*xc?ZjAkDac8D%iM+cu1pM84)viQ|k}JteQs zr@{{qb;Fv&RrBSvYKmz=DDK9$i&*sTG9V}8szV^55NzJ?*b3>_%n10#=?)LyFy^?UlknC^vmdL0eDw_9Ov)D1^~~zJk3D#%CnAx;5f>;H zQB>R_AY&EAZDoMjA{dJfpFe?R)=`4HTb5#LETLt8Q&P%BnBiwR3t>cE=%9|)FHv)3 zs&6gqj`6!w1i^IQXe0a=@eTX)LqGz?s71Ln2NX-o&ZEY2<|u-x`?T>>07y{?nLo@8 zTwu?^1(wQP?Gcfn>s|7i;jvTN5kM18__7!{W0_gW(arsN4OTL}(tpt;9k$$r*9+Br zYnCqRWpgJVy}H*{b?A&`u$<-rvnZ7Tr}K+1qHm-9KTn!qx?rsJx^ zNG^4^C#Jj>-3$i$nTS+<2$E;Go;Pnqk1EJtbj|UwF<;cMFM7`MPkF`>ItTOPK|ear zIj-EU0Q1ko*GmW6X-l$e%?UVy-rpX^p;;c>8X0a{p&iWI{MAO)=L6_o%-d%G^ZCOc zwEv0WlvTUsglrOcAns)wv+~QL)Fq)Hwt>2>BHY3Cb;0r6=FMB!;h&vhazkKwNLIO6KNxETMs*~i8W?4bJX#tw;H&dMuh6v7iR$Q+B_B_(}zb64e|m)}Gz38Y6V?#QXfBNZZZq!=&M- zQ;nZ~^^xu1p~`pJqM3>kK#0CmPP&0GPMQ!lf{@r16ixu@wyRKu7ha5|o-qh7_c|em ze4t}5=O?!Tor`tuvlTMSIjJkFjj?(dDRH+{wea3>AjULL8Q>r=-JGs`<2mVhGIOA- z=udz}OADK8Ruv?NF~}HJxOj3U@@M6nM2l`5K0-5I>W6c_rzVE@;^vv&1?P$PMa3ur z$7UZAR%~61rPFtmT^JBgWDC7hpsbjbg-Y@rGKf;yJhQSb%d~fmE8%|2S{|D7alv}b zX)#>RQ-95X%M4($jFy`^wBzt@5tcgUfoQAy*21f)?AqZtl?%8WMYEB`Rba3z6e`$5bzgm6zKU#g^RjZG)G1~p_ zTD>MjDPQ!t-7ac}KFFD5Gx1fcQ+llDj9jf((OJ?~I2K<+Q`9L>%x}pr1UfpB=}rLe zsN~XuvXBXGgQY&rD`o0x0K)`F&k5C2Fxbf>yV{yqSEm-7<3EnbA_nwvEE7K!T3)eD zP$|YU434` z+0M<>jgUx@OiZDfi&%jEMLyq@Q=5j?cmG&Vty2(f-UDVlg(a8ft@d8j^Ao;7PkoX${hCD$6(_o?e;i~%(Td0#ejH)1l_Y*l zEnG#E9uMvM3}A)sy+5FUyXuL;(o*G2>&JEMccK5;*~8z-F@;EeSZZ~Ui&b@&ru|rd zcWdbLbJxIYoCwOl7V9L7Kw605u%*zgSiJK4j3xQk0C}K}+Knxt9`~3b9vNLb` zrYLcnLk+lx9a%%rUNP`(li><$?CDDRoIvFmFZQRADgUl294d8Lf|E^;x{6 z+h=C*gU*o`Tt|*@{Bz>SwT|;hZU+Rbw6naCryrlgnV8b87u2z!d^fS^p=WBh?{Twh z)EQ0Wf=O0#SLHbU=KJ6zLF8+?@m;hCL+SwBBUNOatCug563-tX!28Yjf9!RXHXb8W z37QMk3Xxc0Gju8L397U)TS%@%xM+~Y8 z{26JuUxpEB&vr_lHps7UMXF70T6l_iu zVUF2V0JlWR-Wcp%-H&c*_3uoBU;2J|W^E5srB*e42Kg|Algfwlh7!jcX)|mdGs!(~ z&emc&^mNjhcqIMemExEpOqi{;&&BDMzwY9?cxs-%-zSV^ZZ{@A=%ch z2ZSzCS3-ffu09%aLO`X&MV@lPoIz$4d$}rVy2%GJ{VO%0_~65)C?}VNft62UY1@jcjMEHt}hT zyQFmQ=B4hZVSz-6lTd5%B~Z_%Sm7K_5R;Ee(4QWSaS)6dcTtp4Ro~I$Fs&U^fJ!jP z$n}0Tj$FxYWtY&MSe%GGnh>EdF^m=b#17#N_z6A2pmza@@1X0A27ShB&N+WqJ8ln= z?hHZ55@uu;s8pKQ%MnP+jkTwC6B6`=m2Ap5^r=&wZR13bx2L|f+&LoM6}M5zeCLW4 zzA%mc>k)kyf=o`du86#HF0CYm5lK>*TuzExTT2mV;<&Wj>&F*fY1j|lkSOPPYp()5EO^#j;Vf2@u{eim=C5JzVE|>3u+0dvnl2#!f1l%S<5lip}_R0idD%OOleMBxCz;KK5_B_51!NQ3W8G zn!2Xwxn3;w54mLi;CP<-Kr9mBT3fq#6 z9%~#@GrA}~`Vul}?o$q;4*Zdpt*7p3g$7!9s!I?<%~f}uPsrw}ule~!5*NdGOuh#N zID^6J_(l=GqA`VZ^ed|R3f6hK2uECTd*40YmONfWn|~R7bMS!sa{kFPgG~fi8=x4NAVb>vC>j(@0;F6NKQhTUbL=DZc`m zq$otq?^8@GgM^fqM|5u%P`;X8tfR3>Dqu^cmdqSC7g%&OZt5Y68;l;HyQ_N%LoOwT zGCm+RfE#*zAp=FDLb>_Hf=C@xg;tf3!r)*RzQoB!2QldSRtq!8xLIwwz{BzYu)Vz0 zb|jCB6A*5nZG1deO;&vNkbk|5M)BZ1g3aH*TW;xxcFUaTLHd+u&^9u|99WfK+_E^| z-(d*i8vw2m+(RdwPf9hIV_XGzelJVD={4OG?Sy56hx%1j_IrCxdo$-Z>n|%$&#j2% zZHHBRIrA4KAM!L;p9lcz>_p;WKq{P`qF2FypO(u$m6L@c=I&Zu$I`Wzr*u(nbsa6fPHlyT{@c+DhIRoo4iz zp+>tVjwF^L9^KlnT`Br4`rZAT+2Wt}Tk|9qGLWTDGz*Rw~L>Z<>^^L)m!G%F* zl$8N&*! zh4E;hZcd**N{KRxl>u>YLYW|+ zq`(eJ!#5P*<&=}*PRR<=5M^#}EGmlq3wa6UG3ej`;z+&a?QU;^tSn|UR8>UOWdFCZ z{?p!{HZO`JV<$ufc(e>jC}#=$3#Gomc_GYdZL3nD?Jn;hp}^=S`Hw4spp+ESClEb^ zAxfi*1(Kj78=xeUp#J?gHch=%M8M9ku8gp=mKL(Ss?kpf%Ikvnl9-T4g6p&5f~o*y zA=Kz}mfF}_*;?3QwkVK~!t6Ue8D?rz_lS}Hz7YDjQaqZ`!tPI-z6GsjO|I}Qigb62 zp3U6wt;Njw;xOa*i~@}D7QAa%E3X~Er8)jn7F|q0Ew4J_4UW4E4yAeW(QA&f5~GCPZ?ySA2Iaa=a+vcwd->gbY<2i$UL+?rJ}wQ-oa<-m8XBrJC9m?(wwQu`6?ym1s4``|Z= zD&K$9H#jvo{It)Ds%l)+U(D2%G8h$P{r*Tq>?~Q`ej!92=L7WTYzRFEsPk;#H%^7w zstmt6IKlTh>+cpc6eZHKF8Og+DYcP`!&Ny?;9NQTWkUoyiMWaUKZal+NBeu zo%2Xu+JuYxAMW^L9_wsBdO#xnf{@ON2WK%Z?AI0+JO?HKCn51VB9bilYdl_kXLst- zxTNZOM}Tl`+;>yx-vVeU=gztJ zk}Amx%&V^~&(C`wOUK5OA)-n18u1vjtd#~TXapU3CQ*KJDq9pZ_iERfHZp&A{-A^v z5!?#|(lS|lk`)%C3F8D|R!fe}ep3Uxx3b}Ci9;U@7as3>-YskVmq)UM_#TULBn||AIdb33j9DChHka1yp>$gu>&i)#9t-~9m$SltEt8w1neYjPPHT&6(jztrZ=W>rR+zE1iHSez6!|8okBIivk zT1%84CqADa@&7RK4S50gApTY|;N>L|+{#)K0GBeDJ4nPseg~0QD;~=W+|c)b=)M_= zOgEiY%=bHwS6JSlas8d>!>oqa zKh1?LT@Fi+sd5ORmIU}yc5^VOgK)}RNV)l;D&g)M1_{@gzU`vqtSB(+FH~{E$oOMv z2ou0sNwVgb7Z_;7b=O;U3yFT*HW+NAHNfBvI6tyhEV`SsEii~RMom>?)F>_GhBs)# z1RIH~6ssa()2Sg<Ilq(YZ=Jg~?Xc63KmK3Fk*cJPthJI|u7FJ(K6Ff}WjxQH@MuT;)tVz3gujv+^n zwdZDDgb|Wzkp`}@BwuqnU+01ildJWM!cQo0;o@BUN|~`AkDL^vW?!17OPXTlCW{*C z&4=CKccYtR@=)1AslO8+34Yn{s6v8W9K=cH{AMclV;Z@~y6$i~`9^$~(~^PYSKu)R zhQANT^f2P9S0992bf`OeR2pRaX|2iF+sWICKacBAXb=g*JUfVA;}jX-!OfIV*n~J? z{Df)VzeB`Y*dP^CJgw7WEA1*GXLz;hD4Q9I^y_tS7tQ5w7j418JNxoaZ99Kybi1GH z^u9k;LMp~&&rcBf>d3wM#diz8dAK!y- zj?XX1!n5P8`|Qt%i-R-m`ov)vAF1gPXXs~NdSy&YS%*aL6CleEpP_G%r^`+PnCMRO z>zB2Ke2yP4uzG5UtmTw0V95p|YHM{yr|IOT4>mvOGFpedLsfl?Br8Y~-#$vC%_#o# zm|X4tax59UHIS+c;x$U;Bs5hwFISA_XZV`;OL#A5^qFVK_E7PGRfa`|t18CY0cs~G zRgxQt!jKy|L_Xi-R$hVl5me~~Xg*-OFI1s$>JR(niy)5^{CTtjx5_flxA!9t7x5l0 zJ6Q?d-NOBkh@O%~>?K8q*(e_(%pJ%EEPjo~`=A07=~YE<-mKcTWhg^x#ojjq$Nz~Ewli`eSDk0C_AS~^?0v5Pnz@mS(&m|X_QX(jC9#n5L zuB`iB&MeWukS@0;I|6#846v{BPtE)5kFavuBP8l}#ysCBZOMK%qDFjvw&-A_%_0mMxMM{vEY zyLh^bQTANF&)dNGM7#YHn)5BJ)4T^J;IE2mJYJ>@DVN)c4zPyBlYIp-7WYysI^8NN zkUVl$!fD>Yx_cOCJ9H*Ohht3f*#A6@IQQp-E7-%0Jf!3t@QVpp)cDT-U!+9hDq;W# zSxjH_9%ANRJxMF|_Wfp{Zi`{Rt$kN>6gwkUGS46H?Nm0pKd$L>z8Y|pDa+(qS_7Je zr$BGTuQEx^tL-}wdx|>$fn)l7GG$4iSePV%Q4>vu13`T1 zdq+~?C_Mva>(4yWqs^QPn1VHe3*$KtzwK6n_t|TW|lUa&Jo2eD{o(%Z4|BRO~8AD*~X1ED+*%#`ND-bwNO4tjj zma9UV$8l3$;wIr{%7oK&9@KT>d0S3Nsp4JrSkC1O+47B#IF*?j>04ZqJq+g;zOb)w z!JG*}HW8q!;tBx?FV=(v%X!K+Mz?Q-$+%__@R5 zmi%z*;BIjJ&YLSszv=@JG22wHdE{QA^Pn5?M<3_Od%4POWZAcg*T*Db>`h(37={^Z zq&1o@-8vDAC~3njDy{ZFU!uUCh%$6InNVJTus;=_V5{DY(=UzmZeGj*G1SP`pqH*J zEgM|8B?XzZx5{QZI0Nj~1}h@C4GSQ<(6=+Y}d^Il*pL3VvOvO@_nHP z$+*l#cVjU@HQ)F^pG04mNB3FGd<=pkl$y!NK{SGf>$82M7(uW(6cs_61B$gvbJWIN zK7@=k4pfXlA#eIv{8Ir=f@n(vmw*fOgU&_9x*@M{F6*LKM&@({(s=Nz<4MLfF}cMk zwZm=9&#nXmnKr=l`$B*eq^&)xe{kme+^D#8kNi{ucw;oJZ{$RGl0!T#_nj@4I}A}Z z?>?4=#E|=Vta%*_i3?p1&ju%Yg&EmVg=-4n<4i~UQ*7tz*h=W~FM4bVI95Y{!;g;+ zp`gguu@zWM#;jwabt_aAo34oqbV)2nq2)<1N~Y?MuIC0Mxs~E$lbjmJOr3=o3an`e zk0r{}3Ja(ZT4>cdl$}EdPpH?6eEQ-wquilv}N##K_7B6!oJI198 zLQVpIXw#?u3Z+Os%a4gJm`O(S#O0+XYo)CZcZO!~k6xcwRr$R-ArvmPL28($G|JMy zkf0_C6h-GNNhr3oGNz|J4W=875(O3T9Sl-_EuiUegR69ES&D6q?pbjCIUh~e1+jAd zb*mK9Nu`p#u(AN;Mig{I**SC2>wH=gU7$3i2wr^Aq#Uj*{>vhLuc83Sa!`?WyY%&6 z5jQ;I2LDZ94akb0H}ok2$U2x#-lJJ`&wVunmN{ zlNc>3^^V~KlK4(PA)@j3annI^?ZxPT(CeqL6A4BCn6KFm*`+k~PM{U|h z`u<^_J6&NDngdIHPC#;E<6!3Tp=oidHOn3XO0fGjo}H{7##oOnw}oT@8+jb~IcG_H zHfNh=PjprWNYW<{IF7TW1nUL%@EI+%CPS&I9eqr75G14_c6jQH`_^Tp#oJ?f;`YFf zq1Gf}QyBkir6AEl5j^g6^)9GBRc6`r&m<(K&F-~Md%n}9WDKfXn=|xdX{!J?RB1C< znCzx~UeK6_L%BGI?4bEeudD%SsoIPrUxVHcMt3NOpHk?rYv~TwO|Rqkh8?i_w_LHK zk`p-7nUZHL5>YPCO_=p~yJViH?keJ&dv(?O1a+rHl`52e?ACqa2Pg=iN|+Ben`_+W zW!sI?aAD98MR1UN&Yk=yX;i0OK4dcx$e~{QQ@_62`|)ZP$O1mJhmk~25VK#JNP`fr zG0*Pbd>~X4GS@`lmKGeN8dr26m8{m$@r9WP3eSIjHntaLc1Miyc}62SgWxm{SF5WG zC6UcYGiLriIB9E!g|ym=rbbot@MAsUM%o5Zj_65^8Z5~u;r>c8wtpmf~Xex zzyq_&Q>bE7xX>tzARbikz-&an5j-%n%sUZIR0u|et;9V!ryMrs~tSPws~Kfw<39kC47V?q*dHW|6wuBP0Rvmc6CNyG>>-qU`(Wj!lD>*_82F zBE=Tza6BkgwoWp(C}xMnCtYT6=l+A$9bLBxGs*5=(}vfBWy~X| zUH#hUUZg;T4*HgJU9_#+-rVr{dizy*@buhhnKShXGybY1Q++UA8+J<=CRH)h6Jv%k zFn~I~=Y<+XdilQ@)81Y=juG} z`5E!zU*HlXv1*7I4yc?ljgWr=-qCnxUioxm;{+-#hBwH&_gqz7B^n|e^g&kXf3wMP7b$2EeQG8TwOu!(}zrEYAuD8C>b;WpE0T?+{?RxVAm6=o!DPx zF#g=*2WCooHhfD*kYTzQq6eE%iNM3VKA}Orxl^shHdfg%ZHMsXTpH~d?bLg0y*K4DQi#@>mXGkmM=F=D4>w2IuN213KNN;K z9_%d;Ed>_zU<_lJ$L2zE;R*_A(DX8^0_onoCDWw5K2y}RJ-mR8zOJ-ALv5-sQ?>ke zrHuJ-QK6{Q@fizovdjPAhaG^NH=wEH_o!_(*G1k%9<=4Atq9=5HGAC=zag8vxE zwsHQ9C->Nlxq@p}hQ9_fX5-BdBrfIxidP3I^FI!oRmglt7@pn70hA)dYCm2;kT0b_&iY_N!qubA_%oe7I}PZBYL zimA)v?p@S3p!Q{igP&3c@`~E?dU^%odnJ>CuCdghrH(qjpaX=1&Qz((ev6Y%n+2A) zc+|2kQ!g)VG=#ciEwQH6ceXqyHHw^zDu&BKNuK7b;F%nvc!pH(JtFewp3f{z(*sO%6(g$I7U^{)g3cUljBL$Hc;zjFUspl%S{tUPU{WXF z;s;v_3g256aFanL3G>WCq$Ka0Iuxd#Z}-${v6WReLfavI*>)Z^aEkH$oRTDPl$U4` z2k_bB8{cUeu7-Gwf`h~x!&`h0^z>3Lp#=_-uaD@%M~RoNceK)Y|LQVqGS^in{DN3l z7~WF~r^ZpcfF~4aEV6o{c>?jDONmZqgNX_ z=Wk6-!I0-v5oG{#$^)8l3XQ42&EFVVTcj9rv>jz<;5AN#M?l34B}MLWT6n(FR&yHh(TlIdhkmD)lINM5}v&byD_f|L~51frgwu1{^(77{X`?l7B%R z{e08rzo5=w2ZK)(_O%Dfc$JBO;(PABDBsVzuiXbn^V3MAtwlVq`x@56ceiN&e^Lhr z)(&UVd-E8IE_m70GTICeRWj7}9$r7eL3Id*$ct)tCxK6m3h0goXN+L*Xc#s5w434! z=KCHiMfTroWme1CQLKM6#yC{j{Rt6V^0&S`iz13f)@*w--Y#`C@&9BDmZHBIqs{-> z<7++)<+!u%<+ugSJ_4bS2Jl?ZjoaqV=tnIvS(*Fo#%DbB9V5*NRWV#t@_2urduzZ6<7FbOzzQtcxfqX>7@CQA?QZW!jJE*HXT^ zzi%&0GQ7svdZrc|Jal&&Qs<990I4;@131G)EbQo!-2iaHc(7Dd%V7ALPoY^jWpkY> z8qq9MaP(DYlTNN~EQQ~tqm0t+H5k}nd?GJpg%&+O{@yDI40te?^CtxR|A1e3y)wOO zZgoxgJGW!;`1qe(p4+&*VOp&Dtbl0AV5Yr4U*AwG=!K?HTnbm~a77L2!m$e{pxe{`|)yk+6`COm&o>yfPgbPpw6OM_C4dL`iR>=RLaT!`dB{YsfX*FQZ@m0lZ$Ptf?Sb_M^k>pUUCE|D@;xF5%#X6S z(2syXe~vV*mX78*oXxZ3!Uf{xvgx#*3ZCzJqG>Y&yC^8;;#4qx;?!R@b6YSCNA&rd z&aP<7Qid3Bv38w#CD*?EuT5dpX= z=jqqqyrx2^ncSiR*7OCyi- z_W#9jy(fIwh-Q1=A1Bc7Ag+~O(1MXu81XiJkkoEc{0)BHh&BTxg8sTF;tUwsDv8(q z0Khpdzywtc71w*ItMcjan8B;r$k^@`mYuFKa~**XuGC|Ipm$#ej9;?(i0a?-RLiBa zmz*1b53cm6ydGQ`ykYSy?EAc?rjv&4m~dtSEMun9@`0mky&zXc7>1|?D+_SbSE zC8)Tck8|wUY+1BtAN4K$*Ft{q1g(BZZ|+--cTx4JFz>EEA%h;mT7h)h^Mfqd$$T9D z>Fa0WmVRmb@LCSQg7d}Q@NYRV{Cmo(7*oEoVjGLyliJ2JC}^j45m0+=0?On>h_*`< zm?#xaHG2?>_PgAo%MMs-F%_F$d}Vl^{_AA&=#BEmE#I1tP!!vIIz+ir?pf;?mOR_~ zkt&Pp(akL^!l^*&&k8?S9bm2i?i9UaYk62z7LEqDfI7!MJl5WvsY}z9-l^T$o4vqn zx1M_#P%UUyiz%rQrEI+M#E?dd|ED6nqMJJVV03c}jBa)#<9z-vbkmiXmt4;1u@1+W zH$oW4J>2omi+P~#_Ltr9ZAHIF+8+R^uJaOz*Eazc8<$YT*B{=GDdF!txHjP@&#zYV z$}N4GMYgK?YIcaK8e#>gr}%s9)#J-qb~-P4$`+Q|h0Z3s?FoH6L0Ygv$zT(23@!D(iB;}v7ozTWh;(@nHdA?g95QLmS341vH4QuSI$AN&E$(gxbr3x!;@ z4M#94i%5s>wM)l&h1v1hE#(##kVAP@;sWfil;=A`akiv_75&nT?kpKuCnvo#D#4UJ8w1<}3%)h5JzQ=t~`Kb20 zDA&k}lg3a!@ycjlkm63zsT}+w1x1~-2vy?pyQv*lUNco2T!~;NXT%?A%0glq_h|{L zByx&L#IR8LBklf9yGiI+ciO6@6U6m&BFNh!DQPe}qYPxs`2Su=3A>rEJ+T5_nT$?o z!$Mu|*;g3tu82hot5h&?pPI;<6up1-^w7(ZNm8OV)!J9x-+>;JD*j&s|?=&SWw%PpPj|4HRg;8f1_?^OQpOzuqjpG*$)CcZ+r z7*4D~d52mcJ*zqC$`vdl4{4%4dxEJ&Fl$Ns%3A(o9Xl+7+K~TRN7Pr=lI+x(t>j;< z<*wQ7%Z4PPS^aE724tF!v9An2IbPn8oaZQjL6!clcRi5*ySvI`O>BOD7FJhpJX+5* z&uGfXHqcRu_{g(nhb^i332wwT8RpUh2Tn7+PcTjNz`1^uT@m-?UzuEO=&bd72F=K{nkrOCvBg6b~YzDEuLXS4q~{f zm?)c&?~y=t9={}Inl@0^BoG0`T>kyfg4NVu>uBcSe7(||1*UK{?7u4uEMZGH z<^{z}xj7+9Fkq?JmRXS%1J2~^85KcG$iCfc7|&Iy$f6HO3Ae_Ez=s&g<;Wk`M3KL$ z@O=4~gcCk3Q4uxFwWKr9>+?NW9hY~M%oSDXdC!W;k-;Q{L=Hno$`S|GCrG=oB={Hl zK}oi`3lDRX@9&dev%H&@Aig0BCWs7J2|?#TOk75xw00uJxzcR@Rxp?~=n&u-%L6zi zEM+M={BFE5I33u80p{h(HtsyT^M>{9JwT}b<<{pGd5Scgh{!8Z{q&Vtn758Z?V*rx z#?VZN1dd+~)dM|YV3z3ZKzdGBnw~`()E|t8x;+Bgk>d&khF1;RclYaJm(#XfKVu1_ z4iIdsmg*`P9B+zkiGsgJ7Ye#2S`cmv<%&Bn8cNu`;JnNlpboMdj8=Dz6J?~VW2JNJRgnp(6qCu#b%P?;WOK4iCt_LY1$|nS^0I>bN3e!Beifb za2W8kA}SqOQBYQ#rPkhuV|U)Wds*^FGNZtj3w(I$M{=`ke=%@WHSK=Gz*?{=LbMk?H}Y_GL?cn zxC_1uOrE?BYa<8%@U(V}AAZChw9cfAoX>Qgql;yqB1tSh%d0K2$Xl6jnrvI}=$N+q zIa_V^_o05f<~)?!f_f0Lz0_kWW*fygXtARA%@g*c$xGY+D&#EVqT0TP&kWr?AR!Gh zq;v=ZN;e`PLy0s9NJ$7ow-SdA>5>kS6i`A$8bpvrx}+QaCT z)}Ax9*Ix2I^hf%1%?sGz4j=YFu*=FMq4#NLN0uw^1+K#IwzH;j?Ia&1 zZ|k?AAht($r=&&d7}?Jg!KgC*ZEqiLPLM29Zq~bs&nZ$?wuy0N$QwJcMCw~TAMkIs zX;Jwu!xkXaXsrvp*DykOI#u&AgLnNAY4B;ETIMgp6|E)^Z>H?l% zbS>OGW6O!#<;H|l4i_#Wv>7wiEliQhy60D$BRo6qS8ig{_nr3*qVhfWpNfmQ7)p2+ z$v==dExokYHLQD|$ z_4WG3s1AlD$AfVtXZ3!M%=$z^BxAwRYuYlJ(x!K@C(Ih+-G{o4d$m&` zIoWiYR9=QZnx3Av*i`K-LAK2F1&VB<74tRD5& zOAAZnd%@o!O8D)A@yUt`aSxg8c$e*~KGdE~_E>&d=%W&IkMMgHZ(T=Uq52eNRYGanx(0){qM--SZceyiA>sB2o*NoO1>$=N5m9S&k8Y2(a ztlRpq;h=-jXy@G zdFCoBtK~3M3q4#94`bBZXopqYoLRYuwd?MqB)Ug#I;hBl=vW&r2w|$Q7#93U37bzvlm1Nu0hU3uUHo2bu^J3)x2?^6@|)npJjJQae0`aJyolktuZO#ijBM5Qa8IDoQiyqsxehT_ddIv(pUg>V4my*I zY-hlYIb2C_9LPKaiPtnS_Epu`loP#n4(YLTSF8edv>riEWWX@aN>YuJO4Ja+PrGGnSeR&@5&j~r z4AS>uUbHLLBV(eJn+K=Pp?yr{z0HIj^-7<{*#*z@nSM!}(tFcLg?TURaOus1?!d?j zVQuKeX5Ox3P}f5oLEh|F#zmb8ft_y%dvF*a*~;}V1Fd>OJUTbrL$?)YaugJfvlkQh zql_m%>cJnvl8NG}&G75}F1vs6`RzQF?e8wYELIGl$Ftdt_rcF}q8%$gvb~s4%D~7R z#L`KbFkLUvZIBKYlvw#D)ykD$!{b@}dBaTK29UkC1S=dqhGsBegCSi?)7gE4v-X z5iW`9$-*5!g$1*i&b$+26;8@2aX>5H{-d%%JW$VPiixQYEmdKgU`H%e#E_2}Q-^U; zMw~ggj?(}`rjTo9W9m>WvO#C+v%C;VEqXS=LP(OneN*HTNe6fQ1F}cMbBzje+)PJs zidSayr!q3R1xbA6_d8|k%*veg%8B=jTY0xCwg+YHnCh8BxYHX$?~;x`3z@>|;4kQ9 z4pZ5%oF~#E^AJvQX*)`4elWt{4q~DH?44@DG3mhS50fg(SM=~NbXb}s?&(PyKYJm~ zhSjK--}@}W@MoGxbq;@qeo=G8UD+-xcx2_vb^Z`^TA}J!Z>hamz&hh?W$g|A8wQX2 zu^}3)MGY3|j4#6~wA|>_$1%JPA#9sV5&4{NsGP6LI zT5f7&BB_p7ZB_XpOWpW*Y~5m(>AHf;k6&N4l-mexF7D|sH6@6an;SS7)8!^~_fQnt zA+pZ5PTHwqlB6=L9}P5`I9$f$TjA8b?kE8bw0T~!#gJJ}|EMPc-JeuNhxl$=?m9E( zTwwKP3Tl7H|5o%xTRlkl8CXotQ}2V;4)#>Twm{U;50Oomgr0ZB$qgFx$x(@)MnznT z%)-k?_qL{#nzusKToYZbePbnF6L*@`5nOW+#7YD^osn z)ux36Ri{vn>}>_aLY9&}LUG@FD~CvvA?nufFv8%F%(Eu4)YA0Fw}TYM+| z!Kz4hHI_MBxZ)DK0kHCw=AvfTZzrzw7EQy}r+^|clbN#-?>)`ZdzR4_J{I~V*=nl7 z$v$-trYoTLA959It$p7fVMMIQ#jew9rWdqRt4)0nA;KHp?dBYg>sS{k5fP_;uPXl8 zzxDUS=K(W1mVv{h+n&1tlil!=$C&L^Wg}tb<==W?&)pm;zT{vYg*Zcl2w3*rME$dQ zn*CI)QER_QB#(yv?oM?c+TBk4hSnW{HQMuQul;ji!eTC@br}?03AWP0%ab%Qdq+RBTeym$` zd>aIAFfPVR8MP?TvC{b&ATzKofYWPV`e?;9J(4hGZuVzhP>EsJhL{>7Y^_LDIEnY_7YX|x!Iz@_m=R= zr4T#da2XKA01??w?sh!RR!^PG%^tZpS=(DW|5+)G)BowTAVlIl^X>vO?sjce{LYt@ zjbBx^;~=anknrb0g7Mbg3-u`YD!;uQ;|I%zn7WTRHsDWFu2mQiu>wt;aNCwkFmyOxjj#a7PkJqfI7_Zj~pt+EIQ;gs_t4H;Wj|N@}r=`{R4`J;uRF3VME< zKRqlhC}k^IpAR|-DkIw6ee>MuVqlsIy&}7W*PoLvw}@Esu&qIrG|v37-IP)7w8i8n zT2=-Am~eXzkG|>Lj|6k=w(Ps`$e5yS*{+y5l292YKI?I<1Y@z#&WyH^DA-fx6Tl>+ zK1LGtqREQ%!Y-GbEut5LlRt>ua8iMVPbmIeZfQtK(;v?ycSrgi{aGpW`5bnj35FZA zrgE2Xt9tEM;^9^|4=1$tUJw>_4q-?=chs??irC1w%?I>;%KNd6>~H1aAF+Z@L zxxYEp7&oo9#1;Ud+5YxoQ6AzDEqIg)mB8_Vm=0J_KFIBoBddZ+z00bL|BCLEXm@vJ zC)I$yD^DS)=?ISN%)L|MftJZza3;I@Rop^D@xx&yf46Wc)dkGax>Wn0~29Bo)*lnfUiJ{iRoRh@lr{$N4FMeFt> zf4&JhUMR<{lisj1&{4QaN3|`1Xmu(b<=GQ;dSQ&o$lXSgZ9PYxb-eD&j)={xA4w(N zxTC-9D!~a8Hibt^ZE$n18Q{3=Q*1Ko`h|3&|MDRL36Y%02{}27+tC}doHV98^;#!& zmL!u@$+6f_)HS0_R*FNlB(*cNJyEnmt@xVE*P3xstO<)BGyQrOP70enO872ji=T3r zhio<{Hc~S&$bEn0nEyW8)B!q55m|`7HC_B^IsY?5-DpK@{W?73N3`Q5E^aL>#{;J} zVx+bztPC&P)>JiKjDwaZT(ZXDzDwRoT}><%PuqU5rl^(}-RZYI9F?s#ALDU4D=9LA zfv;5WE}_st(x+Q2@vUTxzZ{<@L(VXQ84IQ_-ni1@#u%`T_i35q$VxS5Krus{8kqzM zXlg>{sb13%sw=?#x4hhgW;IthVvmQDJ?N6&h1 zbt^nJGn41hVxc>HZw32@93yj4n4DF^-YlbBk*P3>OB4P)+8+olA(bMb*pK^6qCBTr z71QTTkd$^$cS zrAxIeO>F(p9Vv(*%ow7v`h6J^b!9)yR_Mn(c61&gKEMuAGYCRy=1?X1zGK}^``#dS z!sQ*0z?+w*>RK{BUXE^3JCK$;pn*{BC!(+jOG;RHtd`l?(jf8IygCY;9YKAW&y-xC z>aM6&F9!#V7ZhCZ7Heg4R(=T0>d%}-_yn``n*Ovd?KU;1q$kSGFW#&ya9q$!gDEWq z@5eLwT;z3{HRlljj-od4dXo`7m-nNZU_lA08taX=>~6}9YO-C#9?>*xIcCvTA;Uei z30yf*0ST)EGdv7>NM>uZVN?`f=vHGbm66ej>Pw~08759RkF|xep&Yk6Hl+gFr7sy; zMZd=d)*?#2WSN+x`?T`lAa21^_`7tw6(2JX-ivjcfPf;q^H(O6>9oviq$e@y`^>=*U|Hgd#A=^Wsj~rlWtvMj)jixF1pC?8+}7`PHtA3Xj{gW@TWNT8B=2+}E&uXJBGC$l1@aMlA8pCh0<{ zdfK=1gwVw{wCF`A;_8$8+C~T{7S` z^9Im)XIJv;ntV7x1HSolfVhEvAxJerz%d6JZqo~^YMgR55L{ZhotG$R+y5%-*RK3i2Xc;T6i8X1iYy%6<$wV(5VPB54=#%^{<(2P1~0Y%z=FjRv% zM0MU`U#?x?^fo*#dOKVG4tq03faV=ybQ<4!Sm>`nZ71ml!u#J)SU{KpR?kp$c#n7F z_F2yxc{wcAau%lZGSpu5k1O5o=xE0EysI{>tuji(v}pPJ)bUE7CB@cjMTybcmSx#_ zGM8JJ>Tv?rx%)QEv&eqCLC{)RUq{s3Lss0agMER;yLb*uIJ0z4eR-1tS(H@6qGf-#YtteIAqS?ukkkBAmI$#);J=qM%ZP-Sy2)w&DPDv4 zi{`%&h!qK0BcPjNCVxZu>`15_0o@c__#3j~MnVS&=w_?x-%zg*5)%JPPII#d3z>FIBt(K);s2bT zN(b4~`5QUSP0GsO!wWq@LJbJ$p9BVyzo9V$Bn0Xur@mPt{TrGC?)Gxh+$`|?-HvC8 zm>~!d)k{wE&!2kqC@7@=nV|rCpwgZk8103E@MHVvssH{Fas!6~;(gNKQ-6RsoCZkg z69AtCAk~dN0nib^(60$T3_?1h`vt)J!3dQanClk=?}Z?}0s{iz-B6^LCkzLO8i0bg z!w@D5FpE$(!jUa(g96~y2&C$cP?sW+YBxe%c#Tx)-{AbYbpe4PD)8(Zq>DD{+7&cJ z1)2gfhcrMFz|N2ccq|6dOamAX3xG#sk!mGE9R`pp>WBb%C=RI-#p3|RBT&#F5I!Oe z-be%pk|2L>YG7}K3Vf9eFr+{LmQg534$v5t2B*ITTHZo{B7`Fu7#WoYWdo4!G6?<2 zcNy^Oe4sp^;?L^~X#Y+H3I%2mO<4frm<*^LFdw^ve4_?p#vq_wpm0nQBmyiWnz#Yz x4+)SEVD&=^WCj%dkOmzB>xg3lz%?!d3IiMw#{!@laV!Q7$JsDCI{!?{{{f%DKREyZ delta 24162 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 JdOQD&_kR-ojimqp