forexsmartbot 3.2.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.
Files changed (129) hide show
  1. forexsmartbot/__init__.py +1 -0
  2. forexsmartbot/adapters/__init__.py +4 -0
  3. forexsmartbot/adapters/brokers/__init__.py +8 -0
  4. forexsmartbot/adapters/brokers/ib_tws_broker.py +244 -0
  5. forexsmartbot/adapters/brokers/mt4_broker.py +266 -0
  6. forexsmartbot/adapters/brokers/paper_broker.py +131 -0
  7. forexsmartbot/adapters/brokers/rest_broker.py +304 -0
  8. forexsmartbot/adapters/data/__init__.py +27 -0
  9. forexsmartbot/adapters/data/alpha_vantage_provider.py +261 -0
  10. forexsmartbot/adapters/data/config.py +101 -0
  11. forexsmartbot/adapters/data/csv_provider.py +137 -0
  12. forexsmartbot/adapters/data/dummy_provider.py +35 -0
  13. forexsmartbot/adapters/data/mt4_provider.py +217 -0
  14. forexsmartbot/adapters/data/multi_provider.py +191 -0
  15. forexsmartbot/adapters/data/oanda_provider.py +173 -0
  16. forexsmartbot/adapters/data/stooq_provider.py +114 -0
  17. forexsmartbot/adapters/data/twelve_data_provider.py +117 -0
  18. forexsmartbot/adapters/data/yfinance_provider.py +246 -0
  19. forexsmartbot/analytics/__init__.py +28 -0
  20. forexsmartbot/analytics/chart_patterns.py +322 -0
  21. forexsmartbot/analytics/correlation_matrix.py +186 -0
  22. forexsmartbot/analytics/economic_calendar.py +616 -0
  23. forexsmartbot/analytics/enhanced_chart.py +57 -0
  24. forexsmartbot/analytics/market_depth.py +134 -0
  25. forexsmartbot/analytics/performance_attribution.py +210 -0
  26. forexsmartbot/analytics/portfolio_analytics.py +303 -0
  27. forexsmartbot/analytics/risk_analytics.py +308 -0
  28. forexsmartbot/analytics/strategy_comparison.py +194 -0
  29. forexsmartbot/analytics/trade_journal.py +235 -0
  30. forexsmartbot/brokers/mt4_bridge.py +49 -0
  31. forexsmartbot/builder/__init__.py +12 -0
  32. forexsmartbot/builder/code_generator.py +173 -0
  33. forexsmartbot/builder/strategy_builder.py +119 -0
  34. forexsmartbot/builder/strategy_template.py +138 -0
  35. forexsmartbot/cloud/__init__.py +24 -0
  36. forexsmartbot/cloud/api_client.py +188 -0
  37. forexsmartbot/cloud/api_server.py +366 -0
  38. forexsmartbot/cloud/cloud_sync.py +279 -0
  39. forexsmartbot/cloud/error_handler.py +150 -0
  40. forexsmartbot/cloud/mobile_api.py +213 -0
  41. forexsmartbot/cloud/remote_monitor.py +310 -0
  42. forexsmartbot/cloud/security.py +208 -0
  43. forexsmartbot/core/backtester.py +57 -0
  44. forexsmartbot/core/config.py +38 -0
  45. forexsmartbot/core/interfaces.py +253 -0
  46. forexsmartbot/core/paper_broker.py +28 -0
  47. forexsmartbot/core/portfolio.py +227 -0
  48. forexsmartbot/core/risk.py +24 -0
  49. forexsmartbot/core/risk_engine.py +203 -0
  50. forexsmartbot/core/strategy.py +44 -0
  51. forexsmartbot/core/trade_manager.py +261 -0
  52. forexsmartbot/gui/main_window.py +168 -0
  53. forexsmartbot/gui/theme.py +32 -0
  54. forexsmartbot/marketplace/__init__.py +10 -0
  55. forexsmartbot/marketplace/marketplace.py +178 -0
  56. forexsmartbot/marketplace/strategy_rating.py +64 -0
  57. forexsmartbot/monitoring/__init__.py +12 -0
  58. forexsmartbot/monitoring/health_check.py +109 -0
  59. forexsmartbot/monitoring/performance_tracker.py +213 -0
  60. forexsmartbot/monitoring/strategy_monitor.py +175 -0
  61. forexsmartbot/optimization/__init__.py +26 -0
  62. forexsmartbot/optimization/adaptive_parameters.py +224 -0
  63. forexsmartbot/optimization/genetic_optimizer.py +116 -0
  64. forexsmartbot/optimization/hyperparameter_optimizer.py +77 -0
  65. forexsmartbot/optimization/monte_carlo.py +114 -0
  66. forexsmartbot/optimization/multi_objective_optimizer.py +299 -0
  67. forexsmartbot/optimization/parameter_sensitivity.py +187 -0
  68. forexsmartbot/optimization/walk_forward.py +131 -0
  69. forexsmartbot/services/__init__.py +7 -0
  70. forexsmartbot/services/backtest.py +354 -0
  71. forexsmartbot/services/calculation_engine.py +99 -0
  72. forexsmartbot/services/controller.py +419 -0
  73. forexsmartbot/services/data_streaming.py +116 -0
  74. forexsmartbot/services/enhanced_backtest.py +390 -0
  75. forexsmartbot/services/fast_trading_controller.py +317 -0
  76. forexsmartbot/services/gpu_backtest.py +303 -0
  77. forexsmartbot/services/language_manager.py +621 -0
  78. forexsmartbot/services/notification_service.py +283 -0
  79. forexsmartbot/services/persistence.py +299 -0
  80. forexsmartbot/services/startup_manager.py +200 -0
  81. forexsmartbot/strategies/__init__.py +120 -0
  82. forexsmartbot/strategies/adaptive_trend_flow.py +311 -0
  83. forexsmartbot/strategies/breakout_atr.py +136 -0
  84. forexsmartbot/strategies/ensemble_ml_strategy.py +209 -0
  85. forexsmartbot/strategies/fear_index.py +174 -0
  86. forexsmartbot/strategies/lstm_strategy.py +269 -0
  87. forexsmartbot/strategies/mean_reversion.py +158 -0
  88. forexsmartbot/strategies/ml_adaptive_supertrend.py +272 -0
  89. forexsmartbot/strategies/momentum_breakout.py +183 -0
  90. forexsmartbot/strategies/multi_timeframe_strategy.py +186 -0
  91. forexsmartbot/strategies/news_trading.py +187 -0
  92. forexsmartbot/strategies/rl_strategy.py +277 -0
  93. forexsmartbot/strategies/rsi_reversion.py +162 -0
  94. forexsmartbot/strategies/scalping_ma.py +195 -0
  95. forexsmartbot/strategies/sma_crossover.py +118 -0
  96. forexsmartbot/strategies/svm_strategy.py +199 -0
  97. forexsmartbot/strategies/transformer_strategy.py +233 -0
  98. forexsmartbot/testing/__init__.py +14 -0
  99. forexsmartbot/testing/ci_cd_pipeline.py +286 -0
  100. forexsmartbot/testing/paper_account_tester.py +326 -0
  101. forexsmartbot/testing/strategy_sandbox.py +284 -0
  102. forexsmartbot/ui/__init__.py +8 -0
  103. forexsmartbot/ui/analytics_dialog.py +262 -0
  104. forexsmartbot/ui/analytics_widget.py +68 -0
  105. forexsmartbot/ui/backtest_dialog.py +908 -0
  106. forexsmartbot/ui/charts.py +172 -0
  107. forexsmartbot/ui/cloud_dialog.py +371 -0
  108. forexsmartbot/ui/enhanced_charts.py +491 -0
  109. forexsmartbot/ui/enhanced_main_window.py +2800 -0
  110. forexsmartbot/ui/export_dialog.py +333 -0
  111. forexsmartbot/ui/main_window.py +648 -0
  112. forexsmartbot/ui/marketplace_dialog.py +438 -0
  113. forexsmartbot/ui/monitoring_dialog.py +215 -0
  114. forexsmartbot/ui/monitoring_widget.py +80 -0
  115. forexsmartbot/ui/optimization_dialog.py +603 -0
  116. forexsmartbot/ui/settings_dialog.py +671 -0
  117. forexsmartbot/ui/simple_chart.py +309 -0
  118. forexsmartbot/ui/strategy_builder_dialog.py +595 -0
  119. forexsmartbot/ui/theme.py +109 -0
  120. forexsmartbot/ui/tradingview_chart.py +736 -0
  121. forexsmartbot/utils/__init__.py +42 -0
  122. forexsmartbot/utils/gpu_numpy.py +194 -0
  123. forexsmartbot/utils/gpu_utils.py +260 -0
  124. forexsmartbot-3.2.0.dist-info/METADATA +177 -0
  125. forexsmartbot-3.2.0.dist-info/RECORD +129 -0
  126. forexsmartbot-3.2.0.dist-info/WHEEL +5 -0
  127. forexsmartbot-3.2.0.dist-info/entry_points.txt +2 -0
  128. forexsmartbot-3.2.0.dist-info/licenses/LICENSE +150 -0
  129. forexsmartbot-3.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,304 @@
1
+ """REST API broker implementation for live trading."""
2
+
3
+ import requests
4
+ import hashlib
5
+ import hmac
6
+ import time
7
+ import json
8
+ from typing import Dict, Optional, List
9
+ from datetime import datetime
10
+ from ...core.interfaces import IBroker, Position, Order
11
+
12
+
13
+ class RestBroker(IBroker):
14
+ """REST API broker for live trading with real brokers."""
15
+
16
+ def __init__(self, api_key: str = "", api_secret: str = "",
17
+ base_url: str = "https://api.example.com", account_id: str = None,
18
+ sandbox: bool = True):
19
+ self._api_key = api_key
20
+ self._api_secret = api_secret
21
+ self._base_url = base_url.rstrip('/')
22
+ self._account_id = account_id
23
+ self._sandbox = sandbox
24
+ self._connected = False
25
+ self._session = requests.Session()
26
+ self._session.headers.update({
27
+ 'Content-Type': 'application/json',
28
+ 'User-Agent': 'ForexSmartBot/2.0'
29
+ })
30
+
31
+ def connect(self) -> bool:
32
+ """Connect to the REST API broker."""
33
+ try:
34
+ if not self._api_key or not self._api_secret:
35
+ print("REST broker: API key or secret not provided")
36
+ return False
37
+
38
+ # Test connection with account info
39
+ response = self._make_request('GET', '/v1/accounts')
40
+ if response and response.get('status') == 'success':
41
+ self._connected = True
42
+ print("REST broker: Connected successfully")
43
+ return True
44
+ else:
45
+ # Fallback: try to get balance as connection test
46
+ balance = self.get_balance()
47
+ if balance is not None:
48
+ self._connected = True
49
+ print("REST broker: Connected successfully (fallback)")
50
+ return True
51
+ except Exception as e:
52
+ print(f"REST broker connection failed: {e}")
53
+
54
+ return False
55
+
56
+ def disconnect(self) -> None:
57
+ """Disconnect from the REST API broker."""
58
+ self._connected = False
59
+ if hasattr(self, '_session'):
60
+ self._session.close()
61
+ print("REST broker: Disconnected")
62
+
63
+ def is_connected(self) -> bool:
64
+ """Check if connected to the broker."""
65
+ return self._connected
66
+
67
+ def get_price(self, symbol: str) -> Optional[float]:
68
+ """Get current price for a symbol."""
69
+ try:
70
+ response = self._make_request('GET', f'/v1/prices/{symbol}')
71
+ if response and 'price' in response:
72
+ return float(response['price'])
73
+ except Exception as e:
74
+ print(f"Error getting price for {symbol}: {e}")
75
+ return None
76
+
77
+ def submit_order(self, symbol: str, side: int, quantity: float,
78
+ stop_loss: Optional[float] = None,
79
+ take_profit: Optional[float] = None) -> Optional[str]:
80
+ """Submit an order to the broker."""
81
+ try:
82
+ order_data = {
83
+ 'symbol': symbol,
84
+ 'side': 'buy' if side > 0 else 'sell',
85
+ 'quantity': quantity,
86
+ 'type': 'market'
87
+ }
88
+
89
+ if stop_loss:
90
+ order_data['stop_loss'] = stop_loss
91
+ if take_profit:
92
+ order_data['take_profit'] = take_profit
93
+
94
+ response = self._make_request('POST', '/v1/orders', data=order_data)
95
+ if response and 'order_id' in response:
96
+ print(f"REST broker: Order submitted for {symbol}")
97
+ return response['order_id']
98
+ except Exception as e:
99
+ print(f"Error submitting order: {e}")
100
+ return None
101
+
102
+ def close_all(self, symbol: str) -> bool:
103
+ """Close all positions for a symbol."""
104
+ try:
105
+ response = self._make_request('DELETE', f'/v1/positions/{symbol}')
106
+ success = response and response.get('status') == 'success'
107
+ if success:
108
+ print(f"REST broker: Closed all positions for {symbol}")
109
+ return success
110
+ except Exception as e:
111
+ print(f"Error closing positions for {symbol}: {e}")
112
+ return False
113
+
114
+ def get_positions(self) -> Dict[str, Position]:
115
+ """Get all open positions."""
116
+ try:
117
+ response = self._make_request('GET', '/v1/positions')
118
+ positions = {}
119
+
120
+ if response and 'positions' in response:
121
+ for pos_data in response['positions']:
122
+ position = Position(
123
+ symbol=pos_data['symbol'],
124
+ side=1 if pos_data['side'] == 'long' else -1,
125
+ quantity=pos_data['quantity'],
126
+ entry_price=pos_data['entry_price'],
127
+ current_price=pos_data['current_price'],
128
+ unrealized_pnl=pos_data.get('unrealized_pnl', 0.0)
129
+ )
130
+ positions[position.symbol] = position
131
+
132
+ return positions
133
+ except Exception as e:
134
+ print(f"Error getting positions: {e}")
135
+ return {}
136
+
137
+ def get_balance(self) -> float:
138
+ """Get account balance."""
139
+ try:
140
+ response = self._make_request('GET', '/v1/accounts/balance')
141
+ if response and 'balance' in response:
142
+ return float(response['balance'])
143
+ except Exception as e:
144
+ print(f"Error getting balance: {e}")
145
+ return 0.0
146
+
147
+ def get_equity(self) -> float:
148
+ """Get account equity."""
149
+ try:
150
+ response = self._make_request('GET', '/v1/accounts/equity')
151
+ if response and 'equity' in response:
152
+ return float(response['equity'])
153
+ except Exception as e:
154
+ print(f"Error getting equity: {e}")
155
+ return 0.0
156
+
157
+ def get_orders(self) -> List[Dict]:
158
+ """Get all orders."""
159
+ try:
160
+ response = self._make_request('GET', '/v1/orders')
161
+ if response and 'orders' in response:
162
+ return response['orders']
163
+ except Exception as e:
164
+ print(f"Error getting orders: {e}")
165
+ return []
166
+
167
+ def cancel_order(self, order_id: str) -> bool:
168
+ """Cancel an order."""
169
+ try:
170
+ response = self._make_request('DELETE', f'/v1/orders/{order_id}')
171
+ success = response and response.get('status') == 'success'
172
+ if success:
173
+ print(f"REST broker: Canceled order {order_id}")
174
+ return success
175
+ except Exception as e:
176
+ print(f"Error canceling order {order_id}: {e}")
177
+ return False
178
+
179
+ def get_account_info(self) -> Dict:
180
+ """Get account information."""
181
+ try:
182
+ response = self._make_request('GET', '/v1/accounts/info')
183
+ return response or {}
184
+ except Exception as e:
185
+ print(f"Error getting account info: {e}")
186
+ return {}
187
+
188
+ def _make_request(self, method: str, endpoint: str, data: Dict = None) -> Optional[Dict]:
189
+ """Make an authenticated HTTP request to the broker API."""
190
+ try:
191
+ url = f"{self._base_url}{endpoint}"
192
+
193
+ # Add authentication headers
194
+ timestamp = str(int(time.time() * 1000))
195
+ signature = self._create_signature(method, endpoint, data, timestamp)
196
+
197
+ headers = {
198
+ 'X-API-Key': self._api_key,
199
+ 'X-Timestamp': timestamp,
200
+ 'X-Signature': signature
201
+ }
202
+
203
+ if method == 'GET':
204
+ response = self._session.get(url, headers=headers, timeout=10)
205
+ elif method == 'POST':
206
+ response = self._session.post(url, headers=headers, json=data, timeout=10)
207
+ elif method == 'PUT':
208
+ response = self._session.put(url, headers=headers, json=data, timeout=10)
209
+ elif method == 'DELETE':
210
+ response = self._session.delete(url, headers=headers, timeout=10)
211
+ else:
212
+ raise ValueError(f"Unsupported HTTP method: {method}")
213
+
214
+ response.raise_for_status()
215
+ return response.json()
216
+
217
+ except requests.exceptions.RequestException as e:
218
+ print(f"HTTP request failed: {e}")
219
+ except json.JSONDecodeError as e:
220
+ print(f"JSON decode error: {e}")
221
+ except Exception as e:
222
+ print(f"Request error: {e}")
223
+
224
+ return None
225
+
226
+ def _create_signature(self, method: str, endpoint: str, data: Dict, timestamp: str) -> str:
227
+ """Create HMAC signature for API authentication."""
228
+ try:
229
+ # Create message to sign
230
+ message = f"{method}{endpoint}{timestamp}"
231
+ if data:
232
+ message += json.dumps(data, sort_keys=True)
233
+
234
+ # Create HMAC signature
235
+ signature = hmac.new(
236
+ self._api_secret.encode('utf-8'),
237
+ message.encode('utf-8'),
238
+ hashlib.sha256
239
+ ).hexdigest()
240
+
241
+ return signature
242
+ except Exception as e:
243
+ print(f"Error creating signature: {e}")
244
+ return ""
245
+
246
+ def get_market_data(self, symbol: str, timeframe: str = '1h', limit: int = 100) -> List[Dict]:
247
+ """Get historical market data."""
248
+ try:
249
+ params = {
250
+ 'symbol': symbol,
251
+ 'timeframe': timeframe,
252
+ 'limit': limit
253
+ }
254
+ response = self._make_request('GET', '/v1/market_data', params)
255
+ if response and 'data' in response:
256
+ return response['data']
257
+ except Exception as e:
258
+ print(f"Error getting market data: {e}")
259
+ return []
260
+
261
+ def get_trading_hours(self, symbol: str) -> Dict:
262
+ """Get trading hours for a symbol."""
263
+ try:
264
+ response = self._make_request('GET', f'/v1/trading_hours/{symbol}')
265
+ return response or {}
266
+ except Exception as e:
267
+ print(f"Error getting trading hours: {e}")
268
+ return {}
269
+
270
+ def get_margin_requirements(self, symbol: str) -> Dict:
271
+ """Get margin requirements for a symbol."""
272
+ try:
273
+ response = self._make_request('GET', f'/v1/margin/{symbol}')
274
+ return response or {}
275
+ except Exception as e:
276
+ print(f"Error getting margin requirements: {e}")
277
+ return {}
278
+
279
+ def get_instruments(self) -> List[Dict]:
280
+ """Get available trading instruments."""
281
+ try:
282
+ response = self._make_request('GET', '/v1/instruments')
283
+ if response and 'instruments' in response:
284
+ return response['instruments']
285
+ except Exception as e:
286
+ print(f"Error getting instruments: {e}")
287
+ return []
288
+
289
+ def get_historical_prices(self, symbol: str, start_date: str, end_date: str,
290
+ timeframe: str = '1h') -> List[Dict]:
291
+ """Get historical price data."""
292
+ try:
293
+ params = {
294
+ 'symbol': symbol,
295
+ 'start_date': start_date,
296
+ 'end_date': end_date,
297
+ 'timeframe': timeframe
298
+ }
299
+ response = self._make_request('GET', '/v1/historical_prices', params)
300
+ if response and 'prices' in response:
301
+ return response['prices']
302
+ except Exception as e:
303
+ print(f"Error getting historical prices: {e}")
304
+ return []
@@ -0,0 +1,27 @@
1
+ """Data provider adapters."""
2
+
3
+ from .yfinance_provider import YFinanceProvider
4
+ from .csv_provider import CSVProvider
5
+ from .alpha_vantage_provider import AlphaVantageProvider
6
+ from .oanda_provider import OANDAProvider
7
+ from .mt4_provider import MT4Provider
8
+ from .multi_provider import MultiProvider
9
+ from .dummy_provider import DummyProvider
10
+ from .twelve_data_provider import TwelveDataProvider
11
+ from .stooq_provider import StooqProvider
12
+ from .config import DataProviderConfig
13
+ from ...core.interfaces import IDataProvider
14
+
15
+ __all__ = [
16
+ 'YFinanceProvider',
17
+ 'CSVProvider',
18
+ 'AlphaVantageProvider',
19
+ 'OANDAProvider',
20
+ 'MT4Provider',
21
+ 'MultiProvider',
22
+ 'DummyProvider',
23
+ 'TwelveDataProvider',
24
+ 'StooqProvider',
25
+ 'DataProviderConfig',
26
+ 'IDataProvider',
27
+ ]
@@ -0,0 +1,261 @@
1
+ """Alpha Vantage data provider implementation."""
2
+
3
+ import requests
4
+ import pandas as pd
5
+ from typing import Optional
6
+ from datetime import datetime, timedelta
7
+ import time
8
+ from ...core.interfaces import IDataProvider
9
+
10
+
11
+ class AlphaVantageProvider(IDataProvider):
12
+ """Alpha Vantage data provider for forex data."""
13
+
14
+ def __init__(self, api_key: str = None):
15
+ self.api_key = api_key or "demo" # Use demo key if none provided
16
+ self.base_url = "https://www.alphavantage.co/query"
17
+ self._available = True
18
+ self._rate_limit_delay = 12 # Alpha Vantage free tier: 5 calls per minute
19
+
20
+ def get_data(self, symbol: str, start: str, end: str,
21
+ interval: str = "1h") -> pd.DataFrame:
22
+ """Get historical data from Alpha Vantage."""
23
+ try:
24
+ # Convert forex symbol to Alpha Vantage format
25
+ if not symbol.endswith('=X'):
26
+ symbol = symbol.upper() + "=X"
27
+
28
+ # Remove =X suffix for Alpha Vantage
29
+ av_symbol = symbol.replace('=X', '')
30
+
31
+ # Map interval to Alpha Vantage format
32
+ av_interval = self._map_interval(interval)
33
+
34
+ params = {
35
+ 'function': 'FX_INTRADAY' if av_interval == '1min' else 'FX_DAILY',
36
+ 'from_symbol': av_symbol[:3],
37
+ 'to_symbol': av_symbol[3:],
38
+ 'apikey': self.api_key,
39
+ 'outputsize': 'full'
40
+ }
41
+
42
+ if av_interval == '1min':
43
+ params['interval'] = '1min'
44
+
45
+ response = requests.get(self.base_url, params=params, timeout=5)
46
+ data = response.json()
47
+
48
+ # Debug: Print the response structure
49
+ print(f"Alpha Vantage response for {symbol}: {list(data.keys())}")
50
+
51
+ if 'Error Message' in data:
52
+ print(f"Alpha Vantage error for {symbol}: {data['Error Message']}")
53
+ return pd.DataFrame(columns=['Open', 'High', 'Low', 'Close', 'Volume'])
54
+
55
+ if 'Note' in data:
56
+ print(f"Alpha Vantage rate limit for {symbol}: {data['Note']}")
57
+ time.sleep(self._rate_limit_delay)
58
+ return pd.DataFrame(columns=['Open', 'High', 'Low', 'Close', 'Volume'])
59
+
60
+ if 'Information' in data:
61
+ print(f"Alpha Vantage information for {symbol}: {data['Information']}")
62
+ # Alpha Vantage free tier doesn't support forex - generate sample data
63
+ print(f"Generating sample forex data for {symbol} (Alpha Vantage free tier doesn't support forex)")
64
+ return self._generate_sample_forex_data(symbol, start, end, interval)
65
+
66
+ # Extract time series data - check for different possible keys
67
+ time_series_key = None
68
+ possible_keys = ['Time Series (FX)', 'Time Series (1min)', 'Time Series (5min)', 'Time Series (15min)', 'Time Series (30min)', 'Time Series (60min)']
69
+
70
+ for key in possible_keys:
71
+ if key in data:
72
+ time_series_key = key
73
+ break
74
+
75
+ if time_series_key is None:
76
+ print(f"No time series data for {symbol}. Available keys: {list(data.keys())}")
77
+ return pd.DataFrame(columns=['Open', 'High', 'Low', 'Close', 'Volume'])
78
+
79
+ time_series = data[time_series_key]
80
+
81
+ # Convert to DataFrame
82
+ df = pd.DataFrame.from_dict(time_series, orient='index')
83
+ df.index = pd.to_datetime(df.index)
84
+ df.columns = ['Open', 'High', 'Low', 'Close']
85
+
86
+ # Add volume (synthetic for forex)
87
+ df['Volume'] = 1000000
88
+
89
+ # Filter by date range
90
+ start_date = pd.to_datetime(start)
91
+ end_date = pd.to_datetime(end)
92
+ df = df[(df.index >= start_date) & (df.index <= end_date)]
93
+
94
+ # Resample if needed
95
+ if interval != '1min' and interval != '1d':
96
+ df = self._resample_data(df, interval)
97
+
98
+ return df.sort_index()
99
+
100
+ except Exception as e:
101
+ print(f"Error getting Alpha Vantage data for {symbol}: {e}")
102
+ return pd.DataFrame(columns=['Open', 'High', 'Low', 'Close', 'Volume'])
103
+
104
+ def get_latest_price(self, symbol: str) -> Optional[float]:
105
+ """Get latest price from Alpha Vantage."""
106
+ try:
107
+ # Convert forex symbol to Alpha Vantage format
108
+ if not symbol.endswith('=X'):
109
+ symbol = symbol.upper() + "=X"
110
+
111
+ av_symbol = symbol.replace('=X', '')
112
+
113
+ params = {
114
+ 'function': 'CURRENCY_EXCHANGE_RATE',
115
+ 'from_currency': av_symbol[:3],
116
+ 'to_currency': av_symbol[3:],
117
+ 'apikey': self.api_key
118
+ }
119
+
120
+ response = requests.get(self.base_url, params=params, timeout=5)
121
+ data = response.json()
122
+
123
+ if 'Realtime Currency Exchange Rate' in data:
124
+ rate = data['Realtime Currency Exchange Rate']
125
+ return float(rate['5. Exchange Rate'])
126
+
127
+ return None
128
+
129
+ except Exception as e:
130
+ print(f"Error getting Alpha Vantage latest price for {symbol}: {e}")
131
+ return None
132
+
133
+ def get_historical_data(self, symbol: str, period: str = '1d', interval: str = '1h') -> pd.DataFrame:
134
+ """Get historical data for a symbol with specified period and interval."""
135
+ try:
136
+ # Convert period to start/end dates
137
+ now = datetime.now()
138
+
139
+ if period == '1d':
140
+ end_date = now
141
+ start_date = now - timedelta(days=1)
142
+ elif period == '5d':
143
+ end_date = now
144
+ start_date = now - timedelta(days=5)
145
+ elif period == '1mo':
146
+ end_date = now
147
+ start_date = now - timedelta(days=30)
148
+ elif period == '3mo':
149
+ end_date = now
150
+ start_date = now - timedelta(days=90)
151
+ elif period == '6mo':
152
+ end_date = now
153
+ start_date = now - timedelta(days=180)
154
+ elif period == '1y':
155
+ end_date = now
156
+ start_date = now - timedelta(days=365)
157
+ else:
158
+ # Default to 1 day
159
+ end_date = now
160
+ start_date = now - timedelta(days=1)
161
+
162
+ return self.get_data(symbol, start_date.strftime('%Y-%m-%d'), end_date.strftime('%Y-%m-%d'), interval)
163
+
164
+ except Exception as e:
165
+ print(f"Error getting Alpha Vantage historical data for {symbol}: {e}")
166
+ return pd.DataFrame(columns=['Open', 'High', 'Low', 'Close', 'Volume'])
167
+
168
+ def _map_interval(self, interval: str) -> str:
169
+ """Map interval to Alpha Vantage format."""
170
+ mapping = {
171
+ '1m': '1min',
172
+ '5m': '5min',
173
+ '15m': '15min',
174
+ '30m': '30min',
175
+ '1h': '1min', # Alpha Vantage doesn't have 1h, use 1min
176
+ '4h': '1min', # Alpha Vantage doesn't have 4h, use 1min
177
+ '1d': '1d'
178
+ }
179
+ return mapping.get(interval, '1min')
180
+
181
+ def _resample_data(self, df: pd.DataFrame, interval: str) -> pd.DataFrame:
182
+ """Resample data to desired interval."""
183
+ try:
184
+ if interval == '1h':
185
+ return df.resample('1H').agg({
186
+ 'Open': 'first',
187
+ 'High': 'max',
188
+ 'Low': 'min',
189
+ 'Close': 'last',
190
+ 'Volume': 'sum'
191
+ }).dropna()
192
+ elif interval == '4h':
193
+ return df.resample('4H').agg({
194
+ 'Open': 'first',
195
+ 'High': 'max',
196
+ 'Low': 'min',
197
+ 'Close': 'last',
198
+ 'Volume': 'sum'
199
+ }).dropna()
200
+ else:
201
+ return df
202
+ except Exception:
203
+ return df
204
+
205
+ def _generate_sample_forex_data(self, symbol: str, start: str, end: str, interval: str) -> pd.DataFrame:
206
+ """Generate sample forex data when real data is not available."""
207
+ import numpy as np
208
+
209
+ # Parse dates
210
+ start_date = pd.to_datetime(start)
211
+ end_date = pd.to_datetime(end)
212
+
213
+ # Generate date range based on interval
214
+ if interval == '1h':
215
+ freq = '1H'
216
+ elif interval == '4h':
217
+ freq = '4H'
218
+ elif interval == '1d':
219
+ freq = '1D'
220
+ else:
221
+ freq = '1H'
222
+
223
+ dates = pd.date_range(start=start_date, end=end_date, freq=freq)
224
+
225
+ # Set base price based on symbol
226
+ if 'EUR' in symbol:
227
+ base_price = 1.1800
228
+ elif 'GBP' in symbol:
229
+ base_price = 1.2500
230
+ elif 'JPY' in symbol:
231
+ base_price = 150.0
232
+ elif 'AUD' in symbol:
233
+ base_price = 0.7500
234
+ elif 'CAD' in symbol:
235
+ base_price = 1.3500
236
+ elif 'CHF' in symbol:
237
+ base_price = 0.9200
238
+ elif 'NZD' in symbol:
239
+ base_price = 0.7000
240
+ else:
241
+ base_price = 1.0000
242
+
243
+ # Create realistic price movements
244
+ np.random.seed(42) # For consistent results
245
+ price_changes = np.random.randn(len(dates)) * 0.0005 # Smaller changes for more realistic data
246
+ prices = base_price + np.cumsum(price_changes)
247
+
248
+ # Ensure prices stay within reasonable bounds
249
+ prices = np.clip(prices, base_price * 0.5, base_price * 2.0)
250
+
251
+ return pd.DataFrame({
252
+ 'Open': prices,
253
+ 'High': prices + np.abs(np.random.randn(len(dates))) * 0.0002,
254
+ 'Low': prices - np.abs(np.random.randn(len(dates))) * 0.0002,
255
+ 'Close': prices + np.random.randn(len(dates)) * 0.0001,
256
+ 'Volume': np.random.randint(1000000, 10000000, len(dates))
257
+ }, index=dates)
258
+
259
+ def is_available(self) -> bool:
260
+ """Check if Alpha Vantage is available."""
261
+ return self._available and self.api_key and self.api_key != "demo"