Владимиров Владимир
Владимиров Владимир личный блог
03 ноября 2025, 17:50

Выгрузка исторических данных

  Сегодня обсуждалась тема о том, что использование ряда зарубежных сайтов  для скачивания исторических данных стало недоступно. Добрый человек подсказал решение проблемы на питоне с использованием yfinance. При помощи DeepSeak создал программу для выгрузки данных. Возможно кому-то еще будет полезно.
   Для использования программы надо создать в текущей папке с кодом две папки: InData и OutData. В первой папке создайте текстовый файл со списком тикеров (активов), разделенных точкой с запятой (;) с расширением txt. Во второй папке будет сохранен файл «текущая дата.txt» с данными разделителем точка с запятой. Данные по всем заданным тикерам записываются в один файл последовательно. Формат вывода:
                              Тикер / Data  Open  High  Low  Close Volume /  данные по дням за период
   После запуска — выбрать период дат, выбрать файл с исходным списком активов, запустить скачивание данных.
     Сам код следующий:

import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import yfinance as yf
import pandas as pd
import numpy as np
import os
from datetime import datetime

class YFinanceDownloader:
def __init__(self, root):
self.root = root
self.root.title(«YFinance Data Downloader»)
self.root.geometry(«600x400»)

self.create_widgets()

def create_widgets(self):
# Заголовок
title_label = ttk.Label(self.root, text=«Выгрузка данных торгов», font=(«Arial», 14, «bold»))
title_label.pack(pady=10)

# Фрейм для ввода дат
date_frame = ttk.Frame(self.root)
date_frame.pack(pady=10, padx=20, fill=«x»)

ttk.Label(date_frame, text=«Период дат (ДД.ММ.ГГГГ — ДД.ММ.ГГГГ):»).pack(anchor=«w»)

self.date_entry = ttk.Entry(date_frame, width=30)
self.date_entry.pack(pady=5, fill=«x»)
self.date_entry.insert(0, «01.10.2025 — 31.10.2025»)

# Фрейм для выбора файла
file_frame = ttk.Frame(self.root)
file_frame.pack(pady=10, padx=20, fill=«x»)

ttk.Label(file_frame, text=«Файл с тикерами:»).pack(anchor=«w»)

file_select_frame = ttk.Frame(file_frame)
file_select_frame.pack(pady=5, fill=«x»)

self.file_path = tk.StringVar()
self.file_entry = ttk.Entry(file_select_frame, textvariable=self.file_path, width=40)
self.file_entry.pack(side=«left», fill=«x», expand=True)

ttk.Button(file_select_frame, text=«Обзор», command=self.browse_file).pack(side=«left», padx=5)

# Кнопка выполнения
ttk.Button(self.root, text=«Выгрузить данные», command=self.download_data).pack(pady=20)

# Прогресс бар
self.progress = ttk.Progressbar(self.root, mode='indeterminate')
self.progress.pack(pady=10, padx=20, fill=«x»)

# Текстовое поле для логов
self.log_text = tk.Text(self.root, height=10, width=70)
self.log_text.pack(pady=10, padx=20, fill=«both», expand=True)

# Добавляем скроллбар для текстового поля
scrollbar = ttk.Scrollbar(self.log_text)
scrollbar.pack(side=«right», fill=«y»)
self.log_text.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=self.log_text.yview)

def browse_file(self):
filename = filedialog.askopenfilename(
initialdir=«InData»,
title=«Выберите файл с тикерами»,
filetypes=((«Text files», "*.txt"), («All files», "*.*"))
)
if filename:
self.file_path.set(filename)

def log_message(self, message):
self.log_text.insert(«end», f"{datetime.now().strftime('%H:%M:%S')} — {message}\n")
self.log_text.see(«end»)
self.root.update()

def parse_dates(self, date_range):
""«Парсинг дат из формата ДД.ММ.ГГГГ»""
try:
start_str, end_str = date_range.split("-")
start_date = datetime.strptime(start_str.strip(), "%d.%m.%Y").strftime("%Y-%m-%d")
end_date = datetime.strptime(end_str.strip(), "%d.%m.%Y").strftime("%Y-%m-%d")
return start_date, end_date
except ValueError as e:
raise ValueError(«Неверный формат даты. Используйте: ДД.ММ.ГГГГ — ДД.ММ.ГГГГ»)

def read_tickers(self, filepath):
""«Чтение тикеров из файла»""
try:
with open(filepath, 'r', encoding='utf-8') as file:
content = file.read().strip()
tickers = [ticker.strip() for ticker in content.split(';') if ticker.strip()]
return tickers
except Exception as e:
raise Exception(f«Ошибка чтения файла: {e}»)

def download_data(self):
try:
self.progress.start()
self.log_text.delete(1.0, «end»)

# Проверка введенных данных
if not self.file_path.get():
messagebox.showerror(«Ошибка», «Выберите файл с тикерами»)
return

if not self.date_entry.get():
messagebox.showerror(«Ошибка», «Введите период дат»)
return

# Парсинг дат
self.log_message(«Парсинг дат...»)
start_date, end_date = self.parse_dates(self.date_entry.get())
self.log_message(f«Период: {start_date} — {end_date}»)

# Чтение тикеров
self.log_message(«Чтение тикеров из файла...»)
tickers = self.read_tickers(self.file_path.get())
self.log_message(f«Найдено тикеров: {len(tickers)}»)

if not tickers:
messagebox.showerror(«Ошибка», «Файл не содержит тикеров»)
return

# Создание папки OutData если её нет
if not os.path.exists(«OutData»):
os.makedirs(«OutData»)
self.log_message(«Создана папка OutData»)

# Создание имени файла с текущей датой
current_date = datetime.now().strftime("%d-%m-%Y")
output_filename = f«OutData/{current_date}.txt»

# Открываем файл для записи
with open(output_filename, 'w', encoding='utf-8') as f:
total_tickers_saved = 0

for ticker in tickers:
try:
self.log_message(f«Обработка тикера {ticker}...»)

# Загружаем данные для текущего тикера
data = yf.download(ticker, start=start_date, end=end_date, progress=False)

if data.empty:
self.log_message(f«Предупреждение: нет данных для {ticker}»)
continue

self.log_message(f«Загружено {len(data)} строк для {ticker}»)

# Записываем заголовок тикера
f.write(f«Тикер {ticker}\n»)

# Записываем названия колонок
f.write(«Date;Open;High;Low;Close;Volume\n»)

# Подготовка и запись данных
rows_written = 0
for index, row in data.iterrows():
try:
# Преобразуем дату в формат ДД.ММ.ГГГГ
date_str = index.strftime('%d.%m.%Y')

# Получаем значения как числа
open_price = float(row['Open'])
high_price = float(row['High'])
low_price = float(row['Low'])
close_price = float(row['Close'])
volume = int(row['Volume'])

# Форматируем строку данных
data_line = f"{date_str};{open_price:.2f};{high_price:.2f};{low_price:.2f};{close_price:.2f};{volume}\n"
f.write(data_line)
rows_written += 1

except Exception as row_error:
continue

# Добавляем пустую строку между тикерами (кроме последнего)
if ticker != tickers[-1]:
f.write("\n")

total_tickers_saved += 1
self.log_message(f«Записано {rows_written} строк для {ticker}»)

except Exception as e:
self.log_message(f«Ошибка при обработке {ticker}: {str(e)}»)

self.progress.stop()

# Проверяем содержимое файла
if os.path.exists(output_filename):
with open(output_filename, 'r', encoding='utf-8') as check_file:
content = check_file.read()
file_size = len(content)

self.log_message(f«Файл создан: {output_filename}»)
self.log_message(f«Размер файла: {file_size} байт»)

if file_size > 0:
messagebox.showinfo(«Успех», f«Данные успешно выгружены для {total_tickers_saved} из {len(tickers)} тикеров\nФайл: {output_filename}»)
else:
messagebox.showwarning(«Предупреждение», f«Файл создан, но пустой: {output_filename}»)
else:
messagebox.showerror(«Ошибка», f«Файл не был создан: {output_filename}»)

except Exception as e:
self.progress.stop()
self.log_message(f«Ошибка: {str(e)}»)
messagebox.showerror(«Ошибка», str(e))

def main():
# Создаем папки если их нет
if not os.path.exists(«InData»):
os.makedirs(«InData»)
print(«Создана папка InData — поместите туда файлы с тикерами»)

if not os.path.exists(«OutData»):
os.makedirs(«OutData»)
print(«Создана папка OutData — туда будут сохраняться результаты»)

root = tk.Tk()
app = YFinanceDownloader(root)
root.mainloop()

if __name__ == "__main__":
main()



16 Комментариев
  • Serg_Mich
    16 апреля 2026, 10:09
    Меня зациклило на одной задаче, не могу подобрать длину окна истории, которое требуется для обучения модели. Перепробовал много вариантов, но ни один не работает точно. Ищу как эту задачу решают другие, но по теме почти пусто, даже в книгах… Вот думаю, почему бы не спросить у более подкованных в математике и физике, например у тебя :) Наверняка же приходилось сталкиваться с похожей задачей, как считаешь, есть ли хорошее аналитическое решение у сей проблемы, может какую-то теорию сюда можно притянуть? 
  • Serg_Mich
    16 апреля 2026, 12:09

    В правильно заданом вопросе есть уже половина ответа и это более-менее понятно:) Дело тут даже не в оценке, ведь чтобы ее получить — надо:

    a) сперва что-то с чем-то сравнить. И это, на мой взгляд,  больше похоже на эмпирический метод.

    b) потратить время на перебор вариантов. 

    Это всё допустимо, но наверное можно попытаться вычислить шаг до получения этой оценки — как-то аналитически и «заранее» посчитать длину этой ретроспективы опираясь на характеристики самих данных 

     

  • Serg_Mich
    16 апреля 2026, 18:47

    Можно и паттернами мерять, можно твоими величинами, думаю принцип должен быть похожим. Если паттернами, тогда, что если на некой глубине ретроспективы встречается N-повторений одного паттерна, допустим 20 или 120 и статистика вроде как набрана. Вот тут, чтобы не получить переобучение или наоборот шум на выходе, как понять, а в идеале вычислить заранее и без перебора, этих N уже хватит или еще недостаточно? (если отталкиваться от того, что глубина задается N-повторениями одного паттерна, но мне эта идея не очень). Но в целом твоя идея понятна, она +- похожа на мою. Ладно, буду думать, это единственно нерешенная задача осталась в этой чертовщине :)

Активные форумы
Что сейчас обсуждают

Старый дизайн
Старый
дизайн