bullishpy 0.13.0__py3-none-any.whl → 0.14.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of bullishpy might be problematic. Click here for more details.
- bullish/analysis/analysis.py +3 -0
- bullish/analysis/constants.py +403 -0
- bullish/analysis/filter.py +2 -405
- bullish/analysis/functions.py +10 -19
- bullish/analysis/indicators.py +16 -8
- bullish/analysis/industry_views.py +201 -0
- bullish/analysis/predefined_filters.py +81 -248
- bullish/app/app.py +5 -1
- bullish/database/alembic/versions/040b15fba458_.py +61 -0
- bullish/database/alembic/versions/5b10ee7604c1_.py +44 -0
- bullish/database/alembic/versions/ec25c8fa449f_.py +63 -0
- bullish/database/crud.py +72 -4
- bullish/database/schemas.py +17 -0
- bullish/figures/figures.py +28 -5
- bullish/interface/interface.py +29 -0
- {bullishpy-0.13.0.dist-info → bullishpy-0.14.0.dist-info}/METADATA +1 -1
- {bullishpy-0.13.0.dist-info → bullishpy-0.14.0.dist-info}/RECORD +19 -14
- {bullishpy-0.13.0.dist-info → bullishpy-0.14.0.dist-info}/WHEEL +0 -0
- {bullishpy-0.13.0.dist-info → bullishpy-0.14.0.dist-info}/entry_points.txt +0 -0
bullish/database/crud.py
CHANGED
|
@@ -3,19 +3,27 @@ import logging
|
|
|
3
3
|
from datetime import date
|
|
4
4
|
from functools import cached_property
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from sqlite3 import OperationalError
|
|
7
6
|
from typing import TYPE_CHECKING, Any, List, Optional
|
|
8
7
|
|
|
9
8
|
import pandas as pd
|
|
10
9
|
from bearish.database.crud import BearishDb # type: ignore
|
|
11
10
|
from bearish.models.base import Ticker # type: ignore
|
|
12
|
-
from bearish.database.schemas import EarningsDateORM # type: ignore
|
|
11
|
+
from bearish.database.schemas import EarningsDateORM, EquityORM # type: ignore
|
|
12
|
+
from bearish.types import Sources # type: ignore
|
|
13
13
|
from pydantic import ConfigDict
|
|
14
14
|
from sqlalchemy import Engine, create_engine, insert, delete, update
|
|
15
15
|
from sqlmodel import Session, select
|
|
16
16
|
|
|
17
17
|
from bullish.analysis.analysis import Analysis
|
|
18
|
-
from bullish.
|
|
18
|
+
from bullish.analysis.constants import Industry, IndustryGroup, Sector, Country
|
|
19
|
+
from bullish.analysis.industry_views import Type, IndustryView
|
|
20
|
+
|
|
21
|
+
from bullish.database.schemas import (
|
|
22
|
+
AnalysisORM,
|
|
23
|
+
JobTrackerORM,
|
|
24
|
+
FilteredResultsORM,
|
|
25
|
+
IndustryViewORM,
|
|
26
|
+
)
|
|
19
27
|
from bullish.database.scripts.upgrade import upgrade
|
|
20
28
|
from bullish.exceptions import DatabaseFileNotFoundError
|
|
21
29
|
from bullish.analysis.filter import FilteredResults
|
|
@@ -45,7 +53,7 @@ class BullishDb(BearishDb, BullishDbBase): # type: ignore
|
|
|
45
53
|
database_url = f"sqlite:///{Path(self.database_path)}"
|
|
46
54
|
try:
|
|
47
55
|
upgrade(self.database_path)
|
|
48
|
-
except
|
|
56
|
+
except Exception as e:
|
|
49
57
|
logger.warning(
|
|
50
58
|
f"Failed to upgrade the database at {self.database_path}. "
|
|
51
59
|
f"Reason: {e}"
|
|
@@ -193,3 +201,63 @@ class BullishDb(BearishDb, BullishDbBase): # type: ignore
|
|
|
193
201
|
select(EarningsDateORM.date).where(EarningsDateORM.symbol == symbol)
|
|
194
202
|
)
|
|
195
203
|
]
|
|
204
|
+
|
|
205
|
+
def read_industry_symbols(
|
|
206
|
+
self, industries: List[Industry], country: Country, source: Sources = "Yfinance"
|
|
207
|
+
) -> List[str]:
|
|
208
|
+
with Session(self._engine) as session:
|
|
209
|
+
stmt = select(EquityORM.symbol).where(
|
|
210
|
+
EquityORM.industry.in_(industries),
|
|
211
|
+
EquityORM.source == source,
|
|
212
|
+
EquityORM.country == country,
|
|
213
|
+
)
|
|
214
|
+
result = session.exec(stmt).all()
|
|
215
|
+
return list(result)
|
|
216
|
+
|
|
217
|
+
def read_industry_group_symbols(
|
|
218
|
+
self,
|
|
219
|
+
industry_groups: List[IndustryGroup],
|
|
220
|
+
country: Country,
|
|
221
|
+
source: Sources = "Yfinance",
|
|
222
|
+
) -> List[str]:
|
|
223
|
+
with Session(self._engine) as session:
|
|
224
|
+
stmt = select(EquityORM.symbol).where(
|
|
225
|
+
EquityORM.industry_group.in_(industry_groups),
|
|
226
|
+
EquityORM.source == source,
|
|
227
|
+
EquityORM.country == country,
|
|
228
|
+
)
|
|
229
|
+
result = session.exec(stmt).all()
|
|
230
|
+
return list(result)
|
|
231
|
+
|
|
232
|
+
def read_sector_symbols(
|
|
233
|
+
self, sectors: List[Sector], country: Country, source: Sources = "Yfinance"
|
|
234
|
+
) -> List[str]:
|
|
235
|
+
with Session(self._engine) as session:
|
|
236
|
+
stmt = select(EquityORM.symbol).where(
|
|
237
|
+
EquityORM.sector.in_(sectors),
|
|
238
|
+
EquityORM.source == source,
|
|
239
|
+
EquityORM.country == country,
|
|
240
|
+
)
|
|
241
|
+
result = session.exec(stmt).all()
|
|
242
|
+
return list(result)
|
|
243
|
+
|
|
244
|
+
def write_returns(self, industry_returns: List[IndustryView]) -> None:
|
|
245
|
+
with Session(self._engine) as session:
|
|
246
|
+
stmt = (
|
|
247
|
+
insert(IndustryViewORM)
|
|
248
|
+
.prefix_with("OR REPLACE")
|
|
249
|
+
.values([a.model_dump() for a in industry_returns])
|
|
250
|
+
)
|
|
251
|
+
session.exec(stmt) # type: ignore
|
|
252
|
+
session.commit()
|
|
253
|
+
|
|
254
|
+
def read_returns(
|
|
255
|
+
self, type: Type, industry: Industry, country: Country
|
|
256
|
+
) -> List[IndustryView]:
|
|
257
|
+
with Session(self._engine) as session:
|
|
258
|
+
stmt = select(IndustryViewORM).where(
|
|
259
|
+
IndustryViewORM.industry == industry,
|
|
260
|
+
IndustryViewORM.country == country,
|
|
261
|
+
)
|
|
262
|
+
result = session.exec(stmt).all()
|
|
263
|
+
return [IndustryView.model_validate(r) for r in result]
|
bullish/database/schemas.py
CHANGED
|
@@ -4,6 +4,8 @@ from sqlmodel import Field, SQLModel
|
|
|
4
4
|
from sqlalchemy import Column, JSON
|
|
5
5
|
from bullish.analysis.analysis import Analysis
|
|
6
6
|
from bullish.analysis.filter import FilteredResults
|
|
7
|
+
from bullish.analysis.industry_views import IndustryView
|
|
8
|
+
|
|
7
9
|
from bullish.jobs.models import JobTracker
|
|
8
10
|
from sqlalchemy import Index
|
|
9
11
|
|
|
@@ -44,3 +46,18 @@ class FilteredResultsORM(SQLModel, FilteredResults, table=True):
|
|
|
44
46
|
name: str = Field(primary_key=True)
|
|
45
47
|
symbols: list[str] = Field(sa_column=Column(JSON))
|
|
46
48
|
filter_query: Dict[str, Any] = Field(sa_column=Column(JSON)) # type: ignore
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class IndustryViewORM(SQLModel, IndustryView, table=True):
|
|
52
|
+
__tablename__ = "industryview"
|
|
53
|
+
__table_args__ = {"extend_existing": True} # noqa:RUF012
|
|
54
|
+
date: str = Field(primary_key=True) # type: ignore
|
|
55
|
+
created_at: str = Field(default=None, nullable=True) # type: ignore
|
|
56
|
+
simple_return: float | None = Field(default=None, nullable=True) # type: ignore
|
|
57
|
+
log_return: float | None = Field(default=None, nullable=True) # type: ignore
|
|
58
|
+
normalized_close: float | None = Field(default=None, nullable=True) # type: ignore
|
|
59
|
+
country: str = Field(primary_key=True) # type: ignore
|
|
60
|
+
industry: str = Field(primary_key=True) # type: ignore
|
|
61
|
+
industry_group: str | None = Field(default=None, nullable=True) # type: ignore
|
|
62
|
+
sector: str | None = Field(default=None, nullable=True) # type: ignore
|
|
63
|
+
type: str = Field(primary_key=True) # type: ignore
|
bullish/figures/figures.py
CHANGED
|
@@ -14,6 +14,7 @@ def plot(
|
|
|
14
14
|
symbol: str,
|
|
15
15
|
name: Optional[str] = None,
|
|
16
16
|
dates: Optional[List[date]] = None,
|
|
17
|
+
industry_data: Optional[pd.DataFrame] = None,
|
|
17
18
|
) -> go.Figure:
|
|
18
19
|
data = add_indicators(data)
|
|
19
20
|
fig = make_subplots(
|
|
@@ -122,11 +123,33 @@ def plot(
|
|
|
122
123
|
row=6,
|
|
123
124
|
col=1,
|
|
124
125
|
)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
126
|
+
if (
|
|
127
|
+
industry_data is not None
|
|
128
|
+
and not industry_data.empty
|
|
129
|
+
and "symbol" in industry_data.columns
|
|
130
|
+
and "industry" in industry_data.columns
|
|
131
|
+
):
|
|
132
|
+
fig.add_trace(
|
|
133
|
+
go.Scatter(
|
|
134
|
+
x=industry_data.index,
|
|
135
|
+
y=industry_data.symbol,
|
|
136
|
+
name="Symbol",
|
|
137
|
+
mode="lines",
|
|
138
|
+
),
|
|
139
|
+
row=7,
|
|
140
|
+
col=1,
|
|
141
|
+
)
|
|
142
|
+
fig.add_trace(
|
|
143
|
+
go.Scatter(
|
|
144
|
+
x=industry_data.index,
|
|
145
|
+
y=industry_data.industry,
|
|
146
|
+
name="Industry",
|
|
147
|
+
mode="lines",
|
|
148
|
+
opacity=0.5,
|
|
149
|
+
),
|
|
150
|
+
row=7,
|
|
151
|
+
col=1,
|
|
152
|
+
)
|
|
130
153
|
if dates is not None and dates:
|
|
131
154
|
for date in dates:
|
|
132
155
|
if (
|
bullish/interface/interface.py
CHANGED
|
@@ -6,10 +6,13 @@ from typing import List, Optional
|
|
|
6
6
|
import pandas as pd
|
|
7
7
|
from bearish.interface.interface import BearishDbBase # type: ignore
|
|
8
8
|
from bearish.models.base import Ticker # type: ignore
|
|
9
|
+
from bearish.types import Sources # type: ignore
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
from bullish.analysis.analysis import Analysis, AnalysisView
|
|
13
|
+
from bullish.analysis.constants import Industry, Sector, IndustryGroup, Country
|
|
12
14
|
from bullish.analysis.filter import FilterQuery, FilteredResults
|
|
15
|
+
from bullish.analysis.industry_views import Type, IndustryView
|
|
13
16
|
from bullish.jobs.models import JobTracker, JobTrackerStatus, add_icons
|
|
14
17
|
|
|
15
18
|
logger = logging.getLogger(__name__)
|
|
@@ -96,3 +99,29 @@ class BullishDbBase(BearishDbBase): # type: ignore
|
|
|
96
99
|
|
|
97
100
|
@abc.abstractmethod
|
|
98
101
|
def read_dates(self, symbol: str) -> List[date]: ...
|
|
102
|
+
|
|
103
|
+
@abc.abstractmethod
|
|
104
|
+
def read_industry_symbols(
|
|
105
|
+
self, industries: List[Industry], country: Country, source: Sources = "Yfinance"
|
|
106
|
+
) -> List[str]: ...
|
|
107
|
+
|
|
108
|
+
@abc.abstractmethod
|
|
109
|
+
def read_industry_group_symbols(
|
|
110
|
+
self,
|
|
111
|
+
industry_groups: List[IndustryGroup],
|
|
112
|
+
country: Country,
|
|
113
|
+
source: Sources = "Yfinance",
|
|
114
|
+
) -> List[str]: ...
|
|
115
|
+
|
|
116
|
+
@abc.abstractmethod
|
|
117
|
+
def read_sector_symbols(
|
|
118
|
+
self, sectors: List[Sector], country: Country, source: Sources = "Yfinance"
|
|
119
|
+
) -> List[str]: ...
|
|
120
|
+
|
|
121
|
+
@abc.abstractmethod
|
|
122
|
+
def write_returns(self, industry_returns: List[IndustryView]) -> None: ...
|
|
123
|
+
|
|
124
|
+
@abc.abstractmethod
|
|
125
|
+
def read_returns(
|
|
126
|
+
self, type: Type, industry: Industry, country: Country
|
|
127
|
+
) -> List[IndustryView]: ...
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
bullish/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
bullish/analysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
bullish/analysis/analysis.py,sha256=
|
|
4
|
-
bullish/analysis/
|
|
5
|
-
bullish/analysis/
|
|
6
|
-
bullish/analysis/
|
|
7
|
-
bullish/analysis/
|
|
3
|
+
bullish/analysis/analysis.py,sha256=ZTRf7YaEnFL9HBVkamY7JXys3XJtkLGNMkU4FbkV_04,19270
|
|
4
|
+
bullish/analysis/constants.py,sha256=tVDPQEufH8lytMj4DdUdvXt79b7cvWaDwSUOpeqMWts,9851
|
|
5
|
+
bullish/analysis/filter.py,sha256=kSG6fXZrnwqE1HvKQW6O3yVNV49qhVleer9M_7BIDpg,8381
|
|
6
|
+
bullish/analysis/functions.py,sha256=A2eFBqNx5XohEhJFU_LvyU0_s0ozErtsiYUqVSb3Wvs,14367
|
|
7
|
+
bullish/analysis/indicators.py,sha256=9-768_ntZRxNgeNXj3MbRO9QCq97uYKMHQ-9hQMu7Mo,20938
|
|
8
|
+
bullish/analysis/industry_views.py,sha256=1B5V39Fm9rNQEsun1xrwELfOiKlGdTie0ZolS2UBh2w,6247
|
|
9
|
+
bullish/analysis/predefined_filters.py,sha256=28e42hGaH7Qb6SPNeH7EK9YIhjERj-qpbY-7xLahvDM,8361
|
|
8
10
|
bullish/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
bullish/app/app.py,sha256=
|
|
11
|
+
bullish/app/app.py,sha256=3xSO4x3T7BnD60M-AQM6-xkRVFLCWqGJ6DWqLmKxbzw,13663
|
|
10
12
|
bullish/cli.py,sha256=uYLZmGDAolZKWzduZ58bP-xul1adg0oKfeUQtZMXTvA,1958
|
|
11
13
|
bullish/database/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
14
|
bullish/database/alembic/README,sha256=heMzebYwlGhnE8_4CWJ4LS74WoEZjBy-S-mIJRxAEKI,39
|
|
@@ -14,35 +16,38 @@ bullish/database/alembic/alembic.ini,sha256=VuwqBJV5ObTyyRNrqv8Xr-TDIRfqPjP9R1mq
|
|
|
14
16
|
bullish/database/alembic/env.py,sha256=TBsN4TyVppyc2QpWqViebd4-xxUT7Cs3GDYXQdKiAMs,2260
|
|
15
17
|
bullish/database/alembic/script.py.mako,sha256=MEqL-2qATlST9TAOeYgscMn1uy6HUS9NFvDgl93dMj8,635
|
|
16
18
|
bullish/database/alembic/versions/037dbd721317_.py,sha256=U7EA4odH3t9w0-J4FmvBUt8HOuGDMn0rEAu_0vPUYaI,8595
|
|
19
|
+
bullish/database/alembic/versions/040b15fba458_.py,sha256=scSauB4wZe0sMFHOAMHkx-rNSF06Pn3D52QJ10PvERg,2314
|
|
17
20
|
bullish/database/alembic/versions/08ac1116e055_.py,sha256=zMEiCbraMEAZItT4ibc3evAH7-7mkXpdgnZy4tPVYeg,27263
|
|
18
21
|
bullish/database/alembic/versions/11d35a452b40_.py,sha256=j2PaU1RssLQ20OevGmBC7S9E9ocWiXpBue9SOS4AQoY,11521
|
|
19
22
|
bullish/database/alembic/versions/17e51420e7ad_.py,sha256=xeiVIm1YUZb08opE9rocHZP1__9WQWXsKsXgeFV9cvs,2960
|
|
20
23
|
bullish/database/alembic/versions/49c83f9eb5ac_.py,sha256=kCBItp7KmqpJ03roy5ikQjhefZia1oKgfZwournQDq8,3890
|
|
21
24
|
bullish/database/alembic/versions/4b0a2f40b7d3_.py,sha256=G0K7w7pOPYjPZkXTB8LWhxoxuWBPcPwOfnubTBtdeEY,1827
|
|
25
|
+
bullish/database/alembic/versions/5b10ee7604c1_.py,sha256=YlqaagPasR3RKASv7acME1jPS8p26VoTE2BvpOwdCpY,1463
|
|
22
26
|
bullish/database/alembic/versions/73564b60fe24_.py,sha256=MTlDRDNHj3E9gK7IMeAzv2UxxxYtWiu3gI_9xTLE-wg,1008
|
|
23
27
|
bullish/database/alembic/versions/b76079e9845f_.py,sha256=W8eeTABjI9tT1dp3hlK7g7tiKqDhmA8AoUX9Sw-ykLI,1165
|
|
24
28
|
bullish/database/alembic/versions/bf6b86dd5463_.py,sha256=fKB8knCprGmiL6AEyFdhybVmB7QX_W4MPFF9sPzUrSM,1094
|
|
25
29
|
bullish/database/alembic/versions/d663166c531d_.py,sha256=U92l6QXqPniAYrPeu2Bt77ReDbXveLj4aGXtgd806JY,1915
|
|
30
|
+
bullish/database/alembic/versions/ec25c8fa449f_.py,sha256=8Yts74KEjK4jg20zIo90_0atw-sOBuE3hgCKl-rfS5E,2271
|
|
26
31
|
bullish/database/alembic/versions/ee5baabb35f8_.py,sha256=nBMEY-_C8AsSXVPyaDdUkwrFFo2gxShzJhmrjejDwtc,1632
|
|
27
32
|
bullish/database/alembic/versions/fc191121f522_.py,sha256=0sstF6TpAJ09-Mt-Vek9SdSWksvi4C58a5D92rBtuY8,1894
|
|
28
|
-
bullish/database/crud.py,sha256=
|
|
29
|
-
bullish/database/schemas.py,sha256=
|
|
33
|
+
bullish/database/crud.py,sha256=ubRXV88GAo4prDQPylouEn8DBvoyNtM6hx12HPhD_2w,9889
|
|
34
|
+
bullish/database/schemas.py,sha256=gI6hWYv1C4G9xRXiNTSLxXftkgIOANDyfct2_KwSavo,2442
|
|
30
35
|
bullish/database/scripts/create_revision.py,sha256=rggIf-3koPqJNth8FIg89EOfnIM7a9QrvL8X7UJsP0g,628
|
|
31
36
|
bullish/database/scripts/stamp.py,sha256=PWgVUEBumjNUMjTnGw46qmU3p221LeN-KspnW_gFuu4,839
|
|
32
37
|
bullish/database/scripts/upgrade.py,sha256=-Gz7aFNPEt9y9e1kltqXE76-j_8QeNtet_VlwY5AWjo,806
|
|
33
38
|
bullish/database/settings.py,sha256=nMudufmF7iC_62_PHrGSMjlqDLN2I0qTbtz9JKZHSko,164
|
|
34
39
|
bullish/exceptions.py,sha256=4z_i-dD-CDz1bkGmZH9DOf1L_awlCPCgdUDPF7dhWAI,106
|
|
35
40
|
bullish/figures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
|
-
bullish/figures/figures.py,sha256=
|
|
41
|
+
bullish/figures/figures.py,sha256=imrvIIcL9L-z-3vzWK5hDEsNttZs60QxlFI-PLw0hJQ,4829
|
|
37
42
|
bullish/interface/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
-
bullish/interface/interface.py,sha256=
|
|
43
|
+
bullish/interface/interface.py,sha256=UB2ATVtUsnXetnLCrSmNVrFpIvCw_0kuVxKHZC7sT7U,4233
|
|
39
44
|
bullish/jobs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
40
45
|
bullish/jobs/app.py,sha256=5MJ5KXUo7JSNAvOPgkpIMasD11VTrjQvGzM7vmCY65E,77
|
|
41
46
|
bullish/jobs/models.py,sha256=ndrGTMP08S57yGLGEG9TQt8Uw2slc4HvbG-TZtEEuN0,744
|
|
42
47
|
bullish/jobs/tasks.py,sha256=V_b0c8_GQC0-KIxaHDlLFhtkclQJOsck0gXaW6OlC_w,3055
|
|
43
48
|
bullish/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
44
49
|
bullish/utils/checks.py,sha256=Va10_xDVVnxYkOD2hafvyQ-TFV8FQpOkr4huJ7XgpDM,2188
|
|
45
|
-
bullishpy-0.
|
|
46
|
-
bullishpy-0.
|
|
47
|
-
bullishpy-0.
|
|
48
|
-
bullishpy-0.
|
|
50
|
+
bullishpy-0.14.0.dist-info/METADATA,sha256=BH2BdzlEth8FoZFyxME0sbOlPnqqPdbHoxdHJ3X_GHg,784
|
|
51
|
+
bullishpy-0.14.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
52
|
+
bullishpy-0.14.0.dist-info/entry_points.txt,sha256=eaPpmL6vmSBFo0FBtwibCXGqAW4LFJ83whJzT1VjD-0,43
|
|
53
|
+
bullishpy-0.14.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|