parent
9e024bfec3
commit
175173de0f
@ -0,0 +1,77 @@
|
||||
# API 测试用例清单
|
||||
|
||||
## 新增数据同步接口测试用例
|
||||
|
||||
| 测试ID | 测试名称 | 方法 | 路径 | 说明 |
|
||||
|--------|----------|------|------|------|
|
||||
| `admin_data_sync_full` | 全量数据同步 | POST | `/v1/admin/data/sync` | 同步基础+行情+财务数据 |
|
||||
| `admin_data_sync_base` | 同步基础K线数据 | POST | `/v1/admin/data/sync` | 仅同步OHLCV基础数据 |
|
||||
| `admin_data_sync_quote` | 同步行情指标数据 | POST | `/v1/admin/data/sync` | 同步均线/MACD/涨跌幅 |
|
||||
| `admin_data_sync_finance` | 同步财务数据 | POST | `/v1/admin/data/sync` | 同步市值/股本/利润 |
|
||||
| `admin_data_sync_incremental` | 增量数据同步 | POST | `/v1/admin/data/sync/incremental` | 同步最近30天数据 |
|
||||
| `admin_data_sync_futures` | 期货数据同步 | POST | `/v1/admin/data/sync` | 同步期货数据 |
|
||||
|
||||
## 测试用例分类统计
|
||||
|
||||
| 分类 | 用例数 |
|
||||
|------|--------|
|
||||
| 股票接口 | 4 |
|
||||
| 期货接口 | 5 |
|
||||
| 管理接口 | 7 |
|
||||
| 适配器管理 | 3 |
|
||||
| **数据同步接口** | **6** |
|
||||
| 测试管理 | 1 |
|
||||
| **总计** | **26** |
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 通过管理后台API测试
|
||||
```bash
|
||||
# 1. 获取测试列表
|
||||
curl "http://localhost:8080/v1/admin/tests/api"
|
||||
|
||||
# 2. 执行全量数据同步测试
|
||||
curl -X POST "http://localhost:8080/v1/admin/tests/api/run" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"id": "admin_data_sync_full",
|
||||
"params": {
|
||||
"symbols": ["000001.SZ"],
|
||||
"sync_type": "full"
|
||||
}
|
||||
}'
|
||||
|
||||
# 3. 执行基础数据同步测试
|
||||
curl -X POST "http://localhost:8080/v1/admin/tests/api/run" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"id": "admin_data_sync_base"
|
||||
}'
|
||||
```
|
||||
|
||||
### 直接调用数据同步接口
|
||||
```bash
|
||||
# 全量同步
|
||||
curl -X POST "http://localhost:8080/v1/admin/data/sync" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"symbols": ["600519.SH", "000001.SZ"],
|
||||
"sync_type": "full",
|
||||
"start_date": "20240101",
|
||||
"end_date": "20240301"
|
||||
}'
|
||||
|
||||
# 增量同步
|
||||
curl -X POST "http://localhost:8080/v1/admin/data/sync/incremental" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '["600519.SH", "000001.SZ"]'
|
||||
```
|
||||
|
||||
## 对应的适配器方法
|
||||
|
||||
| API接口 | 适配器方法 |
|
||||
|---------|-----------|
|
||||
| `/admin/data/sync` (base) | `fetch_kline_base` |
|
||||
| `/admin/data/sync` (quote) | `fetch_kline_quote` |
|
||||
| `/admin/data/sync` (finance) | `fetch_kline_finance` |
|
||||
| `/admin/data/sync/incremental` | `sync_daily_incremental` |
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,204 @@
|
||||
#!/usr/bin/env python3
|
||||
"""添加15/30/60分钟K线表"""
|
||||
|
||||
# 股票15/30/60分钟表
|
||||
stock_tables = '''
|
||||
|
||||
|
||||
# ============================================
|
||||
# 股票15/30/60分钟K线表 (新增)
|
||||
# ============================================
|
||||
|
||||
class StockKLine15M(Base):
|
||||
"""股票15分钟K线"""
|
||||
__tablename__ = "stock_klines_15m"
|
||||
__table_args__ = (
|
||||
Index("idx_stock_15m_symbol_ts", "symbol_id", "ts"),
|
||||
)
|
||||
|
||||
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||
symbol_id = Column(String(20), nullable=False, index=True, comment="标的代码")
|
||||
ts = Column(DateTime, nullable=False, comment="时间戳")
|
||||
trade_date = Column(String(10), nullable=False, comment="交易日")
|
||||
|
||||
open = Column(Numeric(18, 4), nullable=False, comment="开盘价")
|
||||
high = Column(Numeric(18, 4), nullable=False, comment="最高价")
|
||||
low = Column(Numeric(18, 4), nullable=False, comment="最低价")
|
||||
close = Column(Numeric(18, 4), nullable=False, comment="收盘价")
|
||||
volume = Column(BigInteger, nullable=False, comment="成交量")
|
||||
amount = Column(Numeric(20, 4), nullable=False, comment="成交额")
|
||||
|
||||
change_pct = Column(Numeric(8, 4), nullable=True, comment="涨跌幅%")
|
||||
macd_dif = Column(Numeric(18, 6), nullable=True, comment="MACD DIF")
|
||||
macd_dea = Column(Numeric(18, 6), nullable=True, comment="MACD DEA")
|
||||
macd_bar = Column(Numeric(18, 6), nullable=True, comment="MACD BAR")
|
||||
|
||||
created_at = Column(DateTime, default=datetime.now, comment="创建时间")
|
||||
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now, comment="更新时间")
|
||||
|
||||
|
||||
class StockKLine30M(Base):
|
||||
"""股票30分钟K线"""
|
||||
__tablename__ = "stock_klines_30m"
|
||||
__table_args__ = (
|
||||
Index("idx_stock_30m_symbol_ts", "symbol_id", "ts"),
|
||||
)
|
||||
|
||||
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||
symbol_id = Column(String(20), nullable=False, index=True, comment="标的代码")
|
||||
ts = Column(DateTime, nullable=False, comment="时间戳")
|
||||
trade_date = Column(String(10), nullable=False, comment="交易日")
|
||||
|
||||
open = Column(Numeric(18, 4), nullable=False, comment="开盘价")
|
||||
high = Column(Numeric(18, 4), nullable=False, comment="最高价")
|
||||
low = Column(Numeric(18, 4), nullable=False, comment="最低价")
|
||||
close = Column(Numeric(18, 4), nullable=False, comment="收盘价")
|
||||
volume = Column(BigInteger, nullable=False, comment="成交量")
|
||||
amount = Column(Numeric(20, 4), nullable=False, comment="成交额")
|
||||
|
||||
change_pct = Column(Numeric(8, 4), nullable=True, comment="涨跌幅%")
|
||||
macd_dif = Column(Numeric(18, 6), nullable=True, comment="MACD DIF")
|
||||
macd_dea = Column(Numeric(18, 6), nullable=True, comment="MACD DEA")
|
||||
macd_bar = Column(Numeric(18, 6), nullable=True, comment="MACD BAR")
|
||||
|
||||
created_at = Column(DateTime, default=datetime.now, comment="创建时间")
|
||||
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now, comment="更新时间")
|
||||
|
||||
|
||||
class StockKLine60M(Base):
|
||||
"""股票60分钟K线"""
|
||||
__tablename__ = "stock_klines_60m"
|
||||
__table_args__ = (
|
||||
Index("idx_stock_60m_symbol_ts", "symbol_id", "ts"),
|
||||
)
|
||||
|
||||
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||
symbol_id = Column(String(20), nullable=False, index=True, comment="标的代码")
|
||||
ts = Column(DateTime, nullable=False, comment="时间戳")
|
||||
trade_date = Column(String(10), nullable=False, comment="交易日")
|
||||
|
||||
open = Column(Numeric(18, 4), nullable=False, comment="开盘价")
|
||||
high = Column(Numeric(18, 4), nullable=False, comment="最高价")
|
||||
low = Column(Numeric(18, 4), nullable=False, comment="最低价")
|
||||
close = Column(Numeric(18, 4), nullable=False, comment="收盘价")
|
||||
volume = Column(BigInteger, nullable=False, comment="成交量")
|
||||
amount = Column(Numeric(20, 4), nullable=False, comment="成交额")
|
||||
|
||||
change_pct = Column(Numeric(8, 4), nullable=True, comment="涨跌幅%")
|
||||
macd_dif = Column(Numeric(18, 6), nullable=True, comment="MACD DIF")
|
||||
macd_dea = Column(Numeric(18, 6), nullable=True, comment="MACD DEA")
|
||||
macd_bar = Column(Numeric(18, 6), nullable=True, comment="MACD BAR")
|
||||
|
||||
created_at = Column(DateTime, default=datetime.now, comment="创建时间")
|
||||
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now, comment="更新时间")
|
||||
|
||||
'''
|
||||
|
||||
# 期货15/30/60分钟表
|
||||
futures_tables = '''
|
||||
|
||||
|
||||
# ============================================
|
||||
# 期货15/30/60分钟K线表 (新增)
|
||||
# ============================================
|
||||
|
||||
class FuturesKLine15MBase(Base):
|
||||
"""期货15分钟K线 - 基础表"""
|
||||
__tablename__ = "futures_klines_15m_base"
|
||||
__table_args__ = (
|
||||
Index("idx_futures_15m_base_symbol_ts", "symbol_id", "ts"),
|
||||
)
|
||||
|
||||
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||
symbol_id = Column(String(20), nullable=False, index=True, comment="合约代码")
|
||||
ts = Column(DateTime, nullable=False, comment="时间戳")
|
||||
trade_date = Column(String(10), nullable=False, comment="交易日")
|
||||
|
||||
open = Column(Numeric(18, 4), nullable=False, comment="开盘价")
|
||||
high = Column(Numeric(18, 4), nullable=False, comment="最高价")
|
||||
low = Column(Numeric(18, 4), nullable=False, comment="最低价")
|
||||
close = Column(Numeric(18, 4), nullable=False, comment="收盘价")
|
||||
volume = Column(BigInteger, nullable=False, comment="成交量")
|
||||
amount = Column(Numeric(20, 4), nullable=False, comment="成交额")
|
||||
open_interest = Column(BigInteger, nullable=True, comment="持仓量")
|
||||
|
||||
created_at = Column(DateTime, default=datetime.now, comment="创建时间")
|
||||
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now, comment="更新时间")
|
||||
|
||||
|
||||
class FuturesKLine30MBase(Base):
|
||||
"""期货30分钟K线 - 基础表"""
|
||||
__tablename__ = "futures_klines_30m_base"
|
||||
__table_args__ = (
|
||||
Index("idx_futures_30m_base_symbol_ts", "symbol_id", "ts"),
|
||||
)
|
||||
|
||||
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||
symbol_id = Column(String(20), nullable=False, index=True, comment="合约代码")
|
||||
ts = Column(DateTime, nullable=False, comment="时间戳")
|
||||
trade_date = Column(String(10), nullable=False, comment="交易日")
|
||||
|
||||
open = Column(Numeric(18, 4), nullable=False, comment="开盘价")
|
||||
high = Column(Numeric(18, 4), nullable=False, comment="最高价")
|
||||
low = Column(Numeric(18, 4), nullable=False, comment="最低价")
|
||||
close = Column(Numeric(18, 4), nullable=False, comment="收盘价")
|
||||
volume = Column(BigInteger, nullable=False, comment="成交量")
|
||||
amount = Column(Numeric(20, 4), nullable=False, comment="成交额")
|
||||
open_interest = Column(BigInteger, nullable=True, comment="持仓量")
|
||||
|
||||
created_at = Column(DateTime, default=datetime.now, comment="创建时间")
|
||||
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now, comment="更新时间")
|
||||
|
||||
|
||||
class FuturesKLine60MBase(Base):
|
||||
"""期货60分钟K线 - 基础表"""
|
||||
__tablename__ = "futures_klines_60m_base"
|
||||
__table_args__ = (
|
||||
Index("idx_futures_60m_base_symbol_ts", "symbol_id", "ts"),
|
||||
)
|
||||
|
||||
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||
symbol_id = Column(String(20), nullable=False, index=True, comment="合约代码")
|
||||
ts = Column(DateTime, nullable=False, comment="时间戳")
|
||||
trade_date = Column(String(10), nullable=False, comment="交易日")
|
||||
|
||||
open = Column(Numeric(18, 4), nullable=False, comment="开盘价")
|
||||
high = Column(Numeric(18, 4), nullable=False, comment="最高价")
|
||||
low = Column(Numeric(18, 4), nullable=False, comment="最低价")
|
||||
close = Column(Numeric(18, 4), nullable=False, comment="收盘价")
|
||||
volume = Column(BigInteger, nullable=False, comment="成交量")
|
||||
amount = Column(Numeric(20, 4), nullable=False, comment="成交额")
|
||||
open_interest = Column(BigInteger, nullable=True, comment="持仓量")
|
||||
|
||||
created_at = Column(DateTime, default=datetime.now, comment="创建时间")
|
||||
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now, comment="更新时间")
|
||||
|
||||
'''
|
||||
|
||||
def main():
|
||||
models_path = 'app/repositories/models.py'
|
||||
|
||||
with open(models_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
added = []
|
||||
|
||||
# 添加股票表
|
||||
if 'class StockKLine15M' not in content:
|
||||
content = content + stock_tables
|
||||
added.append('StockKLine15M/30M/60M')
|
||||
|
||||
# 添加期货表
|
||||
if 'class FuturesKLine15MBase' not in content:
|
||||
content = content + futures_tables
|
||||
added.append('FuturesKLine15MBase/30MBase/60MBase')
|
||||
|
||||
if added:
|
||||
with open(models_path, 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
print(f'Added: {", ".join(added)}')
|
||||
else:
|
||||
print('All tables already exist.')
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -0,0 +1,522 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Add new data fetch methods to amazingdata_adapter.py using English comments"""
|
||||
|
||||
new_methods = '''
|
||||
|
||||
# ==================== New Split Table Data Fetch Methods ====================
|
||||
|
||||
async def fetch_kline_base(
|
||||
self,
|
||||
symbol: str,
|
||||
start: str,
|
||||
end: str,
|
||||
freq: str
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Fetch K-line base data (OHLCV)
|
||||
|
||||
Corresponding tables: stock_klines_1d_base, stock_klines_1m_base, etc.
|
||||
|
||||
Returns:
|
||||
List[Dict] containing fields:
|
||||
- symbol: Symbol code
|
||||
- ts: Timestamp
|
||||
- trade_date: Trade date
|
||||
- open/high/low/close: Price data
|
||||
- volume: Trading volume
|
||||
- amount: Trading amount
|
||||
- adj_factor: Adjustment factor
|
||||
"""
|
||||
print(f"[amazingdata_adapter fetch_kline_base]Fetching {symbol} {freq} base data...")
|
||||
self._check_login()
|
||||
|
||||
period_map = {
|
||||
"1m": self._ad.constant.Period.min1,
|
||||
"5m": self._ad.constant.Period.min5,
|
||||
"15m": self._ad.constant.Period.min15,
|
||||
"30m": self._ad.constant.Period.min30,
|
||||
"60m": self._ad.constant.Period.min60,
|
||||
"1d": self._ad.constant.Period.day,
|
||||
"1w": self._ad.constant.Period.week,
|
||||
"1month": self._ad.constant.Period.month,
|
||||
}
|
||||
period_value = period_map.get(freq, self._ad.constant.Period.day).value
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
return await loop.run_in_executor(
|
||||
None,
|
||||
lambda: self._fetch_kline_base_sync(symbol, start, end, period_value, freq)
|
||||
)
|
||||
|
||||
def _fetch_kline_base_sync(
|
||||
self,
|
||||
symbol: str,
|
||||
start_date: str,
|
||||
end_date: str,
|
||||
period_value: int,
|
||||
freq: str
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Sync method to fetch K-line base data"""
|
||||
codes = [symbol]
|
||||
start_int = self._format_date(start_date)
|
||||
end_int = self._format_date(end_date)
|
||||
|
||||
kline_dict = self._market_data.query_kline(
|
||||
code_list=codes,
|
||||
begin_date=start_int,
|
||||
end_date=end_int,
|
||||
period=period_value
|
||||
)
|
||||
|
||||
if symbol not in kline_dict:
|
||||
info(f"No kline data found for {symbol}")
|
||||
return []
|
||||
|
||||
df = kline_dict[symbol]
|
||||
results = []
|
||||
|
||||
for _, row in df.iterrows():
|
||||
kline_time = row.get('kline_time')
|
||||
if pd.isna(kline_time) or kline_time is None:
|
||||
continue
|
||||
|
||||
if isinstance(kline_time, pd.Timestamp):
|
||||
ts = int(kline_time.timestamp())
|
||||
trade_date = kline_time.strftime('%Y-%m-%d')
|
||||
else:
|
||||
date_str = str(int(kline_time))
|
||||
if len(date_str) != 8:
|
||||
continue
|
||||
dt = datetime.strptime(date_str, "%Y%m%d")
|
||||
ts = int(dt.timestamp())
|
||||
trade_date = dt.strftime('%Y-%m-%d')
|
||||
|
||||
results.append({
|
||||
"symbol": symbol,
|
||||
"ts": ts,
|
||||
"trade_date": trade_date,
|
||||
"open": float(row.get('open', 0)),
|
||||
"high": float(row.get('high', 0)),
|
||||
"low": float(row.get('low', 0)),
|
||||
"close": float(row.get('close', 0)),
|
||||
"volume": int(row.get('volume', 0)),
|
||||
"amount": float(row.get('amount', 0)),
|
||||
"adj_factor": float(row.get('adj_factor', 1.0)) if 'adj_factor' in df.columns else 1.0,
|
||||
})
|
||||
|
||||
info(f"Fetched {len(results)} base kline records for {symbol}")
|
||||
return results
|
||||
|
||||
async def fetch_kline_quote(
|
||||
self,
|
||||
symbol: str,
|
||||
start: str,
|
||||
end: str
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Fetch daily quote indicator data (calculated)
|
||||
|
||||
Corresponding table: stock_klines_1d_quote
|
||||
|
||||
Returns:
|
||||
List[Dict] containing fields:
|
||||
- change_pct: Price change percentage
|
||||
- change_Nd_pct: N-day price change
|
||||
- ma_N: Moving averages
|
||||
- macd_dif/dea/bar: MACD indicators
|
||||
- bias_N: Bias ratios
|
||||
- is_limit_up/down: Limit up/down status
|
||||
- is_st: ST status
|
||||
"""
|
||||
print(f"[amazingdata_adapter fetch_kline_quote]Calculating {symbol} quote indicators...")
|
||||
self._check_login()
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
return await loop.run_in_executor(
|
||||
None,
|
||||
lambda: self._fetch_kline_quote_sync(symbol, start, end)
|
||||
)
|
||||
|
||||
def _fetch_kline_quote_sync(
|
||||
self,
|
||||
symbol: str,
|
||||
start_date: str,
|
||||
end_date: str
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Sync method to calculate quote indicators"""
|
||||
import numpy as np
|
||||
|
||||
start_dt = datetime.strptime(start_date, "%Y%m%d")
|
||||
extended_start = datetime(start_dt.year - 1, start_dt.month, start_dt.day)
|
||||
extended_start_str = extended_start.strftime("%Y%m%d")
|
||||
|
||||
codes = [symbol]
|
||||
start_int = self._format_date(extended_start_str)
|
||||
end_int = self._format_date(end_date)
|
||||
|
||||
kline_dict = self._market_data.query_kline(
|
||||
code_list=codes,
|
||||
begin_date=start_int,
|
||||
end_date=end_int,
|
||||
period=self._ad.constant.Period.day.value
|
||||
)
|
||||
|
||||
if symbol not in kline_dict:
|
||||
return []
|
||||
|
||||
df = kline_dict[symbol].copy()
|
||||
df = df.sort_values('kline_time')
|
||||
|
||||
try:
|
||||
code_info_df = self._base_data.get_code_info(security_type=SecurityType.STOCK_A.value)
|
||||
if symbol in code_info_df.index:
|
||||
high_limited = float(code_info_df.loc[symbol, 'high_limited']) if 'high_limited' in code_info_df.columns else None
|
||||
low_limited = float(code_info_df.loc[symbol, 'low_limited']) if 'low_limited' in code_info_df.columns else None
|
||||
else:
|
||||
high_limited = low_limited = None
|
||||
except:
|
||||
high_limited = low_limited = None
|
||||
|
||||
results = []
|
||||
closes = df['close'].values
|
||||
|
||||
for i, (_, row) in enumerate(df.iterrows()):
|
||||
kline_time = row.get('kline_time')
|
||||
if pd.isna(kline_time):
|
||||
continue
|
||||
|
||||
if isinstance(kline_time, pd.Timestamp):
|
||||
trade_date = kline_time.strftime('%Y-%m-%d')
|
||||
else:
|
||||
date_str = str(int(kline_time))
|
||||
if len(date_str) != 8:
|
||||
continue
|
||||
dt = datetime.strptime(date_str, "%Y%m%d")
|
||||
trade_date = dt.strftime('%Y-%m-%d')
|
||||
|
||||
close = float(row.get('close', 0))
|
||||
|
||||
change_pct = None
|
||||
if i > 0:
|
||||
prev_close = closes[i-1]
|
||||
if prev_close > 0:
|
||||
change_pct = round((close - prev_close) / prev_close * 100, 4)
|
||||
|
||||
def calc_n_day_change(n):
|
||||
if i >= n and closes[i-n] > 0:
|
||||
return round((close - closes[i-n]) / closes[i-n] * 100, 4)
|
||||
return None
|
||||
|
||||
def calc_ma(n):
|
||||
if i >= n - 1:
|
||||
return round(np.mean(closes[i-n+1:i+1]), 4)
|
||||
return None
|
||||
|
||||
def calc_macd():
|
||||
if i < 33:
|
||||
return None, None, None
|
||||
ema12 = pd.Series(closes[:i+1]).ewm(span=12).mean().iloc[-1]
|
||||
ema26 = pd.Series(closes[:i+1]).ewm(span=26).mean().iloc[-1]
|
||||
dif = ema12 - ema26
|
||||
dea = pd.Series([ema12 - ema26 for _ in range(i+1)]).ewm(span=9).mean().iloc[-1]
|
||||
bar = (dif - dea) * 2
|
||||
return round(dif, 6), round(dea, 6), round(bar, 6)
|
||||
|
||||
def calc_bias(n):
|
||||
ma = calc_ma(n)
|
||||
if ma and ma > 0:
|
||||
return round((close - ma) / ma * 100, 4)
|
||||
return None
|
||||
|
||||
is_limit_up = False
|
||||
is_limit_down = False
|
||||
if high_limited and low_limited and close > 0:
|
||||
is_limit_up = close >= high_limited * 0.995
|
||||
is_limit_down = close <= low_limited * 1.005
|
||||
|
||||
macd_dif, macd_dea, macd_bar = calc_macd()
|
||||
|
||||
if trade_date.replace('-', '') >= start_date:
|
||||
results.append({
|
||||
"symbol": symbol,
|
||||
"trade_date": trade_date,
|
||||
"change_pct": change_pct,
|
||||
"change_5d_pct": calc_n_day_change(5),
|
||||
"change_10d_pct": calc_n_day_change(10),
|
||||
"change_20d_pct": calc_n_day_change(20),
|
||||
"change_30d_pct": calc_n_day_change(30),
|
||||
"change_60d_pct": calc_n_day_change(60),
|
||||
"macd_dif": macd_dif,
|
||||
"macd_dea": macd_dea,
|
||||
"macd_bar": macd_bar,
|
||||
"bias_5": calc_bias(5),
|
||||
"bias_10": calc_bias(10),
|
||||
"bias_20": calc_bias(20),
|
||||
"is_limit_up": is_limit_up,
|
||||
"is_limit_down": is_limit_down,
|
||||
"limit_up_price": round(high_limited, 4) if high_limited else None,
|
||||
"limit_down_price": round(low_limited, 4) if low_limited else None,
|
||||
"is_st": None,
|
||||
"ma_5": calc_ma(5),
|
||||
"ma_10": calc_ma(10),
|
||||
"ma_20": calc_ma(20),
|
||||
"ma_30": calc_ma(30),
|
||||
"ma_60": calc_ma(60),
|
||||
"ma_120": calc_ma(120),
|
||||
"ma_250": calc_ma(250),
|
||||
})
|
||||
|
||||
info(f"Calculated {len(results)} quote indicators for {symbol}")
|
||||
return results
|
||||
|
||||
async def fetch_kline_finance(
|
||||
self,
|
||||
symbol: str,
|
||||
start: str,
|
||||
end: str
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Fetch daily finance data
|
||||
|
||||
Corresponding table: stock_klines_1d_finance
|
||||
Data sources: get_equity_structure, get_share_holder, get_income
|
||||
|
||||
Returns:
|
||||
List[Dict] containing fields:
|
||||
- total_market_cap: Total market cap
|
||||
- float_market_cap: Float market cap
|
||||
- total_shares: Total shares
|
||||
- float_shares: Float shares
|
||||
- inst_holding_shares: Institutional holding shares
|
||||
- inst_holding_ratio: Institutional holding ratio
|
||||
- net_profit: Net profit
|
||||
- revenue: Revenue
|
||||
- eps: EPS
|
||||
- roe: ROE
|
||||
"""
|
||||
print(f"[amazingdata_adapter fetch_kline_finance]Fetching {symbol} finance data...")
|
||||
self._check_login()
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
return await loop.run_in_executor(
|
||||
None,
|
||||
lambda: self._fetch_kline_finance_sync(symbol, start, end)
|
||||
)
|
||||
|
||||
def _fetch_kline_finance_sync(
|
||||
self,
|
||||
symbol: str,
|
||||
start_date: str,
|
||||
end_date: str
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Sync method to fetch finance data"""
|
||||
codes = [symbol]
|
||||
start_int = self._format_date(start_date)
|
||||
end_int = self._format_date(end_date)
|
||||
|
||||
results = []
|
||||
|
||||
try:
|
||||
equity_dict = self._info_data.get_equity_structure(
|
||||
code_list=codes,
|
||||
local_path=self.config.local_path,
|
||||
is_local=self.config.use_local_cache
|
||||
)
|
||||
|
||||
equity_data = {}
|
||||
if symbol in equity_dict:
|
||||
equity_df = equity_dict[symbol]
|
||||
for _, row in equity_df.iterrows():
|
||||
ann_date = row.get('ANN_DATE')
|
||||
if pd.notna(ann_date):
|
||||
if isinstance(ann_date, (int, float)):
|
||||
date_key = str(int(ann_date))
|
||||
else:
|
||||
date_key = str(ann_date).replace('-', '').replace('/', '')
|
||||
equity_data[date_key] = {
|
||||
'total_shares': float(row.get('TOT_A_SHARE', 0)) * 10000 if pd.notna(row.get('TOT_A_SHARE')) else 0,
|
||||
'float_shares': float(row.get('FLOAT_A_SHARE', 0)) * 10000 if pd.notna(row.get('FLOAT_A_SHARE')) else 0,
|
||||
}
|
||||
except Exception as e:
|
||||
print(f"[amazingdata_adapter]Failed to get equity structure: {e}")
|
||||
equity_data = {}
|
||||
|
||||
kline_dict = self._market_data.query_kline(
|
||||
code_list=codes,
|
||||
begin_date=start_int,
|
||||
end_date=end_int,
|
||||
period=self._ad.constant.Period.day.value
|
||||
)
|
||||
|
||||
if symbol not in kline_dict:
|
||||
return []
|
||||
|
||||
df = kline_dict[symbol]
|
||||
|
||||
for _, row in df.iterrows():
|
||||
kline_time = row.get('kline_time')
|
||||
if pd.isna(kline_time):
|
||||
continue
|
||||
|
||||
if isinstance(kline_time, pd.Timestamp):
|
||||
trade_date = kline_time.strftime('%Y-%m-%d')
|
||||
trade_date_int = int(kline_time.strftime('%Y%m%d'))
|
||||
else:
|
||||
date_str = str(int(kline_time))
|
||||
if len(date_str) != 8:
|
||||
continue
|
||||
dt = datetime.strptime(date_str, "%Y%m%d")
|
||||
trade_date = dt.strftime('%Y-%m-%d')
|
||||
trade_date_int = int(date_str)
|
||||
|
||||
close = float(row.get('close', 0))
|
||||
|
||||
total_shares = 0
|
||||
float_shares = 0
|
||||
for date_key in sorted(equity_data.keys(), reverse=True):
|
||||
if int(date_key) <= trade_date_int:
|
||||
total_shares = equity_data[date_key]['total_shares']
|
||||
float_shares = equity_data[date_key]['float_shares']
|
||||
break
|
||||
|
||||
total_market_cap = close * total_shares if total_shares > 0 and close > 0 else None
|
||||
float_market_cap = close * float_shares if float_shares > 0 and close > 0 else None
|
||||
|
||||
results.append({
|
||||
"symbol": symbol,
|
||||
"trade_date": trade_date,
|
||||
"total_market_cap": round(total_market_cap, 2) if total_market_cap else None,
|
||||
"float_market_cap": round(float_market_cap, 2) if float_market_cap else None,
|
||||
"total_shares": int(total_shares) if total_shares > 0 else None,
|
||||
"float_shares": int(float_shares) if float_shares > 0 else None,
|
||||
"inst_holding_shares": None,
|
||||
"inst_holding_ratio": None,
|
||||
"top10_holders_ratio": None,
|
||||
"net_profit": None,
|
||||
"revenue": None,
|
||||
"eps": None,
|
||||
"roe": None,
|
||||
"trading_days": None,
|
||||
})
|
||||
|
||||
info(f"Fetched {len(results)} finance records for {symbol}")
|
||||
return results
|
||||
|
||||
async def fetch_stock_basic_info(
|
||||
self,
|
||||
codes: Optional[List[str]] = None
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Fetch stock basic info
|
||||
|
||||
Corresponding table: stock_symbols
|
||||
Data source: get_stock_basic
|
||||
|
||||
Returns:
|
||||
List[Dict] containing fields:
|
||||
- symbol_id: Symbol code
|
||||
- name: Name
|
||||
- exchange: Exchange
|
||||
- list_date: List date
|
||||
- list_board: List board
|
||||
- industry: Industry
|
||||
- status: Status
|
||||
- is_delisted: Is delisted
|
||||
- delist_date: Delist date
|
||||
"""
|
||||
print(f"[amazingdata_adapter fetch_stock_basic_info]Fetching stock basic info...")
|
||||
self._check_login()
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
return await loop.run_in_executor(
|
||||
None,
|
||||
lambda: self._fetch_stock_basic_info_sync(codes)
|
||||
)
|
||||
|
||||
def _fetch_stock_basic_info_sync(
|
||||
self,
|
||||
codes: Optional[List[str]] = None
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Sync method to fetch stock basic info"""
|
||||
try:
|
||||
all_codes = self._base_data.get_code_list(
|
||||
security_type=SecurityType.STOCK_A.value
|
||||
)
|
||||
|
||||
if codes:
|
||||
all_codes = [c for c in all_codes if c in codes]
|
||||
|
||||
info_df = self._base_data.get_code_info(
|
||||
security_type=SecurityType.STOCK_A.value
|
||||
)
|
||||
|
||||
results = []
|
||||
for code in all_codes:
|
||||
if ".SH" in code:
|
||||
exchange = "SH"
|
||||
elif ".SZ" in code:
|
||||
exchange = "SZ"
|
||||
elif ".BJ" in code:
|
||||
exchange = "BJ"
|
||||
else:
|
||||
exchange = ""
|
||||
|
||||
name = code
|
||||
if code in info_df.index and 'symbol' in info_df.columns:
|
||||
name = info_df.loc[code, 'symbol']
|
||||
|
||||
list_date = None
|
||||
try:
|
||||
equity_dict = self._info_data.get_equity_structure(
|
||||
code_list=[code],
|
||||
local_path=self.config.local_path,
|
||||
is_local=self.config.use_local_cache
|
||||
)
|
||||
if code in equity_dict and not equity_dict[code].empty:
|
||||
first_record = equity_dict[code].iloc[0]
|
||||
ann_date = first_record.get('ANN_DATE')
|
||||
if pd.notna(ann_date):
|
||||
if isinstance(ann_date, (int, float)):
|
||||
list_date = datetime.strptime(str(int(ann_date)), "%Y%m%d")
|
||||
else:
|
||||
list_date = pd.to_datetime(ann_date)
|
||||
except:
|
||||
pass
|
||||
|
||||
results.append({
|
||||
"symbol_id": code,
|
||||
"name": name,
|
||||
"exchange": exchange,
|
||||
"list_date": list_date,
|
||||
"list_board": None,
|
||||
"industry": None,
|
||||
"status": "active",
|
||||
"is_delisted": False,
|
||||
"delist_date": None,
|
||||
"is_st": None,
|
||||
"total_shares": None,
|
||||
"float_shares": None,
|
||||
})
|
||||
|
||||
info(f"Fetched {len(results)} stock basic info records")
|
||||
return results
|
||||
|
||||
except Exception as e:
|
||||
error(f"Failed to fetch stock basic info: {e}")
|
||||
return []
|
||||
'''
|
||||
|
||||
def main():
|
||||
# Read original file
|
||||
with open('app/adapters/amazingdata_adapter.py', 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# Check if already added
|
||||
if 'fetch_kline_base' not in content:
|
||||
# Append new methods before the last line
|
||||
content = content.rstrip() + new_methods
|
||||
|
||||
with open('app/adapters/amazingdata_adapter.py', 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
print('New methods added successfully!')
|
||||
else:
|
||||
print('Methods already exist.')
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -0,0 +1,147 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
创建拆分后的新表结构
|
||||
|
||||
用法:
|
||||
python scripts/create_split_tables.py
|
||||
python scripts/create_split_tables.py --drop-existing
|
||||
"""
|
||||
import argparse
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 添加项目根目录到路径
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from sqlalchemy import create_engine, text
|
||||
from app.repositories.database import Base, engine
|
||||
from app.core.config import get_config
|
||||
from app.core.logger import info, error
|
||||
|
||||
|
||||
def create_tables(drop_existing: bool = False):
|
||||
"""创建所有表"""
|
||||
config = get_config()
|
||||
|
||||
# 创建数据库连接
|
||||
db_url = f"mysql+pymysql://{config.database.user}:{config.database.password}@{config.database.host}:{config.database.port}/{config.database.database}"
|
||||
engine_local = create_engine(db_url, echo=False)
|
||||
|
||||
if drop_existing:
|
||||
info("Dropping existing split tables...")
|
||||
tables_to_drop = [
|
||||
# 股票分表
|
||||
"stock_klines_1d_base", "stock_klines_1d_quote", "stock_klines_1d_finance",
|
||||
"stock_realtime_quotes",
|
||||
# 期货分表
|
||||
"futures_klines_1d_base", "futures_klines_1d_quote",
|
||||
"futures_klines_1m_base", "futures_klines_5m_base",
|
||||
"futures_realtime_quotes",
|
||||
]
|
||||
with engine_local.connect() as conn:
|
||||
for table in tables_to_drop:
|
||||
try:
|
||||
conn.execute(text(f"DROP TABLE IF EXISTS {table}"))
|
||||
info(f"Dropped table: {table}")
|
||||
except Exception as e:
|
||||
error(f"Failed to drop {table}: {e}")
|
||||
conn.commit()
|
||||
|
||||
info("Creating new split tables...")
|
||||
|
||||
# 导入所有模型以确保它们被注册
|
||||
from app.repositories.models import (
|
||||
# 股票分表
|
||||
StockKLine1DBase, StockKLine1DQuote, StockKLine1DFinance,
|
||||
StockKLine1M, StockKLine5M, StockKLine15M, StockKLine30M, StockKLine60M,
|
||||
StockRealTimeQuote,
|
||||
# 期货分表
|
||||
FuturesKLine1DBase, FuturesKLine1DQuote,
|
||||
FuturesKLine1MBase, FuturesKLine5MBase, FuturesKLine15MBase, FuturesKLine30MBase, FuturesKLine60MBase,
|
||||
FuturesRealTimeQuote,
|
||||
)
|
||||
|
||||
# 创建表
|
||||
tables = [
|
||||
# 股票表 - 日线分表
|
||||
StockKLine1DBase.__table__,
|
||||
StockKLine1DQuote.__table__,
|
||||
StockKLine1DFinance.__table__,
|
||||
# 股票表 - 分钟线
|
||||
StockKLine1M.__table__,
|
||||
StockKLine5M.__table__,
|
||||
StockKLine15M.__table__,
|
||||
StockKLine30M.__table__,
|
||||
StockKLine60M.__table__,
|
||||
StockRealTimeQuote.__table__,
|
||||
# 期货表 - 日线分表
|
||||
FuturesKLine1DBase.__table__,
|
||||
FuturesKLine1DQuote.__table__,
|
||||
# 期货表 - 分钟线
|
||||
FuturesKLine1MBase.__table__,
|
||||
FuturesKLine5MBase.__table__,
|
||||
FuturesKLine15MBase.__table__,
|
||||
FuturesKLine30MBase.__table__,
|
||||
FuturesKLine60MBase.__table__,
|
||||
FuturesRealTimeQuote.__table__,
|
||||
]
|
||||
|
||||
Base.metadata.create_all(bind=engine_local, tables=tables)
|
||||
|
||||
info("Tables created successfully!")
|
||||
|
||||
# 显示创建的表
|
||||
with engine_local.connect() as conn:
|
||||
result = conn.execute(text("""
|
||||
SELECT TABLE_NAME, TABLE_COMMENT
|
||||
FROM information_schema.TABLES
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND (
|
||||
TABLE_NAME LIKE 'stock_klines_%'
|
||||
OR TABLE_NAME LIKE 'futures_klines_%'
|
||||
OR TABLE_NAME LIKE '%realtime_quotes'
|
||||
)
|
||||
"""))
|
||||
tables = result.fetchall()
|
||||
|
||||
info("\nCreated split tables:")
|
||||
stock_tables = []
|
||||
futures_tables = []
|
||||
for table_name, comment in tables:
|
||||
if table_name.startswith('stock_'):
|
||||
stock_tables.append(f" - {table_name}")
|
||||
else:
|
||||
futures_tables.append(f" - {table_name}")
|
||||
|
||||
if stock_tables:
|
||||
info("\n[Stock Tables]")
|
||||
for t in stock_tables:
|
||||
info(t)
|
||||
|
||||
if futures_tables:
|
||||
info("\n[Futures Tables]")
|
||||
for t in futures_tables:
|
||||
info(t)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Create split table structure")
|
||||
parser.add_argument(
|
||||
"--drop-existing",
|
||||
action="store_true",
|
||||
help="Drop existing tables before creating (WARNING: data will be lost!)"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.drop_existing:
|
||||
confirm = input("WARNING: This will drop existing tables and ALL DATA will be lost!\nType 'yes' to continue: ")
|
||||
if confirm != "yes":
|
||||
print("Aborted.")
|
||||
return
|
||||
|
||||
create_tables(drop_existing=args.drop_existing)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -0,0 +1,143 @@
|
||||
#!/usr/bin/env python3
|
||||
"""追加期货分表模型到 models.py"""
|
||||
|
||||
append_content = '''
|
||||
|
||||
|
||||
# ============================================
|
||||
# 期货分表结构 (新增)
|
||||
# ============================================
|
||||
|
||||
class FuturesKLine1DBase(Base):
|
||||
"""期货日线K线 - 基础表"""
|
||||
__tablename__ = "futures_klines_1d_base"
|
||||
__table_args__ = (
|
||||
Index("idx_futures_1d_base_symbol_ts", "symbol_id", "ts"),
|
||||
Index("idx_futures_1d_base_symbol_date", "symbol_id", "trade_date"),
|
||||
)
|
||||
|
||||
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||
symbol_id = Column(String(20), nullable=False, index=True, comment="合约代码")
|
||||
ts = Column(DateTime, nullable=False, comment="时间戳")
|
||||
trade_date = Column(String(10), nullable=False, index=True, comment="交易日 YYYY-MM-DD")
|
||||
open = Column(Numeric(18, 4), nullable=False, comment="开盘价")
|
||||
high = Column(Numeric(18, 4), nullable=False, comment="最高价")
|
||||
low = Column(Numeric(18, 4), nullable=False, comment="最低价")
|
||||
close = Column(Numeric(18, 4), nullable=False, comment="收盘价")
|
||||
volume = Column(BigInteger, nullable=False, comment="成交量")
|
||||
amount = Column(Numeric(20, 4), nullable=False, comment="成交额")
|
||||
open_interest = Column(BigInteger, nullable=True, comment="持仓量")
|
||||
settlement = Column(Numeric(18, 4), nullable=True, comment="结算价")
|
||||
pre_settlement = Column(Numeric(18, 4), nullable=True, comment="昨结算价")
|
||||
created_at = Column(DateTime, default=datetime.now, comment="创建时间")
|
||||
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now, comment="更新时间")
|
||||
|
||||
|
||||
class FuturesKLine1DQuote(Base):
|
||||
"""期货日线K线 - 行情指标表"""
|
||||
__tablename__ = "futures_klines_1d_quote"
|
||||
__table_args__ = (
|
||||
Index("idx_futures_1d_quote_symbol_date", "symbol_id", "trade_date"),
|
||||
)
|
||||
|
||||
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||
symbol_id = Column(String(20), nullable=False, index=True, comment="合约代码")
|
||||
trade_date = Column(String(10), nullable=False, index=True, comment="交易日 YYYY-MM-DD")
|
||||
change_pct = Column(Numeric(8, 4), nullable=True, comment="涨跌幅%")
|
||||
change_5d_pct = Column(Numeric(8, 4), nullable=True, comment="5日涨跌幅%")
|
||||
change_10d_pct = Column(Numeric(8, 4), nullable=True, comment="10日涨跌幅%")
|
||||
change_20d_pct = Column(Numeric(8, 4), nullable=True, comment="20日涨跌幅%")
|
||||
ma5 = Column(Numeric(18, 4), nullable=True, comment="5日均线")
|
||||
ma10 = Column(Numeric(18, 4), nullable=True, comment="10日均线")
|
||||
ma20 = Column(Numeric(18, 4), nullable=True, comment="20日均线")
|
||||
ma30 = Column(Numeric(18, 4), nullable=True, comment="30日均线")
|
||||
ma60 = Column(Numeric(18, 4), nullable=True, comment="60日均线")
|
||||
macd_dif = Column(Numeric(18, 6), nullable=True, comment="MACD DIF")
|
||||
macd_dea = Column(Numeric(18, 6), nullable=True, comment="MACD DEA")
|
||||
macd_bar = Column(Numeric(18, 6), nullable=True, comment="MACD BAR")
|
||||
oi_change = Column(BigInteger, nullable=True, comment="持仓量变化")
|
||||
oi_change_pct = Column(Numeric(8, 4), nullable=True, comment="持仓量变化%")
|
||||
amplitude = Column(Numeric(8, 4), nullable=True, comment="振幅%")
|
||||
created_at = Column(DateTime, default=datetime.now, comment="创建时间")
|
||||
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now, comment="更新时间")
|
||||
|
||||
|
||||
class FuturesKLine1MBase(Base):
|
||||
"""期货1分钟K线 - 基础表"""
|
||||
__tablename__ = "futures_klines_1m_base"
|
||||
__table_args__ = (Index("idx_futures_1m_base_symbol_ts", "symbol_id", "ts"),)
|
||||
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||
symbol_id = Column(String(20), nullable=False, index=True, comment="合约代码")
|
||||
ts = Column(DateTime, nullable=False, comment="时间戳")
|
||||
trade_date = Column(String(10), nullable=False, comment="交易日")
|
||||
open = Column(Numeric(18, 4), nullable=False, comment="开盘价")
|
||||
high = Column(Numeric(18, 4), nullable=False, comment="最高价")
|
||||
low = Column(Numeric(18, 4), nullable=False, comment="最低价")
|
||||
close = Column(Numeric(18, 4), nullable=False, comment="收盘价")
|
||||
volume = Column(BigInteger, nullable=False, comment="成交量")
|
||||
amount = Column(Numeric(20, 4), nullable=False, comment="成交额")
|
||||
open_interest = Column(BigInteger, nullable=True, comment="持仓量")
|
||||
created_at = Column(DateTime, default=datetime.now, comment="创建时间")
|
||||
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now, comment="更新时间")
|
||||
|
||||
|
||||
class FuturesKLine5MBase(Base):
|
||||
"""期货5分钟K线 - 基础表"""
|
||||
__tablename__ = "futures_klines_5m_base"
|
||||
__table_args__ = (Index("idx_futures_5m_base_symbol_ts", "symbol_id", "ts"),)
|
||||
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||
symbol_id = Column(String(20), nullable=False, index=True, comment="合约代码")
|
||||
ts = Column(DateTime, nullable=False, comment="时间戳")
|
||||
trade_date = Column(String(10), nullable=False, comment="交易日")
|
||||
open = Column(Numeric(18, 4), nullable=False, comment="开盘价")
|
||||
high = Column(Numeric(18, 4), nullable=False, comment="最高价")
|
||||
low = Column(Numeric(18, 4), nullable=False, comment="最低价")
|
||||
close = Column(Numeric(18, 4), nullable=False, comment="收盘价")
|
||||
volume = Column(BigInteger, nullable=False, comment="成交量")
|
||||
amount = Column(Numeric(20, 4), nullable=False, comment="成交额")
|
||||
open_interest = Column(BigInteger, nullable=True, comment="持仓量")
|
||||
created_at = Column(DateTime, default=datetime.now, comment="创建时间")
|
||||
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now, comment="更新时间")
|
||||
|
||||
|
||||
class FuturesRealTimeQuote(Base):
|
||||
"""期货实时行情快照"""
|
||||
__tablename__ = "futures_realtime_quotes"
|
||||
symbol_id = Column(String(20), primary_key=True, comment="合约代码")
|
||||
update_time = Column(DateTime, nullable=False, comment="更新时间")
|
||||
last_price = Column(Numeric(18, 4), nullable=True, comment="最新价")
|
||||
open = Column(Numeric(18, 4), nullable=True, comment="开盘价")
|
||||
high = Column(Numeric(18, 4), nullable=True, comment="最高价")
|
||||
low = Column(Numeric(18, 4), nullable=True, comment="最低价")
|
||||
pre_close = Column(Numeric(18, 4), nullable=True, comment="昨收")
|
||||
pre_settlement = Column(Numeric(18, 4), nullable=True, comment="昨结算")
|
||||
settlement = Column(Numeric(18, 4), nullable=True, comment="结算价")
|
||||
volume = Column(BigInteger, nullable=True, comment="成交量")
|
||||
amount = Column(Numeric(20, 4), nullable=True, comment="成交额")
|
||||
open_interest = Column(BigInteger, nullable=True, comment="持仓量")
|
||||
bid1 = Column(Numeric(18, 4), nullable=True, comment="买一价")
|
||||
bid1_volume = Column(BigInteger, nullable=True, comment="买一量")
|
||||
ask1 = Column(Numeric(18, 4), nullable=True, comment="卖一价")
|
||||
ask1_volume = Column(BigInteger, nullable=True, comment="卖一量")
|
||||
limit_up = Column(Numeric(18, 4), nullable=True, comment="涨停价")
|
||||
limit_down = Column(Numeric(18, 4), nullable=True, comment="跌停价")
|
||||
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now, comment="更新时间")
|
||||
|
||||
|
||||
# 向后兼容别名
|
||||
FuturesKLine1M = FuturesKLine1MBase
|
||||
FuturesKLine5M = FuturesKLine5MBase
|
||||
FuturesKLine1D = FuturesKLine1DBase
|
||||
'''
|
||||
|
||||
# 读取原始文件
|
||||
with open('app/repositories/models.py', 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# 检查是否已存在
|
||||
if 'class FuturesKLine1DBase' not in content:
|
||||
with open('app/repositories/models.py', 'w', encoding='utf-8') as f:
|
||||
f.write(content + append_content)
|
||||
print('Futures split tables appended successfully!')
|
||||
else:
|
||||
print('Futures split tables already exist.')
|
||||
Loading…
Reference in new issue