[Add] spread trading algo and engine

This commit is contained in:
vn.py 2019-09-15 17:44:08 +08:00
parent 0f4402833d
commit 0ad86c8638
8 changed files with 591 additions and 43 deletions

View File

@ -5,7 +5,7 @@ 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.binance import BinanceGateway # from vnpy.gateway.binance import BinanceGateway
# from vnpy.gateway.bitmex import BitmexGateway from vnpy.gateway.bitmex import BitmexGateway
# from vnpy.gateway.futu import FutuGateway # from vnpy.gateway.futu import FutuGateway
# from vnpy.gateway.ib import IbGateway # from vnpy.gateway.ib import IbGateway
# from vnpy.gateway.ctp import CtpGateway # from vnpy.gateway.ctp import CtpGateway
@ -38,6 +38,7 @@ from vnpy.app.cta_backtester import CtaBacktesterApp
# from vnpy.app.risk_manager import RiskManagerApp # from vnpy.app.risk_manager import RiskManagerApp
from vnpy.app.script_trader import ScriptTraderApp from vnpy.app.script_trader import ScriptTraderApp
from vnpy.app.rpc_service import RpcServiceApp from vnpy.app.rpc_service import RpcServiceApp
from vnpy.app.spread_trading import SpreadTradingApp
def main(): def main():
@ -57,7 +58,7 @@ def main():
# main_engine.add_gateway(FemasGateway) # main_engine.add_gateway(FemasGateway)
# main_engine.add_gateway(IbGateway) # main_engine.add_gateway(IbGateway)
# main_engine.add_gateway(FutuGateway) # main_engine.add_gateway(FutuGateway)
# main_engine.add_gateway(BitmexGateway) main_engine.add_gateway(BitmexGateway)
# main_engine.add_gateway(TigerGateway) # main_engine.add_gateway(TigerGateway)
# main_engine.add_gateway(OesGateway) # main_engine.add_gateway(OesGateway)
# main_engine.add_gateway(OkexGateway) # main_engine.add_gateway(OkexGateway)
@ -74,14 +75,15 @@ def main():
# main_engine.add_gateway(DaGateway) # main_engine.add_gateway(DaGateway)
main_engine.add_gateway(CoinbaseGateway) main_engine.add_gateway(CoinbaseGateway)
main_engine.add_app(CtaStrategyApp) # main_engine.add_app(CtaStrategyApp)
main_engine.add_app(CtaBacktesterApp) # main_engine.add_app(CtaBacktesterApp)
# main_engine.add_app(CsvLoaderApp) # main_engine.add_app(CsvLoaderApp)
# main_engine.add_app(AlgoTradingApp) # main_engine.add_app(AlgoTradingApp)
# main_engine.add_app(DataRecorderApp) # main_engine.add_app(DataRecorderApp)
# main_engine.add_app(RiskManagerApp) # main_engine.add_app(RiskManagerApp)
main_engine.add_app(ScriptTraderApp) # main_engine.add_app(ScriptTraderApp)
main_engine.add_app(RpcServiceApp) # main_engine.add_app(RpcServiceApp)
main_engine.add_app(SpreadTradingApp)
main_window = MainWindow(main_engine, event_engine) main_window = MainWindow(main_engine, event_engine)
main_window.showMaximized() main_window.showMaximized()

View File

@ -5,7 +5,7 @@ from vnpy.trader.app import BaseApp
from .engine import SpreadEngine, APP_NAME from .engine import SpreadEngine, APP_NAME
class AlgoTradingApp(BaseApp): class SpreadTradingApp(BaseApp):
"""""" """"""
app_name = APP_NAME app_name = APP_NAME

View File

@ -0,0 +1,132 @@
from typing import Any
from math import floor, ceil
from vnpy.trader.constant import Direction
from vnpy.trader.object import (TickData, OrderData, TradeData)
from .template import SpreadAlgoTemplate
from .base import SpreadData
class SpreadTakerAlgo(SpreadAlgoTemplate):
""""""
algo_name = "SpreadTaker"
def __init__(
self,
algo_engine: Any,
algoid: str,
spread: SpreadData,
direction: Direction,
price: float,
volume: float
):
""""""
super().__init__(algo_engine, algoid, spread, direction, price, volume)
self.cancel_interval: int = 2
self.timer_count: int = 0
def on_tick(self, tick: TickData):
""""""
# Return if there are any existing orders
if not self.check_order_finished():
return
# Hedge if active leg is not fully hedged
if not self.check_hedge_finished():
self.hedge_passive_legs()
return
# Otherwise check if should take active leg
if self.direction == Direction.LONG:
if self.spread.ask_price <= self.price:
self.take_active_leg()
else:
if self.spread.bid_price >= self.price:
self.take_active_leg()
def on_order(self, order: OrderData):
""""""
# Only care active leg order update
if order.vt_symbol != self.spread.active_leg.vt_symbol:
return
# Do nothing if still any existing orders
if not self.check_order_finished():
return
# Hedge passive legs if necessary
if not self.check_hedge_finished():
self.hedge_passive_legs()
def on_trade(self, trade: TradeData):
""""""
pass
def on_interval(self):
""""""
if not self.check_order_finished():
self.cancel_all_order()
def take_active_leg(self):
""""""
# Calculate spread order volume of new round trade
spread_volume_left = self.target - self.traded
if self.direction == Direction.LONG:
spread_order_volume = self.spread.ask_volume
spread_order_volume = min(spread_order_volume, spread_volume_left)
else:
spread_order_volume = -self.spread.bid_volume
spread_order_volume = max(spread_order_volume, spread_volume_left)
# Calculate active leg order volume
leg_order_volume = self.spread.caculate_leg_volume(
self.spread.active_leg.vt_symbol,
spread_order_volume
)
# Send active leg order
self.send_leg_order(
self.spread.active_leg.vt_symbol,
leg_order_volume
)
def hedge_passive_legs(self):
"""
Send orders to hedge all passive legs.
"""
# Calcualte spread volume to hedge
active_leg = self.spread.active_leg
active_traded = self.leg_traded[active_leg.vt_symbol]
hedge_volume = self.spread.calculate_spread_volume(
active_leg.vt_symbol,
active_traded
)
# Calculate passive leg target volume and do hedge
for leg in self.spread.passive_legs:
passive_traded = self.leg_orders[leg.vt_symbol]
passive_target = self.spread.caculate_leg_volume(
leg.vt_symbol,
hedge_volume
)
leg_order_volume = passive_target - passive_traded
if leg_order_volume:
self.send_leg_order(leg.vt_symbol, leg_order_volume)
def send_leg_order(self, vt_symbol: str, leg_volume: float):
""""""
leg = self.spread.legs[vt_symbol]
leg_tick = self.get_tick(vt_symbol)
leg_contract = self.get_contract(vt_symbol)
if leg_volume > 0:
price = leg_tick.ask_price_1 + leg_contract.pricetick * self.payup
self.send_long_order(leg.vt_symbol, price, abs(leg_volume))
else:
price = leg_tick.bid_price_1 - leg_contract.pricetick * self.payup
self.send_short_order(leg.vt_symbol, price, abs(leg_volume))

View File

@ -6,6 +6,12 @@ from vnpy.trader.object import TickData, PositionData
from vnpy.trader.constant import Direction from vnpy.trader.constant import Direction
EVENT_SPREAD_DATA = "eSpreadData"
EVENT_SPREAD_LOG = "eSpreadLog"
EVENT_SPREAD_ALGO = "eSpreadAlgo"
EVENT_SPREAD_STRATEGY = "eSpreadStrategy"
class LegData: class LegData:
"""""" """"""
@ -69,6 +75,9 @@ class SpreadData:
self.active_leg: LegData = None self.active_leg: LegData = None
self.passive_legs: List[LegData] = [] self.passive_legs: List[LegData] = []
self.price_formula: str = ""
self.trading_formula: str = ""
for leg in legs: for leg in legs:
self.legs[leg.vt_symbol] = leg self.legs[leg.vt_symbol] = leg
if leg.vt_symbol == active_symbol: if leg.vt_symbol == active_symbol:
@ -76,6 +85,16 @@ class SpreadData:
else: else:
self.passive_legs.append(leg) self.passive_legs.append(leg)
if leg.price_multiplier > 0:
self.price_formula += f"+{leg.trading_multiplier}*{leg.vt_symbol}"
else:
self.price_formula += f"{leg.trading_multiplier}*{leg.vt_symbol}"
if leg.trading_multiplier > 0:
self.trading_formula += f"+{leg.trading_multiplier}*{leg.vt_symbol}"
else:
self.trading_formula += f"{leg.trading_multiplier}*{leg.vt_symbol}"
# Spread data # Spread data
self.bid_price: float = 0 self.bid_price: float = 0
self.ask_price: float = 0 self.ask_price: float = 0
@ -154,3 +173,21 @@ class SpreadData:
self.ask_price = 0 self.ask_price = 0
self.bid_volume = 0 self.bid_volume = 0
self.ask_volume = 0 self.ask_volume = 0
def calculate_leg_volume(self, vt_symbol: str, spread_volume: float) -> float:
""""""
leg = self.legs[vt_symbol]
leg_volume = spread_volume * leg.trading_multiplier
return leg_volume
def calculate_spread_volume(self, vt_symbol: str, leg_volume: float) -> float:
""""""
leg = self.legs[vt_symbol]
spread_volume = leg_volume / leg.trading_multiplier
if spread_volume > 0:
spread_volume = floor(spread_volume)
else:
spread_volume = ceil(spread_volume)
return spread_volume

View File

@ -1,13 +1,27 @@
from typing import List, Dict from typing import List, Dict
from collections import defaultdict from collections import defaultdict
from copy import copy
from vnpy.event import EventEngine, Event from vnpy.event import EventEngine, Event
from vnpy.trader.engine import BaseEngine, MainEngine from vnpy.trader.engine import BaseEngine, MainEngine
from vnpy.trader.event import EVENT_TICK, EVENT_POSITION from vnpy.trader.event import (
EVENT_TICK, EVENT_POSITION, EVENT_CONTRACT,
EVENT_ORDER, EVENT_TRADE, EVENT_TIMER
)
from vnpy.trader.utility import load_json, save_json from vnpy.trader.utility import load_json, save_json
from vnpy.trader.object import TickData, ContractData from vnpy.trader.object import (
TickData, ContractData, LogData,
SubscribeRequest, OrderRequest, CancelRequest
)
from vnpy.trader.constant import Direction
from .base import LegData, SpreadData from .base import (
LegData, SpreadData,
EVENT_SPREAD_DATA, EVENT_SPREAD_ALGO,
EVENT_SPREAD_LOG, EVENT_SPREAD_STRATEGY
)
from .template import SpreadAlgoTemplate
from .algo import SpreadTakerAlgo
APP_NAME = "SpreadTrading" APP_NAME = "SpreadTrading"
@ -20,9 +34,28 @@ class SpreadEngine(BaseEngine):
"""Constructor""" """Constructor"""
super().__init__(main_engine, event_engine, APP_NAME) super().__init__(main_engine, event_engine, APP_NAME)
self.active = False
self.data_engine: SpreadDataEngine = SpreadDataEngine(self)
self.algo_engine: SpreadAlgoEngine = SpreadAlgoEngine(self)
def start(self):
""""""
if self.active:
return
self.active = True
self.data_engine.start()
self.algo_engine.start()
def write_log(self, msg: str): def write_log(self, msg: str):
"""""" """"""
pass log = LogData(
msg=msg,
gateway_name=APP_NAME
)
event = Event(EVENT_SPREAD_LOG, log)
self.event_engine.put(event)
class SpreadDataEngine: class SpreadDataEngine:
@ -41,10 +74,33 @@ class SpreadDataEngine:
self.spreads: Dict[str, SpreadData] = {} # name: spread self.spreads: Dict[str, SpreadData] = {} # name: spread
self.symbol_spread_map: Dict[str, List[SpreadData]] = defaultdict(list) self.symbol_spread_map: Dict[str, List[SpreadData]] = defaultdict(list)
def start(self):
""""""
self.load_setting() self.load_setting()
self.register_event() self.register_event()
self.test()
def load_setting(self): self.write_log("价差数据引擎启动成功")
def test(self):
""""""
name = "test"
leg_settings = [
{
"vt_symbol": "XBTUSD.BITMEX",
"price_multiplier": 1,
"trading_multiplier": 1
},
{
"vt_symbol": "XBTZ19.BITMEX",
"price_multiplier": -1,
"trading_multiplier": -1
}
]
active_symbol = "XBTUSD.BITMEX"
self.add_spread(name, leg_settings, active_symbol, True)
def load_setting(self) -> None:
"""""" """"""
setting = load_json(self.setting_filename) setting = load_json(self.setting_filename)
@ -56,13 +112,13 @@ class SpreadDataEngine:
save=False save=False
) )
def save_setting(self): def save_setting(self) -> None:
"""""" """"""
setting = [] setting = []
for spread in self.spreads.values(): for spread in self.spreads.values():
leg_settings = [] leg_settings = []
for leg in spread.legs: for leg in spread.legs.values():
leg_setting = { leg_setting = {
"vt_symbol": leg.vt_symbol, "vt_symbol": leg.vt_symbol,
"price_multiplier": leg.price_multiplier, "price_multiplier": leg.price_multiplier,
@ -79,12 +135,13 @@ class SpreadDataEngine:
save_json(self.setting_filename, setting) save_json(self.setting_filename, setting)
def register_event(self): def register_event(self) -> None:
"""""" """"""
self.event_engine.register(EVENT_TICK, self.process_tick_event) self.event_engine.register(EVENT_TICK, self.process_tick_event)
self.event_engine.register(EVENT_POSITION, self.process_position_event) self.event_engine.register(EVENT_POSITION, self.process_position_event)
self.event_engine.register(EVENT_CONTRACT, self.process_contract_event)
def process_tick_event(self, event: Event): def process_tick_event(self, event: Event) -> None:
"""""" """"""
tick = event.data tick = event.data
@ -96,7 +153,9 @@ class SpreadDataEngine:
for spread in self.symbol_spread_map[tick.vt_symbol]: for spread in self.symbol_spread_map[tick.vt_symbol]:
spread.calculate_price() spread.calculate_price()
def process_position_event(self, event: Event): self.put_data_event(spread)
def process_position_event(self, event: Event) -> None:
"""""" """"""
position = event.data position = event.data
@ -108,13 +167,30 @@ class SpreadDataEngine:
for spread in self.symbol_spread_map[position.vt_symbol]: for spread in self.symbol_spread_map[position.vt_symbol]:
spread.calculate_pos() spread.calculate_pos()
self.put_data_event(spread)
def process_contract_event(self, event: Event) -> None:
""""""
contract = event.data
if contract.vt_symbol in self.legs:
req = SubscribeRequest(
contract.symbol, contract.exchange
)
self.main_engine.subscribe(req, contract.gateway_name)
def put_data_event(self, spread: SpreadData) -> None:
""""""
event = Event(EVENT_SPREAD_DATA, spread)
self.event_engine.put(event)
def add_spread( def add_spread(
self, self,
name: str, name: str,
leg_settings: List[Dict], leg_settings: List[Dict],
active_symbol: str, active_symbol: str,
save: bool = True save: bool = True
): ) -> None:
"""""" """"""
if name in self.spreads: if name in self.spreads:
self.write_log("价差创建失败,名称重复:{}".format(name)) self.write_log("价差创建失败,名称重复:{}".format(name))
@ -138,15 +214,16 @@ class SpreadDataEngine:
spread = SpreadData(name, legs, active_symbol) spread = SpreadData(name, legs, active_symbol)
self.spreads[name] = spread self.spreads[name] = spread
for leg in spread.legs: for leg in spread.legs.values():
self.symbol_spread_map[leg.vt_symbol].append(spread) self.symbol_spread_map[leg.vt_symbol].append(spread)
if save: if save:
self.save_setting() self.save_setting()
self.write_log("价差创建成功:{}".format(name)) self.write_log("价差创建成功:{}".format(name))
self.put_data_event(spread)
def remove_spread(self, name: str): def remove_spread(self, name: str) -> None:
"""""" """"""
if name not in self.spreads: if name not in self.spreads:
return return
@ -161,6 +238,7 @@ class SpreadDataEngine:
class SpreadAlgoEngine: class SpreadAlgoEngine:
"""""" """"""
algo_class = SpreadTakerAlgo
def __init__(self, spread_engine: SpreadEngine): def __init__(self, spread_engine: SpreadEngine):
"""""" """"""
@ -170,23 +248,146 @@ class SpreadAlgoEngine:
self.write_log = spread_engine.write_log self.write_log = spread_engine.write_log
def put_event(self, algo) -> None: self.spreads: Dict[str: SpreadData] = {}
self.algos: Dict[str: SpreadAlgoTemplate] = {}
self.order_algo_map: dict[str: SpreadAlgoTemplate] = {}
self.symbol_algo_map: dict[str: SpreadAlgoTemplate] = defaultdict(list)
self.algo_count: int = 0
def start(self):
"""""" """"""
pass self.register_event()
self.write_log("价差算法引擎启动成功")
def register_event(self):
""""""
self.event_engine.register(EVENT_TICK, self.process_tick_event)
self.event_engine.register(EVENT_ORDER, self.process_order_event)
self.event_engine.register(EVENT_TRADE, self.process_trade_event)
self.event_engine.register(EVENT_TIMER, self.process_timer_event)
def process_spread_event(self, event: Event):
""""""
spread: SpreadData = event.data
self.spreads[spread.name] = spread
def process_tick_event(self, event: Event):
""""""
tick = event.data
algos = self.symbol_algo_map[tick.vt_symbol]
if not algos:
return
buf = copy(algos)
for algo in buf:
if not algo.is_active():
algos.remove(algo)
else:
algo.update_tick(tick)
def process_order_event(self, event: Event):
""""""
order = event.data
algo = self.order_algo_map.get(order.vt_orderid, None)
if algo and algo.is_active():
algo.update_order(order)
def process_trade_event(self, event: Event):
""""""
trade = event.data
algo = self.order_algo_map.get(trade.vt_orderid, None)
if algo and algo.is_active():
algo.update_trade(trade)
def process_timer_event(self, event: Event):
""""""
buf = self.algos.values()
for algo in buf:
if not algo.is_active():
self.algos.pop(algo.algoid)
else:
algo.update_timer()
def start_algo(
self,
spread_name: str,
direction: Direction,
price: float,
volume: float,
payup: int,
interval: int
) -> str:
# Find spread object
spread = self.spreads.get(spread_name, None)
if not spread:
self.write_log("创建价差算法失败,找不到价差:{}".format(spread_name))
return ""
# Generate algoid str
self.algo_count += 1
algo_count_str = str(self.algo_count).rjust(6, "0")
algoid = f"{self.algo_class.algo_name}_{algo_count_str}"
# Create algo object
algo = self.algo_class(
self,
algoid,
spread,
direction,
price,
volume,
payup,
interval
)
self.algos[algoid] = algo
# Generate map between vt_symbol and algo
for leg in spread.legs.values():
self.symbol_algo_map[leg.vt_symbol].append(algo)
# Put event to update GUI
self.put_algo_event(algo)
return algoid
def stop_algo(
self,
algoid: str
):
""""""
algo = self.algos.get(algoid, None)
if not algo:
self.write_log("停止价差算法失败,找不到算法:{}".format(algoid))
return
algo.stop()
def put_algo_event(self, algo: SpreadAlgoTemplate) -> None:
""""""
event = Event(EVENT_SPREAD_ALGO, algo)
self.event_engine.put(event)
def write_algo_log(self, algo: SpreadAlgoTemplate, msg: str) -> None:
""""""
msg = f"{algo.algoid}{msg}"
self.write_log(msg)
def send_order( def send_order(
self, self,
algo, algo: SpreadAlgoTemplate,
vt_symbol, vt_symbol: str,
price, price: float,
volume, volume: float,
direction, direction: Direction,
offset
) -> List[str]: ) -> List[str]:
"""""" """"""
pass pass
def cancel_order(self, algo, vt_orderid) -> None: def cancel_order(self, algo: SpreadAlgoTemplate, vt_orderid: str) -> None:
"""""" """"""
pass pass

View File

@ -4,11 +4,10 @@ from typing import Dict, List
from math import floor, ceil from math import floor, ceil
from vnpy.trader.object import TickData, TradeData, OrderData, ContractData from vnpy.trader.object import TickData, TradeData, OrderData, ContractData
from vnpy.trader.constant import Direction, Status, Offset from vnpy.trader.constant import Direction, Status
from vnpy.trader.utility import virtual from vnpy.trader.utility import virtual
from .base import SpreadData from .base import SpreadData
from .engine import SpreadAlgoEngine
class SpreadAlgoTemplate: class SpreadAlgoTemplate:
@ -19,31 +18,37 @@ class SpreadAlgoTemplate:
def __init__( def __init__(
self, self,
algo_engine: SpreadAlgoEngine, algo_engine,
algoid: str, algoid: str,
spread: SpreadData, spread: SpreadData,
direction: Direction, direction: Direction,
price: float, price: float,
volume: float, volume: float,
payup: int payup: int,
interval: int
): ):
"""""" """"""
self.algo_engine: SpreadAlgoEngine = algo_engine self.algo_engine = algo_engine
self.algoid: str = algoid self.algoid: str = algoid
self.spread: SpreadData = spread self.spread: SpreadData = spread
self.spread_name: str = spread.name
self.direction: Direction = direction self.direction: Direction = direction
self.price: float = price self.price: float = price
self.volume: float = volume self.volume: float = volume
self.payup: int = payup self.payup: int = payup
self.interval = interval
if direction == Direction.LONG: if direction == Direction.LONG:
self.target = volume self.target = volume
else: else:
self.target = -volume self.target = -volume
self.status: Status = Status.NOTTRADED self.status: Status = Status.NOTTRADED # Algo status
self.traded: float = 0 self.count: int = 0 # Timer count
self.traded: float = 0 # Volume traded
self.traded_volume: float = 0 # Volume traded (Abs value)
self.leg_traded: Dict[str, float] = defaultdict(int) self.leg_traded: Dict[str, float] = defaultdict(int)
self.leg_orders: Dict[str, List[str]] = defaultdict[list] self.leg_orders: Dict[str, List[str]] = defaultdict[list]
@ -55,12 +60,50 @@ class SpreadAlgoTemplate:
else: else:
return False return False
def check_order_finished(self):
""""""
finished = True
for leg in self.spread.legs.values():
vt_orderids = self.leg_orders[leg.vt_symbol]
if vt_orderids:
finished = False
break
return finished
def check_hedge_finished(self):
""""""
active_symbol = self.spread.active_leg.vt_symbol
active_traded = self.leg_traded[active_symbol]
spread_volume = self.spread.calculate_spread_volume(
active_symbol, active_traded
)
finished = True
for leg in self.spread.passive_legs:
passive_symbol = leg.vt_symbol
leg_target = self.spread.calculate_leg_volume(
passive_symbol, spread_volume
)
leg_traded = self.leg_traded[passive_symbol]
if leg_traded != leg_target:
finished = False
break
return finished
def stop(self): def stop(self):
"""""" """"""
if self.is_active(): if self.is_active():
self.cancel_leg_order() self.cancel_all_order()
self.status = Status.CANCELLED self.status = Status.CANCELLED
self.put_event() self.put_algo_event()
def update_tick(self, tick: TickData): def update_tick(self, tick: TickData):
"""""" """"""
@ -86,7 +129,12 @@ class SpreadAlgoTemplate:
def update_timer(self): def update_timer(self):
"""""" """"""
self.on_timer() self.count += 1
if self.count < self.interval:
return
self.count = 0
self.on_interval()
def put_event(self): def put_event(self):
"""""" """"""
@ -94,7 +142,7 @@ class SpreadAlgoTemplate:
def write_log(self, msg: str): def write_log(self, msg: str):
"""""" """"""
self.algo_engine.write_log(msg) self.algo_engine.write_algo_log(msg)
def send_long_order(self, vt_symbol: str, price: float, volume: float): def send_long_order(self, vt_symbol: str, price: float, volume: float):
"""""" """"""
@ -153,6 +201,8 @@ class SpreadAlgoTemplate:
else: else:
self.traded = max(self.traded, adjusted_leg_traded) self.traded = max(self.traded, adjusted_leg_traded)
self.traded_volume = abs(self.traded)
if self.traded == self.target: if self.traded == self.target:
self.status = Status.ALLTRADED self.status = Status.ALLTRADED
elif not self.traded: elif not self.traded:
@ -184,6 +234,6 @@ class SpreadAlgoTemplate:
pass pass
@virtual @virtual
def on_timer(self): def on_interval(self):
"""""" """"""
pass pass

View File

@ -5,11 +5,21 @@ Widget for spread trading.
from vnpy.event import EventEngine, Event from vnpy.event import EventEngine, Event
from vnpy.trader.engine import MainEngine from vnpy.trader.engine import MainEngine
from vnpy.trader.ui import QtWidgets, QtCore from vnpy.trader.ui import QtWidgets, QtCore
from vnpy.trader.ui.widget import (
BaseMonitor, BaseCell,
BidCell, AskCell,
TimeCell, MsgCell,
PnlCell, DirectionCell,
EnumCell,
)
from ..engine import ( from ..engine import (
AlgoEngine, SpreadEngine,
AlgoTemplate,
APP_NAME, APP_NAME,
EVENT_SPREAD_DATA,
EVENT_SPREAD_LOG,
EVENT_SPREAD_ALGO,
EVENT_SPREAD_STRATEGY
) )
@ -23,3 +33,116 @@ class SpreadManager(QtWidgets.QWidget):
self.main_engine = main_engine self.main_engine = main_engine
self.event_engine = event_engine self.event_engine = event_engine
self.spread_engine = main_engine.get_engine(APP_NAME) self.spread_engine = main_engine.get_engine(APP_NAME)
self.init_ui()
def init_ui(self):
""""""
self.setWindowTitle("价差交易")
self.data_monitor = SpreadDataMonitor(
self.main_engine,
self.event_engine
)
self.log_monitor = SpreadLogMonitor(
self.main_engine,
self.event_engine
)
self.algo_monitor = SpreadAlgoMonitor(
self.main_engine,
self.event_engine
)
vbox = QtWidgets.QVBoxLayout()
vbox.addWidget(self.data_monitor)
vbox.addWidget(self.log_monitor)
hbox = QtWidgets.QHBoxLayout()
hbox.addLayout(vbox)
hbox.addWidget(self.algo_monitor)
self.setLayout(hbox)
def show(self):
""""""
self.spread_engine.start()
self.showMaximized()
class SpreadDataMonitor(BaseMonitor):
"""
Monitor for spread data.
"""
event_type = EVENT_SPREAD_DATA
data_key = "name"
sorting = False
headers = {
"name": {"display": "名称", "cell": BaseCell, "update": False},
"price_formula": {"display": "定价", "cell": BaseCell, "update": False},
"trading_formula": {"display": "交易", "cell": BaseCell, "update": False},
"bid_volume": {"display": "买量", "cell": BidCell, "update": True},
"bid_price": {"display": "买价", "cell": BidCell, "update": True},
"ask_price": {"display": "卖价", "cell": AskCell, "update": True},
"ask_volume": {"display": "卖量", "cell": AskCell, "update": True},
"net_pos": {"display": "净仓", "cell": PnlCell, "update": True},
"datetime": {"display": "时间", "cell": TimeCell, "update": True},
}
class SpreadLogMonitor(QtWidgets.QTextEdit):
"""
Monitor for log data.
"""
signal = QtCore.pyqtSignal(Event)
def __init__(self, main_engine: MainEngine, event_engine: EventEngine):
""""""
super().__init__()
self.main_engine = main_engine
self.event_engine = event_engine
self.init_ui()
self.register_event()
def init_ui(self):
""""""
self.setReadOnly(True)
def register_event(self):
""""""
self.signal.connect(self.process_log_event)
self.event_engine.register(EVENT_SPREAD_LOG, self.signal.emit)
def process_log_event(self, event: Event):
""""""
log = event.data
msg = f"{log.time}{log.msg}"
self.append(msg)
class SpreadAlgoMonitor(BaseMonitor):
"""
Monitor for algo status.
"""
event_type = EVENT_SPREAD_ALGO
data_key = "algoid"
sorting = False
headers = {
"algoid": {"display": "算法", "cell": BaseCell, "update": False},
"spread_name": {"display": "价差", "cell": BaseCell, "update": False},
"direction": {"display": "方向", "cell": EnumCell, "update": False},
"price": {"display": "价格", "cell": BaseCell, "update": False},
"payup": {"display": "超价", "cell": BaseCell, "update": False},
"volume": {"display": "数量", "cell": BaseCell, "update": False},
"traded_volume": {"display": "成交", "cell": BaseCell, "update": True},
"interval": {"display": "间隔", "cell": BaseCell, "update": False},
"count": {"display": "计数", "cell": BaseCell, "update": True},
"status": {"display": "状态", "cell": EnumCell, "update": True},
}

View File

@ -156,6 +156,9 @@ class TimeCell(BaseCell):
""" """
Time format is 12:12:12.5 Time format is 12:12:12.5
""" """
if content is None:
return
timestamp = content.strftime("%H:%M:%S") timestamp = content.strftime("%H:%M:%S")
millisecond = int(content.microsecond / 1000) millisecond = int(content.microsecond / 1000)