Przejdź do treści

Obiekty wykresu i rysunki

Obiekty wykresu (rysunki) umożliwiają rysowanie linii lub kształtów geometrycznych na wykresach cTrader. Używając ich, możesz rysować wzory lub pokazywać określone zdarzenia na wykresach na podstawie danych Twojego cBota lub wskaźnika.

Obiekty wykresu używają współrzędnych X i Y do pozycjonowania.

  • Oś X reprezentuje czas wykresu lub indeksy słupków.
  • Oś Y to cena symbolu.

Wszystkie obiekty wykresu pochodzą z klasy bazowej ChartObject, co oznacza, że wszystkie dziedziczą pewne podstawowe funkcje.

Przykłady kodu obiektów wykresu

Rysowanie wewnątrz okien wskaźników

Podczas tworzenia obiektów wykresu nie jesteś ograniczony do głównego wykresu symbolu. W rzeczywistości możesz rysować obiekty wykresu wewnątrz dowolnych okien zawierających wyniki wskaźników.

Klasa Chart ma kolekcję IndicatorAreas. Zawiera ona wszystkie okna nienakładane lub oddzielne, w których wskaźniki mogą wyświetlać swoje wyniki.

Możesz również uzyskać dostęp do swojego bieżącego obszaru wskaźnika, używając właściwości IndicatorArea swojej klasy Indicator.

 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)
        {
        }
    }
}

Po uruchomieniu instancji powyższego wskaźnika powinieneś zobaczyć czerwoną linię w oddzielnym oknie wskaźnika. Podczas tworzenia wykresów w oddzielnych obszarach wskaźników możesz używać wszystkich metod rysowania, ponieważ zarówno Chart, jak i IndicatorArea dziedziczą z klasy bazowej ChartArea.

Tekst statyczny

Zamiast używać współrzędnych X i Y do pozycjonowania, tekst statyczny używa statycznych wyrównań poziomych i pionowych. Aby pokazać lub narysować tekst statyczny, użyj metody DrawStaticText().

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

Jeśli dołączysz ten obiekt wykresu do wskaźnika i uruchomisz instancję, zobaczysz następujący tekst na głównym wykresie.

Image title

Linia pionowa

Użyj metody DrawVerticalLine(), aby narysować linię pionową.

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

Linia pozioma

Użyj metody DrawHorizontalLine(), aby narysować linię poziomą.

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);

Linia trendu

Linia trendu zaczyna się w określonym punkcie wykresu i kończy w innym punkcie. Linie trendu są przydatne do tworzenia różnych kształtów lub złożonych formacji.

Aby narysować linię trendu, użyj metody 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);

Prostokąt

Użyj metody DrawRectangle(), aby narysować prostokąt.

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);

Trójkąt

Użyj metody DrawTriangle(), aby narysować trójkąt.

 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);

Inne obiekty wykresu

Dla zwięzłości nie wymieniliśmy kilku innych obiektów wykresu w powyższych fragmentach kodu. Obiekty te obejmują między innymi:

  • Elipsa
  • Ikonę
  • Widły Andrewsa
  • Kanał ekwidystantny
  • Rozszerzenie Fibonacciego
  • Wachlarz Fibonacciego
  • Zniesienia Fibonacciego

Wszystkie te obiekty można narysować za pomocą podobnie nazwanych metod Draw...() przyjmujących podobne parametry.

Ryzyko/zysk

Interfejs ChartRiskReward udostępnia typy, które umożliwiają programowe tworzenie obiektów ryzyko/zysk na wykresach i zarządzanie nimi.

Poniższy kod pokazuje, jak utworzyć wskaźnik rysujący obiekt ryzyko/zysk:

 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)
        {
        }
    }
}

Kluczowe ustawienia obiektów

Nazywanie obiektów wykresu

Za każdym razem, gdy próbujesz narysować obiekt wykresu, musisz podać jego nazwę jako argument metody rysowania.

Wszystkie nazwy obiektów wykresu muszą być unikalne. Jeśli ten wymóg nie zostanie spełniony, istnieje ryzyko, że obiekty wykresu będą nadpisywać obiekty wykresu o tych samych nazwach w zależności od czasu ich wykonania. Możesz użyć wartości indeksu bieżącego słupka lub bieżącego czasu, aby wygenerować unikalne nazwy dla każdego obiektu.

Dostęp do obiektów wykresu

Wszystkie obiekty wykresu są zawarte w kolekcji Objects. Ta kolekcja obejmuje obiekty narysowane przez użytkownika. W rezultacie można uzyskać dostęp do kolekcji Objects, aby usunąć obiekty z wykresów lub zmodyfikować ich właściwości.

Poniższy przykład uzyskuje dostęp do elementów kolekcji 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)
        {
        }
    }
}

Zdarzenia

Obiekty wykresu mają kilka zdarzeń, których można użyć, aby wiedzieć, jakie obiekty są rysowane, aktualizowane lub usuwane:

  • ObjectsAdded - jest wyzwalane, gdy jeden lub więcej obiektów wykresu jest dodawanych do wykresu.
  • ObjectsRemoved - jest wyzwalane, gdy jeden lub więcej obiektów wykresu jest usuwanych z wykresu.
  • ObjectsUpdated - wyzwalane, gdy jeden lub więcej obiektów wykresu jest aktualizowanych (jedna z ich właściwości jest zmieniana przez użytkownika lub aktywny wskaźnik lub cBot).
  • ObjectsSelectionChanged - wyzwalane, gdy jeden lub więcej obiektów wykresu jest zaznaczanych przez użytkownika.
  • ObjectHoverChanged - wyzwalane, gdy kursor myszy najeżdża na obiekt wykresu.

Poniższy przykład wykorzystuje wszystkie pięć typów zdarzeń:

 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)
        {
        }
    }
}

Obiekty interaktywne i nieinteraktywne

Podczas pracy z obiektami interaktywnymi użytkownicy mogą zmieniać ich właściwości, takie jak pozycja obiektu, kolor, komentarz itp. Wszystkie obiekty narysowane przez użytkownika są domyślnie interaktywne.

Uwaga

Właściwość ChartStaticText nie może być zmieniana niezależnie od tego, czy powiązany z nią obiekt jest interaktywny.

Aby uczynić obiekt interaktywnym, ustaw jego właściwość IsInteractive na true, jak pokazano poniżej:

 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)
        {
        }
    }
}

Wszystkie obiekty wykresu mają właściwość IsInteractive. Jej wartość domyślna to false.

Jeśli obiekt wykresu jest interaktywny, nie zostanie usunięty po usunięciu lub zatrzymaniu instancji wskaźnika lub cBota. W przeciwieństwie do tego obiekty nieinteraktywne są usuwane automatycznie, gdy wskaźnik lub cBot przestaje działać.

Aby uniknąć bałaganu, upewnij się, że obiekty interaktywne są czyszczone po usunięciu powiązanej instancji, używając metody Destroy wskaźnika lub metody OnStop cBota.

Jeśli obiekt wykresu nie jest interaktywny, będzie również niewidoczny na listach i w kolekcjach obiektów wykresu.

Obiekty zablokowane i odblokowane

Zablokowanie obiektu wykresu uniemożliwia użytkownikowi jego modyfikowanie lub aktualizowanie. Możesz to zrobić za pomocą okna Trader (właściwości wykresu) lub ustawiając właściwość IsLocked na true. Wartość domyślna to false.

Możesz użyć blokowania, aby uczynić obiekt interaktywnym, ale jednocześnie uniemożliwić użytkownikowi jego aktualizowanie. Poniższy przykład blokuje obiekt linii pionowej:

 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)
        {
        }
    }
}

Pokaż i ukryj

Możesz narysować obiekt na wykresie, a następnie go ukryć. Chociaż obiekt nadal będzie obecny na wykresie, będzie całkowicie niewidoczny.

Aby to zrobić, ustaw właściwość IsHidden na 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)
        {
        }
    }
}

Zużycie zasobów jest głównym czynnikiem przy korzystaniu z tej funkcji. Jeśli istnieje obiekt, który powinien często pojawiać się na wykresie (ale nie przez cały czas), lepiej go ukryć zamiast usuwać i ponownie rysować za każdym razem, gdy jest potrzebny.

Zaznaczanie obiektów i indeks wyglądu (ZIndex)

Naturalnie mogą wystąpić przypadki, w których kilka obiektów przecina się ze sobą na wykresie, co utrudnia lub wręcz uniemożliwia zaznaczenie jednego konkretnego obiektu. Tylko obiekt, który został narysowany jako ostatni, będzie możliwy do zaznaczenia po najechaniu kursorem myszy na grupę przecinających się obiektów wykresu.

Aby zmienić to zachowanie, możesz użyć właściwości ZIndex. Może być pomocna przy próbie wyświetlenia złożonych formacji obiektów wykresu.

 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)
        {
        }
    }
}

Po dołączeniu do wykresu powyższy wskaźnik wyświetli czerwoną linię pionową zamiast żółtej. Ponieważ wartość właściwości ZIndex pierwszej linii jest wyższa niż odpowiednia wartość drugiej linii, ma ona pierwszeństwo przed drugą (żółtą) linią.

Innymi słowy, właściwość ZIndex określa, które obiekty wykresu są wyświetlane jako pierwsze, gdy się nakładają. Wszystkie narysowane obiekty wykresu są automatycznie przypisywane z określoną wartością właściwości ZIndex.

Sprawdź, czy obiekt został usunięty

Wszystkie obiekty wykresu dziedziczą właściwość IsAlive z klasy bazowej ChartObject. Wartość tej właściwości to true, dopóki obiekt jest obecny na wykresie (niezależnie od tego, czy jest widoczny, czy niewidoczny). Wartość jest ustawiana na false, gdy tylko obiekt zostanie usunięty z wykresu.

Jeśli zapiszesz odwołanie do obiektu wykresu, a ten obiekt zostanie usunięty przez użytkownika, możesz sprawdzić właściwość IsAlive, aby sprawdzić, czy obiekt jest aktywny, czy nieaktywny.

 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)
        {
        }
    }
}

Jeśli uruchomisz instancję tego wskaźnika, kliknij prawym przyciskiem myszy czerwoną linię i usuń ją. Dziennik powinien wtedy wyświetlić komunikat "Object reference removed".

Jeśli zachowasz odwołanie do (martwego) obiektu wykresu, wystąpi wyciek pamięci. Podczas odśmiecania pamięci Twój obiekt będzie nadal uważany za aktywny, co oznacza, że przetrwa proces odśmiecania.

Aby uniknąć tego problemu, użyj zdarzenia ObjectsRemoved w połączeniu z właściwością IsAlive.

Indeks słupka lub czas

Podczas kodowania obiektów wykresu możesz użyć czasu wykresu lub indeksu słupka dla osi X.

Naszym zdaniem czas wykresu jest lepszą opcją. Podczas używania indeksów słupków nie możesz planować przyszłych wartości (ponieważ wymagane indeksy jeszcze nie istnieją) i nie możesz uwzględnić żadnych odstępów między słupkami. Jednak indeksy słupków mogą być łatwiejsze w użyciu, szczególnie w przypadku stosunkowo prostych obiektów wykresu.

Wszystkie metody Chart.Draw mają różne przeciążenia dla indeksów słupków i czasu wykresu.