Compare commits
12 Commits
f08254ec6a
...
46662a37a4
Author | SHA1 | Date | |
---|---|---|---|
|
46662a37a4 | ||
|
d87733b80e | ||
|
30ed373ac5 | ||
|
c11c2bccf4 | ||
|
600d6b76f6 | ||
|
4fb70813e6 | ||
|
35de3b208c | ||
|
5b67db68ea | ||
|
41d0f4060f | ||
|
13d5987e49 | ||
|
f5aa0e5848 | ||
|
b4cc94a444 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -209,3 +209,4 @@ pyrightconfig.json
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/python,go
|
||||
.aider*
|
||||
*.db
|
||||
|
14
devenv.lock
14
devenv.lock
@ -74,16 +74,16 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1758532697,
|
||||
"owner": "cachix",
|
||||
"repo": "devenv-nixpkgs",
|
||||
"rev": "207a4cb0e1253c7658c6736becc6eb9cace1f25f",
|
||||
"lastModified": 1760775323,
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "cef7b5a1998b7d7f4b945168421141f50f98e4ed",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"ref": "rolling",
|
||||
"repo": "devenv-nixpkgs",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "cef7b5a1998b7d7f4b945168421141f50f98e4ed",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
|
@ -3,8 +3,9 @@
|
||||
{
|
||||
env.GREET = "devenv";
|
||||
cachix.enable = false;
|
||||
dotenv.disableHint = true;
|
||||
|
||||
packages = with pkgs; [ poetry ];
|
||||
packages = with pkgs; [ poetry repomix ];
|
||||
|
||||
languages.python = {
|
||||
enable = true;
|
||||
|
@ -1,7 +1,7 @@
|
||||
# yaml-language-server: $schema=https://devenv.sh/devenv.schema.json
|
||||
inputs:
|
||||
nixpkgs:
|
||||
url: github:cachix/devenv-nixpkgs/rolling
|
||||
url: github:nixos/nixpkgs/cef7b5a1998b7d7f4b945168421141f50f98e4ed
|
||||
|
||||
# If you're using non-OSS software, you can set allowUnfree to true.
|
||||
# allowUnfree: true
|
||||
|
39
paperone.py
39
paperone.py
@ -1,39 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from sys import exit
|
||||
from dotenv import load_dotenv
|
||||
from typing import NoReturn
|
||||
from paperone.utils import (
|
||||
parse_date_yyyymmdd,
|
||||
is_trading_day,
|
||||
get_last_n_trading_days,
|
||||
)
|
||||
from os import environ
|
||||
from paperone.client import Client
|
||||
from rich.progress import track
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
def main() -> NoReturn:
|
||||
api_key = environ.get("API_KEY")
|
||||
|
||||
if not api_key:
|
||||
print("API_KEY not set")
|
||||
exit(0)
|
||||
|
||||
client = Client(api_key)
|
||||
date = parse_date_yyyymmdd("20250821")
|
||||
days_range = 60
|
||||
dates_range = get_last_n_trading_days(date, days_range)
|
||||
# tickers = ["VIX"]
|
||||
# indicators = list(IndicatorEnum)
|
||||
|
||||
for x in track([x for x in dates_range if is_trading_day(x)]):
|
||||
print(client.ticker_data_for("AAPL", x))
|
||||
|
||||
exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,67 +1,74 @@
|
||||
from datetime import datetime, timedelta
|
||||
from .taapi import TaapiClient
|
||||
from .data import TickerData
|
||||
from typing import List
|
||||
from .models import TickerOHLCV
|
||||
import yfinance as yf
|
||||
import pandas as pd
|
||||
|
||||
|
||||
class Client:
|
||||
def __init__(self, taapi_key: str):
|
||||
self._taapi = TaapiClient(taapi_key)
|
||||
|
||||
class Fetcher:
|
||||
@staticmethod
|
||||
def ticker_data_for(ticker: str, date: datetime) -> TickerData | None:
|
||||
# Set end date to next day to ensure we get the target date
|
||||
start_date = date.strftime("%Y-%m-%d")
|
||||
end_date = (date + timedelta(days=1)).strftime("%Y-%m-%d")
|
||||
def ticker_data_for(
|
||||
ticker: str, date: datetime, end_date: datetime | None = None
|
||||
) -> List[TickerOHLCV]:
|
||||
"""
|
||||
Fetch OHLCV data for a ticker for a date range.
|
||||
|
||||
Args:
|
||||
ticker: Stock ticker symbol
|
||||
date: Start date (inclusive)
|
||||
end_date: End date (inclusive). If None, fetches only the start date.
|
||||
|
||||
Returns:
|
||||
List of TickerOHLCV records, one per trading day in the range.
|
||||
Returns empty list if no data available.
|
||||
"""
|
||||
start_date_str = date.strftime("%Y-%m-%d")
|
||||
|
||||
if not end_date:
|
||||
end_date_str = (date + timedelta(days=1)).strftime("%Y-%m-%d")
|
||||
else:
|
||||
end_date_str = (end_date + timedelta(days=1)).strftime("%Y-%m-%d")
|
||||
|
||||
try:
|
||||
data = yf.download(
|
||||
ticker,
|
||||
start=start_date,
|
||||
end=end_date,
|
||||
start=start_date_str,
|
||||
end=end_date_str,
|
||||
auto_adjust=True,
|
||||
progress=False,
|
||||
)
|
||||
|
||||
if data.empty:
|
||||
return None
|
||||
return []
|
||||
|
||||
row = data.iloc[0]
|
||||
results: List[TickerOHLCV] = []
|
||||
|
||||
open_price = (
|
||||
float(row["Open"].iloc[0])
|
||||
if isinstance(row["Open"], pd.Series)
|
||||
else float(row["Open"])
|
||||
)
|
||||
high = (
|
||||
float(row["High"].iloc[0])
|
||||
if isinstance(row["High"], pd.Series)
|
||||
else float(row["High"])
|
||||
)
|
||||
low = (
|
||||
float(row["Low"].iloc[0])
|
||||
if isinstance(row["Low"], pd.Series)
|
||||
else float(row["Low"])
|
||||
)
|
||||
close = (
|
||||
float(row["Close"].iloc[0])
|
||||
if isinstance(row["Close"], pd.Series)
|
||||
else float(row["Close"])
|
||||
)
|
||||
volume = (
|
||||
int(row["Volume"].iloc[0])
|
||||
if isinstance(row["Volume"], pd.Series)
|
||||
else int(row["Volume"])
|
||||
if "Volume" in row
|
||||
else 0
|
||||
)
|
||||
for idx, row in data.iterrows():
|
||||
# Extract datetime from index
|
||||
if hasattr(idx, "to_pydatetime"):
|
||||
row_date = idx.to_pydatetime()
|
||||
elif isinstance(idx, datetime):
|
||||
row_date = idx
|
||||
else:
|
||||
row_date = pd.to_datetime(idx).to_pydatetime()
|
||||
|
||||
# Extract values (handle both Series and scalar)
|
||||
def safe_extract(value):
|
||||
if isinstance(value, pd.Series):
|
||||
return float(value.iloc[0])
|
||||
return float(value)
|
||||
|
||||
open_price = safe_extract(row["Open"])
|
||||
high = safe_extract(row["High"])
|
||||
low = safe_extract(row["Low"])
|
||||
close = safe_extract(row["Close"])
|
||||
volume = int(safe_extract(row.get("Volume", 0)))
|
||||
|
||||
# Calculate average price
|
||||
avg = (high + low) / 2.0
|
||||
|
||||
return TickerData(
|
||||
date=date,
|
||||
ohlcv = TickerOHLCV(
|
||||
ticker=ticker,
|
||||
date=row_date,
|
||||
open=round(open_price, 2),
|
||||
high=round(high, 2),
|
||||
low=round(low, 2),
|
||||
@ -69,8 +76,45 @@ class Client:
|
||||
avg=round(avg, 2),
|
||||
volume=volume,
|
||||
)
|
||||
results.append(ohlcv)
|
||||
|
||||
return results
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error fetching data for {ticker} on {start_date}: {str(e)}")
|
||||
print(
|
||||
f"Error fetching data for {ticker} from {start_date_str} to {end_date_str}: {str(e)}"
|
||||
)
|
||||
return []
|
||||
|
||||
return None
|
||||
@staticmethod
|
||||
def ticker_data_for_single_day(ticker: str, date: datetime) -> TickerOHLCV | None:
|
||||
"""
|
||||
Fetch OHLCV data for a single day (backward compatibility).
|
||||
|
||||
Args:
|
||||
ticker: Stock ticker symbol
|
||||
date: The specific date
|
||||
|
||||
Returns:
|
||||
TickerOHLCV record for that day, or None if not available
|
||||
"""
|
||||
results = Fetcher.ticker_data_for(ticker, date, end_date=None)
|
||||
|
||||
return results[0] if results else None
|
||||
|
||||
@staticmethod
|
||||
def ticker_data_for_range(
|
||||
ticker: str, start_date: datetime, end_date: datetime
|
||||
) -> List[TickerOHLCV]:
|
||||
"""
|
||||
Fetch OHLCV data for a date range (explicit range method).
|
||||
|
||||
Args:
|
||||
ticker: Stock ticker symbol
|
||||
start_date: Start date (inclusive)
|
||||
end_date: End date (inclusive)
|
||||
|
||||
Returns:
|
||||
List of TickerOHLCV records for the range
|
||||
"""
|
||||
return Fetcher.ticker_data_for(ticker, start_date, end_date)
|
||||
|
477
paperone/database.py
Normal file
477
paperone/database.py
Normal file
@ -0,0 +1,477 @@
|
||||
from sqlmodel import SQLModel, Session, create_engine, select
|
||||
from typing import List, Optional, Tuple
|
||||
from datetime import datetime
|
||||
from contextlib import contextmanager
|
||||
from .models import TickerOHLCV, IndicatorsData
|
||||
from .entities import TimeSeriesTickerData
|
||||
|
||||
|
||||
class TradingDataCRUD:
|
||||
"""
|
||||
CRUD operations for trading data with SQLModel/SQLite.
|
||||
Handles OHLCV data and technical indicators with proper session management.
|
||||
"""
|
||||
|
||||
def __init__(self, database_url: str = "sqlite:///trading_data.db"):
|
||||
"""
|
||||
Initialize the CRUD manager with database connection.
|
||||
|
||||
Args:
|
||||
database_url: SQLite database URL (default: local file)
|
||||
"""
|
||||
self.engine = create_engine(
|
||||
database_url,
|
||||
echo=False, # Set to True for SQL query debugging
|
||||
connect_args={"check_same_thread": False}, # Needed for SQLite
|
||||
)
|
||||
self._create_tables()
|
||||
|
||||
def _create_tables(self):
|
||||
"""Create all tables if they don't exist."""
|
||||
SQLModel.metadata.create_all(self.engine)
|
||||
|
||||
@contextmanager
|
||||
def get_session(self):
|
||||
"""Context manager for database sessions with automatic cleanup."""
|
||||
session = Session(self.engine, expire_on_commit=False)
|
||||
|
||||
try:
|
||||
yield session
|
||||
session.commit()
|
||||
except Exception:
|
||||
session.rollback()
|
||||
raise
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
# ========================================================================
|
||||
# CREATE Operations
|
||||
# ========================================================================
|
||||
|
||||
def create_ohlcv(self, ohlcv: TickerOHLCV) -> TickerOHLCV:
|
||||
"""
|
||||
Insert a single OHLCV record.
|
||||
|
||||
Args:
|
||||
ohlcv: TickerOHLCV instance to insert
|
||||
|
||||
Returns:
|
||||
The inserted TickerOHLCV record
|
||||
"""
|
||||
with self.get_session() as session:
|
||||
session.add(ohlcv)
|
||||
session.commit()
|
||||
session.refresh(ohlcv)
|
||||
return ohlcv
|
||||
|
||||
def create_ohlcv_bulk(self, ohlcv_list: List[TickerOHLCV]) -> int:
|
||||
"""
|
||||
Bulk insert OHLCV records (more efficient for large datasets).
|
||||
|
||||
Args:
|
||||
ohlcv_list: List of TickerOHLCV instances
|
||||
|
||||
Returns:
|
||||
Number of records inserted
|
||||
"""
|
||||
with self.get_session() as session:
|
||||
session.add_all(ohlcv_list)
|
||||
session.commit()
|
||||
return len(ohlcv_list)
|
||||
|
||||
def create_indicators(self, indicators: IndicatorsData) -> IndicatorsData:
|
||||
"""
|
||||
Insert a single indicators record.
|
||||
|
||||
Args:
|
||||
indicators: IndicatorsData instance to insert
|
||||
|
||||
Returns:
|
||||
The inserted IndicatorsData record
|
||||
"""
|
||||
with self.get_session() as session:
|
||||
session.add(indicators)
|
||||
session.commit()
|
||||
session.refresh(indicators)
|
||||
return indicators
|
||||
|
||||
def create_indicators_bulk(self, indicators_list: List[IndicatorsData]) -> int:
|
||||
"""
|
||||
Bulk insert indicators records.
|
||||
|
||||
Args:
|
||||
indicators_list: List of IndicatorsData instances
|
||||
|
||||
Returns:
|
||||
Number of records inserted
|
||||
"""
|
||||
with self.get_session() as session:
|
||||
session.add_all(indicators_list)
|
||||
session.commit()
|
||||
return len(indicators_list)
|
||||
|
||||
# ========================================================================
|
||||
# READ Operations
|
||||
# ========================================================================
|
||||
|
||||
def get_ohlcv(self, ticker: str, date: datetime) -> Optional[TickerOHLCV]:
|
||||
"""
|
||||
Get a single OHLCV record by ticker and date.
|
||||
|
||||
Args:
|
||||
ticker: Stock ticker symbol
|
||||
date: Trading date
|
||||
|
||||
Returns:
|
||||
TickerOHLCV record or None if not found
|
||||
"""
|
||||
with self.get_session() as session:
|
||||
statement = select(TickerOHLCV).where(
|
||||
TickerOHLCV.ticker == ticker, TickerOHLCV.date == date
|
||||
)
|
||||
return session.exec(statement).first()
|
||||
|
||||
def get_ohlcv_range(
|
||||
self, ticker: str, start_date: datetime, end_date: datetime
|
||||
) -> List[TickerOHLCV]:
|
||||
"""
|
||||
Get OHLCV records for a ticker within a date range.
|
||||
|
||||
Args:
|
||||
ticker: Stock ticker symbol
|
||||
start_date: Start of date range (inclusive)
|
||||
end_date: End of date range (inclusive)
|
||||
|
||||
Returns:
|
||||
List of TickerOHLCV records, sorted by date
|
||||
"""
|
||||
with self.get_session() as session:
|
||||
statement = (
|
||||
select(TickerOHLCV)
|
||||
.where(
|
||||
TickerOHLCV.ticker == ticker,
|
||||
TickerOHLCV.date >= start_date,
|
||||
TickerOHLCV.date <= end_date,
|
||||
)
|
||||
.order_by(TickerOHLCV.date)
|
||||
)
|
||||
return list(session.exec(statement).all())
|
||||
|
||||
def get_ohlcv_latest(self, ticker: str, limit: int = 1) -> List[TickerOHLCV]:
|
||||
"""
|
||||
Get the most recent OHLCV record(s) for a ticker.
|
||||
|
||||
Args:
|
||||
ticker: Stock ticker symbol
|
||||
limit: Number of recent records to retrieve
|
||||
|
||||
Returns:
|
||||
List of most recent TickerOHLCV records, sorted by date descending
|
||||
"""
|
||||
with self.get_session() as session:
|
||||
statement = (
|
||||
select(TickerOHLCV)
|
||||
.where(TickerOHLCV.ticker == ticker)
|
||||
.order_by(TickerOHLCV.date.desc())
|
||||
.limit(limit)
|
||||
)
|
||||
return list(session.exec(statement).all())
|
||||
|
||||
def get_indicators(self, ticker: str, date: datetime) -> Optional[IndicatorsData]:
|
||||
"""
|
||||
Get indicators for a specific ticker and date.
|
||||
|
||||
Args:
|
||||
ticker: Stock ticker symbol
|
||||
date: Trading date
|
||||
|
||||
Returns:
|
||||
IndicatorsData record or None if not found
|
||||
"""
|
||||
with self.get_session() as session:
|
||||
statement = select(IndicatorsData).where(
|
||||
IndicatorsData.ticker == ticker, IndicatorsData.date == date
|
||||
)
|
||||
return session.exec(statement).first()
|
||||
|
||||
def get_indicators_range(
|
||||
self, ticker: str, start_date: datetime, end_date: datetime
|
||||
) -> List[IndicatorsData]:
|
||||
"""
|
||||
Get indicators for a ticker within a date range.
|
||||
|
||||
Args:
|
||||
ticker: Stock ticker symbol
|
||||
start_date: Start of date range (inclusive)
|
||||
end_date: End of date range (inclusive)
|
||||
|
||||
Returns:
|
||||
List of IndicatorsData records, sorted by date
|
||||
"""
|
||||
with self.get_session() as session:
|
||||
statement = (
|
||||
select(IndicatorsData)
|
||||
.where(
|
||||
IndicatorsData.ticker == ticker,
|
||||
IndicatorsData.date >= start_date,
|
||||
IndicatorsData.date <= end_date,
|
||||
)
|
||||
.order_by(IndicatorsData.date)
|
||||
)
|
||||
return list(session.exec(statement).all())
|
||||
|
||||
def get_ohlcv_with_indicators(
|
||||
self, ticker: str, date: datetime
|
||||
) -> Optional[Tuple[TickerOHLCV, IndicatorsData]]:
|
||||
"""
|
||||
Get OHLCV and indicators together for a specific ticker and date.
|
||||
|
||||
Args:
|
||||
ticker: Stock ticker symbol
|
||||
date: Trading date
|
||||
|
||||
Returns:
|
||||
Tuple of (TickerOHLCV, IndicatorsData) or None if not found
|
||||
"""
|
||||
with self.get_session() as session:
|
||||
ohlcv_statement = select(TickerOHLCV).where(
|
||||
TickerOHLCV.ticker == ticker, TickerOHLCV.date == date
|
||||
)
|
||||
ohlcv = session.exec(ohlcv_statement).first()
|
||||
|
||||
if not ohlcv:
|
||||
return None
|
||||
|
||||
indicators_statement = select(IndicatorsData).where(
|
||||
IndicatorsData.ticker == ticker, IndicatorsData.date == date
|
||||
)
|
||||
indicators = session.exec(indicators_statement).first()
|
||||
|
||||
if not indicators:
|
||||
return None
|
||||
|
||||
return (ohlcv, indicators)
|
||||
|
||||
def get_time_series(
|
||||
self, ticker: str, start_date: datetime, end_date: datetime
|
||||
) -> TimeSeriesTickerData:
|
||||
"""
|
||||
Get time series data for building TimeSeriesTickerData.
|
||||
|
||||
Args:
|
||||
ticker: Stock ticker symbol
|
||||
start_date: Start of date range
|
||||
end_date: End of date range
|
||||
|
||||
Returns:
|
||||
TimeSeriesTickerData instance with OHLCV data
|
||||
"""
|
||||
ohlcv_list = self.get_ohlcv_range(ticker, start_date, end_date)
|
||||
return TimeSeriesTickerData.build_time_series_ticker_data(ticker, ohlcv_list)
|
||||
|
||||
# ========================================================================
|
||||
# UPDATE Operations
|
||||
# ========================================================================
|
||||
|
||||
def update_ohlcv(
|
||||
self, ticker: str, date: datetime, **kwargs
|
||||
) -> Optional[TickerOHLCV]:
|
||||
"""
|
||||
Update OHLCV fields for a specific record.
|
||||
|
||||
Args:
|
||||
ticker: Stock ticker symbol
|
||||
date: Trading date
|
||||
**kwargs: Fields to update (e.g., close=150.0, volume=1000000)
|
||||
|
||||
Returns:
|
||||
Updated TickerOHLCV record or None if not found
|
||||
"""
|
||||
with self.get_session() as session:
|
||||
statement = select(TickerOHLCV).where(
|
||||
TickerOHLCV.ticker == ticker, TickerOHLCV.date == date
|
||||
)
|
||||
ohlcv = session.exec(statement).first()
|
||||
|
||||
if not ohlcv:
|
||||
return None
|
||||
|
||||
for key, value in kwargs.items():
|
||||
if hasattr(ohlcv, key):
|
||||
setattr(ohlcv, key, value)
|
||||
|
||||
session.add(ohlcv)
|
||||
session.commit()
|
||||
session.refresh(ohlcv)
|
||||
return ohlcv
|
||||
|
||||
def update_indicators(
|
||||
self, ticker: str, date: datetime, **kwargs
|
||||
) -> Optional[IndicatorsData]:
|
||||
"""
|
||||
Update indicator fields for a specific record.
|
||||
|
||||
Args:
|
||||
ticker: Stock ticker symbol
|
||||
date: Trading date
|
||||
**kwargs: Fields to update (e.g., rsi_14=55.2, macd_line=1.8)
|
||||
|
||||
Returns:
|
||||
Updated IndicatorsData record or None if not found
|
||||
"""
|
||||
with self.get_session() as session:
|
||||
statement = select(IndicatorsData).where(
|
||||
IndicatorsData.ticker == ticker, IndicatorsData.date == date
|
||||
)
|
||||
indicators = session.exec(statement).first()
|
||||
|
||||
if not indicators:
|
||||
return None
|
||||
|
||||
for key, value in kwargs.items():
|
||||
if hasattr(indicators, key):
|
||||
setattr(indicators, key, value)
|
||||
|
||||
session.add(indicators)
|
||||
session.commit()
|
||||
session.refresh(indicators)
|
||||
return indicators
|
||||
|
||||
# ========================================================================
|
||||
# UPSERT Operations (Insert or Update)
|
||||
# ========================================================================
|
||||
|
||||
def upsert_ohlcv(self, ohlcv: TickerOHLCV) -> TickerOHLCV:
|
||||
"""
|
||||
Insert or update OHLCV record if it already exists.
|
||||
|
||||
Args:
|
||||
ohlcv: TickerOHLCV instance
|
||||
|
||||
Returns:
|
||||
The upserted TickerOHLCV record
|
||||
"""
|
||||
existing = self.get_ohlcv(ohlcv.ticker, ohlcv.date)
|
||||
|
||||
if existing:
|
||||
return self.update_ohlcv(
|
||||
ohlcv.ticker,
|
||||
ohlcv.date,
|
||||
open=ohlcv.open,
|
||||
close=ohlcv.close,
|
||||
low=ohlcv.low,
|
||||
high=ohlcv.high,
|
||||
avg=ohlcv.avg,
|
||||
volume=ohlcv.volume,
|
||||
)
|
||||
else:
|
||||
return self.create_ohlcv(ohlcv)
|
||||
|
||||
def upsert_indicators(self, indicators: IndicatorsData) -> IndicatorsData:
|
||||
"""
|
||||
Insert or update indicators record if it already exists.
|
||||
|
||||
Args:
|
||||
indicators: IndicatorsData instance
|
||||
|
||||
Returns:
|
||||
The upserted IndicatorsData record
|
||||
"""
|
||||
existing = self.get_indicators(indicators.ticker, indicators.date)
|
||||
|
||||
if existing:
|
||||
# Get all indicator fields dynamically
|
||||
indicator_fields = {
|
||||
k: v
|
||||
for k, v in indicators.__dict__.items()
|
||||
if k not in ["ticker", "date", "ohlcv", "_sa_instance_state"]
|
||||
}
|
||||
return self.update_indicators(
|
||||
indicators.ticker, indicators.date, **indicator_fields
|
||||
)
|
||||
else:
|
||||
return self.create_indicators(indicators)
|
||||
|
||||
# ========================================================================
|
||||
# DELETE Operations
|
||||
# ========================================================================
|
||||
|
||||
def delete_ohlcv(self, ticker: str, date: datetime) -> bool:
|
||||
"""
|
||||
Delete an OHLCV record.
|
||||
|
||||
Args:
|
||||
ticker: Stock ticker symbol
|
||||
date: Trading date
|
||||
|
||||
Returns:
|
||||
True if deleted, False if not found
|
||||
"""
|
||||
with self.get_session() as session:
|
||||
statement = select(TickerOHLCV).where(
|
||||
TickerOHLCV.ticker == ticker, TickerOHLCV.date == date
|
||||
)
|
||||
ohlcv = session.exec(statement).first()
|
||||
|
||||
if not ohlcv:
|
||||
return False
|
||||
|
||||
session.delete(ohlcv)
|
||||
session.commit()
|
||||
return True
|
||||
|
||||
def delete_indicators(self, ticker: str, date: datetime) -> bool:
|
||||
"""
|
||||
Delete an indicators record.
|
||||
|
||||
Args:
|
||||
ticker: Stock ticker symbol
|
||||
date: Trading date
|
||||
|
||||
Returns:
|
||||
True if deleted, False if not found
|
||||
"""
|
||||
with self.get_session() as session:
|
||||
statement = select(IndicatorsData).where(
|
||||
IndicatorsData.ticker == ticker, IndicatorsData.date == date
|
||||
)
|
||||
indicators = session.exec(statement).first()
|
||||
|
||||
if not indicators:
|
||||
return False
|
||||
|
||||
session.delete(indicators)
|
||||
session.commit()
|
||||
return True
|
||||
|
||||
def delete_ticker_data(self, ticker: str) -> Tuple[int, int]:
|
||||
"""
|
||||
Delete all data (OHLCV and indicators) for a ticker.
|
||||
|
||||
Args:
|
||||
ticker: Stock ticker symbol
|
||||
|
||||
Returns:
|
||||
Tuple of (ohlcv_deleted_count, indicators_deleted_count)
|
||||
"""
|
||||
with self.get_session() as session:
|
||||
# Delete indicators first (due to foreign key constraint)
|
||||
indicators_statement = select(IndicatorsData).where(
|
||||
IndicatorsData.ticker == ticker
|
||||
)
|
||||
indicators_list = session.exec(indicators_statement).all()
|
||||
indicators_count = len(list(indicators_list))
|
||||
|
||||
for ind in indicators_list:
|
||||
session.delete(ind)
|
||||
|
||||
# Delete OHLCV
|
||||
ohlcv_statement = select(TickerOHLCV).where(TickerOHLCV.ticker == ticker)
|
||||
ohlcv_list = session.exec(ohlcv_statement).all()
|
||||
ohlcv_count = len(list(ohlcv_list))
|
||||
|
||||
for ohlcv in ohlcv_list:
|
||||
session.delete(ohlcv)
|
||||
|
||||
session.commit()
|
||||
return (ohlcv_count, indicators_count)
|
@ -1,56 +1,13 @@
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from typing import List
|
||||
|
||||
|
||||
@dataclass
|
||||
class TickerData:
|
||||
date: datetime
|
||||
open: float
|
||||
close: float
|
||||
low: float
|
||||
high: float
|
||||
avg: float
|
||||
volume: int
|
||||
from .models import TickerOHLCV
|
||||
|
||||
|
||||
@dataclass
|
||||
class TimeSeriesTickerData:
|
||||
ticker: str
|
||||
series: List[TickerData] = field(default_factory=list)
|
||||
|
||||
@staticmethod
|
||||
def build_time_series_ticker_data(
|
||||
ticker: str, all_data: List[TickerData]
|
||||
):
|
||||
ordered = sorted({d.date: d for d in all_data}.values(), key=lambda d: d.date)
|
||||
|
||||
return TimeSeriesTickerData(
|
||||
ticker=ticker,
|
||||
series=ordered,
|
||||
)
|
||||
|
||||
def get_by_date(self, dt: datetime) -> TickerData | None:
|
||||
for d in self.series:
|
||||
if d.date == dt:
|
||||
return d
|
||||
|
||||
return None
|
||||
|
||||
def get_latest(self) -> TickerData | None:
|
||||
if not self.series:
|
||||
return None
|
||||
|
||||
return self.series[-1]
|
||||
|
||||
def get_range(self, start_date: datetime, end_date: datetime) -> List[TickerData]:
|
||||
return [d for d in self.series if start_date <= d.date <= end_date]
|
||||
|
||||
@property
|
||||
def target_date_data(self) -> TickerData | None:
|
||||
"""Return the most recent TickerData (by convention, the target date tick)."""
|
||||
return self.get_latest()
|
||||
|
||||
series: List[TickerOHLCV] = field(default_factory=list)
|
||||
|
||||
|
||||
@dataclass
|
369
paperone/indicators.py
Normal file
369
paperone/indicators.py
Normal file
@ -0,0 +1,369 @@
|
||||
import talib
|
||||
import numpy as np
|
||||
from typing import List
|
||||
from datetime import datetime, timedelta
|
||||
from .models import (
|
||||
TickerOHLCV,
|
||||
IndicatorsData,
|
||||
MarketRegimeIndicators,
|
||||
MomentumIndicators,
|
||||
VolatilityIndicators,
|
||||
VolumeIndicators,
|
||||
SupportResistanceIndicators,
|
||||
TrendIndicators,
|
||||
OHLCVArrays,
|
||||
)
|
||||
from .database import TradingDataCRUD
|
||||
|
||||
|
||||
class IndicatorService:
|
||||
"""
|
||||
Middleware service for calculating technical indicators from stored OHLCV data.
|
||||
|
||||
This service:
|
||||
1. Fetches historical OHLCV data from the database
|
||||
2. Calculates all 29 technical indicators using TA-Lib
|
||||
3. Returns strongly-typed IndicatorsData ready to be saved
|
||||
|
||||
All calculations are done locally without external API calls.
|
||||
"""
|
||||
|
||||
def __init__(self, crud: TradingDataCRUD):
|
||||
"""
|
||||
Initialize the indicator service with database access.
|
||||
|
||||
Args:
|
||||
crud: TradingDataCRUD instance for database operations
|
||||
"""
|
||||
self._crud: TradingDataCRUD = crud
|
||||
|
||||
def calculate_indicators_for_date(
|
||||
self, ticker: str, target_date: datetime, lookback_days: int = 50
|
||||
) -> IndicatorsData | None:
|
||||
"""
|
||||
Calculate all indicators for a ticker on a specific date using stored OHLCV data.
|
||||
|
||||
Args:
|
||||
ticker: Stock ticker symbol
|
||||
target_date: Date to calculate indicators for
|
||||
lookback_days: Number of historical days needed for calculations (default: 50)
|
||||
|
||||
Returns:
|
||||
IndicatorsData instance ready to save to database, or None if insufficient data
|
||||
or if any calculated values are NaN
|
||||
"""
|
||||
# Add indicator-specific validation
|
||||
required_periods = {
|
||||
"MACD": 26 + 9, # slowperiod + signalperiod
|
||||
"ADX": 14 + 14, # Additional warmup needed
|
||||
"RSI": 14 + 1, # Plus warmup
|
||||
}
|
||||
min_required = max(required_periods.values()) + 10 # Safety buffer
|
||||
|
||||
if lookback_days < min_required:
|
||||
return None
|
||||
|
||||
# Fetch historical OHLCV data from database
|
||||
start_date: datetime = target_date - timedelta(
|
||||
days=lookback_days * 2
|
||||
) # Buffer for weekends
|
||||
ohlcv_records: List[TickerOHLCV] = self._crud.get_ohlcv_range(
|
||||
ticker=ticker, start_date=start_date, end_date=target_date
|
||||
)
|
||||
|
||||
if len(ohlcv_records) < min_required:
|
||||
return None
|
||||
|
||||
# Convert to numpy arrays for TA-Lib
|
||||
arrays: OHLCVArrays = self._convert_to_arrays(ohlcv_records)
|
||||
|
||||
# Calculate all indicator categories
|
||||
momentum: MomentumIndicators | None = self._calculate_momentum(arrays)
|
||||
volatility: VolatilityIndicators | None = self._calculate_volatility(arrays)
|
||||
trend: TrendIndicators | None = self._calculate_trend(arrays)
|
||||
volume: VolumeIndicators | None = self._calculate_volume(arrays)
|
||||
support_resistance: SupportResistanceIndicators = (
|
||||
self._calculate_support_resistance(arrays)
|
||||
)
|
||||
market_regime: MarketRegimeIndicators | None = self._calculate_market_regime(
|
||||
arrays
|
||||
)
|
||||
|
||||
# Skip if any indicator group returned None (contains NaN values)
|
||||
if (
|
||||
momentum is None
|
||||
or volatility is None
|
||||
or trend is None
|
||||
or volume is None
|
||||
or market_regime is None
|
||||
):
|
||||
return None
|
||||
|
||||
return IndicatorsData(
|
||||
ticker=ticker,
|
||||
date=target_date,
|
||||
# Momentum (7 indicators)
|
||||
rsi_14=momentum.rsi_14,
|
||||
rsi_20=momentum.rsi_20,
|
||||
macd_line=momentum.macd_line,
|
||||
macd_signal=momentum.macd_signal,
|
||||
macd_histogram=momentum.macd_histogram,
|
||||
stoch_k=momentum.stoch_k,
|
||||
stoch_d=momentum.stoch_d,
|
||||
# Volatility (6 indicators)
|
||||
bb_upper=volatility.bb_upper,
|
||||
bb_middle=volatility.bb_middle,
|
||||
bb_lower=volatility.bb_lower,
|
||||
bb_width=volatility.bb_width,
|
||||
bb_percent=volatility.bb_percent,
|
||||
atr_14=volatility.atr_14,
|
||||
# Trend (4 indicators)
|
||||
adx_14=trend.adx_14,
|
||||
di_plus=trend.di_plus,
|
||||
di_minus=trend.di_minus,
|
||||
sar=trend.sar,
|
||||
# Volume (3 indicators)
|
||||
obv=volume.obv,
|
||||
obv_sma_20=volume.obv_sma_20,
|
||||
volume_roc_5=volume.volume_roc_5,
|
||||
# Support/Resistance (6 indicators)
|
||||
fib_236=support_resistance.fib_236,
|
||||
fib_382=support_resistance.fib_382,
|
||||
fib_618=support_resistance.fib_618,
|
||||
pivot_point=support_resistance.pivot_point,
|
||||
resistance_1=support_resistance.resistance_1,
|
||||
support_1=support_resistance.support_1,
|
||||
# Market Regime (2 indicators)
|
||||
cci_20=market_regime.cci_20,
|
||||
williams_r_14=market_regime.williams_r_14,
|
||||
)
|
||||
|
||||
def calculate_and_save_indicators(
|
||||
self, ticker: str, target_date: datetime, force_update: bool = False
|
||||
) -> IndicatorsData | None:
|
||||
if not force_update:
|
||||
existing = self._crud.get_indicators(ticker=ticker, date=target_date)
|
||||
|
||||
if existing is not None:
|
||||
return existing
|
||||
|
||||
indicators = self.calculate_indicators_for_date(
|
||||
ticker=ticker, target_date=target_date
|
||||
)
|
||||
|
||||
if indicators is None:
|
||||
return None
|
||||
|
||||
return self._crud.upsert_indicators(indicators)
|
||||
|
||||
def bulk_calculate_indicators(
|
||||
self, ticker: str, dates: List[datetime]
|
||||
) -> List[IndicatorsData]:
|
||||
"""
|
||||
Calculate indicators for multiple dates efficiently.
|
||||
|
||||
Args:
|
||||
ticker: Stock ticker symbol
|
||||
dates: List of dates to calculate indicators for
|
||||
|
||||
Returns:
|
||||
List of IndicatorsData instances (may be less than input if some failed)
|
||||
"""
|
||||
results: List[IndicatorsData] = []
|
||||
|
||||
for date in dates:
|
||||
indicators: IndicatorsData | None = self.calculate_indicators_for_date(
|
||||
ticker=ticker, target_date=date
|
||||
)
|
||||
if indicators is not None:
|
||||
results.append(indicators)
|
||||
|
||||
return results
|
||||
|
||||
def bulk_calculate_and_save_indicators(
|
||||
self, ticker: str, dates: List[datetime], force_update: bool = False
|
||||
) -> int:
|
||||
# Batch check existing records
|
||||
if not force_update:
|
||||
existing_dates = self._crud.get_existing_indicator_dates(ticker, dates)
|
||||
dates = [d for d in dates if d not in existing_dates]
|
||||
|
||||
if not dates:
|
||||
return 0
|
||||
|
||||
start_date = min(dates) - timedelta(days=150)
|
||||
end_date = max(dates)
|
||||
all_ohlcv = self._crud.get_ohlcv_range(ticker, start_date, end_date)
|
||||
|
||||
results = []
|
||||
for date in dates:
|
||||
indicators = self._calculate_for_date_from_data(all_ohlcv, date)
|
||||
|
||||
if indicators:
|
||||
results.append(indicators)
|
||||
|
||||
# Batch insert
|
||||
return self._crud.bulk_upsert_indicators(results)
|
||||
|
||||
|
||||
# ========================================================================
|
||||
# PRIVATE: Data Conversion
|
||||
# ========================================================================
|
||||
|
||||
@staticmethod
|
||||
def _convert_to_arrays(ohlcv_records: List[TickerOHLCV]) -> "OHLCVArrays":
|
||||
"""Convert list of TickerOHLCV records to numpy arrays for TA-Lib."""
|
||||
return OHLCVArrays(
|
||||
open=np.array([r.open for r in ohlcv_records], dtype=np.float64),
|
||||
high=np.array([r.high for r in ohlcv_records], dtype=np.float64),
|
||||
low=np.array([r.low for r in ohlcv_records], dtype=np.float64),
|
||||
close=np.array([r.close for r in ohlcv_records], dtype=np.float64),
|
||||
volume=np.array([r.volume for r in ohlcv_records], dtype=np.float64),
|
||||
)
|
||||
|
||||
# ========================================================================
|
||||
# PRIVATE: Indicator Calculations
|
||||
# ========================================================================
|
||||
|
||||
@staticmethod
|
||||
def _calculate_momentum(arrays: "OHLCVArrays") -> "MomentumIndicators":
|
||||
"""Calculate momentum indicators (RSI, MACD, Stochastic)."""
|
||||
rsi_14: float = float(talib.RSI(arrays.close, timeperiod=14)[-1])
|
||||
rsi_20: float = float(talib.RSI(arrays.close, timeperiod=20)[-1])
|
||||
|
||||
macd_line_arr, macd_signal_arr, macd_hist_arr = talib.MACD(
|
||||
arrays.close, fastperiod=12, slowperiod=26, signalperiod=9
|
||||
)
|
||||
|
||||
stoch_k_arr, stoch_d_arr = talib.STOCH(
|
||||
arrays.high,
|
||||
arrays.low,
|
||||
arrays.close,
|
||||
fastk_period=14,
|
||||
slowk_period=3,
|
||||
slowd_period=3,
|
||||
)
|
||||
|
||||
return MomentumIndicators(
|
||||
rsi_14=round(rsi_14, 2),
|
||||
rsi_20=round(rsi_20, 2),
|
||||
macd_line=round(float(macd_line_arr[-1]), 4),
|
||||
macd_signal=round(float(macd_signal_arr[-1]), 4),
|
||||
macd_histogram=round(float(macd_hist_arr[-1]), 4),
|
||||
stoch_k=round(float(stoch_k_arr[-1]), 2),
|
||||
stoch_d=round(float(stoch_d_arr[-1]), 2),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _calculate_volatility(arrays: "OHLCVArrays") -> "VolatilityIndicators":
|
||||
"""Calculate volatility indicators (Bollinger Bands, ATR)."""
|
||||
bb_upper_arr, bb_middle_arr, bb_lower_arr = talib.BBANDS(
|
||||
arrays.close, timeperiod=20, nbdevup=2, nbdevdn=2
|
||||
)
|
||||
|
||||
bb_upper: float = float(bb_upper_arr[-1])
|
||||
bb_middle: float = float(bb_middle_arr[-1])
|
||||
bb_lower: float = float(bb_lower_arr[-1])
|
||||
bb_width: float = bb_upper - bb_lower
|
||||
bb_percent: float = (
|
||||
(arrays.close[-1] - bb_lower) / (bb_upper - bb_lower)
|
||||
if bb_width > 0
|
||||
else 0.5
|
||||
)
|
||||
|
||||
atr_14: float = float(
|
||||
talib.ATR(arrays.high, arrays.low, arrays.close, timeperiod=14)[-1]
|
||||
)
|
||||
|
||||
return VolatilityIndicators(
|
||||
bb_upper=round(bb_upper, 2),
|
||||
bb_middle=round(bb_middle, 2),
|
||||
bb_lower=round(bb_lower, 2),
|
||||
bb_width=round(bb_width, 2),
|
||||
bb_percent=round(bb_percent, 4),
|
||||
atr_14=round(atr_14, 2),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _calculate_trend(arrays: "OHLCVArrays") -> "TrendIndicators":
|
||||
"""Calculate trend indicators (ADX, DI+, DI-, SAR)."""
|
||||
adx_14: float = float(
|
||||
talib.ADX(arrays.high, arrays.low, arrays.close, timeperiod=14)[-1]
|
||||
)
|
||||
di_plus: float = float(
|
||||
talib.PLUS_DI(arrays.high, arrays.low, arrays.close, timeperiod=14)[-1]
|
||||
)
|
||||
di_minus: float = float(
|
||||
talib.MINUS_DI(arrays.high, arrays.low, arrays.close, timeperiod=14)[-1]
|
||||
)
|
||||
sar: float = float(
|
||||
talib.SAR(arrays.high, arrays.low, acceleration=0.02, maximum=0.2)[-1]
|
||||
)
|
||||
|
||||
return TrendIndicators(
|
||||
adx_14=round(adx_14, 2),
|
||||
di_plus=round(di_plus, 2),
|
||||
di_minus=round(di_minus, 2),
|
||||
sar=round(sar, 2),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _calculate_volume(arrays: "OHLCVArrays") -> "VolumeIndicators":
|
||||
"""Calculate volume indicators (OBV, Volume ROC)."""
|
||||
obv_arr: np.ndarray = talib.OBV(arrays.close, arrays.volume)
|
||||
obv: float = float(obv_arr[-1])
|
||||
obv_sma_20: float = float(talib.SMA(obv_arr, timeperiod=20)[-1])
|
||||
volume_roc_5: float = float(talib.ROC(arrays.volume, timeperiod=5)[-1])
|
||||
|
||||
return VolumeIndicators(
|
||||
obv=round(obv, 0),
|
||||
obv_sma_20=round(obv_sma_20, 0),
|
||||
volume_roc_5=round(volume_roc_5, 2),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _calculate_support_resistance(
|
||||
arrays: "OHLCVArrays",
|
||||
) -> "SupportResistanceIndicators":
|
||||
"""Calculate support/resistance levels (Fibonacci, Pivot Points)."""
|
||||
# Fibonacci retracements (last 30 days)
|
||||
lookback: int = min(30, len(arrays.high))
|
||||
max_price: float = float(np.max(arrays.high[-lookback:]))
|
||||
min_price: float = float(np.min(arrays.low[-lookback:]))
|
||||
diff: float = max_price - min_price
|
||||
|
||||
fib_236: float = max_price - (diff * 0.236)
|
||||
fib_382: float = max_price - (diff * 0.382)
|
||||
fib_618: float = max_price - (diff * 0.618)
|
||||
|
||||
# Pivot points (previous day's data)
|
||||
high_prev: float = float(arrays.high[-1])
|
||||
low_prev: float = float(arrays.low[-1])
|
||||
close_prev: float = float(arrays.close[-1])
|
||||
|
||||
pivot_point: float = (high_prev + low_prev + close_prev) / 3
|
||||
resistance_1: float = (2 * pivot_point) - low_prev
|
||||
support_1: float = (2 * pivot_point) - high_prev
|
||||
|
||||
return SupportResistanceIndicators(
|
||||
fib_236=round(fib_236, 2),
|
||||
fib_382=round(fib_382, 2),
|
||||
fib_618=round(fib_618, 2),
|
||||
pivot_point=round(pivot_point, 2),
|
||||
resistance_1=round(resistance_1, 2),
|
||||
support_1=round(support_1, 2),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _calculate_market_regime(arrays: "OHLCVArrays") -> "MarketRegimeIndicators":
|
||||
"""Calculate market regime indicators (CCI, Williams %R)."""
|
||||
cci_20: float = float(
|
||||
talib.CCI(arrays.high, arrays.low, arrays.close, timeperiod=20)[-1]
|
||||
)
|
||||
williams_r_14: float = float(
|
||||
talib.WILLR(arrays.high, arrays.low, arrays.close, timeperiod=14)[-1]
|
||||
)
|
||||
|
||||
return MarketRegimeIndicators(
|
||||
cci_20=round(cci_20, 2), williams_r_14=round(williams_r_14, 2)
|
||||
)
|
163
paperone/models.py
Normal file
163
paperone/models.py
Normal file
@ -0,0 +1,163 @@
|
||||
from sqlmodel import SQLModel, Field, Relationship, ForeignKeyConstraint
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
import numpy as np
|
||||
|
||||
|
||||
class TickerOHLCV(SQLModel, table=True):
|
||||
__tablename__ = "ohlcv"
|
||||
|
||||
ticker: str = Field(primary_key=True)
|
||||
date: datetime = Field(primary_key=True, index=True)
|
||||
open: float
|
||||
close: float
|
||||
low: float
|
||||
high: float
|
||||
avg: float
|
||||
volume: int
|
||||
|
||||
indicators: Optional["IndicatorsData"] = Relationship(
|
||||
back_populates="ohlcv",
|
||||
sa_relationship_kwargs={"uselist": False},
|
||||
)
|
||||
|
||||
|
||||
class IndicatorsData(SQLModel, table=True):
|
||||
__tablename__ = "indicators"
|
||||
__table_args__ = (
|
||||
ForeignKeyConstraint(["ticker", "date"], ["ohlcv.ticker", "ohlcv.date"]),
|
||||
)
|
||||
|
||||
ticker: str = Field(primary_key=True)
|
||||
date: datetime = Field(primary_key=True, index=True)
|
||||
|
||||
# ========================================================================
|
||||
# MOMENTUM INDICATORS (Trend Direction & Strength)
|
||||
# ========================================================================
|
||||
rsi_14: float # Standard 14-period RSI
|
||||
rsi_20: float # Longer 20-period RSI for smoother signal
|
||||
macd_line: float # MACD line (12 EMA - 26 EMA)
|
||||
macd_signal: float # Signal line (9-period EMA of MACD)
|
||||
macd_histogram: float # Histogram (MACD - Signal), shows momentum strength
|
||||
stoch_k: float # Fast stochastic %K (14-period)
|
||||
stoch_d: float # Slow stochastic %D (3-period SMA of %K)
|
||||
|
||||
# ========================================================================
|
||||
# VOLATILITY INDICATORS (Price Dispersion & Risk)
|
||||
# ========================================================================
|
||||
bb_upper: float # Upper Bollinger Band (SMA + 2*std)
|
||||
bb_middle: float # Middle band (20-period SMA)
|
||||
bb_lower: float # Lower Bollinger Band (SMA - 2*std)
|
||||
bb_width: float # Band width (upper - lower), measures volatility magnitude
|
||||
bb_percent: float # %B indicator: (close - lower) / (upper - lower)
|
||||
atr_14: float # 14-period Average True Range
|
||||
|
||||
# ========================================================================
|
||||
# TREND INDICATORS (Trend Presence & Sustainability)
|
||||
# ========================================================================
|
||||
adx_14: float # 14-period ADX (trend strength)
|
||||
di_plus: float # +DI (bullish directional indicator)
|
||||
di_minus: float # -DI (bearish directional indicator)
|
||||
sar: float # Current Parabolic SAR level
|
||||
|
||||
# ========================================================================
|
||||
# VOLUME INDICATORS (Institutional Participation)
|
||||
# ========================================================================
|
||||
obv: float # On-Balance Volume cumulative total
|
||||
obv_sma_20: float # 20-day SMA of OBV (trend confirmation)
|
||||
volume_roc_5: float # 5-day volume rate of change (%)
|
||||
|
||||
# ========================================================================
|
||||
# SUPPORT/RESISTANCE INDICATORS (Key Price Levels)
|
||||
# ========================================================================
|
||||
fib_236: float # 23.6% Fibonacci retracement level
|
||||
fib_382: float # 38.2% Fibonacci retracement level
|
||||
fib_618: float # 61.8% Fibonacci retracement level (golden ratio)
|
||||
pivot_point: float # Standard pivot point (High + Low + Close) / 3
|
||||
resistance_1: float # First resistance level (R1)
|
||||
support_1: float # First support level (S1)
|
||||
|
||||
# ========================================================================
|
||||
# MARKET REGIME INDICATORS (Market Condition Classification)
|
||||
# ========================================================================
|
||||
cci_20: float # 20-period Commodity Channel Index
|
||||
williams_r_14: float # 14-period Williams %R
|
||||
|
||||
# One-to-one relationship back to TickerOHLCV
|
||||
ohlcv: TickerOHLCV = Relationship(back_populates="indicators")
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class OHLCVArrays:
|
||||
"""Numpy arrays of OHLCV data for TA-Lib calculations."""
|
||||
|
||||
open: np.ndarray
|
||||
high: np.ndarray
|
||||
low: np.ndarray
|
||||
close: np.ndarray
|
||||
volume: np.ndarray
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class MomentumIndicators:
|
||||
"""Momentum indicator values (RSI, MACD, Stochastic)."""
|
||||
|
||||
rsi_14: float
|
||||
rsi_20: float
|
||||
macd_line: float
|
||||
macd_signal: float
|
||||
macd_histogram: float
|
||||
stoch_k: float
|
||||
stoch_d: float
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class VolatilityIndicators:
|
||||
"""Volatility indicator values (Bollinger Bands, ATR)."""
|
||||
|
||||
bb_upper: float
|
||||
bb_middle: float
|
||||
bb_lower: float
|
||||
bb_width: float
|
||||
bb_percent: float
|
||||
atr_14: float
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TrendIndicators:
|
||||
"""Trend indicator values (ADX, DI+, DI-, SAR)."""
|
||||
|
||||
adx_14: float
|
||||
di_plus: float
|
||||
di_minus: float
|
||||
sar: float
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class VolumeIndicators:
|
||||
"""Volume indicator values (OBV, Volume ROC)."""
|
||||
|
||||
obv: float
|
||||
obv_sma_20: float
|
||||
volume_roc_5: float
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SupportResistanceIndicators:
|
||||
"""Support/Resistance levels (Fibonacci, Pivot Points)."""
|
||||
|
||||
fib_236: float
|
||||
fib_382: float
|
||||
fib_618: float
|
||||
pivot_point: float
|
||||
resistance_1: float
|
||||
support_1: float
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class MarketRegimeIndicators:
|
||||
"""Market regime indicators (CCI, Williams %R)."""
|
||||
|
||||
cci_20: float
|
||||
williams_r_14: float
|
554
poetry.lock
generated
554
poetry.lock
generated
@ -1,4 +1,16 @@
|
||||
# This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "annotated-types"
|
||||
version = "0.7.0"
|
||||
description = "Reusable constraint types to use with typing.Annotated"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"},
|
||||
{file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asttokens"
|
||||
@ -58,6 +70,27 @@ charset-normalizer = ["charset-normalizer"]
|
||||
html5lib = ["html5lib"]
|
||||
lxml = ["lxml"]
|
||||
|
||||
[[package]]
|
||||
name = "build"
|
||||
version = "1.3.0"
|
||||
description = "A simple, correct Python build frontend"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "build-1.3.0-py3-none-any.whl", hash = "sha256:7145f0b5061ba90a1500d60bd1b13ca0a8a4cebdd0cc16ed8adf1c0e739f43b4"},
|
||||
{file = "build-1.3.0.tar.gz", hash = "sha256:698edd0ea270bde950f53aed21f3a0135672206f3911e0176261a31e0e07b397"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "os_name == \"nt\""}
|
||||
packaging = ">=19.1"
|
||||
pyproject_hooks = "*"
|
||||
|
||||
[package.extras]
|
||||
uv = ["uv (>=0.1.18)"]
|
||||
virtualenv = ["virtualenv (>=20.11) ; python_version < \"3.10\"", "virtualenv (>=20.17) ; python_version >= \"3.10\" and python_version < \"3.14\"", "virtualenv (>=20.31) ; python_version >= \"3.14\""]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2025.10.5"
|
||||
@ -278,7 +311,7 @@ description = "Cross-platform colored terminal text."
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||
groups = ["main"]
|
||||
markers = "platform_system == \"Windows\" or sys_platform == \"win32\""
|
||||
markers = "platform_system == \"Windows\" or sys_platform == \"win32\" or os_name == \"nt\""
|
||||
files = [
|
||||
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
@ -314,6 +347,88 @@ dev = ["charset_normalizer (>=3.3.2,<4.0)", "coverage (>=6.4.1,<7.0)", "cryptogr
|
||||
extra = ["lxml_html_clean", "markdownify (>=1.1.0)", "readability-lxml (>=0.8.1)"]
|
||||
test = ["charset_normalizer (>=3.3.2,<4.0)", "cryptography (>=42.0.5,<43.0)", "fastapi (==0.110.0)", "httpx (==0.23.1)", "proxy.py (>=2.4.3,<3.0)", "pytest (>=8.1.1,<9.0)", "pytest-asyncio (>=0.23.6,<1.0)", "pytest-trio (>=0.8.0,<1.0)", "python-multipart (>=0.0.9,<1.0)", "trio (>=0.25.0,<1.0)", "trustme (>=1.1.0,<2.0)", "typing_extensions", "uvicorn (>=0.29.0,<1.0)", "websockets (>=12.0,<13.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "cython"
|
||||
version = "3.1.4"
|
||||
description = "The Cython compiler for writing C extensions in the Python language."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "cython-3.1.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:523110241408ef6511d897e9cebbdffb99120ac82ef3aea89baacce290958f93"},
|
||||
{file = "cython-3.1.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd34f960c3809fa2a7c3487ce9b3cb2c5bbc5ae2107f073a1a51086885958881"},
|
||||
{file = "cython-3.1.4-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:90842e7fb8cddfd173478670297f6a6b3df090e029a31ea6ce93669030e67b81"},
|
||||
{file = "cython-3.1.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c88234303e2c15a5a88ae21c99698c7195433280b049aa2ad0ace906e6294dab"},
|
||||
{file = "cython-3.1.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f06b037f7c244dda9fc38091e87a68498c85c7c27ddc19aa84b08cf42a8a84a"},
|
||||
{file = "cython-3.1.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1aba748e9dcb9c0179d286cdb20215246c46b69cf227715e46287dcea8de7372"},
|
||||
{file = "cython-3.1.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:297b6042d764f68dc6213312578ef4b69310d04c963f94a489914efbf44ab133"},
|
||||
{file = "cython-3.1.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2ecf927e73bde50043d3a9fe3159f834b0e642b97e60a21018439fd25d344888"},
|
||||
{file = "cython-3.1.4-cp310-cp310-win32.whl", hash = "sha256:3d940d603f85732627795518f9dba8fa63080d8221bb5f477c7a156ee08714ad"},
|
||||
{file = "cython-3.1.4-cp310-cp310-win_amd64.whl", hash = "sha256:1e0671be9859bb313d8df5ca9b9c137e384f1e025831c58cee9a839ace432d3c"},
|
||||
{file = "cython-3.1.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d1d7013dba5fb0506794d4ef8947ff5ed021370614950a8d8d04e57c8c84499e"},
|
||||
{file = "cython-3.1.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:eed989f5c139d6550ef2665b783d86fab99372590c97f10a3c26c4523c5fce9e"},
|
||||
{file = "cython-3.1.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3df3beb8b024dfd73cfddb7f2f7456751cebf6e31655eed3189c209b634bc2f2"},
|
||||
{file = "cython-3.1.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f8354703f1168e1aaa01348940f719734c1f11298be333bdb5b94101d49677c0"},
|
||||
{file = "cython-3.1.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a928bd7d446247855f54f359057ab4a32c465219c8c1e299906a483393a59a9e"},
|
||||
{file = "cython-3.1.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c233bfff4cc7b9d629eecb7345f9b733437f76dc4441951ec393b0a6e29919fc"},
|
||||
{file = "cython-3.1.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e9691a2cbc2faf0cd819108bceccf9bfc56c15a06d172eafe74157388c44a601"},
|
||||
{file = "cython-3.1.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ada319207432ea7c6691c70b5c112d261637d79d21ba086ae3726fedde79bfbf"},
|
||||
{file = "cython-3.1.4-cp311-cp311-win32.whl", hash = "sha256:dae81313c28222bf7be695f85ae1d16625aac35a0973a3af1e001f63379440c5"},
|
||||
{file = "cython-3.1.4-cp311-cp311-win_amd64.whl", hash = "sha256:60d2f192059ac34c5c26527f2beac823d34aaa766ef06792a3b7f290c18ac5e2"},
|
||||
{file = "cython-3.1.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0d26af46505d0e54fe0f05e7ad089fd0eed8fa04f385f3ab88796f554467bcb9"},
|
||||
{file = "cython-3.1.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66ac8bb5068156c92359e3f0eefa138c177d59d1a2e8a89467881fa7d06aba3b"},
|
||||
{file = "cython-3.1.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2e42714faec723d2305607a04bafb49a48a8d8f25dd39368d884c058dbcfbc"},
|
||||
{file = "cython-3.1.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0fd655b27997a209a574873304ded9629de588f021154009e8f923475e2c677"},
|
||||
{file = "cython-3.1.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9def7c41f4dc339003b1e6875f84edf059989b9c7f5e9a245d3ce12c190742d9"},
|
||||
{file = "cython-3.1.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:196555584a8716bf7e017e23ca53e9f632ed493f9faa327d0718e7551588f55d"},
|
||||
{file = "cython-3.1.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7fff0e739e07a20726484b8898b8628a7b87acb960d0fc5486013c6b77b7bb97"},
|
||||
{file = "cython-3.1.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c2754034fa10f95052949cd6b07eb2f61d654c1b9cfa0b17ea53a269389422e8"},
|
||||
{file = "cython-3.1.4-cp312-cp312-win32.whl", hash = "sha256:2e0808ff3614a1dbfd1adfcbff9b2b8119292f1824b3535b4a173205109509f8"},
|
||||
{file = "cython-3.1.4-cp312-cp312-win_amd64.whl", hash = "sha256:f262b32327b6bce340cce5d45bbfe3972cb62543a4930460d8564a489f3aea12"},
|
||||
{file = "cython-3.1.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ab549d0fc187804e0f14fc4759e4b5ad6485ffc01554b2f8b720cc44aeb929cd"},
|
||||
{file = "cython-3.1.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:52eae5d9bcc515441a436dcae2cbadfd00c5063d4d7809bd0178931690c06a76"},
|
||||
{file = "cython-3.1.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6f06345cfa583dd17fff1beedb237853689b85aa400ea9e0db7e5265f3322d15"},
|
||||
{file = "cython-3.1.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f5d915556c757212cb8ddd4e48c16f2ab481dbb9a76f5153ab26f418c3537eb5"},
|
||||
{file = "cython-3.1.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c3f3bb603f28b3c1df66baaa5cdbf6029578552b458f1d321bae23b87f6c3199"},
|
||||
{file = "cython-3.1.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7aff230893ee1044e7bc98d313c034ead70a3dd54d4d22e89ca1734540d94084"},
|
||||
{file = "cython-3.1.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8e83f114c04f72f85591ddb0b28f08ab2e40d250c26686d6509c0f70a9e2ca34"},
|
||||
{file = "cython-3.1.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8096394960d38b793545753b73781bc0ec695f0b8c22454431704b297e296045"},
|
||||
{file = "cython-3.1.4-cp313-cp313-win32.whl", hash = "sha256:4e7c726ac753ca1a5aa30286cbadcd10ed4b4312ea710a8a16bb908d41e9c059"},
|
||||
{file = "cython-3.1.4-cp313-cp313-win_amd64.whl", hash = "sha256:f2ee2bb77943044f301cec04d0b51d8e3810507c9c250d6cd079a3e2d6ba88f2"},
|
||||
{file = "cython-3.1.4-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c7258739d5560918741cb040bd85ba7cc2f09d868de9116a637e06714fec1f69"},
|
||||
{file = "cython-3.1.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b2d522ee8d3528035e247ee721fb40abe92e9ea852dc9e48802cec080d5de859"},
|
||||
{file = "cython-3.1.4-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a4e0560baeb56c29d7d8d693a050dd4d2ed922d8d7c66f5c5715c6f2be84e903"},
|
||||
{file = "cython-3.1.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4223cacc81cba0df0f06f79657c5d6286e153b9a9b989dad1cdf4666f618c073"},
|
||||
{file = "cython-3.1.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ff4d1f159edee6af38572318681388fbd6448b0d08b9a47494aaf0b698e93394"},
|
||||
{file = "cython-3.1.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2537c53071a9a124e0bc502a716e1930d9bb101e94c26673016cf1820e4fdbd1"},
|
||||
{file = "cython-3.1.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:85416717c529fb5ccf908464657a5187753e76d7b6ffec9b1c2d91544f6c3628"},
|
||||
{file = "cython-3.1.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:18882e2f5c0e0c25f9c44f16f2fb9c48f33988885c5f9eae2856f10c6f089ffa"},
|
||||
{file = "cython-3.1.4-cp314-cp314-win32.whl", hash = "sha256:8ef8deadc888eaf95e5328fc176fb6c37bccee1213f07517c6ea55b5f817c457"},
|
||||
{file = "cython-3.1.4-cp314-cp314-win_amd64.whl", hash = "sha256:acb99ddec62ba1ea5de0e0087760fa834ec42c94f0488065a4f1995584e8e94e"},
|
||||
{file = "cython-3.1.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:949074a2445c8fe2b84e81b6cf9c23b30b92e853ad05689457d6735acb2ca738"},
|
||||
{file = "cython-3.1.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:533970de5d96ca9ba207aa096caa883e2365ce7d41f0531f070292842b4ba97a"},
|
||||
{file = "cython-3.1.4-cp38-cp38-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:078e4e48d3867abf1ba11ca6bd2d62974a326ef2c0d139daa6691f5e01a691c5"},
|
||||
{file = "cython-3.1.4-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c162d63c2a7636864bcee0f333d974ece325d4fbc33e75d38886926e1cc997a1"},
|
||||
{file = "cython-3.1.4-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e10357ea1acc27b42d83aaeb14cd94587b515806a41227a0100028d45140d753"},
|
||||
{file = "cython-3.1.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:71435feecca14f4de4b3a6b3828d8b1ae4a3b535e53597c32d5f22bc87f3d2bb"},
|
||||
{file = "cython-3.1.4-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:d24c8a4fb10c089ec45fcc1c44f73df5b4228c95f20539cc4aade7a8933be31c"},
|
||||
{file = "cython-3.1.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:2ccbd6db72f864c6717dcc62e44673e81cfe38984277748818cbe427f461573c"},
|
||||
{file = "cython-3.1.4-cp38-cp38-win32.whl", hash = "sha256:94fd9a7807fdd7cffe82c0b7167a9f5fcf0e7c6ef518d89bed66430bd9107854"},
|
||||
{file = "cython-3.1.4-cp38-cp38-win_amd64.whl", hash = "sha256:b47a82cfbc8e6a87143f02e1b33b7a0332bd1b83b668cb41ebcb5e4f9a39bc09"},
|
||||
{file = "cython-3.1.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b95d2303ee54cf469f7c61aa94ef46c195d42e75a76881b9e33770bcf6c0a5c6"},
|
||||
{file = "cython-3.1.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1fc723ffca257beebe42c8e2dcb2b9771957b752d6f42684f17ca1d2a4346b21"},
|
||||
{file = "cython-3.1.4-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1fb5060dd0c89f5c64002a0c44c2fcb5d066d119e2ae4d1bfa2c6482333dd42a"},
|
||||
{file = "cython-3.1.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:077cc5881da9b48cc7b7f7952faedcea0ad9c3fcb9ba9f5fb89fdb5ded07dd70"},
|
||||
{file = "cython-3.1.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:33da3c03797f7b7fde371a05f9e0d63eca679875c1c75e01066893eff2ec2f12"},
|
||||
{file = "cython-3.1.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:daf1bde7af8202f52cfd51625a5c48de55de27786392e38a429bfe8b9a16161f"},
|
||||
{file = "cython-3.1.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3371af4af1aae0d46c10f8e3c1edff0361f03a5687081c8b36f61678545caff3"},
|
||||
{file = "cython-3.1.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d4e1115c6b4b848ade9d76a4e3f72c7bb2b7e7a935fcbda56f6032f50e079999"},
|
||||
{file = "cython-3.1.4-cp39-cp39-win32.whl", hash = "sha256:1b59709bcec2f38e447e53c51a20caee8c30911d4025dd3d102835f3f10b7bef"},
|
||||
{file = "cython-3.1.4-cp39-cp39-win_amd64.whl", hash = "sha256:006e2a175ba9898a7f1551e6c7c4cafd8eb9b42e53d1dfe4308caba9e8453cc3"},
|
||||
{file = "cython-3.1.4-py3-none-any.whl", hash = "sha256:d194d95e4fa029a3f6c7d46bdd16d973808c7ea4797586911fdb67cb98b1a2c6"},
|
||||
{file = "cython-3.1.4.tar.gz", hash = "sha256:9aefefe831331e2d66ab31799814eae4d0f8a2d246cbaaaa14d1be29ef777683"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "decorator"
|
||||
version = "5.2.1"
|
||||
@ -413,6 +528,75 @@ files = [
|
||||
{file = "frozendict-2.4.6.tar.gz", hash = "sha256:df7cd16470fbd26fc4969a208efadc46319334eb97def1ddf48919b351192b8e"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "greenlet"
|
||||
version = "3.2.4"
|
||||
description = "Lightweight in-process concurrent programming"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""
|
||||
files = [
|
||||
{file = "greenlet-3.2.4-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8c68325b0d0acf8d91dde4e6f930967dd52a5302cd4062932a6b2e7c2969f47c"},
|
||||
{file = "greenlet-3.2.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:94385f101946790ae13da500603491f04a76b6e4c059dab271b3ce2e283b2590"},
|
||||
{file = "greenlet-3.2.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f10fd42b5ee276335863712fa3da6608e93f70629c631bf77145021600abc23c"},
|
||||
{file = "greenlet-3.2.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c8c9e331e58180d0d83c5b7999255721b725913ff6bc6cf39fa2a45841a4fd4b"},
|
||||
{file = "greenlet-3.2.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58b97143c9cc7b86fc458f215bd0932f1757ce649e05b640fea2e79b54cedb31"},
|
||||
{file = "greenlet-3.2.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2ca18a03a8cfb5b25bc1cbe20f3d9a4c80d8c3b13ba3df49ac3961af0b1018d"},
|
||||
{file = "greenlet-3.2.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9fe0a28a7b952a21e2c062cd5756d34354117796c6d9215a87f55e38d15402c5"},
|
||||
{file = "greenlet-3.2.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8854167e06950ca75b898b104b63cc646573aa5fef1353d4508ecdd1ee76254f"},
|
||||
{file = "greenlet-3.2.4-cp310-cp310-win_amd64.whl", hash = "sha256:73f49b5368b5359d04e18d15828eecc1806033db5233397748f4ca813ff1056c"},
|
||||
{file = "greenlet-3.2.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:96378df1de302bc38e99c3a9aa311967b7dc80ced1dcc6f171e99842987882a2"},
|
||||
{file = "greenlet-3.2.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1ee8fae0519a337f2329cb78bd7a8e128ec0f881073d43f023c7b8d4831d5246"},
|
||||
{file = "greenlet-3.2.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:94abf90142c2a18151632371140b3dba4dee031633fe614cb592dbb6c9e17bc3"},
|
||||
{file = "greenlet-3.2.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:4d1378601b85e2e5171b99be8d2dc85f594c79967599328f95c1dc1a40f1c633"},
|
||||
{file = "greenlet-3.2.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0db5594dce18db94f7d1650d7489909b57afde4c580806b8d9203b6e79cdc079"},
|
||||
{file = "greenlet-3.2.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2523e5246274f54fdadbce8494458a2ebdcdbc7b802318466ac5606d3cded1f8"},
|
||||
{file = "greenlet-3.2.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1987de92fec508535687fb807a5cea1560f6196285a4cde35c100b8cd632cc52"},
|
||||
{file = "greenlet-3.2.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:55e9c5affaa6775e2c6b67659f3a71684de4c549b3dd9afca3bc773533d284fa"},
|
||||
{file = "greenlet-3.2.4-cp311-cp311-win_amd64.whl", hash = "sha256:9c40adce87eaa9ddb593ccb0fa6a07caf34015a29bf8d344811665b573138db9"},
|
||||
{file = "greenlet-3.2.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3b67ca49f54cede0186854a008109d6ee71f66bd57bb36abd6d0a0267b540cdd"},
|
||||
{file = "greenlet-3.2.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddf9164e7a5b08e9d22511526865780a576f19ddd00d62f8a665949327fde8bb"},
|
||||
{file = "greenlet-3.2.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f28588772bb5fb869a8eb331374ec06f24a83a9c25bfa1f38b6993afe9c1e968"},
|
||||
{file = "greenlet-3.2.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5c9320971821a7cb77cfab8d956fa8e39cd07ca44b6070db358ceb7f8797c8c9"},
|
||||
{file = "greenlet-3.2.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c60a6d84229b271d44b70fb6e5fa23781abb5d742af7b808ae3f6efd7c9c60f6"},
|
||||
{file = "greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0"},
|
||||
{file = "greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0"},
|
||||
{file = "greenlet-3.2.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:20fb936b4652b6e307b8f347665e2c615540d4b42b3b4c8a321d8286da7e520f"},
|
||||
{file = "greenlet-3.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:a7d4e128405eea3814a12cc2605e0e6aedb4035bf32697f72deca74de4105e02"},
|
||||
{file = "greenlet-3.2.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:1a921e542453fe531144e91e1feedf12e07351b1cf6c9e8a3325ea600a715a31"},
|
||||
{file = "greenlet-3.2.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd3c8e693bff0fff6ba55f140bf390fa92c994083f838fece0f63be121334945"},
|
||||
{file = "greenlet-3.2.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:710638eb93b1fa52823aa91bf75326f9ecdfd5e0466f00789246a5280f4ba0fc"},
|
||||
{file = "greenlet-3.2.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c5111ccdc9c88f423426df3fd1811bfc40ed66264d35aa373420a34377efc98a"},
|
||||
{file = "greenlet-3.2.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76383238584e9711e20ebe14db6c88ddcedc1829a9ad31a584389463b5aa504"},
|
||||
{file = "greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23768528f2911bcd7e475210822ffb5254ed10d71f4028387e5a99b4c6699671"},
|
||||
{file = "greenlet-3.2.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:00fadb3fedccc447f517ee0d3fd8fe49eae949e1cd0f6a611818f4f6fb7dc83b"},
|
||||
{file = "greenlet-3.2.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d25c5091190f2dc0eaa3f950252122edbbadbb682aa7b1ef2f8af0f8c0afefae"},
|
||||
{file = "greenlet-3.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:554b03b6e73aaabec3745364d6239e9e012d64c68ccd0b8430c64ccc14939a8b"},
|
||||
{file = "greenlet-3.2.4-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:49a30d5fda2507ae77be16479bdb62a660fa51b1eb4928b524975b3bde77b3c0"},
|
||||
{file = "greenlet-3.2.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:299fd615cd8fc86267b47597123e3f43ad79c9d8a22bebdce535e53550763e2f"},
|
||||
{file = "greenlet-3.2.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c17b6b34111ea72fc5a4e4beec9711d2226285f0386ea83477cbb97c30a3f3a5"},
|
||||
{file = "greenlet-3.2.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b4a1870c51720687af7fa3e7cda6d08d801dae660f75a76f3845b642b4da6ee1"},
|
||||
{file = "greenlet-3.2.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:061dc4cf2c34852b052a8620d40f36324554bc192be474b9e9770e8c042fd735"},
|
||||
{file = "greenlet-3.2.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44358b9bf66c8576a9f57a590d5f5d6e72fa4228b763d0e43fee6d3b06d3a337"},
|
||||
{file = "greenlet-3.2.4-cp314-cp314-win_amd64.whl", hash = "sha256:e37ab26028f12dbb0ff65f29a8d3d44a765c61e729647bf2ddfbbed621726f01"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:b6a7c19cf0d2742d0809a4c05975db036fdff50cd294a93632d6a310bf9ac02c"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:27890167f55d2387576d1f41d9487ef171849ea0359ce1510ca6e06c8bece11d"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:18d9260df2b5fbf41ae5139e1be4e796d99655f023a636cd0e11e6406cca7d58"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:671df96c1f23c4a0d4077a325483c1503c96a1b7d9db26592ae770daa41233d4"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:16458c245a38991aa19676900d48bd1a6f2ce3e16595051a4db9d012154e8433"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9913f1a30e4526f432991f89ae263459b1c64d1608c0d22a5c79c287b3c70df"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b90654e092f928f110e0007f572007c9727b5265f7632c2fa7415b4689351594"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:81701fd84f26330f0d5f4944d4e92e61afe6319dcd9775e39396e39d7c3e5f98"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-win32.whl", hash = "sha256:65458b409c1ed459ea899e939f0e1cdb14f58dbc803f2f93c5eab5694d32671b"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-win_amd64.whl", hash = "sha256:d2e685ade4dafd447ede19c31277a224a239a0a1a4eca4e6390efedf20260cfb"},
|
||||
{file = "greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["Sphinx", "furo"]
|
||||
test = ["objgraph", "psutil", "setuptools"]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.10"
|
||||
@ -653,6 +837,18 @@ files = [
|
||||
{file = "numpy-2.3.4.tar.gz", hash = "sha256:a7d018bfedb375a8d979ac758b120ba846a7fe764911a64465fd87b8729f4a6a"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "25.0"
|
||||
description = "Core utilities for Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"},
|
||||
{file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pandas"
|
||||
version = "2.3.3"
|
||||
@ -905,6 +1101,158 @@ files = [
|
||||
{file = "pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.12.2"
|
||||
description = "Data validation using Python type hints"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "pydantic-2.12.2-py3-none-any.whl", hash = "sha256:25ff718ee909acd82f1ff9b1a4acfd781bb23ab3739adaa7144f19a6a4e231ae"},
|
||||
{file = "pydantic-2.12.2.tar.gz", hash = "sha256:7b8fa15b831a4bbde9d5b84028641ac3080a4ca2cbd4a621a661687e741624fd"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
annotated-types = ">=0.6.0"
|
||||
pydantic-core = "2.41.4"
|
||||
typing-extensions = ">=4.14.1"
|
||||
typing-inspection = ">=0.4.2"
|
||||
|
||||
[package.extras]
|
||||
email = ["email-validator (>=2.0.0)"]
|
||||
timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic-core"
|
||||
version = "2.41.4"
|
||||
description = "Core functionality for Pydantic validation and serialization"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "pydantic_core-2.41.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2442d9a4d38f3411f22eb9dd0912b7cbf4b7d5b6c92c4173b75d3e1ccd84e36e"},
|
||||
{file = "pydantic_core-2.41.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:30a9876226dda131a741afeab2702e2d127209bde3c65a2b8133f428bc5d006b"},
|
||||
{file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d55bbac04711e2980645af68b97d445cdbcce70e5216de444a6c4b6943ebcccd"},
|
||||
{file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e1d778fb7849a42d0ee5927ab0f7453bf9f85eef8887a546ec87db5ddb178945"},
|
||||
{file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b65077a4693a98b90ec5ad8f203ad65802a1b9b6d4a7e48066925a7e1606706"},
|
||||
{file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62637c769dee16eddb7686bf421be48dfc2fae93832c25e25bc7242e698361ba"},
|
||||
{file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dfe3aa529c8f501babf6e502936b9e8d4698502b2cfab41e17a028d91b1ac7b"},
|
||||
{file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ca2322da745bf2eeb581fc9ea3bbb31147702163ccbcbf12a3bb630e4bf05e1d"},
|
||||
{file = "pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e8cd3577c796be7231dcf80badcf2e0835a46665eaafd8ace124d886bab4d700"},
|
||||
{file = "pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:1cae8851e174c83633f0833e90636832857297900133705ee158cf79d40f03e6"},
|
||||
{file = "pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a26d950449aae348afe1ac8be5525a00ae4235309b729ad4d3399623125b43c9"},
|
||||
{file = "pydantic_core-2.41.4-cp310-cp310-win32.whl", hash = "sha256:0cf2a1f599efe57fa0051312774280ee0f650e11152325e41dfd3018ef2c1b57"},
|
||||
{file = "pydantic_core-2.41.4-cp310-cp310-win_amd64.whl", hash = "sha256:a8c2e340d7e454dc3340d3d2e8f23558ebe78c98aa8f68851b04dcb7bc37abdc"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:28ff11666443a1a8cf2a044d6a545ebffa8382b5f7973f22c36109205e65dc80"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:61760c3925d4633290292bad462e0f737b840508b4f722247d8729684f6539ae"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eae547b7315d055b0de2ec3965643b0ab82ad0106a7ffd29615ee9f266a02827"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ef9ee5471edd58d1fcce1c80ffc8783a650e3e3a193fe90d52e43bb4d87bff1f"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:15dd504af121caaf2c95cb90c0ebf71603c53de98305621b94da0f967e572def"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a926768ea49a8af4d36abd6a8968b8790f7f76dd7cbd5a4c180db2b4ac9a3a2"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6916b9b7d134bff5440098a4deb80e4cb623e68974a87883299de9124126c2a8"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5cf90535979089df02e6f17ffd076f07237efa55b7343d98760bde8743c4b265"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7533c76fa647fade2d7ec75ac5cc079ab3f34879626dae5689b27790a6cf5a5c"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:37e516bca9264cbf29612539801ca3cd5d1be465f940417b002905e6ed79d38a"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0c19cb355224037c83642429b8ce261ae108e1c5fbf5c028bac63c77b0f8646e"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-win32.whl", hash = "sha256:09c2a60e55b357284b5f31f5ab275ba9f7f70b7525e18a132ec1f9160b4f1f03"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-win_amd64.whl", hash = "sha256:711156b6afb5cb1cb7c14a2cc2c4a8b4c717b69046f13c6b332d8a0a8f41ca3e"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-win_arm64.whl", hash = "sha256:6cb9cf7e761f4f8a8589a45e49ed3c0d92d1d696a45a6feaee8c904b26efc2db"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ab06d77e053d660a6faaf04894446df7b0a7e7aba70c2797465a0a1af00fc887"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c53ff33e603a9c1179a9364b0a24694f183717b2e0da2b5ad43c316c956901b2"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:304c54176af2c143bd181d82e77c15c41cbacea8872a2225dd37e6544dce9999"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:025ba34a4cf4fb32f917d5d188ab5e702223d3ba603be4d8aca2f82bede432a4"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9f5f30c402ed58f90c70e12eff65547d3ab74685ffe8283c719e6bead8ef53f"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd96e5d15385d301733113bcaa324c8bcf111275b7675a9c6e88bfb19fc05e3b"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98f348cbb44fae6e9653c1055db7e29de67ea6a9ca03a5fa2c2e11a47cff0e47"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec22626a2d14620a83ca583c6f5a4080fa3155282718b6055c2ea48d3ef35970"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3a95d4590b1f1a43bf33ca6d647b990a88f4a3824a8c4572c708f0b45a5290ed"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:f9672ab4d398e1b602feadcffcdd3af44d5f5e6ddc15bc7d15d376d47e8e19f8"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:84d8854db5f55fead3b579f04bda9a36461dab0730c5d570e1526483e7bb8431"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-win32.whl", hash = "sha256:9be1c01adb2ecc4e464392c36d17f97e9110fbbc906bcbe1c943b5b87a74aabd"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-win_amd64.whl", hash = "sha256:d682cf1d22bab22a5be08539dca3d1593488a99998f9f412137bc323179067ff"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-win_arm64.whl", hash = "sha256:833eebfd75a26d17470b58768c1834dfc90141b7afc6eb0429c21fc5a21dcfb8"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:85e050ad9e5f6fe1004eec65c914332e52f429bc0ae12d6fa2092407a462c746"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7393f1d64792763a48924ba31d1e44c2cfbc05e3b1c2c9abb4ceeadd912cced"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94dab0940b0d1fb28bcab847adf887c66a27a40291eedf0b473be58761c9799a"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:de7c42f897e689ee6f9e93c4bec72b99ae3b32a2ade1c7e4798e690ff5246e02"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:664b3199193262277b8b3cd1e754fb07f2c6023289c815a1e1e8fb415cb247b1"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d95b253b88f7d308b1c0b417c4624f44553ba4762816f94e6986819b9c273fb2"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1351f5bbdbbabc689727cb91649a00cb9ee7203e0a6e54e9f5ba9e22e384b84"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1affa4798520b148d7182da0615d648e752de4ab1a9566b7471bc803d88a062d"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7b74e18052fea4aa8dea2fb7dbc23d15439695da6cbe6cfc1b694af1115df09d"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:285b643d75c0e30abda9dc1077395624f314a37e3c09ca402d4015ef5979f1a2"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f52679ff4218d713b3b33f88c89ccbf3a5c2c12ba665fb80ccc4192b4608dbab"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-win32.whl", hash = "sha256:ecde6dedd6fff127c273c76821bb754d793be1024bc33314a120f83a3c69460c"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-win_amd64.whl", hash = "sha256:d081a1f3800f05409ed868ebb2d74ac39dd0c1ff6c035b5162356d76030736d4"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-win_arm64.whl", hash = "sha256:f8e49c9c364a7edcbe2a310f12733aad95b022495ef2a8d653f645e5d20c1564"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ed97fd56a561f5eb5706cebe94f1ad7c13b84d98312a05546f2ad036bafe87f4"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a870c307bf1ee91fc58a9a61338ff780d01bfae45922624816878dce784095d2"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d25e97bc1f5f8f7985bdc2335ef9e73843bb561eb1fa6831fdfc295c1c2061cf"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313t-win_amd64.whl", hash = "sha256:d405d14bea042f166512add3091c1af40437c2e7f86988f3915fabd27b1e9cd2"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313t-win_arm64.whl", hash = "sha256:19f3684868309db5263a11bace3c45d93f6f24afa2ffe75a647583df22a2ff89"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:e9205d97ed08a82ebb9a307e92914bb30e18cdf6f6b12ca4bedadb1588a0bfe1"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:82df1f432b37d832709fbcc0e24394bba04a01b6ecf1ee87578145c19cde12ac"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3b4cc4539e055cfa39a3763c939f9d409eb40e85813257dcd761985a108554"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b1eb1754fce47c63d2ff57fdb88c351a6c0150995890088b33767a10218eaa4e"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6ab5ab30ef325b443f379ddb575a34969c333004fca5a1daa0133a6ffaad616"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:31a41030b1d9ca497634092b46481b937ff9397a86f9f51bd41c4767b6fc04af"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a44ac1738591472c3d020f61c6df1e4015180d6262ebd39bf2aeb52571b60f12"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d72f2b5e6e82ab8f94ea7d0d42f83c487dc159c5240d8f83beae684472864e2d"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:c4d1e854aaf044487d31143f541f7aafe7b482ae72a022c664b2de2e466ed0ad"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b568af94267729d76e6ee5ececda4e283d07bbb28e8148bb17adad93d025d25a"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6d55fb8b1e8929b341cc313a81a26e0d48aa3b519c1dbaadec3a6a2b4fcad025"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-win32.whl", hash = "sha256:5b66584e549e2e32a1398df11da2e0a7eff45d5c2d9db9d5667c5e6ac764d77e"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-win_amd64.whl", hash = "sha256:557a0aab88664cc552285316809cab897716a372afaf8efdbef756f8b890e894"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-win_arm64.whl", hash = "sha256:3f1ea6f48a045745d0d9f325989d8abd3f1eaf47dd00485912d1a3a63c623a8d"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6c1fe4c5404c448b13188dd8bd2ebc2bdd7e6727fa61ff481bcc2cca894018da"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:523e7da4d43b113bf8e7b49fa4ec0c35bf4fe66b2230bfc5c13cc498f12c6c3e"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5729225de81fb65b70fdb1907fcf08c75d498f4a6f15af005aabb1fdadc19dfa"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314t-win_amd64.whl", hash = "sha256:de2cfbb09e88f0f795fd90cf955858fc2c691df65b1f21f0aa00b99f3fbc661d"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314t-win_arm64.whl", hash = "sha256:d34f950ae05a83e0ede899c595f312ca976023ea1db100cd5aa188f7005e3ab0"},
|
||||
{file = "pydantic_core-2.41.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:646e76293345954acea6966149683047b7b2ace793011922208c8e9da12b0062"},
|
||||
{file = "pydantic_core-2.41.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cc8e85a63085a137d286e2791037f5fdfff0aabb8b899483ca9c496dd5797338"},
|
||||
{file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:692c622c8f859a17c156492783902d8370ac7e121a611bd6fe92cc71acf9ee8d"},
|
||||
{file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d1e2906efb1031a532600679b424ef1d95d9f9fb507f813951f23320903adbd7"},
|
||||
{file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e04e2f7f8916ad3ddd417a7abdd295276a0bf216993d9318a5d61cc058209166"},
|
||||
{file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df649916b81822543d1c8e0e1d079235f68acdc7d270c911e8425045a8cfc57e"},
|
||||
{file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66c529f862fdba70558061bb936fe00ddbaaa0c647fd26e4a4356ef1d6561891"},
|
||||
{file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3b4c5a1fd3a311563ed866c2c9b62da06cb6398bee186484ce95c820db71cb"},
|
||||
{file = "pydantic_core-2.41.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6e0fc40d84448f941df9b3334c4b78fe42f36e3bf631ad54c3047a0cdddc2514"},
|
||||
{file = "pydantic_core-2.41.4-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:44e7625332683b6c1c8b980461475cde9595eff94447500e80716db89b0da005"},
|
||||
{file = "pydantic_core-2.41.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:170ee6835f6c71081d031ef1c3b4dc4a12b9efa6a9540f93f95b82f3c7571ae8"},
|
||||
{file = "pydantic_core-2.41.4-cp39-cp39-win32.whl", hash = "sha256:3adf61415efa6ce977041ba9745183c0e1f637ca849773afa93833e04b163feb"},
|
||||
{file = "pydantic_core-2.41.4-cp39-cp39-win_amd64.whl", hash = "sha256:a238dd3feee263eeaeb7dc44aea4ba1364682c4f9f9467e6af5596ba322c2332"},
|
||||
{file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:a1b2cfec3879afb742a7b0bcfa53e4f22ba96571c9e54d6a3afe1052d17d843b"},
|
||||
{file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:d175600d975b7c244af6eb9c9041f10059f20b8bbffec9e33fdd5ee3f67cdc42"},
|
||||
{file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f184d657fa4947ae5ec9c47bd7e917730fa1cbb78195037e32dcbab50aca5ee"},
|
||||
{file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed810568aeffed3edc78910af32af911c835cc39ebbfacd1f0ab5dd53028e5c"},
|
||||
{file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:4f5d640aeebb438517150fdeec097739614421900e4a08db4a3ef38898798537"},
|
||||
{file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:4a9ab037b71927babc6d9e7fc01aea9e66dc2a4a34dff06ef0724a4049629f94"},
|
||||
{file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4dab9484ec605c3016df9ad4fd4f9a390bc5d816a3b10c6550f8424bb80b18c"},
|
||||
{file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8a5028425820731d8c6c098ab642d7b8b999758e24acae03ed38a66eca8335"},
|
||||
{file = "pydantic_core-2.41.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1e5ab4fc177dd41536b3c32b2ea11380dd3d4619a385860621478ac2d25ceb00"},
|
||||
{file = "pydantic_core-2.41.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:3d88d0054d3fa11ce936184896bed3c1c5441d6fa483b498fac6a5d0dd6f64a9"},
|
||||
{file = "pydantic_core-2.41.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b2a054a8725f05b4b6503357e0ac1c4e8234ad3b0c2ac130d6ffc66f0e170e2"},
|
||||
{file = "pydantic_core-2.41.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0d9db5a161c99375a0c68c058e227bee1d89303300802601d76a3d01f74e258"},
|
||||
{file = "pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:6273ea2c8ffdac7b7fda2653c49682db815aebf4a89243a6feccf5e36c18c347"},
|
||||
{file = "pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:4c973add636efc61de22530b2ef83a65f39b6d6f656df97f678720e20de26caa"},
|
||||
{file = "pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b69d1973354758007f46cf2d44a4f3d0933f10b6dc9bf15cf1356e037f6f731a"},
|
||||
{file = "pydantic_core-2.41.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3619320641fd212aaf5997b6ca505e97540b7e16418f4a241f44cdf108ffb50d"},
|
||||
{file = "pydantic_core-2.41.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:491535d45cd7ad7e4a2af4a5169b0d07bebf1adfd164b0368da8aa41e19907a5"},
|
||||
{file = "pydantic_core-2.41.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:54d86c0cada6aba4ec4c047d0e348cbad7063b87ae0f005d9f8c9ad04d4a92a2"},
|
||||
{file = "pydantic_core-2.41.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eca1124aced216b2500dc2609eade086d718e8249cb9696660ab447d50a758bd"},
|
||||
{file = "pydantic_core-2.41.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c9024169becccf0cb470ada03ee578d7348c119a0d42af3dcf9eda96e3a247c"},
|
||||
{file = "pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:26895a4268ae5a2849269f4991cdc97236e4b9c010e51137becf25182daac405"},
|
||||
{file = "pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:ca4df25762cf71308c446e33c9b1fdca2923a3f13de616e2a949f38bf21ff5a8"},
|
||||
{file = "pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:5a28fcedd762349519276c36634e71853b4541079cab4acaaac60c4421827308"},
|
||||
{file = "pydantic_core-2.41.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c173ddcd86afd2535e2b695217e82191580663a1d1928239f877f5a1649ef39f"},
|
||||
{file = "pydantic_core-2.41.4.tar.gz", hash = "sha256:70e47929a9d4a1905a67e4b687d5946026390568a8e952b92824118063cee4d5"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
typing-extensions = ">=4.14.1"
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.19.2"
|
||||
@ -936,6 +1284,18 @@ files = [
|
||||
doc = ["sphinx (>=6.1.3,<6.2.0)", "sphinx_rtd_theme (>=1.2.0,<1.3.0)"]
|
||||
test = ["beautifulsoup4", "flake8", "pytest", "pytest-cov"]
|
||||
|
||||
[[package]]
|
||||
name = "pyproject-hooks"
|
||||
version = "1.2.0"
|
||||
description = "Wrappers to call pyproject.toml-based build backend hooks."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913"},
|
||||
{file = "pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "python-dateutil"
|
||||
version = "2.9.0.post0"
|
||||
@ -1055,6 +1415,118 @@ files = [
|
||||
{file = "soupsieve-2.8.tar.gz", hash = "sha256:e2dd4a40a628cb5f28f6d4b0db8800b8f581b65bb380b97de22ba5ca8d72572f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlalchemy"
|
||||
version = "2.0.44"
|
||||
description = "Database Abstraction Library"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "SQLAlchemy-2.0.44-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:471733aabb2e4848d609141a9e9d56a427c0a038f4abf65dd19d7a21fd563632"},
|
||||
{file = "SQLAlchemy-2.0.44-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48bf7d383a35e668b984c805470518b635d48b95a3c57cb03f37eaa3551b5f9f"},
|
||||
{file = "SQLAlchemy-2.0.44-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bf4bb6b3d6228fcf3a71b50231199fb94d2dd2611b66d33be0578ea3e6c2726"},
|
||||
{file = "SQLAlchemy-2.0.44-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:e998cf7c29473bd077704cea3577d23123094311f59bdc4af551923b168332b1"},
|
||||
{file = "SQLAlchemy-2.0.44-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:ebac3f0b5732014a126b43c2b7567f2f0e0afea7d9119a3378bde46d3dcad88e"},
|
||||
{file = "SQLAlchemy-2.0.44-cp37-cp37m-win32.whl", hash = "sha256:3255d821ee91bdf824795e936642bbf43a4c7cedf5d1aed8d24524e66843aa74"},
|
||||
{file = "SQLAlchemy-2.0.44-cp37-cp37m-win_amd64.whl", hash = "sha256:78e6c137ba35476adb5432103ae1534f2f5295605201d946a4198a0dea4b38e7"},
|
||||
{file = "sqlalchemy-2.0.44-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c77f3080674fc529b1bd99489378c7f63fcb4ba7f8322b79732e0258f0ea3ce"},
|
||||
{file = "sqlalchemy-2.0.44-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4c26ef74ba842d61635b0152763d057c8d48215d5be9bb8b7604116a059e9985"},
|
||||
{file = "sqlalchemy-2.0.44-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4a172b31785e2f00780eccab00bc240ccdbfdb8345f1e6063175b3ff12ad1b0"},
|
||||
{file = "sqlalchemy-2.0.44-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9480c0740aabd8cb29c329b422fb65358049840b34aba0adf63162371d2a96e"},
|
||||
{file = "sqlalchemy-2.0.44-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:17835885016b9e4d0135720160db3095dc78c583e7b902b6be799fb21035e749"},
|
||||
{file = "sqlalchemy-2.0.44-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cbe4f85f50c656d753890f39468fcd8190c5f08282caf19219f684225bfd5fd2"},
|
||||
{file = "sqlalchemy-2.0.44-cp310-cp310-win32.whl", hash = "sha256:2fcc4901a86ed81dc76703f3b93ff881e08761c63263c46991081fd7f034b165"},
|
||||
{file = "sqlalchemy-2.0.44-cp310-cp310-win_amd64.whl", hash = "sha256:9919e77403a483ab81e3423151e8ffc9dd992c20d2603bf17e4a8161111e55f5"},
|
||||
{file = "sqlalchemy-2.0.44-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fe3917059c7ab2ee3f35e77757062b1bea10a0b6ca633c58391e3f3c6c488dd"},
|
||||
{file = "sqlalchemy-2.0.44-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:de4387a354ff230bc979b46b2207af841dc8bf29847b6c7dbe60af186d97aefa"},
|
||||
{file = "sqlalchemy-2.0.44-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3678a0fb72c8a6a29422b2732fe423db3ce119c34421b5f9955873eb9b62c1e"},
|
||||
{file = "sqlalchemy-2.0.44-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cf6872a23601672d61a68f390e44703442639a12ee9dd5a88bbce52a695e46e"},
|
||||
{file = "sqlalchemy-2.0.44-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:329aa42d1be9929603f406186630135be1e7a42569540577ba2c69952b7cf399"},
|
||||
{file = "sqlalchemy-2.0.44-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:70e03833faca7166e6a9927fbee7c27e6ecde436774cd0b24bbcc96353bce06b"},
|
||||
{file = "sqlalchemy-2.0.44-cp311-cp311-win32.whl", hash = "sha256:253e2f29843fb303eca6b2fc645aca91fa7aa0aa70b38b6950da92d44ff267f3"},
|
||||
{file = "sqlalchemy-2.0.44-cp311-cp311-win_amd64.whl", hash = "sha256:7a8694107eb4308a13b425ca8c0e67112f8134c846b6e1f722698708741215d5"},
|
||||
{file = "sqlalchemy-2.0.44-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72fea91746b5890f9e5e0997f16cbf3d53550580d76355ba2d998311b17b2250"},
|
||||
{file = "sqlalchemy-2.0.44-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:585c0c852a891450edbb1eaca8648408a3cc125f18cf433941fa6babcc359e29"},
|
||||
{file = "sqlalchemy-2.0.44-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b94843a102efa9ac68a7a30cd46df3ff1ed9c658100d30a725d10d9c60a2f44"},
|
||||
{file = "sqlalchemy-2.0.44-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:119dc41e7a7defcefc57189cfa0e61b1bf9c228211aba432b53fb71ef367fda1"},
|
||||
{file = "sqlalchemy-2.0.44-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0765e318ee9179b3718c4fd7ba35c434f4dd20332fbc6857a5e8df17719c24d7"},
|
||||
{file = "sqlalchemy-2.0.44-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2e7b5b079055e02d06a4308d0481658e4f06bc7ef211567edc8f7d5dce52018d"},
|
||||
{file = "sqlalchemy-2.0.44-cp312-cp312-win32.whl", hash = "sha256:846541e58b9a81cce7dee8329f352c318de25aa2f2bbe1e31587eb1f057448b4"},
|
||||
{file = "sqlalchemy-2.0.44-cp312-cp312-win_amd64.whl", hash = "sha256:7cbcb47fd66ab294703e1644f78971f6f2f1126424d2b300678f419aa73c7b6e"},
|
||||
{file = "sqlalchemy-2.0.44-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ff486e183d151e51b1d694c7aa1695747599bb00b9f5f604092b54b74c64a8e1"},
|
||||
{file = "sqlalchemy-2.0.44-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b1af8392eb27b372ddb783b317dea0f650241cea5bd29199b22235299ca2e45"},
|
||||
{file = "sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b61188657e3a2b9ac4e8f04d6cf8e51046e28175f79464c67f2fd35bceb0976"},
|
||||
{file = "sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b87e7b91a5d5973dda5f00cd61ef72ad75a1db73a386b62877d4875a8840959c"},
|
||||
{file = "sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:15f3326f7f0b2bfe406ee562e17f43f36e16167af99c4c0df61db668de20002d"},
|
||||
{file = "sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e77faf6ff919aa8cd63f1c4e561cac1d9a454a191bb864d5dd5e545935e5a40"},
|
||||
{file = "sqlalchemy-2.0.44-cp313-cp313-win32.whl", hash = "sha256:ee51625c2d51f8baadf2829fae817ad0b66b140573939dd69284d2ba3553ae73"},
|
||||
{file = "sqlalchemy-2.0.44-cp313-cp313-win_amd64.whl", hash = "sha256:c1c80faaee1a6c3428cecf40d16a2365bcf56c424c92c2b6f0f9ad204b899e9e"},
|
||||
{file = "sqlalchemy-2.0.44-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2fc44e5965ea46909a416fff0af48a219faefd5773ab79e5f8a5fcd5d62b2667"},
|
||||
{file = "sqlalchemy-2.0.44-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dc8b3850d2a601ca2320d081874033684e246d28e1c5e89db0864077cfc8f5a9"},
|
||||
{file = "sqlalchemy-2.0.44-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d733dec0614bb8f4bcb7c8af88172b974f685a31dc3a65cca0527e3120de5606"},
|
||||
{file = "sqlalchemy-2.0.44-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22be14009339b8bc16d6b9dc8780bacaba3402aa7581658e246114abbd2236e3"},
|
||||
{file = "sqlalchemy-2.0.44-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:357bade0e46064f88f2c3a99808233e67b0051cdddf82992379559322dfeb183"},
|
||||
{file = "sqlalchemy-2.0.44-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4848395d932e93c1595e59a8672aa7400e8922c39bb9b0668ed99ac6fa867822"},
|
||||
{file = "sqlalchemy-2.0.44-cp38-cp38-win32.whl", hash = "sha256:2f19644f27c76f07e10603580a47278abb2a70311136a7f8fd27dc2e096b9013"},
|
||||
{file = "sqlalchemy-2.0.44-cp38-cp38-win_amd64.whl", hash = "sha256:1df4763760d1de0dfc8192cc96d8aa293eb1a44f8f7a5fbe74caf1b551905c5e"},
|
||||
{file = "sqlalchemy-2.0.44-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f7027414f2b88992877573ab780c19ecb54d3a536bef3397933573d6b5068be4"},
|
||||
{file = "sqlalchemy-2.0.44-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3fe166c7d00912e8c10d3a9a0ce105569a31a3d0db1a6e82c4e0f4bf16d5eca9"},
|
||||
{file = "sqlalchemy-2.0.44-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3caef1ff89b1caefc28f0368b3bde21a7e3e630c2eddac16abd9e47bd27cc36a"},
|
||||
{file = "sqlalchemy-2.0.44-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc2856d24afa44295735e72f3c75d6ee7fdd4336d8d3a8f3d44de7aa6b766df2"},
|
||||
{file = "sqlalchemy-2.0.44-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:11bac86b0deada30b6b5f93382712ff0e911fe8d31cb9bf46e6b149ae175eff0"},
|
||||
{file = "sqlalchemy-2.0.44-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4d18cd0e9a0f37c9f4088e50e3839fcb69a380a0ec957408e0b57cff08ee0a26"},
|
||||
{file = "sqlalchemy-2.0.44-cp39-cp39-win32.whl", hash = "sha256:9e9018544ab07614d591a26c1bd4293ddf40752cc435caf69196740516af7100"},
|
||||
{file = "sqlalchemy-2.0.44-cp39-cp39-win_amd64.whl", hash = "sha256:8e0e4e66fd80f277a8c3de016a81a554e76ccf6b8d881ee0b53200305a8433f6"},
|
||||
{file = "sqlalchemy-2.0.44-py3-none-any.whl", hash = "sha256:19de7ca1246fbef9f9d1bff8f1ab25641569df226364a0e40457dc5457c54b05"},
|
||||
{file = "sqlalchemy-2.0.44.tar.gz", hash = "sha256:0ae7454e1ab1d780aee69fd2aae7d6b8670a581d8847f2d1e0f7ddfbf47e5a22"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
greenlet = {version = ">=1", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""}
|
||||
typing-extensions = ">=4.6.0"
|
||||
|
||||
[package.extras]
|
||||
aiomysql = ["aiomysql (>=0.2.0)", "greenlet (>=1)"]
|
||||
aioodbc = ["aioodbc", "greenlet (>=1)"]
|
||||
aiosqlite = ["aiosqlite", "greenlet (>=1)", "typing_extensions (!=3.10.0.1)"]
|
||||
asyncio = ["greenlet (>=1)"]
|
||||
asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (>=1)"]
|
||||
mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5,!=1.1.10)"]
|
||||
mssql = ["pyodbc"]
|
||||
mssql-pymssql = ["pymssql"]
|
||||
mssql-pyodbc = ["pyodbc"]
|
||||
mypy = ["mypy (>=0.910)"]
|
||||
mysql = ["mysqlclient (>=1.4.0)"]
|
||||
mysql-connector = ["mysql-connector-python"]
|
||||
oracle = ["cx_oracle (>=8)"]
|
||||
oracle-oracledb = ["oracledb (>=1.0.1)"]
|
||||
postgresql = ["psycopg2 (>=2.7)"]
|
||||
postgresql-asyncpg = ["asyncpg", "greenlet (>=1)"]
|
||||
postgresql-pg8000 = ["pg8000 (>=1.29.1)"]
|
||||
postgresql-psycopg = ["psycopg (>=3.0.7)"]
|
||||
postgresql-psycopg2binary = ["psycopg2-binary"]
|
||||
postgresql-psycopg2cffi = ["psycopg2cffi"]
|
||||
postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"]
|
||||
pymysql = ["pymysql"]
|
||||
sqlcipher = ["sqlcipher3_binary"]
|
||||
|
||||
[[package]]
|
||||
name = "sqlmodel"
|
||||
version = "0.0.27"
|
||||
description = "SQLModel, SQL databases in Python, designed for simplicity, compatibility, and robustness."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "sqlmodel-0.0.27-py3-none-any.whl", hash = "sha256:667fe10aa8ff5438134668228dc7d7a08306f4c5c4c7e6ad3ad68defa0e7aa49"},
|
||||
{file = "sqlmodel-0.0.27.tar.gz", hash = "sha256:ad1227f2014a03905aef32e21428640848ac09ff793047744a73dfdd077ff620"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pydantic = ">=1.10.13,<3.0.0"
|
||||
SQLAlchemy = ">=2.0.14,<2.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "stack-data"
|
||||
version = "0.6.3"
|
||||
@ -1075,6 +1547,67 @@ pure-eval = "*"
|
||||
[package.extras]
|
||||
tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"]
|
||||
|
||||
[[package]]
|
||||
name = "ta-lib"
|
||||
version = "0.6.7"
|
||||
description = "Python wrapper for TA-Lib"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "ta_lib-0.6.7-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:98ca46298595196976d685e3628d28b866bece556c1a11cc1a950260931748f2"},
|
||||
{file = "ta_lib-0.6.7-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:79e0d15db755d4c4c34216945467e2c0c0a0768bb6cd5ced09c331deb54c7702"},
|
||||
{file = "ta_lib-0.6.7-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:35ba7ce42cc5dcdd0ad9dee6d4b9fd9025538ffd00865b1be81d5b56e44da377"},
|
||||
{file = "ta_lib-0.6.7-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a04fdc1bc77093098557d2af8ba7ac6cf9fdbe33c9224471f8e0c066c9e6dfa0"},
|
||||
{file = "ta_lib-0.6.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:79efa70190cee4cb3440a57ce8e7897fb0939890ee35f1564f01ea313319ddba"},
|
||||
{file = "ta_lib-0.6.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c6b86adaeb801248f4043fce58c560655179969c9133c16763687fb28c2a3bdc"},
|
||||
{file = "ta_lib-0.6.7-cp310-cp310-win32.whl", hash = "sha256:3123ffe82a5677b79085b2206b4ee370b13c6ba7594818277734326ecace21ee"},
|
||||
{file = "ta_lib-0.6.7-cp310-cp310-win_amd64.whl", hash = "sha256:39c60c48a5fb62c890f1cb3faa165dfe66e0e9c7e49bb3b43b3c7843868f1fb4"},
|
||||
{file = "ta_lib-0.6.7-cp310-cp310-win_arm64.whl", hash = "sha256:250b9047dc52c6d9fa812aebf86495537dcf4f24d84b6bd8fbae32c7ee2f110e"},
|
||||
{file = "ta_lib-0.6.7-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:f3f5fda59669203d2f890606eb0a146bc4682ece9bfd77dacf5bc749b91c38d6"},
|
||||
{file = "ta_lib-0.6.7-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:293c92b4b5b58174c7689b7349c8be104886ee34245e8354016ba6849cf628c1"},
|
||||
{file = "ta_lib-0.6.7-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:ea4ea820dad59415a5cab2ab86b3529ea1ca230230fb3d4036f2273c4345140f"},
|
||||
{file = "ta_lib-0.6.7-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:348c615e8c5fc9461cb241616dc7953977fef7004f2cf1d5ff009e43edf39759"},
|
||||
{file = "ta_lib-0.6.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1118a0957c8b3d26dd4a0d49c49c7fda61794aa04b39b652d258b65a0f3c0869"},
|
||||
{file = "ta_lib-0.6.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:028defb19a9042e2876fd269aee489b300029cc5c58a1cb5a21f31d7495ad728"},
|
||||
{file = "ta_lib-0.6.7-cp311-cp311-win32.whl", hash = "sha256:ef3bb9ba7ed8e8f112a7c60e386a41b260c77c126613a6a01d393681c32a60dc"},
|
||||
{file = "ta_lib-0.6.7-cp311-cp311-win_amd64.whl", hash = "sha256:681517c82d4803bf5940524d2e046bbc55b86a82c449f7c70b1bfce93cc5828e"},
|
||||
{file = "ta_lib-0.6.7-cp311-cp311-win_arm64.whl", hash = "sha256:46214874dfbee6f3b071737df1ddf0f7ccfbb7764cf612a2f3d74942d308cc64"},
|
||||
{file = "ta_lib-0.6.7-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:4c28957ab9e6f26e4ffc8c9354dc2d3f920a16b3516ff010888cded8fe258b14"},
|
||||
{file = "ta_lib-0.6.7-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:fb49c2388238bd176c7e4589979ab3d58f9876ca144cd6551273c19d9e6aa1eb"},
|
||||
{file = "ta_lib-0.6.7-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d9d2352c4b12bb221d599b1730fca5748b9259ff29aa19c4b1a368d87c54656d"},
|
||||
{file = "ta_lib-0.6.7-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:a07967a28066a12b8fb94c97fc3bd3c563f04432f2383703763f53bc94aba92b"},
|
||||
{file = "ta_lib-0.6.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1843491cb19de376366e490c9ea60d43408162ddd9675c006cec03eeca32063d"},
|
||||
{file = "ta_lib-0.6.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:76e5b89bc9be6f1721a65384c8c33bb384a17f29ec22490dd57db49ed185cd5b"},
|
||||
{file = "ta_lib-0.6.7-cp312-cp312-win32.whl", hash = "sha256:411b4d05e976c9de4993c3b578aaadec353478287e886a0b72db881e0abba43d"},
|
||||
{file = "ta_lib-0.6.7-cp312-cp312-win_amd64.whl", hash = "sha256:1cb66ca90d954a5bf3dbfad2679ba14e83f7d2ee72feeb51c1187efb88ef282c"},
|
||||
{file = "ta_lib-0.6.7-cp312-cp312-win_arm64.whl", hash = "sha256:0ff9490faaaba625ffc2cfd520764067cd6255e663dd8d020cbcaa33870c8a56"},
|
||||
{file = "ta_lib-0.6.7-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:dfe4a7fb1c6a6452e7704b0baa3633c03241d616515d07365bc9114c5b85be87"},
|
||||
{file = "ta_lib-0.6.7-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:d3fd2175bdfeb5822b7c8364a0fcdc8eb4c07af9593f84480a10a4cf827d9266"},
|
||||
{file = "ta_lib-0.6.7-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:167a4b75a8c723d0b995269e67270d9cdfa22bed59957f0386cea0fe82b41c33"},
|
||||
{file = "ta_lib-0.6.7-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:8640e9e12a4bcddf5f7e3460430f259fea16c27eeab13481ff16a6964db1e58b"},
|
||||
{file = "ta_lib-0.6.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f69f22a561f10457b86e09bb8700c36eeafad1944010c7cc894bba8be827cd9"},
|
||||
{file = "ta_lib-0.6.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ef03e89b39f0e5d8ab74a9d2a2537ce202bcf622ed190d8d1c0077cf9cb7c11b"},
|
||||
{file = "ta_lib-0.6.7-cp313-cp313-win32.whl", hash = "sha256:3af4953d128676488a58df42b61bcaace669f83110f54d2dd2e866105093f597"},
|
||||
{file = "ta_lib-0.6.7-cp313-cp313-win_amd64.whl", hash = "sha256:213423db3afddc9541ab54e8841bcf5f6a6b5a41625d70bd1544e58952a8fe2c"},
|
||||
{file = "ta_lib-0.6.7-cp313-cp313-win_arm64.whl", hash = "sha256:ffc54a335c0e7ea69a4217ab8e93cada8871e591badb95fd7f2a0430eaa59825"},
|
||||
{file = "ta_lib-0.6.7-cp39-cp39-macosx_13_0_x86_64.whl", hash = "sha256:0bbb20f2653397c28fddafbd23b5ee98b64a3c60e7d11fe67eae7435f464896c"},
|
||||
{file = "ta_lib-0.6.7-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:e6ddceae8193ca4337cf70698fd2f58b341d073f0c5a9d49c1ba5d289dc1d6c9"},
|
||||
{file = "ta_lib-0.6.7-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:07d20576d12dd75f9823212d8aa297bdb50f1c88749ccc4d3b44a771ee74fb99"},
|
||||
{file = "ta_lib-0.6.7-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:c7d1ba210ac9f8f211b3cace3c8e2fe6d65a8ff729af33133efac8910f239a44"},
|
||||
{file = "ta_lib-0.6.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:79e8d8d1e9d7fdf0ce784c7a11dfef4dc1c5010f82bb3b5bf97c36eb399eefa8"},
|
||||
{file = "ta_lib-0.6.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0bb487f39c333e93226a796eec8c8fbfdea1275bb8415790601967aa3280fe04"},
|
||||
{file = "ta_lib-0.6.7-cp39-cp39-win32.whl", hash = "sha256:3c56e43aede6f8514045102d3d0bae52d8752aaac559c34fee5c447fd1cbf90a"},
|
||||
{file = "ta_lib-0.6.7-cp39-cp39-win_amd64.whl", hash = "sha256:f6e62d9e2d2d7b3825030a94eb82741299825d6d90016b2fc01c96610a8853f9"},
|
||||
{file = "ta_lib-0.6.7-cp39-cp39-win_arm64.whl", hash = "sha256:da66c7c6feda7bdc9ece5ecb518d872339f764d890d1c6e71b98bf4ad37deb07"},
|
||||
{file = "ta_lib-0.6.7.tar.gz", hash = "sha256:d702cbb20ab34d6d6c46bef87a1bd4bfeae0cc0ab65cbdf1d23dd392ce110d69"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
build = "*"
|
||||
cython = "*"
|
||||
numpy = "*"
|
||||
|
||||
[[package]]
|
||||
name = "toolz"
|
||||
version = "1.0.0"
|
||||
@ -1133,6 +1666,21 @@ files = [
|
||||
{file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-inspection"
|
||||
version = "0.4.2"
|
||||
description = "Runtime typing introspection tools"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7"},
|
||||
{file = "typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
typing-extensions = ">=4.12.0"
|
||||
|
||||
[[package]]
|
||||
name = "tzdata"
|
||||
version = "2025.2"
|
||||
@ -1287,4 +1835,4 @@ repair = ["scipy (>=1.6.3)"]
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = ">=3.13"
|
||||
content-hash = "3519edd3b81c820008526606d507cec7cac4734a4ff83eb9c8c46ffb3b21ac56"
|
||||
content-hash = "c9a5936e446cf6234585a330d4fc3888dd4a8c8db505bbc9e78d454cfe864569"
|
||||
|
79
populate.py
Executable file
79
populate.py
Executable file
@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from sys import exit
|
||||
from dotenv import load_dotenv
|
||||
from typing import NoReturn
|
||||
from paperone.utils import (
|
||||
get_last_n_trading_days,
|
||||
)
|
||||
from paperone.database import TradingDataCRUD
|
||||
from paperone.indicators import IndicatorService
|
||||
from paperone.client import Fetcher
|
||||
from rich.progress import track
|
||||
from datetime import datetime
|
||||
|
||||
load_dotenv()
|
||||
|
||||
DB_FILE = "trading_data.db"
|
||||
|
||||
|
||||
def main() -> NoReturn:
|
||||
fetcher = Fetcher()
|
||||
crud = TradingDataCRUD(f"sqlite:///{DB_FILE}")
|
||||
ind = IndicatorService(crud)
|
||||
date = datetime.now()
|
||||
days_range = 360 * 10
|
||||
|
||||
tickers = [
|
||||
"AAPL",
|
||||
"ARM",
|
||||
"AMD",
|
||||
"GOOG",
|
||||
"META",
|
||||
"MNDY",
|
||||
"MSFT",
|
||||
"NVDA",
|
||||
]
|
||||
|
||||
reference_tickers = [
|
||||
"VOO", # US
|
||||
"VT", # global
|
||||
"GLD", # gold
|
||||
"^VIX", # volatility
|
||||
"VNQ", # real estate
|
||||
"DBC", # commodities
|
||||
"VGK", # EU
|
||||
"VPL", # Asia
|
||||
"VWO", # Emerging
|
||||
"BND", # Global bonds
|
||||
]
|
||||
|
||||
for ticker in tickers + reference_tickers:
|
||||
trading_days = get_last_n_trading_days(date, days_range)
|
||||
start_date = trading_days[0]
|
||||
end_date = trading_days[-1]
|
||||
|
||||
all_ohlcv_data = fetcher.ticker_data_for(ticker, start_date, end_date)
|
||||
|
||||
ohlcv_dates = []
|
||||
for ohlcv in track(all_ohlcv_data, description=f"→ {ticker} OHLCV"):
|
||||
existing = crud.get_ohlcv(ticker, ohlcv.date)
|
||||
|
||||
if not existing:
|
||||
crud.create_ohlcv(ohlcv)
|
||||
|
||||
ohlcv_dates.append(ohlcv.date)
|
||||
|
||||
for calc_date in track(ohlcv_dates, description=f"→ {ticker} Indicators"):
|
||||
existing_indicator = crud.get_indicators(ticker, calc_date)
|
||||
|
||||
if existing_indicator:
|
||||
continue
|
||||
|
||||
ind.calculate_and_save_indicators(ticker=ticker, target_date=calc_date)
|
||||
|
||||
exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -14,7 +14,9 @@ dependencies = [
|
||||
"typer (>=0.19.2,<0.20.0)",
|
||||
"yfinance (>=0.2.66,<0.3.0)",
|
||||
"ipython (>=9.6.0,<10.0.0)",
|
||||
"pandas-market-calendars (>=5.1.1,<6.0.0)"
|
||||
"pandas-market-calendars (>=5.1.1,<6.0.0)",
|
||||
"sqlmodel (>=0.0.27,<0.0.28)",
|
||||
"ta-lib (>=0.6.7,<0.7.0)"
|
||||
]
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user