I am modeling a ship that has contracts. Each contract can either be a fixed rate contract which pays a set fixed amount every day or a market index contract which pays an amount that varies based on a market index.
I hope to run simulations on each ship where I would provide required information on what the market index is in the future to calculate the earnings from each contract.
I had approached this as follows using rust
where a ship has a vector of ContractLike
contracts:
struct Ship {
pub name: String,
pub contracts: Vec<Box<dyn ContractLike>>,
}
pub struct FixedContract {
dayrate: i32,
start_date: NaiveDate,
end_date: NaiveDate,
}
pub struct MarketIndexContract {
// no fixed dayrate since function of index price which is not known
// should I store the MarketIndex data here?
start_date: NaiveDate,
end_date: NaiveDate,
}
Both contract types however are ContractLike
in that they implement the trait ContractedRevenues
which tells you how much money is due under the contract between 2 dates (I return it as a vector of ints for each day):
pub trait ContractLike: ContractedRevenues{}
impl<T: ContractedRevenues> ContractLike for T {}
pub trait ContractedRevenues {
fn contracted_revenues(&self, date1: NaiveDate, date2: NaiveDate) -> Vec<i32>;
}
The problem is that MarketIndexContract
needs more than just date1
and date2
to compute the ContractedRevenues
.
I was thinking I should store the future Market Index values in the MarketIndexContract
struct by changing the MarketIndexContract
constructor. Then when I ran each simulation I would re-construct the MarketIndexContracts
structs with new future index prices and the ContractedRevenues
trait implementation for MarketIndexContracts
could get the data from the struct's data member.
The downside, however, is it seems to make the Ship
dependent on a simulation existing to construct it. There are uses where I want to use my Ship
without there being a simulation. For instance, what if I wanted to query my Ship
for how many contracts it has? That use case doesn't depend on a simulation existing (i.e. the future index prices being known).
How best should I approach this design?
1 Answer 1
Unless I misunderstood, your app is supposed to answer the following question:
Given a set of contractual agreements C, some of which depend on external market data, what would happen in scenarios S1...Sn, where each scenario simulates a different market environment.
Contract information are part of C, i.e. they don't change between scenarios. Market data is specific to a scenario Si. Therefore, they should be kept separate.
At some point, you need to loop over all contracts, regardless of their type, to calculate some value. You need to bring along the market data, in case you need it.
One option is to keep the ContractedValues
abstraction, and a separate type for each contract that implements this abstraction. In this case, you add a third parameter to your contracted_revenues
method, which the FixedContract
simply ignores. This feels kind of OOP-inspired, and is needlessly complex, in my opinion.
By the way: overloading the method with a different number of parameters does not help - how would you know which one to call?
Instead, I would propose to encode the different pricing models directly:
#[derive(...)]
enum PricingModel {
FixedRate(i32),
IndexBased,
}
#[derive(...)]
pub struct Contract {
pricing_model: PricingModel,
active_period: TimePeriod,
}
impl Contract {
pub fn fixed_rate(rate: i32) -> Self { ... }
pub fn index_based() -> Self { ... }
pub fn contracted_revenues(&self, period: TimePeriod, market_data: &MarketData)-> Vec<i32> {
match self.pricing_model {
...
}
}
}
Yes, this is less open to new pricing models - especially by third parties - but those would likely also require additional outside information beside the market data, i.e. it's unlikely they'd fit into your model anyways. It's possible to make that work as well, but everything becomes much more complex.
-
Could you elaborate on how you what type of traits you see being derived in your use of
#[derive(....)]
above?cpage– cpage2021年05月18日 19:41:29 +00:00Commented May 18, 2021 at 19:41 -
1Nothing in particular (Debug, Eq, etc. depending on what you need). They have no relevance for the answer, it's just a habit.doubleYou– doubleYou2021年05月27日 16:26:28 +00:00Commented May 27, 2021 at 16:26
Explore related questions
See similar questions with these tags.
contracted_revenues()
function? (2) Are all types of contracts known upfront? Consider using an enum instead of aBox<dyn ...>
object. Since enums allow access to fields of their variants, you have far more flexibility and are not constrained to the trait's methods.contracted_revenues()
but wasn't sure how to make a trait that allows the parameters to change inrust
. (2) They are not all known but would be acceptable to lock in to these types. Could you elaborate on how to use an enum here? Assuming you instead have 1Contract
type that accepts an enum in constructor, how would you be able to accept different arguments forcontracted_revenues
?