goldhand 14.6__tar.gz → 15.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.
Potentially problematic release.
This version of goldhand might be problematic. Click here for more details.
- {goldhand-14.6 → goldhand-15.3}/PKG-INFO +1 -1
- {goldhand-14.6 → goldhand-15.3}/goldhand/backtest.py +3 -3
- {goldhand-14.6 → goldhand-15.3}/goldhand/stocks.py +7 -1
- {goldhand-14.6 → goldhand-15.3}/goldhand/strategy_goldhand_line.py +57 -28
- {goldhand-14.6 → goldhand-15.3}/goldhand/strategy_rsi.py +38 -30
- {goldhand-14.6 → goldhand-15.3}/goldhand/tw.py +4 -2
- {goldhand-14.6 → goldhand-15.3}/goldhand.egg-info/PKG-INFO +1 -1
- {goldhand-14.6 → goldhand-15.3}/setup.py +1 -1
- {goldhand-14.6 → goldhand-15.3}/README.md +0 -0
- {goldhand-14.6 → goldhand-15.3}/goldhand/__init__.py +0 -0
- {goldhand-14.6 → goldhand-15.3}/goldhand/helpers.py +0 -0
- {goldhand-14.6 → goldhand-15.3}/goldhand.egg-info/SOURCES.txt +0 -0
- {goldhand-14.6 → goldhand-15.3}/goldhand.egg-info/dependency_links.txt +0 -0
- {goldhand-14.6 → goldhand-15.3}/goldhand.egg-info/requires.txt +0 -0
- {goldhand-14.6 → goldhand-15.3}/goldhand.egg-info/top_level.txt +0 -0
- {goldhand-14.6 → goldhand-15.3}/setup.cfg +0 -0
|
@@ -30,8 +30,10 @@ class Backtest:
|
|
|
30
30
|
'ticker' : self.data['ticker'].iloc[0],
|
|
31
31
|
'number_of_trades' : self.trades.shape[0],
|
|
32
32
|
'win_ratio(%)' : round(((sum(self.trades['result'] >1) / self.trades.shape[0])*100),2),
|
|
33
|
-
|
|
34
33
|
'average_res(%)' : round(((self.trades['result'].mean()-1)*100),2),
|
|
34
|
+
'average_trade_len(days)' : round(self.trades['days_in_trade'].mean(), 0),
|
|
35
|
+
|
|
36
|
+
|
|
35
37
|
'median_res(%)': round(((self.trades['result'].median()-1)*100),2),
|
|
36
38
|
'cumulative_result': list(np.cumprod(self.trades['result'], axis=0))[-1],
|
|
37
39
|
'trade_results': ' # '.join([ str(round(((x-1)*100),2)) for x in self.trades['result']]),
|
|
@@ -46,8 +48,6 @@ class Backtest:
|
|
|
46
48
|
'looser_trades_mean' : round((( np.mean([x for x in self.trades['result'] if x<1])-1)*100),2),
|
|
47
49
|
'looser_trades_median' : round((( np.median([x for x in self.trades['result'] if x<1])-1)*100),2),
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
'average_trade_len(days)' : self.trades['days_in_trade'].mean(),
|
|
51
51
|
'median_trade_len(days)' : self.trades['days_in_trade'].median(),
|
|
52
52
|
|
|
53
53
|
|
|
@@ -21,6 +21,9 @@ class GoldHand:
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
def get_olhc(self):
|
|
24
|
+
"""
|
|
25
|
+
Download historical stock data for the last year
|
|
26
|
+
"""
|
|
24
27
|
#scraper = cloudscraper.create_scraper()
|
|
25
28
|
response = self.scraper.get(f"https://query1.finance.yahoo.com/v8/finance/chart/{self.ticker}?interval={self.interval}&range={self.range}")
|
|
26
29
|
t= response.json()
|
|
@@ -32,6 +35,9 @@ class GoldHand:
|
|
|
32
35
|
return(df)
|
|
33
36
|
|
|
34
37
|
def smma(self, data, window, colname):
|
|
38
|
+
"""
|
|
39
|
+
Calculate Smoothed Moving Average (SMMA)
|
|
40
|
+
"""
|
|
35
41
|
hl2 = data['hl2'].values
|
|
36
42
|
smma_values = [hl2[0]]
|
|
37
43
|
|
|
@@ -141,7 +147,7 @@ class GoldHand:
|
|
|
141
147
|
else:
|
|
142
148
|
temj = '😭💔'
|
|
143
149
|
self.df.loc[states[i], 'local_text'] = f'{temj}{fall}%<br>${round(current_low, 2)}'
|
|
144
|
-
|
|
150
|
+
self.df.reset_index(inplace=True, drop=True)
|
|
145
151
|
|
|
146
152
|
def plotly_last_year(self, plot_title, plot_height=900, ndays=500, ad_local_min_max=True):
|
|
147
153
|
tdf = self.df.tail(ndays)
|
|
@@ -8,7 +8,16 @@ from goldhand import *
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
def goldhand_line_strategy(data):
|
|
11
|
+
def goldhand_line_strategy(data, buy_at='gold', sell_at='grey'):
|
|
12
|
+
"""
|
|
13
|
+
This function implements the goldhand line strategy.
|
|
14
|
+
Parameters:
|
|
15
|
+
data (pandas.DataFrame): The dataframe containing the data.
|
|
16
|
+
buy_at (str): The color of the line to buy at. Default is 'gold'.
|
|
17
|
+
sell_at (str): The color of the line to sell at. Default is 'grey'.
|
|
18
|
+
Returns:
|
|
19
|
+
res_df (pandas.DataFrame): The dataframe containing the results.
|
|
20
|
+
"""
|
|
12
21
|
|
|
13
22
|
data['hl2'] = (data['high'] + data['low'])/2
|
|
14
23
|
|
|
@@ -45,23 +54,33 @@ def goldhand_line_strategy(data):
|
|
|
45
54
|
# Check if not already in a trade
|
|
46
55
|
if not in_trade:
|
|
47
56
|
# Generate buy signal
|
|
48
|
-
if (data['color'][i] ==
|
|
49
|
-
|
|
50
|
-
|
|
57
|
+
if (data['color'][i] ==buy_at) :
|
|
58
|
+
|
|
59
|
+
if i == (len(data) -1):
|
|
60
|
+
temp_trade['buy_price'] = data['close'][i]
|
|
61
|
+
temp_trade.update(dict(data.iloc[i].add_prefix('buy_')))
|
|
62
|
+
else:
|
|
63
|
+
temp_trade['buy_price'] = data['open'][i+1]
|
|
64
|
+
temp_trade.update(dict(data.iloc[i+1].add_prefix('buy_')))
|
|
65
|
+
|
|
66
|
+
|
|
51
67
|
temp_trade['trade_id'] = trade_id
|
|
52
68
|
temp_trade['status'] = 'open'
|
|
53
|
-
temp_trade.update(dict(data.iloc[i].add_prefix('buy_')))
|
|
54
69
|
in_trade = True # Set flag to indicate in a trade
|
|
55
70
|
else:
|
|
56
71
|
# Generate sell signal
|
|
57
|
-
if (data['color'][i] ==
|
|
58
|
-
|
|
59
|
-
|
|
72
|
+
if (data['color'][i] ==sell_at) :
|
|
73
|
+
|
|
74
|
+
if i == (len(data) -1):
|
|
75
|
+
temp_trade['sell_price'] = data['close'][i]
|
|
76
|
+
temp_trade.update(dict(data.iloc[i].add_prefix('sell_')))
|
|
77
|
+
else:
|
|
78
|
+
temp_trade['sell_price'] = data['open'][i+1]
|
|
79
|
+
temp_trade.update(dict(data.iloc[i+1].add_prefix('sell_')))
|
|
80
|
+
|
|
60
81
|
temp_trade['trade_id'] = trade_id
|
|
61
82
|
temp_trade['status'] = 'closed'
|
|
62
83
|
|
|
63
|
-
temp_trade.update(dict(data.iloc[i].add_prefix('sell_')))
|
|
64
|
-
|
|
65
84
|
# calculate results
|
|
66
85
|
temp_trade['result'] = temp_trade['sell_price'] / temp_trade['buy_price']
|
|
67
86
|
temp_trade['days_in_trade'] = (temp_trade['sell_date'] - temp_trade['buy_date']).days
|
|
@@ -91,7 +110,18 @@ def goldhand_line_strategy(data):
|
|
|
91
110
|
return(res_df)
|
|
92
111
|
|
|
93
112
|
|
|
94
|
-
def show_indicator_goldhand_line_strategy(ticker, plot_title = '', ndays=0, plot_height=
|
|
113
|
+
def show_indicator_goldhand_line_strategy(ticker, plot_title = '', buy_at='gold', sell_at='grey', ndays=0, plot_height=1000, add_strategy_summary = True):
|
|
114
|
+
"""
|
|
115
|
+
This function shows the goldhand line strategy on a plotly candlestick chart.
|
|
116
|
+
Parameters:
|
|
117
|
+
ticker (str): The ticker of the stock or ETF.
|
|
118
|
+
plot_title (str): The title of the plot.
|
|
119
|
+
ndays (int): The number of days to show. If 0, all data will be shown.
|
|
120
|
+
plot_height (int): The height of the plot.
|
|
121
|
+
add_strategy_summary (bool): If True, the strategy summary will be added to the plot.
|
|
122
|
+
Returns:
|
|
123
|
+
fig (plotly.graph_objects.Figure): The plotly figure.
|
|
124
|
+
"""
|
|
95
125
|
|
|
96
126
|
data = GoldHand(ticker).df
|
|
97
127
|
|
|
@@ -129,11 +159,10 @@ def show_indicator_goldhand_line_strategy(ticker, plot_title = '', ndays=0, plot
|
|
|
129
159
|
# Create a 'group' column and increase the value only when there's a color change
|
|
130
160
|
data['group'] = (data['color_change']).cumsum()
|
|
131
161
|
|
|
132
|
-
|
|
133
162
|
##### data prepar end
|
|
134
163
|
|
|
135
164
|
##### backtest
|
|
136
|
-
backtest = Backtest( data, goldhand_line_strategy, plot_title =plot_title)
|
|
165
|
+
backtest = Backtest( data, goldhand_line_strategy, plot_title =plot_title, buy_at='gold', sell_at='grey')
|
|
137
166
|
trades =backtest.trades
|
|
138
167
|
|
|
139
168
|
if ndays!=0:
|
|
@@ -237,21 +266,21 @@ def show_indicator_goldhand_line_strategy(ticker, plot_title = '', ndays=0, plot
|
|
|
237
266
|
fig.update_layout(showlegend=False, plot_bgcolor='white', height=plot_height, title=plot_title)
|
|
238
267
|
fig.update(layout_xaxis_rangeslider_visible=False)
|
|
239
268
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
269
|
+
if add_strategy_summary:
|
|
270
|
+
t= backtest.trades_summary
|
|
271
|
+
trade_text = f"Trades: {t['number_of_trades']}<br>"\
|
|
272
|
+
f"Win ratio: {t['win_ratio(%)']}%<br>"\
|
|
273
|
+
f"Average result: {t['average_res(%)']}%<br>"\
|
|
274
|
+
f"Median result: {t['median_res(%)']}%<br>"\
|
|
275
|
+
f"Average trade lenght: {round(t['average_trade_len(days)'], 0)} days<br>"\
|
|
276
|
+
f"Cumulative result: {round(t['cumulative_result'], 2)}x<br>"\
|
|
277
|
+
f"Profitable trades mean: {t['profitable_trades_mean']}%<br>"\
|
|
278
|
+
f"Profitable trades median: {t['profitable_trades_median']}%<br>"\
|
|
279
|
+
f"Looser trades mean: {t['looser_trades_mean']}%<br>"\
|
|
280
|
+
f"Looser trades median: {t['looser_trades_median']}%<br>"
|
|
281
|
+
|
|
282
|
+
# Add a larger textbox using annotations
|
|
283
|
+
fig.add_annotation( go.layout.Annotation( x=tex_loc[0], y=tex_loc[1], xref='paper', yref='paper', text=trade_text, showarrow=True, arrowhead=4, ax=0, ay=0, bordercolor='black', borderwidth=2, bgcolor='white', align='left', font=dict(size=14, color='black')))
|
|
255
284
|
|
|
256
285
|
# Show the plot
|
|
257
286
|
return (fig)
|
|
@@ -20,33 +20,43 @@ def rsi_strategy(data, buy_threshold = 30, sell_threshold = 70):
|
|
|
20
20
|
# Check if not already in a trade
|
|
21
21
|
if not in_trade:
|
|
22
22
|
# Generate buy signal
|
|
23
|
+
#You have to change olne the buy and sell signal
|
|
23
24
|
if data['rsi'][i] < buy_threshold:
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
if i == (len(data) -1):
|
|
27
|
+
temp_trade['buy_price'] = data['close'][i]
|
|
28
|
+
temp_trade.update(dict(data.iloc[i].add_prefix('buy_')))
|
|
29
|
+
else:
|
|
30
|
+
temp_trade['buy_price'] = data['open'][i+1]
|
|
31
|
+
temp_trade.update(dict(data.iloc[i+1].add_prefix('buy_')))
|
|
32
|
+
|
|
26
33
|
temp_trade['trade_id'] = trade_id
|
|
27
34
|
temp_trade['status'] = 'open'
|
|
28
|
-
temp_trade.update(dict(data.iloc[i].add_prefix('buy_')))
|
|
29
35
|
in_trade = True # Set flag to indicate in a trade
|
|
30
36
|
else:
|
|
31
37
|
# Generate sell signal
|
|
38
|
+
#You have to change olne the buy and sell signal
|
|
32
39
|
if data['rsi'][i] > sell_threshold:
|
|
33
|
-
|
|
34
|
-
|
|
40
|
+
if i == (len(data) -1):
|
|
41
|
+
temp_trade['sell_price'] = data['close'][i]
|
|
42
|
+
temp_trade.update(dict(data.iloc[i].add_prefix('sell_')))
|
|
43
|
+
else:
|
|
44
|
+
temp_trade['sell_price'] = data['open'][i+1]
|
|
45
|
+
temp_trade.update(dict(data.iloc[i+1].add_prefix('sell_')))
|
|
46
|
+
|
|
35
47
|
temp_trade['trade_id'] = trade_id
|
|
36
48
|
temp_trade['status'] = 'closed'
|
|
37
49
|
|
|
38
|
-
|
|
39
|
-
|
|
50
|
+
|
|
40
51
|
# calculate results
|
|
41
52
|
temp_trade['result'] = temp_trade['sell_price'] / temp_trade['buy_price']
|
|
42
53
|
temp_trade['days_in_trade'] = (temp_trade['sell_date'] - temp_trade['buy_date']).days
|
|
43
54
|
|
|
44
|
-
|
|
45
|
-
|
|
46
55
|
in_trade = False # Reset flag to indicate not in a trade
|
|
47
56
|
trade_id +=1
|
|
48
57
|
all_trades.append(temp_trade)
|
|
49
58
|
temp_trade = {}
|
|
59
|
+
|
|
50
60
|
if temp_trade:
|
|
51
61
|
temp_trade['sell_price'] = data['close'][i]
|
|
52
62
|
temp_trade['trade_id'] = trade_id
|
|
@@ -59,7 +69,6 @@ def rsi_strategy(data, buy_threshold = 30, sell_threshold = 70):
|
|
|
59
69
|
res_df = pd.DataFrame(all_trades)
|
|
60
70
|
|
|
61
71
|
# change orders
|
|
62
|
-
|
|
63
72
|
all_col = res_df.columns.tolist()
|
|
64
73
|
first = ['result', 'buy_price', 'sell_price', 'buy_date', 'sell_date', 'days_in_trade']
|
|
65
74
|
first.extend([x for x in all_col if x not in first])
|
|
@@ -68,14 +77,14 @@ def rsi_strategy(data, buy_threshold = 30, sell_threshold = 70):
|
|
|
68
77
|
|
|
69
78
|
|
|
70
79
|
|
|
71
|
-
def show_indicator_rsi_strategy(ticker, buy_threshold = 30, sell_threshold = 70, plot_title = '', ndays=0, plot_height=
|
|
80
|
+
def show_indicator_rsi_strategy(ticker, buy_threshold = 30, sell_threshold = 70, plot_title = '', ndays=0, plot_height=1000, add_strategy_summary = True):
|
|
72
81
|
|
|
73
82
|
tdf = GoldHand(ticker).df
|
|
74
83
|
backtest = Backtest( tdf, rsi_strategy, buy_threshold=buy_threshold, sell_threshold=sell_threshold)
|
|
75
84
|
trades =backtest.trades
|
|
76
85
|
|
|
77
|
-
if ndays
|
|
78
|
-
tdf =
|
|
86
|
+
if ndays > 0:
|
|
87
|
+
tdf = tdf.tail(ndays)
|
|
79
88
|
trades = trades.loc[trades['buy_date']>tdf.date.min()]
|
|
80
89
|
|
|
81
90
|
if tdf['high'].max() == max(tdf['high'][0:50]):
|
|
@@ -86,7 +95,7 @@ def show_indicator_rsi_strategy(ticker, buy_threshold = 30, sell_threshold = 70,
|
|
|
86
95
|
|
|
87
96
|
|
|
88
97
|
# Create subplots with shared x-axis and custom heights
|
|
89
|
-
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1, subplot_titles=[
|
|
98
|
+
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1, subplot_titles=['', "RSI"], row_heights=[0.7, 0.3])
|
|
90
99
|
|
|
91
100
|
# Add OHLC candlestick chart
|
|
92
101
|
fig.add_trace(go.Ohlc(x=tdf['date'], open=tdf['open'], high=tdf['high'], low=tdf['low'], close=tdf['close']), row=1, col=1)
|
|
@@ -151,7 +160,7 @@ def show_indicator_rsi_strategy(ticker, buy_threshold = 30, sell_threshold = 70,
|
|
|
151
160
|
font=dict(size=13, color=triangle_color, family="Times New Roman")))
|
|
152
161
|
|
|
153
162
|
# Update layout
|
|
154
|
-
fig.update_layout(showlegend=False, plot_bgcolor='white', height=
|
|
163
|
+
fig.update_layout(showlegend=False, plot_bgcolor='white', height=plot_height, title=plot_title)
|
|
155
164
|
fig.update(layout_xaxis_rangeslider_visible=False)
|
|
156
165
|
|
|
157
166
|
# Update x-axes and y-axes for the main chart
|
|
@@ -162,22 +171,21 @@ def show_indicator_rsi_strategy(ticker, buy_threshold = 30, sell_threshold = 70,
|
|
|
162
171
|
fig.update_xaxes(mirror=True, ticks='outside', showline=True, linecolor='black', gridcolor='lightgrey', row=2, col=1)
|
|
163
172
|
fig.update_yaxes(mirror=True, ticks='outside', showline=True, linecolor='black', gridcolor='lightgrey', row=2, col=1)
|
|
164
173
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
174
|
+
if add_strategy_summary:
|
|
175
|
+
t= backtest.trades_summary
|
|
176
|
+
trade_text = f"Trades: {t['number_of_trades']}<br>"\
|
|
177
|
+
f"Win ratio: {t['win_ratio(%)']}%<br>"\
|
|
178
|
+
f"Average result: {t['average_res(%)']}%<br>"\
|
|
179
|
+
f"Median result: {t['median_res(%)']}%<br>"\
|
|
180
|
+
f"Average trade length: {round(t['average_trade_len(days)'], 0)} days<br>"\
|
|
181
|
+
f"Cumulative result: {round(t['cumulative_result'], 2)}x<br>"\
|
|
182
|
+
f"Profitable trades mean: {t['profitable_trades_mean']}%<br>"\
|
|
183
|
+
f"Profitable trades median: {t['profitable_trades_median']}%<br>"\
|
|
184
|
+
f"Looser trades mean: {t['looser_trades_mean']}%<br>"\
|
|
185
|
+
f"Looser trades median: {t['looser_trades_median']}%<br>"
|
|
186
|
+
|
|
187
|
+
# Add a larger textbox using annotations
|
|
188
|
+
fig.add_annotation( go.layout.Annotation( x=tex_loc[0], y=tex_loc[1], xref='paper', yref='paper', text=trade_text, showarrow=True, arrowhead=4, ax=0, ay=0, bordercolor='black', borderwidth=2, bgcolor='white', align='left', font=dict(size=14, color='black')))
|
|
181
189
|
|
|
182
190
|
|
|
183
191
|
# Add RSI line
|
|
@@ -92,7 +92,9 @@ class Tw:
|
|
|
92
92
|
|
|
93
93
|
secdf.rename(columns = {'description': 'Company'}, inplace=True)
|
|
94
94
|
|
|
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')
|
|
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
|
+
|
|
97
|
+
fig = px.bar(secdf, x='name', y='market_cap_basic', title = self.get_plotly_title(ticker), labels={'market_cap_basic':'Market kapitalization'}, text='Company')
|
|
96
98
|
|
|
97
99
|
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)
|
|
98
100
|
fig.update_layout(showlegend=False, plot_bgcolor='white', height=600)
|
|
@@ -110,7 +112,7 @@ class Tw:
|
|
|
110
112
|
|
|
111
113
|
inddf.rename(columns = {'description': 'Company'}, inplace=True)
|
|
112
114
|
|
|
113
|
-
fig = px.bar(inddf, x='name', y='market_cap_basic', title =
|
|
115
|
+
fig = px.bar(inddf, x='name', y='market_cap_basic', title = self.get_plotly_title(ticker), labels={'market_cap_basic':'Market kapitalization'}, text='Company')
|
|
114
116
|
|
|
115
117
|
|
|
116
118
|
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)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|