File size: 10,395 Bytes
d05d6ed |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
import requests
import json
import time
from datetime import datetime, timedelta
import pandas as pd
from tabulate import tabulate
def fetch_financial_data(symbol="USDCAD=X", start_date=None, end_date=None, interval="1m", days_to_fetch=1):
"""
Fetch and process financial data from Yahoo Finance.
Parameters:
symbol (str): The ticker symbol (e.g., "USDCAD=X" for USD/CAD exchange rate)
start_date (datetime): Start date for the data (optional, defaults to 1 day ago if not provided)
end_date (datetime): End date for the data (optional, defaults to current time if not provided)
interval (str): Data interval - Options include "1m", "5m", "15m", "30m", "60m", "1d", "1wk", "1mo"
days_to_fetch (int): Number of days to fetch data for if end_date is not specified
Returns:
dict: A dictionary containing:
- 'data': pandas DataFrame with the processed financial data
- 'meta': dictionary with meta information
- 'table': formatted table string
- 'meta_info': formatted meta information string
- 'stats': formatted summary statistics string
- 'success': boolean indicating if the operation was successful
- 'message': status message
"""
# Set default dates if not provided
if start_date is None:
start_date = datetime.now() - timedelta(days=days_to_fetch)
if end_date is None:
end_date = datetime.now()
result = {
'data': None,
'meta': None,
'table': None,
'meta_info': None,
'stats': None,
'success': False,
'message': ""
}
try:
# Convert datetime to Unix timestamp
period1 = int(time.mktime(start_date.timetuple()))
period2 = int(time.mktime(end_date.timetuple()))
# Construct the URL
url = f"https://query2.finance.yahoo.com/v8/finance/chart/{symbol}"
# Parameters for the request
params = {
"period1": period1,
"period2": period2,
"interval": interval,
"includePrePost": "true",
"events": "div|split|earn",
"lang": "en-US",
"region": "US",
"source": "cosaic"
}
# Headers to mimic a browser request
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
}
# Make the request
response = requests.get(url, params=params, headers=headers)
if response.status_code == 200:
data = response.json()
# Process the data
if not data or 'chart' not in data or 'result' not in data['chart'] or not data['chart']['result']:
result['message'] = "No valid data to process"
return result
# Extract the relevant data
api_result = data['chart']['result'][0]
meta = api_result['meta']
timestamps = api_result['timestamp']
quotes = api_result['indicators']['quote'][0]
# Create a DataFrame
df = pd.DataFrame({
'timestamp': timestamps,
'open': quotes['open'],
'high': quotes['high'],
'low': quotes['low'],
'close': quotes['close'],
'volume': quotes['volume']
})
# Convert timestamps to datetime
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='s')
# Calculate additional metrics
df['range'] = df['high'] - df['low']
df['change'] = df['close'] - df['open']
df['change_pct'] = (df['change'] / df['open'] * 100).round(3)
# Mark if the candle is bullish (close > open) or bearish (close < open)
df['trend'] = ['Bullish' if c > o else 'Bearish' if c < o else 'Neutral'
for c, o in zip(df['close'], df['open'])]
# Format the DataFrame for display
display_df = df.copy()
# Format the timestamp
display_df['Date'] = display_df['timestamp'].dt.strftime('%Y-%m-%d')
display_df['Time'] = display_df['timestamp'].dt.strftime('%H:%M:%S')
# Round numerical values to 4 decimal places
for col in ['open', 'high', 'low', 'close', 'range']:
if col in display_df.columns:
display_df[col] = display_df[col].round(4)
# Add color indicators for trend (will be visible in HTML output)
display_df['Change'] = display_df['change'].round(4)
display_df['% Change'] = display_df['change_pct'].round(2).astype(str) + '%'
# Select columns for display
table_df = display_df[['Date', 'Time', 'open', 'high', 'low', 'close',
'range', 'Change', '% Change', 'trend']]
# Rename columns for better readability
table_df.columns = ['Date', 'Time', 'Open', 'High', 'Low', 'Close',
'Range', 'Change', '% Change', 'Trend']
# Create the table using tabulate
table = tabulate(table_df, headers='keys', tablefmt='fancy_grid', showindex=False)
# Add a title
title = f"{symbol} Price Data ({interval} interval)"
title_line = "=" * len(title)
formatted_table = f"\n{title_line}\n{title}\n{title_line}\n\n{table}"
# Format meta information
meta_str = "META INFORMATION\n===============\n"
# Extract key information
key_info = {
'Currency': meta.get('currency'),
'Symbol': meta.get('symbol'),
'Exchange Name': meta.get('exchangeName'),
'Full Exchange Name': meta.get('fullExchangeName'),
'Instrument Type': meta.get('instrumentType'),
'First Trade Date': datetime.fromtimestamp(meta.get('firstTradeDate', 0)).strftime('%Y-%m-%d') if 'firstTradeDate' in meta else 'N/A',
'Regular Market Time': datetime.fromtimestamp(meta.get('regularMarketTime', 0)).strftime('%Y-%m-%d %H:%M:%S') if 'regularMarketTime' in meta else 'N/A',
'Timezone': meta.get('timezone'),
'Exchange Timezone': meta.get('exchangeTimezoneName'),
'Regular Market Price': meta.get('regularMarketPrice'),
'52 Week High': meta.get('fiftyTwoWeekHigh'),
'52 Week Low': meta.get('fiftyTwoWeekLow'),
'Day High': meta.get('regularMarketDayHigh'),
'Day Low': meta.get('regularMarketDayLow'),
'Volume': meta.get('regularMarketVolume'),
'Long Name': meta.get('longName'),
'Short Name': meta.get('shortName'),
'Previous Close': meta.get('previousClose'),
'Scale': meta.get('scale'),
'Price Hint': meta.get('priceHint')
}
# Format the key_info as a string
for key, value in key_info.items():
if isinstance(value, float):
meta_str += f"{key}: {value:.4f}\n"
else:
meta_str += f"{key}: {value}\n"
# Add trading period information
if 'currentTradingPeriod' in meta:
meta_str += "\nCURRENT TRADING PERIOD\n=====================\n"
for period_type, period_info in meta['currentTradingPeriod'].items():
start_time = datetime.fromtimestamp(period_info.get('start', 0)).strftime('%Y-%m-%d %H:%M:%S')
end_time = datetime.fromtimestamp(period_info.get('end', 0)).strftime('%Y-%m-%d %H:%M:%S')
meta_str += f"{period_type.capitalize()} Period: {start_time} to {end_time} ({period_info.get('timezone', 'Unknown')})\n"
# Generate summary statistics
stats = {
'Symbol': symbol,
'Long Name': meta.get('longName', symbol),
'Period Start': df['timestamp'].min().strftime("%Y-%m-%d %H:%M:%S"),
'Period End': df['timestamp'].max().strftime("%Y-%m-%d %H:%M:%S"),
'Data Points': len(df),
'Opening Price': df['open'].iloc[0],
'Closing Price': df['close'].iloc[-1],
'Current Price': meta.get('regularMarketPrice', df['close'].iloc[-1]),
'Overall Change': df['close'].iloc[-1] - df['open'].iloc[0],
'Overall % Change': ((df['close'].iloc[-1] / df['open'].iloc[0]) - 1) * 100,
'Highest Price': df['high'].max(),
'Lowest Price': df['low'].min(),
'Average Price': df[['open', 'high', 'low', 'close']].mean().mean(),
'Price Range': df['high'].max() - df['low'].min(),
'Day High (Meta)': meta.get('regularMarketDayHigh'),
'Day Low (Meta)': meta.get('regularMarketDayLow'),
'52 Week High': meta.get('fiftyTwoWeekHigh'),
'52 Week Low': meta.get('fiftyTwoWeekLow'),
'Bullish Candles': (df['trend'] == 'Bullish').sum(),
'Bearish Candles': (df['trend'] == 'Bearish').sum(),
'Neutral Candles': (df['trend'] == 'Neutral').sum()
}
# Format the stats as a string
stats_str = "SUMMARY STATISTICS\n==================\n"
for key, value in stats.items():
if isinstance(value, float):
stats_str += f"{key}: {value:.4f}\n"
else:
stats_str += f"{key}: {value}\n"
# Populate result dictionary
result['data'] = df
result['meta'] = meta
result['table'] = formatted_table
result['meta_info'] = meta_str
result['stats'] = stats_str
result['success'] = True
result['message'] = f"Successfully processed {len(df)} data points."
return result
else:
result['message'] = f"Failed to fetch data: Status code {response.status_code}"
return result
except Exception as e:
result['message'] = f"Error: {str(e)}"
return result
|