bullishpy 0.63.0__tar.gz → 0.64.0__tar.gz

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.

Files changed (62) hide show
  1. {bullishpy-0.63.0 → bullishpy-0.64.0}/PKG-INFO +2 -2
  2. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/analysis/filter.py +1 -0
  3. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/analysis/indicators.py +2 -2
  4. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/analysis/predefined_filters.py +93 -91
  5. {bullishpy-0.63.0 → bullishpy-0.64.0}/pyproject.toml +2 -2
  6. {bullishpy-0.63.0 → bullishpy-0.64.0}/LICENSE +0 -0
  7. {bullishpy-0.63.0 → bullishpy-0.64.0}/README.md +0 -0
  8. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/__init__.py +0 -0
  9. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/analysis/__init__.py +0 -0
  10. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/analysis/analysis.py +0 -0
  11. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/analysis/backtest.py +0 -0
  12. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/analysis/constants.py +0 -0
  13. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/analysis/functions.py +0 -0
  14. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/analysis/industry_views.py +0 -0
  15. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/app/__init__.py +0 -0
  16. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/app/app.py +0 -0
  17. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/cli.py +0 -0
  18. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/__init__.py +0 -0
  19. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/alembic/README +0 -0
  20. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/alembic/alembic.ini +0 -0
  21. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/alembic/env.py +0 -0
  22. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/alembic/script.py.mako +0 -0
  23. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/alembic/versions/037dbd721317_.py +0 -0
  24. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/alembic/versions/040b15fba458_.py +0 -0
  25. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/alembic/versions/08ac1116e055_.py +0 -0
  26. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/alembic/versions/11d35a452b40_.py +0 -0
  27. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/alembic/versions/12889a2cbd7d_.py +0 -0
  28. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/alembic/versions/17e51420e7ad_.py +0 -0
  29. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/alembic/versions/260fcff7212e_.py +0 -0
  30. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/alembic/versions/49c83f9eb5ac_.py +0 -0
  31. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/alembic/versions/4b0a2f40b7d3_.py +0 -0
  32. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/alembic/versions/4ee82b171449_.py +0 -0
  33. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/alembic/versions/5b10ee7604c1_.py +0 -0
  34. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/alembic/versions/6d252e23f543_.py +0 -0
  35. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/alembic/versions/73564b60fe24_.py +0 -0
  36. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/alembic/versions/79bc71ec6f9e_.py +0 -0
  37. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/alembic/versions/ae444f338124_.py +0 -0
  38. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/alembic/versions/b76079e9845f_.py +0 -0
  39. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/alembic/versions/bf6b86dd5463_.py +0 -0
  40. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/alembic/versions/d0e58e050845_.py +0 -0
  41. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/alembic/versions/d663166c531d_.py +0 -0
  42. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/alembic/versions/ec25c8fa449f_.py +0 -0
  43. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/alembic/versions/ee5baabb35f8_.py +0 -0
  44. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/alembic/versions/fc191121f522_.py +0 -0
  45. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/alembic/versions/ff0cc4ba40ec_.py +0 -0
  46. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/crud.py +0 -0
  47. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/schemas.py +0 -0
  48. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/scripts/create_revision.py +0 -0
  49. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/scripts/stamp.py +0 -0
  50. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/scripts/upgrade.py +0 -0
  51. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/database/settings.py +0 -0
  52. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/exceptions.py +0 -0
  53. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/figures/__init__.py +0 -0
  54. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/figures/figures.py +0 -0
  55. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/interface/__init__.py +0 -0
  56. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/interface/interface.py +0 -0
  57. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/jobs/__init__.py +0 -0
  58. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/jobs/app.py +0 -0
  59. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/jobs/models.py +0 -0
  60. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/jobs/tasks.py +0 -0
  61. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/utils/__init__.py +0 -0
  62. {bullishpy-0.63.0 → bullishpy-0.64.0}/bullish/utils/checks.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bullishpy
3
- Version: 0.63.0
3
+ Version: 0.64.0
4
4
  Summary:
5
5
  Author: aan
6
6
  Author-email: andoludovic.andriamamonjy@gmail.com
@@ -17,7 +17,7 @@ Requires-Dist: streamlit (>=1.45.1,<2.0.0)
17
17
  Requires-Dist: streamlit-file-browser (>=3.2.22,<4.0.0)
18
18
  Requires-Dist: streamlit-pydantic (>=v0.6.1-rc.3,<0.7.0)
19
19
  Requires-Dist: ta-lib (>=0.6.4,<0.7.0)
20
- Requires-Dist: tickermood (>=0.27.0,<0.28.0)
20
+ Requires-Dist: tickermood (>=0.28.0,<0.29.0)
21
21
  Requires-Dist: vectorbt (>=0.28.0,<0.29.0)
22
22
  Description-Content-Type: text/markdown
23
23
 
@@ -178,6 +178,7 @@ class GeneralFilter(BaseModel):
178
178
  )
179
179
  market_capitalization: Optional[List[float]] = Field(default=[5e8, 1e12])
180
180
  price_per_earning_ratio: Optional[List[float]] = Field(default=[0.0, 1000.0])
181
+ last_price: Optional[List[float]] = Field(default=[0.0, 100000.0])
181
182
 
182
183
 
183
184
  class FilterQuery(GeneralFilter, *TechnicalAnalysisFilters, *FundamentalAnalysisFilters): # type: ignore
@@ -266,7 +266,7 @@ def indicators_factory() -> List[Indicator]:
266
266
  description="RSI Oversold Signal",
267
267
  type_info="Oversold",
268
268
  type=Optional[date],
269
- function=lambda d: (d.RSI < 30) & (d.RSI > 0),
269
+ function=lambda d: (d.RSI <= 30) & (d.RSI > 0),
270
270
  in_use_backtest=True,
271
271
  ),
272
272
  Signal(
@@ -281,7 +281,7 @@ def indicators_factory() -> List[Indicator]:
281
281
  description="RSI Neutral Signal",
282
282
  type_info="Overbought",
283
283
  type=Optional[date],
284
- function=lambda d: (d.RSI < 60) & (d.RSI > 40),
284
+ function=lambda d: (d.RSI < 60) & (d.RSI > 30),
285
285
  ),
286
286
  ],
287
287
  ),
@@ -3,7 +3,7 @@ import json
3
3
  import os
4
4
  from datetime import timedelta
5
5
  from pathlib import Path
6
- from typing import Dict, Any, Optional, List, Union, get_args
6
+ from typing import Dict, Any, Optional, List, Union, get_args, Tuple
7
7
 
8
8
  from bullish.analysis.analysis import AnalysisView
9
9
  from bullish.analysis.backtest import (
@@ -30,6 +30,10 @@ DATE_THRESHOLD = [
30
30
  ]
31
31
 
32
32
 
33
+ def _get_variants(variants: List[str]) -> List[Tuple[str, ...]]:
34
+ return [tuple(variants[:i]) for i in range(1, len(variants) + 1)]
35
+
36
+
33
37
  class NamedFilterQuery(FilterQuery):
34
38
  name: str
35
39
  description: Optional[str] = None
@@ -157,57 +161,29 @@ class NamedFilterQuery(FilterQuery):
157
161
  }
158
162
  return self._custom_variant("Poor Performers", properties)
159
163
 
160
- def short_term_profitability(self) -> "NamedFilterQuery":
164
+ def fundamentals(self) -> "NamedFilterQuery":
161
165
  properties = {
162
166
  "income": [
163
167
  "positive_operating_income",
164
168
  "positive_net_income",
165
- "quarterly_positive_operating_income",
166
- "quarterly_positive_net_income",
167
- ],
168
- "cash_flow": [
169
- "positive_free_cash_flow",
170
- "quarterly_positive_free_cash_flow",
169
+ "growing_net_income",
170
+ "growing_operating_income",
171
171
  ],
172
+ "cash_flow": ["positive_free_cash_flow", "growing_operating_cash_flow"],
172
173
  "eps": [
174
+ "growing_basic_eps",
175
+ "growing_diluted_eps",
173
176
  "positive_basic_eps",
174
177
  "positive_diluted_eps",
175
- "quarterly_positive_basic_eps",
176
- "quarterly_positive_diluted_eps",
177
178
  ],
178
179
  "properties": [
179
180
  "positive_return_on_assets",
180
181
  "positive_return_on_equity",
181
182
  "positive_debt_to_equity",
182
183
  "operating_cash_flow_is_higher_than_net_income",
183
- "quarterly_positive_return_on_assets",
184
- "quarterly_positive_return_on_equity",
185
- "quarterly_positive_debt_to_equity",
186
- "quarterly_operating_cash_flow_is_higher_than_net_income",
187
184
  ],
188
185
  }
189
- return self._custom_variant("Short-term profitability", properties)
190
-
191
- def long_term_profitability(self) -> "NamedFilterQuery":
192
- properties = {
193
- "income": [
194
- "growing_net_income",
195
- "growing_operating_income",
196
- "quarterly_growing_net_income",
197
- "quarterly_growing_operating_income",
198
- ],
199
- "cash_flow": [
200
- "growing_operating_cash_flow",
201
- "quarterly_growing_operating_cash_flow",
202
- ],
203
- "eps": [
204
- "growing_basic_eps",
205
- "growing_diluted_eps",
206
- "quarterly_growing_basic_eps",
207
- "quarterly_growing_diluted_eps",
208
- ],
209
- }
210
- return self._custom_variant("Long-term profitability", properties)
186
+ return self._custom_variant("Fundamentals", properties)
211
187
 
212
188
  def high_growth(self) -> "NamedFilterQuery":
213
189
  properties = {"industry": list(get_args(HighGrowthIndustry))}
@@ -217,48 +193,57 @@ class NamedFilterQuery(FilterQuery):
217
193
  properties = {"industry": list(get_args(DefensiveIndustries))}
218
194
  return self._custom_variant("Defensive", properties)
219
195
 
220
- def variants(self) -> List["NamedFilterQuery"]:
221
- variants_ = [
222
- self.country_variant("Europe", list(get_args(Europe))),
223
- self.country_variant("Us", list(get_args(Us))),
224
- self.country_variant("Europe", list(get_args(Europe))).top_performers(),
225
- self.country_variant("Us", list(get_args(Us))).top_performers(),
226
- self.country_variant("Europe", list(get_args(Europe))).poor_performers(),
227
- self.country_variant("Us", list(get_args(Us))).poor_performers(),
228
- self.country_variant("Europe", list(get_args(Europe)))
229
- .update_indicator_filter("RSI 30", "rsi_bullish_crossover_30")
230
- .update_indicator_filter("MACD", "macd_12_26_9_bullish_crossover"),
231
- self.country_variant("Europe", list(get_args(Europe)))
232
- .update_indicator_filter("RSI 40", "rsi_bullish_crossover_40")
233
- .update_indicator_filter("MACD", "macd_12_26_9_bullish_crossover"),
234
- self.country_variant("Europe", list(get_args(Europe)))
235
- .update_indicator_filter("RSI Neutral", "rsi_neutral")
236
- .update_indicator_filter("MACD", "macd_12_26_9_bullish_crossover"),
237
- self.country_variant("Us", list(get_args(Us)))
238
- .update_indicator_filter("RSI 30", "rsi_bullish_crossover_30")
239
- .update_indicator_filter("MACD", "macd_12_26_9_bullish_crossover"),
240
- self.country_variant("Us", list(get_args(Us)))
241
- .update_indicator_filter("RSI 40", "rsi_bullish_crossover_40")
242
- .update_indicator_filter("MACD", "macd_12_26_9_bullish_crossover"),
243
- self.country_variant("Us", list(get_args(Us)))
244
- .update_indicator_filter("RSI Neutral", "rsi_neutral")
245
- .update_indicator_filter("MACD", "macd_12_26_9_bullish_crossover"),
246
- ]
247
- variants_short_term_profitability = [
248
- v.short_term_profitability() for v in variants_
249
- ]
250
- variants_long_term_profitability = [
251
- v.long_term_profitability() for v in variants_
252
- ]
253
- variants_growth = [v.high_growth() for v in variants_]
254
- variants_defensive = [v.defensive() for v in variants_]
255
- return [
256
- *variants_,
257
- *variants_short_term_profitability,
258
- *variants_long_term_profitability,
259
- *variants_growth,
260
- *variants_defensive,
261
- ]
196
+ def cheap(self) -> "NamedFilterQuery":
197
+ properties = {"last_price": [1, 30]}
198
+ return self._custom_variant("Cheap", properties)
199
+
200
+ def europe(self) -> "NamedFilterQuery":
201
+ return self.country_variant("Europe", list(get_args(Europe)))
202
+
203
+ def us(self) -> "NamedFilterQuery":
204
+ return self.country_variant("Us", list(get_args(Us)))
205
+
206
+ def rsi_30(self) -> "NamedFilterQuery":
207
+ return self.update_indicator_filter("RSI 30", "rsi_bullish_crossover_30")
208
+
209
+ def rsi_40(self) -> "NamedFilterQuery":
210
+ return self.update_indicator_filter("RSI 40", "rsi_bullish_crossover_40")
211
+
212
+ def macd(self) -> "NamedFilterQuery":
213
+ return self.update_indicator_filter("MACD", "macd_12_26_9_bullish_crossover")
214
+
215
+ def rsi_neutral_(self) -> "NamedFilterQuery":
216
+ return self.update_indicator_filter("RSI Neutral", "rsi_neutral")
217
+
218
+ def rsi_oversold_(self) -> "NamedFilterQuery":
219
+ return self.update_indicator_filter("RSI Oversold", "rsi_oversold")
220
+
221
+ def earnings_date(self) -> "NamedFilterQuery":
222
+ return NamedFilterQuery.model_validate(
223
+ self.model_dump()
224
+ | {
225
+ "name": f"{self.name} (Earnings Date)",
226
+ "next_earnings_date": [
227
+ datetime.date.today(),
228
+ datetime.date.today() + timedelta(days=20),
229
+ ],
230
+ }
231
+ )
232
+
233
+ def variants(
234
+ self, variants: Optional[List[List[str]]] = None
235
+ ) -> List["NamedFilterQuery"]:
236
+ variants = variants or [["europe"], ["us"]]
237
+
238
+ _variants = {v for variant in variants for v in _get_variants(variant)}
239
+ filters = []
240
+ for attributes in _variants:
241
+ filter = self
242
+ for attr in attributes:
243
+ filter = getattr(filter, attr)()
244
+ filters.append(filter)
245
+
246
+ return filters
262
247
 
263
248
 
264
249
  def load_custom_filters() -> List[NamedFilterQuery]:
@@ -282,28 +267,46 @@ SMALL_CAP = NamedFilterQuery(
282
267
  properties=["positive_debt_to_equity"],
283
268
  average_volume_30=[50000, 5e9],
284
269
  order_by_desc="market_capitalization",
285
- ).variants()
270
+ ).variants(
271
+ variants=[
272
+ ["europe", "top_performers", "fundamentals"],
273
+ ["us", "top_performers", "fundamentals"],
274
+ ["europe", "earnings_date"],
275
+ ["us", "earnings_date"],
276
+ ]
277
+ )
286
278
 
287
279
  LARGE_CAPS = NamedFilterQuery(
288
280
  name="Large Cap",
289
281
  order_by_desc="market_capitalization",
290
282
  market_capitalization=[1e10, 1e14],
291
- ).variants()
283
+ ).variants(
284
+ variants=[
285
+ ["europe", "rsi_oversold_", "macd", "fundamentals"],
286
+ ["us", "rsi_oversold_", "macd", "fundamentals"],
287
+ ["europe", "rsi_neutral_", "macd", "fundamentals"],
288
+ ["us", "rsi_neutral_", "macd", "fundamentals"],
289
+ ["europe", "rsi_30", "macd", "fundamentals"],
290
+ ["us", "rsi_30", "macd", "fundamentals"],
291
+ ["europe", "top_performers", "cheap"],
292
+ ["us", "top_performers", "cheap"],
293
+ ["europe", "earnings_date"],
294
+ ["us", "earnings_date"],
295
+ ]
296
+ )
292
297
 
293
298
  MID_CAPS = NamedFilterQuery(
294
299
  name="Mid Cap",
295
300
  order_by_desc="market_capitalization",
296
301
  market_capitalization=[5e8, 1e10],
297
- ).variants()
298
-
299
- NEXT_EARNINGS_DATE = NamedFilterQuery(
300
- name="Next Earnings date",
301
- order_by_desc="market_capitalization",
302
- next_earnings_date=[
303
- datetime.date.today(),
304
- datetime.date.today() + timedelta(days=20),
305
- ],
306
- ).variants()
302
+ ).variants(
303
+ variants=[
304
+ ["europe", "top_performers", "fundamentals"],
305
+ ["us", "top_performers", "fundamentals"],
306
+ ["europe", "earnings_date"],
307
+ ["us", "earnings_date"],
308
+ ]
309
+ )
307
310
 
308
311
 
309
312
  def predefined_filters() -> list[NamedFilterQuery]:
@@ -312,7 +315,6 @@ def predefined_filters() -> list[NamedFilterQuery]:
312
315
  *SMALL_CAP,
313
316
  *MID_CAPS,
314
317
  *LARGE_CAPS,
315
- *NEXT_EARNINGS_DATE,
316
318
  ]
317
319
 
318
320
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "bullishpy"
3
- version = "0.63.0"
3
+ version = "0.64.0"
4
4
  description = ""
5
5
  authors = ["aan <andoludovic.andriamamonjy@gmail.com>"]
6
6
  readme = "README.md"
@@ -8,7 +8,7 @@ packages = [{ include = "bullish" }]
8
8
  [tool.poetry.dependencies]
9
9
  python = ">=3.12,<3.13"
10
10
  bearishpy = "^0.26.0"
11
- tickermood = "^0.27.0"
11
+ tickermood = "^0.28.0"
12
12
  streamlit = "^1.45.1"
13
13
  streamlit-pydantic = "^v0.6.1-rc.3"
14
14
  streamlit-file-browser = "^3.2.22"
File without changes
File without changes
File without changes