Files
instagram-bot/scratch/hougaard_analyzer.py
2026-04-17 00:44:41 +02:00

133 lines
4.8 KiB
Python

import pandas as pd
import json
import os
import numpy as np
from datetime import datetime
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, confusion_matrix
class HougaardAnalyzer:
def __init__(self, file_path):
self.file_path = file_path
self.df = None
self.results = {}
def load_data(self):
"""Loads Freqtrade/Bybit JSON format and converts to DataFrame."""
print(f"Loading data from {self.file_path}...")
with open(self.file_path, 'r') as f:
data = json.load(f)
# Format: [timestamp, open, high, low, close, volume]
cols = ['date', 'open', 'high', 'low', 'close', 'volume']
self.df = pd.DataFrame(data, columns=cols)
# Convert timestamp (ms) to datetime
self.df['date'] = pd.to_datetime(self.df['date'], unit='ms', utc=True)
self.df.set_index('date', inplace=True)
print(f"Loaded {len(self.df)} candles.")
def engineer_features(self):
"""Creates Hougaard-style features."""
print("Engineering features...")
df = self.df
# 1. Time-based features
df['hour'] = df.index.hour
df['day_of_week'] = df.index.dayofweek
# 2. Overnight Range (00:00 - 08:00 UTC)
# We group by day and calculate High-Low for the 0-8h window
df['date_only'] = df.index.date
overnight = df.between_time('00:00', '08:00').groupby('date_only').agg({
'high': 'max',
'low': 'min',
'open': 'first'
}).rename(columns={'high': 'on_high', 'low': 'on_low', 'open': 'on_open'})
overnight['on_range_pct'] = (overnight['on_high'] - overnight['on_low']) / overnight['on_open']
# Map back to main DF
df = df.join(overnight, on='date_only')
# 3. Distance from Overnight High/Low at 08:00
df['dist_from_on_high'] = (df['close'] - df['on_high']) / df['on_high']
df['dist_from_on_low'] = (df['close'] - df['on_low']) / df['on_low']
# 4. Volatility (ATR-like)
df['body_size'] = abs(df['close'] - df['open']) / df['open']
df['wick_size'] = (df['high'] - np.maximum(df['open'], df['close'])) / df['open']
self.df = df.dropna()
def label_data(self, target_pct=0.01, stop_pct=0.005, horizon_candles=48):
"""
Labels a 'Long' setup at 08:00 UTC.
1: Hits target before stop
0: Hits stop before target or expires
"""
print(f"Labeling data (Target: {target_pct*100}%, Stop: {stop_pct*100}%)...")
# We only look at the 08:00 candle (London Open)
setups = self.df[self.df.index.hour == 8].copy()
labels = []
for idx, row in setups.iterrows():
entry_price = row['close']
target_price = entry_price * (1 + target_pct)
stop_price = entry_price * (1 - stop_pct)
# Look ahead
future_data = self.df.loc[idx:].iloc[1:horizon_candles]
success = 0
for f_idx, f_row in future_data.iterrows():
if f_row['high'] >= target_price:
success = 1
break
if f_row['low'] <= stop_price:
success = 0
break
labels.append(success)
setups['label'] = labels
return setups
def run_analysis(self):
self.load_data()
self.engineer_features()
setups = self.label_data()
# Features for the model
features = ['hour', 'day_of_week', 'on_range_pct', 'dist_from_on_high', 'dist_from_on_low', 'body_size', 'wick_size']
X = setups[features]
y = setups['label']
if len(y.unique()) < 2:
print("Error: Not enough variance in labels. Adjust target/stop.")
return
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
# Importance
importances = pd.Series(model.feature_importances_, index=features).sort_values(ascending=False)
print("\n--- Feature Importance (Hougaard Insights) ---")
print(importances)
# Accuracy
y_pred = model.predict(X_test)
print("\n--- Model Performance ---")
print(classification_report(y_test, y_pred))
if __name__ == "__main__":
# Test with BTC 5m data
data_path = "/Volumes/Alpha SSD/Coding/freqtrade/user_data/data/bybit/BTC_USDT-5m-futures.json"
analyzer = HougaardAnalyzer(data_path)
analyzer.run_analysis()