goldhand 10.0__py3-none-any.whl → 12.7__py3-none-any.whl
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.
- goldhand/__init__.py +2 -2
- goldhand/backtest.py +112 -0
- goldhand/stocks.py +109 -88
- goldhand/strategy_rsi.py +166 -0
- goldhand/tw.py +6 -22
- {goldhand-10.0.dist-info → goldhand-12.7.dist-info}/METADATA +1 -1
- goldhand-12.7.dist-info/RECORD +10 -0
- {goldhand-10.0.dist-info → goldhand-12.7.dist-info}/WHEEL +1 -1
- goldhand/test.py +0 -10
- goldhand/video.py +0 -0
- goldhand-10.0.dist-info/RECORD +0 -10
- {goldhand-10.0.dist-info → goldhand-12.7.dist-info}/top_level.txt +0 -0
goldhand/__init__.py
CHANGED
goldhand/backtest.py
ADDED
|
@@ -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)
|
goldhand/stocks.py
CHANGED
|
@@ -10,31 +10,46 @@ import json
|
|
|
10
10
|
import cloudscraper
|
|
11
11
|
|
|
12
12
|
class GoldHand:
|
|
13
|
-
def __init__(self, ticker,
|
|
13
|
+
def __init__(self, ticker, ad_ticker=True, range='18y', interval='1d'):
|
|
14
14
|
self.scraper = cloudscraper.create_scraper()
|
|
15
|
+
self.ad_ticker = ad_ticker
|
|
16
|
+
self.range = range
|
|
17
|
+
self.interval = interval
|
|
15
18
|
self.ticker = ticker
|
|
16
19
|
self.df = None
|
|
17
|
-
self.static_plot = static_plot
|
|
18
20
|
self.download_historical_data()
|
|
19
21
|
|
|
20
22
|
|
|
21
|
-
def get_olhc(self
|
|
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}")
|
|
23
26
|
t= response.json()
|
|
24
27
|
df = pd.DataFrame(t['chart']['result'][0]['indicators']['quote'][0])
|
|
25
28
|
df['date'] = pd.to_datetime(t['chart']['result'][0]['timestamp'], unit='s').date
|
|
26
29
|
df = df[['date', 'open', 'low', 'high', 'close', 'volume']]
|
|
27
|
-
if ad_ticker:
|
|
28
|
-
df['ticker'] = ticker
|
|
30
|
+
if self.ad_ticker:
|
|
31
|
+
df['ticker'] = self.ticker
|
|
29
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
|
|
30
44
|
|
|
31
45
|
|
|
32
46
|
|
|
33
47
|
def download_historical_data(self):
|
|
34
48
|
# Download historical stock data for the last year
|
|
35
|
-
self.df = self.get_olhc(
|
|
49
|
+
self.df = self.get_olhc()
|
|
36
50
|
self.df.columns = self.df.columns.str.lower()
|
|
37
|
-
|
|
51
|
+
self.df['hl2'] = (self.df['high'] + self.df['low'])/2
|
|
52
|
+
|
|
38
53
|
# Rsi
|
|
39
54
|
self.df['rsi'] = ta.rsi(self.df['close'], 14)
|
|
40
55
|
|
|
@@ -102,50 +117,30 @@ class GoldHand:
|
|
|
102
117
|
|
|
103
118
|
states = self.df[self.df['local']!='']['local'].index.to_list()
|
|
104
119
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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)}'
|
|
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)}'
|
|
121
133
|
else:
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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)}'
|
|
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 = '💸'
|
|
140
141
|
else:
|
|
141
|
-
|
|
142
|
-
|
|
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)}'
|
|
142
|
+
temj = '😭💔'
|
|
143
|
+
self.df.loc[states[i], 'local_text'] = f'{temj}{fall}%<br>${round(current_low, 2)}'
|
|
149
144
|
|
|
150
145
|
|
|
151
146
|
def plotly_last_year(self, plot_title, plot_height=900):
|
|
@@ -160,51 +155,77 @@ class GoldHand:
|
|
|
160
155
|
min_price = row['low']
|
|
161
156
|
max_price = row['high']
|
|
162
157
|
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)
|
|
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)
|
|
173
159
|
|
|
174
160
|
if direction == 'minimum':
|
|
175
|
-
fig.add_annotation( x=tdate, y=min_price, text=local_text, showarrow=True,
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
)
|
|
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')
|
|
202
167
|
fig.update(layout_xaxis_rangeslider_visible=False)
|
|
203
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') )
|
|
204
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') )
|
|
205
170
|
return(fig)
|
|
206
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)
|
|
207
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
|
+
|
|
208
228
|
# https://stackoverflow.com/questions/71411995/pandas-plotly-secondary-graph-needs-to-be-to-rsi
|
|
209
229
|
|
|
210
|
-
#https://wire.insiderfinance.io/plot-candlestick-rsi-bollinger-bands-and-macd-charts-using-yfinance-python-api-1c2cb182d147
|
|
230
|
+
#https://wire.insiderfinance.io/plot-candlestick-rsi-bollinger-bands-and-macd-charts-using-yfinance-python-api-1c2cb182d147
|
|
231
|
+
|
goldhand/strategy_rsi.py
ADDED
|
@@ -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)
|
goldhand/tw.py
CHANGED
|
@@ -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","
|
|
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)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
goldhand/__init__.py,sha256=PYVuqK8lz7FmiWuiBngkqB1SBTUxA002In9d4z1OESA,115
|
|
2
|
+
goldhand/backtest.py,sha256=3TTN98Yt_CIgCdt3vSLx-fmDieJZvWr2K_HvFAqgPJg,6124
|
|
3
|
+
goldhand/helpers.py,sha256=l9yn0kVTiwfUR8sI5nH1QFx6dYikaUQgRA227Ox7hs0,6130
|
|
4
|
+
goldhand/stocks.py,sha256=0MnjAex2A2c06JHinToKHBAvUJvYtDFXxtGudqBux6o,10521
|
|
5
|
+
goldhand/strategy_rsi.py,sha256=X38rjkjcph_D7U1CSh8Ismc8CFwhfr3rTWc7VinLIv8,7567
|
|
6
|
+
goldhand/tw.py,sha256=K8MwMDkW5JtBFBG0qcPzj8OVx2OhDjrOH2UGo6nwtSs,8375
|
|
7
|
+
goldhand-12.7.dist-info/METADATA,sha256=RYqbk7_AKvGicD-vqX6GycP9SWQ2C6PJZbyhmJDO4Mo,1952
|
|
8
|
+
goldhand-12.7.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
9
|
+
goldhand-12.7.dist-info/top_level.txt,sha256=siEJ2_a_Fx_7hqRI4Ms6SzCelbXrK_1H_eOF8KAaMdA,9
|
|
10
|
+
goldhand-12.7.dist-info/RECORD,,
|
goldhand/test.py
DELETED
|
@@ -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", )
|
goldhand/video.py
DELETED
|
File without changes
|
goldhand-10.0.dist-info/RECORD
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
goldhand/__init__.py,sha256=emyQ10OQsNR6j0i7vSedLP2c-3SXr4tZKK9Xz0twW9s,85
|
|
2
|
-
goldhand/helpers.py,sha256=l9yn0kVTiwfUR8sI5nH1QFx6dYikaUQgRA227Ox7hs0,6130
|
|
3
|
-
goldhand/stocks.py,sha256=bmpDEXfIRMhgYOnyyOUiNRkKAxSLN1TlVZLj2hkUw1A,9085
|
|
4
|
-
goldhand/test.py,sha256=zXeBIeaBU5GCypO1NpcqitImCBTII2xVDGmBcbYNzns,253
|
|
5
|
-
goldhand/tw.py,sha256=U6B-DRLjlFN-cWvnBsEuw4o5XMLynhycg0c5de76uqI,8466
|
|
6
|
-
goldhand/video.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
goldhand-10.0.dist-info/METADATA,sha256=d9YWTibKaZERt13oeWf8hKx_BqSO3AYzRpCrEn_vOeg,1952
|
|
8
|
-
goldhand-10.0.dist-info/WHEEL,sha256=Xo9-1PvkuimrydujYJAjF7pCkriuXBpUPEjma1nZyJ0,92
|
|
9
|
-
goldhand-10.0.dist-info/top_level.txt,sha256=siEJ2_a_Fx_7hqRI4Ms6SzCelbXrK_1H_eOF8KAaMdA,9
|
|
10
|
-
goldhand-10.0.dist-info/RECORD,,
|
|
File without changes
|