Trading Bot con Python

Come crearsi un bot per fare trading online con Python

Pubblicato il 04/02/2021 da Luca Pasut

Avete mai pensato di investire i vostri soldi ma siete troppo emotivi per farlo? Perchè non farlo fare ad un robot?

Mi sono sempre interessato al trading e al suo funzionamento. La mia tesi di laurea era proprio incentrata sull’applicazione di un algoritmo per decidere quali azioni era meglio comprare e verificare quanto si sarebbero valutate nel periodo di osservazione. Ma questa è un’altra storia.

In questo ultimo periodo per lavoro e iniziare ad automatizzare alcune attività che stavano diventando ripetitive ho iniziato a studiare Python. Imbattendomi nel fantastico mondo del Web ho scoperto che Python si poteva usare anche per fare trading (algotrading), sia per testare le proprie strategie che per eseguire effettivamente le operazioni di acquisto e vendita in modo automatizzato. Data la mia curiosità nel provare sempre nuove soluzioni mi sono fiondato anche io a creare il mio bot per fare trading online.

Da dove posso iniziare?

Per utilizzare Python è necessario avere un proprio ambiente di sviluppo. Per semplicità io ho scelto Visual Studio Code di Microsoft che è presente in tutti i principali sistemi operativi e permette di iniziare velocemente a programmare.

Se non avete nessuna base di programmazione vi consiglio di iniziare a guardare qualche tutorial online o fare qualche corso. Di seguito vi allego alcuni link:

Setup e installazione: Per settare il vostro pc all’utilizzo di Python potete seguire questo link:

Per tutti quelli che invece hanno già configurato Python possono iniziare ad installare le librerie necessarie per lo sviluppo del nostro bot.

pip install python-binance
pip install pandas
pip install numpy
pip install matplotlib

Una volta settato il vostro ambiente di sviluppo possiamo iniziare a scrivere il nostro codice.

Cosa serve per fare trading online?

  • Avere una strategia
  • Collegarsi ad un exchange per scaricare i dati necessari
  • Creare una funzione per vendere e una per acquistare
  • Applicare la strategia
  • Visualizzare in un grafico l’andamento della propria strategia

Strategia

Per questo articolo vi propongo la strategia che utilizza l’indicatore MFI (Money Flow Index) per definire quando comprare e quando vendere una determinato titolo. Ce ne sono molte altre e vi consiglio di cercare sul web quale strategia scegliere che possa andare bene anche a voi.

Come funziona e come posso utilizzare questo indicatore?

Il Money Flow Index (MFI) è un indicatore che usa sia il prezzo che il volume per identificare i segnali di ipercomprato o ipervenduto di un asset. L’indicatore si muove tra lo 0 e il 100. Un valore di MFI sopra 80 è considerato ipercomprato mentre un valore sotto il 20 è considerato ipervenduto. Anche i livelli 90 e 10 sono utilizzati come soglie.

Per maggiori dettagli visitate questa pagina.

Vi elenco alcuni indicatori che possono essere utilizzati per generare dei segnali di acquisto e vendita delle azioni:

  • SMA
  • EMA
  • Bollinger bands
  • MACD
  • RSI

Collegarsi ad un exchange

Prima di scrivere il nostro bot dobbiamo innanzitutto reperire i dati per calcolare il nostro indice. Nel mio caso ho scelto come exchange Binance, dato che è già predisposto per l’utilizzo di bot tramite API.

Per collegarsi all’exchange dobbiamo importare nel nostro file .py la libreria Python-binance nel seguente modo:

import binance
from binance.client import Client

Successivamente dobbiamo comunicare al server di Binance le nostre credenziali per poter accedere nel seguente modo:

client = Client(api_key, api_secret)

Con api_key e api_secret le chiavi che ci vengono fornite da Binance una volta richieste nella pagina del proprio profilo. Una volta collegati dobbiamo scaricare i dati storici e calcolare quindi il nostro MFI.

Utilizzeremo la funzione di python-binance per scaricare tutti i dati storici che vogliamo:

klines = client.get_historical_klines(tickerSymbol+symbol_trade, time_frame, str(start_date), str(end_date))

Ora che abbiamo il nostro storico di dati, li mettiamo in un 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')

A questo punto calcoliamo le colonne necessarie per calcolare l’indice MFI da aggiungere al 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

Visualizzare gli andamenti graficamente

Prima di iniziare a testare la nostra strategia sul campo è sempre bene testarla virtualmente e quale miglior modo di farlo se non graficamente.

Innanzitutto dobbiamo creare una funzione che permetta di graficare l’andamento del nostro 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()

Ora per visualizzare i dati dobbiamo calcolare i segnali di acquisto e vendita utilizzando la seguente funzione:

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)

Ora che abbiamo le nostre 3 funzioni possiamo creare un file che ci permetta finalmente di visualizzare i risultati, come nell’esempio linkato sotto:

plot_stock_article.py

Il risultato dovrebbe essere simile al seguente:

Creare una funzione per acquistare e vendere

Ora che abbiamo verificato che la strategia potrebbe funzionare dobbiamo scrivere delle funzioni che permettano di comprare o vendere in modo automatico.

Di seguito un esempio di funzione per comprare gli asset:

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, 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)

La funzione per vendere:

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)

La funzione necessaria per poter calcolare la quantità di un determinato asset.

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

Applicare una strategia

Ora che abbiamo la nostra strategia e le funzioni per vendere e comprare dobbiamo utilizzare la funzione che ci dica ogni minuto quando comprare e quando vendere.

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)

Di seguito un esempio di come richiamare queste funzioni:

trading_article.py

Cosa manca quindi per il nostro trading bot? Testare, testare e ancora testare.