Lamma I/O

Full Schedule Generation

All code used in this tutorial can be found here: Scala / Java

This tutorial is based on some of the very important concepts introduced in Quick Start, Basic Date Generation and Advanced Date Generation You are strongly recommended to finish them first if you haven't.

Sequence generation would be simple and convenient for most of the use case. But in real life, there are still some cases cannot be easily handled by date sequence generation. For example:

  • What if I want to keep the fraction part of a period?
    For example, generate a sequence from 2014-05-01 to 2014-05-20 every week will yield List(2014-05-01, 2014-05-08, 2014-05-15), and the last fraction period from 2014-05-16 to 2014-05-20 will be ignored.
  • What if I want to generate multiple date sequences, and one of them depends on others?
    For example, generate a sequence of payment date from 2014-05-31 to 2014-09-30 on every month end and another sequence of settlement date which will be 2 working days later than each payment date. The first sequence can be easily generated by what we learnt so far, but the second one will require some manual work with date object.

Instead of complicating sequence generation API for these more complicated use cases, Lamma provides a more powerful API Lamma.schedule to specifically solve this problem. Let's get started with an example:

First example: generate multiple dates with Schedule.apply

Generate coupon payment and settlement schedule from 2015-01-01 to 2016-12-31. Payment frequency is 6 months and settlement delay is 2 working days.

val dateDefs = List(
  DateDef("CouponDate", relativeTo = PeriodEnd, selector = ModifiedFollowing(Weekends)),
  DateDef("SettlementDate", relativeTo = OtherDate("CouponDate"), shifter = ShiftWorkingDays(2, Weekends))
)
Schedule(
  start = Date(2015, 1, 1),
  end = Date(2016, 12, 31),
  pattern = (6 months) on lastDay,
  dateDefs = dateDefs
)
DateDef couponDate = DateDefs.of("CouponDate", Anchors.periodEnd(), Selectors.modifiedFollowing(HolidayRules.weekends()));
DateDef settlementDate = DateDefs.of("SettlementDate", Anchors.otherDate("CouponDate"), Shifters.byWorkingDays(2, HolidayRules.weekends()));

Schedule4j result = Schedule4j.schedule(
  Dates.newDate(2015, 1, 1),
  Dates.newDate(2016, 12, 31),
  Patterns.monthly(6, DayOfMonths.lastDay()),
  Lists.newArrayList(couponDate, settlementDate)
);

Result: Lamma will first generate all the recurring periods, and then based on that generate the required two dates for each period.

From DateTo DateCouponDateSettlementDate
12015-01-012015-06-302015-06-302015-07-02
22015-07-012015-12-312015-12-312016-01-04
32016-01-012016-06-302016-06-302016-07-04
42016-07-012016-12-312016-12-302017-01-03

How does this example work?
  • Step 1: generate a date sequence of the ending dates of all recurring periods, ie, column To Date.
  • Step 2: based on the results in Step 1, Lamma will determine the starting date and construct each recurring period, ie, column From Date.
  • Step 3: generate CouponDate based on the periods generated in Step 2, ie, column CouponDate.
  • Step 4: generate SettlementDate based on the generated coupon dates in Step 3, ie, column SettlementDate.

What happened if last period is not a complete one? Is it ignored just like sequence?

Generate Coupon Dates from 2015-01-01 to 2017-01-31 every 6 months with Modified Following convention.

val dateDefs = DateDef("CouponDate", relativeTo = PeriodEnd, selector = ModifiedFollowing(Weekends)) :: Nil
Schedule(Date(2015, 1, 1), Date(2017, 1, 31), (6 months) on lastDay, dateDefs = dateDefs)
DateDef couponDate = DateDefs.of("CouponDate", Anchors.periodEnd(), Selectors.modifiedFollowing(HolidayRules.weekends()));
Schedule4j result = Schedule4j.schedule(
  Dates.newDate(2015, 1, 1),
  Dates.newDate(2017, 1, 31),
  Patterns.monthly(6, DayOfMonths.lastDay()),
  Lists.newArrayList(couponDate)
);

Result: unlike date sequence generation, Lamma creates a shorter period at the end.

From DateTo DateCouponDate
12015-01-012015-06-302015-06-30
22015-07-012015-12-312015-12-31
32016-01-012016-06-302016-06-30
42016-07-012016-12-312016-12-30
52017-01-012017-01-312017-01-31

How does this example work?

In this case, Step 2 is slightly different from the previous example.

  • Step 1: generate a date sequence of the ending dates of all the recurring period, ie, column To Date.
  • Step 2: based on the results in Step 1, Lamma will determine the starting date and construct each recurring period, ie, column From Date. The last row will be created as the extra period.
  • Step 3: generate CouponDate based on the periods generated in Step 2, ie, column CouponDate.
  • Step 4: generate SettlementDate based on the generated coupon dates in Step 3, ie, column SettlementDate.

Stub Rule

A new concept Stub Rule is used by Lamma to merge extra period at the beginning or the end of a schedule, aka start stub / end stub.

Example

Generate Coupon Dates from 2015-01-01 to 2017-01-31 every 6 months long end stub with Modified Following convention.

val dateDefs = DateDef("CouponDate", relativeTo = PeriodEnd, selector = ModifiedFollowing(Weekends)) :: Nil
Schedule(Date(2015, 1, 1), Date(2017, 1, 31), (6 months) on lastDay, StubRulePeriodBuilder(endRule = LongEnd(270)), dateDefs = dateDefs)
DateDef couponDate = DateDefs.of("CouponDate", Anchors.periodEnd(), Selectors.modifiedFollowing(HolidayRules.weekends()));

Schedule4j result = Schedule4j.schedule(
  new Date(2015, 1, 1),
  new Date(2017, 1, 31),
  Patterns.monthly(6, DayOfMonths.lastDay()),
  StubRulePeriodBuilders.of(StubRulePeriodBuilders.Rules.longEnd(270)),
  Lists.newArrayList(couponDate)
);

Result: LongEnd(270) means merging the end stub with the last complete period, if the merged period does not exceed 270 days. Thus the end stub is merged to become part of the last period.

From DateTo DateCouponDate
12015-01-012015-06-302015-06-30
22015-07-012015-12-312015-12-31
32016-01-012016-06-302016-06-30
42016-07-012017-01-312017-01-31

How does this example work?

In this case, Step 2 is again slightly different from previous example.

  • Step 1: generate the ending dates of all the recurring periods, ie, column To Date.
  • Step 2: construct periods from specified PeriodBuilder, ie, column From Date. In this case, StubRulePeriodBuilder(endRule = LongEnd(270)) is used, so that the end date is merged to the last complete period.
  • Step 3: generate CouponDate based on the periods generated in Step 2, ie, column CouponDate.
  • Step 4: generate SettlementDate based on the generated coupon dates in Step 3, ie, column SettlementDate.