Разработка движка соответствия: конец

Архитектура Go

Добро пожаловать на официальный аккаунт "Keegan Xiaogang" для получения дополнительных статей.


Разработка движка соответствия: начало

Разработка соответствующего движка: версия MVP

Разработка механизма сопоставления: проектирование структуры данных

Разработка Matching Engine: стыковка черного ящика

Разработка механизма сопоставления: процесс расшифровки «черного ящика»

Разработка механизма сопоставления: кодовая реализация процесса

Разработка соответствующего движка: кэш и MQ

Разработка механизма сопоставления: вывод журнала

Разработка движка соответствия: конец


Этот раздел является последним в этой серии статей и объяснит оставшиеся вещи, включая логику реализации очереди заказов в книге заказов транзакций и логику реализации других типов заказов. Кроме того, многие друзья спрашивают, а будет ли весь код открытым и выкладываться на Github после доработки? Все, что я могу сказать, это то, что существует высокая вероятность того, что в долгосрочной перспективе он будет открыт, но в краткосрочной перспективе его открывать не планируется.

очередь заказов

Книга транзакционных ордеров фактически состоит из двух очередей ордеров: очереди ордеров на покупку и очереди ордеров на продажу. Любой запрос и операция в регистре делегирования транзакций фактически представляют собой две очереди запроса и операции. Дизайн очереди заказов также напрямую влияет на производительность подбора игроков.В предыдущей статье также кратко говорилось о дизайне очереди заказов, когда речь шла о дизайне структуры данных.Мы в основном используем двумерные ссылки в сочетании с картой для сохранения всех заказов. , опираясь наcontainer/listСумка.

Структура очереди заказов следующая:

type orderQueue struct {
	sortBy     enum.SortDirection
	parentList *list.List
	elementMap map[string]*list.Element
}

sortByЗадает направление сортировки цен, очередь ордеров на покупку идет по убыванию, а очередь ордеров на продажу — по возрастанию.parentListСохраните все заказы всего двумерного связанного списка, первое измерение отсортировано по цене, а второе измерение отсортировано по времени.elementMapЭто пара ключ-значение, где Key — цена, а Value — двумерный список ордеров.

Функция инициализации относительно проста, она лишь присваивает значения нескольким полям, код выглядит следующим образом:

func (q *orderQueue) init(sortBy enum.SortDirection) {
	q.sortBy = sortBy
	q.parentList = list.New()
	q.elementMap = make(map[string]*list.Element)
}

В дополнение к функции инициализации предусмотрено пять других функций:

  • addOrder(order): добавить заказ
  • getHeadOrder(): читать порядок заголовков
  • popHeadOrder(): читать и удалять порядок заголовков
  • removeOrder(order): удалить заказ
  • getDepthPrice(depth): читать цену глубины

Сложнее будет только первая функция из пяти вышеперечисленных функций.Для облегчения понимания процесса обработки я не буду выкладывать код и рисовать полную блок-схему на всеобщее обозрение:

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

Остальные функции просты, и последнюю функцию необходимо объяснить. Цель считывания цены глубины состоит в том, чтобы облегчить определение верхней предельной цены при обработке ордеров типа «рыночный оппонент», «рыночный топ5», «рынок топ10» и других типов. Пожалуйста, посмотрите код функции, чтобы понять логику и использование функции:

func (q *orderQueue) getDepthPrice(depth int) (string, int) {
	if q.parentList.Len() == 0 {
		return "", 0
	}
	p := q.parentList.Front()
	i := 1
	for ; i < depth; i++ {
		t := p.Next()
		if t != nil {
			p = t
		} else {
			break;
		}
	}
	o := p.Value.(*list.List).Front().Value.(*Order)
	return o.Price.String(), i
}

Несколько типов заказов

Наш движок поддерживает в общей сложности шесть типов ордеров. В предыдущих статьях они были кратко представлены, но не объяснялись подробно, какой должна быть конкретная бизнес-логика этих разных типов. Поэтому эта часть дополняется здесь.

1. limit

Обычный ценовой лимит самый простой, а реализация кода была показана в предыдущей статье.Чтобы углубить ваше понимание, нарисую для вас еще одну картинку:

Логика обработки такая:

  1. Определите, является ли новый ордер ордером на покупку или продажу.
  2. Если это ордер на покупку, прочитать головной ордер на продажу из OrderBook, то есть головной ордер в очереди ордеров на продажу; если это ордер на продажу, прочитать головной ордер на покупку из OrderBook, то есть головной ордер в очереди на покупку.
  3. Когда новый ордер является ордером на покупку, если головной ордер пуст или новый ордер меньше, чем головной ордер, транзакция не может быть выполнена, тогда новый ордер будет добавлен в очередь ордеров на покупку, и обработка завершится. ; когда новый ордер является ордером на продажу, если головной ордер Если новый ордер пуст, или новый ордер больше, чем головной ордер, то есть транзакция не может быть исполнена, то новый ордер добавляется в очередь ордеров на продажу, и обработка завершается.
  4. В противном случае, если условия сопоставления соблюдены, новый ордер и верхний ордер будут сопоставлены и проданы.
  5. После завершения сопоставления, если оставшееся количество нового заказа равно нулю, оно завершится.Если оно все еще больше нуля, вернитесь к шагу 2 и продолжайте принимать следующий головной заказ и так далее.

2. limit-ioc

Существует только одно различие между лимитной ценой IOC и обычной лимитной ценой.Если новый ордер не соответствует основному ордеру, обычный лимитный ордер будет добавлен в очередь ордеров, а лимитная цена IOC будет обработана как отмена, пожалуйста, смотрите картинку ниже:

3. market

Логика рыночного ордера по умолчанию также относительно проста.Ему не нужно оценивать цену.Пока головной ордер не пуст, он будет напрямую соответствовать головному ордеру.Логика обработки следующая:

4. market-top5/market-top10

Логика оптимального пятого/десятого рыночного ордера аналогична логике рыночного ордера по умолчанию, разница в том, что цена сделки рыночной цены по умолчанию не имеет верхнего или нижнего предела, а оптимальная пятая/десятая рыночная цена имеет верхний предел цены или нижний предел Ордера с верхним и нижним пределом не будут исполнены. Рисовать слишком утомительно, давайте просто вставим код напрямую.Далее идет обработка заказа на покупку:

func dealBuyMarketTop(order *Order, book *orderBook, lastTradePrice *decimal.Decimal, depth int) {
	priceStr, _ := book.getSellDepthPrice(depth)
	if priceStr == "" {
		cancelOrder(order)
		return
	}
	limitPrice, _ := decimal.NewFromString(priceStr)
LOOP:
	headOrder := book.getHeadSellOrder()
	if headOrder != nil && limitPrice.GreaterThanOrEqual(headOrder.Price) {
		matchTrade(headOrder, order, book, lastTradePrice)
		if order.Amount.IsPositive() {
			goto LOOP
		}
	} else {
		cancelOrder(order)
	}
}

5. market-opponent

Последний тип, лучшая цена контрагента, этот тип имеет дело только с ценой первого уровня контрагента, но он немного отличается от лучшей цены 5-го/10-го уровня: неторгуемая часть лучшей цены 5-го/10-го уровня – это неисполненная часть лучшей цены контрагента конвертируется в лимитную заявку. Пожалуйста, смотрите код:

func dealBuyMarketOpponent(order *Order, book *orderBook, lastTradePrice *decimal.Decimal) {
	priceStr, _ := book.getSellDepthPrice(1)
	if priceStr == "" {
		cancelOrder(order)
		return
	}
	limitPrice, _ := decimal.NewFromString(priceStr)
LOOP:
	headOrder := book.getHeadSellOrder()
	if headOrder != nil && limitPrice.GreaterThanOrEqual(headOrder.Price) {
		matchTrade(headOrder, order, book, lastTradePrice)
		if order.Amount.IsPositive() {
			goto LOOP
		}
	} else {
		order.Price = limitPrice
		order.Type = enum.TypeLimit
		book.addBuyOrder(order)
		cache.UpdateOrder(order.ToMap())
		log.Info("engine %s, a order has added to the orderbook: %s", order.Symbol, order.ToJson())
	}
}

конец

На этом вся серия закончилась. Тем не менее, моя программа подбора игроков будет постоянно обновляться, а также будут разрабатываться другие компоненты, которые будут использоваться в сочетании с текущим механизмом подбора игроков. Добро пожаловать в последующие новости.


Личный блог автора

Отсканируйте следующий QR-код, чтобы подписаться на официальный аккаунт (имя общедоступного аккаунта: Keegan Xiaogang)