Summary

GF API Documentation

GF API keeps certain conventions that should make understanding and using the API easier:

GF API has more functionality that hasn't been covered in this guide:

  • Allocation Blocks

  • Listing related users and their accounts (relevant to Trader Managers)

  • Trading with compound contracts

  • Configuring risk limits

  • Listing other connected applications with the same username

  • GF API log statements

  • ...and more

Some of these topics are addressed in the GF API Advanced Example

Final code of our example:

Full Example
using System;
using System.Collections.Generic;
using System.Linq;
using GF.Api;
using GF.Api.Accounts;
using GF.Api.Connection;
using GF.Api.Contracts;
using GF.Api.Contracts.Lookup;
using GF.Api.Contracts.Lookup.Request;
using GF.Api.Messaging.Chat;
using GF.Api.Messaging.Notifications;
using GF.Api.Orders;
using GF.Api.Orders.Drafts;
using GF.Api.Orders.Drafts.Validation;
using GF.Api.Positions;
using GF.Api.Threading;
using GF.Api.Utils;
using GF.Api.Values.Contracts.Lookup;
using GF.Api.Values.Orders;

namespace ConsoleSample
{
    internal class Program
    {
        private static SymbolLookupRequestID _frontEsSymbolLookupRequestID;
        private static System.Timers.Timer _statusTimer;

        private static void Main(string[] args)
        {
            Console.WriteLine("Hello OEC!");

            IGFClient client = GF.Api.Impl.GFApi.CreateClient();

            var runner = new GFClientRunner(client);
            runner.Start();

            client.Connection.Aggregate.LoginCompleted += GFClient_OnLoginCompleted;
            client.Connection.Aggregate.LoginFailed += GFClient_OnLoginFailed;
            client.Connection.Aggregate.Disconnected += GFClient_OnDisconnected;
            client.Contracts.Lookup.SymbolLookupReceived += GFClient_OnSymbolLookupReceived;
            client.Subscriptions.Price.PriceTick += GFClient_OnPriceTick;
            client.Orders.OrderStateChanged += GFClient_OnOrderStateChanged;
            client.Orders.OrderConfirmed += GFClient_OnOrderConfirmed;
            client.Orders.OrderFilled += GFClient_OnOrderFilled;
            client.Accounts.AvgPositionChanged += GFClient_OnAvgPositionChanged;
            client.Accounts.AccountSummaryChanged += GFClient_OnAccountSummaryChanged;
            client.Accounts.BalanceChanged += GFClient_OnBalanceChanged;
            client.Messaging.Notifications.NotificationMessageReceived += GFClient_OnNotificationMessageReceived;
            client.Messaging.Chat.ChatMessageReceived += GFClient_OnChatMessageReceived;
            client.Logging.ErrorOccurred += GFClient_OnErrorOccurred;

            client.Connection.Aggregate.Connect(
                new ConnectionContextBuilder()
                    .WithUserName("username")
                    .WithPassword("password")
                    .WithPort(9210)
                    .WithHost("api.gainfutures.com")
                    .WithUUID("9e61a8bc-0a31-4542-ad85-33ebab0e4e86")
                    .WithForceLogin(true)
                    .Build());

            Console.WriteLine("Connecting...");

            _statusTimer = new System.Timers.Timer {Interval = TimeSpan.FromSeconds(10).TotalMilliseconds};
            _statusTimer.Elapsed += (_, __) => StatusTimer_Tick(client);
            _statusTimer.Start();

            Console.WriteLine("Press any key to exit");
            Console.ReadKey();

            client.Connection.Aggregate.Disconnect();
            _statusTimer.Stop();
            runner.Stop();
        }

        private static void GFClient_OnChatMessageReceived(IGFClient client, ChatMessageEventArgs e)
        {
            Console.WriteLine($"User Message. {e.ChatMessage.FromUser.Name} - {e.ChatMessage.Message}");
            client.Messaging.Chat.SendMessage(e.ChatMessage.FromUser.ID, "Hey, I'm just a robot!");
        }

        private static void GFClient_OnNotificationMessageReceived(IGFClient client, NotificationMessageEventArgs e)
        {
            DisplayNotificationMessage(DateTime.UtcNow, e.NotificationMessage.Channel, e.NotificationMessage.Message);
        }

        private static void DisplayNotificationHistory(IGFClient client)
        {
            foreach (var notification in client.Messaging.Notifications.Get())
                DisplayNotificationMessage(notification.Timestamp, notification.Channel, notification.Message);
        }

        private static void DisplayNotificationMessage(DateTime timestamp, NotificationChannel channel, string message)
        {
            Console.WriteLine($"Notification. {channel}: {message} at {timestamp.ToLocalTime()}");
        }

        private static void GFClient_OnBalanceChanged(IGFClient client, BalanceChangedEventArgs e)
        {
            DisplayBalance("Balance Changed", e.Account, e.Currency);
        }

        private static void GFClient_OnAccountSummaryChanged(IGFClient client, AccountSummaryChangedEventArgs e)
        {
            DisplayBalance("Account Summary Changed", e.Account, e.Currency);
        }

        private static void DisplayBalance(string comment, IAccount account, GF.Api.Currencies.ICurrency currency)
        {
            GF.Api.Balances.IBalance balance = account.Balances[currency];
            Console.WriteLine($"{comment}. {account.Spec} ({currency.Name}): P/L = {Math.Round(balance.OpenPnL + balance.RealizedPnL, 2)}. Total Net Liq: {account.TotalBalance.NetLiq:c}");
        }

        private static void GFClient_OnAvgPositionChanged(IGFClient client, PositionChangedEventArgs e)
        {
            Console.WriteLine(
                "Average Position. {0}/{1}: Net Pos: {2} @ {3}, Bought: {4}, Sold {5}, Prev Pos: {6} P/L: {7:c}",
                e.Account.Spec, e.ContractPosition.Contract.Symbol,
                e.ContractPosition.Net.Volume, e.ContractPosition.Contract.PriceToString(e.ContractPosition.Net.Price),
                e.ContractPosition.Long.Volume, e.ContractPosition.Short.Volume,
                e.ContractPosition.Prev.Volume,
                e.ContractPosition.OTE + e.ContractPosition.Gain);
        }

        private static void GFClient_OnOrderFilled(IGFClient client, OrderFilledEventArgs e)
        {
            Console.WriteLine(
                "#{0} New fill: {1} @ {2} ({3}). Total filled qty: {4}, avg. price: {5}",
                e.Order.ID,
                e.Fill.Quantity,
                e.Fill.Contract.PriceToString(e.Fill.Price),
                e.Fill.IsActive ? "active" : "cancelled",
                e.Order.Fills.TotalQuantity,
                e.Order.Contract.PriceToString(e.Order.Fills.AvgPrice));
        }

        private static void GFClient_OnErrorOccurred(IGFClient client, ErrorEventArgs e)
        {
            Console.WriteLine($"OnError: {e.Exception.Message}");
        }

        private static void GFClient_OnOrderConfirmed(IGFClient client, OrderConfirmedEventArgs e)
        {
            Console.WriteLine($"#{e.OriginalOrderID} order confirmed. New order ID is {e.Order.ID}");
        }

        private static void GFClient_OnOrderStateChanged(IGFClient client, OrderStateChangedEventArgs e)
        {
            Console.WriteLine($"#{e.Order.ID} order state changed from {e.PreviousOrderState} to {e.Order.CurrentState}");
        }

        private static void GFClient_OnLoginFailed(IGFClient client, LoginFailedEventArgs e)
        {
            Console.WriteLine($"OnLoginFailed: {e.FailReason}");
        }

        private static void GFClient_OnDisconnected(IGFClient client, DisconnectedEventArgs e)
        {
            Console.WriteLine($"OnDisconnected: {e.Message}");
        }

        private static void GFClient_OnLoginCompleted(IGFClient client, LoginCompleteEventArgs e)
        {
            Console.WriteLine($"OnLoginComplete: CompleteConnected={client.Connection.Aggregate.IsConnected}");
            Console.WriteLine($"\tAccounts: {client.Accounts.Get().Count}, orders: {client.Orders.Get().Count}, base contracts: {client.Contracts.Base.Get().Count}");

            DisplayAccount(client, client.Accounts.Get().First());
            DisplayNotificationHistory(client);

            _frontEsSymbolLookupRequestID = client.Contracts.Lookup.ByCriteria(
                new SymbolLookupRequestBuilder()
                    .WithResultCount(1)
                    .WithSymbol("ES", TextSearchMode.StartsWith)
                    .Build());
        }

        private static void GFClient_OnSymbolLookupReceived(IGFClient client, SymbolLookupEventArgs e)
        {
            if (_frontEsSymbolLookupRequestID != null && e.RequestID == _frontEsSymbolLookupRequestID)
            {
                if (e.Contracts.Any())
                {
                    client.Subscriptions.Price.Subscribe(e.Contracts.First().ID);
                }
            }
        }

        private static void GFClient_OnPriceTick(IGFClient client, PriceChangedEventArgs e)
        {
            if (Math.Abs(e.Price.LastPrice - e.Price.BidPrice) < e.Contract.TickSize)
                PlaceOrder(client, e.Contract, OrderSide.Buy, e.Price.BidPrice, "By Bid");
            else if (Math.Abs(e.Price.LastPrice - e.Price.AskPrice) < e.Contract.TickSize)
                PlaceOrder(client, e.Contract, OrderSide.Sell, e.Price.AskPrice, "By Ask");
        }

        private static void StatusTimer_Tick(IGFClient client)
        {
            client.Threading.BeginInvoke(() =>
            {
                if (client.Connection.Aggregate.IsConnected)
                {
                    DisplayAccount(client, client.Accounts.Get().First());
                    CheckAndCancelOrder(client);
                    ModifyOrder(client);
                }
            });
        }

        private static void PlaceOrder(IGFClient client, IContract contract, OrderSide orderSide, double limitPrice, string comments)
        {
            if (client.Orders.Get().Count == 0 || client.Orders.Get().Last().IsFinalState)
            {
                var orderDraft = new OrderDraftBuilder()
                    .WithAccountID(client.Accounts.Get().First().ID)
                    .WithContractID(contract.ID)
                    .WithSide(orderSide)
                    .WithOrderType(OrderType.Limit)
                    .WithPrice(limitPrice)
                    .WithQuantity(1)
                    .WithEnd(DateTime.UtcNow.AddMinutes(1))
                    .WithComments(comments)
                    .Build();

                IReadOnlyList<OrderDraftValidationError> validationErrors = client.Orders.Drafts.Validate(orderDraft);
                if (validationErrors.Any())
                {
                    Console.WriteLine($"ERROR. Order draft is invalid ({orderSide} {orderDraft.Quantity} {contract.Symbol} @ {contract.PriceToString(limitPrice)}):");
                    foreach (var error in validationErrors)
                        Console.WriteLine($"\t{error.Message}");
                }
                else
                {
                    IOrder order = client.Orders.SendOrder(orderDraft);
                    Console.WriteLine($"Order {order} was sent");
                }
            }
        }

        private static void ModifyOrder(IGFClient client)
        {
            if (client.Orders.Get().Any() && !client.Orders.Get().Last().IsFinalState)
            {
                IOrder currentWorkingOrder = client.Orders.Get().Last();
                if (currentWorkingOrder.Commands.Last().State == CommandState.Executed)
                {
                    var priceChange = currentWorkingOrder.IsBuySide
                        ? currentWorkingOrder.Contract.TickSize
                        : -currentWorkingOrder.Contract.TickSize;
                    ModifyOrderDraft modifyOrderDraft = new ModifyOrderDraftBuilder()
                        .FromOrder(currentWorkingOrder)
                        .WithPrice((currentWorkingOrder.Price ?? 0.0) + priceChange)
                        .Build();

                    IReadOnlyList<OrderDraftValidationError> validationErrors = client.Orders.Drafts.Validate(modifyOrderDraft);
                    if (validationErrors.Any())
                    {
                        Console.WriteLine($"ERROR. Attempt to modify the order {currentWorkingOrder} failed:");
                        foreach (var error in validationErrors)
                            Console.WriteLine($"\t{error.Message}");
                    }
                    else
                    {
                        IOrder order = client.Orders.ModifyOrder(modifyOrderDraft);
                        Console.WriteLine($"Modify request has been sent: {order}");
                    }
                }
            }
        }

        private static void CheckAndCancelOrder(IGFClient client)
        {
            if (client.Orders.Get().Any() && !client.Orders.Get().Last().IsFinalState)
            {
                IOrder currentWorkingOrder = client.Orders.Get().Last();
                if (currentWorkingOrder.Commands.Last().State == CommandState.Executed)
                {
                    if (client.Orders.Drafts.GetPriceCount(currentWorkingOrder.Type) > 0)
                    {
                        if (Math.Abs((currentWorkingOrder.Price ?? 0.0) - currentWorkingOrder.Contract.CurrentPrice.LastPrice) >= 3 * currentWorkingOrder.Contract.TickSize)
                        {
                            Console.WriteLine($"Send request to cancel: {currentWorkingOrder}");
                            client.Orders.CancelOrder(currentWorkingOrder.ID, SubmissionType.Automatic);
                        }
                    }
                }
            }
        }

        private static void DisplayAccount(IGFClient client, IAccount account)
        {
            GF.Api.Balances.IBalance totalBalance = account.TotalBalance;

            Console.WriteLine($"Account: {account.Spec}");
            Console.WriteLine($"\tNetLiq: {totalBalance.NetLiq:c}");
            Console.WriteLine($"\tCash: {totalBalance.Cash:c}");
            Console.WriteLine($"\tOpen P/L: {totalBalance.OpenPnL:c}");
            Console.WriteLine($"\tTotal P/L: {totalBalance.RealizedPnL + totalBalance.OpenPnL:c}");
            Console.WriteLine($"\tInitial Margin: {totalBalance.InitialMargin:c}");
            Console.WriteLine($"\tNet Options Value: {totalBalance.LongCallOptionsValue + totalBalance.LongPutOptionsValue + totalBalance.ShortCallOptionsValue + totalBalance.ShortPutOptionsValue:c}");
            Console.WriteLine($"Average Positions: {account.AvgPositions.Count}");
            Console.WriteLine($"Orders: {client.Orders.Get().Count}, last one: {(client.Orders.Get().Count > 0 ? client.Orders.Get().Last().ToString() : string.Empty)}");
        }
    }
}