Trading Evolution

Markets are never wrong - opinions often are

RSI (relative strength index) in C#

The RSI (relative strength index) is a momentum indicator that compares the average of recent gains to average of recent losses. It is calculated using the following formula:

RSI = 100 - 100/(1 + RS)

RS = Average of x days' up closes / Average of x days' down closes.

Below is the implementation of the formula in C#:

	public class Rsi
	{
		int		_period;
		int		_valueCount;
		decimal _last;
		decimal _previousValue;
		decimal _averageGain;
		decimal _averageLoss;

		public Rsi(int period)
		{
			if (period <= 1 || 1000 < period)
				throw new ArgumentOutOfRangeException("period");

			_period = period;
		}

		public bool AddValue(decimal value)
		{
			_last = 0;

			if (_previousValue <= 0)
			{
				_previousValue = value;
				return false;
			}

			if (_valueCount < _period - 1)
			{
				if (_previousValue < value)
					_averageGain += value - _previousValue;
				else
					_averageLoss += _previousValue - value;
				_valueCount++;
				_previousValue = value;
				return false;
			}

			if (_valueCount == _period - 1)
			{
				if (_previousValue < value)
					_averageGain += value - _previousValue;
				else
					_averageLoss += _previousValue - value;
				_valueCount++;

				_averageGain   /= _period;
				_averageLoss   /= _period;
			}
			else
			{
				_valueCount++;
				if (_previousValue < value)
				{
					_averageGain = (_averageGain * (_period - 1.0m) + (value - _previousValue))/_period;
					_averageLoss = (_averageLoss * (_period - 1.0m))/_period;
				}
				else
				{
					_averageGain = (_averageGain * (_period - 1.0m))/_period;
					_averageLoss = (_averageLoss * (_period - 1.0m) + (_previousValue - value))/_period;
				}
			}

			if (_averageLoss != 0)
				_last = 100.0m - 100.0m / (1.0m + _averageGain/_averageLoss);
			else
				_last = 100;

			_previousValue = value;

			return true;
		}

		public decimal Last
		{
			get
			{
				return _last;
			}
		}
	}

 

Least Recently Used (LRU) cache

C# implementation of Least Recently Used (LRU) cache with usage counter

/// <summary>
/// Least Recently Used (LRU) cache
/// </summary>
public class LRUCache<T>
{
	class CacheItem
	{
		public T Value;
		public int Count;

		public override string ToString()
		{
			return string.Format("Value:{0} Count:{1}", Value, Count);
		}
	}

	Dictionary<int, CacheItem> _data;
	int _capacity;

	/// <summary>
	/// Default Constructor
	/// </summary>
	public LRUCache(int capacity)
	{
		if (capacity <= 0)
			throw new ArgumentOutOfRangeException("capacity", "capacity must be a positive number");

		_capacity   = capacity;
		_data       = new Dictionary<int, CacheItem>(capacity);
	}

	/// <summary>
	/// Get/set value by key
	/// </summary>
	/// <param name="key"></param>
	/// <returns>default(T) if value is not found</returns>
	public T this [int key]
    {
		get
		{
			CacheItem result;

			// Return default(T) if item it not found
			if (!_data.TryGetValue(key, out result))
				return default(T);

			// Increase usage count if item is found
			result.Count += 1;
			return result.Value;
		}

		set
		{
			CacheItem result;

			// If key already exists then reset usage count and set new value
			if (_data.TryGetValue(key, out result))
			{
				// If setting the same value assume nothing happend and keep usage
				if (value.Equals(result.Value))
					return;

				result.Value = value;
				result.Count = 0;
				return;
			}

			// New key. Check capacity and drop least used key
			if (_capacity <= _data.Count)
			{
				var sortedUsage = _data.ToList();

				sortedUsage.Sort((v1, v2) => v1.Value.Count.CompareTo(v2.Value.Count));

				// Drop least used key
				_data.Remove(sortedUsage[0].Key);
			}

			_data[key] = new CacheItem() { Value = value };
		}
	}
}

 

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);