Skip to content

Commit

Permalink
feat: add counting of put price
Browse files Browse the repository at this point in the history
  • Loading branch information
0xvbetsun committed Sep 1, 2021
1 parent 6b50a28 commit 99afc66
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 122 deletions.
26 changes: 20 additions & 6 deletions lib/stock-oprions.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ import { StockOptionsService } from './stock-options.service';

describe('StockOptionsService', () => {
let service: StockOptionsService;
const underlying = 60;
const strike = 58;
const time = 0.5;
const interest = 0.035;
const sigma = 0.2;
const dividend = 0.0125;

beforeEach(async () => {
const moduleRef: TestingModule = await Test.createTestingModule({
Expand All @@ -13,22 +19,30 @@ describe('StockOptionsService', () => {
});

describe('should correctly count call price', () => {
const underlying = 60;
const strike = 58;
const time = 0.5;
const interest = 0.035;
const sigma = 0.2;
it('without dividends', () => {
const price = 5.0157;

expect(service.priceCall(underlying, strike, time, interest, sigma)).toBe(price);
});

it('with dividends', () => {
const dividend = 0.0125;
const price = 4.769;

expect(service.priceCall(underlying, strike, time, interest, sigma, dividend)).toBe(price);
});
});

describe('should correctly count put price', () => {
it('without dividends', () => {
const price = 2.0095;

expect(service.pricePut(underlying, strike, time, interest, sigma)).toBe(price);
});

it('with dividends', () => {
const price = 2.1367;

expect(service.pricePut(underlying, strike, time, interest, sigma, dividend)).toBe(price);
});
});
});
79 changes: 41 additions & 38 deletions lib/stock-options.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Injectable } from '@nestjs/common';
import { parser, round } from 'mathjs';
import { ToPrecision } from './to-precision';

// used for min/max normal distribution
const MIN_Z_SCORE = -8;
Expand All @@ -10,6 +10,7 @@ export class StockOptionsService {
/**
* computes the fair value of the call based on the knowns and assumed volatility (sigma)
*/
@ToPrecision()
priceCall(
underlying: number,
strike: number,
Expand All @@ -19,24 +20,36 @@ export class StockOptionsService {
dividend = 0,
): number {
const d1 = this.dOne(underlying, strike, time, interest, sigma, dividend);
const discountedUnderlying = parser().evaluate(
`exp(-1 * ${dividend} * ${time}) * ${underlying}`,
);
const probabilityWeightedValueOfBeingExercised = parser().evaluate(
`${discountedUnderlying} * ${this.normSDist(d1)}`,
);
const d2 = parser().evaluate(`${d1} - ${sigma} * sqrt(${time})`);
const discountedStrike = parser().evaluate(`exp(-1 * ${interest} * ${time}) * ${strike}`);
const probabilityWeightedValueOfDiscountedStrike = parser().evaluate(
`${discountedStrike} * ${this.normSDist(d2)}`,
);
const discountedUnderlying = Math.exp(-1 * dividend * time) * underlying;
const probabilityWeightedValueOfBeingExercised = discountedUnderlying * this.normSDist(d1);
const d2 = d1 - sigma * Math.sqrt(time);
const discountedStrike = Math.exp(-1 * interest * time) * strike;
const probabilityWeightedValueOfDiscountedStrike = discountedStrike * this.normSDist(d2);

return round(
parser().evaluate(
`${probabilityWeightedValueOfBeingExercised} - ${probabilityWeightedValueOfDiscountedStrike}`,
),
4,
);
return probabilityWeightedValueOfBeingExercised - probabilityWeightedValueOfDiscountedStrike;
}

/**
* computes the fair value of the put based on the knowns and assumed volatility (sigma)
*/
@ToPrecision()
pricePut(
underlying: number,
strike: number,
time: number,
interest: number,
sigma: number,
dividend = 0,
): number {
const d2 = this.dTwo(underlying, strike, time, interest, sigma, dividend);

const discountedStrike = strike * Math.exp(-1 * interest * time);
const probabiltityWeightedValueOfDiscountedStrike = discountedStrike * this.normSDist(-1 * d2);
const d1 = d2 + sigma * Math.sqrt(time);
const discountedUnderlying = underlying * Math.exp(-1 * dividend * time);
const probabilityWeightedValueOfBeingExercised = discountedUnderlying * this.normSDist(-1 * d1);

return probabiltityWeightedValueOfDiscountedStrike - probabilityWeightedValueOfBeingExercised;
}

/**
Expand All @@ -50,12 +63,11 @@ export class StockOptionsService {
sigma: number,
dividend: number,
): number {
const numerator = parser().evaluate(
`log(${underlying} / ${strike}) + (${interest} - ${dividend} + 0.5 * ${sigma}^2) * ${time}`,
);
const denominator = parser().evaluate(`${sigma} * sqrt(${time})`);
const numerator =
Math.log(underlying / strike) + (interest - dividend + 0.5 * sigma ** 2) * time;
const denominator = sigma * Math.sqrt(time);

return parser().evaluate(`${numerator} / ${denominator}`);
return numerator / denominator;
}

/**
Expand All @@ -69,16 +81,7 @@ export class StockOptionsService {
sigma: number,
dividend: number,
) {
return parser().evaluate(
`${this.dOne(
underlying,
strike,
time,
interest,
sigma,
dividend,
)} - ${sigma} * sqrt(${time})`,
);
return this.dOne(underlying, strike, time, interest, sigma, dividend) - sigma * Math.sqrt(time);
}

/**
Expand All @@ -99,20 +102,20 @@ export class StockOptionsService {

while (sum + term !== sum) {
sum += term;
term = parser().evaluate(`${term} * ${z}^2 / ${i}`);
term = (term * z ** 2) / i;
i += 2;
}

return parser().evaluate(`0.5 + ${sum} * ${this.phi(z)}`);
return 0.5 + sum * this.phi(z);
}

/**
* Standard Gaussian pdf
*/
private phi(x: number): number {
const numerator = parser().evaluate(`exp(-1 * ${x}^2 / 2)`);
const denominator = parser().evaluate('sqrt(2 * PI)');
const numerator = Math.exp((-1 * x ** 2) / 2);
const denominator = Math.sqrt(2 * Math.PI);

return parser().evaluate(`${numerator} / ${denominator}`);
return numerator / denominator;
}
}
17 changes: 17 additions & 0 deletions lib/to-precision.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export function ToPrecision(precision = 4) {
return function (
target: unknown,
propertyKey: string,
descriptor: PropertyDescriptor,
): PropertyDescriptor {
const originalMethod = descriptor.value;

descriptor.value = function () {
const result = originalMethod.apply(this, arguments);

return Math.round((result + Number.EPSILON) * 10 ** precision) / 10 ** precision;
};

return descriptor;
};
}
76 changes: 2 additions & 74 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 1 addition & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,14 @@
"url": "https://github.com/VBetsun/nestjs-stock-options/issues"
},
"homepage": "https://github.com/VBetsun/nestjs-stock-options#readme",
"dependencies": {
"mathjs": "^9.4.4"
},
"dependencies": {},
"devDependencies": {
"@commitlint/cli": "^12.1.4",
"@commitlint/config-angular": "^12.1.4",
"@nestjs/common": "^8.0.6",
"@nestjs/core": "^8.0.6",
"@nestjs/testing": "^8.0.6",
"@types/jest": "^27.0.1",
"@types/mathjs": "^9.4.2",
"@types/node": "^16.7.4",
"@typescript-eslint/eslint-plugin": "^4.29.3",
"@typescript-eslint/parser": "^4.29.3",
Expand Down

0 comments on commit 99afc66

Please sign in to comment.