---
updatedAt: 2026-08-14T20:05:59.000Z
agentTools:
  projectIndex: https://docs.dinari.com/llms.txt
---

# Stock Splits

How Dinari applies forward and reverse stock splits to dShares

Dinari processes both forward and reverse stock splits through a single, unified pipeline. The only differences are the ratio's direction and how leftover fractions are handled.

```mermaid
graph LR
    A[Trading Halts] --> B[Token Rebased] --> C[Balances Verified] --> D[Trading Resumes]
```

***

## Reading a split

A split is described by two fields:

| Field        | Meaning                |
| :----------- | :--------------------- |
| `split_from` | Shares you hold before |
| `split_to`   | Shares you hold after  |

A **10-for-1 forward split** has `split_from = 1`, `split_to = 10`. A **1-for-40 reverse split** has `split_from = 40`, `split_to = 1`.

The direction is simply which value is larger:

```typescript TypeScript
const isReverse = split.split_from > split.split_to;
const multiplier = split.split_to / split.split_from; // 10 forward, 0.025 reverse
```
```python Python
is_reverse = split.split_from > split.split_to
multiplier = split.split_to / split.split_from   # 10.0 forward, 0.025 reverse
```

### Dates and status

| Field          | Meaning                                  |
| :------------- | :--------------------------------------- |
| `record_date`  | Date a holder must hold to be entitled   |
| `payable_date` | Trading halts at the **end** of this day |
| `ex_date`      | Trading resumes at split-adjusted prices |
| `status`       | `PENDING` → `IN_PROGRESS` → `COMPLETE`   |

Splits are usually scheduled over a weekend to minimize disruption. While a split is `IN_PROGRESS`, the stock is not available for trading.

***

## Forward splits

### What happens

1. **Trading halts.** New orders are rejected, active orders are cancelled, and the dShare™ token is paused.
2. **The token is rebased** to match the split ratio.
3. **Dinari verifies balances** across all systems and confirms the underlying stock changes are reflected.
4. **Trading resumes.** New orders are accepted and the token is unpaused.
   Balances scale automatically. Dinari adjusts the token's balance-per-share once rather than minting to each holder individually, so there is no per-wallet transaction to watch for — read the new balance from the portfolio endpoint after `COMPLETE`.

```mermaid
sequenceDiagram
    autonumber
    participant D as Dinari
    participant P as Partner
    participant U as End user
 
    Note over D: 10-for-1 split announced — status = PENDING
    P->>D: Poll for splits
    D-->>P: split_from 1, split_to 10,<br/>payable_date, ex_date
    P->>U: Notify: split scheduled
 
    Note over D: End of payable_date — status = IN_PROGRESS
    D->>D: Halt trading, cancel open orders,<br/>rebase the token
    P->>P: Pause trading in your UI
 
    Note over D: ex_date — status = COMPLETE, trading resumes
    P->>D: Read portfolio
    D-->>P: Balance x10, value unchanged
    P->>U: Update ledger, show new share count
```

***

## Reverse splits

A reverse split does the opposite: the share count falls and the price rises proportionally. A 1-for-40 reverse split turns 40 dShares into 1.

Reverse splits use the same pipeline as forward splits — halt, rebase, verify, resume. The ratio is simply inverted. The one difference that matters to your integration is fractional handling.

### Fractional handling

Because dShares are fractionable, most reverse splits produce no rounding problem: the holder ends up with fewer dShares at a higher price and the position value is unchanged.

The exception is when the post-split stock becomes **non-fractionable**. The leftover fraction cannot be represented, so it is liquidated and the holder receives the proceeds in **USD+** instead of dShares. A small enough position can end at zero shares plus a cash credit.

```mermaid
sequenceDiagram
    autonumber
    participant D as Dinari
    participant P as Partner
    participant U as End user
 
    Note over D: 1-for-40 reverse split announced — status = PENDING
    P->>D: Poll for splits
    D-->>P: split_from 40, split_to 1,<br/>payable_date, ex_date
    P->>U: Notify: reverse split scheduled
 
    Note over D: End of payable_date — status = IN_PROGRESS
    D->>D: Halt trading, cancel open orders,<br/>rebase the token
    P->>P: Pause trading in your UI
 
    alt Post-split balance remains fractionable
        D->>D: Holder keeps a smaller<br/>dShare balance
    else Post-split balance is non-fractionable
        D->>D: Burn the leftover fraction,<br/>credit USD+ of equivalent value
        D->>P: Direct notification of<br/>affected holders
    end
 
    Note over D: ex_date — status = COMPLETE, trading resumes
    P->>D: Read portfolio and cash balances
    D-->>P: New balances, any USD+ credit
    P->>U: Update ledger, show new share count<br/>and any cash in lieu
```

### Duplicate asset records

A reverse split is sometimes accompanied by a CUSIP change, which can cause the market data provider to emit a duplicate stock record under the same symbol. Dinari refreshes the asset list and merges the duplicate into the existing record, so **the stock ID your integration references stays stable**. No action is required from you.

***

## Recommended Flow

Both directions call for the same integration work.

1. **Poll for splits** daily on the stocks your users hold. Splits are scheduled well in advance.
2. **Pause trading** in your interface while `status` is `IN_PROGRESS`, and expect open orders to be cancelled.
3. **Re-read balances** once `status` is `COMPLETE` and update your ledger. For reverse splits, check cash balances too — a USD+ credit may have replaced a fraction.
4. **Show the ratio, not just the new number.** "Your 5 shares became 50 in a 10-for-1 split, value unchanged" prevents most support tickets.

***

## SDK examples

### Detect upcoming and in-progress splits

```typescript TypeScript
import Dinari from '@dinari/api-sdk';
 
const client = new Dinari({
  apiKeyID: process.env['DINARI_API_KEY_ID'],
  apiSecretKey: process.env['DINARI_API_SECRET_KEY'],
  environment: 'sandbox',
});
 
async function main() {
  const stockID = 'stock-id-here';
  const splits = await client.v2.marketData.stocks.splits.listForStock(stockID, {
    page: 1,
    page_size: 10,
  });
 
  for (const s of splits) {
    const isReverse = s.split_from > s.split_to;
    console.log(
      `${s.split_from}-for-${s.split_to} ` +
      `${isReverse ? 'reverse' : 'forward'} — ` +
      `${s.status}, ex-date ${s.ex_date}`
    );
  }
}
 
main();
```
```python
import os
from dinari_api_sdk import Dinari
 
client = Dinari(
    api_key_id=os.environ.get("DINARI_API_KEY_ID"),
    api_secret_key=os.environ.get("DINARI_API_SECRET_KEY"),
    environment="sandbox",
)
 
splits = client.v2.market_data.stocks.splits.list_for_stock(
    stock_id="stock_xxx",
    page=1,
    page_size=25,
)
 
for s in splits:
    is_reverse = s.split_from > s.split_to
    direction = "reverse" if is_reverse else "forward"
    print(f"{s.split_from}-for-{s.split_to} {direction} — "
          f"{s.status}, ex-date {s.ex_date}")
```

> **Info:** Each `StockSplit` contains `id`, `stock_id`, `split_from`, `split_to`, `record_date`, `payable_date`, `ex_date`, and `status`.

***

### Gate trading on split status

```typescript TypeScript
async function canTrade(stockID: string): Promise<[boolean, string | null]> {
  const splits = await client.v2.marketData.stocks.splits.listForStock(stockID);
 
  for (const s of splits) {
    if (s.status === 'IN_PROGRESS') {
      const direction = s.split_from > s.split_to ? 'reverse split' : 'stock split';
      return [
        false,
        `Trading is paused for a ${s.split_from}-for-${s.split_to} ` +
        `${direction}. It resumes on ${s.ex_date}.`,
      ];
    }
  }
  return [true, null];
}
 
const [allowed, message] = await canTrade('stock-id-here');
if (!allowed) {
  showBanner(message);
}
```
```python Python
def can_trade(stock_id: str) -> tuple[bool, str | None]:
    splits = client.v2.market_data.stocks.splits.list_for_stock(stock_id=stock_id)
 
    for s in splits:
        if s.status == "IN_PROGRESS":
            direction = "reverse split" if s.split_from > s.split_to else "stock split"
            return False, (
                f"Trading is paused for a {s.split_from}-for-{s.split_to} "
                f"{direction}. It resumes on {s.ex_date}."
            )
    return True, None
 
 
allowed, message = can_trade("stock_xxx")
if not allowed:
    show_banner(message)
```

***

### Reconcile balances after a split completes

```typescript TypeScript
const accountID = 'your-account-id';
 
// Snapshot before the split
const beforePortfolio = await client.v2.accounts.getPortfolio(accountID);
const before = new Map(beforePortfolio.assets.map((b) => [b.stock_id, b.amount]));
 
// ... after status flips to COMPLETE ...
 
const portfolio = await client.v2.accounts.getPortfolio(accountID);
 
for (const b of portfolio.assets) {
  const old = before.get(b.stock_id);
  if (old !== undefined && old !== b.amount) {
    console.log(`${b.symbol} on chain ${b.chain_id}: ${old} -> ${b.amount}`);
    ledger.setBalance(accountID, b.stock_id, b.amount, 'stock_split');
  }
}
 
// Reverse splits may also produce a USD+ credit in lieu of a fraction
const cash = await client.v2.accounts.getCash(accountID);
```
```python Python
ACCOUNT_ID = "account_xxx"
 
# Snapshot before the split
before = {
    b.stock_id: b.amount
    for b in client.v2.accounts.get_portfolio(account_id=ACCOUNT_ID).assets
}
 
# ... after status flips to COMPLETE ...
 
portfolio = client.v2.accounts.get_portfolio(account_id=ACCOUNT_ID)
 
for b in portfolio.assets:
    old = before.get(b.stock_id)
    if old is not None and old != b.amount:
        print(f"{b.symbol} on chain {b.chain_id}: {old} -> {b.amount}")
        ledger.set_balance(ACCOUNT_ID, b.stock_id, b.amount, reason="stock_split")
 
# Reverse splits may also produce a USD+ credit in lieu of a fraction
cash = client.v2.accounts.get_cash(account_id=ACCOUNT_ID)
```

> Each `DshareBalance` in the portfolio contains `stock_id`, `symbol`, `amount`, `chain_id`, and `token_address`.

***