Skip to content

Commit

Permalink
Merge pull request #97 from agrotz/master
Browse files Browse the repository at this point in the history
Use native finmath curves in equities framework
  • Loading branch information
cfries authored Dec 19, 2024
2 parents 4b45ea9 + f86b7af commit af5fb87
Show file tree
Hide file tree
Showing 10 changed files with 970 additions and 1,130 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,116 +6,91 @@
import java.util.Comparator;
import java.util.HashMap;


/**
* Class to store and handle a stream of affine dividends
*
* @author Andreas Grotz
*/

public class AffineDividendStream {
private final AffineDividend[] dividendStream;

public AffineDividendStream(
final AffineDividend[] dividendStream)
{
final var diviList = Arrays.asList(dividendStream);
diviList.sort(Comparator.comparing(pt -> pt.getDate()));
this.dividendStream = diviList.toArray(new AffineDividend[0]);
}
private final AffineDividend[] dividendStream;

public AffineDividendStream(final AffineDividend[] dividendStream) {
final var diviList = Arrays.asList(dividendStream);
diviList.sort(Comparator.comparing(pt -> pt.getDate()));
this.dividendStream = diviList.toArray(new AffineDividend[0]);
}

public ArrayList<LocalDate> getDividendDates()
{
final var dates = new ArrayList<LocalDate>();
for (final AffineDividend divi : dividendStream) {
dates.add(divi.getDate());
}
return dates;
}
public ArrayList<LocalDate> getDividendDates() {
final var dates = new ArrayList<LocalDate>();
for (final AffineDividend divi : dividendStream) {
dates.add(divi.getDate());
}
return dates;
}

public double getDividend(
final LocalDate date,
final double stockPrice)
{
for (final AffineDividend divi : dividendStream)
{
if (divi.getDate() == date) {
return divi.getDividend(stockPrice);
}
}
return 0.0;
}
public double getDividend(final LocalDate date, final double stockPrice) {
for (final AffineDividend divi : dividendStream) {
if (divi.getDate() == date) {
return divi.getDividend(stockPrice);
}
}
return 0.0;
}

public double getProportionalDividendFactor(
final LocalDate date)
{
for (final AffineDividend divi : dividendStream)
{
if (divi.getDate() == date) {
return divi.getProportionalDividendFactor();
}
}
return 1.0;
}
public double getProportionalDividendFactor(final LocalDate date) {
for (final AffineDividend divi : dividendStream) {
if (divi.getDate() == date) {
return divi.getProportionalDividendFactor();
}
}
return 1.0;
}

public double getCashDividend(
final LocalDate date)
{
for (final AffineDividend divi : dividendStream)
{
if (divi.getDate() == date) {
return divi.getCashDividend();
}
}
return 0.0;
}
public double getCashDividend(final LocalDate date) {
for (final AffineDividend divi : dividendStream) {
if (divi.getDate() == date) {
return divi.getCashDividend();
}
}
return 0.0;
}

public static AffineDividendStream getAffineDividendsFromCashDividends(
AffineDividendStream cashDividends,
HashMap<LocalDate, Double> transformationFactors,
LocalDate valDate,
double spot,
FlatYieldCurve repoCurve)
{
// This method takes a stream of cash dividends and converts them to affine dividends,
// by transforming a part of each cash dividend to a proportional dividend.
// The percentage of each cash dividend to be transformed to a proportional dividend
// is specified in the member propDividendFactor of the dividend.
// The transformation is done in an arbitrage-free way, i.e. the forward structure is preserved.
// This method is usefull in practice, where traders use dividend futures as input, and transform
// a part to a proportional dividend (the further away the dividend, the higher the proportional part
// and the lower the cash part.
public static AffineDividendStream getAffineDividendsFromCashDividends(AffineDividendStream cashDividends,
HashMap<LocalDate, Double> transformationFactors, LocalDate valDate, double spot, YieldCurve repoCurve) {
// This method takes a stream of cash dividends and converts them to affine dividends,
// by transforming a part of each cash dividend to a proportional dividend.
// The percentage of each cash dividend to be transformed to a proportional dividend
// is specified in the member propDividendFactor of the dividend.
// The transformation is done in an arbitrage-free way, i.e. the forward structure is preserved.
// This method is usefull in practice, where traders use dividend futures as input, and transform
// a part to a proportional dividend (the further away the dividend, the higher the proportional part
// and the lower the cash part.

final var dates = cashDividends.getDividendDates();
final var dates = cashDividends.getDividendDates();

final var affineDividends = new ArrayList<AffineDividend>();
final var affineDividends = new ArrayList<AffineDividend>();

for (final var date : dates)
{
if (date.isBefore(valDate)) {
continue;
}
assert cashDividends.getProportionalDividendFactor(date) == 0.0 :
"Proportional dividend different from zero for date " + date;
final var cashDividend = cashDividends.getCashDividend(date);
var fwd = spot;
for (final var otherDate : dates)
{
if (otherDate.isBefore(date) && !otherDate.isBefore(valDate)) {
fwd -= cashDividends.getCashDividend(otherDate)
* repoCurve.getForwardDiscountFactor(valDate, otherDate);
}
}
final var q = transformationFactors.get(date) * cashDividend
* repoCurve.getForwardDiscountFactor(valDate, date)
/ fwd;
affineDividends.add(
new AffineDividend(
date,
(1.0 - transformationFactors.get(date)) * cashDividend,
q));
}
for (final var date : dates) {
if (date.isBefore(valDate)) {
continue;
}
assert cashDividends.getProportionalDividendFactor(
date) == 0.0 : "Proportional dividend different from zero for date " + date;
final var cashDividend = cashDividends.getCashDividend(date);
var fwd = spot;
for (final var otherDate : dates) {
if (otherDate.isBefore(date) && !otherDate.isBefore(valDate)) {
fwd -= cashDividends.getCashDividend(otherDate)
* repoCurve.getForwardDiscountFactor(valDate, otherDate);
}
}
final var q = transformationFactors.get(date) * cashDividend
* repoCurve.getForwardDiscountFactor(valDate, date) / fwd;
affineDividends.add(new AffineDividend(date, (1.0 - transformationFactors.get(date)) * cashDividend, q));
}

return new AffineDividendStream(affineDividends.toArray(new AffineDividend[0]));
}
return new AffineDividendStream(affineDividends.toArray(new AffineDividend[0]));
}
}
63 changes: 10 additions & 53 deletions src/main/java/net/finmath/equities/marketdata/FlatYieldCurve.java
Original file line number Diff line number Diff line change
@@ -1,69 +1,26 @@
package net.finmath.equities.marketdata;

import java.time.LocalDate;

import net.finmath.time.daycount.DayCountConvention;

/**
* Class to provide methods of a flat yield curve.
* TODO This class should be integrated into or replaced by finmat-lib's curve universe.
*
* @author Andreas Grotz
*/

public class FlatYieldCurve {
private final LocalDate curveDate;
private final double rate;
private final DayCountConvention dayCounter;

public FlatYieldCurve(
final LocalDate curveDate,
final double rate,
final DayCountConvention dayCounter)
{
this.curveDate = curveDate;
this.rate = rate;
this.dayCounter = dayCounter;
}

public FlatYieldCurve rollToDate(LocalDate date)
{
return new FlatYieldCurve(date, rate, dayCounter);
}

public double getRate(double maturity)
{
assert maturity >= 0.0 : "maturity must be positive";
return rate;
}

public double getRate(LocalDate date)
{
return getRate(dayCounter.getDaycountFraction(curveDate, date));
}
public class FlatYieldCurve extends YieldCurve {

public double getDiscountFactor(double maturity)
{
assert maturity >= 0.0 : "maturity must be positive";
return Math.exp(-maturity * rate);
}
private final static int longTime = 100;

public double getForwardDiscountFactor(double start, double expiry)
{
assert start >= 0.0 : "start must be positive";
assert expiry >= start : "start must be before expiry";
return getDiscountFactor(expiry) / getDiscountFactor(start);
}
public FlatYieldCurve(final LocalDate curveDate, final double rate, final DayCountConvention dayCounter) {
super("NONE", curveDate, dayCounter, new LocalDate[] { curveDate.plusYears(longTime) }, new double[] {
Math.exp(-rate * dayCounter.getDaycountFraction(curveDate, curveDate.plusYears(longTime))) });
}

public double getDiscountFactor(LocalDate date)
{
return getDiscountFactor(dayCounter.getDaycountFraction(curveDate, date));
}
public FlatYieldCurve rollToDate(LocalDate date) {
assert date.isAfter(baseCurve.getReferenceDate()) : "can only roll to future dates";
return new FlatYieldCurve(date, getRate(baseCurve.getReferenceDate().plusYears(longTime)), dayCounter);
}

public double getForwardDiscountFactor(LocalDate startDate, LocalDate endDate)
{
assert !startDate.isBefore(curveDate) : "start date must be after curve date";
assert !endDate.isBefore(startDate) : "end date must be after start date";
return getDiscountFactor(endDate) / getDiscountFactor(startDate);
}
}
81 changes: 81 additions & 0 deletions src/main/java/net/finmath/equities/marketdata/YieldCurve.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package net.finmath.equities.marketdata;

import java.time.LocalDate;
import java.util.Arrays;
import net.finmath.marketdata.model.curves.CurveInterpolation.ExtrapolationMethod;
import net.finmath.marketdata.model.curves.CurveInterpolation.InterpolationEntity;
import net.finmath.marketdata.model.curves.CurveInterpolation.InterpolationMethod;
import net.finmath.marketdata.model.curves.DiscountCurveInterpolation;
import net.finmath.time.daycount.DayCountConvention;

/**
* Class to provide methods of a yield curve.
*
* @author Andreas Grotz
*/

public class YieldCurve {

protected final LocalDate referenceDate;
protected final LocalDate[] discountDates;
protected final DayCountConvention dayCounter;
protected final DiscountCurveInterpolation baseCurve;

public YieldCurve(final String name, final LocalDate referenceDate, final DayCountConvention dayCounter,
final LocalDate[] discountDates, final double[] discountFactors) {
this.dayCounter = dayCounter;
this.discountDates = discountDates;
double[] times = new double[discountDates.length];
boolean[] isParameter = new boolean[discountDates.length];
for (int i = 0; i < times.length; i++) {
times[i] = dayCounter.getDaycountFraction(referenceDate, discountDates[i]);
}
baseCurve = DiscountCurveInterpolation.createDiscountCurveFromDiscountFactors(name, referenceDate, times,
discountFactors, isParameter, InterpolationMethod.LINEAR, ExtrapolationMethod.CONSTANT,
InterpolationEntity.LOG_OF_VALUE_PER_TIME);

this.referenceDate = referenceDate;
}

public YieldCurve rollToDate(LocalDate date) {
assert date.isAfter(referenceDate) : "can only roll to future dates";
LocalDate[] rolledDiscountDates = Arrays.stream(discountDates).filter(p -> p.isAfter(date))
.toArray(LocalDate[]::new);
double[] rolledDiscountFactors = new double[rolledDiscountDates.length];
for (int i = 0; i < rolledDiscountDates.length; i++) {
rolledDiscountFactors[i] = getForwardDiscountFactor(date, rolledDiscountDates[i]);
}

return new YieldCurve(baseCurve.getName(), date, dayCounter, rolledDiscountDates, rolledDiscountFactors);
}

public double getRate(double maturity) {
assert maturity >= 0.0 : "maturity must be positive";
return baseCurve.getZeroRate(maturity);
}

public double getRate(LocalDate date) {
return baseCurve.getZeroRate(dayCounter.getDaycountFraction(referenceDate, date));
}

public double getDiscountFactor(double maturity) {
assert maturity >= 0.0 : "maturity must be positive";
return baseCurve.getDiscountFactor(maturity);
}

public double getForwardDiscountFactor(double start, double expiry) {
assert start >= 0.0 : "start must be positive";
assert expiry >= start : "start must be before expiry";
return getDiscountFactor(expiry) / getDiscountFactor(start);
}

public double getDiscountFactor(LocalDate date) {
return baseCurve.getDiscountFactor(dayCounter.getDaycountFraction(referenceDate, date));
}

public double getForwardDiscountFactor(LocalDate startDate, LocalDate endDate) {
assert !startDate.isBefore(referenceDate) : "start date must be after curve date";
assert !endDate.isBefore(startDate) : "end date must be after start date";
return getDiscountFactor(endDate) / getDiscountFactor(startDate);
}
}
Loading

0 comments on commit af5fb87

Please sign in to comment.