Финансовая лаборатория

Биржевая торговля и торговые роботы

Доброго времени суток!

На нашем последнем уроке мы рассмотрим свойства и метода отвечающие за отображение информации в программе FinaLab.MTS.

Начнем с заполнения свойств TargetPriceBuy и TargetPriceSell. Эти методы показывают текущую цену по которой вы будете выставлять заявку(конечно же вы можете вывести в эти поля что захотите, хоть загружать результаты последнего матча любимой футбольной команды). В данном примере мы выставляем заявку по ценам Bid  и Ask соответственно, поэтому мы можем напрямую их выводить. Или же если наша программа рассчитывает цену которая будет в будущем, то мы можем сделать так: создать переменные, например PriceBuy и PriceSell и  присвоить им рассчитанные цены, а потом вывести их через рассматриваемые сейчас свойства(TargetPriceBuy и TargetPriceSell) чтобы вы могли контролировать работу программы.

1. На этом шаге мы сделаем так чтобы в ячейках программы TargetPriceSell и TargetPriceBuy выводились цены по которым мы собираемся выставлять заявки. В данном примере это, как я уже сказал, цена Bid и цена Ask(Рис1).

Рис1 - Методы TargetPriceBuy и TargetPriceSell

Рис1 - Методы TargetPriceBuy и TargetPriceSell

2. Далее мы рассмотрим заполнение метода, который по умолчанию выводит среднюю цену в стакане(напоминаю, использовать мы его можем для вывода любой необходимой нам информации)(Рис2).

Рис2 - Заполнение метода AveragePrice

Рис2 - Заполнение метода AveragePrice

3. В следующих методах следует указать количество лотов необходимые для открытия или закрытия позиции(Рис3).

Рис3 - Методы CurrentLotSizeBuy/Sell

Рис3 - Методы CurrentLotSizeBuy/Sell

4. Теперь мы подошли к рассмотрению, на мой взгляд, самого полезного инструмента вывода информации через системы FinLab.MTS - инструмент который может выводить практически любое  количество переменных.

Для начала мы переопределим метода CreateInformer(), в котором через экземпляр конструктора следует указать количество создаваемых ячеек и их заголовки, в виде массива строк(Рис4, Рис5).

Рис4 - Переопределяем метод CreateInformer(), который на данном рисунке возвращает базовую реализацию.

Рис4 - Переопределяем метод CreateInformer(), который на данном рисунке возвращает базовую реализацию.

Рис4 - Переопределяем метод CreateInformer(), который на данном рисунке возвращает базовую реализацию.

Рис5 - Метод CreateInformer(), в котором создадим 2 ячейки (Продажа, Покупка)

5. Переопределим метод OnUpdateStakan, который вызыается при каждом обновлении стакана(первой ноги или второй, если таковая имеется) и передает нам интерфейс IStakan для каждой ноги через который можно получить нужную нам информацию, в том числе Bid И Ask(Рис 6).

Рис6 - Метод OnUpdateStakan реализованный по умолчанию.

Рис6 - Метод OnUpdateStakan реализованный по умолчанию.

6. Создадим метод, в котором будем задавать цвет ячеек Informer'a и собственно передавать сами значения(Рис7).

Рис7 - Создаем метод Info(можете назвать его по своему)

Рис7 - Создаем метод Info(можете назвать его по своему)

7. По указанному ниже примеру зададим цвета наших ячеек(Рис8).

Рис8 - Задаем цвета наших ячеек

Рис8 - Задаем цвета наших ячеек

8. Зададим значения, которые будут выводиться в ячейках(Рис9).

Рис9 - Задаем значения для вывода

Рис9 - Задаем значения для вывода

9.  Добавляем созданный нами метод Info в OnUpdateStakan, так как на м нужно чтобы каждое обновление стакана вызывался метод Info(Рис10).

Рис10 - Добавляем метод Info в метод OnUpdateStakan

Рис10 - Добавляем метод Info в метод OnUpdateStakan

Вот собственно и все. На этом я позволю себе закончить. Успехов вам в написание СВОИХ торговых роботов, а ГЛАВНОЕ чтобы заложенные в них стратегии были прибыльными! Весь исходный код нашей программы выглядит так.

Код программы:


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using FinLab.MTS.Infinity;
using FinLab.TradeBase;
using System.Drawing;
using System.Windows.Forms;

namespace ClassLibrary1
{
    public class Class1:Algorithm
    {
        public Class1()
        { }

        private enum Regim
        {
            Buy,
            Sell,
            Close,
            No
        }
        private Regim _Regim = Regim.No;

        public int Lot_Size { get; set; }

        private void CreateOrder()
        {
            if (Futures.Ask > Futures.PrevAsk)//Если текущий Ask больше предыдущего, то даем сигнал на покупку
                _Regim = Regim.Buy;
            if (Futures.Bid < Futures.PrevBid)//Если текущий Bid меньше предыдущего, то даем сигнал на продажу
                _Regim = Regim.Sell;
        }

        public override AddCommand CheckBuyDirection(IStakan futures, IStakan spot)
        {
            double res = AverageOpenedSpread - futures.Bid;
            if (OpenedPosition < 0)
                if (res < 0 && Math.Abs(res) > 250)
                    return new AddCommand(futures.SecBoard, futures.SecCode, "", "B", "L", -OpenedPosition, futures.GetBasisBidPrice(-OpenedPosition, 3));

            if (OpenedPosition>=0)
            {
                if (_Regim == Regim.Buy)
                {
                    _Regim = Regim.No;
                    return new AddCommand(futures.SecBoard, futures.SecCode, "", "B", "L", Lot_Size, futures.Bid);
                }
            }
            else
            {
                if (AverageOpenedSpread - futures.Bid >= 100)
                {

                    return new AddCommand(futures.SecBoard, futures.SecCode, "", "B", "L", Math.Min(Lot_Size, -OpenedPosition), futures.Bid);
                }
            }
            return null;
        }

        public override AddCommand CheckSellDirection(IStakan futures, IStakan spot)
        {
            double res = futures.Ask - AverageOpenedSpread;
            if (OpenedPosition < 0)
                if (res < 0 && Math.Abs(res) > 250)
                    return new AddCommand(futures.SecBoard, futures.SecCode, "", "S", "L", OpenedPosition, futures.GetBasisAskPrice(OpenedPosition, 3));

            if (OpenedPosition <= 0)
            {
                if (_Regim == Regim.Sell)
                {
                    _Regim = Regim.No;
                    return new AddCommand(futures.SecBoard, futures.SecCode, "", "S", "L", Lot_Size, futures.Ask);
                }

            }
            else
            {
                if (futures.Ask - AverageOpenedSpread >= 100)
                {
                    return new AddCommand(futures.SecBoard, futures.SecCode, "", "S", "L", Math.Min(Lot_Size, OpenedPosition), futures.Ask);
                }
            }
            return null;
        }

        public override bool DeleteOrderBuy(AddCommand addCommand, IStakan futures, IStakan spot)
        {
            if (OpenedPosition>0)
            {
                //В данном примеры здесь ничего не делаем

            }
            else
            {
                double res = AverageOpenedSpread - futures.Bid;
                    if (res < 0 && Math.Abs(res) > 250)
                        return true;
            }
            return false;
        }

        public override bool DeleteOrderSell(AddCommand addCommand, IStakan futures, IStakan spot)
        {
            if (OpenedPosition < 0)
            {
                //В данном примеры здесь ничего не делаем

            }
            else
            {
                double res = futures.Ask - AverageOpenedSpread;
                    if (res < 0 && Math.Abs(res) > 250)
                        return true;
            }
            return false;
        }

        public override bool Initialize()
        {
            return true;
        }

        public override void Dispose()
        {

        }

        public override ObjectAssociatedControl CreateControl()
        {
            UserControl1 usc = new UserControl1(this);
            return usc;
        }

        public override double TargetPriceBuy
        {
            get { return Futures.Bid;}
        }

        public override double TargetPriceSell
        {
            get { return Futures.Ask;}
        }

        public override double AveragePrice
        {
            get { return RoundPrice((Futures.Ask + Futures.Bid) / 2); }
        }

        public override int CurrentLotSizeBuy
        {
            get { return OpenedPosition >= 0 ? Lot_Size : Math.Min(Lot_Size, -this.OpenedPosition); }
        }

        public override int CurrentLotSizeSell
        {
            get { return this.OpenedPosition <= 0 ? Lot_Size : Math.Min(Lot_Size, this.OpenedPosition); }
        }

        protected override Informer CreateInformer()
        {
            return new Informer(2,new string[]{"Продажа","Покупка"});
        }

        public override void OnTrade(Trade trade)
        {
            /*
                         Здесь можно получить информацию о сделке, но пока нам это не нужно,
                         но так как это abstract метод, мы обязаны его определить.
                     */
        }

        public override void OnUpdateStakan(IStakan futures, IStakan spot)
        {
            Info();
        }

        public void Info()
        {
            this.Informer.Colors = new System.Drawing.Color[] { System.Drawing.Color.IndianRed, System.Drawing.Color.GreenYellow };
            this.Informer.Values = new object[] { Futures.Ask, Futures.Bid};
        }
    }
}

    public partial class UserControl1 : ObjectAssociatedControl
    {
        private Class1 Cla;
        public UserControl1(Class1 state)
        {
            Cla = state;
            InitializeComponent();
            numericUpDown1.Value = (decimal)Cla.Lot_Size;
        }

        public override void ApplyChanges()
        {
            Cla.Lot_Size = (int)numericUpDown1.Value;
        }
    }

    partial class UserControl1
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Component Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.numericUpDown1 = new System.Windows.Forms.NumericUpDown();
            this.label1 = new System.Windows.Forms.Label();
            this.groupBox1 = new System.Windows.Forms.GroupBox();
            ((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).BeginInit();
            this.SuspendLayout();
            //
            // numericUpDown1
            //
            this.numericUpDown1.Location = new System.Drawing.Point(85, 8);
            this.numericUpDown1.Name = "numericUpDown1";
            this.numericUpDown1.Size = new System.Drawing.Size(45, 20);
            this.numericUpDown1.TabIndex = 0;
            //
            // label1
            //
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(3, 10);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(76, 13);
            this.label1.TabIndex = 1;
            this.label1.Text = "Кол-ва лотов:";
            //
            // groupBox1
            //
            this.groupBox1.Location = new System.Drawing.Point(3, 34);
            this.groupBox1.Name = "groupBox1";
            this.groupBox1.Size = new System.Drawing.Size(229, 186);
            this.groupBox1.TabIndex = 2;
            this.groupBox1.TabStop = false;
            this.groupBox1.Text = "Другие настройки:";
            //
            // UserControl1
            //
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.Controls.Add(this.groupBox1);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.numericUpDown1);
            this.Name = "UserControl1";
            this.Size = new System.Drawing.Size(235, 234);
            ((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.NumericUpDown numericUpDown1;
        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.GroupBox groupBox1;
    }

P.S. Напоминаю описание API для FinLab.MTS можно скачать здесь!

Так же вы можете посмотреть предыдущие 3 урока:

Рубрики: FinLab

Комментариев: 13

  1. jfjfjfjf пишет:

    При компиляции пишет: “Проект, создающий библиотеку классов, не может быть непосредственно запущен.
    Для отладки данного проекта добавьте к решению проект создающий приложение и ссылающийся на эту библиотеку и сделайте его запускаемым.”

    Поясните, пожалуйста, по шагам, что здесь сделать надо…

  2. korshun пишет:

    Доброе утро!
    Все правильно он пишет. Нужно нажимать построить (F6), а не запустить (F5). Так как это dll библиотека и сама она не может быть запущена. Она может использоваться другим приложением, например WindowsForm, а вот WindowsForm сам может быть запущен. В нашем примере он будет запускаться FinLab.PairTrade, нажав добавить пользовательскую задачу и выбрав свой проект(если он лежит не в корне программы, то нужно будет указать где dll-ка лежит)запустить его.

  3. Rucobor пишет:

    Продолжение будет? Как связать, например, пользовательскую форму с параметрами бота, которые нужно регулировать.

  4. Юрий пишет:

    Правильно ли я понимаю:

    1) Такой робот запускается на моей Windows машине с .NET, обращается к брокеру, при этом между мной и брокером будет довольно большая задержка (секунды).
    2) От брокера до биржи будет еще задержка сколько-то секунд или милисекунд.

    Если я все так – можно ли таким роботом скальпировать?

    3) Можно ли располагать роботов максимально близко к бирже, например, у брокеров?

    Не пинайте ногами, я новичек, только разбираюсь с вопросом :)

    • korshun пишет:

      Юрий,добрый день.
      Схему вы описали правильно(там еще можно добавить время на обработку вашей заявки ПО биржи). Такая задержка про которую вы пишете бывает, но только на очень загруженных серверах брокера, при плохом соединении и при слабом компьютере, который будет долго обрабатывать ваши запросы.
      На самом же деле заявка появляется в стакане в течении секунды(а на плазе еще быстрее). Да, конечно можно делать и скальперских роботов, и таковых не мало участвует в ЛЧИ.
      Располагать то можно, если конечно брокер будет не против и выделит вам место))

  5. Роман пишет:

    Приветствую!
    Очень хотелось бы увидеть пример написания “метода” для “заготовок”, хотя бы простейшего, и желательно “на пальцах” что бы понять где и на какие кнопки нажимать. На мой взгляд, существующих методов маловато.
    С уважением, Роман.

  6. Андрей пишет:

    на строке: return new AddCommand(futures.SecBoard, futures.SecCode, “”, “B”, “L”, -OpenedPosition, futures.GetBasisBidPrice(-OpenedPosition, 3));

    Ошибка: “FinLab.TradeBase.AddCommand” не содержит конструктор, который принимает аргументы “7” ClassLibrary1

    Как ее устранить?

  7. Андрей пишет:

    и Не хватает 5 -го урока, где этот код подключаем в Фин лаб Паир трейд и его там запускаем и настраиваем.

  8. Евгений пишет:

    Как связать робота с данными от индикаторов, поступающих на графики Алор-Трейда ? Хорошо бы прописать в C# пример получения информации о 2 -х ЕМА с изменяемым периодом для дальнейшего анализа в алгоритме торговли.По аналогии можно будет увязать и с другими “индюками”.

  9. Евгений пишет:

    После проделанного при открытии конструктора( построить F6) вылетает ошибка : Ошибка – Не удалось найти имя типа или пространства имен “Class1″ (пропущена директива using или ссылка на сборку?). Подскажите где чего искать . Пытался просто сохранить проект. Потом хотел открыть библиотеку ClassLibrary1.dll из FinLab.PairTrade – он её не видит в упор и не добавляет в окно. Полскажите ПЛИЗ ,где соьака зарыта?

  10. Евгений пишет:

    «Спасибо», что никто не ответил , т.к. это заставило самому во всём разобраться – «Class1.» выдавал ошибку т.к. эта часть кода пишется на другой странице кода (UserContrul1.cs) , а поначалу я всё в Class1.cs прописывал. А насчет того что FinLab.PairTrade не видит библиотеки – вот тут я помучился . Оказалось всё просто – я создавал библиотеку с использованием 4 .NET. а не 3.5.NET.( о чём написано в 1 уроке). Как всегда – ВСЁ ИМЕЕТ ЗНАЧЕНИЕ ! Так что будьте внимательны !!!

  11. Вячеслав пишет:

    А можно эти статьи оформить для скачивания в виде нормального файла как пример создания стратегии на этом продукте?

Оставить комментарий