Drawing
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).
On this page
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. |