Advanced Derivatives: Options, Structured Products, and Exotics

Build advanced derivatives beyond perpetuals. Implement options, structured products, and exotic derivatives with automated pricing and settlement.

Kevan Shah · · 19 min read

While perpetual swaps dominate DeFi derivatives volume, the next frontier lies in options, structured products, and exotic derivatives. These instruments offer sophisticated risk management tools, yield enhancement strategies, and institutional-grade products that attract professional traders and hedgers.

This comprehensive guide explores the architecture and implementation of advanced derivatives beyond perpetuals.

Options Infrastructure

Core Options Mechanics

contract OptionsDEX {
    enum OptionType { CALL, PUT }
    enum ExerciseType { AMERICAN, EUROPEAN, BERMUDAN }
    
    struct Option {
        address underlying;
        uint256 strike;
        uint256 expiry;
        OptionType optionType;
        ExerciseType exerciseType;
        uint256 size;
        address writer;
        address holder;
        uint256 premium;
        bool exercised;
        bool expired;
    }
    
    mapping(bytes32 => Option) public options;
    bytes32[] public activeOptions;
    uint256 public optionCount;
    
    address public oracle;
    address public pricingEngine;
    
    event OptionCreated(
        bytes32 indexed optionId,
        address indexed writer,
        address indexed holder,
        uint256 strike,
        uint256 expiry,
        OptionType optionType
    );
    
    function writeOption(
        address underlying,
        uint256 strike,
        uint256 expiry,
        OptionType optionType,
        ExerciseType exerciseType,
        uint256 size,
        address holder
    ) external payable returns (bytes32 optionId) {
        require(expiry > block.timestamp, "Invalid expiry");
        require(strike > 0, "Invalid strike");
        require(size > 0, "Invalid size");
        
        // Calculate required collateral for writer
        uint256 requiredCollateral = calculateWriterCollateral(
            underlying,
            strike,
            optionType,
            size
        );
        
        require(msg.value >= requiredCollateral, "Insufficient collateral");
        
        optionId = keccak256(abi.encodePacked(
            underlying,
            strike,
            expiry,
            optionType,
            block.timestamp,
            nonce++
        ));
        
        // Calculate premium using Black-Scholes or on-chain model
        uint256 premium = calculatePremium(
            underlying,
            strike,
            expiry,
            optionType,
            size
        );
        
        options[optionId] = Option({
            underlying: underlying,
            strike: strike,
            expiry: expiry,
            optionType: optionType,
            exerciseType: exerciseType,
            size: size,
            writer: msg.sender,
            holder: holder,
            premium: premium,
            exercised: false,
            expired: false
        });
        
        activeOptions.push(optionId);
        optionCount++;
        
        emit OptionCreated(optionId, msg.sender, holder, strike, expiry, optionType);
    }
    
    function exerciseOption(bytes32 optionId) external {
        Option storage option = options[optionId];
        require(option.holder == msg.sender, "Not holder");
        require(!option.exercised && !option.expired, "Option inactive");
        require(
            option.exerciseType == ExerciseType.AMERICAN || 
            block.timestamp >= option.expiry,
            "Cannot exercise yet"
        );
        
        uint256 currentPrice = oracle.getPrice(option.underlying);
        uint256 payoff = calculatePayoff(option, currentPrice);
        
        require(payoff > 0, "Option out of the money");
        
        option.exercised = true;
        
        // Transfer payoff to holder
        payable(msg.sender).transfer(payoff);
        
        // Return remaining collateral to writer
        uint256 remainingCollateral = calculateRemainingCollateral(option, payoff);
        if (remainingCollateral > 0) {
            payable(option.writer).transfer(remainingCollateral);
        }
        
        emit OptionExercised(optionId, msg.sender, payoff);
    }
    
    function calculatePayoff(
        Option memory option,
        uint256 currentPrice
    ) public pure returns (uint256) {
        if (option.optionType == OptionType.CALL) {
            if (currentPrice <= option.strike) return 0;
            return ((currentPrice - option.strike) * option.size) / 1e18;
        } else {
            if (currentPrice >= option.strike) return 0;
            return ((option.strike - currentPrice) * option.size) / 1e18;
        }
    }
}

On-Chain Black-Scholes Pricing

contract BlackScholesPricing {
    // Approximation constants
    uint256 constant ONE = 1e18;
    uint256 constant SQRT_TWO_PI = 2506628274631000502; // sqrt(2*pi) * 1e18
    
    struct PricingParams {
        uint256 spot;
        uint256 strike;
        uint256 timeToExpiry; // in seconds
        uint256 volatility;   // annualized, 1e18 = 100%
        uint256 riskFreeRate; // annualized, 1e18 = 100%
    }
    
    function calculateCallPrice(PricingParams memory params) 
        public pure returns (uint256) {
        (uint256 d1, uint256 d2) = calculateD(params);
        
        uint256 nd1 = cumulativeNormalDistribution(d1);
        uint256 nd2 = cumulativeNormalDistribution(d2);
        
        uint256 callPrice = (params.spot * nd1) / ONE - 
                           (params.strike * nd2 * discountFactor(params)) / ONE;
        
        return callPrice;
    }
    
    function calculatePutPrice(PricingParams memory params)
        public pure returns (uint256) {
        (uint256 d1, uint256 d2) = calculateD(params);
        
        uint256 nd1Neg = cumulativeNormalDistribution(ONE - d1);
        uint256 nd2Neg = cumulativeNormalDistribution(ONE - d2);
        
        uint256 putPrice = (params.strike * nd2Neg * discountFactor(params)) / ONE -
                          (params.spot * nd1Neg) / ONE;
        
        return putPrice;
    }
    
    function calculateD(PricingParams memory params)
        internal pure returns (uint256 d1, uint256 d2) {
        uint256 spot = params.spot;
        uint256 strike = params.strike;
        uint256 time = params.timeToExpiry;
        uint256 vol = params.volatility;
        uint256 rate = params.riskFreeRate;
        
        // d1 = (ln(S/K) + (r + σ²/2)*T) / (σ*sqrt(T))
        uint256 lnSK = ln((spot * ONE) / strike);
        uint256 volSqDiv2 = (vol * vol) / (2 * ONE);
        uint256 ratePlusVol = rate + volSqDiv2;
        uint256 numerator = lnSK + ((ratePlusVol * time) / 31536000); // Convert to years
        
        uint256 sqrtTime = sqrt((time * ONE) / 31536000);
        uint256 denominator = (vol * sqrtTime) / ONE;
        
        d1 = numerator / denominator;
        d2 = d1 - denominator;
    }
    
    function cumulativeNormalDistribution(uint256 x) 
        internal pure returns (uint256) {
        // Approximation of CDF
        if (x >= 3 * ONE) return ONE;
        if (x <= -3 * ONE) return 0;
        
        // Simple approximation: 0.5 * (1 + erf(x / sqrt(2)))
        // Using polynomial approximation for erf
        uint256 absX = x > 0 ? x : -x;
        uint256 t = ONE / (ONE + (2316419 * absX) / 10000000);
        
        uint256 poly = (319381530 * t) / 1000000000 -
                      (356563782 * t * t) / 1000000000 +
                      (1781477937 * t * t * t) / 1000000000 -
                      (1821255978 * t * t * t * t) / 1000000000 +
                      (1330274429 * t * t * t * t * t) / 1000000000;
        
        uint256 expTerm = exp(-((absX * absX) / (2 * ONE)));
        uint256 result = ONE - (poly * expTerm) / SQRT_TWO_PI;
        
        return x > 0 ? result : ONE - result;
    }
    
    function discountFactor(PricingParams memory params)
        internal pure returns (uint256) {
        // e^(-r*T)
        uint256 exponent = -((params.riskFreeRate * params.timeToExpiry) / 31536000);
        return exp(exponent);
    }
    
    // Math helper functions
    function ln(uint256 x) internal pure returns (uint256) {
        // Taylor series approximation
        require(x > 0, "Invalid input");
        
        uint256 result = 0;
        uint256 y = x;
        
        while (y >= 2 * ONE) {
            result += 693147180559945309; // ln(2)
            y /= 2;
        }
        
        // Taylor series: ln(1+z) ≈ z - z²/2 + z³/3 - ...
        uint256 z = y - ONE;
        uint256 z2 = (z * z) / ONE;
        uint256 z3 = (z2 * z) / ONE;
        
        result += z - z2/2 + z3/3;
        
        return result;
    }
    
    function sqrt(uint256 x) internal pure returns (uint256) {
        if (x == 0) return 0;
        
        uint256 z = (x + ONE) / 2;
        uint256 y = x;
        
        while (z < y) {
            y = z;
            z = (x / z + z) / 2;
        }
        
        return y;
    }
    
    function exp(int256 x) internal pure returns (uint256) {
        // Taylor series: e^x = 1 + x + x²/2! + x³/3! + ...
        if (x == 0) return ONE;
        
        uint256 absX = x > 0 ? uint256(x) : uint256(-x);
        uint256 result = ONE;
        uint256 term = ONE;
        
        for (uint i = 1; i <= 10; i++) {
            term = (term * absX) / (ONE * i);
            result += term;
        }
        
        return x > 0 ? result : (ONE * ONE) / result;
    }
}

Options AMM with Concentrated Liquidity

contract OptionsAMM {
    struct LiquidityPosition {
        int24 tickLower;
        int24 tickUpper;
        uint128 liquidity;
        uint256 feeGrowthInside0LastX128;
        uint256 feeGrowthInside1LastX128;
        address owner;
    }
    
    // Each strike-expiry pair is a separate "pool"
    struct OptionPool {
        uint256 strike;
        uint256 expiry;
        OptionType optionType;
        uint160 sqrtPriceX96;
        int24 currentTick;
        uint128 liquidity;
        uint256 feeGrowthGlobal0X128;
        uint256 feeGrowthGlobal1X128;
    }
    
    mapping(bytes32 => OptionPool) public pools;
    mapping(bytes32 => mapping(bytes32 => LiquidityPosition)) public positions;
    
    function createPool(
        uint256 strike,
        uint256 expiry,
        OptionType optionType,
        uint256 initialPrice
    ) external returns (bytes32 poolId) {
        poolId = keccak256(abi.encodePacked(strike, expiry, optionType));
        
        uint160 sqrtPriceX96 = uint160(sqrt(initialPrice) * (2**96) / 1e9);
        
        pools[poolId] = OptionPool({
            strike: strike,
            expiry: expiry,
            optionType: optionType,
            sqrtPriceX96: sqrtPriceX96,
            currentTick: priceToTick(initialPrice),
            liquidity: 0,
            feeGrowthGlobal0X128: 0,
            feeGrowthGlobal1X128: 0
        });
    }
    
    function addLiquidity(
        bytes32 poolId,
        int24 tickLower,
        int24 tickUpper,
        uint128 liquidity
    ) external returns (bytes32 positionId) {
        OptionPool storage pool = pools[poolId];
        
        // Calculate token amounts
        (uint256 amount0, uint256 amount1) = getAmountsForLiquidity(
            pool.sqrtPriceX96,
            tickLower,
            tickUpper,
            liquidity
        );
        
        // Transfer tokens
        if (amount0 > 0) underlying.transferFrom(msg.sender, address(this), amount0);
        if (amount1 > 0) quoteToken.transferFrom(msg.sender, address(this), amount1);
        
        positionId = keccak256(abi.encodePacked(msg.sender, poolId, tickLower, tickUpper));
        
        positions[poolId][positionId] = LiquidityPosition({
            tickLower: tickLower,
            tickUpper: tickUpper,
            liquidity: liquidity,
            feeGrowthInside0LastX128: 0,
            feeGrowthInside1LastX128: 0,
            owner: msg.sender
        });
        
        pool.liquidity += liquidity;
    }
    
    function buyOption(
        bytes32 poolId,
        uint256 size,
        uint256 maxPremium
    ) external {
        OptionPool storage pool = pools[poolId];
        
        // Calculate premium based on pool state
        uint256 premium = calculatePremium(poolId, size, true);
        require(premium <= maxPremium, "Slippage too high");
        
        // Execute swap through concentrated liquidity
        (int256 amount0, int256 amount1) = swap(
            poolId,
            true, // zeroForOne
            int256(size),
            0 // sqrtPriceLimitX96
        );
        
        // Transfer premium
        quoteToken.transferFrom(msg.sender, address(this), uint256(amount1));
        
        // Mint option to buyer
        _mintOption(msg.sender, poolId, size, pool.strike, pool.expiry, pool.optionType);
    }
    
    function swap(
        bytes32 poolId,
        bool zeroForOne,
        int256 amountSpecified,
        uint160 sqrtPriceLimitX96
    ) internal returns (int256 amount0, int256 amount1) {
        OptionPool storage pool = pools[poolId];
        
        SwapState memory state = SwapState({
            amountSpecifiedRemaining: amountSpecified,
            amountCalculated: 0,
            sqrtPriceX96: pool.sqrtPriceX96,
            tick: pool.currentTick,
            liquidity: pool.liquidity,
            feeGrowthGlobalX128: zeroForOne ? pool.feeGrowthGlobal0X128 : pool.feeGrowthGlobal1X128
        });
        
        // ... swap logic similar to Uniswap V3
        
        pool.sqrtPriceX96 = state.sqrtPriceX96;
        pool.currentTick = state.tick;
        pool.liquidity = state.liquidity;
    }
}

Structured Products

Principal Protected Notes

contract PrincipalProtectedNotes {
    struct Note {
        address holder;
        uint256 principal;
        uint256 maturity;
        uint256 strikePrice;
        uint256 participationRate; // e.g., 80% = 8000
        bool redeemed;
    }
    
    mapping(bytes32 => Note) public notes;
    uint256 public constant PERFORMANCE_FEE = 1000; // 10%
    
    function issueNote(
        address holder,
        uint256 maturity,
        uint256 strikePrice,
        uint256 participationRate
    ) external payable returns (bytes32 noteId) {
        require(msg.value > 0, "Zero principal");
        require(maturity > block.timestamp, "Invalid maturity");
        require(participationRate <= 10000, "Invalid participation rate");
        
        noteId = keccak256(abi.encodePacked(
            holder,
            block.timestamp,
            msg.value
        ));
        
        notes[noteId] = Note({
            holder: holder,
            principal: msg.value,
            maturity: maturity,
            strikePrice: strikePrice,
            participationRate: participationRate,
            redeemed: false
        });
    }
    
    function redeem(bytes32 noteId) external {
        Note storage note = notes[noteId];
        require(note.holder == msg.sender, "Not holder");
        require(block.timestamp >= note.maturity, "Not matured");
        require(!note.redeemed, "Already redeemed");
        
        note.redeemed = true;
        
        uint256 currentPrice = oracle.getPrice();
        uint256 payout;
        
        if (currentPrice <= note.strikePrice) {
            // No upside, return principal
            payout = note.principal;
        } else {
            // Calculate upside participation
            uint256 upside = currentPrice - note.strikePrice;
            uint256 upsideReturn = (upside * note.principal) / note.strikePrice;
            uint256 participation = (upsideReturn * note.participationRate) / 10000;
            
            payout = note.principal + participation;
            
            // Take performance fee
            uint256 fee = (participation * PERFORMANCE_FEE) / 10000;
            payout -= fee;
            
            payable(treasury).transfer(fee);
        }
        
        payable(msg.sender).transfer(payout);
    }
}

Yield Enhancement Strategies (Covered Calls)

contract CoveredCallVault {
    struct Vault {
        uint256 totalDeposits;
        uint256 activeDeposits;
        uint256 weeklyYield;
        uint256 callStrike;
        uint256 callExpiry;
        bool callWritten;
    }
    
    Vault public vault;
    mapping(address => uint256) public deposits;
    
    function deposit() external payable {
        deposits[msg.sender] += msg.value;
        vault.totalDeposits += msg.value;
        vault.activeDeposits += msg.value;
    }
    
    function writeCoveredCall(
        uint256 strike,
        uint256 expiry,
        uint256 premium
    ) external onlyManager {
        require(!vault.callWritten, "Call already written");
        require(expiry <= block.timestamp + 7 days, "Max 1 week expiry");
        
        vault.callStrike = strike;
        vault.callExpiry = expiry;
        vault.callWritten = true;
        
        // Sell call option and collect premium
        vault.weeklyYield += premium;
    }
    
    function settleWeek() external {
        require(block.timestamp >= vault.callExpiry, "Call not expired");
        require(vault.callWritten, "No call to settle");
        
        uint256 currentPrice = oracle.getPrice();
        
        if (currentPrice > vault.callStrike) {
            // Call exercised - cap upside at strike
            uint256 exercisedAmount = (vault.activeDeposits * vault.callStrike) / currentPrice;
            vault.activeDeposits = exercisedAmount;
        }
        
        // Distribute yield to depositors
        _distributeYield();
        
        // Reset for next week
        vault.callWritten = false;
        vault.weeklyYield = 0;
    }
    
    function _distributeYield() internal {
        uint256 totalYield = vault.weeklyYield;
        
        for (/* iterate depositors */) {
            uint256 share = (deposits[depositor] * totalYield) / vault.totalDeposits;
            payable(depositor).transfer(share);
        }
    }
}

Exotic Derivatives

Binary Options

contract BinaryOptions {
    enum BinaryType { HIGHER, LOWER, TOUCH, NO_TOUCH }
    
    struct BinaryOption {
        address trader;
        BinaryType binaryType;
        uint256 stake;
        uint256 targetPrice;
        uint256 expiry;
        uint256 payoutPercentage; // e.g., 180 = 180% return
        bool settled;
        bool won;
    }
    
    mapping(bytes32 => BinaryOption) public binaryOptions;
    
    function buyBinaryOption(
        BinaryType binaryType,
        uint256 targetPrice,
        uint256 expiry,
        uint256 payoutPercentage
    ) external payable returns (bytes32 optionId) {
        require(msg.value > 0, "Zero stake");
        require(expiry > block.timestamp, "Invalid expiry");
        require(payoutPercentage > 100 && payoutPercentage <= 200, "Invalid payout");
        
        optionId = keccak256(abi.encodePacked(
            msg.sender,
            block.timestamp,
            binaryType,
            targetPrice
        ));
        
        binaryOptions[optionId] = BinaryOption({
            trader: msg.sender,
            binaryType: binaryType,
            stake: msg.value,
            targetPrice: targetPrice,
            expiry: expiry,
            payoutPercentage: payoutPercentage,
            settled: false,
            won: false
        });
    }
    
    function settle(bytes32 optionId) external {
        BinaryOption storage option = binaryOptions[optionId];
        require(!option.settled, "Already settled");
        require(block.timestamp >= option.expiry, "Not expired");
        
        uint256 currentPrice = oracle.getPrice();
        option.settled = true;
        
        if (option.binaryType == BinaryType.HIGHER) {
            option.won = currentPrice > option.targetPrice;
        } else if (option.binaryType == BinaryType.LOWER) {
            option.won = currentPrice < option.targetPrice;
        } else if (option.binaryType == BinaryType.TOUCH) {
            // Check if price touched target during option period
            option.won = didPriceTouch(option.targetPrice, option.expiry);
        }
        
        if (option.won) {
            uint256 payout = (option.stake * option.payoutPercentage) / 100;
            payable(option.trader).transfer(payout);
        }
        // If lost, stake stays in contract (house edge)
    }
}

Barrier Options

contract BarrierOptions {
    enum BarrierType { UP_AND_IN, UP_AND_OUT, DOWN_AND_IN, DOWN_AND_OUT }
    
    struct BarrierOption {
        address holder;
        uint256 strike;
        uint256 barrier;
        uint256 expiry;
        BarrierType barrierType;
        uint256 size;
        bool knocked;
        bool knockedIn;
        bool exercised;
    }
    
    mapping(bytes32 => BarrierOption) public options;
    mapping(bytes32 => uint256[]) public priceHistory;
    
    function createBarrierOption(
        uint256 strike,
        uint256 barrier,
        uint256 expiry,
        BarrierType barrierType,
        uint256 size
    ) external payable returns (bytes32 optionId) {
        require(msg.value >= calculatePremium(strike, barrier, expiry, barrierType, size),
                "Insufficient premium");
        
        optionId = keccak256(abi.encodePacked(
            msg.sender,
            strike,
            barrier,
            expiry,
            barrierType
        ));
        
        options[optionId] = BarrierOption({
            holder: msg.sender,
            strike: strike,
            barrier: barrier,
            expiry: expiry,
            barrierType: barrierType,
            size: size,
            knocked: false,
            knockedIn: false,
            exercised: false
        });
    }
    
    function recordPrice(bytes32 optionId, uint256 price) external {
        BarrierOption storage option = options[optionId];
        require(block.timestamp <= option.expiry, "Expired");
        
        priceHistory[optionId].push(price);
        
        // Check if barrier hit
        if (!option.knocked) {
            if (option.barrierType == BarrierType.UP_AND_IN || 
                option.barrierType == BarrierType.UP_AND_OUT) {
                if (price >= option.barrier) {
                    option.knocked = true;
                    option.knockedIn = (option.barrierType == BarrierType.UP_AND_IN);
                }
            } else {
                if (price <= option.barrier) {
                    option.knocked = true;
                    option.knockedIn = (option.barrierType == BarrierType.DOWN_AND_IN);
                }
            }
        }
    }
    
    function exercise(bytes32 optionId) external {
        BarrierOption storage option = options[optionId];
        require(msg.sender == option.holder, "Not holder");
        require(block.timestamp >= option.expiry, "Not expired");
        require(!option.exercised, "Already exercised");
        
        option.exercised = true;
        
        bool isActive;
        if (option.barrierType == BarrierType.UP_AND_IN || 
            option.barrierType == BarrierType.DOWN_AND_IN) {
            isActive = option.knockedIn;
        } else {
            isActive = !option.knocked;
        }
        
        if (isActive) {
            uint256 currentPrice = oracle.getPrice();
            uint256 payoff = calculatePayoff(option.strike, currentPrice, option.size);
            
            if (payoff > 0) {
                payable(msg.sender).transfer(payoff);
            }
        }
    }
}

Auto-Compounding Yield Strategies

Delta-Neutral Yield Farming

contract DeltaNeutralVault {
    struct Strategy {
        uint256 depositAmount;
        uint256 longPerpSize;
        uint256 shortPerpSize;
        uint256 lendingYield;
        uint256 fundingYield;
        int256 fundingDebt;
    }
    
    mapping(address => Strategy) public strategies;
    
    function enterDeltaNeutral(uint256 amount) external {
        // 1. Deposit into lending protocol
        lendingProtocol.deposit(amount);
        
        // 2. Open long perpetual (delta positive)
        uint256 longSize = (amount * 2) / oracle.getPrice(); // 2x leverage
        perpDEX.openLong(longSize);
        
        // 3. Open short perpetual (delta negative)
        uint256 shortSize = (amount * 2) / oracle.getPrice(); // 2x leverage
        perpDEX.openShort(shortSize);
        
        // Net delta ≈ 0, collect funding + lending yield
        strategies[msg.sender] = Strategy({
            depositAmount: amount,
            longPerpSize: longSize,
            shortPerpSize: shortSize,
            lendingYield: 0,
            fundingYield: 0,
            fundingDebt: 0
        });
    }
    
    function harvestYield() external {
        Strategy storage strategy = strategies[msg.sender];
        
        // Collect lending yield
        uint256 newLendingYield = lendingProtocol.claimYield();
        strategy.lendingYield += newLendingYield;
        
        // Collect funding payments
        (int256 longFunding, int256 shortFunding) = perpDEX.getFundingPayments();
        strategy.fundingDebt += (longFunding - shortFunding);
        
        if (strategy.fundingDebt < 0) {
            // Net positive funding received
            strategy.fundingYield += uint256(-strategy.fundingDebt);
            strategy.fundingDebt = 0;
        }
    }
    
    function exitDeltaNeutral() external {
        Strategy storage strategy = strategies[msg.sender];
        
        // Close perp positions
        perpDEX.closeLong(strategy.longPerpSize);
        perpDEX.closeShort(strategy.shortPerpSize);
        
        // Withdraw from lending
        uint256 totalAmount = strategy.depositAmount + 
                             strategy.lendingYield + 
                             strategy.fundingYield;
        
        lendingProtocol.withdraw(totalAmount);
        
        payable(msg.sender).transfer(totalAmount);
        
        delete strategies[msg.sender];
    }
}

Risk Management for Complex Products

Greeks Calculation and Hedging

contract GreeksCalculator {
    struct Greeks {
        int256 delta;
        int256 gamma;
        int256 theta;
        int256 vega;
        int256 rho;
    }
    
    function calculateGreeks(
        uint256 spot,
        uint256 strike,
        uint256 timeToExpiry,
        uint256 volatility,
        bool isCall
    ) public pure returns (Greeks memory) {
        (uint256 d1, uint256 d2) = calculateD(spot, strike, timeToExpiry, volatility);
        
        uint256 nd1 = cumulativeNormalDistribution(d1);
        uint256 nd2 = cumulativeNormalDistribution(d2);
        uint256 nd1Prime = normalDistribution(d1);
        
        Greeks memory greeks;
        
        // Delta
        if (isCall) {
            greeks.delta = int256(nd1);
        } else {
            greeks.delta = int256(nd1) - 1e18;
        }
        
        // Gamma (same for calls and puts)
        greeks.gamma = int256((nd1Prime * 1e18) / (spot * volatility * sqrt(timeToExpiry)));
        
        // Theta
        uint256 thetaTerm = (spot * nd1Prime * volatility) / (2 * sqrt(timeToExpiry));
        if (isCall) {
            greeks.theta = -int256(thetaTerm + RISK_FREE_RATE * strike * nd2);
        } else {
            greeks.theta = -int256(thetaTerm - RISK_FREE_RATE * strike * (1e18 - nd2));
        }
        
        // Vega (same for calls and puts)
        greeks.vega = int256(spot * nd1Prime * sqrt(timeToExpiry));
        
        // Rho
        if (isCall) {
            greeks.rho = int256(strike * timeToExpiry * nd2);
        } else {
            greeks.rho = -int256(strike * timeToExpiry * (1e18 - nd2));
        }
        
        return greeks;
    }
    
    function hedgeDelta(bytes32 positionId, int256 targetDelta) external {
        Greeks memory greeks = calculatePositionGreeks(positionId);
        int256 deltaToHedge = targetDelta - greeks.delta;
        
        if (deltaToHedge > 0) {
            // Need positive delta - buy spot or open long perp
            perpDEX.openLong(uint256(deltaToHedge));
        } else {
            // Need negative delta - sell spot or open short perp
            perpDEX.openShort(uint256(-deltaToHedge));
        }
    }
}

Best Practices

1. Pricing Accuracy

  • Use proven models (Black-Scholes, Binomial)
  • Update volatility inputs frequently
  • Validate against market prices
  • Provide price bounds

2. Risk Controls

  • Position limits per user
  • Maximum exposure limits
  • Circuit breakers for extreme moves
  • Insurance funds for tail risk

3. Settlement Reliability

  • Multiple oracle sources
  • TWAP for settlement price
  • Grace periods for disputes
  • Clear dispute resolution

4. Gas Optimization

  • Batch settlements
  • Off-chain pricing, on-chain settlement
  • Efficient data structures
  • Layer 2 deployment

5. User Protection

  • Clear payoff diagrams
  • Max loss warnings
  • Educational resources
  • Graduated complexity (simple -> advanced)

Conclusion

Advanced derivatives expand DeFi beyond perpetuals into sophisticated financial instruments. Success requires:

  1. Accurate Pricing: Reliable on-chain models
  2. Robust Risk Management: Greeks tracking, dynamic hedging
  3. User Education: Clear explanations of complex products
  4. Gradual Rollout: Start simple, add complexity
  5. Institutional Features: Block trading, RFQ, portfolio margin

The platforms that master advanced derivatives will capture institutional flows and offer tools unavailable in traditional finance—creating sustainable competitive moats in the evolving DeFi landscape.