Przykłady kodu wskaźników
Ta strona zawiera kilka przykładów kodu w Pythonie i C# do tworzenia wskaźników technicznych używanych w handlu manualnym lub algorytmicznym.
Repozytoria przykładowych wskaźników
Kompleksowe przykłady kodu wskaźników, w tym gotowe do uruchomienia szablony dla różnych typów wskaźników i narzędzi analizy technicznej, są dostępne w oddzielnych repozytoriach Python i C# w serwisie GitHub.
Proste wskaźniki
Wskaźnik High Minus Low oblicza różnicę między cenami maksymalną i minimalną bieżącego słupka i wyświetla ją w serii wyjściowej. Seria jest rysowana na wykresie jako linia łącząca wynikowe wartości w każdym słupku.
1 2 3 4 5 6 7 8 9 10 11 | |
Uwaga
Wskaźniki Pythona używają dostosowywalnych parametrów zadeklarowanych w ich plikach .cs.
1 2 3 4 5 6 7 | |
Deklaracja [Indicator...] zawiera kilka parametrów, które są zdefiniowane w następujący sposób:
IsOverlay— wartość logiczna określająca, czy linia będzie nakładana na wykres, czy wyświetlana w oddzielnym panelu interfejsu użytkownika.TimeZone— pole klasyTimeZonesokreślające strefę czasową danych wskaźnika i czasu serwera.AccessRights— pole klasyAccessRightsokreślające prawa dostępu przydzielone Twojemu wskaźnikowi.ScalePrecision— liczba całkowita ustawiająca precyzję skali danych wyjściowych wskaźnika.
Jak już wcześniej poznałeś podczas edycji kodu wskaźnika, atrybut Output jest deklarowany w celu oznaczenia właściwości jako danych wyjściowych wskaźnika. Ta właściwość powinna być public, aby mogła być przywoływana przez inne klasy.
Dane wyjściowe wskaźnika powinny zawsze być typu danych IndicatorDataSeries, który jest listą wartości podwójnych, które można indeksować jak tablicę. Dlatego wartość w każdym [index] na liście Result może być przypisana w metodzie Calculate w następujący sposób.
1 2 3 4 | |
1 2 | |
Wskaźniki z parametrami
W większości przypadków dane wyjściowe wskaźników mogą się różnić w zależności od danych wprowadzonych przez użytkownika. Sposób konfigurowania dostosowywalnych parametrów dla wskaźników jest podobny do tego, jak robi się to w przypadku cBotów.
Poniższy wskaźnik Simple Moving Average został zaprojektowany tak, aby akceptował źródło ceny i okres jako parametry dostosowywalne. Takie parametry (Source i Periods w tym konkretnym przykładzie) muszą być poprzedzone atrybutem Parameter.
Podobnie jak omówione wcześniej atrybuty [Indicator()] i [Output()], atrybut [Parameter()] może definiować pewne cechy stosowane do danych wprowadzanych przez użytkownika.
1 | |
Uwaga
Wskaźniki Pythona używają dostosowywalnych parametrów zadeklarowanych w ich plikach .cs.
Powyżej określamy następujące cechy:
- Nazwę wyświetlaną w celu oznaczenia tego parametru w interfejsie użytkownika cTrader (
"MA Periods). - Wartość domyślną parametru; może zostać zmieniona przez użytkownika podczas dostosowywania instancji (
DefaultValue = 14) - Minimalne i maksymalne wartości parametru (
MinValue = 1, MaxValue = 20)
W poniższym fragmencie pokazujemy, jak dostosowywalne parametry mogą być zintegrowane z kodem wskaźnika.
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 | |
Uwaga
Wskaźniki Pythona używają dostosowywalnych parametrów zadeklarowanych w ich plikach .cs.
1 2 3 4 5 6 7 8 | |
Wskaźniki zagnieżdżone
Wskaźniki zagnieżdżone są definiowane jako wskaźniki, których wartość zależy od wyników obliczeń wykonywanych przez inne wskaźniki. Są przydatne podczas pisania niektórych typów rozszerzeń, takich jak wskaźnik DeMark 9. Rozważ następujący przykładowy kod:
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 | |
Uwaga
Wskaźniki Pythona używają dostosowywalnych parametrów zadeklarowanych w ich plikach .cs.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
W powyższym przykładzie deMinMA i deMaxMA to dwie zmienne używane do obliczania wartości naszego wskaźnika DeMarker.
Wskaźniki zagnieżdżone muszą być zdefiniowane w metodzie Initialize(). Na przykład deMinMA jest zdefiniowany jako Simple Moving Average serii deMin.
1 | |
1 | |
deMin z kolei jest zdefiniowany w metodzie Calculate() jako maksimum z dwóch ostatnich wartości ceny minimalnej.
1 | |
1 | |
Aby uprościć pracę ze wskaźnikami zagnieżdżonymi, IntelliSense automatycznie wypełnia listę wszystkich wskaźników wbudowanych, gdy wpiszesz Indicators, a następnie kropkę do edytora kodu. Wyświetli również wszystkie powiązane parametry wejściowe po wybraniu określonego wskaźnika z tej listy.

Ładowanie z opóźnieniem
cTrader Algo używa ładowania z opóźnieniem, gdy korzystasz z przywoływanych wskaźników. Wartości dostarczane przez przywoływany wskaźnik nie są obliczane, dopóki Twój kod nie zacznie ich aktywnie używać.
Jeśli uzyskujesz dostęp do danych Outputs przywoływanego wskaźnika, cTrader rozpocznie ładowanie danych wskaźnika, wywołując jego metodę Calculate() na poprzednich i przyszłych słupkach. W każdym innym przypadku przywoływany wskaźnik pozostanie bezczynny i dlatego nie będzie zużywał żadnych zasobów systemowych.
Oznacza to również, że jeśli Twój wskaźnik nie ma żadnych Output lub jeśli spróbujesz uzyskać dostęp do którejkolwiek z jego właściwości publicznych, otrzymasz wartość domyślną danej właściwości. Aby rozwiązać ten problem, wywołaj metodę Calculate() swojego niestandardowego wskaźnika z metody Calculate() bieżącego wskaźnika.
Oscylatory i atrybut poziomów
Termin „oscylatory" obejmuje wszystkie wskaźniki, które oscylują wokół określonej stałej zmiennej.
Podczas tworzenia oscylatorów przydatne jest najpierw narysowanie poziomej lub „poziomowej" linii przy tej stałej wartości; wskaźnik będzie następnie oscylował wokół tej linii. W wielu przypadkach wartość stała wynosi zero.
W poniższym przykładzie definiujemy oscylator momentum. Zazwyczaj oscyluje on wokół wartości 100. Dodajemy linię poziomu przy tej wartości, używając atrybutu Levels, który jest deklarowany przed atrybutami wskaźnika.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
Uwaga
Wskaźniki Pythona używają dostosowywalnych parametrów zadeklarowanych w ich plikach .cs.
1 2 3 4 | |
Należy pamiętać, że atrybut Levels może być używany tylko wtedy, gdy wskaźnik nie jest nakładany na wykres, co oznacza, że właściwość IsOverlay nie jest ustawiona na true. Domyślnie wartość IsOverlay to false. Jeśli ten atrybut zostanie pominięty w Twoim kodzie, Levels powinien działać prawidłowo.
Jeśli musisz ustanowić wiele linii „poziomowych", dodaj listę wartości oddzielonych przecinkami w nawiasach, jak pokazano poniżej.
1 | |
1 | |
1 | |
Poziomy we wskaźnikach Pythona
W Pythonie deklarujesz poziomy w pliku C# swojego wskaźnika tak samo jak we wskaźnikach C#.
Właściwość IsLastBar
W niektórych przypadkach możesz chcieć utworzyć wskaźnik, który musi być obliczany tylko dla ostatniego słupka na wykresie handlowym. Upraszczając to, właściwość IsLastBar może być używana do sprawdzenia, czy parametr indeksu metody Calculate() jest parametrem ostatniego słupka.
Poniższy wskaźnik jest oparty na czasie UTC; może jednak wyświetlać ostatni czas otwarcia w Nowym Jorku i Tokio.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Łączenie wskaźników
cTrader umożliwia łączenie wielu wskaźników w tym samym panelu lub na tym samym wykresie.
Następujący wskaźnik łączy wskaźniki Aroon, RSI i systemu ruchu kierunkowego w jeden.
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 | |
Uwaga
Wskaźniki Pythona używają dostosowywalnych parametrów zadeklarowanych w ich plikach .cs.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
Wiele ram czasowych
-
Używanie wielu ram czasowych
Poniższy przykład wyświetla wskaźnik Moving Average na różnych ramach czasowych.
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
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC)] public class MultiTF_MA : Indicator { [Parameter(DefaultValue = 50)] public int Period { get; set; } [Output("MA", LineColor = "Yellow")] public IndicatorDataSeries MA { get; set; } [Output("MA5", LineColor = "Orange")] public IndicatorDataSeries MA5 { get; set; } [Output("MA10", LineColor = "Red")] public IndicatorDataSeries MA10 { get; set; } private Bars bars5; private Bars bars10; private MovingAverage ma; private MovingAverage ma5; private MovingAverage ma10; protected override void Initialize() { bars5 = MarketData.GetBars(TimeFrame.Minute5); bars10 = MarketData.GetBars(TimeFrame.Minute10); ma = Indicators.MovingAverage(Bars.ClosePrices, Period, MovingAverageType.Triangular); ma5 = Indicators.MovingAverage(bars5.ClosePrices, Period, MovingAverageType.Triangular); ma10 = Indicators.MovingAverage(bars10.ClosePrices, Period, MovingAverageType.Triangular); } public override void Calculate(int index) { MA[index] = ma.Result[index]; var index5 = bars5.OpenTimes.GetIndexByTime(Bars.OpenTimes[index]); if (index5 != -1) MA5[index] = ma5.Result[index5]; var index10 = bars10.OpenTimes.GetIndexByTime(Bars.OpenTimes[index]); if (index10 != -1) MA10[index] = ma10.Result[index10]; } }Uwaga
Wskaźniki Pythona używają dostosowywalnych parametrów zadeklarowanych w ich plikach
.cs.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
class MultiTF_MA(): def initialize(self): self.bars5 = api.MarketData.GetBars(TimeFrame.Minute5) self.bars10 = api.MarketData.GetBars(TimeFrame.Minute10) # Period is declared as a parameter in the C# file # We use indicator time frame bars for this moving average self.ma = Indicators.MovingAverage(api.Bars.ClosePrices, api.Period, MovingAverageType.Triangular) # We use other two time frame bars that we created previously # for these two moving averages self.ma5 = Indicators.MovingAverage(self.bars5.ClosePrices, api.Period, MovingAverageType.Triangular) self.ma10 = Indicators.MovingAverage(self.bars10.ClosePrices, api.Period, MovingAverageType.Triangular) def calculate(self, index): # MA, MA5, and MA10 are outputs we declared in the C# file api.MA[index] = self.ma.Result[index] index5 = self.bars5.OpenTimes.GetIndexByTime(api.Bars.OpenTimes[index]) if index5 > -1: api.MA5[index] = self.ma5.Result[index5] index10 = self.bars10.OpenTimes.GetIndexByTime(api.Bars.OpenTimes[index]) if index10 > -1: api.MA10[index] = self.ma10.Result[index10] -
Używanie wielu ram czasowych i symboli
Poniższy przykład wyświetla wskaźnik Moving Average na wielu ramach czasowych i symbolach.
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
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC)] public class MultiSymbolMA : Indicator { private MovingAverage ma1, ma2, ma3; private Bars bars2, bars3; private Symbol symbol2, symbol3; [Parameter(DefaultValue = "EURCHF")] public string Symbol2 { get; set; } [Parameter(DefaultValue = "EURCAD")] public string Symbol3 { get; set; } [Parameter(DefaultValue = 14)] public int Period { get; set; } [Parameter(DefaultValue = MovingAverageType.Simple)] public MovingAverageType MaType { get; set; } [Output("MA Symbol 1", LineColor = "Magenta")] public IndicatorDataSeries Result1 { get; set; } [Output("MA Symbol 2", LineColor = "Magenta")] public IndicatorDataSeries Result2 { get; set; } [Output("MA Symbol 3", LineColor = "Magenta")] public IndicatorDataSeries Result3 { get; set; } protected override void Initialize() { symbol2 = Symbols.GetSymbol(Symbol2); symbol3 = Symbols.GetSymbol(Symbol3); bars2 = MarketData.GetBars(TimeFrame, symbol2.Name); bars3 = MarketData.GetBars(TimeFrame, symbol3.Name); ma1 = Indicators.MovingAverage(Bars.ClosePrices, Period, MaType); ma2 = Indicators.MovingAverage(bars2.ClosePrices, Period, MaType); ma3 = Indicators.MovingAverage(bars3.ClosePrices, Period, MaType); } public override void Calculate(int index) { ShowOutput(Symbol, Result1, ma1, Bars, index); ShowOutput(symbol2, Result2, ma2, bars2, index); ShowOutput(symbol3, Result3, ma3, bars3, index); } private void ShowOutput(Symbol symbol, IndicatorDataSeries result, MovingAverage movingAverage, Bars bars, int index) { var index2 = bars.OpenTimes.GetIndexByTime(Bars.OpenTimes[index]); result[index] = movingAverage.Result[index2]; string text = string.Format("{0} {1}", symbol.Name, Math.Round(result[index], symbol.Digits)); Chart.DrawStaticText(symbol.Name, text, VerticalAlignment.Top, HorizontalAlignment.Right, Color.Yellow); } }Uwaga
Wskaźniki Pythona używają dostosowywalnych parametrów zadeklarowanych w ich plikach
.cs.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
class MultiSymbolMA(): def initialize(self): # Symbol2 and Symbol3 are declared as parameters in the C# file self.symbol2 = api.Symbols.GetSymbol(api.Symbol2) self.symbol3 = api.Symbols.GetSymbol(api.Symbol3) self.bars2 = api.MarketData.GetBars(api.TimeFrame, self.symbol2.Name) self.bars3 = api.MarketData.GetBars(api.TimeFrame, self.symbol3.Name) self.ma1 = api.Indicators.MovingAverage(api.Bars.ClosePrices, api.Period, api.MaType) self.ma2 = api.Indicators.MovingAverage(self.bars2.ClosePrices, api.Period, api.MaType) self.ma3 = api.Indicators.MovingAverage(self.bars3.ClosePrices, api.Period, api.MaType) def calculate(self, index): # Result1, Result2, and Result3 are declared as outputs in the C# file self.show_output(api.Symbol, api.Result1, self.ma1, api.Bars, index) self.show_output(self.symbol2, api.Result2, self.ma2, self.bars2, index) self.show_output(self.symbol3, api.Result3, self.ma3, self.bars3, index) def show_output(self, symbol, result, movingAverage, bars, index): index2 = bars.OpenTimes.GetIndexByTime(api.Bars.OpenTimes[index]) result[index] = movingAverage.Result[index2] text = f"{symbol.Name} {round(result[index], symbol.Digits)}" api.Chart.DrawStaticText(symbol.Name, text, VerticalAlignment.Top, HorizontalAlignment.Right, Color.Yellow)
