<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>derivatives | Joey O'Brien</title><link>https://obrienjoey.github.io/tags/derivatives/</link><atom:link href="https://obrienjoey.github.io/tags/derivatives/index.xml" rel="self" type="application/rss+xml"/><description>derivatives</description><generator>Source Themes Academic (https://sourcethemes.com/academic/)</generator><language>en-us</language><copyright>© 2026 Joey O'Brien</copyright><lastBuildDate>Tue, 28 Jul 2026 00:00:00 +0000</lastBuildDate><image><url>https://obrienjoey.github.io/img/og-banner.png</url><title>derivatives</title><link>https://obrienjoey.github.io/tags/derivatives/</link></image><item><title>From CSV to ORE: Automating Trade Translation with Python</title><link>https://obrienjoey.github.io/post/ore_trade_translation/</link><pubDate>Tue, 28 Jul 2026 00:00:00 +0000</pubDate><guid>https://obrienjoey.github.io/post/ore_trade_translation/</guid><description>&lt;p>Trade ingestion is often one of the biggest challenges when adopting a new quantitative risk platform. Portfolio definitions vary considerably across trading desks, front-office systems, and vendor platforms, with each using its own conventions and data formats. To perform valuations or risk analysis in &lt;strong>Open Source Risk Engine (ORE)&lt;/strong>, these trades must ultimately be translated into ORE&amp;rsquo;s XML portfolio schema. This translation step is often a significant source of effort, making robust and automated trade ingestion a key requirement for any successful ORE deployment.&lt;/p>
&lt;p>Once you can &lt;a href="https://obrienjoey.github.io/post/ore_sofr_bootstrap/">bootstrap curves&lt;/a> and construct a market environment, the next step is to load your trade portfolio into the engine. This post focuses on that process. The missing piece is a translation layer that converts a flat trade representation into ORE&amp;rsquo;s hierarchical XML portfolio schema, producing a valid, schema-compliant portfolio ready for valuation and risk analysis.&lt;/p>
&lt;p>The translation layer is deliberately independent of the underlying data source. Whether trade data originates from a CSV file, database, or API, the workflow is the same: load the data into a standard Python structure, such as a Pandas DataFrame, apply the mapping rules, and serialise the result as ORE XML. This separation keeps the translation logic reusable while allowing it to integrate with a wide range of storage and data ingestion pipelines.&lt;/p>
&lt;p>The approach described here is based on work we have carried out for clients evaluating ORE as an alternative to established commercial risk platforms, as well as organisations undertaking full production migrations. In some cases, the objective was to build a proof of concept that could validate pricing and risk against an incumbent system; in others, it was to develop a robust, production-grade trade translation pipeline. Although every implementation differs, the underlying design principles remain remarkably consistent. This post focuses on those principles and the practices that have proven most effective across a range of real-world projects.&lt;/p>
&lt;blockquote>
&lt;h3 id="walkthrough-objectives">Walkthrough Objectives&lt;/h3>
&lt;p>In this guide, we will:&lt;/p>
&lt;ol>
&lt;li>Build a Python translation adaptor using standard libraries (&lt;code>pandas&lt;/code> and &lt;code>xml.etree.ElementTree&lt;/code>).&lt;/li>
&lt;li>Convert a flat trade file of European Equity Options into a fully schema-valid ORE XML portfolio.&lt;/li>
&lt;li>Validate the generated XML portfolio against ORE&amp;rsquo;s XML Schema Definitions (XSD) using &lt;code>lxml&lt;/code>.&lt;/li>
&lt;li>Price the generated portfolio using ORE&amp;rsquo;s Python bindings.&lt;/li>
&lt;/ol>
&lt;p>All scripts and configuration files used in this guide are available for download in the resources section below.&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h2 id="trades-csv">1. The Trade File — &lt;code>trades.csv&lt;/code>&lt;/h2>
&lt;p>Our starting point is a standard, flat trade file representing a book of vanilla Equity Options. The CSV file (&lt;code>trades.csv&lt;/code>) contains basic fields that any trading system would capture:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th align="left">TradeID&lt;/th>
&lt;th align="left">Counterparty&lt;/th>
&lt;th align="left">Underlying&lt;/th>
&lt;th align="left">Currency&lt;/th>
&lt;th align="left">Quantity&lt;/th>
&lt;th align="left">Strike&lt;/th>
&lt;th align="left">OptionType&lt;/th>
&lt;th align="left">LongShort&lt;/th>
&lt;th align="left">ExpiryDate&lt;/th>
&lt;th align="left">PremiumAmount&lt;/th>
&lt;th align="left">PremiumCcy&lt;/th>
&lt;th align="left">PremiumDate&lt;/th>
&lt;th align="left">ExerciseStyle&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td align="left">&lt;strong>&lt;code>EQ_CALL_SP5_1&lt;/code>&lt;/strong>&lt;/td>
&lt;td align="left">CPTY_A&lt;/td>
&lt;td align="left">RIC:.SPX&lt;/td>
&lt;td align="left">USD&lt;/td>
&lt;td align="left">100.0&lt;/td>
&lt;td align="left">4000.0&lt;/td>
&lt;td align="left">Call&lt;/td>
&lt;td align="left">Long&lt;/td>
&lt;td align="left">2023-07-31&lt;/td>
&lt;td align="left">150.0&lt;/td>
&lt;td align="left">USD&lt;/td>
&lt;td align="left">2023-02-02&lt;/td>
&lt;td align="left">E&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td align="left">&lt;strong>&lt;code>EQ_PUT_SP5_2&lt;/code>&lt;/strong>&lt;/td>
&lt;td align="left">CPTY_B&lt;/td>
&lt;td align="left">RIC:.SPX&lt;/td>
&lt;td align="left">USD&lt;/td>
&lt;td align="left">50.0&lt;/td>
&lt;td align="left">4100.0&lt;/td>
&lt;td align="left">Put&lt;/td>
&lt;td align="left">Short&lt;/td>
&lt;td align="left">2023-07-31&lt;/td>
&lt;td align="left">120.0&lt;/td>
&lt;td align="left">USD&lt;/td>
&lt;td align="left">2023-02-02&lt;/td>
&lt;td align="left">E&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Each row represents an option position:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;code>EQ_CALL_SP5_1&lt;/code>&lt;/strong>: A long position in 100 Call options on the S&amp;amp;P 500 (&lt;code>RIC:.SPX&lt;/code>) with a strike of 4000.0, expiring on 31 July 2023, with European style (&lt;code>ExerciseStyle: E&lt;/code>).&lt;/li>
&lt;li>&lt;strong>&lt;code>EQ_PUT_SP5_2&lt;/code>&lt;/strong>: A short position in 50 Put options on the S&amp;amp;P 500 (&lt;code>RIC:.SPX&lt;/code>) with a strike of 4100.0, expiring on the same date, also European style (&lt;code>ExerciseStyle: E&lt;/code>).&lt;/li>
&lt;/ul>
&lt;p>While we are using a flat CSV file for this case study to keep the setup easy to follow, the source format in a production environment can be anything. Because the mapping step is decoupled from the ingestion source, you can swap this file-reading step for a SQL database connector, an API query returning JSON payloads, or a parser reading complex XML/FpML records. Once the trade parameters are loaded into a Python data structure, the downstream translation mapping logic remains identical.&lt;/p>
&lt;hr>
&lt;h2 id="market-data-config">2. Market Data &amp;amp; ORE Configuration&lt;/h2>
&lt;p>Before we can price these trades, we need the supporting market data and configuration infrastructure in ORE format. As detailed in our &lt;a href="https://obrienjoey.github.io/post/ore_sofr_bootstrap/">previous ORE Curve Bootstrapping post&lt;/a>, ORE references market inputs using a structured, space-delimited text file (&lt;code>market.txt&lt;/code>) representing market quotes as of our valuation date (&lt;code>2023-01-31&lt;/code>).&lt;/p>
&lt;p>For our equity options case study, the relevant quotes in &lt;code>market.txt&lt;/code> are:&lt;/p>
&lt;pre>&lt;code class="language-text">2023-01-31 EQUITY/PRICE/RIC:.SPX/USD 4000.00
2023-01-31 EQUITY_FWD/PRICE/RIC:.SPX/USD/6M 4000.00
2023-01-31 EQUITY_OPTION/RATE_LNVOL/RIC:.SPX/USD/6M/ATMF 0.10
&lt;/code>&lt;/pre>
&lt;p>These quotes follow ORE&amp;rsquo;s strict hierarchical format:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;code>EQUITY/PRICE/[Name]/[Currency]&lt;/code>&lt;/strong>: Represents the spot price of the equity asset. Here, the S&amp;amp;P 500 index (&lt;code>RIC:.SPX&lt;/code>) is priced at &lt;code>4000.00&lt;/code> USD.&lt;/li>
&lt;li>&lt;strong>&lt;code>EQUITY_FWD/PRICE/[Name]/[Currency]/[Tenor]&lt;/code>&lt;/strong>: Represents forward equity price quotes.&lt;/li>
&lt;li>&lt;strong>&lt;code>EQUITY_OPTION/RATE_LNVOL/[Name]/[Currency]/[Tenor]/[Strike]&lt;/code>&lt;/strong>: Specifies lognormal implied volatility. In this case, it is set to a flat 10% (&lt;code>0.10&lt;/code>) for a 6-month at-the-money forward tenor. The 6-month tenor (&lt;code>6M&lt;/code>) corresponds directly to our trade expiry date (&lt;code>2023-07-31&lt;/code>) from the valuation date (&lt;code>2023-01-31&lt;/code>).&lt;/li>
&lt;/ul>
&lt;h3 id="ore-configuration-dependency-chain">ORE Configuration Dependency Chain&lt;/h3>
&lt;p>To instruct ORE how to parse these quotes and evaluate portfolios, a set of XML configurations must be passed to the engine. These configurations form a standard dependency tree:&lt;/p>
&lt;pre>&lt;code class="language-mermaid">graph TD
A[ore.xml &amp;lt;br&amp;gt; Master Parameters] --&amp;gt; B[todaysmarket.xml &amp;lt;br&amp;gt; Market Config]
A --&amp;gt; C[pricingengine.xml &amp;lt;br&amp;gt; Pricing Models]
A --&amp;gt; G[portfolio.xml &amp;lt;br&amp;gt; Trades Portfolio]
B --&amp;gt; D[curveconfig.xml &amp;lt;br&amp;gt; Yield/Vol Curves]
B --&amp;gt; E[conventions.xml &amp;lt;br&amp;gt; Day-Counters/Rules]
B --&amp;gt; F[market.txt &amp;lt;br&amp;gt; Raw Quotes]
&lt;/code>&lt;/pre>
&lt;ul>
&lt;li>&lt;strong>&lt;code>todaysmarket.xml&lt;/code>&lt;/strong>: Maps our active equity curve (&lt;code>RIC:.SPX&lt;/code>) and the discount curve (&lt;code>USD-SOFR&lt;/code>) to the respective market quotes in &lt;code>market.txt&lt;/code>.&lt;/li>
&lt;li>&lt;strong>&lt;code>curveconfig.xml&lt;/code>&lt;/strong>: Configures how curves are bootstrapped or loaded (e.g., flat volatility grids, interest rate discount factors).&lt;/li>
&lt;li>&lt;strong>&lt;code>pricingengine.xml&lt;/code>&lt;/strong>: Specifies the pricing engine. For &lt;code>EquityOption&lt;/code>, it assigns the analytical Black-Scholes-Merton engine.&lt;/li>
&lt;/ul>
&lt;p>For a detailed review of interest rate curve bootstrapping and config construction, refer to our &lt;a href="https://obrienjoey.github.io/post/ore_sofr_bootstrap/">previous bootstrapping post&lt;/a>.&lt;/p>
&lt;hr>
&lt;h2 id="portfolio-xml">3. The Target ORE Schema — &lt;code>portfolio.xml&lt;/code>&lt;/h2>
&lt;p>ORE&amp;rsquo;s portfolio file follows a consistent three-level structure:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;code>&amp;lt;Portfolio&amp;gt;&lt;/code>&lt;/strong> — the root element. It contains all trades in the book.&lt;/li>
&lt;li>&lt;strong>&lt;code>&amp;lt;Trade id=&amp;quot;...&amp;quot;&amp;gt;&lt;/code>&lt;/strong> — one per position. Each trade carries a &lt;code>&amp;lt;TradeType&amp;gt;&lt;/code> tag (e.g. &lt;code>EquityOption&lt;/code>, &lt;code>Swap&lt;/code>, &lt;code>FxOption&lt;/code>) that tells ORE which parser to use, followed by two child blocks:
&lt;ol>
&lt;li>&lt;strong>&lt;code>&amp;lt;Envelope&amp;gt;&lt;/code>&lt;/strong> — trade metadata: counterparty, netting set ID, and any custom additional fields.&lt;/li>
&lt;li>&lt;strong>A trade-specific data element&lt;/strong> — for our equity options, this is &lt;code>&amp;lt;EquityOptionData&amp;gt;&lt;/code>. For swaps it would be &lt;code>&amp;lt;SwapData&amp;gt;&lt;/code>, for FX options &lt;code>&amp;lt;FxOptionData&amp;gt;&lt;/code>, and so on. This block holds all the economic terms (strike, quantity, expiry, etc.).&lt;/li>
&lt;/ol>
&lt;/li>
&lt;/ul>
&lt;p>ORE covers an exceptionally broad range of financial products, spanning vanilla and exotic interest rate, FX, equity, inflation, credit, and commodity derivatives. For a comprehensive guide detailing how this extensive product catalogue is represented and structured inside the ORE XML schema, you can refer to the &lt;a href="https://github.com/OpenSourceRisk/Engine/blob/master/Docs/products.pdf">ORE Product XML Guide (products.pdf)&lt;/a>.&lt;/p>
&lt;p>A single translated Call option trade inside the &lt;code>&amp;lt;Portfolio&amp;gt;&lt;/code> schema looks like this:&lt;/p>
&lt;pre>&lt;code class="language-xml">&amp;lt;Portfolio&amp;gt;
&amp;lt;Trade id=&amp;quot;EQ_CALL_SP5_1&amp;quot;&amp;gt;
&amp;lt;TradeType&amp;gt;EquityOption&amp;lt;/TradeType&amp;gt;
&amp;lt;Envelope&amp;gt;
&amp;lt;CounterParty&amp;gt;CPTY_A&amp;lt;/CounterParty&amp;gt;
&amp;lt;NettingSetId/&amp;gt;
&amp;lt;AdditionalFields/&amp;gt;
&amp;lt;/Envelope&amp;gt;
&amp;lt;EquityOptionData&amp;gt;
&amp;lt;OptionData&amp;gt;
&amp;lt;LongShort&amp;gt;Long&amp;lt;/LongShort&amp;gt;
&amp;lt;OptionType&amp;gt;Call&amp;lt;/OptionType&amp;gt;
&amp;lt;Style&amp;gt;European&amp;lt;/Style&amp;gt;
&amp;lt;ExerciseDates&amp;gt;
&amp;lt;ExerciseDate&amp;gt;2023-07-31&amp;lt;/ExerciseDate&amp;gt;
&amp;lt;/ExerciseDates&amp;gt;
&amp;lt;Settlement&amp;gt;Cash&amp;lt;/Settlement&amp;gt;
&amp;lt;PremiumAmount&amp;gt;150.00&amp;lt;/PremiumAmount&amp;gt;
&amp;lt;PremiumCurrency&amp;gt;USD&amp;lt;/PremiumCurrency&amp;gt;
&amp;lt;PremiumPayDate&amp;gt;2023-02-02&amp;lt;/PremiumPayDate&amp;gt;
&amp;lt;/OptionData&amp;gt;
&amp;lt;Underlying&amp;gt;
&amp;lt;Type&amp;gt;Equity&amp;lt;/Type&amp;gt;
&amp;lt;Name&amp;gt;RIC:.SPX&amp;lt;/Name&amp;gt;
&amp;lt;/Underlying&amp;gt;
&amp;lt;Currency&amp;gt;USD&amp;lt;/Currency&amp;gt;
&amp;lt;Quantity&amp;gt;100.00&amp;lt;/Quantity&amp;gt;
&amp;lt;Strike&amp;gt;4000.00&amp;lt;/Strike&amp;gt;
&amp;lt;/EquityOptionData&amp;gt;
&amp;lt;/Trade&amp;gt;
&amp;lt;/Portfolio&amp;gt;
&lt;/code>&lt;/pre>
&lt;hr>
&lt;h2 id="mapping-table">4. Mapping CSV Fields $\rightarrow$ XML Paths&lt;/h2>
&lt;p>Before writing the adaptor, we establish a formal field-mapping specification:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th align="left">Source CSV Column&lt;/th>
&lt;th align="left">Target XML Path&lt;/th>
&lt;th align="left">Handling / Defaulting&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td align="left">&lt;strong>&lt;code>TradeID&lt;/code>&lt;/strong>&lt;/td>
&lt;td align="left">&lt;code>/Trade/@id&lt;/code>&lt;/td>
&lt;td align="left">Unique string identifier&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td align="left">&lt;strong>&lt;code>Counterparty&lt;/code>&lt;/strong>&lt;/td>
&lt;td align="left">&lt;code>/Trade/Envelope/CounterParty&lt;/code>&lt;/td>
&lt;td align="left">Standard counterparty mapping&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td align="left">Hard-coded&lt;/td>
&lt;td align="left">&lt;code>/Trade/TradeType&lt;/code>&lt;/td>
&lt;td align="left">Defaulted to &lt;code>&amp;quot;EquityOption&amp;quot;&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td align="left">&lt;strong>&lt;code>LongShort&lt;/code>&lt;/strong>&lt;/td>
&lt;td align="left">&lt;code>/Trade/EquityOptionData/OptionData/LongShort&lt;/code>&lt;/td>
&lt;td align="left">Expected: &lt;code>&amp;quot;Long&amp;quot;&lt;/code> or &lt;code>&amp;quot;Short&amp;quot;&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td align="left">&lt;strong>&lt;code>OptionType&lt;/code>&lt;/strong>&lt;/td>
&lt;td align="left">&lt;code>/Trade/EquityOptionData/OptionData/OptionType&lt;/code>&lt;/td>
&lt;td align="left">Expected: &lt;code>&amp;quot;Call&amp;quot;&lt;/code> or &lt;code>&amp;quot;Put&amp;quot;&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td align="left">&lt;strong>&lt;code>ExerciseStyle&lt;/code>&lt;/strong>&lt;/td>
&lt;td align="left">&lt;code>/Trade/EquityOptionData/OptionData/Style&lt;/code>&lt;/td>
&lt;td align="left">Mapped: &lt;code>'E'&lt;/code> $\rightarrow$ &lt;code>&amp;quot;European&amp;quot;&lt;/code>, &lt;code>'A'&lt;/code> $\rightarrow$ &lt;code>&amp;quot;American&amp;quot;&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td align="left">&lt;strong>&lt;code>ExpiryDate&lt;/code>&lt;/strong>&lt;/td>
&lt;td align="left">&lt;code>/Trade/EquityOptionData/OptionData/ExerciseDates/ExerciseDate&lt;/code>&lt;/td>
&lt;td align="left">ISO date format (&lt;code>YYYY-MM-DD&lt;/code>)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td align="left">Hard-coded&lt;/td>
&lt;td align="left">&lt;code>/Trade/EquityOptionData/OptionData/Settlement&lt;/code>&lt;/td>
&lt;td align="left">Defaulted to &lt;code>&amp;quot;Cash&amp;quot;&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td align="left">&lt;strong>&lt;code>PremiumAmount&lt;/code>&lt;/strong>&lt;/td>
&lt;td align="left">&lt;code>/Trade/EquityOptionData/OptionData/PremiumAmount&lt;/code>&lt;/td>
&lt;td align="left">Optional numerical value&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td align="left">&lt;strong>&lt;code>PremiumCcy&lt;/code>&lt;/strong>&lt;/td>
&lt;td align="left">&lt;code>/Trade/EquityOptionData/OptionData/PremiumCurrency&lt;/code>&lt;/td>
&lt;td align="left">Optional currency code&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td align="left">&lt;strong>&lt;code>PremiumDate&lt;/code>&lt;/strong>&lt;/td>
&lt;td align="left">&lt;code>/Trade/EquityOptionData/OptionData/PremiumPayDate&lt;/code>&lt;/td>
&lt;td align="left">Optional payment date&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td align="left">Hard-coded&lt;/td>
&lt;td align="left">&lt;code>/Trade/EquityOptionData/Underlying/Type&lt;/code>&lt;/td>
&lt;td align="left">Defaulted to &lt;code>&amp;quot;Equity&amp;quot;&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td align="left">&lt;strong>&lt;code>Underlying&lt;/code>&lt;/strong>&lt;/td>
&lt;td align="left">&lt;code>/Trade/EquityOptionData/Underlying/Name&lt;/code>&lt;/td>
&lt;td align="left">Asset identifier matching market config&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td align="left">&lt;strong>&lt;code>Currency&lt;/code>&lt;/strong>&lt;/td>
&lt;td align="left">&lt;code>/Trade/EquityOptionData/Currency&lt;/code>&lt;/td>
&lt;td align="left">Trade currency&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td align="left">&lt;strong>&lt;code>Quantity&lt;/code>&lt;/strong>&lt;/td>
&lt;td align="left">&lt;code>/Trade/EquityOptionData/Quantity&lt;/code>&lt;/td>
&lt;td align="left">Numeric quantity&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td align="left">&lt;strong>&lt;code>Strike&lt;/code>&lt;/strong>&lt;/td>
&lt;td align="left">&lt;code>/Trade/EquityOptionData/Strike&lt;/code>&lt;/td>
&lt;td align="left">Strike price&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="translate-trades-py">5. Building the Python Adaptor — &lt;code>translate_trades.py&lt;/code>&lt;/h2>
&lt;p>Using Python&amp;rsquo;s standard &lt;code>xml.etree.ElementTree&lt;/code> and the &lt;code>pandas&lt;/code> library, we can write &lt;code>translate_trades.py&lt;/code> to parse the CSV and build the portfolio XML structure.&lt;/p>
&lt;h3 id="step-1-initialising-and-parsing-the-csv">Step 1: Initialising and Parsing the CSV&lt;/h3>
&lt;p>We start by reading the trade file with &lt;code>pandas&lt;/code>. We establish the output destination and ensure the parent directory is created.&lt;/p>
&lt;pre>&lt;code class="language-python">import os
import pandas as pd
import xml.etree.ElementTree as ET
from pathlib import Path
BASE_DIR = Path(__file__).parent.resolve()
CSV_PATH = BASE_DIR / &amp;quot;trades.csv&amp;quot;
OUTPUT_XML_PATH = BASE_DIR / &amp;quot;Input&amp;quot; / &amp;quot;portfolio.xml&amp;quot;
os.makedirs(OUTPUT_XML_PATH.parent, exist_ok=True)
df = pd.read_csv(CSV_PATH)
portfolio = ET.Element(&amp;quot;Portfolio&amp;quot;)
&lt;/code>&lt;/pre>
&lt;h3 id="step-2-mapping-rows-to-trade-elements">Step 2: Mapping Rows to Trade Elements&lt;/h3>
&lt;p>We iterate through each row using &lt;code>pd.DataFrame.iterrows()&lt;/code>. For each option, we map the metadata to the &lt;code>&amp;lt;Envelope&amp;gt;&lt;/code> and structural details to &lt;code>&amp;lt;EquityOptionData&amp;gt;&lt;/code>. Notice the &lt;code>pd.notna()&lt;/code> check: premium data is optional in ORE, and this guard ensures we only write premium elements when all premium fields are present.&lt;/p>
&lt;pre>&lt;code class="language-python">for _, row in df.iterrows():
trade_id = str(row['TradeID'])
trade = ET.SubElement(portfolio, &amp;quot;Trade&amp;quot;, id=trade_id)
trade_type = ET.SubElement(trade, &amp;quot;TradeType&amp;quot;)
trade_type.text = &amp;quot;EquityOption&amp;quot;
envelope = ET.SubElement(trade, &amp;quot;Envelope&amp;quot;)
cpty = ET.SubElement(envelope, &amp;quot;CounterParty&amp;quot;)
cpty.text = str(row['Counterparty'])
ET.SubElement(envelope, &amp;quot;NettingSetId&amp;quot;)
ET.SubElement(envelope, &amp;quot;AdditionalFields&amp;quot;)
eq_opt_data = ET.SubElement(trade, &amp;quot;EquityOptionData&amp;quot;)
option_data = ET.SubElement(eq_opt_data, &amp;quot;OptionData&amp;quot;)
long_short = ET.SubElement(option_data, &amp;quot;LongShort&amp;quot;)
long_short.text = str(row['LongShort'])
option_type = ET.SubElement(option_data, &amp;quot;OptionType&amp;quot;)
option_type.text = str(row['OptionType'])
style_mapping = {'E': 'European', 'A': 'American'}
style_code = str(row['ExerciseStyle']).strip().upper() if pd.notna(row.get('ExerciseStyle')) else 'E'
style_val = style_mapping.get(style_code, 'European')
style = ET.SubElement(option_data, &amp;quot;Style&amp;quot;)
style.text = style_val
exercise_dates = ET.SubElement(option_data, &amp;quot;ExerciseDates&amp;quot;)
ex_date = ET.SubElement(exercise_dates, &amp;quot;ExerciseDate&amp;quot;)
ex_date.text = str(row['ExpiryDate'])
settlement = ET.SubElement(option_data, &amp;quot;Settlement&amp;quot;)
settlement.text = &amp;quot;Cash&amp;quot;
if pd.notna(row.get('PremiumAmount')) and pd.notna(row.get('PremiumCcy')) and pd.notna(row.get('PremiumDate')):
amount = ET.SubElement(option_data, &amp;quot;PremiumAmount&amp;quot;)
amount.text = f&amp;quot;{float(row['PremiumAmount']):.2f}&amp;quot;
ccy = ET.SubElement(option_data, &amp;quot;PremiumCurrency&amp;quot;)
ccy.text = str(row['PremiumCcy'])
p_date = ET.SubElement(option_data, &amp;quot;PremiumPayDate&amp;quot;)
p_date.text = str(row['PremiumDate'])
underlying = ET.SubElement(eq_opt_data, &amp;quot;Underlying&amp;quot;)
u_type = ET.SubElement(underlying, &amp;quot;Type&amp;quot;)
u_type.text = &amp;quot;Equity&amp;quot;
u_name = ET.SubElement(underlying, &amp;quot;Name&amp;quot;)
u_name.text = str(row['Underlying'])
currency = ET.SubElement(eq_opt_data, &amp;quot;Currency&amp;quot;)
currency.text = str(row['Currency'])
quantity = ET.SubElement(eq_opt_data, &amp;quot;Quantity&amp;quot;)
quantity.text = f&amp;quot;{float(row['Quantity']):.2f}&amp;quot;
strike = ET.SubElement(eq_opt_data, &amp;quot;Strike&amp;quot;)
strike.text = f&amp;quot;{float(row['Strike']):.2f}&amp;quot;
&lt;/code>&lt;/pre>
&lt;h3 id="step-3-pretty-printing-and-serialisation">Step 3: Pretty Printing and Serialisation&lt;/h3>
&lt;p>Standard &lt;code>ElementTree&lt;/code> serialisation outputs flat, unformatted XML without line breaks or indentation. While syntactically valid, this is difficult to inspect.&lt;/p>
&lt;p>To format the XML cleanly, we use Python&amp;rsquo;s built-in &lt;code>xml.dom.minidom&lt;/code> to re-parse the raw string and apply indentation, stripping any duplicate XML declaration:&lt;/p>
&lt;pre>&lt;code class="language-python">from xml.dom import minidom
def prettify(elem):
&amp;quot;&amp;quot;&amp;quot;Return a pretty-printed XML string for the Element.&amp;quot;&amp;quot;&amp;quot;
rough_string = ET.tostring(elem, 'utf-8')
reparsed = minidom.parseString(rough_string)
return reparsed.toprettyxml(indent=&amp;quot; &amp;quot;)
xml_str = prettify(portfolio)
with open(OUTPUT_XML_PATH, &amp;quot;w&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as f:
lines = xml_str.split(&amp;quot;\n&amp;quot;)
if lines[0].startswith(&amp;quot;&amp;lt;?xml&amp;quot;):
lines = lines[1:]
f.write(&amp;quot;\n&amp;quot;.join(lines))
print(f&amp;quot;Successfully generated ORE Portfolio XML at: {OUTPUT_XML_PATH}&amp;quot;)
&lt;/code>&lt;/pre>
&lt;h3 id="step-4-schema-validation-via-inputxsd">Step 4: Schema Validation via &lt;code>input.xsd&lt;/code>&lt;/h3>
&lt;p>To ensure the translation adaptor does not generate malformed or schema-violating XML portfolios, we validate the generated output against the ORE master schema definition (&lt;code>input.xsd&lt;/code>).&lt;/p>
&lt;p>The complete set of ORE XSD schema files can be obtained from the &lt;a href="https://github.com/OpenSourceRisk/Engine/tree/master/xsd">official ORE repository&lt;/a>. Using &lt;code>lxml&lt;/code>, we load and parse local schema files (&lt;code>input.xsd&lt;/code>, which automatically resolves relative imports of &lt;code>instruments.xsd&lt;/code> and &lt;code>ore_types.xsd&lt;/code>):&lt;/p>
&lt;pre>&lt;code class="language-python">from lxml import etree
def validate_xml_with_local_xsd(xml_path, xsd_path):
&amp;quot;&amp;quot;&amp;quot;Validate generated XML file against ORE's input.xsd schema.&amp;quot;&amp;quot;&amp;quot;
schema = etree.XMLSchema(etree.parse(str(xsd_path)))
xml_doc = etree.parse(str(xml_path))
schema.assertValid(xml_doc)
print(&amp;quot;Success: Generated portfolio XML is valid against ORE schema.&amp;quot;)
&lt;/code>&lt;/pre>
&lt;hr>
&lt;h2 id="run-pricing-py">6. Programmatic Valuation — &lt;code>run_pricing.py&lt;/code>&lt;/h2>
&lt;p>Once the XML portfolio is generated, we run the ORE valuation engine. Rather than executing a command-line subprocess, we use the ORE Python bindings to load configurations and trigger the run programmatically:&lt;/p>
&lt;pre>&lt;code class="language-python">import pandas as pd
import ORE as ore
from pathlib import Path
BASE_DIR = Path(__file__).parent.resolve()
INPUT_DIR = BASE_DIR / &amp;quot;Input&amp;quot;
OUTPUT_DIR = BASE_DIR / &amp;quot;Output&amp;quot;
if not (INPUT_DIR / &amp;quot;portfolio.xml&amp;quot;).exists():
print(&amp;quot;Error: portfolio.xml not found. Run translate_trades.py first.&amp;quot;)
exit(1)
print(&amp;quot;Running ORE Trade Pricing...&amp;quot;)
params = ore.Parameters()
params.fromFile(str(INPUT_DIR / &amp;quot;ore.xml&amp;quot;))
app = ore.OREApp(params)
app.run()
print(&amp;quot;--- ORE Trade Translation &amp;amp; Pricing Completed Successfully! ---&amp;quot;)
df_npv = pd.read_csv(OUTPUT_DIR / &amp;quot;npv.csv&amp;quot;)
print(&amp;quot;\nCalculated NPV Results:&amp;quot;)
print(df_npv[['#TradeId', 'TradeType', 'NPV', 'NpvCurrency']])
&lt;/code>&lt;/pre>
&lt;p>Running &lt;code>run_pricing.py&lt;/code> prints:&lt;/p>
&lt;pre>&lt;code class="language-text">Running ORE Trade Pricing...
--- ORE Trade Translation &amp;amp; Pricing Completed Successfully! ---
Calculated NPV Results:
#TradeId TradeType NPV NpvCurrency
0 EQ_CALL_SP5_1 EquityOption 10818.438615 USD
1 EQ_PUT_SP5_2 EquityOption -8211.213933 USD
&lt;/code>&lt;/pre>
&lt;hr>
&lt;h2 id="7-summary--next-steps">7. Summary &amp;amp; Next Steps&lt;/h2>
&lt;p>This workflow establishes a clean, repeatable pattern for bridging the gap between flat database schemas and ORE’s XML format. Automating the translation layer ensures consistent trade ingestion before moving to full risk and analytics runs.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Production Tip: Data Validation &amp;amp; Ingestion Guardrails&lt;/strong>
Real-world trade files can contain missing values, invalid currency codes, or corrupted dates. Before generating XML, consider adding a pre-validation step (e.g., using &lt;code>pydantic&lt;/code> models or &lt;code>pandas&lt;/code> data type assertions) to fail fast on malformed records before passing XML to ORE.&lt;/p>
&lt;/blockquote>
&lt;h3 id="generalising-the-translation-layer">Generalising the Translation Layer&lt;/h3>
&lt;p>While this guide focuses specifically on European Equity Options, the pattern scales cleanly to other asset classes. In a production risk pipeline, you can structure multi-asset trade translators using a modular dispatch architecture:&lt;/p>
&lt;pre>&lt;code class="language-python"># Factory pattern for multi-asset trade translation
TRANSLATORS = {
&amp;quot;EquityOption&amp;quot;: build_equity_option_xml,
&amp;quot;Swap&amp;quot;: build_swap_xml,
&amp;quot;FxOption&amp;quot;: build_fx_option_xml,
}
for _, row in df.iterrows():
trade_type = row.get(&amp;quot;TradeType&amp;quot;)
builder = TRANSLATORS.get(trade_type)
if builder:
builder(portfolio, row)
&lt;/code>&lt;/pre>
&lt;p>For instance:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Interest Rate Swaps&lt;/strong>: Mapping input fields to ORE&amp;rsquo;s &lt;code>&amp;lt;SwapData&amp;gt;&lt;/code> and generating fixed/floating &lt;code>&amp;lt;LegData&amp;gt;&lt;/code> structures. A basic translation script for a USD interest rate swap outputs:
&lt;pre>&lt;code class="language-xml">&amp;lt;SwapData&amp;gt;
&amp;lt;LegData&amp;gt;
&amp;lt;LegType&amp;gt;Fixed&amp;lt;/LegType&amp;gt;
&amp;lt;Payer&amp;gt;true&amp;lt;/Payer&amp;gt;
&amp;lt;Currency&amp;gt;USD&amp;lt;/Currency&amp;gt;
&amp;lt;PaymentConvention&amp;gt;ModifiedFollowing&amp;lt;/PaymentConvention&amp;gt;
&amp;lt;DayCounter&amp;gt;30/360&amp;lt;/DayCounter&amp;gt;
&amp;lt;!-- Fixed rate schedules, coupons, and notionals --&amp;gt;
&amp;lt;/LegData&amp;gt;
&amp;lt;LegData&amp;gt;
&amp;lt;LegType&amp;gt;Floating&amp;lt;/LegType&amp;gt;
&amp;lt;Payer&amp;gt;false&amp;lt;/Payer&amp;gt;
&amp;lt;Currency&amp;gt;USD&amp;lt;/Currency&amp;gt;
&amp;lt;PaymentConvention&amp;gt;ModifiedFollowing&amp;lt;/PaymentConvention&amp;gt;
&amp;lt;DayCounter&amp;gt;Act/360&amp;lt;/DayCounter&amp;gt;
&amp;lt;Index&amp;gt;USD-SOFR&amp;lt;/Index&amp;gt;
&amp;lt;FixingDays&amp;gt;2&amp;lt;/FixingDays&amp;gt;
&amp;lt;Spreads&amp;gt;0.0010&amp;lt;/Spreads&amp;gt;
&amp;lt;!-- Notional schedules, reset dates, and cap/floor provisions --&amp;gt;
&amp;lt;/LegData&amp;gt;
&amp;lt;/SwapData&amp;gt;
&lt;/code>&lt;/pre>
&lt;/li>
&lt;li>&lt;strong>FX Options&lt;/strong>: Generating &lt;code>&amp;lt;FxOptionData&amp;gt;&lt;/code> blocks with underlying currency pairs, strikes, and style parameters.&lt;/li>
&lt;/ul>
&lt;p>The core infrastructure of handling file paths, writing the metadata envelope, and validating the portfolio against XSD schemas remains identical.&lt;/p>
&lt;h3 id="source-code">Source Code&lt;/h3>
&lt;p>All configuration files and Python scripts demonstrated in this guide are available for download:
&lt;a href="ore_trade_translation_files.zip" class="btn btn-outline-primary btn-sm mt-3 mb-3" download>
&lt;i class="fas fa-download mr-1">&lt;/i> Download Source Code
&lt;/a>
&lt;/p>
&lt;hr>
&lt;h3 id="need-help-with-ore-integration">Need Help with ORE Integration?&lt;/h3>
&lt;p>Integrating a risk engine into an existing data landscape can be complex, involving bespoke trade representations, legacy databases, and unique market conventions.&lt;/p>
&lt;p>If you are evaluating ORE for your team or looking to implement a custom trade translation layer, feel free to &lt;strong>&lt;a href="https://obrienjoey.github.io/contact">reach out&lt;/a>&lt;/strong>. We specialise in designing and building robust ORE integration layers, market data connectors, and custom validation pipelines. Let&amp;rsquo;s discuss your specific product catalogue and requirements.&lt;/p></description></item></channel></rss>