{
  "properties": {
    "allocation": {
      "anyOf": [
        {
          "additionalProperties": true,
          "type": "object"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Optional portfolio allocation policy (cross-member weighting + rebalance + capital model). When provided it OVERRIDES the member-weight-derived allocation. 'trailing_return' member rotation ranks members by their OWN trailing realized return and holds the top_n, re-ranking on a calendar cadence. Shape: {'construction': {'type': 'trailing_return', 'top_n': 2, 'lookback_bars': 30}, 'rebalance': {'type': 'calendar', 'schedule': 'weekly'}, 'capital_model': 'partitioned'}. 'member_signal_rotation' ranks logic-bearing members by a declared shared_signals value (reduce-only), keeping each held member's own entry/exit logic running, e.g. {'type': 'member_signal_rotation', 'score_signal': 'momentum', 'top_n': 1} \u2014 requires a calendar rebalance and a declared 'shared_signals' entry named 'momentum'. 'construction.type' may also be 'equal_weight' or 'manual_weights' ({'type': 'manual_weights', 'weights': {strategy_id: fraction}}), or 'rank_based' with a 'score_signal' (construction-managed rotation \u2014 rejects logic-bearing members, unlike 'member_signal_rotation'). 'rank_based' with a raw 'score_ref' / 'signal_proportional' are universe-only and rejected \u2014 use 'trailing_return' or 'member_signal_rotation' to rotate members. Optional 'constraints' and 'collection_risk' dicts are accepted; fields the engine does not enforce are rejected fail-closed.",
      "title": "Allocation"
    },
    "members": {
      "anyOf": [
        {
          "items": {
            "additionalProperties": true,
            "type": "object"
          },
          "type": "array"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "New member list",
      "title": "Members"
    },
    "name": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "New portfolio name",
      "title": "Name"
    },
    "portfolio_id": {
      "description": "Portfolio identifier",
      "title": "Portfolio Id",
      "type": "string"
    },
    "project_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Re-scope this portfolio to a different research project. All (new or existing) member strategies must belong to this project \u2014 cross-project portfolios are rejected.",
      "title": "Project Id"
    },
    "shared_signals": {
      "anyOf": [
        {
          "items": {
            "additionalProperties": true,
            "type": "object"
          },
          "type": "array"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Portfolio-level cross-member shared signals. Each entry is {'name': str, 'source': <market ValueRef>}, e.g. {'name':'mom20','source':{'type':'indicator','indicator':'ema','period':20}}. Declared once, then referenced inside member entry/exit conditions via {'type':'shared_signal','name':'mom20'} so a member can gate on its rank / relationship vs sibling members. Source must be a market-derived per-symbol metric (indicator / price / volume); cross-sectional / state / event refs are rejected. Only valid on a 2+ member portfolio. On update_portfolio, omit to preserve the existing shared_signals; pass a list (including []) to replace them.",
      "title": "Shared Signals"
    }
  },
  "required": [
    "portfolio_id"
  ],
  "title": "update_portfolioArguments",
  "type": "object"
}
