Building apps and products on the Ouro platform
This post will get into the details of how we created the first housing market report using the Ouro API and the Python SDK. This post will be pretty code heavy, but it all should be fairly understandable no matter your experience level.
If you haven't seen the report yet, check it out:
Throughout this post, you will see snippets of code from the Ouro Python SDK. You can learn more about it here: https://pypi.org/project/ouro-py/.
A large part of the actual value of this product comes from the forecasting that was done to generate estimates for the specific time series like Mortgage Rate and Fed Funds Rate.
will make an in-depth post explaining the methodology used there.
In this example, we'll keep it simple and look at some static data without any forecasting or data science. However, realize that because we're able to work in a programming environment, anything is possible in terms of the additional code you write and the logic performed.
Let's see how we can pull some data for this example:
from fredapi import Fred
fred = Fred(api_key=fred_api_key)
data = fred.get_series('MORTGAGE30US', observation_start='1980-01-01')
data.index = pd.to_datetime(data.index)
data.index.name = 'date'
Now that we have some data to work with, this would be where you can really let your creativity run wild. Do whatever you want to elevate the value of your data and find ways to provide insight to the consumers of your post.
With the data collected and elevated, we'll add it to Ouro so that it becomes an asset that we can reference later. You could even make the dataset its own product too.
Let's go ahead and create a new dataset to hold the data.
dataset = {
"name": "sample-data",
"visibility": "private",
"data": data
}
created = ouro.elements.earth.datasets.create(**dataset)
In the background, a CREATE TABLE
statement is executed based on the schema of your DataFrame and the data will be loading into that table so you query it later.
If you look at the created object, you will find all the metadata about your created dataset, including a unique id which we'll use to inline the dataset into the post we create.
You can go to https://ouro.foundation/app/elements/earth/datasets and check to make sure everything was added properly.
Now that we've created our Earth layer asset, we can move on to the post creation part - the Air layer aspect.
The way you create a post from code is completely up to you. Ouro makes no requirements about where the content comes from or how it's generated.
In this example, we show how you can use GenAI to synthesize data and language into a single cohesive post, all completely automated.
In the future, we'll rely on Water layer functionality to help us here, but for now we can use the Python SDK that OpenAI has published to help us interact with their models.
Note that there are practically unlimited ways to prompt and use these models, so what you see here as an example is just one way and by no means the best way.
template = {
"Introduction": {
"input": "Write an engaging introduction that captures the reader's attention and highlights "
+ "the importance of making an informed decision when considering buying a home. Briefly mention the "
+ "factors that will be discussed in the report, such as current housing market trends, mortgage rate "
+ "forecasts, economic indicators, and personal financial considerations.",
"output": {"type": "string"},
},
"Current Housing Market": {
"input": "Provide an overview of the current U.S. housing market using the given inputs. Discuss any notable impacts on homebuyers.",
"output": {"type": "string"},
},
}
fred_series_dict = {
"MORTGAGE30US": {
"desc": "mortgage_rate",
"long_desc": "Average interest rate on a 30-year fixed-rate mortgage in the U.S.",
"current_value": 7.22,
"six_months_forecast": 7.33,
"twelve_months_forecast": 7.43,
},
"DFF": {
"desc": "fed_funds_rate",
"long_desc": "Effective Federal Funds Rate",
"current_value": 5.33,
"six_months_forecast": 7.73,
"twelve_months_forecast": 11.21,
},
}
These two dictionaries are where the core of the product is. The prompts in the template are given to OpenAI and are the main factors determining the text that is generated.
The second dictionary is where the data science and value is added. By injecting these values, the model can use them to reason about a future that has not manifested yet, but is our best guess according to the forecasting methodology.
Now, let's send these messages to OpenAI to get the generated content.
chat_completion = oai_client.chat.completions.create(
messages=[
{
"role": "system",
"content": "You are a macroeconomic expert writing a report to help users decide if they should buy a home now or wait 6-12 months. "
},
{
"role": "system",
"content": json.dumps(template),
},
{
"role": "system",
"content": json.dumps(fred_series_dict),
},
],
response_format={"type": "json_object"},
model="gpt-4-turbo",
)
response = json.loads(chat_completion.choices[0].message.content)
~20 seconds later and we have an object we can use to produce the report! In the real version of this product, there is a lot more prompting going on, and multiple calls to OpenAI we use to focus on key areas of the report. In this example, we share a minimum-viable product but don't let this stop you from adding your own secret sauce to make your posts as good as they can be.
Now that we have the content we want, we'll format it into an Ouro post. Notice how we requested a JSON response from OpenAI? This was done so that we can have an understanding of the sections of the post and therefore be able to insert content within the sections. Examples of this might be adding another chat completion section or inlining an asset visualization.
# Create a new content editor
content = ouro.elements.air.Editor()
# Title of the post
content.new_header(
level=1, text="Housing Market Report (May 2024): Is Now a Good Time to Buy a Home?"
)
# Add all of the sections and their headings
for section in response:
content.new_header(level=2, text=section)
content.new_paragraph(response[section])
That's really all there is to it. Now let's look at some more post components you have access to including in the post.
In this. example, you can take your DataFrames and include them as inline tables. This is useful for looking at a sample or some key aspect of your data.
content.new_table(
pd.DataFrame(
[
{
"Timeframe": "Now",
"value": f'{fred_series_dict["MORTGAGE30US"]["current_value"].round(2)}%',
},
{
"Timeframe": "In 6 months",
"value": f'{fred_series_dict["MORTGAGE30US"]["six_months_forecast"].round(2)}%',
},
{
"Timeframe": "In 12 months",
"value": f'{fred_series_dict["MORTGAGE30US"]["twelve_months_forecast"].round(2)}%',
},
]
)
)
Now, we show how to include an interactive visualization of the dataset we created earlier. In this example, we are filtering on a column value so that we only include the data for a single series because the dataset the report uses stores multiple series.
content.new_inline_asset(
id=dataset_id,
asset_type="dataset",
filters=[
{
"column": "series_id",
"operator": "eq",
"value": "MORTGAGE30US",
"active": True,
}
],
view_mode="chart",
)
Once we are happy with the initial construction, we are ready to save and upload it to Ouro. We'll keep the visibility private until we are sure we're ready to publish. This way, we can iterate on the post in code without anyone else seeing it before it's ready.
post = {
"name": "Housing Market Report (May 2024): Is Now a Good Time to Buy a Home?",
"description": f"Navigating the U.S. Housing Market: Insights for Homebuyers",
"visibility": "private",
"content": content,
}
report = ouro.elements.air.posts.create(**post)
You can see the generated post by finding it in https://ouro.foundation/app/elements/air/posts.
If everything looks good, we can change the visibility in the code or change it from the web interface. If not, keep iterating with the code until it does!
You can make edits to the post by using the update
method.
updatedPost = {
"name": "Housing Market Report (May 2024): Is Now a Good Time to Buy a Home?",
"description": f"Navigating the U.S. Housing Market: Insights for Homebuyers",
"visibility": "private",
"content": content,
}
post_id = "16421f87-a0a3-4d92-9dd0-2097b3aa35c4"
report = ouro.elements.air.posts.update(post_id, **updatedPost)
Make sure to replace post_id
with the ID of your post, which you can find on the original response when you created the post, report.id
or by copying the ID in the URL of the post on the web interface.
That's pretty much all there is to it! Now, depending on what you've built, you can create new versions of the post automatically depending on changing factors like new external data or changing conditions - because it's all done with code.
Let me know if you have any questions or if you want to collab on a new post. Looking forward to seeing what ya'll create ❤️
Deciding when to buy a home is a crucial financial decision, influenced by a variety of economic indicators and personal circumstances. This report aims to equip you with the necessary information regarding the current housing market, mortgage rate forecasts, economic indi
Discover other posts like this one