Python yfinance: как получить финансовые данные Yahoo в своем коде

Пройдите тест, узнайте какой профессии подходите

Я предпочитаю
0%
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы

Для кого эта статья:

  • начинающие и опытные программисты, интересующиеся финансами
  • финансовые аналитики и исследователи
  • студенты и профессионалы, желающие освоить автоматизацию сбора финансовых данных

Финансовые данные — кровь цифровой экономики. Но как получить доступ к этому драгоценному ресурсу без подписок на дорогостоящие сервисы? Python-библиотека yfinance разрушает барьеры между разработчиками и сокровищницей финансовых данных Yahoo Finance. С несколькими строками кода вы получаете доступ к историческим котировкам, фундаментальным показателям и актуальным рыночным данным. Пришло время прекратить копировать таблицы вручную и автоматизировать сбор финансовой информации на профессиональном уровне. 🚀

Хотите освоить Python с нуля и научиться работать с финансовыми данными как профессионал? Курс «Python-разработчик» с нуля от Skypro даст вам не только фундаментальные знания языка, но и практические навыки работы с библиотеками для анализа данных, включая yfinance. Вы научитесь создавать собственные финансовые дашборды и системы анализа рынка, которые помогут принимать взвешенные инвестиционные решения. Инвестиция в знания — лучшая стратегия в мире финансов!

Возможности библиотеки Python yfinance для финансового анализа

Библиотека yfinance — это неофициальный, но самый популярный инструмент для доступа к данным Yahoo Finance через Python. После прекращения поддержки официального API от Yahoo в 2017 году, yfinance стал незаменимым решением для получения биржевой информации. В 2025 году библиотека предлагает расширенный функционал и продолжает активно поддерживаться сообществом разработчиков. 📊

Ключевые возможности yfinance:

  • Загрузка исторических данных о ценах акций, облигаций, ETF и криптовалют
  • Доступ к фундаментальным показателям компаний (P/E, EPS, рыночная капитализация)
  • Получение финансовых отчетов: балансы, отчеты о прибылях и убытках
  • Информация о дивидендах и корпоративных событиях
  • Данные по опционам с различными сроками экспирации
  • Возможность массовой загрузки данных по нескольким тикерам
  • Интеграция с pandas для удобной обработки полученной информации

Библиотека решает критическую проблему доступа к актуальным биржевым данным с минимальными усилиями по внедрению. Ценность этого инструмента сложно переоценить, особенно учитывая, что аналогичные платные API могут стоить до нескольких тысяч долларов в год.

ФункциональностьyfinanceПлатные альтернативы
Исторические данные✅ (с ограничениями по частоте)✅ (с высоким разрешением)
Фундаментальный анализ
Реальные данные⚠️ (задержка 15-20 мин)✅ (в реальном времени)
СтоимостьБесплатно$50-$2000/месяц
Лимиты запросов⚠️ (есть ограничения)✅ (высокие лимиты)
ПоддержкаСообществоПрофессиональная

Несмотря на некоторые ограничения по сравнению с премиальными решениями, yfinance остается золотой серединой для большинства задач анализа рынка, алгоритмической торговли и образовательных целей. В 2025 году разработчики библиотеки внедрили механизмы кэширования и интеллектуальной ротации запросов, что позволило значительно снизить риск блокировки со стороны Yahoo.

Алексей Корнеев, ведущий аналитик-разработчик

Когда я начинал строить свою первую систему скрининга акций, бюджет был ограничен, а премиальные API казались недосягаемой роскошью. Знакомство с yfinance изменило правила игры. Вместо ручного сбора данных по 500 компаниям S&P500, я написал скрипт, который за 10 минут собирал всю необходимую информацию и заполнял мою базу данных. Особенно ценной оказалась возможность получать данные финансовой отчетности — я смог реализовать собственный алгоритм оценки недооцененных компаний. В итоге система не только сэкономила мне десятки часов работы, но и помогла обнаружить несколько перспективных акций, которые за год выросли более чем на 40%.

Кинга Идем в IT: пошаговый план для смены профессии

Установка и настройка yfinance в Python-окружении

Установка yfinance предельно проста — библиотека доступна через менеджер пакетов pip и совместима с большинством версий Python (3.7 и выше). Для начала работы достаточно выполнить одну команду в терминале: 💻

pip install yfinance

Если вы используете Anaconda, альтернативный вариант установки:

conda install -c conda-forge yfinance

Для обеспечения стабильной работы рекомендуется также установить дополнительные зависимости:

pip install pandas numpy matplotlib requests-cache

После установки библиотеки имеет смысл сразу настроить кэширование запросов, чтобы избежать потенциальных блокировок при частых обращениях к API Yahoo Finance:

Python
Скопировать код
import requests_cache
from datetime import timedelta

# Настройка кэширования запросов на 4 часа
session = requests_cache.CachedSession('yfinance.cache', expire_after=timedelta(hours=4))
session.headers['User-agent'] = 'my-program/1.0'

import yfinance as yf
yf.set_yfin_session(session)

Для продвинутых пользователей, работающих с большими объемами данных, рекомендуется также настроить параметры многопоточности для ускорения загрузки информации по множеству тикеров:

Python
Скопировать код
import yfinance as yf
import concurrent.futures

# Настройка многопоточности для массовой загрузки данных
def download_ticker(ticker):
return yf.Ticker(ticker).history(period="1y")

tickers = ["AAPL", "MSFT", "GOOG", "AMZN", "FB"]
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
data = {ticker: result for ticker, result in 
zip(tickers, executor.map(download_ticker, tickers))}

При работе с yfinance полезно учитывать следующие практические аспекты настройки:

  • Устанавливайте интервалы между запросами для предотвращения блокировки (минимум 0.5-1 секунда между запросами)
  • Используйте прокси-ротацию при работе с большими объемами данных
  • Применяйте экспоненциальный backoff при повторных попытках после ошибок соединения
  • Настройте логирование для отслеживания проблем с получением данных
  • Учитывайте часовые пояса при работе с временными рядами (yfinance возвращает данные в UTC)

Получение биржевых данных Yahoo через API yfinance

После настройки окружения можно приступать к получению финансовых данных. Библиотека yfinance предлагает два основных способа работы: через объект Ticker для детального анализа отдельных инструментов и через функцию download для массовой загрузки исторических данных. 📈

Базовый пример получения исторических данных по одному тикеру:

Python
Скопировать код
import yfinance as yf

# Создание объекта тикера для Apple
aapl = yf.Ticker("AAPL")

# Получение исторических данных за последний год
hist_data = aapl.history(period="1y")

# Вывод первых строк данных
print(hist_data.head())

Результатом будет pandas DataFrame с колонками Open, High, Low, Close, Volume и Dividends. Для получения данных можно использовать различные параметры периода и интервала:

ПараметрОписаниеПримеры значений
periodПериод для загрузки данных"1d", "5d", "1mo", "3mo", "6mo", "1y", "2y", "5y", "10y", "ytd", "max"
intervalИнтервал между точками данных"1m", "2m", "5m", "15m", "30m", "60m", "90m", "1h", "1d", "5d", "1wk", "1mo", "3mo"
startДата начала периода"2020-01-01"
endДата окончания периода"2023-12-31"
actionsВключить корпоративные событияTrue, False
auto_adjustАвтоматическая корректировка ценTrue, False
prepostВключать данные до/после торговых сессийTrue, False

Для получения фундаментальных данных о компании можно использовать различные свойства объекта Ticker:

Python
Скопировать код
import yfinance as yf
import pprint

# Создание объекта тикера для Tesla
tsla = yf.Ticker("TSLA")

# Получение основной информации о компании
info = tsla.info
pprint.pprint({
"Name": info.get("shortName"),
"Sector": info.get("sector"),
"Industry": info.get("industry"),
"Market Cap": info.get("marketCap"),
"Forward P/E": info.get("forwardPE"),
"Dividend Yield": info.get("dividendYield"),
"Profit Margins": info.get("profitMargins")
})

# Получение аналитических рекомендаций
print(tsla.recommendations)

# Получение крупных акционеров
print(tsla.major_holders)

# Получение институциональных держателей
print(tsla.institutional_holders)

Массовая загрузка данных по нескольким тикерам одновременно может быть выполнена с помощью функции download:

Python
Скопировать код
import yfinance as yf

# Загрузка данных для нескольких тикеров сразу
data = yf.download(
tickers=["AAPL", "MSFT", "GOOG", "AMZN"],
period="1mo",
interval="1d",
group_by="ticker"
)

# Вывод структуры данных
print(data.head())

Получение данных по опционам для анализа волатильности и настроений рынка:

Python
Скопировать код
import yfinance as yf

# Создание объекта тикера для SPY (ETF на S&P 500)
spy = yf.Ticker("SPY")

# Получение доступных дат экспирации
expiration_dates = spy.options

# Вывод доступных дат
print(f"Доступные даты экспирации: {expiration_dates}")

# Получение данных по опционам для первой доступной даты
options_chain = spy.option_chain(expiration_dates[0])

# Вывод данных по колл-опционам
print(options_chain.calls.head())

При работе с API Yahoo Finance через yfinance важно понимать некоторые ограничения:

  • Существует неофициальный лимит на количество запросов (около 2000 в час с одного IP-адреса)
  • Некоторые данные могут иметь задержку до 15-20 минут относительно рыночных
  • Не все типы данных одинаково надежно доступны для всех рынков (особенно вне США)
  • При загрузке минутных данных глубина истории ограничена (обычно 7 днями)
  • Возможны временные сбои в работе API, которые требуют обработки исключений

Анализ и визуализация финансовых данных с yfinance

Получение данных — лишь первый шаг. Настоящая ценность библиотеки yfinance раскрывается при анализе и визуализации финансовой информации. Благодаря совместимости с экосистемой pandas, numpy и matplotlib, полученные данные легко превращаются в аналитические инсайты. 📉

Начнем с базового технического анализа — расчета скользящих средних для выявления тренда:

Python
Скопировать код
import yfinance as yf
import matplotlib.pyplot as plt
import pandas as pd

# Получение данных по Bitcoin за последний год
btc = yf.Ticker("BTC-USD")
data = btc.history(period="1y")

# Расчет скользящих средних
data['MA50'] = data['Close'].rolling(window=50).mean()
data['MA200'] = data['Close'].rolling(window=200).mean()

# Визуализация данных
plt.figure(figsize=(14, 7))
plt.plot(data.index, data['Close'], label='BTC-USD')
plt.plot(data.index, data['MA50'], label='50-дневная СС')
plt.plot(data.index, data['MA200'], label='200-дневная СС')
plt.title('Анализ тренда Bitcoin')
plt.xlabel('Дата')
plt.ylabel('Цена (USD)')
plt.legend()
plt.grid(True)
plt.show()

# Определение сигналов покупки/продажи по пересечению MA
data['Signal'] = 0
data['Signal'] = np.where(data['MA50'] > data['MA200'], 1, 0)
data['Position'] = data['Signal'].diff()

# Подсчет сигналов
signals = data[data['Position'] != 0]
print(f"Найдено {len(signals)} сигналов торговли")
print(signals[['Close', 'MA50', 'MA200', 'Position']])

Для анализа волатильности и оценки риска можно рассчитать исторический показатель Value at Risk (VaR) и построить распределение доходности:

Python
Скопировать код
import yfinance as yf
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats

# Получение данных по индексу S&P 500
sp500 = yf.Ticker("^GSPC")
data = sp500.history(period="5y")

# Расчет дневной доходности
data['Returns'] = data['Close'].pct_change()

# Удаление пропущенных значений
data = data.dropna()

# Построение гистограммы доходности
plt.figure(figsize=(10, 6))
sns.histplot(data['Returns'], kde=True, bins=50)
plt.title('Распределение дневной доходности S&P 500')
plt.xlabel('Дневная доходность')
plt.ylabel('Частота')

# Расчет Value at Risk (95%)
var_95 = data['Returns'].quantile(0.05)
plt.axvline(var_95, color='r', linestyle='dashed', linewidth=2)
plt.text(var_95, plt.ylim()[1]*0.9, f'VaR 95%: {var_95:.2%}', color='r')

# Проверка на нормальность распределения
k2, p = stats.normaltest(data['Returns'])
plt.text(plt.xlim()[1]*0.7, plt.ylim()[1]*0.8, 
f'Тест на нормальность:\np-value: {p:.4f}', 
bbox=dict(facecolor='white', alpha=0.5))

plt.grid(True)
plt.show()

# Расчет различных мер риска
print(f"Стандартное отклонение: {data['Returns'].std():.2%}")
print(f"Value at Risk (95%): {var_95:.2%}")
print(f"Expected Shortfall (95%): {data[data['Returns'] <= var_95]['Returns'].mean():.2%}")

Мария Соколова, финансовый аналитик-программист

Я занималась сравнительным анализом ETF для семейного фонда. Традиционно это была ручная работа с десятками таблиц Excel. Внедрение yfinance кардинально изменило процесс. Вместо недели рутинной работы, анализ стал занимать один день. Самым сложным оказалось сопоставление корреляций между различными классами активов в разные рыночные циклы. С помощью библиотеки я смогла загрузить 10-летнюю историю по 30 ETF и провести кластерный анализ, выявив скрытые взаимосвязи. Например, обнаружилось, что некоторые секторальные ETF, считавшиеся диверсифицирующими, на самом деле сильно коррелировали в периоды рыночной турбулентности. Этот анализ помог пересмотреть стратегию аллокации и увеличить устойчивость портфеля к просадкам.

Фундаментальный анализ также возможен с помощью yfinance. Например, можно автоматически создать скрининг акций по мультипликаторам:

Python
Скопировать код
import yfinance as yf
import pandas as pd

# Список тикеров для анализа (например, компоненты Dow Jones)
tickers = ['AAPL', 'MSFT', 'AMZN', 'TSLA', 'GOOG', 'BRK-B', 'UNH', 'JNJ', 
'XOM', 'V', 'WMT', 'JPM', 'PG', 'MA', 'NVDA', 'HD', 'MRK', 'CVX']

# Функция для получения ключевых метрик компании
def get_company_metrics(ticker):
stock = yf.Ticker(ticker)
info = stock.info

return {
'Name': info.get('shortName', 'N/A'),
'Sector': info.get('sector', 'N/A'),
'Market Cap (B)': info.get('marketCap', 0) / 1e9,
'P/E': info.get('trailingPE', 0),
'Forward P/E': info.get('forwardPE', 0),
'PEG': info.get('pegRatio', 0),
'P/S': info.get('priceToSalesTrailing12Months', 0),
'P/B': info.get('priceToBook', 0),
'EV/EBITDA': info.get('enterpriseToEbitda', 0),
'Dividend Yield (%)': info.get('dividendYield', 0) * 100 if info.get('dividendYield') else 0,
'ROE (%)': info.get('returnOnEquity', 0) * 100 if info.get('returnOnEquity') else 0,
'Debt/Equity': info.get('debtToEquity', 0) / 100 if info.get('debtToEquity') else 0,
'Current Ratio': info.get('currentRatio', 0),
'Analysts Rating': info.get('recommendationMean', 0)
}

# Сбор данных по всем компаниям
metrics_list = []
for ticker in tickers:
try:
metrics = get_company_metrics(ticker)
metrics['Ticker'] = ticker
metrics_list.append(metrics)
except Exception as e:
print(f"Error with {ticker}: {e}")

# Создание DataFrame с результатами
df = pd.DataFrame(metrics_list)

# Сортировка по P/E
df_sorted = df.sort_values('P/E')

# Вывод результатов
print(df_sorted[['Ticker', 'Name', 'Sector', 'Market Cap (B)', 
'P/E', 'Forward P/E', 'Dividend Yield (%)', 'ROE (%)']])

Для более сложного анализа можно построить карту тепла корреляций между различными активами:

Python
Скопировать код
import yfinance as yf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

# Список активов для анализа корреляций
assets = {
'S&P 500': '^GSPC',
'Nasdaq': '^IXIC',
'Russell 2000': '^RUT',
'Золото': 'GC=F',
'Нефть': 'CL=F',
'US Treasuries': '^TNX',
'USD/EUR': 'EURUSD=X',
'Bitcoin': 'BTC-USD'
}

# Получение данных
data = pd.DataFrame()

for name, ticker in assets.items():
try:
asset_data = yf.download(ticker, period="1y")['Close']
data[name] = asset_data
except Exception as e:
print(f"Ошибка с {name}: {e}")

# Расчет дневных доходностей
returns = data.pct_change().dropna()

# Расчет корреляционной матрицы
corr_matrix = returns.corr()

# Визуализация корреляций
plt.figure(figsize=(12, 10))
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm', fmt='.2f', linewidths=0.5)
plt.title('Корреляционная матрица доходностей различных активов')
plt.tight_layout()
plt.show()

# Вывод пар с наименьшей корреляцией (для диверсификации)
corr_pairs = []
for i in range(len(corr_matrix.columns)):
for j in range(i+1, len(corr_matrix.columns)):
asset1 = corr_matrix.columns[i]
asset2 = corr_matrix.columns[j]
correlation = corr_matrix.iloc[i, j]
corr_pairs.append((asset1, asset2, correlation))

# Сортировка по абсолютному значению корреляции
corr_pairs.sort(key=lambda x: abs(x[2]))

# Вывод наименее коррелированных пар
print("Наименее коррелированные пары активов:")
for asset1, asset2, corr in corr_pairs[:5]:
print(f"{asset1} — {asset2}: {corr:.3f}")

Если вы хотите определиться с направлением своего профессионального развития в сфере финансовых технологий, Тест на профориентацию от Skypro поможет оценить ваши склонности к аналитической работе с данными. Вы узнаете, подходит ли вам карьера финансового аналитика-программиста, где навыки работы с Python и финансовыми библиотеками вроде yfinance являются ключевыми. Тест учитывает ваши технические способности и особенности мышления, предлагая персонализированные карьерные рекомендации в быстрорастущей области финтеха.

Практические сценарии использования Python yfinance в трейдинге

Теоретические знания о библиотеке обретают реальную ценность, когда применяются в практических сценариях. Рассмотрим несколько готовых к использованию решений для трейдеров и инвесторов. 🔍

Первый сценарий: создание автоматизированной системы отслеживания уровней поддержки и сопротивления:

Python
Скопировать код
import yfinance as yf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import argrelextrema

# Функция для определения уровней поддержки и сопротивления
def find_support_resistance_levels(ticker, period="1y", interval="1d", order=5):
# Загрузка данных
data = yf.download(ticker, period=period, interval=interval)

# Находим локальные минимумы и максимумы
price = data['Close'].values

# Определение локальных минимумов
local_min_indices = argrelextrema(price, np.less_equal, order=order)[0]
local_min_values = price[local_min_indices]
local_min_dates = data.index[local_min_indices]

# Определение локальных максимумов
local_max_indices = argrelextrema(price, np.greater_equal, order=order)[0]
local_max_values = price[local_max_indices]
local_max_dates = data.index[local_max_indices]

# Группировка близких уровней поддержки (с погрешностью 2%)
support_levels = []
for price_min in local_min_values:
is_close_to_existing = False
for level in support_levels:
if abs(price_min – level) / level < 0.02:
is_close_to_existing = True
break
if not is_close_to_existing:
support_levels.append(price_min)

# Группировка близких уровней сопротивления
resistance_levels = []
for price_max in local_max_values:
is_close_to_existing = False
for level in resistance_levels:
if abs(price_max – level) / level < 0.02:
is_close_to_existing = True
break
if not is_close_to_existing:
resistance_levels.append(price_max)

# Визуализация
plt.figure(figsize=(14, 7))
plt.plot(data.index, data['Close'], label=ticker)

# Отметка уровней поддержки
for level in support_levels:
plt.axhline(y=level, color='g', linestyle='-', alpha=0.3)

# Отметка уровней сопротивления
for level in resistance_levels:
plt.axhline(y=level, color='r', linestyle='-', alpha=0.3)

plt.scatter(local_min_dates, local_min_values, color='g', label='Поддержка')
plt.scatter(local_max_dates, local_max_values, color='r', label='Сопротивление')

plt.title(f'Уровни поддержки и сопротивления для {ticker}')
plt.xlabel('Дата')
plt.ylabel('Цена')
plt.legend()
plt.grid(True)
plt.show()

# Возвращаем данные для дальнейшего анализа
return {
'ticker': ticker,
'current_price': price[-1],
'support_levels': sorted(support_levels),
'resistance_levels': sorted(resistance_levels),
'nearest_support': max([s for s in support_levels if s < price[-1]], default=None),
'nearest_resistance': min([r for r in resistance_levels if r > price[-1]], default=None)
}

# Пример использования
results = find_support_resistance_levels("NVDA", period="6mo", order=10)

# Вывод результатов анализа
print(f"Текущая цена {results['ticker']}: ${results['current_price']:.2f}")
print(f"Ближайший уровень поддержки: ${results['nearest_support']:.2f}")
print(f"Ближайший уровень сопротивления: ${results['nearest_resistance']:.2f}")
print(f"Потенциал падения до поддержки: {((results['nearest_support'] / results['current_price']) – 1) * 100:.2f}%")
print(f"Потенциал роста до сопротивления: {((results['nearest_resistance'] / results['current_price']) – 1) * 100:.2f}%")

Второй сценарий: автоматизированная система ребалансировки портфеля на основе целевого распределения активов:

Python
Скопировать код
import yfinance as yf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# Класс для управления портфелем
class PortfolioManager:
def __init__(self, tickers, target_allocation, initial_investment=10000):
self.tickers = tickers
self.target_allocation = target_allocation
self.initial_investment = initial_investment
self.current_data = None
self.portfolio_history = None

def download_data(self, period="1y"):
# Загрузка данных по всем тикерам
self.current_data = yf.download(self.tickers, period=period)['Close']

def calculate_current_allocation(self, positions, latest_prices=None):
if latest_prices is None:
latest_prices = self.current_data.iloc[-1]

# Расчет текущей стоимости позиций
position_values = {}
for ticker, shares in positions.items():
position_values[ticker] = shares * latest_prices[ticker]

total_value = sum(position_values.values())

# Расчет текущего распределения
current_allocation = {ticker: value/total_value for ticker, value in position_values.items()}

return current_allocation, total_value

def calculate_rebalance(self, positions, threshold=0.05):
# Получение последних цен
latest_prices = self.current_data.iloc[-1]

# Расчет текущего распределения
current_allocation, total_value = self.calculate_current_allocation(positions, latest_prices)

# Определение необходимости ребалансировки
need_rebalance = False
for ticker in self.tickers:
if abs(current_allocation[ticker] – self.target_allocation[ticker]) > threshold:
need_rebalance = True
break

if not need_rebalance:
return None, current_allocation, total_value

# Расчет целевых позиций
target_positions = {}
for ticker in self.tickers:
target_value = total_value * self.target_allocation[ticker]
target_shares = target_value / latest_prices[ticker]
target_positions[ticker] = int(target_shares) # Округление до целых акций

# Расчет действий по ребалансировке
actions = {}
for ticker in self.tickers:
actions[ticker] = target_positions[ticker] – positions[ticker]

return actions, current_allocation, total_value

def simulate_portfolio(self, rebalance_frequency='monthly', threshold=0.05):
# Создание начальных позиций на основе целевого распределения
initial_prices = self.current_data.iloc[0]
positions = {}

for ticker in self.tickers:
target_value = self.initial_investment * self.target_allocation[ticker]
shares = target_value / initial_prices[ticker]
positions[ticker] = int(shares)

# Отслеживание истории портфеля
dates = self.current_data.index
portfolio_values = []
allocations_history = []
rebalance_dates = []

# Определение дат ребалансировки
if rebalance_frequency == 'monthly':
rebalance_months = list(set([date.month for date in dates]))
rebalance_dates = [date for date in dates if dates.get_loc(date) == 0 or 
date.month != dates[dates.get_loc(date)-1].month]
elif rebalance_frequency == 'quarterly':
rebalance_quarters = list(set([(date.year, (date.month-1)//3) for date in dates]))
rebalance_dates = [date for date in dates if dates.get_loc(date) == 0 or 
(date.month-1)//3 != (dates[dates.get_loc(date)-1].month-1)//3]
elif rebalance_frequency == 'yearly':
rebalance_years = list(set([date.year for date in dates]))
rebalance_dates = [date for date in dates if dates.get_loc(date) == 0 or 
date.year != dates[dates.get_loc(date)-1].year]

# Симуляция работы портфеля
for date in dates:
prices = self.current_data.loc[date]

# Проверка на необходимость ребалансировки
if date in rebalance_dates:
actions, current_allocation, total_value = self.calculate_rebalance(
positions, threshold)

if actions:
# Выполнение ребалансировки
for ticker, shares_change in actions.items():
positions[ticker] += shares_change

# Расчет текущей стоимости портфеля
portfolio_value = sum(positions[ticker] * prices[ticker] for ticker in self.tickers)
portfolio_values.append(portfolio_value)

# Запись текущего распределения
current_allocation, _ = self.calculate_current_allocation(positions, prices)
allocations_history.append(current_allocation)

# Создание истории портфеля
self.portfolio_history = pd.DataFrame({
'Value': portfolio_values
}, index=dates)

# Расчет показателей эффективности
self.portfolio_history['Returns'] = self.portfolio_history['Value'].pct_change()
self.portfolio_history['Cumulative Returns'] = (1 + self.portfolio_history['Returns']).cumprod() – 1

return self.portfolio_history, rebalance_dates, allocations_history

def visualize_performance(self, benchmark_ticker=None):
if self.portfolio_history is None:
print("Сначала выполните simulate_portfolio()")
return

fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 10), gridspec_kw={'height_ratios': [3, 1]})

# График стоимости портфеля
self.portfolio_history['Value'].plot(ax=ax1, label='Портфель')

# Добавление бенчмарка, если указан
if benchmark_ticker:
benchmark_data = yf.download(benchmark_ticker, 
start=self.portfolio_history.index[0],
end=self.portfolio_history.index[-1])['Close']
benchmark_returns = benchmark_data.pct_change()
benchmark_cumulative = (1 + benchmark_returns).cumprod()
normalized_benchmark = benchmark_cumulative * self.portfolio_history['Value'][0] / benchmark_cumulative[0]
normalized_benchmark.plot(ax=ax1, label=f'Бенчмарк ({benchmark_ticker})')

ax1.set_title('Динамика стоимости портфеля')
ax1.set_ylabel('Стоимость ($)')
ax1.legend()
ax1.grid(True)

# График накопленной доходности
self.portfolio_history['Cumulative Returns'].plot(ax=ax2, label='Накопленная доходность')
ax2.set_ylabel('Доходность (%)')
ax2.axhline(y=0, color='k', linestyle='-', alpha=0.2)
ax2.grid(True)

plt.tight_layout()
plt.show()

# Вывод статистики
annual_return = (1 + self.portfolio_history['Returns']).prod() ** (252 / len(self.portfolio_history)) – 1
volatility = self.portfolio_history['Returns'].std() * np.sqrt(252)
sharpe = annual_return / volatility if volatility != 0 else 0
max_drawdown = (self.portfolio_history['Value'] / self.portfolio_history['Value'].cummax() – 1).min()

print(f"Годовая доходность: {annual_return:.2%}")
print(f"Волатильность: {volatility:.2%}")
print(f"Коэффициент Шарпа: {sharpe:.2f}")
print(f"Максимальная просадка: {max_drawdown:.2%}")
print(f"Конечная стоимость: ${self.portfolio_history['Value'].iloc[-1]:.2f}")
print(f"Общая доходность: {self.portfolio_history['Value'].iloc[-1]/self.initial_investment – 1:.2%}")

# Пример использования
tickers = ['SPY', 'QQQ', 'GLD', 'TLT', 'VNQ']
target_allocation = {
'SPY': 0.25, # S&P 500
'QQQ': 0.25, # Nasdaq 100
'GLD': 0.20, # Золото
'TLT': 0.20, # Долгосрочные казначейские облигации
'VNQ': 0.10 # Недвижимость
}

pm = PortfolioManager(tickers, target_allocation, initial_investment=10000)
pm.download_data(period="5y")
history, rebalance_dates, allocations = pm.simulate_portfolio(rebalance_frequency='quarterly')
pm.visualize_performance(benchmark_ticker='SPY')

Третий сценарий: создание системы скрининга акций для выявления потенциальных возможностей роста на основе дивергенции цены и объема:

Python
Скопировать код
import yfinance as yf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from tqdm import tqdm

# Функция для выявления дивергенций
def detect_price_volume_divergence(ticker, period="6mo", window=20):
# Загрузка данных
data = yf.download(ticker, period=period)

if len(data) < window * 2:
return None

# Расчет индикаторов
data['Price_ROC'] = data['Close'].pct_change(window)
data['Volume_ROC'] = data['Volume'].pct_change(window)

# Выявление дивергенций
data['Bullish_Divergence'] = ((data['Price_ROC'] < 0) & (data['Volume_ROC'] > 0)).astype(int)
data['Bearish_Divergence'] = ((data['Price_ROC'] > 0) & (data['Volume_ROC'] < 0)).astype(int)

# Фильтрация сильных сигналов
strong_bullish = data[data['Bullish_Divergence'] == 1].copy()
strong_bearish = data[data['Bearish_Divergence'] == 1].copy()

if not strong_bullish.empty or not strong_bearish.empty:
# Расчет силы дивергенции
if not strong_bullish.empty:
strong_bullish['Divergence_Strength'] = strong_bullish['Volume_ROC'] / abs(strong_bullish['Price_ROC'])

if not strong_bearish.empty:
strong_bearish['Divergence_Strength'] = strong_bearish['Volume_ROC'] / abs(strong_bearish['Price_ROC'])

# Последние сигналы
last_bullish = strong_bullish.iloc[-1] if not strong_bullish.empty else None
last_bearish = strong_bearish.iloc[-1] if not strong_bearish.empty else None

# Возвращаем результаты
return {
'ticker': ticker,
'current_price': data['Close'].iloc[-1],
'last_bullish_date': last_bullish.name if last_bullish is not None else None,
'last_bullish_strength': last_bullish['Divergence_Strength'] if last_bullish is not None else None,
'last_bearish_date': last_bearish.name if last_bearish is not None else None,
'last_bearish_strength': last_bearish['Divergence_Strength'] if last_bearish is not None else None,
'data': data
}

return None

# Функция для скрининга списка акций
def screen_stocks_for_divergences(tickers, period="6mo", window=20):
results = []

for ticker in tqdm(tickers, desc="Анализируем акции"):
try:
divergence = detect_price_volume_divergence(ticker, period, window)
if divergence:
results.append(divergence)
except Exception as e:
print(f"Ошибка при обработке {ticker}: {str(e)}")

return results

# Пример использования: скрининг компонентов S&P 500
def get_sp500_tickers():
# В реальном сценарии здесь был бы запрос к API или парсинг списка
# Для примера используем ограниченный набор тикеров
return ['AAPL', 'MSFT', 'AMZN', 'TSLA', 'GOOG', 'META', 'NVDA', 'BRK-B', 'UNH', 'JNJ']

tickers = get_sp500_tickers()
divergence_results = screen_stocks_for_divergences(tickers)

# Вывод результатов
if divergence_results:
# Сортировка по силе бычьей дивергенции
bullish_opportunities = [r for r in divergence_results if r['last_bullish_date'] is not None]
bullish_opportunities.sort(key=lambda x: x['last_bullish_strength'], reverse=True)

print("Топ-5 акций с бычьей дивергенцией цены и объема:")
for i, result in enumerate(bullish_opportunities[:5], 1):
print(f"{i}. {result['ticker']}: ${result['current_price']:.2f}, сила дивергенции: {result['last_bullish_strength']:.2f}")

# Визуализация лучшей возможности
if bullish_opportunities:
best = bullish_opportunities[0]
plt.figure(figsize=(14, 8))

# Создание подграфиков
ax1 = plt.subplot(211)
ax2 = plt.subplot(212, sharex=ax1)

# График цены
ax1.plot(best['data'].index, best['data']['Close'])
ax1.set_title(f"Бычья дивергенция цены и объема для {best['ticker']}")
ax1.set_ylabel("Цена")
ax1.grid(True)

# Отметка даты последней дивергенции
if best['last_bullish_date']:
ax1.axvline(x=best['last_bullish_date'], color='g', linestyle='--', 
label=f"Дата бычьей дивергенции: {best['last_bullish_date'].date()}")

# График объема
ax2.bar(best['data'].index, best['data']['Volume'])
ax2.set_ylabel("Объем")
ax2.grid(True)

# Отметка даты последней дивергенции
if best['last_bullish_date']:
ax2.axvline(x=best['last_bullish_date'], color='g', linestyle='--')

plt.tight_layout()
plt.show()
else:
print("Дивергенций не обнаружено")

Эти примеры демонстрируют, как с помощью yfinance и стандартных библиотек Python можно создавать профессиональные торговые системы. Важные особенности при практической реализации:

  • Всегда проверяйте полученные данные на полноту и корректность
  • Учитывайте задержку данных при создании торговых стратегий
  • Используйте механизмы кэширования для снижения нагрузки на сервер Yahoo
  • Внедряйте обработку исключений для устойчивости к ошибкам загрузки данных
  • Тестируйте стратегии на исторических данных перед применением на реальных счетах
  • Комбинируйте технический и фундаментальный анализ для повышения точности сигналов
  • Разделяйте системы анализа и исполнения торговых операций для безопасности

Освоив Python и yfinance, вы получили мощный инструмент для работы с финансовыми данными. Эта комбинация открывает дверь в мир алгоритмической торговли, автоматизированного анализа и принятия инвестиционных решений на основе данных. Доступность информации и вычислительн