de Harry Sauers

Cum obțin date despre opțiuni gratuit

O introducere în web scraping pentru finanțare

Cum obtin date despre optiuni gratuit

Ți-ai dorit vreodată să poți accesa datele despre opțiunile istorice, dar să fii blocat de un paravan de plată? Ce se întâmplă dacă doriți doar pentru cercetare, distracție sau pentru a dezvolta o strategie de tranzacționare personală?

În acest tutorial, veți învăța cum să utilizați Python și BeautifulSoup pentru a răscoli datele financiare de pe web și a vă crea propriul set de date.

Noțiuni de bază

Ar trebui să aveți cel puțin o cunoștință de lucru despre Python și tehnologiile web înainte de a începe acest tutorial. Pentru a le construi, vă recomand cu tărie să verificați un site precum codecademie pentru a învăța noi abilități sau pentru a le peria pe cele vechi

În primul rând, să prezentăm IDE-ul preferat. În mod normal, folosesc PyCharm dar, pentru un script rapid ca acesta Repl.it va face și treaba. Adăugați o imprimare rapidă („Hello world”) pentru a vă asigura că mediul dvs. este configurat corect.

Acum trebuie să ne dăm seama de o sursă de date.

Din pacate, Datele minunate ale lanțului de opțiuni ale Cboe este destul de blocat, chiar și pentru cotațiile întârziate actuale. Din fericire, Yahoo Finance are date cu opțiuni suficient de solide aici. Îl vom folosi pentru acest tutorial, deoarece răzuitoarele web au adesea nevoie de o anumită conștientizare a conținutului, dar este ușor de adaptat pentru orice sursă de date doriți.

Dependențe

Nu avem nevoie de multe dependențe externe. Avem nevoie doar de modulele Solicitări și BeautifulSoup din Python. Adăugați acestea în partea de sus a programului:

from bs4 import BeautifulSoupimport requests

Creeaza o main metodă:

def main():  print(“Hello World!”)if __name__ == “__main__”:  main()

Scraping HTML

Acum sunteți gata să începeți răzuirea! Interior main(), adăugați aceste rânduri pentru a prelua pagina completă HTML:

data_url = “https://finance.yahoo.com/quote/SPY/options"data_html = requests.get(data_url).contentprint(data_html)

Aceasta obține pagina completă HTML conținut, astfel încât să putem găsi datele pe care le dorim în el. Simțiți-vă liber să alergați și să observați ieșirea.

Simțiți-vă liber să comentați declarații tipărite pe măsură ce mergeți – acestea sunt doar acolo pentru a vă ajuta să înțelegeți ce face programul la un anumit pas.

BeautifulSoup este instrumentul perfect pentru a lucra cu HTML date în Python. Să restrângem HTML doar la tabelele de prețuri ale opțiunilor, astfel încât să o putem înțelege mai bine:

 content = BeautifulSoup(data_html, “html.parser”) # print(content)
 options_tables = content.find_all(“table”) print(options_tables)

Asta este încă destul de puțin HTML – nu putem obține prea multe din asta, iar codul Yahoo nu este cel mai prietenos cu răzuitoarele web. Să îl împărțim în două tabele, pentru apeluri și plasări:

 options_tables = [] tables = content.find_all(“table”) for i in range(0, len(content.find_all(“table”))):   options_tables.append(tables[i])
 print(options_tables)

Datele Yahoo conțin opțiuni care sunt destul de adânci în interiorul și în afara banilor, ceea ce ar putea fi excelent în anumite scopuri. Mă interesează doar opțiunile aproape de bani, și anume cele două apeluri și cele două cele mai apropiate de prețul actual.

Să le găsim, folosind intrările din tabelul diferențial BeautifulSoup și Yahoo pentru opțiuni în bani și în afara banilor:

expiration = datetime.datetime.fromtimestamp(int(datestamp)).strftime(“%Y-%m-%d”)
calls = options_tables[0].find_all(“tr”)[1:] # first row is header
itm_calls = []otm_calls = []
for call_option in calls:    if “in-the-money” in str(call_option):  itm_calls.append(call_option)  else:    otm_calls.append(call_option)
itm_call = itm_calls[-1]otm_call = otm_calls[0]
print(str(itm_call) + “ nn “ + str(otm_call))

Acum, avem intrările în tabel pentru cele două opțiuni cele mai apropiate de banii din HTML. Să scriem datele de preț, volumul și volatilitatea implicită de la prima opțiune de apel:

 itm_call_data = [] for td in BeautifulSoup(str(itm_call), “html.parser”).find_all(“td”):   itm_call_data.append(td.text)
print(itm_call_data)
itm_call_info = {‘contract’: itm_call_data[0], ‘strike’: itm_call_data[2], ‘last’: itm_call_data[3],  ‘bid’: itm_call_data[4], ‘ask’: itm_call_data[5], ‘volume’: itm_call_data[8], ‘iv’: itm_call_data[10]}
print(itm_call_info)

Adaptați acest cod pentru următoarea opțiune de apel:

# otm callotm_call_data = []for td in BeautifulSoup(str(otm_call), “html.parser”).find_all(“td”):  otm_call_data.append(td.text)
# print(otm_call_data)
otm_call_info = {‘contract’: otm_call_data[0], ‘strike’: otm_call_data[2], ‘last’: otm_call_data[3],  ‘bid’: otm_call_data[4], ‘ask’: otm_call_data[5], ‘volume’: otm_call_data[8], ‘iv’: otm_call_data[10]}
print(otm_call_info)

Dă-i curs programului tău!

Acum aveți dicționare cu cele două opțiuni de apel aproape de bani. Este suficient doar să răzuiești tabelul cu opțiunile put pentru aceleași date:

puts = options_tables[1].find_all("tr")[1:]  # first row is header
itm_puts = []  otm_puts = []
for put_option in puts:    if "in-the-money" in str(put_option):      itm_puts.append(put_option)    else:      otm_puts.append(put_option)
itm_put = itm_puts[0]  otm_put = otm_puts[-1]
# print(str(itm_put) + " nn " + str(otm_put) + "nn")
itm_put_data = []  for td in BeautifulSoup(str(itm_put), "html.parser").find_all("td"):    itm_put_data.append(td.text)
# print(itm_put_data)
itm_put_info = {'contract': itm_put_data[0],                  'last_trade': itm_put_data[1][:10],                  'strike': itm_put_data[2], 'last': itm_put_data[3],                   'bid': itm_put_data[4], 'ask': itm_put_data[5], 'volume': itm_put_data[8], 'iv': itm_put_data[10]}
# print(itm_put_info)
# otm put  otm_put_data = []  for td in BeautifulSoup(str(otm_put), "html.parser").find_all("td"):    otm_put_data.append(td.text)
# print(otm_put_data)
otm_put_info = {'contract': otm_put_data[0],                  'last_trade': otm_put_data[1][:10],                  'strike': otm_put_data[2], 'last': otm_put_data[3],                   'bid': otm_put_data[4], 'ask': otm_put_data[5], 'volume': otm_put_data[8], 'iv': otm_put_data[10]}

Felicitări! Tocmai ați răscolit datele pentru toate opțiunile aproape de bani ale S&P 500 ETF și le puteți vizualiza astfel:

 print("nn") print(itm_call_info) print(otm_call_info) print(itm_put_info) print(otm_put_info)

Executați programul dvs. – ar trebui să primiți astfel de date tipărite pe consolă:

{‘contract’: ‘SPY190417C00289000’, ‘last_trade’: ‘2019–04–15’, ‘strike’: ‘289.00’, ‘last’: ‘1.46’, ‘bid’: ‘1.48’, ‘ask’: ‘1.50’, ‘volume’: ‘4,646’, ‘iv’: ‘8.94%’}{‘contract’: ‘SPY190417C00290000’, ‘last_trade’: ‘2019–04–15’, ‘strike’: ‘290.00’, ‘last’: ‘0.80’, ‘bid’: ‘0.82’, ‘ask’: ‘0.83’, ‘volume’: ‘38,491’, ‘iv’: ‘8.06%’}{‘contract’: ‘SPY190417P00290000’, ‘last_trade’: ‘2019–04–15’, ‘strike’: ‘290.00’, ‘last’: ‘0.77’, ‘bid’: ‘0.75’, ‘ask’: ‘0.78’, ‘volume’: ‘11,310’, ‘iv’: ‘7.30%’}{‘contract’: ‘SPY190417P00289000’, ‘last_trade’: ‘2019–04–15’, ‘strike’: ‘289.00’, ‘last’: ‘0.41’, ‘bid’: ‘0.40’, ‘ask’: ‘0.42’, ‘volume’: ‘44,319’, ‘iv’: ‘7.79%’}

Configurarea culegerii de date recurente

Yahoo, în mod implicit, returnează doar opțiunile pentru data specificată de dvs. Este această parte a adresei URL: https://finance.yahoo.com/quote/SPY/options?date=1555459200

Acesta este un timestamp Unix, deci va trebui să îl generăm sau să îl răzgândim, mai degrabă decât să îl codificăm în programul nostru.

Adăugați câteva dependențe:

import datetime, time

Să scriem un script rapid pentru a genera și verifica un timestamp Unix pentru următorul nostru set de opțiuni:

def get_datestamp():  options_url = “https://finance.yahoo.com/quote/SPY/options?date="  today = int(time.time())  # print(today)  date = datetime.datetime.fromtimestamp(today)  yy = date.year  mm = date.month  dd = date.day

Codul de mai sus conține adresa URL de bază a paginii pe care o scrapăm și generează un datetime.date obiect pe care să îl folosim în viitor.

Să creștem această dată cu o zi, astfel încât să nu primim opțiuni care au expirat deja.

dd += 1

Acum, trebuie să-l reconvertim într-un timestamp Unix și să ne asigurăm că este o dată valabilă pentru contractele de opțiuni:

 options_day = datetime.date(yy, mm, dd) datestamp = int(time.mktime(options_day.timetuple())) # print(datestamp) # print(datetime.datetime.fromtimestamp(options_stamp))
 # vet timestamp, then return if valid for i in range(0, 7):   test_req = requests.get(options_url + str(datestamp)).content   content = BeautifulSoup(test_req, “html.parser”)   # print(content)   tables = content.find_all(“table”)
 if tables != []:   # print(datestamp)   return str(datestamp) else:   # print(“Bad datestamp!”)   dd += 1   options_day = datetime.date(yy, mm, dd)   datestamp = int(time.mktime(options_day.timetuple()))  return str(-1)

Să ne adaptăm fetch_options metoda de a utiliza un timestamp dinamic pentru a prelua date despre opțiuni, mai degrabă decât ceea ce Yahoo dorește să ne dea ca implicit.

Schimbați această linie:

data_url = “https://finance.yahoo.com/quote/SPY/options"

La acest:

datestamp = get_datestamp()data_url = “https://finance.yahoo.com/quote/SPY/options?date=" + datestamp

Felicitări! Tocmai ați scos de pe web datele despre opțiunile din lumea reală.

Acum trebuie să facem câteva I / O de fișiere simple și să configurăm un cronometru pentru a înregistra aceste date în fiecare zi după închiderea pieței.

Îmbunătățirea programului

Redenumiți main() la fetch_options() și adăugați aceste linii în partea de jos:

options_list = {‘calls’: {‘itm’: itm_call_info, ‘otm’: otm_call_info}, ‘puts’: {‘itm’: itm_put_info, ‘otm’: otm_put_info}, ‘date’: datetime.date.fromtimestamp(time.time()).strftime(“%Y-%m-%d”)}return options_list

Creați o nouă metodă numită schedule(). Vom folosi acest lucru pentru a controla când scotam opțiuni, la fiecare douăzeci și patru de ore după închiderea pieței. Adăugați acest cod pentru a programa primul nostru job la următoarea închidere a pieței:

from apscheduler.schedulers.background import BackgroundScheduler
scheduler = BackgroundScheduler()
def schedule():  scheduler.add_job(func=run, trigger=”date”, run_date = datetime.datetime.now())  scheduler.start()

În dumneavoastră if __name__ == “__main__”: declarație, ștergeți main() și adăugați un apel la schedule() pentru a configura primul loc de muncă programat.

Creați o altă metodă numită run(). Aici ne vom ocupa de cea mai mare parte a operațiunilor noastre, inclusiv salvarea efectivă a datelor de piață. Adăugați acest lucru în corpul run():

  today = int(time.time()) date = datetime.datetime.fromtimestamp(today) yy = date.year mm = date.month dd = date.day
 # must use 12:30 for Unix time instead of 4:30 NY time next_close = datetime.datetime(yy, mm, dd, 12, 30)
 # do operations here “”” This is where we’ll write our last bit of code. “””
 # schedule next job scheduler.add_job(func=run, trigger=”date”, run_date = next_close)
 print(“Job scheduled! | “ + str(next_close))

Acest lucru permite codului nostru să se apeleze pe sine în viitor, astfel încât să îl putem pune pe un server și să construim datele noastre de opțiuni în fiecare zi. Adăugați acest cod pentru a prelua datele de fapt “”” This is where we’ll write our last bit of code. “””

options = {}
 # ensures option data doesn’t break the program if internet is out try:   if next_close > datetime.datetime.now():     print(“Market is still open! Waiting until after close…”)   else:     # ensures program was run after market hours     if next_close < datetime.datetime.now():      dd += 1       next_close = datetime.datetime(yy, mm, dd, 12, 30)       options = fetch_options()       print(options)       # write to file       write_to_csv(options)except:  print(“Check your connection and try again.”)

Salvarea datelor

Poate că ai observat asta write_to_csv nu este încă implementat. Fără griji – hai să ne ocupăm de asta aici:

def write_to_csv(options_data):  import csv  with open(‘options.csv’, ‘a’, newline=’n’) as csvfile:  spamwriter = csv.writer(csvfile, delimiter=’,’)  spamwriter.writerow([str(options_data)])

A curăța

Deoarece contractele de opțiuni sunt sensibile la timp, ar putea dori să adăugăm un câmp pentru data de expirare a acestora. Această capacitate nu este inclusă în codul HTML brut pe care l-am răscolit.

Adăugați această linie de cod pentru a salva și a forma data expirării în partea de sus a fetch_options():

expiration =  datetime.datetime.fromtimestamp(int(get_datestamp())).strftime("%Y-%m-%d")

Adăuga ‘expiration’: expiration până la sfârșitul fiecăruia option_info dicționar așa:

itm_call_info = {'contract': itm_call_data[0],  'strike': itm_call_data[2], 'last': itm_call_data[3],   'bid': itm_call_data[4], 'ask': itm_call_data[5], 'volume': itm_call_data[8], 'iv': itm_call_data[10], 'expiration': expiration}

Executați noul dvs. program – acesta va răzuie cele mai recente date despre opțiuni și îl va scrie într-un fișier .csv ca reprezentare șir a unui dicționar. Fișierul .csv va fi gata să fie analizat de un program de testare înapoi sau servit utilizatorilor printr-o aplicație web. Felicitări!