Views
No views yet
1
2import sqlite3
3import pandas as pd
4import pandas_ta as ta
5import numpy as np
6import json
7import warnings
8
9# Suppress warnings from pandas_ta when calculating indicators
10warnings.filterwarnings('ignore', category=FutureWarning)
11pd.set_option('mode.chained_assignment', None)
12
13# --- CONFIGURATION ---
14DB_PATH = '/content/gdrive/MyDrive/TradingBotLogs/ohlcv_data_BTC.db'
15TABLE_NAME = 'btcusd_1h_data_12y'
16TIME_COLUMN = 'timestamp' # Name of the column containing the datetime/timestamp
17WINDOW_SIZE = 72
18PREDICTION_HORIZON = 12
19
20# --- STEP 1: LOAD AND INITIALIZE DATA ---
21print(f"--- Attempting to load data from SQLite: {DB_PATH} - Table: {TABLE_NAME} ---")
22try:
23 conn = sqlite3.connect(DB_PATH)
24 query = f"SELECT * FROM {TABLE_NAME}"
25 df = pd.read_sql_query(query, conn)
26 conn.close()
27 print("--- Successfully loaded BTC data from SQLite ---")
28except Exception as e:
29 print(f"Error loading data: {e}")
30 raise
31
32# Ensure column names are lowercased for pandas_ta compatibility
33df.columns = [col.lower() for col in df.columns]
34
35# --- FIX IS HERE ---
36# 1. Convert the string timestamp column to datetime WITHOUT specifying unit='ms'.
37# Pandas will infer the format from the string 'YYYY-MM-DD HH:MM:SS+00:00'.
38df['datetime'] = pd.to_datetime(df[TIME_COLUMN])
39# --------------------
40
41# Set the datetime index and sort
42df = df.set_index('datetime').sort_index()
43
44# Check column names after lowercasing
45print("Columns in loaded data:", df.columns.tolist())
46# Check the index type to confirm conversion
47print("New Index Type:", df.index.dtype)
48
49
50# --- STEP 2: FEATURE ENGINEERING (ADD TECHNICAL INDICATORS) ---
51
52print("--- Calculating Technical Indicators (SMA, EMA, RSI, Log Returns) ---")
53
54# Simple Moving Average (SMA) - 20 periods
55df['sma_20'] = df['close'].rolling(20).mean()
56
57# Exponential Moving Average (EMA) - 50 periods
58df['ema_50'] = df['close'].ewm(span=50, adjust=False).mean()
59
60# Relative Strength Index (RSI) - 14 periods
61df['rsi_14'] = ta.rsi(df['close'], length=14)
62
63# Log Returns (Percentage change in price, then natural log)
64df['log_return'] = df['close'].pct_change()
65# Use np.log1p for numerical stability, though the original np.log(1+x) works if you handle zeros/negative carefully
66df['log_return'] = df['log_return'].apply(lambda x: np.log(1 + x) if 1 + x > 0 else 0)
67
68# Drop initial NaN rows created by rolling window calculations
69df = df.dropna()
70
71print(f"Data ready. Remaining rows after cleaning: {len(df)}")
72
73# --- STEP 3: INSTRUCTIONAL DATASET CREATION (SLIDING WINDOW) ---
74
75def format_for_llm(data_window, full_df):
76 """Generates a single instruction-tuning sample."""
77
78 # --- 1. Define the Context (Input Features) ---
79 context_data = data_window[['close', 'volume', 'rsi_14', 'sma_20']].tail(5)
80 context_table = context_data.to_markdown(numalign="left", stralign="left")
81
82 # --- 2. Define the Instruction ---
83 instruction = (
84 f"Analyze the 5 most recent 1-hour BTC bars in the table below, focusing on the Close price, "
85 f"Volume, RSI (Relative Strength Index), and 20-period SMA. "
86 f"Predict the price direction (UP or DOWN) for the next {PREDICTION_HORIZON} hours and provide a brief technical rationale."
87 )
88
89 # --- 3. Define the Response (Ground Truth Label) ---
90 try:
91 current_close = data_window['close'].iloc[-1]
92
93 # Look up the close price after the prediction horizon
94 # Use .iloc[] index to safely get the bar (row) that is 12 steps after the end of the current window
95 target_index_loc = full_df.index.get_loc(data_window.index[-1]) + PREDICTION_HORIZON
96 target_close = full_df['close'].iloc[target_index_loc]
97
98 # Calculate final movement
99 movement = target_close - current_close
100 direction = "UP" if movement > 0 else "DOWN"
101
102 # Craft the detailed response
103 response = (
104 f"The {PREDICTION_HORIZON}-hour prediction is **{direction}**. "
105 f"The final bar's RSI of {data_window['rsi_14'].iloc[-1]:.2f} suggests "
106 f"{'overbought pressure' if data_window['rsi_14'].iloc[-1] > 70 else 'room to run'}. "
107 f"The current Close is {'above' if current_close > data_window['sma_20'].iloc[-1] else 'below'} the 20-period SMA, "
108 f"which supports a {direction} bias. The price ultimately moved ${movement:.2f}."
109 )
110
111 except IndexError:
112 return None # Skip if there's not enough future data
113
114 # Final Instruction-Tuning Format (Mistral/Llama standard)
115 template = f"<s>[INST] {instruction}\n\n{context_table} [/INST] {response}</s>"
116 return {'text': template}
117
118# --- Generate the full dataset by sliding the window ---
119
120print(f"--- Generating samples (Window: {WINDOW_SIZE}h, Horizon: {PREDICTION_HORIZON}h) ---")
121fine_tuning_samples = []
122
123# Iterate, leaving enough bars at the end for the prediction horizon
124for i in range(WINDOW_SIZE, len(df) - PREDICTION_HORIZON):
125 history_window = df.iloc[i - WINDOW_SIZE : i]
126
127 # Pass the full df to the function for target lookup
128 sample = format_for_llm(history_window, df)
129
130 if sample:
131 fine_tuning_samples.append(sample)
132
133# --- STEP 4: SAVE DATASET ---
134
135output_file = 'btc_instruction_dataset.jsonl'
136with open(output_file, 'w') as f:
137 for sample in fine_tuning_samples:
138 f.write(json.dumps(sample) + '\n')
139
140print(f"\nSuccessfully generated {len(fine_tuning_samples)} hourly fine-tuning samples.")
141if fine_tuning_samples:
142 print(f"Example of one training sample (first entry):\n")
143 print("="*80)
144 print(fine_tuning_samples[0]['text'])
145 print("="*80)
146print(f"\nDataset saved to '{output_file}'. You are now ready for the fine-tuning stage (QLoRA/SFTTrainer).")
147
1481--- Attempting to load data from SQLite: /content/gdrive/MyDrive/TradingBotLogs/ohlcv_data_BTC.db - Table: btcusd_1h_data_12y ---
2--- Successfully loaded BTC data from SQLite ---
3Columns in loaded data: ['timestamp', 'open', 'high', 'low', 'close', 'volume']
4New Index Type: datetime64[ns, UTC]
5--- Calculating Technical Indicators (SMA, EMA, RSI, Log Returns) ---
6Data ready. Remaining rows after cleaning: 89769
7--- Generating samples (Window: 72h, Horizon: 12h) ---
8
9Successfully generated 89685 hourly fine-tuning samples.
10Example of one training sample (first entry):
11
12================================================================================
13<s>[INST] Analyze the 5 most recent 1-hour BTC bars in the table below, focusing on the Close price, Volume, RSI (Relative Strength Index), and 20-period SMA. Predict the price direction (UP or DOWN) for the next 12 hours and provide a brief technical rationale.
14
15| datetime | close | volume | rsi_14 | sma_20 |
16|:--------------------------|:--------|:---------|:---------|:---------|
17| 2013-10-26 08:00:00+00:00 | 184.58 | 4 | 48.8856 | 187.968 |
18| 2013-10-27 08:00:00+00:00 | 182.21 | 15 | 47.3294 | 186.801 |
19| 2013-10-27 12:00:00+00:00 | 179.47 | 9 | 45.5251 | 186.232 |
20| 2013-10-27 14:00:00+00:00 | 180.276 | 8 | 46.1755 | 186.75 |
21| 2013-10-27 15:00:00+00:00 | 179.236 | 4 | 45.4222 | 185.771 | [/INST] The 12-hour prediction is **UP**. The final bar's RSI of 45.42 suggests room to run. The current Close is below the 20-period SMA, which supports a UP bias. The price ultimately moved $21.53.</s>
22================================================================================
23
24Dataset saved to 'btc_instruction_dataset.jsonl'. You are now ready for the fine-tuning stage (QLoRA/SFTTrainer).
251
2import os
3import torch
4from datasets import load_dataset
5from transformers import (
6 AutoModelForCausalLM,
7 AutoTokenizer,
8 BitsAndBytesConfig,
9 TrainingArguments,
10)
11from peft import LoraConfig
12from trl import SFTTrainer
13
14# --- 0. GPU SETUP ---
15device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
16print(f"--- Running on device: {device} ---")
17if device.type == 'cpu':
18 raise RuntimeError("GPU not found. QLoRA fine-tuning is not feasible without a CUDA-enabled GPU.")
19
20# --- 1. CONFIGURATION ---
21MODEL_NAME = "mistralai/Mistral-7B-v0.1"
22DATASET_PATH = "/content/gdrive/MyDrive/CryptoFT/dataset/btc_instruction_dataset.jsonl"
23NEW_MODEL_NAME = "Mistral-7B-BTC-Expert"
24OUTPUT_DIR = "/content/gdrive/MyDrive/CryptoFT/models/results_btc_finetune"
25MAX_SEQ_LENGTH = 1024
26
27# Create output directory if it doesn't exist
28os.makedirs(OUTPUT_DIR, exist_ok=True)
29
30# LoRA Configuration
31peft_config = LoraConfig(
32 lora_alpha=16,
33 lora_dropout=0.1,
34 r=64,
35 bias="none",
36 task_type="CAUSAL_LM",
37 target_modules=[
38 "q_proj",
39 "k_proj",
40 "v_proj",
41 "o_proj",
42 "gate_proj",
43 "up_proj",
44 "down_proj"
45 ],
46)
47
48# --- Training Arguments (Hyperparameters) ---
49training_arguments = TrainingArguments(
50 output_dir=OUTPUT_DIR,
51 num_train_epochs=1,
52 per_device_train_batch_size=4,
53 gradient_accumulation_steps=4,
54 optim="paged_adamw_8bit",
55 save_steps=500,
56 logging_steps=50,
57 learning_rate=2e-4,
58 weight_decay=0.001,
59 fp16=True,
60 bf16=False,
61 max_grad_norm=0.3,
62 warmup_ratio=0.03,
63 group_by_length=True,
64 lr_scheduler_type="cosine",
65 disable_tqdm=False,
66 report_to="none",
67
68 # CRITICAL ADDITIONS TO SHOW EVAL LOSS
69 eval_strategy="steps", # Enable evaluation at specified step intervals
70 eval_steps=500, # Evaluate every 500 steps
71 load_best_model_at_end=True, # Load the best checkpoint based on eval_loss at the end
72 metric_for_best_model="eval_loss", # Use validation loss as the metric
73)
741
2# This code block successfully loads the model
3
4import torch
5from peft import PeftModel
6from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
7
8# Configuration
9HUB_MODEL_ID = "frankmorales2020/Mistral-7B-BTC-Expert"
10BASE_MODEL_ID = "mistralai/Mistral-7B-v0.1"
11DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
12
13# --- 1. Load Model with Quantization ---
14print(f"--- 1. Loading Model onto {DEVICE} ---")
15try:
16 bnb_config = BitsAndBytesConfig(
17 load_in_4bit=True,
18 bnb_4bit_quant_type="nf4",
19 bnb_4bit_compute_dtype=torch.float16,
20 )
21
22 base_model = AutoModelForCausalLM.from_pretrained(
23 BASE_MODEL_ID,
24 quantization_config=bnb_config,
25 device_map="auto",
26 )
27
28 tokenizer = AutoTokenizer.from_pretrained(HUB_MODEL_ID)
29 tokenizer.pad_token = tokenizer.eos_token
30
31 model = PeftModel.from_pretrained(base_model, HUB_MODEL_ID).eval()
32 print("✅ Model loaded successfully.")
33except Exception as e:
34 print(f"FATAL ERROR during model loading: {e}")
35 raise
36
37
38```python
39
40
41### Results
42
43```python
44
45--- Starting Fine-Tuning --- [5550/5550 7:34:55, Epoch 1/1]
46
47Step Training Loss Validation Loss Entropy Num Tokens Mean Token Accuracy
48
49500 0.215800 0.215459 0.215031 8192000.000000 0.916472
50
511000 0.217400 0.214608 0.214947 16384000.000000 0.916857
52
531500 0.212100 0.212424 0.212158 24576000.000000 0.917407
54
552000 0.211400 0.211059 0.210042 32768000.000000 0.917807
56
572500 0.209900 0.210005 0.208038 40960000.000000 0.918461
58
593000 0.207400 0.208606 0.208269 49152000.000000 0.919172
60
613500 0.206600 0.207157 0.207384 57344000.000000 0.919553
62
634000 0.204900 0.205344 0.205466 65536000.000000 0.920340
64
654500 0.203300 0.203777 0.203547 73728000.000000 0.921309
66
675000 0.201200 0.202193 0.202084 81920000.000000 0.921827
68
695500 0.200400 0.201922 0.201656 90112000.000000 0.922047
701Sat Oct 4 05:42:52 2025
2+-----------------------------------------------------------------------------------------+
3| NVIDIA-SMI 550.54.15 Driver Version: 550.54.15 CUDA Version: 12.4 |
4|-----------------------------------------+------------------------+----------------------+
5| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
6| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
7| | | MIG M. |
8|=========================================+========================+======================|
9| 0 NVIDIA A100-SXM4-80GB Off | 00000000:00:05.0 Off | 0 |
10| N/A 34C P0 56W / 400W | 0MiB / 81920MiB | 0% Default |
11| | | Disabled |
12+-----------------------------------------+------------------------+----------------------+
13
14+-----------------------------------------------------------------------------------------+
15| Processes: |
16| GPU GI CI PID Type Process name GPU Memory |
17| ID ID Usage |
18|=========================================================================================|
19| No running processes found |
20+-----------------------------------------------------------------------------------------+
21