• C++ Programming for Financial Engineering
    Highly recommended by thousands of MFE students. Covers essential C++ topics with applications to financial engineering. Learn more Join!
    Python for Finance with Intro to Data Science
    Gain practical understanding of Python to read, understand, and write professional Python code for your first day on the job. Learn more Join!
    An Intuition-Based Options Primer for FE
    Ideal for entry level positions interviews and graduate studies, specializing in options trading arbitrage and options valuation models. Learn more Join!

C++ vs Rust

Quizzie

1. channels can be found in C++ Actor libraries

Asynchronous Agents Library

2. C++ Concepts are a superset of Rust traits.

3. Would you write a CAD library or Black Scholes option pricer in Rust?

4. How does Rust generics compare to C# generics or C++ templates? The follow-on question: what kinds of applications is it suitable for?

5. Is Rust Turing complete?
 
Option 2 of my poll has to do with actors (and an actor is not an object)

1, Actors use message passing ==> no shared data => problem solved?

2. Null references ==> non-issue in typical producer-consumer applications??

So, objects were NOT built for concurrent access, but actors are.

Problem solved??


From Wiki
"The actor model in computer science is a mathematical model of concurrent computation that treats an actor as the basic building block of concurrent computation. In response to a message it receives, an actor can: make local decisions, create more actors, send more messages, and determine how to respond to the next message received. Actors may modify their own private state, but can only affect each other indirectly through messaging (removing the need for lock-based synchronization)."
 
SUMMARY OF POLL ON RUST
poll.jpg
 
Some years ago when the giant dinos Corba and COM roamed the great plains and shared the spoils, then a clever chap had the brainwave of creating a common interface to them, meaning only 1 interface to learn.

Due to heavy leaking they had to learn THREE interfaces! Go tell management.

Rust and C++ share a common language/heritage; I suspect you will need to be good at C++ for Rust-C++ interop.

I could be wrong.
 
Last edited:
You read my mind! Got a few things in the oven:

I) High-Performance Numerical Computations:
Use Case: Developing and backtesting complex quantitative trading strategies.
Python Role: Python can be used for the rapid development and testing of trading strategies using libraries such as pandas, NumPy, and backtesting.py.
Rust Role: Rust can be used to optimize the performance of computationally intensive parts of the strategy, such as custom technical analysis indicators or simulations, ensuring faster execution and lower latency. (C/C++ has a clear monopoly on this now in banking space, some Java/Scala out there too)

II) Risk Management Systems:
Use Case: Real-time risk assessment and management for large portfolios.
Python Role: Python can be used to design the risk management framework, perform data analysis, and integrate various risk models.
Rust Role: Rust can be used to handle real-time data processing and high-frequency calculations, ensuring the system is robust and performs well under load.
(C++ winner here as well, but 'data science' aspects of this python has a clear advantage/edge.)

III) Pricing and Valuation Models:
Use Case: Implementing complex derivative pricing models.
Python Role: Python can be used to prototype and validate pricing models using libraries such as SciPy and QuantLib.
Rust Role: Rust can be used to implement the computationally intensive parts of these models, such as Monte Carlo simulations or partial differential equation solvers, to achieve higher performance and reliability.
(C++ Winner again, would love some feedback from our PDE expert @Daniel Duffy if Rust even can hold a candle to C++ for this.)

IV) Market Data Processing:
Use Case: Real-time processing and analysis of market data feeds.
Python Role: Python can be used to manage the data ingestion pipeline, initial preprocessing, and feature extraction.
Rust Role: Rust can be used to implement the core data processing engine to handle high-throughput data streams, ensuring low latency and efficient memory usage.
(Here C/C++ is most common from what I've seen. Rust might have most application here as of right now from my thinking. Pure systems dev use case)

V) Algorithmic Trading Infrastructure:
Use Case: Building and deploying algorithmic trading systems.
Python Role: Python can be used for strategy development, parameter tuning, and integrating with various data sources and trading platforms.
Rust Role: Rust can be used to develop the execution engine, ensuring low-latency order execution, robust error handling, and secure memory management to prevent crashes and undefined behavior during live trading.

(Similar comment to above. Most C like/systems infra dev here)


Would love to get input from @achirikhin @APalley @Daniel Duffy on this as well. I'll admit I'm quite biased toward C/C++ myself.
 
Sorry about that sir I missed this! I can only speak for our team specifically about the why, although I imagine every team is always looking to do things in a way that is easier to maintain. Any team that has to process large amounts of data and/or has a somewhat complex schema for their data are looking into Rust. For right now it seems to be used mostly for infrastructure type dev work more so than calculations to risk or price derivatives. Code that is legacy in C/C++ or Java are prime candidates for this migration. While I am specifically in a fixed income derivatives team, I know it is being looked at by commodities, foreign exchange (FX), and equity teams as well with similar future concerns when it comes to their data.

For us we have lots of data that we need to slice through in json and xml formats from various external and internal sources that aren't from our own team for financial data. The current code that is in C or Java is extremely hard to maintain, if not outright just bad with memory leaks that require jobs to be restarted every other day type thing. While I would personally love to hire a couple skilled C guys to clean up these processes/jobs, the kids coming out of school just aren't trained to think like the C programmers of old. Even when I went to school in the early 2000s, we were taught Java and not C. The memory management and concurrency use cases they have in Rust with their ownership model make things substantially better once we get things to compile properly.

We also have the added excitement/pressure of needing a language that is interoperable with Python, as this is a language that is almost sure to come up somewhere in the development cycle for one of the layers. It is why we are eagerly awaiting Professor Duffy's book as well. :)

Happy to answer any specific questions if I can!
I happened to have quite a bit of interop exp lol. I would recommend an “abstract” streaming interface to begin with otherwise you would have nightmare establishing the API. Once having that, you can export the interface to other languages. For Python we use Pybind11. For C# we do p/Invoke.
 
It is NOT an object oriented language. Neither it is functional…. It is a “stack prudent” style new type of coding linguistics that I really find it weird…
That's good summary.

My hunch is that inside Rust there is an actor model trying to get out. But I fear that this is not good for producer-consumer pipeline programs.
I have experimemented with Actors in C++, C# and Python. All nice stuff. Couldn't find much in Rust.

 
Last edited:
The following code uses features from C++20 and QN Adv C++ course that uses Concepts
// At this moment I have a vague idea on how I would do it in Rust; is it even worth the effort..

@APalley
@MichaelLewis

C++:
// Test101ActorConcepts.cpp
//
// Simplest example of a system. Context diagram consists only
// of Input and Output systems.
//
// We use C++20 Concepts to replace V1 (outdated) policy-based design (PBD)
//
// Composition
//
//  V2: design using C++20 Concepts
//  V3: V2 + embedded actors
//
// Problem now becomes a pipeline Source -> SUD -> Sink
//
// Summary: this approach is feasible, so worth investigating..
// 1992 .. everything is an object
// 2024 .. everything is an actor
//
// (C) Datasim Education BV 2015-2024
//

#include <string>
#include <iostream>
#include <type_traits>
#include <mutex>
#include <agents.h>

// Interface contract specification
// 1. Each concept is an abstract method
// 2. interface == concept conjunction (e.g. ISUD)

// 1. Define abstract methods (building blocks)
template<typename Message, template <typename Message> class T> // abstract method
    concept ISource = requires (T<Message> x) { x.message(); };
template<typename Message, template <typename Message> class T> // abstract method
    concept ISink = requires (T<Message> x, const Message& s) { x.print(s); };

// 2. Interface relating to context diagram; cojunctional concepts are an alternative
// to multiple inheritance but no danger of fragile base class problem
template< typename Message, template<typename Message> class Input, template<typename Message> class Output>
    concept ISUD = ISource<Message, Input> && ISink<Message, Output>;

// Agent actor-based constraints
template<typename Derived>
    concept IActor1 = std::derived_from<Derived, concurrency::agent>;
template<typename T>
    concept IActor2 = requires (T x) { x.run(); };

// 3. Interface relating to defining constraints pertaining to Actor technology
// https://learn.microsoft.com/en-us/cpp/parallel/concrt/asynchronous-agents-library?view=msvc-170
template<typename Derived>
    concept IActor = IActor1<Derived> && IActor2<Derived>;
   
// The mediator using template template parameter "trick" => can use generic messages
template <typename Message, template <typename Message> class Source, template <typename Message> class Sink>
                requires ISUD<Message, Source, Sink> &&  IActor<Source<Message>> && IActor<Sink<Message>>
    class SUD : public concurrency::agent
{ // SUD is in a chain from Input to Output

private:
    concurrency::ISource<Message>& _source; // formerly known as src
    concurrency::ITarget<Message>& _target; // formerly known as snk
public:
    explicit SUD(concurrency::ISource<Message>& source, concurrency::ITarget<Message>& target)
        : _source(source), _target(target) {}

    void run()
    {
        Message info = concurrency::receive(_source);
        concurrency::send(_target, info);

        done();
    }
};    

// Instance Systems
template <typename Message>
    class MySource : public concurrency::agent
{
    concurrency::ITarget<Message>& _target; // send to SUD
    Message _msg;
public:
    explicit MySource(const Message& msg, concurrency::ITarget<Message>& target)
        : _target(target), _msg(msg) {}

    Message message() const
    {
        // Get data from hardware device
        return Message(_msg);
    }

    void run()
    {
        concurrency::send(_target, message());

        done();
    }

};

template <typename Message>
    class MySink : public concurrency::agent
{
    concurrency::ISource<Message>& _source; // received from SUD
    int _id;
    Message info;
    std::mutex myMutex;
public:
    explicit MySink(concurrency::ISource<Message>& source, int ID = 0) : _source(source), _id(ID) {}
    void print(const Message& s)
    {
        std::lock_guard<std::mutex> guard(myMutex);
        std::cout << "\nin a sink: " << _id << ": " << info << std::endl;
    }

    void run()
    {
        info = concurrency::receive(_source);
        print(info);
        done();
    }

    double compute(int val)
    {
        info *= val*_id;
        return info;
    }
};

int main()
{
    { // Single sink
        using Message = std::string;
        Message m(" good morning");

        // All actors access (read from/write to) a single buffer
        concurrency::overwrite_buffer<Message> buffer;
        MySource i(m, buffer);
        SUD<Message, MySource, MySink> s(buffer, buffer);
        MySink o(buffer);

        i.start(); o.start(); s.start();
        concurrency::agent::wait(&i); concurrency::agent::wait(&s); concurrency::agent::wait(&o);
    }

    {    // Multiple sinks
        using Message = int;
        Message m(23);

        // All actors access (read from/write to) a single buffer
        concurrency::overwrite_buffer<Message> buffer;
        MySource i(m, buffer);
        SUD<Message, MySource, MySink> s(buffer, buffer);
        MySink o1(buffer, 1);
        MySink o2(buffer, 2);
        MySink o3(buffer, 3);
        i.start(); s.start();
        o1.start(); o2.start(); o3.start();
        concurrency::agent::wait(&i); concurrency::agent::wait(&s);
        concurrency::agent::wait(&o1);
        concurrency::agent::wait(&o2);
        concurrency::agent::wait(&o3);

        std::cout << "\ncompute: " << o1.compute(2) << '\n';
        std::cout << "\ncompute: " << o2.compute(2) << '\n';
        std::cout << "\ncompute: " << o3.compute(2) << '\n';
    }

    return 0;
}
 
Last edited:
Professor you are reading my mind! I was just thinking about this and stumbled on the actix crate: https://crates.io/crates/actix

They use the Tokio runtime which I know is one of the more developed crates. Baby steps to victory I guess. It certainly is still in the teething stages I'd say.
 
Professor you are reading my mind! I was just thinking about this and stumbled on the actix crate: https://crates.io/crates/actix

They use the Tokio runtime which I know is one of the more developed crates. Baby steps to victory I guess. It certainly is still in the teething stages I'd say.
I knew you were going to ask about actix.
I wrote 101 program in actix yesterday.
 
Back
Top