Showing posts with label Configuration. Show all posts
Showing posts with label Configuration. Show all posts

Saturday, April 28, 2018

C# : Building Transactions from XML files

In my previous post, I presented the following Main program for creating just one set of transaction parameters for a zero-coupon bond, to be delivered further for QuantLib C++/CLI wrapper class.

using System;

namespace Client {

    static class ZeroCouponBond {
        static void Main() {
            try {

                // create transaction parameters
                double riskFreeRate = 0.01;
                double faceAmount = 1000000.0;
                DateTime transactionDate = new DateTime(2018, 4, 16);
                DateTime maturityDate = new DateTime(2020, 4, 16);
                string calendar = "TARGET";
                string daycounter = "ACT360";
                int settlementDays = 2;
                
                // use C++/CLI wrapper : create bond, request pv
                QuantLibCppWrapper.MJZeroCouponBondWrapper zero = new QuantLibCppWrapper.MJZeroCouponBondWrapper(
                    riskFreeRate, faceAmount, transactionDate, maturityDate, calendar, daycounter, settlementDays);
                double PV = zero.PV();
                Console.WriteLine(PV.ToString());
                GC.SuppressFinalize(zero);
            }
            catch (Exception e) {
                Console.WriteLine(e.Message);
            }
        }
    }
}

Needless to say, in real life we would need to have a bit more flexible system for creating transaction data. In this post, I am opening one possible scheme for creating transaction data for any type of transaction directly from XML files, using de-serialization. A notable issue in this scheme is, that client program does not implement any kind of pricing model, since the valuation part has completely been outsourced for C++/CLI QuantLib wrapper class (or to some other pricing library). Client program is only creating and hosting transaction-related data and sending calls for wrapper class, when requesting PV for a specific transaction.

UML


The following class diagram is roughly presenting the scheme for building transactions.


In order to create transaction instances, Client program (Main) is using TransactionsBuilder class, which ultimately returns a list of Transaction instances. Transaction is abstract base class for all possible transaction types. This class does not provide any methods, but is merely hosting (in this scheme) properties which are common for all transactions: transaction ID, transaction type information and transaction PV. All concrete transaction implementations, such as ZeroCouponBond class, will be inherited from this base class. Now, there is a lot more than meets the eye inside Builder namespace and we will definitely get into some specifics later.

Library


This namespace is consisting of class implementations for all possible transaction types. It should be implemented entirely into a new C# console project.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml;
using System.Xml.Serialization;

namespace Library {

    // abstract base class for all possible transaction types
    // storing only properties common for all transactions
    public abstract class Transaction {

        // no de-serialization for pv attribute
        private double pv;
        public double PV { get { return pv; } set { pv = value; } }

        public string transactionType;
        public string transactionID;

        // default ctor, required for serialization
        public Transaction() {
        }

        // parameter ctor
        public Transaction(string transactionType, string transactionID) {
            this.transactionType = transactionType;
            this.transactionID = transactionID;
        }

    }

    // class for hosting zero-coupon bond term sheet information
    public class ZeroCouponBond : Transaction {

        public double faceAmount;
        public DateTime transactionDate;
        public DateTime maturityDate;
        public string calendar;
        public string daycounter;
        public int settlementDays;

        public ZeroCouponBond()
            : base() {
            // required ctor for serialization
        }

        public ZeroCouponBond(string transactionType, string transactionID,
            double faceAmount, DateTime transactionDate, DateTime maturityDate,
            string calendar, string daycounter, int settlementDays)
            : base(transactionType, transactionID) {

            this.faceAmount = faceAmount;
            this.transactionDate = transactionDate;
            this.maturityDate = maturityDate;
            this.calendar = calendar;
            this.daycounter = daycounter;
            this.settlementDays = settlementDays;
        }

    }

    // class for hosting equity-linked note term sheet information
    public class EquityLinkedNote : Transaction {

        public double notional;
        public double cap;
        public double floor;
        public int settlementDays;
        public bool useAntitheticVariates;
        public int requiredSamples;
        public int maxSamples;
        public int seed;
        public List<DateTime> fixingDates = null;
        public List<DateTime> paymentDates = null;

        public EquityLinkedNote()
            : base() {
            // required ctor for serialization
        }

        public EquityLinkedNote(string transactionType, string transactionID,
            double notional, double cap, double floor, int settlementDays,
            bool useAntitheticVariates, int requiredSamples,
            int maxSamples, int seed, List<DateTime> fixingDates,
            List<DateTime> paymentDates)
            : base(transactionType, transactionID) {

            this.notional = notional;
            this.cap = cap;
            this.floor = floor;
            this.settlementDays = settlementDays;
            this.useAntitheticVariates = useAntitheticVariates;
            this.requiredSamples = requiredSamples;
            this.maxSamples = maxSamples;
            this.seed = seed;
            this.fixingDates = fixingDates;
            this.paymentDates = paymentDates;
        }
    }

}

After a short review, it should be clear that these implementation classes are nothing more, but wrappers for hosting heterogeneous sets of transaction-specific data members.

Builder


TransactionBuilder (plus a couple of other helper classes) is living in Builder namespace. This content should also be implemented into existing C# console project.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml;
using System.Xml.Serialization;

namespace Builder {

    public static class SerializerFactory {        
        // create generic de-serializer instance from a given transaction type string
        public static dynamic Create(string transactionType) {
            // note : required type string ("Namespace.Classname") is received without namespace string
            Type t = typeof(TransactionSerializer<>).MakeGenericType(Type.GetType(String.Format("Library.{0}", transactionType)));
            return Assembly.GetAssembly(t).CreateInstance(t.FullName);
        }
    }

    // de-serialize given xml file to transaction of type T
    public class TransactionSerializer<T> {
        public T Create(string transactionFilePathName) {
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            FileStream stream = File.OpenRead(transactionFilePathName);
            return (T)serializer.Deserialize(stream);
        }
    }

    public static class TransactionsBuilder {

        // return list of transaction instances, de-serialized from xml files 
        public static List<dynamic> Build() {

            // create configurations
            // NOTE : use environmental variable in path construction
            string configurationsFilePathName =
                Path.Combine(Environment.GetEnvironmentVariable("CONFIGURATIONS").ToString(), "configurations.xml");
            XmlDocument configurations = new XmlDocument();
            configurations.Load(configurationsFilePathName);
            string transactionsDirectory =
                configurations.SelectSingleNode("Configurations/TransactionsFilePathName").InnerText.ToString();

            // create transaction file names and empty list for storing transaction instances
            string[] transactionFiles = Directory.GetFiles(transactionsDirectory);
            List<dynamic> transactions = new List<dynamic>();

            // loop through transaction file names
            foreach(string transactionFile in transactionFiles) {

                // create transaction xml file
                XmlDocument transactionXMLDocument = new XmlDocument();
                transactionXMLDocument.Load(transactionFile);

                // investigate transaction type inside file
                string transactionType = (transactionXMLDocument.DocumentElement)["transactionType"].InnerText.ToString();

                // use factory class for creating correct de-serializer
                dynamic factory = SerializerFactory.Create(transactionType);

                // use de-serializer to create transaction instance
                dynamic transaction = factory.Create(transactionFile);
                transactions.Add(transaction);
            }
            return transactions;
        }

    }
}

Let us briefly go through, what is happening here. Ultimately, Build-method of TransactionsBuilder class is returning a list of Transaction instances (as dynamic) to its caller.

In the beginning of this method, all program-specific configurations are read from specific XML file. Based on created configurations, transaction XML files will then be loaded from specific directory, on a sequential manner. For each loaded transaction file, type string will be sniffed from inside file and the correct de-serializer instance (factory) will be created, based on that type string. The entity, which is creating this correct de-serializer instance, is SerializerFactory class. Finally, factory instance is used to de-serialize XML file to correct Transaction instance by using TransactionSerializer<T> class.

Required template parameter T for TransactionSerializer<T> class is constructed directly from a given transaction type string, by using MakeGenericType method. The actual de-serializer instance will be created from a given assembly by using CreateInstance method.

Client


Client namespace content is presented below. Also, this content should be implemented into existing C# console project.

namespace Client {
    using Library;
    using Builder;

    static class Program {
        static void Main(string[] args) {

            try {
                // 1. input
                // use transaction builder class for creating all transaction instances from xml files
                List<dynamic> transactions = TransactionsBuilder.Build();

                // 2. processing
                // process PV for all transactions
                // in real-life scenario, we could create calls in here for some pricing library 
                // and store processed valuation results to transaction PV attribute

                // select all zero-coupon bonds and modify PV attribute
                List<ZeroCouponBond> zeros =
                    transactions.Where(x => x.transactionType == "ZeroCouponBond")
                    .Select(y => (ZeroCouponBond)y).ToList<ZeroCouponBond>();
                zeros.ForEach(it => it.PV = 987654.32);

                // select all equity-linked notes and modify PV attribute
                List<EquityLinkedNote> notes =
                    transactions.Where(x => x.transactionType == "EquityLinkedNote")
                    .Select(y => (EquityLinkedNote)y).ToList<EquityLinkedNote>();
                notes.ForEach(it => it.PV = 32845.93);

                // 3. output
                // finally, print ID, type and PV for all transactions
                transactions.ForEach(it => Console.WriteLine
                    (String.Format("id:{0}, type:{1}, pv:{2}",
                    it.transactionID, it.transactionType, it.PV)));
            } 
            catch (Exception e) {
                Console.WriteLine(e.Message);
            }
        }
    }
}

The story in Main goes roughly as follows.
  1. Create all transactions instances from specific directory, by using XML de-serialization. 
  2. As a "service request", use created transaction instances for feeding transaction parameters for corresponding wrapper method (say, ZeroCouponBond instance is used for feeding MJZeroCouponBondWrapper). 
  3. Receive PV from wrapper method and store this result back to transaction PV attribute.
Transaction instances (constructed by TransactionsBuilder) can be investigated in Locals window as presented in the following screenshot.



















Finally, make sure to carefully implement all required configurations, which are listed below.

XML configurations


ZeroCouponBond

<ZeroCouponBond>
  <transactionType>ZeroCouponBond</transactionType>
  <transactionID>HSBC.1028</transactionID>
  <faceAmount>1000000</faceAmount>
  <transactionDate>2018-04-24T00:00:00</transactionDate>
  <maturityDate>2020-04-24T00:00:00</maturityDate>
  <calendar>TARGET</calendar>
  <daycounter>ACT360</daycounter>
  <settlementDays>2</settlementDays>
</ZeroCouponBond>

EquityLinkedNote

<EquityLinkedNote>
  <transactionType>EquityLinkedNote</transactionType>
  <transactionID>NORDEA.3866</transactionID>
  <notional>1000000.0</notional>
  <cap>0.015</cap>
  <floor>0.0</floor>
  <transactionDate>2017-10-30T00:00:00</transactionDate>
  <settlementDays>2</settlementDays>
  <calendar>TARGET</calendar>
  <dayCountConvention>ACT360</dayCountConvention>
  <requiredSamples>1000</requiredSamples>
  <seed>0</seed>
  <fixingDates>
    <dateTime>2017-11-30T00:00:00</dateTime>    <dateTime>2017-12-30T00:00:00</dateTime>
    <dateTime>2018-01-30T00:00:00</dateTime>    <dateTime>2018-02-28T00:00:00</dateTime>
    <dateTime>2018-03-30T00:00:00</dateTime>    <dateTime>2018-04-30T00:00:00</dateTime>
    <dateTime>2018-05-30T00:00:00</dateTime>    <dateTime>2018-06-30T00:00:00</dateTime>
    <dateTime>2018-07-30T00:00:00</dateTime>    <dateTime>2018-08-30T00:00:00</dateTime>
    <dateTime>2018-09-30T00:00:00</dateTime>    <dateTime>2018-10-30T00:00:00</dateTime>
    <dateTime>2018-11-30T00:00:00</dateTime>    <dateTime>2018-12-30T00:00:00</dateTime>
    <dateTime>2019-01-30T00:00:00</dateTime>    <dateTime>2019-02-28T00:00:00</dateTime>
    <dateTime>2019-03-30T00:00:00</dateTime>    <dateTime>2019-04-30T00:00:00</dateTime>
    <dateTime>2019-05-30T00:00:00</dateTime>    <dateTime>2019-06-30T00:00:00</dateTime>
    <dateTime>2019-07-30T00:00:00</dateTime>    <dateTime>2019-08-30T00:00:00</dateTime>
    <dateTime>2019-09-30T00:00:00</dateTime>    <dateTime>2019-10-30T00:00:00</dateTime>
    <dateTime>2019-11-30T00:00:00</dateTime>    <dateTime>2019-12-30T00:00:00</dateTime>
    <dateTime>2020-01-30T00:00:00</dateTime>    <dateTime>2020-02-29T00:00:00</dateTime>
    <dateTime>2020-03-30T00:00:00</dateTime>    <dateTime>2020-04-30T00:00:00</dateTime>
    <dateTime>2020-05-30T00:00:00</dateTime>    <dateTime>2020-06-30T00:00:00</dateTime>
    <dateTime>2020-07-30T00:00:00</dateTime>    <dateTime>2020-08-30T00:00:00</dateTime>
    <dateTime>2020-09-30T00:00:00</dateTime>    <dateTime>2020-10-30T00:00:00</dateTime>
  </fixingDates>
  <paymentDates>
    <dateTime>2018-10-30T00:00:00</dateTime>
    <dateTime>2019-10-30T00:00:00</dateTime>
    <dateTime>2020-10-30T00:00:00</dateTime>
  </paymentDates>
</EquityLinkedNote>

Program configurations

<Configurations>
  <TransactionsFilePathName>C:\temp\transactions</TransactionsFilePathName>
</Configurations>

For setting environmental variables, a great tutorial can be found in here. Finally, configuring a new transaction in this scheme should be relatively easy. First, a new XML file for hosting specific set of transaction-related parameters (plus two parameters common for all transactions: ID and type) should be created. Secondly, a new class implementation for hosting these transaction-related parameters should be implemented into Library namespace. Finally (outside of this assembly), there should be method implementation available in wrapper for this new transaction (for calculating PV for a given set of transaction parameters). Job done.

As always, Thanks for reading this blog. Happy First of May season for everybody.
-Mike

Wednesday, July 19, 2017

Batch scripting : managing configurational complexity


Processing calculations using third-party analytical software may turn out to be more like a process, requiring several independent tasks to be executed in a certain order, before any actual magic will happen. The one I have been using, was initially delivered with relatively large collection of black box executables. I decided to streamline those executables out from the picture by (first studying and then) using API. However, there were still a lot of tricky housekeeping stuff to be done before and after the actual number crunching (ex. required source data creation, result processing).

For all required housekeeping stuff, I created independent programs (executables), which were wrapped inside batch script files to be called, since a lot of auxiliary information was required as feed for executables (ex. address to configuration file). The process was working like a dream up to the point, in which some minor changes were made into existing network folder structure. I confess the following : almost all path addresses in batch script wrapper files were actually hard-coded. In order to compensate this horrible tragedy, I decided to use relative paths in script files. Solution was again technically working well, but script files were quickly being polluted by lines of incomprehensible streams. Solving possible issues would definitely be excrutiating experience for any non-experienced person. I also realized, that given some specific change to be made in the future, there would still be janitor work to be done with several script files. Bad omen.

As being non-expert on batch scripting, I finally decided to go to direction which is the most comfortable for me : constructing all required configurations from one centralized place. For this specific process, I created a batch script which is creating all possible configuration strings (mostly addresses to folders containing other configurations or executables). This script would then be called, before any other batch script content would be executed. Since the law of conservation of misery states "total amount of misery in a system is constant", one may well then ask what was the actual gain ? Firstly, lines of commands in wrapper script files were much more comprehensible (think about someone else on your desk when something happens) and secondly, given any future changes in network directory, changes would be made only in one place. At least for me, these issues are more than justifying the proposed scheme.


Batch script implementations


Script for creating "global" configurations











Script for using "global" configurations script











Script (MarketDataFeeder.bat) being called inside previous script






Test run (executing Datafeed.bat)











Finally, thanks for reading this blog.
-Mike

Saturday, October 8, 2016

C# : managing global configurations with XML serializer

Managing configurations within any non-trivial program can easily turn to be a tricky issue. One might have a lot of different types of configuration parameters, which might need to be used as input parameters for several different classes. Moreover, there might be a need for using different set of configurations and one should be able to switch to another set as easily as possible.

This time I wanted to present a scheme, what I have been using for hosting global configuration parameters in some of my applications. Presented scheme solves three following issues :
  1. Configuration set used by program executable can easily be changed to another set
  2. There is no need to touch App.Config file
  3. All configuration properties are global, static and safe (read-only)
First of all, a set of configurations will be processed (read) from XML file using XML serializer. Assume I have following two configuration sets as XML files, one for testing and another for production :



















When running program executable for the both configuration files (path to folder in which the actual configuration XML file exists, will be given as a parameter for executable), we get the following console printouts :










When the program starts, path to configuration folder is captured (command line argument). Calling any class property of static Configurations class will automatically trigger static class constructor. Static class constructor will then use XML serializer (ObjectXMLHandler<T> class) for creating Configuration object instance (nested class inside static Configurations class). Finally, properties of newly created Configuration object will be assigned to properties of static Configurations class.

For testing this scheme, just replicate XML configuration files, create a new console application (GlobalConfigurations) and copyPaste the following code into CS-file.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml.Serialization;

namespace GlobalConfigurations
{
    public class Program
    {
        public static string configurationFilePathName;
        //
        static void Main(string[] args)
        {
            configurationFilePathName = args[0];
            //
            // print configurations
            ConfigurationTester configurationTester = new ConfigurationTester();
            configurationTester.Print();
        }
    }
    //
    //
    //
    /// <summary>
    /// Class for testing global static read-only configurations 
    /// </summary>
    public class ConfigurationTester
    {
        public void Print()
        {
            // print configurations
            Console.WriteLine("{0}, {1}, {2}, {3}", 
                Configurations.ConfigurationString, 
                Configurations.ConfigurationBoolean, 
                Configurations.ConfigurationLong, 
                Configurations.ConfigurationDouble);
        }
    }
    //
    //
    //
    /// <summary>
    /// Static class for hosting global configurations 
    /// </summary>
    public static class Configurations
    {
        // static read-only properties
        public static readonly string ConfigurationString;
        public static readonly bool ConfigurationBoolean;
        public static readonly long ConfigurationLong;
        public static readonly double ConfigurationDouble;
        //
        /// <summary>
        /// Static constructor will be called immediately 
        /// when static class property will be called. 
        /// </summary>
        static Configurations()
        {
            // create instance of ObjectXMLHandler for Configuration type
            ObjectXMLHandler<Configuration> handler = new ObjectXMLHandler<Configuration>();
            //
            // create Configuration objects from XML files
            string filePathName = Program.configurationFilePathName;
            List<Configuration> configuration = handler.Deserialize(filePathName);
            //
            // assign properties from inner configuration class into static read-only properties
            ConfigurationString = configuration[0].configurationString;
            ConfigurationBoolean = configuration[0].configurationBoolean;
            ConfigurationLong = configuration[0].configurationLong;
            ConfigurationDouble = configuration[0].configurationDouble;
        }
        /// <summary>
        /// Nested class for inner configurations. 
        /// </summary>
        public class Configuration
        {
            public string configurationString;
            public bool configurationBoolean;
            public long configurationLong;
            public double configurationDouble;
            //
            /// <summary>
            /// Default constructor is needed to create configuration parameters 
            /// </summary>
            public Configuration()
            {
                // no implementation
            }
        }
    }
    //
    //
    //
    /// <summary>
    /// Generic template class for handling conversion 
    /// between object and XML presentation.
    /// </summary>
    public class ObjectXMLHandler<T>
    {
        private XmlSerializer serializer = null;
        private Stream stream = null;
        //
        /// <summary>
        /// Parameter constructor for creating an instance. 
        /// </summary>
        public ObjectXMLHandler()
        {
            serializer = new XmlSerializer(typeof(T));
        }
        /// <summary>
        /// Convert a list of objects of type T into XML files. 
        /// </summary>
        public void Serialize(List<T> objects, List<string> fileNames, string folderPathName)
        {
            if (objects.Count != fileNames.Count)
                throw new Exception("objects.Count != fileNames.Count");
            //
            int counter = 0;
            foreach (T t in objects)
            {
                stream = File.Create(Path.Combine(folderPathName, String.Concat(fileNames[counter])));
                serializer.Serialize(stream, t);
                stream.Close();
                counter++;
            }
        }
        /// <summary>
        /// Convert XML files in specific folder into a list of objects of type T.
        /// </summary>
        public List<T> Deserialize(string folderPathName)
        {
            List<T> objects = new List<T>();
            foreach (string t in Directory.GetFiles(folderPathName))
            {
                stream = File.OpenRead(t);
                objects.Add((T)serializer.Deserialize(stream));
                stream.Close();
            }
            return objects;
        }
    }
}

Thanks a lot for using your precious time and reading my blog.
-Mike

Sunday, April 10, 2016

C# : flexible design for processing market data

Third-party analytics softwares will usually require a full set of market data to be feeded into system, before performing any of those precious high intensity calculations. Market data (curves, surfaces, fixing time-series, etc.) has to be feeded into system following some specific file configurations. Moreover, source data might have to be collected from several different vendor sources. Needless to say, the process can easily turn into a semi-manageable mess involving Excel workbooks and a lot of manual processing, which is always a bad omen.

For this reason, I finally ended up creating one possible design solution for flexible processing of market data files. I have been going through some iterations starting with Abstract Factory, before landing with the current one using Delegates to pair data and algorithms. With the current solution, I start to feel quite comfortable already.


UML




















Each market data point (such as mid USD swap rate for 2 years) is presented as a RiskFactor object. All individual RiskFactor objects are hosted in a list inside generic MarketDataElements<T> object, which enables hosting any type of data. MarketDataElements<T> object itself is hosted by static BaseMarket class.

The actual algorithms needed for creating any type of vendor data are captured in static ProcessorLibrary class. During the processing task, MarketDataElements<T> object will be handled for a specific library method implementation, which will then request values from vendor source for all involved RiskFactor objects. In the example program, ProcessorLibrary has a method for processing RiskFactor objects using Bloomberg market data API. This specific method is then using DummyBBCOMMWrapper class for requesting values for a RiskFactor object.

For the purpose of pairing specific data (MarketDataElements<T>) and specific algorithm (ProcessorLibrary), BaseMarket class is hosting a list of ElementProcessor objects as well as a list of Delegates bound with specific methods found in ProcessorLibrary. For the processing task, each ElementProcessor object is feeded with delegate method for specific ProcessorLibrary implementation method and information on MarketDataElements<T> object.

Finally, (not shown in UML) program is also using static TextFileHandler class for handling text files and static Configurations class for hosting hardcoded configurations, initially read from App.config file.


Files

 

App.config










CSV for all RiskFactor object configurations
Fields (matching with RiskFactor object properties) :
  • Data vendor identification string, matching with the one given in configuration file
  • Identification code (ticker) for a market data element, found in the system for which the data will be created
  • Vendor ticker for a market data element (Bloomberg ISIN code)
  • Field name for a market data element (Bloomberg field PX_MID)
  • Divider (Bloomberg is presenting a rate as percentage 1.234, but target system may need to have an input as absolute value 0.01234)
  • Empty field for a value to be processed by specific processor implementation for specific data vendor. 
 

Result CSV

























The program


Create a new console project and CopyPaste the following program into corresponding CS files. When testing the program in a real production environment, just add reference to Bloomberg API DLL file and replace DummyBBCOMMWrapper class with this.

Adding a new data vendor processor XYZ involves the following four easy steps :
  1. Update providers in App.config file : <add key="RiskFactorDataProviders" value="BLOOMBERG,XYZ" />
  2. Create a new method implementation into ProcessorLibrary :  public static void ProcessXYZRiskFactors(dynamic marketDataElements) { // implement algorithm}
  3. Add selection for a new processor into BaseMarket class method createElementProcessors: if(dataProviderString.ToUpper() == "XYZ") elementProcessors.Add(new ElementProcessor(ProcessorLibrary.ProcessXYZRiskFactors, elements));
  4. Create new RiskFactor object configurations into source file for a new vendor XYZ
Finally, thanks for reading my blog.
-Mike


// MainProgram.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MarketDataProcess
{
    class MainProgram
    {
        static void Main(string[] args)
        {
            try
            {
                // process base market risk factors to file
                BaseMarket.Process();
                BaseMarket.PrintToFile();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
    }
}
//
//
//
//
// BaseMarket.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MarketDataProcess
{
    // class for administrating risk factors and processors for base market
    public static class BaseMarket
    {
        private static List<string> inputFileStreams = new List<string>();
        private static MarketDataElements<RiskFactor> riskFactors = new MarketDataElements<RiskFactor>();
        private static List<ElementProcessor> elementProcessors = new List<ElementProcessor>();
        private static int nRiskFactors = 0;
        //
        public static void Process()
        {
            // read all source data string streams from file into list
            TextFileHandler.Read(Configurations.BaseMarketSourceDataFilePathName, inputFileStreams);
            //
            // extract string streams and create risk factor objects
            foreach (string inputFileStream in inputFileStreams)
            {
                RiskFactor element = new RiskFactor();
                element.Create(inputFileStream);
                riskFactors.AddElement(element);
            }
            nRiskFactors = riskFactors.elements.Count;
            //
            // create and execute market data element processors
            // finally run technical check on created risk factors
            BaseMarket.createElementProcessors();
            elementProcessors.ForEach(processor => processor.Process());
            BaseMarket.Check();
        }
        public static void PrintToFile()
        {
            // write created base market risk factors into file
            StringBuilder stream = new StringBuilder();
            for (int i = 0; i < riskFactors.elements.Count; i++)
            {
                stream.AppendLine(String.Format("{0},{1}", riskFactors.elements[i].systemTicker, riskFactors.elements[i].value));
            }
            TextFileHandler.Write(Configurations.BaseMarketResultDataFilePathName, stream.ToString(), false);
        }
        private static void createElementProcessors()
        {
            // market data element processor types are defined in configuration file
            List<string> dataProviderStrings = Configurations.RiskFactorDataProviders.Split(',').ToList<string>();
            //
            foreach (string dataProviderString in dataProviderStrings)
            {
                if (dataProviderString == String.Empty) throw new Exception("No element processor defined");
                List<RiskFactor> elements = riskFactors.elements.Where(factor => factor.provider == dataProviderString).ToList<RiskFactor>();
                if(dataProviderString.ToUpper() == "BLOOMBERG") elementProcessors.Add(new ElementProcessor(ProcessorLibrary.ProcessBloombergRiskFactors, elements));
            }
        }
        public static MarketDataElements<RiskFactor> GetRiskFactors()
        {
            // create and return deep copy of all base market risk factors
            return riskFactors.Clone();
        }
        private static void Check()
        {
            int nValidRiskFactors = 0;
            //
            // loop through all created risk factors for base market
            for (int i = 0; i < riskFactors.elements.Count; i++)
            {
                // extract risk factor to be investigated for valid value
                RiskFactor factor = riskFactors.elements[i];
                //
                // valid value inside risk factor should be double-typed converted to string, ex. "0.05328"
                // check validity of this value with Double
                // TryParse-method returning TRUE if string value can be converted to double
                double value = 0;
                bool isValid = Double.TryParse(factor.value, out value);
                if (isValid) nValidRiskFactors++;
                //
                // if value is not convertable to double, get user input for this value
                if (!isValid)
                {
                    while (true)
                    {
                        Console.Write(String.Format("Provide input for {0} >", factor.systemTicker));
                        bool validUserInput = Double.TryParse(Console.ReadLine(), out value);
                        if (validUserInput)
                        {
                            factor.value = value.ToString();
                            nValidRiskFactors++;
                            break;
                        }
                        else
                        {
                            // client is forced to set (at least technically) valid value
                            Console.WriteLine("Invalid value, try again");
                        }
                    }
                }
            }
        }
    }
}
//
//
//
//
// Configurations.cs
using System;
using System.Configuration;

namespace MarketDataProcess
{
    // static class for hosting configurations
    public static class Configurations
    {
        // readonly data for public sharing
        public static readonly string RiskFactorDataProviders;
        public static readonly string BaseMarketSourceDataFilePathName;
        public static readonly string BaseMarketResultDataFilePathName;
        //
        // private constructor will be called just before any configuration is requested 
        static Configurations()
        {
            // configuration strings are assigned to static class data members for easy access
            RiskFactorDataProviders = ConfigurationManager.AppSettings["RiskFactorDataProviders"].ToString();
            BaseMarketSourceDataFilePathName = ConfigurationManager.AppSettings["BaseMarketSourceDataFilePathName"].ToString();
            BaseMarketResultDataFilePathName = ConfigurationManager.AppSettings["BaseMarketResultDataFilePathName"].ToString();
        }
    }
}
//
//
//
//
// MarketDataElement.cs
using System;
using System.Collections.Generic;
using System.Linq;

namespace MarketDataProcess
{
    // generic template for all types of market data elements (risk factors, fixings)
    public class MarketDataElements<T> where T : ICloneable, new()
    {
        public List<T> elements = new List<T>();
        public void AddElement(T element)
        {
            elements.Add(element);
        }
        public MarketDataElements<T> Clone()
        {
            // create a deep copy of market data elements object
            MarketDataElements<T> clone = new MarketDataElements<T>();
            //
            // copy content for all elements into a list
            List<T> elementList = new List<T>();
            foreach (T element in elements)
            {
                elementList.Add((T)element.Clone());
            }
            clone.elements.AddRange(elementList);
            return clone;
        }
    }
    // risk factor as market data element
    public class RiskFactor : ICloneable
    {
        public string provider;
        public string systemTicker;
        public string vendorTicker;
        public string vendorField;
        public string divider;
        public string value;
        //
        public RiskFactor() { }
        public void Create(string stream)
        {
            List<string> fields = stream.Split(',').ToList<string>();
            this.provider = fields[0];
            this.systemTicker = fields[1];
            this.vendorTicker = fields[2];
            this.vendorField = fields[3];
            this.divider = fields[4];
            this.value = fields[5];
        }
        public object Clone()
        {
            RiskFactor clone = new RiskFactor();
            clone.provider = this.provider;
            clone.systemTicker = this.systemTicker;
            clone.vendorTicker = this.vendorTicker;
            clone.vendorField = this.vendorField;
            clone.divider = this.divider;
            clone.value = this.value;
            return clone;
        }
    }
    // fixing as market data element
    public class Fixing : ICloneable
    {
        public string provider;
        public string systemTicker;
        public string vendorTicker;
        public string vendorField;
        public string frequency;
        public string nYearsBack;
        public string divider;
        public string value;
        public Dictionary<string, string> timeSeries = new Dictionary<string, string>();
        //
        public Fixing() { }
        public void Create(string stream)
        {
            List<string> fields = stream.Split(',').ToList<string>();
            this.provider = fields[0];
            this.systemTicker = fields[1];
            this.vendorTicker = fields[2];
            this.vendorField = fields[3];
            this.frequency = fields[4];
            this.nYearsBack = fields[5];
            this.divider = fields[6];
            this.value = fields[7];
        }
        public object Clone()
        {
            Fixing clone = new Fixing();
            clone.provider = this.provider;
            clone.systemTicker = this.systemTicker;
            clone.vendorTicker = this.vendorTicker;
            clone.vendorField = this.vendorField;
            clone.divider = this.divider;
            clone.value = this.value;
            //
            // create deep copy of timeseries dictionary
            Dictionary<string, string> timeSeriesClone = new Dictionary<string, string>();
            foreach (KeyValuePair<string, string> kvp in this.timeSeries)
            {
                timeSeriesClone.Add(kvp.Key, kvp.Value);
            }
            clone.timeSeries = timeSeriesClone;
            return clone;
        }
    }
}
//
//
//
//
// ElementProcessor.cs
using System;
using System.Collections.Generic;

namespace MarketDataProcess
{
    // algorithm for creating market data element objects
    public delegate void Processor(dynamic marketDataElements);
    //
    // class for hosting data and algorithm
    public class ElementProcessor
    {
        private Processor taskProcessor;
        dynamic marketDataElements;
        public ElementProcessor(Processor taskProcessor, dynamic marketDataElements)
        {
            this.taskProcessor = taskProcessor;
            this.marketDataElements = marketDataElements;
        }
        public void Process()
        {
            taskProcessor.Invoke(marketDataElements);
        }
    }
}
//
//
//
//
// ProcessorLibrary.cs
using System;
using System.Collections.Generic;
using System.Linq;

namespace MarketDataProcess
{
    public static class ProcessorLibrary
    {
        public static void ProcessBloombergRiskFactors(dynamic marketDataElements)
        {
            List<RiskFactor> riskFactors = (List<RiskFactor>)marketDataElements;
            BBCOMMWrapper.BBCOMMDataRequest request;
            dynamic[, ,] result = null;
            string SYSTEM_DOUBLE = "System.Double";
            int counter = 0;
            //
            // group data list into N lists grouped by distinct bloomberg field names
            var dataGroups = riskFactors.GroupBy(factor => factor.vendorField);
            //
            // process each group of distinct bloomberg field names
            for (int i = 0; i < dataGroups.Count(); i++)
            {
                // extract group, create data structures for securities and fields
                List<RiskFactor> dataGroup = dataGroups.ElementAt(i).ToList<RiskFactor>();
                List<string> securities = new List<string>();
                List<string> fields = new List<string>() { dataGroup[0].vendorField };
                //
                // import securities into data structure feeded to bloomberg api
                for (int j = 0; j < dataGroup.Count; j++)
                {
                    securities.Add(dataGroup[j].vendorTicker);
                }
                //
                // create and use request object to retrieve bloomberg data
                request = new BBCOMMWrapper.ReferenceDataRequest(securities, fields);
                result = request.ProcessData();
                //
                // write retrieved bloomberg data into risk factor group data
                for (int k = 0; k < result.GetLength(0); k++)
                {
                    string stringValue;
                    dynamic value = result[k, 0, 0];
                    //
                    if (value.GetType().ToString() == SYSTEM_DOUBLE)
                    {
                        // handle output value with divider only if retrieved value is double
                        // this means that data retrieval has been succesfull
                        double divider = Convert.ToDouble(dataGroup[k].divider);
                        stringValue = (value / divider).ToString();
                        dataGroup[k].value = stringValue;
                        counter++;
                    }
                    else
                    {
                        // write non-double value (bloomberg will retrieve #N/A) if retrieved value is not double
                        stringValue = value.ToString();
                        dataGroup[k].value = stringValue;
                    }
                }
            }
        }
    }
}
//
//
//
//
// DummyBBCOMMWrapper.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BBCOMMWrapper
{
    // abstract base class for data request
    public abstract class BBCOMMDataRequest
    {
        // input data structures
        protected List<string> securityNames = new List<string>();
        protected List<string> fieldNames = new List<string>();
        //
        // output result data structure
        protected dynamic[, ,] result;
        //
        public dynamic[, ,] ProcessData()
        {
            // instead of the actual Bloomberg market data, random numbers are going to be generated
            Random randomGenerator = new Random(Math.Abs(Guid.NewGuid().GetHashCode()));
            //
            for (int i = 0; i < securityNames.Count; i++)
            {
                for (int j = 0; j < fieldNames.Count; j++)
                {
                    result[i, 0, j] = randomGenerator.NextDouble();
                }
            }
            return result;
        }
    }
    //
    // concrete class implementation for processing reference data request
    public class ReferenceDataRequest : BBCOMMDataRequest
    {
        public ReferenceDataRequest(List<string> bloombergSecurityNames,
            List<string> bloombergFieldNames)
        {
            securityNames = bloombergSecurityNames;
            fieldNames = bloombergFieldNames;
            result = new dynamic[securityNames.Count, 1, fieldNames.Count];
        }
    }
}
//
//
//
//
// TextFileHandler.cs
using System;
using System.Collections.Generic;
using System.IO;

namespace MarketDataProcess
{
    public static class TextFileHandler
    {
        public static void 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();
        }
        public static void 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();
        }
        public static void Write(string filePathName, string input, bool append)
        {
            // write bulk text stream to file
            StreamWriter writer = new StreamWriter(filePathName, append);
            writer.Write(input);
            writer.Close();
        }
    }
}