Exemple #1
0
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.strftime('%H:%M:%S')}\t{log.msg}"
        self.append(msg)
Exemple #2
0
class SpreadStrategyMonitor(QtWidgets.QWidget):
    """"""

    signal_strategy = QtCore.pyqtSignal(Event)

    def __init__(self, spread_engine: SpreadEngine):
        super().__init__()

        self.strategy_engine = spread_engine.strategy_engine
        self.main_engine = spread_engine.main_engine
        self.event_engine = spread_engine.event_engine

        self.managers = {}

        self.init_ui()
        self.register_event()

    def init_ui(self):
        """"""
        self.scroll_layout = QtWidgets.QVBoxLayout()
        self.scroll_layout.addStretch()

        scroll_widget = QtWidgets.QWidget()
        scroll_widget.setLayout(self.scroll_layout)

        scroll_area = QtWidgets.QScrollArea()
        scroll_area.setWidgetResizable(True)
        scroll_area.setWidget(scroll_widget)

        vbox = QtWidgets.QVBoxLayout()
        vbox.addWidget(scroll_area)
        self.setLayout(vbox)

    def register_event(self):
        """"""
        self.signal_strategy.connect(self.process_strategy_event)

        self.event_engine.register(EVENT_SPREAD_STRATEGY,
                                   self.signal_strategy.emit)

    def process_strategy_event(self, event):
        """
        Update strategy status onto its monitor.
        """
        data = event.data
        strategy_name = data["strategy_name"]

        if strategy_name in self.managers:
            manager = self.managers[strategy_name]
            manager.update_data(data)
        else:
            manager = SpreadStrategyWidget(self, self.strategy_engine, data)
            self.scroll_layout.insertWidget(0, manager)
            self.managers[strategy_name] = manager

    def remove_strategy(self, strategy_name):
        """"""
        manager = self.managers.pop(strategy_name)
        manager.deleteLater()
Exemple #3
0
class LogMonitor(QtWidgets.QTableWidget):
    """"""
    signal = QtCore.pyqtSignal(Event)

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

        self.event_engine = event_engine

        self.init_ui()
        self.register_event()

    def init_ui(self):
        """"""
        labels = [
            "时间",
            "信息"
        ]
        self.setColumnCount(len(labels))
        self.setHorizontalHeaderLabels(labels)
        self.verticalHeader().setVisible(False)
        self.setEditTriggers(self.NoEditTriggers)

        self.verticalHeader().setSectionResizeMode(
            QtWidgets.QHeaderView.ResizeToContents
        )

        self.horizontalHeader().setSectionResizeMode(
            1,
            QtWidgets.QHeaderView.Stretch
        )
        self.setWordWrap(True)

    def register_event(self):
        """"""
        self.signal.connect(self.process_log_event)

        self.event_engine.register(EVENT_ALGO_LOG, self.signal.emit)

    def process_log_event(self, event):
        """"""
        log = event.data
        msg = log.msg
        timestamp = datetime.now().strftime("%H:%M:%S")

        timestamp_cell = QtWidgets.QTableWidgetItem(timestamp)
        msg_cell = QtWidgets.QTableWidgetItem(msg)

        self.insertRow(0)
        self.setItem(0, 0, timestamp_cell)
        self.setItem(0, 1, msg_cell)
Exemple #4
0
class RecorderManager(QtWidgets.QWidget):
    """"""

    signal_log = QtCore.pyqtSignal(Event)
    signal_update = QtCore.pyqtSignal(Event)
    signal_contract = QtCore.pyqtSignal(Event)
    signal_exception = 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.recorder_engine = main_engine.get_engine(APP_NAME)

        self.init_ui()
        self.register_event()
        self.recorder_engine.put_event()

    def init_ui(self):
        """"""
        self.setWindowTitle("行情记录")
        self.resize(1000, 600)

        # Create widgets
        self.symbol_line = QtWidgets.QLineEdit()
        self.symbol_line.setFixedHeight(self.symbol_line.sizeHint().height() *
                                        2)

        contracts = self.main_engine.get_all_contracts()
        self.vt_symbols = [contract.vt_symbol for contract in contracts]

        self.symbol_completer = QtWidgets.QCompleter(self.vt_symbols)
        self.symbol_completer.setFilterMode(QtCore.Qt.MatchContains)
        self.symbol_completer.setCompletionMode(
            self.symbol_completer.PopupCompletion)
        self.symbol_line.setCompleter(self.symbol_completer)

        add_bar_button = QtWidgets.QPushButton("添加")
        add_bar_button.clicked.connect(self.add_bar_recording)

        remove_bar_button = QtWidgets.QPushButton("移除")
        remove_bar_button.clicked.connect(self.remove_bar_recording)

        add_tick_button = QtWidgets.QPushButton("添加")
        add_tick_button.clicked.connect(self.add_tick_recording)

        remove_tick_button = QtWidgets.QPushButton("移除")
        remove_tick_button.clicked.connect(self.remove_tick_recording)

        self.bar_recording_edit = QtWidgets.QTextEdit()
        self.bar_recording_edit.setReadOnly(True)

        self.tick_recording_edit = QtWidgets.QTextEdit()
        self.tick_recording_edit.setReadOnly(True)

        self.log_edit = QtWidgets.QTextEdit()
        self.log_edit.setReadOnly(True)

        # Set layout
        grid = QtWidgets.QGridLayout()
        grid.addWidget(QtWidgets.QLabel("K线记录"), 0, 0)
        grid.addWidget(add_bar_button, 0, 1)
        grid.addWidget(remove_bar_button, 0, 2)
        grid.addWidget(QtWidgets.QLabel("Tick记录"), 1, 0)
        grid.addWidget(add_tick_button, 1, 1)
        grid.addWidget(remove_tick_button, 1, 2)

        hbox = QtWidgets.QHBoxLayout()
        hbox.addWidget(QtWidgets.QLabel("本地代码"))
        hbox.addWidget(self.symbol_line)
        hbox.addWidget(QtWidgets.QLabel("     "))
        hbox.addLayout(grid)
        hbox.addStretch()

        grid2 = QtWidgets.QGridLayout()
        grid2.addWidget(QtWidgets.QLabel("K线记录列表"), 0, 0)
        grid2.addWidget(QtWidgets.QLabel("Tick记录列表"), 0, 1)
        grid2.addWidget(self.bar_recording_edit, 1, 0)
        grid2.addWidget(self.tick_recording_edit, 1, 1)
        grid2.addWidget(self.log_edit, 2, 0, 1, 2)

        vbox = QtWidgets.QVBoxLayout()
        vbox.addLayout(hbox)
        vbox.addLayout(grid2)
        self.setLayout(vbox)

    def register_event(self):
        """"""
        self.signal_log.connect(self.process_log_event)
        self.signal_contract.connect(self.process_contract_event)
        self.signal_update.connect(self.process_update_event)
        self.signal_exception.connect(self.process_exception_event)

        self.event_engine.register(EVENT_CONTRACT, self.signal_contract.emit)
        self.event_engine.register(EVENT_RECORDER_LOG, self.signal_log.emit)
        self.event_engine.register(EVENT_RECORDER_UPDATE,
                                   self.signal_update.emit)
        self.event_engine.register(EVENT_RECORDER_EXCEPTION,
                                   self.signal_exception.emit)

    def process_log_event(self, event: Event):
        """"""
        timestamp = datetime.now().strftime("%H:%M:%S")
        msg = f"{timestamp}\t{event.data}"
        self.log_edit.append(msg)

    def process_update_event(self, event: Event):
        """"""
        data = event.data

        self.bar_recording_edit.clear()
        bar_text = "\n".join(data["bar"])
        self.bar_recording_edit.setText(bar_text)

        self.tick_recording_edit.clear()
        tick_text = "\n".join(data["tick"])
        self.tick_recording_edit.setText(tick_text)

    def process_contract_event(self, event: Event):
        """"""
        contract = event.data
        self.vt_symbols.append(contract.vt_symbol)

        model = self.symbol_completer.model()
        model.setStringList(self.vt_symbols)

    def process_exception_event(self, event: Event):
        """"""
        exc_info = event.data
        raise exc_info[1].with_traceback(exc_info[2])

    def add_bar_recording(self):
        """"""
        vt_symbol = self.symbol_line.text()
        self.recorder_engine.add_bar_recording(vt_symbol)

    def add_tick_recording(self):
        """"""
        vt_symbol = self.symbol_line.text()
        self.recorder_engine.add_tick_recording(vt_symbol)

    def remove_bar_recording(self):
        """"""
        vt_symbol = self.symbol_line.text()
        self.recorder_engine.remove_bar_recording(vt_symbol)

    def remove_tick_recording(self):
        """"""
        vt_symbol = self.symbol_line.text()
        self.recorder_engine.remove_tick_recording(vt_symbol)
Exemple #5
0
class BacktesterManager(QtWidgets.QWidget):
    """"""

    setting_filename = "cta_backtester_setting.json"

    signal_log = QtCore.pyqtSignal(Event)
    signal_backtesting_finished = QtCore.pyqtSignal(Event)
    signal_optimization_finished = 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.backtester_engine = main_engine.get_engine(APP_NAME)
        self.class_names = []
        self.settings = {}

        self.target_display = ""

        self.init_ui()
        self.register_event()
        self.backtester_engine.init_engine()
        self.init_strategy_settings()
        self.load_backtesting_setting()

    def init_strategy_settings(self):
        """"""
        self.class_names = self.backtester_engine.get_strategy_class_names()

        for class_name in self.class_names:
            setting = self.backtester_engine.get_default_setting(class_name)
            self.settings[class_name] = setting

        self.class_combo.addItems(self.class_names)

    def init_ui(self):
        """"""
        self.setWindowTitle("CTA回测")

        # Setting Part
        self.class_combo = QtWidgets.QComboBox()

        self.symbol_line = QtWidgets.QLineEdit("IF88.CFFEX")

        self.interval_combo = QtWidgets.QComboBox()
        for interval in Interval:
            self.interval_combo.addItem(interval.value)

        end_dt = datetime.now()
        start_dt = end_dt - timedelta(days=3 * 365)

        self.start_date_edit = QtWidgets.QDateEdit(
            QtCore.QDate(start_dt.year, start_dt.month, start_dt.day))
        self.end_date_edit = QtWidgets.QDateEdit(QtCore.QDate.currentDate())

        self.rate_line = QtWidgets.QLineEdit("0.000025")
        self.slippage_line = QtWidgets.QLineEdit("0.2")
        self.size_line = QtWidgets.QLineEdit("300")
        self.pricetick_line = QtWidgets.QLineEdit("0.2")
        self.capital_line = QtWidgets.QLineEdit("1000000")

        self.inverse_combo = QtWidgets.QComboBox()
        self.inverse_combo.addItems(["正向", "反向"])

        backtesting_button = QtWidgets.QPushButton("开始回测")
        backtesting_button.clicked.connect(self.start_backtesting)

        optimization_button = QtWidgets.QPushButton("参数优化")
        optimization_button.clicked.connect(self.start_optimization)

        self.result_button = QtWidgets.QPushButton("优化结果")
        self.result_button.clicked.connect(self.show_optimization_result)
        self.result_button.setEnabled(False)

        downloading_button = QtWidgets.QPushButton("下载数据")
        downloading_button.clicked.connect(self.start_downloading)

        self.order_button = QtWidgets.QPushButton("委托记录")
        self.order_button.clicked.connect(self.show_backtesting_orders)
        self.order_button.setEnabled(False)

        self.trade_button = QtWidgets.QPushButton("成交记录")
        self.trade_button.clicked.connect(self.show_backtesting_trades)
        self.trade_button.setEnabled(False)

        self.daily_button = QtWidgets.QPushButton("每日盈亏")
        self.daily_button.clicked.connect(self.show_daily_results)
        self.daily_button.setEnabled(False)

        self.candle_button = QtWidgets.QPushButton("K线图表")
        self.candle_button.clicked.connect(self.show_candle_chart)
        self.candle_button.setEnabled(False)

        edit_button = QtWidgets.QPushButton("代码编辑")
        edit_button.clicked.connect(self.edit_strategy_code)

        reload_button = QtWidgets.QPushButton("策略重载")
        reload_button.clicked.connect(self.reload_strategy_class)

        for button in [
                backtesting_button, optimization_button, downloading_button,
                self.result_button, self.order_button, self.trade_button,
                self.daily_button, self.candle_button, edit_button,
                reload_button
        ]:
            button.setFixedHeight(button.sizeHint().height() * 2)

        form = QtWidgets.QFormLayout()
        form.addRow("交易策略", self.class_combo)
        form.addRow("本地代码", self.symbol_line)
        form.addRow("K线周期", self.interval_combo)
        form.addRow("开始日期", self.start_date_edit)
        form.addRow("结束日期", self.end_date_edit)
        form.addRow("手续费率", self.rate_line)
        form.addRow("交易滑点", self.slippage_line)
        form.addRow("合约乘数", self.size_line)
        form.addRow("价格跳动", self.pricetick_line)
        form.addRow("回测资金", self.capital_line)
        form.addRow("合约模式", self.inverse_combo)

        result_grid = QtWidgets.QGridLayout()
        result_grid.addWidget(self.trade_button, 0, 0)
        result_grid.addWidget(self.order_button, 0, 1)
        result_grid.addWidget(self.daily_button, 1, 0)
        result_grid.addWidget(self.candle_button, 1, 1)

        left_vbox = QtWidgets.QVBoxLayout()
        left_vbox.addLayout(form)
        left_vbox.addWidget(backtesting_button)
        left_vbox.addWidget(downloading_button)
        left_vbox.addStretch()
        left_vbox.addLayout(result_grid)
        left_vbox.addStretch()
        left_vbox.addWidget(optimization_button)
        left_vbox.addWidget(self.result_button)
        left_vbox.addStretch()
        left_vbox.addWidget(edit_button)
        left_vbox.addWidget(reload_button)

        # Result part
        self.statistics_monitor = StatisticsMonitor()

        self.log_monitor = QtWidgets.QTextEdit()
        self.log_monitor.setMaximumHeight(400)

        self.chart = BacktesterChart()
        self.chart.setMinimumWidth(1000)

        self.trade_dialog = BacktestingResultDialog(self.main_engine,
                                                    self.event_engine,
                                                    "回测成交记录",
                                                    BacktestingTradeMonitor)
        self.order_dialog = BacktestingResultDialog(self.main_engine,
                                                    self.event_engine,
                                                    "回测委托记录",
                                                    BacktestingOrderMonitor)
        self.daily_dialog = BacktestingResultDialog(self.main_engine,
                                                    self.event_engine,
                                                    "回测每日盈亏",
                                                    DailyResultMonitor)

        # Candle Chart
        self.candle_dialog = CandleChartDialog()

        # Layout
        vbox = QtWidgets.QVBoxLayout()
        vbox.addWidget(self.statistics_monitor)
        vbox.addWidget(self.log_monitor)

        hbox = QtWidgets.QHBoxLayout()
        hbox.addLayout(left_vbox)
        hbox.addLayout(vbox)
        hbox.addWidget(self.chart)
        self.setLayout(hbox)

    def load_backtesting_setting(self):
        """"""
        setting = load_json(self.setting_filename)
        if not setting:
            return

        self.class_combo.setCurrentIndex(
            self.class_combo.findText(setting["class_name"]))

        self.symbol_line.setText(setting["vt_symbol"])

        self.interval_combo.setCurrentIndex(
            self.interval_combo.findText(setting["interval"]))

        start_str = setting.get("start", "")
        if start_str:
            start_dt = QtCore.QDate.fromString(start_str, "yyyy-MM-dd")
            self.start_date_edit.setDate(start_dt)

        self.rate_line.setText(str(setting["rate"]))
        self.slippage_line.setText(str(setting["slippage"]))
        self.size_line.setText(str(setting["size"]))
        self.pricetick_line.setText(str(setting["pricetick"]))
        self.capital_line.setText(str(setting["capital"]))

        if not setting["inverse"]:
            self.inverse_combo.setCurrentIndex(0)
        else:
            self.inverse_combo.setCurrentIndex(1)

    def register_event(self):
        """"""
        self.signal_log.connect(self.process_log_event)
        self.signal_backtesting_finished.connect(
            self.process_backtesting_finished_event)
        self.signal_optimization_finished.connect(
            self.process_optimization_finished_event)

        self.event_engine.register(EVENT_BACKTESTER_LOG, self.signal_log.emit)
        self.event_engine.register(EVENT_BACKTESTER_BACKTESTING_FINISHED,
                                   self.signal_backtesting_finished.emit)
        self.event_engine.register(EVENT_BACKTESTER_OPTIMIZATION_FINISHED,
                                   self.signal_optimization_finished.emit)

    def process_log_event(self, event: Event):
        """"""
        msg = event.data
        self.write_log(msg)

    def write_log(self, msg):
        """"""
        timestamp = datetime.now().strftime("%H:%M:%S")
        msg = f"{timestamp}\t{msg}"
        self.log_monitor.append(msg)

    def process_backtesting_finished_event(self, event: Event):
        """"""
        statistics = self.backtester_engine.get_result_statistics()
        self.statistics_monitor.set_data(statistics)

        df = self.backtester_engine.get_result_df()
        self.chart.set_data(df)

        self.trade_button.setEnabled(True)
        self.order_button.setEnabled(True)
        self.daily_button.setEnabled(True)

        # Tick data can not be displayed using candle chart
        interval = self.interval_combo.currentText()
        if interval != Interval.TICK.value:
            self.candle_button.setEnabled(True)

    def process_optimization_finished_event(self, event: Event):
        """"""
        self.write_log("请点击[优化结果]按钮查看")
        self.result_button.setEnabled(True)

    def start_backtesting(self):
        """"""
        class_name = self.class_combo.currentText()
        vt_symbol = self.symbol_line.text()
        interval = self.interval_combo.currentText()
        start = self.start_date_edit.dateTime().toPyDateTime()
        end = self.end_date_edit.dateTime().toPyDateTime()
        rate = float(self.rate_line.text())
        slippage = float(self.slippage_line.text())
        size = float(self.size_line.text())
        pricetick = float(self.pricetick_line.text())
        capital = float(self.capital_line.text())

        if self.inverse_combo.currentText() == "正向":
            inverse = False
        else:
            inverse = True

        # Check validity of vt_symbol
        if "." not in vt_symbol:
            self.write_log("本地代码缺失交易所后缀,请检查")
            return

        _, exchange_str = vt_symbol.split(".")
        if exchange_str not in Exchange.__members__:
            self.write_log("本地代码的交易所后缀不正确,请检查")
            return

        # Save backtesting parameters
        backtesting_setting = {
            "class_name": class_name,
            "vt_symbol": vt_symbol,
            "interval": interval,
            "start": start.isoformat(),
            "rate": rate,
            "slippage": slippage,
            "size": size,
            "pricetick": pricetick,
            "capital": capital,
            "inverse": inverse,
        }
        save_json(self.setting_filename, backtesting_setting)

        # Get strategy setting
        old_setting = self.settings[class_name]
        dialog = BacktestingSettingEditor(class_name, old_setting)
        i = dialog.exec()
        if i != dialog.Accepted:
            return

        new_setting = dialog.get_setting()
        self.settings[class_name] = new_setting

        result = self.backtester_engine.start_backtesting(
            class_name, vt_symbol, interval, start, end, rate, slippage, size,
            pricetick, capital, inverse, new_setting)

        if result:
            self.statistics_monitor.clear_data()
            self.chart.clear_data()

            self.trade_button.setEnabled(False)
            self.order_button.setEnabled(False)
            self.daily_button.setEnabled(False)
            self.candle_button.setEnabled(False)

            self.trade_dialog.clear_data()
            self.order_dialog.clear_data()
            self.daily_dialog.clear_data()
            self.candle_dialog.clear_data()

    def start_optimization(self):
        """"""
        class_name = self.class_combo.currentText()
        vt_symbol = self.symbol_line.text()
        interval = self.interval_combo.currentText()
        start = self.start_date_edit.dateTime().toPyDateTime()
        end = self.end_date_edit.dateTime().toPyDateTime()
        rate = float(self.rate_line.text())
        slippage = float(self.slippage_line.text())
        size = float(self.size_line.text())
        pricetick = float(self.pricetick_line.text())
        capital = float(self.capital_line.text())

        if self.inverse_combo.currentText() == "正向":
            inverse = False
        else:
            inverse = True

        parameters = self.settings[class_name]
        dialog = OptimizationSettingEditor(class_name, parameters)
        i = dialog.exec()
        if i != dialog.Accepted:
            return

        optimization_setting, use_ga = dialog.get_setting()
        self.target_display = dialog.target_display

        self.backtester_engine.start_optimization(class_name, vt_symbol,
                                                  interval, start, end, rate,
                                                  slippage, size, pricetick,
                                                  capital, inverse,
                                                  optimization_setting, use_ga)

        self.result_button.setEnabled(False)

    def start_downloading(self):
        """"""
        vt_symbol = self.symbol_line.text()
        interval = self.interval_combo.currentText()
        start_date = self.start_date_edit.date()
        end_date = self.end_date_edit.date()

        start = datetime(start_date.year(),
                         start_date.month(),
                         start_date.day(),
                         tzinfo=get_localzone())

        end = datetime(end_date.year(),
                       end_date.month(),
                       end_date.day(),
                       23,
                       59,
                       59,
                       tzinfo=get_localzone())

        self.backtester_engine.start_downloading(vt_symbol, interval, start,
                                                 end)

    def show_optimization_result(self):
        """"""
        result_values = self.backtester_engine.get_result_values()

        dialog = OptimizationResultMonitor(result_values, self.target_display)
        dialog.exec_()

    def show_backtesting_trades(self):
        """"""
        if not self.trade_dialog.is_updated():
            trades = self.backtester_engine.get_all_trades()
            self.trade_dialog.update_data(trades)

        self.trade_dialog.exec_()

    def show_backtesting_orders(self):
        """"""
        if not self.order_dialog.is_updated():
            orders = self.backtester_engine.get_all_orders()
            self.order_dialog.update_data(orders)

        self.order_dialog.exec_()

    def show_daily_results(self):
        """"""
        if not self.daily_dialog.is_updated():
            results = self.backtester_engine.get_all_daily_results()
            self.daily_dialog.update_data(results)

        self.daily_dialog.exec_()

    def show_candle_chart(self):
        """"""
        if not self.candle_dialog.is_updated():
            history = self.backtester_engine.get_history_data()
            self.candle_dialog.update_history(history)

            trades = self.backtester_engine.get_all_trades()
            self.candle_dialog.update_trades(trades)

        self.candle_dialog.exec_()

    def edit_strategy_code(self):
        """"""
        class_name = self.class_combo.currentText()
        file_path = self.backtester_engine.get_strategy_class_file(class_name)

        self.editor.open_editor(file_path)
        self.editor.show()

    def reload_strategy_class(self):
        """"""
        self.backtester_engine.reload_strategy_class()

        current_strategy_name = self.class_combo.currentText()

        self.class_combo.clear()
        self.init_strategy_settings()

        ix = self.class_combo.findText(current_strategy_name)
        self.class_combo.setCurrentIndex(ix)

    def show(self):
        """"""
        self.showMaximized()
Exemple #6
0
class SettingMonitor(QtWidgets.QTableWidget):
    """"""
    setting_signal = QtCore.pyqtSignal(Event)
    use_signal = QtCore.pyqtSignal(dict)

    def __init__(self, algo_engine: AlgoEngine, event_engine: EventEngine):
        """"""
        super().__init__()

        self.algo_engine = algo_engine
        self.event_engine = event_engine

        self.settings = {}
        self.setting_cells = {}

        self.init_ui()
        self.register_event()

    def init_ui(self):
        """"""
        labels = [
            "",
            "",
            "名称",
            "配置"
        ]
        self.setColumnCount(len(labels))
        self.setHorizontalHeaderLabels(labels)
        self.verticalHeader().setVisible(False)
        self.setEditTriggers(self.NoEditTriggers)

        self.verticalHeader().setSectionResizeMode(
            QtWidgets.QHeaderView.ResizeToContents
        )

        self.horizontalHeader().setSectionResizeMode(
            3,
            QtWidgets.QHeaderView.Stretch
        )
        self.setWordWrap(True)

    def register_event(self):
        """"""
        self.setting_signal.connect(self.process_setting_event)

        self.event_engine.register(
            EVENT_ALGO_SETTING, self.setting_signal.emit)

    def process_setting_event(self, event):
        """"""
        data = event.data
        setting_name = data["setting_name"]
        setting = data["setting"]
        cells = self.get_setting_cells(setting_name)

        if setting:
            self.settings[setting_name] = setting

            cells["setting"].setText(to_text(setting))
        else:
            if setting_name in self.settings:
                self.settings.pop(setting_name)

            row = self.row(cells["setting"])
            self.removeRow(row)

            self.setting_cells.pop(setting_name)

    def get_setting_cells(self, setting_name: str):
        """"""
        cells = self.setting_cells.get(setting_name, None)

        if not cells:
            use_func = partial(self.use_setting, setting_name=setting_name)
            use_button = QtWidgets.QPushButton("使用")
            use_button.clicked.connect(use_func)

            remove_func = partial(self.remove_setting,
                                  setting_name=setting_name)
            remove_button = QtWidgets.QPushButton("移除")
            remove_button.clicked.connect(remove_func)

            name_cell = QtWidgets.QTableWidgetItem(setting_name)
            setting_cell = QtWidgets.QTableWidgetItem()

            self.insertRow(0)
            self.setCellWidget(0, 0, use_button)
            self.setCellWidget(0, 1, remove_button)
            self.setItem(0, 2, name_cell)
            self.setItem(0, 3, setting_cell)

            cells = {
                "name": name_cell,
                "setting": setting_cell
            }
            self.setting_cells[setting_name] = cells

        return cells

    def use_setting(self, setting_name: str):
        """"""
        setting = self.settings[setting_name]
        setting["setting_name"] = setting_name
        self.use_signal.emit(setting)

    def remove_setting(self, setting_name: str):
        """"""
        self.algo_engine.remove_algo_setting(setting_name)
Exemple #7
0
class AlgoMonitor(QtWidgets.QTableWidget):
    """"""
    parameters_signal = QtCore.pyqtSignal(Event)
    variables_signal = QtCore.pyqtSignal(Event)

    def __init__(
        self,
        algo_engine: AlgoEngine,
        event_engine: EventEngine,
        mode_active: bool
    ):
        """"""
        super().__init__()

        self.algo_engine = algo_engine
        self.event_engine = event_engine
        self.mode_active = mode_active

        self.algo_cells = {}

        self.init_ui()
        self.register_event()

    def init_ui(self):
        """"""
        labels = [
            "",
            "算法",
            "参数",
            "状态"
        ]
        self.setColumnCount(len(labels))
        self.setHorizontalHeaderLabels(labels)
        self.verticalHeader().setVisible(False)
        self.setEditTriggers(self.NoEditTriggers)

        self.verticalHeader().setSectionResizeMode(
            QtWidgets.QHeaderView.ResizeToContents
        )

        for column in range(2, 4):
            self.horizontalHeader().setSectionResizeMode(
                column,
                QtWidgets.QHeaderView.Stretch
            )
        self.setWordWrap(True)

        if not self.mode_active:
            self.hideColumn(0)

    def register_event(self):
        """"""
        self.parameters_signal.connect(self.process_parameters_event)
        self.variables_signal.connect(self.process_variables_event)

        self.event_engine.register(
            EVENT_ALGO_PARAMETERS, self.parameters_signal.emit)
        self.event_engine.register(
            EVENT_ALGO_VARIABLES, self.variables_signal.emit)

    def process_parameters_event(self, event):
        """"""
        data = event.data
        algo_name = data["algo_name"]
        parameters = data["parameters"]

        cells = self.get_algo_cells(algo_name)
        text = to_text(parameters)
        cells["parameters"].setText(text)

    def process_variables_event(self, event):
        """"""
        data = event.data
        algo_name = data["algo_name"]
        variables = data["variables"]

        cells = self.get_algo_cells(algo_name)
        variables_cell = cells["variables"]
        text = to_text(variables)
        variables_cell.setText(text)

        row = self.row(variables_cell)
        active = variables["active"]

        if self.mode_active:
            if active:
                self.showRow(row)
            else:
                self.hideRow(row)
        else:
            if active:
                self.hideRow(row)
            else:
                self.showRow(row)

    def stop_algo(self, algo_name: str):
        """"""
        self.algo_engine.stop_algo(algo_name)

    def get_algo_cells(self, algo_name: str):
        """"""
        cells = self.algo_cells.get(algo_name, None)

        if not cells:
            stop_func = partial(self.stop_algo, algo_name=algo_name)
            stop_button = QtWidgets.QPushButton("停止")
            stop_button.clicked.connect(stop_func)

            name_cell = QtWidgets.QTableWidgetItem(algo_name)
            parameters_cell = QtWidgets.QTableWidgetItem()
            variables_cell = QtWidgets.QTableWidgetItem()

            self.insertRow(0)
            self.setCellWidget(0, 0, stop_button)
            self.setItem(0, 1, name_cell)
            self.setItem(0, 2, parameters_cell)
            self.setItem(0, 3, variables_cell)

            cells = {
                "name": name_cell,
                "parameters": parameters_cell,
                "variables": variables_cell
            }
            self.algo_cells[algo_name] = cells

        return cells
Exemple #8
0
class ChartWizardWidget(QtWidgets.QWidget):
    """"""
    signal_tick = QtCore.pyqtSignal(Event)
    signal_history = 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.chart_engine: ChartWizardEngine = main_engine.get_engine(APP_NAME)

        self.bgs: Dict[str, BarGenerator] = {}
        self.charts: Dict[str, ChartWidget] = {}

        self.init_ui()
        self.register_event()

    def init_ui(self) -> None:
        """"""
        self.setWindowTitle("K线图表")

        self.tab: QtWidgets.QTabWidget = QtWidgets.QTabWidget()
        self.symbol_line: QtWidgets.QLineEdit = QtWidgets.QLineEdit()

        self.button = QtWidgets.QPushButton("新建图表")
        self.button.clicked.connect(self.new_chart)

        hbox = QtWidgets.QHBoxLayout()
        hbox.addWidget(QtWidgets.QLabel("本地代码"))
        hbox.addWidget(self.symbol_line)
        hbox.addWidget(self.button)
        hbox.addStretch()

        vbox = QtWidgets.QVBoxLayout()
        vbox.addLayout(hbox)
        vbox.addWidget(self.tab)

        self.setLayout(vbox)

    def create_chart(self) -> ChartWidget:
        """"""
        chart = ChartWidget()
        chart.add_plot("candle", hide_x_axis=True)
        chart.add_plot("volume", maximum_height=200)
        chart.add_item(CandleItem, "candle", "candle")
        chart.add_item(VolumeItem, "volume", "volume")
        chart.add_cursor()
        return chart

    def new_chart(self) -> None:
        """"""
        # Filter invalid vt_symbol
        vt_symbol = self.symbol_line.text()
        if not vt_symbol:
            return

        if vt_symbol in self.charts:
            return

        contract = self.main_engine.get_contract(vt_symbol)
        if not contract:
            return

        # Create new chart
        self.bgs[vt_symbol] = BarGenerator(self.on_bar)

        chart = self.create_chart()
        self.charts[vt_symbol] = chart

        self.tab.addTab(chart, vt_symbol)

        # Query history data
        end = datetime.now(get_localzone())
        start = end - timedelta(days=5)

        self.chart_engine.query_history(vt_symbol, Interval.MINUTE, start, end)

    def register_event(self) -> None:
        """"""
        self.signal_tick.connect(self.process_tick_event)
        self.signal_history.connect(self.process_history_event)

        self.event_engine.register(EVENT_CHART_HISTORY,
                                   self.signal_history.emit)
        self.event_engine.register(EVENT_TICK, self.signal_tick.emit)

    def process_tick_event(self, event: Event) -> None:
        """"""
        tick: TickData = event.data
        bg = self.bgs.get(tick.vt_symbol, None)

        if bg:
            bg.update_tick(tick)

            chart = self.charts[tick.vt_symbol]
            bar = copy(bg.bar)
            bar.datetime = bar.datetime.replace(second=0, microsecond=0)
            chart.update_bar(bar)

    def process_history_event(self, event: Event) -> None:
        """"""
        history: List[BarData] = event.data
        if not history:
            return

        bar = history[0]
        chart = self.charts[bar.vt_symbol]
        chart.update_history(history)

        # Subscribe following data update
        contract = self.main_engine.get_contract(bar.vt_symbol)

        req = SubscribeRequest(contract.symbol, contract.exchange)
        self.main_engine.subscribe(req, contract.gateway_name)

    def on_bar(self, bar: BarData):
        """"""
        chart = self.charts[bar.vt_symbol]
        chart.update_bar(bar)
Exemple #9
0
class PortfolioManager(QtWidgets.QWidget):
    """"""

    signal_contract = QtCore.pyqtSignal(Event)
    signal_portfolio = QtCore.pyqtSignal(Event)
    signal_trade = QtCore.pyqtSignal(Event)

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

        self.main_engine = main_engine
        self.event_engine = event_engine

        self.portfolio_engine: PortfolioEngine = main_engine.get_engine(
            APP_NAME)

        self.portfolio_items: Dict[str, QtWidgets.QTreeWidgetItem] = {}

        self.init_ui()
        self.register_event()
        self.update_trades()

    def init_ui(self) -> None:
        """"""
        self.setWindowTitle("投资组合")

        labels = [
            "组合名称", "本地代码", "开盘仓位", "当前仓位", "交易盈亏", "持仓盈亏", "总盈亏", "多头成交",
            "空头成交"
        ]
        self.column_count = len(labels)

        self.tree = QtWidgets.QTreeWidget()
        self.tree.setColumnCount(self.column_count)
        self.tree.setHeaderLabels(labels)
        self.tree.header().setDefaultAlignment(QtCore.Qt.AlignCenter)
        self.tree.header().setStretchLastSection(False)

        delegate = TreeDelegate()
        self.tree.setItemDelegate(delegate)

        self.monitor = PortfolioTradeMonitor()

        expand_button = QtWidgets.QPushButton("全部展开")
        expand_button.clicked.connect(self.tree.expandAll)

        collapse_button = QtWidgets.QPushButton("全部折叠")
        collapse_button.clicked.connect(self.tree.collapseAll)

        resize_button = QtWidgets.QPushButton("调整列宽")
        resize_button.clicked.connect(self.resize_columns)

        interval_spin = QtWidgets.QSpinBox()
        interval_spin.setMinimum(1)
        interval_spin.setMaximum(60)
        interval_spin.setSuffix("秒")
        interval_spin.setValue(self.portfolio_engine.get_timer_interval())
        interval_spin.valueChanged.connect(
            self.portfolio_engine.set_timer_interval)

        self.reference_combo = QtWidgets.QComboBox()
        self.reference_combo.setMinimumWidth(200)
        self.reference_combo.addItem("")
        self.reference_combo.currentIndexChanged.connect(
            self.set_reference_filter)

        hbox1 = QtWidgets.QHBoxLayout()
        hbox1.addWidget(expand_button)
        hbox1.addWidget(collapse_button)
        hbox1.addWidget(resize_button)
        hbox1.addStretch()
        hbox1.addWidget(QtWidgets.QLabel("刷新频率"))
        hbox1.addWidget(interval_spin)
        hbox1.addStretch()
        hbox1.addWidget(QtWidgets.QLabel("组合成交"))
        hbox1.addWidget(self.reference_combo)

        hbox2 = QtWidgets.QHBoxLayout()
        hbox2.addWidget(self.tree)
        hbox2.addWidget(self.monitor)

        vbox = QtWidgets.QVBoxLayout()
        vbox.addLayout(hbox1)
        vbox.addLayout(hbox2)
        self.setLayout(vbox)

    def register_event(self) -> None:
        """"""
        self.signal_contract.connect(self.process_contract_event)
        self.signal_portfolio.connect(self.process_portfolio_event)
        self.signal_trade.connect(self.process_trade_event)

        self.event_engine.register(EVENT_PM_CONTRACT,
                                   self.signal_contract.emit)
        self.event_engine.register(EVENT_PM_PORTFOLIO,
                                   self.signal_portfolio.emit)
        self.event_engine.register(EVENT_TRADE, self.signal_trade.emit)

    def update_trades(self) -> None:
        """"""
        trades = self.main_engine.get_all_trades()
        for trade in trades:
            # Ignore trade with no order reference
            if hasattr(trade, "reference"):
                self.monitor.update_trade(trade)

    def get_portfolio_item(self, reference: str) -> QtWidgets.QTreeWidgetItem:
        """"""
        portfolio_item = self.portfolio_items.get(reference, None)

        if not portfolio_item:
            portfolio_item = QtWidgets.QTreeWidgetItem()
            portfolio_item.setText(0, reference)
            for i in range(2, self.column_count):
                portfolio_item.setTextAlignment(i, QtCore.Qt.AlignCenter)

            self.portfolio_items[reference] = portfolio_item
            self.tree.addTopLevelItem(portfolio_item)

            self.reference_combo.addItem(reference)

        return portfolio_item

    def get_contract_item(self, reference: str,
                          vt_symbol: str) -> QtWidgets.QTreeWidgetItem:
        """"""
        key = (reference, vt_symbol)
        contract_item = self.contract_items.get(key, None)

        if not contract_item:
            contract_item = QtWidgets.QTreeWidgetItem()
            contract_item.setText(1, vt_symbol)
            for i in range(2, self.column_count):
                contract_item.setTextAlignment(i, QtCore.Qt.AlignCenter)

            self.contract_items[key] = contract_item

            portfolio_item = self.get_portfolio_item(reference)
            portfolio_item.addChild(contract_item)

        return contract_item

    def process_contract_event(self, event: Event) -> None:
        """"""
        contract_result: ContractResult = event.data

        contract_item = self.get_contract_item(contract_result.reference,
                                               contract_result.vt_symbol)
        contract_item.setText(2, str(contract_result.open_pos))
        contract_item.setText(3, str(contract_result.last_pos))
        contract_item.setText(4, str(contract_result.trading_pnl))
        contract_item.setText(5, str(contract_result.holding_pnl))
        contract_item.setText(6, str(contract_result.total_pnl))
        contract_item.setText(7, str(contract_result.long_volume))
        contract_item.setText(8, str(contract_result.short_volume))

        self.update_item_color(contract_item, contract_result)

    def process_portfolio_event(self, event: Event) -> None:
        """"""
        portfolio_result: PortfolioResult = event.data

        portfolio_item = self.get_portfolio_item(portfolio_result.reference)
        portfolio_item.setText(4, str(portfolio_result.trading_pnl))
        portfolio_item.setText(5, str(portfolio_result.holding_pnl))
        portfolio_item.setText(6, str(portfolio_result.total_pnl))

        self.update_item_color(portfolio_item, portfolio_result)

    def process_trade_event(self, event: Event) -> None:
        """"""
        trade: TradeData = event.data
        self.monitor.update_trade(trade)

    def update_item_color(self, item: QtWidgets.QTreeWidgetItem,
                          result: Union[ContractResult, PortfolioResult]):
        start_column = 4
        for n, pnl in enumerate(
            [result.trading_pnl, result.holding_pnl, result.total_pnl]):
            i = n + start_column

            if pnl > 0:
                item.setForeground(i, RED_COLOR)
            elif pnl < 0:
                item.setForeground(i, GREEN_COLOR)
            else:
                item.setForeground(i, WHITE_COLOR)

    def resize_columns(self) -> None:
        """"""
        for i in range(self.column_count):
            self.tree.resizeColumnToContents(i)

    def set_reference_filter(self, filter: str) -> None:
        """"""
        filter = self.reference_combo.currentText()
        self.monitor.set_filter(filter)

    def show(self) -> None:
        """"""
        self.showMaximized()
Exemple #10
0
class CtaManager(QtWidgets.QWidget):
    """"""

    signal_log = QtCore.pyqtSignal(Event)
    signal_strategy = QtCore.pyqtSignal(Event)

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

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

        self.managers = {}

        self.init_ui()
        self.register_event()
        self.cta_engine.init_engine()
        self.update_class_combo()

    def init_ui(self):
        """"""
        self.setWindowTitle("CTA策略")

        # Create widgets
        self.class_combo = QtWidgets.QComboBox()

        add_button = QtWidgets.QPushButton("添加策略")
        add_button.clicked.connect(self.add_strategy)

        init_button = QtWidgets.QPushButton("全部初始化")
        init_button.clicked.connect(self.cta_engine.init_all_strategies)

        start_button = QtWidgets.QPushButton("全部启动")
        start_button.clicked.connect(self.cta_engine.start_all_strategies)

        stop_button = QtWidgets.QPushButton("全部停止")
        stop_button.clicked.connect(self.cta_engine.stop_all_strategies)

        clear_button = QtWidgets.QPushButton("清空日志")
        clear_button.clicked.connect(self.clear_log)

        self.scroll_layout = QtWidgets.QVBoxLayout()
        self.scroll_layout.addStretch()

        scroll_widget = QtWidgets.QWidget()
        scroll_widget.setLayout(self.scroll_layout)

        scroll_area = QtWidgets.QScrollArea()
        scroll_area.setWidgetResizable(True)
        scroll_area.setWidget(scroll_widget)

        self.log_monitor = LogMonitor(self.main_engine, self.event_engine)

        self.stop_order_monitor = StopOrderMonitor(self.main_engine,
                                                   self.event_engine)

        # Set layout
        hbox1 = QtWidgets.QHBoxLayout()
        hbox1.addWidget(self.class_combo)
        hbox1.addWidget(add_button)
        hbox1.addStretch()
        hbox1.addWidget(init_button)
        hbox1.addWidget(start_button)
        hbox1.addWidget(stop_button)
        hbox1.addWidget(clear_button)

        grid = QtWidgets.QGridLayout()
        grid.addWidget(scroll_area, 0, 0, 2, 1)
        grid.addWidget(self.stop_order_monitor, 0, 1)
        grid.addWidget(self.log_monitor, 1, 1)

        vbox = QtWidgets.QVBoxLayout()
        vbox.addLayout(hbox1)
        vbox.addLayout(grid)

        self.setLayout(vbox)

    def update_class_combo(self):
        """"""
        self.class_combo.addItems(
            self.cta_engine.get_all_strategy_class_names())

    def register_event(self):
        """"""
        self.signal_strategy.connect(self.process_strategy_event)

        self.event_engine.register(EVENT_CTA_STRATEGY,
                                   self.signal_strategy.emit)

    def process_strategy_event(self, event):
        """
        Update strategy status onto its monitor.
        """
        data = event.data
        strategy_name = data["strategy_name"]

        if strategy_name in self.managers:
            manager = self.managers[strategy_name]
            manager.update_data(data)
        else:
            manager = StrategyManager(self, self.cta_engine, data)
            self.scroll_layout.insertWidget(0, manager)
            self.managers[strategy_name] = manager

    def remove_strategy(self, strategy_name):
        """"""
        manager = self.managers.pop(strategy_name)
        manager.deleteLater()

    def add_strategy(self):
        """"""
        class_name = str(self.class_combo.currentText())
        if not class_name:
            return

        parameters = self.cta_engine.get_strategy_class_parameters(class_name)
        editor = SettingEditor(parameters, class_name=class_name)
        n = editor.exec_()

        if n == editor.Accepted:
            setting = editor.get_setting()
            vt_symbol = setting.pop("vt_symbol")
            strategy_name = setting.pop("strategy_name")

            self.cta_engine.add_strategy(class_name, strategy_name, vt_symbol,
                                         setting)

    def clear_log(self):
        """"""
        self.log_monitor.setRowCount(0)

    def show(self):
        """"""
        self.showMaximized()