bullishpy 0.11.0__py3-none-any.whl → 0.13.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.
- bullish/analysis/analysis.py +24 -3
- bullish/analysis/functions.py +27 -2
- bullish/analysis/indicators.py +16 -1
- bullish/analysis/predefined_filters.py +74 -0
- bullish/database/alembic/versions/b76079e9845f_.py +40 -0
- bullish/database/alembic/versions/bf6b86dd5463_.py +38 -0
- bullish/database/crud.py +10 -0
- bullish/interface/interface.py +6 -0
- {bullishpy-0.11.0.dist-info → bullishpy-0.13.0.dist-info}/METADATA +3 -2
- {bullishpy-0.11.0.dist-info → bullishpy-0.13.0.dist-info}/RECORD +12 -10
- {bullishpy-0.11.0.dist-info → bullishpy-0.13.0.dist-info}/WHEEL +0 -0
- {bullishpy-0.11.0.dist-info → bullishpy-0.13.0.dist-info}/entry_points.txt +0 -0
bullish/analysis/analysis.py
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
import time
|
|
3
|
+
from itertools import batched
|
|
4
|
+
from pathlib import Path
|
|
2
5
|
from typing import (
|
|
3
6
|
Annotated,
|
|
4
7
|
Any,
|
|
@@ -40,6 +43,7 @@ from bearish.types import TickerOnlySources # type: ignore
|
|
|
40
43
|
from pydantic import BaseModel, BeforeValidator, Field, create_model
|
|
41
44
|
|
|
42
45
|
from bullish.analysis.indicators import Indicators, IndicatorModels
|
|
46
|
+
from joblib import Parallel, delayed # type: ignore
|
|
43
47
|
|
|
44
48
|
if TYPE_CHECKING:
|
|
45
49
|
from bullish.database.crud import BullishDb
|
|
@@ -482,10 +486,27 @@ class Analysis(AnalysisView, BaseEquity, TechnicalAnalysis, FundamentalAnalysis)
|
|
|
482
486
|
)
|
|
483
487
|
|
|
484
488
|
|
|
489
|
+
def compute_analysis(database_path: Path, ticker: Ticker) -> Analysis:
|
|
490
|
+
from bullish.database.crud import BullishDb
|
|
491
|
+
|
|
492
|
+
bullish_db = BullishDb(database_path=database_path)
|
|
493
|
+
return Analysis.from_ticker(bullish_db, ticker)
|
|
494
|
+
|
|
495
|
+
|
|
485
496
|
def run_analysis(bullish_db: "BullishDb") -> None:
|
|
486
497
|
price_trackers = set(bullish_db._read_tracker(TrackerQuery(), PriceTracker))
|
|
487
498
|
finance_trackers = set(bullish_db._read_tracker(TrackerQuery(), FinancialsTracker))
|
|
488
499
|
tickers = list(price_trackers.intersection(finance_trackers))
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
500
|
+
parallel = Parallel(n_jobs=-1)
|
|
501
|
+
|
|
502
|
+
for batch_ticker in batched(tickers, 1000):
|
|
503
|
+
start = time.perf_counter()
|
|
504
|
+
many_analysis = parallel(
|
|
505
|
+
delayed(compute_analysis)(bullish_db.database_path, ticker)
|
|
506
|
+
for ticker in batch_ticker
|
|
507
|
+
)
|
|
508
|
+
bullish_db.write_many_analysis(many_analysis)
|
|
509
|
+
elapsed_time = time.perf_counter() - start
|
|
510
|
+
print(
|
|
511
|
+
f"Computed analysis for {len(batch_ticker)} tickers in {elapsed_time:.2f} seconds."
|
|
512
|
+
)
|
bullish/analysis/functions.py
CHANGED
|
@@ -122,6 +122,9 @@ def compute_roc(data: pd.DataFrame) -> pd.DataFrame:
|
|
|
122
122
|
results["ROC_7"] = talib.ROC(data.close, timeperiod=7) # type: ignore
|
|
123
123
|
results["ROC_1"] = talib.ROC(data.close, timeperiod=1) # type: ignore
|
|
124
124
|
results["ROC_30"] = talib.ROC(data.close, timeperiod=30) # type: ignore
|
|
125
|
+
mom = talib.MOM(data.close, timeperiod=252) # type: ignore
|
|
126
|
+
results["MOM"] = mom.shift(21) # type: ignore
|
|
127
|
+
|
|
125
128
|
return results
|
|
126
129
|
|
|
127
130
|
|
|
@@ -137,6 +140,7 @@ def compute_sma(data: pd.DataFrame) -> pd.DataFrame:
|
|
|
137
140
|
results = pd.DataFrame(index=data.index)
|
|
138
141
|
results["SMA_50"] = talib.SMA(data.close, timeperiod=50) # type: ignore
|
|
139
142
|
results["SMA_200"] = talib.SMA(data.close, timeperiod=200) # type: ignore
|
|
143
|
+
results["CLOSE"] = data.close
|
|
140
144
|
return results
|
|
141
145
|
|
|
142
146
|
|
|
@@ -144,6 +148,8 @@ def compute_pandas_ta_sma(data: pd.DataFrame) -> pd.DataFrame:
|
|
|
144
148
|
results = pd.DataFrame(index=data.index)
|
|
145
149
|
results["SMA_50"] = ta.sma(data.close, length=50)
|
|
146
150
|
results["SMA_200"] = ta.sma(data.close, length=200)
|
|
151
|
+
results["CLOSE"] = data.close
|
|
152
|
+
|
|
147
153
|
return results
|
|
148
154
|
|
|
149
155
|
|
|
@@ -294,6 +300,25 @@ def compute_percentile_return_after_rsi_crossover(
|
|
|
294
300
|
return float(np.percentile(values, 30))
|
|
295
301
|
|
|
296
302
|
|
|
303
|
+
def find_last_true_run_start(series: pd.Series) -> Optional[date]:
|
|
304
|
+
if not series.iloc[-1]:
|
|
305
|
+
return None
|
|
306
|
+
arr = series.to_numpy()
|
|
307
|
+
change_points = np.flatnonzero(np.r_[True, arr[1:] != arr[:-1]])
|
|
308
|
+
run_starts = change_points
|
|
309
|
+
true_runs = run_starts[arr[run_starts]]
|
|
310
|
+
last_true_run_start = true_runs[-1]
|
|
311
|
+
return series.index[last_true_run_start].date() # type: ignore
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def momentum(data: pd.DataFrame) -> Optional[date]:
|
|
315
|
+
date_1 = find_last_true_run_start(data.SMA_50 < data.CLOSE)
|
|
316
|
+
date_2 = find_last_true_run_start(data.SMA_200 < data.SMA_50)
|
|
317
|
+
if date_1 is None or date_2 is None:
|
|
318
|
+
return None
|
|
319
|
+
return max(date_1, date_2)
|
|
320
|
+
|
|
321
|
+
|
|
297
322
|
class IndicatorFunction(BaseModel):
|
|
298
323
|
expected_columns: list[str]
|
|
299
324
|
functions: list[Callable[[pd.DataFrame], pd.DataFrame]]
|
|
@@ -334,7 +359,7 @@ MFI = IndicatorFunction(
|
|
|
334
359
|
expected_columns=["MFI"], functions=[compute_mfi, compute_pandas_ta_mfi]
|
|
335
360
|
)
|
|
336
361
|
ROC = IndicatorFunction(
|
|
337
|
-
expected_columns=["ROC_7", "ROC_1", "ROC_30"],
|
|
362
|
+
expected_columns=["ROC_7", "ROC_1", "ROC_30", "MOM"],
|
|
338
363
|
functions=[compute_roc, compute_pandas_ta_roc],
|
|
339
364
|
)
|
|
340
365
|
CANDLESTOCK_PATTERNS = IndicatorFunction(
|
|
@@ -351,7 +376,7 @@ CANDLESTOCK_PATTERNS = IndicatorFunction(
|
|
|
351
376
|
)
|
|
352
377
|
|
|
353
378
|
SMA = IndicatorFunction(
|
|
354
|
-
expected_columns=["SMA_50", "SMA_200"],
|
|
379
|
+
expected_columns=["SMA_50", "SMA_200", "CLOSE"],
|
|
355
380
|
functions=[compute_sma, compute_pandas_ta_sma],
|
|
356
381
|
)
|
|
357
382
|
|
bullish/analysis/indicators.py
CHANGED
|
@@ -20,6 +20,7 @@ from bullish.analysis.functions import (
|
|
|
20
20
|
ADOSC,
|
|
21
21
|
PRICE,
|
|
22
22
|
compute_percentile_return_after_rsi_crossover,
|
|
23
|
+
momentum,
|
|
23
24
|
)
|
|
24
25
|
|
|
25
26
|
logger = logging.getLogger(__name__)
|
|
@@ -282,6 +283,13 @@ def indicators_factory() -> List[Indicator]:
|
|
|
282
283
|
type=Optional[date],
|
|
283
284
|
function=lambda d: cross(d.SMA_50, d.SMA_200, above=False),
|
|
284
285
|
),
|
|
286
|
+
Signal(
|
|
287
|
+
name="MOMENTUM_TIME_SPAN",
|
|
288
|
+
description="Momentum time span",
|
|
289
|
+
type_info="Overbought",
|
|
290
|
+
type=Optional[date],
|
|
291
|
+
function=lambda d: momentum(d),
|
|
292
|
+
),
|
|
285
293
|
],
|
|
286
294
|
),
|
|
287
295
|
Indicator(
|
|
@@ -334,7 +342,7 @@ def indicators_factory() -> List[Indicator]:
|
|
|
334
342
|
Indicator(
|
|
335
343
|
name="ROC",
|
|
336
344
|
description="Rate Of Change",
|
|
337
|
-
expected_columns=
|
|
345
|
+
expected_columns=ROC.expected_columns,
|
|
338
346
|
function=ROC.call,
|
|
339
347
|
signals=[
|
|
340
348
|
Signal(
|
|
@@ -379,6 +387,13 @@ def indicators_factory() -> List[Indicator]:
|
|
|
379
387
|
type=Optional[float],
|
|
380
388
|
function=lambda d: d.ROC_7.tolist()[-1],
|
|
381
389
|
),
|
|
390
|
+
Signal(
|
|
391
|
+
name="MOMENTUM",
|
|
392
|
+
type_info="Value",
|
|
393
|
+
description="7-day Rate of Change",
|
|
394
|
+
type=Optional[float],
|
|
395
|
+
function=lambda d: d.MOM.iloc[-1],
|
|
396
|
+
),
|
|
382
397
|
],
|
|
383
398
|
),
|
|
384
399
|
Indicator(
|
|
@@ -309,6 +309,75 @@ RSI_CROSSOVER_45_GROWTH_STOCK = NamedFilterQuery(
|
|
|
309
309
|
order_by_desc="market_capitalization",
|
|
310
310
|
country=["Germany", "United states", "France", "United kingdom", "Canada", "Japan"],
|
|
311
311
|
)
|
|
312
|
+
MOMENTUM_STOCK_STRONG_FUNDAMENTAL = NamedFilterQuery(
|
|
313
|
+
name="Momentum stock strong fundamental",
|
|
314
|
+
income=[
|
|
315
|
+
"positive_operating_income",
|
|
316
|
+
"growing_operating_income",
|
|
317
|
+
"positive_net_income",
|
|
318
|
+
"growing_net_income",
|
|
319
|
+
],
|
|
320
|
+
cash_flow=["positive_free_cash_flow"],
|
|
321
|
+
properties=["operating_cash_flow_is_higher_than_net_income"],
|
|
322
|
+
price_per_earning_ratio=[10, 400],
|
|
323
|
+
last_price=[1, 70],
|
|
324
|
+
order_by_desc="momentum",
|
|
325
|
+
country=["Germany", "United states", "France", "United kingdom", "Canada", "Japan"],
|
|
326
|
+
)
|
|
327
|
+
MOMENTUM_STOCK = NamedFilterQuery(
|
|
328
|
+
name="Momentum stock",
|
|
329
|
+
cash_flow=["positive_free_cash_flow"],
|
|
330
|
+
properties=["operating_cash_flow_is_higher_than_net_income"],
|
|
331
|
+
price_per_earning_ratio=[10, 400],
|
|
332
|
+
last_price=[1, 70],
|
|
333
|
+
order_by_desc="momentum",
|
|
334
|
+
country=["Germany", "United states", "France", "United kingdom", "Canada", "Japan"],
|
|
335
|
+
)
|
|
336
|
+
MOMENTUM_STOCK_NO_FUNDAMENTAL_CHECKS = NamedFilterQuery(
|
|
337
|
+
name="Momentum stock no fundamental checks",
|
|
338
|
+
price_per_earning_ratio=[10, 500],
|
|
339
|
+
last_price=[1, 10000],
|
|
340
|
+
order_by_desc="momentum",
|
|
341
|
+
country=["Germany", "United states", "France", "United kingdom", "Canada", "Japan"],
|
|
342
|
+
)
|
|
343
|
+
MOMENTUM_TIME_SPAN_1_MONTH = NamedFilterQuery(
|
|
344
|
+
name="Momentum 1 month",
|
|
345
|
+
price_per_earning_ratio=[10, 500],
|
|
346
|
+
last_price=[1, 10000],
|
|
347
|
+
momentum_time_span=[
|
|
348
|
+
datetime.date.today() - datetime.timedelta(days=90),
|
|
349
|
+
datetime.date.today() - datetime.timedelta(days=31),
|
|
350
|
+
],
|
|
351
|
+
macd_12_26_9_bullish_crossover=[
|
|
352
|
+
datetime.date.today() - datetime.timedelta(days=10),
|
|
353
|
+
datetime.date.today(),
|
|
354
|
+
],
|
|
355
|
+
order_by_desc="momentum",
|
|
356
|
+
country=["Germany", "United states", "France", "United kingdom", "Canada", "Japan"],
|
|
357
|
+
)
|
|
358
|
+
MOMENTUM_TIME_SPAN_1_MONTH_STRONG_FUNDAMENTALS = NamedFilterQuery(
|
|
359
|
+
name="Momentum 1 month strong fundamentals",
|
|
360
|
+
income=[
|
|
361
|
+
"positive_operating_income",
|
|
362
|
+
"growing_operating_income",
|
|
363
|
+
"positive_net_income",
|
|
364
|
+
"growing_net_income",
|
|
365
|
+
],
|
|
366
|
+
cash_flow=["positive_free_cash_flow"],
|
|
367
|
+
properties=["operating_cash_flow_is_higher_than_net_income"],
|
|
368
|
+
price_per_earning_ratio=[10, 500],
|
|
369
|
+
last_price=[1, 10000],
|
|
370
|
+
momentum_time_span=[
|
|
371
|
+
datetime.date.today() - datetime.timedelta(days=90),
|
|
372
|
+
datetime.date.today() - datetime.timedelta(days=31),
|
|
373
|
+
],
|
|
374
|
+
macd_12_26_9_bullish_crossover=[
|
|
375
|
+
datetime.date.today() - datetime.timedelta(days=10),
|
|
376
|
+
datetime.date.today(),
|
|
377
|
+
],
|
|
378
|
+
order_by_desc="momentum",
|
|
379
|
+
country=["Germany", "United states", "France", "United kingdom", "Canada", "Japan"],
|
|
380
|
+
)
|
|
312
381
|
|
|
313
382
|
|
|
314
383
|
def predefined_filters() -> list[NamedFilterQuery]:
|
|
@@ -319,6 +388,11 @@ def predefined_filters() -> list[NamedFilterQuery]:
|
|
|
319
388
|
RSI_CROSSOVER_30_GROWTH_STOCK,
|
|
320
389
|
RSI_CROSSOVER_40_GROWTH_STOCK,
|
|
321
390
|
RSI_CROSSOVER_45_GROWTH_STOCK,
|
|
391
|
+
MOMENTUM_STOCK_STRONG_FUNDAMENTAL,
|
|
392
|
+
MOMENTUM_STOCK,
|
|
393
|
+
MOMENTUM_STOCK_NO_FUNDAMENTAL_CHECKS,
|
|
394
|
+
MOMENTUM_TIME_SPAN_1_MONTH,
|
|
395
|
+
MOMENTUM_TIME_SPAN_1_MONTH_STRONG_FUNDAMENTALS,
|
|
322
396
|
]
|
|
323
397
|
|
|
324
398
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""
|
|
2
|
+
|
|
3
|
+
Revision ID: b76079e9845f
|
|
4
|
+
Revises: bf6b86dd5463
|
|
5
|
+
Create Date: 2025-07-12 21:32:08.865721
|
|
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 = "b76079e9845f"
|
|
17
|
+
down_revision: Union[str, None] = "bf6b86dd5463"
|
|
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("momentum_time_span", sa.Date(), nullable=True))
|
|
27
|
+
batch_op.create_index(
|
|
28
|
+
"ix_analysis_momentum_time_span", ["momentum_time_span"], unique=False
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# ### end Alembic commands ###
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def downgrade() -> None:
|
|
35
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
36
|
+
with op.batch_alter_table("analysis", schema=None) as batch_op:
|
|
37
|
+
batch_op.drop_index("ix_analysis_momentum_time_span")
|
|
38
|
+
batch_op.drop_column("momentum_time_span")
|
|
39
|
+
|
|
40
|
+
# ### end Alembic commands ###
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""
|
|
2
|
+
|
|
3
|
+
Revision ID: bf6b86dd5463
|
|
4
|
+
Revises: 17e51420e7ad
|
|
5
|
+
Create Date: 2025-07-11 18:32:21.450156
|
|
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 = "bf6b86dd5463"
|
|
17
|
+
down_revision: Union[str, None] = "17e51420e7ad"
|
|
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("momentum", sa.Float(), nullable=True))
|
|
27
|
+
batch_op.create_index("ix_analysis_momentum", ["momentum"], unique=False)
|
|
28
|
+
|
|
29
|
+
# ### end Alembic commands ###
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def downgrade() -> None:
|
|
33
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
34
|
+
with op.batch_alter_table("analysis", schema=None) as batch_op:
|
|
35
|
+
batch_op.drop_index("ix_analysis_momentum")
|
|
36
|
+
batch_op.drop_column("momentum")
|
|
37
|
+
|
|
38
|
+
# ### end Alembic commands ###
|
bullish/database/crud.py
CHANGED
|
@@ -67,6 +67,16 @@ class BullishDb(BearishDb, BullishDbBase): # type: ignore
|
|
|
67
67
|
session.exec(stmt) # type: ignore
|
|
68
68
|
session.commit()
|
|
69
69
|
|
|
70
|
+
def _write_many_analysis(self, many_analysis: List[Analysis]) -> None:
|
|
71
|
+
with Session(self._engine) as session:
|
|
72
|
+
stmt = (
|
|
73
|
+
insert(AnalysisORM)
|
|
74
|
+
.prefix_with("OR REPLACE")
|
|
75
|
+
.values([a.model_dump() for a in many_analysis])
|
|
76
|
+
)
|
|
77
|
+
session.exec(stmt) # type: ignore
|
|
78
|
+
session.commit()
|
|
79
|
+
|
|
70
80
|
def _read_analysis(self, ticker: Ticker) -> Optional[Analysis]:
|
|
71
81
|
with Session(self._engine) as session:
|
|
72
82
|
query = select(AnalysisORM).where(AnalysisORM.symbol == ticker.symbol)
|
bullish/interface/interface.py
CHANGED
|
@@ -19,6 +19,9 @@ class BullishDbBase(BearishDbBase): # type: ignore
|
|
|
19
19
|
def write_analysis(self, analysis: "Analysis") -> None:
|
|
20
20
|
return self._write_analysis(analysis)
|
|
21
21
|
|
|
22
|
+
def write_many_analysis(self, many_analysis: List["Analysis"]) -> None:
|
|
23
|
+
return self._write_many_analysis(many_analysis)
|
|
24
|
+
|
|
22
25
|
def read_analysis(self, ticker: Ticker) -> Optional["Analysis"]:
|
|
23
26
|
return self._read_analysis(ticker)
|
|
24
27
|
|
|
@@ -62,6 +65,9 @@ class BullishDbBase(BearishDbBase): # type: ignore
|
|
|
62
65
|
@abc.abstractmethod
|
|
63
66
|
def _write_analysis(self, analysis: "Analysis") -> None: ...
|
|
64
67
|
|
|
68
|
+
@abc.abstractmethod
|
|
69
|
+
def _write_many_analysis(self, many_analysis: List["Analysis"]) -> None: ...
|
|
70
|
+
|
|
65
71
|
@abc.abstractmethod
|
|
66
72
|
def _read_analysis(self, ticker: Ticker) -> Optional["Analysis"]: ...
|
|
67
73
|
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: bullishpy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.13.0
|
|
4
4
|
Summary:
|
|
5
5
|
Author: aan
|
|
6
6
|
Author-email: andoludovic.andriamamonjy@gmail.com
|
|
7
7
|
Requires-Python: >=3.12,<3.13
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
|
9
9
|
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
-
Requires-Dist: bearishpy (>=0.
|
|
10
|
+
Requires-Dist: bearishpy (>=0.22.0,<0.23.0)
|
|
11
11
|
Requires-Dist: click (>=7.0,<=8.1)
|
|
12
12
|
Requires-Dist: huey (>=2.5.3,<3.0.0)
|
|
13
|
+
Requires-Dist: joblib (>=1.5.1,<2.0.0)
|
|
13
14
|
Requires-Dist: pandas-ta (>=0.3.14b0,<0.4.0)
|
|
14
15
|
Requires-Dist: plotly (>=6.1.2,<7.0.0)
|
|
15
16
|
Requires-Dist: streamlit (>=1.45.1,<2.0.0)
|
|
@@ -1,10 +1,10 @@
|
|
|
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=v5MFGhQUpMwGAVE0dg2293ldRSgQmqy5CiF8MQFTibM,19165
|
|
4
4
|
bullish/analysis/filter.py,sha256=S8TuxoTAUY0U8ARPjNHE0tSSE_ToWkfZazAgnfgswk4,18136
|
|
5
|
-
bullish/analysis/functions.py,sha256=
|
|
6
|
-
bullish/analysis/indicators.py,sha256=
|
|
7
|
-
bullish/analysis/predefined_filters.py,sha256=
|
|
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
|
|
8
8
|
bullish/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
9
|
bullish/app/app.py,sha256=E0H78LOODl1H6s308jXpQGTUoFPoLOJkPBXOLQGLCeA,13331
|
|
10
10
|
bullish/cli.py,sha256=uYLZmGDAolZKWzduZ58bP-xul1adg0oKfeUQtZMXTvA,1958
|
|
@@ -20,10 +20,12 @@ bullish/database/alembic/versions/17e51420e7ad_.py,sha256=xeiVIm1YUZb08opE9rocHZ
|
|
|
20
20
|
bullish/database/alembic/versions/49c83f9eb5ac_.py,sha256=kCBItp7KmqpJ03roy5ikQjhefZia1oKgfZwournQDq8,3890
|
|
21
21
|
bullish/database/alembic/versions/4b0a2f40b7d3_.py,sha256=G0K7w7pOPYjPZkXTB8LWhxoxuWBPcPwOfnubTBtdeEY,1827
|
|
22
22
|
bullish/database/alembic/versions/73564b60fe24_.py,sha256=MTlDRDNHj3E9gK7IMeAzv2UxxxYtWiu3gI_9xTLE-wg,1008
|
|
23
|
+
bullish/database/alembic/versions/b76079e9845f_.py,sha256=W8eeTABjI9tT1dp3hlK7g7tiKqDhmA8AoUX9Sw-ykLI,1165
|
|
24
|
+
bullish/database/alembic/versions/bf6b86dd5463_.py,sha256=fKB8knCprGmiL6AEyFdhybVmB7QX_W4MPFF9sPzUrSM,1094
|
|
23
25
|
bullish/database/alembic/versions/d663166c531d_.py,sha256=U92l6QXqPniAYrPeu2Bt77ReDbXveLj4aGXtgd806JY,1915
|
|
24
26
|
bullish/database/alembic/versions/ee5baabb35f8_.py,sha256=nBMEY-_C8AsSXVPyaDdUkwrFFo2gxShzJhmrjejDwtc,1632
|
|
25
27
|
bullish/database/alembic/versions/fc191121f522_.py,sha256=0sstF6TpAJ09-Mt-Vek9SdSWksvi4C58a5D92rBtuY8,1894
|
|
26
|
-
bullish/database/crud.py,sha256=
|
|
28
|
+
bullish/database/crud.py,sha256=0AMrs07MMeDq5AVft_pf43KWPFOusMNzUmxuXwMX6K4,7400
|
|
27
29
|
bullish/database/schemas.py,sha256=bU-DW49NqpBp--1VN486LUdDmLeScrI8TF69afzjoTc,1507
|
|
28
30
|
bullish/database/scripts/create_revision.py,sha256=rggIf-3koPqJNth8FIg89EOfnIM7a9QrvL8X7UJsP0g,628
|
|
29
31
|
bullish/database/scripts/stamp.py,sha256=PWgVUEBumjNUMjTnGw46qmU3p221LeN-KspnW_gFuu4,839
|
|
@@ -33,14 +35,14 @@ bullish/exceptions.py,sha256=4z_i-dD-CDz1bkGmZH9DOf1L_awlCPCgdUDPF7dhWAI,106
|
|
|
33
35
|
bullish/figures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
36
|
bullish/figures/figures.py,sha256=SWTTiEoVyWMZeIIxg0ERi23v7s4tySB5BLKyPu12jC4,4193
|
|
35
37
|
bullish/interface/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
|
-
bullish/interface/interface.py,sha256
|
|
38
|
+
bullish/interface/interface.py,sha256=6HXSK-N-vOuOGVserizknP4CeUGbUhB8P642oZmgpk0,3252
|
|
37
39
|
bullish/jobs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
40
|
bullish/jobs/app.py,sha256=5MJ5KXUo7JSNAvOPgkpIMasD11VTrjQvGzM7vmCY65E,77
|
|
39
41
|
bullish/jobs/models.py,sha256=ndrGTMP08S57yGLGEG9TQt8Uw2slc4HvbG-TZtEEuN0,744
|
|
40
42
|
bullish/jobs/tasks.py,sha256=V_b0c8_GQC0-KIxaHDlLFhtkclQJOsck0gXaW6OlC_w,3055
|
|
41
43
|
bullish/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
42
44
|
bullish/utils/checks.py,sha256=Va10_xDVVnxYkOD2hafvyQ-TFV8FQpOkr4huJ7XgpDM,2188
|
|
43
|
-
bullishpy-0.
|
|
44
|
-
bullishpy-0.
|
|
45
|
-
bullishpy-0.
|
|
46
|
-
bullishpy-0.
|
|
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,,
|
|
File without changes
|
|
File without changes
|