Skip to main content

Overview

The Tilt Portfolio Optimization Platform enables you to create personalized portfolio optimizations for your clients. This guide walks through the complete workflow from initial organization setup to generating optimized trade proposals.

Step 1: Organization Setup

Configure Asset Classes

We’ll start by defining the asset classes available in your organization. An asset class consists of a name, description, and a benchmark that represents it. We’ll first search for available benchmark indexes, and then use that to create the asset class.

Search Available Benchmarks

curl --request GET \
  --url https://api.tilt.io/api/v1/custom/benchmark_indexes/ \
  --header 'X-Api-Key: <api-key>'
View API Reference →

Create Asset Classes

Now we can create an asset class using one of our benchmarks. The example below uses M75BNK-R, which is the identifier for the SPY ETF.
curl --request POST \
  --url https://api.tilt.io/api/v1/custom/org/{organization_uuid}/asset_classes/ \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "name": "US Equities",
  "description": "Example US Equities Asset Class",
  "benchmark_index_identifier": "M75BNK-R"
}'
View API Reference →

Step 2. Portfolio Setup

Now that we have an asset class, let’s create a portfolio which uses that asset class. We can make an example portfolio with a single allocation to US equities, although it is possible to blend multiple asset classes together. We reference the asset class using it’s UUID that was returned when creating it. We can always list available asset classes to retrieve all asset classes. At this point, we can enable direct indexing per asset class, for this portfolio. Later, we can override this setting per client too.
Direct indexing only takes affect if the underlying benchmark index also supports direct indexing.
curl --request POST \
  --url https://api.tilt.io/api/v1/custom/org/{organization_uuid}/portfolio_configs/ \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "name": "Example Portfolio",
  "asset_allocations": [
    {
      "asset_class_uuid": "asset-class-uuid",
      "target_allocation": "1",
      "direct_indexing_enabled": true
    }
  ]
}'
View API Reference →

Step 3: Client and Account Setup

Understanding Clients and Accounts

In Tilt, a client represents a person or entity in your system, and each client can have one or more accounts. Holdings, portfolio assignments, and optimizations all operate at the account level. When you create a client, a default account is automatically created. The response includes both the client UUID and the account UUID within the accounts array.

Create a Client

Let’s create a new client, which will also create a default account. The external_id should be a string that represents this client in your system.
curl --request POST \
  --url https://api.tilt.io/api/v1/custom/org/{organization_uuid}/clients/ \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "external_id": "<string>"
}'
The response will include:
  • uuid: The client UUID
  • accounts: An array containing the default account with its own uuid
When creating a client, we can also provide configuration and holdings information for the default account, but we’ll update those separately in this guide.
View API Reference →

Update Account Holdings

Now, let’s assign the account to a portfolio and update its holdings. We use the portfolio UUID which was returned when creating the portfolio, or we can find it by listing the available portfolios.
curl --request PATCH \
  --url https://api.tilt.io/api/v1/custom/org/{organization_uuid}/clients/{client_uuid}/accounts/{account_uuid} \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
    "portfolio_config_uuid": "portfolio-config-uuid",
    "holdings": {
      "tax_lots": [
        {
          "tilt_asset_id": "MH33D6-R",
          "quantity": "100",
          "cost_basis": "250",
          "trade_date": "2025-01-01",
          "wash_sale_date": "2025-01-01",
          "last_sold_for_loss_date": "2025-01-01"
        },
        {
          "tilt_asset_id": "P8R3C2-R",
          "quantity": "50",
          "cost_basis": "120",
          "trade_date": "2025-01-01",
          "wash_sale_date": "2025-01-01",
          "last_sold_for_loss_date": "2025-01-01"
        }
      ],
      "cash_balance": "50000",
      "non_tradable_assets_value": "5000"
    }
  }'
MH33D6-R and P8R3C2-R are the identifiers for Apple and Microsoft respectively. More information on asset identifiers is in the Tilt Asset IDs section.
View API Reference →

Update Direct Indexing Preference

We may override the direct indexing preferences for an account by updating asset_class_settings. For example, we can disable direct indexing for a particular asset class:
curl --request PATCH \
  --url https://api.tilt.io/api/v1/custom/org/{organization_uuid}/clients/{client_uuid}/accounts/{account_uuid} \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
    "asset_class_settings": [
      {
        "asset_class_uuid": "asset-class-uuid",
        "direct_indexing_enabled": false
      }
    ]
  }'
View API Reference →

Step 4: Portfolio Optimization

We can run optimization either for a single account, or in bulk across many accounts and clients. To start with, let’s run the optimization for a single account.

Single Account Optimization

Run an optimization for an individual account to get customized portfolio weights:
curl --request POST \
  --url https://api.tilt.io/api/v1/custom/org/{organization_uuid}/clients/{client_uuid}/accounts/{account_uuid}/optimize/ \
  --header 'X-Api-Key: <api-key>'
View API Reference →

Generate Trade Proposals

Convert the optimized weights into actionable trade proposals.
curl --request POST \
  --url https://api.tilt.io/api/v1/custom/org/{organization_uuid}/clients/{client_uuid}/accounts/{account_uuid}/proposal/ \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
    "optimized_weights": {
      "MH33D6-R": "0.5",
      "P8R3C2-R": "0.5"
    }
  }'
View API Reference →

Step 5: Bulk Operations

Bulk Optimization and Proposals

We can optimize clients in bulk by calling the bulk optimize endpoint, passing in a list of client UUIDs. This responds with a list of proposal UUID’s, that we can then check the result of.
curl --request POST \
  --url https://api.tilt.io/api/v1/custom/org/{organization_uuid}/optimized_proposals/ \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
    "client_uuids": [
      "client-uuid-1",
      "client-uuid-2",
      "client-uuid-3"
    ]
  }'
View API Reference →

Check Task Results

We can get the status for each proposal by calling the check status endpoint:
curl --request POST \
  --url https://api.tilt.io/api/v1/custom/org/{organization_uuid}/optimized_proposals/ \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
    "proposal_uuids": [
      "proposal-uuid-1",
      "proposal-uuid-2",
      "proposal-uuid-3"
    ]
  }'
View API Reference → To get the result for a generated proposal, we can call the get task result endpoint. This will return both the optimized weights and generated trades for that proposal.
curl --request GET \
  --url https://api.tilt.io/api/v1/custom/org/{organization_uuid}/optimized_proposals/{proposal_uuid}/ \
  --header 'X-Api-Key: <api-key>'
View API Reference → We can also search all generated proposals, and filter them based on criteria such as age, drift or turnover.
curl --request POST \
  --url https://api.tilt.io/api/v1/custom/org/{organization_uuid}/optimized_proposals/list/ \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: <api-key>' \
  --data '{
  "status": "pending",
  "created_after": "2025-10-01",
  "min_average_drift": "0.1"
}'

Configuration Hierarchy

Optimization configuration can be set on the account-level, portfolio-level, and organization level, and are merged together based on this order of precedence:
  1. Account config (highest priority)
  2. Portfolio config
  3. Organization config (fallback defaults)
The configs are represented by the same schema, so all config values can be overriden at each level. The account and organization config is set in the customization_config field in their respective schemas. Note that account and organization values outside of customization_config are unique to the account or organization, and are not merged. For example, factor_values is a top-level field in the organization schema. It is set globally for the organization, and cannot be overridden. Most properties are set via config however, and can be flexibly overridden as needed.
For list values (e.g., no_trade assets), the system uses the most specific non-empty list available. For example, if an account has any no_trade assets defined, these completely replace any organization-level no_trade assets rather than merging.

Tilt Asset IDs

We use asset IDs throughout the API to identify assets, rather than symbols. We can use the search tickers endpoint to find the tilt asset ID corresponding to a certain ticker symbol.
curl --request GET \
  --url https://api.tilt.io/api/v1/tickers/search?symbols=MSFT&ticker_types=common_stock \
  --header 'X-Api-Key: <api-key>'