207 lines
6.6 KiB
Python
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) |