Initial commit
This commit is contained in:
commit
17e5d2a1e1
25
.gitignore
vendored
Normal file
25
.gitignore
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*.class
|
||||
*.so
|
||||
.Python
|
||||
env/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
.env
|
||||
.venv
|
||||
venv/
|
||||
ENV/
|
||||
.DS_Store
|
||||
142
main.py
Normal file
142
main.py
Normal file
@ -0,0 +1,142 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Market Window Analysis Script
|
||||
|
||||
This script performs rolling window analysis on market data using the market_predictor package.
|
||||
|
||||
Example Usage:
|
||||
python analyze_market_windows.py \
|
||||
--symbol BTC-USD \
|
||||
--start-date 2024-01-01 \
|
||||
--end-date 2024-01-31 \
|
||||
--interval 5m \
|
||||
--training-window 60 \
|
||||
--inference-window 12 \
|
||||
--inference-offset 0
|
||||
|
||||
Arguments:
|
||||
--symbol: Trading pair symbol (e.g., BTC-USD)
|
||||
--start-date: Analysis start date (YYYY-MM-DD)
|
||||
--end-date: Analysis end date (YYYY-MM-DD)
|
||||
--interval: Data interval (1m, 5m, 15m, 1h, etc.)
|
||||
--training-window: Number of intervals in training window
|
||||
--inference-window: Number of intervals in inference window
|
||||
--inference-offset: Offset between training and inference windows
|
||||
--output: Optional output file path for predictions CSV
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import argparse
|
||||
from datetime import datetime
|
||||
import pandas as pd
|
||||
from tqdm import tqdm
|
||||
import nest_asyncio
|
||||
|
||||
from market_predictor.market_data_fetcher import MarketDataFetcher
|
||||
from market_predictor.data_processor import MarketDataProcessor
|
||||
from market_predictor.prediction_service import PredictionService
|
||||
from market_predictor.performance_metrics import PerformanceMetrics
|
||||
|
||||
# Enable nested event loops
|
||||
nest_asyncio.apply()
|
||||
|
||||
async def analyze_market_data(
|
||||
market_data: pd.DataFrame,
|
||||
training_window_size: int = 60,
|
||||
inference_window_size: int = 12,
|
||||
inference_offset: int = 0
|
||||
) -> pd.DataFrame:
|
||||
"""Analyze market data using rolling windows."""
|
||||
# Validate required columns
|
||||
required_columns = {'Close', 'VWAP', 'Volume'}
|
||||
missing_columns = required_columns - set(market_data.columns)
|
||||
|
||||
if missing_columns:
|
||||
# Map yfinance columns to required columns
|
||||
column_mapping = {
|
||||
'Adj Close': 'Close',
|
||||
'Volume': 'Volume'
|
||||
}
|
||||
market_data = market_data.rename(columns=column_mapping)
|
||||
|
||||
# Calculate VWAP if missing
|
||||
if 'VWAP' not in market_data.columns:
|
||||
market_data['VWAP'] = (
|
||||
(market_data['High'] + market_data['Low'] + market_data['Close']) / 3 *
|
||||
market_data['Volume']
|
||||
).cumsum() / market_data['Volume'].cumsum()
|
||||
|
||||
processor = MarketDataProcessor(market_data)
|
||||
processed_data = processor.df
|
||||
|
||||
service = PredictionService(
|
||||
market_data=processed_data,
|
||||
training_window_size=training_window_size,
|
||||
inference_window_size=inference_window_size,
|
||||
inference_offset=inference_offset
|
||||
)
|
||||
|
||||
total_size = training_window_size + inference_offset + inference_window_size
|
||||
total_windows = len(processed_data) - total_size
|
||||
|
||||
predictions = []
|
||||
with tqdm(total=total_windows, desc="Processing", ncols=80) as pbar:
|
||||
async for pred in service.generate_rolling_predictions():
|
||||
if pred:
|
||||
predictions.append(pred)
|
||||
pbar.update(1)
|
||||
|
||||
predictions_df = pd.DataFrame(predictions) if predictions else pd.DataFrame()
|
||||
|
||||
if not predictions_df.empty:
|
||||
metrics = PerformanceMetrics(predictions_df, processed_data)
|
||||
report = metrics.generate_report()
|
||||
print("\nPerformance Report:")
|
||||
print(report)
|
||||
|
||||
return predictions_df
|
||||
|
||||
def parse_args():
|
||||
"""Parse command line arguments."""
|
||||
parser = argparse.ArgumentParser(description="Market Window Analysis")
|
||||
|
||||
parser.add_argument("--symbol", required=True, help="Trading pair symbol")
|
||||
parser.add_argument("--start-date", required=True, help="Start date (YYYY-MM-DD)")
|
||||
parser.add_argument("--end-date", required=True, help="End date (YYYY-MM-DD)")
|
||||
parser.add_argument("--interval", default="5m", help="Data interval")
|
||||
parser.add_argument("--training-window", type=int, default=60, help="Training window size")
|
||||
parser.add_argument("--inference-window", type=int, default=12, help="Inference window size")
|
||||
parser.add_argument("--inference-offset", type=int, default=0, help="Inference offset")
|
||||
parser.add_argument("--output", help="Output file path for predictions CSV")
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
async def main():
|
||||
args = parse_args()
|
||||
|
||||
fetcher = MarketDataFetcher(args.symbol)
|
||||
market_data = fetcher.fetch_data(
|
||||
start_date=args.start_date,
|
||||
end_date=args.end_date,
|
||||
interval=args.interval
|
||||
)
|
||||
print(f"Fetched {len(market_data)} rows of data")
|
||||
|
||||
try:
|
||||
predictions_df = await analyze_market_data(
|
||||
market_data,
|
||||
training_window_size=args.training_window,
|
||||
inference_window_size=args.inference_window,
|
||||
inference_offset=args.inference_offset
|
||||
)
|
||||
|
||||
if args.output and not predictions_df.empty:
|
||||
predictions_df.to_csv(args.output)
|
||||
print(f"\nPredictions saved to: {args.output}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Analysis failed: {str(e)}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
nest_asyncio.apply()
|
||||
asyncio.run(main())
|
||||
7
market_predictor/__init__.py
Normal file
7
market_predictor/__init__.py
Normal file
@ -0,0 +1,7 @@
|
||||
from .market_data_fetcher import MarketDataFetcher
|
||||
from .prediction_service import PredictionService
|
||||
from .performance_metrics import PerformanceMetrics
|
||||
from .data_processor import MarketDataProcessor
|
||||
from .rag_engine import RAGEngine
|
||||
|
||||
__version__ = "0.1.0"
|
||||
33
market_predictor/config.py
Normal file
33
market_predictor/config.py
Normal file
@ -0,0 +1,33 @@
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
OPENAI_API_KEY = "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"
|
||||
|
||||
if not OPENAI_API_KEY:
|
||||
raise ValueError("OpenAI API key not found in environment variables")
|
||||
|
||||
# Model Configuration
|
||||
MODEL_NAME = "ft:gpt-4o-mini-2024-07-18:yasha-sheynin::Awacdfg6"
|
||||
|
||||
# RAG Configuration
|
||||
VECTOR_STORE_TYPE = "faiss"
|
||||
CHUNK_SIZE = 1000
|
||||
CHUNK_OVERLAP = 200
|
||||
EMBEDDING_MODEL = "text-embedding-3-small"
|
||||
CHUNK_SIZE = 20
|
||||
MAX_WINDOW_SIZE = 80
|
||||
|
||||
MARKET_DATA_CONFIG = {
|
||||
'test_mode': {
|
||||
'symbol': 'META',
|
||||
'period': '1d',
|
||||
'interval': '5m'
|
||||
},
|
||||
'prod_mode': {
|
||||
'symbol': 'META',
|
||||
'period': '1mo',
|
||||
'interval': '5m'
|
||||
}
|
||||
}
|
||||
86
market_predictor/data_processor.py
Normal file
86
market_predictor/data_processor.py
Normal file
@ -0,0 +1,86 @@
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from typing import List, Dict
|
||||
|
||||
class MarketDataProcessor:
|
||||
REQUIRED_COLUMNS = ['Close', 'VWAP', 'Volume']
|
||||
|
||||
def __init__(self, df: pd.DataFrame):
|
||||
self.df = df.copy()
|
||||
self._validate_columns()
|
||||
self._initialize_moving_averages()
|
||||
|
||||
def _validate_columns(self):
|
||||
"""Verify required columns exist"""
|
||||
missing = set(self.REQUIRED_COLUMNS) - set(self.df.columns)
|
||||
if missing:
|
||||
raise ValueError(f"Missing required columns: {missing}")
|
||||
|
||||
def _initialize_moving_averages(self):
|
||||
"""Initialize or update moving averages"""
|
||||
self.df['MA5'] = self.df['Close'].rolling(window=5, min_periods=1).mean()
|
||||
self.df['MA20'] = self.df['Close'].rolling(window=20, min_periods=1).mean()
|
||||
self.df['Volume_MA5'] = self.df['Volume'].rolling(window=5, min_periods=1).mean()
|
||||
|
||||
def create_windows(self, data: pd.DataFrame = None, window_size: int = 20) -> List[str]:
|
||||
"""Create window descriptions ensuring MA columns exist"""
|
||||
df = data.copy() if data is not None else self.df.copy()
|
||||
if 'MA5' not in df.columns:
|
||||
df['MA5'] = df['Close'].rolling(window=5, min_periods=1).mean()
|
||||
df['MA20'] = df['Close'].rolling(window=20, min_periods=1).mean()
|
||||
df['Volume_MA5'] = df['Volume'].rolling(window=5, min_periods=1).mean()
|
||||
|
||||
windows = []
|
||||
for i in range(len(df) - window_size + 1):
|
||||
window = df.iloc[i:i+window_size]
|
||||
description = self.generate_description(window)
|
||||
windows.append(description)
|
||||
return windows
|
||||
|
||||
def generate_description(self, window: pd.DataFrame, is_training: bool = False) -> str:
|
||||
"""
|
||||
Generates market context with VWAP movement for training data only.
|
||||
"""
|
||||
# If the window is missing technical indicator columns, compute them.
|
||||
required_cols = ['MA5', 'MA20', 'Volume_MA5']
|
||||
missing = [col for col in required_cols if col not in window.columns]
|
||||
if missing:
|
||||
window = window.copy()
|
||||
if "Close" in window.columns:
|
||||
window["MA5"] = window["Close"].rolling(window=5, min_periods=1).mean().bfill()
|
||||
window["MA20"] = window["Close"].rolling(window=20, min_periods=1).mean().bfill()
|
||||
else:
|
||||
window["MA5"] = 0
|
||||
window["MA20"] = 0
|
||||
if "Volume" in window.columns:
|
||||
window["Volume_MA5"] = window["Volume"].rolling(window=5, min_periods=1).mean().bfill()
|
||||
else:
|
||||
window["Volume_MA5"] = 0
|
||||
|
||||
latest = window.iloc[-1]
|
||||
prev = window.iloc[-2] if len(window) > 1 else latest
|
||||
|
||||
volume_change = (
|
||||
((latest['Volume'] - prev['Volume'])/prev['Volume']*100)
|
||||
if prev['Volume'] > 0
|
||||
else 0
|
||||
)
|
||||
# Calculate VWAP movement from previous interval
|
||||
vwap_change = (latest["VWAP"] - prev["VWAP"]) / prev["VWAP"] * 100
|
||||
vwap_direction = "up" if vwap_change > 0 else "down"
|
||||
|
||||
# Base description
|
||||
desc = f"""
|
||||
Current Price: {latest['Close']:.2f}
|
||||
VWAP: {latest['VWAP']:.2f}
|
||||
Volume: {latest['Volume']}
|
||||
MA5: {latest['MA5']:.2f}
|
||||
MA20: {latest['MA20']:.2f}
|
||||
Volume MA5: {latest['Volume_MA5']:.2f}
|
||||
Price Change: {((latest['Close'] - prev['Close'])/prev['Close']*100):.2f}%
|
||||
Volume Change: {volume_change:.2f}%
|
||||
Previous 5min VWAP Movement: {vwap_direction} ({vwap_change:.2f}%)
|
||||
"""
|
||||
|
||||
return desc
|
||||
|
||||
239
market_predictor/fine_tune_dataset_generator.py
Normal file
239
market_predictor/fine_tune_dataset_generator.py
Normal file
@ -0,0 +1,239 @@
|
||||
import asyncio
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
import pandas as pd
|
||||
from tqdm import tqdm
|
||||
from openai import OpenAI
|
||||
from typing import List, Dict
|
||||
from .config import OPENAI_API_KEY
|
||||
|
||||
from .market_data_fetcher import MarketDataFetcher
|
||||
from .data_processor import MarketDataProcessor
|
||||
|
||||
|
||||
class FineTuneDatasetGenerator:
|
||||
def __init__(self, symbols: List[str], lookback_days: int = 30):
|
||||
self.symbols = symbols
|
||||
self.lookback_days = lookback_days
|
||||
self.client = OpenAI(api_key=OPENAI_API_KEY)
|
||||
|
||||
async def generate_dataset(self) -> List[Dict]:
|
||||
"""Generate labeled dataset for fine-tuning"""
|
||||
examples = []
|
||||
|
||||
for symbol in tqdm(self.symbols, desc="Processing symbols"):
|
||||
# Fetch historical data
|
||||
end_date = datetime.now()
|
||||
start_date = end_date - timedelta(days=self.lookback_days)
|
||||
|
||||
fetcher = MarketDataFetcher(symbol)
|
||||
market_data = fetcher.fetch_data(
|
||||
start_date=start_date.strftime('%Y-%m-%d'),
|
||||
end_date=end_date.strftime('%Y-%m-%d'),
|
||||
interval='5m'
|
||||
)
|
||||
|
||||
# Process market data
|
||||
processor = MarketDataProcessor(market_data)
|
||||
processed_data = processor.df
|
||||
|
||||
# Generate training examples
|
||||
examples.extend(self._generate_examples(processed_data))
|
||||
|
||||
return examples
|
||||
|
||||
def _generate_examples(self, data: pd.DataFrame) -> List[Dict]:
|
||||
"""Generate labeled examples from processed market data"""
|
||||
examples = []
|
||||
window_size = 12 # 1-hour context
|
||||
|
||||
for i in range(len(data) - window_size):
|
||||
window = data.iloc[i:i+window_size]
|
||||
next_row = data.iloc[i+window_size] if i+window_size < len(data) else None
|
||||
|
||||
if next_row is not None:
|
||||
# Create market state description
|
||||
context = self._create_context(window)
|
||||
|
||||
# Generate label
|
||||
label = self._create_label(window, next_row)
|
||||
|
||||
examples.append({
|
||||
"messages": [
|
||||
{"role": "system", "content": "You are a market analysis AI that predicts short-term price movements."},
|
||||
{"role": "user", "content": context},
|
||||
{"role": "assistant", "content": json.dumps(label)}
|
||||
]
|
||||
})
|
||||
|
||||
return examples
|
||||
|
||||
def _create_context(self, window: pd.DataFrame) -> str:
|
||||
"""Create market state description using DataProcessor format"""
|
||||
# Ensure window has required columns
|
||||
required_cols = ['MA5', 'MA20', 'Volume_MA5']
|
||||
missing = [col for col in required_cols if col not in window.columns]
|
||||
if missing:
|
||||
window = window.copy()
|
||||
if "Close" in window.columns:
|
||||
window["MA5"] = window["Close"].rolling(window=5, min_periods=1).mean().bfill()
|
||||
window["MA20"] = window["Close"].rolling(window=20, min_periods=1).mean().bfill()
|
||||
else:
|
||||
window["MA5"] = 0
|
||||
window["MA20"] = 0
|
||||
if "Volume" in window.columns:
|
||||
window["Volume_MA5"] = window["Volume"].rolling(window=5, min_periods=1).mean().bfill()
|
||||
else:
|
||||
window["Volume_MA5"] = 0
|
||||
|
||||
latest = window.iloc[-1]
|
||||
prev = window.iloc[-2] if len(window) > 1 else latest
|
||||
|
||||
# Calculate changes
|
||||
volume_change = ((latest['Volume'] - prev['Volume'])/prev['Volume']*100) if prev['Volume'] > 0 else 0
|
||||
vwap_change = (latest["VWAP"] - prev["VWAP"]) / prev["VWAP"] * 100
|
||||
vwap_direction = "up" if vwap_change > 0 else "down"
|
||||
|
||||
return f"""Current Market State:
|
||||
Current Price: {latest['Close']:.2f}
|
||||
VWAP: {latest['VWAP']:.2f}
|
||||
Volume: {latest['Volume']}
|
||||
MA5: {latest['MA5']:.2f}
|
||||
MA20: {latest['MA20']:.2f}
|
||||
Volume MA5: {latest['Volume_MA5']:.2f}
|
||||
Price Change: {((latest['Close'] - prev['Close'])/prev['Close']*100):.2f}%
|
||||
Volume Change: {volume_change:.2f}%
|
||||
Previous 5min VWAP Movement: {vwap_direction} ({vwap_change:.2f}%)
|
||||
Time: {latest.name}
|
||||
"""
|
||||
|
||||
def _create_label(self, window: pd.DataFrame, next_row: pd.Series) -> Dict:
|
||||
"""Create labeled output"""
|
||||
current_vwap = window.iloc[-1]['VWAP']
|
||||
next_vwap = next_row['VWAP']
|
||||
direction = 'up' if next_vwap > current_vwap else 'down'
|
||||
|
||||
return {
|
||||
"vwap_direction_next_5min": direction,
|
||||
"confidence_score": 0.8,
|
||||
"expected_vwap_change": ((next_vwap - current_vwap) / current_vwap) * 100,
|
||||
"volatility_estimate": window['VWAP'].std(),
|
||||
"suggested_entry": current_vwap,
|
||||
"suggested_stop_loss": current_vwap * 0.997 if direction == 'up' else current_vwap * 1.003,
|
||||
"suggested_take_profit": current_vwap * 1.003 if direction == 'up' else current_vwap * 0.997,
|
||||
"key_signals": self._identify_signals(window),
|
||||
"reasoning": self._generate_reasoning(window, direction)
|
||||
}
|
||||
|
||||
def _identify_signals(self, window: pd.DataFrame) -> Dict:
|
||||
"""
|
||||
Identify technical signals from the market data window
|
||||
|
||||
Args:
|
||||
window (pd.DataFrame): DataFrame containing market data for analysis
|
||||
|
||||
Returns:
|
||||
Dict: Dictionary containing identified signals
|
||||
"""
|
||||
return {
|
||||
"trend": self._calculate_trend(window),
|
||||
"volume_trend": "increasing" if window['Volume'].iloc[-1] > window['Volume_MA5'].iloc[-1] else "decreasing"
|
||||
}
|
||||
|
||||
def _calculate_trend(self, window: pd.DataFrame) -> str:
|
||||
"""
|
||||
Calculate the price trend based on moving averages
|
||||
|
||||
Args:
|
||||
window (pd.DataFrame): Market data window with MA5 and MA20 columns
|
||||
|
||||
Returns:
|
||||
str: Trend direction ('upward', 'downward', or 'sideways')
|
||||
"""
|
||||
last_row = window.iloc[-1]
|
||||
ma5 = last_row['MA5']
|
||||
ma20 = last_row['MA20']
|
||||
|
||||
# Calculate trend based on MA crossover
|
||||
if ma5 > ma20 * 1.02: # 2% threshold
|
||||
return "upward"
|
||||
elif ma5 < ma20 * 0.98: # 2% threshold
|
||||
return "downward"
|
||||
else:
|
||||
return "sideways"
|
||||
|
||||
def _generate_reasoning(self, window: pd.DataFrame, direction: str) -> str:
|
||||
"""
|
||||
Generate reasoning for the market prediction
|
||||
|
||||
Args:
|
||||
window (pd.DataFrame): Market data window
|
||||
direction (str): Predicted price direction ('up' or 'down')
|
||||
|
||||
Returns:
|
||||
str: Generated reasoning for the prediction
|
||||
"""
|
||||
signals = self._identify_signals(window)
|
||||
last_row = window.iloc[-1]
|
||||
|
||||
reasoning_parts = []
|
||||
|
||||
# Analyze trend
|
||||
if signals['trend'] == direction:
|
||||
reasoning_parts.append(f"The {signals['trend']} trend supports this prediction")
|
||||
|
||||
# Analyze volume
|
||||
if signals['volume_trend'] == 'increasing':
|
||||
reasoning_parts.append("Increasing volume suggests strong momentum")
|
||||
else:
|
||||
reasoning_parts.append("Decreasing volume suggests potential trend weakness")
|
||||
|
||||
# VWAP analysis
|
||||
vwap = last_row['VWAP']
|
||||
close = last_row['Close']
|
||||
if close > vwap and direction == 'up':
|
||||
reasoning_parts.append("Price above VWAP supports bullish momentum")
|
||||
elif close < vwap and direction == 'down':
|
||||
reasoning_parts.append("Price below VWAP supports bearish momentum")
|
||||
|
||||
return ". ".join(reasoning_parts) + "."
|
||||
|
||||
async def create_fine_tuning_job(self, examples: List[Dict]):
|
||||
"""Create and monitor fine-tuning job"""
|
||||
# Save examples to JSONL file
|
||||
with open('training_data.jsonl', 'w') as f:
|
||||
for example in examples:
|
||||
f.write(json.dumps(example) + '\n')
|
||||
|
||||
# Upload training file - remove await
|
||||
training_file = self.client.files.create(
|
||||
file=open('training_data.jsonl', 'rb'),
|
||||
purpose='fine-tune'
|
||||
)
|
||||
|
||||
# Create fine-tuning job - remove await
|
||||
job = self.client.fine_tuning.jobs.create(
|
||||
training_file=training_file.id,
|
||||
model="gpt-4o-mini-2024-07-18",
|
||||
hyperparameters={
|
||||
"n_epochs": 3
|
||||
}
|
||||
)
|
||||
|
||||
print(f"Fine-tuning job created: {job.id}")
|
||||
return job.id
|
||||
|
||||
async def main():
|
||||
symbols = ['BTC-USD']
|
||||
generator = FineTuneDatasetGenerator(symbols)
|
||||
|
||||
# Generate dataset
|
||||
examples = await generator.generate_dataset()
|
||||
print(f"Generated {len(examples)} training examples")
|
||||
|
||||
# Create fine-tuning job
|
||||
job_id = await generator.create_fine_tuning_job(examples)
|
||||
print(f"Fine-tuning job started. Monitor progress using: openai api fine_tunes.follow -i {job_id}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
68
market_predictor/market_data_fetcher.py
Normal file
68
market_predictor/market_data_fetcher.py
Normal file
@ -0,0 +1,68 @@
|
||||
import yfinance as yf
|
||||
import pandas as pd
|
||||
import pytz
|
||||
from datetime import datetime, time, timedelta
|
||||
|
||||
class MarketDataFetcher:
|
||||
def __init__(self, symbol: str):
|
||||
self.symbol = symbol.upper()
|
||||
self.ticker = yf.Ticker(self.symbol)
|
||||
self.est_tz = pytz.timezone('America/New_York')
|
||||
|
||||
def is_market_hours(self, dt):
|
||||
"""Check if time is within market hours (9:30-16:00 EST)"""
|
||||
if dt.weekday() >= 5: # Weekend
|
||||
return False
|
||||
market_start = time(9, 30)
|
||||
market_end = time(16, 0)
|
||||
return market_start <= dt.time() <= market_end
|
||||
|
||||
def fetch_data(self, start_date: str, end_date: str, interval: str = "5m") -> pd.DataFrame:
|
||||
try:
|
||||
end = pd.to_datetime(end_date).tz_localize(self.est_tz)
|
||||
start = pd.to_datetime(start_date).tz_localize(self.est_tz)
|
||||
|
||||
df = self.ticker.history(
|
||||
start=start,
|
||||
end=end,
|
||||
interval=interval,
|
||||
prepost=False
|
||||
)
|
||||
|
||||
if df.empty:
|
||||
return pd.DataFrame()
|
||||
|
||||
# Filter market hours
|
||||
df = df[df.index.map(self.is_market_hours)]
|
||||
|
||||
# Calculate VWAP
|
||||
df['VWAP'] = (df['Close'] * df['Volume']).cumsum() / df['Volume'].cumsum()
|
||||
|
||||
return df
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error fetching data: {str(e)}")
|
||||
return pd.DataFrame()
|
||||
|
||||
def save_data(self, df: pd.DataFrame, filename: str = None):
|
||||
"""Save market data to CSV"""
|
||||
if filename is None:
|
||||
filename = f"{self.symbol}_market_data.csv"
|
||||
df.to_csv(filename)
|
||||
|
||||
def main():
|
||||
# Test functionality
|
||||
fetcher = MarketDataFetcher("META")
|
||||
df = fetcher.fetch_data(
|
||||
start_date="2024-01-01",
|
||||
end_date="2024-01-31",
|
||||
interval="5m"
|
||||
)
|
||||
|
||||
if not df.empty:
|
||||
print("\nFetched Market Data:")
|
||||
print(df.head())
|
||||
fetcher.save_data(df)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
342
market_predictor/performance_metrics.py
Normal file
342
market_predictor/performance_metrics.py
Normal file
@ -0,0 +1,342 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, confusion_matrix
|
||||
from typing import Dict
|
||||
from collections import Counter, defaultdict
|
||||
|
||||
class PerformanceMetrics:
|
||||
def __init__(self, predictions_df: pd.DataFrame, market_data: pd.DataFrame):
|
||||
self.predictions_df = predictions_df
|
||||
self.market_data = market_data
|
||||
self._calculate_actual_movements()
|
||||
self.metrics = self._calculate_metrics()
|
||||
|
||||
def _calculate_actual_movements(self):
|
||||
"""Calculate actual VWAP movements with detailed logging"""
|
||||
print("\nDebug Counts:")
|
||||
print(f"Initial DataFrame rows: {len(self.predictions_df)}")
|
||||
|
||||
movements = []
|
||||
valid_predictions = 0
|
||||
skipped_timestamps = 0
|
||||
|
||||
for idx, row in self.predictions_df.iterrows():
|
||||
timestamp = row['prediction_timestamp']
|
||||
try:
|
||||
current_idx = self.market_data.index.get_loc(timestamp)
|
||||
if current_idx + 1 < len(self.market_data):
|
||||
current_vwap = self.market_data['VWAP'].iloc[current_idx]
|
||||
next_vwap = self.market_data['VWAP'].iloc[current_idx + 1]
|
||||
movement = 'up' if next_vwap > current_vwap else 'down'
|
||||
movements.append(movement)
|
||||
valid_predictions += 1
|
||||
else:
|
||||
movements.append(None)
|
||||
skipped_timestamps += 1
|
||||
print(f"Skipped: No next VWAP for timestamp {timestamp}")
|
||||
except KeyError:
|
||||
movements.append(None)
|
||||
skipped_timestamps += 1
|
||||
print(f"Skipped: Timestamp not found {timestamp}")
|
||||
|
||||
print(f"\nProcessing Summary:")
|
||||
print(f"Total rows initially: {len(self.predictions_df)}")
|
||||
print(f"Valid predictions: {valid_predictions}")
|
||||
print(f"Skipped timestamps: {skipped_timestamps}")
|
||||
|
||||
self.predictions_df['actual_movement'] = movements
|
||||
valid_mask = self.predictions_df['actual_movement'].notna()
|
||||
self.predictions_df = self.predictions_df[valid_mask].copy()
|
||||
|
||||
print(f"Final predictions count: {len(self.predictions_df)}\n")
|
||||
|
||||
def _calculate_metrics(self) -> dict:
|
||||
if len(self.predictions_df) == 0:
|
||||
return self._empty_metrics()
|
||||
|
||||
y_true = self.predictions_df['actual_movement']
|
||||
y_pred = self.predictions_df['vwap_direction_next_5min']
|
||||
|
||||
print("\nClass distributions:")
|
||||
print("Actual:", y_true.value_counts().to_dict())
|
||||
print("Predicted:", y_pred.value_counts().to_dict())
|
||||
|
||||
acc = accuracy_score(y_true, y_pred)
|
||||
prec = precision_score(y_true, y_pred, pos_label='up', zero_division=0)
|
||||
rec = recall_score(y_true, y_pred, pos_label='up', zero_division=0)
|
||||
f1 = f1_score(y_true, y_pred, pos_label='up', zero_division=0)
|
||||
|
||||
# High confidence metrics
|
||||
high_conf_mask = self.predictions_df['confidence_score'] >= 0.7
|
||||
if high_conf_mask.any():
|
||||
high_conf_correct = ((y_pred == y_true) & high_conf_mask).sum()
|
||||
high_conf_acc = high_conf_correct / high_conf_mask.sum()
|
||||
else:
|
||||
high_conf_acc = 0.0
|
||||
|
||||
# Print confusion matrix for debugging
|
||||
cm = confusion_matrix(y_true, y_pred)
|
||||
print("\nConfusion Matrix:")
|
||||
print(pd.DataFrame(
|
||||
cm,
|
||||
columns=['Pred Down', 'Pred Up'],
|
||||
index=['True Down', 'True Up']
|
||||
))
|
||||
|
||||
# Keep existing metrics calculation
|
||||
metrics = {
|
||||
'total_predictions': len(self.predictions_df),
|
||||
'class_distribution': y_pred.value_counts().to_dict(),
|
||||
'avg_confidence': self.predictions_df['confidence_score'].mean(),
|
||||
'accuracy': acc,
|
||||
'precision': prec,
|
||||
'recall': rec,
|
||||
'f1': f1,
|
||||
'high_confidence_accuracy': high_conf_acc
|
||||
}
|
||||
|
||||
# Add trading metrics
|
||||
metrics.update({
|
||||
'avg_expected_vwap_change': self.predictions_df['expected_vwap_change'].mean(),
|
||||
'avg_volatility_estimate': self.predictions_df['volatility_estimate'].mean(),
|
||||
'price_targets': {
|
||||
'entry_success_rate': self._calculate_entry_success(),
|
||||
'stop_loss_hits': self._calculate_stop_loss_hits(),
|
||||
'take_profit_hits': self._calculate_take_profit_hits(),
|
||||
'avg_risk_reward': self._calculate_risk_reward_ratio()
|
||||
},
|
||||
'signals': self._analyze_signals()
|
||||
})
|
||||
|
||||
return metrics
|
||||
|
||||
def _calculate_entry_success(self) -> float:
|
||||
"""Calculate rate of successful entries"""
|
||||
successes = 0
|
||||
total = len(self.predictions_df)
|
||||
|
||||
for _, row in self.predictions_df.iterrows():
|
||||
entry = row.get('suggested_entry')
|
||||
if entry is None:
|
||||
continue
|
||||
|
||||
# Check if price moved in predicted direction from entry
|
||||
actual_move = row['actual_movement']
|
||||
pred_move = row['vwap_direction_next_5min']
|
||||
if actual_move == pred_move:
|
||||
successes += 1
|
||||
|
||||
return successes / total if total > 0 else 0.0
|
||||
|
||||
def _calculate_stop_loss_hits(self) -> float:
|
||||
"""Calculate stop loss hit rate"""
|
||||
hits = 0
|
||||
total = len(self.predictions_df)
|
||||
|
||||
for _, row in self.predictions_df.iterrows():
|
||||
stop_loss = row.get('suggested_stop_loss')
|
||||
if stop_loss is None:
|
||||
continue
|
||||
|
||||
# Check if price hit stop loss
|
||||
next_vwap = self.market_data.loc[row['prediction_timestamp']:].iloc[1]['VWAP']
|
||||
if (row['vwap_direction_next_5min'] == 'up' and next_vwap <= stop_loss) or \
|
||||
(row['vwap_direction_next_5min'] == 'down' and next_vwap >= stop_loss):
|
||||
hits += 1
|
||||
|
||||
return hits / total if total > 0 else 0.0
|
||||
|
||||
def _calculate_take_profit_hits(self) -> float:
|
||||
"""Calculate take profit hit rate"""
|
||||
hits = 0
|
||||
total = len(self.predictions_df)
|
||||
|
||||
for _, row in self.predictions_df.iterrows():
|
||||
take_profit = row.get('suggested_take_profit')
|
||||
if take_profit is None:
|
||||
continue
|
||||
|
||||
# Check if price hit take profit
|
||||
next_vwap = self.market_data.loc[row['prediction_timestamp']:].iloc[1]['VWAP']
|
||||
if (row['vwap_direction_next_5min'] == 'up' and next_vwap >= take_profit) or \
|
||||
(row['vwap_direction_next_5min'] == 'down' and next_vwap <= take_profit):
|
||||
hits += 1
|
||||
|
||||
return hits / total if total > 0 else 0.0
|
||||
|
||||
def _calculate_risk_reward_ratio(self) -> float:
|
||||
"""Calculate average risk/reward ratio"""
|
||||
ratios = []
|
||||
|
||||
for _, row in self.predictions_df.iterrows():
|
||||
entry = row.get('suggested_entry')
|
||||
stop = row.get('suggested_stop_loss')
|
||||
target = row.get('suggested_take_profit')
|
||||
|
||||
if None in (entry, stop, target):
|
||||
continue
|
||||
|
||||
risk = abs(entry - stop)
|
||||
reward = abs(target - entry)
|
||||
if risk > 0:
|
||||
ratios.append(reward / risk)
|
||||
|
||||
return np.mean(ratios) if ratios else 0.0
|
||||
|
||||
def _format_top_signals(self) -> str:
|
||||
"""Format signal analysis for report"""
|
||||
all_signals = []
|
||||
for signals in self.predictions_df['key_signals']:
|
||||
all_signals.extend(signals)
|
||||
|
||||
signal_counts = Counter(all_signals)
|
||||
top_signals = signal_counts.most_common(5)
|
||||
|
||||
return '\n'.join(f"{signal}: {count}" for signal, count in top_signals)
|
||||
|
||||
def _calculate_weighted_sharpe_ratio(self) -> float:
|
||||
"""Calculate price-weighted Sharpe ratio"""
|
||||
# Constants
|
||||
TRANSACTION_COST = 0.001 # 0.1% per trade
|
||||
RISK_FREE_RATE = 0.02 # 2% annual
|
||||
|
||||
# Calculate returns for each prediction
|
||||
returns = []
|
||||
position_sizes = []
|
||||
|
||||
for _, row in self.predictions_df.iterrows():
|
||||
# Get stock price at prediction time
|
||||
timestamp = row['prediction_timestamp']
|
||||
stock_price = self.market_data.loc[timestamp, 'VWAP']
|
||||
|
||||
# Calculate position size and return
|
||||
is_correct = row['vwap_direction_next_5min'] == row['actual_movement']
|
||||
raw_return = 1 if is_correct else -1
|
||||
|
||||
# Weight return by position size and apply transaction costs
|
||||
position_size = stock_price * 1 # 1 share per trade
|
||||
weighted_return = (raw_return * position_size) - (stock_price * TRANSACTION_COST)
|
||||
|
||||
returns.append(weighted_return)
|
||||
position_sizes.append(position_size)
|
||||
|
||||
# Convert to numpy arrays
|
||||
returns = np.array(returns)
|
||||
position_sizes = np.array(position_sizes)
|
||||
|
||||
# Calculate Sharpe ratio components
|
||||
avg_position_size = np.mean(position_sizes)
|
||||
returns_mean = np.mean(returns) / avg_position_size
|
||||
returns_std = np.std(returns) / avg_position_size
|
||||
daily_rf_rate = RISK_FREE_RATE / 252
|
||||
|
||||
# Compute annualized Sharpe ratio
|
||||
if returns_std == 0:
|
||||
return 0
|
||||
|
||||
daily_sharpe = (returns_mean - daily_rf_rate) / returns_std
|
||||
annual_sharpe = daily_sharpe * np.sqrt(252)
|
||||
|
||||
# Store additional metrics
|
||||
self.trading_metrics = {
|
||||
'avg_position_size': avg_position_size,
|
||||
'total_pnl': np.sum(returns),
|
||||
'avg_return': returns_mean,
|
||||
'return_volatility': returns_std
|
||||
}
|
||||
|
||||
return annual_sharpe
|
||||
|
||||
def _analyze_signals(self) -> Dict:
|
||||
"""Analyze signal effectiveness and frequency"""
|
||||
signal_stats = defaultdict(lambda: {'total': 0, 'correct': 0})
|
||||
|
||||
for _, row in self.predictions_df.iterrows():
|
||||
prediction = row['vwap_direction_next_5min']
|
||||
actual = row['actual_movement']
|
||||
signals = row['key_signals']
|
||||
|
||||
for signal in signals:
|
||||
signal_stats[signal]['total'] += 1
|
||||
if prediction == actual:
|
||||
signal_stats[signal]['correct'] += 1
|
||||
|
||||
# Calculate success rate for each signal
|
||||
signal_accuracy = {
|
||||
signal: {
|
||||
'count': stats['total'],
|
||||
'accuracy': stats['correct'] / stats['total'] if stats['total'] > 0 else 0
|
||||
}
|
||||
for signal, stats in signal_stats.items()
|
||||
}
|
||||
|
||||
# Get most frequent signals
|
||||
signal_counts = Counter([s for row in self.predictions_df['key_signals'] for s in row])
|
||||
top_signals = signal_counts.most_common(5)
|
||||
|
||||
return {
|
||||
'signal_accuracy': signal_accuracy,
|
||||
'top_signals': top_signals,
|
||||
'total_unique_signals': len(signal_stats)
|
||||
}
|
||||
|
||||
def _format_signal_analysis(self) -> str:
|
||||
"""Format signal analysis for report"""
|
||||
signal_analysis = self.metrics.get('signals', {})
|
||||
if not signal_analysis:
|
||||
return "No signal analysis available"
|
||||
|
||||
lines = []
|
||||
lines.append("Most Frequent Signals:")
|
||||
for signal, count in signal_analysis['top_signals']:
|
||||
acc = signal_analysis['signal_accuracy'][signal]['accuracy']
|
||||
lines.append(f" {signal}: {count} occurrences, {acc:.2%} accuracy")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
def generate_report(self) -> str:
|
||||
existing_report = f"""
|
||||
Performance Report
|
||||
=================
|
||||
Total Predictions: {self.metrics['total_predictions']}
|
||||
Accuracy: {self.metrics['accuracy']:.2%}
|
||||
Precision: {self.metrics['precision']:.2%}
|
||||
Recall: {self.metrics['recall']:.2%}
|
||||
F1 Score: {self.metrics['f1']:.2%}
|
||||
|
||||
Direction Distribution:
|
||||
-------------------
|
||||
Up: {self.metrics['class_distribution'].get('up', 0)}
|
||||
Down: {self.metrics['class_distribution'].get('down', 0)}
|
||||
|
||||
Confidence Analysis:
|
||||
-----------------
|
||||
Average Confidence: {self.metrics['avg_confidence']:.2%}
|
||||
High Confidence Accuracy: {self.metrics['high_confidence_accuracy']:.2%}
|
||||
"""
|
||||
|
||||
# Add new trading metrics section
|
||||
trading_metrics = f"""
|
||||
Trading Metrics:
|
||||
--------------
|
||||
Avg Expected VWAP Change: {self.metrics['avg_expected_vwap_change']:.2%}
|
||||
Avg Volatility Estimate: {self.metrics['avg_volatility_estimate']:.2%}
|
||||
|
||||
Price Target Analysis:
|
||||
-------------------
|
||||
Entry Success Rate: {self.metrics['price_targets']['entry_success_rate']:.2%}
|
||||
Stop Loss Hits: {self.metrics['price_targets']['stop_loss_hits']:.2%}
|
||||
Take Profit Hits: {self.metrics['price_targets']['take_profit_hits']:.2%}
|
||||
Avg Risk/Reward Ratio: {self.metrics['price_targets']['avg_risk_reward']:.2f}
|
||||
|
||||
Top Signals:
|
||||
----------
|
||||
{self._format_top_signals()}
|
||||
|
||||
Time Coverage:
|
||||
-----------
|
||||
Start: {self.predictions_df['prediction_timestamp'].min()}
|
||||
End: {self.predictions_df['prediction_timestamp'].max()}
|
||||
"""
|
||||
|
||||
return existing_report + trading_metrics
|
||||
141
market_predictor/prediction_service.py
Normal file
141
market_predictor/prediction_service.py
Normal file
@ -0,0 +1,141 @@
|
||||
import asyncio
|
||||
import pandas as pd
|
||||
from datetime import datetime, date
|
||||
from typing import List, Dict, Optional, Callable, Tuple
|
||||
from .data_processor import MarketDataProcessor
|
||||
from .rag_engine import RAGEngine
|
||||
|
||||
class PredictionService:
|
||||
def __init__(self,
|
||||
market_data: pd.DataFrame,
|
||||
training_window_size: int = 78,
|
||||
inference_window_size: int = 12,
|
||||
inference_offset: int = 0,
|
||||
max_concurrent: int = 3):
|
||||
self.processor = MarketDataProcessor(market_data)
|
||||
self.engine = RAGEngine()
|
||||
self.semaphore = asyncio.Semaphore(max_concurrent)
|
||||
self.training_window_size = training_window_size
|
||||
self.inference_window_size = inference_window_size
|
||||
self.inference_offset = inference_offset
|
||||
|
||||
def get_rolling_windows(self, window_days: int = 4) -> List[Tuple[pd.DataFrame, pd.DataFrame]]:
|
||||
df = self.processor.df.copy()
|
||||
dates = pd.to_datetime(df.index.date).unique()
|
||||
windows = []
|
||||
|
||||
for i in range(len(dates) - window_days):
|
||||
train_dates = dates[i:i+window_days]
|
||||
test_date = dates[i+window_days]
|
||||
|
||||
train_mask = df.index.date.isin([d.date() for d in train_dates])
|
||||
test_mask = df.index.date == test_date.date()
|
||||
|
||||
train_data = df[train_mask]
|
||||
test_data = df[test_mask]
|
||||
|
||||
if not train_data.empty and not test_data.empty:
|
||||
windows.append((train_data, test_data))
|
||||
|
||||
return windows
|
||||
|
||||
async def process_batch(
|
||||
self,
|
||||
window_size: int = 4,
|
||||
callback: Optional[Callable] = None
|
||||
) -> List[Dict]:
|
||||
windows = self.get_rolling_windows(window_size)
|
||||
predictions = []
|
||||
|
||||
for train_data, test_data in windows:
|
||||
# Process windows and generate predictions
|
||||
train_windows = self.processor.create_windows(train_data)
|
||||
self.engine.create_vectorstore(train_windows)
|
||||
|
||||
test_windows = self.processor.create_windows(test_data)
|
||||
for window, timestamp in zip(test_windows, test_data.index):
|
||||
prediction = await self.predict_window(window, timestamp)
|
||||
if prediction:
|
||||
predictions.append(prediction)
|
||||
|
||||
if callback:
|
||||
callback()
|
||||
|
||||
return predictions
|
||||
|
||||
def split_data(self, train_ratio: float = 0.8) -> tuple:
|
||||
"""Split data into training and testing periods"""
|
||||
total_periods = len(self.processor.df)
|
||||
train_size = int(total_periods * train_ratio)
|
||||
|
||||
train_data = self.processor.df.iloc[:train_size]
|
||||
test_data = self.processor.df.iloc[train_size:]
|
||||
|
||||
return train_data, test_data
|
||||
|
||||
async def predict_window(self, historical_data: pd.DataFrame, current_window: pd.DataFrame, timestamp: datetime) -> Dict:
|
||||
async with self.semaphore:
|
||||
try:
|
||||
# Create historical context excluding current window
|
||||
historical_contexts = self.processor.create_windows(historical_data, window_size=self.inference_window_size)
|
||||
self.engine.create_vectorstore(historical_contexts)
|
||||
|
||||
# Generate current context
|
||||
current_context = self.processor.generate_description(current_window)
|
||||
prediction = await self.engine.predict(current_context, timestamp)
|
||||
if not prediction:
|
||||
print(f"No prediction generated for timestamp {timestamp}")
|
||||
return prediction
|
||||
except Exception as e:
|
||||
print(f"Error in predict_window: {e}")
|
||||
return None
|
||||
|
||||
async def generate_rolling_predictions(self):
|
||||
data = self.processor.df.copy().sort_index()
|
||||
total_size = self.training_window_size + self.inference_offset + self.inference_window_size
|
||||
total_windows = len(data) - total_size
|
||||
|
||||
for i in range(total_windows):
|
||||
historical_start = i
|
||||
historical_end = i + self.training_window_size
|
||||
historical_data = data.iloc[historical_start:historical_end]
|
||||
|
||||
current_start = historical_end + self.inference_offset
|
||||
current_end = current_start + self.inference_window_size
|
||||
current_window = data.iloc[current_start:current_end]
|
||||
|
||||
if current_end < len(data):
|
||||
prediction_timestamp = data.index[current_end]
|
||||
prediction = await self.predict_window(
|
||||
historical_data=historical_data,
|
||||
current_window=current_window,
|
||||
timestamp=prediction_timestamp
|
||||
)
|
||||
if prediction:
|
||||
prediction.update({
|
||||
'historical_start': historical_data.index[0],
|
||||
'historical_end': historical_data.index[-1],
|
||||
'current_window_start': current_window.index[0],
|
||||
'current_window_end': current_window.index[-1],
|
||||
'prediction_timestamp': prediction_timestamp
|
||||
})
|
||||
yield prediction
|
||||
else:
|
||||
print(f"No prediction for window ending at {prediction_timestamp}")
|
||||
else:
|
||||
print(f"Skipping window ending at {current_end} due to insufficient data")
|
||||
|
||||
async def batch_predictions(
|
||||
market_data: pd.DataFrame,
|
||||
training_window_size: int = 78, # 1 trading day
|
||||
inference_window_size: int = 12, # 1 hour
|
||||
inference_offset: int = 0
|
||||
) -> pd.DataFrame:
|
||||
service = PredictionService(
|
||||
market_data,
|
||||
training_window_size=training_window_size,
|
||||
inference_window_size=inference_window_size,
|
||||
inference_offset=inference_offset
|
||||
)
|
||||
predictions = await service.generate_rolling_predictions()
|
||||
return pd.DataFrame(predictions)
|
||||
163
market_predictor/rag_engine.py
Normal file
163
market_predictor/rag_engine.py
Normal file
@ -0,0 +1,163 @@
|
||||
import asyncio
|
||||
from typing import List, Dict, Optional
|
||||
from .config import OPENAI_API_KEY, MODEL_NAME, EMBEDDING_MODEL
|
||||
from langchain_core.messages import HumanMessage, SystemMessage
|
||||
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
|
||||
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
|
||||
from langchain_community.vectorstores import FAISS
|
||||
import pandas as pd
|
||||
import json
|
||||
import numpy as np
|
||||
import logging
|
||||
|
||||
|
||||
class RAGEngine:
|
||||
def __init__(self):
|
||||
self.embeddings = OpenAIEmbeddings(
|
||||
openai_api_key=OPENAI_API_KEY, model=EMBEDDING_MODEL
|
||||
)
|
||||
self.llm = ChatOpenAI(
|
||||
openai_api_key=OPENAI_API_KEY, model=MODEL_NAME, temperature=0.1
|
||||
)
|
||||
self.vectorstore = None
|
||||
|
||||
# Define system prompt
|
||||
self.system_prompt = """
|
||||
You are tasked with predicting the VWAP movement for the next 5-minute interval based on historical and current market data. Your goal is to maximize predictive accuracy, estimate the magnitude of movement, assess volatility, and provide actionable trading insights.
|
||||
|
||||
Input Data:
|
||||
Historical Context Windows:
|
||||
|
||||
These represent the top 5 most similar past market windows, selected based on their resemblance to the current market window.
|
||||
Each historical window consists of a variable number of 5-minute intervals leading up to but not including the current window.
|
||||
The length of each window is flexible, meaning the amount of time covered varies.
|
||||
These historical windows help identify patterns that preceded previous VWAP movements.
|
||||
Current Window:
|
||||
|
||||
A dynamically sized collection of recent 5-minute intervals, with the latest interval being the most recent market data available.
|
||||
The next interval (immediately following the latest one) is what you need to predict.
|
||||
Available market metrics for the latest interval include:
|
||||
Price (Current)
|
||||
VWAP (Current)
|
||||
Volume
|
||||
MA5 (5-interval moving average)
|
||||
MA20 (20-interval moving average)
|
||||
Volume MA5 (5-interval volume moving average)
|
||||
Price Change from Previous 5-Minute Interval
|
||||
Volume Change from Previous 5-Minute Interval
|
||||
VWAP Change from Previous 5-Minute Interval
|
||||
Your Tasks:
|
||||
Analyze the Current Window:
|
||||
|
||||
Extract key trends, anomalies, or signals from the most recent market data.
|
||||
Compare Against Historical Context:
|
||||
|
||||
Identify how similar historical windows evolved after reaching a state comparable to the current window.
|
||||
Detect patterns or indicators that historically preceded VWAP increases or decreases.
|
||||
Predict the Next Interval’s VWAP Movement:
|
||||
|
||||
Determine if VWAP is likely to move up or down in the next 5-minute interval.
|
||||
Provide a confidence score (0.0 to 1.0) reflecting the probability of correctness based on historical precedent and current conditions.
|
||||
Estimate Movement Magnitude and Volatility:
|
||||
|
||||
Predict the expected VWAP change (absolute or percentage).
|
||||
Assess short-term volatility, considering recent fluctuations and historical variance.
|
||||
Optimize Trade Execution and Risk Management:
|
||||
|
||||
Recommend ideal trade levels based on the prediction:
|
||||
Suggested Entry Price
|
||||
Suggested Stop-Loss Level
|
||||
Suggested Take-Profit Level
|
||||
Ensure these levels reflect realistic market conditions and risk-adjusted reward expectations.
|
||||
Identify Key Signals Used in Prediction:
|
||||
|
||||
Clearly list which features, patterns, or statistical relationships influenced the decision.
|
||||
Provide Justification for the Prediction:
|
||||
|
||||
Offer a concise, data-driven reasoning explaining why the prediction was made, how historical patterns support it, and why the suggested trade levels are optimal.
|
||||
Output Format:
|
||||
Your response must be structured in strict JSON format as follows:
|
||||
|
||||
{
|
||||
"vwap_direction_next_5min": "up" or "down",
|
||||
"confidence_score": <float between 0.0 and 1.0>,
|
||||
"expected_vwap_change": <float (absolute or percentage)>,
|
||||
"volatility_estimate": <float (estimated short-term volatility)>,
|
||||
"suggested_entry": <float (price level)>,
|
||||
"suggested_stop_loss": <float (price level)>,
|
||||
"suggested_take_profit": <float (price level)>,
|
||||
"key_signals": ["list of relevant signals or indicators used"],
|
||||
"reasoning": "concise explanation of the logic behind your prediction and recommendations"
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
def calculate_recency_weights(self, num_contexts: int) -> np.ndarray:
|
||||
"""Calculate exponential decay weights for contexts"""
|
||||
decay_rate = 0.1 # Adjustable decay rate
|
||||
positions = np.arange(num_contexts)
|
||||
weights = np.exp(-decay_rate * (num_contexts - 1 - positions))
|
||||
return weights / weights.sum() # Normalize weights
|
||||
|
||||
def create_vectorstore(self, texts: List[str]):
|
||||
"""Create weighted vectorstore with recency bias"""
|
||||
weights = self.calculate_recency_weights(len(texts))
|
||||
|
||||
# Create metadata with weights
|
||||
metadatas = [
|
||||
{
|
||||
"index": i,
|
||||
"recency_weight": float(weights[i]) # Convert to float for JSON
|
||||
}
|
||||
for i in range(len(texts))
|
||||
]
|
||||
|
||||
self.vectorstore = FAISS.from_texts(
|
||||
texts, self.embeddings, metadatas=metadatas
|
||||
)
|
||||
|
||||
async def predict(self, query: str, timestamp: pd.Timestamp) -> Optional[Dict]:
|
||||
try:
|
||||
similar_docs = self.vectorstore.similarity_search(
|
||||
query,
|
||||
k=5,
|
||||
search_kwargs={"score_threshold": 0.5}
|
||||
)
|
||||
|
||||
# Build context string
|
||||
context = "\n".join([doc.page_content for doc in similar_docs])
|
||||
|
||||
messages = [
|
||||
SystemMessage(content=self.system_prompt),
|
||||
HumanMessage(content=f"Historical context:\n{context}\n\nCurrent market state:\n{query}")
|
||||
]
|
||||
|
||||
# Get LLM response
|
||||
response = await self.llm.ainvoke(messages)
|
||||
raw_content = response.content.strip()
|
||||
|
||||
# Log raw response for debugging
|
||||
logging.debug(f"Raw LLM response: {raw_content}")
|
||||
|
||||
if not raw_content:
|
||||
logging.error("Empty response from LLM")
|
||||
return None
|
||||
|
||||
try:
|
||||
# Ensure valid JSON format
|
||||
if not raw_content.startswith('{'):
|
||||
raw_content = '{' + raw_content.split('{', 1)[1]
|
||||
if not raw_content.endswith('}'):
|
||||
raw_content = raw_content.rsplit('}', 1)[0] + '}'
|
||||
|
||||
prediction = json.loads(raw_content)
|
||||
prediction['timestamp_prediction'] = timestamp
|
||||
return prediction
|
||||
|
||||
except json.JSONDecodeError as je:
|
||||
logging.error(f"JSON decode error: {je}. Raw content: {raw_content}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Prediction error: {str(e)}")
|
||||
return None
|
||||
93
market_predictor/test.py
Normal file
93
market_predictor/test.py
Normal file
@ -0,0 +1,93 @@
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from datetime import datetime, timedelta
|
||||
import asyncio
|
||||
from prediction_service import PredictionService
|
||||
from performance_metrics import PerformanceMetrics
|
||||
|
||||
def create_test_market_data():
|
||||
"""Create small synthetic dataset for testing"""
|
||||
dates = pd.date_range(
|
||||
start='2024-01-01 09:30:00',
|
||||
end='2024-01-01 10:30:00',
|
||||
freq='5min',
|
||||
tz='America/New_York'
|
||||
)
|
||||
|
||||
# Generate synthetic price data
|
||||
np.random.seed(42)
|
||||
data = {
|
||||
'Open': np.random.uniform(100, 101, len(dates)),
|
||||
'High': np.random.uniform(101, 102, len(dates)),
|
||||
'Low': np.random.uniform(99, 100, len(dates)),
|
||||
'Close': np.random.uniform(100, 101, len(dates)),
|
||||
'Volume': np.random.uniform(1000, 2000, len(dates)),
|
||||
'VWAP': np.random.uniform(100, 101, len(dates))
|
||||
}
|
||||
|
||||
df = pd.DataFrame(data, index=dates)
|
||||
return df
|
||||
|
||||
def create_test_predictions():
|
||||
"""Create sample predictions for testing"""
|
||||
return [
|
||||
{
|
||||
'timestamp_prediction': '2024-01-01 09:35:00-05:00',
|
||||
'vwap_prediction_next_5min': 'up',
|
||||
'confidence_score': 0.75
|
||||
},
|
||||
{
|
||||
'timestamp_prediction': '2024-01-01 09:40:00-05:00',
|
||||
'vwap_prediction_next_5min': 'down',
|
||||
'confidence_score': 0.65
|
||||
},
|
||||
{
|
||||
'timestamp_prediction': '2024-01-01 09:45:00-05:00',
|
||||
'vwap_prediction_next_5min': 'up',
|
||||
'confidence_score': 0.80
|
||||
}
|
||||
]
|
||||
|
||||
async def test_full_pipeline():
|
||||
try:
|
||||
# Create test data
|
||||
market_data = create_test_market_data()
|
||||
print("\nTest Market Data:")
|
||||
print(market_data.head())
|
||||
|
||||
# Test prediction service
|
||||
service = PredictionService(market_data)
|
||||
predictions = await service.process_batch(window_size=5)
|
||||
print("\nPredictions:", predictions)
|
||||
|
||||
if not predictions:
|
||||
raise ValueError("No predictions generated")
|
||||
|
||||
# Test metrics calculation
|
||||
metrics = PerformanceMetrics(predictions, market_data)
|
||||
report = metrics.generate_report()
|
||||
print("\nPerformance Report:")
|
||||
print(report)
|
||||
|
||||
# Visualize results
|
||||
metrics.plot_confusion_matrix()
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error in pipeline: {str(e)}")
|
||||
raise
|
||||
|
||||
async def test_metrics_only():
|
||||
"""Test metrics calculation with synthetic data"""
|
||||
market_data = create_test_market_data()
|
||||
predictions = create_test_predictions()
|
||||
|
||||
metrics = PerformanceMetrics(predictions, market_data)
|
||||
print(metrics.generate_report())
|
||||
metrics.plot_confusion_matrix()
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Test full pipeline
|
||||
asyncio.run(test_full_pipeline())
|
||||
|
||||
# Or test metrics only
|
||||
# asyncio.run(test_metrics_only())
|
||||
0
notebooks/analysis.py
Normal file
0
notebooks/analysis.py
Normal file
168
notebooks/rolling_window_analysis.ipynb
Normal file
168
notebooks/rolling_window_analysis.ipynb
Normal file
@ -0,0 +1,168 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Rolling Window Market Movement Analysis\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Fetched 316 rows of data.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import sys\n",
|
||||
"sys.path.append('../')\n",
|
||||
"import asyncio\n",
|
||||
"import pandas as pd\n",
|
||||
"import numpy as np\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"import seaborn as sns\n",
|
||||
"from datetime import datetime, timedelta\n",
|
||||
"from IPython.display import display\n",
|
||||
"from tqdm.notebook import tqdm\n",
|
||||
"\n",
|
||||
"from market_predictor.market_data_fetcher import MarketDataFetcher\n",
|
||||
"from market_predictor.data_processor import MarketDataProcessor\n",
|
||||
"from market_predictor.rag_engine import RAGEngine\n",
|
||||
"from market_predictor.performance_metrics import PerformanceMetrics\n",
|
||||
"from market_predictor.prediction_service import PredictionService\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"symbol = \"BTC-USD\"\n",
|
||||
"end_date = datetime.now()\n",
|
||||
"start_date = end_date - timedelta(days=5)\n",
|
||||
"fetcher = MarketDataFetcher(symbol)\n",
|
||||
"market_data = fetcher.fetch_data(\n",
|
||||
" start_date=start_date.strftime('%Y-%m-%d'),\n",
|
||||
" end_date=end_date.strftime('%Y-%m-%d'),\n",
|
||||
" interval='5m'\n",
|
||||
")\n",
|
||||
"print(f\"Fetched {len(market_data)} rows of data.\")\n",
|
||||
"\n",
|
||||
"rag_engine = RAGEngine()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Define Rolling Window Prediction Functions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Processing: 58%|████████████████▉ | 142/244 [10:00<18:10, 10.69s/it]"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import asyncio\n",
|
||||
"import pandas as pd\n",
|
||||
"from tqdm import tqdm\n",
|
||||
"import nest_asyncio\n",
|
||||
"\n",
|
||||
"# Enable nested event loops\n",
|
||||
"nest_asyncio.apply()\n",
|
||||
"\n",
|
||||
"async def analyze_market_data(\n",
|
||||
" market_data: pd.DataFrame,\n",
|
||||
" training_window_size: int = 36,\n",
|
||||
" inference_window_size: int = 12,\n",
|
||||
" inference_offset: int = 0\n",
|
||||
") -> pd.DataFrame:\n",
|
||||
" processor = MarketDataProcessor(market_data)\n",
|
||||
" processed_data = processor.df\n",
|
||||
" \n",
|
||||
" service = PredictionService(\n",
|
||||
" market_data=processed_data,\n",
|
||||
" training_window_size=training_window_size,\n",
|
||||
" inference_window_size=inference_window_size,\n",
|
||||
" inference_offset=inference_offset\n",
|
||||
" )\n",
|
||||
" \n",
|
||||
" total_size = training_window_size + inference_offset + inference_window_size\n",
|
||||
" total_windows = len(processed_data) - total_size\n",
|
||||
" \n",
|
||||
" predictions = []\n",
|
||||
" with tqdm(total=total_windows, desc=\"Processing\", ncols=80) as pbar:\n",
|
||||
" async for pred in service.generate_rolling_predictions():\n",
|
||||
" if pred:\n",
|
||||
" # print(pred)\n",
|
||||
" predictions.append(pred)\n",
|
||||
" pbar.update(1)\n",
|
||||
" \n",
|
||||
" return pd.DataFrame(predictions) if predictions else pd.DataFrame()\n",
|
||||
"\n",
|
||||
"# Run analysis\n",
|
||||
"try:\n",
|
||||
" predictions_df = await analyze_market_data(\n",
|
||||
" market_data,\n",
|
||||
" training_window_size=60,\n",
|
||||
" inference_window_size=12,\n",
|
||||
" inference_offset=0\n",
|
||||
" )\n",
|
||||
" \n",
|
||||
" if not predictions_df.empty:\n",
|
||||
" metrics = PerformanceMetrics(predictions_df, market_data)\n",
|
||||
" report = metrics.generate_report()\n",
|
||||
" print(\"\\nPerformance Report:\")\n",
|
||||
" print(report)\n",
|
||||
" \n",
|
||||
" print(\"\\nPredictions Summary:\")\n",
|
||||
" print(predictions_df.head())\n",
|
||||
" else:\n",
|
||||
" print(\"No predictions generated\")\n",
|
||||
" \n",
|
||||
"except Exception as e:\n",
|
||||
" print(f\"Analysis failed: {str(e)}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "DeepSeek Playground (Poetry)",
|
||||
"language": "python",
|
||||
"name": "deepseek_playground"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.8"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
4706
poetry.lock
generated
Normal file
4706
poetry.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
86
predictions.csv
Normal file
86
predictions.csv
Normal file
@ -0,0 +1,86 @@
|
||||
,vwap_direction_next_5min,confidence_score,expected_vwap_change,volatility_estimate,suggested_entry,suggested_stop_loss,suggested_take_profit,key_signals,reasoning,timestamp_prediction,historical_start,historical_end,current_window_start,current_window_end,prediction_timestamp,actual_movement
|
||||
0,down,0.8,0.0,0.0,102019.78582889185,102319.16389182815,101720.40776595555,"['Decreasing volume trend', 'Price below VWAP']","The volume has been decreasing, suggesting potential weakness in price momentum. With the price below VWAP, this supports a bearish outlook.",2025-01-29 15:30:00+00:00,2025-01-29 09:30:00+00:00,2025-01-29 14:25:00+00:00,2025-01-29 14:30:00+00:00,2025-01-29 15:25:00+00:00,2025-01-29 15:30:00+00:00,down
|
||||
1,down,0.8,0.0,0.0,102019.79,102319.79,101719.79,"['Decreasing volume trend', 'Price below VWAP']","The volume has been consistently low, suggesting potential trend weakness. With the price below VWAP, momentum is likely downwards.",2025-01-29 15:35:00+00:00,2025-01-29 09:35:00+00:00,2025-01-29 14:30:00+00:00,2025-01-29 14:35:00+00:00,2025-01-29 15:30:00+00:00,2025-01-29 15:35:00+00:00,down
|
||||
2,down,0.8,0.0,0.0,102019.78582790137,102319.16392824418,101720.40773755858,"['Decreasing volume trend', 'Price below VWAP']","The volume has been decreasing, suggesting potential weakness in price momentum. With the price below the VWAP, this supports a bearish outlook.",2025-01-29 15:40:00+00:00,2025-01-29 09:40:00+00:00,2025-01-29 14:35:00+00:00,2025-01-29 14:40:00+00:00,2025-01-29 15:35:00+00:00,2025-01-29 15:40:00+00:00,down
|
||||
3,down,0.8,0.0,0.0,102019.78582889154,102319.1638918287,101720.4077659544,"['Decreasing volume trend', 'VWAP below price']",The absence of volume suggests potential weakness in price movement. Historical patterns show that decreasing volume often precedes VWAP declines.,2025-01-29 15:45:00+00:00,2025-01-29 09:45:00+00:00,2025-01-29 14:40:00+00:00,2025-01-29 14:45:00+00:00,2025-01-29 15:40:00+00:00,2025-01-29 15:45:00+00:00,down
|
||||
4,down,0.8,0.0,0.0,102019.79,102319.79,101719.79,"['Decreasing volume trend', 'VWAP below price']","With the volume at zero, it suggests a lack of momentum. Historical patterns show that decreasing volume often precedes VWAP declines.",2025-01-29 15:50:00+00:00,2025-01-29 09:50:00+00:00,2025-01-29 14:45:00+00:00,2025-01-29 14:50:00+00:00,2025-01-29 15:45:00+00:00,2025-01-29 15:50:00+00:00,down
|
||||
5,down,0.8,0.0,0.0,102019.79,102319.79,101719.78,"['Decreasing volume trend', 'VWAP direction stability']","With volume at zero, the lack of market activity suggests potential for VWAP to trend downwards. Historical patterns support this, as similar conditions led to declining VWAP.",2025-01-29 15:55:00+00:00,2025-01-29 09:55:00+00:00,2025-01-29 14:50:00+00:00,2025-01-29 14:55:00+00:00,2025-01-29 15:50:00+00:00,2025-01-29 15:55:00+00:00,down
|
||||
6,down,0.8,0.0,0.10246417681014576,102019.64,102319.81,101719.47,"['Decreasing volume trend', 'Price below VWAP']","The decreasing volume suggests potential weakness in price momentum. With the price below VWAP, bearish momentum is supported.",2025-01-29 16:00:00+00:00,2025-01-29 10:00:00+00:00,2025-01-29 14:55:00+00:00,2025-01-29 15:00:00+00:00,2025-01-29 15:55:00+00:00,2025-01-29 16:00:00+00:00,down
|
||||
7,down,0.8,0.0,0.4472135954999579,102019.64,102319.64,101719.64,"['Decreasing volume trend', 'Price below VWAP']","The decreasing volume trend suggests potential weakness in price momentum. With the price below the VWAP, this supports a bearish outlook.",2025-01-30 09:30:00+00:00,2025-01-29 10:05:00+00:00,2025-01-29 15:00:00+00:00,2025-01-29 15:05:00+00:00,2025-01-29 16:00:00+00:00,2025-01-30 09:30:00+00:00,up
|
||||
8,down,0.8,0.0,0.0,102019.79,102319.68,101719.89,"['Decreasing volume trend', 'Price below VWAP']","The decreasing volume trend suggests potential weakness in price momentum. With the price below VWAP, bearish momentum is supported.",2025-01-30 09:35:00+00:00,2025-01-29 10:10:00+00:00,2025-01-29 15:05:00+00:00,2025-01-29 15:10:00+00:00,2025-01-30 09:30:00+00:00,2025-01-30 09:35:00+00:00,up
|
||||
9,up,0.8,0.0008024852011952044,0.8832629343466463,102128.96078431373,102128.96078431373,102128.96078431373,"['Increasing volume suggests strong momentum.', 'Price above MA5 indicates bullish trend.']","The increasing volume suggests strong momentum, which historically precedes VWAP increases. The price being above the MA5 supports the bullish momentum.",2025-01-30 09:40:00+00:00,2025-01-29 10:15:00+00:00,2025-01-29 15:10:00+00:00,2025-01-29 15:15:00+00:00,2025-01-30 09:35:00+00:00,2025-01-30 09:40:00+00:00,up
|
||||
10,up,0.8,0.0017757757757757757,3.172217456456975,102220.91355997595,102220.91355997595,102220.91355997595,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.']","The increasing volume suggests strong momentum, which historically precedes VWAP increases. The price being above the VWAP further supports the bullish momentum.",2025-01-30 09:45:00+00:00,2025-01-29 10:20:00+00:00,2025-01-29 15:15:00+00:00,2025-01-29 15:20:00+00:00,2025-01-30 09:40:00+00:00,2025-01-30 09:45:00+00:00,down
|
||||
11,up,0.8,0.0005306792001956373,0.9517347922785129,102226.2252145539,102195.2252145539,102257.2252145539,"['Increasing MA5 suggests bullish momentum', 'Decreasing volume suggests potential trend weakness']","The increasing 5-interval moving average (MA5) indicates bullish momentum. Additionally, the decreasing volume suggests potential trend weakness, which historically has preceded VWAP increases.",2025-01-30 09:50:00+00:00,2025-01-29 10:25:00+00:00,2025-01-29 15:20:00+00:00,2025-01-29 15:25:00+00:00,2025-01-30 09:45:00+00:00,2025-01-30 09:50:00+00:00,up
|
||||
12,down,0.8,0.0,1.519897975572075e-11,102226.2254979146,102448.2154976327,102004.23549819647,"['Decreasing volume trend', 'Price below VWAP']","The volume has been decreasing, suggesting potential trend weakness. With the price below VWAP, this supports a bearish momentum.",2025-01-30 09:55:00+00:00,2025-01-29 10:30:00+00:00,2025-01-29 15:25:00+00:00,2025-01-29 15:30:00+00:00,2025-01-30 09:50:00+00:00,2025-01-30 09:55:00+00:00,down
|
||||
13,up,0.8,0.01016753619889414,3.080496225279867,102400.61568627966,102099.99999999999,102701.23137255934,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.', 'Recent uptrend in price and VWAP.']","The increasing volume indicates strong momentum, and the price being above the VWAP supports bullish momentum. Historical patterns show that similar conditions led to VWAP increases.",2025-01-30 10:00:00+00:00,2025-01-29 10:35:00+00:00,2025-01-29 15:30:00+00:00,2025-01-29 15:35:00+00:00,2025-01-30 09:55:00+00:00,2025-01-30 10:00:00+00:00,down
|
||||
14,down,0.8,0.0,2.519897975572075e-11,102400.61568656725,102701.61568656725,102099.61568656725,"['Decreasing volume trend', 'Price below VWAP']",The model predicts a downtrend in VWAP due to the current price being below VWAP and the decreasing volume trend suggesting potential weakness in price momentum.,2025-01-30 10:05:00+00:00,2025-01-29 10:40:00+00:00,2025-01-29 15:35:00+00:00,2025-01-29 15:40:00+00:00,2025-01-30 10:00:00+00:00,2025-01-30 10:05:00+00:00,up
|
||||
15,up,0.8,0.0002674551669752016,0.0,102400.6156865531,102108.58384325738,102692.64752984882,"['Increasing MA5 suggests bullish momentum.', 'Current price above VWAP indicates potential upward movement.']","The current price is above the VWAP, suggesting bullish momentum. Additionally, the increasing 5-interval moving average supports the expectation of rising prices.",2025-01-30 10:10:00+00:00,2025-01-29 10:45:00+00:00,2025-01-29 15:40:00+00:00,2025-01-29 15:45:00+00:00,2025-01-30 10:05:00+00:00,2025-01-30 10:10:00+00:00,up
|
||||
16,up,0.8,0.004254156305755225,2.150259452057113,102464.1048438104,102432.69372180967,102495.51596581112,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.', 'Recent uptrend in price and VWAP.']","The increasing volume indicates strong momentum, which historically precedes VWAP increases. The price being above the VWAP further supports the bullish momentum.",2025-01-30 10:15:00+00:00,2025-01-29 10:50:00+00:00,2025-01-29 15:45:00+00:00,2025-01-29 15:50:00+00:00,2025-01-30 10:10:00+00:00,2025-01-30 10:15:00+00:00,down
|
||||
17,up,0.8,0.0002615631762852015,0.9198126409195766,102500.7056515531,102474.28256583738,102527.12873726882,"['Increasing volume suggests strong momentum.', 'Price above MA5 indicates bullish momentum.', 'VWAP below price supports upward pressure.']","The increasing volume suggests strong momentum, supporting the likelihood of VWAP increasing. Additionally, the price being above the MA5 reinforces bullish momentum.",2025-01-30 10:20:00+00:00,2025-01-29 10:55:00+00:00,2025-01-29 15:50:00+00:00,2025-01-29 15:55:00+00:00,2025-01-30 10:15:00+00:00,2025-01-30 10:20:00+00:00,up
|
||||
18,down,0.8,0.0,1.519897975572075e-11,102500.7083378504,102800.7083378504,102200.7083378504,"['Decreasing volume trend', 'Price below VWAP']","The decreasing volume trend suggests potential weakness in price momentum. With the price below the VWAP, this supports a bearish momentum expectation.",2025-01-30 10:25:00+00:00,2025-01-29 11:00:00+00:00,2025-01-29 15:55:00+00:00,2025-01-29 16:00:00+00:00,2025-01-30 10:20:00+00:00,2025-01-30 10:25:00+00:00,down
|
||||
19,up,0.8,0.004066197823123973,1.519897975572075e-11,102533.3086709204,102531.94893508816,102534.66840675264,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.']","The increasing volume indicates strong momentum, and the price being above the VWAP supports the likelihood of further upward movement.",2025-01-30 10:30:00+00:00,2025-01-29 11:05:00+00:00,2025-01-29 16:00:00+00:00,2025-01-30 09:30:00+00:00,2025-01-30 10:25:00+00:00,2025-01-30 10:30:00+00:00,up
|
||||
20,down,0.8,0.0,3.773634474201897,102533.30868892015,102835.97479236356,102230.64258547673,"['Decreasing volume trend', 'Price below VWAP']","The decreasing volume trend suggests potential weakness in price momentum. With the price below the VWAP, this supports a bearish outlook.",2025-01-30 10:35:00+00:00,2025-01-29 11:10:00+00:00,2025-01-30 09:30:00+00:00,2025-01-30 09:35:00+00:00,2025-01-30 10:30:00+00:00,2025-01-30 10:35:00+00:00,up
|
||||
21,up,0.8,0.0001844341987930192,0.1021654086323041,102536.0606064344,102505.16963478833,102566.95157808048,"['Increasing MA5', 'Decreasing Volume', 'Price above VWAP']","The price is above the VWAP, suggesting bullish momentum. The decreasing volume indicates potential trend weakness, supporting the uptrend.",2025-01-30 10:40:00+00:00,2025-01-29 11:15:00+00:00,2025-01-30 09:35:00+00:00,2025-01-30 09:40:00+00:00,2025-01-30 10:35:00+00:00,2025-01-30 10:40:00+00:00,down
|
||||
22,up,0.8,0.003685074303660905,0.6371996655205863,102541.95099077058,102511.55088876728,102572.35109277388,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.']","The increasing volume indicates strong momentum, which historically precedes VWAP increases. The price being above the VWAP further supports the bullish momentum.",2025-01-30 10:45:00+00:00,2025-01-29 11:20:00+00:00,2025-01-30 09:40:00+00:00,2025-01-30 09:45:00+00:00,2025-01-30 10:40:00+00:00,2025-01-30 10:45:00+00:00,down
|
||||
23,up,0.8,0.0001332398231982678,0.9058005198435951,102541.95199747257,102512.53185693568,102571.37213800946,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.']","The increase in volume indicates strong market momentum, which historically precedes VWAP increases. The current price being above the VWAP further supports the bullish momentum.",2025-01-30 10:50:00+00:00,2025-01-29 11:25:00+00:00,2025-01-30 09:45:00+00:00,2025-01-30 09:50:00+00:00,2025-01-30 10:45:00+00:00,2025-01-30 10:50:00+00:00,down
|
||||
24,down,0.8,0.0,0.3092480809735721,102541.95071091194,102841.95071091196,102241.95071091193,"['Decreasing volume trend', 'Price below VWAP']","The decreasing volume trend suggests potential weakness in price momentum. With the price below the VWAP, this supports a bearish outlook.",2025-01-30 10:55:00+00:00,2025-01-29 11:30:00+00:00,2025-01-30 09:50:00+00:00,2025-01-30 09:55:00+00:00,2025-01-30 10:50:00+00:00,2025-01-30 10:55:00+00:00,down
|
||||
25,up,0.8,0.0008942067500075164,0.1025410959512574,102541.95199645754,102511.53189691539,102572.37209699971,"['Decreasing volume suggests potential trend weakness.', 'Price above VWAP supports bullish momentum.']",The increasing price relative to a stable or decreasing volume suggests potential bullish momentum. Historical patterns show that similar conditions led to VWAP increases.,2025-01-30 11:00:00+00:00,2025-01-29 11:35:00+00:00,2025-01-30 09:55:00+00:00,2025-01-30 10:00:00+00:00,2025-01-30 10:55:00+00:00,2025-01-30 11:00:00+00:00,down
|
||||
26,down,0.8,0.0,0.0,105269.0,105569.5,104968.5,"['Decreasing volume trend', 'Price below VWAP']","The decreasing volume trend suggests potential weakness in price momentum. With the price below VWAP, bearish momentum is supported.",2025-01-30 11:05:00+00:00,2025-01-29 11:40:00+00:00,2025-01-30 10:00:00+00:00,2025-01-30 10:05:00+00:00,2025-01-30 11:00:00+00:00,2025-01-30 11:05:00+00:00,down
|
||||
27,down,0.8,0.0,0.0,102541.95199091196,102843.2682890059,102240.63569281701,"['Decreasing volume trend', 'Price below VWAP']","The decreasing volume trend suggests potential weakness in price momentum. With the price below VWAP, this supports the expectation of further downward pressure.",2025-01-30 11:10:00+00:00,2025-01-29 11:45:00+00:00,2025-01-30 10:05:00+00:00,2025-01-30 10:10:00+00:00,2025-01-30 11:05:00+00:00,2025-01-30 11:10:00+00:00,up
|
||||
28,down,0.8,0.0,0.0,102541.95206699694,102833.20464825266,102250.6994837412,"['Decreasing volume trend', 'Price below VWAP']","The decreasing volume trend suggests potential weakness in price momentum. With the price below the VWAP, this supports a bearish outlook.",2025-01-30 11:15:00+00:00,2025-01-29 11:50:00+00:00,2025-01-30 10:10:00+00:00,2025-01-30 10:15:00+00:00,2025-01-30 11:10:00+00:00,2025-01-30 11:15:00+00:00,up
|
||||
29,up,0.8,0.004637080013832198,0.3025630676146465,105183.20000000001,105132.20000000001,105234.20000000001,"['Decreasing volume suggests potential trend weakness.', 'Price above VWAP supports bullish momentum.']","The decreasing volume suggests potential trend weakness, which is confirmed by the price being above the VWAP. Historical patterns show that similar conditions led to VWAP increases.",2025-01-30 11:20:00+00:00,2025-01-29 11:55:00+00:00,2025-01-30 10:15:00+00:00,2025-01-30 10:20:00+00:00,2025-01-30 11:15:00+00:00,2025-01-30 11:20:00+00:00,down
|
||||
30,up,0.8,0.002225775050257402,0.1641037672571862,105089.115975,104834.68829375,105343.54365625,"['Decreasing volume suggests potential trend weakness.', 'Price above VWAP supports bullish momentum.']","The decreasing volume suggests potential trend weakness, but the price remaining above VWAP supports bullish momentum. Historical patterns show that similar conditions led to VWAP increases.",2025-01-30 11:25:00+00:00,2025-01-29 12:00:00+00:00,2025-01-30 10:20:00+00:00,2025-01-30 10:25:00+00:00,2025-01-30 11:20:00+00:00,2025-01-30 11:25:00+00:00,down
|
||||
31,down,0.8,0.0,0.0,102555.33,102855.33,102255.33,"['Decreasing volume trend', 'Stable price trend']",The decreasing volume suggests potential weakness in price momentum. Historical patterns show that similar conditions led to VWAP declines.,2025-01-30 11:30:00+00:00,2025-01-29 12:05:00+00:00,2025-01-30 10:25:00+00:00,2025-01-30 10:30:00+00:00,2025-01-30 11:25:00+00:00,2025-01-30 11:30:00+00:00,up
|
||||
32,down,0.8,0.0,0.8941272570759758,102555.33333333333,102855.33333333333,102255.33333333333,"['Decreasing volume trend', 'Price below VWAP']","The decreasing volume trend suggests potential weakness in price momentum. With the price below the VWAP, this supports a bearish outlook.",2025-01-30 11:35:00+00:00,2025-01-29 12:10:00+00:00,2025-01-30 10:30:00+00:00,2025-01-30 10:35:00+00:00,2025-01-30 11:30:00+00:00,2025-01-30 11:35:00+00:00,up
|
||||
33,up,0.8,0.0001745431952023703,0.10266520816729575,102563.20141694031,102531.2004169403,102595.20241694032,"['Decreasing volume suggests potential trend weakness.', 'Price above VWAP supports bullish momentum.']","The decreasing volume suggests potential trend weakness, but the price remaining above VWAP supports bullish momentum. Historical patterns show that similar conditions led to VWAP increases.",2025-01-30 11:45:00+00:00,2025-01-29 12:20:00+00:00,2025-01-30 10:40:00+00:00,2025-01-30 10:45:00+00:00,2025-01-30 11:40:00+00:00,2025-01-30 11:45:00+00:00,up
|
||||
34,down,0.8,0.0,0.0,102563.20119595597,102863.20119595597,102263.20119595596,"['Decreasing volume trend', 'Price below VWAP']","The decreasing volume trend suggests potential weakness in price momentum. With the price below VWAP, this supports a bearish outlook.",2025-01-30 11:50:00+00:00,2025-01-29 12:25:00+00:00,2025-01-30 10:45:00+00:00,2025-01-30 10:50:00+00:00,2025-01-30 11:45:00+00:00,2025-01-30 11:50:00+00:00,up
|
||||
35,up,0.8,0.00468527964601999,1.519897975572075e-11,102632.91378364613,102630.91378364613,102634.91378364613,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.']","The increasing volume indicates strong momentum, and the price being above the VWAP supports the likelihood of further upward movement.",2025-01-30 11:55:00+00:00,2025-01-29 12:30:00+00:00,2025-01-30 10:50:00+00:00,2025-01-30 10:55:00+00:00,2025-01-30 11:50:00+00:00,2025-01-30 11:55:00+00:00,down
|
||||
36,up,0.8,0.007329368888888888,1.2571958971988972,105330.854843,105029.454439,105632.255246,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.', 'Recent uptrend in VWAP supports positive price momentum.']","The increasing volume indicates strong momentum, supporting the bullish trend. The price being above the VWAP further reinforces this momentum. Historical patterns show that similar conditions led to VWAP increases.",2025-01-30 12:00:00+00:00,2025-01-29 12:35:00+00:00,2025-01-30 10:55:00+00:00,2025-01-30 11:00:00+00:00,2025-01-30 11:55:00+00:00,2025-01-30 12:00:00+00:00,down
|
||||
37,down,0.8,0.0,3.883501155975197,102639.0909016864,102940.50819547287,102337.67360789992,"['Decreasing volume trend', 'Price below VWAP']","The decreasing volume trend suggests potential weakness in price momentum. With the price below the VWAP, this supports a bearish outlook.",2025-01-30 12:05:00+00:00,2025-01-29 12:40:00+00:00,2025-01-30 11:00:00+00:00,2025-01-30 11:05:00+00:00,2025-01-30 12:00:00+00:00,2025-01-30 12:05:00+00:00,down
|
||||
38,down,0.8,0.0,1.519897975572075e-11,102639.09090134467,102941.50819395513,102336.6736087342,"['Decreasing volume trend', 'Price below VWAP']","The volume has been decreasing, suggesting potential weakness in price momentum. With the price below VWAP, this supports a bearish outlook.",2025-01-30 12:10:00+00:00,2025-01-29 12:45:00+00:00,2025-01-30 11:05:00+00:00,2025-01-30 11:10:00+00:00,2025-01-30 12:05:00+00:00,2025-01-30 12:10:00+00:00,down
|
||||
39,down,0.8,0.0,0.0,102639.09022130995,102941.20365837289,102336.976784247,"['Decreasing volume trend', 'Price and VWAP convergence']","The model suggests a downtrend in VWAP due to the decreasing volume trend, indicating potential weakness in price momentum.",2025-01-30 12:15:00+00:00,2025-01-29 12:50:00+00:00,2025-01-30 11:10:00+00:00,2025-01-30 11:15:00+00:00,2025-01-30 12:10:00+00:00,2025-01-30 12:15:00+00:00,down
|
||||
40,down,0.8,0.0,0.0,102639.09002168816,102941.50814151904,102336.67190185726,"['Decreasing volume trend', 'Price below VWAP']","The model suggests a downtrend in VWAP due to consistently decreasing volume, indicating potential weakness in price momentum.",2025-01-30 12:20:00+00:00,2025-01-29 12:55:00+00:00,2025-01-30 11:15:00+00:00,2025-01-30 11:20:00+00:00,2025-01-30 12:15:00+00:00,2025-01-30 12:20:00+00:00,down
|
||||
41,down,0.8,0.0,0.0,102639.0902213098,102941.50887497552,102336.67156764407,"['Decreasing volume trend', 'Price below VWAP']",The absence of volume suggests potential trend weakness. Historical patterns show that decreasing volume often precedes price declines.,2025-01-30 12:25:00+00:00,2025-01-29 13:00:00+00:00,2025-01-30 11:20:00+00:00,2025-01-30 11:25:00+00:00,2025-01-30 12:20:00+00:00,2025-01-30 12:25:00+00:00,down
|
||||
42,down,0.8,0.0,0.0,102639.09002182323,102941.20313352495,102336.9769101215,"['Decreasing volume trend', 'Price below VWAP']","The absence of volume suggests a potential trend weakness. With price below VWAP and decreasing volume, momentum is likely to be downwards.",2025-01-30 12:30:00+00:00,2025-01-29 13:05:00+00:00,2025-01-30 11:25:00+00:00,2025-01-30 11:30:00+00:00,2025-01-30 12:25:00+00:00,2025-01-30 12:30:00+00:00,down
|
||||
43,down,0.8,0.0,0.0,102639.0904319659,102940.50823143143,102337.67263250034,"['Decreasing volume trend', 'Price below VWAP']",The absence of volume suggests a potential trend weakness. Historical patterns show that decreasing volume often precedes VWAP declines.,2025-01-30 12:35:00+00:00,2025-01-29 13:10:00+00:00,2025-01-30 11:30:00+00:00,2025-01-30 11:35:00+00:00,2025-01-30 12:30:00+00:00,2025-01-30 12:35:00+00:00,up
|
||||
44,down,0.8,0.0,0.0,102639.09002167988,102941.20311635516,102336.97692700457,"['Decreasing volume trend', 'Stable price and VWAP']",The absence of volume suggests potential weakness in price momentum. Historical patterns show that decreasing volume often precedes VWAP declines.,2025-01-30 12:40:00+00:00,2025-01-29 13:15:00+00:00,2025-01-30 11:35:00+00:00,2025-01-30 11:40:00+00:00,2025-01-30 12:35:00+00:00,2025-01-30 12:40:00+00:00,down
|
||||
45,up,0.8,0.004775075201257975,0.5335366794219759,104937.25864175239,104685.04588775297,105189.47139575177,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.', 'Recent uptrend in VWAP supports positive momentum.']","The increasing volume indicates strong momentum, while the price being above the VWAP supports bullish momentum. Recent trends show that similar conditions led to VWAP increases.",2025-01-30 12:45:00+00:00,2025-01-29 13:20:00+00:00,2025-01-30 11:40:00+00:00,2025-01-30 11:45:00+00:00,2025-01-30 12:40:00+00:00,2025-01-30 12:45:00+00:00,up
|
||||
46,down,0.8,0.0,3.519897975572075,104976.30001977165,105278.44811510299,104674.15192444027,"['Decreasing volume trend', 'Price below VWAP']","The decreasing volume trend suggests potential weakness in price momentum. With the price below the VWAP, this supports a bearish outlook.",2025-01-30 12:50:00+00:00,2025-01-29 13:25:00+00:00,2025-01-30 11:45:00+00:00,2025-01-30 11:50:00+00:00,2025-01-30 12:45:00+00:00,2025-01-30 12:50:00+00:00,down
|
||||
47,up,0.8,0.005213329197641257,0.9353906320805193,105036.3102575036,104735.6480805033,105336.97243450388,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.']","The increasing volume indicates strong momentum, and the price being above the VWAP supports the likelihood of further upward movement.",2025-01-30 12:55:00+00:00,2025-01-29 13:30:00+00:00,2025-01-30 11:50:00+00:00,2025-01-30 11:55:00+00:00,2025-01-30 12:50:00+00:00,2025-01-30 12:55:00+00:00,down
|
||||
48,down,0.8,0.0,0.0,102689.14971321417,102981.44863407176,102396.85079235658,"['Decreasing volume trend', 'Stable price and VWAP']",The absence of volume suggests potential weakness in price movement. Historical patterns show that decreasing volume often precedes VWAP declines.,2025-01-30 13:00:00+00:00,2025-01-29 13:35:00+00:00,2025-01-30 11:55:00+00:00,2025-01-30 12:00:00+00:00,2025-01-30 12:55:00+00:00,2025-01-30 13:00:00+00:00,down
|
||||
49,down,0.8,0.0,0.0,102689.14997997915,102981.4487999287,102396.8511600296,"['Decreasing volume trend', 'Price below VWAP']","The decreasing volume trend suggests potential weakness in price momentum. With the price below VWAP, this supports a bearish outlook.",2025-01-30 13:05:00+00:00,2025-01-29 13:40:00+00:00,2025-01-30 12:00:00+00:00,2025-01-30 12:05:00+00:00,2025-01-30 13:00:00+00:00,2025-01-30 13:05:00+00:00,down
|
||||
50,down,0.8,0.0,0.9195279292570759,102689.15102117616,102981.9963749787,102396.30566737364,"['Decreasing volume trend', 'Price below VWAP']","The decreasing volume trend suggests potential weakness in price momentum. With the price below the VWAP, this supports a bearish outlook.",2025-01-30 13:10:00+00:00,2025-01-29 13:45:00+00:00,2025-01-30 12:05:00+00:00,2025-01-30 12:10:00+00:00,2025-01-30 13:05:00+00:00,2025-01-30 13:10:00+00:00,down
|
||||
51,down,0.8,0.0,0.9733686322040759,102689.15223821401,102981.24607609246,102397.05840033556,"['Decreasing volume trend', 'Price below VWAP']","The decreasing volume trend suggests potential weakness in price momentum. With the price below the VWAP, this supports a bearish outlook.",2025-01-30 13:15:00+00:00,2025-01-29 13:50:00+00:00,2025-01-30 12:10:00+00:00,2025-01-30 12:15:00+00:00,2025-01-30 13:10:00+00:00,2025-01-30 13:15:00+00:00,down
|
||||
52,down,0.8,0.0,0.0,102689.1485389404,102981.6485389404,102396.6485389404,"['Decreasing volume trend', 'Price below VWAP']","The decreasing volume trend suggests potential weakness in price momentum. With the price below VWAP, this supports a bearish outlook.",2025-01-30 13:20:00+00:00,2025-01-29 13:55:00+00:00,2025-01-30 12:15:00+00:00,2025-01-30 12:20:00+00:00,2025-01-30 13:15:00+00:00,2025-01-30 13:20:00+00:00,down
|
||||
53,down,0.8,0.0,0.0,102689.1499796675,102981.29963220024,102397.00032713475,"['Decreasing volume suggests potential trend weakness.', 'Stable price and VWAP indicate consolidation.']",The absence of volume suggests a potential downtrend in VWAP. Historical patterns show that decreasing volume often precedes VWAP declines.,2025-01-30 13:25:00+00:00,2025-01-29 14:00:00+00:00,2025-01-30 12:20:00+00:00,2025-01-30 12:25:00+00:00,2025-01-30 13:20:00+00:00,2025-01-30 13:25:00+00:00,down
|
||||
54,down,0.8,0.0,0.0,102689.1529388104,102981.24618366647,102397.0596939543,"['Decreasing volume trend', 'Price below VWAP']",The absence of volume suggests a potential trend weakness. Historical patterns show that decreasing volume often precedes VWAP declines.,2025-01-30 13:30:00+00:00,2025-01-29 14:05:00+00:00,2025-01-30 12:25:00+00:00,2025-01-30 12:30:00+00:00,2025-01-30 13:25:00+00:00,2025-01-30 13:30:00+00:00,up
|
||||
55,down,0.8,0.0,0.0,102689.14971317058,102981.73342744411,102396.56699889703,"['Decreasing volume trend', 'Price below VWAP']",The absence of volume suggests potential trend weakness. Historical patterns show that decreasing volume often precedes VWAP declines.,2025-01-30 13:35:00+00:00,2025-01-29 14:10:00+00:00,2025-01-30 12:30:00+00:00,2025-01-30 12:35:00+00:00,2025-01-30 13:30:00+00:00,2025-01-30 13:35:00+00:00,down
|
||||
56,up,0.8,0.01022361020124315,0.8837750807710832,105033.09120135575,104781.0917303556,105285.09067235592,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.', 'Recent uptrend in VWAP supports positive momentum.']","The increasing volume indicates strong momentum, while the price being above the VWAP supports bullish momentum. Historical patterns show that similar conditions led to VWAP increases.",2025-01-30 13:40:00+00:00,2025-01-29 14:15:00+00:00,2025-01-30 12:35:00+00:00,2025-01-30 12:40:00+00:00,2025-01-30 13:35:00+00:00,2025-01-30 13:40:00+00:00,down
|
||||
57,down,0.8,0.0,0.0,102715.75565195915,102987.46283611872,102444.04846779958,"['Decreasing volume trend', 'Price below VWAP']","The decreasing volume trend suggests potential weakness in price momentum. With the price consistently below the VWAP, this supports the expectation of further downward pressure.",2025-01-30 13:45:00+00:00,2025-01-29 14:20:00+00:00,2025-01-30 12:40:00+00:00,2025-01-30 12:45:00+00:00,2025-01-30 13:40:00+00:00,2025-01-30 13:45:00+00:00,down
|
||||
58,down,0.8,0.0,0.9510453702781229,102715.76060681028,102987.43758710423,102444.0836265163,"['Decreasing volume trend', 'Price below VWAP']","The decreasing volume trend suggests potential weakness in price momentum. With the price below VWAP, bearish momentum is supported.",2025-01-30 13:50:00+00:00,2025-01-29 14:25:00+00:00,2025-01-30 12:45:00+00:00,2025-01-30 12:50:00+00:00,2025-01-30 13:45:00+00:00,2025-01-30 13:50:00+00:00,up
|
||||
59,down,0.8,0.0,0.7071067811865476,102715.75702290167,102977.464293194,102454.04975260932,"['Decreasing volume suggests potential trend weakness.', 'Recent downtrend in VWAP supports bearish momentum.']",The absence of volume indicates potential trend weakness. Historical patterns show that decreasing volume often precedes VWAP declines.,2025-01-30 13:55:00+00:00,2025-01-29 14:30:00+00:00,2025-01-30 12:50:00+00:00,2025-01-30 12:55:00+00:00,2025-01-30 13:50:00+00:00,2025-01-30 13:55:00+00:00,up
|
||||
60,up,0.8,0.005021257299999999,0.5524542645566553,104866.68199999999,104570.91999999998,105162.44499999999,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.']","The increasing volume indicates strong momentum, which historically precedes VWAP increases. The price being above the VWAP further supports the bullish momentum.",2025-01-30 14:00:00+00:00,2025-01-29 14:35:00+00:00,2025-01-30 12:55:00+00:00,2025-01-30 13:00:00+00:00,2025-01-30 13:55:00+00:00,2025-01-30 14:00:00+00:00,up
|
||||
61,up,0.8,0.005312066299999999,1.080648149198946,104817.9744334348,104516.8977856333,105119.0510812363,"['Decreasing volume suggests potential trend weakness.', 'Price above VWAP supports bullish momentum.']","The decreasing volume indicates potential trend weakness, while the price being above VWAP supports bullish momentum. Historical patterns show that similar conditions led to VWAP increases.",2025-01-30 14:05:00+00:00,2025-01-29 14:40:00+00:00,2025-01-30 13:00:00+00:00,2025-01-30 13:05:00+00:00,2025-01-30 14:00:00+00:00,2025-01-30 14:05:00+00:00,up
|
||||
62,up,0.8,0.01039999999999999,1.080217569220859,104728.87999999999,104405.68,105052.08000000002,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.']","The increasing volume indicates strong market momentum, which historically precedes VWAP increases. The current price being above the VWAP further supports the bullish momentum.",2025-01-30 14:10:00+00:00,2025-01-29 14:45:00+00:00,2025-01-30 13:05:00+00:00,2025-01-30 13:10:00+00:00,2025-01-30 14:05:00+00:00,2025-01-30 14:10:00+00:00,up
|
||||
63,up,0.8,0.02117602117597599,3.953929324623225,104883.8286515056,104582.3537206468,105185.3035823644,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.', 'Recent uptrend in VWAP supports positive momentum.']","The increasing volume indicates strong momentum, and the price being above the VWAP supports bullish momentum. Historical patterns show that similar conditions led to VWAP increases.",2025-01-30 14:15:00+00:00,2025-01-29 14:50:00+00:00,2025-01-30 13:10:00+00:00,2025-01-30 13:15:00+00:00,2025-01-30 14:10:00+00:00,2025-01-30 14:15:00+00:00,up
|
||||
64,up,0.8,0.01026777512244898,3.074396257905576,102908.80078125,102577.920703125,103239.680859375,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.', 'Recent uptrend in VWAP supports positive momentum.']","The increasing volume indicates strong momentum, which historically precedes VWAP increases. The price being above the VWAP further supports the bullish momentum.",2025-01-30 14:20:00+00:00,2025-01-29 14:55:00+00:00,2025-01-30 13:15:00+00:00,2025-01-30 13:20:00+00:00,2025-01-30 14:15:00+00:00,2025-01-30 14:20:00+00:00,up
|
||||
65,up,0.8,0.02144031926799388,5.080579897755576,104884.04545454546,104582.04545454546,105186.04545454546,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.', 'Recent uptrend in VWAP supports positive momentum.']","The increasing volume indicates strong momentum, which historically precedes VWAP increases. The price being above the VWAP further supports the bullish momentum.",2025-01-30 14:25:00+00:00,2025-01-29 15:00:00+00:00,2025-01-30 13:20:00+00:00,2025-01-30 13:25:00+00:00,2025-01-30 14:20:00+00:00,2025-01-30 14:25:00+00:00,up
|
||||
66,up,0.8,0.0005640752020137759,0.8836720196321336,104778.7236328798,104465.54605995333,105091.90120580627,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.']","The increase in volume suggests strong momentum, which historically precedes VWAP increases. The current price being above the VWAP further supports the bullish momentum.",2025-01-30 14:30:00+00:00,2025-01-29 15:05:00+00:00,2025-01-30 13:25:00+00:00,2025-01-30 13:30:00+00:00,2025-01-30 14:25:00+00:00,2025-01-30 14:30:00+00:00,up
|
||||
67,up,0.8,0.004267080080257165,1.519897975572075e-11,102978.65755326538,102978.65755326538,103281.55390926567,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.']","The increasing volume indicates strong momentum, which historically precedes VWAP increases. The price being above the VWAP further supports the bullish momentum.",2025-01-30 14:35:00+00:00,2025-01-29 15:10:00+00:00,2025-01-30 13:30:00+00:00,2025-01-30 13:35:00+00:00,2025-01-30 14:30:00+00:00,2025-01-30 14:35:00+00:00,down
|
||||
68,up,0.8,0.004174407267269975,4.816771186198176,105146.56681077577,104850.7726484333,105442.3619731182,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.', 'Stable MA5 and MA20 indicate trend consistency.']","The increasing volume suggests strong momentum, supporting the bullish trend as the price remains above the VWAP. Historical patterns show similar conditions led to VWAP increases.",2025-01-30 14:40:00+00:00,2025-01-29 15:15:00+00:00,2025-01-30 13:35:00+00:00,2025-01-30 13:40:00+00:00,2025-01-30 14:35:00+00:00,2025-01-30 14:40:00+00:00,down
|
||||
69,down,0.8,0.0,0.0,102993.889,103293.5889,102694.1891,"['Decreasing volume trend', 'Stable price and VWAP']","The model suggests a downtrend in VWAP due to decreasing volume, indicating potential weakness in price momentum.",2025-01-30 14:45:00+00:00,2025-01-29 15:20:00+00:00,2025-01-30 13:40:00+00:00,2025-01-30 13:45:00+00:00,2025-01-30 14:40:00+00:00,2025-01-30 14:45:00+00:00,up
|
||||
70,down,0.8,0.0,0.0,105322.59,105322.59,105322.59,"['Decreasing volume trend', 'Price below VWAP']","The volume has been consistently decreasing, suggesting potential weakness in price momentum. With the price above the VWAP, this typically indicates bullish momentum; however, the lack of volume suggests that this momentum may not be sustainable.",2025-01-30 14:50:00+00:00,2025-01-29 15:25:00+00:00,2025-01-30 13:45:00+00:00,2025-01-30 13:50:00+00:00,2025-01-30 14:45:00+00:00,2025-01-30 14:50:00+00:00,up
|
||||
71,up,0.8,0.01732725701345766,8.47484332928316,106085.3,105785.3,106385.3,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.', 'Recent uptrend in VWAP supports positive momentum.']","The significant increase in volume indicates strong market momentum. With the price above the VWAP, bullish momentum is supported. Historical patterns show that similar conditions led to VWAP increases.",2025-01-30 14:55:00+00:00,2025-01-29 15:30:00+00:00,2025-01-30 13:50:00+00:00,2025-01-30 13:55:00+00:00,2025-01-30 14:50:00+00:00,2025-01-30 14:55:00+00:00,up
|
||||
72,up,0.9,0.1039346379195434,27.07456982767699,105820.5546875,105519.5546875,106121.5546875,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.', 'Recent uptrend in VWAP supports positive momentum.']","The significant increase in volume indicates strong buying pressure. With the price above the VWAP, momentum is bullish. Historical patterns show that similar conditions led to VWAP increases.",2025-01-30 15:00:00+00:00,2025-01-29 15:35:00+00:00,2025-01-30 13:55:00+00:00,2025-01-30 14:00:00+00:00,2025-01-30 14:55:00+00:00,2025-01-30 15:00:00+00:00,up
|
||||
73,up,0.8,0.004207486075953195,15.95320216864634,103916.5980016326,103616.49875508138,104216.6972481838,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.', 'Recent uptrend in VWAP supports positive momentum.']","The significant increase in volume indicates strong market momentum. With the price above the VWAP, bullish momentum is supported. Historical patterns show that similar conditions led to VWAP increases.",2025-01-30 15:05:00+00:00,2025-01-29 15:40:00+00:00,2025-01-30 14:00:00+00:00,2025-01-30 14:05:00+00:00,2025-01-30 15:00:00+00:00,2025-01-30 15:05:00+00:00,up
|
||||
74,up,0.8,0.01967979262307627,29.57930002121716,106077.83636013416,105777.83636013416,106377.83636013416,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.', 'Recent uptrend in VWAP supports positive momentum.']","The significant increase in volume indicates strong buying interest. With the price above the VWAP, momentum is bullish. Historical patterns show that similar conditions led to VWAP increases.",2025-01-30 15:10:00+00:00,2025-01-29 15:45:00+00:00,2025-01-30 14:05:00+00:00,2025-01-30 14:10:00+00:00,2025-01-30 15:05:00+00:00,2025-01-30 15:10:00+00:00,up
|
||||
75,up,0.9,0.10446962355389486,104.6331976829356,106140.88619520167,105780.79266097938,106500.97972942394,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.', 'Recent uptrend in both price and VWAP.']","The significant increase in volume indicates strong buying pressure. With the price above the VWAP, momentum is bullish. Historical patterns show that similar conditions led to VWAP increases.",2025-01-30 15:15:00+00:00,2025-01-29 15:50:00+00:00,2025-01-30 14:10:00+00:00,2025-01-30 14:15:00+00:00,2025-01-30 15:10:00+00:00,2025-01-30 15:15:00+00:00,up
|
||||
76,up,0.8,0.003215197235201052,15.79201914977512,106090.83120193567,105789.0288369357,106392.63356693563,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.', 'Recent uptrend in VWAP supports positive momentum.']","The significant increase in volume indicates strong buying pressure. With the price above the VWAP, this supports bullish momentum. Historical patterns show that similar conditions led to VWAP increases.",2025-01-30 15:20:00+00:00,2025-01-29 15:55:00+00:00,2025-01-30 14:15:00+00:00,2025-01-30 14:20:00+00:00,2025-01-30 15:15:00+00:00,2025-01-30 15:20:00+00:00,up
|
||||
77,up,0.8,0.004198080329760823,15.52700789766312,104307.57320195316,104006.8824694551,104608.26393445121,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.', 'Recent uptrend in VWAP supports positive momentum.']","The increasing volume indicates strong buying pressure, while the price being above the VWAP supports bullish momentum. Historical patterns show that similar conditions led to VWAP increases.",2025-01-30 15:25:00+00:00,2025-01-29 16:00:00+00:00,2025-01-30 14:20:00+00:00,2025-01-30 14:25:00+00:00,2025-01-30 15:20:00+00:00,2025-01-30 15:25:00+00:00,up
|
||||
78,up,0.8,0.004542021498663128,4.066905232703118,104343.92090129993,104042.58857964692,104645.25322295292,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.', 'Stable MA5 and MA20 indicate trend consistency.']","The increasing volume suggests strong momentum, supporting the bullish trend as the price remains above the VWAP.",2025-01-30 15:30:00+00:00,2025-01-30 09:30:00+00:00,2025-01-30 14:25:00+00:00,2025-01-30 14:30:00+00:00,2025-01-30 15:25:00+00:00,2025-01-30 15:30:00+00:00,up
|
||||
79,up,0.8,0.01926784919828588,22.26709727064816,104394.34999999999,104092.0774,104696.62259999999,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.', 'Recent uptrend in VWAP supports positive momentum.']","The increasing volume indicates strong momentum, and the price being above the VWAP supports bullish momentum. Historical patterns show that similar conditions led to VWAP increases.",2025-01-30 15:35:00+00:00,2025-01-30 09:35:00+00:00,2025-01-30 14:30:00+00:00,2025-01-30 14:35:00+00:00,2025-01-30 15:30:00+00:00,2025-01-30 15:35:00+00:00,up
|
||||
80,up,0.8,0.01944479266329988,12.16880025745512,104442.2376322023,104140.90563293589,104743.56963146872,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.', 'Recent uptrend in VWAP supports positive momentum.']","The increasing volume indicates strong momentum, supporting the bullish trend. The price being above the VWAP further reinforces this momentum. Historical patterns show that similar conditions led to VWAP increases.",2025-01-30 15:40:00+00:00,2025-01-30 09:40:00+00:00,2025-01-30 14:35:00+00:00,2025-01-30 14:40:00+00:00,2025-01-30 15:35:00+00:00,2025-01-30 15:40:00+00:00,up
|
||||
81,up,0.8,0.004577648810731017,12.79257556356912,104468.4438897623,104166.3038897623,104770.5838897623,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.', 'Stable MA5 and MA20 indicate trend consistency.']","The increasing volume suggests strong momentum behind the price movement. With the current price above the VWAP, this supports a bullish momentum. The stability of the MA5 and MA20 further indicates trend consistency, reinforcing the confidence in an upward VWAP movement.",2025-01-30 15:45:00+00:00,2025-01-30 09:45:00+00:00,2025-01-30 14:40:00+00:00,2025-01-30 14:45:00+00:00,2025-01-30 15:40:00+00:00,2025-01-30 15:45:00+00:00,up
|
||||
82,up,0.8,0.003663134257519016,8.56890599364699,106146.3082011956,105846.3082011956,106446.3082011956,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.', 'Decreasing volume trend may indicate potential reversal.']",The increasing volume suggests strong momentum supporting a bullish trend. The price being above the VWAP further reinforces this momentum. Historical patterns show that similar conditions led to VWAP increases.,2025-01-30 15:50:00+00:00,2025-01-30 09:50:00+00:00,2025-01-30 14:45:00+00:00,2025-01-30 14:50:00+00:00,2025-01-30 15:45:00+00:00,2025-01-30 15:50:00+00:00,up
|
||||
83,up,0.8,0.004516356081993437,8.09865299322212,104513.7064367331,104211.67189497386,104815.74097849234,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.', 'Recent uptrend in VWAP supports positive momentum.']","The significant increase in volume indicates strong market momentum. With the price above the VWAP, bullish momentum is supported. Historical patterns show that similar conditions led to VWAP increases.",2025-01-30 15:55:00+00:00,2025-01-30 09:55:00+00:00,2025-01-30 14:50:00+00:00,2025-01-30 14:55:00+00:00,2025-01-30 15:50:00+00:00,2025-01-30 15:55:00+00:00,up
|
||||
84,up,0.8,0.004587646202975197,8.32963461149615,104543.3120459939,104241.6822579354,104844.9418340524,"['Increasing volume suggests strong momentum.', 'Price above VWAP supports bullish momentum.', 'Recent uptrend in VWAP supports positive momentum.']","The increasing volume indicates strong buying interest, which is likely to push the VWAP higher. Additionally, the price being above the VWAP supports bullish momentum.",2025-01-30 16:00:00+00:00,2025-01-30 10:00:00+00:00,2025-01-30 14:55:00+00:00,2025-01-30 15:00:00+00:00,2025-01-30 15:55:00+00:00,2025-01-30 16:00:00+00:00,
|
||||
|
33
pyproject.toml
Normal file
33
pyproject.toml
Normal file
@ -0,0 +1,33 @@
|
||||
[tool.poetry]
|
||||
name = "market-predictor"
|
||||
version = "0.1.0"
|
||||
description = "Market VWAP prediction using RAG model"
|
||||
authors = ["Your Name <your.email@example.com>"]
|
||||
packages = [
|
||||
{ include = "market_predictor" },
|
||||
]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.12"
|
||||
pandas = "^2.2.0"
|
||||
numpy = "^1.26.0"
|
||||
yfinance = "^0.2.36"
|
||||
langchain-openai = "^0.0.5"
|
||||
langchain-community = "^0.0.19"
|
||||
matplotlib = "^3.8.0"
|
||||
seaborn = "^0.13.0"
|
||||
jupyter = "^1.0.0"
|
||||
tqdm = "^4.66.1"
|
||||
pytz = "^2024.1"
|
||||
python-dotenv = "^1.0.0"
|
||||
datetime = "^5.5"
|
||||
ipywidgets = "^8.1.5"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
pytest = "^8.0.0"
|
||||
black = "^24.1.0"
|
||||
isort = "^5.13.0"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
101
rag_engine.py
Normal file
101
rag_engine.py
Normal file
@ -0,0 +1,101 @@
|
||||
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
|
||||
from langchain.prompts import ChatPromptTemplate
|
||||
from langchain_community.vectorstores import FAISS
|
||||
import asyncio
|
||||
from typing import List, Dict
|
||||
from config import OPENAI_API_KEY, MODEL_NAME, EMBEDDING_MODEL
|
||||
from langchain_core.messages import HumanMessage, SystemMessage
|
||||
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
|
||||
import pandas as pd
|
||||
import json
|
||||
|
||||
class RAGEngine:
|
||||
def __init__(self):
|
||||
self.embeddings = OpenAIEmbeddings(
|
||||
openai_api_key=OPENAI_API_KEY,
|
||||
model=EMBEDDING_MODEL
|
||||
)
|
||||
self.llm = ChatOpenAI(
|
||||
openai_api_key=OPENAI_API_KEY,
|
||||
model=MODEL_NAME,
|
||||
temperature=0.1
|
||||
)
|
||||
self.vectorstore = None
|
||||
|
||||
# Define system prompt
|
||||
self.system_prompt = """You are a technical analysis expert predicting 5-minute VWAP movements.
|
||||
|
||||
Analysis Requirements:
|
||||
1. Compare current VWAP to moving averages (5, 20 periods)
|
||||
2. Analyze volume trends vs price movement
|
||||
3. Identify short-term support/resistance levels
|
||||
4. Check momentum indicators (RSI trends)
|
||||
5. Evaluate recent price action patterns
|
||||
|
||||
Confidence Score Rules:
|
||||
- 0.8-1.0: Strong signals with multiple confirmations
|
||||
- 0.6-0.8: Good signals with some confirmation
|
||||
- 0.4-0.6: Mixed or unclear signals
|
||||
- Below 0.4: Weak or contradictory signals
|
||||
|
||||
Volume Analysis:
|
||||
- Compare current volume to 5-period average
|
||||
- Check volume trend direction
|
||||
- Evaluate price/volume relationship
|
||||
|
||||
Pattern Recognition:
|
||||
- Higher highs / lower lows
|
||||
- Support/resistance tests
|
||||
- Volume spikes or drops
|
||||
- VWAP crossovers
|
||||
- Momentum divergences
|
||||
|
||||
Output strict JSON format:
|
||||
{
|
||||
"vwap_prediction_next_5min": "up" or "down",
|
||||
"confidence_score": 0.0 to 1.0,
|
||||
"technical_signals": [
|
||||
"volume_trend": "increasing/decreasing",
|
||||
"price_momentum": "positive/negative",
|
||||
"vwap_trend": "above_ma/below_ma",
|
||||
"support_resistance": "near_support/near_resistance"
|
||||
],
|
||||
"key_levels": {
|
||||
"support": "price_level",
|
||||
"resistance": "price_level",
|
||||
"vwap_ma_cross": "price_level"
|
||||
},
|
||||
"reasoning": "Brief technical analysis explanation"
|
||||
}"""
|
||||
|
||||
def create_vectorstore(self, texts: List[str]):
|
||||
self.vectorstore = FAISS.from_texts(
|
||||
texts,
|
||||
self.embeddings,
|
||||
metadatas=[{"index": i} for i in range(len(texts))]
|
||||
)
|
||||
|
||||
async def predict(self, query: str, timestamp: pd.Timestamp) -> Dict:
|
||||
try:
|
||||
similar_docs = self.vectorstore.similarity_search(query, k=5)
|
||||
context = "\n".join([doc.page_content for doc in similar_docs])
|
||||
|
||||
messages = [
|
||||
SystemMessage(content=self.system_prompt),
|
||||
HumanMessage(content=f"Similar Patterns:\n{context}\n\nCurrent Data:\n{query}")
|
||||
]
|
||||
|
||||
response = await self.llm.ainvoke(messages)
|
||||
|
||||
# Clean and parse JSON response
|
||||
json_str = response.content.replace('```json\n', '').replace('\n```', '').strip()
|
||||
prediction = json.loads(json_str)
|
||||
|
||||
# Add timestamp
|
||||
prediction['timestamp_prediction'] = timestamp
|
||||
|
||||
return prediction
|
||||
|
||||
except Exception as e:
|
||||
print(f"Prediction error: {e}")
|
||||
return None
|
||||
1647
training_data.jsonl
Normal file
1647
training_data.jsonl
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user