Monday, 11 March 2019

Small trading algorithm

zhttps://medium.com/automation-generation/hft-like-trading-algorithm-in-300-lines-of-code-you-can-run-now-983bede4f13a
HFT-like Trading Algorithm in 300 Lines of Code You Can Run Now

To understand the basic concept, we need simplest example of it. This code/post provides it for trading algorithms.

1.
Let's start with the python lessons learnt from this code segments.
Argument parsing: https://docs.python.org/3.7/library/argparse.html
if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument(
        '--symbol', type=str, default='SNAP',
        help='Symbol you want to trade.'
    )
    parser.add_argument(
        '--quantity', type=int, default=500,
        help='Maximum number of shares to hold at once. Minimum 100.'
    )
    parser.add_argument(
        '--key-id', type=str, default=None,
        help='API key ID',
    )
    parser.add_argument(
        '--secret-key', type=str, default=None,
        help='API secret key',
    )
    parser.add_argument(
        '--base-url', type=str, default=None,
        help='set https://paper-api.alpaca.markets if paper trading',
    )
    args = parser.parse_args()
    assert args.quantity >= 100
    run(args)

2.
Coroutines and Tasks: https://docs.python.org/3/library/asyncio-task.html
    # Define our message handling
    @conn.on(r'Q\.' + symbol)
    async def on_quote(conn, channel, data):
        # Quote update received
        quote.update(data)

3.
If statement with ():
        # We've received a trade and might be ready to follow it
        if (
            data.timestamp <= (
                quote.time + pd.Timedelta(np.timedelta64(50, 'ms'))
            )
        ):
            # The trade came too close to the quote update
            # and may have been for the previous level
            return

4.
The Quote class is tracking the market picture for a symbol:
  • Quote.__init__()
  • Quote.reset()
  • Quote.update()
This simple algorithm tracks 1 penny spread only.

5.
The Position class manage the (possible) share counts, you have at any certain time, not exceeded the specified amount, otherwise you can go bankrupt.
  • Position.__init__()
  • Position.update_pending_buy_shares()
  • Position.update_pending_sell_shares()
  • Position.update_filled_amount()
  • Position.remove_pending_order()
  • Position.update_total_shares()

It assume that every order sent with volume(amount) of 100. That is why the remove_pending_order() look like below:
    def remove_pending_order(self, order_id, side):
        old_amount = self.orders_filled_amount[order_id]
        if side == 'buy':
            self.update_pending_buy_shares(old_amount - 100)
        else:
            self.update_pending_sell_shares(old_amount - 100)
        del self.orders_filled_amount[order_id]

6.
There are 3 events from the market:
  • Quote: handled on_quote(), market feeds for orders received/cancelled
  • Trade: handled on_trade(), market feeds for traded orders
  • TradeUpdates: handled on_trade_updates(), market update for my own orders, for many markets this is quicker than above Trade event

7.
On Quote event, it just update the Quote class, to track the market picture.
    # Define our message handling
    @conn.on(r'Q\.' + symbol)
    async def on_quote(conn, channel, data):
        # Quote update received
        quote.update(data)

8.
On Trade event, it try to find opportunities. This is the only event, for this algorithm, to submit/cancel orders.
  • Filter-1: If it is already submit/cancel order, then it will wait until next Quote event
  • Filter-2: If this Trade event is too close to the previous Trade event, like less than 50 ms, then it ignores this Trade event
  • Filter-3: If the Trade event with smaller volume, less than 100, it ignores the event
  • Filter-4: If the Position class saying there's no room for new order, it ignores the event
  • Opportunity-1: If the aggressor of the Trade was buy, and if the market buy volume is bigger than the 1.8 times of the sell volumes, it is opportunity. So join them by buying 100 volumes.
  • Opportunity-2: If the aggressor of the Trade was sell, and if the market sell volume is bigger than the 1.8 times of the buy volumes, it is opportunity. So join them by selling 100 volumes.

9.
So this simple algorithm, make decision based on volume imbalance. Very simple, like if one side volume is bigger than 1.8 time than the other, it is the time to act. Because of this, it doesn't need any valuation algorithms. And also it is not using any other trigger signals, but joining others, when others act.
  • No valuation algorithm
  • Just act on other peoples decision, like fast "me too" strategy

10.
This code, interestingly, imitate immediate order, by sending cancel order following the original order. So if the order sent for the opportunity is not matched, then it will be cancelled right away, by the following cancel order.
                    o = api.submit_order(
                        symbol=symbol, qty='100', side='buy',
                        type='limit', time_in_force='day',
                        limit_price=str(quote.ask)
                    )
                    # Approximate an IOC order by immediately cancelling
                    api.cancel_order(o.id)

11.
On TradeUpdates event, it simply house keeping for:
  • its own orders
  • and Positions

12.
I recommend to visit the post I quoted, and also analyse the code by yourself. It will bring huge upgrade of your knowledge for trading.

13.
Also have a look at the next post (Simple portfolio management).

No comments:

Post a Comment