[Add] calculate realized pnl of strategy

This commit is contained in:
vn.py 2019-11-13 21:56:48 +08:00
parent 9ee00b0a58
commit e7052df3b0
2 changed files with 72 additions and 13 deletions

View File

@ -5,7 +5,9 @@ from copy import copy
from vnpy.event import Event, EventEngine from vnpy.event import Event, EventEngine
from vnpy.trader.engine import BaseEngine, MainEngine from vnpy.trader.engine import BaseEngine, MainEngine
from vnpy.trader.event import EVENT_TRADE, EVENT_ORDER, EVENT_TICK, EVENT_CONTRACT from vnpy.trader.event import (
EVENT_TRADE, EVENT_ORDER, EVENT_TICK, EVENT_CONTRACT, EVENT_TIMER
)
from vnpy.trader.constant import Direction, Offset, OrderType from vnpy.trader.constant import Direction, Offset, OrderType
from vnpy.trader.object import ( from vnpy.trader.object import (
OrderRequest, CancelRequest, SubscribeRequest, OrderRequest, CancelRequest, SubscribeRequest,
@ -29,6 +31,8 @@ class PortfolioEngine(BaseEngine):
"""""" """"""
super().__init__(main_engine, event_engine, APP_NAME) super().__init__(main_engine, event_engine, APP_NAME)
self.inited = False
self.strategies: Dict[str, PortfolioStrategy] = {} self.strategies: Dict[str, PortfolioStrategy] = {}
self.symbol_strategy_map: Dict[str, List] = defaultdict(list) self.symbol_strategy_map: Dict[str, List] = defaultdict(list)
self.order_strategy_map: Dict[str, PortfolioStrategy] = {} self.order_strategy_map: Dict[str, PortfolioStrategy] = {}
@ -36,6 +40,14 @@ class PortfolioEngine(BaseEngine):
self.register_event() self.register_event()
def init_engine(self):
""""""
if self.inited:
return
self.inited = True
self.load_setting()
def load_setting(self): def load_setting(self):
"""""" """"""
setting: dict = load_json(self.setting_filename) setting: dict = load_json(self.setting_filename)
@ -76,6 +88,12 @@ class PortfolioEngine(BaseEngine):
self.event_engine.register(EVENT_TRADE, self.process_trade_event) self.event_engine.register(EVENT_TRADE, self.process_trade_event)
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_CONTRACT, self.process_contract_event) self.event_engine.register(EVENT_CONTRACT, self.process_contract_event)
self.event_engine.register(EVENT_TIMER, self.process_timer_event)
def process_timer_event(self, event: Event):
""""""
if self.inited:
self.save_setting()
def process_contract_event(self, event: Event): def process_contract_event(self, event: Event):
"""""" """"""
@ -121,6 +139,8 @@ class PortfolioEngine(BaseEngine):
event = Event(EVENT_PORTFOLIO_TRADE, strategy_trade) event = Event(EVENT_PORTFOLIO_TRADE, strategy_trade)
self.event_engine.put(event) self.event_engine.put(event)
self.save_setting()
def process_tick_event(self, event: Event): def process_tick_event(self, event: Event):
"""""" """"""
tick: TickData = event.data tick: TickData = event.data
@ -245,7 +265,7 @@ class PortfolioEngine(BaseEngine):
def cancel_all(self, name: str): def cancel_all(self, name: str):
"""""" """"""
for vt_orderid in self.active_orders: for vt_orderid in self.active_orders:
strategy = self.order_symbol_map[vt_orderid] strategy = self.order_strategy_map[vt_orderid]
if strategy.name == name: if strategy.name == name:
self.cancel_order(vt_orderid) self.cancel_order(vt_orderid)
@ -308,10 +328,46 @@ class PortfolioStrategy:
trade_price: float trade_price: float
): ):
"""""" """"""
old_cost = self.net_pos * self.open_price
if trade_direction == Direction.LONG: if trade_direction == Direction.LONG:
self.net_pos += trade_volume new_pos = self.net_pos + trade_volume
# Open new long position
if self.net_pos >= 0:
new_cost = old_cost + trade_volume * trade_price
self.open_price = new_cost / new_pos
# Close short position
else:
close_volume = min(trade_volume, abs(self.net_pos))
realized_pnl = (trade_price - self.open_price) * \
close_volume * (-1)
self.realized_pnl += realized_pnl
if new_pos > 0:
self.open_price = trade_price
# Update net pos
self.net_pos = new_pos
else: else:
self.net_pos -= trade_volume new_pos = self.net_pos - trade_volume
# Open new short position
if self.net_pos <= 0:
new_cost = old_cost - trade_volume * trade_price
self.open_price = new_cost / new_pos
# Close long position
else:
close_volume = min(trade_volume, abs(self.net_pos))
realized_pnl = (trade_price - self.open_price) * close_volume
self.realized_pnl += realized_pnl
if new_pos < 0:
self.open_price = trade_price
# Update net pos
self.net_pos = new_pos
self.calculate_pnl() self.calculate_pnl()

View File

@ -41,24 +41,26 @@ class PortfolioManager(QtWidgets.QWidget):
trade_monitor = PortfolioTradeMonitor( trade_monitor = PortfolioTradeMonitor(
self.main_engine, self.event_engine) self.main_engine, self.event_engine)
trading_widget = StrategyTradingWidget(self.portfolio_engine) self.trading_widget = StrategyTradingWidget(self.portfolio_engine)
management_widget = StrategyManagementWidget( self.management_widget = StrategyManagementWidget(
self.portfolio_engine, self.portfolio_engine,
trading_widget, self.trading_widget,
) )
vbox = QtWidgets.QVBoxLayout() vbox = QtWidgets.QVBoxLayout()
vbox.addWidget(management_widget) vbox.addWidget(self.management_widget)
vbox.addWidget(self.create_group("策略", strategy_monitor)) vbox.addWidget(self.create_group("策略", strategy_monitor))
vbox.addWidget(self.trading_widget)
vbox.addWidget(self.create_group("委托", order_monitor)) vbox.addWidget(self.create_group("委托", order_monitor))
vbox.addWidget(self.create_group("成交", trade_monitor)) vbox.addWidget(self.create_group("成交", trade_monitor))
vbox.addWidget(trading_widget)
self.setLayout(vbox) self.setLayout(vbox)
def show(self): def show(self):
"""""" """"""
self.portfolio_engine.load_setting() self.portfolio_engine.init_engine()
self.management_widget.update_combo()
self.showMaximized() self.showMaximized()
def create_group(self, title: str, widget: QtWidgets.QWidget): def create_group(self, title: str, widget: QtWidgets.QWidget):
@ -91,6 +93,7 @@ class PortfolioStrategyMonitor(BaseMonitor):
"open_price": {"display": "持仓价格", "cell": BaseCell, "update": True}, "open_price": {"display": "持仓价格", "cell": BaseCell, "update": True},
"last_price": {"display": "最新价格", "cell": BaseCell, "update": True}, "last_price": {"display": "最新价格", "cell": BaseCell, "update": True},
"pos_pnl": {"display": "持仓盈亏", "cell": PnlCell, "update": True}, "pos_pnl": {"display": "持仓盈亏", "cell": PnlCell, "update": True},
"realized_pnl": {"display": "平仓盈亏", "cell": PnlCell, "update": True},
"create_time": {"display": "创建时间", "cell": BaseCell, "update": False}, "create_time": {"display": "创建时间", "cell": BaseCell, "update": False},
} }
@ -104,7 +107,7 @@ class PortfolioTradeMonitor(BaseMonitor):
data_key = "" data_key = ""
headers = { headers = {
"gateway_name": {"display": "接口名称", "cell": BaseCell, "update": False}, "gateway_name": {"display": "策略名称", "cell": BaseCell, "update": False},
"tradeid": {"display": "成交号 ", "cell": BaseCell, "update": False}, "tradeid": {"display": "成交号 ", "cell": BaseCell, "update": False},
"orderid": {"display": "委托号", "cell": BaseCell, "update": False}, "orderid": {"display": "委托号", "cell": BaseCell, "update": False},
"symbol": {"display": "代码", "cell": BaseCell, "update": False}, "symbol": {"display": "代码", "cell": BaseCell, "update": False},
@ -127,7 +130,7 @@ class PortfolioOrderMonitor(BaseMonitor):
sorting = True sorting = True
headers = { headers = {
"gateway_name": {"display": "接口名称", "cell": BaseCell, "update": False}, "gateway_name": {"display": "策略名称", "cell": BaseCell, "update": False},
"orderid": {"display": "委托号", "cell": BaseCell, "update": False}, "orderid": {"display": "委托号", "cell": BaseCell, "update": False},
"symbol": {"display": "代码", "cell": BaseCell, "update": False}, "symbol": {"display": "代码", "cell": BaseCell, "update": False},
"exchange": {"display": "交易所", "cell": EnumCell, "update": False}, "exchange": {"display": "交易所", "cell": EnumCell, "update": False},
@ -249,7 +252,7 @@ class StrategyTradingWidget(QtWidgets.QWidget):
def cancel_all(self): def cancel_all(self):
"""""" """"""
name = self.name_line.text() name = self.name_combo.currentText()
self.portfolio_engine.cancel_all(name) self.portfolio_engine.cancel_all(name)
def update_combo(self): def update_combo(self):