273 lines
7.5 KiB
Python
273 lines
7.5 KiB
Python
from abc import ABC, abstractmethod
|
|
|
|
from vnpy.event import Event, EventEngine
|
|
from .event import (
|
|
EVENT_TICK,
|
|
EVENT_ORDER,
|
|
EVENT_TRADE,
|
|
EVENT_POSITION,
|
|
EVENT_ACCOUNT,
|
|
EVENT_CONTRACT,
|
|
EVENT_LOG,
|
|
EVENT_QUOTE,
|
|
)
|
|
from .object import (
|
|
TickData,
|
|
OrderData,
|
|
TradeData,
|
|
PositionData,
|
|
AccountData,
|
|
ContractData,
|
|
LogData,
|
|
QuoteData,
|
|
OrderRequest,
|
|
CancelRequest,
|
|
SubscribeRequest,
|
|
HistoryRequest,
|
|
QuoteRequest,
|
|
Exchange,
|
|
BarData
|
|
)
|
|
|
|
|
|
class BaseGateway(ABC):
|
|
"""
|
|
Abstract gateway class for creating gateways connection
|
|
to different trading systems.
|
|
|
|
# How to implement a gateway:
|
|
|
|
---
|
|
## Basics
|
|
A gateway should satisfies:
|
|
* this class should be thread-safe:
|
|
* all methods should be thread-safe
|
|
* no mutable shared properties between objects.
|
|
* all methods should be non-blocked
|
|
* satisfies all requirements written in docstring for every method and callbacks.
|
|
* automatically reconnect if connection lost.
|
|
|
|
---
|
|
## methods must implements:
|
|
all @abstractmethod
|
|
|
|
---
|
|
## callbacks must response manually:
|
|
* on_tick
|
|
* on_trade
|
|
* on_order
|
|
* on_position
|
|
* on_account
|
|
* on_contract
|
|
|
|
All the XxxData passed to callback should be constant, which means that
|
|
the object should not be modified after passing to on_xxxx.
|
|
So if you use a cache to store reference of data, use copy.copy to create a new object
|
|
before passing that data into on_xxxx
|
|
|
|
|
|
|
|
"""
|
|
|
|
# Default name for the gateway.
|
|
default_name: str = ""
|
|
|
|
# Fields required in setting dict for connect function.
|
|
default_setting: dict[str, str | int | float | bool] = {}
|
|
|
|
# Exchanges supported in the gateway.
|
|
exchanges: list[Exchange] = []
|
|
|
|
def __init__(self, event_engine: EventEngine, gateway_name: str) -> None:
|
|
""""""
|
|
self.event_engine: EventEngine = event_engine
|
|
self.gateway_name: str = gateway_name
|
|
|
|
def on_event(self, type: str, data: object = None) -> None:
|
|
"""
|
|
General event push.
|
|
"""
|
|
event: Event = Event(type, data)
|
|
self.event_engine.put(event)
|
|
|
|
def on_tick(self, tick: TickData) -> None:
|
|
"""
|
|
Tick event push.
|
|
Tick event of a specific vt_symbol is also pushed.
|
|
"""
|
|
self.on_event(EVENT_TICK, tick)
|
|
self.on_event(EVENT_TICK + tick.vt_symbol, tick)
|
|
|
|
def on_trade(self, trade: TradeData) -> None:
|
|
"""
|
|
Trade event push.
|
|
Trade event of a specific vt_symbol is also pushed.
|
|
"""
|
|
self.on_event(EVENT_TRADE, trade)
|
|
self.on_event(EVENT_TRADE + trade.vt_symbol, trade)
|
|
|
|
def on_order(self, order: OrderData) -> None:
|
|
"""
|
|
Order event push.
|
|
Order event of a specific vt_orderid is also pushed.
|
|
"""
|
|
self.on_event(EVENT_ORDER, order)
|
|
self.on_event(EVENT_ORDER + order.vt_orderid, order)
|
|
|
|
def on_position(self, position: PositionData) -> None:
|
|
"""
|
|
Position event push.
|
|
Position event of a specific vt_symbol is also pushed.
|
|
"""
|
|
self.on_event(EVENT_POSITION, position)
|
|
self.on_event(EVENT_POSITION + position.vt_symbol, position)
|
|
|
|
def on_account(self, account: AccountData) -> None:
|
|
"""
|
|
Account event push.
|
|
Account event of a specific vt_accountid is also pushed.
|
|
"""
|
|
self.on_event(EVENT_ACCOUNT, account)
|
|
self.on_event(EVENT_ACCOUNT + account.vt_accountid, account)
|
|
|
|
def on_quote(self, quote: QuoteData) -> None:
|
|
"""
|
|
Quote event push.
|
|
Quote event of a specific vt_symbol is also pushed.
|
|
"""
|
|
self.on_event(EVENT_QUOTE, quote)
|
|
self.on_event(EVENT_QUOTE + quote.vt_symbol, quote)
|
|
|
|
def on_log(self, log: LogData) -> None:
|
|
"""
|
|
Log event push.
|
|
"""
|
|
self.on_event(EVENT_LOG, log)
|
|
|
|
def on_contract(self, contract: ContractData) -> None:
|
|
"""
|
|
Contract event push.
|
|
"""
|
|
self.on_event(EVENT_CONTRACT, contract)
|
|
|
|
def write_log(self, msg: str) -> None:
|
|
"""
|
|
Write a log event from gateway.
|
|
"""
|
|
log: LogData = LogData(msg=msg, gateway_name=self.gateway_name)
|
|
self.on_log(log)
|
|
|
|
@abstractmethod
|
|
def connect(self, setting: dict) -> None:
|
|
"""
|
|
Start gateway connection.
|
|
|
|
to implement this method, you must:
|
|
* connect to server if necessary
|
|
* log connected if all necessary connection is established
|
|
* do the following query and response corresponding on_xxxx and write_log
|
|
* contracts : on_contract
|
|
* account asset : on_account
|
|
* account holding: on_position
|
|
* orders of account: on_order
|
|
* trades of account: on_trade
|
|
* if any of query above is failed, write log.
|
|
|
|
future plan:
|
|
response callback/change status instead of write_log
|
|
|
|
"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def close(self) -> None:
|
|
"""
|
|
Close gateway connection.
|
|
"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def subscribe(self, req: SubscribeRequest) -> None:
|
|
"""
|
|
Subscribe tick data update.
|
|
"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def send_order(self, req: OrderRequest) -> str:
|
|
"""
|
|
Send a new order to server.
|
|
|
|
implementation should finish the tasks blow:
|
|
* create an OrderData from req using OrderRequest.create_order_data
|
|
* assign a unique(gateway instance scope) id to OrderData.orderid
|
|
* send request to server
|
|
* if request is sent, OrderData.status should be set to Status.SUBMITTING
|
|
* if request is failed to sent, OrderData.status should be set to Status.REJECTED
|
|
* response on_order:
|
|
* return vt_orderid
|
|
|
|
:return str vt_orderid for created OrderData
|
|
"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def cancel_order(self, req: CancelRequest) -> None:
|
|
"""
|
|
Cancel an existing order.
|
|
implementation should finish the tasks blow:
|
|
* send request to server
|
|
"""
|
|
pass
|
|
|
|
def send_quote(self, req: QuoteRequest) -> str:
|
|
"""
|
|
Send a new two-sided quote to server.
|
|
|
|
implementation should finish the tasks blow:
|
|
* create an QuoteData from req using QuoteRequest.create_quote_data
|
|
* assign a unique(gateway instance scope) id to QuoteData.quoteid
|
|
* send request to server
|
|
* if request is sent, QuoteData.status should be set to Status.SUBMITTING
|
|
* if request is failed to sent, QuoteData.status should be set to Status.REJECTED
|
|
* response on_quote:
|
|
* return vt_quoteid
|
|
|
|
:return str vt_quoteid for created QuoteData
|
|
"""
|
|
return ""
|
|
|
|
def cancel_quote(self, req: CancelRequest) -> None:
|
|
"""
|
|
Cancel an existing quote.
|
|
implementation should finish the tasks blow:
|
|
* send request to server
|
|
"""
|
|
return
|
|
|
|
@abstractmethod
|
|
def query_account(self) -> None:
|
|
"""
|
|
Query account balance.
|
|
"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def query_position(self) -> None:
|
|
"""
|
|
Query holding positions.
|
|
"""
|
|
pass
|
|
|
|
def query_history(self, req: HistoryRequest) -> list[BarData]:
|
|
"""
|
|
Query bar history data.
|
|
"""
|
|
return []
|
|
|
|
def get_default_setting(self) -> dict[str, str | int | float | bool]:
|
|
"""
|
|
Return default setting dict.
|
|
"""
|
|
return self.default_setting
|