SPB Git

spb/wp27_uqo

Public
11.4 KB · 116 lines latex
1\section{Prompts}2\label{app:prompts}34This appendix reproduces verbatim the frozen prompt version \code{v1} of \code{llm/prompting.py}, one rendered example, the JSON schema sent to the providers, and the validation rules of \code{llm/policy.py}. The system prompt is the persona sentence followed by the rules block; the user message is a deterministic rendering of the agent's \code{Observation}. Nothing in either text depends on the latent state.56\lstset{literate={≈}{{$\approx$ }}2}78\subsection*{Personas (\code{ROLE\_PERSONAS})}9The single-prompt design uses \code{generalist} for every LLM slot; the diverse-prompt design assigns the eight personas cyclically in slot order.1011\begin{lstlisting}12generalist : You are a professional trader managing your own account in a single-stock electronic market.13value      : You are a value investor: you compare the price to the present value of expected dividends and trade mispricings patiently.14momentum   : You are a momentum trader: you follow recent price trends and cut positions when the trend reverses.15contrarian : You are a contrarian trader: you fade sharp moves that are not justified by dividend news.16liquidity  : You are a liquidity provider: you prefer resting limit orders near the best quotes and keep your inventory small.17risk_averse: You are a risk-averse trader: you keep positions small, avoid market orders and prefer to hold when uncertain.18macro      : You are a news-driven trader: you weigh public announcements and dividend surprises and act when the market has not yet reacted.19quant      : You are a quantitative trader: you reason about expected value per share, spread costs and inventory before each order.20\end{lstlisting}2122\subsection*{Rules template (\code{RULES})}23Placeholders in braces are filled from the configuration and the observation (\code{div\_interval}, \code{mu}, \code{r\_pct}, \code{pv\_mean}, \code{rho}, \code{tick}, \code{taker\_bps}, \code{ttl}, \code{max\_qty}, \code{interval}); doubled braces are Python format escapes and render as single braces.2425\begin{lstlisting}26Market mechanics (read carefully):27- One risky stock paying a cash dividend every {div_interval} ticks; the long-run mean dividend is {mu:.2f} per share per period and the per-period discount rate is {r_pct:.2f}%, so a share paying the mean dividend forever is worth about {pv_mean:.2f}.28- Dividends deviate persistently from the mean (autocorrelation {rho:.2f} per period). The deviation of the NEXT dividend is not yet announced; public news and, when you have one, your private signal are noisy estimates of it (in cents). The present value of a share rises by about 1/(1+r-rho) ≈ 44 cents per cent of next-dividend deviation.29- Prices are in currency units with a tick of {tick:.2f}. Limit orders rest in the book; market orders execute immediately against the best quotes (taker fee {taker_bps:.1f} bps). Orders may be partially filled. Unfilled limit orders expire after {ttl} ticks.30- You cannot short sell and cannot spend more than your free cash. Maximum order size: {max_qty} shares.31- You decide every {interval} ticks. Between decisions the market keeps trading.3233Respond with ONLY a JSON object with exactly these keys:34{{"action": "hold"|"place"|"cancel", "side": "buy"|"sell"|null, "order_type": "limit"|"market"|null, "quantity": integer|null, "limit_price": number|null, "cancel_order_ids": [integers], "confidence": number in [0,1]|null, "rationale": "one sentence, max 200 characters"}}35"place" requires side, order_type and quantity (and limit_price for a limit order). "cancel" requires cancel_order_ids. Anything else is treated as hold.36\end{lstlisting}3738\noindent The placeholder \code{interval} is filled with the dividend interval (\code{obs.div\_interval}), not with \code{decision\_interval}; the two coincide (20) in every reported design.3940\subsection*{Rendered example}41The following system prompt and user message were produced by running the prompt builder on the observation of an informed LLM slot (agent~9, persona \code{generalist}, tick~110) in a mock run of the baseline configuration with $\varphi=0.25$, seed~1.\footnote{Generated with \code{system\_prompt()} and \code{user\_message()} of \code{llm/prompting.py}; the observation was captured from \code{Simulation.\_observe}. The mid-price history is sub-sampled to every sixth tick of the 61-tick window.}4243\begin{lstlisting}44You are a professional trader managing your own account in a single-stock electronic market.4546Market mechanics (read carefully):47- One risky stock paying a cash dividend every 20 ticks; the long-run mean dividend is 0.50 per share per period and the per-period discount rate is 0.25%, so a share paying the mean dividend forever is worth about 200.00.48- Dividends deviate persistently from the mean (autocorrelation 0.98 per period). The deviation of the NEXT dividend is not yet announced; public news and, when you have one, your private signal are noisy estimates of it (in cents). The present value of a share rises by about 1/(1+r-rho) ≈ 44 cents per cent of next-dividend deviation.49- Prices are in currency units with a tick of 0.01. Limit orders rest in the book; market orders execute immediately against the best quotes (taker fee 2.0 bps). Orders may be partially filled. Unfilled limit orders expire after 50 ticks.50- You cannot short sell and cannot spend more than your free cash. Maximum order size: 50 shares.51- You decide every 20 ticks. Between decisions the market keeps trading.5253Respond with ONLY a JSON object with exactly these keys:54{"action": "hold"|"place"|"cancel", "side": "buy"|"sell"|null, "order_type": "limit"|"market"|null, "quantity": integer|null, "limit_price": number|null, "cancel_order_ids": [integers], "confidence": number in [0,1]|null, "rationale": "one sentence, max 200 characters"}55"place" requires side, order_type and quantity (and limit_price for a limit order). "cancel" requires cancel_order_ids. Anything else is treated as hold.56\end{lstlisting}5758\begin{lstlisting}59Time: tick 110. Next dividend in 10 ticks.60Best bid 200.80 / best ask 201.12 / mid 200.96 / last trade 201.12.61Bid depth (price x shares): 200.80x14, 200.70x28, 200.69x13, 200.68x14, 200.65x12. Ask depth: 201.12x14, 201.15x4, 201.19x8, 201.20x14, 201.23x4.62Mid-price history (oldest to newest, 61 ticks sampled): 200.20, 200.29, 200.48, 200.09, 200.54, 200.66, 200.64, 200.64, 200.60, 200.73, 200.96.63Change over the window: +0.38%. Recent volatility (std of tick returns): 0.042%.64Last 30 trades: volume 168 shares, 57% buyer-initiated.65Last dividend paid: 0.55 per share. Public estimate of the next dividend's deviation from its mean: public estimate +5.0 cents. Present value of expected dividends using only public information: 202.23.66Your private signal: next dividend deviation estimated at +3.6 cents (signal precision 2.0; higher is more reliable).67Your account: cash 18634.09 (free 18634.09), holdings 92 shares, equity 37122.41.68You have no open orders.69Decide now. Reply with the JSON object only.70\end{lstlisting}7172\noindent\sloppy Optional lines, in the order in which they are inserted when the corresponding treatment is active: \code{TRADING IS HALTED on this stock (orders will be rejected).} after the first line; up to three \code{Public news (tick $t$): \dots} lines after the public-value line; \code{Your open orders: id \dots: buy/sell remaining/qty @ price (age)} (at most six) instead of \code{You have no open orders.}; \code{Feedback: your equity changed by $\pm x$\% since your last decision (cumulative $\pm y$\%).} (adaptive feedback); \code{Your recent decisions and outcomes:} followed by up to five bullet lines (memory); \code{Messages from other traders in your group (unverified opinions):} followed by up to five bullet lines (communication channel).7374\subsection*{JSON schema (\code{DECISION\_JSON\_SCHEMA})}75Sent as \code{text.format} (OpenAI, \code{strict: true}) and \code{output\_config.format} (Anthropic). Every key is required; \code{null} is allowed where indicated; no additional properties.7677\begin{lstlisting}78{79  "type": "object",80  "properties": {81    "action":           {"type": "string", "enum": ["hold", "place", "cancel"]},82    "side":             {"type": ["string", "null"], "enum": ["buy", "sell", null]},83    "order_type":       {"type": ["string", "null"], "enum": ["limit", "market", null]},84    "quantity":         {"type": ["integer", "null"]},85    "limit_price":      {"type": ["number", "null"]},86    "cancel_order_ids": {"type": "array", "items": {"type": "integer"}},87    "confidence":       {"type": ["number", "null"]},88    "rationale":        {"type": "string"}89  },90  "required": ["action", "side", "order_type", "quantity", "limit_price",91               "cancel_order_ids", "confidence", "rationale"],92  "additionalProperties": false93}94\end{lstlisting}9596\subsection*{Parsing and validation rules}97The reply is first parsed, then validated against the pydantic model \code{Decision}, then checked for feasibility by \code{LLMPolicy.to\_action}. Any failure yields a hold action with \code{valid = False} and the reason string below; a provider error or a budget halt yields a hold with the error string as reason.9899\begin{enumerate}\sloppy100\item \textbf{Parsing} (\code{parse\_decision}): whitespace and a surrounding \code{```}/\code{```json} fence are stripped; if the text is not valid JSON, the first \code{\{\dots\}} block is tried; failure $\to$ reason \code{json: <message>}.101\item \textbf{Schema} (\code{Decision}): \code{action} $\in$ \{hold, place, cancel\}; \code{side} $\in$ \{buy, sell, null\}; \code{order\_type} $\in$ \{limit, market, null\}; \code{quantity} integer $\ge 0$ or null; \code{limit\_price} number $>0$ or null (currency units); \code{cancel\_order\_ids} list of integers; \code{confidence} in $[0,1]$ or null; \code{rationale} trimmed to 400 characters; failure $\to$ reason \code{schema: <field>: <message>} (first three errors).102\item \textbf{hold}: always valid; no intent.103\item \textbf{cancel}: the ids are intersected with the agent's open orders (resting and in flight); empty intersection $\to$ \code{cancel: no valid order ids}; otherwise one cancel intent per surviving id.104\item \textbf{place}, in this order:105  \begin{enumerate}106  \item side, order type and quantity must all be present (\code{place: missing side/order\_type/quantity});107  \item quantity $>0$ (\code{place: quantity <= 0}) and $\le$ \code{max\_order\_qty} $=50$ (\code{place: quantity q > max 50});108  \item the asset must not be halted (\code{place: market halted});109  \item limit orders need a price (\code{place: limit without price}); the price is converted to ticks by $\operatorname{round}(100\,\text{price}/\text{tick\_cents})$ and must be positive and within $\pm20\%$ of the midpoint (\code{place: limit price outside ±20\% of mid});110  \item market orders need a non-empty opposite side (\code{place: market order with empty opposite side});111  \item buys: $\lfloor \text{ref}\times\text{tick\_cents}\times q\times1.001\rfloor \le$ free cash, where ref is the limit price or else the best ask or midpoint (\code{place: cost c > free cash f});112  \item sells: $q$ is clipped to holdings minus the remaining quantity of the agent's resting sells; if nothing is free the decision is invalid (\code{place: no free shares to sell}); a clipped sell is recorded as valid.113  \end{enumerate}114\item The surviving intent is tagged \code{llm} and submitted to the exchange, which applies its own checks (price band, cash reservation, position limit, leverage); an exchange rejection is logged in the event log, not in the decision log.115\end{enumerate}116
Files · main
Menu

Theme