[Add] calculate realized pnl of strategy
This commit is contained in:
parent
9ee00b0a58
commit
e7052df3b0
@ -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()
|
||||||
|
|
||||||
|
@ -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):
|
||||||
|
Loading…
Reference in New Issue
Block a user