import sqlite3 import os import logging import pandas as pd import json from datetime import datetime import traceback # Loglama yapılandırması logger = logging.getLogger(__name__) # Veritabanı dosya yolu DB_FILE = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'data', 'stock_analysis.db') # Sektör Çeviri Sözlüğü (Basit Başlangıç) SECTOR_TRANSLATIONS = { "Financials": "Finans", "Industrials": "Sanayi", "Technology": "Teknoloji", "Consumer Cyclical": "Tüketim - Döngüsel", "Consumer Defensive": "Tüketim - Savunma", "Energy": "Enerji", "Basic Materials": "Temel Malzemeler", "Utilities": "Kamu Hizmetleri", "Communication Services": "İletişim Hizmetleri", "Real Estate": "Gayrimenkul", "Healthcare": "Sağlık", "Aviation": "Havacılık", "Banking": "Bankacılık", "Insurance": "Sigortacılık", "Chemicals": "Kimya", "Holding": "Holding", "Conglomerates": "Holding ve Yatırım Şirketleri", # Örnek # ... Diğer sektörler eklenebilir } def create_database(): """ SQLite veritabanı şemasını oluşturur Returns: bool: İşlem başarılıysa True, değilse False """ try: logger.info("Veritabanı oluşturuluyor: " + DB_FILE) conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() # ML Tahminleri tablosu cursor.execute(''' CREATE TABLE IF NOT EXISTS ml_predictions ( id INTEGER PRIMARY KEY AUTOINCREMENT, symbol TEXT NOT NULL, prediction_date TEXT NOT NULL, target_date TEXT, current_price REAL NOT NULL, prediction_percentage REAL NOT NULL, confidence_score REAL NOT NULL, prediction_result TEXT NOT NULL, model_type TEXT NOT NULL, features_used TEXT, was_correct INTEGER DEFAULT -1, actual_result REAL, verified_date TEXT ) ''') # Kullanıcı notları tablosu cursor.execute(''' CREATE TABLE IF NOT EXISTS user_notes ( id INTEGER PRIMARY KEY AUTOINCREMENT, symbol TEXT NOT NULL, note_date TEXT NOT NULL, note_text TEXT NOT NULL ) ''') # Analiz sonuçları tablosu cursor.execute(''' CREATE TABLE IF NOT EXISTS analysis_results ( id INTEGER PRIMARY KEY AUTOINCREMENT, symbol TEXT NOT NULL, analysis_type TEXT NOT NULL, analysis_date TEXT NOT NULL, price REAL NOT NULL, result_data TEXT NOT NULL, indicators TEXT, notes TEXT ) ''') # Favoriler tablosu cursor.execute(''' CREATE TABLE IF NOT EXISTS favorite_stocks ( id INTEGER PRIMARY KEY AUTOINCREMENT, symbol TEXT UNIQUE NOT NULL, added_date TEXT NOT NULL ) ''') # Duyurular tablosu cursor.execute(''' CREATE TABLE IF NOT EXISTS announcements ( id INTEGER PRIMARY KEY AUTOINCREMENT, announcement_date TEXT NOT NULL, title TEXT NOT NULL, content TEXT NOT NULL, is_important INTEGER DEFAULT 0 ) ''') # Portföy tablosu cursor.execute(''' CREATE TABLE IF NOT EXISTS portfolio ( id INTEGER PRIMARY KEY AUTOINCREMENT, symbol TEXT NOT NULL, purchase_date TEXT NOT NULL, quantity REAL NOT NULL, purchase_price REAL NOT NULL, notes TEXT, sector TEXT, is_active INTEGER DEFAULT 1, target_price REAL, stop_loss REAL ) ''') # Portföy işlemleri tablosu cursor.execute(''' CREATE TABLE IF NOT EXISTS portfolio_transactions ( id INTEGER PRIMARY KEY AUTOINCREMENT, symbol TEXT NOT NULL, transaction_date TEXT NOT NULL, transaction_type TEXT NOT NULL, quantity REAL NOT NULL, price REAL NOT NULL, commission REAL DEFAULT 0, total_amount REAL NOT NULL, notes TEXT ) ''') # Hisse Senedi Bilgileri Tablosu cursor.execute(''' CREATE TABLE IF NOT EXISTS stock_info ( symbol TEXT PRIMARY KEY, name TEXT, sector_en TEXT, sector_tr TEXT, last_updated TEXT ) ''') # ML Modelleri Tablosu - eğer yoksa oluştur cursor.execute(''' CREATE TABLE IF NOT EXISTS ml_models ( id INTEGER PRIMARY KEY AUTOINCREMENT, symbol TEXT NOT NULL, model_type TEXT NOT NULL, model_data BLOB NOT NULL, features_used TEXT, training_date TEXT NOT NULL, last_update_date TEXT NOT NULL, performance_metrics TEXT, is_active INTEGER DEFAULT 1, model_version TEXT ) ''') # ml_models tablosunun varlığını kontrol et cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='ml_models'") if cursor.fetchone() is None: # Tablo yoksa tekrar oluşturma denemesi logger.warning("ml_models tablosu oluşturulamadı. Tekrar deneniyor...") cursor.execute(''' CREATE TABLE IF NOT EXISTS ml_models ( id INTEGER PRIMARY KEY AUTOINCREMENT, symbol TEXT NOT NULL, model_type TEXT NOT NULL, model_data BLOB NOT NULL, features_used TEXT, training_date TEXT NOT NULL, last_update_date TEXT NOT NULL, performance_metrics TEXT, is_active INTEGER DEFAULT 1, model_version TEXT ) ''') # Tekrar kontrol et cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='ml_models'") if cursor.fetchone() is None: logger.error("ml_models tablosu ikinci denemede de oluşturulamadı!") else: logger.info("ml_models tablosu ikinci denemede başarıyla oluşturuldu.") conn.commit() conn.close() logger.info("Veritabanı başarıyla oluşturuldu") return True except Exception as e: logger.error(f"Veritabanı oluşturulurken hata: {str(e)}") logger.error(traceback.format_exc()) return False def save_favorite_stock(symbol): """ Bir hisse senedini favorilere ekler Args: symbol (str): Hisse senedi sembolü Returns: bool: İşlem başarılıysa True, değilse False """ try: conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() # Sembolü düzenle symbol = symbol.upper().strip() # Mevcut kayıt kontrolü cursor.execute("SELECT * FROM favorite_stocks WHERE symbol = ?", (symbol,)) if cursor.fetchone(): logger.info(f"{symbol} zaten favorilerde") conn.close() return True # Yeni kayıt ekle now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") cursor.execute("INSERT INTO favorite_stocks (symbol, added_date) VALUES (?, ?)", (symbol, now)) conn.commit() conn.close() logger.info(f"{symbol} favorilere eklendi") return True except Exception as e: logger.error(f"Favori eklenirken hata: {str(e)}") logger.error(traceback.format_exc()) return False def remove_favorite_stock(symbol): """ Bir hisse senedini favorilerden çıkarır Args: symbol (str): Hisse senedi sembolü Returns: bool: İşlem başarılıysa True, değilse False """ try: conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() # Sembolü düzenle symbol = symbol.upper().strip() # Silme işlemi cursor.execute("DELETE FROM favorite_stocks WHERE symbol = ?", (symbol,)) conn.commit() conn.close() logger.info(f"{symbol} favorilerden çıkarıldı") return True except Exception as e: logger.error(f"Favori çıkarılırken hata: {str(e)}") logger.error(traceback.format_exc()) return False def get_favorite_stocks(): """ Favori hisse senetlerini getirir Returns: list: Favori hisse sembollerinin listesi """ try: conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() cursor.execute("SELECT symbol FROM favorite_stocks ORDER BY added_date DESC") results = cursor.fetchall() conn.close() return [r[0] for r in results] except Exception as e: logger.error(f"Favoriler getirilirken hata: {str(e)}") logger.error(traceback.format_exc()) return [] def is_favorite_stock(symbol): """ Bir hisse senedinin favorilerde olup olmadığını kontrol eder Args: symbol (str): Hisse senedi sembolü Returns: bool: Favorilerdeyse True, değilse False """ try: conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() # Sembolü düzenle symbol = symbol.upper().strip() cursor.execute("SELECT * FROM favorite_stocks WHERE symbol = ?", (symbol,)) result = cursor.fetchone() is not None conn.close() return result except Exception as e: logger.error(f"Favori kontrolü sırasında hata: {str(e)}") logger.error(traceback.format_exc()) return False def save_analysis_result(symbol, analysis_type, price, result_data, indicators=None, notes=None): """ Bir hisse senedi için analiz sonucunu kaydeder Args: symbol (str): Hisse senedi sembolü analysis_type (str): Analiz tipi (teknik, temel, ml vb.) price (float): Analiz sırasındaki fiyat result_data (dict): Analiz sonuç verisi indicators (dict, optional): Teknik göstergeler notes (str, optional): Notlar Returns: bool: İşlem başarılıysa True, değilse False """ try: conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() # Sembolü düzenle symbol = symbol.upper().strip() # JSON'a dönüştür result_json = json.dumps(result_data, ensure_ascii=False) indicators_json = json.dumps(indicators, ensure_ascii=False) if indicators else None # Tarih now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") # Veriyi ekle cursor.execute(""" INSERT INTO analysis_results (symbol, analysis_type, analysis_date, price, result_data, indicators, notes) VALUES (?, ?, ?, ?, ?, ?, ?) """, (symbol, analysis_type, now, price, result_json, indicators_json, notes)) conn.commit() conn.close() logger.info(f"{symbol} için {analysis_type} analiz sonucu kaydedildi") return True except Exception as e: logger.error(f"Analiz sonucu kaydedilirken hata: {str(e)}") logger.error(traceback.format_exc()) return False def get_analysis_results(symbol=None, analysis_type=None, limit=20): """ Analiz sonuçlarını getirir Args: symbol (str, optional): Hisse senedi sembolü analysis_type (str, optional): Analiz tipi limit (int, optional): Maksimum sonuç sayısı Returns: list: Analiz sonuçlarının listesi """ try: conn = sqlite3.connect(DB_FILE) conn.row_factory = sqlite3.Row # Sonuçları dict olarak almak için cursor = conn.cursor() query = "SELECT * FROM analysis_results" params = [] # Filtreler conditions = [] if symbol: conditions.append("symbol = ?") params.append(symbol.upper().strip()) if analysis_type: conditions.append("analysis_type = ?") params.append(analysis_type) if conditions: query += " WHERE " + " AND ".join(conditions) query += " ORDER BY analysis_date DESC LIMIT ?" params.append(limit) cursor.execute(query, params) results = cursor.fetchall() # Sonuçları dict listesine dönüştür output = [] for row in results: item = dict(row) # JSON alanları parse et item['result_data'] = json.loads(item['result_data']) if item['result_data'] else {} item['indicators'] = json.loads(item['indicators']) if item['indicators'] else {} output.append(item) conn.close() return output except Exception as e: logger.error(f"Analiz sonuçları getirilirken hata: {str(e)}") logger.error(traceback.format_exc()) return [] def save_ml_prediction(symbol, current_price, prediction_percentage, confidence_score, prediction_result, model_type, features_used, target_date=None): """ ML tahmin sonucunu kaydeder Args: symbol (str): Hisse senedi sembolü current_price (float): Mevcut fiyat prediction_percentage (float): Tahmin edilen yüzde değişim confidence_score (float): Güven puanı prediction_result (str): Tahmin sonucu model_type (str): Model tipi features_used (list): Kullanılan özellikler target_date (str, optional): Hedef tarih Returns: bool: İşlem başarılıysa True, değilse False """ try: conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() # Sembolü düzenle symbol = symbol.upper().strip() # Tarihleri ayarla now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") # JSON'a dönüştür features_json = json.dumps(features_used, ensure_ascii=False) # Veriyi ekle cursor.execute(""" INSERT INTO ml_predictions (symbol, prediction_date, target_date, current_price, prediction_percentage, confidence_score, prediction_result, model_type, features_used) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) """, (symbol, now, target_date, current_price, prediction_percentage, confidence_score, prediction_result, model_type, features_json)) conn.commit() conn.close() logger.info(f"{symbol} için ML tahmin sonucu kaydedildi") return True except Exception as e: logger.error(f"ML tahmin sonucu kaydedilirken hata: {str(e)}") logger.error(traceback.format_exc()) return False def get_ml_predictions(symbol=None, limit=50, include_verified=False): """ ML tahmin sonuçlarını getirir Args: symbol (str, optional): Hisse senedi sembolü limit (int, optional): Maksimum sonuç sayısı include_verified (bool, optional): Doğrulanmış sonuçları da dahil et Returns: list: ML tahmin sonuçlarının listesi """ try: conn = sqlite3.connect(DB_FILE) conn.row_factory = sqlite3.Row cursor = conn.cursor() query = "SELECT * FROM ml_predictions" params = [] # Filtreler conditions = [] if symbol: conditions.append("symbol = ?") params.append(symbol.upper().strip()) if not include_verified: conditions.append("was_correct = -1") if conditions: query += " WHERE " + " AND ".join(conditions) query += " ORDER BY prediction_date DESC LIMIT ?" params.append(limit) cursor.execute(query, params) results = cursor.fetchall() # Sonuçları dict listesine dönüştür output = [] for row in results: item = dict(row) # JSON alanları parse et item['features_used'] = json.loads(item['features_used']) if item['features_used'] else [] output.append(item) conn.close() return output except Exception as e: logger.error(f"ML tahmin sonuçları getirilirken hata: {str(e)}") logger.error(traceback.format_exc()) return [] def update_ml_prediction_result(prediction_id, actual_result, was_correct): """ ML tahmin sonucunu günceller (gerçekleşen sonuçla) Args: prediction_id (int): Tahmin ID'si actual_result (float): Gerçekleşen değer was_correct (int): Doğru olup olmadığı (1:doğru, 0:yanlış) Returns: bool: İşlem başarılıysa True, değilse False """ try: conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() cursor.execute(""" UPDATE ml_predictions SET actual_result = ?, was_correct = ? WHERE id = ? """, (actual_result, was_correct, prediction_id)) conn.commit() conn.close() logger.info(f"ML tahmin ID {prediction_id} güncellendi") return True except Exception as e: logger.error(f"ML tahmin sonucu güncellenirken hata: {str(e)}") logger.error(traceback.format_exc()) return False def save_user_note(symbol, note_text): """ Kullanıcı notunu kaydeder Args: symbol (str): Hisse senedi sembolü note_text (str): Not metni Returns: bool: İşlem başarılıysa True, değilse False """ try: conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() # Sembolü düzenle symbol = symbol.upper().strip() # Tarih now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") # Veriyi ekle cursor.execute(""" INSERT INTO user_notes (symbol, note_date, note_text) VALUES (?, ?, ?) """, (symbol, now, note_text)) conn.commit() conn.close() logger.info(f"{symbol} için kullanıcı notu kaydedildi") return True except Exception as e: logger.error(f"Kullanıcı notu kaydedilirken hata: {str(e)}") logger.error(traceback.format_exc()) return False def get_user_notes(symbol=None): """ Kullanıcı notlarını getirir Args: symbol (str, optional): Hisse senedi sembolü Returns: list: Notların listesi """ try: conn = sqlite3.connect(DB_FILE) conn.row_factory = sqlite3.Row cursor = conn.cursor() query = "SELECT * FROM user_notes" params = [] if symbol: query += " WHERE symbol = ?" params.append(symbol.upper().strip()) query += " ORDER BY note_date DESC" cursor.execute(query, params) results = cursor.fetchall() output = [dict(row) for row in results] conn.close() return output except Exception as e: logger.error(f"Kullanıcı notları getirilirken hata: {str(e)}") logger.error(traceback.format_exc()) return [] def save_announcement(title, content, is_important=0): """ Duyuru kaydeder Args: title (str): Duyuru başlığı content (str): Duyuru içeriği is_important (int, optional): Önemli mi? (1:önemli, 0:değil) Returns: bool: İşlem başarılıysa True, değilse False """ try: conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() # Tarih now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") # Veriyi ekle cursor.execute(""" INSERT INTO announcements (announcement_date, title, content, is_important) VALUES (?, ?, ?, ?) """, (now, title, content, is_important)) conn.commit() conn.close() logger.info(f"Duyuru kaydedildi: {title}") return True except Exception as e: logger.error(f"Duyuru kaydedilirken hata: {str(e)}") logger.error(traceback.format_exc()) return False def get_announcements(limit=10): """ Duyuruları getirir Args: limit (int, optional): Maksimum duyuru sayısı Returns: list: Duyuruların listesi """ try: conn = sqlite3.connect(DB_FILE) conn.row_factory = sqlite3.Row cursor = conn.cursor() cursor.execute(""" SELECT * FROM announcements ORDER BY announcement_date DESC LIMIT ? """, (limit,)) results = cursor.fetchall() output = [dict(row) for row in results] conn.close() return output except Exception as e: logger.error(f"Duyurular getirilirken hata: {str(e)}") logger.error(traceback.format_exc()) return [] def delete_announcement(announcement_id): """ Duyuru siler Args: announcement_id (int): Duyuru ID'si Returns: bool: İşlem başarılıysa True, değilse False """ try: conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() cursor.execute("DELETE FROM announcements WHERE id = ?", (announcement_id,)) conn.commit() conn.close() logger.info(f"Duyuru silindi: ID {announcement_id}") return True except Exception as e: logger.error(f"Duyuru silinirken hata: {str(e)}") logger.error(traceback.format_exc()) return False def get_ml_prediction_stats(): """ ML tahmin istatistiklerini hesaplar Returns: dict: İstatistikler """ try: conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() # Toplam tahmin sayısı cursor.execute("SELECT COUNT(*) FROM ml_predictions") total_predictions = cursor.fetchone()[0] # Doğrulanmış tahmin sayısı cursor.execute("SELECT COUNT(*) FROM ml_predictions WHERE was_correct != -1") verified_predictions = cursor.fetchone()[0] # Doğru tahmin sayısı cursor.execute("SELECT COUNT(*) FROM ml_predictions WHERE was_correct = 1") correct_predictions = cursor.fetchone()[0] # Başarı oranı success_rate = 0 if verified_predictions > 0: success_rate = (correct_predictions / verified_predictions) * 100 # En başarılı model cursor.execute(""" SELECT model_type, COUNT(*) as count FROM ml_predictions WHERE was_correct = 1 GROUP BY model_type ORDER BY count DESC LIMIT 1 """) best_model_result = cursor.fetchone() best_model = best_model_result[0] if best_model_result else "Veri yok" conn.close() return { "total_predictions": total_predictions, "verified_predictions": verified_predictions, "correct_predictions": correct_predictions, "success_rate": success_rate, "best_model": best_model } except Exception as e: logger.error(f"ML istatistikleri hesaplanırken hata: {str(e)}") logger.error(traceback.format_exc()) return { "total_predictions": 0, "verified_predictions": 0, "correct_predictions": 0, "success_rate": 0, "best_model": "Hata" } def get_detailed_analysis_history(symbol=None, analysis_type=None, start_date=None, end_date=None, limit=100): """ Detaylı analiz geçmişini döndürür, çeşitli filtreleme seçenekleriyle Args: symbol (str, optional): Hisse senedi sembolü analysis_type (str, optional): Analiz tipi (teknik, temel, ml vb.) start_date (str, optional): Başlangıç tarihi (YYYY-MM-DD formatında) end_date (str, optional): Bitiş tarihi (YYYY-MM-DD formatında) limit (int, optional): Maksimum sonuç sayısı Returns: list: Detaylı analiz sonuçlarının listesi """ try: conn = sqlite3.connect(DB_FILE) conn.row_factory = sqlite3.Row cursor = conn.cursor() query = "SELECT * FROM analysis_results" params = [] # Filtreler conditions = [] if symbol: conditions.append("symbol = ?") params.append(symbol.upper().strip()) if analysis_type: conditions.append("analysis_type = ?") params.append(analysis_type) if start_date: conditions.append("analysis_date >= ?") params.append(start_date + " 00:00:00") if end_date: conditions.append("analysis_date <= ?") params.append(end_date + " 23:59:59") if conditions: query += " WHERE " + " AND ".join(conditions) query += " ORDER BY analysis_date DESC LIMIT ?" params.append(limit) cursor.execute(query, params) results = cursor.fetchall() # Sonuçları dict listesine dönüştür output = [] for row in results: item = dict(row) # JSON alanları parse et item['result_data'] = json.loads(item['result_data']) if item['result_data'] else {} item['indicators'] = json.loads(item['indicators']) if item['indicators'] else {} output.append(item) conn.close() return output except Exception as e: logger.error(f"Detaylı analiz geçmişi getirilirken hata: {str(e)}") logger.error(traceback.format_exc()) return [] def export_analysis_results(symbol, format="csv", analysis_type=None, start_date=None, end_date=None): """ Belirli bir hisse senedi için analiz sonuçlarını dışa aktarır Args: symbol (str): Hisse senedi sembolü format (str): Dışa aktarma formatı ("csv" veya "json") analysis_type (str, optional): Analiz tipi start_date (str, optional): Başlangıç tarihi (YYYY-MM-DD formatında) end_date (str, optional): Bitiş tarihi (YYYY-MM-DD formatında) Returns: str or bytes: CSV string veya JSON string """ try: # Detaylı analiz sonuçlarını al results = get_detailed_analysis_history( symbol=symbol, analysis_type=analysis_type, start_date=start_date, end_date=end_date, limit=1000 ) if not results: return None if format.lower() == "csv": # Düzleştirilmiş veri yapısı oluştur flat_data = [] for result in results: flat_item = { "id": result["id"], "symbol": result["symbol"], "analysis_type": result["analysis_type"], "analysis_date": result["analysis_date"], "price": result["price"], "notes": result["notes"] } # result_data içindeki verileri düzleştir for key, value in result["result_data"].items(): if isinstance(value, (int, float, str, bool)) or value is None: flat_item[f"result_{key}"] = value # indicators içindeki verileri düzleştir for key, value in result["indicators"].items(): if isinstance(value, (int, float, str, bool)) or value is None: flat_item[f"indicator_{key}"] = value flat_data.append(flat_item) # DataFrame'e dönüştür ve CSV'ye çevir df = pd.DataFrame(flat_data) return df.to_csv(index=False) elif format.lower() == "json": # JSON formatına dönüştür return json.dumps(results, ensure_ascii=False, indent=2) else: raise ValueError(f"Desteklenmeyen format: {format}") except Exception as e: logger.error(f"Analiz sonuçları dışa aktarılırken hata: {str(e)}") logger.error(traceback.format_exc()) return None def compare_analysis_results(analysis_id1, analysis_id2): """ İki farklı analiz sonucunu karşılaştırır Args: analysis_id1 (int): Birinci analiz sonucunun ID'si analysis_id2 (int): İkinci analiz sonucunun ID'si Returns: dict: Karşılaştırma sonuçları """ try: conn = sqlite3.connect(DB_FILE) conn.row_factory = sqlite3.Row cursor = conn.cursor() # Her iki analizi de getir cursor.execute("SELECT * FROM analysis_results WHERE id = ?", (analysis_id1,)) analysis1 = cursor.fetchone() cursor.execute("SELECT * FROM analysis_results WHERE id = ?", (analysis_id2,)) analysis2 = cursor.fetchone() conn.close() if not analysis1 or not analysis2: return {"error": "Bir veya her iki analiz bulunamadı"} # Dict'e dönüştür analysis1_dict = dict(analysis1) analysis2_dict = dict(analysis2) # JSON verilerini parse et analysis1_dict["result_data"] = json.loads(analysis1_dict["result_data"]) if analysis1_dict["result_data"] else {} analysis1_dict["indicators"] = json.loads(analysis1_dict["indicators"]) if analysis1_dict["indicators"] else {} analysis2_dict["result_data"] = json.loads(analysis2_dict["result_data"]) if analysis2_dict["result_data"] else {} analysis2_dict["indicators"] = json.loads(analysis2_dict["indicators"]) if analysis2_dict["indicators"] else {} # Farklılıkları hesapla differences = { "basic_info": { "symbol": [analysis1_dict["symbol"], analysis2_dict["symbol"]], "analysis_type": [analysis1_dict["analysis_type"], analysis2_dict["analysis_type"]], "analysis_date": [analysis1_dict["analysis_date"], analysis2_dict["analysis_date"]], "price": [analysis1_dict["price"], analysis2_dict["price"]], "price_change": round((analysis2_dict["price"] - analysis1_dict["price"]) / analysis1_dict["price"] * 100, 2) if analysis1_dict["price"] else None }, "result_data": {}, "indicators": {} } # result_data karşılaştırması all_keys = set(analysis1_dict["result_data"].keys()) | set(analysis2_dict["result_data"].keys()) for key in all_keys: val1 = analysis1_dict["result_data"].get(key) val2 = analysis2_dict["result_data"].get(key) if val1 != val2: differences["result_data"][key] = [val1, val2] # Sayısal değerlerin değişim yüzdesi if isinstance(val1, (int, float)) and isinstance(val2, (int, float)) and val1 != 0: pct_change = round((val2 - val1) / abs(val1) * 100, 2) differences["result_data"][f"{key}_pct_change"] = f"{pct_change}%" # indicators karşılaştırması all_keys = set(analysis1_dict["indicators"].keys()) | set(analysis2_dict["indicators"].keys()) for key in all_keys: val1 = analysis1_dict["indicators"].get(key) val2 = analysis2_dict["indicators"].get(key) if val1 != val2: differences["indicators"][key] = [val1, val2] # Sayısal değerlerin değişim yüzdesi if isinstance(val1, (int, float)) and isinstance(val2, (int, float)) and val1 != 0: pct_change = round((val2 - val1) / abs(val1) * 100, 2) differences["indicators"][f"{key}_pct_change"] = f"{pct_change}%" return differences except Exception as e: logger.error(f"Analiz sonuçları karşılaştırılırken hata: {str(e)}") logger.error(traceback.format_exc()) return {"error": str(e)} def delete_analysis_result(analysis_id): """ Bir analiz sonucunu veritabanından siler Args: analysis_id (int): Silinecek analiz sonucunun ID'si Returns: bool: İşlem başarılıysa True, değilse False """ try: conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() # Silme işlemi cursor.execute("DELETE FROM analysis_results WHERE id = ?", (analysis_id,)) conn.commit() conn.close() logger.info(f"ID: {analysis_id} analiz sonucu silindi") return True except Exception as e: logger.error(f"Analiz sonucu silinirken hata: {str(e)}") logger.error(traceback.format_exc()) return False def update_analysis_price(analysis_id, price): """ Bir analiz sonucunun fiyat değerini günceller Args: analysis_id (int): Güncellenecek analiz sonucunun ID'si price (float): Yeni fiyat değeri Returns: bool: İşlem başarılıysa True, değilse False """ try: conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() # Fiyat değerini güncelle cursor.execute(""" UPDATE analysis_results SET price = ? WHERE id = ? """, (price, analysis_id)) # Aynı zamanda result_data içindeki fiyat bilgisini de güncelle cursor.execute("SELECT result_data FROM analysis_results WHERE id = ?", (analysis_id,)) result = cursor.fetchone() if result and result[0]: result_data = json.loads(result[0]) # Fiyat bilgilerini güncelle if "last_price" in result_data: result_data["last_price"] = price if "current_price" in result_data: result_data["current_price"] = price # Güncellenmiş veriyi kaydet result_data_json = json.dumps(result_data, ensure_ascii=False) cursor.execute(""" UPDATE analysis_results SET result_data = ? WHERE id = ? """, (result_data_json, analysis_id)) conn.commit() conn.close() logger.info(f"Analiz ID: {analysis_id} fiyat değeri {price} olarak güncellendi") return True except Exception as e: logger.error(f"Analiz fiyatı güncellenirken hata: {str(e)}") logger.error(traceback.format_exc()) return False # Portföy işlemleri için fonksiyonlar def add_portfolio_stock(symbol, purchase_date, quantity, purchase_price, notes=None, sector=None, target_price=None, stop_loss=None): """ Portföye yeni bir hisse senedi ekler Args: symbol (str): Hisse senedi sembolü purchase_date (str): Alım tarihi (YYYY-MM-DD) quantity (float): Hisse adedi purchase_price (float): Alım fiyatı notes (str, optional): Notlar sector (str, optional): Sektör target_price (float, optional): Hedef satış fiyatı stop_loss (float, optional): Zarar kesme seviyesi Returns: bool: İşlem başarılıysa True, değilse False """ try: conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() # Sembolü düzenle symbol = symbol.upper().strip() # Veriyi ekle cursor.execute( "INSERT INTO portfolio (symbol, purchase_date, quantity, purchase_price, notes, sector, target_price, stop_loss) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", (symbol, purchase_date, quantity, purchase_price, notes, sector, target_price, stop_loss) ) # İşlem kaydını ekle total_amount = quantity * purchase_price cursor.execute( "INSERT INTO portfolio_transactions (symbol, transaction_date, transaction_type, quantity, price, total_amount, notes) VALUES (?, ?, ?, ?, ?, ?, ?)", (symbol, purchase_date, "ALIŞ", quantity, purchase_price, total_amount, notes) ) conn.commit() conn.close() logger.info(f"{symbol} portföye eklendi. Adet: {quantity}, Fiyat: {purchase_price}") return True except Exception as e: logger.error(f"Portföye hisse eklenirken hata: {str(e)}") logger.error(traceback.format_exc()) return False def update_portfolio_stock(portfolio_id, quantity=None, purchase_price=None, notes=None, target_price=None, stop_loss=None, is_active=None): """ Portföydeki bir hisseyi günceller Args: portfolio_id (int): Portföy kaydının ID'si quantity (float, optional): Yeni hisse adedi purchase_price (float, optional): Yeni alım fiyatı notes (str, optional): Yeni notlar target_price (float, optional): Yeni hedef fiyat stop_loss (float, optional): Yeni zarar kesme seviyesi is_active (int, optional): Aktiflik durumu (1: aktif, 0: pasif) Returns: bool: İşlem başarılıysa True, değilse False """ try: conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() # Mevcut kaydı al cursor.execute("SELECT * FROM portfolio WHERE id = ?", (portfolio_id,)) existing_record = cursor.fetchone() if not existing_record: logger.error(f"Portföy ID'si bulunamadı: {portfolio_id}") conn.close() return False # Güncellenecek alanları belirle update_fields = [] update_values = [] if quantity is not None: update_fields.append("quantity = ?") update_values.append(quantity) if purchase_price is not None: update_fields.append("purchase_price = ?") update_values.append(purchase_price) if notes is not None: update_fields.append("notes = ?") update_values.append(notes) if target_price is not None: update_fields.append("target_price = ?") update_values.append(target_price) if stop_loss is not None: update_fields.append("stop_loss = ?") update_values.append(stop_loss) if is_active is not None: update_fields.append("is_active = ?") update_values.append(is_active) if not update_fields: logger.info("Güncellenecek alan belirtilmedi") conn.close() return True # Güncelleme sorgusu update_query = f"UPDATE portfolio SET {', '.join(update_fields)} WHERE id = ?" update_values.append(portfolio_id) cursor.execute(update_query, update_values) conn.commit() conn.close() logger.info(f"Portföy kaydı güncellendi. ID: {portfolio_id}") return True except Exception as e: logger.error(f"Portföy güncellenirken hata: {str(e)}") logger.error(traceback.format_exc()) return False def delete_portfolio_stock(portfolio_id): """ Portföyden bir hisseyi siler Args: portfolio_id (int): Portföy kaydının ID'si Returns: bool: İşlem başarılıysa True, değilse False """ try: conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() # Kaydı sil cursor.execute("DELETE FROM portfolio WHERE id = ?", (portfolio_id,)) conn.commit() conn.close() logger.info(f"Portföy kaydı silindi. ID: {portfolio_id}") return True except Exception as e: logger.error(f"Portföy kaydı silinirken hata: {str(e)}") logger.error(traceback.format_exc()) return False def get_portfolio_stocks(only_active=True): """ Portföydeki hisseleri getirir Args: only_active (bool): Sadece aktif hisseleri getir Returns: list: Portföy kayıtlarının listesi """ try: conn = sqlite3.connect(DB_FILE) conn.row_factory = sqlite3.Row # Sonuçları sözlük formatında al cursor = conn.cursor() if only_active: cursor.execute("SELECT * FROM portfolio WHERE is_active = 1 ORDER BY symbol") else: cursor.execute("SELECT * FROM portfolio ORDER BY is_active DESC, symbol") results = cursor.fetchall() # Sonuçları sözlük listesine dönüştür portfolio_list = [dict(row) for row in results] conn.close() return portfolio_list except Exception as e: logger.error(f"Portföy getirilirken hata: {str(e)}") logger.error(traceback.format_exc()) return [] def get_portfolio_stock(portfolio_id): """ Belirli bir portföy kaydını getirir Args: portfolio_id (int): Portföy kaydının ID'si Returns: dict: Portföy kaydı veya bulunamazsa None """ try: conn = sqlite3.connect(DB_FILE) conn.row_factory = sqlite3.Row cursor = conn.cursor() cursor.execute("SELECT * FROM portfolio WHERE id = ?", (portfolio_id,)) result = cursor.fetchone() conn.close() if result: return dict(result) return None except Exception as e: logger.error(f"Portföy kaydı getirilirken hata: {str(e)}") logger.error(traceback.format_exc()) return None def add_portfolio_transaction(symbol, transaction_date, transaction_type, quantity, price, commission=0, notes=None): """ Portföye yeni bir işlem ekler (alış/satış) Args: symbol (str): Hisse senedi sembolü transaction_date (str): İşlem tarihi (YYYY-MM-DD) transaction_type (str): İşlem tipi (ALIŞ/SATIŞ) quantity (float): Hisse adedi price (float): İşlem fiyatı commission (float, optional): Komisyon tutarı notes (str, optional): Notlar Returns: bool: İşlem başarılıysa True, değilse False """ try: conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() # Sembolü düzenle symbol = symbol.upper().strip() # Toplam tutarı hesapla total_amount = quantity * price # İşlemi ekle cursor.execute( "INSERT INTO portfolio_transactions (symbol, transaction_date, transaction_type, quantity, price, commission, total_amount, notes) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", (symbol, transaction_date, transaction_type.upper(), quantity, price, commission, total_amount, notes) ) # Eğer bu bir SATIŞ işlemi ise portföydeki hisseyi güncelle if transaction_type.upper() == "SATIŞ": # Portföyde varolan hisseleri kontrol et cursor.execute("SELECT id, quantity FROM portfolio WHERE symbol = ? AND is_active = 1", (symbol,)) portfolio_records = cursor.fetchall() remaining_quantity = quantity for record in portfolio_records: record_id, record_quantity = record if remaining_quantity <= 0: break if record_quantity <= remaining_quantity: # Bu kaydı tamamen sat cursor.execute("UPDATE portfolio SET is_active = 0 WHERE id = ?", (record_id,)) remaining_quantity -= record_quantity else: # Bu kaydın bir kısmını sat new_quantity = record_quantity - remaining_quantity cursor.execute("UPDATE portfolio SET quantity = ? WHERE id = ?", (new_quantity, record_id)) remaining_quantity = 0 conn.commit() conn.close() logger.info(f"Portföy işlemi eklendi. Sembol: {symbol}, İşlem: {transaction_type}, Adet: {quantity}") return True except Exception as e: logger.error(f"Portföy işlemi eklenirken hata: {str(e)}") logger.error(traceback.format_exc()) return False def get_portfolio_transactions(symbol=None, start_date=None, end_date=None, transaction_type=None): """ Portföy işlemlerini getirir Args: symbol (str, optional): Hisse senedi sembolü start_date (str, optional): Başlangıç tarihi (YYYY-MM-DD) end_date (str, optional): Bitiş tarihi (YYYY-MM-DD) transaction_type (str, optional): İşlem tipi (ALIŞ/SATIŞ) Returns: list: İşlem kayıtlarının listesi """ try: conn = sqlite3.connect(DB_FILE) conn.row_factory = sqlite3.Row cursor = conn.cursor() # Sorgu parametreleri query = "SELECT * FROM portfolio_transactions WHERE 1=1" params = [] if symbol: query += " AND symbol = ?" params.append(symbol.upper().strip()) if start_date: query += " AND transaction_date >= ?" params.append(start_date) if end_date: query += " AND transaction_date <= ?" params.append(end_date) if transaction_type: query += " AND transaction_type = ?" params.append(transaction_type.upper()) query += " ORDER BY transaction_date DESC" cursor.execute(query, params) results = cursor.fetchall() # Sonuçları sözlük listesine dönüştür transactions = [dict(row) for row in results] conn.close() return transactions except Exception as e: logger.error(f"Portföy işlemleri getirilirken hata: {str(e)}") logger.error(traceback.format_exc()) return [] def get_portfolio_performance(): """ Portföy performansını hesaplar Returns: dict: Portföy performans bilgileri """ try: conn = sqlite3.connect(DB_FILE) conn.row_factory = sqlite3.Row cursor = conn.cursor() # Aktif portföy hisselerini al cursor.execute("SELECT * FROM portfolio WHERE is_active = 1") stocks = cursor.fetchall() if not stocks: return { "total_investment": 0, "current_value": 0, "total_gain_loss": 0, "total_gain_loss_percentage": 0, "cash": 60.67, # Varsayılan nakit değeri "investment_fund": 15254.17, # Varsayılan yatırım fonu değeri "stocks": [] } from data.stock_data import get_stock_data_cached # Sonuçları sözlük listesine dönüştür stock_list = [] total_investment = 0 total_current_value = 0 for stock in stocks: symbol = stock["symbol"] quantity = stock["quantity"] purchase_price = stock["purchase_price"] # Alım maliyeti investment = quantity * purchase_price # Güncel fiyatı al stock_data = get_stock_data_cached(symbol, period="1d") if not stock_data.empty: # Güncel fiyat ve değer current_price = stock_data['Close'].iloc[-1] current_value = quantity * current_price # Kâr/zarar hesaplama gain_loss = current_value - investment gain_loss_percentage = (gain_loss / investment * 100) if investment > 0 else 0 # Hisse bilgilerini listeye ekle stock_list.append({ "id": stock["id"], "symbol": symbol, "quantity": quantity, "purchase_price": purchase_price, "current_price": current_price, "investment": investment, "current_value": current_value, "gain_loss": gain_loss, "gain_loss_percentage": gain_loss_percentage }) # Toplam değerleri güncelle total_investment += investment total_current_value += current_value else: # Fiyat alınamazsa alım değerini kullan stock_list.append({ "id": stock["id"], "symbol": symbol, "quantity": quantity, "purchase_price": purchase_price, "current_price": purchase_price, "investment": investment, "current_value": investment, "gain_loss": 0, "gain_loss_percentage": 0 }) # Toplam değerleri güncelle total_investment += investment total_current_value += investment # Toplam kâr/zarar total_gain_loss = total_current_value - total_investment total_gain_loss_percentage = (total_gain_loss / total_investment * 100) if total_investment > 0 else 0 # Nakit ve yatırım fonu değerlerini ekle cash_value = 60.67 # Varsayılan nakit değeri investment_fund_value = 15254.17 # Varsayılan yatırım fonu değeri # Portföy sonuçları portfolio_results = { "total_investment": total_investment, "current_value": total_current_value, "total_gain_loss": total_gain_loss, "total_gain_loss_percentage": total_gain_loss_percentage, "cash": cash_value, "investment_fund": investment_fund_value, "stocks": stock_list } conn.close() return portfolio_results except Exception as e: logger.error(f"Portföy performansı hesaplanırken hata: {str(e)}") logger.error(traceback.format_exc()) return { "total_investment": 0, "current_value": 0, "total_gain_loss": 0, "total_gain_loss_percentage": 0, "cash": 60.67, "investment_fund": 15254.17, "stocks": [] } def get_portfolio_sector_distribution(): """ Portföyün sektör dağılımını hesaplar Returns: dict: Sektör dağılımı (sektör adı -> değer) """ try: # Aktif portföy hisselerini al portfolio = get_portfolio_stocks(only_active=True) if not portfolio: return {} from data.stock_data import get_stock_data_cached, get_company_info sector_values = {} unknown_sector_value = 0 for stock in portfolio: symbol = stock["symbol"] quantity = stock["quantity"] sector = stock["sector"] # Sektör bilgisi yoksa şirket bilgisinden al if not sector: company_info = get_company_info(symbol) sector = company_info.get('sector', 'Diğer') # Hala yoksa "Diğer" olarak ayarla if not sector: sector = "Diğer" # Güncel fiyatı al stock_data = get_stock_data_cached(symbol, period="1d") if not stock_data.empty: current_price = stock_data['Close'].iloc[-1] current_value = quantity * current_price if sector in sector_values: sector_values[sector] += current_value else: sector_values[sector] = current_value else: # Fiyat alınamazsa alım değerini kullan purchase_value = quantity * stock["purchase_price"] if sector in sector_values: sector_values[sector] += purchase_value else: sector_values[sector] = purchase_value return sector_values except Exception as e: logger.error(f"Portföy sektör dağılımı hesaplanırken hata: {str(e)}") logger.error(traceback.format_exc()) return {} # --- Stock Info Fonksiyonları --- def get_stock_info_from_db(symbol): """Belirli bir hissenin bilgisini veritabanından alır. Args: symbol (str): Hisse sembolü. Returns: dict: Hisse bilgileri veya None. """ try: conn = sqlite3.connect(DB_FILE) conn.row_factory = sqlite3.Row # Satırları dict gibi erişilebilir yap cursor = conn.cursor() cursor.execute("SELECT symbol, name, sector_en, sector_tr FROM stock_info WHERE symbol = ?", (symbol.upper(),)) result = cursor.fetchone() conn.close() return dict(result) if result else None except Exception as e: logger.error(f"DB'den hisse bilgisi alınırken hata ({symbol}): {str(e)}") return None def save_stock_info_to_db(symbol, name, sector_en, sector_tr=None): """ Hisse senedi bilgilerini veritabanına kaydeder Args: symbol (str): Hisse senedi sembolü name (str): Şirket adı sector_en (str): İngilizce sektör adı sector_tr (str, optional): Türkçe sektör adı Returns: bool: İşlem başarılıysa True, değilse False """ try: conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() # Sembolü düzenle symbol = symbol.upper().strip() # Sektör çevirisi if sector_tr is None: sector_tr = SECTOR_TRANSLATIONS.get(sector_en, sector_en) # Son güncelleme zamanı now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") # Mevcut kayıt kontrolü cursor.execute("SELECT * FROM stock_info WHERE symbol = ?", (symbol,)) if cursor.fetchone(): # Güncelle cursor.execute( """UPDATE stock_info SET name = ?, sector_en = ?, sector_tr = ?, last_updated = ? WHERE symbol = ?""", (name, sector_en, sector_tr, now, symbol) ) else: # Yeni kayıt ekle cursor.execute( "INSERT INTO stock_info (symbol, name, sector_en, sector_tr, last_updated) VALUES (?, ?, ?, ?, ?)", (symbol, name, sector_en, sector_tr, now) ) conn.commit() conn.close() logger.info(f"{symbol} bilgileri veritabanına kaydedildi") return True except Exception as e: logger.error(f"Hisse bilgisi kaydedilirken hata: {str(e)}") return False def get_or_fetch_stock_info(symbol): """ Hisse senedi bilgilerini veritabanından alır, yoksa API'den çekip kaydeder Args: symbol (str): Hisse senedi sembolü Returns: dict: Hisse senedi bilgileri """ try: # Önce veritabanında kontrol et symbol = symbol.upper().strip() stock_info = get_stock_info_from_db(symbol) if stock_info: # Son güncelleme 7 günden eskiyse, yenileme yapma try: # Önce tam format ile deneyin last_updated = datetime.strptime(stock_info.get('last_updated', '2000-01-01'), "%Y-%m-%d %H:%M:%S") except ValueError: # Tam format başarısız olursa, sadece tarih formatını deneyin last_updated = datetime.strptime(stock_info.get('last_updated', '2000-01-01'), "%Y-%m-%d") days_since_update = (datetime.now() - last_updated).days if days_since_update < 7: return stock_info try: # Olmayan veya eskimiş bilgileri API'den al from data.stock_data import get_company_info # API'ye uygun formata çevir (gerekirse) api_symbol = symbol if not symbol.endswith('.IS'): api_symbol = f"{symbol}.IS" company_info = get_company_info(api_symbol) if company_info and company_info.get('name'): # API'den bilgi alındıysa, veritabanına kaydet sector_en = company_info.get('sector', '') name = company_info.get('name', symbol) # Sektör çevirisi yap sector_tr = SECTOR_TRANSLATIONS.get(sector_en, sector_en) # Veritabanına kaydet save_stock_info_to_db(symbol, name, sector_en, sector_tr) # Güncel bilgileri döndür return { 'symbol': symbol, 'name': name, 'sector_en': sector_en, 'sector_tr': sector_tr, 'last_updated': datetime.now().strftime("%Y-%m-%d %H:%M:%S") } except Exception as api_error: logger.error(f"API'den hisse bilgisi alınırken hata: {str(api_error)}") # API'den bilgi alınamazsa veya hata oluşursa, elimizdeki bilgileri döndür if not stock_info: # Hiç bilgi yoksa, basit bir kayıt oluştur sector_tr = "Bilinmiyor" if "BANK" in symbol: sector_tr = "Bankacılık" elif "GMYO" in symbol or "GYO" in symbol: sector_tr = "Gayrimenkul" elif "ENER" in symbol: sector_tr = "Enerji" elif "HOLD" in symbol: sector_tr = "Holding" elif "TEKNO" in symbol: sector_tr = "Teknoloji" elif "METAL" in symbol: sector_tr = "Metal" stock_info = { 'symbol': symbol, 'name': symbol, 'sector_en': '', 'sector_tr': sector_tr, 'last_updated': datetime.now().strftime("%Y-%m-%d %H:%M:%S") } # Basit kaydı veritabanına ekle save_stock_info_to_db(symbol, symbol, '', sector_tr) return stock_info except Exception as e: logger.error(f"Hisse bilgisi alınırken hata: {str(e)}") return { 'symbol': symbol, 'name': symbol, 'sector_en': '', 'sector_tr': 'Bilinmiyor', 'last_updated': datetime.now().strftime("%Y-%m-%d %H:%M:%S") } def save_ml_model(symbol, model_type, model_data, features_used=None, performance_metrics=None, model_version=None): """Eğitilmiş ML modelini veritabanına kaydeder. Args: symbol (str): Hisse sembolü. model_type (str): Model tipi (RandomForest, XGBoost, LightGBM, Ensemble, Hibrit). model_data (bytes): Pickle ile serileştirilmiş model verisi. features_used (list, optional): Kullanılan özellikler. performance_metrics (dict, optional): Model performans metrikleri. model_version (str, optional): Model versiyonu. Belirtilmezse otomatik oluşturulur. Returns: bool: İşlem başarılıysa True, değilse False. """ try: symbol = symbol.upper().strip() now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") features_json = json.dumps(features_used) if features_used else None metrics_json = json.dumps(performance_metrics) if performance_metrics else None conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() # Tablo yapısını kontrol et ve gerekirse model_version sütununu ekle cursor.execute("PRAGMA table_info(ml_models)") columns = cursor.fetchall() column_names = [col[1] for col in columns] if 'model_version' not in column_names: try: cursor.execute("ALTER TABLE ml_models ADD COLUMN model_version TEXT") logger.info("ml_models tablosuna model_version sütunu eklendi") except Exception as e: logger.error(f"model_version sütunu eklenirken hata: {str(e)}") # Model versiyonu oluştur (belirtilmemişse) if not model_version: # Bugünün tarihi ve saati ile versiyon oluştur (YYYYMMDD_HHMMSS formatında) model_version = f"v{datetime.now().strftime('%Y%m%d_%H%M%S')}" # Daha önce kaydedilmiş aynı sembol ve model tipi var mı kontrol et cursor.execute( "SELECT id, model_version FROM ml_models WHERE symbol = ? AND model_type = ? AND is_active = 1", (symbol, model_type) ) existing_model = cursor.fetchone() if existing_model: # Mevcut modeli pasif hale getir (arşivle) cursor.execute(""" UPDATE ml_models SET is_active = 0 WHERE id = ? """, (existing_model[0],)) # Yeni versiyon bilgisini kaydet old_version = existing_model[1] if existing_model[1] else "v1" # Eski versiyon numarasından bir sonraki versiyonu belirle if not model_version: if old_version and old_version.startswith('v'): try: # Versiyon numarasını arttır (örn: v1 -> v2) if '_' in old_version: base_version = old_version.split('_')[0] if base_version[1:].isdigit(): version_num = int(base_version[1:]) + 1 model_version = f"v{version_num}_{datetime.now().strftime('%Y%m%d')}" else: if old_version[1:].isdigit(): version_num = int(old_version[1:]) + 1 model_version = f"v{version_num}" except: model_version = f"v{datetime.now().strftime('%Y%m%d_%H%M%S')}" # Yeni model kaydı oluştur cursor.execute(""" INSERT INTO ml_models (symbol, model_type, model_data, features_used, training_date, last_update_date, performance_metrics, is_active, model_version) VALUES (?, ?, ?, ?, ?, ?, ?, 1, ?) """, (symbol, model_type, model_data, features_json, now, now, metrics_json, model_version)) logger.info(f"{symbol} için {model_type} modeli güncellendi. Yeni versiyon: {model_version}, Eski versiyon: {old_version}") else: # İlk versiyon olarak kaydet if not model_version: model_version = "v1" # Yeni model kaydı oluştur cursor.execute(""" INSERT INTO ml_models (symbol, model_type, model_data, features_used, training_date, last_update_date, performance_metrics, is_active, model_version) VALUES (?, ?, ?, ?, ?, ?, ?, 1, ?) """, (symbol, model_type, model_data, features_json, now, now, metrics_json, model_version)) logger.info(f"{symbol} için {model_type} modeli ilk kez kaydedildi. Versiyon: {model_version}") conn.commit() conn.close() return True except Exception as e: logger.error(f"ML modeli kaydedilirken hata: {str(e)}") logger.error(traceback.format_exc()) return False def load_ml_model(symbol, model_type=None, version=None): """Veritabanından ML modelini yükler. Args: symbol (str): Hisse sembolü. model_type (str, optional): Model tipi. Belirtilmezse tüm modeller döndürülür. version (str, optional): Model versiyonu. Belirtilmezse aktif versiyon kullanılır. Returns: dict veya None: Model verileri içeren sözlük veya işlem başarısızsa None. """ try: symbol = symbol.upper().strip() conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() if model_type: # Belirli bir model tipi için sorgu if version: # Belirli bir versiyon için cursor.execute(""" SELECT model_data, features_used, training_date, last_update_date, performance_metrics, model_type, model_version FROM ml_models WHERE symbol = ? AND model_type = ? AND model_version = ? """, (symbol, model_type, version)) else: # Aktif versiyon için cursor.execute(""" SELECT model_data, features_used, training_date, last_update_date, performance_metrics, model_type, model_version FROM ml_models WHERE symbol = ? AND model_type = ? AND is_active = 1 """, (symbol, model_type)) result = cursor.fetchone() if result: model_data, features_json, training_date, last_update_date, metrics_json, model_type, model_version = result features = json.loads(features_json) if features_json else None metrics = json.loads(metrics_json) if metrics_json else None model_info = { 'model_data': model_data, 'features': features, 'training_date': training_date, 'last_update_date': last_update_date, 'metrics': metrics, 'model_type': model_type, 'model_version': model_version } conn.close() return model_info else: # Tüm model tipleri için sorgu if version: # Belirli bir versiyon için tüm model tipleri cursor.execute(""" SELECT model_data, features_used, training_date, last_update_date, performance_metrics, model_type, model_version FROM ml_models WHERE symbol = ? AND model_version = ? """, (symbol, version)) else: # Aktif olan tüm model tipleri cursor.execute(""" SELECT model_data, features_used, training_date, last_update_date, performance_metrics, model_type, model_version FROM ml_models WHERE symbol = ? AND is_active = 1 """, (symbol,)) results = cursor.fetchall() if results: models = {} for result in results: model_data, features_json, training_date, last_update_date, metrics_json, model_type, model_version = result features = json.loads(features_json) if features_json else None metrics = json.loads(metrics_json) if metrics_json else None models[model_type] = { 'model_data': model_data, 'features': features, 'training_date': training_date, 'last_update_date': last_update_date, 'metrics': metrics, 'model_version': model_version } conn.close() return models conn.close() logger.info(f"{symbol} için {model_type if model_type else 'hiçbir'} model bulunamadı.") return None except Exception as e: logger.error(f"ML modeli yüklenirken hata ({symbol}, {model_type}): {str(e)}") logger.error(traceback.format_exc()) return None def get_model_update_status(symbol, days_threshold=7): """Modelin güncelleme durumunu kontrol eder. Args: symbol (str): Hisse sembolü. days_threshold (int): Modelin güncel sayılması için gün eşiği. Returns: dict: Modellerin güncelleme durumları. """ try: symbol = symbol.upper().strip() now = datetime.now() conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() cursor.execute(""" SELECT model_type, last_update_date FROM ml_models WHERE symbol = ? AND is_active = 1 """, (symbol,)) results = cursor.fetchall() update_status = {} for model_type, last_update_date in results: last_update = datetime.strptime(last_update_date, "%Y-%m-%d %H:%M:%S") days_since_update = (now - last_update).days update_status[model_type] = { 'last_update': last_update_date, 'days_since_update': days_since_update, 'needs_update': days_since_update > days_threshold } conn.close() return update_status except Exception as e: logger.error(f"Model güncelleme durumu kontrol edilirken hata ({symbol}): {str(e)}") logger.error(traceback.format_exc()) return {} def get_model_versions(symbol, model_type=None): """Bir sembol için kayıtlı tüm model versiyonlarını döndürür. Args: symbol (str): Hisse sembolü. model_type (str, optional): Model tipi. Belirtilmezse tüm model tipleri dahil edilir. Returns: dict: Model versiyonlarını içeren sözlük. """ try: symbol = symbol.upper().strip() conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() if model_type: # Belirli bir model tipi için tüm versiyonlar cursor.execute(""" SELECT id, model_type, model_version, training_date, last_update_date, performance_metrics, is_active FROM ml_models WHERE symbol = ? AND model_type = ? ORDER BY training_date DESC """, (symbol, model_type)) else: # Tüm model tipleri için tüm versiyonlar cursor.execute(""" SELECT id, model_type, model_version, training_date, last_update_date, performance_metrics, is_active FROM ml_models WHERE symbol = ? ORDER BY model_type, training_date DESC """, (symbol,)) results = cursor.fetchall() versions = {} for result in results: id, model_type, model_version, training_date, last_update_date, metrics_json, is_active = result metrics = json.loads(metrics_json) if metrics_json else None if model_type not in versions: versions[model_type] = [] version_info = { 'id': id, 'model_version': model_version if model_version else "v1", 'training_date': training_date, 'last_update_date': last_update_date, 'metrics': metrics, 'is_active': bool(is_active) } versions[model_type].append(version_info) conn.close() return versions except Exception as e: logger.error(f"Model versiyonları alınırken hata ({symbol}, {model_type}): {str(e)}") logger.error(traceback.format_exc()) return {} def rollback_model_version(symbol, model_type, version_id=None, version_name=None): """Belirli bir model versiyonunu aktif hale getirir (rollback). Args: symbol (str): Hisse sembolü. model_type (str): Model tipi. version_id (int, optional): Versiyon ID'si. version_name (str, optional): Versiyon adı. Returns: bool: İşlem başarılıysa True, değilse False. """ try: if not version_id and not version_name: logger.error("Rollback için version_id veya version_name belirtilmelidir.") return False symbol = symbol.upper().strip() conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() # Önce mevcut aktif versiyonu bul cursor.execute(""" SELECT id FROM ml_models WHERE symbol = ? AND model_type = ? AND is_active = 1 """, (symbol, model_type)) current_active = cursor.fetchone() # Rollback yapılacak versiyonu bul if version_id: cursor.execute(""" SELECT id FROM ml_models WHERE id = ? AND symbol = ? AND model_type = ? """, (version_id, symbol, model_type)) else: cursor.execute(""" SELECT id FROM ml_models WHERE symbol = ? AND model_type = ? AND model_version = ? """, (symbol, model_type, version_name)) target_version = cursor.fetchone() if not target_version: logger.error(f"Belirtilen versiyon bulunamadı: {version_id or version_name}") conn.close() return False # Mevcut aktif versiyonu pasif yap if current_active: cursor.execute(""" UPDATE ml_models SET is_active = 0 WHERE id = ? """, (current_active[0],)) # Hedef versiyonu aktif yap cursor.execute(""" UPDATE ml_models SET is_active = 1, last_update_date = ? WHERE id = ? """, (datetime.now().strftime("%Y-%m-%d %H:%M:%S"), target_version[0])) conn.commit() conn.close() logger.info(f"{symbol} için {model_type} modeli, versiyon {version_id or version_name}'e geri döndürüldü.") return True except Exception as e: logger.error(f"Model versiyonu geri alma işleminde hata: {str(e)}") logger.error(traceback.format_exc()) return False