用于EagleEye3.0 规则集漏报和误报测试的示例项目,项目收集于github和gitee
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

804 lines
24 KiB

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pandas import Timestamp
import pandas as pd
from dataclasses import dataclass, field
from datetime import datetime
from logging import INFO
from typing import Any, Callable
from .constant import (EventType, MSG_TYPE, Exchange, Interval,
OrderType, Direction, Offset, Status, OrderFlag, OrderStatus, ACTIVE_STATUSES, StopOrderStatus,
DIRECTION_CTP2VT, ORDERFALG_2VT, ORDERSTATUS_2VT,
Product, OptionType, SYMBOL_TYPE,
OPTIONTYPE_CTP2VT, PRODUCT_CTP2VT, PRODUCT_VT2SQ, EXCHANGE_CTP2VT)
from ..api.ctp_constant import THOST_FTDC_PT_Net
from .utility import generate_full_symbol, extract_full_symbol, generate_vt_symbol
def retrieve_multiplier_from_full_symbol(symbol=""):
return 1.0
# ################### Begin base class definitions##################
class Event(object):
"""
Base Event class for event-driven system
"""
def __init__(self,
type: EventType = EventType.HEADER,
data: Any = None,
des: str = '',
src: str = '',
msgtype: MSG_TYPE = MSG_TYPE.MSG_TYPE_BASE
):
self.event_type = type
self.data = data
self.destination = des
self.source = src
self.msg_type = msgtype
@property
def type(self):
return self.event_type.name
def serialize(self):
msg = self.destination + '|' + self.source + \
'|' + str(self.msg_type.value)
if self.data:
if type(self.data) == str:
msg = msg + '|' + self.data
else:
try:
msg = msg + '|' + self.data.serialize()
except Exception as e:
print(e)
pass
return msg
def deserialize(self, msg: str):
v = msg.split('|', 3)
try:
self.destination = v[0]
self.source = v[1]
msg2type = MSG_TYPE(int(v[2]))
if msg2type in [MSG_TYPE.MSG_TYPE_TICK, MSG_TYPE.MSG_TYPE_TICK_L1, MSG_TYPE.MSG_TYPE_TICK_L5]:
self.event_type = EventType.TICK
self.data = TickData(gateway_name=self.source)
self.data.deserialize(v[3])
elif msg2type == MSG_TYPE.MSG_TYPE_RTN_ORDER:
self.event_type = EventType.ORDERSTATUS
self.data = OrderData(gateway_name=self.source)
self.data.deserialize(v[3])
elif msg2type == MSG_TYPE.MSG_TYPE_RTN_TRADE:
self.event_type = EventType.FILL
self.data = TradeData(gateway_name=self.source)
self.data.deserialize(v[3])
elif msg2type == MSG_TYPE.MSG_TYPE_RSP_POS:
self.event_type = EventType.POSITION
self.data = PositionData(gateway_name=self.source)
self.data.deserialize(v[3])
elif msg2type == MSG_TYPE.MSG_TYPE_BAR:
self.event_type = EventType.BAR
self.data = BarData(gateway_name=self.source)
self.data.deserialize(v[3])
elif msg2type == MSG_TYPE.MSG_TYPE_RSP_ACCOUNT:
self.event_type = EventType.ACCOUNT
self.data = AccountData(gateway_name=self.source)
self.data.deserialize(v[3])
elif msg2type == MSG_TYPE.MSG_TYPE_RSP_CONTRACT:
self.event_type = EventType.CONTRACT
self.data = ContractData(gateway_name=self.source)
self.data.deserialize(v[3])
elif v[2].startswith('11'):
self.event_type = EventType.ENGINE_CONTROL
self.msg_type = msg2type
if len(v) > 3:
self.data = v[3]
elif v[2].startswith('12'):
self.event_type = EventType.STRATEGY_CONTROL
self.msg_type = msg2type
if len(v) > 3:
self.data = v[3]
elif v[2].startswith('14'):
self.event_type = EventType.RECORDER_CONTROL
self.msg_type = msg2type
if len(v) > 3:
self.data = v[3]
elif v[2].startswith('3'): # msg2type == MSG_TYPE.MSG_TYPE_INFO:
self.event_type = EventType.INFO
self.msg_type = msg2type
self.data = LogData(gateway_name=self.source)
self.data.deserialize(v[3])
else:
self.event_type = EventType.HEADER
self.msg_type = msg2type
except Exception as e:
print(e)
pass
@dataclass
class BaseData:
"""
Any data object needs a gateway_name as source
and should inherit base data.
"""
gateway_name: str = ''
def serialize(self):
pass
def deserialize(self, msg):
pass
@dataclass
class TickData(BaseData):
"""
Tick data contains information about:
* last trade in market
* orderbook snapshot
* intraday market statistics.
"""
symbol: str = ""
exchange: Exchange = Exchange.SHFE
datetime: datetime = datetime(2019, 1, 1)
name: str = ""
volume: float = 0
last_price: float = 0
last_volume: float = 0
limit_up: float = 0
limit_down: float = 0
open_price: float = 0
high_price: float = 0
low_price: float = 0
pre_close: float = 0
bid_price_1: float = 0
bid_price_2: float = 0
bid_price_3: float = 0
bid_price_4: float = 0
bid_price_5: float = 0
ask_price_1: float = 0
ask_price_2: float = 0
ask_price_3: float = 0
ask_price_4: float = 0
ask_price_5: float = 0
bid_volume_1: float = 0
bid_volume_2: float = 0
bid_volume_3: float = 0
bid_volume_4: float = 0
bid_volume_5: float = 0
ask_volume_1: float = 0
ask_volume_2: float = 0
ask_volume_3: float = 0
ask_volume_4: float = 0
ask_volume_5: float = 0
# StarQuant unique field
depth: int = 0
open_interest: float = 0
def __post_init__(self):
""""""
self.vt_symbol = f"{self.symbol}.{self.exchange.value}"
self.timestamp = Timestamp(self.datetime)
self.full_symbol = generate_full_symbol(self.exchange, self.symbol)
def deserialize(self, msg: str):
try:
v = msg.split('|')
self.full_symbol = v[0]
self.timestamp = pd.to_datetime(v[1])
self.datetime = self.timestamp.to_pydatetime()
self.symbol, self.exchange = extract_full_symbol(self.full_symbol)
self.vt_symbol = generate_vt_symbol(self.symbol, self.exchange)
self.last_price = float(v[2])
self.volume = int(v[3])
if (len(v) < 17):
self.depth = 1
self.bid_price_1 = float(v[4])
self.bid_volume_1 = int(v[5])
self.ask_price_1 = float(v[6])
self.ask_volume_1 = int(v[7])
self.open_interest = int(v[8])
self.open_price = float(v[9])
self.high_price = float(v[10])
self.low_price = float(v[11])
self.pre_close = float(v[12])
self.limit_up = float(v[13])
self.limit_down = float(v[14])
else:
self.depth = 5
self.bid_price_1 = float(v[4])
self.bid_volume_1 = int(v[5])
self.ask_price_1 = float(v[6])
self.ask_volume_1 = int(v[7])
self.bid_price_2 = float(v[8])
self.bid_volume_2 = int(v[9])
self.ask_price_2 = float(v[10])
self.ask_volume_2 = int(v[11])
self.bid_price_3 = float(v[12])
self.bid_volume_3 = int(v[13])
self.ask_price_3 = float(v[14])
self.ask_volume_3 = int(v[15])
self.bid_price_4 = float(v[16])
self.bid_volume_4 = int(v[17])
self.ask_price_4 = float(v[18])
self.ask_volume_4 = int(v[19])
self.bid_price_5 = float(v[20])
self.bid_volume_5 = int(v[21])
self.ask_price_5 = float(v[22])
self.ask_volume_5 = int(v[23])
self.open_interest = int(v[24])
self.open_price = float(v[25])
self.high_price = float(v[26])
self.low_price = float(v[27])
self.pre_close = float(v[28])
self.limit_up = float(v[29])
self.limit_down = float(v[30])
except Exception as e:
print(e)
pass
@dataclass
class BarData(BaseData):
"""
Candlestick bar data of a certain trading period.
"""
symbol: str = ''
exchange: Exchange = Exchange.SHFE
datetime: datetime = datetime(2019, 1, 1)
interval: Interval = None
volume: float = 0
open_price: float = 0
high_price: float = 0
low_price: float = 0
close_price: float = 0
adj_close_price: float = 0.0
open_interest: int = 0
def __post_init__(self):
""""""
self.vt_symbol = f"{self.symbol}.{self.exchange.value}"
self.full_symbol = generate_full_symbol(self.exchange, self.symbol)
self.bar_start_time = pd.Timestamp(self.datetime)
@dataclass
class OrderData(BaseData):
"""
Order data contains information for tracking lastest status
of a specific order.
"""
symbol: str = ""
exchange: Exchange = Exchange.SHFE
orderid: str = ""
type: OrderType = OrderType.LMT
direction: Direction = Direction.LONG
offset: Offset = Offset.NONE
price: float = 0
volume: int = 0
traded: int = 0
status: Status = Status.SUBMITTING
time: str = ""
# StarQuant unique field
api: str = ""
account: str = ""
clientID: int = -1
client_order_id: int = -1
tag: str = ""
full_symbol: str = ""
flag: OrderFlag = OrderFlag.OPEN
server_order_id: int = -1
broker_order_id: int = -1
orderNo: str = ""
localNo: str = ""
create_time: str = ""
update_time: str = ""
orders_status: OrderStatus = OrderStatus.SUBMITTED
orderfield: Any = None
def __post_init__(self):
""""""
if self.full_symbol:
self.symbol, self.exchange = extract_full_symbol(self.full_symbol)
self.vt_symbol = f"{self.symbol}.{self.exchange.value}"
self.vt_orderid = f"{self.gateway_name}.{self.orderid}"
self.tag = str(self.type.value)
def is_active(self):
"""
Check if the order is active.
"""
if self.status in ACTIVE_STATUSES:
return True
else:
return False
def create_cancel_request(self):
"""
Create cancel request object from order.
"""
req = CancelRequest(
clientID=self.clientID,
client_order_id=self.client_order_id,
server_order_id=self.server_order_id
)
return req
def deserialize(self, msg: str):
v = msg.split('|')
try:
self.api = v[0]
self.account = v[1]
self.clientID = int(v[2])
self.client_order_id = int(v[3])
self.tag = v[4]
self.type = OrderType(int(v[4].split(':')[0]))
self.full_symbol = v[5]
self.symbol, self.exchange = extract_full_symbol(self.full_symbol)
self.vt_symbol = generate_vt_symbol(self.symbol, self.exchange)
self.price = float(v[6])
self.volume = int(v[7])
if self.volume < 0:
self.direction = Direction.SHORT
self.volume = -1 * self.volume
self.traded = abs(int(v[8]))
self.flag = OrderFlag(int(v[9]))
self.offset = ORDERFALG_2VT[self.flag]
self.server_order_id = int(v[10])
self.broker_order_id = int(v[11])
self.orderNo = v[12]
self.localNo = v[13]
self.orderid = self.localNo
self.vt_orderid = f"{self.gateway_name}.{self.orderid}"
self.create_time = v[14]
self.update_time = v[15]
self.time = self.update_time
self.order_status = OrderStatus(int(v[16]))
self.status = ORDERSTATUS_2VT[self.order_status]
except Exception as e:
print(e)
pass
def serialize(self):
msg = str(self.api
+ '|' + self.account
+ '|' + str(self.clientID)
+ '|' + str(self.client_order_id)
+ '|' + self.tag)
if (self.orderfield):
msg = msg + '|' + self.orderfield.serialize()
return msg
@dataclass
class CtpOrderField(object):
InstrumentID: str = ''
OrderPriceType: str = ''
Direction: str = ''
CombOffsetFlag: str = ''
CombHedgeFlag: str = ''
LimitPrice: float = 0.0
VolumeTotalOriginal: int = 0
TimeCondition: str = ''
GTDDate: str = ''
VolumeCondition: str = ''
MinVolume: int = 0
ContingentCondition: str = ''
StopPrice: float = 0.0
ForceCloseReason: str = '0'
IsAutoSuspend: int = 0
UserForceClose: int = 0
IsSwapOrder: int = 0
BusinessUnit: str = ''
CurrencyID: str = ''
def serialize(self):
msg = str(self.InstrumentID
+ '|' + self.OrderPriceType
+ '|' + self.Direction
+ '|' + self.CombOffsetFlag
+ '|' + self.CombHedgeFlag
+ '|' + str(self.LimitPrice)
+ '|' + str(self.VolumeTotalOriginal)
+ '|' + self.TimeCondition
+ '|' + self.GTDDate
+ '|' + self.VolumeCondition
+ '|' + str(self.MinVolume)
+ '|' + self.ContingentCondition
+ '|' + str(self.StopPrice)
+ '|' + self.ForceCloseReason
+ '|' + str(self.IsAutoSuspend)
+ '|' + str(self.UserForceClose)
+ '|' + str(self.IsSwapOrder)
+ '|' + self.BusinessUnit
+ '|' + self.CurrencyID)
return msg
@dataclass
class PaperOrderField(object):
order_type: OrderType = OrderType.MKT
full_symbol: str = ''
order_flag: OrderFlag = OrderFlag.OPEN
limit_price: float = 0.0
stop_price: float = 0.0
order_size: int = 0
def serialize(self):
msg = str(str(self.order_type.value)
+ '|' + self.full_symbol
+ '|' + str(self.order_flag.value)
+ '|' + str(self.order_size)
+ '|' + str(self.limit_price)
+ '|' + str(self.stop_price))
return msg
@dataclass
class TradeData(BaseData):
"""
Trade data contains information of a fill of an order. One order
can have several trade fills.
"""
symbol: str = ""
exchange: Exchange = Exchange.SHFE
orderid: str = ""
tradeid: str = ""
direction: Direction = ""
offset: Offset = Offset.NONE
price: float = 0
volume: float = 0
time: str = ""
# StarQuant field
server_order_id: int = -1
client_order_id: int = -1
clientID: int = -1
localNo: str = ""
orderNo: str = ""
full_symbol: str = ""
fill_flag: OrderFlag = OrderFlag.OPEN
commission: float = 0.0
account: str = ""
api: str = ""
datetime: datetime = datetime(1990, 1, 1)
# Backtest use
commission: float = 0.0
slippage: float = 0.0
turnover: float = 0.0
long_pos: int = 0
long_price: float = 0
long_pnl: float = 0
short_pos: int = 0
short_price: float = 0
short_pnl: float = 0
def __post_init__(self):
""""""
self.vt_symbol = f"{self.symbol}.{self.exchange.value}"
self.vt_orderid = f"{self.gateway_name}.{self.orderid}"
self.vt_tradeid = f"{self.gateway_name}.{self.tradeid}"
def deserialize(self, msg: str):
v = msg.split('|')
try:
self.server_order_id = int(v[0])
self.client_order_id = int(v[1])
self.clientID = int(v[2])
self.localNo = v[3]
self.orderid = self.localNo
self.vt_orderid = f"{self.gateway_name}.{self.orderid}"
self.orderNo = v[4]
self.tradeid = v[5]
self.vt_tradeid = f"{self.gateway_name}.{self.tradeid}"
self.time = v[6]
self.full_symbol = v[7]
self.symbol, self.exchange = extract_full_symbol(self.full_symbol)
self.vt_symbol = f"{self.symbol}.{self.exchange.value}"
self.price = float(v[8])
quantity = int(v[9])
self.volume = abs(quantity)
self.direction = Direction.LONG if quantity > 0 else Direction.SHORT
self.fill_flag = OrderFlag(int(v[10]))
self.offset = ORDERFALG_2VT[self.fill_flag]
self.commission = float(v[11])
self.account = v[12]
self.api = v[13]
except Exception as e:
print(e)
pass
@dataclass
class PositionData(BaseData):
"""
Positon data is used for tracking each individual position holding.
"""
symbol: str = ""
exchange: Exchange = Exchange.SHFE
direction: Direction = Direction.LONG
volume: float = 0
frozen: float = 0
price: float = 0
pnl: float = 0
yd_volume: float = 0
# StarQuant field
key: str = ""
account: str = ""
api: str = ""
full_symbol: str = ""
realized_pnl: float = 0
timestamp: str = ""
def __post_init__(self):
""""""
self.vt_symbol = f"{self.symbol}.{self.exchange.value}"
self.vt_positionid = f"{self.vt_symbol}.{self.direction}"
def deserialize(self, msg: str):
v = msg.split('|')
try:
self.key = v[0]
self.account = v[1]
self.api = v[2]
self.full_symbol = v[3]
self.symbol, self.exchange = extract_full_symbol(self.full_symbol)
self.vt_symbol = f"{self.symbol}.{self.exchange.value}"
self.direction = DIRECTION_CTP2VT[v[4]]
self.price = float(v[5])
self.vt_positionid = f"{self.vt_symbol}.{self.direction}"
self.volume = abs(int(v[6]))
self.yd_volume = abs(int(v[7]))
self.freezed_size = abs(int(v[8]))
self.realized_pnl = float(v[9])
self.pnl = float(v[10])
self.timestamp = v[11]
except Exception as e:
print(e)
pass
BacktestTradeData = TradeData
@dataclass
class AccountData(BaseData):
"""
Account data contains information about balance, frozen and
available.
"""
accountid: str = ""
balance: float = 0
frozen: float = 0
# StarQuant field
yd_balance: float = 0
netliquid: float = 0
commission: float = 0
margin: float = 0
closed_pnl: float = 0
open_pnl: float = 0
timestamp: str = ""
def __post_init__(self):
""""""
self.available = self.balance - self.frozen
self.vt_accountid = f"{self.gateway_name}.{self.accountid}"
def deserialize(self, msg: str):
v = msg.split('|')
try:
self.accountid = v[0]
self.vt_accountid = f"{self.gateway_name}.{self.accountid}"
self.yd_balance = float(v[1])
self.netliquid = float(v[2])
self.available = float(v[3])
self.commission = float(v[4])
self.margin = float(v[5])
self.closed_pnl = float(v[6])
self.open_pnl = float(v[7])
self.balance = float(v[8])
self.frozen = float(v[9])
self.timestamp = v[10]
except Exception as e:
print(e)
pass
@dataclass
class LogData(BaseData):
"""
Log data is used for recording log messages on GUI or in log files.
"""
msg: str = ''
level: int = INFO
# StarQuant field
timestamp: str = ''
def __post_init__(self):
""""""
self.time = datetime.now()
def deserialize(self, msg: str):
v = msg.split('|')
try:
self.msg = v[0]
self.timestamp = v[1]
except Exception as e:
print(e)
pass
@dataclass
class ContractData(BaseData):
"""
Contract data contains basic information about each contract traded.
"""
symbol: str = ""
exchange: Exchange = Exchange.SHFE
name: str = ""
product: Product = Product.FUTURES
size: int = 1
pricetick: float = 0
min_volume: float = 1 # minimum trading volume of the contract
stop_supported: bool = False # whether server supports stop order
net_position: bool = False # whether gateway uses net position volume
option_strike: float = 0
option_underlying: str = "" # vt_symbol of underlying contract
option_type: OptionType = None
option_expiry: datetime = None
# StarQuant field
full_symbol: str = ""
long_margin_ratio: float = 0
short_margin_ratio: float = 0
def __post_init__(self):
""""""
self.vt_symbol = f"{self.symbol}.{self.exchange.value}"
def deserialize(self, msg: str):
v = msg.split('|')
try:
self.symbol = v[0]
self.exchange = EXCHANGE_CTP2VT[v[1]]
self.vt_symbol = f"{self.symbol}.{self.exchange.value}"
self.name = v[2]
self.product = PRODUCT_CTP2VT.get(v[3], None)
st = PRODUCT_VT2SQ[self.product]
self.full_symbol = generate_full_symbol(
self.exchange, self.symbol, st)
self.size = int(v[4])
self.pricetick = float(v[5])
if v[6] == THOST_FTDC_PT_Net:
self.net_position = True
self.long_margin_ratio = float(v[7])
self.short_margin_ratio = float(v[8])
if self.product == Product.OPTION:
self.option_underlying = v[9]
self.option_type = OPTIONTYPE_CTP2VT.get(v[10], None)
self.option_strike = float(v[11])
self.option_expiry = datetime.strptime(v[12], "%Y%m%d")
except Exception as e:
print(e)
pass
# product = PRODUCT_CTP2VT.get(data["ProductClass"], None)
# if product:
# OPTIONTYPE_CTP2VT.get(data["OptionsType"], None),
@dataclass
class StopOrder:
full_symbol: str
direction: Direction
offset: Offset
price: float
volume: float
stop_orderid: str
strategy_name: str
lock: bool = False
vt_orderids: list = field(default_factory=list)
status: StopOrderStatus = StopOrderStatus.WAITING
@dataclass
class QryContractRequest:
"""
qry security
"""
sym_type: SYMBOL_TYPE = SYMBOL_TYPE.FULL
content: str = ''
def serialize(self):
msg = str(self.sym_type.value) + '|' + self.content
return msg
@dataclass
class SubscribeRequest:
"""
subscribe
"""
sym_type: SYMBOL_TYPE = SYMBOL_TYPE.FULL
content: str = ''
def serialize(self):
msg = str(self.sym_type.value) + '|' + self.content
return msg
@dataclass
class CancelRequest:
"""
Request sending to specific gateway for canceling an existing order.
"""
clientID: int = 0
client_order_id: int = 0
server_order_id: int = 0
def serialize(self):
msg = str(self.clientID) + '|' + str(self.client_order_id) + \
'|' + str(self.server_order_id)
return msg
OrderRequest = OrderData
CancelAllRequest = SubscribeRequest
@dataclass
class HistoryRequest:
"""
Request sending to specific gateway for querying history data.
"""
symbol: str
exchange: Exchange
start: datetime
end: datetime = None
interval: Interval = None
def __post_init__(self):
""""""
self.vt_symbol = f"{self.symbol}.{self.exchange.value}"