bullishpy 0.37.0__py3-none-any.whl → 0.38.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 +45 -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.38.0.dist-info}/METADATA +1 -1
- {bullishpy-0.37.0.dist-info → bullishpy-0.38.0.dist-info}/RECORD +10 -9
- {bullishpy-0.37.0.dist-info → bullishpy-0.38.0.dist-info}/WHEEL +0 -0
- {bullishpy-0.37.0.dist-info → bullishpy-0.38.0.dist-info}/entry_points.txt +0 -0
bullish/analysis/analysis.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import json
|
|
1
2
|
import logging
|
|
2
3
|
import time
|
|
3
|
-
from datetime import date
|
|
4
|
+
from datetime import date, datetime
|
|
4
5
|
from itertools import batched, chain
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
from typing import (
|
|
@@ -13,6 +14,7 @@ from typing import (
|
|
|
13
14
|
get_args,
|
|
14
15
|
TYPE_CHECKING,
|
|
15
16
|
ClassVar,
|
|
17
|
+
Dict,
|
|
16
18
|
)
|
|
17
19
|
|
|
18
20
|
import pandas as pd
|
|
@@ -488,12 +490,49 @@ class AnalysisView(BaseModel):
|
|
|
488
490
|
default=None,
|
|
489
491
|
),
|
|
490
492
|
]
|
|
493
|
+
consensus: Optional[str] = None
|
|
494
|
+
recommendation: Optional[str] = None
|
|
491
495
|
yearly_growth: Optional[float] = None
|
|
492
496
|
weekly_growth: Optional[float] = None
|
|
493
497
|
monthly_growth: Optional[float] = None
|
|
494
498
|
|
|
495
499
|
|
|
496
|
-
|
|
500
|
+
def json_loads(value: Any) -> Any:
|
|
501
|
+
if isinstance(value, str):
|
|
502
|
+
try:
|
|
503
|
+
return json.loads(value)
|
|
504
|
+
except Exception as e:
|
|
505
|
+
logger.debug(e)
|
|
506
|
+
return None
|
|
507
|
+
return value
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
class SubjectAnalysis(BaseModel):
|
|
511
|
+
high_price_target: Optional[float] = None
|
|
512
|
+
low_price_target: Optional[float] = None
|
|
513
|
+
consensus: Optional[str] = None
|
|
514
|
+
reason: Optional[str] = None
|
|
515
|
+
recommendation: Optional[str] = None
|
|
516
|
+
explanation: Optional[str] = None
|
|
517
|
+
news_date: Optional[datetime] = None
|
|
518
|
+
news_summary: Annotated[
|
|
519
|
+
Optional[List[Dict[str, Any]]], BeforeValidator(json_loads)
|
|
520
|
+
] = None
|
|
521
|
+
summary: Annotated[Optional[Dict[str, Any]], BeforeValidator(json_loads)] = None
|
|
522
|
+
|
|
523
|
+
def to_news(self) -> Optional[str]:
|
|
524
|
+
if not self.news_summary:
|
|
525
|
+
return None
|
|
526
|
+
return "".join(
|
|
527
|
+
[
|
|
528
|
+
f"<p>{t.get('content')}</p>"
|
|
529
|
+
for t in self.news_summary
|
|
530
|
+
if t.get("content")
|
|
531
|
+
]
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
class Analysis(SubjectAnalysis, AnalysisEarningsDate, AnalysisView, BaseEquity, TechnicalAnalysis, FundamentalAnalysis): # type: ignore
|
|
497
536
|
|
|
498
537
|
@classmethod
|
|
499
538
|
def from_ticker(cls, bearish_db: "BullishDb", ticker: Ticker) -> "Analysis":
|
|
@@ -503,16 +542,20 @@ class Analysis(AnalysisEarningsDate, AnalysisView, BaseEquity, TechnicalAnalysis
|
|
|
503
542
|
excluded_sources=get_args(TickerOnlySources),
|
|
504
543
|
)
|
|
505
544
|
)
|
|
545
|
+
|
|
506
546
|
equity = asset.get_one_equity()
|
|
507
547
|
financials = Financials.from_ticker(bearish_db, ticker)
|
|
508
548
|
fundamental_analysis = FundamentalAnalysis.from_financials(financials, ticker)
|
|
509
549
|
prices = Prices.from_ticker(bearish_db, ticker)
|
|
510
550
|
technical_analysis = TechnicalAnalysis.from_data(prices.to_dataframe(), ticker)
|
|
511
551
|
next_earnings_date = bearish_db.read_next_earnings_date(ticker.symbol)
|
|
552
|
+
subject = bearish_db.read_subject(ticker.symbol)
|
|
553
|
+
|
|
512
554
|
return cls.model_validate(
|
|
513
555
|
equity.model_dump()
|
|
514
556
|
| fundamental_analysis.model_dump()
|
|
515
557
|
| technical_analysis.model_dump()
|
|
558
|
+
| (subject.model_dump() if subject else {})
|
|
516
559
|
| {
|
|
517
560
|
"next_earnings_date": next_earnings_date,
|
|
518
561
|
"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=O7bTqZfgRBj3jMXhcm8COy8M5u9rUQuKKMBsicr1N70,23600
|
|
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.38.0.dist-info/METADATA,sha256=egDaIIcxG8oKLLvNhrZETkS7JkvPX6f5Q5m7_bf49ZI,830
|
|
58
|
+
bullishpy-0.38.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
59
|
+
bullishpy-0.38.0.dist-info/entry_points.txt,sha256=eaPpmL6vmSBFo0FBtwibCXGqAW4LFJ83whJzT1VjD-0,43
|
|
60
|
+
bullishpy-0.38.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|