Esc

    The API is under testing and may change before release.

    Book trades from FpML

    You'll read FpML confirmations into a book as your own side of the trade, with each trade discounting under its counterparty's CSA, and see what couldn't be read.

    Before you start

    • helixio installed: see installing.
    • Your confirmations as text, in FpML 5 confirmation view. The example states one inline: a five-year EUR swap written for these docs, between two fictional banks.
    • Your own party's id or LEI, as the documents state it.
    • If you want trades discounted under their CSAs: a counterparty for each one, keyed by an id the documents state for it. See counterparties and CSA.

    The FpML products the reader books are listed under supported products.

    Steps

    1. Collect the documents as (label, text) pairs. The label names a document in the refusals, so a file name works well.
    2. Read them with Book.from_fpml(documents, party=...), naming your own party by its id or LEI. Without party, the first party in each document is yours. The trade id follows the party.
    3. Pass counterparties= with your counterparty master, so each trade discounts under its counterparty's CSA.
    4. Read the refusals, which come back beside the book: one row per trade or document that couldn't be booked.
    5. If you'd rather stop at the first problem, pass strict=True: it raises and books nothing.

    The comments in the program match these steps.

    Rust
    use helixrs::fpml::{PartySelector, ReadOptions};
    use helixrs::{Book, Counterparties, Counterparty, CsaSpec, market_data_needs};
    
    // 1. The documents are (label, text) pairs. The label names a document in the
    //    refusals, so a file name is a good one.
    const CONFIRMATION: &str = r#"<?xml version="1.0" encoding="utf-8"?>
    <dataDocument xmlns="http://www.fpml.org/FpML-5/confirmation" fpmlVersion="5-14">
      <trade>
        <tradeHeader>
          <partyTradeIdentifier>
            <partyReference href="party1" />
            <tradeId tradeIdScheme="https://example.com/trade-id">HX-IRS-0001</tradeId>
          </partyTradeIdentifier>
          <partyTradeIdentifier>
            <partyReference href="party2" />
            <tradeId tradeIdScheme="https://example.com/trade-id">HX-IRS-0002</tradeId>
          </partyTradeIdentifier>
          <tradeDate>2026-09-29</tradeDate>
        </tradeHeader>
        <swap>
          <swapStream>
            <payerPartyReference href="party1" />
            <receiverPartyReference href="party2" />
            <calculationPeriodDates id="floatingCalcPeriodDates">
              <effectiveDate>
                <unadjustedDate>2026-10-01</unadjustedDate>
                <dateAdjustments>
                  <businessDayConvention>NONE</businessDayConvention>
                </dateAdjustments>
              </effectiveDate>
              <terminationDate>
                <unadjustedDate>2031-10-01</unadjustedDate>
                <dateAdjustments>
                  <businessDayConvention>MODFOLLOWING</businessDayConvention>
                  <businessCenters id="primaryBusinessCenters">
                    <businessCenter>EUTA</businessCenter>
                  </businessCenters>
                </dateAdjustments>
              </terminationDate>
              <calculationPeriodDatesAdjustments>
                <businessDayConvention>MODFOLLOWING</businessDayConvention>
                <businessCentersReference href="primaryBusinessCenters" />
              </calculationPeriodDatesAdjustments>
              <calculationPeriodFrequency>
                <periodMultiplier>6</periodMultiplier>
                <period>M</period>
                <rollConvention>1</rollConvention>
              </calculationPeriodFrequency>
            </calculationPeriodDates>
            <paymentDates>
              <calculationPeriodDatesReference href="floatingCalcPeriodDates" />
              <paymentFrequency>
                <periodMultiplier>6</periodMultiplier>
                <period>M</period>
              </paymentFrequency>
              <payRelativeTo>CalculationPeriodEndDate</payRelativeTo>
              <paymentDatesAdjustments>
                <businessDayConvention>MODFOLLOWING</businessDayConvention>
                <businessCentersReference href="primaryBusinessCenters" />
              </paymentDatesAdjustments>
            </paymentDates>
            <resetDates id="resetDates">
              <calculationPeriodDatesReference href="floatingCalcPeriodDates" />
              <resetRelativeTo>CalculationPeriodStartDate</resetRelativeTo>
              <fixingDates>
                <periodMultiplier>-2</periodMultiplier>
                <period>D</period>
                <dayType>Business</dayType>
                <businessDayConvention>NONE</businessDayConvention>
                <businessCentersReference href="primaryBusinessCenters" />
                <dateRelativeTo href="resetDates" />
              </fixingDates>
              <resetFrequency>
                <periodMultiplier>6</periodMultiplier>
                <period>M</period>
              </resetFrequency>
              <resetDatesAdjustments>
                <businessDayConvention>MODFOLLOWING</businessDayConvention>
                <businessCentersReference href="primaryBusinessCenters" />
              </resetDatesAdjustments>
            </resetDates>
            <calculationPeriodAmount>
              <calculation>
                <notionalSchedule>
                  <notionalStepSchedule>
                    <initialValue>25000000.00</initialValue>
                    <currency currencyScheme="http://www.fpml.org/coding-scheme/external/iso4217">EUR</currency>
                  </notionalStepSchedule>
                </notionalSchedule>
                <floatingRateCalculation>
                  <floatingRateIndex>EUR-EURIBOR-Reuters</floatingRateIndex>
                  <indexTenor>
                    <periodMultiplier>6</periodMultiplier>
                    <period>M</period>
                  </indexTenor>
                </floatingRateCalculation>
                <dayCountFraction>ACT/360</dayCountFraction>
              </calculation>
            </calculationPeriodAmount>
          </swapStream>
          <swapStream>
            <payerPartyReference href="party2" />
            <receiverPartyReference href="party1" />
            <calculationPeriodDates id="fixedCalcPeriodDates">
              <effectiveDate>
                <unadjustedDate>2026-10-01</unadjustedDate>
                <dateAdjustments>
                  <businessDayConvention>NONE</businessDayConvention>
                </dateAdjustments>
              </effectiveDate>
              <terminationDate>
                <unadjustedDate>2031-10-01</unadjustedDate>
                <dateAdjustments>
                  <businessDayConvention>MODFOLLOWING</businessDayConvention>
                  <businessCentersReference href="primaryBusinessCenters" />
                </dateAdjustments>
              </terminationDate>
              <calculationPeriodDatesAdjustments>
                <businessDayConvention>MODFOLLOWING</businessDayConvention>
                <businessCentersReference href="primaryBusinessCenters" />
              </calculationPeriodDatesAdjustments>
              <calculationPeriodFrequency>
                <periodMultiplier>1</periodMultiplier>
                <period>Y</period>
                <rollConvention>1</rollConvention>
              </calculationPeriodFrequency>
            </calculationPeriodDates>
            <paymentDates>
              <calculationPeriodDatesReference href="fixedCalcPeriodDates" />
              <paymentFrequency>
                <periodMultiplier>1</periodMultiplier>
                <period>Y</period>
              </paymentFrequency>
              <payRelativeTo>CalculationPeriodEndDate</payRelativeTo>
              <paymentDatesAdjustments>
                <businessDayConvention>MODFOLLOWING</businessDayConvention>
                <businessCentersReference href="primaryBusinessCenters" />
              </paymentDatesAdjustments>
            </paymentDates>
            <calculationPeriodAmount>
              <calculation>
                <notionalSchedule>
                  <notionalStepSchedule>
                    <initialValue>25000000.00</initialValue>
                    <currency currencyScheme="http://www.fpml.org/coding-scheme/external/iso4217">EUR</currency>
                  </notionalStepSchedule>
                </notionalSchedule>
                <fixedRateSchedule>
                  <initialValue>0.024</initialValue>
                </fixedRateSchedule>
                <dayCountFraction>30E/360</dayCountFraction>
              </calculation>
            </calculationPeriodAmount>
          </swapStream>
        </swap>
      </trade>
      <party id="party1">
        <partyId partyIdScheme="http://www.fpml.org/coding-scheme/external/iso17442">HXDEMO00000000000001</partyId>
        <partyName>Helixio Demo Bank</partyName>
      </party>
      <party id="party2">
        <partyId partyIdScheme="http://www.fpml.org/coding-scheme/external/iso17442">EXCPTY00000000000001</partyId>
        <partyName>Example Counterparty Bank</partyName>
      </party>
    </dataDocument>
    "#;
    
    fn main() -> Result<(), Box<dyn std::error::Error>> {
        let documents = [("hx-irs-0001.xml", CONFIRMATION)];
    
        // 2. Read them as your own party. The first party in a document is the
        //    default; `PartySelector::Id` picks one by its id or LEI, and the trade
        //    id follows that party.
        for party in ["HXDEMO00000000000001", "EXCPTY00000000000001"] {
            let options = ReadOptions { party: PartySelector::Id(party.to_string()), ..ReadOptions::default() };
            let read = Book::from_fpml(&documents, &options, None);
            for trade in read.book.trades() {
                println!(
                    "as {party}  booked {}  {}  x{}",
                    trade.id,
                    trade.instrument.product_name(),
                    trade.quantity
                );
            }
        }
    
        // 3. Give the counterparty master, so each trade discounts under its
        //    counterparty's CSA. Here the other side posts USD collateral.
        let master = Counterparties::new().with(Counterparty::new("EXCPTY00000000000001", CsaSpec::new("USD-SOFR")));
        let options = ReadOptions { counterparties: Some(&master), ..ReadOptions::default() };
        let read = Book::from_fpml(&documents, &options, None);
        let needs = market_data_needs(&read.book.trades()[0].instrument)?.needs();
        let discount = needs.iter().find(|(role, _)| role == "discount").expect("a discount curve").1.clone();
        println!("discounts on  {discount}");
    
        // 4. Read the refusals. A document that can't be read is one refusal row,
        //    and the others still book.
        let with_broken = [("broken.xml", "<dataDocument"), documents[0]];
        let read = Book::from_fpml(&with_broken, &ReadOptions::default(), None);
        println!("booked   {}", read.book.trades().len());
        for refusal in &read.refusals {
            println!("refused  {}: {}", refusal.source, refusal.error);
        }
    
        // 5. Or read strictly: the first refusal fails and nothing is booked.
        let raised = Book::from_fpml_strict(&with_broken, &ReadOptions::default(), None).is_err();
        println!("strict read raises  {}", if raised { "yes" } else { "no" });
    
        Ok(())
    }
    Python
    import helixio as hlx
    
    # 1. Collect the documents as (label, text) pairs. The label names a document
    #    in the refusals, so a file name is a good one.
    CONFIRMATION = """<?xml version="1.0" encoding="utf-8"?>
    <dataDocument xmlns="http://www.fpml.org/FpML-5/confirmation" fpmlVersion="5-14">
      <trade>
        <tradeHeader>
          <partyTradeIdentifier>
            <partyReference href="party1" />
            <tradeId tradeIdScheme="https://example.com/trade-id">HX-IRS-0001</tradeId>
          </partyTradeIdentifier>
          <partyTradeIdentifier>
            <partyReference href="party2" />
            <tradeId tradeIdScheme="https://example.com/trade-id">HX-IRS-0002</tradeId>
          </partyTradeIdentifier>
          <tradeDate>2026-09-29</tradeDate>
        </tradeHeader>
        <swap>
          <swapStream>
            <payerPartyReference href="party1" />
            <receiverPartyReference href="party2" />
            <calculationPeriodDates id="floatingCalcPeriodDates">
              <effectiveDate>
                <unadjustedDate>2026-10-01</unadjustedDate>
                <dateAdjustments>
                  <businessDayConvention>NONE</businessDayConvention>
                </dateAdjustments>
              </effectiveDate>
              <terminationDate>
                <unadjustedDate>2031-10-01</unadjustedDate>
                <dateAdjustments>
                  <businessDayConvention>MODFOLLOWING</businessDayConvention>
                  <businessCenters id="primaryBusinessCenters">
                    <businessCenter>EUTA</businessCenter>
                  </businessCenters>
                </dateAdjustments>
              </terminationDate>
              <calculationPeriodDatesAdjustments>
                <businessDayConvention>MODFOLLOWING</businessDayConvention>
                <businessCentersReference href="primaryBusinessCenters" />
              </calculationPeriodDatesAdjustments>
              <calculationPeriodFrequency>
                <periodMultiplier>6</periodMultiplier>
                <period>M</period>
                <rollConvention>1</rollConvention>
              </calculationPeriodFrequency>
            </calculationPeriodDates>
            <paymentDates>
              <calculationPeriodDatesReference href="floatingCalcPeriodDates" />
              <paymentFrequency>
                <periodMultiplier>6</periodMultiplier>
                <period>M</period>
              </paymentFrequency>
              <payRelativeTo>CalculationPeriodEndDate</payRelativeTo>
              <paymentDatesAdjustments>
                <businessDayConvention>MODFOLLOWING</businessDayConvention>
                <businessCentersReference href="primaryBusinessCenters" />
              </paymentDatesAdjustments>
            </paymentDates>
            <resetDates id="resetDates">
              <calculationPeriodDatesReference href="floatingCalcPeriodDates" />
              <resetRelativeTo>CalculationPeriodStartDate</resetRelativeTo>
              <fixingDates>
                <periodMultiplier>-2</periodMultiplier>
                <period>D</period>
                <dayType>Business</dayType>
                <businessDayConvention>NONE</businessDayConvention>
                <businessCentersReference href="primaryBusinessCenters" />
                <dateRelativeTo href="resetDates" />
              </fixingDates>
              <resetFrequency>
                <periodMultiplier>6</periodMultiplier>
                <period>M</period>
              </resetFrequency>
              <resetDatesAdjustments>
                <businessDayConvention>MODFOLLOWING</businessDayConvention>
                <businessCentersReference href="primaryBusinessCenters" />
              </resetDatesAdjustments>
            </resetDates>
            <calculationPeriodAmount>
              <calculation>
                <notionalSchedule>
                  <notionalStepSchedule>
                    <initialValue>25000000.00</initialValue>
                    <currency currencyScheme="http://www.fpml.org/coding-scheme/external/iso4217">EUR</currency>
                  </notionalStepSchedule>
                </notionalSchedule>
                <floatingRateCalculation>
                  <floatingRateIndex>EUR-EURIBOR-Reuters</floatingRateIndex>
                  <indexTenor>
                    <periodMultiplier>6</periodMultiplier>
                    <period>M</period>
                  </indexTenor>
                </floatingRateCalculation>
                <dayCountFraction>ACT/360</dayCountFraction>
              </calculation>
            </calculationPeriodAmount>
          </swapStream>
          <swapStream>
            <payerPartyReference href="party2" />
            <receiverPartyReference href="party1" />
            <calculationPeriodDates id="fixedCalcPeriodDates">
              <effectiveDate>
                <unadjustedDate>2026-10-01</unadjustedDate>
                <dateAdjustments>
                  <businessDayConvention>NONE</businessDayConvention>
                </dateAdjustments>
              </effectiveDate>
              <terminationDate>
                <unadjustedDate>2031-10-01</unadjustedDate>
                <dateAdjustments>
                  <businessDayConvention>MODFOLLOWING</businessDayConvention>
                  <businessCentersReference href="primaryBusinessCenters" />
                </dateAdjustments>
              </terminationDate>
              <calculationPeriodDatesAdjustments>
                <businessDayConvention>MODFOLLOWING</businessDayConvention>
                <businessCentersReference href="primaryBusinessCenters" />
              </calculationPeriodDatesAdjustments>
              <calculationPeriodFrequency>
                <periodMultiplier>1</periodMultiplier>
                <period>Y</period>
                <rollConvention>1</rollConvention>
              </calculationPeriodFrequency>
            </calculationPeriodDates>
            <paymentDates>
              <calculationPeriodDatesReference href="fixedCalcPeriodDates" />
              <paymentFrequency>
                <periodMultiplier>1</periodMultiplier>
                <period>Y</period>
              </paymentFrequency>
              <payRelativeTo>CalculationPeriodEndDate</payRelativeTo>
              <paymentDatesAdjustments>
                <businessDayConvention>MODFOLLOWING</businessDayConvention>
                <businessCentersReference href="primaryBusinessCenters" />
              </paymentDatesAdjustments>
            </paymentDates>
            <calculationPeriodAmount>
              <calculation>
                <notionalSchedule>
                  <notionalStepSchedule>
                    <initialValue>25000000.00</initialValue>
                    <currency currencyScheme="http://www.fpml.org/coding-scheme/external/iso4217">EUR</currency>
                  </notionalStepSchedule>
                </notionalSchedule>
                <fixedRateSchedule>
                  <initialValue>0.024</initialValue>
                </fixedRateSchedule>
                <dayCountFraction>30E/360</dayCountFraction>
              </calculation>
            </calculationPeriodAmount>
          </swapStream>
        </swap>
      </trade>
      <party id="party1">
        <partyId partyIdScheme="http://www.fpml.org/coding-scheme/external/iso17442">HXDEMO00000000000001</partyId>
        <partyName>Helixio Demo Bank</partyName>
      </party>
      <party id="party2">
        <partyId partyIdScheme="http://www.fpml.org/coding-scheme/external/iso17442">EXCPTY00000000000001</partyId>
        <partyName>Example Counterparty Bank</partyName>
      </party>
    </dataDocument>
    """
    documents = [("hx-irs-0001.xml", CONFIRMATION)]
    
    # 2. Read them as your own party. The first party in a document is the default;
    #    `party=` picks one by its id or LEI, and the trade id follows that party.
    for party in ("HXDEMO00000000000001", "EXCPTY00000000000001"):
        book, _ = hlx.Book.from_fpml(documents, party=party)
        for trade_id, instrument, quantity in book.trades():
            print(f"as {party}  booked {trade_id}  {instrument.product()}  x{quantity:g}")
    
    # 3. Give the counterparty master, so each trade discounts under its
    #    counterparty's CSA. Here the other side posts USD collateral.
    master = [hlx.Counterparty("EXCPTY00000000000001", "USD-SOFR")]
    book, _ = hlx.Book.from_fpml(documents, counterparties=master)
    needs = dict(book.trades()[0][1].market_data_needs())
    print(f"discounts on  {needs['discount']}")
    
    # 4. Read the refusals. A document that can't be read is one refusal row, and
    #    the others still book.
    book, refusals = hlx.Book.from_fpml([("broken.xml", "<dataDocument"), *documents])
    rows = refusals.to_dict()
    print(f"booked   {len(book.trades())}")
    for source, reason in zip(rows["source"], rows["reason"]):
        print(f"refused  {source}: {reason}")
    
    # 5. Or read strictly: the first refusal raises and nothing is booked.
    try:
        hlx.Book.from_fpml([("broken.xml", "<dataDocument"), *documents], strict=True)
        raised = False
    except ValueError:
        raised = True
    print(f"strict read raises  {'yes' if raised else 'no'}")
    as HXDEMO00000000000001  booked HX-IRS-0001  VanillaSwap  x1
    as EXCPTY00000000000001  booked HX-IRS-0002  VanillaSwap  x1
    discounts on  EUR-CSA-USD
    booked   1
    refused  broken.xml: not well-formed XML: the document does not have a root node
    strict read raises  yes

    Check it worked

    • Each trade is booked once, under the trade id your party gives it.
    • With the master, the EUR swap facing a counterparty that posts USD discounts on EUR-CSA-USD.
    • The malformed document is the only refusal, and the good one still books.
    • The strict read raises.

    Supported products

    FpML product Books as
    swap a single-currency or cross-currency swap, including stubs, step schedules, compounding, principal exchanges and FX-linked notionals
    swaption a European or Bermudan swaption; a cancelable swap books as a callable swap
    capFloor a cap, a floor, or a collar
    fra a forward rate agreement
    bulletPayment a single payment
    fxSingleLeg an FX spot, forward or non-deliverable forward
    fxSwap an FX swap
    fxOption a European FX option, non-deliverable options included
    strategy each of its FX components
    creditDefaultSwap a single-name or index CDS
    creditDefaultSwapOption an option on a CDS
    bondOption an option on a bond from your security master (Rust only, see below)
    repo a term repo (Rust only, see below)
    equityOption a European equity option
    returnSwap an equity total return swap

    Any other product, and any element that would change a trade's economics but isn't mapped, is refused by name.

    A bond option and a repo name their bond, so the reader looks it up in a security master: in Rust, pass one as ReadOptions::securities. Python's Book.from_fpml has no such argument yet, so in Python both come back as refusals.

    • Counterparties and CSA, the reference for the counterparty master.
    • How trades are built, for why the reader refuses rather than defaults.
    • Value a book and collect failures, for what to do with the book next.

    From the 0.0 line at revision bf7f2e2. Every printed number is that revision's own output.