5fd021996a
Benchmarks / build (push) Has been cancelled
Build & Test Lean / build (push) Has been cancelled
Regression Tests / build (push) Has been cancelled
Report Generator Tests / build (push) Has been cancelled
Research Regression Tests / build (push) Has been cancelled
Python Virtual Environments / build (push) Has been cancelled
API Tests / build (push) Has been cancelled
* Fix half of the CA1051 warnings This warning is about not declaring visible instance fields. There are something about 500 warnings in the solution, mostly in the QuantConnect and QuantConnect.Algorithm.CSharp projects. I aim to fix one of them in this PR and the other half of them in a second one. To fix it, I'm changing the visible instancce fields for properties. * fix bugs * Addressing minor reviews * More minor fixes --------- Co-authored-by: Martin Molinero <martin.molinero1@gmail.com>
411 lines
16 KiB
C#
411 lines
16 KiB
C#
/*
|
|
* 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.Generic;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using Newtonsoft.Json;
|
|
using QuantConnect.Util;
|
|
|
|
namespace QuantConnect.Notifications
|
|
{
|
|
/// <summary>
|
|
/// Local/desktop implementation of messaging system for Lean Engine.
|
|
/// </summary>
|
|
[JsonConverter(typeof(NotificationJsonConverter))]
|
|
public abstract class Notification
|
|
{
|
|
/// <summary>
|
|
/// Method for sending implementations of notification object types.
|
|
/// </summary>
|
|
/// <remarks>SMS, Email and Web are all handled by the QC Messaging Handler. To implement your own notification type implement it here.</remarks>
|
|
public virtual void Send()
|
|
{
|
|
//
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Web Notification Class
|
|
/// </summary>
|
|
public class NotificationWeb : Notification
|
|
{
|
|
/// <summary>
|
|
/// Optional email headers
|
|
/// </summary>
|
|
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
|
|
public Dictionary<string, string> Headers { get; set; }
|
|
|
|
/// <summary>
|
|
/// Send a notification message to this web address
|
|
/// </summary>
|
|
public string Address { get; set; }
|
|
|
|
/// <summary>
|
|
/// Object data to send.
|
|
/// </summary>
|
|
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
|
|
public object Data { get; set; }
|
|
|
|
/// <summary>
|
|
/// Constructor for sending a notification SMS to a specified phone number
|
|
/// </summary>
|
|
/// <param name="address">Address to send to</param>
|
|
/// <param name="data">Data to send</param>
|
|
/// <param name="headers">Optional headers to use</param>
|
|
public NotificationWeb(string address, object data = null, Dictionary<string, string> headers = null)
|
|
{
|
|
Address = address;
|
|
Data = data;
|
|
Headers = headers;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sms Notification Class
|
|
/// </summary>
|
|
public class NotificationSms : Notification
|
|
{
|
|
/// <summary>
|
|
/// Send a notification message to this phone number
|
|
/// </summary>
|
|
public string PhoneNumber { get; set; }
|
|
|
|
/// <summary>
|
|
/// Message to send. Limited to 160 characters
|
|
/// </summary>
|
|
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
|
|
public string Message { get; set; }
|
|
|
|
/// <summary>
|
|
/// Constructor for sending a notification SMS to a specified phone number
|
|
/// </summary>
|
|
/// <param name="number"></param>
|
|
/// <param name="message"></param>
|
|
public NotificationSms(string number, string message)
|
|
{
|
|
PhoneNumber = number;
|
|
Message = message;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Email notification data.
|
|
/// </summary>
|
|
public class NotificationEmail : Notification
|
|
{
|
|
/// <summary>
|
|
/// Optional email headers
|
|
/// </summary>
|
|
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
|
|
public Dictionary<string, string> Headers { get; set; }
|
|
|
|
/// <summary>
|
|
/// Send to address:
|
|
/// </summary>
|
|
public string Address { get; set; }
|
|
|
|
/// <summary>
|
|
/// Email subject
|
|
/// </summary>
|
|
public string Subject { get; set; }
|
|
|
|
/// <summary>
|
|
/// Message to send.
|
|
/// </summary>
|
|
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
|
|
public string Message { get; set; }
|
|
|
|
/// <summary>
|
|
/// Email Data
|
|
/// </summary>
|
|
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
|
|
public string Data { get; set; }
|
|
|
|
/// <summary>
|
|
/// Default constructor for sending an email notification
|
|
/// </summary>
|
|
/// <param name="address">Address to send to. Will throw <see cref="ArgumentException"/> if invalid
|
|
/// <see cref="Validate.EmailAddress"/></param>
|
|
/// <param name="subject">Subject of the email. Will set to <see cref="string.Empty"/> if null</param>
|
|
/// <param name="message">Message body of the email. Will set to <see cref="string.Empty"/> if null</param>
|
|
/// <param name="data">Data to attach to the email. Will set to <see cref="string.Empty"/> if null</param>
|
|
/// <param name="headers">Optional email headers to use</param>
|
|
public NotificationEmail(string address, string subject = "", string message = "", string data = "", Dictionary<string, string> headers = null)
|
|
{
|
|
if (!Validate.EmailAddress(address))
|
|
{
|
|
throw new ArgumentException(Messages.NotificationEmail.InvalidEmailAddress(address));
|
|
}
|
|
|
|
Address = address;
|
|
Data = data ?? string.Empty;
|
|
Message = message ?? string.Empty;
|
|
Subject = subject ?? string.Empty;
|
|
Headers = headers;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Telegram notification data
|
|
/// </summary>
|
|
public class NotificationTelegram : Notification
|
|
{
|
|
/// <summary>
|
|
/// Send a notification message to this user on Telegram
|
|
/// Can be either a personal ID or Group ID.
|
|
/// </summary>
|
|
public string Id { get; set; }
|
|
|
|
/// <summary>
|
|
/// Message to send. Limited to 4096 characters
|
|
/// </summary>
|
|
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
|
|
public string Message { get; set; }
|
|
|
|
/// <summary>
|
|
/// Token to use
|
|
/// </summary>
|
|
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
|
|
public string Token { get; set; }
|
|
|
|
/// <summary>
|
|
/// Constructor for sending a telegram notification to a specific User ID
|
|
/// or group ID. Note: The bot must have an open chat with the user or be
|
|
/// added to the group for messages to deliver.
|
|
/// </summary>
|
|
/// <param name="id">User Id or Group Id to send the message too</param>
|
|
/// <param name="message">Message to send</param>
|
|
/// <param name="token">Bot token to use, if null defaults to "telegram-token"
|
|
/// in config on send</param>
|
|
public NotificationTelegram(string id, string message, string token = null)
|
|
{
|
|
Id = id;
|
|
Message = message;
|
|
Token = token;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// FTP notification data
|
|
/// </summary>
|
|
public class NotificationFtp : Notification
|
|
{
|
|
private static readonly Regex HostnameProtocolRegex = new(@"^[s]?ftp\:\/\/", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
|
|
|
private const int DefaultPort = 21;
|
|
|
|
/// <summary>
|
|
/// Whether to use SFTP or FTP.
|
|
/// </summary>
|
|
[JsonProperty("secure")]
|
|
public bool Secure { get; }
|
|
|
|
/// <summary>
|
|
/// The FTP server hostname.
|
|
/// </summary>
|
|
[JsonProperty("host")]
|
|
public string Hostname { get; }
|
|
|
|
/// <summary>
|
|
/// The FTP server port.
|
|
/// </summary>
|
|
[JsonProperty("port")]
|
|
public int Port { get; }
|
|
|
|
/// <summary>
|
|
/// The FTP server username.
|
|
/// </summary>
|
|
[JsonProperty("username")]
|
|
public string Username { get; }
|
|
|
|
/// <summary>
|
|
/// The FTP server password.
|
|
/// </summary>
|
|
[JsonProperty("password", DefaultValueHandling = DefaultValueHandling.Ignore)]
|
|
public string Password { get; }
|
|
|
|
/// <summary>
|
|
/// The path to file on the FTP server.
|
|
/// </summary>
|
|
[JsonProperty("fileDestinationPath")]
|
|
public string FilePath { get; }
|
|
|
|
/// <summary>
|
|
/// The contents of the file to send.
|
|
/// </summary>
|
|
[JsonProperty("fileContent")]
|
|
public string FileContent { get; private set; }
|
|
|
|
/// <summary>
|
|
/// The private key to use for authentication (optional).
|
|
/// </summary>
|
|
[JsonProperty("privateKey", DefaultValueHandling = DefaultValueHandling.Ignore)]
|
|
public string PrivateKey { get; }
|
|
|
|
/// <summary>
|
|
/// The passphrase for the private key (optional).
|
|
/// </summary>
|
|
[JsonProperty("passphrase", DefaultValueHandling = DefaultValueHandling.Ignore)]
|
|
public string PrivateKeyPassphrase { get; }
|
|
|
|
private NotificationFtp(string hostname, string username, string filePath, byte[] fileContent, bool secure, int? port)
|
|
{
|
|
Hostname = NormalizeHostname(hostname);
|
|
Port = port ?? DefaultPort;
|
|
Username = username;
|
|
FilePath = filePath;
|
|
FileContent = Convert.ToBase64String(fileContent);
|
|
Secure = secure;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructor for a notification to sent as a file to an FTP server using password authentication.
|
|
/// </summary>
|
|
/// <param name="hostname">FTP server hostname</param>
|
|
/// <param name="username">The FTP server username</param>
|
|
/// <param name="password">The FTP server password</param>
|
|
/// <param name="filePath">The path to file on the FTP server</param>
|
|
/// <param name="fileContent">The contents of the file</param>
|
|
/// <param name="secure">Whether to use SFTP or FTP. Defaults to true</param>
|
|
/// <param name="port">The FTP server port. Defaults to 21</param>
|
|
public NotificationFtp(string hostname, string username, string password, string filePath, byte[] fileContent,
|
|
bool secure = true, int? port = null)
|
|
: this(hostname, username, filePath, fileContent, secure, port)
|
|
{
|
|
if (string.IsNullOrEmpty(password))
|
|
{
|
|
throw new ArgumentException(Messages.NotificationFtp.MissingPassword);
|
|
}
|
|
|
|
Password = password;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructor for a notification to sent as a file to an FTP server over SFTP using SSH keys.
|
|
/// </summary>
|
|
/// <param name="hostname">FTP server hostname</param>
|
|
/// <param name="username">The FTP server username</param>
|
|
/// <param name="privateKey">The private SSH key to use for authentication</param>
|
|
/// <param name="privateKeyPassphrase">The optional passphrase to decrypt the private key.
|
|
/// This can be empty or null if the private key is not encrypted</param>
|
|
/// <param name="filePath">The path to file on the FTP server</param>
|
|
/// <param name="fileContent">The contents of the file</param>
|
|
/// <param name="port">The FTP server port. Defaults to 21</param>
|
|
public NotificationFtp(string hostname, string username, string privateKey, string privateKeyPassphrase,
|
|
string filePath, byte[] fileContent, int? port = null)
|
|
: this(hostname, username, filePath, fileContent, true, port)
|
|
{
|
|
if (string.IsNullOrEmpty(privateKey))
|
|
{
|
|
throw new ArgumentException(Messages.NotificationFtp.MissingSSHKey);
|
|
}
|
|
|
|
PrivateKey = privateKey;
|
|
PrivateKeyPassphrase = privateKeyPassphrase;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructor for a notification to sent as a file to an FTP server using password authentication.
|
|
/// </summary>
|
|
/// <param name="hostname">FTP server hostname</param>
|
|
/// <param name="username">The FTP server username</param>
|
|
/// <param name="password">The FTP server password</param>
|
|
/// <param name="filePath">The path to file on the FTP server</param>
|
|
/// <param name="fileContent">The contents of the file</param>
|
|
/// <param name="secure">Whether to use SFTP or FTP. Defaults to true</param>
|
|
/// <param name="port">The FTP server port. Defaults to 21</param>
|
|
public NotificationFtp(string hostname, string username, string password, string filePath, string fileContent,
|
|
bool secure = true, int? port = null)
|
|
: this(hostname, username, password, filePath, Encoding.ASCII.GetBytes(fileContent), secure, port)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructor for a notification to sent as a file to an FTP server over SFTP using SSH keys.
|
|
/// </summary>
|
|
/// <param name="hostname">FTP server hostname</param>
|
|
/// <param name="username">The FTP server username</param>
|
|
/// <param name="privateKey">The private SSH key to use for authentication</param>
|
|
/// <param name="privateKeyPassphrase">The optional passphrase to decrypt the private key.
|
|
/// This can be empty or null if the private key is not encrypted</param>
|
|
/// <param name="filePath">The path to file on the FTP server</param>
|
|
/// <param name="fileContent">The contents of the file</param>
|
|
/// <param name="port">The FTP server port. Defaults to 21</param>
|
|
public NotificationFtp(string hostname, string username, string privateKey, string privateKeyPassphrase,
|
|
string filePath, string fileContent, int? port = null)
|
|
: this(hostname, username, privateKey, privateKeyPassphrase, filePath, Encoding.ASCII.GetBytes(fileContent), port)
|
|
{
|
|
}
|
|
|
|
private static string NormalizeHostname(string hostname)
|
|
{
|
|
// Remove trailing slashes
|
|
hostname = hostname.Trim().TrimEnd('/');
|
|
// Remove protocol if present
|
|
return HostnameProtocolRegex.Replace(hostname, "");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Factory method for Json deserialization: the file contents are already encoded
|
|
/// </summary>
|
|
internal static NotificationFtp FromEncodedData(string hostname, string username, string password, string filePath, string encodedFileContent,
|
|
bool secure, int? port)
|
|
{
|
|
var notification = new NotificationFtp(hostname, username, password, filePath, Array.Empty<byte>(), secure, port);
|
|
notification.FileContent = encodedFileContent;
|
|
return notification;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Factory method for Json deserialization: the file contents are already encoded
|
|
/// </summary>
|
|
internal static NotificationFtp FromEncodedData(string hostname, string username, string privateKey, string privateKeyPassphrase,
|
|
string filePath, string encodedFileContent, int? port)
|
|
{
|
|
var notification = new NotificationFtp(hostname, username, privateKey, privateKeyPassphrase, filePath, Array.Empty<byte>(), port);
|
|
notification.FileContent = encodedFileContent;
|
|
return notification;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extension methods for <see cref="Notification"/>
|
|
/// </summary>
|
|
public static class NotificationExtensions
|
|
{
|
|
/// <summary>
|
|
/// Check if the notification can be sent (implements the <see cref="Notification.Send"/> method)
|
|
/// </summary>
|
|
/// <param name="notification">The notification</param>
|
|
/// <returns>Whether the notification can be sent</returns>
|
|
public static bool CanSend(this Notification notification)
|
|
{
|
|
if (notification == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var type = notification.GetType();
|
|
return type != typeof(NotificationEmail) &&
|
|
type != typeof(NotificationWeb) &&
|
|
type != typeof(NotificationSms) &&
|
|
type != typeof(NotificationTelegram) &&
|
|
type != typeof(NotificationFtp);
|
|
}
|
|
}
|
|
}
|