[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:
vn.py 2019-02-17 22:59:01 +08:00
parent ae78a7f0c0
commit 6c91951e02
10 changed files with 58 additions and 45 deletions

3
.gitignore vendored
View File

@ -15,3 +15,6 @@
build build
dist dist
*.local *.local
# vn.py
.vntrader

View File

@ -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):
""" """

View File

@ -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

View File

@ -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)

View File

@ -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):

View File

@ -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"

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@ -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/")

View File

@ -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)