Article

Using Eikon Data APIs in Matlab

Alex Putkov
Developer Advocate Developer Advocate

Introduction

At the time of writing there's no native implementation of Eikon Data APIs for Matlab. However, Matlab provides rich capabilities for integration and interoperability with other development environments including Python. Thanks to these capabilities, it is perfectly possible and in fact quite easy to utilize Eikon Data APIs library for Python to retrieve data from Eikon and use it in Matlab. In this article I will discuss two approaches for utilizing data retrieved through Eikon Data APIs library for Python: calling Python functions from Matlab and using Matlab Engine for Python to call Matlab as a computational engine from Python code. This article assumes one has already set up Python environment for Eikon Data APIs. For documentation on setting up this environment refer to Quick Start guide for Eikon Data APIs.

Calling methods of Eikon Data APIs library for Python from Matlab

It is very easy to call a Python library function from Matlab. You can access Python libraries directly from Matlab by adding the "py." prefix to the Python name. Simply add "py." in front of the Python function or class name to call that function or class in a standard Python library. To call a function or a class in a Python module, add "py." in front of the Python module name followed by the Python function or class name. E.g. to create a Python list use the following expression, which calls Python built-in function named "list":

    	
            py.list({'EUR=','GBP=','CHF='}
        
        
    

To start using Eikon Data APIs library for Python (Python module named Eikon) you first needs to call set_app_key function and provide the app key generated as described in Quick Start guide for Eikon Data APIs.

    	
            py.eikon.set_app_key('use_your_app_key_here')
        
        
    

Once the app key is set you can call data retrieval functions of Eikon Data APIs library for Python.

    	
            

instruments = py.list({'EUR=','GBP=','CHF='})

fields = py.list({'BID','ASK'})

res = py.eikon.get_data(instruments, fields)

display(res)

The above code returns:

res = 
  Python tuple with no properties.
    (  Instrument     BID     ASK
    0       EUR=  1.2188  1.2192
    1       GBP=  1.3501  1.3509
    2       CHF=  0.8879  0.8881, None)

To add keyword arguments to a Python function call you can use Matlab pyargs function, which has the syntax pyargs(argKey,argValue). E.g.

 

    	
            

fields = py.list({'OPEN','HIGH','LOW','CLOSE'})

kwa = pyargs('start_date','20201201','end_date','20201215')

py.eikon.get_timeseries('EUR=',fields,kwa)

This returns

ans = 
  Python DataFrame with properties:
               T: [1×1 py.pandas.core.frame.DataFrame]
              at: [1×1 py.pandas.core.indexing._AtIndexer]
         attrs: [1×1 py.dict]
         axes: [1×2 py.list]
  columns: [1×1 py.pandas.core.indexes.base.Index]
      empty: 0
             iat: [1×1 py.pandas.core.indexing._iAtIndexer]
           iloc: [1×1 py.pandas.core.indexing._iLocIndexer]
        index: [1×1 py.pandas.core.indexes.datetimes.DatetimeIndex]
            loc: [1×1 py.pandas.core.indexing._LocIndexer]
        ndim: [1×1 py.int]
       shape: [1×2 py.tuple]
           size: [1×1 py.numpy.int64]
         style: [1×1 py.pandas.io.formats.style.Styler]
      values: [1×1 py.numpy.ndarray]

    EUR=              OPEN    HIGH     LOW       CLOSE
    Date                                      
    2020-12-01      1.1926    1.2076    1.1925      1.2070
    2020-12-02     1.2068   1.2118     1.2038     1.2115
    2020-12-03     1.2113     1.2175    1.2099     1.2140
    2020-12-04     1.2140    1.2177    1.2109      1.2120
    2020-12-07     1.2128     1.2166   1.2077      1.2108
    2020-12-08     1.2107     1.2133   1.2094      1.2101
    2020-12-09     1.2101     1.2147    1.2057      1.2081
    2020-12-10      1.2081    1.2158    1.2073      1.2136
    2020-12-11       1.2135    1.2162    1.2103       1.2111
    2020-12-14      1.2129    1.2176    1.2114        1.2143
    2020-12-15      1.2143    1.2169    1.2120       1.2151

By default data retrieval functions in Eikon Data APIs library for Python return data as pandas dataframe, which I don't find convenient for use in Matlab. I prefer retrieving data from Eikon as JSON, which can be achieved by setting the value of raw_output kwarg to True when calling data retrieval functions of Eikon Data APIs library for Python. Technically the return type of data retrieval functions in Eikon Data APIs library for Python is Python dictionary. The value corresponding to the 'data' key in this Python dictionary contains the data payload represented as Python types, namely as Python list of lists. You can extract the values from the payload by index. Exctracted values can be used in Matlab functions, as they're automatically converted to Matlab types: Python double to Matlab double and Python string to Matlab string.

    	
            

instruments = py.list({'EUR=','GBP='})

fields = py.list({'BID','ASK'})

eikon_data = py.eikon.get_data(instruments,fields,pyargs('raw_output',true))

eurbid = eikon_data{'data'}{1}{2}

eikon_data = 
  Python dict with no properties.
    {'columnHeadersCount': 1, 'data': [['EUR=', 1.2189, 1.2191], ['GBP=', 1.351, 1.3514]], 'headerOrientation': 'horizontal', 'headers': [[{'displayName': 'Instrument'}, {'displayName': 'BID', 'field': 'BID'}, {'displayName': 'ASK', 'field': 'ASK'}]], 'rowHeadersCount': 1, 'totalColumnsCount': 3, 'totalRowsCount': 3}

eurbid =
    1.2189

 

Alternatively you can decode JSON. Unfortunately jsondecode Matlab function is quite rudimentary. Some packages on Matlab File Exchange may be more convenient, though I haven't specifically tried any. One of the limitations of jsondecode function is that it requires that double quotes are used to encapsulate strings in JSON. In other words the function cannot parse JSON with strings enclosed in single quotes. For this reason to decode JSON returned by Eikon Data APIs for Python functions it is necessary to replace single quotes with double quotes.

    	
            

eikon_data = py.eikon.get_data(py.list({'IBM.N','AAPL.O'}), ...

    py.list({'TR.Revenue.fpa','TR.Revenue'}),pyargs('raw_output',true))

mjson = strrep(char(eikon_data), char(39), char(34))

mdata = jsondecode(mjson)

display(mdata.data{1})

eikon_data = 
  Python dict with no properties.
    {'columnHeadersCount': 1, 'data': [['IBM.N', 'FY2019', 77147000000], ['AAPL.O', 'FY2020', 274515000000]], 'headerOrientation': 'horizontal', 'headers': [[{'displayName': 'Instrument'}, {'displayName': 'Financial Period Absolute', 'field': 'TR.REVENUE.FPA'}, {'displayName': 'Revenue', 'field': 'TR.REVENUE'}]], 'rowHeadersCount': 1, 'totalColumnsCount': 3, 'totalRowsCount': 3}

mjson =
    '{"columnHeadersCount": 1, "data": [["IBM.N", "FY2019", 77147000000], ["AAPL.O", "FY2020", 274515000000]], "headerOrientation": "horizontal", "headers": [[{"displayName": "Instrument"}, {"displayName": "Financial Period Absolute", "field": "TR.REVENUE.FPA"}, {"displayName": "Revenue", "field": "TR.REVENUE"}]], "rowHeadersCount": 1, "totalColumnsCount": 3, "totalRowsCount": 3}'

mdata = 
  struct with fields:
    columnHeadersCount: 1
                  data: {2×1 cell}
     headerOrientation: 'horizontal'
               headers: {{3×1 cell}}
       rowHeadersCount: 1
     totalColumnsCount: 3
        totalRowsCount: 3

  3×1 cell array
    {'IBM.N'     }
    {'FY2019'    }
    {[7.7147e+10]}

Working with streaming data

To subscribe to streaming market data using Eikon Data APIs you can utilize "StreamingPrices" class. To create StreamingPrices object call the class constructor, and then, to start the subscription, call "open" function.

    	
            

sp = py.eikon.StreamingPrices(py.list({'EUR=','GBP=','CHF='}), ...

    pyargs('fields',py.list({'BID','ASK'})))

sp.open()

Once the subscription is open, you can at any time get the snapshot of the most up to date values from the object's cache using "get_snapshot" function

    	
            sp.get_snapshot()
        
        
    

ans = 
  Python DataFrame with properties:
                 T: [1×1 py.pandas.core.frame.DataFrame]
                at: [1×1 py.pandas.core.indexing._AtIndexer]
           attrs: [1×1 py.dict]
           axes: [1×2 py.list]
    columns: [1×1 py.pandas.core.indexes.base.Index]
       empty: 0
              iat: [1×1 py.pandas.core.indexing._iAtIndexer]
            iloc: [1×1 py.pandas.core.indexing._iLocIndexer]
         index: [1×1 py.pandas.core.indexes.range.RangeIndex]
             loc: [1×1 py.pandas.core.indexing._LocIndexer]
         ndim: [1×1 py.int]
        shape: [1×2 py.tuple]
            size: [1×1 py.numpy.int64]
          style: [1×1 py.pandas.io.formats.style.Styler]
       values: [1×1 py.numpy.ndarray]

          Instrument     BID         ASK 
    0           EUR=      1.2209     1.2211
    1            GBP=      1.3439     1.3440
    2           CHF=      0.8890    0.8891

When the open subscription is no longer needed, it is good practice to close it and release associated resources by calling "close" function.

    	
            sp.close()
        
        
    

Unfortunately the approach of calling Python functions from Matlab does not allow you to implement event driven notification (e.g. recalculate something whenever streaming market data is updated). This is because Python does not have built-in event handling mechanism that Matlab could hook up to. If you require event driven notification, you may consider using RDP Library for .NET in Matlab, once it is released. Or you may consider the approach of using Matlab as a computational engine and calling it from Python.

Using Matlab Engine for Python

To use Matlab as a computational engine and call it from Python you need to install Matlab Engine API for Python. Once Matlab Engine API for Python package is installed, you can use it in Python by importing matlab.engine module and calling "matlab.engine.start_matlab" function to launch a new Matlab process or using matlab.engine.connect_matlab to connect to a shared Matlab session. For more details see "Start and Stop MATLAB Engine for Python" or "Connect Python to Running Matlab Session" articles on Mathworks site.

    	
            

import matlab.engine

mleng = matlab.engine.start_matlab()

Once the Matlab engine is connected to Python session, you can call any Matlab functions from Python. Here's an example calculating interquartile range for daily returns on S&P 500 index in 2019.

    	
            

In:

df = ek.get_timeseries('.SPX', 'CLOSE', 

                       start_date='2019-01-01', 

                       end_date='2019-12-31')

df['Return'] = df['CLOSE'].diff() / df['CLOSE']

returns = df['Return'].tolist()

ts = mleng.timeseries(matlab.double(returns))

mleng.iqr(ts)

Out:

0.00857255954388043

And here's an example calculating real-time P&L on every price update for a simple portfolio of stocks. Note that the portfolio value is calculated using Matlab "dot" function, which returns the dot product (or inner product) of two matrices. In our case the arguments are the vectors containing the number of shares and the stock price for each stock in the portfolio.

    	
            

def update_price(streaming_price, instrument_name, fields, pf_value):

    prices[instrument_name] = fields['CF_LAST']

    init_value = pf_value['Initial value']

    if init_value > 0:

        current_value = mleng.dot(num_shares, matlab.double(list(prices.values()))) - init_value

        pf_value['Current value'] = current_value

        print(f'Portfolio P&L: {current_value}')

 

def init_portf_value(streaming_price, pf_value):

    pf_value['Initial value'] = mleng.dot(num_shares, matlab.double(list(prices.values())))

    print(f'Portfolio P&L: {pf_value["Initial value"]}')

 

rics = ['AAPL.O', 'IBM.N', 'FB.O']

num_shares = matlab.double([20, 12, 5])

prices = dict.fromkeys(rics,0)

pf_value = {'Initial value':0, 'Current value':0}

sp = ek.StreamingPrices(rics, fields = ['CF_LAST'], 

                        on_refresh = lambda streaming_price, instrument_name, fields : 

                            update_price(streaming_price, instrument_name, fields, pf_value),

                        on_update = lambda streaming_price, instrument_name, fields : 

                            update_price(streaming_price, instrument_name, fields, pf_value),

                        on_complete = lambda streaming_price : 

                            init_portf_value(streaming_price, pf_value))

sp.open()

When streaming data subscriptions are no longer needed, it's always a good idea to close them. And when the Matlab session is no longer required, it can be terminated using "quit" function.

    	
            sp.close()
mleng.quit()