新增期货数据动态播放器功能,包括基础版和增强版实现,添加测试脚本和详细文档说明。主要变更包括: 1. 实现买卖盘深度可视化播放功能 2. 添加播放控制、速度调节和跳转功能 3. 提供统一价格轴显示优化版本 4. 添加测试脚本验证功能 5. 编写详细使用文档和README说明
202 lines
7.4 KiB
Python
202 lines
7.4 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
Quick demo for Unified Futures Data Player
|
|
快速演示统一价格轴版本的期货数据播放器
|
|
"""
|
|
|
|
import pandas as pd
|
|
import numpy as np
|
|
import matplotlib.pyplot as plt
|
|
|
|
def demo_unified_chart():
|
|
"""演示统一价格轴图表效果"""
|
|
print("=== Unified Market Depth Demo ===")
|
|
print("Creating sample visualization...")
|
|
|
|
# Load some sample data
|
|
df = pd.read_parquet('data/au2512_20251013.parquet')
|
|
|
|
# Column mapping
|
|
columns_mapping = {
|
|
'时间': 'time', '累积成交量': 'cumulative_volume', '成交价': 'price',
|
|
'买1价': 'bid1_price', '卖1价': 'ask1_price',
|
|
'买1量': 'bid1_volume', '卖1量': 'ask1_volume',
|
|
'买2价': 'bid2_price', '卖2价': 'ask2_price',
|
|
'买2量': 'bid2_volume', '卖2量': 'ask2_volume',
|
|
'买3价': 'bid3_price', '卖3价': 'ask3_price',
|
|
'买3量': 'bid3_volume', '卖3量': 'ask3_volume',
|
|
'买4价': 'bid4_price', '卖4价': 'ask4_price',
|
|
'买4量': 'bid4_volume', '卖4量': 'ask4_volume',
|
|
'买5价': 'bid5_price', '卖5价': 'ask5_price',
|
|
'买5量': 'bid5_volume', '卖5量': 'ask5_volume'
|
|
}
|
|
df = df.rename(columns=columns_mapping)
|
|
|
|
# Get a sample row
|
|
sample_row = df.iloc[1000] # Sample from middle of data
|
|
current_price = sample_row['price']
|
|
min_tick = 0.02
|
|
|
|
# Create figure
|
|
fig, ax = plt.subplots(figsize=(12, 8))
|
|
fig.suptitle('Unified Market Depth Demo - AU2512', fontsize=16, fontweight='bold')
|
|
|
|
# Collect market depth data
|
|
bid_prices = []
|
|
bid_volumes = []
|
|
ask_prices = []
|
|
ask_volumes = []
|
|
|
|
for i in range(1, 6):
|
|
bid_price = sample_row[f'bid{i}_price']
|
|
bid_volume = sample_row[f'bid{i}_volume']
|
|
ask_price = sample_row[f'ask{i}_price']
|
|
ask_volume = sample_row[f'ask{i}_volume']
|
|
|
|
if pd.notna(bid_price) and pd.notna(bid_volume) and bid_volume > 0:
|
|
bid_prices.append(bid_price)
|
|
bid_volumes.append(bid_volume)
|
|
|
|
if pd.notna(ask_price) and pd.notna(ask_volume) and ask_volume > 0:
|
|
ask_prices.append(ask_price)
|
|
ask_volumes.append(ask_volume)
|
|
|
|
print(f"Sample data at sequence 1000:")
|
|
print(f"Current price: {current_price:.2f}")
|
|
print(f"Bid prices: {[f'{p:.2f}' for p in bid_prices]}")
|
|
print(f"Ask prices: {[f'{p:.2f}' for p in ask_prices]}")
|
|
|
|
# Define volume scale points (equal spacing)
|
|
volume_scale_points = [10, 30, 60, 150, 300]
|
|
|
|
# Create unified visualization with horizontal bars
|
|
all_prices = []
|
|
all_volumes = []
|
|
all_colors = []
|
|
|
|
# Add bid data (green horizontal bars)
|
|
for bp, bv in zip(bid_prices, bid_volumes):
|
|
all_prices.append(bp)
|
|
all_volumes.append(bv)
|
|
all_colors.append('green')
|
|
|
|
# Add ask data (red horizontal bars)
|
|
for ap, av in zip(ask_prices, ask_volumes):
|
|
all_prices.append(ap)
|
|
all_volumes.append(av)
|
|
all_colors.append('red')
|
|
|
|
# Map volumes to scale positions
|
|
def map_volume_to_scale(volume):
|
|
"""Map actual volume to scale position"""
|
|
if volume <= 10:
|
|
return 10
|
|
elif volume <= 30:
|
|
return 30
|
|
elif volume <= 60:
|
|
return 60
|
|
elif volume <= 150:
|
|
return 150
|
|
elif volume <= 300:
|
|
return 300
|
|
else:
|
|
return 300 # Cap at 300 for display
|
|
|
|
# Map volumes to display scale
|
|
mapped_volumes = [map_volume_to_scale(v) for v in all_volumes]
|
|
|
|
# Create horizontal bars (volume on x-axis, price on y-axis)
|
|
bars = ax.barh(all_prices, mapped_volumes, height=min_tick * 0.8,
|
|
color=all_colors, alpha=0.7,
|
|
edgecolor='darkgreen' if 'green' in all_colors else 'darkred',
|
|
linewidth=1)
|
|
|
|
# Add volume labels on bars
|
|
for price, volume, mapped_vol in zip(all_prices, all_volumes, mapped_volumes):
|
|
if volume > 0:
|
|
ax.text(mapped_vol + 5, price, f'{int(volume):,}',
|
|
ha='left', va='center', fontsize=10, fontweight='bold')
|
|
|
|
# Set up price axis with true tick spacing
|
|
price_range = 1.0
|
|
min_price = current_price - price_range/2
|
|
max_price = current_price + price_range/2
|
|
|
|
tick_prices = np.arange(np.floor(min_price / min_tick) * min_tick,
|
|
np.ceil(max_price / min_tick) * min_tick + min_tick,
|
|
min_tick)
|
|
|
|
ax.set_xticks(tick_prices[::3]) # Show every 3rd tick
|
|
ax.set_xticklabels([f'{p:.2f}' for p in tick_prices[::3]], rotation=45)
|
|
ax.set_xlim(min_price, max_price)
|
|
|
|
# Add current price line
|
|
ax.axvline(x=current_price, color='blue', linestyle='--', alpha=0.8, linewidth=2,
|
|
label=f'Last Price: {current_price:.2f}')
|
|
|
|
# Highlight spread
|
|
if bid_prices and ask_prices:
|
|
best_bid = max(bid_prices)
|
|
best_ask = min(ask_prices)
|
|
spread = best_ask - best_bid
|
|
|
|
ax.axvspan(best_bid, best_ask, alpha=0.3, color='yellow',
|
|
label=f'Spread: {spread:.2f}')
|
|
|
|
print(f"Best bid: {best_bid:.2f}, Best ask: {best_ask:.2f}")
|
|
print(f"Bid-ask spread: {spread:.2f}")
|
|
|
|
# Set y-axis with true tick spacing (price)
|
|
ax.set_yticks(tick_prices[::3]) # Show every 3rd tick to avoid crowding
|
|
ax.set_yticklabels([f'{p:.2f}' for p in tick_prices[::3]])
|
|
ax.set_ylim(min_price, max_price)
|
|
|
|
# Set x-axis with defined volume scale points
|
|
ax.set_xticks(volume_scale_points)
|
|
ax.set_xticklabels([f'{v}' for v in volume_scale_points])
|
|
ax.set_xlim(0, max(volume_scale_points) * 1.2)
|
|
|
|
# Labels and formatting
|
|
ax.set_xlabel('Volume', fontsize=12)
|
|
ax.set_ylabel('Price (¥)', fontsize=12)
|
|
ax.set_title('Unified Market Depth View', fontsize=14)
|
|
ax.grid(True, alpha=0.3, axis='x')
|
|
|
|
# Legend
|
|
import matplotlib.patches as mpatches
|
|
bid_patch = mpatches.Patch(color='green', alpha=0.7, label='Bid Volume')
|
|
ask_patch = mpatches.Patch(color='red', alpha=0.7, label='Ask Volume')
|
|
ax.legend(handles=[bid_patch, ask_patch], loc='upper right')
|
|
|
|
# Add annotations
|
|
ax.text(0.02, 0.98, f"Time: {sample_row['time']}",
|
|
transform=ax.transAxes, fontsize=10,
|
|
verticalalignment='top',
|
|
bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.8))
|
|
|
|
ax.text(0.02, 0.92, f"Tick Size: {min_tick}",
|
|
transform=ax.transAxes, fontsize=10,
|
|
verticalalignment='top',
|
|
bbox=dict(boxstyle='round', facecolor='lightblue', alpha=0.8))
|
|
|
|
plt.tight_layout()
|
|
|
|
# Save the demo chart
|
|
output_file = 'unified_market_depth_demo.png'
|
|
plt.savefig(output_file, dpi=150, bbox_inches='tight')
|
|
print(f"\nDemo chart saved as: {output_file}")
|
|
|
|
plt.show()
|
|
print("\nThis demo shows the unified market depth visualization where:")
|
|
print("- Green horizontal bars: Bid volumes at different price levels")
|
|
print("- Red horizontal bars: Ask volumes at different price levels")
|
|
print("- Blue dashed line: Current last price (horizontal)")
|
|
print("- Yellow shaded area: Bid-ask spread (horizontal)")
|
|
print("- Price axis (vertical) shows true minimum tick spacing (0.02)")
|
|
print("- Volume axis (horizontal) uses simplified scale: 10, 30, 60, 150, 300 (equal spacing)")
|
|
print("- Volume bars extend rightward with sizes matching the scale points")
|
|
print("- Volumes larger than 300 are capped at 300 for display consistency")
|
|
|
|
if __name__ == "__main__":
|
|
demo_unified_chart() |