bullishpy 0.4.0__tar.gz → 0.6.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 (44) hide show
  1. {bullishpy-0.4.0 → bullishpy-0.6.0}/PKG-INFO +4 -5
  2. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/analysis/analysis.py +114 -250
  3. bullishpy-0.6.0/bullish/analysis/filter.py +625 -0
  4. bullishpy-0.6.0/bullish/analysis/functions.py +344 -0
  5. bullishpy-0.6.0/bullish/analysis/indicators.py +450 -0
  6. bullishpy-0.6.0/bullish/analysis/predefined_filters.py +87 -0
  7. bullishpy-0.6.0/bullish/app/app.py +370 -0
  8. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/cli.py +3 -1
  9. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/database/alembic/versions/037dbd721317_.py +1 -0
  10. bullishpy-0.6.0/bullish/database/alembic/versions/08ac1116e055_.py +592 -0
  11. bullishpy-0.6.0/bullish/database/alembic/versions/11d35a452b40_.py +368 -0
  12. bullishpy-0.6.0/bullish/database/alembic/versions/49c83f9eb5ac_.py +103 -0
  13. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/database/alembic/versions/4b0a2f40b7d3_.py +1 -0
  14. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/database/alembic/versions/73564b60fe24_.py +1 -0
  15. bullishpy-0.6.0/bullish/database/alembic/versions/ee5baabb35f8_.py +51 -0
  16. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/database/crud.py +14 -2
  17. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/database/schemas.py +13 -0
  18. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/figures/figures.py +52 -12
  19. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/interface/interface.py +16 -27
  20. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/jobs/tasks.py +10 -0
  21. bullishpy-0.6.0/bullish/utils/__init__.py +0 -0
  22. bullishpy-0.6.0/bullish/utils/checks.py +64 -0
  23. {bullishpy-0.4.0 → bullishpy-0.6.0}/pyproject.toml +8 -6
  24. bullishpy-0.4.0/bullish/analysis/filter.py +0 -123
  25. bullishpy-0.4.0/bullish/app/app.py +0 -218
  26. {bullishpy-0.4.0 → bullishpy-0.6.0}/README.md +0 -0
  27. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/__init__.py +0 -0
  28. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/analysis/__init__.py +0 -0
  29. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/app/__init__.py +0 -0
  30. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/database/__init__.py +0 -0
  31. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/database/alembic/README +0 -0
  32. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/database/alembic/alembic.ini +0 -0
  33. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/database/alembic/env.py +0 -0
  34. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/database/alembic/script.py.mako +0 -0
  35. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/database/scripts/create_revision.py +0 -0
  36. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/database/scripts/stamp.py +0 -0
  37. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/database/scripts/upgrade.py +0 -0
  38. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/database/settings.py +0 -0
  39. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/exceptions.py +0 -0
  40. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/figures/__init__.py +0 -0
  41. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/interface/__init__.py +0 -0
  42. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/jobs/__init__.py +0 -0
  43. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/jobs/app.py +0 -0
  44. {bullishpy-0.4.0 → bullishpy-0.6.0}/bullish/jobs/models.py +0 -0
@@ -1,21 +1,20 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bullishpy
3
- Version: 0.4.0
3
+ Version: 0.6.0
4
4
  Summary:
5
5
  Author: aan
6
6
  Author-email: andoludovic.andriamamonjy@gmail.com
7
- Requires-Python: >=3.10,<3.13
7
+ Requires-Python: >=3.12,<3.13
8
8
  Classifier: Programming Language :: Python :: 3
9
- Classifier: Programming Language :: Python :: 3.10
10
- Classifier: Programming Language :: Python :: 3.11
11
9
  Classifier: Programming Language :: Python :: 3.12
12
- Requires-Dist: bearishpy (>=0.19.0,<0.20.0)
10
+ Requires-Dist: bearishpy (>=0.20.0,<0.21.0)
13
11
  Requires-Dist: huey (>=2.5.3,<3.0.0)
14
12
  Requires-Dist: pandas-ta (>=0.3.14b0,<0.4.0)
15
13
  Requires-Dist: plotly (>=6.1.2,<7.0.0)
16
14
  Requires-Dist: streamlit (>=1.45.1,<2.0.0)
17
15
  Requires-Dist: streamlit-file-browser (>=3.2.22,<4.0.0)
18
16
  Requires-Dist: streamlit-pydantic (>=v0.6.1-rc.3,<0.7.0)
17
+ Requires-Dist: ta-lib (>=0.6.4,<0.7.0)
19
18
  Requires-Dist: tickermood (>=0.4.0,<0.5.0)
20
19
  Description-Content-Type: text/markdown
21
20
 
@@ -1,5 +1,4 @@
1
1
  import logging
2
- from datetime import date
3
2
  from typing import (
4
3
  Annotated,
5
4
  Any,
@@ -7,13 +6,12 @@ from typing import (
7
6
  Optional,
8
7
  Sequence,
9
8
  Type,
10
- cast,
11
9
  get_args,
12
10
  TYPE_CHECKING,
11
+ ClassVar,
13
12
  )
14
13
 
15
14
  import pandas as pd
16
- import pandas_ta as ta # type: ignore
17
15
  from bearish.interface.interface import BearishDbBase # type: ignore
18
16
  from bearish.models.assets.equity import BaseEquity # type: ignore
19
17
  from bearish.models.base import ( # type: ignore
@@ -41,6 +39,8 @@ from bearish.models.query.query import AssetQuery, Symbols # type: ignore
41
39
  from bearish.types import TickerOnlySources # type: ignore
42
40
  from pydantic import BaseModel, BeforeValidator, Field, create_model
43
41
 
42
+ from bullish.analysis.indicators import Indicators, IndicatorModels
43
+
44
44
  if TYPE_CHECKING:
45
45
  from bullish.database.crud import BullishDb
46
46
 
@@ -61,59 +61,6 @@ def to_float(value: Any) -> Optional[float]:
61
61
  return float(value)
62
62
 
63
63
 
64
- def price_growth(prices: pd.DataFrame, days: int, max: bool = False) -> Optional[float]:
65
- prices_ = prices.copy()
66
- last_index = prices_.last_valid_index()
67
- delta = pd.Timedelta(days=days)
68
- start_index = last_index - delta # type: ignore
69
-
70
- try:
71
- closest_index = prices_.index.unique().asof(start_index) # type: ignore
72
- price = (
73
- prices_.loc[closest_index].close
74
- if not max
75
- else prices_[closest_index:].close.max()
76
- )
77
- except Exception as e:
78
- logger.warning(
79
- f"""Failing to calculate price growth: {e}.""",
80
- exc_info=True,
81
- )
82
- return None
83
- return ( # type: ignore
84
- (prices_.loc[last_index].close - price) * 100 / prices_.loc[last_index].close
85
- )
86
-
87
-
88
- def buy_opportunity(
89
- series_a: pd.Series, series_b: pd.Series # type: ignore
90
- ) -> Optional[date]:
91
- sell = ta.cross(series_a=series_a, series_b=series_b)
92
- buy = ta.cross(series_a=series_b, series_b=series_a)
93
- if not buy[buy == 1].index.empty and not sell[sell == 1].index.empty:
94
- last_buy_signal = pd.Timestamp(buy[buy == 1].index[-1])
95
- last_sell_signal = pd.Timestamp(sell[sell == 1].index[-1])
96
- if last_buy_signal > last_sell_signal:
97
- return last_buy_signal
98
- return None
99
-
100
-
101
- def perc(data: pd.Series) -> float: # type: ignore
102
- return cast(float, ((data.iloc[-1] - data.iloc[0]) / data.iloc[0]) * 100)
103
-
104
-
105
- def yoy(prices: pd.DataFrame) -> pd.Series: # type: ignore
106
- return prices.close.resample("YE").apply(perc) # type: ignore
107
-
108
-
109
- def mom(prices: pd.DataFrame) -> pd.Series: # type: ignore
110
- return prices.close.resample("ME").apply(perc) # type: ignore
111
-
112
-
113
- def wow(prices: pd.DataFrame) -> pd.Series: # type: ignore
114
- return prices.close.resample("W").apply(perc) # type: ignore
115
-
116
-
117
64
  def _load_data(
118
65
  data: Sequence[DataSourceBase], symbol: str, class_: Type[DataSourceBase]
119
66
  ) -> pd.DataFrame:
@@ -128,23 +75,23 @@ def _load_data(
128
75
  return pd.DataFrame(columns=columns).sort_index()
129
76
 
130
77
 
131
- def _compute_growth(series: pd.Series) -> bool: # type: ignore
78
+ def _compute_growth(series: pd.Series) -> bool:
132
79
  if series.empty:
133
80
  return False
134
81
  return all(series.pct_change(fill_method=None).dropna() > 0)
135
82
 
136
83
 
137
- def _all_positive(series: pd.Series) -> bool: # type: ignore
84
+ def _all_positive(series: pd.Series) -> bool:
138
85
  if series.empty:
139
86
  return False
140
87
  return all(series.dropna() > 0)
141
88
 
142
89
 
143
- def _get_last(data: pd.Series) -> Optional[float]: # type: ignore
90
+ def _get_last(data: pd.Series) -> Optional[float]:
144
91
  return data.iloc[-1] if not data.empty else None
145
92
 
146
93
 
147
- def _abs(data: pd.Series) -> pd.Series: # type: ignore
94
+ def _abs(data: pd.Series) -> pd.Series:
148
95
  try:
149
96
  return abs(data)
150
97
  except Exception as e:
@@ -152,17 +99,8 @@ def _abs(data: pd.Series) -> pd.Series: # type: ignore
152
99
  return data
153
100
 
154
101
 
155
- class TechnicalAnalysis(BaseModel):
156
- rsi_last_value: Optional[float] = None
157
- macd_12_26_9_buy_date: Optional[date] = None
158
- ma_50_200_buy_date: Optional[date] = None
159
- slope_7: Optional[float] = None
160
- slope_14: Optional[float] = None
161
- slope_30: Optional[float] = None
162
- slope_60: Optional[float] = None
163
- last_adx: Optional[float] = None
164
- last_dmp: Optional[float] = None
165
- last_dmn: Optional[float] = None
102
+ class TechnicalAnalysisBase(BaseModel):
103
+ _description: ClassVar[str] = "General technical indicators"
166
104
  last_price: Annotated[
167
105
  Optional[float],
168
106
  BeforeValidator(to_float),
@@ -170,189 +108,113 @@ class TechnicalAnalysis(BaseModel):
170
108
  default=None,
171
109
  ),
172
110
  ]
173
- last_price_date: Annotated[
174
- Optional[date],
175
- Field(
176
- default=None,
177
- ),
178
- ]
179
- year_to_date_growth: Annotated[
180
- Optional[float],
181
- Field(
182
- default=None,
183
- ),
184
- ]
185
- last_52_weeks_growth: Annotated[
186
- Optional[float],
187
- Field(
188
- default=None,
189
- ),
190
- ]
191
- last_week_growth: Annotated[
192
- Optional[float],
193
- Field(
194
- default=None,
195
- ),
196
- ]
197
- last_month_growth: Annotated[
198
- Optional[float],
199
- Field(
200
- default=None,
201
- ),
202
- ]
203
- last_year_growth: Annotated[
204
- Optional[float],
205
- Field(
206
- default=None,
207
- ),
208
- ]
209
- year_to_date_max_growth: Annotated[
210
- Optional[float],
211
- Field(
212
- default=None,
213
- ),
214
- ]
215
- last_week_max_growth: Annotated[
216
- Optional[float],
217
- Field(
218
- default=None,
219
- ),
220
- ]
221
- last_month_max_growth: Annotated[
222
- Optional[float],
223
- Field(
224
- default=None,
225
- ),
226
- ]
227
- last_year_max_growth: Annotated[
228
- Optional[float],
229
- Field(
230
- default=None,
231
- ),
232
- ]
233
- macd_12_26_9_buy: Annotated[
234
- Optional[float],
235
- Field(
236
- default=None,
237
- ),
238
- ]
239
- star_yoy: Annotated[
240
- Optional[float],
241
- Field(
242
- default=None,
243
- ),
244
- ]
245
- star_wow: Annotated[
246
- Optional[float],
247
- Field(
248
- default=None,
249
- ),
250
- ]
251
- star_mom: Annotated[
252
- Optional[float],
253
- Field(
254
- default=None,
255
- ),
256
- ]
111
+
112
+
113
+ TechnicalAnalysisModels = [*IndicatorModels, TechnicalAnalysisBase]
114
+
115
+
116
+ class TechnicalAnalysis(*TechnicalAnalysisModels): # type: ignore
257
117
 
258
118
  @classmethod
259
119
  def from_data(cls, prices: pd.DataFrame) -> "TechnicalAnalysis":
260
120
  try:
261
- last_index = prices.last_valid_index()
262
- year_to_date_days = (
263
- last_index
264
- - pd.Timestamp(year=last_index.year, month=1, day=1, tz="UTC") # type: ignore
265
- ).days
266
- year_to_date_growth = price_growth(prices, year_to_date_days)
267
- last_52_weeks_growth = price_growth(prices=prices, days=399)
268
- last_week_growth = price_growth(prices=prices, days=7)
269
- last_month_growth = price_growth(prices=prices, days=31)
270
- last_year_growth = price_growth(prices=prices, days=365)
271
- year_to_date_max_growth = price_growth(prices, year_to_date_days, max=True)
272
- last_week_max_growth = price_growth(prices=prices, days=7, max=True)
273
- last_month_max_growth = price_growth(prices=prices, days=31, max=True)
274
- last_year_max_growth = price_growth(prices=prices, days=365, max=True)
275
- prices.ta.sma(50, append=True)
276
- prices.ta.sma(200, append=True)
277
- prices.ta.adx(append=True)
278
- prices["SLOPE_14"] = ta.linreg(prices.close, slope=True, length=14)
279
- prices["SLOPE_7"] = ta.linreg(prices.close, slope=True, length=7)
280
- prices["SLOPE_30"] = ta.linreg(prices.close, slope=True, length=30)
281
- prices["SLOPE_60"] = ta.linreg(prices.close, slope=True, length=60)
282
- prices.ta.macd(append=True)
283
- prices.ta.rsi(append=True)
284
-
285
- rsi_last_value = prices.RSI_14.iloc[-1]
286
- macd_12_26_9_buy_date = buy_opportunity(
287
- prices.MACDs_12_26_9, prices.MACD_12_26_9
288
- )
289
- star_yoy = yoy(prices).median()
290
- star_mom = mom(prices).median()
291
- star_wow = wow(prices).median()
292
- try:
293
- macd_12_26_9_buy = (
294
- prices.MACD_12_26_9.iloc[-1] > prices.MACDs_12_26_9.iloc[-1]
295
- )
296
- except Exception as e:
297
- logger.warning(
298
- f"Failing to calculate MACD buy date: {e}", exc_info=True
299
- )
300
- macd_12_26_9_buy = None
301
- ma_50_200_buy_date = buy_opportunity(prices.SMA_200, prices.SMA_50)
302
- return cls(
303
- rsi_last_value=rsi_last_value,
304
- macd_12_26_9_buy_date=macd_12_26_9_buy_date,
305
- macd_12_26_9_buy=macd_12_26_9_buy,
306
- ma_50_200_buy_date=ma_50_200_buy_date,
307
- last_price=prices.close.iloc[-1],
308
- last_price_date=prices.index[-1],
309
- last_adx=prices.ADX_14.iloc[-1],
310
- last_dmp=prices.DMP_14.iloc[-1],
311
- last_dmn=prices.DMN_14.iloc[-1],
312
- slope_7=prices.SLOPE_7.iloc[-1],
313
- slope_14=prices.SLOPE_14.iloc[-1],
314
- slope_30=prices.SLOPE_30.iloc[-1],
315
- slope_60=prices.SLOPE_60.iloc[-1],
316
- year_to_date_growth=year_to_date_growth,
317
- last_52_weeks_growth=last_52_weeks_growth,
318
- last_week_growth=last_week_growth,
319
- last_month_growth=last_month_growth,
320
- last_year_growth=last_year_growth,
321
- year_to_date_max_growth=year_to_date_max_growth,
322
- last_week_max_growth=last_week_max_growth,
323
- last_month_max_growth=last_month_max_growth,
324
- last_year_max_growth=last_year_max_growth,
325
- star_yoy=star_yoy,
326
- star_mom=star_mom,
327
- star_wow=star_wow,
328
- )
121
+ res = Indicators().to_dict(prices)
122
+ return cls(last_price=prices.close.iloc[-1], **res)
329
123
  except Exception as e:
330
124
  logger.error(f"Failing to calculate technical analysis: {e}", exc_info=True)
331
- return cls() # type: ignore
125
+ return cls()
332
126
 
333
127
 
334
128
  class BaseFundamentalAnalysis(BaseModel):
335
- positive_free_cash_flow: Optional[float] = None
336
- growing_operating_cash_flow: Optional[float] = None
337
- operating_cash_flow_is_higher_than_net_income: Optional[float] = None
338
- mean_capex_ratio: Optional[float] = None
339
- max_capex_ratio: Optional[float] = None
340
- min_capex_ratio: Optional[float] = None
341
- mean_dividend_payout_ratio: Optional[float] = None
342
- max_dividend_payout_ratio: Optional[float] = None
343
- min_dividend_payout_ratio: Optional[float] = None
344
- positive_net_income: Optional[float] = None
345
- positive_operating_income: Optional[float] = None
346
- growing_net_income: Optional[float] = None
347
- growing_operating_income: Optional[float] = None
348
- positive_diluted_eps: Optional[float] = None
349
- positive_basic_eps: Optional[float] = None
350
- growing_basic_eps: Optional[float] = None
351
- growing_diluted_eps: Optional[float] = None
352
- positive_debt_to_equity: Optional[float] = None
353
- positive_return_on_assets: Optional[float] = None
354
- positive_return_on_equity: Optional[float] = None
355
- earning_per_share: Optional[float] = None
129
+ positive_debt_to_equity: Optional[bool] = Field(
130
+ None,
131
+ description="True if the company's debt-to-equity ratio is favorable (typically low or improving).",
132
+ )
133
+ positive_return_on_assets: Optional[bool] = Field(
134
+ None,
135
+ description="True if the company reports a positive return on assets (ROA), "
136
+ "indicating efficient use of its assets.",
137
+ )
138
+ positive_return_on_equity: Optional[bool] = Field(
139
+ None,
140
+ description="True if the return on equity (ROE) is positive, "
141
+ "showing profitability relative to shareholder equity.",
142
+ )
143
+ positive_diluted_eps: Optional[bool] = Field(
144
+ None,
145
+ description="True if the diluted earnings per share (EPS), "
146
+ "which includes the effect of convertible securities, is positive.",
147
+ )
148
+ positive_basic_eps: Optional[bool] = Field(
149
+ None,
150
+ description="True if the basic earnings per share (EPS) is positive, reflecting profitable operations.",
151
+ )
152
+ growing_basic_eps: Optional[bool] = Field(
153
+ None,
154
+ description="True if the basic EPS has shown consistent growth over a defined time period.",
155
+ )
156
+ growing_diluted_eps: Optional[bool] = Field(
157
+ None,
158
+ description="True if the diluted EPS has consistently increased over time.",
159
+ )
160
+ positive_net_income: Optional[bool] = Field(
161
+ None,
162
+ description="True if the net income is positive, indicating overall profitability.",
163
+ )
164
+ positive_operating_income: Optional[bool] = Field(
165
+ None,
166
+ description="True if the company has positive operating income from its core business operations.",
167
+ )
168
+ growing_net_income: Optional[bool] = Field(
169
+ None, description="True if net income has shown consistent growth over time."
170
+ )
171
+ growing_operating_income: Optional[bool] = Field(
172
+ None,
173
+ description="True if the operating income has consistently increased over a period.",
174
+ )
175
+ positive_free_cash_flow: Optional[bool] = Field(
176
+ None,
177
+ description="True if the company has positive free cash flow, indicating financial flexibility and health.",
178
+ )
179
+ growing_operating_cash_flow: Optional[bool] = Field(
180
+ None,
181
+ description="True if the company's operating cash flow is growing steadily.",
182
+ )
183
+ operating_cash_flow_is_higher_than_net_income: Optional[bool] = Field(
184
+ None,
185
+ description="True if the operating cash flow exceeds net income, often a sign of high-quality earnings.",
186
+ )
187
+
188
+ # Capital Expenditure Ratios
189
+ mean_capex_ratio: Optional[float] = Field(
190
+ None,
191
+ description="Average capital expenditure (CapEx) ratio, usually "
192
+ "calculated as CapEx divided by revenue or operating cash flow.",
193
+ )
194
+ max_capex_ratio: Optional[float] = Field(
195
+ None, description="Maximum observed CapEx ratio over the evaluation period."
196
+ )
197
+ min_capex_ratio: Optional[float] = Field(
198
+ None, description="Minimum observed CapEx ratio over the evaluation period."
199
+ )
200
+
201
+ # Dividend Payout Ratios
202
+ mean_dividend_payout_ratio: Optional[float] = Field(
203
+ None,
204
+ description="Average dividend payout ratio, representing the proportion of earnings paid out as dividends.",
205
+ )
206
+ max_dividend_payout_ratio: Optional[float] = Field(
207
+ None, description="Maximum dividend payout ratio observed over the period."
208
+ )
209
+ min_dividend_payout_ratio: Optional[float] = Field(
210
+ None, description="Minimum dividend payout ratio observed over the period."
211
+ )
212
+
213
+ # EPS Value
214
+ earning_per_share: Optional[float] = Field(
215
+ None,
216
+ description="The latest or most relevant value of earnings per share (EPS), indicating net income per share.",
217
+ )
356
218
 
357
219
  def is_empty(self) -> bool:
358
220
  return all(getattr(self, field) is None for field in self.model_fields)
@@ -481,13 +343,15 @@ class BaseFundamentalAnalysis(BaseModel):
481
343
  return cls()
482
344
 
483
345
 
484
- class YearlyFundamentalAnalysis(BaseFundamentalAnalysis):
485
- ...
346
+ class YearlyFundamentalAnalysis(BaseFundamentalAnalysis): ...
486
347
 
487
348
 
488
349
  fields_with_prefix = {
489
- f"{QUARTERLY}_{name}": (Optional[float], Field(default=None))
490
- for name in BaseFundamentalAnalysis.model_fields
350
+ f"{QUARTERLY}_{name}": (
351
+ field_info.annotation,
352
+ Field(default=None, description=field_info.description),
353
+ )
354
+ for name, field_info in BaseFundamentalAnalysis.model_fields.items()
491
355
  }
492
356
 
493
357
  # Create the new model
@@ -507,7 +371,7 @@ class QuarterlyFundamentalAnalysis(BaseQuarterlyFundamentalAnalysis): # type: i
507
371
  cash_flows=financials.quarterly_cash_flows,
508
372
  ticker=ticker,
509
373
  )
510
- return cls.model_validate({f"{QUARTERLY}_{k}": v for k, v in base_financial_analisys.model_dump().items()}) # type: ignore # noqa: E501
374
+ return cls.model_validate({f"{QUARTERLY}_{k}": v for k, v in base_financial_analisys.model_dump().items()}) # type: ignore
511
375
 
512
376
 
513
377
  class FundamentalAnalysis(YearlyFundamentalAnalysis, QuarterlyFundamentalAnalysis):