Обновление зависимостей и игнорируемых файлов

- Добавлен yfinance в requirements.txt
- Расширен .gitignore для исключения служебных файлов Python
- Исправлен импорт в currency.py для корректной работы с модулями
This commit is contained in:
belikovme 2025-03-12 14:49:12 +07:00
parent 74d4e14476
commit 61b6f03e26
13 changed files with 4910 additions and 2 deletions

6
.gitignore vendored
View File

@ -1 +1,7 @@
venv/
pycache/
*.db
*.db-shm
*.db-wal
__pycache__/
__pycache__

11
4.2.0 Normal file
View File

@ -0,0 +1,11 @@
Requirement already satisfied: nbformat in d:\pythonpython\globalsy\globalsy_streamlit\venv\lib\site-packages (5.10.4)
Requirement already satisfied: fastjsonschema>=2.15 in d:\pythonpython\globalsy\globalsy_streamlit\venv\lib\site-packages (from nbformat) (2.21.1)
Requirement already satisfied: jsonschema>=2.6 in d:\pythonpython\globalsy\globalsy_streamlit\venv\lib\site-packages (from nbformat) (4.23.0)
Requirement already satisfied: jupyter-core!=5.0.*,>=4.12 in d:\pythonpython\globalsy\globalsy_streamlit\venv\lib\site-packages (from nbformat) (5.7.2)
Requirement already satisfied: traitlets>=5.1 in d:\pythonpython\globalsy\globalsy_streamlit\venv\lib\site-packages (from nbformat) (5.14.3)
Requirement already satisfied: attrs>=22.2.0 in d:\pythonpython\globalsy\globalsy_streamlit\venv\lib\site-packages (from jsonschema>=2.6->nbformat) (24.3.0)
Requirement already satisfied: jsonschema-specifications>=2023.03.6 in d:\pythonpython\globalsy\globalsy_streamlit\venv\lib\site-packages (from jsonschema>=2.6->nbformat) (2024.10.1)
Requirement already satisfied: referencing>=0.28.4 in d:\pythonpython\globalsy\globalsy_streamlit\venv\lib\site-packages (from jsonschema>=2.6->nbformat) (0.35.1)
Requirement already satisfied: rpds-py>=0.7.1 in d:\pythonpython\globalsy\globalsy_streamlit\venv\lib\site-packages (from jsonschema>=2.6->nbformat) (0.22.3)
Requirement already satisfied: platformdirs>=2.5 in d:\pythonpython\globalsy\globalsy_streamlit\venv\lib\site-packages (from jupyter-core!=5.0.*,>=4.12->nbformat) (4.3.6)
Requirement already satisfied: pywin32>=300 in d:\pythonpython\globalsy\globalsy_streamlit\venv\lib\site-packages (from jupyter-core!=5.0.*,>=4.12->nbformat) (308)

View File

@ -134,3 +134,5 @@ class ForexDataHandler:
raise ValueError(f"Неизвестный индекс: {index}")
return self.get_market_data(self.russian_indices[index])

70
classes/moex_class.py Normal file
View File

@ -0,0 +1,70 @@
import pandas as pd
import requests
from datetime import datetime, timedelta
class RussianMarketData:
def __init__(self):
self.moex_base_url = "https://iss.moex.com/iss"
# Словарь с индексами
self.indices = {
'oil_gas': 'MOEXOG', # Нефть и газ
'finance': 'MOEXFN', # Финансы
'telecom': 'MOEXTL', # Телекоммуникации
'metals': 'MOEXMM', # Металлы и добыча
'consumer': 'MOEXCN', # Потребительский сектор
'transport': 'MOEXTN' # Транспорт
}
def get_moex_index_history(self, index_code='MOEXOG', start_date=None, end_date=None):
"""
Получение исторических данных индекса MOEX.
"""
if start_date is None:
start_date = (datetime.now() - timedelta(days=365)).strftime('%Y-%m-%d')
if end_date is None:
end_date = datetime.now().strftime('%Y-%m-%d')
all_data = []
start = 0
while True:
url = f"{self.moex_base_url}/history/engines/stock/markets/index/securities/{index_code}.json"
params = {
'from': start_date,
'till': end_date,
'start': start
}
response = requests.get(url, params=params)
data = response.json()
# Проверка наличия данных
if 'history' not in data or len(data['history']['data']) == 0:
break
# Добавление данных в общий список
all_data.extend(data['history']['data'])
start += 100 # Увеличиваем смещение на 100 для следующего запроса
# Преобразование данных в DataFrame
columns = data['history']['columns']
df = pd.DataFrame(all_data, columns=columns)
# Обработка данных
df['TRADEDATE'] = pd.to_datetime(df['TRADEDATE'])
df = df.set_index('TRADEDATE')
return df[['CLOSE', 'VOLUME']]
def get_historical_data(self, sector: str, start_date=None, end_date=None):
"""
Получает исторические данные для указанного сектора.
Args:
sector: ключ сектора из словаря indices
"""
if sector not in self.indices:
raise ValueError(f"Неизвестный сектор: {sector}")
index_code = self.indices[sector]
return self.get_moex_index_history(index_code, start_date, end_date)

228
classes/moex_history.py Normal file
View File

@ -0,0 +1,228 @@
import pandas as pd
import requests
from datetime import datetime, timedelta
from typing import List, Dict, Optional
import asyncio
import aiohttp
import numpy as np
class MOEXHistoricalData:
def __init__(self):
self.base_url = "https://iss.moex.com/iss"
# Словарь соответствия секторов и их индексов на MOEX
self.sector_indices = {
'metals_mining': 'MOEXMM', # Индекс Металлов и добычи
'oil_gas': 'MOEXOG', # Индекс Нефти и газа
'chemicals': 'MOEXCH', # Индекс Химии и нефтехимии
'electric_utilities': 'MOEXEU', # Индекс Электроэнергетики
'telecom': 'MOEXTL', # Индекс Телекоммуникаций
'finance': 'MOEXFN', # Индекс Финансов
'consumer': 'MOEXCN', # Индекс Потребительского сектора
'transport': 'MOEXTN' # Индекс Транспорта
}
async def get_security_history(
self,
ticker: str,
start_date: str,
end_date: str,
engine: str = "stock",
market: str = "shares",
board: str = "TQBR"
) -> pd.DataFrame:
"""
Получение исторических данных по отдельному тикеру
Args:
ticker: Тикер акции
start_date: Начальная дата в формате YYYY-MM-DD
end_date: Конечная дата в формате YYYY-MM-DD
engine: Торговый движок (по умолчанию stock)
market: Рынок (по умолчанию shares)
board: Режим торгов (по умолчанию TQBR)
Returns:
DataFrame с историческими данными
"""
url = f"{self.base_url}/history/engines/{engine}/markets/{market}/boards/{board}/securities/{ticker}.json"
all_data = []
start = 0
async with aiohttp.ClientSession() as session:
while True:
params = {
"from": start_date,
"till": end_date,
"start": start,
"limit": 100
}
async with session.get(url, params=params) as response:
data = await response.json()
# Получаем данные истории
history_data = data['history']
if not history_data['data']:
break
# Добавляем данные в общий список
all_data.extend(history_data['data'])
start += 100
if len(history_data['data']) < 100:
break
# Создаем DataFrame
df = pd.DataFrame(all_data, columns=history_data['columns'])
# Конвертируем даты и числовые значения
df['TRADEDATE'] = pd.to_datetime(df['TRADEDATE'])
numeric_columns = ['OPEN', 'HIGH', 'LOW', 'CLOSE', 'VALUE', 'VOLUME']
df[numeric_columns] = df[numeric_columns].apply(pd.to_numeric)
return df
async def get_sector_history(
self,
sector_tickers: List[str],
start_date: str,
end_date: str,
engine: str = "stock",
market: str = "shares",
board: str = "TQBR"
) -> Dict[str, pd.DataFrame]:
"""
Получение исторических данных по всем тикерам сектора
Args:
sector_tickers: Список тикеров сектора
start_date: Начальная дата в формате YYYY-MM-DD
end_date: Конечная дата в формате YYYY-MM-DD
engine: Торговый движок (по умолчанию stock)
market: Рынок (по умолчанию shares)
board: Режим торгов (по умолчанию TQBR)
Returns:
Словарь {тикер: DataFrame с историческими данными}
"""
tasks = []
for ticker in sector_tickers:
task = self.get_security_history(
ticker=ticker,
start_date=start_date,
end_date=end_date,
engine=engine,
market=market,
board=board
)
tasks.append(task)
results = await asyncio.gather(*tasks)
return dict(zip(sector_tickers, results))
async def get_official_sector_index(
self,
sector: str,
start_date: str,
end_date: str
) -> pd.DataFrame:
"""
Получение официального отраслевого индекса с MOEX
Args:
sector: Название сектора (ключ из словаря sector_indices)
start_date: Начальная дата в формате YYYY-MM-DD
end_date: Конечная дата в формате YYYY-MM-DD
Returns:
DataFrame с данными индекса
"""
if sector not in self.sector_indices:
raise ValueError(f"Неизвестный сектор: {sector}. Доступные секторы: {list(self.sector_indices.keys())}")
index_ticker = self.sector_indices[sector]
url = f"{self.base_url}/history/engines/stock/markets/index/securities/{index_ticker}.json"
all_data = []
start = 0
async with aiohttp.ClientSession() as session:
while True:
params = {
"from": start_date,
"till": end_date,
"start": start,
"limit": 100
}
async with session.get(url, params=params) as response:
data = await response.json()
history_data = data['history']
if not history_data['data']:
break
all_data.extend(history_data['data'])
start += 100
if len(history_data['data']) < 100:
break
df = pd.DataFrame(all_data, columns=history_data['columns'])
# Конвертируем даты и числовые значения
df['TRADEDATE'] = pd.to_datetime(df['TRADEDATE'])
numeric_columns = ['OPEN', 'HIGH', 'LOW', 'CLOSE', 'VALUE', 'VOLUME']
df[numeric_columns] = df[numeric_columns].apply(pd.to_numeric)
return df
def calculate_sector_index(
self,
sector_data: Dict[str, pd.DataFrame],
weights: Optional[Dict[str, float]] = None
) -> pd.DataFrame:
"""
Расчет индекса сектора на основе исторических данных входящих в него компаний
Args:
sector_data: Словарь с историческими данными по тикерам {тикер: DataFrame}
weights: Словарь с весами компаний {тикер: вес}. Если None, веса будут равными
Returns:
DataFrame с рассчитанным индексом сектора
"""
# Если веса не указаны, используем равные веса
if weights is None:
weights = {ticker: 1/len(sector_data) for ticker in sector_data.keys()}
# Создаем DataFrame с датами и ценами закрытия для каждого тикера
prices_df = pd.DataFrame()
for ticker, df in sector_data.items():
prices_df[ticker] = df.set_index('TRADEDATE')['CLOSE']
# Рассчитываем относительное изменение цен
returns_df = prices_df.pct_change()
# Рассчитываем взвешенную доходность индекса
weighted_returns = pd.DataFrame()
for ticker in returns_df.columns:
weighted_returns[ticker] = returns_df[ticker] * weights[ticker]
index_returns = weighted_returns.sum(axis=1)
# Рассчитываем значения индекса
index_values = (1 + index_returns).cumprod() * 1000 # Начальное значение 1000
# Создаем итоговый DataFrame
result_df = pd.DataFrame({
'INDEX_VALUE': index_values,
'INDEX_RETURN': index_returns
})
return result_df

255
config.py Normal file
View File

@ -0,0 +1,255 @@
sector_indices = {
'metals_mining': 'MOEXMM', # Индекс Металлов и добычи
'oil_gas': 'MOEXOG', # Индекс Нефти и газа
'chemicals': 'MOEXCH', # Индекс Химии и нефтехимии
'electric_utilities': 'MOEXEU', # Индекс Электроэнергетики
'telecom': 'MOEXTL', # Индекс Телекоммуникаций
'finance': 'MOEXFN', # Индекс Финансов
'consumer': 'MOEXCN', # Индекс Потребительского сектора
'transport': 'MOEXTN' # Индекс Транспорта
}
sector_tickers = {'metals_mining': ['ALRS',
'AMEZ',
'BELO',
'BLNG',
'CHEP',
'CHMF',
'CHMK',
'CHZN',
'ENPG',
'GMKN',
'KBTK',
'KOGK',
'LNZL',
'LNZLP',
'MAGN',
'MGOK',
'MTLR',
'MTLRP',
'NLMK',
'PGIL',
'PLZL',
'PMTL',
'POGR',
'POLY',
'RASP',
'RUAL',
'RUALR',
'SELG',
'SELGP',
'TRMK',
'UGLD',
'UNKL',
'VSMO',
'VSMZ'],
'oil_gas': ['BANE',
'BANEP',
'GAZP',
'JNOSP',
'KRKNP',
'LKOH',
'MFGS',
'MFGSP',
'NOTK',
'NVTK',
'RITK',
'RNFT',
'RNHSP',
'ROSN',
'SIBN',
'SNGS',
'SNGSP',
'TATN',
'TATNP',
'TNBP',
'TNBPP',
'TRMK',
'TRNFP'],
'chemicals': ['AKRN',
'AZKM',
'DGBZ',
'DGBZP',
'KAZT',
'KZOS',
'KZOSP',
'MGNZ',
'NKNC',
'NKNCP',
'OMSH',
'PHOR',
'SILV',
'URKA',
'YASH'],
'electric_utilities': ['ARSB',
'BEGY',
'DVEC',
'EESR',
'EESRP',
'ELFV',
'ENRU',
'EONR',
'FEES',
'HYDR',
'IRAO',
'IRGZ',
'KISB',
'KRNG',
'KRSG',
'LSNG',
'LSNGP',
'MGSV',
'MRKC',
'MRKH',
'MRKK',
'MRKP',
'MRKS',
'MRKU',
'MRKV',
'MRKY',
'MRKZ',
'MSNG',
'MSRS',
'MSSB',
'MSSV',
'OGK1',
'OGK2',
'OGK4',
'OGK6',
'OGKA',
'OGKB',
'OGKC',
'OGKD',
'OGKE',
'OGKF',
'RSTI',
'RSTIP',
'SAGO',
'SARE',
'SVER',
'TGKA',
'TGKB',
'TGKD',
'TGKE',
'TGKF',
'TGKH',
'TGKI',
'TGKJ',
'TGKN',
'TNSE',
'UPRO',
'VRAO',
'VTGK',
'YKEN'],
'telecom': ['AFKC',
'AFKS',
'BISV',
'BISVP',
'CMST',
'CNTL',
'CNTLP',
'CTLK',
'DLSV',
'DLSVP',
'MFON',
'MGTS',
'MGTSP',
'MTSI',
'MTSS',
'RTKM',
'RTKMP',
'SPTL',
'SPTLP',
'STKM',
'STKMP',
'TTLK',
'URSI',
'URSIP',
'UTEL',
'VTEL',
'VTELP'],
'finance': ['AFKS',
'BSPB',
'BSPBP',
'CBOM',
'EPLN',
'FTRE',
'LEAS',
'MBNK',
'MMBM',
'MOEX',
'PSBR',
'QIWI',
'RENI',
'ROSB',
'SBER',
'SBERP',
'SFIN',
'SPBE',
'T',
'TAVR',
'TCSG',
'TRHN',
'URSAP',
'VTBR',
'VTBS',
'VZRZ',
'VZRZP',
'YRSL',
'ZAYM'],
'consumer': ['ABIO',
'AGRO',
'APTK',
'AQUA',
'AVAZ',
'AVAZP',
'BELU',
'DELI',
'DIXY',
'DSKY',
'EUTR',
'FIVE',
'FIXP',
'GCHE',
'GEMC',
'GRAZ',
'HNFG',
'ISKJ',
'KLNA',
'LENT',
'LIFE',
'LNTA',
'MDMG',
'MGNT',
'MVID',
'OBUV',
'OKEY',
'ORUP',
'OTCP',
'PHST',
'PKBA',
'PKBAP',
'PRMD',
'PRTK',
'ROST',
'RSEA',
'SCOH',
'SCON',
'SVAV',
'SYNG',
'VFRM',
'VRPH',
'VSEH',
'WBDF',
'WUSH',
'YNDX'],
'transport': ['AFLT',
'FESH',
'FLOT',
'GLTR',
'GTRK',
'NKHP',
'NMTP',
'TAER',
'TRCN',
'UTAR']}

View File

@ -1,5 +1,11 @@
import sys
import os
# Добавляем путь к родительской директории
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
import sqlite3
from exchange import ForexDataHandler
from classes.exchange import ForexDataHandler
# Создаем или подключаемся к базе данных
conn = sqlite3.connect('currency_rates.db')

101
get/tick_history.py Normal file
View File

@ -0,0 +1,101 @@
import pandas as pd
import os
import sys
import sqlite3
from datetime import datetime, timedelta
import asyncio
# Добавляем путь к родительской директории
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from classes.moex_history import MOEXHistoricalData
from config import sector_tickers
class DataLoader:
def __init__(self, db_path: str):
"""
Инициализация загрузчика данных.
Args:
db_path: Путь к файлу базы данных SQLite
"""
self.db_path = db_path
self.moex_data_handler = MOEXHistoricalData()
def _get_connection(self):
"""
Создает и возвращает соединение с базой данных.
Returns:
sqlite3.Connection: Объект соединения с базой данных
"""
return sqlite3.connect(self.db_path)
async def load_sector_data(self, sector: str):
"""
Получение и сохранение исторических данных по сектору.
Args:
sector: Название сектора
"""
end_date = datetime.now()
start_date = end_date - timedelta(days=730) # За последние 2 года
# Получаем данные по официальному секторному индексу
sector_df = await self.moex_data_handler.get_official_sector_index(
sector,
start_date.strftime('%Y-%m-%d'),
end_date.strftime('%Y-%m-%d')
)
# Сохраняем данные в таблицу
with self._get_connection() as conn:
sector_df.to_sql(sector, conn, if_exists='replace', index=False)
print(f"Данные для сектора '{sector}' сохранены в таблице '{sector}'.")
async def load_ticker_data(self, tickers: list):
"""
Получение и сохранение исторических данных по тикерам.
Args:
tickers: Список тикеров
"""
end_date = datetime.now()
start_date = end_date - timedelta(days=730) # За последние 2 года
tasks = []
for ticker in tickers:
task = self.moex_data_handler.get_security_history(
ticker,
start_date.strftime('%Y-%m-%d'),
end_date.strftime('%Y-%m-%d')
)
tasks.append(task)
results = await asyncio.gather(*tasks)
with self._get_connection() as conn:
for ticker, df in zip(tickers, results):
df.to_sql(ticker, conn, if_exists='replace', index=False)
print(f"Данные для тикера '{ticker}' сохранены в таблице '{ticker}'.")
async def load_data(self, sector: str, tickers: list):
"""
Загрузка данных по сектору и тикерам.
Args:
sector: Название сектора
tickers: Список тикеров
"""
await asyncio.gather(
self.load_sector_data(sector),
self.load_ticker_data(tickers)
)
# Пример использования
if __name__ == "__main__":
db_path = 'moex_data.db' # Путь к файлу базы данных SQLite
data_loader = DataLoader(db_path)
for sector in sector_tickers.keys():
asyncio.run(data_loader.load_data(sector, sector_tickers[sector]))

29
get_news_and_analyze.py Normal file
View File

@ -0,0 +1,29 @@
from openai import OpenAI
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key="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",
)
completion = client.chat.completions.create(
extra_body={},
model="google/gemini-2.0-pro-exp-02-05:free",
messages=[
{
"role": "user",
"content": [
{
"type": "text",
"text": "What is in this image?"
},
{
"type": "image_url",
"image_url": {
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"
}
}
]
}
]
)
print(completion.choices[0].message.content)

1668
open_router.ipynb Normal file

File diff suppressed because one or more lines are too long

View File

@ -15,4 +15,5 @@ seaborn
statsmodels
xgboost
lightgbm
groq
groq
yfinance

262
sector_tickers.json Normal file
View File

@ -0,0 +1,262 @@
{
"metals_mining": [
"ALRS",
"AMEZ",
"BELO",
"BLNG",
"CHEP",
"CHMF",
"CHMK",
"CHZN",
"ENPG",
"GMKN",
"KBTK",
"KOGK",
"LNZL",
"LNZLP",
"MAGN",
"MGOK",
"MTLR",
"MTLRP",
"NLMK",
"PGIL",
"PLZL",
"PMTL",
"POGR",
"POLY",
"RASP",
"RUAL",
"RUALR",
"SELG",
"SELGP",
"TRMK",
"UGLD",
"UNKL",
"VSMO",
"VSMZ"
],
"oil_gas": [
"BANE",
"BANEP",
"GAZP",
"JNOSP",
"KRKNP",
"LKOH",
"MFGS",
"MFGSP",
"NOTK",
"NVTK",
"RITK",
"RNFT",
"RNHSP",
"ROSN",
"SIBN",
"SNGS",
"SNGSP",
"TATN",
"TATNP",
"TNBP",
"TNBPP",
"TRMK",
"TRNFP"
],
"chemicals": [
"AKRN",
"AZKM",
"DGBZ",
"DGBZP",
"KAZT",
"KZOS",
"KZOSP",
"MGNZ",
"NKNC",
"NKNCP",
"OMSH",
"PHOR",
"SILV",
"URKA",
"YASH"
],
"electric_utilities": [
"ARSB",
"BEGY",
"DVEC",
"EESR",
"EESRP",
"ELFV",
"ENRU",
"EONR",
"FEES",
"HYDR",
"IRAO",
"IRGZ",
"KISB",
"KRNG",
"KRSG",
"LSNG",
"LSNGP",
"MGSV",
"MRKC",
"MRKH",
"MRKK",
"MRKP",
"MRKS",
"MRKU",
"MRKV",
"MRKY",
"MRKZ",
"MSNG",
"MSRS",
"MSSB",
"MSSV",
"OGK1",
"OGK2",
"OGK4",
"OGK6",
"OGKA",
"OGKB",
"OGKC",
"OGKD",
"OGKE",
"OGKF",
"RSTI",
"RSTIP",
"SAGO",
"SARE",
"SVER",
"TGKA",
"TGKB",
"TGKD",
"TGKE",
"TGKF",
"TGKH",
"TGKI",
"TGKJ",
"TGKN",
"TNSE",
"UPRO",
"VRAO",
"VTGK",
"YKEN"
],
"telecom": [
"AFKC",
"AFKS",
"BISV",
"BISVP",
"CMST",
"CNTL",
"CNTLP",
"CTLK",
"DLSV",
"DLSVP",
"MFON",
"MGTS",
"MGTSP",
"MTSI",
"MTSS",
"RTKM",
"RTKMP",
"SPTL",
"SPTLP",
"STKM",
"STKMP",
"TTLK",
"URSI",
"URSIP",
"UTEL",
"VTEL",
"VTELP"
],
"finance": [
"AFKS",
"BSPB",
"BSPBP",
"CBOM",
"EPLN",
"FTRE",
"LEAS",
"MBNK",
"MMBM",
"MOEX",
"PSBR",
"QIWI",
"RENI",
"ROSB",
"SBER",
"SBERP",
"SFIN",
"SPBE",
"T",
"TAVR",
"TCSG",
"TRHN",
"URSAP",
"VTBR",
"VTBS",
"VZRZ",
"VZRZP",
"YRSL",
"ZAYM"
],
"consumer": [
"ABIO",
"AGRO",
"APTK",
"AQUA",
"AVAZ",
"AVAZP",
"BELU",
"DELI",
"DIXY",
"DSKY",
"EUTR",
"FIVE",
"FIXP",
"GCHE",
"GEMC",
"GRAZ",
"HNFG",
"ISKJ",
"KLNA",
"LENT",
"LIFE",
"LNTA",
"MDMG",
"MGNT",
"MVID",
"OBUV",
"OKEY",
"ORUP",
"OTCP",
"PHST",
"PKBA",
"PKBAP",
"PRMD",
"PRTK",
"ROST",
"RSEA",
"SCOH",
"SCON",
"SVAV",
"SYNG",
"VFRM",
"VRPH",
"VSEH",
"WBDF",
"WUSH",
"YNDX"
],
"transport": [
"AFLT",
"FESH",
"FLOT",
"GLTR",
"GTRK",
"NKHP",
"NMTP",
"TAER",
"TRCN",
"UTAR"
]
}

2269
test.ipynb Normal file

File diff suppressed because it is too large Load Diff