finance-flow 0.1.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.
- finance_flow/__init__.py +4 -0
- finance_flow/bars.py +106 -0
- finance_flow/config/__init__.py +0 -0
- finance_flow/config/task/backtest_portfolio.yaml +6 -0
- finance_flow/config/task/build_alpha_report.yaml +6 -0
- finance_flow/config/task/build_risk_report.yaml +6 -0
- finance_flow/config/task/build_universe.yaml +7 -0
- finance_flow/config/task/calculate_signals.yaml +7 -0
- finance_flow/config/task/construct_target_positions.yaml +7 -0
- finance_flow/config/task/evaluate_real_portfolio.yaml +6 -0
- finance_flow/config/task/optimize_portfolio.yaml +7 -0
- finance_flow/tasks.py +645 -0
- finance_flow/tests/test_bars.py +117 -0
- finance_flow/tests/test_task_chain_integration.py +50 -0
- finance_flow/tests/test_tasks.py +223 -0
- finance_flow-0.1.0.dist-info/METADATA +105 -0
- finance_flow-0.1.0.dist-info/RECORD +20 -0
- finance_flow-0.1.0.dist-info/WHEEL +4 -0
- finance_flow-0.1.0.dist-info/entry_points.txt +2 -0
- finance_flow-0.1.0.dist-info/licenses/LICENSE +201 -0
finance_flow/__init__.py
ADDED
finance_flow/bars.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
from datetime import date
|
|
2
|
+
from typing import Any, Dict, List, Optional, Type, Union
|
|
3
|
+
|
|
4
|
+
from ccflow import CallableModel, ContextBase, ContextType, Flow, GenericResult, ResultType
|
|
5
|
+
from pydantic import BaseModel, field_serializer
|
|
6
|
+
|
|
7
|
+
__all__ = (
|
|
8
|
+
"DailyBar",
|
|
9
|
+
"MassiveDailyBarsNormalizeContext",
|
|
10
|
+
"MassiveDailyBarsNormalizeModel",
|
|
11
|
+
"normalize_massive_daily_bars",
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class DailyBar(BaseModel):
|
|
16
|
+
ticker: str
|
|
17
|
+
date: date
|
|
18
|
+
open: float
|
|
19
|
+
high: float
|
|
20
|
+
low: float
|
|
21
|
+
close: float
|
|
22
|
+
volume: int
|
|
23
|
+
vwap: Optional[float] = None
|
|
24
|
+
transactions: Optional[int] = None
|
|
25
|
+
|
|
26
|
+
@field_serializer("date")
|
|
27
|
+
def serialize_date(self, value: date) -> str:
|
|
28
|
+
return value.isoformat()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class MassiveDailyBarsNormalizeContext(ContextBase):
|
|
32
|
+
payload: Union[Dict[str, Any], List[Dict[str, Any]]]
|
|
33
|
+
ticker: str
|
|
34
|
+
session_date: date
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class MassiveDailyBarsNormalizeModel(CallableModel):
|
|
38
|
+
@property
|
|
39
|
+
def context_type(self) -> Type[ContextType]:
|
|
40
|
+
return MassiveDailyBarsNormalizeContext
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def result_type(self) -> Type[ResultType]:
|
|
44
|
+
return GenericResult
|
|
45
|
+
|
|
46
|
+
@Flow.call
|
|
47
|
+
def __call__(self, context: MassiveDailyBarsNormalizeContext) -> GenericResult:
|
|
48
|
+
return GenericResult(value=normalize_massive_daily_bars(context.payload, ticker=context.ticker, session_date=context.session_date))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _items(payload: Any) -> List[Dict[str, Any]]:
|
|
52
|
+
if isinstance(payload, dict):
|
|
53
|
+
value = payload.get("results", payload.get("rows", []))
|
|
54
|
+
return list(value or [])
|
|
55
|
+
return list(payload or [])
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _value(row: Dict[str, Any], *names: str) -> Any:
|
|
59
|
+
for name in names:
|
|
60
|
+
if name in row and row[name] is not None:
|
|
61
|
+
return row[name]
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _float(row: Dict[str, Any], *names: str) -> float:
|
|
66
|
+
value = _value(row, *names)
|
|
67
|
+
if value is None:
|
|
68
|
+
raise ValueError(f"Missing numeric field; expected one of {names}.")
|
|
69
|
+
return float(value)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _int(row: Dict[str, Any], *names: str) -> int:
|
|
73
|
+
value = _value(row, *names)
|
|
74
|
+
if value is None:
|
|
75
|
+
raise ValueError(f"Missing integer field; expected one of {names}.")
|
|
76
|
+
return int(value)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _optional_float(row: Dict[str, Any], *names: str) -> Optional[float]:
|
|
80
|
+
value = _value(row, *names)
|
|
81
|
+
return None if value is None else float(value)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _optional_int(row: Dict[str, Any], *names: str) -> Optional[int]:
|
|
85
|
+
value = _value(row, *names)
|
|
86
|
+
return None if value is None else int(value)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def normalize_massive_daily_bars(payload: Union[Dict[str, Any], List[Dict[str, Any]]], ticker: str, session_date: Union[str, date]) -> List[DailyBar]:
|
|
90
|
+
business_date = date.fromisoformat(session_date) if isinstance(session_date, str) else session_date
|
|
91
|
+
bars = []
|
|
92
|
+
for row in _items(payload):
|
|
93
|
+
bars.append(
|
|
94
|
+
DailyBar(
|
|
95
|
+
ticker=str(_value(row, "ticker", "T") or ticker),
|
|
96
|
+
date=_value(row, "date") or business_date,
|
|
97
|
+
open=_float(row, "open", "o"),
|
|
98
|
+
high=_float(row, "high", "h"),
|
|
99
|
+
low=_float(row, "low", "l"),
|
|
100
|
+
close=_float(row, "close", "c"),
|
|
101
|
+
volume=_int(row, "volume", "v"),
|
|
102
|
+
vwap=_optional_float(row, "vwap", "vw"),
|
|
103
|
+
transactions=_optional_int(row, "transactions", "n"),
|
|
104
|
+
)
|
|
105
|
+
)
|
|
106
|
+
return bars
|
|
File without changes
|