从理论到实操:期权核心逻辑梳理与代码实现

4 阅读10分钟

前言

本文主要梳理期权相关的理论知识,涵盖期权的定义、功能、运行原理、行业应用及优劣势,同时解析期权与其他金融衍生品的区别;并基于 Hardhat V3 开发框架,结合 OpenZeppelin V5、Solidity 0.8.24 + 及 Chainlink 工具,完整实现了极简期权智能合约从开发、测试到部署落地的全流程。

期权相关知识梳理

一、期权是什么

期权是一种金融衍生品,买方支付少量权利金,获得在约定时间内以约定价格买卖标的资产(股票、指数、期货等)的权利;卖方收权利金后,需承担买方行权的履约义务。核心是“权利与义务分离”,买方只享权利,卖方仅担义务。

关键分类:按权利方向分认购(看涨)、认沽(看跌)期权;按行权时间分欧式(仅到期可行权)、美式(到期前任意时间可行权)。

二、期权能做什么

  1. 风险管理:用少量权利金对冲标的资产价格波动风险(如持股买认沽防下跌);
  2. 杠杆投机:以低权利金撬动标的大幅波动收益,杠杆效率高于股票、期货;
  3. 收益增强:持仓者卖期权收权利金,提升震荡市资产收益;
  4. 套利交易:利用期权与标的、不同期权合约的价格偏差,赚取低风险收益。

三、运行原理

  1. 定价核心:权利金=内在价值(行权即时收益,分实值、平值、虚值)+时间价值(到期前波动潜力,越临近到期衰减越快);

  2. 交易流程:场内标准化合约交易,买方付权利金,卖方缴保证金;交易后可平仓了结,或持有至到期行权(实值行权,虚值放弃);

  3. 结算方式:分实物交割(如股票)和现金交割(如指数),场内以现金交割为主。

四、行业应用

  • 金融机构:用期权对冲资管产品风险、增强收益,或开展做市业务;
  • 实体企业:对冲原材料、产品价格及汇率风险,锁定经营利润;
  • 投资者:个人用期权低门槛参与市场,专业机构通过组合策略投机、套利。

五、优劣势

优势

买方风险可控(最大亏权利金)、收益无限;杠杆灵活、资金成本低;策略丰富,适配牛熊震荡全行情,兼顾攻防。

劣势

有时间损耗,到期虚值期权权利金归零;定价复杂,对专业度要求高;卖方无对冲时风险无限;部分合约流动性不足。

补充:期权与期货、股票的核心差异(简表)

工具风险收益特征权利义务资金成本策略丰富度
期权(买方)亏损有限,收益无限只享权利,不担义务低(仅付权利金)极高(多策略适配所有行情)
期权(卖方)收益有限,风险无限只担义务,享权利金中(缴纳保证金)较高(震荡市收权利金)
期货亏损无限,收益无限权利义务对等中(缴纳保证金)一般(多空为主,组合少)
股票亏损有限(本金),收益无限权利义务对等高(全额支付本金)低(低买高卖为主)

期权和永续合约核心维度对比表:期权的核心是 “权利与义务分离” 的选择权,永续合约的核心是 “无到期日” 的杠杆式标的资产交易

对比维度永续合约期权
核心定义无到期日的杠杆式衍生品,对标标的资产价格,本质是双方对标的价格走势的赌约,需缴纳保证金买方支付权利金获得 “行权选择权”,卖方收取权利金承担履约义务,本质是权利的买卖
权利义务买卖双方权利义务完全对等,均需履约,无选择权买卖双方权利义务完全分离:买方只享权利、不担义务;卖方只担义务、享权利金收益
到期规则无到期日,可长期持有,只需维持保证金充足,避免爆仓有明确到期日,到期未行权则权利作废,时间价值随到期日临近衰减
成本 / 保证金买卖双方均需缴纳保证金(杠杆交易,保证金比例低),无额外费用,保证金随标的价格波动补仓 / 平仓买方:仅支付权利金(最大亏损,无保证金要求);卖方:缴纳保证金(担保履约),权利金是卖方的收益
风险收益特征买卖双方均风险无限、收益无限:做多涨则赚、跌则亏;做空跌则赚、涨则亏,极端行情易爆仓买方:亏损有限(权利金)、收益无限;卖方:收益有限(权利金)、风险无限(无对冲时)
价格影响因素仅对标标的资产的市场价格,受标的供需、行情波动影响标的价格、行权价、到期日、波动率、无风险利率等多重因素影响,定价更复杂
核心功能主要用于杠杆投机、短期趋势交易,少量用于对冲(需主动平仓)风险管理(对冲)、杠杆投机、收益增强、套利,策略丰富,适配所有行情

智能合约开发、测试、部署

智能合约

  • MockV3Aggregator3合约hardhatV3本地部署MockV3Aggregator3便于测试,正式环境使用Chainlink对应的合约地址
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract MockV3Aggregator3 {
    uint8 public decimals;
    int256 public latestAnswer;
    uint256 public updatedAt;

    constructor(uint8 _decimals, int256 _initialAnswer) {
        decimals = _decimals;
        latestAnswer = _initialAnswer;
        updatedAt = block.timestamp;
    }

    // 关键:在测试脚本中调用此函数模拟行情变动
    function updateAnswer(int256 _newAnswer) external {
        latestAnswer = _newAnswer;
        updatedAt = block.timestamp;
    }

    function latestRoundData() external view returns (
        uint80 roundId,
        int256 answer,
        uint256 startedAt,
        uint256 updatedAt_,
        uint80 answeredInRound
    ) {
        return (1, latestAnswer, updatedAt, updatedAt, 1);
    }
}
  • 价格预言机
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";

contract PriceOracle {
    function getAssetPrice(address feedAddress) public view returns (uint256) {
        AggregatorV3Interface feed = AggregatorV3Interface(feedAddress);
        (, int256 price, , uint256 updatedAt, ) = feed.latestRoundData();
        
        require(price > 0, "Oracle: Invalid price");
        // 本地测试时,如果 block.timestamp 和 updatedAt 都是 0 会报错,这里确保兼容
        require(updatedAt != 0, "Oracle: Invalid update time");

        uint8 decimals = feed.decimals();
        // 统一转为 18 位小数,方便 Engine 计算
        return uint256(price) * (10 ** (18 - decimals));
    }
}
  • 资产金库
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

contract FuturesVault is Ownable {
    using SafeERC20 for IERC20;

    IERC20 public immutable asset; // 结算货币 (如 USDT)
    mapping(address => bool) public isEngine;
    mapping(address => uint256) public userMargin; // 用户保证金余额

    constructor(address _asset) Ownable(msg.sender) {
        asset = IERC20(_asset);
    }

    modifier onlyEngine() {
        require(isEngine[msg.sender], "Vault: Not an authorized engine");
        _;
    }

    function setEngine(address _engine, bool _status) external onlyOwner {
        isEngine[_engine] = _status;
    }

    // 充值保证金
    function depositMargin(uint256 amount) external {
        asset.safeTransferFrom(msg.sender, address(this), amount);
        userMargin[msg.sender] += amount;
    }

    // 引擎结算盈亏:从金库划转或扣除
    function settlePnL(address trader, int256 pnl) external onlyEngine {
        if (pnl > 0) {
            userMargin[trader] += uint256(pnl);
        } else {
            userMargin[trader] -= uint256(-pnl);
        }
    }

    // 提现
    function withdraw(uint256 amount) external {
        require(userMargin[msg.sender] >= amount, "Vault: Insufficient balance");
        userMargin[msg.sender] -= amount;
        asset.safeTransfer(msg.sender, amount);
    }
}
  • 交易引擎
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {PriceOracle} from "./PriceOracle.sol";
import {FuturesVault} from "./FuturesVault.sol";

contract FuturesEngine is ReentrancyGuard {
    PriceOracle public immutable oracle;
    FuturesVault public immutable vault;
    address public immutable priceFeed; // Chainlink 喂价地址

    struct Position {
        uint256 size;       // 头寸大小
        uint256 entryPrice; // 开仓价格
        bool isLong;        // 方向
        bool isActive;
    }

    mapping(address => Position) public positions;

    constructor(address _oracle, address _vault, address _priceFeed) {
        oracle = PriceOracle(_oracle);
        vault = FuturesVault(_vault);
        priceFeed = _priceFeed;
    }

    // 开仓
    function openPosition(uint256 size, bool isLong) external nonReentrant {
        require(!positions[msg.sender].isActive, "Engine: Position already exists");
        
        uint256 currentPrice = oracle.getAssetPrice(priceFeed);
        // 极简风控:保证金必须至少是头寸大小的 10% (10倍杠杆限制)
        require(vault.userMargin(msg.sender) >= size / 10, "Engine: Insufficient margin");

        positions[msg.sender] = Position({
            size: size,
            entryPrice: currentPrice,
            isLong: isLong,
            isActive: true
        });
    }

    // 平仓并结算盈亏
    function closePosition() external nonReentrant {
        Position storage pos = positions[msg.sender];
        require(pos.isActive, "Engine: No active position");

        uint256 currentPrice = oracle.getAssetPrice(priceFeed);
        int256 pnl;

        // 计算 PnL = size * (price_diff / entry_price)
        if (pos.isLong) {
            pnl = int256(pos.size * (currentPrice - pos.entryPrice) / pos.entryPrice);
        } else {
            pnl = int256(pos.size * (pos.entryPrice - currentPrice) / pos.entryPrice);
        }

        pos.isActive = false;
        vault.settlePnL(msg.sender, pnl); // 通知金库更新余额
    }
}

测试脚本

用例说明

  • 交易流程:(正常的交易结算)
  • 安全检测:(保证金不足以支撑杠杆交易回滚)
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { parseUnits } from "viem";
import { network } from "hardhat";

const TOKEN_DECIMALS = 18;
const ORACLE_DECIMALS = 8; // 模拟 Chainlink 常见的 8 位小数

describe("Futures System - Local Mock Test", () => {
  async function deployFixture() {
    const { viem } = await network.connect();
    const [owner, trader] = await viem.getWalletClients();
    const publicClient = await viem.getPublicClient();

    // 1. 部署 Mock 资产和 Oracle
    const mockUSDT = await viem.deployContract("BoykaYuriToken", [owner.account.address, owner.account.address]);
    const oracle = await viem.deployContract("PriceOracle");
    const vault = await viem.deployContract("FuturesVault", [mockUSDT.address]);

    // 2. 【重点】部署本地价格模拟器:初始价格 3000 USD
    const initialPrice = parseUnits("3000", ORACLE_DECIMALS);
    const mockAggregator = await viem.deployContract("MockV3Aggregator3", [ORACLE_DECIMALS, initialPrice]);

    // 3. 部署 Engine,绑定到 Mock 预言机地址
    const engine = await viem.deployContract("FuturesEngine", [
      oracle.address,
      vault.address,
      mockAggregator.address, // 这里传入的是 mock 地址,而非在线地址
    ]);

    await vault.write.setEngine([engine.address, true]);

    return { trader, mockUSDT, vault, engine, mockAggregator, publicClient };
  }


  it("手动价格操纵后应正确计算利润", async () => {
    const { trader, mockUSDT, vault, engine, mockAggregator } = await deployFixture();

    // 步骤 A: 准备 1000 USDT 保证金
    const marginAmount = parseUnits("1000", TOKEN_DECIMALS);
    await mockUSDT.write.mint([trader.account.address, marginAmount]);
    await mockUSDT.write.approve([vault.address, marginAmount], { account: trader.account });
    await vault.write.depositMargin([marginAmount], { account: trader.account });

    // 步骤 B: 价格 3000 时开 5000 USDT 的多单
    const positionSize = parseUnits("5000", TOKEN_DECIMALS);
    await engine.write.openPosition([positionSize, true], { account: trader.account });

    // 步骤 C: 【手动操纵价格】将价格上调至 3300 (+10%)
    const newPrice = parseUnits("3300", ORACLE_DECIMALS);
    await mockAggregator.write.updateAnswer([newPrice]);

    // 步骤 D: 平仓并验证盈亏
    await engine.write.closePosition([], { account: trader.account });

    const finalMargin = await vault.read.userMargin([trader.account.address]);
    
    // 理论盈利:5000 * (3300-3000)/3000 = 500 USDT
    // 最终余额应接近 1500 USDT
    const expectedProfit = parseUnits("500", TOKEN_DECIMALS);
    assert.ok(finalMargin >= marginAmount + expectedProfit, "Trader should have earned 500 USDT");
  });
  it("应因高杠杆保证金不足而恢复", async () => {
    const { trader, mockUSDT, vault, engine } = await deployFixture();
    
    const smallMargin = parseUnits("10", TOKEN_DECIMALS);
    
    // 1. 铸造代币
    await mockUSDT.write.mint([trader.account.address, smallMargin]);
    
    // 2. 【缺失的修复步骤】授权 Vault 扣款
    await mockUSDT.write.approve([vault.address, smallMargin], { account: trader.account });
    
    // 3. 存入保证金
    await vault.write.depositMargin([smallMargin], { account: trader.account });

    const hugeSize = parseUnits("2000", TOKEN_DECIMALS); // 200x leverage

    // 4. 验证开仓失败
    await assert.rejects(
      engine.write.openPosition([hugeSize, true], { account: trader.account }),
      (err: any) => {
        // 打印错误详情有助于调试
        // console.log(err.message); 
        return err.message.includes("Engine: Insufficient margin");
      }
    );
  });

});

部署脚本

// scripts/deploy.js
import { network, artifacts } from "hardhat";
import { parseUnits } from "viem";
async function main() {
  // 连接网络
  const { viem } = await network.connect({ network: network.name });//指定网络进行链接
  
  // 获取客户端
  const [deployer] = await viem.getWalletClients();
  const publicClient = await viem.getPublicClient();
 
  const deployerAddress = deployer.account.address;
   console.log("部署者的地址:", deployerAddress);
  // 加载合约
  const BoykaYuriTokenArtifact = await artifacts.readArtifact("BoykaYuriToken");
    const PriceOracleArtifact = await artifacts.readArtifact("PriceOracle");
    const FuturesVaultArtifact = await artifacts.readArtifact("FuturesVault");
    const MockV3Aggregator3Artifact = await artifacts.readArtifact("MockV3Aggregator3");
  const FuturesEngineArtifact = await artifacts.readArtifact("FuturesEngine");
 
  // 部署(构造函数参数:recipient, initialOwner)
  const BoykaYuriTokenHash = await deployer.deployContract({
    abi: BoykaYuriTokenArtifact.abi,//获取abi
    bytecode: BoykaYuriTokenArtifact.bytecode,//硬编码
    args: [deployerAddress,deployerAddress],//部署者地址,初始所有者地址
  });
   const BoykaYuriTokenReceipt = await publicClient.waitForTransactionReceipt({ hash: BoykaYuriTokenHash });
   console.log("代币合约地址:", BoykaYuriTokenReceipt.contractAddress);
//
const PriceOracleHash = await deployer.deployContract({
    abi: PriceOracleArtifact.abi,//获取abi
    bytecode: PriceOracleArtifact.bytecode,//硬编码
    args: [],
  });
  // 等待确认并打印地址
  const PriceOracleReceipt = await publicClient.waitForTransactionReceipt({ hash: PriceOracleHash });
  console.log("预言机合约地址:", PriceOracleReceipt.contractAddress);
  // 部署(构造函数参数:recipient, initialOwner)
const FuturesVaultHash = await deployer.deployContract({
    abi: FuturesVaultArtifact.abi,//获取abi
    bytecode: FuturesVaultArtifact.bytecode,//硬编码
    args: [BoykaYuriTokenReceipt.contractAddress],//
  });
  // 等待确认并打印地址
  const FuturesVaultReceipt = await publicClient.waitForTransactionReceipt({ hash: FuturesVaultHash });
  console.log("期货合约地址:", FuturesVaultReceipt.contractAddress);
    const ORACLE_DECIMALS = 8; 
   const initialPrice = parseUnits("3000", ORACLE_DECIMALS);
  const MockV3Aggregator3Hash = await deployer.deployContract({
    abi: MockV3Aggregator3Artifact.abi,//获取abi
    bytecode: MockV3Aggregator3Artifact.bytecode,//硬编码
    args: [ORACLE_DECIMALS, initialPrice],//
  });
  // 等待确认并打印地址
  const MockV3Aggregator3Receipt = await publicClient.waitForTransactionReceipt({ hash: MockV3Aggregator3Hash });
  console.log("预言机合约地址:", MockV3Aggregator3Receipt.contractAddress);
  const FuturesEngineHash = await deployer.deployContract({
    abi: FuturesEngineArtifact.abi,//获取abi
    bytecode: FuturesEngineArtifact.bytecode,//硬编码
    args: [PriceOracleReceipt.contractAddress,FuturesVaultReceipt.contractAddress,MockV3Aggregator3Receipt.contractAddress],//
  });
  // 等待确认并打印地址
  const FuturesEngineReceipt = await publicClient.waitForTransactionReceipt({ hash: FuturesEngineHash });
  console.log("期货引擎合约地址:", FuturesEngineReceipt.contractAddress);
}

main().catch(console.error);

结语

至此,期权相关的知识体系梳理与代码落地实操已全部完成,同时也对各类金融衍生品的核心特性与差异完成了系统的对比梳理。从理论框架到实操落地,从单一产品解析到同类衍生品横向对标,整套内容已形成完整闭环,希望能为大家理解期权及金融衍生品、开展相关实操应用提供切实的参考与助力。