import pandas as pd import config as cfg import json # Используем json вместо pandas.io.json для большей стандартности def get_target_signal(current_close, next_day_close, threshold): """ Определяет сигнал (BUY, SELL, HOLD) на основе next_day_close. """ if pd.isna(next_day_close) or pd.isna(current_close) or current_close == 0: return "HOLD" # Недостаточно данных или некорректные данные price_change_ratio = (next_day_close - current_close) / current_close if price_change_ratio > threshold: return "BUY" elif price_change_ratio < -threshold: return "SELL" else: return "HOLD" def format_input_for_llm(row, all_indicator_columns): """ Форматирует текущее состояние рынка и индикаторы в текстовый промпт для LLM. """ # Базовая информация prompt_parts = [ f"Current open: {row['open']:.2f}", f"high: {row['high']:.2f}", f"low: {row['low']:.2f}", f"close: {row['close']:.2f}", f"volume: {row['volume']:.0f}." ] # Добавляем все остальные индикаторы indicator_descs = [] for col in all_indicator_columns: # Исключаем уже добавленные и целевую переменную if col not in ['date', 'open', 'high', 'low', 'close', 'volume', 'next_day_close'] and col in row and pd.notna(row[col]): # Убираем префиксы rsi_, cci_, sma_, ema_, atr_ для более естественного языка, если хотим # cleaned_col_name = col.replace("rsi_", "RSI ").replace("cci_", "CCI ").replace("sma_", "SMA ").replace("ema_", "EMA ").replace("atr_", "ATR ") # indicator_descs.append(f"{cleaned_col_name.replace('_', ' ')}: {row[col]:.2f}") indicator_descs.append(f"{col.replace('_', ' ')}: {row[col]:.2f}") # Простой вариант if indicator_descs: prompt_parts.append("Technical indicators: " + ", ".join(indicator_descs) + ".") return " ".join(prompt_parts) def main(): try: df = pd.read_csv(cfg.CSV_FILE_PATH) except FileNotFoundError: print(f"Ошибка: CSV файл не найден по пути: {cfg.CSV_FILE_PATH}") print("Пожалуйста, убедитесь, что файл существует и путь в config.py указан верно.") return # Преобразуем 'date' в datetime и установим как индекс if 'date' not in df.columns: print("Ошибка: колонка 'date' отсутствует в CSV файле.") return df['date'] = pd.to_datetime(df['date']) df.set_index('date', inplace=True) df.sort_index(inplace=True) # Убедимся, что данные отсортированы по времени # Получаем список всех колонок-индикаторов (кроме date и next_day_close) # 'open', 'high', 'low', 'close', 'volume' будут обработаны отдельно в format_input_for_llm all_feature_columns = [col for col in df.columns if col not in ['next_day_close']] # Удаляем строки, где есть NaN в фичах или в 'close'/'next_day_close' # (кроме 'next_day_close' для последней строки, это обработается в цикле) df.dropna(subset=[col for col in all_feature_columns if col != 'next_day_close'], inplace=True) # Удаляем NaN в фичах # df.dropna(subset=['close', 'next_day_close'], inplace=True) # Удаляем если нет таргета или текущей цены if df.empty: print("Датафрейм пуст после удаления NaN. Проверьте данные.") return print(f"Количество строк после начальной обработки: {len(df)}") processed_data = [] # Идем почти до конца, так как для последней строки может не быть next_day_close # или next_day_close может быть NaN, что обработает get_target_signal for i in range(len(df)): current_row = df.iloc[i] # Проверяем, есть ли next_day_close для текущей строки (особенно актуально для последней строки файла) if 'next_day_close' not in current_row or pd.isna(current_row['next_day_close']): if i == len(df) - 1: # Если это последняя строка и нет next_day_close, просто пропускаем ее print(f"Пропускаем последнюю строку {current_row.name}, т.к. отсутствует 'next_day_close'.") continue else: # Если это не последняя строка, но 'next_day_close' NaN, это странно, но get_target_signal вернет HOLD pass market_description = format_input_for_llm(current_row, all_feature_columns) current_close_price = current_row['close'] next_day_close_price = current_row['next_day_close'] signal = get_target_signal(current_close_price, next_day_close_price, cfg.PRICE_CHANGE_THRESHOLD_SIGNAL) # Формат для SFTTrainer formatted_text = f"[INST] Анализ рынка BTC/USDT на основе следующих данных: {market_description} Какое торговое действие (BUY, SELL, или HOLD) следует предпринять? [/INST] {signal}" processed_data.append({"text": formatted_text}) # Сохраняем в формате JSONL with open(cfg.TRAINING_DATA_JSONL, 'w', encoding='utf-8') as f: for item in processed_data: f.write(json.dumps(item) + '\n') print(f"Данные подготовлены и сохранены в {cfg.TRAINING_DATA_JSONL}. Количество примеров: {len(processed_data)}") if processed_data: print("Пример первой строки данных для обучения:") print(processed_data[0]['text']) else: print("Не было сгенерировано ни одного примера для обучения. Проверьте логику и данные.") if __name__ == "__main__": main()