goldhand 20.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
goldhand-20.3/PKG-INFO ADDED
@@ -0,0 +1,199 @@
1
+ Metadata-Version: 2.4
2
+ Name: goldhand
3
+ Version: 20.3
4
+ Summary: A package working with financial data
5
+ Home-page: https://github.com/misrori/goldhand
6
+ Author: Mihaly
7
+ Author-email: ormraat.pte@gmail.com
8
+ License: MIT
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: pandas
11
+ Requires-Dist: plotly
12
+ Requires-Dist: scipy
13
+ Requires-Dist: numpy
14
+ Requires-Dist: numba
15
+ Requires-Dist: requests
16
+ Requires-Dist: tqdm
17
+ Requires-Dist: yfinance<1.0
18
+ Requires-Dist: ipython
19
+ Dynamic: author
20
+ Dynamic: author-email
21
+ Dynamic: description
22
+ Dynamic: description-content-type
23
+ Dynamic: home-page
24
+ Dynamic: license
25
+ Dynamic: requires-dist
26
+ Dynamic: summary
27
+
28
+
29
+ # [Goldhand](https://github.com/misrori/goldhand)
30
+ The ultimate python package to work with stock and crypto data
31
+
32
+ ```bash
33
+ pip install goldhand
34
+ ```
35
+
36
+
37
+ # [TradingView]((https://github.com/misrori/goldhand/tw.py))
38
+
39
+
40
+ ```python
41
+ from goldhand import *
42
+
43
+ # tradingView data
44
+ tw = Tw()
45
+
46
+ # data frame of the stocks
47
+ tw.stock
48
+
49
+ # data frame of the top 300 crypto currency
50
+ tw.crypto
51
+
52
+ # data frame of the top 3000 etf
53
+ tw.etf
54
+
55
+ ```
56
+
57
+ ```python
58
+ # Get a plot of the stock to see the location in the sector
59
+ tw.get_sec_plot('AMD').show()
60
+
61
+ ```
62
+ ![Sector plot](https://github.com/misrori/goldhand/blob/main/img/sec_plot.png?raw=true "Sector location of FDS")
63
+
64
+
65
+ ```python
66
+ # Get a plot of the stock to see the location in the industry
67
+ tw.get_sec_plot('AMD').show()
68
+
69
+ ```
70
+ ![Sector plot](https://github.com/misrori/goldhand/blob/main/img/ind_plot.png?raw=true "Sector location of FDS")
71
+
72
+
73
+
74
+ # [Goldhand class](https://github.com/misrori/goldhand/stock.py)
75
+
76
+ The `GoldHand` class is a part of the `goldhand` Python package, which provides functionality for working with stock and crypto data. This class allows users to retrieve detailed information and charts for a specific stock.
77
+
78
+
79
+
80
+ ```python
81
+
82
+ # Get a detailed chart of a stock AMD
83
+ ticker = "AMD"
84
+ t = GoldHand(ticker)
85
+ t.df.tail().T
86
+ ```
87
+ ![data structure](https://github.com/misrori/goldhand/blob/main/img/df_structure.png?raw=true "data structure")
88
+
89
+
90
+ ```python
91
+
92
+ # Get a detailed chart of a stock AMD
93
+ ticker = "TSLA"
94
+ t = GoldHand(ticker)
95
+ t.plotly_last_year(tw.get_plotly_title(ticker)).show()
96
+
97
+ ## Stock Chart
98
+
99
+ ```
100
+ !['Detailed stock chart'](https://github.com/misrori/goldhand/blob/main/img/stock_plot.png?raw=true "Stock plot")
101
+
102
+ ```python
103
+
104
+ # Get a detailed chart of a crypto
105
+ ticker = "BTC-USD"
106
+ t = GoldHand(ticker)
107
+ t.plotly_last_year(tw.get_plotly_title(ticker)).show()
108
+
109
+
110
+ ```
111
+ !['Detailed crypto chart'](https://github.com/misrori/goldhand/blob/main/img/crypto_plot.png?raw=true "crypto plot")
112
+
113
+
114
+ ## [GoldHand Line indicator](https://gist.github.com/misrori/ae77642c31fb1a973c7627cc077a1df2)
115
+
116
+
117
+ ```python
118
+ ticker = "TSLA"
119
+ t = GoldHand(ticker)
120
+ t.plot_goldhand_line(tw.get_plotly_title(ticker)).show()
121
+
122
+ ```
123
+ !['Detailed crypto chart'](https://github.com/misrori/goldhand/blob/main/img/goldhand_line_plot.png?raw=true "crypto plot")
124
+
125
+
126
+
127
+ # [Backtest](https://github.com/misrori/goldhand/backtest.py)
128
+
129
+ The Backtest class is a powerful tool for evaluating the performance of trading strategies using historical data. It allows you to simulate trades and calculate various performance metrics to assess the profitability and risk of your strategy.
130
+
131
+ It takes a data and a function and display the trades.
132
+
133
+
134
+
135
+ ```python
136
+ ticker= 'TSLA'
137
+ data = GoldHand(ticker).df
138
+ backtest = Backtest( data, rsi_strategy, plot_title=tw.get_plotly_title(ticker), buy_threshold=30, sell_threshold=70)
139
+ backtest.summarize_strategy()
140
+
141
+ ```
142
+ `summarize_strategy` will show the trades summary, a plot with trades and the trades in DataFrame.
143
+
144
+
145
+ !['Summary of trades'](https://github.com/misrori/goldhand/blob/main/img/tradesdf.png?raw=true "summary of trades")
146
+
147
+ !['Trades plot'](https://github.com/misrori/goldhand/blob/main/img/backtest_plot.png?raw=true "trades plot")
148
+
149
+ !['Trades'](https://github.com/misrori/goldhand/blob/main/img/trades_summary.png?raw=true "trades df")
150
+
151
+
152
+ # Strategys
153
+
154
+ ## [RSI Strategy](https://github.com/misrori/goldhand/strategy_rsi.py)
155
+
156
+ ```python
157
+ """
158
+ RSI strategy for backtesting with Backtest class
159
+
160
+ Parameters:
161
+ - data: pandas DataFrame with columns: date, open, high, low, close, volume and rsi
162
+ - buy_threshold: int, default 30, buy when RSI is below this value
163
+ - sell_threshold: int, default 70, sell when RSI is above this value
164
+ """
165
+ backtest = Backtest( data, rsi_strategy, plot_title=tw.get_plotly_title(ticker), buy_threshold=30, sell_threshold=70)
166
+
167
+ ```
168
+
169
+
170
+ ```python
171
+ ticker = 'TSLA'
172
+ p = show_indicator_rsi_strategy(ticker = ticker, buy_threshold=30, sell_threshold=70, plot_title=tw.get_plotly_title(ticker), add_strategy_summary=True)
173
+ ```
174
+ !['RSI strategy plot'](https://github.com/misrori/goldhand/blob/main/img/rsi_strategy_plot.png?raw=true "RSI Strategy plot")
175
+
176
+ ## [GoldHand Line indicator](https://github.com/misrori/goldhand/strategy_goldhand_line.py)
177
+
178
+ ```python
179
+ """
180
+ This function implements the GoldHandLine strategy.
181
+
182
+ Parameters:
183
+ - data (pandas DataFrame) : The DataFrame containing the data.
184
+ - buy_at (str): The color of the line to buy at. Default is 'gold'.
185
+ - sell_at (str): The color of the line to sell at. Default is 'grey'.
186
+
187
+ """
188
+ backtest = Backtest( data, goldhand_line_strategy,)
189
+
190
+ ```
191
+
192
+ ```python
193
+ ticker = 'BTC-USD'
194
+ show_indicator_goldhand_line_strategy(ticker = ticker, plot_title=tw.get_plotly_title(ticker), buy_at='gold', sell_at='blue', add_strategy_summary=True)
195
+ ```
196
+ !['GoldHand Line strategy plot'](https://github.com/misrori/goldhand/blob/main/img/goldhand_line_strategy_plot.png?raw=true "GoldHand Line Strategy plot")
197
+
198
+
199
+
@@ -0,0 +1,172 @@
1
+
2
+ # [Goldhand](https://github.com/misrori/goldhand)
3
+ The ultimate python package to work with stock and crypto data
4
+
5
+ ```bash
6
+ pip install goldhand
7
+ ```
8
+
9
+
10
+ # [TradingView]((https://github.com/misrori/goldhand/tw.py))
11
+
12
+
13
+ ```python
14
+ from goldhand import *
15
+
16
+ # tradingView data
17
+ tw = Tw()
18
+
19
+ # data frame of the stocks
20
+ tw.stock
21
+
22
+ # data frame of the top 300 crypto currency
23
+ tw.crypto
24
+
25
+ # data frame of the top 3000 etf
26
+ tw.etf
27
+
28
+ ```
29
+
30
+ ```python
31
+ # Get a plot of the stock to see the location in the sector
32
+ tw.get_sec_plot('AMD').show()
33
+
34
+ ```
35
+ ![Sector plot](https://github.com/misrori/goldhand/blob/main/img/sec_plot.png?raw=true "Sector location of FDS")
36
+
37
+
38
+ ```python
39
+ # Get a plot of the stock to see the location in the industry
40
+ tw.get_sec_plot('AMD').show()
41
+
42
+ ```
43
+ ![Sector plot](https://github.com/misrori/goldhand/blob/main/img/ind_plot.png?raw=true "Sector location of FDS")
44
+
45
+
46
+
47
+ # [Goldhand class](https://github.com/misrori/goldhand/stock.py)
48
+
49
+ The `GoldHand` class is a part of the `goldhand` Python package, which provides functionality for working with stock and crypto data. This class allows users to retrieve detailed information and charts for a specific stock.
50
+
51
+
52
+
53
+ ```python
54
+
55
+ # Get a detailed chart of a stock AMD
56
+ ticker = "AMD"
57
+ t = GoldHand(ticker)
58
+ t.df.tail().T
59
+ ```
60
+ ![data structure](https://github.com/misrori/goldhand/blob/main/img/df_structure.png?raw=true "data structure")
61
+
62
+
63
+ ```python
64
+
65
+ # Get a detailed chart of a stock AMD
66
+ ticker = "TSLA"
67
+ t = GoldHand(ticker)
68
+ t.plotly_last_year(tw.get_plotly_title(ticker)).show()
69
+
70
+ ## Stock Chart
71
+
72
+ ```
73
+ !['Detailed stock chart'](https://github.com/misrori/goldhand/blob/main/img/stock_plot.png?raw=true "Stock plot")
74
+
75
+ ```python
76
+
77
+ # Get a detailed chart of a crypto
78
+ ticker = "BTC-USD"
79
+ t = GoldHand(ticker)
80
+ t.plotly_last_year(tw.get_plotly_title(ticker)).show()
81
+
82
+
83
+ ```
84
+ !['Detailed crypto chart'](https://github.com/misrori/goldhand/blob/main/img/crypto_plot.png?raw=true "crypto plot")
85
+
86
+
87
+ ## [GoldHand Line indicator](https://gist.github.com/misrori/ae77642c31fb1a973c7627cc077a1df2)
88
+
89
+
90
+ ```python
91
+ ticker = "TSLA"
92
+ t = GoldHand(ticker)
93
+ t.plot_goldhand_line(tw.get_plotly_title(ticker)).show()
94
+
95
+ ```
96
+ !['Detailed crypto chart'](https://github.com/misrori/goldhand/blob/main/img/goldhand_line_plot.png?raw=true "crypto plot")
97
+
98
+
99
+
100
+ # [Backtest](https://github.com/misrori/goldhand/backtest.py)
101
+
102
+ The Backtest class is a powerful tool for evaluating the performance of trading strategies using historical data. It allows you to simulate trades and calculate various performance metrics to assess the profitability and risk of your strategy.
103
+
104
+ It takes a data and a function and display the trades.
105
+
106
+
107
+
108
+ ```python
109
+ ticker= 'TSLA'
110
+ data = GoldHand(ticker).df
111
+ backtest = Backtest( data, rsi_strategy, plot_title=tw.get_plotly_title(ticker), buy_threshold=30, sell_threshold=70)
112
+ backtest.summarize_strategy()
113
+
114
+ ```
115
+ `summarize_strategy` will show the trades summary, a plot with trades and the trades in DataFrame.
116
+
117
+
118
+ !['Summary of trades'](https://github.com/misrori/goldhand/blob/main/img/tradesdf.png?raw=true "summary of trades")
119
+
120
+ !['Trades plot'](https://github.com/misrori/goldhand/blob/main/img/backtest_plot.png?raw=true "trades plot")
121
+
122
+ !['Trades'](https://github.com/misrori/goldhand/blob/main/img/trades_summary.png?raw=true "trades df")
123
+
124
+
125
+ # Strategys
126
+
127
+ ## [RSI Strategy](https://github.com/misrori/goldhand/strategy_rsi.py)
128
+
129
+ ```python
130
+ """
131
+ RSI strategy for backtesting with Backtest class
132
+
133
+ Parameters:
134
+ - data: pandas DataFrame with columns: date, open, high, low, close, volume and rsi
135
+ - buy_threshold: int, default 30, buy when RSI is below this value
136
+ - sell_threshold: int, default 70, sell when RSI is above this value
137
+ """
138
+ backtest = Backtest( data, rsi_strategy, plot_title=tw.get_plotly_title(ticker), buy_threshold=30, sell_threshold=70)
139
+
140
+ ```
141
+
142
+
143
+ ```python
144
+ ticker = 'TSLA'
145
+ p = show_indicator_rsi_strategy(ticker = ticker, buy_threshold=30, sell_threshold=70, plot_title=tw.get_plotly_title(ticker), add_strategy_summary=True)
146
+ ```
147
+ !['RSI strategy plot'](https://github.com/misrori/goldhand/blob/main/img/rsi_strategy_plot.png?raw=true "RSI Strategy plot")
148
+
149
+ ## [GoldHand Line indicator](https://github.com/misrori/goldhand/strategy_goldhand_line.py)
150
+
151
+ ```python
152
+ """
153
+ This function implements the GoldHandLine strategy.
154
+
155
+ Parameters:
156
+ - data (pandas DataFrame) : The DataFrame containing the data.
157
+ - buy_at (str): The color of the line to buy at. Default is 'gold'.
158
+ - sell_at (str): The color of the line to sell at. Default is 'grey'.
159
+
160
+ """
161
+ backtest = Backtest( data, goldhand_line_strategy,)
162
+
163
+ ```
164
+
165
+ ```python
166
+ ticker = 'BTC-USD'
167
+ show_indicator_goldhand_line_strategy(ticker = ticker, plot_title=tw.get_plotly_title(ticker), buy_at='gold', sell_at='blue', add_strategy_summary=True)
168
+ ```
169
+ !['GoldHand Line strategy plot'](https://github.com/misrori/goldhand/blob/main/img/goldhand_line_strategy_plot.png?raw=true "GoldHand Line Strategy plot")
170
+
171
+
172
+
@@ -0,0 +1,9 @@
1
+ from goldhand.stocks import GoldHand
2
+ from goldhand.backtest import Backtest
3
+ from goldhand.data import Data
4
+ from goldhand.indicators import Indicators
5
+ from goldhand.plotting import Plotting
6
+ from goldhand.tw import Tw
7
+ from goldhand.strategies import goldhand_line_strategy, show_indicator_goldhand_line_strategy
8
+ from goldhand.strategies import rsi_strategy, show_indicator_rsi_strategy
9
+ from goldhand.strategies import adaptive_trend_strategy, show_indicator_adaptive_trend_strategy
@@ -0,0 +1,168 @@
1
+ from IPython.display import display
2
+ import pandas as pd
3
+ import numpy as np
4
+ from goldhand.plotting import Plotting
5
+
6
+ class Backtest:
7
+ """
8
+ Backtest class for running trading strategies.
9
+ """
10
+
11
+ def __init__(self, data, strategy_func, plot_title='Backtest', **kwargs):
12
+ """
13
+ Initialize backtest.
14
+
15
+ Parameters:
16
+ - data: DataFrame with OHLCV data
17
+ - strategy_func: Function that takes data and returns trades DataFrame
18
+ - plot_title: Title for plots
19
+ - **kwargs: Additional parameters to pass to strategy function
20
+ """
21
+ self.data = data.copy()
22
+ self.strategy_func = strategy_func
23
+ self.plot_title = plot_title
24
+ self.additional_params = kwargs
25
+
26
+ # Run strategy
27
+ self.trades = self.strategy_func(self.data, **kwargs)
28
+
29
+ # Ensure trades is a DataFrame
30
+ if not isinstance(self.trades, pd.DataFrame):
31
+ self.trades = pd.DataFrame()
32
+
33
+ # Reorder columns for better readability
34
+ if not self.trades.empty:
35
+ first = ['ticker', 'buy_price', 'buy_date', 'trade_id', 'status', 'sell_price', 'sell_date', 'result', 'days_in_trade']
36
+ all_col = list(self.trades.columns)
37
+ first = [x for x in first if x in all_col]
38
+ first.extend([x for x in all_col if x not in first])
39
+ self.trades = self.trades[first]
40
+
41
+ self.calculate_summary()
42
+
43
+ def calculate_summary(self):
44
+ """
45
+ Calculate comprehensive performance metrics.
46
+ """
47
+ if self.trades.empty:
48
+ self.trades_summary = {'number_of_trades': 0}
49
+ self.trade_summary_plot_text = "No trades executed."
50
+ return
51
+
52
+ # Explicit type conversion to numeric to avoid errors
53
+ results = pd.to_numeric(self.trades['result'], errors='coerce').fillna(1.0)
54
+ days = pd.to_numeric(self.trades['days_in_trade'], errors='coerce').fillna(0)
55
+
56
+ n_trades = len(self.trades)
57
+ win_trades = results[results > 1]
58
+ loss_trades = results[results < 1]
59
+
60
+ # Calculate profit percentages
61
+ profit_pcts = (results - 1) * 100
62
+ win_profit_pcts = profit_pcts[results > 1]
63
+ loss_profit_pcts = profit_pcts[results < 1]
64
+
65
+ # Basic metrics
66
+ win_ratio = (len(win_trades) / n_trades) * 100 if n_trades > 0 else 0
67
+ avg_res = profit_pcts.mean()
68
+ median_res = profit_pcts.median()
69
+ cum_res = results.prod()
70
+
71
+ # Detailed profit/loss metrics
72
+ profitable_trades_mean = win_profit_pcts.mean() if len(win_profit_pcts) > 0 else 0
73
+ profitable_trades_median = win_profit_pcts.median() if len(win_profit_pcts) > 0 else 0
74
+ looser_trades_mean = loss_profit_pcts.mean() if len(loss_profit_pcts) > 0 else 0
75
+ looser_trades_median = loss_profit_pcts.median() if len(loss_profit_pcts) > 0 else 0
76
+
77
+ # Trade results lists
78
+ trade_results_str = " # ".join([f"{x:.2f}" for x in profit_pcts.values[:10]]) # First 10
79
+ if len(profit_pcts) > 10:
80
+ trade_results_str += " # ..."
81
+
82
+ profitable_results_str = " # ".join([f"{x:.2f}" for x in win_profit_pcts.values[:10]])
83
+ if len(win_profit_pcts) > 10:
84
+ profitable_results_str += " # ..."
85
+
86
+ looser_results_str = " # ".join([f"{x:.2f}" for x in loss_profit_pcts.values[:10]])
87
+ if len(loss_profit_pcts) > 10:
88
+ looser_results_str += " # ..."
89
+
90
+ # Date and price info
91
+ first_trade_date = self.trades['buy_date'].iloc[0] if 'buy_date' in self.trades.columns else None
92
+ last_trade_date = self.trades['sell_date'].iloc[-1] if 'sell_date' in self.trades.columns else None
93
+ first_data_date = self.data['date'].iloc[0]
94
+ last_data_date = self.data['date'].iloc[-1]
95
+ first_open_price = self.data['open'].iloc[0]
96
+ last_close_price = self.data['close'].iloc[-1]
97
+
98
+ # Hold result
99
+ hold_result = last_close_price / first_open_price
100
+
101
+ # Buy/Sell colors (from original strategy if available)
102
+ buy_color = self.additional_params.get('buy_at', 'gold')
103
+ sell_color = self.additional_params.get('sell_at', 'grey')
104
+
105
+ self.trades_summary = {
106
+ 'ticker': self.data['ticker'].iloc[0] if 'ticker' in self.data.columns else 'Unknown',
107
+ 'number_of_trades': n_trades,
108
+ 'win_ratio(%)': round(win_ratio, 2),
109
+ 'average_res(%)': round(avg_res, 2),
110
+ 'average_trade_len(days)': round(days.mean(), 1),
111
+ 'median_res(%)': round(median_res, 2),
112
+ 'cumulative_result': round(cum_res, 6),
113
+ 'trade_results': trade_results_str,
114
+ 'profitable_trade_results': profitable_results_str,
115
+ 'profitable_trades_mean': round(profitable_trades_mean, 2),
116
+ 'profitable_trades_median': round(profitable_trades_median, 2),
117
+ 'looser_trade_results': looser_results_str,
118
+ 'looser_trades_mean': round(looser_trades_mean, 2),
119
+ 'looser_trades_median': round(looser_trades_median, 2),
120
+ 'median_trade_len(days)': round(days.median(), 1),
121
+ 'number_of_win_trades': len(win_trades),
122
+ 'number_of_lost_trades': len(loss_trades),
123
+ 'max_gain(%)': round((results.max() - 1) * 100, 2),
124
+ 'max_lost(%)': round((results.min() - 1) * 100, 2),
125
+ 'first_trade_buy': str(first_trade_date) if first_trade_date else 'N/A',
126
+ 'first_data_date': str(first_data_date),
127
+ 'first_open_price': round(first_open_price, 6),
128
+ 'last_data_date': str(last_data_date),
129
+ 'last_close_price': round(last_close_price, 6),
130
+ 'hold_result': f"{round(hold_result, 2)} x",
131
+ 'buy_at': buy_color,
132
+ 'sell_at': sell_color
133
+ }
134
+
135
+ # Add additional params to summary for context
136
+ self.trades_summary.update(self.additional_params)
137
+
138
+ # Format text for plot
139
+ self.trade_summary_plot_text = (
140
+ f"Trades: {self.trades_summary['number_of_trades']}<br>"
141
+ f"Win ratio: {self.trades_summary['win_ratio(%)']}%<br>"
142
+ f"Avg Result: {self.trades_summary['average_res(%)']}%<br>"
143
+ f"Cum Result: {self.trades_summary['cumulative_result']}<br>"
144
+ f"Hold Result: {self.trades_summary['hold_result']}"
145
+ )
146
+
147
+ def show_trades(self):
148
+ """
149
+ Return the plotly figure with trades.
150
+ """
151
+ if self.trades.empty:
152
+ print("No trades to show.")
153
+ return Plotting._create_base_ohlc(self.data)
154
+
155
+ return Plotting.plot_strategy_trades(
156
+ self.data,
157
+ self.trades,
158
+ title=self.plot_title,
159
+ summary_text=self.trade_summary_plot_text
160
+ )
161
+
162
+ def summarize_strategy(self):
163
+ """
164
+ Display summary table and plot.
165
+ """
166
+ display(pd.DataFrame(self.trades_summary, index=['Strategy summary']).T)
167
+ self.show_trades().show()
168
+ display(self.trades)
@@ -0,0 +1,68 @@
1
+ import yfinance as yf
2
+ import pandas as pd
3
+ from datetime import datetime, timedelta
4
+
5
+ class Data:
6
+ """
7
+ Class to handle data downloading from Yahoo Finance.
8
+ """
9
+
10
+ @staticmethod
11
+ def download(ticker: str, period: str = 'max', interval: str = '1d', auto_adjust: bool = True) -> pd.DataFrame:
12
+ """
13
+ Download historical data for a single ticker.
14
+
15
+ Parameters:
16
+ - ticker: str, symbol (e.g., 'AAPL', 'BTC-USD')
17
+ - period: str, data period to download (e.g. '1y', '2y', 'max')
18
+ - interval: str, data interval (e.g. '1d', '1h')
19
+
20
+ Returns:
21
+ - pd.DataFrame with lowercase columns ['date', 'open', 'high', 'low', 'close', 'volume', 'ticker']
22
+ """
23
+ try:
24
+ # Using yfinance to download data
25
+ # auto_adjust=True fixes the Close price for splits and dividends
26
+ df = yf.download(ticker, period=period, interval=interval, auto_adjust=auto_adjust, progress=False, multi_level_index=False)
27
+
28
+ if df.empty:
29
+ print(f"Warning: No data found for ticker {ticker}")
30
+ return pd.DataFrame()
31
+
32
+ # Clean up DataFrame
33
+ df.reset_index(inplace=True)
34
+ df.columns = df.columns.str.lower()
35
+
36
+ # Rename 'Date'/'Datetime' to 'date' consistently
37
+ if 'date' not in df.columns:
38
+ if 'datetime' in df.columns:
39
+ df.rename(columns={'datetime': 'date'}, inplace=True)
40
+
41
+ # Ensure 'date' column is datetime.date objects for compatibility with existing logic
42
+ if 'date' in df.columns:
43
+ df['date'] = pd.to_datetime(df['date']).dt.date
44
+
45
+ # Add ticker column
46
+ df['ticker'] = ticker
47
+
48
+ # Select relevant columns
49
+ cols = ['date', 'open', 'high', 'low', 'close', 'volume', 'ticker']
50
+ df = df[[c for c in cols if c in df.columns]]
51
+
52
+ return df
53
+
54
+ except Exception as e:
55
+ print(f"Error downloading data for {ticker}: {e}")
56
+ return pd.DataFrame()
57
+
58
+ @staticmethod
59
+ def get_ticker_info(ticker: str) -> dict:
60
+ """
61
+ Get fundamental info about the ticker.
62
+ """
63
+ try:
64
+ t = yf.Ticker(ticker)
65
+ return t.info
66
+ except Exception as e:
67
+ print(f"Error getting info for {ticker}: {e}")
68
+ return {}