This commit is contained in:
dsyoon
2025-08-23 17:48:05 +09:00
parent 1543304536
commit 62771d068c
29 changed files with 232 additions and 4 deletions

View File

@@ -4,6 +4,7 @@ import yfinance as yf
import matplotlib.pyplot as plt
import matplotlib.dates
import mplcursors
import matplotlib.lines as mlines # 추가: 범례 프록시용
plt.rcParams['font.family'] ='AppleGothic'
plt.rcParams['axes.unicode_minus'] =False
@@ -158,6 +159,7 @@ class Simulation:
# 캔들스틱 데이터 준비
ohlc_data = []
normal_patches = [] # 추가: 일반 캔들 아티스트 목록
for i, (idx, row) in enumerate(data.iterrows()):
ohlc_data.append([
mdates.date2num(idx),
@@ -180,10 +182,41 @@ class Simulation:
rect = plt.Rectangle((date - 0.3, body_bottom), 0.6, body_height,
facecolor=color, edgecolor='black', alpha=0.7)
ax1.add_patch(rect)
normal_patches.append(rect) # 추가: 리스트에 저장
# 캔들 심지 그리기
ax1.plot([date, date], [low, high], color='black', linewidth=1)
normal_patches.append(ax1.lines[-1]) # 추가: 선도 저장
# ----------------- 하이킨아시 캔들스틱 -----------------
# 하이킨아시 OHLC 계산
ha_df = pd.DataFrame(index=data.index, columns=['Open', 'High', 'Low', 'Close'])
ha_df['Close'] = (data['Open'] + data['High'] + data['Low'] + data['Close']) / 4
# 첫 번째 HA Open 값 계산
ha_df.loc[ha_df.index[0], 'Open'] = (data['Open'].iloc[0] + data['Close'].iloc[0]) / 2
# 이후 HA Open 값은 이전 HA 캔들의 (Open+Close)/2
for i in range(1, len(ha_df)):
prev_open = ha_df.loc[ha_df.index[i-1], 'Open']
prev_close = ha_df.loc[ha_df.index[i-1], 'Close']
ha_df.loc[ha_df.index[i], 'Open'] = (prev_open + prev_close) / 2
# High/Low 계산
ha_df['High'] = pd.concat([ha_df['Open'], ha_df['Close'], data['High']], axis=1).max(axis=1)
ha_df['Low'] = pd.concat([ha_df['Open'], ha_df['Close'], data['Low']], axis=1).min(axis=1)
ha_patches = []
for i, (idx, row) in enumerate(ha_df.iterrows()):
date = mdates.date2num(idx)
open_price, high, low, close = row['Open'], row['High'], row['Low'], row['Close']
color = 'red' if close >= open_price else 'blue'
body_height = abs(close - open_price)
body_bottom = min(open_price, close)
rect = plt.Rectangle((date - 0.3, body_bottom), 0.6, body_height,
facecolor=color, edgecolor='black', alpha=0.7, visible=False)
ax1.add_patch(rect)
ha_patches.append(rect)
line = ax1.plot([date, date], [low, high], color='black', linewidth=1, visible=False)[0]
ha_patches.append(line)
# 메인 차트 (가격, 이동평균선, 볼린저 밴드)
line_close = ax1.plot(data.index, data["Close"], label="종가", color="black", linewidth=1.5, alpha=0.8)[0]
line_ma5 = ax1.plot(data.index, data["MA5"], label="MA5", color="red", linewidth=1)[0]
@@ -287,8 +320,13 @@ class Simulation:
home_button = ax1.text(0.02, 0.98, '', transform=ax1.transAxes,
bbox=dict(boxstyle="round,pad=0.3", facecolor='lightblue', alpha=0.8),
fontsize=10, ha='left', va='top', picker=True)
# 하이킨아시 토글 버튼 추가
ha_button = ax1.text(0.06, 0.98, 'HA', transform=ax1.transAxes,
bbox=dict(boxstyle="round,pad=0.3", facecolor='orange', alpha=0.8),
fontsize=10, ha='left', va='top', picker=True)
# --- 범례 생성 및 인터랙티브 토글 ---
# 캔들 및 지표만 범례에 표시 (일반/하이킨아시 토글은 HA 버튼으로 제공)
legend = ax1.legend(loc='upper left', bbox_to_anchor=(0.02, 0.85), fontsize=10)
# 범례 클릭 시 해당 선 토글 기능
@@ -311,12 +349,17 @@ class Simulation:
if scatter_dev1440_buy_points is not None:
plot_lines.append(scatter_dev1440_buy_points)
for leg_handle, orig in zip(legend_handles, plot_lines):
for leg_handle, orig in zip(legend_handles[:len(plot_lines)], plot_lines):
leg_handle.set_picker(True)
lined[leg_handle] = orig
# 하이킨아시/일반 토글 상태 변수
ha_mode = False
def on_pick(event):
nonlocal ha_mode
leg_handle = event.artist
# 홈 버튼 동작
if leg_handle == home_button:
ax1.set_xlim(matplotlib.dates.date2num(data.index[0]), matplotlib.dates.date2num(data.index[-1]))
ax1.set_ylim(data['Low'].min() * 0.99, data['High'].max() * 1.01)
@@ -327,6 +370,22 @@ class Simulation:
)
fig.canvas.draw_idle()
return
# 하이킨아시 토글
if leg_handle == ha_button:
ha_mode = not ha_mode
if ha_mode:
for p in normal_patches:
p.set_visible(False)
for p in ha_patches:
p.set_visible(True)
else:
for p in normal_patches:
p.set_visible(True)
for p in ha_patches:
p.set_visible(False)
fig.canvas.draw_idle()
return
# 선 토글 처리
orig = lined.get(leg_handle)
if orig is None:
return
@@ -431,7 +490,7 @@ if __name__ == "__main__":
sim = Simulation()
interval = 60
days = 90
target_coins = ['POL']
target_coins = ['XRP']
show_graphs = True
for symbol in target_coins:
print(f"\n=== {symbol} 저점 기간 분석 시작 ===")