PingPong, advanced solutions for demanding practitioners
PingPong is a framework designed to help you build your own trading bot. While it is primarily built around the CCXT API, it can be extended to work with any custom exchange, albeit with some effort.
Many trading bots are implemented in Python, which can lead to slow or hard-to-customize vectorized backtesters. By writing trading strategies in Julia, PingPong can perform efficiently during both backtesting and live execution. Other bots implement a faster backtester in another language (Cython, Numba, Rust) but still pay the price for calling code from strategies written in Python, or require the user to implement the strategy itself in a less ergonomic language.
PingPong, being fully written in Julia, does not incur these trade-offs and can offer performant execution in both simulations and live trading. Moreover, sharing the same language means that anyone who manages trading strategies in Julia can also contribute to making the PingPong backtester (and the rest) better and faster for every other user. In other words, a contribution to the PingPong codebase is a worthwhile investment more so than over other non-Julia trading frameworks.
Julia's dispatch mechanism makes it easy to customize any part of the bot without feeling like you are monkey patching code. It allows you to easily implement ad-hoc behavior to solve exchange API inconsistencies (despite CCXT's best efforts at unification). You don't have to wait for upstream to fix some annoying exchange issue, you can fix most things by dispatching a function instead of having to maintain a fork with a patchset. Ad-hoc customizations are non-intrusive.
Most open-source trading frameworks don't have a fully thought-out system for handling margined positions. PingPong employs a type hierarchy that can handle isolated and cross margin trading, with hedged or unhedged positions. (However, only isolated unhedged positions management is currently implemented, PRs welcome).
Strategies can take a lot of data but not everything can fit into memory. PingPong addresses this issue head-on by relying on Zarr.jl for persistence of OHLCV timeseries (and other) with the ability to access data progressively chunk by chunk. It also allows you to save data chunk by chunk.
When dealing with timeseries we want to make sure data is sane. More than other frameworks, PingPong goes the extra mile to ensure that OHLCV data does not have missing entries, requiring data to be contiguous. During IO, we check the dates index to ensure data is always properly appended or prepended to existing ones.
Many frameworks are eager to provide data that you can use to develop your strategies with backtesting in mind, but leave you hanging when it comes to pipeline fresh data into live trading. PingPong provides a standard interface that makes it easier to build jobs that fetch, process and store data feeds to use in real-time.
Dealing with periods of time is crucial for any trading strategy, yet many trading frameworks gloss over this not so small detail causing repeated lookahead bias bugs. PingPong implements a full-featured library to handle parsing and conversions of both dates and timeframes. It has convenient macros to handle date periods and timeframes within the REPL and provides indexing by date and range of dates for dataframes.
Handling a large number of strategies can be cumbersome and brittle. PingPong doesn't step on your toes when you are trying to piece everything together, because there are no requirements for a runtime environment, there is no overly complicated setup, starting and stopping strategies is as easy as calling start!
and stop!
on the strategy object. That's it. You can construct higher-level cross-currency or cross-exchange systems by just instantiating multiple strategies.
Other frameworks build the backtester like an event-driven "simulated" exchange such that they can mirror as precise as possible real-world exchanges. In PingPong instead, the backtester is functionally a loop, with execution implemented from scratch. This makes the backtester:
- Simpler to debug (it is self-contained)
- Faster (it is synchronous)
- Friendlier to parameter optimization (it is standalone and easy to parallelize)
The fine-grained ability to simulate orders and trades allows us to run the simulation even during live trading. This means that we can either tune our simulation against our chosen live trading exchange, or be alerted about exchange misbehavior when our simulation diverges from exchange execution. Achieving this with an event-driven backtester ends up being either very hard, a brittle mess or simply impossible. This is a unique feature of PingPong that no other framework provides and we called it by-simulation1.
In every execution mode, there is always a view of the strategy state which is local first, there is full access to orders, trades history, balances. What differs between the execution modes is not what but how all our internal data structures are populated, which is abstracted away from the user. From the user perspective, strategy code works the same during backtesting, paper and live trading. Yet the user can still choose to branch execution on different modes, for example, to pre-fill some data during simulations, the strategy is of course always self-aware of what mode it is running in.
Other frameworks achieve low code duplication by completely abstracting away order management and instead provide a signal interface. PingPong abstractions are thin, from the strategy, you are sending orders directly yourself, there is no man in the middle, you decide how, what, when to enter or exit trades. If you want a higher level of abstractions like signals and risk management, those can be implemented as modules that the strategy depends on, PRs welcome.
- Can plot OHLCV data, custom indicators, trades history, asset balance history
- Can perform parameter optimization using grid search, evolution and Bayesian opt algorithms. Has restore/resume capability and plotting of the optimization space.
- In Paper mode, trades are simulated using the real order book and exchange trades history
- Has a Telegram bot to control strategies
- Can download data from external archives in parallel, and has API wrappers for crypto APIs.
- Can still easily call into python (with async support!) if you wish
Here's a comparison of features with other popular trading frameworks:
⚠️ This table might be imprecise or outdated (please file an issue for improvements)
Feature | PingPong | Freqtrade | Hummingbot | OctoBot | Jesse | Nautilus | Backtrader |
---|---|---|---|---|---|---|---|
🔴 Paper/Live execution | ✔️ | ✔️ | ✔️ | ✔️ | 〰️ | 〰️ | 〰️ |
🎛 Remote control | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | ❌ | ❌ |
💾 Data Management | ✔️ | ✔️ | ❌ | ❌ | ❌ | ❌ | ✔️ |
⚡ Fast & flexible backtester | ✔️ | ❌ | ❌ | ❌ | ❌ | ✔️ | ❌ |
📈 DEX support | ❌ (planned) | ❌ | ✔️ | ❌ | ✔️ | ❌ | ❌ |
💰 Margin/Leverage | ✔️ | ❌ | ❌ | ✔️ | ✔️ | ✔️ | ❌ |
🔍 Optimization | ✔️ | ✔️ | ❌ | ✔️ | ✔️ | ❌ | ❌ |
📊 Plotting | ✔️ | ✔️ | ❌ | ✔️ | ✔️ | ❌ | 〰️ |
🖥 Dashboard | ❌ | ✔️ | ✔️ | ✔️ | ❌ | ❌ | ❌ |
📡 Live feeds | ✔️ | ❌ | ❌ | ❌ | ❌ | ✔️ | ✔️ |
🛡 Bias hardened | ✔️ | ✔️ | ❌ | ❌ | ❌ | ❌ | ❌ |
🔧 Customizable | ✔️ | ❌ | ✔️ | ❌ | ❌ | ✔️ | ❌ |
🪛 Composable | ✔️ | ❌ | ❌ | ❌ | ❌ | ✔️ | ✔️ |
🔁 Low code duplication | ✔️ | ✔️ | ❌ | ✔️ | ❌ | ✔️ | ❌ |
⚓ By-Simulation | ✔️ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
For developing strategies:
# Sysimage build (the largest number of methods precompiled) plus interactive modules (plotting and optimization)
docker pull docker.io/panifie/pingpong-sysimage-interactive
For running live strategies
# Sysimage build with only the core components, better for live deployments
docker pull docker.io/panifie/pingpong-sysimage
For developing pingpong
# Precomp build. Slower loading times, smaller image
docker pull docker.io/panifie/pingpong-precomp-interactive
# Precomp build without interactive modules
docker pull docker.io/panifie/pingpong-precomp
PingPong.jl requires at least Julia 1.9. Is not in the julia registry, to install it do the following:
- Clone the repository:
git clone --recurse-submodules https://github.com/panifie/PingPong.jl
- Check the env vars in
.envrc
, then enabled them withdirenv allow
.
cd PingPong.jl
direnv allow
- Activate the project specified by
JULIA_PROJECT
in the.envrc
.
julia
- Download and build dependencies:
] instantiate
using PingPong # or PingPongInteractive for plotting and optimization
Read the 📖 documentation (link) to learn how to get started with the bot.
For questions and direct support you can join the 💬 discord chat (link)
Footnotes
-
Not to be confused with bisimulation :) ↩