> ## Documentation Index
> Fetch the complete documentation index at: https://psxdata.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Download PSX historical stock prices with Python

> Download historical OHLCV prices for any PSX-listed stock using psxdata. Works with pandas out of the box.

# Download PSX historical stock prices with Python

psxdata returns complete OHLCV history for any PSX-listed stock in a single call.

## Basic usage

```python theme={null}
import psxdata

df = psxdata.stocks("ENGRO", start="2025-01-01", end="2025-12-31")
print(df.head())
```

Output:

```text theme={null}
        date    open    high     low   close    volume  is_anomaly
0 2025-01-02  286.90  293.50  285.00  291.00   1234567       False
1 2025-01-03  291.00  295.00  289.00  293.50    987654       False
...
```

## Date filtering

`start` and `end` accept `str` (ISO format), `datetime.date`, or `None`.

```python theme={null}
# Last 90 days
from datetime import date, timedelta

end = date.today()
start = end - timedelta(days=90)
df = psxdata.stocks("LUCK", start=start, end=end)
```

## Compute returns

```python theme={null}
import psxdata

df = psxdata.stocks("HBL", start="2024-01-01")
df = df.sort_values("date").reset_index(drop=True)

df["daily_return"] = df["close"].pct_change()
df["cumulative_return"] = (1 + df["daily_return"]).cumprod() - 1

print(df[["date", "close", "daily_return", "cumulative_return"]].tail())
```

## Save to CSV or Excel

```python theme={null}
import psxdata

df = psxdata.stocks("ENGRO", start="2021-01-01")
df.to_csv("engro_history.csv", index=False)
df.to_excel("engro_history.xlsx", index=False)
```

## Fetch multiple stocks

```python theme={null}
import psxdata
import pandas as pd

tickers = ["ENGRO", "LUCK", "HBL"]
frames = {t: psxdata.stocks(t, start="2025-01-01") for t in tickers}

# Build a close-price matrix
close = pd.DataFrame({t: df.set_index("date")["close"] for t, df in frames.items()})
print(close.tail())
```

## Bypass cache

```python theme={null}
# Always fetch fresh from PSX (slower)
df = psxdata.stocks("ENGRO", cache=False)
```

## See also

* [`PSXClient.stocks()`](/sdk/reference/client) — full parameter reference
* [Stock screener guide](/sdk/guides/stock-screener) — find which stocks to analyse
