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