goldhand 11.3__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.
- {goldhand-11.3 → goldhand-12.7}/PKG-INFO +1 -1
- {goldhand-11.3 → goldhand-12.7}/goldhand/__init__.py +2 -1
- goldhand-12.7/goldhand/backtest.py +112 -0
- {goldhand-11.3 → goldhand-12.7}/goldhand/stocks.py +69 -2
- goldhand-12.7/goldhand/strategy_rsi.py +166 -0
- {goldhand-11.3 → goldhand-12.7}/goldhand.egg-info/PKG-INFO +1 -1
- {goldhand-11.3 → goldhand-12.7}/goldhand.egg-info/SOURCES.txt +2 -0
- {goldhand-11.3 → goldhand-12.7}/setup.py +1 -1
- {goldhand-11.3 → goldhand-12.7}/README.md +0 -0
- {goldhand-11.3 → goldhand-12.7}/goldhand/helpers.py +0 -0
- {goldhand-11.3 → goldhand-12.7}/goldhand/tw.py +0 -0
- {goldhand-11.3 → goldhand-12.7}/goldhand.egg-info/dependency_links.txt +0 -0
- {goldhand-11.3 → goldhand-12.7}/goldhand.egg-info/requires.txt +0 -0
- {goldhand-11.3 → goldhand-12.7}/goldhand.egg-info/top_level.txt +0 -0
- {goldhand-11.3 → goldhand-12.7}/setup.cfg +0 -0
|
@@ -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)
|
|
@@ -30,6 +30,17 @@ class GoldHand:
|
|
|
30
30
|
if self.ad_ticker:
|
|
31
31
|
df['ticker'] = self.ticker
|
|
32
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
|
|
33
44
|
|
|
34
45
|
|
|
35
46
|
|
|
@@ -37,7 +48,8 @@ class GoldHand:
|
|
|
37
48
|
# Download historical stock data for the last year
|
|
38
49
|
self.df = self.get_olhc()
|
|
39
50
|
self.df.columns = self.df.columns.str.lower()
|
|
40
|
-
|
|
51
|
+
self.df['hl2'] = (self.df['high'] + self.df['low'])/2
|
|
52
|
+
|
|
41
53
|
# Rsi
|
|
42
54
|
self.df['rsi'] = ta.rsi(self.df['close'], 14)
|
|
43
55
|
|
|
@@ -148,7 +160,7 @@ class GoldHand:
|
|
|
148
160
|
if direction == 'minimum':
|
|
149
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)
|
|
150
162
|
|
|
151
|
-
|
|
163
|
+
fig.update_layout(showlegend=False, plot_bgcolor='white', height=plot_height, title= plot_title)
|
|
152
164
|
|
|
153
165
|
fig.update_xaxes( mirror=True, ticks='outside', showline=True, linecolor='black', gridcolor='lightgrey' )
|
|
154
166
|
fig.update_yaxes( mirror=True, ticks='outside', showline=True, linecolor='black', gridcolor='lightgrey')
|
|
@@ -157,7 +169,62 @@ class GoldHand:
|
|
|
157
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') )
|
|
158
170
|
return(fig)
|
|
159
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)' }
|
|
160
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
|
+
|
|
161
228
|
# https://stackoverflow.com/questions/71411995/pandas-plotly-secondary-graph-needs-to-be-to-rsi
|
|
162
229
|
|
|
163
230
|
#https://wire.insiderfinance.io/plot-candlestick-rsi-bollinger-bands-and-macd-charts-using-yfinance-python-api-1c2cb182d147
|
|
@@ -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)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|