From time to time, I need to have reliable second opinion or benchmark valuation for a given book of swap transactions. Bloomberg Swap Manager (SWPM) is just a great tool for pricing many different kinds of swaps and other types of transactions. In this small project, we are constructing a program, which can be used to request valuation fields for several portfolios (one portfolio is a list of Swap Manager transactions ID's stored in source file). Program is using Bloomberg C# API for requesting valuation fields from Bloomberg.
When using Bloomberg Field Search (FLDS) for this transaction (SL5Q3637 Corp), I can request all the fields (static, calculated) what are available in any of the Swap Manager screens. Just as one example, we see that in the transaction screen above, dirty PV for this swap is 12 157 660 EUR (for historical valuation date 5.6.2015).
Field search is showing the value of 11 889 635 EUR (SW_MARKET_VAL). However, since the transaction valuation date is historical, we need to override settlement and curve date. Clicking SW_MARKET_VAL (dirty PV) field in the Field Search screen will show all the parameters, what are available for overriding for this particular field.
By overriding SETTLE_DT and SW_CURVE_DT, PV (SW_MARKET_VAL) is 12 157 660 EUR, which is exactly the same as in our initial transaction screen above. Note, that if you are valuing any transaction per historical date, the both fields (settlement data, curve date) have to have the same date value.
Now we come to the beef of this posting : since we can use Bloomberg Field Search for finding a value for any fields what are available in any of the Swap Manager screens, we can also request values for those fields by using Bloomberg API. The easiest way is just to use Excel and Bloomberg-addin function BDP (security = SL5Q3637 Corp, field = SW_MARKET_VAL). Another way is to request those fields programmatically, by using Bloomberg programming API. In this posting, we are using the latter approach.
The program is producing the following output for a given portfolio of swap transactions (using current configurations).
Application configurations file (App.Config in C# project) is only defining repository folder for all individual portfolio configurations. It should be noted, that we can have several different portfolio configurations at the same time in that repository folder.
Single portfolio configuration file (as shown in the picture below) defines the following information
By handling portfolio construction via individual configuration files, we are achieving a lot of flexibility into this program. For example, different portfolios can
In a nutshell, the program is first finding configurations repository folder based on the information given in Application configurations file (App.Config). After this, the program is constructing N portfolio configuration objects for all existing configuration files given in repository folder. Finally, program is using Bloomberg API for requesting data for each configuration and then printing out the results based on given configuration.
UML class diagram has been presented below.
GREEN
In the first stage, Configuration objects has to be created. In this example program, Client is sending creation request for static Configurations class. Static Configurations class is then using concrete XMLConfigurationBuilder (implementation of abstract ConfigurationBuilder) to create Configuration objects. Static Configuration class has one to many Configuration objects. Static Configurations class is available for all the other objects during the program execution.
THISTLE
In the second stage, PortfolioProcessor object has to be created. Client is sending creation request for one concrete FileProcessorBuilder (implementation of abstract PortfolioProcessorBuilder) to build one PortfolioProcessor object. On a general level, PortfolioProcessor is hosting several other objects: one to many Portfolio objects (aggregation), one BBCOMMWrapper object (composition) and one to many PortfolioVisitor objects (aggregation).
YELLOW
PortfolioProcessor has one BBCOMMWrapper object (composition) for requesting data from Bloomberg by using BBCOMM Server API. For any portfolio being processed, all data fields (and optional field overrides), which are going to be requested from Bloomberg API, have been defined inside static Configurations class.
TURQUOISE
There is a hierarchy of objects. PortfolioProcessor object has one to many Portfolio objects (aggregation). One Portfolio object has one to many Contract objects (aggregation). One Contract object has one to many Field objects (aggregation). Essentially, when PortfolioProcessor is executing its Process method, it will request results from BBCOMMWrapper object for all contracts and data fields, for all Portfolio objects which are hosted inside PortfolioProcessor object. After receiving results from BBCOMMWrapper object, it will then export all results into corresponding Field objects for each Contract and Portfolio.
WHEAT
When PortfolioProcessor object is being built, concrete FileProcessorBuilder is also using PortfolioVisitorFactory object for creating one or more concrete Visitor objects. References of those Visitor objects are stored inside PortfolioProcessor (aggregation). Essentially, PortfolioProcessor object method Print will print all results (values from stored in Field objects inside Contract objects for each Portfolio object) into selected target medium. For this task, PortfolioProcessor object is using one or more Visitor objects (specific Visitor type for each Portfolio).
One small disappointing issue is the manual administration tasks of hosting that list of transactions which are stored in Bloomberg Swap Manager. So far, I have not been able to find a way to request those transactions directly from Bloomberg. However, Bloomberg has recently been releasing its Swaps Toolkit for Excel, which (according to our Bloomberg account manager) has also programming interface. I might get some new ideas for handling swaps books, as soon as I am familiar enough with the product.
UML class diagram has been created with the online tool called YUML. It is completely free, easy to use, the most flexible and the coolest UML tool what I have been using so far, so check it out. And thanks again for spending your precious time here and reading my blog.
-Mike
SWAP MANAGER AND FIELD SEARCH
Let us assume, that the following cross-currency basis swap transaction has been created and stored in Bloomberg Swap Manager. Note, that when the transaction is stored, Swap Manager is automatically creating identification code for this transaction (SL5Q3637 - on the upper right corner of the screen).When using Bloomberg Field Search (FLDS) for this transaction (SL5Q3637 Corp), I can request all the fields (static, calculated) what are available in any of the Swap Manager screens. Just as one example, we see that in the transaction screen above, dirty PV for this swap is 12 157 660 EUR (for historical valuation date 5.6.2015).
Field search is showing the value of 11 889 635 EUR (SW_MARKET_VAL). However, since the transaction valuation date is historical, we need to override settlement and curve date. Clicking SW_MARKET_VAL (dirty PV) field in the Field Search screen will show all the parameters, what are available for overriding for this particular field.
By overriding SETTLE_DT and SW_CURVE_DT, PV (SW_MARKET_VAL) is 12 157 660 EUR, which is exactly the same as in our initial transaction screen above. Note, that if you are valuing any transaction per historical date, the both fields (settlement data, curve date) have to have the same date value.
Now we come to the beef of this posting : since we can use Bloomberg Field Search for finding a value for any fields what are available in any of the Swap Manager screens, we can also request values for those fields by using Bloomberg API. The easiest way is just to use Excel and Bloomberg-addin function BDP (security = SL5Q3637 Corp, field = SW_MARKET_VAL). Another way is to request those fields programmatically, by using Bloomberg programming API. In this posting, we are using the latter approach.
PROGRAM INPUT AND OUTPUT
A list of swap transactions (which have already been created and stored in Swap Manager) for a given portfolio (tradingSwaps in this example) is defined in the transaction source file (csv).The program is producing the following output for a given portfolio of swap transactions (using current configurations).
CONFIGURATION FILES
Application configurations file (App.Config in C# project) is only defining repository folder for all individual portfolio configurations. It should be noted, that we can have several different portfolio configurations at the same time in that repository folder.
Single portfolio configuration file (as shown in the picture below) defines the following information
- Portfolio name
- Source data path and file name
- Field separator for source file
- Bloomberg fields to be requested
- Possible Bloomberg override fields
- Possible Bloomberg override values
- Type of target output (file, console, excel, etc)
- Path and file name for output file (if not console)
By handling portfolio construction via individual configuration files, we are achieving a lot of flexibility into this program. For example, different portfolios can
- read source data (Bloomberg transaction ID's) from any given source data file.
- have completely different set of Bloomberg request fields and set of field overrides.
- be printed out into different target medium (console, csv, xml, web-page, Excel, etc).
In a nutshell, the program is first finding configurations repository folder based on the information given in Application configurations file (App.Config). After this, the program is constructing N portfolio configuration objects for all existing configuration files given in repository folder. Finally, program is using Bloomberg API for requesting data for each configuration and then printing out the results based on given configuration.
PROGRAM DESIGN
UML class diagram has been presented below.
GREEN
In the first stage, Configuration objects has to be created. In this example program, Client is sending creation request for static Configurations class. Static Configurations class is then using concrete XMLConfigurationBuilder (implementation of abstract ConfigurationBuilder) to create Configuration objects. Static Configuration class has one to many Configuration objects. Static Configurations class is available for all the other objects during the program execution.
THISTLE
In the second stage, PortfolioProcessor object has to be created. Client is sending creation request for one concrete FileProcessorBuilder (implementation of abstract PortfolioProcessorBuilder) to build one PortfolioProcessor object. On a general level, PortfolioProcessor is hosting several other objects: one to many Portfolio objects (aggregation), one BBCOMMWrapper object (composition) and one to many PortfolioVisitor objects (aggregation).
YELLOW
PortfolioProcessor has one BBCOMMWrapper object (composition) for requesting data from Bloomberg by using BBCOMM Server API. For any portfolio being processed, all data fields (and optional field overrides), which are going to be requested from Bloomberg API, have been defined inside static Configurations class.
TURQUOISE
There is a hierarchy of objects. PortfolioProcessor object has one to many Portfolio objects (aggregation). One Portfolio object has one to many Contract objects (aggregation). One Contract object has one to many Field objects (aggregation). Essentially, when PortfolioProcessor is executing its Process method, it will request results from BBCOMMWrapper object for all contracts and data fields, for all Portfolio objects which are hosted inside PortfolioProcessor object. After receiving results from BBCOMMWrapper object, it will then export all results into corresponding Field objects for each Contract and Portfolio.
WHEAT
When PortfolioProcessor object is being built, concrete FileProcessorBuilder is also using PortfolioVisitorFactory object for creating one or more concrete Visitor objects. References of those Visitor objects are stored inside PortfolioProcessor (aggregation). Essentially, PortfolioProcessor object method Print will print all results (values from stored in Field objects inside Contract objects for each Portfolio object) into selected target medium. For this task, PortfolioProcessor object is using one or more Visitor objects (specific Visitor type for each Portfolio).
THE PROGRAM
It should be noted, that each class (or class hierarchy) should be copy-pasted into a separate cs-file. References to Bloomberg API and System.Configuration should be made in order to compile this program.// file : Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Configuration;
using System.IO;
using System.Xml;
namespace PortfolioValuation
{
// CLIENT
classProgram
{
staticvoid Main(string[] args)
{
try
{
// build static configurations from pre-defined configuration files for each portfolio
Configurations.Build(new XMLConfigurationBuilder());
//
// build swap manager object from a given file information
PortfolioProcessorBuilder swapManagerBuilder = new FileProcessorBuilder();
PortfolioProcessor swapManager = swapManagerBuilder.Build();
//
// process and print portfolio
swapManager.Process();
swapManager.Print();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.WriteLine("Program has been executed");
Console.ReadLine();
}
}
}
//
//
// file : PortfolioProcessorBuilder.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Configuration;
namespace PortfolioValuation
{
// builder for swap manager object - class hierarchy
publicabstractclassPortfolioProcessorBuilder
{
publicabstract PortfolioProcessor Build();
}
// build data for swap manager object using source files
publicclassFileProcessorBuilder : PortfolioProcessorBuilder
{
publicoverride PortfolioProcessor Build()
{
// build portfolio and visitor objects based on configurations
List<Portfolio> portfolios = new List<Portfolio>();
List<PortfolioVisitor> visitors = new List<PortfolioVisitor>();
//
// loop through configurations
for (int i = 0; i < Configurations.N; i++)
{
// get one configuration, create portfolio object and
// read source data (isin codes) for this portfolio
Configuration table = Configurations.Get(i);
string ID = table.ID;
Portfolio portfolio = new Portfolio(ID);
List<string> sourceData = new List<string>();
TextFileHandler.Read(table.Get("sourcedatafilepathname"), sourceData);
//
// create empty contracts into portfolio
char fieldSeparator = Convert.ToChar(Configurations.Get(ID, "fieldseparator"));
sourceData.ForEach(it =>
{
if (it.Split(Convert.ToChar(fieldSeparator))[0] == ID)
portfolio.AddContract(new Contract(it.Split(fieldSeparator)[1]));
});
//
// decision : portfolio must have at least one contract
if (portfolio.Count() == 0) thrownew Exception("builder : empty portfolio error");
//
// request visitor for swap manager object, created by factory method
PortfolioOutputVisitorFactory factory = new PortfolioOutputVisitorFactory();
string visitorType = table.Get("visitortype");
PortfolioVisitor visitor = factory.CreatePortfolioOutputVisitor(visitorType);
portfolios.Add(portfolio);
visitors.Add(visitor);
}
// return constructed swap manager objects for a client
returnnew PortfolioProcessor(portfolios, visitors);
}
}
}
//
//
// file : PortfolioProcessor.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PortfolioValuation
{
publicclassPortfolioProcessor
{
private BBCOMMDataRequest bloombergAPI;
private List<PortfolioVisitor> visitors;
private List<Portfolio> portfolios;
private List<string> securities;
private List<string> bloombergFields;
private List<string> bloombergOverrideFields;
private List<string> bloombergOverrideValues;
//
public PortfolioProcessor(List<Portfolio> portfolios, List<PortfolioVisitor> visitors)
{
this.portfolios = portfolios;
this.visitors = visitors;
}
publicvoid Process()
{
// process all portfolios
foreach (Portfolio portfolio in portfolios)
{
// create information for Bloomberg API
string ID = portfolio.ID;
char fieldSeparator = Convert.ToChar(Configurations.Get(ID, "fieldseparator"));
securities = portfolio.Select(it => it.Isin).ToList();
//
bloombergFields = Configurations.Get(ID, "bloombergfields").Split(fieldSeparator).ToList<string>();
bloombergOverrideFields = Configurations.Get(ID, "bloombergoverridefields").Split(fieldSeparator).ToList<string>();
bloombergOverrideValues = Configurations.Get(ID, "bloombergoverridevalues").Split(fieldSeparator).ToList<string>();
//
// decision : override collections always has to be provided
if ((bloombergOverrideFields == null) || (bloombergOverrideValues == null))
thrownew Exception("swap manager : null override collection error");
//
// create reference data request for Bloomberg API
if ((bloombergOverrideFields.Count != 0) && (bloombergOverrideValues.Count != 0))
{
// data request with overrides
bloombergAPI = new ReferenceDataRequest(securities, bloombergFields, bloombergOverrideFields, bloombergOverrideValues);
}
else
{
// data request without overrides
bloombergAPI = new ReferenceDataRequest(securities, bloombergFields);
}
// receive requested data
dynamic[, ,] result = bloombergAPI.ProcessData();
//
// process bloomberg results into portfolio contracts and fields
int nFields = bloombergFields.Count;
int nSecurities = securities.Count;
for (int i = 0; i < nSecurities; i++)
{
for (int j = 0; j < nFields; j++)
{
Field field = new Field(bloombergFields[j], result[i, 0, j]);
portfolio.UpdateContract(securities[i], field);
}
}
}
}
publicvoid Print()
{
// delegate accept requests for portfolio objects
for (int i = 0; i < portfolios.Count; i++)
{
portfolios[i].AcceptVisitor(visitors[i]);
}
}
}
}
//
//
// file : TextFileHandler.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace PortfolioValuation
{
publicstaticclassTextFileHandler
{
publicstaticvoid Read(string filePathName, List<string> output)
{
// read file content into list
StreamReader reader = new StreamReader(filePathName);
while (!reader.EndOfStream)
{
output.Add(reader.ReadLine());
}
reader.Close();
}
publicstaticvoid Write(string filePathName, List<string> input, bool append)
{
// write text stream list to file
StreamWriter writer = new StreamWriter(filePathName, append);
input.ForEach(it => writer.WriteLine(it));
writer.Close();
}
}
}
//
//
// file : PortfolioVisitor.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Configuration;
namespace PortfolioValuation
{
// output visitor - class hierarchy
publicabstractclassPortfolioVisitor
{
publicabstractvoid Visit(Portfolio portfolio);
}
publicclassConsolePortfolioVisitor : PortfolioVisitor
{
publicoverridevoid Visit(Portfolio portfolio)
{
//Configuration configuration
foreach (Contract contract in portfolio)
{
string fieldSeparator = Configurations.Get(portfolio.ID, "fieldseparator");
StringBuilder sb = new StringBuilder();
foreach (Field f in contract)
{
sb.Append(f.Value);
sb.Append(fieldSeparator);
}
// print the content of string builder into console
Console.WriteLine(sb.ToString());
}
}
}
publicclassFilePortfolioVisitor : PortfolioVisitor
{
publicoverridevoid Visit(Portfolio portfolio)
{
// extract filePathName for a given portfolio from configuration table
string fieldSeparator = Configurations.Get(portfolio.ID, "fieldseparator");
List<string> resultDataFilePathNames = Configurations.Get(portfolio.ID, "resultdatafilepathname").Split(Convert.ToChar(fieldSeparator)).ToList();
List<string> portfolioIDs = Configurations.Get(portfolio.ID, "portfolioid").Split(Convert.ToChar(fieldSeparator)).ToList();
string resultDataFilePathName = null;
for (int i = 0; i < portfolioIDs.Count; i++)
{
if(portfolioIDs[i] == portfolio.ID)
{
resultDataFilePathName = resultDataFilePathNames[i];
break;
}
}
List<string> fileOutputList = new List<string>();
StringBuilder sb = new StringBuilder();
//
foreach (Contract contract in portfolio)
{
// add all fields into string builder object
foreach (Field f in contract)
{
sb.Append(f.Value);
sb.Append(fieldSeparator);
}
// add constructed string into output list and clear string builder
fileOutputList.Add(sb.ToString());
sb.Clear();
}
// print the content of output list into file
TextFileHandler.Write(resultDataFilePathName, fileOutputList, false);
}
}
}
//
//
// file : PortfolioConfiguration.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PortfolioValuation
{
publicclassPortfolioConfiguration
{
// private member data
publicreadonlystring ID;
private Dictionary<string, string> configurationTable = new Dictionary<string, string>();
//
public PortfolioConfiguration(string ID)
{
this.ID = ID;
}
// add key-value pair
publicvoid Add(string key, stringvalue)
{
// decision : configuration table cannot contain two identical keys
if (configurationTable.ContainsKey(key))
thrownew Exception("configuration table : duplicate key error");
configurationTable.Add(key, value);
}
// get value for a given key
publicstring Get(string key)
{
// decision : value for a given key cannot be returnde if it does not exist
if (!configurationTable.ContainsKey(key))
thrownew Exception("configuration table : invalid key error");
return configurationTable[key];
}
// clear all key-value pairs
publicvoid Clear()
{
// clear all configurations
configurationTable.Clear();
}
}
}
//
//
// file : Portfolio.cs
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PortfolioValuation
{
publicclassPortfolio : IEnumerable<Contract>
{
// private member data
publicreadonlystring ID;
private List<Contract> contracts;
//
// ctor
public Portfolio(string ID)
{
this.ID = ID;
contracts = new List<Contract>();
}
// add one contract into list of contracts
publicvoid AddContract(Contract contract)
{
// decision : portfolio cannot contain two identical contracts
if(contracts.Exists(it => it.Isin == contract.Isin))
thrownew Exception("portfolio : duplicate contract error");
contracts.Add(contract);
}
// update existing contract in list of contracts
publicvoid UpdateContract(string isin, Field field)
{
// contract cannot be updated if it does not exist
if (!contracts.Exists(it => it.Isin == isin))
thrownew Exception("portfolio : update contract error");
//
for (int i = 0; i < contracts.Count; i++)
{
if (contracts[i].Isin == isin)
{
contracts[i].AddField(field);
break;
}
}
}
// implementation for generic IEnumerable
public IEnumerator<Contract> GetEnumerator()
{
foreach (Contract contract in contracts)
{
yieldreturn contract;
}
}
// implementation for non-generic IEnumerable
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
// implementation method for visitor pattern
publicvoid AcceptVisitor(PortfolioVisitor visitor)
{
// send this-object (portfolio) for visitor implementation
visitor.Visit(this);
}
}
}
//
//
// file : PortfolioOutputVisitorFactory.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PortfolioValuation
{
publicclassPortfolioOutputVisitorFactory
{
// create output visitor
public PortfolioVisitor CreatePortfolioOutputVisitor(string visitorType)
{
// HARD-CODED selection of output visitors
// add new visitor type into switch branch and write
// implementation into visitor class hierarchy
PortfolioVisitor visitor = null;
switch (visitorType)
{
case"console":
visitor = new ConsolePortfolioVisitor();
break;
case"file":
visitor = new FilePortfolioVisitor();
break;
default:
thrownew Exception("factory : undefined visitor error");
}
return visitor;
}
}
}
//
//
// file : Field.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PortfolioValuation
{
publicclassField
{
// private data and read-only accessors
private KeyValuePair<string, dynamic> kvp;
publicstring Key { get { return kvp.Key; } }
publicdynamic Value { get { return kvp.Value; } }
//
// ctor
public Field(string key, dynamicvalue)
{
kvp = new KeyValuePair<string, dynamic>(key, value);
}
}
}
//
//
// file : Contract.cs
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PortfolioValuation
{
publicclassContract : IEnumerable<Field>
{
// private data and read-only accessor
privatestring isin;
private List<Field> fields;
publicstring Isin { get { return isin; } }
//
// ctor
public Contract(string isin)
{
this.isin = isin;
fields = new List<Field>();
}
// add one field into list of fields
publicvoid AddField(Field field)
{
// decision : contract cannot contain two identical fields
if (fields.Exists(it => it.Key == field.Key))
thrownew Exception("contract : duplicate field error");
fields.Add(field);
}
// implementation for generic IEnumerable
public IEnumerator<Field> GetEnumerator()
{
foreach (Field field in fields)
{
yieldreturn field;
}
}
// implementation for non-generic IEnumerable
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
//
//
// file : Configurations.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PortfolioValuation
{
// static wrapper class for all configurations
publicstaticclassConfigurations
{
// static members and accessors
privatestaticint n;
publicstaticint N { get { return Configurations.n; } }
privatestatic List<Configuration> tables = null;
//
// ctor
publicstaticvoid Build(ConfigurationBuilder builder)
{
tables = builder.Build();
n = tables.Count;
}
publicstaticstring Get(string ID, string key)
{
stringvalue = null;
foreach (Configuration table in tables)
{
if (table.ID == ID)
{
value = table.Get(key);
break;
}
}
returnvalue;
}
publicstatic Configuration Get(int index)
{
return tables[index];
}
}
}
//
//
// file : ConfigurationBuilder.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using System.IO;
using System.Configuration;
namespace PortfolioValuation
{
// class hierarchy for configuration builder
publicabstractclassConfigurationBuilder
{
publicabstract List<Configuration> Build();
}
publicclassXMLConfigurationBuilder : ConfigurationBuilder
{
privatestring folderPath;
private List<Configuration> configurations;
public XMLConfigurationBuilder()
{
// read path to source folder containing configurations for all portfolios directly from system configuration file
this.folderPath = ConfigurationManager.AppSettings["PortfolioConfigurationsFolder"].ToString();
configurations = new List<Configuration>();
}
publicoverride List<Configuration> Build()
{
foreach (string filePathName in Directory.GetFiles(folderPath))
{
XDocument database = XDocument.Load(filePathName);
Dictionary<string, string> pairs = database.Descendants("configuration")
.Elements().ToDictionary(r => r.Attribute("key").Value.ToString(),
r => r.Attribute("value").Value.ToString());
configurations.Add(new Configuration(pairs["portfolioid"], pairs));
}
return configurations;
}
}
}
//
//
// file : Configuration.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PortfolioValuation
{
publicclassConfiguration
{
publicreadonlystring ID;
private Dictionary<string, string> pairs;
//
// ctor
public Configuration(string ID, Dictionary<string, string> pairs)
{
this.ID = ID;
this.pairs = pairs;
}
// accessor method
publicstring Get(string key)
{
// decision : value for a given key cannot be returned if it does not exist
if (!pairs.ContainsKey(key))
thrownew Exception("configuration : invalid key error");
returnthis.pairs[key];
}
}
}
//
//
// file : BBCOMMWrapper.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BBCOMM = Bloomberglp.Blpapi;
namespace PortfolioValuation
{
// enumerators for historical data request settings
publicenum E_PRICING_OPTION { PRICING_OPTION_PRICE, PRICING_OPTION_YIELD };
publicenum E_PERIODICITY_ADJUSTMENT { ACTUAL, CALENDAR, FISCAL };
publicenum E_PERIODICITY_SELECTION { DAILY, WEEKLY, MONTHLY, QUARTERLY, SEMI_ANNUALLY, YEARLY };
publicenum E_NON_TRADING_DAY_FILL_OPTION { NON_TRADING_WEEKDAYS, ALL_CALENDAR_DAYS, ACTIVE_DAYS_ONLY };
publicenum E_NON_TRADING_DAY_FILL_METHOD { PREVIOUS_VALUE, NIL_VALUE };
//
// abstract base class for data request
publicabstractclassBBCOMMDataRequest
{
// BBCOMM names
protectedreadonly BBCOMM.Name SECURITY_DATA = new BBCOMM.Name("securityData");
protectedreadonly BBCOMM.Name FIELD_DATA = new BBCOMM.Name("fieldData");
protectedreadonly BBCOMM.Name FIELD_ID = new BBCOMM.Name("fieldId");
protectedreadonly BBCOMM.Name VALUE = new BBCOMM.Name("value");
protectedreadonly BBCOMM.Name OVERRIDES = new BBCOMM.Name("overrides");
protectedreadonly BBCOMM.Name SECURITIES = new BBCOMM.Name("securities");
protectedreadonly BBCOMM.Name FIELDS = new BBCOMM.Name("fields");
protectedreadonly BBCOMM.Name SEQUENCE_NUMBER = new BBCOMM.Name("sequenceNumber");
protectedreadonly BBCOMM.Name START_DATE = new BBCOMM.Name("startDate");
protectedreadonly BBCOMM.Name END_DATE = new BBCOMM.Name("endDate");
protectedreadonly BBCOMM.Name DATE = new BBCOMM.Name("date");
protectedreadonly BBCOMM.Name PRICING_OPTION = new BBCOMM.Name("pricingOption");
protectedreadonly BBCOMM.Name PERIODICITY_ADJUSTMENT = new BBCOMM.Name("periodicityAdjustment");
protectedreadonly BBCOMM.Name PERIODICITY_SELECTION = new BBCOMM.Name("periodicitySelection");
protectedreadonly BBCOMM.Name NON_TRADING_DAY_FILL_OPTION = new BBCOMM.Name("nonTradingDayFillOption");
protectedreadonly BBCOMM.Name NON_TRADING_DAY_FILL_METHOD = new BBCOMM.Name("nonTradingDayFillMethod");
protectedreadonly BBCOMM.Name OVERRIDE_CURRENCY = new BBCOMM.Name("currency");
//
// const strings, enumerators, etc.
protectedreadonlystring NOT_AVAILABLE = "#N/A";
protectedreadonlystring SESSION_EXCEPTION = "Session not started";
protectedreadonlystring SERVICE_EXCEPTION = "Service not opened";
protectedreadonlystring REQUEST_TYPE_REFERENCE = "ReferenceDataRequest";
protectedreadonlystring REQUEST_TYPE_HISTORICAL = "HistoricalDataRequest";
protectedreadonlystring REFERENCE_DATA_SERVICE = "//blp/refdata";
protectedreadonlystring BLOOMBERG_DATE_FORMAT = "yyyyMMdd";
protected E_PRICING_OPTION pricingOption;
protected E_PERIODICITY_ADJUSTMENT periodicityAdjustment;
protected E_PERIODICITY_SELECTION periodicitySelection;
protected E_NON_TRADING_DAY_FILL_OPTION nonTradingDayFillOption;
protected E_NON_TRADING_DAY_FILL_METHOD nonTradingDayFillMethod;
protectedstring requestType;
protectedstring startDate;
protectedstring endDate;
protectedstring overrideCurrency;
//
// wrapped BBCOMM objects
protected BBCOMM.Session session;
protected BBCOMM.Service service;
protected BBCOMM.Request request;
//
// input data structures
protected List<string> securityNames = new List<string>();
protected List<string> fieldNames = new List<string>();
protected List<string> overrideFields = new List<string>();
protected List<string> overrideValues = new List<string>();
//
// output result data structure
protecteddynamic[, ,] result;
//
publicdynamic[, ,] ProcessData()
{
Open();
CreateRequest();
SendRequest();
Close();
return result;
}
privatevoid Open()
{
// create and start bloomberg BBCOMM session
BBCOMM.SessionOptions sessionOptions = new BBCOMM.SessionOptions();
session = new BBCOMM.Session(sessionOptions);
if (!session.Start()) thrownew Exception(SESSION_EXCEPTION);
//
// get service from session object and create request by service object
if (!session.OpenService(REFERENCE_DATA_SERVICE)) thrownew Exception(SERVICE_EXCEPTION);
service = session.GetService(REFERENCE_DATA_SERVICE);
request = service.CreateRequest(requestType);
}
privatevoid CreateRequest()
{
// append securities, fields
foreach (string securityName in securityNames) request.Append(SECURITIES, securityName);
foreach (string fieldName in fieldNames) request.Append(FIELDS, fieldName);
//
// conditionally, append overrides into request object
if (overrideFields.Count > 0)
{
BBCOMM.Element requestOverrides = request.GetElement(OVERRIDES);
for (int i = 0; i < overrideFields.Count; i++)
{
BBCOMM.Element requestOverride = requestOverrides.AppendElement();
requestOverride.SetElement(FIELD_ID, overrideFields[i]);
requestOverride.SetElement(VALUE, overrideValues[i]);
}
}
// set optional parameters for historical data request
if (requestType == REQUEST_TYPE_HISTORICAL)
{
request.Set(START_DATE, startDate);
request.Set(END_DATE, endDate);
request.Set(PRICING_OPTION, pricingOption.ToString());
request.Set(PERIODICITY_ADJUSTMENT, periodicityAdjustment.ToString());
request.Set(PERIODICITY_SELECTION, periodicitySelection.ToString());
request.Set(NON_TRADING_DAY_FILL_OPTION, nonTradingDayFillOption.ToString());
request.Set(NON_TRADING_DAY_FILL_METHOD, nonTradingDayFillMethod.ToString());
if (overrideCurrency != String.Empty) request.Set(OVERRIDE_CURRENCY, overrideCurrency);
}
}
privatevoid SendRequest()
{
// send constructed request to BBCOMM server
long ID = Guid.NewGuid().GetHashCode();
session.SendRequest(request, new BBCOMM.CorrelationID(ID));
bool isProcessing = true;
//
while (isProcessing)
{
// receive data response from BBCOMM server, send
// response to be processed by sub-classed algorithm
BBCOMM.Event response = session.NextEvent();
switch (response.Type)
{
case BBCOMM.Event.EventType.PARTIAL_RESPONSE:
ProcessDataResponse(ref response);
break;
case BBCOMM.Event.EventType.RESPONSE:
ProcessDataResponse(ref response);
isProcessing = false;
break;
default:
break;
}
}
}
privatevoid Close()
{
// close BBCOMM session
if (session != null) session.Stop();
}
//
// sub-classes are providing specific algorithm implementations for
// processing and packing BBCOMM server response data into resulting data structure
protectedabstractvoid ProcessDataResponse(ref BBCOMM.Event response);
}
//
// concrete class implementation for processing reference data request
publicclassReferenceDataRequest : BBCOMMDataRequest
{
public ReferenceDataRequest(List<string> bloombergSecurityNames,
List<string> bloombergFieldNames)
{
// ctor : create reference data request without field overrides
requestType = REQUEST_TYPE_REFERENCE;
securityNames = bloombergSecurityNames;
fieldNames = bloombergFieldNames;
//
// define result data structure dimensions for reference data request
result = newdynamic[securityNames.Count, 1, fieldNames.Count];
}
public ReferenceDataRequest(List<string> bloombergSecurityNames,
List<string> bloombergFieldNames, List<string> bloombergOverrideFields,
List<string> bloombergOverrideValues)
{
// ctor : create reference data request with field overrides
requestType = REQUEST_TYPE_REFERENCE;
securityNames = bloombergSecurityNames;
fieldNames = bloombergFieldNames;
overrideFields = bloombergOverrideFields;
overrideValues = bloombergOverrideValues;
//
// define result data structure dimensions for reference data request
result = newdynamic[securityNames.Count, 1, fieldNames.Count];
}
protectedoverridevoid ProcessDataResponse(ref BBCOMM.Event response)
{
// receive response, which contains N securities and M fields
// event queue can send multiple responses for large requests
foreach (BBCOMM.Message message in response.GetMessages())
{
// extract N securities
BBCOMM.Element securities = message.GetElement(SECURITY_DATA);
int nSecurities = securities.NumValues;
//
// loop through all securities
for (int i = 0; i < nSecurities; i++)
{
// extract one security and fields for this security
BBCOMM.Element security = securities.GetValueAsElement(i);
BBCOMM.Element fields = security.GetElement(FIELD_DATA);
int sequenceNumber = security.GetElementAsInt32(SEQUENCE_NUMBER);
int nFieldNames = fieldNames.Count;
//
// loop through all M fields for this security
for (int j = 0; j < nFieldNames; j++)
{
// if the requested field has been found, pack value into result data structure
if (fields.HasElement(fieldNames[j]))
{
result[sequenceNumber, 0, j] = fields.GetElement(fieldNames[j]).GetValue();
}
// otherwise, pack NOT_AVAILABLE string into data structure
else
{
result[sequenceNumber, 0, j] = NOT_AVAILABLE;
}
}
}
}
}
}
//
// concrete class implementation for processing historical data request
publicclassHistoricalDataRequest : BBCOMMDataRequest
{
privatebool hasDimensions = false;
//
// optional parameters are configured to retrieve time-series having actual daily observations, including all weekdays,
// in the case of non-trading days the previous date value will be used.
public HistoricalDataRequest(List<string> bloombergSecurityNames, List<string> bloombergFieldNames,
DateTime bloombergStartDate, DateTime BloombergEndDate,
E_PRICING_OPTION bloombergPricingOption = E_PRICING_OPTION.PRICING_OPTION_PRICE,
E_PERIODICITY_SELECTION bloombergPeriodicitySelection = E_PERIODICITY_SELECTION.DAILY,
E_PERIODICITY_ADJUSTMENT bloombergPeriodicityAdjustment = E_PERIODICITY_ADJUSTMENT.ACTUAL,
E_NON_TRADING_DAY_FILL_OPTION bloombergNonTradingDayFillOption = E_NON_TRADING_DAY_FILL_OPTION.ALL_CALENDAR_DAYS,
E_NON_TRADING_DAY_FILL_METHOD bloombergNonTradingDayFillMethod = E_NON_TRADING_DAY_FILL_METHOD.PREVIOUS_VALUE,
string bloombergOverrideCurrency = "")
{
// ctor : create historical data request without field overrides
requestType = REQUEST_TYPE_HISTORICAL;
securityNames = bloombergSecurityNames;
fieldNames = bloombergFieldNames;
startDate = bloombergStartDate.ToString(BLOOMBERG_DATE_FORMAT);
endDate = BloombergEndDate.ToString(BLOOMBERG_DATE_FORMAT);
//
pricingOption = bloombergPricingOption;
periodicitySelection = bloombergPeriodicitySelection;
periodicityAdjustment = bloombergPeriodicityAdjustment;
nonTradingDayFillOption = bloombergNonTradingDayFillOption;
nonTradingDayFillMethod = bloombergNonTradingDayFillMethod;
overrideCurrency = bloombergOverrideCurrency;
}
public HistoricalDataRequest(List<string> bloombergSecurityNames, List<string> bloombergFieldNames,
DateTime bloombergStartDate, DateTime BloombergEndDate, List<string> bloombergOverrideFields,
List<string> bloombergOverrideValues,
E_PRICING_OPTION bloombergPricingOption = E_PRICING_OPTION.PRICING_OPTION_PRICE,
E_PERIODICITY_SELECTION bloombergPeriodicitySelection = E_PERIODICITY_SELECTION.DAILY,
E_PERIODICITY_ADJUSTMENT bloombergPeriodicityAdjustment = E_PERIODICITY_ADJUSTMENT.ACTUAL,
E_NON_TRADING_DAY_FILL_OPTION bloombergNonTradingDayFillOption = E_NON_TRADING_DAY_FILL_OPTION.ALL_CALENDAR_DAYS,
E_NON_TRADING_DAY_FILL_METHOD bloombergNonTradingDayFillMethod = E_NON_TRADING_DAY_FILL_METHOD.PREVIOUS_VALUE,
string bloombergOverrideCurrency = "")
{
// ctor : create historical data request with field overrides
requestType = REQUEST_TYPE_HISTORICAL;
securityNames = bloombergSecurityNames;
fieldNames = bloombergFieldNames;
overrideFields = bloombergOverrideFields;
overrideValues = bloombergOverrideValues;
startDate = bloombergStartDate.ToString(BLOOMBERG_DATE_FORMAT);
endDate = BloombergEndDate.ToString(BLOOMBERG_DATE_FORMAT);
//
pricingOption = bloombergPricingOption;
periodicitySelection = bloombergPeriodicitySelection;
periodicityAdjustment = bloombergPeriodicityAdjustment;
nonTradingDayFillOption = bloombergNonTradingDayFillOption;
nonTradingDayFillMethod = bloombergNonTradingDayFillMethod;
overrideCurrency = bloombergOverrideCurrency;
}
protectedoverridevoid ProcessDataResponse(ref BBCOMM.Event response)
{
// unzip and pack messages received from BBCOMM server
// receive one security per message and multiple messages per event
foreach (BBCOMM.Message message in response.GetMessages())
{
// extract security and fields
BBCOMM.Element security = message.GetElement(SECURITY_DATA);
BBCOMM.Element fields = security.GetElement(FIELD_DATA);
//
int sequenceNumber = security.GetElementAsInt32(SEQUENCE_NUMBER);
int nFieldNames = fieldNames.Count;
int nObservationDates = fields.NumValues;
//
// the exact dimension will be known only, when the response has been received from BBCOMM server
if (!hasDimensions)
{
// define result data structure dimensions for historical data request
// observation date will be stored into first field for each observation date
result = newdynamic[securityNames.Count, nObservationDates, fieldNames.Count + 1];
hasDimensions = true;
}
//
// loop through all observation dates
for (int i = 0; i < nObservationDates; i++)
{
// extract all field data for a single observation date
BBCOMM.Element observationDateFields = fields.GetValueAsElement(i);
//
// pack observation date into data structure
result[sequenceNumber, i, 0] = observationDateFields.GetElementAsDatetime(DATE);
//
// then, loop through all 'user-requested' fields for a given observation date
// and pack results data into data structure
for (int j = 0; j < nFieldNames; j++)
{
// pack field value into data structure if such value has been found
if (observationDateFields.HasElement(fieldNames[j]))
{
result[sequenceNumber, i, j + 1] = observationDateFields.GetElement(fieldNames[j]).GetValue();
}
// otherwise, pack NOT_AVAILABLE string into data structure
else
{
result[sequenceNumber, i, j + 1] = NOT_AVAILABLE;
}
}
}
}
}
}
}
AFTERTHOUGHTS
Stripped from all of its superficial complexities, this example program is actually nothing more, but very standard INPUT DATA - PROCESS DATA - OUTPUT RESULTS scheme. However, by implementing some useful Design Patterns (Builder, Factory method, Visitor), we are now having a lot of flexibility and configurability in this program. Getting results file with all requested fields from Bloomberg, for any new portfolio (for a list of Bloomberg Swap Manager transactions stored in a file) is just a matter of creating a new configuration file into existing repository folder.One small disappointing issue is the manual administration tasks of hosting that list of transactions which are stored in Bloomberg Swap Manager. So far, I have not been able to find a way to request those transactions directly from Bloomberg. However, Bloomberg has recently been releasing its Swaps Toolkit for Excel, which (according to our Bloomberg account manager) has also programming interface. I might get some new ideas for handling swaps books, as soon as I am familiar enough with the product.
UML class diagram has been created with the online tool called YUML. It is completely free, easy to use, the most flexible and the coolest UML tool what I have been using so far, so check it out. And thanks again for spending your precious time here and reading my blog.
-Mike



