At the time of writing Adfin Analytics package is only available as a part of Eikon Excel add-in and as a COM library. There’s no native Python implementation of Adfin Analytics. It is however quite possible to use the COM library also known as AdfinX Analytics in Python. This article explains how to use AdfinX Analytics in Python and provides a code sample. Since AdfinX Analytics package is only available in 32-bit, it can only be used in 32-bit Python. There’s no way to use AdfinX Analytics in 64-bit Python without creating an out of proc COM server, which is very involved and in my opinion is not worth the effort.



This article assumes the reader is proficient in Python, has some experience with Adfin Analytics (even if only through Excel) and has working understanding of Eikon COM APIs for use in custom applications. For introduction to the latter topic please see the following section on this portal:

https://developers.refinitiv.com/eikon-com/eikon-desktop-data-api

 

Installation

First pywin32 Python extension needs to be installed. Among other things this extension provides capability to instantiate COM objects, and to call their methods from Python. Pywin32 can either be installed with pip (```pip install pywin32```) or can be manually downloaded from SourceForge or from GitHub.

Dynamic vs. static COM proxy

Win32com.client package installed as a part of pywin32 extension offers two types of COM proxies: dynamic, which provides the equivalent of late binding of interfaces, and static, which provides the equivalent of early binding. Dynamic proxy generates a class on the fly, which represents the COM object you're interested in (late-binding). Static proxy makes use of a generated Python module, which contains class definitions (early-binding). With dynamic proxy you can attach to any COM object, and the Python class will simply proxy requests and responses to and fro. The advantage of the static proxy is that it creates a certain amount of documentation, which makes it possible to explore the interfaces available from the COM library using for example Python inspect module.

When working with AdfinX Analytics in Python we have to use dynamic proxy. This is because AdfinX Analytics is a reg free COM library and because it has a dependency on the host application to have an established connection with Eikon. For these reasons COM objects from AdfinX Analytics library cannot be instantiated using win32com.client.Dispatch method. They need to be instantiated using methods provided by EikonDesktopDataAPI COM library.

 

Creating a class for enums for better code readability

When working with EikonDesktopDataAPI COM object I need to use enums defined in EikonDesktopDataAPI COM library. Rather than using numeric enum values, which make the code hard to read, I’m going to create a class where members are constants corresponding to enums I need to use from the COM library. The class with identical members is created by makepy utility in the module the utility generates for EikonDesktopDataAPI type library when win32com.client.DispatchWithEvents method is called. Therefore instead of creating my own class I could have used the constants provided by win32com.client.constants object. However I find that by creating my own class I make the code easier to read: the name ConnectionToEikonEnum.Connected is much more descriptive in reference to an enum value in the library used to establish the connection to Eikon than win32com.client.constants.Connected.

class ConnectionToEikonEnum(Enum):
        Error_InitializeFail          =2          # from enum EEikonDataAPIInitializeResult
        Error_Reinitialize            =1          # from enum EEikonDataAPIInitializeResult
        Succeed                       =0          # from enum EEikonDataAPIInitializeResult
        Connected                     =1          # from enum EEikonStatus
        Disconnected                  =0          # from enum EEikonStatus
        Disconnected_No_license_agreement_sign_off=8          # from enum EEikonStatus
        LocalMode                     =2          # from enum EEikonStatus
        Offline                       =4          # from enum EEikonStatus

 

Initialization

AdfinX Analytics can only be used while the application is connected to Eikon. Therefore the first step in utilizing AdfinX Analytics is establishing the connection to Eikon. This is done through EikonDesktopDataAPI COM object. The object is a singleton. You can only have one instance of it in your application. Here I create the object and assign event handler class to handle its events. I also define the function to handle the OnStatusChanged event, which is raised whenever the connection status between my Python application and Eikon changes. Then I call Initialize method of the object, which initiates the connection to Eikon. The connection to Eikon triggers OnStatusChange event. If connection is successful I set the global variable named connectedToEikon to True. Later in the code I use this variable in the logic that checks whether my application is connected to Eikon and therefore if it’s safe to use AdfinX Analytics.

connectedToEikon = False
connectionToEikon = win32com.client.DispatchWithEvents("EikonDesktopDataAPILib.EikonDesktopDataAPI", EikonDesktopDataAPI_EventHandler)
print("Connecting to Eikon...")

class EikonDesktopDataAPI_EventHandler:
        def OnStatusChanged(self, EStatus):
                if EStatus == ConnectionToEikonEnum.Connected.value or EStatus == ConnectionToEikonEnum.LocalMode.value:
                        print("EikonDesktopDataAPI is connected in regular or local mode")
                        global connectedToEikon
                        connected = True
                elif EStatus == ConnectionToEikonEnum.Disconnected.value:
                        print("EikonDesktopDataAPI is disconnected or not initialized")
                elif EStatus == ConnectionToEikonEnum.Disconnected_No_license_agreement_sign_off.value:
                        print("EikonDesktopDataAPI is disconnected because the user did not accept license agreement")
                elif EStatus == ConnectionToEikonEnum.Offline.value:
                        print("EikonDesktopDataAPI has lost connection to the platform due to network or platform issue")

if not connectedToEikon:
        retval = connectionToEikon.Initialize()
        if retval != ConnectionToEikonEnum.Succeed.value:
                print("Failed to initialize Eikon Desktop Data API")
                sys.exit()
else:
        print("Already connected to Eikon")        

 

Creating a loop to keep the application running

As you can see from the previous section the connection from my application to Eikon is asynchronous. I cannot know how long it’s going to take. It can be quick or it can take a bit of time, e.g. if Eikon application was not running and is automatically launched when the connection is initialized. For this reason I need to control the runtime of my script, so it doesn't terminate before COM object finished its job and raised the event. To achieve this I create an endless loop and break out of it when either my application is successfully connected to Eikon or when Ctrl+C is pressed by the user to create a keyboard interrupt.

while True:

        try:
                time.sleep(1) 
        except KeyboardInterrupt:
                print("KeyboardInterrupt")
                break
        
        pythoncom.PumpWaitingMessages()

        if connectedToEikon:
                #The code that utilizes AdfinX Analytics goes here
                break

Windows message pump is required for COM objects to be able to raise events. PumpWaitingMessages method of pythoncom library pumps all waiting messages for the current thread. I call this method in the above loop to ensure Windows message pump is running and COM objects can raise events.

 

Setting up calculation inputs and calling AdfinX Analytics methods

When OnStatusChanged event of EikonDesktopDataAPI object is raised and the value of “connected” global variable is set to True the application is ready to use AdfinX Analytics. For this example I chose a less trivial AdCalibrate method, which does Black Derman & Toy or Hull & White model calibration. It calculates the model parameters (dates, rates and volatility curve or dates, rates, volatility plus mean-reversion) from an array of instruments. In this example I use two swaptions to calibrate Hull & White model. Of course in real life two instruments would not be enough to produce any meaningful model calibration. But for the purpose of demonstrating function calls to keep the example concise it’s good enough.

            #first swaption
            inputArray[0][0] = “S”
            inputArray[0][1] = pythoncom.MakeTime(datetime.date(2017,11,20))
            inputArray[0][2] = pythoncom.MakeTime(datetime.date(2017,12,20))
            inputArray[0][3] = “1Y”
            inputArray[0][4] = 0
            inputArray[0][5] = pythoncom.MakeTime(datetime.date(2017,12,20))
            inputArray[0][6] = 0.1
            inputArray[0][7] = “CALL EXM:E”
            inputArray[0][8] = “EUR_AB6E”

            #second swaption    
            inputArray[1][0] = “S”
            inputArray[1][1] = pythoncom.MakeTime(datetime.date(2017,11,20))
            inputArray[1][2] = pythoncom.MakeTime(datetime.date(2019,11,22))
            inputArray[1][3] = “2Y”
            inputArray[1][4] = 0
            inputArray[1][5] = pythoncom.MakeTime(datetime.date(2019,11,22))
            inputArray[1][6] = 0.11
            inputArray[1][7] = “CALL EXM:E”
            inputArray[1][8] = “EUR_AB6E”

There are numerous standards for date and time representations. COM uses date and time representation known as OLE Automation date, which is different from Python’s datetime. In order to pass the dates to AdfinX Analytics library I use pythoncom.MakeTime method, which converts Python’s datetime to a pywintypes time object, which can be passed to the COM object as OLE Automation date.

Other inputs required for AdCalibrate method are the zero curve and the RateStructue and CalcStructure strings. For illustration purposes a spreadsheet using AdCalibrate function with the same inputs is attached to this article.

Once I have the inputs setup I just need to create the module object from AdfinX Analytics containing the method I want to use and call the method.

           curveModule = connectionToEikon.CreateAdxYieldCurveModule()
           try:
                calibratedRateArray = curveModule.AdCalibrate(inputArray,zeroCurve,[],rateStructure,calcStructure,"")
                print ("Calibrated rate array:")
                print (calibratedRateArray)
            except pythoncom.com_error as e:
                print (str(e))

The result of my calculation is

Calibrated rate array:
((43060.0, 0.02, 0.0019831040554409676, -0.1230536700741586), (43061.0, 0.02, 0.0019831040554409676, -0.1230536700741586), (43242.0, 0.021, 0.0019831040554409676, -0.1230536700741586), (43791.0, 0.022, 0.0019831040554409676, -0.1230536700741586), (46714.0, 0.025, 0.0019831040554409676, -0.1230536700741586), (54018.0, 0.035, 0.0019831040554409676, -0.1230536700741586))