Live data from Hacker News

C++ patterns for low-latency applications including high-frequency trading

arxiv.org

151–160 of 240 posts

Re: C++ patterns for low-latency applications including high-frequency trading

#151
post #148
post #142

Earlier quoted context omitted.

> obviously good trades Are you able to expand with any examples of this?

If every other exchange is selling $AAPL at $100 and suddenly the top level of one exchange drops to $99, then if you just take out that order you basically gain a free dollar. Do this very fast and have pricing the product accurately and you will print tons of money.

It's not that simple. It could just be that exchange is the first one to drop to 99 but all others will as well.

Re: C++ patterns for low-latency applications including high-frequency trading

#152

Earlier quoted context omitted.

How I might approach it. Interested in feedback from people closer to the space. First, split the load in to simple asset-specific data streams with a front-end FPGA for raw speed. Resist the temptation to actually execute here as the friction is too high for iteration, people, supply chain, etc. Input may be a FIX stream or similar, output is a series of asset-specific binary event streams along low-latency buses, s…

The work I do is all in the single-to-low-double-digit microsecond range to give you an idea of timing constraints. I'm peripheral to HFT as a field though. > First, split the load in to simple asset-specific data streams with a front-end FPGA for raw speed. Resist the temptation to actually execute here as the friction is too high for iteration, people, supply chain, etc. This is largely incorrect, or more generousl…

Doing anything other than the dumb trigger-release logic on FPGA is counter-productive IMHO.

You're already heavily constrained by placement in order to achieve the lowest latency; you can't afford to have logic that's too complicated.

Re: C++ patterns for low-latency applications including high-frequency trading

#153
post #64

Earlier quoted context omitted.

When I did low latency everyone was offloading TCP to dedicated hardware. They would shut down every single process on the server and bind the trading trading app to the CPUs during trading hours to ensure nothing interrupted. Electrons travel slower than light so they would rent server space at the exchange so they had direct access to the exchange network and didn't have to transverse miles of cables to send their…

I’ve worked at a few firms and never heard of an IT budget for f-ups. Sounds like a toxic work environment.

Same. That sounds like a way to make that relationship between front office and back office as toxic and unproductive as possible.

Re: C++ patterns for low-latency applications including high-frequency trading

#154

Fairly trivial base introduction to the subject. In my experience teaching undergrads they mostly get this stuff already. Their CompArch class has taught them the basics of branch prediction, cache coherence, and instruction caches; the trivial elements of performance. I'm somewhat surprised the piece doesn't deal at all with a classic performance killer, false sharing, although it seems mostly concerned with single-…

> Fairly trivial base introduction to the subject.

Might be, but low-latency C++, in spite of being a field on its own, is a desert of information.

The best resources available at the moment on low-latency C++ are a hand full of lectures from C++ conferences which left much to be desired.

Putting aside the temptation to grandstand, this document is an outstanding contribution to the field and perhaps the first authoritative reference on the subject. Vague claims that you can piece together similar info from other courses does not count as a contribution, and helps no one.

Re: C++ patterns for low-latency applications including high-frequency trading

#155
post #69
post #26

Earlier quoted context omitted.

The price movement does indeed overwhelm the spread. Half the time it goes up, half the time down.

>Half the time it goes up, half the time down. This is not true and in fact when I hire quants or developers, I have to spend a surprising amount of time even teaching people with PhD's in statistics that the random nature of the stock market does not mean that it's a coin toss. It's surprising the number of people who should know better think trading is just about being right 51% of the time, or that typically stock…

Sorry about that, I didn't mean exactly half or anything like that.

Still, I don't feel that it's wrong: Even on rereading, my phrasing seems to address GP's misunderstanding in an immediately accessible way. Which is better, a complicated answer that leads to proper understanding (if you understand it) or a simple answer that solves the acute misunderstanding (and leads to a smaller misunderstanding)? Both kinds of answer have merit IMO.

Re: C++ patterns for low-latency applications including high-frequency trading

#156

Earlier quoted context omitted.

It's not easy to get data structures like this right in C++. There are a couple of problems with your implementation of the queue. Memory accesses can be reordered by both the compiler and the CPU, so you should use std::atomic for your producer and consumer positions to get the barriers described in the original LMAX Disruptor paper. In the get method, you're returning a pointer to the element within the queue after…

>> In the get method, you're returning a pointer to the element within the queue after bumping the consumer position (which frees the slot for the producer), so it can get overwritten while the user is accessing it. And then your producer and consumer positions will most likely end up in the same cache line, leading to false sharing. I did not realize this. Thank you so much for pointing this out. I'm going to take a…

Fowler's implementation is written in Java which has a different memory model from C++. To see another example of Java memory model vs a different language, Jon Gjengset ports ConcurrentHashMap to Rust

Re: C++ patterns for low-latency applications including high-frequency trading

#157
post #3

I've got an implementation of a stock exchange that uses the LMAX disruptor pattern in C++ https://github.com/sneilan/stock-exchange And a basic implementation of the LMAX disruptor as a couple C++ files https://github.com/sneilan/lmax-disruptor-tutorial I've been looking to rebuild this in rust however. I reached the point where I implemented my own websocket protocol, authentication system, SSL etc. Then I realized…

Instead of this:

  T *item = &this->shared_mem_region
                 ->entities[this->shared_mem_region->consumer_position];
  this->shared_mem_region->consumer_position++;
  this->shared_mem_region->consumer_position %= this->slots;
you can do this.

  uint64_t mask = slot_count - 1;  // all 1's in binary

  item = &slots[ pos & mask ];

  pos ++;
i.e. you can replace a division / modulo with a bitwise AND, saving a bit of computation. This requires that the size of the ringbuffer is a power of two.

What's more, you get to use sequence numbers over the full range of e.g. uint64_t. Wraparound is automatic. You can easily subtract two sequence numbers, this will work without a problem even accounting for wraparound. And you won't have to deal with stupid problems like having to leave one empty slot in the buffer because you would otherwise not be able to discern a full buffer from an empty one.

Naturally, you'll still want to be careful that the window of "live" sequence numbers never exceeds the size of your ringbuffer "window".

Re: C++ patterns for low-latency applications including high-frequency trading

#158
post #68

Earlier quoted context omitted.

> In function signatures, use const references: foo(const std::shared_ptr &p). This will prevent unnecessary bumps of the refcount. This advice doesn't seem quite right to me, and in my codebases I strictly forbid passing shared_ptr by const reference. If you don't need to share ownership of bar, then you do the following: foo(const bar&); If you do need to share ownership of bar, then you do the following: foo(std::…

> Why do we pass by value when sharing ownership? Because it allows for move semantics, so that you give the caller to option to make a copy, which bumps up the reference count, or to entirely avoid any copy whatsoever, which allows transfering ownership without bumping the reference count. What if the callee sometimes wants to get a reference count and sometimes doesn't? In the latter case, your proposed signature f…

> If you pass

   > foo(shared_ptr const&)
> you incur an extra pointer chase in the callee.

Actually this is usually not the case (assuming of course that caller is holding the original pointer in a shared_ptr which is the use case we were discussing.)

That shared_ptr instance is held either on the stack (with address FP + offset or SP + offset) or inside another object (typically 'this' + offset.) To call foo(const shared_ptr &), the compiler adds the base pointer and offset together, then passes the result of that addition - without dereferencing it.

So as it turns out, you may actually have one fewer pointer chase in the const shared_ptr & case. For example, if foo() is a virtual method and a specific implementation happens to ignore the parameter, neither the caller nor the callee ever dereference the pointer.

The one exception is if you've already resolved the underlying bar& for an unrelated reason in the caller.

I do agree that intrusive_ptr is nice (and we actually have one codebase that uses something very similar.) However shared_ptr has become the standard idiom, and boost can be a hard sell engineering-wise.

Re: C++ patterns for low-latency applications including high-frequency trading

#159
post #114

Earlier quoted context omitted.

As someone who does quant trading professionally and game development as a hobby, they both are performance sensitive, but they emphasize different kinds of performance. Trading is about minimizing latency while video games are about maximizing bandwidth. Video games try to cram as much work as possible within about 16 milliseconds whereas for most trading algorithms 16 milliseconds is too slow to do anything, you wa…

I'm curious how HFT relates to pro audio programming. The timescale is close to gaming (usually It's not hard real-time like you're going to crash your car, but if you miss your deadline it causes an unacceptable and audible glitch. I've always been a bit surprised that Jane Street uses OCaml. I know they've put a lot of attention into the GC, but it still seems fundamentally indeterminate in a way that would make mo…

Audio has a lot of buffering behaviour that you wouldn't generally see in event-reactive HFT. Think of all the plugins that you know of that have non-zero latency, compressors with 'lookahead' etc. There are maybe some similarities where the logic is more complex (loop unrolling, SIMD and so on) but I feel like plugins are generally optimizing for throughput (CPU usage) and quality (oversampling etc) rather than purely latency in most cases.

Re: C++ patterns for low-latency applications including high-frequency trading

#160
post #68

Earlier quoted context omitted.

> In function signatures, use const references: foo(const std::shared_ptr &p). This will prevent unnecessary bumps of the refcount. This advice doesn't seem quite right to me, and in my codebases I strictly forbid passing shared_ptr by const reference. If you don't need to share ownership of bar, then you do the following: foo(const bar&); If you do need to share ownership of bar, then you do the following: foo(std::…

> If you don't need to share ownership of bar, then you do the following: > > foo(const bar&); Exactly! > This advice doesn't seem quite right to me, and in my codebases I strictly forbid passing shared_ptr by const reference There is at least one use case I can think of: the function may copy the shared_ptr, but you want to avoid touching the reference count for the (frequent) case where it doesn't. This is an edge…

> This is an edge case, though, and personally I almost never do it.

My experience is the opposite. It has to do with the coarseness of the objects involved and the amount of inter-object links. We typically have a vast variety of classes. Many of them have shared_ptr members, resulting in rich graphs.

Many methods capture the shared_ptr parameters by copying them inside other objects. However, many methods just want to call a couple methods on the passed-in object, without capturing it. By standardizing on const shared_ptr &, all calls are alike, and callees can change over time (e.g. from not capturing to capturing.)

Post reply on HN