From 8ec6a5cb7cf3125a636bc9b34d864ba2e0b232a1 Mon Sep 17 00:00:00 2001 From: ssnyder-intrinio <87659980+ssnyder-intrinio@users.noreply.github.com> Date: Wed, 10 Jul 2024 09:24:42 -0500 Subject: [PATCH] Add delayed option to realtime sources (#15) * delayed * update example and readme * move the delay to query token for ws --- IntrinioRealtimeOptions/Client.fs | 6 ++++-- IntrinioRealtimeOptions/Config.fs | 4 +++- IntrinioRealtimeOptions/config.json | 3 ++- README.md | 14 ++++++++++++++ SampleApp/Program.cs | 16 ++++++++++++++++ SampleApp/config.json | 3 ++- 6 files changed, 41 insertions(+), 5 deletions(-) diff --git a/IntrinioRealtimeOptions/Client.fs b/IntrinioRealtimeOptions/Client.fs index 127f53e..d19ee98 100644 --- a/IntrinioRealtimeOptions/Client.fs +++ b/IntrinioRealtimeOptions/Client.fs @@ -182,6 +182,8 @@ type Client( let clientInfoHeaderKey : string = "Client-Information" let clientInfoHeaderValue : string = "IntrinioRealtimeOptionsDotNetSDKv6.0" + let delayHeaderKey : string = "delay" + let delayHeaderValue : string = "true" let useOnTrade : bool = not (obj.ReferenceEquals(onTrade,null)) let useOnQuote : bool = not (obj.ReferenceEquals(onQuote,null)) @@ -201,8 +203,8 @@ type Client( let getWebSocketUrl (token: string) : string = match config.Provider with - | Provider.OPRA -> "wss://realtime-options.intrinio.com/socket/websocket?vsn=1.0.0&token=" + token - | Provider.MANUAL -> "ws://" + config.IPAddress + "/socket/websocket?vsn=1.0.0&token=" + token + | Provider.OPRA -> "wss://realtime-options.intrinio.com/socket/websocket?vsn=1.0.0&token=" + token + (if config.Delayed then "&delayed=true" else String.Empty) + | Provider.MANUAL -> "ws://" + config.IPAddress + "/socket/websocket?vsn=1.0.0&token=" + token + (if config.Delayed then "&delayed=true" else String.Empty) | _ -> failwith "Provider not specified!" let parseSocketMessage (bytes: byte[], startIndex: byref) : unit = diff --git a/IntrinioRealtimeOptions/Config.fs b/IntrinioRealtimeOptions/Config.fs index ff7acb4..0642ae8 100644 --- a/IntrinioRealtimeOptions/Config.fs +++ b/IntrinioRealtimeOptions/Config.fs @@ -30,12 +30,14 @@ module Config = let mutable ipAddress : string = String.Empty let mutable numThreads : int = 4 let mutable symbols : string[] = [||] + let mutable delayed : bool = false member this.ApiKey with get () : string = apiKey and set (value : string) = apiKey <- value member this.Provider with get () : Provider = provider and set (value : Provider) = provider <- value member this.IPAddress with get () : string = ipAddress and set (value : string) = ipAddress <- value member this.Symbols with get () : string[] = symbols and set (value : string[]) = symbols <- value member this.NumThreads with get () : int = numThreads and set (value : int) = numThreads <- value + member this.Delayed with get () : bool = delayed and set (value : bool) = delayed <- value member _.Validate() : unit = if String.IsNullOrWhiteSpace(apiKey) @@ -47,7 +49,7 @@ module Config = if (numThreads <= 0) then failwith "You must specify a valid 'NumThreads'" for i = 0 to symbols.Length-1 do - symbols[i] <- TranslateContract(symbols[i]) + symbols[i] <- TranslateContract(symbols[i]) let LoadConfig() = Log.Information("Loading application configuration") diff --git a/IntrinioRealtimeOptions/config.json b/IntrinioRealtimeOptions/config.json index e40acd5..b744d94 100644 --- a/IntrinioRealtimeOptions/config.json +++ b/IntrinioRealtimeOptions/config.json @@ -3,7 +3,8 @@ "ApiKey": "", "Provider": "OPRA", "Symbols": [ "GOOG__220408C02870000", "MSFT__220408C00315000", "AAPL__220414C00180000", "SPY", "TSLA" ], - "NumThreads": 16 + "NumThreads": 16, + "Delayed": false }, "Serilog": { "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ], diff --git a/README.md b/README.md index 8541063..7af6b86 100644 --- a/README.md +++ b/README.md @@ -168,6 +168,20 @@ namespace SampleApp // Register only the callbacks that you want. // Take special care when registering the 'OnQuote' handler as it will increase throughput by ~10x _client = new Client(onTrade: onTrade, onQuote: onQuote, onRefresh: OnRefresh, onUnusualActivity: OnUnusualActivity); + + // Alternatively, you can programmatically make your configuration if you don't want to use a config.json file + // Config.Config config = new Config.Config() + // { + // ApiKey = "", + // Delayed = false, //Use this to specify that even though you have realtime access, for this connection you want delayed 15minute + // NumThreads = 8, + // Provider = Provider.OPRA, + // Symbols = new string[]{"AAPL", "MSFT"} + // }; + //Also, if you're not using a config.json to configure serilogs, then you'll need to programmatically configure it: + // var logConfig = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("config.json").Build(); + // Serilog.Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(logConfig).CreateLogger(); + // _client = new Client(onTrade: onTrade, onQuote: onQuote, onRefresh: OnRefresh, onUnusualActivity: OnUnusualActivity, config: config); _timer = new Timer(TimerCallback, _client, 10_000, 10_000); diff --git a/SampleApp/Program.cs b/SampleApp/Program.cs index 325c622..19ee993 100644 --- a/SampleApp/Program.cs +++ b/SampleApp/Program.cs @@ -1,6 +1,8 @@ using System; +using System.IO; using System.Threading; using Intrinio.Realtime.Options; +using Microsoft.Extensions.Configuration; using Serilog; using Serilog.Core; @@ -150,6 +152,20 @@ static void Main(string[] args) // Take special care when registering the 'OnQuote' handler as it will increase throughput by ~10x _client = new Client(onTrade: onTrade, onQuote: onQuote, onRefresh: OnRefresh, onUnusualActivity: OnUnusualActivity); + // // Alternatively, you can programmatically make your configuration if you don't want to use a config.json file + // Config.Config config = new Config.Config() + // { + // ApiKey = "", + // Delayed = false, //Use this to specify that even though you have realtime access, for this connection you want delayed 15minute + // NumThreads = 8, + // Provider = Provider.OPRA, + // Symbols = new string[]{"AAPL", "MSFT"} + // }; + // //Also, if you're not using a config.json to configure serilogs, then you'll need to programmatically configure it: + // var logConfig = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("config.json").Build(); + // Serilog.Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(logConfig).CreateLogger(); + // _client = new Client(onTrade: onTrade, onQuote: onQuote, onRefresh: OnRefresh, onUnusualActivity: OnUnusualActivity, config: config); + _timer = new Timer(TimerCallback, _client, 30_000, 30_000); // Use this to subscribe to a static list of symbols (option contracts) provided in config.json diff --git a/SampleApp/config.json b/SampleApp/config.json index d89a74d..850578f 100644 --- a/SampleApp/config.json +++ b/SampleApp/config.json @@ -3,7 +3,8 @@ "ApiKey": "", //Your Intrinio API key. "Provider": "OPRA", "Symbols": [ "GOOG__210917C01040000", "MSFT__210917C00180000", "AAPL__210917C00130000", "SPY" ], //This is a list of individual contracts (or option chains) to subscribe to. - "NumThreads": 8 //The number of threads to use for processing events. + "NumThreads": 8, //The number of threads to use for processing events. + "Delayed": false }, "Serilog": { "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],