feat: add portfolio phase — discretize alpha weights into tradable positions
Adds a fourth pipeline phase modeling A-share microstructure: lot sizes, the 2023-08-10 Main Board increment change, STAR 200-share minimum/odd-lot rules, limit-up/down, suspensions, volume caps, costs, and slippage. Two layers: research (continuous weights → return/Sharpe/turnover/Fitness, no IC per repo convention) and execution (state-dependent lot rounding + two-stage greedy exposure repair + next-open reference simulator). Wires `portfolio build/simulate/eval` into the CLI and adds the POSITION/FILL/PNL schema contracts. Covered by tests/test_portfolio.py. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -42,3 +42,43 @@ COMBO_COLUMNS: Final[list[str]] = [
|
||||
"combo_name", # str: identifies which combo (e.g. 'equal_weight')
|
||||
"weight", # float64: combined weight, signed
|
||||
]
|
||||
|
||||
# Required columns for portfolio (position) parquet files.
|
||||
# A position is a tradable integer holding derived from a target weight under
|
||||
# A-share lot/board rules. Produced by the `portfolio build` phase.
|
||||
POSITION_COLUMNS: Final[list[str]] = [
|
||||
"symbol_id", # str
|
||||
"date", # date
|
||||
"portfolio_name", # str: identifies this construction run
|
||||
"target_weight", # float64: w = alpha / sum(|alpha|); signed, sum(|w|)=1
|
||||
"target_value", # float64: v_target = booksize * w (signed dollar exposure)
|
||||
"target_shares", # float64: q_target = v_target / price (continuous, signed)
|
||||
"position_shares", # int64: discretized + repaired integer shares (signed)
|
||||
"position_value", # float64: position_shares * price (signed)
|
||||
"price", # float64: construction price (close by default)
|
||||
]
|
||||
|
||||
# Required columns for execution-simulator fill parquet files.
|
||||
FILL_COLUMNS: Final[list[str]] = [
|
||||
"symbol_id", # str
|
||||
"date", # date: the EXECUTION date (open[t+1] of the target date)
|
||||
"portfolio_name", # str
|
||||
"prev_shares", # int64: realized position carried in
|
||||
"target_shares", # int64: requested target for this execution
|
||||
"traded_shares", # int64: signed delta actually executed
|
||||
"realized_shares", # int64: resulting position (blocked trades revert to prev)
|
||||
"blocked", # int: 1 if the trade was (fully or partially) blocked
|
||||
"trade_cost", # float64: commission + slippage in yuan
|
||||
]
|
||||
|
||||
# Required columns for execution-simulator per-day PnL parquet files.
|
||||
PNL_COLUMNS: Final[list[str]] = [
|
||||
"date", # date
|
||||
"portfolio_name", # str
|
||||
"gross_exposure", # float64: sum(|position_value|)
|
||||
"net_exposure", # float64: sum(signed position_value)
|
||||
"pnl", # float64: daily mark-to-market PnL (yuan), net of cost
|
||||
"cost", # float64: total trade cost that day (yuan)
|
||||
"turnover", # float64: sum(|traded_value|) / booksize
|
||||
"n_positions", # int: count of nonzero holdings
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user