#!/usr/bin/env python import requests import math from sys import exit from datetime import datetime from dotenv import load_dotenv from typing import NoReturn from os import environ from enum import Enum from dataclasses import dataclass load_dotenv() @dataclass(frozen=True) class Indicator: endpoint: str class IndicatorEnum(Enum): RSI = Indicator("rsi") class Interval(Enum): OneMinute = 60 FiveMinutes = 300 FifteenMinutes = 900 ThirtyMinutes = 1800 OneHour = 3600 TwoHours = 7200 FourHours = 14400 TwelveHours = 43200 OneDay = 86400 OneWeek = 604800 class TaapiClient: def __init__(self, api_key: str) -> None: self._api_key: str = api_key self._base_url: str = "https://api.taapi.io" self._session: requests.Session = requests.Session() def __build_indicator_url(self, indicator: Indicator) -> str: return f"{self._base_url}/{indicator.endpoint}" def query_indicator( self, ticker: str, indicator: Indicator, target_date: datetime, interval: str = "1d", results: int = 14, ) -> requests.Response: backtrack_candles: int = self._candles_to_target_date(target_date, interval) target_url: str = self.__build_indicator_url(indicator) params: dict[str, str | int | bool] = { "secret": self._api_key, "symbol": ticker, "interval": interval, "type": "stocks", "gaps": "false", "addResultTimestamp": "true", "backtrack": backtrack_candles, "results": str(results), } response: requests.Response = self._session.get(target_url, params=params) return response @staticmethod def _candles_to_target_date( target_date: datetime, interval: str = "1h", current_time: datetime | None = None, ) -> int: if current_time is None: current_time = datetime.now() # Calculate time difference time_diff: datetime = current_time - target_date time_diff_seconds: float = time_diff.total_seconds() # Parse interval to get candle duration in seconds interval_map: dict[str, int] = { "1m": 60, "5m": 300, "15m": 900, "30m": 1800, "1h": 3600, "2h": 7200, "4h": 14400, "12h": 43200, "1d": 86400, "1w": 604800, } candle_duration_seconds: int = interval_map[interval] # Calculate number of candles (round up) num_candles: int = math.ceil(time_diff_seconds / candle_duration_seconds) return num_candles def close(self) -> None: self._session.close() def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb) -> None: self.close() def is_trading_day(date: datetime) -> bool: return date.weekday() not in [5, 6] def parse_date_yyyymmdd(date_str: str) -> datetime: return datetime.strptime(date_str, "%Y%m%d") def format_date_readable(date: datetime) -> str: return date.strftime("%B %d, %Y") def main() -> NoReturn: api_key = environ.get("API_KEY") if not api_key: print("API_KEY not set") exit(0) date = parse_date_yyyymmdd("20250821") indicator: Indicator = IndicatorEnum.RSI.value ticker = "AAPL" with TaapiClient(api_key) as client: response = client.query_indicator(ticker, indicator, date) if response.status_code == 200: data: dict[str, list[float] | list[int]] = response.json() print(f"{indicator} values for {ticker} on {format_date_readable(date)}:") for rsi_val, ts in zip(data["value"], data["timestamp"]): dt: datetime = datetime.fromtimestamp(ts) if not is_trading_day(dt): continue print(f"{dt.strftime('%Y-%m-%d %H:%M:%S')} | RSI: {rsi_val:.2f}") else: print(f"Error: {response.status_code}") print(response.text) exit(0) if __name__ == "__main__": main()