{
  "files": {
    "D:\\A股低位放量研究\\experiments\\20261022_exit_confirmation\\config\\frozen_v1.json": "0a569e050b44ace42d81bec9eadc19264d77484fab5ae00b48a7aa2216ceb3c0",
    "D:\\A股低位放量研究\\experiments\\20261022_exit_confirmation\\src\\run_confirmation_study.py": "5910aed25d2f26bb6b71b0c42d29881d143ca119f6deca669fed7ee1cac0c497",
    "D:\\A股低位放量研究\\experiments\\20261020_exit_path_iteration\\src\\run_exit_study.py": "2e4ddb3154d57c4ee3663d6edcf72faa757798f1961edd0db20c84d151286d1a",
    "D:\\A股低位放量研究\\experiments\\20261020_exit_path_iteration\\results\\F60_all_mature_filled.csv": "37dd6dacc0d78f44f08584842eab89dcd63541382d253ba2c4581c3dc6b36735",
    "D:\\A股低位放量研究\\experiments\\20261020_daily_budget_exits\\src\\daily_study.py": "e43d048749bf9c187c2d00e1957ee6a28849c64db1221d6768540694b0e72828"
  },
  "index_row": 353,
  "data_cutoff": "2026-10-19",
  "compiled_execution_source": "def run_rule(m,a,z,vs,buy,slip,allacts,rule):\n r,e=int(z['row']),int(z['entry_t']);pending=None;trigger=None;blocked=0;exitv=None;reason=None;sell=None\n for v in vs:\n  if v['age']==1:continue\n  if pending:\n   sell=v['open'];reason=pending\n  elif v['age']>=60:sell=v['close'];reason='max60_close'\n  else:sell=None\n  if sell is not None:\n   ref=float(a['close'][r,v['t']-1]);xr=allacts.get(v['date'])\n   if xr:ref=(ref-xr.get('reference_cash_per_share',xr['cash_per_share_gross']))/(1+xr.get('reference_bonus_per_share',xr['bonus_per_share']))\n   if not np.isfinite(ref) or sell<=round(ref*(1-float(a['limit_rate'][r])),2)+.011:\n    blocked+=1\n    # Original F60 retries the scheduled closing liquidation. Triggered exit keeps next-open retry.\n    if pending is None:sell=None\n   else:exitv=v;break\n  if pending:continue\n  weak=v['age']>=20 and v['econ_close']<buy and v['below_ma20']\n  protect=v['peak_close_gain_pct']>=15 and v['drawdown_close_pct']<=-10\n  trend=v['peak_close_gain_pct']>=15 and v['two_below_ma20']\n  hit=False if rule=='F60' else trend if rule=='P15_MA20' else (trend and v.get('slope_available',False) and v['ma20']<v['ma20_tminus5']) if rule=='P15_MA20_SLOPE' else (trend and v.get('rs_available',False) and v['economic_ret20']<v['index_ret20']) if rule=='P15_MA20_RS' else False\n  if hit and v['age']<60:\n   reason=('no_progress_below_entry_and_ma20' if weak else 'profit_armed_close_drawdown10') if rule=='H20_P15' else rule\n   pending=reason;trigger={'trigger_t':v['t'],'trigger_date':v['date'],'trigger_age':v['age'],'trigger_gain_pct':v['gain_pct'],'trigger_peak_close_gain_pct':v['peak_close_gain_pct'],'trigger_drawdown_close_pct':v['drawdown_close_pct'],'trigger_ma20':v['ma20']}\n if exitv is None:\n  v=vs[-1];price=v['close']*(1-slip);closed=False;reason='marked_not_exited'\n else:v=exitv;price=sell*(1-slip);closed=True\n return {'status':'closed' if closed else 'open_right_censored','reason':reason,'entry_price':buy,'exit_price':price if closed else None,'mark_price':price,'exit_t':v['t'] if closed else None,'exit_date':v['date'] if closed else None,'holding_sessions':v['age'],'economic_value_per_original_share':v['units']*price+v['cash'],'bonus_units':v['units'],'dividend_net20_per_original_share':v['cash'],'gross_price_economic_return_pct':((v['units']*price+v['cash'])/buy-1)*100,'unresolved_action_flag':v['unknown'],'holding_missing_flag':v['missing'],'known_action_count':v['known_actions'],'blocked_sell_attempts':blocked,'last_mark_date':v['date'],'entry_day_no_sell':True,**(trigger or {})}\n"
}