A few weeks ago I began to look at Dexbot and read all I could about it, including the code (written in Python) which can be found on Github. I joined a few of the discussion groups on Telegram and found the Cabinet (responsible for developing Dexbot) a pleasant and intelligent crew. Some of the participants on the forums (as is entirely usual in the trading world) were less so.
What was lacking in Dexbot was any attempt to back test trading strategies, which in my view is a grave mistake. I believe I may have persuaded the Cabinet of the advisability of providing at least a rudimentary back testing harness. It is difficult to conceive of any trading at a quant based hedge fund or investment bank proceeding in the absence of back testing any given strategy on historical data.
Which is not to say that back testing is predictive of future performance. The main difficulties are that it is all too easy to torture the parameters to fit the data and that in any event market behavior changes all the time.
The main difficulty is that historical order book data does not appear to be easily available on the Bitshares blockchain. Not without intimate knowledge of Bitshares in any event. I decided that I would have to make do with plain old OHLC data, and daily at that. Minutely data is prohibitively expensive unless you collect it yourself and I had neither the time nor patience. In any event, many claim that market data is fractal, in which case the timescale is irrelevant.
It is an imperfect compromise but nonetheless I believe that my efforts have produced valuable insights.
The Staggered Orders strategy:
“Places a large amount of buy and sell orders at certain intervals, covering the orderbook from very low prices to very high prices.” If the concept is new to you, you will much discussion of such “grid trading” strategies on the various forex trading websites.
“The range is supposed to cover all conceivable prices for as long as the user intends to run the strategy. That could be from -100x to +100x (-99% to +10000%).”
“Profits will be made from price movements, and the strategy introduces friction to such movements. It gives markets depth, and makes them look better. It never “sells at a loss”, but always at a profit.”
Spread: Spread between your highest bid and lowest ask (%)
Increment: At which increments will orders be placed (%)
Lower bound: How low do you expect the price to ever go? Buy orders will be placed from center price down to this price. Price of Quote asset as measured in Base asset.
Upper bound: How high do you expect price to ever reach? Sell orders will be placed from center price up to this price. Price of Quote asset as measured in Base asset.
There are various further wrinkles and options, but the essentials are as above.
As far as Bitshares and Dexbot are concerned, the object is to make rather than take liquidity. Dexbot was funded with the object of increasing liquidity in the various Bitshares crypto currency pairs and (hopefully) for users to profit in so doing.
In backtesting I had to ignore whether liquidity was taken or made.
With the luxury of hindsight I created two lists of orders: buys and sells and chose a range to accord with the range of my historic data. I began with the buys and created 500 buy orders, logarithmically spaced, between the low and high of my range. I then created a list of 500 sell orders such that each sell order was priced 1% above each buy order. This seemed fair enough for a system which is designed to make liquidity. A 1% spread, while generous in conventional markets, might not be so out of place in an illiquid crypto market.
I assumed an equal float of both the base and quote currency to kick off the test. Dexbot is aimed at cash or spot markets: not forward or futures markets. Therefore you can only buy the quote currency if you have sufficient base currency. And you can only sell quote currency which you actually own. Although I believe that there are or will be various margin arrangements if you wish to use leverage. I chose to ignore leverage and shorting. As does the literature and instructions for the Staggered Orders strategy.
I used fixed fractional position sizing. I chose a bet size of 1% of the base currency for both purchases and sales of the quote currency. In other words each trade is sized at 1% of the then existing value of the base currency as adjusted daily for profits and losses.
For the purposes of this article I used daily OHLC data for USD/CHF for the period 2nd October 2001 to 17th August 2019. During this time the USD declined consistently against CHF from 1.6174 to 0.9779. Just for fun and because of the argument that Staggered Orders works best in a mean reversion environment, I doubled up on the data by reversing it and adding the reversed series to the end of the real data series.
Having set up the orders grid I then looped day by day through the daily data. Using python list comprehension, I added python lists of orders each day to Buy and Sell columns. The list comprehension compared the entire list of 500 buy and 500 sell orders each day to the high and low recorded in the data. If any of those orders fell between the low and high of the day, they were added to be executed on that day during the back test.
I have no doubt there are more elegant and less computationally expensive ways of achieving the same thing but the method sufficed for my purposes.
I then looped through the daily data once again to execute the orders and log the resulting equity curve. Day by day the base and quote currencies were increased or diminished by the orders executed. At the end of each day the equity curve valuation was based on the closing price to arrive at the account value in terms of the base currency. In other words the holding of the base currency was added to the quote currency converted by the then current exchange rate back into the base.
Taking the real data first (USD/CHF to 2019) there were 20,000 round trips. No account was taken of slippage or charges (you will need to look up the charges for yourselves, but needless to say they will have an impact on the results).
Unfortunately the results for this first test were not hugely encouraging. They roughly matched buy and hold. If allowance had been made for slippage and costs, the back test would have under performed buy and hold. More so if interest for the period on the base and quote currencies had been added into the buy and hold figures. No doubt much liquidity would have been created but I am a little challenged to see how the trader would have profited.
The second test (the extended series) saw CHF against USD back to the starting point: it begins and ends with USD 1 = CHF 1.6174. Unfortunately the system, as I had expected, found itself below its starting price. You can see a chart of the system equity below and again above in the header image for this post.
An earlier and cruder attempt at back testing had come to much the same conclusion. In the earlier attempt I simply assumed selling each day if the price rose, and buying each day if the price fell. I had not bothered with a grid and assumed that a grid would add some magic I had missed. It did not but perhaps I am still missing some vital assumption or key parameter?
I very much hope that I have got my coding and or my analysis wrong. Perhaps by now one or other members of the Cabinet will have found a better solution than mine which will prove me wrong.
I tried other parameters and other price series none of which made the situation any more cheerful.
At some stage I will make my code available so that others may discover whether my attempt is reasonable or whether I have made glaring errors and erroneous conclusions.