feat: Optimize discovery position depth calculation

- Implement dynamic discovery depth based on anchor position share
- Add configurable discovery_max_multiple (1.5-4x) for flexible adjustment
- Update BullMarketOptimizer with new depth calculation logic
- Fix scenario visualizer floor position visibility
- Add comprehensive tests for discovery depth behavior

The discovery position now dynamically adjusts its depth based on the anchor
position's share of total liquidity, allowing for more effective price discovery
while maintaining protection against manipulation.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
johba 2025-08-16 16:45:24 +02:00
parent 7ac6b33850
commit 2205ae719b
8 changed files with 383 additions and 95 deletions

View file

@ -76,13 +76,13 @@ contract MockThreePositionStrategy is ThreePositionStrategy {
}
function setAnchorPosition(int24 currentTick, uint256 anchorEthBalance, PositionParams memory params)
external returns (uint256) {
external returns (uint256, uint128) {
return _setAnchorPosition(currentTick, anchorEthBalance, params);
}
function setDiscoveryPosition(int24 currentTick, uint256 pulledHarb, PositionParams memory params)
function setDiscoveryPosition(int24 currentTick, uint128 anchorLiquidity, PositionParams memory params)
external returns (uint256) {
return _setDiscoveryPosition(currentTick, pulledHarb, params);
return _setDiscoveryPosition(currentTick, anchorLiquidity, params);
}
function setFloorPosition(
@ -163,7 +163,7 @@ contract ThreePositionStrategyTest is TestConstants {
ThreePositionStrategy.PositionParams memory params = getDefaultParams();
uint256 anchorEthBalance = 20 ether; // 20% of total
uint256 pulledHarb = strategy.setAnchorPosition(CURRENT_TICK, anchorEthBalance, params);
(uint256 pulledHarb, ) = strategy.setAnchorPosition(CURRENT_TICK, anchorEthBalance, params);
// Verify position was created
assertEq(strategy.getMintedPositionsCount(), 1, "Should have minted one position");
@ -212,16 +212,26 @@ contract ThreePositionStrategyTest is TestConstants {
function testDiscoveryPositionDependsOnAnchor() public {
ThreePositionStrategy.PositionParams memory params = getDefaultParams();
uint256 pulledHarb = 1000 ether; // Simulated from anchor
uint128 anchorLiquidity = 1000e18; // Simulated anchor liquidity
uint256 discoveryAmount = strategy.setDiscoveryPosition(CURRENT_TICK, pulledHarb, params);
uint256 discoveryAmount = strategy.setDiscoveryPosition(CURRENT_TICK, anchorLiquidity, params);
// Discovery amount should be proportional to pulledHarb
// Discovery amount should be proportional to anchor liquidity
assertGt(discoveryAmount, 0, "Discovery amount should be positive");
assertGt(discoveryAmount, pulledHarb / 100, "Discovery should be meaningful portion of pulled HARB");
MockThreePositionStrategy.MintedPosition memory pos = strategy.getMintedPosition(0);
assertEq(uint256(pos.stage), uint256(ThreePositionStrategy.Stage.DISCOVERY), "Should be discovery position");
// Discovery liquidity should ensure multiple times more liquidity per tick
uint256 expectedMultiplier = 200 + (800 * params.discoveryDepth / 10 ** 18);
// Calculate anchor width (same calculation as in _setDiscoveryPosition)
int24 anchorSpacing = 200 + (34 * int24(params.anchorWidth) * 200 / 100);
int24 anchorWidth = 2 * anchorSpacing;
// Adjust for width difference
uint128 expectedLiquidity = uint128(
uint256(anchorLiquidity) * expectedMultiplier * 11000 / (100 * uint256(int256(anchorWidth)))
);
assertEq(pos.liquidity, expectedLiquidity, "Discovery liquidity should match expected multiple adjusted for width");
}
function testDiscoveryPositionPlacement() public {
@ -231,8 +241,8 @@ contract ThreePositionStrategyTest is TestConstants {
// Test with WETH as token0
strategy = new MockThreePositionStrategy(HARB_TOKEN, WETH_TOKEN, token0IsWeth, ETH_BALANCE, OUTSTANDING_SUPPLY);
uint256 pulledHarb = 1000 ether;
strategy.setDiscoveryPosition(CURRENT_TICK, pulledHarb, params);
uint128 anchorLiquidity = 1000e18;
strategy.setDiscoveryPosition(CURRENT_TICK, anchorLiquidity, params);
MockThreePositionStrategy.MintedPosition memory pos = strategy.getMintedPosition(0);
@ -245,12 +255,12 @@ contract ThreePositionStrategyTest is TestConstants {
ThreePositionStrategy.PositionParams memory params = getDefaultParams();
params.discoveryDepth = 10 ** 18; // Maximum depth (100%)
uint256 pulledHarb = 1000 ether;
uint256 discoveryAmount1 = strategy.setDiscoveryPosition(CURRENT_TICK, pulledHarb, params);
uint128 anchorLiquidity = 1000e18;
uint256 discoveryAmount1 = strategy.setDiscoveryPosition(CURRENT_TICK, anchorLiquidity, params);
strategy.clearMintedPositions();
params.discoveryDepth = 0; // Minimum depth
uint256 discoveryAmount2 = strategy.setDiscoveryPosition(CURRENT_TICK, pulledHarb, params);
uint256 discoveryAmount2 = strategy.setDiscoveryPosition(CURRENT_TICK, anchorLiquidity, params);
assertGt(discoveryAmount1, discoveryAmount2, "Higher discovery depth should result in more tokens");
}