We'll need some data to make trading decisions. GF API can give us quotes, DOMs, bars, ticks and histograms. To do so, we will request a subscription or a chunk of data, and listen to the response event.
This topic contains the following sections:
Quotes are the simplest kind of market data. Let's enhance our example to output all quotes for a symbol retrieved by symbol lookup.
private static SymbolLookupRequestID _esh18SymbolLookupRequestID; private static IPriceSubscription _quoteSubscription; private static void RegisterOnPriceTick(IGFClient client) { client.Subscriptions.Price.PriceTick += GFClient_OnPriceTick; } private static void GFClient_OnSymbolLookupReceived(IGFClient client, GF.Api.Contracts.Lookup.SymbolLookupEventArgs e) { if (e.RequestID == _esh18SymbolLookupRequestID) { Console.WriteLine($"Symbol lookup request for ESH18 found {e.Contracts.Count} contracts"); foreach (var contract in e.Contracts) { DisplayContract(contract); _quoteSubscription = client.Subscriptions.Price.Subscribe(contract.ID); } } } private static void GFClient_OnPriceTick(IGFClient client, GF.Api.Contracts.PriceChangedEventArgs e) { Console.WriteLine( "{0} {1} O:{2} H:{3} L:{4} Last:{5}, Bid:{6} Ask:{7} TotalVol:{8}", e.Contract.Symbol, e.Price.LastDateTime, e.Contract.PriceToString(e.Price.OpenPrice), e.Contract.PriceToString(e.Price.HighPrice), e.Contract.PriceToString(e.Price.LowPrice), e.Contract.PriceToString(e.Price.LastPrice), e.Contract.PriceToString(e.Price.BidPrice), e.Contract.PriceToString(e.Price.AskPrice), e.Price.TotalVol); } private static void UnsubscribeAll() { _quoteSubscription.Unsubscribe(); }
Tip |
---|
Alternatively, we could access the price from IContract.CurrentPrice. |
To unsubscribe, call ISubscription.Unsubscribe. Subscriptions are counted, so you must Unsubscribe from every ISubscription to avoid subscription leaks. There are a limited number of simultaneous subscriptions allowed.
Note |
---|
To have accurate position values, the relevant contracts need to have quote subscriptions. In legacy OEC API, the API did this automatically, but in GF API it is the client's responsibility. This allows more control over the limited number of active subscriptions. For additional guidance, see the PNL Subscription Manager snippet. |
DOM subscription is similar to Quotes:
private static IDomSubscription _domSubscription; private static void RegisterOnDOMChanged(IGFClient client) { client.Subscriptions.Dom.DomChanged += GFClient_OnDomChanged; } private static void GFClient_OnSymbolLookupReceived(IGFClient client, GF.Api.Contracts.Lookup.SymbolLookupEventArgs e) { _domSubscription = client.Subscriptions.Dom.Subscribe(e.Contracts.First().ID); } private static void GFClient_OnDomChanged(IGFClient client, GF.Api.Subscriptions.Doms.DomChangedEventArgs e) { GF.Api.Subscriptions.Doms.IDom dom = e.Contract.DOM; Console.WriteLine( "DOM {0} {1} Bids: {2} Asks: {3} Best Bid: {4} Best Ask: {5}", e.Contract.Symbol, dom.LastUpdate, dom.BidLevels.Count, dom.AskLevels.Count, dom.BidLevels.Count > 0 ? dom.BidSizes[0] + "@" + e.Contract.PriceToString(dom.BidLevels[0]) : "-", dom.AskLevels.Count > 0 ? dom.AskSizes[0] + "@" + e.Contract.PriceToString(dom.AskLevels[0]) : "-"); } private static void UnsubscribeAll() { _domSubscription.Unsubscribe(); }
For bars, we can either request a single chunk of historical data, or load some historical bars up to now and open a stream of updates via subscription.
GF API provides a variety of bar types:
- Second
- Minute
- Range
- Momentum
- Tick
- Volume
- Daily
- Weekly
- Monthly
You can use IBarDurationFactory and IBarDescriptionFactory to help construct the subscription parameters.
private static void RegisterOnBarsReceived(IGFClient client) { client.Subscriptions.Bars.BarsReceived += GFClient_OnBarsReceived; } private static void GFClient_OnSymbolLookupReceived(IGFClient client, GF.Api.Contracts.Lookup.SymbolLookupEventArgs e) { // Subscribe 10-min bars and loads previous 3 days of historical bars client.Subscriptions.Bars.Subscribe( e.Contracts.First().ID, client.Subscriptions.Bars.Duration.Create(DateTime.UtcNow.AddDays(-3)), client.Subscriptions.Bars.Description.CreateMinutes(10)); } private static void GFClient_OnBarsReceived(IGFClient client, GF.Api.Subscriptions.Bars.BarsReceivedEventArgs e) { Console.WriteLine($"{e.Bars.Count} bars received for {e.Subscription.Contract.Symbol} {e.Subscription.Description.Type} {e.Subscription.Description.Interval}"); foreach (var bar in e.Bars) DisplayBar(e.Subscription.Contract, bar); } private static void DisplayBar(GF.Api.Contracts.IContract contract, Bar bar) { Console.WriteLine( "{0} {1} O:{2} H:{3} L:{4} C:{5} Vol:{6} Ticks:{7} UpVol:{8} DownVol:{9}", contract.Symbol, bar.OpenTimestamp.ToLocalTime(), contract.PriceToString(bar.Open), contract.PriceToString(bar.High), contract.PriceToString(bar.Low), contract.PriceToString(bar.Close), bar.Volume, bar.Ticks, bar.UpVolume, bar.DownVolume); }
Ticks are similar to Bars:
private static void RegisterOnTicksReceived(IGFClient client) { client.Subscriptions.Ticks.TicksReceived += GFClient_OnTicksReceived; } private static void GFClient_OnSymbolLookupReceived(IGFClient client, GF.Api.Contracts.Lookup.SymbolLookupEventArgs e) { // Subscribe 10-min bars and loads previous 3 days of historical bars client.Subscriptions.Bars.Subscribe( e.Contracts.First().ID, client.Subscriptions.Bars.Duration.Create(DateTime.UtcNow.AddDays(-3)), client.Subscriptions.Bars.Description.CreateMinutes(10)); // Request ticks for the last hour without further updates client.Subscriptions.Ticks.Subscribe( e.Contracts.First().ID, client.Subscriptions.Ticks.Duration.Create(DateTime.UtcNow.AddHours(-3), DateTime.UtcNow)); } private static void GFClient_OnTicksReceived(IGFClient client, GF.Api.Subscriptions.Ticks.TicksReceivedEventArgs e) { Console.WriteLine($"{e.Ticks.Count} ticks received for {e.Subscription.Contract.Symbol}"); foreach (var tick in e.Ticks) { Console.WriteLine($"{tick.Timestamp.ToLocalTime()} {tick.Volume}@{tick.Price}\tBid:{tick.BidPrice} Ask:{tick.AskPrice}"); } }
Subscriptions are a limited resource in GF API. If you no longer need a subscription, unsubscribe from it. If the subscription limit is reached, additional subscriptions will be declined by the server.
Current subscription limits properties:
You can check the property values like so:
private string GetBarLimit(IGFClient client) { return client.Properties.ContainsKey("MaxBars") ? client.Properties["MaxBars"] : null; }
Bar and tick requests are a more expensive resource than quotes and DOMs, so they have additional limitations. These limits may be changed at any time:
MaxBars = 8192: Maximum amount of bars returned to a client per request
MaxDayBarDays = 365: Maximum allowed days to load for day and day-based bars
MaxIntraBarDays = 90: Maximum allowed days to load for intraday bars
MaxPerGroup = 4096: Maximum items per collection returned to a client
MaxTickBasedBarDays = 10: Maximum allowed days to load for tick-based bars
MaxTickDays = 3: Maximum allowed days to load for ticks
MaxTicks = 65536: Maximum amount of ticks returned to a client per request
Let's output the current subscriptions status to the console window.
private static System.Timers.Timer _statusTimer; private static void MonitorSubscriptionLimits(IGFClient client) { _statusTimer = new System.Timers.Timer {Interval = TimeSpan.FromSeconds(1).TotalMilliseconds}; _statusTimer.Elapsed += (_, __) => StatusTimer_Tick(client); _statusTimer.Start(); Console.WriteLine("Press any key to exit"); Console.ReadKey(); } private static void StatusTimer_Tick(IGFClient client) { client.Threading.BeginInvoke(() => { if (client.Connection.Aggregate.IsConnected) ShowCurrentSubscriptionStatus(client); }); } private static void ShowCurrentSubscriptionStatus(IGFClient client) { Console.WriteLine( "Quotes: {0}/{1}; DOMs: {2}/{3}; bars: {4}/{5}; ticks: {6}/{7}", GetQuotesSubscriptionCount(client), GetSubscriptionLimit(client, "Quotes"), GetDomSubscriptionCount(client), GetSubscriptionLimit(client, "DOMs"), GetBarsSubscriptionCount(client), GetSubscriptionLimit(client, "MaxBars"), GetTicksSubscriptionCount(client), GetSubscriptionLimit(client, "MaxTicks")); } private static int GetSubscriptionLimit(IGFClient client, string key) { var limit = 0; if (client.Properties.ContainsKey(key)) int.TryParse(client.Properties[key], out limit); return limit; } private static IEnumerable<GF.Api.Subscriptions.ISubscription> GetContinuousSubscriptions(IGFClient client, params SubscriptionType[] subscriptionTypes) { return client.Subscriptions.Get() .Where(subscription => subscription.Duration.Continuity == GF.Api.Subscriptions.Continuity.Continuous && subscriptionTypes.Contains(subscription.Description.Type)); } private static int GetQuotesSubscriptionCount(IGFClient client) { return GetContinuousSubscriptions(client, SubscriptionType.Price).Count(); } private static int GetDomSubscriptionCount(IGFClient client) { return GetContinuousSubscriptions(client, SubscriptionType.DOM).Count(); } private static int GetBarsSubscriptionCount(IGFClient client) { return GetContinuousSubscriptions( client, SubscriptionType.Bar, SubscriptionType.DayBar, SubscriptionType.WeeklyBar, SubscriptionType.MonthlyBar, SubscriptionType.SecondBar, SubscriptionType.RangeBar, SubscriptionType.MomentumBar, SubscriptionType.TickBar, SubscriptionType.VolumeBar) .Count(); } private static int GetTicksSubscriptionCount(IGFClient client) { return GetContinuousSubscriptions(client, SubscriptionType.Tick, SubscriptionType.Histogram).Count(); }