跳转至

图表对象和绘图

图表对象(绘图)允许在 cTrader 图表上绘制线条或几何形状。 通过使用它们,您可以根据您的 cBot 或指标数据在图表上绘制形态或显示某些事件。

图表对象使用 X 和 Y 坐标进行定位。

  • X 轴表示图表时间或柱状图索引。
  • Y 轴是交易品种的价格。

所有图表对象都派生自 ChartObject 基类,这意味着它们都继承了某些基本特性。

图表对象代码示例

在指标窗口内绘图

创建图表对象时,您不仅限于主交易品种图表。 实际上,您可以在任何包含指标输出的窗口中绘制图表对象。

Chart 类具有 IndicatorAreas 集合。 它包含所有非叠加或独立的窗口,指标可以在这些窗口中显示其输出。

您还可以通过使用 Indicator 类的 IndicatorArea 属性访问当前的指标区域。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
using cAlgo.API;

namespace NewIndicator
{
    [Indicator(IsOverlay = false, AccessRights = AccessRights.None)]
    public class NewIndicator : Indicator
    {
        protected override void Initialize()
        {
            var verticalLine = IndicatorArea.DrawVerticalLine("line", Chart.LastVisibleBarIndex, Color.Red);
        }

        public override void Calculate(int index)
        {
        }
    }
}

当您运行上述指标的实例时,您应该在一个独立的指标窗口中看到一条红线。 在独立的指标区域中创建图表时,您可以使用所有绘图方法,因为 ChartIndicatorArea 都继承自 ChartArea 基类。

静态文本

静态文本不使用 X 和 Y 坐标进行定位,而是使用静态水平和垂直对齐。 要显示或绘制静态文本,请使用 DrawStaticText() 方法。

1
var staticText = Chart.DrawStaticText("static", "This is the text that will be shown", VerticalAlignment.Center, HorizontalAlignment.Center, Color.Red);

如果您将此图表对象附加到指标并运行实例,您将在主图表中看到以下文本。

Image title

垂直线

使用 DrawVerticalLine() 方法绘制垂直线。

1
2
3
var verticalLine = Chart.DrawVerticalLine("line", Chart.LastVisibleBarIndex, Color.Red);
// Or
var verticalLine = Chart.DrawVerticalLine("line", Bars.OpenTimes[Chart.LastVisibleBarIndex], Color.Red);

水平线

使用 DrawHorizontalLine() 方法绘制水平线。

1
2
3
/* We use the maximum of the high prices
of the last ten bars as the Y coordinate. */ 
var horizontalLine = Chart.DrawHorizontalLine("line", Bars.HighPrices.Maximum(10), Color.Red);

趋势线

趋势线从图表中的某个点开始,到另一个点结束。 趋势线可用于创建不同的形状或复杂的形态。

要绘制趋势线,请使用 DrawTrendLine() 方法。

1
2
3
4
5
6
7
/* We draw a line from the low price
 of the first visible bar to the high price
 of the last visible bar on the chart. */
var trendLine = Chart.DrawTrendLine("line", Chart.FirstVisibleBarIndex, Bars.LowPrices[Chart.FirstVisibleBarIndex], Chart.LastVisibleBarIndex, Bars.HighPrices[Chart.LastVisibleBarIndex], Color.Red);

// Alternatively, consider the following.
var trendLine = Chart.DrawTrendLine("line", Bars.OpenTimes[Chart.FirstVisibleBarIndex], Bars.LowPrices[Chart.FirstVisibleBarIndex], Bars.OpenTimes[Chart.LastVisibleBarIndex], Bars.HighPrices[Chart.LastVisibleBarIndex], Color.Red);

矩形

使用 DrawRectangle() 方法绘制矩形。

1
2
3
4
5
6
7
var rectangle = Chart.DrawRectangle("rectangle", Chart.FirstVisibleBarIndex + 1, Bars.LowPrices[Chart.FirstVisibleBarIndex + 1], Chart.LastVisibleBarIndex, Bars.HighPrices[Chart.LastVisibleBarIndex], Color.Red);

/* We fill the rectangle with a transparent color.
By using its current color, we only change the alpha
channel. */
rectangle.IsFilled = true;
rectangle.Color = Color.FromArgb(80, rectangle.Color);

三角形

使用 DrawTriangle() 方法绘制三角形。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
var middleX = Chart.FirstVisibleBarIndex + (Chart.LastVisibleBarIndex - Chart.FirstVisibleBarIndex) / 2;
var middleY = Bars.LowPrices[Chart.FirstVisibleBarIndex] + (Bars.HighPrices[Chart.LastVisibleBarIndex] - Bars.LowPrices[Chart.FirstVisibleBarIndex]) / 2;

var triangle = Chart.DrawTriangle("triangle", Chart.FirstVisibleBarIndex, Bars.LowPrices[Chart.FirstVisibleBarIndex], middleX, middleY, Chart.LastVisibleBarIndex, Bars.LowPrices[Chart.FirstVisibleBarIndex], Color.Red);

// We fill the triangle with a transparent color
// by using it's current color, we change only the alpha channel

/* We fill the triangle with a transparent color.
By using its current color, we only change the alpha
channel. */
triangle.IsFilled = true;
triangle.Color = Color.FromArgb(80, triangle.Color);

其他图表对象

为了简洁起见,我们没有在上述代码片段中提及其他几种图表对象。 这些对象包括但不限于以下内容:

  • 椭圆
  • 图标
  • 安德鲁斯叉
  • 等距通道
  • 斐波纳契扩展线
  • 斐波纳契扇形线
  • 斐波纳契回调线

所有这些对象都可以使用类似命名的 Draw...() 方法绘制,这些方法接受类似的参数。

风险回报

ChartRiskReward 接口提供了类型,使您能够以编程方式在图表上创建和管理风险回报对象。

以下代码展示了如何创建一个绘制风险回报对象的指标:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
using System;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;

namespace cAlgo
{
    [Indicator(AccessRights = AccessRights.None, IsOverlay = true)]
    public class SimpleRiskReward : Indicator
    {
        protected override void Initialize()
        {
            var type = new ChartRiskRewardFixedRiskType(
                ChartRiskRewardAmountType.BalancePercentage, 
                1                                            
            );
            var tradeType = TradeType.Buy;
            var orderType = OrderType.Market;

            var entryPrice = Chart.Bars.LastBar.Close;
            var time1 = Chart.LastVisibleBarIndex - 20; 
            var time2 = Chart.LastVisibleBarIndex;      

            var rr = Chart.DrawRiskReward(
                "SimpleRR",     
                time1,           
                time2,            
                entryPrice,       
                orderType,        
                tradeType,        
                type              
            );

            rr.IsInteractive = true;

            rr.RiskColor = Color.Yellow;
            rr.RiskLineStyle = LineStyle.DotsRare;
            rr.RiskThickness = 3;

            rr.RewardColor = Color.Blue;
            rr.RewardLineStyle = LineStyle.LinesDots;
            rr.RewardThickness = 2;

            PrintRRInfo(rr);
        }

        private void PrintRRInfo(ChartRiskReward rr)
        {
            Print($"Name: {rr.Name} | Type: {rr.Type.GetType().Name} | Order Type: {rr.OrderType} | Trade Type: {rr.TradeType} | Time 1: {rr.Time1:o} | Time 2: {rr.Time2:o} | Entry: {rr.EntryPrice} | SL: {rr.StopLossPrice} | TP: {rr.TakeProfitPrice} | RR: {rr.RiskRewardRatio} | Volume: {rr.VolumeInUnits}");
        }

        public override void Calculate(int index)
        {
        }
    }
}

关键对象设置

命名图表对象

每当您尝试绘制图表对象时,您必须提供其名称作为绘图方法的参数。

所有图表对象的名称必须是唯一的。 如果未满足此要求,您可能会面临图表对象根据其执行时间覆盖同名图表对象的风险。 您可以使用当前柱状图索引值或当前时间为每个对象生成唯一的名称。

访问图表对象

所有图表对象都包含在 Objects 集合中。 此集合包括用户绘制的对象。 因此,可以访问 Objects 集合以从图表中删除对象或修改其属性。

以下示例访问 Objects 集合的成员:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
using cAlgo.API;
using System.Linq;
namespace NewIndicator
{
    [Indicator(IsOverlay = true, AccessRights = AccessRights.None)]
    public class NewIndicator : Indicator
    {
        protected override void Initialize()
        {
            foreach (var chartObject in Chart.Objects)
            {
                /* If the object is a trend line 
                we change its Y1/2 properties */
                if (chartObject is ChartTrendLine trendLine)
                {
                    trendLine.Y1 = Chart.BottomY;
                    trendLine.Y2 = Chart.TopY;
                }
            }

            /* Here, we filter all objects of the 'ChartRectangle'
            type. */
            var rectangles = from chartObject in Chart.Objects
                             where chartObject is ChartRectangle
                             select chartObject as ChartRectangle;

            /* We select only the chart objects with a name that
            begins with "MyObjects". */
            var myObjects = from chartObject in Chart.Objects
                            where chartObject.Name.StartsWith("MyObjects", System.StringComparison.OrdinalIgnoreCase)
                            select chartObject;

            /* We select only interactive objects. If an object
            is interactive, it will not be removed when the 
            indicator is removed or reloaded. */
            var interactiveObjects = from chartObject in Chart.Objects
                                     where chartObject.IsInteractive
                                     select chartObject;

            /* We remove all objects with a name 
            that begins with "ToRemove". */
            var chartObjectsCopy = Chart.Objects.ToArray();

            foreach (var chartObject in chartObjectsCopy)
            {
                if (chartObject.Name.StartsWith("ToRemove", System.StringComparison.OrdinalIgnoreCase))
                {
                    /* Chart 'RemoveObject' gets the object name
                    as a parameter. */
                    Chart.RemoveObject(chartObject.Name);
                }

            }
        }

        public override void Calculate(int index)
        {
        }
    }
}

事件

图表对象有几个事件,您可以使用这些事件来了解哪些对象被绘制、更新或删除:

  • ObjectsAdded - 当一个或多个图表对象被添加到图表时触发。
  • ObjectsRemoved - 当一个或多个图表对象从图表中删除时触发。
  • ObjectsUpdated - 当一个或多个图表对象被更新时触发(用户或活动指标或 cBot 更改了它们的某个属性)。
  • ObjectsSelectionChanged - 当用户选择一个或多个图表对象时触发。
  • ObjectHoverChanged - 当鼠标光标悬停在图表对象上时触发。

以下示例使用了所有五种事件类型:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using cAlgo.API;

namespace NewIndicator
{
    [Indicator(IsOverlay = true, AccessRights = AccessRights.None)]
    public class NewIndicator : Indicator
    {
        protected override void Initialize()
        {
            Chart.ObjectsAdded += Chart_ObjectsAdded;
            Chart.ObjectsRemoved += Chart_ObjectsRemoved;
            Chart.ObjectsUpdated += Chart_ObjectsUpdated;
            Chart.ObjectsSelectionChanged += Chart_ObjectsSelectionChanged;
            Chart.ObjectHoverChanged += Chart_ObjectHoverChanged;
        }

        private void Chart_ObjectHoverChanged(ChartObjectHoverChangedEventArgs obj) => throw new System.NotImplementedException();

        private void Chart_ObjectsSelectionChanged(ChartObjectsSelectionChangedEventArgs obj) => throw new System.NotImplementedException();

        private void Chart_ObjectsUpdated(ChartObjectsUpdatedEventArgs obj) => throw new System.NotImplementedException();

        private void Chart_ObjectsRemoved(ChartObjectsRemovedEventArgs obj) => throw new System.NotImplementedException();

        private void Chart_ObjectsAdded(ChartObjectsAddedEventArgs obj) => throw new System.NotImplementedException();

        public override void Calculate(int index)
        {
        }
    }
}

交互式和非交互式对象

使用交互式对象时,用户可以更改其属性,例如对象位置、颜色、注释等。 所有用户绘制的对象默认都是交互式的。

注意

ChartStaticText 属性无法更改,无论其相关对象是否为交互式。

要使对象具有交互性,请将其 IsInteractive 属性设置为 true,如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
using cAlgo.API;

namespace NewIndicator
{
    [Indicator(IsOverlay = true, AccessRights = AccessRights.None)]
    public class NewIndicator : Indicator
    {
        protected override void Initialize()
        {
            var verticalLine = Chart.DrawVerticalLine("line", Chart.LastVisibleBarIndex, Color.Red);

            verticalLine.IsInteractive = true;
        }

        public override void Calculate(int index)
        {
        }
    }
}

所有图表对象都具有 IsInteractive 属性。 其默认值为 false

如果图表对象是交互式的,则当指标或 cBot 实例被删除或停止时,它不会被删除。 相反,非交互式对象在指标或 cBot 停止工作时会自动删除。

为了避免混乱,请确保在相关实例被删除后使用指标的 Destroy 或 cBot 的 OnStop 方法清理交互式对象。

如果图表对象不是交互式的,它也将不可见于图表对象的列表和集合中。

锁定和解锁对象

锁定图表对象可防止用户修改或更新它。 您可以通过交易者(图表属性)框或通过将 IsLocked 属性设置为 true 来实现。 默认值为 false

您可以使用锁定使对象具有交互性,但同时防止用户更新它。 以下示例锁定了一个垂直线对象:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
using cAlgo.API;

namespace NewIndicator
{
    [Indicator(IsOverlay = true, AccessRights = AccessRights.None)]
    public class NewIndicator : Indicator
    {
        protected override void Initialize()
        {
            var verticalLine = Chart.DrawVerticalLine("line", Chart.LastVisibleBarIndex, Color.Red);

            verticalLine.IsLocked = true;
        }

        public override void Calculate(int index)
        {
        }
    }
}

显示和隐藏

您可以在图表上绘制一个对象,然后隐藏它。 虽然该对象仍存在于图表上,但它将完全不可见。

为此,请将 IsHidden 属性设置为 true

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
using cAlgo.API;

namespace NewIndicator
{
    [Indicator(IsOverlay = true, AccessRights = AccessRights.None)]
    public class NewIndicator : Indicator
    {
        protected override void Initialize()
        {
            var verticalLine = Chart.DrawVerticalLine("line", Chart.LastVisibleBarIndex, Color.Red);

            verticalLine.IsHidden = true;
        }

        public override void Calculate(int index)
        {
        }
    }
}

资源使用是使用此功能的主要考虑因素。 如果有一个对象需要频繁出现在图表上(但不是一直出现),最好隐藏它,而不是每次需要时都删除并重新绘制它。

对象选择和外观索引(ZIndex)

自然,在某些情况下,图表上可能会有多个对象相互交叉,使得选择特定对象变得困难甚至不可能。 当鼠标悬停在一组交叉的图表对象上时,只有最后绘制的对象是可选的。

要改变这种行为,您可以使用 ZIndex 属性。 这在尝试显示复杂的图表对象模式时可能会有所帮助。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using cAlgo.API;

namespace NewIndicator
{
    [Indicator(IsOverlay = true, AccessRights = AccessRights.None)]
    public class NewIndicator : Indicator
    {
        protected override void Initialize()
        {
            var firstVerticalLine = Chart.DrawVerticalLine("line1", Chart.LastVisibleBarIndex, Color.Red);

            firstVerticalLine.IsInteractive = true;

            var secondVerticalLine = Chart.DrawVerticalLine("line2", Chart.LastVisibleBarIndex, Color.Yellow);

            secondVerticalLine.IsInteractive = true;

            firstVerticalLine.ZIndex = secondVerticalLine.ZIndex + 1;
        }

        public override void Calculate(int index)
        {
        }
    }
}

一旦附加到图表上,上述指标将显示一条红色垂直线而不是黄色垂直线。 由于第一条线的 ZIndex 属性值高于第二条线的相应值,因此它优先于第二条(黄色)线。

换句话说,ZIndex 属性决定了当图表对象重叠时,哪些对象会首先显示。 所有绘制的图表对象都会自动分配一个 ZIndex 属性值。

了解对象是否被移除

所有图表对象都从 ChartObject 基类继承了 IsAlive 属性。 只要对象存在于图表上(无论是否可见),此属性的值就为 true。 一旦对象从图表中移除,该值就会设置为 false

如果您保存了对图表对象的引用,并且该对象被用户移除,您可以检查 IsAlive 属性来确认对象是否仍然存在。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
using cAlgo.API;

namespace NewIndicator
{
    [Indicator(IsOverlay = true, AccessRights = AccessRights.None)]
    public class NewIndicator : Indicator
    {
        private ChartVerticalLine _verticalLine;

        protected override void Initialize()
        {
            _verticalLine = Chart.DrawVerticalLine("line1", Chart.LastVisibleBarIndex, Color.Red);
            _verticalLine.IsInteractive = true;

            Chart.ObjectsRemoved += Chart_ObjectsRemoved;

            Print(_verticalLine.IsAlive);
        }

        private void Chart_ObjectsRemoved(ChartObjectsRemovedEventArgs obj)
        {
            Print(_verticalLine.IsAlive);

            /* If the object is removed, we should rid of its
            reference. Otherwise, it will remain in memory. */
            if (_verticalLine.IsAlive is false)
            {
                _verticalLine = null;

                Print("Object reference removed");
            }
        }

        public override void Calculate(int index)
        {
        }
    }
}

如果您运行此指标的实例,右键单击红线并将其移除。 日志应输出 "Object reference removed" 消息。

如果您保留对(已移除的)图表对象的引用,将会发生内存泄漏。 在垃圾回收期间,您的对象仍将被视为存活,这意味着它将存活于回收过程。

为避免此问题,请结合使用 ObjectsRemoved 事件和 IsAlive 属性。

柱索引或时间

在编写图表对象代码时,您可以使用图表时间或柱索引作为 X 轴。

我们认为,图表时间是更好的选择。 使用柱索引时,您无法规划未来值(因为所需的索引尚不存在),也无法考虑柱之间的任何间隙。 然而,柱索引可能更容易使用,特别是在相对简单的图表对象的情况下。

所有 Chart.Draw 方法都有针对柱索引和图表时间的不同重载。