bullishpy 0.4.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,96 @@
1
+ import abc
2
+ import logging
3
+ from typing import List, Optional
4
+
5
+ import pandas as pd
6
+ from bearish.interface.interface import BearishDbBase # type: ignore
7
+ from bearish.models.base import Ticker # type: ignore
8
+
9
+
10
+ from bullish.analysis.analysis import Analysis, AnalysisView
11
+ from bullish.analysis.filter import FilterQuery, FilteredResults
12
+ from bullish.jobs.models import JobTracker, JobTrackerStatus, add_icons
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class BullishDbBase(BearishDbBase): # type: ignore
18
+ def write_analysis(self, analysis: "Analysis") -> None:
19
+ return self._write_analysis(analysis)
20
+
21
+ def read_analysis(self, ticker: Ticker) -> Optional["Analysis"]:
22
+ return self._read_analysis(ticker)
23
+
24
+ def read_filter_query(self, query: FilterQuery) -> pd.DataFrame:
25
+
26
+ if not set(query.query_parameters).issubset(set(Analysis.model_fields)):
27
+ raise ValueError(
28
+ f"Query parameters {query.query_parameters} are not a "
29
+ f"subset of Analysis model fields {Analysis.model_fields}"
30
+ )
31
+ query_ = query.to_query()
32
+ fields = ",".join(list(AnalysisView.model_fields))
33
+ query_str: str = f"""
34
+ SELECT {fields} FROM analysis WHERE {query_} LIMIT 1000
35
+ """ # noqa: S608
36
+ return self._read_filter_query(query_str)
37
+
38
+ def read_analysis_data(
39
+ self, columns: Optional[List[str]] = None, symbols: Optional[List[str]] = None
40
+ ) -> pd.DataFrame:
41
+ columns = columns or list(AnalysisView.model_fields)
42
+ data = self._read_analysis_data(columns, symbols=symbols)
43
+ if set(data.columns) != set(columns):
44
+ raise ValueError(
45
+ f"Expected columns {columns}, but got {data.columns.tolist()}"
46
+ )
47
+ return data
48
+
49
+ def read_job_trackers(self) -> pd.DataFrame:
50
+ return add_icons(self._read_job_trackers())
51
+
52
+ @abc.abstractmethod
53
+ def _read_job_trackers(self) -> pd.DataFrame:
54
+ ...
55
+
56
+ @abc.abstractmethod
57
+ def write_job_tracker(self, job_tracker: JobTracker) -> None:
58
+ ...
59
+
60
+ @abc.abstractmethod
61
+ def delete_job_trackers(self, job_ids: List[str]) -> None:
62
+ ...
63
+
64
+ @abc.abstractmethod
65
+ def update_job_tracker_status(self, job_tracker_status: JobTrackerStatus) -> None:
66
+ ...
67
+
68
+ @abc.abstractmethod
69
+ def _write_analysis(self, analysis: "Analysis") -> None:
70
+ ...
71
+
72
+ @abc.abstractmethod
73
+ def _read_analysis(self, ticker: Ticker) -> Optional["Analysis"]:
74
+ ...
75
+
76
+ @abc.abstractmethod
77
+ def _read_filter_query(self, query: str) -> pd.DataFrame:
78
+ ...
79
+
80
+ @abc.abstractmethod
81
+ def _read_analysis_data(
82
+ self, columns: List[str], symbols: Optional[List[str]] = None
83
+ ) -> pd.DataFrame:
84
+ ...
85
+
86
+ @abc.abstractmethod
87
+ def read_filtered_results(self, name: str) -> Optional[FilteredResults]:
88
+ ...
89
+
90
+ @abc.abstractmethod
91
+ def read_list_filtered_results(self) -> List[str]:
92
+ ...
93
+
94
+ @abc.abstractmethod
95
+ def write_filtered_results(self, filtered_results: FilteredResults) -> None:
96
+ ...
File without changes
bullish/jobs/app.py ADDED
@@ -0,0 +1,3 @@
1
+ from huey import SqliteHuey # type: ignore
2
+
3
+ huey = SqliteHuey(name="queue")
bullish/jobs/models.py ADDED
@@ -0,0 +1,25 @@
1
+ from datetime import datetime
2
+ from typing import Literal, get_args
3
+
4
+ import pandas as pd
5
+ from pydantic import BaseModel, Field
6
+
7
+ JobType = Literal["Update data", "Update analysis", "Fetching news"]
8
+ JobStatus = Literal["Completed", "Failed", "Running", "Started"]
9
+ StatusIcon = ["✅ Completed", "❌ Failed", "🔄 Running", "🚀 Started"]
10
+
11
+
12
+ class JobTrackerStatus(BaseModel):
13
+ job_id: str
14
+ status: JobStatus = "Started"
15
+
16
+
17
+ class JobTracker(JobTrackerStatus):
18
+ type: JobType
19
+ started_at: datetime = Field(default_factory=datetime.now)
20
+
21
+
22
+ def add_icons(data: pd.DataFrame) -> pd.DataFrame:
23
+ status_map = dict(zip(list(get_args(JobStatus)), StatusIcon, strict=True))
24
+ data["status"] = data["status"].map(status_map)
25
+ return data
bullish/jobs/tasks.py ADDED
@@ -0,0 +1,77 @@
1
+ import functools
2
+ import logging
3
+ from typing import Optional, Any, Callable, List
4
+
5
+ from bearish.main import Bearish # type: ignore
6
+ from tickermood.main import get_news # type: ignore
7
+ from tickermood.types import DatabaseConfig # type: ignore
8
+
9
+ from .app import huey
10
+ from pathlib import Path
11
+ from huey.api import Task # type: ignore
12
+
13
+ from .models import JobTrackerStatus
14
+ from ..analysis.analysis import run_analysis
15
+ from ..database.crud import BullishDb
16
+ from bullish.analysis.filter import FilterUpdate
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ def job_tracker(func: Callable[..., Any]) -> Callable[..., Any]:
22
+ @functools.wraps(func)
23
+ def wrapper(
24
+ database_path: Path, *args: Any, task: Optional[Task] = None, **kwargs: Any
25
+ ) -> None:
26
+ bullish_db = BullishDb(database_path=database_path)
27
+ if task is None:
28
+ raise ValueError("Task must be provided for job tracking.")
29
+ bullish_db.update_job_tracker_status(
30
+ JobTrackerStatus(job_id=task.id, status="Running")
31
+ )
32
+ try:
33
+ func(database_path, *args, task=task, **kwargs)
34
+ bullish_db.update_job_tracker_status(
35
+ JobTrackerStatus(job_id=task.id, status="Completed")
36
+ )
37
+ except Exception as e:
38
+ logger.exception(f"Fail to complete job {func.__name__}: {e}")
39
+ bullish_db.update_job_tracker_status(
40
+ JobTrackerStatus(job_id=task.id, status="Failed")
41
+ )
42
+
43
+ return wrapper
44
+
45
+
46
+ @huey.task(context=True) # type: ignore
47
+ @job_tracker
48
+ def update(
49
+ database_path: Path,
50
+ symbols: List[str],
51
+ update_query: FilterUpdate,
52
+ task: Optional[Task] = None,
53
+ ) -> None:
54
+ logger.debug(f"Running update task for {len(symbols)} tickers.")
55
+ if not update_query.update_analysis_only:
56
+ bearish = Bearish(path=database_path, auto_migration=False)
57
+ bearish.update_prices(
58
+ symbols,
59
+ series_length=update_query.window_size,
60
+ delay=update_query.data_age_in_days,
61
+ )
62
+ if update_query.update_financials:
63
+ bearish.update_financials(symbols)
64
+ bullish_db = BullishDb(database_path=database_path)
65
+ run_analysis(bullish_db)
66
+
67
+
68
+ @huey.task(context=True) # type: ignore
69
+ @job_tracker
70
+ def news(
71
+ database_path: Path,
72
+ symbols: List[str],
73
+ headless: bool = True,
74
+ task: Optional[Task] = None,
75
+ ) -> None:
76
+ database_config = DatabaseConfig(database_path=database_path, no_migration=True)
77
+ get_news(symbols, database_config, headless=headless)
@@ -0,0 +1,22 @@
1
+ Metadata-Version: 2.3
2
+ Name: bullishpy
3
+ Version: 0.4.0
4
+ Summary:
5
+ Author: aan
6
+ Author-email: andoludovic.andriamamonjy@gmail.com
7
+ Requires-Python: >=3.10,<3.13
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Requires-Dist: bearishpy (>=0.19.0,<0.20.0)
13
+ Requires-Dist: huey (>=2.5.3,<3.0.0)
14
+ Requires-Dist: pandas-ta (>=0.3.14b0,<0.4.0)
15
+ Requires-Dist: plotly (>=6.1.2,<7.0.0)
16
+ Requires-Dist: streamlit (>=1.45.1,<2.0.0)
17
+ Requires-Dist: streamlit-file-browser (>=3.2.22,<4.0.0)
18
+ Requires-Dist: streamlit-pydantic (>=v0.6.1-rc.3,<0.7.0)
19
+ Requires-Dist: tickermood (>=0.4.0,<0.5.0)
20
+ Description-Content-Type: text/markdown
21
+
22
+ ## Bullish
@@ -0,0 +1,34 @@
1
+ bullish/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ bullish/analysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ bullish/analysis/analysis.py,sha256=JqaFzWfZ_AxF-7qa7BPmSWESnrcVdSEJKmUMRFGHzew,22168
4
+ bullish/analysis/filter.py,sha256=rNqGGTIlaOq8Gw8mblX09bQOfR98by-NY8VY2TwP5oQ,3783
5
+ bullish/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ bullish/app/app.py,sha256=Ef9IfrOJelV5_oymPFtEhyW438LDw0mlK6EhZR9Yqq8,7656
7
+ bullish/cli.py,sha256=mxobC_49Zpn_qgXThVGhtpexjKLJS2tZZbOlo_wMLfg,1893
8
+ bullish/database/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ bullish/database/alembic/README,sha256=heMzebYwlGhnE8_4CWJ4LS74WoEZjBy-S-mIJRxAEKI,39
10
+ bullish/database/alembic/alembic.ini,sha256=VuwqBJV5ObTyyRNrqv8Xr-TDIRfqPjP9R1mqewYM_xE,3695
11
+ bullish/database/alembic/env.py,sha256=TBsN4TyVppyc2QpWqViebd4-xxUT7Cs3GDYXQdKiAMs,2260
12
+ bullish/database/alembic/script.py.mako,sha256=MEqL-2qATlST9TAOeYgscMn1uy6HUS9NFvDgl93dMj8,635
13
+ bullish/database/alembic/versions/037dbd721317_.py,sha256=pbmfCcJBFwy0SH42lfYVXawDlM72bR0oUUO-0RgFmUM,8594
14
+ bullish/database/alembic/versions/4b0a2f40b7d3_.py,sha256=iyLvZY1Wiy7NucqxPi0TCAYD0Q8zmuFweMRZsAYx6o4,1826
15
+ bullish/database/alembic/versions/73564b60fe24_.py,sha256=zp9Hvr8e3FWc9SLNqiQpbA8lsd0cU8DKDScKQdQwHYA,1007
16
+ bullish/database/crud.py,sha256=xUSSzhHGp-KKBlG4iCNVtAEs9U5BNv4VhGSrLIR4fmM,5611
17
+ bullish/database/schemas.py,sha256=FjFhCa_VijAu2EEWsQ6rShSRzdH6FS9IM66ibh2kx3g,1104
18
+ bullish/database/scripts/create_revision.py,sha256=rggIf-3koPqJNth8FIg89EOfnIM7a9QrvL8X7UJsP0g,628
19
+ bullish/database/scripts/stamp.py,sha256=PWgVUEBumjNUMjTnGw46qmU3p221LeN-KspnW_gFuu4,839
20
+ bullish/database/scripts/upgrade.py,sha256=-Gz7aFNPEt9y9e1kltqXE76-j_8QeNtet_VlwY5AWjo,806
21
+ bullish/database/settings.py,sha256=nMudufmF7iC_62_PHrGSMjlqDLN2I0qTbtz9JKZHSko,164
22
+ bullish/exceptions.py,sha256=4z_i-dD-CDz1bkGmZH9DOf1L_awlCPCgdUDPF7dhWAI,106
23
+ bullish/figures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
+ bullish/figures/figures.py,sha256=3Ifrnl4I7gq6DiMEWZs2P0589nEsaGKYNiq-Cxj35_g,2771
25
+ bullish/interface/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
+ bullish/interface/interface.py,sha256=1LiTFaMI1WXgUHLf73_KPiYKbv4RFcctyPxIcvzkkf0,3082
27
+ bullish/jobs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
+ bullish/jobs/app.py,sha256=5MJ5KXUo7JSNAvOPgkpIMasD11VTrjQvGzM7vmCY65E,77
29
+ bullish/jobs/models.py,sha256=ndrGTMP08S57yGLGEG9TQt8Uw2slc4HvbG-TZtEEuN0,744
30
+ bullish/jobs/tasks.py,sha256=vRjbCBcQciTC9283El_ji7BKxx40IbLcJkMbMVoE5wA,2533
31
+ bullishpy-0.4.0.dist-info/METADATA,sha256=EVIlGsSkq8hAovop7OSQ_tNy2csZzF6QysWCrwXElNg,772
32
+ bullishpy-0.4.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
33
+ bullishpy-0.4.0.dist-info/entry_points.txt,sha256=eaPpmL6vmSBFo0FBtwibCXGqAW4LFJ83whJzT1VjD-0,43
34
+ bullishpy-0.4.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 2.1.3
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ bullish=bullish.cli:app
3
+