20 September 2022 • 18 min read
Until relatively recently, if you weren’t working at one of the big financial investment institutions, then you just didn’t have access to algorithmic trading.
However, the internet and accompanying rise of disruptive FinTech companies and decentralized finance (DeFi) have revolutionized personal investing by bringing professional-grade investment tools that were once the exclusive domain of Wall Street to Main Street. Now, anyone with Python knowledge and an internet connection can design and implement sophisticated trading algorithms.
In this article, we’ll explore the exciting world of algorithmic trading with Python and learn how Trality’s state-of-the-art Python Code Bot Editor can help you create, backtest, optimize, and deploy algorithms for live trading, whether you’re a trader looking to learn more about Python or a Python coder interested in applying your skills to crypto trading.
Let’s start off with the obvious question: Why use Python at all? According to Tiobe’s index of programming language popularity, Python is the most common programming language in the world followed by C, Java, C++, and C#. If you’re interested in a programming language that is used extensively today, then Python should be your top choice, with the obvious exception of C/C++ if you’re dealing with embedded systems. Below are some of the reasons why it has become so popular.
Whether a computer language or a foreign language, learning any new language is hard work, but Python is different. It’s relatively easy to learn and easy to use, making it both beginner- and user-friendly due to its shallow learning curve. It’s simplified, uncomplicated syntax means that it’s closer to natural language, making writing and execution much faster than the alternatives. And its ease of maintenance means that you’re not wasting time working your way through unnecessary documentation.
Conceived in the 1980s by Guido van Rossum and first appearing in 1991, Python benefits from having withstood over three decades of use and real-world applications. Ever since its inception, it has continued to amass a knowledgeable and helpful community of programmers along with incredible support and documentation.
Whatever your take on corporations, their endorsement can pay dividends. Facebook, Amazon, and Google rely heavily on Python, while companies ranging from Instagram and Spotify to Wikipedia and even Reddit have adopted it to varying degrees. In fact, Google first started using it in 2006.
Beyond the corporate realm, CERN and NASA both make use of Python. Just think of the flex that Python has from being used to streamline space shuttle mission design. If it can be used quickly, efficiently, and accurately to send large, heavy, metallic objects into space (and back), then it is certainly up to the challenge of creating a profitable algorithmic trading system.
One of the things that is particularly convenient about Python is the extent to which it makes writing and evaluating algorithmic trading structures easier thanks to its functional programming approach. In fact, relative ease and simplicity of use are some of Python’s main selling points for traders interested in coding their first or next crypto trading bot.
Below are a few more reasons why Python is the perfect choice for algorithmic trading.
For people new to algorithmic trading, Python code is readable and accessible. Unlike other coding languages, there’s simply less of it, which means that trading with Python requires fewer lines of code due to the availability of extensive libraries.
Chances are that the algorithmic platforms and tools for trading already on your radar are using Python. The culture of algorithmic trading is done in the language of Python, making it easier for you to collaborate, trade code, or crowdsource for assistance.
Parallelization and Python’s tremendous computational power endow your portfolio with scalability. Compared to other languages, it’s easier to fix new modules to Python and make it expansive. And because of the existing modules, it’s easier for traders to share functionality between different programs.
Python is an “interpreted” language. An interpreter executes code statements “one-by-one,” unlike a compiler that executes code in its entirety, listing all possible errors at once. Debugging in Python is comprehensive and thorough, as it permits live changes to code and data, increasing execution speed since single errors (rather than multiple ones) appear and can be cleared.
Python’s extensive, comprehensive support libraries mean that most highly used programming tasks are already scripted into it, limiting the length(s) of the code(s) to be written. More importantly, Python just works straight out of the box, which many programmers attribute to a combination of dynamic typing, pseudocode-like syntax, and the Python interpreter.
Not only is Python free, open source, easy to learn, and easy to use, it also has an outstanding selection of libraries for virtually every task related to algorithmic trading (and trading in general). Below we run through a number of popular libraries based on their purpose, from data collection, data manipulation, and plotting to technical analysis, machine learning, and backtesting.
If you’re looking for a robust open source data analysis and manipulation tool that is quick and easy to use, then look no further than Pandas (particularly good with time series data and manipulating numerical tables).
Python wasn’t originally intended for numerical computing, which is where NumPy, or Numerical Python, comes into play. Virtually anyone working with Python today is drawing on NumPy’s powerful suite of tools, including C/C++ and Fortran code integration tools, N-dimensional array objects, and Fourier transforms, among other things.
SciPy is an open-source Python library intended for technical and scientific computing, joining mathematics, engineering, and science. Features include linear algebra, integration, interpolation, special functions, FFT, signal and image processing, and ODE solvers, among other things.
If you need online tools for analytics, statistics, and scientific graphing libraries for Python, then Plotly is your answer. Modifying candlestick charts to include volume, for example, can be done in one of two ways with Plotly (or even create a separate chart).
When it comes to Python libraries for machine learning, there are a number of good ones at your disposal as an algo trader, including scikit-learn, LightGBM, PyTorch, and TensorFlow. And be sure to read our in-house expert’s article on Avoiding Common Pitfalls of Machine Learning Strategies.
Implement Bayesian statistical models and fit algorithms such as Markov chain Monte Carlo (MCMC) for sampling from a probability distribution by using PyMc and pair it with NumPy for numerics, wherever possible.
Looking for a financial technical analysis indicator library? Check out tulipy, which requires NumPy (all inputs and outputs are NumPy arrays). And while you’re at it, have a look at pandas-ta and choose from more than 130 indicators and utility functions as well as more than 60 technical analysis candlestick patterns.
Algo traders can perform portfolio profiling by using QuantStats, which enables users to better understand their performance via risk metrics and analytics.
In the sections above, we’ve seen some of the many advantages of using Python for algorithmic trading. It’s easy to learn, easy to use, readable, accessible, powerful, flexible, and works straight out of the box—key ingredients when building a profitable algorithmic trading strategy.
By bridging economics, finance, and data science, Python has become one of the most popular programming languages for FinTech companies, consistently ranking among the top three most popular languages in financial services.
In fact, Python offers the greatest number of job opportunities in absolute terms within the banking sector. According to research done in 2020, there were nearly 1,500 Python jobs, with 14 other Python programmers chasing each one. Big players such as Citigroup now offer Python coding classes to banking analysts and traders as a part of their continuing education initiatives. For many of the reasons mentioned earlier in this article, Python has a great deal to offer traders as well as analysts and researchers.
If you’re interested in a job in banking, then Python should definitely interest you. Bank of America’s Quartz program uses Python as its core language. In the words of former BoA tech guru Kirat Singh, “Everyone at JPMorgan now needs to know Python and there are around 5,000 developers using it at Bank of America,” adding that “There are close to 10 million lines of Python code in Quartz and we got close to 3,000 commits a day. It’s a good scripting language and easily integrated into both the front and back ends, which was one of the reasons we chose it in the first place.”
Because of its analytics tools, Python is widely used in quantitative trading. Thanks to libraries such as Pandas, Python users benefit from easier data visualization and sophisticated statistical calculations. Financial services providers can also harness powerful machine-learning algorithms and their predictive analytics with Python-based solutions that utilize libraries such as scikit-learn or PyBrain, among the many others mentioned earlier.
One of Python’s primary strengths is also one of its weaknesses. Because of its ease of use, features and extensive libraries, Python users can have trouble learning and working in other programming languages, which are more time consuming to learn and master.
While Python excels in desktop and server applications, it is less impressive in mobile computing, at least according to some users.
And there is also the issue of variables, which are considered objects and can therefore translate into memory leaks and performance bottlenecks (i.e., millions of variables are stored) stemming from inefficient memory management.
Right. You now know more about Python. You understand its benefits and how it can help you profit from algorithmic trading. Now you need to set up your workspace, which can have varying levels of difficulty. A bare minimum involves having Python and an Integrated Development Environment (IDE) running on your system.
A better alternative is to use Trality’s state-of-the-art Python Bot Code Editor — a powerful browser-based Python Bot Code Editor designed for traders who want to build, backtest, optimize, and live trade with algorithmic trading bots.
Trality’s Bot Code Editor is completely FREE to use for virtual trading. If your real trading volume exceeds 5,000 EUR per month, then the pricing plan starts from 9.99 EUR and tops out at 59.99 EUR for unlimited trading volume.
So, how can you actually get started using Trality’s Code Editor? In the following sections, we’ll run through a simple algorithmic trading strategy that simultaneously benefits from price rises while also protecting your portfolio. The strategy uses two time intervals, the Quantitative Qualitative Estimation (QQE) momentum indicator, and places a take-profit and trailing stop-loss as soon as we take a position. (For a detailed explanation of any aspect of the strategy, see the Trality Documentation.)
In this strategy, we only want to enter a trade when the asset is in uptrend for both short and long term. For the shorter trend, we will use 1 hour candles and define the trend as uptrend if the exponential moving average (EMA) of 5 is on top of the EMA of 20. For the longer trend, we will use 1 day candles and define the trend as an uptrend if the simple moving average (SMA) of 15 is on top of the SMA of 80. Furthermore, we will only enter a trade under the condition that the current price of the asset is below the EMA of 5.
As mentioned, we will use the QQE indicator in order to enter the trade at the correct moment. The QQE is the super trend of the relative strength index (RSI). If you are unsure of how it works or need additional explanations, then head over to our documentation page.
Lastly, we will set a take-profit at 5% and trailing stop-loss at 10% to protect our portfolio. This Python strategy is based on a high win rate. In other words, hitting the take-profit should be a high probability event, and hitting the trailing stop-loss should be a low probability event.
Having defined the strategy we are now ready to start building. Let’s go through each of the main building blocks one step at a time.
For starters, every function that is annotated with our schedule decorator is run on a specified time interval and receives symbol data. We call these annotated functions handlers, but you can name them whatever you want. They must take two arguments, though. We call the first one state and the second one data. The second argument will always receive the symbol data for the interval that you specified. In this particular bot, we trade on two intervals as we use 1 day candles and 1 hour candles. Therefore, we will use two handlers and specify BTCUSDT as the trading pair. Of course, it’s possible to trade on multiple symbols, too.
In this step we will only focus on defining the handler_long, which will take care of identifying the long term trend.
def initialize(state): pass @schedule(interval="1d", symbol="BTCUSDT") def handler_long(state, data):
In the first step of our algorithm, we build the functionality to identify an uptrend for the handler_long function. We define our simple moving averages (SMA), one with a shorter look-back period of 15 candles and one longer with a period of 80 candles.
sma_short = data.sma(15).last sma_long = data.sma(80).last if sma_short > sma_long: state.long_trend = "uptrend" else: state.long_trend = "downtrend"
That was simple! We have now finished the handler_long function. This is how it looks:
@schedule(interval="1d", symbol="BTCUSDT") def handler_long(state, data): sma_short = data.sma(15).last sma_long = data.sma(80).last if sma_short > sma_long: state.long_trend = "uptrend" else: state.long_trend = "downtrend"
Lets now carry on and define handler short. Remember, handler_short will run on 1 hour candles!
@schedule(interval="1h", symbol="BTCUSDT") def handler_short(state, data):
Now let’s work on the handler_short function, but, before we do, we need to fetch the indicator data first. However, let’s do a quick recap before proceeding:
We will use an EMA of 5 and 20 to identify the shorter trend.
We will use the QQE, with settings (20, 5, 4.2) to enter the trade at the correct moment.
We will only enter a trade if the asset price is below an EMA of 5, hence we need to fetch the asset price from data.
ema_short = data.ema(5).last ema_long = data.ema(20).last qqe = data.qqe(20, 5, 4.2) last_trend = qqe["trend"].last last_closing_price = data.close_last
In this step, we set the rules to enter a trade. To do so, we make an inner function called signal_check, which takes care of checking if the rules to enter a trade are met. The inner function checks the following: the longer trend is uptrend, the shorter trend is uptrend, QQE indicates buy, and the price of the asset is below the EMA of 5. If all of the rules are met, then the signal_check returns “True” (otherwise it will return “False”).
def signal_check(): if state.long_trend == "uptrend" and ema_short > ema_long and last_trend > 0 and last_closing_price < ema_short: return True else: return False
We now query for any open position by symbol. By calling this function, we receive a Boolean value indicating whether an open position for that symbol exists or not. Lastly, we check for any open orders.
position = query_open_position_by_symbol(data.symbol, include_dust=False) has_position = position is not None has_open_orders = len(query_open_orders()) > 0
This is where the heart and soul of our algorithm is defined: its trading strategy. We use the order API to create orders. Specifically, the algorithm places a market order going long if inner function signal_check signals buy for 300 USDT. Furthermore, the algorithm uses the one cancels other order scope as we want to place a take-profit at 5% and a trailing stop-loss at 10% both for the full position.
if signal_check() == True and not has_position and not has_open_orders: state.buy_order = order_value(symbol=data.symbol, value=300) with OrderScope.one_cancels_others(): state.take_profit = order_take_profit(symbol=data.symbol,amount=state.buy_order.quantity, stop_percent=0.05,subtract_fees=True) state.stop_loss = order_trailing_iftouched_amount(symbol=data.symbol, amount=-subtract_order_fees(state.buy_order.quantity ), trailing_percent= 0.1, stop_price=data.close[-1]*0.88)
If we put all these steps together, we get the following little code snippet, which we can subsequently put through our first backtest:
def initialize(state): pass @schedule(interval="1d", symbol="BTCUSDT") def handler_long(state, data): sma_short = data.sma(15).last sma_long = data.sma(80).last if sma_short > sma_long: state.long_trend = "uptrend" else: state.long_trend = "downtrend" @schedule(interval="1h", symbol="BTCUSDT") def handler_short(state, data): ema_short = data.ema(5).last ema_long = data.ema(20).last qqe = data.qqe(20, 5, 4.2) last_trend = qqe["trend"].last last_closing_price = data.close_last def signal_check(): if state.long_trend == "uptrend" and ema_short > ema_long and last_trend > 0 and last_closing_price < ema_short: return True else: return False position = query_open_position_by_symbol(data.symbol, include_dust=False) has_position = position is not None has_open_orders = len(query_open_orders()) > 0 if signal_check() == True and not has_position and not has_open_orders: state.buy_order = order_value(symbol=data.symbol, value=300) with OrderScope.one_cancels_others(): state.take_profit = order_take_profit(symbol=data.symbol,amount=state.buy_order.quantity, stop_percent=0.05,subtract_fees=True) state.stop_loss = order_trailing_iftouched_amount(symbol=data.symbol, amount=-subtract_order_fees(state.buy_order.quantity ), trailing_percent= 0.1, stop_price=data.close[-1]*0.88)
Trality provides its users with a full suite of metrics to use when testing their strategy, of which there are three categories: performance, risk-return, and runs. Our Trality Docs contains everything you need to know about the various tools and data at your disposal when backtesting your trading strategy!
It’s time to backtest the trading strategy and take a look at the most important metrics: Sharpe ratio, total return, and maximum drawdown. The timeframe used for this scenario is 01.06.21-01.12.21.
The Sharpe ratio is one of the most popular risk-return measures used in trading, providing investors with a better understanding of the return of an investment compared to its risk. It’s obtained by calculating the difference between the returns of the investment and the risk-free return, divided by the standard deviation of the investment (i.e., its volatility).
In the figure above, you can see that our trading bot achieved a high Sharpe ratio. This is evidence that the bot managed to keep our portfolio safe while making a nice return (9.39%).
Total return is a performance statistic (measured as a percentage) that represents the accumulated net profit or loss for a given time horizon. It is calculated as follows:
where PVt and PVT represent portfolio value at start t and end time T.
The main objective of our strategy is to generate profit while keeping our portfolio safe. Therefore, since we didn’t take much risk, we didn’t manage to beat the market by generating a total return of 9.39% with our trading bot.
A maximum drawdown (MDD) is the maximum observed loss from a peak to a trough of a portfolio before a new peak is attained. This is the percentage difference between running high and low PnL. Maximum drawdown is an indicator of downside risk over a specified period of time.
As shown in the backtest results above, the MDD for the specified period of our Python trading strategy is 6.35%. In other words, our strategy’s maximum observed loss from a peak was 6.35%. This result was achieved as the trailing stop-loss in our Python strategy limits the maximum drawdown.
Creating a profitable Python-based bot can be challenging. Even when you have an algorithm idea with which you’re satisfied, optimizing its parameters can be frustrating and time-consuming. That’s why our research team has built the Trality Optimizer.
In this section, we’ll optimize our algorithmic trading strategy. As you can see from the code below, we will need to add our new feature annotation “@parameter” on top of the initializer. To use the @parameter annotations, we then need to add the params object to the functions and to the indicators.
Now it’s ready to be optimized. Don’t forget to activate the Optimizer under advanced settings!
@parameter(name="ema_short", type="float", default=5, min=3, max=15, enabled=True) @parameter(name="ema_long", type="float", default=20, min=15, max=40, enabled=True) def initialize(state, params): pass @schedule(interval="1d", symbol="BTCUSDT") def handler_long(state, data, params): sma_short = data.sma(15).last sma_long = data.sma(80).last if sma_short > sma_long: state.long_trend = "uptrend" else: state.long_trend = "downtrend" @schedule(interval="1h", symbol="BTCUSDT") def handler_short(state, data, params): ema_short = data.ema(params.ema_short).last ema_long = data.ema(params.ema_long).last qqe = data.qqe(20, 5, 4.2) last_trend = qqe["trend"].last last_closing_price = data.close_last def signal_check(): if state.long_trend == "uptrend" and ema_short > ema_long and last_trend > 0 and last_closing_price < ema_short: return True else: return False position = query_open_position_by_symbol(data.symbol, include_dust=False) has_position = position is not None has_open_orders = len(query_open_orders()) > 0 if signal_check() == True and not has_position and not has_open_orders: state.buy_order = order_value(symbol=data.symbol, value=300) with OrderScope.one_cancels_others(): state.take_profit = order_take_profit(symbol=data.symbol,amount=state.buy_order.quantity, stop_percent=0.05,subtract_fees=True) state.stop_loss = order_trailing_iftouched_amount(symbol=data.symbol, amount=-subtract_order_fees(state.buy_order.quantity ), trailing_percent= 0.1, stop_price=data.close[-1]*0.88)
By running the Optimizer, we found that the optimal parameter for ema_short is 6 and for ema_long is 21.25. You can see the backtesting results in the image below.
With the optimal parameters, the bot managed to increase its returns from 9.39% to 12.76% and increase the Sharpe ratio from 1.39 to an outstanding value of 2.01, which you can also see above.
Hopefully, you’ve found this walkthrough tutorial of how to create a simple Python trading strategy both useful and inspiring! Now you can use Trality’s Code Editor for FREE to tweak the settings and get a better feel for the platform and what it can do for you. Or create your own trading bot from scratch and customize it to meet your needs.
Once you’re happy with your Python trading bot, the next step is to deploy it for virtual trading using Trality, and we walk you through the simple steps below. The final leg of your strategy development journey involves live trading, where you can choose from Trality’s constantly expanding list of some of the world’s most trusted exchanges, including Binance, Binance.US, Kraken, Bitpanda, FTX, and Coinbase Pro.
However, we encourage you to test your bot in virtual trading first in order to see how the strategy performs in real-world conditions, but without any risk to your finances. Remember, virtual trading is just that—no real funds are used.
Let’s get started!
You can now practice trading as long as you want with your custom Python bot, optimize its parameters, and sharpen your skills in the process before live trading with actual funds.
Sprint, swim, cycle—algorithmic trading is a lot like being a triathlete. (Now I know what you’re thinking—not another one of those inspirational sports analogies.)
Just like triathletes, though, traders must master three essential skills in order to succeed: mathematics, finance, and coding. You can be brilliant at maths and know coding inside-out, but if you don’t know much about finance then you’re going to have difficulty making it to the finish line. You need to have creative ideas about how to trade; you need to be able to translate those ideas into mathematical models; and, finally, you need to be able to implement them in code.
But it’s more than just mastering technical skills. Anyone can learn to swim. Or become good at running. Or be a whiz on a bike. Those are the things that will get you past the qualifying stage and into the race. But to really outperform others or exceed what you thought was possible for yourself, you’ve got to love the feel of the water and the ground beneath your feet. That metal frame, with its gears, pedals and wheels, needs to become an extension of your body.
At Trality, we can equip you with world-class, state-of-the-art tools to put you in the best position possible when it comes to the big race.
The rest is up to you.
Python isn’t just a fantastic programming language for algorithmic traders. From multi-billion dollar corporations to start-up companies, it’s the language driving some of today’s biggest brands and likely the stars of tomorrow. Google, Facebook, and Microsoft use Python for things such as web applications, data science, AI, machine learning, deep learning, and task automation, while Instagram, Spotify and Uber use Python to power their websites.
Closer to home, however, traders require robust tools for conducting comprehensive market analysis in order to discern trends and insights and then make predictions and forecasts based on their findings. Python empowers algorithmic traders to create profitable trading strategies and benefit from predictive analytical insights into the conditions of specific markets.