Робострой - пишемо торгового робота на c #

Останнім часом все частіше чую від багатьох трейдерів заяви, що дуже здорово знати мову програмування і самому писати роботів. Багато посилено намагаються вивчати модний останнім часом мова C #. Однак новачкові з нуля написати якесь вартісне додаток буде досить складно. У цій статті я спробую дати мінімальні знання мови програмування, показати логіку побудови програми, спроектувати і запустити торгового робота для терміналу QUIK.

На початку подивимося, які кошти представлені зараз, для розробки роботів і створення автоматизованих алгоритмічних торгових систем:

1) Інструменти вбудовані в термінал

  1. Qpile - вбудовану мову в найбільш популярний термінал для торгівлі QUIK, мова досить просто. Однак у цієї простоти крім очевидного плюса в легкому освоєнні, є очевидні мінуси, як обмежений функціонал, складність налагодження.
  2. LUA - ще один нескладний мову для терміналу QUIK. Має більший функціонал, ніж Qpile. Однак більшу популярність ще не придбав. Сам код працює безпосередньо в терміналі QUIK.

2) Зовнішні коробкові продукти - дуже схожі по функціоналу: можливість тестування на історичних даних, написання торгових систем на мові платформи .NET (зазвичай C #), інтеграція з торговим терміналом.

3) Зовнішні програми, розроблені самостійно. В даному випадку можна використовувати будь-яку мову програмування, який знає людина. Кілька років тому була популярна зв'язка терміналу QUIK і Excel з роботом, написаним на Visual Basic. На даний момент можна скористатися досить потужною і гнучкою бібліотекою для написання роботів Stock #, проте не програмісту буде досить складно в ній розібратися.

Для розробки свого робота «з нуля» визначимося зі списком завдань:

1) Реалізація імпорту даних з терміналу QUIK в нашу програму

2) Програмування торгового алгоритму

3) Створення механізму відправки заявок на здійснення торгівельних операцій в термінал

Цей список можна розбити на безліч підпунктів, які ми будемо формулювати по ходу реалізації цих етапів.

Перш ніж почати реалізовувати імпорт даних з терміналу QUIK в нашу програму, згадаємо основи програмування. Щоб не перетворювати статтю в черговий нудний підручник з програмування, опишемо основні конструкції мови, які нам знадобляться тільки для розробки програми.

У C # є два способи побудови інтерфейсів клієнтських додатків. Перший - використовувати Windows Forms - технологія побудови додатків, яка вже понад 10 років застосовується при проектуванні візуального інтерфейсу. Другий - Windows Presentation Foundation (WPF) - досить нова система для побудови інтерфейсу, має можливість створювати привабливий елементи з використанням 3D-графіки, використовує MVC-підхід, однак вимагає великих ресурсів комп'ютера, ніж WinForms. Оскільки Windows Forms простіше і менш вимогливий до ресурсів, ми будемо використовувати саме цю технологію.

Мал. 1. створення додатка Windows Forms

Після створення ми бачимо Форму - на ній будуть розміщуватися візуальні елементи і дерево файлів, включаючи файл з кодом (Рис. 2, 3). Щоб перейти до коду додатка, потрібно натиснути клавішу F7.

Мал. 3. Код додатка

Якщо подивитися на код, який був створений Visual Studio, то ми бачимо що, платформа нам автоматично створила об'єкт класу Form. Клас - це логічна структура, що дозволяє створювати свої власні користувацькі типи шляхом групування змінних інших типів, методів і подій. Клас подібний кресленням. Він визначає дані і поведінку типу. Клас і об'єкт - це різні речі, хоча в деяких випадках вони взаємозамінні. Клас визначає тип об'єкта, але не сам об'єкт. Об'єкт - це конкретна сутність, заснована на класі і іноді звана екземпляром класу.

Також нам потрібно згадати це таке змінні. Змінні оголошуються в такий спосіб:

int i = 10; // змінна цілого типу

string st = "text"; // змінна строкового типу

bool flag = true; // змінна логічного типу, приймає значення тільки true або false

Мінлива представляє числове або строкове значення або об'єкт класу. Значення, що зберігається в змінної, може змінитися, однак ім'я залишається колишнім.

Точно також позначаються і масиви, які являють собою набір даних одного типу.

Перш ніж почати писати код для імпорту даних, визначимо які дані нам потрібно імпортувати з QUIK в нашу програму. У спрощеному варіанті для нас будуть важливі історичні свічки і поточні ціни торгуемого інструменту.

Мал. 4. Завантаження портфеля

Мал. 5. Відкриття та запуск портфеля

Рис.6. Результат відкриття портфеля

Тепер давайте подумаємо, як можна отримати дані з терміналу QUIK. З QUIK дані можна отримати трьома способами:

1) експортувати дані через ODBC

2) експортувати дані через DDE

3) брати дані безпосередньо з пам'яті

Третій спосіб дуже трудомісткий для програмування, хоча є найшвидшим для отримання даних. Висновок даних через ODBC, як пишуть розробники Quik, працює повільніше, ніж висновок через DDE. Тому ми скористаємося найбільш простим способом - висновком через DDE.

Налаштуємо відразу ж QUIK для виведення даних (Рис. 7). Для цього потрібно виділити потрібну таблицю і натиснути в меню Експорт даних - Висновок по DDE. Як DDE сервера потрібно вказати ім'я «DDEServer». В якості робочої книги потрібно вказати наступне:

1) для таблиці історичних свічок - «candles»

2) для таблиці котирувань і інструментів - «quotes»

3) для таблиці заявок - «orders»

Не забудьте встановити галочку «Висновок при натисканні Ctrl + Shift + L».

Мал. 7. Налаштування виводу таблиці по DDE

В результаті вікно QUIK буде виглядати наступним чином:

Для імпорту по DDE нам буде потрібно:

1) Клас XLTable, який реалізує приведення даних з формату Excel до звичайного вигляду (всі необхідні файли є в додатку до статті)

2) Бібліотека NDde.dll для створення DDE сервера

Щоб додати динамічну бібліотеку, яка містить необхідний набір функцій для нашого застосування, потрібно вибрати пункт «Додати посилання» в Обозревателе рішень і відкрити файл NDde.dll (Рис. 4).

Мал. 9. Імпорт DLL

Сформуємо клас для роботи з DDE сервером:

class MyDDEServer. DdeServer

public MyDDEServer (string service). base (service)

В цьому класі повинні бути передбачений мінімальний набір функцій:

1) Реєстрація сервера

2) розреєстрації сервера

3) Подія спрацьовування таймера для отримання нових даних

4) Метод для обробки прийняття даних

Додамо об'єкти на форму, для яких призначимо ці події (Рис. 5).

Мал. 10. Кнопки на формі

Тепер призначимо на кнопку «Запустити» подія натиснення мишею, двічі клікнувши на цю кнопку в конструкторі і додамо код запуску DDE сервера:

private void buttonRunDDEServer_Click (object sender, EventArgs e)

server = new MyDDEServer ( "DDEServer"); // Створюємо об'єкт DDE сервер

server.Register (); // Реєструємо його

В цьому випадку ми створили об'єкт класу MyDDEServer і потім викликали метод цього об'єкта Register (). Конструктор класу приймає одне значення - строкову змінну, яка позначає назву ім'я нашого сервера. У реалізації конструктора класу MyDDEServer ми використовуємо таймер, який буде відповідати за оповіщення при отриманні нових даних:

System.Timers.Timer _Timer = new System.Timers.Timer ();


public MyDDEServer (string service). base (service)

_Timer.Elapsed + = this.OnTimerElapsed; // підписуємося на подія прийняття даних

_Timer.Interval = 100; // Інтервал таймера в мс

Розглянемо тепер метод отримання даних через DDE:

protected override PokeResult OnPoke (DdeConversation conversation, string item, byte [] data, int format)

XLTable table = new XLTable (data);

// Дані та Заголовки таблиць

double [,] impdata = new double [table.Rows, table.Columns];

string [,] imptitles = new string [table.Rows, table.Columns];

// Конвертуємо дані в зрозумілий вид

XLTable.BytesToMatrix (data, ref impdata, ref imptitles);