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/calendar.py ADDED
@@ -0,0 +1,272 @@
1
+ """EconomicCalendar class for economic events - yfinance-like API."""
2
+
3
+ from datetime import datetime, timedelta
4
+ from typing import Any
5
+
6
+ import pandas as pd
7
+
8
+ from borsapy._providers.dovizcom_calendar import get_calendar_provider
9
+
10
+
11
+ class EconomicCalendar:
12
+ """
13
+ A yfinance-like interface for economic calendar data.
14
+
15
+ Data source: doviz.com
16
+
17
+ Examples:
18
+ >>> import borsapy as bp
19
+ >>> cal = bp.EconomicCalendar()
20
+ >>> cal.events(period="1w") # This week's events
21
+ Date Time Country Importance Event Actual Forecast Previous
22
+ 0 2024-01-15 10:00:00 Türkiye high Enflasyon (YoY) 64.77% 65.00% 61.98%
23
+ ...
24
+
25
+ >>> cal.today() # Today's events
26
+ >>> cal.events(country="TR", importance="high") # High importance TR events
27
+ """
28
+
29
+ # Valid country codes
30
+ COUNTRIES = ["TR", "US", "EU", "DE", "GB", "JP", "CN", "FR", "IT", "CA", "AU", "CH"]
31
+
32
+ def __init__(self):
33
+ """Initialize EconomicCalendar."""
34
+ self._provider = get_calendar_provider()
35
+
36
+ def events(
37
+ self,
38
+ period: str = "1w",
39
+ start: datetime | str | None = None,
40
+ end: datetime | str | None = None,
41
+ country: str | list[str] | None = None,
42
+ importance: str | None = None,
43
+ ) -> pd.DataFrame:
44
+ """
45
+ Get economic calendar events.
46
+
47
+ Args:
48
+ period: How much data to fetch. Valid periods: 1d, 1w, 2w, 1mo.
49
+ Ignored if start is provided.
50
+ start: Start date (string or datetime).
51
+ end: End date (string or datetime). Defaults to start + period.
52
+ country: Country code(s) to filter by (TR, US, EU, etc.).
53
+ Can be a single code or list of codes.
54
+ Defaults to ['TR', 'US'].
55
+ importance: Filter by importance level ('low', 'mid', 'high').
56
+
57
+ Returns:
58
+ DataFrame with columns: Date, Time, Country, Importance, Event,
59
+ Actual, Forecast, Previous, Period.
60
+
61
+ Examples:
62
+ >>> cal = EconomicCalendar()
63
+ >>> cal.events(period="1w") # Next 7 days
64
+ >>> cal.events(country="TR", importance="high") # High importance TR events
65
+ >>> cal.events(country=["TR", "US", "EU"]) # Multiple countries
66
+ >>> cal.events(start="2024-01-01", end="2024-01-31") # Date range
67
+ """
68
+ # Parse dates
69
+ start_dt = self._parse_date(start) if start else None
70
+ end_dt = self._parse_date(end) if end else None
71
+
72
+ # If no start, use today
73
+ if start_dt is None:
74
+ start_dt = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
75
+
76
+ # If no end, calculate from period
77
+ if end_dt is None:
78
+ days = {"1d": 1, "1w": 7, "2w": 14, "1mo": 30}.get(period, 7)
79
+ end_dt = start_dt + timedelta(days=days)
80
+
81
+ # Parse country parameter
82
+ countries = self._parse_countries(country)
83
+
84
+ # Fetch events
85
+ events = self._provider.get_economic_calendar(
86
+ start=start_dt,
87
+ end=end_dt,
88
+ countries=countries,
89
+ importance=importance,
90
+ )
91
+
92
+ # Convert to DataFrame
93
+ if not events:
94
+ return pd.DataFrame(
95
+ columns=[
96
+ "Date",
97
+ "Time",
98
+ "Country",
99
+ "Importance",
100
+ "Event",
101
+ "Actual",
102
+ "Forecast",
103
+ "Previous",
104
+ "Period",
105
+ ]
106
+ )
107
+
108
+ df = pd.DataFrame(events)
109
+
110
+ # Rename columns
111
+ df = df.rename(
112
+ columns={
113
+ "date": "Date",
114
+ "time": "Time",
115
+ "country": "Country",
116
+ "importance": "Importance",
117
+ "event": "Event",
118
+ "actual": "Actual",
119
+ "forecast": "Forecast",
120
+ "previous": "Previous",
121
+ "period": "Period",
122
+ }
123
+ )
124
+
125
+ # Drop internal columns
126
+ if "country_code" in df.columns:
127
+ df = df.drop(columns=["country_code"])
128
+
129
+ # Reorder columns
130
+ column_order = [
131
+ "Date",
132
+ "Time",
133
+ "Country",
134
+ "Importance",
135
+ "Event",
136
+ "Actual",
137
+ "Forecast",
138
+ "Previous",
139
+ "Period",
140
+ ]
141
+ df = df[[c for c in column_order if c in df.columns]]
142
+
143
+ return df
144
+
145
+ def today(
146
+ self,
147
+ country: str | list[str] | None = None,
148
+ importance: str | None = None,
149
+ ) -> pd.DataFrame:
150
+ """
151
+ Get today's economic events.
152
+
153
+ Args:
154
+ country: Country code(s) to filter by.
155
+ importance: Filter by importance level.
156
+
157
+ Returns:
158
+ DataFrame with today's economic events.
159
+ """
160
+ return self.events(period="1d", country=country, importance=importance)
161
+
162
+ def this_week(
163
+ self,
164
+ country: str | list[str] | None = None,
165
+ importance: str | None = None,
166
+ ) -> pd.DataFrame:
167
+ """
168
+ Get this week's economic events.
169
+
170
+ Args:
171
+ country: Country code(s) to filter by.
172
+ importance: Filter by importance level.
173
+
174
+ Returns:
175
+ DataFrame with this week's economic events.
176
+ """
177
+ return self.events(period="1w", country=country, importance=importance)
178
+
179
+ def this_month(
180
+ self,
181
+ country: str | list[str] | None = None,
182
+ importance: str | None = None,
183
+ ) -> pd.DataFrame:
184
+ """
185
+ Get this month's economic events.
186
+
187
+ Args:
188
+ country: Country code(s) to filter by.
189
+ importance: Filter by importance level.
190
+
191
+ Returns:
192
+ DataFrame with this month's economic events.
193
+ """
194
+ return self.events(period="1mo", country=country, importance=importance)
195
+
196
+ def high_importance(
197
+ self,
198
+ period: str = "1w",
199
+ country: str | list[str] | None = None,
200
+ ) -> pd.DataFrame:
201
+ """
202
+ Get high importance events only.
203
+
204
+ Args:
205
+ period: Time period (1d, 1w, 2w, 1mo).
206
+ country: Country code(s) to filter by.
207
+
208
+ Returns:
209
+ DataFrame with high importance events.
210
+ """
211
+ return self.events(period=period, country=country, importance="high")
212
+
213
+ @staticmethod
214
+ def countries() -> list[str]:
215
+ """
216
+ Get list of supported country codes.
217
+
218
+ Returns:
219
+ List of country codes.
220
+
221
+ Examples:
222
+ >>> EconomicCalendar.countries()
223
+ ['TR', 'US', 'EU', 'DE', 'GB', 'JP', 'CN', ...]
224
+ """
225
+ return EconomicCalendar.COUNTRIES.copy()
226
+
227
+ def _parse_date(self, date: str | datetime) -> datetime:
228
+ """Parse a date string to datetime."""
229
+ if isinstance(date, datetime):
230
+ return date
231
+ for fmt in ["%Y-%m-%d", "%Y/%m/%d", "%d-%m-%Y", "%d/%m/%Y"]:
232
+ try:
233
+ return datetime.strptime(date, fmt)
234
+ except ValueError:
235
+ continue
236
+ raise ValueError(f"Could not parse date: {date}")
237
+
238
+ def _parse_countries(self, country: str | list[str] | None) -> list[str]:
239
+ """Parse country parameter to list of codes."""
240
+ if country is None:
241
+ return ["TR", "US"]
242
+ if isinstance(country, str):
243
+ return [country.upper()]
244
+ return [c.upper() for c in country]
245
+
246
+ def __repr__(self) -> str:
247
+ return "EconomicCalendar()"
248
+
249
+
250
+ def economic_calendar(
251
+ period: str = "1w",
252
+ country: str | list[str] | None = None,
253
+ importance: str | None = None,
254
+ ) -> pd.DataFrame:
255
+ """
256
+ Get economic calendar events (convenience function).
257
+
258
+ Args:
259
+ period: Time period (1d, 1w, 2w, 1mo). Defaults to 1w.
260
+ country: Country code(s) to filter by. Defaults to ['TR', 'US'].
261
+ importance: Filter by importance level ('low', 'mid', 'high').
262
+
263
+ Returns:
264
+ DataFrame with economic events.
265
+
266
+ Examples:
267
+ >>> import borsapy as bp
268
+ >>> bp.economic_calendar() # This week, TR + US
269
+ >>> bp.economic_calendar(country="TR", importance="high")
270
+ """
271
+ cal = EconomicCalendar()
272
+ return cal.events(period=period, country=country, importance=importance)
borsapy/crypto.py ADDED
@@ -0,0 +1,153 @@
1
+ """Crypto class for cryptocurrency data - yfinance-like API."""
2
+
3
+ from datetime import datetime
4
+ from typing import Any
5
+
6
+ import pandas as pd
7
+
8
+ from borsapy._providers.btcturk import get_btcturk_provider
9
+
10
+
11
+ class Crypto:
12
+ """
13
+ A yfinance-like interface for cryptocurrency data from BtcTurk.
14
+
15
+ Examples:
16
+ >>> import borsapy as bp
17
+ >>> btc = bp.Crypto("BTCTRY")
18
+ >>> btc.current
19
+ {'symbol': 'BTCTRY', 'last': 3500000.0, ...}
20
+ >>> btc.history(period="1mo")
21
+ Open High Low Close Volume
22
+ Date
23
+ 2024-12-01 3400000.0 3550000.0 3380000.0 3500000.0 1234.5678
24
+ ...
25
+
26
+ >>> eth = bp.Crypto("ETHTRY")
27
+ >>> eth.current['last']
28
+ 125000.0
29
+ """
30
+
31
+ def __init__(self, pair: str):
32
+ """
33
+ Initialize a Crypto object.
34
+
35
+ Args:
36
+ pair: Trading pair (e.g., "BTCTRY", "ETHTRY", "BTCUSDT").
37
+ Common pairs: BTCTRY, ETHTRY, XRPTRY, DOGETRY, SOLTRY
38
+ """
39
+ self._pair = pair.upper()
40
+ self._provider = get_btcturk_provider()
41
+ self._current_cache: dict[str, Any] | None = None
42
+
43
+ @property
44
+ def pair(self) -> str:
45
+ """Return the trading pair."""
46
+ return self._pair
47
+
48
+ @property
49
+ def symbol(self) -> str:
50
+ """Return the trading pair (alias)."""
51
+ return self._pair
52
+
53
+ @property
54
+ def current(self) -> dict[str, Any]:
55
+ """
56
+ Get current ticker information.
57
+
58
+ Returns:
59
+ Dictionary with current market data:
60
+ - symbol: Trading pair
61
+ - last: Last traded price
62
+ - open: Opening price
63
+ - high: 24h high
64
+ - low: 24h low
65
+ - bid: Best bid price
66
+ - ask: Best ask price
67
+ - volume: 24h volume
68
+ - change: Price change
69
+ - change_percent: Percent change
70
+ """
71
+ if self._current_cache is None:
72
+ self._current_cache = self._provider.get_ticker(self._pair)
73
+ return self._current_cache
74
+
75
+ @property
76
+ def info(self) -> dict[str, Any]:
77
+ """Alias for current property (yfinance compatibility)."""
78
+ return self.current
79
+
80
+ def history(
81
+ self,
82
+ period: str = "1mo",
83
+ interval: str = "1d",
84
+ start: datetime | str | None = None,
85
+ end: datetime | str | None = None,
86
+ ) -> pd.DataFrame:
87
+ """
88
+ Get historical OHLCV data.
89
+
90
+ Args:
91
+ period: How much data to fetch. Valid periods:
92
+ 1d, 5d, 1mo, 3mo, 6mo, 1y.
93
+ Ignored if start is provided.
94
+ interval: Data granularity. Valid intervals:
95
+ 1m, 5m, 15m, 30m, 1h, 4h, 1d, 1wk.
96
+ start: Start date (string or datetime).
97
+ end: End date (string or datetime). Defaults to now.
98
+
99
+ Returns:
100
+ DataFrame with columns: Open, High, Low, Close, Volume.
101
+ Index is the Date.
102
+
103
+ Examples:
104
+ >>> crypto = Crypto("BTCTRY")
105
+ >>> crypto.history(period="1mo") # Last month
106
+ >>> crypto.history(period="1y", interval="1wk") # Weekly for 1 year
107
+ >>> crypto.history(start="2024-01-01", end="2024-06-30") # Date range
108
+ """
109
+ start_dt = self._parse_date(start) if start else None
110
+ end_dt = self._parse_date(end) if end else None
111
+
112
+ return self._provider.get_history(
113
+ pair=self._pair,
114
+ period=period,
115
+ interval=interval,
116
+ start=start_dt,
117
+ end=end_dt,
118
+ )
119
+
120
+ def _parse_date(self, date: str | datetime) -> datetime:
121
+ """Parse a date string to datetime."""
122
+ if isinstance(date, datetime):
123
+ return date
124
+ for fmt in ["%Y-%m-%d", "%Y/%m/%d", "%d-%m-%Y", "%d/%m/%Y"]:
125
+ try:
126
+ return datetime.strptime(date, fmt)
127
+ except ValueError:
128
+ continue
129
+ raise ValueError(f"Could not parse date: {date}")
130
+
131
+ def __repr__(self) -> str:
132
+ return f"Crypto('{self._pair}')"
133
+
134
+
135
+ def crypto_pairs(quote: str = "TRY") -> list[str]:
136
+ """
137
+ Get list of available cryptocurrency trading pairs.
138
+
139
+ Args:
140
+ quote: Quote currency filter (TRY, USDT, BTC)
141
+
142
+ Returns:
143
+ List of available trading pair symbols.
144
+
145
+ Examples:
146
+ >>> import borsapy as bp
147
+ >>> bp.crypto_pairs()
148
+ ['BTCTRY', 'ETHTRY', 'XRPTRY', ...]
149
+ >>> bp.crypto_pairs("USDT")
150
+ ['BTCUSDT', 'ETHUSDT', ...]
151
+ """
152
+ provider = get_btcturk_provider()
153
+ return provider.get_pairs(quote)
borsapy/exceptions.py ADDED
@@ -0,0 +1,64 @@
1
+ """Custom exceptions for borsapy."""
2
+
3
+
4
+ class BorsapyError(Exception):
5
+ """Base exception for all borsapy errors."""
6
+
7
+ pass
8
+
9
+
10
+ class TickerNotFoundError(BorsapyError):
11
+ """Raised when a ticker symbol is not found."""
12
+
13
+ def __init__(self, symbol: str):
14
+ self.symbol = symbol
15
+ super().__init__(f"Ticker not found: {symbol}")
16
+
17
+
18
+ class DataNotAvailableError(BorsapyError):
19
+ """Raised when requested data is not available."""
20
+
21
+ def __init__(self, message: str = "Data not available"):
22
+ super().__init__(message)
23
+
24
+
25
+ class APIError(BorsapyError):
26
+ """Raised when an API request fails."""
27
+
28
+ def __init__(self, message: str, status_code: int | None = None):
29
+ self.status_code = status_code
30
+ super().__init__(
31
+ f"API Error: {message}" + (f" (status: {status_code})" if status_code else "")
32
+ )
33
+
34
+
35
+ class AuthenticationError(BorsapyError):
36
+ """Raised when authentication fails."""
37
+
38
+ pass
39
+
40
+
41
+ class RateLimitError(BorsapyError):
42
+ """Raised when rate limit is exceeded."""
43
+
44
+ pass
45
+
46
+
47
+ class InvalidPeriodError(BorsapyError):
48
+ """Raised when an invalid period is specified."""
49
+
50
+ def __init__(self, period: str):
51
+ self.period = period
52
+ valid_periods = ["1d", "5d", "1mo", "3mo", "6mo", "1y", "2y", "5y", "10y", "ytd", "max"]
53
+ super().__init__(f"Invalid period: {period}. Valid periods: {', '.join(valid_periods)}")
54
+
55
+
56
+ class InvalidIntervalError(BorsapyError):
57
+ """Raised when an invalid interval is specified."""
58
+
59
+ def __init__(self, interval: str):
60
+ self.interval = interval
61
+ valid_intervals = ["1m", "5m", "15m", "30m", "1h", "1d", "1wk", "1mo"]
62
+ super().__init__(
63
+ f"Invalid interval: {interval}. Valid intervals: {', '.join(valid_intervals)}"
64
+ )