Files
cTrader-Algo/Sources/Common/MSLib/ZorroBinaryFiles.cs
T
Michael Schimmel a1d2e96f8a Initial commit
2026-01-28 09:10:52 +01:00

286 lines
9.9 KiB
C#

using System;
using System.Collections.Generic; // Required for List<T>
using System.IO; // For File operations
using System.Runtime.InteropServices; // Required for StructLayout and Marshal
namespace Zorro
{
/// <summary>
/// Provides generic methods for loading and saving arrays of structures
/// from/to binary files using marshalling.
/// This class is intended for internal use within this assembly.
/// </summary>
internal static class BinaryFileHandler
{
public static void SaveArrayAsBlock<T>(string filePath, T[] data) where T : struct
{
if (data == null || data.Length == 0)
{
File.WriteAllBytes(filePath, Array.Empty<byte>());
return;
}
int structSize = Marshal.SizeOf(typeof(T));
byte[] byteArray = new byte[data.Length * structSize];
IntPtr buffer = Marshal.AllocHGlobal(structSize);
try
{
for (int i = 0; i < data.Length; i++)
{
Marshal.StructureToPtr(data[i], buffer, false);
Marshal.Copy(buffer, byteArray, i * structSize, structSize);
}
File.WriteAllBytes(filePath, byteArray);
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}
public static T[]? LoadArrayAsBlock<T>(string filePath) where T : struct
{
if (!File.Exists(filePath))
{
return null;
}
byte[] byteArray = File.ReadAllBytes(filePath);
if (byteArray.Length == 0)
{
return Array.Empty<T>();
}
int structSize = Marshal.SizeOf(typeof(T));
if (byteArray.Length % structSize != 0)
{
throw new ArgumentException($"File size ({byteArray.Length} bytes) is not a valid multiple of the structure size ({structSize} bytes) for type {typeof(T).Name}. The file may be corrupted or not in the expected format.");
}
int itemCount = byteArray.Length / structSize;
T[] dataArray = new T[itemCount];
IntPtr buffer = Marshal.AllocHGlobal(structSize);
try
{
for (int i = 0; i < itemCount; i++)
{
Marshal.Copy(byteArray, i * structSize, buffer, structSize);
dataArray[i] = Marshal.PtrToStructure<T>(buffer);
}
}
finally
{
Marshal.FreeHGlobal(buffer);
}
return dataArray;
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct ZorroT1
{
public double OADate;
public float Value;
/// <summary>
/// Returns the time as a UTC DateTime object.
/// Note: DateTime.FromOADate returns a DateTime with Kind=Unspecified.
/// We specify it as UTC as Zorro DATEs are GMT/UTC.
/// </summary>
public DateTime TimeUtc => DateTime.SpecifyKind(DateTime.FromOADate(OADate), DateTimeKind.Utc);
public ZorroT1(double oaDate, float value)
{
OADate = oaDate;
Value = value;
}
public ZorroT1(DateTime dateTime, float value)
{
// Ensure the DateTime is converted to its UTC equivalent for OADate
OADate = dateTime.ToUniversalTime().ToOADate();
Value = value;
}
public static void SaveToFile(string filePath, ZorroT1[] data) => BinaryFileHandler.SaveArrayAsBlock<ZorroT1>(filePath, data);
public static ZorroT1[]? LoadFromFile(string filePath) => BinaryFileHandler.LoadArrayAsBlock<ZorroT1>(filePath);
}
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct ZorroT2
{
public double OADate;
public float Price;
public float Volume;
public DateTime TimeUtc => DateTime.SpecifyKind(DateTime.FromOADate(OADate), DateTimeKind.Utc);
public ZorroT2(double oaDate, float price, float volume)
{
OADate = oaDate;
Price = price;
Volume = volume;
}
public ZorroT2(DateTime dateTime, float price, float volume)
{
OADate = dateTime.ToUniversalTime().ToOADate();
Price = price;
Volume = volume;
}
public static void SaveToFile(string filePath, ZorroT2[] data) => BinaryFileHandler.SaveArrayAsBlock<ZorroT2>(filePath, data);
public static ZorroT2[]? LoadFromFile(string filePath) => BinaryFileHandler.LoadArrayAsBlock<ZorroT2>(filePath);
}
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct ZorroT6
{
public double OADate;
public float High;
public float Low;
public float Open;
public float Close;
public float AdditionalValue1;
public float AdditionalValue2;
public DateTime TimeUtc => DateTime.SpecifyKind(DateTime.FromOADate(OADate), DateTimeKind.Utc);
public ZorroT6(double oaDate, float high, float low, float open, float close, float additionalValue1, float additionalValue2)
{
OADate = oaDate;
High = high;
Low = low;
Open = open;
Close = close;
AdditionalValue1 = additionalValue1;
AdditionalValue2 = additionalValue2;
}
public ZorroT6(DateTime dateTime, float high, float low, float open, float close, float additionalValue1, float additionalValue2)
{
OADate = dateTime.ToUniversalTime().ToOADate();
High = high;
Low = low;
Open = open;
Close = close;
AdditionalValue1 = additionalValue1;
AdditionalValue2 = additionalValue2;
}
public static void SaveToFile(string filePath, ZorroT6[] data) => BinaryFileHandler.SaveArrayAsBlock<ZorroT6>(filePath, data);
public static ZorroT6[]? LoadFromFile(string filePath) => BinaryFileHandler.LoadArrayAsBlock<ZorroT6>(filePath);
}
// --- Builder Classes for Incremental Array Construction ---
public class ZorroT1ArrayBuilder
{
private readonly List<ZorroT1> _items;
public ZorroT1ArrayBuilder(int initialCapacity = 0)
{
_items = initialCapacity > 0 ? new List<ZorroT1>(initialCapacity) : new List<ZorroT1>();
}
public ZorroT1ArrayBuilder Add(DateTime time, float value)
{
// Constructor of ZorroT1 now handles UTC conversion
_items.Add(new ZorroT1(time, value));
return this;
}
public ZorroT1ArrayBuilder Add(double oaDate, float value)
{
_items.Add(new ZorroT1(oaDate, value));
return this;
}
public ZorroT1ArrayBuilder Add(ZorroT1 item)
{
_items.Add(item);
return this;
}
public ZorroT1[] ToArray() => _items.ToArray();
public void Clear() => _items.Clear();
public int Count => _items.Count;
public void SaveToFile(string filePath)
{
ZorroT1.SaveToFile(filePath, _items.ToArray());
}
}
public class ZorroT2ArrayBuilder
{
private readonly List<ZorroT2> _items;
public ZorroT2ArrayBuilder(int initialCapacity = 0)
{
_items = initialCapacity > 0 ? new List<ZorroT2>(initialCapacity) : new List<ZorroT2>();
}
public ZorroT2ArrayBuilder Add(DateTime time, float price, float volume)
{
_items.Add(new ZorroT2(time, price, volume));
return this;
}
public ZorroT2ArrayBuilder Add(double oaDate, float price, float volume)
{
_items.Add(new ZorroT2(oaDate, price, volume));
return this;
}
public ZorroT2ArrayBuilder Add(ZorroT2 item)
{
_items.Add(item);
return this;
}
public ZorroT2[] ToArray() => _items.ToArray();
public void Clear() => _items.Clear();
public int Count => _items.Count;
public void SaveToFile(string filePath)
{
ZorroT2.SaveToFile(filePath, _items.ToArray());
}
}
public class ZorroT6ArrayBuilder
{
private readonly List<ZorroT6> _items;
public ZorroT6ArrayBuilder(int initialCapacity = 0)
{
_items = initialCapacity > 0 ? new List<ZorroT6>(initialCapacity) : new List<ZorroT6>();
}
public ZorroT6ArrayBuilder Add(DateTime time, float high, float low, float open, float close, float additionalValue1, float additionalValue2)
{
_items.Add(new ZorroT6(time, high, low, open, close, additionalValue1, additionalValue2));
return this;
}
public ZorroT6ArrayBuilder Add(double oaDate, float high, float low, float open, float close, float additionalValue1, float additionalValue2)
{
_items.Add(new ZorroT6(oaDate, high, low, open, close, additionalValue1, additionalValue2));
return this;
}
public ZorroT6ArrayBuilder Add(ZorroT6 item)
{
_items.Add(item);
return this;
}
public ZorroT6[] ToArray() => _items.ToArray();
public void Clear() => _items.Clear();
public int Count => _items.Count;
public void SaveToFile(string filePath)
{
ZorroT6.SaveToFile(filePath, _items.ToArray());
}
}
}