From e4c98431524a87e9417181c889eb785e050a9563 Mon Sep 17 00:00:00 2001 From: Matthias Groncki Date: Tue, 8 May 2018 19:12:27 +0700 Subject: [PATCH 1/8] New Blog entry about Logistic Regression First part of the series from Logistic Regression in Sckit-Learn to Deep Learning with TensorFlow --- Blog_DS_1.ipynb | 421 +++++++++++++++++++++++++++++++++ LogisticRegression_Part1.ipynb | 326 +++++++++++++++++++++++++ 2 files changed, 747 insertions(+) create mode 100644 Blog_DS_1.ipynb create mode 100644 LogisticRegression_Part1.ipynb diff --git a/Blog_DS_1.ipynb b/Blog_DS_1.ipynb new file mode 100644 index 0000000..153b4cc --- /dev/null +++ b/Blog_DS_1.ipynb @@ -0,0 +1,421 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Logistic Regression with Sklearn and TensorFlow Part I" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import tensorflow as tf\n", + "from sklearn.model_selection import train_test_split\n", + "from sklearn.preprocessing import StandardScaler\n", + "from sklearn.linear_model import LogisticRegression\n", + "from sklearn.pipeline import Pipeline\n", + "from sklearn.metrics import roc_curve, roc_auc_score, classification_report, accuracy_score\n", + "import seaborn as sns\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load data and visualize the data" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "credit_card = pd.read_csv('creditcard.csv')" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "f, ax = plt.subplots(figsize=(7, 5))\n", + "sns.countplot(x='Class', data=credit_card)\n", + "plt.title('# Fraud vs NonFraud')\n", + "plt.xlabel('Class (1==Fraud)')\n", + "plt.savefig('inbalance_class.png', dpi=600)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As we can see we have mostly non-fraudulent transactions. Such a problem is also called inbalanced class problem.\n", + "\n", + "99.8% of all transactions are non-fraudulent. The easiest classifier would always predict no fraud and would be in almost all cases correct. Such classifier would have a very high accuracy but is quite useless." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.99827251436937992" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "base_line_accuracy = 1-np.sum(credit_card.Class)/credit_card.shape[0]\n", + "base_line_accuracy" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For such an inbalanced class problem we could use over or undersampling methods to try to balance the classes (see inbalance-learn for example: https://github.com/scikit-learn-contrib/imbalanced-learn), but this out of the scope of todays post. We will come back to this in a later post.\n", + "\n", + "As accuracy is not very informative in this case the AUC (Aera under the curve) a better metric to assess the model quality. The AUC in a two class classification class is equal to the probability that our classifier will detect a fraudulent transaction given one fraudulent and genuiune transaction to choice from. Guessing would have a probability of 50%." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "X = credit_card.drop(columns='Class', axis=1)\n", + "y = credit_card.Class.values" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Due to the construction of the dataset (PCA transformed features, which minimizes the correlation between factors), we dont have any highly correlated features. Multicolinearity could cause problems in a logisitc regression.\n", + "\n", + "To test for multicolinearity one could look into the correlation matrix (works only for non categorical features) or run partial regressions and compare the standard errors or use pseudo-R^2 values and calculate Variance-Inflation-Factors.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "corr = X.corr()\n", + "\n", + "mask = np.zeros_like(corr, dtype=np.bool)\n", + "mask[np.triu_indices_from(mask)] = True\n", + "\n", + "cmap = sns.diverging_palette(220, 10, as_cmap=True)\n", + "\n", + "# Draw the heatmap with the mask and correct aspect ratio\n", + "f, ax = plt.subplots(figsize=(11, 9))\n", + "sns.heatmap(corr, mask=mask, cmap=cmap, vmax=.3, center=0,\n", + " square=True, linewidths=.5, cbar_kws={\"shrink\": .5})\n", + "plt.savefig('heat_map.png', dpi=600)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Logisitc Regression with Sklearn\n", + "\n", + "Short reminder of Logistic Regression:\n", + "\n", + "In Logisitic Regression the logits (logs of the odds) are assumed to be a linear function of the features\n", + "\n", + "$$L=\\log(\\frac{P(Y=1)}{1-P(Y=1)}) = \\beta_0 + \\sum_{i=1}^n \\beta_i X_i. $$\n", + "\n", + "Solving this equatation for $p=P(Y=1)$ yields to\n", + "\n", + "$$ p = \\frac{\\exp(L)}{1-\\exp(L)}.$$\n", + "\n", + "The parameters $\\beta_i$ can be derived by Maximum Likelihood Estimation (MLE). The likelihood for a given $m$ observation $Y_j$ is\n", + "\n", + "$$ lkl = \\prod_{j=1}^m p^{Y_j}(1-p)^{1-Y_j}.$$\n", + "\n", + "To find the maximum of the likelihood is equivalent to the minimize the negative logarithm of the likelihood (loglikelihood).\n", + "\n", + "$$ -llkh = -\\sum_{j=1}^m Y_j \\log(p) + (1-Y_j) \\log(1-p),$$\n", + "\n", + "which is numerical more stable. The log-likelihood function has the same form as the cross-entropy error function for a discrete case.\n", + "\n", + "So finding the maximum likelihood estimator is the same problem as minimizing the average cross entropy error function.\n", + "\n", + "In SciKit-Learn uses by default a coordinate descent algorithm to find the minimum of L2 regularized version of the loss function (see. http://scikit-learn.org/stable/modules/linear_model.html#logistic-regression).\n", + "\n", + "The main difference between L1 (Lasso) and L2 (Ridge) regulaziation is, that the L1 prefer a sparse solution (the higher the regulazation parameter the more parameter will be zero) while L2 enforce small parameter values." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Train the model\n", + "\n", + "### Training and test set\n", + "\n", + "First we split our data set into a train and a validation set by using the function train_test_split. The model performace " + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "np.random.seed(42)\n", + "X_train, X_test, y_train, y_test = train_test_split(X, y)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Model definition\n", + "\n", + "As preperation we standardize our features to have zero mean and a unit standard deviation. The convergence of gradient descent algorithm are better. We use the class `StandardScaler`. The class *StandardScaler* has the method `fit_transform()` which learn the mean $\\mu_i$ and standard deviation $\\sigma_i$ of each feature $i$ and return a standardized version $\\frac{x_i - \\mu_i}{\\sigma}$. We learn the mean and sd on the training data. We can apply the same standardization on the test set with the function *transform()*.\n", + "\n", + "\n", + "The logistic regression is implemented in the class `LogisticRegression`, we will use for now the default parameterization. The model can be fit using the function `fit()`. After fitting the model can be used to make predicitons `predict()` or return the estimated the class probabilities `predict_proba()`.\n", + "\n", + "We combine both steps into a Pipeline. The pipline performs both steps automatically. When we call the method `fit()` of the pipeline, it will invoke the method `fit_and_transform()` for all but the last step and the method `fit()` of the last step, which is equivalent to:\n", + "\n", + "```python\n", + "lr.fit(scaler.fit_transform(X_train), y_train)\n", + "```\n", + "\n", + "or visualized as a dataflow:\n", + "\n", + "```X_train => scaler.fit_transform(.) => lr.fit(., y_train)```\n", + "\n", + "If we invoke the method `predict()` of the pipeline its equvivalent to\n", + "\n", + "\n", + "```python\n", + "lr.predict(scaler.transform(X_train))\n", + "```\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "scaler = StandardScaler()\n", + "lr = LogisticRegression()\n", + "model1 = Pipeline([('standardize', scaler),\n", + " ('log_reg', lr)])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the next step we fit our model to the training data" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Pipeline(memory=None,\n", + " steps=[('standardize', StandardScaler(copy=True, with_mean=True, with_std=True)), ('log_reg', LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,\n", + " intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,\n", + " penalty='l2', random_state=None, solver='liblinear', tol=0.0001,\n", + " verbose=0, warm_start=False))])" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model1.fit(X_train, y_train)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "### Training score and Test score" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Training accuracy: 99.9237 %\n", + "Training AUC: 98.0664 %\n" + ] + } + ], + "source": [ + "y_train_hat = model1.predict(X_train)\n", + "y_train_hat_probs = model1.predict_proba(X_train)[:,1]\n", + "train_accuracy = accuracy_score(y_train, y_train_hat)*100\n", + "train_auc_roc = roc_auc_score(y_train, y_train_hat_probs)*100\n", + "print('Training accuracy: %.4f %%' % train_accuracy)\n", + "print('Training AUC: %.4f %%' % train_auc_roc)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Training accuracy: 99.9199 %\n", + "Training AUC: 97.4810 %\n" + ] + } + ], + "source": [ + "y_test_hat = model1.predict(X_test)\n", + "y_test_hat_probs = model1.predict_proba(X_test)[:,1]\n", + "test_accuracy = accuracy_score(y_test, y_test_hat)*100\n", + "test_auc_roc = roc_auc_score(y_test, y_test_hat_probs)*100\n", + "print('Training accuracy: %.4f %%' % test_accuracy)\n", + "print('Training AUC: %.4f %%' % test_auc_roc)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " precision recall f1-score support\n", + "\n", + " 0 0.9994 0.9998 0.9996 71089\n", + " 1 0.8500 0.6018 0.7047 113\n", + "\n", + "avg / total 0.9991 0.9992 0.9991 71202\n", + "\n" + ] + } + ], + "source": [ + "print(classification_report(y_test, y_test_hat, digits=4))" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAicAAAGACAYAAAB2ocIWAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzs3XlcVOehPvBnFoZtkH0RFGQRBuOCmKhZ1LhF4xLZQdCk9yZpmjTpbZvetrdLmk9qEpPcprlJ+7PNUrOIRgHFNe4krnGLqCQMqwKissk2MMwwc87vj7S0Nsq4MHNmeb5/MefMnHl4M8KT9xzOKxNFUQQRERGRnZBLHYCIiIjoX7GcEBERkV1hOSEiIiK7wnJCREREdoXlhIiIiOwKywkRERHZFaXUAYjIcfT392PmzJnQaDR4//33B7YnJCTg6NGjCAgIGNi2c+dO5Ofn45NPPgEAdHV14e2338axY8cgl8shk8mQl5eHzMzM77zPsWPH8OSTTyI6OnpgW09PD+Li4vDqq6/C398fANDQ0IA333wT586dg5eXF1QqFXJycpCRkTHwOoPBgFWrVuHzzz+HKIoQBAGLFy/Gk08+CZlMNuRjRER3juWEiG7anj17oNFoUFZWhpqaGsTGxt7U6wwGA5YtW4bFixdj06ZNUCqVaGxsxPe+9z0AuG5BiYyMxObNmwcem81mPPfcc/jb3/6G559/HhcvXsSyZcvw4x//GG+++SZkMhmamprws5/9DHV1dXj++echiiKeeeYZREdHY/369XB3d0d7ezueeuop9Pb24sc//vGQjAsRDS2e1iGim7Zu3TrMnj0bCxYswEcffXTTr9uxYwe8vLzw5JNPQqn89v+JIiIi8NZbb2H06NE3dQydToerV6/C19cXAPDuu+9i0aJFSE1NHZgBCQ0NxR//+Ed8/PHHaG5uxokTJ1BbW4v/+Z//gbu7OwDA398fr7/+Ou65555b+daJyIY4c0JEN6W6uhqnT5/G22+/jbvuugvLly/HT37yk4FTLIMpKytDcnLyd7bfddddN3xNfX09lixZApPJhKtXryIsLAwPP/wwHnvsMQDA6dOn8fzzz3/ndUFBQYiNjcWZM2fQ0NCA8ePHQ6FQXPOcUaNGYdSoURZzE5E0OHNCRDdl3bp1mDlzJvz9/TF+/HiMGDECGzZsAIDrXrshCALkcvnA/ltdKeMfp3W2b9+On/3sZ2hpacHDDz8MNze3geeYTKbrvtZoNEImk0Eul9/y+xKR9FhOiMii3t5ebN68GadOncKsWbMwa9YstLS0YM2aNejv74e/vz86OjqueU1bWxv8/PwAAElJSSgtLf3Ocfft24fXXnvN4vunp6dj1qxZ+K//+q+BQjJx4kQcO3bsO89tamrCxYsXMW7cOEyYMAHnzp2D2Wy+5jlnz57Ff//3f9/0909EtsVyQkQWbd26FX5+fjh48CD279+P/fv3Y+/evejt7cXOnTsxffp0fPLJJxAEAQDQ2dmJTZs2YcaMGQCAhx56CDqdDu+9995AUWhoaMDKlStv+qLan/3sZ7h8+TLy8/MBAE8//TQ+++wzbNq0aeA5V65cwU9/+lMsXboUoaGhmDhxImJiYvDqq6/CYDAAAFpbW7FixQqMGDFiyMaHiIaWjKsSE5ElKSkpyMzMRF5e3jXb33rrLRw8eBCrV6/GypUrUVpaOnB9x5IlS/D4448PnPJpbW3FG2+8gXPnzkGhUEChUODRRx9FWlrad97v2LFj+P3vf49t27Zds72oqAivvvoqdu7ciaCgIDQ2NuKtt97C2bNnoVQq4e7ujqysLGRnZw+8b09PD/74xz/i6NGjUCgUEAQBKSkp12QjIvvCckJERER2had1iIiIyK6wnBAREZFdYTkhIiIiu8JyQkRERHaF5YSIiIjsisPcvr6lpdtqx/b390J7e6/Vjk//xLG2LY637XCsbYdjbTvWHOvgYJ8b7uPMCQClUmH5STQkONa2xfG2HY617XCsbUeqsWY5ISIiIrvCckJERER2heWEiIiI7ArLCREREdkVlhMiIiKyKywnREREZFdYToiIiMiusJwQERGRXWE5ISIiIrvCckJERER2xarl5MyZM1i+fPl3tu/fvx/p6enIzs7Ghg0brBmBiIiIHIzVFv577733sGXLFnh6el6zvb+/H6+++ioKCwvh6emJpUuXYubMmQgODrZWFCIiIoexYX81TmibpY4BUW7E6LH9+M97H4Sbws2m7221chIZGYl33nkHP//5z6/ZXlNTg8jISPj6+gIAJk2ahJMnT+Lhhx8e9Hj+/l5WXYBosNURaWhxrG2L4207HGvbceax/qqqBe06A4J8PSR5fxEiTOpLMASfwVmjAW2yMRgXrLFpBquVk3nz5uHixYvf2a7T6eDj888Plbe3N3Q6ncXjWXN57OBgH7S0dFvt+PRPHGvb4njbDsfadpx9rM1mEf5qd6x86l6bv3eHoRMbKopxpvVruMmVyBqbihDZcKuM92AF02rl5EbUajV6enoGHvf09FxTVoiIpHCnU+kKhQxmsziEiehGnH2s27sN8Pdxt+l7iqKIo5dPYGP1NuhNfYjzi0aeJgN3RcVIUgRtXk5iY2NRV1eHjo4OeHl54eTJk3j88cdtHYOI6BontM2S/FIg+nf+Pu64RxNis/dr1bdhrbYIFe3V8FC4IychFfeHT4FcJt0f9NqsnGzduhW9vb3Izs7GL3/5Szz++OMQRRHp6ekIDQ21VQwiohvy93HHG8/cd1uvdfZTDfaEYz00BFHA5xcPY2vNThiFftwVqMHShDT4e/hJHc265WTEiBEDfyq8ePHige2zZs3CrFmzrPnWRHSH7OUvBmyFsybkSi73NCG/vADnu+rh7eaFpZp03BM6ETKZTOpoACQ4rUNEjsHVTnPYeiqdSAomwYQ9dZ9j54V9MIlmTAqZgMz4JfBRqaWOdg2WEyIHcTszGXdy4eA/isntnuYgIvtS19WAfG0hGnWX4asahpyEVIwPvkvqWNfFckLkIGw9k8GZBCLnYDQbsf38HuyrPwARIu4Pn4zUuIXwVHpafrFEWE6IHMitzmTwwkEi11bVXoN8bSFa9G0I8ghAriYDCQFxUseyiOWEhsTNnnJw9vsTWJMrXf9BRHdGb+pDcc0OHGr8EjLIMGvkNCyOmQeVQiV1tJvCckJDwtUunpQCT7MQ0c0oay3HuoqN6DB0Yrh3KPI0mYj2jZQ61i1hOaEhczOnHHiagYjIOnTGHhRWbcGJptNQyBRYED0X86JmQil3vF/1jpeYhsRQ38OCsyZERNIQRRGnms+goHIzdP09iBo2Ess0mQhXh0kd7baxnLiooT4Nw1MORES212HoxKcVG3GutRxucjekxS3CzJEPSHrr+aHAcuIkbnUmhPewICJyXKIo4sil49hYvR195j7E+8UiV5OBYK9AqaMNCZYTJ3GrMyGc6SAickwtvW1Yqy1EZUcNPBQeyE1Ix33hk+3m1vNDgeXEiXAmhIjIeQmigJKGQ9hauwv9Qj/GBSUiJyENfu6+UkcbciwnQ0jKhdJ4QSoRkfO6pLuCNdoC1HU1QO3mjWWJmZgUMsGpZkv+FcvJEJLyXh88TUNE5HxMggm7LuzHrroSmEUz7gmdiIzRj0Ct8pY6mlWxnNyiwWZHeJEpERENlQtd9VhTXoDLPU3wc/fF0oQ0jA1KlDqWTbCc3KLBZkc4e0FERHfKaDZia+0ulDQcgggRD0RMRUrsAngqPaSOZjMsJ7eBsyNERGQNle3VyC8vRGvfVQR7BiJPk4HR/rFSx7I5lpN/Y+miVl54SkREQ01v0mNT9XYcvnQcMsgwJ3IGFkbPdZiF+oYay8m/sXRRK0/dEBHRUDrX+g3WaTei09iFcO8wLEvMRNSwkVLHkhTLyXXwtA0REVlbt1GHgsrNONV8BkqZAoui52Fu1AyHXKhvqHEEiIiIbEgURZxoOo3Cqi3o6e9F9LBI5CVmYrh3qNTR7AbLCRERkY2093Xg04qNKGvTQiV3Q8boRzBjxH0Ov1DfUGM5+bt/XAjLC16JiGioCaKAw5eOobh6B/rMBiT4xyFXk44gT+dYqG+osZz83b8WE17wSkREQ6W5twVrtUWo6qiFp9IDeZpM3Dv8bqe99fxQYDn5F7wQloiIhopZMGN/w0FsP78b/YIJE4LuQlZCilMu1DfUWE6IiIiG2MXuS8jXFqC+uxE+bmo8OiYFE4PHcbbkJrGcEBERDZF+wYSdF/Zhd10JBFHAlLBJSBu9CGo3516ob6ixnBAREQ2B2s465JcX4EpvM/zd/bBUk4a7AjVSx3JILCdERER3wGA2YmvNTnx+8TBEiJgecR+WxM6Hhwst1DfUWE6IiIhuk/ZqFdZqC9HW144QryDkaTIR5xctdSyHx3IC4G9bv0ZbVx8Ch7HlEhGRZb39vdhYvR1HL5+AXCbHQ1EzsWDUHLgp3KSO5hRYTgAcPtMIALy/CRERWVTaUob1FZvQZezGCHU48hIzEOkzQupYToXl5O8Ch3kga1ac1DGIiMhOdRm7saFyM043n4VSpsDimPmYGzkDCrlC6mhOx+XLyYb91Whu1/OUDhERXZcoijh+5SsUVm1Br0mPGN8o5GkyEebN2XZrcflyckLbDICndIiI6Luu9rVjnXYjvrlaAZVChczRSzB9xL1cqM/KXL6cAECIvydP6RAR0QBBFHCw8UtsrtkBg9mIxIB4LE1IQ6BngNTRXALLCRER0b9o6mlGvrYQNZ0X4KX0xPLELEwJm8Rbz9sQywkRERG+XahvX/0BbL+wBybBhKTgcciKT4Gvu4/U0VwOywkREbm8hu5G5JcXoEF3CT4qNbLjUzExZJzUsVwWywkREbmsfnM/PruwD3vqP4cgCpgadjfSRi+Ct5uX1NFcGssJERG5pJqOC8jXFqCptwUBHv7ITUhHYmC81LEILCdERORi+kwGbKndiQMXjwAAHhxxPxbHzIeH0l3iZPQPLCdEROQyvmmrwLqKjbja145QrxDkaTIQ6zdK6lj0b1hOiIjI6fX096KoaiuOXTkFuUyO+VGzMH/UbC7UZ6dYToiIyKmdbj6H9ZWb0G3UYaQ6HHmJWRjpEy51LBoEywkRETmlTkMXNlQWo7SlDEq5EktiH8bskdO5UJ8DYDkhIiKnIooivrxyCkVVW6E36RHrG408TTpCuVCfw2A5ISIip9Gmv4q12iJo26vgrlAhOz4FD0RM5UJ9DoblhIiIHJ4gCjhw8Sg2134Go9mIMQEJWKpJQ4CHv9TR6DawnBARkUO70tOEfG0hajvr4K30Qk5iKiaHJXOhPgfGckJERA7JLJixp/5zfHZ+L0yiGckh45EZvwTDVFyoz9FZrZwIgoAXX3wRFRUVUKlUWLFiBaKiogb2f/DBB9i+fTtkMhl+8IMfYO7cudaKQkRETqa++yLWlBegUXcZw1Q+yElIxYTgsVLHoiFitXKyd+9eGI1GrF+/HqWlpVi5ciVWrVoFAOjq6sInn3yC3bt3Q6/XIyUlheWEiIgsMpr7kX9mE7ZW7IUgCrhv+D1IjVsILy7U51SsVk5OnTqFadOmAQCSkpJQVlY2sM/T0xPh4eHQ6/XQ6/U8L0hERBZVd5xHvrYAzb2tCPQIQK4mHZqA0VLHIiuwWjnR6XRQq9UDjxUKBUwmE5TKb99y+PDhWLhwIcxmM5566imLx/P394JSOfQ3zlEovi1GwcE8R2krHGvb4njbDsfaOnr79Vh7thi7qw9ABhkWxM9CzrhHuFCfjUjxubZaOVGr1ejp6Rl4LAjCQDE5cOAAmpubsW/fPgDA448/juTkZIwfP/6Gx2tv77VKTrNZhEIhQ0tLt1WOT9cKDvbhWNsQx9t2ONbW8XWbFuu0G9Fu6ECYdyiWaTIwOW4sWlq60Q2j1PGcnjU/14OVHquVk+TkZJSUlGDBggUoLS1FfHz8wD5fX194eHhApVJBJpPBx8cHXV1d1opCREQORtffg6KqrTh+5SvIZXI8PGo25o2aDTc5/8jUFVjtv/LcuXNx+PBh5OTkQBRFvPLKK1i9ejUiIyMxe/ZsHDlyBFlZWZDL5UhOTsb9999vrShEROQgRFHEV81nsaGyGLr+HkT6RGBZYhYi1MOljkY2ZLVyIpfL8dJLL12zLTY2duDrH/3oR/jRj35krbcnIiIH02HoxIaKYpxp/RpuciVS4xZi5ogHuFCfC+L8GBERSUoURRy9fAIbq7dBb+rDaL8Y5GoyEOIVJHU0kgjLCRERSaZV34a12iJUtFfDQ+GOnIQ03B8+mQv1uTiWEyIisjlBFPD5xcPYWrMTRqEfYwM1yElIg7+Hn9TRyA6wnBARkU1d0l1BvrYQF7rq4e3mhVxNBu4OTeINOWkAywkREdmESTBhT93n+OzCPphFM+4OTULG6Efgo1JbfjG5FJYTIiKyurquBqwpL8Clnivwc/dFTkIqxgWNkToW2SmWEyIishqj2Yjt5/dgX/0BiBBxf/gUpMYtgKfSU+poZMdYToiIyCoq22uwVluIFn0bgjwDkadJR7x/nNSxyAGwnBAR0ZDSm/Qort6BQ5eOQQYZZo+cjkUxD0GlUEkdjRwEywkREQ2ZstZyrKvYiA5DJ8K9w5CXmIFRwyKljkUOhuWEiIjuWLdRh8KqLTjZVAqFTIEF0XMxL2omlFyoj24DPzVERHTbRFHEqeYzKKjcDF1/D6KGjcQyTSbC1WFSRyMHxnJCRES3pcPQiU8rNuJcaznc5G5Ij1uEB0c+wFvP0x1jOSEiolsiiAKOXDqOTdU70GfuQ7xfLHI1GQj2CpQ6GjkJlhMiIrppzb2tWKstRFVHLTwUHsjVpOO+4ZN563kaUiwnRERkkSAK2N9wENtqd6Nf6Me4oETkJKTBz91X6mjkhFhOiIhoUJd0V7CmvAB13Q1Qu3ljeWImkkMmcLaErIblhIiIrsskmLDrwn7sqiuBWTTjntBkZIxeDLXKW+po5ORYToiI6DsudNVjTXkBLvc0wc/dF0sT0jA2KFHqWOQiWE6IiGiAwWzEttpdKGk4BBEipkXciyWxD8NT6SF1NHIhLCdERAQAqLhajbXaQrT2XUWwZyDyNBkY7R8rdSxyQSwnREQurrdfj03V23Hk8nHIIMPcyAexIHouVAo3qaORi2I5ISJyYWdbvsanFZvQaexChHo48jQZiBo2UupY5OJYToiIXFC3UYeCys041XwGSpkCi6Ln4aGoB6GQK6SORsRyQkTkSkRRxImm0yis2oKe/l5ED4tEXmImhnuHSh2NaADLCRGRi2jv68C6io34uk0LldwNGaMfwYwR93GhPrI7LCdERE5OEAUcajyGzTU70Gc2QOM/Gks16QjyDJA6GtF1sZwQETmx5t4W5GsLUd1xHp5KTyzTZGLq8Lt563myaywnREROyCyYsb/hILaf341+wYQJwWORHZ8CX/dhUkcjsojlhIjIyVzsvoR8bQHquxvh46bGo2NSMDF4HGdLyGGwnBAROYl+wYSdF/Zhd10JBFHAlLBJSBu9CGo3LtRHjoXlhIjICdR21iG/vABXepvh7+6HpZp03BWYIHUsotvCckJE5MD6TAZsq92Fzy8ehggR0yPuw5LY+fDgQn3kwFhOiIgcVPnVSqzTFqGtrx0hXkHI02Qizi9a6lhEd4zlhIjIwfT292Jj9XYcvXwCcpkcD0XNxIJRc+DGhfrISbCcEBE5kNKWMqyv2IQuYzdGqMORl5iBSJ8RUsciGlIsJ0REDqDL2I0NlZtxuvkslHIlHomZjzmRM7hQHzkllhMiIjsmiiKOX/kKhVVb0GvSI8Y3CnmaTIR5h0gdjchqWE6IiOxUm74d6yqKUH61EiqFCpnxSzA94l4u1EdOj+WEiMjOCKKAg41fYnPNDhjMRiQGxGNpQjoCPf2ljkZkEywnRER2pKmnGfnaQtR0XoCX0hPLE7MwJWwSbz1PLoXlhIjIDpgFM/bVH8D2C3tgEkyYGDwOmfEp8HX3kToakc2xnBARSayhuxH55QVo0F2Cj0qNnPhUJIWMkzoWkWQslhOj0YgPPvgA58+fxwsvvIAPP/wQ3//+96FSqWyRj4jIafWb+7Hjwl7srf8Cgihg6vC7kR63CF5uXlJHI5KUxUu+X3rpJej1enzzzTdQKBSor6/Hr371K1tkIyJyWjUdF/Dqibewu64E/u6+eHbCE1iemMViQoSbmDn5+uuvsWnTJhw4cACenp547bXXsHjxYltkIyJyOn2mPmyp3YkDF48CAB4ccT8Wx8yHh9Jd4mRE9sNiOZHJZDAajQNXire3t/OqcSKi2/BNWwXWaovQbuhAqFcIliVmIMZ3lNSxiOyOxXLy6KOP4j/+4z/Q0tKCl19+GXv37sUzzzxji2xERE6hp78XRVVbcezKKchlcsyPmoX5o2ZzoT6iG7BYTlJSUjB27FgcO3YMZrMZq1atgkajsUU2IiKHd7r5HNZXbkK3UYeRPhHI02RipE+41LGI7JrFcvLcc8/hnXfeQVxc3MC2xx57DB999JFVgxERObJOQxc2VBajtKUMSrkSS2IfxuyR07lQH9FNuGE5efbZZ1FeXo7m5mbMnj17YLvZbEZYWJhNwhERORpRFPHl5ZMoqt4GvUmPWN9o5CVmINQrWOpoRA7jhuVk5cqV6OjowMsvv4zf/OY3/3yBUonAwECLBxYEAS+++CIqKiqgUqmwYsUKREVFDez/4osv8Oc//xkAMGbMGPzud7/jhbZE5NDa9FexVlsEbXsV3BUqZMen4oGIKVyoj+gW3bCcqNVqqNVqrFq1Ct988w16e3shiiLMZjMOHjyIjIyMQQ+8d+9eGI1GrF+/HqWlpVi5ciVWrVoFANDpdHjjjTfw8ccfIyAgAO+99x7a29sREBAwtN8dEZENCKKAHZX7sfbsZhjNRowJTMDShDQEeHChPqLbYfGak9/85jc4fvw4Ojs7ERMTA61Wi+TkZIvl5NSpU5g2bRoAICkpCWVlZQP7Tp8+jfj4eLz22mtoaGhAZmYmiwkROaQrPU3I1xaitrMO3kovLB2ThntCJ3ImmOgOWCwnR44cwa5du/D73/8ejz76KPR6PVauXGnxwDqdDmq1euCxQqGAyWSCUqlEe3s7jh07huLiYnh5eSEvLw9JSUmIjo6+4fH8/b2gVA79hWQKxbc/QIKDubiWrXCsbYvjbR0mwYwt2t0o/HoHTIIJ946chP9MzoKvxzCpo7kEfq5tR4qxtlhOQkJC4ObmhtjYWFRUVGDhwoXo7u62eGC1Wo2enp6Bx4IgQKn89u38/Pwwbtw4BAd/e4HY3XffjfLy8kHLSXt7r8X3vB1mswiFQoaWFsvfE9254GAfjrUNcbyto77rItZoC9CouwxflQ+yE1IxZ8y9aGnpRstN/HykO8PPte1Yc6wHKz0Wy0loaCj++te/4t5778Ubb7wB4NvFAC1JTk5GSUkJFixYgNLSUsTHxw/sGzt2LCorK3H16lUMGzYMZ86cQVZW1s18L0REkjGa+7Hj/B7sazgAQRRw3/DJSI1bCC83T6mjETkVi+Xk5ZdfxhdffIHx48fjoYcewrZt2/Diiy9aPPDcuXNx+PBh5OTkQBRFvPLKK1i9ejUiIyMxe/ZsPP/883jiiScAAPPnz7+mvBAR2Zuq9lqs1RaiWd+KQI8A5GrSoQkYLXUsIqckE0VRHOwJjz/+OD744ANb5bkha00r/ff/OwKFQoaVT91rlePTtTgda1sc7zunN/VhS81nONB4FDLIMHPkA1gUMw/uCtU1z+NY2w7H2nbs9rSOXq/H5cuXMXz48CENRURk775u02KddiPaDR0I8w7FMk0Gon2jLL+QiO6IxXLS3t6OWbNmITAwEO7u7hBFETKZDPv27bNFPiIim9MZe1BYtRUnmr6CXCbHw6PmYN6oWXCTW/yRSURDwOK/tPfff98WOYiIJCeKIr5qPosNlcXQ9fcg0mcEliVmIkLNmWMiW7JYTiIiImyRg4hIUh2GTqyvKMbZ1q/hJlciNW4hZo54gAv1EUmAc5RE5NJEUcTRyyewsXob9KY+jPaLQa4mAyFeQVJHI3JZLCdE5LJa9W3I1xahsr0aHgp3LE1Iw33hk7lQH5HEbqqcbN26FdXV1fjBD36AXbt2ISUlxdq5iIisRhAFfH7xMLbW7IRR6MfYQA1yEtLg7+EndTQiwk2Uk//93//FlStX8PXXX+PJJ59EUVERtFotfvnLX9oiHxHRkLqku4J8bSEudNVD7eaNPE0GJoUmcaE+Ijtice7y0KFDeOONN+Du7g61Wo3Vq1fjwIEDtshGRDRkTIIJO87vwcoT/4cLXfW4OzQJv5nyPO4O4wrCRPbG4syJXP5tf/nHP16j0TiwjYjIEdR1NWBNeQEu9VyBn7svchJSMS5ojNSxiOgGLJaT+fPn48c//jE6Ozvx4YcfYsuWLVi0aJEtshER3RGj2Yht53djf/1BiBDxQPgUpMQtgKeSC/UR2TOL5eT73/8+Dh48iPDwcFy+fBnPPfccZs6caYtsRES3rbK9Bmu1hWjRtyHIMxB5mnTE+8dJHYuIboLFcvLDH/4QjzzyCH7yk59ApVJZejoRkaT0Jj2Kq3fg0KVjkEGG2ZHTsSj6IagU/PlF5CgslpOMjAxs374dr776Kh544AE88sgjmDx5si2yERHdknOt3+DTik3oMHQi3DsMeYkZGDUsUupYRHSLLJaTmTNnYubMmTAYDCgpKcHKlSvR3t6OkpISW+QjIrKo26hDYdUWnGwqhUKmwMLouXgoaiaUXKiPyCHd1L/c6upqbN++HTt37sTw4cPx6KOPWjsXEZFFoijiVFMpCqq2QNffg1HDIpGnyUC4OkzqaER0ByyWk8WLF0OhUGDx4sX46KOPEBISYotcRESDau/rwKcVm1DWVg43uRvS4xbhwZEP8NbzRE7gpu4Qm5CQYIssREQWCaKAI5eOY1P1DvSZ+xDvH4c8TTqCPAOljkZEQ+SG5eS3v/0tfv/732PFihXXvXvixx9/bNVgRET/rrm3FWu1hajqqIWHwgO5mnTcN3wy7/BK5GRuWE6ys7MBAM8995zNwhA3X/jfAAAgAElEQVQRXY9ZMKPk4iFsq92FfsGEcUFjkJOQCj93X6mjEZEV3LCcjB07FgCwa9cu/Pa3v71m3y9+8Qv+OTER2USj7jLyywtR190AtZs3lidmIzlkPGdLiJzYDcvJr3/9azQ0NKCsrAxVVVUD281mM7q6umwSjohcV79gwq4L+7Grbj8EUcA9ocnIiF8MtZu31NGIyMpuWE6efvppNDY24uWXX8azzz47sF2hUCA2NtYm4YjINZ3vrEe+tgCXe5rg5+6LpQlpGBuUKHUsIrKRG5YTd3d3TJkyBX/5y1++s6+3txd+fn5WDUZErsdgNmJb7S6UNByCCBHTIu7FktiH4an0kDoaEdnQDcvJb37zG/z1r3/FsmXLIJPJIIriwD6ZTIZ9+/bZJCARuYaKq9VYqy1Ea99VhHgGIVeTgdH+MVLHIiIJ3LCc/PWvfwUA7N+/32ZhiMj19Pbrsal6O45cPg65TI65kQ9iQfRcqBRuUkcjIolYvJXi2bNnsXr1ahiNRvznf/4npk6digMHDtgiGxE5uTMtX2PFsf/FkcvHEaEejv+e9CxS4hawmBC5OIvlZMWKFYiLi8OuXbvg7u6OjRs34v/+7/9skY2InFS3UYe/leXj3XMfoae/F4tj5uEXd/8IkcNGSB2NiOyAxdvXC4KAadOm4fnnn8e8efMQHh4Os9lsi2xE5GREUcSJptMorNyCHlMvoodFYVliBsK8Q6WORkR2xGI58fT0xN/+9jd8+eWXeOGFF/Dxxx/D25v3GSCiW9Pe14F1FRvxdZsWKrkbMkY/ghkj7uNCfUT0HTe18F9BQQH+9Kc/wdfXF01NTfjDH/5gi2xE5AQEUcChxmPYXLMDfWYDNP6jsVSTjiDPAKmjEZGdslhOQkNDMW7cOOzevRs7duzAlClTEBYWZotsROTgmnpbkF9eiJrO8/BUemKZJhNTh9/NW88T0aAslpP33nsPu3fvxuLFiyGKIv7yl7+gqqoKTz/9tC3yEZEDMgtm7G84iO3nd6NfMGFC8Fhkx6fA132Y1NGIyAFYLCdbtmxBQUEBPDy+vUNjVlYW0tLSWE6I6Loudl/CGm0BGrob4aNS47H4VEwMGSd1LCJyIBbLiSiKA8UE+Pa29kqlxZcRkYvpF0zYeWEfdteVQBAFTAmbhPTRi+Ht5iV1NCJyMBZbxtSpU/Hcc88hNTUVAFBcXIwpU6ZYPRgROY7azjrklxfgSm8z/N39kKtJx5jABKljEZGDslhOfv3rX2PdunUoLi4GAEyZMgXZ2dlWD0ZE9q/PZMC22l34/OJhiBAxY8R9eCRmPjy4UB8R3QGL5UQmk2HixIno6+uDUqnE1KlTeVqHiFB+tRLrtEVo62tHqFcwcjUZiPOLljoWETkBiy3jgw8+wPr16zF79myYzWY8/fTTeOqpp5Cenm6LfERkZ3r7e1FUvQ1fXj4JuUyOh6JmYsGoOXDjejhENEQslpMNGzZg48aNUKvVAIAf/vCHWLp0KcsJkQsqbSnD+opN6DJ2Y4Q6HMsSMzHSJ0LqWETkZCyWEz8/v2tO43h6evL29UQuptPQjYLKYpxuOQelXIlHYuZjTuQMKOQKqaMRkROyWE5iYmKQnZ2NhQsXQqlUYs+ePVCr1fjTn/4EAHj22WetHpKIpCGKIo5f+QqFVVvQa9IjxncU8jQZCPMOkToaETkxi+UkIiICERERMBqNMBqNuP/++22Ri4gk1qZvx7qKIpRfrYS7QoWs+BRMi5jKhfqIyOoslhPOjBC5FkEUcKDxKDbXfAaj2YjEgHgsTUhHoKe/1NGIyEXwb4KJaEBTTzPWaAtR23kBXkpP5CRmY3JYMhfqIyKbYjkhIpgFM/bWf4EdF/bCJJgwMXgcshJSMEzlI3U0InJBN1VOent7UV9fj4SEBOj1enh5ca0MImfR0N2I/PICNOguYZjKB9nxKUjiQn1EJCGLV7YdPXoUS5YswTPPPIPW1lbMnDkThw4dskU2IrKifnM/Ntd8htdPvoMG3SVMHX43fjvleRYTIpKcxXLy5ptvYu3atRg2bBiCg4ORn5+P119/3RbZiMhKqjvO49UTb2F3XQn83X3xbNITWJ6YBS+uIExEdsDiaR1BEBAcHDzwOC4uzqqBiMh6+kx92FyzEwcaj0AGGWaOeACLYubBQ+kudTQiogEWy0lYWBhKSkogk8nQ1dWF/Px8hIeH2yIbEQ2hb9oqsFZbhHZDB8K8QpCXmIkY3yipYxERfYfF0zovvfQStm7disuXL2POnDkoLy/HSy+9ZPHAgiDghRdeQHZ2NpYvX466urrrPueJJ57AunXrbi89EVnU09+Lj79Zjz+f+QCdxi7MHzUbv5z8YxYTIrJbFmdOAgMD8eabb97ygffu3Quj0Yj169ejtLQUK1euxKpVq655zltvvYXOzs5bPjYR3ZwvG77CeyfWobtfh0ifCORpMjHChzOfRGTfLJaTWbNmXfcGTPv27Rv0dadOncK0adMAAElJSSgrK7tm/86dOyGTyTB9+vRbyUtEN6HT0IUNlcUobSmDm1yJlNgFmDVyGhfqIyKHYLGcfPLJJwNfm0wm7NmzB0aj0eKBdTod1Gr1wGOFQgGTyQSlUonKykps27YNb7/9Nv785z/fVFB/fy8olUP/g1Wh+LZ4BQfzZlO2wrG2HlEU8fn5o/i4tBA9/XokBo/GU/fkIdwnVOpoLoGfbdvhWNuOFGN9Uwv//asnnngCaWlpeOaZZwZ9nVqtRk9Pz8BjQRCgVH77dsXFxWhqasJjjz2GxsZGuLm5ISIiYtBZlPb2XktRb4vZLEKhkKGlpdsqx6drBQf7cKytpFV/Feu0RdC2V8FD4Y6chFSkTJiDttYetPRxzK2Nn23b4VjbjjXHerDSY7GcnDhxYuBrURRRVVUFg8Fg8U2Tk5NRUlKCBQsWoLS0FPHx8QP7fv7znw98/c477yAoKIind4hukyAK+OLiEWyp+QxGoR9jAhOQm5AOfw8/riBMRA7JYjl5++23B76WyWTw9/fHypUrLR547ty5OHz4MHJyciCKIl555RWsXr0akZGRmD179p2lJiIAwJWeJqwpL8T5rjp4u3lhqSYd94RO5EJ9ROTQLJaTBQsWYOnSpbd8YLlc/p0/OY6Njf3O85577rlbPjaRqzMLZuyp/xyfnd8Lk2jGpJAJyIxfAh+V2vKLiYjsnMVykp+ff1vlhIiso77rItZoC9CouwxflQ+yE9IwIfguqWMREQ2Zm7pD7KOPPooJEybA3f2ft7h+9tlnrRqMiK5lNPdjx/k92NdwAIIo4P7wyUiJXQgvN0+poxERDSmL5SQpKckWOYhoEFXttVirLUSzvhVBHgHI1WQgIYDrXBGRc7phOdm0aRNSU1M5Q0IkIb2pD5trPsPBxqOQQYZZI6dhUcw8uCtUUkcjIrKaG5aTjz/+GKmpqbbMQkT/oqy1HJ9WbPp2oT7vUCzTZCLaN1LqWEREVmfxtA4R2ZbO2IPCqq040fQV5DI5Foyag4dGzYKbnP9cicg13PCnXVVV1XXvRyKKImQymcW1dYjo1oiiiK+az2BD5Wbo+nsQ5TMSeYkZiFAPlzoaEZFN3bCcREVF4d1337VlFiKX1WHoxKcVm3Cu9Ru4yd2QGrcQs0ZO4x1eicgl3bCc/GO9GyKyHlEUceTycWyq3g69qQ+j/WKQq8lAiFeQ1NGIiCRzw3KSnJxsyxxELqdV34Z8bREq26vhofDA0oQ03Bc+mbMlROTyblhOXnjhBVvmIHIZgijg84ZD2FK7C/1CP8YGJiInIRX+Hn5SRyMisgu8/J/Ihi7priBfW4gLXfVQu3ljmSYDk0KTuFAfEdG/YDkhsgGTYMLuuhLsvLAfZtGMu0OTkDH6ES7UR0R0HSwnRFZW19WANeUFuNRzBX7uvshJSMW4oDFSxyIislssJ0RWYjQbsa12N/Y3HIQIEQ+ET0FK3AJ4KrlQHxHRYFhOiKygsr0G+dpCtOrbEOQZiDxNBuL9Y6WORUTkEFhOiIaQ3qTHpuodOHzpGGSQYXbkdCyKfggqLtRHRHTTWE6Ihsi51m/wacUmdBg6Ee4dhmWJmYgaNlLqWEREDoflhOgOdRt1KKzagpNNpVDIFFgU/RDmRj0IJRfqIyK6LfzpSXSbRFHEyaZSFFRtRk9/L0YNi0SeJgPh6jCpoxEROTSWE6Lb0N7XgU8rNqGsrRwquRvSRy/GgyPu563niYiGAMsJ0S0QRAGHLx1HcfV29JkNSPCPQ64mHUGegVJHIyJyGiwnRDepubcVa7WFqOqohafSA3maDNw7/B7eep6IaIixnBBZYBbMKLl4CNtqd6FfMGF80F3ITkiBn7uv1NGIiJwSywnRIBp1l5FfXoi67gb4uKnx6JgUTAwex9kSIiIrYjkhuo5+wYRdF/ZjV91+CKKAyWHJSB+9GGo3b6mjERE5PZYTon9zvrMOa7SFuNLTBH93PyzVpOGuQI3UsYiIXAbLCdHfGcxGbKvdhZKGQxAhYnrEvXgk9mF4Kj2kjkZE5FJYTogAaK9WYa22CG19VxHiGYRcTQZG+8dIHYuIyCWxnJBL6+3XY1P1dhy5fBxymRxzIx/Egui5UCncpI5GROSyWE7IZZ1p+RrrKzai09iNCPVwLNNkInLYCKljERG5PJYTcjldxm4UVG7GV81noZQpsDhmPuZGzoBCrpA6GhERgeWEXIgoijjRdBqFlVvQY+pF9LAoLEvMQJh3qNTRiIjoX7CckEu42teOdRUb8U1bBVQKFTJHL8H0EfdyoT4iIjvEckJOTRAFHGo8huKa7TCYjdD4j0auJh2BngFSRyMiohtgOSGn1dTbgvzyQtR0noen0hPLErMwNWwSbz1PRGTnWE7I6ZgFM/Y3HMT287vRL5iQFDwWWfGp8HX3kToaERHdBJYTcioXuy9hjbYADd2N8FGp8Vh8KiaGjJM6FhER3QKWE3IK/eZ+7LywD7vrP4cgCpgSNgnpoxfD281L6mhERHSLWE7I4dV2XsCa8kI09TbD390PuZp0jAlMkDoWERHdJpYTclh9JgO21u7EFxePAABmjLgPj8TMhwcX6iMicmgsJ+SQyq9WYp22CG197Qj1CkaeJhOxfqOkjkVEREOA5YQcSm9/L4qqt+HLyychl8kxL2oWHh41G25cqI+IyGmwnJDDKG0+h/WVxegydmOkOhx5iVkY6RMudSwiIhpiLCdk9zoN3dhQWYzSlnNQypVYEvMwZkdO50J9REROiuWE7JYoijh25RSKqrai16RHrO8o5GkyEOodInU0IiKyIpYTsktt+nasqyhC+dVKuCtUyI5PwQMRU7lQHxGRC2A5IbsiiAIONB7F5prPYDQbMSYgATkJaQj09Jc6GhER2QjLCdmNKz3NyNcWorbzAryVXshJTMXksGQu1EdE5GJYTkhyZsGMPfVf4LPze2ASzZgYMh5Z8UswTMWF+oiIXBHLCUmqobsRa8oLcFF3CcNUPshOSEVS8FipYxERkYRYTkgS/eZ+7LiwF3vrv4AgCrh3+D1Ii1sILy7UR0Tk8qxWTgRBwIsvvoiKigqoVCqsWLECUVFRA/s//PBDbN++HQAwY8YMPPvss9aKQnamuuM88rUFaO5tRaCHP3I1GdAEjJY6FhER2QmrlZO9e/fCaDRi/fr1KC0txcqVK7Fq1SoAQENDA7Zs2YKCggLIZDLk5uZizpw50Gg01opDdqDP1IcPTm3HruovIIMMM0c+gMUx8+GuUEkdjYiI7IjVysmpU6cwbdo0AEBSUhLKysoG9oWFheH999+HQvHtHT5NJhPc3d2tFYXswNdtFVinLUK7oQNhXiHIS8xEjG+U5RcSEZHLsVo50el0UKvVA48VCgVMJhOUSiXc3NwQEBAAURTx+uuvY8yYMYiOjh70eP7+XlAqh/525QrFt3+mGhzMvwyxhm6DDh+VFuLAhWNQyORIH7MAaWPmc6E+G+Jn23Y41rbDsbYdKcbaauVErVajp6dn4LEgCFAq//l2BoMBv/rVr+Dt7Y3f/e53Fo/X3t5rlZxmswiFQoaWlm6rHN9ViaKI0y3nsKGiGN39OkT6RCBPk4mJMQl/H+s+qSO6hOBgH362bYRjbTsca9ux5lgPVnqsVk6Sk5NRUlKCBQsWoLS0FPHx8QP7RFHEM888gylTpuD73/++tSKQRDoNXVhfWYwzLWVwkyuRErsAs0ZO40J9RER0U6xWTubOnYvDhw8jJycHoijilVdewerVqxEZGQlBEHD8+HEYjUYcPHgQAPDTn/4UEydOtFYcsgFRFPHl5ZMoqt4GvUmPOL9o5GkyEOIVLHU0IiJyIFYrJ3K5HC+99NI122JjYwe+PnfunLXemiTQqr+KddoiaNur4KFwR05CKu4Pn8KF+oiI6JbxJmx0RwRRwBcXj2BLzWcwCv24K1CDpQlp8PfwkzoaERE5KJYTum2Xe5qQX16I81118HbzwlJNOu4JnciF+oiI6I6wnNAtMwtm7K77HDsv7IVJNGNSyARkxi+Bj0pt+cVEREQWsJzQLanvuog12gI06i7DVzUMOQmpGB98l9SxiIjIibCc0E0xmvux4/we7K3/AiJE3B8+GalxC+Gp9JQ6GhERORmWE7Koqr0Ga7VFaNa3IsgjALmaDCQExEkdi4iInBTLCd2Q3tSH4podONT4JWSQYdbIaVgcMw8qLtRHRERWxHJC11XWWo51FRvRYejEcO9Q5GkyEe0bKXUsIiJyASwndA2dsQeFVVtwouk0FDIFFoyag3mjZkEp50eFiIhsg79xCMC3t57/qvkMNlRuhq6/B1E+I5GXmIEI9XCpoxERkYthOSF0GDrxacUmnGv9Bm5yN6TFLcLMkQ/w1vNERCQJlhMXJooijlw+jk3V26E39SHeLxa5mgwEewVKHY2IiFwYy4mLaultw1ptISo7auCh8EBuQjruC5/MW88TEZHkWE5cjCAKKGk4hK21u9Av9GNcUCJyEtLg5+4rdTQiIiIALCcu5ZLuCtZoC1DX1QC1mzeWJWZiUsgEzpYQEZFdYTlxASbBhF11Jdh1YT/Mohn3hE5ExuhHoFZ5Sx2NiIjoO1hOnFxdVwPWlBfgUs8V+Ln7YmlCGsYGJUodi4iI6IZYTpyU0WzEttrd2N9wECJEPBAxFSmxC+Cp9JA6GhER0aBYTpxQZXs18ssL0dp3FcGegcjTZGC0f6zUsYiIiG4Ky4kT0Zv02FS9A4cvHYMMMsyJnIGF0XO5UB8RETkUlhMnca71G6zTbkSnsQvh3mFYlpiJqGEjpY5FRER0y1hOHFy3UYfCqi042VQKpUyBRdHzMDdqBhfqIyIih8XfYA5KFEWcbCpFQdVm9PT3InpYJPISMzHcO1TqaERERHeE5cQBtfd14NOKTShrK4dK7oaM0Y9gxoj7uFAfERE5BZYTByKIAg5fOo7i6u3oMxuQ4B+HXE06gjy5UB8RETkPlhMH0dzbgrXaIlR11MJT6YE8TSbuHX43bz1PREROh+XEzpkFM0ouHsK22l3oF0yYEHQXshJSuFAfERE5LZYTO9aou4w15QWo774IHzc1Hh2TgonB4zhbQkRETo3lxA71CybsurAfu+r2QxAFTAmbhLTRi6B240J9RETk/FhO7Mz5zjqs0RbiSk8T/N39sFSThrsCNVLHIiIishmWEzthMBuxtXYnPm84DBEipkfchyWx8+HBhfqIiMjFsJzYAe3VKqzVFqGt7ypCPIOQl5iJOL9oqWMRERFJguVEQr39emyq3oYjl09ALpPjoaiZeHjUHKgUblJHIyIikgzLiUTOtHyN9RUb0Wnsxgh1OPISMxDpM0LqWERERJJjObGxLmM3Cio346vms1DKFFgcMx9zI2dAIVdIHY2IiMgusJzYiCiKOH7lKxRVbUWPqRcxvlHI02QizDtE6mhERER2heXEBq72tWNdxUZ801YBlUKFzNFLMH3EvVyoj4iI6DpYTqxIEAUcavwSxTU7YDAbofEfjVxNOgI9A6SORkREZLdYTqykqbcF+eWFqOk8Dy+lJ5YnZmFK2CTeep6IiMgClpMhZhbM2NdwANvP74FJMCEpeByy4lPg6+4jdTQiIiKHwHIyhC52X8IabQEauhvho1IjOz4VE0PGSR2LiIjIobCcDIF+cz92XtiH3fWfQxAFTA27G2mjF8HbzUvqaERERA6H5eQO1XZewJryQjT1NiPAwx+5CelIDIyXOhYREZHDYjm5TX0mA7bU7sSBi0cAAA+OuB+LY+bDQ+kucTIiIiLHxnJyG8rbKrG2oghX+9oR6hWCPE0GYv1GSR2LiIjIKbCc3ILe/l4UVW3Dl1dOQi6TY37ULMwfNRtuXKiPiIhoyLCc3KTS5nNYX1mMLmM3RqrDkZeYhZE+4VLHIiIicjosJxZ0GrqxobIYpS3noJQrsST2YcweOZ0L9REREVkJy8kNiKKIL6+cwsaqreg16RHrOwp5mgyEcqE+IiIiq2I5uY42/VWsq9iI8quVcFeokB2fggcipnKhPiIiIhtgOfkXgijgwMWj2Fz7GYxmI8YEJGCpJg0BHv5SRyMiInIZVisngiDgxRdfREVFBVQqFVasWIGoqKiB/Rs2bMCnn34KpVKJp59+GjNnzrRWlEHdowmBp5cKV3qaka8tRG3nBXgrvZCTmIrJYclcqI+IiMjGrFZO9u7dC6PRiPXr16O0tBQrV67EqlWrAAAtLS345JNPUFRUBIPBgNzcXNx///1QqVTWinND6Q9G40jbUbx6fDtMohnJIeORGb8Ew1RcqI+IiEgKVisnp06dwrRp0wAASUlJKCsrG9h39uxZTJw4ESqVCiqVCpGRkdBqtRg/fry14tzQe2Uf41xrOYapfJCTkIoJwWNtnoGIiIj+yWrlRKfTQa1WDzxWKBQwmUxQKpXQ6XTw8fnnzIS3tzd0Ot2gx/P394JSOfR/vhsbHIkI/1BkjV0Etcp7yI9P3xUczFkpW+J42w7H2nY41rYjxVhbrZyo1Wr09PQMPBYEAUql8rr7enp6rikr19Pe3muVnHOHz0ZwsA9aWrqhR7dV3oP+6R9jTbbB8bYdjrXtcKxtx5pjPVjpsdrfxiYnJ+PAgQMAgNLSUsTH/3Ol3vHjx+PUqVMwGAzo7u5GTU3NNfuJiIjIdVlt5mTu3Lk4fPgwcnJyIIoiXnnlFaxevRqRkZGYPXs2li9fjtzcXIiiiJ/85Cdwd+dqvkRERATIRFEUpQ5xM6w5hccpQtvhWNsWx9t2ONa2w7G2Hac7rUNERER0O1hOiIiIyK6wnBAREZFdYTkhIiIiu8JyQkRERHaF5YSIiIjsCssJERER2RWWEyIiIrIrLCdERERkV1hOiIiIyK44zO3riYiIyDVw5oSIiIjsCssJERER2RWWEyIiIrIrLCdERERkV1hOiIiIyK6wnBAREZFdcZlyIggCXnjhBWRnZ2P58uWoq6u7Zv+GDRuQlpaGrKwslJSUSJTSeVga7w8//BCZmZnIzMzEn/70J4lSOgdLY/2P5zzxxBNYt26dBAmdh6Wx/uKLL5CVlYWsrCy8+OKL4J0a7oyl8f7ggw+QlpaG9PR07NmzR6KUzuXMmTNYvnz5d7bv378f6enpyM7OxoYNG6wfRHQRu3btEn/xi1+IoiiKp0+fFn/wgx8M7GtubhYXLVokGgwGsaura+Brun2DjXd9fb2Ympoqmkwm0Ww2i9nZ2WJ5eblUUR3eYGP9D3/4wx/EjIwMce3atbaO51QGG+vu7m5x4cKFYltbmyiKovjuu+8OfE23Z7Dx7uzsFGfMmCEaDAaxo6NDfPDBB6WK6TTeffddcdGiRWJmZuY1241Gozhnzhyxo6NDNBgMYlpamtjc3GzVLC4zc3Lq1ClMmzYNAJCUlISysrKBfWfPnsXEiROhUqng4+ODyMhIaLVaqaI6hcHGOywsDO+//z4UCgXkcjlMJhPc3d2liurwBhtrANi5cydkMhmmT58uRTynMthYnz59GvHx8XjttdeQm5uLoKAgBAQESBXVKQw23p6enggPD4der4der4dMJpMqptOIjIzEO++8853tNTU1iIyMhK+vL1QqFSZNmoSTJ09aNYvSqke3IzqdDmq1euCxQqGAyWSCUqmETqeDj4/PwD5vb2/odDopYjqNwcbbzc0NAQEBEEURr7/+OsaMGYPo6GgJ0zq2wca6srIS27Ztw9tvv40///nPEqZ0DoONdXt7O44dO4bi4mJ4eXkhLy8PSUlJ/GzfgcHGGwCGDx+OhQsXwmw246mnnpIqptOYN28eLl68+J3tUvyOdJlyolar0dPTM/BYEISBD/i/7+vp6bnmPwTdusHGGwAMBgN+9atfwdvbG7/73e+kiOg0Bhvr4uJiNDU14bHHHkNjYyPc3NwQERHBWZTbNNhY+/n5Ydy4cQgODgYA3H333SgvL2c5uQODjfeBAwfQ3NyMffv2AQAef/xxJCcnY/z48ZJkdWZS/I50mdM6ycnJOHDgAACgtLQU8fHxA/vGjx+PU6dOwWAwoLu7GzU1Ndfsp1s32HiLoohnnnkGCQkJeOmll6BQKKSK6RQGG+uf//znKCgowCeffILU1FR873vfYzG5A4ON9dixY1FZWYmrV6/CZDLhzJkziIuLkyqqUxhsvH19feHh4QGVSgV3d3f4+Pigq6tLqqhOLTY2FnV1dejo6IDRaMTJkycxceJEq76ny8yczJ07F4f/f3v3GhLV1gZw/F8GcyqJQlCysA8VaeiHLqSUmVaGpTM6KmOJMohdSFMqMcwGMTAh+1AkEVphJmSWmZcm0G6WaJlEZZpioYUDlhRkmuZlZt4PcobmtY6d06nm5PODgYG191prr71hnllrs57aWjZv3ozZbCYzM5O8vDxcXFxYt24d0dHRREZGYjab2bNnj7wD8Z3+arxNJhMPHjxgaGiImotcxhkAAAZhSURBVJoaAPbu3fvDH/bf1XjPtvj3jDfWSUlJbN26FYCAgAD5k/Odxhvvuro6NBoNkydPZunSpaxatepXd/m3UlFRQX9/PxEREaSkpBAbG4vZbCYsLAwnJ6cf2rZkJRZCCCGETZkwyzpCCCGE+G+Q4EQIIYQQNkWCEyGEEELYFAlOhBBCCGFTJDgRQgghhE2R4ESI35zBYMDd3Z3g4GCrT1dX11fPyc7O/uI21r/Ctm3bePPmDZ2dnaSmpgLw9OlTDhw48FPa7+3tJT4+/qe0JYQYNWH2ORFiInN0dKSsrOxXd+MfOXXqFAD19fV0dnYC4OHhgYeHx09pv6enh5aWlp/SlhBilMycCDGBtbW1ER0dTVhYGH5+fhQWFlqVDw8Pk5ycTEhICCEhIZZU6W/fviUuLs6Srr6urm5M3dnZ2ezfvx+NRoO/vz+nT58GRrcgz8jIIDAwkKCgIHJzcwF4/fo1UVFRhIaGEh4ezuPHjwFYu3YtBoOBjIwMmpqaOHjwIPX19URHR9Pa2opSqbS0eevWLXbu3AlAbm4uarUalUpFVlYW/7+lk8FgICAggC1bthATE0NfXx+JiYlERETg5+dHamoqZrOZjIwMuru7LbMnpaWlqNVqgoODSU1NZXBw8N+4FUKIz0hwIsQE0N3dbbWk82egcOnSJeLi4rh8+TLnzp0jKyvL6rxHjx7R09NDaWkpOTk5lkykhw4dIiwsjJKSEk6ePElaWtoXE4E1NTWRl5dHSUkJRUVFNDc3U1hYSFdXF+Xl5Vy6dImqqiqqq6spLi7G19eXkpISEhMTefjwoVVdOp0Od3d3q1xMrq6uTJo0iba2NgD0ej0qlYq7d+/S1NREcXGxJb9QeXn5mP51dHRw5MgR8vLyqK6uxs3NjaKiIiorK2loaKC5uRmdToejoyMnTpzg+fPnXLx4kQsXLlBWVoaDgwNnzpz5vpsjhBhDlnWEmAC+tqyTkpJCTU0NOTk5tLW10d/fb1W+cOFCOjo6iI2NxcfHh3379gFQV1dHe3s7x48fB2BkZITOzk7c3Nyszg8KCmL69OnA6AzI/fv3efLkCWq1Gjs7O6ZOnYpSqeTevXts2LCBhIQEWlpaWLNmDVFRUd90bSqVCr1ej4uLCw0NDWRmZnLs2DEaGxsJDQ0F4NOnTzg7O48518HBgblz51r62tjYyNmzZ2lvb+f9+/f09/czc+ZMy/H19fW8evUKjUYDjM4sLV68+Jv6KYT4dhKcCDGB7d69mxkzZuDn58emTZu4evWqVfmsWbPQ6/XU1tZy584d1Go1er0ek8lEfn6+5Ye7u7sbBweHMfV/ntTRZDJhZ2eHyWSyOsZsNmM0Glm2bBl6vZ7q6mquXbvGlStXyMvLG/calEolWq0WV1dXvL29USgUGI1GtFotMTExAHz48OGLCSb/+OMPy/eCggIqKyvRaDSsXLmStra2MUtBRqORjRs3otPpgNHsrEajcdw+CiH+HlnWEWICq62tJTExkfXr11uyv37+Y3vz5k2Sk5Px9fVFp9Mxbdo0urq68PLy4vz58wC8ePECpVLJwMDAmPpv3LjB0NAQPT093L59G29vb7y8vCgtLcVoNDIwMEBFRQWenp5kZWVRXl6OWq0mLS2NZ8+eWdVlZ2fHyMjImDacnJyYPXs2ubm5qFQqALy8vCgrK+Pjx4+MjIwQHx9PZWXluGMRERGBSqVicHCQ1tZWTCYTU6ZMsbTr6enJ9evXeffuHWazmfT0dPLz8//GiAshvoXMnAgxgSUkJBAZGYlCocDV1ZU5c+ZgMBgs5T4+PlRVVREYGIhCoUClUrFo0SJ0Oh1paWmWl1GzsrKwt7cfU79CoSAyMpK+vj527NjBggULmDdvHi9fviQ4OJjh4WGUSiX+/v64u7uTlJRESUkJdnZ2HD582Kqu+fPn09vbS3JyMuHh4VZlwcHBHD16lBUrVgCjS0itra1oNBqMRiOrV69GrVb/5VhotVrS09PJzc3F3t6eJUuWYDAYWL58Oc7OzkRHR1NQUMCuXbvQarWYTCbc3NzYvn37Pxp7IcTXSVZiIcQP8ec+KQkJCb+4J0KI/xpZ1hFCCCGETZGZEyGEEELYFJk5EUIIIYRNkeBECCGEEDZFghMhhBBC2BQJToQQQghhUyQ4EUIIIYRNkeBECCGEEDblf5moVjOGEMRwAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fpr, tpr, thresholds = roc_curve(y_test, y_test_hat_probs, drop_intermediate=True)\n", + "\n", + "f, ax = plt.subplots(figsize=(9, 6))\n", + "_ = plt.plot(fpr, tpr, [0,1], [0, 1])\n", + "_ = plt.title('AUC ROC')\n", + "_ = plt.xlabel('False positive rate')\n", + "_ = plt.ylabel('True positive rate')\n", + "plt.style.use('seaborn')\n", + "\n", + "plt.savefig('auc_roc.png', dpi=600)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/LogisticRegression_Part1.ipynb b/LogisticRegression_Part1.ipynb new file mode 100644 index 0000000..40900af --- /dev/null +++ b/LogisticRegression_Part1.ipynb @@ -0,0 +1,326 @@ +{ + "cells": [ + { + "metadata": { + "_uuid": "8f2839f25d086af736a60e9eeb907d3b93b6e0e5", + "_cell_guid": "b1076dfc-b9ad-4769-8c92-a6c4dae69d19", + "trusted": false, + "collapsed": true + }, + "cell_type": "code", + "source": "# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load in \n\nimport numpy as np\nimport pandas as pd\nimport tensorflow as tf\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.linear_model import LogisticRegression\nfrom sklearn.pipeline import Pipeline\nfrom sklearn.metrics import roc_curve, roc_auc_score, classification_report, accuracy_score, confusion_matrix \nimport seaborn as sns\nimport matplotlib.pyplot as plt\n\n# Input data files are available in the \"../input/\" directory.\n# For example, running this (by clicking run or pressing Shift+Enter) will list the files in the input directory\n\nimport os\nprint(os.listdir(\"../input\"))\n\n# Any results you write to the current directory are saved as output.", + "execution_count": null, + "outputs": [] + }, + { + "metadata": { + "_uuid": "6d3b41843580ae037846b7079f2fd79d7add5147", + "_cell_guid": "41650b3a-dff8-4d14-9595-6b3fb4ccdea7" + }, + "cell_type": "markdown", + "source": "## Load data and visualize the data" + }, + { + "metadata": { + "_uuid": "d629ff2d2480ee46fbb7e2d37f6b5fab8052498a", + "collapsed": true, + "_cell_guid": "79c7e3d0-c299-4dcb-8224-4455121ee9b0", + "trusted": false + }, + "cell_type": "code", + "source": "credit_card = pd.read_csv('../input/creditcard.csv')", + "execution_count": null, + "outputs": [] + }, + { + "metadata": { + "_uuid": "c4e55ed51d60613c4f19457af0131b4fb973fad9", + "_cell_guid": "fd1b18c4-d0bf-4d81-b784-3fcec2578c12", + "trusted": false, + "collapsed": true + }, + "cell_type": "code", + "source": "f, ax = plt.subplots(figsize=(7, 5))\nsns.countplot(x='Class', data=credit_card)\n_ = plt.title('# Fraud vs NonFraud')\n_ = plt.xlabel('Class (1==Fraud)')", + "execution_count": null, + "outputs": [] + }, + { + "metadata": { + "_uuid": "5b132aff2b6d28b35e2c0ff256a3f97ff74f3cb3", + "_cell_guid": "19175b74-8157-418c-b6b0-6c2a1b719814" + }, + "cell_type": "markdown", + "source": "As we can see we have mostly non-fraudulent transactions. Such a problem is also called inbalanced class problem.\n\n99.8% of all transactions are non-fraudulent. The easiest classifier would always predict no fraud and would be in almost all cases correct. Such classifier would have a very high accuracy but is quite useless." + }, + { + "metadata": { + "_uuid": "5ee8b7fcd59def3b33733b5bbfddf39709dfd077", + "_cell_guid": "0c90460f-5a34-46a0-bd6f-88e699b27d2f", + "trusted": false, + "collapsed": true + }, + "cell_type": "code", + "source": "base_line_accuracy = 1-np.sum(credit_card.Class)/credit_card.shape[0]\nbase_line_accuracy", + "execution_count": null, + "outputs": [] + }, + { + "metadata": { + "_uuid": "8ecd2b9433bc7759f08a3c5c08627a57f7a2a2e3", + "_cell_guid": "0f4090aa-ccfc-442f-ada5-03466fccbb59" + }, + "cell_type": "markdown", + "source": "For such an inbalanced class problem we could use over or undersampling methods to try to balance the classes (see inbalance-learn for example: https://github.com/scikit-learn-contrib/imbalanced-learn), but this out of the scope of todays post. We will come back to this in a later post.\n\nAs accuracy is not very informative in this case the AUC (Aera under the curve) a better metric to assess the model quality. The AUC in a two class classification class is equal to the probability that our classifier will detect a fraudulent transaction given one fraudulent and genuiune transaction to choice from. Guessing would have a probability of 50%." + }, + { + "metadata": { + "_uuid": "16728c540fc206821df972fcd071646f48b385e5", + "collapsed": true, + "_cell_guid": "3de8033f-5ddb-4a12-9230-c1cd01ab68e2", + "trusted": false + }, + "cell_type": "code", + "source": "X = credit_card.drop(columns='Class', axis=1)\ny = credit_card.Class.values", + "execution_count": null, + "outputs": [] + }, + { + "metadata": { + "_uuid": "7fd243cb53c6126ef7001ac3a8c92649b6effd94", + "_cell_guid": "cacba1af-23cd-4a35-9603-c904387956b4" + }, + "cell_type": "markdown", + "source": "Due to the construction of the dataset (PCA transformed features, which minimizes the correlation between factors), we dont have any highly correlated features. Multicolinearity could cause problems in a logisitc regression.\n\nTo test for multicolinearity one could look into the correlation matrix (works only for non categorical features) or run partial regressions and compare the standard errors or use pseudo-R^2 values and calculate Variance-Inflation-Factors.\n\n" + }, + { + "metadata": { + "_uuid": "5c3a98163d12601711b33425f479edfb37a92fca", + "_cell_guid": "ff310572-91fe-40d7-8370-89088320d9f0", + "trusted": false, + "collapsed": true + }, + "cell_type": "code", + "source": "corr = X.corr()\n\nmask = np.zeros_like(corr, dtype=np.bool)\nmask[np.triu_indices_from(mask)] = True\n\ncmap = sns.diverging_palette(220, 10, as_cmap=True)\n\n# Draw the heatmap with the mask and correct aspect ratio\nf, ax = plt.subplots(figsize=(11, 9))\nsns.heatmap(corr, mask=mask, cmap=cmap, vmax=.3, center=0,\n square=True, linewidths=.5, cbar_kws={\"shrink\": .5})", + "execution_count": null, + "outputs": [] + }, + { + "metadata": { + "_uuid": "be916ae070cd9df31f67306364b5faa00de99b79", + "_cell_guid": "65c81b41-418d-47fa-a484-98e643aaf208" + }, + "cell_type": "markdown", + "source": "## Logisitc Regression with Sklearn\n\nShort reminder of Logistic Regression:\n\nIn Logisitic Regression the logits (logs of the odds) are assumed to be a linear function of the features\n\n$$L=\\log(\\frac{P(Y=1)}{1-P(Y=1)}) = \\beta_0 + \\sum_{i=1}^n \\beta_i X_i. $$\n\nSolving this equatation for $p=P(Y=1)$ yields to\n\n$$ p = \\frac{\\exp(L)}{1-\\exp(L)}.$$\n\nThe parameters $\\beta_i$ can be derived by Maximum Likelihood Estimation (MLE). The likelihood for a given $m$ observation $Y_j$ is\n\n$$ lkl = \\prod_{j=1}^m p^{Y_j}(1-p)^{1-Y_j}.$$\n\nTo find the maximum of the likelihood is equivalent to the minimize the negative logarithm of the likelihood (loglikelihood).\n\n$$ -llkh = -\\sum_{j=1}^m Y_j \\log(p) + (1-Y_j) \\log(1-p),$$\n\nwhich is numerical more stable. The log-likelihood function has the same form as the cross-entropy error function for a discrete case.\n\nSo finding the maximum likelihood estimator is the same problem as minimizing the average cross entropy error function.\n\nIn SciKit-Learn uses by default a coordinate descent algorithm to find the minimum of L2 regularized version of the loss function (see. http://scikit-learn.org/stable/modules/linear_model.html#logistic-regression).\n\nThe main difference between L1 (Lasso) and L2 (Ridge) regulaziation is, that the L1 prefer a sparse solution (the higher the regulazation parameter the more parameter will be zero) while L2 enforce small parameter values." + }, + { + "metadata": { + "_uuid": "67c42133bf74c0f3aaa84e49456fe04a2f5e9d96", + "_cell_guid": "763a278e-91af-4fe6-a255-ddaea0bbd790" + }, + "cell_type": "markdown", + "source": "## Train the model\n\n### Training and test set\n\nFirst we split our data set into a train and a validation set by using the function train_test_split. The model performace " + }, + { + "metadata": { + "_uuid": "d19b826169ff464f82eba4e0b349bacc4361c8a0", + "collapsed": true, + "_cell_guid": "d54b91c2-fc78-4b24-a7c8-5b1a974cdf36", + "trusted": false + }, + "cell_type": "code", + "source": "np.random.seed(42)\nX_train, X_test, y_train, y_test = train_test_split(X, y)", + "execution_count": null, + "outputs": [] + }, + { + "metadata": { + "_uuid": "d1ea1658ffca81f4f9c83e9759b90f1311ce7a71", + "_cell_guid": "88a09d8f-4238-4560-a180-17d048ae5dd7" + }, + "cell_type": "markdown", + "source": "### Model definition\n\nAs preperation we standardize our features to have zero mean and a unit standard deviation. The convergence of gradient descent algorithm are better. We use the class `StandardScaler`. The class *StandardScaler* has the method `fit_transform()` which learn the mean $\\mu_i$ and standard deviation $\\sigma_i$ of each feature $i$ and return a standardized version $\\frac{x_i - \\mu_i}{\\sigma}$. We learn the mean and sd on the training data. We can apply the same standardization on the test set with the function *transform()*.\n\n\nThe logistic regression is implemented in the class `LogisticRegression`, we will use for now the default parameterization. The model can be fit using the function `fit()`. After fitting the model can be used to make predicitons `predict()` or return the estimated the class probabilities `predict_proba()`.\n\nWe combine both steps into a Pipeline. The pipline performs both steps automatically. When we call the method `fit()` of the pipeline, it will invoke the method `fit_and_transform()` for all but the last step and the method `fit()` of the last step, which is equivalent to:\n\n```python\nlr.fit(scaler.fit_transform(X_train), y_train)\n```\n\nor visualized as a dataflow:\n\n```X_train => scaler.fit_transform(.) => lr.fit(., y_train)```\n\nIf we invoke the method `predict()` of the pipeline its equvivalent to\n\n\n```python\nlr.predict(scaler.transform(X_train))\n```\n\n\n\n" + }, + { + "metadata": { + "_uuid": "86ecda4d1700405da9ed0d8bc6f7d8eabc0f1ddb", + "collapsed": true, + "_cell_guid": "fb45b18e-9357-4a98-9d98-078f96c3a591", + "trusted": false + }, + "cell_type": "code", + "source": "scaler = StandardScaler()\nlr = LogisticRegression()\nmodel1 = Pipeline([('standardize', scaler),\n ('log_reg', lr)])", + "execution_count": null, + "outputs": [] + }, + { + "metadata": { + "_uuid": "7c6abdd6d0db2ffede02f01d2d3f1ff916de0eee", + "_cell_guid": "804537d0-b635-4e90-a88c-56fac8749e2e" + }, + "cell_type": "markdown", + "source": "In the next step we fit our model to the training data" + }, + { + "metadata": { + "_uuid": "bc769713962566efac8b2310f45d7ebe79dcb173", + "_cell_guid": "b8f46ca5-f69a-4755-abbf-9cc76723c133", + "trusted": false, + "collapsed": true + }, + "cell_type": "code", + "source": "model1.fit(X_train, y_train)", + "execution_count": null, + "outputs": [] + }, + { + "metadata": { + "_uuid": "dc612edede19c594a8f28e3c396f0797a98888ba", + "_cell_guid": "4c3a4e42-36ee-4814-b8b2-0513ed7404cb" + }, + "cell_type": "markdown", + "source": "### Training score and Test score\n\n`confusion_matrix()` returns the confusion matrix, C where $C_{0,0}$ are the true negatives (TN) and $C_{0,1}$ the false positives (FP) and vice-versa for the positives in the 2nd row. We use the function `accurary_score()` to calculate the accuracy our models on the train and test data. We see that the accuracy is quite high (99,9%) which is expected in such an unbalanced class problem. With the method `roc_auc_score()`can we get the area under the receiver-operator-curve (AUC) for our simple model." + }, + { + "metadata": { + "_uuid": "e4f22259d9018f233874d9e4afdb42ec882bd967", + "_cell_guid": "f708ecc2-9114-4b5e-86fe-bd8423aa622c", + "trusted": false, + "collapsed": true + }, + "cell_type": "code", + "source": "y_train_hat = model1.predict(X_train)\ny_train_hat_probs = model1.predict_proba(X_train)[:,1]\ntrain_accuracy = accuracy_score(y_train, y_train_hat)*100\ntrain_auc_roc = roc_auc_score(y_train, y_train_hat_probs)*100\nprint('Confusion matrix:\\n', confusion_matrix(y_train, y_train_hat))\nprint('Training accuracy: %.4f %%' % train_accuracy)\nprint('Training AUC: %.4f %%' % train_auc_roc)", + "execution_count": null, + "outputs": [] + }, + { + "metadata": { + "_uuid": "6c97c5b3180d8cc2cd7019137aae19d45f53906d", + "_cell_guid": "94f52985-d9ca-4fa3-ba67-84b1ab4f27c6" + }, + "cell_type": "markdown", + "source": "![](http://)Our model is able to detect 68 fraudulent transactions out of 113 (recall of 60%) and produce 12 false alarms (<0.02%) on the test data." + }, + { + "metadata": { + "_uuid": "a83a2229893813ad2986910d20f562b369b76eb4", + "_cell_guid": "ca356e72-46ac-4766-8640-11d644c4534f", + "trusted": false, + "collapsed": true + }, + "cell_type": "code", + "source": "y_test_hat = model1.predict(X_test)\ny_test_hat_probs = model1.predict_proba(X_test)[:,1]\ntest_accuracy = accuracy_score(y_test, y_test_hat)*100\ntest_auc_roc = roc_auc_score(y_test, y_test_hat_probs)*100\nprint('Confusion matrix:\\n', confusion_matrix(y_test, y_test_hat))\nprint('Training accuracy: %.4f %%' % test_accuracy)\nprint('Training AUC: %.4f %%' % test_auc_roc)", + "execution_count": null, + "outputs": [] + }, + { + "metadata": { + "_uuid": "c22dc7db8f1a4c9d133c74ec502cfe9614667be8", + "_cell_guid": "fddb2dfa-7826-40ce-b49d-6177fc659aa2" + }, + "cell_type": "markdown", + "source": "With the function `classification_report()` we print get the precision, recall per each class." + }, + { + "metadata": { + "_uuid": "258dda7a672ec39dd33f095dbc59cc42832f50b0", + "_cell_guid": "26394d30-80ec-4db0-929c-0a81585d4d66", + "trusted": false, + "collapsed": true + }, + "cell_type": "code", + "source": "print(classification_report(y_test, y_test_hat, digits=6))", + "execution_count": null, + "outputs": [] + }, + { + "metadata": { + "_uuid": "e0fed511c8db4728079d5210a3a6a97a5eadfb72", + "_cell_guid": "c715932b-cac8-4582-a866-dcc913a5b6d0" + }, + "cell_type": "markdown", + "source": "To visualize the Receiver-Operator-Curve we use the function `roc_curve`. The method returns the true positive rate (recall) and the false positive rate (probability for a false alarm) for a bunch of different thresholds. This curve shows the trade-off between recall (detect fraud) and false alarm probability.\n\nIf we classifiy all transaction as fraud, we would have a recall of 100% but also the highest false alarm rate possible (100%). The naive way to minimize the false alarm probability is to classify all transaction as legitime. **" + }, + { + "metadata": { + "_uuid": "9317259c2c241aa6cb00346b5b91bd485d7ec448", + "_cell_guid": "d5db8aad-8866-4639-9a1c-cd32e33e11a1", + "trusted": false, + "collapsed": true + }, + "cell_type": "code", + "source": "fpr, tpr, thresholds = roc_curve(y_test, y_test_hat_probs, drop_intermediate=True)\n\nf, ax = plt.subplots(figsize=(9, 6))\n_ = plt.plot(fpr, tpr, [0,1], [0, 1])\n_ = plt.title('AUC ROC')\n_ = plt.xlabel('False positive rate')\n_ = plt.ylabel('True positive rate')\nplt.style.use('seaborn')\n\nplt.savefig('auc_roc.png', dpi=600)", + "execution_count": null, + "outputs": [] + }, + { + "metadata": { + "_uuid": "9e1692cf8dd385592b20e30f229ff192a30ce5c7", + "collapsed": true, + "_cell_guid": "9d834f9c-5817-4df8-b4a4-e31103236daf" + }, + "cell_type": "markdown", + "source": "Our model classify all transaction with a fraud probability => 50% as fraud. If we choose the threshold higher, we could reach a lower false positive rate but we would also miss more fraudulent transactions. If we choose the thredhold lower we can catch more fraud but need to investigate more false positives.\n\nDepending on the costs for each error, it make sense to select another threshold.\n\nIf we set the threshold to 90% the recall decrease from 60% to 45%. while the false positve rate is the same. We can see that our model assign some non-fraudulent a very high probability to be fraud" + }, + { + "metadata": { + "_uuid": "dc371656eeeced96d8343a37312d92bf116eede0", + "_cell_guid": "a5a4d2a5-c86d-4e27-8245-08325644794d", + "trusted": false, + "collapsed": true + }, + "cell_type": "code", + "source": "y_hat_90 = (y_test_hat_probs > 0.90 )*1\nprint('Confusion matrix:\\n', confusion_matrix(y_test, y_hat_90))\nprint(classification_report(y_test, y_hat_90, digits=6))\n", + "execution_count": null, + "outputs": [] + }, + { + "metadata": { + "_uuid": "9f177e8213acc10e0dc7ab4f49c33a06989dce9d", + "_cell_guid": "d8009ffc-1caf-4cf5-808b-3347ad39eb49" + }, + "cell_type": "markdown", + "source": "If we set the threshold down to 10%, we can detect around 75% of all fraud case but almost double our false positive rate (now 25 false alarms)" + }, + { + "metadata": { + "_uuid": "69fbb96b4062e67e8df8beddab7cd48598b6e377", + "_cell_guid": "df14952e-6d10-4964-b977-fd732a878347", + "trusted": false, + "collapsed": true + }, + "cell_type": "code", + "source": "y_hat_10 = (y_test_hat_probs > 0.10)*1\nprint('Confusion matrix:\\n', confusion_matrix(y_test, y_hat_10))\nprint(classification_report(y_test, y_hat_10, digits=4))", + "execution_count": null, + "outputs": [] + }, + { + "metadata": { + "_uuid": "500e582bb9055575cf61d0e7fccd465afbab1c5b", + "_cell_guid": "9e9a216b-0533-46d8-bec0-a82b7bccc667" + }, + "cell_type": "markdown", + "source": "Where to go from here?\n-------------------\n\nWe just scratched the surface of sklearn and logistic regression. For example we could spent much more time with the \n\n- feature selection / engineering (which is a bit hard without any background information about the features),\n- we could try techniques to counter the data inbalance and \n- we could use cross-validation to fine tune the hyperparameters (regulaziation constant C) or\n- try a different regulization (Lasso/Elastic Net) or \n- optimizer (stochastic gradient desent instead of coordinate descnet)\n- adjust class weights to move the decision boundary (make missed frauds more expansive in the loss function)\n!- and finally we could try different classifer models in sklearn like decision trees, random forrests, knn, naive bayes or support vector machines. \n\nBut for now we will stop here and will implement a logisitc regression model with stochastic gradient descent in TensorFlow and then extend it to a neural net.\n\n" + } + ], + "metadata": { + "language_info": { + "name": "python", + "version": "3.6.5", + "mimetype": "text/x-python", + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "pygments_lexer": "ipython3", + "nbconvert_exporter": "python", + "file_extension": ".py" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} \ No newline at end of file From 2d6eb2afd8140d08422a1aea05ae98ea011ce51e Mon Sep 17 00:00:00 2001 From: Matthias Groncki Date: Tue, 8 May 2018 19:14:45 +0700 Subject: [PATCH 2/8] Updated Version of LR_part1 --- LogisticRegression_Part1.ipynb | 209 +++++++++++++++++++++++++-------- 1 file changed, 157 insertions(+), 52 deletions(-) diff --git a/LogisticRegression_Part1.ipynb b/LogisticRegression_Part1.ipynb index 40900af..228a62f 100644 --- a/LogisticRegression_Part1.ipynb +++ b/LogisticRegression_Part1.ipynb @@ -4,13 +4,23 @@ "metadata": { "_uuid": "8f2839f25d086af736a60e9eeb907d3b93b6e0e5", "_cell_guid": "b1076dfc-b9ad-4769-8c92-a6c4dae69d19", - "trusted": false, - "collapsed": true + "trusted": true }, "cell_type": "code", "source": "# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load in \n\nimport numpy as np\nimport pandas as pd\nimport tensorflow as tf\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.linear_model import LogisticRegression\nfrom sklearn.pipeline import Pipeline\nfrom sklearn.metrics import roc_curve, roc_auc_score, classification_report, accuracy_score, confusion_matrix \nimport seaborn as sns\nimport matplotlib.pyplot as plt\n\n# Input data files are available in the \"../input/\" directory.\n# For example, running this (by clicking run or pressing Shift+Enter) will list the files in the input directory\n\nimport os\nprint(os.listdir(\"../input\"))\n\n# Any results you write to the current directory are saved as output.", - "execution_count": null, - "outputs": [] + "execution_count": 1, + "outputs": [ + { + "output_type": "stream", + "text": "/opt/conda/lib/python3.6/site-packages/h5py/__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.\n from ._conv import register_converters as _register_converters\n", + "name": "stderr" + }, + { + "output_type": "stream", + "text": "['creditcard.csv']\n", + "name": "stdout" + } + ] }, { "metadata": { @@ -25,24 +35,32 @@ "_uuid": "d629ff2d2480ee46fbb7e2d37f6b5fab8052498a", "collapsed": true, "_cell_guid": "79c7e3d0-c299-4dcb-8224-4455121ee9b0", - "trusted": false + "trusted": true }, "cell_type": "code", "source": "credit_card = pd.read_csv('../input/creditcard.csv')", - "execution_count": null, + "execution_count": 2, "outputs": [] }, { "metadata": { "_uuid": "c4e55ed51d60613c4f19457af0131b4fb973fad9", "_cell_guid": "fd1b18c4-d0bf-4d81-b784-3fcec2578c12", - "trusted": false, - "collapsed": true + "trusted": true }, "cell_type": "code", "source": "f, ax = plt.subplots(figsize=(7, 5))\nsns.countplot(x='Class', data=credit_card)\n_ = plt.title('# Fraud vs NonFraud')\n_ = plt.xlabel('Class (1==Fraud)')", - "execution_count": null, - "outputs": [] + "execution_count": 3, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": "", + "image/png": "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\nAABJRU5ErkJggg==\n" + }, + "metadata": {} + } + ] }, { "metadata": { @@ -56,13 +74,21 @@ "metadata": { "_uuid": "5ee8b7fcd59def3b33733b5bbfddf39709dfd077", "_cell_guid": "0c90460f-5a34-46a0-bd6f-88e699b27d2f", - "trusted": false, - "collapsed": true + "trusted": true }, "cell_type": "code", "source": "base_line_accuracy = 1-np.sum(credit_card.Class)/credit_card.shape[0]\nbase_line_accuracy", - "execution_count": null, - "outputs": [] + "execution_count": 4, + "outputs": [ + { + "output_type": "execute_result", + "execution_count": 4, + "data": { + "text/plain": "0.9982725143693799" + }, + "metadata": {} + } + ] }, { "metadata": { @@ -77,11 +103,11 @@ "_uuid": "16728c540fc206821df972fcd071646f48b385e5", "collapsed": true, "_cell_guid": "3de8033f-5ddb-4a12-9230-c1cd01ab68e2", - "trusted": false + "trusted": true }, "cell_type": "code", "source": "X = credit_card.drop(columns='Class', axis=1)\ny = credit_card.Class.values", - "execution_count": null, + "execution_count": 5, "outputs": [] }, { @@ -96,13 +122,29 @@ "metadata": { "_uuid": "5c3a98163d12601711b33425f479edfb37a92fca", "_cell_guid": "ff310572-91fe-40d7-8370-89088320d9f0", - "trusted": false, - "collapsed": true + "trusted": true }, "cell_type": "code", "source": "corr = X.corr()\n\nmask = np.zeros_like(corr, dtype=np.bool)\nmask[np.triu_indices_from(mask)] = True\n\ncmap = sns.diverging_palette(220, 10, as_cmap=True)\n\n# Draw the heatmap with the mask and correct aspect ratio\nf, ax = plt.subplots(figsize=(11, 9))\nsns.heatmap(corr, mask=mask, cmap=cmap, vmax=.3, center=0,\n square=True, linewidths=.5, cbar_kws={\"shrink\": .5})", - "execution_count": null, - "outputs": [] + "execution_count": 6, + "outputs": [ + { + "output_type": "execute_result", + "execution_count": 6, + "data": { + "text/plain": "" + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": "", + "image/png": "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\n" + }, + "metadata": {} + } + ] }, { "metadata": { @@ -125,11 +167,11 @@ "_uuid": "d19b826169ff464f82eba4e0b349bacc4361c8a0", "collapsed": true, "_cell_guid": "d54b91c2-fc78-4b24-a7c8-5b1a974cdf36", - "trusted": false + "trusted": true }, "cell_type": "code", "source": "np.random.seed(42)\nX_train, X_test, y_train, y_test = train_test_split(X, y)", - "execution_count": null, + "execution_count": 7, "outputs": [] }, { @@ -145,11 +187,11 @@ "_uuid": "86ecda4d1700405da9ed0d8bc6f7d8eabc0f1ddb", "collapsed": true, "_cell_guid": "fb45b18e-9357-4a98-9d98-078f96c3a591", - "trusted": false + "trusted": true }, "cell_type": "code", "source": "scaler = StandardScaler()\nlr = LogisticRegression()\nmodel1 = Pipeline([('standardize', scaler),\n ('log_reg', lr)])", - "execution_count": null, + "execution_count": 8, "outputs": [] }, { @@ -164,13 +206,21 @@ "metadata": { "_uuid": "bc769713962566efac8b2310f45d7ebe79dcb173", "_cell_guid": "b8f46ca5-f69a-4755-abbf-9cc76723c133", - "trusted": false, - "collapsed": true + "trusted": true }, "cell_type": "code", "source": "model1.fit(X_train, y_train)", - "execution_count": null, - "outputs": [] + "execution_count": 9, + "outputs": [ + { + "output_type": "execute_result", + "execution_count": 9, + "data": { + "text/plain": "Pipeline(memory=None,\n steps=[('standardize', StandardScaler(copy=True, with_mean=True, with_std=True)), ('log_reg', LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,\n intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,\n penalty='l2', random_state=None, solver='liblinear', tol=0.0001,\n verbose=0, warm_start=False))])" + }, + "metadata": {} + } + ] }, { "metadata": { @@ -184,13 +234,18 @@ "metadata": { "_uuid": "e4f22259d9018f233874d9e4afdb42ec882bd967", "_cell_guid": "f708ecc2-9114-4b5e-86fe-bd8423aa622c", - "trusted": false, - "collapsed": true + "trusted": true }, "cell_type": "code", "source": "y_train_hat = model1.predict(X_train)\ny_train_hat_probs = model1.predict_proba(X_train)[:,1]\ntrain_accuracy = accuracy_score(y_train, y_train_hat)*100\ntrain_auc_roc = roc_auc_score(y_train, y_train_hat_probs)*100\nprint('Confusion matrix:\\n', confusion_matrix(y_train, y_train_hat))\nprint('Training accuracy: %.4f %%' % train_accuracy)\nprint('Training AUC: %.4f %%' % train_auc_roc)", - "execution_count": null, - "outputs": [] + "execution_count": 10, + "outputs": [ + { + "output_type": "stream", + "text": "Confusion matrix:\n [[213200 26]\n [ 137 242]]\nTraining accuracy: 99.9237 %\nTraining AUC: 98.0664 %\n", + "name": "stdout" + } + ] }, { "metadata": { @@ -204,13 +259,18 @@ "metadata": { "_uuid": "a83a2229893813ad2986910d20f562b369b76eb4", "_cell_guid": "ca356e72-46ac-4766-8640-11d644c4534f", - "trusted": false, - "collapsed": true + "trusted": true }, "cell_type": "code", "source": "y_test_hat = model1.predict(X_test)\ny_test_hat_probs = model1.predict_proba(X_test)[:,1]\ntest_accuracy = accuracy_score(y_test, y_test_hat)*100\ntest_auc_roc = roc_auc_score(y_test, y_test_hat_probs)*100\nprint('Confusion matrix:\\n', confusion_matrix(y_test, y_test_hat))\nprint('Training accuracy: %.4f %%' % test_accuracy)\nprint('Training AUC: %.4f %%' % test_auc_roc)", - "execution_count": null, - "outputs": [] + "execution_count": 11, + "outputs": [ + { + "output_type": "stream", + "text": "Confusion matrix:\n [[71077 12]\n [ 45 68]]\nTraining accuracy: 99.9199 %\nTraining AUC: 97.4810 %\n", + "name": "stdout" + } + ] }, { "metadata": { @@ -224,13 +284,18 @@ "metadata": { "_uuid": "258dda7a672ec39dd33f095dbc59cc42832f50b0", "_cell_guid": "26394d30-80ec-4db0-929c-0a81585d4d66", - "trusted": false, - "collapsed": true + "trusted": true }, "cell_type": "code", "source": "print(classification_report(y_test, y_test_hat, digits=6))", - "execution_count": null, - "outputs": [] + "execution_count": 12, + "outputs": [ + { + "output_type": "stream", + "text": " precision recall f1-score support\n\n 0 0.999367 0.999831 0.999599 71089\n 1 0.850000 0.601770 0.704663 113\n\navg / total 0.999130 0.999199 0.999131 71202\n\n", + "name": "stdout" + } + ] }, { "metadata": { @@ -244,13 +309,21 @@ "metadata": { "_uuid": "9317259c2c241aa6cb00346b5b91bd485d7ec448", "_cell_guid": "d5db8aad-8866-4639-9a1c-cd32e33e11a1", - "trusted": false, - "collapsed": true + "trusted": true }, "cell_type": "code", "source": "fpr, tpr, thresholds = roc_curve(y_test, y_test_hat_probs, drop_intermediate=True)\n\nf, ax = plt.subplots(figsize=(9, 6))\n_ = plt.plot(fpr, tpr, [0,1], [0, 1])\n_ = plt.title('AUC ROC')\n_ = plt.xlabel('False positive rate')\n_ = plt.ylabel('True positive rate')\nplt.style.use('seaborn')\n\nplt.savefig('auc_roc.png', dpi=600)", - "execution_count": null, - "outputs": [] + "execution_count": 13, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": "", + "image/png": "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\n" + }, + "metadata": {} + } + ] }, { "metadata": { @@ -265,13 +338,18 @@ "metadata": { "_uuid": "dc371656eeeced96d8343a37312d92bf116eede0", "_cell_guid": "a5a4d2a5-c86d-4e27-8245-08325644794d", - "trusted": false, - "collapsed": true + "trusted": true }, "cell_type": "code", "source": "y_hat_90 = (y_test_hat_probs > 0.90 )*1\nprint('Confusion matrix:\\n', confusion_matrix(y_test, y_hat_90))\nprint(classification_report(y_test, y_hat_90, digits=6))\n", - "execution_count": null, - "outputs": [] + "execution_count": 14, + "outputs": [ + { + "output_type": "stream", + "text": "Confusion matrix:\n [[71077 12]\n [ 62 51]]\n precision recall f1-score support\n\n 0 0.999128 0.999831 0.999480 71089\n 1 0.809524 0.451327 0.579545 113\n\navg / total 0.998828 0.998961 0.998813 71202\n\n", + "name": "stdout" + } + ] }, { "metadata": { @@ -285,13 +363,18 @@ "metadata": { "_uuid": "69fbb96b4062e67e8df8beddab7cd48598b6e377", "_cell_guid": "df14952e-6d10-4964-b977-fd732a878347", - "trusted": false, - "collapsed": true + "trusted": true }, "cell_type": "code", "source": "y_hat_10 = (y_test_hat_probs > 0.10)*1\nprint('Confusion matrix:\\n', confusion_matrix(y_test, y_hat_10))\nprint(classification_report(y_test, y_hat_10, digits=4))", - "execution_count": null, - "outputs": [] + "execution_count": 15, + "outputs": [ + { + "output_type": "stream", + "text": "Confusion matrix:\n [[71064 25]\n [ 25 88]]\n precision recall f1-score support\n\n 0 0.9996 0.9996 0.9996 71089\n 1 0.7788 0.7788 0.7788 113\n\navg / total 0.9993 0.9993 0.9993 71202\n\n", + "name": "stdout" + } + ] }, { "metadata": { @@ -300,6 +383,28 @@ }, "cell_type": "markdown", "source": "Where to go from here?\n-------------------\n\nWe just scratched the surface of sklearn and logistic regression. For example we could spent much more time with the \n\n- feature selection / engineering (which is a bit hard without any background information about the features),\n- we could try techniques to counter the data inbalance and \n- we could use cross-validation to fine tune the hyperparameters (regulaziation constant C) or\n- try a different regulization (Lasso/Elastic Net) or \n- optimizer (stochastic gradient desent instead of coordinate descnet)\n- adjust class weights to move the decision boundary (make missed frauds more expansive in the loss function)\n!- and finally we could try different classifer models in sklearn like decision trees, random forrests, knn, naive bayes or support vector machines. \n\nBut for now we will stop here and will implement a logisitc regression model with stochastic gradient descent in TensorFlow and then extend it to a neural net.\n\n" + }, + { + "metadata": { + "trusted": true, + "collapsed": true, + "_uuid": "27797e77d2e868b65f511b2fb0319d07df471155" + }, + "cell_type": "code", + "source": "", + "execution_count": null, + "outputs": [] + }, + { + "metadata": { + "trusted": true, + "collapsed": true, + "_uuid": "eeebb0cae9ddebb8fafab51d3c0a45e13736c6a8" + }, + "cell_type": "code", + "source": "", + "execution_count": null, + "outputs": [] } ], "metadata": { From 0e2bd635379b0480c16817bc072374ba8b6b1407 Mon Sep 17 00:00:00 2001 From: Matthias Groncki Date: Sun, 20 May 2018 21:36:52 +0700 Subject: [PATCH 3/8] Pricing Exotics with TensorFlow Added an Example how to use TensorFlow to price single Barriers (Down-Out) and Bermudan Calls. Use the automatic differentiation to get analytical path derivates. Performance is not so good, but quite easy to implement. Maybe useful for validation tasks. --- BarriersTensorFlow.ipynb | 517 +++++++++++++++++++++++++++++++++++++++ BermudanTensorFlow.ipynb | 224 +++++++++++++++++ 2 files changed, 741 insertions(+) create mode 100644 BarriersTensorFlow.ipynb create mode 100644 BermudanTensorFlow.ipynb diff --git a/BarriersTensorFlow.ipynb b/BarriersTensorFlow.ipynb new file mode 100644 index 0000000..38a3f2d --- /dev/null +++ b/BarriersTensorFlow.ipynb @@ -0,0 +1,517 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Pricing Exotic Options with TensorFlow\n", + "\n", + "When i was writing my post about gradient descent and automatic differentiation in TensorFLow I had the idea to use TensorFlow for Monte-Carlo pricing of some path dependent derivates.\n", + "\n", + "TensorFlow supports GPU computing (unfortunatly I can not try this on my laptop) which can speed and with TensorFlows automatic differentiation we can get analytical 'path' derivates. Calculating path derivates with a bump and revaluation is usally very computational costly and can be numerical unstable, depending on the bump size.\n", + "\n", + "In this post we want to focus on the implementation in TensorFlow therefore we will use a Black-Scholes model simplicity and try to price a Plain-Vanilla, a Down-And-Out Barrier and a Bermudan Call Option.\n", + "\n", + "Lets start with the plain vanilla one." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/anaconda/lib/python3.6/site-packages/h5py/__init__.py:34: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.\n", + " from ._conv import register_converters as _register_converters\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "import tensorflow as tf\n", + "import scipy.stats as stats\n", + "from sklearn.linear_model import LinearRegression\n", + "from sklearn.metrics import mean_squared_error\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 117, + "metadata": {}, + "outputs": [], + "source": [ + "## Plain Vanilla Call in TensorFlow\n", + "\n", + "def blackScholes_py(S_0, strike, time_to_expiry, implied_vol, riskfree_rate):\n", + " S = S_0\n", + " K = strike\n", + " dt = time_to_expiry\n", + " sigma = implied_vol\n", + " r = riskfree_rate\n", + " Phi = stats.norm.cdf\n", + " d_1 = (np.log(S_0 / K) + (r+sigma**2/2)*dt) / (sigma*np.sqrt(dt))\n", + " d_2 = d_1 - sigma*np.sqrt(dt)\n", + " return S*Phi(d_1) - K*np.exp(-r*dt)*Phi(d_2)\n", + "\n", + "\n", + "def blackScholes_tf_pricer():\n", + " # Build the static computational graph\n", + " S = tf.placeholder(tf.float32)\n", + " K = tf.placeholder(tf.float32)\n", + " dt = tf.placeholder(tf.float32)\n", + " sigma = tf.placeholder(tf.float32)\n", + " r = tf.placeholder(tf.float32)\n", + " Phi = tf.distributions.Normal(0.,1.).cdf\n", + " d_1 = (tf.log(S / K) + (r+sigma**2/2)*dt) / (sigma*tf.sqrt(dt))\n", + " d_2 = d_1 - sigma*tf.sqrt(dt)\n", + " npv = S*Phi(d_1) - K*tf.exp(-r*dt)*Phi(d_2)\n", + " greeks = tf.gradients(npv, [S, sigma, r, K, dt])\n", + " # Calculate mixed 2nd order greeks for S (esp. gamma, vanna) and sigma (esp. volga)\n", + " dS_2ndOrder = tf.gradients(greeks[0], [S, sigma, r, K, dt]) \n", + " dsigma_2ndOrder = tf.gradients(greeks[1], [S, sigma, r, K, dt]) \n", + " # Function to feed in the input and calculate the computational graph\n", + " def execute_graph(S_0, strike, time_to_expiry, implied_vol, riskfree_rate):\n", + " with tf.Session() as sess:\n", + " res = sess.run([npv, greeks, dS_2ndOrder, dsigma_2ndOrder], \n", + " {\n", + " S: S_0,\n", + " K : strike,\n", + " r : riskfree_rate,\n", + " sigma: implied_vol,\n", + " dt: time_to_expiry})\n", + " return res\n", + " return execute_graph\n", + " \n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 118, + "metadata": {}, + "outputs": [], + "source": [ + "tf_pricer = blackScholes_tf_pricer()" + ] + }, + { + "cell_type": "code", + "execution_count": 139, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "6.934486895210796" + ] + }, + "execution_count": 139, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blackScholes_py(100., 110., 2., 0.2, 0.03) - (100/90)**(1-0.03/0.2**2)*blackScholes_py(90**2/100., 110., 2., 0.2, 0.03)" + ] + }, + { + "cell_type": "code", + "execution_count": 120, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2.15 s Β± 21.5 ms per loop (mean Β± std. dev. of 7 runs, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit\n", + "npv = tf_pricer(100., 110., 2., 0.2, 0.03)\n", + "npv" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Monte Carlo with TensorFlow\n", + "\n", + "Lets try a path dependent exotic option, a down and out call. A down-and-out Call behaves as a normal Call but if the price of the underlying touch or fall below a certain level (the barrier B) at any time until the expiry the option, then it becomes worthless, even if its at expiry in the money.\n", + "\n", + "A down and out call is cheaper than the plain vanilla case, since there is a risk that the option get knocked out before reaching the expiry. It can be used to reduce the hedging costs.\n", + "\n", + "In the Black-Scholes model there is again a closed formula to calculate the price. See ... .\n", + "\n", + "We want to price this kind of option in TensorFlow with a Monte-Carlo Simulation and let TensorFLow calculate the path derivates with automatic differentitation.\n", + "\n", + "First implement the analytical solutions in 'pure' Python (actually we rely heavly on numpy) and TensorFLow." + ] + }, + { + "cell_type": "code", + "execution_count": 167, + "metadata": {}, + "outputs": [], + "source": [ + "def analytical_downOut_py(S_0, strike, time_to_expiry, implied_vol, riskfree_rate, barrier):\n", + " S = S_0\n", + " K = strike\n", + " dt = time_to_expiry\n", + " sigma = implied_vol\n", + " r = riskfree_rate\n", + " alpha = 0.5 - r/sigma**2\n", + " B = barrier\n", + " Phi = stats.norm.cdf\n", + " d_1 = (np.log(S_0 / K) + (r+sigma**2/2)*dt) / (sigma*np.sqrt(dt))\n", + " d_2 = d_1 - sigma*np.sqrt(dt)\n", + " bs = S*Phi(d_1) - K*np.exp(-r*dt)*Phi(d_2)\n", + " d_1a = (np.log(B**2 / (S*K)) + (r+sigma**2/2)*dt) / (sigma*np.sqrt(dt))\n", + " d_2a = d_1a - sigma*np.sqrt(dt)\n", + " reflection = (S/B)**(1-r/sigma**2) * ((B**2/S)*Phi(d_1a) - K*np.exp(-r*dt)*Phi(d_2a))\n", + " return bs - reflection\n", + "\n", + "\n", + "def analytical_downOut_tf_pricer(enable_greeks = True):\n", + " S = tf.placeholder(tf.float32)\n", + " K = tf.placeholder(tf.float32)\n", + " dt = tf.placeholder(tf.float32)\n", + " sigma = tf.placeholder(tf.float32)\n", + " r = tf.placeholder(tf.float32)\n", + " B = tf.placeholder(tf.float32)\n", + " Phi = tf.distributions.Normal(0.,1.).cdf\n", + " d_1 = (tf.log(S / K) + (r+sigma**2/2)*dt) / (sigma*tf.sqrt(dt))\n", + " d_2 = d_1 - sigma*tf.sqrt(dt)\n", + " bs_npv = S*Phi(d_1) - K*tf.exp(-r*dt)*Phi(d_2)\n", + " d_1a = (tf.log(B**2 / (S*K)) + (r+sigma**2/2)*dt) / (sigma*tf.sqrt(dt))\n", + " d_2a = d_1a - sigma*tf.sqrt(dt)\n", + " reflection = (S/B)**(1-r/sigma**2) * ((B**2/S)*Phi(d_1a) - K*tf.exp(-r*dt)*Phi(d_2a))\n", + " npv = bs_npv - reflection\n", + " target_calc = [npv]\n", + " if enable_greeks:\n", + " greeks = tf.gradients(npv, [S, sigma, r, K, dt, B])\n", + " # Calculate mixed 2nd order greeks for S (esp. gamma, vanna) and sigma (esp. volga)\n", + " dS_2ndOrder = tf.gradients(greeks[0], [S, sigma, r, K, dt, B]) \n", + " dsigma_2ndOrder = tf.gradients(greeks[1], [S, sigma, r, K, dt, B]) \n", + " # Function to feed in the input and calculate the computational graph\n", + " target_calc += [greeks, dS_2ndOrder, dsigma_2ndOrder]\n", + " def price(S_0, strike, time_to_expiry, implied_vol, riskfree_rate, barrier):\n", + " with tf.Session() as sess:\n", + " \n", + " res = sess.run(target_calc, \n", + " {\n", + " S: S_0,\n", + " K : strike,\n", + " r : riskfree_rate,\n", + " sigma: implied_vol,\n", + " dt: time_to_expiry,\n", + " B : barrier})\n", + " return res\n", + " return price" + ] + }, + { + "cell_type": "code", + "execution_count": 501, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "6.934486895210796" + ] + }, + "execution_count": 501, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "analytical_downOut_py(100., 110., 2., 0.2, 0.03, 90)" + ] + }, + { + "cell_type": "code", + "execution_count": 502, + "metadata": {}, + "outputs": [], + "source": [ + "down_out_pricer = analytical_downOut_tf_pricer(True)" + ] + }, + { + "cell_type": "code", + "execution_count": 503, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[6.9344816,\n", + " [0.69351405, 18.208786, 56.060673, -0.22123335, 1.7513491, -0.423125],\n", + " [0.002250306, 1.6649915, 4.7459536, -0.020622324, 0.15443888, 0.02270472],\n", + " [1.6649914, -159.15932, -191.44804, 0.9917286, -6.277489, -2.859783]]" + ] + }, + "execution_count": 503, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "down_out_pricer(100., 110., 2., 0.2, 0.03, 90.)" + ] + }, + { + "cell_type": "code", + "execution_count": 321, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2.23 s Β± 95.9 ms per loop (mean Β± std. dev. of 7 runs, 1 loop each)\n" + ] + } + ], + "source": [ + "down_out_pricer = analytical_downOut_tf_pricer(False)\n", + "%timeit down_out_pricer(100., 110., 2., 0.2, 0.03, 95.)" + ] + }, + { + "cell_type": "code", + "execution_count": 173, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3.85 s Β± 54.6 ms per loop (mean Β± std. dev. of 7 runs, 1 loop each)\n" + ] + } + ], + "source": [ + "down_out_pricer = analytical_downOut_tf_pricer(True)\n", + "%timeit down_out_pricer(100., 110., 2., 0.2, 0.03, 90.)" + ] + }, + { + "cell_type": "code", + "execution_count": 174, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "265 Β΅s Β± 11.2 Β΅s per loop (mean Β± std. dev. of 7 runs, 1000 loops each)\n" + ] + } + ], + "source": [ + "%timeit analytical_downOut_py(100., 110., 2., 0.2, 0.03, 95.)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now lets implement the Monte Carlo Pricing. We will pass the random varibales to the pricing function. \n", + "The pricing function we assume equvidistant timegrid." + ] + }, + { + "cell_type": "code", + "execution_count": 178, + "metadata": {}, + "outputs": [], + "source": [ + "def generate_random_variables_for_down_out(steps, samples, seed=42):\n", + " np.random.seed(seed)\n", + " return np.random.randn(samples, steps)" + ] + }, + { + "cell_type": "code", + "execution_count": 398, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(10000, 100)" + ] + }, + "execution_count": 398, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "N = generate_random_variables_for_down_out(100, 10000)\n", + "N.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 399, + "metadata": {}, + "outputs": [], + "source": [ + "def monte_carlo_down_out_py(S_0, strike, time_to_expiry, implied_vol, riskfree_rate, barrier, stdnorm_random_variates):\n", + " S = S_0\n", + " K = strike\n", + " dt = time_to_expiry / stdnorm_random_variates.shape[1]\n", + " sigma = implied_vol\n", + " r = riskfree_rate\n", + " B = barrier\n", + " # See Advanced Monte Carlo methods for barrier and related exotic options by Emmanuel Gobet\n", + " B_shift = B*np.exp(0.5826*sigma*np.sqrt(dt))\n", + " S_T = S * np.cumprod(np.exp((r-sigma**2/2)*dt+sigma*np.sqrt(dt)*stdnorm_random_variates), axis=1)\n", + " non_touch = (np.min(S_T, axis=1) > B_shift)*1\n", + " call_payout = np.maximum(S_T[:,-1] - K, 0)\n", + " npv = np.mean(non_touch * call_payout)\n", + " return np.exp(-time_to_expiry*r)*npv" + ] + }, + { + "cell_type": "code", + "execution_count": 526, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "11.5 ms Β± 1.55 ms per loop (mean Β± std. dev. of 7 runs, 100 loops each)\n" + ] + } + ], + "source": [ + "%%timeit\n", + "monte_carlo_down_out_py(100., 110., 2., 0.2, 0.03, 90., N)" + ] + }, + { + "cell_type": "code", + "execution_count": 521, + "metadata": {}, + "outputs": [], + "source": [ + "def monte_carlo_down_out_tf(enable_greeks = True):\n", + " S = tf.placeholder(tf.float32)\n", + " K = tf.placeholder(tf.float32)\n", + " dt = tf.placeholder(tf.float32)\n", + " T = tf.placeholder(tf.float32)\n", + " sigma = tf.placeholder(tf.float32)\n", + " r = tf.placeholder(tf.float32)\n", + " B = tf.placeholder(tf.float32)\n", + " dw = tf.placeholder(tf.float32)\n", + " # See Advanced Monte Carlo methods for barrier and related exotic options by Emmanuel Gobet\n", + " B_shift = B * tf.exp(0.5826*sigma*tf.sqrt(dt))\n", + " S_T = S * tf.cumprod(tf.exp((r-sigma**2/2)*dt+sigma*tf.sqrt(dt)*dw), axis=1)\n", + " non_touch = tf.cast(tf.reduce_all(S_T > B_shift, axis=1), tf.float32)\n", + " call_payout = tf.maximum(S_T[:,-1] - K, 0)\n", + " npv = tf.exp(-r*T) * tf.reduce_mean(non_touch * call_payout)\n", + " target_calc = [npv]\n", + " if enable_greeks:\n", + " greeks = tf.gradients(npv, [S, sigma, r, K, T])\n", + " target_calc += [greeks]\n", + " def pricer(S_0, strike, time_to_expiry, implied_vol, riskfree_rate, barrier, stdnorm_random_variates):\n", + " with tf.Session() as sess:\n", + " timedelta = time_to_expiry / stdnorm_random_variates.shape[1]\n", + " res = sess.run(target_calc, \n", + " {\n", + " S: S_0,\n", + " K : strike,\n", + " r : riskfree_rate,\n", + " sigma: implied_vol,\n", + " dt : timedelta,\n", + " T: time_to_expiry,\n", + " B : barrier,\n", + " dw : stdnorm_random_variates\n", + " })\n", + " return res\n", + " return pricer\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 522, + "metadata": {}, + "outputs": [], + "source": [ + "tf_mc_pricer = monte_carlo_down_out_tf()" + ] + }, + { + "cell_type": "code", + "execution_count": 525, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[7.035021, [0.31648976, 41.60718, 49.227932, -0.22376387, -0.21105061]]" + ] + }, + "execution_count": 525, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tf_mc_pricer(100., 110., 2., 0.2, 0.03, 90., N)" + ] + }, + { + "cell_type": "code", + "execution_count": 527, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3.08 s Β± 275 ms per loop (mean Β± std. dev. of 7 runs, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit\n", + "tf_mc_pricer(100., 110., 2., 0.2, 0.03, 90., N)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/BermudanTensorFlow.ipynb b/BermudanTensorFlow.ipynb new file mode 100644 index 0000000..8f31431 --- /dev/null +++ b/BermudanTensorFlow.ipynb @@ -0,0 +1,224 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Pricing Exotic Options with TensorFlow\n", + "\n", + "Bermudan Options " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/anaconda/lib/python3.6/site-packages/h5py/__init__.py:34: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.\n", + " from ._conv import register_converters as _register_converters\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "import tensorflow as tf\n", + "import scipy.stats as stats\n", + "from sklearn.linear_model import LinearRegression\n", + "from sklearn.metrics import mean_squared_error\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "def get_continuation_function():\n", + " X = tf.placeholder(tf.float32, (None, 3))\n", + " y = tf.placeholder(tf.float32, (None, 1))\n", + " w = tf.Variable(initial_value=tf.random_normal((3,1))*0.1)\n", + " b = tf.Variable(initial_value=tf.ones(1)*1)\n", + " y_hat = tf.add(tf.matmul(X,w), b)\n", + " pre_error = tf.pow(y-y_hat,2)\n", + " error = tf.reduce_mean(pre_error)\n", + " train = tf.train.AdamOptimizer(0.1).minimize(error)\n", + " return(X, y, train, w, b, y_hat)\n", + "\n", + "def feature_matrix_from_current_state(state):\n", + " feature_0 = tf.pow(state,0)\n", + "\n", + " feature_1 = tf.pow(state,1)\n", + " feature_1_mean = tf.reduce_mean(feature_1)\n", + " feature_1_std = tf.sqrt(tf.reduce_sum(tf.square(feature_1 - feature_1_mean))/(N_samples_pricing+1))\n", + " feature_1_norm = (feature_1 - feature_1_mean) / feature_1_std\n", + "\n", + "\n", + " feature_2 = 2*tf.pow(state,2)-1\n", + " feature_2_mean = tf.reduce_mean(feature_2)\n", + " feature_2_std = tf.sqrt(tf.reduce_sum(tf.square(feature_2 - feature_2_mean))/(N_samples_pricing+1))\n", + " feature_2_norm = (feature_2 - feature_2_mean) / feature_2_std\n", + "\n", + " feature_3 = 4*tf.pow(state,3)-3*feature_1\n", + " feature_3_mean = tf.reduce_mean(feature_3)\n", + " feature_3_std = tf.sqrt(tf.reduce_sum(tf.square(feature_3 - feature_3_mean))/(N_samples_pricing+1))\n", + " feature_3_norm = (feature_3 - feature_3_mean) / feature_3_std\n", + "\n", + " features = tf.concat([feature_1_norm, feature_2_norm, feature_3_norm], axis=0)\n", + " features = tf.reshape(features, shape=(3, N_samples_pricing))\n", + " features = tf.transpose(features)\n", + " return features\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "def pricing_function(number_call_dates):\n", + " S = tf.placeholder(tf.float32)\n", + " # First excerise date\n", + " dts = tf.placeholder(tf.float32)\n", + " # 2nd exersice date\n", + " K = tf.placeholder(tf.float32)\n", + " r = tf.placeholder(tf.float32)\n", + " sigma = tf.placeholder(tf.float32)\n", + " dW = tf.placeholder(tf.float32)\n", + " \n", + " S_t = S * tf.cumprod(tf.exp((r-sigma**2/2)*dts + sigma*tf.sqrt(dts)*dW), axis=1)\n", + " \n", + " E_t = tf.exp(-r*tf.cumsum(dts))*tf.maximum(S_t-K, 0)\n", + "\n", + " \n", + " continuationValues = []\n", + " training_functions = []\n", + " \n", + " previous_exersies = 0\n", + " npv = 0\n", + " for i in range(number_call_dates-1):\n", + " (input_x, input_y, train, w, b, y_hat) = get_continuation_function()\n", + " training_functions.append((input_x, input_y, train, w, b, y_hat))\n", + " X = feature_matrix_from_current_state(S_t[:, i])\n", + " contValue = tf.add(tf.matmul(X, w),b)\n", + " continuationValues.append(contValue)\n", + " inMoney = tf.cast(tf.greater(E_t[:,i], 0.), tf.float32)\n", + " exercise = tf.cast(tf.greater(E_t[:,i], contValue[:,0]), tf.float32) * inMoney * (1-previous_exersies)\n", + " previous_exersies += exercise\n", + " npv += exercise*E_t[:,i]\n", + " \n", + " # Last exercise date\n", + " inMoney = tf.cast(tf.greater(E_t[:,-1], 0.), tf.float32)\n", + " exercise = inMoney * (1-previous_exersies)\n", + " npv += exercise*E_t[:,-1]\n", + " npv = tf.reduce_mean(npv)\n", + " greeks = tf.gradients(npv, [S, r, sigma])\n", + " return([S, dts, K, r, sigma, dW, S_t, E_t, npv, greeks, training_functions])" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "def bermudanMC_tensorFlow(S_0, strike, exTimes, impliedvol, riskfree_r, random_train, random_pricing):\n", + " n_excerises = len(exTimes)\n", + " with tf.Session() as sess:\n", + " \n", + " S, dts, K, r, sigma, dW, S_t, E_t, npv, greeks, training_functions= pricing_function(n_excerises)\n", + " sess.run(tf.global_variables_initializer())\n", + " paths, exercise_values = sess.run([S_t, E_t], {\n", + " S: S_0,\n", + " dts : exTimes,\n", + " K : strike,\n", + " r : riskfree_r,\n", + " sigma: impliedvol,\n", + " dW : random_train\n", + " }) \n", + " \n", + " for i in range(n_excerises-1)[::-1]:\n", + " (input_x, input_y, train, w, b, y_hat) = training_functions[i]\n", + " y = exercise_values[:, i+1:i+2]\n", + " X = paths[:, i]\n", + " X = np.c_[X**1, 2*X**2-1, 4*X**3 - 3 * X]\n", + " X = (X - np.mean(X, axis=0)) / np.std(X, axis=0)\n", + " for epoch in range(80):\n", + " _ = sess.run(train, {input_x:X[exercise_values[:,i]>0], \n", + " input_y:y[exercise_values[:,i]>0]})\n", + " cont_value = sess.run(y_hat, {input_x:X, \n", + " input_y:y})\n", + " exercise_values[:, i+1:i+2] = np.maximum(exercise_values[:, i+1:i+2], cont_value)\n", + " \n", + " npv, greeks = sess.run([npv, greeks], { S: S_0,\n", + " dts : exTimes,\n", + " K : strike,\n", + " r : riskfree_r,\n", + " sigma: impliedvol,\n", + " dW : N_pricing\n", + " })\n", + " return(npv, greeks)\n", + " \n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "N_samples_learn = 10000\n", + "N_samples_pricing = 100000\n", + "calldates = 2\n", + "np.random.seed(42)\n", + "N = np.random.randn(N_samples_learn,calldates)\n", + "N_pricing = np.random.randn(N_samples_pricing,calldates)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(9.751283, [0.5057875, 81.654884, 56.51146])" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bermudanMC_tensorFlow(100., 110., [1., 1.], 0.2, 0.03, N, N_pricing)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 9b8d724d669bc1bb369d34d611b4e6607e104c69 Mon Sep 17 00:00:00 2001 From: Matthias Groncki Date: Sun, 18 Nov 2018 17:43:05 +0700 Subject: [PATCH 4/8] Monte carlo pricing n adjoint greeks on gpus --- .DS_Store | Bin 0 -> 8196 bytes .gitignore | 4 +- BarriersTensorFlow.ipynb | 2 +- LICENSE | 29 + PricingPyTorch/.DS_Store | Bin 0 -> 6148 bytes PricingPyTorch/BarrierOptionGPU.ipynb | 185 +++++++ .../PlainVanillaAndBarriersCPU.ipynb | 510 ++++++++++++++++++ 7 files changed, 728 insertions(+), 2 deletions(-) create mode 100644 .DS_Store create mode 100644 LICENSE create mode 100644 PricingPyTorch/.DS_Store create mode 100644 PricingPyTorch/BarrierOptionGPU.ipynb create mode 100644 PricingPyTorch/PlainVanillaAndBarriersCPU.ipynb diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..8591f3d9657cc4f73e6984a12b0d9cb4eaaf1e9f GIT binary patch literal 8196 zcmeHM-D(p-6h4zy-B5pu3avM?UI-Q1khJ0tBE&RN0}8>EdZCgg*^n-5cEj$*w8lto zd&3Xi`7bf`xbkX`vb;OKat6L@FgpA8fbh;?|A zp*^$#?M?b2mJ)gwpvS(qsYXSrqRma;g?OLPUZPFfrU%e!N~=xm#z8NHFCM(9QF#X1 z_XOGkJZRxxmmZ_F;1lntJ-LHaJ)vb%3485AqY1mLr+$0beGd$`uq#{g9QSPMNu|Cf z@Gs$XZQukzuK{fj`|H4-gZ@+$dEOsl=Y0)KxIpm1iv_>f*8`3!7_~7XcMBVjfXtD> zQ8C>DM?l=dReB=N(UzVv-ZS8|fz-lC5VOW{9L_vvj*>X`3O$wQ$^34@FJH#Yq~B}c zxsSQ-cxP)=k!zW^^2|u!xQg4m@mK8qF81OU%|Eg@^>k#E#_`XCi})#5@RZmBr#{YU zHxc0+fk)PD^n-5nocYq|I(86dv)@E&ZvNEiGlpR-80U;v)m^V&4eMbqDAfHI{HszA zcN$cnSrLt|cu;k@)+qJxY!RxtJAaSZ{BAref>DuCz z!Qjru?NxJqV|}=44szL>xmEL4_U>?)HkPjE?w5Bu`)>wshwnaOhbSWYLxnD=-{bZZ zdNUb&UG-hZbK4ApnntXb@Ej`Q6ic`>9kM1l<@+d%*nKn+ts(da`|@PCr|wAx`uwct z8`DIaL3Pl#Y6r9x*0h?68a!ad#was~nh?Rn9t!FF_j$DXZ*xgU`AL-1#RdRY@A&AI( zusd(x*X|A6egFvN)AA6Q02s3ggOmZ0dC;|!gM}g0sBw=C&auYhK%u{A(%Ppeam#gY zu)Y2oEmkd@!K>A+iaJjGbZHI@sy@ zEdNL-p{{GOFlH<=c82s&f~68IHRLBouyopEjw>>DhL( B_shift)*1\n", + " call_payout = np.maximum(S_T[:,-1] - K, 0)\n", + " npv = np.mean(non_touch * call_payout)\n", + " return np.exp(-time_to_expiry*r)*npv" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "8.24 s Β± 22.5 ms per loop (mean Β± std. dev. of 7 runs, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit\n", + "monte_carlo_down_out_py(100., 110., 2., 0.2, 0.03, 90., 1000, 100000)" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": {}, + "outputs": [], + "source": [ + "def monte_carlo_down_out_torch_cuda(S_0, strike, time_to_expiry, implied_vol, riskfree_rate, barrier, steps, samples):\n", + " stdnorm_random_variates = torch.cuda.FloatTensor(steps, samples).normal_()\n", + " S = S_0\n", + " K = strike\n", + " dt = time_to_expiry / stdnorm_random_variates.shape[1]\n", + " sigma = implied_vol\n", + " r = riskfree_rate\n", + " B = barrier\n", + " # See Advanced Monte Carlo methods for barrier and related exotic options by Emmanuel Gobet\n", + " B_shift = B*torch.exp(0.5826*sigma*torch.sqrt(dt))\n", + " S_T = S * torch.cumprod(torch.exp((r-sigma**2/2)*dt+sigma*torch.sqrt(dt)*stdnorm_random_variates), dim=1)\n", + " non_touch = torch.min(S_T, dim=1)[0] > B_shift\n", + " non_touch = non_touch.type(torch.cuda.FloatTensor)\n", + " call_payout = S_T[:,-1] - K\n", + " call_payout[call_payout<0]=0\n", + " npv = torch.mean(non_touch * call_payout)\n", + " return torch.exp(-time_to_expiry*r)*npv" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "56.3 ms Β± 89.1 Β΅s per loop (mean Β± std. dev. of 7 runs, 10 loops each)\n" + ] + } + ], + "source": [ + "%%timeit\n", + "S = torch.tensor([100.],requires_grad=True, device='cuda')\n", + "K = torch.tensor([110.],requires_grad=True, device='cuda')\n", + "T = torch.tensor([2.],requires_grad=True, device='cuda')\n", + "sigma = torch.tensor([0.2],requires_grad=True, device='cuda')\n", + "r = torch.tensor([0.03],requires_grad=True, device='cuda')\n", + "B = torch.tensor([90.],requires_grad=True, device='cuda')\n", + "monte_carlo_down_out_torch_cuda(S, K, T, sigma, r, B, 1000, 100000)" + ] + }, + { + "cell_type": "code", + "execution_count": 115, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "160 ms Β± 94 Β΅s per loop (mean Β± std. dev. of 7 runs, 10 loops each)\n" + ] + } + ], + "source": [ + "%%timeit\n", + "S = torch.tensor([100.],requires_grad=True, device='cuda')\n", + "K = torch.tensor([110.],requires_grad=True, device='cuda')\n", + "T = torch.tensor([2.],requires_grad=True, device='cuda')\n", + "sigma = torch.tensor([0.2],requires_grad=True, device='cuda')\n", + "r = torch.tensor([0.03],requires_grad=True, device='cuda')\n", + "B = torch.tensor([90.],requires_grad=True, device='cuda')\n", + "npv_torch_mc = monte_carlo_down_out_torch_cuda(S, K, T, sigma, r, B, 1000, 100000)\n", + "npv_torch_mc.backward()" + ] + }, + { + "cell_type": "code", + "execution_count": 113, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [default]", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/PricingPyTorch/PlainVanillaAndBarriersCPU.ipynb b/PricingPyTorch/PlainVanillaAndBarriersCPU.ipynb new file mode 100644 index 0000000..6eca960 --- /dev/null +++ b/PricingPyTorch/PlainVanillaAndBarriersCPU.ipynb @@ -0,0 +1,510 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Option Pricing with with PyTorch \n", + "\n", + "Copyright Matthias Groncki, 2018\n", + "\n", + "This is a port of one of my previous blog posts about using TensorFlow to price options.\n", + "\n", + "After using PyTorch for another project, i was impressed how straight forward it is, so I've decided to revisit my previous examples and use PyTorch this time" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/anaconda/lib/python3.6/site-packages/h5py/__init__.py:34: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.\n", + " from ._conv import register_converters as _register_converters\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "import torch\n", + "import tensorflow as tf\n", + "import scipy.stats as stats" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Plain Vanillas\n", + "\n", + "Lets start with plain vanillas in a Black Scholes World. \n", + "\n", + "### Numpy Implementation\n", + "\n", + "I am using the same implementation as in the TensorFlow notebook:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "## Plain Vanilla Call in TensorFlow\n", + "\n", + "def blackScholes_py(S_0, strike, time_to_expiry, implied_vol, riskfree_rate):\n", + " S = S_0\n", + " K = strike\n", + " dt = time_to_expiry\n", + " sigma = implied_vol\n", + " r = riskfree_rate\n", + " Phi = stats.norm.cdf\n", + " d_1 = (np.log(S_0 / K) + (r+sigma**2/2)*dt) / (sigma*np.sqrt(dt))\n", + " d_2 = d_1 - sigma*np.sqrt(dt)\n", + " return S*Phi(d_1) - K*np.exp(-r*dt)*Phi(d_2)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "281 Β΅s Β± 25.4 Β΅s per loop (mean Β± std. dev. of 7 runs, 1000 loops each)\n" + ] + } + ], + "source": [ + "%%timeit\n", + "S_0 = 100\n", + "K = 101\n", + "T = 1\n", + "sigma = 0.3\n", + "r = 0.01\n", + "\n", + "npv_numpy = blackScholes_py(S_0, K, T, sigma, r)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And as expected its super fast. No suprises here" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### PyTorch Implementation\n", + "\n", + "There are only minimal code changes compared to the numpy version required. In the actual pricing function we just need to replace ```np``` with ```torch``` and exchange the cdf function to use the PyTorch one and we have to convert our input into ```torch.tensor```." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "def blackScholes_pyTorch(S_0, strike, time_to_expiry, implied_vol, riskfree_rate):\n", + " S = S_0\n", + " K = strike\n", + " dt = time_to_expiry\n", + " sigma = implied_vol\n", + " r = riskfree_rate\n", + " Phi = torch.distributions.Normal(0,1).cdf\n", + " d_1 = (torch.log(S_0 / K) + (r+sigma**2/2)*dt) / (sigma*torch.sqrt(dt))\n", + " d_2 = d_1 - sigma*torch.sqrt(dt)\n", + " return S*Phi(d_1) - K*torch.exp(-r*dt)*Phi(d_2)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "283 Β΅s Β± 23.7 Β΅s per loop (mean Β± std. dev. of 7 runs, 1000 loops each)\n" + ] + } + ], + "source": [ + "%%timeit\n", + "S_0 = torch.tensor([100.],requires_grad=True)\n", + "K = torch.tensor([101.],requires_grad=True)\n", + "T = torch.tensor([1.],requires_grad=True)\n", + "sigma = torch.tensor([0.3],requires_grad=True)\n", + "r = torch.tensor([0.01],requires_grad=True)\n", + "npv_pytorch = blackScholes_pyTorch(S_0, K, T, sigma, r)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Seems the PyTorch version is even faster as the pure numpy version\n", + "\n", + "#### Greeks in PyTorch\n", + "\n", + "We just need to call the ```.backward()``` operator of the tensor which stores the prices and we can access the greeks with the ```.grad``` properity." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "639 Β΅s Β± 9.85 Β΅s per loop (mean Β± std. dev. of 7 runs, 1000 loops each)\n" + ] + } + ], + "source": [ + "%%timeit\n", + "S_0 = torch.tensor([100.],requires_grad=True)\n", + "K = torch.tensor([101.],requires_grad=True)\n", + "T = torch.tensor([1.],requires_grad=True)\n", + "sigma = torch.tensor([0.3],requires_grad=True)\n", + "r = torch.tensor([0.01],requires_grad=True)\n", + "npv_pytorch = blackScholes_pyTorch(S_0, K, T, sigma, r)\n", + "npv_pytorch.backward()\n", + "delta_pytorch = S_0.grad\n", + "vega_pytorch = sigma.grad\n", + "rho_pytorch = r.grad\n", + "theta_pytorch = T.grad\n", + "digital_pytoch = -K.grad" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Its almost 2.5 times slower but gives us five greeks. A naive finite-difference approximation would costs us at least 5 calculations and would be only an numerical approximation. Here we have 'exact' derivates." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### TensorFlow implementation\n", + "\n", + "Using the same code as in the original notebook (but I removed the calculation of the 2nd order greeks. There is a bit of overhead for constructing the computational graph." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "def blackScholes_tf_pricer():\n", + " # Build the static computational graph\n", + " S = tf.placeholder(tf.float32)\n", + " K = tf.placeholder(tf.float32)\n", + " dt = tf.placeholder(tf.float32)\n", + " sigma = tf.placeholder(tf.float32)\n", + " r = tf.placeholder(tf.float32)\n", + " Phi = tf.distributions.Normal(0.,1.).cdf\n", + " d_1 = (tf.log(S / K) + (r+sigma**2/2)*dt) / (sigma*tf.sqrt(dt))\n", + " d_2 = d_1 - sigma*tf.sqrt(dt)\n", + " npv = S*Phi(d_1) - K*tf.exp(-r*dt)*Phi(d_2)\n", + " greeks = tf.gradients(npv, [S, sigma, r, K, dt])\n", + " def execute_graph(S_0, strike, time_to_expiry, implied_vol, riskfree_rate):\n", + " with tf.Session() as sess:\n", + " res = sess.run([npv, greeks], \n", + " {\n", + " S: S_0,\n", + " K : strike,\n", + " r : riskfree_rate,\n", + " sigma: implied_vol,\n", + " dt: time_to_expiry})\n", + " return res\n", + " return execute_graph " + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "820 ms Β± 99.9 ms per loop (mean Β± std. dev. of 7 runs, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit\n", + "S_0 = 100\n", + "K = 101\n", + "T = 1\n", + "sigma = 0.3\n", + "r = 0.01\n", + "tf_pricer = blackScholes_tf_pricer()\n", + "npv_numpy_tf = tf_pricer(S_0, K, T, sigma, r)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Its much slower than the numpy and the PyTorch implementaion. Maybe my implementation is just bad." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Second order greeks in Pytorch\n", + "\n", + "We using the same example as before but now we want to calculate the 2nd order greeks. Thats need\n", + "to create a computational graph of the gradient. We use the function ```.grad()``` from the autograd module." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "S_0 = torch.tensor([100.],requires_grad=True)\n", + "K = torch.tensor([101.],requires_grad=True)\n", + "T = torch.tensor([1.],requires_grad=True)\n", + "sigma = torch.tensor([0.3],requires_grad=True)\n", + "r = torch.tensor([0.01],requires_grad=True)\n", + "npv_pytorch = blackScholes_pyTorch(S_0, K, T, sigma, r)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Gamma" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "gradient = torch.autograd.grad(npv_pytorch, S_0, create_graph=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([ 0.5597])" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "delta, = gradient\n", + "delta" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Delta: tensor([ 0.5597])\n", + "Gamma tensor(1.00000e-02 *\n", + " [ 2.6298])\n" + ] + } + ], + "source": [ + "delta.backward(retain_graph=True)\n", + "print('Delta: ', delta)\n", + "print('Gamma', S_0.grad)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Monte Carlo Pricing for Single Barrier Option\n", + "\n", + "### Numpy Implementation" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "def monte_carlo_down_out_py(S_0, strike, time_to_expiry, implied_vol, riskfree_rate, barrier, steps, samples):\n", + " stdnorm_random_variates = np.random.randn(samples, steps)\n", + " S = S_0\n", + " K = strike\n", + " dt = time_to_expiry / stdnorm_random_variates.shape[1]\n", + " sigma = implied_vol\n", + " r = riskfree_rate\n", + " B = barrier\n", + " # See Advanced Monte Carlo methods for barrier and related exotic options by Emmanuel Gobet\n", + " B_shift = B*np.exp(0.5826*sigma*np.sqrt(dt))\n", + " S_T = S * np.cumprod(np.exp((r-sigma**2/2)*dt+sigma*np.sqrt(dt)*stdnorm_random_variates), axis=1)\n", + " non_touch = (np.min(S_T, axis=1) > B_shift)*1\n", + " call_payout = np.maximum(S_T[:,-1] - K, 0)\n", + " npv = np.mean(non_touch * call_payout)\n", + " return np.exp(-time_to_expiry*r)*npv" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "4.78 s Β± 104 ms per loop (mean Β± std. dev. of 7 runs, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit\n", + "monte_carlo_down_out_py(100., 110., 2., 0.2, 0.03, 90., 1000, 100000)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### PyTorch Implementation" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "def monte_carlo_down_out_torch(S_0, strike, time_to_expiry, implied_vol, riskfree_rate, barrier, steps, samples):\n", + " stdnorm_random_variates = torch.distributions.Normal(0,1).sample((samples, steps))\n", + " S = S_0\n", + " K = strike\n", + " dt = time_to_expiry / stdnorm_random_variates.shape[1]\n", + " sigma = implied_vol\n", + " r = riskfree_rate\n", + " B = barrier\n", + " # See Advanced Monte Carlo methods for barrier and related exotic options by Emmanuel Gobet\n", + " B_shift = B*torch.exp(0.5826*sigma*torch.sqrt(dt))\n", + " S_T = S * torch.cumprod(torch.exp((r-sigma**2/2)*dt+sigma*torch.sqrt(dt)*stdnorm_random_variates), dim=1)\n", + " non_touch = torch.min(S_T, dim=1)[0] > B_shift\n", + " call_payout = S_T[:,-1] - K\n", + " call_payout[call_payout<0]=0\n", + " npv = torch.mean(non_touch.type(torch.FloatTensor) * call_payout)\n", + " return torch.exp(-time_to_expiry*r)*npv" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3.09 s Β± 106 ms per loop (mean Β± std. dev. of 7 runs, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit\n", + "S = torch.tensor([100.],requires_grad=True)\n", + "K = torch.tensor([110.],requires_grad=True)\n", + "T = torch.tensor([2.],requires_grad=True)\n", + "sigma = torch.tensor([0.2],requires_grad=True)\n", + "r = torch.tensor([0.03],requires_grad=True)\n", + "B = torch.tensor([90.],requires_grad=True)\n", + "monte_carlo_down_out_torch(S, K, T, sigma, r, B, 1000, 100000)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "7.34 s Β± 229 ms per loop (mean Β± std. dev. of 7 runs, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit\n", + "S = torch.tensor([100.],requires_grad=True)\n", + "K = torch.tensor([110.],requires_grad=True)\n", + "T = torch.tensor([2.],requires_grad=True)\n", + "sigma = torch.tensor([0.2],requires_grad=True)\n", + "r = torch.tensor([0.03],requires_grad=True)\n", + "B = torch.tensor([90.],requires_grad=True)\n", + "npv_torch_mc = monte_carlo_down_out_torch(S, K, T, sigma, r, B, 1000, 100000)\n", + "npv_torch_mc.backward()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [default]", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 598d34f3128409cb5e3e62bfc314c23db584c65e Mon Sep 17 00:00:00 2001 From: Matthias Groncki Date: Sat, 28 Feb 2026 16:08:22 +0100 Subject: [PATCH 5/8] Initial version of my first ai agent --- .gitignore | 5 + EconomicStressAgentORE/.env.example | 5 + EconomicStressAgentORE/.gitignore | 2 + EconomicStressAgentORE/README.md | 165 +++ EconomicStressAgentORE/agent.py | 195 ++++ EconomicStressAgentORE/config.py | 45 + EconomicStressAgentORE/data/scenarios.json | 899 +++++++++++++++ .../data/sector_mapping.csv | 4 + .../historical_scenarios.py | 96 ++ EconomicStressAgentORE/impact_summarizer.py | 220 ++++ EconomicStressAgentORE/ore_runner.py | 90 ++ .../oredata/Input/conventions.xml | 126 +++ .../oredata/Input/curveconfig.xml | 582 ++++++++++ .../oredata/Input/fixings.csv | 416 +++++++ .../oredata/Input/marketdata.csv | 859 ++++++++++++++ .../oredata/Input/parstresstest.xml | 791 +++++++++++++ .../oredata/Input/portfolio.xml | 379 +++++++ .../oredata/Input/pricingengine.xml | 1006 +++++++++++++++++ .../oredata/Input/sensitivity.xml | 158 +++ .../oredata/Input/simulation.xml | 79 ++ .../oredata/Input/todaysmarket.xml | 80 ++ EconomicStressAgentORE/oredata/ore.xml | 64 ++ EconomicStressAgentORE/requirements.txt | 5 + EconomicStressAgentORE/scenario_analyzer.py | 166 +++ EconomicStressAgentORE/stresstest_builder.py | 323 ++++++ EconomicStressAgentORE/test_integration.py | 98 ++ .../todaysmarket_analyzer.py | 436 +++++++ 27 files changed, 7294 insertions(+) create mode 100644 EconomicStressAgentORE/.env.example create mode 100644 EconomicStressAgentORE/.gitignore create mode 100644 EconomicStressAgentORE/README.md create mode 100644 EconomicStressAgentORE/agent.py create mode 100644 EconomicStressAgentORE/config.py create mode 100644 EconomicStressAgentORE/data/scenarios.json create mode 100644 EconomicStressAgentORE/data/sector_mapping.csv create mode 100644 EconomicStressAgentORE/historical_scenarios.py create mode 100644 EconomicStressAgentORE/impact_summarizer.py create mode 100644 EconomicStressAgentORE/ore_runner.py create mode 100644 EconomicStressAgentORE/oredata/Input/conventions.xml create mode 100644 EconomicStressAgentORE/oredata/Input/curveconfig.xml create mode 100644 EconomicStressAgentORE/oredata/Input/fixings.csv create mode 100644 EconomicStressAgentORE/oredata/Input/marketdata.csv create mode 100644 EconomicStressAgentORE/oredata/Input/parstresstest.xml create mode 100644 EconomicStressAgentORE/oredata/Input/portfolio.xml create mode 100644 EconomicStressAgentORE/oredata/Input/pricingengine.xml create mode 100644 EconomicStressAgentORE/oredata/Input/sensitivity.xml create mode 100644 EconomicStressAgentORE/oredata/Input/simulation.xml create mode 100644 EconomicStressAgentORE/oredata/Input/todaysmarket.xml create mode 100644 EconomicStressAgentORE/oredata/ore.xml create mode 100644 EconomicStressAgentORE/requirements.txt create mode 100644 EconomicStressAgentORE/scenario_analyzer.py create mode 100644 EconomicStressAgentORE/stresstest_builder.py create mode 100644 EconomicStressAgentORE/test_integration.py create mode 100644 EconomicStressAgentORE/todaysmarket_analyzer.py diff --git a/.gitignore b/.gitignore index 0e59d72..a2da2c1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ .ipynb_checkpoints ipynb_checkpoints *.png +*/.env +.env +EconomicStressAgentORE/oredata/Output +EconomicStressAgentORE/oredata/ore_agent.xml +EconomicStressAgentORE/oredata/Input/agent_stress.xml diff --git a/EconomicStressAgentORE/.env.example b/EconomicStressAgentORE/.env.example new file mode 100644 index 0000000..c552cc2 --- /dev/null +++ b/EconomicStressAgentORE/.env.example @@ -0,0 +1,5 @@ +# LLM API key (OpenAI) +OPENAI_API_KEY=sk-... + +# OpenAI model to use +OPENAI_MODEL=gpt-4 diff --git a/EconomicStressAgentORE/.gitignore b/EconomicStressAgentORE/.gitignore new file mode 100644 index 0000000..2b08d06 --- /dev/null +++ b/EconomicStressAgentORE/.gitignore @@ -0,0 +1,2 @@ +OREDir/Output/* +__pycache__/ \ No newline at end of file diff --git a/EconomicStressAgentORE/README.md b/EconomicStressAgentORE/README.md new file mode 100644 index 0000000..538648c --- /dev/null +++ b/EconomicStressAgentORE/README.md @@ -0,0 +1,165 @@ +# Economic Scenario Stress Test Agent + +An AI agent that maps a free-text economic scenario to similar historical scenarios, +derives market shifts, runs an ORE stress test, and summarises the P&L impact. + +This is just educational purpose, I developed this agent to learn more about AI Agents +and how to integrate Open Source Risk Engine into an Agent. + +The scenarios are mock and AI generated, as is most of the code. + +## Workflow + +``` +User describes scenario + β”‚ + β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ 1. Scenario Analyzer β”‚ GPT-4 + 20-scenario knowledge base +β”‚ (historical lookup) β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ structured JSON of market shifts + β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ 2. Stress Test Builder β”‚ generates agent_stress.xml for ORE +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ ore_agent.xml + Input/agent_stress.xml + β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ 3. ORE Runner β”‚ runs ORE (Python API) +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ Output/stresstest.csv + β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ 4. Impact Summarizer β”‚ Markdown report + LLM narrative +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +## Setup + +### 1. Install dependencies + +```bash +pip install -r requirements.txt +``` + +The `open-source-risk-engine` package provides the ORE Python bindings (`from ORE import *`). + +### 2. Configure environment + +```bash +cp .env.example .env +# then edit .env +``` + +`.env` keys: + +| Key | Description | Required | +| ---------------- | ----------------------------- | -------- | +| `OPENAI_API_KEY` | Your OpenAI API key | Yes | +| `OPENAI_MODEL` | Model name (default: `gpt-4`) | No | + +### 3. ORE workspace + +The agent expects the ORE workspace at `./OREDir/` containing: + +``` +OREDir/ +β”œβ”€β”€ ore.xml ← main ORE config +β”œβ”€β”€ Input/ +β”‚ β”œβ”€β”€ portfolio.xml +β”‚ β”œβ”€β”€ marketdata.csv +β”‚ β”œβ”€β”€ conventions.xml +β”‚ β”œβ”€β”€ curveconfig.xml +β”‚ β”œβ”€β”€ todaysmarket.xml +β”‚ β”œβ”€β”€ simulation.xml +β”‚ β”œβ”€β”€ sensitivity.xml +β”‚ └── pricingengine.xml +└── Output/ ← auto-created by ORE +``` + +The agent injects two files at runtime: + +- `OREDir/Input/agent_stress.xml` β€” generated stress scenario +- `OREDir/ore_agent.xml` β€” stripped-down ore.xml (stress analytic only) + +## Usage + +```bash +# Basic usage +python agent.py --scenario "A sudden European banking crisis with contagion fears" + +# With all options +python agent.py \ + --scenario "A sudden European banking crisis with contagion fears" \ + --ore-workspace ./OREDir \ + --scenario-id my_scenario \ + --output report.md \ + --verbose +``` + +## Architecture + +| File | Purpose | +| ------------------------- | --------------------------------------------- | +| `agent.py` | CLI orchestrator β€” ties all steps together | +| `scenario_analyzer.py` | LLM call β€” maps text to market shifts | +| `stresstest_builder.py` | XML generator β€” writes ORE stress test config | +| `ore_runner.py` | ORE execution (Python API or subprocess) | +| `impact_summarizer.py` | CSV parser + LLM narrative report | +| `historical_scenarios.py` | Knowledge base of 20 historical episodes | +| `config.py` | Paths, model settings, curve grids | + +## Market entities handled + +| Asset class | Scenario key | ORE name | +| ------------- | ------------ | ------------------------------ | +| FX | `EURUSD` | `EURUSD` (absolute shift) | +| Euro equities | `SX5E` | `RIC:.STOXX50` | +| US equities | `SPX` | `RIC:.SPX` | +| EUR rates | `rates_eur` | EUR-ESTER + EUR discount curve | +| USD rates | `rates_usd` | USD-SOFR + USD discount curve | + +Rate shifts are specified at **1Y, 2Y, 5Y, 10Y, 30Y** and interpolated onto +the full ORE tenor grid using piecewise-linear interpolation. + +## Example output + +``` +╔══════════════════════════════════════════════════════════╗ +β•‘ Economic Scenario Stress Test Agent β•‘ +β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β• + +Analyzing scenario: "A sudden European banking crisis with contagion fears" + + β–Ά Step 1/4 Analyzing scenario with LLM … + βœ“ Scenario analysis complete + +Matched scenarios: + β€’ 2010-2012 European Sovereign Debt Crisis + β€’ 2023 US Regional Banking Crisis + +EURUSD : -0.0700 (absolute) +SX5E : -19.0% +SPX : -8.5% +EUR rates: 1Y -8bp 2Y -10bp 5Y -9bp 10Y -7bp 30Y -4bp +USD rates: 1Y -8bp 2Y -12bp 5Y -14bp 10Y -12bp 30Y -8bp + + β–Ά Step 2/4 Generating ORE stress test XML … + β–Ά Step 3/4 Running ORE … + β–Ά Step 4/4 Generating impact report … + +════════════════════════════════════════════════════════════ + Portfolio Stress Test Impact Report +════════════════════════════════════════════════════════════ + +**TOTAL P&L: +92,000 EUR [GAIN]** + +| Trade | Base NPV | Stressed NPV | P&L Impact | +|-------------|------------|--------------|-------------| +| CDS | -64,059 | -63,293 | +766 | +| XccySwap | 268,878 | 230,964 | -37,914 | +| EUR6MSwap | 5,924,804 | 5,867,079 | -57,725 | +| ... | | | | +| **TOTAL** | ... | ... | **+92,000** | +``` diff --git a/EconomicStressAgentORE/agent.py b/EconomicStressAgentORE/agent.py new file mode 100644 index 0000000..35c33af --- /dev/null +++ b/EconomicStressAgentORE/agent.py @@ -0,0 +1,195 @@ +""" +agent.py β€” Economic Scenario Stress Test Agent (CLI orchestrator). + +Usage: + python agent.py --scenario "A sudden European banking crisis" [options] +""" + +from __future__ import annotations + +import sys +from pathlib import Path + +import click + +import config +from historical_scenarios import ScenarioKnowledgeBase +import impact_summarizer +import ore_runner +import scenario_analyzer +import stresstest_builder +import todaysmarket_analyzer + +BANNER = """ +╔══════════════════════════════════════════════════════════╗ +β•‘ Economic Scenario Stress Test Agent β•‘ +β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β• +""" + +STEP = " β–Ά " +OK = " βœ“ " +WARN = " ⚠ " + + +# ── Core orchestration ──────────────────────────────────────────────────────── + +def run( + scenario_description: str, + ore_workspace: Path, + scenario_id: str = "agent_scenario", + verbose: bool = False, +) -> str: + """ + Full pipeline: analyze β†’ build β†’ run ORE β†’ summarize. + + Parameters + ---------- + scenario_description : free-text user scenario + ore_workspace : path to the OREDir workspace + scenario_id : identifier embedded in the stress test XML + verbose : print extra debug info + + Returns + ------- + Markdown-formatted report string. + """ + print(BANNER) + print(f'Analyzing scenario: "{scenario_description}"\n') + + # ── Step 1: Scenario Analysis ───────────────────────────────────────── + print(STEP + "Step 1/5 Analyzing scenario with LLM …") + knowledge_base = ScenarioKnowledgeBase( + config.DATA_DIR / "scenarios.json" + ) + analysis = scenario_analyzer.analyze(scenario_description, knowledge_base) + print(OK + "Scenario analysis complete\n") + print(scenario_analyzer.format_shifts(analysis)) + print() + + shifts = analysis["shifts"] + + # ── Step 2: Parse market structure ──────────────────────────────────── + print(STEP + "Step 2/5 Parsing todaysmarket.xml …") + ore_workspace = Path(ore_workspace) + tm_xml = ore_workspace / "Input" / "todaysmarket.xml" + market = todaysmarket_analyzer.parse(tm_xml) + sector_map = todaysmarket_analyzer.load_sector_mapping() + print(OK + f"Discovered {len(market.currencies)} currencies, " + f"{len(market.discount_curves)} discount curves, " + f"{len(market.equity_curves)} equities, " + f"{len(market.default_curves)} credit names\n") + + # ── Step 3: Build stress test XML ───────────────────────────────────── + print(STEP + "Step 3/5 Generating ORE stress test XML …") + + stress_xml = ore_workspace / "Input" / "agent_stress.xml" + stresstest_builder.build( + shifts=shifts, + market=market, + sector_map=sector_map, + output_path=stress_xml, + scenario_id=scenario_id, + scenario_label=( + "Generated by Economic Scenario Stress Test Agent | " + "Matched: " + ", ".join(analysis["matched_scenarios"]) + ), + ) + print(OK + f"Written: {stress_xml}") + + ore_agent_xml = ore_workspace / "ore_agent.xml" + stresstest_builder.build_ore_config( + base_ore_xml=ore_workspace / "ore.xml", + output_ore_xml=ore_agent_xml, + stress_config_file="agent_stress.xml", + ) + print(OK + f"Written: {ore_agent_xml}\n") + + # ── Step 4: Run ORE ─────────────────────────────────────────────────── + print(STEP + "Step 4/5 Running ORE …") + csv_path = ore_runner.run( + ore_xml=ore_agent_xml, + workspace=ore_workspace, + ) + print(OK + f"ORE completed. Results: {csv_path}\n") + + # ── Step 5: Summarize ───────────────────────────────────────────────── + print(STEP + "Step 5/5 Generating impact report …") + report = impact_summarizer.summarize( + csv_path=csv_path, + scenario_description=scenario_description, + shifts=shifts, + scenario_id=scenario_id, + ) + print(OK + "Report ready\n") + + return report + + +# ── CLI ─────────────────────────────────────────────────────────────────────── + +@click.command() +@click.option( + "--scenario", "-s", + required=True, + help="Free-text description of the economic scenario to stress-test.", +) +@click.option( + "--ore-workspace", + default=str(config.ORE_WORKSPACE), + show_default=True, + help="Path to the ORE workspace directory (must contain ore.xml, Input/, Output/).", +) +@click.option( + "--scenario-id", + default="agent_scenario", + show_default=True, + help="Identifier for the stress scenario in the ORE XML (no spaces).", +) +@click.option( + "--output", + "-o", + default=None, + help="Write the Markdown report to this file (prints to stdout if not set).", +) +@click.option("--verbose", "-v", is_flag=True, help="Enable verbose output.") +def main( + scenario: str, + ore_workspace: str, + scenario_id: str, + output: str | None, + verbose: bool, +) -> None: + """Economic Scenario Stress Test Agent. + + Analyses a free-text economic scenario, maps it to historical market + parallels, generates an ORE stress test, runs it, and prints a P&L report. + """ + try: + report = run( + scenario_description=scenario, + ore_workspace=Path(ore_workspace), + scenario_id=scenario_id, + verbose=verbose, + ) + except EnvironmentError as exc: + click.echo(f"\n{WARN} Configuration error: {exc}", err=True) + sys.exit(1) + except FileNotFoundError as exc: + click.echo(f"\n{WARN} File not found: {exc}", err=True) + sys.exit(1) + except Exception as exc: # noqa: BLE001 + click.echo(f"\n{WARN} Unexpected error: {exc}", err=True) + if verbose: + import traceback + traceback.print_exc() + sys.exit(1) + + if output: + Path(output).write_text(report, encoding="utf-8") + click.echo(f"Report written to: {output}") + else: + print(report) + + +if __name__ == "__main__": + main() diff --git a/EconomicStressAgentORE/config.py b/EconomicStressAgentORE/config.py new file mode 100644 index 0000000..f4b0430 --- /dev/null +++ b/EconomicStressAgentORE/config.py @@ -0,0 +1,45 @@ +""" +config.py β€” central configuration for the Economic Scenario Stress Test Agent. +""" + +import os +from pathlib import Path + +from dotenv import load_dotenv + +load_dotenv() + +# ── Paths ───────────────────────────────────────────────────────────────────── +BASE_DIR = Path(__file__).parent +DATA_DIR = BASE_DIR / "data" # raw data files (e.g. historical scenarios) +ORE_WORKSPACE = BASE_DIR / "oredata" # workspace that contains ore.xml +ORE_INPUT_DIR = ORE_WORKSPACE / "Input" +ORE_OUTPUT_DIR = ORE_WORKSPACE / "Output" + +# Generated files written by the agent +AGENT_STRESS_XML = ORE_INPUT_DIR / "agent_stress.xml" +AGENT_ORE_XML = ORE_WORKSPACE / "ore_agent.xml" + +# ── LLM ─────────────────────────────────────────────────────────────────────── +OPENAI_API_KEY: str = os.getenv("OPENAI_API_KEY", "") +OPENAI_MODEL: str = os.getenv("OPENAI_MODEL", "gpt-4") + +# ── Sector-mapping CSV (maps equity/credit names β†’ sectors) ─────────────────── +SECTOR_MAPPING_CSV: Path = BASE_DIR / "sector_mapping.csv" + +# ── Standard tenor grids (used when generating simulation.xml & stresstest) ─── +STANDARD_RATE_TENORS: list[str] = [ + "1Y", "2Y", "3Y", "5Y", "7Y", "10Y", "15Y", "20Y", "30Y", +] + +STANDARD_CREDIT_TENORS: list[str] = [ + "6M", "1Y", "2Y", "3Y", "5Y", "7Y", "10Y", "15Y", "20Y", +] + +STANDARD_INFLATION_TENORS: list[str] = [ + "1Y", "2Y", "3Y", "5Y", "7Y", "10Y", "15Y", "20Y", "30Y", +] + +STANDARD_EQUITY_DIV_TENORS: list[str] = [ + "6M", "1Y", "2Y", "3Y", "5Y", "7Y", "10Y", +] diff --git a/EconomicStressAgentORE/data/scenarios.json b/EconomicStressAgentORE/data/scenarios.json new file mode 100644 index 0000000..67801ec --- /dev/null +++ b/EconomicStressAgentORE/data/scenarios.json @@ -0,0 +1,899 @@ +[ + { + "name": "2008 Global Financial Crisis", + "period": "Sep 2008 – Mar 2009", + "description": "Collapse of Lehman Brothers triggered a global credit crunch, massive equity sell-off, flight to quality into US Treasuries, and extreme volatility across all asset classes.", + "shifts": { + "rates": { + "EUR": { + "1Y": -0.02, + "2Y": -0.025, + "5Y": -0.015, + "10Y": -0.01, + "30Y": -0.005 + }, + "USD": { + "1Y": -0.03, + "2Y": -0.03, + "5Y": -0.02, + "10Y": -0.015, + "30Y": -0.01 + } + }, + "fx": { "EURUSD": -0.1 }, + "equity": { + "EUR": -0.45, + "USD": -0.4 + }, + "credit": { + "EUR": { + "1Y": 0.015, + "2Y": 0.02, + "3Y": 0.025, + "5Y": 0.03, + "10Y": 0.035 + }, + "USD": { "1Y": 0.02, "2Y": 0.025, "3Y": 0.03, "5Y": 0.035, "10Y": 0.04 } + } + } + }, + { + "name": "2010-2012 European Sovereign Debt Crisis", + "period": "2010 – 2012", + "description": "Greek debt crisis spread to Ireland, Portugal, Spain, and Italy. EUR weakened significantly, European equities fell, and EUR rates were cut while peripheral spreads blew out.", + "shifts": { + "rates": { + "EUR": { + "1Y": -0.01, + "2Y": -0.012, + "5Y": -0.008, + "10Y": -0.005, + "30Y": -0.003 + }, + "USD": { + "1Y": -0.008, + "2Y": -0.01, + "5Y": -0.012, + "10Y": -0.015, + "30Y": -0.01 + } + }, + "fx": { "EURUSD": -0.15 }, + "equity": { + "EUR": -0.3, + "USD": -0.12 + }, + "credit": { + "EUR": { + "1Y": 0.012, + "2Y": 0.018, + "3Y": 0.022, + "5Y": 0.025, + "10Y": 0.02 + }, + "USD": { + "1Y": 0.005, + "2Y": 0.006, + "3Y": 0.007, + "5Y": 0.008, + "10Y": 0.008 + }, + "Sovereign": { + "1Y": 0.03, + "2Y": 0.04, + "3Y": 0.05, + "5Y": 0.06, + "10Y": 0.05 + } + } + } + }, + { + "name": "2015 China Devaluation / EM Shock", + "period": "Aug – Sep 2015", + "description": "PBoC surprised markets with a CNY devaluation, triggering a global equity sell-off and flight to safety.", + "shifts": { + "rates": { + "EUR": { + "1Y": -0.002, + "2Y": -0.003, + "5Y": -0.005, + "10Y": -0.008, + "30Y": -0.006 + }, + "USD": { + "1Y": -0.005, + "2Y": -0.008, + "5Y": -0.012, + "10Y": -0.015, + "30Y": -0.01 + } + }, + "fx": { "EURUSD": 0.04 }, + "equity": { + "EUR": -0.18, + "USD": -0.12 + }, + "credit": { + "EUR": { + "1Y": 0.003, + "2Y": 0.004, + "3Y": 0.005, + "5Y": 0.005, + "10Y": 0.004 + }, + "USD": { + "1Y": 0.002, + "2Y": 0.003, + "3Y": 0.004, + "5Y": 0.004, + "10Y": 0.003 + } + } + } + }, + { + "name": "2020 COVID-19 Crash", + "period": "Feb – Mar 2020", + "description": "Global pandemic caused the fastest equity bear market in history, massive rate cuts, and a short-lived USD liquidity squeeze.", + "shifts": { + "rates": { + "EUR": { + "1Y": -0.003, + "2Y": -0.005, + "5Y": -0.007, + "10Y": -0.008, + "30Y": -0.005 + }, + "USD": { + "1Y": -0.015, + "2Y": -0.015, + "5Y": -0.012, + "10Y": -0.01, + "30Y": -0.008 + } + }, + "fx": { "EURUSD": -0.03 }, + "equity": { + "EUR": -0.35, + "USD": -0.34 + }, + "credit": { + "EUR": { + "1Y": 0.012, + "2Y": 0.018, + "3Y": 0.022, + "5Y": 0.025, + "10Y": 0.02 + }, + "USD": { + "1Y": 0.015, + "2Y": 0.02, + "3Y": 0.025, + "5Y": 0.028, + "10Y": 0.022 + } + } + } + }, + { + "name": "2022 Russia-Ukraine / Energy Crisis", + "period": "Feb – Oct 2022", + "description": "Russian invasion of Ukraine caused energy supply shock in Europe, EUR fell to parity with USD, European equities dropped, and central banks hiked rates aggressively to fight inflation.", + "shifts": { + "rates": { + "EUR": { + "1Y": 0.025, + "2Y": 0.03, + "5Y": 0.025, + "10Y": 0.02, + "30Y": 0.015 + }, + "USD": { + "1Y": 0.03, + "2Y": 0.035, + "5Y": 0.025, + "10Y": 0.018, + "30Y": 0.012 + } + }, + "fx": { "EURUSD": -0.15 }, + "equity": { + "EUR": -0.22, + "USD": -0.25 + }, + "credit": { + "EUR": { + "1Y": 0.01, + "2Y": 0.014, + "3Y": 0.016, + "5Y": 0.018, + "10Y": 0.015 + }, + "USD": { + "1Y": 0.004, + "2Y": 0.006, + "3Y": 0.007, + "5Y": 0.008, + "10Y": 0.007 + } + } + } + }, + { + "name": "1992 ERM Crisis / Black Wednesday", + "period": "Sep 1992", + "description": "Speculative attack on the British pound and Italian lira forced exits from the ERM. European currencies fell sharply against the DEM. EUR proxy (DEM) strengthened vs USD.", + "shifts": { + "rates": { + "EUR": { + "1Y": 0.01, + "2Y": 0.008, + "5Y": 0.005, + "10Y": 0.003, + "30Y": 0.002 + }, + "USD": { + "1Y": -0.003, + "2Y": -0.003, + "5Y": -0.002, + "10Y": -0.002, + "30Y": -0.001 + } + }, + "fx": { "EURUSD": 0.05 }, + "equity": { + "EUR": -0.1, + "USD": -0.03 + }, + "credit": { + "EUR": { + "1Y": 0.002, + "2Y": 0.003, + "3Y": 0.004, + "5Y": 0.004, + "10Y": 0.003 + }, + "USD": { + "1Y": 0.001, + "2Y": 0.002, + "3Y": 0.002, + "5Y": 0.002, + "10Y": 0.002 + } + } + } + }, + { + "name": "2001 Dot-com Bust", + "period": "Mar 2000 – Oct 2002", + "description": "Bursting of the tech bubble led to a prolonged equity bear market, recession fears, and aggressive Fed rate cuts.", + "shifts": { + "rates": { + "EUR": { + "1Y": -0.015, + "2Y": -0.012, + "5Y": -0.008, + "10Y": -0.005, + "30Y": -0.003 + }, + "USD": { + "1Y": -0.04, + "2Y": -0.035, + "5Y": -0.025, + "10Y": -0.015, + "30Y": -0.01 + } + }, + "fx": { "EURUSD": 0.08 }, + "equity": { + "EUR": -0.55, + "USD": -0.45, + "Tech": -0.75 + }, + "credit": { + "EUR": { + "1Y": 0.005, + "2Y": 0.007, + "3Y": 0.008, + "5Y": 0.009, + "10Y": 0.008 + }, + "USD": { + "1Y": 0.008, + "2Y": 0.01, + "3Y": 0.012, + "5Y": 0.014, + "10Y": 0.012 + } + } + } + }, + { + "name": "1997 Asian Financial Crisis", + "period": "Jul 1997 – Jan 1998", + "description": "Currency collapses across SE Asia (THB, KRW, IDR) spread contagion to global markets. Safe-haven flows benefited USD and Treasuries.", + "shifts": { + "rates": { + "EUR": { + "1Y": -0.003, + "2Y": -0.004, + "5Y": -0.005, + "10Y": -0.005, + "30Y": -0.003 + }, + "USD": { + "1Y": -0.005, + "2Y": -0.008, + "5Y": -0.01, + "10Y": -0.01, + "30Y": -0.008 + } + }, + "fx": { "EURUSD": -0.06 }, + "equity": { + "EUR": -0.15, + "USD": -0.08 + }, + "credit": { + "EUR": { + "1Y": 0.002, + "2Y": 0.003, + "3Y": 0.004, + "5Y": 0.005, + "10Y": 0.004 + }, + "USD": { + "1Y": 0.003, + "2Y": 0.005, + "3Y": 0.006, + "5Y": 0.007, + "10Y": 0.006 + } + } + } + }, + { + "name": "2023 US Regional Banking Crisis", + "period": "Mar 2023", + "description": "Collapse of SVB and Signature Bank raised fears of a wider banking crisis. Rates fell sharply as markets priced in Fed cuts; equities initially fell but recovered quickly.", + "shifts": { + "rates": { + "EUR": { + "1Y": -0.005, + "2Y": -0.008, + "5Y": -0.01, + "10Y": -0.008, + "30Y": -0.005 + }, + "USD": { + "1Y": -0.01, + "2Y": -0.015, + "5Y": -0.018, + "10Y": -0.015, + "30Y": -0.01 + } + }, + "fx": { "EURUSD": 0.03 }, + "equity": { + "EUR": -0.08, + "USD": -0.05 + }, + "credit": { + "EUR": { + "1Y": 0.003, + "2Y": 0.004, + "3Y": 0.005, + "5Y": 0.005, + "10Y": 0.004 + }, + "USD": { + "1Y": 0.006, + "2Y": 0.008, + "3Y": 0.01, + "5Y": 0.012, + "10Y": 0.01 + }, + "SeniorUnsecured": { + "1Y": 0.012, + "2Y": 0.016, + "3Y": 0.02, + "5Y": 0.024, + "10Y": 0.02 + } + } + } + }, + { + "name": "1998 LTCM / Russian Default", + "period": "Aug – Oct 1998", + "description": "Russian debt default and LTCM collapse caused a global liquidity crisis. Massive flight to quality into US Treasuries, equity sell-off, and credit spreads blew out.", + "shifts": { + "rates": { + "EUR": { + "1Y": -0.005, + "2Y": -0.008, + "5Y": -0.01, + "10Y": -0.01, + "30Y": -0.008 + }, + "USD": { + "1Y": -0.008, + "2Y": -0.012, + "5Y": -0.015, + "10Y": -0.015, + "30Y": -0.012 + } + }, + "fx": { "EURUSD": 0.06 }, + "equity": { + "EUR": -0.3, + "USD": -0.2 + }, + "credit": { + "EUR": { + "1Y": 0.008, + "2Y": 0.012, + "3Y": 0.015, + "5Y": 0.018, + "10Y": 0.015 + }, + "USD": { + "1Y": 0.012, + "2Y": 0.016, + "3Y": 0.02, + "5Y": 0.024, + "10Y": 0.02 + }, + "Sovereign": { + "1Y": 0.02, + "2Y": 0.03, + "3Y": 0.04, + "5Y": 0.05, + "10Y": 0.04 + } + } + } + }, + { + "name": "2011 US Debt-Ceiling Crisis / S&P Downgrade", + "period": "Jul – Aug 2011", + "description": "US debt-ceiling standoff and first-ever S&P downgrade of US sovereign debt. Paradoxically Treasuries rallied on flight to quality, equities sold off.", + "shifts": { + "rates": { + "EUR": { + "1Y": -0.003, + "2Y": -0.005, + "5Y": -0.008, + "10Y": -0.01, + "30Y": -0.008 + }, + "USD": { + "1Y": -0.005, + "2Y": -0.008, + "5Y": -0.012, + "10Y": -0.012, + "30Y": -0.01 + } + }, + "fx": { "EURUSD": -0.05 }, + "equity": { + "EUR": -0.25, + "USD": -0.17 + }, + "credit": { + "EUR": { + "1Y": 0.004, + "2Y": 0.006, + "3Y": 0.008, + "5Y": 0.01, + "10Y": 0.008 + }, + "USD": { + "1Y": 0.005, + "2Y": 0.007, + "3Y": 0.009, + "5Y": 0.01, + "10Y": 0.008 + } + } + } + }, + { + "name": "2018 Volmageddon / Feb Sell-off", + "period": "Feb 2018", + "description": "A sharp spike in US wage growth triggered fears of faster Fed tightening. VIX exploded, inverse-vol products blew up, equities fell sharply.", + "shifts": { + "rates": { + "EUR": { + "1Y": 0.002, + "2Y": 0.003, + "5Y": 0.005, + "10Y": 0.005, + "30Y": 0.003 + }, + "USD": { + "1Y": 0.005, + "2Y": 0.008, + "5Y": 0.01, + "10Y": 0.008, + "30Y": 0.005 + } + }, + "fx": { "EURUSD": 0.02 }, + "equity": { + "EUR": -0.1, + "USD": -0.1 + }, + "credit": { + "EUR": { + "1Y": 0.001, + "2Y": 0.002, + "3Y": 0.002, + "5Y": 0.003, + "10Y": 0.002 + }, + "USD": { + "1Y": 0.002, + "2Y": 0.002, + "3Y": 0.003, + "5Y": 0.003, + "10Y": 0.002 + } + } + } + }, + { + "name": "2013 Taper Tantrum", + "period": "May – Sep 2013", + "description": "Bernanke hinted at tapering QE. US long-term rates spiked, EM currencies and equities fell, USD strengthened.", + "shifts": { + "rates": { + "EUR": { + "1Y": 0.002, + "2Y": 0.005, + "5Y": 0.008, + "10Y": 0.01, + "30Y": 0.008 + }, + "USD": { + "1Y": 0.003, + "2Y": 0.008, + "5Y": 0.012, + "10Y": 0.013, + "30Y": 0.01 + } + }, + "fx": { "EURUSD": -0.04 }, + "equity": { + "EUR": -0.08, + "USD": -0.05 + }, + "credit": { + "EUR": { + "1Y": 0.002, + "2Y": 0.003, + "3Y": 0.003, + "5Y": 0.004, + "10Y": 0.003 + }, + "USD": { + "1Y": 0.002, + "2Y": 0.003, + "3Y": 0.004, + "5Y": 0.004, + "10Y": 0.003 + } + } + } + }, + { + "name": "2016 Brexit Referendum", + "period": "Jun 2016", + "description": "The UK voted to leave the EU. GBP collapsed, EUR weakened, European equities fell, and rates dropped as markets priced slower growth.", + "shifts": { + "rates": { + "EUR": { + "1Y": -0.003, + "2Y": -0.005, + "5Y": -0.008, + "10Y": -0.01, + "30Y": -0.008 + }, + "USD": { + "1Y": -0.005, + "2Y": -0.008, + "5Y": -0.01, + "10Y": -0.012, + "30Y": -0.01 + } + }, + "fx": { "EURUSD": -0.04 }, + "equity": { + "EUR": -0.12, + "USD": -0.05 + }, + "credit": { + "EUR": { + "1Y": 0.004, + "2Y": 0.006, + "3Y": 0.007, + "5Y": 0.008, + "10Y": 0.006 + }, + "USD": { + "1Y": 0.001, + "2Y": 0.002, + "3Y": 0.002, + "5Y": 0.002, + "10Y": 0.002 + } + } + } + }, + { + "name": "2022-2023 Global Rate Hiking Cycle", + "period": "2022 – 2023", + "description": "Post-COVID inflation surge led to synchronised global rate hikes by the Fed, ECB, BoE. Bond markets had historic losses, equities de-rated.", + "shifts": { + "rates": { + "EUR": { + "1Y": 0.035, + "2Y": 0.03, + "5Y": 0.025, + "10Y": 0.02, + "30Y": 0.015 + }, + "USD": { + "1Y": 0.045, + "2Y": 0.04, + "5Y": 0.03, + "10Y": 0.022, + "30Y": 0.015 + } + }, + "fx": { "EURUSD": -0.12 }, + "equity": { + "EUR": -0.18, + "USD": -0.2 + }, + "credit": { + "EUR": { + "1Y": 0.005, + "2Y": 0.007, + "3Y": 0.008, + "5Y": 0.009, + "10Y": 0.008 + }, + "USD": { + "1Y": 0.004, + "2Y": 0.005, + "3Y": 0.006, + "5Y": 0.007, + "10Y": 0.006 + } + } + } + }, + { + "name": "1970s-style Stagflation", + "period": "1973 – 1980 (composite)", + "description": "Oil-price shocks combined with wage-price spirals produced persistently high inflation and economic stagnation. Rates rose dramatically, equities underperformed in real terms.", + "shifts": { + "rates": { + "EUR": { + "1Y": 0.03, + "2Y": 0.03, + "5Y": 0.025, + "10Y": 0.02, + "30Y": 0.015 + }, + "USD": { "1Y": 0.04, "2Y": 0.04, "5Y": 0.035, "10Y": 0.03, "30Y": 0.02 } + }, + "fx": { "EURUSD": -0.08 }, + "equity": { + "EUR": -0.2, + "USD": -0.15 + }, + "credit": { + "EUR": { + "1Y": 0.006, + "2Y": 0.008, + "3Y": 0.01, + "5Y": 0.012, + "10Y": 0.01 + }, + "USD": { + "1Y": 0.008, + "2Y": 0.01, + "3Y": 0.012, + "5Y": 0.014, + "10Y": 0.012 + } + } + } + }, + { + "name": "Deflationary Recession (Japan-style)", + "period": "Composite (1990s–2010s Japan)", + "description": "Protracted deflation, zero-bound rates, equity malaise. If applied to EUR/USD: massive rate cuts, equities drift lower, EUR weakens as ECB goes ultra-loose.", + "shifts": { + "rates": { + "EUR": { + "1Y": -0.02, + "2Y": -0.02, + "5Y": -0.018, + "10Y": -0.015, + "30Y": -0.01 + }, + "USD": { + "1Y": -0.015, + "2Y": -0.015, + "5Y": -0.012, + "10Y": -0.01, + "30Y": -0.005 + } + }, + "fx": { "EURUSD": -0.1 }, + "equity": { + "EUR": -0.25, + "USD": -0.1 + }, + "credit": { + "EUR": { + "1Y": 0.005, + "2Y": 0.007, + "3Y": 0.008, + "5Y": 0.009, + "10Y": 0.008 + }, + "USD": { + "1Y": 0.003, + "2Y": 0.004, + "3Y": 0.005, + "5Y": 0.005, + "10Y": 0.004 + } + } + } + }, + { + "name": "Eurozone Break-up (Tail Risk)", + "period": "Hypothetical", + "description": "A large Eurozone member exits the single currency. Extreme EUR weakness, European equities collapse, EUR rates spike on redenomination risk, USD rates fall on safe-haven flows.", + "shifts": { + "rates": { + "EUR": { + "1Y": 0.03, + "2Y": 0.035, + "5Y": 0.03, + "10Y": 0.025, + "30Y": 0.02 + }, + "USD": { + "1Y": -0.015, + "2Y": -0.02, + "5Y": -0.025, + "10Y": -0.02, + "30Y": -0.015 + } + }, + "fx": { "EURUSD": -0.25 }, + "equity": { + "EUR": -0.5, + "USD": -0.2 + }, + "credit": { + "EUR": { + "1Y": 0.025, + "2Y": 0.035, + "3Y": 0.04, + "5Y": 0.045, + "10Y": 0.04 + }, + "USD": { + "1Y": 0.006, + "2Y": 0.008, + "3Y": 0.01, + "5Y": 0.012, + "10Y": 0.01 + }, + "Sovereign": { + "1Y": 0.05, + "2Y": 0.07, + "3Y": 0.08, + "5Y": 0.09, + "10Y": 0.08 + } + } + } + }, + { + "name": "US-China Trade War Escalation", + "period": "2018 – 2019", + "description": "Escalating tariffs between US and China caused global growth fears, equity sell-offs, and a flight to bonds.", + "shifts": { + "rates": { + "EUR": { + "1Y": -0.003, + "2Y": -0.005, + "5Y": -0.008, + "10Y": -0.01, + "30Y": -0.008 + }, + "USD": { + "1Y": -0.005, + "2Y": -0.008, + "5Y": -0.012, + "10Y": -0.012, + "30Y": -0.008 + } + }, + "fx": { "EURUSD": -0.03 }, + "equity": { + "EUR": -0.12, + "USD": -0.1 + }, + "credit": { + "EUR": { + "1Y": 0.002, + "2Y": 0.003, + "3Y": 0.004, + "5Y": 0.005, + "10Y": 0.004 + }, + "USD": { + "1Y": 0.003, + "2Y": 0.004, + "3Y": 0.005, + "5Y": 0.006, + "10Y": 0.005 + } + } + } + }, + { + "name": "Sudden Inflation Spike", + "period": "Hypothetical / 2021-style", + "description": "Unexpected jump in inflation forces central banks to tighten much faster than expected. Front-end rates spike, curves flatten, equities de-rate.", + "shifts": { + "rates": { + "EUR": { + "1Y": 0.02, + "2Y": 0.018, + "5Y": 0.012, + "10Y": 0.008, + "30Y": 0.005 + }, + "USD": { + "1Y": 0.025, + "2Y": 0.022, + "5Y": 0.015, + "10Y": 0.01, + "30Y": 0.005 + } + }, + "fx": { "EURUSD": -0.05 }, + "equity": { + "EUR": -0.15, + "USD": -0.12 + }, + "credit": { + "EUR": { + "1Y": 0.004, + "2Y": 0.005, + "3Y": 0.006, + "5Y": 0.007, + "10Y": 0.006 + }, + "USD": { + "1Y": 0.003, + "2Y": 0.004, + "3Y": 0.005, + "5Y": 0.006, + "10Y": 0.005 + } + } + } + } +] diff --git a/EconomicStressAgentORE/data/sector_mapping.csv b/EconomicStressAgentORE/data/sector_mapping.csv new file mode 100644 index 0000000..ed7fc9e --- /dev/null +++ b/EconomicStressAgentORE/data/sector_mapping.csv @@ -0,0 +1,4 @@ +type,name,currency,sector +equity,RIC:.SPX,USD,Index +equity,RIC:.STOXX50,EUR,Index +credit,Underlying1,USD,SeniorUnsecured diff --git a/EconomicStressAgentORE/historical_scenarios.py b/EconomicStressAgentORE/historical_scenarios.py new file mode 100644 index 0000000..53d34aa --- /dev/null +++ b/EconomicStressAgentORE/historical_scenarios.py @@ -0,0 +1,96 @@ +""" +Historical economic scenarios knowledge base. + +Loads scenario data from a JSON file and provides structured access +for the scenario analyser and stress-test builder. + +Each scenario has the **generic schema**:: + + shifts: + rates: { CCY: { tenor: abs_shift } } + fx: { PAIR: abs_shift } + equity: { CCY_or_sector: relative_shift } + credit: { CCY_or_sector: { tenor: abs_shift } } + +* Rates / credit: absolute change (decimal, e.g. -0.015 = -150 bps). +* FX: absolute change in spot (e.g. -0.10 means spot drops by 0.10). +* Equity: relative change (e.g. -0.25 = -25 %). +""" + +from __future__ import annotations + +import json +from pathlib import Path + + +class ScenarioKnowledgeBase: + """Immutable collection of historical stress scenarios loaded from JSON.""" + + def __init__(self, path: Path | str) -> None: + self._path = Path(path) + if not self._path.exists(): + raise FileNotFoundError(f"Scenario file not found: {self._path}") + self._scenarios: list[dict] = self._load() + + # -- loading ------------------------------------------------------------- + + def _load(self) -> list[dict]: + with open(self._path, "r") as fh: + return json.load(fh) + + # -- public accessors ---------------------------------------------------- + + @property + def scenarios(self) -> list[dict]: + """Return the raw list of scenario dicts.""" + return self._scenarios + + def __len__(self) -> int: + return len(self._scenarios) + + def __getitem__(self, index: int) -> dict: + return self._scenarios[index] + + def __iter__(self): + return iter(self._scenarios) + + # -- formatting ---------------------------------------------------------- + + @staticmethod + def _fmt_tenor_dict(d: dict[str, float], unit: str = "bps") -> str: + """Format a {tenor: shift} dict for display.""" + return " / ".join( + f"{k}: {v * 10000:+.0f}" if unit == "bps" else f"{k}: {v:+.0%}" + for k, v in d.items() + ) + + def get_scenarios_text(self) -> str: + """Return a formatted text dump of all scenarios for LLM context.""" + lines: list[str] = [] + for i, s in enumerate(self._scenarios, 1): + lines.append(f"### {i}. {s['name']} ({s['period']})") + lines.append(s["description"]) + sh = s["shifts"] + + if "fx" in sh: + for pair, v in sh["fx"].items(): + lines.append(f" FX {pair}: {v:+.2f}") + + if "equity" in sh: + for key, v in sh["equity"].items(): + lines.append(f" Equity {key}: {v:+.0%}") + + if "rates" in sh: + for ccy, tenors in sh["rates"].items(): + lines.append( + f" Rates {ccy} (bps): {self._fmt_tenor_dict(tenors)}" + ) + + if "credit" in sh: + for key, tenors in sh["credit"].items(): + lines.append( + f" Credit {key} (bps): {self._fmt_tenor_dict(tenors)}" + ) + + lines.append("") + return "\n".join(lines) diff --git a/EconomicStressAgentORE/impact_summarizer.py b/EconomicStressAgentORE/impact_summarizer.py new file mode 100644 index 0000000..011eccc --- /dev/null +++ b/EconomicStressAgentORE/impact_summarizer.py @@ -0,0 +1,220 @@ +""" +impact_summarizer.py β€” Step 4: parse ORE stresstest.csv output, compute P&L +impacts, and generate a human-readable narrative via LLM. +""" + +from __future__ import annotations + +import io +from pathlib import Path +from typing import Any + +import pandas as pd +from openai import OpenAI + +import config + +# ── CSV parsing ─────────────────────────────────────────────────────────────── + +def _read_stresstest_csv(csv_path: Path, scenario_id: str) -> pd.DataFrame: + """ + Load stresstest.csv and return rows matching *scenario_id*. + + The file has a header line that starts with ``#TradeId`` β€” pandas handles + the ``#`` by treating it as part of the first column name, so we strip it. + """ + with csv_path.open() as f: + content = f.read() + + # Remove leading # from the header line + content = content.replace("#TradeId", "TradeId", 1) + + df = pd.read_csv(io.StringIO(content)) + df.columns = [c.strip() for c in df.columns] + + # Filter to this scenario + mask = df["ScenarioLabel"].str.strip() == scenario_id + filtered = df[mask].copy() + + if filtered.empty: + raise ValueError( + f"No results found for scenario '{scenario_id}' in {csv_path}.\n" + f"Available scenarios: {df['ScenarioLabel'].unique().tolist()}" + ) + + filtered["PnL"] = filtered["Scenario NPV"] - filtered["Base NPV"] + return filtered + + +# ── Analysis helpers ────────────────────────────────────────────────────────── + +def _compute_summary(df: pd.DataFrame) -> dict[str, Any]: + """Return a dict with totals and per-trade data.""" + total_base = df["Base NPV"].sum() + total_stressed = df["Scenario NPV"].sum() + total_pnl = total_stressed - total_base + + rows = df.sort_values("PnL").to_dict(orient="records") + + return { + "total_base_npv": total_base, + "total_stressed_npv": total_stressed, + "total_pnl": total_pnl, + "trades": rows, + "top_losers": sorted(rows, key=lambda r: r["PnL"])[:3], + "top_gainers": sorted(rows, key=lambda r: r["PnL"], reverse=True)[:3], + } + + +# ── Markdown table ──────────────────────────────────────────────────────────── + +def _format_table(summary: dict[str, Any]) -> str: + """Build a Markdown results table.""" + lines = [ + "| Trade | Base NPV | Stressed NPV | P&L Impact |", + "|-------|----------|--------------|------------|", + ] + for row in summary["trades"]: + pnl_str = f"{row['PnL']:+,.0f}" + lines.append( + f"| {row['TradeId']} " + f"| {row['Base NPV']:,.0f} " + f"| {row['Scenario NPV']:,.0f} " + f"| {pnl_str} |" + ) + lines.append( + f"| **TOTAL** " + f"| **{summary['total_base_npv']:,.0f}** " + f"| **{summary['total_stressed_npv']:,.0f}** " + f"| **{summary['total_pnl']:+,.0f}** |" + ) + return "\n".join(lines) + + +# ── LLM narrative ───────────────────────────────────────────────────────────── + +_NARRATIVE_SYSTEM = """\ +You are a senior risk analyst writing a concise executive summary of a +portfolio stress test. Keep the summary to 3-4 paragraphs. Use plain prose β€” +no bullet points. Be specific about the numbers provided. +""" + +def _llm_narrative( + scenario_description: str, + shifts: dict[str, Any], + summary: dict[str, Any], +) -> str: + """Call the LLM to produce a narrative paragraph about the results.""" + if not config.OPENAI_API_KEY: + return ( + "(LLM narrative unavailable β€” OPENAI_API_KEY not set)\n\n" + f"Total P&L impact: {summary['total_pnl']:+,.0f} EUR" + ) + + client = OpenAI(api_key=config.OPENAI_API_KEY) + + # ── Build a shifts summary from the generic schema ── + shift_lines: list[str] = [] + + # FX + for pair, v in shifts.get("fx", {}).items(): + shift_lines.append(f" FX {pair}: {v:+.4f} (absolute)") + + # Equity + for key, v in shifts.get("equity", {}).items(): + shift_lines.append(f" Equity {key}: {v:+.1%}") + + # Rates + for ccy, tenors in shifts.get("rates", {}).items(): + parts = " / ".join(f"{t} {val*1e4:+.0f}bp" for t, val in tenors.items()) + shift_lines.append(f" Rates {ccy}: {parts}") + + # Credit + for key, tenors in shifts.get("credit", {}).items(): + parts = " / ".join(f"{t} {val*1e4:+.0f}bp" for t, val in tenors.items()) + shift_lines.append(f" Credit {key}: {parts}") + + shifts_text = "\n".join(shift_lines) if shift_lines else " (none)" + + prompt = f"""\ +Scenario description: {scenario_description} + +Applied market shifts: +{shifts_text} + +Portfolio results (all in EUR, base currency): + Base portfolio NPV: {summary['total_base_npv']:,.0f} + Stressed portfolio NPV: {summary['total_stressed_npv']:,.0f} + Total P&L impact: {summary['total_pnl']:+,.0f} + +Trade-level impacts (sorted by P&L): +""" + "\n".join( + f" {r['TradeId']:30s} Base: {r['Base NPV']:>14,.0f} " + f"Stressed: {r['Scenario NPV']:>14,.0f} P&L: {r['PnL']:>+14,.0f}" + for r in summary["trades"] + ) + f""" + +Top losers: {', '.join(r['TradeId'] for r in summary['top_losers'])} +Top gainers: {', '.join(r['TradeId'] for r in summary['top_gainers'])} + +Write an executive summary explaining which asset classes drove the result, +which trades were most affected, and what the overall risk interpretation is. +""" + + resp = client.chat.completions.create( + model=config.OPENAI_MODEL, + messages=[ + {"role": "system", "content": _NARRATIVE_SYSTEM}, + {"role": "user", "content": prompt}, + ], + temperature=0.4, + ) + return resp.choices[0].message.content or "" + + +# ── Main entry point ────────────────────────────────────────────────────────── + +def summarize( + csv_path: Path, + scenario_description: str, + shifts: dict[str, Any], + scenario_id: str = "agent_scenario", +) -> str: + """ + Parse *csv_path*, compute impacts, and return a Markdown report string. + + Parameters + ---------- + csv_path : path to stresstest.csv produced by ORE + scenario_description : original user text (used for narrative prompt) + shifts : the shifts dict from scenario_analyzer.analyze() + scenario_id : the StressTest id used when building the XML + + Returns + ------- + Markdown-formatted report string. + """ + df = _read_stresstest_csv(csv_path, scenario_id) + summary = _compute_summary(df) + table = _format_table(summary) + narrative = _llm_narrative(scenario_description, shifts, summary) + + header = ( + "═" * 60 + "\n" + " Portfolio Stress Test Impact Report\n" + "═" * 60 + ) + + pnl_sign = "+" if summary["total_pnl"] >= 0 else "" + pnl_label = f"TOTAL P&L: {pnl_sign}{summary['total_pnl']:,.0f} EUR" + direction = "GAIN" if summary["total_pnl"] >= 0 else "LOSS" + + report = ( + f"{header}\n\n" + f"**{pnl_label} [{direction}]**\n\n" + f"{table}\n\n" + "---\n\n" + "**Narrative Summary**\n\n" + f"{narrative}\n" + ) + return report diff --git a/EconomicStressAgentORE/ore_runner.py b/EconomicStressAgentORE/ore_runner.py new file mode 100644 index 0000000..6ed301d --- /dev/null +++ b/EconomicStressAgentORE/ore_runner.py @@ -0,0 +1,90 @@ +""" +ore_runner.py β€” Step 3: execute ORE with the agent-generated stress test config +and return the path to the output stresstest.csv. + +Uses the ORE Python bindings (``from ORE import *``). +ORE is logically "run from" the OREDir workspace directory so +that relative ``inputPath`` / ``outputPath`` in ore.xml resolve correctly. +""" + +from __future__ import annotations + +import os +import shutil +from pathlib import Path + +import config + + +# ── Public API ──────────────────────────────────────────────────────────────── + +def run( + ore_xml: Path | str | None = None, + workspace: Path | str | None = None, +) -> Path: + """ + Run ORE and return the path to the output stresstest.csv. + + Parameters + ---------- + ore_xml : path to the ORE main config file. + Defaults to ``config.AGENT_ORE_XML``. + workspace : directory from which ORE should be launched. + Defaults to ``config.ORE_WORKSPACE``. + + Returns + ------- + Path to ``Output/stresstest.csv`` inside the workspace. + """ + if ore_xml is None: + ore_xml = config.AGENT_ORE_XML + if workspace is None: + workspace = config.ORE_WORKSPACE + + ore_xml = Path(ore_xml) + workspace = Path(workspace) + + if not ore_xml.exists(): + raise FileNotFoundError(f"ORE config not found: {ore_xml}") + if not workspace.exists(): + raise FileNotFoundError(f"ORE workspace not found: {workspace}") + + # Path passed to ORE should be relative to the workspace (since ORE resolves + # Input/Output paths relative to its own working directory). + try: + ore_xml_rel = ore_xml.relative_to(workspace) + except ValueError: + ore_xml_rel = ore_xml # use absolute if not inside workspace + + # Clean the Output directory so stale results never mask a real failure. + output_dir = workspace / "Output" + if output_dir.exists(): + shutil.rmtree(output_dir) + output_dir.mkdir(parents=True, exist_ok=True) + + _run_via_python_api(ore_xml_rel, workspace) + + stdout_csv = workspace / "Output" / "stresstest.csv" + if not stdout_csv.exists(): + raise RuntimeError( + f"ORE run did not produce stresstest.csv at {stdout_csv}.\n" + "Check the ORE log at: " + str(workspace / "Output" / "log.txt") + ) + return stdout_csv + + +# ── Execution back-end ───────────────────────────────────────────────────────── + +def _run_via_python_api(ore_xml_rel: Path, workspace: Path) -> None: + """Execute ORE using the Python bindings (OREApp).""" + from ORE import OREApp, Parameters # type: ignore[import] + + orig_cwd = Path.cwd() + try: + os.chdir(workspace) + params = Parameters() + params.fromFile(str(ore_xml_rel)) + ore = OREApp(params, True) + ore.run() + finally: + os.chdir(orig_cwd) diff --git a/EconomicStressAgentORE/oredata/Input/conventions.xml b/EconomicStressAgentORE/oredata/Input/conventions.xml new file mode 100644 index 0000000..8134b78 --- /dev/null +++ b/EconomicStressAgentORE/oredata/Input/conventions.xml @@ -0,0 +1,126 @@ + + + CDS-STANDARD-CONVENTIONS + 0 + WeekendsOnly + Quarterly + Following + CDS2015 + A360 + true + true + + + EUR-DEPOSIT + true + EUR-EURIBOR + + + EUR-ON-DEPOSIT-ESTER + true + EUR-ESTER + + + USD-ON-SOFR-DEPOSIT + true + USD-SOFR + + + USD-SOFR-3M-FUTURES + USD-SOFR + + + EURIBOR-3M-FUTURES + EUR-EURIBOR-3M + + + EUR-6M-FRA + EUR-EURIBOR-6M + + + EUR-ESTER-OIS + 1 + EUR-ESTER + A360 + 1 + false + Annual + MF + MF + Backward + + + USD-SOFR-OIS + 0 + USD-SOFR + A360 + 2 + false + Annual + MF + MF + Backward + + + EUR-USD-ON-XCCY-BASIS + 2 + TARGET,US + MF + USD-SOFR + 3M + EUR-ESTER + 3M + true + true + + + EUR-USD-FX + 2 + EUR + USD + 10000 + TARGET,US + true + + + EUR-EURIBOR-3M-SWAP + TARGET + Annual + MF + 30/360 + EUR-EURIBOR-3M + + + EUR-EURIBOR-6M-SWAP + TARGET + Annual + MF + 30/360 + EUR-EURIBOR-6M + + + EURIBOR-3M-6M-BASIS + TARGET + Annual + MF + 30/360 + EUR-EURIBOR-6M + Annual + MF + 30/360 + EUR-EURIBOR-3M + true + + + EUHICPXT_INFLATIONSWAP + TARGET + MF + 30/360 + EUHICPXT + false + 3M + false + TARGET + MF + + \ No newline at end of file diff --git a/EconomicStressAgentORE/oredata/Input/curveconfig.xml b/EconomicStressAgentORE/oredata/Input/curveconfig.xml new file mode 100644 index 0000000..684d2eb --- /dev/null +++ b/EconomicStressAgentORE/oredata/Input/curveconfig.xml @@ -0,0 +1,582 @@ + + + + EUR + EUR normal cap floor volatilities + Normal + Flat + Bilinear + false + Actual/365 (Fixed) + TARGET + Following + 1Y, 2Y, 3Y, 4Y, 5Y, 6Y, 7Y, 8Y, 9Y + -0.01, 0, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.1 + false + EUR-EURIBOR-6M + Yield/EUR/EUR-ESTER + 1Y, 2Y, 3Y, 4Y, 5Y, 6Y, 7Y, 8Y, 9Y + 0 + OptionletVolatilities + LinearFlat + LinearFlat + false + + 0.0000000000010000 + 0.0000000000010000 + true + 5 + 2 + 2 + 10 + + TermVolatilities + + + + EUR-ESTER + ESTER vols proxied by EURIBOR-6M vols + + + CapFloorVolatility/EUR/EUR + EUR-EURIBOR-6M + + + EUR-ESTER + 3M + + + + + + + + EUR-ESTER + EUR ESTER discount curve bootstrapped from OIS swap rates + EUR + EUR-ESTER + + + Deposit + + MM/RATE/EUR/ESTER/0D/1D + + EUR-ON-DEPOSIT-ESTER + LastRelevantPillarDate + 0 + 1 + + + OIS + + IR_SWAP/RATE/EUR/ESTER/2D/1D/1W + IR_SWAP/RATE/EUR/ESTER/2D/1D/2W + IR_SWAP/RATE/EUR/ESTER/2D/1D/3W + IR_SWAP/RATE/EUR/ESTER/2D/1D/1M + IR_SWAP/RATE/EUR/ESTER/2D/1D/2M + IR_SWAP/RATE/EUR/ESTER/2D/1D/3M + IR_SWAP/RATE/EUR/ESTER/2D/1D/4M + IR_SWAP/RATE/EUR/ESTER/2D/1D/5M + IR_SWAP/RATE/EUR/ESTER/2D/1D/6M + IR_SWAP/RATE/EUR/ESTER/2D/1D/7M + IR_SWAP/RATE/EUR/ESTER/2D/1D/8M + IR_SWAP/RATE/EUR/ESTER/2D/1D/9M + IR_SWAP/RATE/EUR/ESTER/2D/1D/10M + IR_SWAP/RATE/EUR/ESTER/2D/1D/11M + IR_SWAP/RATE/EUR/ESTER/2D/1D/1Y + IR_SWAP/RATE/EUR/ESTER/2D/1D/15M + IR_SWAP/RATE/EUR/ESTER/2D/1D/18M + IR_SWAP/RATE/EUR/ESTER/2D/1D/21M + IR_SWAP/RATE/EUR/ESTER/2D/1D/2Y + IR_SWAP/RATE/EUR/ESTER/2D/1D/3Y + IR_SWAP/RATE/EUR/ESTER/2D/1D/4Y + IR_SWAP/RATE/EUR/ESTER/2D/1D/5Y + IR_SWAP/RATE/EUR/ESTER/2D/1D/6Y + IR_SWAP/RATE/EUR/ESTER/2D/1D/7Y + IR_SWAP/RATE/EUR/ESTER/2D/1D/8Y + IR_SWAP/RATE/EUR/ESTER/2D/1D/9Y + IR_SWAP/RATE/EUR/ESTER/2D/1D/10Y + IR_SWAP/RATE/EUR/ESTER/2D/1D/11Y + IR_SWAP/RATE/EUR/ESTER/2D/1D/12Y + IR_SWAP/RATE/EUR/ESTER/2D/1D/15Y + IR_SWAP/RATE/EUR/ESTER/2D/1D/20Y + IR_SWAP/RATE/EUR/ESTER/2D/1D/25Y + IR_SWAP/RATE/EUR/ESTER/2D/1D/30Y + IR_SWAP/RATE/EUR/ESTER/2D/1D/40Y + IR_SWAP/RATE/EUR/ESTER/2D/1D/50Y + IR_SWAP/RATE/EUR/ESTER/2D/1D/60Y + + EUR-ESTER-OIS + LastRelevantPillarDate + 0 + 1 + + + Discount + LogLinear + 1 + A365 + 0.0000000000010000 + true + + 0.0000000000010000 + 0.0000000000010000 + false + 5 + 2 + 2 + 10 + + + + USD-IN-EUR + USD collateralised in EUR discount curve + USD + + + + FX Forward + + FXFWD/RATE/EUR/USD/ON + FXFWD/RATE/EUR/USD/TN + FXFWD/RATE/EUR/USD/SN + FXFWD/RATE/EUR/USD/1W + FXFWD/RATE/EUR/USD/2W + FXFWD/RATE/EUR/USD/3W + FXFWD/RATE/EUR/USD/1M + FXFWD/RATE/EUR/USD/2M + FXFWD/RATE/EUR/USD/3M + FXFWD/RATE/EUR/USD/4M + FXFWD/RATE/EUR/USD/5M + FXFWD/RATE/EUR/USD/6M + FXFWD/RATE/EUR/USD/7M + FXFWD/RATE/EUR/USD/8M + FXFWD/RATE/EUR/USD/9M + FXFWD/RATE/EUR/USD/10M + FXFWD/RATE/EUR/USD/11M + FXFWD/RATE/EUR/USD/1Y + FXFWD/RATE/EUR/USD/15M + FXFWD/RATE/EUR/USD/18M + FXFWD/RATE/EUR/USD/21M + + EUR-USD-FX + LastRelevantPillarDate + 0 + 1 + + FX/RATE/EUR/USD + + + Cross Currency Basis Swap + + CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/2Y + CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/3Y + CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/4Y + CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/5Y + CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/6Y + CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/7Y + CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/8Y + CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/9Y + CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/10Y + CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/12Y + CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/15Y + CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/20Y + CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/25Y + CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/30Y + CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/40Y + CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/50Y + + EUR-USD-ON-XCCY-BASIS + LastRelevantPillarDate + 0 + 1 + + FX/RATE/EUR/USD + USD-SOFR + EUR-ESTER + + + Discount + LogLinear + 1 + A365 + 0.0000000000010000 + true + + 0.0000000000010000 + 0.0000000000010000 + false + 5 + 2 + 2 + 10 + + + + USD-SOFR + + USD + USD-SOFR + + + Deposit + + MM/RATE/USD/SOFR/0D/1D + + USD-ON-SOFR-DEPOSIT + LastRelevantPillarDate + 0 + 1 + USD-SOFR + + + Future + + OI_FUTURE/PRICE/USD/2024-04/XCME:SRA/3M + OI_FUTURE/PRICE/USD/2024-05/XCME:SRA/3M + OI_FUTURE/PRICE/USD/2024-06/XCME:SRA/3M + OI_FUTURE/PRICE/USD/2024-07/XCME:SRA/3M + OI_FUTURE/PRICE/USD/2024-08/XCME:SRA/3M + OI_FUTURE/PRICE/USD/2024-09/XCME:SRA/3M + OI_FUTURE/PRICE/USD/2024-10/XCME:SRA/3M + OI_FUTURE/PRICE/USD/2024-11/XCME:SRA/3M + OI_FUTURE/PRICE/USD/2024-12/XCME:SRA/3M + OI_FUTURE/PRICE/USD/2025-03/XCME:SRA/3M + OI_FUTURE/PRICE/USD/2025-06/XCME:SRA/3M + OI_FUTURE/PRICE/USD/2025-09/XCME:SRA/3M + + USD-SOFR-3M-FUTURES + LastRelevantPillarDate + 0 + 1 + + + OIS + + IR_SWAP/RATE/USD/SOFR/0D/1D/2Y + IR_SWAP/RATE/USD/SOFR/0D/1D/3Y + IR_SWAP/RATE/USD/SOFR/0D/1D/4Y + IR_SWAP/RATE/USD/SOFR/0D/1D/5Y + IR_SWAP/RATE/USD/SOFR/0D/1D/6Y + IR_SWAP/RATE/USD/SOFR/0D/1D/7Y + IR_SWAP/RATE/USD/SOFR/0D/1D/8Y + IR_SWAP/RATE/USD/SOFR/0D/1D/9Y + IR_SWAP/RATE/USD/SOFR/0D/1D/10Y + IR_SWAP/RATE/USD/SOFR/0D/1D/12Y + IR_SWAP/RATE/USD/SOFR/0D/1D/15Y + IR_SWAP/RATE/USD/SOFR/0D/1D/20Y + IR_SWAP/RATE/USD/SOFR/0D/1D/25Y + IR_SWAP/RATE/USD/SOFR/0D/1D/30Y + IR_SWAP/RATE/USD/SOFR/0D/1D/40Y + IR_SWAP/RATE/USD/SOFR/0D/1D/50Y + + USD-SOFR-OIS + LastRelevantPillarDate + 0 + 1 + + + Discount + LogLinear + 1 + A365 + 0.0000000000010000 + true + + 0.0000000000010000 + 0.0000000000010000 + false + 5 + 2 + 2 + 10 + + + + EUR-EURIBOR-3M + EUR EURIBOR 3M Index Curve + EUR + EUR-ESTER + + + Deposit + + MM/RATE/EUR/2D/3M + + EUR-DEPOSIT + LastRelevantPillarDate + 0 + 1 + + + Future + + MM_FUTURE/PRICE/EUR/2024-04/XICE:FEI/3M + MM_FUTURE/PRICE/EUR/2024-05/XICE:FEI/3M + MM_FUTURE/PRICE/EUR/2024-06/XICE:FEI/3M + MM_FUTURE/PRICE/EUR/2024-07/XICE:FEI/3M + MM_FUTURE/PRICE/EUR/2024-08/XICE:FEI/3M + MM_FUTURE/PRICE/EUR/2024-09/XICE:FEI/3M + MM_FUTURE/PRICE/EUR/2024-12/XICE:FEI/3M + MM_FUTURE/PRICE/EUR/2025-03/XICE:FEI/3M + MM_FUTURE/PRICE/EUR/2025-06/XICE:FEI/3M + MM_FUTURE/PRICE/EUR/2025-09/XICE:FEI/3M + + EURIBOR-3M-FUTURES + LastRelevantPillarDate + 0 + 1 + + + Swap + + IR_SWAP/RATE/EUR/2D/3M/2Y + IR_SWAP/RATE/EUR/2D/3M/3Y + IR_SWAP/RATE/EUR/2D/3M/4Y + IR_SWAP/RATE/EUR/2D/3M/5Y + IR_SWAP/RATE/EUR/2D/3M/6Y + IR_SWAP/RATE/EUR/2D/3M/7Y + IR_SWAP/RATE/EUR/2D/3M/8Y + IR_SWAP/RATE/EUR/2D/3M/9Y + IR_SWAP/RATE/EUR/2D/3M/10Y + IR_SWAP/RATE/EUR/2D/3M/12Y + IR_SWAP/RATE/EUR/2D/3M/15Y + IR_SWAP/RATE/EUR/2D/3M/20Y + IR_SWAP/RATE/EUR/2D/3M/25Y + IR_SWAP/RATE/EUR/2D/3M/30Y + IR_SWAP/RATE/EUR/2D/3M/40Y + IR_SWAP/RATE/EUR/2D/3M/50Y + IR_SWAP/RATE/EUR/2D/3M/60Y + + EUR-EURIBOR-3M-SWAP + LastRelevantPillarDate + 0 + 1 + EUR-EURIBOR-3M + + + Discount + LogLinear + 1 + A365 + 0.0000000000010000 + true + + 0.0000000000010000 + 0.0000000000010000 + false + 5 + 2 + 2 + 10 + + + + EUR-EURIBOR-6M + EUR EURIBOR 6M Index Curve + EUR + EUR-ESTER + + + Deposit + + MM/RATE/EUR/2D/6M + + EUR-DEPOSIT + LastRelevantPillarDate + 0 + 1 + + + FRA + + FRA/RATE/EUR/1M/6M + FRA/RATE/EUR/2M/6M + FRA/RATE/EUR/3M/6M + FRA/RATE/EUR/4M/6M + FRA/RATE/EUR/5M/6M + FRA/RATE/EUR/6M/6M + FRA/RATE/EUR/9M/6M + FRA/RATE/EUR/12M/6M + + EUR-6M-FRA + LastRelevantPillarDate + 0 + 1 + EUR-EURIBOR-6M + + + Tenor Basis Two Swaps + + BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/2Y + BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/3Y + BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/4Y + BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/5Y + BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/6Y + BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/7Y + BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/8Y + BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/9Y + BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/10Y + BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/12Y + BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/15Y + BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/20Y + BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/25Y + BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/30Y + BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/40Y + BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/50Y + BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/60Y + + EURIBOR-3M-6M-BASIS + LastRelevantPillarDate + 0 + 1 + EUR-EURIBOR-6M + EUR-EURIBOR-3M + + + Discount + LogLinear + 1 + A365 + 0.0000000000010000 + true + + 0.0000000000010000 + 0.0000000000010000 + false + 5 + 2 + 2 + 10 + + + + + + Underlying1 + + USD + + + SpreadCDS + Yield/USD/USD-SOFR + Actual/365 (Fixed) + RECOVERY_RATE/RATE/Underlying1/SNRFOR/USD + + CDS/CREDIT_SPREAD/Underlying1/SNRFOR/USD/* + + CDS-STANDARD-CONVENTIONS + true + 2018-09-20 + + 0.0000000000010000 + 0.0000000000010000 + true + 10 + 1 + 2 + 5 + + false + + + + + + + RIC:.SPX + Standard & Poor's Corp + USD + USD + USD-SOFR + ForwardPrice + EQUITY/PRICE/RIC:.SPX/USD + + EQUITY_FWD/PRICE/RIC:.SPX/USD/* + + A365 + + Zero + Linear + + false + false + + + RIC:.STOXX50 + STOXX Europe 50 EUR Price Index option implied forward curve + EUR + + EUR-ESTER + OptionPremium + European + EQUITY/PRICE/RIC:.STOXX50/EUR + + EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/* + + A365 + + Zero + Linear + + false + true + + + + + EUHICPXT_ZC_Swaps + Estimation Curve for EUHICPXT + Yield/EUR/EUR-ESTER + ZC + + ZC_INFLATIONSWAP/RATE/EUHICPXT/1Y + ZC_INFLATIONSWAP/RATE/EUHICPXT/2Y + ZC_INFLATIONSWAP/RATE/EUHICPXT/3Y + ZC_INFLATIONSWAP/RATE/EUHICPXT/4Y + ZC_INFLATIONSWAP/RATE/EUHICPXT/5Y + ZC_INFLATIONSWAP/RATE/EUHICPXT/6Y + ZC_INFLATIONSWAP/RATE/EUHICPXT/7Y + ZC_INFLATIONSWAP/RATE/EUHICPXT/8Y + ZC_INFLATIONSWAP/RATE/EUHICPXT/9Y + ZC_INFLATIONSWAP/RATE/EUHICPXT/10Y + ZC_INFLATIONSWAP/RATE/EUHICPXT/12Y + ZC_INFLATIONSWAP/RATE/EUHICPXT/15Y + ZC_INFLATIONSWAP/RATE/EUHICPXT/20Y + ZC_INFLATIONSWAP/RATE/EUHICPXT/25Y + ZC_INFLATIONSWAP/RATE/EUHICPXT/30Y + ZC_INFLATIONSWAP/RATE/EUHICPXT/35Y + ZC_INFLATIONSWAP/RATE/EUHICPXT/40Y + ZC_INFLATIONSWAP/RATE/EUHICPXT/45Y + ZC_INFLATIONSWAP/RATE/EUHICPXT/50Y + + EUHICPXT_INFLATIONSWAP + true + TARGET + Actual/365 (Fixed) + 3M + Monthly + + 0.0000000000010000 + true + + 2008-01-01 + Monthly + + SEASONALITY/RATE/MULT/EUHICPXT/JAN + SEASONALITY/RATE/MULT/EUHICPXT/FEB + SEASONALITY/RATE/MULT/EUHICPXT/MAR + SEASONALITY/RATE/MULT/EUHICPXT/APR + SEASONALITY/RATE/MULT/EUHICPXT/MAY + SEASONALITY/RATE/MULT/EUHICPXT/JUN + SEASONALITY/RATE/MULT/EUHICPXT/JUL + SEASONALITY/RATE/MULT/EUHICPXT/AUG + SEASONALITY/RATE/MULT/EUHICPXT/SEP + SEASONALITY/RATE/MULT/EUHICPXT/OCT + SEASONALITY/RATE/MULT/EUHICPXT/NOV + SEASONALITY/RATE/MULT/EUHICPXT/DEC + + + + + \ No newline at end of file diff --git a/EconomicStressAgentORE/oredata/Input/fixings.csv b/EconomicStressAgentORE/oredata/Input/fixings.csv new file mode 100644 index 0000000..f5b4c20 --- /dev/null +++ b/EconomicStressAgentORE/oredata/Input/fixings.csv @@ -0,0 +1,416 @@ +#fixingDate,fixingId,fixingValue +2023-11-06,EUR-EONIA,0.0398700000 +2023-11-07,EUR-EONIA,0.0398500000 +2023-11-08,EUR-EONIA,0.0398700000 +2023-11-09,EUR-EONIA,0.0398700000 +2023-11-10,EUR-EONIA,0.0398700000 +2023-11-13,EUR-EONIA,0.0398900000 +2023-11-14,EUR-EONIA,0.0398800000 +2023-11-15,EUR-EONIA,0.0398700000 +2023-11-16,EUR-EONIA,0.0398400000 +2023-11-17,EUR-EONIA,0.0399100000 +2023-11-20,EUR-EONIA,0.0398700000 +2023-11-21,EUR-EONIA,0.0398800000 +2023-11-22,EUR-EONIA,0.0398600000 +2023-11-23,EUR-EONIA,0.0398700000 +2023-11-24,EUR-EONIA,0.0398700000 +2023-11-27,EUR-EONIA,0.0398900000 +2023-11-28,EUR-EONIA,0.0398800000 +2023-11-29,EUR-EONIA,0.0398800000 +2023-11-30,EUR-EONIA,0.0398700000 +2023-12-01,EUR-EONIA,0.0397600000 +2023-12-04,EUR-EONIA,0.0397600000 +2023-12-05,EUR-EONIA,0.0398900000 +2023-12-06,EUR-EONIA,0.0399000000 +2023-12-07,EUR-EONIA,0.0398900000 +2023-12-08,EUR-EONIA,0.0398800000 +2023-12-11,EUR-EONIA,0.0398800000 +2023-12-12,EUR-EONIA,0.0398700000 +2023-12-13,EUR-EONIA,0.0399000000 +2023-12-14,EUR-EONIA,0.0399000000 +2023-12-15,EUR-EONIA,0.0399200000 +2023-12-18,EUR-EONIA,0.0398900000 +2023-12-19,EUR-EONIA,0.0398600000 +2023-12-20,EUR-EONIA,0.0398600000 +2023-12-21,EUR-EONIA,0.0398600000 +2023-12-22,EUR-EONIA,0.0398400000 +2023-12-25,EUR-EONIA,0.0398400000 +2023-12-26,EUR-EONIA,0.0398400000 +2023-12-27,EUR-EONIA,0.0398400000 +2023-12-28,EUR-EONIA,0.0398500000 +2023-12-29,EUR-EONIA,0.0398500000 +2024-01-01,EUR-EONIA,0.0398500000 +2024-01-02,EUR-EONIA,0.0396700000 +2024-01-03,EUR-EONIA,0.0399100000 +2024-01-04,EUR-EONIA,0.0398900000 +2024-01-05,EUR-EONIA,0.0398700000 +2024-01-08,EUR-EONIA,0.0399000000 +2024-01-09,EUR-EONIA,0.0398900000 +2024-01-10,EUR-EONIA,0.0398900000 +2024-01-11,EUR-EONIA,0.0399100000 +2024-01-12,EUR-EONIA,0.0399000000 +2024-01-15,EUR-EONIA,0.0398900000 +2024-01-16,EUR-EONIA,0.0398700000 +2024-01-17,EUR-EONIA,0.0399000000 +2024-01-18,EUR-EONIA,0.0398900000 +2024-01-19,EUR-EONIA,0.0399200000 +2024-01-22,EUR-EONIA,0.0398900000 +2024-01-23,EUR-EONIA,0.0398700000 +2024-01-24,EUR-EONIA,0.0398700000 +2024-01-25,EUR-EONIA,0.0399000000 +2024-01-26,EUR-EONIA,0.0399000000 +2024-01-29,EUR-EONIA,0.0398900000 +2024-01-30,EUR-EONIA,0.0399200000 +2024-01-31,EUR-EONIA,0.0399200000 +2024-02-01,EUR-EONIA,0.0397900000 +2024-02-02,EUR-EONIA,0.0399000000 +2024-02-05,EUR-EONIA,0.0399100000 +2024-02-06,EUR-EONIA,0.0399200000 +2024-02-07,EUR-EONIA,0.0399300000 +2024-02-08,EUR-EONIA,0.0399300000 +2024-02-09,EUR-EONIA,0.0399300000 +2024-02-12,EUR-EONIA,0.0399500000 +2024-02-13,EUR-EONIA,0.0399400000 +2024-02-14,EUR-EONIA,0.0399400000 +2024-02-15,EUR-EONIA,0.0399600000 +2024-02-16,EUR-EONIA,0.0399600000 +2024-02-19,EUR-EONIA,0.0399400000 +2024-02-20,EUR-EONIA,0.0399500000 +2024-02-21,EUR-EONIA,0.0399600000 +2024-02-22,EUR-EONIA,0.0399500000 +2024-02-23,EUR-EONIA,0.0399400000 +2024-02-26,EUR-EONIA,0.0399200000 +2024-02-27,EUR-EONIA,0.0398900000 +2024-02-28,EUR-EONIA,0.0399000000 +2024-02-29,EUR-EONIA,0.0399100000 +2024-03-01,EUR-EONIA,0.0397200000 +2024-03-04,EUR-EONIA,0.0398900000 +2024-03-05,EUR-EONIA,0.0398800000 +2023-09-28,EUR-ESTER,0.0390400000 +2023-09-29,EUR-ESTER,0.0390500000 +2023-10-02,EUR-ESTER,0.0390600000 +2023-10-03,EUR-ESTER,0.0388000000 +2023-10-04,EUR-ESTER,0.0389900000 +2023-10-05,EUR-ESTER,0.0390000000 +2023-10-06,EUR-ESTER,0.0390100000 +2023-10-09,EUR-ESTER,0.0390000000 +2023-10-10,EUR-ESTER,0.0389900000 +2023-10-11,EUR-ESTER,0.0390000000 +2023-10-12,EUR-ESTER,0.0389900000 +2023-10-13,EUR-ESTER,0.0390100000 +2023-10-16,EUR-ESTER,0.0390100000 +2023-10-17,EUR-ESTER,0.0390300000 +2023-10-18,EUR-ESTER,0.0390200000 +2023-10-19,EUR-ESTER,0.0390400000 +2023-10-20,EUR-ESTER,0.0390300000 +2023-10-23,EUR-ESTER,0.0390500000 +2023-10-24,EUR-ESTER,0.0390500000 +2023-10-25,EUR-ESTER,0.0390500000 +2023-10-26,EUR-ESTER,0.0390400000 +2023-10-27,EUR-ESTER,0.0390000000 +2023-10-30,EUR-ESTER,0.0390100000 +2023-10-31,EUR-ESTER,0.0390300000 +2023-11-01,EUR-ESTER,0.0389900000 +2023-11-02,EUR-ESTER,0.0388200000 +2023-11-03,EUR-ESTER,0.0390000000 +2023-11-06,EUR-ESTER,0.0390300000 +2023-11-07,EUR-ESTER,0.0390200000 +2023-11-08,EUR-ESTER,0.0390000000 +2023-11-09,EUR-ESTER,0.0390200000 +2023-11-10,EUR-ESTER,0.0390200000 +2023-11-13,EUR-ESTER,0.0390200000 +2023-11-14,EUR-ESTER,0.0390400000 +2023-11-15,EUR-ESTER,0.0390300000 +2023-11-16,EUR-ESTER,0.0390200000 +2023-11-17,EUR-ESTER,0.0389900000 +2023-11-20,EUR-ESTER,0.0390600000 +2023-11-21,EUR-ESTER,0.0390200000 +2023-11-22,EUR-ESTER,0.0390300000 +2023-11-23,EUR-ESTER,0.0390100000 +2023-11-24,EUR-ESTER,0.0390200000 +2023-11-27,EUR-ESTER,0.0390200000 +2023-11-28,EUR-ESTER,0.0390400000 +2023-11-29,EUR-ESTER,0.0390300000 +2023-11-30,EUR-ESTER,0.0390300000 +2023-12-01,EUR-ESTER,0.0390200000 +2023-12-04,EUR-ESTER,0.0389100000 +2023-12-05,EUR-ESTER,0.0389100000 +2023-12-06,EUR-ESTER,0.0390400000 +2023-12-07,EUR-ESTER,0.0390500000 +2023-12-08,EUR-ESTER,0.0390400000 +2023-12-11,EUR-ESTER,0.0390300000 +2023-12-12,EUR-ESTER,0.0390300000 +2023-12-13,EUR-ESTER,0.0390200000 +2023-12-14,EUR-ESTER,0.0390500000 +2023-12-15,EUR-ESTER,0.0390500000 +2023-12-18,EUR-ESTER,0.0390700000 +2023-12-19,EUR-ESTER,0.0390400000 +2023-12-20,EUR-ESTER,0.0390100000 +2023-12-21,EUR-ESTER,0.0390100000 +2023-12-22,EUR-ESTER,0.0389900000 +2023-12-25,EUR-ESTER,0.0389900000 +2023-12-26,EUR-ESTER,0.0389900000 +2023-12-27,EUR-ESTER,0.0389900000 +2023-12-28,EUR-ESTER,0.0389900000 +2023-12-29,EUR-ESTER,0.0390000000 +2024-01-01,EUR-ESTER,0.0390000000 +2024-01-02,EUR-ESTER,0.0390000000 +2024-01-03,EUR-ESTER,0.0388200000 +2024-01-04,EUR-ESTER,0.0390600000 +2024-01-05,EUR-ESTER,0.0390400000 +2024-01-08,EUR-ESTER,0.0390200000 +2024-01-09,EUR-ESTER,0.0390500000 +2024-01-10,EUR-ESTER,0.0390400000 +2024-01-11,EUR-ESTER,0.0390400000 +2024-01-12,EUR-ESTER,0.0390600000 +2024-01-15,EUR-ESTER,0.0390500000 +2024-01-16,EUR-ESTER,0.0390400000 +2024-01-17,EUR-ESTER,0.0390200000 +2024-01-18,EUR-ESTER,0.0390500000 +2024-01-19,EUR-ESTER,0.0390400000 +2024-01-22,EUR-ESTER,0.0390700000 +2024-01-23,EUR-ESTER,0.0390400000 +2024-01-24,EUR-ESTER,0.0390200000 +2024-01-25,EUR-ESTER,0.0390200000 +2024-01-26,EUR-ESTER,0.0390500000 +2024-01-29,EUR-ESTER,0.0390500000 +2024-01-30,EUR-ESTER,0.0390400000 +2024-01-31,EUR-ESTER,0.0390700000 +2024-02-01,EUR-ESTER,0.0390700000 +2024-02-02,EUR-ESTER,0.0389400000 +2024-02-05,EUR-ESTER,0.0390500000 +2024-02-06,EUR-ESTER,0.0390600000 +2024-02-07,EUR-ESTER,0.0390700000 +2024-02-08,EUR-ESTER,0.0390800000 +2024-02-09,EUR-ESTER,0.0390800000 +2024-02-12,EUR-ESTER,0.0390800000 +2024-02-13,EUR-ESTER,0.0391000000 +2024-02-14,EUR-ESTER,0.0390900000 +2024-02-15,EUR-ESTER,0.0390900000 +2024-02-16,EUR-ESTER,0.0391100000 +2024-02-19,EUR-ESTER,0.0391100000 +2024-02-20,EUR-ESTER,0.0390900000 +2024-02-21,EUR-ESTER,0.0391000000 +2024-02-22,EUR-ESTER,0.0391100000 +2024-02-23,EUR-ESTER,0.0391000000 +2024-02-26,EUR-ESTER,0.0390900000 +2024-02-27,EUR-ESTER,0.0390700000 +2024-02-28,EUR-ESTER,0.0390400000 +2024-02-29,EUR-ESTER,0.0390500000 +2024-03-01,EUR-ESTER,0.0390600000 +2024-03-04,EUR-ESTER,0.0388700000 +2024-03-05,EUR-ESTER,0.0390400000 +2023-11-06,USD-FedFunds,0.0533000000 +2023-11-07,USD-FedFunds,0.0533000000 +2023-11-08,USD-FedFunds,0.0533000000 +2023-11-09,USD-FedFunds,0.0533000000 +2023-11-10,USD-FedFunds,0.0533000000 +2023-11-13,USD-FedFunds,0.0533000000 +2023-11-14,USD-FedFunds,0.0533000000 +2023-11-15,USD-FedFunds,0.0533000000 +2023-11-16,USD-FedFunds,0.0533000000 +2023-11-17,USD-FedFunds,0.0533000000 +2023-11-20,USD-FedFunds,0.0533000000 +2023-11-21,USD-FedFunds,0.0533000000 +2023-11-22,USD-FedFunds,0.0533000000 +2023-11-23,USD-FedFunds,0.0533000000 +2023-11-24,USD-FedFunds,0.0533000000 +2023-11-27,USD-FedFunds,0.0533000000 +2023-11-28,USD-FedFunds,0.0533000000 +2023-11-29,USD-FedFunds,0.0533000000 +2023-11-30,USD-FedFunds,0.0533000000 +2023-12-01,USD-FedFunds,0.0533000000 +2023-12-04,USD-FedFunds,0.0533000000 +2023-12-05,USD-FedFunds,0.0533000000 +2023-12-06,USD-FedFunds,0.0533000000 +2023-12-07,USD-FedFunds,0.0533000000 +2023-12-08,USD-FedFunds,0.0533000000 +2023-12-11,USD-FedFunds,0.0533000000 +2023-12-12,USD-FedFunds,0.0533000000 +2023-12-13,USD-FedFunds,0.0533000000 +2023-12-14,USD-FedFunds,0.0533000000 +2023-12-15,USD-FedFunds,0.0533000000 +2023-12-18,USD-FedFunds,0.0533000000 +2023-12-19,USD-FedFunds,0.0533000000 +2023-12-20,USD-FedFunds,0.0533000000 +2023-12-21,USD-FedFunds,0.0533000000 +2023-12-22,USD-FedFunds,0.0533000000 +2023-12-25,USD-FedFunds,0.0533000000 +2023-12-26,USD-FedFunds,0.0533000000 +2023-12-27,USD-FedFunds,0.0533000000 +2023-12-28,USD-FedFunds,0.0533000000 +2023-12-29,USD-FedFunds,0.0533000000 +2024-01-01,USD-FedFunds,0.0533000000 +2024-01-02,USD-FedFunds,0.0533000000 +2024-01-03,USD-FedFunds,0.0533000000 +2024-01-04,USD-FedFunds,0.0533000000 +2024-01-05,USD-FedFunds,0.0533000000 +2024-01-08,USD-FedFunds,0.0533000000 +2024-01-09,USD-FedFunds,0.0533000000 +2024-01-10,USD-FedFunds,0.0533000000 +2024-01-11,USD-FedFunds,0.0533000000 +2024-01-12,USD-FedFunds,0.0533000000 +2024-01-15,USD-FedFunds,0.0533000000 +2024-01-16,USD-FedFunds,0.0533000000 +2024-01-17,USD-FedFunds,0.0533000000 +2024-01-18,USD-FedFunds,0.0533000000 +2024-01-19,USD-FedFunds,0.0533000000 +2024-01-22,USD-FedFunds,0.0533000000 +2024-01-23,USD-FedFunds,0.0533000000 +2024-01-24,USD-FedFunds,0.0533000000 +2024-01-25,USD-FedFunds,0.0533000000 +2024-01-26,USD-FedFunds,0.0533000000 +2024-01-29,USD-FedFunds,0.0533000000 +2024-01-30,USD-FedFunds,0.0533000000 +2024-01-31,USD-FedFunds,0.0533000000 +2024-02-01,USD-FedFunds,0.0533000000 +2024-02-02,USD-FedFunds,0.0533000000 +2024-02-05,USD-FedFunds,0.0533000000 +2024-02-06,USD-FedFunds,0.0533000000 +2024-02-07,USD-FedFunds,0.0533000000 +2024-02-08,USD-FedFunds,0.0533000000 +2024-02-09,USD-FedFunds,0.0533000000 +2024-02-12,USD-FedFunds,0.0533000000 +2024-02-13,USD-FedFunds,0.0533000000 +2024-02-14,USD-FedFunds,0.0533000000 +2024-02-15,USD-FedFunds,0.0533000000 +2024-02-16,USD-FedFunds,0.0533000000 +2024-02-19,USD-FedFunds,0.0533000000 +2024-02-20,USD-FedFunds,0.0533000000 +2024-02-21,USD-FedFunds,0.0533000000 +2024-02-22,USD-FedFunds,0.0533000000 +2024-02-23,USD-FedFunds,0.0533000000 +2024-02-26,USD-FedFunds,0.0533000000 +2024-02-27,USD-FedFunds,0.0533000000 +2024-02-28,USD-FedFunds,0.0533000000 +2024-02-29,USD-FedFunds,0.0533000000 +2024-03-01,USD-FedFunds,0.0533000000 +2024-03-04,USD-FedFunds,0.0533000000 +2024-03-05,USD-FedFunds,0.0533000000 +2023-09-28,USD-SOFR,0.0531000000 +2023-09-29,USD-SOFR,0.0531000000 +2023-10-02,USD-SOFR,0.0532000000 +2023-10-03,USD-SOFR,0.0533000000 +2023-10-04,USD-SOFR,0.0532000000 +2023-10-05,USD-SOFR,0.0532000000 +2023-10-06,USD-SOFR,0.0532000000 +2023-10-10,USD-SOFR,0.0532000000 +2023-10-11,USD-SOFR,0.0532000000 +2023-10-12,USD-SOFR,0.0532000000 +2023-10-13,USD-SOFR,0.0532000000 +2023-10-16,USD-SOFR,0.0532000000 +2023-10-17,USD-SOFR,0.0532000000 +2023-10-18,USD-SOFR,0.0532000000 +2023-10-19,USD-SOFR,0.0532000000 +2023-10-20,USD-SOFR,0.0530000000 +2023-10-23,USD-SOFR,0.0530000000 +2023-10-24,USD-SOFR,0.0530000000 +2023-10-25,USD-SOFR,0.0530000000 +2023-10-26,USD-SOFR,0.0531000000 +2023-10-27,USD-SOFR,0.0531000000 +2023-10-30,USD-SOFR,0.0531000000 +2023-10-31,USD-SOFR,0.0535000000 +2023-11-01,USD-SOFR,0.0532000000 +2023-11-02,USD-SOFR,0.0533000000 +2023-11-03,USD-SOFR,0.0532000000 +2023-11-06,USD-SOFR,0.0532000000 +2023-11-07,USD-SOFR,0.0532000000 +2023-11-08,USD-SOFR,0.0532000000 +2023-11-09,USD-SOFR,0.0532000000 +2023-11-10,USD-SOFR,0.0532000000 +2023-11-13,USD-SOFR,0.0532000000 +2023-11-14,USD-SOFR,0.0532000000 +2023-11-15,USD-SOFR,0.0532000000 +2023-11-16,USD-SOFR,0.0532000000 +2023-11-17,USD-SOFR,0.0532000000 +2023-11-20,USD-SOFR,0.0531000000 +2023-11-21,USD-SOFR,0.0531000000 +2023-11-22,USD-SOFR,0.0531000000 +2023-11-23,USD-SOFR,0.0531000000 +2023-11-24,USD-SOFR,0.0532000000 +2023-11-27,USD-SOFR,0.0532000000 +2023-11-28,USD-SOFR,0.0532000000 +2023-11-29,USD-SOFR,0.0531000000 +2023-11-30,USD-SOFR,0.0533000000 +2023-12-01,USD-SOFR,0.0539000000 +2023-12-04,USD-SOFR,0.0537000000 +2023-12-05,USD-SOFR,0.0533000000 +2023-12-06,USD-SOFR,0.0532000000 +2023-12-07,USD-SOFR,0.0532000000 +2023-12-08,USD-SOFR,0.0532000000 +2023-12-11,USD-SOFR,0.0532000000 +2023-12-12,USD-SOFR,0.0531000000 +2023-12-13,USD-SOFR,0.0531000000 +2023-12-14,USD-SOFR,0.0531000000 +2023-12-15,USD-SOFR,0.0532000000 +2023-12-18,USD-SOFR,0.0532000000 +2023-12-19,USD-SOFR,0.0531000000 +2023-12-20,USD-SOFR,0.0531000000 +2023-12-21,USD-SOFR,0.0531000000 +2023-12-22,USD-SOFR,0.0532000000 +2023-12-25,USD-SOFR,0.0532000000 +2023-12-26,USD-SOFR,0.0535000000 +2023-12-27,USD-SOFR,0.0539000000 +2023-12-28,USD-SOFR,0.0540000000 +2023-12-29,USD-SOFR,0.0538000000 +2024-01-01,USD-SOFR,0.0538000000 +2024-01-02,USD-SOFR,0.0540000000 +2024-01-03,USD-SOFR,0.0539000000 +2024-01-04,USD-SOFR,0.0532000000 +2024-01-05,USD-SOFR,0.0531000000 +2024-01-08,USD-SOFR,0.0531000000 +2024-01-09,USD-SOFR,0.0531000000 +2024-01-10,USD-SOFR,0.0531000000 +2024-01-11,USD-SOFR,0.0531000000 +2024-01-12,USD-SOFR,0.0531000000 +2024-01-15,USD-SOFR,0.0531000000 +2024-01-16,USD-SOFR,0.0532000000 +2024-01-17,USD-SOFR,0.0532000000 +2024-01-18,USD-SOFR,0.0531000000 +2024-01-19,USD-SOFR,0.0531000000 +2024-01-22,USD-SOFR,0.0531000000 +2024-01-23,USD-SOFR,0.0531000000 +2024-01-24,USD-SOFR,0.0531000000 +2024-01-25,USD-SOFR,0.0532000000 +2024-01-26,USD-SOFR,0.0532000000 +2024-01-29,USD-SOFR,0.0531000000 +2024-01-30,USD-SOFR,0.0531000000 +2024-01-31,USD-SOFR,0.0532000000 +2024-02-01,USD-SOFR,0.0532000000 +2024-02-02,USD-SOFR,0.0532000000 +2024-02-05,USD-SOFR,0.0531000000 +2024-02-06,USD-SOFR,0.0531000000 +2024-02-07,USD-SOFR,0.0531000000 +2024-02-08,USD-SOFR,0.0531000000 +2024-02-09,USD-SOFR,0.0531000000 +2024-02-12,USD-SOFR,0.0531000000 +2024-02-13,USD-SOFR,0.0531000000 +2024-02-14,USD-SOFR,0.0530000000 +2024-02-15,USD-SOFR,0.0531000000 +2024-02-16,USD-SOFR,0.0530000000 +2024-02-19,USD-SOFR,0.0530000000 +2024-02-20,USD-SOFR,0.0530000000 +2024-02-21,USD-SOFR,0.0530000000 +2024-02-22,USD-SOFR,0.0530000000 +2024-02-23,USD-SOFR,0.0531000000 +2024-02-26,USD-SOFR,0.0531000000 +2024-02-27,USD-SOFR,0.0531000000 +2024-02-28,USD-SOFR,0.0531000000 +2024-02-29,USD-SOFR,0.0532000000 +2024-03-01,USD-SOFR,0.0531000000 +2024-03-04,USD-SOFR,0.0531000000 +2024-03-05,USD-SOFR,0.0531000000 +2024-02-27,EUR-EURIBOR-3M,0.0395200000 +2024-02-28,EUR-EURIBOR-3M,0.0394200000 +2024-02-29,EUR-EURIBOR-3M,0.0393700000 +2024-03-01,EUR-EURIBOR-3M,0.0393800000 +2024-03-04,EUR-EURIBOR-3M,0.0393200000 +2024-03-05,EUR-EURIBOR-3M,0.0392600000 +2024-02-27,EUR-EURIBOR-6M,0.0391800000 +2024-02-28,EUR-EURIBOR-6M,0.0391500000 +2024-02-29,EUR-EURIBOR-6M,0.0390800000 +2024-03-01,EUR-EURIBOR-6M,0.0391200000 +2024-03-04,EUR-EURIBOR-6M,0.0391500000 +2024-03-05,EUR-EURIBOR-6M,0.0392100000 +2023-12-01,EUHICPXT,100. +2024-01-01,EUHICPXT,100.25 \ No newline at end of file diff --git a/EconomicStressAgentORE/oredata/Input/marketdata.csv b/EconomicStressAgentORE/oredata/Input/marketdata.csv new file mode 100644 index 0000000..6303345 --- /dev/null +++ b/EconomicStressAgentORE/oredata/Input/marketdata.csv @@ -0,0 +1,859 @@ +#datumDate,datumId, datumValue +2024-03-05,CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/10Y,-0.0018 +2024-03-05,CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/12Y,-0.0018 +2024-03-05,CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/15Y,-0.0019 +2024-03-05,CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/20Y,-0.0018 +2024-03-05,CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/25Y,-0.0014 +2024-03-05,CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/2Y,-0.0012 +2024-03-05,CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/30Y,-0.0011 +2024-03-05,CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/3Y,-0.0013 +2024-03-05,CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/40Y,-0.0006 +2024-03-05,CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/4Y,-0.0014 +2024-03-05,CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/50Y,-0.0001 +2024-03-05,CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/5Y,-0.0015 +2024-03-05,CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/6Y,-0.0015 +2024-03-05,CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/7Y,-0.0016 +2024-03-05,CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/8Y,-0.0017 +2024-03-05,CC_BASIS_SWAP/BASIS_SPREAD/USD/1D/EUR/1D/9Y,-0.0017 +2024-03-05,FX/RATE/EUR/USD,1.08 +2024-03-05,FXFWD/RATE/EUR/USD/10M,147.32 +2024-03-05,FXFWD/RATE/EUR/USD/11M,162.45 +2024-03-05,FXFWD/RATE/EUR/USD/15M,222.64 +2024-03-05,FXFWD/RATE/EUR/USD/18M,265.55 +2024-03-05,FXFWD/RATE/EUR/USD/1M,14.05 +2024-03-05,FXFWD/RATE/EUR/USD/1W,2.94 +2024-03-05,FXFWD/RATE/EUR/USD/1Y,176.2 +2024-03-05,FXFWD/RATE/EUR/USD/21M,308.54 +2024-03-05,FXFWD/RATE/EUR/USD/2M,27.01 +2024-03-05,FXFWD/RATE/EUR/USD/2W,5.89 +2024-03-05,FXFWD/RATE/EUR/USD/3M,40.74 +2024-03-05,FXFWD/RATE/EUR/USD/3W,8.86 +2024-03-05,FXFWD/RATE/EUR/USD/4M,55.18 +2024-03-05,FXFWD/RATE/EUR/USD/5M,69.2 +2024-03-05,FXFWD/RATE/EUR/USD/6M,84.4 +2024-03-05,FXFWD/RATE/EUR/USD/7M,98.35 +2024-03-05,FXFWD/RATE/EUR/USD/8M,113.45 +2024-03-05,FXFWD/RATE/EUR/USD/9M,129.05 +2024-03-05,FXFWD/RATE/EUR/USD/ON,0.43 +2024-03-05,FXFWD/RATE/EUR/USD/SN,0.42 +2024-03-05,FXFWD/RATE/EUR/USD/TN,0.42 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/10M,0.036 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/10Y,0.025 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/11M,0.035 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/11Y,0.025 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/12Y,0.025 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/15M,0.033 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/15Y,0.026 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/18M,0.032 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/1M,0.039 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/1W,0.039 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/1Y,0.035 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/20Y,0.025 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/21M,0.031 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/25Y,0.025 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/2M,0.039 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/2W,0.039 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/2Y,0.03 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/30Y,0.024 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/3M,0.039 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/3W,0.039 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/3Y,0.027 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/40Y,0.022 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/4M,0.039 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/4Y,0.026 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/50Y,0.021 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/5M,0.038 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/5Y,0.025 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/60Y,0.022 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/6M,0.038 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/6Y,0.025 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/7M,0.038 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/7Y,0.025 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/8M,0.037 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/8Y,0.025 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/9M,0.036 +2024-03-05,IR_SWAP/RATE/EUR/ESTER/2D/1D/9Y,0.025 +2024-03-05,IR_SWAP/RATE/USD/2D/1D/1M,0.053 +2024-03-05,IR_SWAP/RATE/USD/2D/1D/2M,0.053 +2024-03-05,IR_SWAP/RATE/USD/2D/1D/2W,0.053 +2024-03-05,IR_SWAP/RATE/USD/2D/1D/3M,0.053 +2024-03-05,IR_SWAP/RATE/USD/2D/1D/4M,0.053 +2024-03-05,IR_SWAP/RATE/USD/2D/1D/5M,0.053 +2024-03-05,IR_SWAP/RATE/USD/SOFR/0D/1D/10Y,0.038 +2024-03-05,IR_SWAP/RATE/USD/SOFR/0D/1D/12Y,0.038 +2024-03-05,IR_SWAP/RATE/USD/SOFR/0D/1D/15Y,0.038 +2024-03-05,IR_SWAP/RATE/USD/SOFR/0D/1D/20Y,0.038 +2024-03-05,IR_SWAP/RATE/USD/SOFR/0D/1D/25Y,0.037 +2024-03-05,IR_SWAP/RATE/USD/SOFR/0D/1D/2Y,0.045 +2024-03-05,IR_SWAP/RATE/USD/SOFR/0D/1D/30Y,0.036 +2024-03-05,IR_SWAP/RATE/USD/SOFR/0D/1D/3Y,0.042 +2024-03-05,IR_SWAP/RATE/USD/SOFR/0D/1D/40Y,0.034 +2024-03-05,IR_SWAP/RATE/USD/SOFR/0D/1D/4Y,0.04 +2024-03-05,IR_SWAP/RATE/USD/SOFR/0D/1D/50Y,0.032 +2024-03-05,IR_SWAP/RATE/USD/SOFR/0D/1D/5Y,0.039 +2024-03-05,IR_SWAP/RATE/USD/SOFR/0D/1D/6Y,0.039 +2024-03-05,IR_SWAP/RATE/USD/SOFR/0D/1D/7Y,0.038 +2024-03-05,IR_SWAP/RATE/USD/SOFR/0D/1D/8Y,0.038 +2024-03-05,IR_SWAP/RATE/USD/SOFR/0D/1D/9Y,0.038 +2024-03-05,MM/RATE/EUR/ESTER/0D/1D,0.039 +2024-03-05,MM/RATE/USD/0D/1D,0.054 +2024-03-05,MM/RATE/USD/SOFR/0D/1D,0.053 +2024-03-05,OI_FUTURE/PRICE/USD/2024-04/XCME:SRA/3M,95.23 +2024-03-05,OI_FUTURE/PRICE/USD/2024-05/XCME:SRA/3M,93.86 +2024-03-05,OI_FUTURE/PRICE/USD/2024-06/XCME:SRA/3M,95.33 +2024-03-05,OI_FUTURE/PRICE/USD/2024-07/XCME:SRA/3M,93.94 +2024-03-05,OI_FUTURE/PRICE/USD/2024-08/XCME:SRA/3M,94.56 +2024-03-05,OI_FUTURE/PRICE/USD/2024-09/XCME:SRA/3M,95.6 +2024-03-05,OI_FUTURE/PRICE/USD/2024-10/XCME:SRA/3M,94.58 +2024-03-05,OI_FUTURE/PRICE/USD/2024-11/XCME:SRA/3M,94.28 +2024-03-05,OI_FUTURE/PRICE/USD/2024-12/XCME:SRA/3M,94.65 +2024-03-05,OI_FUTURE/PRICE/USD/2025-03/XCME:SRA/3M,96.5 +2024-03-05,OI_FUTURE/PRICE/USD/2025-06/XCME:SRA/3M,95.02 +2024-03-05,OI_FUTURE/PRICE/USD/2025-09/XCME:SRA/3M,96.6 +2024-03-05,CDS/CREDIT_SPREAD/Underlying1/SNRFOR/USD/10Y,0.03 +2024-03-05,CDS/CREDIT_SPREAD/Underlying1/SNRFOR/USD/3Y,0.008 +2024-03-05,CDS/CREDIT_SPREAD/Underlying1/SNRFOR/USD/5Y,0.017 +2024-03-05,CDS/CREDIT_SPREAD/Underlying1/SNRFOR/USD/7Y,0.022 +2024-03-05,RECOVERY_RATE/RATE/Underlying1/SNRFOR/USD,0.4 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/1Y/6M/0/0/-0.01,0.019 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/1Y/6M/0/0/0,0.017 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/1Y/6M/0/0/0.01,0.014 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/1Y/6M/0/0/0.02,0.011 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/1Y/6M/0/0/0.03,0.009 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/1Y/6M/0/0/0.04,0.008 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/1Y/6M/0/0/0.05,0.01 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/1Y/6M/0/0/0.06,0.012 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/1Y/6M/0/0/0.07,0.015 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/1Y/6M/0/0/0.1,0.022 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/2Y/6M/0/0/-0.01,0.015 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/2Y/6M/0/0/0,0.013 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/2Y/6M/0/0/0.01,0.012 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/2Y/6M/0/0/0.02,0.011 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/2Y/6M/0/0/0.03,0.01 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/2Y/6M/0/0/0.04,0.01 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/2Y/6M/0/0/0.05,0.012 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/2Y/6M/0/0/0.06,0.014 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/2Y/6M/0/0/0.07,0.016 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/2Y/6M/0/0/0.1,0.022 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/3Y/6M/0/0/-0.01,0.012 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/3Y/6M/0/0/0,0.012 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/3Y/6M/0/0/0.01,0.011 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/3Y/6M/0/0/0.02,0.011 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/3Y/6M/0/0/0.03,0.01 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/3Y/6M/0/0/0.04,0.011 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/3Y/6M/0/0/0.05,0.013 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/3Y/6M/0/0/0.06,0.014 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/3Y/6M/0/0/0.07,0.016 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/3Y/6M/0/0/0.1,0.02 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/4Y/6M/0/0/-0.01,0.011 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/4Y/6M/0/0/0,0.011 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/4Y/6M/0/0/0.01,0.01 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/4Y/6M/0/0/0.02,0.01 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/4Y/6M/0/0/0.03,0.01 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/4Y/6M/0/0/0.04,0.011 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/4Y/6M/0/0/0.05,0.013 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/4Y/6M/0/0/0.06,0.014 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/4Y/6M/0/0/0.07,0.015 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/4Y/6M/0/0/0.1,0.02 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/5Y/6M/0/0/-0.01,0.011 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/5Y/6M/0/0/0,0.01 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/5Y/6M/0/0/0.01,0.01 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/5Y/6M/0/0/0.02,0.01 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/5Y/6M/0/0/0.03,0.01 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/5Y/6M/0/0/0.04,0.011 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/5Y/6M/0/0/0.05,0.012 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/5Y/6M/0/0/0.06,0.014 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/5Y/6M/0/0/0.07,0.015 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/5Y/6M/0/0/0.1,0.019 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/6Y/6M/0/0/-0.01,0.011 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/6Y/6M/0/0/0,0.01 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/6Y/6M/0/0/0.01,0.01 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/6Y/6M/0/0/0.02,0.01 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/6Y/6M/0/0/0.03,0.01 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/6Y/6M/0/0/0.04,0.011 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/6Y/6M/0/0/0.05,0.012 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/6Y/6M/0/0/0.06,0.014 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/6Y/6M/0/0/0.07,0.015 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/6Y/6M/0/0/0.1,0.019 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/7Y/6M/0/0/-0.01,0.01 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/7Y/6M/0/0/0,0.01 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/7Y/6M/0/0/0.01,0.01 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/7Y/6M/0/0/0.02,0.01 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/7Y/6M/0/0/0.03,0.01 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/7Y/6M/0/0/0.04,0.011 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/7Y/6M/0/0/0.05,0.012 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/7Y/6M/0/0/0.06,0.013 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/7Y/6M/0/0/0.07,0.015 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/7Y/6M/0/0/0.1,0.019 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/8Y/6M/0/0/-0.01,0.01 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/8Y/6M/0/0/0,0.01 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/8Y/6M/0/0/0.01,0.01 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/8Y/6M/0/0/0.02,0.01 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/8Y/6M/0/0/0.03,0.01 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/8Y/6M/0/0/0.04,0.011 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/8Y/6M/0/0/0.05,0.012 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/8Y/6M/0/0/0.06,0.013 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/8Y/6M/0/0/0.07,0.014 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/8Y/6M/0/0/0.1,0.019 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/9Y/6M/0/0/-0.01,0.01 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/9Y/6M/0/0/0,0.009 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/9Y/6M/0/0/0.01,0.009 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/9Y/6M/0/0/0.02,0.009 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/9Y/6M/0/0/0.03,0.01 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/9Y/6M/0/0/0.04,0.011 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/9Y/6M/0/0/0.05,0.012 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/9Y/6M/0/0/0.06,0.013 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/9Y/6M/0/0/0.07,0.014 +2024-03-05,CAPFLOOR/RATE_NVOL/EUR/9Y/6M/0/0/0.1,0.018 +2024-03-05,MM/RATE/EUR/2D/6M,0.039 +2024-03-05,FRA/RATE/EUR/12M/6M,0.028 +2024-03-05,FRA/RATE/EUR/1M/6M,0.039 +2024-03-05,FRA/RATE/EUR/2M/6M,0.037 +2024-03-05,FRA/RATE/EUR/3M/6M,0.036 +2024-03-05,FRA/RATE/EUR/4M/6M,0.035 +2024-03-05,FRA/RATE/EUR/5M/6M,0.034 +2024-03-05,FRA/RATE/EUR/6M/6M,0.033 +2024-03-05,FRA/RATE/EUR/9M/6M,0.03 +2024-03-05,BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/10Y,0.0002 +2024-03-05,BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/12Y,0.00003 +2024-03-05,BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/15Y,-0.0002 +2024-03-05,BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/20Y,-0.0005 +2024-03-05,BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/25Y,-0.00062 +2024-03-05,BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/2Y,0.00085 +2024-03-05,BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/30Y,-0.0007 +2024-03-05,BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/3Y,0.00079 +2024-03-05,BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/40Y,-0.0009 +2024-03-05,BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/4Y,0.00072 +2024-03-05,BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/50Y,-0.00105 +2024-03-05,BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/5Y,0.00064 +2024-03-05,BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/60Y,-0.00108 +2024-03-05,BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/6Y,0.00056 +2024-03-05,BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/7Y,0.00048 +2024-03-05,BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/8Y,0.0004 +2024-03-05,BASIS_SWAP/BASIS_SPREAD/3M/6M/EUR/9Y,0.0003 +2024-03-05,MM/RATE/EUR/2D/3M,0.04 +2024-03-05,MM_FUTURE/PRICE/EUR/2024-04/XICE:FEI/3M,95.31 +2024-03-05,MM_FUTURE/PRICE/EUR/2024-05/XICE:FEI/3M,96.25 +2024-03-05,MM_FUTURE/PRICE/EUR/2024-06/XICE:FEI/3M,96.29 +2024-03-05,MM_FUTURE/PRICE/EUR/2024-07/XICE:FEI/3M,96.42 +2024-03-05,MM_FUTURE/PRICE/EUR/2024-08/XICE:FEI/3M,97.53 +2024-03-05,MM_FUTURE/PRICE/EUR/2024-09/XICE:FEI/3M,97.68 +2024-03-05,MM_FUTURE/PRICE/EUR/2024-12/XICE:FEI/3M,97.3 +2024-03-05,MM_FUTURE/PRICE/EUR/2025-03/XICE:FEI/3M,96.81 +2024-03-05,MM_FUTURE/PRICE/EUR/2025-06/XICE:FEI/3M,98.03 +2024-03-05,MM_FUTURE/PRICE/EUR/2025-09/XICE:FEI/3M,97.04 +2024-03-05,OI_FUTURE/PRICE/USD/2024-04/XCME:SRA/3M,95.23 +2024-03-05,OI_FUTURE/PRICE/USD/2024-05/XCME:SRA/3M,93.86 +2024-03-05,OI_FUTURE/PRICE/USD/2024-06/XCME:SRA/3M,95.33 +2024-03-05,OI_FUTURE/PRICE/USD/2024-07/XCME:SRA/3M,93.94 +2024-03-05,OI_FUTURE/PRICE/USD/2024-08/XCME:SRA/3M,94.56 +2024-03-05,OI_FUTURE/PRICE/USD/2024-09/XCME:SRA/3M,95.6 +2024-03-05,OI_FUTURE/PRICE/USD/2024-10/XCME:SRA/3M,94.58 +2024-03-05,OI_FUTURE/PRICE/USD/2024-11/XCME:SRA/3M,94.28 +2024-03-05,OI_FUTURE/PRICE/USD/2024-12/XCME:SRA/3M,94.65 +2024-03-05,OI_FUTURE/PRICE/USD/2025-03/XCME:SRA/3M,96.5 +2024-03-05,OI_FUTURE/PRICE/USD/2025-06/XCME:SRA/3M,95.02 +2024-03-05,OI_FUTURE/PRICE/USD/2025-09/XCME:SRA/3M,96.6 +2024-03-05,IR_SWAP/RATE/EUR/2D/3M/10Y,0.03 +2024-03-05,IR_SWAP/RATE/EUR/2D/3M/12Y,0.03 +2024-03-05,IR_SWAP/RATE/EUR/2D/3M/15Y,0.03 +2024-03-05,IR_SWAP/RATE/EUR/2D/3M/20Y,0.03 +2024-03-05,IR_SWAP/RATE/EUR/2D/3M/25Y,0.03 +2024-03-05,IR_SWAP/RATE/EUR/2D/3M/2Y,0.03 +2024-03-05,IR_SWAP/RATE/EUR/2D/3M/30Y,0.02 +2024-03-05,IR_SWAP/RATE/EUR/2D/3M/3Y,0.03 +2024-03-05,IR_SWAP/RATE/EUR/2D/3M/40Y,0.02 +2024-03-05,IR_SWAP/RATE/EUR/2D/3M/4Y,0.03 +2024-03-05,IR_SWAP/RATE/EUR/2D/3M/50Y,0.02 +2024-03-05,IR_SWAP/RATE/EUR/2D/3M/5Y,0.03 +2024-03-05,IR_SWAP/RATE/EUR/2D/3M/60Y,0.02 +2024-03-05,IR_SWAP/RATE/EUR/2D/3M/6Y,0.03 +2024-03-05,IR_SWAP/RATE/EUR/2D/3M/7Y,0.03 +2024-03-05,IR_SWAP/RATE/EUR/2D/3M/8Y,0.03 +2024-03-05,IR_SWAP/RATE/EUR/2D/3M/9Y,0.03 +2024-03-05,ZC_INFLATIONSWAP/RATE/EUHICPXT/10Y,0.01984317 +2024-03-05,ZC_INFLATIONSWAP/RATE/EUHICPXT/12Y,0.02019488 +2024-03-05,ZC_INFLATIONSWAP/RATE/EUHICPXT/15Y,0.02060033 +2024-03-05,ZC_INFLATIONSWAP/RATE/EUHICPXT/1Y,0.02195007 +2024-03-05,ZC_INFLATIONSWAP/RATE/EUHICPXT/20Y,0.02126171 +2024-03-05,ZC_INFLATIONSWAP/RATE/EUHICPXT/25Y,0.02182012 +2024-03-05,ZC_INFLATIONSWAP/RATE/EUHICPXT/2Y,0.02013430 +2024-03-05,ZC_INFLATIONSWAP/RATE/EUHICPXT/30Y,0.02220907 +2024-03-05,ZC_INFLATIONSWAP/RATE/EUHICPXT/35Y,0.02255919 +2024-03-05,ZC_INFLATIONSWAP/RATE/EUHICPXT/3Y,0.01962276 +2024-03-05,ZC_INFLATIONSWAP/RATE/EUHICPXT/40Y,0.02318439 +2024-03-05,ZC_INFLATIONSWAP/RATE/EUHICPXT/45Y,0.02362066 +2024-03-05,ZC_INFLATIONSWAP/RATE/EUHICPXT/4Y,0.01931579 +2024-03-05,ZC_INFLATIONSWAP/RATE/EUHICPXT/50Y,0.02413343 +2024-03-05,ZC_INFLATIONSWAP/RATE/EUHICPXT/5Y,0.01925836 +2024-03-05,ZC_INFLATIONSWAP/RATE/EUHICPXT/6Y,0.01938726 +2024-03-05,ZC_INFLATIONSWAP/RATE/EUHICPXT/7Y,0.01958277 +2024-03-05,ZC_INFLATIONSWAP/RATE/EUHICPXT/8Y,0.01973091 +2024-03-05,ZC_INFLATIONSWAP/RATE/EUHICPXT/9Y,0.01975486 +2024-03-05,EQUITY/PRICE/RIC:.SPX/USD,6082.97246883 +2024-03-05,EQUITY/PRICE/RIC:.STOXX50/EUR,4662.44403643 +2024-03-05,EQUITY_FWD/PRICE/RIC:.SPX/USD/1080D,6740.01708904 +2024-03-05,EQUITY_FWD/PRICE/RIC:.SPX/USD/120D,6151.32084033 +2024-03-05,EQUITY_FWD/PRICE/RIC:.SPX/USD/14D,6074.30623424 +2024-03-05,EQUITY_FWD/PRICE/RIC:.SPX/USD/150D,6184.86929616 +2024-03-05,EQUITY_FWD/PRICE/RIC:.SPX/USD/180D,6164.48338064 +2024-03-05,EQUITY_FWD/PRICE/RIC:.SPX/USD/21D,6079.22445552 +2024-03-05,EQUITY_FWD/PRICE/RIC:.SPX/USD/270D,6240.23324663 +2024-03-05,EQUITY_FWD/PRICE/RIC:.SPX/USD/30D,6102.98679037 +2024-03-05,EQUITY_FWD/PRICE/RIC:.SPX/USD/360D,6297.66612054 +2024-03-05,EQUITY_FWD/PRICE/RIC:.SPX/USD/60D,6069.86392566 +2024-03-05,EQUITY_FWD/PRICE/RIC:.SPX/USD/720D,6528.14213714 +2024-03-05,EQUITY_FWD/PRICE/RIC:.SPX/USD/7D,6066.03636276 +2024-03-05,EQUITY_FWD/PRICE/RIC:.SPX/USD/90D,6104.36640965 +2024-03-05,SEASONALITY/RATE/MULT/EUHICPXT/APR,1.00528739 +2024-03-05,SEASONALITY/RATE/MULT/EUHICPXT/AUG,1.00211507 +2024-03-05,SEASONALITY/RATE/MULT/EUHICPXT/DEC,0.99718925 +2024-03-05,SEASONALITY/RATE/MULT/EUHICPXT/FEB,0.98922387 +2024-03-05,SEASONALITY/RATE/MULT/EUHICPXT/JAN,0.98610447 +2024-03-05,SEASONALITY/RATE/MULT/EUHICPXT/JUL,0.99752192 +2024-03-05,SEASONALITY/RATE/MULT/EUHICPXT/JUN,1.00811576 +2024-03-05,SEASONALITY/RATE/MULT/EUHICPXT/MAR,1.00441990 +2024-03-05,SEASONALITY/RATE/MULT/EUHICPXT/MAY,1.00745949 +2024-03-05,SEASONALITY/RATE/MULT/EUHICPXT/NOV,0.99835510 +2024-03-05,SEASONALITY/RATE/MULT/EUHICPXT/OCT,1.00059361 +2024-03-05,SEASONALITY/RATE/MULT/EUHICPXT/SEP,1.00118437 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4075/C,596.48356355 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4075/P,1.10459328 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4100/C,572.63795944 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4100/P,1.20437051 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4125/C,548.92304776 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4125/P,1.30225859 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4150/C,521.16111485 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4150/P,1.40536293 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4175/C,499.15688349 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4175/P,1.50158177 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4200/C,470.72974012 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4200/P,1.59361211 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4225/C,446.06986284 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4225/P,1.79879553 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4250/C,424.13611270 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4250/P,1.99030062 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4275/C,397.48434201 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4275/P,2.19752007 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4300/C,374.38279415 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4300/P,2.40666773 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4325/C,346.98703564 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4325/P,2.69151399 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4350/C,325.42579461 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4350/P,3.01375166 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4375/C,299.86645271 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4375/P,3.40588954 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4400/C,273.80636780 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4400/P,4.00995540 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4425/C,251.80019043 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4425/P,4.58447502 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4450/C,227.40402223 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4450/P,5.41855932 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4475/C,201.80717340 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4475/P,6.48866539 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4500/C,178.15159301 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4500/P,7.78018912 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4525/C,155.76266798 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4525/P,9.57191373 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4550/C,133.47421567 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4550/P,11.89818358 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4575/C,111.22110144 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4575/P,15.01251531 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4600/C,90.10971794 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4600/P,19.03507674 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4625/C,70.40959596 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4625/P,24.71752951 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4650/C,53.12541631 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4650/P,31.75700916 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4675/C,37.54283744 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4675/P,41.39795233 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4700/C,24.62519465 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4700/P,53.70918132 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4725/C,15.14545514 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4725/P,68.95045593 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4750/C,8.60287603 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4750/P,87.22552100 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4775/C,4.38866574 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4775/P,107.81154538 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4800/C,2.09325321 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4800/P,131.17084539 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4825/C,0.99757390 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-03-16/4825/P,154.38087488 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4050/C,626.96894228 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4050/P,5.18586500 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4075/C,599.64995898 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4075/P,5.48802752 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4100/C,575.89713315 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4100/P,5.92585079 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4125/C,553.44687577 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4125/P,6.38007554 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4150/C,526.06598906 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4150/P,6.92385564 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4175/C,502.34034362 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4175/P,7.39195692 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4200/C,479.46434099 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4200/P,8.10029505 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4225/C,455.49829086 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4225/P,8.82445172 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4250/C,428.12954723 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4250/P,9.66998778 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4275/C,404.87584927 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4275/P,10.65294024 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4300/C,380.54262063 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4300/P,11.66370071 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4325/C,359.48581562 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4325/P,12.91166422 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4350/C,333.55342066 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4350/P,14.41581568 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4375/C,310.75522556 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4375/P,16.02326495 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4400/C,288.09386089 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4400/P,18.00415583 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4425/C,265.49778927 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4425/P,20.10541189 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4450/C,242.91299080 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4450/P,22.79753826 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4475/C,220.58091423 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4475/P,25.76212031 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4500/C,200.25443817 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4500/P,29.36900313 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4525/C,179.61004769 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4525/P,33.38811345 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4550/C,158.78466045 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4550/P,38.11409330 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4575/C,138.99785698 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4575/P,43.93025010 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4600/C,120.90406009 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4600/P,50.39604006 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4625/C,104.06472120 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4625/P,57.76241045 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4650/C,87.42969136 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4650/P,66.54147835 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4675/C,72.42249847 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4675/P,76.19796823 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4700/C,58.68943975 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4700/P,87.53814951 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4725/C,46.76798355 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4725/P,100.69175642 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4750/C,36.68192634 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4750/P,115.30709300 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4775/C,27.95105522 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4775/P,131.33389671 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4800/C,20.91095885 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4800/P,149.63096439 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4825/C,15.31325713 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-04-13/4825/P,168.29557962 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4300/C,400.85046307 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4300/P,21.05535641 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4325/C,377.78665531 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4325/P,23.11324210 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4350/C,356.18908386 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4350/P,25.15473079 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4375/C,334.24169697 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4375/P,27.53135416 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4400/C,309.55184209 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4400/P,30.16017415 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4425/C,289.75414222 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4425/P,33.35577124 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4450/C,267.64130430 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4450/P,36.87966761 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4475/C,247.57884570 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4475/P,40.79060413 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4500/C,225.75504016 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4500/P,45.18722482 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4525/C,206.41799581 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4525/P,49.69038442 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4550/C,186.65672086 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4550/P,55.30932363 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4575/C,167.83590852 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4575/P,61.67095246 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4600/C,149.39924934 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4600/P,68.63279029 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4625/C,132.65062871 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4625/P,76.28151628 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4650/C,116.21403480 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4650/P,84.74422388 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4675/C,101.06142602 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4675/P,93.99002049 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4700/C,86.38994585 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4700/P,104.52659669 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4725/C,73.88536739 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4725/P,116.22439606 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4750/C,61.85743697 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4750/P,130.31891221 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4775/C,51.43458211 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4775/P,144.15466249 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4800/C,42.13268574 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4800/P,160.53824406 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4825/C,34.43075201 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-05-10/4825/P,176.56294694 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/2900/C,1720.40098386 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/2900/P,4.08385793 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/3000/C,1627.45115834 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/3000/P,4.71927717 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/3100/C,1529.63242474 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/3100/P,5.37461161 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/3200/C,1435.13128137 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/3200/P,6.22591482 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/3300/C,1329.53463865 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/3300/P,7.10046758 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/3400/C,1230.66549877 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/3400/P,8.18201478 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/3500/C,1133.67381635 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/3500/P,9.50266025 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/3600/C,1032.69257734 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/3600/P,11.14459783 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/3700/C,942.27625256 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/3700/P,13.32993698 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/3800/C,844.50950218 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/3800/P,15.99029682 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/3900/C,747.47065592 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/3900/P,19.48903838 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4000/C,652.21790089 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4000/P,24.31066751 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4050/C,609.13752417 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4050/P,27.10127470 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4100/C,562.65091880 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4100/P,30.46792600 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4150/C,515.45768291 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4150/P,34.70567347 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4200/C,470.06147855 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4200/P,39.47109304 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4250/C,427.95628119 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4250/P,44.75882865 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4300/C,382.67093553 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4300/P,51.42793803 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4350/C,340.59922242 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4350/P,58.75126839 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4400/C,301.14764971 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4400/P,67.95151430 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4450/C,263.64113450 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4450/P,78.79079596 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4500/C,225.07081978 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4500/P,91.39695123 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4550/C,190.88011116 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4550/P,105.66449927 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4600/C,158.40185205 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4600/P,122.43583663 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4650/C,128.23082903 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4650/P,142.94951374 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4700/C,101.62102908 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4700/P,165.24210084 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4750/C,79.37893902 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4750/P,193.75518261 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4800/C,60.21763448 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4800/P,222.56321095 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4850/C,44.92863880 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4850/P,257.35206649 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4900/C,32.56991662 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/4900/P,294.37581696 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/5000/C,16.26861923 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-07-13/5000/P,377.33612070 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4050/C,644.47197618 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4050/P,50.45642242 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4100/C,596.95620969 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4100/P,55.54198473 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4150/C,552.90323048 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4150/P,60.74508213 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4200/C,510.21389956 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4200/P,66.82632433 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4250/C,469.63353125 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4250/P,73.73978152 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4300/C,426.49802734 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4300/P,82.66110795 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4350/C,387.29721221 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4350/P,92.21166094 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4400/C,348.72977143 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4400/P,102.90679821 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4450/C,310.99761629 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4450/P,115.28762273 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4500/C,277.45950051 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4500/P,128.35814983 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4550/C,243.03131796 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4550/P,144.16894008 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4600/C,210.63150831 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4600/P,162.77631174 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4650/C,182.48158047 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4650/P,183.33281489 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4700/C,155.99495221 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4700/P,205.09003973 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4750/C,131.02861492 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4750/P,231.13431195 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4800/C,108.59551253 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4800/P,258.28666734 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4850/C,89.84914428 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2024-10-12/4850/P,287.93990515 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/2600/C,2035.52409086 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/2600/P,10.93979652 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/2700/C,1935.24455878 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/2700/P,12.08911311 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/2800/C,1840.30161594 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/2800/P,13.56201069 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/2900/C,1738.86089628 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/2900/P,14.97723340 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/3000/C,1653.24535969 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/3000/P,16.74956649 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/3100/C,1544.93488614 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/3100/P,18.72390032 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/3200/C,1460.07624702 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/3200/P,21.24138878 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/3300/C,1362.53328210 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/3300/P,23.96105442 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/3400/C,1269.70084793 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/3400/P,27.22896064 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/3500/C,1168.95873822 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/3500/P,31.00365062 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/3600/C,1077.11586316 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/3600/P,35.84717144 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/3700/C,982.09934140 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/3700/P,41.20000169 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/3800/C,896.03422510 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/3800/P,48.36626029 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/3900/C,801.08212360 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/3900/P,56.41483512 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4000/C,714.05000808 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4000/P,66.82234460 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4050/C,672.42366466 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4050/P,72.83979008 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4100/C,631.72255136 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4100/P,79.26220458 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4150/C,589.08014819 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4150/P,86.53628841 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4200/C,547.85833984 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4200/P,94.63782104 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4250/C,504.88313375 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4250/P,103.37979827 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4300/C,469.22593173 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4300/P,112.80868661 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4350/C,429.07007375 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4350/P,123.83278553 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4400/C,391.02390304 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4400/P,135.47619814 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4450/C,355.90157372 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4450/P,148.15957554 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4500/C,323.28530119 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4500/P,162.67339272 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4550/C,290.62232232 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4550/P,178.64864428 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4600/C,256.36259782 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4600/P,196.75347354 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4650/C,226.68987042 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4650/P,217.16844473 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4700/C,200.19370950 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4700/P,237.43581678 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4750/C,174.35695616 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4750/P,260.49896730 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4800/C,150.41304765 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4800/P,285.73735790 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4850/C,129.49356189 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4850/P,313.12496203 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4900/C,110.41026232 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/4900/P,344.29116827 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/5000/C,77.75472964 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-01-11/5000/P,409.16800667 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/3400/C,1232.18093055 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/3400/P,49.52911915 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/3500/C,1133.21080961 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/3500/P,55.96203519 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/3600/C,1045.80414708 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/3600/P,63.94565791 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/3700/C,963.07582859 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/3700/P,73.09833492 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/3800/C,874.10969592 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/3800/P,83.39293672 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/3900/C,787.38983822 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/3900/P,96.81667647 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/4000/C,709.42466931 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/4000/P,110.89755886 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/4100/C,627.37281688 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/4100/P,128.97414488 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/4200/C,548.39265107 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/4200/P,148.82633224 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/4300/C,475.98632037 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/4300/P,172.32134434 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/4400/C,406.11281004 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/4400/P,201.15708415 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/4500/C,340.91792702 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/4500/P,234.20110207 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/4600/C,283.23486404 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/4600/P,271.98636578 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/4700/C,229.40318503 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/4700/P,315.25516295 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/4800/C,182.11002835 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/4800/P,367.15631033 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/4900/C,143.26658715 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/4900/P,424.90470462 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/5000/C,109.70478334 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/5000/P,487.91817882 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/5100/C,83.41047597 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2025-07-12/5100/P,559.35583284 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/2900/C,1690.26706075 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/2900/P,39.01207583 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/3000/C,1601.65193232 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/3000/P,43.60577363 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/3100/C,1509.63611323 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/3100/P,48.66920797 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/3200/C,1412.31557896 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/3200/P,54.04593479 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/3300/C,1327.08008917 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/3300/P,61.04315529 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/3400/C,1245.11404514 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/3400/P,67.96076800 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/3500/C,1152.58584056 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/3500/P,76.77995843 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/3600/C,1061.53963400 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/3600/P,86.45247370 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/3700/C,978.87500083 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/3700/P,97.70192796 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/3800/C,901.61217560 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/3800/P,111.41992723 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/3900/C,814.71138571 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/3900/P,125.72401577 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/4000/C,735.79707191 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/4000/P,143.86216682 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/4100/C,658.25371784 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/4100/P,163.89456254 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/4200/C,587.04304945 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/4200/P,185.49273980 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/4300/C,516.90175181 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/4300/P,213.18971696 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/4400/C,451.41131964 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/4400/P,240.99589873 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/4500/C,387.87005204 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/4500/P,274.44601137 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/4600/C,328.65375430 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/4600/P,313.64030197 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/4700/C,277.89683033 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/4700/P,356.71343528 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/4800/C,230.09621078 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/4800/P,404.82672123 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/4900/C,187.06450937 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/4900/P,461.13932331 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/5000/C,151.95095938 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/5000/P,519.02316743 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/5100/C,121.87792265 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-01-10/5100/P,586.43716038 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/3800/C,872.97042063 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/3800/P,146.91657830 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/3900/C,797.48506359 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/3900/P,165.14474597 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/4000/C,719.88920215 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/4000/P,186.71952026 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/4100/C,644.88730410 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/4100/P,209.34307943 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/4200/C,579.17376605 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/4200/P,236.40071886 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/4300/C,513.68689414 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/4300/P,264.37326212 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/4400/C,447.96819932 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/4400/P,298.44485943 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/4500/C,389.54460552 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/4500/P,332.71899194 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/4600/C,335.19286396 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/4600/P,375.94314404 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/4700/C,287.29585739 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/4700/P,422.21549883 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/4800/C,241.79798685 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/4800/P,469.32247016 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/4900/C,203.53920590 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/4900/P,525.56126530 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/5000/C,167.75371403 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/5000/P,588.46764789 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/5100/C,138.06672595 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2026-07-11/5100/P,653.17466621 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/3300/C,1294.20050705 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/3300/P,100.53773581 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/3400/C,1209.39594149 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/3400/P,111.88928950 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/3500/C,1128.46848128 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/3500/P,124.78575560 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/3600/C,1050.12858263 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/3600/P,138.81495951 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/3700/C,968.98875149 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/3700/P,153.84243453 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/3800/C,888.94057618 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/3800/P,172.22557056 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/3900/C,812.78834716 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/3900/P,191.42010365 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/4000/C,744.15051657 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/4000/P,213.69884154 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/4100/C,672.55987506 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/4100/P,238.77154345 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/4200/C,609.06878768 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/4200/P,266.03233935 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/4300/C,540.04903682 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/4300/P,296.13701416 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/4400/C,479.47621931 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/4400/P,330.32485222 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/4500/C,422.14887776 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/4500/P,366.05937126 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/4600/C,372.11702313 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/4600/P,407.49699782 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/4700/C,319.98573938 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/4700/P,452.67644311 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/4800/C,275.19651421 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/4800/P,498.52349744 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/4900/C,236.74816951 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/4900/P,552.18724415 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/5000/C,199.81128917 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/5000/P,616.44918670 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/5100/C,168.30574760 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2027-01-09/5100/P,676.33377453 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/3600/C,1050.21635048 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/3600/P,186.92522262 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/3700/C,974.01092595 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/3700/P,205.43447000 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/3800/C,905.53648788 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/3800/P,225.47943629 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/3900/C,835.36958107 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/3900/P,249.19649367 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/4000/C,764.84507102 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/4000/P,274.04091583 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/4100/C,698.50285410 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/4100/P,298.82516204 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/4200/C,636.93272336 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/4200/P,328.39082664 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/4300/C,579.00636975 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/4300/P,362.87028320 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/4400/C,521.23391499 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/4400/P,397.59368890 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/4500/C,463.39577379 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/4500/P,435.10930578 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/4600/C,413.96987786 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/4600/P,478.03811005 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/4700/C,365.96401851 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/4700/P,519.98687015 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/4800/C,322.39668087 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/4800/P,569.72081074 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/4900/C,283.91518617 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/4900/P,620.35276081 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/5000/C,247.68814878 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/5000/P,677.97746689 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/5100/C,216.18930959 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2028-01-08/5100/P,740.37186462 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2029-01-13/3900/C,935.76070252 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2029-01-13/3900/P,265.77847696 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2029-01-13/4000/C,867.32311819 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2029-01-13/4000/P,291.85924981 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2029-01-13/4100/C,805.27548443 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2029-01-13/4100/P,316.46979953 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2029-01-13/4200/C,743.58599646 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2029-01-13/4200/P,345.38147310 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2029-01-13/4300/C,678.67164710 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2029-01-13/4300/P,375.36805282 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2029-01-13/4400/C,620.31546169 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2029-01-13/4400/P,409.93624116 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2029-01-13/4500/C,569.21860264 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2029-01-13/4500/P,443.84887625 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2029-01-13/4600/C,518.27604336 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2029-01-13/4600/P,482.74722592 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2029-01-13/4700/C,466.17823359 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2029-01-13/4700/P,525.45678958 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2029-01-13/4800/C,418.63282772 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2029-01-13/4800/P,569.36320965 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2029-01-13/4900/C,378.70019795 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2029-01-13/4900/P,616.88077650 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2029-01-13/5000/C,338.69767858 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2029-01-13/5000/P,667.10148579 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2029-01-13/5100/C,302.73407740 +2024-03-05,EQUITY_OPTION/PRICE/RIC:.STOXX50/EUR/2029-01-13/5100/P,717.20671080 \ No newline at end of file diff --git a/EconomicStressAgentORE/oredata/Input/parstresstest.xml b/EconomicStressAgentORE/oredata/Input/parstresstest.xml new file mode 100644 index 0000000..ae02ff4 --- /dev/null +++ b/EconomicStressAgentORE/oredata/Input/parstresstest.xml @@ -0,0 +1,791 @@ + + true + + + false + false + false + + + + + + + + + + Absolute + + 0.01 + 0.01 + 0.01 + 0.01 + 0.01 + 0.01 + 0.01 + 0.01 + 0.01 + + 1Y, 2Y, 3Y, 4Y, 5Y, 6Y, 7Y, 8Y, 9Y + + + + + + + + + + + + false + false + false + + + + + + + + + + Absolute + + 0.01 + 0.01 + 0.01 + 0.01 + 0.01 + 0.01 + 0.01 + 0.01 + 0.01 + + 1Y, 2Y, 3Y, 4Y, 5Y, 6Y, 7Y, 8Y, 9Y + + + + + + + + + + + + false + false + + + + + Absolute + + + 0.00, + 0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00 + + 1D, + 1W,2W,3W,1M,2M,3M,4M,5M,6M,7M,8M,9M,10M,11M,1Y,15M,18M,21M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,11Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y,60Y + + + Absolute + + 0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01 + + + 1D,2D,3D,1W,2W,3W,1M,2M,3M,4M,5M,6M,7M,8M,9M,10M,11M,1Y,15M,18M,21M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y + + + + + + Absolute + + 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, + 0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00 + + 1D, + 1W,2W,3W,1M,2M,3M,4M,5M,6M,7M,8M,9M,10M,11M,1Y,15M,18M,21M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,11Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y,60Y + + + + Absolute + 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, + 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00 + 1D, 1W, 2W, 1M, 2M, 3M, 4M, 5M, 6M, 7M, 8M, 9M, 10M, 11M, 1Y, 15M, 18M, 21M, + 2Y, 3Y, 5Y, 10Y, 15Y, 20Y, 30Y + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + false + + + + + + + + + + + + + + + Absolute + 0.01, 0.01, 0.01, 0.01, 0.01 + 1Y, 2Y, 3Y, 5Y, 10Y + + + + + + + + + + + + + + + + + + + Absolute + 0.01, 0.01, 0.01, 0.01, 0.01 + 1Y, 2Y, 3Y, 5Y, 10Y + + + + + + + Absolute + + + 0.01, + 0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01 + + 1D, + 1W,2W,3W,1M,2M,3M,4M,5M,6M,7M,8M,9M,10M,11M,1Y,15M,18M,21M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,11Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y,60Y + + + Absolute + + 0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00 + + 1D,2D,3D,1W,2W,3W,1M,2M,3M,4M,5M,6M,7M,8M,9M,10M,11M,1Y,15M,18M,21M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y + + + + + + Absolute + + 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, + 0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01 + + 1D, + 1W,2W,3W,1M,2M,3M,4M,5M,6M,7M,8M,9M,10M,11M,1Y,15M,18M,21M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,11Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y,60Y + + + + Absolute + 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, + 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00 + 1D, 1W, 2W, 1M, 2M, 3M, 4M, 5M, 6M, 7M, 8M, 9M, 10M, 11M, 1Y, 15M, 18M, 21M, + 2Y, 3Y, 5Y, 10Y, 15Y, 20Y, 30Y + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + true + + + + + Absolute + + + 0.00, + 0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00 + + 1D, + 1W,2W,3W,1M,2M,3M,4M,5M,6M,7M,8M,9M,10M,11M,1Y,15M,18M,21M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,11Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y,60Y + + + Absolute + + 0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00 + + + 1D,2D,3D,1W,2W,3W,1M,2M,3M,4M,5M,6M,7M,8M,9M,10M,11M,1Y,15M,18M,21M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y + + + + + + Absolute + + 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, + 0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00 + + 1D, + 1W,2W,3W,1M,2M,3M,4M,5M,6M,7M,8M,9M,10M,11M,1Y,15M,18M,21M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,11Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y,60Y + + + + Absolute + 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, + 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01 + 6M, + 7M,8M,9M,10M,11M,12M,15M,18M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y,60Y + + + + Absolute + 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, + 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00 + 1D, 1W, 2W, 1M, 2M, 3M, 4M, 5M, 6M, 7M, 8M, 9M, 10M, 11M, 1Y, 15M, 18M, 21M, + 2Y, 3Y, 5Y, 10Y, 15Y, 20Y, 30Y + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + false + false + + + + + Absolute + + + 0.00, + 0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00 + + 1D, + 1W,2W,3W,1M,2M,3M,4M,5M,6M,7M,8M,9M,10M,11M,1Y,15M,18M,21M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,11Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y,60Y + + + Absolute + + 0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00 + + + 1D,2D,3D,1W,2W,3W,1M,2M,3M,4M,5M,6M,7M,8M,9M,10M,11M,1Y,15M,18M,21M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y + + + + + + Absolute + + 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, + 0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00 + + 1D, + 1W,2W,3W,1M,2M,3M,4M,5M,6M,7M,8M,9M,10M,11M,1Y,15M,18M,21M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,11Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y,60Y + + + + Absolute + 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, + 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01 + 6M, + 7M,8M,9M,10M,11M,12M,15M,18M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y,60Y + + + + Absolute + 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, + 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00 + 1D, 1W, 2W, 1M, 2M, 3M, 4M, 5M, 6M, 7M, 8M, 9M, 10M, 11M, 1Y, 15M, 18M, 21M, + 2Y, 3Y, 5Y, 10Y, 15Y, 20Y, 30Y + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + false + + + + + Absolute + + + 0.00, + 0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00 + + 1D, + 1W,2W,3W,1M,2M,3M,4M,5M,6M,7M,8M,9M,10M,11M,1Y,15M,18M,21M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,11Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y,60Y + + + Absolute + + 0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00 + + + 1D,2D,3D,1W,2W,3W,1M,2M,3M,4M,5M,6M,7M,8M,9M,10M,11M,1Y,15M,18M,21M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y + + + + + + Absolute + + 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, + 0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00 + + 1D, + 1W,2W,3W,1M,2M,3M,4M,5M,6M,7M,8M,9M,10M,11M,1Y,15M,18M,21M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,11Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y,60Y + + + + Absolute + 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, + 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01 + 6M, + 7M,8M,9M,10M,11M,12M,15M,18M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y,60Y + + + + Absolute + 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, + 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00 + 1D, 1W, 2W, 1M, 2M, 3M, 4M, 5M, 6M, 7M, 8M, 9M, 10M, 11M, 1Y, 15M, 18M, 21M, + 2Y, 3Y, 5Y, 10Y, 15Y, 20Y, 30Y + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + false + + + + + Absolute + + + 0.00, + 0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00 + + 1D, + 1W,2W,3W,1M,2M,3M,4M,5M,6M,7M,8M,9M,10M,11M,1Y,15M,18M,21M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,11Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y,60Y + + + Absolute + + 0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01 + + + 1D,2D,3D,1W,2W,3W,1M,2M,3M,4M,5M,6M,7M,8M,9M,10M,11M,1Y,15M,18M,21M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y + + + + + + Absolute + + 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, + 0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00 + + 1D, + 1W,2W,3W,1M,2M,3M,4M,5M,6M,7M,8M,9M,10M,11M,1Y,15M,18M,21M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,11Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y,60Y + + + + Absolute + 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, + 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00 + 1D, 1W, 2W, 1M, 2M, 3M, 4M, 5M, 6M, 7M, 8M, 9M, 10M, 11M, 1Y, 15M, 18M, 21M, + 2Y, 3Y, 5Y, 10Y, 15Y, 20Y, 30Y + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + false + false + + + + + Absolute + + + 0.01, + 0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01 + + 1D, + 1W,2W,3W,1M,2M,3M,4M,5M,6M,7M,8M,9M,10M,11M,1Y,15M,18M,21M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,11Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y,60Y + + + Absolute + + 0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00 + + 1D,2D,3D,1W,2W,3W,1M,2M,3M,4M,5M,6M,7M,8M,9M,10M,11M,1Y,15M,18M,21M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y + + + + + + Absolute + + 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, + 0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01 + + 1D, + 1W,2W,3W,1M,2M,3M,4M,5M,6M,7M,8M,9M,10M,11M,1Y,15M,18M,21M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,11Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y,60Y + + + + Absolute + 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, + 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00 + 1D, 1W, 2W, 1M, 2M, 3M, 4M, 5M, 6M, 7M, 8M, 9M, 10M, 11M, 1Y, 15M, 18M, 21M, + 2Y, 3Y, 5Y, 10Y, 15Y, 20Y, 30Y + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + true + + + + + Absolute + + + 0.01, + 0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01 + + 1D, + 1W,2W,3W,1M,2M,3M,4M,5M,6M,7M,8M,9M,10M,11M,1Y,15M,18M,21M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,11Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y,60Y + + + Absolute + + 0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00 + + 1D,2D,3D,1W,2W,3W,1M,2M,3M,4M,5M,6M,7M,8M,9M,10M,11M,1Y,15M,18M,21M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y + + + + + + Absolute + + 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, + 0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01 + + 1D, + 1W,2W,3W,1M,2M,3M,4M,5M,6M,7M,8M,9M,10M,11M,1Y,15M,18M,21M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,11Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y,60Y + + + + Absolute + 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, + 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00 + 1D, 1W, 2W, 1M, 2M, 3M, 4M, 5M, 6M, 7M, 8M, 9M, 10M, 11M, 1Y, 15M, 18M, 21M, + 2Y, 3Y, 5Y, 10Y, 15Y, 20Y, 30Y + + + + + + + + Relative + 0.01 + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/EconomicStressAgentORE/oredata/Input/portfolio.xml b/EconomicStressAgentORE/oredata/Input/portfolio.xml new file mode 100644 index 0000000..cee4011 --- /dev/null +++ b/EconomicStressAgentORE/oredata/Input/portfolio.xml @@ -0,0 +1,379 @@ + + + + Swap + + A + + + + + + Floating + true + USD + + 102120250.0 + + EUR + 95000000.0 + FX-ECB-EUR-USD + 2 + + + true + true + + + ACT/360 + ModifiedFollowing + 2 + + USD-SOFR + + 0.0 + + false + 2 + + + + 2024-01-02 + 2026-01-02 + 3M + US,TARGET + ModifiedFollowing + ModifiedFollowing + Forward + + + + + + + + Floating + false + EUR + + 95000000.0 + + true + true + + + ACT/360 + ModifiedFollowing + 2 + + EUR-ESTER + + -0.00255 + + false + 2 + + + + 2024-01-02 + 2026-01-02 + 3M + US,TARGET + ModifiedFollowing + ModifiedFollowing + Forward + + + + + + + + + + Swap + + CPTY_A + CPTY_A + + + + + Floating + false + EUR + + 100000000 + + A360 + MF + + EUR-EURIBOR-6M + 2 + + 0.0 + + + 0.03 + + false + + + + 20240301 + 20260301 + 3M + TARGET + MF + MF + Backward + false + + + + + + + + + Swap + + CPTY_A + CPTY_A + + + + + Floating + false + EUR + + 100000000 + + A360 + MF + + EUR-EURIBOR-6M + 2 + + 0.0 + + false + + + + 20240301 + 20260301 + 3M + TARGET + MF + MF + Backward + false + + + + + + + + + CreditDefaultSwap + + CP + NS + + + + Underlying1 + Y + Y + + Fixed + true + USD + + 1000000 + + A360 + F + + + 0.05 + + + + + 2021-09-21 + 2026-12-20 + 3M + WeekendsOnly + F + U + CDS2015 + + + + + + + + + + ContractForDifference + + CPTY + NS + + party + 2024-03-05 + + + + + + EquityPosition + + 1000 + + Equity + .STOXX50 + 1.0 + RIC + + + + + + false + EUR + + + + + 2021-05-29 + + 2025-02-11 + + + + 3399.20 + EUR + + + + + ContractForDifference + + CPTY + NS + + party + 2024-03-05 + + + + + + EquityPosition + + 1000 + + Equity + .SPX + 1.0 + RIC + + + + + + false + USD + + + + + 2021-05-29 + + 2025-02-11 + + + + 6000.20 + USD + + + + + InflationSwap + + CPTY + NS + + party + 2024-03-05 + + + + + ZeroCouponFixed + true + EUR + + 184500.0000 + + A360 + MF + + + 2024-01-15 + 2029-01-15 + 0D + EUR + MF + Zero + + + + + 0.016500 + + Compounded + + + + CPI + false + EUR + + 184500 + + true + + + A360 + F + + + EUR + + 2029-01-15 + + + + + EUHICPXT + + 0 + + 114.4 + 2024-01-15 + 3M + Linear + true + + + + + \ No newline at end of file diff --git a/EconomicStressAgentORE/oredata/Input/pricingengine.xml b/EconomicStressAgentORE/oredata/Input/pricingengine.xml new file mode 100644 index 0000000..61f9129 --- /dev/null +++ b/EconomicStressAgentORE/oredata/Input/pricingengine.xml @@ -0,0 +1,1006 @@ + + + + DiscountedCashflows + + DiscountingBondTRSEngine + + 6M + IR_Analytical + + + + DiscountedCashflows + + DiscountingForwardBondEngine + + 3M + IR_Analytical + + + + DiscountedCashflows + + DiscountingSwapEngine + + IR_Analytical + + + + DiscountedCashflows + + DiscountingCrossCurrencySwapEngine + + FX_Analytical + + + + DiscountedCashflows + + DiscountingFxForwardEngine + + FX_Analytical + + + + GarmanKohlhagen + + AnalyticEuropeanEngine + + FX_Analytical + + + + GarmanKohlhagen + + FdBlackScholesVanillaEngine + + Douglas + 100 + 100 + 0 + true + FX_Analytical + + + + BlackBachelier + + + BlackBachelierSwaptionEngine + + IR_Analytical + + + + LGM + + Bootstrap + CoterminalDealStrike + 400,3M + 0.0 + HullWhite + 0.01 + Hagan + + 0.5 + 0.20 + + Grid + + 5.0 + 30 + 5.0 + 30 + IR_FD + + + + IborCapModel + + IborCapEngine + + IR_Analytical + + + + YYCapModel + + YYCapEngine + + IR_Analytical + + + + CpiCapModel + + CpiCapEngine + + true + IR_Analytical + + + + BlackOrBachelier + + BlackIborCouponPricer + + + Black76 + + 1.0 + IR_Analytical + + + + BlackOrBachelier + + BlackOvernightIndexedCouponPricer + + IR_Analytical + + + + BlackOrBachelier + + BlackAverageONIndexedCouponPricer + + IR_Analytical + + + + Black + + BlackAnalytic + + true + IR_Analytical + + + + Black + + BlackAnalytic + + true + IR_Analytical + + + + BlackOrBachelier + + BlackAverageBMACouponPricer + + IR_Analytical + + + + DiscountedCashflows + + DiscountingEquityForwardEngine + + EQ_Analytical + + + + BlackScholesMerton + + AnalyticEuropeanEngine + + EQ_Analytical + + + + BlackScholes + + GENERIC + + AnalyticEuropeanEngine + + EQ_Analytical + + + + BlackScholesMerton + + FdBlackScholesVanillaEngine + + Douglas + 100 + 100 + 0 + true + EQ_FD + + + + BlackScholesMerton + + AnalyticOutperformanceOptionEngine + + 32 + EQ_Analytical + + + + DiscountedCashflows + + 50Y + + DiscountingRiskyBondEngine + + 6M + IR_Analytical + + + + DiscountedCashflows + + MidPointCdsEngine + + IR_Analytical + + + + + LinearTSR + + LinearTSRPricer + + 0.0 + + RateBound + + 0.0001 + 2.0000 + + -2.0000 + 2.0000 + + 0.01 + + 0.0000001 + + 3.0 + IR_Semianalytical + + + + LinearTSR + + LinearTSRPricer + + 0.0 + 0.0001 + 2.0000 + -2.0000 + 2.0000 + false + IR_Semianalytical + + + + + BrigoMercurio + + 0.8 + 0.75 + 0.72 + 0.7 + + Analytic + + 16 + IR_Semianalytical + + + + DiscountedCashflows + + DiscountingCommodityForwardEngine + + COMM_Analytical + + + + DiscountedCashflows + + CommoditySwapEngine + + COMM_Analytical + + + + Black + + AnalyticalApproximation + + 2.05 + COMM_Analytical + + + + BlackScholes + + AnalyticEuropeanEngine + + COMM_Analytical + + + + BlackScholes + + CommoditySpreadOptionEngine + + 2.05 + COMM_Analytical + + + + BlackScholes + + AnalyticEuropeanForwardEngine + + COMM_Analytical + + + + Black + + AnalyticalApproximation + + 0 + COMM_Analytical + + + + DiscountedCashflows + + MidPointIndexCdsEngine + + Underlying + + IR_Analytical + + + + Black + + BlackIndexCdsOptionEngine + + + + Underlying + IR_Semianalytical + + + + + LGM + + Bootstrap + CoterminalDealStrike + 400,3M + 0.0 + HullWhite + 0.01 + HullWhite + 0.02 + + Grid + + Automatic + 20.0 + 5.0 + 30 + 5.0 + 30 + IR_Semianalytical + + + + LGM-FlexiSwap + + 0.0 + 2.0 + Bootstrap + CoterminalDealStrike + 400,3M + 0.0 + HullWhite + 0.01 + HullWhite + 0.02 + + Grid + + Automatic + 20.0 + 5.0 + 30 + 5.0 + 30 + IR_Semianalytical + + + + SensiTrade + + SensiTradeEngine + + 1Y,2Y + 0.01 + 6M,1Y,2Y,3Y,5Y,10Y,15Y,20Y,30Y + SensiTrade + + + + GaussCopula + + 0.0 + -5.0 + 5.0 + 64 + + Y + + + + Markit2020 + + 0.35,0.3,0.35 + + Bucketing + + 124 + + DeltaWeighted + + + + N + + N + + N + 3Y,5Y + CR_Semianalytical + + + + + BrigoMercurio + + GENERIC + + MC + + 10000 + 42 + Y + Spectral + IR_MC + + + + BlackScholesMerton + + false + + ReplicatingVarianceSwapEngine + + Segment + PriceThreshold + 1E-5 + 1000 + 1000 + 1E-10 + 500 + 0.1 + -5.0 + 5.0 + FX_Semianalytical + + + + BlackScholesMerton + + ReplicatingVarianceSwapEngine + + Segment + PriceThreshold + 1E-5 + 1000 + 1000 + 1E-10 + 500 + 0.1 + -5.0 + 5.0 + EQ_Semianalytical + + + + BlackScholesMerton + + ReplicatingVarianceSwapEngine + + Segment + PriceThreshold + 1E-5 + 1000 + 1000 + 1E-10 + 500 + 0.1 + -5.0 + 5.0 + COMM_Semianalytical + + + + GarmanKohlhagen + + AnalyticEuropeanEngine + + FX_Analytical + + + + GarmanKohlhagen + + AnalyticCashSettledEuropeanEngine + + FX_Analytical + + + + GarmanKohlhagen + + AnalyticBarrierEngine + + FX_Analytical + + + + GarmanKohlhagen + + FdBlackScholesBarrierEngine + + Douglas + 100 + 100 + 0 + FX_Analytical + + + + GarmanKohlhagen + + AnalyticDoubleBarrierEngine + + FX_Analytical + + + + GarmanKohlhagen + + AnalyticDoubleBarrierBinaryEngine + + FX_Analytical + + + + + GarmanKohlhagen + + AnalyticDigitalAmericanEngine + + FX_Analytical + + + + DiscountedCashflows + + DiscountingCommodityForwardEngine + + COMM_Analytical + + + + DefaultableEquityJumpDiffusion + + 0.0 + 1.0 + true + false + false + true + true + true + + FD + + true + 6M,1Y,2Y,3Y,4Y,5Y,7Y,10Y,15Y,20Y,25Y,30Y,40Y,50Y + 24 + 400 + 1E-5 + 1.5 + Alternating + 24 + 100 + 1E-4 + 1.5 + 0.5,0.55,0.6,0.65,0.7,0.75,0.8,0.85,0.9,0.95,1.0,1.05,1.1,1.15,1.2,1.25,1.5,1.75,2.0 + EQ_FD + + + + Black + + 0.0 + true + + Analytic + + false + 400 + 20 + IR_Semianalytical + + + + Black + + Analytic + + false + 400 + 20 + FX_Semianalytical + + + + LGM + + Bootstrap + CoterminalDealStrike + 400,3M + 0.0 + HullWhite + 0.01 + Hagan + 0.5 + 0.02 + + Grid + + 5.0 + 30 + 5.0 + 30 + 400 + 20 + IR_FD + + + + LGM + + Bootstrap + CoterminalATM + 400,3M + 0.0 + HullWhite + 0.01 + Hagan + 0.5 + 0.02 + 3M + + Grid + + 5.0 + 30 + 5.0 + 30 + 24 + IR_FD + + + + Generic + + + USD + false + 3M(1W),1Y(1M),5Y(3M),10Y(1Y),50Y(5Y) + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + true + true + 400,3M + Deal + BlackScholes + DK + + GaussianCam + GaussianCam + GaussianCam + GaussianCam + GaussianCam + GaussianCam + + Generic + + + MC + 10000 + 200 + 1E-4 + 1.5 + 0.1 + 9999 + true + 2 + 24 + false + 999.9 + + 6 + 6 + 6 + FD + FD + FD + true + true + true + true + true + true + true + true + true + true + true + true + true + true + EQ_MC + FX_MC + COMM_MC + EQ_MC + FX_MC + COMM_MC + EQ_FD + FX_FD + COMM_FD + EQ_MC + FX_MC + COMM_MC + EQ_MC + FX_MC + COMM_MC + HYBRID_MC + EQ_MC + FX_MC + COMM_MC + HYBRID_MC + EQ_MC + FX_MC + COMM_MC + HYBRID_MC + IR_MC + IR_MC + HYBRID_MC + HYBRID_MC + HYBRID_MC + HYBRID_MC + + + + BlackScholesMerton + + AnalyticCashSettledEuropeanEngine + + EQ_Analytical + + + + BlackScholes + + AnalyticCashSettledEuropeanEngine + + COMM_Analytical + + + + BlackScholes + + FdBlackScholesVanillaEngine + + Douglas + 100 + 100 + 0 + true + COMM_FD + + + + GarmanKohlhagen + + AnalyticCashSettledEuropeanEngine + + FX_Analytical + + + + Accrual + + true + + AccrualRepoEngine + + IR_Analytical + + + + BlackScholes + + MCScript + + MC + 10000 + 6 + 24 + false + false + EQ_MC + + + + Black + + BlackBondOptionEngine + + 6M + IR_Analytical + + + + BlackScholesMerton + + AnalyticBarrierEngine + + EQ_Analytical + + + + BlackScholesMerton + + AnalyticDoubleBarrierEngine + + EQ_Analytical + + + + BlackScholesMerton + + AnalyticEuropeanEngine + + EQ_Analytical + + + + BlackScholesMerton + + AnalyticDigitalAmericanEngine + + EQ_Analytical + + + + BlackScholes + + Intrinsic + + 24 + EQ_FD + + + + BlackScholes + + AnalyticEuropeanEngine + + EQ_Analytical + + + + ScriptedTrade + + ScriptedTrade + + EQ_MC + + + + ScriptedTrade + + ScriptedTrade + + EQ_MC + + + + ScriptedTrade + + ScriptedTrade + + FX_MC + + + + ScriptedTrade + + ScriptedTrade + + FX_MC + + + + OneFactorCopula + + MonteCarloCBOEngine + + 1000 + 20 + 42 + + 0.2 + CR_MC + + + + Black + + MonteCarlo + + 0 + 10000 + 42 + COMM_MC + + + + + true + false + USD + GENERIC + PM:XAUUSD + PM:XAGUSD + PM:XPTUSD + PM:XPDUSD + ^RED:[0-9A-Z]{9}$ + + diff --git a/EconomicStressAgentORE/oredata/Input/sensitivity.xml b/EconomicStressAgentORE/oredata/Input/sensitivity.xml new file mode 100644 index 0000000..33e1b81 --- /dev/null +++ b/EconomicStressAgentORE/oredata/Input/sensitivity.xml @@ -0,0 +1,158 @@ + + + + Absolute + 0.0001 + Forward + + 1D, + 1W,2W,3W,1M,2M,3M,4M,5M,6M,7M,8M,9M,10M,11M,1Y,15M,18M,21M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,11Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y,60Y + + + DEP, + OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS + true + + EUR-ON-DEPOSIT-ESTER + EUR-ESTER-OIS + + + + + Absolute + 0.0001 + Forward + + 1D,2D,3D,1W,2W,3W,1M,2M,3M,4M,5M,6M,7M,8M,9M,10M,11M,1Y,15M,18M,21M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y + + + FXF,FXF,FXF,FXF,FXF,FXF,FXF,FXF,FXF,FXF,FXF,FXF,FXF,FXF,FXF,FXF,FXF,FXF,FXF,FXF,FXF,XBS,XBS,XBS,XBS,XBS,XBS,XBS,XBS,XBS,XBS,XBS,XBS,XBS,XBS,XBS,XBS + true + + EUR-USD-ON-XCCY-BASIS + USD-ON-SOFR-DEPOSIT + USD-SOFR-OIS + EUR-USD-FX + + + + + + + Absolute + 0.0001 + Forward + + 1D, + 1W,2W,3W,1M,2M,3M,4M,5M,6M,7M,8M,9M,10M,11M,1Y,15M,18M,21M,2Y,3Y,4Y,5Y,6Y,7Y,8Y,9Y,10Y,11Y,12Y,15Y,20Y,25Y,30Y,40Y,50Y,60Y + + + DEP, + OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS,OIS + true + + EUR-ON-DEPOSIT-ESTER + EUR-ESTER-OIS + + + + + Absolute + 0.0001 + Forward + 6M, 7M, 8M,9M,10M,11M,1Y,15M,18M, 2Y, 3Y, 4Y, 5Y, 6Y, 7Y, 8Y, 9Y, 10Y, 12Y, + 15Y, 20Y, 25Y, 30Y, 40Y, 50Y, 60Y + + DEP, FRA, FRA,FRA,FRA,FRA,FRA,FRA,FRA, IRS, IRS, IRS, IRS, IRS, IRS, + IRS, IRS, IRS, IRS, IRS, IRS, IRS, IRS, IRS, IRS, IRS + false + EUR-ESTER + + EUR-DEPOSIT + EUR-6M-FRA + EUR-EURIBOR-6M-SWAP + + + + + Absolute + 0.0001 + Forward + 1D, 1W, 2W, 1M, 2M, 3M, 4M, 5M, 6M, 7M, 8M, 9M, 10M, 11M, 1Y,15M, 18M, 21M, + 2Y, 3Y, 5Y, 10Y, 15Y, 20Y, 30Y + + DEP, OIS, OIS, OIS, OIS, OIS, OIS, OIS, OIS, OIS, OIS, OIS, OIS, OIS, + OIS, OIS, OIS, OIS, OIS, OIS, OIS, OIS, OIS, OIS, OIS + true + + USD-ON-SOFR-DEPOSIT + USD-SOFR-OIS + + + + + + + + Relative + 0.01 + + + + + USD + Absolute + 0.0001 + Forward + 1Y, 2Y, 3Y, 5Y, 10Y + + CDS, CDS, CDS, CDS, CDS + false + + CDS-STANDARD-CONVENTIONS + + + + + + + Absolute + 0.0001 + 1Y, 2Y, 3Y, 4Y, 5Y, 6Y, 7Y, 8Y, 9Y + -0.01, 0, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.1 + EUR-EURIBOR-6M + + CapFloor + EUR-ESTER + + + + + + Absolute + 0.0001 + 6M,1Y,2Y,3Y,5Y,7Y,10Y,15Y,20Y,30Y + + ZIS,ZIS,ZIS,ZIS,ZIS,ZIS,ZIS,ZIS,ZIS,ZIS + false + + EUHICPXT_INFLATIONSWAP + + + + + + + Relative + 0.01 + Forward + + + Relative + 0.01 + Forward + + + false + true + \ No newline at end of file diff --git a/EconomicStressAgentORE/oredata/Input/simulation.xml b/EconomicStressAgentORE/oredata/Input/simulation.xml new file mode 100644 index 0000000..a6ee241 --- /dev/null +++ b/EconomicStressAgentORE/oredata/Input/simulation.xml @@ -0,0 +1,79 @@ + + + EUR + + EUR + USD + + + + 1D, 1W, 1M, 1Y, 10Y + LogLinear + FlatZero + + + + + USDEUR + + + + EUR-ESTER + EUR-EURIBOR-3M + EUR-EURIBOR-6M + USD-SOFR + + + true + ForwardVariance + + EUR-EURIBOR-6M + + 1Y, 2Y, 3Y, 4Y, 5Y, 6Y, 7Y, 8Y, 9Y, 10Y, 12Y, 15Y, 20Y, + 25Y, 30Y + + -0.015, -0.01, 0, 0.0005 + true + false + StickyStrike + StickyStrike + + + + Underlying1 + + 1Y, 2Y, 3Y, 5Y, 10Y + true + false + + TARGET + + FlatZero + + + + + EUHICPXT + + 6M,1Y,2Y,3Y,5Y,7Y,10Y,15Y,20Y + + + + true + + RIC:.SPX + RIC:.STOXX50 + + 2W, 1M, 3M, 6M, 1Y, 2Y, 3Y, 5Y, 10Y, 15Y, 20Y, 30Y + + + 0 + + + 0 + + + \ No newline at end of file diff --git a/EconomicStressAgentORE/oredata/Input/todaysmarket.xml b/EconomicStressAgentORE/oredata/Input/todaysmarket.xml new file mode 100644 index 0000000..986e4f9 --- /dev/null +++ b/EconomicStressAgentORE/oredata/Input/todaysmarket.xml @@ -0,0 +1,80 @@ + + + default + inccy + default + default + default + default + default + default + default + default + default + default + default + default + default + default + default + default + default + default + default + default + + + default + default + default + default + default + default + default + default + default + default + default + default + default + default + default + default + default + default + default + default + default + default + + + Yield/EUR/EUR-ESTER + Yield/USD/USD-IN-EUR + + + Yield/EUR/EUR-ESTER + Yield/USD/USD-SOFR + + + Yield/EUR/EUR-ESTER + Yield/EUR/EUR-EURIBOR-6M + Yield/EUR/EUR-EURIBOR-3M + Yield/USD/USD-SOFR + + + FX/EUR/USD + + + CapFloorVolatility/EUR/EUR + + + Default/USD/Underlying1 + + + Equity/USD/RIC:.SPX + Equity/EUR/RIC:.STOXX50 + + + Inflation/EUHICPXT/EUHICPXT_ZC_Swaps + + \ No newline at end of file diff --git a/EconomicStressAgentORE/oredata/ore.xml b/EconomicStressAgentORE/oredata/ore.xml new file mode 100644 index 0000000..3810927 --- /dev/null +++ b/EconomicStressAgentORE/oredata/ore.xml @@ -0,0 +1,64 @@ + + + + 2024-03-05 + Input + Output + log.txt + 31 + marketdata.csv + fixings.csv + N + curveconfig.xml + conventions.xml + todaysmarket.xml + pricingengine.xml + portfolio.xml + None + + + inccy + default + default + default + default + + + + Y + EUR + npv.csv + + + Y + flows.csv + + + Y + simulation.xml + sensitivity.xml + pricingengine.xml + scenario.csv + sensitivity.csv + crossgamma.csv + parsensi.csv + 0.000001 + + + Y + default + 240,1M + curves.csv + + + Y + simulation.xml + parstresstest.xml + sensitivity.xml + pricingengine.xml + stresstest.csv + 0.000001 + zeroStressScenarioData.xml + + + \ No newline at end of file diff --git a/EconomicStressAgentORE/requirements.txt b/EconomicStressAgentORE/requirements.txt new file mode 100644 index 0000000..d0ab6b1 --- /dev/null +++ b/EconomicStressAgentORE/requirements.txt @@ -0,0 +1,5 @@ +openai>=1.0.0 +python-dotenv>=1.0.0 +pandas>=2.0.0 +click>=8.0.0 +open-source-risk-engine>=1.8 \ No newline at end of file diff --git a/EconomicStressAgentORE/scenario_analyzer.py b/EconomicStressAgentORE/scenario_analyzer.py new file mode 100644 index 0000000..16d4925 --- /dev/null +++ b/EconomicStressAgentORE/scenario_analyzer.py @@ -0,0 +1,166 @@ +""" +scenario_analyzer.py β€” Step 1: map a free-text economic scenario to concrete +market shifts using an LLM + an in-process historical knowledge base. +""" + +from __future__ import annotations + +import json +import re +from typing import Any + +from openai import OpenAI + +import config +from historical_scenarios import ScenarioKnowledgeBase + +# ── System prompt ───────────────────────────────────────────────────────────── +_SYSTEM_PROMPT = """\ +You are a quantitative risk analyst specialising in macro stress testing. + +You are given a knowledge base of historical economic crises and their +approximate market impacts, and a user-described economic scenario. + +Your task is to: +1. Identify which historical episodes best match the described scenario + (pick 1-3 episodes). +2. Derive a *weighted average* of the market shifts from those episodes that + best represents the described scenario. You may scale the shifts up or down + to reflect the described severity. +3. Return a JSON object (and nothing else) with this exact schema: + +{ + "matched_scenarios": ["", ...], + "reasoning": "", + "shifts": { + "rates": { + "": { "": , ... } + }, + "fx": { "": }, + "equity": { + "": + }, + "credit": { + "": { "": , ... } + } + } +} + +Keys: +- rates: keyed by ISO currency (EUR, USD, …). Values are absolute changes + in swap/yield rates in decimal (e.g. -0.015 = -150 bps, +0.010 = +100 bps). + Tenors: 1Y, 2Y, 3Y, 5Y, 10Y, 30Y (at minimum include 1Y, 2Y, 5Y, 10Y, 30Y). +- fx: keyed by pair (e.g. "EURUSD"). Value is the absolute change in the spot + rate (e.g. -0.08 means EUR falls 8 big figures vs USD). +- equity: keyed by Currency (e.g. "EUR", "USD") or sector override + (e.g. "Tech", "Index"). Values are relative (fractional) changes + (e.g. -0.25 = -25 %). Always include at least the currency-level keys. +- credit: keyed by Currency or sector (e.g. "EUR", "USD", + "SeniorUnsecured", "Sovereign"). Values are dicts of tenor β†’ absolute + shift to hazard/CDS spread (positive = widening, e.g. 0.02 = +200 bps). + Tenors: 1Y, 2Y, 3Y, 5Y, 10Y. + +Return ONLY the JSON object β€” no markdown fences, no extra text. +""" + + +def _build_user_message( + scenario_description: str, + knowledge_base: ScenarioKnowledgeBase, +) -> str: + """Combine the historical knowledge base with the user's scenario.""" + kb = knowledge_base.get_scenarios_text() + return ( + f"## Historical Scenarios Knowledge Base\n\n{kb}\n\n" + f"## User-Described Scenario\n\n{scenario_description}\n\n" + "Analyse the described scenario and return the JSON object as instructed." + ) + + +def analyze( + scenario_description: str, + knowledge_base: ScenarioKnowledgeBase, +) -> dict[str, Any]: + """ + Analyse *scenario_description* and return a structured dict of market shifts. + + Returns + ------- + dict with keys: + matched_scenarios : list[str] + reasoning : str + shifts : dict (fx, equity, rates_eur, rates_usd) + """ + if not config.OPENAI_API_KEY: + raise EnvironmentError( + "OPENAI_API_KEY is not set. Please add it to your .env file." + ) + + client = OpenAI(api_key=config.OPENAI_API_KEY) + + response = client.chat.completions.create( + model=config.OPENAI_MODEL, + messages=[ + {"role": "system", "content": _SYSTEM_PROMPT}, + {"role": "user", "content": _build_user_message(scenario_description, knowledge_base)}, + ], + temperature=0.2, + response_format={"type": "json_object"}, + ) + + raw = response.choices[0].message.content or "{}" + + # Strip accidental markdown fences if the model includes them + raw = re.sub(r"^```(?:json)?\s*", "", raw.strip()) + raw = re.sub(r"```\s*$", "", raw.strip()) + + result: dict[str, Any] = json.loads(raw) + _validate(result) + return result + + +def _validate(result: dict[str, Any]) -> None: + """Raise ValueError if the returned JSON is missing required keys.""" + required_top = {"matched_scenarios", "reasoning", "shifts"} + missing = required_top - result.keys() + if missing: + raise ValueError(f"LLM response missing keys: {missing}") + + shifts = result["shifts"] + required_shifts = {"rates", "fx", "equity", "credit"} + missing_shifts = required_shifts - shifts.keys() + if missing_shifts: + raise ValueError(f"shifts missing sub-keys: {missing_shifts}") + + +def format_shifts(result: dict[str, Any]) -> str: + """Return a human-readable string of the proposed shifts.""" + s = result["shifts"] + lines = [ + "Matched scenarios:", + *[f" β€’ {name}" for name in result["matched_scenarios"]], + "", + f"Reasoning: {result['reasoning']}", + "", + "Proposed market shifts:", + ] + + # FX + for pair, v in s.get("fx", {}).items(): + lines.append(f" FX {pair}: {v:+.4f}") + + # Equity + for key, v in s.get("equity", {}).items(): + lines.append(f" Equity {key}: {v:+.1%}") + + # Rates + for ccy, tenors in s.get("rates", {}).items(): + parts = " ".join(f"{t} {v*10000:+.0f}bp" for t, v in tenors.items()) + lines.append(f" Rates {ccy}: {parts}") + + # Credit + for key, tenors in s.get("credit", {}).items(): + parts = " ".join(f"{t} {v*10000:+.0f}bp" for t, v in tenors.items()) + lines.append(f" Credit {key}: {parts}") + + return "\n".join(lines) diff --git a/EconomicStressAgentORE/stresstest_builder.py b/EconomicStressAgentORE/stresstest_builder.py new file mode 100644 index 0000000..ce22426 --- /dev/null +++ b/EconomicStressAgentORE/stresstest_builder.py @@ -0,0 +1,323 @@ +""" +stresstest_builder.py β€” Step 2: convert structured market shifts into an +ORE-compatible par stress test XML file. + +This version is **market-driven**: it accepts a ``MarketStructure`` (from +``todaysmarket_analyzer.parse()``) and a sector mapping so that every curve +present in todaysmarket.xml is automatically stressed. +""" + +from __future__ import annotations + +from pathlib import Path +from typing import Any +from xml.dom import minidom +from xml.etree import ElementTree as ET + +import config +import ORE + +from todaysmarket_analyzer import ( + CurveInfo, + MarketStructure, + SectorEntry, + load_sector_mapping, + resolve_credit_shifts, + resolve_equity_shift, +) + +# ── Tenor helpers ───────────────────────────────────────────────────────────── + +def _tenor_to_year_fraction(tenor: str, today, day_counter) -> float: + """Convert an ORE tenor string to a year-fraction relative to *today*.""" + + return day_counter.yearFraction(today, today + ORE.Period(tenor.strip())) + + +def interpolate_shift( + tenors: list[str], scenario_shifts: dict[str, float] +) -> list[float]: + """ + Linear interpolation with flat extrapolation of scenario key-tenor + shifts onto an arbitrary ORE tenor grid, using ORE's own date/period + logic and ``LinearInterpolation``. + """ + if not scenario_shifts: + return [0.0] * len(tenors) + + today = ORE.Settings.instance().evaluationDate + dc = ORE.Actual365Fixed() + + # Build sorted anchor arrays from scenario shifts + anchors = sorted( + (_tenor_to_year_fraction(t, today, dc), v) + for t, v in scenario_shifts.items() + ) + xs = [a[0] for a in anchors] + ys = [a[1] for a in anchors] + + interp = ORE.LinearInterpolation(xs, ys) + + def _lookup(t_yf: float) -> float: + if t_yf <= xs[0]: + return ys[0] + if t_yf >= xs[-1]: + return ys[-1] + return interp(t_yf) + + return [ + _lookup(_tenor_to_year_fraction(t, today, dc)) + for t in tenors + ] + + +# ── XML helpers ─────────────────────────────────────────────────────────────── + +def _shifts_str(values: list[float]) -> str: + """Format a list of shift values as a comma-separated string.""" + return ", ".join(f"{v:.6f}" for v in values) + + +def _add_curve_element( + parent: ET.Element, + tag: str, + attr: dict[str, str], + tenors: list[str], + shifts: list[float], +) -> None: + """Append a or element with shifts.""" + el = ET.SubElement(parent, tag, attr) + ET.SubElement(el, "ShiftType").text = "Absolute" + ET.SubElement(el, "Shifts").text = _shifts_str(shifts) + ET.SubElement(el, "ShiftTenors").text = ", ".join(tenors) + + +def _prettify(element: ET.Element) -> str: + """Return a pretty-printed XML string (no blank lines).""" + rough = ET.tostring(element, encoding="unicode") + reparsed = minidom.parseString(rough) + xml = reparsed.toprettyxml(indent=" ", encoding=None) + # minidom inserts blank lines when the source already has whitespace nodes + return "\n".join(line for line in xml.splitlines() if line.strip()) + + +# ── Main builder ────────────────────────────────────────────────────────────── + +def _resolve_rate_shifts( + ccy: str, shifts: dict[str, Any] +) -> dict[str, float]: + """Return the rate tenor-shift dict for a currency (empty if missing).""" + rates: dict = shifts.get("rates", {}) + return rates.get(ccy, {}) + + +def build( + shifts: dict[str, Any], + market: MarketStructure | None = None, + sector_map: dict[tuple[str, str], SectorEntry] | None = None, + output_path: Path | None = None, + scenario_id: str = "agent_scenario", + scenario_label: str | None = None, +) -> Path: + """ + Build the ORE stress test XML from *shifts* (generic schema) and the + discovered *market* structure. + + Parameters + ---------- + shifts : generic schema ``{rates, fx, equity, credit}`` + market : MarketStructure β€” if None, parsed from todaysmarket.xml + sector_map : sector mapping β€” if None, loaded from config + output_path : where to write the XML + scenario_id : ``id`` attribute of the ```` element + scenario_label : optional human-readable label embedded as a comment + """ + from todaysmarket_analyzer import parse as tm_parse # local to avoid circular + + if market is None: + market = tm_parse() + if sector_map is None: + sector_map = load_sector_mapping() + if output_path is None: + output_path = config.AGENT_STRESS_XML + + output_path = Path(output_path) + output_path.parent.mkdir(parents=True, exist_ok=True) + + # Root + root = ET.Element("StressTesting") + ET.SubElement(root, "UseSpreadedTermStructures").text = "true" + + st = ET.SubElement(root, "StressTest", {"id": scenario_id}) + if scenario_label: + st.append(ET.Comment(f" {scenario_label} ")) + + par = ET.SubElement(st, "ParShifts") + ET.SubElement(par, "IRCurves").text = "false" + ET.SubElement(par, "SurvivalProbability").text = "false" + ET.SubElement(par, "CapFloorVolatilities").text = "false" + + # ── Discount curves ──────────────────────────────────────────────── + disc_el = ET.SubElement(st, "DiscountCurves") + for curve in market.discount_curves: + rate_shifts = _resolve_rate_shifts(curve.currency, shifts) + if not rate_shifts: + continue + tenors = config.STANDARD_RATE_TENORS + interp = interpolate_shift(tenors, rate_shifts) + _add_curve_element(disc_el, "DiscountCurve", + {"ccy": curve.currency}, tenors, interp) + + # ── Index curves ─────────────────────────────────────────────────── + idx_el = ET.SubElement(st, "IndexCurves") + for curve in market.index_curves: + rate_shifts = _resolve_rate_shifts(curve.currency, shifts) + if not rate_shifts: + continue + tenors = config.STANDARD_RATE_TENORS + interp = interpolate_shift(tenors, rate_shifts) + _add_curve_element(idx_el, "IndexCurve", + {"index": curve.name}, tenors, interp) + + # ── Yield curves ─────────────────────────────────────────────────── + yc_el = ET.SubElement(st, "YieldCurves") + for curve in market.yield_curves: + rate_shifts = _resolve_rate_shifts(curve.currency, shifts) + if not rate_shifts: + continue + tenors = config.STANDARD_RATE_TENORS + interp = interpolate_shift(tenors, rate_shifts) + _add_curve_element(yc_el, "YieldCurve", + {"name": curve.name}, tenors, interp) + + # ── FX spots ─────────────────────────────────────────────────────── + fx_el = ET.SubElement(st, "FxSpots") + fx_shifts: dict[str, float] = shifts.get("fx", {}) + for pair in market.fx_pairs: + foreign = pair[:3] + domestic = pair[3:] + # Try both conventions + raw_shift = fx_shifts.get(pair, fx_shifts.get(domestic + foreign, None)) + if raw_shift is None: + continue + # ORE uses inverted convention (USDEUR) + ore_pair = domestic + foreign + if pair == ore_pair: + relative_shift = raw_shift + else: + # EURUSD scenario β†’ USDEUR in ORE: negate + relative_shift = -raw_shift + spot_el = ET.SubElement(fx_el, "FxSpot", {"ccypair": ore_pair}) + ET.SubElement(spot_el, "ShiftType").text = "Relative" + ET.SubElement(spot_el, "ShiftSize").text = f"{relative_shift:.6f}" + + # ── FxVolatilities (empty) ──────────────────────────────────────── + ET.SubElement(st, "FxVolatilities") + + # ── SwaptionVolatilities (empty) ────────────────────────────────── + ET.SubElement(st, "SwaptionVolatilities") + + # ── CapFloorVolatilities (empty) ────────────────────────────────── + ET.SubElement(st, "CapFloorVolatilities") + + # ── Equity spots ───────────────────────────────────────────────── + eq_el = ET.SubElement(st, "EquitySpots") + for curve in market.equity_curves: + rel_shift = resolve_equity_shift(curve, shifts, sector_map) + if rel_shift == 0.0: + continue + eq_spot = ET.SubElement(eq_el, "EquitySpot", {"equity": curve.name}) + ET.SubElement(eq_spot, "ShiftType").text = "Relative" + ET.SubElement(eq_spot, "ShiftSize").text = f"{rel_shift:.6f}" + + # ── EquityVolatilities (empty) ──────────────────────────────────── + ET.SubElement(st, "EquityVolatilities") + + # ── SecuritySpreads, RecoveryRates (empty) ──────────────────────── + ET.SubElement(st, "SecuritySpreads") + ET.SubElement(st, "RecoveryRates") + + # ── Survival probabilities ──────────────────────────────────────── + surv_el = ET.SubElement(st, "SurvivalProbabilities") + for curve in market.default_curves: + credit_tenor_shifts = resolve_credit_shifts(curve, shifts, sector_map) + if not credit_tenor_shifts: + continue + tenors = config.STANDARD_CREDIT_TENORS + interp = interpolate_shift(tenors, credit_tenor_shifts) + sp = ET.SubElement(surv_el, "SurvivalProbability", {"name": curve.name}) + ET.SubElement(sp, "ShiftType").text = "Absolute" + ET.SubElement(sp, "Shifts").text = _shifts_str(interp) + ET.SubElement(sp, "ShiftTenors").text = ", ".join(tenors) + + # ── Serialise ───────────────────────────────────────────────────── + xml_str = _prettify(root) + lines = xml_str.splitlines() + if lines and lines[0].startswith(" Path: + """ + Write a minimal ore_agent.xml that activates only the stress analytic and + points to the agent-generated stress test configuration. + + Parameters + ---------- + base_ore_xml : source ore.xml to base settings on + (defaults to config.ORE_WORKSPACE / "ore.xml") + output_ore_xml : where to write the agent ore config + (defaults to config.AGENT_ORE_XML) + stress_config_file : filename (relative to Input/) of the stress XML + + Returns + ------- + Path to the written file. + """ + if base_ore_xml is None: + base_ore_xml = config.ORE_WORKSPACE / "ore.xml" + if output_ore_xml is None: + output_ore_xml = config.AGENT_ORE_XML + + base_ore_xml = Path(base_ore_xml) + output_ore_xml = Path(output_ore_xml) + + tree = ET.parse(base_ore_xml) + root = tree.getroot() + + # Strip whitespace-only text/tail so _prettify produces clean output + for el in root.iter(): + if el.text and not el.text.strip(): + el.text = None + if el.tail and not el.tail.strip(): + el.tail = None + + # Disable every analytic except stress; update stress config file + analytics = root.find("Analytics") + if analytics is not None: + for analytic in analytics.findall("Analytic"): + atype = analytic.get("type", "") + if atype == "stress": + for param in analytic.findall("Parameter"): + if param.get("name") == "stressConfigFile": + param.text = stress_config_file + if param.get("name") == "active": + param.text = "Y" + else: + for param in analytic.findall("Parameter"): + if param.get("name") == "active": + param.text = "N" + + xml_str = _prettify(root) + lines = xml_str.splitlines() + if lines and lines[0].startswith("' + output_ore_xml.write_text("\n".join(lines), encoding="utf-8") + + return output_ore_xml diff --git a/EconomicStressAgentORE/test_integration.py b/EconomicStressAgentORE/test_integration.py new file mode 100644 index 0000000..50b2c2d --- /dev/null +++ b/EconomicStressAgentORE/test_integration.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +"""Quick integration test for the generic schema refactoring.""" + +import config +import todaysmarket_analyzer +import stresstest_builder +import ore_runner +from pathlib import Path +from historical_scenarios import ScenarioKnowledgeBase + +# 1. Test scenario loading +kb = ScenarioKnowledgeBase(config.DATA_DIR / "scenarios.json") +print(f"Loaded {len(kb)} scenarios") +print(f"First: {kb[0]['name']}") +print(f"Schema keys: {list(kb[0]['shifts'].keys())}") +print() + +# 2. Test market structure parsing +ms = todaysmarket_analyzer.parse() +print(todaysmarket_analyzer.format_market_structure(ms)) +print() + +# 3. Test sector mapping +sector_map = todaysmarket_analyzer.load_sector_mapping() +print(f"Sector map entries: {len(sector_map)}") +for k, v in sector_map.items(): + print(f" {k} -> {v}") +print() + +# 4. Test equity resolution with first scenario +shifts = kb[0]["shifts"] +for eq in ms.equity_curves: + s = todaysmarket_analyzer.resolve_equity_shift(eq, shifts, sector_map) + print(f"Equity {eq.name} ({eq.currency}): shift={s:+.0%}") + +# 5. Test credit resolution +for dc in ms.default_curves: + c = todaysmarket_analyzer.resolve_credit_shifts(dc, shifts, sector_map) + print(f"Credit {dc.name} ({dc.currency}): {c}") +print() + +# 6. Build the stress test XML +path = stresstest_builder.build(shifts=shifts, market=ms, sector_map=sector_map) +print(f"Stress XML written to: {path}") + +# 7. Validate XML +from xml.etree import ElementTree as ET +tree = ET.parse(path) +root = tree.getroot() +print(f"Root tag: {root.tag}") +stress_tests = root.findall("StressTest") +print(f"StressTests: {len(stress_tests)}") +for st in stress_tests: + disc = st.findall(".//DiscountCurve") + idx = st.findall(".//IndexCurve") + eq = st.findall(".//EquitySpot") + fx = st.findall(".//FxSpot") + surv = st.findall(".//SurvivalProbability") + print(f" Discount curves: {len(disc)}") + print(f" Index curves: {len(idx)}") + print(f" FX spots: {len(fx)}") + print(f" Equity spots: {len(eq)}") + print(f" Survival probs: {len(surv)}") + +print("\nβœ“ All imports and build OK!") + +# 8. Build ore_agent.xml (stress-only ORE config) +ore_workspace = config.ORE_WORKSPACE +ore_agent_xml = stresstest_builder.build_ore_config( + base_ore_xml=ore_workspace / "ore.xml", + stress_config_file="agent_stress.xml", +) +print(f"\nORE agent config written to: {ore_agent_xml}") + +# 9. Run ORE via Python bindings +csv_path = ore_runner.run(ore_xml=ore_agent_xml, workspace=ore_workspace) +print(f"ORE completed β€” output: {csv_path}") + +# 10. Read and display stress test results +import pandas as pd +import io + +with csv_path.open() as f: + content = f.read().replace("#TradeId", "TradeId", 1) + +df = pd.read_csv(io.StringIO(content)) +df.columns = [c.strip() for c in df.columns] +df["PnL"] = df["Scenario NPV"] - df["Base NPV"] + +print(f"\nStress test results ({len(df)} rows):") +print(f" Scenarios: {df['ScenarioLabel'].unique().tolist()}") +print(f" Total Base NPV: {df['Base NPV'].sum():>14,.0f}") +print(f" Total Stressed NPV: {df['Scenario NPV'].sum():>14,.0f}") +print(f" Total P&L: {df['PnL'].sum():>+14,.0f}") +print() +print(df[["TradeId", "Base NPV", "Scenario NPV", "PnL"]].to_string(index=False)) + +print("\nβœ“ Full pipeline (build + ORE run + output) OK!") diff --git a/EconomicStressAgentORE/todaysmarket_analyzer.py b/EconomicStressAgentORE/todaysmarket_analyzer.py new file mode 100644 index 0000000..ba4cc62 --- /dev/null +++ b/EconomicStressAgentORE/todaysmarket_analyzer.py @@ -0,0 +1,436 @@ +""" +todaysmarket_analyzer.py β€” Parse todaysmarket.xml to extract all market +entities (curves, FX pairs, equities, credit names, inflation indices). + +Returns a MarketStructure dataclass consumed by the stress test builder +and can generate a matching simulation.xml. +""" + +from __future__ import annotations + +import csv +from dataclasses import dataclass, field +from pathlib import Path +from xml.dom import minidom +from xml.etree import ElementTree as ET + +import config + + +# ── Data classes ────────────────────────────────────────────────────────────── + +@dataclass +class CurveInfo: + """A single market curve / entity extracted from todaysmarket.xml.""" + name: str # e.g. "EUR-ESTER", "Underlying1", "RIC:.SPX" + currency: str # e.g. "EUR", "USD" + curve_type: str # "discount", "index", "yield", "default", "equity", "inflation" + spec: str # full ORE spec, e.g. "Yield/EUR/EUR-ESTER" + + +@dataclass +class MarketStructure: + """Complete market topology derived from todaysmarket.xml.""" + base_currency: str = "EUR" + discount_curves: list[CurveInfo] = field(default_factory=list) + index_curves: list[CurveInfo] = field(default_factory=list) + yield_curves: list[CurveInfo] = field(default_factory=list) + default_curves: list[CurveInfo] = field(default_factory=list) + equity_curves: list[CurveInfo] = field(default_factory=list) + fx_pairs: list[str] = field(default_factory=list) + inflation_indices: list[CurveInfo] = field(default_factory=list) + capfloor_vols: list[CurveInfo] = field(default_factory=list) + + @property + def currencies(self) -> set[str]: + """Union of all currencies found across every curve.""" + ccys: set[str] = set() + for curves in (self.discount_curves, self.index_curves, + self.yield_curves, self.default_curves, + self.equity_curves, self.inflation_indices): + for c in curves: + ccys.add(c.currency) + # Also extract from FX pairs (first 3 and last 3 chars) + for pair in self.fx_pairs: + ccys.add(pair[:3]) + ccys.add(pair[3:]) + return ccys + + +# ── Sector mapping ──────────────────────────────────────────────────────────── + +@dataclass +class SectorEntry: + entity_type: str # "equity" | "credit" + name: str # ORE entity name + currency: str # optional override (empty β†’ auto-detect) + sector: str # e.g. "Tech", "SeniorUnsecured" + + +def load_sector_mapping(csv_path: Path | None = None) -> dict[tuple[str, str], SectorEntry]: + """ + Load sector_mapping.csv and return a lookup dict keyed by (type, name). + + Parameters + ---------- + csv_path : path to CSV file; defaults to config.SECTOR_MAPPING_CSV + + Returns + ------- + dict mapping (entity_type, entity_name) β†’ SectorEntry + """ + if csv_path is None: + csv_path = config.SECTOR_MAPPING_CSV + csv_path = Path(csv_path) + + mapping: dict[tuple[str, str], SectorEntry] = {} + if not csv_path.exists(): + return mapping + + with csv_path.open(newline="") as f: + reader = csv.DictReader(f) + for row in reader: + entry = SectorEntry( + entity_type=row["type"].strip(), + name=row["name"].strip(), + currency=row.get("currency", "").strip(), + sector=row["sector"].strip(), + ) + mapping[(entry.entity_type, entry.name)] = entry + return mapping + + +def resolve_equity_shift( + curve: CurveInfo, + shifts: dict, + sector_map: dict[tuple[str, str], SectorEntry], +) -> float: + """Resolve the equity shift for a given equity curve. + + Priority: sector override β†’ currency default β†’ 0. + """ + equity_shifts: dict = shifts.get("equity", {}) + entry = sector_map.get(("equity", curve.name)) + if entry and entry.sector in equity_shifts: + return equity_shifts[entry.sector] + if curve.currency in equity_shifts: + return equity_shifts[curve.currency] + return 0.0 + + +def resolve_credit_shifts( + curve: CurveInfo, + shifts: dict, + sector_map: dict[tuple[str, str], SectorEntry], +) -> dict[str, float]: + """Resolve the credit tenor shifts for a given default curve. + + Priority: sector override β†’ currency default β†’ empty dict. + """ + credit_shifts: dict = shifts.get("credit", {}) + entry = sector_map.get(("credit", curve.name)) + if entry and entry.sector in credit_shifts: + return credit_shifts[entry.sector] + if curve.currency in credit_shifts: + return credit_shifts[curve.currency] + return {} + + +# ── todaysmarket.xml parser ─────────────────────────────────────────────────── + +def _ccy_from_spec(spec: str) -> str: + """Extract currency from an ORE curve spec like 'Yield/EUR/EUR-ESTER'.""" + parts = spec.split("/") + if len(parts) >= 2: + return parts[1] + return "" + + +def _ccy_from_index_name(name: str) -> str: + """Extract currency from an index name like 'EUR-ESTER' or 'USD-SOFR'.""" + parts = name.split("-") + if parts and len(parts[0]) == 3 and parts[0].isalpha(): + return parts[0].upper() + return "" + + +def parse(todaysmarket_xml: Path | None = None) -> MarketStructure: + """ + Parse todaysmarket.xml and return the full market structure. + + Parameters + ---------- + todaysmarket_xml : path to todaysmarket.xml; defaults to + config.ORE_INPUT_DIR / "todaysmarket.xml" + """ + if todaysmarket_xml is None: + todaysmarket_xml = config.ORE_INPUT_DIR / "todaysmarket.xml" + todaysmarket_xml = Path(todaysmarket_xml) + + tree = ET.parse(todaysmarket_xml) + root = tree.getroot() + + ms = MarketStructure() + + # ── Discount curves ─────────────────────────────────────────────── + for dc_section in root.findall(".//DiscountingCurves[@id='default']"): + for dc in dc_section.findall("DiscountingCurve"): + ccy = dc.get("currency", "") + spec = (dc.text or "").strip() + name = spec.split("/")[-1] if spec else ccy + ms.discount_curves.append( + CurveInfo(name=name, currency=ccy, curve_type="discount", spec=spec) + ) + + # ── Index forwarding curves ─────────────────────────────────────── + for idx_section in root.findall(".//IndexForwardingCurves[@id='default']"): + for idx in idx_section.findall("Index"): + name = idx.get("name", "") + spec = (idx.text or "").strip() + ccy = _ccy_from_index_name(name) or _ccy_from_spec(spec) + ms.index_curves.append( + CurveInfo(name=name, currency=ccy, curve_type="index", spec=spec) + ) + + # ── Yield curves ────────────────────────────────────────────────── + for yc_section in root.findall(".//YieldCurves[@id='default']"): + for yc in yc_section.findall("YieldCurve"): + name = yc.get("name", "") + spec = (yc.text or "").strip() + ccy = _ccy_from_spec(spec) + ms.yield_curves.append( + CurveInfo(name=name, currency=ccy, curve_type="yield", spec=spec) + ) + + # ── Default curves ──────────────────────────────────────────────── + for dc_section in root.findall(".//DefaultCurves[@id='default']"): + for dc in dc_section.findall("DefaultCurve"): + name = dc.get("name", "") + spec = (dc.text or "").strip() + ccy = _ccy_from_spec(spec) + ms.default_curves.append( + CurveInfo(name=name, currency=ccy, curve_type="default", spec=spec) + ) + + # ── Equity curves ───────────────────────────────────────────────── + for eq_section in root.findall(".//EquityCurves[@id='default']"): + for eq in eq_section.findall("EquityCurve"): + name = eq.get("name", "") + spec = (eq.text or "").strip() + ccy = _ccy_from_spec(spec) + ms.equity_curves.append( + CurveInfo(name=name, currency=ccy, curve_type="equity", spec=spec) + ) + + # ── FX pairs ────────────────────────────────────────────────────── + for fx_section in root.findall(".//FxSpots[@id='default']"): + for fx in fx_section.findall("FxSpot"): + pair = fx.get("pair", "") + if pair: + ms.fx_pairs.append(pair) + + # ── Inflation indices ───────────────────────────────────────────── + for zi_section in root.findall(".//ZeroInflationIndexCurves[@id='default']"): + for zi in zi_section.findall("ZeroInflationIndexCurve"): + name = zi.get("name", "") + spec = (zi.text or "").strip() + # Inflation specs: Inflation/EUHICPXT/... β†’ ccy heuristic from name prefix + ccy = name[:2] + "R" if name[:2] in ("EU", "US", "GB") else "" + if name.startswith("EU"): + ccy = "EUR" + elif name.startswith("US"): + ccy = "USD" + elif name.startswith("GB"): + ccy = "GBP" + else: + ccy = _ccy_from_spec(spec) + ms.inflation_indices.append( + CurveInfo(name=name, currency=ccy, curve_type="inflation", spec=spec) + ) + + # ── CapFloor volatilities ───────────────────────────────────────── + for cf_section in root.findall(".//CapFloorVolatilities[@id='default']"): + for cf in cf_section.findall("CapFloorVolatility"): + key = cf.get("key", "") + spec = (cf.text or "").strip() + ccy = _ccy_from_index_name(key) or _ccy_from_spec(spec) + ms.capfloor_vols.append( + CurveInfo(name=key, currency=ccy, curve_type="capfloor_vol", spec=spec) + ) + + # Derive base currency from first discount curve or default to EUR + if ms.discount_curves: + ms.base_currency = ms.discount_curves[0].currency + else: + ms.base_currency = "EUR" + + return ms + + +# ── Simulation XML generation ───────────────────────────────────────────────── + +def _prettify(element: ET.Element) -> str: + """Return a pretty-printed XML string (no XML declaration).""" + rough = ET.tostring(element, encoding="unicode") + reparsed = minidom.parseString(rough) + pretty = reparsed.toprettyxml(indent=" ") + # Remove the XML declaration added by minidom + lines = pretty.splitlines() + if lines and lines[0].startswith(" Path: + """ + Generate a simulation.xml from the discovered MarketStructure. + + Uses standard tenor grids from config for each curve type. + + Parameters + ---------- + market : MarketStructure from parse() + output_path : where to write; defaults to config.ORE_INPUT_DIR / "simulation.xml" + """ + if output_path is None: + output_path = config.ORE_INPUT_DIR / "simulation.xml" + output_path = Path(output_path) + + root = ET.Element("Simulation") + mkt = ET.SubElement(root, "Market") + + # ── BaseCurrency ────────────────────────────────────────────────── + ET.SubElement(mkt, "BaseCurrency").text = market.base_currency + + # ── Currencies ──────────────────────────────────────────────────── + ccys_el = ET.SubElement(mkt, "Currencies") + for ccy in sorted(market.currencies): + ET.SubElement(ccys_el, "Currency").text = ccy + + # ── YieldCurves (global config) ─────────────────────────────────── + yc_el = ET.SubElement(mkt, "YieldCurves") + cfg = ET.SubElement(yc_el, "Configuration", {"curve": ""}) + ET.SubElement(cfg, "Tenors").text = ", ".join(config.STANDARD_RATE_TENORS) + ET.SubElement(cfg, "Interpolation").text = "LogLinear" + ET.SubElement(cfg, "Extrapolation").text = "FlatZero" + + # ── FxRates ─────────────────────────────────────────────────────── + fx_el = ET.SubElement(mkt, "FxRates") + pairs_el = ET.SubElement(fx_el, "CurrencyPairs") + for pair in market.fx_pairs: + # ORE simulation uses inverted convention (USDEUR instead of EURUSD) + foreign = pair[:3] + domestic = pair[3:] + sim_pair = domestic + foreign # e.g. EURUSD β†’ USDEUR + ET.SubElement(pairs_el, "CurrencyPair").text = sim_pair + + # ── Indices ─────────────────────────────────────────────────────── + indices_el = ET.SubElement(mkt, "Indices") + for idx in market.index_curves: + ET.SubElement(indices_el, "Index").text = idx.name + + # ── BenchmarkCurves (empty) ─────────────────────────────────────── + ET.SubElement(mkt, "BenchmarkCurves") + + # ── CapFloorVolatilities ────────────────────────────────────────── + if market.capfloor_vols: + cf_el = ET.SubElement(mkt, "CapFloorVolatilities") + ET.SubElement(cf_el, "Simulate").text = "true" + ET.SubElement(cf_el, "ReactionToTimeDecay").text = "ForwardVariance" + keys_el = ET.SubElement(cf_el, "Keys") + for cv in market.capfloor_vols: + ET.SubElement(keys_el, "Key").text = cv.name + ET.SubElement(cf_el, "Expiries", {"key": cv.name}).text = ( + "1Y, 2Y, 3Y, 4Y, 5Y, 6Y, 7Y, 8Y, 9Y, 10Y, 12Y, 15Y, 20Y, 25Y, 30Y" + ) + ET.SubElement(cf_el, "Strikes", {"key": cv.name}).text = ( + "-0.015, -0.01, 0, 0.0005" + ) + ET.SubElement(cf_el, "AdjustOptionletPillars").text = "true" + ET.SubElement(cf_el, "UseCapAtm").text = "false" + ET.SubElement(cf_el, "SmileDynamics", {"key": ""}).text = "StickyStrike" + for cv in market.capfloor_vols: + ET.SubElement(cf_el, "SmileDynamics", {"key": cv.name}).text = "StickyStrike" + + # ── DefaultCurves ───────────────────────────────────────────────── + if market.default_curves: + dc_el = ET.SubElement(mkt, "DefaultCurves") + names_el = ET.SubElement(dc_el, "Names") + for dc in market.default_curves: + ET.SubElement(names_el, "Name").text = dc.name + ET.SubElement(dc_el, "Tenors").text = ", ".join(config.STANDARD_CREDIT_TENORS) + ET.SubElement(dc_el, "SimulateSurvivalProbabilities").text = "true" + ET.SubElement(dc_el, "SimulateRecoveryRates").text = "false" + cals = ET.SubElement(dc_el, "Calendars") + ET.SubElement(cals, "Calendar", {"name": ""}).text = "TARGET" + ET.SubElement(dc_el, "Extrapolation").text = "FlatZero" + + # ── ZeroInflationIndexCurves ────────────────────────────────────── + if market.inflation_indices: + zi_el = ET.SubElement(mkt, "ZeroInflationIndexCurves") + names_el = ET.SubElement(zi_el, "Names") + for zi in market.inflation_indices: + ET.SubElement(names_el, "Name").text = zi.name + ET.SubElement(zi_el, "Tenors").text = ", ".join(config.STANDARD_INFLATION_TENORS) + + # ── Equities ────────────────────────────────────────────────────── + if market.equity_curves: + eq_el = ET.SubElement(mkt, "Equities") + ET.SubElement(eq_el, "SimulateDividendYield").text = "true" + names_el = ET.SubElement(eq_el, "Names") + for eq in market.equity_curves: + ET.SubElement(names_el, "Name").text = eq.name + ET.SubElement(eq_el, "DividendTenors").text = ", ".join( + config.STANDARD_EQUITY_DIV_TENORS + ) + + # ── CreditStates (empty) ───────────────────────────────────────── + cs = ET.SubElement(mkt, "CreditStates") + ET.SubElement(cs, "NumberOfFactors").text = "0" + acs = ET.SubElement(mkt, "AggregationScenarioDataCreditStates") + ET.SubElement(acs, "NumberOfFactors").text = "0" + + # ── Write ───────────────────────────────────────────────────────── + xml_str = _prettify(root) + output_path.write_text(xml_str, encoding="utf-8") + return output_path + + +# ── Pretty-print MarketStructure ────────────────────────────────────────────── + +def format_market_structure(ms: MarketStructure) -> str: + """Return a human-readable summary of the discovered market structure.""" + lines = [ + f"Base currency: {ms.base_currency}", + f"Currencies: {', '.join(sorted(ms.currencies))}", + "", + f"Discount curves ({len(ms.discount_curves)}):", + ] + for c in ms.discount_curves: + lines.append(f" {c.currency:4s} {c.name:30s} {c.spec}") + lines.append(f"\nIndex curves ({len(ms.index_curves)}):") + for c in ms.index_curves: + lines.append(f" {c.currency:4s} {c.name:30s} {c.spec}") + if ms.yield_curves: + lines.append(f"\nYield curves ({len(ms.yield_curves)}):") + for c in ms.yield_curves: + lines.append(f" {c.currency:4s} {c.name:30s} {c.spec}") + lines.append(f"\nFX pairs ({len(ms.fx_pairs)}):") + for p in ms.fx_pairs: + lines.append(f" {p}") + if ms.equity_curves: + lines.append(f"\nEquity curves ({len(ms.equity_curves)}):") + for c in ms.equity_curves: + lines.append(f" {c.currency:4s} {c.name:30s} {c.spec}") + if ms.default_curves: + lines.append(f"\nDefault curves ({len(ms.default_curves)}):") + for c in ms.default_curves: + lines.append(f" {c.currency:4s} {c.name:30s} {c.spec}") + if ms.inflation_indices: + lines.append(f"\nInflation indices ({len(ms.inflation_indices)}):") + for c in ms.inflation_indices: + lines.append(f" {c.currency:4s} {c.name:30s} {c.spec}") + return "\n".join(lines) From 1d33ae1414e53d0a3dab684953f8bf6bb00510d8 Mon Sep 17 00:00:00 2001 From: Matthias Groncki Date: Sat, 28 Feb 2026 16:30:22 +0100 Subject: [PATCH 6/8] smaller improvements --- EconomicStressAgentORE/.env.example | 5 +- EconomicStressAgentORE/README.md | 216 +++++++++++++++----- EconomicStressAgentORE/config.py | 3 +- EconomicStressAgentORE/impact_summarizer.py | 75 ++++--- EconomicStressAgentORE/scenario_analyzer.py | 2 +- 5 files changed, 219 insertions(+), 82 deletions(-) diff --git a/EconomicStressAgentORE/.env.example b/EconomicStressAgentORE/.env.example index c552cc2..0bd2e4a 100644 --- a/EconomicStressAgentORE/.env.example +++ b/EconomicStressAgentORE/.env.example @@ -2,4 +2,7 @@ OPENAI_API_KEY=sk-... # OpenAI model to use -OPENAI_MODEL=gpt-4 +OPENAI_MODEL=gpt-5.2 + +# LLM temperature (0.0 = deterministic, 1.0 = creative) +OPENAI_TEMPERATURE=0.2 diff --git a/EconomicStressAgentORE/README.md b/EconomicStressAgentORE/README.md index 538648c..b46f1c0 100644 --- a/EconomicStressAgentORE/README.md +++ b/EconomicStressAgentORE/README.md @@ -35,6 +35,65 @@ User describes scenario β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` +## Known Shortcomings & Shortcuts + +This project is a proof-of-concept built for learning purposes. Several design +decisions were taken as shortcuts and would need to be rethought for any +production use. + +### 1. Historical scenarios are mock data + +The 20 historical episodes in `data/scenarios.json` are AI-generated +approximations, not rigorously sourced market data. In a real system these +would be replaced by: + +- Curated, auditable data sets of actual market moves (sourced from market + data providers or internal risk databases). +- Proper versioning and provenance tracking so every shift can be traced + back to an observed market event. + +### 2. The LLM produces the final shifts directly β€” not auditable + +Currently the LLM receives the historical scenarios, picks the closest +matches, and returns a _weighted average_ of market shifts in a single step. +This is problematic because: + +- **Non-reproducible**: the same prompt can yield different numbers across + runs or model versions, making results impossible to audit. +- **Not explainable**: there is no transparent formula an auditor or + regulator can inspect β€” the blending logic is a black box inside the model. + +A better architecture would split the task in two: + +1. **LLM step (qualitative)**: the model selects the _N_ closest historical + scenarios and proposes a _severity multiplier_ (e.g. "80% of the 2008 + crisis combined with 50% of the Dot-com bust"). This output is + human-readable and auditable. +2. **Deterministic step (quantitative)**: a macro-economic model or a simple rule-based mapping takes the selected scenarios and severity parameters + as inputs and produces the final shift vector. Because the model is + versioned code with fixed parameters, the output is fully reproducible, + testable, and auditable. + +This separation keeps the LLM's role limited to _judgement_ (scenario +matching and severity assessment) while all _numerical_ work is done by +auditable, deterministic code. + +### 3. Other simplifications + +- **Single stress scenario per run** β€” no support for running a grid of + severities or multiple scenarios in one invocation. +- **No volatility or correlation shifts** β€” only spot-level shifts are + applied; a real stress test would also shock implied vols, correlations, + and basis spreads. +- **Flat extrapolation of tenor grids** β€” shift curves are linearly + interpolated and flat-extrapolated, which may be too simplistic for + far-out-of-grid tenors. +- **No validation against market bounds** β€” the LLM-generated shifts are + not sanity-checked (e.g. negative rates below a floor, FX moves beyond + historical extremes). +- **Toy portfolio** β€” the included ORE workspace has only a handful of + trades; + ## Setup ### 1. Install dependencies @@ -54,17 +113,17 @@ cp .env.example .env `.env` keys: -| Key | Description | Required | -| ---------------- | ----------------------------- | -------- | -| `OPENAI_API_KEY` | Your OpenAI API key | Yes | -| `OPENAI_MODEL` | Model name (default: `gpt-4`) | No | +| Key | Description | Required | +| ---------------- | ------------------------------- | -------- | +| `OPENAI_API_KEY` | Your OpenAI API key | Yes | +| `OPENAI_MODEL` | Model name (default: `gpt-5.2`) | No | ### 3. ORE workspace -The agent expects the ORE workspace at `./OREDir/` containing: +The agent expects the ORE workspace at `./oredata/` containing: ``` -OREDir/ +oredata/ β”œβ”€β”€ ore.xml ← main ORE config β”œβ”€β”€ Input/ β”‚ β”œβ”€β”€ portfolio.xml @@ -75,13 +134,13 @@ OREDir/ β”‚ β”œβ”€β”€ simulation.xml β”‚ β”œβ”€β”€ sensitivity.xml β”‚ └── pricingengine.xml -└── Output/ ← auto-created by ORE +└── Output/ ← cleaned and recreated on each run ``` The agent injects two files at runtime: -- `OREDir/Input/agent_stress.xml` β€” generated stress scenario -- `OREDir/ore_agent.xml` β€” stripped-down ore.xml (stress analytic only) +- `oredata/Input/agent_stress.xml` β€” generated stress scenario +- `oredata/ore_agent.xml` β€” stripped-down ore.xml (stress analytic only) ## Usage @@ -92,7 +151,7 @@ python agent.py --scenario "A sudden European banking crisis with contagion fear # With all options python agent.py \ --scenario "A sudden European banking crisis with contagion fears" \ - --ore-workspace ./OREDir \ + --ore-workspace ./oredata \ --scenario-id my_scenario \ --output report.md \ --verbose @@ -100,66 +159,115 @@ python agent.py \ ## Architecture -| File | Purpose | -| ------------------------- | --------------------------------------------- | -| `agent.py` | CLI orchestrator β€” ties all steps together | -| `scenario_analyzer.py` | LLM call β€” maps text to market shifts | -| `stresstest_builder.py` | XML generator β€” writes ORE stress test config | -| `ore_runner.py` | ORE execution (Python API or subprocess) | -| `impact_summarizer.py` | CSV parser + LLM narrative report | -| `historical_scenarios.py` | Knowledge base of 20 historical episodes | -| `config.py` | Paths, model settings, curve grids | - -## Market entities handled - -| Asset class | Scenario key | ORE name | -| ------------- | ------------ | ------------------------------ | -| FX | `EURUSD` | `EURUSD` (absolute shift) | -| Euro equities | `SX5E` | `RIC:.STOXX50` | -| US equities | `SPX` | `RIC:.SPX` | -| EUR rates | `rates_eur` | EUR-ESTER + EUR discount curve | -| USD rates | `rates_usd` | USD-SOFR + USD discount curve | - -Rate shifts are specified at **1Y, 2Y, 5Y, 10Y, 30Y** and interpolated onto -the full ORE tenor grid using piecewise-linear interpolation. +| File | Purpose | +| -------------------------- | ------------------------------------------------------- | +| `agent.py` | CLI orchestrator β€” ties all steps together | +| `scenario_analyzer.py` | LLM call β€” maps text to market shifts | +| `stresstest_builder.py` | XML generator β€” writes ORE stress test config | +| `ore_runner.py` | ORE execution via Python API | +| `impact_summarizer.py` | CSV parser + LLM narrative report | +| `historical_scenarios.py` | `ScenarioKnowledgeBase` class β€” loads `scenarios.json` | +| `todaysmarket_analyzer.py` | Parses todaysmarket.xml to discover curves and equities | +| `config.py` | Paths, model settings, tenor grids | +| `data/scenarios.json` | 20 historical episodes with structured shifts | + +## Market shift schema + +Shifts are specified in a generic, multi-currency schema: + +| Asset class | Schema key | Value convention | ORE mapping | +| ----------- | ------------ | ---------------------------------------- | ------------------------------- | +| Rates | `rates.CCY` | Absolute (decimal, e.g. -0.015 = -150bp) | Discount + Index curves per ccy | +| FX | `fx.PAIR` | Absolute change in spot | FxSpot (inverted for ORE) | +| Equity | `equity.KEY` | Relative (e.g. -0.25 = -25%) | EquitySpot | +| Credit | `credit.KEY` | Absolute per tenor (widening positive) | SurvivalProbability | + +Rate and credit shifts are interpolated onto the ORE tenor grid using +`ORE.LinearInterpolation` with flat extrapolation. + +Equity and credit names are resolved via an optional sector mapping +(`sector_mapping.csv`) that maps trade-level names to scenario-level keys. ## Example output ``` +python agent.py -s "world wide AI mass lay offs for high income jobs" + ╔══════════════════════════════════════════════════════════╗ β•‘ Economic Scenario Stress Test Agent β•‘ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β• -Analyzing scenario: "A sudden European banking crisis with contagion fears" +Analyzing scenario: "world wide AI mass lay offs for high income jobs" - β–Ά Step 1/4 Analyzing scenario with LLM … + β–Ά Step 1/5 Analyzing scenario with LLM … βœ“ Scenario analysis complete Matched scenarios: - β€’ 2010-2012 European Sovereign Debt Crisis - β€’ 2023 US Regional Banking Crisis - -EURUSD : -0.0700 (absolute) -SX5E : -19.0% -SPX : -8.5% -EUR rates: 1Y -8bp 2Y -10bp 5Y -9bp 10Y -7bp 30Y -4bp -USD rates: 1Y -8bp 2Y -12bp 5Y -14bp 10Y -12bp 30Y -8bp - - β–Ά Step 2/4 Generating ORE stress test XML … - β–Ά Step 3/4 Running ORE … - β–Ά Step 4/4 Generating impact report … + β€’ 2001 Dot-com Bust (Mar 2000 – Oct 2002) + β€’ 2015 China Devaluation / EM Shock (Aug – Sep 2015) + β€’ 2011 US Debt-Ceiling Crisis / S&P Downgrade (Jul – Aug 2011) + +Reasoning: A worldwide AI-driven wave of layoffs in high-income (white-collar/tech-heavy) jobs is most analogous to a tech-led equity drawdown with growth fears and policy easing (dot-com), with an added global risk-off/flight-to-quality component (2015, 2011). Severity is set to moderate-to-severe but below 2008: large equity hit (especially tech), meaningful rate declines, and moderate credit widening; USD benefits as a relative safe haven so EURUSD falls. + +Proposed market shifts: + FX EURUSD: -0.0126 + Equity EUR: -30.8% + Equity USD: -25.2% + Equity Tech: -45.0% + Rates EUR: 1Y -105bp 2Y -99bp 3Y -80bp 5Y -84bp 10Y -84bp 30Y -63bp + Rates USD: 1Y -210bp 2Y -196bp 3Y -161bp 5Y -154bp 10Y -126bp 30Y -84bp + Credit EUR: 1Y +35bp 2Y +49bp 3Y +60bp 5Y +67bp 10Y +56bp + Credit USD: 1Y +49bp 2Y +63bp 3Y +77bp 5Y +88bp 10Y +74bp + + β–Ά Step 2/5 Parsing todaysmarket.xml … + βœ“ Discovered 2 currencies, 2 discount curves, 2 equities, 1 credit names + + β–Ά Step 3/5 Generating ORE stress test XML … + βœ“ Written: /Users/matthiasgroncki/quant-dev/IPythonScripts/EconomicStressAgentORE/oredata/Input/agent_stress.xml + βœ“ Written: /Users/matthiasgroncki/quant-dev/IPythonScripts/EconomicStressAgentORE/oredata/ore_agent.xml + + β–Ά Step 4/5 Running ORE … +Loading inputs OK +Requested analytics STRESS +StressTestAnalytic: Build Market OK +StressTestAnalytic: Build Portfolio OK +Risk: Stress Test Report OK +Writing reports... OK +Writing cubes... OK +run time: 0.180000 sec +ORE done. + βœ“ ORE completed. Results: /Users/matthiasgroncki/quant-dev/IPythonScripts/EconomicStressAgentORE/oredata/Output/stresstest.csv + + β–Ά Step 5/5 Generating impact report … + βœ“ Report ready ════════════════════════════════════════════════════════════ Portfolio Stress Test Impact Report ════════════════════════════════════════════════════════════ -**TOTAL P&L: +92,000 EUR [GAIN]** + TOTAL P&L: -6,614,962 EUR [β–Ό LOSS] + +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Trade β”‚ Base NPV β”‚ Stressed NPV β”‚ P&L Impact β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ EUR6MSwap β”‚ 5,924,804 β”‚ 4,415,089 β”‚ -1,509,715 β”‚ +β”‚ EquityCFD_USD β”‚ 76,647 β”‚ -1,359,666 β”‚ -1,436,313 β”‚ +β”‚ EquityCFD_EUR β”‚ 1,263,244 β”‚ -172,789 β”‚ -1,436,033 β”‚ +β”‚ XccySwap β”‚ 268,878 β”‚ -1,027,997 β”‚ -1,296,875 β”‚ +β”‚ Cap β”‚ 4,988,927 β”‚ 4,045,146 β”‚ -943,781 β”‚ +β”‚ ZeroCouponInflationSwapEUR β”‚ -20,433 β”‚ -21,311 β”‚ -878 β”‚ +β”‚ CDS β”‚ -64,059 β”‚ -55,424 β”‚ +8,635 β”‚ +β”œβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β”Όβ•β•β•β•β•β•β•β•β•β•β•β•β”Όβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β”Όβ•β•β•β•β•β•β•β•β•β•β•β•β”€ +β”‚ TOTAL β”‚ 12,438,009 β”‚ 5,823,047 β”‚ -6,614,962 β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + +──────────────────────────────────────────────────────────── + Narrative Summary +──────────────────────────────────────────────────────────── + +The β€œAI mass layoffs in high‑income jobs” scenario produces a severe risk‑off shock across equities and a strong rally in core rates, alongside widening credit spreads and a modest EURUSD move. Equities fall sharply (EUR equities -30.8%, USD equities -25.2%, and Tech -45.0%), while rates decline materially (EUR down 63–105bp across 1Y–30Y; USD down 84–210bp across 1Y–30Y). Credit spreads widen in both regions (EUR +35–67bp out to 10Y; USD +49–88bp out to 10Y). Under these combined shifts, portfolio NPV drops from EUR 12,438,009 to EUR 5,823,047, for a total P&L impact of -EUR 6,614,962 (about -53% of base NPV). + +Losses are concentrated in equity and rates/FX-sensitive positions. The two equity CFD positions are the largest equity-driven detractors: EquityCFD_USD loses -EUR 1,436,313 (from EUR 76,647 to -EUR 1,359,666) and EquityCFD_EUR loses -EUR 1,436,033 (from EUR 1,263,244 to -EUR 172,789), consistent with the large equity drawdowns in both currencies. The rates rally and cross-currency dynamics also contribute heavily: the EUR6MSwap loses -EUR 1,509,715 (from EUR 5,924,804 to EUR 4,415,089) and the XccySwap loses -EUR 1,296,875 (from EUR 268,878 to -EUR 1,027,997), indicating significant exposure to curve moves and/or basis/FX interactions (EURUSD shifts by -0.0126 in absolute terms). The Cap position also detracts -EUR 943,781 (from EUR 4,988,927 to EUR 4,045,146), suggesting the scenario’s large rate move and volatility/convexity effects are adverse for this structure despite the directionally lower yields. -| Trade | Base NPV | Stressed NPV | P&L Impact | -|-------------|------------|--------------|-------------| -| CDS | -64,059 | -63,293 | +766 | -| XccySwap | 268,878 | 230,964 | -37,914 | -| EUR6MSwap | 5,924,804 | 5,867,079 | -57,725 | -| ... | | | | -| **TOTAL** | ... | ... | **+92,000** | +Offsets are limited and primarily credit-related. The CDS position gains EUR 8,635 (from -EUR 64,059 to -EUR 55,424), consistent with credit spread widening improving the value of protection, but the magnitude is small relative to the equity and rates-driven losses. The ZeroCouponInflationSwapEUR is essentially flat (-EUR 878), providing negligible diversification in this shock. Overall, the risk interpretation is that the portfolio is materially exposed to a synchronized risk-off regime where equities reprice lower and rates rally sharply; credit hedging is present but insufficient in size to counteract the dominant equity and rates/cross-currency losses, leaving the portfolio vulnerable to macro shocks that combine growth fears, policy-rate cuts, and widening spreads. ``` diff --git a/EconomicStressAgentORE/config.py b/EconomicStressAgentORE/config.py index f4b0430..57034c4 100644 --- a/EconomicStressAgentORE/config.py +++ b/EconomicStressAgentORE/config.py @@ -22,7 +22,8 @@ # ── LLM ─────────────────────────────────────────────────────────────────────── OPENAI_API_KEY: str = os.getenv("OPENAI_API_KEY", "") -OPENAI_MODEL: str = os.getenv("OPENAI_MODEL", "gpt-4") +OPENAI_MODEL: str = os.getenv("OPENAI_MODEL", "gpt-5.2") +OPENAI_TEMPERATURE: float = float(os.getenv("OPENAI_TEMPERATURE", "0.2")) # ── Sector-mapping CSV (maps equity/credit names β†’ sectors) ─────────────────── SECTOR_MAPPING_CSV: Path = BASE_DIR / "sector_mapping.csv" diff --git a/EconomicStressAgentORE/impact_summarizer.py b/EconomicStressAgentORE/impact_summarizer.py index 011eccc..37ccb6e 100644 --- a/EconomicStressAgentORE/impact_summarizer.py +++ b/EconomicStressAgentORE/impact_summarizer.py @@ -69,25 +69,48 @@ def _compute_summary(df: pd.DataFrame) -> dict[str, Any]: # ── Markdown table ──────────────────────────────────────────────────────────── def _format_table(summary: dict[str, Any]) -> str: - """Build a Markdown results table.""" - lines = [ - "| Trade | Base NPV | Stressed NPV | P&L Impact |", - "|-------|----------|--------------|------------|", + """Build a nicely aligned plain-text table with box-drawing characters.""" + # Determine column widths dynamically + trade_ids = [str(r["TradeId"]) for r in summary["trades"]] + ["TOTAL"] + base_vals = [f"{r['Base NPV']:,.0f}" for r in summary["trades"]] + [ + f"{summary['total_base_npv']:,.0f}" ] - for row in summary["trades"]: - pnl_str = f"{row['PnL']:+,.0f}" - lines.append( - f"| {row['TradeId']} " - f"| {row['Base NPV']:,.0f} " - f"| {row['Scenario NPV']:,.0f} " - f"| {pnl_str} |" + stress_vals = [f"{r['Scenario NPV']:,.0f}" for r in summary["trades"]] + [ + f"{summary['total_stressed_npv']:,.0f}" + ] + pnl_vals = [f"{r['PnL']:+,.0f}" for r in summary["trades"]] + [ + f"{summary['total_pnl']:+,.0f}" + ] + + w_trade = max(len("Trade"), max(len(t) for t in trade_ids)) + 2 + w_base = max(len("Base NPV"), max(len(v) for v in base_vals)) + 2 + w_stress = max(len("Stressed NPV"), max(len(v) for v in stress_vals)) + 2 + w_pnl = max(len("P&L Impact"), max(len(v) for v in pnl_vals)) + 2 + + def hline(left: str, mid: str, right: str, fill: str = "─") -> str: + return f"{left}{fill * w_trade}{mid}{fill * w_base}{mid}{fill * w_stress}{mid}{fill * w_pnl}{right}" + + def row(c1: str, c2: str, c3: str, c4: str, bold: bool = False) -> str: + s = ( + f"β”‚ {c1:<{w_trade - 2}} " + f"β”‚ {c2:>{w_base - 2}} " + f"β”‚ {c3:>{w_stress - 2}} " + f"β”‚ {c4:>{w_pnl - 2}} β”‚" ) - lines.append( - f"| **TOTAL** " - f"| **{summary['total_base_npv']:,.0f}** " - f"| **{summary['total_stressed_npv']:,.0f}** " - f"| **{summary['total_pnl']:+,.0f}** |" - ) + return s + + lines: list[str] = [] + lines.append(hline("β”Œ", "┬", "┐")) + lines.append(row("Trade", "Base NPV", "Stressed NPV", "P&L Impact")) + lines.append(hline("β”œ", "β”Ό", "─")) + + for tid, bv, sv, pv in zip(trade_ids[:-1], base_vals[:-1], stress_vals[:-1], pnl_vals[:-1]): + lines.append(row(tid, bv, sv, pv)) + + lines.append(hline("β”œ", "β”Ό", "─", "═")) + lines.append(row("TOTAL", base_vals[-1], stress_vals[-1], pnl_vals[-1])) + lines.append(hline("β””", "β”΄", "β”˜")) + return "\n".join(lines) @@ -167,7 +190,7 @@ def _llm_narrative( {"role": "system", "content": _NARRATIVE_SYSTEM}, {"role": "user", "content": prompt}, ], - temperature=0.4, + temperature=config.OPENAI_TEMPERATURE, ) return resp.choices[0].message.content or "" @@ -199,22 +222,24 @@ def summarize( table = _format_table(summary) narrative = _llm_narrative(scenario_description, shifts, summary) + width = 60 header = ( - "═" * 60 + "\n" - " Portfolio Stress Test Impact Report\n" - "═" * 60 + "═" * width + "\n" + + " Portfolio Stress Test Impact Report\n" + + "═" * width ) pnl_sign = "+" if summary["total_pnl"] >= 0 else "" pnl_label = f"TOTAL P&L: {pnl_sign}{summary['total_pnl']:,.0f} EUR" - direction = "GAIN" if summary["total_pnl"] >= 0 else "LOSS" + direction = "β–² GAIN" if summary["total_pnl"] >= 0 else "β–Ό LOSS" report = ( f"{header}\n\n" - f"**{pnl_label} [{direction}]**\n\n" + f" {pnl_label} [{direction}]\n\n" f"{table}\n\n" - "---\n\n" - "**Narrative Summary**\n\n" + + "─" * width + "\n" + " Narrative Summary\n" + + "─" * width + "\n\n" f"{narrative}\n" ) return report diff --git a/EconomicStressAgentORE/scenario_analyzer.py b/EconomicStressAgentORE/scenario_analyzer.py index 16d4925..c8e779a 100644 --- a/EconomicStressAgentORE/scenario_analyzer.py +++ b/EconomicStressAgentORE/scenario_analyzer.py @@ -104,7 +104,7 @@ def analyze( {"role": "system", "content": _SYSTEM_PROMPT}, {"role": "user", "content": _build_user_message(scenario_description, knowledge_base)}, ], - temperature=0.2, + temperature=config.OPENAI_TEMPERATURE, response_format={"type": "json_object"}, ) From 8e552d027f0b8d86b22e05090630ceb169aaff5e Mon Sep 17 00:00:00 2001 From: Matthias Groncki Date: Sat, 28 Feb 2026 17:25:07 +0100 Subject: [PATCH 7/8] build simulation and sensitivity xml for stresstest --- EconomicStressAgentORE/README.md | 2 + EconomicStressAgentORE/agent.py | 1 + EconomicStressAgentORE/stresstest_builder.py | 74 ++++++++++-- EconomicStressAgentORE/test_integration.py | 1 + .../todaysmarket_analyzer.py | 114 ++++++++++++++++++ 5 files changed, 179 insertions(+), 13 deletions(-) diff --git a/EconomicStressAgentORE/README.md b/EconomicStressAgentORE/README.md index b46f1c0..86fa9fc 100644 --- a/EconomicStressAgentORE/README.md +++ b/EconomicStressAgentORE/README.md @@ -93,6 +93,8 @@ auditable, deterministic code. historical extremes). - **Toy portfolio** β€” the included ORE workspace has only a handful of trades; +- **Construction of simulation/stresstest/sensitivity config** is incomplete and can handle + only a few risk factors (no volatilities, no commodity risk factors) and allow only a small range or products. ## Setup diff --git a/EconomicStressAgentORE/agent.py b/EconomicStressAgentORE/agent.py index 35c33af..14046de 100644 --- a/EconomicStressAgentORE/agent.py +++ b/EconomicStressAgentORE/agent.py @@ -101,6 +101,7 @@ def run( base_ore_xml=ore_workspace / "ore.xml", output_ore_xml=ore_agent_xml, stress_config_file="agent_stress.xml", + market=market, ) print(OK + f"Written: {ore_agent_xml}\n") diff --git a/EconomicStressAgentORE/stresstest_builder.py b/EconomicStressAgentORE/stresstest_builder.py index ce22426..bfdf745 100644 --- a/EconomicStressAgentORE/stresstest_builder.py +++ b/EconomicStressAgentORE/stresstest_builder.py @@ -101,6 +101,13 @@ def _prettify(element: ET.Element) -> str: return "\n".join(line for line in xml.splitlines() if line.strip()) +def _add_param(parent: ET.Element, name: str, value: str) -> ET.Element: + """Add a ``value`` child element.""" + p = ET.SubElement(parent, "Parameter", name=name) + p.text = value + return p + + # ── Main builder ────────────────────────────────────────────────────────────── def _resolve_rate_shifts( @@ -263,11 +270,17 @@ def build_ore_config( base_ore_xml: Path | None = None, output_ore_xml: Path | None = None, stress_config_file: str = "agent_stress.xml", + market: MarketStructure | None = None, ) -> Path: """ Write a minimal ore_agent.xml that activates only the stress analytic and points to the agent-generated stress test configuration. + If *market* is provided, ``simulation_agent.xml`` and + ``sensitivity_agent.xml`` are generated from the market structure and + the stress analytic is pointed at them. This removes the dependency on + hand-maintained simulation.xml / sensitivity.xml files. + Parameters ---------- base_ore_xml : source ore.xml to base settings on @@ -275,11 +288,16 @@ def build_ore_config( output_ore_xml : where to write the agent ore config (defaults to config.AGENT_ORE_XML) stress_config_file : filename (relative to Input/) of the stress XML + market : MarketStructure from todaysmarket_analyzer.parse(); + when provided, agent simulation & sensitivity XMLs + are auto-generated Returns ------- Path to the written file. """ + from todaysmarket_analyzer import generate_simulation_xml, generate_sensitivity_xml + if base_ore_xml is None: base_ore_xml = config.ORE_WORKSPACE / "ore.xml" if output_ore_xml is None: @@ -288,6 +306,17 @@ def build_ore_config( base_ore_xml = Path(base_ore_xml) output_ore_xml = Path(output_ore_xml) + # ── Generate simulation & sensitivity XMLs if market is available ── + sim_file = "simulation.xml" + sensi_file = "sensitivity.xml" + + if market is not None: + input_dir = output_ore_xml.parent / "Input" + sim_path = generate_simulation_xml(market, input_dir / "simulation_agent.xml") + sensi_path = generate_sensitivity_xml(market, input_dir / "sensitivity_agent.xml") + sim_file = sim_path.name + sensi_file = sensi_path.name + tree = ET.parse(base_ore_xml) root = tree.getroot() @@ -300,19 +329,38 @@ def build_ore_config( # Disable every analytic except stress; update stress config file analytics = root.find("Analytics") - if analytics is not None: - for analytic in analytics.findall("Analytic"): - atype = analytic.get("type", "") - if atype == "stress": - for param in analytic.findall("Parameter"): - if param.get("name") == "stressConfigFile": - param.text = stress_config_file - if param.get("name") == "active": - param.text = "Y" - else: - for param in analytic.findall("Parameter"): - if param.get("name") == "active": - param.text = "N" + if analytics is None: + analytics = ET.SubElement(root, "Analytics") + + has_stress = False + for analytic in analytics.findall("Analytic"): + atype = analytic.get("type", "") + if atype == "stress": + has_stress = True + for param in analytic.findall("Parameter"): + if param.get("name") == "stressConfigFile": + param.text = stress_config_file + if param.get("name") == "active": + param.text = "Y" + if param.get("name") == "marketConfigFile": + param.text = sim_file + if param.get("name") == "sensitivityConfigFile": + param.text = sensi_file + else: + for param in analytic.findall("Parameter"): + if param.get("name") == "active": + param.text = "N" + + # If no stress analytic exists in the source ore.xml, add one + if not has_stress: + stress_el = ET.SubElement(analytics, "Analytic", type="stress") + _add_param(stress_el, "active", "Y") + _add_param(stress_el, "marketConfigFile", sim_file) + _add_param(stress_el, "stressConfigFile", stress_config_file) + _add_param(stress_el, "sensitivityConfigFile", sensi_file) + _add_param(stress_el, "pricingEnginesFile", "pricingengine.xml") + _add_param(stress_el, "scenarioOutputFile", "stresstest.csv") + _add_param(stress_el, "outputThreshold", "0.000001") xml_str = _prettify(root) lines = xml_str.splitlines() diff --git a/EconomicStressAgentORE/test_integration.py b/EconomicStressAgentORE/test_integration.py index 50b2c2d..3886556 100644 --- a/EconomicStressAgentORE/test_integration.py +++ b/EconomicStressAgentORE/test_integration.py @@ -69,6 +69,7 @@ ore_agent_xml = stresstest_builder.build_ore_config( base_ore_xml=ore_workspace / "ore.xml", stress_config_file="agent_stress.xml", + market=market, ) print(f"\nORE agent config written to: {ore_agent_xml}") diff --git a/EconomicStressAgentORE/todaysmarket_analyzer.py b/EconomicStressAgentORE/todaysmarket_analyzer.py index ba4cc62..2e0e375 100644 --- a/EconomicStressAgentORE/todaysmarket_analyzer.py +++ b/EconomicStressAgentORE/todaysmarket_analyzer.py @@ -399,6 +399,120 @@ def generate_simulation_xml( return output_path +# ── Sensitivity XML generation ──────────────────────────────────────────────── + +def generate_sensitivity_xml( + market: MarketStructure, + output_path: Path | None = None, +) -> Path: + """ + Generate a minimal sensitivity.xml from the discovered MarketStructure. + + This produces a zero-shift sensitivity config (ShiftSize = 0.0001) that + covers all market entities required by the stress test analytic. + No ParConversion blocks are emitted β€” this is a simplified "zero" config + sufficient for the stress test engine to resolve all risk factors. + + Parameters + ---------- + market : MarketStructure from parse() + output_path : where to write; defaults to + config.ORE_INPUT_DIR / "sensitivity_agent.xml" + """ + if output_path is None: + output_path = config.ORE_INPUT_DIR / "sensitivity_agent.xml" + output_path = Path(output_path) + + rate_tenors = ", ".join(config.STANDARD_RATE_TENORS) + credit_tenors = ", ".join(config.STANDARD_CREDIT_TENORS) + inflation_tenors = ", ".join(config.STANDARD_INFLATION_TENORS) + + root = ET.Element("SensitivityAnalysis") + + # ── Discount curves ─────────────────────────────────────────────── + dc_el = ET.SubElement(root, "DiscountCurves") + for dc in market.discount_curves: + curve = ET.SubElement(dc_el, "DiscountCurve", ccy=dc.currency) + ET.SubElement(curve, "ShiftType").text = "Absolute" + ET.SubElement(curve, "ShiftSize").text = "0.0001" + ET.SubElement(curve, "ShiftScheme").text = "Forward" + ET.SubElement(curve, "ShiftTenors").text = rate_tenors + + # ── Index curves ────────────────────────────────────────────────── + ic_el = ET.SubElement(root, "IndexCurves") + for idx in market.index_curves: + curve = ET.SubElement(ic_el, "IndexCurve", index=idx.name) + ET.SubElement(curve, "ShiftType").text = "Absolute" + ET.SubElement(curve, "ShiftSize").text = "0.0001" + ET.SubElement(curve, "ShiftScheme").text = "Forward" + ET.SubElement(curve, "ShiftTenors").text = rate_tenors + + # ── Yield curves (empty) ────────────────────────────────────────── + ET.SubElement(root, "YieldCurves") + + # ── FX spots ────────────────────────────────────────────────────── + fx_el = ET.SubElement(root, "FxSpots") + for pair in market.fx_pairs: + foreign = pair[:3] + domestic = pair[3:] + sim_pair = domestic + foreign # ORE convention + spot = ET.SubElement(fx_el, "FxSpot", ccypair=sim_pair) + ET.SubElement(spot, "ShiftType").text = "Relative" + ET.SubElement(spot, "ShiftSize").text = "0.01" + + # ── Credit curves ───────────────────────────────────────────────── + if market.default_curves: + cc_el = ET.SubElement(root, "CreditCurves") + for dc in market.default_curves: + curve = ET.SubElement(cc_el, "CreditCurve", name=dc.name) + ET.SubElement(curve, "Currency").text = dc.currency + ET.SubElement(curve, "ShiftType").text = "Absolute" + ET.SubElement(curve, "ShiftSize").text = "0.0001" + ET.SubElement(curve, "ShiftScheme").text = "Forward" + ET.SubElement(curve, "ShiftTenors").text = credit_tenors + + # ── CapFloor volatilities ───────────────────────────────────────── + if market.capfloor_vols: + cfv_el = ET.SubElement(root, "CapFloorVolatilities") + for cv in market.capfloor_vols: + vol = ET.SubElement(cfv_el, "CapFloorVolatility", key=cv.name) + ET.SubElement(vol, "ShiftType").text = "Absolute" + ET.SubElement(vol, "ShiftSize").text = "0.0001" + ET.SubElement(vol, "ShiftExpiries").text = ( + "1Y, 2Y, 3Y, 5Y, 7Y, 10Y, 15Y, 20Y" + ) + ET.SubElement(vol, "ShiftStrikes").text = ( + "-0.01, 0, 0.01, 0.02, 0.03, 0.04, 0.05" + ) + ET.SubElement(vol, "Index").text = cv.name + + # ── Zero inflation index curves ─────────────────────────────────── + if market.inflation_indices: + zi_el = ET.SubElement(root, "ZeroInflationIndexCurves") + for zi in market.inflation_indices: + curve = ET.SubElement(zi_el, "ZeroInflationIndexCurve", index=zi.name) + ET.SubElement(curve, "ShiftType").text = "Absolute" + ET.SubElement(curve, "ShiftSize").text = "0.0001" + ET.SubElement(curve, "ShiftTenors").text = inflation_tenors + + # ── Equity spots ────────────────────────────────────────────────── + if market.equity_curves: + eq_el = ET.SubElement(root, "EquitySpots") + for eq in market.equity_curves: + spot = ET.SubElement(eq_el, "EquitySpot", equity=eq.name) + ET.SubElement(spot, "ShiftType").text = "Relative" + ET.SubElement(spot, "ShiftSize").text = "0.01" + ET.SubElement(spot, "ShiftScheme").text = "Forward" + + # ── Global flags ────────────────────────────────────────────────── + ET.SubElement(root, "ComputeGamma").text = "false" + ET.SubElement(root, "UseSpreadedTermStructures").text = "true" + + xml_str = _prettify(root) + output_path.write_text(xml_str, encoding="utf-8") + return output_path + + # ── Pretty-print MarketStructure ────────────────────────────────────────────── def format_market_structure(ms: MarketStructure) -> str: From 265da426f3d0060784f07c2b3eb10c22e87f8095 Mon Sep 17 00:00:00 2001 From: Matthias Groncki Date: Sat, 28 Feb 2026 20:43:39 +0100 Subject: [PATCH 8/8] update readme --- EconomicStressAgentORE/README.md | 69 +++++----- EconomicStressAgentORE/data/scenarios.json | 88 +++++++++++++ EconomicStressAgentORE/ore_runner.py | 2 +- .../oredata/Input/portfolio.xml | 119 +----------------- 4 files changed, 125 insertions(+), 153 deletions(-) diff --git a/EconomicStressAgentORE/README.md b/EconomicStressAgentORE/README.md index 86fa9fc..7d267cf 100644 --- a/EconomicStressAgentORE/README.md +++ b/EconomicStressAgentORE/README.md @@ -148,11 +148,11 @@ The agent injects two files at runtime: ```bash # Basic usage -python agent.py --scenario "A sudden European banking crisis with contagion fears" +python agent.py --scenario "Giant monsters emerge from the ocean and destroy two major capitals in Europe and Asia simultaneously, triggering martial law, insurance system collapse, and a global flight to safety" # With all options python agent.py \ - --scenario "A sudden European banking crisis with contagion fears" \ + --scenario "python agent.py --scenario "Giant monsters emerge from the ocean and destroy two major capitals in Europe and Asia simultaneously, triggering martial law, insurance system collapse, and a global flight to safety" \ --ore-workspace ./oredata \ --scenario-id my_scenario \ --output report.md \ @@ -193,33 +193,31 @@ Equity and credit names are resolved via an optional sector mapping ## Example output ``` -python agent.py -s "world wide AI mass lay offs for high income jobs" - ╔══════════════════════════════════════════════════════════╗ β•‘ Economic Scenario Stress Test Agent β•‘ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β• -Analyzing scenario: "world wide AI mass lay offs for high income jobs" +Analyzing scenario: "Giant monsters emerge from the ocean and destroy two major capitals in Europe and Asia simultaneously, triggering martial law, insurance system collapse, and a global flight to safety" β–Ά Step 1/5 Analyzing scenario with LLM … βœ“ Scenario analysis complete Matched scenarios: - β€’ 2001 Dot-com Bust (Mar 2000 – Oct 2002) - β€’ 2015 China Devaluation / EM Shock (Aug – Sep 2015) - β€’ 2011 US Debt-Ceiling Crisis / S&P Downgrade (Jul – Aug 2011) + β€’ 2008 Global Financial Crisis (Sep 2008 – Mar 2009) + β€’ 2020 COVID-19 Crash (Feb – Mar 2020) + β€’ Eurozone Break-up (Tail Risk) (Hypothetical) -Reasoning: A worldwide AI-driven wave of layoffs in high-income (white-collar/tech-heavy) jobs is most analogous to a tech-led equity drawdown with growth fears and policy easing (dot-com), with an added global risk-off/flight-to-quality component (2015, 2011). Severity is set to moderate-to-severe but below 2008: large equity hit (especially tech), meaningful rate declines, and moderate credit widening; USD benefits as a relative safe haven so EURUSD falls. +Reasoning: Simultaneous destruction of major capitals with martial law and insurance-system collapse implies an extreme, sudden global risk-off/liquidity shock (GFC/COVID-like) plus acute Europe-specific tail risk and EUR dislocation (Eurozone break-up proxy). Weighted blend emphasizes severe credit stress and safe-haven bid, with EUR underperformance; scaled up to reflect catastrophic severity beyond typical historical episodes. Proposed market shifts: - FX EURUSD: -0.0126 - Equity EUR: -30.8% - Equity USD: -25.2% - Equity Tech: -45.0% - Rates EUR: 1Y -105bp 2Y -99bp 3Y -80bp 5Y -84bp 10Y -84bp 30Y -63bp - Rates USD: 1Y -210bp 2Y -196bp 3Y -161bp 5Y -154bp 10Y -126bp 30Y -84bp - Credit EUR: 1Y +35bp 2Y +49bp 3Y +60bp 5Y +67bp 10Y +56bp - Credit USD: 1Y +49bp 2Y +63bp 3Y +77bp 5Y +88bp 10Y +74bp + FX EURUSD: -0.1875 + Equity EUR: -63.7% + Equity USD: -48.0% + Rates EUR: 1Y -8bp 2Y -9bp 3Y -9bp 5Y -6bp 10Y -4bp 30Y -2bp + Rates USD: 1Y -225bp 2Y -240bp 3Y -240bp 5Y -225bp 10Y -180bp 30Y -135bp + Credit EUR: 1Y +270bp 2Y +375bp 3Y +435bp 5Y +495bp 10Y +465bp + Credit USD: 1Y +225bp 2Y +300bp 3Y +360bp 5Y +420bp 10Y +375bp + Credit Sovereign: 1Y +675bp 2Y +945bp 3Y +1080bp 5Y +1215bp 10Y +1080bp β–Ά Step 2/5 Parsing todaysmarket.xml … βœ“ Discovered 2 currencies, 2 discount curves, 2 equities, 1 credit names @@ -229,6 +227,7 @@ Proposed market shifts: βœ“ Written: /Users/matthiasgroncki/quant-dev/IPythonScripts/EconomicStressAgentORE/oredata/ore_agent.xml β–Ά Step 4/5 Running ORE … +Running ORE with config: ore_agent.xml from workspace: /Users/matthiasgroncki/quant-dev/IPythonScripts/EconomicStressAgentORE/oredata Loading inputs OK Requested analytics STRESS StressTestAnalytic: Build Market OK @@ -247,29 +246,29 @@ ORE done. Portfolio Stress Test Impact Report ════════════════════════════════════════════════════════════ - TOTAL P&L: -6,614,962 EUR [β–Ό LOSS] - -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ Trade β”‚ Base NPV β”‚ Stressed NPV β”‚ P&L Impact β”‚ -β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ -β”‚ EUR6MSwap β”‚ 5,924,804 β”‚ 4,415,089 β”‚ -1,509,715 β”‚ -β”‚ EquityCFD_USD β”‚ 76,647 β”‚ -1,359,666 β”‚ -1,436,313 β”‚ -β”‚ EquityCFD_EUR β”‚ 1,263,244 β”‚ -172,789 β”‚ -1,436,033 β”‚ -β”‚ XccySwap β”‚ 268,878 β”‚ -1,027,997 β”‚ -1,296,875 β”‚ -β”‚ Cap β”‚ 4,988,927 β”‚ 4,045,146 β”‚ -943,781 β”‚ -β”‚ ZeroCouponInflationSwapEUR β”‚ -20,433 β”‚ -21,311 β”‚ -878 β”‚ -β”‚ CDS β”‚ -64,059 β”‚ -55,424 β”‚ +8,635 β”‚ -β”œβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β”Όβ•β•β•β•β•β•β•β•β•β•β•β•β”Όβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β”Όβ•β•β•β•β•β•β•β•β•β•β•β•β”€ -β”‚ TOTAL β”‚ 12,438,009 β”‚ 5,823,047 β”‚ -6,614,962 β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + TOTAL P&L: -20,452,247 EUR [β–Ό LOSS] + +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Trade β”‚ Base NPV β”‚ Stressed NPV β”‚ P&L Impact β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ XccySwap β”‚ 268,878 β”‚ -18,241,162 β”‚ -18,510,040 β”‚ +β”‚ EquityCFD_USD β”‚ 76,647 β”‚ -3,119,320 β”‚ -3,195,967 β”‚ +β”‚ EquityCFD_EUR β”‚ 1,263,244 β”‚ -1,709,064 β”‚ -2,972,308 β”‚ +β”‚ EUR6MSwap β”‚ 5,924,804 β”‚ 5,774,330 β”‚ -150,474 β”‚ +β”‚ CDS β”‚ -6,405,864 β”‚ -2,029,322 β”‚ +4,376,542 β”‚ +β”œβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β”Όβ•β•β•β•β•β•β•β•β•β•β•β•β”Όβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β”Όβ•β•β•β•β•β•β•β•β•β•β•β•β•β”€ +β”‚ TOTAL β”‚ 1,127,710 β”‚ -19,324,538 β”‚ -20,452,247 β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ──────────────────────────────────────────────────────────── Narrative Summary ──────────────────────────────────────────────────────────── -The β€œAI mass layoffs in high‑income jobs” scenario produces a severe risk‑off shock across equities and a strong rally in core rates, alongside widening credit spreads and a modest EURUSD move. Equities fall sharply (EUR equities -30.8%, USD equities -25.2%, and Tech -45.0%), while rates decline materially (EUR down 63–105bp across 1Y–30Y; USD down 84–210bp across 1Y–30Y). Credit spreads widen in both regions (EUR +35–67bp out to 10Y; USD +49–88bp out to 10Y). Under these combined shifts, portfolio NPV drops from EUR 12,438,009 to EUR 5,823,047, for a total P&L impact of -EUR 6,614,962 (about -53% of base NPV). +Under the β€œglobal flight to safety” shockβ€”EURUSD down 0.1875 (absolute), equities down 63.7% in EUR and 48.0% in USD, sharp USD rate rallies (down 225–240bp out to 5Y and down 180bp at 10Y), and severe spread widening (EUR credit +270bp to +495bp, USD credit +225bp to +420bp, sovereign credit +675bp to +1,215bp)β€”the portfolio moves from a base NPV of EUR 1,127,710 to a stressed NPV of EUR -19,324,538. This is a total P&L impact of EUR -20,452,247, indicating the portfolio is highly exposed to combined FX dislocation, equity crash risk, and cross-currency/rates basis dynamics under extreme systemic stress. + +The loss is overwhelmingly driven by the cross-currency and equity risk factors. The XccySwap contributes EUR -18,510,040 of the total drawdown, with its valuation swinging from EUR 268,878 to EUR -18,241,162, consistent with a regime where USD funding stress and large EURUSD moves dominate outcomes. Equity risk is the next major driver: EquityCFD_USD loses EUR -3,195,967 (EUR 76,647 to EUR -3,119,320) and EquityCFD_EUR loses EUR -2,972,308 (EUR 1,263,244 to EUR -1,709,064), reflecting the -48.0% and -63.7% equity shocks compounded by the EURUSD drop for USD-denominated exposure when reported in EUR. -Losses are concentrated in equity and rates/FX-sensitive positions. The two equity CFD positions are the largest equity-driven detractors: EquityCFD_USD loses -EUR 1,436,313 (from EUR 76,647 to -EUR 1,359,666) and EquityCFD_EUR loses -EUR 1,436,033 (from EUR 1,263,244 to -EUR 172,789), consistent with the large equity drawdowns in both currencies. The rates rally and cross-currency dynamics also contribute heavily: the EUR6MSwap loses -EUR 1,509,715 (from EUR 5,924,804 to EUR 4,415,089) and the XccySwap loses -EUR 1,296,875 (from EUR 268,878 to -EUR 1,027,997), indicating significant exposure to curve moves and/or basis/FX interactions (EURUSD shifts by -0.0126 in absolute terms). The Cap position also detracts -EUR 943,781 (from EUR 4,988,927 to EUR 4,045,146), suggesting the scenario’s large rate move and volatility/convexity effects are adverse for this structure despite the directionally lower yields. +Offsetting gains come primarily from credit protection. The CDS position generates EUR +4,376,542 (from EUR -6,405,864 to EUR -2,029,322), benefiting from the very large credit and sovereign spread widening (up to +495bp in EUR credit and +1,215bp in sovereign spreads at 5Y). Rates contribute only marginally: the EUR6MSwap loses EUR -150,474 (EUR 5,924,804 to EUR 5,774,330), consistent with relatively small EUR curve shifts (single-digit bp rally across tenors) compared with the much larger moves in USD rates and credit. -Offsets are limited and primarily credit-related. The CDS position gains EUR 8,635 (from -EUR 64,059 to -EUR 55,424), consistent with credit spread widening improving the value of protection, but the magnitude is small relative to the equity and rates-driven losses. The ZeroCouponInflationSwapEUR is essentially flat (-EUR 878), providing negligible diversification in this shock. Overall, the risk interpretation is that the portfolio is materially exposed to a synchronized risk-off regime where equities reprice lower and rates rally sharply; credit hedging is present but insufficient in size to counteract the dominant equity and rates/cross-currency losses, leaving the portfolio vulnerable to macro shocks that combine growth fears, policy-rate cuts, and widening spreads. +Overall, the stress test indicates a concentrated tail risk profile: the portfolio’s protection via CDS is meaningful but insufficient against the dominant cross-currency swap exposure and equity beta in a simultaneous FX break and equity crash. The result suggests the portfolio is effectively short extreme funding/FX dislocation (via the XccySwap) and long risk assets (via the equity CFDs), with credit hedges providing partial convexity but not enough to prevent a large negative NPV under systemic shock. The key risk interpretation is that diversification breaks down in this scenario and the portfolio’s P&L is governed by a small number of positions whose sensitivities amplify precisely when liquidity and basis risks are most stressed. ``` diff --git a/EconomicStressAgentORE/data/scenarios.json b/EconomicStressAgentORE/data/scenarios.json index 67801ec..0ba7a2e 100644 --- a/EconomicStressAgentORE/data/scenarios.json +++ b/EconomicStressAgentORE/data/scenarios.json @@ -895,5 +895,93 @@ } } } + }, + { + "name": "9/11 Terror Attack", + "period": "Sep 2001", + "description": "Coordinated terrorist attacks on the World Trade Center and Pentagon caused an immediate market shutdown and a sharp sell-off on reopening. The Fed cut rates 50bp within days. Flight to US Treasuries intensified, equities fell sharply, and credit spreads widened across aviation, insurance, and broader corporate sectors.", + "shifts": { + "rates": { + "EUR": { + "1Y": -0.008, + "2Y": -0.007, + "5Y": -0.005, + "10Y": -0.003, + "30Y": -0.002 + }, + "USD": { + "1Y": -0.018, + "2Y": -0.015, + "5Y": -0.01, + "10Y": -0.006, + "30Y": -0.003 + } + }, + "fx": { "EURUSD": 0.02 }, + "equity": { + "EUR": -0.14, + "USD": -0.12 + }, + "credit": { + "EUR": { + "1Y": 0.005, + "2Y": 0.007, + "3Y": 0.009, + "5Y": 0.01, + "10Y": 0.008 + }, + "USD": { + "1Y": 0.008, + "2Y": 0.011, + "3Y": 0.013, + "5Y": 0.015, + "10Y": 0.012 + } + } + } + }, + { + "name": "2011 Tōhoku Earthquake / Fukushima", + "period": "Mar 2011", + "description": "Magnitude-9.0 earthquake and subsequent tsunami devastated northeastern Japan, triggering a nuclear crisis at Fukushima Daiichi. The Nikkei fell ~20% in the first two weeks. Global risk-off sentiment pushed European and US equities lower, rates declined on growth concerns, and credit spreads widened modestly. Safe-haven flows initially strengthened the yen before G7 coordinated intervention reversed the move.", + "shifts": { + "rates": { + "EUR": { + "1Y": -0.004, + "2Y": -0.005, + "5Y": -0.006, + "10Y": -0.007, + "30Y": -0.005 + }, + "USD": { + "1Y": -0.006, + "2Y": -0.008, + "5Y": -0.009, + "10Y": -0.008, + "30Y": -0.005 + } + }, + "fx": { "EURUSD": 0.01 }, + "equity": { + "EUR": -0.1, + "USD": -0.06 + }, + "credit": { + "EUR": { + "1Y": 0.003, + "2Y": 0.004, + "3Y": 0.005, + "5Y": 0.006, + "10Y": 0.005 + }, + "USD": { + "1Y": 0.003, + "2Y": 0.004, + "3Y": 0.005, + "5Y": 0.005, + "10Y": 0.004 + } + } + } } ] diff --git a/EconomicStressAgentORE/ore_runner.py b/EconomicStressAgentORE/ore_runner.py index 6ed301d..9b65f55 100644 --- a/EconomicStressAgentORE/ore_runner.py +++ b/EconomicStressAgentORE/ore_runner.py @@ -78,7 +78,7 @@ def run( def _run_via_python_api(ore_xml_rel: Path, workspace: Path) -> None: """Execute ORE using the Python bindings (OREApp).""" from ORE import OREApp, Parameters # type: ignore[import] - + print(f"Running ORE with config: {ore_xml_rel} from workspace: {workspace}") orig_cwd = Path.cwd() try: os.chdir(workspace) diff --git a/EconomicStressAgentORE/oredata/Input/portfolio.xml b/EconomicStressAgentORE/oredata/Input/portfolio.xml index cee4011..6cb58a4 100644 --- a/EconomicStressAgentORE/oredata/Input/portfolio.xml +++ b/EconomicStressAgentORE/oredata/Input/portfolio.xml @@ -90,51 +90,7 @@ - - Swap - - CPTY_A - CPTY_A - - - - - Floating - false - EUR - - 100000000 - - A360 - MF - - EUR-EURIBOR-6M - 2 - - 0.0 - - - 0.03 - - false - - - - 20240301 - 20260301 - 3M - TARGET - MF - MF - Backward - false - - - - - - - + Swap @@ -193,7 +149,7 @@ true USD - 1000000 + 100000000 A360 F @@ -305,75 +261,4 @@ - - InflationSwap - - CPTY - NS - - party - 2024-03-05 - - - - - ZeroCouponFixed - true - EUR - - 184500.0000 - - A360 - MF - - - 2024-01-15 - 2029-01-15 - 0D - EUR - MF - Zero - - - - - 0.016500 - - Compounded - - - - CPI - false - EUR - - 184500 - - true - - - A360 - F - - - EUR - - 2029-01-15 - - - - - EUHICPXT - - 0 - - 114.4 - 2024-01-15 - 3M - Linear - true - - - - \ No newline at end of file