bullishpy 0.37.0__py3-none-any.whl → 0.39.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 +51 -2
- bullish/app/app.py +82 -0
- bullish/database/alembic/versions/ae444f338124_.py +96 -0
- bullish/database/crud.py +24 -6
- bullish/database/schemas.py +5 -1
- bullish/interface/interface.py +4 -1
- {bullishpy-0.37.0.dist-info → bullishpy-0.39.0.dist-info}/METADATA +1 -1
- {bullishpy-0.37.0.dist-info → bullishpy-0.39.0.dist-info}/RECORD +10 -9
- {bullishpy-0.37.0.dist-info → bullishpy-0.39.0.dist-info}/WHEEL +0 -0
- {bullishpy-0.37.0.dist-info → bullishpy-0.39.0.dist-info}/entry_points.txt +0 -0
bullish/analysis/analysis.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import json
|
|
1
2
|
import logging
|
|
3
|
+
import re
|
|
2
4
|
import time
|
|
3
|
-
from datetime import date
|
|
5
|
+
from datetime import date, datetime
|
|
4
6
|
from itertools import batched, chain
|
|
5
7
|
from pathlib import Path
|
|
6
8
|
from typing import (
|
|
@@ -13,6 +15,7 @@ from typing import (
|
|
|
13
15
|
get_args,
|
|
14
16
|
TYPE_CHECKING,
|
|
15
17
|
ClassVar,
|
|
18
|
+
Dict,
|
|
16
19
|
)
|
|
17
20
|
|
|
18
21
|
import pandas as pd
|
|
@@ -488,12 +491,54 @@ class AnalysisView(BaseModel):
|
|
|
488
491
|
default=None,
|
|
489
492
|
),
|
|
490
493
|
]
|
|
494
|
+
consensus: Optional[str] = None
|
|
495
|
+
recommendation: Optional[str] = None
|
|
491
496
|
yearly_growth: Optional[float] = None
|
|
492
497
|
weekly_growth: Optional[float] = None
|
|
493
498
|
monthly_growth: Optional[float] = None
|
|
494
499
|
|
|
495
500
|
|
|
496
|
-
|
|
501
|
+
def json_loads(value: Any) -> Any:
|
|
502
|
+
if isinstance(value, str):
|
|
503
|
+
try:
|
|
504
|
+
return json.loads(value)
|
|
505
|
+
except Exception as e:
|
|
506
|
+
logger.debug(e)
|
|
507
|
+
return None
|
|
508
|
+
return value
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
def scrub(text: str) -> str:
|
|
512
|
+
strip_markup = re.compile(r"[\\`*_{}\[\]()>#+\-.!|~:$;\"\'<>&]").sub
|
|
513
|
+
return strip_markup("", text)
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
class SubjectAnalysis(BaseModel):
|
|
517
|
+
high_price_target: Optional[float] = None
|
|
518
|
+
low_price_target: Optional[float] = None
|
|
519
|
+
consensus: Optional[str] = None
|
|
520
|
+
reason: Optional[str] = None
|
|
521
|
+
recommendation: Optional[str] = None
|
|
522
|
+
explanation: Optional[str] = None
|
|
523
|
+
news_date: Optional[datetime] = None
|
|
524
|
+
news_summary: Annotated[
|
|
525
|
+
Optional[List[Dict[str, Any]]], BeforeValidator(json_loads)
|
|
526
|
+
] = None
|
|
527
|
+
summary: Annotated[Optional[Dict[str, Any]], BeforeValidator(json_loads)] = None
|
|
528
|
+
|
|
529
|
+
def to_news(self) -> Optional[str]:
|
|
530
|
+
if not self.news_summary:
|
|
531
|
+
return None
|
|
532
|
+
return "".join(
|
|
533
|
+
[
|
|
534
|
+
f"<p>{scrub(t.get('content').replace("\n",""))}</p>" # type: ignore
|
|
535
|
+
for t in self.news_summary
|
|
536
|
+
if t.get("content")
|
|
537
|
+
]
|
|
538
|
+
)
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
class Analysis(SubjectAnalysis, AnalysisEarningsDate, AnalysisView, BaseEquity, TechnicalAnalysis, FundamentalAnalysis): # type: ignore
|
|
497
542
|
|
|
498
543
|
@classmethod
|
|
499
544
|
def from_ticker(cls, bearish_db: "BullishDb", ticker: Ticker) -> "Analysis":
|
|
@@ -503,16 +548,20 @@ class Analysis(AnalysisEarningsDate, AnalysisView, BaseEquity, TechnicalAnalysis
|
|
|
503
548
|
excluded_sources=get_args(TickerOnlySources),
|
|
504
549
|
)
|
|
505
550
|
)
|
|
551
|
+
|
|
506
552
|
equity = asset.get_one_equity()
|
|
507
553
|
financials = Financials.from_ticker(bearish_db, ticker)
|
|
508
554
|
fundamental_analysis = FundamentalAnalysis.from_financials(financials, ticker)
|
|
509
555
|
prices = Prices.from_ticker(bearish_db, ticker)
|
|
510
556
|
technical_analysis = TechnicalAnalysis.from_data(prices.to_dataframe(), ticker)
|
|
511
557
|
next_earnings_date = bearish_db.read_next_earnings_date(ticker.symbol)
|
|
558
|
+
subject = bearish_db.read_subject(ticker.symbol)
|
|
559
|
+
|
|
512
560
|
return cls.model_validate(
|
|
513
561
|
equity.model_dump()
|
|
514
562
|
| fundamental_analysis.model_dump()
|
|
515
563
|
| technical_analysis.model_dump()
|
|
564
|
+
| (subject.model_dump() if subject else {})
|
|
516
565
|
| {
|
|
517
566
|
"next_earnings_date": next_earnings_date,
|
|
518
567
|
"price_per_earning_ratio": (
|
bullish/app/app.py
CHANGED
|
@@ -91,11 +91,13 @@ def on_table_select() -> None:
|
|
|
91
91
|
prices = db.read_series(query, months=24)
|
|
92
92
|
data = Prices(prices=prices).to_dataframe()
|
|
93
93
|
dates = db.read_dates(symbol)
|
|
94
|
+
subject = db.read_subject(symbol)
|
|
94
95
|
industry_data = get_industry_comparison_data(db, data, "Mean", industry, country)
|
|
95
96
|
|
|
96
97
|
fig = plot(data, symbol, dates=dates, industry_data=industry_data)
|
|
97
98
|
|
|
98
99
|
st.session_state.ticker_figure = fig
|
|
100
|
+
st.session_state.ticker_news = subject
|
|
99
101
|
|
|
100
102
|
|
|
101
103
|
@st.dialog("🔑 Provide database file to continue")
|
|
@@ -278,6 +280,7 @@ def filter() -> None:
|
|
|
278
280
|
st.session_state.database_path
|
|
279
281
|
).read_filter_query(query)
|
|
280
282
|
st.session_state.ticker_figure = None
|
|
283
|
+
st.session_state.ticker_news = None
|
|
281
284
|
st.session_state.filter_query = {}
|
|
282
285
|
st.session_state.query = query
|
|
283
286
|
st.rerun()
|
|
@@ -297,8 +300,87 @@ def dialog_plot_figure() -> None:
|
|
|
297
300
|
unsafe_allow_html=True,
|
|
298
301
|
)
|
|
299
302
|
st.html("<span class='big-dialog'></span>")
|
|
303
|
+
if st.session_state.ticker_news:
|
|
304
|
+
st.markdown(
|
|
305
|
+
f"""
|
|
306
|
+
<div class="news-hover" >
|
|
307
|
+
📰 <span class="label">News</span>
|
|
308
|
+
<div class="tooltip">
|
|
309
|
+
<h2>Date: {st.session_state.ticker_news.news_date.date()}</h2>
|
|
310
|
+
<h2>Price targets</h2>
|
|
311
|
+
<p>High price target: {st.session_state.ticker_news.high_price_target}</p>
|
|
312
|
+
<p>Low price target: {st.session_state.ticker_news.low_price_target}</p>
|
|
313
|
+
<h2>Recommendation: {st.session_state.ticker_news.recommendation}</h2>
|
|
314
|
+
<h2>Consensus: {st.session_state.ticker_news.consensus}</h2>
|
|
315
|
+
<h2>Explanation & reasons</h2>
|
|
316
|
+
<p>{st.session_state.ticker_news.explanation}</p>
|
|
317
|
+
<p>{st.session_state.ticker_news.reason}</p>
|
|
318
|
+
<h2>News summaries</h2>
|
|
319
|
+
{st.session_state.ticker_news.to_news()}
|
|
320
|
+
</div>
|
|
321
|
+
</div>
|
|
322
|
+
<style>
|
|
323
|
+
/* Hover target (fixed top-left) */
|
|
324
|
+
.news-hover {{
|
|
325
|
+
position: absolute;
|
|
326
|
+
left: 1rem;
|
|
327
|
+
display: inline-flex;
|
|
328
|
+
align-items: center;
|
|
329
|
+
gap: .4rem;
|
|
330
|
+
font-size: 1.7rem; /* big label */
|
|
331
|
+
font-weight: 600;
|
|
332
|
+
color: #333;
|
|
333
|
+
cursor: pointer;
|
|
334
|
+
user-select: none;
|
|
335
|
+
z-index: 1100;
|
|
336
|
+
}}
|
|
337
|
+
/* Tooltip bubble */
|
|
338
|
+
.news-hover .tooltip {{
|
|
339
|
+
position: absolute;
|
|
340
|
+
top: 110%; /* below the label */
|
|
341
|
+
left: 0;
|
|
342
|
+
width: 840px;
|
|
343
|
+
max-height: 620px;
|
|
344
|
+
overflow-y: auto;
|
|
345
|
+
background: #222;
|
|
346
|
+
color: #fff;
|
|
347
|
+
padding: 1.2rem;
|
|
348
|
+
border-radius: 10px;
|
|
349
|
+
font-size: .95rem;
|
|
350
|
+
line-height: 1.45;
|
|
351
|
+
box-shadow: 0 8px 20px rgba(0,0,0,.4);
|
|
352
|
+
opacity: 0;
|
|
353
|
+
visibility: hidden;
|
|
354
|
+
transition: opacity .25s ease;
|
|
355
|
+
}}
|
|
356
|
+
.news-hover .tooltip hr {{
|
|
357
|
+
border: none;
|
|
358
|
+
border-top: 1px solid #444;
|
|
359
|
+
margin: 1rem 0;
|
|
360
|
+
}}
|
|
361
|
+
/* Show tooltip on hover or keyboard focus */
|
|
362
|
+
.news-hover:hover .tooltip,
|
|
363
|
+
.news-hover:focus-within .tooltip {{
|
|
364
|
+
opacity: 1;
|
|
365
|
+
visibility: visible;
|
|
366
|
+
}}
|
|
367
|
+
/* Little arrow under the bubble */
|
|
368
|
+
.news-hover .tooltip::after {{
|
|
369
|
+
content: "";
|
|
370
|
+
position: absolute;
|
|
371
|
+
top: -10px;
|
|
372
|
+
left: 20px;
|
|
373
|
+
border: 10px solid transparent;
|
|
374
|
+
border-top-color: #222;
|
|
375
|
+
}}
|
|
376
|
+
</style>
|
|
377
|
+
""",
|
|
378
|
+
unsafe_allow_html=True,
|
|
379
|
+
)
|
|
380
|
+
|
|
300
381
|
st.plotly_chart(st.session_state.ticker_figure, use_container_width=True)
|
|
301
382
|
st.session_state.ticker_figure = None
|
|
383
|
+
st.session_state.ticker_news = None
|
|
302
384
|
|
|
303
385
|
|
|
304
386
|
@st.dialog("⭐ Save filtered results")
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""
|
|
2
|
+
|
|
3
|
+
Revision ID: ae444f338124
|
|
4
|
+
Revises: d0e58e050845
|
|
5
|
+
Create Date: 2025-08-07 11:07:48.169941
|
|
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 = "ae444f338124"
|
|
18
|
+
down_revision: Union[str, None] = "d0e58e050845"
|
|
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
|
+
with op.batch_alter_table("analysis", schema=None) as batch_op:
|
|
26
|
+
batch_op.add_column(
|
|
27
|
+
sa.Column("consensus", sqlmodel.sql.sqltypes.AutoString(), nullable=True)
|
|
28
|
+
)
|
|
29
|
+
batch_op.add_column(
|
|
30
|
+
sa.Column(
|
|
31
|
+
"recommendation", sqlmodel.sql.sqltypes.AutoString(), nullable=True
|
|
32
|
+
)
|
|
33
|
+
)
|
|
34
|
+
batch_op.add_column(sa.Column("high_price_target", sa.Float(), nullable=True))
|
|
35
|
+
batch_op.add_column(sa.Column("low_price_target", sa.Float(), nullable=True))
|
|
36
|
+
batch_op.add_column(
|
|
37
|
+
sa.Column("reason", sqlmodel.sql.sqltypes.AutoString(), nullable=True)
|
|
38
|
+
)
|
|
39
|
+
batch_op.add_column(
|
|
40
|
+
sa.Column("explanation", sqlmodel.sql.sqltypes.AutoString(), nullable=True)
|
|
41
|
+
)
|
|
42
|
+
batch_op.add_column(sa.Column("news_date", sa.DateTime(), nullable=True))
|
|
43
|
+
batch_op.add_column(sa.Column("news_summary", sa.JSON(), nullable=True))
|
|
44
|
+
batch_op.alter_column(
|
|
45
|
+
"summary",
|
|
46
|
+
existing_type=sa.VARCHAR(),
|
|
47
|
+
type_=sa.JSON(),
|
|
48
|
+
existing_nullable=True,
|
|
49
|
+
)
|
|
50
|
+
batch_op.create_index("ix_analysis_consensus", ["consensus"], unique=False)
|
|
51
|
+
batch_op.create_index("ix_analysis_explanation", ["explanation"], unique=False)
|
|
52
|
+
batch_op.create_index(
|
|
53
|
+
"ix_analysis_high_price_target", ["high_price_target"], unique=False
|
|
54
|
+
)
|
|
55
|
+
batch_op.create_index(
|
|
56
|
+
"ix_analysis_low_price_target", ["low_price_target"], unique=False
|
|
57
|
+
)
|
|
58
|
+
batch_op.create_index("ix_analysis_news_date", ["news_date"], unique=False)
|
|
59
|
+
batch_op.create_index(
|
|
60
|
+
"ix_analysis_news_summary", ["news_summary"], unique=False
|
|
61
|
+
)
|
|
62
|
+
batch_op.create_index("ix_analysis_reason", ["reason"], unique=False)
|
|
63
|
+
batch_op.create_index(
|
|
64
|
+
"ix_analysis_recommendation", ["recommendation"], unique=False
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# ### end Alembic commands ###
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def downgrade() -> None:
|
|
71
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
72
|
+
with op.batch_alter_table("analysis", schema=None) as batch_op:
|
|
73
|
+
batch_op.drop_index("ix_analysis_recommendation")
|
|
74
|
+
batch_op.drop_index("ix_analysis_reason")
|
|
75
|
+
batch_op.drop_index("ix_analysis_news_summary")
|
|
76
|
+
batch_op.drop_index("ix_analysis_news_date")
|
|
77
|
+
batch_op.drop_index("ix_analysis_low_price_target")
|
|
78
|
+
batch_op.drop_index("ix_analysis_high_price_target")
|
|
79
|
+
batch_op.drop_index("ix_analysis_explanation")
|
|
80
|
+
batch_op.drop_index("ix_analysis_consensus")
|
|
81
|
+
batch_op.alter_column(
|
|
82
|
+
"summary",
|
|
83
|
+
existing_type=sa.JSON(),
|
|
84
|
+
type_=sa.VARCHAR(),
|
|
85
|
+
existing_nullable=True,
|
|
86
|
+
)
|
|
87
|
+
batch_op.drop_column("news_summary")
|
|
88
|
+
batch_op.drop_column("news_date")
|
|
89
|
+
batch_op.drop_column("explanation")
|
|
90
|
+
batch_op.drop_column("reason")
|
|
91
|
+
batch_op.drop_column("low_price_target")
|
|
92
|
+
batch_op.drop_column("high_price_target")
|
|
93
|
+
batch_op.drop_column("recommendation")
|
|
94
|
+
batch_op.drop_column("consensus")
|
|
95
|
+
|
|
96
|
+
# ### end Alembic commands ###
|
bullish/database/crud.py
CHANGED
|
@@ -7,21 +7,21 @@ from typing import TYPE_CHECKING, Any, List, Optional
|
|
|
7
7
|
|
|
8
8
|
import pandas as pd
|
|
9
9
|
from bearish.database.crud import BearishDb # type: ignore
|
|
10
|
-
from bearish.models.base import Ticker # type: ignore
|
|
11
10
|
from bearish.database.schemas import EarningsDateORM, EquityORM, PriceORM # type: ignore
|
|
12
|
-
from bearish.
|
|
11
|
+
from bearish.models.base import Ticker # type: ignore
|
|
13
12
|
from bearish.models.price.price import Price # type: ignore
|
|
14
13
|
from bearish.models.price.prices import Prices # type: ignore
|
|
14
|
+
from bearish.types import Sources # type: ignore
|
|
15
15
|
from pydantic import ConfigDict
|
|
16
16
|
from sqlalchemy import Engine, create_engine, insert, delete, update
|
|
17
|
+
from sqlalchemy import text
|
|
17
18
|
from sqlmodel import Session, select
|
|
18
19
|
|
|
19
|
-
from bullish.analysis.analysis import Analysis
|
|
20
|
-
|
|
20
|
+
from bullish.analysis.analysis import Analysis, SubjectAnalysis
|
|
21
21
|
from bullish.analysis.constants import Industry, IndustryGroup, Sector, Country
|
|
22
|
+
from bullish.analysis.filter import FilteredResults
|
|
22
23
|
from bullish.analysis.indicators import SignalSeries
|
|
23
24
|
from bullish.analysis.industry_views import Type, IndustryView
|
|
24
|
-
|
|
25
25
|
from bullish.database.schemas import (
|
|
26
26
|
AnalysisORM,
|
|
27
27
|
JobTrackerORM,
|
|
@@ -32,7 +32,6 @@ from bullish.database.schemas import (
|
|
|
32
32
|
)
|
|
33
33
|
from bullish.database.scripts.upgrade import upgrade
|
|
34
34
|
from bullish.exceptions import DatabaseFileNotFoundError
|
|
35
|
-
from bullish.analysis.filter import FilteredResults
|
|
36
35
|
from bullish.interface.interface import BullishDbBase
|
|
37
36
|
from bullish.jobs.models import JobTracker, JobTrackerStatus
|
|
38
37
|
|
|
@@ -341,3 +340,22 @@ class BullishDb(BearishDb, BullishDbBase): # type: ignore
|
|
|
341
340
|
if result:
|
|
342
341
|
return result.date() # type: ignore
|
|
343
342
|
return None
|
|
343
|
+
|
|
344
|
+
def read_subject(self, symbol: str) -> Optional[SubjectAnalysis]:
|
|
345
|
+
sql = text(
|
|
346
|
+
"""
|
|
347
|
+
SELECT *
|
|
348
|
+
FROM subject
|
|
349
|
+
WHERE symbol = :symbol
|
|
350
|
+
ORDER BY date DESC
|
|
351
|
+
LIMIT 1
|
|
352
|
+
"""
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
with Session(self._engine) as session:
|
|
356
|
+
row = session.execute(sql, {"symbol": symbol}).mappings().one_or_none()
|
|
357
|
+
if row:
|
|
358
|
+
row_dict = dict(row)
|
|
359
|
+
row_dict = row_dict | {"news_date": row_dict["date"]}
|
|
360
|
+
return SubjectAnalysis.model_validate(row_dict)
|
|
361
|
+
return None
|
bullish/database/schemas.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Dict, Any
|
|
1
|
+
from typing import Dict, Any, List, Optional
|
|
2
2
|
|
|
3
3
|
from sqlmodel import Field, SQLModel
|
|
4
4
|
from sqlalchemy import Column, JSON
|
|
@@ -25,6 +25,10 @@ dynamic_indexes = tuple(
|
|
|
25
25
|
class AnalysisORM(BaseTable, Analysis, table=True):
|
|
26
26
|
__tablename__ = "analysis"
|
|
27
27
|
__table_args__ = {"extend_existing": True} # noqa:RUF012
|
|
28
|
+
news_summary: Optional[List[Dict[str, Any]]] = Field(
|
|
29
|
+
default=None, sa_column=Column(JSON)
|
|
30
|
+
)
|
|
31
|
+
summary: Optional[Dict[str, Any]] = Field(default=None, sa_column=Column(JSON))
|
|
28
32
|
|
|
29
33
|
|
|
30
34
|
AnalysisORM.__table_args__ = tuple( # type: ignore # noqa: RUF005
|
bullish/interface/interface.py
CHANGED
|
@@ -9,7 +9,7 @@ from bearish.models.base import Ticker # type: ignore
|
|
|
9
9
|
from bearish.types import Sources # type: ignore
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
from bullish.analysis.analysis import Analysis, AnalysisView
|
|
12
|
+
from bullish.analysis.analysis import Analysis, AnalysisView, SubjectAnalysis
|
|
13
13
|
from bullish.analysis.backtest import BacktestResult, BacktestResultQuery
|
|
14
14
|
from bullish.analysis.constants import Industry, Sector, IndustryGroup, Country
|
|
15
15
|
from bullish.analysis.filter import FilterQuery, FilteredResults
|
|
@@ -152,3 +152,6 @@ class BullishDbBase(BearishDbBase): # type: ignore
|
|
|
152
152
|
|
|
153
153
|
@abc.abstractmethod
|
|
154
154
|
def read_next_earnings_date(self, symbol: str) -> Optional[date]: ...
|
|
155
|
+
|
|
156
|
+
@abc.abstractmethod
|
|
157
|
+
def read_subject(self, symbol: str) -> Optional[SubjectAnalysis]: ...
|
|
@@ -1,6 +1,6 @@
|
|
|
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=
|
|
3
|
+
bullish/analysis/analysis.py,sha256=8FMEaDlgFERrgRPYq5zqIRgoeBvJVh3C91Zxl8eD7HU,23788
|
|
4
4
|
bullish/analysis/backtest.py,sha256=x91ek5kOzJHvYq0TmJh1Q8wBDDduIaieE0zDaoZFXew,14325
|
|
5
5
|
bullish/analysis/constants.py,sha256=X3oCyYNA6B-jsZSYJLeGQ94S453Z7jIVNPmv3lMPp8Q,9922
|
|
6
6
|
bullish/analysis/filter.py,sha256=mGWizTda_QHEmusDmB_xvOhjlsOKcbILfKLjJLAnCnE,9285
|
|
@@ -9,7 +9,7 @@ bullish/analysis/indicators.py,sha256=XsMHc4-hEZwxFpI3JI-s4C2hcg0eCQLWcAQ8P46dtL
|
|
|
9
9
|
bullish/analysis/industry_views.py,sha256=-B4CCAYz2arGQtWTXLLMpox0loO_MGdVQd2ycCRMOQQ,6799
|
|
10
10
|
bullish/analysis/predefined_filters.py,sha256=kx3vhtvGu_0ySWsWPzkqXONmB7COWgMowv3TEVrk1Uc,8198
|
|
11
11
|
bullish/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
-
bullish/app/app.py,sha256=
|
|
12
|
+
bullish/app/app.py,sha256=37RPSxKkncP9yzrlJtTr9xkgf7PLdOByN-hyERF3Y5Q,17554
|
|
13
13
|
bullish/cli.py,sha256=azhVLwOUrmwrtFAJSgva8-UFgNgkepXhjp7DxQNc-yw,2427
|
|
14
14
|
bullish/database/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
15
|
bullish/database/alembic/README,sha256=heMzebYwlGhnE8_4CWJ4LS74WoEZjBy-S-mIJRxAEKI,39
|
|
@@ -28,6 +28,7 @@ bullish/database/alembic/versions/5b10ee7604c1_.py,sha256=YlqaagPasR3RKASv7acME1
|
|
|
28
28
|
bullish/database/alembic/versions/6d252e23f543_.py,sha256=izF-ejdXk733INkAokGqjA2U_M0_c1f_ruihZ-cgP7s,1525
|
|
29
29
|
bullish/database/alembic/versions/73564b60fe24_.py,sha256=MTlDRDNHj3E9gK7IMeAzv2UxxxYtWiu3gI_9xTLE-wg,1008
|
|
30
30
|
bullish/database/alembic/versions/79bc71ec6f9e_.py,sha256=4nShut2NEd1F3piSckIIBtke0GEsFAxYw5TZl5YYRzc,1140
|
|
31
|
+
bullish/database/alembic/versions/ae444f338124_.py,sha256=u8RphcniLCQce-HvN666QgCJpLsv6A91-a4R-Nif4bU,3672
|
|
31
32
|
bullish/database/alembic/versions/b76079e9845f_.py,sha256=W8eeTABjI9tT1dp3hlK7g7tiKqDhmA8AoUX9Sw-ykLI,1165
|
|
32
33
|
bullish/database/alembic/versions/bf6b86dd5463_.py,sha256=fKB8knCprGmiL6AEyFdhybVmB7QX_W4MPFF9sPzUrSM,1094
|
|
33
34
|
bullish/database/alembic/versions/d0e58e050845_.py,sha256=x_LS3J27FNyy_WD99uvZzNehly-jpgn9abOYN-VjjZc,1164
|
|
@@ -36,8 +37,8 @@ bullish/database/alembic/versions/ec25c8fa449f_.py,sha256=8Yts74KEjK4jg20zIo90_0
|
|
|
36
37
|
bullish/database/alembic/versions/ee5baabb35f8_.py,sha256=nBMEY-_C8AsSXVPyaDdUkwrFFo2gxShzJhmrjejDwtc,1632
|
|
37
38
|
bullish/database/alembic/versions/fc191121f522_.py,sha256=0sstF6TpAJ09-Mt-Vek9SdSWksvi4C58a5D92rBtuY8,1894
|
|
38
39
|
bullish/database/alembic/versions/ff0cc4ba40ec_.py,sha256=74lxga54ig_LoNZYK9toJL9iRwGbNRezh1zvO1YI40U,2719
|
|
39
|
-
bullish/database/crud.py,sha256=
|
|
40
|
-
bullish/database/schemas.py,sha256=
|
|
40
|
+
bullish/database/crud.py,sha256=8qc1WqL3AR9_7TkIDMB_icbXghl1DgJnxVxPnopHPho,13778
|
|
41
|
+
bullish/database/schemas.py,sha256=fQ4RZeOjlFoIor7rjwpisbHRNDd7-zbyDdzNKaiNGQQ,3637
|
|
41
42
|
bullish/database/scripts/create_revision.py,sha256=rggIf-3koPqJNth8FIg89EOfnIM7a9QrvL8X7UJsP0g,628
|
|
42
43
|
bullish/database/scripts/stamp.py,sha256=PWgVUEBumjNUMjTnGw46qmU3p221LeN-KspnW_gFuu4,839
|
|
43
44
|
bullish/database/scripts/upgrade.py,sha256=-Gz7aFNPEt9y9e1kltqXE76-j_8QeNtet_VlwY5AWjo,806
|
|
@@ -46,14 +47,14 @@ bullish/exceptions.py,sha256=4z_i-dD-CDz1bkGmZH9DOf1L_awlCPCgdUDPF7dhWAI,106
|
|
|
46
47
|
bullish/figures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
47
48
|
bullish/figures/figures.py,sha256=EpJQOiSqSp7cHvZoGlZrF6UVpyv-fFyDApAfskqdUkU,4562
|
|
48
49
|
bullish/interface/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
49
|
-
bullish/interface/interface.py,sha256=
|
|
50
|
+
bullish/interface/interface.py,sha256=R2qVEMyBl9mBlPUO40zXp4vhfLKH7pgl_u2BmAVlD4w,5250
|
|
50
51
|
bullish/jobs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
51
52
|
bullish/jobs/app.py,sha256=5MJ5KXUo7JSNAvOPgkpIMasD11VTrjQvGzM7vmCY65E,77
|
|
52
53
|
bullish/jobs/models.py,sha256=S2yvBf69lmt4U-5OU5CjXCMSw0s9Ubh9xkrB3k2qOZo,764
|
|
53
54
|
bullish/jobs/tasks.py,sha256=jqiTTe9PV4x9ebV5QPRBiGyANhOoAymigYqs44WWH9k,3714
|
|
54
55
|
bullish/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
55
56
|
bullish/utils/checks.py,sha256=Va10_xDVVnxYkOD2hafvyQ-TFV8FQpOkr4huJ7XgpDM,2188
|
|
56
|
-
bullishpy-0.
|
|
57
|
-
bullishpy-0.
|
|
58
|
-
bullishpy-0.
|
|
59
|
-
bullishpy-0.
|
|
57
|
+
bullishpy-0.39.0.dist-info/METADATA,sha256=IMv3i6VaCnGsaBH4F3SuYXNGQzkFclHglk8AklF4ngw,830
|
|
58
|
+
bullishpy-0.39.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
59
|
+
bullishpy-0.39.0.dist-info/entry_points.txt,sha256=eaPpmL6vmSBFo0FBtwibCXGqAW4LFJ83whJzT1VjD-0,43
|
|
60
|
+
bullishpy-0.39.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|