Skip to main content

Fetch fund data from https://www.tefas.gov.tr

Project description

tefas-client

PyPI - Version PyPI - Python Version PyPI - License CI Docs

tefas-client is a type-safe Python client for real-time fund data from TEFAS (Türkiye Elektronik Fon Alım Satım Platformu).

Fetch fund prices, allocations, and metrics with a simple, synchronous API. Perfect for building financial dashboards, analysis tools, and investment tracking apps.

v2 API — Production-ready, fully tested, zero breaking changes.

Features

  • 📊 Real-time data: Fund prices (NAV), market cap, investor counts
  • 🔍 Portfolio breakdown: Asset allocation across thousands of securities
  • 🗂️ Fund discovery: List umbrella fund types and founder institutions, with built-in caching
  • 📋 Fund snapshots: Instant overview (price, daily return, category rank, market share)
  • 🌍 Language support: Turkish (TR) and English (EN) responses
  • 🔎 Advanced filters: Filter by umbrella type code or founder institution
  • ⏱️ Smart chunking: Automatic handling of TEFAS's 28-day query limits
  • 🛡️ Type-safe: Full Pydantic validation, mypy compatible
  • ⚡ Resilient: Exponential backoff, automatic weekend date handling
  • 🐍 Modern Python: 3.10+ with context manager support

Installation

pip install tefas-client

Other package managers

# uv
uv pip install tefas-client

# Poetry
poetry add tefas-client

# Conda
conda install -c conda-forge tefas-client

Quick start

from datetime import date
from tefas_client import Tefas

# Basic usage – single fund
with Tefas() as tefas:
    funds = tefas.fetch("AAK", start_date=date(2024, 2, 1), end_date=date(2024, 2, 1))
    fund = funds["AAK"]
    print(f"{fund.title}: {fund.latest().price} TRY")
    # Output: Ak Portfoy Amerikan Dolar Yabanci BYF: 23.456789 TRY

Usage Examples

Basic: Fetch latest price

from datetime import date, timedelta
from tefas_client import Tefas

with Tefas(timeout=15.0) as tefas:
    today = date.today()
    funds = tefas.fetch("AAK", start_date=today, end_date=today)
    latest = funds["AAK"].latest()
    print(f"Price: {latest.price}")
    print(f"Market Cap: {latest.market_cap} TRY")
    print(f"Investors: {latest.number_of_investors}")

Advanced: Date range with allocation

from datetime import date
from tefas_client import Tefas

with Tefas() as tefas:
    # Fetch 3-month history with portfolio breakdown
    funds = tefas.fetch(
        "AAK",
        start_date=date(2024, 1, 1),
        end_date=date(2024, 3, 31),
        include_allocation=True,
    )
    
    for history in funds["AAK"].history:
        print(f"{history.date}: {history.price} TRY")
        
        if history.allocation:
            print("  Top holdings:")
            for code in list(history.allocation.assets.keys())[:3]:
                pct = history.allocation.assets[code]
                name = history.allocation.asset_names[code]
                print(f"    {name}: {pct:.2f}%")

Batch: Multiple funds at once

from datetime import date
from tefas_client import Tefas

fund_codes = ["AAK", "AAFTIYTF", "AKBLV"]

with Tefas() as tefas:
    # Fetch all funds at once (empty code = all available funds)
    all_funds = tefas.fetch(start_date=date(2024, 2, 1), end_date=date(2024, 2, 28))
    
    # Filter to codes of interest
    for code in fund_codes:
        if code in all_funds:
            fund = all_funds[code]
            price = fund.latest().price
            print(f"{code}: {price}")

Fund overview: instant snapshot

from tefas_client import Tefas

with Tefas() as tefas:
    ov = tefas.fetch_overview("IPB")
    print(f"{ov.title}")
    print(f"  Price     : {ov.price} TRY")
    print(f"  Daily ret : {ov.daily_return}%")
    print(f"  Rank      : {ov.category_rank}/{ov.category_fund_count} in {ov.category}")
    print(f"  Investors : {ov.number_of_investors:,}")
    print(f"  Mkt share : {ov.market_share}%")

Discovery: list fund types and founders

from tefas_client import Tefas

with Tefas() as tefas:
    # Umbrella fund type codes — use as umbrella_type= in fetch()
    types = tefas.fetch_fund_types()
    for t in types:
        print(t.code, t.name)

    # Founder codes — use as founder_code= in fetch()
    founders = tefas.fetch_founders()
    for f in founders:
        print(f.code, f.name)

Filtered fetch: umbrella type + founder

from datetime import date
from tefas_client import Tefas

with Tefas() as tefas:
    # Only equity umbrella funds (code 104) by a specific founder
    funds = tefas.fetch(
        fund_type="YAT",
        umbrella_type=104,
        founder_code="IPO",
        start_date=date(2024, 2, 1),
        end_date=date(2024, 2, 29),
    )
    for code, fund in funds.items():
        print(code, fund.latest().price)

English language responses

from tefas_client import Tefas

with Tefas(lang="EN") as tefas:
    ov = tefas.fetch_overview("IPB")
    print(ov.category)   # "Money Market Fund" instead of "Para Piyasası Fonu"

    types = tefas.fetch_fund_types()
    print(types[0].name) # English umbrella type names

Integration: Export to Pandas

import pandas as pd
from datetime import date
from tefas_client import Tefas

with Tefas() as tefas:
    funds = tefas.fetch("AAK", start_date=date(2024, 1, 1), end_date=date(2024, 3, 31))
    
    # Convert to DataFrame
    df = pd.DataFrame([
        {
            "date": h.date,
            "price": h.price,
            "market_cap": h.market_cap,
            "investors": h.number_of_investors,
        }
        for h in funds["AAK"].history
    ])
    
    print(df.describe())

Error handling

from tefas_client import Tefas, RateLimitError, EmptyResponseError

try:
    with Tefas() as tefas:
        funds = tefas.fetch("INVALID")
except EmptyResponseError:
    print("No data for this date range")
except RateLimitError as e:
    print(f"Rate limited. Retry after {e.retry_after} seconds")

API reference

Tefas(timeout: float = 30.0, lang: str = "TR")

Context manager for managing HTTP connections and sessions.

with Tefas(timeout=15.0, lang="EN") as tefas:
    funds = tefas.fetch("AAK", start_date=date.today(), end_date=date.today())
Parameter Type Default Description
timeout float 30.0 Per-request HTTP timeout in seconds
lang str "TR" Response language: "TR" (Turkish) or "EN" (English)

Tefas.fetch(...) -> dict[str, Fund]

Fetch fund price history for a given date range. Large ranges are automatically split into ≤28-day chunks.

with Tefas() as tefas:
    # Single fund — type auto-detected (YAT → EMK → BYF)
    funds = tefas.fetch("AAK", start_date=date(2024, 1, 1), end_date=date(2024, 3, 31))

    # All investment funds
    all_funds = tefas.fetch(start_date=date.today(), end_date=date.today())

    # Pension (BES) funds filtered by founder
    funds = tefas.fetch(fund_type="EMK", founder_code="IPO", start_date=date(2024, 2, 1))

    # Filter by umbrella type (e.g. equity funds = 104)
    funds = tefas.fetch(fund_type="YAT", umbrella_type=104, start_date=date(2024, 2, 1))
Parameter Type Default Description
fund_code str | list[str] "" TEFAS code(s), e.g. "AAK" or ["AAK", "TLY"]. Empty = all funds
start_date date | None end_date Inclusive range start. Defaults to end_date
end_date date | None today Inclusive range end. Auto-adjusted to nearest Friday if weekend
include_allocation bool False Include portfolio allocation breakdown
fund_type "YAT" | "EMK" | "BYF" | None None Fund type. When None and a specific code is given, auto-detected via YAT → EMK → BYF
umbrella_type int | None None Umbrella fund type code (e.g. 104). Use fetch_fund_types() to list valid codes
founder_code str | None None Founder institution code (e.g. "IPO"). Use fetch_founders() to list valid codes

Returns: dict[str, Fund] — mapping of fund code → fund data


Tefas.fetch_overview(fund_code: str) -> FundOverview

Fetch an instant snapshot of a single fund — current price, daily return, category ranking, and market share. Not a time-series; reflects the state at call time.

from tefas_client import Tefas, FundOverview

with Tefas() as tefas:
    ov = tefas.fetch_overview("IPB")
    print(f"{ov.title}: {ov.price} TRY, rank {ov.category_rank}/{ov.category_fund_count}")
Field Type Description
code str TEFAS fund code
title str Full fund name
price float | None Latest NAV in TRY
daily_return float | None Daily return percentage
shares float | None Total circulating shares
market_cap float | None Portfolio size in TRY
category str | None Fund category name
category_rank int | None Rank within category
category_fund_count int | None Total funds in category
number_of_investors int | None Active investor count
market_share float | None Market share percentage

Tefas.fetch_fund_types(fund_type: "YAT" | "EMK" = "YAT", *, refresh: bool = False) -> list[UmbrellaFundType]

List umbrella fund type codes and names. Pass the returned code values as umbrella_type in fetch().

Results are cached per instance — the second call returns the same list without hitting the API. Pass refresh=True to force a fresh request.

from tefas_client import Tefas

with Tefas() as tefas:
    types = tefas.fetch_fund_types()          # fetches from API
    types = tefas.fetch_fund_types()          # returns cached result
    types = tefas.fetch_fund_types(refresh=True)  # forces fresh fetch
    for t in types:
        print(t.code, t.name)  # e.g. 104 "Hisse Senedi Şemsiye Fonu"
Parameter Type Default Description
fund_type "YAT" | "EMK" "YAT" Fund type to list categories for
refresh bool False When True, bypass cache and fetch fresh data
Field Type Description
code int Numeric umbrella type code
name str Umbrella type name

Tefas.fetch_founders(fund_type: "YAT" | "EMK" = "YAT", *, refresh: bool = False) -> list[Founder]

List founder institution codes and names. Pass the returned code values as founder_code in fetch().

Results are cached per instance — the second call returns the same list without hitting the API. Pass refresh=True to force a fresh request.

from tefas_client import Tefas

with Tefas() as tefas:
    founders = tefas.fetch_founders()          # fetches from API
    founders = tefas.fetch_founders()          # returns cached result
    founders = tefas.fetch_founders(refresh=True)  # forces fresh fetch
    for f in founders:
        print(f.code, f.name)  # e.g. "IPO" "İŞ PORTFÖY YÖNETİMİ A.Ş."
Parameter Type Default Description
fund_type "YAT" | "EMK" "YAT" Fund type to list founders for
refresh bool False When True, bypass cache and fetch fresh data
Field Type Description
code str Founder institution code
name str Founder institution name
fund_type str | None Fund type indicator

Fund

Represents a single fund.

fund: Fund
fund.code          # "AAK"
fund.title         # "Ak Portfoy Amerikan Dolar Yabanci BYF"
fund.latest()      # History (most recent entry)
fund.history       # list[History] (all historical entries, oldest first)
Attribute Type Description
code str TEFAS fund code (e.g. "AAK")
title str Full fund name in Turkish
history list[History] Chronological price/metric history, oldest first

History

Single fund snapshot for a trading date.

h: History
h.price                    # Fund unit price (NAV) in TRY
h.market_cap              # Portfolio value in TRY
h.number_of_investors     # Active investor count
h.allocation              # Allocation data (if requested)
Field Type Description
date date Trading date (never a weekend/holiday)
price float | None Fund unit price (NAV) in TRY
market_cap float | None Portfolio size (TRY)
number_of_shares float | None Total circulating shares
number_of_investors int | None Active investor count
exchange_bulletin_price float | None Exchange bulletin price (BYF only)
allocation Allocation | None Portfolio breakdown (if include_allocation=True)

Allocation

Portfolio composition snapshot.

a: Allocation
a.assets           # {"US0378331005": 45.5, "IE00B4L5Y983": 30.2, ...}
a.asset_names      # {"US0378331005": "APPLE INC.", ...}
Field Type Description
date date Allocation date
assets dict[str, float] {ISIN: percentage_allocation}
asset_names dict[str, str] {ISIN: security_name}

Exceptions

All exceptions inherit from TefasError and can be caught with:

from tefas_client import Tefas, TefasError

try:
    with Tefas() as tefas:
        funds = tefas.fetch("AAK", start_date=date(2024, 1, 1))
except TefasError as e:
    print(f"TEFAS error: {e}")
Exception When Example
TefasError Base class for all library errors except TefasError: pass
RateLimitError HTTP 429 after retries (has retry_after attribute) except RateLimitError as e: time.sleep(e.retry_after)
EmptyResponseError API returned 200 but no rows for query except EmptyResponseError: print("No data")

Known Constraints

Constraint Impact Workaround
Rate limit ~6 req/min, retries with backoff, raises after 3 failures Space requests ≥10s apart, use batch queries
28-day window Max query is ~28 calendar days Automatic; fetch() chunks larger ranges
No weekend data TEFAS closed weekends/holidays Dates auto-adjust to nearest Friday
WAF/IP blocks Datacenter IPs may get 403/503 Use residential IP or VPN
Synchronous only No built-in async/await Use asyncio.to_thread() if needed (advanced)

Troubleshooting

"403 Forbidden" or "503 Service Unavailable"

Cause: TEFAS WAF blocks datacenter IP ranges (AWS, Azure, etc.)

Solution:

  • Use a residential VPN or local ISP connection
  • For production: Use a proxy service with residential IPs
  • The library auto-retries; wait a few seconds before trying again

"RateLimitError: Retry after X seconds"

Cause: Too many requests to TEFAS API (>6/min)

Solution:

import time
from tefas_client import Tefas, RateLimitError

with Tefas() as tefas:
    for fund_code in ["AAK", "AAFTIYTF", "AKBLV"]:
        try:
            funds = tefas.fetch(fund_code, start_date=date.today(), end_date=date.today())
        except RateLimitError as e:
            print(f"Rate limited. Waiting {e.retry_after}s...")
            time.sleep(e.retry_after + 1)  # +1 for safety margin
            # Retry logic here

"EmptyResponseError"

Cause: No trading data for that date (e.g., weekend, holiday, fund didn't exist)

Solution:

from tefas_client import Tefas, EmptyResponseError

try:
    with Tefas() as tefas:
        # Try a date range instead of single day
        funds = tefas.fetch("AAK", start_date=date(2024, 1, 1), end_date=date(2024, 1, 10))
except EmptyResponseError:
    print("No data available; fund may not have existed or dates were all holidays")

Slow performance

Tip 1: Use context manager (reuses connection across multiple fetch() calls)

# ✅ Good: single connection for 3 calls
with Tefas() as tefas:
    funds1 = tefas.fetch("AAK", ...)
    funds2 = tefas.fetch("AAFTIYTF", ...)
    funds3 = tefas.fetch("AKBLV", ...)

Tip 2: Fetch all funds in one call instead of per-code

# ✅ Good: 1 request
with Tefas() as tefas:
    all_funds = tefas.fetch(start_date=date.today(), end_date=date.today())  
    tefas_funds = {k: v for k, v in all_funds.items() if k.startswith("AAFTI")}

# ❌ Avoid: 3 requests
with Tefas() as tefas:
    funds1 = tefas.fetch("AAK", ...)
    funds2 = tefas.fetch("AAFTIYTF", ...)
    funds3 = tefas.fetch("AKBLV", ...)

Tip 3: Use shorter date ranges

# ✅ Good: smaller response, faster
with Tefas() as tefas:
    funds = tefas.fetch("AAK", start_date=date(2024, 1, 1), end_date=date(2024, 1, 31))

# ❌ Slow: year of data
with Tefas() as tefas:
    funds = tefas.fetch("AAK", start_date=date(2023, 1, 1), end_date=date(2024, 12, 31))

Contributing

We welcome contributions! To get started:

# Clone and install
git clone https://github.com/semudu/tefas-client
cd tefas-client
make install

# Run tests
make test

# Lint and format
make lint
make format

Development workflow:

  • Fork the repository on GitHub
  • Create a feature branch: git checkout -b feature/my-feature
  • Make your changes and ensure tests pass (make test)
  • Commit: git commit -am "Add my feature"
  • Push and open a pull request

Code style:

  • Python 3.10+ with type hints
  • Ruff for formatting and linting
  • Mypy for static type checking
  • Pytest for testing

See CONTRIBUTING.md (if available) or open an issue to discuss ideas.

License

MIT — Free for personal and commercial use


Questions? Open an issue
Found a bug? Report it
Have a feature idea? Suggest it

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tefas_client-2.0.2.tar.gz (64.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tefas_client-2.0.2-py3-none-any.whl (21.0 kB view details)

Uploaded Python 3

File details

Details for the file tefas_client-2.0.2.tar.gz.

File metadata

  • Download URL: tefas_client-2.0.2.tar.gz
  • Upload date:
  • Size: 64.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for tefas_client-2.0.2.tar.gz
Algorithm Hash digest
SHA256 4f405ba97b5bd41dc024c067e2312c14fa050cbda4d162e9c461c6db71ef35c1
MD5 00247b754823e921ee3d420707662772
BLAKE2b-256 325c9b37aa06969292e2b403630f0664703950a5c0c3dcfa79056aeaba37c245

See more details on using hashes here.

Provenance

The following attestation bundles were made for tefas_client-2.0.2.tar.gz:

Publisher: release.yml on semudu/tefas-client

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file tefas_client-2.0.2-py3-none-any.whl.

File metadata

  • Download URL: tefas_client-2.0.2-py3-none-any.whl
  • Upload date:
  • Size: 21.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for tefas_client-2.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 b3fafcbf5b210c3ee1e4e4d444dd46d8627f2584cf6966384d7de104f3f505ad
MD5 0f10d185bbcc40886ca5fd4e6f476eb3
BLAKE2b-256 b2838b93e21a0ea495bcd4d62a8df1aed0b1cb461b6a84ec7f487a6e498dba63

See more details on using hashes here.

Provenance

The following attestation bundles were made for tefas_client-2.0.2-py3-none-any.whl:

Publisher: release.yml on semudu/tefas-client

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page