Bu Python kodu, hisse senedi yatırımcıları için tasarlanmış, EMA (Üssel Hareketli Ortalama) tabanlı bir sinyal analiz sistemidir. Kod, her hisse için farklı vadelere özel sinyal üretir ve bu sinyallerin nasıl oluşup sonlandığını titizlikle takip eder.
Kodun kısa, orta ve uzun vadeli sinyal mantığına dair detaylar şu şekilde
Python Kodu, Daha önce oluşturduğumuz python StokData/Kapanis klasöründe bulunan hisselerin kapanış datalarını kullanır. Seans kapanışından sonra kodu çalıştırdığınızda Ema sinyallerini oluşturur, bu sinyallerin durumlarını analiz eder güncel durumu raporlar. Bu rapor, hisselerin son sinyalini, sinyalin kaç gündür aktif olduğunu ve bu süreçteki kar/zarar durumunu net bir şekilde gösterir. Bu sayede yatırımcılar, hangi hisselerin hangi trendde olduğunu kolayca anlayabilir ve buna göre pozisyonlarını yönetebilir.
Örnek Excel Çalışması
BorsaPin_EMA_Analizleri
X_06_BorsaPin_EmaSinyals.py olarak adlandırdım. Ver.1.02 Güncellndi
Kısa orta ve uzun vade de al sat bekle gibi sinyalleri Excel'e dökümünü yapacaktır.
Python Betiğimize ait kod
import pandas as pd import os import warnings from datetime import datetime from colorama import Fore, init import glob from typing import List, Dict, Any, Optional from openpyxl import load_workbook from openpyxl.utils.dataframe import dataframe_to_rows from openpyxl.styles import PatternFill, Font, Alignment, Border, Side from openpyxl.formatting.rule import ColorScaleRule """ Borsapin StokData/Kapanis klasöründeki hisse datalarından Kısa, orta,uzun vade al sat tut gibi sinyalleri excele aktarır. Pine Script™'e EMA Sinyal Tablosu indikatörümüze göre güncellenmiş kurallar ile Sinyal bozulma uyarıları eklendi Ver 1.02 www.kursatsenturk.com """ # Uyarıları kapat warnings.filterwarnings('ignore') init(autoreset=True) class BorsaPinEMAAnalyzer: def __init__(self): self.ema_periods = { 'kisa_vade': [5, 8, 13, 21], 'orta_vade': [34, 55], 'uzun_vade': [89, 144] } self.successful_files: List[str] = [] self.failed_files: List[str] = [] @staticmethod def calculate_ema(data: pd.Series, period: int) -> pd.Series: """EMA hesaplama fonksiyonu""" return data.ewm(span=period, adjust=False).mean() @staticmethod def load_stock_data(file_path: str) -> pd.DataFrame: """Hisse verilerini Excel dosyasından yükleme""" try: df = pd.read_excel(file_path) required_columns = ['Tarih', 'Kapanış'] missing_columns = [col for col in required_columns if col not in df.columns] if missing_columns: raise ValueError(f"Excel dosyasında eksik sütunlar: {missing_columns}") df['Tarih'] = pd.to_datetime(df['Tarih']) df = df.sort_values('Tarih').reset_index(drop=True) return df except (FileNotFoundError, ValueError, pd.errors.ParserError) as e: print(f"{Fore.RED}❌ Veri yükleme hatası: {e}") return pd.DataFrame() def calculate_all_emas(self, df: pd.DataFrame) -> pd.DataFrame: """Tüm EMA'ları hesapla""" close_prices = df['Kapanış'] all_periods = self.ema_periods['kisa_vade'] + self.ema_periods['orta_vade'] + self.ema_periods['uzun_vade'] for period in all_periods: if len(df) >= period: df[f'EMA{period}'] = self.calculate_ema(close_prices, period) else: df[f'EMA{period}'] = pd.NA return df @staticmethod def check_signal_deterioration(df: pd.DataFrame, i: int) -> Dict[str, str]: """Sinyal bozulma durumlarını kontrol et""" warnings = { 'kisa_uyari': '', 'orta_uyari': '', 'uzun_uyari': '' } if i < 1: # En az 1 önceki veri gerekli return warnings kapanis = df.loc[i, 'Kapanış'] # KISA VADE UYARILARI if all(pd.notna(df.loc[i, col]) for col in ['EMA5', 'EMA8', 'EMA13', 'EMA21']) and \ all(pd.notna(df.loc[i - 1, col]) for col in ['EMA5', 'EMA8', 'EMA13', 'EMA21']): ema5, ema8, ema13, ema21 = df.loc[i, 'EMA5'], df.loc[i, 'EMA8'], df.loc[i, 'EMA13'], df.loc[i, 'EMA21'] ema5_once, ema8_once, ema13_once = df.loc[i - 1, 'EMA5'], df.loc[i - 1, 'EMA8'], df.loc[i - 1, 'EMA13'] # Önce durum kontrolü yap kisa_durum = df.loc[i, 'Kisa_Durum'] if 'Kisa_Durum' in df.columns else 'BEKLE' kisa_durum_once = df.loc[i - 1, 'Kisa_Durum'] if i > 0 and 'Kisa_Durum' in df.columns else 'BEKLE' # AL sinyali varken 21 EMA altına sarktığında if kisa_durum == 'AL' and kapanis < ema21: warnings['kisa_uyari'] = "⚠️ 21 EMA Altına Sarkma" # AL sinyali varken 5, 8, 13 EMA'da aşağı eğim başladığında (sinyal bozulmaya başladı) elif kisa_durum == 'AL' and (ema5 < ema5_once and ema8 < ema8_once and ema13 < ema13_once): warnings['kisa_uyari'] = "⚠️ EMA Eğim Bozulması" # ORTA VADE UYARILARI if all(pd.notna(df.loc[i, col]) for col in ['EMA34', 'EMA55']) and i > 0 and \ all(pd.notna(df.loc[i - 1, col]) for col in ['EMA34', 'EMA55']): ema34 = df.loc[i, 'EMA34'] ema55 = df.loc[i, 'EMA55'] ema34_once = df.loc[i - 1, 'EMA34'] ema55_once = df.loc[i - 1, 'EMA55'] orta_durum = df.loc[i, 'Orta_Durum'] if 'Orta_Durum' in df.columns else 'BEKLE' orta_durum_once = df.loc[i - 1, 'Orta_Durum'] if i > 0 and 'Orta_Durum' in df.columns else 'BEKLE' # AL sinyali varken 34 EMA altında kapatmalarda if orta_durum == 'AL' and kapanis < ema34: warnings['orta_uyari'] = "⚠️ 34 EMA Altında Kapanış" # AL sinyali varken 34 ve 55 EMA'da aşağı eğim (sinyal bozulmaya başladı) elif orta_durum == 'AL' and (ema34 < ema34_once and ema55 < ema55_once): warnings['orta_uyari'] = "⚠️ EMA Eğim Bozulması" # UZUN VADE UYARILARI if all(pd.notna(df.loc[i, col]) for col in ['EMA89', 'EMA144']) and i > 0 and \ all(pd.notna(df.loc[i - 1, col]) for col in ['EMA89', 'EMA144']): ema89 = df.loc[i, 'EMA89'] ema144 = df.loc[i, 'EMA144'] ema89_once = df.loc[i - 1, 'EMA89'] ema144_once = df.loc[i - 1, 'EMA144'] uzun_durum = df.loc[i, 'Uzun_Durum'] if 'Uzun_Durum' in df.columns else 'BEKLE' uzun_durum_once = df.loc[i - 1, 'Uzun_Durum'] if i > 0 and 'Uzun_Durum' in df.columns else 'BEKLE' # AL sinyali varken 89 EMA altına sarkmada if uzun_durum == 'AL' and kapanis < ema89: warnings['uzun_uyari'] = "⚠️ 89 EMA Altına Sarkma" # AL sinyali varken 89 ve 144 EMA'da aşağı eğim (sinyal bozulmaya başladı) elif uzun_durum == 'AL' and (ema89 < ema89_once and ema144 < ema144_once): warnings['uzun_uyari'] = "⚠️ EMA Eğim Bozulması" return warnings def determine_signal_status(self, df: pd.DataFrame) -> pd.DataFrame: """Sinyal durumlarını belirle - Pine Script™'e kurallar güncellendi 16.08.25""" df['Kisa_Durum'] = 'BEKLE' df['Orta_Durum'] = 'BEKLE' df['Uzun_Durum'] = 'BEKLE' df['Kisa_Bar_Sayaci'] = 0 df['Orta_Bar_Sayaci'] = 0 df['Uzun_Bar_Sayaci'] = 0 df['Kisa_Sinyal_Tarihi'] = pd.NaT df['Orta_Sinyal_Tarihi'] = pd.NaT df['Uzun_Sinyal_Tarihi'] = pd.NaT df['Kisa_Sinyal_Fiyati'] = pd.NA df['Orta_Sinyal_Fiyati'] = pd.NA df['Uzun_Sinyal_Fiyati'] = pd.NA # Yeni uyarı sütunları df['Kisa_Uyari'] = '' df['Orta_Uyari'] = '' df['Uzun_Uyari'] = '' if len(df) < 2: return df for i in range(1, len(df)): kisa_durum_once = df.loc[i - 1, 'Kisa_Durum'] orta_durum_once = df.loc[i - 1, 'Orta_Durum'] uzun_durum_once = df.loc[i - 1, 'Uzun_Durum'] kapanis = df.loc[i, 'Kapanış'] # KISA VADE SİNYALLERİ - Pine Script™ indikatörümüzle uyumlu if all(pd.notna(df.loc[i, col]) for col in ['EMA5', 'EMA8', 'EMA13', 'EMA21']) and \ all(pd.notna(df.loc[i - 1, col]) for col in ['EMA5', 'EMA8', 'EMA13', 'EMA21']): ema5, ema8, ema13, ema21 = df.loc[i, 'EMA5'], df.loc[i, 'EMA8'], df.loc[i, 'EMA13'], df.loc[i, 'EMA21'] kisa_al_kosulu = (kapanis > ema5 and kapanis > ema8 and kapanis > ema13 and kapanis > ema21) kisa_egim_yukari = (ema5 > df.loc[i - 1, 'EMA5'] and ema8 > df.loc[i - 1, 'EMA8'] and ema13 > df.loc[i - 1, 'EMA13'] and ema21 > df.loc[i - 1, 'EMA21']) if kisa_al_kosulu and kisa_egim_yukari: df.loc[i, 'Kisa_Durum'] = "AL" if kisa_durum_once != "AL": df.loc[i, 'Kisa_Bar_Sayaci'] = 1 df.loc[i, 'Kisa_Sinyal_Tarihi'] = df.loc[i, 'Tarih'] df.loc[i, 'Kisa_Sinyal_Fiyati'] = df.loc[i, 'Kapanış'] else: df.loc[i, 'Kisa_Bar_Sayaci'] = df.loc[i - 1, 'Kisa_Bar_Sayaci'] + 1 df.loc[i, 'Kisa_Sinyal_Tarihi'] = df.loc[i - 1, 'Kisa_Sinyal_Tarihi'] df.loc[i, 'Kisa_Sinyal_Fiyati'] = df.loc[i - 1, 'Kisa_Sinyal_Fiyati'] else: kisa_egim_asagi = (ema21 < df.loc[i - 1, 'EMA21'] and ema13 < df.loc[i - 1, 'EMA13']) if kapanis < ema21 and kisa_egim_asagi: df.loc[i, 'Kisa_Durum'] = "SAT" if kisa_durum_once != "SAT": df.loc[i, 'Kisa_Bar_Sayaci'] = 1 df.loc[i, 'Kisa_Sinyal_Tarihi'] = df.loc[i, 'Tarih'] df.loc[i, 'Kisa_Sinyal_Fiyati'] = df.loc[i, 'Kapanış'] else: df.loc[i, 'Kisa_Bar_Sayaci'] = df.loc[i - 1, 'Kisa_Bar_Sayaci'] + 1 df.loc[i, 'Kisa_Sinyal_Tarihi'] = df.loc[i - 1, 'Kisa_Sinyal_Tarihi'] df.loc[i, 'Kisa_Sinyal_Fiyati'] = df.loc[i - 1, 'Kisa_Sinyal_Fiyati'] else: # AL sinyalini koruma mantığı if kisa_durum_once == 'AL' and df.loc[i, 'Kisa_Durum'] == 'BEKLE': df.loc[i, 'Kisa_Durum'] = "AL" df.loc[i, 'Kisa_Bar_Sayaci'] = df.loc[i - 1, 'Kisa_Bar_Sayaci'] + 1 df.loc[i, 'Kisa_Sinyal_Tarihi'] = df.loc[i - 1, 'Kisa_Sinyal_Tarihi'] df.loc[i, 'Kisa_Sinyal_Fiyati'] = df.loc[i - 1, 'Kisa_Sinyal_Fiyati'] else: df.loc[i, 'Kisa_Durum'] = "BEKLE" df.loc[i, 'Kisa_Bar_Sayaci'] = 0 df.loc[i, 'Kisa_Sinyal_Tarihi'] = pd.NaT df.loc[i, 'Kisa_Sinyal_Fiyati'] = pd.NA # ORTA VADE SİNYALLERİ if all(col in df.columns for col in ['EMA34', 'EMA55']) and all( pd.notna(df.loc[i, col]) for col in ['EMA34', 'EMA55']): ema34 = df.loc[i, 'EMA34'] ema55 = df.loc[i, 'EMA55'] if kapanis > ema34 and kapanis > ema55 and ema34 > ema55: df.loc[i, 'Orta_Durum'] = "AL" if orta_durum_once != "AL": df.loc[i, 'Orta_Bar_Sayaci'] = 1 df.loc[i, 'Orta_Sinyal_Tarihi'] = df.loc[i, 'Tarih'] df.loc[i, 'Orta_Sinyal_Fiyati'] = df.loc[i, 'Kapanış'] else: df.loc[i, 'Orta_Bar_Sayaci'] = df.loc[i - 1, 'Orta_Bar_Sayaci'] + 1 df.loc[i, 'Orta_Sinyal_Tarihi'] = df.loc[i - 1, 'Orta_Sinyal_Tarihi'] df.loc[i, 'Orta_Sinyal_Fiyati'] = df.loc[i - 1, 'Orta_Sinyal_Fiyati'] elif kapanis < ema55: df.loc[i, 'Orta_Durum'] = "SAT" if orta_durum_once != "SAT": df.loc[i, 'Orta_Bar_Sayaci'] = 1 df.loc[i, 'Orta_Sinyal_Tarihi'] = df.loc[i, 'Tarih'] df.loc[i, 'Orta_Sinyal_Fiyati'] = df.loc[i, 'Kapanış'] else: df.loc[i, 'Orta_Bar_Sayaci'] = df.loc[i - 1, 'Orta_Bar_Sayaci'] + 1 df.loc[i, 'Orta_Sinyal_Tarihi'] = df.loc[i - 1, 'Orta_Sinyal_Tarihi'] df.loc[i, 'Orta_Sinyal_Fiyati'] = df.loc[i - 1, 'Orta_Sinyal_Fiyati'] else: df.loc[i, 'Orta_Durum'] = "BEKLE" df.loc[i, 'Orta_Bar_Sayaci'] = 0 df.loc[i, 'Orta_Sinyal_Tarihi'] = pd.NaT df.loc[i, 'Orta_Sinyal_Fiyati'] = pd.NA # UZUN VADE SİNYALLERİ if all(col in df.columns for col in ['EMA89', 'EMA144']) and all( pd.notna(df.loc[i, col]) for col in ['EMA89', 'EMA144']): ema89 = df.loc[i, 'EMA89'] ema144 = df.loc[i, 'EMA144'] if kapanis > ema89 and kapanis > ema144: df.loc[i, 'Uzun_Durum'] = "AL" if uzun_durum_once != "AL": df.loc[i, 'Uzun_Bar_Sayaci'] = 1 df.loc[i, 'Uzun_Sinyal_Tarihi'] = df.loc[i, 'Tarih'] df.loc[i, 'Uzun_Sinyal_Fiyati'] = df.loc[i, 'Kapanış'] else: df.loc[i, 'Uzun_Bar_Sayaci'] = df.loc[i - 1, 'Uzun_Bar_Sayaci'] + 1 df.loc[i, 'Uzun_Sinyal_Tarihi'] = df.loc[i - 1, 'Uzun_Sinyal_Tarihi'] df.loc[i, 'Uzun_Sinyal_Fiyati'] = df.loc[i - 1, 'Uzun_Sinyal_Fiyati'] elif kapanis < ema144: df.loc[i, 'Uzun_Durum'] = "SAT" if uzun_durum_once != "SAT": df.loc[i, 'Uzun_Bar_Sayaci'] = 1 df.loc[i, 'Uzun_Sinyal_Tarihi'] = df.loc[i, 'Tarih'] df.loc[i, 'Uzun_Sinyal_Fiyati'] = df.loc[i, 'Kapanış'] else: df.loc[i, 'Uzun_Bar_Sayaci'] = df.loc[i - 1, 'Uzun_Bar_Sayaci'] + 1 df.loc[i, 'Uzun_Sinyal_Tarihi'] = df.loc[i - 1, 'Uzun_Sinyal_Tarihi'] df.loc[i, 'Uzun_Sinyal_Fiyati'] = df.loc[i - 1, 'Uzun_Sinyal_Fiyati'] else: df.loc[i, 'Uzun_Durum'] = "BEKLE" df.loc[i, 'Uzun_Bar_Sayaci'] = 0 df.loc[i, 'Uzun_Sinyal_Tarihi'] = pd.NaT df.loc[i, 'Uzun_Sinyal_Fiyati'] = pd.NA # SİNYAL BOZULMA UYARILARINI KONTROL ET warnings_dict = self.check_signal_deterioration(df, i) df.loc[i, 'Kisa_Uyari'] = warnings_dict['kisa_uyari'] df.loc[i, 'Orta_Uyari'] = warnings_dict['orta_uyari'] df.loc[i, 'Uzun_Uyari'] = warnings_dict['uzun_uyari'] return df @staticmethod def get_latest_signals(df: pd.DataFrame) -> Optional[Dict[str, Any]]: """En son sinyal durumlarını getir""" if len(df) == 0: return None latest = df.iloc[-1] # Sinyal gücü ataması kisa_guc = '⭐' if latest['Kisa_Durum'] != 'BEKLE' else '⏳' orta_guc = '⭐' if latest['Orta_Durum'] != 'BEKLE' else '⏳' uzun_guc = '⭐' if latest['Uzun_Durum'] != 'BEKLE' else '⏳' # Fiyat farkı hesaplamaları kisa_fark_tl = latest['Kapanış'] - latest['Kisa_Sinyal_Fiyati'] if pd.notna( latest['Kisa_Sinyal_Fiyati']) else pd.NA kisa_fark_yuzde = (kisa_fark_tl / latest['Kisa_Sinyal_Fiyati']) * 100 if pd.notna(kisa_fark_tl) else pd.NA orta_fark_tl = latest['Kapanış'] - latest['Orta_Sinyal_Fiyati'] if pd.notna( latest['Orta_Sinyal_Fiyati']) else pd.NA orta_fark_yuzde = (orta_fark_tl / latest['Orta_Sinyal_Fiyati']) * 100 if pd.notna(orta_fark_tl) else pd.NA uzun_fark_tl = latest['Kapanış'] - latest['Uzun_Sinyal_Fiyati'] if pd.notna( latest['Uzun_Sinyal_Fiyati']) else pd.NA uzun_fark_yuzde = (uzun_fark_tl / latest['Uzun_Sinyal_Fiyati']) * 100 if pd.notna(uzun_fark_tl) else pd.NA # NaN değerler yerine 'Yok' metnini kullanma def format_value(value, format_str, default='Yok'): if pd.notna(value): return format_str.format(value) return default return { 'tarih': latest['Tarih'].strftime('%Y-%m-%d'), 'kapanis': latest['Kapanış'], 'kisa': { 'durum': latest['Kisa_Durum'], 'bar_sayaci': latest['Kisa_Bar_Sayaci'], 'sinyal_tarihi': format_value(latest['Kisa_Sinyal_Tarihi'], '{:%d.%m.%Y}'), 'sinyal_fiyati': format_value(latest['Kisa_Sinyal_Fiyati'], '{:.2f}'), 'fark_tl': format_value(kisa_fark_tl, '{:.2f}'), 'fark_yuzde': format_value(kisa_fark_yuzde, '{:.2f}%'), 'guc': kisa_guc, 'uyari': latest['Kisa_Uyari'] if 'Kisa_Uyari' in df.columns else '' }, 'orta': { 'durum': latest['Orta_Durum'], 'bar_sayaci': latest['Orta_Bar_Sayaci'], 'sinyal_tarihi': format_value(latest['Orta_Sinyal_Tarihi'], '{:%d.%m.%Y}'), 'sinyal_fiyati': format_value(latest['Orta_Sinyal_Fiyati'], '{:.2f}'), 'fark_tl': format_value(orta_fark_tl, '{:.2f}'), 'fark_yuzde': format_value(orta_fark_yuzde, '{:.2f}%'), 'guc': orta_guc, 'uyari': latest['Orta_Uyari'] if 'Orta_Uyari' in df.columns else '' }, 'uzun': { 'durum': latest['Uzun_Durum'], 'bar_sayaci': latest['Uzun_Bar_Sayaci'], 'sinyal_tarihi': format_value(latest['Uzun_Sinyal_Tarihi'], '{:%d.%m.%Y}'), 'sinyal_fiyati': format_value(latest['Uzun_Sinyal_Fiyati'], '{:.2f}'), 'fark_tl': format_value(uzun_fark_tl, '{:.2f}'), 'fark_yuzde': format_value(uzun_fark_yuzde, '{:.2f}%'), 'guc': uzun_guc, 'uyari': latest['Uzun_Uyari'] if 'Uzun_Uyari' in df.columns else '' } } def process_single_stock(self, file_path: str) -> Optional[Dict[str, Any]]: """Tek bir hisse için EMA analizi yap ve sonuçları topla""" ticker_name = os.path.splitext(os.path.basename(file_path))[0] print(f"{Fore.YELLOW} EMA sinyalleri hesaplanıyor: {ticker_name}...") try: df = self.load_stock_data(file_path) # En az kısa vade EMA için yeterli veri yoksa hata ver if df.empty or len(df) < min(self.ema_periods['kisa_vade']): raise ValueError("Yeterli veri yok veya dosya geçersiz.") # Tüm EMA'ları ve sinyalleri hesapla df = self.calculate_all_emas(df) df = self.determine_signal_status(df) latest_signals = self.get_latest_signals(df) if latest_signals: self.successful_files.append(ticker_name) latest_signals['hisse'] = ticker_name print(f"{Fore.GREEN}✅ {ticker_name} için analiz başarılı.") else: self.failed_files.append(ticker_name) print(f"{Fore.RED}❌ {ticker_name} için sinyal oluşturulamadı.") return latest_signals except (ValueError, pd.errors.ParserError) as e: print(f"{Fore.RED}❌ {ticker_name} için hata: {e}") self.failed_files.append(ticker_name) return None def main(self): """Ana fonksiyon""" print(f"{Fore.CYAN} BorsaPin EMA Analiz Sistemi Başlatılıyor...") input_folder = "StokData/Kapanis/" input_files = glob.glob(os.path.join(input_folder, "*.xlsx")) if not input_files: print(f"{Fore.RED}❌ {input_folder} klasöründe Excel dosyası bulunamadı!") return all_latest_results: List[Dict[str, Any]] = [] for i, file_path in enumerate(input_files, 1): print(f"\n{Fore.MAGENTA}[{i}/{len(input_files)}] İşleniyor...") latest_signals = self.process_single_stock(file_path) if latest_signals: all_latest_results.append(latest_signals) if all_latest_results: self.save_all_signals_to_excel(all_latest_results) self.print_summary() self.save_failed_list() print(f"\n{Fore.GREEN} EMA analiz işlemi tamamlandı!") def save_all_signals_to_excel(self, results: List[Dict[str, Any]]): """Tüm sinyalleri tek bir Excel dosyasına farklı sayfalarda kaydet""" output_folder = "StokData/" os.makedirs(output_folder, exist_ok=True) filename = os.path.join(output_folder, "BorsaPin_EMA_Analizleri.xlsx") summary_df = self.create_summary_table(results) strong_signals_df = self.filter_strong_signals_df(summary_df) warning_signals_df = self.filter_warning_signals_df(summary_df) # Detaylı sinyal tabloları oluştur kisa_vade_df = self.create_detailed_signal_table(results, 'kisa') orta_vade_df = self.create_detailed_signal_table(results, 'orta') uzun_vade_df = self.create_detailed_signal_table(results, 'uzun') # İstatistik tabloları oluştur statistics_df = self.create_statistics_table(results) with pd.ExcelWriter(filename, engine='openpyxl') as writer: # Ana özet sayfası if not summary_df.empty: summary_df.to_excel(writer, sheet_name='Genel Özet', index=False) self.format_worksheet(writer.sheets['Genel Özet'], summary_df) # Güçlü sinyaller sayfası if not strong_signals_df.empty: strong_signals_df.to_excel(writer, sheet_name='Güçlü Sinyaller', index=False) self.format_worksheet(writer.sheets['Güçlü Sinyaller'], strong_signals_df) # Uyarı sinyalleri sayfası if not warning_signals_df.empty: warning_signals_df.to_excel(writer, sheet_name='Uyarı Sinyalleri', index=False) self.format_worksheet(writer.sheets['Uyarı Sinyalleri'], warning_signals_df) # Detaylı vade sayfaları if not kisa_vade_df.empty: kisa_vade_df.to_excel(writer, sheet_name='Kısa Vade Detay', index=False) self.format_worksheet(writer.sheets['Kısa Vade Detay'], kisa_vade_df) if not orta_vade_df.empty: orta_vade_df.to_excel(writer, sheet_name='Orta Vade Detay', index=False) self.format_worksheet(writer.sheets['Orta Vade Detay'], orta_vade_df) if not uzun_vade_df.empty: uzun_vade_df.to_excel(writer, sheet_name='Uzun Vade Detay', index=False) self.format_worksheet(writer.sheets['Uzun Vade Detay'], uzun_vade_df) # İstatistik sayfası if not statistics_df.empty: statistics_df.to_excel(writer, sheet_name='İstatistikler', index=False) self.format_worksheet(writer.sheets['İstatistikler'], statistics_df) print(f"{Fore.GREEN}✅ Tüm analiz sonuçları başarıyla kaydedildi: {filename}") @staticmethod def create_summary_table(results: List[Dict[str, Any]]) -> pd.DataFrame: """Özet Tablolar """ summary_data = [ { 'Hisse': res['hisse'], 'Kapanış': round(res['kapanis'], 2), 'Kısa Vade': res['kisa']['durum'], 'Kısa Uyarı': res['kisa']['uyari'], 'Sinyal Süresi': res['kisa']['bar_sayaci'], 'Kısa Vade Sinyal Tarihi': res['kisa']['sinyal_tarihi'], 'Kısa Vade Sinyal Fiyatı': res['kisa']['sinyal_fiyati'], 'Kısa Vade Kazanç TL': res['kisa']['fark_tl'], 'Kısa Vade Kazanç %': res['kisa']['fark_yuzde'], 'Orta Vade': res['orta']['durum'], 'Orta Uyarı': res['orta']['uyari'], 'Orta Sinyal Süresi': res['orta']['bar_sayaci'], 'Orta Vade Sinyal Tarihi': res['orta']['sinyal_tarihi'], 'Orta Vade Sinyal Fiyatı': res['orta']['sinyal_fiyati'], 'Orta Vade Kazanç TL': res['orta']['fark_tl'], 'Orta Vade Kazanç %': res['orta']['fark_yuzde'], 'Uzun Vade': res['uzun']['durum'], 'Uzun Uyarı': res['uzun']['uyari'], 'Uzun Sinyal Süresi': res['uzun']['bar_sayaci'], 'Uzun Vade Sinyal Tarihi': res['uzun']['sinyal_tarihi'], 'Uzun Vade Sinyal Fiyatı': res['uzun']['sinyal_fiyati'], 'Uzun Vade Kazanç TL': res['uzun']['fark_tl'], 'Uzun Vade Kazanç %': res['uzun']['fark_yuzde'] } for res in results ] return pd.DataFrame(summary_data) @staticmethod def create_detailed_signal_table(results: List[Dict[str, Any]], vade_type: str) -> pd.DataFrame: """Belirli vade için detaylı sinyal tablosu oluştur""" detailed_data = [] for res in results: vade_data = res[vade_type] if vade_data['durum'] != 'BEKLE': detailed_data.append({ 'Hisse': res['hisse'], 'Kapanış': round(res['kapanis'], 2), 'Sinyal Durumu': vade_data['durum'], 'Uyarı Durumu': vade_data['uyari'], 'Sinyal Süresi (Gün)': vade_data['bar_sayaci'], 'Sinyal Tarihi': vade_data['sinyal_tarihi'], 'Sinyal Fiyatı': vade_data['sinyal_fiyati'], 'Kazanç/Kayıp TL': vade_data['fark_tl'], 'Kazanç/Kayıp %': vade_data['fark_yuzde'] }) df = pd.DataFrame(detailed_data) if not df.empty: # Sinyal süresine göre sırala (en uzun süreli sinyaller önce) df = df.sort_values('Sinyal Süresi (Gün)', ascending=False).reset_index(drop=True) return df @staticmethod def filter_warning_signals_df(summary_df: pd.DataFrame) -> pd.DataFrame: """Uyarı sinyali olan hisseleri filtreleme""" if summary_df.empty: return pd.DataFrame() warning_signals_df = summary_df[ (summary_df['Kısa Uyarı'] != '') | (summary_df['Orta Uyarı'] != '') | (summary_df['Uzun Uyarı'] != '') ].reset_index(drop=True) return warning_signals_df @staticmethod def create_statistics_table(results: List[Dict[str, Any]]) -> pd.DataFrame: """İstatistik tablosu""" stats_data = [] # Genel istatistikler total_stocks = len(results) # Her vade için istatistikler for vade_key, vade_name in [('kisa', 'Kısa Vade'), ('orta', 'Orta Vade'), ('uzun', 'Uzun Vade')]: al_sinyali = sum(1 for res in results if res[vade_key]['durum'] == 'AL') sat_sinyali = sum(1 for res in results if res[vade_key]['durum'] == 'SAT') bekle_sinyali = sum(1 for res in results if res[vade_key]['durum'] == 'BEKLE') uyari_sinyali = sum(1 for res in results if res[vade_key]['uyari'] != '') # Ortalama sinyal süresi (sadece aktif sinyaller için) aktif_sinyaller = [res[vade_key]['bar_sayaci'] for res in results if res[vade_key]['durum'] != 'BEKLE' and res[vade_key]['bar_sayaci'] > 0] ortalama_sure = round(sum(aktif_sinyaller) / len(aktif_sinyaller), 1) if aktif_sinyaller else 0 # En uzun sinyal süresi max_sure = max(aktif_sinyaller) if aktif_sinyaller else 0 # Pozitif/negatif performans (sadece AL sinyalleri için) al_sinyalleri = [res for res in results if res[vade_key]['durum'] == 'AL'] pozitif_performans = 0 negatif_performans = 0 for res in al_sinyalleri: try: fark_str = res[vade_key]['fark_yuzde'] if fark_str != 'Yok' and '%' in fark_str: fark_value = float(fark_str.replace('%', '')) if fark_value > 0: pozitif_performans += 1 else: negatif_performans += 1 except ValueError: pass stats_data.extend([ { 'Vade Türü': vade_name, 'Metrik': 'Toplam Hisse Sayısı', 'Değer': total_stocks, 'Oran %': '100.0' }, { 'Vade Türü': vade_name, 'Metrik': 'AL Sinyali', 'Değer': al_sinyali, 'Oran %': f"{(al_sinyali / total_stocks * 100):.1f}" if total_stocks > 0 else "0.0" }, { 'Vade Türü': vade_name, 'Metrik': 'SAT Sinyali', 'Değer': sat_sinyali, 'Oran %': f"{(sat_sinyali / total_stocks * 100):.1f}" if total_stocks > 0 else "0.0" }, { 'Vade Türü': vade_name, 'Metrik': 'BEKLE Durumu', 'Değer': bekle_sinyali, 'Oran %': f"{(bekle_sinyali / total_stocks * 100):.1f}" if total_stocks > 0 else "0.0" }, { 'Vade Türü': vade_name, 'Metrik': 'Uyarı Sinyali', 'Değer': uyari_sinyali, 'Oran %': f"{(uyari_sinyali / total_stocks * 100):.1f}" if total_stocks > 0 else "0.0" }, { 'Vade Türü': vade_name, 'Metrik': 'Ortalama Sinyal Süresi (Gün)', 'Değer': ortalama_sure, 'Oran %': '-' }, { 'Vade Türü': vade_name, 'Metrik': 'En Uzun Sinyal Süresi (Gün)', 'Değer': max_sure, 'Oran %': '-' }, { 'Vade Türü': vade_name, 'Metrik': 'Pozitif Performans (AL)', 'Değer': pozitif_performans, 'Oran %': f"{(pozitif_performans / al_sinyali * 100):.1f}" if al_sinyali > 0 else "0.0" }, { 'Vade Türü': vade_name, 'Metrik': 'Negatif Performans (AL)', 'Değer': negatif_performans, 'Oran %': f"{(negatif_performans / al_sinyali * 100):.1f}" if al_sinyali > 0 else "0.0" } ]) # Güçlü sinyaller (tüm vadeler AL) guclu_sinyaller = sum(1 for res in results if res['kisa']['durum'] == 'AL' and res['orta']['durum'] == 'AL' and res['uzun']['durum'] == 'AL') # Toplam uyarı sinyalleri toplam_uyari = sum(1 for res in results if res['kisa']['uyari'] != '' or res['orta']['uyari'] != '' or res['uzun']['uyari'] != '') stats_data.extend([ { 'Vade Türü': 'Genel', 'Metrik': 'Güçlü Sinyal (Tüm Vadeler AL)', 'Değer': guclu_sinyaller, 'Oran %': f"{(guclu_sinyaller / total_stocks * 100):.1f}" if total_stocks > 0 else "0.0" }, { 'Vade Türü': 'Genel', 'Metrik': 'Toplam Uyarı Sinyali', 'Değer': toplam_uyari, 'Oran %': f"{(toplam_uyari / total_stocks * 100):.1f}" if total_stocks > 0 else "0.0" } ]) return pd.DataFrame(stats_data) @staticmethod def format_worksheet(worksheet, df): """Excel çalışma sayfası formatları """ try: # Başlık satırı formatı header_fill = PatternFill(start_color='366092', end_color='366092', fill_type='solid') header_font = Font(color='FFFFFF', bold=True) header_alignment = Alignment(horizontal='center', vertical='center') # Kenarlık stili thin_border = Border( left=Side(style='thin'), right=Side(style='thin'), top=Side(style='thin'), bottom=Side(style='thin') ) # Başlık satırını formatla for col_num, column_title in enumerate(df.columns, 1): cell = worksheet.cell(row=1, column=col_num) cell.fill = header_fill cell.font = header_font cell.alignment = header_alignment cell.border = thin_border # Veri satırlarını formatla for row_num in range(2, len(df) + 2): for col_num in range(1, len(df.columns) + 1): cell = worksheet.cell(row=row_num, column=col_num) cell.border = thin_border cell.alignment = Alignment(horizontal='center', vertical='center') # Sütun genişliklerini ayarla for column in worksheet.columns: max_length = 0 column_letter = column[0].column_letter for cell in column: try: if len(str(cell.value)) > max_length: max_length = len(str(cell.value)) except (TypeError, ValueError): pass adjusted_width = min(max_length + 2, 20) worksheet.column_dimensions[column_letter].width = adjusted_width # Freeze panes ve autofilter worksheet.freeze_panes = 'A2' worksheet.auto_filter.ref = worksheet.dimensions # Sinyal durumu sütunları için renk kodlaması for row_num in range(2, len(df) + 2): for col_num, column_name in enumerate(df.columns, 1): cell = worksheet.cell(row=row_num, column=col_num) # Ana özet sayfaları için vade sütunları if 'Vade' in column_name and column_name.endswith('Vade'): if cell.value == 'AL': cell.fill = PatternFill(start_color='90EE90', end_color='90EE90', fill_type='solid') cell.font = Font(bold=True, color='006400') elif cell.value == 'SAT': cell.fill = PatternFill(start_color='FFB6C1', end_color='FFB6C1', fill_type='solid') cell.font = Font(bold=True, color='8B0000') elif cell.value == 'BEKLE': cell.fill = PatternFill(start_color='FFFFE0', end_color='FFFFE0', fill_type='solid') cell.font = Font(bold=True, color='B8860B') # Detay sayfaları için "Sinyal Durumu" sütunu elif column_name == 'Sinyal Durumu': if cell.value == 'AL': cell.fill = PatternFill(start_color='90EE90', end_color='90EE90', fill_type='solid') cell.font = Font(bold=True, color='006400') elif cell.value == 'SAT': cell.fill = PatternFill(start_color='FFB6C1', end_color='FFB6C1', fill_type='solid') cell.font = Font(bold=True, color='8B0000') # Uyarı sütunları için renk kodlaması elif 'Uyarı' in column_name: if cell.value and cell.value != '': cell.fill = PatternFill(start_color='FFA500', end_color='FFA500', fill_type='solid') cell.font = Font(bold=True, color='FFFFFF') # Uyarı durumu sütunu için renk kodlaması elif column_name == 'Uyarı Durumu': if cell.value and cell.value != '': cell.fill = PatternFill(start_color='FFA500', end_color='FFA500', fill_type='solid') cell.font = Font(bold=True, color='FFFFFF') # Kazanç/Kayıp yüzde değerleri için renk kodlaması elif 'Kazanç' in column_name and '%' in column_name: try: if cell.value and cell.value != 'Yok' and '%' in str(cell.value): percentage_value = float(str(cell.value).replace('%', '')) if percentage_value > 0: cell.fill = PatternFill(start_color='E6FFE6', end_color='E6FFE6', fill_type='solid') cell.font = Font(color='006400', bold=True) elif percentage_value < 0: cell.fill = PatternFill(start_color='FFE6E6', end_color='FFE6E6', fill_type='solid') cell.font = Font(color='8B0000', bold=True) except (ValueError, TypeError): pass # Kazanç/Kayıp TL değerleri için renk kodlaması elif 'Kazanç' in column_name and 'TL' in column_name: try: if cell.value and cell.value != 'Yok': tl_value = float(str(cell.value)) if tl_value > 0: cell.fill = PatternFill(start_color='E6FFE6', end_color='E6FFE6', fill_type='solid') cell.font = Font(color='006400', bold=True) elif tl_value < 0: cell.fill = PatternFill(start_color='FFE6E6', end_color='FFE6E6', fill_type='solid') cell.font = Font(color='8B0000', bold=True) except (ValueError, TypeError): pass # Sinyal süresi için renk gradyanı (uzun süreli sinyaller daha koyu) elif 'Sinyal Süresi' in column_name or 'Sinyal Süresi (Gün)' in column_name: try: if cell.value and isinstance(cell.value, (int, float)) and cell.value > 0: days = int(cell.value) if days >= 30: cell.fill = PatternFill(start_color='4CAF50', end_color='4CAF50', fill_type='solid') cell.font = Font(color='FFFFFF', bold=True) elif days >= 14: cell.fill = PatternFill(start_color='8BC34A', end_color='8BC34A', fill_type='solid') cell.font = Font(color='FFFFFF', bold=True) elif days >= 7: cell.fill = PatternFill(start_color='CDDC39', end_color='CDDC39', fill_type='solid') cell.font = Font(color='333333', bold=True) elif days >= 3: cell.fill = PatternFill(start_color='FFF176', end_color='FFF176', fill_type='solid') cell.font = Font(color='333333', bold=True) except (ValueError, TypeError): pass except Exception as e: print(f"{Fore.RED}❌ Excel formatlama hatası: {e}") @staticmethod def filter_strong_signals_df(summary_df: pd.DataFrame) -> pd.DataFrame: """Tüm vadelerde 'AL' sinyali olan hisseleri filtrele""" if summary_df.empty: return pd.DataFrame() strong_signals_df = summary_df[ (summary_df['Kısa Vade'] == 'AL') & (summary_df['Orta Vade'] == 'AL') & (summary_df['Uzun Vade'] == 'AL') ].reset_index(drop=True) return strong_signals_df def print_summary(self): """İşlem özetini ekrana yazdır""" total_files = len(self.successful_files) + len(self.failed_files) print("\n" + "=" * 40) print(f"{Fore.CYAN}BorsaPin EMA Analizi Özeti") print("=" * 40) print(f"{Fore.GREEN}✅ Başarılı Analiz Edilen Hisse Sayısı: {len(self.successful_files)} / {total_files}") if self.successful_files: print(f"{Fore.GREEN} - Hisse Listesi: {', '.join(self.successful_files)}") print(f"{Fore.RED}❌ Hata Alan Hisse Sayısı: {len(self.failed_files)} / {total_files}") if self.failed_files: print(f"{Fore.RED} - Hisse Listesi: {', '.join(self.failed_files)}") print("=" * 40) def save_failed_list(self): """Hata veren hisseleri bir dosyaya kaydet""" if self.failed_files: with open("StokData/hata_veren_hisseler.txt", "w") as f: f.write("\n".join(self.failed_files)) print(f"{Fore.RED}❌ Hata veren hisse listesi 'StokData/hata_veren_hisseler.txt' dosyasına kaydedildi.") if __name__ == "__main__": analyzer = BorsaPinEMAAnalyzer() analyzer.main()
Paylaştığım kodlarda sıkıntı yaşarsanız Google Drive Alanından indirmeniz daha iyi olur. Sitedeki kod gösterici eklentide ufak bir problem vardı. Bu sorunu başka bir plugingle çözdüm ve yazıların bir çoğunu güncelledim ama gözden kaçan düzenlenmemiş Python betiği yada Trading View Pine Script kodlarında sıkıntı olabilir.
Eğer sorun yaşarsanız yazının altına yorum yazarsanız tekrar gözden geçiririm.
Borsapin Python Bist Tarama ve Trading View Pine Script İndikatörleri için Google Drive alanı için buraya tıklayabilirsiniz.
Bu Python betiği, X_01_BorsaPin_StokData.py Python betiğiyle oluşturulan StokData/Kapanis klasöründeki hisse kapanış verilerinden lineer regresyon trend analizi ve Pearson korelasyon ölçümleri yaparak detaylı teknik analiz raporu üretir. Analizler, Excel formatında renklendirilmiş ve filtrelenebilir şekilde kayıt altına alınır.
Terminalden şu iki komutu çalıştırın .
python.exe -m pip install --upgrade pip
pip install pandas numpy scipy colorama openpyxl
Lineer Regresyon Analizi: Kapanış fiyatları üzerinden trend çizgisi (slope), kanal bandı, standart sapma gibi metrikleri hesaplar.
Pearson Korelasyon: Trendin gücünü ölçmek için farklı periyotlarda Pearson korelasyon katsayısı hesaplanır.
Kanal Altı Fırsatları: Alt banda yakın fiyat hareketleri ve güçlü korelasyon içeren hisseler.
Breakout Adayları: Üst banda yakın olup yükseliş sinyali veren hisseler.
EMA Dizilim Entegrasyonu: Farklı script tarafından oluşturulan EMA analizleriyle birleştirilebilir.
Otomatik Excel Raporu: Tüm veriler biçimlendirilmiş bir Excel dosyasına kaydedilir (filtrelenebilir, renklendirilmiş).
Başarısız Dosya Takibi: Hatalı veriler ayrı bir .txt dosyasına kaydedilir. Analiz Süreci Otomatik Gerçekleşir:
Tüm dosyalar sırasıyla işlenir. Lineer regresyon analizleri yapılır. Fırsatlar belirlenir. Sonuçlar Excel ve TXT dosyalarına kaydedilir.
StokData/lineer_regresyon_analiz.xlsx → Ana analiz dosyası
StokData/LinearRegression/lineer_regresyon_analiz_YYYY-MM-DD.xlsx → Arşiv dosyası
basarisiz_regresyon_dosyalari.txt → Başarısız analizlerin listesi
Excel Sayfaları
TumSonuclar: Tüm hisse ve periyot sonuçları
IdealPearson: En güçlü Pearson korelasyonuna sahip hisseler
KanalFirsatlari_Top50: Kanal altına yakın fırsatlar
BreakoutAdaylari_Top50: Yükselişe aday hisseler
Periyot_XXX: Her analiz periyodu için ayrı sayfalar
Istatistikler: Genel başarı ve analiz özetleri
Otomatik, hızlı ve güvenilir teknik analiz. Yatırım kararlarını destekleyecek fırsat ve sinyal tespiti. Excel üzerinden kolay görsel analiz ve filtreleme imkânı
Bu betik, teknik analizde trend doğruluğunu ve fırsatları istatistiksel olarak belirlemek isteyen yatırımcılar için güçlü bir araçtır. Özellikle BIST verileriyle çalışan sistemlerde regresyon ve Pearson tabanlı analiz ile öne çıkan hisseleri kolayca tespit etmeye yardımcı olur.
LinReg tarama çalışmamızı X_05_BorsaPin_LinReg_Tarama.py adıyla kayıt edebilirsiniz.
Python Betiği
import pandas as pd import numpy as np import os import glob from scipy.stats import linregress, pearsonr from datetime import datetime, timedelta from colorama import Fore, init from openpyxl.styles import Font, PatternFill, Alignment from openpyxl.utils import get_column_letter from typing import List, Dict, Optional """ Borsapin StokData/Kapanis klasöründeki hisse verilerinden Lineer Regresyon ve Pearson analizi yapar www.kursatsenturk.com """ init(autoreset=True) class LinearRegressionAnalyzer: def __init__(self): self.pearson_periods = [55, 89, 144, 233, 370, 610, 987] self.analysis_periods = [55, 89, 144, 233, 370, 610, 987] # Özel analiz periyotları self.successful_files = [] self.failed_files = [] # Sonuç listeleri self.all_results = [] self.period_results = {period: [] for period in self.pearson_periods} self.ideal_pearson = [] self.channel_opportunities = [] self.breakout_candidates = [] self.statistics = {} @staticmethod def calculate_regression(data_df: pd.DataFrame, period: int) -> Optional[Dict]: """Lineer regresyon ve kanal hesaplama""" try: # Son 'period' a kadar veriyi al data_df = data_df.tail(period).copy() if len(data_df) < period: return None # X ekseni (zaman) ve Y ekseni (fiyat) değerleri x = np.arange(len(data_df)) y = data_df["Kapanış"].values # Lineer regresyon hesaplama slope, intercept, r_value, p_value, std_err = linregress(x, y) # Trend çizgisi hesaplama trend_line = intercept + slope * x # Residual (artık) değerler ve standart sapma residuals = y - trend_line std_dev = np.std(residuals) # Kanal bantları (2 standart sapma) upper_channel = trend_line + 2 * std_dev lower_channel = trend_line - 2 * std_dev # Pearson korelasyon katsayısı corr, _ = pearsonr(x, y) # Son değerler upper_channel_value = round(float(upper_channel[-1]), 2) lower_channel_value = round(float(lower_channel[-1]), 2) trend_value = round(float(trend_line[-1]), 2) last_price = float(y[-1]) # Yüzde farkları upper_diff_pct = round((last_price - upper_channel_value) / last_price * 100, 2) lower_diff_pct = round((last_price - lower_channel_value) / last_price * 100, 2) trend_diff_pct = round((last_price - trend_value) / last_price * 100, 2) # Trend yönü ve güç trend_direction = "Yükseliş" if slope > 0 else "Düşüş" trend_strength = "Güçlü" if abs(slope) > 0.05 else "Orta" if abs(slope) > 0.01 else "Zayıf" # Kanal pozisyonu channel_position = "Üst Bant" if upper_diff_pct >= -2 else "Alt Bant" if lower_diff_pct <= 2 else "Orta" return { "Periyot": period, "Kanal_Ust": upper_channel_value, "Kanal_Alt": lower_channel_value, "Trend_Cizgisi": trend_value, "Ust_Fark_Pct": upper_diff_pct, "Alt_Fark_Pct": lower_diff_pct, "Trend_Fark_Pct": trend_diff_pct, "Trend_Yonu": trend_direction, "Trend_Gucu": trend_strength, "Kanal_Pozisyonu": channel_position, "Pearson": round(corr, 4), "R_Kare": round(r_value ** 2, 4), "Slope": round(slope, 6), "P_Value": round(p_value, 6), "Std_Error": round(std_err, 6) } except Exception as e: print(f"{Fore.RED}❌ Regresyon hesaplama hatası (Period {period}): {e}") return None def process_single_file(self, file_path: str) -> bool: """Tek dosya için lineer regresyon analizi""" ticker_name = None try: # Dosya adından hisse adını alma file_name = os.path.basename(file_path) ticker_name = os.path.splitext(file_name)[0] print(f"{Fore.YELLOW} Lineer regresyon analizi: {ticker_name}...") # Excel dosyasını okuma df = pd.read_excel(file_path) # Gerekli sütunların varlığını kontrol etme if 'Tarih' not in df.columns or 'Kapanış' not in df.columns: raise ValueError("Tarih veya Kapanış sütunu bulunamadı") # Veri temizleme ve sıralama df = df[["Tarih", "Kapanış"]].dropna() df["Tarih"] = pd.to_datetime(df["Tarih"]) df = df.sort_values("Tarih").reset_index(drop=True) df["Kapanış"] = pd.to_numeric(df["Kapanış"], errors='coerce') df = df.dropna() if df.empty: raise ValueError("Veri boş veya geçersiz") last_close = round(float(df["Kapanış"].iloc[-1]), 2) last_date = df["Tarih"].iloc[-1] print(f"{Fore.CYAN} ⚡ Periyotlar hesaplanıyor: ", end="") # Her periyot için analiz ticker_pearson_data = {"Hisse_Adi": ticker_name, "Kapanış": last_close, "Tarih": last_date} for i, period in enumerate(self.pearson_periods): print(f"{period}", end="") result = self.calculate_regression(df.copy(), period) if result: # Ana sonuç listesine ekle main_result = { "Hisse_Adi": ticker_name, "Kapanış": last_close, "Tarih": last_date, **result } self.all_results.append(main_result) # Periyot bazlı sonuçlara ekle self.period_results[period].append(main_result) # İdeal Pearson için veri toplama ticker_pearson_data[f"Pearson_{period}"] = result["Pearson"] ticker_pearson_data[f"Kanal_Pozisyon_{period}"] = result["Kanal_Pozisyonu"] ticker_pearson_data[f"Alt_Fark_{period}"] = result["Alt_Fark_Pct"] ticker_pearson_data[f"Ust_Fark_{period}"] = result["Ust_Fark_Pct"] if i < len(self.pearson_periods) - 1: print(", ", end="") print() # İdeal Pearson listesine ekle self.ideal_pearson.append(ticker_pearson_data) # Özel analizler için kontrol self.analyze_opportunities(ticker_name, last_close, last_date, df) print(f"{Fore.GREEN}✅ {ticker_name} analizi tamamlandı.") self.successful_files.append(ticker_name) return True except Exception as e: print(f"{Fore.RED}❌ {ticker_name if ticker_name else file_path} için hata: {e}") self.failed_files.append(os.path.basename(file_path)) return False def analyze_opportunities(self, ticker_name: str, last_close: float, last_date, df: pd.DataFrame): """Fırsat analizi - kanal alt bandı ve breakout adayları""" try: for period in self.analysis_periods: result = self.calculate_regression(df.copy(), period) if result: # Alt banda yakın fırsatlar (Alt fark <= 4% ve Pearson >= 0.7) if (-2 <= result["Alt_Fark_Pct"] <= 4 and abs(result["Pearson"]) >= 0.7): self.channel_opportunities.append({ "Hisse_Adi": ticker_name, "Kapanış": last_close, "Tarih": last_date, "Periyot": period, "Alt_Fark_Pct": result["Alt_Fark_Pct"], "Pearson": result["Pearson"], "Trend_Yonu": result["Trend_Yonu"], "Kanal_Alt": result["Kanal_Alt"], "Kanal_Ust": result["Kanal_Ust"], "Potansiyel_Kazanc": round((result["Kanal_Ust"] - last_close) / last_close * 100, 2) }) # Breakout adayları (Üst fark >= -4% ve Pearson >= 0.8) if (-4 <= result["Ust_Fark_Pct"] <= 2 and abs(result["Pearson"]) >= 0.8): self.breakout_candidates.append({ "Hisse_Adi": ticker_name, "Kapanış": last_close, "Tarih": last_date, "Periyot": period, "Ust_Fark_Pct": result["Ust_Fark_Pct"], "Pearson": result["Pearson"], "Trend_Yonu": result["Trend_Yonu"], "Kanal_Ust": result["Kanal_Ust"], "Hedef_Fiyat": round(result["Kanal_Ust"] * 1.05, 2) # %5 üst hedef }) except Exception as e: print(f"{Fore.RED}❌ Fırsat analizi hatası ({ticker_name}): {e}") def calculate_statistics(self): """İstatistik hesaplama""" try: print(f"{Fore.CYAN} İstatistikler hesaplanıyor...") # Genel istatistikler stats = { "Toplam_Analiz_Edilen": len(self.successful_files), "Basarisiz_Dosya": len(self.failed_files) } # Periyot bazlı istatistikler for period in self.pearson_periods: period_data = self.period_results[period] if period_data: pearson_values = [item["Pearson"] for item in period_data if item.get("Pearson")] stats[f"Ortalama_Pearson_{period}"] = round(np.mean(pearson_values), 4) if pearson_values else 0 stats[f"Yuksek_Pearson_Sayisi_{period}"] = len([p for p in pearson_values if abs(p) >= 0.8]) # Fırsat istatistikleri stats["Kanal_Alt_Firsatlari"] = len(self.channel_opportunities) stats["Breakout_Adaylari"] = len(self.breakout_candidates) # Trend analizi trend_up = len([item for item in self.all_results if item.get("Trend_Yonu") == "Yükseliş"]) trend_down = len([item for item in self.all_results if item.get("Trend_Yonu") == "Düşüş"]) stats["Yukselis_Trendi"] = trend_up stats["Dusus_Trendi"] = trend_down self.statistics = stats return stats except Exception as e: print(f"{Fore.RED}❌ İstatistik hesaplama hatası: {e}") return {} @staticmethod def find_input_files(input_folder="StokData/Kapanis/"): """Giriş dosyalarını bulma""" try: pattern = os.path.join(input_folder, "*.xlsx") files = glob.glob(pattern) if not files: print(f"{Fore.RED}❌ {input_folder} klasöründe Excel dosyası bulunamadı!") return [] print(f"{Fore.BLUE} {len(files)} adet Excel dosyası bulundu.") return files except Exception as e: print(f"{Fore.RED}❌ Dosya arama hatası: {e}") return [] def merge_ema_data(self): """EMA dizilim verilerini birleştirme""" try: ema_file = "StokData/idealema_analiz.xlsx" if os.path.exists(ema_file): print(f"{Fore.CYAN} EMA verileri birleştiriliyor...") ema_df = pd.read_excel(ema_file, sheet_name="IdealEMAUp") ema_mapping = dict(zip(ema_df["Hisse_Adi"], ema_df["EMA_Dizilim"])) # Tüm sonuçlara EMA bilgisi ekle for result in self.all_results: result["EMA_Dizilim"] = ema_mapping.get(result["Hisse_Adi"], "Bilinmiyor") for result in self.ideal_pearson: result["EMA_Dizilim"] = ema_mapping.get(result["Hisse_Adi"], "Bilinmiyor") print(f"{Fore.GREEN}✅ EMA verileri başarıyla birleştirildi.") else: print(f"{Fore.YELLOW}⚠️ EMA dosyası bulunamadı: {ema_file}") except Exception as e: print(f"{Fore.RED}❌ EMA veri birleştirme hatası: {e}") @staticmethod def write_sheet_with_formatting(excel_writer, df, sheet_name, freeze_panes=None): """Excel sayfası yazma ve biçimlendirme""" try: if isinstance(df, list): df = pd.DataFrame(df) if df.empty: df = pd.DataFrame([{"Mesaj": "Veri bulunamadı"}]) df.to_excel(excel_writer, sheet_name=sheet_name, index=False) ws = excel_writer.sheets[sheet_name] # Başlık biçimlendirme header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid') for col_num, column in enumerate(df.columns, 1): cell = ws.cell(row=1, column=col_num) cell.font = Font(bold=True, color='FFFFFF') cell.fill = header_fill cell.alignment = Alignment(horizontal='center') # Sütun genişlikleri if any(keyword in str(column) for keyword in ['Tarih', 'Date']): ws.column_dimensions[get_column_letter(col_num)].width = 15 elif any(keyword in str(column) for keyword in ['Hisse', 'CODE']): ws.column_dimensions[get_column_letter(col_num)].width = 12 elif any(keyword in str(column) for keyword in ['Pearson', 'Fark', 'Pct']): ws.column_dimensions[get_column_letter(col_num)].width = 18 else: ws.column_dimensions[get_column_letter(col_num)].width = 20 # Veri satırlarını biçimlendirme for row_num in range(2, len(df) + 2): for col_num in range(1, len(df.columns) + 1): cell = ws.cell(row=row_num, column=col_num) cell.alignment = Alignment(horizontal='center') column_name = df.columns[col_num - 1] # Pearson değerleri için renklendirme if 'Pearson' in str(column_name) and isinstance(cell.value, (int, float)): if abs(cell.value) >= 0.9: cell.fill = PatternFill(start_color='00FF00', end_color='00FF00', fill_type='solid') elif abs(cell.value) >= 0.8: cell.fill = PatternFill(start_color='90EE90', end_color='90EE90', fill_type='solid') elif abs(cell.value) >= 0.7: cell.fill = PatternFill(start_color='FFFF99', end_color='FFFF99', fill_type='solid') # Fark yüzdeleri için renklendirme elif 'Alt_Fark' in str(column_name) and isinstance(cell.value, (int, float)): if -2 <= cell.value <= 4: cell.fill = PatternFill(start_color='FFFFCC', end_color='FFFFCC', fill_type='solid') elif 'Ust_Fark' in str(column_name) and isinstance(cell.value, (int, float)): if -4 <= cell.value <= 2: cell.fill = PatternFill(start_color='FFCCCC', end_color='FFCCCC', fill_type='solid') # Freeze panes if freeze_panes: ws.freeze_panes = freeze_panes # AutoFilter ekleme (başlık satırına) if len(df) > 0: ws.auto_filter.ref = f"A1:{get_column_letter(len(df.columns))}{len(df) + 1}" except Exception as e: print(f"{Fore.RED}❌ Excel biçimlendirme hatası ({sheet_name}): {e}") def save_results(self): """Sonuçları Excel'e kaydetme""" try: # Klasörler oluşturma output_folder = "StokData/LinearRegression/" os.makedirs(output_folder, exist_ok=True) # Dosya isimleri today = datetime.today().strftime('%Y-%m-%d') main_file = os.path.join("StokData", "lineer_regresyon_analiz.xlsx") archive_file = os.path.join(output_folder, f"lineer_regresyon_analiz_{today}.xlsx") # Veri hazırlama print(f"{Fore.GREEN} Excel dosyası hazırlanıyor...") # Sıralama işlemleri # İdeal Pearson - maksimum mutlak Pearson değerine göre sırala ideal_pearson_df = pd.DataFrame(self.ideal_pearson) if not ideal_pearson_df.empty: pearson_cols = [col for col in ideal_pearson_df.columns if 'Pearson_' in col] if pearson_cols: ideal_pearson_df['Max_Abs_Pearson'] = ideal_pearson_df[pearson_cols].abs().max(axis=1) ideal_pearson_df = ideal_pearson_df.sort_values('Max_Abs_Pearson', ascending=False) # Kanal fırsatları - Pearson değerine göre sırala (İlk 50) channel_opp_df = pd.DataFrame(self.channel_opportunities) if not channel_opp_df.empty: channel_opp_df = channel_opp_df.sort_values(['Pearson', 'Alt_Fark_Pct'], ascending=[False, True]).head( 50) # Breakout adayları - Pearson değerine göre sırala (İlk 50) breakout_df = pd.DataFrame(self.breakout_candidates) if not breakout_df.empty: breakout_df = breakout_df.sort_values(['Pearson', 'Ust_Fark_Pct'], ascending=[False, False]).head(50) # İstatistikler stats_df = pd.DataFrame([ {"Metrik": key, "Deger": value} for key, value in self.statistics.items() ]) # Ana dosyayı kaydet print(f"{Fore.GREEN} Ana dosya kaydediliyor: {main_file}") with pd.ExcelWriter(main_file, engine='openpyxl') as writer: # Ana sayfalar self.write_sheet_with_formatting(writer, pd.DataFrame(self.all_results), "TumSonuclar", "A2") self.write_sheet_with_formatting(writer, ideal_pearson_df, "IdealPearson", "A2") self.write_sheet_with_formatting(writer, channel_opp_df, "KanalFirsatlari_Top50", "A2") self.write_sheet_with_formatting(writer, breakout_df, "BreakoutAdaylari_Top50", "A2") self.write_sheet_with_formatting(writer, stats_df, "Istatistikler", "A2") # Periyot bazlı sayfalar for period in self.pearson_periods: period_df = pd.DataFrame(self.period_results[period]) if not period_df.empty: period_df = period_df.sort_values('Pearson', ascending=False) self.write_sheet_with_formatting(writer, period_df, f"Periyot_{period}", "A2") # Arşiv dosyasını kaydet print(f"{Fore.BLUE} Arşiv dosyası kaydediliyor: {archive_file}") with pd.ExcelWriter(archive_file, engine='openpyxl') as writer: self.write_sheet_with_formatting(writer, pd.DataFrame(self.all_results), "TumSonuclar", "A2") self.write_sheet_with_formatting(writer, ideal_pearson_df, "IdealPearson", "A2") self.write_sheet_with_formatting(writer, channel_opp_df, "KanalFirsatlari_Top50", "A2") self.write_sheet_with_formatting(writer, breakout_df, "BreakoutAdaylari_Top50", "A2") self.write_sheet_with_formatting(writer, stats_df, "Istatistikler", "A2") for period in self.pearson_periods: period_df = pd.DataFrame(self.period_results[period]) if not period_df.empty: period_df = period_df.sort_values('Pearson', ascending=False) self.write_sheet_with_formatting(writer, period_df, f"Periyot_{period}", "A2") return True except Exception as e: print(f"{Fore.RED}❌ Sonuç kaydetme hatası: {e}") return False def print_summary(self): """Özet rapor""" total = len(self.successful_files) + len(self.failed_files) success_rate = (len(self.successful_files) / total * 100) if total > 0 else 0 print(f"\n{Fore.CYAN} ===== LİNEER REGRESYON ANALİZ RAPORU =====") print(f"{Fore.BLUE} Analiz Periyotları: {', '.join(map(str, self.pearson_periods))}") print(f"{Fore.GREEN}✅ Başarılı: {len(self.successful_files)}") print(f"{Fore.RED}❌ Başarısız: {len(self.failed_files)}") print(f"{Fore.BLUE} Başarı oranı: {success_rate:.1f}%") print(f"\n{Fore.MAGENTA} SONUÇLAR:") print(f"{Fore.GREEN} Toplam Analiz: {len(self.all_results)}") print(f"{Fore.YELLOW} Kanal Alt Fırsatları: {len(self.channel_opportunities)}") print(f"{Fore.RED} Breakout Adayları: {len(self.breakout_candidates)}") print(f"{Fore.BLUE} İdeal Pearson Sayısı: {len(self.ideal_pearson)}") # En yüksek Pearson değerleri if self.all_results: high_pearson = [r for r in self.all_results if abs(r.get('Pearson', 0)) >= 0.9] print(f"{Fore.GREEN} ⭐ Yüksek Pearson (>=0.9): {len(high_pearson)}") def save_failed_list(self, filename="basarisiz_regresyon_dosyalari.txt"): """Başarısız dosyaları kaydetme""" if self.failed_files: try: with open(filename, 'w', encoding='utf-8') as f: f.write("# Başarısız Lineer Regresyon analizi dosyaları\n") f.write(f"# Tarih: {datetime.now().strftime('%Y-%m-%d')}\n\n") for file_name in self.failed_files: f.write(f"{file_name}\n") print(f"{Fore.YELLOW} Başarısız dosyalar {filename} dosyasına kaydedildi.") except Exception as e: print(f"{Fore.RED}❌ Başarısız dosya listesi kaydetme hatası: {e}") def main(self): """Ana fonksiyon""" print(f"{Fore.CYAN} Lineer Regresyon ve Pearson Analizi Başlatılıyor...") print(f"{Fore.BLUE} Analiz Periyotları: {', '.join(map(str, self.pearson_periods))}") print(f"{Fore.BLUE} Özel Analiz Periyotları: {', '.join(map(str, self.analysis_periods))}") # Giriş dosyalarını bulma input_files = self.find_input_files() if not input_files: return print(f"{Fore.BLUE} Toplam işlenecek dosya: {len(input_files)}\n") # Dosyaları işleme for i, file_path in enumerate(input_files, 1): print(f"\n{Fore.MAGENTA}[{i}/{len(input_files)}] İşleniyor...") self.process_single_file(file_path) # EMA verilerini birleştirme self.merge_ema_data() # İstatistikleri hesaplama self.calculate_statistics() # Sonuçları kaydetme success = self.save_results() if success: self.print_summary() # Başarısız dosyaları kaydetme self.save_failed_list() print(f"\n{Fore.GREEN} Lineer regresyon analizi tamamlandı!") print(f"{Fore.BLUE} Ana dosya: StokData/lineer_regresyon_analiz.xlsx") print(f"{Fore.BLUE} Arşiv klasörü: StokData/LinearRegression/") # Kullanım if __name__ == "__main__": analyzer = LinearRegressionAnalyzer() analyzer.main()
Borsa analizinde teknik göstergeler, yatırım kararlarının vazgeçilmez araçlarıdır. Bu yazımızda, Python kullanarak borsa hisselerinin kapanış verilerinden Wave Trend Osilatörü (WT) hesaplayan ve bu verilerden al-sat sinyalleri üreten gelişmiş bir sistem paylaşacağım.
Bu sistem, pandas, numpy, openpyxl, colorama ve glob gibi popüler Python kütüphanelerini kullanır. Önceki çalışamalarımızda ki Excel dosyalarından kapanış verilerini okur, WT1 ve WT2 eğrilerini hesaplar, Dip ve tepe bölgelerinde al-sat sinyalleri üretir, Sonuçları Excel’e kaydeder ve Başarılı/başarısız hisse senetleri için rapor oluşturur.
Wave Trend İndikatörü Nedir?
Wave Trend (WT) osilatörü, fiyatın momentumu hakkında bilgi sağlayan ve özellikle dip/tepe bölgelerinde verdiği sinyallerle ön plana çıkan bir teknik göstergedir. İki bileşenden oluşur:
WT1: Ana sinyal çizgisi
WT2: WT1’in kısa vadeli hareketli ortalaması (sinyal çizgisi)
Alım/satım sinyalleri bu iki çizginin kesişimleriyle belirlenir. Ek olarak, aşırı alım (OB) ve aşırı satım (OS) seviyeleri ile sinyallerin güvenilirliği artırılır.
1. Wave Trend Hesaplama
Kodun kalbinde, calculate_wave_trend fonksiyonu yer alıyor. Burada;
ESA (Exponential Smoothed Average) hesaplanır.
ESA’den sapmalar üzerinden CI (Composite Index) elde edilir.
CI’nın üstel ortalaması WT1, onun da 4 dönemlik ortalaması WT2 olarak alınır.
WT1 ve WT2 arasındaki kesişimlere göre sinyaller üretilir:
Buy (AL): WT1, WT2’nin üzerine çıkar
Sell(SAT): WT1, WT2’nin altına iner
BuyAtBottom(DIPTE AL): Aşırı satım bölgesinde al sinyali
SellAtTop(TEPEDE SAT): Aşırı alım bölgesinde sat sinyali
2. Dosya Taraması ve İşleme
Tüm hisse senedi kapanış verileri “StokData/Kapanis/” klasöründen otomatik olarak taranır. Her bir dosya için:
Tarih sıralaması yapılır. WT hesaplanır. Son sinyal (AL, SAT, DIPTE_AL, TEPEDE_SAT) analiz edilir.
Sonuçlar “StokData/WaveTrend/” klasörüne kaydedilir.
3. Sinyal Özeti ve Raporlama
Tüm hisseler için elde edilen sinyaller, Tarih ve WT1/WT2 değerleriyle birlikte özetlenir.
“StokData/wave_trend_signals_YYYYMMDD_HHMM.xlsx” adıyla kaydedilir.
Başarılı ve başarısız işlenen hisselerin listesi oluşturulur.
Aşağıda paylaşıcağım Python Çalışmasını X_04_BorsaPin_WaveTrend.py olarak kayıt edebilirsiniz.
Python Kodu
import pandas as pd import numpy as np import os from datetime import datetime from colorama import Fore, init import glob """ Borsapin StokData/Kapanis/ klasöründeki hisse kapanış datalarından Wave Trend Sinyallerini hesaplar. www.kursatsenturk.com """ init(autoreset=True) class WaveTrendCalculator: def __init__(self, n1=10, n2=21, ob2=53, os2=-53): self.n1 = n1 # ESA periyodu self.n2 = n2 # CI periyodu self.ob2 = ob2 # Overbought seviyesi self.os2 = os2 # Oversold seviyesi self.successful_files = [] self.failed_files = [] self.signal_results = [] def calculate_wave_trend(self, df): """Wave Trend hesaplama fonksiyonu""" try: close = df['Kapanış'] # ESA (Exponential Smoothed Average) esa = close.ewm(span=self.n1, adjust=False).mean() # D (Deviation) d = (close - esa).abs().ewm(span=self.n1, adjust=False).mean() # CI (Currency Index) ci = (close - esa) / (0.015 * d) # WT1 ve WT2 hesaplama wt1 = ci.ewm(span=self.n2, adjust=False).mean() wt2 = wt1.rolling(window=4).mean() # Sinyaller buy = (wt1 > wt2) & (wt1.shift(1) <= wt2.shift(1)) buy_at_bottom = buy & (wt2 < self.os2) sell = (wt2 > wt1) & (wt2.shift(1) <= wt1.shift(1)) sell_at_top = sell & (wt2 > self.ob2) return wt1, wt2, buy, buy_at_bottom, sell, sell_at_top except Exception as e: print(f"{Fore.RED}❌ Wave Trend hesaplama hatası: {e}") return None, None, None, None, None, None @staticmethod def get_last_signal(df, ticker_name): """Son sinyali bulma fonksiyonu""" try: # NaN değerleri temizle clean_df = df.dropna(subset=['WT1', 'WT2']) if clean_df.empty: return None, None, None, None # Son tarihten geriye doğru sinyal ara for i in reversed(clean_df.index): row = clean_df.loc[i] date = row['Tarih'] wt1 = row['WT1'] wt2 = row['WT2'] if row['BuyAtBottom']: return 'DIPTE_AL', date, wt1, wt2 elif row['SellAtTop']: return 'TEPEDE_SAT', date, wt1, wt2 elif row['Buy']: return 'AL', date, wt1, wt2 elif row['Sell']: return 'SAT', date, wt1, wt2 return None, None, None, None except Exception as e: print(f"{Fore.RED}❌ {ticker_name} sinyal arama hatası: {e}") return None, None, None, None def process_single_file(self, file_path): """Tek dosya için Wave Trend hesaplama""" # ticker_name'i en başta tanımla ticker_name = "BILINMEYEN" try: # Dosya adından hisse adını alma file_name = os.path.basename(file_path) ticker_name = os.path.splitext(file_name)[0] print(f"{Fore.YELLOW} Wave Trend hesaplanıyor: {ticker_name}...") # Excel dosyasını okuma df = pd.read_excel(file_path) # Gerekli sütunların varlığını kontrol etme required_columns = ['Tarih', 'Kapanış'] missing_columns = [col for col in required_columns if col not in df.columns] if missing_columns: raise ValueError(f"Eksik sütunlar: {missing_columns}") # Veri kontrolü if df.empty or df['Kapanış'].isna().all(): raise ValueError("Kapanış verisi boş veya geçersiz") # Tarihe göre sıralama df = df.sort_values('Tarih').reset_index(drop=True) # Wave Trend hesaplamaları print(f"{Fore.CYAN} ⚡ Wave Trend parametreleri: n1={self.n1}, n2={self.n2}, ob={self.ob2}, os={self.os2}") wt1, wt2, buy, buy_at_bottom, sell, sell_at_top = self.calculate_wave_trend(df) if wt1 is None: raise ValueError("Wave Trend hesaplama başarısız") # Yeni DataFrame oluşturma result_df = pd.DataFrame() result_df['Hisse_Adi'] = [ticker_name] * len(df) result_df['Tarih'] = df['Tarih'] result_df['Kapanış'] = df['Kapanış'] result_df['WT1'] = wt1.round(4) result_df['WT2'] = wt2.round(4) result_df['Buy'] = buy result_df['BuyAtBottom'] = buy_at_bottom result_df['Sell'] = sell result_df['SellAtTop'] = sell_at_top # Son sinyali bulma signal_type, signal_date, last_wt1, last_wt2 = self.get_last_signal(result_df, ticker_name) # Sinyal sonucunu kaydetme if signal_type: self.signal_results.append({ 'Hisse_Adi': ticker_name, 'Sinyal': signal_type, 'Sinyal_Tarihi': pd.to_datetime(signal_date).date() if signal_date else None, 'WT1': round(last_wt1, 2) if last_wt1 else None, 'WT2': round(last_wt2, 2) if last_wt2 else None }) # Renkli sinyal yazdırma renk = Fore.GREEN if "AL" in signal_type else Fore.RED print(renk + f" Son Sinyal: {signal_type} @ {signal_date}") else: print(f"{Fore.WHITE} ▫️ Aktif sinyal yok") # Çıktı klasörünü oluşturma output_folder = "StokData/WaveTrend/" os.makedirs(output_folder, exist_ok=True) # Dosya yolu output_file = os.path.join(output_folder, f"{ticker_name}.xlsx") # Excel'e kaydetme result_df.to_excel(output_file, index=False) print(f"{Fore.GREEN}✅ {ticker_name} Wave Trend verileri başarıyla kaydedildi.") print(f"{Fore.BLUE} Konum: {output_file}") print(f"{Fore.BLUE} Toplam satır: {len(result_df)}") self.successful_files.append(ticker_name) return True except Exception as e: print(f"{Fore.RED}❌ {ticker_name} için hata: {e}") self.failed_files.append(ticker_name) return False @staticmethod def find_input_files(input_folder="StokData/Kapanis/"): """Giriş dosyalarını bulma""" try: # Excel dosyalarını arama pattern = os.path.join(input_folder, "*.xlsx") files = glob.glob(pattern) if not files: print(f"{Fore.RED}❌ {input_folder} klasöründe Excel dosyası bulunamadı!") return [] print(f"{Fore.BLUE} {len(files)} adet Excel dosyası bulundu.") return files except Exception as e: print(f"{Fore.RED}❌ Dosya arama hatası: {e}") return [] def save_signals_summary(self, filename=None): """Sinyal özetini kaydetme""" if not self.signal_results: print(f"{Fore.YELLOW}⚠️ Kaydedilecek sinyal yok.") return if filename is None: timestamp = datetime.now().strftime('%Y%m%d_%H%M') filename = f"StokData/wave_trend_signals_{timestamp}.xlsx" try: # Klasör oluşturma os.makedirs(os.path.dirname(filename), exist_ok=True) # Sinyal DataFrame'i oluşturma signals_df = pd.DataFrame(self.signal_results) # Sinyal türüne göre sıralama signal_order = ['DIPTE_AL', 'AL', 'SAT', 'TEPEDE_SAT'] signals_df['sinyal_order'] = signals_df['Sinyal'].map({s: i for i, s in enumerate(signal_order)}) signals_df = signals_df.sort_values(['sinyal_order', 'Sinyal_Tarihi'], ascending=[True, False]) signals_df = signals_df.drop('sinyal_order', axis=1) # Excel'e kaydetme signals_df.to_excel(filename, index=False) print(f"{Fore.GREEN} Sinyal özeti kaydedildi: {filename}") print(f"{Fore.BLUE} Toplam sinyal: {len(signals_df)}") # Sinyal türü dağılımı signal_counts = signals_df['Sinyal'].value_counts() for signal, count in signal_counts.items(): color = Fore.GREEN if "AL" in signal else Fore.RED print(f"{color} {signal}: {count} adet") except Exception as e: print(f"{Fore.RED}❌ Sinyal özeti kaydetme hatası: {e}") def print_summary(self): """Özet rapor""" total = len(self.successful_files) + len(self.failed_files) success_rate = (len(self.successful_files) / total * 100) if total > 0 else 0 print(f"\n{Fore.CYAN} ===== WAVE TREND HESAPLAMA RAPORU =====") print(f"{Fore.BLUE} Parametreler: n1={self.n1}, n2={self.n2}, OB={self.ob2}, OS={self.os2}") print(f"{Fore.GREEN}✅ Başarılı: {len(self.successful_files)}") print(f"{Fore.RED}❌ Başarısız: {len(self.failed_files)}") print(f"{Fore.BLUE} Başarı oranı: {success_rate:.1f}%") print(f"{Fore.MAGENTA} Toplam sinyal: {len(self.signal_results)}") if self.successful_files: print(f"{Fore.GREEN} Başarılı dosyalar: {', '.join(self.successful_files[:10])}") if len(self.successful_files) > 10: print(f"{Fore.GREEN} ... ve {len(self.successful_files) - 10} dosya daha") if self.failed_files: print(f"{Fore.RED} Başarısız dosyalar: {', '.join(self.failed_files)}") def save_failed_list(self, filename="basarisiz_wavetrend_dosyalari.txt"): """Başarısız dosyaları kaydetme""" if self.failed_files: try: with open(filename, 'w', encoding='utf-8') as f: f.write("# Başarısız Wave Trend hesaplama dosyaları\n") f.write(f"# Tarih: {datetime.now().strftime('%Y-%m-%d')}\n") f.write(f"# Parametreler: n1={self.n1}, n2={self.n2}, OB={self.ob2}, OS={self.os2}\n\n") for file_name in self.failed_files: f.write(f"{file_name}\n") print(f"{Fore.YELLOW} Başarısız dosyalar {filename} dosyasına kaydedildi.") except Exception as e: print(f"{Fore.RED}❌ Başarısız dosya listesi kaydetme hatası: {e}") def main(self): """Ana fonksiyon""" print(f"{Fore.CYAN} Wave Trend Hesaplama Sistemi Başlatılıyor...") print(f"{Fore.BLUE} Parametreler: n1={self.n1}, n2={self.n2}, OB={self.ob2}, OS={self.os2}") # Giriş dosyalarını bulma input_files = self.find_input_files() if not input_files: return print(f"{Fore.BLUE} Toplam işlenecek dosya: {len(input_files)}\n") # Dosyaları işleme for i, file_path in enumerate(input_files, 1): print(f"\n{Fore.MAGENTA}[{i}/{len(input_files)}] İşleniyor...") self.process_single_file(file_path) # Sonuçları kaydetme ve raporlama self.save_signals_summary() self.save_failed_list() self.print_summary() print(f"\n{Fore.GREEN} Wave Trend hesaplama işlemi tamamlandı!") print(f"{Fore.BLUE} Detay dosyaları: StokData/WaveTrend/") print(f"{Fore.BLUE} Sinyal özeti: StokData/Wave_Trend_Sinyal_*.xlsx") # Kullanım if __name__ == "__main__": # Özelleştirilebilir parametreler calculator = WaveTrendCalculator( n1=10, # ESA periyodu n2=21, # CI periyodu ob2=53, # Overbought seviyesi os2=-53 # Oversold seviyesi ) calculator.main()
Bu Bist Tarama projesininin 6. makalesi oldu sanırım. Bir sonraki çalışmamızda yukarıda paylaştığım python betiğinin (wavetrend osilatör) TradingView üzerinde çalışan indikatör kodlarını paylaşacağım, sonrasında Python Bist Tarama çalışmaları için Osilatör değerleri yada Linear Regresyon kanalları ile ilgili betikler paylaşabilirim.
Hisse senedi piyasalarında başarılı olmak için doğru stratejileri belirlemek büyük önem taşır. Bu stratejilerden biri olan Üstel Hareketli Ortalama (Exponential Moving Average – EMA), fiyat hareketlerinin daha yumuşak ve tepkisel bir şekilde izlenmesini sağlar. Birden fazla EMA’nın bir araya getirilerek kullanıldığı İdeal EMA Stratejisi ise yatırımcılara trendin gücü, yönü ve olası dönüş noktaları hakkında kapsamlı bir bakış sunar. Bu yazıda stratejiyi Python kodları ile nasıl uygulayabileceğinizi ve analizlerinizi nasıl otomatikleştirebileceğinizi ele alacağız.
İdeal EMA, belirli periyotlardaki üstel hareketli ortalamaların (örneğin 5, 8, 13, 21, 34, 55, 89…) birbirine göre dizilimini temel alan bir analiz yöntemidir. Bu stratejinin ana mantığı, bir hisse senedinin fiyatı ve EMA’larının belirli bir sırayla dizilmesi durumunda güçlü bir trendin varlığını işaret etmesidir.
Yazının devamında paylaşacağım Python kodu, bu stratejiyi otomatikleştirmek için tasarlanmıştır.
IdealEMACalculator adlı bir sınıf etrafında kurgulanan bu kod, birden fazla hisse senedi dosyasını analiz ederek İdeal EMA sinyallerini tespit eder ve sonuçları Excel dosyasına kaydeder.
Kod, modüler bir yapıya sahip. Bu yapı sayesinde, her bir metot belirli bir işi yapar ve kodun anlaşılırlığı artar.
Bağımlı olduğu kütüphaneleri terminalde aşağıdaki komutla install edebilirsiniz. pip install pandas numpy openpyxl colorama
Python ile oluşturulan bu otomatize sistem, yatırımcılara manuel olarak yüzlerce hisse senedini tek tek inceleme zahmetinden kurtarır. Her gün yüzlerce veriyi otomatik olarak tarayarak potansiyel İdeal EMA Dizilim sinyallerini anında tespit edebilir.
Yalnız unutmamak gerekir ki, hiçbir strateji her zaman kazanç sağlamaz. İdeal EMA stratejisi de tek başına yeterli olmayabilir. Genellikle hacim, diğer teknik göstergelerde ve temel analiz verileriyle birlikte kullanılması, sinyallerin güvenilirliğini artırır.
Bu Makale Daha önceki Makalelerin devamı niteliğinde olduğu için, aşağıdaki makaleleri de incelemeniz büyük önem taşıyor.
Python ve PyCharm kurulumu
Gerekli kütüphanelerin kurulumu
Kapanış Datalarını çeken Script
Python ile Kapanış Fiyatlarından EMA Hesaplama Betiği
Çalışmaların tamamını içeren Google Drive Alanı
X_03_Borsapin_EmaDizilim.py Çalışmayı Bu isimde kayıt edebilirsiniz.
Python Kodları
import pandas as pd import numpy as np import os from datetime import datetime, timedelta from colorama import Fore, init import glob from openpyxl.styles import Font, PatternFill, Alignment from openpyxl.utils import get_column_letter """ Borsapin StokData/Ema klasöründeki hisse datalarından EMA ALIGNMENT dizilimlerini hesaplar. www.kursatsenturk.com """ init(autoreset=True) class IdealEMACalculator: def __init__(self): self.ema_periods = [5, 8, 13, 21, 34, 55, 89, 144, 233, 370] self.successful_files = [] self.failed_files = [] # Sonuç listeleri self.ideal_up = [] self.ideal_down = [] self.neutral = [] self.potential = [] self.all_signals = [] def check_alignment(self, stock_row): """EMA dizilimi kontrolü""" try: ema_vals = [stock_row[f'EMA_{p}'] for p in self.ema_periods] closing_price = stock_row['Kapanış'] # İdeal yükseliş: Kapanış > tüm EMA'lar ve EMA'lar büyükten küçüğe sıralı if closing_price > max(ema_vals) and ema_vals == sorted(ema_vals, reverse=True): return 'İdeal EMA Yükseliş' # İdeal düşüş: Kapanış < tüm EMA'lar ve EMA'lar küçükten büyüğe sıralı elif closing_price < min(ema_vals) and ema_vals == sorted(ema_vals): return 'İdeal EMA Düşüş' else: return 'İdeal EMA Nötr' except (KeyError, ValueError, TypeError) as e: # Daha spesifik istisna yakalama print(f"{Fore.RED}❌ EMA alignment kontrol hatası: {e}") return 'İdeal EMA Nötr' def check_potential(self, stock_row): """Potansiyel oluşum kontrolü""" try: ema_vals = [stock_row[f'EMA_{p}'] for p in self.ema_periods] closing_price = stock_row['Kapanış'] return min(ema_vals) < closing_price < max(ema_vals) except (KeyError, ValueError, TypeError): # Basit durumlar için 'except:' kullanmak yerine 'except Exception' veya spesifik hataları kullanın return False def price_below_any_ema(self, stock_row): """Kapanış EMA'ların altına sarktı mı?""" try: return any(stock_row['Kapanış'] < stock_row[f'EMA_{p}'] for p in self.ema_periods) except (KeyError, ValueError, TypeError): return False @staticmethod def calculate_gain(entry_price, exit_price): """Kazanç hesaplama fonksiyonu""" if entry_price and exit_price and entry_price > 0: gain = exit_price - entry_price gain_percent = (gain / entry_price) * 100 return gain, gain_percent return 0, 0 @staticmethod def find_max_price_after_signal(dataframe, signal_date): """Sinyal sonrası maksimum fiyat bulma""" try: future_data = dataframe[dataframe['Tarih'] > signal_date] if not future_data.empty: max_price = future_data['Kapanış'].max() max_date = future_data[future_data['Kapanış'] == max_price]['Tarih'].iloc[0] return max_price, max_date return None, None except (KeyError, ValueError, IndexError): return None, None @staticmethod def find_min_price_after_signal(dataframe, signal_date): """Sinyal sonrası minimum fiyat bulma""" try: future_data = dataframe[dataframe['Tarih'] > signal_date] if not future_data.empty: min_price = future_data['Kapanış'].min() min_date = future_data[future_data['Kapanış'] == min_price]['Tarih'].iloc[0] return min_price, min_date return None, None except (KeyError, ValueError, IndexError): return None, None def find_latest_signal_date(self, dataframe, target_status): """İdeal EMA sinyalinin son oluştuğu tarihi bulma""" try: dataframe = dataframe.sort_values("Tarih").reset_index(drop=True) last_status = None latest_signal_date = None for _, row in dataframe.iterrows(): current_status = self.check_alignment(row) if current_status == target_status and last_status != target_status: latest_signal_date = row['Tarih'] last_status = current_status return latest_signal_date except (KeyError, ValueError, IndexError): return None def process_single_file(self, file_path): """Tek dosya için İdeal EMA analizi""" ticker_name = None # ticker_name değişkeni için başlangıç değeri atama try: # Dosya adından hisse adını alma file_name = os.path.basename(file_path) ticker_name = os.path.splitext(file_name)[0] print(f"{Fore.YELLOW} İdeal EMA analizi: {ticker_name}...") # Excel dosyasını okuma df = pd.read_excel(file_path) # Gerekli sütunların varlığını kontrol etme required_columns = ['Tarih', 'Kapanış'] + [f'EMA_{p}' for p in self.ema_periods] missing_columns = [col for col in required_columns if col not in df.columns] if missing_columns: raise ValueError(f"Eksik sütunlar: {missing_columns}") # Veri kontrolü ve temizlik df = df.dropna(subset=[f'EMA_{p}' for p in self.ema_periods]) if df.empty: raise ValueError("EMA verileri boş") # Tarih formatı ve sıralama df['Tarih'] = pd.to_datetime(df['Tarih']) df = df.sort_values('Tarih').reset_index(drop=True) print(f"{Fore.CYAN} ⚡ EMA dizilim analizi yapılıyor...") # Tüm sinyal değişimlerini takip et last_status = None signals_history = [] for _, row in df.iterrows(): status = self.check_alignment(row) if status != last_status: if last_status is not None: # İlk durumu kaydetme signals_history.append({ 'CODE': ticker_name, 'DATE': row["Tarih"], 'CLOSING_TL': row["Kapanış"], 'STATUS': status, 'PREV_STATUS': last_status }) last_status = status # Son durumu analiz et if not df.empty: last_row = df.iloc[-1] last_status = self.check_alignment(last_row) # Son sinyal tarihini bul latest_signal_date = None if last_status == "İdeal EMA Yükseliş": latest_signal_date = self.find_latest_signal_date(df.copy(), "İdeal EMA Yükseliş") elif last_status == "İdeal EMA Düşüş": latest_signal_date = self.find_latest_signal_date(df.copy(), "İdeal EMA Düşüş") # Sinyal sonrası kazanç hesapla max_price, max_date = None, None min_price, min_date = None, None gain, gain_percent = 0, 0 if signals_history: last_signal = signals_history[-1] if last_signal['STATUS'] == 'İdeal EMA Yükseliş': max_price, max_date = self.find_max_price_after_signal(df, last_signal['DATE']) if max_price: gain, gain_percent = self.calculate_gain(last_signal['CLOSING_TL'], max_price) elif last_signal['STATUS'] == 'İdeal EMA Düşüş': min_price, min_date = self.find_min_price_after_signal(df, last_signal['DATE']) if min_price: gain, gain_percent = self.calculate_gain(last_signal['CLOSING_TL'], min_price) # Kayıt oluştur record = { "Hisse_Adi": ticker_name, "Tarih": pd.to_datetime(last_row["Tarih"]).date(), "Kapanış": last_row["Kapanış"], "EMA_Dizilim": last_status, "Son_Sinyal_Tarihi": latest_signal_date.date() if latest_signal_date else None, "Bozulma": "Evet" if self.price_below_any_ema(last_row) else "Hayır", "Sinyal_Sonrasi_Max_Fiyat": max_price, "Max_Fiyat_Tarihi": max_date.date() if max_date else None, "Sinyal_Sonrasi_Min_Fiyat": min_price, "Min_Fiyat_Tarihi": min_date.date() if min_date else None, "Kazanc_TL": round(gain, 2), "Kazanc_Yuzde": round(gain_percent, 2) } # Kategorilere ayır if last_status == "İdeal EMA Yükseliş": self.ideal_up.append(record) print(f"{Fore.GREEN} İdeal EMA Yükseliş tespit edildi!") elif last_status == "İdeal EMA Düşüş": self.ideal_down.append(record) print(f"{Fore.RED} İdeal EMA Düşüş tespit edildi!") else: self.neutral.append(record) print(f"{Fore.WHITE} ➖ Nötr durumda") # Potansiyel kontrol if self.check_potential(last_row): potential_record = record.copy() potential_record["EMA_Dizilim"] = "İdeal EMA Olabilir" self.potential.append(potential_record) print(f"{Fore.YELLOW} ⚡ Potansiyel oluşum tespit edildi!") # Tüm sinyalleri kaydet self.all_signals.extend(signals_history) print(f"{Fore.GREEN}✅ {ticker_name} İdeal EMA analizi tamamlandı.") self.successful_files.append(ticker_name) return True except FileNotFoundError: print(f"{Fore.RED}❌ {ticker_name if ticker_name else file_path} dosyası bulunamadı.") self.failed_files.append(os.path.basename(file_path)) return False except pd.errors.EmptyDataError: print(f"{Fore.RED}❌ {ticker_name if ticker_name else file_path} dosyası boş veya hatalı.") self.failed_files.append(os.path.basename(file_path)) return False except ValueError as e: # Eksik sütun veya boş veri hatası için print(f"{Fore.RED}❌ {ticker_name if ticker_name else file_path} için hata: {e}") self.failed_files.append(os.path.basename(file_path)) return False except Exception as e: # Diğer tüm beklenmeyen hatalar için print(f"{Fore.RED}❌ {ticker_name if ticker_name else file_path} için beklenmeyen hata: {e}") self.failed_files.append(os.path.basename(file_path)) return False @staticmethod def find_input_files(input_folder="StokData/Emas/"): """Giriş dosyalarını bulma (EMA dosyalarından)""" try: pattern = os.path.join(input_folder, "*.xlsx") files = glob.glob(pattern) if not files: print(f"{Fore.RED}❌ {input_folder} klasöründe Excel dosyası bulunamadı!") return [] print(f"{Fore.BLUE} {len(files)} adet EMA dosyası bulundu.") return files except Exception as e: print(f"{Fore.RED}❌ Dosya arama hatası: {e}") return [] def calculate_statistics(self): """İstatistik hesaplama""" try: # Son 1 yılın verilerini filtrele one_year_ago = datetime.now() - timedelta(days=365) recent_signals = [signal for signal in self.all_signals if signal['DATE'] >= one_year_ago] # İdeal EMA UP sinyalleri için istatistikler ideal_up_stats = [] for signal in recent_signals: if signal['STATUS'] == 'İdeal EMA Yükseliş': # Bu sinyalden sonra çıkış noktasını bul exit_signals = [s for s in recent_signals if s['CODE'] == signal['CODE'] and s['DATE'] > signal['DATE']] exit_signal = None if exit_signals: exit_signal = min(exit_signals, key=lambda x: x['DATE']) if exit_signal: gain, gain_percent = self.calculate_gain(signal['CLOSING_TL'], exit_signal['CLOSING_TL']) ideal_up_stats.append({ 'Hisse_Adi': signal['CODE'], 'Giris_Tarihi': signal['DATE'].date(), 'Giris_Fiyati': signal['CLOSING_TL'], 'Cikis_Tarihi': exit_signal['DATE'].date(), 'Cikis_Fiyati': exit_signal['CLOSING_TL'], 'Cikis_Sinyali': exit_signal['STATUS'], 'Kazanc_TL': round(gain, 2), 'Kazanc_Yuzde': round(gain_percent, 2), 'Gun_Sayisi': (exit_signal['DATE'] - signal['DATE']).days }) return ideal_up_stats except Exception as e: print(f"{Fore.RED}❌ İstatistik hesaplama hatası: {e}") return [] @staticmethod def write_sheet(excel_writer, result_df, sheet_name, freeze_panes=None): """Excel sayfası yazma ve biçimlendirme""" try: if isinstance(result_df, list): result_df = pd.DataFrame(result_df) if result_df.empty: result_df = pd.DataFrame([{"Mesaj": "Veri bulunamadı"}]) result_df.to_excel(excel_writer, sheet_name=sheet_name, index=False) ws = excel_writer.sheets[sheet_name] # Başlık biçimlendirme header_fill = PatternFill(start_color='ADD8E6', end_color='ADD8E6', fill_type='solid') for col_num, column in enumerate(result_df.columns, 1): cell = ws.cell(row=1, column=col_num) cell.font = Font(bold=True) cell.fill = header_fill cell.alignment = Alignment(horizontal='center') # Sütun genişliklerini ayarla if 'Tarih' in column: ws.column_dimensions[get_column_letter(col_num)].width = 15 elif 'Hisse' in column: ws.column_dimensions[get_column_letter(col_num)].width = 12 elif 'Kazanc' in column or 'Kapanış' in column or 'Fiyat' in column: ws.column_dimensions[get_column_letter(col_num)].width = 18 else: ws.column_dimensions[get_column_letter(col_num)].width = 20 # Freeze panes if freeze_panes: ws.freeze_panes = freeze_panes # Sayısal verileri formatlama for row_num in range(2, len(result_df) + 2): for col_num in range(1, len(result_df.columns) + 1): cell = ws.cell(row=row_num, column=col_num) cell.alignment = Alignment(horizontal='center') except Exception as e: print(f"{Fore.RED}❌ Excel yazma hatası ({sheet_name}): {e}") def save_results(self): """Sonuçları Excel'e kaydetme""" try: # Klasör oluşturma output_folder = "StokData/IdealEma/" os.makedirs(output_folder, exist_ok=True) # Dosya isimleri today = datetime.today().strftime('%Y-%m-%d') main_file = os.path.join("StokData", "idealema_analiz.xlsx") # Ana dosya Stok klasörüne archive_file = os.path.join(output_folder, f"idealema_analiz_{today}.xlsx") # Arşiv IdealEma klasörüne # İstatistikleri hesapla print(f"{Fore.CYAN} İstatistikler hesaplanıyor...") ideal_up_stats = self.calculate_statistics() # Özet istatistikler summary_stats = pd.DataFrame({ 'Kategori': ['Toplam İdeal EMA UP', 'Toplam İdeal EMA DOWN', 'Toplam Nötr', 'Toplam Potansiyel', 'Son 1 Yıl UP Sinyali', 'Son 1 Yıl Ortalama Kazanç %', 'Son 1 Yıl Ortalama Gün'], 'Deger': [len(self.ideal_up), len(self.ideal_down), len(self.neutral), len(self.potential), len(ideal_up_stats), round(np.mean([stat['Kazanc_Yuzde'] for stat in ideal_up_stats]) if ideal_up_stats else 0, 2), round(np.mean([stat['Gun_Sayisi'] for stat in ideal_up_stats]) if ideal_up_stats else 0, 2)] }) # Ana dosyayı kaydet print(f"{Fore.GREEN} Ana dosya kaydediliyor: {main_file}") with pd.ExcelWriter(main_file, engine='openpyxl') as excel_writer: self.write_sheet(excel_writer, self.ideal_up, "IdealEMAUp", freeze_panes='A2') self.write_sheet(excel_writer, self.ideal_down, "IdealEMADown", freeze_panes='A2') self.write_sheet(excel_writer, self.neutral, "Neutral", freeze_panes='A2') self.write_sheet(excel_writer, self.potential, "Potansiyel", freeze_panes='A2') self.write_sheet(excel_writer, ideal_up_stats, "Son1Yil_IstatistikUp", freeze_panes='A2') self.write_sheet(excel_writer, summary_stats, "Ozet_Istatistikler", freeze_panes='A2') # Arşiv dosyasını kaydet print(f"{Fore.BLUE} Arşiv dosyası kaydediliyor: {archive_file}") with pd.ExcelWriter(archive_file, engine='openpyxl') as excel_writer: self.write_sheet(excel_writer, self.ideal_up, "IdealEMAUp", freeze_panes='A2') self.write_sheet(excel_writer, self.ideal_down, "IdealEMADown", freeze_panes='A2') self.write_sheet(excel_writer, self.neutral, "Neutral", freeze_panes='A2') self.write_sheet(excel_writer, self.potential, "Potansiyel", freeze_panes='A2') self.write_sheet(excel_writer, ideal_up_stats, "Son1Yil_IstatistikUp", freeze_panes='A2') self.write_sheet(excel_writer, summary_stats, "Ozet_Istatistikler", freeze_panes='A2') return True, ideal_up_stats except Exception as e: print(f"{Fore.RED}❌ Sonuç kaydetme hatası: {e}") return False, [] def print_summary(self, ideal_up_stats): """Özet rapor""" total = len(self.successful_files) + len(self.failed_files) success_rate = (len(self.successful_files) / total * 100) if total > 0 else 0 print(f"\n{Fore.CYAN} ===== İDEAL EMA ANALİZ RAPORU =====") print(f"{Fore.BLUE} EMA Periyodları: {', '.join(map(str, self.ema_periods))}") print(f"{Fore.GREEN}✅ Başarılı: {len(self.successful_files)}") print(f"{Fore.RED}❌ Başarısız: {len(self.failed_files)}") print(f"{Fore.BLUE} Başarı oranı: {success_rate:.1f}%") print(f"\n{Fore.MAGENTA} SONUÇLAR:") print(f"{Fore.GREEN} İdeal EMA Yükseliş: {len(self.ideal_up)}") print(f"{Fore.RED} İdeal EMA Düşüş: {len(self.ideal_down)}") print(f"{Fore.WHITE} ➖ Nötr: {len(self.neutral)}") print(f"{Fore.YELLOW} ⚡ Potansiyel: {len(self.potential)}") print(f"{Fore.BLUE} Son 1 Yıl İstatistik: {len(ideal_up_stats)}") if ideal_up_stats: avg_gain = np.mean([stat['Kazanc_Yuzde'] for stat in ideal_up_stats]) avg_days = np.mean([stat['Gun_Sayisi'] for stat in ideal_up_stats]) print(f"{Fore.GREEN} Ortalama Kazanç: %{avg_gain:.2f}") print(f"{Fore.BLUE} Ortalama Elde Tutma: {avg_days:.1f} gün") def main(self): """Ana fonksiyon""" print(f"{Fore.CYAN} İdeal EMA Dizilim Analizi Başlatılıyor...") print(f"{Fore.BLUE} EMA Periyodları: {', '.join(map(str, self.ema_periods))}") # Giriş dosyalarını bulma input_files = self.find_input_files() if not input_files: return print(f"{Fore.BLUE} Toplam işlenecek dosya: {len(input_files)}\n") # Dosyaları işleme for i, file_path in enumerate(input_files, 1): print(f"\n{Fore.MAGENTA}[{i}/{len(input_files)}] İşleniyor...") self.process_single_file(file_path) # Sonuçları kaydetme ve raporlama success, ideal_up_stats = self.save_results() if success: self.print_summary(ideal_up_stats) print(f"\n{Fore.GREEN} İdeal EMA analizi tamamlandı!") print(f"{Fore.BLUE} Ana dosya: Stok/idealema_analiz.xlsx") print(f"{Fore.BLUE} Arşiv klasörü: Stok/IdealEma/") # Kullanım if __name__ == "__main__": calculator = IdealEMACalculator() calculator.main()
Bir sonraki yazıda Borsa İstanbul BIST Spot hisseleri için Wave Trend Osilatör sinyallerini tarama çalışması ve Trading View Wave Trend osilatör indikatörü ayrı ayrı ele alınacaktır.
Finans piyasalarında, fiyat hareketlerini anlamak ve geleceğe yönelik tahminlerde bulunmak için teknik analiz araçları hayati önem taşır. Bu araçların en popülerlerinden biri, özellikle kısa ve orta vadeli trendleri belirlemede etkili olan Üstel Hareketli Ortalama (Exponential Moving Average – EMA)’dır. EMA’nın ne olduğunu, neden bu kadar önemli olduğunu ve kendi borsa verileriniz üzerinde bu analizi otomatik olarak yapacak bir Python aracını nasıl kullanabileceğinizi ele alacağız.
Hareketli ortalamalar, belirli bir zaman dilimindeki fiyatların ortalamasını alarak fiyat dalgalanmalarını yumuşatan göstergelerdir. EMA, bu ortalama hesaplamasını yaparken son fiyatlara daha fazla ağırlık vererek, basit hareketli ortalamalardan (SMA) daha hızlı tepki verir. Bu özellik, yatırımcıların son piyasa değişikliklerini daha çabuk fark etmelerini sağlar.
EMA’nın temel mantığı, fiyatın EMA’nın üzerinde olması durumunda yükseliş trendinin devam ettiğini, altında olması durumunda ise düşüş trendinin hakim olduğunu gösterir. Farklı periyotlardaki EMA’ların birbiriyle kesişimi ise alım veya satım sinyali olarak yorumlanabilir.
Örneğin, kısa vadeli bir EMA’nın (örneğin EMA 8) uzun vadeli bir EMA’yı (örneğin EMA 21) yukarı yönlü kesmesi, güçlü bir alım sinyali olarak kabul edilebilir.
Python kodu, EMACalculator adında güçlü ve modüler bir sınıf yapısına sahiptir. Bu sınıf, borsa verilerinizi otomatik olarak okur, farklı periyotlarda EMA hesaplamaları yapar ve sonuçları düzenli bir şekilde kaydeder.
Kodun ana bölümleri
ema_periods: Kod, 5, 8, 13, 21, 34, 55, 89, 144, 233, 370 gibi finansal analizde sıkça kullanılan Fibonacci sayılarına dayalı EMA periyotlarını kullanır. Bu periyotlar, farklı zaman dilimlerindeki trendleri gözlemlemenize olanak tanır.
process_single_file: Bu metot, her bir hisse senedi için ayrı ayrı EMA’ları hesaplar. Girdi olarak alınan Excel dosyasından Tarih ve Kapanış verilerini okur ve her bir EMA periyodu için yeni sütunlar oluşturur.
find_input_files: StokData/Kapanis/ klasöründeki tüm Excel dosyalarını otomatik olarak bulur. Bu, yüzlerce hisse senedi verisini tek tek elle işlemek yerine toplu bir şekilde işlem yapmanızı sağlar.
main: Programın ana akışını yöneten metottur. Giriş dosyalarını bulur, her bir dosya için EMA hesaplamasını başlatır ve işlemin sonunda başarılı/başarısız dosyaların özetini gösteren bir rapor sunar.
Bu Makale Daha önceki Makalelerin devamı niteliğinde olduğu için, aşağıdaki makaleleri de incelemeniz büyük önem taşıyor.
Python ve PyCharm kurulumu
Çalışmaların tamamını içeren Google Drive Alanı
X_02_BorsaPin_Emas.py olarak dosyayı kayıt edebilirsiniz.
Python Kodu
import pandas as pd import os import numpy as np from datetime import datetime from colorama import Fore, init import glob """ Borsapin StokData/Kapanis klasöründeki hisse kapanış datalarından EMA'larını hesaplar www.kursatsenturk.com """ init(autoreset=True) class EMACalculator: def __init__(self): self.ema_periods = [5, 8, 13, 21, 34, 55, 89, 144, 233, 370] self.successful_files = [] self.failed_files = [] @staticmethod def calculate_ema(data, period): """EMA hesaplama fonksiyonu""" try: return data.ewm(span=period, adjust=False).mean() except Exception as e: print(f"{Fore.RED}❌ EMA hesaplama hatası (Period {period}): {e}") return pd.Series([np.nan] * len(data)) def process_single_file(self, file_path): """Tek dosya için EMA hesaplama""" ticker_name = os.path.splitext(os.path.basename(file_path))[0] try: print(f"{Fore.YELLOW} EMA hesaplanıyor: {ticker_name}...") df = pd.read_excel(file_path) required_columns = ['Tarih', 'Kapanış'] missing_columns = [col for col in required_columns if col not in df.columns] if missing_columns: raise ValueError(f"Eksik sütunlar: {missing_columns}") if df.empty or df['Kapanış'].isna().all(): raise ValueError("Kapanış verisi boş veya geçersiz") df = df.sort_values('Tarih').reset_index(drop=True) result_df = pd.DataFrame() result_df['Hisse_Adi'] = [ticker_name] * len(df) result_df['Tarih'] = df['Tarih'] result_df['Kapanış'] = df['Kapanış'] print(f"{Fore.CYAN} ⚡ EMA hesaplanıyor: ", end="") for i, period in enumerate(self.ema_periods): ema_column_name = f'EMA_{period}' print(f"{period}", end="") ema_values = self.calculate_ema(df['Kapanış'], period) result_df[ema_column_name] = ema_values.round(4) if i < len(self.ema_periods) - 1: print(", ", end="") print() output_folder = "StokData/Emas/" os.makedirs(output_folder, exist_ok=True) output_file = os.path.join(output_folder, f"{ticker_name}.xlsx") result_df.to_excel(output_file, index=False) print(f"{Fore.GREEN}✅ {ticker_name} EMA verileri başarıyla kaydedildi.") print(f"{Fore.BLUE} Konum: {output_file}") print(f"{Fore.BLUE} Toplam satır: {len(result_df)}") self.successful_files.append(ticker_name) return True except Exception as e: print(f"{Fore.RED}❌ {ticker_name} için hata: {e}") self.failed_files.append(os.path.basename(file_path)) return False @staticmethod def find_input_files(input_folder="StokData/Kapanis/"): """Giriş dosyalarını bulma""" try: pattern = os.path.join(input_folder, "*.xlsx") files = glob.glob(pattern) if not files: print(f"{Fore.RED}❌ {input_folder} klasöründe Excel dosyası bulunamadı!") return [] print(f"{Fore.BLUE} {len(files)} adet Excel dosyası bulundu.") return files except Exception as e: print(f"{Fore.RED}❌ Dosya arama hatası: {e}") return [] @staticmethod def validate_input_file(file_path): """Giriş dosyasını doğrulama""" try: df = pd.read_excel(file_path, nrows=1) required_columns = ['Tarih', 'Kapanış'] if not all(col in df.columns for col in required_columns): return False, f"Eksik sütunlar: {[col for col in required_columns if col not in df.columns]}" return True, "OK" except Exception as e: return False, f"Dosya okuma hatası: {e}" def print_summary(self): """Özet rapor""" total = len(self.successful_files) + len(self.failed_files) success_rate = (len(self.successful_files) / total * 100) if total > 0 else 0 print(f"\n{Fore.CYAN} ===== EMA HESAPLAMA RAPORU =====") print(f"{Fore.BLUE} EMA Periyodları: {', '.join(map(str, self.ema_periods))}") print(f"{Fore.GREEN}✅ Başarılı: {len(self.successful_files)}") print(f"{Fore.RED}❌ Başarısız: {len(self.failed_files)}") print(f"{Fore.BLUE} Başarı oranı: {success_rate:.1f}%") if self.successful_files: print(f"{Fore.GREEN} Başarılı dosyalar: {', '.join(self.successful_files[:10])}") if len(self.successful_files) > 10: print(f"{Fore.GREEN} ... ve {len(self.successful_files) - 10} dosya daha") if self.failed_files: print(f"{Fore.RED} Başarısız dosyalar: {', '.join(self.failed_files)}") def save_failed_list(self, filename="basarisiz_ema_dosyalari.txt"): """Başarısız dosyaları kaydetme""" if self.failed_files: try: with open(filename, 'w', encoding='utf-8') as f: f.write("# Başarısız EMA hesaplama dosyaları\n") f.write(f"# Tarih: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n") for file_name in self.failed_files: f.write(f"{file_name}\n") print(f"{Fore.YELLOW} Başarısız dosyalar {filename} dosyasına kaydedildi.") except Exception as e: print(f"{Fore.RED}❌ Başarısız dosya listesi kaydetme hatası: {e}") def create_sample_ema_analysis(self, ticker_name="SAMPLE"): """Örnek EMA analiz dosyası oluşturma""" try: dates = pd.date_range(start='2023-01-01', end='2024-01-01', freq='D') np.random.seed(42) base_price = 100 price_changes = np.random.normal(0, 2, len(dates)) prices = [base_price] for change in price_changes[1:]: new_price = prices[-1] * (1 + change / 100) prices.append(max(new_price, 1)) df = pd.DataFrame({ 'Tarih': dates, 'Kapanış': prices }) result_df = pd.DataFrame() result_df['Hisse_Adi'] = [ticker_name] * len(df) result_df['Tarih'] = df['Tarih'] result_df['Kapanış'] = df['Kapanış'] for period in self.ema_periods: ema_values = self.calculate_ema(df['Kapanış'], period) result_df[f'EMA_{period}'] = ema_values.round(4) output_folder = "StokData/Emas/" os.makedirs(output_folder, exist_ok=True) output_file = os.path.join(output_folder, f"{ticker_name}.xlsx") result_df.to_excel(output_file, index=False) print(f"{Fore.GREEN}✅ Örnek EMA dosyası oluşturuldu: {output_file}") return True except Exception as e: print(f"{Fore.RED}❌ Örnek dosya oluşturma hatası: {e}") return False def main(self): """Ana fonksiyon""" print(f"{Fore.CYAN} EMA Hesaplama Sistemi Başlatılıyor...") print(f"{Fore.BLUE} EMA Periyodları: {', '.join(map(str, self.ema_periods))}") input_files = self.find_input_files() if not input_files: print(f"{Fore.YELLOW}⚠️ Giriş dosyası bulunamadı. Örnek dosya oluşturuluyor...") self.create_sample_ema_analysis() return print(f"{Fore.BLUE} Toplam işlenecek dosya: {len(input_files)}\n") for i, file_path in enumerate(input_files, 1): print(f"\n{Fore.MAGENTA}[{i}/{len(input_files)}] İşleniyor...") is_valid, error_msg = self.validate_input_file(file_path) if not is_valid: print(f"{Fore.RED}❌ Geçersiz dosya: {os.path.basename(file_path)} - {error_msg}") self.failed_files.append(os.path.basename(file_path)) continue self.process_single_file(file_path) self.save_failed_list() self.print_summary() print(f"\n{Fore.GREEN} EMA hesaplama işlemi tamamlandı!") print(f"{Fore.BLUE} Çıktı klasörü: StokData/Emas/") # Kullanım if __name__ == "__main__": calculator = EMACalculator() calculator.main()
Bir sonraki makalede İdeal EMA dizilim (sıralama) yani Ema Alignment betiğini paylaşacağım.
Burada bütün hisseleri tarayıp belirli kurallara uyan hisseleri çalışma sayfalarında listeleteceğiz.
idealemaup, idealemadown, idealemanötr, potansiyel taşıyan ve 1 yıllık istatistikleri gösteren çalışma sayfaları olacak.
Finansal analiz ve algoritmik işlem geliştirmek isteyen herkesin ilk adımı, güvenilir hisse senedi kapanış verilerini elde etmektir. Bu veriler, teknik analizden sinyal üretimine kadar pek çok işlemin temelini oluşturur. Elinizdeki veriler ne kadar doğru ve güncelse, analizleriniz de o kadar isabetli olur.
Python ve yfinance kütüphanesi kullanılarak oluşturulmuş, Borsa İstanbul (BIST) hisselerine ait kapanış verilerini otomatik olarak indiren gelişmiş bir scripti detaylıca ele alacağız. Script, .xlsx formatında çıktılar üreterek analizlerinize doğrudan entegre edilebilir veriler sunar.
hisselistesi_txt.txt adlı dosyadan hisse senedi sembollerini okumak
Yahoo Finance üzerinden kapanış verilerini çekmek (yfinance)
Verileri işleyip Excel’e kaydetmek (pandas ve openpyxl)
Başarısız işlemleri tespit edip yeniden denemek
Kullanıcıya görsel ve metinsel özet rapor sunmak
Otomatik Hisse Formatlama
Yahoo Finance üzerinde işlem gören BIST hisseleri .IS uzantısına sahiptir.
Örneğin THYAO → THYAO.IS formatına otomatik olarak dönüştürülür.
Yeniden Deneme Mekanizması
Veri çekimi sırasında yaşanan geçici sorunlar için belirli aralıklarla otomatik yeniden deneme yapılır (max_retries, retry_delay).
Excel Kaydı ve Türkçe Sütunlar
Çekilen veriler şu sütunlar ile .xlsx dosyasına kaydedilir:
Tarih Açılış Yüksek Düşük Kapanış Hacim
Eksik Veri ve NaN Temizliği
İndirilen verilerdeki eksik veya bozuk kayıtlar otomatik olarak temizlenir. Bu sayede daha sağlıklı analizler yapılabilir.
Kullanıcı Dostu Konsol Çıktıları
colorama kütüphanesi sayesinde kullanıcıya renkli ve bilgilendirici çıktılar sunulur (örn. yeşil = başarı, kırmızı = hata).
Klasör Yapısı ve Çıktı
Kaydedilen dosyalar StokData/Kapanis/ klasörü altında saklanır. Her hisse senedi için ayrı bir Excel dosyası oluşturulur:
StokData/Kapanis/THYAO.xlsx gibi.
Kullanım Adımları
hisselistesi_txt.txt dosyasına BIST hisse sembollerini yazın (her satıra bir hisse, örn. AKBNK).
Temeli sağlam şirketleri listeleyebilirsiniz. Ya da belirli sektör yada endeks şirketlerin sembollerini de yazabilirsiniz. (XU100, XU030)
Scripti çalıştırdığınızda
Kapanış verileri indirilecek, işlenecek ve .xlsx olarak kaydedilecektir. Başarısız olan hisseler için sistem size yeniden deneme opsiyonu sunacaktır. Süreç sonunda konsolda özet rapor gösterilecektir.
Örnek Konsol Çıktısı
Veri çekiliyor: AKBNK (Deneme 1/3)…
✅ AKBNK verisi başarıyla kaydedildi.
Veri çekiliyor: THYAO (Deneme 1/3)…
❌ THYAO için hata (Deneme 1): Connection timed out.
⏳ 2 saniye bekleniyor…
…
===== ÖZET RAPOR =====
✅ Başarılı: 18
❌ Başarısız: 2
Başarı oranı: 90.0%
Başarısız hisseler: THYAO, TAVHL
Sonraki Adım
X_01_BorsaPin_StokData.py olarak kayıt edebilirsiniz.
Pyhon Kodumuz
import yfinance as yf import os import pandas as pd from datetime import datetime, timedelta from colorama import Fore, init import time from typing import List, Optional """ Borsapin StokData Yahoo Finans Üzerinden Kapanış Datalarını çekmeye yarar www.kursatsenturk.com """ init(autoreset=True) class StockDownloader: def __init__(self, max_retries: int = 3, retry_delay: int = 2): """ Hisse senedi veri indirici sınıfı Args: max_retries: Maksimum yeniden deneme sayısı retry_delay: Denemeler arası bekleme süresi (saniye) """ self.max_retries = max_retries self.retry_delay = retry_delay self.failed_tickers: List[str] = [] self.successful_tickers: List[str] = [] @staticmethod def read_tickers(file_path: str) -> List[str]: """Hisse kodlarını dosyadan okuma fonksiyonu""" try: with open(file_path, 'r', encoding='utf-8') as f: tickers = [line.strip() for line in f.readlines() if line.strip()] return tickers except FileNotFoundError: print(f"{Fore.RED}❌ {file_path} dosyası bulunamadı!") return [] except Exception as e: print(f"{Fore.RED}❌ Dosya okuma hatası: {e}") return [] @staticmethod def format_ticker(ticker: str) -> str: """Hisse sembolüne .IS eklemek""" if not ticker.endswith('.IS'): ticker = ticker + '.IS' return ticker @staticmethod def save_to_excel(data: pd.DataFrame, file_path: str) -> bool: """Veriyi Excel'e kaydetme fonksiyonu""" try: data.to_excel(file_path, index=False) return True except Exception as e: print(f"{Fore.RED}❌ Excel kaydetme hatası: {e}") return False def download_single_ticker(self, ticker: str, start_date: str, end_date: str, attempt: int = 1) -> bool: """Tek hisse için veri çekme fonksiyonu""" formatted_ticker = self.format_ticker(ticker) ticker_name = formatted_ticker.replace(".IS", "") print(f"{Fore.YELLOW} Veri çekiliyor: {ticker_name} (Deneme {attempt}/{self.max_retries})...") try: # Veri çekme stock_data = yf.download( formatted_ticker, start=start_date, end=end_date, progress=False, timeout=30, auto_adjust=True, # Uyarıyı önlemek için explicit olarak True prepost=True, threads=True ) if stock_data is None or stock_data.empty: raise ValueError("Veri boş veya None") # MultiIndex sütunlarını düzelt if isinstance(stock_data.columns, pd.MultiIndex): # MultiIndex'i tek seviyeye indir stock_data.columns = stock_data.columns.droplevel(1) # Sütun isimlerini temizle stock_data.columns = [str(col).strip() for col in stock_data.columns] # Veri işleme - sadece gerekli sütunları al required_columns = ['Open', 'High', 'Low', 'Close', 'Volume'] available_columns = [col for col in required_columns if col in stock_data.columns] if not available_columns: raise ValueError("Gerekli sütunlar bulunamadı") stock_data = stock_data[available_columns] # Sayıları düzgün formatta işleme for column in stock_data.columns: if stock_data[column].dtype == 'object': try: stock_data[column] = pd.to_numeric( stock_data[column].astype(str).str.replace(',', '.'), errors='coerce' ) except Exception: continue # NaN değerleri temizle stock_data = stock_data.dropna() # Boş veri kontrolü if stock_data.empty: raise ValueError("İşlenen veri boş") # Dosya kaydetme folder_path = "StokData/Kapanis/" os.makedirs(folder_path, exist_ok=True) file_path = os.path.join(folder_path, f"{ticker_name}.xlsx") # Tarih formatı düzenleme stock_data_copy = stock_data.copy() # Index'i reset et ve tarih sütununu ekle stock_data_copy.reset_index(inplace=True) # Tarih sütunu varsa formatla if 'Date' in stock_data_copy.columns: stock_data_copy['Date'] = pd.to_datetime(stock_data_copy['Date']).dt.date stock_data_copy.rename(columns={"Date": "Tarih"}, inplace=True) # Sütun isimlerini Türkçeye çevirme column_mapping = { 'Open': 'Açılış', 'High': 'Yüksek', 'Low': 'Düşük', 'Close': 'Kapanış', 'Volume': 'Hacim' } # Mevcut sütunları yeniden adlandır for eng_name, tr_name in column_mapping.items(): if eng_name in stock_data_copy.columns: stock_data_copy.rename(columns={eng_name: tr_name}, inplace=True) # Kaydetme if self.save_to_excel(stock_data_copy, file_path): print(f"{Fore.GREEN}✅ {ticker_name} verisi başarıyla kaydedildi.") return True else: raise Exception("Excel kaydetme başarısız") except Exception as e: print(f"{Fore.RED}❌ {ticker_name} için hata (Deneme {attempt}): {str(e)}") return False def download_with_retry(self, ticker: str, start_date: str, end_date: str) -> bool: """Yeniden deneme mekanizması ile veri çekme""" for attempt in range(1, self.max_retries + 1): if self.download_single_ticker(ticker, start_date, end_date, attempt): self.successful_tickers.append(ticker) return True if attempt < self.max_retries: print(f"{Fore.YELLOW}⏳ {self.retry_delay} saniye bekleniyor...") time.sleep(self.retry_delay) # Tüm denemeler başarısız self.failed_tickers.append(ticker) print(f"{Fore.RED} {ticker} için tüm denemeler başarısız!") return False def retry_failed_tickers(self, start_date: str, end_date: str) -> None: """Başarısız hisseleri tekrar deneme""" if not self.failed_tickers: print(f"{Fore.GREEN} Yeniden denenecek hisse yok!") return print(f"\n{Fore.CYAN} Başarısız {len(self.failed_tickers)} hisse tekrar deneniyor...") print(f"{Fore.CYAN}Başarısız hisseler: {', '.join(self.failed_tickers)}") retry_failed = [] retry_successful = [] # Başarısız hisselerin kopyasını al failed_copy = self.failed_tickers.copy() for ticker in failed_copy: print(f"\n{Fore.MAGENTA} Tekrar deneniyor: {ticker}") if self.download_with_retry(ticker, start_date, end_date): retry_successful.append(ticker) self.failed_tickers.remove(ticker) else: retry_failed.append(ticker) # Sonuçları yazdır if retry_successful: print(f"\n{Fore.GREEN}✅ Tekrar denemede başarılı: {', '.join(retry_successful)}") if retry_failed: print(f"\n{Fore.RED}❌ Hala başarısız: {', '.join(retry_failed)}") def save_failed_list(self, filename: str = "basarisiz_hisseler.txt") -> None: """Başarısız hisseleri dosyaya kaydet""" if self.failed_tickers: try: with open(filename, 'w', encoding='utf-8') as f: f.write('\n'.join(self.failed_tickers)) print(f"{Fore.YELLOW} Başarısız hisseler {filename} dosyasına kaydedildi.") except Exception as e: print(f"{Fore.RED}❌ Başarısız hisse listesi kaydetme hatası: {e}") def print_summary(self) -> None: """Özet rapor yazdır""" total = len(self.successful_tickers) + len(self.failed_tickers) success_rate = (len(self.successful_tickers) / total * 100) if total > 0 else 0 print(f"\n{Fore.CYAN} ===== ÖZET RAPOR =====") print(f"{Fore.GREEN}✅ Başarılı: {len(self.successful_tickers)}") print(f"{Fore.RED}❌ Başarısız: {len(self.failed_tickers)}") print(f"{Fore.BLUE} Başarı oranı: {success_rate:.1f}%") if self.failed_tickers: print(f"{Fore.RED} Başarısız hisseler: {', '.join(self.failed_tickers)}") def main(self) -> None: """Ana fonksiyon""" print(f"{Fore.CYAN} Veri çekme başlıyor...") # Parametreler tickers = self.read_tickers("hisselistesi_txt.txt") if not tickers: print(f"{Fore.RED}❌ Hisse listesi okunamadı, işlem sonlandırılıyor.") return start_date = '2020-01-01' end_date = (datetime.today() + timedelta(days=1)).strftime('%Y-%m-%d') print(f"{Fore.BLUE} Tarih aralığı: {start_date} - {end_date}") print(f"{Fore.BLUE} Toplam hisse sayısı: {len(tickers)}") print(f"{Fore.BLUE} Maksimum deneme sayısı: {self.max_retries}") print(f"{Fore.BLUE}⏱️ Deneme arası bekleme: {self.retry_delay} saniye\n") # İlk deneme for i, ticker in enumerate(tickers, 1): print(f"\n{Fore.BLUE}[{i}/{len(tickers)}] İşleniyor...") self.download_with_retry(ticker, start_date, end_date) # Başarısız hisseleri tekrar deneme if self.failed_tickers: user_input = input( f"\n{Fore.YELLOW}❓ Başarısız {len(self.failed_tickers)} hisseyi tekrar denemek istiyor musunuz? (e/h): " ).lower().strip() if user_input in ['e', 'evet', 'y', 'yes']: self.retry_failed_tickers(start_date, end_date) # Sonuçları kaydet ve yazdır self.save_failed_list() self.print_summary() print(f"\n{Fore.GREEN} İşlem tamamlandı!") # Kullanım if __name__ == "__main__": # Özelleştirilebilir parametreler downloader = StockDownloader( max_retries=3, # Maksimum deneme sayısı retry_delay=2 # Denemeler arası bekleme süresi (saniye) ) downloader.main()
Bu script, ileri seviye analizlerin temelini oluşturur. Bir sonraki yazımızda, bu verileri kullanarak:
5 8 13 21 34 55 89 144 233 370 EMA seviyelerini hesaplayan EMAs script yayınlayacağım.
Ve yatırım kararları için Python tabanlı teknik analiz sistemleri geliştirmeye devam edeceğiz.
Finansal analiz projelerinde Python, sunduğu zengin kütüphane ekosistemi ile güçlü bir araçtır. Borsa İstanbul (BIST) hisselerine ait kapanış verilerini çekmek ve bu veriler üzerinden teknik sinyaller üretmek için öncelikle bazı temel Python kütüphanelerine ihtiyaç duyarız.
Bu yazıda, gerekli kütüphanelerin sisteminizde yüklü olup olmadığını kontrol eden ve eksik olanları otomatik olarak kuran bir Python scriptini ele alacağız. Bu adım, ileri aşamalarda Borsa İstanbul Kapanış ve Hacim verilerini çekme ve analiz etme işlemleri için altyapı hazırlığı niteliğindedir.
Aşağıdaki kütüphaneler, veri çekme ve analiz süreçlerinde kullanılacaktır:
pandas: Veri manipülasyonu ve analiz için temel kütüphane.
numpy: Sayısal hesaplamalar için kullanılır.
colorama: Konsolda renkli yazı yazmak için kullanılır (bilgilendirici çıktılar için).
yfinance: Yahoo Finance üzerinden hisse senedi verilerini çekmeye yarar.
openpyxl: Excel dosyaları oluşturmak ve düzenlemek için kullanılır.
Kod özetle şunları yapar:
1. Belirlenen `required_packages` listesinde yer alan kütüphanelerin sistemde kurulu olup olmadığını kontrol eder.
2. Eğer bir kütüphane yüklü değilse, `pip install` komutu ile yükler.
3. Her bir paket için yükleme durumu kullanıcıya yazdırılır.
GerekliKütüphaneler.py diye kayıt edebilirsiniz.
Python Kodu
import subprocess import sys # Gerekli kütüphaneler listesi required_packages = [ "pandas", "numpy", "colorama", "yfinance", "openpyxl" ] def install_missing_packages(): for package in required_packages: try: __import__(package) print(f"{package} zaten yüklü.") except ImportError: print(f"{package} eksik, yükleniyor...") subprocess.check_call([sys.executable, "-m", "pip", "install", package]) if __name__ == "__main__": install_missing_packages()
Sonraki Adımlar
Eğer farklı bir kütüphaneye ihtiyaç duyduğumuzda bu betiğe ekleme yapacağız.
Şimdi bu temel kurulumu yaptıktan sonra, aşağıdaki adımlarla devam edeceğiz:
Borsa İstanbul Hisse Verilerinin Çekilmesi
yfinance kütüphanesini kullanarak BIST hisselerine ait günlük kapanış verilerini çekeceğiz. (2 Ocak 2020 tarihinden itibaren)
Verilerin Excel’e Aktarılması
pandas ve openpyxl kütüphaneleri sayesinde çekilen veriler .xlsx formatında saklanacak.
Teknik Göstergeler ve Sinyallerin Hesaplanması
EMA, EMA ALIGNMENT (İdeal Ema, Ema Sıralama), WaveTrend, Günlük Haftalık Aylık Yıllık Pivot, Lineer Regresyon kanalları gibi göstergeleri hesaplayıp, yatırım kararlarında kullanılabilecek potansiyel al/sat sinyallerinide analiz edeceğiz.
Sonuç olarak bu script, Python tabanlı finansal veri analizlerine başlamadan önce gerekli kütüphaneleri yükleyerek ortam hazırlığını otomatikleştirir. Böylece kullanıcılar doğrudan veri çekme ve analiz etme adımlarına geçebilirler.
Python, günümüzün en popüler programlama dillerinden biridir. Veri bilimi, yapay zeka, web geliştirme, otomasyon ve daha birçok alanda yaygın olarak kullanılır. Bu makalede, Python’u bilgisayarınıza nasıl kuracağınızı ve PyCharm adlı güçlü bir geliştirme ortamını (IDE) nasıl kullanmaya başlayacağınızı adım adım öğreneceksiniz.
Python Kurulumu
Adım 1: Python Web Sitesine Gidin
Tarayıcınızda https://www.python.org adresine gidin.
Adım 2: Python Sürümünü İndirin
Ana sayfadaki “Download Python X.X.X” (örneğin 3.13.5) butonuna tıklayın. İşletim sisteminiz otomatik olarak algılanır (Windows, macOS veya Linux).
Adım 3: Kurulum Dosyasını Çalıştırın
İndirilen .exe dosyasını çalıştırın.
Kurulum ekranında mutlaka “Add Python to PATH” seçeneğini işaretleyin.
Ardından “Install Now” seçeneğine tıklayın.
Adım 4: Kurulumu Doğrulayın
Başlat Menüsü > Komut İstemi’ni (cmd) açın ve şu komutu yazın:
PyCharm Kurulumu
PyCharm, Python için özel olarak geliştirilmiş bir IDE’dir. Kod yazmayı, çalıştırmayı, hata ayıklamayı ve projeleri yönetmeyi kolaylaştırır.
Adım 1: PyCharm Web Sitesine Gidin
https://www.jetbrains.com/pycharm adresini ziyaret edin.
Adım 2: Sürüm Seçimi
İki farklı sürüm vardır:
Community (Ücretsiz) – Başlangıç için yeterlidir.
Professional (Ücretli) – Web geliştirme, veri bilimi gibi ek özellikler içerir.
Community Edition’ı indirin.
Adım 3: Kurulumu Başlatın
İndirilen kurulum dosyasını açın ve yönergeleri takip ederek PyCharm’ı kurun.
Kurulum sırasında aşağıdaki seçenekleri işaretleyebilirsiniz:
64-bit launcher ekle
.py dosyalarını PyCharm ile ilişkilendirin
Adım 4: İlk Çalıştırma
Kurulum tamamlandıktan sonra PyCharm’ı başlatın ve tema (açık/koyu) gibi başlangıç ayarlarını yapın.
İlk Python Projeni Oluşturma
PyCharm’ı açın ve “New Project” seçeneğini seçin.
Projeye bir ad verin (örneğin: ilk_proje).
Python interpreter (yürütücü) olarak daha önce kurduğunuz Python sürümünü seçin. (Mesela venv)
main.py adlı bir dosya oluşturun ve şu kodu yazın:
print(“Merhaba, Python dünyası!”)
Sağ üstteki yeşil “Run” (Çalıştır) butonuna tıklayarak kodunuzu çalıştırın.
Ek Tavsiyeler
PyCharm kısayollarına zamanla alışmak size büyük hız kazandırır.
Terminal kullanarak pip komutu ile paketler kurabilirsiniz.
Örneğin: pip install numpy
Virtual environment (sanallaştırılmış çalışma ortamı) kullanmak projelerinizi izole tutar ve karışıklığı önler.
İzleyen makalelerde, Python kullanarak Yahoo Finance üzerinden BIST Spot hisse senetlerinin kapanış verilerini nasıl çekeceğimizi adım adım öğreneceğiz. Elde ettiğimiz bu verileri Excel formatında kaydederek, şu analizleri gerçekleştireceğiz:
Farklı periyotlara göre EMA (Üssel Hareketli Ortalama) hesaplamaları
Osilatör ve momentum göstergeleri
EMA Alignment analizi (ideal dizilim ve sapma durumları)
WaveTrend tabanlı sinyal üretimi
Belirli periyotlarda Doğrusal Regresyon (Linear Regression Channel) hesaplamaları
Her hisse için bu analizlerin Pearson korelasyon katsayısı ile değerlendirilmesi
Tüm bu analiz sonuçlarını Python ile tasarlanmış Excel şablonlarına aktararak, hisse senetlerinin teknik durumlarını görsel olarak kolayca takip edebileceğiniz tarama dosyaları oluşturabileceksiniz.
Son Yorumlar