[Add] new portfolio manager app
This commit is contained in:
parent
3cd0a23854
commit
c0d15ad9cb
16
vnpy/app/portfolio_manager/__init__.py
Normal file
16
vnpy/app/portfolio_manager/__init__.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from vnpy.trader.app import BaseApp
|
||||||
|
|
||||||
|
from .engine import PortfolioEngine, APP_NAME
|
||||||
|
|
||||||
|
|
||||||
|
class DataRecorderApp(BaseApp):
|
||||||
|
""""""
|
||||||
|
app_name = APP_NAME
|
||||||
|
app_module = __module__
|
||||||
|
app_path = Path(__file__).parent
|
||||||
|
display_name = "投资组合"
|
||||||
|
engine_class = PortfolioEngine
|
||||||
|
widget_name = "PortfolioManager"
|
||||||
|
icon_name = "portfolio.ico"
|
289
vnpy/app/portfolio_manager/engine.py
Normal file
289
vnpy/app/portfolio_manager/engine.py
Normal file
@ -0,0 +1,289 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
from typing import Dict, List, Set
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
from vnpy.event import Event, EventEngine
|
||||||
|
from vnpy.trader.engine import BaseEngine, MainEngine
|
||||||
|
from vnpy.trader.event import EVENT_TRADE, EVENT_ORDER, EVENT_TICK
|
||||||
|
from vnpy.trader.constant import Direction, Offset, OrderType
|
||||||
|
from vnpy.trader.object import (
|
||||||
|
OrderRequest, CancelRequest, SubscribeRequest,
|
||||||
|
OrderData, TradeData, TickData
|
||||||
|
)
|
||||||
|
from vnpy.trader.utility import load_json, save_json
|
||||||
|
|
||||||
|
|
||||||
|
APP_NAME = "PortfolioManager"
|
||||||
|
|
||||||
|
EVENT_PORTFOLIO_UPDATE = "ePortfioUpdate"
|
||||||
|
|
||||||
|
|
||||||
|
class PortfolioEngine(BaseEngine):
|
||||||
|
""""""
|
||||||
|
setting_filename = "portfolio_manager_setting.json"
|
||||||
|
|
||||||
|
def __init__(self, main_engine: MainEngine, event_engine: EventEngine):
|
||||||
|
""""""
|
||||||
|
super().__init__(main_engine, event_engine, APP_NAME)
|
||||||
|
|
||||||
|
self.strategies: Dict[str, PortfolioStrategy] = {}
|
||||||
|
self.symbol_strategy_map: Dict[str, List] = defaultdict(list)
|
||||||
|
self.order_strategy_map: Dict[str, PortfolioStrategy] = {}
|
||||||
|
self.active_orders: Set[str] = set()
|
||||||
|
|
||||||
|
self.register_event()
|
||||||
|
self.load_setting()
|
||||||
|
|
||||||
|
def load_setting(self):
|
||||||
|
""""""
|
||||||
|
setting: dict = load_json(self.setting_filename)
|
||||||
|
|
||||||
|
for d in setting.values():
|
||||||
|
self.add_strategy(
|
||||||
|
d["name"],
|
||||||
|
d["vt_symbol"],
|
||||||
|
d["size"],
|
||||||
|
d["net_pos"],
|
||||||
|
d["open_price"],
|
||||||
|
d["last_price"],
|
||||||
|
d["create_time"],
|
||||||
|
d["note_text"],
|
||||||
|
)
|
||||||
|
|
||||||
|
def save_setting(self):
|
||||||
|
""""""
|
||||||
|
setting: dict = {}
|
||||||
|
|
||||||
|
for strategy in self.strategies:
|
||||||
|
setting[strategy.name] = {
|
||||||
|
"name": strategy.name,
|
||||||
|
"vt_symbol": strategy.vt_symbol,
|
||||||
|
"size": strategy.size,
|
||||||
|
"net_pos": strategy.net_pos,
|
||||||
|
"open_price": strategy.open_price,
|
||||||
|
"last_price": strategy.last_price,
|
||||||
|
"create_time": strategy.create_time,
|
||||||
|
"note_text": strategy.note_text,
|
||||||
|
}
|
||||||
|
|
||||||
|
save_json(self.setting_filename, setting)
|
||||||
|
|
||||||
|
def register_event(self):
|
||||||
|
""""""
|
||||||
|
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_TICK, self.process_tick_event)
|
||||||
|
|
||||||
|
def process_order_event(self, event: Event):
|
||||||
|
""""""
|
||||||
|
order: OrderData = event.data
|
||||||
|
|
||||||
|
if order.vt_orderid not in self.active_orders:
|
||||||
|
return
|
||||||
|
|
||||||
|
if not order.is_active():
|
||||||
|
self.active_orders.remove(order.vt_orderid)
|
||||||
|
|
||||||
|
def process_trade_event(self, event: Event):
|
||||||
|
""""""
|
||||||
|
trade: TradeData = event.data
|
||||||
|
|
||||||
|
strategy: PortfolioStrategy = self.order_strategy_map.get(
|
||||||
|
trade.vt_orderid)
|
||||||
|
if strategy:
|
||||||
|
strategy.update_trade(
|
||||||
|
trade.trade_direction,
|
||||||
|
trade.trade_volume,
|
||||||
|
trade.trade_price
|
||||||
|
)
|
||||||
|
|
||||||
|
self.put_strategy_event(strategy.name)
|
||||||
|
|
||||||
|
def process_tick_event(self, event: Event):
|
||||||
|
""""""
|
||||||
|
tick: TickData = event.data
|
||||||
|
|
||||||
|
strategies: List = self.symbol_strategy_map[tick.vt_symbol]
|
||||||
|
for strategy in strategies:
|
||||||
|
strategy.update_price(tick.last_price)
|
||||||
|
|
||||||
|
self.put_strategy_event(strategy.name)
|
||||||
|
|
||||||
|
def add_strategy(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
vt_symbol: str,
|
||||||
|
size: int,
|
||||||
|
net_pos: int = 0,
|
||||||
|
open_price: float = 0,
|
||||||
|
last_price: float = 0,
|
||||||
|
create_time: str = "",
|
||||||
|
note_text: str = ""
|
||||||
|
):
|
||||||
|
""""""
|
||||||
|
if name in self.strategies:
|
||||||
|
return False
|
||||||
|
|
||||||
|
strategy = PortfolioStrategy(
|
||||||
|
name,
|
||||||
|
vt_symbol,
|
||||||
|
size,
|
||||||
|
net_pos,
|
||||||
|
open_price,
|
||||||
|
last_price,
|
||||||
|
create_time,
|
||||||
|
note_text
|
||||||
|
)
|
||||||
|
|
||||||
|
self.strategies[strategy.name] = strategy
|
||||||
|
self.symbol_strategy_map[strategy.vt_symbol].append(strategy)
|
||||||
|
|
||||||
|
self.subscribe_data(vt_symbol)
|
||||||
|
|
||||||
|
def remove_strategy(self, name: str):
|
||||||
|
""""""
|
||||||
|
if name not in self.strategies:
|
||||||
|
return False
|
||||||
|
|
||||||
|
strategy = self.strategies.pop(name)
|
||||||
|
self.symbol_strategy_map[strategy.vt_symbol].remove(strategy)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def subscribe_data(self, vt_symbol: str):
|
||||||
|
""""""
|
||||||
|
contract = self.main_engine.get_contract(vt_symbol)
|
||||||
|
if not contract:
|
||||||
|
return
|
||||||
|
|
||||||
|
req = SubscribeRequest(
|
||||||
|
symbol=contract.symbol,
|
||||||
|
exchange=contract.exchange
|
||||||
|
)
|
||||||
|
self.main_engine.subscribe(req, contract.gateway_name)
|
||||||
|
|
||||||
|
def send_order(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
price: float,
|
||||||
|
volume: int,
|
||||||
|
direction: Direction,
|
||||||
|
offset: Offset = Offset.NONE
|
||||||
|
):
|
||||||
|
""""""
|
||||||
|
strategy = self.strategies[name]
|
||||||
|
vt_symbol = strategy.vt_symbol
|
||||||
|
|
||||||
|
contract = self.main_engine.get_contract(vt_symbol)
|
||||||
|
if not contract:
|
||||||
|
return False
|
||||||
|
|
||||||
|
req = OrderRequest(
|
||||||
|
symbol=contract.symbol,
|
||||||
|
exchange=contract.exchange,
|
||||||
|
direction=direction,
|
||||||
|
type=OrderType.LIMIT,
|
||||||
|
volume=volume,
|
||||||
|
price=price,
|
||||||
|
offset=offset
|
||||||
|
)
|
||||||
|
vt_orderid = self.main_engine.send_order(req, contract.gateway_name)
|
||||||
|
|
||||||
|
self.order_strategy_map[vt_orderid] = strategy
|
||||||
|
self.active_orders.add(vt_orderid)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def cancel_order(self, vt_orderid: str):
|
||||||
|
""""""
|
||||||
|
if vt_orderid not in self.order_strategy_map:
|
||||||
|
return False
|
||||||
|
|
||||||
|
order = self.main_engine.get_order(vt_orderid)
|
||||||
|
|
||||||
|
req = CancelRequest(
|
||||||
|
orderid=order.orderid,
|
||||||
|
symbol=order.symbol,
|
||||||
|
exchange=order.exchange
|
||||||
|
)
|
||||||
|
self.main_engine.cancel_order(req, order.gateway_name)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def cancel_all(self, name: str):
|
||||||
|
""""""
|
||||||
|
for vt_orderid in self.active_orders:
|
||||||
|
strategy = self.order_symbol_map[vt_orderid]
|
||||||
|
if strategy.name == name:
|
||||||
|
self.cancel_order(vt_orderid)
|
||||||
|
|
||||||
|
def put_strategy_event(self, name: str):
|
||||||
|
""""""
|
||||||
|
strategy = self.strategies[name]
|
||||||
|
event = Event(EVENT_PORTFOLIO_UPDATE, strategy)
|
||||||
|
self.event_engine.put(event)
|
||||||
|
|
||||||
|
|
||||||
|
class PortfolioStrategy:
|
||||||
|
""""""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
vt_symbol: str,
|
||||||
|
size: int,
|
||||||
|
net_pos: int,
|
||||||
|
open_price: float,
|
||||||
|
last_price: float,
|
||||||
|
create_time: str,
|
||||||
|
note_text: str
|
||||||
|
):
|
||||||
|
""""""
|
||||||
|
self.name: str = name
|
||||||
|
self.vt_symbol: str = vt_symbol
|
||||||
|
self.size: int = size
|
||||||
|
|
||||||
|
self.net_pos: int = net_pos
|
||||||
|
self.open_price: float = open_price
|
||||||
|
self.last_price: float = last_price
|
||||||
|
|
||||||
|
self.pos_pnl: float = 0
|
||||||
|
self.realized_pnl: float = 0
|
||||||
|
|
||||||
|
self.create_time: str = ""
|
||||||
|
if create_time:
|
||||||
|
self.create_time = create_time
|
||||||
|
else:
|
||||||
|
self.create_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
|
self.note_text: str = note_text
|
||||||
|
|
||||||
|
self.calculate_pnl()
|
||||||
|
|
||||||
|
def calculate_pnl(self):
|
||||||
|
""""""
|
||||||
|
self.pos_pnl = (self.last_price - self.open_price) * \
|
||||||
|
self.net_pos * self.size
|
||||||
|
|
||||||
|
def update_trade(
|
||||||
|
self,
|
||||||
|
trade_direction: Direction,
|
||||||
|
trade_volume: int,
|
||||||
|
trade_price: float
|
||||||
|
):
|
||||||
|
""""""
|
||||||
|
if trade_direction == Direction.LONG:
|
||||||
|
self.net_pos += trade_volume
|
||||||
|
else:
|
||||||
|
self.net_pos -= trade_volume
|
||||||
|
|
||||||
|
self.calculate_pnl()
|
||||||
|
|
||||||
|
def update_price(self, last_price: float):
|
||||||
|
""""""
|
||||||
|
self.last_price = last_price
|
||||||
|
self.calculate_pnl()
|
||||||
|
|
||||||
|
def update_note(self, note_text: str):
|
||||||
|
""""""
|
||||||
|
self.note_text = note_text
|
1
vnpy/app/portfolio_manager/ui/__init__.py
Normal file
1
vnpy/app/portfolio_manager/ui/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from .widget import PortfolioManager
|
BIN
vnpy/app/portfolio_manager/ui/portfolio.ico
Normal file
BIN
vnpy/app/portfolio_manager/ui/portfolio.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
845
vnpy/app/portfolio_manager/ui/widget.py
Normal file
845
vnpy/app/portfolio_manager/ui/widget.py
Normal file
@ -0,0 +1,845 @@
|
|||||||
|
"""
|
||||||
|
Widget for spread trading.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from vnpy.event import EventEngine, Event
|
||||||
|
from vnpy.trader.engine import MainEngine
|
||||||
|
from vnpy.trader.constant import Direction, Offset
|
||||||
|
from vnpy.trader.ui import QtWidgets, QtCore, QtGui
|
||||||
|
from vnpy.trader.ui.widget import (
|
||||||
|
BaseMonitor, BaseCell,
|
||||||
|
BidCell, AskCell,
|
||||||
|
TimeCell, PnlCell,
|
||||||
|
DirectionCell, EnumCell,
|
||||||
|
)
|
||||||
|
|
||||||
|
from ..engine import (
|
||||||
|
SpreadEngine,
|
||||||
|
SpreadStrategyEngine,
|
||||||
|
APP_NAME,
|
||||||
|
EVENT_SPREAD_DATA,
|
||||||
|
EVENT_SPREAD_POS,
|
||||||
|
EVENT_SPREAD_LOG,
|
||||||
|
EVENT_SPREAD_ALGO,
|
||||||
|
EVENT_SPREAD_STRATEGY
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SpreadManager(QtWidgets.QWidget):
|
||||||
|
""""""
|
||||||
|
|
||||||
|
def __init__(self, main_engine: MainEngine, event_engine: EventEngine):
|
||||||
|
""""""
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.main_engine = main_engine
|
||||||
|
self.event_engine = event_engine
|
||||||
|
|
||||||
|
self.spread_engine = main_engine.get_engine(APP_NAME)
|
||||||
|
|
||||||
|
self.init_ui()
|
||||||
|
|
||||||
|
def init_ui(self):
|
||||||
|
""""""
|
||||||
|
self.setWindowTitle("价差交易")
|
||||||
|
|
||||||
|
self.algo_dialog = SpreadAlgoWidget(self.spread_engine)
|
||||||
|
algo_group = self.create_group("交易", self.algo_dialog)
|
||||||
|
algo_group.setMaximumWidth(300)
|
||||||
|
|
||||||
|
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.spread_engine
|
||||||
|
)
|
||||||
|
|
||||||
|
self.strategy_monitor = SpreadStrategyMonitor(
|
||||||
|
self.spread_engine
|
||||||
|
)
|
||||||
|
|
||||||
|
grid = QtWidgets.QGridLayout()
|
||||||
|
grid.addWidget(self.create_group("价差", self.data_monitor), 0, 0)
|
||||||
|
grid.addWidget(self.create_group("日志", self.log_monitor), 1, 0)
|
||||||
|
grid.addWidget(self.create_group("算法", self.algo_monitor), 0, 1)
|
||||||
|
grid.addWidget(self.create_group("策略", self.strategy_monitor), 1, 1)
|
||||||
|
|
||||||
|
hbox = QtWidgets.QHBoxLayout()
|
||||||
|
hbox.addWidget(algo_group)
|
||||||
|
hbox.addLayout(grid)
|
||||||
|
|
||||||
|
self.setLayout(hbox)
|
||||||
|
|
||||||
|
def show(self):
|
||||||
|
""""""
|
||||||
|
self.spread_engine.start()
|
||||||
|
self.algo_dialog.update_class_combo()
|
||||||
|
self.showMaximized()
|
||||||
|
|
||||||
|
def create_group(self, title: str, widget: QtWidgets.QWidget):
|
||||||
|
""""""
|
||||||
|
group = QtWidgets.QGroupBox()
|
||||||
|
|
||||||
|
vbox = QtWidgets.QVBoxLayout()
|
||||||
|
vbox.addWidget(widget)
|
||||||
|
|
||||||
|
group.setLayout(vbox)
|
||||||
|
group.setTitle(title)
|
||||||
|
|
||||||
|
return group
|
||||||
|
|
||||||
|
|
||||||
|
class SpreadDataMonitor(BaseMonitor):
|
||||||
|
"""
|
||||||
|
Monitor for spread data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
event_type = EVENT_SPREAD_DATA
|
||||||
|
data_key = "name"
|
||||||
|
sorting = False
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"name": {"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},
|
||||||
|
"price_formula": {"display": "定价", "cell": BaseCell, "update": False},
|
||||||
|
"trading_formula": {"display": "交易", "cell": BaseCell, "update": False},
|
||||||
|
}
|
||||||
|
|
||||||
|
def register_event(self):
|
||||||
|
"""
|
||||||
|
Register event handler into event engine.
|
||||||
|
"""
|
||||||
|
super().register_event()
|
||||||
|
self.event_engine.register(EVENT_SPREAD_POS, self.signal.emit)
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
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": DirectionCell, "update": False},
|
||||||
|
"offset": {"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},
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, spread_engine: SpreadEngine):
|
||||||
|
""""""
|
||||||
|
super().__init__(spread_engine.main_engine, spread_engine.event_engine)
|
||||||
|
|
||||||
|
self.spread_engine = spread_engine
|
||||||
|
|
||||||
|
def init_ui(self):
|
||||||
|
"""
|
||||||
|
Connect signal.
|
||||||
|
"""
|
||||||
|
super().init_ui()
|
||||||
|
|
||||||
|
self.setToolTip("双击单元格停止算法")
|
||||||
|
self.itemDoubleClicked.connect(self.stop_algo)
|
||||||
|
|
||||||
|
def stop_algo(self, cell):
|
||||||
|
"""
|
||||||
|
Stop algo if cell double clicked.
|
||||||
|
"""
|
||||||
|
algo = cell.get_data()
|
||||||
|
self.spread_engine.stop_algo(algo.algoid)
|
||||||
|
|
||||||
|
|
||||||
|
class SpreadAlgoWidget(QtWidgets.QFrame):
|
||||||
|
""""""
|
||||||
|
|
||||||
|
def __init__(self, spread_engine: SpreadEngine):
|
||||||
|
""""""
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.spread_engine: SpreadEngine = spread_engine
|
||||||
|
self.strategy_engine: SpreadStrategyEngine = spread_engine.strategy_engine
|
||||||
|
|
||||||
|
self.init_ui()
|
||||||
|
|
||||||
|
def init_ui(self):
|
||||||
|
""""""
|
||||||
|
self.setWindowTitle("启动算法")
|
||||||
|
self.setFrameShape(self.Box)
|
||||||
|
self.setLineWidth(1)
|
||||||
|
|
||||||
|
self.name_line = QtWidgets.QLineEdit()
|
||||||
|
|
||||||
|
self.direction_combo = QtWidgets.QComboBox()
|
||||||
|
self.direction_combo.addItems(
|
||||||
|
[Direction.LONG.value, Direction.SHORT.value]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.offset_combo = QtWidgets.QComboBox()
|
||||||
|
self.offset_combo.addItems(
|
||||||
|
[Offset.NONE.value, Offset.OPEN.value, Offset.CLOSE.value]
|
||||||
|
)
|
||||||
|
|
||||||
|
float_validator = QtGui.QDoubleValidator()
|
||||||
|
|
||||||
|
self.price_line = QtWidgets.QLineEdit()
|
||||||
|
self.price_line.setValidator(float_validator)
|
||||||
|
|
||||||
|
self.volume_line = QtWidgets.QLineEdit()
|
||||||
|
self.volume_line.setValidator(float_validator)
|
||||||
|
|
||||||
|
int_validator = QtGui.QIntValidator()
|
||||||
|
|
||||||
|
self.payup_line = QtWidgets.QLineEdit()
|
||||||
|
self.payup_line.setValidator(int_validator)
|
||||||
|
|
||||||
|
self.interval_line = QtWidgets.QLineEdit()
|
||||||
|
self.interval_line.setValidator(int_validator)
|
||||||
|
|
||||||
|
button_start = QtWidgets.QPushButton("启动")
|
||||||
|
button_start.clicked.connect(self.start_algo)
|
||||||
|
|
||||||
|
self.lock_combo = QtWidgets.QComboBox()
|
||||||
|
self.lock_combo.addItems(
|
||||||
|
["否", "是"]
|
||||||
|
)
|
||||||
|
|
||||||
|
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.strategy_engine.init_all_strategies)
|
||||||
|
|
||||||
|
start_button = QtWidgets.QPushButton("全部启动")
|
||||||
|
start_button.clicked.connect(self.strategy_engine.start_all_strategies)
|
||||||
|
|
||||||
|
stop_button = QtWidgets.QPushButton("全部停止")
|
||||||
|
stop_button.clicked.connect(self.strategy_engine.stop_all_strategies)
|
||||||
|
|
||||||
|
add_spread_button = QtWidgets.QPushButton("创建价差")
|
||||||
|
add_spread_button.clicked.connect(self.add_spread)
|
||||||
|
|
||||||
|
remove_spread_button = QtWidgets.QPushButton("移除价差")
|
||||||
|
remove_spread_button.clicked.connect(self.remove_spread)
|
||||||
|
|
||||||
|
form = QtWidgets.QFormLayout()
|
||||||
|
form.addRow("价差", self.name_line)
|
||||||
|
form.addRow("方向", self.direction_combo)
|
||||||
|
form.addRow("开平", self.offset_combo)
|
||||||
|
form.addRow("价格", self.price_line)
|
||||||
|
form.addRow("数量", self.volume_line)
|
||||||
|
form.addRow("超价", self.payup_line)
|
||||||
|
form.addRow("间隔", self.interval_line)
|
||||||
|
form.addRow("锁仓", self.lock_combo)
|
||||||
|
form.addRow(button_start)
|
||||||
|
|
||||||
|
vbox = QtWidgets.QVBoxLayout()
|
||||||
|
vbox.addLayout(form)
|
||||||
|
vbox.addStretch()
|
||||||
|
vbox.addWidget(self.class_combo)
|
||||||
|
vbox.addWidget(add_button)
|
||||||
|
vbox.addWidget(init_button)
|
||||||
|
vbox.addWidget(start_button)
|
||||||
|
vbox.addWidget(stop_button)
|
||||||
|
vbox.addStretch()
|
||||||
|
vbox.addWidget(add_spread_button)
|
||||||
|
vbox.addWidget(remove_spread_button)
|
||||||
|
|
||||||
|
self.setLayout(vbox)
|
||||||
|
|
||||||
|
def start_algo(self):
|
||||||
|
""""""
|
||||||
|
name = self.name_line.text()
|
||||||
|
direction = Direction(self.direction_combo.currentText())
|
||||||
|
offset = Offset(self.offset_combo.currentText())
|
||||||
|
price = float(self.price_line.text())
|
||||||
|
volume = float(self.volume_line.text())
|
||||||
|
payup = int(self.payup_line.text())
|
||||||
|
interval = int(self.interval_line.text())
|
||||||
|
|
||||||
|
lock_str = self.lock_combo.currentText()
|
||||||
|
if lock_str == "是":
|
||||||
|
lock = True
|
||||||
|
else:
|
||||||
|
lock = False
|
||||||
|
|
||||||
|
self.spread_engine.start_algo(
|
||||||
|
name, direction, offset, price, volume, payup, interval, lock
|
||||||
|
)
|
||||||
|
|
||||||
|
def add_spread(self):
|
||||||
|
""""""
|
||||||
|
dialog = SpreadDataDialog(self.spread_engine)
|
||||||
|
dialog.exec_()
|
||||||
|
|
||||||
|
def remove_spread(self):
|
||||||
|
""""""
|
||||||
|
dialog = SpreadRemoveDialog(self.spread_engine)
|
||||||
|
dialog.exec_()
|
||||||
|
|
||||||
|
def update_class_combo(self):
|
||||||
|
""""""
|
||||||
|
self.class_combo.addItems(
|
||||||
|
self.strategy_engine.get_all_strategy_class_names()
|
||||||
|
)
|
||||||
|
|
||||||
|
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.strategy_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()
|
||||||
|
spread_name = setting.pop("spread_name")
|
||||||
|
strategy_name = setting.pop("strategy_name")
|
||||||
|
|
||||||
|
self.strategy_engine.add_strategy(
|
||||||
|
class_name, strategy_name, spread_name, setting
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SpreadDataDialog(QtWidgets.QDialog):
|
||||||
|
""""""
|
||||||
|
|
||||||
|
def __init__(self, spread_engine: SpreadEngine):
|
||||||
|
""""""
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.spread_engine: SpreadEngine = spread_engine
|
||||||
|
|
||||||
|
self.leg_widgets = []
|
||||||
|
|
||||||
|
self.init_ui()
|
||||||
|
|
||||||
|
def init_ui(self):
|
||||||
|
""""""
|
||||||
|
self.setWindowTitle("创建价差")
|
||||||
|
|
||||||
|
self.name_line = QtWidgets.QLineEdit()
|
||||||
|
self.active_line = QtWidgets.QLineEdit()
|
||||||
|
|
||||||
|
self.min_volume_combo = QtWidgets.QComboBox()
|
||||||
|
self.min_volume_combo.addItems([
|
||||||
|
"1",
|
||||||
|
"0.1",
|
||||||
|
"0.01",
|
||||||
|
"0.001",
|
||||||
|
"0.0001",
|
||||||
|
"0.00001",
|
||||||
|
"0.000001",
|
||||||
|
])
|
||||||
|
|
||||||
|
self.grid = QtWidgets.QGridLayout()
|
||||||
|
|
||||||
|
button_add = QtWidgets.QPushButton("创建价差")
|
||||||
|
button_add.clicked.connect(self.add_spread)
|
||||||
|
|
||||||
|
Label = QtWidgets.QLabel
|
||||||
|
|
||||||
|
grid = QtWidgets.QGridLayout()
|
||||||
|
grid.addWidget(Label("价差名称"), 0, 0)
|
||||||
|
grid.addWidget(self.name_line, 0, 1, 1, 4)
|
||||||
|
grid.addWidget(Label("主动腿代码"), 1, 0)
|
||||||
|
grid.addWidget(self.active_line, 1, 1, 1, 4)
|
||||||
|
grid.addWidget(Label("最小交易量"), 2, 0)
|
||||||
|
grid.addWidget(self.min_volume_combo, 2, 1, 1, 4)
|
||||||
|
|
||||||
|
grid.addWidget(Label(""), 3, 0)
|
||||||
|
grid.addWidget(Label("本地代码"), 4, 1)
|
||||||
|
grid.addWidget(Label("价格乘数"), 4, 2)
|
||||||
|
grid.addWidget(Label("交易乘数"), 4, 3)
|
||||||
|
grid.addWidget(Label("合约模式"), 4, 4)
|
||||||
|
|
||||||
|
int_validator = QtGui.QIntValidator()
|
||||||
|
|
||||||
|
leg_count = 5
|
||||||
|
for i in range(leg_count):
|
||||||
|
symbol_line = QtWidgets.QLineEdit()
|
||||||
|
|
||||||
|
price_line = QtWidgets.QLineEdit()
|
||||||
|
price_line.setValidator(int_validator)
|
||||||
|
|
||||||
|
trading_line = QtWidgets.QLineEdit()
|
||||||
|
trading_line.setValidator(int_validator)
|
||||||
|
|
||||||
|
inverse_combo = QtWidgets.QComboBox()
|
||||||
|
inverse_combo.addItems(["正向", "反向"])
|
||||||
|
|
||||||
|
grid.addWidget(Label("腿{}".format(i + 1)), 5 + i, 0)
|
||||||
|
grid.addWidget(symbol_line, 5 + i, 1)
|
||||||
|
grid.addWidget(price_line, 5 + i, 2)
|
||||||
|
grid.addWidget(trading_line, 5 + i, 3)
|
||||||
|
grid.addWidget(inverse_combo, 5 + i, 4)
|
||||||
|
|
||||||
|
d = {
|
||||||
|
"symbol": symbol_line,
|
||||||
|
"price": price_line,
|
||||||
|
"trading": trading_line,
|
||||||
|
"inverse": inverse_combo
|
||||||
|
}
|
||||||
|
self.leg_widgets.append(d)
|
||||||
|
|
||||||
|
grid.addWidget(Label(""), 5 + leg_count, 0,)
|
||||||
|
grid.addWidget(button_add, 6 + leg_count, 0, 1, 5)
|
||||||
|
|
||||||
|
self.setLayout(grid)
|
||||||
|
|
||||||
|
def add_spread(self):
|
||||||
|
""""""
|
||||||
|
spread_name = self.name_line.text()
|
||||||
|
if not spread_name:
|
||||||
|
QtWidgets.QMessageBox.warning(
|
||||||
|
self,
|
||||||
|
"创建失败",
|
||||||
|
"请输入价差名称",
|
||||||
|
QtWidgets.QMessageBox.Ok
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
active_symbol = self.active_line.text()
|
||||||
|
min_volume = float(self.min_volume_combo.currentText())
|
||||||
|
|
||||||
|
leg_settings = {}
|
||||||
|
for d in self.leg_widgets:
|
||||||
|
try:
|
||||||
|
vt_symbol = d["symbol"].text()
|
||||||
|
price_multiplier = int(d["price"].text())
|
||||||
|
trading_multiplier = int(d["trading"].text())
|
||||||
|
|
||||||
|
if d["inverse"].currentText() == "正向":
|
||||||
|
inverse_contract = False
|
||||||
|
else:
|
||||||
|
inverse_contract = True
|
||||||
|
|
||||||
|
leg_settings[vt_symbol] = {
|
||||||
|
"vt_symbol": vt_symbol,
|
||||||
|
"price_multiplier": price_multiplier,
|
||||||
|
"trading_multiplier": trading_multiplier,
|
||||||
|
"inverse_contract": inverse_contract
|
||||||
|
}
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if len(leg_settings) < 2:
|
||||||
|
QtWidgets.QMessageBox.warning(
|
||||||
|
self,
|
||||||
|
"创建失败",
|
||||||
|
"价差最少需要2条腿",
|
||||||
|
QtWidgets.QMessageBox.Ok
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
if active_symbol not in leg_settings:
|
||||||
|
QtWidgets.QMessageBox.warning(
|
||||||
|
self,
|
||||||
|
"创建失败",
|
||||||
|
"各条腿中找不到主动腿代码",
|
||||||
|
QtWidgets.QMessageBox.Ok
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.spread_engine.add_spread(
|
||||||
|
spread_name,
|
||||||
|
list(leg_settings.values()),
|
||||||
|
active_symbol,
|
||||||
|
min_volume
|
||||||
|
)
|
||||||
|
self.accept()
|
||||||
|
|
||||||
|
|
||||||
|
class SpreadRemoveDialog(QtWidgets.QDialog):
|
||||||
|
""""""
|
||||||
|
|
||||||
|
def __init__(self, spread_engine: SpreadEngine):
|
||||||
|
""""""
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.spread_engine: SpreadEngine = spread_engine
|
||||||
|
|
||||||
|
self.init_ui()
|
||||||
|
|
||||||
|
def init_ui(self):
|
||||||
|
""""""
|
||||||
|
self.setWindowTitle("移除价差")
|
||||||
|
self.setMinimumWidth(300)
|
||||||
|
|
||||||
|
self.name_combo = QtWidgets.QComboBox()
|
||||||
|
spreads = self.spread_engine.get_all_spreads()
|
||||||
|
for spread in spreads:
|
||||||
|
self.name_combo.addItem(spread.name)
|
||||||
|
|
||||||
|
button_remove = QtWidgets.QPushButton("移除")
|
||||||
|
button_remove.clicked.connect(self.remove_spread)
|
||||||
|
|
||||||
|
hbox = QtWidgets.QHBoxLayout()
|
||||||
|
hbox.addWidget(self.name_combo)
|
||||||
|
hbox.addWidget(button_remove)
|
||||||
|
|
||||||
|
self.setLayout(hbox)
|
||||||
|
|
||||||
|
def remove_spread(self):
|
||||||
|
""""""
|
||||||
|
spread_name = self.name_combo.currentText()
|
||||||
|
self.spread_engine.remove_spread(spread_name)
|
||||||
|
self.accept()
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
|
||||||
|
class SpreadStrategyWidget(QtWidgets.QFrame):
|
||||||
|
"""
|
||||||
|
Manager for a strategy
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
strategy_monitor: SpreadStrategyMonitor,
|
||||||
|
strategy_engine: SpreadStrategyEngine,
|
||||||
|
data: dict
|
||||||
|
):
|
||||||
|
""""""
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.strategy_monitor = strategy_monitor
|
||||||
|
self.strategy_engine = strategy_engine
|
||||||
|
|
||||||
|
self.strategy_name = data["strategy_name"]
|
||||||
|
self._data = data
|
||||||
|
|
||||||
|
self.init_ui()
|
||||||
|
|
||||||
|
def init_ui(self):
|
||||||
|
""""""
|
||||||
|
self.setFixedHeight(300)
|
||||||
|
self.setFrameShape(self.Box)
|
||||||
|
self.setLineWidth(1)
|
||||||
|
|
||||||
|
init_button = QtWidgets.QPushButton("初始化")
|
||||||
|
init_button.clicked.connect(self.init_strategy)
|
||||||
|
|
||||||
|
start_button = QtWidgets.QPushButton("启动")
|
||||||
|
start_button.clicked.connect(self.start_strategy)
|
||||||
|
|
||||||
|
stop_button = QtWidgets.QPushButton("停止")
|
||||||
|
stop_button.clicked.connect(self.stop_strategy)
|
||||||
|
|
||||||
|
edit_button = QtWidgets.QPushButton("编辑")
|
||||||
|
edit_button.clicked.connect(self.edit_strategy)
|
||||||
|
|
||||||
|
remove_button = QtWidgets.QPushButton("移除")
|
||||||
|
remove_button.clicked.connect(self.remove_strategy)
|
||||||
|
|
||||||
|
strategy_name = self._data["strategy_name"]
|
||||||
|
spread_name = self._data["spread_name"]
|
||||||
|
class_name = self._data["class_name"]
|
||||||
|
author = self._data["author"]
|
||||||
|
|
||||||
|
label_text = (
|
||||||
|
f"{strategy_name} - {spread_name} ({class_name} by {author})"
|
||||||
|
)
|
||||||
|
label = QtWidgets.QLabel(label_text)
|
||||||
|
label.setAlignment(QtCore.Qt.AlignCenter)
|
||||||
|
|
||||||
|
self.parameters_monitor = StrategyDataMonitor(self._data["parameters"])
|
||||||
|
self.variables_monitor = StrategyDataMonitor(self._data["variables"])
|
||||||
|
|
||||||
|
hbox = QtWidgets.QHBoxLayout()
|
||||||
|
hbox.addWidget(init_button)
|
||||||
|
hbox.addWidget(start_button)
|
||||||
|
hbox.addWidget(stop_button)
|
||||||
|
hbox.addWidget(edit_button)
|
||||||
|
hbox.addWidget(remove_button)
|
||||||
|
|
||||||
|
vbox = QtWidgets.QVBoxLayout()
|
||||||
|
vbox.addWidget(label)
|
||||||
|
vbox.addLayout(hbox)
|
||||||
|
vbox.addWidget(self.parameters_monitor)
|
||||||
|
vbox.addWidget(self.variables_monitor)
|
||||||
|
self.setLayout(vbox)
|
||||||
|
|
||||||
|
def update_data(self, data: dict):
|
||||||
|
""""""
|
||||||
|
self._data = data
|
||||||
|
|
||||||
|
self.parameters_monitor.update_data(data["parameters"])
|
||||||
|
self.variables_monitor.update_data(data["variables"])
|
||||||
|
|
||||||
|
def init_strategy(self):
|
||||||
|
""""""
|
||||||
|
self.strategy_engine.init_strategy(self.strategy_name)
|
||||||
|
|
||||||
|
def start_strategy(self):
|
||||||
|
""""""
|
||||||
|
self.strategy_engine.start_strategy(self.strategy_name)
|
||||||
|
|
||||||
|
def stop_strategy(self):
|
||||||
|
""""""
|
||||||
|
self.strategy_engine.stop_strategy(self.strategy_name)
|
||||||
|
|
||||||
|
def edit_strategy(self):
|
||||||
|
""""""
|
||||||
|
strategy_name = self._data["strategy_name"]
|
||||||
|
|
||||||
|
parameters = self.strategy_engine.get_strategy_parameters(
|
||||||
|
strategy_name)
|
||||||
|
editor = SettingEditor(parameters, strategy_name=strategy_name)
|
||||||
|
n = editor.exec_()
|
||||||
|
|
||||||
|
if n == editor.Accepted:
|
||||||
|
setting = editor.get_setting()
|
||||||
|
self.strategy_engine.edit_strategy(strategy_name, setting)
|
||||||
|
|
||||||
|
def remove_strategy(self):
|
||||||
|
""""""
|
||||||
|
result = self.strategy_engine.remove_strategy(self.strategy_name)
|
||||||
|
|
||||||
|
# Only remove strategy gui manager if it has been removed from engine
|
||||||
|
if result:
|
||||||
|
self.strategy_monitor.remove_strategy(self.strategy_name)
|
||||||
|
|
||||||
|
|
||||||
|
class StrategyDataMonitor(QtWidgets.QTableWidget):
|
||||||
|
"""
|
||||||
|
Table monitor for parameters and variables.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, data: dict):
|
||||||
|
""""""
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self.cells = {}
|
||||||
|
|
||||||
|
self.init_ui()
|
||||||
|
|
||||||
|
def init_ui(self):
|
||||||
|
""""""
|
||||||
|
labels = list(self._data.keys())
|
||||||
|
self.setColumnCount(len(labels))
|
||||||
|
self.setHorizontalHeaderLabels(labels)
|
||||||
|
|
||||||
|
self.setRowCount(1)
|
||||||
|
self.verticalHeader().setSectionResizeMode(
|
||||||
|
QtWidgets.QHeaderView.Stretch
|
||||||
|
)
|
||||||
|
self.verticalHeader().setVisible(False)
|
||||||
|
self.setEditTriggers(self.NoEditTriggers)
|
||||||
|
|
||||||
|
for column, name in enumerate(self._data.keys()):
|
||||||
|
value = self._data[name]
|
||||||
|
|
||||||
|
cell = QtWidgets.QTableWidgetItem(str(value))
|
||||||
|
cell.setTextAlignment(QtCore.Qt.AlignCenter)
|
||||||
|
|
||||||
|
self.setItem(0, column, cell)
|
||||||
|
self.cells[name] = cell
|
||||||
|
|
||||||
|
def update_data(self, data: dict):
|
||||||
|
""""""
|
||||||
|
for name, value in data.items():
|
||||||
|
cell = self.cells[name]
|
||||||
|
cell.setText(str(value))
|
||||||
|
|
||||||
|
|
||||||
|
class SettingEditor(QtWidgets.QDialog):
|
||||||
|
"""
|
||||||
|
For creating new strategy and editing strategy parameters.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, parameters: dict, strategy_name: str = "", class_name: str = ""
|
||||||
|
):
|
||||||
|
""""""
|
||||||
|
super(SettingEditor, self).__init__()
|
||||||
|
|
||||||
|
self.parameters = parameters
|
||||||
|
self.strategy_name = strategy_name
|
||||||
|
self.class_name = class_name
|
||||||
|
|
||||||
|
self.edits = {}
|
||||||
|
|
||||||
|
self.init_ui()
|
||||||
|
|
||||||
|
def init_ui(self):
|
||||||
|
""""""
|
||||||
|
form = QtWidgets.QFormLayout()
|
||||||
|
|
||||||
|
# Add spread_name and name edit if add new strategy
|
||||||
|
if self.class_name:
|
||||||
|
self.setWindowTitle(f"添加策略:{self.class_name}")
|
||||||
|
button_text = "添加"
|
||||||
|
parameters = {"strategy_name": "", "spread_name": ""}
|
||||||
|
parameters.update(self.parameters)
|
||||||
|
else:
|
||||||
|
self.setWindowTitle(f"参数编辑:{self.strategy_name}")
|
||||||
|
button_text = "确定"
|
||||||
|
parameters = self.parameters
|
||||||
|
|
||||||
|
for name, value in parameters.items():
|
||||||
|
type_ = type(value)
|
||||||
|
|
||||||
|
edit = QtWidgets.QLineEdit(str(value))
|
||||||
|
if type_ is int:
|
||||||
|
validator = QtGui.QIntValidator()
|
||||||
|
edit.setValidator(validator)
|
||||||
|
elif type_ is float:
|
||||||
|
validator = QtGui.QDoubleValidator()
|
||||||
|
edit.setValidator(validator)
|
||||||
|
|
||||||
|
form.addRow(f"{name} {type_}", edit)
|
||||||
|
|
||||||
|
self.edits[name] = (edit, type_)
|
||||||
|
|
||||||
|
button = QtWidgets.QPushButton(button_text)
|
||||||
|
button.clicked.connect(self.accept)
|
||||||
|
form.addRow(button)
|
||||||
|
|
||||||
|
self.setLayout(form)
|
||||||
|
|
||||||
|
def get_setting(self):
|
||||||
|
""""""
|
||||||
|
setting = {}
|
||||||
|
|
||||||
|
if self.class_name:
|
||||||
|
setting["class_name"] = self.class_name
|
||||||
|
|
||||||
|
for name, tp in self.edits.items():
|
||||||
|
edit, type_ = tp
|
||||||
|
value_text = edit.text()
|
||||||
|
|
||||||
|
if type_ == bool:
|
||||||
|
if value_text == "True":
|
||||||
|
value = True
|
||||||
|
else:
|
||||||
|
value = False
|
||||||
|
else:
|
||||||
|
value = type_(value_text)
|
||||||
|
|
||||||
|
setting[name] = value
|
||||||
|
|
||||||
|
return setting
|
Loading…
Reference in New Issue
Block a user