Cycles/test/simple_alignment_test.py

199 lines
7.0 KiB
Python
Raw Normal View History

2025-05-28 18:26:51 +08:00
#!/usr/bin/env python3
"""
Simple alignment test with synthetic data to clearly show timeframe alignment.
"""
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from matplotlib.patches import Rectangle
import sys
import os
# Add the project root to Python path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from IncrementalTrader.utils import aggregate_minute_data_to_timeframe, parse_timeframe_to_minutes
def create_simple_test_data():
"""Create simple test data for clear visualization."""
start_time = pd.Timestamp('2024-01-01 09:00:00')
minute_data = []
# Create exactly 60 minutes of data (4 complete 15-min bars)
for i in range(60):
timestamp = start_time + pd.Timedelta(minutes=i)
# Create a simple price pattern that's easy to follow
base_price = 100.0
minute_in_hour = i % 60
price_trend = base_price + (minute_in_hour * 0.1) # Gradual uptrend
minute_data.append({
'timestamp': timestamp,
'open': price_trend,
'high': price_trend + 0.2,
'low': price_trend - 0.2,
'close': price_trend + 0.1,
'volume': 1000
})
return minute_data
def plot_timeframe_bars(ax, data, timeframe, color, alpha=0.7, show_labels=True):
"""Plot timeframe bars with clear boundaries."""
if not data:
return
timeframe_minutes = parse_timeframe_to_minutes(timeframe)
for i, bar in enumerate(data):
timestamp = bar['timestamp']
open_price = bar['open']
high_price = bar['high']
low_price = bar['low']
close_price = bar['close']
# Calculate bar boundaries (end timestamp mode)
bar_start = timestamp - pd.Timedelta(minutes=timeframe_minutes)
bar_end = timestamp
# Draw the bar as a rectangle spanning the full time period
body_height = abs(close_price - open_price)
body_bottom = min(open_price, close_price)
# Bar body
rect = Rectangle((bar_start, body_bottom),
bar_end - bar_start, body_height,
facecolor=color, edgecolor='black',
alpha=alpha, linewidth=1)
ax.add_patch(rect)
# High-low wick at center
bar_center = bar_start + (bar_end - bar_start) / 2
ax.plot([bar_center, bar_center], [low_price, high_price],
color='black', linewidth=2, alpha=alpha)
# Add labels if requested
if show_labels:
ax.text(bar_center, high_price + 0.1, f"{timeframe}\n#{i+1}",
ha='center', va='bottom', fontsize=8, fontweight='bold')
def create_alignment_visualization():
"""Create a clear visualization of timeframe alignment."""
print("🎯 Creating Timeframe Alignment Visualization")
print("=" * 50)
# Create test data
minute_data = create_simple_test_data()
print(f"📊 Created {len(minute_data)} minute data points")
print(f"📅 Range: {minute_data[0]['timestamp']} to {minute_data[-1]['timestamp']}")
# Aggregate to different timeframes
timeframes = ["5min", "15min", "30min", "1h"]
colors = ['red', 'green', 'blue', 'purple']
alphas = [0.8, 0.6, 0.4, 0.2]
aggregated_data = {}
for tf in timeframes:
aggregated_data[tf] = aggregate_minute_data_to_timeframe(minute_data, tf, "end")
print(f" {tf}: {len(aggregated_data[tf])} bars")
# Create visualization
fig, ax = plt.subplots(1, 1, figsize=(16, 10))
fig.suptitle('Timeframe Alignment Visualization\n(Smaller timeframes should fit inside larger ones)',
fontsize=16, fontweight='bold')
# Plot timeframes from largest to smallest (background to foreground)
for i, tf in enumerate(reversed(timeframes)):
color = colors[timeframes.index(tf)]
alpha = alphas[timeframes.index(tf)]
show_labels = (tf in ["5min", "15min"]) # Only label smaller timeframes for clarity
plot_timeframe_bars(ax, aggregated_data[tf], tf, color, alpha, show_labels)
# Format the plot
ax.set_ylabel('Price (USD)', fontsize=12)
ax.set_xlabel('Time', fontsize=12)
ax.grid(True, alpha=0.3)
# Format x-axis
ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M'))
ax.xaxis.set_major_locator(mdates.MinuteLocator(interval=15))
plt.setp(ax.xaxis.get_majorticklabels(), rotation=45)
# Add legend
legend_elements = []
for i, tf in enumerate(timeframes):
legend_elements.append(plt.Rectangle((0,0),1,1,
facecolor=colors[i],
alpha=alphas[i],
label=f"{tf} ({len(aggregated_data[tf])} bars)"))
ax.legend(handles=legend_elements, loc='upper left', fontsize=10)
# Add explanation
explanation = ("Each bar spans its full time period.\n"
"5min bars should fit exactly inside 15min bars.\n"
"15min bars should fit exactly inside 30min and 1h bars.")
ax.text(0.02, 0.98, explanation, transform=ax.transAxes,
verticalalignment='top', fontsize=10,
bbox=dict(boxstyle='round', facecolor='lightyellow', alpha=0.9))
plt.tight_layout()
# Print alignment verification
print(f"\n🔍 Alignment Verification:")
bars_5m = aggregated_data["5min"]
bars_15m = aggregated_data["15min"]
for i, bar_15m in enumerate(bars_15m):
print(f"\n15min bar {i+1}: {bar_15m['timestamp']}")
bar_15m_start = bar_15m['timestamp'] - pd.Timedelta(minutes=15)
contained_5m = []
for bar_5m in bars_5m:
bar_5m_start = bar_5m['timestamp'] - pd.Timedelta(minutes=5)
bar_5m_end = bar_5m['timestamp']
# Check if 5min bar is contained within 15min bar
if bar_15m_start <= bar_5m_start and bar_5m_end <= bar_15m['timestamp']:
contained_5m.append(bar_5m)
print(f" Contains {len(contained_5m)} x 5min bars:")
for j, bar_5m in enumerate(contained_5m):
print(f" {j+1}. {bar_5m['timestamp']}")
return fig
def main():
"""Main function."""
print("🚀 Simple Timeframe Alignment Test")
print("=" * 40)
try:
fig = create_alignment_visualization()
plt.show()
print("\n✅ Alignment test completed!")
print("📊 In the chart, you should see:")
print(" - Each 15min bar contains exactly 3 x 5min bars")
print(" - Each 30min bar contains exactly 6 x 5min bars")
print(" - Each 1h bar contains exactly 12 x 5min bars")
print(" - All bars are properly aligned with no gaps or overlaps")
return True
except Exception as e:
print(f"❌ Error: {e}")
import traceback
traceback.print_exc()
return False
if __name__ == "__main__":
success = main()
sys.exit(0 if success else 1)