All posts by Trader247

August 2025

First up – FastTrade.

This was intended to be trading for a few weeks until my new bot – Volt – was up and running, more on that below, but for now it’s still my non-stop bot.

The data is thin on this one due to my minimal code. From the results, I get a good idea of what’s going on. There’s a gap in my model. I’ve always noticed this gap. It is between what I watch and predict some trading code will do, and what it actually does once running.

My data suggests I should be able to trade in around 40% of the markets I monitor. When I open the parameters to do this, I see more losses. When I tighten the parameters, there’s fewer trades and more profits, and I’m only entering around 15% of markets. But I’m struggling to see the difference between some of the made trades and some of missed ones. However, my data isn’t robust enough to be conclusive, so I might be way off with my thoughts.

Here’s a chart from the past few weeks, the orange line indicates the point where my last significant change was made. Results after this are more positive and steady.

As for Volt, it’s a work in progress. I run it live for a few markets on my laptop, not the VPS, review the results and run again. This allows me to gather more data and then continue with the building process.

My idea with this bot was to find the trades as I built the code, and I am getting somewhere, but it’s not giving me the activity I want quick enough, and I’m starting to think of other possible angles.

My most successful trading has come from statistical models with a bit of technical analysis thrown in, trading the moves in the markets around various indicators. My ventures into fundamental trading, looking at runner and track stats, has never really produced anything. I have enjoyed writing the data gathering code for this analysis and think there’s probably some value in what I’ve got, but I haven’t found it yet.

Still, happy botting everyone!

Excel VBA timer

I use the Timer function in VBA to help identify any problems in my code. After the code is triggered, I set startTime = Timer. My code’s first job is to lift the data that is imported from Gruss to Excel into an array. Once the bot has done its thing, the array is pasted back into excel with any triggers/new data added (so my code only does 1x excel read and 1x excel write). The last thing it does before the paste instruction is to add the time taken to run the code to an unused cell in the array area – stopTime = Timer – startTime. This is just to provide a visual indication while I’m watching, to see what’s happening and if there’s any blockages.

Since I’ve recoded, the time reported is rarely anything other than zero. But zero what? What is zero? What is the resolution of the built in Timer function? I initially read that the Timer counted in 1/64ths of a second. Which is 0.015625 seconds. This made sense, as this is a number I’ve seen flash up on the sheet as the bot was running (I actually saw 0.0156 due to cell format). It would normally bounce between this and zero.

This means my code is running, normally, in less than 0.02 seconds. The shortest interval between price update I’ve observed on Gruss, when on full stream, is around 0.1 seconds (up to 10 refreshes per second). So I know my code is running fast enough not to be missing any data refreshes. That’s good.

An interesting observation from the VPS – I’ve seen, though not often, the reported time as 0.0078. this looks like half of 1/64th, and it is. 1/128th seconds is 0.0078125.

My conclusion is that the Timer function is actually linked to the hardware and/or  the operating system, and therefore may differ depending on your set up.

Enhance Your System Time Sync

If my computer’s time drifts a little, it’s no major issue. But if it becomes a few seconds out of line it can have an impact on trading, especially greening up. For me the greening period is important as trading can be quite frantic in the last minute or so before the off. And having a balanced book is part of my plan.

You can set Windows to keep time synced but this doesn’t appear to be very good in my experience. I’ve used an app to regularly keep my operating system time accurately synced. As mentioned in this post I’ve previously used Dimension4 by Thinking Man Software. However, after some googles I decided to try something different.

I’ve installed Meinberg NTP (see here) on my VPS. It works as soon as it’s installed but inreasing NTP pools is recommended, so I added a few extra European pools. There’s also a really useful guide here to help get started, but it really is straight forward to set up. It also seems light enough with no impact on processor when running. Both time.is and Gruss refresh chart show near perfect times, and I’ve not seen any drift, so all good.

Building a Better Trading Bot: Lessons from FastTrade to VOLT

The bot that’s running on the VPS, which is testing my ability to stomach losses, I’ve called FastTrade (now at version 1.3). This is the bare-bones design, and “works” perfectly. I was happy with where I’d got it to but didn’t want to be slowed by years old coding decisions. It’s mad how you can have code that works well, but then when you spend a bit of time actually thinking about it, you come up with another solution. Then you think some more, and change it again.

One example of this was my logging code. In OSCAR (a retired bot) I’d written code for each logging event – this wrote a line to my logging sheet individually as they were called, including an xlUp line (obviously this brings into question how serious I am about efficiency). It did work, though writing to the sheet on each log event is not in line with my quest for speed. So, one goal I had was to store log events to an array and paste the array at market change, where trading isn’t happening so speed is less important. I moved all the logging code to it’s own module, added an array, and put the repeated code in it’s own sub. Each logging event, still having its own sub, added a string element such as “Trading Period Started” and called the sub that added all the other detail at that exact moment. I then refined this further by adding a string argument to the sub for the name of the logging event. My logging code has gone from multiple subs to just 2 – the log-event-with-argument sub, and the log-log-to-log-sheet sub. Splendid.

I’ve been building my new bot from the ground up, including the log stuff. I’m calling this one VOLT (VOLume Trader). I’d tested manually and got to a point where live testing was possible. An important element here – always have a boolean argument that allows trading. Set to false and the entire code can run but the bit that writes the trigger/order is bypassed. Once you’re happy to trade, change it to true. Although this isn’t an emergency stop, it does allow for testing and quick interventions if things aren’t quite right but you want monitoring to continue.

Well I’d tested and tested, debugged and tested, debugged etc. Literally as soon as I went live it failed – subscript out of range. I actually like this fault finding part, it’s solving the puzzle that I enjoy. Fortunately for me it was one puzzle after another. Not checking for previous data entries, so writing the same thing over and over, exceeding the array size. Then not incrementing after adding data, so missing loads of log events. Checking for the wrong cased words (Closing vs CLOSING). The list is long but my time is short (a bit dramatic).

I’ve written this post whilst watching VOLT run through Sunday evening US horse markets on another screen and all is now well. Logging is working, as is market navigation, where I appear to have got rid of the double quick-pick-list refresh that caused no problem but I found mildly irritating.. The trigger code is there but not active yet. Next step is to add some temporary what-if logging, see how it fares. Happy trading, botters.

Some changes

First a small clarification. I said in the last post that when the currently active bot is triggered, it takes the Back offer. However, it doesn’t do that. It places a back bet at 1 tick lower than the best Lay price. In a tight market that is most likely going to take the Back offer as there won’t be any gap in the spread, but I thought I should be clear. It also explains why I don’t always get matched and the fill or kill element is in the order.

Admin news – I’ve stumped up for a personal WordPress plan to get rid of the banner and ads that came with viewing my blog which should make your experience more pleasurable, or less painful. It also came with a new domain name which looks neat, and my view count has jumped, bonus. There may be some decorative changes to come too, if I get on to it.

Admin news extra – Twitter/X has descended to such a low that WordPress no longer connects to it. But it does connect to Bluesky, so I’ve joined that small village scale community…

To the code – it’s always worth going back to basics. Thinking about it, there are elements of my code that I’ve reused without thinking, as they just work. To that end, in all my time botting and with all the different bots I’ve tried, I’ve always used the same worksheet change event code trigger in Sheet1, which is basically this –

Public Sub Worksheet_Change(ByVal Target As Range)

If Target.Columns.Count <> 16 Then Exit Sub

Application.EnableEvents = False

With Workbooks("MyBot.xlsm").Sheets(Target.Worksheet.Name)

MyCode 'this is in module1

End With

Application.EnableEvents = True

End Sub

Well it turns out that the With reference part – “.Sheets(Target.Worksheets.Name)” is a general reference and uses a look-up to get to the specific sheet from within the Sheets collection. However, for my bot it’s always going to be the same sheet so the reference can be changed to “.Worksheets(“Sheet1″)”. This is apparently better form as specific references should be used where possible. By not looking-up, there should also be a speed advantage (possibly multiple microseconds if I’m lucky). Remember, small wins are still wins. Anyhoo, it now looks like this –

Public Sub Worksheet_Change(ByVal Target As Range)

If Target.Columns.Count <> 16 Then Exit Sub

Application.EnableEvents = False

With Workbooks("MyBot.xlsm").Worksheets("Sheet1")

MyCode 'this is in module1

End With

Application.EnableEvents = True

End Sub

Finally – a shout out to Toby at Punter2Pro who featured my blog in an article last year. I’ve had a few click-throughs, so thanks.

A little P&L, mostly L

The bot has run flawlessly so far, as in it hasn’t failed to navigate the markets or missed any tick offset or stop loss. I do have a chasing stop loss set, and that’s been activated a few times but worked as it should.

The trade/order I’m placing with this set up when triggered is – Take Back offer, fill or kill 3s, 1 tick offset, 2 tick stop loss, 0.5s chasing stop loss, all set for levelling. I’ve made a few adjustments to the trigger to find the limit of effects of different parameters.

In cleaning the code I have got rid of an annoying problem where I’d get an error every time I linked the sheet to Gruss. I’d select any blank cell on the sheet, press Del, and then F5 the code and away it would go, no further problem. It was the reference to the spreadsheet that was incorrect, I’d not included the file extension .xlsm, which makes me wonder how it ever worked. But it did and now it does better.

Here’s a chart. This is for most of March and April up to today. Stakes are around £2. The early sharper drop was done with quite open settings. After reigning it in the losses slowed and remained relatively consistent.

My new bot is coming on with improved logging and some better tracking going on. I’m hoping to have it on the VPS in May for testing. The volume tracking is working but I think I can improve it, specifically the amount of data it holds, which I think can be reduced, but doing that efficiently without slowing the whole thing is my goal.

Setting up a VPS in 2025

Setting up my VPS recently was similar to my previous experience, but there’s a few options out there so I thought I’d tell you about what I did and the spec I’ve gone for.

My previous set ups – Tagadab 2016 which I updated to cloud, and followed up with Another VPS update

When I opened my new VPS for 2025 I went for the basic VPS1 Windows package from Hostworld – 1x vCPU 1gd ram. I was immediately underwhelmed. Everything was slow. Response of mouse and clicks was delayed, opening apps took a while. I was surprised as previously I found the performance of this spec level ok. The difference may be due to the Hostworld server set up, the Windows Server version, or something else. Maybe at this point I should have tried another provider.

Anyway, I upgraded to the 4x virtual core 4gb ram VPS3 option, which works a treat and should be good for multiple bots. It feels as fast as my laptop OS. The price of this is similar to Simply Cloud’s 2x vCPU 4gb option which  may have been suitable, but I haven’t tried it so I don’t know.

After sign up, you get a link to connect to the VPS and it creates a desktop shortcut. Once the connection is made the VPS appears like a standard desktop, which you navigate in the usual way. If you have the new Windows App on your phone (it used to be called Remote Desktop) and open the email link sent to you at sign up, you can connect directly to the VPS. The window that opens is a standard desktop. Swiping moves the cursor, tapping to left click.

Back on the laptop and logged into the VPS, downloading Gruss was simple – open browser, go to Gruss Betting Assistant download, and download. Then open and sign in. For info – you can be signed in on the VPS and your home computer at the same time.

A note here regarding the Windows Server option. I initially chose 2019, but Gruss wouldn’t open due to requiring a specific .NET version. I think it was v4.1 or higher. Although all my Google searches said upgrading Server 2019 was possible, I struggled to do so. I reset my VPS with server 2022 OS and it worked fine. Again, this may be to do with Hostworld’s settings, I don’t know. It works now though.

Getting excel was also easy, although slightly unbelievable. I searched for an Office key, and purchased one for 84p – yes £0.84 – for office 2019! Which is perfectly fine. At that price it doesn’t matter that it’s a one use key. Activating required using the phone method, entering the product key and a very long verification code. Nothing difficult though.

Getting my excel file to the VPS was done by copy/paste from my laptop file explorer to VPS file explorer.

If you haven’t set up a VPS and are thinking about it, I’d say give it a go, it really is doable. And once it’s up and running, there’s very little maintenance needed. I had a couple of updates to do in the first 24 hours and nothing else since. Do remember to change automatic updates to ask before doing, you don’t want the VPS restarting mid trade.

A brief catch-up

After posting in July 2020 I continued the bot for a few more months, then switched it off and closed the VPS due to not spending any time on it. I had plenty of ideas I still wanted to try but other things took priority.

Apparently nothing in 2021, that I can remember.

Throughout 2022 I worked on a strategy based around Australian harness racing. This was mainly due to the availability of race data from harness.org.au, and my ability to collect and work on it. I built my VBA code initially to scrape the previous 18 months of data, and then altered it to run on a daily basis, adding all the new daily data and working backwards to calculate my own ratings, then picking some value bets. It was at this point that the obvious fact I’d missed became apparent – relatively few harness races are available on Betfair. Top tip – always research ideas thoroughly before acting.

Late 2023 and into early 24 I modified a bot to trade on the dogs following prices movements over time. The code was a bit messy and took a while to refine to something that worked. The main problem was once I’d got the code working, I struggled with finding an effective trigger. I thought there was something not quite right and I was planning on moving that to a VPS, but it slipped away for a while.

Then after this last Christmas holiday and into 2025, I picked it up again. Almost immediately spotting an absolute wanger of an error in the code, meaning my price tracking was out of sync with the market. It’s so obvious now as to be almost embarrassing. Simply, each refresh added selected price/volume data to an array and, to keep things trim, deleted entries deemed old. New data was added to the next available slot, but as refreshed are not at set intervals, new data was being dropped in almost randomly. Anyway, I popped that to one side and focused on getting back to basics. With bloated code and masses of notes, I wanted to refine my old favourite bot to be as minimal, or as fast, as possible. The code is now down to the bare minimum, with a clear flow and separated market navigation and trading. So far this approach hasn’t failed. That’s where I got to before starting a VPS and trading from there.

With the bot running ok, although not producing much, it’s the solid base I wanted to get to for my new developments which continue on and off.

VPS Latency

I’ve set up a new VPS with Hostworld and have made a short video showing the difference in latency between my laptop and the VPS. This was a UK greyhound race a minute or two before the start. The number in the green arrow tracing the green line is the latency. the blue line shows the refresh. The VPS is chart is to the rear, with the green marker at the bottom left. It is generally a lower latency, but also more consistent than the laptop.

Fancy a date?

Oh, Excel! You fry my brain, you buggy git.

If you want to slow down VBA code then you add loads of sheet read and writes. Equally, to speed things up, you get rid of the read writes. I’ve been scraping data from the web and for each worksheet row of data, I could find an individual element and write it to a cell, then move to the next and repeat to complete the row. This is vastly speeded up by collecting data in to an array and then pasting it in one go, neat little trick.

This appeared to be working fine until I noticed a date value not pulling through consistently. I was holding the date, as read from a webpage, in the format ddmmyy. However, upon writing to the worksheet it altered to mmddyy, but only when the day value was less than 10. Once the day was 10 or greater, the value appeared in the correct format.

I tried to solve this. I tried setting the format of the cell in the worksheet before writing – didn’t work. I tried formatting within the array – didn’t work. Even though it appears correct in the array when viewing in the locals window of VBA, the problem persisted. I checked the regional settings on my computer – all ok. The array holds lots of values of differing data types and all save and paste ok except this. I even tried pulling the day, month and year value separately and then combine prior to writing – no better.

Searched the web and found some reference to the problem but no solutions.

I eventually solved this though through just guessing. I changed the format of the saved date value within the array to yymmdd. Now, when the array is pasted to the sheet, the regional settings display the value in the correct format, ddmmyy. Seems a ridiculous bug and I’ve no idea why VBA would do this only with certain dates and not throw an error. It’s like it’s guessing at autocorrecting but I can’t turn it off.