Author:
Overview
The current article introduces functions to construct option RICs on equities and indices. Section 1 defines supplementary functions further called by the main RIC construction functions. In section 2, I define separate functions per supported stock exchange. Section 3 defines a universal function that takes ISIN, maturity, strike, and option type as an input finds all exchanges where the options on the given asset are traded, constructs RICs for them, validates, and returns the constructed RICs along with the prices.
Additionally, I have conducted the class implementation of the solution, which offers more scaleable usability of the functions. Please check it in the associated GitHub folder.
The current version covers the following exchanges:
- US OPRA - refer to RULES7, RULES2, RULES3, RULES4 in Workspace, and Guideline for strikes above 10000 in MyRefinitiv.
- EUREX - refer to RULES2, RULES3, RULES4 in Workspace, and general option RIC structure in MyRefinitiv.
- Osaka Exchange - refer to RULES2, RULES3, RULES4 in Workspace, and RIC structure for Osaka exchange in MyRefinitiv.
- Stock Exchange of Hong Kong - refer to RULES2, RULES3, RULES4 in Workspace, and RIC structure for HK exchange in MyRefinitiv.
- Hong Kong Future Exchange - refer to RULES2, RULES3, RULES4 in Workspace, and RIC structure for HK exchange in MyRefinitiv.
- Intercontinental Exchange (ICE) - refer to RULES2, RULES3, RULES4 in Workspace, and general option RIC structure in MyRefinitiv.
The syntax for the expired options is universal across exchanges and can be found here.
Table of Content
Overview
Install and import packages
Section 1: Define supplementary functions
1.1 Function to get exchange codes where the option is traded
1.2 Function to check if found RIC is valid (if price data exists)
1.3 Function to get option Expiration month code
Section 2: Define functions to get option RICs for each exchange
2.1 Function for OPRA
2.2 Function for the Stock Exchange of Hong Kong
2.3 Function for the Osaka Stock Exchange
2.4 Function for the EUREX
2.5 Function for the Intercontinental Exchange
Section 3: Universal function for all above exchanges
!pip install refinitiv.data
import refinitiv.data as rd
import pandas as pd
from refinitiv.data.errors import RDError
from datetime import timedelta
from datetime import datetime
rd.open_session()
1.1 Function to get exchange codes where the option is traded
This function allows to get the list of exchange codes where an option on the given asset is traded. The function takes asset RIC as an input and returns the list of exchanges. Then function is Section 3 constructs RICs for all supported exchanges in that list. The core of the function is the RD Search function.
def get_exchange_code(asset):
response = rd.discovery.search(
query = asset,
filter = "SearchAllCategory eq 'Options' and Periodicity eq 'Monthly' ",
select = 'ExchangeCode',
group_by = "ExchangeCode",
)
exchanges = response.drop_duplicates()["ExchangeCode"].to_list()
exchange_codes = []
for exchange in exchanges:
exchange_codes.append(exchange)
return exchange_codes
get_exchange_code('.FTSE')
['IEU', 'EUX']
def get_exp_month(maturity, opt_type, strike = None, opra=False):
maturity = pd.to_datetime(maturity)
# define option expiration identifiers
ident = {'1': {'exp': 'A','C': 'A', 'P': 'M'},
'2': {'exp': 'B', 'C': 'B', 'P': 'N'},
'3': {'exp': 'C', 'C': 'C', 'P': 'O'},
'4': {'exp': 'D', 'C': 'D', 'P': 'P'},
'5': {'exp': 'E', 'C': 'E', 'P': 'Q'},
'6': {'exp': 'F', 'C': 'F', 'P': 'R'},
'7': {'exp': 'G', 'C': 'G', 'P': 'S'},
'8': {'exp': 'H', 'C': 'H', 'P': 'T'},
'9': {'exp': 'I', 'C': 'I', 'P': 'U'},
'10': {'exp': 'J', 'C': 'J', 'P': 'V'},
'11': {'exp': 'K', 'C': 'K', 'P': 'W'},
'12': {'exp': 'L', 'C': 'L', 'P': 'X'}}
# get expiration month code for a month
if opt_type.upper() == 'C':
exp_month = ident[str(maturity.month)]['C']
elif opt_type.upper() == 'P':
exp_month = ident[str(maturity.month)]['P']
if opra and strike > 999.999:
exp_month = exp_month.lower()
return ident, exp_month
1.3 Function to check for expiry
This function checks if the option is expired or not and adds the expiration numenclature if it is.
def check_expiry(ric, maturity, ident):
maturity = pd.to_datetime(maturity)
if maturity < datetime.now():
ric = ric + '^' + ident[str(maturity.month)]['exp'] + str(maturity.year)[-2:]
return ric
check_expiry('AAPLA212216000.U', '2022-01-21', ident)
'AAPLA212216000.U^A22'
1.4 Function to request prices
This function allows validation of the constructed option RICs by requesting prices. If prices are returned, we can confirm that the RIC(s) is (are) valid, otherwise, we can't confirm the validation.
def request_prices(ric):
prices = []
try:
prices = rd.get_history(ric, fields = ['BID','ASK','TRDPRC_1','SETTLE'])
except RDError as err:
print(f'Constructed ric {ric} - {err}')
return prices
Section 2: Define functions to get option RICs for each exchange
In this section, I define functions for option RIC construction for different exchanges. Those functions take asset RIC, maturity, strike, and option type as input and return validated rics and the prices. If no price is found for constructed RICs, the functions print out possible RICs.
def get_ric_opra(asset, maturity, strike, opt_type):
maturity = pd.to_datetime(maturity)
# trim underlying asset's RIC to get the required part for option RIC
if asset[0] == '.': # check if the asset is an index or an equity
asset_name = asset[1:] # get the asset name - we remove "." symbol for index options
else:
asset_name = asset.split('.')[0] # we need only the first part of the RICs for equities
ident, exp_month = get_exp_month(maturity, opt_type, strike = strike, opra=True)
# get strike prrice
if type(strike) == float:
int_part = int(strike)
dec_part = str(str(strike).split('.')[1])
else:
int_part = int(strike)
dec_part = '00'
if len(dec_part) == 1:
dec_part = dec_part + '0'
if int(strike) < 10:
strike_ric = '00' + str(int_part) + dec_part
elif int_part >= 10 and int_part < 100:
strike_ric = '0' + str(int_part) + dec_part
elif int_part >= 100 and int_part < 1000:
strike_ric = str(int_part) + dec_part
elif int_part >= 1000 and int_part < 10000:
strike_ric = str(int_part) + '0'
elif int_part >= 10000 and int_part < 20000:
strike_ric = 'A' + str(int_part)[-4:]
elif int_part >= 20000 and int_part < 30000:
strike_ric = 'B' + str(int_part)[-4:]
elif int_part >= 30000 and int_part < 40000:
strike_ric = 'C' + str(int_part)[-4:]
elif int_part >= 40000 and int_part < 50000:
strike_ric = 'D' + str(int_part)[-4:]
# build ric
ric = asset_name + exp_month + str(maturity.day) + str(maturity.year)[-2:] + strike_ric + '.U'
ric = check_expiry(ric, maturity, ident)
prices = request_prices(ric)
# return valid ric(s)
if len(prices) == 0:
print('RIC with specified parameters is not found')
return ric, prices
ric, prices = get_ric_opra('AAPL.O', '2022-01-21', 180, 'C')
ric
'AAPLA212218000.U^A22'
prices.head()
TRDPRC_1 | BID | ASK | |
10/22/2021 | 0.54 | 0.51 | 0.54 |
10/25/2021 | 0.5 | 0.49 | 0.52 |
10/26/2021 | 0.55 | 0.53 | 0.55 |
10/27/2021 | 0.6 | 0.59 | 0.62 |
10/28/2021 | 0.82 | 0.75 | 0.84 |
def get_ric_hk(asset, maturity, strike, opt_type):
maturity = pd.to_datetime(maturity)
# get asset name and strike price for the asset
if asset[0] == '.':
asset_name = asset[1:]
strike_ric = str(int(strike))
else:
asset_name = asset.split('.')[0]
strike_ric = str(int(strike * 100))
# get expiration month codes
ident, exp_month = get_exp_month(maturity, opt_type)
# get rics for options on indexes. Return if valid add to the possible_rics list if no price is found
if asset[0] == '.':
ric = asset_name + strike_ric + exp_month + str(maturity.year)[-1:] + '.HF'
ric = check_expiry(ric, maturity, ident)
prices = request_prices(ric)
if len(prices) == 0:
print('RIC with specified parameters is not found')
else:
return ric, prices
else:
# get rics for options on equities. Return if valid add to the possible_rics list if no price is found
# there could be several generations of options depending on the number of price adjustments due to a corporate event
# here we use 4 adjustment opportunities.
for i in range(4):
ric = asset_name + strike_ric + str(i)+ exp_month + str(maturity.year)[-1:] + '.HK'
ric = check_expiry(ric, maturity, ident)
prices = request_prices(ric)
if len(prices) == 0:
print('RIC with specified parameters is not found')
else:
return ric, prices
return ric, prices
ric, prices = get_ric_hk('.HSI', '2022-03-30', 18400, 'C')
ric
'HSI18400C2.HF^C22'
prices.head()
TRDPRC_1 | BID | ASK | SETTLE | |
12/29/2021 | <NA> | <NA> | <NA> | 4651 |
12/30/2021 | <NA> | <NA> | <NA> | 4732 |
12/31/2021 | <NA> | <NA> | <NA> | 5016 |
1/3/2022 | <NA> | <NA> | <NA> | 4826 |
1/4/2022 | <NA> | <NA> | <NA> | 4862 |
def get_ric_ose(asset, maturity, strike, opt_type):
maturity = pd.to_datetime(maturity)
strike_ric = str(strike)[:3]
ident, exp_month = get_exp_month(maturity, opt_type)
j_nets = ['', 'L', 'R']
generations = ['Y', 'Z', 'A', 'B', 'C']
if asset[0] == '.':
index_dict = {'N225':'JNI', 'TOPX':'JTI'}
# Option Root codes for indexes are different from the RIC, so we rename where necessery
asset_name = index_dict[asset.split('.')[1]]
# we consider also J-NET (Off-Auction(with "L")) and High frequency (with 'R') option structures
for jnet in j_nets:
ric = asset_name + jnet + strike_ric + exp_month + str(maturity.year)[-1:] + '.OS'
ric = check_expiry(ric, maturity, ident)
prices = request_prices(ric)
if len(prices) == 0:
print('RIC with specified parameters is not found')
else:
return ric, prices
else:
asset_name = asset.split('.')[0]
# these are generation codes similar to one from HK
for jnet in j_nets:
for gen in generations:
ric = asset_name + jnet + gen + strike_ric + exp_month + str(maturity.year)[-1:] + '.OS'
ric = check_expiry(ric, maturity, ident)
prices = request_prices(ric)
if len(prices) == 0:
print('RIC with specified parameters is not found')
else:
return ric, prices
return ric, prices
ric, prices = get_ric_ose('7974.T', '2022-03-30', 50000, 'P')
ric
'7974Y500O2.OS^C22'
prices.head()
TRDPRC_1 | BID | ASK | SETTLE | |
12/29/2021 | None | None | None | 1609 |
12/30/2021 | None | None | None | 1777 |
1/4/2022 | None | None | None | 1516 |
1/5/2022 | None | None | None | 1647 |
1/6/2022 | None | None | None | 1848 |
def get_ric_eurex(asset, maturity, strike, opt_type):
maturity = pd.to_datetime(maturity)
if asset[0] == '.':
index_dict = {'FTSE':'OTUK', 'SSMI':'OSMI', 'GDAXI':'GDAX', 'ATX':'FATXA', 'STOXX50E':'STXE'}
asset_name = index_dict[asset.split('.')[1]]
else:
asset_name = asset.split('.')[0]
ident, exp_month = get_exp_month(maturity, opt_type)
if type(strike) == float:
int_part = int(strike)
dec_part = str(str(strike).split('.')[1])[0]
else:
int_part = int(strike)
dec_part = '0'
if len(str(int(strike))) == 1:
strike_ric = '0' + str(int_part) + dec_part
else:
strike_ric = str(int_part) + dec_part
generations = ['', 'a', 'b', 'c', 'd']
for gen in generations:
ric = asset_name + strike_ric + gen + exp_month + str(maturity.year)[-1:] + '.EX'
ric = check_expiry(ric, maturity, ident)
prices = request_prices(ric)
if len(prices) == 0:
print('RIC with specified parameters is not found')
else:
return ric, prices
return ric, prices
ric, prices = get_ric_eurex('.STOXX50E', '2022-03-30', 4200, 'P')
ric
'STXE42000O2.EX^C22'
prices.head()
TRDPRC_1 | BID | ASK | SETTLE | |
12/29/2021 | 112.5 | 110.8 | 114.4 | 112.9 |
12/30/2021 | 100.7 | 101.5 | 104.1 | 102.5 |
1/3/2022 | 91.1 | 87.3 | 88.8 | 88 |
1/4/2022 | 72.8 | 79.7 | 82.8 | 81.2 |
1/5/2022 | 69.4 | 68.4 | 71.5 | 70.3 |
def get_ric_ieu(asset, maturity, strike, opt_type):
maturity = pd.to_datetime(maturity)
if asset[0] == '.':
index_dict = {'FTSE':'LFE'}
asset_name = index_dict[asset.split('.')[1]]
else:
asset_name = asset.split('.')[0]
ident, exp_month = get_exp_month(maturity, opt_type)
if len(str(int(strike))) == 2:
strike_ric = '0' + str(int(strike))
else:
strike_ric = str(int(strike))
if type(strike) == float and len(str(int(strike))) == 1:
int_part = int(strike)
dec_part = str(str(strike).split('.')[1])[0]
strike_ric = '0' + str(int_part) + dec_part
generations = ['', 'a', 'b', 'c', 'd']
for gen in generations:
ric = asset_name + strike_ric + gen + exp_month + str(maturity.year)[-1:] + '.L'
ric = check_expiry(ric, maturity, ident)
prices = request_prices(ric)
if len(prices) == 0:
print('RIC with specified parameters is not found')
else:
return ric, prices
return ric, prices
ric, prices = get_ric_ieu('.FTSE', '2022-06-30', 7000, 'C')
ric
'LFE7000F2.L'
prices.head()
TRDPRC_1 | BID | ASK | SETTLE | |
1/19/2022 | <NA> | 629.5 | 654 | 630.5 |
1/20/2022 | <NA> | 611 | 637.5 | 619 |
1/21/2022 | <NA> | 543 | 577 | 564.5 |
1/24/2022 | <NA> | 410 | 470.5 | 472 |
1/25/2022 | <NA> | 492.5 | 530 | 515 |
Section 3: Universal function for all above exchanges
In this section I built a universal function which takes ric, maturity, strike and option type as an input, finds all exchanges where the options on given asset are traded, constructs RICs for them, validates and returns the constructed RICs along with the prices.
def get_option_ric(asset, maturity, strike, opt_type):
# define covered exchanges along with functions to get RICs from
exchanges = {'OPQ': get_ric_opra,
'IEU': get_ric_ieu,
'EUX': get_ric_eurex,
'HKG': get_ric_hk,
'HFE': get_ric_hk,
'OSA': get_ric_ose}
# get exchanges codes where the option on the given asset is traded
exchnage_codes = get_exchange_code(asset)
# get the list of (from all available and covered exchanges) valid rics and their prices
options_data = {}
for exch in exchnage_codes:
if exch in exchanges.keys():
ric, prices = exchanges[exch](asset, maturity, strike, opt_type)
if len(prices) != 0:
options_data[ric] = prices
print(f'Option RIC for {exch} exchange is successfully constructed')
else:
print(f'The {exch} exchange is not supported yet')
return options_data
Below, I test the fuinction for several exchanges and assets:
# options_data = get_option_ric('ABBN.S', '2022-03-30', 34, 'C')
# options_data = get_option_ric('ALVG.DE', '2022-03-18', 220, 'C')
# options_data = get_option_ric('BBVA.MC', '2022-03-18', 4.7, 'C')
# options_data = get_option_ric('AIRP.PA', '2022-03-18', 150, 'C')
# options_data = get_option_ric('BARC.L', '2022-03-18', 210, 'P')
# options_data = get_option_ric('AZN.L', '2022-03-18', 9000, 'P')
# options_data = get_option_ric('VOD.L', '2022-03-18', 100, 'P')
# options_data = get_option_ric('ALSO.PA', '2022-06-18', 36, 'P')
# options_data = get_option_ric('ENI.MI', '2022-06-16', 13, 'C')
# options_data = get_option_ric('ASML.AS', '2022-02-18', 640, 'P')
# options_data = get_option_ric('.HSI', '2022-03-30', 18400, 'C')
# options_data = get_option_ric('1093.HK', '2022-03-30', 10, 'C')
# options_data = get_option_ric('0700.HK', '2022-04-28', 480, 'C')
# options_data = get_option_ric('.TOPX', '2022-06-10', 1900, 'C')
# options_data = get_option_ric('6501.T', '2022-06-10', 6500, 'C')
# options_data = get_option_ric('.STOXX50E', '2022-03-18', 4200, 'C')
# options_data = get_option_ric('.N225', '2022-01-17', 25875, 'C')
options_data = get_option_ric('.SPX', '2022-02-18', 5000, 'C')
Option RIC for IEU exchange is successfully constructed
Option RIC for EUX exchange is successfully constructed
options_data
{'SPXb182250000.U^B22': SPXb182250000.U^B22 BID ASK TRDPRC_1 SETTLE
Date
2022-01-21 0.25 0.6 0.5 <NA>
2022-01-24 0.5 0.7 0.65 <NA>
2022-01-25 0.4 0.6 0.53 <NA>
2022-01-26 0.4 0.8 0.75 <NA>
2022-01-27 0.3 0.4 0.4 <NA>
2022-01-28 0.45 0.65 0.6 <NA>
2022-01-31 0.2 0.3 0.25 <NA>
2022-02-01 0.2 0.3 0.23 <NA>
2022-02-02 0.05 0.25 0.25 <NA>
2022-02-03 0.05 0.15 0.15 <NA>
2022-02-04 0.05 0.1 0.1 <NA>
2022-02-07 <NA> 0.1 0.05 <NA>
2022-02-08 <NA> 0.1 0.08 <NA>
2022-02-09 <NA> 0.15 0.08 <NA>
2022-02-10 <NA> 0.05 0.03 <NA>
2022-02-11 <NA> 0.15 <NA> <NA>
2022-02-14 <NA> 0.05 0.05 <NA>
2022-02-15 <NA> 0.05 0.05 <NA>
2022-02-16 <NA> 0.05 0.05 <NA>
2022-02-17 <NA> 0.05 0.05 <NA>}
Conclusion
Current article preseneted functions to find valid RICs for options on stock and indices traded in several exchanges. Additionally, I presented a universal function which finds the exchanges where an option on a given asset is traded, constructs RICs for them, validates and returns the constructed RICs along with the price.
The current version of this article is limited for certain exchanges, includig OPRA, EUREX, ICE, Hong Kong and Osaka, and for options on certain asset types, including indices and stocks. Please let me know which other exchanges and/or options on other asset categories you want me to build functions for. You can contact me directly via h.aramyan@lseg.com or raise your questions/suggestion via Q&A portal of Developer community.
- Register or Log in to applaud this article
- Let the author know how much this article helped you