mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-18 10:38:05 +00:00
style patterns
This commit is contained in:
@@ -24,7 +24,11 @@ public sealed class CsvFeedTests : IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
|
||||
@@ -883,4 +887,4 @@ public sealed class CsvFeedTests : IDisposable
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,10 +74,14 @@ public sealed class CsvFeed : IFeed
|
||||
public CsvFeed(string filePath)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(filePath))
|
||||
{
|
||||
throw new ArgumentException("File path cannot be null or empty", nameof(filePath));
|
||||
}
|
||||
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
throw new FileNotFoundException($"CSV file not found: {filePath}", filePath);
|
||||
}
|
||||
|
||||
FilePath = filePath;
|
||||
Data = LoadFromCsv(filePath);
|
||||
@@ -97,18 +101,24 @@ public sealed class CsvFeed : IFeed
|
||||
{
|
||||
var header = reader.ReadLine();
|
||||
if (header is null)
|
||||
{
|
||||
throw new InvalidDataException("CSV file is empty");
|
||||
}
|
||||
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
var line = reader.ReadLine();
|
||||
if (!string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
dataLines.Add(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dataLines.Count == 0)
|
||||
{
|
||||
throw new InvalidDataException("CSV file contains only header, no data");
|
||||
}
|
||||
|
||||
// Reverse in-place to chronological order (oldest first)
|
||||
dataLines.Reverse();
|
||||
@@ -292,7 +302,9 @@ public sealed class CsvFeed : IFeed
|
||||
public TBarSeries Fetch(int count, long startTime, TimeSpan interval)
|
||||
{
|
||||
if (count <= 0)
|
||||
{
|
||||
throw new ArgumentException("Count must be positive", nameof(count));
|
||||
}
|
||||
|
||||
var result = new TBarSeries(count);
|
||||
|
||||
@@ -300,7 +312,9 @@ public sealed class CsvFeed : IFeed
|
||||
int startIndex = FindStartIndex(startTime);
|
||||
|
||||
if (startIndex == -1)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
// Collect bars matching interval
|
||||
long expectedTime = startTime;
|
||||
@@ -349,13 +363,19 @@ public sealed class CsvFeed : IFeed
|
||||
private int FindStartIndex(long startTime)
|
||||
{
|
||||
if (Count == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (Data[0].Time >= startTime)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (Data[Count - 1].Time < startTime)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int left = 0;
|
||||
int right = Count - 1;
|
||||
@@ -365,9 +385,13 @@ public sealed class CsvFeed : IFeed
|
||||
int mid = left + (right - left) / 2;
|
||||
|
||||
if (Data[mid].Time < startTime)
|
||||
{
|
||||
left = mid + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
right = mid;
|
||||
}
|
||||
}
|
||||
|
||||
return left;
|
||||
@@ -391,7 +415,9 @@ public sealed class CsvFeed : IFeed
|
||||
public void Reset(int index)
|
||||
{
|
||||
if (index < 0 || index > Count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(index), index, $"Index must be between 0 and {Count}");
|
||||
}
|
||||
|
||||
_currentIndex = index;
|
||||
_hasCurrentBar = false;
|
||||
@@ -407,7 +433,9 @@ public sealed class CsvFeed : IFeed
|
||||
public TBar GetBar(int index)
|
||||
{
|
||||
if (index < 0 || index >= Count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(index), index, $"Index must be between 0 and {Count - 1}");
|
||||
}
|
||||
|
||||
return Data[index];
|
||||
}
|
||||
@@ -416,4 +444,4 @@ public sealed class CsvFeed : IFeed
|
||||
/// Gets the underlying data series (read-only access).
|
||||
/// </summary>
|
||||
public TBarSeries Data { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -584,12 +584,17 @@ public class GBMTests
|
||||
{
|
||||
double mean = 0;
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
mean += values[i];
|
||||
}
|
||||
|
||||
mean /= values.Length;
|
||||
|
||||
double sumSquares = 0;
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
sumSquares += (values[i] - mean) * (values[i] - mean);
|
||||
}
|
||||
|
||||
return Math.Sqrt(sumSquares / values.Length);
|
||||
}
|
||||
|
||||
@@ -75,7 +75,10 @@ public static class ValidationHelper
|
||||
double qValue = qSeries[i].Value;
|
||||
double? sValue = selector(sSeries[i]);
|
||||
|
||||
if (!sValue.HasValue) continue;
|
||||
if (!sValue.HasValue)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Assert.True(
|
||||
Math.Abs(qValue - sValue.Value) <= tolerance,
|
||||
@@ -103,7 +106,10 @@ public static class ValidationHelper
|
||||
double qValue = qResults[i];
|
||||
double? sValue = selector(sSeries[i]);
|
||||
|
||||
if (!sValue.HasValue) continue;
|
||||
if (!sValue.HasValue)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Assert.True(
|
||||
Math.Abs(qValue - sValue.Value) <= tolerance,
|
||||
@@ -131,7 +137,10 @@ public static class ValidationHelper
|
||||
double qValue = qOutput[i];
|
||||
double? sValue = selector(sSeries[i]);
|
||||
|
||||
if (!sValue.HasValue) continue;
|
||||
if (!sValue.HasValue)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Assert.True(
|
||||
Math.Abs(qValue - sValue.Value) <= tolerance,
|
||||
@@ -161,10 +170,16 @@ public static class ValidationHelper
|
||||
{
|
||||
double qValue = qSeries[i].Value;
|
||||
|
||||
if (i < lookback) continue;
|
||||
if (i < lookback)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int tIndex = i - lookback;
|
||||
if (tIndex >= tOutput.Length) continue;
|
||||
if (tIndex >= tOutput.Length)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
double tValue = tOutput[tIndex];
|
||||
|
||||
@@ -191,10 +206,16 @@ public static class ValidationHelper
|
||||
{
|
||||
double qValue = qResults[i];
|
||||
|
||||
if (i < lookback) continue;
|
||||
if (i < lookback)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int tIndex = i - lookback;
|
||||
if (tIndex >= tOutput.Length) continue;
|
||||
if (tIndex >= tOutput.Length)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
double tValue = tOutput[tIndex];
|
||||
|
||||
@@ -221,10 +242,16 @@ public static class ValidationHelper
|
||||
{
|
||||
double qValue = qOutput[i];
|
||||
|
||||
if (i < lookback) continue;
|
||||
if (i < lookback)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int tIndex = i - lookback;
|
||||
if (tIndex >= tOutput.Length) continue;
|
||||
if (tIndex >= tOutput.Length)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
double tValue = tOutput[tIndex];
|
||||
|
||||
@@ -253,10 +280,16 @@ public static class ValidationHelper
|
||||
{
|
||||
double qValue = qSeries[i].Value;
|
||||
|
||||
if (i < lookback) continue;
|
||||
if (i < lookback)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int tIndex = i - offset;
|
||||
if (tIndex < 0 || tIndex >= length) continue;
|
||||
if (tIndex < 0 || tIndex >= length)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
double tValue = tOutput[tIndex];
|
||||
|
||||
@@ -285,10 +318,16 @@ public static class ValidationHelper
|
||||
{
|
||||
double qValue = qResults[i];
|
||||
|
||||
if (i < lookback) continue;
|
||||
if (i < lookback)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int tIndex = i - offset;
|
||||
if (tIndex < 0 || tIndex >= length) continue;
|
||||
if (tIndex < 0 || tIndex >= length)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
double tValue = tOutput[tIndex];
|
||||
|
||||
@@ -317,10 +356,16 @@ public static class ValidationHelper
|
||||
{
|
||||
double qValue = qOutput[i];
|
||||
|
||||
if (i < lookback) continue;
|
||||
if (i < lookback)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int tIndex = i - offset;
|
||||
if (tIndex < 0 || tIndex >= length) continue;
|
||||
if (tIndex < 0 || tIndex >= length)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
double tValue = tOutput[tIndex];
|
||||
|
||||
@@ -388,18 +433,25 @@ public static class ValidationHelper
|
||||
Func<TResult, double?> selector)
|
||||
{
|
||||
if (qSeries.Count != sSeries.Count)
|
||||
{
|
||||
throw new ArgumentException("Series must have the same count", nameof(sSeries));
|
||||
}
|
||||
|
||||
double maxDiff = 0;
|
||||
|
||||
for (int i = 0; i < qSeries.Count; i++)
|
||||
{
|
||||
double? sValue = selector(sSeries[i]);
|
||||
if (!sValue.HasValue) continue;
|
||||
if (!sValue.HasValue)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
double diff = Math.Abs(qSeries[i].Value - sValue.Value);
|
||||
if (diff > maxDiff)
|
||||
{
|
||||
maxDiff = diff;
|
||||
}
|
||||
}
|
||||
|
||||
return maxDiff;
|
||||
@@ -415,20 +467,27 @@ public static class ValidationHelper
|
||||
Func<TResult, double?> selector)
|
||||
{
|
||||
if (qSeries.Count != sSeries.Count)
|
||||
{
|
||||
throw new ArgumentException("Series must have the same count", nameof(sSeries));
|
||||
}
|
||||
|
||||
double maxDiff = 0;
|
||||
|
||||
for (int i = 0; i < qSeries.Count; i++)
|
||||
{
|
||||
double? sValue = selector(sSeries[i]);
|
||||
if (!sValue.HasValue || Math.Abs(sValue.Value) < double.Epsilon) continue;
|
||||
if (!sValue.HasValue || Math.Abs(sValue.Value) < double.Epsilon)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
double relDiff = Math.Abs((qSeries[i].Value - sValue.Value) / sValue.Value);
|
||||
if (relDiff > maxDiff)
|
||||
{
|
||||
maxDiff = relDiff;
|
||||
}
|
||||
}
|
||||
|
||||
return maxDiff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +178,9 @@ public sealed class ValidationTestData : IDisposable
|
||||
public ValidationTestData CreateSubset(int count)
|
||||
{
|
||||
if (count <= 0 || count > Count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(count), count, $"Count must be between 1 and {Count}");
|
||||
}
|
||||
|
||||
return new ValidationTestData(count, DefaultStartPrice, DefaultMu, DefaultSigma, DefaultSeed);
|
||||
}
|
||||
@@ -216,4 +218,4 @@ public sealed class ValidationTestData : IDisposable
|
||||
// No unmanaged resources to dispose
|
||||
// Implemented for IDisposable pattern compatibility with test fixtures
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+50
-6
@@ -75,22 +75,30 @@ public sealed class GBM : IFeed
|
||||
{
|
||||
// Validate startPrice
|
||||
if (startPrice <= 0 || !double.IsFinite(startPrice))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(startPrice), startPrice, "Start price must be positive and finite");
|
||||
}
|
||||
|
||||
// Validate mu
|
||||
if (!double.IsFinite(mu))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(mu), mu, "Drift (mu) must be finite");
|
||||
}
|
||||
|
||||
// Validate sigma
|
||||
if (sigma < 0 || !double.IsFinite(sigma))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(sigma), sigma, "Volatility (sigma) must be non-negative and finite");
|
||||
}
|
||||
|
||||
// Use provided timeframe or default to 1 minute
|
||||
var timeframe = defaultTimeframe ?? TimeSpan.FromMinutes(1);
|
||||
|
||||
// Validate timeframe
|
||||
if (timeframe <= TimeSpan.Zero)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(defaultTimeframe), defaultTimeframe, "Timeframe must be positive");
|
||||
}
|
||||
|
||||
_rnd = seed.HasValue ? new Random(seed.Value) : null;
|
||||
StartPrice = startPrice;
|
||||
@@ -170,7 +178,9 @@ public sealed class GBM : IFeed
|
||||
|
||||
// Guard against log(0) which produces -Infinity
|
||||
if (u1 <= double.Epsilon)
|
||||
{
|
||||
u1 = double.Epsilon;
|
||||
}
|
||||
|
||||
double mag = Math.Sqrt(-2.0 * Math.Log(u1));
|
||||
double angle = 2.0 * Math.PI * u2;
|
||||
@@ -200,7 +210,9 @@ public sealed class GBM : IFeed
|
||||
|
||||
// Ensure price stays positive and finite
|
||||
if (!double.IsFinite(price) || price <= 0)
|
||||
{
|
||||
price = _lastPrice;
|
||||
}
|
||||
|
||||
double volume = 1000 + NextDouble() * 1000;
|
||||
|
||||
@@ -232,7 +244,9 @@ public sealed class GBM : IFeed
|
||||
|
||||
// Ensure price stays positive and finite
|
||||
if (!double.IsFinite(price) || price <= 0)
|
||||
{
|
||||
price = _lastPrice;
|
||||
}
|
||||
|
||||
double additionalVolume = 1000 + NextDouble() * 1000;
|
||||
|
||||
@@ -274,9 +288,14 @@ public sealed class GBM : IFeed
|
||||
public TBarSeries Fetch(int count, long startTime, TimeSpan interval)
|
||||
{
|
||||
if (count <= 0)
|
||||
{
|
||||
throw new ArgumentException("Count must be positive", nameof(count));
|
||||
}
|
||||
|
||||
if (interval <= TimeSpan.Zero)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(interval), interval, "Interval must be positive");
|
||||
}
|
||||
|
||||
var series = new TBarSeries(count);
|
||||
|
||||
@@ -333,12 +352,35 @@ public sealed class GBM : IFeed
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (rentedT != null) ArrayPool<long>.Shared.Return(rentedT);
|
||||
if (rentedO != null) ArrayPool<double>.Shared.Return(rentedO);
|
||||
if (rentedH != null) ArrayPool<double>.Shared.Return(rentedH);
|
||||
if (rentedL != null) ArrayPool<double>.Shared.Return(rentedL);
|
||||
if (rentedC != null) ArrayPool<double>.Shared.Return(rentedC);
|
||||
if (rentedV != null) ArrayPool<double>.Shared.Return(rentedV);
|
||||
if (rentedT != null)
|
||||
{
|
||||
ArrayPool<long>.Shared.Return(rentedT);
|
||||
}
|
||||
|
||||
if (rentedO != null)
|
||||
{
|
||||
ArrayPool<double>.Shared.Return(rentedO);
|
||||
}
|
||||
|
||||
if (rentedH != null)
|
||||
{
|
||||
ArrayPool<double>.Shared.Return(rentedH);
|
||||
}
|
||||
|
||||
if (rentedL != null)
|
||||
{
|
||||
ArrayPool<double>.Shared.Return(rentedL);
|
||||
}
|
||||
|
||||
if (rentedC != null)
|
||||
{
|
||||
ArrayPool<double>.Shared.Return(rentedC);
|
||||
}
|
||||
|
||||
if (rentedV != null)
|
||||
{
|
||||
ArrayPool<double>.Shared.Return(rentedV);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,7 +413,9 @@ public sealed class GBM : IFeed
|
||||
|
||||
// Ensure price stays positive and finite
|
||||
if (!double.IsFinite(price) || price <= 0)
|
||||
{
|
||||
price = currentPrice;
|
||||
}
|
||||
|
||||
double open = currentPrice;
|
||||
double close = price;
|
||||
|
||||
Reference in New Issue
Block a user