Lean  $LEAN_TAG$
NotificationManager.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14 */
15 
16 using System;
17 using System.Collections.Concurrent;
18 using System.Collections.Generic;
19 using Python.Runtime;
20 
22 {
23  /// <summary>
24  /// Local/desktop implementation of messaging system for Lean Engine.
25  /// </summary>
26  public class NotificationManager
27  {
28  private const int RateLimit = 30;
29 
30  private int _count;
31  private DateTime _resetTime;
32 
33  private readonly bool _liveMode;
34  private readonly object _sync = new object();
35 
36  /// <summary>
37  /// Public access to the messages
38  /// </summary>
39  public ConcurrentQueue<Notification> Messages { get; set; }
40 
41  /// <summary>
42  /// Initialize the messaging system
43  /// </summary>
44  public NotificationManager(bool liveMode)
45  {
46  _count = 0;
47  _liveMode = liveMode;
48  Messages = new ConcurrentQueue<Notification>();
49 
50  // start counting reset time based on first invocation of NotificationManager
51  _resetTime = default(DateTime);
52  }
53 
54  /// <summary>
55  /// Send an email to the address specified for live trading notifications.
56  /// </summary>
57  /// <param name="subject">Subject of the email</param>
58  /// <param name="message">Message body, up to 10kb</param>
59  /// <param name="data">Data attachment (optional)</param>
60  /// <param name="address">Email address to send to</param>
61  /// <param name="headers">Optional email headers to use</param>
62  public bool Email(string address, string subject, string message, string data, PyObject headers)
63  {
64  return Email(address, subject, message, data, headers.ConvertToDictionary<string, string>());
65  }
66 
67  /// <summary>
68  /// Send an email to the address specified for live trading notifications.
69  /// </summary>
70  /// <param name="subject">Subject of the email</param>
71  /// <param name="message">Message body, up to 10kb</param>
72  /// <param name="data">Data attachment (optional)</param>
73  /// <param name="address">Email address to send to</param>
74  /// <param name="headers">Optional email headers to use</param>
75  public bool Email(string address, string subject, string message, string data = "", Dictionary<string, string> headers = null)
76  {
77  if (!Allow())
78  {
79  return false;
80  }
81 
82  var email = new NotificationEmail(address, subject, message, data, headers);
83  Messages.Enqueue(email);
84 
85  return true;
86  }
87 
88  /// <summary>
89  /// Send an SMS to the phone number specified
90  /// </summary>
91  /// <param name="phoneNumber">Phone number to send to</param>
92  /// <param name="message">Message to send</param>
93  public bool Sms(string phoneNumber, string message)
94  {
95  if (!Allow())
96  {
97  return false;
98  }
99 
100  var sms = new NotificationSms(phoneNumber, message);
101  Messages.Enqueue(sms);
102 
103  return true;
104  }
105 
106  /// <summary>
107  /// Place REST POST call to the specified address with the specified DATA.
108  /// Python overload for Headers parameter.
109  /// </summary>
110  /// <param name="address">Endpoint address</param>
111  /// <param name="data">Data to send in body JSON encoded</param>
112  /// <param name="headers">Optional headers to use</param>
113  public bool Web(string address, object data, PyObject headers)
114  {
115  return Web(address, data, headers.ConvertToDictionary<string, string>());
116  }
117 
118  /// <summary>
119  /// Place REST POST call to the specified address with the specified DATA.
120  /// </summary>
121  /// <param name="address">Endpoint address</param>
122  /// <param name="data">Data to send in body JSON encoded (optional)</param>
123  /// <param name="headers">Optional headers to use</param>
124  public bool Web(string address, object data = null, Dictionary<string, string> headers = null)
125  {
126  if (!Allow())
127  {
128  return false;
129  }
130 
131  var web = new NotificationWeb(address, data, headers);
132  Messages.Enqueue(web);
133 
134  return true;
135  }
136 
137  /// <summary>
138  /// Send a telegram message to the chat ID specified, supply token for custom bot.
139  /// Note: Requires bot to have chat with user or be in the group specified by ID.
140  /// </summary>
141  /// <param name="id">Chat or group ID to send message to</param>
142  /// <param name="message">Message to send</param>
143  /// <param name="token">Bot token to use for this message</param>
144  public bool Telegram(string id, string message, string token = null)
145  {
146  if (!Allow())
147  {
148  return false;
149  }
150 
151  var telegram = new NotificationTelegram(id, message, token);
152  Messages.Enqueue(telegram);
153 
154  return true;
155  }
156 
157  /// <summary>
158  /// Send a file to the FTP specified server using password authentication over unsecure FTP.
159  /// </summary>
160  /// <param name="hostname">FTP server hostname</param>
161  /// <param name="username">The FTP server username</param>
162  /// <param name="password">The FTP server password</param>
163  /// <param name="filePath">The path to file on the FTP server</param>
164  /// <param name="fileContent">The contents of the file</param>
165  /// <param name="port">The FTP server port. Defaults to 21</param>
166  public bool Ftp(string hostname, string username, string password, string filePath, byte[] fileContent, int? port = null)
167  {
168  return Ftp(hostname, username, password, filePath, fileContent, secure: false, port);
169  }
170 
171  /// <summary>
172  /// Send a file to the FTP specified server using password authentication over unsecure FTP.
173  /// </summary>
174  /// <param name="hostname">FTP server hostname</param>
175  /// <param name="username">The FTP server username</param>
176  /// <param name="password">The FTP server password</param>
177  /// <param name="filePath">The path to file on the FTP server</param>
178  /// <param name="fileContent">The string contents of the file</param>
179  /// <param name="port">The FTP server port. Defaults to 21</param>
180  public bool Ftp(string hostname, string username, string password, string filePath, string fileContent, int? port = null)
181  {
182  return Ftp(hostname, username, password, filePath, fileContent, secure: false, port);
183  }
184 
185  /// <summary>
186  /// Send a file to the FTP specified server using password authentication over SFTP.
187  /// </summary>
188  /// <param name="hostname">FTP server hostname</param>
189  /// <param name="username">The FTP server username</param>
190  /// <param name="password">The FTP server password</param>
191  /// <param name="filePath">The path to file on the FTP server</param>
192  /// <param name="fileContent">The contents of the file</param>
193  /// <param name="port">The FTP server port. Defaults to 21</param>
194  public bool Sftp(string hostname, string username, string password, string filePath, byte[] fileContent, int? port = null)
195  {
196  return Ftp(hostname, username, password, filePath, fileContent, secure: true, port);
197  }
198 
199  /// <summary>
200  /// Send a file to the FTP specified server using password authentication over SFTP.
201  /// </summary>
202  /// <param name="hostname">FTP server hostname</param>
203  /// <param name="username">The FTP server username</param>
204  /// <param name="password">The FTP server password</param>
205  /// <param name="filePath">The path to file on the FTP server</param>
206  /// <param name="fileContent">The string contents of the file</param>
207  /// <param name="port">The FTP server port. Defaults to 21</param>
208  public bool Sftp(string hostname, string username, string password, string filePath, string fileContent, int? port = null)
209  {
210  return Ftp(hostname, username, password, filePath, fileContent, secure: true, port);
211  }
212 
213  /// <summary>
214  /// Send a file to the FTP specified server using password authentication over SFTP using SSH keys.
215  /// </summary>
216  /// <param name="hostname">FTP server hostname</param>
217  /// <param name="username">The FTP server username</param>
218  /// <param name="privateKey">The private SSH key to use for authentication</param>
219  /// <param name="privateKeyPassphrase">The optional passphrase to decrypt the private key.
220  /// This can be empty or null if the private key is not encrypted</param>
221  /// <param name="filePath">The path to file on the FTP server</param>
222  /// <param name="fileContent">The contents of the file</param>
223  /// <param name="port">The FTP server port. Defaults to 21</param>
224  public bool Sftp(string hostname, string username, string privateKey, string privateKeyPassphrase, string filePath, byte[] fileContent,
225  int? port = null)
226  {
227  if (!Allow())
228  {
229  return false;
230  }
231 
232  var ftp = new NotificationFtp(hostname, username, privateKey, privateKeyPassphrase, filePath, fileContent, port);
233  Messages.Enqueue(ftp);
234 
235  return true;
236  }
237 
238  /// <summary>
239  /// Send a file to the FTP specified server using password authentication over SFTP using SSH keys.
240  /// </summary>
241  /// <param name="hostname">FTP server hostname</param>
242  /// <param name="username">The FTP server username</param>
243  /// <param name="privateKey">The private SSH key to use for authentication</param>
244  /// <param name="privateKeyPassphrase">The optional passphrase to decrypt the private key.
245  /// This can be empty or null if the private key is not encrypted</param>
246  /// <param name="filePath">The path to file on the FTP server</param>
247  /// <param name="fileContent">The string contents of the file</param>
248  /// <param name="port">The FTP server port. Defaults to 21</param>
249  public bool Sftp(string hostname, string username, string privateKey, string privateKeyPassphrase, string filePath, string fileContent,
250  int? port = null)
251  {
252  if (!Allow())
253  {
254  return false;
255  }
256 
257  var ftp = new NotificationFtp(hostname, username, privateKey, privateKeyPassphrase, filePath, fileContent, port);
258  Messages.Enqueue(ftp);
259 
260  return true;
261  }
262 
263  private bool Ftp(string hostname, string username, string password, string filePath, byte[] fileContent, bool secure = true, int? port = null)
264  {
265  if (!Allow())
266  {
267  return false;
268  }
269 
270  var ftp = new NotificationFtp(hostname, username, password, filePath, fileContent, secure: secure, port);
271  Messages.Enqueue(ftp);
272 
273  return true;
274  }
275 
276  private bool Ftp(string hostname, string username, string password, string filePath, string fileContent, bool secure = true, int? port = null)
277  {
278  if (!Allow())
279  {
280  return false;
281  }
282 
283  var ftp = new NotificationFtp(hostname, username, password, filePath, fileContent, secure: secure, port);
284  Messages.Enqueue(ftp);
285 
286  return true;
287  }
288 
289  /// <summary>
290  /// Maintain a rate limit of the notification messages per hour send of roughly 20 messages per hour.
291  /// </summary>
292  /// <returns>True when running in live mode and under the rate limit</returns>
293  private bool Allow()
294  {
295  if (!_liveMode)
296  {
297  return false;
298  }
299 
300  lock (_sync)
301  {
302  var now = DateTime.UtcNow;
303  if (now > _resetTime)
304  {
305  _count = 0;
306 
307  // rate limiting set at 30/hour
308  _resetTime = now.Add(TimeSpan.FromHours(1));
309  }
310 
311  if (_count < RateLimit)
312  {
313  _count++;
314  return true;
315  }
316 
317  return false;
318  }
319  }
320  }
321 }