Corporate Actions
Overview
SpiderRock provides support for a variety of corporate actions affecting listed options. Each corporate action type is processed with specific adjustment logic to ensure accurate handling of strike, position, and mark price values.
The supported corporate action types are as follows:
- Cash Adjustments
- Cash Dividend
- Special Dividend
- Stock Adjustments
- Stock Dividend
- Non-Integer Stock Split
- Reverse Stock Split
- Standard Integer Stock Split
- Structural Changes
- Merger
- Rights Offering
- Spinoff
- Symbol Conversion
- Name Change
- Consolidation
- Liquidation
Data Ingestion and Processing
SpiderRock ingests raw corporate action data from OCC and EDI feeds. These feeds are parsed internally to generate optionCorpActionRecordV5, which include all relevant adjustment parameters.
These records are accessible through SRSE and MLink.
At the end of the day, SpiderRock automatically applies all applicable adjustments to client portfolios as part of the start-of-day position rotation process.
Adjustment Parameters
Each optionCorpActionRecordV5 record contains three primary adjustment parameters:
| Parameter | Calculation | Description |
|---|---|---|
| Strike Price Adjustment | Adjusts option strike prices to reflect corporate action impact. | |
| Position Adjustment | Adjusts open positions for splits or other ratio-based events. | |
| Mark Price Adjustment | Adjusts market marks for continuity post-event. |
Manual Adjustments
In addition to automated processing, SpiderRock supports manual interventions through user-defined overrides, allowing for exceptional handling or data corrections when necessary.
Viewing Corporate Actions via SRSE and MLink
Common Queries
All records, sorted by most recent effective date:
- SRSE
- MLINK
SELECT * FROM srrisk.msgoptioncorpactionrecordv5 ORDER BY exDate DESC;
#MsgType.
MSG_TYPE = 'optioncorpactionrecordv5'
# Request Parameters for getmsgs Of The MsgType
params = {
# Required Parameters
"apiKey": API_KEY,
"cmd": 'getmsgs',
"msgType": MSG_TYPE,
"order": 'timestamp:DESC'
}
response = requests.get(MLINK_PROD_URL, params=params)
df = json_record_to_dataframe(response.json())
df = df.drop(df.index[-1])
display(df)
All records for a specified corporate action type (ex: Stock Split), sorted by most recent effective date:
- SRSE
- MLINK
SELECT * FROM srrisk.msgoptioncorpactionrecordv5 WHERE corpActionType='StockSplit' ORDER BY exDate DESC;
#MsgType.
MSG_TYPE = 'optioncorpactionrecordv5'
WHERE = 'corpactiontype:eq:StockSplit'
# Request Parameters for getmsgs Of The MsgType
params = {
# Required Parameters
"apiKey": API_KEY,
"cmd": 'getmsgs',
"msgType": MSG_TYPE,
"order": 'exDate:DESC'
}
response = requests.get(MLINK_PROD_URL, params=params)
df = json_record_to_dataframe(response.json())
df = df.drop(df.index[-1])
display(df)
All records for a specified ticker (ex: DFDV), sorted by most recent effective date:
- SRSE
- MLINK
SELECT * FROM srrisk.msgoptioncorpactionrecordv5 WHERE root_tk='DFDV' ORDER BY exDate DESC;
#MsgType.
MSG_TYPE = 'optioncorpactionrecordv5'
WHERE = 'root.tk:eq:DFDV'
# Request Parameters for getmsgs Of The MsgType
params = {
# Required Parameters
"apiKey": API_KEY,
"cmd": 'getmsgs',
"msgType": MSG_TYPE,
"where": WHERE,
"order": 'exDate:DESC'
}
response = requests.get(MLINK_PROD_URL, params=params)
df = json_record_to_dataframe(response.json())
df = df.drop(df.index[-1])
display(df)
Record count by ticker, sorted from highest to lowest:
- SRSE
- MLINK
SELECT *, COUNT(*) OVER (PARTITION BY root_tk) AS record_count FROM srrisk.msgoptioncorpactionrecordv5 ORDER BY record_count DESC;
#MsgType.
MSG_TYPE = 'optioncorpactionrecordv5'
# Request Parameters for getmsgs Of The MsgType
params = {
# Required Parameters
"apiKey": API_KEY,
"cmd": 'getmsgs',
"msgType": MSG_TYPE
}
response = requests.get(MLINK_PROD_URL, params=params)
df = json_record_to_dataframe(response.json())
df = df.drop(df.index[-1])
def sort_by_ticker_frequency(df, ticker_column='root_tk'):
"""
Sort DataFrame by ticker frequency, with most frequent tickers first.
Parameters:
-----------
df : pd.DataFrame
The DataFrame to sort
ticker_column : str
Name of the ticker column (default: 'root_tk')
Returns:
--------
pd.DataFrame
Sorted DataFrame with most frequent tickers first
"""
# Check if column exists
if ticker_column not in df.columns:
print(f"Warning: '{ticker_column}' column not found in DataFrame")
return df
# Method 1: Using value_counts to create a frequency mapping
ticker_counts = df[ticker_column].value_counts()
# Create a temporary column with the count for each ticker
df['_ticker_count'] = df[ticker_column].map(ticker_counts)
# Sort by count (descending) and then by ticker (for consistency)
df_sorted = df.sort_values(
by=['_ticker_count', ticker_column],
ascending=[False, True]
)
# Drop the temporary column
df_sorted = df_sorted.drop('_ticker_count', axis=1)
return df_sorted
sort_by_ticker_frequency(df)
All records added today, sorted by most recent timestamp:
- SRSE
- MLINK
SELECT * FROM srrisk.msgoptioncorpactionrecordv5 WHERE DATE(`timestamp`) = CURDATE() ORDER BY `timestamp` DESC;
trade_date = datetime.today().strftime('%Y-%m-%d')
#MsgType.
MSG_TYPE = 'optioncorpactionrecordv5'
WHERE = f'timestamp:gt:{trade_date} 00:00:00'
# Request Parameters for getmsgs Of The MsgType
params = {
# Required Parameters
"apiKey": API_KEY,
"cmd": 'getmsgs',
"msgType": MSG_TYPE,
"where": WHERE
}
response = requests.get(MLINK_PROD_URL, params=params)
df = json_record_to_dataframe(response.json())
df = df.drop(df.index[-1])
display(df)
All records added within a specified time interval (ex: past 7 days), sorted by most recent timestamp:
- SRSE
- MLINK
SELECT * FROM srrisk.msgoptioncorpactionrecordv5 WHERE `timestamp` >= CURDATE() - INTERVAL 7 DAY AND `timestamp` < CURDATE() + INTERVAL 1 DAY ORDER BY `timestamp` DESC;
trade_date = datetime.today().strftime('%Y-%m-%d')
last_week = (datetime.now() - timedelta(days = 7)).strftime('%Y-%m-%d')
MSG_TYPE = 'optioncorpactionrecordv5'
WHERE = f'timestamp:cb:{last_week} 00:00:00${trade_date} 00:00:00'
# Request Parameters for getmsgs Of The MsgType
params = {
# Required Parameters
"apiKey": API_KEY,
"cmd": 'getmsgs',
"msgType": MSG_TYPE,
"where": WHERE,
"order": 'timestamp:DESC'
}
response = requests.get(MLINK_PROD_URL, params=params)
df = json_record_to_dataframe(response.json())
df = df.drop(df.index[-1])
display(df)
Record count by corporate action type, sorted from highest to lowest
- SRSE
- MLINK
SELECT corpActionType, COUNT(*) AS action_count FROM srrisk.msgoptioncorpactionrecordv5 GROUP BY corpActionType ORDER BY action_count DESC;
#MsgType.
MSG_TYPE = 'optioncorpactionrecordv5'
# Request Parameters for getmsgs Of The MsgType
params = {
# Required Parameters
"apiKey": API_KEY,
"cmd": 'getmsgs',
"msgType": MSG_TYPE,
"order": 'timestamp:DESC',
}
response = requests.get(MLINK_PROD_URL, params=params)
df = json_record_to_dataframe(response.json())
df = df.drop(df.index[-1])
grouped_counts = df.groupby('corpActionType').size().reset_index(name='count')
grouped_counts = grouped_counts.sort_values('count', ascending=False)
print("Corporate Action Type Counts (using groupby):")
print(grouped_counts)
print("\n" + "="*50 + "\n")
MLink Display
The function below makes displaying return information easier. Please view 'Common Queries' above for specific examples.
import pandas as pd
import json
from typing import Dict, List, Union
import requests
from datetime import datetime
from IPython.display import display
def json_record_to_dataframe(record: Union[Dict, str, List[Dict]]) -> pd.DataFrame:
"""
Convert JSON record(s) with nested structure into a pandas DataFrame.
Parameters:
-----------
record : dict, str, or list of dicts
The JSON record(s) to convert. Can be:
- A dictionary (single record)
- A JSON string (single or multiple records)
- A list of dictionaries (multiple records)
Returns:
--------
pd.DataFrame
A flattened DataFrame containing the record data
"""
# Handle different input types
if isinstance(record, str):
record = json.loads(record)
# Ensure we're working with a list of records
if isinstance(record, dict):
records = [record]
else:
records = record
# Process each record and flatten the nested structure
flattened_records = []
for rec in records:
flat_record = {}
# Extract header information
if 'header' in rec:
flat_record['mTyp'] = rec['header'].get('mTyp')
# Extract message information
if 'message' in rec:
msg = rec['message']
# Extract pkey fields
if 'pkey' in msg:
if 'root' in msg['pkey']:
flat_record['root_at'] = msg['pkey']['root'].get('at')
flat_record['root_ts'] = msg['pkey']['root'].get('ts')
flat_record['root_tk'] = msg['pkey']['root'].get('tk')
flat_record['exDate'] = msg['pkey'].get('exDate')
# Extract sodRoot fields
if 'sodRoot' in msg:
flat_record['sodRoot_at'] = msg['sodRoot'].get('at')
flat_record['sodRoot_ts'] = msg['sodRoot'].get('ts')
flat_record['sodRoot_tk'] = msg['sodRoot'].get('tk')
# Extract other message fields
flat_record['markMultiplier'] = msg.get('markMultiplier')
flat_record['positionMultiplier'] = msg.get('positionMultiplier')
flat_record['strikeMultiplier'] = msg.get('strikeMultiplier')
flat_record['strikePrecision'] = msg.get('strikePrecision')
flat_record['corpActionText'] = msg.get('corpActionText')
flat_record['corpActionType'] = msg.get('corpActionType')
flat_record['timestamp'] = msg.get('timestamp')
flattened_records.append(flat_record)
# Create DataFrame
df = pd.DataFrame(flattened_records)
# Convert timestamp to datetime if present
if 'timestamp' in df.columns:
df['timestamp'] = pd.to_datetime(df['timestamp'])
# Convert exDate to datetime if present
if 'exDate' in df.columns:
df['exDate'] = pd.to_datetime(df['exDate'])
return df
def print_corp_action_texts(df):
"""
Print all corporate action text messages from the DataFrame.
Parameters:
-----------
df : pd.DataFrame
DataFrame containing the 'corpActionText' column
"""
# Check if the column exists
if 'corpActionText' not in df.columns:
print("Warning: 'corpActionText' column not found in DataFrame")
return
# Get the total number of messages
total_messages = len(df)
if total_messages == 0:
print("No messages to display (DataFrame is empty)")
return
print(f"Found {total_messages} corporate action message(s):\n")
print("=" * 80)
# Method 1: Simple iteration with index
for idx, text in enumerate(df['corpActionText'], 1):
if pd.notna(text): # Check if not NaN
print(f"Message {idx}:")
print(f"{text}")
print("-" * 80)
else:
print(f"Message {idx}: (No text available)")
print("-" * 80)
Additionally, here is the MLink REST URL and the user should supply their own API_KEY.
#MLINK URL
MLINK_PROD_URL = 'https://mlink-live.nms.saturn.spiderrockconnect.com/rest/json'
#MLINK API Key
API_KEY = ''
Provided here is a code snippet on how to parse in message in corpActionText:
print_corp_action_texts(df)
Examples by Action Type
Special Dividend
OCC Memo

API Queries
- SRSE
- MLINK
SELECT * FROM srrisk.msgoptioncorpactionrecordv5 WHERE corpActionType='SpecialDividend' AND root_tk='RDVI' AND exDate='2025-10-21';
#MsgType.
MSG_TYPE = 'optioncorpactionrecordv5'
WHERE = 'corpactiontype:eq:SpecialDividend & root.tk:eq:RDVI & exDate:eq:2025-10-21'
# Request Parameters for getmsgs Of The MsgType
params = {
# Required Parameters
"apiKey": API_KEY,
"cmd": 'getmsgs',
"msgType": MSG_TYPE,
"where": WHERE,
}
response = requests.get(MLINK_PROD_URL, params=params)
df = json_record_to_dataframe(response.json())
df = df.drop(df.index[-1])
display(df)
As Seen in the Record
| Field | Value |
|---|---|
| root_at | EQT |
| root_ts | NMS |
| root_tk | RDVI |
| exDate | 2025-10-21 |
| sodRoot_at | EQT |
| sodRoot_ts | NMS |
| sodRoot_tk | RDVI |
| markMultiplier | 1 |
| positionMultiplier | 1 |
| strikeFactor | 0.18 |
| strikeMultiplier | 1 |
| strikePrecision | 2 |
| corpActionText | Type=S |
| corpActionType | SpecialDividend |
| timestamp | 2025-10-21 07:31:46.023808 |
Stock Split
OCC Memo

API Queries
- SRSE
- MLINK
SELECT * FROM srrisk.msgoptioncorpactionrecordv5 WHERE corpActionType = 'StockSplit' AND root_tk = 'APPX' AND exDate = '2025-10-21';
#MsgType.
MSG_TYPE = 'optioncorpactionrecordv5'
WHERE = 'corpactiontype:eq:StockSplit & root.tk:eq:APPX & exDate:eq:2025-10-21'
# Request Parameters for getmsgs Of The MsgType
params = {
# Required Parameters
"apiKey": API_KEY,
"cmd": 'getmsgs',
"msgType": MSG_TYPE,
"where": WHERE,
}
response = requests.get(MLINK_PROD_URL, params=params)
df = json_record_to_dataframe(response.json())
df = df.drop(df.index[-1])
display(df)
As Seen in the Record
| Field | Value |
|---|---|
| root_at | EQT |
| root_ts | NMS |
| root_tk | APPX |
| exDate | 2025-10-21 |
| sodRoot_at | EQT |
| sodRoot_ts | NMS |
| sodRoot_tk | APPX |
| markMultiplier | 1 |
| positionMultiplier | 1 |
| strikeFactor | 0 |
| strikeMultiplier | 0.3335 |
| strikePrecision | 2 |
| corpActionText | Type=J |
| corpActionType | StockSplit |
| timestamp | 2025-10-21 07:31:46.023794 |
Reverse Stock Split
OCC Memo

API Queries
- SRSE
- MLINK
SELECT * FROM srrisk.msgoptioncorpactionrecordv5 WHERE corpActionType='ReverseStockSplit' AND root_tk='NVDS' AND exDate='2025-10-21';
#MsgType.
MSG_TYPE = 'optioncorpactionrecordv5'
WHERE = 'corpactiontype:eq:ReverseStockSplit & root.tk:eq:NVDS & exDate:eq:2025-10-21'
# Request Parameters for getmsgs Of The MsgType
params = {
# Required Parameters
"apiKey": API_KEY,
"cmd": 'getmsgs',
"msgType": MSG_TYPE,
"where": WHERE,
"order": 'timestamp:DESC'
}
response = requests.get(MLINK_PROD_URL, params=params)
df = json_record_to_dataframe(response.json())
df = df.drop(df.index[-1])
display(df)
As Seen in the Record
| Field | Value |
|---|---|
| root_at | EQT |
| root_ts | NMS |
| root_tk | NVDS |
| exDate | 2025-10-21 |
| sodRoot_at | EQT |
| sodRoot_ts | NMS |
| sodRoot_tk | NVDS2 |
| markMultiplier | 1 |
| positionMultiplier | 1 |
| strikeFactor | 0 |
| strikeMultiplier | 1 |
| strikePrecision | 2 |
| corpActionText | Type=I |
| corpActionType | ReverseStockSplit |
| timestamp | 2025-10-21 07:31:46.023760 |
Symbol Conversion
OCC Memo

API Queries
- SRSE
- MLINK
SELECT * FROM srrisk.msgoptioncorpactionrecordv5 WHERE corpActionType='SymbolConversion' AND root_tk='BTCM' AND exDate='2025-10-20';
#MsgType.
MSG_TYPE = 'optioncorpactionrecordv5'
WHERE = 'corpactiontype:eq:SymbolConversion & root.tk:eq:BTCM & exDate:eq:2025-10-20'
# Request Parameters for getmsgs Of The MsgType
params = {
# Required Parameters
"apiKey": API_KEY,
"cmd": 'getmsgs',
"msgType": MSG_TYPE,
"where": WHERE,
"order": 'timestamp:DESC'
}
response = requests.get(MLINK_PROD_URL, params=params)
df = json_record_to_dataframe(response.json())
df = df.drop(df.index[-1])
display(df)
As Seen in the Record
| Field | Value |
|---|---|
| root_at | EQT |
| root_ts | NMS |
| root_tk | BTCM |
| exDate | 2025-10-20 |
| sodRoot_at | EQT |
| sodRoot_ts | NMS |
| sodRoot_tk | SLAI |
| markMultiplier | 1 |
| positionMultiplier | 1 |
| strikeFactor | 0 |
| strikeMultiplier | 1 |
| strikePrecision | 2 |
| corpActionText | Type=T |
| corpActionType | SymbolConversion |
| timestamp | 2025-10-20 07:32:07.190834 |