Smart Curves
Introduction
Smart Curves are the easiest way of deriving new Forward Curves. They rely on having a real Forward Curve as a BASE in order to both determine when the curve has valuation dates and potentially to use in the Smart Curve expression.
In this tutorial, we show how to use Smart Curves writing ODSL code and using the REST API
Creating Basic Smart Curves
A Smart Curve can be created independently in ODSL code, but in order to save it to the database, you must add it to an Object.
The object can be a basic object or an object created from a Type
Using a basic object
A basic 'untyped' object is ok to use, but you maybe want to consider using either a public type or create your own private type.
In the example below, we are creating an object called CORN_TEST with a Smart Curve called CLOSE. The CLOSE curve is a backward filled CORN Forward Curve from MatbaRofex. We are also adding a 'category' property on the curve to easily filter and find it.
- OpenDataDSL
- REST API
// Creating using a basic object
CORN_TEST = Object()
CORN_TEST.CLOSE = SmartCurve("#MATBAROFEX.ROS.CORN.FUT:CLOSE", "bootstrapCurve(BASE)")
CORN_TEST.category = "TUTORIAL"
// Save to the database
save ${object: CORN_TEST}
POST https://api.opendatadsl.com/api/object/v1
Authorization: Bearer {{token}}
{
"_id":"CORN_TEST",
"_type":"#Object",
"category": "TUTORIAL",
"CLOSE":
{
"_id":"CLOSE",
"_type":"VarSmartCurve",
"baseCurve":"${data:'#MATBAROFEX.ROS.CORN.FUT:CLOSE'}",
"expression":"interpolate(BASE, 'BACKWARD')"
}
}
Creating using a type
This is the preferred method of creating objects, this categorises your objects correctly and implies a structure using default properties and dimensions of your object. You can read more about objects and types here.
- OpenDataDSL
- REST API
// Creating using a type
CORN_TEST_TYPE = object as #Agriculture
CLOSE = SmartCurve("#MATBAROFEX.ROS.CORN.FUT:CLOSE", "interpolate(BASE, 'BACKWARD')")
category = "TUTORIAL"
end
// Save to the database
save ${object: CORN_TEST_TYPE}
POST https://api.opendatadsl.com/api/object/v1
Authorization: Bearer {{token}}
{
"_id":"CORN_TEST",
"_type":"#Agriculture",
"category": "TUTORIAL",
"CLOSE":
{
"_id":"CLOSE",
"_type":"VarSmartCurve",
"baseCurve":"${data:'#MATBAROFEX.ROS.CORN.FUT:CLOSE'}",
"expression":"interpolate(BASE, 'BACKWARD')"
}
}
This is what the curve looks like when selected in the portal for a specific date:

Adding multiple Smart Curves to an object
You don't need to add a new object for every Smart Curve, you can add all related Smart Curves on the same object.
In this next example, we derive a premium curve from the CLOSE curve by adding a 15% increase in the prices.
This is achieved by using our CLOSE curve as the base curve and using an expression: BASE*1.15
- OpenDataDSL
- REST API
// Create a PREMIUM curve
CORN_TEST_TYPE = object as #Agriculture
CURVE = SmartCurve("#MATBAROFEX.ROS.CORN.FUT:CLOSE", "interpolate(BASE, 'BACKWARD')")
PREMIUM = SmartCurve("CORN_TEST_TYPE:CLOSE", "BASE*1.15")
category = "TUTORIAL"
end
// Save to the database
save ${object: CORN_TEST_TYPE}
POST https://api.opendatadsl.com/api/object/v1
Authorization: Bearer {{token}}
{
"_id":"CORN_TEST_TYPE",
"_type":"#Agriculture",
"category": "TUTORIAL",
"CLOSE":
{
"_id":"CLOSE",
"_type":"VarSmartCurve",
"baseCurve":"${data:'#MATBAROFEX.ROS.CORN.FUT:CLOSE'}",
"expression":"interpolate(BASE, 'BACKWARD')"
},
"PREMIUM":
{
"_id":"PREMIUM",
"_type":"VarSmartCurve",
"baseCurve":"${data:'CORN_TEST_TYPE:CLOSE'}",
"expression":"BASE*1.15"
}
}
Testing
If you are using ODSL to create the Smart Curves, you can also directly test the curve to see that it is creating the curve you expect before saving the Smart Curve to the database.
To test our CLOSE curve, we simply need to pick a date to test for and use the build method on the Smart Curve like shown below:
// Test our Smart Curve for 2022-01-12
CLOSE = SmartCurve("#MATBAROFEX.ROS.CORN.FUT:CLOSE", "interpolate(BASE, 'BACKWARD')")
print CLOSE.build("2022-01-12")
Smart Curves With Multiple Inputs
Not all curves derive from a single base curve, so you need to be able to reference other input data. For example, if you want to create a MID curve between a BID and OFFER, you need 2 curves in order to do this.
With Smart Curves, you can add references to other input data you require and then use them within the Smart Curve expression.
Multiple curve inputs
Using the MID example, we could use the BID curve as the Smart Curve base curve and add a reference to the OFFER curve, naming it OFFER. Your Smart Curve expression would then be:
(BASE+OFFER)/2
Here we see how to create this using the same MatbaRofex Corn curve, creating a MID between the MIN and MAX curves.
We are adding this Smart Curve to the object we created above
- OpenDataDSL
- REST API
// Add a MID curve to our existing Smart Curve
CORN_TEST_TYPE = ${object:"CORN_TEST_TYPE"}
CORN_TEST_TYPE.MID = SmartCurve("#MATBAROFEX.ROS.CORN.FUT:MIN", "(BASE+MAX)/2")
// Add a reference to the MAX curve
CORN_TEST_TYPE.MID.MAX = ref("data", "#MATBAROFEX.ROS.CORN.FUT:MAX")
// Save to the database
save ${object: CORN_TEST_TYPE}
POST https://api.opendatadsl.com/api/object/v1
Authorization: Bearer {{token}}
{
"_id":"CORN_TEST_TYPE",
"_type":"#Agriculture",
"MID":
{
"_id":"PREMIUM",
"_type":"VarSmartCurve",
"baseCurve":"${data:'#MATBAROFEX.ROS.CORN.FUT:MIN'}",
"expression":"BASE*1.15",
"properties": {
"MAX":"${data:'#MATBAROFEX.ROS.CORN.FUT:MAX'}"
}
}
}
Using scalar inputs
We can also add scalar values to the Smart Curve and use these within the Smart Curve expression.
In this example, we add a factor property to the Smart Curve and use it to scale the input curve.
- OpenDataDSL
- REST API
// Add a new curve using a factor
CORN_TEST_TYPE = ${object:"CORN_TEST_TYPE"}
CORN_TEST_TYPE.SCALED = SmartCurve("CORN_TEST_TYPE:CLOSE", "BASE*factor")
// Add the factor to the Smart Curve
CORN_TEST_TYPE.SCALED.factor = 0.8
// Save to the database
save ${object: CORN_TEST_TYPE}
POST https://api.opendatadsl.com/api/object/v1
Authorization: Bearer {{token}}
{
"_id":"CORN_TEST_TYPE",
"_type":"#Agriculture",
"SCALED":
{
"_id":"SCALED",
"_type":"VarSmartCurve",
"baseCurve":"${data:'CORN_TEST_TYPE:CLOSE'}",
"expression":"BASE*factor",
"properties": {
"factor":0.8
}
}
}
Using TimeSeries inputs
We may want to use values from a TimeSeries in our expression, for example if we want to apply a scaling factor which changes over time.
For this example we are going to create a Monthly TimeSeries on our object and populate it with 6 months of scaling factors which will be used to scale the CLOSE prices based on the month they are priced.
- OpenDataDSL
- REST API
// Use a TimeSeries scaling factor
CORN_TEST_TYPE = ${object:"CORN_TEST_TYPE"}
// Create a new TimeSeries with 6 months of scaling factors
CORN_TEST_TYPE.SCALER = TimeSeries("2022-01-01", MonthlyCalendar(), [0.82, 0.85, 0.86, 0.88, 0.9, 0.91])
// Create the Smart Curve
CORN_TEST_TYPE.TSSCALED = SmartCurve("CORN_TEST_TYPE:CLOSE", "BASE*SCALER")
// Add a reference to the Scaler TimeSeries
CORN_TEST_TYPE.TSSCALED.SCALER = ref("data", "CORN_TEST_TYPE:SCALER")
// Save to the database
save ${object: CORN_TEST_TYPE}
POST https://api.opendatadsl.com/api/object/v1
Authorization: Bearer {{token}}
{
"_id":"CORN_TEST_TYPE",
"_type":"#Agriculture",
"TSSCALED":
{
"_id":"TSSCALED",
"_type":"VarSmartCurve",
"baseCurve":"${data:'CORN_TEST_TYPE:CLOSE'}",
"expression":"BASE*SCALER",
"properties": {
"SCALER":"${data:'CORN_TEST_TYPE:SCALER'}"
}
},
"SCALER":
{
"_id":"SCALER",
"_type":"VarTimeSeries",
"start":"2022-01-01",
"calendar":"MONTHLY",
"data":[
{"time":"2022-01-01", "value":0.82},
{"time":"2022-02-01", "value":0.85},
{"time":"2022-03-01", "value":0.86},
{"time":"2022-04-01", "value":0.88},
{"time":"2022-05-01", "value":0.9},
{"time":"2022-06-01", "value":0.91}
]
}
}
Converting
You can specify a currency and units on Smart Curves and it will automatically convert all the input data to the chosen currency and units.