bullishpy 0.13.0__py3-none-any.whl → 0.15.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.

@@ -0,0 +1,61 @@
1
+ """
2
+
3
+ Revision ID: 040b15fba458
4
+ Revises: ec25c8fa449f
5
+ Create Date: 2025-07-15 09:22:11.456381
6
+
7
+ """
8
+
9
+ from typing import Sequence, Union
10
+
11
+ from alembic import op
12
+ import sqlalchemy as sa
13
+ from sqlalchemy.dialects import sqlite
14
+ import sqlmodel
15
+
16
+ # revision identifiers, used by Alembic.
17
+ revision: str = "040b15fba458"
18
+ down_revision: Union[str, None] = "ec25c8fa449f"
19
+ branch_labels: Union[str, Sequence[str], None] = None
20
+ depends_on: Union[str, Sequence[str], None] = None
21
+
22
+
23
+ def upgrade() -> None:
24
+ # ### commands auto generated by Alembic - please adjust! ###
25
+ op.create_table(
26
+ "industryview",
27
+ sa.Column("simple_return", sa.Float(), nullable=True),
28
+ sa.Column("log_return", sa.Float(), nullable=True),
29
+ sa.Column("normalized_close", sa.Float(), nullable=True),
30
+ sa.Column("date", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
31
+ sa.Column("created_at", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
32
+ sa.Column("country", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
33
+ sa.Column("industry", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
34
+ sa.Column("industry_group", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
35
+ sa.Column("sector", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
36
+ sa.Column("type", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
37
+ sa.PrimaryKeyConstraint("date", "country", "industry", "type"),
38
+ )
39
+
40
+ op.drop_table("industryreturns")
41
+
42
+
43
+ def downgrade() -> None:
44
+ # ### commands auto generated by Alembic - please adjust! ###
45
+
46
+ op.create_table(
47
+ "industryreturns",
48
+ sa.Column("date", sa.VARCHAR(), nullable=False),
49
+ sa.Column("created_at", sa.VARCHAR(), nullable=True),
50
+ sa.Column("simple_return", sa.FLOAT(), nullable=True),
51
+ sa.Column("log_return", sa.FLOAT(), nullable=True),
52
+ sa.Column("country", sa.VARCHAR(), nullable=False),
53
+ sa.Column("industry", sa.VARCHAR(), nullable=False),
54
+ sa.Column("industry_group", sa.VARCHAR(), nullable=True),
55
+ sa.Column("sector", sa.VARCHAR(), nullable=True),
56
+ sa.Column("type", sa.VARCHAR(), nullable=False),
57
+ sa.PrimaryKeyConstraint("date", "country", "industry", "type"),
58
+ )
59
+
60
+ op.drop_table("industryview")
61
+ # ### end Alembic commands ###
@@ -0,0 +1,51 @@
1
+ """
2
+
3
+ Revision ID: 3e1a14c41916
4
+ Revises: 040b15fba458
5
+ Create Date: 2025-07-17 15:07:44.125783
6
+
7
+ """
8
+
9
+ from typing import Sequence, Union
10
+
11
+ from alembic import op
12
+ import sqlalchemy as sa
13
+ from sqlalchemy.dialects import sqlite
14
+ import sqlmodel
15
+
16
+ # revision identifiers, used by Alembic.
17
+ revision: str = "3e1a14c41916"
18
+ down_revision: Union[str, None] = "040b15fba458"
19
+ branch_labels: Union[str, Sequence[str], None] = None
20
+ depends_on: Union[str, Sequence[str], None] = None
21
+
22
+
23
+ def upgrade() -> None:
24
+ # ### commands auto generated by Alembic - please adjust! ###
25
+ op.create_table(
26
+ "signalseries",
27
+ sa.Column("date", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
28
+ sa.Column("value", sa.Float(), nullable=False),
29
+ sa.Column("name", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
30
+ sa.Column("symbol", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
31
+ sa.PrimaryKeyConstraint("date", "name", "symbol"),
32
+ )
33
+ with op.batch_alter_table("analysis", schema=None) as batch_op:
34
+ batch_op.drop_index(batch_op.f("ix_analysis_momentum_time_span"))
35
+ batch_op.drop_column("momentum_time_span")
36
+
37
+ # ### end Alembic commands ###
38
+
39
+
40
+ def downgrade() -> None:
41
+ # ### commands auto generated by Alembic - please adjust! ###
42
+ with op.batch_alter_table("analysis", schema=None) as batch_op:
43
+ batch_op.add_column(sa.Column("momentum_time_span", sa.DATE(), nullable=True))
44
+ batch_op.create_index(
45
+ batch_op.f("ix_analysis_momentum_time_span"),
46
+ ["momentum_time_span"],
47
+ unique=False,
48
+ )
49
+
50
+ op.drop_table("signalseries")
51
+ # ### end Alembic commands ###
@@ -0,0 +1,44 @@
1
+ """
2
+
3
+ Revision ID: 5b10ee7604c1
4
+ Revises: b76079e9845f
5
+ Create Date: 2025-07-13 11:59:59.351417
6
+
7
+ """
8
+
9
+ from typing import Sequence, Union
10
+
11
+ from alembic import op
12
+ import sqlalchemy as sa
13
+ from sqlalchemy.dialects import sqlite
14
+
15
+ # revision identifiers, used by Alembic.
16
+ revision: str = "5b10ee7604c1"
17
+ down_revision: Union[str, None] = "b76079e9845f"
18
+ branch_labels: Union[str, Sequence[str], None] = None
19
+ depends_on: Union[str, Sequence[str], None] = None
20
+
21
+
22
+ def upgrade() -> None:
23
+ # ### commands auto generated by Alembic - please adjust! ###
24
+
25
+ with op.batch_alter_table("analysis", schema=None) as batch_op:
26
+ batch_op.add_column(sa.Column("sma_50_above_sma_200", sa.Date(), nullable=True))
27
+ batch_op.add_column(sa.Column("price_above_sma_50", sa.Date(), nullable=True))
28
+ batch_op.create_index(
29
+ "ix_analysis_price_above_sma_50", ["price_above_sma_50"], unique=False
30
+ )
31
+ batch_op.create_index(
32
+ "ix_analysis_sma_50_above_sma_200", ["sma_50_above_sma_200"], unique=False
33
+ )
34
+
35
+ # ### end Alembic commands ###
36
+
37
+
38
+ def downgrade() -> None:
39
+ # ### commands auto generated by Alembic - please adjust! ###
40
+ with op.batch_alter_table("analysis", schema=None) as batch_op:
41
+ batch_op.drop_index("ix_analysis_sma_50_above_sma_200")
42
+ batch_op.drop_index("ix_analysis_price_above_sma_50")
43
+ batch_op.drop_column("price_above_sma_50")
44
+ batch_op.drop_column("sma_50_above_sma_200")
@@ -0,0 +1,63 @@
1
+ """
2
+
3
+ Revision ID: ec25c8fa449f
4
+ Revises: 5b10ee7604c1
5
+ Create Date: 2025-07-14 12:26:29.346903
6
+
7
+ """
8
+
9
+ from typing import Sequence, Union
10
+
11
+ from alembic import op
12
+ import sqlalchemy as sa
13
+ from sqlalchemy.dialects import sqlite
14
+ import sqlmodel
15
+
16
+ # revision identifiers, used by Alembic.
17
+ revision: str = "ec25c8fa449f"
18
+ down_revision: Union[str, None] = "5b10ee7604c1"
19
+ branch_labels: Union[str, Sequence[str], None] = None
20
+ depends_on: Union[str, Sequence[str], None] = None
21
+
22
+
23
+ def upgrade() -> None:
24
+ # ### commands auto generated by Alembic - please adjust! ###
25
+ op.create_table(
26
+ "industryreturns",
27
+ sa.Column("date", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
28
+ sa.Column("created_at", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
29
+ sa.Column("simple_return", sa.Float(), nullable=True),
30
+ sa.Column("log_return", sa.Float(), nullable=True),
31
+ sa.Column("country", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
32
+ sa.Column("industry", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
33
+ sa.Column("industry_group", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
34
+ sa.Column("sector", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
35
+ sa.Column("type", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
36
+ sa.PrimaryKeyConstraint("date", "country", "industry", "type"),
37
+ )
38
+
39
+ with op.batch_alter_table("analysis", schema=None) as batch_op:
40
+ batch_op.drop_index(
41
+ batch_op.f("ix_analysis_return_after_rsi_crossover_45_period_90")
42
+ )
43
+ batch_op.drop_column("return_after_rsi_crossover_45_period_90")
44
+
45
+ # ### end Alembic commands ###
46
+
47
+
48
+ def downgrade() -> None:
49
+ # ### commands auto generated by Alembic - please adjust! ###
50
+ with op.batch_alter_table("analysis", schema=None) as batch_op:
51
+ batch_op.add_column(
52
+ sa.Column(
53
+ "return_after_rsi_crossover_45_period_90", sa.FLOAT(), nullable=True
54
+ )
55
+ )
56
+ batch_op.create_index(
57
+ batch_op.f("ix_analysis_return_after_rsi_crossover_45_period_90"),
58
+ ["return_after_rsi_crossover_45_period_90"],
59
+ unique=False,
60
+ )
61
+
62
+ op.drop_table("industryreturns")
63
+ # ### end Alembic commands ###
bullish/database/crud.py CHANGED
@@ -3,19 +3,29 @@ 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.database.schemas import AnalysisORM, JobTrackerORM, FilteredResultsORM
18
+ from bullish.analysis.constants import Industry, IndustryGroup, Sector, Country
19
+ from bullish.analysis.indicators import SignalSeries
20
+ from bullish.analysis.industry_views import Type, IndustryView
21
+
22
+ from bullish.database.schemas import (
23
+ AnalysisORM,
24
+ JobTrackerORM,
25
+ FilteredResultsORM,
26
+ IndustryViewORM,
27
+ SignalSeriesORM,
28
+ )
19
29
  from bullish.database.scripts.upgrade import upgrade
20
30
  from bullish.exceptions import DatabaseFileNotFoundError
21
31
  from bullish.analysis.filter import FilteredResults
@@ -45,7 +55,7 @@ class BullishDb(BearishDb, BullishDbBase): # type: ignore
45
55
  database_url = f"sqlite:///{Path(self.database_path)}"
46
56
  try:
47
57
  upgrade(self.database_path)
48
- except OperationalError as e:
58
+ except Exception as e:
49
59
  logger.warning(
50
60
  f"Failed to upgrade the database at {self.database_path}. "
51
61
  f"Reason: {e}"
@@ -193,3 +203,84 @@ class BullishDb(BearishDb, BullishDbBase): # type: ignore
193
203
  select(EarningsDateORM.date).where(EarningsDateORM.symbol == symbol)
194
204
  )
195
205
  ]
206
+
207
+ def read_industry_symbols(
208
+ self, industries: List[Industry], country: Country, source: Sources = "Yfinance"
209
+ ) -> List[str]:
210
+ with Session(self._engine) as session:
211
+ stmt = select(EquityORM.symbol).where(
212
+ EquityORM.industry.in_(industries),
213
+ EquityORM.source == source,
214
+ EquityORM.country == country,
215
+ )
216
+ result = session.exec(stmt).all()
217
+ return list(result)
218
+
219
+ def read_industry_group_symbols(
220
+ self,
221
+ industry_groups: List[IndustryGroup],
222
+ country: Country,
223
+ source: Sources = "Yfinance",
224
+ ) -> List[str]:
225
+ with Session(self._engine) as session:
226
+ stmt = select(EquityORM.symbol).where(
227
+ EquityORM.industry_group.in_(industry_groups),
228
+ EquityORM.source == source,
229
+ EquityORM.country == country,
230
+ )
231
+ result = session.exec(stmt).all()
232
+ return list(result)
233
+
234
+ def read_sector_symbols(
235
+ self, sectors: List[Sector], country: Country, source: Sources = "Yfinance"
236
+ ) -> List[str]:
237
+ with Session(self._engine) as session:
238
+ stmt = select(EquityORM.symbol).where(
239
+ EquityORM.sector.in_(sectors),
240
+ EquityORM.source == source,
241
+ EquityORM.country == country,
242
+ )
243
+ result = session.exec(stmt).all()
244
+ return list(result)
245
+
246
+ def write_returns(self, industry_returns: List[IndustryView]) -> None:
247
+ with Session(self._engine) as session:
248
+ stmt = (
249
+ insert(IndustryViewORM)
250
+ .prefix_with("OR REPLACE")
251
+ .values([a.model_dump() for a in industry_returns])
252
+ )
253
+ session.exec(stmt) # type: ignore
254
+ session.commit()
255
+
256
+ def read_returns(
257
+ self, type: Type, industry: Industry, country: Country
258
+ ) -> List[IndustryView]:
259
+ with Session(self._engine) as session:
260
+ stmt = select(IndustryViewORM).where(
261
+ IndustryViewORM.industry == industry,
262
+ IndustryViewORM.country == country,
263
+ )
264
+ result = session.exec(stmt).all()
265
+ return [IndustryView.model_validate(r) for r in result]
266
+
267
+ def write_signal_series(self, signal_series: List[SignalSeries]) -> None:
268
+ with Session(self._engine) as session:
269
+ stmt = (
270
+ insert(SignalSeriesORM)
271
+ .prefix_with("OR REPLACE")
272
+ .values([a.model_dump() for a in signal_series])
273
+ )
274
+ session.exec(stmt) # type: ignore
275
+ session.commit()
276
+
277
+ def read_signal_series(
278
+ self, name: str, start_date: date, end_date: date
279
+ ) -> List[str]:
280
+ with Session(self._engine) as session:
281
+ stmt = select(SignalSeriesORM.symbol).where(
282
+ SignalSeriesORM.name == name,
283
+ SignalSeriesORM.date >= start_date, # type: ignore
284
+ SignalSeriesORM.date <= end_date, # type: ignore
285
+ )
286
+ return list(set(session.exec(stmt).all()))
@@ -4,6 +4,9 @@ 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.indicators import SignalSeries
8
+ from bullish.analysis.industry_views import IndustryView
9
+
7
10
  from bullish.jobs.models import JobTracker
8
11
  from sqlalchemy import Index
9
12
 
@@ -44,3 +47,26 @@ class FilteredResultsORM(SQLModel, FilteredResults, table=True):
44
47
  name: str = Field(primary_key=True)
45
48
  symbols: list[str] = Field(sa_column=Column(JSON))
46
49
  filter_query: Dict[str, Any] = Field(sa_column=Column(JSON)) # type: ignore
50
+
51
+
52
+ class SignalSeriesORM(SQLModel, SignalSeries, table=True):
53
+ __tablename__ = "signalseries"
54
+ __table_args__ = {"extend_existing": True} # noqa:RUF012
55
+ date: str = Field(primary_key=True) # type: ignore
56
+ name: str = Field(primary_key=True)
57
+ symbol: str = Field(primary_key=True)
58
+
59
+
60
+ class IndustryViewORM(SQLModel, IndustryView, table=True):
61
+ __tablename__ = "industryview"
62
+ __table_args__ = {"extend_existing": True} # noqa:RUF012
63
+ date: str = Field(primary_key=True) # type: ignore
64
+ created_at: str = Field(default=None, nullable=True) # type: ignore
65
+ simple_return: float | None = Field(default=None, nullable=True) # type: ignore
66
+ log_return: float | None = Field(default=None, nullable=True) # type: ignore
67
+ normalized_close: float | None = Field(default=None, nullable=True) # type: ignore
68
+ country: str = Field(primary_key=True) # type: ignore
69
+ industry: str = Field(primary_key=True) # type: ignore
70
+ industry_group: str | None = Field(default=None, nullable=True) # type: ignore
71
+ sector: str | None = Field(default=None, nullable=True) # type: ignore
72
+ type: str = Field(primary_key=True) # type: ignore
@@ -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
- fig.add_trace(
126
- go.Scatter(x=data.index, y=data.ATR, name="ATR", mode="lines"),
127
- row=7,
128
- col=1,
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 (
@@ -6,10 +6,14 @@ 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.indicators import SignalSeries
16
+ from bullish.analysis.industry_views import Type, IndustryView
13
17
  from bullish.jobs.models import JobTracker, JobTrackerStatus, add_icons
14
18
 
15
19
  logger = logging.getLogger(__name__)
@@ -96,3 +100,37 @@ class BullishDbBase(BearishDbBase): # type: ignore
96
100
 
97
101
  @abc.abstractmethod
98
102
  def read_dates(self, symbol: str) -> List[date]: ...
103
+
104
+ @abc.abstractmethod
105
+ def read_industry_symbols(
106
+ self, industries: List[Industry], country: Country, source: Sources = "Yfinance"
107
+ ) -> List[str]: ...
108
+
109
+ @abc.abstractmethod
110
+ def read_industry_group_symbols(
111
+ self,
112
+ industry_groups: List[IndustryGroup],
113
+ country: Country,
114
+ source: Sources = "Yfinance",
115
+ ) -> List[str]: ...
116
+
117
+ @abc.abstractmethod
118
+ def read_sector_symbols(
119
+ self, sectors: List[Sector], country: Country, source: Sources = "Yfinance"
120
+ ) -> List[str]: ...
121
+
122
+ @abc.abstractmethod
123
+ def write_returns(self, industry_returns: List[IndustryView]) -> None: ...
124
+
125
+ @abc.abstractmethod
126
+ def read_returns(
127
+ self, type: Type, industry: Industry, country: Country
128
+ ) -> List[IndustryView]: ...
129
+
130
+ @abc.abstractmethod
131
+ def write_signal_series(self, signal_series: List[SignalSeries]) -> None: ...
132
+
133
+ @abc.abstractmethod
134
+ def read_signal_series(
135
+ self, name: str, start_date: date, end_date: date
136
+ ) -> List[str]: ...
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bullishpy
3
- Version: 0.13.0
3
+ Version: 0.15.0
4
4
  Summary:
5
5
  Author: aan
6
6
  Author-email: andoludovic.andriamamonjy@gmail.com
@@ -12,12 +12,13 @@ Requires-Dist: click (>=7.0,<=8.1)
12
12
  Requires-Dist: huey (>=2.5.3,<3.0.0)
13
13
  Requires-Dist: joblib (>=1.5.1,<2.0.0)
14
14
  Requires-Dist: pandas-ta (>=0.3.14b0,<0.4.0)
15
- Requires-Dist: plotly (>=6.1.2,<7.0.0)
15
+ Requires-Dist: plotly (>=4.12.0,<6.0.0)
16
16
  Requires-Dist: streamlit (>=1.45.1,<2.0.0)
17
17
  Requires-Dist: streamlit-file-browser (>=3.2.22,<4.0.0)
18
18
  Requires-Dist: streamlit-pydantic (>=v0.6.1-rc.3,<0.7.0)
19
19
  Requires-Dist: ta-lib (>=0.6.4,<0.7.0)
20
20
  Requires-Dist: tickermood (>=0.4.0,<0.5.0)
21
+ Requires-Dist: vectorbt (>=0.28.0,<0.29.0)
21
22
  Description-Content-Type: text/markdown
22
23
 
23
24
  ## Bullish
@@ -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=v5MFGhQUpMwGAVE0dg2293ldRSgQmqy5CiF8MQFTibM,19165
4
- bullish/analysis/filter.py,sha256=S8TuxoTAUY0U8ARPjNHE0tSSE_ToWkfZazAgnfgswk4,18136
5
- bullish/analysis/functions.py,sha256=DYqx5ZGR-zjCwDwhDQJqRFH8LpmzvFEcfItojbIcddU,14699
6
- bullish/analysis/indicators.py,sha256=JXqXsRDn-hiXcrBqqzJ3-xxANAherwaCZy38XykjJBA,20726
7
- bullish/analysis/predefined_filters.py,sha256=MvIuGug-RWO7QtWNEtOFROf-sY8IXu444g174cE-5m0,15200
3
+ bullish/analysis/analysis.py,sha256=ag1wAfNDKWmPxJz7sd1mNhuh4CZfEleTp2_a3DIkEL4,20524
4
+ bullish/analysis/constants.py,sha256=tVDPQEufH8lytMj4DdUdvXt79b7cvWaDwSUOpeqMWts,9851
5
+ bullish/analysis/filter.py,sha256=kSG6fXZrnwqE1HvKQW6O3yVNV49qhVleer9M_7BIDpg,8381
6
+ bullish/analysis/functions.py,sha256=ebCXxYeKlWhvcRRFMbX8E63bL7OquxyoWsYIy0o0SCA,14277
7
+ bullish/analysis/indicators.py,sha256=S3pUsWn4MC_BKPhY1iFQDNNuHcqulez8jOsfWJdht_8,23470
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=E0H78LOODl1H6s308jXpQGTUoFPoLOJkPBXOLQGLCeA,13331
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,39 @@ 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
23
+ bullish/database/alembic/versions/3e1a14c41916_.py,sha256=TmpfLl4dBw-CqHsnxwnnBc00nlUtHb0Hh8cc3yLBRME,1668
20
24
  bullish/database/alembic/versions/49c83f9eb5ac_.py,sha256=kCBItp7KmqpJ03roy5ikQjhefZia1oKgfZwournQDq8,3890
21
25
  bullish/database/alembic/versions/4b0a2f40b7d3_.py,sha256=G0K7w7pOPYjPZkXTB8LWhxoxuWBPcPwOfnubTBtdeEY,1827
26
+ bullish/database/alembic/versions/5b10ee7604c1_.py,sha256=YlqaagPasR3RKASv7acME1jPS8p26VoTE2BvpOwdCpY,1463
22
27
  bullish/database/alembic/versions/73564b60fe24_.py,sha256=MTlDRDNHj3E9gK7IMeAzv2UxxxYtWiu3gI_9xTLE-wg,1008
23
28
  bullish/database/alembic/versions/b76079e9845f_.py,sha256=W8eeTABjI9tT1dp3hlK7g7tiKqDhmA8AoUX9Sw-ykLI,1165
24
29
  bullish/database/alembic/versions/bf6b86dd5463_.py,sha256=fKB8knCprGmiL6AEyFdhybVmB7QX_W4MPFF9sPzUrSM,1094
25
30
  bullish/database/alembic/versions/d663166c531d_.py,sha256=U92l6QXqPniAYrPeu2Bt77ReDbXveLj4aGXtgd806JY,1915
31
+ bullish/database/alembic/versions/ec25c8fa449f_.py,sha256=8Yts74KEjK4jg20zIo90_0atw-sOBuE3hgCKl-rfS5E,2271
26
32
  bullish/database/alembic/versions/ee5baabb35f8_.py,sha256=nBMEY-_C8AsSXVPyaDdUkwrFFo2gxShzJhmrjejDwtc,1632
27
33
  bullish/database/alembic/versions/fc191121f522_.py,sha256=0sstF6TpAJ09-Mt-Vek9SdSWksvi4C58a5D92rBtuY8,1894
28
- bullish/database/crud.py,sha256=0AMrs07MMeDq5AVft_pf43KWPFOusMNzUmxuXwMX6K4,7400
29
- bullish/database/schemas.py,sha256=bU-DW49NqpBp--1VN486LUdDmLeScrI8TF69afzjoTc,1507
34
+ bullish/database/crud.py,sha256=EIXCnhvPAxwldicUG4fwsdiXiq08TjXoZ8wSt27ph0g,10808
35
+ bullish/database/schemas.py,sha256=ySTaw77X9rvLg-4PAKaOH6fPe8Bgi8kTtPc8DvsR6F8,2791
30
36
  bullish/database/scripts/create_revision.py,sha256=rggIf-3koPqJNth8FIg89EOfnIM7a9QrvL8X7UJsP0g,628
31
37
  bullish/database/scripts/stamp.py,sha256=PWgVUEBumjNUMjTnGw46qmU3p221LeN-KspnW_gFuu4,839
32
38
  bullish/database/scripts/upgrade.py,sha256=-Gz7aFNPEt9y9e1kltqXE76-j_8QeNtet_VlwY5AWjo,806
33
39
  bullish/database/settings.py,sha256=nMudufmF7iC_62_PHrGSMjlqDLN2I0qTbtz9JKZHSko,164
34
40
  bullish/exceptions.py,sha256=4z_i-dD-CDz1bkGmZH9DOf1L_awlCPCgdUDPF7dhWAI,106
35
41
  bullish/figures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
- bullish/figures/figures.py,sha256=SWTTiEoVyWMZeIIxg0ERi23v7s4tySB5BLKyPu12jC4,4193
42
+ bullish/figures/figures.py,sha256=imrvIIcL9L-z-3vzWK5hDEsNttZs60QxlFI-PLw0hJQ,4829
37
43
  bullish/interface/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
- bullish/interface/interface.py,sha256=6HXSK-N-vOuOGVserizknP4CeUGbUhB8P642oZmgpk0,3252
44
+ bullish/interface/interface.py,sha256=QbjC_tWLbhvQr8vaiQl8ymrb6f_vR_KCxEmzJ5lA1Zg,4528
39
45
  bullish/jobs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
46
  bullish/jobs/app.py,sha256=5MJ5KXUo7JSNAvOPgkpIMasD11VTrjQvGzM7vmCY65E,77
41
47
  bullish/jobs/models.py,sha256=ndrGTMP08S57yGLGEG9TQt8Uw2slc4HvbG-TZtEEuN0,744
42
48
  bullish/jobs/tasks.py,sha256=V_b0c8_GQC0-KIxaHDlLFhtkclQJOsck0gXaW6OlC_w,3055
43
49
  bullish/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
50
  bullish/utils/checks.py,sha256=Va10_xDVVnxYkOD2hafvyQ-TFV8FQpOkr4huJ7XgpDM,2188
45
- bullishpy-0.13.0.dist-info/METADATA,sha256=5aWLV_c8a5ys2AjiyEGgx-CnRK1Qs3EBXdCbu77wSIw,784
46
- bullishpy-0.13.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
47
- bullishpy-0.13.0.dist-info/entry_points.txt,sha256=eaPpmL6vmSBFo0FBtwibCXGqAW4LFJ83whJzT1VjD-0,43
48
- bullishpy-0.13.0.dist-info/RECORD,,
51
+ bullishpy-0.15.0.dist-info/METADATA,sha256=j-VPiqIl7LJyJ56b4oolBx4ZAsJljbxw-NA6wKzmir8,828
52
+ bullishpy-0.15.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
53
+ bullishpy-0.15.0.dist-info/entry_points.txt,sha256=eaPpmL6vmSBFo0FBtwibCXGqAW4LFJ83whJzT1VjD-0,43
54
+ bullishpy-0.15.0.dist-info/RECORD,,