65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 HTML 5 Canvas 和 JavaScript 绘制金融期权的盈亏图

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.63/5 (6投票s)

2016年5月18日

CPOL

5分钟阅读

viewsIcon

17513

使用 HTML 5 Canvas 和 JavaScript 绘制金融期权的盈亏图

引言

在本文中,我将介绍如何使用 HTML 5 Canvas 和 JavaScript 绘制期权交易策略的盈亏图,该图是资产价格的函数。

背景

作为一名对使用期权等金融杠杆交易证券资产的理论和实践都感兴趣的开发者,我需要在下单前计算交易的盈亏状况。对于不熟悉期权的人来说,可以在 http://www.investopedia.com/university/options/option.asp 找到许多参考资料作为入门,否则我将假设您已经是该领域的专家,并且已经在进行交易并从中获利或亏损。你们中的一些人可能非常擅长这一点,以至于在进行交易时能够将潜在收益和损失的所有数字都记在脑海中,但对我而言,我发现有一个关于股票/资产价格与盈亏的图表非常重要。一图胜千言,这句话永远是正确的。期权交易策略多种多样,从简单的单腿策略到复杂的包含多条腿的策略。在本文中,我将通过绘制买入看涨期权策略的盈亏与资产价格的图表来简化。复杂策略的图表绘制将使用相同的技术。使用 HTML 5 Canvas 及其绘图功能以及 JavaScript,我们将构建一个方程,并绘制出我们高中时都熟悉的带有 X 和 Y 轴的盈亏图。

所以,假设我们要购买 1 手(100 股)代号为 SPX 的美国标普 500 ETF 指数的看涨期权。截至本文撰写之时,即 2016 年 5 月 17 日收盘时,SPX 价格为 2047。2016 年 6 月 17 日到期、行权价为 2110 的看涨期权价格约为每手 5.5 美元。那么,当合同于 2016 年 6 月 17 日到期时,这笔交易的盈亏图会是什么样子?预览一下,它看起来会像下图所示

从上图可以看出,最大亏损封顶为 550 美元,盈亏平衡点为 2115,一旦价格超过盈亏平衡点,上涨空间利润则无限。如果你认为标普 500 指数在下个月内会上涨几百点,那么使用这种策略你将变得非常富有!

使用代码

好了,现在我们来技术性地讨论一下如何使用 HTML 5 Canvas 和 JavaScript 来绘制这样的图表。步骤如下

  1. 定义 HTML 5 绘图表面。
  2. 定义一个 JavaScript 对象,其中包含描述图表的必要参数。
  3. 为上述对象定义一个方法来绘制 X 轴(股票价格)。
  4. 为上述对象定义一个方法来绘制 Y 轴(盈亏)。
  5. 为上述对象定义一个方法来绘制代表“买入看涨期权”策略的方程。
  6. 定义一个方法来将图表适配到绘图表面。
  7. 使用具有目标参数的对象并调用其方法来绘制图表。

步骤 1
在此代码片段中使用 HTML 5 Canvas 定义绘图表面

<!DOCTYPE HTML>
<html>
<head>
    <style>
        body {
            margin: 0px;
            padding: 0px;
        }
    </style>
</head>
<body>
    <canvas id="canvasProfitLoss" width="1200" height="600" style="border:1px solid #000000;"></canvas>
<script>
</script>
</body>
</html>

第二步
方程图表需要某些参数来定义 X 和 Y 轴的范围、X 和 Y 轴的单位等。以下是 JavaScript 图表对象的代码片段,应插入到步骤 1 中的 <script></script> 标签之间
//object definition
function ProfitLossChart(minPrice, minProfitLoss, maxPrice, maxProfitLoss, unitsPrice, unitsProfitLoss) {

            this.canvas = document.getElementById("canvasProfitLoss");
            this.minPrice = minPrice;
            this.minProfitLoss = minProfitLoss;
            this.maxPrice = maxPrice;
            this.maxProfitLoss = maxProfitLoss;
            this.unitsPrice = unitsPrice;
            this.unitsProfitLoss = unitsProfitLoss;
            this.tickSize = 20;

            this.context = this.canvas.getContext('2d');
            this.rangePrice = this.maxPrice - this.minPrice;
            this.rangeProfitLoss = this.maxProfitLoss - this.minProfitLoss;
            this.unitPrice = this.canvas.width / this.rangePrice;
            this.unitProfitLoss = this.canvas.height / (this.rangeProfitLoss);
            this.centerProfitLoss = Math.round(Math.abs(this.minProfitLoss / this.rangeProfitLoss) * this.canvas.height) ;
            this.centerPrice =  + Math.round(Math.abs(this.minPrice / this.rangePrice) * this.canvas.width);
            this.iteration = (this.maxPrice - this.minPrice) / 1000;
            this.scalePrice = this.canvas.width / this.rangePrice;
            this.scaleProfitLoss = this.canvas.height / this.rangeProfitLoss;

        };


步骤 3
要绘制 X 轴(价格),请使用 JavaScript 的 prototype 为该对象添加功能。我们还绘制刻度线、单位标签和轴标签。
    // using prototype and create function
        ProfitLossChart.prototype.drawPriceAxis= function () {
            var context = this.context;
            context.save();
            context.beginPath();

            //start x 10 pix to the left edge, y is at the center
            context.moveTo(10, this.centerProfitLoss); 
            //draw horizontal line, Price axis
            context.lineTo(this.canvas.width, this.centerProfitLoss);

            context.strokeStyle = this.axisColor;
            context.lineWidth = 2;
            context.stroke();

            // draw right tick marks
            var pricePosIncrement = this.unitsPrice * this.unitPrice;
            var pricePos, unit;
            context.font = this.font;
            context.textAlign = 'center';
            context.textBaseline = 'top';

            pricePos =  pricePosIncrement;
            unit = this.unitsPrice ;
            while (pricePos < this.canvas.width) {
                //draw tick marks as line crossing the Price axis
                context.moveTo(pricePos , this.centerProfitLoss - this.tickSize / 2);
                context.lineTo(pricePos , this.centerProfitLoss + this.tickSize / 2);
                context.stroke();
                //draw numerical label for tic marks
                //note we add minPrice
                context.fillText(unit + this.minPrice, pricePos, this.centerProfitLoss + this.tickSize / 2 - 30);
                unit += this.unitsPrice;
                pricePos = Math.round(pricePos + pricePosIncrement);
            }
            
            //draw X axix label as Price at center of X axis
            context.font = "12px";
            context.fillStyle = "green";
            context.fillText("Price($)", this.canvas.width / 2, this.centerProfitLoss + this.tickSize / 2 -50);
            context.restore();
        };

步骤 4
与步骤 3 的概念和技术相同,创建一个对象的函数来绘制 Y 轴(盈亏),带有刻度线和标签
    ProfitLossChart.prototype.drawProfitLossAxis = function () {
          var context = this.context;
          context.save();
          context.beginPath();
          //draw vertical line
          context.moveTo( 20, 0)
          context.lineTo( 20, this.canvas.height);
          context.strokeStyle = this.axisColor;
          context.lineWidth = 2;
          context.stroke();

          // draw tick marks
          var profitLossPosIncrement = this.unitsProfitLoss * this.unitProfitLoss;
          var profitLossPos, unit;
          context.font = this.font;
          context.textAlign = 'right';
          context.textBaseline = 'middle';

          // draw top tick marks
          profitLossPos = this.centerProfitLoss - profitLossPosIncrement;
          unit = this.unitsProfitLoss;
          while (profitLossPos > 0) {
              context.moveTo(20 - this.tickSize / 2, profitLossPos);
              context.lineTo(20 + this.tickSize / 2, profitLossPos);
              context.stroke();
              context.fillText(unit, 70 - this.tickSize / 2 - 3 , profitLossPos);
              unit += this.unitsProfitLoss;
              profitLossPos = Math.round(profitLossPos - profitLossPosIncrement);
          }

          // draw bottom tick marks
          profitLossPos = this.centerProfitLoss + profitLossPosIncrement;
          unit = -1 * this.unitsProfitLoss;
          while (profitLossPos < this.canvas.height) {
              context.moveTo(20- this.tickSize / 2, profitLossPos);
              context.lineTo(20 + this.tickSize / 2, profitLossPos);
              context.stroke();
              context.fillText(unit, 70 - this.tickSize / 2 - 3, profitLossPos);
              unit -= this.unitsProfitLoss;
              profitLossPos = Math.round(profitLossPos + profitLossPosIncrement);
          }
          
            //draw Y axix label as Profit/Loss (P/L) to the right
         
              context.font = "12px";
          context.fillStyle = "green";
          context.fillText("Profit/Loss($)", this.centerPrice + 140, this.canvas.height / 4);
          context.restore();
      };

步骤 5
绘制盈亏方程涉及在定义的迭代中绘制一系列小线,并将它们“连接”起来形成一个完整的图表。所需的策略的确切方程作为匿名函数由该对象的用户传入。在我们的例子中,它是“买入看涨期权”方程,对于其他策略,将传入相应的方程。代码片段如下
    ProfitLossChart.prototype.drawEquation = function (drawFunction) {
          var context = this.context;
          context.save();
          this.transformContext();

          context.beginPath();
          context.moveTo(this.minPrice, drawFunction(this.minPrice));

          for (var x = this.minPrice + this.iteration; x <= this.maxPrice; x += this.iteration) {
              context.lineTo(x, drawFunction(x));
          }
          context.restore();
          context.lineJoin = 'round';
          context.lineWidth = 3;
          context.strokeStyle = 'red';
          context.stroke();
          context.restore();
      };

步骤 6
    //do the transform
        ProfitLossChart.prototype.transformContext = function () {
          var context = this.context;
          this.context.translate(this.centerPrice, this.centerProfitLoss);
          context.scale(this.scalePrice , -this.scaleProfitLoss);
      };

就是这样。我们已经定义了绘制图表所需对象的所有属性和方法。让我们以之前提到的 SPX 指数买入看涨期权为例来绘制它

步骤 7
    //define parameters, instantiate and draw the chart!
        var minPrice = 0;
        var maxPrice = 2200;
        var minPL = -10000;
        var maxPL = 10000;
        var unitsPrice = 50;
        var unitsProfitLoss = 500;

        //create object
        var chart = new ProfitLossChart(minPrice, minPL, maxPrice, maxPL, unitsPrice, unitsProfitLoss);
      chart.drawPriceAxis();
      chart.drawProfitLossAxis();
      
      //draw buy call equation, spx June 17, today is 5/16/2016
       var pStrike = 2110;
       var pCall = 5.5;
       chart.drawEquation(function (x) {
            if (x > pStrike)
            {
                return 100 * (x - pStrike - pCall);
            }
            else
            {
                return -(100 * pCall);
            }
       });

关注点

本文仅简要说明了 HTML5 Canvas 功能在金融期权领域绘制图表的一些基本应用。当然,还可以开发和添加更高级的功能,例如缩放、交互式图表,当鼠标指针移到图表上时显示该点的 X、Y 坐标等。

目前有 1 到 2 个网站提供许多其他复杂策略的免费期权图表绘制,并具有更实用的功能,例如获取实时期权数据。我也成功地开发了自己的网站 www.optionschart101.com 来实现这一目的。目前,该网站提供了铁蝶式、蝶式、跨式等策略的图表绘制。该网站仍在开发中,我希望有一天能从中获利,提供其他付费服务,但目前是免费的,并且在一段时间内也是免费的,因此可能仍存在错误。 

 

© . All rights reserved.