E2Q 协议说明
E2Q 协议
协议采用 (network byte order) 结构
Init 初始化订阅 symbol
- 格式
| Name | Offset | Length | Value | Notes |
|---|---|---|---|---|
| Message Type | 0 | 1 | 'I' | Init Type |
| stock | 1 | 10 | Alpha | 股票名称 |
| cficode | 11 | 4 | Integer | cfi code |
| type | 15 | 1 | Alpha | symbol type ['i':index, 't':trade] |
| TickTime | 16 | 4 | Integer | 每一笔报价的间隔时间, 回测的时候使用的, 免得策略超过这个报价的间隔时间就有点麻烦了 |
| unix_time | 20 | 8 | Inte 64 | 默认为上市时间 |
| Aligned | 28 | 1 | Alpha | 当前的状态 ['U': 后面还有数据, 'P': 当前一个时间对齐完成] |
- C++ 类
struct BaseMessage {
char MsgType;
char Aligned;
}; /* ---------- end of struct BaseMessage ---------- */
struct SystemInitMessage : public BaseMessage {
char Stock[E2QSTOCK_LENGTH] = {0};
std::uint32_t CfiCode = 0;
char Itype = InitType::INDEX;
std::uint32_t OfferTime = 0;
}; /* ---------- end of struct SystemInitMessage ---------- */
typedef struct SystemInitMessage SystemInitMessage;
Exit 系统退出
- 格式
| Name | Offset | Length | Value | Notes |
|---|---|---|---|---|
| Message Type | 0 | 1 | 'E' | Exit process |
- C++ 类
SUSPEND 暂停交易
- 格式
| Name | Offset | Length | Value | Notes |
|---|---|---|---|---|
| Message Type | 0 | 1 | 'S' | suspend order |
- C++ 类
MARKET 交易市场
- 格式
| Name | Offset | Length | Value | Notes |
|---|---|---|---|---|
| Message Type | 0 | 1 | 'M' | market |
| Action | 1 | 1 | 'L' or 'D' | 'L' to list(Going Public), 'D' is Delisting |
| cficode | 2 | 4 | Integer | cfi code |
| unix_time | 20 | 8 | Integer 64 | 上市或退市时间 |
- C++ 类
除权分红
- 格式
| Name | Offset | Length | Value | Notes |
|---|---|---|---|---|
| Message Type | 0 | 1 | 'X' | 除权分红 |
| cficode | 1 | 4 | Integer | cfi code |
| year | 5 | 2 | Integer | year |
| month | 7 | 2 | Integer | month |
| day | 9 | 2 | Integer | day |
| category | 11 | 2 | Integer | category |
| fenhong | 13 | 4 | Integer | fenhong |
| songzhuangu | 17 | 4 | Integer | songzhuangu |
| split | 21 | 4 | Integer | split |
| outstanding | 25 | 4 | Integer | outstanding |
| outstandend | 26 | 4 | Integer | outstandend |
| mrketCaping | 33 | 4 | Integer | mrketCaping |
| Aligned | 37 | 1 | Alpha | 当前的状态 ['U': 后面还有数据, 'P': 当前一个时间对齐完成] |
- C++ 类
struct StockAXdxrMessage : public BaseMessage {
std::uint32_t CfiCode = 0;
std::uint16_t year = 0;
std::uint16_t month = 0;
std::uint16_t day = 0;
std::uint16_t category = 0;
std::uint32_t fenhong = 0;
std::uint32_t songzhuangu = 0;
std::uint32_t outstanding = 0;
std::uint32_t outstandend = 0;
std::uint32_t mrketCaping = 0;
std::uint16_t uint = 0; // 10 送,还是 100 送
}; /* ---------- end of struct StockAXdxrMessage ---------- */
typedef struct StockAXdxrMessage StockAXdxrMessage;
TICK 行情报价
- 格式
| Name | Offset | Length | Value | Notes |
|---|---|---|---|---|
| Message Type | 0 | 1 | 'T' | Tick process |
| cficode | 1 | 4 | Integer | cfi code |
| unix_time | 5 | 8 | Integer 64 | unix_time |
| frame | 13 | 2 | Integer 16 | frame |
| side | 15 | 1 | Alpha | side [ 'B' : Buy Order, 'S' : Sell Order ] |
| price | 16 | 6 | Integer 64 | price |
| qty | 22 | 6 | Integer 64 | qty |
| number | 28 | 6 | Integer 32 | number |
| match | 34 | 1 | Alpha | 'n','y' 当前一笔报价是否可作为撮合价格 |
| Aligned | 35 | 1 | Alpha | 当前的状态 ['U': 后面还有数据, 'P': 当前一个时间对齐完成] |
- C++ 类
// if qty == 0 // 涨跌停 不撮合交易
struct MarketTickMessage : public BaseMessage {
std::uint32_t CfiCode = 0;
std::uint64_t unix_time = 0;
std::uint16_t frame = 0;
char side = 'B'; // BID OR ASK change e2::Side
std::uint64_t price = 0; // last price
std::uint64_t qty = 0;
std::uint32_t number = 0;
}; /* ---------- end of struct MarketTickMessage ---------- */
typedef struct MarketTickMessage MarketTickMessage;
独立撮合价
- 格式
| Name | Offset | Length | Value | Notes |
|---|---|---|---|---|
| Message Type | 0 | 1 | 'D' | Deal |
| stock | 1 | 10 | Alpha | 股票名称 |
| side | 11 | 1 | Alpha | 'B', 'S' |
| dprice | 12 | 6 | Integer64 | 成交均价 |
| dqty | 18 | 6 | Integer64 | qty |
| commission | 24 | 6 | Integer64 | commission |
| tamount | 30 | 6 | Integer64 | 成交额 |
| tdate | 36 | 6 | Integer32 | trade date |
| ttime | 42 | 6 | Integer32 | trade time |
| unix_time | 48 | 8 | Integer64 | unix_time |
| ticket | 54 | 8 | Integer64 | 当前一笔的ticket |
| unique_size | 62 | 6 | Integer16 | size |
| unique_id | 68 | 256 | Alpha | unique value |
| Aligned | 324 | 1 | Alpha | Aligned_t |
- python
class BaseMessage:
'''
整数的精度
'''
number_deci = 10000.0
class MsgType:
INIT = b'I'
XDXR = b'X'
SUSPEND = b'S'
TICK = b'T'
CUSTOM = b'C'
MARKETING = b'M'
EXIT = b'E'
LOG = b'L'
DEAL = b'D'
class Aligned:
# 进行中
UNDER = b'U'
# 完成
PULL = b'P'
class DealMatchMessage(BaseMessage):
""""""
def __init__(self):
"""Constructor for """
self._mt = MsgType()
self._al = Aligned()
# 转成整数
self._number_deci = BaseMessage.number_deci
self.msgtype = self._mt.DEAL
self._stock = ""
self._side = b''
self._dprice = 0
self._dqty = 0
self._commision = 0
self._tamount = 0
self._tdate = 0
self._ttime = 0
self._unix_time = int(time.time())
self._ticket = 0
self._unique_size = 0
self._unique_id = ""
self.anligned = self._al.UNDER
def Stock(self, symbol):
if not isinstance(symbol, bytearray):
self._stock = symbol.encode('utf-8')
else:
self._stock = symbol
def data(self, side, price, qty, commision, amount, tdate, ttime, unix_time,
ticket, unique):
self._side = side
self._dprice = int(price * self._number_deci)
self._dqty = qty
self._commision = commision
self._tamount = amount
self._tdate = tdate
self._ttime = ttime
self._unix_time = unix_time
self._ticket = ticket
self._unique_size = len(unique)
self._unique_id = unique
if not isinstance(unique, bytearray):
self._unique_id = unique.encode('utf-8')
else:
self._unique_id = unique
def toString(self):
"""
"""
uinx_time_64 = struct.pack("!Q", self._unix_time)
tick_64 = struct.pack("!Q", self._ticket)
price_64 = struct.pack("!Q", int(self._dprice))
price_64 = price_64[2:]
qty_64 = struct.pack("!Q", int(self._dqty))
qty_64 = qty_64[2:]
commi_64 = struct.pack("!Q", int(self._commision * self._number_deci))
commi_64 = commi_64[2:]
amount_64 = struct.pack("!Q", int(self._tamount * self._number_deci))
amount_64 = amount_64[2:]
fmt = '!c9sc6s6s6s6sII8s8sH' + str(self._unique_size) + 'sc'
data = struct.pack(fmt,
self.msgtype, self._stock,
self._side, price_64, qty_64, commi_64, amount_64,
self._tdate, self._ttime,
uinx_time_64, tick_64, self._unique_size,
self._unique_id,
self.anligned)
return data
自定义数据
- 格式
| Name | Offset | Length | Value | Notes |
|---|---|---|---|---|
| Message Type | 0 | 1 | 'C' | Custom process |
| cficode | 1 | 4 | Integer | cfi code |
| index | 5 | 2 | Integer 16 | value deci |
| size | 7 | 2 | Integer 16 | value deci |
| type | 9 | 1 | Alpha | Integer 16 = '6' , Integer 32 = '2', Integer 64 = '4' |
| value | 10 | 2,4,8 (...) | Integer 16,32,64 | data list |
| Aligned | list size | 1 | Alpha | 当前的状态 ['U': 后面还有数据, 'P': 当前一个时间对齐完成] |
- C++ 类
enum CmType {
UINT16 = '6',
UINT32 = '2',
UINT64 = '4'
}; /* ---------- end of enum CmType ---------- */
typedef enum CmType CmType;
struct CustomMessage : public BaseMessage {
std::uint32_t CfiCode = 0;
std::uint16_t index = 0; // 可以用作 dict 的索引
std::uint16_t size = 0;
char type; // CmType
}; /* ---------- end of struct CustomMessage ---------- */
typedef struct CustomMessage CustomMessage;
- E2L 代码
idx = 0;
size = FCustomDataSize(cficode, idx);
echo(size);
LOG 日志
- 格式
| Name | Offset | Length | Value | Notes |
|---|---|---|---|---|
| Message Type | 0 | 1 | 'L' | Log process |
| logt | 1 | 2 | Alpha | LogType_t BASE = 'B', LINE = 'L', PRO = 'P', TIME = 'T' |
| numt | 1 | 3 | Alpha | NumberType_t NEGATIVE = 'N', // -1121 POSITIVE = 'P' // 2323 |
| val | 4 | 8 | Integer 64 | variable value |
| deci | 12 | 2 | Integer 16 | value deci |
| loc | 14 | 4 | Integer 32 | code line |
| ticket_now | 18 | 8 | Integer 64 | code line |
| pid | 26 | 4 | Integer 32 | code line |
| vname_len | 30 | 2 | Integer 16 | variable name length |
| path_len | 32 | 2 | Integer 16 | code path length |
| alpha | 34 | 256 | Alpha | code path + variable name length , if > 256 alpha[:256] |
- C++ 类
struct E2LScriptLogMessage {
char MsgType;
char logt;
char numt;
std::uint64_t value;
std::uint16_t deci = 0;
std::uint32_t loc = 0;
std::uint64_t ticket_now = 0;
std::uint32_t pid = 0;
std::uint16_t vname_len = 0;
std::uint16_t path_len = 0;
char alpha[DYNAMIC_ALPHA] = {0};
}; /* ---------- end of struct E2LScriptLogMessage ---------- */
typedef struct E2LScriptLogMessage E2LScriptLogMessage;
行情报价API
c++ (部分代码)
/*
* === FUNCTION =============================
*
* Name: DataFormat::init
* -> void *
* Parameters:
* - size_t arg
* Description:
* 初始化订阅行情
* ============================================
*/
void DataFormat::init(char* ptr, std::uint32_t cficode, std::string symbol,
char type)
{
using namespace e2q;
// (network byte order) 结构的转换
e2q::SystemInitMessage sim;
if (ptr == nullptr) {
return;
}
std::size_t stock_len = fldsiz(SystemInitMessage, Stock);
sim.MsgType = e2l_pro_t::INIT;
stock_len--;
sim.CfiCode = cficode;
sim.Itype = type;
sim.Aligned = aligned_t::UNDER;
sim.OfferTime = 0;
if (type == 'i') {
sim.Aligned = aligned_t::PULL;
sim.OfferTime = std::uint32_t(_tick_sleep_time * _deci);
}
std::size_t idx = 0;
*(ptr + idx) = sim.MsgType;
idx++;
memcpy((ptr + idx), symbol.c_str(), stock_len);
idx += stock_len;
idx += serialize_uint_t((ptr + idx), sim.CfiCode);
*(ptr + idx) = sim.Itype;
idx++;
idx += serialize_uint_t((ptr + idx), sim.OfferTime);
*(ptr + idx) = sim.Aligned;
} /* ----- end of function DataFormat::init ----- */
python (代码案例)
- 行情订阅及退出
class BaseMessage:
'''
整数的精度
'''
number_deci = 10000.0
class DataFormatProto(BaseMessage):
'''
Q = uint64
I = uint32
H = uint16
s = Alpha
[x]s = char[x]
fmt = cIH8sIH
'''
def __init__(self, symId=0, tick_time=0.05):
# 自己定义 id
self._symId = symId
# 当前一笔 ticket 报价有多少个 symbol
self._tick_num = 0
self._tick_data = ""
# 间隔报价的时间
self._tick_sleep_time = tick_time
# 转成整数
self._number_deci = BaseMessage.number_deci
self.init_data = SystemInitMessage()
def IndexCfiCode(self):
'''
指数代码
'''
return 0
def thash(self):
'''
cfi code index
'''
self._symId = self._symId + 1
return self._symId
def add_symbol(self, sym):
'''
订阅 symbol
'''
symId = self.thash()
self.init_data.Stock(sym, symId)
data = self.init_data.toString()
# logger.info(symId)
return (symId, data)
def Index(self, index_code):
'''
转成 二进制 char
'''
offer_time = int(self._tick_sleep_time * self._number_deci)
# print(offer_time)
self.init_data.Index(index_code, offer_time)
data = self.init_data.toString()
return data
def pExit(self):
'''
退出
'''
data = struct.pack("!c", MsgType.EXIT)
return data
HUB A Book API
c++ (部分代码)
/*
* ================================
* Class: BaseMatcher
* Description: matcher 基类,不管是A Book (上游柜台) 或 B Book(backtest)
* ================================
*/
class BaseMatcher {
public:
/* ============= LIFECYCLE =================== */
BaseMatcher() = default; /* constructor */
virtual ~BaseMatcher() = default;
/* ============= ACCESSORS =================== */
/* ============= MUTATORS =================== */
virtual void InitSequence() = 0;
virtual SeqType sequence(const FIX::SessionID &, e2::Side) = 0;
// match
virtual std::vector<OrderLots> matcher(std::string symbol, e2::Int_e now,
e2::Int_e price,
e2::Int_e adjprice) = 0;
virtual bool insert(OrderItem *) = 0;
virtual void display() = 0;
virtual e2::Int_e CheckClose(SeqType ticket, const std::string &,
e2::Int_e lots) = 0;
virtual int AddBotTicket(SeqType ticket, e2::OrdType ordType, e2::Side side,
double bot_qty, e2::Int_e symbol) = 0;
virtual bool OrdTypePending() = 0;
virtual void TopLevelPrice(const std::string &symbol, SeqType) = 0;
// broker
virtual double Equity(const FIX::SessionID &, std::size_t,
const char status) = 0;
// virtual void SettlInst(OrderLots &) = 0;
virtual void freeMargin(const FIX::SessionID &, std::size_t, double) = 0;
virtual bool Margin(const FIX::SessionID &, std::size_t, double, long) = 0;
virtual double CheckMargin(const FIX::SessionID &, double, long) = 0;
virtual double traders(const FIX::SessionID &, double) = 0;
virtual void ExdrChange(SeqType, SeqType ticket, double cash, double qty,
std::size_t ctime) = 0;
virtual void exist() = 0;
/* ============= OPERATORS =================== */
protected:
/* ============= METHODS =================== */
/* ============= DATA MEMBERS =================== */
private:
/* ============= METHODS =================== */
/* ============= DATA MEMBERS =================== */
}; /* ----- end of class BaseMatcher ----- */
给 E2Language 语言调用的 API
/*
* === FUNCTION =============================
*
* Name: XXXMatcher
* -> void *
* Parameters:
* - size_t arg
* Description:
*
* ============================================
*/
void XXXMatcher(const char* config)
{
if (GlobalMatcher == nullptr) {
GlobalMatcher = std::make_shared<XXXMatch>(config);
}
} /* ----- end of function XXXMatcher ----- */
/*
* === FUNCTION =============================
*
* Name: CallE2XXX
* -> void *
* Parameters:
* - size_t arg
* Description:
*
* ============================================
*/
void CallE2XXX(std::vector<e2q::E2lFun_t>& funs)
{
AddFunExt(funs, E2XXX, xxxMatcher, 1, "XXXMatcher", E2L_NORETURN, "(config);");
} /* ----- end of function CallE2XXX ----- */
E2LANGUAGE 调用案例
#--------
# Name:function
# Parameters:
# - arg1: xxx
# - arg2: xxx
# -> return
# Description:
#
#--------
func ABook() {
FBrokerBook(UBookType.ABook);
xxx_config = "/opt/TraderXXX/config/XXXSTrade.ini";
XXXMatcher(xxx_config);
}
#----- func end
E2Q Protocol 生成历史记录文件 (e2b)
Python 案例
def BuildE2B(self, stock, symId, df_price, frame):
'''
生成 e2b
'''
if not os.path.isdir(self._e2b_dir):
os.makedirs(self._e2b_dir)
path_name = self._e2b_dir + stock + "_" + str(symId) + ".e2b"
mtickm = MarketTickMessage()
columns = ["open", "low", "high", "close", "volume"]
number = 0
for index, index_row in df_price.iterrows():
unix_time = self.__unixtime(index.value)
index_bar = index_row[columns].values
for idx in range(4):
qty = index_bar[-1]
price = index_bar[idx]
mtickm.UinxTime(unix_time)
mtickm.Stock(frame, qty, price, number, symId)
bin_data = mtickm.toString()
with open(path_name, "ab+") as bin_files:
bin_files.write(bin_data)
number += 1
def __parse(self, bdata):
'''
解释二进制数据
'''
data = {}
idx = 0
_MsgType = struct.unpack_from('!c', bdata, idx)
idx += 1
data['msgtype'] = _MsgType[0]
_CfiCode = struct.unpack_from('!I', bdata, idx)
_CfiCode = _CfiCode[0]
data['cficode'] = _CfiCode
idx += 4
_ticket_now = struct.unpack_from('!Q', bdata, idx)
_ticket_now = _ticket_now[0]
idx += 8
data['ticket_now'] = _ticket_now
_frame = struct.unpack_from('!H', bdata, idx)
_frame = _frame[0]
data['frame'] = _frame
idx += 2
_side = struct.unpack_from('!c', bdata, idx)
data['side'] = _side[0]
idx += 1
price_64 = struct.unpack_from('!6s', bdata, idx)
b64 = b'\x00\x00' + price_64[0]
price = struct.unpack_from('!Q', b64)
data['price'] = price[0]
idx += 6
qty_64 = struct.unpack_from('!6s', bdata, idx)
b64 = b'\x00\x00' + qty_64[0]
qty = struct.unpack_from('!Q', b64)
data['qty'] = qty[0]
idx += 6
_number = struct.unpack_from('!I', bdata, idx)
_number = _number[0]
data['number'] = _number
idx += 4
logger.info(data)
def ReadE2B(self, stock, symId):
path_name = self._e2b_dir + stock + "_" + str(symId) + ".e2b"
tick_len = 33
next_seek = tick_len
with open(path_name, "rb") as bin_files:
while True:
data = bin_files.read(tick_len)
if len(data) == 0:
break
bin_files.seek(next_seek)
self.__parse(data)
next_seek += tick_len