/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Concurrent; using System.Collections.Generic; using Python.Runtime; namespace QuantConnect.Notifications { /// /// Local/desktop implementation of messaging system for Lean Engine. /// public class NotificationManager { private const int RateLimit = 30; private int _count; private DateTime _resetTime; private readonly bool _liveMode; private readonly object _sync = new object(); /// /// Public access to the messages /// public ConcurrentQueue Messages { get; set; } /// /// Initialize the messaging system /// public NotificationManager(bool liveMode) { _count = 0; _liveMode = liveMode; Messages = new ConcurrentQueue(); // start counting reset time based on first invocation of NotificationManager _resetTime = default(DateTime); } /// /// Send an email to the address specified for live trading notifications. /// /// Subject of the email /// Message body, up to 10kb /// Data attachment (optional) /// Email address to send to /// Optional email headers to use public bool Email(string address, string subject, string message, string data, PyObject headers) { return Email(address, subject, message, data, headers.ConvertToDictionary()); } /// /// Send an email to the address specified for live trading notifications. /// /// Subject of the email /// Message body, up to 10kb /// Data attachment (optional) /// Email address to send to /// Optional email headers to use public bool Email(string address, string subject, string message, string data = "", Dictionary headers = null) { if (!Allow()) { return false; } var email = new NotificationEmail(address, subject, message, data, headers); Messages.Enqueue(email); return true; } /// /// Send an SMS to the phone number specified /// /// Phone number to send to /// Message to send public bool Sms(string phoneNumber, string message) { if (!Allow()) { return false; } var sms = new NotificationSms(phoneNumber, message); Messages.Enqueue(sms); return true; } /// /// Place REST POST call to the specified address with the specified DATA. /// Python overload for Headers parameter. /// /// Endpoint address /// Data to send in body JSON encoded /// Optional headers to use public bool Web(string address, object data, PyObject headers) { return Web(address, data, headers.ConvertToDictionary()); } /// /// Place REST POST call to the specified address with the specified DATA. /// /// Endpoint address /// Data to send in body JSON encoded (optional) /// Optional headers to use public bool Web(string address, object data = null, Dictionary headers = null) { if (!Allow()) { return false; } var web = new NotificationWeb(address, data, headers); Messages.Enqueue(web); return true; } /// /// Send a telegram message to the chat ID specified, supply token for custom bot. /// Note: Requires bot to have chat with user or be in the group specified by ID. /// /// Chat or group ID to send message to /// Message to send /// Bot token to use for this message public bool Telegram(string id, string message, string token = null) { if (!Allow()) { return false; } var telegram = new NotificationTelegram(id, message, token); Messages.Enqueue(telegram); return true; } /// /// Send a file to the FTP specified server using password authentication over unsecure FTP. /// /// FTP server hostname /// The FTP server username /// The FTP server password /// The path to file on the FTP server /// The contents of the file /// The FTP server port. Defaults to 21 public bool Ftp(string hostname, string username, string password, string filePath, byte[] fileContent, int? port = null) { return Ftp(hostname, username, password, filePath, fileContent, secure: false, port); } /// /// Send a file to the FTP specified server using password authentication over unsecure FTP. /// /// FTP server hostname /// The FTP server username /// The FTP server password /// The path to file on the FTP server /// The string contents of the file /// The FTP server port. Defaults to 21 public bool Ftp(string hostname, string username, string password, string filePath, string fileContent, int? port = null) { return Ftp(hostname, username, password, filePath, fileContent, secure: false, port); } /// /// Send a file to the FTP specified server using password authentication over SFTP. /// /// FTP server hostname /// The FTP server username /// The FTP server password /// The path to file on the FTP server /// The contents of the file /// The FTP server port. Defaults to 21 public bool Sftp(string hostname, string username, string password, string filePath, byte[] fileContent, int? port = null) { return Ftp(hostname, username, password, filePath, fileContent, secure: true, port); } /// /// Send a file to the FTP specified server using password authentication over SFTP. /// /// FTP server hostname /// The FTP server username /// The FTP server password /// The path to file on the FTP server /// The string contents of the file /// The FTP server port. Defaults to 21 public bool Sftp(string hostname, string username, string password, string filePath, string fileContent, int? port = null) { return Ftp(hostname, username, password, filePath, fileContent, secure: true, port); } /// /// Send a file to the FTP specified server using password authentication over SFTP using SSH keys. /// /// FTP server hostname /// The FTP server username /// The private SSH key to use for authentication /// The optional passphrase to decrypt the private key. /// This can be empty or null if the private key is not encrypted /// The path to file on the FTP server /// The contents of the file /// The FTP server port. Defaults to 21 public bool Sftp(string hostname, string username, string privateKey, string privateKeyPassphrase, string filePath, byte[] fileContent, int? port = null) { if (!Allow()) { return false; } var ftp = new NotificationFtp(hostname, username, privateKey, privateKeyPassphrase, filePath, fileContent, port); Messages.Enqueue(ftp); return true; } /// /// Send a file to the FTP specified server using password authentication over SFTP using SSH keys. /// /// FTP server hostname /// The FTP server username /// The private SSH key to use for authentication /// The optional passphrase to decrypt the private key. /// This can be empty or null if the private key is not encrypted /// The path to file on the FTP server /// The string contents of the file /// The FTP server port. Defaults to 21 public bool Sftp(string hostname, string username, string privateKey, string privateKeyPassphrase, string filePath, string fileContent, int? port = null) { if (!Allow()) { return false; } var ftp = new NotificationFtp(hostname, username, privateKey, privateKeyPassphrase, filePath, fileContent, port); Messages.Enqueue(ftp); return true; } private bool Ftp(string hostname, string username, string password, string filePath, byte[] fileContent, bool secure = true, int? port = null) { if (!Allow()) { return false; } var ftp = new NotificationFtp(hostname, username, password, filePath, fileContent, secure: secure, port); Messages.Enqueue(ftp); return true; } private bool Ftp(string hostname, string username, string password, string filePath, string fileContent, bool secure = true, int? port = null) { if (!Allow()) { return false; } var ftp = new NotificationFtp(hostname, username, password, filePath, fileContent, secure: secure, port); Messages.Enqueue(ftp); return true; } /// /// Maintain a rate limit of the notification messages per hour send of roughly 20 messages per hour. /// /// True when running in live mode and under the rate limit private bool Allow() { if (!_liveMode) { return false; } lock (_sync) { var now = DateTime.UtcNow; if (now > _resetTime) { _count = 0; // rate limiting set at 30/hour _resetTime = now.Add(TimeSpan.FromHours(1)); } if (_count < RateLimit) { _count++; return true; } return false; } } } }