👋 Hey, Pedma here! Welcome to this month’s ✨ free edition ✨ of Trading Research Hub’s Newsletter. Each week, I release a new research article with trading strategies, code, and much more.
If you’re not a subscriber, here’s what you missed in the last 30 days:
Subscribe to get access to these posts, and every post.
Have you ever wondered how is a simple trade execution model built in python?
Today we will be getting a bit more practical.
We will build this simple trade execution model, explore what it means to do that, problems that may arise, and general tips on how to make it as robust as possible.
Keep in mind that I don’t come from an engineering background, at all.
Everything I’ve built has been through trial and error.
But that doesn’t stop us from building something that works, does it?
Sometimes we can make excuses and not realize what we’re capable off, given that we put in some work.
Not that I’ve done anything super special, but without an IT background or anything related to that, I’ve managed to build some interesting projects such as:
Multi-strategy portfolios that I’ve launched and deployed on AWS servers
Machine learning engines
Countless backtests of both simple and complex models
Language categorization models (eg. sentiment models)
All it takes is a bit of effort and a determination to get the job done.
I want to this newsletter’s target audience to be practitioners of the game of trading.
I don’t want to be just another theoretical newsletter, that prides itself on how technical it is, but has 0 practical ability.
That is not who I am.
I like to “get my hands dirty” and actually do the task I write about.
Today I’ve heard the following quote from a philosopher, and it’s just on point with what I was writing here:
“If your words should have any value, you must live them yourself.”
So let’s get into today’s article!
Index
Introduction
Index
Connection to API
Setting a Stop-Market Order
Setting Stop-Loss and Take-Profit Orders
Normalizing Orders for Execution
Final Thoughts
Python Code Section
Connection to API
The first thing we have to do is connect to the API of our exchange of choice.
For this example we will Binance exchange and CCXT library to connect and handle the execution.
We will need the API keys from our exchange.
Go to “API Management” under your account on binance, and create new access keys.
Make sure that these keys are always safe.
If you’re going to be trading futures, make sure you tick that box “Enable Futures”.
It isn’t automatically set, so you need to change it.
For added security you can restrict the access from a trusted IP only.
For the purposes of this example, I will skip that step but it’s a good security feature to have.
Now that we have our keys, let’s connect to the exchange.
import ccxt
import time
# Replace these with your own API keys
api_key = ''
secret = ''
# Create the Binance exchange object
exchange = ccxt.binance({
'apiKey': api_key,
'secret': secret,
'enableRateLimit': True,
'options': {
'defaultType': 'future' # Specify that we're using perpetual swaps
}
})
Make sure that you add your key’s as strings to api_key and secret, and you’re good to go.
Running your code now should connect you to the exchange.
Setting a Stop-Market Order
Now that we are connected to an exchange, we want to start sending some orders next right?
The first thing we do is setting our order size to notional.
I personally prefer to look at notional prices for my execution, than quantity of shares, coins, contracts, etc, because it’s easier to manage.
So we first fetch the price using the function get_current_price(), and then we execute a stop market order with the execute_stop_market_order() function.
def get_current_price(symbol):
"""
Get the current market price for a given symbol.
:param symbol: The trading pair symbol (e.g., 'BTC/USDT')
:return: The current price
"""
ticker = exchange.fetch_ticker(symbol)
return ticker['last']
def execute_stop_market_order(symbol, side, usd_amount, stop_price):
"""
Execute a stop market order on Binance.
:param symbol: The trading pair symbol (e.g., 'BTC/USDT')
:param side: 'buy' or 'sell'
:param usd_amount: The amount in USD to buy or sell
:param stop_price: The stop price to trigger the market order
"""
order_type = 'STOP_MARKET'
params = {
'stopPrice': stop_price,
'reduceOnly': False,
}
try:
# Get the current price of the base currency (e.g., BTC in BTC/USDT)
current_price = get_current_price(symbol)
# Calculate the amount in base currency
amount = usd_amount / current_price
order = exchange.create_order(symbol, order_type, side, amount, None, params)
print(f"Stop market order executed: {order}")
return order
except ccxt.BaseError as e:
print(f"An error occurred: {e}")
Let’s say we want to execute $200 worth of notional size and set the order to be triggered at $70,000.00 for Bitcoin, we pass the following call:
# Example usage: Place a stop market buy order for $200 worth of BTC if the price reaches $70,000
symbol = 'BTC/USDT'
side = 'buy'
usd_amount = 200
stop_price = 70000
execute_stop_market_order(symbol, side, usd_amount, stop_price)
And we get our order in.
Perfect!
Now let’s move on to the next part.
Setting Stop-Loss and Take-Profit Orders
Now that we can set up our orders to get us into the market, we will be setting up our stop-loss and take-profit orders.
Doesn’t mean necessarily that we have to use these orders, but if the strategy that we employ requires them, we already have them set up.
Now I’ve made a slight change to our previous execute_stop_market_order()
function, and added the functionality of checking if the order was executed or not, and if it was, it executes a stop_loss and take_profit order, if the user defines that he wants those orders to be executed.
We want to make this flexible to handle multiple strategy types, so we only pass these orders if the user wants them.
def execute_order_with_tp_sl(symbol, side, usd_amount, stop_price, set_tp=False, tp_price=None, set_sl=False, sl_price=None):
"""
Execute an order on Binance futures with optional take profit and stop loss.
:param symbol: The trading pair symbol (e.g., 'BTC/USDT')
:param side: 'buy' or 'sell'
:param usd_amount: The amount in USD to buy or sell
:param stop_price: The stop price to trigger the initial market order
:param set_tp: Boolean indicating whether to set a take profit order
:param tp_price: The take profit price (optional)
:param set_sl: Boolean indicating whether to set a stop loss order
:param sl_price: The stop loss price (optional)
"""
try:
# Get the current price of the base currency (e.g., BTC in BTC/USDT)
current_price = get_current_price(symbol)
# Calculate the amount in base currency
amount = usd_amount / current_price
# Place the initial stop market order
order_type = 'STOP_MARKET'
params = {
'stopPrice': stop_price,
}
initial_order = exchange.create_order(symbol, order_type, side, amount, None, params)
print(f"Initial stop market order placed: {initial_order}")
# Wait for the initial order to be triggered and filled
while True:
order_status = exchange.fetch_order(initial_order['id'], symbol)
if order_status['status'] == 'closed':
print("Initial order filled.")
break
time.sleep(1) # Wait for a second before checking the order status again
# Place take profit order if specified
if set_tp and tp_price:
tp_side = 'sell' if side == 'buy' else 'buy'
tp_order = exchange.create_order(symbol, 'TAKE_PROFIT_MARKET', tp_side, amount, None, {'stopPrice': tp_price})
print(f"Take profit order placed: {tp_order}")
# Place stop loss order if specified
if set_sl and sl_price:
sl_side = 'sell' if side == 'buy' else 'buy'
sl_order = exchange.create_order(symbol, 'STOP_MARKET', sl_side, amount, None, {'stopPrice': sl_price})
print(f"Stop loss order placed: {sl_order}")
except ccxt.BaseError as e:
print(f"An error occurred: {e}")
And here it is the function in action.
The order was executed, and it sent the new orders that we had set up.
Normalizing Orders for Execution
Now that we have our simple execution engine setup, we need to “feed” orders into it right?
There’s a lot of different trading models out there, each one with their unique need for order handling.
Of course we can’t cover all types of requirements in a single function.
mean reversion models will require different type of execution than momentum models
momentum models from trend , etc.
trend models from arbitrage
arbitrage models from carry models
carry models from market making models
etc
But we’ll make it possible that simple forms of execution, can be done within a single function.
def get_signal_from_data(signal_data):
signals = {}
for symbol, df in signal_data.items():
#Add logic to extract trades from the data
signals[symbol] = {
'entry_price': entry_price,
'stop_loss': stop_loss,
'take_profit_1': take_profit_1,
}
return signals
Let’s assume that we have a signal_data
with multiple assets in it and we want to extract trades to be opened at this moment.
The function above will go through the dictionary containing all the data for these assets, identify these signals, add them to the dictionary signals
and return the dictionary with all signals to be executed.
We keep this logic equal across all systems so that we only need to change the logic to find the signal when we have a different system.
The goal is to go from point A to point B with the least modifications as possible between signals.
Final Thoughts
This is a pretty simple implementation and there’s way more that goes into it depending on the type of strategy you’re trying to launch.
For example if you want to do trend-following approaches, you need to ensure a lot of different things we’ve talked about before:
Portfolio management
Inventory management
Regular tracking of open exposure
Volatility targeting (if part of the model)
Volatility forecasts (if part of the model)
Delta between modeled execution and realized execution
Also this execution model waits until the position is executed, only to execute the take profit and stop loss orders after.
This means that the model is “stuck” within that order until it is executed.
Which is not the most ideal for a lot of strategies.
So a lot of adaptation needs to be made, depending on your own model.
This is just a simple example showing you how to get started sending some orders into your exchange.
I hope you’ve liked it!
If you want more in detail articles about trading strategies and systematic trading content in general, join us below!
Wish you all a great day!
Pedma
Python Code Section
import ccxt
import time
# Replace these with your own API keys
api_key = ''
secret = ''
# Create the Binance exchange object
exchange = ccxt.binance({
'apiKey': api_key,
'secret': secret,
'enableRateLimit': True,
'options': {
'defaultType': 'future' # Specify that we're using perpetual swaps
}
})
def get_current_price(symbol):
"""
Get the current market price for a given symbol.
:param symbol: The trading pair symbol (e.g., 'BTC/USDT')
:return: The current price
"""
ticker = exchange.fetch_ticker(symbol)
return ticker['last']
def execute_stop_market_order(symbol, side, usd_amount, stop_price):
"""
Execute a stop market order on Binance.
:param symbol: The trading pair symbol (e.g., 'BTC/USDT')
:param side: 'buy' or 'sell'
:param usd_amount: The amount in USD to buy or sell
:param stop_price: The stop price to trigger the market order
"""
order_type = 'STOP_MARKET'
params = {
'stopPrice': stop_price,
'reduceOnly': False,
}
try:
# Get the current price of the base currency (e.g., BTC in BTC/USDT)
current_price = get_current_price(symbol)
# Calculate the amount in base currency
amount = usd_amount / current_price
order = exchange.create_order(symbol, order_type, side, amount, None, params)
print(f"Stop market order executed: {order}")
return order
except ccxt.BaseError as e:
print(f"An error occurred: {e}")
def execute_order_with_tp_sl(symbol, side, usd_amount, stop_price, set_tp=False, tp_price=None, set_sl=False, sl_price=None):
"""
Execute an order on Binance futures with optional take profit and stop loss.
:param symbol: The trading pair symbol (e.g., 'BTC/USDT')
:param side: 'buy' or 'sell'
:param usd_amount: The amount in USD to buy or sell
:param stop_price: The stop price to trigger the initial market order
:param set_tp: Boolean indicating whether to set a take profit order
:param tp_price: The take profit price (optional)
:param set_sl: Boolean indicating whether to set a stop loss order
:param sl_price: The stop loss price (optional)
"""
try:
# Get the current price of the base currency (e.g., BTC in BTC/USDT)
current_price = get_current_price(symbol)
# Calculate the amount in base currency
amount = usd_amount / current_price
# Place the initial stop market order
order_type = 'STOP_MARKET'
params = {
'stopPrice': stop_price,
}
initial_order = exchange.create_order(symbol, order_type, side, amount, None, params)
print(f"Initial stop market order placed: {initial_order}")
# Wait for the initial order to be triggered and filled
while True:
order_status = exchange.fetch_order(initial_order['id'], symbol)
if order_status['status'] == 'closed':
print("Initial order filled.")
break
time.sleep(1) # Wait for a second before checking the order status again
# Place take profit order if specified
if set_tp and tp_price:
tp_side = 'sell' if side == 'buy' else 'buy'
tp_order = exchange.create_order(symbol, 'TAKE_PROFIT_MARKET', tp_side, amount, None, {'stopPrice': tp_price})
print(f"Take profit order placed: {tp_order}")
# Place stop loss order if specified
if set_sl and sl_price:
sl_side = 'sell' if side == 'buy' else 'buy'
sl_order = exchange.create_order(symbol, 'STOP_MARKET', sl_side, amount, None, {'stopPrice': sl_price})
print(f"Stop loss order placed: {sl_order}")
except ccxt.BaseError as e:
print(f"An error occurred: {e}")
def get_signal_from_data(signal_data, entry_price, stop_loss, take_profit_1):
signals = {}
for symbol, df in signal_data.items():
#Add logic to extract trades from the data
signals[symbol] = {
'entry_price': entry_price,
'stop_loss': stop_loss,
'take_profit_1': take_profit_1,
}
return signals
symbol = 'BTCUSDT'
side = 'buy'
usd_amount = 150
stop_price = 60828.6
set_tp = True
tp_price = 62000
set_sl = True
sl_price = 60600
execute_order_with_tp_sl(symbol, side, usd_amount, stop_price, set_tp, tp_price, set_sl, sl_price)
Disclaimer: The content and information provided by the Trading Research Hub, including all other materials, are for educational and informational purposes only and should not be considered financial advice or a recommendation to buy or sell any type of security or investment. Always conduct your own research and consult with a licensed financial professional before making any investment decisions. Trading and investing can involve significant risk of loss, and you should understand these risks before making any financial decisions.
Hi Pedma, thanks for the article!
Can you please write on different execution types per the models ?