Metadata-Version: 2.4
Name: tefasfon
Version: 1.0.0
Summary: Fetches fund data from the TEFAS website.
Author-email: Uraz Akgül <urazdev@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/urazakgul/tefasfon
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: curl-cffi>=0.7
Requires-Dist: numpy
Requires-Dist: pandas
Requires-Dist: openpyxl
Requires-Dist: rich
Dynamic: license-file

# tefasfon v1.0.0

Fetches fund data from the TEFAS (Turkey Electronic Fund Trading Platform) website.

## Installation

```bash
pip install tefasfon
```

## Functions

### `get_funds`

Fetches general information by date range and fund type.

**Parameters**

| Parameter | Type | Default | Description |
|---|---|---|---|
| `fund_type` | `str` | — | `"SEC"` Securities · `"PEN"` Pension · `"ETF"` Exchange-Traded Fund · `"RE"` Real Estate · `"VC"` Venture Capital |
| `start_date` | `str` | — | `DD.MM.YYYY` |
| `end_date` | `str` | — | `DD.MM.YYYY` |
| `fund_codes` | `list \| None` | `None` | Filter by exact fund code(s) |
| `fund_title_contains` | `list \| None` | `None` | Filter by term(s) in fund name |
| `save_to_excel` | `bool` | `False` | Save result to `.xlsx` |

**Returns** `pandas.DataFrame`

**Columns**

| Column | Description |
|---|---|
| `fonKodu` | Fund code |
| `fonUnvan` | Fund name |
| `tarih` | Date |
| `fiyat` | Price |
| `tedPaySayisi` | Number of shares in circulation |
| `kisiSayisi` | Number of investors |
| `portfoyBuyukluk` | Portfolio size (TRY) |
| `borsaBultenFiyat` | Exchange bulletin price |

**Example**

```python
from tefasfon import get_funds

df = get_funds(
    fund_type="SEC",
    start_date="01.04.2026",
    end_date="30.04.2026",
)

df = get_funds(
    fund_type="PEN",
    start_date="01.04.2026",
    end_date="30.04.2026",
    fund_codes=["AEK", "AEY"],
)

df = get_funds(
    fund_type="ETF",
    start_date="01.04.2026",
    end_date="30.04.2026",
    fund_title_contains=["ALTIN", "GÜMÜŞ"],
)
```

---

### `get_portfolio`

Fetches portfolio breakdown by date range and fund type.

**Parameters**

| Parameter | Type | Default | Description |
|---|---|---|---|
| `fund_type` | `str` | — | `"SEC"` Securities · `"PEN"` Pension · `"ETF"` Exchange-Traded Fund · `"RE"` Real Estate · `"VC"` Venture Capital |
| `start_date` | `str` | — | `DD.MM.YYYY` |
| `end_date` | `str` | — | `DD.MM.YYYY` |
| `fund_codes` | `list \| None` | `None` | Filter by exact fund code(s) |
| `fund_title_contains` | `list \| None` | `None` | Filter by term(s) in fund name |
| `save_to_excel` | `bool` | `False` | Save result to `.xlsx` |

**Returns** `pandas.DataFrame`

**Columns**

| Column | Description |
|---|---|
| `fonKodu` | Fund code |
| `fonUnvan` | Fund name |
| `tarih` | Date |
| `hs` | Stocks (%) |
| `yhs` | Foreign stocks (%) |
| `dt` | Government bonds (%) |
| `hb` | Treasury bills (%) |
| `vdm` | Time deposits (%) |
| `vmtl` | Time deposits — TL (%) |
| `tr` | Reverse repo (%) |
| `byf` | ETFs (%) |
| `yyf` | Foreign investment funds (%) |
| `km` | Precious metals (%) |
| `kkstl` | Lease certificates — TL (%) |
| `osks` | Private sector lease certificates (%) |

> Other columns represent additional asset class breakdowns; `null` means no allocation.

**Example**

```python
from tefasfon import get_portfolio

df = get_portfolio(
    fund_type="PEN",
    start_date="01.04.2026",
    end_date="30.04.2026",
)
```

---

### `get_returns`

Fetches fund return data by fund type and basis.

**Parameters**

| Parameter | Type | Default | Description |
|---|---|---|---|
| `fund_type` | `str` | — | `"SEC"` Securities · `"PEN"` Pension · `"ETF"` Exchange-Traded Fund · `"RE"` Real Estate · `"VC"` Venture Capital |
| `basis` | `str` | — | `"RB"` Return-based · `"SB"` Size-based · `"MB"` Management fee-based |
| `start_date` | `str \| None` | `None` | `DD.MM.YYYY` — optional for `"RB"`, required for `"SB"` and `"MB"` |
| `end_date` | `str \| None` | `None` | `DD.MM.YYYY` — optional for `"RB"`, required for `"SB"` and `"MB"` |
| `fund_codes` | `list \| None` | `None` | Filter by exact fund code(s) |
| `fund_title_contains` | `list \| None` | `None` | Filter by term(s) in fund name |
| `save_to_excel` | `bool` | `False` | Save result to `.xlsx` |

**Returns** `pandas.DataFrame`

**Columns (RB — without date)**

| Column | Description |
|---|---|
| `fonKodu` | Fund code |
| `fonUnvan` | Fund name |
| `getiri1a` | 1-month return (%) |
| `getiri3a` | 3-month return (%) |
| `getiri6a` | 6-month return (%) |
| `getiri1y` | 1-year return (%) |
| `getiriyb` | Year-to-date return (%) |
| `getiri3y` | 3-year return (%) |
| `getiri5y` | 5-year return (%) |
| `riskDegeri` | Risk level |

**Columns (RB — with date)**

| Column | Description |
|---|---|
| `fonKodu` | Fund code |
| `fonUnvan` | Fund name |
| `getiriOrani` | Return for the given period (%) |
| `riskDegeri` | Risk level |

**Columns (SB)**

| Column | Description |
|---|---|
| `fonKodu` | Fund code |
| `fonUnvan` | Fund name |
| `ilkPortfoyDegeri` | Portfolio size at start (TRY) |
| `sonPortfoyDegeri` | Portfolio size at end (TRY) |
| `portBuyuklukDegisim` | Portfolio size change (%) |
| `ilkPayAdedi` | Share count at start |
| `sonPayAdedi` | Share count at end |
| `payAdetDegisim` | Share count change (%) |
| `netGetiriOrani` | Net return (%) |

**Columns (MB)**

| Column | Description |
|---|---|
| `fonKodu` | Fund code |
| `fonUnvan` | Fund name |
| `fonTurAciklama` | Fund type description |
| `tefasDurum` | TEFAS status |
| `fonTurKod` | Fund type code |
| `kurucuKod` | Founder code |
| `altbaslik1` | Label for `uygulananYu1Y` (API-provided field name) |
| `uygulananYu1Y` | Applied management fee — 1 year (%) |
| `altbaslik2` | Label for `fonIcTuzukYu1G` (API-provided field name) |
| `fonIcTuzukYu1G` | Management fee per fund prospectus — annual (%) |
| `yillikGetiri` | Annual return (%) |
| `altbaslik3` | Label for `fonTopGiderKesoran` (API-provided field name) |
| `fonTopGiderKesoran` | Total expense ratio — string, Turkish decimal format (e.g. `"1,65"`) |

**Example**

```python
from tefasfon import get_returns

# Return-based — standard periods
df = get_returns(
    fund_type="ETF",
    basis="RB",
)

# Return-based — specific date range
df = get_returns(
    fund_type="PEN",
    basis="RB",
    start_date="01.04.2026",
    end_date="30.04.2026",
)

# Size-based
df = get_returns(
    fund_type="RE",
    basis="SB",
    start_date="01.04.2026",
    end_date="30.04.2026",
)

# Management fee-based
df = get_returns(
    fund_type="SEC",
    basis="MB",
    start_date="01.04.2026",
    end_date="30.04.2026",
)
```

---

### `analyze_funds`

Calculates performance metrics from a `get_funds()` DataFrame.

**Parameters**

| Parameter | Type | Default | Description |
|---|---|---|---|
| `df` | `pd.DataFrame` | — | Output of `get_funds()` |
| `price_col` | `str` | `"fiyat"` | Price column |
| `fund_code_col` | `str` | `"fonKodu"` | Fund code column |
| `fund_title_col` | `str` | `"fonUnvan"` | Fund name column |
| `date_col` | `str` | `"tarih"` | Date column |
| `freq` | `str` | `"D"` | Return frequency: `D` `B` `W` `M` `Q` `A`/`Y` |
| `risk_free_annual` | `float` | `0.0` | Annual risk-free rate (e.g. `0.10` = 10%) |
| `periods_per_year` | `int \| None` | `None` | Inferred from `freq` if not set |
| `method` | `str` | `"simple"` | `"simple"` or `"log"` |
| `drop_empty` | `bool` | `True` | Drop funds with insufficient data |

**Returns** `pandas.DataFrame` sorted by Sharpe ratio (descending)

**Columns**

| Column | Description |
|---|---|
| `Fund Code` | Fund code |
| `Fund Name` | Fund name |
| `Observations` | Number of return observations |
| `Cumulative Return` | Total return over the period |
| `Annualized Return` | Annualized return |
| `Annualized Volatility` | Annualized standard deviation of returns |
| `Sharpe Ratio` | Risk-adjusted return (annualized) |

**Example**

```python
from tefasfon import get_funds, analyze_funds

df = get_funds(
    fund_type="SEC",
    start_date="01.01.2026",
    end_date="30.04.2026",
)

metrics = analyze_funds(
    df,
    freq="B",
    method="log",
    risk_free_annual=0.40,
)
```

---

## Changelog

### v1.0.0 — 04/05/2026

* **No browser needed.** Google Chrome no longer has to be installed.
* **Faster.** Data is fetched directly from the API — no page rendering.
* Function renamed from `fetch_tefas_data` to `get_funds`.
* Added `get_portfolio` and `get_returns`.
* `lang`, `headless`, and `wait_seconds` parameters removed.

### v0.4.1 — 18/10/2025

* `fund_title_contains` now matches whole words only.

### v0.4.0 — 18/10/2025

* Added `analyze_funds`.

### v0.3.0 — 15/10/2025

* Added `fund_codes` and `fund_title_contains` filters.

### v0.2.0 — 05/09/2025

* Returns an empty DataFrame instead of raising an error when no data is found.

### v0.1.0 — 20/07/2025

* First release.

---

## Contributing

Bug reports, feature requests, and pull requests are welcome at [github.com/urazakgul/tefasfon](https://github.com/urazakgul/tefasfon).

## License

MIT
