1
0
mirror of https://gitlab.com/simple-stock-bots/simple-stock-bot.git synced 2025-06-16 07:16:40 +00:00

Merge branch '36-add-rate-limiting' into 'master'

Resolve "Add rate limiting"

Closes #36

See merge request simple-stock-bots/simple-stock-bot!57
This commit is contained in:
Anson Biggs 2023-10-16 05:54:35 +00:00
commit 686cf889c1
5 changed files with 48 additions and 16 deletions

View File

@ -1,8 +1,8 @@
import datetime as dt import datetime as dt
import logging import logging
import os import os
from typing import Dict
from collections import OrderedDict from collections import OrderedDict
from typing import Dict
import humanize import humanize
import pandas as pd import pandas as pd

View File

@ -7,6 +7,7 @@ import schedule
from markdownify import markdownify from markdownify import markdownify
from common.Symbol import Coin from common.Symbol import Coin
from common.utilities import rate_limited
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -24,6 +25,11 @@ class cg_Crypto:
self.get_symbol_list() self.get_symbol_list()
schedule.every().day.do(self.get_symbol_list) schedule.every().day.do(self.get_symbol_list)
# Coingecko's rate limit is 30 requests per minute.
# Since there are two bots sharing the same IP, we allocate half of that limit to each bot.
# 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) -> dict:
url = "https://api.coingecko.com/api/v3" + endpoint url = "https://api.coingecko.com/api/v3" + endpoint
resp = r.get(url, params=params, timeout=timeout) resp = r.get(url, params=params, timeout=timeout)

View File

@ -4,4 +4,5 @@ markdownify==0.11.6
mplfinance==0.12.10b0 mplfinance==0.12.10b0
pandas==2.1.1 pandas==2.1.1
requests==2.31.0 requests==2.31.0
rush==2021.4.0
schedule==1.2.1 schedule==1.2.1

31
common/utilities.py Normal file
View File

@ -0,0 +1,31 @@
import time
import logging
log = logging.getLogger(__name__)
def rate_limited(max_per_second):
"""
Decorator that ensures the wrapped function is called at most `max_per_second` times per second.
"""
min_interval = 1.0 / max_per_second
def decorate(func):
last_called = [0.0]
def rate_limited_function(*args, **kwargs):
elapsed = time.time() - last_called[0]
left_to_wait = min_interval - elapsed
if left_to_wait > 0:
log.info(f"Rate limit exceeded. Waiting for {left_to_wait:.2f} seconds.")
time.sleep(left_to_wait)
ret = func(*args, **kwargs)
last_called[0] = time.time()
return ret
return rate_limited_function
return decorate

View File

@ -11,27 +11,21 @@ import traceback
from uuid import uuid4 from uuid import uuid4
import mplfinance as mpf import mplfinance as mpf
import telegram from T_info import T_info
from telegram import (
InlineQueryResultArticle,
InputTextMessageContent,
LabeledPrice,
Update,
)
import telegram
from common.symbol_router import Router
from telegram import InlineQueryResultArticle, InputTextMessageContent, LabeledPrice, Update
from telegram.ext import ( from telegram.ext import (
Application, Application,
CommandHandler, CommandHandler,
InlineQueryHandler,
PreCheckoutQueryHandler,
MessageHandler,
filters,
ContextTypes, ContextTypes,
InlineQueryHandler,
MessageHandler,
PreCheckoutQueryHandler,
filters,
) )
from common.symbol_router import Router
from T_info import T_info
# Enable logging # Enable logging
logging.basicConfig(format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO) logging.basicConfig(format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO)