-
Notifications
You must be signed in to change notification settings - Fork 29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Generated code for functions to all follow same structure #43
Comments
Reopening this issue as the structure and API of the generated java code for Functions is not consistent. Generated java code for EquityResetEvent follows the new structure: /**
* Function specification for resetting an equity payout following an equity price observation, which means calculating the equity performance and resetting the equity notional.
*/
@ImplementedBy(EquityResetEventImpl.class)
public abstract class EquityResetEvent implements RosettaFunction {
// RosettaFunction dependencies
//
@Inject protected EquityReset equityReset;
/**
* @param contract
* @param observation
* @return reset
*/
public Event evaluate(Contract contract, Event observation) {
// pre-conditions
//
assert
exists(MapperS.of(contract).<ContractualProduct>map("getContractualProduct", Contract::getContractualProduct).<EconomicTerms>map("getEconomicTerms", ContractualProduct::getEconomicTerms).<Payout>map("getPayout", EconomicTerms::getPayout).<EquityPayout>mapC("getEquityPayout", Payout::getEquityPayout), false).get() &&
exists(MapperS.of(observation).<PrimitiveEvent>map("getPrimitive", Event::getPrimitive).<ObservationPrimitive>mapC("getObservation", PrimitiveEvent::getObservation), false).get()
: "There must be an equity payout on the contract, and the event input must contain an observation.";
assert
areEqual(MapperS.of(observation).<PrimitiveEvent>map("getPrimitive", Event::getPrimitive).<ObservationPrimitive>mapC("getObservation", PrimitiveEvent::getObservation).<BigDecimal>map("getObservation", ObservationPrimitive::getObservation), MapperS.of(BigDecimal.valueOf(3.0))).get()
: "";
// Delegate to implementation
//
Event reset = doEvaluate(contract, observation);
// post-conditions
//
assert
areEqual(MapperS.of(reset).<PrimitiveEvent>map("getPrimitive", Event::getPrimitive).<ResetPrimitive>mapC("getReset", PrimitiveEvent::getReset), MapperS.of(equityReset.calculate(MapperS.of(contract).<ContractualProduct>map("getContractualProduct", Contract::getContractualProduct).<EconomicTerms>map("getEconomicTerms", ContractualProduct::getEconomicTerms).<Payout>map("getPayout", EconomicTerms::getPayout).<EquityPayout>mapC("getEquityPayout", Payout::getEquityPayout).get(), MapperS.of(observation).<PrimitiveEvent>map("getPrimitive", Event::getPrimitive).<ObservationPrimitive>mapC("getObservation", PrimitiveEvent::getObservation).<BigDecimal>map("getObservation", ObservationPrimitive::getObservation).get(), MapperS.of(observation).<PrimitiveEvent>map("getPrimitive", Event::getPrimitive).<ObservationPrimitive>mapC("getObservation", PrimitiveEvent::getObservation).<Date>map("getDate", ObservationPrimitive::getDate).get()))).get() &&
exists(MapperS.of(reset).<PrimitiveEvent>map("getPrimitive", Event::getPrimitive).<ResetPrimitive>mapC("getReset", PrimitiveEvent::getReset), true).get()
: "Event must contain a reset primitive that is calculated based on the observation, and no other primitive.";
assert
areEqual(MapperS.of(reset).<EventEffect>map("getEventEffect", Event::getEventEffect).mapC("getEffectedContract", EventEffect::getEffectedContract).<Contract>map("getValue", FieldWithMeta::getValue), MapperS.of(contract)).get() &&
areEqual(MapperS.of(reset).<EventEffect>map("getEventEffect", Event::getEventEffect).mapC("getContract", EventEffect::getContract).<Contract>map("getValue", FieldWithMeta::getValue), MapperS.of(contract)).get() &&
notExists(MapperS.of(reset).<EventEffect>map("getEventEffect", Event::getEventEffect).mapC("getEffectedExecution", EventEffect::getEffectedExecution).<Execution>map("getValue", FieldWithMeta::getValue)).get() &&
notExists(MapperS.of(reset).<EventEffect>map("getEventEffect", Event::getEventEffect).mapC("getExecution", EventEffect::getExecution).<Execution>map("getValue", FieldWithMeta::getValue)).get() &&
notExists(MapperS.of(reset).<EventEffect>map("getEventEffect", Event::getEventEffect).mapC("getProductIdentifier", EventEffect::getProductIdentifier).<ProductIdentifier>map("getValue", FieldWithMeta::getValue)).get()
: "Event effect must include the notional reset on the contract.";
assert
areEqual(MapperS.of(reset).<Lineage>map("getLineage", Event::getLineage).mapC("getContractReference", Lineage::getContractReference).<Contract>map("getValue", FieldWithMeta::getValue), MapperS.of(contract)).get() &&
areEqual(MapperS.of(reset).<Lineage>map("getLineage", Event::getLineage).mapC("getEventReference", Lineage::getEventReference).<Event>map("getValue", FieldWithMeta::getValue), MapperS.of(observation)).get()
: "Event lineage must point to the contract being reset and the observation event.";
return reset;
}
protected abstract Event doEvaluate(Contract contract, Event observation);
} Generated code for EquityReset still on old structure public class EquityReset {
private final GetBusinessDateSpec getBusinessDateSpec;
private final EquityCalculationPeriod equityCalculationPeriod;
private final ResolvePrice resolvePrice;
private final RateOfReturn rateOfReturn;
private final EquityNotionalAmount equityNotionalAmount;
private final EquityPerformance equityPerformance;
private final Abs abs;
private final EquityCashSettlementAmount equityCashSettlementAmount;
public EquityReset(GetBusinessDateSpec getBusinessDateSpec, EquityCalculationPeriod equityCalculationPeriod, ResolvePrice resolvePrice, RateOfReturn rateOfReturn, EquityNotionalAmount equityNotionalAmount, EquityPerformance equityPerformance, Abs abs, EquityCashSettlementAmount equityCashSettlementAmount) {
this.getBusinessDateSpec = getBusinessDateSpec;
this.equityCalculationPeriod = equityCalculationPeriod;
this.resolvePrice = resolvePrice;
this.rateOfReturn = rateOfReturn;
this.equityNotionalAmount = equityNotionalAmount;
this.equityPerformance = equityPerformance;
this.abs = abs;
this.equityCashSettlementAmount = equityCashSettlementAmount;
}
public CalculationResult calculate(EquityPayout equityPayout, BigDecimal observation, Date date) {
CalculationInput input = new CalculationInput().create(equityPayout, observation, date, getBusinessDateSpec, equityCalculationPeriod, resolvePrice, rateOfReturn, equityNotionalAmount, equityPerformance, abs, equityCashSettlementAmount);
CalculationResult result = new CalculationResult(input);
if(result.reset == null) result.reset = ResetPrimitive.builder().build();
ResetPrimitive.ResetPrimitiveBuilder __builder = result.reset.toBuilder();
__builder.getOrCreateAfter().getOrCreateUpdatedContract().getOrCreateContractualProduct().getOrCreateEconomicTerms().getOrCreatePayout().getOrCreateEquityPayout(0).setRateOfReturn(rateOfReturn.calculate(equityPayout).getRateOfReturn());
result.reset = __builder.build();
__builder.getOrCreateAfter().getOrCreateUpdatedContract().getOrCreateContractualProduct().getOrCreateEconomicTerms().getOrCreatePayout().getOrCreateEquityPayout(0).setPerformance(equityPerformance.calculate(equityPayout).getEquityPerformance());
result.reset = __builder.build();
__builder.getOrCreateAfter().getOrCreateUpdatedContract().getOrCreateContractualProduct().getOrCreateEconomicTerms().getOrCreatePayout().getOrCreateEquityPayout(0).setEquityCashSettlementAmount(equityCashSettlementAmount.calculate(equityPayout).getEquityCashSettlementAmount());
result.reset = __builder.build();
__builder.getOrCreateBefore().getOrCreateUpdatedContract().getOrCreateContractualProduct().getOrCreateEconomicTerms().getOrCreatePayout().addEquityPayout(equityPayout);
result.reset = __builder.build();
return result;
}
public static class CalculationInput implements ICalculationInput {
private CalculationInput input = this; // For when arguments need to reference other arguments
private EquityPayout equityPayout;
private BigDecimal observation;
private Date date;
public CalculationInput create(EquityPayout equityPayout, BigDecimal observation, Date date, GetBusinessDateSpec getBusinessDateSpec, EquityCalculationPeriod equityCalculationPeriod, ResolvePrice resolvePrice, RateOfReturn rateOfReturn, EquityNotionalAmount equityNotionalAmount, EquityPerformance equityPerformance, Abs abs, EquityCashSettlementAmount equityCashSettlementAmount) {
this.equityPayout = equityPayout;
this.observation = observation;
this.date = date;
return this;
}
@Override
public List<Formula> getFormulas() {
return Arrays.asList(
new Formula("EquityReset", "RateOfReturn( equityPayout )", this),
new Formula("EquityReset", "EquityPerformance( equityPayout )", this),
new Formula("EquityReset", "EquityCashSettlementAmount( equityPayout )", this),
new Formula("EquityReset", "equityPayout", this));
}
public EquityPayout getEquityPayout() {
return equityPayout;
}
public BigDecimal getObservation() {
return observation;
}
public Date getDate() {
return date;
}
private static final List<Attribute<?>> ATTRIBUTES = Arrays.asList(
);
@Override
public List<Attribute<?>> getAttributes() {
return ATTRIBUTES;
}
}
public static class CalculationResult implements ICalculationResult {
private CalculationInput calculationInput;
private ResetPrimitive reset;
public CalculationResult(CalculationInput calculationInput) {
this.calculationInput = calculationInput;
}
public ResetPrimitive getReset() {
return this.reset;
}
public CalculationResult setReset(ResetPrimitive reset) {
this.reset = reset;
return this;
}
@Override
public CalculationInput getCalculationInput() {
return calculationInput;
}
private static final List<Attribute<?>> ATTRIBUTES = Arrays.asList(
new Attribute<>("reset", ResetPrimitive.class, (IResult res) -> ((CalculationResult) res).getReset())
);
@Override
public List<Attribute<?>> getAttributes() {
return ATTRIBUTES;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CalculationResult _that = (CalculationResult) o;
if (reset != null ? !reset.equals(_that.reset) : _that.reset != null) return false;
return true;
}
@Override
public int hashCode() {
int _result = 0;
_result = 31 * _result + (reset != null ? reset.hashCode() : 0);
return _result;
}
@Override
public String toString() {
return "CalculationResult {" +
"reset=" + this.reset +
'}';
}
}
} Desired generated code structure for EquityReset: /**
* Function specification for resetting an equity payout following an equity price observation. This function only concerns itself with building the primitive, which currently does not affect the underlying contract (until such time when 'ResetPrimitive' is refactored to directly accomodate a 'before' and 'after' states). The contract effect will be part of the 'EventEffect' attribute on the a fully-formed Business Event that is built by the 'EquityResetEvent' function spec.
*/
@ImplementedBy(EquityResetNewImpl.class)
public abstract class EquityReset implements RosettaFunction {
// RosettaFunction dependencies
//
@Inject protected CalculationPeriod calculationPeriod;
@Inject protected RateOfReturn rateOfReturn;
@Inject protected EquityPerformance equityPerformance;
@Inject protected EquityCashSettlementAmount equityCashSettlementAmount;
/**
* @param equityPayout
* @param number
* @return date
*/
public ResetPrimitive evaluate(EquityPayout equityPayout, BigDecimal number, Date date) {
// pre-conditions
//
assert
areEqual(MapperS.of(date), MapperS.of(calculationPeriod.evaluate(MapperS.of(equityPayout).<CalculationPeriodDates>map("getCalculationPeriodDates", EquityPayout::getCalculationPeriodDates).get()).getEndDate())).get()
: "The reset date must be the period end date on the equity payout.";
// Delegate to implementation
//
ResetPrimitiveBuilder resetBuilder = doEvaluate(equityPayout, number, date);
// Assign values to the output
//
resetBuilder.getOrCreateAfter().getOrCreateUpdatedContract().getOrCreateContractualProduct().getOrCreateEconomicTerms().getOrCreatePayout().getOrCreateEquityPayout(0).setRateOfReturn(rateOfReturn.calculate(equityPayout).getRateOfReturn());
resetBuilder.getOrCreateAfter().getOrCreateUpdatedContract().getOrCreateContractualProduct().getOrCreateEconomicTerms().getOrCreatePayout().getOrCreateEquityPayout(0).setPerformance(equityPerformance.calculate(equityPayout).getEquityPerformance());
resetBuilder.getOrCreateAfter().getOrCreateUpdatedContract().getOrCreateContractualProduct().getOrCreateEconomicTerms().getOrCreatePayout().getOrCreateEquityPayout(0).setEquityCashSettlementAmount(equityCashSettlementAmount.calculate(equityPayout).getEquityCashSettlementAmount());
resetBuilder.getOrCreateBefore().getOrCreateUpdatedContract().getOrCreateContractualProduct().getOrCreateEconomicTerms().getOrCreatePayout().addEquityPayout(equityPayout);
ResetPrimitive reset = resetBuilder.build();
return reset;
}
protected abstract ResetPrimitiveBuilder doEvaluate(EquityPayout equityPayout, BigDecimal number, Date date);
} There will be a number of downstream clients that will require updating. |
@jim-h-wang Should we also remove |
@jim-h-wang And don't forget the daycountfraction enum function. |
Yes we should |
A model example of a function that makes use of alias' on inputs and outputs, conditions, post-conditions and has multiple assignments func EquityReset: <"Function specification for resetting an equity payout following an equity price observation. This function only concerns itself with building the primitive, which currently does not affect the underlying contract (until such time when 'ResetPrimitive' is refactored to directly accomodate a 'before' and 'after' states). The contract effect will be part of the 'EventEffect' attribute on the a fully-formed Business Event that is built by the 'EquityResetEvent' function spec.">
inputs:
equityPayout EquityPayout (1..1)
// source ObservationSource (1..1)
observation number (1..1)
date date (1..1)
output:
reset ResetPrimitive (1..1)
alias effectiveDate :
equityPayout -> calculationPeriodDates -> effectiveDate -> adjustableDate -> adjustedDate
alias stepSchedule :
equityPayout -> payoutQuantity -> quantitySchedule -> stepSchedule
alias scheduleDates :
stepSchedule -> step -> stepDate
// alias on the output
alias EquityPayout_after: <"TODO: should be usable in assign-output blocks.">
reset -> after -> updatedContract -> contractualProduct -> economicTerms -> payout -> equityPayout only-element
alias EquityPayout_before: <"TODO: should be usable in assign-output blocks.">
reset -> before -> updatedContract -> contractualProduct -> economicTerms -> payout -> equityPayout only-element
condition date_is_period_end_date :
<"The reset date must be the period end date on the equity payout.">
date = CalculationPeriod( equityPayout -> calculationPeriodDates ) -> endDate;
condition : <"effective date should be in the past.">
if effectiveDate exists then effectiveDate < date;
condition : <"A step schedule needs to have more than 1 date.">
if stepSchedule exists then scheduleDates count > 1;
// TODO: use alias EquityPayout_before when feature is available
assign-output reset -> before -> updatedContract -> contractualProduct -> economicTerms -> payout -> equityPayout :
<"Reset primitive before state must be correctly populated with the equity payout that is being reset.">
equityPayout
// TODO: use alias EquityPayout_after when feature is available
assign-output reset -> after -> updatedContract -> contractualProduct -> economicTerms -> payout -> equityPayout -> rateOfReturn :
<"Reset primitive after state must be correctly populated with the equity payout including the rate of return.">
RateOfReturn( equityPayout )
// TODO: use alias EquityPayout_after when feature is available
assign-output reset -> after -> updatedContract -> contractualProduct -> economicTerms -> payout -> equityPayout -> performance :
<"Reset primitive after state must be correctly populated with the equity payout including the performance.">
EquityPerformance( equityPayout )
// TODO: use alias EquityPayout_after when feature is available
assign-output reset -> after -> updatedContract -> contractualProduct -> economicTerms -> payout -> equityPayout -> equityCashSettlementAmount :
<"Reset primitive after state must be correctly populated with the equity payout including the cash settlement amount.">
EquityCashSettlementAmount( equityPayout )
post-condition : <"updatedContract values between before and after should be different.">
reset -> after -> updatedContract <> reset -> before -> updatedContract; And what I think should get generated (which is yet untested). A point to note is that for generated code will be different between alias on inputs vs alias on the output. /**
* Function specification for resetting an equity payout following an equity price observation. This function only concerns itself with building the primitive, which currently does not affect the underlying contract (until such time when 'ResetPrimitive' is refactored to directly accomodate a 'before' and 'after' states). The contract effect will be part of the 'EventEffect' attribute on the a fully-formed Business Event that is built by the 'EquityResetEvent' function spec.
*/
//@ImplementedBy(EquityResetNewImpl.class)
public abstract class EquityResetNew implements RosettaFunction {
// RosettaFunction dependencies
//
@Inject protected CalculationPeriod calculationPeriod;
@Inject protected RateOfReturn rateOfReturn;
@Inject protected EquityPerformance equityPerformance;
@Inject protected EquityCashSettlementAmount equityCashSettlementAmount;
/**
* @param equityPayout
* @param number
* @return date
*/
public ResetPrimitive evaluate(EquityPayout equityPayout, BigDecimal number, Date date) {
// Define alias on inputs
//
MapperBuilder<Date> aliasEffectiveDate = aliasEffectiveDate(equityPayout);
MapperBuilder<NonNegativeStepSchedule> aliasStepSchedule = aliasStepSchedules(equityPayout);
MapperBuilder<Date> aliasScheduleDates = aliasScheduleDates(equityPayout);
// pre-conditions
//
assert
areEqual(MapperS.of(date), MapperS.of(calculationPeriod.evaluate(MapperS.of(equityPayout).<CalculationPeriodDates>map("getCalculationPeriodDates", EquityPayout::getCalculationPeriodDates).get()).getEndDate())).get()
: "The reset date must be the period end date on the equity payout.";
assert
doIf(exists(aliasEffectiveDate, false), lessThan(aliasEffectiveDate, MapperS.of(date)), MapperS.of(Boolean.TRUE)).get()
: "effective date should be in the past.";
assert
doIf(exists(aliasStepSchedule, false), greaterThan(MapperS.of(aliasScheduleDates.resultCount()), MapperS.of(1)), MapperS.of(Boolean.TRUE)).get()
: "A step schedule needs to have more than 1 date.";
// Delegate to implementation
//
ResetPrimitiveBuilder resetPrimitiveBuilder = doEvaluate(equityPayout, number, date);
// Assign values to the output
//
aliasEquityPayoutAfter(resetPrimitiveBuilder)
.setRateOfReturn(rateOfReturn.calculate(equityPayout).getRateOfReturn());
aliasEquityPayoutAfter(resetPrimitiveBuilder)
.setPerformance(equityPerformance.calculate(equityPayout).getEquityPerformance());
aliasEquityPayoutAfter(resetPrimitiveBuilder)
.setEquityCashSettlementAmount(equityCashSettlementAmount.calculate(equityPayout).getEquityCashSettlementAmount());
aliasPayoutBefore(resetPrimitiveBuilder)
.addEquityPayout(equityPayout);
// post-conditions
//
assert
notEqual(MapperS.of(resetPrimitiveBuilder).<ContractStateBuilder>map("getAfter", ResetPrimitiveBuilder::getAfter).<ContractBuilder>map("getUpdatedContract", ContractStateBuilder::getUpdatedContract), MapperS.of(resetPrimitiveBuilder).<ContractStateBuilder>map("getBefore", ResetPrimitiveBuilder::getBefore).<ContractBuilder>map("getUpdatedContract", ContractStateBuilder::getUpdatedContract)).get()
: "updatedContract values between before and after should be different.";
// Build and return the output object
//
return resetPrimitiveBuilder.build();
}
protected abstract ResetPrimitiveBuilder doEvaluate(EquityPayout equityPayout, BigDecimal number, Date date);
private EquityPayoutBuilder aliasEquityPayoutAfter(ResetPrimitiveBuilder resetPrimitive) {
return resetPrimitive.getOrCreateAfter().getOrCreateUpdatedContract().getOrCreateContractualProduct().getOrCreateEconomicTerms().getOrCreatePayout().getOrCreateEquityPayout(0);
}
private PayoutBuilder aliasPayoutBefore(ResetPrimitiveBuilder resetPrimitive) {
return resetPrimitive.getOrCreateBefore().getOrCreateUpdatedContract().getOrCreateContractualProduct().getOrCreateEconomicTerms().getOrCreatePayout();
}
private MapperBuilder<Date> aliasEffectiveDate(EquityPayout equityPayout) {
return MapperS.of(equityPayout)
.<CalculationPeriodDates>map("getCalculationPeriodDates", EquityPayout::getCalculationPeriodDates)
.<AdjustableOrRelativeDate>map("getAdjustableOrRelativeDate", CalculationPeriodDates::getEffectiveDate)
.<AdjustableDate>map("getAdjustableDate", AdjustableOrRelativeDate::getAdjustableDate)
.<FieldWithMetaDate>map("getAdjustedDate", AdjustableDate::getAdjustedDate)
.<Date>map("getValue", FieldWithMetaDate::getValue);
}
private MapperBuilder<NonNegativeStepSchedule> aliasStepSchedules(EquityPayout equityPayout) {
return MapperS.of(equityPayout)
.<ResolvablePayoutQuantity>map("getPayoutQuantity", EquityPayout::getPayoutQuantity)
.<NonNegativeQuantitySchedule>map("getQuantitySchedule", ResolvablePayoutQuantity::getQuantitySchedule)
.<NonNegativeStepSchedule>map("getStepSchedule", NonNegativeQuantitySchedule::getStepSchedule);
}
private MapperBuilder<Date> aliasScheduleDates(EquityPayout equityPayout) {
return aliasStepSchedules(equityPayout)
.<NonNegativeStep>mapC("getStep", NonNegativeStepSchedule::getStep)
.<Date>map("getDate", NonNegativeStep::getStepDate);
}
} |
* Allow alias in assignment #43 * Move calculation impl to the doEvaluate method #43 * Additiona FuncGenerator fixes. Adjusted test expectation #43 * Added type check for assign-output * Added Reference type support #43 * Allow usage of output in assign expression #43 * Added cardinality and name validation. Better type inference for if-else * Allow only-element for callable call * assignment of an alias #53 * Validate list element access #65
merged |
func RateOfReturn
utilises code generators that were used for (the now deprecated)calculation
s. We should aim to have a consistent structure and api for all generated code that relate to Rosetta Functions.Additionally, This causes incompatibility in the java code when new-style functions reference old-style functions. In the below example,
func EquityReset
was modified to callfunc RateOfReturn
, which yielded the below error in the generated code forEquityReset
.The text was updated successfully, but these errors were encountered: