File size: 7,297 Bytes
3a7314c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
147e9ed
3a7314c
147e9ed
3a7314c
 
 
 
 
147e9ed
3a7314c
147e9ed
3a7314c
147e9ed
 
 
 
 
 
3a7314c
 
 
 
 
 
 
147e9ed
 
 
3a7314c
 
 
 
 
147e9ed
 
 
 
 
3a7314c
 
 
 
 
 
 
147e9ed
 
3a7314c
147e9ed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3a7314c
 
 
147e9ed
3a7314c
 
 
 
147e9ed
3a7314c
 
 
 
 
 
 
 
 
147e9ed
 
 
 
 
3a7314c
147e9ed
 
 
 
 
3a7314c
147e9ed
 
 
 
 
3a7314c
 
 
 
 
147e9ed
 
 
 
 
 
 
3a7314c
147e9ed
 
 
 
 
 
 
 
 
3a7314c
 
 
147e9ed
3a7314c
147e9ed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3a7314c
 
 
 
 
147e9ed
3a7314c
147e9ed
3a7314c
 
 
 
 
 
 
 
 
 
147e9ed
3a7314c
147e9ed
 
31e81ea
 
 
3a7314c
 
 
 
 
 
147e9ed
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
---
title: IndicatorsEnv
emoji: πŸ“ˆ
colorFrom: indigo
colorTo: blue
sdk: docker
pinned: true
tags:
  - openenv
  - reinforcement-learning
  - finance
  - stock-market
  - technical-analysis
  - rl-environment
license: mit
---

# IndicatorsEnv v4.1 β€” Multi-stock Portfolio MDP

> **An OpenEnv-compatible RL environment where agents learn within-sector relative alpha on real NSE (India) equities β€” with genuine portfolio capital dynamics.**

---

## Environment Description

`IndicatorsEnv` simulates the task a quantitative portfolio manager performs: given 3 stocks from the **same sector**, identify which has the strongest relative momentum signal, declare a direction, and set a conviction level. Reward is the **market-neutral alpha** the agent captures β€” the chosen stock's return minus the sector average.

### Why Multi-stock + Portfolio State

Single-stock environments collapse to contextual bandits once you remove multi-step structure. IndicatorsEnv v4.1 solves this with two sources of genuine MDP state:

1. **Signal history** β€” RSI trends, price momentum, and past pick outcomes accumulate across steps. The agent can detect multi-week divergence patterns.
2. **Portfolio capital** β€” `current_holding`, `capital`, and `drawdown` are in every observation. Switching holdings costs 0.1% of capital. This creates a real exploration-exploitation tradeoff: staying in a position avoids transaction cost; switching to a better signal costs capital.

A random policy earns β‰ˆ 0 expected reward (market-neutral by construction). A skilled policy earns consistently positive alpha.

---

## Action Space

```json
{
  "stock":     "HDFCBANK",
  "direction": "Bullish",
  "conviction": 0.8
}
```

| Field | Type | Description |
|---|---|---|
| `stock` | string | NSE symbol to trade, or `"NONE"` to pass this step |
| `direction` | string (enum) | `Bullish` (long alpha), `Bearish` (short alpha), or `NONE` |
| `conviction` | float [0, 1] | Kelly fraction β€” fraction of virtual wealth wagered |

Passing (`NONE`) skips the step with zero reward and preserves the current holding at no cost. Switching to a **different** stock from the one held last step costs 0.1% of current capital.

---

## Observation Space

```json
{
  "step": 3,
  "max_steps": 10,
  "term": "MEDIUM",
  "sector": "banking",
  "available_stocks": ["HDFCBANK", "ICICIBANK", "AXISBANK"],
  "stocks": {
    "HDFCBANK":  { "rsi_14": 61.2, "rsi_trend": "up",   "price_momentum_pct": 2.4, "indicators": {...} },
    "ICICIBANK": { "rsi_14": 54.8, "rsi_trend": "flat", "price_momentum_pct": 0.8, "indicators": {...} },
    "AXISBANK":  { "rsi_14": 48.1, "rsi_trend": "down", "price_momentum_pct": -1.1, "indicators": {...} }
  },
  "signal_history": [
    { "step": 1, "picked_stock": "HDFCBANK", "direction": "Bullish", "correct": true,  "alpha_pct": 1.8, "reward": 0.63 },
    { "step": 2, "picked_stock": "NONE",     "direction": "NONE",    "correct": null,  "alpha_pct": 0.0, "reward": 0.0  }
  ],
  "current_holding": "HDFCBANK",
  "capital": 1.0126,
  "drawdown": 0.0,
  "macro": null
}
```

### Per-stock Indicator Suite (25+ signals)

| Category | Indicators |
|---|---|
| **Moving Averages** | SMA(20/50/200), EMA(20/50), Golden/Death Cross |
| **Momentum** | RSI(14) + trend, MACD(12/26/9) + crossover, Stochastic(14/3) |
| **Volatility** | Bollinger Bands (%, width, squeeze), ATR(14), volatility regime |
| **Trend** | ADX(14), +DI/-DI, trend strength |
| **Volume** | OBV, VWAP, MFI(14), CMF(20), A/D Line, volume ratio |
| **Levels** | Pivot Points (R2/R1/P/S1/S2) |

---

## Tasks

### Task 1: Short-term Relative Alpha *(Easy)*
- **Steps**: 5 (1 trading day apart = 1 week)
- **GT**: 1-day forward return, Β±0.3% threshold
- **Portfolio**: capital tracked, 5% drawdown terminates episode early
- **Grader**: Directional accuracy on active (non-NONE) steps

### Task 2: Medium-term Relative Alpha *(Medium)*
- **Steps**: 10 (5 trading days apart = 10 weeks)
- **GT**: 5-day forward return, Β±1.5% threshold
- **Portfolio**: capital tracked, 10% drawdown terminates episode early
- **Grader**: Weighted accuracy β€” Bearish/Neutral correct = 1.5Γ— weight; participation bonus

### Task 3: Long-term Risk-Constrained Alpha *(Hard)*
- **Steps**: 15 (20 trading days apart = 15 months)
- **GT**: 20-day forward return, Β±2.5% threshold
- **Portfolio**: capital tracked, 15% drawdown terminates episode early; macro context (NIFTY50 trend, market regime) added
- **Grader**: Conviction-calibrated accuracy β€” correct + conviction β‰₯ 0.7 β†’ 1.0; overconfident wrong β†’ βˆ’0.1

---

## Reward Function

```
alpha          = chosen_stock_period_return βˆ’ sector_average_return
direction_sign = +1 (Bullish) or βˆ’1 (Bearish)
raw_reward     = alpha Γ— direction_sign Γ— conviction Γ— 50
tx_cost        = 0.001 Γ— capital  (if switching from a different held stock, else 0)
reward         = clamp(raw_reward βˆ’ tx_cost, βˆ’1.5, 1.5)
```

**Example (medium task, step 3):**
- HDFCBANK returns +3.5%, sector average +1.5%
- alpha = +2.0%, direction = Bullish, conviction = 0.8
- No switch (was already holding HDFCBANK): tx_cost = 0
- reward = 0.02 Γ— 1 Γ— 0.8 Γ— 50 = **0.80**

**Market-neutral property:** Because reward is relative to the sector average, a random policy earns β‰ˆ 0 in expectation. Broad market moves cancel out. The agent profits only from correctly identifying within-sector outperformers.

**Kelly conviction:** Higher conviction amplifies both gains and losses, incentivizing calibrated confidence rather than uniform maximum bets.

---

## Episode Structure

```
reset()  β†’ sector=banking  stocks=[HDFCBANK, ICICIBANK, AXISBANK]  capital=1.0  holding=NONE

step 1   β†’ pick HDFCBANK Bullish 0.7   reward=+0.63  done=False  capital=1.009  tx_cost=0.0
step 2   β†’ NONE                         reward=0.00   done=False  capital=1.009  tx_cost=0.0
step 3   β†’ pick ICICIBANK Bullish 0.8   reward=+0.41  done=False  capital=1.017  tx_cost=0.001  ← switch cost
step 4   β†’ pick ICICIBANK Bullish 0.6   reward=-0.15  done=False  capital=1.008  tx_cost=0.0   ← same stock
step 5   β†’ pick HDFCBANK Bearish 0.5    reward=+0.22  done=True   capital=1.012  tx_cost=0.001  ← switch cost
```

---

## Baseline Scores (Random Agent, seed=42)

| Task | Steps per run | Expected score |
|---|---|---|
| Short-term Direction | 50 (10 eps Γ— 5 steps) | ~0.33 |
| Medium-term Direction | 100 (10 eps Γ— 10 steps) | ~0.33 |
| Long-term Conviction | ≀150 (10 eps Γ— ≀15 steps) | ~0.10 |

---

## Setup & Usage

### Local
```bash
pip install -r requirements-server.txt
cd env/
python indicators_env.py   # Runs on http://localhost:7860
```

### Docker
```bash
docker build -t indicators-env .
docker run -p 7860:7860 indicators-env
```

### Inference Agent
```bash
export API_BASE_URL=https://router.huggingface.co/v1/
export MODEL_NAME=meta-llama/Llama-3.2-1B-Instruct
export HF_TOKEN=hf_your_token

python inference.py --env_url http://localhost:7860 --n_episodes 5
```

---

## Links

- **HF Space (live env)**: [bawsi99/indicators-env](https://huggingface.co/spaces/bawsi99/indicators-env)
- **Fine-tuned LoRA**: [bawsi99/indicators-grpo-qwen7b](https://huggingface.co/bawsi99/indicators-grpo-qwen7b)
- **OpenEnv**: [meta-pytorch/OpenEnv](https://github.com/meta-pytorch/OpenEnv)