mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-14 08:38:04 +00:00
Add UCFG2 type definitions and lock file for QuanTAlib
- Introduced type definitions for various classes in the QuanTAlib library, including Ema, EmaVector, EmaState, TSeries, CsvFeed, GBM, TBarSeries, TBar, and TValue. - Added methods and properties for each class to enhance functionality and maintainability. - Created a lock file to manage dependencies and ensure consistent builds.
This commit is contained in:
+28
-19
@@ -12,7 +12,7 @@ namespace QuanTAlib;
|
||||
public class CsvFeed : IFeed
|
||||
{
|
||||
private readonly TBarSeries _data;
|
||||
|
||||
|
||||
// Streaming state
|
||||
private int _currentIndex;
|
||||
private TBar _currentBar;
|
||||
@@ -38,27 +38,36 @@ public class CsvFeed : IFeed
|
||||
/// <summary>
|
||||
/// Parses CSV file into TBarSeries.
|
||||
/// Expected format: timestamp,open,high,low,close,volume
|
||||
/// Memory-efficient: reads lines into list, reverses in-place (no LINQ allocations).
|
||||
/// </summary>
|
||||
private static TBarSeries LoadFromCsv(string filePath)
|
||||
{
|
||||
var lines = File.ReadAllLines(filePath);
|
||||
|
||||
if (lines.Length == 0)
|
||||
throw new InvalidDataException("CSV file is empty");
|
||||
var dataLines = new List<string>();
|
||||
using (var reader = new StreamReader(filePath))
|
||||
{
|
||||
var header = reader.ReadLine();
|
||||
if (header is null)
|
||||
throw new InvalidDataException("CSV file is empty");
|
||||
|
||||
// Skip header, reverse to chronological order (oldest first)
|
||||
var dataLines = lines.Skip(1).Reverse().ToArray();
|
||||
|
||||
if (dataLines.Length == 0)
|
||||
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");
|
||||
|
||||
var series = new TBarSeries(dataLines.Length);
|
||||
// Reverse in-place to chronological order (oldest first)
|
||||
dataLines.Reverse();
|
||||
|
||||
for (int i = 0; i < dataLines.Length; i++)
|
||||
var series = new TBarSeries(dataLines.Count);
|
||||
|
||||
for (int i = 0; i < dataLines.Count; i++)
|
||||
{
|
||||
var line = dataLines[i];
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
continue;
|
||||
|
||||
var parts = line.Split(',');
|
||||
if (parts.Length != 6)
|
||||
@@ -68,7 +77,7 @@ public class CsvFeed : IFeed
|
||||
{
|
||||
// Parse timestamp (YYYY-MM-DD format, assume UTC midnight)
|
||||
var timestamp = DateTime.ParseExact(parts[0].Trim(), "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
|
||||
|
||||
|
||||
// Parse OHLCV values
|
||||
double open = double.Parse(parts[1].Trim(), CultureInfo.InvariantCulture);
|
||||
double high = double.Parse(parts[2].Trim(), CultureInfo.InvariantCulture);
|
||||
@@ -142,7 +151,7 @@ public class CsvFeed : IFeed
|
||||
throw new ArgumentException("Count must be positive", nameof(count));
|
||||
|
||||
var result = new TBarSeries(count);
|
||||
|
||||
|
||||
// Find starting index
|
||||
int startIndex = 0;
|
||||
for (int i = 0; i < _data.Count; i++)
|
||||
@@ -157,15 +166,15 @@ public class CsvFeed : IFeed
|
||||
// Collect bars matching interval
|
||||
long expectedTime = startTime;
|
||||
int collected = 0;
|
||||
|
||||
|
||||
for (int i = startIndex; i < _data.Count && collected < count; i++)
|
||||
{
|
||||
var bar = _data[i];
|
||||
|
||||
|
||||
// Check if bar time matches expected time (within tolerance)
|
||||
long timeDiff = Math.Abs(bar.Time - expectedTime);
|
||||
long tolerance = interval.Ticks / 2; // Allow 50% tolerance
|
||||
|
||||
|
||||
if (timeDiff <= tolerance)
|
||||
{
|
||||
result.Add(bar, isNew: true);
|
||||
@@ -177,7 +186,7 @@ public class CsvFeed : IFeed
|
||||
// Gap in data - skip forward
|
||||
long gaps = (bar.Time - expectedTime) / interval.Ticks;
|
||||
expectedTime += (gaps + 1) * interval.Ticks;
|
||||
|
||||
|
||||
if (Math.Abs(bar.Time - expectedTime + interval.Ticks) <= tolerance)
|
||||
{
|
||||
result.Add(bar, isNew: true);
|
||||
|
||||
+17
-11
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace QuanTAlib;
|
||||
@@ -8,9 +7,10 @@ namespace QuanTAlib;
|
||||
/// Generates realistic price data for testing indicators and strategies.
|
||||
/// Stateless design - only maintains minimal state needed for price continuity.
|
||||
/// </summary>
|
||||
[SkipLocalsInit]
|
||||
public class GBM : IFeed
|
||||
{
|
||||
private readonly Random _rnd = new();
|
||||
private readonly Random _rnd;
|
||||
|
||||
private double _lastPrice;
|
||||
private long _lastTime;
|
||||
@@ -35,26 +35,32 @@ public class GBM : IFeed
|
||||
/// <summary>
|
||||
/// Creates a new GBM generator.
|
||||
/// </summary>
|
||||
/// <param name="startPrice">Initial price (default: 100.0)</param>
|
||||
/// <param name="startPrice">Initial price (default: 100.0, must be positive)</param>
|
||||
/// <param name="mu">Annual drift/return rate (default: 0.05 = 5%)</param>
|
||||
/// <param name="sigma">Annual volatility (default: 0.2 = 20%)</param>
|
||||
/// <param name="sigma">Annual volatility (default: 0.2 = 20%, must be non-negative)</param>
|
||||
/// <param name="defaultTimeframe">Default timeframe for bars (default: 1 minute)</param>
|
||||
/// <param name="seed">Optional random seed for reproducibility (default: null for non-deterministic)</param>
|
||||
public GBM(
|
||||
double startPrice = 100.0,
|
||||
double mu = 0.05,
|
||||
double sigma = 0.2,
|
||||
TimeSpan? defaultTimeframe = null)
|
||||
TimeSpan? defaultTimeframe = null,
|
||||
int? seed = null)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(startPrice);
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(sigma);
|
||||
|
||||
_rnd = seed.HasValue ? new Random(seed.Value) : new Random();
|
||||
_lastPrice = startPrice;
|
||||
_lastTime = DateTime.UtcNow.Ticks;
|
||||
|
||||
_mu = mu;
|
||||
_sigma = sigma;
|
||||
|
||||
|
||||
// Use provided timeframe or default to 1 minute
|
||||
var timeframe = defaultTimeframe ?? TimeSpan.FromMinutes(1);
|
||||
_defaultTimeStep = timeframe.Ticks;
|
||||
|
||||
|
||||
// Calculate dt based on timeframe (assuming 252 trading days/year, 6.5 hours/day)
|
||||
double minutesPerYear = 252.0 * 6.5 * 60.0;
|
||||
_dt = timeframe.TotalMinutes / minutesPerYear;
|
||||
@@ -94,12 +100,12 @@ public class GBM : IFeed
|
||||
public TBar Next(ref bool isNew)
|
||||
{
|
||||
// GBM always honors request - parameter unchanged
|
||||
|
||||
|
||||
if (isNew || !_hasCurrentBar)
|
||||
{
|
||||
// Generate new bar
|
||||
long currentTime = _lastTime + _defaultTimeStep;
|
||||
|
||||
|
||||
double z = NextNormal();
|
||||
double price = _lastPrice * Math.Exp(_drift + _vol * z);
|
||||
double volume = 1000 + _rnd.NextDouble() * 1000;
|
||||
@@ -199,10 +205,10 @@ public class GBM : IFeed
|
||||
// Update internal state to continue from end of batch
|
||||
_lastPrice = currentPrice;
|
||||
_lastTime = currentTime - timeStep; // Last bar time, not next bar time
|
||||
|
||||
|
||||
// Bulk add to series
|
||||
series.Add(t, o, h, l, c, v);
|
||||
|
||||
|
||||
// Reset streaming state after batch
|
||||
_hasCurrentBar = false;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user