search Ничего не найдено
Главная Документация по алготрейдингу С чего начать?

Создаем робота

Введение

Для создания торгового робота нам необходимо иметь торговую стратегию, которую мы хотим автоматизировать.

Напишем небольшое техническое задание в котором определим все условия открытия и закрытия позиций.

Для примера возьмем простую торговую стратегию по паттерну "Молот" ("Перевернутый молот").

Визуально данный паттерн напоминает молоток висящий на зажимах рукояткой вниз: нижняя тень ― ручка, тело ― ударная часть, верхняя тень ― выступающее из ударной части окончание рукоятки.

Общие условия:

В рынке не должно быть открытых позиций.


Условия для продажи:

  1. high2>high3
  2. high1>high2
  3. close1<open1
  4. ((open1-low1)/(high1-low1))<=F(%),

где F это критерий качества свечи "молот"


Условия для покупки

  1. low2<high3
  2. high1<high2
  3. close1>open1
  4. ((high1-open1)/(high1-low1))<=F(%), 

где F это критерий качества свечи "молот"


Создаем нового робота

Введите имя робота и нажмите кнопку "Создать"

Программа создаст стандартный шаблон с тремя функциями обработки событий: OnStart(), OnTick() и OnStop()


Входные параметры

Переходим к созданию внешних переменных, чтобы у пользователя была возможность задать свои параметры работы робота.

  • Lot — торговый лот, с которым будет открываться позиция. По умолчанию задаем его минимальным. Тип переменной будет double, т.к. это лот не может быть целым числом.
  • Stop Loss — Стоп Лосс (ограничение убытка) для каждой позиции робота. По умолчанию задается в пунктах. Тип переменной будет int т.к. это целое число.
  • Take Profit — Тейк Профит нужен для закрытия позиции при достижении заданной прибыли. По умолчанию задается в пунктах. Тип переменной будет int т.к. это целое число.
  • Parameter F(%) — Параметр качаства свечи "Молот". Тип переменной будет int т.к. это целое число.

Первые три параметра являются необходимыми даже для самого простого робота. Чтобы их можно было видеть при настройке робота как входные параметры, воспользуемся модификатором public, который определит наши переменные как внешние. Данный блок кода прописывается в самом начале редактора.

using System;
using System.Runtime.InteropServices;

namespace uAlgo.Bots
{
public class NewBot : Bot
{
// Входные параметры
//Lot
[Parameter("Lot", MinValue = 0.1, MaxValue = 50)]
public double Lot = 0.1;
// StopLoss
[Parameter("StopLoss (pips)", MinValue = 1, MaxValue = 1000)]
public int StopLossPips = 300;
// TakeProfit
[Parameter("TakeProfit (pips)", MinValue = 1, MaxValue = 1000)]
public int TakeProfitPips = 300;
// критерий качества фигуры "молот"
[Parameter("Parameter F (%)", MinValue = 1, MaxValue = 50)]
public int F = 50;



public void OnTick()
{
// Put your core logic here
}

public void OnStart()
{
// Put your initialization logic here
}

public void OnStop()
{
// Put your deinitialization logic here
}
}
}


Создадим пользовательскую функцию GetSignal()

Условия для входа в рынок определены в постановке и теперь необходимо объяснить торговому роботу. Данная функция будет проверять условия и возвращать нам сигнал на "покупку" или "продажу".

private string GetSignal()
{
string signal = "";
int err;
Bar[] rates;

err = Bars.Select(Symbol.SymbolName, TimeFrame.Current, 4, out rates);
// Условия для продажи (Восходящий тренд.)
if (rates[3].High < rates[2].High) {
if (rates[2].High < rates[1].High) {
if (rates[1].Close < rates[1].Open && (rates[1].High - rates[1].Low) > 0) {
if ((((rates[1].Open - rates[1].Low) / (rates[1].High - rates[1].Low)) * 100) <= F) {
signal = "sell";
}
}
}
}
// Условия для покупки (Нисходящий тренд.)
if (rates[3].Low > rates[2].Low) {
if (rates[2].Low > rates[1].Low) {
if (rates[1].Close >= rates[1].Open && (rates[1].High - rates[1].Low) > 0) {
if ((((rates[1].High - rates[1].Open) / (rates[1].High - rates[1].Low)) * 100) <= F) {
signal = "buy";
}
}
}
}
return(signal);
}


Мы дошли до функции OnTick(). В ней дальнейший код будет совершать свой цикл каждый тик. В соответствии с нашей постановкой вначале мы должны проверить, что в рынке нет открытых позиций у данного робота по текущему символу. Для этого напишем пользовательскую функцию GetPositionCount(), которая будет нам возвращать количество открытых позиций.


private int GetPositionCount()
{
int poc = 0;
for (var i = Positions.Count - 1; i >= 0; i--) {
Position pos = Positions.Find(Positions[i].PositionId);
if (pos != null) {
if (pos.SymbolName == Symbol.SymbolName) {
if ((pos.PositionType == (int)PositionType.Buy || pos.PositionType == (int)PositionType.Sell)) {
poc++;
}
}
}
}
return(poc);
}


В OnTick() мы будем вызывать функцию GetSignal() и открывать позиции на покупку или продажу.

Выглядеть это будет так:

public void OnTick()
{
int err;
int id;
if (GetPositionCount() == 0) {
if (GetSignal() == "buy") {
// покупка
err = Positions.Open(Symbol.SymbolName, PositionType.Buy, Lot, 0, 0, TakeProfitPips, StopLossPips, "hammer", out id);
if (err > 0) {
Print("log.txt", "error: " + err.ToString());
}
}
if (GetSignal() == "sell") {
// продажа
err = Positions.Open(Symbol.SymbolName, PositionType.Sell, Lot, 0, 0, TakeProfitPips, StopLossPips, "hammer", out id);
if (err > 0) {
Print("log.txt", "error: " + err.ToString());
}
}
}
}


Проверка ошибок торговых запросов

При отправке запроса на сервер рекомендуется проверять торговые запросы на ошибки, для этого в переменную err мы будем записывать код ошибки при неудачном запросе. Создадим функцию Print которая будет записывать ошибки в наш журнал. В функцию будем передавать 2 параметра (имя файла для логирования информации и строку для записи в файл)

private void Print(string f, string l)
{
// получаем путь к папке с нашим роботом
string AssemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + Path.DirectorySeparatorChar.ToString();
// полный путь к файлу для записи служебной информации
string mLog = AssemblyPath + f;

using (StreamWriter sw = new StreamWriter(mLog, true))
sw.WriteLine(l);
}


Собрав все описанное выше вместе у нас получается готовый робот, который будет торговать по нашей торговой стратегии

using System;
using System.Runtime.InteropServices;
using TraderAPI;
using System.IO;
using System.Reflection;

namespace uAlgo.Bots
{
public class NewBot : Bot
{
// Входные параметры
//Lot
[Parameter("Lot", MinValue = 0.1, MaxValue = 50)]
public double Lot = 0.1;
// StopLoss
[Parameter("StopLoss (pips)", MinValue = 1, MaxValue = 1000)]
public int StopLossPips = 300;
// TakeProfit
[Parameter("TakeProfit (pips)", MinValue = 1, MaxValue = 1000)]
public int TakeProfitPips = 300;
// критерий качества фигуры "молот"
[Parameter("Parameter F (%)", MinValue = 1, MaxValue = 50)]
public int F = 50;

//-------------------------------------
//
//-------------------------------------
private string GetSignal()
{
string signal = "";
int err;
Bar[] rates;

err = Bars.Select(Symbol.SymbolName, TimeFrame.Current, 4, out rates);
// Условия для продажи (Восходящий тренд.)
if (rates[3].High < rates[2].High) {
if (rates[2].High < rates[1].High) {
if (rates[1].Close < rates[1].Open && (rates[1].High - rates[1].Low) > 0) {
if ((((rates[1].Open - rates[1].Low) / (rates[1].High - rates[1].Low)) * 100) <= F) {
signal = "sell";
}
}
}
}
// Условия для покупки (Нисходящий тренд.)
if (rates[3].Low > rates[2].Low) {
if (rates[2].Low > rates[1].Low) {
if (rates[1].Close >= rates[1].Open && (rates[1].High - rates[1].Low) > 0) {
if ((((rates[1].High - rates[1].Open) / (rates[1].High - rates[1].Low)) * 100) <= F) {
signal = "buy";
}
}
}
}
return(signal);
}
//-------------------------------------
//
//-------------------------------------
private int GetPositionCount()
{
int poc = 0;
for (var i = Positions.Count - 1; i >= 0; i--) {
Position pos = Positions.Find(Positions[i].PositionId);
if (pos != null) {
if (pos.SymbolName == Symbol.SymbolName) {
if ((pos.PositionType == (int)PositionType.Buy || pos.PositionType == (int)PositionType.Sell)) {
poc++;
}
}
}
}
return(poc);
}

//-------------------------------------
//
//-------------------------------------
private void Print(string f, string l)
{
// получаем путь к папке с нашим роботом
string AssemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + Path.DirectorySeparatorChar.ToString();
// полный путь к файлу для записи служебной информации
string mLog = AssemblyPath + f;

using (StreamWriter sw = new StreamWriter(mLog, true))
sw.WriteLine(l);
}

public void OnTick()
{
int err;
int id;
if (GetPositionCount() == 0) {
if (GetSignal() == "buy") {
// покупка
err = Positions.Open(Symbol.SymbolName, PositionType.Buy, Lot, 0, 0, TakeProfitPips, StopLossPips, "hammer", out id);
if (err > 0) {
Print("log.txt", "error: " + err.ToString());
}
}
if (GetSignal() == "sell") {
// продажа
err = Positions.Open(Symbol.SymbolName, PositionType.Sell, Lot, 0, 0, TakeProfitPips, StopLossPips, "hammer", out id);
if (err > 0) {
Print("log.txt", "error: " + err.ToString());
}
}
}
}

public void OnStart()
{
// Put your initialization logic here
}

public void OnStop()
{
// Put your deinitialization logic here
}
}
}


Перед тем как вы сможете использовать своего робота в торговле Вам необходимо скомпилировать ваш исходный код в исполняемый. Для этого нажмите кнопку "Собрать" и смотрите в окно результата сборки. Если в коде нет никаких ошибок, то вы увидите сообщение "Build succeeded", это означает что робот был успешно собран и готов для торговли.