Published on Feb 04, 2021 by Luca Pasut
Have you ever thought about investing your money but you are too emotional to do it? Why not have a robot do it?
I have always been interested in trading and how it works. My thesis was focused on the application of an algorithm to decide which shares were best to buy and verify how much they would be valued in the observation period. But that’s another story.
In this last period for work and to start automating some tasks that were becoming repetitive I started studying Python. Bumping into the fantastic world of the Web I discovered that Python could also be used for trading (algotrading), both to test strategies and to actually execute buying and selling operations in an automated way. Given my curiosity in always trying new solutions, I created my own bot to do online trading.
To use Python you need to have your own development environment. For simplicity, I chose Microsoft’s Visual Studio Code which is present in all major operating systems and allows you to quickly start programming.
If you don’t have any programming background, I suggest you start watching some online tutorials or take some courses. Below I am attaching some links:
Installation and Setup: To set up your pc to use Python you can follow this link:
For all those who have already configured Python they can start installing the libraries necessary for the development of our bot.
pip install python-binance
pip install pandas
pip install numpy
pip install matplotlib
Once you have set up your development environment we can start writing our code.
What do you need to trade online?
For this article I propose the strategy that uses the MFI (Money Flow Index) indicator to define when to buy and when to sell a particular stock. There are many others and I advise you to search on the web which strategy to choose that may be good for you too.
How does it work and how can I use this indicator?
The Money Flow Index (MFI) is an indicator that uses both price and volume to identify an asset’s overbought or oversold signals. The indicator moves between 0 and 100. An MFI value above 80 is considered overbought while a value below 20 is considered oversold. Levels 90 and 10 are also used as thresholds.
For more details visit [this page] (https://www.investopedia.com/terms/m/mfi.asp).
Here are some indicators that can be used to generate buy and sell signals for stocks:
Before writing our bot we must first find the data to calculate our index. In my case, I chose Binance as the exchange, since it is already set up for the use of bots via API.
To connect to the exchange we need to import the Python-binance library into our .py file as follows:
import binance
from binance.client import Client
Then we must communicate our credentials to the Binance server to be able to log in as follows:
client = Client(api_key, api_secret)
With api_key and api_secret the keys that are provided to us by Binance once requested on your profile page. Once connected we need to download the historical data and then calculate our MFI. Here is the function that allows you to do this:
We will use the python-binance function to download all the historical data we want:
klines = client.get_historical_klines(tickerSymbol+symbol_trade, time_frame, str(start_date), str(end_date))
Now that we have our data history, we put it in a dataframe.
stock_data = pd.DataFrame(klines, columns = ['Timestamp', 'Open', 'High', 'Low', 'Close', 'Volume', 'close_time', 'quote_av', 'trades', 'tb_base_av', 'tb_quote_av', 'ignore' ])
stock_data = stock_data.drop(['close_time', 'quote_av', 'trades', 'tb_base_av', 'tb_quote_av', 'ignore'], axis = 1)
stock_data['Timestamp'] = pd.to_datetime(stock_data['Timestamp'], unit='ms')
stock_data = stock_data.set_index('Timestamp')
stock_data = stock_data.astype('float64')
At this point we calculate the columns necessary to calculate the MFI index to be added to the dataframe.
# Columns for MFI
stock_data['Typical Price'] = (stock_data['Close'] + stock_data['Low'] + stock_data['High'])/3
stock_data['Money Flow'] = stock_data['Typical Price'] * stock_data['Volume']
positive_flow = []
negative_flow = []
for i in range (1, len(stock_data)):
if stock_data['Typical Price'][i]>stock_data['Typical Price'][i-1]:
positive_flow.append(stock_data['Money Flow'][i-1])
negative_flow.append(0)
elif stock_data['Typical Price'][i]<stock_data['Typical Price'][i-1]:
positive_flow.append(0)
negative_flow.append(stock_data['Money Flow'][i-1])
else:
positive_flow.append(0)
negative_flow.append(0)
positive_mf = []
negative_mf = []
for i in range(14 - 1, len(positive_flow)):
positive_mf.append(sum(positive_flow[i+1-14:i+1]))
for i in range(14 - 1, len(negative_flow)):
negative_mf.append(sum(negative_flow[i+1-14:i+1]))
mfi = 100 * (np.array(positive_mf)/(np.array(positive_mf) + np.array(negative_mf)))
stock_data = stock_data[14:]
stock_data['MFI'] = mfi
Before starting to test our strategy in the field it is always good to test it virtually and what better way to do it than graphically.
First of all we need to create a function that allows us to graph the performance of our asset.
def plot_graph (stock_data, tickerSymbol):
fig = plt.figure(figsize=(12.5, 6.5))
ax = fig.add_subplot(1,1,1)
x_axis = stock_data.index
ax.plot(x_axis, stock_data['Close'], color = 'gold', lw = 3, label = 'Close Price', alpha = 0.35)
if not (np.isnan(stock_data['Buy Mix'].values).all()):
ax.scatter(x_axis, stock_data['Buy Mix'], label = "Buy", marker = '^', color = 'green', lw = 3)
if not (np.isnan(stock_data['Sell Mix'].values).all()):
ax.scatter(x_axis, stock_data['Sell Mix'], label = "Sell", marker = 'v', color = 'red', lw = 3)
ax.set_title('Trading for '+ tickerSymbol)
ax.set_xlabel('Date')
ax.set_ylabel('USD price ($)')
ax.legend(loc='upper right')
plt.xticks(rotation = 45)
plt.show()
Now to visualize the data we need to calculate the buy and sell signals using the following function:
def define_strategy_MFI(stock_data, money_start, client, symbol):
sigPriceBuy = []
sigPriceSell = []
priceBought = 0
priceSold = 0
total_earned = 0
number_of_stocks = 0
total_money = money_start
for i in range(len(stock_data)):
if stock_data['MFI'][i] <= low_MFI:
if number_of_stocks == 0 and total_money > 0:
sigPriceBuy.append(stock_data['Close'][i])
sigPriceSell.append(np.nan)
priceBought = stock_data['Close'][i]*(1+fee)
number_of_stocks = money_start/priceBought
total_money = 0
else:
sigPriceBuy.append(np.nan)
sigPriceSell.append(np.nan)
elif stock_data['MFI'][i] >= high_MFI:
if number_of_stocks > 0:
sigPriceBuy.append(np.nan)
sigPriceSell.append(stock_data['Close'][i])
priceSold = stock_data['Close'][i]*(1-fee)
if priceBought > 0:
total_earned = total_earned + (priceSold - priceBought)*(number_of_stocks)
total_money = money_start + total_earned
number_of_stocks = 0
else:
sigPriceBuy.append(np.nan)
sigPriceSell.append(np.nan)
else:
sigPriceBuy.append(np.nan)
sigPriceSell.append(np.nan)
if number_of_stocks != 0:
priceSold = stock_data['Close'][i]*(1-fee)
total_earned = total_earned + (priceSold - priceBought)*(number_of_stocks)
total_money = money_start + total_earned
return (sigPriceBuy, sigPriceSell, total_money, total_earned, number_of_stocks)
Now that we have our 3 functions we can create a file that finally allows us to view the results.
The result should look like this:
Now that we have verified that the strategy could work, we need to write functions that allow us to buy or sell automatically. Here some examples of a function that allows us to buy and to sell, plus the function that allows us to calculate the quantity to buy and sell:
def buy_call (stock_data, money_start, delta_test, symbol, client, last_update):
tm = money_start
symbol_info = client.get_symbol_info(symbol+'USDT')
price_symbol = float(client.get_symbol_ticker(symbol=symbol+'USDT')['price'])
tick_size = float(list(filter(lambda f: f['filterType'] == 'PRICE_FILTER', symbol_info['filters']))[0]['tickSize'])
step_size = float(list(filter(lambda f: f['filterType'] == 'LOT_SIZE', symbol_info['filters']))[0]['stepSize'])
price = floatPrecision(price_symbol*(1-constant_to_buy), tick_size)
quantity = floatPrecision(money_start / float(price), step_size)
client.order_market_buy(symbol=symbol+'USDT',quantity=quantity)
orders = client.get_all_orders(symbol=symbol+'USDT', limit = 1)
pb = float(orders[0]['price']) * (1 + fee)
ns = float(orders[0]['origQty'])
last_update = pd.Timestamp(time.time() - delta_test, unit='s')
return (pb, ns, last_update)
def sell_call (stock_data, money_start, pb, ns, te, delta_test, symbol, client, last_update):
tm = money_start
symbol_info = client.get_symbol_info(symbol+'USDT')
price_symbol = float(client.get_symbol_ticker(symbol=symbol+'USDT')['price'])
quantity_control = float(symbol_info['filters'][3]['minNotional'])
num_stocks = float(client.get_asset_balance(asset=symbol)['free'])
if num_stocks*price_symbol > quantity_control:
step_size = float(list(filter(lambda f: f['filterType'] == 'LOT_SIZE', symbol_info['filters']))[0]['stepSize'])
tick_size = float(list(filter(lambda f: f['filterType'] == 'PRICE_FILTER', symbol_info['filters']))[0]['tickSize'])
quantity = floatPrecision(num_stocks, step_size)
client.order_market_sell(symbol=symbol+'USDT',quantity=quantity)
ns = 0
pb = 0
last_update = pd.Timestamp(time.time() - delta_test, unit='s')
return (pb, ns, last_update)
def floatPrecision(f, n):
n = int(math.log10(1 / float(n)))
f = math.floor(float(f) * 10 ** n) / 10 ** n
f = "{:0.0{}f}".format(float(f), n)
return str(int(f)) if int(n) == 0 else f
Now that we have our strategy and the functions to buy and sell assets we need to use a function that will tell us when buy or sell our asset.
def buy_sell_MFI(stock_data, money_start, priceBought, number_of_stocks, total_earned, last_update, delta_test, symbol, client):
te = total_earned
ns = number_of_stocks
tm = money_start
pb = priceBought
if stock_data['MFI'][-1] <= low_MFI and ns == 0:
# put the call to buy the stock
bc = buy_call(stock_data, money_start, delta_test, symbol, client, last_update)
pb = bc[0]
ns = bc[1]
tm = bc[2]
last_update = bc[3]
elif stock_data['MFI'][-1] >= high_MFI and ns > 0:
# put the call to sell the stock
sc = sell_call(stock_data, money_start, pb, ns, te, delta_test, symbol, client, last_update)
pb = sc[0]
ns = sc[1]
te = sc[2]
tm = sc[3]
last_update = sc[4]
else:
te = total_earned
ns = number_of_stocks
tm = money_start
pb = priceBought
return (tm, te, ns, pb, last_update)
Here is an example of how to call these functions:
So what’s missing for our trading bot? Test, test and test again.