Trading Evolution

Markets are never wrong - opinions often are

Getting real-time data from TWS API in C#

Small code snippet to get real-time data using C# TWS API.

Here how it looks like:

Real-time data

  • Lines 13-15: Define contract (stock, futures, forex). In this case it is SPY ETF for S&P 500
  • Line 23: Connect when "Connect" button is clicked
  • Line 28: Request market data after application is connected
  • Lines 33-43: Handle real-time tick data
// Interactive Brokers fields
IBWrapper _ibWrapper;
Contract _ibContract;
int _nextTickerId = 0;

public Main()
{
	InitializeComponent();

	_ibWrapper = new IBWrapper(this);
	_ibContract = new Contract();

	_ibContract.Symbol			= "SPY";
	_ibContract.SecType			= "STK";
	_ibContract.Exchange		= "SMART";
}

private void Connect_Click(object sender, EventArgs e)
{
	_ibWrapper.ClientConnected      += IbWrapper_ClientConnected;
	_ibWrapper.TickPrice            += IbWrapper_TickPrice;

	_ibWrapper.Connect("127.0.0.1", 7496, 0);
}

private void IbWrapper_ClientConnected(object sender, EventArgs e)
{
	_ibWrapper.IBClient.reqMktData(_nextTickerId++, _ibContract, null, false, null);
}

private void IbWrapper_TickPrice(object sender, TickPriceEventArgs e)
{
	switch (e.Field)
	{
		case TickTypeEnum.Bid:
			_bidTextBox.Text = e.Price.ToString("C");
			break;
		case TickTypeEnum.Ask:
			_askTextBox.Text = e.Price.ToString("C");
			break;
		case TickTypeEnum.Last:
			_lastTextBox.Text = e.Price.ToString("C");
			break;
	}
}

 

How to attach stop loss order to any order

Everybody knows that stop loss orders are important. Interactive Brokers API allows you to send an order with attached stop loss so you don't have to write code to monitor when order is filled then you place your stop order. You can send your order and attached stop loss order which will be monitored by Interactive Brokers and as soon as the main order is filled Interactive Brokers will activate your stop. When order is not filled and you cancel it stop loss order will be cancelled as well.

var entryOrder = new IBApi.Order();

entryOrder.OrderId	= _ibWrapper.NextOrderId++;
entryOrder.Action	= "BUY";
entryOrder.OrderType	= "LMT";
entryOrder.LmtPrice	= price;
entryOrder.TotalQuantity = quantity;
entryOrder.Tif		= "GTC";
entryOrder.Transmit	= false;
_ibWrapper.IBClient.placeOrder(entryOrder.OrderId, contract, entryOrder);

var stopOrder = new IBApi.Order();

stopOrder.OrderId	= _ibWrapper.NextOrderId++;
stopOrder.ParentId	= entryOrder.OrderId;
stopOrder.Action	= "SELL";
stopOrder.OrderType	= "STP";
stopOrder.AuxPrice	= stopPrice;
stopOrder.TotalQuantity	= entryOrder.TotalQuantity;
stopOrder.Tif		= "GTC";
stopOrder.Transmit	= false;
_ibWrapper.IBClient.placeOrder(stopOrder.OrderId, contract, stopOrder);

Thread.Sleep(1000); // Let IB process orders first and then transmit parent order

entryOrder.Transmit         = true;
_ibWrapper.IBClient.placeOrder(entryOrder.OrderId, contract, entryOrder);

Few things to note here. First, set stop loss order ParentId to entry order Id. Second, entry order and stop loss order are sent with Transmit field set to "false" and finally transmit entry order.

How to create an order that automatically cancels in 5 min

When you write your automated system sometimes your signal is valid only for the next 5, 10 or 15 min. It is useful to create orders that auto expire if not filled after certain period of time so you don't have to write code that cancels them. All is needed is to set Time In Force (Tif) property to "GTD" and set GoodTillDate to the time in a future you want this order to expire if it is not filled. In the example below I used DateTime.Now.AddMinutes to add 10 minutes to current time.

var order = new IBApi.Order();

order.OrderId = _ibWrapper.NextOrderId++;
order.Action = "BUY";
order.OrderType = "LMT";
order.LmtPrice = price;
order.TotalQuantity = quantity;
order.Tif = "GTD";
order.GoodTillDate     = DateTime.Now.AddMinutes(10).ToString("yyyyMMdd HH:mm:ss ");

_ibWrapper.IBClient.placeOrder(order.OrderId, ..., order);

 

How to enable API access in TWS

 

  1. File ⇒ Global Configuration or Edit ⇒ Global Configuration (Classic TWS)
  2. API ⇒ Settings
  3. Check "Enable ActiveX and Socket Clients"
  4. Uncheck "Read-Only API"
  5. Verify socket port. The default it 7496
  6. Check "Allow connections from localhost only"

 

More details on InteractiveBrokers.com Trader Workstation API Settings

How to send an order to IB TWS in just 68 lines

Below is C# code snippet to send buy 100 shares of SPY at market order. Code is pretty much self-explanatory. Connect, PlaceOrder, Disconnect and we are done.

class Program
{
	static IBWrapper _ibWrapper;
	static IBApi.Contract _ibContract;
	static IBApi.Order _ibOrder;

	static ManualResetEvent _connectedEvent = new ManualResetEvent(false);
	static ManualResetEvent _orderSubmittedEvent = new ManualResetEvent(false);

	static void Main(string[] args)
	{
		try
		{
			_ibWrapper = new IBWrapper();

			// Subscribe to events
			_ibWrapper.ClientConnected      += IbWrapper_ClientConnected;
			_ibWrapper.OrderStatusEvent     += IbWrapper_OrderStatusEvent;
			_ibWrapper.Error                += IbWrapper_Error;

			// Connect to TWS
			_ibWrapper.Connect("127.0.0.1", 7496, 0);
			if (!_ibWrapper.IBClient.IsConnected())
			{
				Console.WriteLine("Failed to connect to TWS");
				return;
			}

			_connectedEvent.WaitOne();

			_ibContract = new IBApi.Contract() { Symbol = "SPY", SecType="STK", Exchange = "SMART", PrimaryExch = "Nasdaq" };
			_ibOrder = new IBApi.Order() { Action = "BUY", OrderType = "MKT", Tif = "DAY", TotalQuantity = 100, Transmit = true, OrderId = _ibWrapper.NextOrderId++ };

			_ibWrapper.IBClient.placeOrder(_ibOrder.OrderId, _ibContract, _ibOrder);
			_orderSubmittedEvent.WaitOne();
			Console.WriteLine("Disconnect");

			_ibWrapper.Disconnect();
		}
		catch (Exception e)
		{
			Console.WriteLine(e.Message);
		}
	}

	private static void IbWrapper_ClientConnected(object sender, EventArgs e)
	{
		Console.WriteLine("Connected");
		_connectedEvent.Set();
	}

	private static void IbWrapper_OrderStatusEvent(object sender, OrderStatusEventArgs e)
	{
		if (_ibOrder != null && _ibOrder.OrderId == e.OrderId)
		{
			Console.WriteLine("Order status: " + e.Status);
			_orderSubmittedEvent.Set();
		}
	}

	private static void IbWrapper_Error(object sender, ErrorEventArgs e)
	{
		Console.WriteLine(string.Format("Error: {0}.", e.Error));

		_connectedEvent.Set();
		_orderSubmittedEvent.Set();
	}
}