quantification 0.1.0__py3-none-any.whl → 0.1.1__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.
- quantification/__init__.py +3 -2
- quantification/api/__init__.py +3 -0
- quantification/api/akshare/__init__.py +1 -0
- quantification/api/akshare/akshare.py +17 -0
- quantification/api/akshare/delegate/__init__.py +6 -0
- quantification/api/akshare/delegate/macro_china_fdi.py +46 -0
- quantification/api/akshare/delegate/macro_china_lpr.py +43 -0
- quantification/api/akshare/delegate/macro_china_qyspjg.py +51 -0
- quantification/api/akshare/delegate/macro_china_shrzgm.py +47 -0
- quantification/api/akshare/delegate/macro_cnbs.py +47 -0
- quantification/api/akshare/delegate/stock_zh_a_hist.py +77 -0
- quantification/api/akshare/setting.py +5 -0
- quantification/api/api.py +11 -0
- quantification/api/api.pyi +21 -0
- quantification/api/tushare/__init__.py +1 -0
- quantification/api/tushare/delegate/__init__.py +7 -0
- quantification/api/tushare/delegate/balancesheet.py +66 -0
- quantification/api/tushare/delegate/cashflow.py +29 -0
- quantification/api/tushare/delegate/common.py +64 -0
- quantification/api/tushare/delegate/daily_basic.py +81 -0
- quantification/api/tushare/delegate/fina_indicator.py +20 -0
- quantification/api/tushare/delegate/income.py +34 -0
- quantification/api/tushare/delegate/index_daily.py +61 -0
- quantification/api/tushare/delegate/pro_bar.py +80 -0
- quantification/api/tushare/setting.py +5 -0
- quantification/api/tushare/tushare.py +17 -0
- quantification/core/__init__.py +9 -0
- quantification/core/asset/__init__.py +6 -0
- quantification/core/asset/base_asset.py +96 -0
- quantification/core/asset/base_broker.py +42 -0
- quantification/core/asset/broker.py +108 -0
- quantification/core/asset/cash.py +75 -0
- quantification/core/asset/stock.py +268 -0
- quantification/core/cache.py +93 -0
- quantification/core/configure.py +15 -0
- quantification/core/data/__init__.py +5 -0
- quantification/core/data/base_api.py +109 -0
- quantification/core/data/base_delegate.py +73 -0
- quantification/core/data/field.py +213 -0
- quantification/core/data/panel.py +42 -0
- quantification/core/env.py +25 -0
- quantification/core/logger.py +94 -0
- quantification/core/strategy/__init__.py +3 -0
- quantification/core/strategy/base_strategy.py +66 -0
- quantification/core/strategy/base_trigger.py +69 -0
- quantification/core/strategy/base_use.py +69 -0
- quantification/core/trader/__init__.py +7 -0
- quantification/core/trader/base_order.py +45 -0
- quantification/core/trader/base_stage.py +16 -0
- quantification/core/trader/base_trader.py +173 -0
- quantification/core/trader/collector.py +47 -0
- quantification/core/trader/order.py +23 -0
- quantification/core/trader/portfolio.py +72 -0
- quantification/core/trader/query.py +29 -0
- quantification/core/trader/report.py +76 -0
- quantification/core/util.py +181 -0
- quantification/default/__init__.py +5 -0
- quantification/default/stage/__init__.py +1 -0
- quantification/default/stage/cn_stock.py +23 -0
- quantification/default/strategy/__init__.py +1 -0
- quantification/default/strategy/simple/__init__.py +1 -0
- quantification/default/strategy/simple/strategy.py +8 -0
- quantification/default/trader/__init__.py +2 -0
- quantification/default/trader/a_factor/__init__.py +1 -0
- quantification/default/trader/a_factor/trader.py +27 -0
- quantification/default/trader/simple/__init__.py +1 -0
- quantification/default/trader/simple/trader.py +8 -0
- quantification/default/trigger/__init__.py +1 -0
- quantification/default/trigger/trigger.py +63 -0
- quantification/default/use/__init__.py +1 -0
- quantification/default/use/factors/__init__.py +2 -0
- quantification/default/use/factors/factor.py +205 -0
- quantification/default/use/factors/use.py +38 -0
- quantification-0.1.1.dist-info/METADATA +19 -0
- quantification-0.1.1.dist-info/RECORD +76 -0
- {quantification-0.1.0.dist-info → quantification-0.1.1.dist-info}/WHEEL +1 -1
- quantification-0.1.0.dist-info/METADATA +0 -13
- quantification-0.1.0.dist-info/RECORD +0 -4
@@ -0,0 +1,213 @@
|
|
1
|
+
from enum import Enum, auto
|
2
|
+
|
3
|
+
|
4
|
+
class Field(Enum):
|
5
|
+
"""
|
6
|
+
----------------------------------------------------------------
|
7
|
+
股票 ST
|
8
|
+
----------------------------------------------------------------
|
9
|
+
"""
|
10
|
+
ST_开盘价 = auto()
|
11
|
+
ST_最高价 = auto()
|
12
|
+
ST_最低价 = auto()
|
13
|
+
ST_收盘价 = auto()
|
14
|
+
ST_昨收价 = auto() # 后复权
|
15
|
+
ST_涨跌额 = auto()
|
16
|
+
ST_涨跌幅 = auto() # 后复权
|
17
|
+
ST_成交量 = auto()
|
18
|
+
ST_成交额 = auto() # 万元
|
19
|
+
ST_振幅 = auto()
|
20
|
+
ST_换手率 = auto()
|
21
|
+
ST_自由流通股换手率 = auto()
|
22
|
+
ST_量比 = auto()
|
23
|
+
ST_总市值 = auto()
|
24
|
+
ST_流通市值 = auto()
|
25
|
+
|
26
|
+
"""
|
27
|
+
----------------------------------------------------------------
|
28
|
+
利润表 IS
|
29
|
+
----------------------------------------------------------------
|
30
|
+
"""
|
31
|
+
IS_营业总收入 = auto()
|
32
|
+
IS_营业总成本 = auto()
|
33
|
+
IS_营业利润 = auto()
|
34
|
+
IS_利润总额 = auto()
|
35
|
+
IS_息税前利润 = auto()
|
36
|
+
IS_息税折旧摊销前利润 = auto()
|
37
|
+
IS_所得税费用 = auto()
|
38
|
+
IS_净利润 = auto()
|
39
|
+
IS_归母净利润 = auto()
|
40
|
+
IS_综合收益 = auto()
|
41
|
+
IS_归母综合收益 = auto()
|
42
|
+
|
43
|
+
IS_销售费用 = auto()
|
44
|
+
IS_管理费用 = auto()
|
45
|
+
IS_财务费用 = auto()
|
46
|
+
IS_研发费用 = auto()
|
47
|
+
|
48
|
+
"""
|
49
|
+
----------------------------------------------------------------
|
50
|
+
资产负债表 BS
|
51
|
+
----------------------------------------------------------------
|
52
|
+
"""
|
53
|
+
BS_总股本 = auto()
|
54
|
+
BS_库存股 = auto()
|
55
|
+
BS_资本公积 = auto()
|
56
|
+
BS_盈余公积 = auto()
|
57
|
+
BS_未分配利润 = auto()
|
58
|
+
BS_股东权益合计 = auto()
|
59
|
+
BS_归母股东权益合计 = auto()
|
60
|
+
|
61
|
+
BS_货币资金 = auto()
|
62
|
+
BS_交易性金融资产 = auto()
|
63
|
+
BS_应收票据 = auto()
|
64
|
+
BS_应收账款 = auto()
|
65
|
+
BS_应收股利 = auto()
|
66
|
+
BS_应收利息 = auto()
|
67
|
+
BS_其他应收款 = auto()
|
68
|
+
BS_预付款项 = auto()
|
69
|
+
BS_存货 = auto()
|
70
|
+
BS_一年内到期的非流动资产 = auto()
|
71
|
+
BS_可供出售金融资产 = auto()
|
72
|
+
BS_持有至到期投资 = auto()
|
73
|
+
BS_长期股权投资 = auto()
|
74
|
+
BS_投资性房地产 = auto()
|
75
|
+
BS_定期存款 = auto()
|
76
|
+
BS_长期应收款 = auto()
|
77
|
+
BS_固定资产 = auto()
|
78
|
+
BS_在建工程 = auto()
|
79
|
+
BS_无形资产 = auto()
|
80
|
+
BS_研发支出 = auto()
|
81
|
+
BS_商誉 = auto()
|
82
|
+
BS_流动资产合计 = auto()
|
83
|
+
BS_非流动资产合计 = auto()
|
84
|
+
BS_资产合计 = auto()
|
85
|
+
|
86
|
+
BS_短期借款 = auto()
|
87
|
+
BS_长期借款 = auto()
|
88
|
+
BS_交易性金融负债 = auto()
|
89
|
+
BS_应付票据 = auto()
|
90
|
+
BS_应付账款 = auto()
|
91
|
+
BS_应付职工薪酬 = auto()
|
92
|
+
BS_应付利息 = auto()
|
93
|
+
BS_应付股利 = auto()
|
94
|
+
BS_应付短期债券 = auto()
|
95
|
+
BS_应付债券 = auto()
|
96
|
+
BS_长期应付款 = auto()
|
97
|
+
BS_预收款项 = auto()
|
98
|
+
BS_一年内到期的非流动负债 = auto()
|
99
|
+
BS_流动负债合计 = auto()
|
100
|
+
BS_非流动负债合计 = auto()
|
101
|
+
BS_负债合计 = auto()
|
102
|
+
|
103
|
+
"""
|
104
|
+
----------------------------------------------------------------
|
105
|
+
现金流量表 CS
|
106
|
+
----------------------------------------------------------------
|
107
|
+
"""
|
108
|
+
CS_经营活动现金流入 = auto()
|
109
|
+
CS_经营活动现金流出 = auto()
|
110
|
+
CS_经营活动净现金流 = auto()
|
111
|
+
|
112
|
+
CS_投资活动现金流入 = auto()
|
113
|
+
CS_投资活动现金流出 = auto()
|
114
|
+
CS_投资活动净现金流 = auto()
|
115
|
+
|
116
|
+
CS_筹资活动现金流入 = auto()
|
117
|
+
CS_筹资活动现金流出 = auto()
|
118
|
+
CS_筹资活动净现金流 = auto()
|
119
|
+
|
120
|
+
CS_现金及现金等价物净增加额 = auto()
|
121
|
+
|
122
|
+
"""
|
123
|
+
----------------------------------------------------------------
|
124
|
+
财务指标 FI
|
125
|
+
----------------------------------------------------------------
|
126
|
+
"""
|
127
|
+
|
128
|
+
FI_净资产收益率 = auto()
|
129
|
+
|
130
|
+
"""
|
131
|
+
----------------------------------------------------------------
|
132
|
+
指数 IN
|
133
|
+
----------------------------------------------------------------
|
134
|
+
"""
|
135
|
+
IN_开盘点位 = auto()
|
136
|
+
IN_收盘点位 = auto()
|
137
|
+
IN_最高点位 = auto()
|
138
|
+
IN_最低点位 = auto()
|
139
|
+
|
140
|
+
"""
|
141
|
+
----------------------------------------------------------------
|
142
|
+
杠杆率 LEV
|
143
|
+
----------------------------------------------------------------
|
144
|
+
"""
|
145
|
+
LEV_居民部门 = auto() # 注意单位: %
|
146
|
+
LEV_非金融企业部门 = auto() # 注意单位: %
|
147
|
+
LEV_政府部门 = auto() # 注意单位: %
|
148
|
+
LEV_中央政府 = auto() # 注意单位: %
|
149
|
+
LEV_地方政府 = auto() # 注意单位: %
|
150
|
+
LEV_实体经济部门 = auto() # 注意单位: %
|
151
|
+
LEV_金融部门资产方 = auto() # 注意单位: %
|
152
|
+
LEV_金融部门负债方 = auto() # 注意单位: %
|
153
|
+
|
154
|
+
"""
|
155
|
+
----------------------------------------------------------------
|
156
|
+
企业商品交易价格指数 CGPI
|
157
|
+
----------------------------------------------------------------
|
158
|
+
"""
|
159
|
+
CGPI_总指数 = auto()
|
160
|
+
CGPI_总指数同比增长 = auto() # 注意单位: %
|
161
|
+
CGPI_总指数环比增长 = auto() # 注意单位: %
|
162
|
+
CGPI_农产品 = auto()
|
163
|
+
CGPI_农产品同比增长 = auto() # 注意单位: %
|
164
|
+
CGPI_农产品环比增长 = auto() # 注意单位: %
|
165
|
+
CGPI_矿产品 = auto()
|
166
|
+
CGPI_矿产品同比增长 = auto() # 注意单位: %
|
167
|
+
CGPI_矿产品环比增长 = auto() # 注意单位: %
|
168
|
+
CGPI_煤油电 = auto()
|
169
|
+
CGPI_煤油电同比增长 = auto() # 注意单位: %
|
170
|
+
CGPI_煤油电环比增长 = auto() # 注意单位: %
|
171
|
+
|
172
|
+
"""
|
173
|
+
----------------------------------------------------------------
|
174
|
+
国际直接投资 FDI
|
175
|
+
----------------------------------------------------------------
|
176
|
+
"""
|
177
|
+
FDI_当月 = auto() # 注意单位: 万美元
|
178
|
+
FDI_当月同比增长 = auto() # 注意单位: %
|
179
|
+
FDI_当月环比增长 = auto() # 注意单位: %
|
180
|
+
FDI_累计 = auto() # 注意单位: 万美元
|
181
|
+
FDI_累计同比增长 = auto() # 注意单位: %
|
182
|
+
|
183
|
+
"""
|
184
|
+
----------------------------------------------------------------
|
185
|
+
利率 RATE
|
186
|
+
----------------------------------------------------------------
|
187
|
+
"""
|
188
|
+
RATE_LPR1年 = auto() # 注意单位: %
|
189
|
+
RATE_LPR5年 = auto() # 注意单位: %
|
190
|
+
RATE_短期贷款 = auto() # 注意单位: %
|
191
|
+
RATE_中长期贷款 = auto() # 注意单位: %
|
192
|
+
|
193
|
+
"""
|
194
|
+
----------------------------------------------------------------
|
195
|
+
社会融资规模 AFRE
|
196
|
+
----------------------------------------------------------------
|
197
|
+
"""
|
198
|
+
AFRE_总增量 = auto() # 注意单位: 亿元
|
199
|
+
AFRE_人民币贷款增量 = auto() # 注意单位: 亿元
|
200
|
+
AFRE_委托贷款外币贷款增量 = auto() # 注意单位: 折合人民币, 亿元
|
201
|
+
AFRE_委托贷款增量 = auto() # 注意单位: 亿元
|
202
|
+
AFRE_信托贷款增量 = auto() # 注意单位: 亿元
|
203
|
+
AFRE_未贴现银行承兑汇票增量 = auto() # 注意单位: 亿元
|
204
|
+
AFRE_企业债券增量 = auto() # 注意单位: 亿元
|
205
|
+
AFRE_非金融企业境内股票融资增量 = auto() # 注意单位: 亿元
|
206
|
+
|
207
|
+
def __repr__(self):
|
208
|
+
return self.name
|
209
|
+
|
210
|
+
__str__ = __repr__
|
211
|
+
|
212
|
+
|
213
|
+
__all__ = ["Field"]
|
@@ -0,0 +1,42 @@
|
|
1
|
+
from datetime import datetime
|
2
|
+
|
3
|
+
import pandas as pd
|
4
|
+
|
5
|
+
from .field import Field
|
6
|
+
|
7
|
+
|
8
|
+
class DataPanel(pd.DataFrame):
|
9
|
+
def __init__(self, data: pd.DataFrame = None, mask: pd.DataFrame = None):
|
10
|
+
data = data if data is not None else pd.DataFrame()
|
11
|
+
mask = mask if mask is not None else pd.DataFrame()
|
12
|
+
self.mask = mask
|
13
|
+
super().__init__(data)
|
14
|
+
|
15
|
+
if self.shape[0] != 0:
|
16
|
+
assert type(self.index) == pd.DatetimeIndex, \
|
17
|
+
f"DataPanel data的索引必须为pd.DatetimeIndex, 实际为{type(self.index)}"
|
18
|
+
|
19
|
+
for col in self.columns:
|
20
|
+
assert type(col) == Field, \
|
21
|
+
f"DataPanel data的列必须为core.Field, {col}实际为{type(col)}"
|
22
|
+
|
23
|
+
if self.mask.shape[0] != 0:
|
24
|
+
assert type(self.index) == pd.DatetimeIndex, \
|
25
|
+
f"DataPanel mask的索引必须为pd.DatetimeIndex, 实际为{type(self.index)}"
|
26
|
+
|
27
|
+
for col in self.columns:
|
28
|
+
assert type(col) == Field, \
|
29
|
+
f"DataPanel mask的列必须为core.Field, {col}实际为{type(col)}"
|
30
|
+
|
31
|
+
assert self.columns.equals(self.mask.columns) and self.shape == self.mask.shape, \
|
32
|
+
f"DataPanel data与mask的结构不一致, data:\n{self}\nmask:\n{self.mask} "
|
33
|
+
|
34
|
+
def __lshift__(self, other: "DataPanel"):
|
35
|
+
assert isinstance(other, DataPanel), f"DataPanel只能与DataPanel进行lshift操作, 实际为{type(other)}"
|
36
|
+
return DataPanel(self.join(other, how='outer'), self.mask.join(other.mask, how='outer'))
|
37
|
+
|
38
|
+
def on(self, d: datetime) -> pd.DataFrame:
|
39
|
+
return self.where((self.mask <= d) | self.isna(), other="invalid")[:d.date()]
|
40
|
+
|
41
|
+
|
42
|
+
__all__ = ["DataPanel"]
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import datetime
|
2
|
+
from abc import ABCMeta
|
3
|
+
from dataclasses import dataclass
|
4
|
+
|
5
|
+
from typing import Callable
|
6
|
+
|
7
|
+
|
8
|
+
@dataclass
|
9
|
+
class Env:
|
10
|
+
date: datetime.date
|
11
|
+
time: datetime.time
|
12
|
+
|
13
|
+
|
14
|
+
class EnvGetter(metaclass=ABCMeta):
|
15
|
+
getter: Callable[[], Env] | None = None
|
16
|
+
|
17
|
+
@property
|
18
|
+
def env(self) -> Env | None:
|
19
|
+
if self.__class__.getter is not None:
|
20
|
+
return self.__class__.getter()
|
21
|
+
|
22
|
+
return None
|
23
|
+
|
24
|
+
|
25
|
+
__all__ = ['EnvGetter', "Env"]
|
@@ -0,0 +1,94 @@
|
|
1
|
+
import sys
|
2
|
+
from datetime import datetime
|
3
|
+
|
4
|
+
from loguru import logger as _logger
|
5
|
+
from pydantic import BaseModel
|
6
|
+
from loguru._datetime import datetime as _datetime
|
7
|
+
|
8
|
+
from .env import EnvGetter
|
9
|
+
from .configure import config
|
10
|
+
|
11
|
+
|
12
|
+
class Record(BaseModel):
|
13
|
+
file: str
|
14
|
+
func: str
|
15
|
+
line: int
|
16
|
+
level: str
|
17
|
+
message: str
|
18
|
+
|
19
|
+
|
20
|
+
class Logger(EnvGetter):
|
21
|
+
def __init__(self):
|
22
|
+
super().__init__()
|
23
|
+
|
24
|
+
_logger.remove()
|
25
|
+
_logger.add(
|
26
|
+
sink=self.sink,
|
27
|
+
colorize=True, # 在支持颜色的终端显示彩色日志
|
28
|
+
backtrace=True, # 记录异常堆栈
|
29
|
+
diagnose=True, # 显示变量值
|
30
|
+
level=config.log_level,
|
31
|
+
format=(
|
32
|
+
"<g>{time:YYYY-MM-DD HH:mm:ss}</g> "
|
33
|
+
"[<lvl>{level:^7}</lvl>] "
|
34
|
+
"|函数 <r><u>{function}</u></r>: <c>{line}</c>行| "
|
35
|
+
"{message}"
|
36
|
+
),
|
37
|
+
)
|
38
|
+
|
39
|
+
self.records: dict[str, list[Record]] = {}
|
40
|
+
|
41
|
+
def sink(self, message):
|
42
|
+
if not self.env:
|
43
|
+
return
|
44
|
+
|
45
|
+
sys.stdout.write(message)
|
46
|
+
sys.stdout.flush()
|
47
|
+
|
48
|
+
datetime_str = message.record["time"].isoformat()
|
49
|
+
if datetime_str not in self.records:
|
50
|
+
self.records[datetime_str] = []
|
51
|
+
|
52
|
+
self.records[datetime_str].append(Record(
|
53
|
+
file=message.record["file"].name,
|
54
|
+
func=message.record["function"],
|
55
|
+
line=message.record["line"],
|
56
|
+
level=message.record["level"].name,
|
57
|
+
message=message.record["message"]
|
58
|
+
))
|
59
|
+
|
60
|
+
def hijack(self):
|
61
|
+
if env := self.env:
|
62
|
+
current = datetime.combine(env.date, env.time)
|
63
|
+
else:
|
64
|
+
current = datetime.now()
|
65
|
+
return _logger.opt(depth=1).patch(lambda r: r.update(time=_datetime(
|
66
|
+
year=current.year,
|
67
|
+
month=current.month,
|
68
|
+
day=current.day,
|
69
|
+
hour=current.hour,
|
70
|
+
minute=current.minute,
|
71
|
+
)))
|
72
|
+
|
73
|
+
def trace(self, msg):
|
74
|
+
self.hijack().trace(msg)
|
75
|
+
|
76
|
+
def debug(self, msg):
|
77
|
+
self.hijack().debug(msg)
|
78
|
+
|
79
|
+
def success(self, msg):
|
80
|
+
self.hijack().success(msg)
|
81
|
+
|
82
|
+
def info(self, msg):
|
83
|
+
self.hijack().info(msg)
|
84
|
+
|
85
|
+
def warning(self, msg):
|
86
|
+
self.hijack().warning(msg)
|
87
|
+
|
88
|
+
def error(self, msg):
|
89
|
+
self.hijack().error(msg)
|
90
|
+
|
91
|
+
|
92
|
+
logger = Logger()
|
93
|
+
|
94
|
+
__all__ = ["logger", "Record"]
|
@@ -0,0 +1,66 @@
|
|
1
|
+
from typing import Any
|
2
|
+
|
3
|
+
from .base_use import WrappedFunc, Func
|
4
|
+
from .base_trigger import Trigger
|
5
|
+
|
6
|
+
|
7
|
+
class Hook:
|
8
|
+
def __init__(
|
9
|
+
self,
|
10
|
+
func: WrappedFunc,
|
11
|
+
*,
|
12
|
+
once: bool = False,
|
13
|
+
):
|
14
|
+
self.func = func
|
15
|
+
self.once = once
|
16
|
+
self.triggered = False
|
17
|
+
|
18
|
+
@property
|
19
|
+
def active(self) -> bool:
|
20
|
+
if self.once:
|
21
|
+
return not self.triggered
|
22
|
+
|
23
|
+
return True
|
24
|
+
|
25
|
+
def __call__(self, **kwargs):
|
26
|
+
self.triggered = True
|
27
|
+
return self.func(**kwargs)
|
28
|
+
|
29
|
+
def __repr__(self) -> str:
|
30
|
+
return f"<Hook {self.func}>"
|
31
|
+
|
32
|
+
__str__ = __repr__
|
33
|
+
|
34
|
+
|
35
|
+
class BaseStrategy:
|
36
|
+
def __init__(self):
|
37
|
+
self.context: dict[str, Any] = {}
|
38
|
+
self.hooks: dict[Trigger, list[Hook]] = {}
|
39
|
+
|
40
|
+
def on(self, trigger: Trigger, *, once: bool = False):
|
41
|
+
def wrapper(func: Func | WrappedFunc) -> WrappedFunc:
|
42
|
+
wrapped_func = func if isinstance(func, WrappedFunc) else WrappedFunc(func)
|
43
|
+
|
44
|
+
if not self.hooks.get(trigger):
|
45
|
+
self.hooks[trigger] = []
|
46
|
+
|
47
|
+
self.hooks[trigger].append(Hook(wrapped_func, once=once))
|
48
|
+
|
49
|
+
return wrapped_func
|
50
|
+
|
51
|
+
return wrapper
|
52
|
+
|
53
|
+
def triggered(self, **kwargs) -> list[Hook]:
|
54
|
+
triggered_hooks: list[Hook] = []
|
55
|
+
for trigger, hooks in self.hooks.items():
|
56
|
+
if not trigger.fulfilled(**kwargs):
|
57
|
+
continue
|
58
|
+
|
59
|
+
for hook in hooks:
|
60
|
+
if hook.active:
|
61
|
+
triggered_hooks.append(hook)
|
62
|
+
|
63
|
+
return triggered_hooks
|
64
|
+
|
65
|
+
|
66
|
+
__all__ = ["BaseStrategy"]
|
@@ -0,0 +1,69 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
|
3
|
+
from ..util import inject
|
4
|
+
|
5
|
+
|
6
|
+
class Condition(ABC):
|
7
|
+
@abstractmethod
|
8
|
+
def __call__(self, **kwargs):
|
9
|
+
raise NotImplementedError
|
10
|
+
|
11
|
+
@abstractmethod
|
12
|
+
def name(self) -> str:
|
13
|
+
raise NotImplementedError
|
14
|
+
|
15
|
+
def settle(self, **kwargs):
|
16
|
+
...
|
17
|
+
|
18
|
+
def __repr__(self):
|
19
|
+
return f"Condition({self.name()})"
|
20
|
+
|
21
|
+
__str__ = __repr__
|
22
|
+
|
23
|
+
|
24
|
+
class Trigger:
|
25
|
+
def __init__(self, *conditions: Condition):
|
26
|
+
self.conditions = conditions
|
27
|
+
|
28
|
+
def fulfilled(self, **kwargs) -> bool:
|
29
|
+
is_fulfilled = all([inject(condition, **kwargs) for condition in self.conditions])
|
30
|
+
|
31
|
+
if not is_fulfilled:
|
32
|
+
return False
|
33
|
+
|
34
|
+
for condition in self.conditions:
|
35
|
+
inject(condition.settle, **kwargs)
|
36
|
+
|
37
|
+
return True
|
38
|
+
|
39
|
+
def __and__(self, other):
|
40
|
+
if other is None:
|
41
|
+
return self
|
42
|
+
|
43
|
+
if isinstance(other, Trigger):
|
44
|
+
return Trigger(*self.conditions, *other.conditions)
|
45
|
+
|
46
|
+
raise NotImplementedError(f"不支持合并条件{other}")
|
47
|
+
|
48
|
+
def __rand__(self, other):
|
49
|
+
if other is None:
|
50
|
+
return self
|
51
|
+
|
52
|
+
if isinstance(other, Trigger):
|
53
|
+
return Trigger(*other.conditions, *self.conditions)
|
54
|
+
|
55
|
+
raise NotImplementedError(f"不支持合并条件{other}")
|
56
|
+
|
57
|
+
def __or__(self, other):
|
58
|
+
raise NotImplementedError(f"不支持or运算")
|
59
|
+
|
60
|
+
def __call__(self, *args, **kwargs):
|
61
|
+
return self
|
62
|
+
|
63
|
+
def __repr__(self):
|
64
|
+
return f"<Trigger {self.conditions}>"
|
65
|
+
|
66
|
+
__str__ = __repr__
|
67
|
+
|
68
|
+
|
69
|
+
__all__ = ["Trigger", "Condition"]
|
@@ -0,0 +1,69 @@
|
|
1
|
+
import inspect
|
2
|
+
from abc import ABC, abstractmethod
|
3
|
+
from typing import Generator, Callable, TYPE_CHECKING
|
4
|
+
|
5
|
+
from ..util import inject
|
6
|
+
|
7
|
+
if TYPE_CHECKING:
|
8
|
+
from ..trader import BaseOrder, Result
|
9
|
+
|
10
|
+
OrderGenerator = Generator["BaseOrder", "Result", None]
|
11
|
+
CommonFunc = Callable[..., None]
|
12
|
+
GeneratorFunc = Callable[..., OrderGenerator]
|
13
|
+
Func = CommonFunc | GeneratorFunc
|
14
|
+
|
15
|
+
|
16
|
+
class Injection(ABC):
|
17
|
+
name: str
|
18
|
+
|
19
|
+
@abstractmethod
|
20
|
+
def __init__(self, *args, **kwargs) -> None:
|
21
|
+
raise NotImplementedError
|
22
|
+
|
23
|
+
def __init_subclass__(cls, **kwargs):
|
24
|
+
assert hasattr(cls, "name"), \
|
25
|
+
"Injection类需要指定参数名"
|
26
|
+
|
27
|
+
assert isinstance(cls.name, str), \
|
28
|
+
"Injection指定的参数名必须为str"
|
29
|
+
|
30
|
+
@abstractmethod
|
31
|
+
def __call__(self, **kwargs):
|
32
|
+
raise NotImplementedError
|
33
|
+
|
34
|
+
|
35
|
+
class Use:
|
36
|
+
def __init__(self, injection: type[Injection]):
|
37
|
+
self.injection = injection
|
38
|
+
|
39
|
+
def __call__(self, *args, **kwargs):
|
40
|
+
def wrapper(func: Func | WrappedFunc) -> WrappedFunc:
|
41
|
+
wrapped_func = func if isinstance(func, WrappedFunc) else WrappedFunc(func)
|
42
|
+
wrapped_func.register(self.injection(*args, **kwargs))
|
43
|
+
return wrapped_func
|
44
|
+
|
45
|
+
return wrapper
|
46
|
+
|
47
|
+
|
48
|
+
class WrappedFunc:
|
49
|
+
def __init__(self, func: Func):
|
50
|
+
self.func = func
|
51
|
+
self.injections: list[Injection] = []
|
52
|
+
|
53
|
+
def register(self, injection: Injection):
|
54
|
+
self.injections.append(injection)
|
55
|
+
|
56
|
+
def __call__(self, **kwargs):
|
57
|
+
result = inject(self.func, **{
|
58
|
+
**kwargs,
|
59
|
+
**{injection.name: injection(**kwargs) for injection in self.injections}
|
60
|
+
})
|
61
|
+
return result if not inspect.isgenerator(result) else (yield from result)
|
62
|
+
|
63
|
+
def __repr__(self) -> str:
|
64
|
+
return f"<WrappedFunc {self.func.__name__}>"
|
65
|
+
|
66
|
+
__str__ = __repr__
|
67
|
+
|
68
|
+
|
69
|
+
__all__ = ["Use", "Injection", "WrappedFunc", "Func"]
|
@@ -0,0 +1,45 @@
|
|
1
|
+
from typing import TypeVar, Generic, TYPE_CHECKING
|
2
|
+
from abc import ABC, abstractmethod
|
3
|
+
|
4
|
+
if TYPE_CHECKING:
|
5
|
+
from ..asset import BaseAsset
|
6
|
+
|
7
|
+
AssetType = TypeVar('AssetType', bound="BaseAsset")
|
8
|
+
|
9
|
+
|
10
|
+
class BaseOrder(Generic[AssetType], ABC):
|
11
|
+
def __init__(self, asset: AssetType, category: str, *args, **kwargs):
|
12
|
+
self.asset = asset
|
13
|
+
self.category = category
|
14
|
+
|
15
|
+
def type(self):
|
16
|
+
return self.__class__.__name__
|
17
|
+
|
18
|
+
@property
|
19
|
+
@abstractmethod
|
20
|
+
def extra(self):
|
21
|
+
raise NotImplementedError
|
22
|
+
|
23
|
+
@abstractmethod
|
24
|
+
def __repr__(self):
|
25
|
+
raise NotImplementedError
|
26
|
+
|
27
|
+
__str__ = __repr__
|
28
|
+
|
29
|
+
|
30
|
+
OrderType = TypeVar('OrderType', bound=BaseOrder)
|
31
|
+
|
32
|
+
|
33
|
+
class Result(Generic[OrderType]):
|
34
|
+
def __init__(self, *, order: OrderType, sold: list["BaseAsset"], brought: list["BaseAsset"]):
|
35
|
+
self.order = order
|
36
|
+
self.sold = sold
|
37
|
+
self.brought = brought
|
38
|
+
|
39
|
+
def __repr__(self):
|
40
|
+
return f"<Result sold={self.sold} brought={self.brought}>"
|
41
|
+
|
42
|
+
__str__ = __repr__
|
43
|
+
|
44
|
+
|
45
|
+
__all__ = ['BaseOrder', 'Result']
|