Skip to main content
Version: 8.5.11.1

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:

ParameterCalculationDescription
Strike Price AdjustmentsodStrike=eodStrike×strikeMultiplierstrikeFactorsodStrike = eodStrike \times strikeMultiplier - strikeFactorAdjusts option strike prices to reflect corporate action impact.
Position AdjustmentsodPosition=eodPosition×positionMultipliersodPosition = eodPosition \times positionMultiplierAdjusts open positions for splits or other ratio-based events.
Mark Price AdjustmentsodMark=eodMark×markMultipliersodMark = eodMark \times markMultiplierAdjusts 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.

Common Queries

All records, sorted by most recent effective date:

SELECT * FROM srrisk.msgoptioncorpactionrecordv5 ORDER BY exDate DESC;

All records for a specified corporate action type (ex: Stock Split), sorted by most recent effective date:

SELECT * FROM srrisk.msgoptioncorpactionrecordv5 WHERE corpActionType='StockSplit' ORDER BY exDate DESC;

All records for a specified ticker (ex: DFDV), sorted by most recent effective date:

SELECT * FROM srrisk.msgoptioncorpactionrecordv5 WHERE root_tk='DFDV'  ORDER BY exDate DESC;

Record count by ticker, sorted from highest to lowest:

SELECT  *, COUNT(*) OVER (PARTITION BY root_tk) AS record_count FROM srrisk.msgoptioncorpactionrecordv5 ORDER BY record_count DESC;

All records added today, sorted by most recent timestamp:

SELECT * FROM srrisk.msgoptioncorpactionrecordv5 WHERE DATE(`timestamp`) = CURDATE() ORDER BY `timestamp` DESC;

All records added within a specified time interval (ex: past 7 days), sorted by most recent timestamp:

SELECT * FROM srrisk.msgoptioncorpactionrecordv5 WHERE `timestamp` >= CURDATE() - INTERVAL 7 DAY AND `timestamp` < CURDATE() + INTERVAL 1 DAY ORDER BY `timestamp` DESC;

Record count by corporate action type, sorted from highest to lowest

SELECT corpActionType, COUNT(*) AS action_count FROM srrisk.msgoptioncorpactionrecordv5 GROUP BY corpActionType ORDER BY action_count DESC;

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

Special Dividend OCC Memo

API Queries

SELECT * FROM srrisk.msgoptioncorpactionrecordv5 WHERE corpActionType='SpecialDividend' AND root_tk='RDVI' AND exDate='2025-10-21';

As Seen in the Record

FieldValue
root_atEQT
root_tsNMS
root_tkRDVI
exDate2025-10-21
sodRoot_atEQT
sodRoot_tsNMS
sodRoot_tkRDVI
markMultiplier1
positionMultiplier1
strikeFactor0.18
strikeMultiplier1
strikePrecision2
corpActionTextType=S
corpActionTypeSpecialDividend
timestamp2025-10-21 07:31:46.023808

Stock Split

OCC Memo

Stock Split OCC Memo

API Queries

SELECT * FROM srrisk.msgoptioncorpactionrecordv5 WHERE corpActionType = 'StockSplit' AND root_tk = 'APPX' AND exDate = '2025-10-21';

As Seen in the Record

FieldValue
root_atEQT
root_tsNMS
root_tkAPPX
exDate2025-10-21
sodRoot_atEQT
sodRoot_tsNMS
sodRoot_tkAPPX
markMultiplier1
positionMultiplier1
strikeFactor0
strikeMultiplier0.3335
strikePrecision2
corpActionTextType=J
corpActionTypeStockSplit
timestamp2025-10-21 07:31:46.023794

Reverse Stock Split

OCC Memo

Stock Split OCC Memo

API Queries

SELECT * FROM srrisk.msgoptioncorpactionrecordv5 WHERE corpActionType='ReverseStockSplit' AND root_tk='NVDS' AND exDate='2025-10-21';

As Seen in the Record

FieldValue
root_atEQT
root_tsNMS
root_tkNVDS
exDate2025-10-21
sodRoot_atEQT
sodRoot_tsNMS
sodRoot_tkNVDS2
markMultiplier1
positionMultiplier1
strikeFactor0
strikeMultiplier1
strikePrecision2
corpActionTextType=I
corpActionTypeReverseStockSplit
timestamp2025-10-21 07:31:46.023760

Symbol Conversion

OCC Memo

Stock Split OCC Memo

API Queries

SELECT * FROM srrisk.msgoptioncorpactionrecordv5 WHERE corpActionType='SymbolConversion' AND root_tk='BTCM' AND exDate='2025-10-20';

As Seen in the Record

FieldValue
root_atEQT
root_tsNMS
root_tkBTCM
exDate2025-10-20
sodRoot_atEQT
sodRoot_tsNMS
sodRoot_tkSLAI
markMultiplier1
positionMultiplier1
strikeFactor0
strikeMultiplier1
strikePrecision2
corpActionTextType=T
corpActionTypeSymbolConversion
timestamp2025-10-20 07:32:07.190834