"""
Freqtrade feather to Zorro T6 format converter
T6 structure (32 bytes per record):
- time: double (8 bytes) - OLE datetime (days since 1899-12-30)
- fHigh: float (4 bytes)
- fLow: float (4 bytes)
- fOpen: float (4 bytes)
- fClose: float (4 bytes)
- fVal: float (4 bytes) - price multiplier or point value
- fVol: float (4 bytes) - volume
"""
import os
import struct
import pandas as pd
from datetime import datetime, timezone
from pathlib import Path
# OLE datetime base: 1899-12-30 00:00:00 UTC
OLE_EPOCH = datetime(1899, 12, 30, tzinfo=timezone.utc)
def datetime_to_ole(dt):
"""Convert datetime to OLE datetime format"""
if dt.tzinfo is None:
dt = dt.replace(tzinfo=timezone.utc)
delta = dt - OLE_EPOCH
return delta.total_seconds() / 86400.0 # Convert to days
def write_t6_record(f, time_ole, high, low, open_, close, val, vol):
"""Write single T6 record"""
# Format: d=double, f=float, little-endian
record = struct.pack('<d4f2f', time_ole, high, low, open_, close, val, vol)
f.write(record)
def convert_feather_to_t6(feather_path, t6_path, price_multiplier=1.0):
"""
Convert single feather file to t6 file
Parameters:
feather_path: Path to feather file
t6_path: Output t6 file path
price_multiplier: Price multiplier (for fVal field)
"""
# Read feather file
df = pd.read_feather(feather_path)
# Ensure reverse chronological order (Zorro requires newest to oldest)
df = df.sort_values('date', ascending=False).reset_index(drop=True)
# Write t6 file
with open(t6_path, 'wb') as f:
for _, row in df.iterrows():
time_ole = datetime_to_ole(row['date'].to_pydatetime())
write_t6_record(
f,
time_ole,
float(row['high']),
float(row['low']),
float(row['open']),
float(row['close']),
price_multiplier,
float(row['volume'])
)
return len(df)
def batch_convert(input_dir, output_dir, pattern="*-1h-futures.feather"):
"""
Batch convert all feather files in directory
Parameters:
input_dir: Input directory
output_dir: Output directory
pattern: File matching pattern
"""
input_path = Path(input_dir)
output_path = Path(output_dir)
output_path.mkdir(parents=True, exist_ok=True)
feather_files = list(input_path.glob(pattern))
print(f"Found {len(feather_files)} feather files")
for i, feather_file in enumerate(feather_files, 1):
# Generate output filename: BTC_USDT_USDT-1h-futures.feather -> BTC_USDT.t6
stem = feather_file.stem # Without extension
# Extract symbol name
parts = stem.split('-')
symbol = parts[0].replace('_USDT_USDT', '_USDT')
t6_filename = f"{symbol}.t6"
t6_path = output_path / t6_filename
try:
count = convert_feather_to_t6(feather_file, t6_path)
print(f"[{i}/{len(feather_files)}] {feather_file.name} -> {t6_filename} ({count} records)")
except Exception as e:
print(f"[{i}/{len(feather_files)}] Error: {feather_file.name} - {e}")
if __name__ == "__main__":
# Configure paths
INPUT_DIR = r"D:\freqtradeStudy\user_data\data\binance\futures"
OUTPUT_DIR = r"D:\freqtradeStudy\user_data\data\zorro_t6"
# Execute batch conversion
print("Starting feather -> t6 conversion...")
print(f"Input directory: {INPUT_DIR}")
print(f"Output directory: {OUTPUT_DIR}")
print("-" * 50)
batch_convert(INPUT_DIR, OUTPUT_DIR, pattern="*-1h-futures.feather")
print("-" * 50)
print("Conversion complete!")