Description
Hello,
FX_NAS100, 5.csv
I hope I'm not out of line with the problem I can't solve.
I would like to draw a red line and a green line representing the amplitude of the US trading session on the NASDAQ100.
The history is in candlestick units of 5 minutes.
The two lines (for the historical period) should be drawn from the 15:30 candle to the 21:55 candle.
Tracing is correct at first, but the lines move as soon as you use the program's zoom or advance functions.
Perhaps tracing with “axhline” isn't the right solution; I've tried using the xplot method to exploit the position of the reference candle index, but I can't get anywhere.
Do you have any ideas for solving this problem?
Later, I want to draw other lines with different levels, but until I solve this problem, I'm stuck.
Thanks
import` pandas as pd
import mplfinance as mpf
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import tkinter as tk
from tkinter import ttk
import os
import matplotlib.pyplot as plt
df_bourse = pd.DataFrame([
{'Année': 2022, 'Période': 'Été US', 'Début': '2022-03-14', 'Fin': '2022-03-28', 'Modedate': 1},
{'Année': 2022, 'Période': 'Hiver US', 'Début': '2022-10-31', 'Fin': '2022-11-04', 'Modedate': 1},
{'Année': 2023, 'Période': 'Été US', 'Début': '2023-03-13', 'Fin': '2023-03-24', 'Modedate': 1},
{'Année': 2023, 'Période': 'Hiver US', 'Début': '2023-10-30', 'Fin': '2023-11-03', 'Modedate': 1},
{'Année': 2024, 'Période': 'Été US', 'Début': '2024-03-11', 'Fin': '2024-03-28', 'Modedate': 1},
{'Année': 2024, 'Période': 'Hiver US', 'Début': '2024-10-28', 'Fin': '2024-11-01', 'Modedate': 1}
])
df_bourse['Début'] = pd.to_datetime(df_bourse['Début'])
df_bourse['Fin'] = pd.to_datetime(df_bourse['Fin'])
csv_path = r"C:\Formation Python\FX_NAS100, 5.csv"
df = pd.read_csv(csv_path)
csv_filename = os.path.basename(csv_path)
df["time"] = pd.to_datetime(df["time"], unit='s')
df['date'] = df['time'].dt.date
def determine_modedate(date):
for _, row in df_bourse.iterrows():
if row['Début'].date() <= date <= row['Fin'].date():
return row['Modedate']
return 2
df['Modedate'] = df['date'].apply(determine_modedate)
df['amp_journée'] = 0.0
for day in df['date'].unique():
day_data = df[df['date'] == day]
if not day_data.empty:
modedate = day_data['Modedate'].iloc[0]
if modedate == 1:
start_time = pd.to_datetime("14:30").time()
end_time = pd.to_datetime("21:00").time()
else:
start_time = pd.to_datetime("15:30").time()
end_time = pd.to_datetime("22:00").time()
filtered_data = day_data[(day_data['time'].dt.time >= start_time) & (day_data['time'].dt.time <= end_time)]
if not filtered_data.empty:
high_max = filtered_data['high'].max()
low_min = filtered_data['low'].min()
amplitude = high_max - low_min
df.loc[df['date'] == day, 'amp_journée'] = amplitude
percentages = [25, 50, 75, 100, 125, 150, 175, 200, 225, 250, 275]
for p in percentages:
df[f'{p}%'] = df['amp_journée'] * (p / 100)
df[f'-{p}%'] = df['amp_journée'] * (-p / 100)
cols = ['date', 'time', 'open', 'high', 'low', 'close', 'Modedate', 'amp_journée']
percentage_cols = [f'{p}%' for p in percentages] + [f'-{p}%' for p in percentages]
df = df[cols + percentage_cols]
# Interface graphique principale
root = tk.Tk()
root.title("Graphique en chandelier japonais")
root.state('zoomed')
frame = ttk.Frame(root)
frame.pack(fill=tk.BOTH, expand=True)
start_index = 0
num_candles = 100
# Champs pour les pas
step_frame = ttk.Frame(root)
step_frame.pack(side=tk.TOP, fill=tk.X)
tk.Label(step_frame, text="Pas graphique:").pack(side=tk.LEFT, padx=5)
graph_step_entry = ttk.Entry(step_frame, width=5)
graph_step_entry.insert(0, "30")
graph_step_entry.pack(side=tk.LEFT, padx=5)
tk.Label(step_frame, text="Pas zoom:").pack(side=tk.LEFT, padx=5)
zoom_step_entry = ttk.Entry(step_frame, width=5)
zoom_step_entry.insert(0, "30")
zoom_step_entry.pack(side=tk.LEFT, padx=5)
# Fenêtre pour afficher le DataFrame
def show_dataframe():
df_window = tk.Toplevel(root)
df_window.title("Tableau des données")
df_window.state('zoomed')
tree_frame = ttk.Frame(df_window)
tree_frame.pack(fill=tk.BOTH, expand=True)
tree_scroll_y = ttk.Scrollbar(tree_frame, orient=tk.VERTICAL)
tree_scroll_x = ttk.Scrollbar(tree_frame, orient=tk.HORIZONTAL)
tree = ttk.Treeview(tree_frame, yscrollcommand=tree_scroll_y.set, xscrollcommand=tree_scroll_x.set)
tree_scroll_y.pack(side=tk.RIGHT, fill=tk.Y)
tree_scroll_x.pack(side=tk.BOTTOM, fill=tk.X)
tree_scroll_y.config(command=tree.yview)
tree_scroll_x.config(command=tree.xview)
tree.pack(fill=tk.BOTH, expand=True)
df_display = df.copy().reset_index()
numeric_columns = ['amp_journée'] + [col for col in df_display.columns if '%' in col] + ['open', 'high', 'low', 'close']
for column in numeric_columns:
if column in df_display.columns:
df_display[column] = df_display[column].round(2)
tree["columns"] = ["index"] + list(df_display.columns)[1:]
tree["show"] = "headings"
tree.heading("index", text="Index")
tree.column("index", width=50, anchor='center')
for column in df_display.columns[1:]:
tree.heading(column, text=column)
if column == 'time':
tree.column(column, width=200, anchor='center')
else:
tree.column(column, width=100, anchor='center')
for _, row in df_display.iterrows():
tree.insert("", "end", values=[row['index']] + list(row)[1:])
# Champ de saisie pour l'index
index_frame = ttk.Frame(df_window)
index_frame.pack(side=tk.TOP, fill=tk.X)
tk.Label(index_frame, text="Index:").pack(side=tk.LEFT, padx=5)
index_entry = ttk.Entry(index_frame, width=10)
index_entry.pack(side=tk.LEFT, padx=5)
def select_by_index(event=None):
index = index_entry.get()
if index.isdigit():
index = int(index)
for item in tree.get_children():
if int(tree.item(item)['values'][0]) == index:
tree.selection_set(item)
tree.see(item)
break
index_entry.bind('<Return>', select_by_index)
df_window.mainloop()
btn_show_df = ttk.Button(root, text="Afficher le tableau", command=show_dataframe)
btn_show_df.pack(side=tk.TOP, pady=5)
# Ajouter une ligne rouge et verte
def add_lines(ax, df_slice):
try:
unique_dates = df_slice['date'].unique()
for date in unique_dates:
daily_data = df_slice[df_slice['date'] == date]
if daily_data.empty:
continue
modedate = daily_data['Modedate'].iloc[0]
if modedate == 1:
start_time = pd.to_datetime("14:30").time()
end_time = pd.to_datetime("21:00").time()
else:
start_time = pd.to_datetime("15:30").time()
end_time = pd.to_datetime("21:55").time()
filtered_data = daily_data[
(daily_data['time'].dt.time >= start_time) &
(daily_data['time'].dt.time <= end_time)
]
if filtered_data.empty:
continue
max_high = filtered_data['high'].max()
min_low = filtered_data['low'].min()
time_range = df_slice['time']
xmin = (pd.Timestamp.combine(date, start_time) - time_range.min()).total_seconds() / (time_range.max() - time_range.min()).total_seconds()
xmax = (pd.Timestamp.combine(date, end_time) - time_range.min()).total_seconds() / (time_range.max() - time_range.min()).total_seconds()
ax.axhline(y=max_high, color='red', linestyle='--', xmin=xmin, xmax=xmax)
ax.axhline(y=min_low, color='green', linestyle='--', xmin=xmin, xmax=xmax)
except Exception as e:
pass
# fonction pour gérer le mouvement de la souris
def on_mouse_move(event):
if event.inaxes:
x, y = event.xdata, event.ydata
ax = event.inaxes
# Effacer les lignes précédentes
for line in ax.lines:
if line.get_label() in ['crosshair_h', 'crosshair_v']:
line.remove()
# Dessiner les nouvelles lignes
ax.axhline(y=y, color='black', linewidth=0.5, label='crosshair_h')
ax.axvline(x=x, color='black', linewidth=0.5, label='crosshair_v')
# Mettre à jour les données dans la fenêtre
if df_slice is not None and len(df_slice) > 0:
index = max(0, min(int(x), len(df_slice) - 1))
data = df_slice.iloc[index]
info_text = f"Index: {start_index + index}\nOpen: {data['open']:.2f}\nHigh: {data['high']:.2f}\nLow: {data['low']:.2f}\nClose: {data['close']:.2f}\nTime: {data['time']}"
info_window.set(info_text)
# Mise à jour des valeurs X et Y
x_value.set(f"X: {data['time']}")
y_value.set(f"Y: {y:.2f}")
# Redessiner le graphique
fig.canvas.draw_idle()
# Evènement molette de la sourie
def on_scroll(event):
global num_candles
if event.button == 'up':
num_candles = max(num_candles - int(zoom_step_entry.get()), 10)
elif event.button == 'down':
num_candles += int(zoom_step_entry.get())
update_chart()
# Fonctions pour le graphique
def update_chart():
global start_index, num_candles, df_slice, fig
plt.close('all') # Ferme toutes les figures existantes
df_slice = df.iloc[start_index:start_index + num_candles]
if df_slice.empty:
return
df_ohlc = df_slice[['time', 'open', 'high', 'low', 'close']].copy()
df_ohlc.set_index('time', inplace=True)
fig, axlist = mpf.plot(df_ohlc, type='candle', style='charles', title=csv_filename,
ylabel='Prix', volume=False, returnfig=True)
ax = axlist[0]
add_lines(ax, df_slice)
if hasattr(frame, "canvas"):
frame.canvas.get_tk_widget().destroy()
canvas = FigureCanvasTkAgg(fig, master=frame)
canvas.draw()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
frame.canvas = canvas
fig.canvas.mpl_connect('motion_notify_event', on_mouse_move)
fig.canvas.mpl_connect('scroll_event', on_scroll)
def increase_candles():
global num_candles
num_candles += int(zoom_step_entry.get())
update_chart()
def decrease_candles():
global num_candles
num_candles = max(num_candles - int(zoom_step_entry.get()), 10) # Minimum de 10 chandeliers
update_chart()
def move_right():
global start_index
start_index += int(graph_step_entry.get())
update_chart()
def move_left():
global start_index
start_index = max(start_index - int(graph_step_entry.get()), 0)
update_chart()
# Boutons
button_frame = ttk.Frame(root)
button_frame.pack(side=tk.BOTTOM, fill=tk.X)
plus_button = ttk.Button(button_frame, text="+", command=decrease_candles)
plus_button.pack(side=tk.LEFT, padx=5, pady=5)
minus_button = ttk.Button(button_frame, text="-", command=increase_candles)
minus_button.pack(side=tk.LEFT, padx=5, pady=5)
left_button = ttk.Button(button_frame, text="←", command=move_left)
left_button.pack(side=tk.LEFT, padx=5, pady=5)
right_button = ttk.Button(button_frame, text="→", command=move_right)
right_button.pack(side=tk.LEFT, padx=5, pady=5)
# Fenêtre d'information
info_frame = ttk.Frame(root)
info_frame.pack(side=tk.BOTTOM, fill=tk.X)
# Sous-frame pour les informations centrées
center_info_frame = ttk.Frame(info_frame)
center_info_frame.pack(side=tk.LEFT, expand=True)
info_window = tk.StringVar()
info_label = ttk.Label(center_info_frame, textvariable=info_window, justify=tk.LEFT)
info_label.pack(pady=10)
# Sous-frame pour les valeurs X et Y à droite
xy_info_frame = ttk.Frame(info_frame)
xy_info_frame.pack(side=tk.RIGHT)
x_value = tk.StringVar()
y_value = tk.StringVar()
x_label = ttk.Label(xy_info_frame, textvariable=x_value, justify=tk.LEFT)
y_label = ttk.Label(xy_info_frame, textvariable=y_value, justify=tk.LEFT)
x_label.pack(padx=10, anchor='w')
y_label.pack(padx=10, anchor='w')
update_chart()
root.mainloop()