[Add] general functions of VN Trader
1. use json instead of shelve for setting/data storage 2. call original sys.__excepthook__ before showing exception with QMessageBox
This commit is contained in:
parent
ae78a7f0c0
commit
6c91951e02
3
.gitignore
vendored
3
.gitignore
vendored
@ -15,3 +15,6 @@
|
|||||||
build
|
build
|
||||||
dist
|
dist
|
||||||
*.local
|
*.local
|
||||||
|
|
||||||
|
# vn.py
|
||||||
|
.vntrader
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import importlib
|
import importlib
|
||||||
import os
|
import os
|
||||||
import shelve
|
|
||||||
import traceback
|
import traceback
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@ -21,7 +20,7 @@ from vnpy.trader.object import (
|
|||||||
)
|
)
|
||||||
from vnpy.trader.event import EVENT_TICK, EVENT_ORDER, EVENT_TRADE
|
from vnpy.trader.event import EVENT_TICK, EVENT_ORDER, EVENT_TRADE
|
||||||
from vnpy.trader.constant import Direction, PriceType, Interval
|
from vnpy.trader.constant import Direction, PriceType, Interval
|
||||||
from vnpy.trader.utility import get_temp_path, load_json, save_json
|
from vnpy.trader.utility import load_json, save_json
|
||||||
from vnpy.trader.database import DbTickData, DbBarData
|
from vnpy.trader.database import DbTickData, DbBarData
|
||||||
|
|
||||||
from .base import (
|
from .base import (
|
||||||
@ -43,7 +42,7 @@ class CtaEngine(BaseEngine):
|
|||||||
|
|
||||||
engine_type = EngineType.LIVE # live trading engine
|
engine_type = EngineType.LIVE # live trading engine
|
||||||
|
|
||||||
setting_filename = "cta_strategy_setting.vt"
|
setting_filename = "cta_strategy_setting.json"
|
||||||
data_filename = "cta_strategy_data.json"
|
data_filename = "cta_strategy_data.json"
|
||||||
|
|
||||||
def __init__(self, main_engine: MainEngine, event_engine: EventEngine):
|
def __init__(self, main_engine: MainEngine, event_engine: EventEngine):
|
||||||
@ -52,6 +51,7 @@ class CtaEngine(BaseEngine):
|
|||||||
main_engine, event_engine, "CtaStrategy")
|
main_engine, event_engine, "CtaStrategy")
|
||||||
|
|
||||||
self.setting_file = None # setting file object
|
self.setting_file = None # setting file object
|
||||||
|
self.strategy_setting = {} # strategy_name: dict
|
||||||
self.strategy_data = {} # strategy_name: dict
|
self.strategy_data = {} # strategy_name: dict
|
||||||
|
|
||||||
self.classes = {} # class_name: stategy_class
|
self.classes = {} # class_name: stategy_class
|
||||||
@ -80,7 +80,7 @@ class CtaEngine(BaseEngine):
|
|||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
""""""
|
""""""
|
||||||
self.save_strategy_setting()
|
pass
|
||||||
|
|
||||||
def register_event(self):
|
def register_event(self):
|
||||||
""""""
|
""""""
|
||||||
@ -410,7 +410,7 @@ class CtaEngine(BaseEngine):
|
|||||||
def init_strategy(self, strategy_name: str):
|
def init_strategy(self, strategy_name: str):
|
||||||
"""
|
"""
|
||||||
Init a strategy.
|
Init a strategy.
|
||||||
"""
|
"""
|
||||||
self.init_queue.put(strategy_name)
|
self.init_queue.put(strategy_name)
|
||||||
|
|
||||||
if not self.init_thread:
|
if not self.init_thread:
|
||||||
@ -612,12 +612,15 @@ class CtaEngine(BaseEngine):
|
|||||||
"""
|
"""
|
||||||
Load setting file.
|
Load setting file.
|
||||||
"""
|
"""
|
||||||
filepath = str(get_temp_path(self.setting_filename))
|
self.strategy_setting = load_json(self.setting_filename)
|
||||||
self.setting_file = shelve.open(filepath)
|
|
||||||
|
|
||||||
for tp in list(self.setting_file.values()):
|
for strategy_name, strategy_config in self.strategy_setting.items():
|
||||||
class_name, strategy_name, vt_symbol, setting = tp
|
self.add_strategy(
|
||||||
self.add_strategy(class_name, strategy_name, vt_symbol, setting)
|
strategy_config["class_name"],
|
||||||
|
strategy_name,
|
||||||
|
strategy_config["vt_symbol"],
|
||||||
|
strategy_config["setting"]
|
||||||
|
)
|
||||||
|
|
||||||
def update_strategy_setting(self, strategy_name: str, setting: dict):
|
def update_strategy_setting(self, strategy_name: str, setting: dict):
|
||||||
"""
|
"""
|
||||||
@ -625,13 +628,12 @@ class CtaEngine(BaseEngine):
|
|||||||
"""
|
"""
|
||||||
strategy = self.strategies[strategy_name]
|
strategy = self.strategies[strategy_name]
|
||||||
|
|
||||||
self.setting_file[strategy_name] = (
|
self.strategy_setting[strategy_name] = {
|
||||||
strategy.__class__.__name__,
|
"class_name": strategy.__class__.__name__,
|
||||||
strategy_name,
|
"vt_symbol": strategy.vt_symbol,
|
||||||
strategy.vt_symbol,
|
"setting": setting,
|
||||||
setting,
|
}
|
||||||
)
|
save_json(self.setting_filename, self.strategy_setting)
|
||||||
self.setting_file.sync()
|
|
||||||
|
|
||||||
def remove_strategy_setting(self, strategy_name: str):
|
def remove_strategy_setting(self, strategy_name: str):
|
||||||
"""
|
"""
|
||||||
@ -640,15 +642,8 @@ class CtaEngine(BaseEngine):
|
|||||||
if strategy_name not in self.setting_file:
|
if strategy_name not in self.setting_file:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.setting_file.pop(strategy_name)
|
self.strategy_setting.pop(strategy_name)
|
||||||
self.setting_file.sync()
|
save_json(self.setting_filename, self.strategy_setting)
|
||||||
|
|
||||||
def save_strategy_setting(self):
|
|
||||||
"""
|
|
||||||
Save and close setting file.
|
|
||||||
"""
|
|
||||||
if self.setting_file:
|
|
||||||
self.setting_file.close()
|
|
||||||
|
|
||||||
def put_stop_order_event(self, stop_order: StopOrder):
|
def put_stop_order_event(self, stop_order: StopOrder):
|
||||||
"""
|
"""
|
||||||
|
@ -5,7 +5,7 @@ class DoubleMaStrategy(CtaTemplate):
|
|||||||
author = "用Python的交易员"
|
author = "用Python的交易员"
|
||||||
|
|
||||||
fast_window = 10
|
fast_window = 10
|
||||||
slow_window = 20.10
|
slow_window = 20
|
||||||
|
|
||||||
fast_ma = 0.0
|
fast_ma = 0.0
|
||||||
slow_ma = 0.0
|
slow_ma = 0.0
|
||||||
|
@ -27,7 +27,7 @@ from vnpy.trader.object import (
|
|||||||
CancelRequest,
|
CancelRequest,
|
||||||
SubscribeRequest,
|
SubscribeRequest,
|
||||||
)
|
)
|
||||||
from vnpy.trader.utility import get_temp_path
|
from vnpy.trader.utility import get_folder_path
|
||||||
from vnpy.trader.event import EVENT_TIMER
|
from vnpy.trader.event import EVENT_TIMER
|
||||||
|
|
||||||
|
|
||||||
@ -305,8 +305,8 @@ class CtpMdApi(MdApi):
|
|||||||
|
|
||||||
# If not connected, then start connection first.
|
# If not connected, then start connection first.
|
||||||
if not self.connect_status:
|
if not self.connect_status:
|
||||||
path = get_temp_path(f"{self.gateway_name}_md_" )
|
path = get_folder_path(self.gateway_name.lower())
|
||||||
self.createFtdcMdApi(str(path))
|
self.createFtdcMdApi(str(path) + "\\Md")
|
||||||
|
|
||||||
self.registerFront(address)
|
self.registerFront(address)
|
||||||
self.init()
|
self.init()
|
||||||
@ -1076,8 +1076,8 @@ class CtpTdApi(TdApi):
|
|||||||
self.product_info = product_info
|
self.product_info = product_info
|
||||||
|
|
||||||
if not self.connect_status:
|
if not self.connect_status:
|
||||||
path = get_temp_path(f"{self.gateway_name}_td_")
|
path = get_folder_path(self.gateway_name.lower())
|
||||||
self.createFtdcTraderApi(str(path))
|
self.createFtdcTraderApi(str(path) + "\\Td")
|
||||||
|
|
||||||
self.subscribePrivateTopic(0)
|
self.subscribePrivateTopic(0)
|
||||||
self.subscribePublicTopic(0)
|
self.subscribePublicTopic(0)
|
||||||
|
@ -4,10 +4,10 @@ from peewee import SqliteDatabase, Model, CharField, DateTimeField, FloatField
|
|||||||
|
|
||||||
from .constant import Exchange, Interval
|
from .constant import Exchange, Interval
|
||||||
from .object import BarData, TickData
|
from .object import BarData, TickData
|
||||||
from .utility import get_temp_path
|
from .utility import get_file_path
|
||||||
|
|
||||||
DB_NAME = "database.vt"
|
DB_NAME = "database.db"
|
||||||
DB = SqliteDatabase(str(get_temp_path(DB_NAME)))
|
DB = SqliteDatabase(str(get_file_path(DB_NAME)))
|
||||||
|
|
||||||
|
|
||||||
class DbBarData(Model):
|
class DbBarData(Model):
|
||||||
|
@ -24,7 +24,7 @@ from .event import (
|
|||||||
from .gateway import BaseGateway
|
from .gateway import BaseGateway
|
||||||
from .object import CancelRequest, LogData, OrderRequest, SubscribeRequest
|
from .object import CancelRequest, LogData, OrderRequest, SubscribeRequest
|
||||||
from .setting import SETTINGS
|
from .setting import SETTINGS
|
||||||
from .utility import Singleton, get_temp_path
|
from .utility import Singleton, get_folder_path
|
||||||
|
|
||||||
|
|
||||||
class MainEngine:
|
class MainEngine:
|
||||||
@ -246,7 +246,8 @@ class LogEngine(BaseEngine):
|
|||||||
"""
|
"""
|
||||||
today_date = datetime.now().strftime("%Y%m%d")
|
today_date = datetime.now().strftime("%Y%m%d")
|
||||||
filename = f"vt_{today_date}.log"
|
filename = f"vt_{today_date}.log"
|
||||||
file_path = get_temp_path(filename)
|
log_path = get_folder_path("log")
|
||||||
|
file_path = log_path.joinpath(filename)
|
||||||
|
|
||||||
file_handler = logging.FileHandler(
|
file_handler = logging.FileHandler(
|
||||||
file_path, mode="w", encoding="utf8"
|
file_path, mode="w", encoding="utf8"
|
||||||
|
@ -12,7 +12,12 @@ from ..utility import get_icon_path
|
|||||||
|
|
||||||
|
|
||||||
def excepthook(exctype, value, tb):
|
def excepthook(exctype, value, tb):
|
||||||
"""异常捕捉钩子"""
|
"""
|
||||||
|
Raise exception under debug mode, otherwise
|
||||||
|
show exception detail with QMessageBox.
|
||||||
|
"""
|
||||||
|
sys.__excepthook__(exctype, value, tb)
|
||||||
|
|
||||||
msg = "".join(traceback.format_exception(exctype, value, tb))
|
msg = "".join(traceback.format_exception(exctype, value, tb))
|
||||||
QtWidgets.QMessageBox.critical(
|
QtWidgets.QMessageBox.critical(
|
||||||
None, "Exception", msg, QtWidgets.QMessageBox.Ok
|
None, "Exception", msg, QtWidgets.QMessageBox.Ok
|
||||||
|
BIN
vnpy/trader/ui/ico/forum.ico
Normal file
BIN
vnpy/trader/ui/ico/forum.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
@ -2,6 +2,7 @@
|
|||||||
Implements main window of VN Trader.
|
Implements main window of VN Trader.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import webbrowser
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
@ -23,7 +24,7 @@ from .widget import (
|
|||||||
AboutDialog,
|
AboutDialog,
|
||||||
)
|
)
|
||||||
from ..engine import MainEngine
|
from ..engine import MainEngine
|
||||||
from ..utility import get_icon_path, get_trader_path
|
from ..utility import get_icon_path, TRADER_PATH
|
||||||
|
|
||||||
|
|
||||||
class MainWindow(QtWidgets.QMainWindow):
|
class MainWindow(QtWidgets.QMainWindow):
|
||||||
@ -37,8 +38,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
self.main_engine = main_engine
|
self.main_engine = main_engine
|
||||||
self.event_engine = event_engine
|
self.event_engine = event_engine
|
||||||
|
|
||||||
self.path = get_trader_path()
|
self.window_title = f"VN Trader [{TRADER_PATH}]"
|
||||||
self.window_title = f"VN Trader [{self.path}]"
|
|
||||||
|
|
||||||
self.connect_dialogs = {}
|
self.connect_dialogs = {}
|
||||||
self.widgets = {}
|
self.widgets = {}
|
||||||
@ -129,6 +129,10 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
help_menu, "测试邮件", "email.ico", self.send_test_email
|
help_menu, "测试邮件", "email.ico", self.send_test_email
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.add_menu_action(
|
||||||
|
help_menu, "社区论坛", "forum.ico", self.open_forum
|
||||||
|
)
|
||||||
|
|
||||||
self.add_menu_action(
|
self.add_menu_action(
|
||||||
help_menu,
|
help_menu,
|
||||||
"关于",
|
"关于",
|
||||||
@ -246,3 +250,8 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
Sending a test email.
|
Sending a test email.
|
||||||
"""
|
"""
|
||||||
self.main_engine.send_email("VN Trader", "testing")
|
self.main_engine.send_email("VN Trader", "testing")
|
||||||
|
|
||||||
|
def open_forum(self):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
webbrowser.open("https://www.vnpy.com/forum/")
|
||||||
|
@ -20,7 +20,7 @@ from ..event import (
|
|||||||
EVENT_LOG
|
EVENT_LOG
|
||||||
)
|
)
|
||||||
from ..object import OrderRequest, SubscribeRequest
|
from ..object import OrderRequest, SubscribeRequest
|
||||||
from ..utility import load_setting, save_setting
|
from ..utility import load_json, save_json
|
||||||
|
|
||||||
COLOR_LONG = QtGui.QColor("red")
|
COLOR_LONG = QtGui.QColor("red")
|
||||||
COLOR_SHORT = QtGui.QColor("green")
|
COLOR_SHORT = QtGui.QColor("green")
|
||||||
@ -490,7 +490,7 @@ class ConnectDialog(QtWidgets.QDialog):
|
|||||||
|
|
||||||
self.main_engine = main_engine
|
self.main_engine = main_engine
|
||||||
self.gateway_name = gateway_name
|
self.gateway_name = gateway_name
|
||||||
self.filename = f"Connect{gateway_name}.vt"
|
self.filename = f"connect_{gateway_name.lower()}.json"
|
||||||
|
|
||||||
self.widgets = {}
|
self.widgets = {}
|
||||||
|
|
||||||
@ -505,7 +505,7 @@ class ConnectDialog(QtWidgets.QDialog):
|
|||||||
self.gateway_name)
|
self.gateway_name)
|
||||||
|
|
||||||
# Saved setting provides field data used last time.
|
# Saved setting provides field data used last time.
|
||||||
loaded_setting = load_setting(self.filename)
|
loaded_setting = load_json(self.filename)
|
||||||
|
|
||||||
# Initialize line edits and form layout based on setting.
|
# Initialize line edits and form layout based on setting.
|
||||||
form = QtWidgets.QFormLayout()
|
form = QtWidgets.QFormLayout()
|
||||||
@ -550,7 +550,7 @@ class ConnectDialog(QtWidgets.QDialog):
|
|||||||
field_value = field_type(widget.text())
|
field_value = field_type(widget.text())
|
||||||
setting[field_name] = field_value
|
setting[field_name] = field_value
|
||||||
|
|
||||||
save_setting(self.filename, setting)
|
save_json(self.filename, setting)
|
||||||
|
|
||||||
self.main_engine.connect(setting, self.gateway_name)
|
self.main_engine.connect(setting, self.gateway_name)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user