
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.
Teknik analizde hareketli ortalamalar, piyasanın genel trendini ve momentum değişimlerini anlamak için kullanılan en temel araçlardan biridir.
Borsapin EMA Sinyal Tablosu, Fibonacci sayı dizisine dayalı EMA (Üstel Hareketli Ortalama) sistemini kullanarak yatırımcılara üç farklı zaman diliminde (kısa, orta, uzun vade) sinyal üreten gelişmiş bir indikatördür.
Fibonacci EMA Serileri
Kısa Vade EMA’lar: 5, 8, 13, 21
En hızlı tepki veren ortalamalar, Günlük işlemler için ideal , Kısa vadeli trend değişimlerini yakalar
Orta Vade EMA’lar: 34, 55
Orta vadeli trendin yönünü belirler, Haftalık strateji geliştiriciler için uygun, Gürültüyü filtreler, daha güvenilir sinyaller üretir
Uzun Vade EMA’lar: 89, 144
Ana trendin yönünü gösterir, Uzun vadeli yatırım kararları için kritik, En az yanıltıcı sinyaller üretir
Kısa Vade Sinyal Sistemi Mantığı
AL Sinyali Başlangıcı: Fiyat tüm EMA’ların (5,8,13,21) üzerinde ve EMA’ların eğimi yukarı yönlü
Sinyal Devamı: EMA 21’in altına geçici sarkmalarda bile, EMA eğimi yukarı yönlü kaldığı sürece AL sinyali bozulmaz
SAT Sinyali: Fiyat EMA 21’in altında ve EMA eğimi aşağı yönlü ise
Orta ve Uzun Vade Sistemlerin Mantığı
AL Sinyali: Fiyat ilgili EMA’ların üzerinde
SAT Sinyali: Fiyat kritik EMA seviyesinin altında (55 ve 89)
Aynı anda üç farklı perspektiften piyasayı değerlendirebilirsiniz. Kısa vadeli gürültüyü uzun vadeli trendden ayırabilirsiniz. Sinyal çakışmalarında güçlü fırsatları belirleyebilirsiniz.
Bar sayacı ile sinyalin ne kadar süredir aktif olduğunu görebilir, Güç göstergesi (⭐) ile sinyalin kalitesini değerlendirme yapabilirsiniz,
Avantajları
Farklı zaman dilimlerinde çelişkili sinyallerde temkinli olmanızı sağlar. Kısa vade sinyaller ile giriş/çıkış noktalarını, Orta-uzun vade sinyalleri ile ana trend yönünün teyidi, Sinyal tarihlerini takip ederek performans analizi yapabilirsiniz. Farklı hisseler için aynı kriterlerde karşılaştırma yapabilir, ayrıca sektörel rotasyonda hangi hisselerin güçlü olduğunu belirleme şansına sahip olabilirsiniz. Varlıklara sistematik yaklaşıp, duygusal karar almanızı engeller. Size Objektif sinyal üretimlerini gösterir.

Dezavantajları ve Riskleri
EMA’lar geçmiş verilerle hesaplandığı için sinyaller gecikmeli gelir, Hızlı piyasa hareketlerinde geç kalabilir. Trend dönüş noktalarında yanıltıcı sinyaller verebilir. Yatay seyreden piyasalarda çok fazla sinyal üretebilir. Whipsaw (sahte sinyal) riski yüksek. Kısa vadede sık giriş-çıkış maliyetleri artabilir. Piyasa sadece teknik faktörlerden etkilenmez. Haberlere, makro ekonomik değişimlere tepki verebilir. Hacim, volatilite gibi faktörleri indikatör hesaplamıyor. Geçmiş verilerle mükemmel çalışan parametreler gelecekte başarısız olabilir. Mesala Fibonacci sayıları her piyasa için optimal olmayabilir. Farklı volatilite ortamlarında farklı sonuçlar verebilir
Uyarılar
Diğer teknik indikatörlerle teyit edin. Hacim analizini ihmal etmeyin, Para Giriş çıkışlarını kontrol edin, Temel analiz ile destekleyin. Yüksek Volatilite Dönemlerinde Özellikle dikkat etmeniz önerilir. Borsapin EMA Sinyal Tablosu, Fibonacci sayı dizisinin doğal uyumunu teknik analize taşıyan, çoklu zaman diliminde objektif sinyaller üreten güçlü bir araçtır. Doğru kullanıldığında yatırımcılara önemli avantajlar sağlarken, limitasyonlarının da farkında olunması gerekmektedir. En önemlisi, hiçbir teknik indikatör tek başına %100 başarılı değildir. Bu sistemi, kapsamlı bir analiz sürecinin bir parçası olarak kullanmak, risk yönetimi kurallarıyla desteklemek ve sürekli performansını takip etmek başarılı sonuçlar için kritik öneme sahiptir. Geçmiş performans gelecekteki sonuçları garanti etmez. Her yatırım kararında kendi risk toleransınızı göz önünde bulundurun ve gerektiğinde profesyonel destek alın.
Trading View Pine Script Kodu
// Bu, Pine Script™ kodu Mozilla Kamu Lisansı 2.0 (MPL-2.0) altında lisanslandı
// © krstsntrk © BorsaPin codeispriority
//@version=6
indicator("BorsaPin Ema Sinyal Tablosu", overlay=true)
// Borsapin Ema Sinyal Tablosu
// Kısa Vade Fibonacci EMA'ları (5, 8, 13, 21)
ema5 = input.int(5, "EMA 5", minval=1, group="Kısa Vade")
ema8 = input.int(8, "EMA 8", minval=1, group="Kısa Vade")
ema13 = input.int(13, "EMA 13", minval=1, group="Kısa Vade")
ema21a = input.int(21, "EMA 21", minval=1, group="Kısa Vade")
// Orta Vade Fibonacci EMA'ları (34, 55)
ema34 = input.int(34, "EMA 34", minval=1, group="Orta Vade")
ema55a = input.int(55, "EMA 55", minval=1, group="Orta Vade")
// Uzun Vade Fibonacci EMA'ları (89, 144)
ema89 = input.int(89, "EMA 89", minval=1, group="Uzun Vade")
ema144 = input.int(144, "EMA 144", minval=1, group="Uzun Vade")
// Görsel ayarlar
showTable = input.bool(true, "Tabloyu Göster")
tablePos = input.string("Sağ Üst", "Tablo Konumu", options=["Sol Üst", "Sağ Üst", "Sol Alt", "Sağ Alt"])
showEMAs = input.bool(true, "EMA Çizgilerini Göster", group="Görsel")
onlyDaily = input.bool(false, "Sadece Günlük Verileri Göster", group="Görsel")
// Günlük Zaman Dilimi Verileri
[dailyClose, dailyFib5, dailyFib8, dailyFib13, dailyFib21a, dailyFib34, dailyFib55a, dailyFib89, dailyFib144] = request.security(syminfo.tickerid, "1D", [close, ta.ema(close, ema5), ta.ema(close, ema8), ta.ema(close, ema13), ta.ema(close, ema21a), ta.ema(close, ema34), ta.ema(close, ema55a), ta.ema(close, ema89), ta.ema(close, ema144)])
// Günlük veriler için sinyal hesaplamaları
dailyEgimYukari = dailyFib5 > dailyFib5[1] and dailyFib8 > dailyFib8[1] and dailyFib13 > dailyFib13[1] and dailyFib21a > dailyFib21a[1]
dailyEgimAsagi = dailyFib21a < dailyFib21a[1] and dailyFib13 < dailyFib13[1]
// Günlük sinyaller
dailySinyalKisaAL = dailyClose > dailyFib5 and dailyClose > dailyFib8 and dailyClose > dailyFib13 and dailyClose > dailyFib21a and dailyEgimYukari
dailySinyalKisaSAT = dailyClose < dailyFib21a and dailyEgimAsagi
dailySinyalOrtaAL = dailyClose > dailyFib34 and dailyClose > dailyFib55a
dailySinyalOrtaSAT = dailyClose < dailyFib55a
dailySinyalUzunAL = dailyClose > dailyFib89 and dailyClose > dailyFib144
dailySinyalUzunSAT = dailyClose < dailyFib144
// Fibonacci EMA Hesapları
fib5 = ta.ema(close, ema5)
fib8 = ta.ema(close, ema8)
fib13 = ta.ema(close, ema13)
fib21a = ta.ema(close, ema21a)
fib34 = ta.ema(close, ema34)
fib55a = ta.ema(close, ema55a)
fib89 = ta.ema(close, ema89)
fib144 = ta.ema(close, ema144)
// Günlük sinyal durumları
var string dailyKisaSinyalDurumu = "BEKLİYOR"
var string dailyOrtaSinyalDurumu = "BEKLİYOR"
var string dailyUzunSinyalDurumu = "BEKLİYOR"
var int dailyKisaBarSayaci = 0
var int dailyOrtaBarSayaci = 0
var int dailyUzunBarSayaci = 0
var string dailyTarihKisa = "Yok"
var string dailyTarihOrta = "Yok"
var string dailyTarihUzun = "Yok"
// Günlük sinyal mantıkları
if dailySinyalKisaAL and dailyKisaSinyalDurumu[1] != "AL"
dailyKisaSinyalDurumu := "AL"
dailyKisaBarSayaci := 1
dailyTarihKisa := str.format("{0,date,dd.MM.yyyy}", time)
else if dailyKisaSinyalDurumu[1] == "AL" and dailyClose > dailyFib21a and dailyEgimYukari
dailyKisaSinyalDurumu := "AL"
dailyKisaBarSayaci += 1
else if dailySinyalKisaSAT
dailyKisaSinyalDurumu := "SAT"
if dailyKisaSinyalDurumu[1] != "SAT"
dailyKisaBarSayaci := 1
dailyTarihKisa := str.format("{0,date,dd.MM.yyyy}", time)
else
dailyKisaBarSayaci += 1
else
if dailyKisaSinyalDurumu[1] == "AL"
dailyKisaSinyalDurumu := "AL"
dailyKisaBarSayaci += 1
else
dailyKisaSinyalDurumu := "BEKLİYOR"
dailyKisaBarSayaci := 0
if dailySinyalOrtaAL
dailyOrtaSinyalDurumu := "AL"
if dailyOrtaSinyalDurumu[1] != "AL"
dailyOrtaBarSayaci := 1
dailyTarihOrta := str.format("{0,date,dd.MM.yyyy}", time)
else
dailyOrtaBarSayaci += 1
else if dailySinyalOrtaSAT
dailyOrtaSinyalDurumu := "SAT"
if dailyOrtaSinyalDurumu[1] != "SAT"
dailyOrtaBarSayaci := 1
dailyTarihOrta := str.format("{0,date,dd.MM.yyyy}", time)
else
dailyOrtaBarSayaci += 1
else
dailyOrtaSinyalDurumu := "BEKLİYOR"
dailyOrtaBarSayaci := 0
if dailySinyalUzunAL
dailyUzunSinyalDurumu := "AL"
if dailyUzunSinyalDurumu[1] != "AL"
dailyUzunBarSayaci := 1
dailyTarihUzun := str.format("{0,date,dd.MM.yyyy}", time)
else
dailyUzunBarSayaci += 1
else if dailySinyalUzunSAT
dailyUzunSinyalDurumu := "SAT"
if dailyUzunSinyalDurumu[1] != "SAT"
dailyUzunBarSayaci := 1
dailyTarihUzun := str.format("{0,date,dd.MM.yyyy}", time)
else
dailyUzunBarSayaci += 1
else
dailyUzunSinyalDurumu := "BEKLİYOR"
dailyUzunBarSayaci := 0
// EMA Çizgileri
plot(showEMAs ? fib5 : na, "EMA 5", color=color.new(color.maroon, 50), linewidth=1)
plot(showEMAs ? fib8 : na, "EMA 8", color=color.new(color.blue, 50), linewidth=1)
plot(showEMAs ? fib13 : na, "EMA 13", color=color.new(color.green, 50), linewidth=1)
plot(showEMAs ? fib21a : na, "EMA 21", color=color.new(color.orange, 30), linewidth=1)
plot(showEMAs ? fib34 : na, "EMA 34", color=color.new(color.purple, 30), linewidth=1)
plot(showEMAs ? fib55a : na, "EMA 55", color=color.new(color.red, 20), linewidth=2)
plot(showEMAs ? fib89 : na, "EMA 89", color=color.new(color.fuchsia, 40), linewidth=2)
plot(showEMAs ? fib144 : na, "EMA 144", color=color.new(color.aqua, 40), linewidth=2)
// KISA VADE Sinyalleri Detaylı Sinyal Mantığı Her Vade İçin Ayrı
// Eğim kontrolü
kisaVadeEgimYukari = fib5 > fib5[1] and fib8 > fib8[1] and fib13 > fib13[1] and fib21a > fib21a[1]
// AL sinyali: Tüm EMA'ların üzerinde ve eğim yukarı
sinyalKisaAL = close > fib5 and close > fib8 and close > fib13 and close > fib21a and kisaVadeEgimYukari
// SAT sinyali: EMA 21'in altına düşmesi ve eğim aşağı
kisaVadeEgimAsagi = fib21a < fib21a[1] and fib13 < fib13[1]
sinyalKisaSAT = close < fib21a and kisaVadeEgimAsagi
// ORTA VADE Sinyalleri (Kısa vade mantığı uygulandı)
sinyalOrtaAL = close > fib34 and close > fib55a and fib34 > fib55a
sinyalOrtaSAT = close < fib55a
// UZUN VADE Sinyalleri (Sadece fiyat kontrolü)
sinyalUzunAL = close > fib89 and close > fib144
sinyalUzunSAT = close < fib144
// Sinyal Durumu Değişkenleri
var string kisaSinyalDurumu = "BEKLİYOR"
var string ortaSinyalDurumu = "BEKLİYOR"
var string uzunSinyalDurumu = "BEKLİYOR"
var int kisaBarSayaci = 0
var int ortaBarSayaci = 0
var int uzunBarSayaci = 0
var string tarihKisa = "Yok"
var string tarihOrta = "Yok"
var string tarihUzun = "Yok"
// KISA VADE Sinyal Mantığı - Sürekli AL Mantığı
if sinyalKisaAL and kisaSinyalDurumu[1] != "AL"
// İlk AL sinyali - tarih ve bar sayacı sıfırla
kisaSinyalDurumu := "AL"
kisaBarSayaci := 1
tarihKisa := str.format("{0,date,dd.MM.yyyy}", time)
else if kisaSinyalDurumu[1] == "AL" and close > fib21a and kisaVadeEgimYukari
// AL sinyali devam ediyor - EMA 21 üzerinde ve eğim yukarı
kisaSinyalDurumu := "AL"
kisaBarSayaci += 1
else if sinyalKisaSAT
// SAT sinyali
kisaSinyalDurumu := "SAT"
if kisaSinyalDurumu[1] != "SAT"
kisaBarSayaci := 1
tarihKisa := str.format("{0,date,dd.MM.yyyy}", time)
else
kisaBarSayaci += 1
else
// Belirsizlik durumu
if kisaSinyalDurumu[1] == "AL"
kisaSinyalDurumu := "AL"
kisaBarSayaci += 1
else
kisaSinyalDurumu := "BEKLİYOR"
kisaBarSayaci := 0
// ORTA VADE Sinyal Mantığı
if sinyalOrtaAL
ortaSinyalDurumu := "AL"
if ortaSinyalDurumu[1] != "AL"
ortaBarSayaci := 0
tarihOrta := str.format("{0,date,dd.MM.yyyy} ", time)
else
ortaBarSayaci += 1
else if sinyalOrtaSAT
ortaSinyalDurumu := "SAT"
if ortaSinyalDurumu[1] != "SAT"
ortaBarSayaci := 0
tarihOrta := str.format("{0,date,dd.MM.yyyy} ", time)
else
ortaBarSayaci += 1
else
ortaSinyalDurumu := "BEKLİYOR"
ortaBarSayaci := 0
// UZUN VADE Sinyal Mantığı
if sinyalUzunAL
uzunSinyalDurumu := "AL"
if uzunSinyalDurumu[1] != "AL"
uzunBarSayaci := 0
tarihUzun := str.format("{0,date,dd.MM.yyyy} ", time)
else
uzunBarSayaci += 1
else if sinyalUzunSAT
uzunSinyalDurumu := "SAT"
if uzunSinyalDurumu[1] != "SAT"
uzunBarSayaci := 0
tarihUzun := str.format("{0,date,dd.MM.yyyy}", time)
else
uzunBarSayaci += 1
else
uzunSinyalDurumu := "BEKLİYOR"
uzunBarSayaci := 0
// Tablo konumu belirleme
tablePosition = if tablePos == "Sol Üst"
position.top_left
else if tablePos == "Sağ Üst"
position.top_right
else if tablePos == "Sol Alt"
position.bottom_left
else if tablePos == "Sağ Alt"
position.bottom_right
else
position.top_right
// Tablo Oluşturma
if showTable
var table tablo = table.new(tablePosition, 5, 4, border_width=1, bgcolor=color.white, border_color=color.gray)
// Başlık satırı
table.cell(tablo, 0, 0, "Vade", bgcolor=color.new(color.blue, 80), text_color=color.black, text_size=size.small)
table.cell(tablo, 1, 0, "Sinyal Tarihi", bgcolor=color.new(color.blue, 80), text_color=color.black, text_size=size.small)
table.cell(tablo, 2, 0, "Bar", bgcolor=color.new(color.blue, 80), text_color=color.black, text_size=size.small)
table.cell(tablo, 3, 0, "Durum", bgcolor=color.new(color.blue, 80), text_color=color.black, text_size=size.small)
table.cell(tablo, 4, 0, "Güç", bgcolor=color.new(color.blue, 80), text_color=color.black, text_size=size.small)
// KISA VADE SATIRI
// Hangi veriler kullanılacak
useKisaSinyalDurumu = onlyDaily ? dailyKisaSinyalDurumu : kisaSinyalDurumu
useKisaBarSayaci = onlyDaily ? dailyKisaBarSayaci : kisaBarSayaci
useTarihKisa = onlyDaily ? dailyTarihKisa : tarihKisa
useKisaClose = onlyDaily ? dailyClose : close
useKisaFib5 = onlyDaily ? dailyFib5 : fib5
useKisaFib8 = onlyDaily ? dailyFib8 : fib8
kisaRenk = if useKisaSinyalDurumu == "AL"
if useKisaClose < useKisaFib8 and useKisaClose > useKisaFib5
color.new(color.green, 40) // Orta Güçlü AL
else if useKisaClose < useKisaFib5
color.new(color.green, 60) // Zayıf AL
else
color.new(color.green, 20) // Güçlü AL
else if useKisaSinyalDurumu == "SAT"
color.new(color.red, 20)
else
color.new(color.gray, 40)
kisaDurumText = if useKisaSinyalDurumu == "AL" and useKisaClose < useKisaFib8
"AL (Zayıf)"
else if useKisaSinyalDurumu == "AL" and useKisaClose > useKisaFib8
"AL (Güçlü)"
else
useKisaSinyalDurumu
kisaGuc = if useKisaSinyalDurumu == "AL"
if useKisaBarSayaci <= 5
"⭐⭐⭐"
else if useKisaBarSayaci <= 15
"⭐⭐"
else if useKisaBarSayaci <= 30
"⭐"
else
""
else if useKisaSinyalDurumu == "SAT"
if useKisaBarSayaci <= 5
"⭐⭐⭐"
else if useKisaBarSayaci <= 15
"⭐⭐"
else if useKisaBarSayaci <= 30
"⭐"
else
""
else
"⏳"
kisaBarText = useKisaSinyalDurumu == "BEKLİYOR" ? "" : str.tostring(useKisaBarSayaci)
table.cell(tablo, 0, 1, "Kısa Vade", text_size=size.small)
table.cell(tablo, 1, 1, useTarihKisa, text_size=size.tiny)
table.cell(tablo, 2, 1, kisaBarText, text_size=size.small)
table.cell(tablo, 3, 1, kisaDurumText, bgcolor=kisaRenk, text_color=color.white, text_size=size.small)
table.cell(tablo, 4, 1, kisaGuc, text_size=size.small)
// ORTA VADE SATIRI
ortaRenk = if ortaSinyalDurumu == "AL"
if close < fib34 and close > fib55a
color.new(color.green, 60) // Zayıf AL
else if close > fib34
color.new(color.green, 20) // Güçlü AL
else
color.new(color.green, 40) // Orta AL
else if ortaSinyalDurumu == "SAT"
color.new(color.red, 20)
else
color.new(color.gray, 40)
ortaDurumText = if ortaSinyalDurumu == "AL" and close < fib34
"AL (Zayıf)"
else if ortaSinyalDurumu == "AL" and close > fib34
"AL (Güçlü)"
else
ortaSinyalDurumu
ortaGuc = if ortaSinyalDurumu == "AL"
if ortaBarSayaci <= 5
"⭐⭐⭐"
else if ortaBarSayaci <= 15
"⭐⭐"
else if ortaBarSayaci <= 30
"⭐"
else
""
else if ortaSinyalDurumu == "SAT"
if ortaBarSayaci <= 5
"⭐⭐⭐"
else if ortaBarSayaci <= 15
"⭐⭐"
else if ortaBarSayaci <= 30
"⭐"
else
""
else
"⏳"
ortaBarText = ortaSinyalDurumu == "BEKLİYOR" ? "" : str.tostring(ortaBarSayaci)
table.cell(tablo, 0, 2, "Orta Vade", text_size=size.small)
table.cell(tablo, 1, 2, tarihOrta, text_size=size.tiny)
table.cell(tablo, 2, 2, ortaBarText, text_size=size.small)
table.cell(tablo, 3, 2, ortaDurumText, bgcolor=ortaRenk, text_color=color.white, text_size=size.small)
table.cell(tablo, 4, 2, ortaGuc, text_size=size.small)
// UZUN VADE SATIRI
uzunRenk = if uzunSinyalDurumu == "AL"
if close < fib89 and close > fib144
color.new(color.green, 60) // Zayıf AL
else if close > fib89
color.new(color.green, 20) // Güçlü AL
else
color.new(color.green, 40) // Orta AL
else if uzunSinyalDurumu == "SAT"
color.new(color.red, 20)
else
color.new(color.gray, 40)
uzunDurumText = if uzunSinyalDurumu == "AL" and close < fib89
"AL (Zayıf)"
else if uzunSinyalDurumu == "AL" and close > fib89
"AL (Güçlü)"
else
uzunSinyalDurumu
uzunGuc = if uzunSinyalDurumu == "AL"
if uzunBarSayaci <= 5
"⭐⭐⭐⭐"
else if uzunBarSayaci <= 15
"⭐⭐⭐"
else if uzunBarSayaci <= 30
"⭐⭐"
else if uzunBarSayaci <= 50
"⭐"
else
""
else if uzunSinyalDurumu == "SAT"
if uzunBarSayaci <= 5
"⭐⭐⭐⭐"
else if uzunBarSayaci <= 15
"⭐⭐⭐"
else if uzunBarSayaci <= 30
"⭐⭐"
else if uzunBarSayaci <= 50
"⭐"
else
""
else
"⏳"
uzunBarText = uzunSinyalDurumu == "BEKLİYOR" ? "" : str.tostring(uzunBarSayaci)
table.cell(tablo, 0, 3, "Uzun Vade", text_size=size.small)
table.cell(tablo, 1, 3, tarihUzun, text_size=size.tiny)
table.cell(tablo, 2, 3, uzunBarText, text_size=size.small)
table.cell(tablo, 3, 3, uzunDurumText, bgcolor=uzunRenk, text_color=color.white, text_size=size.small)
table.cell(tablo, 4, 3, uzunGuc, text_size=size.small)
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()

Borsapin WT Sinyal Osilatör, Wave Trend (WT) göstergesine dayalı olarak geliştirilen, alım-satım fırsatlarını görsel olarak tespit etmeye yardımcı olan gelişmiş bir teknik analiz aracıdır. Fiyatın ortalama eğilimlerinden sapmalarını ölçerek aşırı alım ve aşırı satım bölgelerini tanımlar, bu sayede yatırımcıya potansiyel dönüş noktaları hakkında bilgi verir.
Temel Özellikleri:
Wave Trend Çift Çizgi Sistemi (WT1 & WT2):
WT1 ve WT2, fiyatların içsel momentumunu ortaya koymak için iki farklı EMA (Üssel Hareketli Ortalama) ile hesaplanır. Bu çizgiler arasındaki kesişimler, alım ve satım sinyallerini oluşturur.
Aşırı Alım/Satım Seviyeleri:
Kullanıcı tarafından ayarlanabilen seviye çizgileri sayesinde WT2’nin bu bölgeleri geçmesiyle gelen sinyaller daha anlamlı hale gelir.
Aşırı alım: 53 ve 60 üzeri
Aşırı satım: -53 ve -60 altı
Histogram Gösterimi:
WT1 ile WT2 arasındaki fark histogram şeklinde çizilerek momentum gücü görsel olarak analiz edilebilir.
Alım/Satım Sinyalleri:
Al Sinyali: WT1’in WT2’yi yukarı yönde kesmesi
Dipte Al Sinyali: Bu kesişimin WT2, aşırı satım bölgesindeyken gerçekleşmesi
Sat Sinyali: WT2’nin WT1’i yukarıdan kesmesi
Tepede Sat Sinyali: Bu kesişimin WT2, aşırı alım bölgesindeyken gerçekleşmesi
Son Sinyal Tablosu:
Grafik üzerinde son oluşan sinyal ve zaman bilgisi, üst sağ köşede tablo olarak gösterilir.
Neden Kullanılmalı?
Borsapin WT Sinyal Osilatör, sadece klasik WT göstergesinden daha fazlasını sunar. Sinyallerin türünü ve konumunu ayırt etmesi, arka plan renk uyarıları ve tablo ile son sinyali açıkça belirtmesi, karar alma süreçlerini kolaylaştırır. Özellikle dip ve tepe bölgelerinde oluşan sinyaller, yüksek risk-getiri oranına sahip işlemler için avantaj sağlar.
Not: Her teknik gösterge gibi WT Sinyal Osilatör de tek başına kullanılmamalı, hacim, trend yapısı ve diğer teknik araçlarla birlikte değerlendirilmelidir.
Borsapin WT Sinyal Osilatör TradingView Pine Script Kodu
// Bu, Pine Script™ kodu Mozilla Kamu Lisansı 2.0 (MPL-2.0) altında lisanslandı
// © krstsntrk © BorsaPin codeispriority https://www.kursatsenturk.com
// Saraylarda süremem, Dağlarda sürdüğümü. Bin cihana değişmem Şu öksüz Türk'lüğümü... Atsız
//@version=5
indicator("Borsapin WT Sinyal Osilatör", shorttitle="Borsapin WT Sinyal Osilatör", overlay=false)
// Girdi Parametreleri
n1 = input.int(10, title="Kanal Uzunluğu", minval=1, maxval=500)
n2 = input.int(21, title="Ortalama Uzunluğu", minval=1, maxval=500)
obLevel1 = input.int(60, title="Aşırı Alım Seviyesi 1", minval=1, maxval=500)
obLevel2 = input.int(53, title="Aşırı Alım Seviyesi 2", minval=1, maxval=500)
osLevel1 = input.int(-60, title="Aşırı Satım Seviyesi 1", minval=-500, maxval=-1)
osLevel2 = input.int(-53, title="Aşırı Satım Seviyesi 2", minval=-500, maxval=-1)
// Wave Trend Hesaplaması
ap = (high + low + close) / 3
esa = ta.ema(ap, n1)
d = ta.ema(math.abs(ap - esa), n1)
ci = (ap - esa) / (0.015 * d)
tci = ta.ema(ci, n2)
wt1 = tci
wt2 = ta.sma(wt1, 4)
// Wave Trend Çizgilerini Çiz
plot(wt1, color=color.green, title="WT1", linewidth=2)
plot(wt2, color=color.red, title="WT2", linewidth=2)
plot(wt1 - wt2, color=color.blue, title="WT1-WT2", style=plot.style_histogram)
// Seviye Çizgileri
hline(obLevel1, "Aşırı Alım 1", color=color.red, linestyle=hline.style_dashed)
hline(obLevel2, "Aşırı Alım 2", color=color.orange, linestyle=hline.style_dashed)
hline(osLevel1, "Aşırı Satım 1", color=color.green, linestyle=hline.style_dashed)
hline(osLevel2, "Aşırı Satım 2", color=color.lime, linestyle=hline.style_dashed)
hline(0, "Sıfır Çizgisi", color=color.gray, linestyle=hline.style_solid)
// Sinyal Koşulları
buySignal = ta.crossover(wt1, wt2)
buyAtBottom = buySignal and wt2 < osLevel2
sellSignal = ta.crossover(wt2, wt1)
sellAtTop = sellSignal and wt2 > obLevel2
// Sinyal Şekilleri
plotshape(buySignal, title="Al Sinyali", location=location.bottom, color=color.green, style=shape.circle, size=size.tiny)
plotshape(buyAtBottom, title="Dipte Al Sinyali", location=location.bottom, color=color.lime, style=shape.circle, size=size.small)
plotshape(sellSignal, title="Sat Sinyali", location=location.top, color=color.red, style=shape.circle, size=size.tiny)
plotshape(sellAtTop, title="Tepede Sat Sinyali", location=location.top, color=color.maroon, style=shape.circle, size=size.small)
// Arka Plan Rengi
bgcolor(buyAtBottom ? color.new(color.green, 90) : sellAtTop ? color.new(color.red, 90) : na)
// Alarm Koşulları
alertcondition(buySignal, title="Al Sinyali", message="Wave Trend: Al Sinyali")
alertcondition(buyAtBottom, title="Dipte Al", message="Wave Trend: Dipte Al Sinyali")
alertcondition(sellSignal, title="Sat Sinyali", message="Wave Trend: Sat Sinyali")
alertcondition(sellAtTop, title="Tepede Sat", message="Wave Trend: Tepede Sat Sinyali")
// Son Sinyal ve Zaman Bilgisi
var table sigTable = table.new(position.top_right, 2, 2, frame_color=color.gray, border_width=1)
// Tarih formatlaması
yearStr = str.tostring(year)
monthStr = str.tostring(month, "00")
dayStr = str.tostring(dayofmonth, "00")
hourStr = str.tostring(hour, "00")
minuteStr = str.tostring(minute, "00")
newFormattedTime = yearStr + "-" + monthStr + "-" + dayStr + " " + hourStr + ":" + minuteStr
// Son sinyal bilgileri
var string lastSignal = na
var string lastDate = na
if buyAtBottom
lastSignal := "Dipte Al"
lastDate := newFormattedTime
else if buySignal
lastSignal := "Al"
lastDate := newFormattedTime
else if sellAtTop
lastSignal := "Tepede Sat"
lastDate := newFormattedTime
else if sellSignal
lastSignal := "Sat"
lastDate := newFormattedTime
// Tablo
table.cell(sigTable, 0, 0, "Son Sinyal", text_color=color.white, bgcolor=color.gray)
table.cell(sigTable, 1, 0, lastSignal != na ? lastSignal : "Yok", text_color=color.white, bgcolor=color.new(#3192e2, 1))
table.cell(sigTable, 0, 1, "Tarih", text_color=color.white, bgcolor=color.gray)
table.cell(sigTable, 1, 1, lastDate != na ? lastDate : "-", text_color=color.white, bgcolor=color.new(#3192e2, 25))
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.
Trend takibi, finansal piyasalarda başarılı işlem stratejilerinin temelini oluşturur. Bu kapsamda, hareketli ortalamalar (MA) özellikle Exponential Moving Average (EMA), piyasa yönünü ve momentumunu belirlemede sıklıkla tercih edilen araçlardır. “Ideal EMA Dizilim” adlı bu özel gösterge, çoklu EMA’ların sıralanışını analiz ederek trendin gücünü ve netliğini ölçmeyi amaçlıyor. Önceki Makalelerde Python ile kapanış verilerini çektik, o verilerle Ema periyodlarını belirledik sonrasında ise Python ile İdeal Ema sinyallerini oluşturan betiklede bu sinyalleri Excele dökmüştük.
Gösterge, aşağıdaki 10 adet üstel hareketli ortalamayı (EMA) hesaplar:
Bu periyotlar kısa, orta ve uzun vadeli eğilimleri birlikte analiz etmeye olanak tanır.
EMA dizilimleri büyükten küçüğe (azalan) veya küçükten büyüğe (artan) doğru sıralanabilir.
Grafiklerde Gösterim
Bir Önceki Makaledeki Python idealEmaDizilim tarama çalışması dosyasıyla uyumlu çalışır.
Trading View Pine Script İdeal Ema Dizilim İndikatör Kodu
// Bu, Pine Script™ kodu Mozilla Kamu Lisansı 2.0 (MPL-2.0) altında lisanslandı
// © krstsntrk © BorsaPin codeispriority
// Saraylarda süremem, Dağlarda sürdüğümü. Bin cihana değişmem Şu öksüz Türk'lüğümü... Atsız
// @version=6
indicator("BorsaPin IdealEMA Dizilim (EMA ALIGNMENT) ", shorttitle="BorsaPin IdealEMA Dizilim (EMA ALIGNMENT)", overlay=true)
// Ayarlar
show_table = input.bool(true, "Tabloyu Göster", group="Görünüm")
table_position = input.string("Sağ Alt", "Tablo Konumu", options=["Sol Üst", "Orta Üst", "Sağ Üst", "Sol Orta", "Tam Orta", "Sağ Orta", "Sol Alt", "Orta Alt", "Sağ Alt"], group="Görünüm")
table_size = input.string("Küçük", "Tablo Boyutu", options=["Çok Küçük", "Küçük", "Normal", "Büyük"], group="Görünüm")
show_ema_lines = input.bool(true, "EMA Cizgilerini Goster", group="EMA Ayarlari")
show_background = input.bool(true, "Arka Plan Renklerini Goster", group="EMA Ayarlari")
// EMA gorunurluk ayarlari
show_ema5 = input.bool(true, "EMA 5", inline="ema1", group="EMA Cizgileri")
show_ema8 = input.bool(true, "EMA 8", inline="ema1", group="EMA Cizgileri")
show_ema13 = input.bool(true, "EMA 13", inline="ema2", group="EMA Cizgileri")
show_ema21 = input.bool(true, "EMA 21", inline="ema2", group="EMA Cizgileri")
show_ema34 = input.bool(true, "EMA 34", inline="ema3", group="EMA Cizgileri")
show_ema55 = input.bool(true, "EMA 55", inline="ema3", group="EMA Cizgileri")
show_ema89 = input.bool(true, "EMA 89", inline="ema4", group="EMA Cizgileri")
show_ema144 = input.bool(true, "EMA 144", inline="ema4", group="EMA Cizgileri")
show_ema233 = input.bool(true, "EMA 233", inline="ema5", group="EMA Cizgileri")
show_ema370 = input.bool(true, "EMA 370", inline="ema5", group="EMA Cizgileri")
// EMA Periyodları (Python kodundaki ile aynı)
ema5 = ta.ema(close, 5)
ema8 = ta.ema(close, 8)
ema13 = ta.ema(close, 13)
ema21 = ta.ema(close, 21)
ema34 = ta.ema(close, 34)
ema55 = ta.ema(close, 55)
ema89 = ta.ema(close, 89)
ema144 = ta.ema(close, 144)
ema233 = ta.ema(close, 233)
ema370 = ta.ema(close, 370)
// EMA'ları dizi olarak tanımlama
ema_values = array.new<float>()
array.push(ema_values, ema5)
array.push(ema_values, ema8)
array.push(ema_values, ema13)
array.push(ema_values, ema21)
array.push(ema_values, ema34)
array.push(ema_values, ema55)
array.push(ema_values, ema89)
array.push(ema_values, ema144)
array.push(ema_values, ema233)
array.push(ema_values, ema370)
ema_periods = array.new<int>()
array.push(ema_periods, 5)
array.push(ema_periods, 8)
array.push(ema_periods, 13)
array.push(ema_periods, 21)
array.push(ema_periods, 34)
array.push(ema_periods, 55)
array.push(ema_periods, 89)
array.push(ema_periods, 144)
array.push(ema_periods, 233)
array.push(ema_periods, 370)
// İdeal Ema Dizilim hesaplama
// Dizilimin sirali olup olmadigini kontrol etme fonksiyonu
is_descending_order(ema_array) =>
size = array.size(ema_array)
is_desc = true
for i = 0 to size - 2
if array.get(ema_array, i) < array.get(ema_array, i + 1)
is_desc := false
break
is_desc
is_ascending_order(ema_array) =>
size = array.size(ema_array)
is_asc = true
for i = 0 to size - 2
if array.get(ema_array, i) > array.get(ema_array, i + 1)
is_asc := false
break
is_asc
// Maximum ve minimum EMA değerlerini bulma
max_ema = math.max(ema5, math.max(ema8, math.max(ema13, math.max(ema21, math.max(ema34, math.max(ema55, math.max(ema89, math.max(ema144, math.max(ema233, ema370)))))))))
min_ema = math.min(ema5, math.min(ema8, math.min(ema13, math.min(ema21, math.min(ema34, math.min(ema55, math.min(ema89, math.min(ema144, math.min(ema233, ema370)))))))))
// Ideal EMA dizilim kontrolu (Python kodumuzdaki mantik)
ideal_ema_status = ""
if close > max_ema and is_descending_order(ema_values)
ideal_ema_status := "Ideal EMA Yukselis"
else if close < min_ema and is_ascending_order(ema_values)
ideal_ema_status := "Ideal EMA Dusus"
else
ideal_ema_status := "Ideal EMA Notr"
// Potansiyel olusum kontrolu
is_potential = close > min_ema and close < max_ema
// Sinyal degisimi tespiti
var string prev_status = ""
var int signal_bar = 0
var string signal_date = ""
if ideal_ema_status != prev_status
prev_status := ideal_ema_status
signal_bar := bar_index
signal_date := str.tostring(year) + "-" + str.tostring(month, "00") + "-" + str.tostring(dayofmonth, "00")
// Görseller
// EMA cizgileri - sadece secilenleri goster
plot(show_ema_lines and show_ema5 ? ema5 : na, "EMA 5", color=color.new(color.red, 0), linewidth=1)
plot(show_ema_lines and show_ema8 ? ema8 : na, "EMA 8", color=color.new(color.orange, 0), linewidth=1)
plot(show_ema_lines and show_ema13 ? ema13 : na, "EMA 13", color=color.new(color.yellow, 0), linewidth=1)
plot(show_ema_lines and show_ema21 ? ema21 : na, "EMA 21", color=color.new(color.green, 0), linewidth=1)
plot(show_ema_lines and show_ema34 ? ema34 : na, "EMA 34", color=color.new(color.blue, 0), linewidth=1)
plot(show_ema_lines and show_ema55 ? ema55 : na, "EMA 55", color=color.new(color.purple, 0), linewidth=1)
plot(show_ema_lines and show_ema89 ? ema89 : na, "EMA 89", color=color.new(color.gray, 0), linewidth=1)
plot(show_ema_lines and show_ema144 ? ema144 : na, "EMA 144", color=color.new(color.maroon, 0), linewidth=1)
plot(show_ema_lines and show_ema233 ? ema233 : na, "EMA 233", color=color.new(color.navy, 0), linewidth=1)
plot(show_ema_lines and show_ema370 ? ema370 : na, "EMA 370", color=color.new(color.black, 0), linewidth=2)
// Sinyal noktalarini isaretleme - sadece secildiginde goster
signal_color = show_background ? (ideal_ema_status == "Ideal EMA Yukselis" ? color.new(color.green, 90) : ideal_ema_status == "Ideal EMA Dusus" ? color.new(color.red, 90) : is_potential ? color.new(color.yellow, 95) : na) : na
bgcolor(signal_color)
// Tablo
if show_table and barstate.islast
// Tablo pozisyonunu dinamik olarak ayarlama
table_pos = table_position == "Sol Üst" ? position.top_left : table_position == "Orta Üst" ? position.top_center : table_position == "Sağ Üst" ? position.top_right : table_position == "Sol Orta" ? position.middle_left : table_position == "Tam Orta" ? position.middle_center : table_position == "Sağ Orta" ? position.middle_right : table_position == "Sol Alt" ? position.bottom_left : table_position == "Orta Alt" ? position.bottom_center : position.bottom_right
t_size = table_size == "Çok Küçük" ? size.tiny : table_size == "Küçük" ? size.small : table_size == "Büyük" ? size.large : size.normal
// Ana tablo olusturma
info_table = table.new(table_pos, 3, 15, bgcolor=color.new(color.white, 0), border_width=2, border_color=color.new(color.gray, 0))
// Baslik satiri
table.cell(info_table, 0, 0, "EMA DIZILIM ANALIZI", text_color=color.white, bgcolor=color.new(color.blue, 0), text_size=t_size)
table.merge_cells(info_table, 0, 0, 2, 0)
// Mevcut durum
status_color = ideal_ema_status == "Ideal EMA Yukselis" ? color.new(color.green, 70) :
ideal_ema_status == "Ideal EMA Dusus" ? color.new(color.red, 70) :
color.new(color.gray, 70)
table.cell(info_table, 0, 1, "Mevcut Durum:", text_color=color.black, bgcolor=color.new(color.silver, 70), text_size=t_size)
table.cell(info_table, 1, 1, ideal_ema_status, text_color=color.black, bgcolor=status_color, text_size=t_size)
table.cell(info_table, 2, 1, is_potential ? "Potansiyel ✓" : "", text_color=color.black, bgcolor=color.new(color.yellow, 80), text_size=t_size)
// Son sinyal bilgisi
table.cell(info_table, 0, 2, "Son Sinyal:", text_color=color.black, bgcolor=color.new(color.silver, 70), text_size=t_size)
table.cell(info_table, 1, 2, signal_date, text_color=color.black, bgcolor=color.new(color.white, 50), text_size=t_size)
table.cell(info_table, 2, 2, str.tostring(bar_index - signal_bar) + " bar once", text_color=color.black, bgcolor=color.new(color.white, 50), text_size=t_size)
// EMA bilgileri baslik
table.cell(info_table, 0, 3, "EMA", text_color=color.white, bgcolor=color.new(color.navy, 0), text_size=t_size)
table.cell(info_table, 1, 3, "Deger", text_color=color.white, bgcolor=color.new(color.navy, 0), text_size=t_size)
table.cell(info_table, 2, 3, "Kapanis Orani", text_color=color.white, bgcolor=color.new(color.navy, 0), text_size=t_size)
// EMA degerleri ve oranlari
ema_list = array.from(ema5, ema8, ema13, ema21, ema34, ema55, ema89, ema144, ema233, ema370)
ema_names = array.from("EMA 5", "EMA 8", "EMA 13", "EMA 21", "EMA 34", "EMA 55", "EMA 89", "EMA 144", "EMA 233", "EMA 370")
for i = 0 to 9
row = i + 4
ema_val = array.get(ema_list, i)
ema_name = array.get(ema_names, i)
ratio = (close / ema_val - 1) * 100
// Renk belirleme (kapanis fiyatina gore) - pastel renkler
cell_color = close > ema_val ? color.new(color.green, 80) : color.new(color.red, 80)
table.cell(info_table, 0, row, ema_name, text_color=color.black, bgcolor=color.new(color.white, 70), text_size=t_size)
table.cell(info_table, 1, row, str.tostring(math.round(ema_val, 2)), text_color=color.black, bgcolor=cell_color, text_size=t_size)
table.cell(info_table, 2, row, str.tostring(math.round(ratio, 2)) + "%", text_color=color.black, bgcolor=cell_color, text_size=t_size)
// Sinyal degisimi icin alarm
alertcondition(ideal_ema_status != prev_status[1], "Ideal EMA Sinyal Degisimi", "Ideal EMA durumu degisti")
// Spesifik sinyaller icin alarm
alertcondition(ideal_ema_status == "Ideal EMA Yukselis" and prev_status[1] != "Ideal EMA Yukselis", "Ideal EMA Yukselis", "Ideal EMA Yukselis sinyali olustu!")
alertcondition(ideal_ema_status == "Ideal EMA Dusus" and prev_status[1] != "Ideal EMA Dusus", "Ideal EMA Dusus", "Ideal EMA Dusus sinyali olustu!")
// Potansiyel olusum alarmı
alertcondition(is_potential and not is_potential[1], "Potansiyel Olusim", "Ideal EMA potansiyel olusum tespit edildi!")

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.

Ben bu hayatta hiç var olmadım ki zaten…
Ne birinin teninde ısındım,
Ne de bir kelimesinde yer bulabildim kendime.
Ne bir yürek beni anladı,
Ne bir ses adımı gerçekten söyledi.
Sanki bir rüyanın unutulan köşesiydim;
uyanıldığında hatırlanmayan,
bir hayalin eşiğinden bile geçemeyen silik bir iz.
Zaman aktı üzerimden,
ama ben hiçbir saate ait olmadım.
Bir gölgeydim belki;
ışık varsa varım, yoksa hiçim.
Hiçbir el tutmadı beni,
hiçbir bakışta kendimi bulamadım.
Bir gün gidecek olsam,
ardımdan ‘kimdi o?’ bile demez kimse.
Çünkü ben burada değildim aslında —
yalnızca yokluğumun yankısıydım sizin kalabalığınızda
Yazmazsam Unuturum…
Son Yorumlar