/// <summary> /// Recursively processes an FpML 5-x document to produce a new document /// containin the stripped down product infosets. /// </summary> /// <param name="context">The <see cref="XmlNode"/> to be copied.</param> /// <param name="document">The new <see cref="XmlDocument"/> instance.</param> /// <param name="parent">The new parent <see cref="XmlNode"/>.</param> private void Transcribe(XmlNode context, XmlDocument document, XmlElement parent) { if (context != null) { switch (context.NodeType) { case XmlNodeType.Attribute: { XmlAttribute attribute = (XmlAttribute)context; // Remove unnecessary ID attributes if (XPath.Match(attribute, "businessCenters", "id") || XPath.Match(attribute, "swapStream", "id") || XPath.Match(attribute, "swapStream", "resetDates", "id") || XPath.Match(attribute, "europeanExercise", "id") || XPath.Match(attribute, "americanExercise", "id") || XPath.Match(attribute, "bermudaExercise", "id")) { break; } if (XPath.Match(attribute, "currencyScheme") && attribute.Value.Equals("http://www.fpml.org/ext/iso4217")) { break; } parent.SetAttribute(attribute.Name, attribute.Value); break; } case XmlNodeType.Element: { XmlElement element = context as XmlElement; // Strip an existing taxonomy data and identification if (XPath.Match(element, "productType") || XPath.Match(element, "assetClass") || XPath.Match(element, "productid")) { break; } // Remove party specific references if (XPath.Match(element, "buyerPartyReference") || XPath.Match(element, "buyerAccountReference") || XPath.Match(element, "sellerPartyReference") || XPath.Match(element, "sellerAccountReference") || XPath.Match(element, "payerPartyReference") || XPath.Match(element, "payerAccountReference") || XPath.Match(element, "receiverPartyReference") || XPath.Match(element, "receiverAccountReference") || XPath.Match(element, "partyReference") || XPath.Match(element, "calculationAgentPartyReference")) { break; } // Remove monetary amounts if (XPath.Match(element, "notional", "amount") || XPath.Match(element, "paymentAmount", "amount") || XPath.Match(element, "stubAmount", "amount")) { break; } // Remove calculation period references if (XPath.Match(element, "paymentDates", "calculationPeriodDatesReference") || XPath.Match(element, "resetDates", "calculationPeriodDatesReference") || XPath.Match(element, "stubCalculationPeriodAmount", "calculationPeriodDatesReference")) { break; } // Remove reset dates references if (XPath.Match(element, "fixingDates", "dateRelativeTo") || XPath.Match(element, "fixingDateOffset", "dateRelativeTo") || XPath.Match(element, "relativeDate", "dateRelativeTo")) { break; } // Remove dates if (XPath.Match(element, "paymentDate", "unadjustedDate") || XPath.Match(element, "adjustableDate", "unadjustedDate") || XPath.Match(element, "paymentDates", "firstPaymentDate") || XPath.Match(element, "fra", "adjustedEffectiveDate") || XPath.Match(element, "fra", "adjustedTerminationDate")) { break; } // Replace businessCentersReference with a copy of the target if (XPath.Match(element, "businessCentersReference")) { Transcribe(element.OwnerDocument.GetElementById(element.GetAttribute("href")), document, parent); break; } // Strip schedules if (XPath.Match(element, "notionalStepSchedule", "initialValue") || XPath.Match(element, "fixedRateSchedule", "initialValue") || XPath.Match(element, "spreadSchedule", "initialValue") || XPath.Match(element, "capRateSchedule", "initialValue") || XPath.Match(element, "floorRateSchedule", "initialValue")) { break; } // Keep an indication of step presence but remove values if (XPath.Match(element, "step")) { XmlElement prevElement = DOM.GetPreviousSibling(element); if ((prevElement != null) && XPath.Match(prevElement, "step")) { break; } } if (XPath.Match(element, "step", "stepDate") || XPath.Match(element, "step", "stepValue")) { break; } // Remove rates if (XPath.Match(element, "initialStub", "stubRate") || XPath.Match(element, "finalStub", "stubRate") || XPath.Match(element, "fra", "fixedRate")) { break; } // Remove other odd elements if (XPath.Match(element, "fra", "calculationPeriodNumberOfDays")) { break; } // Clone the original element XmlElement clone = document.CreateElement(element.LocalName, R1_0.NamespaceUri); parent.AppendChild(clone); // Keep an indication of cashflows but remove values if (XPath.Match(element, "swapStream", "cashflows")) { break; } // Normalise swaps if (XPath.Match(element, "swap")) { OrderSwapStreams(element, document, clone); Transcribe(element ["earlyTerminationProvision"], document, clone); Transcribe(element ["cancelableProvision"], document, clone); Transcribe(element ["extendibleProvision"], document, clone); break; } // Make swap streams relative if (XPath.Match(element, "swapStream", "calculationPeriodDates")) { XmlElement effectiveDate; XmlElement terminationDate; if ((effectiveDate = XPath.Path(element, "firstRegularPeriodDate", "unadjustedDate")) == null) { effectiveDate = XPath.Path(element, "effectiveDate", "unadjustedDate"); } if ((terminationDate = XPath.Path(element, "lastRegularPeriodDate", "unadjustedDate")) == null) { terminationDate = XPath.Path(element, "terminationDate", "unadjustedDate"); } // Replace absolute dates with tenors if ((effectiveDate != null) && (terminationDate != null)) { XmlElement tenor = document.CreateElement("relativeTerminationDate", R1_0.NamespaceUri); clone.AppendChild(tenor); DeriveTenor(Types.ToDate(effectiveDate), Types.ToDate(terminationDate), document, tenor); Transcribe(element ["dayType"], document, tenor); Transcribe(element ["calculationPeriodDatesAdjustments"], document, clone); Transcribe(element ["stubPeriodType"], document, clone); Transcribe(element ["calculationPeriodFrequency"], document, clone); break; } } // Make FRAs relative if (XPath.Match(element, "fra")) { XmlElement effectiveDate = XPath.Path(element, "adjustedEffectiveDate"); XmlElement terminationDate = XPath.Path(element, "adjustedTerminationDate"); if ((effectiveDate != null) && (terminationDate != null)) { XmlElement tenor = document.CreateElement("relativeTerminationDate", R1_0.NamespaceUri); clone.AppendChild(tenor); DeriveTenor(Types.ToDate(effectiveDate), Types.ToDate(terminationDate), document, tenor); } } // Derive Expiration date tenor if (XPath.Match(element, "europeanExercise", "expirationDate")) { XmlElement tradeDate = XPath.Path(FindTrade(element), "tradeHeader", "tradeDate"); XmlElement expiryDate = XPath.Path(element, "adjustableDate", "unadjustedDate"); if ((tradeDate != null) && (expiryDate != null)) { XmlElement tenor = document.CreateElement("relativeExpirationDate", R1_0.NamespaceUri); clone.AppendChild(tenor); DeriveTenor(Types.ToDate(tradeDate), Types.ToDate(expiryDate), document, tenor); XmlElement adjustments = XPath.Path(element, "adjustableDate", "dateAdjustments"); if (adjustments != null) { Transcribe(adjustments, document, clone); } break; } } // Transfer and update attributes foreach (XmlAttribute attr in element.Attributes) { Transcribe(attr, document, clone); } // Recursively copy the child node across foreach (XmlNode node in element.ChildNodes) { Transcribe(node, document, clone); } break; } case XmlNodeType.Text: { parent.AppendChild(document.CreateTextNode(((XmlText)context).Value)); break; } } } }