yobro4619's picture
Upload folder using huggingface_hub
e9c52c9 verified
"""
Medium Task: Pairs Trading with Constraints
The agent must implement a Z-score mean-reversion pairs strategy
with cooldown logic and exposure constraints.
"""
MEDIUM_TASK = {
"id": "medium",
"name": "Pairs Trading with Constraints",
"max_steps": 20,
"ground_truth": {
"total_trades_per_leg": 18815,
"total_spread_entries": 9408,
"total_pnl": -60226.70,
"max_drawdown": 60677.26,
"annualized_sharpe": -2.8732,
},
"tolerances": {
"total_trades_per_leg": 0.05,
"total_pnl": 0.02,
"max_drawdown": 0.02,
"annualized_sharpe": 0.05,
},
"description": """# Task: Pairs Trading with Z-Score and Constraints
## Objective
Implement a Z-score based mean-reversion pairs trading strategy on NIFTY and BANKNIFTY.
## Strategy Specification
### Spread and Z-Score
- **Spread** = `nifty_close - 0.35 * banknifty_close`
- **Z-score**: rolling window of 60 bars, using `ddof=1` for standard deviation
```python
spread_mean = spread.rolling(60).mean()
spread_std = spread.rolling(60).std(ddof=1)
z_score = (spread - spread_mean) / spread_std
```
### Signal Rules
- **Enter short spread** when z > +2.0: set nifty_position = -1, banknifty_position = +1
- **Enter long spread** when z < -2.0: set nifty_position = +1, banknifty_position = -1
- **Exit** when |z| < 0.5: set both positions to 0
- **30-bar cooldown**: After each exit, wait 30 bars before entering again
- During warmup (first 60 bars) and cooldown, positions must be 0
### Execution Model
- Next-bar execution: signal at bar `i` determines position at bar `i+1`
- Positions are always +1, -1, or 0 (the 0.35 factor is for spread calculation only, NOT position sizing)
### Exposure Constraint
- Net exposure ratio = |net_notional| / gross_notional must be <= 0.80
- net_notional = nifty_pos * nifty_close + bn_pos * bn_close
- gross_notional = |nifty_pos * nifty_close| + |bn_pos * bn_close|
- This constraint is naturally satisfied by the +1/-1 position sizing
## Function Signature
```python
def generate_trades(nifty_df: pd.DataFrame, banknifty_df: pd.DataFrame) -> pd.DataFrame:
\"\"\"
Generate pairs trading signals.
Parameters
----------
nifty_df : DataFrame with columns [Date, Open, High, Low, Close] for NIFTY.
banknifty_df : DataFrame with columns [Date, Open, High, Low, Close] for BANKNIFTY.
Both DataFrames are aligned (same length, same Date index).
Returns
-------
DataFrame with columns ['bar', 'nifty_position', 'banknifty_position'].
Only include rows where any position CHANGES.
\"\"\"
```
## Pseudocode
```
state = FLAT
cooldown_remaining = 0
for bar in range(n_bars):
if bar < 60: # warmup
continue
z = z_score[bar]
if cooldown_remaining > 0:
cooldown_remaining -= 1
continue
if state == FLAT:
if z > 2.0:
# Short spread at next bar
emit trade at bar+1: nifty=-1, banknifty=+1
state = SHORT_SPREAD
elif z < -2.0:
# Long spread at next bar
emit trade at bar+1: nifty=+1, banknifty=-1
state = LONG_SPREAD
elif state in (SHORT_SPREAD, LONG_SPREAD):
if abs(z) < 0.5:
# Exit at next bar
emit trade at bar+1: nifty=0, banknifty=0
state = FLAT
cooldown_remaining = 30
```
""",
"function_signature": """def generate_trades(nifty_df: pd.DataFrame, banknifty_df: pd.DataFrame) -> pd.DataFrame:
\"\"\"
Generate pairs trading signals.
Args:
nifty_df: NIFTY DataFrame with [Date, Open, High, Low, Close].
banknifty_df: BANKNIFTY DataFrame with same columns.
Returns:
DataFrame with columns ['bar', 'nifty_position', 'banknifty_position'].
Only include rows where any position changes.
\"\"\"
import pandas as pd
import numpy as np
# Your implementation here
pass""",
}