Skill Index

ai-asset-pricing/

run

community[skill]

Fast batch/single portfolio sort on Dickerson bond data. Bypasses orchestrator agent — runs directly via Bash. Use for known workflows on real data. Examples: /run batch cs ytm bbtm, /run batch cs ytm --wfs, /run single cs --nport 10

$/plugin install ai-asset-pricing

details

/run — Fast Portfolio Sort Execution

Executes batch or single portfolio sorts on the Dickerson bond dataset without spawning an agent. One Bash call, results saved automatically.

Arguments

/run <mode> <signals...> [options]
ArgumentValuesDefault
modebatch, singlerequired
signalsspace-separated signal names (e.g., cs ytm bbtm)required
--wfsUse WithinFirmSort instead of SingleSortoff
--ratingIG, NIG, or omit for allall
--nportNumber of portfolios (SingleSort only)5
--hpHolding period1

Execution Steps

  1. Parse arguments from the user's /run command
  2. Determine the bond data path — look for Dickerson bond parquet in data/ (check metadata.json for datasets from bonds-wrds-expert), or use the path from canonical local state reported by tools/bootstrap.py audit
  3. Generate the Python script from the template below (fill in signals, options, data path)
  4. Execute via single Bash call using the Python path from canonical local state reported by tools/bootstrap.py audit
  5. Print the mandatory summary table:
| Signal | Weight | Mean(%) | t-stat | SR | Turn(%) |

All values annualized (%). Turnover = avg monthly two-way (%).

  1. Follow with 2-3 sentences interpreting the results. Always state the sample period (first and last date in the returns).

Date Filtering

The TRACE-aligned sample filter (2002-08 to 2024-12) should only be applied when:

  • The data is corporate bond / bond data, AND
  • The user explicitly requests a TRACE-aligned sample

Otherwise, use the full date range in the data as-is. Do NOT hardcode the TRACE filter by default.

Template: Batch SingleSort

import warnings; warnings.filterwarnings('ignore')
import pandas as pd
import numpy as np
import PyBondLab as pbl
from PyBondLab.report import ResultsReporter
from PyBondLab.describe.utils import compute_nw_tstat

SIGNALS = {signals}          # e.g., ['cs', 'ytm', 'bbtm']
RATING = {rating}            # None, 'IG', or 'NIG'
NPORT = {nport}              # e.g., 5
HP = {hp}                    # e.g., 1
MNEMONIC = '{mnemonic}'      # e.g., 'batch_3s'

SCRIPT = '<generated by /run skill>'

data = pd.read_parquet('{data_path}')
# TRACE-aligned filter: ONLY apply if corporate bond data AND user requests TRACE-aligned sample
# data = data[(data['date'] >= '2002-08-01') & (data['date'] <= '2024-12-31')]
data['spc_rat'] = data['spc_rat'].astype('float64')

batch = pbl.BatchStrategyFormation(
    data=data, signals=SIGNALS,
    holding_period=HP, num_portfolios=NPORT,
    turnover=True, rating=RATING,
    columns={'ID': 'cusip', 'ret': 'ret_vw', 'VW': 'mcap_e', 'RATING_NUM': 'spc_rat'},
    n_jobs=-2, verbose=False,
)
results = batch.fit()

report_path = ResultsReporter(results, mnemonic=MNEMONIC, script_text=SCRIPT).generate()

# Print summary table
rows = []
for sig in SIGNALS:
    r = results[sig]
    ew_ls, vw_ls = r.get_long_short()
    for label, ls in [('EW', ew_ls), ('VW', vw_ls)]:
        valid = ls.dropna()
        ann_mean = valid.mean() * 12 * 100
        sr = valid.mean() / valid.std() * np.sqrt(12) if valid.std() > 0 else 0
        nw_lag = int(len(valid) ** 0.25)
        t = compute_nw_tstat(valid, nw_lag=nw_lag)[0]
        turn = '—'
        try:
            ew_t, vw_t = r.get_turnover()
            td = ew_t if label == 'EW' else vw_t
            if td is not None and not td.empty:
                turn = f'{td.mean().mean() * 100:.1f}'
        except: pass
        rows.append((sig, label, ann_mean, t, sr, turn))

print('|Signal|Weight|Mean(%)|t-stat|SR|Turn(%)|')
print('|---|---|---|---|---|---|')
for sig, wt, m, t, sr, turn in rows:
    print(f'|{sig}|{wt}|{m:.2f}|{t:.2f}|{sr:.3f}|{turn}|')
first_sig = SIGNALS[0]
r0 = results[first_sig]
ew0, _ = r0.get_long_short()
print(f'Sample: {ew0.dropna().index[0].strftime("%Y-%m")} to {ew0.dropna().index[-1].strftime("%Y-%m")}')
print(f'Report: {report_path}')

Template: Batch WithinFirmSort

Same as above but replace BatchStrategyFormation with:

batch = pbl.BatchWithinFirmSortFormation(
    data=data, signals=SIGNALS,
    firm_id_col='permno',
    min_bonds_per_firm=2,
    turnover=True, rating=RATING,
    columns={'ID': 'cusip', 'ret': 'ret_vw', 'VW': 'mcap_e', 'RATING_NUM': 'spc_rat'},
    n_jobs=-2, verbose=False,
)

Mnemonic: batchwfs_{n}s instead of batch_{n}s.

Template: Single Sort

For mode=single, run one signal at a time with StrategyFormation:

strategy = pbl.SingleSort(holding_period=HP, sort_var=SIGNAL, num_portfolios=NPORT)
sf = pbl.StrategyFormation(data, strategy=strategy, turnover=True, rating=RATING, verbose=False)
result = sf.fit(IDvar='cusip', RETvar='ret_vw', VWvar='mcap_e', RATINGvar='spc_rat')
report_path = ResultsReporter(result, mnemonic=f'{SIGNAL}_single_{NPORT}', script_text=SCRIPT).generate()

Mnemonic Convention

ModePatternExample
batchbatch_{n}sbatch_3s
batch --wfsbatchwfs_{n}sbatchwfs_3s
single{signal}_single_{nport}cs_single_5

When NOT to Use /run

Use the pybondlab-orchestrator agent instead when:

  • Working with new/unfamiliar data (needs column discovery and mapping)
  • Running DoubleSort (needs sort_var/sort_var2 ordering decisions)
  • Running DataUncertaintyAnalysis or AssayAnomaly (complex config)
  • Debugging errors from a previous run

technical

github
Alexander-M-Dickerson/ai-asset-pricing
stars
49
license
MIT
contributors
1
last commit
2026-04-19T07:58:01Z
file
.claude/skills/run/SKILL.md

related