borsapy.search

Symbol search functionality for borsapy.

This module provides unified symbol search across TradingView and local KAP data.

Examples:

import borsapy as bp bp.search("banka") # Search all assets ['AKBNK', 'GARAN', 'ISCTR', 'YKBNK', 'HALKB', ...]

>>> bp.search("gold", type="forex")  # Filter by type
['XAUUSD', 'XAUTRY', ...]

>>> bp.search("GARAN", exchange="BIST")  # Filter by exchange
['GARAN']
  1"""Symbol search functionality for borsapy.
  2
  3This module provides unified symbol search across TradingView and local KAP data.
  4
  5Examples:
  6    >>> import borsapy as bp
  7    >>> bp.search("banka")               # Search all assets
  8    ['AKBNK', 'GARAN', 'ISCTR', 'YKBNK', 'HALKB', ...]
  9
 10    >>> bp.search("gold", type="forex")  # Filter by type
 11    ['XAUUSD', 'XAUTRY', ...]
 12
 13    >>> bp.search("GARAN", exchange="BIST")  # Filter by exchange
 14    ['GARAN']
 15"""
 16
 17from __future__ import annotations
 18
 19import logging
 20from typing import Any
 21
 22logger = logging.getLogger(__name__)
 23
 24
 25def search(
 26    query: str,
 27    type: str | None = None,
 28    exchange: str | None = None,
 29    limit: int = 50,
 30    full_info: bool = False,
 31) -> list[str] | list[dict[str, Any]]:
 32    """Search for symbols matching the query.
 33
 34    Searches TradingView's symbol database and optionally merges with local
 35    KAP company data for comprehensive BIST coverage.
 36
 37    Args:
 38        query: Search query (e.g., "banka", "enerji", "THY", "gold")
 39        type: Filter by asset type:
 40              - "stock": Stocks/equities
 41              - "forex" or "fx": Forex pairs
 42              - "crypto": Cryptocurrencies
 43              - "index": Market indices
 44              - "futures": Futures contracts
 45              - "fund" or "etf": Funds and ETFs
 46              - "bond": Bonds
 47        exchange: Filter by exchange (e.g., "BIST", "NASDAQ", "NYSE")
 48        limit: Maximum number of results (default 50, max 100)
 49        full_info: If True, return full result dicts; if False, return symbol list
 50
 51    Returns:
 52        If full_info=False (default):
 53            List of symbol strings: ['AKBNK', 'GARAN', ...]
 54
 55        If full_info=True:
 56            List of result dicts:
 57            [
 58                {
 59                    "symbol": "AKBNK",
 60                    "full_name": "BIST:AKBNK",
 61                    "description": "AKBANK T.A.S.",
 62                    "exchange": "BIST",
 63                    "type": "stock",
 64                    "currency": "TRY",
 65                    "country": "TR"
 66                },
 67                ...
 68            ]
 69
 70    Examples:
 71        >>> import borsapy as bp
 72
 73        >>> # Simple search
 74        >>> bp.search("banka")
 75        ['AKBNK', 'GARAN', 'ISCTR', 'YKBNK', 'HALKB', ...]
 76
 77        >>> # Search with type filter
 78        >>> bp.search("gold", type="forex")
 79        ['XAUUSD', 'XAUTRY', ...]
 80
 81        >>> # Search with exchange filter
 82        >>> bp.search("THY", exchange="BIST")
 83        ['THYAO']
 84
 85        >>> # Get full info
 86        >>> bp.search("GARAN", full_info=True)
 87        [{'symbol': 'GARAN', 'description': 'TURKIYE GARANTI BANKASI A.S.', ...}]
 88
 89        >>> # Search crypto
 90        >>> bp.search("BTC", type="crypto")
 91        ['BTCUSD', 'BTCTRY', 'BTCUSDT', ...]
 92
 93        >>> # Search indices
 94        >>> bp.search("XU", type="index", exchange="BIST")
 95        ['XU100', 'XU030', 'XU050', ...]
 96
 97    Raises:
 98        ValueError: If query is empty
 99    """
100    if not query or not query.strip():
101        raise ValueError("Search query cannot be empty")
102
103    from borsapy._providers.tradingview_search import get_search_provider
104
105    provider = get_search_provider()
106
107    # Search TradingView
108    results = provider.search(
109        query=query,
110        asset_type=type,
111        exchange=exchange,
112        limit=limit,
113    )
114
115    # Try to enhance with KAP data for BIST stocks
116    if not type or type.lower() == "stock":
117        if not exchange or exchange.upper() == "BIST":
118            results = _merge_with_kap(results, query)
119
120    if full_info:
121        return results
122    else:
123        # Return just symbol list (deduplicated, maintaining order)
124        seen = set()
125        symbols = []
126        for r in results:
127            sym = r.get("symbol", "")
128            if sym and sym not in seen:
129                seen.add(sym)
130                symbols.append(sym)
131        return symbols
132
133
134def _merge_with_kap(
135    tv_results: list[dict[str, Any]], query: str
136) -> list[dict[str, Any]]:
137    """Merge TradingView results with KAP company data.
138
139    Adds any local KAP matches that TradingView might have missed.
140
141    Args:
142        tv_results: TradingView search results
143        query: Original search query
144
145    Returns:
146        Merged results with KAP data appended
147    """
148    try:
149        from borsapy.market import search_companies
150
151        # Get KAP matches
152        kap_results = search_companies(query)
153
154        if not kap_results.empty:
155            # Get symbols already in TV results
156            tv_symbols = {r.get("symbol", "").upper() for r in tv_results}
157
158            # Add KAP results not already in TV
159            for _, row in kap_results.iterrows():
160                symbol = str(row.get("symbol", "")).upper()
161                if symbol and symbol not in tv_symbols:
162                    tv_results.append({
163                        "symbol": symbol,
164                        "full_name": f"BIST:{symbol}",
165                        "description": str(row.get("company", "")),
166                        "exchange": "BIST",
167                        "type": "stock",
168                        "currency": "TRY",
169                        "country": "TR",
170                        "source": "kap",
171                    })
172
173    except Exception as e:
174        logger.debug(f"KAP merge failed: {e}")
175
176    return tv_results
177
178
179def search_bist(query: str, limit: int = 50) -> list[str]:
180    """Search BIST symbols only.
181
182    Convenience function for Turkish stock search.
183
184    Args:
185        query: Search query
186        limit: Maximum results
187
188    Returns:
189        List of BIST symbol strings
190
191    Examples:
192        >>> bp.search_bist("banka")
193        ['AKBNK', 'GARAN', 'ISCTR', 'YKBNK', 'HALKB']
194    """
195    return search(query, type="stock", exchange="BIST", limit=limit)
196
197
198def search_crypto(query: str, limit: int = 50) -> list[str]:
199    """Search cryptocurrency symbols.
200
201    Args:
202        query: Search query (e.g., "BTC", "ETH")
203        limit: Maximum results
204
205    Returns:
206        List of crypto symbol strings
207
208    Examples:
209        >>> bp.search_crypto("BTC")
210        ['BTCUSD', 'BTCTRY', 'BTCUSDT', ...]
211    """
212    return search(query, type="crypto", limit=limit)
213
214
215def search_forex(query: str, limit: int = 50) -> list[str]:
216    """Search forex symbols.
217
218    Args:
219        query: Search query (e.g., "USD", "EUR", "gold")
220        limit: Maximum results
221
222    Returns:
223        List of forex symbol strings
224
225    Examples:
226        >>> bp.search_forex("gold")
227        ['XAUUSD', 'XAUTRY', ...]
228    """
229    return search(query, type="forex", limit=limit)
230
231
232def search_index(query: str, limit: int = 50) -> list[str]:
233    """Search market index symbols.
234
235    Args:
236        query: Search query (e.g., "XU", "SP500")
237        limit: Maximum results
238
239    Returns:
240        List of index symbol strings
241
242    Examples:
243        >>> bp.search_index("XU")
244        ['XU100', 'XU030', 'XU050', ...]
245    """
246    return search(query, type="index", limit=limit)
247
248
249def search_viop(query: str, limit: int = 50) -> list[str]:
250    """Search VIOP (Turkish derivatives) symbols.
251
252    Searches for futures and options contracts on BIST.
253
254    Args:
255        query: Search query (e.g., "XU030", "AKBNK", "gold")
256        limit: Maximum results
257
258    Returns:
259        List of VIOP contract symbol strings
260
261    Examples:
262        >>> bp.search_viop("XU030")
263        ['XU030D', 'XU030DG2026', 'XU030DJ2026', ...]
264
265        >>> bp.search_viop("gold")
266        ['XAUTRYD', 'XAUTRYG2026', ...]
267    """
268    return search(query, type="futures", exchange="BIST", limit=limit)
269
270
271def viop_contracts(
272    base_symbol: str,
273    full_info: bool = False,
274) -> list[str] | list[dict]:
275    """Get available VIOP contracts for a base symbol.
276
277    Queries TradingView to find all active contracts (expiry months)
278    for a given futures base symbol.
279
280    Args:
281        base_symbol: Base futures symbol (e.g., "XU030D", "XAUTRYD", "USDTRYD")
282                    Can be with or without 'D' suffix.
283        full_info: If True, return full contract info dicts
284
285    Returns:
286        If full_info=False (default):
287            List of contract symbol strings: ['XU030DG2026', 'XU030DJ2026']
288
289        If full_info=True:
290            List of contract dicts with month/year info
291
292    Note:
293        Contract month codes:
294        F=Jan, G=Feb, H=Mar, J=Apr, K=May, M=Jun,
295        N=Jul, Q=Aug, U=Sep, V=Oct, X=Nov, Z=Dec
296
297    Examples:
298        >>> import borsapy as bp
299
300        >>> # Get BIST30 futures contracts
301        >>> bp.viop_contracts("XU030D")
302        ['XU030DG2026', 'XU030DJ2026']
303
304        >>> # Get gold TRY futures
305        >>> bp.viop_contracts("XAUTRYD")
306        ['XAUTRYG2026', 'XAUTRYJ2026']
307
308        >>> # Get full contract info
309        >>> bp.viop_contracts("XU030D", full_info=True)
310        [
311            {'symbol': 'XU030DG2026', 'month_code': 'G', 'year': '2026', ...},
312            {'symbol': 'XU030DJ2026', 'month_code': 'J', 'year': '2026', ...},
313        ]
314
315    See Also:
316        search_viop: Search for VIOP symbols by keyword
317    """
318    from borsapy._providers.tradingview_search import get_search_provider
319
320    provider = get_search_provider()
321    contracts = provider.get_viop_contracts(base_symbol)
322
323    if full_info:
324        return contracts
325    else:
326        # Filter out continuous contracts (they don't work with streaming)
327        return [c["symbol"] for c in contracts if not c.get("is_continuous", False)]
logger = <Logger borsapy.search (WARNING)>
def search_bist(query: str, limit: int = 50) -> list[str]:
180def search_bist(query: str, limit: int = 50) -> list[str]:
181    """Search BIST symbols only.
182
183    Convenience function for Turkish stock search.
184
185    Args:
186        query: Search query
187        limit: Maximum results
188
189    Returns:
190        List of BIST symbol strings
191
192    Examples:
193        >>> bp.search_bist("banka")
194        ['AKBNK', 'GARAN', 'ISCTR', 'YKBNK', 'HALKB']
195    """
196    return search(query, type="stock", exchange="BIST", limit=limit)

Search BIST symbols only.

Convenience function for Turkish stock search.

Args: query: Search query limit: Maximum results

Returns: List of BIST symbol strings

Examples:

bp.search_bist("banka") ['AKBNK', 'GARAN', 'ISCTR', 'YKBNK', 'HALKB']

def search_crypto(query: str, limit: int = 50) -> list[str]:
199def search_crypto(query: str, limit: int = 50) -> list[str]:
200    """Search cryptocurrency symbols.
201
202    Args:
203        query: Search query (e.g., "BTC", "ETH")
204        limit: Maximum results
205
206    Returns:
207        List of crypto symbol strings
208
209    Examples:
210        >>> bp.search_crypto("BTC")
211        ['BTCUSD', 'BTCTRY', 'BTCUSDT', ...]
212    """
213    return search(query, type="crypto", limit=limit)

Search cryptocurrency symbols.

Args: query: Search query (e.g., "BTC", "ETH") limit: Maximum results

Returns: List of crypto symbol strings

Examples:

bp.search_crypto("BTC") ['BTCUSD', 'BTCTRY', 'BTCUSDT', ...]

def search_forex(query: str, limit: int = 50) -> list[str]:
216def search_forex(query: str, limit: int = 50) -> list[str]:
217    """Search forex symbols.
218
219    Args:
220        query: Search query (e.g., "USD", "EUR", "gold")
221        limit: Maximum results
222
223    Returns:
224        List of forex symbol strings
225
226    Examples:
227        >>> bp.search_forex("gold")
228        ['XAUUSD', 'XAUTRY', ...]
229    """
230    return search(query, type="forex", limit=limit)

Search forex symbols.

Args: query: Search query (e.g., "USD", "EUR", "gold") limit: Maximum results

Returns: List of forex symbol strings

Examples:

bp.search_forex("gold") ['XAUUSD', 'XAUTRY', ...]

def search_index(query: str, limit: int = 50) -> list[str]:
233def search_index(query: str, limit: int = 50) -> list[str]:
234    """Search market index symbols.
235
236    Args:
237        query: Search query (e.g., "XU", "SP500")
238        limit: Maximum results
239
240    Returns:
241        List of index symbol strings
242
243    Examples:
244        >>> bp.search_index("XU")
245        ['XU100', 'XU030', 'XU050', ...]
246    """
247    return search(query, type="index", limit=limit)

Search market index symbols.

Args: query: Search query (e.g., "XU", "SP500") limit: Maximum results

Returns: List of index symbol strings

Examples:

bp.search_index("XU") ['XU100', 'XU030', 'XU050', ...]

def search_viop(query: str, limit: int = 50) -> list[str]:
250def search_viop(query: str, limit: int = 50) -> list[str]:
251    """Search VIOP (Turkish derivatives) symbols.
252
253    Searches for futures and options contracts on BIST.
254
255    Args:
256        query: Search query (e.g., "XU030", "AKBNK", "gold")
257        limit: Maximum results
258
259    Returns:
260        List of VIOP contract symbol strings
261
262    Examples:
263        >>> bp.search_viop("XU030")
264        ['XU030D', 'XU030DG2026', 'XU030DJ2026', ...]
265
266        >>> bp.search_viop("gold")
267        ['XAUTRYD', 'XAUTRYG2026', ...]
268    """
269    return search(query, type="futures", exchange="BIST", limit=limit)

Search VIOP (Turkish derivatives) symbols.

Searches for futures and options contracts on BIST.

Args: query: Search query (e.g., "XU030", "AKBNK", "gold") limit: Maximum results

Returns: List of VIOP contract symbol strings

Examples:

bp.search_viop("XU030") ['XU030D', 'XU030DG2026', 'XU030DJ2026', ...]

>>> bp.search_viop("gold")
['XAUTRYD', 'XAUTRYG2026', ...]
def viop_contracts(base_symbol: str, full_info: bool = False) -> list[str] | list[dict]:
272def viop_contracts(
273    base_symbol: str,
274    full_info: bool = False,
275) -> list[str] | list[dict]:
276    """Get available VIOP contracts for a base symbol.
277
278    Queries TradingView to find all active contracts (expiry months)
279    for a given futures base symbol.
280
281    Args:
282        base_symbol: Base futures symbol (e.g., "XU030D", "XAUTRYD", "USDTRYD")
283                    Can be with or without 'D' suffix.
284        full_info: If True, return full contract info dicts
285
286    Returns:
287        If full_info=False (default):
288            List of contract symbol strings: ['XU030DG2026', 'XU030DJ2026']
289
290        If full_info=True:
291            List of contract dicts with month/year info
292
293    Note:
294        Contract month codes:
295        F=Jan, G=Feb, H=Mar, J=Apr, K=May, M=Jun,
296        N=Jul, Q=Aug, U=Sep, V=Oct, X=Nov, Z=Dec
297
298    Examples:
299        >>> import borsapy as bp
300
301        >>> # Get BIST30 futures contracts
302        >>> bp.viop_contracts("XU030D")
303        ['XU030DG2026', 'XU030DJ2026']
304
305        >>> # Get gold TRY futures
306        >>> bp.viop_contracts("XAUTRYD")
307        ['XAUTRYG2026', 'XAUTRYJ2026']
308
309        >>> # Get full contract info
310        >>> bp.viop_contracts("XU030D", full_info=True)
311        [
312            {'symbol': 'XU030DG2026', 'month_code': 'G', 'year': '2026', ...},
313            {'symbol': 'XU030DJ2026', 'month_code': 'J', 'year': '2026', ...},
314        ]
315
316    See Also:
317        search_viop: Search for VIOP symbols by keyword
318    """
319    from borsapy._providers.tradingview_search import get_search_provider
320
321    provider = get_search_provider()
322    contracts = provider.get_viop_contracts(base_symbol)
323
324    if full_info:
325        return contracts
326    else:
327        # Filter out continuous contracts (they don't work with streaming)
328        return [c["symbol"] for c in contracts if not c.get("is_continuous", False)]

Get available VIOP contracts for a base symbol.

Queries TradingView to find all active contracts (expiry months) for a given futures base symbol.

Args: base_symbol: Base futures symbol (e.g., "XU030D", "XAUTRYD", "USDTRYD") Can be with or without 'D' suffix. full_info: If True, return full contract info dicts

Returns: If full_info=False (default): List of contract symbol strings: ['XU030DG2026', 'XU030DJ2026']

If full_info=True:
    List of contract dicts with month/year info

Note: Contract month codes: F=Jan, G=Feb, H=Mar, J=Apr, K=May, M=Jun, N=Jul, Q=Aug, U=Sep, V=Oct, X=Nov, Z=Dec

Examples:

import borsapy as bp

>>> # Get BIST30 futures contracts
>>> bp.viop_contracts("XU030D")
['XU030DG2026', 'XU030DJ2026']

>>> # Get gold TRY futures
>>> bp.viop_contracts("XAUTRYD")
['XAUTRYG2026', 'XAUTRYJ2026']

>>> # Get full contract info
>>> bp.viop_contracts("XU030D", full_info=True)
[
    {'symbol': 'XU030DG2026', 'month_code': 'G', 'year': '2026', ...},
    {'symbol': 'XU030DJ2026', 'month_code': 'J', 'year': '2026', ...},
]

See Also: search_viop: Search for VIOP symbols by keyword