C# para negociação algorítmica
O que é C#
C# é uma linguagem de programação orientada a objetos usada para criar uma ampla gama de serviços e aplicações. A sua sintaxe clara e estruturada torna-a fácil de aprender e ler, mesmo para iniciantes, ao mesmo tempo que oferece uma abordagem estruturada para escrever código compacto e reutilizável.
Algoritmos C# em um minuto!
- Mesmo que nunca tenha trabalhado com C# antes, criar e implementar o seu primeiro robô ou indicador leva apenas alguns minutos.
- Como o C# é fácil de usar, pode rapidamente reescrever quaisquer trechos de código desta documentação para atender às suas necessidades.
- Ao usar C#, pode aceder a um grande número de bibliotecas contendo classes e métodos predefinidos. Estes métodos podem lidar eficientemente com tarefas comuns, deixando-o livre para resolver problemas complexos de negociação.
- Em C#, pode escrever código que não bloqueia os threads do servidor durante a execução. Por outras palavras, pode iniciar certas tarefas simultaneamente com outras tarefas.
O que é o .NET?
Para que os programas C# sejam executados, o seu código-fonte deve ser compilado em Linguagem Intermédia (IL). Este código IL segue a especificação Common Language Infrastructure (CLI) e é posteriormente compilado em instruções nativas da máquina em tempo de execução. A framework .NET fornece um ambiente de execução virtual construído sobre extensas bibliotecas de classes e o Common Language Runtime (CLR), a implementação da Microsoft do CLI.
Sem entrar em detalhes técnicos, o .NET cumpre as seguintes funções:
- Facilitar o desenvolvimento e execução de aplicações: O SDK .NET já contém vários compiladores e motores de compilação integrados, eliminando a necessidade de criar soluções personalizadas.
- Fornecer bibliotecas de tempo de execução: Ao adicionar novos tipos de dados ou coleções ao seu código, frequentemente descobrirá que o .NET já oferece classes e métodos adequados para a tarefa.
Os fundamentos do C#
Tipos de dados e declarações de variáveis
Os tipos de dados são uma forma de categorizar dados para que o C# saiba exatamente como tratar variáveis e propriedades. Nas declarações de variáveis/propriedades, os tipos de dados sempre precedem o nome da variável/propriedade.
1 | |
Alternativamente, pode usar a palavra-chave var para evitar especificar um tipo de dados.
1 | |
Objetos e classes
Pense nos objetos como abstrações de entidades tangíveis ou intangíveis. Estas entidades podem ter certas características (propriedades) e podem realizar várias operações (métodos). Por sua vez, as classes servem como modelos para a criação de objetos.
Como o C# é uma linguagem orientada a objetos, as classes também podem herdar propriedades e métodos de outras classes. Considere o seguinte exemplo no qual declaramos uma nova classe NewBot que herda da classe Robot. Também definimos algumas novas propriedades de classe.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
Tipos de dados
Como o C# é uma linguagem fortemente tipada, é necessário especificar tipos de dados ao declarar variáveis e propriedades de classe. Em resumo, os tipos de dados constituem classes únicas com diferentes conjuntos de comportamentos. Considere os seguintes exemplos nos quais vários tipos de dados são especificados para diferentes propriedades de classe e parâmetros relacionados.
1 2 | |
No código acima, o tipo de dados DataSeries representa uma lista somente leitura de valores e, portanto, é tipicamente usado para representar preços de mercado.
Quando há necessidade de criar parâmetros com múltiplas opções, pode usar o tipo de dados integrado enum como detalhado abaixo. Pode pensar nos enum como classes especiais que contêm várias constantes.
1 2 3 4 5 6 7 8 9 | |
Para aceder a uma constante contida num enum, use a seguinte sintaxe.
1 | |
Se um parâmetro ou uma propriedade precisar de ser um número, na maioria das vezes usará os tipos de dados int (inteiro) ou double (decimal). Embora o tipo double tenha menos precisão que o tipo decimal, também consome menos recursos.
1 2 | |
Por último, o tipo bool tipicamente representa uma entrada sim ou não. Os valores sim e não correspondem a true e false, respetivamente.
1 2 | |
Namespaces
Os namespaces atuam como coleções designadas de classes. Uma vez que atribua uma classe a um namespace, ela pode ser acedida posteriormente usando a notação Namespace.NomeDaClasse. Vamos atribuir o nosso NewBot ao namespace CoolTradingBots.
1 2 3 4 5 6 7 8 | |
Métodos de classe
Os métodos de classe são definidos após a declaração da classe. Todos os objetos da nossa classe NewBot poderão chamar o método CustomTradeOperation(). Ele recebe dois argumentos, nomeadamente um objeto Symbol e um objeto double. O nosso método deve receber um símbolo e executar algum tipo de ação de negociação para o volume especificado
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
Bibliotecas de classes
As bibliotecas de classes (e, subsequentemente, os métodos de classe) podem ser acedidas pelo seu namespace, como mostrado no exemplo abaixo.
1 | |
Alternativamente, pode digitar a palavra-chave using no início do seu código para designar certos namespaces e evitar redundâncias. Considere o seguinte trecho de código.
1 2 3 | |
Declarações condicionais
Para usar declarações condicionais, use uma palavra-chave seguida de uma expressão entre parênteses. O exemplo abaixo usa a palavra-chave if para finalizar o nosso método CustomTradingOperation(). Para isso, usamos o método EvaluateMarket() que é definido no namespace Analytics.Actions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
Coleções
As coleções são definidas como contentores que podem armazenar um ou mais objetos de uma classe particular.
As coleções são totalmente indexáveis, o que significa que os seus membros podem ser acedidos passando um certo valor integer entre parênteses retos. Considere o seguinte exemplo no qual o método Calculate() imprime as propriedades de uma barra acedida através do seu índice.
1 2 3 4 5 | |
Criar extensões cTrader
No trecho abaixo, criamos um robô de negociação básico usando apenas o conhecimento abordado nas secções acima.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | |
Operações síncronas e assíncronas
Como mencionado anteriormente, o C# suporta totalmente operações assíncronas. O diagrama abaixo define um exemplo básico de como as atividades de negociação são realizadas na execução síncrona.
graph TD
A(Encontrar um Sinal Técnico) ==> B(Executar uma Ordem <br>de Mercado)
B ==> C(Take Profit/Stop Loss <br>Atingido)
C ==> D(Fechar a Posição)
D ==> A A execução síncrona tem uma desvantagem importante. No exemplo acima, a ação de executar uma ordem de mercado ocupa totalmente todas as threads do servidor, o que significa que o seu robô de negociação não pode fazer mais nada antes que esta operação seja concluída.
Isto é menos que ideal quando se quer reagir rapidamente a eventos de mercado e volatilidade. Idealmente, o seu bot deveria ser capaz de armazenar várias ações ou colocar certas tarefas em espera para se envolver em outras atividades mais urgentes. Vamos expandir o nosso exemplo para refletir melhor como as operações assíncronas são realizadas.
graph TD
A([Encontrar um Sinal Técnico]) ==> B([Colocar uma Ordem <br>de Compra]) & C([Colocar uma Ordem de<br> Venda para Cobertura]) ==> D([Uma Ordem Atinge o Seu <br>Take Profit/Stop Loss])
D ==> E([Fechar a Posição])
E ==> A No exemplo acima, o nosso robô de negociação colocou simultaneamente ordens em ambas as direções para fazer cobertura das suas posições. Em contraste com as operações síncronas, não há necessidade de esperar que uma operação seja concluída antes de prosseguir com outra. Isto expande significativamente as oportunidades dos programadores para criar robôs de negociação eficientes e confiáveis.
Note que a execução assíncrona é diferente da multi-threading:
- Na execução assíncrona, todas as tarefas são iniciadas na mesma thread. Quando são armazenadas ou colocadas em espera, libertam esta thread e, quando a sua execução continua mais tarde, ocorre numa thread diferente escolhida do pool de threads.
- No multi-threading, todas as tarefas começam em threads diferentes e continuam a sua execução nas suas threads iniciais. Não há troca de threads.
O cTrader nunca invoca os seus métodos em paralelo, por isso não precisa de se preocupar com questões de multi-threading.
