Cycles/test/test_bar_alignment.py

207 lines
6.6 KiB
Python

#!/usr/bin/env python3
"""
Test Bar Alignment Between TimeframeAggregator and Pandas Resampling
====================================================================
This script tests whether the TimeframeAggregator creates the same bar boundaries
as pandas resampling to identify the timing issue.
"""
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
import sys
import os
# Add the parent directory to the path to import cycles modules
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from cycles.IncStrategies.base import TimeframeAggregator
def create_test_data():
"""Create test minute-level data."""
# Create 2 hours of minute data starting at 2025-01-01 10:00:00
start_time = pd.Timestamp('2025-01-01 10:00:00')
timestamps = [start_time + timedelta(minutes=i) for i in range(120)]
data = []
for i, ts in enumerate(timestamps):
data.append({
'timestamp': ts,
'open': 100.0 + i * 0.1,
'high': 100.5 + i * 0.1,
'low': 99.5 + i * 0.1,
'close': 100.2 + i * 0.1,
'volume': 1000.0
})
return data
def test_pandas_resampling(data):
"""Test how pandas resampling creates 15-minute bars."""
print("🔍 TESTING PANDAS RESAMPLING")
print("=" * 60)
# Convert to DataFrame
df = pd.DataFrame(data)
df.set_index('timestamp', inplace=True)
# Resample to 15-minute bars
agg_rules = {
'open': 'first',
'high': 'max',
'low': 'min',
'close': 'last',
'volume': 'sum'
}
resampled = df.resample('15min').agg(agg_rules)
resampled = resampled.dropna()
print(f"Original data points: {len(df)}")
print(f"15-minute bars: {len(resampled)}")
print(f"\nFirst 10 bars:")
for i, (timestamp, row) in enumerate(resampled.head(10).iterrows()):
print(f" {i+1:2d}. {timestamp} - Open: {row['open']:.1f}, Close: {row['close']:.1f}")
return resampled
def test_timeframe_aggregator(data):
"""Test how TimeframeAggregator creates 15-minute bars."""
print(f"\n🔍 TESTING TIMEFRAME AGGREGATOR")
print("=" * 60)
aggregator = TimeframeAggregator(timeframe_minutes=15)
completed_bars = []
for point in data:
ohlcv_data = {
'open': point['open'],
'high': point['high'],
'low': point['low'],
'close': point['close'],
'volume': point['volume']
}
completed_bar = aggregator.update(point['timestamp'], ohlcv_data)
if completed_bar is not None:
completed_bars.append(completed_bar)
print(f"Completed bars: {len(completed_bars)}")
print(f"\nFirst 10 bars:")
for i, bar in enumerate(completed_bars[:10]):
print(f" {i+1:2d}. {bar['timestamp']} - Open: {bar['open']:.1f}, Close: {bar['close']:.1f}")
return completed_bars
def compare_alignments(pandas_bars, aggregator_bars):
"""Compare the bar alignments between pandas and aggregator."""
print(f"\n📊 COMPARING BAR ALIGNMENTS")
print("=" * 60)
print(f"Pandas bars: {len(pandas_bars)}")
print(f"Aggregator bars: {len(aggregator_bars)}")
# Compare timestamps
print(f"\nTimestamp comparison:")
min_len = min(len(pandas_bars), len(aggregator_bars))
for i in range(min(10, min_len)):
pandas_ts = pandas_bars.index[i]
aggregator_ts = aggregator_bars[i]['timestamp']
time_diff = (aggregator_ts - pandas_ts).total_seconds() / 60 # minutes
print(f" {i+1:2d}. Pandas: {pandas_ts}, Aggregator: {aggregator_ts}, Diff: {time_diff:+.0f}min")
# Calculate average difference
time_diffs = []
for i in range(min_len):
pandas_ts = pandas_bars.index[i]
aggregator_ts = aggregator_bars[i]['timestamp']
time_diff = (aggregator_ts - pandas_ts).total_seconds() / 60
time_diffs.append(time_diff)
if time_diffs:
avg_diff = np.mean(time_diffs)
print(f"\nAverage timing difference: {avg_diff:+.1f} minutes")
if abs(avg_diff) < 0.1:
print("✅ Bar alignments match!")
else:
print("❌ Bar alignments differ!")
print("This explains the 15-minute delay in the incremental strategy.")
def test_specific_timestamps():
"""Test specific timestamps that appear in the actual trading data."""
print(f"\n🎯 TESTING SPECIFIC TIMESTAMPS FROM TRADING DATA")
print("=" * 60)
# Test timestamps from the actual trading data
test_timestamps = [
'2025-01-03 11:15:00', # Original strategy
'2025-01-03 11:30:00', # Incremental strategy
'2025-01-04 18:00:00', # Original strategy
'2025-01-04 18:15:00', # Incremental strategy
]
aggregator = TimeframeAggregator(timeframe_minutes=15)
for ts_str in test_timestamps:
ts = pd.Timestamp(ts_str)
# Test what bar this timestamp belongs to
ohlcv_data = {'open': 100, 'high': 101, 'low': 99, 'close': 100.5, 'volume': 1000}
# Get the bar start time using the aggregator's method
bar_start = aggregator._get_bar_start_time(ts)
# Test pandas resampling for the same timestamp
temp_df = pd.DataFrame([ohlcv_data], index=[ts])
resampled = temp_df.resample('15min').first()
pandas_bar_start = resampled.index[0] if len(resampled) > 0 else None
print(f"Timestamp: {ts}")
print(f" Aggregator bar start: {bar_start}")
print(f" Pandas bar start: {pandas_bar_start}")
print(f" Difference: {(bar_start - pandas_bar_start).total_seconds() / 60:.0f} minutes")
print()
def main():
"""Main test function."""
print("🚀 TESTING BAR ALIGNMENT BETWEEN STRATEGIES")
print("=" * 80)
try:
# Create test data
data = create_test_data()
# Test pandas resampling
pandas_bars = test_pandas_resampling(data)
# Test TimeframeAggregator
aggregator_bars = test_timeframe_aggregator(data)
# Compare alignments
compare_alignments(pandas_bars, aggregator_bars)
# Test specific timestamps
test_specific_timestamps()
return True
except Exception as e:
print(f"\n❌ Error during testing: {e}")
import traceback
traceback.print_exc()
return False
if __name__ == "__main__":
success = main()
exit(0 if success else 1)