You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
844 lines
28 KiB
Python
844 lines
28 KiB
Python
"""
|
|
Stock Oracle Python Client
|
|
|
|
A comprehensive Python client library for accessing the Stock Oracle API.
|
|
Provides easy-to-use methods for retrieving financial, price, and ETF holdings data.
|
|
|
|
Usage:
|
|
from stock_oracle_client import StockOracleClient
|
|
|
|
client = StockOracleClient("http://localhost:18001")
|
|
|
|
# Get financial data using period
|
|
data = client.get_financial_data("AAPL", period="1y")
|
|
|
|
# Get price data using date range
|
|
prices = client.get_price_data("MSFT", start_date="2024-01-01", end_date="2024-12-31")
|
|
|
|
# Get ETF holdings
|
|
etf = client.get_etf_holdings("QQQ", as_of_date="2024-01-01")
|
|
"""
|
|
|
|
import requests
|
|
import json
|
|
from datetime import datetime, date, timedelta
|
|
from typing import Dict, List, Optional, Union, Any, Tuple
|
|
import logging
|
|
from enum import Enum
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class StockOracleError(Exception):
|
|
"""Base exception for Stock Oracle client errors"""
|
|
pass
|
|
|
|
|
|
class StockOracleAPIError(StockOracleError):
|
|
"""API-specific errors"""
|
|
def __init__(self, message: str, status_code: int = None, response_data: Dict = None):
|
|
super().__init__(message)
|
|
self.status_code = status_code
|
|
self.response_data = response_data
|
|
|
|
|
|
class ETFDataNotAvailableError(StockOracleError):
|
|
"""ETF data not available for requested date"""
|
|
def __init__(self, message: str, availability_info: Dict = None):
|
|
super().__init__(message)
|
|
self.availability_info = availability_info
|
|
|
|
|
|
class PriceInterval(Enum):
|
|
"""Valid price data intervals"""
|
|
ONE_MINUTE = "1m"
|
|
TWO_MINUTES = "2m"
|
|
FIVE_MINUTES = "5m"
|
|
FIFTEEN_MINUTES = "15m"
|
|
THIRTY_MINUTES = "30m"
|
|
SIXTY_MINUTES = "60m"
|
|
NINETY_MINUTES = "90m"
|
|
ONE_HOUR = "1h"
|
|
ONE_DAY = "1d"
|
|
FIVE_DAYS = "5d"
|
|
ONE_WEEK = "1wk"
|
|
ONE_MONTH = "1mo"
|
|
THREE_MONTHS = "3mo"
|
|
|
|
|
|
class PeriodType(Enum):
|
|
"""Period types for financial data"""
|
|
QUARTERLY = "quarterly"
|
|
ANNUAL = "annual"
|
|
ALL = "all"
|
|
|
|
|
|
class StockOracleClient:
|
|
"""
|
|
Python client for Stock Oracle API
|
|
|
|
Args:
|
|
base_url: Base URL of the Stock Oracle API (e.g., "http://localhost:18001")
|
|
api_key: Optional API key for authentication (not implemented yet)
|
|
timeout: Request timeout in seconds (default: 30)
|
|
auto_retry: Automatically retry failed requests (default: True)
|
|
max_retries: Maximum number of retries (default: 3)
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
base_url: str,
|
|
api_key: Optional[str] = None,
|
|
timeout: int = 30,
|
|
auto_retry: bool = True,
|
|
max_retries: int = 3
|
|
):
|
|
self.base_url = base_url.rstrip('/')
|
|
self.api_key = api_key
|
|
self.timeout = timeout
|
|
self.auto_retry = auto_retry
|
|
self.max_retries = max_retries
|
|
self.session = requests.Session()
|
|
|
|
# Set up session headers
|
|
self.session.headers.update({
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/json',
|
|
'User-Agent': 'StockOracle-Python-Client/2.0.0'
|
|
})
|
|
|
|
if api_key:
|
|
self.session.headers['Authorization'] = f'Bearer {api_key}'
|
|
|
|
def _make_request(self, method: str, endpoint: str, **kwargs) -> Dict:
|
|
"""
|
|
Make HTTP request to API with retry logic
|
|
|
|
Args:
|
|
method: HTTP method (GET, POST, etc.)
|
|
endpoint: API endpoint path
|
|
**kwargs: Additional arguments for requests
|
|
|
|
Returns:
|
|
Parsed JSON response
|
|
|
|
Raises:
|
|
StockOracleAPIError: On API errors
|
|
"""
|
|
url = f"{self.base_url}{endpoint}"
|
|
kwargs.setdefault('timeout', self.timeout)
|
|
|
|
retries = 0
|
|
while retries <= (self.max_retries if self.auto_retry else 0):
|
|
try:
|
|
response = self.session.request(method, url, **kwargs)
|
|
response.raise_for_status()
|
|
return response.json()
|
|
|
|
except requests.exceptions.HTTPError as e:
|
|
try:
|
|
error_data = response.json()
|
|
except (ValueError, AttributeError):
|
|
error_data = {"message": response.text}
|
|
|
|
# Don't retry on client errors (4xx)
|
|
if response.status_code < 500:
|
|
raise StockOracleAPIError(
|
|
f"API Error: {error_data.get('message', error_data.get('detail', str(e)))}",
|
|
status_code=response.status_code,
|
|
response_data=error_data
|
|
)
|
|
|
|
# Retry on server errors (5xx)
|
|
retries += 1
|
|
if retries > self.max_retries:
|
|
raise StockOracleAPIError(
|
|
f"API Error after {self.max_retries} retries: {error_data.get('message', str(e))}",
|
|
status_code=response.status_code,
|
|
response_data=error_data
|
|
)
|
|
|
|
except requests.exceptions.RequestException as e:
|
|
retries += 1
|
|
if retries > self.max_retries:
|
|
raise StockOracleAPIError(f"Request Error after {self.max_retries} retries: {str(e)}")
|
|
|
|
def _format_date(self, date_obj: Union[str, date, datetime]) -> str:
|
|
"""Format date object to API-compatible string"""
|
|
if isinstance(date_obj, str):
|
|
return date_obj
|
|
elif isinstance(date_obj, datetime):
|
|
return date_obj.date().isoformat()
|
|
elif isinstance(date_obj, date):
|
|
return date_obj.isoformat()
|
|
else:
|
|
raise ValueError(f"Invalid date type: {type(date_obj)}")
|
|
|
|
# ============= Health & Status =============
|
|
|
|
def get_health(self) -> Dict:
|
|
"""
|
|
Get API health status
|
|
|
|
Returns:
|
|
Health status information
|
|
"""
|
|
return self._make_request('GET', '/api/v1/health')
|
|
|
|
def get_detailed_health(self) -> Dict:
|
|
"""
|
|
Get detailed health status including database and cache
|
|
|
|
Returns:
|
|
Detailed health status
|
|
"""
|
|
return self._make_request('GET', '/api/v1/health/detailed')
|
|
|
|
|
|
# ============= Financial Data API =============
|
|
|
|
def get_financial_data(
|
|
self,
|
|
ticker: str,
|
|
start_date: Optional[Union[str, date, datetime]] = None,
|
|
end_date: Optional[Union[str, date, datetime]] = None,
|
|
quarters: Optional[List[str]] = None,
|
|
period: Optional[str] = None,
|
|
period_type: Union[str, PeriodType] = PeriodType.ALL,
|
|
include_metrics: bool = True,
|
|
force_refresh: bool = False
|
|
) -> Dict:
|
|
"""
|
|
Get financial data for a stock ticker
|
|
|
|
⚠️ IMPORTANT: Period parameter now uses yesterday as end date to ensure data availability
|
|
|
|
Args:
|
|
ticker: Stock ticker symbol (e.g., "AAPL")
|
|
start_date: Start date (string, date, or datetime)
|
|
end_date: End date (string, date, or datetime)
|
|
quarters: List of quarters (e.g., ["2024Q1", "2024Q2"])
|
|
period: Period string (e.g., "1d", "3m", "2y") - automatically excludes today's data
|
|
period_type: Type of periods (PeriodType enum or string)
|
|
include_metrics: Include calculated metrics
|
|
force_refresh: Force refresh from SEC data
|
|
|
|
Returns:
|
|
Financial data response
|
|
|
|
Note:
|
|
Must specify exactly one of: (start_date + end_date), quarters, or period
|
|
"""
|
|
if isinstance(period_type, PeriodType):
|
|
period_type = period_type.value
|
|
|
|
data = {
|
|
"ticker": ticker,
|
|
"period_type": period_type,
|
|
"include_metrics": include_metrics,
|
|
"force_refresh": force_refresh
|
|
}
|
|
|
|
# Add time parameters
|
|
if period:
|
|
data["period"] = period
|
|
elif quarters:
|
|
data["quarters"] = quarters
|
|
elif start_date and end_date:
|
|
data["start_date"] = self._format_date(start_date)
|
|
data["end_date"] = self._format_date(end_date)
|
|
else:
|
|
# Default to last year
|
|
data["period"] = "1y"
|
|
|
|
return self._make_request('POST', '/api/v1/financial/data', json=data)
|
|
|
|
# ============= Price Data API =============
|
|
|
|
def get_price_data(
|
|
self,
|
|
ticker: str,
|
|
start_date: Optional[Union[str, date, datetime]] = None,
|
|
end_date: Optional[Union[str, date, datetime]] = None,
|
|
quarters: Optional[List[str]] = None,
|
|
period: Optional[str] = None,
|
|
interval: Union[str, PriceInterval] = PriceInterval.ONE_DAY,
|
|
force_refresh: bool = False
|
|
) -> Dict:
|
|
"""
|
|
Get price data for a stock ticker
|
|
|
|
⚠️ IMPORTANT: Period parameter now uses yesterday as end date to ensure data availability
|
|
|
|
Args:
|
|
ticker: Stock ticker symbol (e.g., "AAPL")
|
|
start_date: Start date (string, date, or datetime)
|
|
end_date: End date (string, date, or datetime)
|
|
quarters: List of quarters (e.g., ["2024Q1", "2024Q2"])
|
|
period: Period string (e.g., "1d", "3m", "2y") - automatically excludes today's data
|
|
interval: Data interval (PriceInterval enum or string)
|
|
force_refresh: Force refresh from Yahoo Finance
|
|
|
|
Returns:
|
|
Price data response
|
|
|
|
Note:
|
|
Must specify exactly one of: (start_date + end_date), quarters, or period
|
|
"""
|
|
if isinstance(interval, PriceInterval):
|
|
interval = interval.value
|
|
|
|
data = {
|
|
"ticker": ticker,
|
|
"interval": interval,
|
|
"force_refresh": force_refresh
|
|
}
|
|
|
|
# Add time parameters
|
|
if period:
|
|
data["period"] = period
|
|
elif quarters:
|
|
data["quarters"] = quarters
|
|
elif start_date and end_date:
|
|
data["start_date"] = self._format_date(start_date)
|
|
data["end_date"] = self._format_date(end_date)
|
|
else:
|
|
# Default to last year
|
|
data["period"] = "1y"
|
|
|
|
return self._make_request('POST', '/api/v1/price/data', json=data)
|
|
|
|
# ============= Bulk Data API =============
|
|
|
|
def get_bulk_financial_data(
|
|
self,
|
|
tickers: List[str],
|
|
start_date: Optional[Union[str, date, datetime]] = None,
|
|
end_date: Optional[Union[str, date, datetime]] = None,
|
|
quarters: Optional[List[str]] = None,
|
|
period: Optional[str] = None,
|
|
period_type: Union[str, PeriodType] = PeriodType.ALL,
|
|
include_metrics: bool = True,
|
|
force_refresh: bool = False
|
|
) -> Dict:
|
|
"""
|
|
Get financial data for multiple stock tickers
|
|
|
|
⚠️ IMPORTANT: Period parameter now uses yesterday as end date to ensure data availability
|
|
|
|
Args:
|
|
tickers: List of stock ticker symbols (max 500)
|
|
start_date: Start date (string, date, or datetime)
|
|
end_date: End date (string, date, or datetime)
|
|
quarters: List of quarters (e.g., ["2024Q1", "2024Q2"])
|
|
period: Period string (e.g., "1d", "3m", "2y") - automatically excludes today's data
|
|
period_type: Type of periods (PeriodType enum or string)
|
|
include_metrics: Include calculated metrics
|
|
force_refresh: Force refresh from SEC data
|
|
|
|
Returns:
|
|
Bulk financial data response
|
|
|
|
Note:
|
|
Must specify exactly one of: (start_date + end_date), quarters, or period
|
|
"""
|
|
if isinstance(period_type, PeriodType):
|
|
period_type = period_type.value
|
|
|
|
data = {
|
|
"tickers": tickers,
|
|
"period_type": period_type,
|
|
"include_metrics": include_metrics,
|
|
"force_refresh": force_refresh
|
|
}
|
|
|
|
# Add time parameters
|
|
if period:
|
|
data["period"] = period
|
|
elif quarters:
|
|
data["quarters"] = quarters
|
|
elif start_date and end_date:
|
|
data["start_date"] = self._format_date(start_date)
|
|
data["end_date"] = self._format_date(end_date)
|
|
else:
|
|
# Default to last year
|
|
data["period"] = "1y"
|
|
|
|
return self._make_request('POST', '/api/v1/financial/data/bulk', json=data)
|
|
|
|
def get_bulk_price_data(
|
|
self,
|
|
tickers: List[str],
|
|
start_date: Optional[Union[str, date, datetime]] = None,
|
|
end_date: Optional[Union[str, date, datetime]] = None,
|
|
quarters: Optional[List[str]] = None,
|
|
period: Optional[str] = None,
|
|
interval: Union[str, PriceInterval] = PriceInterval.ONE_DAY,
|
|
force_refresh: bool = False
|
|
) -> Dict:
|
|
"""
|
|
Get price data for multiple stock tickers
|
|
|
|
⚠️ IMPORTANT: Period parameter now uses yesterday as end date to ensure data availability
|
|
|
|
Args:
|
|
tickers: List of stock ticker symbols (max 500)
|
|
start_date: Start date (string, date, or datetime)
|
|
end_date: End date (string, date, or datetime)
|
|
quarters: List of quarters (e.g., ["2024Q1", "2024Q2"])
|
|
period: Period string (e.g., "1d", "3m", "2y") - automatically excludes today's data
|
|
interval: Data interval (PriceInterval enum or string)
|
|
force_refresh: Force refresh from Yahoo Finance
|
|
|
|
Returns:
|
|
Bulk price data response
|
|
|
|
Note:
|
|
Must specify exactly one of: (start_date + end_date), quarters, or period
|
|
"""
|
|
if isinstance(interval, PriceInterval):
|
|
interval = interval.value
|
|
|
|
data = {
|
|
"tickers": tickers,
|
|
"interval": interval,
|
|
"force_refresh": force_refresh
|
|
}
|
|
|
|
# Add time parameters
|
|
if period:
|
|
data["period"] = period
|
|
elif quarters:
|
|
data["quarters"] = quarters
|
|
elif start_date and end_date:
|
|
data["start_date"] = self._format_date(start_date)
|
|
data["end_date"] = self._format_date(end_date)
|
|
else:
|
|
# Default to last year
|
|
data["period"] = "1y"
|
|
|
|
return self._make_request('POST', '/api/v1/price/data/bulk', json=data)
|
|
|
|
|
|
# ============= News & Social Media API =============
|
|
|
|
def get_news_social_data(
|
|
self,
|
|
ticker: str,
|
|
days_back: int = 7,
|
|
max_articles: int = 20,
|
|
max_social_posts: int = 15,
|
|
include_social: bool = True
|
|
) -> Dict:
|
|
"""
|
|
Get news and social media data for a stock ticker
|
|
|
|
Args:
|
|
ticker: Stock ticker symbol (e.g., "AAPL")
|
|
days_back: Number of days to look back (default: 7)
|
|
max_articles: Maximum number of news articles (default: 20)
|
|
max_social_posts: Maximum number of social posts (default: 15)
|
|
include_social: Include social media data (default: True)
|
|
|
|
Returns:
|
|
News and social media data response
|
|
"""
|
|
params = {
|
|
"days_back": days_back,
|
|
"max_articles": max_articles,
|
|
"max_social_posts": max_social_posts,
|
|
"include_social": str(include_social).lower()
|
|
}
|
|
|
|
return self._make_request('GET', f'/api/v1/news/{ticker}', params=params)
|
|
|
|
def get_news_only(
|
|
self,
|
|
ticker: str,
|
|
days_back: int = 7,
|
|
max_articles: int = 30
|
|
) -> Dict:
|
|
"""
|
|
Get news data only (faster response)
|
|
|
|
Args:
|
|
ticker: Stock ticker symbol (e.g., "AAPL")
|
|
days_back: Number of days to look back (default: 7)
|
|
max_articles: Maximum number of news articles (default: 30)
|
|
|
|
Returns:
|
|
News data response
|
|
"""
|
|
params = {
|
|
"days_back": days_back,
|
|
"max_articles": max_articles
|
|
}
|
|
|
|
return self._make_request('GET', f'/api/v1/news/{ticker}/news-only', params=params)
|
|
|
|
def get_social_only(
|
|
self,
|
|
ticker: str,
|
|
days_back: int = 7,
|
|
max_social_posts: int = 20
|
|
) -> Dict:
|
|
"""
|
|
Get social media data only
|
|
|
|
Args:
|
|
ticker: Stock ticker symbol (e.g., "AAPL")
|
|
days_back: Number of days to look back (default: 7)
|
|
max_social_posts: Maximum number of social posts (default: 20)
|
|
|
|
Returns:
|
|
Social media data response
|
|
"""
|
|
params = {
|
|
"days_back": days_back,
|
|
"max_social_posts": max_social_posts
|
|
}
|
|
|
|
return self._make_request('GET', f'/api/v1/news/{ticker}/social-only', params=params)
|
|
|
|
# ============= Metadata & Catalog =============
|
|
|
|
def get_data_catalog(self) -> Dict:
|
|
"""
|
|
Get data field catalog
|
|
|
|
Returns:
|
|
Data catalog with field descriptions
|
|
"""
|
|
return self._make_request('GET', '/api/v1/metadata/catalog')
|
|
|
|
def get_error_logs(
|
|
self,
|
|
limit: int = 100,
|
|
offset: int = 0,
|
|
min_level: str = "ERROR"
|
|
) -> Dict:
|
|
"""
|
|
Get error logs (admin)
|
|
|
|
Args:
|
|
limit: Number of logs to retrieve
|
|
offset: Pagination offset
|
|
min_level: Minimum log level (ERROR, WARNING, INFO)
|
|
|
|
Returns:
|
|
Error logs
|
|
"""
|
|
params = {
|
|
"limit": limit,
|
|
"offset": offset,
|
|
"min_level": min_level
|
|
}
|
|
return self._make_request('GET', '/api/v1/admin/errors', params=params)
|
|
|
|
# ============= Migration & Admin =============
|
|
|
|
def migrate_data(
|
|
self,
|
|
source_url: str,
|
|
api_key: str,
|
|
tickers: Optional[List[str]] = None,
|
|
start_date: Optional[Union[str, date, datetime]] = None,
|
|
end_date: Optional[Union[str, date, datetime]] = None
|
|
) -> Dict:
|
|
"""
|
|
Migrate data from another Stock Oracle instance
|
|
|
|
Args:
|
|
source_url: Source API URL
|
|
api_key: API key for migration
|
|
tickers: Specific tickers to migrate (optional)
|
|
start_date: Start date for migration (optional)
|
|
end_date: End date for migration (optional)
|
|
|
|
Returns:
|
|
Migration status
|
|
"""
|
|
data = {
|
|
"source_url": source_url,
|
|
"api_key": api_key
|
|
}
|
|
|
|
if tickers:
|
|
data["tickers"] = tickers
|
|
if start_date:
|
|
data["start_date"] = self._format_date(start_date)
|
|
if end_date:
|
|
data["end_date"] = self._format_date(end_date)
|
|
|
|
# Server expects X-API-Key header (dependency verify_migration_key)
|
|
headers = {"X-API-Key": api_key}
|
|
return self._make_request('POST', '/api/v1/admin/migrate', json=data, headers=headers)
|
|
|
|
# ============= Utility Methods =============
|
|
|
|
def search_tickers(self, query: str) -> List[str]:
|
|
"""
|
|
Search for ticker symbols (client-side)
|
|
|
|
Args:
|
|
query: Search query
|
|
|
|
Returns:
|
|
List of matching ticker symbols
|
|
"""
|
|
# This is a placeholder - you might want to implement a real search
|
|
# against a ticker database or API endpoint
|
|
common_tickers = [
|
|
"AAPL", "MSFT", "GOOGL", "AMZN", "META", "TSLA", "NVDA",
|
|
"QQQ", "SPY", "IWM", "EFA", "EEM", "VTI", "VOO", "ARKK"
|
|
]
|
|
query = query.upper()
|
|
return [t for t in common_tickers if query in t]
|
|
|
|
def validate_ticker(self, ticker: str) -> bool:
|
|
"""
|
|
Validate if a ticker exists
|
|
|
|
Args:
|
|
ticker: Ticker symbol to validate
|
|
|
|
Returns:
|
|
True if ticker is valid
|
|
"""
|
|
try:
|
|
# Try to get minimal data to validate ticker
|
|
response = self.get_financial_data(
|
|
ticker,
|
|
period="1d",
|
|
include_metrics=False
|
|
)
|
|
return 'error' not in response
|
|
except:
|
|
return False
|
|
|
|
def get_latest_filing_date(self, ticker: str) -> Optional[str]:
|
|
"""
|
|
Get the latest SEC filing date for a ticker
|
|
|
|
Args:
|
|
ticker: Stock ticker symbol
|
|
|
|
Returns:
|
|
Latest filing date as string or None
|
|
"""
|
|
try:
|
|
data = self.get_financial_data(ticker, period="1d")
|
|
if data.get('financial_data'):
|
|
return data['financial_data'][0].get('date')
|
|
except:
|
|
pass
|
|
return None
|
|
|
|
|
|
# Convenience functions for quick access
|
|
def get_financial_data(ticker: str, period: str = "1y", base_url: str = "http://localhost:18001") -> Dict:
|
|
"""
|
|
Quick function to get financial data
|
|
|
|
Args:
|
|
ticker: Stock ticker symbol
|
|
period: Period string (e.g., "1d", "3m", "2y")
|
|
base_url: API base URL
|
|
|
|
Returns:
|
|
Financial data
|
|
"""
|
|
client = StockOracleClient(base_url)
|
|
return client.get_financial_data(ticker, period=period)
|
|
|
|
|
|
def get_price_data(ticker: str, period: str = "1y", base_url: str = "http://localhost:18001") -> Dict:
|
|
"""
|
|
Quick function to get price data
|
|
|
|
Args:
|
|
ticker: Stock ticker symbol
|
|
period: Period string (e.g., "1d", "3m", "2y")
|
|
base_url: API base URL
|
|
|
|
Returns:
|
|
Price data
|
|
"""
|
|
client = StockOracleClient(base_url)
|
|
return client.get_price_data(ticker, period=period)
|
|
|
|
|
|
def get_etf_holdings(ticker: str, as_of_date: Optional[str] = None, base_url: str = "http://localhost:18001") -> Dict:
|
|
"""
|
|
Quick function to get ETF holdings
|
|
|
|
Args:
|
|
ticker: ETF ticker symbol
|
|
as_of_date: Optional date for historical data
|
|
base_url: API base URL
|
|
|
|
Returns:
|
|
ETF holdings data
|
|
"""
|
|
client = StockOracleClient(base_url)
|
|
return client.get_etf_holdings(ticker, as_of_date=as_of_date)
|
|
|
|
|
|
def get_news_social_data(
|
|
ticker: str,
|
|
days_back: int = 7,
|
|
max_articles: int = 20,
|
|
include_social: bool = True,
|
|
base_url: str = "http://localhost:18001"
|
|
) -> Dict:
|
|
"""
|
|
Quick function to get news and social media data
|
|
|
|
Args:
|
|
ticker: Stock ticker symbol
|
|
days_back: Number of days to look back
|
|
max_articles: Maximum number of news articles
|
|
include_social: Include social media data
|
|
base_url: API base URL
|
|
|
|
Returns:
|
|
News and social media data
|
|
"""
|
|
client = StockOracleClient(base_url)
|
|
return client.get_news_social_data(ticker, days_back, max_articles, include_social=include_social)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# Example usage
|
|
client = StockOracleClient("http://localhost:18001")
|
|
|
|
try:
|
|
# Test health
|
|
health = client.get_health()
|
|
print("API Health:", health["status"])
|
|
|
|
print("\n" + "="*50)
|
|
print("FINANCIAL DATA EXAMPLES")
|
|
print("="*50)
|
|
|
|
# Get financial data using period (POST endpoint)
|
|
print("\n=== Financial Data (Period: 1y) ===")
|
|
financial = client.get_financial_data("AAPL", period="1y")
|
|
print(f"Company: {financial['company']['name']}")
|
|
print(f"Data points: {len(financial['financial_data'])}")
|
|
if financial['financial_data']:
|
|
latest = financial['financial_data'][0]
|
|
print(f"Latest filing: {latest['date']}")
|
|
if 'metrics' in latest:
|
|
print(f" P/E Ratio: {latest['metrics'].get('pe_ratio', 'N/A')}")
|
|
print(f" ROE: {latest['metrics'].get('return_on_equity', 'N/A')}")
|
|
|
|
print("\n" + "="*50)
|
|
print("PRICE DATA EXAMPLES")
|
|
print("="*50)
|
|
|
|
# Get price data using date range
|
|
print("\n=== Price Data (Date Range) ===")
|
|
from datetime import date
|
|
price = client.get_price_data(
|
|
"AAPL",
|
|
start_date=date(2024, 1, 1),
|
|
end_date=date(2024, 12, 31),
|
|
interval=PriceInterval.ONE_DAY
|
|
)
|
|
print(f"Ticker: {price['ticker']}")
|
|
print(f"Price points: {len(price['price_data'])}")
|
|
if price['price_data']:
|
|
latest = price['price_data'][-1]
|
|
print(f"Latest date: {latest['date']}")
|
|
print(f" Close: ${latest['close']:.2f}")
|
|
print(f" Volume: {latest['volume']:,}")
|
|
|
|
print("\n" + "="*50)
|
|
print("BULK DATA EXAMPLES")
|
|
print("="*50)
|
|
|
|
# Bulk financial data
|
|
print("\n=== Bulk Financial Data ===")
|
|
bulk_financial = client.get_bulk_financial_data(
|
|
["AAPL", "MSFT", "GOOGL"],
|
|
period="3m",
|
|
period_type=PeriodType.QUARTERLY
|
|
)
|
|
print(f"Requested: {len(bulk_financial['requested_tickers'])} tickers")
|
|
print(f"Successful: {len(bulk_financial['results'])} tickers")
|
|
if bulk_financial.get('errors'):
|
|
print(f"Failed: {len(bulk_financial['errors'])} tickers")
|
|
|
|
|
|
print("\n" + "="*50)
|
|
print("NEWS & SOCIAL MEDIA EXAMPLES")
|
|
print("="*50)
|
|
|
|
# Get news and social media data
|
|
print("\n=== News & Social Media Data ===")
|
|
news_social = client.get_news_social_data("AAPL", days_back=7, max_articles=10, include_social=True)
|
|
print(f"Ticker: {news_social['ticker']}")
|
|
print(f"Retrieved at: {news_social['retrieved_at']}")
|
|
print(f"Total news articles: {news_social['news']['total_articles']}")
|
|
print(f"Total social posts: {news_social['social_media']['total_posts']}")
|
|
print(f"Total items: {news_social['summary']['total_items']}")
|
|
|
|
if news_social['news']['articles']:
|
|
print("\nLatest news article:")
|
|
article = news_social['news']['articles'][0]
|
|
print(f" Title: {article['title'][:80]}...")
|
|
print(f" Source: {article['source']}")
|
|
print(f" Published: {article.get('published_at', 'N/A')}")
|
|
|
|
if news_social['social_media']['posts']:
|
|
print("\nLatest social post:")
|
|
post = news_social['social_media']['posts'][0]
|
|
print(f" Title: {post['title'][:80]}...")
|
|
print(f" Platform: {post['platform']}")
|
|
print(f" Score: {post.get('score', 'N/A')}")
|
|
|
|
# Get news only (faster)
|
|
print("\n=== News Only (Faster) ===")
|
|
news_only = client.get_news_only("TSLA", days_back=3, max_articles=5)
|
|
print(f"News articles for TSLA: {news_only['news']['total_articles']}")
|
|
|
|
# Get social media only
|
|
print("\n=== Social Media Only ===")
|
|
social_only = client.get_social_only("NVDA", days_back=5, max_social_posts=10)
|
|
print(f"Social posts for NVDA: {social_only['social_media']['total_posts']}")
|
|
|
|
print("\n" + "="*50)
|
|
print("ADDITIONAL FEATURES")
|
|
print("="*50)
|
|
|
|
# Get supported ETFs
|
|
print("\n=== Supported ETFs ===")
|
|
supported = client.get_supported_etfs()
|
|
print(f"Total supported ETFs: {supported['total_etfs']}")
|
|
print(f"Examples: {', '.join(supported['supported_tickers'][:10])}...")
|
|
|
|
# Search tickers (client-side example)
|
|
print("\n=== Ticker Search ===")
|
|
results = client.search_tickers("AA")
|
|
print(f"Search 'AA' results: {results}")
|
|
|
|
# Validate ticker
|
|
print("\n=== Ticker Validation ===")
|
|
is_valid = client.validate_ticker("AAPL")
|
|
print(f"AAPL is valid: {is_valid}")
|
|
is_valid = client.validate_ticker("INVALID123")
|
|
print(f"INVALID123 is valid: {is_valid}")
|
|
|
|
except StockOracleAPIError as e:
|
|
print(f"\nAPI Error: {e}")
|
|
print(f"Status Code: {e.status_code}")
|
|
print(f"Response: {e.response_data}")
|
|
except ETFDataNotAvailableError as e:
|
|
print(f"\nETF Data Not Available: {e}")
|
|
if e.availability_info:
|
|
print(f"Availability Info: {json.dumps(e.availability_info, indent=2)}")
|
|
except Exception as e:
|
|
print(f"\nError: {e}")
|
|
import traceback
|
|
traceback.print_exc() |