Spaces:
Sleeping
Sleeping
| 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"<s>[INST] Анализ рынка BTC/USDT на основе следующих данных: {market_description} Какое торговое действие (BUY, SELL, или HOLD) следует предпринять? [/INST] {signal}</s>" | |
| 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() |