Как написать простую стратегию
Реализуем следующую стратегию: будем поддерживать наши заявки на лучшей цене в обоих направлениях.
Сначала научимся ставить заявку. Для этого используем метод:
bool add_limit_order(Dir dir, Price price, Amount amount);
def add_limit_order(self, dir, price, amount)
Внимание: в Python функции trading_book_update, trading_deals_update, execution_report_update являются свободными, поэтому в них первым параметром передаётся объект strat типа ParticipantStrategy, от которого и нужно вызывать методы стратегии.
Функция add_limit_order выставляет нашу лимитную заявку, где:
- dir — направление (BID = 0 — покупка, ASK = 1 — продажа).
- price — цена, по которой заявка будет выставлена.
- amount — размер заявки.
Будем выставлять нашу заявку внутри функции trading_book_update, когда нам приходит новый стакан order_book
.
Для определения лучшей цены используем метод best_price класса OrderBook:
void trading_book_update(const OrderBook& order_book) override {
for (Dir dir : {BID, ASK}) {
Price best_price = order_book.best_price(dir);
Amount amount = 1;
add_limit_order(dir, best_price, amount);
}
}
def trading_book_update(strat, order_book):
for dir in (BID, ASK):
best_price = order_book.best_price(dir)
amount = 1
strat.add_limit_order(dir, best_price, amount)
В тот момент, когда у нас вызывается функция trading_book_update, наши заявки, поставленные в прошлых вызовах этой функции, всё еще могут быть не исполнены. В итоге, у нас может скопиться огромное количество заявок. К счастью, у нас есть возможность посмотреть все наши активные заявки:
const auto& our_orders = order_book.orders();
our_orders = order_book.orders()
Здесь мы используем метод orders, возвращающий ссылку на объект типа SecurityOrdersSnapshot.
Замечание 1: Определённая выше переменная
our_orders
содержит те заявки, которые мы уже отправили, но на которые ещё не отправили запрос на удаление. Поэтому если для какой-то заявки будет вызван метод delete_order, то к следующему обновлению вour_orders
этой заявки точно не будет, даже если в реальности она ещё не успела удалиться.Замечание 2: Обновление объекта order_book.orders() происходит только между апдейтами, внутри апдейта он не меняется.
Будем ставить заявку, если не существует активной заявки по этому направлению. Для определения наличия активной заявки используем метод active_orders_count(Dir dir) класса SecurityOrdersSnapshot, возвращающий количество наших активных заявок по направлению:
void trading_book_update(const OrderBook& order_book) override {
const auto& our_orders = order_book.orders();
for (Dir dir : {BID, ASK}) {
if (our_orders.active_orders_count(dir) == 0) {
Price best_price = order_book.best_price(dir);
Amount amount = 1;
add_limit_order(dir, best_price, amount);
}
}
}
def trading_book_update(strat, order_book):
our_orders = order_book.orders()
for dir in (BID, ASK):
if our_orders.active_orders_count(dir) == 0:
best_price = order_book.best_price(dir)
amount = 1
strat.add_limit_order(dir, best_price, amount)
В такой реализации есть минус — если лучшая цена изменится, то мы на это не отреагируем. Это может привести к тому, что мы долго не будем торговать по одному из направлений. Чтобы получить список наших активных заявок, используем метод orders_by_dir() класса SecurityOrdersSnapshot. Полный код стратегии будет выглядеть так:
#include "participant_strategy.h"
using namespace hftbattle;
namespace {
class UserStrategy : public ParticipantStrategy {
public:
explicit UserStrategy(const JsonValue& config) { }
void trading_book_update(const OrderBook& order_book) override {
const auto& our_orders = order_book.orders();
for (Dir dir : {BID, ASK}) {
Price best_price = order_book.best_price(dir);
Amount amount = 1;
if (our_orders.active_orders_count(dir) == 0) {
add_limit_order(dir, best_price, amount);
} else { // есть хотя бы одна наша активная заявка
auto first_order = our_orders.orders_by_dir(dir).front();
bool on_best_price = (first_order->price() == best_price);
if (!on_best_price) { // наша заявка стоит, но не на текущей лучшей цене
delete_order(first_order);
add_limit_order(dir, best_price, amount);
}
}
}
}
};
} // namespace
REGISTER_CONTEST_STRATEGY(UserStrategy, user_strategy)
# -*- coding: utf-8 -*-
from py_defs import *
from py_defs import Decimal as Price
from common_enums import *
def trading_book_update(strat, order_book):
our_orders = order_book.orders()
for dir in (BID, ASK):
best_price = order_book.best_price(dir)
amount = 1
if our_orders.active_orders_count(dir) == 0:
strat.add_limit_order(dir, best_price, amount)
else:
first_order = our_orders.orders_by_dir(dir)[0]
on_best_price = (first_order.price() == best_price)
if not on_best_price: # наша заявка стоит, но не на текущей лучшей цене
strat.delete_order(first_order)
strat.add_limit_order(dir, best_price, amount)
Теперь вы можете писать простейшие стратегии.