mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-07-28 01:37:43 +00:00
511 lines
11 KiB
C#
511 lines
11 KiB
C#
// Mock types for TradingPlatform.BusinessLayer to enable testing
|
|
// These are minimal implementations for unit testing purposes only
|
|
|
|
using System.Drawing;
|
|
using TradingPlatform.BusinessLayer.Chart;
|
|
|
|
namespace TradingPlatform.BusinessLayer;
|
|
|
|
#region Enums
|
|
|
|
/// <summary>
|
|
/// Specifies the style of indicator line.
|
|
/// </summary>
|
|
public enum LineStyle
|
|
{
|
|
Solid,
|
|
Dash,
|
|
Dot,
|
|
DashDot,
|
|
Histogramm,
|
|
Points,
|
|
Columns,
|
|
StepLine,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Price data types
|
|
/// </summary>
|
|
public enum PriceType
|
|
{
|
|
Open,
|
|
High,
|
|
Low,
|
|
Close,
|
|
Median,
|
|
Typical,
|
|
Weighted,
|
|
Bid,
|
|
BidSize,
|
|
Ask,
|
|
AskSize,
|
|
Last,
|
|
Volume,
|
|
Ticks,
|
|
AggressorFlag,
|
|
TickDirection,
|
|
BidTickDirection,
|
|
AskTickDirection,
|
|
OpenInterest,
|
|
Mark,
|
|
FundingRate,
|
|
QuoteAssetVolume,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Seek origin for historical data
|
|
/// </summary>
|
|
public enum SeekOriginHistory
|
|
{
|
|
Begin,
|
|
End,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update reason for indicator
|
|
/// </summary>
|
|
public enum UpdateReason
|
|
{
|
|
Unknown,
|
|
HistoricalBar,
|
|
NewTick,
|
|
NewBar,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Indicator line marker icon type
|
|
/// </summary>
|
|
public enum IndicatorLineMarkerIconType
|
|
{
|
|
None,
|
|
Point,
|
|
Circle,
|
|
Square,
|
|
Diamond,
|
|
Triangle,
|
|
TriangleDown,
|
|
Cross,
|
|
Plus,
|
|
Star,
|
|
Flag,
|
|
ArrowUp,
|
|
ArrowDown,
|
|
ArrowLeft,
|
|
ArrowRight,
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Attributes
|
|
|
|
/// <summary>
|
|
/// Attribute for input parameters
|
|
/// </summary>
|
|
[AttributeUsage(AttributeTargets.Property)]
|
|
public class InputParameterAttribute(
|
|
string name = "",
|
|
int sortIndex = 0,
|
|
double minimum = int.MinValue,
|
|
double maximum = int.MaxValue,
|
|
double increment = 0.01,
|
|
int decimalPlaces = 2,
|
|
object[]? variants = null) : Attribute
|
|
{
|
|
public string Name { get; } = name;
|
|
public int SortIndex { get; } = sortIndex;
|
|
public double Minimum { get; } = minimum;
|
|
public double Maximum { get; } = maximum;
|
|
public double Increment { get; } = increment;
|
|
public int DecimalPlaces { get; } = decimalPlaces;
|
|
public IComparable[]? Variants { get; } = variants?.Cast<IComparable>().ToArray();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region History Item
|
|
|
|
/// <summary>
|
|
/// History item interface
|
|
/// </summary>
|
|
public interface IHistoryItem
|
|
{
|
|
DateTime TimeLeft { get; }
|
|
long TicksLeft { get; set; }
|
|
long TicksRight { get; set; }
|
|
double this[PriceType priceType] { get; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Mock history item for testing
|
|
/// </summary>
|
|
public class MockHistoryItem : IHistoryItem
|
|
{
|
|
public DateTime TimeLeft { get; set; }
|
|
public long TicksLeft { get; set; }
|
|
public long TicksRight { get; set; }
|
|
public double Open { get; set; }
|
|
public double High { get; set; }
|
|
public double Low { get; set; }
|
|
public double Close { get; set; }
|
|
public double Volume { get; set; }
|
|
|
|
public double this[PriceType priceType] => priceType switch
|
|
{
|
|
PriceType.Open => Open,
|
|
PriceType.High => High,
|
|
PriceType.Low => Low,
|
|
PriceType.Close => Close,
|
|
PriceType.Volume => Volume,
|
|
PriceType.Median => (High + Low) / 2,
|
|
PriceType.Typical => (High + Low + Close) / 3,
|
|
PriceType.Weighted => (High + Low + Close + Close) / 4,
|
|
_ => Close,
|
|
};
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Historical Data
|
|
|
|
/// <summary>
|
|
/// Mock historical data for testing
|
|
/// </summary>
|
|
public class HistoricalData
|
|
{
|
|
private readonly List<IHistoryItem> _items = [];
|
|
|
|
public int Count => _items.Count;
|
|
|
|
public IHistoryItem this[int offset, SeekOriginHistory origin = SeekOriginHistory.End]
|
|
{
|
|
get
|
|
{
|
|
int index = origin == SeekOriginHistory.End
|
|
? Count - 1 - offset
|
|
: offset;
|
|
return _items[index];
|
|
}
|
|
}
|
|
|
|
public DateTime Time(int offset = 0, SeekOriginHistory origin = SeekOriginHistory.End)
|
|
{
|
|
return this[offset, origin].TimeLeft;
|
|
}
|
|
|
|
public long GetIndexByTime(long ticks)
|
|
{
|
|
for (int i = 0; i < _items.Count; i++)
|
|
{
|
|
if (_items[i].TicksLeft == ticks)
|
|
{
|
|
return Count - 1 - i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
public void Add(IHistoryItem item)
|
|
{
|
|
_items.Add(item);
|
|
}
|
|
|
|
public void AddBar(DateTime time, double open, double high, double low, double close, double volume = 0)
|
|
{
|
|
_items.Add(new MockHistoryItem
|
|
{
|
|
TimeLeft = time,
|
|
TicksLeft = time.Ticks,
|
|
TicksRight = time.Ticks,
|
|
Open = open,
|
|
High = high,
|
|
Low = low,
|
|
Close = close,
|
|
Volume = volume,
|
|
});
|
|
}
|
|
|
|
public void Clear() => _items.Clear();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Update Args
|
|
|
|
/// <summary>
|
|
/// Update arguments for indicator
|
|
/// </summary>
|
|
public class UpdateArgs(UpdateReason reason)
|
|
{
|
|
public UpdateReason Reason { get; } = reason;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Line Series
|
|
|
|
/// <summary>
|
|
/// Base class for lines
|
|
/// </summary>
|
|
public class IndicatorLineMarker(Color color, IndicatorLineMarkerIconType icon = IndicatorLineMarkerIconType.None)
|
|
{
|
|
public Color Color { get; set; } = color;
|
|
public IndicatorLineMarkerIconType Icon { get; set; } = icon;
|
|
}
|
|
|
|
public class Line(string name, Color color, int width, LineStyle style)
|
|
{
|
|
public string Name { get; set; } = name;
|
|
public Color Color { get; set; } = color;
|
|
public int Width { get; set; } = width;
|
|
public LineStyle Style { get; set; } = style;
|
|
public bool Visible { get; set; } = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Line series for indicator output
|
|
/// </summary>
|
|
public class LineSeries(string name, Color color, int width, LineStyle style)
|
|
: Line(name, color, width, style)
|
|
{
|
|
private readonly List<double> _values = [];
|
|
private readonly List<Color> _markers = [];
|
|
|
|
public int TimeShift { get; set; }
|
|
public int DrawBegin { get; set; }
|
|
public bool ShowLineMarker { get; set; } = true;
|
|
|
|
public double this[int offset = 0, SeekOriginHistory origin = SeekOriginHistory.End]
|
|
{
|
|
get => GetValue(offset, origin);
|
|
set => SetValue(value, offset, origin);
|
|
}
|
|
|
|
public double GetValue(int offset = 0, SeekOriginHistory origin = SeekOriginHistory.End)
|
|
{
|
|
if (_values.Count == 0)
|
|
{
|
|
return double.NaN;
|
|
}
|
|
|
|
int index = origin == SeekOriginHistory.End
|
|
? _values.Count - 1 - offset
|
|
: offset;
|
|
|
|
if (index < 0 || index >= _values.Count)
|
|
{
|
|
return double.NaN;
|
|
}
|
|
|
|
return _values[index];
|
|
}
|
|
|
|
public void SetValue(double value, int offset = 0, SeekOriginHistory origin = SeekOriginHistory.End)
|
|
{
|
|
EnsureCapacity(offset + 1);
|
|
int index = origin == SeekOriginHistory.End
|
|
? _values.Count - 1 - offset
|
|
: offset;
|
|
_values[index] = value;
|
|
}
|
|
|
|
public void SetMarker(int offset, Color color)
|
|
{
|
|
EnsureMarkerCapacity(offset + 1);
|
|
int index = _markers.Count - 1 - offset;
|
|
if (index >= 0 && index < _markers.Count)
|
|
{
|
|
_markers[index] = color;
|
|
}
|
|
}
|
|
|
|
public void SetMarker(int offset, IndicatorLineMarker marker)
|
|
{
|
|
SetMarker(offset, marker.Color);
|
|
}
|
|
|
|
internal void AddValue()
|
|
{
|
|
_values.Add(double.NaN);
|
|
_markers.Add(Color.Transparent);
|
|
}
|
|
|
|
private void EnsureCapacity(int count)
|
|
{
|
|
while (_values.Count < count)
|
|
{
|
|
_values.Add(double.NaN);
|
|
}
|
|
}
|
|
|
|
private void EnsureMarkerCapacity(int count)
|
|
{
|
|
while (_markers.Count < count)
|
|
{
|
|
_markers.Add(Color.Transparent);
|
|
}
|
|
}
|
|
|
|
public int Count => _values.Count;
|
|
public IReadOnlyList<double> Values => _values;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Line level for indicator horizontal lines
|
|
/// </summary>
|
|
public class LineLevel(double value, string name, Color color, int width, LineStyle style)
|
|
: Line(name, color, width, style)
|
|
{
|
|
public double Value { get; set; } = value;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Paint Chart Event Args
|
|
|
|
/// <summary>
|
|
/// Paint chart event arguments
|
|
/// </summary>
|
|
public class PaintChartEventArgs(Graphics graphics, Rectangle clipRectangle, int windowIndex = 0) : EventArgs
|
|
{
|
|
public Graphics Graphics { get; } = graphics;
|
|
public Rectangle ClipRectangle { get; } = clipRectangle;
|
|
public int WindowIndex { get; } = windowIndex;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Chart
|
|
|
|
/// <summary>
|
|
/// Chart interface
|
|
/// </summary>
|
|
public interface IChart
|
|
{
|
|
ChartWindow MainWindow { get; }
|
|
IList<ChartWindow> Windows { get; }
|
|
int BarsWidth { get; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Chart window
|
|
/// </summary>
|
|
public class ChartWindow
|
|
{
|
|
public Rectangle ClientRectangle { get; set; }
|
|
public IChartWindowCoordinatesConverter CoordinatesConverter { get; set; } = new MockCoordinatesConverter();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Mock coordinates converter
|
|
/// </summary>
|
|
public class MockCoordinatesConverter : IChartWindowCoordinatesConverter
|
|
{
|
|
public DateTime GetTime(int x) => DateTime.UtcNow;
|
|
public double GetChartX(DateTime time) => 0;
|
|
public double GetChartY(double value) => 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Mock chart for testing
|
|
/// </summary>
|
|
public class MockChart : IChart
|
|
{
|
|
public ChartWindow MainWindow { get; } = new();
|
|
public IList<ChartWindow> Windows { get; } = [new ChartWindow()];
|
|
public int BarsWidth { get; set; } = 10;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Indicator Base
|
|
|
|
/// <summary>
|
|
/// Watchlist indicator interface
|
|
/// </summary>
|
|
public interface IWatchlistIndicator
|
|
{
|
|
int MinHistoryDepths { get; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Base class for indicators
|
|
/// </summary>
|
|
public abstract class Indicator
|
|
{
|
|
private readonly List<LineSeries> _lineSeries = [];
|
|
private readonly List<LineLevel> _lineLevels = [];
|
|
|
|
public string Name { get; set; } = string.Empty;
|
|
public string Description { get; set; } = string.Empty;
|
|
public virtual string ShortName => Name;
|
|
public virtual string SourceCodeLink => string.Empty;
|
|
|
|
public bool SeparateWindow { get; set; }
|
|
public bool OnBackGround { get; set; }
|
|
|
|
public HistoricalData HistoricalData { get; set; } = new();
|
|
public IChart? CurrentChart { get; set; }
|
|
|
|
public int Count => HistoricalData.Count;
|
|
|
|
public IReadOnlyList<LineSeries> LinesSeries => _lineSeries;
|
|
public IReadOnlyList<LineLevel> LineLevels => _lineLevels;
|
|
|
|
protected void AddLineSeries(LineSeries series)
|
|
{
|
|
_lineSeries.Add(series);
|
|
}
|
|
|
|
protected void AddLineLevel(double value, string name, Color color, int width, LineStyle style)
|
|
{
|
|
_lineLevels.Add(new LineLevel(value, name, color, width, style));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when indicator is initialized
|
|
/// </summary>
|
|
protected virtual void OnInit()
|
|
{
|
|
// Intentionally empty
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called on each update
|
|
/// </summary>
|
|
protected virtual void OnUpdate(UpdateArgs args)
|
|
{
|
|
// Intentionally empty
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called for chart painting
|
|
/// </summary>
|
|
public virtual void OnPaintChart(PaintChartEventArgs args)
|
|
{
|
|
// Intentionally empty
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialize the indicator (for testing)
|
|
/// </summary>
|
|
public void Initialize()
|
|
{
|
|
OnInit();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Process an update (for testing)
|
|
/// </summary>
|
|
public void ProcessUpdate(UpdateArgs args)
|
|
{
|
|
// Ensure line series have capacity for new data
|
|
foreach (var series in _lineSeries)
|
|
{
|
|
series.AddValue();
|
|
}
|
|
OnUpdate(args);
|
|
}
|
|
}
|
|
|
|
#endregion
|