Creșterea tranzacționării automate: Mașinile care tranzacționează S&P 500

Publicat: 2022-03-11

În zilele noastre, peste 60 la sută din activitățile de tranzacționare cu diferite active (cum ar fi acțiuni, futures pe index, mărfuri) nu mai sunt realizate de comercianți „ființe umane”, ci se bazează pe tranzacționare automată. Există programe specializate bazate pe algoritmi specifici care cumpără și vând automat active pe diferite piețe, menite să obțină un randament pozitiv pe termen lung.

În acest articol, vă voi arăta cum să preziceți, cu o bună acuratețe, cum ar trebui plasată următoarea tranzacție pentru a obține un câștig pozitiv. Pentru acest exemplu, ca activ de bază pentru tranzacționare, am selectat indicele S&P 500, media ponderată a 500 de companii din SUA cu capitalizare mai mare. O strategie foarte simplă de implementat este să cumpărați indicele S&P 500 atunci când Wall Street Exchange începe tranzacționarea, la 9:30 AM, și să îl vindeți în sesiunea de închidere la 4:00 PM, ora Estului. Dacă prețul de închidere al indicelui este mai mare decât prețul de deschidere, există un câștig pozitiv, în timp ce un câștig negativ s-ar obține dacă prețul de închidere este mai mic decât prețul de deschidere. Deci întrebarea este: de unde știm dacă sesiunea de tranzacționare se va termina cu un preț de închidere mai mare decât prețul de deschidere? Machine Learning este un instrument puternic pentru a realiza o sarcină atât de complexă și poate fi un instrument util pentru a ne sprijini în decizia de tranzacționare.

Machine Learning este noua frontieră a multor aplicații utile din viața reală. Tranzacționarea financiară este una dintre acestea și este folosită foarte des în acest sector. Un concept important despre Machine Learning este că nu trebuie să scriem cod pentru orice fel de reguli posibile, cum ar fi recunoașterea modelelor. Acest lucru se datorează faptului că fiecare model asociat cu Machine Learning învață din datele în sine și apoi poate fi folosit ulterior pentru a prezice date noi nevăzute.

Disclaimer : Scopul acestui articol este de a arăta cum să antrenați metodele de învățare automată, iar în exemplele de cod furnizate nu sunt explicate toate funcțiile. Acest articol nu are scopul de a permite unei singure să copieze și să lipească tot codul și să ruleze aceleași teste furnizate, deoarece lipsesc unele detalii care au fost în afara domeniului de aplicare al articolului. De asemenea, sunt necesare cunoștințe de bază despre Python. Intenția principală a articolului este de a arăta un exemplu despre modul în care învățarea automată poate fi eficientă pentru a prezice cumpărările și vânzările în sectorul financiar. Cu toate acestea, comerțul cu bani reali înseamnă să ai multe alte abilități, cum ar fi managementul banilor și managementul riscului. Acest articol este doar o mică parte din „imaginea de ansamblu”.

Creați primul program de tranzacționare automată a datelor financiare

Deci, doriți să vă creați primul program pentru a analiza datele financiare și a prezice tranzacția corectă? Lasă-mă să-ți arăt cum. Voi folosi codul Python pentru Machine Learning și vom folosi datele istorice de la serviciul Yahoo Finance. După cum am menționat anterior, datele istorice sunt necesare pentru a antrena modelul înainte de a face predicțiile noastre.

Pentru a începe, trebuie să instalăm:

  • Python și, în special, sugerez să utilizați notebook-ul IPython.
  • Pachetul Yahoo Finance Python (numele exact este yahoo-finance ) prin comanda terminalului: pip install yahoo-finance .
  • O versiune de probă gratuită a pachetului Machine Learning numit GraphLab. Nu ezitați să verificați documentația utilă a bibliotecii respective.

Rețineți că doar o parte din GraphLab este open source, SFrame, așa că pentru a folosi întreaga bibliotecă avem nevoie de o licență. Există o licență gratuită de 30 de zile și o licență non-comercială pentru studenții sau cei care participă la competițiile Kaggle. Din punctul meu de vedere, GraphLab Create este o bibliotecă foarte intuitivă și ușor de utilizat pentru a analiza datele și a antrena modele de învățare automată.

Săpat în codul Python

Să cercetăm ceva cod Python pentru a vedea cum să descărcați date financiare de pe Internet. Vă sugerez să folosiți notebook-ul IPython pentru a testa următorul cod, deoarece IPython are multe avantaje în comparație cu un IDE tradițional, mai ales atunci când trebuie să combinăm codul sursă, codul de execuție, datele tabelului și diagramele împreună pe același document. Pentru o scurtă explicație despre utilizarea blocnotesului IPython, vă rugăm să consultați articolul Introducere în blocnotesul IPython.

Deci, să creăm un nou notebook IPython și să scriem un cod pentru a descărca prețurile istorice ale indexului S&P 500. Rețineți, dacă preferați să utilizați alte instrumente, puteți începe cu un nou proiect Python în IDE-ul preferat.

 import graphlab as gl from __future__ import division from datetime import datetime from yahoo_finance import Share # download historical prices of S&P 500 index today = datetime.strftime(datetime.today(), "%Y-%m-%d") stock = Share('^GSPC') # ^GSPC is the Yahoo finance symbol to refer S&P 500 index # we gather historical quotes from 2001-01-01 up to today hist_quotes = stock.get_historical('2001-01-01', today) # here is how a row looks like hist_quotes[0] {'Adj_Close': '2091.580078', 'Close': '2091.580078', 'Date': '2016-04-22', 'High': '2094.320068', 'Low': '2081.199951', 'Open': '2091.48999', 'Symbol': '%5eGSPC', 'Volume': '3790580000'}

Aici, hist_quotes este o listă de dicționare, iar fiecare obiect de dicționar este o zi de tranzacționare cu valori Open , High , Low , Close , Adj_close , Volume , Symbol și Date . În timpul fiecărei zile de tranzacționare, prețul se modifică de obicei începând de la prețul de deschidere Open la prețul de închidere Close și atingând o valoare maximă și minimă High și Low . Trebuie să-l citim și să creăm liste cu fiecare dintre cele mai relevante date. De asemenea, datele trebuie să fie ordonate după cele mai recente valori la început, așa că trebuie să le inversăm:

 l_date = [] l_open = [] l_high = [] l_low = [] l_close = [] l_volume = [] # reverse the list hist_quotes.reverse() for quotes in hist_quotes: l_date.append(quotes['Date']) l_open.append(float(quotes['Open'])) l_high.append(float(quotes['High'])) l_low.append(float(quotes['Low'])) l_close.append(float(quotes['Close'])) l_volume.append(int(quotes['Volume']))

Putem împacheta toate ghilimele descărcate într-un obiect SFrame , care este un cadru de date foarte scalabil bazat pe coloană, și este comprimat. Unul dintre avantaje este că poate fi, de asemenea, mai mare decât cantitatea de RAM, deoarece este suportat de disc. Puteți verifica documentația pentru a afla mai multe despre SFrame.

Deci, să stocăm și apoi să verificăm datele istorice:

 qq = gl.SFrame({'datetime' : l_date, 'open' : l_open, 'high' : l_high, 'low' : l_low, 'close' : l_close, 'volume' : l_volume}) # datetime is a string, so convert into datetime object qq['datetime'] = qq['datetime'].apply(lambda x:datetime.strptime(x, '%Y-%m-%d')) # just to check if data is sorted in ascending mode qq.head(3)
închide datetime înalt scăzut deschis volum
1283,27 2001-01-02 00:00:00 1320,28 1276,05 1320,28 1129400000
1347,56 2001-01-03 00:00:00 1347,76 1274,62 1283,27 1880700000
1333,34 2001-01-04 00:00:00 1350,24 1329,14 1347,56 2131000000

Acum putem salva datele pe disc cu metoda SFrame save , după cum urmează:

 qq.save(“SP500_daily.bin”) # once data is saved, we can use the following instruction to retrieve it qq = gl.SFrame(“SP500_daily.bin/”)

Să vedem cum arată S&P 500

Pentru a vedea cum vor arăta datele S&P 500 încărcate, putem folosi următorul cod:

 import matplotlib.pyplot as plt %matplotlib inline # only for those who are using IPython notebook plt.plot(qq['close'])

Rezultatul codului este următorul grafic:

Graficul S&P 500, în general în creștere în timp, așa cum este redat de codul Python de mai sus.

Antrenarea unor modele de învățare automată

Adăugarea rezultatului

După cum am afirmat în partea introductivă a acestui articol, scopul fiecărui model este de a prezice dacă prețul de închidere va fi mai mare decât prețul de deschidere. Prin urmare, în acest caz, putem obține un randament pozitiv atunci când cumpărăm activul suport. Deci, trebuie să adăugăm o coloană de outcome pe datele noastre, care va fi target sau variabila predicted . Fiecare rând al acestei noi coloane va fi:

  • +1 pentru o zi Up cu un preț de Closing mai mare decât prețul de Opening .
  • -1 pentru o zi Down cu un preț de Closing mai mic decât prețul de Opening .
 # add the outcome variable, 1 if the trading session was positive (close>open), 0 otherwise qq['outcome'] = qq.apply(lambda x: 1 if x['close'] > x['open'] else -1) # we also need to add three new columns 'ho' 'lo' and 'gain' # they will be useful to backtest the model, later qq['ho'] = qq['high'] - qq['open'] # distance between Highest and Opening price qq['lo'] = qq['low'] - qq['open'] # distance between Lowest and Opening price qq['gain'] = qq['close'] - qq['open']

Deoarece trebuie să evaluăm cu câteva zile înainte de ultima zi de tranzacționare, trebuie să întârziem datele cu una sau mai multe zile. Pentru acest tip de operație de întârziere , avem nevoie de un alt obiect din pachetul GraphLab numit TimeSeries . TimeSeries are o shift de metodă care întârzie datele cu un anumit număr de rânduri.

 ts = gl.TimeSeries(qq, index='datetime') # add the outcome variable, 1 if the bar was positive (close>open), 0 otherwise ts['outcome'] = ts.apply(lambda x: 1 if x['close'] > x['open'] else -1) # GENERATE SOME LAGGED TIMESERIES ts_1 = ts.shift(1) # by 1 day ts_2 = ts.shift(2) # by 2 days # ...etc.... # it's an arbitrary decision how many days of lag are needed to create a good forecaster, so # everyone can experiment by his own decision

Adăugarea de predictori

Predictorii sunt un set de variabile caracteristice care trebuie alese pentru a antrena modelul și a prezice rezultatul nostru. Deci, alegerea factorului de prognoză este esențială, dacă nu cea mai importantă, componentă a prognozatorului.

Doar pentru a numi câteva exemple, un factor de luat în considerare poate fi dacă închiderea de astăzi este mai mare decât închiderea de ieri și aceasta ar putea fi prelungită cu închiderea în două zile anterioare etc. O alegere similară poate fi tradusă cu următorul cod:

 ts['feat1'] = ts['close'] > ts_1['close'] ts['feat2'] = ts['close'] > ts_2['close']

După cum se arată mai sus, am adăugat două coloane de caracteristici noi, feat1 și feat2 în setul nostru de date ( ts ) care conține 1 dacă comparația este adevărată și 0 în caz contrar.

Acest articol are scopul de a oferi un exemplu de învățare automată aplicată sectorului financiar. Prefer să mă concentrez asupra modului în care modelele de învățare automată pot fi utilizate cu datele financiare și nu vom intra în detalii despre cum să alegem factorii potriviți pentru a antrena modelele. Este prea exhaustiv să explic de ce anumiți factori sunt utilizați în raport cu alții, din cauza creșterii considerabile a complexității. Cercetarea mea de muncă este de a studia multe ipoteze de alegere a factorilor pentru a crea un bun predictor. Deci, pentru început, vă sugerez să experimentați cu o mulțime de combinații diferite de factori, pentru a vedea dacă aceștia pot crește acuratețea modelului.

 # add_features is a helper function, which is out of the scope of this article, # and it returns a tuple with: # ts: a timeseries object with, in addition to the already included columns, also lagged columns # as well as some features added to train the model, as shown above with feat1 and feat2 examples # l_features: a list with all features used to train Classifier models # l_lr_features: a list all features used to train Linear Regression models ts, l_features, l_lr_features = add_features(ts) # add the gain column, for trading operations with LONG only positions. # The gain is the difference between Closing price - Opening price ts['gain'] = ts['close'] - ts['open'] ratio = 0.8 # 80% of training set and 20% of testing set training = ts.to_sframe()[0:round(len(ts)*ratio)] testing = ts.to_sframe()[round(len(ts)*ratio):]

Antrenarea unui model de arbore de decizie

GraphLab Create are o interfață foarte curată pentru a implementa modele de învățare automată. Fiecare model are o metodă create folosită pentru a se potrivi modelului cu un set de date de antrenament. Parametrii tipici sunt:

  • training - este un set de antrenament care conține coloane de caracteristici și o coloană țintă.
  • target - este numele coloanei care conține variabila țintă.
  • validation_set - este un set de date pentru monitorizarea performanței de generalizare a modelului. În cazul nostru, nu avem validation_set .
  • features - este o listă de nume de coloane ale caracteristicilor utilizate pentru antrenamentul modelului.
  • verbose - dacă este true , imprimați informațiile despre progres în timpul antrenamentului.

În timp ce alți parametri sunt tipici modelului în sine, cum ar fi:

  • max_depth - este adâncimea maximă a unui copac.

Cu următorul cod construim arborele nostru de decizie:

 max_tree_depth = 6 decision_tree = gl.decision_tree_classifier.create(training, validation_set=None, target='outcome', features=l_features, max_depth=max_tree_depth, verbose=False)

Măsurarea performanței modelului montat

Precizia este o măsură importantă pentru a evalua bunătatea prognozatorului. Este numărul de predicții corecte împărțit la numărul total de puncte de date. Deoarece modelul este echipat cu date de antrenament, acuratețea evaluată cu setul de antrenament este mai bună decât cea obținută cu un set de testare.

Precizia este fracția de predicții pozitive care sunt pozitive. Avem nevoie de precizie pentru a fi un număr mai aproape de 1 , pentru a obține o rată de câștig „perfectă”. decision_tree , ca un alt clasificator din pachetul GraphLab Create, are metoda sa de evaluate pentru a obține multe valori importante ale modelului adaptat.

Rechemarea cuantifică capacitatea unui clasificator de a prezice exemple pozitive. Reamintirea poate fi interpretată ca probabilitatea ca un exemplu pozitiv selectat aleatoriu să fie identificat corect de către clasificator. Avem nevoie ca precizia să fie un număr mai aproape de 1 , pentru a obține o rată de câștig „perfectă”.

Următorul cod va arăta acuratețea modelului montat atât cu setul de antrenament, cât și cu setul de testare:

 decision_tree.evaluate(training)['accuracy'], decision_tree.evaluate(testing)['accuracy'] (0.6077348066298343, 0.577373211963589)

După cum se arată mai sus, acuratețea modelului cu setul de testare este de aproximativ 57 la sută, ceea ce este cumva mai bine decât aruncarea unei monede (50 la sută).

Predictarea datelor

GraphLab Create are aceeași interfață pentru a prezice date de la diferite modele montate. Vom folosi metoda predict , care necesită un set de teste pentru a prezice variabila țintă, în cazul nostru outcome . Acum, putem prezice datele din setul de testare:

 predictions = decision_tree.predict(testing) # and we add the predictions column in testing set testing['predictions'] = predictions # let's see the first 10 predictions, compared to real values (outcome column) testing[['datetime', 'outcome', 'predictions']].head(10)
datetime rezultat previziuni
05-04-2013 00:00:00 -1 -1
08-04-2013 00:00:00 1 1
2013-04-09 00:00:00 1 1
10-04-2013 00:00:00 1 -1
2013-04-11 00:00:00 1 -1
2013-04-12 00:00:00 -1 -1
15-04-2013 00:00:00 -1 1
16-04-2013 00:00:00 1 1
17-04-2013 00:00:00 -1 -1
18-04-2013 00:00:00 -1 1

Fals pozitive sunt cazuri în care modelul prezice un rezultat pozitiv, în timp ce rezultatul real din setul de testare este negativ. Viceversa, fals-negative sunt cazurile în care modelul prezice un rezultat negativ în care rezultatul real din setul de testare este pozitiv.

Strategia noastră de tranzacționare așteaptă un rezultat prezis pozitiv pentru a cumpăra S&P 500 la prețul de Opening și a-l vinde la prețul de Closing , așa că speranța noastră este să avem cea mai mică rată a fals-pozitive pentru a evita pierderile. Cu alte cuvinte, ne așteptăm ca modelul nostru să aibă cea mai mare rată de precizie .

După cum putem vedea, există două fals negative (la 2013-04-10 și 2013-04-11) și două fals pozitive (la 2013-04-15 și 2013-04-18) în primele zece valori prezise ale set de testare.

Cu un calcul simplu, luând în considerare acest mic set de zece predicții:

  • precizie = 6/10 = 0,6 sau 60%
  • precizie = 3/5 = 0,6 sau 60%
  • reamintire = 3/5 = 0,6 sau 60%

Rețineți că, de obicei, numerele de mai sus sunt diferite unele de altele, dar în acest caz sunt aceleași.

Testarea inversă a modelului

Simulăm acum modul în care modelul s-ar tranzacționa folosind valorile sale prezise. Dacă rezultatul prezis este egal cu +1 , înseamnă că ne așteptăm la o zi Up . Cu o zi Up cumpărăm indicele la începutul sesiunii și vindem indicele la sfârșitul sesiunii în aceeași zi. În schimb, dacă rezultatul prezis este egal cu -1 , ne așteptăm la o zi Down , deci nu vom tranzacționa în timpul acelei zile.

Profitul și pierderea ( pnl ) pentru o tranzacție zilnică completă, numită și round turn , în acest exemplu este dat de:

  • pnl = Close - Open (pentru fiecare zi de tranzacționare)

Cu codul afișat mai jos, apelez la funcția de ajutor plot_equity_chart pentru a crea o diagramă cu curba câștigurilor cumulate (curba capitalurilor proprii). Fără a merge prea adânc, pur și simplu obține o serie de valori de profit și pierdere și calculează seria de sume cumulate de reprezentat.

 pnl = testing[testing['predictions'] == 1]['gain'] # the gain column contains (Close - Open) values # I have written a simple helper function to plot the result of all the trades applied to the # testing set and represent the total return expressed by the index basis points # (not expressed in dollars $) plot_equity_chart(pnl,'Decision tree model') 

Modelul arborelui decizional, care merge în general în sus și în dreapta, așa cum este redat de codul Python de mai sus.

 Mean of PnL is 1.843504 Sharpe is 1.972835 Round turns 511

Aici, Sharpe este raportul Sharpe anual, un indicator important al bunătății modelului de tranzacționare.

Raportul Sharpe este egal cu rădăcina pătrată a lui 252, apoi înmulțit cu media lui pnl împărțit la abaterea standard a lui pnl.

Luând în considerare tranzacțiile exprimate zi de zi, în timp ce mean este media listei de profit și pierdere, iar sd este abaterea standard. Pentru simplitate în formula descrisă mai sus, am considerat un randament fără risc egal cu 0.

Câteva elemente de bază despre tranzacționare

Tranzacționarea indicelui necesită cumpărarea unui activ, care este derivat direct din indice. Mulți brokeri reproduc indicele S&P 500 cu un produs derivat numit CFD (Contract for difference), care este un acord între două părți pentru a schimba diferența dintre prețul de deschidere și prețul de închidere al unui contract.

Exemplu : Cumpărați 1 CFD S&P 500 la Open (valoarea este 2000), vindeți-l la Close zilei (valoarea este 2020). Diferența, deci și câștigul, este de 20 de puncte. Dacă fiecare punct are o valoare de 25 USD:

  • Câștigul brut este 20 points x $25 = $500 cu 1 contract CFD.

Să presupunem că brokerul păstrează un derapaj de 0,6 puncte pentru propriul venit:

  • Câștigul net este (20 - 0.6) points x $25 = $485 .

Un alt aspect important de luat în considerare este evitarea pierderilor semnificative în cadrul unei tranzacții. Ele pot apărea ori de câte ori rezultatul prezis este +1 , dar valoarea rezultatului real este -1 , deci este un fals pozitiv . În acest caz, sesiunea de sfârșit se dovedește a fi o zi Down cu un preț de închidere mai mic decât cel de deschidere și obținem o pierdere.

Un ordin stop loss trebuie plasat pentru a proteja împotriva unei pierderi maxime pe care am tolera-o în cadrul unei tranzacții, iar un astfel de ordin este declanșat ori de câte ori prețul activului scade sub o valoare fixă ​​pe care am stabilit-o anterior.

Dacă ne uităm la seria temporală descărcată de pe Yahoo Finance la începutul acestui articol, fiecare zi are un preț Low , care este cel mai mic preț atins în acea zi. Dacă stabilim un nivel de stop de -3 puncte departe de prețul de Opening și Low - Open = -5 ordinul stop va fi declanșat, iar poziția deschisă va fi închisă cu o pierdere de -3 puncte în loc de -5 . Aceasta este o metodă simplă de a reduce riscul. Următorul cod reprezintă funcția mea de ajutor pentru a simula o tranzacție cu un nivel de oprire:

 # This is a helper function to trade 1 bar (for example 1 day) with a Buy order at opening session # and a Sell order at closing session. To protect against adverse movements of the price, a STOP order # will limit the loss to the stop level (stop parameter must be a negative number) # each bar must contains the following attributes: # Open, High, Low, Close prices as well as gain = Close - Open and lo = Low - Open def trade_with_stop(bar, slippage = 0, stop=None): """ Given a bar, with a gain obtained by the closing price - opening price it applies a stop limit order to limit a negative loss If stop is equal to None, then it returns bar['gain'] """ bar['gain'] = bar['gain'] - slippage if stop<>None: real_stop = stop - slippage if bar['lo']<=stop: return real_stop # stop == None return bar['gain']

Costuri de tranzacționare

Costurile de tranzacție sunt cheltuieli suportate la cumpărarea sau vânzarea titlurilor de valoare. Costurile de tranzacție includ comisioanele și spread-urile brokerilor (diferența dintre prețul plătit de dealer pentru un titlu și prețul plătit de cumpărător) și trebuie luate în considerare dacă dorim să ne testăm strategia, similar unui scenariu real. Derapajul în tranzacționarea acțiunilor apare adesea atunci când există o schimbare a spread-ului. În acest exemplu și pentru următoarele simulări în curs, costurile de tranzacționare sunt fixate astfel:

  • Alunecare = 0,6 puncte
  • Comision = 1$ pentru fiecare tranzacție (o rundă va costa 2$)

Doar pentru a scrie câteva cifre, dacă câștigul nostru brut ar fi de 10 puncte, 1 punct = 25 USD, deci 250 USD inclusiv costurile de tranzacționare, câștigul nostru net ar fi (10 - 0.6)*$25 - 2 = $233 .

Următorul cod arată o simulare a strategiei anterioare de tranzacționare cu un stop loss de -3 puncte. Curba albastră este curba randamentelor cumulate. Singurele costuri luate în considerare sunt derapajul (0,6 puncte), iar rezultatul este exprimat în puncte de bază (aceeași unitate de bază a valorilor S&P 500 descărcată de la Yahoo Finance).

 SLIPPAGE = 0.6 STOP = -3 trades = testing[testing['predictions'] == 1][('datetime', 'gain', 'ho', 'lo', 'open', 'close')] trades['pnl'] = trades.apply(lambda x: trade_with_stop(x, slippage=SLIPPAGE, stop=STOP)) plot_equity_chart(trades['pnl'],'Decision tree model') print("Slippage is %s, STOP level at %s" % (SLIPPAGE, STOP)) 

Model de arbore decizional, în general în sus și în dreapta, dar cu vârfuri mai puțin pronunțate, așa cum este redat de codul Python de mai sus.

 Mean of PnL is 2.162171 Sharpe is 3.502897 Round turns 511 Slippage is 0.6 STOP level at -3

Următorul cod este folosit pentru a face predicții într-un mod ușor diferit. Vă rugăm să acordați atenție metodei de predict care este apelată cu un parametru suplimentar output_type = “probability” . Acest parametru este utilizat pentru a returna probabilitățile valorilor prezise în loc de predicția lor de clasă ( +1 pentru un rezultat prezis pozitiv, -1 pentru un rezultat prezis negativ). O probabilitate mai mare sau egală cu 0.5 este asociată cu o valoare prezisă +1 și o valoare a probabilității mai mică de 0.5 este legată de o valoare prezisă de -1 . Cu cât această probabilitate este mai mare, cu atât avem mai multe șanse de a prezice o adevărată Up Day .

 predictions_prob = decision_tree.predict(testing, output_type = 'probability') # predictions_prob will contain probabilities instead of the predicted class (-1 or +1)

Acum testăm înapoi modelul cu o funcție de ajutor numită backtest_ml_model care calculează seria de returnări cumulative, inclusiv alunecarea și comisioanele, și trasează valorile acestora. Pentru concizie, fără a explica în detaliu funcția backtest_ml_model , detaliul important de evidențiat este că, în loc să filtram acele zile cu un outcome = 1 , așa cum am făcut în exemplul anterior, acum filtrăm acele predictions_prob egale sau mai mari decât un threshold = 0.5 , ca următorul:

 trades = testing[predictions_prob>=0.5][('datetime', 'gain', 'ho', 'lo', 'open', 'close')]

Amintiți-vă că câștigul net al fiecărei zile de tranzacționare este: Net gain = (Gross gain - SLIPPAGE) * MULT - 2 * COMMISSION .

O altă măsură importantă folosită pentru a evalua bunătatea unei strategii de tranzacționare este Maximum Drawdown . În general, măsoară cea mai mare scădere unică de la vârf la jos, a valorii unui portofoliu investit. În cazul nostru, este cea mai semnificativă scădere de la vârf în jos a curbei acțiunilor (avem doar un activ în portofoliu, S&P 500). Deci, având în vedere un SArray de profit și pierdere pnl , calculăm reducerea astfel:

 drawdown = pnl - pnl.cumulative_max() max_drawdown = min(drawdown)

În interiorul funcției de ajutor backtest_summary este calculată:

  • Retragere maximă (în dolari), așa cum se arată mai sus.
  • Precizie, cu metoda Graphlab.evaluation .
  • Precizie, cu metoda Graphlab.evaluation .
  • Recall, cu metoda Graphlab.evaluation .

Punând totul împreună, următorul exemplu arată curba capitalului propriu reprezentând randamentele cumulate ale strategiei modelului, cu toate valorile exprimate în dolari.

 model = decision_tree predictions_prob = model.predict(testing, output_type="probability") THRESHOLD = 0.5 bt_1_1 = backtest_ml_model(testing, predictions_prob, target='outcome', threshold=THRESHOLD, STOP=-3, MULT=25, SLIPPAGE=0.6, COMMISSION=1, plot_title='DecisionTree') backtest_summary(bt_1_1) 

Graficul DecisionTree, cu axa Y etichetată „dolari” și urcând până la 30.000, iar axa X etichetată „# of roundturns” și extinzându-se la 600, așa cum este redat de codul Python de mai sus. Datele grafice în sine sunt identice cu randarea anterioară.

 Mean of PnL is 54.054286 Sharpe is 3.502897 Round turns 511 Name: DecisionTree Accuracy: 0.577373211964 Precision: 0.587084148728 Recall: 0.724637681159 Max Drawdown: -1769.00025

Pentru a crește precizia valorilor prognozate, în loc de o probabilitate standard de 0.5 (50 la sută), alegem o valoare de prag mai mare, pentru a fi mai siguri că modelul prezice o zi Up .

 THRESHOLD = 0.55 # it's the minimum threshold to predict an Up day so hopefully a good day to trade bt_1_2 = backtest_ml_model(testing, predictions_prob, target='outcome', threshold=THRESHOLD, STOP=-3, MULT=25, SLIPPAGE=0.6, COMMISSION=1, plot_title='DecisionTree') backtest_summary(bt_1_2) 

Graficul DecisionTree, cu axa Y etichetată „dolari” și urcând până la 30.000, iar axa X etichetată „# of roundturns” și extinzându-se la doar 250 de data aceasta, așa cum este redat de codul Python de mai sus. Datele grafice în sine sunt similare cu randarea anterioară, dar și mai netezite.

 Mean of PnL is 118.244689 Sharpe is 6.523478 Round turns 234 Name: DecisionTree Accuracy: 0.560468140442 Precision: 0.662393162393 Recall: 0.374396135266 Max Drawdown: -1769.00025

După cum putem vedea din graficul de mai sus, curba acțiunilor este mult mai bună decât înainte (Sharpe este 6,5 în loc de 3,5), chiar și cu mai puține rotații.

Din acest moment, vom lua în considerare toate modelele următoare cu un prag mai mare decât o valoare standard.

Formarea unui clasificator logistic

Putem aplica cercetările noastre, așa cum am făcut anterior cu arborele de decizie, într-un model de clasificator logistic. GraphLab Create are aceeași interfață cu obiectul Logistic Classifier și vom apela metoda create pentru a construi modelul nostru cu aceeași listă de parametri. Mai mult decât atât, preferăm să prezicăm vectorul de probabilitate în loc de vectorul de clasă prezis (compus din +1 pentru un rezultat pozitiv și -1 pentru un rezultat negativ), astfel încât am avea un prag mai mare de 0.5 pentru a obține o precizie mai bună în prognoza.

 model = gl.logistic_classifier.create(training, target='outcome', features=l_features, validation_set=None, verbose=False) predictions_prob = model.predict(testing, 'probability') THRESHOLD = 0.6 bt_2_2 = backtest_ml_model(testing, predictions_prob, target='outcome', threshold=THRESHOLD, STOP=-3, plot_title=model.name()) backtest_summary(bt_2_2) 

Graficul LogisticClassifier, cu axa Y etichetată „dolari” și mergând de data aceasta până la 50.000, iar axa X etichetată „# of roundturns” și extinzându-se acum la 450, așa cum este redat de codul Python de mai sus. Datele grafice în sine sunt similare cu redarea anterioară în tendința sa generală.

 Mean of PnL is 112.704215 Sharpe is 6.447859 Round turns 426 Name: LogisticClassifier Accuracy: 0.638491547464 Precision: 0.659624413146 Recall: 0.678743961353 Max Drawdown: -1769.00025

În acest caz, există un rezumat foarte asemănător cu Decision Tree. La urma urmei, ambele modele sunt clasificatoare, ele prezic doar o clasă de rezultate binare ( +1 , -1 ).

Antrenarea unui model de regresie liniară

Principala diferență a acestui model este că se ocupă de valori continue în loc de clase binare, așa cum am menționat anterior. Nu trebuie să antrenăm modelul cu o variabilă țintă egală cu +1 pentru zilele în sus și -1 pentru zilele în jos , ținta noastră trebuie să fie o variabilă continuă. Deoarece dorim să anticipăm un câștig pozitiv sau, cu alte cuvinte, un preț de închidere mai mare decât prețul de deschidere , acum ținta trebuie să fie coloana de câștig a setului nostru de antrenament. De asemenea, lista de caracteristici trebuie să fie compusă din valori continue, cum ar fi anterioară Open , Close , etc.

Pentru concizie, nu voi intra în detalii despre cum să selectăm caracteristicile potrivite, deoarece acest lucru depășește scopul acestui articol, care este mai înclinat să arate cum ar trebui să aplicăm diferite modele de învățare automată asupra unui set de date. Lista parametrilor trecuți metodei create sunt:

  • training - este un set de antrenament care conține coloane de caracteristici și o coloană țintă.
  • target - este numele coloanei care conține variabila țintă.
  • validation_set - este un set de date pentru monitorizarea performanței de generalizare a modelului. În cazul nostru, nu avem validation_set .
  • features - este o listă de nume de coloane ale caracteristicilor utilizate pentru antrenamentul modelului, pentru acest model vom folosi un alt set referitor la modelele Clasificatorului.
  • verbose - dacă este true , va imprima informații despre progres în timpul antrenamentului.
  • max_iterations - este numărul maxim de treceri permise prin date. Mai multe treceri peste date pot duce la un model antrenat mai precis.
 model = gl.linear_regression.create(training, target='gain', features = l_lr_features, validation_set=None, verbose=False, max_iterations=100) predictions = model.predict(testing) # a linear regression model, predict continuous values, so we need to make an estimation of their # probabilities of success and normalize all values in order to have a vector of probabilities predictions_max, predictions_min = max(predictions), min(predictions) predictions_prob = (predictions - predictions_min)/(predictions_max - predictions_min)

Până acum, avem predicții care reprezintă SArray de câștiguri prezise, ​​în timp ce predictions_prob este SArray cu valorile predictions normalizate. Pentru a avea o precizie bună și un anumit număr de ture rotunde, comparabil cu modelele anterioare, am ales o valoare de prag de 0.4 . Pentru un predictions_prob mai mic de 0.4 , funcția de ajutor backtest_linear_model nu va deschide o tranzacție deoarece este așteptată o zi Down . În caz contrar, se va deschide o tranzacție.

 THRESHOLD = 0.4 bt_3_2 = backtest_linear_model(testing, predictions_prob, target='gain', threshold=THRESHOLD, STOP = -3, plot_title=model.name()) backtest_summary(bt_3_2) 

Grafic de regresie liniară, cu axa Y etichetată „dolari” și urcând până la 45.000, iar axa X etichetată „# of roundturns” și extinzându-se la 350, așa cum este redat de codul Python de mai sus. Datele grafice în sine sunt din nou similare, dar nu exact identice, cu redarea anterioară.

 Mean of PnL is 138.868280 Sharpe is 7.650187 Round turns 319 Name: LinearRegression Accuracy: 0.631989596879 Precision: 0.705329153605 Recall: 0.54347826087 Max Drawdown: -1769.00025

Antrenarea unui arbore amplificat

Așa cum am antrenat anterior un arbore de decizie, acum vom antrena un clasificator de arbore îmbunătățit cu aceiași parametri folosiți pentru alte modele de clasificator. În plus, am setat numărul de max_iterations = 12 pentru a crește numărul maxim de iterații pentru boosting. Fiecare iterație are ca rezultat crearea unui arbore suplimentar. De asemenea, am stabilit o valoare a pragului mai mare decât 0.5 pentru a crește precizia.

 model = gl.boosted_trees_classifier.create(training, target='outcome', features=l_features, validation_set=None, max_iterations=12, verbose=False) predictions_prob = model.predict(testing, 'probability') THRESHOLD = 0.7 bt_4_2 = backtest_ml_model(testing, predictions_prob, target='outcome', threshold=THRESHOLD, STOP=-3, plot_title=model.name()) backtest_summary(bt_4_2) 

Graficul BoostedTreesClassifier, cu axa Y etichetată „dolari” și urcând până la 25.000, iar axa X etichetată „# of roundturns” și extinzându-se la 250, așa cum este redat de codul Python de mai sus. Datele grafice în sine sunt din nou similare cu randarea anterioară, cu o creștere mai accentuată în jurul valorii de 175 pe axa X.

 Mean of PnL is 112.002338 Sharpe is 6.341981 Round turns 214 Name: BoostedTreesClassifier Accuracy: 0.563068920676 Precision: 0.682242990654 Recall: 0.352657004831 Max Drawdown: -1769.00025

Antrenarea unei păduri aleatorii

Acesta este ultimul nostru model antrenat, un clasificator de pădure aleatoriu, compus dintr-un ansamblu de arbori de decizie. Numărul maxim de arbori de utilizat în model este setat la num_trees = 10 , pentru a evita prea multă complexitate și supraadaptare.

 model = gl.random_forest_classifier.create(training, target='outcome', features=l_features, validation_set=None, verbose=False, num_trees = 10) predictions_prob = model.predict(testing, 'probability') THRESHOLD = 0.6 bt_5_2 = backtest_ml_model(testing, predictions_prob, target='outcome', threshold=THRESHOLD, STOP=-3, plot_title=model.name()) backtest_summary(bt_5_2) 

Graficul RandomForestClassifier, cu axa Y etichetată „dolari” și urcând până la 40.000, iar axa X etichetată „# of roundturns” și extinzându-se la 350, așa cum este redat de codul Python de mai sus. Datele grafice în sine sunt din nou similare cu randarea anterioară.

 Mean of PnL is 114.786962 sharpe is 6.384243 Round turns 311 Name: RandomForestClassifier Accuracy: 0.598179453836 Precision: 0.668810289389 Recall: 0.502415458937 Max Drawdown: -1769.00025

Collecting All the Models Together

Now we can join all the strategies together and see the overall result. It's interesting to see the summary of all Machine Learning models, sorted by their precision.

name accuracy precision round turns sharpe
LinearRegression 0.63 0.71 319 7.65
BoostedTreesClassifier 0.56 0.68 214 6.34
RandomForestClassifier 0.60 0.67 311 6.38
DecisionTree 0.56 0.66 234 6.52
LogisticClassifier 0.64 0.66 426 6.45

If we collect all the profit and loss for each one of the previous models in the array pnl , the following chart depicts the equity curve obtained by the sum of each profit and loss, day by day.

 Mean of PnL is 119.446463 Sharpe is 6.685744 Round turns 1504 First trading day 2013-04-09 Last trading day 2016-04-22 Total return 179647

Just to give some numbers, with about 3 years of trading, all models have a total gain of about 180,000 dollars. The maximum exposition is 5 CFD contracts in the market, but to reduce the risk they all are closed at the end of each day, so overnight positions are not allowed.

Statistics of the Aggregation of All Models Together

Since each model can open a trade, but we added 5 concurrent models together, during the same day there could be from 1 contract up to 5 CFD contracts. If all models agree to open trades during the same day, there is a high chance to have an Up day predicted. Moreover, we can group by the number of models that open a trade at the same time during the opening session of the day. Then we evaluate precision as a function of the number of concurrent models.

As we can see by the chart depicted above, the precision gets better as the number of models do agree to open a trade. The more models agree, the more precision we get. For instance, with 5 models triggered the same day, the chance to predict an Up day is greater than 85%.

Concluzie

Even in the financial world, Machine Learning is welcomed as a powerful instrument to learn from data and give us great forecasting tools. Each model shows different values of accuracy and precision, but in general, all models can be aggregated to achieve a better result than each one of them taken singularly. GraphLab Create is a great library, easy to use, scalable and able to manage Big Data very quickly. It implements different scientific and forecasting models, and there is a free license for students and Kaggle competitions.

Înrudit: O introducere în teoria învățării automate și aplicațiile sale: un tutorial vizual cu exemple

Additional disclosure: This article has been prepared solely for information purposes, and is not an offer to buy or sell or a solicitation of an offer to buy or sell any security or instrument or to participate in any particular trading strategy. Examples presented on these sites are for educational purposes only. Past results are not necessarily indicative of future results.