vnpy source reading and learning back to OptionMaster

Keywords: Python

Back to OptionMaster

According to our code reading of APP calling, we basically know how an app is called. Then we go back to OptionMaster to learn the implementation of this app.

Look at the structure

class OptionManager(QtWidgets.QWidget):
    """"""
    signal_new_portfolio = QtCore.pyqtSignal(Event)

    def __init__(self, main_engine: MainEngine, event_engine: EventEngine):
        pass

    def init_ui(self):
        pass

    def register_event(self):
        pass

    def process_new_portfolio_event(self, event: Event):
        pass

    def update_portfolio_combo(self):
        pass

    def open_portfolio_dialog(self):
        pass

    def init_widgets(self):
        pass

    def calculate_underlying_adjustment(self):
        pass

Through structure and annotation, we basically know that this is an APP in terms of options. Let's study the code carefully

__init__

    def __init__(self, main_engine: MainEngine, event_engine: EventEngine):
        """"""
        super().__init__()

        self.main_engine = main_engine
        self.event_engine = event_engine
        self.option_engine = main_engine.get_engine(APP_NAME)

        self.portfolio_name: str = ""

        self.market_monitor: OptionMarketMonitor = None
        self.greeks_monitor: OptionGreeksMonitor = None

        self.docks: List[QtWidgets.QDockWidget] = []

        self.init_ui()
        self.register_event()

In init, we see that the main engine and event engine are different from the main window, and a new engine is added. This engine is configured in the engine class when the app is registered. Then there are two reports: market monitor and Greeks monitor

init_ui

    def init_ui(self):
        #Specific implementation
        self.portfolio_button = QtWidgets.QPushButton("To configure")
        self.portfolio_button.clicked.connect(self.open_portfolio_dialog)

The simple interface configuration we can see from the running program is as follows

We only saw portfolio_button clicking and calling self.open_portfolio_dialog.

open_portfolio_dialog

    def open_portfolio_dialog(self):
        """"""
        portfolio_name = self.portfolio_combo.currentText()
        if not portfolio_name:
            return

        self.portfolio_name = portfolio_name

        dialog = PortfolioDialog(self.option_engine, portfolio_name)
        result = dialog.exec_()

        if result == dialog.Accepted:
            self.portfolio_combo.setEnabled(False)
            self.portfolio_button.setEnabled(False)

            self.init_widgets()

We see that after clicking configure, if there is an option product selected, the portfolio dialog dialog box will pop up. According to the execution result of the dialog box, if you click accept, the drop-down box and configuration button are not available, and then execute init "widgets

init_widgets

    def init_widgets(self):
        """"""
        self.market_monitor = OptionMarketMonitor(self.option_engine, self.portfolio_name)
        self.greeks_monitor = OptionGreeksMonitor(self.option_engine, self.portfolio_name)
        self.manual_trader = OptionManualTrader(self.option_engine, self.portfolio_name)

        self.market_monitor.itemDoubleClicked.connect(self.manual_trader.update_symbol)

        self.market_button.clicked.connect(self.market_monitor.showMaximized)
        self.greeks_button.clicked.connect(self.greeks_monitor.showMaximized)
        self.manual_button.clicked.connect(self.manual_trader.show)
        self.chain_button.clicked.connect(self.calculate_underlying_adjustment)

        for button in [
            self.market_button,
            self.greeks_button,
            self.chain_button,
            self.manual_button
        ]:
            button.setEnabled(True)

We can see that we instantiate the market monitor, Greeks monitor, manual trader and bind the signal slot of the event. At the same time, some buttons are bound, which are basically the display of the form. It is mainly the corresponding display of the following buttons and forms

        self.market_button = QtWidgets.QPushButton("T Type quote")
        self.greeks_button = QtWidgets.QPushButton("Position Greek")
        self.chain_button = QtWidgets.QPushButton("Fitting premium")
        self.manual_button = QtWidgets.QPushButton("Fast Trading")

And each form has passed in the engine defined by the APP

register_event

We see the call to this method in the init method at the same time.

    def register_event(self):
        """"""
        self.signal_new_portfolio.connect(self.process_new_portfolio_event)

        self.event_engine.register(EVENT_OPTION_NEW_PORTFOLIO, self.signal_new_portfolio.emit)

We see that this method brings the Handler and register of process ﹣ new ﹣ portfolio ﹣ event into MainEngine.

process_new_portfolio_event

    def process_new_portfolio_event(self, event: Event):
        """"""
        self.update_portfolio_combo()

    def update_portfolio_combo(self):
        """"""
        if not self.portfolio_combo.isEnabled():
            return

        self.portfolio_combo.clear()
        portfolio_names = self.option_engine.get_portfolio_names()
        self.portfolio_combo.addItems(portfolio_names)

Update the list of options products according to the call of handler

calculate_underlying_adjustment

self.chain_button.clicked.connect(self.calculate_underlying_adjustment)
    def calculate_underlying_adjustment(self):
        """"""
        portfolio = self.option_engine.get_portfolio(self.portfolio_name)

        for chain in portfolio.chains.values():
            chain.calculate_underlying_adjustment()

We can see that after clicking the quick transaction, we will execute the method to obtain the option product data from the engine, and then execute the method of calculate  underlying  adjustment(). To go further, we have several directions:

  1. market_monitor
  2. greeks_monitor
  3. manual_trader
  4. option_engine
  5. calculate_underlying_adjustment

Let's learn from the surrounding to the heart, first look at several forms, and then go deep into the code learning of the engine

market_monitor

class OptionMarketMonitor(MonitorTable):

    def __init__(self, option_engine: OptionEngine, portfolio_name: str):
        

    def init_ui(self):
        

    def register_event(self):
        

    def process_tick_event(self, event: Event):
        

    def process_trade_event(self, event: Event):
        

    def process_position_event(self, event: Event):
        

    def update_pos(self, vt_symbol: str):
        

    def update_price(self, vt_symbol: str):
        

    def update_impv(self, vt_symbol: str):
        

    def update_greeks(self, vt_symbol: str):
        

    def scroll_to_middle(self):
        

    def resizeEvent(self, event: QtGui.QResizeEvent):
        

We see that the OptionMarketMonitor inherits the MonitorTable, which is basically the same as the MonitorBase in the framework. In fact, a lot of code can almost be reused.
Let's take a look at the specific code of this class. The most worth pondering is this

    def register_event(self):
        """"""
        self.signal_tick.connect(self.process_tick_event)
        self.signal_trade.connect(self.process_trade_event)
        self.signal_position.connect(self.process_position_event)

        self.event_engine.register(EVENT_TICK, self.signal_tick.emit)
        self.event_engine.register(EVENT_TRADE, self.signal_trade.emit)
        self.event_engine.register(EVENT_POSITION, self.signal_position.emit)
    def __init__(self, option_engine: OptionEngine, portfolio_name: str):
        """"""
        super().__init__()

        self.option_engine = option_engine
        self.event_engine = option_engine.event_engine
        self.portfolio_name = portfolio_name

Connect the processing of tick events, transaction events, and bin events with the option engine.
These handlers change the interface according to the information, so they will not study one by one.

Other forms

class OptionManualTrader(QtWidgets.QWidget):
    def __init__(self, option_engine: OptionEngine, portfolio_name: str):
        """"""
        super().__init__()

        self.option_engine = option_engine
        self.main_engine: MainEngine = option_engine.main_engine
        self.event_engine: EventEngine = option_engine.event_engine

We can see that the form calls not only its own engine, OptionEngine, but also mainEngin and EventEngine.

 def send_order(self):
        """"""
        symbol = self.symbol_line.text()
        contract = self.contracts.get(symbol, None)
        if not contract:
            return

        price_text = self.price_line.text()
        volume_text = self.volume_line.text()

        if not price_text or not volume_text:
            return

        price = float(price_text)
        volume = int(volume_text)
        direction = Direction(self.direction_combo.currentText())
        offset = Offset(self.offset_combo.currentText())

        req = OrderRequest(
            symbol=contract.symbol,
            exchange=contract.exchange,
            direction=direction,
            type=OrderType.LIMIT,
            offset=offset,
            volume=volume,
            price=price
        )
        self.main_engine.send_order(req, contract.gateway_name)

The whole order is placed through main eng in. When we analyze the main engine, we know that send order is placed through gateway name.

The gateway name comes from the contract
And the contract comes from main eng in

    def init_contracts(self):
        """"""
        contracts = self.main_engine.get_all_contracts()
        for contract in contracts:
            self.contracts[contract.symbol] = contract

We can imagine that. The contract information in main engine comes from the order management system of OmsEngine
Next, we need to go deep into the content of the engine.

Posted by pthes on Sun, 26 Apr 2020 21:09:18 -0700