Initial commit
This commit is contained in:
+137
@@ -0,0 +1,137 @@
|
||||
from enum import Enum, auto
|
||||
from typing import Literal
|
||||
|
||||
|
||||
class DATA_SRC(Enum):
|
||||
BAO_STOCK = auto()
|
||||
CCXT = auto()
|
||||
CSV = auto()
|
||||
|
||||
|
||||
class KL_TYPE(Enum):
|
||||
K_1M = auto()
|
||||
K_DAY = auto()
|
||||
K_WEEK = auto()
|
||||
K_MON = auto()
|
||||
K_YEAR = auto()
|
||||
K_5M = auto()
|
||||
K_15M = auto()
|
||||
K_30M = auto()
|
||||
K_60M = auto()
|
||||
K_4H = auto()
|
||||
K_6H = auto()
|
||||
K_8H = auto()
|
||||
K_12H = auto()
|
||||
K_3DAY = auto()
|
||||
K_3M = auto()
|
||||
K_QUARTER = auto()
|
||||
|
||||
|
||||
class KLINE_DIR(Enum):
|
||||
UP = auto()
|
||||
DOWN = auto()
|
||||
COMBINE = auto()
|
||||
INCLUDED = auto()
|
||||
|
||||
|
||||
class FX_TYPE(Enum):
|
||||
BOTTOM = auto()
|
||||
TOP = auto()
|
||||
UNKNOWN = auto()
|
||||
|
||||
|
||||
class BI_DIR(Enum):
|
||||
UP = auto()
|
||||
DOWN = auto()
|
||||
|
||||
class SEG_DIR(Enum):
|
||||
UP = auto()
|
||||
DOWN = auto()
|
||||
|
||||
class BI_TYPE(Enum):
|
||||
UNKNOWN = auto()
|
||||
STRICT = auto()
|
||||
SUB_VALUE = auto() # 次高低点成笔
|
||||
TIAOKONG_THRED = auto()
|
||||
DAHENG = auto()
|
||||
TUIBI = auto()
|
||||
UNSTRICT = auto()
|
||||
TIAOKONG_VALUE = auto()
|
||||
|
||||
|
||||
BSP_MAIN_TYPE = Literal['1', '2', '3']
|
||||
|
||||
|
||||
class BSP_TYPE(Enum):
|
||||
T1 = '1'
|
||||
T1P = '1p'
|
||||
T2 = '2'
|
||||
T2S = '2s'
|
||||
T3A = '3a' # 中枢在1类后面
|
||||
T3B = '3b' # 中枢在1类前面
|
||||
|
||||
def main_type(self) -> BSP_MAIN_TYPE:
|
||||
return self.value[0] # type: ignore
|
||||
|
||||
|
||||
class AUTYPE(Enum):
|
||||
QFQ = auto()
|
||||
HFQ = auto()
|
||||
NONE = auto()
|
||||
|
||||
|
||||
class TREND_TYPE(Enum):
|
||||
MEAN = "mean"
|
||||
MAX = "max"
|
||||
MIN = "min"
|
||||
|
||||
|
||||
class TREND_LINE_SIDE(Enum):
|
||||
INSIDE = auto()
|
||||
OUTSIDE = auto()
|
||||
|
||||
|
||||
class LEFT_SEG_METHOD(Enum):
|
||||
ALL = auto()
|
||||
PEAK = auto()
|
||||
|
||||
|
||||
class FX_CHECK_METHOD(Enum):
|
||||
STRICT = auto()
|
||||
LOSS = auto()
|
||||
HALF = auto()
|
||||
TOTALLY = auto()
|
||||
|
||||
|
||||
class SEG_TYPE(Enum):
|
||||
BI = auto()
|
||||
SEG = auto()
|
||||
|
||||
|
||||
class MACD_ALGO(Enum):
|
||||
AREA = auto()
|
||||
PEAK = auto()
|
||||
FULL_AREA = auto()
|
||||
DIFF = auto()
|
||||
SLOPE = auto()
|
||||
AMP = auto()
|
||||
VOLUMN = auto()
|
||||
AMOUNT = auto()
|
||||
VOLUMN_AVG = auto()
|
||||
AMOUNT_AVG = auto()
|
||||
TURNRATE_AVG = auto()
|
||||
RSI = auto()
|
||||
|
||||
|
||||
class DATA_FIELD:
|
||||
FIELD_TIME = "time_key"
|
||||
FIELD_OPEN = "open"
|
||||
FIELD_HIGH = "high"
|
||||
FIELD_LOW = "low"
|
||||
FIELD_CLOSE = "close"
|
||||
FIELD_VOLUME = "volume" # 成交量
|
||||
FIELD_TURNOVER = "turnover" # 成交额
|
||||
FIELD_TURNRATE = "turnover_rate" # 换手率
|
||||
|
||||
|
||||
TRADE_INFO_LST = [DATA_FIELD.FIELD_VOLUME, DATA_FIELD.FIELD_TURNOVER, DATA_FIELD.FIELD_TURNRATE]
|
||||
@@ -0,0 +1,44 @@
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class CTime:
|
||||
def __init__(self, year, month, day, hour, minute, second=0, auto=True):
|
||||
self.year = year
|
||||
self.month = month
|
||||
self.day = day
|
||||
self.hour = hour
|
||||
self.minute = minute
|
||||
self.second = second
|
||||
self.auto = auto # 自适应对天的理解
|
||||
self.set_timestamp() # set self.ts
|
||||
|
||||
def __str__(self):
|
||||
if self.hour == 0 and self.minute == 0:
|
||||
return f"{self.year:04}/{self.month:02}/{self.day:02}"
|
||||
else:
|
||||
return f"{self.year:04}/{self.month:02}/{self.day:02} {self.hour:02}:{self.minute:02}"
|
||||
|
||||
def to_str(self):
|
||||
if self.hour == 0 and self.minute == 0:
|
||||
return f"{self.year:04}/{self.month:02}/{self.day:02}"
|
||||
else:
|
||||
return f"{self.year:04}/{self.month:02}/{self.day:02} {self.hour:02}:{self.minute:02}"
|
||||
|
||||
def toDateStr(self, splt=''):
|
||||
return f"{self.year:04}{splt}{self.month:02}{splt}{self.day:02}"
|
||||
|
||||
def toDate(self):
|
||||
return CTime(self.year, self.month, self.day, 0, 0, auto=False)
|
||||
|
||||
def set_timestamp(self):
|
||||
if self.hour == 0 and self.minute == 0 and self.auto:
|
||||
date = datetime(self.year, self.month, self.day, 23, 59, self.second)
|
||||
else:
|
||||
date = datetime(self.year, self.month, self.day, self.hour, self.minute, self.second)
|
||||
self.ts = date.timestamp()
|
||||
|
||||
def __gt__(self, t2):
|
||||
return self.ts > t2.ts
|
||||
|
||||
def __ge__(self, t2):
|
||||
return self.ts >= t2.ts
|
||||
@@ -0,0 +1,88 @@
|
||||
from enum import IntEnum
|
||||
|
||||
|
||||
class ErrCode(IntEnum):
|
||||
# chan err
|
||||
_CHAN_ERR_BEGIN = 0
|
||||
COMMON_ERROR = 1
|
||||
SRC_DATA_NOT_FOUND = 3
|
||||
SRC_DATA_TYPE_ERR = 4
|
||||
PARA_ERROR = 5
|
||||
EXTRA_KLU_ERR = 6
|
||||
SEG_END_VALUE_ERR = 7
|
||||
SEG_EIGEN_ERR = 8
|
||||
BI_ERR = 9
|
||||
COMBINER_ERR = 10
|
||||
PLOT_ERR = 11
|
||||
MODEL_ERROR = 12
|
||||
SEG_LEN_ERR = 13
|
||||
ENV_CONF_ERR = 14
|
||||
UNKNOWN_DB_TYPE = 15
|
||||
FEATURE_ERROR = 16
|
||||
CONFIG_ERROR = 17
|
||||
SRC_DATA_FORMAT_ERROR = 18
|
||||
_CHAN_ERR_END = 99
|
||||
|
||||
# Trade Error
|
||||
_TRADE_ERR_BEGIN = 100
|
||||
SIGNAL_EXISTED = 101
|
||||
RECORD_NOT_EXIST = 102
|
||||
RECORD_ALREADY_OPENED = 103
|
||||
QUOTA_NOT_ENOUGH = 104
|
||||
RECORD_NOT_OPENED = 105
|
||||
TRADE_UNLOCK_FAIL = 106
|
||||
PLACE_ORDER_FAIL = 107
|
||||
LIST_ORDER_FAIL = 108
|
||||
CANDEL_ORDER_FAIL = 109
|
||||
GET_FUTU_PRICE_FAIL = 110
|
||||
GET_FUTU_LOT_SIZE_FAIL = 111
|
||||
OPEN_RECORD_NOT_WATCHING = 112
|
||||
GET_HOLDING_QTY_FAIL = 113
|
||||
RECORD_CLOSED = 114
|
||||
REQUEST_TRADING_DAYS_FAIL = 115
|
||||
COVER_ORDER_ID_NOT_UNIQUE = 116
|
||||
SIGNAL_TRADED = 117
|
||||
_TRADE_ERR_END = 199
|
||||
|
||||
# KL data Error
|
||||
_KL_ERR_BEGIN = 200
|
||||
PRICE_BELOW_ZERO = 201
|
||||
KL_DATA_NOT_ALIGN = 202
|
||||
KL_DATA_INVALID = 203
|
||||
KL_TIME_INCONSISTENT = 204
|
||||
TRADEINFO_TOO_MUCH_ZERO = 205
|
||||
KL_NOT_MONOTONOUS = 206
|
||||
SNAPSHOT_ERR = 207
|
||||
SUSPENSION = 208 # 疑似停牌
|
||||
STOCK_IPO_TOO_LATE = 209
|
||||
NO_DATA = 210
|
||||
STOCK_NOT_ACTIVE = 211
|
||||
STOCK_PRICE_NOT_ACTIVE = 212
|
||||
_KL_ERR_END = 299
|
||||
|
||||
|
||||
class CChanException(Exception):
|
||||
def __init__(self, message, code=ErrCode.COMMON_ERROR):
|
||||
self.errcode = code
|
||||
self.msg = message
|
||||
Exception.__init__(self, message)
|
||||
|
||||
def is_kldata_err(self):
|
||||
return ErrCode._KL_ERR_BEGIN < self.errcode < ErrCode._KL_ERR_END
|
||||
|
||||
def is_chan_err(self):
|
||||
return ErrCode._CHAN_ERR_BEGIN < self.errcode < ErrCode._CHAN_ERR_END
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
def foo():
|
||||
raise CChanException("XXX", ErrCode.CONFIG_ERROR)
|
||||
|
||||
try:
|
||||
foo()
|
||||
except CChanException as e:
|
||||
print(str(e.errcode))
|
||||
# python3.8 结果为: ErrCode.CONFIG_ERROR
|
||||
# python3.11 结果为:17
|
||||
|
||||
print(e.errcode.name, type(e.errcode.name))
|
||||
@@ -0,0 +1,34 @@
|
||||
import inspect
|
||||
import types
|
||||
|
||||
|
||||
class make_cache:
|
||||
def __init__(self, func):
|
||||
self.func = func
|
||||
|
||||
fargspec = inspect.getfullargspec(func)
|
||||
if len(fargspec.args) != 1 or fargspec.args[0] != "self":
|
||||
raise Exception("@memoize must be `(self)`")
|
||||
|
||||
# set key for this function
|
||||
self.func_key = str(func)
|
||||
|
||||
def __get__(self, instance, cls):
|
||||
if instance is None:
|
||||
raise Exception("@memoize's must be bound")
|
||||
|
||||
if not hasattr(instance, "_memoize_cache"):
|
||||
setattr(instance, "_memoize_cache", {})
|
||||
|
||||
return types.MethodType(self, instance)
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
instance = args[0]
|
||||
cache = instance._memoize_cache
|
||||
|
||||
if self.func_key in cache:
|
||||
return cache[self.func_key]
|
||||
|
||||
result = self.func(*args, **kwargs)
|
||||
cache[self.func_key] = result
|
||||
return result
|
||||
@@ -0,0 +1,59 @@
|
||||
from .CEnum import BI_DIR, KL_TYPE
|
||||
|
||||
|
||||
def kltype_lt_day(_type):
|
||||
return _type in [KL_TYPE.K_1M, KL_TYPE.K_5M, KL_TYPE.K_15M, KL_TYPE.K_30M, KL_TYPE.K_60M]
|
||||
|
||||
|
||||
def kltype_lte_day(_type):
|
||||
return _type in [KL_TYPE.K_1M, KL_TYPE.K_5M, KL_TYPE.K_15M, KL_TYPE.K_30M, KL_TYPE.K_60M, KL_TYPE.K_DAY]
|
||||
|
||||
|
||||
def check_kltype_order(type_list: list):
|
||||
_dict = {
|
||||
KL_TYPE.K_1M: 1,
|
||||
KL_TYPE.K_3M: 2,
|
||||
KL_TYPE.K_5M: 3,
|
||||
KL_TYPE.K_15M: 4,
|
||||
KL_TYPE.K_30M: 5,
|
||||
KL_TYPE.K_60M: 6,
|
||||
KL_TYPE.K_4H: 7, # 4小时
|
||||
KL_TYPE.K_6H: 8, # 6小时
|
||||
KL_TYPE.K_8H: 9, # 8小时
|
||||
KL_TYPE.K_12H: 10, # 12小时
|
||||
KL_TYPE.K_DAY: 11,
|
||||
KL_TYPE.K_3DAY: 12, # 3日线
|
||||
KL_TYPE.K_WEEK: 13,
|
||||
KL_TYPE.K_MON: 14,
|
||||
KL_TYPE.K_QUARTER: 15,
|
||||
KL_TYPE.K_YEAR: 16,
|
||||
}
|
||||
last_lv = float("inf")
|
||||
for kl_type in type_list:
|
||||
cur_lv = _dict[kl_type]
|
||||
assert cur_lv < last_lv, "lv_list的顺序必须从大级别到小级别"
|
||||
last_lv = cur_lv
|
||||
|
||||
|
||||
def revert_bi_dir(dir):
|
||||
return BI_DIR.DOWN if dir == BI_DIR.UP else BI_DIR.UP
|
||||
|
||||
|
||||
def has_overlap(l1, h1, l2, h2, equal=False):
|
||||
return h2 >= l1 and h1 >= l2 if equal else h2 > l1 and h1 > l2
|
||||
|
||||
|
||||
def str2float(s):
|
||||
try:
|
||||
return float(s)
|
||||
except ValueError:
|
||||
return 0.0
|
||||
|
||||
|
||||
def _parse_inf(v):
|
||||
if type(v) == float:
|
||||
if v == float("inf"):
|
||||
v = 'float("inf")'
|
||||
if v == float("-inf"):
|
||||
v = 'float("-inf")'
|
||||
return v
|
||||
Reference in New Issue
Block a user