Przejdź do treści

Jak zautomatyzować manualną strategię handlową

Handel zautomatyzowany lub algorytmiczny oferuje kilka korzyści w porównaniu z handlem manualnym:

  • Zautomatyzowane systemy eliminują podejmowanie decyzji pod wpływem emocji i wykonują transakcje ze zwiększoną precyzją, szybkością i wydajnością.
  • Algorytmy konsekwentnie wdrażają zaawansowane strategie handlowe, a środki zarządzania ryzykiem są niezawodnie egzekwowane.
  • Algorytmy umożliwiają backtesting strategii przy użyciu danych historycznych oraz dywersyfikację w wielu aktywach i rynkach jednocześnie.

Interfejs API cTrader Algo umożliwia traderom automatyzację złożonych strategii manualnych, pozwalając im wykorzystać wiele zalet handlu algorytmicznego. W tym artykule i towarzyszącym mu filmie dowiesz się, jak automatyzować skomplikowane strategie manualne i przekształcać sekwencje operacji w algorytmy.

Zidentyfikuj wzorce strategii manualnej

Aby opracować algorytm, załóżmy, że ręcznie handlujemy formacją hammer dla pozycji długich i formacją hanging man dla pozycji krótkich.

Formacja hammer pojawia się podczas trendu spadkowego i sugeruje potencjalne odwrócenie bycze. Formacja hammer służy do identyfikacji wejść kupna.

Formacja hanging man pojawia się podczas trendu wzrostowego i wskazuje na potencjalne odwrócenie niedźwiedzie. Formacja hanging man służy do identyfikacji wejść sprzedaży.

Opracuj cBota do handlu strategią manualną

W cTrader Algo utwórzmy algorytm, który implementuje strategię opisaną w poprzedniej sekcji.

Kliknij przycisk Nowy w zakładce cBot. Wpisz Patterns Strategy w polu nazwy i kliknij Utwórz.

W metodzie OnBarClosed() implementujemy logikę naszej strategii. Nasze wejścia muszą spełniać te dwa warunki:

  • Cena zamknięcia odpowiada cenie maksymalnej.
  • Rozmiar świecy jest co najmniej pięć razy większy niż korpus świecy.

Zaczynamy od zdefiniowania parametrów Volume, StopLoss i TakeProfit.

1
2
3
4
5
6
7
8
[Parameter(DefaultValue = 1000)]
public double Volume { get; set; }

[Parameter(DefaultValue = 10)]
public double StopLoss { get; set; }

[Parameter(DefaultValue = 10)]
public double TakeProfit { get; set; }

Następnie piszemy fragment kodu do wykonania transakcji kupna, gdy wymagane warunki są spełnione.

1
2
3
4
5
if (Bars.Last(0).Close == Bars.Last(0).High &&
            (Bars.Last(0).Close - Bars.Last(0).Open) < (Bars.Last(0).Close - Bars.Last(0).Low) * 0.2)
{
    ExecuteMarketOrder(TradeType.Buy, SymbolName, Volume, InstanceId, StopLoss, TakeProfit);
}
1
2
3
bar = api.Bars.Last(0)
if bar.Close == bar.High and (bar.Close - bar.Open) < (bar.Close - bar.Low) * 0.2:
    api.ExecuteMarketOrder(TradeType.Buy, api.SymbolName, api.Volume, api.InstanceId, api.StopLoss, api.TakeProfit)

Po przeciwnej stronie, oto warunki, które muszą być spełnione dla naszych wejść sprzedaży:

  • Cena zamknięcia odpowiada cenie minimalnej.
  • Rozmiar świecy jest co najmniej pięć razy większy niż korpus świecy.

Podobnie piszemy fragment kodu do wykonania transakcji sprzedaży, gdy wymagane warunki są spełnione.

1
2
3
4
5
if (Bars.Last(0).Close == Bars.Last(0).Low &&
                (Bars.Last(0).Open - Bars.Last(0).Close) < (Bars.Last(0).High - Bars.Last(0).Close) * 0.2)
{
    ExecuteMarketOrder(TradeType.Sell, SymbolName, Volume, InstanceId, StopLoss, TakeProfit);
}
1
2
if bar.Close == bar.Low and (bar.Open - bar.Close) < (bar.High - bar.Close) * 0.2:
    api.ExecuteMarketOrder(TradeType.Sell, api.SymbolName, api.Volume, api.InstanceId, api.StopLoss, api.TakeProfit)

Możesz skopiować pełny kod poniżej:

 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
using System;
using cAlgo.API;
using cAlgo.API.Collections;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;

namespace cAlgo.Robots
{
    [Robot(AccessRights = AccessRights.None, AddIndicators = true)]
    public class PatternsStrategy : Robot
    {
        [Parameter(DefaultValue = 1000)]
        public double Volume { get; set; }

        [Parameter(DefaultValue = 10)]
        public double StopLoss { get; set; }

        [Parameter(DefaultValue = 10)]
        public double TakeProfit { get; set; }

        protected override void OnStart()
        {

        }

        protected override void OnBarClosed()
        {
            if (Bars.Last(0).Close == Bars.Last(0).High &&
            (Bars.Last(0).Close - Bars.Last(0).Open) < (Bars.Last(0).Close - Bars.Last(0).Low) * 0.2)
            {
                ExecuteMarketOrder(TradeType.Buy, SymbolName, Volume, InstanceId, StopLoss, TakeProfit);
            }

            if (Bars.Last(0).Close == Bars.Last(0).Low &&
            (Bars.Last(0).Open - Bars.Last(0).Close) < (Bars.Last(0).High - Bars.Last(0).Close) * 0.2)
            {
                ExecuteMarketOrder(TradeType.Sell, SymbolName, Volume, InstanceId, StopLoss, TakeProfit);
            }

        }

        protected override void OnStop()
        {

        }
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import clr
clr.AddReference("cAlgo.API")
from cAlgo.API import *
from robot_wrapper import *

class PatternsStrategy():
    def on_start(self):
        pass

    def on_bar_closed(self):
        bar = api.Bars.Last(0)

        if bar.Close == bar.High and (bar.Close - bar.Open) < (bar.Close - bar.Low) * 0.2:
            api.ExecuteMarketOrder(TradeType.Buy, api.SymbolName, api.Volume, api.InstanceId, api.StopLoss, api.TakeProfit)

        if bar.Close == bar.Low and (bar.Open - bar.Close) < (bar.High - bar.Close) * 0.2:
            api.ExecuteMarketOrder(TradeType.Sell, api.SymbolName, api.Volume, api.InstanceId, api.StopLoss, api.TakeProfit)

    def on_stop(self):
        pass

Aby zbudować cBota, naciśnij Ctrl+B lub kliknij Zbuduj.

Aby przeprowadzić backtesting cBota, najpierw dodajemy instancję. Wybierz opcję Lokalnie, określ preferowane parametry i kliknij przycisk Dodaj instancję.

Następnie przechodzimy do zakładki Backtesting, określamy okres dla operacji backtestingu i rozpoczynamy backtesting.

Po zakończeniu procesu backtestingu możemy sprawdzić transakcje. Zobaczysz, że warunki wejścia są spełnione tuż przed zawarciem każdej transakcji.

Dzięki pomyślnej automatyzacji naszej strategii możemy wykorzystać cBota do zawierania transakcji w naszym imieniu.

Wdrożenie strategii odwrócenia RSI

W naszym drugim przykładzie chcemy wdrożyć strategię opartą na odwróceniu wskaźnika Relative Strength Index (RSI). W tej strategii otwieramy pozycje, oczekując, że RSI odwróci swój kierunek i postępujemy zgodnie z tymi zasadami:

  • Gdy wartość RSI spadnie poniżej progu kupna RSI, otwieramy pozycję kupna.
  • Gdy wartość RSI wzrośnie powyżej progu sprzedaży RSI, otwieramy pozycję sprzedaży.

Utwórzmy nowego cBota, wprowadź RSI Reversal Strategy jako jego nazwę i kliknij Utwórz.

Po pojawieniu się edytora kodu dodaj System.Linq na górze kodu jako odniesienie.

1
using System.Linq;

Dodajmy dwa parametry, które pozwolą nam modyfikować progi przed uruchomieniem cBota.

1
2
3
4
5
[Parameter(DefaultValue = 30)]
public int BuyLevel { get; set; }

[Parameter(DefaultValue = 70)]
public int SellLevel { get; set; }

Deklarujemy i inicjalizujemy nasz wskaźnik siły względnej.

1
2
3
4
5
6
private RelativeStrengthIndex _rsi;

protected override void OnStart()
{
    _rsi = Indicators.RelativeStrengthIndex(Bars.ClosePrices, 14);
}
1
2
3
class RSIReversalStrategy():
    def on_start(self):
        self._rsi = api.Indicators.RelativeStrengthIndex(api.Bars.ClosePrices, 14)

Następnie implementujemy warunki, które definiują naszą logikę handlową.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
protected override void OnBarClosed()
{

    if (_rsi.Result.LastValue < BuyLevel)
    {
        if (Positions.Count == 0)
            ExecuteMarketOrder(TradeType.Buy, SymbolName, 1000);
        foreach (var position in Positions.Where(p => p.TradeType == TradeType.Sell))
        {
            position.Close();
        }

    }
    else if (_rsi.Result.LastValue > SellLevel)
    {
        if (Positions.Count == 0)
            ExecuteMarketOrder(TradeType.Sell, SymbolName, 1000);
        foreach (var position in Positions.Where(p => p.TradeType == TradeType.Buy))
        {
            position.Close();
        }
    }

}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
def on_bar_closed(self):
    rsi_value = self._rsi.Result.LastValue

    if rsi_value < api.BuyLevel:
        if api.Positions.Count == 0:
            api.ExecuteMarketOrder(TradeType.Buy, api.SymbolName, 1000)
        for position in api.Positions:
            if position.TradeType == TradeType.Sell:
                position.Close()

    elif rsi_value > api.SellLevel:
        if api.Positions.Count == 0:
            api.ExecuteMarketOrder(TradeType.Sell, api.SymbolName, 1000)
        for position in api.Positions:
            if position.TradeType == TradeType.Buy:
                position.Close()

Możesz skopiować pełny kod poniżej:

 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 System.Linq;
using cAlgo.API;
using cAlgo.API.Collections;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;

namespace cAlgo.Robots
{
    [Robot(AccessRights = AccessRights.None, AddIndicators = true)]
    public class RSIReversalStrategy : Robot
    {
        [Parameter(DefaultValue = 30)]
        public int BuyLevel { get; set; }

        [Parameter(DefaultValue = 70)]
        public int SellLevel { get; set; }

        private RelativeStrengthIndex _rsi;

        protected override void OnStart()
        {
            _rsi = Indicators.RelativeStrengthIndex(Bars.ClosePrices, 14);
        }

        protected override void OnBarClosed()
        {

            if (_rsi.Result.LastValue < BuyLevel)
            {
                if (Positions.Count == 0)
                    ExecuteMarketOrder(TradeType.Buy, SymbolName, 1000);
                foreach (var position in Positions.Where(p => p.TradeType == TradeType.Sell))
                {
                    position.Close();
                }

            }
            else if (_rsi.Result.LastValue > SellLevel)
            {
                if (Positions.Count == 0)
                    ExecuteMarketOrder(TradeType.Sell, SymbolName, 1000);
                foreach (var position in Positions.Where(p => p.TradeType == TradeType.Buy))
                {
                    position.Close();
                }
            }

        }

        protected override void OnStop()
        {

        }
    }
}
 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
import clr
clr.AddReference("cAlgo.API")
from cAlgo.API import *
from robot_wrapper import *

class RSIReversalStrategy():
    def on_start(self):
        self._rsi = api.Indicators.RelativeStrengthIndex(api.Bars.ClosePrices, 14)

    def on_bar_closed(self):
        rsi_value = self._rsi.Result.LastValue

        if rsi_value < api.BuyLevel:
            if api.Positions.Count == 0:
                api.ExecuteMarketOrder(TradeType.Buy, api.SymbolName, 1000)
            for position in api.Positions:
                if position.TradeType == TradeType.Sell:
                    position.Close()

        elif rsi_value > api.SellLevel:
            if api.Positions.Count == 0:
                api.ExecuteMarketOrder(TradeType.Sell, api.SymbolName, 1000)
            for position in api.Positions:
                if position.TradeType == TradeType.Buy:
                    position.Close()

    def on_stop(self):
        pass

Zbudujmy naszą strategię, dodajmy instancję i przeprowadźmy backtesting, tak jak zrobiliśmy to z poprzednią strategią.

Możemy ponownie sprawdzić i przejrzeć niektóre transakcje, aby zobaczyć, że warunki wejścia są spełnione tuż przed zawarciem transakcji.

Ten artykuł pokazał, jak zidentyfikować strategię manualną i przekształcić ją w zautomatyzowaną, umożliwiając operacje handlu algorytmicznego.

Image title