@@ -70,7 +70,7 @@ namespace QuantConnect
7070 /// </summary>
7171 public static class Extensions
7272 {
73- private static readonly Dictionary < string , bool > _emptyDirectories = new ( ) ;
73+ private static readonly Dictionary < string , bool > _emptyDirectories = new ( ) ;
7474 private static readonly HashSet < string > InvalidSecurityTypes = new HashSet < string > ( ) ;
7575 private static readonly Regex DateCheck = new Regex ( @"\d{8}" , RegexOptions . Compiled ) ;
7676 private static RecyclableMemoryStreamManager MemoryManager = new RecyclableMemoryStreamManager ( ) ;
@@ -174,7 +174,7 @@ public static bool IsDirectoryEmpty(this string directoryPath)
174174 {
175175 lock ( _emptyDirectories )
176176 {
177- if ( ! _emptyDirectories . TryGetValue ( directoryPath , out var result ) )
177+ if ( ! _emptyDirectories . TryGetValue ( directoryPath , out var result ) )
178178 {
179179 // is empty unless it exists and it has at least 1 file or directory in it
180180 result = true ;
@@ -285,7 +285,73 @@ public static List<T> DeserializeList<T>(this string jsonArray)
285285 /// <param name="headers">Add custom headers for the request</param>
286286 public static bool TryDownloadData ( this HttpClient client , string url , out string data , out HttpStatusCode ? statusCode , Dictionary < string , string > headers = null )
287287 {
288- data = null ;
288+ return client . TryDownloadData ( url , out data , out statusCode , headers , null ) ;
289+ }
290+
291+ /// <summary>
292+ /// Helper method to download a provided url as a string
293+ /// </summary>
294+ /// <param name="client">The http client to use</param>
295+ /// <param name="url">The url to download data from</param>
296+ /// <param name="headers">Add custom headers for the request</param>
297+ public static string DownloadData ( this HttpClient client , string url , Dictionary < string , string > headers = null )
298+ {
299+ return client . DownloadData < string > ( url , headers , null ) ;
300+ }
301+
302+ /// <summary>
303+ /// Helper method to download a provided url as a string
304+ /// </summary>
305+ /// <param name="url">The url to download data from</param>
306+ /// <param name="headers">Add custom headers for the request</param>
307+ public static string DownloadData ( this string url , Dictionary < string , string > headers = null )
308+ {
309+ return url . DownloadData < string > ( headers , null ) ;
310+ }
311+
312+ /// <summary>
313+ /// Download the content of a url to a string and deserialize it to the specified type
314+ /// </summary>
315+ /// <typeparam name="T">The type to deserialize to</typeparam>
316+ /// <param name="client">The http client to use</param>
317+ /// <param name="url">The url to download data from</param>
318+ /// <param name="headers">Add custom headers for the request</param>
319+ /// <param name="settings">Optional JSON serializer settings</param>
320+ /// <returns>The deserialized data</returns>
321+ public static T DownloadData < T > ( this HttpClient client , string url , Dictionary < string , string > headers = null , JsonSerializerSettings settings = null )
322+ {
323+ client . TryDownloadData < T > ( url , out var result , out _ , headers , settings ) ;
324+ return result ;
325+ }
326+
327+ /// <summary>
328+ /// Download the content of a url to a string and deserialize it to the specified type
329+ /// </summary>
330+ /// <typeparam name="T">The type to deserialize to</typeparam>
331+ /// <param name="url">The url to download data from</param>
332+ /// <param name="headers">Add custom headers for the request</param>
333+ /// <param name="settings">Optional JSON serializer settings</param>
334+ /// <returns>The deserialized data</returns>
335+ public static T DownloadData < T > ( this string url , Dictionary < string , string > headers = null , JsonSerializerSettings settings = null )
336+ {
337+ using var client = new HttpClient ( ) ;
338+ return client . DownloadData < T > ( url , headers , settings ) ;
339+ }
340+
341+ /// <summary>
342+ /// Tries to download and deserialize directly from stream to T
343+ /// </summary>
344+ /// <typeparam name="T">The type to deserialize to</typeparam>
345+ /// <param name="client">The http client to use</param>
346+ /// <param name="url">The url to download data from</param>
347+ /// <param name="result">The deserialized data if successful</param>
348+ /// <param name="statusCode">The request status code</param>
349+ /// <param name="headers">Add custom headers for the request</param>
350+ /// <param name="settings">Optional JSON serializer settings</param>
351+ /// <returns>True if successful, otherwise false</returns>
352+ public static bool TryDownloadData < T > ( this HttpClient client , string url , out T result , out HttpStatusCode ? statusCode , Dictionary < string , string > headers = null , JsonSerializerSettings settings = null )
353+ {
354+ result = default ;
289355 statusCode = null ;
290356 using var request = new HttpRequestMessage ( HttpMethod . Get , url ) ;
291357 if ( headers != null )
@@ -295,6 +361,7 @@ public static bool TryDownloadData(this HttpClient client, string url, out strin
295361 request . Headers . TryAddWithoutValidation ( kvp . Key , kvp . Value ) ;
296362 }
297363 }
364+
298365 try
299366 {
300367 using var response = client . SendAsync ( request ) . SynchronouslyAwaitTaskResult ( ) ;
@@ -306,39 +373,29 @@ public static bool TryDownloadData(this HttpClient client, string url, out strin
306373 return false ;
307374 }
308375
309- data = response . Content . ReadAsStringAsync ( ) . SynchronouslyAwaitTaskResult ( ) ;
376+ using var stream = response . Content . ReadAsStreamAsync ( ) . SynchronouslyAwaitTaskResult ( ) ;
377+ using var reader = new StreamReader ( stream ) ;
378+
379+ if ( typeof ( T ) == typeof ( string ) )
380+ {
381+ // Special case: return the response as a raw string without deserialization
382+ result = ( T ) ( object ) reader . ReadToEnd ( ) ;
383+ }
384+ else
385+ {
386+ using var jsonReader = new JsonTextReader ( reader ) ;
387+ var serializer = JsonSerializer . Create ( settings ) ;
388+ result = serializer . Deserialize < T > ( jsonReader ) ;
389+ }
310390 return true ;
311391 }
312- catch ( WebException ex )
392+ catch ( HttpRequestException ex )
313393 {
314394 Log . Error ( ex , $ "DownloadData(): { Messages . Extensions . DownloadDataFailed ( url ) } ") ;
315395 return false ;
316396 }
317397 }
318398
319- /// <summary>
320- /// Helper method to download a provided url as a string
321- /// </summary>
322- /// <param name="client">The http client to use</param>
323- /// <param name="url">The url to download data from</param>
324- /// <param name="headers">Add custom headers for the request</param>
325- public static string DownloadData ( this HttpClient client , string url , Dictionary < string , string > headers = null )
326- {
327- client . TryDownloadData ( url , out var data , out _ , headers ) ;
328- return data ;
329- }
330-
331- /// <summary>
332- /// Helper method to download a provided url as a string
333- /// </summary>
334- /// <param name="url">The url to download data from</param>
335- /// <param name="headers">Add custom headers for the request</param>
336- public static string DownloadData ( this string url , Dictionary < string , string > headers = null )
337- {
338- using var client = new HttpClient ( ) ;
339- return client . DownloadData ( url , headers ) ;
340- }
341-
342399 /// <summary>
343400 /// Helper method to download a provided url as a byte array
344401 /// </summary>
@@ -841,7 +898,8 @@ public static IEnumerable<IPortfolioTarget> OrderTargetsByMarginImpact(
841898 && ( targetIsDelta ? Math . Abs ( x . TargetQuantity ) : Math . Abs ( x . TargetQuantity - x . ExistingQuantity ) )
842899 >= x . Security . SymbolProperties . LotSize
843900 )
844- . Select ( x => new {
901+ . Select ( x => new
902+ {
845903 x . PortfolioTarget ,
846904 OrderValue = Math . Abs ( ( targetIsDelta ? x . TargetQuantity : ( x . TargetQuantity - x . ExistingQuantity ) ) * x . Security . Price ) ,
847905 IsReducingPosition = x . ExistingQuantity != 0
@@ -867,7 +925,7 @@ public static BaseData GetBaseDataInstance(this Type type)
867925 }
868926
869927 var instance = objectActivator . Invoke ( new object [ ] { type } ) ;
870- if ( instance == null )
928+ if ( instance == null )
871929 {
872930 // shouldn't happen but just in case...
873931 throw new ArgumentException ( Messages . Extensions . FailedToCreateInstanceOfType ( type ) ) ;
@@ -1007,7 +1065,8 @@ public static string SerializeJsonToString<T>(this T value, JsonSerializer seria
10071065 public static void Clear < T > ( this ConcurrentQueue < T > queue )
10081066 {
10091067 T item ;
1010- while ( queue . TryDequeue ( out item ) ) {
1068+ while ( queue . TryDequeue ( out item ) )
1069+ {
10111070 // NOP
10121071 }
10131072 }
@@ -1294,7 +1353,7 @@ public static void Add(this Ticks dictionary, Symbol key, Tick tick)
12941353 public static decimal RoundToSignificantDigits ( this decimal d , int digits )
12951354 {
12961355 if ( d == 0 ) return 0 ;
1297- var scale = ( decimal ) Math . Pow ( 10 , Math . Floor ( Math . Log10 ( ( double ) Math . Abs ( d ) ) ) + 1 ) ;
1356+ var scale = ( decimal ) Math . Pow ( 10 , Math . Floor ( Math . Log10 ( ( double ) Math . Abs ( d ) ) ) + 1 ) ;
12981357 return scale * Math . Round ( d / scale , digits ) ;
12991358 }
13001359
@@ -1459,9 +1518,9 @@ public static decimal SafeDecimalCast(this double input)
14591518 ) ;
14601519 }
14611520
1462- if ( input <= ( double ) decimal . MinValue ) return decimal . MinValue ;
1463- if ( input >= ( double ) decimal . MaxValue ) return decimal . MaxValue ;
1464- return ( decimal ) input ;
1521+ if ( input <= ( double ) decimal . MinValue ) return decimal . MinValue ;
1522+ if ( input >= ( double ) decimal . MaxValue ) return decimal . MaxValue ;
1523+ return ( decimal ) input ;
14651524 }
14661525
14671526 /// <summary>
@@ -1803,7 +1862,8 @@ public static decimal GetDecimalEpsilon()
18031862 /// </summary>
18041863 /// <param name="str">String we're looking for the extension for.</param>
18051864 /// <returns>Last 4 character string of string.</returns>
1806- public static string GetExtension ( this string str ) {
1865+ public static string GetExtension ( this string str )
1866+ {
18071867 var ext = str . Substring ( Math . Max ( 0 , str . Length - 4 ) ) ;
18081868 var allowedExt = new List < string > { ".zip" , ".csv" , ".json" , ".tsv" } ;
18091869 if ( ! allowedExt . Contains ( ext ) )
@@ -2212,19 +2272,19 @@ public static Resolution ToHigherResolutionEquivalent(this TimeSpan timeSpan, bo
22122272 {
22132273 if ( requireExactMatch )
22142274 {
2215- if ( TimeSpan . Zero == timeSpan ) return Resolution . Tick ;
2275+ if ( TimeSpan . Zero == timeSpan ) return Resolution . Tick ;
22162276 if ( Time . OneSecond == timeSpan ) return Resolution . Second ;
22172277 if ( Time . OneMinute == timeSpan ) return Resolution . Minute ;
2218- if ( Time . OneHour == timeSpan ) return Resolution . Hour ;
2219- if ( Time . OneDay == timeSpan ) return Resolution . Daily ;
2278+ if ( Time . OneHour == timeSpan ) return Resolution . Hour ;
2279+ if ( Time . OneDay == timeSpan ) return Resolution . Daily ;
22202280 throw new InvalidOperationException ( Messages . Extensions . UnableToConvertTimeSpanToResolution ( timeSpan ) ) ;
22212281 }
22222282
22232283 // for non-perfect matches
22242284 if ( Time . OneSecond > timeSpan ) return Resolution . Tick ;
22252285 if ( Time . OneMinute > timeSpan ) return Resolution . Second ;
2226- if ( Time . OneHour > timeSpan ) return Resolution . Minute ;
2227- if ( Time . OneDay > timeSpan ) return Resolution . Hour ;
2286+ if ( Time . OneHour > timeSpan ) return Resolution . Minute ;
2287+ if ( Time . OneDay > timeSpan ) return Resolution . Hour ;
22282288
22292289 return Resolution . Daily ;
22302290 }
@@ -2263,7 +2323,7 @@ public static bool TryParseSecurityType(this string value, out SecurityType secu
22632323 /// <returns>The converted value</returns>
22642324 public static T ConvertTo < T > ( this string value )
22652325 {
2266- return ( T ) value . ConvertTo ( typeof ( T ) ) ;
2326+ return ( T ) value . ConvertTo ( typeof ( T ) ) ;
22672327 }
22682328
22692329 /// <summary>
@@ -2279,16 +2339,16 @@ public static object ConvertTo(this string value, Type type)
22792339 return Enum . Parse ( type , value , true ) ;
22802340 }
22812341
2282- if ( typeof ( IConvertible ) . IsAssignableFrom ( type ) )
2342+ if ( typeof ( IConvertible ) . IsAssignableFrom ( type ) )
22832343 {
22842344 return Convert . ChangeType ( value , type , CultureInfo . InvariantCulture ) ;
22852345 }
22862346
22872347 // try and find a static parse method
2288- var parse = type . GetMethod ( "Parse" , new [ ] { typeof ( string ) } ) ;
2348+ var parse = type . GetMethod ( "Parse" , new [ ] { typeof ( string ) } ) ;
22892349 if ( parse != null )
22902350 {
2291- var result = parse . Invoke ( null , new object [ ] { value } ) ;
2351+ var result = parse . Invoke ( null , new object [ ] { value } ) ;
22922352 return result ;
22932353 }
22942354
@@ -2323,7 +2383,7 @@ public static bool WaitOne(this WaitHandle waitHandle, CancellationToken cancell
23232383 /// <exception cref="T:System.InvalidOperationException">The maximum number of waiters has been exceeded. </exception><exception cref="T:System.ObjectDisposedException">The object has already been disposed or the <see cref="T:System.Threading.CancellationTokenSource"/> that created <paramref name="cancellationToken"/> has been disposed.</exception>
23242384 public static bool WaitOne ( this WaitHandle waitHandle , TimeSpan timeout , CancellationToken cancellationToken )
23252385 {
2326- return waitHandle . WaitOne ( ( int ) timeout . TotalMilliseconds , cancellationToken ) ;
2386+ return waitHandle . WaitOne ( ( int ) timeout . TotalMilliseconds , cancellationToken ) ;
23272387 }
23282388
23292389 /// <summary>
@@ -2906,7 +2966,7 @@ public static bool TryConvert<T>(this PyObject pyObject, out T result, bool allo
29062966 {
29072967 result = ( T ) pyObject . AsManagedObject ( type ) ;
29082968 // pyObject is a C# object wrapped in PyObject, in this case return true
2909- if ( ! pyObject . HasAttr ( "__name__" ) )
2969+ if ( ! pyObject . HasAttr ( "__name__" ) )
29102970 {
29112971 return true ;
29122972 }
@@ -3343,7 +3403,7 @@ public static IEnumerable<List<T>> BatchBy<T>(this IEnumerable<T> enumerable, in
33433403 {
33443404 if ( list == null )
33453405 {
3346- list = new List < T > { enumerator . Current } ;
3406+ list = new List < T > { enumerator . Current } ;
33473407 }
33483408 else if ( list . Count < batchSize )
33493409 {
@@ -3352,7 +3412,7 @@ public static IEnumerable<List<T>> BatchBy<T>(this IEnumerable<T> enumerable, in
33523412 else
33533413 {
33543414 yield return list ;
3355- list = new List < T > { enumerator . Current } ;
3415+ list = new List < T > { enumerator . Current } ;
33563416 }
33573417 }
33583418
@@ -3600,7 +3660,7 @@ public static IEnumerator<BaseData> SubscribeWithMapping(this IDataQueueHandler
36003660 /// <returns>Enumeration of lines in file</returns>
36013661 public static IEnumerable < string > ReadLines ( this IDataProvider dataProvider , string file )
36023662 {
3603- if ( dataProvider == null )
3663+ if ( dataProvider == null )
36043664 {
36053665 throw new ArgumentException ( Messages . Extensions . NullDataProvider ) ;
36063666 }
@@ -4161,7 +4221,7 @@ public static OptionRight Invert(this OptionRight right)
41614221 switch ( right )
41624222 {
41634223 case OptionRight . Call : return OptionRight . Put ;
4164- case OptionRight . Put : return OptionRight . Call ;
4224+ case OptionRight . Put : return OptionRight . Call ;
41654225 default :
41664226 throw new ArgumentOutOfRangeException ( nameof ( right ) , right , null ) ;
41674227 }
0 commit comments