diff --git a/vnpy/trader/engine.py b/vnpy/trader/engine.py index b473e33c..82b75484 100644 --- a/vnpy/trader/engine.py +++ b/vnpy/trader/engine.py @@ -5,6 +5,10 @@ import logging from datetime import datetime from abc import ABC from typing import Any +from threading import Thread +from queue import Queue, Empty +import smtplib +from email.message import EmailMessage from vnpy.event import EventEngine, Event @@ -62,6 +66,7 @@ class MainEngine: """ self.add_engine(LogEngine) self.add_engine(OmsEngine) + self.add_engine(EmailEngine) def write_log(self, msg: str): """ @@ -135,6 +140,9 @@ class MainEngine: Make sure every gateway and app is closed properly before programme exit. """ + for engine in self.engines.values(): + engine.close() + for gateway in self.gateways.values(): gateway.close() @@ -157,6 +165,10 @@ class BaseEngine(ABC): self.event_engine = event_engine self.engine_name = engine_name + def close(self): + """""" + pass + class LogEngine(BaseEngine): """ @@ -397,4 +409,67 @@ class OmsEngine(BaseEngine): order for order in self.active_orders.values() if order.vt_symbol == vt_symbol ] - return active_orders \ No newline at end of file + return active_orders + + +class EmailEngine(BaseEngine): + """ + Provides email sending function for VN Trader. + """ + + def __init__(self, main_engine: MainEngine, event_engine: EventEngine): + """""" + super(EmailEngine, self).__init__(main_engine, event_engine, "email") + + self.thread = Thread(target=self.run) + self.queue = Queue() + self.active = False + + self.main_engine.send_email = self.send_email + + def send_email(self, subject: str, content: str, receiver: str = ""): + """""" + # Start email engine when sending first email. + if not self.active: + self.start() + + # Use default receiver if not specified. + if not receiver: + receiver = SETTINGS["email.receiver"] + + msg = EmailMessage() + msg["From"] = SETTINGS["email.sender"] + msg["To"] = SETTINGS["email.receiver"] + msg["Subject"] = subject + msg.set_content(content) + + self.queue.put(msg) + + def run(self): + """""" + while self.active: + try: + msg = self.queue.get(block=True, timeout=1) + + with smtplib.SMTP_SSL(SETTINGS["email.server"], + SETTINGS["email.port"]) as smtp: + smtp.login( + SETTINGS["email.username"], + SETTINGS["email.password"] + ) + smtp.send_message(msg) + except Empty: + pass + + def start(self): + """""" + self.active = True + self.thread.start() + + def close(self): + """""" + if not self.active: + return + + self.active = False + self.thread.join() diff --git a/vnpy/trader/setting.py b/vnpy/trader/setting.py index 6cd288b4..f91d121d 100644 --- a/vnpy/trader/setting.py +++ b/vnpy/trader/setting.py @@ -7,9 +7,14 @@ from logging import CRITICAL SETTINGS = { "font.family": "Arial", "font.size": 12, - "log.active": True, "log.level": CRITICAL, "log.console": True, - "log.file": True + "log.file": True, + "email.server": "smtp.qq.com", + "email.port": 465, + "email.username": "", + "email.password": "", + "email.sender": "", + "email.receiver": "" } \ No newline at end of file diff --git a/vnpy/trader/ui/ico/email.ico b/vnpy/trader/ui/ico/email.ico new file mode 100644 index 00000000..ffda3a8f Binary files /dev/null and b/vnpy/trader/ui/ico/email.ico differ diff --git a/vnpy/trader/ui/mainwindow.py b/vnpy/trader/ui/mainwindow.py index 3153a4a7..58d84b71 100644 --- a/vnpy/trader/ui/mainwindow.py +++ b/vnpy/trader/ui/mainwindow.py @@ -103,6 +103,13 @@ class MainWindow(QtWidgets.QMainWindow): self.restore_window_setting ) + self.add_menu_action( + help_menu, + "测试邮件", + "email.ico", + self.send_test_email + ) + self.add_menu_action( help_menu, "关于", @@ -218,4 +225,10 @@ class MainWindow(QtWidgets.QMainWindow): Restore window to default setting. """ self.load_window_setting("default") - self.showMaximized() \ No newline at end of file + self.showMaximized() + + def send_test_email(self): + """ + Sending a test email. + """ + self.main_engine.send_email("VN Trader", "testing") diff --git a/vnpy/trader/ui/widget.py b/vnpy/trader/ui/widget.py index 6c32b24d..a49308fe 100644 --- a/vnpy/trader/ui/widget.py +++ b/vnpy/trader/ui/widget.py @@ -310,6 +310,11 @@ class TickMonitor(BaseMonitor): "cell": BaseCell, "update": False }, + "exchange": { + "display": "交易所", + "cell": BaseCell, + "update": False + }, "last_price": { "display": "最新价", "cell": BaseCell, @@ -419,6 +424,11 @@ class TradeMonitor(BaseMonitor): "cell": BaseCell, "update": False }, + "exchange": { + "display": "交易所", + "cell": BaseCell, + "update": False + }, "direction": { "display": "方向", "cell": DirectionCell, @@ -466,6 +476,11 @@ class OrderMonitor(BaseMonitor): "cell": BaseCell, "update": False }, + "exchange": { + "display": "交易所", + "cell": BaseCell, + "update": False + }, "direction": { "display": "方向", "cell": DirectionCell, @@ -540,6 +555,11 @@ class PositionMonitor(BaseMonitor): "cell": BaseCell, "update": False }, + "exchange": { + "display": "交易所", + "cell": BaseCell, + "update": False + }, "direction": { "display": "方向", "cell": DirectionCell,