Analysis of Regulatory Change Regarding Equity Ownership and Related Analysis
Published
April 21, 2026
Part 0: Rationale for this Project
So the idea for this project came as part of the interview process for a company, where i was tasked to extract tables from a pdf and analyse the data in the table. The pdf included data from Indonesia’s Financial Services Authority which lowers the public reporting threshold from 5% to 1%.
Part 1: Background of the Data in the PDF
In a significant move to enhance market transparency, Indonesia’s Financial Services Authority (Otoritas Jasa Keuangan or OJK) introduced a pivotal regulatory change in early 2026. Through the OJK Board of Commissioners Decree No. 1/KDK.04/2026 and the subsequent OJK Stipulation Letter No. S-20/PM.1/2026 dated February 26, 2026, the regulator officially designated the Indonesian Central Securities Depository (KSEI) and the Indonesia Stock Exchange (IDX/BEI) as the primary public providers of corporate share ownership data.
The most impactful element of this mandate is the lowering of the public reporting threshold. Historically, Indonesian capital market regulations typically required the disclosure of “major” shareholders who held 5% or more of a listed company. Under the new 2026 mandate, KSEI is required to report the total share ownership of any investor holding above 1% (one percent) of a public company’s shares.
This data aggregates holdings across two primary systems:
C-BEST System: Captures scripless (paperless) shares from Securities Sub-Accounts (SRE), including both accounts connected to a Single Investor Identification (SID) and those that are not.
eBAE System: Captures traditional scrip (paper) shares administered by Securities Administration Bureaus (BAE) or in-house issuers, holding dedicated accounts for investors yet to be assigned an SID.
By lowering the threshold to 1%, the OJK is shining a light on the “hidden middle” of equity ownership—revealing mid-sized institutional allocations, foreign fund footprints, and the holdings of high-net-worth individuals that previously flew under the radar.
Section 1: The “Whale Effect”: Headcount vs. Capital Weight
A superficial look at the KSEI data might suggest that Indonesian equities are overwhelmingly controlled by local investors. By pure headcount, domestic entities make up 71.0% of all major block positions (holdings over 1%). However, headcount is a poor proxy for market power.
When we shift our metric from the number of positions to the actual volume of shares controlled, a stark discrepancy emerges. Foreign investors, despite accounting for only 29.0% of the headcount, control a disproportionate 40.17% of the total share volume. This “Whale Effect” highlights a highly concentrated offshore presence, where a smaller pool of foreign funds and institutions wield massive directional influence over Indonesian market capitalization.
Show Data Processing Code
import pandas as pdimport plotly.graph_objects as godef analyze_whale_discrepancy(df):# --- 1. CLEANING & CLASSIFICATION --- df['TOTAL_HOLDING_SHARES'] = ( df['TOTAL_HOLDING_SHARES'] .astype(str) .str.replace('.', '', regex=False) .str.replace(',', '.', regex=False) ) df['TOTAL_HOLDING_SHARES'] = pd.to_numeric(df['TOTAL_HOLDING_SHARES'], errors='coerce') df['DOMICILE_CLEAN'] = df['DOMICILE'].fillna('UNKNOWN').astype(str).str.upper().str.strip() df['GEO_CLASSIFICATION'] = df['DOMICILE_CLEAN'].apply(lambda x: 'Domestic (Indonesia)'if'INDONESIA'in x else'Foreign' )# --- 2. CALCULATE PROPORTIONS ---# Percentage of Total Positions (Count) count_prop = df['GEO_CLASSIFICATION'].value_counts(normalize=True) *100# Percentage of Total Share Volume vol_sum = df.groupby('GEO_CLASSIFICATION')['TOTAL_HOLDING_SHARES'].sum() vol_prop = (vol_sum / vol_sum.sum()) *100# Ensure indexes align for plotting categories = ['Domestic (Indonesia)', 'Foreign'] counts = [count_prop.get(cat, 0) for cat in categories] volumes = [vol_prop.get(cat, 0) for cat in categories]# --- 3. PLOT GROUPED BAR CHART --- fig = go.Figure(data=[ go.Bar( name='% of Total Positions (Headcount)', x=categories, y=counts, text=[f"{val:.1f}%"for val in counts], textposition='auto', marker_color='#3A5FCD'# Royal Blue ), go.Bar( name='% of Total Share Volume (Wealth)', x=categories, y=volumes, text=[f"{val:.1f}%"for val in volumes], textposition='auto', marker_color='#FF8C00'# Dark Orange ) ])# --- 4. CUSTOMIZE LAYOUT & FIX MARGINS --- fig.update_layout(# Pin title to the top center title=dict( text='The Foreign Whale Effect: Investor Headcount vs. Actual Volume Controlled', y=0.98, yanchor='top', x=0.5, xanchor='center' ), barmode='group', xaxis_title='Investor Domicile', yaxis_title='Percentage (%)', yaxis=dict(range=[0, 100]), # Standardize y-axis to 100%# Drop legend slightly below the title legend=dict( orientation="h", yanchor="top", y=0.88, xanchor="center", x=0.5 ),# Increase top margin to make room for both margin=dict(t=110, b=40, l=40, r=40), template='plotly_white' )# --- 5. HIDE PLOTLY BUTTONS --- fig.show(config={'displayModeBar': False})# Run the updated visualizationdf = pd.read_csv("cleaned_ownership_data.csv")analyze_whale_discrepancy(df)
Section 2: Mapping the Offshore Capital Pipeline
To understand the nature of this foreign capital, we must look at where these offshore whales are domiciled. Because traditional choropleth maps struggle to represent financial havens geographically, we utilize a high-resolution bubble map to visualize capital origin by the number of holdings.
While Indonesia naturally anchors the map, the visualization immediately exposes the primary conduits of offshore capital. Singapore emerges as the dominant regional hub for Indonesian equity investment. Further afield, traditional tax and structuring havens, such as the British Virgin Islands, show significant financial density.
This geographic distribution underscores how much of the “foreign” capital might actually be routed through institutional structuring rather than pure international retail investment.
Show Data Processing Code
import plotly.express as pximport pandas as pddef analyze_ownership_bubble_map(df): df['DOMICILE_CLEAN'] = df['DOMICILE'].fillna('UNKNOWN').astype(str).str.upper().str.strip()# Count holdings (including Indonesia this time for scale) country_counts = df['DOMICILE_CLEAN'].value_counts().reset_index() country_counts.columns = ['Country', 'Number of Holdings']# Fix Plotly mapping for financial havens country_counts['Country'] = country_counts['Country'].replace({'VIRGIN ISLANDS, BRITISH': 'British Virgin Islands','UNITED STATES': 'United States','UNKNOWN': 'Unknown'# Plotly will ignore this, which is fine for the map })# Use a Bubble Map instead of Choropleth fig = px.scatter_geo( country_counts, locations="Country", locationmode="country names", size="Number of Holdings", # The size of the bubble dictates the volume color="Number of Holdings", hover_name="Country", projection="natural earth", title="Global Footprint: Origin of Capital by Number of Holdings", size_max=60, # Caps the maximum size so Indonesia doesn't cover all of Asia color_continuous_scale=px.colors.sequential.Plasma ) fig.update_layout( geo=dict(showframe=False, showcoastlines=True, landcolor='lightgray', resolution=50), width=None, height=650, margin=dict(l=20, r=20, t=50, b=20) ) fig.show()analyze_ownership_bubble_map(df)
Section 3: The Scrip Reality: Assessing Clearing Modernization
The 2026 OJK mandate draws a clear distinction between scripless (C-BEST) and physical scrip (eBAE) holdings. While the Indonesian market has largely modernized, physical paper shares still present a structural friction point for rapid clearing and settlement.
To identify the true laggards in modernization, we cannot rely solely on the ratio of scrip shares, nor can we look purely at nominal volume, which is easily skewed by arbitrary stock splits. By plotting the physical scrip percentage against the logarithmic scale of total block shares issued, we isolate the systemic risks.
Companies clustered in the top-right quadrant represent massive market footprints that are still heavily bogged down by legacy paper structures, posing potential liquidity bottlenecks.
Show Data Processing Code
import plotly.express as pximport pandas as pddef analyze_scrip_reality(df):# Clean the numeric data df['HOLDINGS_SCRIP'] = df['HOLDINGS_SCRIP'].fillna(0).astype(str).str.replace('.', '', regex=False).str.replace(',', '.', regex=False) df['HOLDINGS_SCRIP'] = pd.to_numeric(df['HOLDINGS_SCRIP'], errors='coerce') df['TOTAL_HOLDING_SHARES'] = df['TOTAL_HOLDING_SHARES'].astype(str).str.replace('.', '', regex=False).str.replace(',', '.', regex=False) df['TOTAL_HOLDING_SHARES'] = pd.to_numeric(df['TOTAL_HOLDING_SHARES'], errors='coerce')# Aggregate by company issuer_scrip = df.groupby(['SHARE_CODE', 'ISSUER_NAME']).agg( TOTAL_SCRIP=('HOLDINGS_SCRIP', 'sum'), TOTAL_SHARES=('TOTAL_HOLDING_SHARES', 'sum') ).reset_index()# Calculate True Ratio issuer_scrip['SCRIP_PERCENTAGE'] = (issuer_scrip['TOTAL_SCRIP'] / issuer_scrip['TOTAL_SHARES']) *100 issuer_scrip['SCRIP_PERCENTAGE'] = issuer_scrip['SCRIP_PERCENTAGE'].fillna(0)# Filter out fully modernized companies (0% scrip) legacy_companies = issuer_scrip[issuer_scrip['SCRIP_PERCENTAGE'] >0] fig = px.scatter( legacy_companies, x='TOTAL_SHARES', y='SCRIP_PERCENTAGE', hover_name='ISSUER_NAME', hover_data=['SHARE_CODE', 'TOTAL_SCRIP'], log_x=True, # Applies a logarithmic scale to account for arbitrary share splits title="The Scrip Reality: Proportion of Physical Shares vs. Scale of Holdings", labels={'TOTAL_SHARES': 'Total Block Shares Issued (Log Scale)','SCRIP_PERCENTAGE': 'Physical Scrip Percentage (%)' }, color='SCRIP_PERCENTAGE', color_continuous_scale='Reds' )# Add styling fig.update_traces(marker=dict(size=10, opacity=0.7, line=dict(width=1, color='DarkRed'))) fig.show()analyze_scrip_reality(df)
Section 4: Whale Fragmentation: Single Dominance vs. Institutional Crowds
Not all concentrated ownership structures are built the same. A company with 60% of its float locked up by a single parent conglomerate behaves very differently in the market than a company with 60% of its float locked up across twenty different mid-sized institutional funds.
By plotting Total Block Ownership against the Number of >1% Investors, we map out the fragmentation of market power. The 50% majority control threshold provides a critical visual baseline. Companies positioned high on the Y-axis but far to the left on the X-axis represent classic conglomerate dominance (a single whale). Conversely, companies drifting further to the right reveal a crowded trade of institutional players, where coordination is low but total block lock-up remains high.
Show Data Processing Code
import plotly.express as pximport pandas as pddef analyze_whale_fragmentation(df):# Group by issuer to find total concentration AND the count of >1% investors fragmentation = df.groupby(['SHARE_CODE', 'ISSUER_NAME']).agg( TOTAL_BLOCK_OWNERSHIP=('PERCENTAGE', 'sum'), INVESTOR_COUNT=('INVESTOR_NAME', 'count') # How many entities make up that block? ).reset_index()# Cap total ownership at 100% just in case of data rounding errors fragmentation['TOTAL_BLOCK_OWNERSHIP'] = fragmentation['TOTAL_BLOCK_OWNERSHIP'].clip(upper=100) fig = px.scatter( fragmentation, x='INVESTOR_COUNT', y='TOTAL_BLOCK_OWNERSHIP', hover_name='ISSUER_NAME', hover_data=['SHARE_CODE'], title='Ownership Structure: Single Whale vs. Fragmented Funds', labels={'TOTAL_BLOCK_OWNERSHIP': 'Total Block Ownership (%)', 'INVESTOR_COUNT': 'Number of >1% Investors' }, color='TOTAL_BLOCK_OWNERSHIP', color_continuous_scale=px.colors.sequential.Viridis )# Add styling fig.update_traces(marker=dict(size=9, opacity=0.7, line=dict(width=1, color='DarkSlateGrey')))# Add a horizontal line at 50% for majority control visual threshold fig.add_hline(y=50, line_dash="dash", line_color="red", annotation_text="Majority Control Threshold") fig.show()analyze_whale_fragmentation(df)
Section 5: Unmasking the Offshore Tycoons
Finally, the strict dichotomy between “Domestic” and “Foreign” capital is often blurred by high-net-worth individuals utilizing offshore vehicles for their domestic investments. By cross-referencing prominent Indonesian conglomerate and tycoon surnames against non-Indonesian domiciles, we can identify “hidden” domestic whales.
The interactive table below isolates offshore entities controlled by major domestic business families. Use the column filters to search for specific tickers or surnames to track how local market power is projected from abroad.
Loading ITables v2.7.3 from the internet...
(need help?)
Section 6: The “Shadow Whale” Phenomenon: Caught in the Regulatory Net
One of the most fascinating psychological elements in financial markets is “threshold bunching.” Under the legacy regulatory framework, any investor holding 5% or more of a listed company’s shares was legally required to disclose their identity and transaction history to the public. As a result, a sophisticated class of investors—which we will call “Shadow Whales”—deliberately optimized their portfolios to sit at 4.98% or 4.99%. This allowed them to maximize their capital allocation and voting influence while remaining legally invisible to retail traders and rival institutions.
When the OJK suddenly lowered the mandatory reporting threshold to 1% in early 2026, it acted as a sudden dragnet. Because the implementation occurred rapidly, these Shadow Whales lacked the time (or the market liquidity) to quietly dump or restructure millions of shares without crashing the stock prices.
By plotting the distribution of all newly revealed holdings between 1.0% and 4.99%, we can clearly see the impact. The histogram reveals a distinct, unnatural cluster of investors densely packed between 4.5% and 4.99%. Our analysis exposed dozens of these sophisticated positions that were “hugging the line.” Overnight, billions of shares previously operating in the dark were forced into the light, drastically changing the perceived ownership structure of numerous mid-cap and large-cap Indonesian equities.
Show Data Processing Code
import pandas as pdimport plotly.express as pxdef analyze_shadow_whales(df):# --- 1. CLEANING --- df['PERCENTAGE'] = pd.to_numeric( df['PERCENTAGE'].astype(str).str.replace(',', '.'), errors='coerce' ) df['TOTAL_HOLDING_SHARES'] = pd.to_numeric( df['TOTAL_HOLDING_SHARES'].astype(str).str.replace('.', '', regex=False).str.replace(',', '.', regex=False), errors='coerce' )# --- 2. ISOLATE THE NEWLY REVEALED BRACKET (1% to <5%) --- exposed_bracket = df[(df['PERCENTAGE'] >=1.0) & (df['PERCENTAGE'] <5.0)].copy()# Flag the "Shadow Whales" (those hugging the old 5% line, e.g., 4.5% to 4.99%) exposed_bracket['IS_SHADOW_WHALE'] = exposed_bracket['PERCENTAGE'] >=4.5# --- 3. PLOT THE DISTRIBUTION --- fig = px.histogram( exposed_bracket, x='PERCENTAGE', nbins=40, # Creates bins of roughly 0.1% each color='IS_SHADOW_WHALE', color_discrete_map={True: '#B22222', False: '#708090'}, # Firebrick Red vs Slate Gray title='Exposing the "Shadow Whales": Distribution of Newly Revealed 1-4.99% Holdings', hover_data=['ISSUER_NAME', 'INVESTOR_NAME', 'TOTAL_HOLDING_SHARES'] )# Calculate stats for annotation shadow_count = exposed_bracket['IS_SHADOW_WHALE'].sum() shadow_volume = exposed_bracket.loc[exposed_bracket['IS_SHADOW_WHALE'], 'TOTAL_HOLDING_SHARES'].sum()# Make it look good fig.update_layout( barmode='stack', xaxis_title='Ownership Percentage (%)', yaxis_title='Number of Investor Positions', showlegend=False, template='plotly_white' )# Add annotation pointing to the 4.5-4.99% cluster fig.add_annotation( x=4.8, y=shadow_count +10, text=f"<b>{shadow_count} Shadow Whales</b><br>Caught hugging the<br>old 5% radar", showarrow=True, arrowhead=2, arrowcolor="#B22222", font=dict(color="#B22222"), ax=-60, ay=-40 ) fig.show(config={'displayModeBar': False})# Print summary statisticsprint("--- THE SHADOW WHALE IMPACT ---")print(f"Total positions exposed between 1% and 4.99%: {len(exposed_bracket)}")print(f"Positions caught specifically between 4.5% and 4.99%: {shadow_count}")print(f"Total share volume exposed in the 4.5-4.99% bracket: {shadow_volume:,.0f} shares")# Run the analysisdf = pd.read_csv("cleaned_ownership_data.csv")analyze_shadow_whales(df)
--- THE SHADOW WHALE IMPACT ---
Total positions exposed between 1% and 4.99%: 4942
Positions caught specifically between 4.5% and 4.99%: 547
Total share volume exposed in the 4.5-4.99% bracket: 243,659,455,781 shares