forex-python-trading-algoritmico-001

DTForex #1 – Trading Automatico sul Forex tramite le API di Oanda

Sommario

Nonostante io stesso svolgo la mia attività di ricerca sui mercati azionari e futures, ho pensato che sarebbe stato divertente (ed educativo!) scrivere delle mie esperienze di studio del mercato forex tramite una nuova serie di articoli. Ogni articolo sarà costruito sulla base dei precedenti articoli, ma dovrebbe essere anche relativamente autosufficiente.

In questo primo articolo  descriviamo come creare un nuovo account DEMO con il broker OANDA e come creare un motore di trading basato su eventi multithreading in grado di eseguire automaticamente le operazioni sia in modalità demo che live.

Negli articoli precedenti abbiamo dedicato molto tempo a esaminare il backtester guidato dagli eventi, principalmente per le azioni e gli ETF. In questo articolo introduciamo un motore di backtest orientato al forex, che può essere utilizzato sia per il paper-trading che per il live-trading.

Le istruzioni operative di questo articolo sono in ambiente Ubuntu 18.04, ma possono essere facilmente tradotte in Windows o Mac OS X, utilizzando una distribuzione Python come Anaconda. L’unica libreria aggiuntiva utilizzata per il motore di trading in Python è la libreria requests, necessaria per la comunicazione HTTP con l’API di OANDA.

Poiché questo è il primo post diretto al trading sul forex e il codice presentato di seguito può essere facilmente adattato a un ambiente di trading dal vivo, è necessario presentare l’opportuna dichiarazione di non responsabilità:

Disclaimer: il trading sul forex a margine comporta un alto livello di rischio e potrebbe non essere adatto a tutti gli investitori. I rendimenti passati non sono indicativi di risultati futuri. L’elevato grado di leva finanziaria può funzionare sia contro di te che per te. Prima di decidere di investire in valuta estera, dovresti considerare attentamente i tuoi obiettivi di investimento, il livello di esperienza e la propensione al rischio. Esiste la possibilità che tu possa sostenere una perdita parziale o totale del tuo investimento iniziale e quindi non dovresti investire denaro che non puoi permetterti di perdere. È necessario essere consapevoli di tutti i rischi associati al trading in valuta estera e, in caso di dubbi, chiedere consiglio a un consulente finanziario indipendente.

Questo software viene fornito “così com’è” e qualsiasi garanzia espressa o implicita, incluse, ma non limitate a, le garanzie implicite di commerciabilità e idoneità per uno scopi particolari sono escluse. In nessun caso i realizzatori o i contributori saranno responsabili per danni diretti, indiretti, incidentali, speciali, esemplari o consequenziali (inclusi, ma non limitati a, l’approvvigionamento di beni o servizi sostitutivi; perdita di usabilità, dati o profitti; o interruzione dell’attività) comunque causati e in base a qualsiasi teoria di responsabilità, sia contrattuale, oggettiva o illecita (inclusa negligenza o altro) derivante dall’uso di questo software, anche se informati della possibilità di tale danno.

Creazione di un account con OANDA

La prima domanda che mi viene in mente è “Perché scegliere OANDA?”. In poche parole, dopo aver cercato su Google i broker forex che disponevano di API, ho visto che OANDA aveva recentemente rilasciato una REST API adeguata con cui era possibile comunicare facilmente con quasi tutti i linguaggi in modo estremamente facile. Dopo aver letto la loro documentazione API per sviluppatori, ho deciso di provarlo, almeno con un account di prova.

Per essere chiari, non ho alcun rapporto precedente o esistente con OANDA e sto fornendo questa raccomandazione solo in base alla mia limitata esperienza limitata nel testare nella pratica la loro API e un breve utilizzo (per il download dei dati di mercato). Se qualcuno si è imbattuto in altri broker forex che hanno anche un’API altrettanto moderna, sarei felice di dare un’occhiata anche a questi broker.

Prima di utilizzare l’API è necessario registrarsi per un account di pratica. Per fare ciò, utilizza il link di registrazione. Si avrà una schermata simile alla seguente:

qs-oanda-forex-sign-up-trading-algoritmico
Si potrà quindi loggarsi con le proprie credenziali di accesso. Assicurarsi di selezionare la scheda “fxTradePractice” dalla schermata di accesso:
qs-oanda-forex-sign-in-trading-algoritmico
Una volta entrati, si dovrà prendere nota del proprio ID account. È elencato sotto l’intestazione nera “My Funds” accanto a “Primary”. Il mio è un numero di 7 cifre. Inoltre si dovrà generare anche un token API personale. A tale scopo, si deve far clic su “Manage API Access” nella scheda “Other Actions”:
qs-oanda-forex-manage-api-trading-algoritmico

A questo punto saremo in grado di generare un token API. Si avrà bisogno di questo token per usarla successivamente, quindi bisogna assicurarsi di scriverla correttamente.Ora si può lanciare l’applicazione FXTrade Practice, che permetterà di consultare gli ordini eseguiti e i profitti e perdite (in paper!).

Se si sta utilizzando un sistema Ubuntu, si dovrà installare una versione leggermente diversa di Java. In particolare, la versione Oracle di Java 8. Se non lo si fa, il simulatore di pratica non verrà caricato dal browser.

In Ubuntu si eseguono questi comandi:

            sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update
sudo apt-get install oracle-java8-installer
        
Ora siamo in grado di avviare l’ambiente di trading demo. Tornando alla dashboard di OANDA e si fa clic sul link evidenziato in verde “Launch FXTrade Practice”. Verrà visualizzata una finestra di dialogo Java che chiede se si desidera eseguirlo. Cliccando su “Run” si carica l’applicazione fxTrade Practice. Ad esempio si può visualizzare un grafico a candele di 15 minuti di EUR / USD con il pannello delle quotazioni a sinistra:
qs-oanda-forex-manage-api-trading-algoritmico
A questo punto siamo pronti per iniziare a progettare e codificare il nostro sistema di forex trading automatizzato contro l’API di OANDA.

Panoramica dell'Architettura di Trading

Nella serie di articoli relativi alla progettazione di un backtester basato sugli eventi per azioni ed ETF, scritti in precedenza, abbiamo descritto il funzionamento di un sistema di trading basato sugli eventi. Se si desira capire questo motore basato sugli eventi, suggerisco caldamente di leggere questi articoli per avere un’idea di come funziona.

In sostanza, l’intero software viene eseguito in un ciclo continuo che termina solo quando il sistema di trading viene spento. Il meccanismo di comunicazione centrale del sistema è dato tramite una coda di eventi.

La coda viene costantemente interrogata per verificare la presenza di nuovi eventi. Una volta che un evento è stato rimosso dalla parte superiore della coda, deve essere gestito da un componente appropriato del sistema. Quindi un feed di dati di mercato potrebbe creare TickEvents che vengono inseriti in coda quando arriva un nuovo prezzo di mercato. Un oggetto Strategy di generazione di segnali potrebbe creare OrderEvents che devono essere inviati a un broker o società di intermediazione.

L’utilità di un tale sistema consiste nell’essere indipendente dal tipo di ordine o dal tipo di eventi vengono posti in coda, in quanto saranno sempre gestiti correttamente dal giusto componente all’interno del programma.

Inoltre, diverse parti del programma possono essere eseguite in thread separati, quindi non c’è bisogno di attendere la conclusione di particolare componente prima di elaborarne un altro. Ciò è estremamente utile negli scenari di trading algoritmico in cui i gestori di feed dei dati di mercato e i generatori di segnali strategici hanno caratteristiche di performance molto diverse.

Il ciclo di trading principale è dato dal seguente pseudo-codice Python:

            while True:
    try:
        event = events_queue.get(False)
    except Queue.Empty:
        pass
    else:
        if event is not None:
            if event.type == 'TICK':
                strategy.calculate_signals(event)
            elif event.type == 'ORDER':
                execution.execute_order(event)
    time.sleep(heartbeat)
        
Come detto in precedenza, il codice viene eseguito in un ciclo infinito. In primo luogo, viene eseguito il polling della coda per recuperare un nuovo evento. Se la coda è vuota, il ciclo si riavvia semplicemente dopo un breve periodo di sospensione noto come “battito cardiaco”. Se viene trovato un evento, viene valutato il suo tipo e quindi viene chiamato il relativo modulo (il gestore strategy o execution) per gestire l’evento ed eventualmente generarne di nuovi che saranno inseriti in coda. I componenti di base del nostro motore di trading includono quanto segue:
  • Streaming Price Handler – Mantiene  una connessione di lunga durata aperta verso i server di OANDA e invierà dati tick (ad es. bid/ask) attraverso la connessione per tutti gli strumenti a cui siamo interessati.
  • Strategy Signal Generator – Utilizza una sequenza di eventi tick e li utilizzerà per generare ordini di trading che verranno eseguiti dal gestore dell’esecuzione.
  • Execution Handler – Il gestore dell’esecuzione: prende un insieme di eventi Ordine e li esegue alla cieca con OANDA.
  • Events: questi oggetti costituiscono i “messaggi” che vengono passati nella coda degli eventi. Ne occorrono solo due per questa implementazione, vale a dire TickEvent e OrderEvent.
  • Main Entry Point – Il punto di ingresso principale include anche il ciclo “trading” che esegue continuamente il polling della coda dei messaggi e invia i messaggi al corretto componente. Questo è spesso noto come “ciclo di eventi” o “gestore di eventi”.
Discuteremo ora in dettaglio l’implementazione del codice. In fondo all’articolo c’è l’elenco completo di tutti i file del codice sorgente. Inserendoli nella stessa directory ed eseguendo python trading.py inizierai a generare ordini, supponendo che tu abbia inserito l’account ID e il token di autenticazione da OANDA.

Implementazione Python

È una cattiva pratica memorizzare le password o le chiavi di autenticazione all’interno del codice poiché non è mai possibile prevedere a chi verrà consentito l’accesso a un progetto. In un sistema in produzione si memorizzano queste credenziali come variabili di ambiente del sistema e quindi si interroga questi “envvars” ogni volta che il codice viene ridistribuito. Ciò garantisce che le password e i token di autenticazione non siano mai archiviati in un sistema di controllo della versione.

Tuttavia, poiché siamo interessati esclusivamente a costruire un sistema di trading “paper” e in questo articolo  non ci occupiamo dei dettagli di deploy, memorizziamo questi token di autenticazione in un file di impostazioni.

Nel seguente file di configurazione settings.py abbiamo un dizionario chiamato ENVIRONMENTS che memorizza gli API endpoint sia per l’API di streaming dei prezzi di OANDA che per l’API di trading. Ogni dizionario secondario contiene tre separati API endpoint: real, practice e sandbox.

L’API sandbox serve esclusivamente per testare il codice e per verificare che non ci siano errori o bug. Non ha le garanzie di uptime delle API real o practice. L’API practice, in sostanza, fornisce la funzionalità di paper trading. Cioè, fornisce tutte le funzionalità dell’API real su un account DEMO simulato. L’API real è proprio questo: è il live trading! Se si usa questo endpoint nel codice, verrà effettuato il trading in un conto con denaro reale. STATE ESTREMAMENTE ATTENTI!

IMPORTANTE: quando si fa trading con l’API practice, bisogna ricordare che un costo di transazione importante, cioè l’impatto sul mercato, non viene considerato. Dal momento che nessuna negoziazione viene effettivamente collocata nell’ambiente, questo costo deve essere contabilizzato in un altro modo utilizzando un modello di impatto sul mercato se si desidera una valutazione realistica delle prestazioni.

Di seguito utilizziamo l’account practice fornito dall’impostazione DOMAIN. Abbiamo bisogno di due dizionari separati per i domini, uno per i componenti di streaming e e uno per il trading. Infine abbiamo ACCESS_TOKEN e ACCOUNT_ID. Ho valorizzato i due ID con valori fittizi, quindi si dovrà utilizzare i propri ID, che possono essere ricavati dalla pagina dell’account OANDA:

            ENVIRONMENTS = {
    "streaming": {
        "real": "stream-fxtrade.oanda.com",
        "practice": "stream-fxpractice.oanda.com",
        "sandbox": "stream-sandbox.oanda.com"
    },
    "api": {
        "real": "api-fxtrade.oanda.com",
        "practice": "api-fxpractice.oanda.com",
        "sandbox": "api-sandbox.oanda.com"
    }
}

DOMAIN = "practice"
STREAM_DOMAIN = ENVIRONMENTS["streaming"][DOMAIN]
API_DOMAIN = ENVIRONMENTS["api"][DOMAIN]
ACCESS_TOKEN = 'abcdef0123456abcdef0123456-abcdef0123456abcdef0123456'
ACCOUNT_ID = '12345678'
        

Il passaggio successivo consiste nel definire gli events che la coda utilizzerà per aiutare la comunicazione tra tutti i singoli componenti del sistema. Abbiamo bisogno di due eventi: TickEvent e OrderEvent. Il primo memorizza le informazioni sui dati di mercato dello strumento come la (migliore) bid/ask e il tempo di negoziazione. Il secondo è utilizzato per trasmettere gli ordini all’executive handler e quindi contiene lo strumento, il numero di unità da negoziare, il tipo di ordine (“mercato” o “limite”) e il “lato” (cioè “long” e “short” ).

Per rendere flessibile il codice si crea una classe base chiamata Event e tutti gli eventi ereditano da questa classe. Di seguito il codice della classe events.py:

            class Event(object):
    pass


class TickEvent(Event):
    def __init__(self, instrument, time, bid, ask):
        self.type = 'TICK'
        self.instrument = instrument
        self.time = time
        self.bid = bid
        self.ask = ask


class OrderEvent(Event):
    def __init__(self, instrument, units, order_type, side):
        self.type = 'ORDER'
        self.instrument = instrument
        self.units = units
        self.order_type = order_type
        self.side = side
        

La prossima classe è quella per gestire la strategia di trading. In questa demo creiamo una strategia piuttosto priva di senso che riceve semplicemente tutti i tick di mercato e ogni 5 tick acquista o vende a caso 10.000 unità di EUR / USD.Chiaramente questa è una “strategia” ridicola! Tuttavia, è utile per scopi di didattici perché è semplice da codificare e da capire. Negli articoli successivi vedremo come sostituire questa logica con qualcosa di più significativo che (si spera) genererà un profitto!

Il file strategy.py è riportato di seguito. Analizziamolo e vediamo cosa implementa. Per prima cosa importiamo la libreria random e l’oggetto OrderEvent da events.py. Abbiamo bisogno della libreria random per selezionare casualmente un ordine di acquisto o vendita casuale. Abbiamo bisogno di OrderEvent poiché questo è l’evento con cui l’oggetto strategy invierà gli ordini alla coda degli eventi, che verrà successivamente eseguita dal gestore di esecuzione.

La classe TestRandomStrategy prende semplicemente lo strumento (in questo caso EUR/USD), il numero di unità e gli eventi in coda come un insieme di parametri. Quindi crea un contatore ticks che viene utilizzato per dire quante istanze di TickEvent sono state viste.

La maggior parte del lavoro avviene nel metodo calcolate_signals, che prende semplicemente un evento, determina se si tratta di un TickEvent (altrimenti lo ignora) e incrementa il contatore ticks. Quindi controlla se il conteggio è divisibile per 5 e poi acquista o vende in modo casuale, con un ordine di mercato, il numero di unità specificato. Non è certamente la migliore strategia di trading al mondo, ma sarà più che adatta ai nostri scopi di test delle API di intermediazione di OANDA!

            import random

from event import OrderEvent


class TestRandomStrategy(object):
    def __init__(self, instrument, units, events):
        self.instrument = instrument
        self.units = units
        self.events = events
        self.ticks = 0

    def calculate_signals(self, event):
        if event.type == 'TICK':
            self.ticks += 1
            if self.ticks % 5 == 0:
                side = random.choice(["buy", "sell"])
                order = OrderEvent(
                    self.instrument, self.units, "market", side
                )
                self.events.put(order)
        

Il componente successivo è l’execution handler. Questa classe ha il compito di agire sulle istanze OrderEvent e di effettuare richieste al broker (in questo caso OANDA) in modo “stupido”. Cioè, non vi è alcuna gestione del rischio o sovrapposizione della costruzione del portafoglio. Il gestore dell’esecuzione eseguirà semplicemente qualsiasi ordine che gli è stato inviato.

Dobbiamo passare tutte le informazioni di autenticazione alla classe Execution, incluso il “dominio” (practice, real o sandbox), il token di accesso e l’ID account. Quindi creiamo una connessione sicura con http.client, una delle librerie incorporate in Pythons.

La maggior parte del lavoro avviene in execute_order. Il metodo richiede un evento come parametro. Quindi costruisce due dizionari: le headers e i params. Questi dizionari sono quindi correttamente codificati (tramite urllib, un’altra libreria Python) per essere inviati come richiesta HTTP POST all’API di OANDA.

Passiamo i parametri di intestazione Content-Type e Authorization, che includono le nostre informazioni di autenticazione. Inoltre codifichiamo i parametri, che includono lo strumento (EUR / USD), le unità, il tipo di ordine e il lato (acquisto / vendita). Infine, effettuiamo la richiesta e salviamo la risposta:

            import http.client
import urllib


class Execution(object):
    def __init__(self, domain, access_token, account_id):
        self.domain = domain
        self.access_token = access_token
        self.account_id = account_id
        self.conn = self.obtain_connection()

    def obtain_connection(self):
        return http.client.HTTPSConnection(self.domain)

    def execute_order(self, event):
        headers = {
            "Content-Type": "application/x-www-form-urlencoded",
            "Authorization": "Bearer " + self.access_token
        }
        params = urllib.urlencode({
            "instrument" : event.instrument,
            "units" : event.units,
            "type" : event.order_type,
            "side" : event.side
        })
        self.conn.request(
            "POST",
            "/v1/accounts/%s/orders" % str(self.account_id),
            params, headers
        )
        response = self.conn.getresponse().read()
        print(response)
        
Il componente più complesso del sistema di trading è l’oggetto StreamingForexPrices, che gestisce gli aggiornamenti dei prezzi di mercato da OANDA. Esistono due metodi: connect_to_stream e stream_to_queue. Il primo metodo utilizza la libreria requests di Python per connettersi a un socket di streaming con le intestazioni e i parametri appropriati. I parametri includono l’ID account e l’elenco degli strumenti  che devono essere monitorati per ricevere gli aggiornamenti (in questo caso è solo EUR/USD). Da notare la seguente riga:
            resp = s.send(pre, stream=True, verify=False)
        

Questo indica che la connessione deve essere trasmessa in streaming e quindi mantenuta costantemente aperta per un periodo molto prolungato.

Il secondo metodo, stream_to_queue, tenta effettivamente di connettersi allo stream. Se la risposta non ha esito positivo (ovvero il codice di risposta non è HTTP 200), si esce e cede il controllo alla chiamante. Se ha successo si prova a caricare il pacchetto JSON ricevuto in un dizionario Python. Infine, converte il dizionario Python con lo strumento, bid / ask e timestamp in un TickEvent che viene inviato alla coda degli eventi:

            import requests
import json

from event import TickEvent


class StreamingForexPrices(object):
    def __init__(
        self, domain, access_token,
        account_id, instruments, events_queue
    ):
        self.domain = domain
        self.access_token = access_token
        self.account_id = account_id
        self.instruments = instruments
        self.events_queue = events_queue

    def connect_to_stream(self):
        try:
            s = requests.Session()
            url = "https://" + self.domain + "/v1/prices"
            headers = {'Authorization' : 'Bearer ' + self.access_token}
            params = {'instruments' : self.instruments, 'accountId' : self.account_id}
            req = requests.Request('GET', url, headers=headers, params=params)
            pre = req.prepare()
            resp = s.send(pre, stream=True, verify=False)
            return resp
        except Exception as e:
            s.close()
            print("Caught exception when connecting to stream\n" + str(e))

    def stream_to_queue(self):
        response = self.connect_to_stream()
        if response.status_code != 200:
            return
        for line in response.iter_lines(1):
            if line:
                try:
                    msg = json.loads(line)
                except Exception as e:
                    print("Caught exception when converting message into json\n" + str(e))
                    return
                if msg.has_key("instrument") or msg.has_key("tick"):
                    print(msg)
                    instrument = msg["tick"]["instrument"]
                    time = msg["tick"]["time"]
                    bid = msg["tick"]["bid"]
                    ask = msg["tick"]["ask"]
                    tev = TickEvent(instrument, time, bid, ask)
                    self.events_queue.put(tev)
        

Disponiamo ora di tutti i componenti principali. Il passaggio finale è racchiudere tutto ciò che abbiamo scritto finora in un programma “principale”. L’obiettivo di questo file, noto come trading.py, è creare due thread separati, uno che esegue il gestore dei prezzi e l’altro che esegue il gestore del trading.

Perché abbiamo bisogno di due thread separati? In parole povere, stiamo eseguendo due pezzi di codice “separati”, entrambi in esecuzione continua. Se dovessimo creare un programma senza thread, il socket di streaming utilizzato per gli aggiornamenti dei prezzi non verrebbe mai “rilasciato”, cioè non si tornerebbe mai al codice principale e quindi non effettueremmo mai alcun trading. Allo stesso modo, se eseguissimo il ciclo trade(vedi sotto), non restituiremmo mai il controllo del flusso di esecuzione del codice al socket di streaming dei prezzi. Quindi abbiamo bisogno di più thread, uno per ogni componente, in modo che possano essere eseguiti in modo indipendente. Entrambi comunicheranno tra loro tramite la coda degli eventi.

Esaminiamo un po’ nel dettaglio. Creiamo due thread separati con le seguenti righe:

            trade_thread = threading.Thread(target=trade, args=(events, strategy, execution))
price_thread = threading.Thread(target=prices.stream_to_queue, args=[])
        

Passiamo la funzione o il nome del metodo all’argomento della parola chiave target e quindi passiamo un iterabile (un elenco o una tupla) come argomento della parola chiave args, che corrispondo ai parametri d’ingresso del metodo / funzione specificato in “target”.

Infine iniziamo entrambi i thread con le seguenti righe:

            trade_thread.start()
price_thread.start()
        

In questo modo siamo in grado di eseguire in modo indipendente e separato due segmenti di codice a ciclo infinito, che comunicano entrambi attraverso la coda degli eventi. Si noti che la libreria threading di Python non produce un vero ambiente multi-core multithread a causa dell’implementazione CPython di Python e del Global Interpreter Lock (GIL).

Esaminiamo nel dettaglio il resto del codice. Per prima cosa importiamo tutte le librerie necessarie tra cui queue, threading e time. Quindi importiamo tutti i file di codice descritti in precedenza. Personalmente preferisco capitalizzare qualsiasi impostazione di configurazione, è un’abitudine che ho acquisito lavorando con Django!

Dopodiché definiamo la funzione trade, che è stata descritta con il precedente pseudo-codice in python. Viene eseguito un ciclo “while” infinito (while True:) che esegue il polling continuo dalla coda degli eventi e salta il ciclo solo se viene trovato vuoto (nessun evento). Se viene rilevato un evento, che può essere un TickEvent o un OrderEvent e quindi viene chiamato il componente appropriato per eseguirlo. In questo caso si tratta di una strategia o di un gestore di esecuzione. Il ciclo quindi si ferma semplicemente per “heartbeat” di secondi (in questo caso 0,5 secondi) e si rincomincia il ciclo.

Infine, definiamo l’entrypoint principale del codice nella funzione __main__. In particolare, istanziamo la coda degli eventi e definiamo gli strumenti / unità. Creiamo quindi la classe di streaming dei prezzi StreamingForexPrices e successivamente il gestore di esecuzione Execution. Entrambi ricevono i necessari parametri di autenticazione forniti da OANDA durante la creazione di un account.

Quindi creiamo l’istanza TestRandomStrategy. Infine definiamo i due thread e poi li avviamo:

            import queue
import threading
import time

from execution import Execution
from settings import STREAM_DOMAIN, API_DOMAIN, ACCESS_TOKEN, ACCOUNT_ID
from strategy import TestRandomStrategy
from data import StreamingForexPrices


def trade(events, strategy, execution):
    """
    Esegue un ciclo while infinito che effettua il polling 
    della coda degli eventi e indirizza ogni evento al 
    componente strategia del gestore di esecuzione. 
    Il ciclo si fermerà per "heartbeat" di alcuni secondi 
    e continuerà.
    """
    while True:
        try:
            event = events.get(False)
        except queue.Empty:
            pass
        else:
            if event is not None:
                if event.type == 'TICK':
                    strategy.calculate_signals(event)
                elif event.type == 'ORDER':
                    print("Executing order!")
                    execution.execute_order(event)
        time.sleep(heartbeat)


if __name__ == "__main__":
    heartbeat = 0.5  # Pausa di mezzo secondo
    events = queue.Queue()

    # Trading di 10000 unità di EUR/USD
    instrument = "EUR_USD"
    units = 10000

    # Creazione di una classe di streaming di prezzi da 
    # OANDA, assicurandosi di fornire le credenziali 
    # di autenticazione
    prices = StreamingForexPrices(
        STREAM_DOMAIN, ACCESS_TOKEN, ACCOUNT_ID,
        instrument, events
    )

    # Creazione di un gestore di esecuzione con parametri
    # di autenticazioni di OANDA
    execution = Execution(API_DOMAIN, ACCESS_TOKEN, ACCOUNT_ID)

    # Creazione del generatore di strategia/segnali, utilizzando
    # lo strumento, la quantità di unità e la coda di eventi come
    # parametri
    strategy = TestRandomStrategy(instrument, units, events)

    # Creazione di due thread separati: uno per il ciclo di trading
    # e uno per lo streaming dei prezzi di mercato
    trade_thread = threading.Thread(target=trade, args=(events, strategy, execution))
    price_thread = threading.Thread(target=prices.stream_to_queue, args=[])

    # Avvio di entrambi i thread
    trade_thread.start()
    price_thread.start()
        
Per eseguire il codice è sufficiente posizionare tutti i file nella stessa directory e eseguire il seguente codice sul terminale:
            python trading.py
        
Si noti che per fermare il codice in questa fase è necessario un hard kill del processo Python, tramite “Ctrl-Z” o equivalente! Non ho inserito un thread aggiuntivo per gestire la ricerca di sys.exit() che sarebbe necessario per interrompere il codice in modo sicuro. Un potenziale modo per interrompere il codice su una macchina Ubuntu / Linux è digitare:
            pgrep python
        
E quindi passare l’output di questo (un numero di processo) nel seguente comando:
            kill -9 PROCESS_ID
        

Dove PROCESS_ID deve essere sostituito con l’output di pgrep. Da notare che questa NON è una pratica particolarmente corretta!

Negli articoli successivi creeremo un meccanismo di avvio / arresto più sofisticato che utilizza la supervisione del processo di Ubuntu per far funzionare il sistema di trading 24 ore su 24, 7 giorni su 7.

L’output dopo circa 30 secondi, a seconda dell’ora del giorno relativa ai principali orari di negoziazione per EUR / USD, per il codice descritto in precedenza, è il seguente:

            {u'tick': {u'ask': 1.16283, u'instrument': u'EUR_USD', u'bid': 1.1627, u'time': u'2018-01-19T15:28:19.563256Z'}}
{u'tick': {u'ask': 1.16287, u'instrument': u'EUR_USD', u'bid': 1.16274, u'time': u'2018-01-19T15:28:28.021045Z'}}
{u'tick': {u'ask': 1.16287, u'instrument': u'EUR_USD', u'bid': 1.16273, u'time': u'2018-01-19T15:28:30.982725Z'}}
{u'tick': {u'ask': 1.16285, u'instrument': u'EUR_USD', u'bid': 1.16272, u'time': u'2018-01-19T15:28:52.493297Z'}}
{u'tick': {u'ask': 1.16283, u'instrument': u'EUR_USD', u'bid': 1.16272, u'time': u'2018-01-19T15:29:12.854066Z'}}
Executing order!
{
    "instrument" : "EUR_USD",
    "time" : "2018-01-19T15:29:14.000000Z",
    "price" : 1.16283,
    "tradeOpened" : {
        "id" : 821102691,
        "units" : 10000,
        "side" : "buy",
        "takeProfit" : 0,
        "stopLoss" : 0,
        "trailingStop" : 0
    },
    "tradesClosed" : [],
    "tradeReduced" : {}
}
{u'tick': {u'ask': 1.16284, u'instrument': u'EUR_USD', u'bid': 1.1627, u'time': u'2018-01-19T15:29:17.817401Z'}}
{u'tick': {u'ask': 1.16283, u'instrument': u'EUR_USD', u'bid': 1.1627, u'time': u'2018-01-19T15:29:17.920900Z'}}
        

Le prime cinque righe mostrano i dati del tick JSON restituiti da OANDA con prezzi bid / ask. Successivamente puoi vedere l’ordine di esecuzione! così come la risposta JSON restituita da OANDA confermando l’apertura di un’operazione di acquisto per 10.000 unità di EUR / USD e il prezzo a cui è stata raggiunta.

Questo continuerà a funzionare all’infinito fino a quando non si ucciderà il programma con un comando “Ctrl-Z” o simile.

Prossimi Passi

Negli articoli successivi apporteremo alcuni miglioramenti fondamentali, tra cui:

  • Strategie reali – Strategie forex corrette che generano segnali redditizi.
  • Infrastruttura per il live trading – Implementazione del server remoto e sistema di trading monitorato 24 ore su 24, 7 giorni su 7, con funzionalità di avvio / arresto.
  • Portafoglio e gestione del rischio – Portafoglio e sovrapposizioni di rischio per tutti gli ordini suggeriti dalla strategia.
  • Strategie multiple – costruzione di un portafoglio di strategie che si integrano nella copertura della gestione del rischio

Come con il backtester basato sugli eventi per il mercato azionario, dobbiamo anche creare un modulo di backtesting forex. Ciò ci consentirà di effettuare ricerche rapide e semplificare l’implementazione delle strategie.

 

Per il codice completo riportato in questo articolo, utilizzando il modulo di backtesting event-driven per il forex (DTForex) si può consultare il seguente repository di github:
https://github.com/datatrading-info/DTForex

Benvenuto su DataTrading!

Sono Gianluca, ingegnere software e data scientist. Sono appassionato di coding, finanza e trading. Leggi la mia storia.

Ho creato DataTrading per aiutare le altre persone ad utilizzare nuovi approcci e nuovi strumenti, ed applicarli correttamente al mondo del trading.

DataTrading vuole essere un punto di ritrovo per scambiare esperienze, opinioni ed idee.

SCRIVIMI SU TELEGRAM

Per informazioni, suggerimenti, collaborazioni...

Torna in alto
Scroll to Top