Skip to main content

Tutorial: Managing Concentrated Liquidity

Master the art of concentrated liquidity with Saros DLMM. Learn how to create, optimize, and manage profitable liquidity positions while minimizing risks and maximizing capital efficiency.

What You'll Learn​

By completing this tutorial, you'll understand:

  • DLMM Fundamentals: How concentrated liquidity works vs traditional AMMs
  • Position Creation: Setting up profitable liquidity ranges
  • Risk Management: Protecting against impermanent loss and volatility
  • Rebalancing Strategies: When and how to adjust positions
  • Fee Optimization: Maximizing earnings from trading fees
  • Advanced Tactics: Professional liquidity management techniques

Prerequisites​

  • Completed the Quick Start Guide
  • Basic understanding of AMMs and liquidity provision
  • DLMM SDK installed (@saros-finance/dlmm-sdk)
  • Wallet with tokens for liquidity provision
  • Understanding of price ranges and slippage

Part 1: Understanding DLMM Mechanics​

Traditional AMM vs DLMM​

// Traditional AMM (like Uniswap V2)
// Your liquidity is spread across ALL possible prices (0 to āˆž)
// Capital efficiency: ~12%

// DLMM (Dynamic Liquidity Market Maker)
// Your liquidity is concentrated in specific price bins
// Capital efficiency: up to 4000x higher

import { DLMM, DLMMPool } from '@saros-finance/dlmm-sdk';
import { Connection, PublicKey } from '@solana/web3.js';

// Example: SOL-USDC pool analysis
const connection = new Connection('https://api.mainnet-beta.solana.com');
const pool = await DLMM.create(connection, new PublicKey('your-pool-address'));

// Traditional AMM would use ALL price ranges
const traditionalRange = { min: 0, max: Infinity };
console.log('Traditional AMM range:', traditionalRange);

// DLMM uses targeted ranges
const currentPrice = pool.getCurrentPrice();
const optimalRange = {
min: currentPrice * 0.95, // 5% below current
max: currentPrice * 1.05 // 5% above current
};

console.log('DLMM focused range:', optimalRange);
console.log('Capital efficiency gain:',
(traditionalRange.max - traditionalRange.min) / (optimalRange.max - optimalRange.min)
);

Understanding Bins and Price Levels​

// Bins are discrete price levels where liquidity can be placed
// Each bin represents a specific price point

class BinEducation {
static demonstrateBinConcept(pool: DLMMPool) {
const binStep = pool.binStep;
const currentBinId = pool.activeId;
const currentPrice = pool.getCurrentPrice();

console.log('🧮 BIN EDUCATION');
console.log('═'.repeat(40));
console.log(`Current Price: $${currentPrice.toFixed(4)}`);
console.log(`Active Bin ID: ${currentBinId}`);
console.log(`Bin Step: ${binStep} (${(binStep/100).toFixed(2)}%)`);

// Show price levels for surrounding bins
for (let i = -5; i <= 5; i++) {
const binId = currentBinId + i;
const binPrice = currentPrice * Math.pow(1 + binStep/10000, i);
const isActive = i === 0 ? ' ← CURRENT' : '';

console.log(`Bin ${binId}: $${binPrice.toFixed(4)}${isActive}`);
}
}
}

Part 2: Creating Your First Position​

Step 1: Pool Selection and Analysis​

// src/positionSetup.ts
import {
DLMM,
DLMMPool,
getBinArrays,
getPositionsByUser
} from '@saros-finance/dlmm-sdk';
import { Connection, PublicKey, Keypair } from '@solana/web3.js';

export class ConcentratedLiquidityManager {
private connection: Connection;
private wallet: Keypair;

constructor(rpcUrl: string, wallet: Keypair) {
this.connection = new Connection(rpcUrl, 'confirmed');
this.wallet = wallet;
}

async analyzePool(poolAddress: string): Promise<PoolAnalysis> {
console.log('šŸ” Analyzing pool for liquidity opportunity...');

const pool = await DLMM.create(this.connection, new PublicKey(poolAddress));
const currentPrice = pool.getCurrentPrice();
const binStep = pool.binStep;

// Get historical price data (simplified - in reality you'd use price feeds)
const priceHistory = await this.getRecentPriceHistory(pool);
const volatility = this.calculateVolatility(priceHistory);

// Analyze existing liquidity distribution
const binArrays = await getBinArrays(this.connection, pool.poolAddress);
const liquidityMap = this.buildLiquidityMap(binArrays);

return {
poolAddress,
currentPrice,
binStep,
volatility,
liquidityMap,
recommendations: this.generateRangeRecommendations(currentPrice, volatility, liquidityMap)
};
}

private async getRecentPriceHistory(pool: DLMMPool): Promise<number[]> {
// In a real implementation, you'd fetch from price feeds or transaction history
// For this example, we'll simulate realistic price movements
const currentPrice = pool.getCurrentPrice();
const prices: number[] = [];

for (let i = 0; i < 100; i++) {
const randomWalk = (Math.random() - 0.5) * 0.02; // ±1% random movement
const price = i === 0 ? currentPrice : prices[i-1] * (1 + randomWalk);
prices.push(price);
}

return prices;
}

private calculateVolatility(prices: number[]): number {
if (prices.length < 2) return 0;

const returns = [];
for (let i = 1; i < prices.length; i++) {
returns.push(Math.log(prices[i] / prices[i-1]));
}

const meanReturn = returns.reduce((sum, ret) => sum + ret, 0) / returns.length;
const variance = returns.reduce((sum, ret) =>
sum + Math.pow(ret - meanReturn, 2), 0) / returns.length;

return Math.sqrt(variance * 365); // Annualized volatility
}

private buildLiquidityMap(binArrays: any[]): Map<number, number> {
const liquidityMap = new Map<number, number>();

for (const binArray of binArrays) {
for (const bin of binArray.bins) {
if (bin.liquidityGross.gt(0)) {
liquidityMap.set(bin.binId, bin.liquidityGross.toNumber());
}
}
}

return liquidityMap;
}

private generateRangeRecommendations(
currentPrice: number,
volatility: number,
liquidityMap: Map<number, number>
): RangeRecommendation[] {
const recommendations: RangeRecommendation[] = [];

// Conservative strategy: Wide range, lower fees, more stable
recommendations.push({
strategy: 'Conservative',
description: 'Wide range for stability and lower management overhead',
range: {
lower: currentPrice * (1 - volatility * 2),
upper: currentPrice * (1 + volatility * 2)
},
expectedAPY: 0.15,
riskLevel: 'LOW',
managementFrequency: 'Weekly'
});

// Aggressive strategy: Tight range, higher fees, needs active management
recommendations.push({
strategy: 'Aggressive',
description: 'Tight range for maximum fee capture, requires active monitoring',
range: {
lower: currentPrice * (1 - volatility * 0.5),
upper: currentPrice * (1 + volatility * 0.5)
},
expectedAPY: 0.45,
riskLevel: 'HIGH',
managementFrequency: 'Daily'
});

// Balanced strategy: Medium range with good risk/reward
recommendations.push({
strategy: 'Balanced',
description: 'Optimized balance between fees and risk',
range: {
lower: currentPrice * (1 - volatility),
upper: currentPrice * (1 + volatility)
},
expectedAPY: 0.28,
riskLevel: 'MEDIUM',
managementFrequency: 'Bi-weekly'
});

return recommendations;
}
}

interface PoolAnalysis {
poolAddress: string;
currentPrice: number;
binStep: number;
volatility: number;
liquidityMap: Map<number, number>;
recommendations: RangeRecommendation[];
}

interface RangeRecommendation {
strategy: string;
description: string;
range: {
lower: number;
upper: number;
};
expectedAPY: number;
riskLevel: 'LOW' | 'MEDIUM' | 'HIGH';
managementFrequency: string;
}

Step 2: Position Creation with Optimal Parameters​

// src/positionCreator.ts
import {
createPosition,
addLiquidity,
PositionV2,
LiquidityParameterByWeight
} from '@saros-finance/dlmm-sdk';

export class PositionCreator {
private manager: ConcentratedLiquidityManager;

constructor(manager: ConcentratedLiquidityManager) {
this.manager = manager;
}

async createOptimizedPosition(config: PositionConfig): Promise<CreatedPosition> {
console.log('šŸš€ Creating optimized DLMM position...');
console.log(`Strategy: ${config.strategy}`);
console.log(`Range: $${config.priceRange.lower.toFixed(4)} - $${config.priceRange.upper.toFixed(4)}`);

try {
// Step 1: Calculate bin IDs for the price range
const lowerBinId = this.priceToBinId(config.priceRange.lower, config.pool);
const upperBinId = this.priceToBinId(config.priceRange.upper, config.pool);

console.log(`šŸ“Š Bin range: ${lowerBinId} to ${upperBinId}`);

// Step 2: Optimize liquidity distribution
const liquidityDistribution = this.calculateOptimalDistribution(
lowerBinId,
upperBinId,
config.strategy,
config.pool
);

console.log('šŸ’§ Liquidity distribution calculated');

// Step 3: Create position with optimized parameters
const positionParams = {
user: this.manager['wallet'].publicKey,
poolAddress: config.pool.poolAddress,
lowerBinId,
upperBinId,
amountX: config.amountX,
amountY: config.amountY,
liquidityDistribution
};

const transaction = await createPosition(
this.manager['connection'],
positionParams,
this.manager['wallet'].publicKey
);

// Step 4: Sign and send transaction
transaction.feePayer = this.manager['wallet'].publicKey;
transaction.recentBlockhash = (
await this.manager['connection'].getLatestBlockhash()
).blockhash;
transaction.sign(this.manager['wallet']);

const signature = await this.manager['connection'].sendTransaction(transaction);
await this.manager['connection'].confirmTransaction(signature, 'confirmed');

console.log('āœ… Position created successfully!');
console.log('šŸ“ Transaction:', signature);

// Step 5: Extract position address and return details
const positionAddress = await this.extractPositionAddress(signature);

const createdPosition: CreatedPosition = {
address: positionAddress,
signature,
lowerBinId,
upperBinId,
priceRange: config.priceRange,
strategy: config.strategy,
createdAt: new Date(),
initialAmountX: config.amountX,
initialAmountY: config.amountY
};

return createdPosition;

} catch (error) {
console.error('āŒ Position creation failed:', error);
throw error;
}
}

private priceToBinId(price: number, pool: DLMMPool): number {
// Convert price to bin ID using pool's bin step
const currentPrice = pool.getCurrentPrice();
const currentBinId = pool.activeId;
const binStep = pool.binStep;

const priceRatio = price / currentPrice;
const binOffset = Math.log(priceRatio) / Math.log(1 + binStep / 10000);

return Math.round(currentBinId + binOffset);
}

private calculateOptimalDistribution(
lowerBinId: number,
upperBinId: number,
strategy: string,
pool: DLMMPool
): LiquidityParameterByWeight[] {
const distribution: LiquidityParameterByWeight[] = [];
const totalBins = upperBinId - lowerBinId + 1;
const currentBinId = pool.activeId;

for (let binId = lowerBinId; binId <= upperBinId; binId++) {
let weight = 0;

switch (strategy) {
case 'Conservative':
// Uniform distribution across all bins
weight = 1 / totalBins;
break;

case 'Aggressive':
// Concentrated around current price
const distance = Math.abs(binId - currentBinId);
weight = Math.exp(-distance / 3) / totalBins;
break;

case 'Balanced':
// Normal distribution centered on current price
const variance = totalBins / 4;
const normalizedDistance = Math.pow(binId - currentBinId, 2) / (2 * variance);
weight = Math.exp(-normalizedDistance) / totalBins;
break;

default:
weight = 1 / totalBins;
}

distribution.push({
binId,
weight: Math.max(weight, 0.001) // Minimum weight
});
}

// Normalize weights to sum to 1
const totalWeight = distribution.reduce((sum, item) => sum + item.weight, 0);
return distribution.map(item => ({
...item,
weight: item.weight / totalWeight
}));
}

private async extractPositionAddress(signature: string): Promise<string> {
// Extract position address from transaction logs
// This is a simplified implementation
// In practice, you'd parse transaction logs to find the created position
return 'extracted-position-address';
}
}

interface PositionConfig {
pool: DLMMPool;
strategy: 'Conservative' | 'Aggressive' | 'Balanced';
priceRange: {
lower: number;
upper: number;
};
amountX: number;
amountY: number;
}

interface CreatedPosition {
address: string;
signature: string;
lowerBinId: number;
upperBinId: number;
priceRange: {
lower: number;
upper: number;
};
strategy: string;
createdAt: Date;
initialAmountX: number;
initialAmountY: number;
}

Part 2: Position Monitoring and Performance Tracking​

Real-time Position Monitoring​

// src/positionMonitor.ts
import {
getPositionsByUser,
getPositionInfo,
collectFees
} from '@saros-finance/dlmm-sdk';

export class PositionMonitor {
private manager: ConcentratedLiquidityManager;
private positions: Map<string, PositionTracker> = new Map();
private isMonitoring: boolean = false;

constructor(manager: ConcentratedLiquidityManager) {
this.manager = manager;
}

async startMonitoring(): Promise<void> {
if (this.isMonitoring) {
console.log('šŸ“Š Already monitoring positions');
return;
}

console.log('šŸ‘ļø Starting position monitoring...');
this.isMonitoring = true;

// Load existing positions
await this.loadUserPositions();

// Start monitoring loop
const monitoringLoop = setInterval(async () => {
try {
await this.updateAllPositions();
await this.checkRebalanceNeeds();
await this.autoCollectFees();
} catch (error) {
console.error('āŒ Monitoring error:', error);
}
}, 30000); // Update every 30 seconds

console.log('āœ… Monitoring started (30s intervals)');

// Graceful shutdown
process.on('SIGINT', () => {
console.log('\nšŸ›‘ Stopping position monitoring...');
clearInterval(monitoringLoop);
this.isMonitoring = false;
process.exit(0);
});
}

private async loadUserPositions(): Promise<void> {
try {
const userPositions = await getPositionsByUser(
this.manager['connection'],
this.manager['wallet'].publicKey
);

console.log(`šŸ“‹ Found ${userPositions.length} existing positions`);

for (const position of userPositions) {
const tracker: PositionTracker = {
address: position.publicKey.toString(),
poolAddress: position.poolBinArrays[0]?.toString() || '',
lowerBinId: position.lowerBinId,
upperBinId: position.upperBinId,
strategy: 'Unknown', // Would be stored separately
createdAt: new Date(), // Would be fetched from transaction history
lastUpdate: new Date(),
metrics: {
currentValue: 0,
feesEarned: 0,
impermanentLoss: 0,
timeInRange: 1.0,
dailyYield: 0
},
alerts: []
};

this.positions.set(tracker.address, tracker);
}

} catch (error) {
console.error('āŒ Failed to load positions:', error);
}
}

private async updateAllPositions(): Promise<void> {
const updatePromises = Array.from(this.positions.values()).map(async (tracker) => {
try {
await this.updatePosition(tracker);
} catch (error) {
console.error(`āŒ Failed to update position ${tracker.address}:`, error);
}
});

await Promise.all(updatePromises);
}

private async updatePosition(tracker: PositionTracker): Promise<void> {
const positionInfo = await getPositionInfo(
this.manager['connection'],
new PublicKey(tracker.address)
);

if (!positionInfo) {
console.warn(`āš ļø Position ${tracker.address} not found`);
return;
}

// Update metrics
const currentValue = this.calculatePositionValue(positionInfo);
const feesEarned = this.calculateFeesEarned(positionInfo);
const impermanentLoss = this.calculateImpermanentLoss(tracker, positionInfo);
const timeInRange = this.calculateTimeInRange(tracker, positionInfo);

tracker.metrics = {
currentValue,
feesEarned,
impermanentLoss,
timeInRange,
dailyYield: this.calculateDailyYield(tracker, currentValue)
};

tracker.lastUpdate = new Date();

// Check for alerts
await this.checkPositionAlerts(tracker);
}

private calculatePositionValue(positionInfo: any): number {
// Calculate current USD value of the position
// This is a simplified calculation
return positionInfo.tokenX.amount + positionInfo.tokenY.amount;
}

private calculateFeesEarned(positionInfo: any): number {
return positionInfo.feeX.amount + positionInfo.feeY.amount;
}

private calculateImpermanentLoss(tracker: PositionTracker, positionInfo: any): number {
// Calculate impermanent loss vs holding
const initialValue = tracker.metrics.currentValue;
const currentValue = this.calculatePositionValue(positionInfo);

// Simplified IL calculation
return Math.max(0, initialValue - currentValue);
}

private calculateTimeInRange(tracker: PositionTracker, positionInfo: any): number {
// Calculate what percentage of time the position has been in active range
// This would require historical price data
return 0.85; // Example: 85% time in range
}

private calculateDailyYield(tracker: PositionTracker, currentValue: number): number {
const daysSinceCreated = (Date.now() - tracker.createdAt.getTime()) / (1000 * 60 * 60 * 24);
if (daysSinceCreated < 1) return 0;

const totalReturn = (currentValue - (tracker.metrics.currentValue || 0)) / currentValue;
return totalReturn / daysSinceCreated;
}

private async checkPositionAlerts(tracker: PositionTracker): Promise<void> {
tracker.alerts = [];

// Alert 1: Position out of range
if (tracker.metrics.timeInRange < 0.1) {
tracker.alerts.push({
type: 'OUT_OF_RANGE',
severity: 'HIGH',
message: 'Position is mostly out of range - consider rebalancing',
action: 'REBALANCE'
});
}

// Alert 2: High impermanent loss
if (tracker.metrics.impermanentLoss > tracker.metrics.currentValue * 0.05) {
tracker.alerts.push({
type: 'HIGH_IL',
severity: 'MEDIUM',
message: 'Impermanent loss exceeds 5% - monitor closely',
action: 'MONITOR'
});
}

// Alert 3: Low fee generation
if (tracker.metrics.dailyYield < 0.0001) {
tracker.alerts.push({
type: 'LOW_FEES',
severity: 'LOW',
message: 'Low fee generation - consider tighter range',
action: 'OPTIMIZE_RANGE'
});
}

// Alert 4: High uncollected fees
if (tracker.metrics.feesEarned > tracker.metrics.currentValue * 0.02) {
tracker.alerts.push({
type: 'UNCOLLECTED_FEES',
severity: 'MEDIUM',
message: 'Significant uncollected fees - consider harvesting',
action: 'COLLECT_FEES'
});
}

// Display alerts
if (tracker.alerts.length > 0) {
console.log(`🚨 Position ${tracker.address.slice(0, 8)}... has ${tracker.alerts.length} alerts:`);
tracker.alerts.forEach(alert => {
console.log(` ${this.getAlertEmoji(alert.severity)} ${alert.message}`);
});
}
}

private getAlertEmoji(severity: string): string {
switch (severity) {
case 'HIGH': return 'šŸ”“';
case 'MEDIUM': return '🟔';
case 'LOW': return '🟢';
default: return 'ā„¹ļø';
}
}

private async checkRebalanceNeeds(): Promise<void> {
for (const [address, tracker] of this.positions) {
if (tracker.alerts.some(alert => alert.action === 'REBALANCE')) {
console.log(`šŸ”„ Position ${address.slice(0, 8)}... needs rebalancing`);
await this.suggestRebalanceStrategy(tracker);
}
}
}

private async suggestRebalanceStrategy(tracker: PositionTracker): Promise<void> {
console.log(`šŸ’” Rebalance suggestions for ${tracker.address.slice(0, 8)}...:`);

// Analyze current market conditions
const pool = await DLMM.create(
this.manager['connection'],
new PublicKey(tracker.poolAddress)
);

const currentPrice = pool.getCurrentPrice();
const positionPrice = (tracker.lowerBinId + tracker.upperBinId) / 2; // Simplified
const priceDeviation = Math.abs(currentPrice - positionPrice) / positionPrice;

if (priceDeviation > 0.1) { // 10% deviation
console.log('šŸŽÆ Suggestion: Close and recreate position around current price');
console.log(` Current price: $${currentPrice.toFixed(4)}`);
console.log(` Position center: $${positionPrice.toFixed(4)}`);
console.log(` Deviation: ${(priceDeviation * 100).toFixed(1)}%`);
} else {
console.log('āœ… Position range is still optimal');
}
}

private async autoCollectFees(): Promise<void> {
for (const [address, tracker] of this.positions) {
if (tracker.alerts.some(alert => alert.action === 'COLLECT_FEES')) {
try {
console.log(`šŸ’° Auto-collecting fees for ${address.slice(0, 8)}...`);

const collectTransaction = await collectFees(
this.manager['connection'],
new PublicKey(address),
this.manager['wallet'].publicKey
);

const signature = await this.manager['connection'].sendTransaction(collectTransaction);
await this.manager['connection'].confirmTransaction(signature, 'confirmed');

console.log(`āœ… Fees collected: ${signature}`);

// Update tracker
tracker.metrics.feesEarned = 0; // Reset after collection

} catch (error) {
console.error(`āŒ Fee collection failed for ${address}:`, error);
}
}
}
}

async generatePerformanceReport(): Promise<PerformanceReport> {
const positions = Array.from(this.positions.values());

const totalValue = positions.reduce((sum, pos) => sum + pos.metrics.currentValue, 0);
const totalFees = positions.reduce((sum, pos) => sum + pos.metrics.feesEarned, 0);
const avgTimeInRange = positions.reduce((sum, pos) => sum + pos.metrics.timeInRange, 0) / positions.length;
const totalIL = positions.reduce((sum, pos) => sum + pos.metrics.impermanentLoss, 0);

return {
totalPositions: positions.length,
totalValue,
totalFeesEarned: totalFees,
averageTimeInRange: avgTimeInRange,
totalImpermanentLoss: totalIL,
netPerformance: totalFees - totalIL,
averageDailyYield: positions.reduce((sum, pos) => sum + pos.metrics.dailyYield, 0) / positions.length,
bestPerformingStrategy: this.findBestStrategy(positions),
recommendations: this.generatePortfolioRecommendations(positions)
};
}

private findBestStrategy(positions: PositionTracker[]): string {
const strategyPerformance = new Map<string, number[]>();

for (const position of positions) {
if (!strategyPerformance.has(position.strategy)) {
strategyPerformance.set(position.strategy, []);
}
strategyPerformance.get(position.strategy)!.push(position.metrics.dailyYield);
}

let bestStrategy = 'Unknown';
let bestPerformance = -Infinity;

for (const [strategy, yields] of strategyPerformance) {
const avgYield = yields.reduce((sum, y) => sum + y, 0) / yields.length;
if (avgYield > bestPerformance) {
bestPerformance = avgYield;
bestStrategy = strategy;
}
}

return bestStrategy;
}

private generatePortfolioRecommendations(positions: PositionTracker[]): string[] {
const recommendations: string[] = [];

const outOfRangePositions = positions.filter(p => p.metrics.timeInRange < 0.2);
if (outOfRangePositions.length > 0) {
recommendations.push(`Rebalance ${outOfRangePositions.length} out-of-range positions`);
}

const lowYieldPositions = positions.filter(p => p.metrics.dailyYield < 0.0001);
if (lowYieldPositions.length > 0) {
recommendations.push(`Optimize ranges for ${lowYieldPositions.length} low-yield positions`);
}

const highILPositions = positions.filter(p =>
p.metrics.impermanentLoss > p.metrics.currentValue * 0.05
);
if (highILPositions.length > 0) {
recommendations.push(`Review ${highILPositions.length} positions with high impermanent loss`);
}

return recommendations;
}
}

interface PositionTracker {
address: string;
poolAddress: string;
lowerBinId: number;
upperBinId: number;
strategy: string;
createdAt: Date;
lastUpdate: Date;
metrics: PositionMetrics;
alerts: PositionAlert[];
}

interface PositionMetrics {
currentValue: number;
feesEarned: number;
impermanentLoss: number;
timeInRange: number;
dailyYield: number;
}

interface PositionAlert {
type: 'OUT_OF_RANGE' | 'HIGH_IL' | 'LOW_FEES' | 'UNCOLLECTED_FEES';
severity: 'HIGH' | 'MEDIUM' | 'LOW';
message: string;
action: 'REBALANCE' | 'MONITOR' | 'OPTIMIZE_RANGE' | 'COLLECT_FEES';
}

interface PerformanceReport {
totalPositions: number;
totalValue: number;
totalFeesEarned: number;
averageTimeInRange: number;
totalImpermanentLoss: number;
netPerformance: number;
averageDailyYield: number;
bestPerformingStrategy: string;
recommendations: string[];
}

Part 3: Advanced Rebalancing Strategies​

Automated Rebalancing System​

// src/rebalancer.ts
import {
removeLiquidity,
createPosition,
addLiquidity
} from '@saros-finance/dlmm-sdk';

export class AutoRebalancer {
private monitor: PositionMonitor;
private creator: PositionCreator;
private rebalanceSettings: RebalanceSettings;

constructor(
monitor: PositionMonitor,
creator: PositionCreator,
settings: RebalanceSettings
) {
this.monitor = monitor;
this.creator = creator;
this.rebalanceSettings = settings;
}

async executeRebalance(positionAddress: string): Promise<RebalanceResult> {
console.log(`šŸ”„ Executing rebalance for ${positionAddress.slice(0, 8)}...`);

try {
// Step 1: Get current position info
const currentPosition = await getPositionInfo(
this.monitor['manager']['connection'],
new PublicKey(positionAddress)
);

if (!currentPosition) {
throw new Error('Position not found');
}

// Step 2: Analyze optimal new range
const pool = await DLMM.create(
this.monitor['manager']['connection'],
currentPosition.poolAddress
);

const currentPrice = pool.getCurrentPrice();
const optimalRange = this.calculateOptimalRange(currentPrice, pool);

console.log(`šŸ“Š New range: $${optimalRange.lower.toFixed(4)} - $${optimalRange.upper.toFixed(4)}`);

// Step 3: Remove liquidity from old position
const removeTransaction = await removeLiquidity(
this.monitor['manager']['connection'],
new PublicKey(positionAddress),
this.monitor['manager']['wallet'].publicKey,
100 // Remove 100% of liquidity
);

// Sign and send remove transaction
const removeSignature = await this.sendTransaction(removeTransaction);
console.log('āœ… Old position closed:', removeSignature);

// Step 4: Create new position with optimal range
const newPositionConfig: PositionConfig = {
pool,
strategy: this.rebalanceSettings.preferredStrategy,
priceRange: optimalRange,
amountX: currentPosition.tokenX.amount,
amountY: currentPosition.tokenY.amount
};

const newPosition = await this.creator.createOptimizedPosition(newPositionConfig);
console.log('āœ… New position created:', newPosition.address);

// Step 5: Calculate rebalance cost and benefit
const rebalanceCost = this.calculateRebalanceCost(removeSignature, newPosition.signature);
const expectedBenefit = this.estimateRebalanceBenefit(optimalRange, currentPrice);

return {
success: true,
oldPositionAddress: positionAddress,
newPositionAddress: newPosition.address,
rebalanceCost,
expectedBenefit,
netBenefit: expectedBenefit - rebalanceCost,
timestamp: new Date()
};

} catch (error) {
console.error('āŒ Rebalance failed:', error);
return {
success: false,
oldPositionAddress: positionAddress,
newPositionAddress: '',
rebalanceCost: 0,
expectedBenefit: 0,
netBenefit: 0,
timestamp: new Date(),
error: error.message
};
}
}

private calculateOptimalRange(currentPrice: number, pool: DLMMPool): { lower: number; upper: number } {
const binStep = pool.binStep / 10000; // Convert to decimal

switch (this.rebalanceSettings.strategy) {
case 'TIGHT':
return {
lower: currentPrice * (1 - binStep * 5),
upper: currentPrice * (1 + binStep * 5)
};

case 'MEDIUM':
return {
lower: currentPrice * (1 - binStep * 15),
upper: currentPrice * (1 + binStep * 15)
};

case 'WIDE':
return {
lower: currentPrice * (1 - binStep * 30),
upper: currentPrice * (1 + binStep * 30)
};

default:
return {
lower: currentPrice * 0.9,
upper: currentPrice * 1.1
};
}
}

private async sendTransaction(transaction: any): Promise<string> {
transaction.feePayer = this.monitor['manager']['wallet'].publicKey;
transaction.recentBlockhash = (
await this.monitor['manager']['connection'].getLatestBlockhash()
).blockhash;
transaction.sign(this.monitor['manager']['wallet']);

const signature = await this.monitor['manager']['connection'].sendTransaction(transaction);
await this.monitor['manager']['connection'].confirmTransaction(signature, 'confirmed');

return signature;
}

private calculateRebalanceCost(removeSignature: string, createSignature: string): number {
// Estimate transaction costs (simplified)
const avgTransactionFee = 0.0001; // ~$0.01 per transaction
return avgTransactionFee * 2; // Two transactions
}

private estimateRebalanceBenefit(range: {lower: number; upper: number}, currentPrice: number): number {
// Estimate increased fee generation from better range (simplified)
const rangeSize = range.upper - range.lower;
const efficiency = currentPrice / rangeSize; // Higher price/range ratio = more efficient

return efficiency * 0.0001; // Estimated daily benefit
}
}

interface RebalanceSettings {
strategy: 'TIGHT' | 'MEDIUM' | 'WIDE';
preferredStrategy: 'Conservative' | 'Aggressive' | 'Balanced';
autoCollectFees: boolean;
rebalanceThreshold: number; // Price deviation percentage
maxRebalanceFrequency: number; // Max rebalances per day
}

interface RebalanceResult {
success: boolean;
oldPositionAddress: string;
newPositionAddress: string;
rebalanceCost: number;
expectedBenefit: number;
netBenefit: number;
timestamp: Date;
error?: string;
}

Part 4: Risk Management and Safety​

Comprehensive Risk Management​

// src/riskManager.ts
export class RiskManager {
private maxPositionValue: number;
private maxImpermanentLoss: number;
private emergencySettings: EmergencySettings;

constructor(settings: RiskSettings) {
this.maxPositionValue = settings.maxPositionValue;
this.maxImpermanentLoss = settings.maxImpermanentLoss;
this.emergencySettings = settings.emergency;
}

async assessPositionRisk(position: PositionTracker): Promise<RiskAssessment> {
console.log(`šŸ›”ļø Assessing risk for position ${position.address.slice(0, 8)}...`);

const risks: RiskFactor[] = [];

// Risk Factor 1: Position size relative to portfolio
if (position.metrics.currentValue > this.maxPositionValue) {
risks.push({
type: 'POSITION_SIZE',
severity: 'HIGH',
description: 'Position size exceeds maximum allowed',
impact: 'Potential for large losses',
mitigation: 'Reduce position size or split into multiple positions'
});
}

// Risk Factor 2: Impermanent loss threshold
const ilPercentage = position.metrics.impermanentLoss / position.metrics.currentValue;
if (ilPercentage > this.maxImpermanentLoss) {
risks.push({
type: 'IMPERMANENT_LOSS',
severity: ilPercentage > this.maxImpermanentLoss * 2 ? 'CRITICAL' : 'HIGH',
description: `Impermanent loss at ${(ilPercentage * 100).toFixed(1)}%`,
impact: 'Erosion of principal value',
mitigation: 'Consider closing position or hedging'
});
}

// Risk Factor 3: Time out of range
if (position.metrics.timeInRange < 0.2) {
risks.push({
type: 'OUT_OF_RANGE',
severity: 'MEDIUM',
description: 'Position out of active trading range',
impact: 'No fee generation, potential IL',
mitigation: 'Rebalance position to current price range'
});
}

// Risk Factor 4: Pool liquidity risk
const poolRisk = await this.assessPoolRisk(position.poolAddress);
if (poolRisk.severity !== 'LOW') {
risks.push(poolRisk);
}

// Calculate overall risk score
const riskScore = this.calculateRiskScore(risks);

return {
positionAddress: position.address,
overallRisk: this.getRiskLevel(riskScore),
riskScore,
riskFactors: risks,
recommendedActions: this.generateRiskMitigation(risks),
emergencyActions: riskScore > 0.8 ? this.getEmergencyActions() : []
};
}

private async assessPoolRisk(poolAddress: string): Promise<RiskFactor> {
// Assess pool-specific risks (TVL, volume, volatility)
try {
const pool = await DLMM.create(
this.monitor['manager']['connection'],
new PublicKey(poolAddress)
);

// Check pool health indicators
const binArrays = await getBinArrays(this.monitor['manager']['connection'], pool.poolAddress);
const totalLiquidity = binArrays.reduce((sum, array) =>
sum + array.bins.reduce((binSum, bin) => binSum + bin.liquidityGross.toNumber(), 0), 0
);

if (totalLiquidity < 10000) { // Less than $10k TVL
return {
type: 'POOL_LIQUIDITY',
severity: 'HIGH',
description: 'Low pool liquidity may cause high slippage',
impact: 'Difficulty exiting position, high price impact',
mitigation: 'Monitor closely or consider larger pools'
};
}

return {
type: 'POOL_LIQUIDITY',
severity: 'LOW',
description: 'Pool liquidity appears healthy',
impact: 'Minimal',
mitigation: 'Continue monitoring'
};

} catch (error) {
return {
type: 'POOL_ACCESS',
severity: 'CRITICAL',
description: 'Cannot access pool data',
impact: 'Unable to assess position properly',
mitigation: 'Check network connection and pool status'
};
}
}

private calculateRiskScore(risks: RiskFactor[]): number {
const severityWeights = {
'LOW': 0.1,
'MEDIUM': 0.3,
'HIGH': 0.7,
'CRITICAL': 1.0
};

const totalRisk = risks.reduce((sum, risk) =>
sum + severityWeights[risk.severity], 0
);

return Math.min(1.0, totalRisk / risks.length);
}

private getRiskLevel(score: number): 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL' {
if (score < 0.2) return 'LOW';
if (score < 0.5) return 'MEDIUM';
if (score < 0.8) return 'HIGH';
return 'CRITICAL';
}

private generateRiskMitigation(risks: RiskFactor[]): string[] {
return risks.map(risk => risk.mitigation);
}

private getEmergencyActions(): string[] {
return [
'Immediately close high-risk positions',
'Collect all available fees',
'Monitor market conditions closely',
'Consider hedging with derivatives',
'Reduce overall exposure'
];
}
}

interface RiskSettings {
maxPositionValue: number;
maxImpermanentLoss: number;
emergency: EmergencySettings;
}

interface EmergencySettings {
autoCloseEnabled: boolean;
maxLossThreshold: number;
emergencyContacts: string[];
}

interface RiskAssessment {
positionAddress: string;
overallRisk: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
riskScore: number;
riskFactors: RiskFactor[];
recommendedActions: string[];
emergencyActions: string[];
}

interface RiskFactor {
type: string;
severity: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
description: string;
impact: string;
mitigation: string;
}

Part 5: Complete Implementation Example​

Professional DLMM Management System​

// src/dlmmManager.ts
export class ProfessionalDLMMManager {
private liquidityManager: ConcentratedLiquidityManager;
private positionCreator: PositionCreator;
private monitor: PositionMonitor;
private rebalancer: AutoRebalancer;
private riskManager: RiskManager;

constructor(rpcUrl: string, wallet: Keypair) {
this.liquidityManager = new ConcentratedLiquidityManager(rpcUrl, wallet);
this.positionCreator = new PositionCreator(this.liquidityManager);
this.monitor = new PositionMonitor(this.liquidityManager);

const rebalanceSettings: RebalanceSettings = {
strategy: 'MEDIUM',
preferredStrategy: 'Balanced',
autoCollectFees: true,
rebalanceThreshold: 0.1, // Rebalance when price moves 10%
maxRebalanceFrequency: 2 // Max 2 rebalances per day
};

this.rebalancer = new AutoRebalancer(this.monitor, this.positionCreator, rebalanceSettings);

const riskSettings: RiskSettings = {
maxPositionValue: 10000, // $10k max per position
maxImpermanentLoss: 0.1, // 10% max IL
emergency: {
autoCloseEnabled: true,
maxLossThreshold: 0.2, // 20% loss triggers emergency close
emergencyContacts: ['admin@example.com']
}
};

this.riskManager = new RiskManager(riskSettings);
}

async runFullStrategy(poolAddress: string): Promise<void> {
console.log('šŸŽÆ Running Professional DLMM Management Strategy');
console.log('═'.repeat(50));

try {
// Step 1: Analyze pool and market conditions
const analysis = await this.liquidityManager.analyzePool(poolAddress);
console.log(`šŸ“Š Pool Analysis Complete`);
console.log(` Current Price: $${analysis.currentPrice.toFixed(4)}`);
console.log(` Volatility: ${(analysis.volatility * 100).toFixed(1)}%`);
console.log(` Recommendations: ${analysis.recommendations.length}`);

// Step 2: Select optimal strategy
const selectedStrategy = analysis.recommendations[0];
console.log(`\nšŸŽÆ Selected Strategy: ${selectedStrategy.strategy}`);
console.log(` Expected APY: ${(selectedStrategy.expectedAPY * 100).toFixed(1)}%`);
console.log(` Risk Level: ${selectedStrategy.riskLevel}`);

// Step 3: Create initial position
const pool = await DLMM.create(
this.liquidityManager['connection'],
new PublicKey(poolAddress)
);

const positionConfig: PositionConfig = {
pool,
strategy: selectedStrategy.strategy as any,
priceRange: selectedStrategy.range,
amountX: 1000, // 1000 tokens (adjust based on your capital)
amountY: 1000 // 1000 tokens
};

const position = await this.positionCreator.createOptimizedPosition(positionConfig);
console.log(`\nāœ… Position Created: ${position.address}`);

// Step 4: Start monitoring and management
await this.monitor.startMonitoring();

// Step 5: Set up periodic reporting
this.setupPeriodicReporting();

console.log('\nšŸ”„ Automated management system is now active!');
console.log(' - Real-time monitoring: Every 30 seconds');
console.log(' - Performance reports: Every 6 hours');
console.log(' - Auto fee collection: When threshold reached');
console.log(' - Risk assessment: Continuous');
console.log(' - Rebalancing: When needed (max 2/day)');

} catch (error) {
console.error('āŒ Strategy execution failed:', error);
throw error;
}
}

private setupPeriodicReporting(): void {
// Generate performance report every 6 hours
setInterval(async () => {
try {
const report = await this.monitor.generatePerformanceReport();
this.printPerformanceReport(report);
} catch (error) {
console.error('āŒ Report generation failed:', error);
}
}, 6 * 60 * 60 * 1000); // 6 hours
}

private printPerformanceReport(report: PerformanceReport): void {
console.log('\nšŸ“Š PERFORMANCE REPORT');
console.log('═'.repeat(40));
console.log(`šŸ“ˆ Total Positions: ${report.totalPositions}`);
console.log(`šŸ’° Total Value: $${report.totalValue.toFixed(2)}`);
console.log(`šŸŽ‰ Fees Earned: $${report.totalFeesEarned.toFixed(2)}`);
console.log(`⚔ Avg Time in Range: ${(report.averageTimeInRange * 100).toFixed(1)}%`);
console.log(`šŸ“‰ Impermanent Loss: $${report.totalImpermanentLoss.toFixed(2)}`);
console.log(`šŸ’Ž Net Performance: $${report.netPerformance.toFixed(2)}`);
console.log(`šŸ“… Daily Yield: ${(report.averageDailyYield * 100).toFixed(3)}%`);
console.log(`šŸ† Best Strategy: ${report.bestPerformingStrategy}`);

if (report.recommendations.length > 0) {
console.log('\nšŸ’” RECOMMENDATIONS:');
report.recommendations.forEach((rec, i) => {
console.log(` ${i + 1}. ${rec}`);
});
}
console.log('═'.repeat(40));
}
}

Usage Example: Complete Workflow​

// examples/professionalWorkflow.ts
import { ProfessionalDLMMManager } from '../src/dlmmManager';
import { Keypair, clusterApiUrl } from '@solana/web3.js';

async function runProfessionalWorkflow() {
console.log('šŸŽ“ Professional DLMM Management Workflow');

try {
// Load wallet (in production, use secure key management)
const wallet = Keypair.generate(); // Replace with your actual wallet
console.log('šŸ’¼ Wallet loaded:', wallet.publicKey.toString());

// Initialize professional manager
const manager = new ProfessionalDLMMManager(
clusterApiUrl('mainnet-beta'),
wallet
);

// Run strategy on SOL-USDC pool
const solUsdcPool = 'your-sol-usdc-pool-address';
await manager.runFullStrategy(solUsdcPool);

// Keep running (in production, this would be a long-running service)
console.log('\nā³ Professional DLMM management is now active...');
console.log('Press Ctrl+C to stop');

// Handle graceful shutdown
process.on('SIGINT', () => {
console.log('\nšŸ›‘ Shutting down professional manager...');
// Here you'd clean up resources, close positions if needed, etc.
process.exit(0);
});

} catch (error) {
console.error('āŒ Professional workflow failed:', error);
process.exit(1);
}
}

// Run the workflow
runProfessionalWorkflow().catch(console.error);

Production Best Practices​

Security Considerations​

  1. Private Key Management: Never hardcode private keys
  2. Environment Variables: Use .env files for sensitive data
  3. Access Controls: Implement proper authentication
  4. Audit Logging: Track all position changes and transactions
  5. Emergency Stops: Implement circuit breakers for extreme conditions

Performance Optimization​

  1. Connection Pooling: Reuse RPC connections efficiently
  2. Batch Operations: Combine multiple operations when possible
  3. Cache Management: Cache pool and position data appropriately
  4. Rate Limiting: Respect RPC endpoint limits
  5. Error Recovery: Implement robust retry mechanisms

Monitoring and Alerts​

// src/alerting.ts
export class AlertingSystem {
async sendAlert(alert: Alert): Promise<void> {
console.log(`🚨 ${alert.severity} ALERT: ${alert.message}`);

// In production, integrate with:
// - Discord webhooks
// - Slack notifications
// - Email alerts
// - SMS for critical alerts
// - PagerDuty for emergencies
}
}

interface Alert {
type: string;
severity: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
message: string;
positionAddress?: string;
timestamp: Date;
}

Key Takeaways​

āœ… Do's​

  • Start Conservative: Begin with wide ranges to understand DLMM behavior
  • Monitor Actively: Check positions at least daily during volatile periods
  • Collect Fees Regularly: Don't let uncollected fees accumulate
  • Risk Management: Set clear stop-loss and position size limits
  • Diversify Strategies: Use different approaches across multiple positions

āŒ Don'ts​

  • Don't Set and Forget: DLMM positions require active management
  • Don't Ignore Volatility: Adjust ranges based on market conditions
  • Don't Overconcentrate: Spread liquidity across reasonable ranges
  • Don't Skip Testing: Always test strategies on devnet first
  • Don't Neglect Gas Costs: Factor transaction fees into profitability

What's Next?​

šŸŽ‰ Congratulations! You're now a DLMM expert!

Advanced Topics:​

  1. Optimizing DLMM Strategies - Professional techniques
  2. Building Analytics Dashboard - Track performance
  3. Implementing MEV Protection - Protect against front-running
  4. Multi-Pool Arbitrage - Cross-pool opportunities

Resources:​

Ready to optimize your DLMM strategies? Let's dive deeper into advanced techniques! šŸš€