Initial commit

This commit is contained in:
jackyu66git
2025-06-10 01:16:09 +08:00
commit 05bab03b8c
155 changed files with 19979 additions and 0 deletions
+114
View File
@@ -0,0 +1,114 @@
import baostock as bs
from Common.CEnum import AUTYPE, DATA_FIELD, KL_TYPE
from Common.CTime import CTime
from Common.func_util import kltype_lt_day, str2float
from KLine.KLine_Unit import CKLine_Unit
from .CommonStockAPI import CCommonStockApi
def create_item_dict(data, column_name):
for i in range(len(data)):
data[i] = parse_time_column(data[i]) if i == 0 else str2float(data[i])
return dict(zip(column_name, data))
def parse_time_column(inp):
# 20210902113000000
# 2021-09-13
if len(inp) == 10:
year = int(inp[:4])
month = int(inp[5:7])
day = int(inp[8:10])
hour = minute = 0
elif len(inp) == 17:
year = int(inp[:4])
month = int(inp[4:6])
day = int(inp[6:8])
hour = int(inp[8:10])
minute = int(inp[10:12])
elif len(inp) == 19:
year = int(inp[:4])
month = int(inp[5:7])
day = int(inp[8:10])
hour = int(inp[11:13])
minute = int(inp[14:16])
else:
raise Exception(f"unknown time column from baostock:{inp}")
return CTime(year, month, day, hour, minute)
def GetColumnNameFromFieldList(fileds: str):
_dict = {
"time": DATA_FIELD.FIELD_TIME,
"date": DATA_FIELD.FIELD_TIME,
"open": DATA_FIELD.FIELD_OPEN,
"high": DATA_FIELD.FIELD_HIGH,
"low": DATA_FIELD.FIELD_LOW,
"close": DATA_FIELD.FIELD_CLOSE,
"volume": DATA_FIELD.FIELD_VOLUME,
"amount": DATA_FIELD.FIELD_TURNOVER,
"turn": DATA_FIELD.FIELD_TURNRATE,
}
return [_dict[x] for x in fileds.split(",")]
class CBaoStock(CCommonStockApi):
is_connect = None
def __init__(self, code, k_type=KL_TYPE.K_DAY, begin_date=None, end_date=None, autype=AUTYPE.QFQ):
super(CBaoStock, self).__init__(code, k_type, begin_date, end_date, autype)
def get_kl_data(self):
# 天级别以上才有详细交易信息
if kltype_lt_day(self.k_type):
if not self.is_stock:
raise Exception("没有获取到数据,注意指数是没有分钟级别数据的!")
fields = "time,open,high,low,close"
else:
fields = "date,open,high,low,close,volume,amount,turn"
autype_dict = {AUTYPE.QFQ: "2", AUTYPE.HFQ: "1", AUTYPE.NONE: "3"}
rs = bs.query_history_k_data_plus(
code=self.code,
fields=fields,
start_date=self.begin_date,
end_date=self.end_date,
frequency=self.__convert_type(),
adjustflag=autype_dict[self.autype],
)
if rs.error_code != '0':
raise Exception(rs.error_msg)
while rs.error_code == '0' and rs.next():
yield CKLine_Unit(create_item_dict(rs.get_row_data(), GetColumnNameFromFieldList(fields)))
def SetBasciInfo(self):
rs = bs.query_stock_basic(code=self.code)
if rs.error_code != '0':
raise Exception(rs.error_msg)
code, code_name, ipoDate, outDate, stock_type, status = rs.get_row_data()
self.name = code_name
self.is_stock = (stock_type == '1')
@classmethod
def do_init(cls):
if not cls.is_connect:
cls.is_connect = bs.login()
@classmethod
def do_close(cls):
if cls.is_connect:
bs.logout()
cls.is_connect = None
def __convert_type(self):
_dict = {
KL_TYPE.K_DAY: 'd',
KL_TYPE.K_WEEK: 'w',
KL_TYPE.K_MON: 'm',
KL_TYPE.K_5M: '5',
KL_TYPE.K_15M: '15',
KL_TYPE.K_30M: '30',
KL_TYPE.K_60M: '60',
}
return _dict[self.k_type]
+34
View File
@@ -0,0 +1,34 @@
import abc
from typing import Iterable
from KLine.KLine_Unit import CKLine_Unit
class CCommonStockApi:
def __init__(self, code, k_type, begin_date, end_date, autype):
self.code = code
self.name = None
self.is_stock = None
self.k_type = k_type
self.begin_date = begin_date
self.end_date = end_date
self.autype = autype
self.SetBasciInfo()
@abc.abstractmethod
def get_kl_data(self) -> Iterable[CKLine_Unit]:
pass
@abc.abstractmethod
def SetBasciInfo(self):
pass
@classmethod
@abc.abstractmethod
def do_init(cls):
pass
@classmethod
@abc.abstractmethod
def do_close(cls):
pass
+100
View File
@@ -0,0 +1,100 @@
from datetime import datetime
import pandas as pd
from Common.CEnum import AUTYPE, DATA_FIELD, KL_TYPE
from Common.CTime import CTime
from Common.func_util import kltype_lt_day, str2float
from KLine.KLine_Unit import CKLine_Unit
from .CommonStockAPI import CCommonStockApi
class WebDataAPI(CCommonStockApi):
"""Web应用的数据API适配器,用于将web应用的数据转换为chan.py格式"""
# 类变量用于存储数据
_data_cache = {}
def __init__(self, code, k_type=KL_TYPE.K_DAY, begin_date=None, end_date=None, autype=AUTYPE.QFQ):
super().__init__(code, k_type, begin_date, end_date, autype)
@classmethod
def set_data(cls, code, df_data):
"""设置指定代码的数据"""
cls._data_cache[code] = df_data
def get_kl_data(self):
"""将DataFrame数据转换为CKLine_Unit迭代器"""
df_data = self._data_cache.get(self.code)
if df_data is None or len(df_data) == 0:
return
# 确保数据按时间排序并重置索引
df_data = df_data.sort_values('date').reset_index(drop=True)
print(f"WebDataAPI: 处理 {len(df_data)} 条K线数据,K线级别: {self.k_type}")
# 检查并处理重复时间
if 'timestamp' in df_data.columns:
df_data = df_data.drop_duplicates(subset=['timestamp'], keep='last')
df_data = df_data.sort_values('timestamp').reset_index(drop=True)
prev_timestamp = None
for idx, row in df_data.iterrows():
# 转换时间格式
if isinstance(row['date'], str):
time_obj = datetime.fromisoformat(row['date'].replace('Z', '+00:00'))
else:
time_obj = row['date']
# 检查时间戳确保单调递增
if 'timestamp' in row:
current_timestamp = row['timestamp']
if prev_timestamp is not None and current_timestamp <= prev_timestamp:
print(f"跳过重复或倒序时间戳: {current_timestamp}, 上个时间戳: {prev_timestamp}")
continue
prev_timestamp = current_timestamp
# 创建CTime对象 - 根据K线级别智能决定auto参数
# 对于日线及以上级别,且时分秒为0的情况,使用auto=True
# 对于分钟级别或有具体时分的数据,使用auto=False确保精确时间
use_auto = False # 默认不使用auto,确保时间精确
# 只有在日线级别且时分秒都为0时才考虑使用auto
if self.k_type in [KL_TYPE.K_DAY, KL_TYPE.K_WEEK, KL_TYPE.K_MON]:
if time_obj.hour == 0 and time_obj.minute == 0 and time_obj.second == 0:
use_auto = True
ctime = CTime(
time_obj.year,
time_obj.month,
time_obj.day,
time_obj.hour,
time_obj.minute,
time_obj.second,
auto=use_auto
)
# 输出详细调试信息(只输出前几条)
if idx < 3:
print(f"{idx+1}条数据: 原始时间={time_obj}, CTime={ctime}, auto={use_auto}, timestamp={ctime.ts}")
# 创建数据字典
data_dict = {
DATA_FIELD.FIELD_TIME: ctime,
DATA_FIELD.FIELD_OPEN: float(row['open']),
DATA_FIELD.FIELD_HIGH: float(row['high']),
DATA_FIELD.FIELD_LOW: float(row['low']),
DATA_FIELD.FIELD_CLOSE: float(row['close']),
DATA_FIELD.FIELD_VOLUME: float(row['volume']) if 'volume' in row and pd.notna(row['volume']) else 0.0
}
yield CKLine_Unit(data_dict, autofix=True)
def SetBasciInfo(self):
pass
@classmethod
def do_init(cls):
pass
@classmethod
def do_close(cls):
pass
View File
+102
View File
@@ -0,0 +1,102 @@
from datetime import datetime
import ccxt
import json
from Common.CEnum import AUTYPE, DATA_FIELD, KL_TYPE
from Common.CTime import CTime
from Common.func_util import kltype_lt_day, str2float
from KLine.KLine_Unit import CKLine_Unit
from .CommonStockAPI import CCommonStockApi
def GetColumnNameFromFieldList(fileds: str):
_dict = {
"time": DATA_FIELD.FIELD_TIME,
"open": DATA_FIELD.FIELD_OPEN,
"high": DATA_FIELD.FIELD_HIGH,
"low": DATA_FIELD.FIELD_LOW,
"close": DATA_FIELD.FIELD_CLOSE,
}
return [_dict[x] for x in fileds.split(",")]
class CCXT(CCommonStockApi):
is_connect = None
def __init__(self, code, k_type=KL_TYPE.K_DAY, begin_date=None, end_date=None, autype=AUTYPE.QFQ):
super(CCXT, self).__init__(code, k_type, begin_date, end_date, autype)
def get_kl_data(self):
fields = "time,open,high,low,close"
exchange = ccxt.binance()
timeframe = self.__convert_type()
since_date = exchange.parse8601(f'{self.begin_date}T08:00:00')
#file_path = "BTC_USDT-5m-futures.json"
#with open(file_path, 'r') as file:
# data = json.load(file)
data = exchange.fetch_ohlcv(self.code, timeframe, since=since_date)
for item in data:
time_obj = datetime.fromtimestamp(item[0] / 1000)
time_str = time_obj.strftime('%Y-%m-%d %H:%M:%S')
item_data = [
time_str,
item[1],
item[2],
item[3],
item[4],
item[5]
]
yield CKLine_Unit(self.create_item_dict(item_data, GetColumnNameFromFieldList(fields)), autofix=True)
def SetBasciInfo(self):
pass
@classmethod
def do_init(cls):
pass
@classmethod
def do_close(cls):
pass
def __convert_type(self):
_dict = {
KL_TYPE.K_DAY: '1d',
KL_TYPE.K_WEEK: '1w',
KL_TYPE.K_MON: '1M',
KL_TYPE.K_5M: '5m',
KL_TYPE.K_15M: '15m',
KL_TYPE.K_30M: '30m',
KL_TYPE.K_60M: '1h',
KL_TYPE.K_1M: '1m', # need to add this 1m
}
return _dict[self.k_type]
def parse_time_column(self, inp):
if len(inp) == 10:
year = int(inp[:4])
month = int(inp[5:7])
day = int(inp[8:10])
hour = minute = 0
elif len(inp) == 17:
year = int(inp[:4])
month = int(inp[4:6])
day = int(inp[6:8])
hour = int(inp[8:10])
minute = int(inp[10:12])
elif len(inp) == 19:
year = int(inp[:4])
month = int(inp[5:7])
day = int(inp[8:10])
hour = int(inp[11:13])
minute = int(inp[14:16])
else:
raise Exception(f"unknown time column from TradingView:{inp}")
return CTime(year, month, day, hour, minute, auto=not kltype_lt_day(self.k_type))
def create_item_dict(self, data, column_name):
for i in range(len(data)):
data[i] = self.parse_time_column(data[i]) if i == 0 else str2float(data[i])
return dict(zip(column_name, data))
+86
View File
@@ -0,0 +1,86 @@
import os
from Common.CEnum import DATA_FIELD, KL_TYPE
from Common.ChanException import CChanException, ErrCode
from Common.CTime import CTime
from Common.func_util import str2float
from KLine.KLine_Unit import CKLine_Unit
from .CommonStockAPI import CCommonStockApi
def create_item_dict(data, column_name):
for i in range(len(data)):
data[i] = parse_time_column(data[i]) if column_name[i] == DATA_FIELD.FIELD_TIME else str2float(data[i])
return dict(zip(column_name, data))
def parse_time_column(inp):
# 20210902113000000
# 2021-09-13
if len(inp) == 10:
year = int(inp[:4])
month = int(inp[5:7])
day = int(inp[8:10])
hour = minute = 0
elif len(inp) == 17:
year = int(inp[:4])
month = int(inp[4:6])
day = int(inp[6:8])
hour = int(inp[8:10])
minute = int(inp[10:12])
elif len(inp) == 19:
year = int(inp[:4])
month = int(inp[5:7])
day = int(inp[8:10])
hour = int(inp[11:13])
minute = int(inp[14:16])
else:
raise Exception(f"unknown time column from csv:{inp}")
return CTime(year, month, day, hour, minute)
class CSV_API(CCommonStockApi):
def __init__(self, code, k_type=KL_TYPE.K_DAY, begin_date=None, end_date=None, autype=None):
self.headers_exist = True # 第一行是否是标题,如果是数据,设置为False
self.columns = [
DATA_FIELD.FIELD_TIME,
DATA_FIELD.FIELD_OPEN,
DATA_FIELD.FIELD_HIGH,
DATA_FIELD.FIELD_LOW,
DATA_FIELD.FIELD_CLOSE,
# DATA_FIELD.FIELD_VOLUME,
# DATA_FIELD.FIELD_TURNOVER,
# DATA_FIELD.FIELD_TURNRATE,
] # 每一列字段
self.time_column_idx = self.columns.index(DATA_FIELD.FIELD_TIME)
super(CSV_API, self).__init__(code, k_type, begin_date, end_date, autype)
def get_kl_data(self):
cur_path = os.path.dirname(os.path.realpath(__file__))
file_path = f"{cur_path}/../{self.code}.csv"
if not os.path.exists(file_path):
raise CChanException(f"file not exist: {file_path}", ErrCode.SRC_DATA_NOT_FOUND)
for line_number, line in enumerate(open(file_path, 'r')):
if self.headers_exist and line_number == 0:
continue
data = line.strip("\n").split(",")
if len(data) != len(self.columns):
raise CChanException(f"file format error: {file_path}", ErrCode.SRC_DATA_FORMAT_ERROR)
if self.begin_date is not None and data[self.time_column_idx] < self.begin_date:
continue
if self.end_date is not None and data[self.time_column_idx] > self.end_date:
continue
yield CKLine_Unit(create_item_dict(data, self.columns))
def SetBasciInfo(self):
pass
@classmethod
def do_init(cls):
pass
@classmethod
def do_close(cls):
pass