goldhand 10.0__tar.gz → 12.7__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.

Potentially problematic release.


This version of goldhand might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: goldhand
3
- Version: 10.0
3
+ Version: 12.7
4
4
  Summary: A package working with financial data
5
5
  Home-page: https://github.com/misrori/goldhand
6
6
  Author: Mihaly
@@ -1,5 +1,5 @@
1
1
  from .tw import *
2
2
  from .stocks import *
3
3
  from .helpers import *
4
- from .video import *
5
-
4
+ from .backtest import *
5
+ from .strategy_rsi import *
@@ -0,0 +1,112 @@
1
+ from IPython.display import display
2
+ import numpy as np
3
+ import pandas as pd
4
+ import plotly.graph_objects as go
5
+ import plotly.express as px
6
+
7
+ class Backtest:
8
+ def __init__(self, data, strategy_function, **kwargs):
9
+ self.data = data
10
+ self.strategy_function = strategy_function
11
+ self.additional_params = kwargs
12
+ self.add_trades()
13
+ self.summary_of_trades()
14
+
15
+
16
+ def add_trades(self):
17
+ self.trades = self.strategy_function(self.data, **self.additional_params)
18
+
19
+
20
+ def summary_of_trades(self):
21
+ self.trades_summary = {
22
+ 'number_of_trades' : self.trades.shape[0],
23
+
24
+ 'average_res(%)' : round(((self.trades['result'].mean()-1)*100),2),
25
+ 'median_res(%)': round(((self.trades['result'].median()-1)*100),2),
26
+ 'cumulative_result': list(np.cumprod(self.trades['result'], axis=0))[-1],
27
+ 'trade_results': ' # '.join([ str(round(((x-1)*100),2)) for x in self.trades['result']]),
28
+
29
+ 'profitable_trade_results': ' # '.join([ str(round(((x-1)*100),2)) for x in self.trades['result'] if x>=1]),
30
+
31
+ 'profitable_trades_mean' : round((( np.mean([x for x in self.trades['result'] if x>=1]) -1)*100),2),
32
+ 'profitable_trades_median' : round((( np.median([x for x in self.trades['result'] if x>=1])-1)*100),2),
33
+
34
+ 'looser_trade_results': ' # '.join([ str(round(((x-1)*100),2)) for x in self.trades['result'] if x<1]),
35
+
36
+ 'looser_trades_mean' : round((( np.mean([x for x in self.trades['result'] if x<1])-1)*100),2),
37
+ 'looser_trades_median' : round((( np.median([x for x in self.trades['result'] if x<1])-1)*100),2),
38
+
39
+
40
+ 'average_trade_len(days)' : self.trades['days_in_trade'].mean(),
41
+ 'median_trade_len(days)' : self.trades['days_in_trade'].median(),
42
+
43
+ 'win_ratio(%)' : round(((sum(self.trades['result'] >1) / self.trades.shape[0])*100),2),
44
+ 'number_of_win_trades': sum(self.trades['result'] >1),
45
+ 'number_of_lost_trades': (self.trades.shape[0] - sum(self.trades['result'] >1)),
46
+
47
+ 'max_gain(%)' : round(((self.trades['result'].max()-1)*100),2),
48
+ 'max_lost(%)' : round(((self.trades['result'].min()-1)*100),2),
49
+
50
+ 'first_trade_buy' : min(self.trades['buy_date'])
51
+
52
+ }
53
+ self.trades_summary.update(self.additional_params)
54
+
55
+ def show_trades(self):
56
+ tdf = self.data
57
+ fig = go.Figure(data=go.Ohlc(x=tdf['date'], open=tdf['open'], high=tdf['high'], low=tdf['low'],close=tdf['close']))
58
+ fig.add_trace( go.Scatter(x=tdf['date'], y=tdf['sma_50'], opacity =0.5, line=dict(color='lightblue', width = 2) , name = 'SMA 50') )
59
+ fig.add_trace( go.Scatter(x=tdf['date'], y=tdf['sma_200'], opacity =0.7, line=dict(color='red', width = 2.5) , name = 'SMA 200') )
60
+ fig.update_layout(showlegend=False, plot_bgcolor='white', height=800, title= f"Closed trades" )
61
+ fig.update(layout_xaxis_rangeslider_visible=False)
62
+ fig.update_xaxes( mirror=True, ticks='outside', showline=True, linecolor='black', gridcolor='lightgrey')
63
+ fig.update_yaxes( mirror=True, ticks='outside', showline=True, linecolor='black', gridcolor='lightgrey')
64
+
65
+ for index, row in self.trades.iterrows():
66
+ buy_date= row['buy_date']
67
+ sell_date= row['sell_date']
68
+ buy_price = row['buy_price']
69
+ sell_price = row['sell_price']
70
+ trade_id = row['trade_id']
71
+ status = row['status']
72
+ triangle_color = 'green' if row['result'] >1 else 'red'
73
+
74
+ rise = (row['result'] -1)*100
75
+
76
+ if rise>100:
77
+ if status =='closed':
78
+ result = f'Up:{round(((rise+100)/100), 2)}x'
79
+ else:
80
+ result = f'Up:{round(((rise+100)/100), 2)}x <br> Still open'
81
+ else:
82
+ if status =='closed':
83
+ result = f"{round(((row['result']-1)*100),2) }%"
84
+ else:
85
+ result = f"{round(((row['result']-1)*100),2) }% <br> Still open"
86
+
87
+
88
+ # add buy
89
+ buy_point=(buy_date, buy_price)
90
+ triangle_trace = go.Scatter(x=[buy_point[0]], y=[buy_point[1]], mode='markers', marker=dict(symbol='triangle-up', size=16, color=triangle_color))
91
+ fig.add_trace(triangle_trace)
92
+ fig.add_annotation( x=buy_date, y=buy_price, text=f"Buy: ${round(buy_price, 2)}<br>#{trade_id}", hovertext = f"Buy: ${round(buy_price, 2)}<br>#{trade_id}", showarrow=True, align="center", bordercolor="#c7c7c7", font=dict(family="Courier New, monospace", size=12, color= triangle_color ), borderwidth=2, borderpad=4, bgcolor="#f4fdff", opacity=0.8, arrowhead=2, arrowsize=1, arrowwidth=1, ax=30,ay=30)
93
+
94
+ # add sell
95
+ sell_point=(sell_date, sell_price)
96
+ triangle_trace = go.Scatter(x=[sell_point[0]], y=[sell_point[1]], mode='markers', marker=dict(symbol='triangle-down', size=16, color=triangle_color))
97
+ fig.add_trace(triangle_trace)
98
+ fig.add_annotation( x=sell_date, y=sell_price, text=f"Sell: ${round(sell_price, 2)}<br>#{trade_id}, {result}", hovertext = f"Sell: ${round(sell_price, 2)}<br>#{trade_id}, {result}" ,showarrow=True, align="center", bordercolor="#c7c7c7", font=dict(family="Courier New, monospace", size=12, color= triangle_color ), borderwidth=2, borderpad=4, bgcolor="#f4fdff", opacity=0.8, arrowhead=2, arrowsize=1, arrowwidth=1, ax=-30,ay=-30)
99
+
100
+ # add reactangle
101
+ fig.add_shape(type="rect", x0=buy_point[0], y0=buy_point[1], x1=sell_point[0], y1=sell_point[1],line=dict(color= triangle_color ,width=2,),fillcolor="LightSkyBlue", opacity=0.3, label=dict( text=f"{result}<br>{row['days_in_trade']} days", textposition="bottom center", font=dict(size=13, color =triangle_color, family="Times New Roman")))
102
+
103
+ # set size
104
+ fig.update_layout(showlegend=False, plot_bgcolor='white', )
105
+
106
+ return(fig)
107
+
108
+
109
+ def summarize_strategy(self):
110
+ display(pd.DataFrame(self.trades_summary, index=['Strategy summary']).T)
111
+ self.show_trades().show()
112
+ display(self.trades)
@@ -0,0 +1,231 @@
1
+ from datetime import datetime, timedelta
2
+ import pandas as pd
3
+ import pandas_ta as ta
4
+ import plotly.graph_objects as go
5
+ import plotly.express as px
6
+ from scipy.signal import argrelextrema
7
+ import numpy as np
8
+ import requests
9
+ import json
10
+ import cloudscraper
11
+
12
+ class GoldHand:
13
+ def __init__(self, ticker, ad_ticker=True, range='18y', interval='1d'):
14
+ self.scraper = cloudscraper.create_scraper()
15
+ self.ad_ticker = ad_ticker
16
+ self.range = range
17
+ self.interval = interval
18
+ self.ticker = ticker
19
+ self.df = None
20
+ self.download_historical_data()
21
+
22
+
23
+ def get_olhc(self):
24
+ #scraper = cloudscraper.create_scraper()
25
+ response = self.scraper.get(f"https://query1.finance.yahoo.com/v8/finance/chart/{self.ticker}?interval={self.interval}&range={self.range}")
26
+ t= response.json()
27
+ df = pd.DataFrame(t['chart']['result'][0]['indicators']['quote'][0])
28
+ df['date'] = pd.to_datetime(t['chart']['result'][0]['timestamp'], unit='s').date
29
+ df = df[['date', 'open', 'low', 'high', 'close', 'volume']]
30
+ if self.ad_ticker:
31
+ df['ticker'] = self.ticker
32
+ return(df)
33
+
34
+ def smma(self, data, window, colname):
35
+ hl2 = data['hl2'].values
36
+ smma_values = [hl2[0]]
37
+
38
+ for i in range(1, len(hl2)):
39
+ smma_val = (smma_values[-1] * (window - 1) + hl2[i]) / window
40
+ smma_values.append(smma_val)
41
+
42
+ data[colname] = smma_values
43
+ return data
44
+
45
+
46
+
47
+ def download_historical_data(self):
48
+ # Download historical stock data for the last year
49
+ self.df = self.get_olhc()
50
+ self.df.columns = self.df.columns.str.lower()
51
+ self.df['hl2'] = (self.df['high'] + self.df['low'])/2
52
+
53
+ # Rsi
54
+ self.df['rsi'] = ta.rsi(self.df['close'], 14)
55
+
56
+ # SMAS
57
+ self.df['sma_50']= ta.sma(self.df['close'], 50)
58
+ self.df['diff_sma50'] = (self.df['close']/self.df['sma_50'] -1)*100
59
+ self.df['sma_100']= ta.sma(self.df['close'], 100)
60
+ self.df['diff_sma100'] = (self.df['close']/self.df['sma_100'] -1)*100
61
+ self.df['sma_200']= ta.sma(self.df['close'], 200)
62
+ self.df['diff_sma200'] = (self.df['close']/self.df['sma_200'] -1)*100
63
+
64
+ #Bolinger bands
65
+ bb = ta.bbands(self.df['close'])
66
+ bb.columns = ['bb_lower', 'bb_mid', 'bb_upper', 'bandwidth', 'percent']
67
+ self.df['bb_lower'] = bb['bb_lower']
68
+ self.df['bb_upper'] = bb['bb_upper']
69
+ self.df['diff_upper_bb'] = (self.df['bb_upper']/self.df['close'] -1)*100
70
+ self.df['diff_lower_bb'] = (self.df['bb_lower']/self.df['close'] -1)*100
71
+
72
+ #local min maxs
73
+ self.df['local'] = ''
74
+ self.df['local_text'] = ''
75
+ max_ids = list(argrelextrema(self.df['high'].values, np.greater, order=30)[0])
76
+ min_ids = list(argrelextrema(self.df['low'].values, np.less, order=30)[0])
77
+ self.df.loc[min_ids, 'local'] = 'minimum'
78
+ self.df.loc[max_ids, 'local'] = 'maximum'
79
+
80
+
81
+ states = self.df[self.df['local']!='']['local'].index.to_list()
82
+ problem = []
83
+ problem_list = []
84
+ for i in range(0, (len(states)-1) ):
85
+
86
+ if (self.df.loc[states[i], 'local'] != self.df.loc[states[i+1], 'local']):
87
+ if (len(problem)==0):
88
+ continue
89
+ else:
90
+ problem.append(states[i])
91
+ text = self.df.loc[states[i], 'local']
92
+ if(text=='minimum'):
93
+ real_min = self.df.loc[problem, 'low'].idxmin()
94
+ problem.remove(real_min)
95
+ self.df.loc[problem, 'local']=''
96
+ else:
97
+ real_max = self.df.loc[problem, 'high'].idxmax()
98
+ problem.remove(real_max)
99
+ self.df.loc[problem, 'local']=''
100
+
101
+ problem = []
102
+ else:
103
+ problem.append(states[i])
104
+
105
+ states = self.df[self.df['local']!='']['local'].index.to_list()
106
+
107
+ # if first is min ad the price
108
+ if self.df.loc[states[0], 'local']== 'minimum':
109
+ self.df.loc[states[0],'local_text'] = f"${round(self.df.loc[states[0], 'low'], 2)}"
110
+ else:
111
+ self.df.loc[states[0],'local_text'] = f"${round(self.df.loc[states[0], 'high'], 2)}"
112
+
113
+ # add last fall if last local is max
114
+ if list(self.df[self.df['local']!='']['local'])[-1]=='maximum':
115
+ last_min_id = self.df.loc[self.df['low']==min(self.df['low'][-3:] )].index.to_list()[0]
116
+ self.df.loc[last_min_id , 'local'] = 'minimum'
117
+
118
+ states = self.df[self.df['local']!='']['local'].index.to_list()
119
+
120
+
121
+ for i in range(1,len(states)):
122
+ prev = self.df.loc[states[i-1], 'local']
123
+ current= self.df.loc[states[i], 'local']
124
+ prev_high = self.df.loc[states[i-1], 'high']
125
+ prev_low = self.df.loc[states[i-1], 'low']
126
+ current_high = self.df.loc[states[i], 'high']
127
+ current_low = self.df.loc[states[i], 'low']
128
+ if current == 'maximum':
129
+ # rise
130
+ rise = (current_high/ prev_low -1)*100
131
+ if rise>100:
132
+ self.df.loc[states[i], 'local_text'] = f'🚀🌌{round(((rise+100)/100), 2)}x<br>${round(current_high, 2)}'
133
+ else:
134
+ self.df.loc[states[i], 'local_text'] = f'🚀{round(rise, 2)}%<br>${round(current_high, 2)}'
135
+ else:
136
+ fall = round((1-(current_low / prev_high))*100, 2)
137
+ if fall < 30:
138
+ temj = '💸'
139
+ elif fall < 50:
140
+ temj = '💸'
141
+ else:
142
+ temj = '😭💔'
143
+ self.df.loc[states[i], 'local_text'] = f'{temj}{fall}%<br>${round(current_low, 2)}'
144
+
145
+
146
+ def plotly_last_year(self, plot_title, plot_height=900):
147
+ tdf = self.df.tail(500)
148
+
149
+ fig = go.Figure(data=go.Ohlc(x=tdf['date'], open=tdf['open'], high=tdf['high'], low=tdf['low'],close=tdf['close']))
150
+
151
+ for index, row in tdf[tdf['local']!=''].iterrows():
152
+ direction = row['local']
153
+ tdate = row['date']
154
+ local_text = row['local_text']
155
+ min_price = row['low']
156
+ max_price = row['high']
157
+ if direction == 'maximum':
158
+ fig.add_annotation( x=tdate, y=max_price, text=local_text, showarrow=True, align="center", bordercolor="#c7c7c7", font=dict(family="Courier New, monospace", size=16, color="#214e34" ), borderwidth=2, borderpad=4, bgcolor="#f4fdff", opacity=0.8, arrowhead=2, arrowsize=1, arrowwidth=1, ax=-45,ay=-45)
159
+
160
+ if direction == 'minimum':
161
+ fig.add_annotation( x=tdate, y=min_price, text=local_text, showarrow=True, align="center", bordercolor="#c7c7c7", font=dict(family="Courier New, monospace", size=16, color="red" ), borderwidth=2, borderpad=4, bgcolor="#f4fdff", opacity=0.8, arrowhead=2, arrowsize=1, arrowwidth=1, ax=45,ay=45)
162
+
163
+ fig.update_layout(showlegend=False, plot_bgcolor='white', height=plot_height, title= plot_title)
164
+
165
+ fig.update_xaxes( mirror=True, ticks='outside', showline=True, linecolor='black', gridcolor='lightgrey' )
166
+ fig.update_yaxes( mirror=True, ticks='outside', showline=True, linecolor='black', gridcolor='lightgrey')
167
+ fig.update(layout_xaxis_rangeslider_visible=False)
168
+ fig.add_trace( go.Scatter(x=tdf['date'], y=tdf['sma_50'], opacity =0.5, line=dict(color='lightblue', width = 2) , name = 'SMA 50') )
169
+ fig.add_trace( go.Scatter(x=tdf['date'], y=tdf['sma_200'], opacity =0.7, line=dict(color='red', width = 2.5) , name = 'SMA 200') )
170
+ return(fig)
171
+
172
+ def plot_goldhand_line(self, plot_title, plot_height=900):
173
+
174
+ data = self.df.copy()
175
+ # Apply SMMA to the dataframe
176
+ data = self.smma(data, 15, 'v1')
177
+ data = self.smma(data, 19, 'v2')
178
+ data = self.smma(data, 25, 'v3')
179
+ data = self.smma(data, 29, 'v4')
180
+
181
+ data['color'] = 'grey' # Set default color to grey
182
+
183
+ # Update color based on conditions
184
+ data.loc[(data['v4'] < data['v3']) & (data['v3'] < data['v2']) & (data['v2'] < data['v1']), 'color'] = 'gold'
185
+ data.loc[(data['v1'] < data['v2']) & (data['v2'] < data['v3']) & (data['v3'] < data['v4']), 'color'] = 'blue'
186
+
187
+ # Identify rows where color changes compared to the previous row
188
+ data['color_change'] = data['color'] != data['color'].shift(1)
189
+
190
+ # Create a 'group' column and increase the value only when there's a color change
191
+ data['group'] = (data['color_change']).cumsum()
192
+
193
+ tdf = data.tail(800)
194
+
195
+ fig = go.Figure(data=go.Ohlc(x=tdf['date'], open=tdf['open'], high=tdf['high'], low=tdf['low'],close=tdf['close']))
196
+
197
+ fig.update_xaxes( mirror=True, ticks='outside', showline=True, linecolor='black', gridcolor='lightgrey' )
198
+ fig.update_yaxes( mirror=True, ticks='outside', showline=True, linecolor='black', gridcolor='lightgrey')
199
+ fig.update(layout_xaxis_rangeslider_visible=False)
200
+
201
+ for group_id in tdf['group'].unique():
202
+ if group_id ==tdf['group'].unique().max():
203
+ indices = tdf[tdf['group'] == group_id].index.to_list()
204
+ else:
205
+ indices = tdf[tdf['group'] == group_id].index.to_list()
206
+ indices.append(indices[-1]+1)
207
+
208
+
209
+ group_df = tdf.loc[indices]
210
+
211
+ group_color = group_df['color'].iloc[0]
212
+ color_dict = {'gold' : 'rgba(255, 215, 0, 0.4)' , 'grey' : 'rgba(128, 128 ,128, 0.4)' , 'blue' : 'rgba(0, 0, 255, 0.4)' }
213
+
214
+ # Create v1 and v4 traces
215
+ trace_v1 = go.Scatter(x=group_df['date'], y=group_df['v1'], mode='lines', name='v1', line=dict(color=color_dict[group_color]) )
216
+ trace_v4 = go.Scatter(x=group_df['date'], y=group_df['v4'], mode='lines', name='v4', line=dict(color=color_dict[group_color]), fill='tonexty', fillcolor =color_dict[group_color])
217
+
218
+ # Add candlestick trace and additional lines to the figure
219
+ fig.add_trace(trace_v1)
220
+ fig.add_trace(trace_v4)
221
+
222
+ fig.update_layout(showlegend=False, plot_bgcolor='white', height=plot_height, title= plot_title)
223
+ return(fig)
224
+
225
+
226
+
227
+
228
+ # https://stackoverflow.com/questions/71411995/pandas-plotly-secondary-graph-needs-to-be-to-rsi
229
+
230
+ #https://wire.insiderfinance.io/plot-candlestick-rsi-bollinger-bands-and-macd-charts-using-yfinance-python-api-1c2cb182d147
231
+
@@ -0,0 +1,166 @@
1
+ from IPython.display import display
2
+ import numpy as np
3
+ import pandas as pd
4
+ import plotly.graph_objects as go
5
+ from plotly.subplots import make_subplots
6
+ import plotly.express as px
7
+ from goldhand import *
8
+
9
+
10
+ def rsi_strategy(data, buy_threshold = 30, sell_threshold = 70):
11
+
12
+ in_trade = False # Flag to track if already in a trade
13
+ trade_id = 1
14
+ all_trades = []
15
+
16
+ temp_trade = {}
17
+
18
+ for i in range(1, len(data)):
19
+ # Check if not already in a trade
20
+ if not in_trade:
21
+ # Generate buy signal
22
+ if data['rsi'][i] < buy_threshold:
23
+
24
+ temp_trade['buy_price'] = data['close'][i]
25
+ temp_trade['trade_id'] = trade_id
26
+ temp_trade['status'] = 'open'
27
+ temp_trade.update(dict(data.iloc[i].add_prefix('buy_')))
28
+ in_trade = True # Set flag to indicate in a trade
29
+ else:
30
+ # Generate sell signal
31
+ if data['rsi'][i] > sell_threshold:
32
+
33
+ temp_trade['sell_price'] = data['close'][i]
34
+ temp_trade['trade_id'] = trade_id
35
+ temp_trade['status'] = 'closed'
36
+
37
+ temp_trade.update(dict(data.iloc[i].add_prefix('sell_')))
38
+
39
+ # calculate results
40
+ temp_trade['result'] = temp_trade['sell_price'] / temp_trade['buy_price']
41
+ temp_trade['days_in_trade'] = (temp_trade['sell_date'] - temp_trade['buy_date']).days
42
+
43
+
44
+
45
+ in_trade = False # Reset flag to indicate not in a trade
46
+ trade_id +=1
47
+ all_trades.append(temp_trade)
48
+ temp_trade = {}
49
+ if temp_trade:
50
+ temp_trade['sell_price'] = data['close'][i]
51
+ temp_trade['trade_id'] = trade_id
52
+ temp_trade['sell_date'] = data['date'][i]
53
+
54
+ temp_trade['result'] = temp_trade['sell_price'] / temp_trade['buy_price']
55
+ temp_trade['days_in_trade'] = (temp_trade['sell_date'] - temp_trade['buy_date']).days
56
+ all_trades.append(temp_trade)
57
+
58
+ res_df = pd.DataFrame(all_trades)
59
+ # change orders
60
+
61
+ all_col = res_df.columns.tolist()
62
+ first = ['result', 'buy_price', 'sell_price', 'buy_date', 'sell_date', 'days_in_trade']
63
+ first.extend([x for x in all_col if x not in first])
64
+ res_df = res_df[first]
65
+ return(res_df)
66
+
67
+
68
+
69
+ def show_indicator_rsi_strategy(ticker, buy_threshold = 30, sell_threshold = 70):
70
+
71
+ tdf = GoldHand(ticker).df
72
+ backtest = Backtest( tdf, rsi_strategy, buy_threshold=buy_threshold, sell_threshold=sell_threshold)
73
+ trades =backtest.trades
74
+
75
+ # Create subplots with shared x-axis and custom heights
76
+ fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1, subplot_titles=["Price Chart", "RSI"], row_heights=[0.7, 0.3])
77
+
78
+ # Add OHLC candlestick chart
79
+ fig.add_trace(go.Ohlc(x=tdf['date'], open=tdf['open'], high=tdf['high'], low=tdf['low'], close=tdf['close']), row=1, col=1)
80
+
81
+ # Add SMA lines
82
+ fig.add_trace(go.Scatter(x=tdf['date'], y=tdf['sma_50'], opacity=0.5, line=dict(color='lightblue', width=2), name='SMA 50'), row=1, col=1)
83
+ fig.add_trace(go.Scatter(x=tdf['date'], y=tdf['sma_200'], opacity=0.7, line=dict(color='red', width=2.5), name='SMA 200'), row=1, col=1)
84
+
85
+ # Add trade points and annotations
86
+ for index, row in trades.iterrows():
87
+ buy_date = row['buy_date']
88
+ sell_date = row['sell_date']
89
+ buy_price = row['buy_price']
90
+ sell_price = row['sell_price']
91
+ trade_id = row['trade_id']
92
+ status = row['status']
93
+ triangle_color = 'green' if row['result'] > 1 else 'red'
94
+
95
+ rise = (row['result'] - 1) * 100
96
+
97
+ if rise > 100:
98
+ if status == 'closed':
99
+ result = f'Up:{round(((rise + 100) / 100), 2)}x'
100
+ else:
101
+ result = f'Up:{round(((rise + 100) / 100), 2)}x <br> Still open'
102
+ else:
103
+ if status == 'closed':
104
+ result = f"{round(((row['result'] - 1) * 100), 2)}%"
105
+ else:
106
+ result = f"{round(((row['result'] - 1) * 100), 2)}% <br> Still open"
107
+
108
+ # add buy
109
+ buy_point = (buy_date, buy_price)
110
+ triangle_trace = go.Scatter(x=[buy_point[0]], y=[buy_point[1]], mode='markers',
111
+ marker=dict(symbol='triangle-up', size=16, color=triangle_color))
112
+ fig.add_trace(triangle_trace)
113
+ fig.add_annotation(x=buy_date, y=buy_price, text=f"Buy: ${round(buy_price, 2)}<br>#{trade_id}",
114
+ showarrow=True, align="center", bordercolor="#c7c7c7",
115
+ font=dict(family="Courier New, monospace", size=12, color=triangle_color),
116
+ borderwidth=2, borderpad=4, bgcolor="#f4fdff", opacity=0.8,
117
+ arrowhead=2, arrowsize=1, arrowwidth=1, ax=30, ay=30,
118
+ hovertext= f"Buy: ${round(buy_price, 2)}")
119
+
120
+ # add sell
121
+ sell_point = (sell_date, sell_price)
122
+ triangle_trace = go.Scatter(x=[sell_point[0]], y=[sell_point[1]], mode='markers',
123
+ marker=dict(symbol='triangle-down', size=16, color=triangle_color))
124
+ fig.add_trace(triangle_trace)
125
+ fig.add_annotation(x=sell_date, y=sell_price,
126
+ text=f"Sell: ${round(sell_price, 2)}<br>#{trade_id}, {result}",
127
+ showarrow=True, align="center", bordercolor="#c7c7c7",
128
+ font=dict(family="Courier New, monospace", size=12, color=triangle_color),
129
+ borderwidth=2, borderpad=4, bgcolor="#f4fdff", opacity=0.8,
130
+ arrowhead=2, arrowsize=1, arrowwidth=1, ax=-30, ay=-30,
131
+ hovertext = f"Sell: ${round(sell_price, 2)}<br>#{trade_id}, {result}")
132
+
133
+ # add rectangle
134
+ fig.add_shape(type="rect", x0=buy_point[0], y0=buy_point[1], x1=sell_point[0], y1=sell_point[1],
135
+ line=dict(color=triangle_color, width=2,), fillcolor="LightSkyBlue", opacity=0.3,
136
+ label=dict(text=f"{result}<br>{row['days_in_trade']} days",
137
+ textposition="bottom center",
138
+ font=dict(size=13, color=triangle_color, family="Times New Roman")))
139
+
140
+ # Update layout
141
+ fig.update_layout(showlegend=False, plot_bgcolor='white', height=800, title=f"Closed trades")
142
+ fig.update(layout_xaxis_rangeslider_visible=False)
143
+
144
+ # Update x-axes and y-axes for the main chart
145
+ fig.update_xaxes(mirror=True, ticks='outside', showline=True, linecolor='black', gridcolor='lightgrey', row=1, col=1)
146
+ fig.update_yaxes(mirror=True, ticks='outside', showline=True, linecolor='black', gridcolor='lightgrey', row=1, col=1)
147
+
148
+ # Update x-axes and y-axes for the RSI subplot
149
+ fig.update_xaxes(mirror=True, ticks='outside', showline=True, linecolor='black', gridcolor='lightgrey', row=2, col=1)
150
+ fig.update_yaxes(mirror=True, ticks='outside', showline=True, linecolor='black', gridcolor='lightgrey', row=2, col=1)
151
+
152
+
153
+
154
+
155
+ # Add RSI line
156
+ fig.add_trace(go.Scatter(x=tdf['date'], y=tdf['rsi'], line=dict(color='green', width=2), name='RSI'), row=2, col=1)
157
+ fig.add_shape(type="line", x0=tdf['date'].min(), x1=tdf['date'].max(), y0=buy_threshold, y1=buy_threshold, line=dict(color="black", width=2, dash="dash"), row=2, col=1)
158
+ fig.add_shape(type="line", x0=tdf['date'].min(), x1=tdf['date'].max(), y0=sell_threshold, y1=sell_threshold, line=dict(color="black", width=2, dash="dash"), row=2, col=1)
159
+
160
+
161
+ # Show the plot
162
+ fig.show()
163
+
164
+
165
+ # Test
166
+ # show_indicator_rsi_strategy('TSLA', 30,80)
@@ -16,7 +16,7 @@ class Tw:
16
16
  self.get_all_crypto()
17
17
 
18
18
  def get_all_stock(self):
19
- data_query = '{"filter":[{"left":"type","operation":"in_range","right":["stock","dr","fund"]},{"left":"subtype","operation":"in_range","right":["common","foreign-issuer","","etf","etf,odd","etf,otc","etf,cfd"]},{"left":"exchange","operation":"in_range","right":["AMEX","NASDAQ","NYSE"]},{"left":"is_primary","operation":"equal","right":true},{"left":"active_symbol","operation":"equal","right":true}],"options":{"lang":"en"},"markets":["america"],"symbols":{"query":{"types":[]},"tickers":[]},"columns":["logoid","name","close","change","change_abs","Recommend.All","volume","Value.Traded","market_cap_basic","price_earnings_ttm","earnings_per_share_basic_ttm","number_of_employees","sector","industry","RSI","SMA50","SMA100","SMA200","BB.lower","BB.upper","Perf.W","Perf.Y","Perf.YTD","Perf.3M","Perf.6M","Perf.1M","High.1M","High.3M","High.6M","price_52_week_high","High.All","description","type","subtype","update_mode","pricescale","minmov","fractional","minmove2","RSI[1]","currency","fundamental_currency_code"],"sort":{"sortBy":"market_cap_basic","sortOrder":"desc"},"range":[0,9000]}'
19
+ data_query = '{"filter":[{"left":"type","operation":"in_range","right":["stock","dr","fund"]},{"left":"subtype","operation":"in_range","right":["common","foreign-issuer","","etf","etf,odd","etf,otc","etf,cfd"]},{"left":"exchange","operation":"in_range","right":["AMEX","NASDAQ","NYSE"]},{"left":"is_primary","operation":"equal","right":true},{"left":"active_symbol","operation":"equal","right":true}],"options":{"lang":"en"},"markets":["america"],"symbols":{"query":{"types":[]},"tickers":[]},"columns":["logoid","name","close","change","change_abs","Recommend.All","volume","Value.Traded","market_cap_basic","price_earnings_ttm","earnings_per_share_basic_ttm","number_of_employees","sector","High.3M","Low.3M","Perf.3M","Perf.5Y","High.1M","Low.1M","High.6M","Low.6M","Perf.6M","beta_1_year","price_52_week_high","price_52_week_low","High.All","Low.All","BB.lower","BB.upper","change|1M","change_abs|1M","change|1W","change_abs|1W","change|240","country","EMA50","EMA100","EMA200","MACD.macd","MACD.signal","Mom","Perf.1M","RSI7","SMA50","SMA100","SMA200","Stoch.RSI.K","Stoch.RSI.D","Perf.W","Perf.Y","Perf.YTD","industry","Perf.All","description","type","subtype","update_mode","pricescale","minmov","fractional","minmove2","Mom[1]","RSI7[1]","Rec.Stoch.RSI","currency","fundamental_currency_code"],"sort":{"sortBy":"market_cap_basic","sortOrder":"desc"},"range":[0,8000]}'
20
20
  response = requests.post('https://scanner.tradingview.com/america/scan', data=data_query)
21
21
  data = response.json()
22
22
  list_elements = list(map(lambda x:x['d'], data['data'] ))
@@ -94,16 +94,7 @@ class Tw:
94
94
 
95
95
  fig = px.bar(secdf, x='name', y='market_cap_basic', title = f"{row_df['Company'].iloc[0]} ({row_df['name'].iloc[0]})<br>{row_df['sector'].iloc[0]} | {row_df['industry'].iloc[0]}", labels={'market_cap_basic':'Market kapitalization'}, text='Company')
96
96
 
97
- fig.add_annotation( x=row_df['name'].iloc[0], y=row_df['market_cap_basic'].iloc[0], text= f"{market_cap}", showarrow=True,
98
- align="center", bordercolor="#c7c7c7",
99
- font=dict(family="Courier New, monospace", size=16, color="#214e34" ), borderwidth=2,
100
- borderpad=4,
101
- bgcolor="#f4fdff",
102
- opacity=0.8,
103
- arrowhead=2,
104
- arrowsize=1,
105
- arrowwidth=1,
106
- ax=65,ay=-45)
97
+ fig.add_annotation( x=row_df['name'].iloc[0], y=row_df['market_cap_basic'].iloc[0], text= f"{market_cap}", showarrow=True, align="center", bordercolor="#c7c7c7", font=dict(family="Courier New, monospace", size=16, color="#214e34" ), borderwidth=2, borderpad=4, bgcolor="#f4fdff", opacity=0.8, arrowhead=2, arrowsize=1, arrowwidth=1, ax=65,ay=-45)
107
98
  fig.update_layout(showlegend=False, plot_bgcolor='white', height=600)
108
99
  fig.show()
109
100
 
@@ -122,19 +113,12 @@ class Tw:
122
113
  fig = px.bar(inddf, x='name', y='market_cap_basic', title = f"{row_df['Company'].iloc[0]} ({row_df['name'].iloc[0]})<br>{row_df['sector'].iloc[0]} | {row_df['industry'].iloc[0]}", labels={'market_cap_basic':'Market kapitalization'}, text='Company')
123
114
 
124
115
 
125
- fig.add_annotation( x=row_df['name'].iloc[0], y=row_df['market_cap_basic'].iloc[0], text= f"{market_cap}", showarrow=True,
126
- align="center", bordercolor="#c7c7c7",
127
- font=dict(family="Courier New, monospace", size=16, color="#214e34" ), borderwidth=2,
128
- borderpad=4,
129
- bgcolor="#f4fdff",
130
- opacity=0.8,
131
- arrowhead=2,
132
- arrowsize=1,
133
- arrowwidth=1,
134
- ax=65,ay=-45)
116
+ fig.add_annotation( x=row_df['name'].iloc[0], y=row_df['market_cap_basic'].iloc[0], text= f"{market_cap}", showarrow=True, align="center", bordercolor="#c7c7c7", font=dict(family="Courier New, monospace", size=16, color="#214e34" ), borderwidth=2, borderpad=4, bgcolor="#f4fdff", opacity=0.8, arrowhead=2, arrowsize=1, arrowwidth=1, ax=65,ay=-45)
135
117
  fig.update_layout(showlegend=False, plot_bgcolor='white', height=600)
136
118
  fig.show()
137
119
 
138
120
 
139
121
 
140
- tw = Tw()
122
+ #tw = Tw()
123
+ #print(tw.stock.head(1).T)
124
+ #print(tw.crypto.head(1).T)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: goldhand
3
- Version: 10.0
3
+ Version: 12.7
4
4
  Summary: A package working with financial data
5
5
  Home-page: https://github.com/misrori/goldhand
6
6
  Author: Mihaly
@@ -1,11 +1,11 @@
1
1
  README.md
2
2
  setup.py
3
3
  goldhand/__init__.py
4
+ goldhand/backtest.py
4
5
  goldhand/helpers.py
5
6
  goldhand/stocks.py
6
- goldhand/test.py
7
+ goldhand/strategy_rsi.py
7
8
  goldhand/tw.py
8
- goldhand/video.py
9
9
  goldhand.egg-info/PKG-INFO
10
10
  goldhand.egg-info/SOURCES.txt
11
11
  goldhand.egg-info/dependency_links.txt
@@ -8,7 +8,7 @@ long_description = (this_directory / "README.md").read_text()
8
8
 
9
9
  setup(
10
10
  name="goldhand",
11
- version="10.0",
11
+ version="12.7",
12
12
  author="Mihaly",
13
13
  author_email="ormraat.pte@gmail.com",
14
14
  description="A package working with financial data",
@@ -1,210 +0,0 @@
1
- from datetime import datetime, timedelta
2
- import pandas as pd
3
- import pandas_ta as ta
4
- import plotly.graph_objects as go
5
- import plotly.express as px
6
- from scipy.signal import argrelextrema
7
- import numpy as np
8
- import requests
9
- import json
10
- import cloudscraper
11
-
12
- class GoldHand:
13
- def __init__(self, ticker, static_plot =False):
14
- self.scraper = cloudscraper.create_scraper()
15
- self.ticker = ticker
16
- self.df = None
17
- self.static_plot = static_plot
18
- self.download_historical_data()
19
-
20
-
21
- def get_olhc(self, ticker, scraper = cloudscraper.create_scraper(), ad_ticker=False, range='18y', interval='1d'):
22
- response = scraper.get(f"https://query1.finance.yahoo.com/v8/finance/chart/{ticker}?interval={interval}&range={range}")
23
- t= response.json()
24
- df = pd.DataFrame(t['chart']['result'][0]['indicators']['quote'][0])
25
- df['date'] = pd.to_datetime(t['chart']['result'][0]['timestamp'], unit='s').date
26
- df = df[['date', 'open', 'low', 'high', 'close', 'volume']]
27
- if ad_ticker:
28
- df['ticker'] = ticker
29
- return(df)
30
-
31
-
32
-
33
- def download_historical_data(self):
34
- # Download historical stock data for the last year
35
- self.df = self.get_olhc(self.ticker, self.scraper)
36
- self.df.columns = self.df.columns.str.lower()
37
-
38
- # Rsi
39
- self.df['rsi'] = ta.rsi(self.df['close'], 14)
40
-
41
- # SMAS
42
- self.df['sma_50']= ta.sma(self.df['close'], 50)
43
- self.df['diff_sma50'] = (self.df['close']/self.df['sma_50'] -1)*100
44
- self.df['sma_100']= ta.sma(self.df['close'], 100)
45
- self.df['diff_sma100'] = (self.df['close']/self.df['sma_100'] -1)*100
46
- self.df['sma_200']= ta.sma(self.df['close'], 200)
47
- self.df['diff_sma200'] = (self.df['close']/self.df['sma_200'] -1)*100
48
-
49
- #Bolinger bands
50
- bb = ta.bbands(self.df['close'])
51
- bb.columns = ['bb_lower', 'bb_mid', 'bb_upper', 'bandwidth', 'percent']
52
- self.df['bb_lower'] = bb['bb_lower']
53
- self.df['bb_upper'] = bb['bb_upper']
54
- self.df['diff_upper_bb'] = (self.df['bb_upper']/self.df['close'] -1)*100
55
- self.df['diff_lower_bb'] = (self.df['bb_lower']/self.df['close'] -1)*100
56
-
57
- #local min maxs
58
- self.df['local'] = ''
59
- self.df['local_text'] = ''
60
- max_ids = list(argrelextrema(self.df['high'].values, np.greater, order=30)[0])
61
- min_ids = list(argrelextrema(self.df['low'].values, np.less, order=30)[0])
62
- self.df.loc[min_ids, 'local'] = 'minimum'
63
- self.df.loc[max_ids, 'local'] = 'maximum'
64
-
65
-
66
- states = self.df[self.df['local']!='']['local'].index.to_list()
67
- problem = []
68
- problem_list = []
69
- for i in range(0, (len(states)-1) ):
70
-
71
- if (self.df.loc[states[i], 'local'] != self.df.loc[states[i+1], 'local']):
72
- if (len(problem)==0):
73
- continue
74
- else:
75
- problem.append(states[i])
76
- text = self.df.loc[states[i], 'local']
77
- if(text=='minimum'):
78
- real_min = self.df.loc[problem, 'low'].idxmin()
79
- problem.remove(real_min)
80
- self.df.loc[problem, 'local']=''
81
- else:
82
- real_max = self.df.loc[problem, 'high'].idxmax()
83
- problem.remove(real_max)
84
- self.df.loc[problem, 'local']=''
85
-
86
- problem = []
87
- else:
88
- problem.append(states[i])
89
-
90
- states = self.df[self.df['local']!='']['local'].index.to_list()
91
-
92
- # if first is min ad the price
93
- if self.df.loc[states[0], 'local']== 'minimum':
94
- self.df.loc[states[0],'local_text'] = f"${round(self.df.loc[states[0], 'low'], 2)}"
95
- else:
96
- self.df.loc[states[0],'local_text'] = f"${round(self.df.loc[states[0], 'high'], 2)}"
97
-
98
- # add last fall if last local is max
99
- if list(self.df[self.df['local']!='']['local'])[-1]=='maximum':
100
- last_min_id = self.df.loc[self.df['low']==min(self.df['low'][-3:] )].index.to_list()[0]
101
- self.df.loc[last_min_id , 'local'] = 'minimum'
102
-
103
- states = self.df[self.df['local']!='']['local'].index.to_list()
104
-
105
- if self.static_plot:
106
-
107
- for i in range(1,len(states)):
108
- prev = self.df.loc[states[i-1], 'local']
109
- current= self.df.loc[states[i], 'local']
110
- prev_high = self.df.loc[states[i-1], 'high']
111
- prev_low = self.df.loc[states[i-1], 'low']
112
- current_high = self.df.loc[states[i], 'high']
113
- current_low = self.df.loc[states[i], 'low']
114
- if current == 'maximum':
115
- # rise
116
- rise = (current_high/ prev_low -1)*100
117
- if rise>100:
118
- self.df.loc[states[i], 'local_text'] = f'Up:{round(((rise+100)/100), 2)}x<br>${round(current_high, 2)}'
119
- else:
120
- self.df.loc[states[i], 'local_text'] = f'Up:{round(rise, 2)}%<br>${round(current_high, 2)}'
121
- else:
122
- fall = round((1-(current_low / prev_high))*100, 2)
123
- self.df.loc[states[i], 'local_text'] = f'Down:{fall}%<br>${round(current_low, 2)}'
124
- else:
125
-
126
- for i in range(1,len(states)):
127
- prev = self.df.loc[states[i-1], 'local']
128
- current= self.df.loc[states[i], 'local']
129
- prev_high = self.df.loc[states[i-1], 'high']
130
- prev_low = self.df.loc[states[i-1], 'low']
131
- current_high = self.df.loc[states[i], 'high']
132
- current_low = self.df.loc[states[i], 'low']
133
- if current == 'maximum':
134
- # rise
135
- rise = (current_high/ prev_low -1)*100
136
- if rise>100:
137
- self.df.loc[states[i], 'local_text'] = f'🚀🌌{round(((rise+100)/100), 2)}x<br>${round(current_high, 2)}'
138
- else:
139
- self.df.loc[states[i], 'local_text'] = f'🚀{round(rise, 2)}%<br>${round(current_high, 2)}'
140
- else:
141
- fall = round((1-(current_low / prev_high))*100, 2)
142
- if fall < 30:
143
- temj = '💸'
144
- elif fall < 50:
145
- temj = '💸'
146
- else:
147
- temj = '😭💔'
148
- self.df.loc[states[i], 'local_text'] = f'{temj}{fall}%<br>${round(current_low, 2)}'
149
-
150
-
151
- def plotly_last_year(self, plot_title, plot_height=900):
152
- tdf = self.df.tail(500)
153
-
154
- fig = go.Figure(data=go.Ohlc(x=tdf['date'], open=tdf['open'], high=tdf['high'], low=tdf['low'],close=tdf['close']))
155
-
156
- for index, row in tdf[tdf['local']!=''].iterrows():
157
- direction = row['local']
158
- tdate = row['date']
159
- local_text = row['local_text']
160
- min_price = row['low']
161
- max_price = row['high']
162
- if direction == 'maximum':
163
- fig.add_annotation( x=tdate, y=max_price, text=local_text, showarrow=True,
164
- align="center", bordercolor="#c7c7c7",
165
- font=dict(family="Courier New, monospace", size=16, color="#214e34" ), borderwidth=2,
166
- borderpad=4,
167
- bgcolor="#f4fdff",
168
- opacity=0.8,
169
- arrowhead=2,
170
- arrowsize=1,
171
- arrowwidth=1,
172
- ax=-45,ay=-45)
173
-
174
- if direction == 'minimum':
175
- fig.add_annotation( x=tdate, y=min_price, text=local_text, showarrow=True,
176
- align="center", bordercolor="#c7c7c7",
177
- font=dict(family="Courier New, monospace", size=16, color="red" ), borderwidth=2,
178
- borderpad=4,
179
- bgcolor="#f4fdff",
180
- opacity=0.8,
181
- arrowhead=2,
182
- arrowsize=1,
183
- arrowwidth=1,
184
- ax=45,ay=45)
185
-
186
- fig.update_layout(showlegend=False, plot_bgcolor='white', height=plot_height, title= plot_title)
187
-
188
- fig.update_xaxes(
189
- mirror=True,
190
- ticks='outside',
191
- showline=True,
192
- linecolor='black',
193
- gridcolor='lightgrey'
194
- )
195
- fig.update_yaxes(
196
- mirror=True,
197
- ticks='outside',
198
- showline=True,
199
- linecolor='black',
200
- gridcolor='lightgrey'
201
- )
202
- fig.update(layout_xaxis_rangeslider_visible=False)
203
- fig.add_trace( go.Scatter(x=tdf['date'], y=tdf['sma_50'], opacity =0.5, line=dict(color='lightblue', width = 2) , name = 'SMA 50') )
204
- fig.add_trace( go.Scatter(x=tdf['date'], y=tdf['sma_200'], opacity =0.7, line=dict(color='red', width = 2.5) , name = 'SMA 200') )
205
- return(fig)
206
-
207
-
208
- # https://stackoverflow.com/questions/71411995/pandas-plotly-secondary-graph-needs-to-be-to-rsi
209
-
210
- #https://wire.insiderfinance.io/plot-candlestick-rsi-bollinger-bands-and-macd-charts-using-yfinance-python-api-1c2cb182d147
@@ -1,10 +0,0 @@
1
- from tw import *
2
- from stocks import *
3
- from video import *
4
-
5
- tw =Tw()
6
- # stock_ticker = "AMD"
7
- # t = GoldHand(stock_ticker)
8
- # p = t.plotly_last_year(tw.get_plotly_title(stock_ticker))
9
- # p.update_layout(height=1080, width=1920)
10
- # p.write_image("fig2.png", )
File without changes
File without changes
File without changes
File without changes