bullishpy 0.12.0__py3-none-any.whl → 0.14.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.

@@ -1,6 +1,6 @@
1
1
  import datetime
2
2
  from datetime import date
3
- from typing import Literal, get_args, Any, Optional, List, Tuple, Type, Dict
3
+ from typing import get_args, Any, Optional, List, Tuple, Type, Dict
4
4
 
5
5
  from bearish.types import SeriesLength # type: ignore
6
6
  from pydantic import BaseModel, Field, ConfigDict
@@ -14,411 +14,8 @@ from bullish.analysis.analysis import (
14
14
  TechnicalAnalysis,
15
15
  AnalysisView,
16
16
  )
17
+ from bullish.analysis.constants import Industry, IndustryGroup, Sector, Country
17
18
 
18
- Industry = Literal[
19
- "Publishing",
20
- "Internet Retail",
21
- "Scientific & Technical Instruments",
22
- "Engineering & Construction",
23
- "Diagnostics & Research",
24
- "Software - Infrastructure",
25
- "Thermal Coal",
26
- "Software - Application",
27
- "Auto Manufacturers",
28
- "Farm Products",
29
- "Medical Devices",
30
- "Education & Training Services",
31
- "Auto Parts",
32
- "Specialty Chemicals",
33
- "Marine Shipping",
34
- "Biotechnology",
35
- "Real Estate Services",
36
- "Gold",
37
- "Entertainment",
38
- "Specialty Retail",
39
- "Utilities - Independent Power Producers",
40
- "Steel",
41
- "Mortgage Finance",
42
- "Communication Equipment",
43
- "Drug Manufacturers - Specialty & Generic",
44
- "Electronic Gaming & Multimedia",
45
- "Banks - Regional",
46
- "Oil & Gas E&P",
47
- "Travel Services",
48
- "Real Estate - Diversified",
49
- "Telecom Services",
50
- "Uranium",
51
- "Consulting Services",
52
- "Waste Management",
53
- "Agricultural Inputs",
54
- "Utilities - Diversified",
55
- "Auto & Truck Dealerships",
56
- "Confectioners",
57
- "Other Industrial Metals & Mining",
58
- "Beverages - Wineries & Distilleries",
59
- "Oil & Gas Midstream",
60
- "Recreational Vehicles",
61
- "Electrical Equipment & Parts",
62
- "Household & Personal Products",
63
- "Packaging & Containers",
64
- "REIT - Specialty",
65
- "Home Improvement Retail",
66
- "Electronic Components",
67
- "Asset Management",
68
- "Consumer Electronics",
69
- "Conglomerates",
70
- "Health Information Services",
71
- "Medical Instruments & Supplies",
72
- "Building Products & Equipment",
73
- "Information Technology Services",
74
- "Specialty Industrial Machinery",
75
- "Food Distribution",
76
- "Packaged Foods",
77
- "Rental & Leasing Services",
78
- "Medical Distribution",
79
- "Grocery Stores",
80
- "Advertising Agencies",
81
- "Beverages - Non - Alcoholic",
82
- "Apparel Manufacturing",
83
- "Oil & Gas Equipment & Services",
84
- "Coking Coal",
85
- "Industrial Distribution",
86
- "Restaurants",
87
- "Beverages - Brewers",
88
- "Chemicals",
89
- "Real Estate - Development",
90
- "Credit Services",
91
- "Tobacco",
92
- "Metal Fabrication",
93
- "Building Materials",
94
- "Residential Construction",
95
- "Specialty Business Services",
96
- "REIT - Hotel & Motel",
97
- "Internet Content & Information",
98
- "Lodging",
99
- "Furnishings, Fixtures & Appliances",
100
- "Airlines",
101
- "Computer Hardware",
102
- "Integrated Freight & Logistics",
103
- "Solar",
104
- "Capital Markets",
105
- "Leisure",
106
- "Airports & Air Services",
107
- "Aluminum",
108
- "Insurance Brokers",
109
- "Semiconductors",
110
- "REIT - Retail",
111
- "Luxury Goods",
112
- "Lumber & Wood Production",
113
- "REIT - Mortgage",
114
- "Semiconductor Equipment & Materials",
115
- "Aerospace & Defense",
116
- "Security & Protection Services",
117
- "Utilities - Renewable",
118
- "Utilities - Regulated Gas",
119
- "Apparel Retail",
120
- "Pollution & Treatment Controls",
121
- "Broadcasting",
122
- "Resorts & Casinos",
123
- "Other Precious Metals & Mining",
124
- "Financial Data & Stock Exchanges",
125
- "Footwear & Accessories",
126
- "Medical Care Facilities",
127
- "Electronics & Computer Distribution",
128
- "Gambling",
129
- "Tools & Accessories",
130
- "Insurance - Property & Casualty",
131
- "Utilities - Regulated Water",
132
- "Insurance - Specialty",
133
- "Personal Services",
134
- "Pharmaceutical Retailers",
135
- "Farm & Heavy Construction Machinery",
136
- "Utilities - Regulated Electric",
137
- "Department Stores",
138
- "Staffing & Employment Services",
139
- "Textile Manufacturing",
140
- "Silver",
141
- "REIT - Industrial",
142
- "REIT - Diversified",
143
- "Copper",
144
- "Business Equipment & Supplies",
145
- "Infrastructure Operations",
146
- "Trucking",
147
- "Insurance - Reinsurance",
148
- "Insurance - Diversified",
149
- "Drug Manufacturers - General",
150
- "Oil & Gas Drilling",
151
- "Banks - Diversified",
152
- "REIT - Residential",
153
- "Oil & Gas Refining & Marketing",
154
- "Shell Companies",
155
- "Financial Conglomerates",
156
- "Paper & Paper Products",
157
- "Insurance - Life",
158
- "REIT - Office",
159
- "Railroads",
160
- "Oil & Gas Integrated",
161
- "Healthcare Plans",
162
- "REIT - Healthcare Facilities",
163
- "Discount Stores",
164
- ]
165
-
166
- IndustryGroup = Literal[
167
- "publishing",
168
- "internet-retail",
169
- "scientific-technical-instruments",
170
- "engineering-construction",
171
- "diagnostics-research",
172
- "software-infrastructure",
173
- "thermal-coal",
174
- "software-application",
175
- "auto-manufacturers",
176
- "farm-products",
177
- "medical-devices",
178
- "education-training-services",
179
- "auto-parts",
180
- "specialty-chemicals",
181
- "marine-shipping",
182
- "biotechnology",
183
- "real-estate-services",
184
- "gold",
185
- "entertainment",
186
- "specialty-retail",
187
- "utilities-independent-power-producers",
188
- "steel",
189
- "mortgage-finance",
190
- "communication-equipment",
191
- "drug-manufacturers-specialty-generic",
192
- "electronic-gaming-multimedia",
193
- "banks-regional",
194
- "oil-gas-e-p",
195
- "travel-services",
196
- "real-estate-diversified",
197
- "telecom-services",
198
- "uranium",
199
- "consulting-services",
200
- "waste-management",
201
- "agricultural-inputs",
202
- "utilities-diversified",
203
- "auto-truck-dealerships",
204
- "confectioners",
205
- "other-industrial-metals-mining",
206
- "beverages-wineries-distilleries",
207
- "oil-gas-midstream",
208
- "recreational-vehicles",
209
- "electrical-equipment-parts",
210
- "household-personal-products",
211
- "packaging-containers",
212
- "reit-specialty",
213
- "home-improvement-retail",
214
- "electronic-components",
215
- "asset-management",
216
- "consumer-electronics",
217
- "conglomerates",
218
- "health-information-services",
219
- "medical-instruments-supplies",
220
- "building-products-equipment",
221
- "information-technology-services",
222
- "specialty-industrial-machinery",
223
- "food-distribution",
224
- "packaged-foods",
225
- "rental-leasing-services",
226
- "medical-distribution",
227
- "grocery-stores",
228
- "advertising-agencies",
229
- "beverages-non-alcoholic",
230
- "apparel-manufacturing",
231
- "oil-gas-equipment-services",
232
- "coking-coal",
233
- "industrial-distribution",
234
- "restaurants",
235
- "beverages-brewers",
236
- "chemicals",
237
- "real-estate-development",
238
- "credit-services",
239
- "tobacco",
240
- "metal-fabrication",
241
- "building-materials",
242
- "residential-construction",
243
- "specialty-business-services",
244
- "reit-hotel-motel",
245
- "internet-content-information",
246
- "lodging",
247
- "furnishings-fixtures-appliances",
248
- "airlines",
249
- "computer-hardware",
250
- "integrated-freight-logistics",
251
- "solar",
252
- "capital-markets",
253
- "leisure",
254
- "airports-air-services",
255
- "aluminum",
256
- "insurance-brokers",
257
- "semiconductors",
258
- "reit-retail",
259
- "luxury-goods",
260
- "lumber-wood-production",
261
- "reit-mortgage",
262
- "semiconductor-equipment-materials",
263
- "aerospace-defense",
264
- "security-protection-services",
265
- "utilities-renewable",
266
- "utilities-regulated-gas",
267
- "apparel-retail",
268
- "pollution-treatment-controls",
269
- "broadcasting",
270
- "resorts-casinos",
271
- "other-precious-metals-mining",
272
- "financial-data-stock-exchanges",
273
- "footwear-accessories",
274
- "medical-care-facilities",
275
- "electronics-computer-distribution",
276
- "gambling",
277
- "tools-accessories",
278
- "insurance-property-casualty",
279
- "utilities-regulated-water",
280
- "insurance-specialty",
281
- "personal-services",
282
- "pharmaceutical-retailers",
283
- "farm-heavy-construction-machinery",
284
- "utilities-regulated-electric",
285
- "department-stores",
286
- "staffing-employment-services",
287
- "textile-manufacturing",
288
- "silver",
289
- "reit-industrial",
290
- "reit-diversified",
291
- "copper",
292
- "business-equipment-supplies",
293
- "infrastructure-operations",
294
- "trucking",
295
- "insurance-reinsurance",
296
- "insurance-diversified",
297
- "drug-manufacturers-general",
298
- "oil-gas-drilling",
299
- "banks-diversified",
300
- "reit-residential",
301
- "oil-gas-refining-marketing",
302
- "shell-companies",
303
- "financial-conglomerates",
304
- "paper-paper-products",
305
- "insurance-life",
306
- "reit-office",
307
- "railroads",
308
- "oil-gas-integrated",
309
- "healthcare-plans",
310
- "reit-healthcare-facilities",
311
- "discount-stores",
312
- ]
313
-
314
- Sector = Literal[
315
- "Communication Services",
316
- "Consumer Cyclical",
317
- "Technology",
318
- "Industrials",
319
- "Healthcare",
320
- "Energy",
321
- "Consumer Defensive",
322
- "Basic Materials",
323
- "Real Estate",
324
- "Utilities",
325
- "Financial Services",
326
- "Conglomerates",
327
- ]
328
-
329
- Country = Literal[
330
- "Australia",
331
- "China",
332
- "Japan",
333
- "United kingdom",
334
- "United states",
335
- "Poland",
336
- "Switzerland",
337
- "Canada",
338
- "Greece",
339
- "Spain",
340
- "Germany",
341
- "Indonesia",
342
- "Belgium",
343
- "France",
344
- "Netherlands",
345
- "British virgin islands",
346
- "Italy",
347
- "Hungary",
348
- "Austria",
349
- "Finland",
350
- "Sweden",
351
- "Bermuda",
352
- "Taiwan",
353
- "Israel",
354
- "Ukraine",
355
- "Singapore",
356
- "Jersey",
357
- "Ireland",
358
- "Luxembourg",
359
- "Cyprus",
360
- "Cayman islands",
361
- "Norway",
362
- "Denmark",
363
- "Hong kong",
364
- "New zealand",
365
- "Kazakhstan",
366
- "Nigeria",
367
- "Argentina",
368
- "Brazil",
369
- "Czech republic",
370
- "Mauritius",
371
- "South africa",
372
- "India",
373
- "Mexico",
374
- "Mongolia",
375
- "Slovenia",
376
- "Thailand",
377
- "Malaysia",
378
- "Costa rica",
379
- "Isle of man",
380
- "Egypt",
381
- "Turkey",
382
- "United arab emirates",
383
- "Colombia",
384
- "Gibraltar",
385
- "Malta",
386
- "Liechtenstein",
387
- "Guernsey",
388
- "Peru",
389
- "Estonia",
390
- "French guiana",
391
- "Portugal",
392
- "Uruguay",
393
- "Chile",
394
- "Martinique",
395
- "Monaco",
396
- "Panama",
397
- "Papua new guinea",
398
- "South korea",
399
- "Macau",
400
- "Gabon",
401
- "Romania",
402
- "Senegal",
403
- "Morocco",
404
- "Jordan",
405
- "Lithuania",
406
- "Dominican republic",
407
- "Reunion",
408
- "Zambia",
409
- "Cambodia",
410
- "Myanmar",
411
- "Bahamas",
412
- "Philippines",
413
- "Bangladesh",
414
- "Latvia",
415
- "Vietnam",
416
- "Iceland",
417
- "Azerbaijan",
418
- "Georgia",
419
- "Liberia",
420
- "Kenya",
421
- ]
422
19
  SIZE_RANGE = 2
423
20
 
424
21
 
@@ -1,4 +1,3 @@
1
- import datetime
2
1
  import logging
3
2
  from datetime import date
4
3
  from typing import Optional, Callable, cast
@@ -122,6 +121,9 @@ def compute_roc(data: pd.DataFrame) -> pd.DataFrame:
122
121
  results["ROC_7"] = talib.ROC(data.close, timeperiod=7) # type: ignore
123
122
  results["ROC_1"] = talib.ROC(data.close, timeperiod=1) # type: ignore
124
123
  results["ROC_30"] = talib.ROC(data.close, timeperiod=30) # type: ignore
124
+ mom = talib.MOM(data.close, timeperiod=252) # type: ignore
125
+ results["MOM"] = mom.shift(21) # type: ignore
126
+
125
127
  return results
126
128
 
127
129
 
@@ -137,6 +139,7 @@ def compute_sma(data: pd.DataFrame) -> pd.DataFrame:
137
139
  results = pd.DataFrame(index=data.index)
138
140
  results["SMA_50"] = talib.SMA(data.close, timeperiod=50) # type: ignore
139
141
  results["SMA_200"] = talib.SMA(data.close, timeperiod=200) # type: ignore
142
+ results["CLOSE"] = data.close
140
143
  return results
141
144
 
142
145
 
@@ -144,6 +147,8 @@ def compute_pandas_ta_sma(data: pd.DataFrame) -> pd.DataFrame:
144
147
  results = pd.DataFrame(index=data.index)
145
148
  results["SMA_50"] = ta.sma(data.close, length=50)
146
149
  results["SMA_200"] = ta.sma(data.close, length=200)
150
+ results["CLOSE"] = data.close
151
+
147
152
  return results
148
153
 
149
154
 
@@ -276,22 +281,33 @@ def compute_price(data: pd.DataFrame) -> pd.DataFrame:
276
281
  return results
277
282
 
278
283
 
279
- def compute_percentile_return_after_rsi_crossover(
280
- data: pd.DataFrame, rsi_threshold: int = 45, period: int = 90
281
- ) -> float:
282
- data_ = cross_value_series(data.RSI, rsi_threshold)
283
- values = []
284
- for crossing_date in data_[data_ == 1].index:
285
- data_crossed = data[
286
- (data.index >= crossing_date)
287
- & (data.index <= crossing_date + datetime.timedelta(days=period))
288
- ]
289
- v = (
290
- data_crossed.CLOSE.pct_change(periods=len(data_crossed.CLOSE) - 1).iloc[-1]
291
- * 100
292
- )
293
- values.append(v)
294
- return float(np.percentile(values, 30))
284
+ def find_last_true_run_start(series: pd.Series) -> Optional[date]:
285
+ if not series.iloc[-1]:
286
+ return None
287
+ arr = series.to_numpy()
288
+ change_points = np.flatnonzero(np.r_[True, arr[1:] != arr[:-1]])
289
+ run_starts = change_points
290
+ true_runs = run_starts[arr[run_starts]]
291
+ last_true_run_start = true_runs[-1]
292
+ return series.index[last_true_run_start].date() # type: ignore
293
+
294
+
295
+ def sma_50_above_sma_200(data: pd.DataFrame) -> Optional[date]:
296
+ date_1 = find_last_true_run_start(data.SMA_50 > data.SMA_200)
297
+ return date_1
298
+
299
+
300
+ def price_above_sma50(data: pd.DataFrame) -> Optional[date]:
301
+ date_1 = find_last_true_run_start(data.SMA_50 < data.CLOSE)
302
+ return date_1
303
+
304
+
305
+ def momentum(data: pd.DataFrame) -> Optional[date]:
306
+ date_1 = find_last_true_run_start(data.SMA_50 < data.CLOSE)
307
+ date_2 = find_last_true_run_start(data.SMA_200 < data.SMA_50)
308
+ if date_1 is None or date_2 is None:
309
+ return None
310
+ return max(date_1, date_2)
295
311
 
296
312
 
297
313
  class IndicatorFunction(BaseModel):
@@ -334,7 +350,7 @@ MFI = IndicatorFunction(
334
350
  expected_columns=["MFI"], functions=[compute_mfi, compute_pandas_ta_mfi]
335
351
  )
336
352
  ROC = IndicatorFunction(
337
- expected_columns=["ROC_7", "ROC_1", "ROC_30"],
353
+ expected_columns=["ROC_7", "ROC_1", "ROC_30", "MOM"],
338
354
  functions=[compute_roc, compute_pandas_ta_roc],
339
355
  )
340
356
  CANDLESTOCK_PATTERNS = IndicatorFunction(
@@ -351,7 +367,7 @@ CANDLESTOCK_PATTERNS = IndicatorFunction(
351
367
  )
352
368
 
353
369
  SMA = IndicatorFunction(
354
- expected_columns=["SMA_50", "SMA_200"],
370
+ expected_columns=["SMA_50", "SMA_200", "CLOSE"],
355
371
  functions=[compute_sma, compute_pandas_ta_sma],
356
372
  )
357
373
 
@@ -19,7 +19,9 @@ from bullish.analysis.functions import (
19
19
  SMA,
20
20
  ADOSC,
21
21
  PRICE,
22
- compute_percentile_return_after_rsi_crossover,
22
+ momentum,
23
+ sma_50_above_sma_200,
24
+ price_above_sma50,
23
25
  )
24
26
 
25
27
  logger = logging.getLogger(__name__)
@@ -205,13 +207,6 @@ def indicators_factory() -> List[Indicator]:
205
207
  (d.RSI < 60) & (d.RSI > 40)
206
208
  ].last_valid_index(),
207
209
  ),
208
- Signal(
209
- name="RETURN_AFTER_RSI_CROSSOVER_45_PERIOD_90",
210
- description="Percentile 30 return after RSI crossover 45 in the next 90 days",
211
- type_info="Long",
212
- type=Optional[float],
213
- function=lambda d: compute_percentile_return_after_rsi_crossover(d),
214
- ),
215
210
  ],
216
211
  ),
217
212
  Indicator(
@@ -282,6 +277,27 @@ def indicators_factory() -> List[Indicator]:
282
277
  type=Optional[date],
283
278
  function=lambda d: cross(d.SMA_50, d.SMA_200, above=False),
284
279
  ),
280
+ Signal(
281
+ name="MOMENTUM_TIME_SPAN",
282
+ description="Momentum time span",
283
+ type_info="Overbought",
284
+ type=Optional[date],
285
+ function=lambda d: momentum(d),
286
+ ),
287
+ Signal(
288
+ name="SMA_50_ABOVE_SMA_200",
289
+ description="SMA 50 is above SMA 200",
290
+ type_info="Overbought",
291
+ type=Optional[date],
292
+ function=lambda d: sma_50_above_sma_200(d),
293
+ ),
294
+ Signal(
295
+ name="PRICE_ABOVE_SMA_50",
296
+ description="Price is above SMA 50",
297
+ type_info="Overbought",
298
+ type=Optional[date],
299
+ function=lambda d: price_above_sma50(d),
300
+ ),
285
301
  ],
286
302
  ),
287
303
  Indicator(
@@ -334,7 +350,7 @@ def indicators_factory() -> List[Indicator]:
334
350
  Indicator(
335
351
  name="ROC",
336
352
  description="Rate Of Change",
337
- expected_columns=["ROC_7", "ROC_30", "ROC_1"],
353
+ expected_columns=ROC.expected_columns,
338
354
  function=ROC.call,
339
355
  signals=[
340
356
  Signal(
@@ -379,6 +395,13 @@ def indicators_factory() -> List[Indicator]:
379
395
  type=Optional[float],
380
396
  function=lambda d: d.ROC_7.tolist()[-1],
381
397
  ),
398
+ Signal(
399
+ name="MOMENTUM",
400
+ type_info="Value",
401
+ description="7-day Rate of Change",
402
+ type=Optional[float],
403
+ function=lambda d: d.MOM.iloc[-1],
404
+ ),
382
405
  ],
383
406
  ),
384
407
  Indicator(