Libraries Used: lseg.data
Level: Intermediate
Introduction
Corporate actions are among the most common sources of data discontinuity in financial time series. A stock split, a dividend, or a merger can introduce breaks in price history that - if left unaddressed - propagate errors through every downstream calculation: returns, volatility, signals, and portfolio valuations.
This guide covers the full lifecycle of working with corporate actions using the LSEG Data Library for Python: from understanding what corporate actions are, to fetching events, applying adjustments, and safeguarding your pipelines against identifier changes.
Prerequisites:
- Python 3.8+
- LSEG Workspace (desktop) or valid Platform credentials
- lseg-data library installed
Table of Contents
Theory:
- 01 - What Are Corporate Actions?
- 02 - Types of Corporate Actions
- 03 - Key Dates
- 04 - Price Adjustments
Practical:
- 05 - Environment Setup
- 06 - Fetching Capital Change Events
- 07 Adjusted vs. Unadjusted Prices
- 08. Detecting Price Discontinuities
- 09 - Dividends
- 10. Using Field-Level Parameters
- 11. Applying Adjustment Factors Manually
- 12. Handling RIC Changes with PermIDs
- 13. Broader Corporate Events (TR.EventType)
- 14. Event Metadata: Status, IDs, and Forward-Looking Events
- 15 - Summary & Resources
01 - What Are Corporate Actions?
A corporate action is any event initiated by a company that materially changes its shareholders' position, its outstanding securities, or its capital structure. 'Material' here means measurable: share count, price per share, capital structure, or the investor's relationship with the company.
Why developers need to care:
Corporate actions break raw price series. Consider a stock trading at $800 on Tuesday that opens at $200 on Wednesday. In unadjusted data, this appears as a 75% decline. In reality, it was a 4-for-1 stock split - total market capitalisation unchanged, share count quadrupled, price quartered.
The challenge: unadjusted price data does not raise errors or warnings. It returns numbers that are systematically incorrect for any calculation that spans across a corporate action date. Most financial data bugs - incorrect returns, inflated volatility, false signals - trace back to corporate actions not being accounted for.
Corporate actions are classified as either mandatory (automatic, no shareholder action required) or voluntary (requires shareholder election to participate).
02 - Types of Corporate Actions
2.1 Mandatory Corporate Actions
These events affect all shareholders automatically - no action or election is required. The common ones are:
| Type | Description | Price Impact |
|---|---|---|
| Cash Dividend | Company distributes earnings to shareholders on a per-share basis. Eligibility determined by the record date. | Price typically drops by the dividend amount on ex-date |
| Stock Split | A stock split is a corporate event which increases the number of a company’s shares, while simultaneously reducing its per share price, such that the market capitalization of the company remains the same before and after the event. Stock splits are quoted in terms of shares received to shares held. E.g., 4-for-1: each share becomes four, price quartered. | Direct price reduction by split ratio |
| Reverse Stock Split | Existing shares are consolidated to form new shares with a higher nominal value (where applicable). This is the opposite of a stock split and is also referred to as a “Reverse Stock Split”. In a reverse split, the shares of a company are decreased while its per-share-price is increased by the adjustment factor. Also, like a stock split, the overall market capitalization of the company remains unchanged by the event. Reverse Stock splits are quoted in terms of shares received to shares held. | Direct price increase by consolidation ratio |
| Bonus Issue | Additional shares issued to existing shareholders at no cost, funded by capitalising retained earnings. Similar market effect to a split, but different accounting treatment. | Price adjusts proportionally |
| Spin-Off | pin-off represents a form of divestiture usually resulting in an independent company or in an existing company. The spunoff company takes assets, intellectual property, technology, and/or existing products from the parent organization and forms its own private or publicly listed company. Shares of the new organization are distributed to the equity shareholders of the parent organization, at a ratio established by the parent. The new company formed by this divestiture is called the “spun-off” entity. Spin-offs is also referred to as “demergers”. | Parent price adjusts; new entity begins trading |
| Merger & Acquisition | Two companies combine. Shareholders of the acquired entity receive cash, shares of the acquirer, or both. The acquired ticker may cease to exist. | Acquired ticker delisted; data continuity challenge |
2.2 Voluntary Corporate Actions
These require shareholders to actively choose whether to participate. Non-participation is the default outcome. The common ones are:
| Type | Description | Shareholder Options |
|---|---|---|
| Rights Issue | A short-term privilege granted to common shareholders to purchase additional common shares, usually at a discount, from the company itself, at a stated price and within a specified time. Rights issues are either renounceable or non-renounceable. If the issue is renounceable, the shareholders can trade on their rights on the stock exchange and these rights are forfeited if the shareholder does not exercise. Rights are traded for a short period of time. | Subscribe, sell the rights (if renounceable), or let them lapse |
| Tender Offer | A buyer proposes to purchase shares at a premium over market price. | Tender (sell) shares at the offered price, or reject and continue holding |
| Buyback (Share Repurchase) | Offer to existing shareholders by the issuing company to repurchase equity or other securities convertible into equity. The objective of the offer is to reduce the number of outstanding equities. These repurchased shares could either be retired by the company or retained as treasury stock, to be reissued at a later date. The share buyback can be either mandatory or voluntary. | Participate in the buyback, or hold and ignore the offer |
03 - Key Dates
Every corporate action revolves around a set of critical dates that determine eligibility, price adjustment timing, and settlement. These apply across global markets.
| Date | Purpose |
|---|---|
| Declaration (Announcement) Date | The company officially announces the corporate action, disclosing type, ratio, record date, payment date, and other terms. |
| Ex-Date | The stock begins trading without the entitlement. Buyers on or after this date do not receive the benefit. Price typically adjusts on this date. Usually falls 1-2 business days before the record date (varies by market). |
| Record Date | Shareholders registered on the company's books at the end of this day are eligible. Shares must be settled (not just traded) to qualify. |
| Payment (Effective) Date | The benefit is delivered - cash, shares, rights, or other instruments. |
Timeline example (Cash Dividend):
Declaration ──── Ex-Date ──── Record Date ──── Payment Date
Apr 18 May 30 May 30 Jun 30
Note: The relationship between ex-date and record date varies by market. In most markets, ex-date precedes record date by the settlement cycle (T+1 or T+2).
04 - Price Adjustments
The effect of Corporate Action event creates gap in current market price and historical market prices of the share and it misleads investors choice of buying/selling if not filled the gap. Adjustment factor is calculated and applied to fill the gap appropriately. Historical prices must be adjusted backwards.
How it works:
A stock trading at 800 undergoes a 4-for-1 split. Post-split price: 200. To create a continuous, comparable series, all historical prices before the split are divided by the split ratio (4). The 800 becomes 200 in the adjusted series.
Adjustment Factor:
LSEG provides an Adjustment Factor - a multiplier that can be applied backward through price history. This allows you to:
- Convert unadjusted prices to adjusted prices: Adjusted Price = Unadjusted Price × Adjustment Factor
- Maintain correct return calculations across event dates
- Preserve data integrity in automated pipelines
The adjustment factors should be applied on Price Only or Pricing data (Price and Volume) & Per Share data (Dividends, Earnings, Shares). Adjustment type is also provided which indicate the factors should be adjusted on components for each event type. The adjustment types are
- Capital Change (CCH) - This adjustment type indicates the adjustment factor should be applied on Pricing data (Price and Volume) & Per Share data (Dividends, Earnings, Shares). This event type is predominantly used where the shares outstanding value changes due to corporate action event. For example- Stock splits, consolidations, Rights issue (same stock), Priority Issue (same stock), Bonus Issue (Same stock) and Stock Dividend (Same stock)
- Reuters Pricing Only (RPO) - This adjustment type indicates the adjustment factor should be applied on Pricing only. This type is predominantly used where there would not be change in the existing number of outstanding shares due to corporate action event. For example, Demergers, Rights issue (different stock), Priority Issue (different stock), Bonus Issue (different stock) and Stock Dividend (different stock), capital returns and so on.
Note: Refer section 11 for practical demo.
Default behaviour: LSEG delivers adjusted prices by default. You do not need to do anything extra to get a clean, split-adjusted series. To retrieve raw unadjusted prices, you must explicitly pass Adjusted=0 as a field-level parameter.
Both views have valid use cases:
Adjusted (default): Continuous series for return calculations, backtesting, technical analysis
Unadjusted (Adjusted=0): Actual traded prices, identifying corporate action dates visually, regulatory reporting
# Install the LSEG Data Library (run once)
# pip install lseg-data
import lseg.data as ld
import pandas as pd
import matplotlib.pyplot as plt
from IPython.display import display
# Visual defaults
plt.rcParams['figure.figsize'] = (13, 6)
plt.rcParams['axes.grid'] = True
plt.rcParams['grid.alpha'] = 0.3
# I am using the desktop session (requires LSEG Workspace running locally)
ld.open_session()
Tip: To identify the correct field names for any LSEG content set, use the Data Item Browser (DIB) available in Workspace. Filter by Corporate Action under content classification to browse all available fields, their descriptions, and supported parameters.
06 - Fetching Capital Change Events
Capital change events are corporate actions that directly affect shares - quantity, face value, or price. These include stock splits, bonus issues, and reverse splits.
The field TR.CACorpActEventType returns capital change events specifically. Below, we retrieve the complete split history for Apple (AAPL.O).
share_splits = ld.get_data(
universe=['AAPL.O'],
fields=[
'TR.CACorpActEventType', # Type of capital change event
'TR.CACorpActDesc', # Description of the event
'TR.CATermsOldShares', # Old shares in ratio (e.g., 1 in a 4-for-1)
'TR.CATermsNewShares', # New shares in ratio (e.g., 4 in a 4-for-1)
'TR.CAAnnouncementDate',
'TR.CAExDate',
'TR.CARecordDate',
'TR.CAEffectiveDate',
'TR.CAIsRescinded' # Whether the action was subsequently revoked
],
parameters={
'SDate': '1950-01-01',
'EDate': '2026-05-31',
'CAEventType': 'SSP;SIS' # SSP = Share Split, SIS = Scrip/Bonus Issue
}
)
display(share_splits)
The CAEventType parameter accepts semicolon-separated codes to filter by event type.
The full list of valid codes is available in the Data Item Browser (DIB) in the parameters section for the CAEventType field. Always verify codes against DIB, as they may vary by coverage.
You can pass multiple codes (e.g., 'SSP;SIS') or omit the parameter to retrieve all capital change events.
# Fetch unadjusted close prices
price_unadj = ld.get_history(
universe=['AAPL.O'],
fields=['TR.CLOSEPRICE(Adjusted=0)'], # 0 = unadjusted
interval='1D',
start='2000-01-01',
end='2026-05-31'
)
# Fetch adjusted close prices
price_adj = ld.get_history(
universe=['AAPL.O'],
fields=['TR.CLOSEPRICE(Adjusted=1)'], # 1 = adjusted
interval='1D',
start='2000-01-01',
end='2026-05-31'
)
fig, axes = plt.subplots(2, 1, figsize=(14, 10), sharex=True)
# Unadjusted
axes[0].plot(price_unadj.index, price_unadj['Close Price'], color='#d9534f', linewidth=0.8)
axes[0].set_title('AAPL — Unadjusted Close Price', fontsize=13, fontweight='bold')
axes[0].set_ylabel('Price (USD)')
axes[0].annotate('Visible price drops at\ncorporate action dates',
xy=(0.65, 0.75), xycoords='axes fraction',
fontsize=10, color='#d9534f', style='italic')
# Adjusted
axes[1].plot(price_adj.index, price_adj['Close Price'], color='#1a7f37', linewidth=0.8)
axes[1].set_title('AAPL — Adjusted Close Price', fontsize=13, fontweight='bold')
axes[1].set_ylabel('Price (USD)')
axes[1].set_xlabel('Date')
axes[1].annotate('Continuous series — corporate actions\nadjusted backward through history',
xy=(0.02, 0.80), xycoords='axes fraction',
fontsize=10, color='#1a7f37', style='italic')
plt.tight_layout()
plt.show()
Observations:
- The unadjusted chart shows sudden price drops at each split date. These are not crashes - they are the raw traded prices before and after each corporate action. This view is useful for identifying when events occurred and analysing post-event price recovery.
- The adjusted chart rewrites history so that the entire series is comparable on a single scale. This is the correct input for return calculations, moving averages, and backtesting.
Important: TR. Fields vs. Real-Time Pricing Fields
There are two different adjustment mechanisms depending on the type of field you use. This is a common source of confusion.
| Field Type | Example Fields | Adjustment Control |
|---|---|---|
| TR. Fields (fundamental/reference) | TR.CLOSEPRICE, TR.OPENPRICE | Use inline parameter: TR.CLOSEPRICE(Adjusted=0) or (Adjusted=1) |
| Real-time pricing fields (non-TR.) | TRDPRC_1, HIGH_1, LOW_1, OPEN_PRC | Use the adjustments parameter in get_history() |
For real-time pricing fields, the adjustments parameter accepts a list of correction types:
| Value | Meaning |
| CCH | Capital Change (splits, bonus issues) |
| CRE | Currency Redenomination |
| RTS | LSEG TimeSeries adjustment (price + volume) |
| RPO | LSEG Price Only adjustment (price only, not volume) |
| exchangeCorrection | Exchange-level corrections |
| manualCorrection | Analyst-made corrections |
| unadjusted | No adjustments applied |
# Real-time pricing fields - adjustments parameter
# This applies CORAX (Corporate Actions) adjustments to non-TR fields
price_rt_adjusted = ld.get_history(
universe=['AAPL.O'],
fields=['TRDPRC_1', 'HIGH_1', 'LOW_1', 'OPEN_PRC', 'ACVOL_UNS'],
adjustments=['exchangeCorrection', 'manualCorrection', 'CCH', 'CRE', 'RPO', 'RTS'],
start='2020-08-25',
end='2020-09-04'
)
print("Real-time pricing fields WITH adjustments (around 2020 split):")
display(price_rt_adjusted)
# Same fields WITHOUT adjustments — raw traded prices
price_rt_unadjusted = ld.get_history(
universe=['AAPL.O'],
fields=['TRDPRC_1', 'HIGH_1', 'LOW_1', 'OPEN_PRC', 'ACVOL_UNS'],
adjustments=['unadjusted'],
start='2020-08-25',
end='2020-09-04'
)
print("Real-time pricing fields WITHOUT adjustments (raw):")
display(price_rt_unadjusted)
# Calculate daily returns on unadjusted prices
price_unadj_clean = price_unadj.dropna() # variable from section 07
price_unadj_clean['Daily Return'] = price_unadj_clean['Close Price'].pct_change()
# Flag days with >30% absolute move (likely corporate actions, not market events)
threshold = 0.30
discontinuities = price_unadj_clean[
price_unadj_clean['Daily Return'].abs() > threshold
].copy()
print(f"Detected {len(discontinuities)} price discontinuities exceeding {threshold:.0%} in unadjusted data:\n")
display(discontinuities[['Close Price', 'Daily Return']])
These detected dates should align with the split events retrieved in Section 6. This technique is useful for data quality checks - if discontinuities appear that do not correspond to known corporate actions, they may indicate data errors.
dividends = ld.get_data(
universe=['INFY.NS'],
fields=[
'TR.DivAnnouncementDate', # When the dividend was announced
'TR.DivType', # Interim, Final, Special
'TR.DivPaymentType', # Cash, Stock, etc.
'TR.DividendFrequency', # Annual, Semi-annual, Quarterly
'TR.DivExDate',
'TR.DivRecordDate',
'TR.DivPayDate',
'TR.DivAdjustedGross', # Gross dividend amount (adjusted for splits)
'TR.DivAdjustedNet', # Net dividend amount (after withholding)
'TR.DivOriginalCurr' # Currency of dividend
],
parameters={
'SDate': '1950-01-01',
'EDate': '2026-05-31',
'CAEventType': 'SDI' # SDI = Standard Dividend
},
header_type=ld.HeaderType.NAME # Column headers match field names
)
print(f"Total dividend events: {len(dividends)}")
display(dividends.head(10))
display(dividends.tail(5))
# Prepare data
div_viz = dividends.dropna(subset=['TR.DIVPAYDATE', 'TR.DIVADJUSTEDGROSS', 'TR.DIVTYPE']).copy()
div_viz['TR.DIVPAYDATE'] = pd.to_datetime(div_viz['TR.DIVPAYDATE'])
div_viz['YEAR'] = div_viz['TR.DIVPAYDATE'].dt.year
div_viz['MONTH_NAME'] = div_viz['TR.DIVPAYDATE'].dt.strftime('%b')
MONTH_ORDER = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
MONTH_TO_X = {m: i for i, m in enumerate(MONTH_ORDER)}
div_viz['MONTH_X'] = div_viz['MONTH_NAME'].map(MONTH_TO_X)
# Bubble sizing
SIZE_SCALE = 30
MIN_SIZE = 30
div_viz['BUBBLE_SIZE'] = div_viz['TR.DIVADJUSTEDGROSS'] * SIZE_SCALE + MIN_SIZE
# Color by dividend type
div_types = div_viz['TR.DIVTYPE'].unique()
COLORS = plt.cm.Set2.colors
COLOR_MAP = {t: COLORS[i % len(COLORS)] for i, t in enumerate(div_types)}
# Plot
fig, ax = plt.subplots(figsize=(14, 11))
for dtype, group in div_viz.groupby('TR.DIVTYPE'):
ax.scatter(
group['MONTH_X'],
group['YEAR'],
s=group['BUBBLE_SIZE'],
color=COLOR_MAP[dtype],
alpha=0.7,
edgecolors='black',
linewidths=0.4,
label=dtype
)
ax.set_xticks(range(len(MONTH_ORDER)))
ax.set_xticklabels(MONTH_ORDER)
ax.set_yticks(sorted(div_viz['YEAR'].unique()))
ax.invert_yaxis()
ax.set_xlabel('Payment Month')
ax.set_ylabel('Year')
ax.set_title('INFY.NS — Dividend History (Month vs. Year)', fontsize=14, fontweight='bold')
ax.grid(axis='y', linestyle=':', linewidth=0.6, alpha=0.4)
ax.grid(axis='x', linestyle='--', linewidth=0.4, alpha=0.2)
leg = ax.legend(title='Dividend Type', bbox_to_anchor=(1.02, 1), loc='upper left')
for handle in leg.legend_handles:
handle.set_sizes([80])
plt.tight_layout(rect=[0, 0, 0.85, 1])
plt.show()
9.2 - Annualised Dividend
To compare companies better & calculate yields, dividend periods need to be standardized. This is where annualized dividend becomes useful. LSEG provides annualized dividends, calculated using the following four methodologies:
| Code | Full Form | Type | Method |
|---|---|---|---|
| PMD | Primary Market Default | Auto | Uses market/exchange default |
| IAD | Indicated Annual Dividend | Forward-looking | Latest dividend x frequency |
| TTM | Trailing Twelve Months | Backward-looking | Sum of last 12 months dividends |
| LFY | Latest Financial Year | Official | Dividend from last financial year report |
# Fetching dividend data with different annualization methods
ld.get_data(
universe=['INFY.NS','TCS.NS','WIPR.NS',],
fields=['TR.AnnDivPrimaryAnnTaxStatus',
'TR.AnnDivPrimaryAnnMethod',
'TR.AnnDivDivCurr',
'TR.ClosePrice',
'(TR.AnnDivAdjustedGross/TR.ClosePrice)*100', # Dividend yield using default method
'TR.AnnDivAdjustedGross', # default method
'TR.AnnDivAdjustedGross(ANN=PMD)', # Adj Gross dividend annualized by default method
'TR.AnnDivAdjustedGross(ANN=IAD)', # Forward looking adj annualized dividend
'TR.AnnDivAdjustedGross(ANN=TTM)', # Trailing twelve months adj annualized dividend
'TR.AnnDivAdjustedGross(ANN=LFY)' # Last fiscal year adj annualized dividend
],
header_type = ld.HeaderType.NAME # to differentiate b/w fields with same name.
)
💡TIP:
Instead of retrieving multiple fields and calculating metrics separately, you can embed calculations directly within your API query. This approach makes your requests more efficient and keeps your logic centralized.
10. Using Field-Level Parameters
The LSEG Data Library supports inline parameters on individual fields. This allows a single generic field to serve multiple purposes depending on the parameter passed - reducing the number of distinct field names you need to remember.
For example, TR.DivDate with the DateType parameter can return different dates:
- DateType=PD → Payment Date
- DateType=RD → Record Date
- DateType=ED → Ex-Date
- DateType=AD → Announcement Date
# Using parameterized fields instead of separate field names
div_dates = ld.get_data(
universe=['INFY.NS'],
fields=[
'TR.DivDate(DateType=AD)', # Announcement Date
'TR.DivDate(DateType=ED)', # Ex-Date
'TR.DivDate(DateType=RD)', # Record Date
'TR.DivDate(DateType=PD)' # Payment Date
],
parameters={
'SDate': '2024-01-01',
'EDate': '2026-05-31'
},
header_type=ld.HeaderType.NAME
)
display(div_dates)
💡TIP:
Each field listed in the Data Item Browser (DIB) shows its supported parameters. Field-level parameters (applied inline) override global parameters (applied in the parameters dict).
11. Applying Adjustment Factors Manually
Section 7 showed that LSEG delivers adjusted prices by default. Some workflows require storing unadjusted prices - for audit trails, regulatory reporting, or exchange settlement matching - and computing the adjusted series on demand.
LSEG provides two fields for adjustment factors:
| Fields | Returns |
|---|---|
| TR.CAAdjustmentFactor | The per-event adjustment factor for each historical capital change event |
| TR.AdjmtFactorAdjustmentFactor (legacy field) | The factor from the most recent split event only |
Use TR.CAAdjustmentFactor when you need the full event history.
How factors work:
Each factor is event-specific and is applied backward - to prices before the corporate action date. Prices after the event are already at the post-event level and need no modification.
Different historical periods need different adjustments, depending on how many events occurred after that date:
Formula: Adjusted Price = Unadjusted Price × (product of factors for all events after that date)
Diagram:
─── Zone 1 ───|── Split A ──|─── Zone 2 ───|── Split B ──|─── Zone 3 ───
| f_A = 0.5 | | f_B = 0.25 |
f_A × f_B applied | f_B applied | No adjustment
to Zone 1 prices | to Zone 2 prices | (already current)
# Fetch the adjustment factor for each historical capital change event
adj_factors = ld.get_data(
universe=['AAPL.O'],
fields=[
'TR.CACorpActEventType',
'TR.CAEffectiveDate',
'TR.CAAdjustmentFactor',
'TR.CAAdjustmentType', # CCH or RPO (LPO is the new renamed output)
'TR.CATermsOldShares',
'TR.CATermsNewShares'
],
parameters={
'SDate': '1980-01-01',
'EDate': '2026-05-31',
'CAEventType': 'SSP;SIS'
}
)
print("AAPL.O — per-event adjustment factors:")
display(adj_factors)
# Verify: apply the 2020 4-for-1 split factor to unadjusted prices
# Ex-Date: 2020-08-31. Factor = 0.25 (from TR.CAAdjustmentFactor output above)
#
# Prices BEFORE 2020-08-31: multiply by 0.25 (one split is "ahead" of them)
# Prices ON/AFTER 2020-08-31: no adjustment needed (already at post-split level)
split_date = pd.Timestamp('2020-08-31')
split_factor = 0.25 # Read from adj_factors output above
window_unadj = ld.get_history(
universe=['AAPL.O'],
fields=['TR.CLOSEPRICE(Adjusted=0)'],
interval='1D',
start='2020-08-25',
end='2020-09-04'
)
window_adj = ld.get_history(
universe=['AAPL.O'],
fields=['TR.CLOSEPRICE(Adjusted=1)'],
interval='1D',
start='2020-08-25',
end='2020-09-04'
)
comparison = pd.DataFrame({
'Unadjusted': window_unadj['Close Price'],
'LSEG Adjusted': window_adj['Close Price']
})
comparison['Manual Adjusted'] = comparison.apply(
lambda row: row['Unadjusted'] * split_factor if row.name < split_date else row['Unadjusted'],
axis=1
)
comparison['Difference'] = (comparison['LSEG Adjusted'] - comparison['Manual Adjusted']).abs()
print("Verification: manually applied factor vs. LSEG adjusted series")
print("Difference should be near zero for pre-split dates, exactly zero for post-split.\n")
display(comparison.round(4))
Why 0.25 and not 1/224? The factor 1/224 accounts for all five AAPL splits combined (2×2×2×7×4). It only applies to prices from before 1987. Prices from 2020 only have one split "ahead" of them (the 4-for-1), so the correct factor is 0.25. Always use the factor that matches how many splits are after your price date.
12. Handling RIC Changes with PermIDs
Certain corporate actions - name changes, mergers, de-mergers, spin-offs - can result in RIC (Reuters Instrument Code) changes. A workflow hardcoded to a specific RIC will break when this happens.
PermIDs (Permanent Identifiers) provide a stable reference that persists across RIC changes. They are assigned to organisations, instruments, and individuals, and do not change regardless of corporate events.
Example: Meta Platforms (formerly Facebook)
When Facebook rebranded to Meta in 2021, the RIC changed from FB.OQ to META.OQ. Any pipeline referencing FB.OQ would stop receiving data.
# Step 1: Get the Organisation PermID for the current RIC
org_permid = ld.get_data(
universe=['META.OQ'],
fields=['TR.OrganizationID']
)
print("Organisation PermID for META.OQ:")
display(org_permid)
# Step 2: Use the PermID to find all historical RICs for the organisation
# Replace the PermID below with the value returned above
permid_value = '4297297477' # Meta Platforms Inc.
all_rics = ld.get_history(
universe=[permid_value],
fields=['TR.RIC'],
start='2000-01-01',
end='2026-05-31'
)
print(f"All RICs associated with PermID {permid_value}:")
display(all_rics)
Best Practice:
PermID-Based Pipelines
For production workflows, store the Organisation PermID as your primary key rather than the RIC. If there are multiple venues or forms, then using RIC is ideal.
13. Broader Corporate Events (TR.EventType)
The TR.CACorpActEventType field used above covers capital change events specifically - splits, bonus issues, and similar. But companies generate a much wider range of announcements: earnings releases, shareholder meetings, executive changes, M&A activity, and more.
For this broader scope, use the TR.EventType family of fields with the EventType parameter.
# Fetch all corporate events for BHP over the last year
events = ld.get_data(
universe=['BHP.AX'],
fields=[
'TR.EventStartDate',
'TR.EventType',
'TR.EventTitle',
'TR.EventEndDate'
],
parameters={
'SDate': '-1Y',
'EDate': '1D',
'EventType': 'ALL' # Returns all event types: earnings, M&A, dividends, meetings, etc.
}
)
display(events)
The EventType parameter accepts:
- ALL - all event types
- Specific types like CV for company visits, or RES for earings release etc.
This is useful when building event calendars, screening for upcoming catalysts, or monitoring corporate activity across a watchlist.
💡TIP:
When querying events for large universes (e.g., all ASX-listed companies), batch your requests into chunks to avoid timeouts:
14. Event Metadata: Status, IDs, and Forward-Looking Events
14.1 Event Status (Confirmed vs Estimated)
Corporate action dates can be confirmed (company-announced) or estimated. The field TR.EventStatus indicates this:
| Value | Meaning |
|---|---|
| False | Date is confirmed by the company |
| True | Date is estimated |
This matters for forward-looking workflows - estimated dates shift frequently and should be treated as provisional.
Each corporate action has a unique identifier (TR.EventEventID). Use it to:
- Deduplicate events when polling for updates
- Track lifecycle changes (announced → confirmed → completed → rescinded)
- Link related records across separate API calls
A corporate action can be announced and later cancelled. The field TR.CAIsRescinded returns True for withdrawn events. Always filter these out before applying adjustments - a rescinded split never took effect.
# Fetch upcoming dividend events with status and event ID
upcoming_divs = ld.get_data(
universe=['AAPL.O'],
fields=[
'TR.CAEventID', # Unique event identifier
'TR.DivExDate',
'TR.DivPayDate',
'TR.DivAdjustedGross',
'TR.DivType',
'TR.CADateStatus', # True = estimated, False = confirmed
'TR.CAIsRescinded'
],
parameters={
'SDate': '0D', # From today
'EDate': '1Y', # Looking 1 year ahead
'CAEventType': 'SDI'
}
)
print("Upcoming dividends (with confirmation status and event ID):")
display(upcoming_divs)
# Historical events with IDs — useful for tracking and deduplication
hist_events = ld.get_data(
universe=['AAPL.O'],
fields=[
'TR.CAEventID',
'TR.CACorpActEventType',
'TR.CAEffectiveDate',
'TR.CAAnnouncementDate',
'TR.CADateStatus',
'TR.CAIsRescinded'
],
parameters={
'SDate': '2014-01-01',
'EDate': '2026-05-31',
'CAEventType': 'SSP;SIS;SDI'
}
)
print("Historical events with IDs and status:")
display(hist_events.head(10))
Important: Do not combine forward-looking and historical date ranges in the same request. The API behaves differently for estimated future events vs. confirmed past events. Query them separately.
For cross-border portfolios, the dividend currency matters for tax and FX conversion. The field TR.DivOriginalCurr returns the ISO currency code of the dividend payment. The difference between TR.DivAdjustedGross and TR.DivAdjustedNet represents withholding tax - relevant for international holdings.
15 - Summary & Resources
| Topic | Key Point |
|---|---|
| Adjusted by default | LSEG delivers adjusted prices by default - use Adjusted=0 only when you need raw traded prices |
| Adjustment factors | Per-event factors from TR.CAAdjustmentFactor; apply backward to prices before the event date |
| Adjustment Type | The TR.CAAdjustmentType field indicates whether the adjustment type is CCH or RPO. |
| Zone-specific | The factor for a price depends on how many splits occured after that date - not one cumulative number for all history |
| CAEventType | Filters capital change events (splits, bonus issues, spin-offs). Verify codes in DIB |
| TR.EventType | Covers broader corporate events: earnings, M&A, meetings, dividends, executive changes |
| Event Status | TR.EventStatus distinguishes confirmed vs estimated dates- critical for forward-looking workflows |
| Event ID | TR.EventEventID enables deduplication and lifecycle tracking across refreshes |
| Field parameters | Inline parameters (e.g., DateType=PD) reduce complexity by reusing generic fields |
| RIC stability | Use PermIDs as your primary key to survive RIC changes |
| Batching | For large universes, split requests into chunks of ~50 RICs to avoid timeouts |
| Data validation | Cross-check detected price discontinuities against known corporate action dates |
| Resource | URL |
|---|---|
| LSEG Data Library for Python | LSEG Data Library for Python | Devportal |
| Data Item Browser (DIB) | Available in LSEG Workspace - field discovery and parameter reference |
| Developer Community | https://community.developers.lseg.com/ |
| DataScope Select (DSS) | LSEG DataScope Select - REST API | Devportal |
| LSEG Developer Portal | https://developers.lseg.com/ |
If you have questions, find an issue, or want to suggest improvements:
- Post on the Developer Community Q&A
- Contact your LSEG representative for access and licensing queries
- Register or Log in to applaud this article
- Let the author know how much this article helped you