Solidity 与 LP 策略学习笔记

本文整理 Solidity 函数可见性、数据位置和集中流动性策略中的基础判断。两个主题都强调状态边界:Solidity 关注调用与存储状态,LP 策略关注价格区间与仓位状态。

Solidity 函数可见性

可见性 合约内部直接调用 派生合约调用 外部账户或合约调用
private 可以 不可以 不可以
internal 可以 可以 不可以
public 可以 可以 可以
external 不可以 不可以直接调用 可以

external 函数不能在同一合约中通过函数名直接执行内部调用。若确需调用,可使用 this.operation() 发起外部消息调用,但这会改变 msg.sender、增加 Gas,并产生新的调用边界。复用逻辑时,更合理的做法是抽取 internal 函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pragma solidity ^0.8.20;

contract VisibilityDemo {
function operation(uint256 value) external pure returns (uint256) {
return _operation(value);
}

function execute(uint256 value) public pure returns (uint256) {
return _operation(value);
}

function _operation(uint256 value) internal pure returns (uint256) {
return value * 2;
}
}
1
2
3
4
5
flowchart TD
A[外部调用者] --> B[external operation]
B --> C[internal _operation]
D[public execute] --> C
E[同合约其他函数] --> C

内部调用与外部调用

内部调用使用跳转执行,保留当前调用上下文。外部调用通过 EVM 消息调用进入目标函数,重新进行 ABI 编码与解码,并可能触发回调。this.operation() 中的 msg.sender 是当前合约地址,而不是原始调用者。

不应为了调用自己的 external 函数而普遍使用 this。除了额外成本,它还会引入重入面与异常处理差异。

Memory 与 Storage

memory 参数接收独立副本,对它的修改不会写回状态。storage 引用直接指向链上存储,对其修改会改变状态变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
contract DataLocationDemo {
uint256[5] private values = [uint256(10), 20, 30, 40, 50];

function operation() external returns (uint256, uint256) {
_changeMemory(values);
_changeStorage(values);
return (values[3], values[4]);
}

function _changeMemory(uint256[5] memory input) internal pure {
input[3] = 8;
}

function _changeStorage(uint256[5] storage input) internal {
input[4] = 10;
}
}

执行 operation 后,返回值为 4010_changeMemory 只修改副本,因此 values[3] 保持 40_changeStorage 修改实际状态,因此 values[4] 变为 10

Pure 与 View

  • pure 函数既不读取也不修改合约状态。
  • view 函数可以读取状态,但不能修改状态。
  • 普通非 payable 函数可以读取和修改状态,但不能接收原生代币。
  • payable 函数可以接收原生代币,也可以修改状态。

上例中的 operation_changeStorage 会修改存储,因此不能声明为 pureview。通过 Remix 发送交易时,返回值通常不作为普通调用结果直接展示,验证状态变化更适合读取状态变量、检查事件或编写测试。

LP 策略训练与回测

集中流动性 LP 策略需要根据价格、波动率、成交量、手续费和 Gas 成本动态选择区间。强化学习可以用于探索调仓策略,但必须先建立可验证的模拟环境和基准策略。

1
2
3
4
5
6
7
8
flowchart LR
A[链上池数据] --> B[数据清洗与特征工程]
B --> C[市场与仓位模拟器]
C --> D[策略或强化学习代理]
D --> E[调仓动作]
E --> C
C --> F[收益与风险指标]
F --> G[回测报告]

状态空间

可包含当前价格、区间上下界、仓位内外状态、流动性分布、短期和长期波动率、成交量、手续费增长、Gas 价格、未实现损失与剩余投资期限。所有特征必须基于决策时刻可获得的数据,防止使用未来信息。

动作空间

  • 保持当前区间。
  • 撤出流动性并持有资产。
  • 重新设置更窄或更宽的区间。
  • 调整区间中心,使其偏向预期价格方向。
  • 调整投入比例或保留现金缓冲。

奖励函数

奖励不能只使用手续费收入。较完整的净收益应扣除无常损失、价格滑点、Gas 成本和再平衡成本,并考虑最大回撤与尾部风险。

1
净收益 = 期末仓位价值 - 期初资产价值 + 已收手续费 - Gas 成本 - 滑点成本

撤仓判断示例

若 ETH 与 USDC 仓位当前相对持币基准落后百分之八,过去七天手续费收益为百分之四,预计继续持有三十天,并判断未来波动显著上升,则不能仅用历史手续费线性外推。应重新估计未来成交量、区间外概率、调仓成本和方向性风险。

更稳健的判断流程如下:

  1. 计算继续持有、立即退出和重新设定区间的情景收益。
  2. 对价格趋势、波动率与成交量设置多个情景,而非单点预测。
  3. 计算每个情景的净收益、最大回撤和退出成本。
  4. 当继续持有的风险调整收益持续低于替代方案时执行调仓。

回测要求

  • 使用事件时间顺序切分训练集与测试集。
  • 模拟真实手续费层级、价格冲击、Gas 与交易延迟。
  • 与持币、固定宽度区间和定期调仓等简单基准比较。
  • 防止幸存者偏差、前视偏差与数据重复。
  • 记录策略在极端行情和流动性枯竭阶段的表现。

链上数据可由节点、索引服务或 Dune 等分析平台获取。关系型数据库适合保存标准化事件和仓位快照,XGBoost 与 LightGBM 可用于建立可解释的监督学习基线,再判断是否确有必要引入强化学习。