[Add] Complete ib gateway development
This commit is contained in:
parent
84eaeb8733
commit
64c1c3ccde
@ -1,2 +1,2 @@
|
|||||||
pyqt
|
PyQt5
|
||||||
qdarkstyle
|
qdarkstyle
|
@ -1,8 +1,11 @@
|
|||||||
from vnpy.event import EventEngine
|
from vnpy.event import EventEngine
|
||||||
from vnpy.trader.engine import MainEngine
|
from vnpy.trader.engine import MainEngine
|
||||||
from vnpy.trader.ui import MainWindow, create_qapp
|
from vnpy.trader.ui import MainWindow, create_qapp
|
||||||
|
from vnpy.gateway.ib import IbGateway
|
||||||
|
|
||||||
from vnpy.trader.ui.widget import TickMonitor
|
import os
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
@ -10,7 +13,9 @@ def main():
|
|||||||
qapp = create_qapp()
|
qapp = create_qapp()
|
||||||
|
|
||||||
event_engine = EventEngine()
|
event_engine = EventEngine()
|
||||||
|
|
||||||
main_engine = MainEngine(event_engine)
|
main_engine = MainEngine(event_engine)
|
||||||
|
main_engine.add_gateway(IbGateway)
|
||||||
|
|
||||||
main_window = MainWindow(main_engine, event_engine)
|
main_window = MainWindow(main_engine, event_engine)
|
||||||
main_window.showMaximized()
|
main_window.showMaximized()
|
||||||
|
0
vnpy/gateway/__init__.py
Normal file
0
vnpy/gateway/__init__.py
Normal file
@ -1 +1 @@
|
|||||||
from ib_gateway import IbGateway
|
from .ib_gateway import IbGateway
|
@ -3,15 +3,19 @@ Please install ibapi from Interactive Brokers github page.
|
|||||||
"""
|
"""
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from copy import copy
|
from copy import copy
|
||||||
|
from threading import Thread
|
||||||
|
from queue import Empty
|
||||||
|
|
||||||
from ibapi.wrapper import EWrapper
|
from ibapi.wrapper import EWrapper
|
||||||
from ibapi.client import EClient
|
from ibapi.client import EClient
|
||||||
from ibapi.contract import Contract, ContractDetails
|
from ibapi.contract import Contract, ContractDetails
|
||||||
from ibapi.order import Order
|
from ibapi.order import Order
|
||||||
from ibapi.common import TickerId, OrderId, TickAttrib
|
from ibapi.common import TickerId, OrderId, TickAttrib, MAX_MSG_LEN
|
||||||
from ibapi.ticktype import TickType
|
from ibapi.ticktype import TickType
|
||||||
from ibapi.order_state import OrderState
|
from ibapi.order_state import OrderState
|
||||||
from ibapi.execution import Execution
|
from ibapi.execution import Execution
|
||||||
|
from ibapi.utils import BadMessage
|
||||||
|
from ibapi import comm
|
||||||
|
|
||||||
from vnpy.trader.gateway import BaseGateway
|
from vnpy.trader.gateway import BaseGateway
|
||||||
from vnpy.trader.object import (
|
from vnpy.trader.object import (
|
||||||
@ -62,7 +66,9 @@ PRICETYPE_VT2IB = {PRICETYPE_LIMIT: "LMT", PRICETYPE_MARKET: "MKT"}
|
|||||||
PRICETYPE_IB2VT = {v: k for k, v in PRICETYPE_VT2IB.items()}
|
PRICETYPE_IB2VT = {v: k for k, v in PRICETYPE_VT2IB.items()}
|
||||||
|
|
||||||
DIRECTION_VT2IB = {DIRECTION_LONG: "BUY", DIRECTION_SHORT: "SELL"}
|
DIRECTION_VT2IB = {DIRECTION_LONG: "BUY", DIRECTION_SHORT: "SELL"}
|
||||||
DIRECTION_IB2VT = {v: k for k, v in DIRECTION_IB2VT.items()}
|
DIRECTION_IB2VT = {v: k for k, v in DIRECTION_VT2IB.items()}
|
||||||
|
DIRECTION_IB2VT["BOT"] = DIRECTION_LONG
|
||||||
|
DIRECTION_IB2VT["SLD"] = DIRECTION_SHORT
|
||||||
|
|
||||||
EXCHANGE_VT2IB = {
|
EXCHANGE_VT2IB = {
|
||||||
EXCHANGE_SMART: "SMART",
|
EXCHANGE_SMART: "SMART",
|
||||||
@ -91,7 +97,7 @@ PRODUCT_VT2IB = {
|
|||||||
PRODUCT_OPTION: "OPT",
|
PRODUCT_OPTION: "OPT",
|
||||||
PRODUCT_FUTURES: "FUT"
|
PRODUCT_FUTURES: "FUT"
|
||||||
}
|
}
|
||||||
PRODUCT_IB2VT = {v: k for k,v in PRODUCT_VT2IB.items()}
|
PRODUCT_IB2VT = {v: k for k, v in PRODUCT_VT2IB.items()}
|
||||||
|
|
||||||
OPTION_VT2IB = {OPTION_CALL: "CALL", OPTION_PUT: "PUT"}
|
OPTION_VT2IB = {OPTION_CALL: "CALL", OPTION_PUT: "PUT"}
|
||||||
|
|
||||||
@ -123,15 +129,11 @@ ACCOUNTFIELD_IB2VT = {
|
|||||||
class IbGateway(BaseGateway):
|
class IbGateway(BaseGateway):
|
||||||
""""""
|
""""""
|
||||||
|
|
||||||
default_setting = {
|
default_setting = {"host": "127.0.0.1", "port": 7497, "clientid": 1}
|
||||||
"host": "127.0.0.1",
|
|
||||||
"port": 7497,
|
|
||||||
"clientid": 1
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, event_engine):
|
def __init__(self, event_engine):
|
||||||
""""""
|
""""""
|
||||||
super(Gateway, self).__init__(event_engine, "IB")
|
super(IbGateway, self).__init__(event_engine, "IB")
|
||||||
|
|
||||||
self.api = IbApi(self)
|
self.api = IbApi(self)
|
||||||
|
|
||||||
@ -181,7 +183,7 @@ class IbGateway(BaseGateway):
|
|||||||
class IbApi(EWrapper):
|
class IbApi(EWrapper):
|
||||||
""""""
|
""""""
|
||||||
|
|
||||||
def __init__(self, gateway: Gateway):
|
def __init__(self, gateway: BaseGateway):
|
||||||
""""""
|
""""""
|
||||||
super(IbApi, self).__init__()
|
super(IbApi, self).__init__()
|
||||||
|
|
||||||
@ -189,15 +191,36 @@ class IbApi(EWrapper):
|
|||||||
self.gateway_name = gateway.gateway_name
|
self.gateway_name = gateway.gateway_name
|
||||||
|
|
||||||
self.status = False
|
self.status = False
|
||||||
|
|
||||||
self.reqid = 0
|
self.reqid = 0
|
||||||
self.orderid = 0
|
self.orderid = 0
|
||||||
self.clientid = 0
|
self.clientid = 0
|
||||||
self.ticks = {}
|
self.ticks = {}
|
||||||
self.orders = {}
|
self.orders = {}
|
||||||
self.accounts = {}
|
self.accounts = {}
|
||||||
self.contracts = {}
|
|
||||||
|
|
||||||
self.client = EClient(self)
|
self.tick_exchange = {}
|
||||||
|
|
||||||
|
self.client = IbClient(self)
|
||||||
|
self.thread = Thread(target=self.client.run)
|
||||||
|
|
||||||
|
def connectAck(self):
|
||||||
|
"""
|
||||||
|
Callback when connection is established.
|
||||||
|
"""
|
||||||
|
self.status = True
|
||||||
|
|
||||||
|
log = LogData(msg="IB TWS连接成功", gateway_name=self.gateway_name)
|
||||||
|
self.gateway.on_log(log)
|
||||||
|
|
||||||
|
def connectionClosed(self):
|
||||||
|
"""
|
||||||
|
Callback when connection is closed.
|
||||||
|
"""
|
||||||
|
self.status = False
|
||||||
|
|
||||||
|
log = LogData(msg="IB TWS连接断开", gateway_name=self.gateway_name)
|
||||||
|
self.gateway.on_log(log)
|
||||||
|
|
||||||
def nextValidId(self, orderId: int):
|
def nextValidId(self, orderId: int):
|
||||||
"""
|
"""
|
||||||
@ -213,12 +236,10 @@ class IbApi(EWrapper):
|
|||||||
"""
|
"""
|
||||||
super(IbApi, self).currentTime(time)
|
super(IbApi, self).currentTime(time)
|
||||||
|
|
||||||
self.status = True
|
|
||||||
|
|
||||||
dt = datetime.fromtimestamp(time)
|
dt = datetime.fromtimestamp(time)
|
||||||
time_string = dt.strftime("%Y-%m-%d %H:%M:%S.%f")
|
time_string = dt.strftime("%Y-%m-%d %H:%M:%S.%f")
|
||||||
log = LogData(
|
log = LogData(
|
||||||
msg=f"IB TWS连接成功,服务器时间: {time_string}",
|
msg=f"服务器时间: {time_string}",
|
||||||
gateway_name=self.gateway_name
|
gateway_name=self.gateway_name
|
||||||
)
|
)
|
||||||
self.gateway.on_log(log)
|
self.gateway.on_log(log)
|
||||||
@ -229,7 +250,7 @@ class IbApi(EWrapper):
|
|||||||
"""
|
"""
|
||||||
super(IbApi, self).error(reqId, errorCode, errorString)
|
super(IbApi, self).error(reqId, errorCode, errorString)
|
||||||
log = LogData(
|
log = LogData(
|
||||||
msg=f"发生错误,错误代码:{errorCode},错误信息: {errorString}",
|
msg=f"信息通知,代码:{errorCode},内容: {errorString}",
|
||||||
gateway_name=self.gateway_name
|
gateway_name=self.gateway_name
|
||||||
)
|
)
|
||||||
self.gateway.on_log(log)
|
self.gateway.on_log(log)
|
||||||
@ -253,10 +274,10 @@ class IbApi(EWrapper):
|
|||||||
name = TICKFIELD_IB2VT[tickType]
|
name = TICKFIELD_IB2VT[tickType]
|
||||||
setattr(tick, name, price)
|
setattr(tick, name, price)
|
||||||
|
|
||||||
# Forex and spot product of IB has no tick time and last price.
|
# Forex and spot product of IDEALPRO has no tick time and last price.
|
||||||
# We need to calculate locally.
|
# We need to calculate locally.
|
||||||
contract = self.contracts[reqId]
|
exchange = self.tick_exchange[reqId]
|
||||||
if contract.product in (PRODUCT_FOREX, PRODUCT_SPOT):
|
if exchange == EXCHANGE_IDEALPRO:
|
||||||
tick.last_price = (tick.bid_price_1 + tick.ask_price_1) / 2
|
tick.last_price = (tick.bid_price_1 + tick.ask_price_1) / 2
|
||||||
tick.datetime = datetime.now()
|
tick.datetime = datetime.now()
|
||||||
self.gateway.on_tick(copy(tick))
|
self.gateway.on_tick(copy(tick))
|
||||||
@ -323,7 +344,7 @@ class IbApi(EWrapper):
|
|||||||
)
|
)
|
||||||
|
|
||||||
orderid = str(orderId)
|
orderid = str(orderId)
|
||||||
order = self.orders.get[orderid]
|
order = self.orders.get(orderid, None)
|
||||||
order.status = STATUS_IB2VT[status]
|
order.status = STATUS_IB2VT[status]
|
||||||
order.traded = filled
|
order.traded = filled
|
||||||
|
|
||||||
@ -349,12 +370,13 @@ class IbApi(EWrapper):
|
|||||||
ib_contract.exchange
|
ib_contract.exchange
|
||||||
),
|
),
|
||||||
orderid=orderid,
|
orderid=orderid,
|
||||||
direction=DIRECTION_IB2V[ib_order.action],
|
direction=DIRECTION_IB2VT[ib_order.action],
|
||||||
price=order.lmtPrice,
|
price=ib_order.lmtPrice,
|
||||||
volume=order.totalQuantity,
|
volume=ib_order.totalQuantity,
|
||||||
gateway_name=self.gateway_name
|
gateway_name=self.gateway_name
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.orders[orderid] = order
|
||||||
self.gateway.on_order(copy(order))
|
self.gateway.on_order(copy(order))
|
||||||
|
|
||||||
def updateAccountValue(
|
def updateAccountValue(
|
||||||
@ -379,6 +401,7 @@ class IbApi(EWrapper):
|
|||||||
accountid=accountid,
|
accountid=accountid,
|
||||||
gateway_name=self.gateway_name
|
gateway_name=self.gateway_name
|
||||||
)
|
)
|
||||||
|
self.accounts[accountid] = account
|
||||||
|
|
||||||
name = ACCOUNTFIELD_IB2VT[key]
|
name = ACCOUNTFIELD_IB2VT[key]
|
||||||
setattr(account, name, float(val))
|
setattr(account, name, float(val))
|
||||||
@ -426,7 +449,6 @@ class IbApi(EWrapper):
|
|||||||
Callback of account update time.
|
Callback of account update time.
|
||||||
"""
|
"""
|
||||||
super(IbApi, self).updateAccountTime(timeStamp)
|
super(IbApi, self).updateAccountTime(timeStamp)
|
||||||
|
|
||||||
for account in self.accounts.values():
|
for account in self.accounts.values():
|
||||||
self.gateway.on_account(copy(account))
|
self.gateway.on_account(copy(account))
|
||||||
|
|
||||||
@ -435,6 +457,7 @@ class IbApi(EWrapper):
|
|||||||
Callback of contract data update.
|
Callback of contract data update.
|
||||||
"""
|
"""
|
||||||
super(IbApi, self).contractDetails(reqId, contractDetails)
|
super(IbApi, self).contractDetails(reqId, contractDetails)
|
||||||
|
|
||||||
ib_symbol = contractDetails.contract.conId
|
ib_symbol = contractDetails.contract.conId
|
||||||
ib_exchange = contractDetails.contract.exchange
|
ib_exchange = contractDetails.contract.exchange
|
||||||
ib_size = contractDetails.contract.multiplier
|
ib_size = contractDetails.contract.multiplier
|
||||||
@ -445,30 +468,32 @@ class IbApi(EWrapper):
|
|||||||
exchange=EXCHANGE_IB2VT.get(ib_exchange,
|
exchange=EXCHANGE_IB2VT.get(ib_exchange,
|
||||||
ib_exchange),
|
ib_exchange),
|
||||||
name=contractDetails.longName,
|
name=contractDetails.longName,
|
||||||
product=PRODUCT_VT2IB[ib_product]
|
product=PRODUCT_IB2VT[ib_product],
|
||||||
size=ib_size,
|
size=ib_size,
|
||||||
pricetick=contractDetails.minTick,
|
pricetick=contractDetails.minTick,
|
||||||
gateway_name=self.gateway_name
|
gateway_name=self.gateway_name
|
||||||
)
|
)
|
||||||
self.gateway.on_contract(contract)
|
self.gateway.on_contract(contract)
|
||||||
|
|
||||||
def execDetails(self, reqId: int, contract: Contract, execution: Execution):
|
def execDetails(self, reqId: int, contract: Contract, execution: Execution):
|
||||||
"""
|
"""
|
||||||
Callback of trade data update.
|
Callback of trade data update.
|
||||||
"""
|
"""
|
||||||
super(IbApi, self).execDetails(reqId, contract, execution)
|
super(IbApi, self).execDetails(reqId, contract, execution)
|
||||||
|
|
||||||
today_date = datetime.now().strftime("%Y%m%d")
|
today_date = datetime.now().strftime("%Y%m%d")
|
||||||
trade = TradeData(
|
trade = TradeData(
|
||||||
symbol=contract.conId,
|
symbol=contract.conId,
|
||||||
exchange=EXCHANGE_IB2VT.get(contract.exchange, contract.exchange)
|
exchange=EXCHANGE_IB2VT.get(contract.exchange,
|
||||||
orderid = str(execution.orderId),
|
contract.exchange),
|
||||||
|
orderid=str(execution.orderId),
|
||||||
tradeid=str(execution.execId),
|
tradeid=str(execution.execId),
|
||||||
direction=DIRECTION_IB2VT[execution.side],
|
direction=DIRECTION_IB2VT[execution.side],
|
||||||
price=execution.price,
|
price=execution.price,
|
||||||
volume=execution.shares,
|
volume=execution.shares,
|
||||||
time=datetime.strptime(f"{today_date} {execution.time}", "%Y%m%d %H:%M:%S"),
|
time=datetime.strptime(execution.time,
|
||||||
gateway_name = self.gateway_name
|
"%Y%m%d %H:%M:%S"),
|
||||||
|
gateway_name=self.gateway_name
|
||||||
)
|
)
|
||||||
|
|
||||||
self.gateway.on_trade(trade)
|
self.gateway.on_trade(trade)
|
||||||
@ -478,39 +503,50 @@ class IbApi(EWrapper):
|
|||||||
Callback of all sub accountid.
|
Callback of all sub accountid.
|
||||||
"""
|
"""
|
||||||
super(IbApi, self).managedAccounts(accountsList)
|
super(IbApi, self).managedAccounts(accountsList)
|
||||||
|
|
||||||
for account_code in accountsList.split(","):
|
for account_code in accountsList.split(","):
|
||||||
self.client.reqAccountUpdates(account_code)
|
self.client.reqAccountUpdates(True, account_code)
|
||||||
|
|
||||||
def connect(self, setting: dict):
|
def connect(self, setting: dict):
|
||||||
"""
|
"""
|
||||||
Connect to TWS.
|
Connect to TWS.
|
||||||
"""
|
"""
|
||||||
|
if self.status:
|
||||||
|
return
|
||||||
|
|
||||||
self.clientid = setting["clientid"]
|
self.clientid = setting["clientid"]
|
||||||
|
|
||||||
self.client.connect(
|
self.client.connect(
|
||||||
setting["host"].
|
setting["host"],
|
||||||
setting["port"],
|
setting["port"],
|
||||||
setting["clientid"]
|
setting["clientid"]
|
||||||
)
|
)
|
||||||
|
|
||||||
self.client.reqCurrentTime()
|
self.thread.start()
|
||||||
|
|
||||||
|
n = self.client.reqCurrentTime()
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
"""
|
"""
|
||||||
Disconnect to TWS.
|
Disconnect to TWS.
|
||||||
"""
|
"""
|
||||||
|
if not self.status:
|
||||||
|
return
|
||||||
|
|
||||||
self.status = False
|
self.status = False
|
||||||
self.client.eDisconnect()
|
self.client.disconnect()
|
||||||
|
|
||||||
def subscribe(self, req:SubscribeRequest):
|
def subscribe(self, req: SubscribeRequest):
|
||||||
"""
|
"""
|
||||||
Subscribe tick data update.
|
Subscribe tick data update.
|
||||||
"""
|
"""
|
||||||
|
if not self.status:
|
||||||
|
return
|
||||||
|
|
||||||
ib_contract = Contract()
|
ib_contract = Contract()
|
||||||
ib_contract.conId = str(req.symbol)
|
ib_contract.conId = str(req.symbol)
|
||||||
ib_contract.exchange = EXCHANGE_VT2IB[req.exchange]
|
ib_contract.exchange = EXCHANGE_VT2IB[req.exchange]
|
||||||
|
|
||||||
# Get contract data from TWS.
|
# Get contract data from TWS.
|
||||||
self.reqid += 1
|
self.reqid += 1
|
||||||
self.client.reqContractDetails(self.reqid, ib_contract)
|
self.client.reqContractDetails(self.reqid, ib_contract)
|
||||||
@ -526,26 +562,30 @@ class IbApi(EWrapper):
|
|||||||
gateway_name=self.gateway_name
|
gateway_name=self.gateway_name
|
||||||
)
|
)
|
||||||
self.ticks[self.reqid] = tick
|
self.ticks[self.reqid] = tick
|
||||||
|
self.tick_exchange[self.reqid] = req.exchange
|
||||||
|
|
||||||
def send_order(self, req: OrderRequest):
|
def send_order(self, req: OrderRequest):
|
||||||
"""
|
"""
|
||||||
Send a new order.
|
Send a new order.
|
||||||
"""
|
"""
|
||||||
|
if not self.status:
|
||||||
|
return ''
|
||||||
|
|
||||||
self.orderid += 1
|
self.orderid += 1
|
||||||
|
|
||||||
ib_contract = Contract()
|
ib_contract = Contract()
|
||||||
ib_contract.conId = str(req.symbol)
|
ib_contract.conId = str(req.symbol)
|
||||||
ib_contract.exchange = EXCHANGE_VT2IB[req.exchange]
|
ib_contract.exchange = EXCHANGE_VT2IB[req.exchange]
|
||||||
|
|
||||||
order = Order()
|
ib_order = Order()
|
||||||
order.orderId = self.orderid
|
ib_order.orderId = self.orderid
|
||||||
order.clientId = self.clientid
|
ib_order.clientId = self.clientid
|
||||||
order.action = DIRECTION_VT2IB[req.direction]
|
ib_order.action = DIRECTION_VT2IB[req.direction]
|
||||||
order.orderType = PRICETYPE_VT2IB[req.price_type]
|
ib_order.orderType = PRICETYPE_VT2IB[req.price_type]
|
||||||
order.lmtPrice = req.price
|
ib_order.lmtPrice = req.price
|
||||||
order.totalQuantity = req.volume
|
ib_order.totalQuantity = req.volume
|
||||||
|
|
||||||
self.client.placeOrder(self.orderid, contract, order)
|
self.client.placeOrder(self.orderid, ib_contract, ib_order)
|
||||||
self.client.reqIds(1)
|
self.client.reqIds(1)
|
||||||
|
|
||||||
vt_orderid = f"{self.gateway_name}.{self.orderid}"
|
vt_orderid = f"{self.gateway_name}.{self.orderid}"
|
||||||
@ -555,7 +595,32 @@ class IbApi(EWrapper):
|
|||||||
"""
|
"""
|
||||||
Cancel an existing order.
|
Cancel an existing order.
|
||||||
"""
|
"""
|
||||||
self.client.cancelOrder(int(req.orderid)))
|
if not self.status:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.client.cancelOrder(int(req.orderid))
|
||||||
|
|
||||||
|
|
||||||
|
class IbClient(EClient):
|
||||||
|
""""""
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""
|
||||||
|
Reimplement the original run message loop of eclient.
|
||||||
|
|
||||||
|
Remove all unnecessary try...catch... and allow exceptions to interrupt loop.
|
||||||
|
"""
|
||||||
|
while not self.done and self.isConnected():
|
||||||
|
try:
|
||||||
|
text = self.msg_queue.get(block=True, timeout=0.2)
|
||||||
|
|
||||||
|
if len(text) > MAX_MSG_LEN:
|
||||||
|
errorMsg = "%s:%d:%s" % (BAD_LENGTH.msg(), len(text), text)
|
||||||
|
self.wrapper.error(NO_VALID_ID, BAD_LENGTH.code(), errorMsg)
|
||||||
|
self.disconnect()
|
||||||
|
break
|
||||||
|
|
||||||
|
fields = comm.read_fields(text)
|
||||||
|
self.decoder.interpret(fields)
|
||||||
|
except Empty:
|
||||||
|
pass
|
||||||
|
11
vnpy/trader/app.py
Normal file
11
vnpy/trader/app.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
from abc import ABC
|
||||||
|
|
||||||
|
|
||||||
|
class BaseApp(ABC):
|
||||||
|
"""
|
||||||
|
Abstract class for app.
|
||||||
|
"""
|
||||||
|
|
||||||
|
app_name = ''
|
||||||
|
app_engine = None
|
||||||
|
app_ui = None
|
@ -18,7 +18,7 @@ from .event import (
|
|||||||
EVENT_CONTRACT
|
EVENT_CONTRACT
|
||||||
)
|
)
|
||||||
from .object import LogData, SubscribeRequest, OrderRequest, CancelRequest
|
from .object import LogData, SubscribeRequest, OrderRequest, CancelRequest
|
||||||
from .utility import Singleton, get_temp_path, check_order_active
|
from .utility import Singleton, get_temp_path
|
||||||
from .setting import SETTINGS
|
from .setting import SETTINGS
|
||||||
from .gateway import BaseGateway
|
from .gateway import BaseGateway
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ class MainEngine:
|
|||||||
Add gateway.
|
Add gateway.
|
||||||
"""
|
"""
|
||||||
gateway = gateway_class(self.event_engine)
|
gateway = gateway_class(self.event_engine)
|
||||||
self.gateways[gateway.gateway_name] = engine
|
self.gateways[gateway.gateway_name] = gateway
|
||||||
|
|
||||||
def init_engines(self):
|
def init_engines(self):
|
||||||
"""
|
"""
|
||||||
@ -78,6 +78,7 @@ class MainEngine:
|
|||||||
gateway = self.gateways.get(gateway_name, None)
|
gateway = self.gateways.get(gateway_name, None)
|
||||||
if not gateway:
|
if not gateway:
|
||||||
self.write_log(f"找不到底层接口:{gateway_name}")
|
self.write_log(f"找不到底层接口:{gateway_name}")
|
||||||
|
return None
|
||||||
return gateway
|
return gateway
|
||||||
|
|
||||||
def get_default_setting(self, gateway_name: str):
|
def get_default_setting(self, gateway_name: str):
|
||||||
@ -87,14 +88,21 @@ class MainEngine:
|
|||||||
gateway = self.get_gateway(gateway_name)
|
gateway = self.get_gateway(gateway_name)
|
||||||
if gateway:
|
if gateway:
|
||||||
return gateway.get_default_setting()
|
return gateway.get_default_setting()
|
||||||
|
return None
|
||||||
|
|
||||||
def connect(self, gateway_name: str):
|
def get_all_gateway_names(self):
|
||||||
|
"""
|
||||||
|
Get all names of gatewasy added in main engine.
|
||||||
|
"""
|
||||||
|
return list(self.gateways.keys())
|
||||||
|
|
||||||
|
def connect(self, setting: dict, gateway_name: str):
|
||||||
"""
|
"""
|
||||||
Start connection of a specific gateway.
|
Start connection of a specific gateway.
|
||||||
"""
|
"""
|
||||||
gateway = self.get_gateway(gateway_name)
|
gateway = self.get_gateway(gateway_name)
|
||||||
if gateway:
|
if gateway:
|
||||||
gateway.connect()
|
gateway.connect(setting)
|
||||||
|
|
||||||
def subscribe(self, req: SubscribeRequest, gateway_name: str):
|
def subscribe(self, req: SubscribeRequest, gateway_name: str):
|
||||||
"""
|
"""
|
||||||
@ -120,7 +128,7 @@ class MainEngine:
|
|||||||
"""
|
"""
|
||||||
gateway = self.get_gateway(gateway_name)
|
gateway = self.get_gateway(gateway_name)
|
||||||
if gateway:
|
if gateway:
|
||||||
gateway.send_order(req)
|
gateway.cancel_order(req)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
"""
|
"""
|
||||||
@ -277,7 +285,7 @@ class OmsEngine(BaseEngine):
|
|||||||
self.orders[order.vt_orderid] = order
|
self.orders[order.vt_orderid] = order
|
||||||
|
|
||||||
# If order is active, then update data in dict.
|
# If order is active, then update data in dict.
|
||||||
if check_order_active(order.status):
|
if order.check_active():
|
||||||
self.active_orders[order.vt_orderid] = order
|
self.active_orders[order.vt_orderid] = order
|
||||||
# Otherwise, pop inactive order from in dict
|
# Otherwise, pop inactive order from in dict
|
||||||
elif order.vt_orderid in self.active_orders:
|
elif order.vt_orderid in self.active_orders:
|
||||||
|
@ -6,6 +6,10 @@ from dataclasses import dataclass
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from logging import INFO
|
from logging import INFO
|
||||||
|
|
||||||
|
from .constant import (STATUS_SUBMITTING, STATUS_NOTTRADED, STATUS_PARTTRADED)
|
||||||
|
|
||||||
|
ACTIVE_STATUSES = set([STATUS_SUBMITTING, STATUS_NOTTRADED, STATUS_PARTTRADED])
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class BaseData:
|
class BaseData:
|
||||||
@ -110,6 +114,26 @@ class OrderData(BaseData):
|
|||||||
self.vt_symbol = f"{self.symbol}.{self.exchange}"
|
self.vt_symbol = f"{self.symbol}.{self.exchange}"
|
||||||
self.vt_orderid = f"{self.gateway_name}.{self.orderid}"
|
self.vt_orderid = f"{self.gateway_name}.{self.orderid}"
|
||||||
|
|
||||||
|
def check_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(
|
||||||
|
orderid=self.orderid,
|
||||||
|
symbol=self.symbol,
|
||||||
|
exchange=self.exchange
|
||||||
|
)
|
||||||
|
return req
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class TradeData(BaseData):
|
class TradeData(BaseData):
|
||||||
@ -182,7 +206,7 @@ class LogData(BaseData):
|
|||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
""""""
|
""""""
|
||||||
time = datetime
|
self.time = datetime.now()
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -7,6 +7,7 @@ from PyQt5 import QtWidgets, QtGui
|
|||||||
|
|
||||||
from .mainwindow import MainWindow
|
from .mainwindow import MainWindow
|
||||||
from ..setting import SETTINGS
|
from ..setting import SETTINGS
|
||||||
|
from ..utility import get_icon_path
|
||||||
|
|
||||||
|
|
||||||
def create_qapp():
|
def create_qapp():
|
||||||
@ -19,9 +20,7 @@ def create_qapp():
|
|||||||
font = QtGui.QFont(SETTINGS["font.family"], SETTINGS["font.size"])
|
font = QtGui.QFont(SETTINGS["font.family"], SETTINGS["font.size"])
|
||||||
qapp.setFont(font)
|
qapp.setFont(font)
|
||||||
|
|
||||||
ui_path = Path(__file__).parent
|
icon = QtGui.QIcon(get_icon_path(__file__, "vnpy.ico"))
|
||||||
icon_path = ui_path.joinpath("ico", "vnpy.ico")
|
|
||||||
icon = QtGui.QIcon(str(icon_path))
|
|
||||||
qapp.setWindowIcon(icon)
|
qapp.setWindowIcon(icon)
|
||||||
|
|
||||||
if 'Windows' in platform.uname():
|
if 'Windows' in platform.uname():
|
||||||
|
@ -1,14 +1,23 @@
|
|||||||
from PyQt5 import QtWidgets, QtCore
|
"""
|
||||||
|
Implements main window of VN Trader.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
|
from PyQt5 import QtWidgets, QtCore, QtGui
|
||||||
|
|
||||||
from vnpy.event import EventEngine
|
from vnpy.event import EventEngine
|
||||||
from ..engine import MainEngine
|
from ..engine import MainEngine
|
||||||
|
from ..utility import get_icon_path
|
||||||
from .widget import (
|
from .widget import (
|
||||||
TickMonitor,
|
TickMonitor,
|
||||||
OrderMonitor,
|
OrderMonitor,
|
||||||
TradeMonitor,
|
TradeMonitor,
|
||||||
PositionMonitor,
|
PositionMonitor,
|
||||||
AccountMonitor,
|
AccountMonitor,
|
||||||
LogMonitor
|
LogMonitor,
|
||||||
|
ConnectDialog,
|
||||||
|
TradingWidget
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -23,6 +32,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
self.main_engine = main_engine
|
self.main_engine = main_engine
|
||||||
self.event_engine = event_engine
|
self.event_engine = event_engine
|
||||||
|
|
||||||
|
self.connect_dialogs = {}
|
||||||
self.widgets = {}
|
self.widgets = {}
|
||||||
|
|
||||||
self.init_ui()
|
self.init_ui()
|
||||||
@ -35,6 +45,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
|
|
||||||
def init_dock(self):
|
def init_dock(self):
|
||||||
""""""
|
""""""
|
||||||
|
trading_widget, trading_dock = self.create_dock(TradingWidget, "交易", QtCore.Qt.LeftDockWidgetArea)
|
||||||
tick_widget, tick_dock = self.create_dock(TickMonitor, "行情", QtCore.Qt.RightDockWidgetArea)
|
tick_widget, tick_dock = self.create_dock(TickMonitor, "行情", QtCore.Qt.RightDockWidgetArea)
|
||||||
order_widget, order_dock = self.create_dock(OrderMonitor, "委托", QtCore.Qt.RightDockWidgetArea)
|
order_widget, order_dock = self.create_dock(OrderMonitor, "委托", QtCore.Qt.RightDockWidgetArea)
|
||||||
trade_widget, trade_dock = self.create_dock(TradeMonitor, "成交", QtCore.Qt.RightDockWidgetArea)
|
trade_widget, trade_dock = self.create_dock(TradeMonitor, "成交", QtCore.Qt.RightDockWidgetArea)
|
||||||
@ -44,7 +55,22 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
|
|
||||||
def init_menu(self):
|
def init_menu(self):
|
||||||
""""""
|
""""""
|
||||||
pass
|
bar = self.menuBar()
|
||||||
|
|
||||||
|
sys_menu = bar.addMenu("系统")
|
||||||
|
app_menu = bar.addMenu("功能")
|
||||||
|
help_menu = bar.addMenu("帮助")
|
||||||
|
|
||||||
|
gateway_names = self.main_engine.get_all_gateway_names()
|
||||||
|
for name in gateway_names:
|
||||||
|
func = partial(self.connect, name)
|
||||||
|
icon = QtGui.QIcon(get_icon_path(__file__, "connect.ico"))
|
||||||
|
|
||||||
|
action = QtWidgets.QAction(f"连接{name}", self)
|
||||||
|
action.triggered.connect(func)
|
||||||
|
action.setIcon(icon)
|
||||||
|
|
||||||
|
sys_menu.addAction(action)
|
||||||
|
|
||||||
def create_dock(
|
def create_dock(
|
||||||
self,
|
self,
|
||||||
@ -62,4 +88,36 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
dock.setObjectName(name)
|
dock.setObjectName(name)
|
||||||
dock.setFeatures(dock.DockWidgetFloatable | dock.DockWidgetMovable)
|
dock.setFeatures(dock.DockWidgetFloatable | dock.DockWidgetMovable)
|
||||||
self.addDockWidget(area, dock)
|
self.addDockWidget(area, dock)
|
||||||
return widget, dock
|
return widget, dock
|
||||||
|
|
||||||
|
def connect(self, gateway_name: str):
|
||||||
|
"""
|
||||||
|
Open connect dialog for gateway connection.
|
||||||
|
"""
|
||||||
|
dialog = self.connect_dialogs.get(gateway_name, None)
|
||||||
|
if not dialog:
|
||||||
|
dialog = ConnectDialog(self.main_engine, gateway_name)
|
||||||
|
|
||||||
|
dialog.exec()
|
||||||
|
|
||||||
|
def closeEvent(self, event):
|
||||||
|
"""
|
||||||
|
Call main engine close function before exit.
|
||||||
|
"""
|
||||||
|
reply = QtWidgets.QMessageBox.question(
|
||||||
|
self,
|
||||||
|
"退出",
|
||||||
|
"确认退出?",
|
||||||
|
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
|
||||||
|
QtWidgets.QMessageBox.No
|
||||||
|
)
|
||||||
|
|
||||||
|
if reply == QtWidgets.QMessageBox.Yes:
|
||||||
|
for widget in self.widgets.values():
|
||||||
|
widget.close()
|
||||||
|
|
||||||
|
self.main_engine.close()
|
||||||
|
|
||||||
|
event.accept()
|
||||||
|
else:
|
||||||
|
event.ignore()
|
||||||
|
@ -8,10 +8,16 @@ from typing import Any
|
|||||||
from PyQt5 import QtWidgets, QtGui, QtCore
|
from PyQt5 import QtWidgets, QtGui, QtCore
|
||||||
|
|
||||||
from vnpy.event import EventEngine, Event
|
from vnpy.event import EventEngine, Event
|
||||||
from ..constant import DIRECTION_LONG, DIRECTION_SHORT, DIRECTION_NET
|
from ..constant import (DIRECTION_LONG, DIRECTION_SHORT, DIRECTION_NET,
|
||||||
|
OFFSET_OPEN, OFFSET_CLOSE, OFFSET_CLOSETODAY, OFFSET_CLOSEYESTERDAY,
|
||||||
|
PRICETYPE_LIMIT, PRICETYPE_MARKET, PRICETYPE_FAK, PRICETYPE_FOK,
|
||||||
|
EXCHANGE_CFFEX, EXCHANGE_SHFE, EXCHANGE_DCE, EXCHANGE_CZCE, EXCHANGE_SSE,
|
||||||
|
EXCHANGE_SZSE, EXCHANGE_SGE, EXCHANGE_HKEX, EXCHANGE_HKFE, EXCHANGE_SMART,
|
||||||
|
EXCHANGE_ICE, EXCHANGE_CME, EXCHANGE_NYMEX, EXCHANGE_GLOBEX, EXCHANGE_IDEALPRO)
|
||||||
from ..engine import MainEngine
|
from ..engine import MainEngine
|
||||||
from ..event import (EVENT_TICK, EVENT_ORDER, EVENT_TRADE, EVENT_ACCOUNT,
|
from ..event import (EVENT_TICK, EVENT_ORDER, EVENT_TRADE, EVENT_ACCOUNT,
|
||||||
EVENT_POSITION, EVENT_CONTRACT, EVENT_LOG)
|
EVENT_POSITION, EVENT_CONTRACT, EVENT_LOG)
|
||||||
|
from ..object import SubscribeRequest, OrderRequest, CancelRequest
|
||||||
|
|
||||||
COLOR_LONG = QtGui.QColor("red")
|
COLOR_LONG = QtGui.QColor("red")
|
||||||
COLOR_SHORT = QtGui.QColor("green")
|
COLOR_SHORT = QtGui.QColor("green")
|
||||||
@ -27,7 +33,7 @@ class BaseCell(QtWidgets.QTableWidgetItem):
|
|||||||
|
|
||||||
def __init__(self, content: Any, data: Any):
|
def __init__(self, content: Any, data: Any):
|
||||||
""""""
|
""""""
|
||||||
super(BaseCel, self).__init__()
|
super(BaseCell, self).__init__()
|
||||||
self.setTextAlignment(QtCore.Qt.AlignCenter)
|
self.setTextAlignment(QtCore.Qt.AlignCenter)
|
||||||
self.set_content(content, data)
|
self.set_content(content, data)
|
||||||
|
|
||||||
@ -108,7 +114,7 @@ class PnlCell(BaseCell):
|
|||||||
"""
|
"""
|
||||||
super(PnlCell, self).set_content(content, data)
|
super(PnlCell, self).set_content(content, data)
|
||||||
|
|
||||||
if content.startswith("-"):
|
if str(content).startswith("-"):
|
||||||
self.setForeground(COLOR_SHORT)
|
self.setForeground(COLOR_SHORT)
|
||||||
else:
|
else:
|
||||||
self.setForeground(COLOR_LONG)
|
self.setForeground(COLOR_LONG)
|
||||||
@ -171,7 +177,7 @@ class BaseMonitor(QtWidgets.QTableWidget):
|
|||||||
"""
|
"""
|
||||||
self.setColumnCount(len(self.headers))
|
self.setColumnCount(len(self.headers))
|
||||||
|
|
||||||
labels = [d['display'] for d in self.headers.values()]
|
labels = [d["display"] for d in self.headers.values()]
|
||||||
self.setHorizontalHeaderLabels(labels)
|
self.setHorizontalHeaderLabels(labels)
|
||||||
|
|
||||||
self.verticalHeader().setVisible(False)
|
self.verticalHeader().setVisible(False)
|
||||||
@ -198,7 +204,7 @@ class BaseMonitor(QtWidgets.QTableWidget):
|
|||||||
Register event handler into event engine.
|
Register event handler into event engine.
|
||||||
"""
|
"""
|
||||||
self.signal.connect(self.process_event)
|
self.signal.connect(self.process_event)
|
||||||
self.event_engine.register(self.event_type, self.process_event)
|
self.event_engine.register(self.event_type, self.signal.emit)
|
||||||
|
|
||||||
def process_event(self, event):
|
def process_event(self, event):
|
||||||
"""
|
"""
|
||||||
@ -236,10 +242,10 @@ class BaseMonitor(QtWidgets.QTableWidget):
|
|||||||
setting = self.headers[header]
|
setting = self.headers[header]
|
||||||
|
|
||||||
content = data.__getattribute__(header)
|
content = data.__getattribute__(header)
|
||||||
cell = setting['cell'](content, data)
|
cell = setting["cell"](content, data)
|
||||||
self.setItem(0, column, cell)
|
self.setItem(0, column, cell)
|
||||||
|
|
||||||
if setting['update']:
|
if setting["update"]:
|
||||||
row_cells[header] = cell
|
row_cells[header] = cell
|
||||||
|
|
||||||
if self.data_key:
|
if self.data_key:
|
||||||
@ -253,7 +259,7 @@ class BaseMonitor(QtWidgets.QTableWidget):
|
|||||||
key = data.__getattribute__(self.data_key)
|
key = data.__getattribute__(self.data_key)
|
||||||
row_cells = self.cells[key]
|
row_cells = self.cells[key]
|
||||||
|
|
||||||
for header, cell in row_cells:
|
for header, cell in row_cells.items():
|
||||||
content = data.__getattribute__(header)
|
content = data.__getattribute__(header)
|
||||||
cell.set_content(content, data)
|
cell.set_content(content, data)
|
||||||
|
|
||||||
@ -298,65 +304,65 @@ class TickMonitor(BaseMonitor):
|
|||||||
sorting = True
|
sorting = True
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
'symbol': {
|
"symbol": {
|
||||||
'display': '代码',
|
"display": "代码",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': False
|
"update": False
|
||||||
},
|
},
|
||||||
'last_price': {
|
"last_price": {
|
||||||
'display': '最新价',
|
"display": "最新价",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': True
|
"update": True
|
||||||
},
|
},
|
||||||
'volume': {
|
"volume": {
|
||||||
'display': '成交量',
|
"display": "成交量",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': True
|
"update": True
|
||||||
},
|
},
|
||||||
'open_price': {
|
"open_price": {
|
||||||
'display': '开盘价',
|
"display": "开盘价",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': True
|
"update": True
|
||||||
},
|
},
|
||||||
'high_price': {
|
"high_price": {
|
||||||
'display': '最高价',
|
"display": "最高价",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': True
|
"update": True
|
||||||
},
|
},
|
||||||
'low_price': {
|
"low_price": {
|
||||||
'display': '最低价',
|
"display": "最低价",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': True
|
"update": True
|
||||||
},
|
},
|
||||||
'bid_price_1': {
|
"bid_price_1": {
|
||||||
'display': '买1价',
|
"display": "买1价",
|
||||||
'cell': BidCell,
|
"cell": BidCell,
|
||||||
'update': True
|
"update": True
|
||||||
},
|
},
|
||||||
'bid_volume_1': {
|
"bid_volume_1": {
|
||||||
'display': '买1量',
|
"display": "买1量",
|
||||||
'cell': BidCell,
|
"cell": BidCell,
|
||||||
'update': True
|
"update": True
|
||||||
},
|
},
|
||||||
'ask_price_1': {
|
"ask_price_1": {
|
||||||
'display': '卖1价',
|
"display": "卖1价",
|
||||||
'cell': AskCell,
|
"cell": AskCell,
|
||||||
'update': True
|
"update": True
|
||||||
},
|
},
|
||||||
'ask_volume_1': {
|
"ask_volume_1": {
|
||||||
'display': '卖1量',
|
"display": "卖1量",
|
||||||
'cell': AskCell,
|
"cell": AskCell,
|
||||||
'update': True
|
"update": True
|
||||||
},
|
},
|
||||||
'time': {
|
"datetime": {
|
||||||
'display': '时间',
|
"display": "时间",
|
||||||
'cell': TimeCell,
|
"cell": TimeCell,
|
||||||
'update': True
|
"update": True
|
||||||
},
|
},
|
||||||
'gateway_name': {
|
"gateway_name": {
|
||||||
'display': '接口',
|
"display": "接口",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': False
|
"update": False
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -370,28 +376,23 @@ class LogMonitor(BaseMonitor):
|
|||||||
sorting = False
|
sorting = False
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
'time': {
|
"time": {
|
||||||
'display': '时间',
|
"display": "时间",
|
||||||
'cell': BaseCell,
|
"cell": TimeCell,
|
||||||
'update': False
|
"update": False
|
||||||
},
|
},
|
||||||
'msg': {
|
"msg": {
|
||||||
'display': '信息',
|
"display": "信息",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': False
|
"update": False
|
||||||
},
|
},
|
||||||
'gateway_name': {
|
"gateway_name": {
|
||||||
'display': '接口',
|
"display": "接口",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': False
|
"update": False
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def process_event(self, event):
|
|
||||||
"""Resize row heights for diplaying long log message."""
|
|
||||||
super(LogMonitor, self).process_event(event)
|
|
||||||
self.resizeRowToContents(0)
|
|
||||||
|
|
||||||
|
|
||||||
class TradeMonitor(BaseMonitor):
|
class TradeMonitor(BaseMonitor):
|
||||||
"""
|
"""
|
||||||
@ -402,45 +403,45 @@ class TradeMonitor(BaseMonitor):
|
|||||||
sorting = True
|
sorting = True
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
'tradeid': {
|
"tradeid": {
|
||||||
'display': "成交号 ",
|
"display": "成交号 ",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': False
|
"update": False
|
||||||
},
|
},
|
||||||
'orderid': {
|
"orderid": {
|
||||||
'display': '委托号',
|
"display": "委托号",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': False
|
"update": False
|
||||||
},
|
},
|
||||||
'symbol': {
|
"symbol": {
|
||||||
'display': '代码',
|
"display": "代码",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': False
|
"update": False
|
||||||
},
|
},
|
||||||
'direction': {
|
"direction": {
|
||||||
'display': '方向',
|
"display": "方向",
|
||||||
'cell': DirectionCell,
|
"cell": DirectionCell,
|
||||||
'update': False
|
"update": False
|
||||||
},
|
},
|
||||||
'offset': {
|
"offset": {
|
||||||
'display': '开平',
|
"display": "开平",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': False
|
"update": False
|
||||||
},
|
},
|
||||||
'price': {
|
"price": {
|
||||||
'display': '价格',
|
"display": "价格",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': False
|
"update": False
|
||||||
},
|
},
|
||||||
'volume': {
|
"volume": {
|
||||||
'display': '数量',
|
"display": "数量",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': False
|
"update": False
|
||||||
},
|
},
|
||||||
'gateway_name': {
|
"gateway_name": {
|
||||||
'display': '接口',
|
"display": "接口",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': False
|
"update": False
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -454,55 +455,55 @@ class OrderMonitor(BaseMonitor):
|
|||||||
sorting = True
|
sorting = True
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
'orderid': {
|
"orderid": {
|
||||||
'display': '委托号',
|
"display": "委托号",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': False
|
"update": False
|
||||||
},
|
},
|
||||||
'symbol': {
|
"symbol": {
|
||||||
'display': '代码',
|
"display": "代码",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': False
|
"update": False
|
||||||
},
|
},
|
||||||
'direction': {
|
"direction": {
|
||||||
'display': '方向',
|
"display": "方向",
|
||||||
'cell': DirectionCell,
|
"cell": DirectionCell,
|
||||||
'update': False
|
"update": False
|
||||||
},
|
},
|
||||||
'offset': {
|
"offset": {
|
||||||
'display': '开平',
|
"display": "开平",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': False
|
"update": False
|
||||||
},
|
},
|
||||||
'price': {
|
"price": {
|
||||||
'display': '价格',
|
"display": "价格",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': False
|
"update": False
|
||||||
},
|
},
|
||||||
'volume': {
|
"volume": {
|
||||||
'display': '总数量',
|
"display": "总数量",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': True
|
"update": True
|
||||||
},
|
},
|
||||||
'traded': {
|
"traded": {
|
||||||
'display': '已成交',
|
"display": "已成交",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': True
|
"update": True
|
||||||
},
|
},
|
||||||
'status': {
|
"status": {
|
||||||
'display': '状态',
|
"display": "状态",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': True
|
"update": True
|
||||||
},
|
},
|
||||||
'time': {
|
"time": {
|
||||||
'display': '时间',
|
"display": "时间",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': True
|
"update": True
|
||||||
},
|
},
|
||||||
'gateway_name': {
|
"gateway_name": {
|
||||||
'display': '接口',
|
"display": "接口",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': False
|
"update": False
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -515,40 +516,40 @@ class PositionMonitor(BaseMonitor):
|
|||||||
sorting = True
|
sorting = True
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
'symbol': {
|
"symbol": {
|
||||||
'display': '代码',
|
"display": "代码",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': False
|
"update": False
|
||||||
},
|
},
|
||||||
'direction': {
|
"direction": {
|
||||||
'display': '方向',
|
"display": "方向",
|
||||||
'cell': DirectionCell,
|
"cell": DirectionCell,
|
||||||
'update': False
|
"update": False
|
||||||
},
|
},
|
||||||
'volume': {
|
"volume": {
|
||||||
'display': '数量',
|
"display": "数量",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': True
|
"update": True
|
||||||
},
|
},
|
||||||
'frozen': {
|
"frozen": {
|
||||||
'display': '冻结',
|
"display": "冻结",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': True
|
"update": True
|
||||||
},
|
},
|
||||||
'price': {
|
"price": {
|
||||||
'display': '均价',
|
"display": "均价",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': False
|
"update": False
|
||||||
},
|
},
|
||||||
'pnl': {
|
"pnl": {
|
||||||
'display': '盈亏',
|
"display": "盈亏",
|
||||||
'cell': PnlCell,
|
"cell": PnlCell,
|
||||||
'update': True
|
"update": True
|
||||||
},
|
},
|
||||||
'gateway_name': {
|
"gateway_name": {
|
||||||
'display': '接口',
|
"display": "接口",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': False
|
"update": False
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -562,29 +563,399 @@ class AccountMonitor(BaseMonitor):
|
|||||||
sorting = True
|
sorting = True
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
'accountid': {
|
"accountid": {
|
||||||
'display': '账号',
|
"display": "账号",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': False
|
"update": False
|
||||||
},
|
},
|
||||||
'balance': {
|
"balance": {
|
||||||
'display': '余额',
|
"display": "余额",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': True
|
"update": True
|
||||||
},
|
},
|
||||||
'frozen': {
|
"frozen": {
|
||||||
'display': '冻结',
|
"display": "冻结",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': True
|
"update": True
|
||||||
},
|
},
|
||||||
'available': {
|
"available": {
|
||||||
'display': '可用',
|
"display": "可用",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': True
|
"update": True
|
||||||
},
|
},
|
||||||
'gateway_name': {
|
"gateway_name": {
|
||||||
'display': '接口',
|
"display": "接口",
|
||||||
'cell': BaseCell,
|
"cell": BaseCell,
|
||||||
'update': False
|
"update": False
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ConnectDialog(QtWidgets.QDialog):
|
||||||
|
"""
|
||||||
|
Start connection of a certain gateway.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, main_engine: MainEngine, gateway_name: str):
|
||||||
|
""""""
|
||||||
|
super(ConnectDialog, self).__init__()
|
||||||
|
|
||||||
|
self.main_engine = main_engine
|
||||||
|
self.gateway_name = gateway_name
|
||||||
|
|
||||||
|
self.line_edits = {}
|
||||||
|
|
||||||
|
self.init_ui()
|
||||||
|
|
||||||
|
def init_ui(self):
|
||||||
|
""""""
|
||||||
|
self.setWindowTitle(f"连接{self.gateway_name}")
|
||||||
|
|
||||||
|
form = QtWidgets.QFormLayout()
|
||||||
|
default_setting = self.main_engine.get_default_setting(self.gateway_name)
|
||||||
|
|
||||||
|
# Initialize line edits and form layout based on default setting.
|
||||||
|
for field_name, field_value in default_setting.items():
|
||||||
|
field_type = type(field_value)
|
||||||
|
line_edit = QtWidgets.QLineEdit(str(field_value))
|
||||||
|
|
||||||
|
form.addRow(f"{field_name} <{field_type.__name__}>", line_edit)
|
||||||
|
self.line_edits[field_name] = (line_edit, field_type)
|
||||||
|
|
||||||
|
button = QtWidgets.QPushButton(u"连接")
|
||||||
|
button.clicked.connect(self.connect)
|
||||||
|
form.addRow(button)
|
||||||
|
|
||||||
|
self.setLayout(form)
|
||||||
|
|
||||||
|
def connect(self):
|
||||||
|
"""
|
||||||
|
Get setting value from line edits and connect the gateway.
|
||||||
|
"""
|
||||||
|
setting = {}
|
||||||
|
for field_name, tp in self.line_edits.items():
|
||||||
|
line_edit, field_type = tp
|
||||||
|
field_value = field_type(line_edit.text())
|
||||||
|
setting[field_name] = field_value
|
||||||
|
|
||||||
|
self.main_engine.connect(setting, self.gateway_name)
|
||||||
|
self.accept()
|
||||||
|
|
||||||
|
|
||||||
|
class TradingWidget(QtWidgets.QWidget):
|
||||||
|
"""
|
||||||
|
General manual trading widget.
|
||||||
|
"""
|
||||||
|
|
||||||
|
signal_tick = QtCore.pyqtSignal(Event)
|
||||||
|
|
||||||
|
def __init__(self, main_engine: MainEngine, event_engine: EventEngine):
|
||||||
|
""""""
|
||||||
|
super(TradingWidget, self).__init__()
|
||||||
|
|
||||||
|
self.main_engine = main_engine
|
||||||
|
self.event_engine = event_engine
|
||||||
|
|
||||||
|
self.vt_symbol = ""
|
||||||
|
|
||||||
|
self.init_ui()
|
||||||
|
self.register_event()
|
||||||
|
|
||||||
|
def init_ui(self):
|
||||||
|
""""""
|
||||||
|
self.setFixedWidth(300)
|
||||||
|
|
||||||
|
# Trading function area
|
||||||
|
self.exchange_combo = QtWidgets.QComboBox()
|
||||||
|
self.exchange_combo.addItems([
|
||||||
|
EXCHANGE_CFFEX,
|
||||||
|
EXCHANGE_SHFE,
|
||||||
|
EXCHANGE_DCE,
|
||||||
|
EXCHANGE_CZCE,
|
||||||
|
EXCHANGE_SSE,
|
||||||
|
EXCHANGE_SZSE,
|
||||||
|
EXCHANGE_HKEX,
|
||||||
|
EXCHANGE_HKFE,
|
||||||
|
EXCHANGE_SMART,
|
||||||
|
EXCHANGE_ICE,
|
||||||
|
EXCHANGE_CME,
|
||||||
|
EXCHANGE_NYMEX,
|
||||||
|
EXCHANGE_GLOBEX,
|
||||||
|
EXCHANGE_IDEALPRO
|
||||||
|
])
|
||||||
|
|
||||||
|
self.symbol_line = QtWidgets.QLineEdit()
|
||||||
|
self.symbol_line.returnPressed.connect(self.set_vt_symbol)
|
||||||
|
|
||||||
|
self.name_line = QtWidgets.QLineEdit()
|
||||||
|
self.name_line.setReadOnly(True)
|
||||||
|
|
||||||
|
self.direction_combo = QtWidgets.QComboBox()
|
||||||
|
self.direction_combo.addItems([
|
||||||
|
DIRECTION_LONG,
|
||||||
|
DIRECTION_SHORT
|
||||||
|
])
|
||||||
|
|
||||||
|
self.offset_combo = QtWidgets.QComboBox()
|
||||||
|
self.offset_combo.addItems([
|
||||||
|
OFFSET_OPEN,
|
||||||
|
OFFSET_CLOSE,
|
||||||
|
OFFSET_CLOSETODAY,
|
||||||
|
OFFSET_CLOSEYESTERDAY
|
||||||
|
])
|
||||||
|
|
||||||
|
self.pricetype_combo = QtWidgets.QComboBox()
|
||||||
|
self.pricetype_combo.addItems([
|
||||||
|
PRICETYPE_LIMIT,
|
||||||
|
PRICETYPE_MARKET,
|
||||||
|
PRICETYPE_FAK,
|
||||||
|
PRICETYPE_FOK
|
||||||
|
])
|
||||||
|
|
||||||
|
double_validator = QtGui.QDoubleValidator()
|
||||||
|
double_validator.setBottom(0)
|
||||||
|
|
||||||
|
self.price_line = QtWidgets.QLineEdit()
|
||||||
|
self.price_line.setValidator(double_validator)
|
||||||
|
|
||||||
|
self.volume_line = QtWidgets.QLineEdit()
|
||||||
|
self.volume_line.setValidator(double_validator)
|
||||||
|
|
||||||
|
self.gateway_combo = QtWidgets.QComboBox()
|
||||||
|
self.gateway_combo.addItems(self.main_engine.get_all_gateway_names())
|
||||||
|
|
||||||
|
send_button = QtWidgets.QPushButton("委托")
|
||||||
|
send_button.clicked.connect(self.send_order)
|
||||||
|
|
||||||
|
cancel_button = QtWidgets.QPushButton("全撤")
|
||||||
|
cancel_button.clicked.connect(self.cancel_all)
|
||||||
|
|
||||||
|
form1 = QtWidgets.QFormLayout()
|
||||||
|
form1.addRow("交易所", self.exchange_combo)
|
||||||
|
form1.addRow("代码", self.symbol_line)
|
||||||
|
form1.addRow("名称", self.name_line)
|
||||||
|
form1.addRow("方向", self.direction_combo)
|
||||||
|
form1.addRow("开平", self.offset_combo)
|
||||||
|
form1.addRow("类型", self.pricetype_combo)
|
||||||
|
form1.addRow("价格", self.price_line)
|
||||||
|
form1.addRow("数量", self.volume_line)
|
||||||
|
form1.addRow("接口", self.gateway_combo)
|
||||||
|
form1.addRow(send_button)
|
||||||
|
form1.addRow(cancel_button)
|
||||||
|
|
||||||
|
# Market depth display area
|
||||||
|
bid_color = "rgb(255,174,201)"
|
||||||
|
ask_color = "rgb(160,255,160)"
|
||||||
|
|
||||||
|
self.bp1_label = self.create_label(bid_color)
|
||||||
|
self.bp2_label = self.create_label(bid_color)
|
||||||
|
self.bp3_label = self.create_label(bid_color)
|
||||||
|
self.bp4_label = self.create_label(bid_color)
|
||||||
|
self.bp5_label = self.create_label(bid_color)
|
||||||
|
|
||||||
|
self.bv1_label = self.create_label(bid_color, alignment=QtCore.Qt.AlignRight)
|
||||||
|
self.bv2_label = self.create_label(bid_color, alignment=QtCore.Qt.AlignRight)
|
||||||
|
self.bv3_label = self.create_label(bid_color, alignment=QtCore.Qt.AlignRight)
|
||||||
|
self.bv4_label = self.create_label(bid_color, alignment=QtCore.Qt.AlignRight)
|
||||||
|
self.bv5_label = self.create_label(bid_color, alignment=QtCore.Qt.AlignRight)
|
||||||
|
|
||||||
|
self.ap1_label = self.create_label(ask_color)
|
||||||
|
self.ap2_label = self.create_label(ask_color)
|
||||||
|
self.ap3_label = self.create_label(ask_color)
|
||||||
|
self.ap4_label = self.create_label(ask_color)
|
||||||
|
self.ap5_label = self.create_label(ask_color)
|
||||||
|
|
||||||
|
self.av1_label = self.create_label(ask_color, alignment=QtCore.Qt.AlignRight)
|
||||||
|
self.av2_label = self.create_label(ask_color, alignment=QtCore.Qt.AlignRight)
|
||||||
|
self.av3_label = self.create_label(ask_color, alignment=QtCore.Qt.AlignRight)
|
||||||
|
self.av4_label = self.create_label(ask_color, alignment=QtCore.Qt.AlignRight)
|
||||||
|
self.av5_label = self.create_label(ask_color, alignment=QtCore.Qt.AlignRight)
|
||||||
|
|
||||||
|
self.lp_label = self.create_label()
|
||||||
|
self.return_label = self.create_label(alignment=QtCore.Qt.AlignRight)
|
||||||
|
|
||||||
|
form2 = QtWidgets.QFormLayout()
|
||||||
|
form2.addRow(self.ap5_label, self.av5_label)
|
||||||
|
form2.addRow(self.ap4_label, self.av4_label)
|
||||||
|
form2.addRow(self.ap3_label, self.av3_label)
|
||||||
|
form2.addRow(self.ap2_label, self.av2_label)
|
||||||
|
form2.addRow(self.ap1_label, self.av1_label)
|
||||||
|
form2.addRow(self.lp_label, self.return_label)
|
||||||
|
form2.addRow(self.bp1_label, self.bv1_label)
|
||||||
|
form2.addRow(self.bp2_label, self.bv2_label)
|
||||||
|
form2.addRow(self.bp3_label, self.bv3_label)
|
||||||
|
form2.addRow(self.bp4_label, self.bv4_label)
|
||||||
|
form2.addRow(self.bp5_label, self.bv5_label)
|
||||||
|
|
||||||
|
# Overall layout
|
||||||
|
vbox = QtWidgets.QVBoxLayout()
|
||||||
|
vbox.addLayout(form1)
|
||||||
|
vbox.addLayout(form2)
|
||||||
|
self.setLayout(vbox)
|
||||||
|
|
||||||
|
def create_label(self, color: str = "", alignment: int = QtCore.Qt.AlignLeft):
|
||||||
|
"""
|
||||||
|
Create label with certain font color.
|
||||||
|
"""
|
||||||
|
label = QtWidgets.QLabel()
|
||||||
|
if color:
|
||||||
|
label.setStyleSheet(f"color:{color}")
|
||||||
|
label.setAlignment(alignment)
|
||||||
|
return label
|
||||||
|
|
||||||
|
def register_event(self):
|
||||||
|
""""""
|
||||||
|
self.signal_tick.connect(self.process_tick_event)
|
||||||
|
self.event_engine.register(EVENT_TICK, self.signal_tick.emit)
|
||||||
|
|
||||||
|
def process_tick_event(self, event: Event):
|
||||||
|
""""""
|
||||||
|
tick = event.data
|
||||||
|
if tick.vt_symbol != self.vt_symbol:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.lp_label.setText(str(tick.last_price))
|
||||||
|
self.bp1_label.setText(str(tick.bid_price_1))
|
||||||
|
self.bv1_label.setText(str(tick.bid_volume_1))
|
||||||
|
self.ap1_label.setText(str(tick.ask_price_1))
|
||||||
|
self.av1_label.setText(str(tick.ask_volume_1))
|
||||||
|
|
||||||
|
if tick.pre_close:
|
||||||
|
r = (tick.last_price / tick.pre_close - 1) * 100
|
||||||
|
self.return_label.setText(f"{r:.2f}%")
|
||||||
|
|
||||||
|
if tick.bid_price_2:
|
||||||
|
self.bp2_label.setText(str(tick.bid_price_2))
|
||||||
|
self.bv2_label.setText(str(tick.bid_volume_2))
|
||||||
|
self.ap2_label.setText(str(tick.ask_price_2))
|
||||||
|
self.av2_label.setText(str(tick.ask_volume_2))
|
||||||
|
|
||||||
|
self.bp3_label.setText(str(tick.bid_price_3))
|
||||||
|
self.bv3_label.setText(str(tick.bid_volume_3))
|
||||||
|
self.ap3_label.setText(str(tick.ask_price_3))
|
||||||
|
self.av3_label.setText(str(tick.ask_volume_3))
|
||||||
|
|
||||||
|
self.bp4_label.setText(str(tick.bid_price_4))
|
||||||
|
self.bv4_label.setText(str(tick.bid_volume_4))
|
||||||
|
self.ap4_label.setText(str(tick.ask_price_4))
|
||||||
|
self.av4_label.setText(str(tick.ask_volume_4))
|
||||||
|
|
||||||
|
self.bp5_label.setText(str(tick.bid_price_5))
|
||||||
|
self.bv5_label.setText(str(tick.bid_volume_5))
|
||||||
|
self.ap5_label.setText(str(tick.ask_price_5))
|
||||||
|
self.av5_label.setText(str(tick.ask_volume_5))
|
||||||
|
|
||||||
|
def set_vt_symbol(self):
|
||||||
|
"""
|
||||||
|
Set the tick depth data to monitor by vt_symbol.
|
||||||
|
"""
|
||||||
|
symbol = str(self.symbol_line.text())
|
||||||
|
if not symbol:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Generate vt_symbol from symbol and exchange
|
||||||
|
exchange = str(self.exchange_combo.currentText())
|
||||||
|
vt_symbol = f"{symbol}.{exchange}"
|
||||||
|
|
||||||
|
if vt_symbol == self.vt_symbol:
|
||||||
|
return
|
||||||
|
self.vt_symbol = vt_symbol
|
||||||
|
|
||||||
|
# Update name line widget and clear all labels
|
||||||
|
contract = self.main_engine.get_contract(vt_symbol)
|
||||||
|
if not contract:
|
||||||
|
self.name_line.setText("")
|
||||||
|
else:
|
||||||
|
self.name_line.setText(contract.name)
|
||||||
|
|
||||||
|
self.clear_label_text()
|
||||||
|
|
||||||
|
# Subscribe tick data
|
||||||
|
req = SubscribeRequest(
|
||||||
|
symbol=symbol,
|
||||||
|
exchange=exchange
|
||||||
|
)
|
||||||
|
gateway_name = (self.gateway_combo.currentText())
|
||||||
|
|
||||||
|
self.main_engine.subscribe(req, gateway_name)
|
||||||
|
|
||||||
|
def clear_label_text(self):
|
||||||
|
"""
|
||||||
|
Clear text on all labels.
|
||||||
|
"""
|
||||||
|
self.lp_label.setText("")
|
||||||
|
self.return_label.setText("")
|
||||||
|
|
||||||
|
self.bv1_label.setText("")
|
||||||
|
self.bv2_label.setText("")
|
||||||
|
self.bv3_label.setText("")
|
||||||
|
self.bv4_label.setText("")
|
||||||
|
self.bv5_label.setText("")
|
||||||
|
|
||||||
|
self.av1_label.setText("")
|
||||||
|
self.av2_label.setText("")
|
||||||
|
self.av3_label.setText("")
|
||||||
|
self.av4_label.setText("")
|
||||||
|
self.av5_label.setText("")
|
||||||
|
|
||||||
|
self.bp1_label.setText("")
|
||||||
|
self.bp2_label.setText("")
|
||||||
|
self.bp3_label.setText("")
|
||||||
|
self.bp4_label.setText("")
|
||||||
|
self.bp5_label.setText("")
|
||||||
|
|
||||||
|
self.ap1_label.setText("")
|
||||||
|
self.ap2_label.setText("")
|
||||||
|
self.ap3_label.setText("")
|
||||||
|
self.ap4_label.setText("")
|
||||||
|
self.ap5_label.setText("")
|
||||||
|
|
||||||
|
def send_order(self):
|
||||||
|
"""
|
||||||
|
Send new order manually.
|
||||||
|
"""
|
||||||
|
symbol = str(self.symbol_line.text())
|
||||||
|
if not symbol:
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
"委托失败",
|
||||||
|
"请输入合约代码"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
volume_text = str(self.volume_line.text())
|
||||||
|
if not volume_text:
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
"委托失败",
|
||||||
|
"请输入委托数量"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
volume = float(volume_text)
|
||||||
|
|
||||||
|
price_text = str(self.price_line.text())
|
||||||
|
if not price_text:
|
||||||
|
price = 0
|
||||||
|
else:
|
||||||
|
price = float(price_text)
|
||||||
|
|
||||||
|
req = OrderRequest(
|
||||||
|
symbol=symbol,
|
||||||
|
exchange=str(self.exchange_combo.currentText()),
|
||||||
|
direction=str(self.direction_combo.currentText()),
|
||||||
|
price_type=str(self.pricetype_combo.currentText()),
|
||||||
|
volume=volume,
|
||||||
|
price=price,
|
||||||
|
offset=str(self.offset_combo.currentText())
|
||||||
|
)
|
||||||
|
|
||||||
|
gateway_name = str(self.gateway_combo.currentText())
|
||||||
|
|
||||||
|
self.main_engine.send_order(req, gateway_name)
|
||||||
|
|
||||||
|
def cancel_all(self):
|
||||||
|
"""
|
||||||
|
Cancel all active orders.
|
||||||
|
"""
|
||||||
|
order_list = self.main_engine.get_all_active_orders()
|
||||||
|
for order in order_list:
|
||||||
|
req = order.create_cancel_request()
|
||||||
|
self.main_engine.cancel_order(req, order.gateway_name)
|
@ -40,14 +40,10 @@ def get_temp_path(filename: str):
|
|||||||
return temp_path.joinpath(filename)
|
return temp_path.joinpath(filename)
|
||||||
|
|
||||||
|
|
||||||
ACTIVE_STATUSES = set([STATUS_SUBMITTING, STATUS_NOTTRADED, STATUS_PARTTRADED])
|
def get_icon_path(file_path: str, ico_name: str):
|
||||||
|
|
||||||
|
|
||||||
def check_order_active(status: str):
|
|
||||||
"""
|
"""
|
||||||
Check if order is active by status.
|
Get path for icon file with ico name.
|
||||||
"""
|
"""
|
||||||
if status in ACTIVE_STATUSES:
|
ui_path = Path(file_path).parent
|
||||||
return True
|
icon_path = ui_path.joinpath("ico", ico_name)
|
||||||
else:
|
return str(icon_path)
|
||||||
return False
|
|
||||||
|
Loading…
Reference in New Issue
Block a user