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="user">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  /// Maintain a rate limit of the notification messages per hour send of roughly 20 messages per hour.
159  /// </summary>
160  /// <returns>True when running in live mode and under the rate limit</returns>
161  private bool Allow()
162  {
163  if (!_liveMode)
164  {
165  return false;
166  }
167 
168  lock (_sync)
169  {
170  var now = DateTime.UtcNow;
171  if (now > _resetTime)
172  {
173  _count = 0;
174 
175  // rate limiting set at 30/hour
176  _resetTime = now.Add(TimeSpan.FromHours(1));
177  }
178 
179  if (_count < RateLimit)
180  {
181  _count++;
182  return true;
183  }
184 
185  return false;
186  }
187  }
188  }
189 }