Ir para o conteúdo

Operações avançadas com cBots

Este guia destina-se principalmente a complementar a nossa extensa lista de exemplos de código cBot.

Negociar outros símbolos

Pode desenvolver um cBot que negocie múltiplos símbolos além do especificado ao criar uma nova instância.

Tornando isso possível, Symbols é uma coleção de todos os símbolos disponíveis para a sua conta de negociação. Pode iterar sobre ela usando loops; alternativamente, pode pesquisar nela para encontrar símbolos específicos.

Para trabalhar corretamente com múltiplos símbolos, os cBots precisam de conhecer as seguintes informações:

  • O volume mínimo e máximo permitido
  • O passo do volume
  • O tamanho do tick em pips
  • O valor do tick em pips (o valor monetário de um pip ou tick na moeda de depósito da conta)

Estes dados podem ser obtidos a partir de um objeto Symbol. No código abaixo, encontramos um símbolo com "GBPUSD" como nome e depois criamos uma ordem de mercado para ele. Também usamos o seu volume mínimo permitido como volume da ordem.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[Robot()]
public class Sample_cBot : Robot
{
    protected override void OnStart()
    {
        var symbol = Symbols.GetSymbol("GBPUSD");

        if (symbol is not null)
        {
            ExecuteMarketOrder(TradeType.Sell, symbol.Name, symbol.VolumeInUnitsMin);
        }
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import clr

clr.AddReference("cAlgo.API")

# Import cAlgo API types
from cAlgo.API import *
from cAlgo.API.Internals import *

# Import trading wrapper functions
from robot_wrapper import *

class Sample_cBot():
    def on_start(self):
        symbol = api.Symbols.GetSymbol("GBPUSD")

        if symbol is not None:
            api.ExecuteMarketOrder(TradeType.Sell, symbol.Name, symbol.VolumeInUnitsMin)

Converter pips para ticks

Ao codificar cBots, é fácil encontrar bugs decorrentes do facto de alguns valores de variáveis serem calculados em unidades enquanto outros valores são calculados em pips.

A classe SymbolExtensions abaixo mostra como os pips podem ser convertidos em ticks. Também demonstra como os pips podem ser adicionados a um valor de preço absoluto.

  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
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
using System;
using cAlgo.API;
using cAlgo.API.Internals;

namespace Samples
{
    [Robot(AccessRights = AccessRights.None)]
    public class Sample : Robot
    {
        protected override void OnStart()
        {
            var spreadInPips = Symbol.NormalizePips(Symbol.ToPips(Symbol.Spread));

            Print(spreadInPips);

            var spreadInTicks = Math.Round(Symbol.ToTicks(Symbol.Spread));

            Print(spreadInTicks);

            /* Calculating a long position stop loss using the
            absolute price value. */
            var stopLossInPips = 60;

            /* We use 'NormalizePips' to avoid the 'invalid decimal places'
            error in case our stop loss value has too many decimals. */
            var stopLossInPrice = Symbol.Ask - (Symbol.NormalizePips(stopLossInPips) * Symbol.PipSize);

            Print(stopLossInPrice);
        }
    }

    public static class SymbolExtensions
    {
        /// <summary>
        /// Returns a symbol pip value
        /// </summary>
        /// <param name="symbol"></param>
        /// <returns>double</returns>
        public static double GetPip(this Symbol symbol)
        {
            return symbol.TickSize / symbol.PipSize * Math.Pow(10, symbol.Digits);
        }

        /// <summary>
        /// Returns a price value in terms of pips
        /// </summary>
        /// <param name="symbol"></param>
        /// <param name="price">The price level</param>
        /// <returns>double</returns>
        public static double ToPips(this Symbol symbol, double price)
        {
            return price * symbol.GetPip();
        }

        /// <summary>
        /// Returns a price value in terms of ticks
        /// </summary>
        /// <param name="symbol"></param>
        /// <param name="price">The price level</param>
        /// <returns>double</returns>
        public static double ToTicks(this Symbol symbol, double price)
        {
            return price * Math.Pow(10, symbol.Digits);
        }

        /// <summary>
        /// Rounds a price level to the number of symbol digits
        /// </summary>
        /// <param name="symbol">The symbol</param>
        /// <param name="price">The price level</param>
        /// <returns>double</returns>
        public static double Round(this Symbol symbol, double price)
        {
            return Math.Round(price, symbol.Digits);
        }

        /// <summary>
        /// Normalize x Pips amount decimal places to something that can be used as a stop loss or take profit for an order.
        /// For example if symbol is EURUSD and you pass to this method 10.456775 it will return back 10.5
        /// </summary>
        /// <param name="symbol">The symbol</param>
        /// <param name="pips">The amount of Pips</param>
        /// <returns>double</returns>
        public static double NormalizePips(this Symbol symbol, double pips)
        {
            var currentPrice = Convert.ToDecimal(symbol.Bid);

            var pipSize = Convert.ToDecimal(symbol.PipSize);

            var pipsDecimal = Convert.ToDecimal(pips);

            var pipsAddedToCurrentPrice = Math.Round((pipsDecimal * pipSize) + currentPrice, symbol.Digits);

            var tickSize = Convert.ToDecimal(symbol.TickSize);

            var result = (pipsAddedToCurrentPrice - currentPrice) * tickSize / pipSize * Convert.ToDecimal(Math.Pow(10, symbol.Digits));

            return decimal.ToDouble(result);
        }
    }
}
 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
import clr
import math

clr.AddReference("cAlgo.API")

from cAlgo.API import *
from robot_wrapper import *


class Sample():
    def on_start(self):
        spreadInPips = SymbolExtensions.normalize_pips(api.Symbol, SymbolExtensions.to_pips(api.Symbol, api.Symbol.Spread))
        print(spreadInPips)

        spreadInTicks = round(SymbolExtensions.to_ticks(api.Symbol, api.Symbol.Spread))
        print(spreadInTicks)

        stopLossInPips = 60

        stopLossInPrice = api.Symbol.Ask - (SymbolExtensions.normalize_pips(api.Symbol, stopLossInPips) * api.Symbol.PipSize)
        print(stopLossInPrice)


class SymbolExtensions:
    @staticmethod
    def get_pip(symbol):
        return symbol.TickSize / symbol.PipSize * math.pow(10, symbol.Digits)

    @staticmethod
    def to_pips(symbol, price):
        return price * SymbolExtensions.get_pip(symbol)

    @staticmethod
    def to_ticks(symbol, price):
        return price * math.pow(10, symbol.Digits)

    @staticmethod
    def round(symbol, price):
        return round(price, symbol.Digits)

    @staticmethod
    def normalize_pips(symbol, pips):
        currentPrice = float(symbol.Bid)
        pipSize = float(symbol.PipSize)
        pipsDecimal = float(pips)

        pipsAddedToCurrentPrice = round((pipsDecimal * pipSize) + currentPrice, symbol.Digits)
        tickSize = float(symbol.TickSize)

        result = (pipsAddedToCurrentPrice - currentPrice) * tickSize / pipSize * math.pow(10, symbol.Digits)
        return round(result, 12)

Obter detalhes da transação

Transactions contém tipos que fornecem dados sobre depósitos e levantamentos para contas. Estes tipos permitem-lhe aceder e gerir detalhes de transações para contas programaticamente.

Pode recuperar dados sobre o tipo de transação (depósito ou levantamento), montante, carimbos de data/hora, ID da transação, detalhes do saldo, propriedades do capital, etc.

Este cBot recupera e imprime detalhes da transação:

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

namespace cAlgo.Robots
{
    [Robot]
    public class TransactionsAPITEST : Robot
    {
        protected override void OnStart()
        {           
            foreach (var transaction in Transactions)
            {
                Print($"Transaction ID: {transaction.Id}, Amount: {transaction.Amount}, Type: {transaction.Type}, Date: {transaction.Time}");
            }
        }
    }
}
 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")

# Import cAlgo API types
from cAlgo.API import *
from cAlgo.API.Internals import *

# Import trading wrapper functions
from robot_wrapper import *

class TransactionsAPITEST():
    def on_start(self):
        for transaction in api.Transactions:
            api.Print(
                f"Transaction ID: {transaction.Id}, "
                f"Amount: {transaction.Amount}, "
                f"Type: {transaction.Type}, "
                f"Date: {transaction.Time}"
            )

Obter dados históricos de ordens

O cTrader fornece a coleção HistoricalOrder para permitir que os utilizadores recuperem informações sobre ordens, incluindo ordens de mercado concluídas e ordens pendentes que foram executadas ou canceladas. Os detalhes recuperáveis incluem o ID da ordem, o seu tipo, etiqueta, preço alvo, expiração, etc.

O código do cBot abaixo mostra-lhe como obter os detalhes das ordens históricas:

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

namespace cAlgo.Robots
{
    [Robot(AccessRights = AccessRights.None)]
    public class HistoricalOrdersExample : Robot
    {
        protected override void OnStart()
        {
            var historicalOrders = HistoricalOrders;
            foreach (var order in historicalOrders)
            {
                Print($"Order ID: {order.Id}");
                Print($"Type: {order.OrderType}");
                Print($"Volume: {order.VolumeInUnits}");
                Print($"Price: {order.TargetPrice}");
                Print($"Expiration: {order.ExpirationTime}");
                Print($"Comment: {order.Comment}");
                Print($"Label: {order.Label}");
                Print($"Stop Loss: {order.StopLoss}");
                Print($"Take Profit: {order.TakeProfit}");
                Print($"TradeType: {order.TradeType}");
                Print($"Quantity: {order.Quantity}");
                Print($"Label: {order.Label}");
            }
        }
    }
}

Usar dados históricos de negociações

Ao codificar cBots, tem acesso ao histórico da sua conta. Isto significa que pode iterar sobre as suas negociações passadas e usar os seus dados para quaisquer fins que tenha.

O snippet de código abaixo cria um ficheiro .CSV contendo todas as suas negociações passadas e os seus dados de negociação.

 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
using System;
using cAlgo.API;
using System.Text;
using System.IO;

namespace Samples
{
    /* We provide the access rights required for
    reading and writing in locally stored files. */
    [Robot(AccessRights = AccessRights.FullAccess)]
    public class Sample : Robot
    {
        protected override void OnStart()
        {
            var stringBuilder = new StringBuilder();

            _ = stringBuilder.AppendLine($"PositionId,TradeType,SymbolName,VolumeInUnits,EntryTime,EntryPrice,ClosingTime,ClosingPrice,NetProfit,Balance");

            // All trades are inside the 'History' collection
            foreach (var trade in History)
            {
                _ = stringBuilder.AppendLine($"{trade.PositionId},{trade.TradeType},{trade.SymbolName},{trade.VolumeInUnits},{trade.EntryTime:o},{trade.EntryPrice},{trade.ClosingTime:o},{trade.ClosingPrice},{trade.NetProfit},{trade.Balance}");
            }

            // We will save the CSV file on our desktop
            var desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);

            var filePath = Path.Combine(desktopPath, $"{Account.Number}_History.csv");

            File.WriteAllText(filePath, stringBuilder.ToString());
        }
    }
}

Obter informações sobre transações

No cTrader, as transações executam ordens e fazem com que as posições sejam abertas ou fechadas. Dependendo da liquidez do mercado, uma ordem pode ser preenchida inteiramente por uma única transação ou em partes por várias transações. Veja Posições e transações para saber mais.

O código do cBot abaixo mostra-lhe como recuperar informações sobre múltiplas transações que existem numa única posição:

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

namespace cAlgo.Robots
{
    [Robot(AccessRights = AccessRights.None)]
    public class DealsExample : Robot
    {
        protected override void OnStart()
        {
            var position = ExecuteMarketOrder(TradeType.Buy, "EURUSD", 10000);

            var deals = position.Position.Deals;

            foreach (var deal in deals)
            {
                Print(deal.ExecutionTime + " " + deal.Status + " " + deal.Id);
            }

            ClosePosition(position.Position, 3000);
            ClosePosition(position.Position, 7000);

            var closing_deals = History.FindByPositionId(position.Position.Id);

            foreach (var closing_deal in closing_deals)
            {
                Print(closing_deal.ClosingDeal.ExecutionTime + " " + closing_deal.ClosingDeal.VolumeInUnits);
            }
        }

        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
29
30
import clr

clr.AddReference("cAlgo.API")

# Import cAlgo API types
from cAlgo.API import *
from cAlgo.API.Internals import *

# Import trading wrapper functions
from robot_wrapper import *

class DealsExample():
    def on_start(self):
        position = api.ExecuteMarketOrder(TradeType.Buy, "EURUSD", 10000)

        deals = position.Position.Deals

        for deal in deals:
            api.Print(f"{deal.ExecutionTime} {deal.Status} {deal.Id}")

        api.ClosePosition(position.Position, 3000)
        api.ClosePosition(position.Position, 7000)

        closing_deals = api.History.FindByPositionId(position.Position.Id)

        for closing_deal in closing_deals:
            api.Print(f"{closing_deal.ClosingDeal.ExecutionTime} {closing_deal.ClosingDeal.VolumeInUnits}")

    def on_stop(self):
        pass

Converter lotes para unidades

Por padrão, os algoritmos do cTrader calculam o volume em unidades em vez de lotes. No entanto, em alguns casos, pode achar que trabalhar com lotes é mais conveniente e familiar.

Pode converter lotes para unidades usando o método Symbol.QuantityToVolumeUnits().

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

namespace Samples
{
    [Robot(AccessRights = AccessRights.None)]
    public class Sample : Robot
    {
        protected override void OnStart()
        {
            var lots = 2.5;

            var volumeInUnits = Symbol.QuantityToVolumeInUnits(lots);

            ExecuteMarketOrder(TradeType.Sell, SymbolName, volumeInUnits);
        }
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import clr

clr.AddReference("cAlgo.API")

# Import cAlgo API types
from cAlgo.API import *
from cAlgo.API.Internals import *

# Import trading wrapper functions
from robot_wrapper import *

class Sample():
    def on_start(self):
        lots = 2.5
        volume_in_units = api.Symbol.QuantityToVolumeInUnits(lots)
        api.ExecuteMarketOrder(TradeType.Sell, api.SymbolName, volume_in_units)

Alternativamente, pode usar o método Symbol.VolumeInUnitsToQuantity() para converter unidades para lotes.

Trabalhar com cores

Os cBots podem usar cores personalizadas ao realizar várias operações (por exemplo, ao exibir texto nos gráficos de negociação). Para adicionar cor como um parâmetro personalizável, adicione o seguinte código às suas declarações de parâmetros.

1
2
[Parameter("Color", DefaultValue = "#f54242")]
public Color DrawingColor { get; set; }

Nota

Pode usar tanto códigos de cor hexadecimais como nomes de cores (como red") ao definir o DefaultValue de tal parâmetro."

Para ilustrar como funcionam os parâmetros de cor, criaremos um cBot simples que, ao ser iniciado, escreverá texto no gráfico de negociação no qual está a operar. A cor deste texto será personalizável através do parâmetro TextColor.

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

namespace cAlgo.Robots
{
    [Robot(AccessRights = AccessRights.None)]
    public class SampleColorcBot : Robot
    {
        [Parameter("Text Color", DefaultValue = "red")]
        public Color TextColor { get; set; }

        protected override void OnStart()
        {
            var staticText = Chart.DrawStaticText("static", "Sample text to demonstrate how color works", VerticalAlignment.Center, HorizontalAlignment.Center, TextColor);

        }

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

# Import cAlgo API types
from cAlgo.API import *
from cAlgo.API.Internals import *

# Import trading wrapper functions
from robot_wrapper import *

class SampleColorcBot():
    def on_start(self):
        api.Chart.DrawStaticText(
            "static",
            "Sample text to demonstrate how color works",
            VerticalAlignment.Center,
            HorizontalAlignment.Center,
            api.TextColor
        )