Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,86 +11,98 @@
*
* @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]);
}

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 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 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 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));
}

return new AffineDividendStream(affineDividends.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 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 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,
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 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));
}

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

import java.time.LocalDate;

import net.finmath.time.daycount.DayCountConvention;

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

public class FlatYieldCurve extends YieldCurve {

private final static int longTime = 100;
private static final int LONG_TIME = 100;

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 FlatYieldCurve(
final LocalDate curveDate,
final double rate,
final DayCountConvention dayCounter) {

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);
}
super(
"NONE",
curveDate,
dayCounter,
new LocalDate[] { curveDate.plusYears(LONG_TIME) },
new double[] {
Math.exp(
-rate * dayCounter.getDaycountFraction(
curveDate,
curveDate.plusYears(LONG_TIME)))
});
}

public FlatYieldCurve rollToDate(LocalDate date) {
assert date.isAfter(baseCurve.getReferenceDate()) : "can only roll to future dates";
return new FlatYieldCurve(
date,
getRate(baseCurve.getReferenceDate().plusYears(LONG_TIME)),
dayCounter);
}
}
142 changes: 79 additions & 63 deletions src/main/java/net/finmath/equities/marketdata/YieldCurve.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,69 +14,85 @@
*
* @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);
}
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