Add Binance Futures support (long and short)
- config.rs: add Instrument::market_kind() mapping exchange name to
"spot"/"futures_um"/"futures_cm", and is_futures() helper
- swym.rs: submit_backtest() accepts market_kind param; passes it as
instrument.kind in the RunConfig instead of hardcoding "spot"
- agent.rs: derive has_futures from instruments; pass to both
system_prompt() and initial_prompt()
- prompts.rs:
- system_prompt() accepts has_futures; injects FUTURES_SHORT_EXAMPLES
(Example 5: EMA trend-following short with ATR stop) when true
- Rewrite position-state anti-patterns to cover both spot (long-only)
and futures (long + short) semantics
- initial_prompt() accepts has_futures; labels market as "spot" or
"futures" and passes flag through to starting instruction context
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,7 @@ use crate::config::ModelFamily;
|
||||
///
|
||||
/// Accepts a `ModelFamily` so each family can receive tailored guidance
|
||||
/// while sharing the common DSL schema and strategy evaluation rules.
|
||||
pub fn system_prompt(dsl_schema: &str, family: &ModelFamily) -> String {
|
||||
pub fn system_prompt(dsl_schema: &str, family: &ModelFamily, has_futures: bool) -> String {
|
||||
let output_instructions = match family {
|
||||
ModelFamily::DeepSeekR1 => {
|
||||
"## Output format\n\n\
|
||||
@@ -484,17 +484,84 @@ CRITICAL: `apply_func` uses `"input"`, not `"expr"`. Writing `"expr":` will be r
|
||||
intervals, the issue is signal logic, not timeframe — fix the signal before changing interval.
|
||||
- Don't create strategies with more than 5-6 conditions — overfitting risk
|
||||
- Don't ignore fees — a strategy needs to overcome 0.1% per round trip
|
||||
- Always gate buy rules with position state "flat" and sell rules with "long"
|
||||
- Never add a short-entry (sell when flat) rule — spot markets are long-only
|
||||
- Spot markets are long-only: gate buy (entry) rules with state "flat" and sell (exit) rules with state "long". Never add a short-entry (sell when flat) rule on spot.
|
||||
- Futures markets support both directions: long entry = buy when flat; long exit = sell when long; short entry = sell when flat; short exit (cover) = buy when short. Always include a stop-loss and time exit for both long and short legs.
|
||||
- Never use a placeholder string for `quantity` — `"ATR_SIZED"`, `"FULL_BALANCE"`, `"dynamic"`, etc. are all invalid and will be rejected.
|
||||
- `{{"method":"position_quantity"}}` is WRONG for exit rules — use `{{"kind":"position_quantity"}}` (see Quantity section above).
|
||||
"##
|
||||
{futures_examples}"##,
|
||||
futures_examples = if has_futures { FUTURES_SHORT_EXAMPLES } else { "" },
|
||||
)
|
||||
}
|
||||
|
||||
/// Short-entry and short-exit strategy examples, injected into the system prompt when
|
||||
/// futures instruments are present.
|
||||
const FUTURES_SHORT_EXAMPLES: &str = r##"
|
||||
|
||||
### Example 5 — Futures short: EMA trend-following short with ATR stop
|
||||
|
||||
On futures you can also short. Short entry = `"side": "sell"` when `"state": "flat"`;
|
||||
short exit (cover) = `"side": "buy"` when `"state": "short"`. Stop-loss for a short
|
||||
is price rising above entry, e.g. entry_price * 1.02. You may run long and short legs
|
||||
in the same strategy (4 rules total), or a short-only strategy (2 rules).
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "rule_based",
|
||||
"candle_interval": "4h",
|
||||
"rules": [
|
||||
{
|
||||
"comment": "Short entry: EMA9 crosses below EMA21 while price is below EMA50 (downtrend)",
|
||||
"when": {
|
||||
"kind": "all_of",
|
||||
"conditions": [
|
||||
{"kind": "position", "state": "flat"},
|
||||
{"kind": "ema_crossover", "fast_period": 9, "slow_period": 21, "direction": "below"},
|
||||
{"kind": "ema_trend", "period": 50, "direction": "below"}
|
||||
]
|
||||
},
|
||||
"then": {"side": "sell", "quantity": {"method": "percent_of_balance", "percent": "10", "asset": "usdc"}}
|
||||
},
|
||||
{
|
||||
"comment": "Short exit: EMA9 crosses back above EMA21, OR 2% stop-loss, OR 48-bar time exit",
|
||||
"when": {
|
||||
"kind": "all_of",
|
||||
"conditions": [
|
||||
{"kind": "position", "state": "short"},
|
||||
{
|
||||
"kind": "any_of",
|
||||
"conditions": [
|
||||
{"kind": "ema_crossover", "fast_period": 9, "slow_period": 21, "direction": "above"},
|
||||
{
|
||||
"kind": "compare",
|
||||
"left": {"kind": "field", "field": "close"},
|
||||
"op": ">",
|
||||
"right": {"kind": "bin_op", "op": "mul", "left": {"kind": "entry_price"}, "right": {"kind": "literal", "value": "1.02"}}
|
||||
},
|
||||
{
|
||||
"kind": "compare",
|
||||
"left": {"kind": "bars_since_entry"},
|
||||
"op": ">=",
|
||||
"right": {"kind": "literal", "value": "48"}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {"side": "buy", "quantity": {"kind": "position_quantity"}}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Key short-specific notes:
|
||||
- Stop-loss for short = close > entry_price * (1 + stop_pct), e.g. `* 1.02` for 2% stop
|
||||
- Take-profit for short = close < entry_price * (1 - target_pct), e.g. `* 0.97` for 3% target
|
||||
- Short exit uses `"side": "buy"` with `{"kind": "position_quantity"}` (same as long exit uses sell)
|
||||
- `percent_of_balance` for short entry uses `"usdc"` as the asset (the collateral currency)"##;
|
||||
|
||||
/// Build the user message for the first iteration (no prior results).
|
||||
/// `prior_summary` contains a formatted summary of results from previous runs, if any.
|
||||
pub fn initial_prompt(instruments: &[String], candle_intervals: &[String], prior_summary: Option<&str>) -> String {
|
||||
pub fn initial_prompt(instruments: &[String], candle_intervals: &[String], prior_summary: Option<&str>, has_futures: bool) -> String {
|
||||
let prior_section = match prior_summary {
|
||||
Some(s) => format!("{s}\n\n"),
|
||||
None => String::new(),
|
||||
@@ -515,8 +582,9 @@ that novelty attempt may you refine prior work.\n\
|
||||
"Start with a multi-timeframe trend-following approach with proper risk management \
|
||||
(stop-loss, time exit, and ATR-based position sizing)."
|
||||
};
|
||||
let market_type = if has_futures { "futures" } else { "spot" };
|
||||
format!(
|
||||
r#"{prior_section}Design a trading strategy for crypto spot markets.
|
||||
r#"{prior_section}Design a trading strategy for crypto {market_type} markets.
|
||||
|
||||
Available instruments: {}
|
||||
Available candle intervals: {}
|
||||
|
||||
Reference in New Issue
Block a user