共享内存资产&策略资产¶
核心需求¶
- 主要解决同一个账户内资金动态分配的问题
- 支持一个账户下多个子策略的资金分配,一个账户内的资金分成多份,给多个子策略使用。每个子策略对应一个虚拟账户,虚拟账户内的钱是这个子策略的最大可用资金,不需要用完
- 支持资金转出和转出这样的业务场景
- 发现和交易所资金不一致的时候,有恢复策略
- 支持跨进程同步,支持借贷
资产¶
如果一个账户内的资产只对应一个策略,不需要做资金划分,用如下简单的方式就可以跨进程的操作资产
示例:写入Currency的Balance
#include "hft/util/shm.h"
Exchange exchange = Exchange::KRAKEN;
DataType dataType = DataType::BALANCE;
Currency currency = Currency::USDT;
int64_t now = getCurrentTimestamp();
SingleQuote usdtBalance{now, now, 10000.0}; // 余额为10000 USDT
ShareMemorySingleQuote::write(exchange, dataType, currency, usdtBalance); // 更新共享内存
import hft
import time
write_single_quote_currency = hft.writeSingleQuoteCurrency
def write_balance(exchange: Exchange, currency: Currency, balance: float, local_timestamp: int = None) -> None:
if local_timestamp is None:
local_timestamp = int(time.time() * 1_000_000)
write_single_quote_currency(
exchange,
DataType.BALANCE,
currency,
SingleQuote(timestamp=local_timestamp,
localTimestamp=local_timestamp,
mid=balance)
)
# 写入USDT余额
write_balance(Exchange.KRAKEN, Currency.USDT, 10000.0)
示例:获取Currency的Balance
#include "hft/util/shm.h"
Exchange exchange = Exchange::KRAKEN;
DataType dataType = DataType::BALANCE;
Currency currency = Currency::USDT;
SingleQuote *usdtBalance = ShareMemorySingleQuote::get(exchange, dataType, currency);
// 输出余额
std::cout << "USDT Balance: " << usdtBalance->mid << std::endl;
import hft
get_single_quote_currency = hft.getSingleQuoteCurrency
def get_balance(exchange: Exchange, currency: Currency) -> SingleQuote:
return get_single_quote_currency(
exchange,
DataType.BALANCE,
currency,
)
# 获取并输出USDT余额
usdt_balance = get_balance(Exchange.KRAKEN, Currency.USDT)
print(f"USDT Balance: {usdt_balance.mid}")
策略资产¶
- 模拟虚拟子账户的逻辑,每个虚拟子账户对一个子策略
- 最多支持63个子策略(1个主策略/钱包 + 62个其他策略)
- strategy[0] = 总资金
- strategy[1] = 主策略(也可以理解成钱包或者资金池)
- strategy[2..N] = 其它策略资金(配额)
- 在使用之前要先初始化,具体的策略数量上层逻辑自己确定,只有
strategies[0..strategyCount]范围内的数据保证有效
结构¶
┌─────────────────────────────────────────────────────────────────┐
│ StrategyBalance │
│ Exchange: KRAKEN Currency: USDC strategyCount: 5 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ strategies[64] 数组 │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ [0] 总资金: 10000.0 ◄── 账户真实余额 │ │
│ ├─────────────────────────────────────────────────────────┤ │
│ │ [1] 主策略: 4000.0 ◄── 资金池,其他策略从这里申请 │ │
│ ├─────────────────────────────────────────────────────────┤ │
│ │ [2] 策略A: 2000.0 ◄── 子策略占用 │ │
│ ├─────────────────────────────────────────────────────────┤ │
│ │ [3] 策略B: 3000.0 ◄── 子策略占用 │ │
│ ├─────────────────────────────────────────────────────────┤ │
│ │ [4] 策略C: 1000.0 ◄── 子策略占用 │ │
│ ├─────────────────────────────────────────────────────────┤ │
│ │ [5..63] 未使用 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 约束: strategies[0] = strategies[1] + strategies[2] + ... │
│ 10000 = 4000 + 2000 + 3000 + 1000 ✓ │
│ │
└─────────────────────────────────────────────────────────────────┘
资金流动¶
- 恒等式:
strategies[0] = sum(strategies[1:strategyCount]) - 申请资金:
strategies[1] -= amount; strategies[i] += amount; - 归还资金:
strategies[i] -= amount; strategies[1] += amount;
┌──────────────────────┐
│ [0] 总资金 10000 │
│ (账户真实余额) │
└──────────────────────┘
│
│ 分配
▼
┌─────────────────────────────────────────────┐
│ [1] 主策略 4000 │
│ (资金池/未分配) │
└─────────────────────────────────────────────┘
│ │ │
申请资金│ 申请资金│ 申请资金│
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
│[2] 策略A │ │[3] 策略B │ │[4] 策略C │
│ 2000 │ │ 3000 │ │ 1000 │
└───────────┘ └───────────┘ └───────────┘
│ │ │
归还资金│ 归还资金│ 归还资金│
└─────────────┼─────────────┘
▼
┌──────────────────────┐
│ [1] 主策略 (资金池) │
└──────────────────────┘
API¶
所有涉及资金操作的API调用之后,会自动再平衡,保证恒等式的成立
| 方法 | 说明 |
|---|---|
reset(currency, balance) |
重置:总资金=主策略=balance,子策略=0 |
set(currency, amount, index=0) |
设置某索引的值,通常不需要直接使用这个API |
adjust(currency, delta, index=0) |
增减某索引的值(+/-),通常只会操作总资金 |
allocate(currency, index, amount) |
子策略申请资金 |
release(currency, index, amount) |
子策略归还资金 |
get(currency) |
获取完整 StrategyBalance |
get_balance(currency, index=0) |
获取某索引的资金,默认返回总资金 |
业务场景¶
- StrategyBalance表示一个Exchange,一个Currency的资产
- StrategyBalanceManager管理一个Exchange,所有Currency的资产
系统启动¶
- 资产的管理者,例如:网关模块需要执行这部分初始化逻辑
- 调用
reset初始化资产
from hftpy.common import StrategyBalanceManager
import hft
# 创建管理器(Kraken交易所,8个策略槽位)
manager = StrategyBalanceManager(hft.Exchange.KRAKEN, strategy_count=8)
# 获取账户余额(从交易所API)
usdc_balance = 10000.0
usd_balance = 5000.0
# 初始化各币种资产
manager.reset(hft.Currency.USDC, usdc_balance)
manager.reset(hft.Currency.USD, usd_balance)
资产买卖¶
- 卖出一个资产,或者买入一个资产
- 本质是总资金的变化,调用
adjust调整总资产
# 买入:买入usdc/usd
manager.adjust(hft.Currency.USD, -1000.0) # USD 减少
manager.adjust(hft.Currency.USDC, 1000.0) # USDC 增加
# 卖出:卖出usdc/usd
manager.adjust(hft.Currency.USDC, -500.0) # USDC 减少
manager.adjust(hft.Currency.USD, 500.0) # USD 增加
子策略申请或者归还资金¶
- 调用
allocate或者release来申请或者归还资金
# 策略A(index=2)申请 2000 USDC
manager.allocate(hft.Currency.USDC, index=2, amount=2000.0)
# 策略B(index=3)申请 3000 USDC
manager.allocate(hft.Currency.USDC, index=3, amount=3000.0)
# 策略A 归还 500 USDC
manager.release(hft.Currency.USDC, index=2, amount=500.0)
# 查询各策略资金
total = manager.get_balance(hft.Currency.USDC, index=0) # 总资金
available = manager.get_balance(hft.Currency.USDC, index=1) # 主策略(可用)
strategy_a = manager.get_balance(hft.Currency.USDC, index=2) # 策略A
strategy_b = manager.get_balance(hft.Currency.USDC, index=3) # 策略B
转账成功¶
- 成功转入或者转出,本质上和买入和卖出一个资产是一样的,调用
adjust调整总资产
# 转入 1000 USDC(从其他交易所转入)
manager.adjust(hft.Currency.USDC, 1000.0)
# 转出 500 USDC(转到其他交易所)
manager.adjust(hft.Currency.USDC, -500.0)
错误恢复¶
- 错误恢复的逻辑:当发现本地的资产和交易所不一致的时候,调整总资产,子策略的资产不动,同时调整主策略(钱包)的余额,保证恒等式成立
- 注意:如果资金修复后,主策略资金 + 借贷额度 < 0,表示资金错误是无法修复的,这时候的简单处理方式是
reset资产,让每个子策略重新申请自己需要的资产
# 从交易所获取真实余额
real_balance = 从交易所获取的资产
# 修正总资产(set会自动refresh,重算主策略)
manager.set(hft.Currency.USDC, real_balance)
# 检查主策略是否为负(考虑借贷额度)
available = manager.get_balance(hft.Currency.USDC, index=1)
credit_limit = 5000.0 # 借贷额度
if available + credit_limit < 0:
# 资金错误无法修复,需要重置
logger.error(f"资金错误无法修复,主策略余额为负且超过借贷额度: {available}, 借贷额度: {credit_limit}")
manager.reset(hft.Currency.USDC, real_balance)
# 子策略需要重新申请资金...
资金的状态检测¶
- 网关模块会定期更新资产,及时资产没有变化,也会在和交易所同步成功后更新下资产的时间戳,如果资产超过一个阈值名有更新,可以任务资产状态不可用
import time
from hftpy.common import StrategyBalanceManager, StrategyBalance
import hft
manager = StrategyBalanceManager(hft.Exchange.KRAKEN, strategy_count=8)
max_timestamp_delay = 10_000_000 # 10秒,单位微秒
current_time = int(time.time() * 1e6)
currency_balance: StrategyBalance = manager.get(hft.Currency.USDC)
# 时效性检测:检查数据是否过期
if (current_time - currency_balance.localTimestamp) > max_timestamp_delay:
logger.warning(f"Invalid balance data for {hft.Currency.USDC}, {currency_balance}")
return
借贷¶
借贷是一个额外的逻辑,账户中存储的资产是不包括借贷额度的。每种资产的借贷额度配置文件中单独配置。资产当前的数量+借贷额度,是这个资产当前真正的可用额度。例如:如果一个资产当前的余额是100,借贷额度是50,这个资产的当前可用额度是100+50=150;如果一个资产当前的余额是-30,借贷额度是50,这个资产当前可用额度是-30+50=20。正常情况下,主策略(资金池)中的资金加上借贷额度,应该是大于等于0的。如果不满足这个条件,表示资金状态出错,需要用reset重置资金状态