跳到主要内容

八、数据结构说明

八、数据结构说明

8.1 K线数据结构

📈 Candle(蜡烛图数据)

单根蜡烛图数据结构,包含完整的OHLCV信息:

pub struct Candle {
/// 开盘时间,Unix时间戳(毫秒)
pub timestamp: i64,
/// 开盘价
pub open: f64,
/// 最高价
pub high: f64,
/// 最低价
pub low: f64,
/// 收盘价
pub close: f64,
/// 成交量(基础货币)
pub volume: f64,
/// 成交额(报价货币)
pub quote_volume: f64,
/// 成交笔数(可选)
pub trades: Option<u32>,
/// taker买入成交量(基础货币,可选)
pub taker_buy_volume: Option<f64>,
/// taker买入成交额(报价货币,可选)
pub taker_buy_quote_volume: Option<f64>,
/// K线状态:false 代表 K 线未完结,true 代表 K 线已完结
pub confirm: bool,
}

字段说明:

  • timestamp: 该K线的开盘时间,使用Unix时间戳(毫秒)
  • open/high/low/close: 标准OHLC价格数据
  • volume: 该时间段内的总成交量(以基础货币计算)
  • quote_volume: 该时间段内的总成交额(以报价货币计算)
  • trades: 该时间段内的成交笔数,某些交易所可能不提供此数据
  • taker_buy_volume: 主动买入的成交量,用于分析买卖压力
  • taker_buy_quote_volume: 主动买入的成交额
  • confirm: 关键字段,表示该K线是否已完结。未完结的K线数据可能还在变化

📊 Kline(K线数据集合)

包含多根蜡烛图数据的集合:

pub struct Kline {
/// 交易对符号
pub symbol: Symbol,
/// K线时间粒度
pub interval: KlineInterval,
/// 蜡烛图数据列表
pub candles: Vec<Candle>,
}

⏰ KlineInterval(K线时间粒度)

支持的K线时间粒度枚举:

pub enum KlineInterval {
/// 1分钟
Min1, // "1m"
/// 3分钟
Min3, // "3m"
/// 5分钟
Min5, // "5m"
/// 15分钟
Min15, // "15m"
/// 30分钟
Min30, // "30m"
/// 1小时
Hour1, // "1h"
/// 2小时
Hour2, // "2h"
/// 4小时
Hour4, // "4h"
/// 6小时
Hour6, // "6h"
/// 8小时
Hour8, // "8h"
/// 12小时
Hour12, // "12h"
/// 1天
Day1, // "1d"
/// 3天
Day3, // "3d"
/// 1周
Week1, // "1w"
/// 1月
Month1, // "1M"
}

KlineInterval枚举方法:

  • as_str(): 转换为字符串表示(如"1m", "1h", "1d"等)
  • from_str(): 从字符串解析为KlineInterval枚举值
  • default(): 返回默认值(Min1,即1分钟)

字符串映射表:

  • 支持大小写不敏感的解析(如"1h"和"1H"都可以)
  • 月份使用大写"M"("1M")以区别于分钟"m"

使用示例:

# 处理K线数据的完整示例
def analyze_kline_data(kline_data):
"""分析K线数据"""
symbol = kline_data['symbol']
interval = kline_data['interval']
candles = kline_data['candles']

print(f"分析 {symbol}{interval} K线数据,共 {len(candles)} 根")

for i, candle in enumerate(candles):
# 检查K线是否完结
status = "已完结" if candle['confirm'] else "未完结"

# 计算涨跌幅
change_pct = (candle['close'] - candle['open']) / candle['open'] * 100

# 计算买卖压力比(如果有taker数据)
buy_pressure = 0
if candle.get('taker_buy_volume') and candle['volume'] > 0:
buy_pressure = candle['taker_buy_volume'] / candle['volume']

# 处理可选的成交笔数信息
trades_count = candle.get('trades', 0)

print(f"K线 {i+1}: 时间={candle['timestamp']}, "
f"OHLC=({candle['open']}, {candle['high']}, {candle['low']}, {candle['close']}), "
f"涨跌幅={change_pct:.2f}%, 成交量={candle['volume']}, "
f"买压={buy_pressure:.2f}, 成交笔数={trades_count}, 状态={status}")

# 只对已完结的K线进行技术分析
if candle['confirm']:
# 进行技术分析逻辑
pass

# 获取并分析K线数据
result = trader.get_kline(0, "BTC_USDT", "1h", limit=24)

if "Ok" in result:
kline_data = result["Ok"]
analyze_kline_data(kline_data)
else:
error = result.get("Err", "未知错误")
print(f"获取K线数据失败: {error}")

K线数据使用最佳实践:

  1. 检查K线完结状态: 始终检查confirm字段,只对已完结的K线进行技术分析
  2. 处理可选字段: tradestaker_buy_volume等字段可能为空,需要安全处理
  3. 时间戳处理: timestamp为毫秒级Unix时间戳,转换为日期时需要除以1000
  4. 买卖压力分析: 利用taker_buy_volume分析市场买卖压力
  5. 成交活跃度: 通过trades字段分析市场活跃程度
# K线数据安全处理示例
def safe_process_candle(candle):
"""安全处理K线数据"""
# 基础OHLCV数据(必有)
timestamp = candle['timestamp']
ohlc = (candle['open'], candle['high'], candle['low'], candle['close'])
volume = candle['volume']
quote_volume = candle['quote_volume']
is_confirmed = candle['confirm']

# 可选数据的安全处理
trades = candle.get('trades')
taker_buy_vol = candle.get('taker_buy_volume')
taker_buy_quote_vol = candle.get('taker_buy_quote_volume')

# 计算衍生指标
price_change = (ohlc[3] - ohlc[0]) / ohlc[0] * 100 if ohlc[0] > 0 else 0

# 买卖压力分析(如果有数据)
buy_pressure = None
if taker_buy_vol is not None and volume > 0:
buy_pressure = taker_buy_vol / volume

# 平均每笔交易金额(如果有数据)
avg_trade_size = None
if trades is not None and trades > 0:
avg_trade_size = quote_volume / trades

return {
'timestamp': timestamp,
'ohlc': ohlc,
'volume': volume,
'quote_volume': quote_volume,
'price_change_pct': price_change,
'buy_pressure': buy_pressure,
'avg_trade_size': avg_trade_size,
'trades_count': trades,
'is_confirmed': is_confirmed
}

8.2 Position(持仓)数据结构

📊 Position(持仓数据)

持仓数据结构包含完整的仓位信息:

{
# 仓位id(可选)
"id": "position_123456",

# 交易对符号
"symbol": "BTC_USDT",

# 创建仓位的Unix时间戳,以毫秒为单位(可选)
"ctime": 1640995200000,

# 仓位更新的Unix时间戳,以毫秒为单位(可选)
"utime": 1640998800000,

# 保证金模式
"margin_mode": "Cross", # "Cross"(全仓)或 "Isolated"(逐仓)

# 维持保证金(可选)
"margin": 1000.0,

# 维持保证金率(可选)
"mmr": 0.005,

# 仓位方向
"side": "Long", # "Long"(多头)或 "Short"(空头)

# 仓位的杠杆率
"leverage": 10,

# 仓位数量
"amount": 0.5,

# 仓位的平均开仓价格
"entry_price": 50000.0,

# 未实现盈亏,市场价格和开仓价格之间的差异乘以合约数量,可以为负
"unrealized_pnl": 250.5,

# 爆仓价格(可选)
"liquidation_price": 45000.0,

# 标记价格(可选)
"mark_price": 50500.0
}

字段说明:

字段类型必填说明
id字符串仓位唯一标识符
symbol字符串交易对符号
ctime整数创建时间(毫秒时间戳)
utime整数更新时间(毫秒时间戳)
margin_mode字符串保证金模式:"Cross"(全仓)或"Isolated"(逐仓)
margin浮点数维持保证金金额
mmr浮点数维持保证金率
side字符串仓位方向:"Long"(多头)或"Short"(空头)
leverage整数杠杆倍数(1-125)
amount浮点数仓位数量(正数)
entry_price浮点数平均开仓价格
unrealized_pnl浮点数未实现盈亏(可为负)
liquidation_price浮点数预估爆仓价格
mark_price浮点数当前标记价格

📊 MaxPosition(最大可开仓数据)

最大可开仓数据结构:

{
# 多仓对应Quote名义价值
"long_notional": 100000.0,

# 空仓对应Quote名义价值
"short_notional": 100000.0,

# 多仓对应Base数量
"long_quantity": 2.0,

# 空仓对应Base数量
"short_quantity": 2.0
}

字段说明:

字段类型说明
long_notional浮点数多仓最大名义价值(以报价货币计)
short_notional浮点数空仓最大名义价值(以报价货币计)
long_quantity浮点数多仓最大数量(以基础货币计)
short_quantity浮点数空仓最大数量(以基础货币计)

使用示例:

# 获取单个持仓信息
result = trader.get_position(0, "BTC_USDT")

if "Ok" in result:
position = result["Ok"]
if position: # 有持仓
print(f"交易对: {position['symbol']}")
print(f"方向: {position['side']}")
print(f"数量: {position['amount']}")
print(f"开仓价: {position['entry_price']}")
print(f"未实现盈亏: {position['unrealized_pnl']}")
print(f"杠杆: {position['leverage']}x")
print(f"保证金模式: {position['margin_mode']}")

# 计算盈亏比例
if position['entry_price'] > 0:
pnl_pct = position['unrealized_pnl'] / (position['amount'] * position['entry_price']) * 100
print(f"盈亏比例: {pnl_pct:.2f}%")

# 风险评估
if position.get('liquidation_price'):
mark = position.get('mark_price', position['entry_price'])
if position['side'] == 'Long':
risk_pct = (mark - position['liquidation_price']) / mark * 100
else:
risk_pct = (position['liquidation_price'] - mark) / mark * 100
print(f"距离爆仓: {risk_pct:.2f}%")
else:
print("无持仓")
else:
error = result.get("Err", "未知错误")
print(f"获取持仓失败: {error}")

# 获取所有持仓信息
result = trader.get_positions(0)

if "Ok" in result:
positions = result["Ok"]
print(f"持仓数量: {len(positions)}")

# 统计持仓信息
total_pnl = 0
long_value = 0
short_value = 0

for pos in positions:
symbol = pos['symbol']
side = pos['side']
amount = pos['amount']
pnl = pos['unrealized_pnl']
mark = pos.get('mark_price', pos['entry_price'])

# 计算持仓价值
position_value = amount * mark
if side == 'Long':
long_value += position_value
else:
short_value += position_value

total_pnl += pnl

# 打印每个持仓
print(f"{symbol} {side}: 数量={amount}, PNL={pnl:.2f}, 价值={position_value:.2f}")

print(f"\n汇总统计:")
print(f"总未实现盈亏: {total_pnl:.2f}")
print(f"多头总价值: {long_value:.2f}")
print(f"空头总价值: {short_value:.2f}")
print(f"净敞口: {long_value - short_value:.2f}")
else:
error = result.get("Err", "未知错误")
print(f"获取所有持仓失败: {error}")

持仓数据处理最佳实践:

  1. 空仓检查: 返回值可能为空列表或None,表示无持仓
  2. 可选字段处理: liquidation_pricemark_price等字段可能不存在
  3. 风险计算: 利用爆仓价格和标记价格计算风险水平
  4. 盈亏分析: 结合数量和开仓价格计算盈亏比例
  5. 保证金监控: 关注维持保证金率,防止爆仓风险
# 持仓风险管理示例
def analyze_position_risk(position):
"""分析持仓风险"""
symbol = position['symbol']
side = position['side']
amount = position['amount']
entry_price = position['entry_price']
unrealized_pnl = position['unrealized_pnl']
leverage = position['leverage']

# 计算持仓价值
position_value = amount * entry_price

# 计算盈亏比例
pnl_percentage = (unrealized_pnl / position_value) * 100 if position_value > 0 else 0

# 风险评级
risk_level = "低"
if leverage > 10:
risk_level = "高"
elif leverage > 5:
risk_level = "中"

# 爆仓风险评估
liquidation_risk = "未知"
if position.get('liquidation_price') and position.get('mark_price'):
liq_price = position['liquidation_price']
mark_price = position['mark_price']

if side == 'Long':
distance_pct = ((mark_price - liq_price) / mark_price) * 100
else:
distance_pct = ((liq_price - mark_price) / mark_price) * 100

if distance_pct < 5:
liquidation_risk = "极高"
elif distance_pct < 10:
liquidation_risk = "高"
elif distance_pct < 20:
liquidation_risk = "中"
else:
liquidation_risk = "低"

return {
'symbol': symbol,
'side': side,
'value': position_value,
'pnl_percentage': pnl_percentage,
'risk_level': risk_level,
'liquidation_risk': liquidation_risk,
'leverage': leverage
}

# 批量分析持仓风险
def analyze_all_positions_risk(positions):
"""批量分析所有持仓风险"""
high_risk_positions = []
total_risk_value = 0

for pos in positions:
risk_info = analyze_position_risk(pos)

# 识别高风险持仓
if risk_info['risk_level'] == '高' or risk_info['liquidation_risk'] in ['高', '极高']:
high_risk_positions.append(risk_info)
total_risk_value += risk_info['value']

return {
'high_risk_count': len(high_risk_positions),
'high_risk_value': total_risk_value,
'high_risk_positions': high_risk_positions
}

8.3 表格数据格式

表格数据格式示例 📋
{
"title": "表格标题",
"cols": ["列1", "列2", "列3", ...],
"rows": [
["行1值1", "行1值2", "行1值3", ...],
["行2值1", "行2值2", "行2值3", ...],
...
]
}

8.4 WebClient配置格式

WebClient配置示例 ⚙️
{
"server_name": "策略名称", // 策略服务器名称,用于在Web平台上标识
"primary_balance": 10000.0, // 主账户初始余额,仅首次初始化需要
"secondary_balance": 5000.0, // 次账户初始余额,仅首次初始化需要
"is_production": true, // 是否为生产环境,影响风控级别
"open_threshold": 0.14, // 开仓阈值,如0.14代表千1.4价差开仓
"max_position_ratio": 100, // 单个币种最大持仓比例,如100代表100%,1x杠杆
"max_leverage": 10, // 最大可用杠杆倍数
"funding_rate_threshold": 0.1, // 资金费率阈值,如0.1代表千1资金费率开仓
"cost": 0.0824, // 成本 主所开平 副所开平,4次交易手续费,如0.0824 代表万8.24
"primary_maker_fee_rate": 0.0002, // 主所maker手续费率
"primary_taker_fee_rate": 0.0005, // 主所taker手续费率
"primary_rebate_rate": 0.3, // 主所返佣率
"secondary_maker_fee_rate": 0.0002, // 次所maker手续费率
"secondary_taker_fee_rate": 0.0005, // 次所taker手续费率
"secondary_rebate_rate": 0.7, // 次所返佣率
}

所有字段均为可选项,但推荐在首次初始化时设置server_nameprimary_balancesecondary_balance。初始余额设置后会被缓存,后续启动策略时会自动从缓存中读取,无需再次设置。

缓存机制说明: WebClient的配置数据(包括初始余额、交易统计数据如总成交量、成交次数、失败次数、成功次数、胜率、利润等)会被缓存到本地文件系统。缓存文件以 {server_name}.json 的格式命名,存储在当前工作目录中。当调用统计和余额相关的API(如update_trade_stats、update_total_balance等)更新数据时,缓存文件会实时自动更新,无需手动操作。这就是为什么server_name参数非常重要,它不仅用于Web界面标识,还用于定位缓存文件。不同的策略应使用不同的server_name以避免缓存冲突。WebClient缓存是自动管理的,与策略缓存(cache_save/cache_load)完全独立。

8.5 命令格式说明

v2版本的命令格式相比v1版本更加扁平化,直接使用方法名作为顶级字段:

v1格式(嵌套结构):

{
"account_id": 0,
"cmd": {
"Sync": "UsdtBalance"
}
}

v2格式(扁平化结构):

{
"account_id": 0,
"method": "UsdtBalance",
"sync": true
}