mirror of
https://gitlab.com/simple-stock-bots/simple-stock-bot.git
synced 2026-06-03 21:00:26 +00:00
Fix bugs in MarketData and CoinGecko handlers
- Change logging.error to logging.debug for response headers in MarketData.get() - Fix MarketData.status() to check its own API instead of CoinGecko uptime monitor - Fix f-string bug in cg_Crypto.cap_reply (market cap was not interpolated) - Add exponential backoff with max 3 retries for CoinGecko 429 errors - Fix formatting issues in cg_Crypto (quote consistency, spacing) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+9
-15
@@ -74,7 +74,7 @@ class MarketData:
|
||||
|
||||
resp = r.get(url, params=params, timeout=timeout, headers=headers)
|
||||
|
||||
logging.error(resp.headers.items())
|
||||
logging.debug(resp.headers.items())
|
||||
|
||||
# Make sure API returned a proper status code
|
||||
try:
|
||||
@@ -133,27 +133,21 @@ class MarketData:
|
||||
self.charts = {}
|
||||
|
||||
def status(self) -> str:
|
||||
# TODO: At the moment this API is poorly documented, this function likely needs to be revisited later.
|
||||
|
||||
"""Check MarketData.app API status by making a test request."""
|
||||
try:
|
||||
# Test the API with a simple request
|
||||
status = r.get(
|
||||
"https://stats.uptimerobot.com/api/getMonitorList/6Kv3zIow0A",
|
||||
"https://api.marketdata.app/v1/stocks/quotes/AAPL/",
|
||||
timeout=5,
|
||||
)
|
||||
status.raise_for_status()
|
||||
return f"MarketData.app API is responding OK with status {status.status_code} in {status.elapsed.total_seconds():.2f} seconds."
|
||||
except r.HTTPError:
|
||||
return f"API returned an HTTP error code {status.status_code} in {status.elapsed.total_seconds()} seconds."
|
||||
return f"MarketData.app API returned an HTTP error code {status.status_code} in {status.elapsed.total_seconds():.2f} seconds."
|
||||
except r.Timeout:
|
||||
return "API timed out before it was able to give status. This is likely due to a surge in usage or a complete outage."
|
||||
|
||||
statusJSON = status.json()
|
||||
|
||||
if statusJSON["status"] == "ok":
|
||||
return (
|
||||
f"CoinGecko API responded that it was OK with a {status.status_code} in {status.elapsed.total_seconds()} seconds."
|
||||
)
|
||||
else:
|
||||
return f"MarketData.app is currently reporting the following status: {statusJSON['status']}"
|
||||
return "MarketData.app API timed out before it could respond. This is likely due to a surge in usage or a complete outage."
|
||||
except Exception as e:
|
||||
return f"MarketData.app API check failed: {str(e)}"
|
||||
|
||||
def price_reply(self, symbol: Stock) -> str:
|
||||
"""Returns price movement of Stock for the last market day, or after hours.
|
||||
|
||||
+22
-13
@@ -31,15 +31,24 @@ class cg_Crypto:
|
||||
# This results in a rate limit of 15 requests per minute for each bot.
|
||||
# Given this, the rate limit effectively becomes 1 request every 4 seconds for each bot.
|
||||
@rate_limited(0.25)
|
||||
def get(self, endpoint, params: dict = {}, timeout=10) -> dict:
|
||||
def get(self, endpoint, params: dict = {}, timeout=10, retry_count=0, max_retries=3) -> dict:
|
||||
url = "https://api.coingecko.com/api/v3" + endpoint
|
||||
resp = r.get(url, params=params, timeout=timeout)
|
||||
# Make sure API returned a proper status code
|
||||
|
||||
if resp.status_code == 429:
|
||||
log.warning(f"CoinGecko returned 429 - Too Many Requests for endpoint: {endpoint}. Sleeping and trying again.")
|
||||
time.sleep(10)
|
||||
return self.get(endpoint=endpoint, params=params, timeout=timeout)
|
||||
if retry_count >= max_retries:
|
||||
log.error(f"CoinGecko 429 retry limit ({max_retries}) exceeded for endpoint: {endpoint}")
|
||||
return {}
|
||||
|
||||
backoff_time = (2**retry_count) * 10 # Exponential backoff: 10s, 20s, 40s
|
||||
log.warning(
|
||||
f"CoinGecko returned 429 - Too Many Requests for endpoint: {endpoint}. Retry {retry_count + 1}/{max_retries} after {backoff_time}s."
|
||||
)
|
||||
time.sleep(backoff_time)
|
||||
return self.get(
|
||||
endpoint=endpoint, params=params, timeout=timeout, retry_count=retry_count + 1, max_retries=max_retries
|
||||
)
|
||||
|
||||
try:
|
||||
resp.raise_for_status()
|
||||
@@ -214,14 +223,14 @@ class cg_Crypto:
|
||||
},
|
||||
):
|
||||
return f"""
|
||||
[{data['name']}]({data['links']['homepage'][0]}) Statistics:
|
||||
Market Cap: ${data['market_data']['market_cap'][self.vs_currency]:,}
|
||||
Market Cap Ranking: {data.get('market_cap_rank',"Not Available")}
|
||||
[{data["name"]}]({data["links"]["homepage"][0]}) Statistics:
|
||||
Market Cap: ${data["market_data"]["market_cap"][self.vs_currency]:,}
|
||||
Market Cap Ranking: {data.get("market_cap_rank", "Not Available")}
|
||||
CoinGecko Scores:
|
||||
Overall: {data.get('coingecko_score','Not Available')}
|
||||
Development: {data.get('developer_score','Not Available')}
|
||||
Community: {data.get('community_score','Not Available')}
|
||||
Public Interest: {data.get('public_interest_score','Not Available')}
|
||||
Overall: {data.get("coingecko_score", "Not Available")}
|
||||
Development: {data.get("developer_score", "Not Available")}
|
||||
Community: {data.get("community_score", "Not Available")}
|
||||
Public Interest: {data.get("public_interest_score", "Not Available")}
|
||||
"""
|
||||
else:
|
||||
return f"{symbol.symbol} returned an error."
|
||||
@@ -261,7 +270,7 @@ class cg_Crypto:
|
||||
|
||||
message = (
|
||||
f"The current price of {coin.name} is $**{price:,}** and"
|
||||
+ " its market cap is $**{cap:,.2f}** {self.vs_currency.upper()}"
|
||||
+ f" its market cap is $**{cap:,.2f}** {self.vs_currency.upper()}"
|
||||
)
|
||||
|
||||
else:
|
||||
@@ -374,7 +383,7 @@ class cg_Crypto:
|
||||
p["usd_24h_change"] = 0
|
||||
|
||||
replies.append(
|
||||
f"{coin.name}: ${p.get('usd',0):,} and has moved {p.get('usd_24h_change',0.0):.2f}% in the past 24 hours."
|
||||
f"{coin.name}: ${p.get('usd', 0):,} and has moved {p.get('usd_24h_change', 0.0):.2f}% in the past 24 hours."
|
||||
)
|
||||
|
||||
return replies
|
||||
|
||||
Reference in New Issue
Block a user