borsapy 0.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
borsapy/inflation.py ADDED
@@ -0,0 +1,166 @@
1
+ """Inflation class for TCMB inflation data - yfinance-like API."""
2
+
3
+ from typing import Any
4
+
5
+ import pandas as pd
6
+
7
+ from borsapy._providers.tcmb import get_tcmb_provider
8
+
9
+
10
+ class Inflation:
11
+ """
12
+ A yfinance-like interface for Turkish inflation data from TCMB.
13
+
14
+ Examples:
15
+ >>> import borsapy as bp
16
+ >>> inf = bp.Inflation()
17
+
18
+ # Get latest inflation
19
+ >>> inf.latest()
20
+ {'date': '2024-11-01', 'yearly_inflation': 47.09, 'monthly_inflation': 2.24, ...}
21
+
22
+ # Get TÜFE history
23
+ >>> inf.tufe(limit=12) # Last 12 months
24
+ YearMonth YearlyInflation MonthlyInflation
25
+ Date
26
+ 2024-11-01 11-2024 47.09 2.24
27
+ ...
28
+
29
+ # Calculate inflation
30
+ >>> inf.calculate(100000, "2020-01", "2024-01")
31
+ {'initial_value': 100000, 'final_value': 342515.0, 'total_change': 242.52, ...}
32
+ """
33
+
34
+ def __init__(self):
35
+ """Initialize an Inflation object."""
36
+ self._provider = get_tcmb_provider()
37
+
38
+ def latest(self, inflation_type: str = "tufe") -> dict[str, Any]:
39
+ """
40
+ Get the latest inflation data.
41
+
42
+ Args:
43
+ inflation_type: 'tufe' (CPI) or 'ufe' (PPI)
44
+
45
+ Returns:
46
+ Dictionary with latest inflation data:
47
+ - date: Date string (YYYY-MM-DD)
48
+ - year_month: Month-Year string
49
+ - yearly_inflation: Year-over-year inflation rate
50
+ - monthly_inflation: Month-over-month inflation rate
51
+ - type: Inflation type (TUFE or UFE)
52
+ """
53
+ return self._provider.get_latest(inflation_type)
54
+
55
+ def tufe(
56
+ self,
57
+ start: str | None = None,
58
+ end: str | None = None,
59
+ limit: int | None = None,
60
+ ) -> pd.DataFrame:
61
+ """
62
+ Get TÜFE (Consumer Price Index) data.
63
+
64
+ Args:
65
+ start: Start date in YYYY-MM-DD format
66
+ end: End date in YYYY-MM-DD format
67
+ limit: Maximum number of records
68
+
69
+ Returns:
70
+ DataFrame with columns: YearMonth, YearlyInflation, MonthlyInflation.
71
+ Index is the Date.
72
+
73
+ Examples:
74
+ >>> inf = Inflation()
75
+ >>> inf.tufe(limit=6) # Last 6 months
76
+ >>> inf.tufe(start="2023-01-01", end="2023-12-31") # 2023 data
77
+ """
78
+ return self._provider.get_data("tufe", start, end, limit)
79
+
80
+ def ufe(
81
+ self,
82
+ start: str | None = None,
83
+ end: str | None = None,
84
+ limit: int | None = None,
85
+ ) -> pd.DataFrame:
86
+ """
87
+ Get ÜFE (Producer Price Index) data.
88
+
89
+ Args:
90
+ start: Start date in YYYY-MM-DD format
91
+ end: End date in YYYY-MM-DD format
92
+ limit: Maximum number of records
93
+
94
+ Returns:
95
+ DataFrame with columns: YearMonth, YearlyInflation, MonthlyInflation.
96
+ Index is the Date.
97
+
98
+ Examples:
99
+ >>> inf = Inflation()
100
+ >>> inf.ufe(limit=6) # Last 6 months
101
+ >>> inf.ufe(start="2023-01-01", end="2023-12-31") # 2023 data
102
+ """
103
+ return self._provider.get_data("ufe", start, end, limit)
104
+
105
+ def calculate(
106
+ self,
107
+ amount: float,
108
+ start: str,
109
+ end: str,
110
+ ) -> dict[str, Any]:
111
+ """
112
+ Calculate inflation-adjusted value between two dates.
113
+
114
+ Uses TCMB's official inflation calculator API.
115
+
116
+ Args:
117
+ amount: Initial amount in TRY
118
+ start: Start date in YYYY-MM format (e.g., "2020-01")
119
+ end: End date in YYYY-MM format (e.g., "2024-01")
120
+
121
+ Returns:
122
+ Dictionary with:
123
+ - start_date: Start date
124
+ - end_date: End date
125
+ - initial_value: Initial amount
126
+ - final_value: Inflation-adjusted value
127
+ - total_years: Total years elapsed
128
+ - total_months: Total months elapsed
129
+ - total_change: Total percentage change
130
+ - avg_yearly_inflation: Average yearly inflation rate
131
+ - start_cpi: CPI at start date
132
+ - end_cpi: CPI at end date
133
+
134
+ Examples:
135
+ >>> inf = Inflation()
136
+ >>> result = inf.calculate(100000, "2020-01", "2024-01")
137
+ >>> print(f"100,000 TL in 2020 = {result['final_value']:,.0f} TL in 2024")
138
+ 100,000 TL in 2020 = 342,515 TL in 2024
139
+ """
140
+ start_year, start_month = self._parse_year_month(start)
141
+ end_year, end_month = self._parse_year_month(end)
142
+
143
+ return self._provider.calculate_inflation(
144
+ start_year=start_year,
145
+ start_month=start_month,
146
+ end_year=end_year,
147
+ end_month=end_month,
148
+ basket_value=amount,
149
+ )
150
+
151
+ def _parse_year_month(self, date_str: str) -> tuple[int, int]:
152
+ """Parse YYYY-MM format to (year, month) tuple."""
153
+ try:
154
+ parts = date_str.split("-")
155
+ if len(parts) != 2:
156
+ raise ValueError(f"Invalid date format: {date_str}. Use YYYY-MM")
157
+ year = int(parts[0])
158
+ month = int(parts[1])
159
+ if not (1 <= month <= 12):
160
+ raise ValueError(f"Invalid month: {month}")
161
+ return year, month
162
+ except Exception as e:
163
+ raise ValueError(f"Could not parse date '{date_str}': {e}") from e
164
+
165
+ def __repr__(self) -> str:
166
+ return "Inflation()"
borsapy/market.py ADDED
@@ -0,0 +1,53 @@
1
+ """Market-level functions for BIST data."""
2
+
3
+ import pandas as pd
4
+
5
+ from borsapy._providers.kap import get_kap_provider
6
+
7
+
8
+ def companies() -> pd.DataFrame:
9
+ """
10
+ Get list of all BIST companies.
11
+
12
+ Returns:
13
+ DataFrame with columns:
14
+ - ticker: Stock ticker code (e.g., "THYAO", "GARAN")
15
+ - name: Company name
16
+ - city: Company headquarters city
17
+
18
+ Examples:
19
+ >>> import borsapy as bp
20
+ >>> bp.companies()
21
+ ticker name city
22
+ 0 ACSEL ACIPAYAM SELULOZ SANAYI A.S. DENIZLI
23
+ 1 ADEL ADEL KALEMCILIK A.S. ISTANBUL
24
+ ...
25
+ """
26
+ provider = get_kap_provider()
27
+ return provider.get_companies()
28
+
29
+
30
+ def search_companies(query: str) -> pd.DataFrame:
31
+ """
32
+ Search BIST companies by name or ticker.
33
+
34
+ Args:
35
+ query: Search query (ticker code or company name)
36
+
37
+ Returns:
38
+ DataFrame with matching companies, sorted by relevance.
39
+
40
+ Examples:
41
+ >>> import borsapy as bp
42
+ >>> bp.search_companies("THYAO")
43
+ ticker name city
44
+ 0 THYAO TURK HAVA YOLLARI A.O. ISTANBUL
45
+
46
+ >>> bp.search_companies("banka")
47
+ ticker name city
48
+ 0 GARAN TURKIYE GARANTI BANKASI A.S. ISTANBUL
49
+ 1 AKBNK AKBANK T.A.S. ISTANBUL
50
+ ...
51
+ """
52
+ provider = get_kap_provider()
53
+ return provider.search(query)
borsapy/multi.py ADDED
@@ -0,0 +1,227 @@
1
+ """Multi-ticker functions and classes - yfinance-like API."""
2
+
3
+ from datetime import datetime
4
+
5
+ import pandas as pd
6
+
7
+ from borsapy._providers.paratic import get_paratic_provider
8
+ from borsapy.ticker import Ticker
9
+
10
+
11
+ class Tickers:
12
+ """
13
+ Container for multiple Ticker objects.
14
+
15
+ Examples:
16
+ >>> import borsapy as bp
17
+ >>> tickers = bp.Tickers("THYAO GARAN AKBNK")
18
+ >>> tickers.tickers["THYAO"].info
19
+ >>> tickers.symbols
20
+ ['THYAO', 'GARAN', 'AKBNK']
21
+
22
+ >>> tickers = bp.Tickers(["THYAO", "GARAN", "AKBNK"])
23
+ >>> for symbol, ticker in tickers:
24
+ ... print(symbol, ticker.info['last'])
25
+ """
26
+
27
+ def __init__(self, symbols: str | list[str]):
28
+ """
29
+ Initialize Tickers with multiple symbols.
30
+
31
+ Args:
32
+ symbols: Space-separated string or list of symbols.
33
+ Example: "THYAO GARAN AKBNK" or ["THYAO", "GARAN", "AKBNK"]
34
+ """
35
+ if isinstance(symbols, str):
36
+ self._symbols = [s.strip().upper() for s in symbols.split() if s.strip()]
37
+ else:
38
+ self._symbols = [s.strip().upper() for s in symbols if s.strip()]
39
+
40
+ self._tickers: dict[str, Ticker] = {}
41
+ for symbol in self._symbols:
42
+ self._tickers[symbol] = Ticker(symbol)
43
+
44
+ @property
45
+ def symbols(self) -> list[str]:
46
+ """Return list of symbols."""
47
+ return self._symbols.copy()
48
+
49
+ @property
50
+ def tickers(self) -> dict[str, Ticker]:
51
+ """Return dictionary of Ticker objects keyed by symbol."""
52
+ return self._tickers
53
+
54
+ def history(
55
+ self,
56
+ period: str = "1mo",
57
+ interval: str = "1d",
58
+ start: datetime | str | None = None,
59
+ end: datetime | str | None = None,
60
+ group_by: str = "column",
61
+ ) -> pd.DataFrame:
62
+ """
63
+ Get historical data for all tickers.
64
+
65
+ Args:
66
+ period: Data period (1d, 5d, 1mo, 3mo, 6mo, 1y, etc.)
67
+ interval: Data interval (1d, 1wk, 1mo)
68
+ start: Start date
69
+ end: End date
70
+ group_by: How to group columns ('column' or 'ticker')
71
+
72
+ Returns:
73
+ DataFrame with multi-level columns.
74
+ """
75
+ return download(
76
+ self._symbols,
77
+ period=period,
78
+ interval=interval,
79
+ start=start,
80
+ end=end,
81
+ group_by=group_by,
82
+ )
83
+
84
+ def __iter__(self):
85
+ """Iterate over (symbol, ticker) pairs."""
86
+ return iter(self._tickers.items())
87
+
88
+ def __len__(self) -> int:
89
+ """Return number of tickers."""
90
+ return len(self._tickers)
91
+
92
+ def __getitem__(self, symbol: str) -> Ticker:
93
+ """Get ticker by symbol."""
94
+ symbol = symbol.upper()
95
+ if symbol not in self._tickers:
96
+ raise KeyError(f"Symbol not found: {symbol}")
97
+ return self._tickers[symbol]
98
+
99
+ def __repr__(self) -> str:
100
+ return f"Tickers({self._symbols})"
101
+
102
+
103
+ def download(
104
+ tickers: str | list[str],
105
+ period: str = "1mo",
106
+ interval: str = "1d",
107
+ start: datetime | str | None = None,
108
+ end: datetime | str | None = None,
109
+ group_by: str = "column",
110
+ progress: bool = True,
111
+ ) -> pd.DataFrame:
112
+ """
113
+ Download historical data for multiple tickers.
114
+
115
+ Similar to yfinance.download(), this function fetches OHLCV data
116
+ for multiple stocks and returns a DataFrame with multi-level columns.
117
+
118
+ Args:
119
+ tickers: Space-separated string or list of symbols.
120
+ Example: "THYAO GARAN AKBNK" or ["THYAO", "GARAN"]
121
+ period: Data period. Valid values:
122
+ 1d, 5d, 1mo, 3mo, 6mo, 1y, 2y, 5y, 10y, ytd, max.
123
+ Ignored if start is provided.
124
+ interval: Data interval. Valid values:
125
+ 1m, 5m, 15m, 30m, 1h, 1d, 1wk, 1mo.
126
+ start: Start date (string YYYY-MM-DD or datetime).
127
+ end: End date (string YYYY-MM-DD or datetime). Defaults to today.
128
+ group_by: How to group the output columns:
129
+ - 'column': MultiIndex (Price, Symbol) - default
130
+ - 'ticker': MultiIndex (Symbol, Price)
131
+ progress: Show progress (not implemented, for yfinance compatibility).
132
+
133
+ Returns:
134
+ DataFrame with OHLCV data.
135
+ - If single ticker: Simple columns (Open, High, Low, Close, Volume)
136
+ - If multiple tickers: MultiIndex columns based on group_by
137
+
138
+ Examples:
139
+ >>> import borsapy as bp
140
+
141
+ # Single ticker (returns simple DataFrame)
142
+ >>> bp.download("THYAO", period="1mo")
143
+ Open High Low Close Volume
144
+ Date
145
+ 2024-12-01 265.00 268.00 264.00 267.50 12345678
146
+
147
+ # Multiple tickers (returns MultiIndex DataFrame)
148
+ >>> bp.download(["THYAO", "GARAN"], period="1mo")
149
+ Open High ...
150
+ THYAO GARAN THYAO GARAN
151
+ Date
152
+ 2024-12-01 265.00 45.50 268.00 46.20
153
+
154
+ # With date range
155
+ >>> bp.download("THYAO GARAN AKBNK", start="2024-01-01", end="2024-06-30")
156
+
157
+ # Group by ticker
158
+ >>> bp.download(["THYAO", "GARAN"], group_by="ticker")
159
+ THYAO GARAN
160
+ Open High Low Close Open High
161
+ Date
162
+ 2024-12-01 265.00 268.00 ... 45.50 46.20
163
+ """
164
+ # Parse symbols
165
+ if isinstance(tickers, str):
166
+ symbols = [s.strip().upper() for s in tickers.split() if s.strip()]
167
+ else:
168
+ symbols = [s.strip().upper() for s in tickers if s.strip()]
169
+
170
+ if not symbols:
171
+ raise ValueError("No symbols provided")
172
+
173
+ # Parse dates
174
+ start_dt = _parse_date(start) if start else None
175
+ end_dt = _parse_date(end) if end else None
176
+
177
+ provider = get_paratic_provider()
178
+
179
+ # Fetch data for each symbol
180
+ data_frames: dict[str, pd.DataFrame] = {}
181
+ for symbol in symbols:
182
+ try:
183
+ df = provider.get_history(
184
+ symbol=symbol,
185
+ period=period,
186
+ interval=interval,
187
+ start=start_dt,
188
+ end=end_dt,
189
+ )
190
+ if not df.empty:
191
+ data_frames[symbol] = df
192
+ except Exception:
193
+ # Skip failed symbols silently (yfinance behavior)
194
+ continue
195
+
196
+ if not data_frames:
197
+ return pd.DataFrame()
198
+
199
+ # Single ticker - return simple DataFrame
200
+ if len(symbols) == 1 and len(data_frames) == 1:
201
+ return list(data_frames.values())[0]
202
+
203
+ # Multiple tickers - create MultiIndex DataFrame
204
+ if group_by == "ticker":
205
+ # Group by ticker first: (THYAO, Open), (THYAO, High), ...
206
+ result = pd.concat(data_frames, axis=1)
207
+ # result columns are already (symbol, price)
208
+ else:
209
+ # Group by column first: (Open, THYAO), (Open, GARAN), ...
210
+ result = pd.concat(data_frames, axis=1)
211
+ # Swap levels to get (price, symbol)
212
+ result = result.swaplevel(axis=1)
213
+ result = result.sort_index(axis=1, level=0)
214
+
215
+ return result
216
+
217
+
218
+ def _parse_date(date: str | datetime) -> datetime:
219
+ """Parse a date string to datetime."""
220
+ if isinstance(date, datetime):
221
+ return date
222
+ for fmt in ["%Y-%m-%d", "%Y/%m/%d", "%d-%m-%Y", "%d/%m/%Y"]:
223
+ try:
224
+ return datetime.strptime(date, fmt)
225
+ except ValueError:
226
+ continue
227
+ raise ValueError(f"Could not parse date: {date}")