Drawing

Reference

Drawing

The contracts for chart drawing tools: the tool interface and base class, the time/price anchor model, the per-frame render context, hit-test geometry helpers, the discovery attributes/descriptors, and the line/placement enums. All types live under TradeStrike.Pipeline.Drawing (Fibonacci helpers under TradeStrike.Pipeline.Drawing.Tools).

Drawing tools — TradeStrike.Pipeline.Drawing

IDrawingTool INTERFACE

One placed instance of a drawing tool (trend line, ray, horizontal line, …). State, render, and hit-test all live on the tool; the chart's drawing layer owns the list and coordinates the lifecycle. Most authors derive from DrawingToolBase rather than implementing this directly.

Member Description
Guid Id { get; } Process-unique identity, generated at construction.
string ToolName { get; } Display-only name (e.g. "Trend Line"); sourced from DrawingToolMetadataAttribute.
string PanelId { get; set; } Panel the tool belongs to. DrawingToolPanelIds.Main = main candle panel; any other value matches a subpanel id. Render + hit-test filter by this.
bool IsSelected { get; set; } True when selected (handles drawn, draggable, deletable).
bool IsPlaced { get; set; } True once the user has finalised the tool (all anchors placed).
int AnchorCount { get; } Currently-placed anchor count.
int MaxAnchorCount { get; } Upper bound on anchor count. Fixed tools: equals AnchorCount; variable tools: a cap or int.MaxValue.
DrawingPlacementMode PlacementMode { get; } How the placement state machine gathers anchors.
bool CanAddAnchor { get; } True when another anchor can still be appended (AnchorCount < MaxAnchorCount).
IReadOnlyList<DrawingAnchor> Anchors { get; } Currently-set anchors; uninitialised slots are zero-valued.
ChartColor Color { get; set; } Stroke colour.
double LineWidthPx { get; set; } Line width in pixels (> 0).
LineStyle LineStyle { get; set; } Stroke style.
void SetAnchor(int index, DrawingAnchor anchor) Set a single anchor by index; validates against AnchorCount.
void AddAnchor(DrawingAnchor anchor) Append an anchor (open-ended / drag-to-paint). Throws InvalidOperationException when CanAddAnchor is false.
bool RemoveLastAnchor() Remove the most-recently-appended anchor; false when nothing to remove.
void Move(TimeSpan dTime, double dPrice) Move all anchors by a (time, price) delta. Constrained tools override.
void OnRender(IDrawingToolRenderContext ctx) Render the tool; called every repaint frame.
bool HitTest(double pixelX, double pixelY, IDrawingToolRenderContext ctx, double tolerancePx = 6) True when the pixel point hits the tool within tolerance; drives hover-highlight + click-to-select.
IDrawingTool.cspublic interface IDrawingTool
{
    Guid Id { get; }
    string ToolName { get; }
    string PanelId { get; set; }
    bool IsSelected { get; set; }
    bool IsPlaced { get; set; }
    int AnchorCount { get; }
    int MaxAnchorCount { get; }
    DrawingPlacementMode PlacementMode { get; }
    bool CanAddAnchor { get; }
    IReadOnlyList<DrawingAnchor> Anchors { get; }
    ChartColor Color { get; set; }
    double LineWidthPx { get; set; }
    LineStyle LineStyle { get; set; }
    void SetAnchor(int index, DrawingAnchor anchor);
    void AddAnchor(DrawingAnchor anchor);
    bool RemoveLastAnchor();
    void Move(TimeSpan dTime, double dPrice);
    void OnRender(IDrawingToolRenderContext ctx);
    bool HitTest(double pixelX, double pixelY, IDrawingToolRenderContext ctx, double tolerancePx = 6);
}

DrawingToolBase

Default IDrawingTool base. Anchor storage, identity, selection, default styling (Color / LineWidth / LineStyle), and reading the metadata attribute for ToolName all live in the base — a subclass only implements OnRender + HitTest. Two construction modes: fixed-anchor (base(anchorCount: N)) pre-allocates N slots and runs DrawingPlacementMode.FixedAnchors; variable-anchor (base(mode, maxAnchorCount, initialAnchorCount)) starts at the initial count and grows via AddAnchor.

Member Description
protected DrawingToolBase(int anchorCount) Fixed-anchor ctor: pre-allocates anchorCount default anchors; mode = FixedAnchors. Throws if anchorCount < 1.
protected DrawingToolBase(DrawingPlacementMode mode, int maxAnchorCount = int.MaxValue, int initialAnchorCount = 0) Variable-anchor ctor. Throws ArgumentException if mode == FixedAnchors (use the int ctor).
int MinAnchorsForFinalize { get; protected set; } Minimum anchors before a finalize gesture is accepted (open-ended / drag-to-paint). Defaults to 2.
ChartColor Color { get; set; } Stroke colour; annotated [DrawingToolProperty]. Defaults to DodgerBlue.
LineStyle LineStyle { get; set; } Stroke pattern; annotated [DrawingToolProperty]. Defaults to Solid.
double LineWidthPx { get; set; } Stroke thickness in px; annotated [DrawingToolProperty]. Setter throws ArgumentOutOfRangeException when <= 0.
public abstract void OnRender(IDrawingToolRenderContext ctx) Implement to paint the tool.
public abstract bool HitTest(double pixelX, double pixelY, IDrawingToolRenderContext ctx, double tolerancePx = 6) Implement the hit-test.
public virtual void Move(TimeSpan dTime, double dPrice) Shifts every anchor by the delta; override for constrained tools.

The interface members (Id, ToolName, PanelId, IsSelected, IsPlaced, AnchorCount, MaxAnchorCount, PlacementMode, CanAddAnchor, Anchors, SetAnchor, AddAnchor, RemoveLastAnchor) are all implemented by the base.

Rectangle.csusing TradeStrike.Pipeline.Drawing;

[DrawingToolMetadata("Rectangle", Category = "Shapes", AnchorCount = 2)]
public sealed class Rectangle : DrawingToolBase
{
    public Rectangle() : base(anchorCount: 2) { }

    public override void OnRender(IDrawingToolRenderContext ctx)
    {
        var p1 = ctx.AnchorToPixel(Anchors[0]);
        var p2 = ctx.AnchorToPixel(Anchors[1]);
        // ... draw the outline via ctx.Renderer ...
        if (ctx.IsSelected) { ctx.DrawHandle(p1.X, p1.Y); ctx.DrawHandle(p2.X, p2.Y); }
    }

    public override bool HitTest(double px, double py, IDrawingToolRenderContext ctx, double tol = 6)
    {
        var p1 = ctx.AnchorToPixel(Anchors[0]);
        var p2 = ctx.AnchorToPixel(Anchors[1]);
        return DrawingGeometry.DistanceToRectangleOutline(px, py, p1.X, p1.Y, p2.X, p2.Y) <= tol;
    }
}

DrawingAnchor

A single anchor point: a (time, price) pair in the bar series' canonical timeline. Anchors are bar-index-independent, so a tool survives live bar updates, scroll, replays, and bar-type changes. Time-only tools ignore Price; price-only tools ignore Time — the unused coordinate is stored as a sentinel and never read.

DrawingAnchor.cspublic readonly record struct DrawingAnchor(DateTime Time, double Price)
{
    public static DrawingAnchor PriceOnly(double price);   // Time = DateTime.MinValue
    public static DrawingAnchor TimeOnly(DateTime time);   // Price = double.NaN
}

DrawingGeometry

Static 2-D geometry helpers for hit tests. All inputs are pixel coordinates, so call them after converting anchors via IDrawingToolRenderContext.AnchorToPixel. Outline helpers return a non-negative distance — compare against the tool's pixel tolerance.

Signature Description
double DistanceToSegment(px, py, x1, y1, x2, y2) Perpendicular distance to a finite line segment.
double DistanceToRay(px, py, x1, y1, x2, y2) Distance to a ray from (x1,y1) through (x2,y2) extending to infinity.
double DistanceToHorizontalLine(py, lineY) Distance to an infinite horizontal line at y = lineY.
double DistanceToVerticalLine(px, lineX) Distance to an infinite vertical line at x = lineX.
double DistanceToHorizontalRay(px, py, lineY, startX, endX) Distance to a horizontal ray from startX (to endX or +∞).
double DistanceToRectangleOutline(px, py, x1, y1, x2, y2) Distance to the closest edge of an axis-aligned rectangle (outline only).
double DistanceToClosedPolyline(px, py, ReadOnlySpan<(double X, double Y)> points) Distance to a closed polyline (last point auto-connects to first).
double DistanceToOpenPolyline(px, py, ReadOnlySpan<(double X, double Y)> points) Distance to an open polyline.
double DistanceToCircleOutline(px, py, cx, cy, radius) Absolute distance to a circle outline.
double DistanceToEllipseOutline(px, py, x1, y1, x2, y2) Distance to an axis-aligned ellipse outline (64-sample approximation).
(double X, double Y) QuadraticBezier((double X, double Y) p0, (double X, double Y) p1, (double X, double Y) p2, double t) Evaluate a quadratic Bézier at t ∈ [0, 1].

IDrawingToolRenderContext INTERFACE

Per-frame context handed to OnRender and HitTest. Encapsulates the renderer, viewport, plot-area rectangle, selection state, and time→pixel conversion. All the plumbing lives here so tool authors write almost-trivial render/hit-test methods. The instrument-metadata members default to null/identity so headless and test shims need not supply them.

Member Description
IChartRenderer Renderer { get; } Active renderer for the current frame.
ChartViewport Viewport { get; } Bar↔pixel + price↔pixel transforms for the current paint.
PlotAreaRect PlotArea { get; } Pixel rectangle of the main candle pane (excludes axis strips).
bool IsSelected { get; } True when the tool being rendered/hit-tested is selected.
double TimeToBarIndex(DateTime time) Convert an anchor time to a (possibly fractional) bar index.
(double X, double Y) AnchorToPixel(DrawingAnchor anchor) Convenience: anchor → pixel (X, Y), combining TimeToBarIndex + viewport transforms.
void DrawHandle(double x, double y, ChartColor? fill = null, double sizePx = 7) Draw a small selection handle at a pixel point.
IBarSeries? Bars { get; } Bar series driving the chart, when the host has one. Default null.
double? TickSize { get; } Instrument minimum price increment, when known. Default null.
double? PointValue { get; } Currency value of a 1.0 price move per contract, when known. Default null.
QuantityUnit QuantityUnit { get; } Unit the instrument's quantity is counted in. Default QuantityUnit.Contract.
double? QuantityStep { get; } Smallest tradable quantity increment, when known. Default null.
IOrderFlowWindowSource? OrderFlowWindows { get; } Host seam building volume-at-price profiles for arbitrary windows. Default null.

Discovery — attributes & descriptors

DrawingToolMetadataAttribute

Marks a class as a discoverable drawing tool — read at assembly-scan time to populate the toolbar menu, hotkeys, icons, and anchor-count requirements. One per tool class; no registration boilerplate. [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)].

Member Description
string Name { get; } Display name (ctor argument; required, non-blank).
string Category { get; init; } Toolbar submenu group. Default "Lines".
string? Hotkey { get; init; } Optional global hotkey (e.g. "Alt+T").
string? IconKey { get; init; } Key into the icon-resource dictionary.
int AnchorCount { get; init; } Anchor points the user clicks before finalising. Default 2.
int Order { get; init; } Display order within the category (lower first). Default 0.
string? Description { get; init; } Tooltip text.

DrawingToolPropertyAttribute

Marks a tool property as user-editable in the generic properties dialog. Opt-in: infrastructure properties stay hidden. DrawingToolBase already annotates Color / LineWidth / LineStyle, so every subclass surfaces those three by default; decorate your own for extra editors. Constraints are advisory (the dialog enforces them; programmatic writes are not clamped). [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)].

Member Description
string? DisplayName { get; set; } Editor label (defaults to property name).
string? Description { get; set; } One-line tooltip.
string Category { get; set; } Group bucket. Default "General".
int Order { get; set; } Order within the category (lower first).
object? MinValue { get; set; } Advisory numeric lower bound. Null = none.
object? MaxValue { get; set; } Advisory numeric upper bound. Null = none.
object? Step { get; set; } Spinner increment. Null = editor decides.

DrawingToolPropertyDescriptor

Reflection metadata for one editable property, consumed by the properties dialog to render editors and write input back onto the live tool. Use Read / Write rather than reflecting yourself so the dialog and headless paths share the same value-coercion contract. (The constructor is internal — instances are produced by the host's discovery.)

Member Description
string Name { get; } CLR property name on the tool type.
string DisplayName { get; } Human-readable label.
string Description { get; } Tooltip; empty when none.
string Category { get; } Group bucket.
int Order { get; } Sort order within the category.
Type PropertyType { get; } Underlying property type (e.g. double, LineStyle, ChartColor).
object? MinValue { get; } Advisory min. Null = none.
object? MaxValue { get; } Advisory max. Null = none.
object? Step { get; } Spinner increment. Null = editor decides.
object? Read(IDrawingTool tool) Read the current value off a live tool.
void Write(IDrawingTool tool, object? value) Write a value back; the value must be assignable to PropertyType.

DrawingToolDescriptor

Frozen description of a discovered tool type: its metadata plus a factory delegate that builds a fresh instance. A sealed record returned by the host's drawing-tool catalog.

DrawingToolDescriptor.cspublic sealed record DrawingToolDescriptor(
    Type ToolType,
    string Name,
    string Category,
    string? Hotkey,
    string? IconKey,
    int AnchorCount,
    int Order,
    string? Description,
    Func<IDrawingTool> Factory);

DrawingToolPanelIds

Well-known panel-id constants for IDrawingTool.PanelId.

Member Value / Meaning
const string Main "main" — the chart's main candle/price panel.

Enums

LineStyle

Stroke style for drawing-tool segments. Mirrors PlotStyle's line variants.

Value Meaning
Solid Continuous stroke.
Dashed Dashed stroke.
Dotted Dotted stroke.

DrawingPlacementMode

How a tool gathers its anchors during placement. The chart's interaction state machine dispatches on this value; each mode has a distinct user gesture.

Value Meaning
FixedAnchors Click once per anchor; placement completes when AnchorCount clicks land. Default for line / rectangle / triangle / arrow.
OpenEnded Click to append anchors, double-click or Escape to finalize (must meet MinAnchorsForFinalize). Used by Path, Polyline.
DragToPaint Mouse-down + drag samples points; mouse-up finalizes. Used by Brush, Highlighter.

Fibonacci levels — TradeStrike.Pipeline.Drawing.Tools

FibLevels

Standard Fibonacci ratios used by the Fib retracement / extension / channel / wedge / fan family. Centralised so changing a default updates every tool at once. Static fields are the raw ratio ladders; the Default*Set() factories build a ready-to-edit FibLevelSet.

Member Description
static readonly double[] StandardRetracements { 0.0, 0.236, 0.382, 0.5, 0.618, 0.786, 1.0 } — classic retracement ladder.
static readonly double[] StandardExtensions { 1.272, 1.414, 1.618, 2.0, 2.618, 3.618, 4.236 } — extension ratios (> 1.0).
static readonly double[] StandardRetracementsAndExtensions { 0.0, 0.236, 0.382, 0.5, 0.618, 0.786, 1.0, 1.272, 1.618, 2.0, 2.618 }.
static readonly double[] StandardSpeedRatios { 0.382, 0.5, 0.618 } — speed-resistance fan / arc ratios.
static readonly int[] StandardTimeZones { 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 } — time-zone multiples.
static readonly (int Price, int Time)[] GannAngles Gann fan slopes from 1×8 to 8×1 (1×1 = central reference).
static FibLevelSet DefaultRetracementSet() Visible retracement ladder paired with default colours.
static FibLevelSet DefaultExtensionSet() Visible retracements + extensions with the extension palette.
static FibLevelSet DefaultRetracementSetInheritColor() Retracement ladder, every level inherits the tool's stroke colour.
static FibLevelSet DefaultSpeedRatioSetInheritColor() Three speed ratios, inherit stroke colour.
static FibLevelSet DefaultCircleRatioSetInheritColor() Fib-circle ring set, inherit stroke colour.

FibLevel — one configurable level (a sealed class):

Member Description
double Ratio { get; set; } Ratio applied to the tool's anchor range.
ChartColor? Color { get; set; } Per-level stroke override; null = inherit the tool's stroke colour.
bool IsVisible { get; set; } Render flag. False hides the level without removing it. Default true.
FibLevel(double ratio, ChartColor? color = null, bool visible = true) Convenience constructor (also has a parameterless ctor).
FibLevel Clone() Deep copy.

FibLevelSet — ordered, mutable collection of levels; inherits ObservableCollection<FibLevel> so the WPF list editor binds directly:

Member Description
FibLevelSet() / FibLevelSet(IEnumerable<FibLevel> items) Empty or seeded from an item sequence.
FibLevelSet Clone() Deep copy — every level is cloned, not shared.
void ReplaceWith(IEnumerable<FibLevel> other) Replace contents in-place (Clear+Add) so change notifications fire.