Portfolio Signal Metadata Routes
The apps/v1 portfolio-signal routes expose SignalMetadataTable rows and
their associated SignalWeightsStorage cleanup operations.
These routes manage signal metadata only. They do not run signal DataNodes or portfolio calculation jobs.
signal_description is a plain-text or Markdown string. Do not send HTML tags;
the API does not define HTML rendering semantics for signal descriptions.
Runtime Sources
- Signal metadata uses
msm_portfolios.api.market_metadata.SignalMetadata. - Create and update payloads use
SignalMetadataCreateandSignalMetadataUpdate. - Historical signal weights use
msm_portfolios.data_nodes.signals.storage.SignalWeightsStorage.
SignalWeightsStorage.signal_uid references SignalMetadataTable.signal_uid.
Deleting a signal metadata row therefore first clears matching signal-weight
storage rows through TimeIndexMetaTable.delete_after_date(None,
dimension_filters=...), scoped by signal_uid, before deleting the metadata
row.
List Portfolio Signals
GET /api/v1/portfolio-signal/?search=&signal_uid=&limit=25&offset=0
Returns PaginatedResponse[SignalMetadata]:
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"uid": "signal-metadata-uid",
"signal_uid": "canonical-signal-key",
"signal_description": "Human-readable signal description"
}
]
}
search is a contains filter over signal_uid. signal_uid is an exact
filter.
Get Portfolio Signal
GET /api/v1/portfolio-signal/{uid}/
Returns one SignalMetadata row by metadata row uid:
{
"uid": "signal-metadata-uid",
"signal_uid": "canonical-signal-key",
"signal_description": "Human-readable signal description"
}
Missing rows return 404.
Create Portfolio Signal
POST /api/v1/portfolio-signal/
Request:
{
"signal_uid": "canonical-signal-key",
"signal_description": "Human-readable signal description"
}
Response: SignalMetadata.
Update Portfolio Signal
PATCH /api/v1/portfolio-signal/{uid}/
Request:
{
"signal_description": "Updated human-readable signal description"
}
Response: SignalMetadata.
signal_uid is immutable because SignalWeightsStorage rows reference it.
Delete Portfolio Signal Weights
DELETE /api/v1/portfolio-signal/{uid}/weights/?weights_date=2026-06-10T10:30:00Z
Deletes historical SignalWeightsStorage rows for the metadata row's
signal_uid through the storage table's
TimeIndexMetaTable.delete_after_date(...) API.
When weights_date is omitted, all weight rows for the signal are deleted.
When weights_date is provided, rows at or after that timestamp are deleted.
The API never calls delete_after_date(None) without a signal_uid dimension
scope.
Response:
{
"detail": "Signal weights deleted.",
"signal_metadata_uid": "signal-metadata-uid",
"signal_uid": "canonical-signal-key",
"weights_date": "2026-06-10T10:30:00Z",
"deleted_count": 12
}
Delete Portfolio Signal
DELETE /api/v1/portfolio-signal/{uid}/
Deletes one signal metadata row and all matching historical
SignalWeightsStorage rows. Storage cleanup uses
TimeIndexMetaTable.delete_after_date(None, dimension_filters=...), scoped by
the signal's signal_uid.
Response:
{
"detail": "Signal metadata deleted.",
"signal_metadata_uid": "signal-metadata-uid",
"signal_uid": "canonical-signal-key",
"deleted_count": 1,
"deleted_weights_count": 12
}
Missing metadata rows return 404. Protected delete conflicts return 409.