bullishpy 0.69.0__py3-none-any.whl → 0.71.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.

@@ -499,7 +499,9 @@ class AnalysisView(BaseModel):
499
499
  upside: Optional[float] = None
500
500
  oai_high_price_target: Optional[float] = None
501
501
  oai_low_price_target: Optional[float] = None
502
+ rsi: Optional[float] = None
502
503
  oai_recommendation: Optional[str] = None
504
+ oai_moat: Optional[bool] = None
503
505
 
504
506
 
505
507
  def json_loads(value: Any) -> Any:
@@ -530,6 +532,7 @@ class SubjectAnalysis(BaseModel):
530
532
  ] = None
531
533
  summary: Annotated[Optional[Dict[str, Any]], BeforeValidator(json_loads)] = None
532
534
  upside: Optional[float] = None
535
+ downside: Optional[float] = None
533
536
 
534
537
  oai_high_price_target: Optional[float] = None
535
538
  oai_low_price_target: Optional[float] = None
@@ -537,11 +540,18 @@ class SubjectAnalysis(BaseModel):
537
540
  oai_recent_news: Optional[str] = None
538
541
  oai_recommendation: Optional[str] = None
539
542
  oai_explanation: Optional[str] = None
543
+ oai_moat: Optional[bool] = None
540
544
 
541
545
  def compute_upside(self, last_price: float) -> None:
542
- if self.high_price_target is not None:
546
+ if self.oai_high_price_target is not None:
543
547
  self.upside = (
544
- (float(self.high_price_target) - float(last_price))
548
+ (float(self.oai_high_price_target) - float(last_price))
549
+ * 100
550
+ / float(last_price)
551
+ )
552
+ if self.oai_low_price_target is not None:
553
+ self.downside = (
554
+ (float(last_price) - float(self.oai_low_price_target))
545
555
  * 100
546
556
  / float(last_price)
547
557
  )
@@ -4,7 +4,7 @@ from typing import Optional, Callable, cast
4
4
 
5
5
  import numpy as np
6
6
  import pandas as pd
7
- import pandas_ta as ta # type: ignore
7
+ import pandas_ta as ta
8
8
 
9
9
  from pydantic import BaseModel
10
10
 
@@ -18,7 +18,7 @@ except Exception:
18
18
  def cross_simple(
19
19
  series_a: pd.Series, series_b: pd.Series, above: bool = True
20
20
  ) -> pd.Series:
21
- crossing = ta.cross(series_a=series_a, series_b=series_b, above=above)
21
+ crossing = ta.cross(x=series_a, y=series_b, above=above) # type: ignore
22
22
  return crossing # type: ignore
23
23
 
24
24
 
@@ -466,6 +466,6 @@ def bollinger_bands(
466
466
  data: pd.DataFrame, window: int = 20, std_dev: float = 2.0
467
467
  ) -> pd.DataFrame:
468
468
  bbands = ta.bbands(
469
- data.close, timeperiod=window, nbdevup=std_dev, nbdevdn=std_dev, matype=0
469
+ data.close, timeperiod=window, nbdevup=std_dev, nbdevdn=std_dev, matype=0 # type: ignore
470
470
  )
471
- return bbands # type: ignore
471
+ return bbands
@@ -297,6 +297,13 @@ def indicators_factory() -> List[Indicator]:
297
297
  type=Optional[date],
298
298
  function=lambda d: (d.RSI < 60) & (d.RSI > 30),
299
299
  ),
300
+ Signal(
301
+ name="RSI",
302
+ description="RSI value",
303
+ type_info="Overbought",
304
+ type=Optional[float],
305
+ function=lambda d: d.RSI,
306
+ ),
300
307
  ],
301
308
  ),
302
309
  Indicator(
@@ -31,6 +31,7 @@ fences, no extra text:
31
31
  "recommendation": str, // One of: "Strong Buy", "Buy", "Hold", "Sell", "Strong Sell"
32
32
  "explanation": str // Concise explanation for the recommendation above, covering key pros/cons
33
33
  for investors
34
+ "moat": bool // Give as a boolean true or false if the company has a strong economic moat
34
35
  }}
35
36
 
36
37
  Formatting rules:
@@ -49,6 +50,7 @@ class OpenAINews(BaseModel):
49
50
  recent_news: Optional[str] = None
50
51
  recommendation: Optional[str] = None
51
52
  explanation: Optional[str] = None
53
+ moat: Optional[bool] = None
52
54
 
53
55
  def valid(self) -> bool:
54
56
  return bool(
bullish/app/app.py CHANGED
@@ -123,7 +123,7 @@ def dialog_pick_database() -> None:
123
123
  f"The database {db_path} has not the necessary data to run this application. "
124
124
  "A backround job will be started to update the data."
125
125
  )
126
- analysis(db_path)
126
+ analysis(db_path, "Update analysis")
127
127
  st.rerun()
128
128
  if event is None:
129
129
  st.stop()
@@ -0,0 +1,48 @@
1
+ """
2
+
3
+ Revision ID: 65662e214031
4
+ Revises: 660897c02c00
5
+ Create Date: 2025-08-20 17:30:47.973725
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 = "65662e214031"
17
+ down_revision: Union[str, None] = "660897c02c00"
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("downside", sa.Float(), nullable=True))
27
+ batch_op.add_column(sa.Column("oai_moat", sa.Boolean(), nullable=True))
28
+ batch_op.create_index("ix_analysis_downside", ["downside"], unique=False)
29
+ batch_op.create_index("ix_analysis_oai_moat", ["oai_moat"], unique=False)
30
+
31
+ with op.batch_alter_table("openai", schema=None) as batch_op:
32
+ batch_op.add_column(sa.Column("moat", sa.Boolean(), nullable=True))
33
+
34
+ # ### end Alembic commands ###
35
+
36
+
37
+ def downgrade() -> None:
38
+ # ### commands auto generated by Alembic - please adjust! ###
39
+ with op.batch_alter_table("openai", schema=None) as batch_op:
40
+ batch_op.drop_column("moat")
41
+
42
+ with op.batch_alter_table("analysis", schema=None) as batch_op:
43
+ batch_op.drop_index("ix_analysis_oai_moat")
44
+ batch_op.drop_index("ix_analysis_downside")
45
+ batch_op.drop_column("oai_moat")
46
+ batch_op.drop_column("downside")
47
+
48
+ # ### end Alembic commands ###
@@ -0,0 +1,36 @@
1
+ """
2
+
3
+ Revision ID: 660897c02c00
4
+ Revises: c828e29e1105
5
+ Create Date: 2025-08-20 17:19:05.423318
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 = "660897c02c00"
17
+ down_revision: Union[str, None] = "c828e29e1105"
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("rsi", sa.Float(), nullable=True))
27
+ batch_op.create_index("ix_analysis_rsi", ["rsi"], 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_rsi")
36
+ batch_op.drop_column("rsi")
bullish/database/crud.py CHANGED
@@ -74,7 +74,11 @@ class BullishDb(BearishDb, BullishDbBase): # type: ignore
74
74
  logger.info(
75
75
  "Running tickermood upgrade to create the subject table in the database."
76
76
  )
77
- tickermood_upgrade(database_url=database_url, no_migration=True)
77
+ try:
78
+ tickermood_upgrade(database_url=database_url, no_migration=True)
79
+ except Exception as e:
80
+ logger.error(f"failed to update database: {e}")
81
+ print(f"failed to update database: {e}")
78
82
  return engine
79
83
 
80
84
  def model_post_init(self, __context: Any) -> None:
@@ -389,6 +393,7 @@ class BullishDb(BearishDb, BullishDbBase): # type: ignore
389
393
  "oai_explanation": row_dict_oai.get("explanation"),
390
394
  "oai_high_price_target": row_dict_oai.get("high_price_target"),
391
395
  "oai_low_price_target": row_dict_oai.get("low_price_target"),
396
+ "oai_moat": row_dict_oai.get("moat"),
392
397
  }
393
398
 
394
399
  return SubjectAnalysis.model_validate(row_dict)
bullish/jobs/tasks.py CHANGED
@@ -195,22 +195,20 @@ def news(
195
195
  logger.debug(
196
196
  f"extracting news for {symbol} subject: {subject.model_dump()}"
197
197
  )
198
- bullish_db.update_analysis(
199
- symbol,
200
- subject.model_dump(
201
- exclude_none=True,
202
- exclude_unset=True,
203
- exclude_defaults=True,
204
- exclude={"symbol"},
205
- ),
206
- )
207
- base_news(
208
- database_path=database_path,
209
- job_type=job_type,
210
- symbols=symbols,
211
- headless=headless,
212
- task=task,
213
- )
198
+ try:
199
+ bullish_db.update_analysis(
200
+ symbol,
201
+ subject.model_dump(
202
+ exclude_none=True,
203
+ exclude_unset=True,
204
+ exclude_defaults=True,
205
+ exclude={"symbol"},
206
+ ),
207
+ )
208
+ except Exception as e:
209
+ logger.error(f"failed to extract news for {symbol}: {e}")
210
+ print(f"failed to extract news for {symbol}: {e}")
211
+ continue
214
212
 
215
213
 
216
214
  @huey.periodic_task(crontab(minute="0", hour="8"), context=True) # type: ignore
@@ -1,17 +1,18 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: bullishpy
3
- Version: 0.69.0
3
+ Version: 0.71.0
4
4
  Summary:
5
+ License-File: LICENSE
5
6
  Author: aan
6
7
  Author-email: andoludovic.andriamamonjy@gmail.com
7
8
  Requires-Python: >=3.12,<3.13
8
9
  Classifier: Programming Language :: Python :: 3
9
10
  Classifier: Programming Language :: Python :: 3.12
10
- Requires-Dist: bearishpy (>=0.26.0,<0.27.0)
11
+ Requires-Dist: bearishpy (>=0.29.0,<0.30.0)
11
12
  Requires-Dist: click (>=7.0,<=8.1)
12
13
  Requires-Dist: huey (>=2.5.3,<3.0.0)
13
14
  Requires-Dist: joblib (>=1.5.1,<2.0.0)
14
- Requires-Dist: pandas-ta (>=0.3.14b0,<0.4.0)
15
+ Requires-Dist: pandas-ta (>=0.4.71b0,<0.5.0)
15
16
  Requires-Dist: plotly (>=4.12.0,<6.0.0)
16
17
  Requires-Dist: streamlit (>=1.45.1,<2.0.0)
17
18
  Requires-Dist: streamlit-file-browser (>=3.2.22,<4.0.0)
@@ -1,16 +1,16 @@
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=rFpd0-CnQ_OMdFQ-O9BjgzyO6vZQMYZJGwa18PLZ9fo,24758
3
+ bullish/analysis/analysis.py,sha256=3lZSLpTe4lFBGNrCpXmYBZhwD7o0rXSbQEkLpxJLylA,25130
4
4
  bullish/analysis/backtest.py,sha256=x91ek5kOzJHvYq0TmJh1Q8wBDDduIaieE0zDaoZFXew,14325
5
5
  bullish/analysis/constants.py,sha256=j3vQwjGhY-4dEEV-TkeKMDUTo2GM7M97Hcpi19LDcFQ,11458
6
6
  bullish/analysis/filter.py,sha256=VvQALnYNyYylXkorYR3oGhsF4L_sAUSE7-aop4Trp9o,9326
7
- bullish/analysis/functions.py,sha256=lrbPvTo3GLtylDCfeIKoXCKF5gaY5QFFToNqtuj7xhI,15794
8
- bullish/analysis/indicators.py,sha256=kdjDVhIFiDBhezJJg9ifGheMC6oCR0gC87d_FiW_tjI,28183
7
+ bullish/analysis/functions.py,sha256=jw1Tc-YtoyobYhC6AWJH-xXgaczwDZMTfQIES6Y_8qM,15780
8
+ bullish/analysis/indicators.py,sha256=CcDu8mu1jOOS5-3gNHYA9qDA3Ua-6PGUyoio2bDIe48,28435
9
9
  bullish/analysis/industry_views.py,sha256=-B4CCAYz2arGQtWTXLLMpox0loO_MGdVQd2ycCRMOQQ,6799
10
- bullish/analysis/openai.py,sha256=oJHa827HVXRV0yRTXz5Iivg2wchaGY2bjx8vEXBf0MI,3308
10
+ bullish/analysis/openai.py,sha256=Fw7A8lFMgSEQFA48Q9GjVpEC3oiBgSHUFi7YO5rzhAc,3444
11
11
  bullish/analysis/predefined_filters.py,sha256=E65qrTSaDFuUxoaeZ8D72K5AobumobpQdpcTIF308D4,14053
12
12
  bullish/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- bullish/app/app.py,sha256=dnTzlyKrG2XTKDeLnJwfdvIf24eXM8fd29bvJWmMr8k,17190
13
+ bullish/app/app.py,sha256=33miXieMVabmJFq8IrQazAzPZtmO3SnT-UFOuFe3QAc,17209
14
14
  bullish/cli.py,sha256=yYqiEQAvOIQ-pTn77RPuE449gwaEGBeQwNHHAJ5yQDM,2739
15
15
  bullish/database/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  bullish/database/alembic/README,sha256=heMzebYwlGhnE8_4CWJ4LS74WoEZjBy-S-mIJRxAEKI,39
@@ -28,6 +28,8 @@ bullish/database/alembic/versions/49c83f9eb5ac_.py,sha256=kCBItp7KmqpJ03roy5ikQj
28
28
  bullish/database/alembic/versions/4b0a2f40b7d3_.py,sha256=G0K7w7pOPYjPZkXTB8LWhxoxuWBPcPwOfnubTBtdeEY,1827
29
29
  bullish/database/alembic/versions/4ee82b171449_.py,sha256=QtPy5VyZPyZxS7MVkk_wGi3C44PVDoHyJ-9m9fWdqqc,1047
30
30
  bullish/database/alembic/versions/5b10ee7604c1_.py,sha256=YlqaagPasR3RKASv7acME1jPS8p26VoTE2BvpOwdCpY,1463
31
+ bullish/database/alembic/versions/65662e214031_.py,sha256=Yq3lOW6liYTYiBaPRcFqVjn3k5z1mWIUXT17bv9ZroY,1596
32
+ bullish/database/alembic/versions/660897c02c00_.py,sha256=Sc_4uJAGheebijw3WzFNHclcWz0YF8vaZKEmVBwglDc,1033
31
33
  bullish/database/alembic/versions/6d252e23f543_.py,sha256=izF-ejdXk733INkAokGqjA2U_M0_c1f_ruihZ-cgP7s,1525
32
34
  bullish/database/alembic/versions/73564b60fe24_.py,sha256=MTlDRDNHj3E9gK7IMeAzv2UxxxYtWiu3gI_9xTLE-wg,1008
33
35
  bullish/database/alembic/versions/79bc71ec6f9e_.py,sha256=4nShut2NEd1F3piSckIIBtke0GEsFAxYw5TZl5YYRzc,1140
@@ -43,7 +45,7 @@ bullish/database/alembic/versions/ec25c8fa449f_.py,sha256=8Yts74KEjK4jg20zIo90_0
43
45
  bullish/database/alembic/versions/ee5baabb35f8_.py,sha256=nBMEY-_C8AsSXVPyaDdUkwrFFo2gxShzJhmrjejDwtc,1632
44
46
  bullish/database/alembic/versions/fc191121f522_.py,sha256=0sstF6TpAJ09-Mt-Vek9SdSWksvi4C58a5D92rBtuY8,1894
45
47
  bullish/database/alembic/versions/ff0cc4ba40ec_.py,sha256=74lxga54ig_LoNZYK9toJL9iRwGbNRezh1zvO1YI40U,2719
46
- bullish/database/crud.py,sha256=5fcnOQftnf1YtRA_ndcKOihyO0sREnu_vLyC-_6Quik,15888
48
+ bullish/database/crud.py,sha256=-pncRg_YA5y2wE2HELJHiGbeTzmaGF7LjMC8be10qwA,16123
47
49
  bullish/database/schemas.py,sha256=HudFJ9lsIkVaEYjQUWammrsDnYSmEe4hOCbim3dN_4A,3946
48
50
  bullish/database/scripts/create_revision.py,sha256=rggIf-3koPqJNth8FIg89EOfnIM7a9QrvL8X7UJsP0g,628
49
51
  bullish/database/scripts/stamp.py,sha256=PWgVUEBumjNUMjTnGw46qmU3p221LeN-KspnW_gFuu4,839
@@ -57,11 +59,11 @@ bullish/interface/interface.py,sha256=6uZAY19WNtDRKdOitqzqMEo6JTep2M3HC8iFUKYntH
57
59
  bullish/jobs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
60
  bullish/jobs/app.py,sha256=5MJ5KXUo7JSNAvOPgkpIMasD11VTrjQvGzM7vmCY65E,77
59
61
  bullish/jobs/models.py,sha256=rBXxtGFBpgZprrxq5_X2Df-bh8BLYEfw-VLMRucrqa8,784
60
- bullish/jobs/tasks.py,sha256=13oK53fBXd5pjMLLMeOoQ4vG-yH-6dfrodhb9KzAYVw,7230
62
+ bullish/jobs/tasks.py,sha256=QFEZbWZQNSzSW_vZzwC5cU5CSs7je1J8jJOoWFB5FRU,7348
61
63
  bullish/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
62
64
  bullish/utils/checks.py,sha256=g-5QXNWNe1_BwHKrc2PtvPiLraL0tqGgxnzG7u-Wkgo,2189
63
- bullishpy-0.69.0.dist-info/LICENSE,sha256=nYb7AJFegu6ndlQhbbk54MjT-GH-0x9RF6Ls-ggJ_g4,1075
64
- bullishpy-0.69.0.dist-info/METADATA,sha256=FJkU1aBY8oPHMG58l5FDh9wtxuiSy-36P8U0o88dCXA,3009
65
- bullishpy-0.69.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
66
- bullishpy-0.69.0.dist-info/entry_points.txt,sha256=eaPpmL6vmSBFo0FBtwibCXGqAW4LFJ83whJzT1VjD-0,43
67
- bullishpy-0.69.0.dist-info/RECORD,,
65
+ bullishpy-0.71.0.dist-info/METADATA,sha256=_zUkXcLDEJ-OAMSzn3zImMNC4-6rBmG7mv45ztSyNGM,3031
66
+ bullishpy-0.71.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
67
+ bullishpy-0.71.0.dist-info/entry_points.txt,sha256=eaPpmL6vmSBFo0FBtwibCXGqAW4LFJ83whJzT1VjD-0,43
68
+ bullishpy-0.71.0.dist-info/licenses/LICENSE,sha256=nYb7AJFegu6ndlQhbbk54MjT-GH-0x9RF6Ls-ggJ_g4,1075
69
+ bullishpy-0.71.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.1.3
2
+ Generator: poetry-core 2.2.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any