agentsclimarketplace

Ptrade dev

Skill kay-ou/ClaudeSkills/.claude/skills/ptrade-dev

A Claude Skills collection providing ptrade‑dev strategy guardrails with lifecycle checks, API‑aware validation, and error‑pattern detection, plus structured parsing and webpage‑to‑Markdown tools—enabling LLMs to reliably analyze, generate, and debug PTrade/SimTradeLab strategies

Install
npx -y skills add kay-ou/ClaudeSkills --skill ptrade-dev

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 9 stars9 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

PTrade/SimTradeLab strategy development guardrails. Auto-loads PTrade API reference, enforces platform constraints (no f-string, no import io/sys), validates lifecycle function usage, and prevents common API misuse. Use when writing, editing, or reviewing any PTrade strategy code or SimTradeLab backtest code. TRIGGER: when editing files in strategies/, src/simtradelab/ptrade/, or any .py file that imports from simtradelab or uses PTrade API functions (initialize, handle_data, set_universe, get_history, order, etc).

SKILL.md

12.0 KB, as published. Nobody here has run it

PTrade Strategy Development Skill

You are writing code for the PTrade quantitative trading platform or its local simulator SimTradeLab. This skill ensures you NEVER make API errors by providing the complete reference inline.

CRITICAL PLATFORM CONSTRAINTS

These constraints apply to ALL code that runs on PTrade (NOT local-only files like research/run_local_backtest.py):

  1. NO f-strings — Use % formatting or .format(). PTrade's Python does not support f-strings.

    # WRONG
    log.info(f"price: {price}")
    # CORRECT
    log.info("price: %s" % price)
    log.info("price: {}".format(price))
    
  2. NO import io or import sys — These modules are blocked on PTrade.

  3. NO walrus operator := — Not supported.

  4. NO match/case — Not supported.

  5. Code format: PTrade uses .SS (Shanghai) and .SZ (Shenzhen) suffixes, but Order objects return .XSHG / .XSHE suffixes. Be aware of this difference.

  6. Global object g: Use g.xxx for cross-function state. Variables starting with __ are private and won't be persisted.

  7. log object: Use log.info(), log.warning(), log.error() etc. Never print().

STRATEGY LIFECYCLE (MANDATORY KNOWLEDGE)

A strategy has exactly 2 required functions and 5 optional ones:

initialize(context)          # REQUIRED — runs once at startup
handle_data(context, data)   # REQUIRED — runs every bar (daily/minute)
before_trading_start(context, data)  # optional — runs before market open
after_trading_end(context, data)     # optional — runs after market close
tick_data(context, data)     # optional — runs every 3s (live trading only)
on_order_response(context, order_list)  # optional — order callback (live only)
on_trade_response(context, trade_list)  # optional — trade callback (live only)

Lifecycle Timing

  • initialize: runs ONCE at strategy start
  • before_trading_start: backtest 8:30, live 9:10 (configurable)
  • handle_data: backtest 9:31-15:00 (minute) or 15:00 (daily); live 9:30-14:59
  • after_trading_end: ~15:30
  • tick_data: 9:30-14:59, every 3s (live only)

API FUNCTION LIFECYCLE RESTRICTIONS

CRITICAL: Each API can ONLY be called from specific lifecycle functions. Calling from the wrong function will error.

initialize-ONLY APIs (setup functions)

set_benchmark(benchmark)          # Set benchmark index
set_commission(commission)        # Set commission (backtest only)
set_fixed_slippage(slippage)      # Set fixed slippage (backtest only)
set_slippage(slippage)            # Set slippage (backtest only)
set_volume_ratio(ratio)           # Set volume ratio (backtest only)
set_limit_mode(mode)              # Set limit mode (backtest only)
set_yesterday_position(positions) # Set initial positions (backtest only)
set_parameters(params)            # Set strategy params
run_daily(func, time)             # Schedule daily function
run_interval(func, interval)      # Schedule interval function (live only)
set_future_commission(commission)  # Futures commission (backtest only)
set_margin_rate(security, rate)   # Futures margin (backtest/live)
permission_test(account, end_date)# Permission check (live only)
create_dir(user_path)             # Create directory (live only)

handle_data / tick_data APIs (trading functions)

set_universe(securities)                            # Set/update stock pool (also in initialize/before_trading_start)
order(security, amount, limit_price=None)           # Buy/sell by amount
order_target(security, target_amount, limit_price=None)  # Target amount
order_value(security, value, limit_price=None)      # Buy/sell by value
order_target_value(security, target_value, limit_price=None)  # Target value
order_market(security, amount)                      # Market order (live only)
cancel_order(order_id)                              # Cancel order (also in on_order_response)
cancel_order_ex(order_id)                           # Cancel order extended (live)
order_tick(security, amount, limit_price, tick_type) # Tick order (live only)
get_snapshot(security_list)                         # Realtime snapshot (live only)
get_gear_price(security_list)                       # Level quotes (live only)

after_trading_end APIs

after_trading_order(security, amount, limit_price)  # After-hours order (live)
after_trading_cancel_order(order_id)                # Cancel after-hours (live)
get_trades_file()                                   # Get trade file (backtest)
get_deliver(start_date, end_date)                   # Delivery records (live)
get_fundjour(start_date, end_date)                  # Fund journal (live)
send_email(...)                                     # Send email (live, also in on_order/on_trade_response)
send_qywx(...)                                      # Send WeChat (live, also in on_order/on_trade_response)

Universal APIs (callable from ANY lifecycle function)

# Market data
get_history(count, frequency, field, security_list, fq, include, fill, is_dict, start_date, end_date)
get_price(security, start_date, end_date, frequency, fields, count)

# Trading info
get_position(security)             # Get position for one stock
get_positions(security_list)       # Get positions for multiple stocks
get_open_orders(security=None)     # Get pending orders
get_order(order_id)                # Get specific order
get_orders(security=None)          # Get all orders today
get_trades(security=None)          # Get trades today

# Stock info
get_stock_name(security_list)
get_stock_info(security_list)
get_stock_status(security_list)
get_stock_exrights(security_list)
get_stock_blocks(security_list)
get_index_stocks(index_code)
get_industry_stocks(industry_code)
get_fundamentals(stocks, table, fields, date)
get_Ashares(date)
check_limit(security, query_date=None)

# Date/Calendar
get_trading_day(day=0)
get_all_trades_days(date=None)
get_trade_days(start_date, end_date)

# Technical indicators
get_MACD(close, short=12, long=26, m=9)
get_KDJ(high, low, close, n=9, m1=3, m2=3)
get_RSI(close, n=6)
get_CCI(high, low, close, n=14)

# Utility
log.info/warning/error/debug/critical
is_trade()                         # True if live trading
get_user_name()
get_research_path()

KEY OBJECTS REFERENCE

context.portfolio (Portfolio)

context.portfolio.cash              # Available cash (excludes frozen)
context.portfolio.positions         # dict: {code: Position}
context.portfolio.portfolio_value   # Total value (cash + positions)
context.portfolio.positions_value   # Positions value only
context.portfolio.capital_used      # Used capital
context.portfolio.returns           # Return ratio vs initial capital
context.portfolio.pnl               # Total P&L
context.portfolio.start_date        # Start date

Position (stock)

pos = context.portfolio.positions[code]
pos.sid                # Stock code
pos.amount             # Total shares held
pos.enable_amount      # Sellable shares (T+1)
pos.last_sale_price    # Latest price
pos.cost_basis         # Average cost
pos.today_amount       # Bought today (backtest only)
pos.business_type      # Position type

data (in handle_data)

# data is a dict: {stock_code: SecurityUnitData}
bar = data[security]
bar['open']    # or bar.open
bar['close']   # or bar.close
bar['high']    # or bar.high
bar['low']     # or bar.low
bar['volume']  # or bar.volume
bar['money']   # or bar.money
bar['price']   # = close
bar['dt']      # datetime

Order object

order_obj.id       # Order ID
order_obj.dt       # Order time (datetime.datetime)
order_obj.limit    # Limit price
order_obj.symbol   # Code (note: uses .XSHG/.XSHE suffix!)
order_obj.amount   # Amount: positive=buy, negative=sell

context.blotter

context.blotter.current_dt  # Current bar datetime (Beijing time)

get_history DETAILED REFERENCE

This is the MOST commonly misused API. Pay close attention:

get_history(
    count,                    # Number of bars to fetch
    frequency='1d',           # '1d' or '1m'
    field='close',            # Single field string or list
    security_list=None,       # Stock code(s) - string or list
    fq=None,                  # 'pre'=forward adj, 'post'=backward adj, None=raw
    include=False,            # True=include current bar
    fill='nan',               # Fill method: 'nan', 'pre' (forward fill)
    is_dict=False,            # True=return dict of DataFrames
    start_date=None,          # Alternative to count
    end_date=None             # End date for range query
)

Return type varies:

  • Single field + single stock: pd.DataFrame with column = field name
  • Single field + multiple stocks: pd.DataFrame with columns = stock codes
  • Multiple fields + is_dict=False: PanelLike object (dict-like, key=field)
  • Multiple fields + is_dict=True: dict of DataFrames

Common patterns:

# Get 20-day close for one stock
df = get_history(20, '1d', 'close', '600570.SS', fq='pre', include=False)
ma20 = df['close'].mean()

# Get OHLCV for one stock
data = get_history(20, '1d', ['open','high','low','close','volume'], '600570.SS', fq='pre')
closes = data['close']['600570.SS']  # PanelLike access

# Get close for multiple stocks
df = get_history(20, '1d', 'close', ['600570.SS', '000001.SZ'])
# df.columns = ['600570.SS', '000001.SZ']

get_price DETAILED REFERENCE

get_price(
    security,                 # Stock code (string)
    start_date=None,          # 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
    end_date=None,            # 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
    frequency='1d',           # '1d' or '1m'
    fields=None,              # List of fields, default all OHLCV
    count=None                # Alternative to date range
)

Returns: pd.DataFrame with DatetimeIndex, columns = requested fields

COMMON MISTAKES TO PREVENT

  1. Calling order() in initialize() — Trading functions only work in handle_data/tick_data
  2. Using set_commission() in handle_data() — Setup functions only work in initialize()
  3. Assuming data[security] always exists — Only stocks in set_universe() are in data
  4. Forgetting T+1 rulepos.enable_amount may be 0 for stocks bought today; always check before selling
  5. Using .XSHG/.XSHE codes with order() — Use .SS/.SZ format for orders
  6. Calling get_snapshot() in backtest — Only available in live trading
  7. Using f-strings — PTrade does NOT support f-strings!
  8. get_history with include=True — Current bar data may be incomplete
  9. Negative amount in order() — Negative = sell, positive = buy

SIMTRADELAB LOCAL-ONLY DIFFERENCES

When writing code for SimTradeLab local backtest (research/run_local_backtest.py):

  • f-strings ARE allowed (local Python 3.9+)
  • import io/sys ARE allowed
  • Data comes from local parquet/CSV files via DataContext
  • API behavior is simulated to match PTrade as closely as possible

SELF-CHECK BEFORE SUBMITTING CODE

Before finishing any PTrade strategy code, verify:

  • No f-strings in PTrade-targeted code
  • No import io or import sys in PTrade-targeted code
  • All API calls are in the correct lifecycle function
  • Stock codes use correct suffix (.SS/.SZ)
  • set_universe() is called in initialize()
  • Trading functions (order etc.) are only in handle_data/tick_data
  • Check enable_amount before selling
  • Using log.info() not print()
  • get_history parameters are correct (especially fq and include)

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.