Skip to content

Commit 851c0b3

Browse files
lev-corruptedclaude
andcommitted
Fix code formatting to pass CI/CD checks
## Changes - Ran black formatter on all Python files - Ran isort with --profile black for import sorting - All 26 files now pass black and isort checks ## Formatting Applied - Consistent code style with black (line length, quotes, etc.) - Properly sorted imports with isort (black-compatible profile) - Ensures CI/CD lint checks will pass ## Files Formatted - tradingview_mcp/server.py - tradingview_mcp/api/*.py - tradingview_mcp/indicators/*.py - tradingview_mcp/pine_script/*.py - tradingview_mcp/utils/*.py This fixes the GitHub Actions "Code Quality Checks" job failure. 🤖 Generated with Claude Code https://claude.com/claude-code Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 228d837 commit 851c0b3

24 files changed

Lines changed: 2441 additions & 1017 deletions

tradingview_mcp/api/alpha_vantage.py

Lines changed: 45 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
11
"""Alpha Vantage API client with rate limiting, caching, and retry logic."""
22

3+
import logging
34
import os
45
import time
5-
import logging
6-
from typing import Dict, Any, Optional
76
from collections import deque
87
from datetime import datetime
8+
from typing import Any, Dict, Optional
99

1010
import requests
11-
from requests.exceptions import RequestException, Timeout, ConnectionError
11+
from requests.exceptions import ConnectionError, RequestException, Timeout
1212

1313
from ..config import (
1414
API_REQUEST_TIMEOUT,
15-
CACHE_TTL_QUOTES,
1615
CACHE_TTL_HISTORICAL,
17-
RATE_LIMIT_CALLS_PER_MINUTE,
18-
TIMEFRAME_MAP,
19-
ERROR_RATE_LIMIT,
20-
ERROR_NO_DATA,
16+
CACHE_TTL_QUOTES,
2117
ERROR_API_KEY_MISSING,
2218
ERROR_NETWORK,
19+
ERROR_NO_DATA,
20+
ERROR_RATE_LIMIT,
21+
RATE_LIMIT_CALLS_PER_MINUTE,
22+
TIMEFRAME_MAP,
2323
)
24-
from ..utils.formatters import format_error_response, safe_float
2524
from ..utils.asset_detector import format_pair_for_alpha_vantage
25+
from ..utils.formatters import format_error_response, safe_float
2626
from .cache import ResponseCache
2727

2828
logger = logging.getLogger(__name__)
@@ -36,6 +36,7 @@ def retry_with_exponential_backoff(max_retries: int = 3, base_delay: float = 1.0
3636
max_retries: Maximum number of retry attempts
3737
base_delay: Base delay in seconds (doubles each retry)
3838
"""
39+
3940
def decorator(func):
4041
def wrapper(*args, **kwargs):
4142
retries = 0
@@ -45,7 +46,9 @@ def wrapper(*args, **kwargs):
4546
except (Timeout, ConnectionError) as e:
4647
retries += 1
4748
if retries >= max_retries:
48-
logger.error(f"Max retries ({max_retries}) reached for {func.__name__}")
49+
logger.error(
50+
f"Max retries ({max_retries}) reached for {func.__name__}"
51+
)
4952
raise
5053

5154
delay = base_delay * (2 ** (retries - 1))
@@ -59,7 +62,9 @@ def wrapper(*args, **kwargs):
5962
logger.error(f"Request failed with non-retryable error: {e}")
6063
raise
6164
return func(*args, **kwargs)
65+
6266
return wrapper
67+
6368
return decorator
6469

6570

@@ -129,8 +134,7 @@ def __init__(self, api_key: Optional[str] = None):
129134
self.base_url = "https://www.alphavantage.co/query"
130135
self.cache = ResponseCache()
131136
self.rate_limiter = RateLimiter(
132-
max_calls=RATE_LIMIT_CALLS_PER_MINUTE,
133-
time_window=60
137+
max_calls=RATE_LIMIT_CALLS_PER_MINUTE, time_window=60
134138
)
135139
self._total_calls = 0
136140

@@ -154,18 +158,18 @@ def _make_request(self, params: Dict[str, str]) -> Dict[str, Any]:
154158
logger.warning(f"Rate limit reached. Need to wait {wait_time:.1f}s")
155159
return format_error_response(
156160
ERROR_RATE_LIMIT,
157-
suggestion=f"Wait {int(wait_time) + 1} seconds before retrying"
161+
suggestion=f"Wait {int(wait_time) + 1} seconds before retrying",
158162
)
159163

160164
# Add API key to params
161165
params["apikey"] = self.api_key
162166

163167
try:
164-
logger.debug(f"API request: {params.get('function')} for {params.get('symbol', 'N/A')}")
168+
logger.debug(
169+
f"API request: {params.get('function')} for {params.get('symbol', 'N/A')}"
170+
)
165171
response = requests.get(
166-
self.base_url,
167-
params=params,
168-
timeout=API_REQUEST_TIMEOUT
172+
self.base_url, params=params, timeout=API_REQUEST_TIMEOUT
169173
)
170174
response.raise_for_status()
171175

@@ -179,37 +183,27 @@ def _make_request(self, params: Dict[str, str]) -> Dict[str, Any]:
179183
if "Error Message" in data:
180184
logger.error(f"API error: {data['Error Message']}")
181185
return format_error_response(
182-
data["Error Message"],
183-
suggestion="Check if the symbol is valid"
186+
data["Error Message"], suggestion="Check if the symbol is valid"
184187
)
185188

186189
if "Note" in data:
187190
logger.warning("API rate limit message received")
188-
return format_error_response(
189-
ERROR_RATE_LIMIT,
190-
details=data["Note"]
191-
)
191+
return format_error_response(ERROR_RATE_LIMIT, details=data["Note"])
192192

193193
return data
194194

195195
except requests.Timeout:
196196
logger.error("API request timeout")
197197
return format_error_response(
198198
"Request timeout",
199-
suggestion="Try again or check your network connection"
199+
suggestion="Try again or check your network connection",
200200
)
201201
except requests.RequestException as e:
202202
logger.error(f"Network error: {str(e)}")
203-
return format_error_response(
204-
ERROR_NETWORK,
205-
details=str(e)
206-
)
203+
return format_error_response(ERROR_NETWORK, details=str(e))
207204
except Exception as e:
208205
logger.error(f"Unexpected error: {str(e)}")
209-
return format_error_response(
210-
"Unexpected error occurred",
211-
details=str(e)
212-
)
206+
return format_error_response("Unexpected error occurred", details=str(e))
213207

214208
def get_forex_quote(self, pair: str) -> Dict[str, Any]:
215209
"""
@@ -248,10 +242,14 @@ def get_forex_quote(self, pair: str) -> Dict[str, Any]:
248242
"symbol": pair,
249243
"asset_type": "forex",
250244
"price": safe_float(rate_data.get("5. Exchange Rate", 0)),
251-
"bid": safe_float(rate_data.get("8. Bid Price", rate_data.get("5. Exchange Rate", 0))),
252-
"ask": safe_float(rate_data.get("9. Ask Price", rate_data.get("5. Exchange Rate", 0))),
245+
"bid": safe_float(
246+
rate_data.get("8. Bid Price", rate_data.get("5. Exchange Rate", 0))
247+
),
248+
"ask": safe_float(
249+
rate_data.get("9. Ask Price", rate_data.get("5. Exchange Rate", 0))
250+
),
253251
"timestamp": rate_data.get("6. Last Refreshed", ""),
254-
"timezone": rate_data.get("7. Time Zone", "UTC")
252+
"timezone": rate_data.get("7. Time Zone", "UTC"),
255253
}
256254
self.cache.set(cache_key, result, CACHE_TTL_QUOTES)
257255
return result
@@ -274,7 +272,7 @@ def get_crypto_quote(self, symbol: str) -> Dict[str, Any]:
274272
return cached
275273

276274
# Remove USD suffix if present
277-
crypto_base = symbol.replace('USD', '') if symbol.endswith('USD') else symbol
275+
crypto_base = symbol.replace("USD", "") if symbol.endswith("USD") else symbol
278276

279277
params = {
280278
"function": "CURRENCY_EXCHANGE_RATE",
@@ -291,7 +289,10 @@ def get_crypto_quote(self, symbol: str) -> Dict[str, Any]:
291289
rate_data = data["Realtime Currency Exchange Rate"]
292290
price = safe_float(rate_data.get("5. Exchange Rate", 0))
293291

294-
from ..config import CRYPTO_SPREAD_MULTIPLIER_BID, CRYPTO_SPREAD_MULTIPLIER_ASK
292+
from ..config import (
293+
CRYPTO_SPREAD_MULTIPLIER_ASK,
294+
CRYPTO_SPREAD_MULTIPLIER_BID,
295+
)
295296

296297
result = {
297298
"symbol": symbol,
@@ -300,7 +301,7 @@ def get_crypto_quote(self, symbol: str) -> Dict[str, Any]:
300301
"bid": price * CRYPTO_SPREAD_MULTIPLIER_BID,
301302
"ask": price * CRYPTO_SPREAD_MULTIPLIER_ASK,
302303
"timestamp": rate_data.get("6. Last Refreshed", ""),
303-
"timezone": rate_data.get("7. Time Zone", "UTC")
304+
"timezone": rate_data.get("7. Time Zone", "UTC"),
304305
}
305306
self.cache.set(cache_key, result, CACHE_TTL_QUOTES)
306307
return result
@@ -357,10 +358,7 @@ def get_stock_quote(self, symbol: str) -> Dict[str, Any]:
357358
return format_error_response(ERROR_NO_DATA, symbol=symbol)
358359

359360
def get_historical_data_forex(
360-
self,
361-
pair: str,
362-
timeframe: str = "1h",
363-
outputsize: str = "compact"
361+
self, pair: str, timeframe: str = "1h", outputsize: str = "compact"
364362
) -> Dict[str, Any]:
365363
"""Get historical forex data."""
366364
cache_key = f"forex_hist_{pair}_{timeframe}_{outputsize}"
@@ -411,10 +409,7 @@ def get_historical_data_forex(
411409
return format_error_response(ERROR_NO_DATA, symbol=pair)
412410

413411
def get_historical_data_stock(
414-
self,
415-
symbol: str,
416-
timeframe: str = "1h",
417-
outputsize: str = "compact"
412+
self, symbol: str, timeframe: str = "1h", outputsize: str = "compact"
418413
) -> Dict[str, Any]:
419414
"""
420415
Get historical stock data.
@@ -468,10 +463,7 @@ def get_historical_data_stock(
468463
return format_error_response(ERROR_NO_DATA, symbol=symbol)
469464

470465
def get_historical_data_crypto(
471-
self,
472-
symbol: str,
473-
timeframe: str = "1h",
474-
outputsize: str = "compact"
466+
self, symbol: str, timeframe: str = "1h", outputsize: str = "compact"
475467
) -> Dict[str, Any]:
476468
"""
477469
Get historical crypto data.
@@ -490,7 +482,7 @@ def get_historical_data_crypto(
490482
return cached
491483

492484
# Remove USD suffix if present
493-
crypto_base = symbol.replace('USD', '') if symbol.endswith('USD') else symbol
485+
crypto_base = symbol.replace("USD", "") if symbol.endswith("USD") else symbol
494486

495487
interval = TIMEFRAME_MAP.get(timeframe, "60min")
496488

@@ -536,6 +528,6 @@ def get_stats(self) -> Dict[str, Any]:
536528
"rate_limit": {
537529
"max_per_minute": RATE_LIMIT_CALLS_PER_MINUTE,
538530
"current_window_calls": len(self.rate_limiter.calls),
539-
"wait_time_seconds": self.rate_limiter.wait_time()
540-
}
531+
"wait_time_seconds": self.rate_limiter.wait_time(),
532+
},
541533
}

tradingview_mcp/api/cache.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
"""Simple in-memory cache with TTL and LRU eviction support."""
22

3-
import time
43
import logging
5-
from typing import Optional, Any, Dict
6-
from dataclasses import dataclass
4+
import time
75
from collections import OrderedDict
6+
from dataclasses import dataclass
7+
from typing import Any, Dict, Optional
88

99
logger = logging.getLogger(__name__)
1010

1111

1212
@dataclass
1313
class CacheEntry:
1414
"""Cache entry with value and expiration time."""
15+
1516
value: Any
1617
expires_at: float
1718

@@ -113,8 +114,7 @@ def cleanup_expired(self) -> int:
113114
"""
114115
current_time = time.time()
115116
expired_keys = [
116-
key for key, entry in self._cache.items()
117-
if current_time > entry.expires_at
117+
key for key, entry in self._cache.items() if current_time > entry.expires_at
118118
]
119119

120120
for key in expired_keys:
@@ -142,5 +142,5 @@ def get_stats(self) -> Dict[str, Any]:
142142
"misses": self._misses,
143143
"evictions": self._evictions,
144144
"hit_rate": f"{hit_rate:.1f}%",
145-
"utilization": f"{len(self._cache) / self._max_size * 100:.1f}%"
145+
"utilization": f"{len(self._cache) / self._max_size * 100:.1f}%",
146146
}

0 commit comments

Comments
 (0)