CBitBuffer/TicksShort_VLQ/TicksShort.mqh

170 lines
14 KiB
MQL5
Raw Permalink Normal View History

2025-10-07 00:22:12 +03:00
<EFBFBD><EFBFBD>//+------------------------------------------------------------------+
//| TicksShort.mqh |
//| Copyright <EFBFBD> 2025, Amr Ali |
//| https://www.mql5.com/en/users/amrali |
//+------------------------------------------------------------------+
#property copyright "Copyright <00> 2025, Amr Ali"
#property link "https://www.mql5.com/en/users/amrali"
#property version "1.02"
#property description "Implementation of the 'TicksShort' library by 'fxsaber' utilizing VLQ encoding."
#property description "Source: https://www.mql5.com/ru/code/61126"
//+------------------------------------------------------------------+
//| Class: TICKS_SHORT |
//| Description: Delta + VLQ Compression for MqlTick[] |
//| Features: |
//| - Delta encoding of time, bid, and ask prices |
//| - ZigZag transform to handle signed values |
//| - VLQ variable-length integer encoding |
//| - Optimized for speed and minimal allocations |
//+------------------------------------------------------------------+
#include "..\CBitBuffer.mqh"
class TICKS_SHORT
{
private:
static long PriceToInteger(const double price, const double power)
{
//return (long)MathRound(price * power);
return (long)(price * power + 0.5);
}
public:
2025-10-07 16:02:51 +03:00
//+---------------------------------------------------------------+
//| Compress ticks into ulong buffer |
//+---------------------------------------------------------------+
2025-10-07 00:22:12 +03:00
static int Compress(const MqlTick &ticks[], ulong &compressed[], const int iDigits = -1)
{
const int nticks = ArraySize(ticks);
if (nticks == 0) { ArrayFree(compressed); return 0; }
2025-10-07 16:02:51 +03:00
2025-10-07 00:22:12 +03:00
const int digits = (iDigits < 0) ? _Digits : MathMin(iDigits, 7);
const double power = MathPow(10, digits);
2025-10-07 16:02:51 +03:00
2025-10-07 00:22:12 +03:00
CBitBuffer buffer(nticks * 7); // Pre-allocate 7 bytes per tick (rough upper bound)
buffer.WriteInt(nticks);
buffer.WriteVarInt(digits);
2025-10-07 16:02:51 +03:00
long prev_time = 0, prev_bid = 0, prev_ask = 0;
for (int i = 0; i < nticks; i++)
2025-10-07 00:22:12 +03:00
{
2025-10-07 16:02:51 +03:00
const MqlTick t = ticks[i];
buffer.WriteVarLong(t.time_msc - prev_time); // ZigZag + VLQ encode each delta
buffer.WriteVarLong(PriceToInteger(t.bid, power) - prev_bid);
buffer.WriteVarLong(PriceToInteger(t.ask, power) - prev_ask);
2025-10-07 00:22:12 +03:00
if (buffer.GetLastError() != BIT_BUFFER_NO_ERROR)
{
PrintFormat("ERROR: Caught buffer error in Compress: %s", buffer.GetLastErrorString());
return -1; // Error in compression
}
2025-10-07 16:02:51 +03:00
prev_time = t.time_msc; // Update previous
prev_bid = PriceToInteger(t.bid, power);
prev_ask = PriceToInteger(t.ask, power);
2025-10-07 00:22:12 +03:00
}
2025-10-07 16:02:51 +03:00
if (!buffer.GetFinalizedBuffer(compressed)) // Export to compressed[]
2025-10-07 00:22:12 +03:00
return -1;
2025-10-07 16:02:51 +03:00
2025-10-07 00:22:12 +03:00
return ArraySize(compressed); // Number of ULongs written
}
2025-10-07 16:02:51 +03:00
//+---------------------------------------------------------------+
//| Decompress ulong buffer back to ticks |
//+---------------------------------------------------------------+
2025-10-07 00:22:12 +03:00
static int Decompress(const ulong &compressed[], MqlTick &out_ticks[])
{
const int size = ArraySize(compressed);
if (size == 0) { ArrayFree(out_ticks); return 0; }
2025-10-07 16:02:51 +03:00
2025-10-07 00:22:12 +03:00
CBitBuffer buffer;
2025-10-07 16:02:51 +03:00
if(!buffer.SetRawBuffer(compressed)) // Import from compressed[]
return -1;
2025-10-07 00:22:12 +03:00
const int totalCompressedTicks = buffer.ReadInt();
const int digits = buffer.ReadVarInt();
const double point = MathPow(10, -digits);
2025-10-07 16:02:51 +03:00
2025-10-07 00:22:12 +03:00
if (ArrayResize(out_ticks, totalCompressedTicks) == -1)
{
Print("ERROR: Failed to resize ticks array in Decompress.");
return -1;
}
2025-10-07 16:02:51 +03:00
long time = 0, bid = 0, ask = 0;
for (int i = 0; i < totalCompressedTicks; i++)
2025-10-07 00:22:12 +03:00
{
2025-10-07 16:02:51 +03:00
time += buffer.ReadVarLong(); // Delta reconstruction
bid += buffer.ReadVarLong();
ask += buffer.ReadVarLong();
2025-10-07 00:22:12 +03:00
if (buffer.GetLastError() != BIT_BUFFER_NO_ERROR)
{
PrintFormat("ERROR: Caught buffer error in Decompress: %s", buffer.GetLastErrorString());
return -1; // Error in decompression
}
2025-10-07 16:02:51 +03:00
MqlTick tick = {};
2025-10-07 00:22:12 +03:00
tick.time_msc = time;
2025-10-07 16:02:51 +03:00
tick.bid = NormalizeDouble(bid * point, digits);
tick.ask = NormalizeDouble(ask * point, digits);
tick.time = (datetime)(time / 1000);
out_ticks[i] = tick; // Append reconstructed tick
2025-10-07 00:22:12 +03:00
}
2025-10-07 16:02:51 +03:00
2025-10-07 00:22:12 +03:00
return ArraySize(out_ticks); // Number of MqlTicks written
}
2025-10-07 16:02:51 +03:00
//+---------------------------------------------------------------+
//| Compress ticks and save to file |
//+---------------------------------------------------------------+
2025-10-07 00:22:12 +03:00
static bool Save(const string fileName, const MqlTick &ticks[],
const bool common = false, const int iDigits = -1)
{
ulong compressed[];
int count = Compress(ticks, compressed, iDigits);
if (count < 0) return false; // Error in compression
return FileSave(fileName, compressed, (common ? FILE_COMMON : 0));
}
2025-10-07 16:02:51 +03:00
//+---------------------------------------------------------------+
//| Load compressed ticks from file and decompress |
//+---------------------------------------------------------------+
2025-10-07 00:22:12 +03:00
static int Load(const string fileName, MqlTick &ticks[], const bool common = false)
{
ulong compressed[];
int countLoaded = (int)FileLoad(fileName, compressed, (common ? FILE_COMMON : 0));
if (countLoaded <= 0) { ArrayFree(ticks); return countLoaded; }
ArrayResize(compressed, countLoaded);
int decompressedCount = Decompress(compressed, ticks);
return(decompressedCount);
}
2025-10-07 16:02:51 +03:00
//+---------------------------------------------------------------+
//| Equality check functions |
//+---------------------------------------------------------------+
2025-10-07 00:22:12 +03:00
static bool IsEqual(const MqlTick &Tick1, const MqlTick &Tick2)
{
return((Tick1.bid == Tick2.bid) && (Tick1.ask == Tick2.ask) && (Tick1.time_msc == Tick2.time_msc));
}
static bool IsEqual(const MqlTick &Ticks1[], const MqlTick &Ticks2[])
{
const int size = ArraySize(Ticks1);
bool Res = (ArraySize(Ticks2) == size);
for (int i = 0; Res && (i < size); i++)
Res = IsEqual(Ticks1[i], Ticks2[i]);
return(Res);
}
static bool IsEqual(const MqlTick &Ticks[], const ulong &Compressed[])
{
MqlTick Ticks2[];
int decompressedCount = Decompress(Compressed, Ticks2);
if (decompressedCount == -1) return false; // Decompression error
return IsEqual(Ticks, Ticks2);
}
};