Lean  $LEAN_TAG$
ApiConnection.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 RestSharp;
18 using Newtonsoft.Json;
19 using QuantConnect.Orders;
20 using QuantConnect.Logging;
21 using System.Threading.Tasks;
22 using RestSharp.Authenticators;
23 
24 namespace QuantConnect.Api
25 {
26  /// <summary>
27  /// API Connection and Hash Manager
28  /// </summary>
29  public class ApiConnection
30  {
31  private readonly static JsonSerializerSettings _jsonSettings = new() { Converters = { new LiveAlgorithmResultsJsonConverter(), new OrderJsonConverter() } };
32 
33  /// <summary>
34  /// Authorized client to use for requests.
35  /// </summary>
36  public RestClient Client { get; set; }
37 
38  // Authorization Credentials
39  private readonly string _userId;
40  private readonly string _token;
41 
42  private LeanAuthenticator _authenticator;
43 
44  /// <summary>
45  /// Create a new Api Connection Class.
46  /// </summary>
47  /// <param name="userId">User Id number from QuantConnect.com account. Found at www.quantconnect.com/account </param>
48  /// <param name="token">Access token for the QuantConnect account. Found at www.quantconnect.com/account </param>
49  public ApiConnection(int userId, string token)
50  {
51  _token = token;
52  _userId = userId.ToStringInvariant();
53  Client = new RestClient(Globals.Api);
54  }
55 
56  /// <summary>
57  /// Return true if connected successfully.
58  /// </summary>
59  public bool Connected
60  {
61  get
62  {
63  var request = new RestRequest("authenticate", Method.GET);
64  AuthenticationResponse response;
65  if (TryRequest(request, out response))
66  {
67  return response.Success;
68  }
69  return false;
70  }
71  }
72 
73  /// <summary>
74  /// Place a secure request and get back an object of type T.
75  /// </summary>
76  /// <typeparam name="T"></typeparam>
77  /// <param name="request"></param>
78  /// <param name="result">Result object from the </param>
79  /// <returns>T typed object response</returns>
80  public bool TryRequest<T>(RestRequest request, out T result)
81  where T : RestResponse
82  {
83  var resultTuple = TryRequestAsync<T>(request).SynchronouslyAwaitTaskResult();
84  result = resultTuple.Item2;
85  return resultTuple.Item1;
86  }
87 
88  /// <summary>
89  /// Place a secure request and get back an object of type T.
90  /// </summary>
91  /// <typeparam name="T"></typeparam>
92  /// <param name="request"></param>
93  /// <returns>T typed object response</returns>
94  public async Task<Tuple<bool, T>> TryRequestAsync<T>(RestRequest request)
95  where T : RestResponse
96  {
97  var responseContent = string.Empty;
98  T result;
99  try
100  {
101  SetAuthenticator(request);
102 
103  // Execute the authenticated REST API Call
104  var restsharpResponse = await Client.ExecuteAsync(request).ConfigureAwait(false);
105 
106  //Verify success
107  if (restsharpResponse.ErrorException != null)
108  {
109  Log.Error($"ApiConnection.TryRequest({request.Resource}): Error: {restsharpResponse.ErrorException.Message}");
110  return new Tuple<bool, T>(false, null);
111  }
112 
113  if (!restsharpResponse.IsSuccessful)
114  {
115  Log.Error($"ApiConnect.TryRequest({request.Resource}): Content: {restsharpResponse.Content}");
116  }
117 
118  responseContent = restsharpResponse.Content;
119  result = JsonConvert.DeserializeObject<T>(responseContent, _jsonSettings);
120 
121  if (result == null || !result.Success)
122  {
123  Log.Debug($"ApiConnection.TryRequest({request.Resource}): Raw response: '{responseContent}'");
124  return new Tuple<bool, T>(false, result);
125  }
126  }
127  catch (Exception err)
128  {
129  Log.Error($"ApiConnection.TryRequest({request.Resource}): Error: {err.Message}, Response content: {responseContent}");
130  return new Tuple<bool, T>(false, null);
131  }
132 
133  return new Tuple<bool, T>(true, result);
134  }
135 
136  private void SetAuthenticator(RestRequest request)
137  {
138  var newTimeStamp = (int)Time.TimeStamp();
139 
140  var currentAuth = _authenticator;
141  if (currentAuth == null || newTimeStamp - currentAuth.TimeStamp > 7000)
142  {
143  // Generate the hash each request
144  // Add the UTC timestamp to the request header.
145  // Timestamps older than 7200 seconds will not work.
146  var hash = Api.CreateSecureHash(newTimeStamp, _token);
147  var authenticator = new HttpBasicAuthenticator(_userId, hash);
148  _authenticator = currentAuth = new LeanAuthenticator(authenticator, newTimeStamp);
149 
150  Client.Authenticator = currentAuth.Authenticator;
151  }
152 
153  request.AddHeader("Timestamp", currentAuth.TimeStampStr);
154  }
155 
156  private class LeanAuthenticator
157  {
158  public int TimeStamp { get; }
159  public string TimeStampStr { get; }
160  public HttpBasicAuthenticator Authenticator { get; }
161  public LeanAuthenticator(HttpBasicAuthenticator authenticator, int timeStamp)
162  {
163  TimeStamp = timeStamp;
164  Authenticator = authenticator;
165  TimeStampStr = timeStamp.ToStringInvariant();
166  }
167  }
168  }
169 }