Live data from Hacker News

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

arxiv.org

161–170 of 240 posts

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

#161
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…

Additionally: if you care about nullability semantics within your function, then you write foo(const bar*) and pass in bar_ptr.get(), and of course check that the value is != nullptr before dereferencing it.

Otherwise, I'm inclined to agree -- don't pass around smart pointers unless you're actually expressing ownership semantics. Atomics aren't free, ref-counting isn't free, but sometimes that genuinely is the correct abstraction for what you want to do.

One more point: shared ownership should not be used as a replacement for carefully considering your ownership model.

(For readers who might not be as familiar with ownership in the context of memory management: ownership is the notion that an object's lifetime is constrained to a given context (e.g. a scope or a different object -- for instance, a web server would typically own its listening sockets and any of its modules), and using that to provide guarantees that an object will be live in subcontexts. Exclusive ownership (often, in the form of unique_ptr) tends to make those guarantees easier to reason about, as shared ownership requires that you consider every live owning context in order to reason about when an object is destroyed. Circular reference? Congrats, you've introduced a memory leak; better break the cycle with weak_ptr.)

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

#162
post #142

Earlier quoted context omitted.

"which is 3 orders of magnitude faster than a single frame in a video game." You're right on the money. I worked in HFT for half a decade. Back in the late 2000s you were on the cutting edge if you were writing really good C++ and had overclocked some CPUs to hell and back and then shoved them in a rack in a datacenter in new jersey. "To hell and back" means "they only crash every hour or two" (because while they're…

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

There are really two schools of approach to this.

On one hand you have quantitatively driven strategies that try to predict either a price or direction based on various inputs. Here you’re mostly focused on predictive accuracy, and the challenge is in exiting the trade at the right time. This is where a lot of the speed comes into play (what is your predictive horizon, and can you act fast enough to take advantage of the current market prices?).

The other mode of trading tends to focus on structural mispricing in the market. An easy to understand example is an intermarket arbitrage trade where one market’s buyer or seller crosses prices with the opposite side of the market on another exchange. These events permit a trader to swoop in a capture the delta between the two order prices (provided they can get to both markets in time).

As easy opportunity has dried up (markets have grown more efficient as systems have gotten faster, and parties understanding of the market structure has improved) you see some blending of the two styles (this is where another commenter was talking about mixing a traditionally computed alpha with some hardware solution to generate the order), but both come with different technical challenges and performance requirements.

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

#163
post #148

Earlier quoted context omitted.

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.

Yeah I gather that is the expectation, but if you are the first to execute an order you will sell that order at the old 100 price before it lowers. You are fighting for making an order before the information spreads to the other bots. (Right?!)

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

#165

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-…

> A creeping feeling that causes them to compulsively run the benchmarks through callgrind

I'm happy I don't deal with such things these days, but I feel where the real paranoia always lies is the Heisenberg feeling of not even being able to even trust these things, the sneaky suspicion that the program is doing something different when I'm not measuring it.

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

#166
post #161

Earlier quoted context omitted.

> 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…

Additionally: if you care about nullability semantics within your function, then you write foo(const bar*) and pass in bar_ptr.get(), and of course check that the value is != nullptr before dereferencing it. Otherwise, I'm inclined to agree -- don't pass around smart pointers unless you're actually expressing ownership semantics. Atomics aren't free, ref-counting isn't free, but sometimes that genuinely is the correc…

Let me preface by noting that I don't necessarily disagree with any part of what you wrote. However, there are design patterns that exceed the guardrails you're thinking of, and those are the patterns that benefit the most from shared_ptr.

Typically, they involve fine- to medium-grained objects, particularly those that have dynamic state (meaning by-value copies are not an option.)

An example might be a FlightAware-like system where each plane has a dynamically-updated position:

   class Plane { ... void UpdatePosition(const Pos &); Pos GetPosition() const; };
   using PlanePtr = std::shared_ptr;
   using PlaneVec = std::vector;
   class Updater { ... PlaneVec mPlanes; };
   class View { ... PlaneVec mPlanes; };
Updater routinely calls UpdatePosition(), whereas View only calls const methods on Plane such as GetPosition(). There can be a View for, say, Delta flights and one for United. Let's simplify by assuming that planes are in the sky forever and don't get added or removed.

Destructing Updater doesn't affect Views and vice-versa. Everything is automatically thread-safe as long as the Pos accesses inside each Plane are thread-safe.

The key here is that Plane is fine-grained enough and inconsequential enough for lazy ownership to be ideal.

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

#167
post #161

Earlier quoted context omitted.

Additionally: if you care about nullability semantics within your function, then you write foo(const bar*) and pass in bar_ptr.get(), and of course check that the value is != nullptr before dereferencing it. Otherwise, I'm inclined to agree -- don't pass around smart pointers unless you're actually expressing ownership semantics. Atomics aren't free, ref-counting isn't free, but sometimes that genuinely is the correc…

Let me preface by noting that I don't necessarily disagree with any part of what you wrote. However, there are design patterns that exceed the guardrails you're thinking of, and those are the patterns that benefit the most from shared_ptr. Typically, they involve fine- to medium-grained objects, particularly those that have dynamic state (meaning by-value copies are not an option.) An example might be a FlightAware-l…

> Let's simplify by assuming that planes are in the sky forever and don't get added or removed.

If planes are around forever, wouldn't you be better off interning them? e.g. having a single global std::vector (or std::array) and passing around offsets in that array? And your PlaneVec would just be a glorified std::vector (or int)? I don't see any value in maintaining a reference count if you're never intending to clean up these objects.

(The argument for using int here would be if you always have fewer than 2 billion planes, and so you can store a PlaneVec in less space. size_t is indistinguishable from Plane* in this context; you have the same amount of indirections either way.)

As I said, shared ownership has its uses, but most instances I've seen could have been replaced with a different model and would have been less painful to debug memory leaks and use-after-free.

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

#168

Earlier quoted context omitted.

I briefly looked over your stock exchange code: - For memory management, consider switching to std::shared_ptr. It won't slow anything down and will put that concern to rest entirely. - For sockets, there are FOSS libraries that will outperform your code and save you a ton of headaches dealing with caveats and annoyances. For example, your looping through FD_ISSET is slower than e.g. epoll or kqueue. - For dependenci…

I did not know std::shared_ptr would not slow things down. I've learned something new today! :) Yes, I agree, epoll is a lot better than FD_ISSET. Maybe I can keep moving with my C++ code but do people still trust C++ projects anymore? My ideal use case is a hobbyist who wants a toy stock exchange to run directly in AWS. I felt that C++ has a lot of bad publicity and if I want anyone to trust/try my code I would have…

C++ might have a bad reputation, but in many fields the only alternative, in terms of ecosystem, tooling and tribal knowledge is C.

Between those two, I rather pick the "Typescript for C" one.

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

#169
post #76
post #66

Earlier quoted context omitted.

I worked in trading and we did the first one, in C++. We'd load all the instruments (stocks etc.) on startup to preallocate the "universe", and use ring buffers as queues. Instruments don't change during trading hours so restarting daily to pick up the new data is enough. I saw a Java team do the second one in an order router (a system that connects to various exchanges and routes+translates orders for each exchange'…

I honestly don't know why the real time trading devs don't make their own OS/programming language for this. It's not like they don't have the money.

That is basically what they do when deploying into FPGAs.

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

#170
post #35
post #34

Earlier quoted context omitted.

You say it's easier in Rust, but you still have a complete C++ implementation and not a Rust one. :)

Linus said he wouldn't start Linux if Unix was ready at that time.

Minix....
Post reply on HN