MatchEngine.h

source: wtcpp/folder98/folder05/folder1/file04.md

#pragma once
#include <stdint.h>
#include <map>
#include <vector>
#include <functional>
#include <string.h>

#include "../Includes/WTSMarcos.h"
#include "../Includes/WTSCollection.hpp"
#include "../Includes/FasterDefs.h"

NS_WTP_BEGIN
class WTSTickData;
class WTSVariant;
NS_WTP_END

USING_NS_WTP;

typedef std::vector<uint32_t> OrderIDs;			// 订单列表
typedef WTSHashMap<std::string>	WTSTickCache;

// "MatchEngine": 尚不清楚具体细节, 暂命名为 匹配引擎吧

// 匹配引擎回调接口
class IMatchSink
{
public:
	/*
	 *	成交回报
	 *	code	合约代码
	 *	isBuy	买or卖
	 *	vol		成交数量, 这里没有正负, 通过isBuy确定买入还是卖出
	 *	price	成交价格
	 */
	virtual void handle_trade(uint32_t localid, const char* stdCode, bool isBuy, double vol, double fireprice, double price, uint64_t ordTime) = 0;

	/*
	 *	订单回报
	 *	localid	本地单号
	 *	code	合约代码
	 *	isBuy	买or卖
	 *	leftover	剩余数量
	 *	price	委托价格
	 *	isCanceled	是否已撤销
	 */
	virtual void handle_order(uint32_t localid, const char* stdCode, bool isBuy, double leftover, double price, bool isCanceled, uint64_t ordTime) = 0;

	/*
	 * 输入订单
	 */
	virtual void handle_entrust(uint32_t localid, const char* stdCode, bool bSuccess, const char* message, uint64_t ordTime) = 0;
};

typedef std::function<void(double)> FuncCancelCallback;

// 匹配引擎
class MatchEngine
{
public:
	MatchEngine() : _tick_cache(NULL),_cancelrate(0), _sink(NULL)
	{

	}
private:
	void	fire_orders(const char* stdCode, OrderIDs& to_erase);
	void	match_orders(WTSTickData* curTick, OrderIDs& to_erase);
	void	update_lob(WTSTickData* curTick);
	// 获取品种对应最新价
	inline WTSTickData*	grab_last_tick(const char* stdCode);

public:
	// 初始化
	void	init(WTSVariant* cfg);
	// 注册回调接口
	void	regisSink(IMatchSink* sink) { _sink = sink; }

	void	clear();

	void	handle_tick(const char* stdCode, WTSTickData* curTick);

	// 买入, 卖出撤单
	OrderIDs	buy(const char* stdCode, double price, double qty, uint64_t curTime);
	OrderIDs	sell(const char* stdCode, double price, double qty, uint64_t curTime);
	virtual OrderIDs cancel(const char* stdCode, bool isBuy, double qty, FuncCancelCallback cb);
	// 修改订单状态: 撤单
	double		cancel(uint32_t localid);

private:
	typedef struct _OrderInfo
	{
		char		_code[32];
		bool		_buy;			// 买入/卖出
		double		_qty;			// 可成交量
		double		_left;			// 市场成交量
		double		_traded;
		double		_limit;			// 限价
		double		_price;			// 最新成交价
		uint32_t	_state;			// 订单状态
		uint64_t	_time;
		double		_queue;			// 排队
		bool		_positive;		// 是否主动成交

		_OrderInfo()
		{
			memset(this, 0, sizeof(_OrderInfo));
		}
	} OrderInfo;

	typedef faster_hashmap<uint32_t, OrderInfo> Orders;
	Orders	_orders;		// 订单字典

	typedef std::map<uint32_t, double>	LOBItems;

	typedef struct _LmtOrdBook
	{
		LOBItems	_items;
		uint32_t	_cur_px;	// 最新价
		uint32_t	_ask_px;	// ask
		uint32_t	_bid_px;	// bid

		void clear()
		{
			_items.clear();
			_cur_px = 0;
			_ask_px = 0;
			_bid_px = 0;
		}

		_LmtOrdBook()
		{
			_cur_px = 0;
			_ask_px = 0;
			_bid_px = 0;
		}
	} LmtOrdBook;
	typedef faster_hashmap<std::string, LmtOrdBook> LmtOrdBooks;
	LmtOrdBooks	_lmt_ord_books;		// 订单簿字典

	IMatchSink*	_sink;				// 匹配引擎回调接口

	double			_cancelrate;
	WTSTickCache*	_tick_cache;	// Tick数据缓存
};

MatchEngine.cpp

#include "MatchEngine.h"
#include "../Includes/WTSDataDef.hpp"
#include "../Includes/WTSVariant.hpp"

#include "../Share/TimeUtils.hpp"
#include "../Share/decimal.h"
#include "../WTSTools/WTSLogger.h"

// 处理数据精度
#define PRICE_DOUBLE_TO_INT_P(x) ((int32_t)((x)*10000.0 + 0.5))
#define PRICE_DOUBLE_TO_INT_N(x) ((int32_t)((x)*10000.0 - 0.5))
#define PRICE_DOUBLE_TO_INT(x) (((x)==DBL_MAX)?0:((x)>0?PRICE_DOUBLE_TO_INT_P(x):PRICE_DOUBLE_TO_INT_N(x)))

extern uint32_t makeLocalOrderID();

// 通过配置初始化匹配引擎
void MatchEngine::init(WTSVariant* cfg)
{
	if (cfg == NULL)
		return;
	// cancelrate?
	_cancelrate = cfg->getDouble("cancelrate");
}

void MatchEngine::clear()
{
	_orders.clear();
}

// 
void MatchEngine::fire_orders(const char* stdCode, OrderIDs& to_erase)
{
	// 遍历订单簿字典
	for (auto& v : _orders)
	{
		uint32_t localid = v.first;						// 编号
		OrderInfo& ordInfo = (OrderInfo&)v.second;		// 订单簿

		if (ordInfo._state == 0)	//需要激活
		{
			// 输入订单, 订单回报
			_sink->handle_entrust(localid, stdCode, true, "", ordInfo._time);
			_sink->handle_order(localid, stdCode, ordInfo._buy, ordInfo._left, ordInfo._limit, false, ordInfo._time);
			ordInfo._state = 1;
		}
	}
}

void MatchEngine::match_orders(WTSTickData* curTick, OrderIDs& to_erase)
{
	// 当前时间
	uint64_t curTime = (uint64_t)curTick->actiondate() * 1000000000 + curTick->actiontime();
	uint64_t curUnixTime = TimeUtils::makeTime(curTick->actiondate(), curTick->actiontime());

	// 遍历订单字典
	for (auto& v : _orders)
	{
		uint32_t localid = v.first;
		OrderInfo& ordInfo = (OrderInfo&)v.second;

		if (ordInfo._state == 9)//要撤单
		{
			_sink->handle_order(localid, ordInfo._code, ordInfo._buy, 0, ordInfo._limit, true, ordInfo._time);
			ordInfo._state = 99;

			to_erase.emplace_back(localid);

			WTSLogger::info("订单%u已撤销, 剩余数量: %d", localid, ordInfo._left*(ordInfo._buy ? 1 : -1));
			ordInfo._left = 0;
			continue;
		}

		if (ordInfo._state != 1 || curTick->volume() == 0)
			continue;

		if (ordInfo._buy)
		{
			double price;
			double volume;

			//主动订单就按照对手价
			if (ordInfo._positive)
			{
				price = curTick->askprice(0);
				volume = curTick->askqty(0);
			}
			else
			{
				price = curTick->price();
				volume = curTick->volume();
			}

			if (decimal::le(price, ordInfo._limit))
			{
				//如果价格相等,需要先看排队位置,如果价格不等说明已经全部被大单吃掉了
				if (!ordInfo._positive && decimal::eq(price, ordInfo._limit))
				{
					double& quepos = ordInfo._queue;

					//如果成交量小于排队位置,则不能成交
					if (volume <= quepos)
					{
						quepos -= volume;
						continue;
					}
					else if (quepos != 0)
					{
						//如果成交量大于排队位置,则可以成交
						volume -= quepos;
						quepos = 0;
					}
				}
				else if (!ordInfo._positive)
				{
					volume = ordInfo._left;
				}

				double qty = min(volume, ordInfo._left);
				// 回调成交回报
				_sink->handle_trade(localid, ordInfo._code, ordInfo._buy, qty, ordInfo._price, price, ordInfo._time);

				ordInfo._traded += qty;
				ordInfo._left -= qty;

				// 回调订单回报
				_sink->handle_order(localid, ordInfo._code, ordInfo._buy, ordInfo._left, price, false, ordInfo._time);

				if (ordInfo._left == 0)
					to_erase.emplace_back(localid);
			}
		}

		if (!ordInfo._buy)
		{
			double price;
			double volume;

			//主动订单就按照对手价
			if (ordInfo._positive)
			{
				price = curTick->bidprice(0);
				volume = curTick->bidqty(0);
			}
			else
			{
				price = curTick->price();
				volume = curTick->volume();
			}

			if (decimal::ge(price, ordInfo._limit))
			{
				//如果价格相等,需要先看排队位置,如果价格不等说明已经全部被大单吃掉了
				if (!ordInfo._positive && decimal::eq(price, ordInfo._limit))
				{
					double& quepos = ordInfo._queue;

					//如果成交量小于排队位置,则不能成交
					if (volume <= quepos)
					{
						quepos -= volume;
						continue;
					}
					else if (quepos != 0)
					{
						//如果成交量大于排队位置,则可以成交
						volume -= quepos;
						quepos = 0;
					}
				}
				else if (!ordInfo._positive)
				{
					volume = ordInfo._left;
				}

				double qty = min(volume, ordInfo._left);
				_sink->handle_trade(localid, ordInfo._code, ordInfo._buy, qty, ordInfo._price, price, ordInfo._time);
				ordInfo._traded += qty;
				ordInfo._left -= qty;

				_sink->handle_order(localid, ordInfo._code, ordInfo._buy, ordInfo._left, price, false, ordInfo._time);

				if (ordInfo._left == 0)
					to_erase.emplace_back(localid);
			}
		}
	}
}

// 更新啥?
void MatchEngine::update_lob(WTSTickData* curTick)
{
	LmtOrdBook& curBook = _lmt_ord_books[curTick->code()];
	curBook._cur_px = PRICE_DOUBLE_TO_INT(curTick->price());
	curBook._ask_px = PRICE_DOUBLE_TO_INT(curTick->askprice(0));
	curBook._bid_px = PRICE_DOUBLE_TO_INT(curTick->bidprice(0));

	for (uint32_t i = 0; i < 10; i++)
	{
		if (PRICE_DOUBLE_TO_INT(curTick->askprice(i)) == 0 && PRICE_DOUBLE_TO_INT(curTick->bidprice(i)) == 0)
			break;

		uint32_t px = PRICE_DOUBLE_TO_INT(curTick->askprice(i));
		if (px != 0)
		{
			double& volume = curBook._items[px];
			volume = curTick->askqty(i);
		}

		px = PRICE_DOUBLE_TO_INT(curTick->bidprice(i));
		if (px != 0)
		{
			double& volume = curBook._items[px];
			volume = curTick->askqty(i);
		}
	}

	//卖一和买一之间的报价必须全部清除掉
	if (!curBook._items.empty())
	{
		auto sit = curBook._items.lower_bound(curBook._bid_px);
		if (sit->first == curBook._bid_px)
			sit++;

		auto eit = curBook._items.lower_bound(curBook._ask_px);

		if (sit->first <= eit->first)
			curBook._items.erase(sit, eit);
	}
}

// 
OrderIDs MatchEngine::buy(const char* stdCode, double price, double qty, uint64_t curTime)
{
	WTSTickData* lastTick = grab_last_tick(stdCode);
	if (lastTick == NULL)
		return OrderIDs();

	// 获取本地订单ID和并填充订单信息
	uint32_t localid = makeLocalOrderID();
	OrderInfo& ordInfo = _orders[localid];
	strcpy(ordInfo._code, stdCode);
	ordInfo._buy = true;
	ordInfo._limit = price;
	ordInfo._qty = qty;
	ordInfo._left = qty;
	ordInfo._price = lastTick->price();

	//订单排队,如果是对手价,则按照对手价的挂单量来排队
	//如果是最新价,则按照买一卖一的加权平均
	if (decimal::ge(price, lastTick->askprice(0)))
		ordInfo._positive = true;
	else if (decimal::eq(price, lastTick->bidprice(0)))
		ordInfo._queue = lastTick->bidqty(0);
	if (decimal::eq(price, lastTick->price()))
		ordInfo._queue = (uint32_t)round((lastTick->askqty(0)*lastTick->askprice(0) + lastTick->bidqty(0)*lastTick->bidprice(0)) / (lastTick->askprice(0) + lastTick->bidprice(0)));

	//排队位置按照平均撤单率,撤销掉部分
	ordInfo._queue -= (uint32_t)round(ordInfo._queue*_cancelrate);
	ordInfo._time = curTime;

	lastTick->release();

	OrderIDs ret;
	ret.emplace_back(localid);
	return ret;
}

OrderIDs MatchEngine::sell(const char* stdCode, double price, double qty, uint64_t curTime)
{
	WTSTickData* lastTick = grab_last_tick(stdCode);
	if (lastTick == NULL)
		return OrderIDs();

	uint32_t localid = makeLocalOrderID();
	OrderInfo& ordInfo = _orders[localid];
	strcpy(ordInfo._code, stdCode);
	ordInfo._buy = false;
	ordInfo._limit = price;
	ordInfo._qty = qty;
	ordInfo._left = qty;
	ordInfo._price = lastTick->price();

	//订单排队,如果是对手价,则按照对手价的挂单量来排队
	//如果是最新价,则按照买一卖一的加权平均
	if (decimal::eq(price, lastTick->askprice(0)))
		ordInfo._queue = lastTick->askqty(0);
	else if (decimal::le(price, lastTick->bidprice(0)))
		ordInfo._positive = true;
	if (decimal::eq(price, lastTick->price()))
		ordInfo._queue = (uint32_t)round((lastTick->askqty(0)*lastTick->askprice(0) + lastTick->bidqty(0)*lastTick->bidprice(0)) / (lastTick->askprice(0) + lastTick->bidprice(0)));

	ordInfo._queue -= (uint32_t)round(ordInfo._queue*_cancelrate);
	ordInfo._time = curTime;

	lastTick->release();

	OrderIDs ret;
	ret.emplace_back(localid);
	return ret;
}

OrderIDs MatchEngine::cancel(const char* stdCode, bool isBuy, double qty, FuncCancelCallback cb)
{
	OrderIDs ret;
	for (auto& v : _orders)
	{
		OrderInfo& ordInfo = (OrderInfo&)v.second;
		if (ordInfo._state != 1)
			continue;

		double left = qty;
		if (ordInfo._buy == isBuy)
		{
			uint32_t localid = v.first;
			ret.emplace_back(localid);
			ordInfo._state = 9;
			cb(ordInfo._left*(ordInfo._buy ? 1 : -1));

			if (qty != 0)
			{
				if ((int32_t)left <= ordInfo._left)
					break;

				left -= ordInfo._left;
			}
		}
	}

	return ret;
}

double MatchEngine::cancel(uint32_t localid)
{
	auto it = _orders.find(localid);
	if (it == _orders.end())
		return 0.0;

	OrderInfo& ordInfo = (OrderInfo&)it->second;
	ordInfo._state = 9;

	return ordInfo._left*(ordInfo._buy ? 1 : -1);
}

void MatchEngine::handle_tick(const char* stdCode, WTSTickData* curTick)
{
	if (NULL == curTick)
		return;

	if (NULL == _tick_cache)
		_tick_cache = WTSTickCache::create();

	_tick_cache->add(stdCode, curTick, true);

	update_lob(curTick);

	OrderIDs to_erase;
	//检查订单状态
	fire_orders(stdCode, to_erase);

	//撮合
	match_orders(curTick, to_erase);

	for (uint32_t localid : to_erase)
	{
		auto it = _orders.find(localid);
		if (it != _orders.end())
			_orders.erase(it);
	}
}

// 获取品种最新tick数据
WTSTickData* MatchEngine::grab_last_tick(const char* stdCode)
{
	if (NULL == _tick_cache)
		return NULL;

	return (WTSTickData*)_tick_cache->grab(stdCode);
}