Over the last couple of days I was playing with what is known as spread betting. Spread betting is not a type of investment. It is gambling. You make money if you predict where the stock market is going. It is as simple as that. However, unlike the traditional forms of stock market investment practices, in spread betting there is no CGT (capital gain tax). Which is excellent!

I’ve made some money and I’ve lost some too. In fact, I’ve made about 100 pounds in less then 10 minutes and then I lost half of them during the course of the entire day. This made me learn an important lesson and also made me think how to improve my strategy.

You see, I am not a spread betting expert and I have very little knowledge about the stock market in general. However, I’ve learned one thing – you do have to have some kind of strategy and really stick to it if you want to be successful. Also, you have to be prepared to lose. But if you have a good strategy then you have nothing to worry about. As long as your winnings are more then your losses then you should be fine.

I started doing spread betting out of interest. I have a couple of friends that do this as well so I though I might take on the game from a slightly different angel. After getting a feeling with my spread betting company (IG Index) I realized that I can make a lot better decisions if I have a better interface to their website, i.e. have more features that will allow me to move faster and decide faster. This is essential if you want to play on highly-volatile indices such as the FTSE100. So I decided to to write a greasemonkey patch for IG Index that gives me essentially more features.

// ==UserScript==
// @name           IG Index Helper
// @namespace      http://www.igindex.co.uk/
// @description    IG Index Helper
// @include        http://www.igindex.co.uk/*
// ==/UserScript==

// some globals
var is_jquery_loaded = false;
var is_engine_started = false;

// the data object
var data = {prices:[], times:0};

// prototype patch for avarage computation on arrays
Array.prototype.average = function() {
	var av = 0;
	var cnt = 0;
	var len = this.length;

	for (var i = 0; i < len; i++) {
		var e = +this[i];

		if (!e && this[i] !== 0 && this[i] !== '0') {
			e--;
		}

		if (this[i] == e) {
			av += e; cnt++;
		}
	}

	return av/cnt;
}

// load jQuery
function load_jquery(callback) {
	// build the jQuery script element
	var scr_e = document.createElement('script');
	scr_e.src = 'http://jquery.com/src/jquery-latest.js';
	scr_e.type = 'text/javascript';

	// add the jQuery script
	document.getElementsByTagName('head')[0].appendChild(scr_e);

	// check for jQuery
	function check_for_jquery() {
		if(typeof unsafeWindow.jQuery == 'undefined') {
			window.setTimeout(check_for_jquery, 100);
		} else {
			is_jquery_loaded = true;

			$ = unsafeWindow.jQuery;
			jQuery = unsafeWindow.jQuery;

			callback();
		}
	}

	// check if jQuery is loaded
	check_for_jquery();
}

// patch
function patch() {
	// get the ticket frame
	var ifr = document.getElementById('ifrBetslip-0');

	// get the ticket window
	var doc = ifr.contentDocument;

	// get the sell and buy elements
	var sell_e = doc.getElementById('F|list|1|3|div');
	var buy_e = doc.getElementById('F|list|1|4|div');

	// populate some important data
	data['sell_e'] = sell_e;
	data['buy_e'] = buy_e;

	// make the spread element
	$('#BID_OFFER tr', doc).append('<td class="slash">/</td><td class="flasher"><span id="spread_e">n/a</span></td></tr>');

	// populate some important data
	data['spread_e'] = $('#spread_e', doc).get(0);

	// make the flunctuation and speed row
	$('#OTOTable > tbody', doc).append('<tr><td class="t1"><label>F/S/D</label></td><td colspan="3"><table cellspacing="0" cellpadding="0" border="0" class="prices"><tbody><tr><td class="flasher"><span id="flunctuation_e">n/a</span></td><td class="slash">/</td><td class="flasher"><span id="speed_e">n/a</span></td><td class="slash">/</td><td class="flasher"><span id="direction_e">n/a</span></td></tr></tbody></table></td></tr>');

	// populate some important data
	data['flunctuation_e'] = $('#flunctuation_e', doc).get(0);
	data['speed_e'] = $('#speed_e', doc).get(0);
	data['direction_e'] = $('#direction_e', doc).get(0);

	// install the goal indicator
	$('#OTOTable > tbody', doc).append('<tr><td class="t1"><label>S/L</label></td><td colspan="3"><table cellspacing="0" cellpadding="0" border="0" class="prices"><tbody><tr><td class="flasher"><span id="spread_in_e">n/a</span></td><td class="slash">/</td><td class="flasher"><span id="limit_in_e">n/a</span></td></tr></tbody></table></td></tr>');

	// populate some important data
	data['spread_in_e'] = $('#spread_in_e', doc).get(0);
	data['limit_in_e'] = $('#limit_in_e', doc).get(0);

	// install auto order level computator
	$('#OTOTable > tbody', doc).append('<tr><td class="t1"><label>Auto OL</label></td><td class="pos3"><div class="inputIncrement"><input type="text" maxlength="9" id="auto_ol_e" value="" class="text large"/><a title="" href="#" class="down"/><a title="" href="#" class="up"/></div></td><td class="t3"></td></tr>');

	// populate some important data
	data['auto_ol_e'] = $('#auto_ol_e', doc).get(0);
	data['order_direction_e'] = $('#directionDisplay', doc).get(0);
	data['order_level_e'] = $('#ORDER_LEVEL', doc).get(0);
	data['order_limit_e'] = $('#limitDistance', doc).get(0);

	// indicated that the patch has completed
	is_patched = true;
}

// start engine
function start_engine() {
	// start the spread calculating rotine
	data['calculate_spread_interval'] = setInterval(function () {
		var sell = parseFloat(data['sell_e'].innerHTML);
		var buy = parseFloat(data['buy_e'].innerHTML);

		data['spread_e'].innerHTML = buy - sell;
	}, 1000);

	// start the prices collection rotine
	data['calculate_prices_collection_interval'] = setInterval(function () {
		var sell = parseFloat(data['sell_e'].innerHTML);
		var buy = parseFloat(data['buy_e'].innerHTML);

		var price = sell + ((buy - sell)/2);

		data['prices'].push(price);
		data['times'] += 1;
	}, 1000);

	// start the f/s/d/s/l calculating rotine
	data['calculate_fsdsl_interval'] = setInterval(function () {
		var fs = [];
		var lfs = undefined;

		for (var i = 0; i < data['prices'].length; i++) {
			if (lfs == undefined) {
				lfs = data['prices'][i];
			} else {
				var dif = lfs - data['prices'][i];

				if (dif < 0) {
					dif = -1 * dif
				}

				fs.push(dif);
			}
		}

		var price_difference = data['prices'][0] - data['prices'][data['prices'].length - 1];
		var price_speed = price_difference/data['times'];
		var price_flunctuation = fs.average();

		if (price_speed < 0) {
			price_speed = -1 * price_speed;
		}

		data['flunctuation_e'].innerHTML = price_flunctuation.toFixed(2);
		data['speed_e'].innerHTML = price_speed.toFixed(2);

		if (price_difference < 0) {
			data['direction_e'].innerHTML = 'Up';
		} else if (price_difference > 0) {
			data['direction_e'].innerHTML = 'Down';
		} else {
			data['direction_e'].innerHTML = 'n/a';
		}

		var spread = parseInt(data['spread_e'].innerHTML);
		data['spread_in_e'].innerHTML = (spread/price_speed).toFixed(2);

		if (data['order_limit_e'].value.replace(/^\s+|\s+$/g, '') != '') {
			data['limit_in_e'].innerHTML = ((parseInt(data['order_limit_e'].value) + spread)/price_speed).toFixed(2);
		} else {
			data['limit_in_e'].innerHTML = 'n/a';
		}
	}, 10000);

	// start the order level calculating rotine
	data['calculate_order_level_interval'] = setInterval(function () {
		if (data['auto_ol_e'].value.replace(/^\s+|\s+$/g, '') != '') {
			try {
				var auto_ol = parseInt(data['auto_ol_e'].value);
			} catch(e) {
				var auto_ol = 0;
			}

			if (data['order_direction_e'].value == 'Sell') {
				var sell = parseFloat(data['sell_e'].innerHTML);
				data['order_level_e'].value = sell - auto_ol;
			} else if (data['order_direction_e'].value == 'Buy') {
				var buy = parseFloat(data['buy_e'].innerHTML);
				data['order_level_e'].value = buy + auto_ol;
			}
		}
	}, 1000);

	// indicate that the engine has started
	is_engine_started = true;
}

// stop engine
function stop_engine() {
	// stop the spread calculating rotine
	clearInterval(data['calculate_spread_interval']);

	// stop the prices collection rotine
	clearInterval(data['calculate_prices_collection_interval']);
	data['prices'] = [];
	data['times'] = 0;

	// stop the f/s/d/s/l calculating rotine
	clearInterval(data['calculate_fsdsl_interval']);

	// stop the order level calculating rotine
	clearInterval(data['calculate_order_level_interval']);

	// indicate that the engine is stopped
	is_engine_started = false;
}

// rotine
function rotine() {
	// patch the window
	patch();

	// if the engine is started then stop it...
	if (is_engine_started == true) {
		stop_engine();
	}

	// ...and start it again
	start_engine();
}

// fire when IG inserts ifrBetslip-0 on the page
document.addEventListener("DOMNodeInserted", function (event) {
	// exit if this is not the element we are looking for
	if (event.target.id != 'ifrBetslip-0') {
		return;
	}

	// start the rotine when the ifrBetslip-0 frame loads
	event.target.addEventListener("load", function () {
		// greasemonkey bug here! and this is the workaround.
		setTimeout(function () {
			// if jQuery is not loaded then load it and fire the rotine
			if (is_jquery_loaded == false) {
				load_jquery(rotine);
			} else {
				// otherwise just fire the rotine
				rotine();
			}
		}, 0);
	}, true);
}, true);

As you can see the patch is really bad. I’ve put it together in an agile fashion. But it works!

My final piece of advice is: don’t gamble! Get a feeling of the game and make smart decisions backed up by substantial data. Otherwise, spread betting will be no different than betting on dog races.