Skip to main content
Version: nightly

Cython

Here you will find guidance and tips for working on NautilusTrader using the Cython language. More information on Cython syntax and conventions can be found by reading the Cython docs.

What is Cython?

Cython is a compiled programming language that aims to be a superset of the Python programming language, designed to give C-like performance with code that is written mostly in Python with optional additional C-inspired syntax.

The project heavily utilizes Cython to provide static type safety and increased performance for Python through C extension modules. The vast majority of the production code is actually written in Cython, however the libraries can be accessed from both Python and Cython.

Function and method signatures

Ensure that all functions and methods returning void or a primitive C type (such as bint, int, double) include the except * keyword in the signature.

This will ensure Python exceptions are not ignored, and instead are “bubbled up” to the caller as expected.

Debugging

PyCharm

Improved debugging support for Cython has remained a highly up-voted PyCharm feature for many years. Unfortunately, it's safe to assume that PyCharm will not be receiving first class support for Cython debugging https://youtrack.jetbrains.com/issue/PY-9476.

Cython Docs

The following recommendations are contained in the Cython docs: https://cython.readthedocs.io/en/latest/src/userguide/debugging.html

The summary is it involves manually running a specialized version of gdb from the command line. We don't recommend this workflow.

Tips

When debugging and seeking to understand a complex system such as NautilusTrader, it can be quite helpful to step through the code with a debugger. With this not being available for the Cython part of the codebase, there are a few things which can help:

  • Ensure LogLevel.DEBUG is configured for the backtesting or live system you are debugging. This is available on BacktestEngineConfig(logging=LoggingConfig(log_level="DEBUG")) or TradingNodeConfig(logging=LoggingConfig=log_level="DEBUG")). With DEBUG mode active you will see more granular and verbose log traces which could be what you need to understand the flow.
  • Beyond this, if you still require more granular visibility around a part of the system, we recommend some well-placed calls to a components logger (normally self._log.debug(f"HERE {variable}" is enough).