bullishpy 0.10.0__tar.gz → 0.11.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 (45) hide show
  1. {bullishpy-0.10.0 → bullishpy-0.11.0}/PKG-INFO +1 -1
  2. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/analysis/predefined_filters.py +132 -15
  3. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/app/app.py +20 -6
  4. {bullishpy-0.10.0 → bullishpy-0.11.0}/pyproject.toml +1 -1
  5. {bullishpy-0.10.0 → bullishpy-0.11.0}/README.md +0 -0
  6. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/__init__.py +0 -0
  7. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/analysis/__init__.py +0 -0
  8. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/analysis/analysis.py +0 -0
  9. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/analysis/filter.py +0 -0
  10. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/analysis/functions.py +0 -0
  11. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/analysis/indicators.py +0 -0
  12. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/app/__init__.py +0 -0
  13. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/cli.py +0 -0
  14. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/database/__init__.py +0 -0
  15. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/database/alembic/README +0 -0
  16. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/database/alembic/alembic.ini +0 -0
  17. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/database/alembic/env.py +0 -0
  18. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/database/alembic/script.py.mako +0 -0
  19. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/database/alembic/versions/037dbd721317_.py +0 -0
  20. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/database/alembic/versions/08ac1116e055_.py +0 -0
  21. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/database/alembic/versions/11d35a452b40_.py +0 -0
  22. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/database/alembic/versions/17e51420e7ad_.py +0 -0
  23. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/database/alembic/versions/49c83f9eb5ac_.py +0 -0
  24. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/database/alembic/versions/4b0a2f40b7d3_.py +0 -0
  25. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/database/alembic/versions/73564b60fe24_.py +0 -0
  26. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/database/alembic/versions/d663166c531d_.py +0 -0
  27. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/database/alembic/versions/ee5baabb35f8_.py +0 -0
  28. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/database/alembic/versions/fc191121f522_.py +0 -0
  29. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/database/crud.py +0 -0
  30. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/database/schemas.py +0 -0
  31. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/database/scripts/create_revision.py +0 -0
  32. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/database/scripts/stamp.py +0 -0
  33. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/database/scripts/upgrade.py +0 -0
  34. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/database/settings.py +0 -0
  35. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/exceptions.py +0 -0
  36. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/figures/__init__.py +0 -0
  37. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/figures/figures.py +0 -0
  38. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/interface/__init__.py +0 -0
  39. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/interface/interface.py +0 -0
  40. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/jobs/__init__.py +0 -0
  41. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/jobs/app.py +0 -0
  42. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/jobs/models.py +0 -0
  43. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/jobs/tasks.py +0 -0
  44. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/utils/__init__.py +0 -0
  45. {bullishpy-0.10.0 → bullishpy-0.11.0}/bullish/utils/checks.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bullishpy
3
- Version: 0.10.0
3
+ Version: 0.11.0
4
4
  Summary:
5
5
  Author: aan
6
6
  Author-email: andoludovic.andriamamonjy@gmail.com
@@ -70,15 +70,61 @@ SHOOTING_STARS = NamedFilterQuery(
70
70
  order_by_asc="last_price",
71
71
  )
72
72
 
73
- RSI_CROSSOVER = NamedFilterQuery(
73
+ RSI_CROSSOVER_TECH = NamedFilterQuery(
74
74
  name="RSI cross-over",
75
75
  cash_flow=["positive_free_cash_flow"],
76
76
  properties=["operating_cash_flow_is_higher_than_net_income"],
77
77
  return_after_rsi_crossover_45_period_90=[0.0, 100],
78
78
  rsi_bullish_crossover_45=DATE_THRESHOLD,
79
- market_capitalization=[1e9, 1e12], # 1 billion to 1 trillion
79
+ market_capitalization=[5e8, 1e11], # 1 billion to 1 trillion
80
+ order_by_desc="market_capitalization",
81
+ country=["Germany", "United states", "France", "United kingdom", "Canada", "Japan"],
82
+ industry=[
83
+ "Semiconductors",
84
+ "Software - Application",
85
+ "Software - Infrastructure",
86
+ "Biotechnology",
87
+ "Diagnostics & Research",
88
+ "Medical Devices",
89
+ "Health Information Services",
90
+ "Internet Retail",
91
+ "Electronic Gaming & Multimedia",
92
+ "Internet Content & Information",
93
+ "Solar",
94
+ "Information Technology Services",
95
+ "Scientific & Technical Instruments",
96
+ "Semiconductor Equipment & Materials",
97
+ "Diagnostics & Research",
98
+ ],
99
+ )
100
+ RSI_CROSSOVER_TECH_PE = NamedFilterQuery(
101
+ name="RSI cross-over P/E",
102
+ cash_flow=["positive_free_cash_flow"],
103
+ properties=["operating_cash_flow_is_higher_than_net_income"],
104
+ price_per_earning_ratio=[5, 30], # P/E ratio between 10 and 100
105
+ rsi_bullish_crossover_45=DATE_THRESHOLD,
106
+ market_capitalization=[5e8, 1e12], # 1 billion to 1 trillion
80
107
  order_by_desc="market_capitalization",
108
+ country=["Germany", "United states", "France", "United kingdom", "Canada", "Japan"],
109
+ industry=[
110
+ "Semiconductors",
111
+ "Software - Application",
112
+ "Software - Infrastructure",
113
+ "Biotechnology",
114
+ "Diagnostics & Research",
115
+ "Medical Devices",
116
+ "Health Information Services",
117
+ "Internet Retail",
118
+ "Electronic Gaming & Multimedia",
119
+ "Internet Content & Information",
120
+ "Solar",
121
+ "Information Technology Services",
122
+ "Scientific & Technical Instruments",
123
+ "Semiconductor Equipment & Materials",
124
+ "Diagnostics & Research",
125
+ ],
81
126
  )
127
+
82
128
  MICRO_CAP_EVENT_SPECULATION = NamedFilterQuery(
83
129
  name="Micro-Cap Event Speculation",
84
130
  description="seeks tiny names where unusual volume and price gaps hint at "
@@ -185,23 +231,94 @@ OVERSOLD_MEAN_REVERSION = NamedFilterQuery(
185
231
  mfi_oversold=DATE_THRESHOLD,
186
232
  lower_than_200_day_high=DATE_THRESHOLD,
187
233
  )
234
+ RSI_CROSSOVER_30_GROWTH_STOCK_STRONG_FUNDAMENTAL = NamedFilterQuery(
235
+ name="RSI cross-over 30 growth stock strong fundamental",
236
+ income=[
237
+ "positive_operating_income",
238
+ "growing_operating_income",
239
+ "positive_net_income",
240
+ "growing_net_income",
241
+ ],
242
+ cash_flow=["positive_free_cash_flow"],
243
+ properties=["operating_cash_flow_is_higher_than_net_income"],
244
+ price_per_earning_ratio=[20, 40],
245
+ rsi_bullish_crossover_30=DATE_THRESHOLD,
246
+ market_capitalization=[5e8, 1e12],
247
+ order_by_desc="market_capitalization",
248
+ country=["Germany", "United states", "France", "United kingdom", "Canada", "Japan"],
249
+ )
250
+ RSI_CROSSOVER_40_GROWTH_STOCK_STRONG_FUNDAMENTAL = NamedFilterQuery(
251
+ name="RSI cross-over 40 growth stock strong fundamental",
252
+ income=[
253
+ "positive_operating_income",
254
+ "growing_operating_income",
255
+ "positive_net_income",
256
+ "growing_net_income",
257
+ ],
258
+ cash_flow=["positive_free_cash_flow"],
259
+ properties=["operating_cash_flow_is_higher_than_net_income"],
260
+ price_per_earning_ratio=[20, 40],
261
+ rsi_bullish_crossover_40=DATE_THRESHOLD,
262
+ market_capitalization=[5e8, 1e12],
263
+ order_by_desc="market_capitalization",
264
+ country=["Germany", "United states", "France", "United kingdom", "Canada", "Japan"],
265
+ )
266
+ RSI_CROSSOVER_45_GROWTH_STOCK_STRONG_FUNDAMENTAL = NamedFilterQuery(
267
+ name="RSI cross-over 45 growth stock strong fundamental",
268
+ income=[
269
+ "positive_operating_income",
270
+ "growing_operating_income",
271
+ "positive_net_income",
272
+ "growing_net_income",
273
+ ],
274
+ cash_flow=["positive_free_cash_flow"],
275
+ properties=["operating_cash_flow_is_higher_than_net_income"],
276
+ price_per_earning_ratio=[20, 40],
277
+ rsi_bullish_crossover_45=DATE_THRESHOLD,
278
+ market_capitalization=[5e8, 1e12],
279
+ order_by_desc="market_capitalization",
280
+ country=["Germany", "United states", "France", "United kingdom", "Canada", "Japan"],
281
+ )
282
+ RSI_CROSSOVER_30_GROWTH_STOCK = NamedFilterQuery(
283
+ name="RSI cross-over 30 growth stock",
284
+ cash_flow=["positive_free_cash_flow"],
285
+ properties=["operating_cash_flow_is_higher_than_net_income"],
286
+ price_per_earning_ratio=[20, 40],
287
+ rsi_bullish_crossover_30=DATE_THRESHOLD,
288
+ market_capitalization=[5e8, 1e12],
289
+ order_by_desc="market_capitalization",
290
+ country=["Germany", "United states", "France", "United kingdom", "Canada", "Japan"],
291
+ )
292
+ RSI_CROSSOVER_40_GROWTH_STOCK = NamedFilterQuery(
293
+ name="RSI cross-over 40 growth stock",
294
+ cash_flow=["positive_free_cash_flow"],
295
+ properties=["operating_cash_flow_is_higher_than_net_income"],
296
+ price_per_earning_ratio=[20, 40],
297
+ rsi_bullish_crossover_40=DATE_THRESHOLD,
298
+ market_capitalization=[5e8, 1e12],
299
+ order_by_desc="market_capitalization",
300
+ country=["Germany", "United states", "France", "United kingdom", "Canada", "Japan"],
301
+ )
302
+ RSI_CROSSOVER_45_GROWTH_STOCK = NamedFilterQuery(
303
+ name="RSI cross-over 45 growth stock",
304
+ cash_flow=["positive_free_cash_flow"],
305
+ properties=["operating_cash_flow_is_higher_than_net_income"],
306
+ price_per_earning_ratio=[20, 40],
307
+ rsi_bullish_crossover_45=DATE_THRESHOLD,
308
+ market_capitalization=[5e8, 1e12],
309
+ order_by_desc="market_capitalization",
310
+ country=["Germany", "United states", "France", "United kingdom", "Canada", "Japan"],
311
+ )
188
312
 
189
313
 
190
314
  def predefined_filters() -> list[NamedFilterQuery]:
191
315
  return [
192
- STRONG_FUNDAMENTALS,
193
- GOOD_FUNDAMENTALS,
194
- MICRO_CAP_EVENT_SPECULATION,
195
- MOMENTUM_BREAKOUT_HUNTER,
196
- DEEP_VALUE_PLUS_CATALYST,
197
- END_OF_TREND_REVERSAL,
198
- HIGH_QUALITY_CASH_GENERATOR,
199
- EARNINGS_ACCELERATION_TREND_CONFIRMATION,
200
- DIVIDEND_GROWTH_COMPOUNDER,
201
- BREAK_OUT_MOMENTUM,
202
- OVERSOLD_MEAN_REVERSION,
203
- SHOOTING_STARS,
204
- RSI_CROSSOVER,
316
+ RSI_CROSSOVER_30_GROWTH_STOCK_STRONG_FUNDAMENTAL,
317
+ RSI_CROSSOVER_40_GROWTH_STOCK_STRONG_FUNDAMENTAL,
318
+ RSI_CROSSOVER_45_GROWTH_STOCK_STRONG_FUNDAMENTAL,
319
+ RSI_CROSSOVER_30_GROWTH_STOCK,
320
+ RSI_CROSSOVER_40_GROWTH_STOCK,
321
+ RSI_CROSSOVER_45_GROWTH_STOCK,
205
322
  ]
206
323
 
207
324
 
@@ -1,3 +1,4 @@
1
+ import logging
1
2
  import shelve
2
3
  import uuid
3
4
  from pathlib import Path
@@ -37,6 +38,7 @@ CACHE_SHELVE = "user_cache"
37
38
  DB_KEY = "db_path"
38
39
 
39
40
  st.set_page_config(layout="wide")
41
+ logger = logging.getLogger(__name__)
40
42
 
41
43
 
42
44
  @st.cache_resource
@@ -146,10 +148,11 @@ def build_filter(model: Type[BaseModel], data: Dict[str, Any]) -> Dict[str, Any]
146
148
  key=hash((model.__name__, field)),
147
149
  )
148
150
  elif info.annotation == Optional[str]: # type: ignore
151
+ options = ["", *groups_mapping()[field]]
149
152
  data[field] = st.selectbox(
150
153
  name,
151
- ["", *groups_mapping()[field]],
152
- index=0 if not default else groups_mapping()[field].index(default),
154
+ options,
155
+ index=0 if not default else options.index(default),
153
156
  key=hash((model.__name__, field)),
154
157
  )
155
158
 
@@ -162,11 +165,22 @@ def build_filter(model: Type[BaseModel], data: Dict[str, Any]) -> Dict[str, Any]
162
165
  (item.le for item in info.metadata if hasattr(item, "le")),
163
166
  info.default[1] if info.default and len(info.default) == 2 else None,
164
167
  )
165
- data[field] = list(
166
- st.slider( # type: ignore
167
- name, ge, le, tuple(default), key=hash((model.__name__, field))
168
+ if info.annotation == Optional[List[float]]: # type: ignore
169
+ ge = int(ge) # type: ignore
170
+ le = int(le) # type: ignore
171
+ default = [int(d) for d in default]
172
+ try:
173
+ data[field] = list(
174
+ st.slider( # type: ignore
175
+ name, ge, le, tuple(default), key=hash((model.__name__, field))
176
+ )
168
177
  )
169
- )
178
+ except Exception as e:
179
+ logger.error(
180
+ f"Error building filter for {model.__name__}.{field} "
181
+ f"with the parameters {(info.annotation, name, ge, le, tuple(default))}: {e}"
182
+ )
183
+ raise e
170
184
  return data
171
185
 
172
186
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "bullishpy"
3
- version = "0.10.0"
3
+ version = "0.11.0"
4
4
  description = ""
5
5
  authors = ["aan <andoludovic.andriamamonjy@gmail.com>"]
6
6
  readme = "README.md"
File without changes
File without changes