Автор оригинала: FreeCodeCapm Team.
Гарри Сауэрами
Введение в веб соскок для финансов
Вы хотели бы вы могли получить доступ к данным исторических вариантов, но заблокирован Paywall? Что, если вы просто хотите, чтобы исследовать, весело или разработать личную торговую стратегию?
В этом руководстве вы узнаете, как использовать Python и Beautifulsoup, чтобы соскрести финансовые данные из Интернета и создать свой собственный набор данных.
Начиная
Вы должны иметь хотя бы рабочие знания Python и Web Technologies перед началом этого урока. Чтобы построить их, я настоятельно рекомендую проверить сайт, как Кодекадемия изучать новые навыки или пошить старые.
Во-первых, давайте раскрутим свою любимую IDE. Обычно я использую Пычарм Но для быстрого сценария вроде этого Reft.it сделаю работу тоже. Добавьте быструю печать («Hello World»), чтобы ваша среда установлена правильно.
Теперь нам нужно выяснить источник данных.
К сожалению, Удивительные варианты цепных вариантов CBOE довольно заперт, даже для тока задержанных цитат. К счастью, Yahoo Finance имеет достаточно прочные варианты вариантов здесь Отказ Мы будем использовать его для этого руководства, поскольку веб-скребки часто нуждаются в некотором осведомленности содержимого, но он легко адаптируется для любого нужного источника данных.
Зависимости
Нам не нужно много внешних зависимостей. Нам просто нужны запросы и красивые модули в Python. Добавьте их в верхней части вашей программы:
from bs4 import BeautifulSoupimport requests
Создать Главная
Метод:
def main(): print("Hello World!")if __name__ == "__main__": main()
Соскоб HTML
Теперь вы готовы начать соскоб! Внутри Главная ()
Добавьте эти строки, чтобы получить полную страницу HTML
:
data_url = "https://finance.yahoo.com/quote/SPY/options"data_html = requests.get(data_url).contentprint(data_html)
Это выбирает полный HTML
Содержание, поэтому мы можем найти данные, которые мы хотим в нем. Не стесняйтесь давать ему пробежку и наблюдать за выходом.
Не стесняйтесь прокомментировать операторы печати, когда вы идете – это просто, чтобы помочь вам понять, что делает программа на любой заданный шаг.
CountrySoup – идеальный инструмент для работы с HTML
Данные в Python. Давайте сузить HTML
Чтобы просто таблицы ценообразования вариантов, чтобы мы могли лучше понять это:
content = BeautifulSoup(data_html, "html.parser") # print(content)
options_tables = content.find_all("table") print(options_tables)
Это все еще совсем немного HTML
– Мы не можем получить много этого, и код Yahoo не самый дружелюбный для веб-скребок. Давайте сломаемся на две таблицы, для звонков и ставит:
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)
Данные Yahoo содержатся параметры, которые довольно глубоки и вне денег, которые могут быть отличными в определенные цели. Я заинтересуюсь только следующим образом, а именно два звонка и два ставит ближе всего к текущей цене.
Давайте найдем их, используя Beautifulsoup и Diflial Staties Yahoo, для вариантов в деньгах и вне денег:
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) + " \n\n " + str(otm_call))
Теперь у нас есть записи в таблицу для двух вариантов, ближайших к деньгам в HTML
Отказ Давайте соскребеем данные ценообразования, объем и подразумеваемой волатильности из первого варианта вызовов:
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)
Адаптируйте этот код для следующего варианта вызова:
# 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)
Дайте свою программу беги!
Теперь у вас есть словари двух вариантов вызовов. Достаточно просто соскрести таблицу настроек параметров для этих же данных:
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) + " \n\n " + str(otm_put) + "\n\n")
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]}
Поздравляю! Вы просто соскабливаете данные для всех вариантов почти денег S & P 500 ETF и можете просмотреть их такие:
print("\n\n") print(itm_call_info) print(otm_call_info) print(itm_put_info) print(otm_put_info)
Дайте своей программе запустить – вы должны получить такие данные, напечатанные в консоли:
{'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%'}
Настройка повторяющихся сбора данных
Yahoo, по умолчанию, возвращает только параметры для уточнения даты. Это эта часть URL: https://finance.yahoo.com/quote/spy/options?date= 1555459200.
Это Timestamp Unix, поэтому нам понадобится сгенерировать или царапать, а не убрать его в нашей программе.
Добавьте некоторые зависимости:
import datetime, time
Давайте напишем быстрый скрипт для генерации и проверки временных меток Unix для нашего следующего набора параметров:
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
Приведенный выше код удерживает базовый URL страницы, который мы соскабливаем и генерируют datetime.date
объект для нас использовать в будущем.
Давайте увеличиваем эту дату на один день, поэтому мы не получим варианты, которые уже истекли.
dd += 1
Теперь нам нужно преобразовать его обратно в метеру UNIX и убедиться, что это действительная дата договоров опций:
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)
Давайте адаптироваться нашим fetch_options
Способ использования динамического временного времени для получения данных параметров, а не то, что Yahoo хочет дать нам по умолчанию.
Изменить эту строку:
data_url = "https://finance.yahoo.com/quote/SPY/options"
К этому:
datestamp = get_datestamp()data_url = "https://finance.yahoo.com/quote/SPY/options?date=" + datestamp
Поздравляю! Вы просто соскреблируете данные реальных вариантов из Интернета.
Теперь нам нужно сделать несколько простых файлов ввода/вывода и настроить таймер для записи этих данных каждый день после закрытия рынка.
Улучшение программы
Переименовать Главная ()
к fetch_options ()
и добавьте эти строки на дно:
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
Создать новый метод под названием Расписание ()
Отказ Мы будем использовать это для контроля, когда мы будем царапать для вариантов, каждые двадцать четыре часа после закрытия рынка. Добавьте этот код, чтобы запланировать нашу первую работу на следующем рынке.
from apscheduler.schedulers.background import BackgroundScheduler
scheduler = BackgroundScheduler()
def schedule(): scheduler.add_job(func=run, trigger="date", run_date = datetime.datetime.now()) scheduler.start()
В вашем Если __name__:
Заявление, удалить Главная ()
и добавить звонок в Расписание ()
Чтобы настроить свою первую запланированную работу.
Создайте другой метод под названием Беги ()
Отказ Именно здесь мы обрабатываем основную часть нашей операции, в том числе на самом деле спасение рыночных данных. Добавьте это в корпус Беги ()
:
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))
Это позволяет нашему коду позвонить себе в будущем, поэтому мы можем просто поставить его на сервер и настроить данные наших вариантов каждый день. Добавьте этот код, чтобы на самом деле получать данные под «Вот где мы напишем наш последний бит кода».
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.")
Сохранение данных
Возможно, вы заметили, что write_to_csv
еще не реализован. Не волнуйтесь – давайте позаботимся об этом здесь:
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)])
Убираться
По мере того, как договоры параметров чувствительны к времени, мы можем захотеть добавить поле для их истечения срока годности. Эта возможность не включена в RAW HTML, мы выскабливаем.
Добавьте эту строку кода, чтобы сохранить и отформатировать дату истечения срока действия вверху fetch_options ()
:
expiration = datetime.datetime.fromtimestamp(int(get_datestamp())).strftime("%Y-%m-%d")
Добавить «Истечение»: срок действия
до конца каждого Option_info
Словарь вроде так:
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}
Дайте свою новую программу запустить – он будет соскребать последние данные параметров и записывать его в файл .csv в виде строкового представления словаря. Файл .csv будет готов проанализировать программу Backtesting или обслуживаемую пользователям через WebApp. Поздравляю!