- Add PositionTracker.sol: tracks position lifecycle (open/close per recenter), records tick ranges, liquidity, entry/exit blocks/timestamps, token amounts (via LiquidityAmounts math), fees (proportional to liquidity share), IL (LP exit value − HODL value at exit price), and net P&L per position. Aggregates total fees, cumulative IL, net P&L, rebalance count, Anchor time-in-range, and capital efficiency accumulators. Logs with [TRACKER][TYPE] prefix; emits cumulative P&L every 500 blocks. - Modify StrategyExecutor.sol: add IUniswapV3Pool + token0isWeth to constructor (creates PositionTracker internally), call tracker.notifyBlock() on every block for time-in-range, and call tracker.recordRecenter() on each successful recenter. logSummary() now delegates to tracker.logFinalSummary(). - Modify BacktestRunner.s.sol: pass sp.pool and token0isWeth to StrategyExecutor constructor; log tracker address. - forge fmt: reformat all backtesting scripts and affected src/test files to project style (number_underscore=thousands, multiline_func_header=all). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a491ecb7d9
commit
cfcf750084
12 changed files with 666 additions and 244 deletions
|
|
@ -1,10 +1,12 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
pragma solidity ^0.8.19;
|
||||
|
||||
import { console2 } from "forge-std/console2.sol";
|
||||
import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol";
|
||||
import { LiquidityManager } from "../../src/LiquidityManager.sol";
|
||||
import { ThreePositionStrategy } from "../../src/abstracts/ThreePositionStrategy.sol";
|
||||
import { PositionTracker } from "./PositionTracker.sol";
|
||||
import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol";
|
||||
import { IUniswapV3Pool } from "@uniswap-v3-core/interfaces/IUniswapV3Pool.sol";
|
||||
import { console2 } from "forge-std/console2.sol";
|
||||
|
||||
/**
|
||||
* @title StrategyExecutor
|
||||
|
|
@ -17,6 +19,10 @@ import { ThreePositionStrategy } from "../../src/abstracts/ThreePositionStrategy
|
|||
* - Fees collected (WETH + KRK) as the delta of feeDestination's balances
|
||||
* - Revert reason on failure (logged and skipped — replay never halts)
|
||||
*
|
||||
* Position tracking and P&L metrics are delegated to PositionTracker, which is
|
||||
* notified on every block (for time-in-range) and on each successful recenter
|
||||
* (for position lifecycle and fee/IL accounting).
|
||||
*
|
||||
* Access model: StrategyExecutor must be set as recenterAccess on the LM so that
|
||||
* the cooldown and TWAP price-stability checks are bypassed in the simulation
|
||||
* (vm.warp advances simulated time, not real oracle state).
|
||||
|
|
@ -37,6 +43,8 @@ contract StrategyExecutor {
|
|||
address public immutable feeDestination;
|
||||
/// @notice Minimum block gap between recenter attempts.
|
||||
uint256 public immutable recenterInterval;
|
||||
/// @notice Position tracker — records lifecycle, fees, and P&L for each position.
|
||||
PositionTracker public immutable tracker;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Runtime state
|
||||
|
|
@ -55,13 +63,16 @@ contract StrategyExecutor {
|
|||
IERC20 _wethToken,
|
||||
IERC20 _krkToken,
|
||||
address _feeDestination,
|
||||
uint256 _recenterInterval
|
||||
uint256 _recenterInterval,
|
||||
IUniswapV3Pool _pool,
|
||||
bool _token0isWeth
|
||||
) {
|
||||
lm = _lm;
|
||||
wethToken = _wethToken;
|
||||
krkToken = _krkToken;
|
||||
feeDestination = _feeDestination;
|
||||
recenterInterval = _recenterInterval;
|
||||
tracker = new PositionTracker(_pool, _token0isWeth);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
|
@ -70,9 +81,13 @@ contract StrategyExecutor {
|
|||
|
||||
/**
|
||||
* @notice Attempt a recenter if enough blocks have elapsed since the last one.
|
||||
* Always notifies the tracker of the current block for time-in-range accounting.
|
||||
* @param blockNum Current block number (as advanced by EventReplayer via vm.roll).
|
||||
*/
|
||||
function maybeRecenter(uint256 blockNum) external {
|
||||
// Always notify the tracker so time-in-range is counted for every observed block.
|
||||
tracker.notifyBlock(blockNum);
|
||||
|
||||
if (blockNum - lastRecenterBlock < recenterInterval) return;
|
||||
|
||||
// Snapshot pre-recenter positions.
|
||||
|
|
@ -103,9 +118,7 @@ contract StrategyExecutor {
|
|||
}
|
||||
|
||||
if (!success) {
|
||||
console2.log(
|
||||
string.concat("[recenter SKIP @ block ", _str(blockNum), "] reason: ", failReason)
|
||||
);
|
||||
console2.log(string.concat("[recenter SKIP @ block ", _str(blockNum), "] reason: ", failReason));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -117,14 +130,56 @@ contract StrategyExecutor {
|
|||
uint256 feesWeth = wethToken.balanceOf(feeDestination) - wethPre;
|
||||
uint256 feesKrk = krkToken.balanceOf(feeDestination) - krkPre;
|
||||
|
||||
// Record recenter in position tracker.
|
||||
tracker.recordRecenter(
|
||||
PositionTracker.PositionSnapshot({
|
||||
floorLiq: fLiqPre,
|
||||
floorLo: fLoPre,
|
||||
floorHi: fHiPre,
|
||||
anchorLiq: aLiqPre,
|
||||
anchorLo: aLoPre,
|
||||
anchorHi: aHiPre,
|
||||
discLiq: dLiqPre,
|
||||
discLo: dLoPre,
|
||||
discHi: dHiPre
|
||||
}),
|
||||
PositionTracker.PositionSnapshot({
|
||||
floorLiq: fLiqPost,
|
||||
floorLo: fLoPost,
|
||||
floorHi: fHiPost,
|
||||
anchorLiq: aLiqPost,
|
||||
anchorLo: aLoPost,
|
||||
anchorHi: aHiPost,
|
||||
discLiq: dLiqPost,
|
||||
discLo: dLoPost,
|
||||
discHi: dHiPost
|
||||
}),
|
||||
feesWeth,
|
||||
feesKrk,
|
||||
blockNum,
|
||||
block.timestamp
|
||||
);
|
||||
|
||||
_logRecenter(
|
||||
blockNum,
|
||||
fLiqPre, fLoPre, fHiPre,
|
||||
aLiqPre, aLoPre, aHiPre,
|
||||
dLiqPre, dLoPre, dHiPre,
|
||||
fLiqPost, fLoPost, fHiPost,
|
||||
aLiqPost, aLoPost, aHiPost,
|
||||
dLiqPost, dLoPost, dHiPost,
|
||||
fLiqPre,
|
||||
fLoPre,
|
||||
fHiPre,
|
||||
aLiqPre,
|
||||
aLoPre,
|
||||
aHiPre,
|
||||
dLiqPre,
|
||||
dLoPre,
|
||||
dHiPre,
|
||||
fLiqPost,
|
||||
fLoPost,
|
||||
fHiPost,
|
||||
aLiqPost,
|
||||
aLoPost,
|
||||
aHiPost,
|
||||
dLiqPost,
|
||||
dLoPost,
|
||||
dHiPost,
|
||||
feesWeth,
|
||||
feesKrk
|
||||
);
|
||||
|
|
@ -146,36 +201,11 @@ contract StrategyExecutor {
|
|||
console2.log("Total recenters: ", totalRecenters);
|
||||
console2.log("Failed recenters: ", failedRecenters);
|
||||
console2.log("Recenter interval:", recenterInterval, "blocks");
|
||||
console2.log(
|
||||
string.concat(
|
||||
"Final Floor: tick [",
|
||||
_istr(fLo),
|
||||
", ",
|
||||
_istr(fHi),
|
||||
"] liq=",
|
||||
_str(uint256(fLiq))
|
||||
)
|
||||
);
|
||||
console2.log(
|
||||
string.concat(
|
||||
"Final Anchor: tick [",
|
||||
_istr(aLo),
|
||||
", ",
|
||||
_istr(aHi),
|
||||
"] liq=",
|
||||
_str(uint256(aLiq))
|
||||
)
|
||||
);
|
||||
console2.log(
|
||||
string.concat(
|
||||
"Final Discovery: tick [",
|
||||
_istr(dLo),
|
||||
", ",
|
||||
_istr(dHi),
|
||||
"] liq=",
|
||||
_str(uint256(dLiq))
|
||||
)
|
||||
);
|
||||
console2.log(string.concat("Final Floor: tick [", _istr(fLo), ", ", _istr(fHi), "] liq=", _str(uint256(fLiq))));
|
||||
console2.log(string.concat("Final Anchor: tick [", _istr(aLo), ", ", _istr(aHi), "] liq=", _str(uint256(aLiq))));
|
||||
console2.log(string.concat("Final Discovery: tick [", _istr(dLo), ", ", _istr(dHi), "] liq=", _str(uint256(dLiq))));
|
||||
|
||||
tracker.logFinalSummary(lastRecenterBlock);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
|
@ -184,84 +214,38 @@ contract StrategyExecutor {
|
|||
|
||||
function _logRecenter(
|
||||
uint256 blockNum,
|
||||
uint128 fLiqPre, int24 fLoPre, int24 fHiPre,
|
||||
uint128 aLiqPre, int24 aLoPre, int24 aHiPre,
|
||||
uint128 dLiqPre, int24 dLoPre, int24 dHiPre,
|
||||
uint128 fLiqPost, int24 fLoPost, int24 fHiPost,
|
||||
uint128 aLiqPost, int24 aLoPost, int24 aHiPost,
|
||||
uint128 dLiqPost, int24 dLoPost, int24 dHiPost,
|
||||
uint128 fLiqPre,
|
||||
int24 fLoPre,
|
||||
int24 fHiPre,
|
||||
uint128 aLiqPre,
|
||||
int24 aLoPre,
|
||||
int24 aHiPre,
|
||||
uint128 dLiqPre,
|
||||
int24 dLoPre,
|
||||
int24 dHiPre,
|
||||
uint128 fLiqPost,
|
||||
int24 fLoPost,
|
||||
int24 fHiPost,
|
||||
uint128 aLiqPost,
|
||||
int24 aLoPost,
|
||||
int24 aHiPost,
|
||||
uint128 dLiqPost,
|
||||
int24 dLoPost,
|
||||
int24 dHiPost,
|
||||
uint256 feesWeth,
|
||||
uint256 feesKrk
|
||||
)
|
||||
internal
|
||||
view
|
||||
{
|
||||
console2.log(
|
||||
string.concat("=== Recenter #", _str(totalRecenters), " @ block ", _str(blockNum), " ===")
|
||||
);
|
||||
console2.log(
|
||||
string.concat(
|
||||
" Floor pre: tick [",
|
||||
_istr(fLoPre),
|
||||
", ",
|
||||
_istr(fHiPre),
|
||||
"] liq=",
|
||||
_str(uint256(fLiqPre))
|
||||
)
|
||||
);
|
||||
console2.log(
|
||||
string.concat(
|
||||
" Anchor pre: tick [",
|
||||
_istr(aLoPre),
|
||||
", ",
|
||||
_istr(aHiPre),
|
||||
"] liq=",
|
||||
_str(uint256(aLiqPre))
|
||||
)
|
||||
);
|
||||
console2.log(
|
||||
string.concat(
|
||||
" Disc pre: tick [",
|
||||
_istr(dLoPre),
|
||||
", ",
|
||||
_istr(dHiPre),
|
||||
"] liq=",
|
||||
_str(uint256(dLiqPre))
|
||||
)
|
||||
);
|
||||
console2.log(
|
||||
string.concat(
|
||||
" Floor post: tick [",
|
||||
_istr(fLoPost),
|
||||
", ",
|
||||
_istr(fHiPost),
|
||||
"] liq=",
|
||||
_str(uint256(fLiqPost))
|
||||
)
|
||||
);
|
||||
console2.log(
|
||||
string.concat(
|
||||
" Anchor post: tick [",
|
||||
_istr(aLoPost),
|
||||
", ",
|
||||
_istr(aHiPost),
|
||||
"] liq=",
|
||||
_str(uint256(aLiqPost))
|
||||
)
|
||||
);
|
||||
console2.log(
|
||||
string.concat(
|
||||
" Disc post: tick [",
|
||||
_istr(dLoPost),
|
||||
", ",
|
||||
_istr(dHiPost),
|
||||
"] liq=",
|
||||
_str(uint256(dLiqPost))
|
||||
)
|
||||
);
|
||||
console2.log(
|
||||
string.concat(" Fees WETH: ", _str(feesWeth), " Fees KRK: ", _str(feesKrk))
|
||||
);
|
||||
console2.log(string.concat("=== Recenter #", _str(totalRecenters), " @ block ", _str(blockNum), " ==="));
|
||||
console2.log(string.concat(" Floor pre: tick [", _istr(fLoPre), ", ", _istr(fHiPre), "] liq=", _str(uint256(fLiqPre))));
|
||||
console2.log(string.concat(" Anchor pre: tick [", _istr(aLoPre), ", ", _istr(aHiPre), "] liq=", _str(uint256(aLiqPre))));
|
||||
console2.log(string.concat(" Disc pre: tick [", _istr(dLoPre), ", ", _istr(dHiPre), "] liq=", _str(uint256(dLiqPre))));
|
||||
console2.log(string.concat(" Floor post: tick [", _istr(fLoPost), ", ", _istr(fHiPost), "] liq=", _str(uint256(fLiqPost))));
|
||||
console2.log(string.concat(" Anchor post: tick [", _istr(aLoPost), ", ", _istr(aHiPost), "] liq=", _str(uint256(aLiqPost))));
|
||||
console2.log(string.concat(" Disc post: tick [", _istr(dLoPost), ", ", _istr(dHiPost), "] liq=", _str(uint256(dLiqPost))));
|
||||
console2.log(string.concat(" Fees WETH: ", _str(feesWeth), " Fees KRK: ", _str(feesKrk)));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue