claude-evolve 1.7.22 → 1.7.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,907 +0,0 @@
1
- #!/bin/bash
2
-
3
- set -e
4
-
5
- # Load configuration
6
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
- # shellcheck source=../lib/config.sh
8
- source "$SCRIPT_DIR/../lib/config.sh"
9
-
10
- # Use CLAUDE_EVOLVE_CONFIG if set, otherwise default
11
- if [[ -n ${CLAUDE_EVOLVE_CONFIG:-} ]]; then
12
- load_config "$CLAUDE_EVOLVE_CONFIG"
13
- else
14
- load_config
15
- fi
16
-
17
- # Function to determine which model to use based on generation
18
- get_model_for_generation() {
19
- local generation="$1"
20
- local gen_num
21
-
22
- # Extract numeric part of generation (e.g., "05" from gen05)
23
- if [[ $generation =~ ^0*([0-9]+)$ ]]; then
24
- gen_num=$((10#${BASH_REMATCH[1]}))
25
- else
26
- gen_num=1 # Default for malformed input
27
- fi
28
-
29
- # Check which AI tools are available
30
- local has_o3=false
31
- local has_gemini=false
32
-
33
- if command -v codex >/dev/null 2>&1; then
34
- has_o3=true
35
- fi
36
-
37
- if command -v gemini >/dev/null 2>&1; then
38
- has_gemini=true
39
- fi
40
-
41
- # Determine rotation based on what's available
42
- if [[ "$has_o3" == "true" && "$has_gemini" == "true" ]]; then
43
- # All three available: opus -> o3 -> gemini rotation
44
- case $((gen_num % 3)) in
45
- 1) echo "opus" ;; # 1, 4, 7, 10...
46
- 2) echo "o3" ;; # 2, 5, 8, 11...
47
- 0) echo "gemini" ;; # 3, 6, 9, 12...
48
- esac
49
- elif [[ "$has_o3" == "true" ]]; then
50
- # Only opus and o3: alternate between them
51
- if (( gen_num % 2 == 1 )); then
52
- echo "opus" # Odd generations
53
- else
54
- echo "o3" # Even generations
55
- fi
56
- elif [[ "$has_gemini" == "true" ]]; then
57
- # Only opus and gemini: alternate between them
58
- if (( gen_num % 2 == 1 )); then
59
- echo "opus" # Odd generations
60
- else
61
- echo "gemini" # Even generations
62
- fi
63
- else
64
- # Only opus available
65
- echo "opus"
66
- fi
67
- }
68
-
69
- # Helper function to call AI model (alternating based on generation)
70
- call_ai_with_limit_check() {
71
- local prompt="$1"
72
- local generation="${2:-01}" # Default to generation 01 if not provided
73
-
74
- # Determine which model to use for this generation
75
- local preferred_model
76
- preferred_model=$(get_model_for_generation "$generation")
77
-
78
- echo "[INFO] Generation $generation: Using $preferred_model" >&2
79
-
80
- # Try preferred model first
81
- if [[ "$preferred_model" == "o3" ]] && command -v codex >/dev/null 2>&1; then
82
- echo "[INFO] Using codex o3 for ideation" >&2
83
-
84
- # Call codex with o3 model using -q flag and --full-auto
85
- local ai_output
86
- ai_output=$(codex -m o3 --full-auto -q "$prompt" 2>&1)
87
- local ai_exit_code=$?
88
-
89
- if [[ $ai_exit_code -eq 0 ]]; then
90
- # Clean o3 output - it may be JSON with the response in a field
91
- local cleaned_output
92
- # Try to extract content from JSON response if present
93
- if echo "$ai_output" | grep -q '"content"'; then
94
- # Attempt to extract content field from JSON
95
- cleaned_output=$(echo "$ai_output" | python3 -c "
96
- import sys
97
- import json
98
- try:
99
- data = json.load(sys.stdin)
100
- if 'content' in data:
101
- print(data['content'])
102
- elif 'response' in data:
103
- print(data['response'])
104
- elif 'text' in data:
105
- print(data['text'])
106
- else:
107
- # If no known field, print the whole thing
108
- print(json.dumps(data))
109
- except:
110
- # If not valid JSON, print as-is
111
- print(sys.stdin.read())
112
- " 2>/dev/null || echo "$ai_output")
113
- else
114
- cleaned_output="$ai_output"
115
- fi
116
-
117
- # Validate the output is not empty and doesn't contain error messages
118
- if [[ -n "$cleaned_output" ]] && ! echo "$cleaned_output" | grep -q "error\|failed\|exception"; then
119
- echo "$cleaned_output"
120
- return 0
121
- else
122
- echo "[WARN] Codex o3 returned invalid output, falling back to Claude Opus" >&2
123
- preferred_model="opus"
124
- fi
125
- else
126
- echo "[WARN] Codex o3 failed with exit code $ai_exit_code, falling back to Claude Opus" >&2
127
- preferred_model="opus"
128
- fi
129
- elif [[ "$preferred_model" == "gemini" ]] && command -v gemini >/dev/null 2>&1; then
130
- echo "[INFO] Using gemini 2.5 pro for ideation" >&2
131
-
132
- # Call gemini with -y and -p flags
133
- local ai_output
134
- ai_output=$(gemini -y -p "$prompt" 2>&1)
135
- local ai_exit_code=$?
136
-
137
- if [[ $ai_exit_code -eq 0 ]]; then
138
- # Check for authentication messages or other non-response content
139
- if echo "$ai_output" | grep -q "Attempting to authenticate\|Authenticating\|Loading\|Initializing"; then
140
- echo "[WARN] Gemini is still authenticating, falling back to Claude Opus" >&2
141
- preferred_model="opus"
142
- elif [[ -z "$ai_output" ]] || [[ $(echo "$ai_output" | wc -l) -lt 2 ]]; then
143
- echo "[WARN] Gemini returned insufficient output, falling back to Claude Opus" >&2
144
- preferred_model="opus"
145
- else
146
- echo "$ai_output"
147
- return 0
148
- fi
149
- else
150
- echo "[WARN] Gemini failed with exit code $ai_exit_code, falling back to Claude Opus" >&2
151
- preferred_model="opus"
152
- fi
153
- fi
154
-
155
- # Use Claude with preferred model (or fallback)
156
- echo "[INFO] Using Claude $preferred_model for ideation" >&2
157
-
158
- # Call Claude and capture output
159
- local claude_output
160
- claude_output=$(echo "$prompt" | claude --dangerously-skip-permissions --model "$preferred_model" -p 2>&1)
161
- local claude_exit_code=$?
162
-
163
- # Check for usage limit
164
- if echo "$claude_output" | grep -q "Claude AI usage limit reached"; then
165
- # Extract timestamp if available
166
- local limit_timestamp=$(echo "$claude_output" | grep -o "Claude AI usage limit reached|[0-9]*" | cut -d'|' -f2)
167
-
168
- # Print red error message
169
- echo -e "\033[31m[ERROR] CLAUDE AI USAGE LIMIT REACHED!\033[0m" >&2
170
- echo -e "\033[31m[ERROR] Ideation halted due to API rate limits.\033[0m" >&2
171
-
172
- if [[ -n $limit_timestamp ]]; then
173
- # Convert timestamp to human-readable format
174
- local limit_date=$(date -r "$limit_timestamp" "+%Y-%m-%d %H:%M:%S" 2>/dev/null || echo "Unknown time")
175
- echo -e "\033[31m[ERROR] Limit will be released at: $limit_date\033[0m" >&2
176
- fi
177
-
178
- echo -e "\033[33m[INFO] Please wait for the rate limit to reset before continuing.\033[0m" >&2
179
- echo -e "\033[33m[INFO] No ideas were generated. Run ideate again when the limit resets.\033[0m" >&2
180
-
181
- exit 1
182
- fi
183
-
184
- # Validate output doesn't contain shell constructs that could corrupt CSV
185
- if echo "$claude_output" | grep -E "EOF.*<.*null|<<.*EOF|<.*dev.*null" >/dev/null 2>&1; then
186
- echo "[ERROR] AI output contains invalid shell constructs" >&2
187
- echo "[DEBUG] Problematic output: $claude_output" | head -5 >&2
188
- return 1
189
- fi
190
-
191
- # Output Claude's response
192
- echo "$claude_output"
193
-
194
- return $claude_exit_code
195
- }
196
-
197
- # Backward compatibility alias
198
- call_claude_with_limit_check() {
199
- call_ai_with_limit_check "$@"
200
- }
201
-
202
- # Parse arguments
203
- use_strategies=true
204
-
205
- while [[ $# -gt 0 ]]; do
206
- case $1 in
207
- --help)
208
- cat <<EOF
209
- claude-evolve ideate - Generate new algorithm ideas using evolutionary strategies
210
-
211
- USAGE:
212
- claude-evolve ideate [--legacy N]
213
-
214
- OPTIONS:
215
- --legacy N Use legacy mode with N ideas (ignores strategy config)
216
- --help Show this help message
217
-
218
- DESCRIPTION:
219
- Generates algorithm ideas using multi-strategy evolutionary approach:
220
- - Novel exploration: Pure creativity, global search
221
- - Hill climbing: Parameter tuning of top performers
222
- - Structural mutation: Algorithmic changes to top performers
223
- - Crossover hybrid: Combine successful approaches
224
-
225
- Strategy distribution is configured in evolution/config.yaml
226
- EOF
227
- exit 0
228
- ;;
229
- --legacy)
230
- use_strategies=false
231
- shift
232
- if [[ $1 =~ ^[0-9]+$ ]]; then
233
- TOTAL_IDEAS=$1
234
- shift
235
- else
236
- echo "[ERROR] --legacy requires a number" >&2
237
- exit 1
238
- fi
239
- ;;
240
- *)
241
- echo "[ERROR] Unknown option: $1" >&2
242
- exit 1
243
- ;;
244
- esac
245
- done
246
-
247
- # Check workspace using config
248
- if [[ ! -d "$FULL_EVOLUTION_DIR" ]]; then
249
- echo "[ERROR] Evolution workspace not found: $FULL_EVOLUTION_DIR. Run 'claude-evolve setup' first." >&2
250
- exit 1
251
- fi
252
-
253
- # Ensure CSV exists
254
- if [[ ! -f "$FULL_CSV_PATH" ]]; then
255
- echo "id,basedOnId,description,performance,status" >"$FULL_CSV_PATH"
256
- fi
257
-
258
- # Validate strategy configuration
259
- if [[ $use_strategies == true ]]; then
260
- total_check=$((NOVEL_EXPLORATION + HILL_CLIMBING + STRUCTURAL_MUTATION + CROSSOVER_HYBRID))
261
- if [[ $total_check -ne $TOTAL_IDEAS ]]; then
262
- echo "[ERROR] Strategy counts don't sum to total_ideas ($total_check != $TOTAL_IDEAS)" >&2
263
- echo "Check your evolution/config.yaml configuration" >&2
264
- exit 1
265
- fi
266
- fi
267
-
268
- # Get next generation number
269
- get_next_generation() {
270
- if [[ ! -f "$FULL_CSV_PATH" ]]; then
271
- echo "01"
272
- return
273
- fi
274
-
275
- # Use Python for proper CSV parsing
276
- local max_gen
277
- max_gen=$("$PYTHON_CMD" -c "
278
- import csv
279
- max_gen = 0
280
- with open('$FULL_CSV_PATH', 'r') as f:
281
- reader = csv.reader(f)
282
- next(reader, None) # Skip header
283
- for row in reader:
284
- if row and len(row) > 0:
285
- id_field = row[0].strip()
286
- if id_field.startswith('gen') and '-' in id_field:
287
- try:
288
- gen_part = id_field.split('-')[0] # e.g., 'gen01'
289
- gen_num = int(gen_part[3:]) # Extract number after 'gen'
290
- max_gen = max(max_gen, gen_num)
291
- except (ValueError, IndexError):
292
- pass
293
- print(max_gen)
294
- ")
295
-
296
- # Increment and format with leading zero
297
- printf "%02d" $((max_gen + 1))
298
- }
299
-
300
- # Get next available ID number for current generation
301
- get_next_id_number() {
302
- "$PYTHON_CMD" -c "
303
- import csv
304
- import re
305
- max_id = 0
306
- pattern = re.compile(r'^gen$CURRENT_GENERATION-(\d+)$')
307
- with open('$FULL_CSV_PATH', 'r') as f:
308
- reader = csv.reader(f)
309
- next(reader, None) # Skip header
310
- for row in reader:
311
- if row and len(row) > 0:
312
- match = pattern.match(row[0].strip())
313
- if match:
314
- max_id = max(max_id, int(match.group(1)))
315
- print(max_id + 1)
316
- "
317
- }
318
-
319
- # Process AI output and add ideas to CSV
320
- process_ai_ideas() {
321
- local ai_output="$1"
322
- local count="$2"
323
- local idea_type="$3" # novel, hill-climbing, structural, crossover
324
- local top_performers="${4:-}" # Optional, for non-novel ideas
325
-
326
- local next_id_num=$(get_next_id_number)
327
- local ideas_added=0
328
-
329
- while IFS= read -r line && [[ $ideas_added -lt $count ]]; do
330
- # Skip empty lines
331
- [[ -z "$line" || "$line" =~ ^[[:space:]]*$ ]] && continue
332
-
333
- # Skip lines that look like headers or metadata
334
- [[ "$line" =~ ^#|^\[|^==|^-- ]] && continue
335
-
336
- # Clean the line
337
- line=$(echo "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
338
- line=$(echo "$line" | sed 's/^[0-9]\+\.\?[[:space:]]*//') # Remove numbering
339
- line=$(echo "$line" | sed 's/^-[[:space:]]*//') # Remove bullet points
340
-
341
- # Extract parent ID and description based on type
342
- local parent_id=""
343
- local description="$line"
344
-
345
- if [[ "$idea_type" != "novel" ]]; then
346
- # For non-novel ideas, extract parent ID from "From X:" format
347
- if [[ "$line" =~ ^From[[:space:]]+([^:]+):[[:space:]]*(.+)$ ]]; then
348
- parent_id="${BASH_REMATCH[1]}"
349
- description="${BASH_REMATCH[2]}"
350
- else
351
- # If no parent specified, use the first from top_performers
352
- parent_id=$(echo "$top_performers" | head -1 | cut -d',' -f1)
353
- fi
354
- fi
355
-
356
- # Skip if description is too short or contains problematic content
357
- [[ ${#description} -lt 20 ]] && continue
358
- if echo "$description" | grep -qE 'EOF|/dev/null|<<<|>>>'; then
359
- continue
360
- fi
361
-
362
- # Skip AI system messages and errors
363
- if echo "$description" | grep -qiE 'loaded.*cached.*credentials|authenticating|loading.*model|initializing|error.*occurred|failed.*to.*load|api.*key|rate.*limit|connection.*error|timeout|please.*try.*again|authentication.*failed|invalid.*request|model.*not.*available'; then
364
- echo "[WARN] Skipping AI system message: $description" >&2
365
- continue
366
- fi
367
-
368
- # Skip responses that don't look like algorithm descriptions
369
- if ! echo "$description" | grep -qiE 'algorithm|strategy|trading|position|signal|indicator|portfolio|risk|stop.?loss|profit|market|price|volatility|momentum|trend|regime|filter|threshold|window|period|weight|allocation|component'; then
370
- echo "[WARN] Skipping non-algorithm description: $description" >&2
371
- continue
372
- fi
373
-
374
- # Generate ID for this idea
375
- local idea_id=$(printf "gen%s-%03d" "$CURRENT_GENERATION" $((next_id_num + ideas_added)))
376
-
377
- # Escape quotes in description for CSV
378
- description="${description//\"/\"\"}"
379
-
380
- # Append to CSV
381
- echo "$idea_id,$parent_id,\"$description\",,pending" >> "$FULL_CSV_PATH"
382
-
383
- ((ideas_added++))
384
- if [[ -n "$parent_id" ]]; then
385
- echo "[INFO] Added $idea_type idea $idea_id (parent: $parent_id): ${description:0:50}..."
386
- else
387
- echo "[INFO] Added $idea_type idea $idea_id: ${description:0:70}..."
388
- fi
389
- done <<< "$ai_output"
390
-
391
- if [[ $ideas_added -lt $count ]]; then
392
- echo "[WARN] Only generated $ideas_added out of $count requested $idea_type ideas" >&2
393
- fi
394
-
395
- # Return error if no valid ideas were generated
396
- if [[ $ideas_added -eq 0 ]]; then
397
- echo "[ERROR] Failed to generate any valid $idea_type ideas - AI may be returning system messages" >&2
398
- return 1
399
- fi
400
-
401
- return 0
402
- }
403
-
404
- # Get next available ID for current generation
405
- get_next_id() {
406
- local generation="$1"
407
- if [[ ! -f "$FULL_CSV_PATH" ]]; then
408
- echo "gen${generation}-001"
409
- return
410
- fi
411
-
412
- # Use Python for proper CSV parsing
413
- local max_id
414
- max_id=$("$PYTHON_CMD" -c "
415
- import csv
416
- import re
417
- max_id = 0
418
- pattern = re.compile(r'^gen${generation}-(\d+)$')
419
- with open('$FULL_CSV_PATH', 'r') as f:
420
- reader = csv.reader(f)
421
- next(reader, None) # Skip header
422
- for row in reader:
423
- if row and len(row) > 0:
424
- id_field = row[0].strip()
425
- match = pattern.match(id_field)
426
- if match:
427
- id_num = int(match.group(1))
428
- max_id = max(max_id, id_num)
429
- print(max_id)
430
- ")
431
-
432
- # Format next ID with generation and 3-digit number
433
- printf "gen%s-%03d" "$generation" $((max_id + 1))
434
- }
435
-
436
-
437
- # Get top performers for parent selection (absolute + top novel candidates)
438
- get_top_performers() {
439
- local num_requested="$1"
440
- if [[ ! -f "$FULL_CSV_PATH" ]]; then
441
- echo ""
442
- return
443
- fi
444
-
445
- # Use Python to properly parse CSV and find top performers + top novel candidates
446
- "$PYTHON_CMD" -c "
447
- import csv
448
- import sys
449
-
450
- with open('$FULL_CSV_PATH', 'r') as f:
451
- reader = csv.reader(f)
452
- next(reader) # Skip header
453
-
454
- completed = []
455
- novel = []
456
-
457
- # Collect all completed candidates
458
- for row in reader:
459
- if len(row) >= 5 and row[3] and row[4] == 'complete':
460
- try:
461
- candidate_id = row[0]
462
- parent_id = row[1] if len(row) > 1 else ''
463
- description = row[2] if len(row) > 2 else ''
464
- score = float(row[3])
465
-
466
- completed.append((candidate_id, description, score))
467
-
468
- # Track novel candidates separately
469
- if not parent_id:
470
- novel.append((candidate_id, description, score))
471
-
472
- except ValueError:
473
- pass
474
-
475
- # Sort absolute leaders by score (descending)
476
- completed.sort(key=lambda x: x[2], reverse=True)
477
-
478
- # Sort novel candidates by score (descending)
479
- novel.sort(key=lambda x: x[2], reverse=True)
480
-
481
- # Collect top performers
482
- selected_ids = set()
483
- results = []
484
-
485
- # Add top absolute performers
486
- for i, (candidate_id, description, score) in enumerate(completed[:$num_requested]):
487
- results.append(f'{candidate_id},{description},{score}')
488
- selected_ids.add(candidate_id)
489
-
490
- # Add top novel candidates (if not already selected)
491
- novel_count = 0
492
- for candidate_id, description, score in novel:
493
- if candidate_id not in selected_ids and novel_count < $NUM_REVOLUTION:
494
- results.append(f'{candidate_id},{description},{score}')
495
- selected_ids.add(candidate_id)
496
- novel_count += 1
497
-
498
- # Output all selected candidates
499
- for result in results:
500
- print(result)
501
- "
502
- }
503
-
504
-
505
-
506
- # Generate ideas using AI with multi-strategy approach
507
- ideate_ai_strategies() {
508
- if [[ ! -f "$FULL_BRIEF_PATH" ]]; then
509
- echo "[ERROR] $BRIEF_FILE not found. Run 'claude-evolve setup' first." >&2
510
- exit 1
511
- fi
512
-
513
- # Baseline should already be evaluated by run command
514
-
515
- # Get top performers (now includes top novel candidates)
516
- local top_performers
517
- top_performers=$(get_top_performers "$NUM_ELITES")
518
-
519
- if [[ -z $top_performers ]]; then
520
- echo "[INFO] No completed algorithms found, will use baseline algorithm for hill climbing"
521
- # For hill climbing and mutations, use the baseline algorithm
522
- top_performers="Baseline Algorithm (algorithm.py): The original algorithm provided"
523
- fi
524
-
525
- echo "[INFO] Generating $TOTAL_IDEAS ideas using multi-strategy approach:"
526
- echo " Novel exploration: $NOVEL_EXPLORATION"
527
- echo " Hill climbing: $HILL_CLIMBING"
528
- echo " Structural mutation: $STRUCTURAL_MUTATION"
529
- echo " Crossover hybrid: $CROSSOVER_HYBRID"
530
-
531
- # Generate each type of idea by having Claude directly edit the CSV
532
- [[ $NOVEL_EXPLORATION -gt 0 ]] && generate_novel_ideas_direct "$NOVEL_EXPLORATION"
533
- [[ $HILL_CLIMBING -gt 0 ]] && generate_hill_climbing_direct "$HILL_CLIMBING" "$top_performers"
534
- [[ $STRUCTURAL_MUTATION -gt 0 ]] && generate_structural_mutation_direct "$STRUCTURAL_MUTATION" "$top_performers"
535
- [[ $CROSSOVER_HYBRID -gt 0 ]] && generate_crossover_direct "$CROSSOVER_HYBRID" "$top_performers"
536
- }
537
-
538
- # Generate novel exploration ideas using structured output
539
- generate_novel_ideas_direct() {
540
- local count="$1"
541
-
542
- # Get next available ID for this generation
543
- local next_id_num
544
- next_id_num=$("$PYTHON_CMD" -c "
545
- import csv
546
- import re
547
- max_id = 0
548
- pattern = re.compile(r'^gen$CURRENT_GENERATION-(\d+)$')
549
- with open('$FULL_CSV_PATH', 'r') as f:
550
- reader = csv.reader(f)
551
- next(reader, None) # Skip header
552
- for row in reader:
553
- if row and len(row) > 0:
554
- match = pattern.match(row[0].strip())
555
- if match:
556
- max_id = max(max_id, int(match.group(1)))
557
- print(max_id + 1)
558
- ")
559
-
560
- local prompt="Generate exactly $count novel algorithmic ideas for trading algorithm evolution.
561
-
562
- Current evolution context:
563
- - Generation: $CURRENT_GENERATION
564
- - Algorithm: $FULL_ALGORITHM_PATH
565
- - Brief: $(head -20 "$FULL_BRIEF_PATH")
566
-
567
- You MUST output EXACTLY $count lines, where each line is a single sentence describing a specific algorithmic change.
568
- NO PREAMBLE, NO EXPLANATION, NO NUMBERING - just $count lines of algorithm descriptions.
569
-
570
- Requirements:
571
- - Each line should be one clear sentence describing a novel algorithmic approach
572
- - Focus on creative, ambitious ideas that haven't been tried yet
573
- - Consider machine learning, new indicators, regime detection, risk management, etc.
574
-
575
- Example output format:
576
- Train LSTM network on 30-day OHLCV sequences to predict next-day direction probability
577
- Add cross-correlation filter that reduces positions when correlation with market breaks down
578
- Implement intraday momentum using 30-minute data to adjust daily position sizes
579
-
580
- Output exactly $count lines now:"
581
-
582
- echo "[INFO] Generating $count novel exploration ideas..."
583
-
584
- # Get AI response
585
- local ai_output
586
- local ai_error
587
- if ! ai_output=$(call_ai_with_limit_check "$prompt" "$CURRENT_GENERATION" 2>&1); then
588
- ai_error="$ai_output"
589
-
590
- # Check for specific error types
591
- if echo "$ai_error" | grep -qi "rate.*limit\|too.*many.*requests"; then
592
- echo "[ERROR] Hit API rate limit. Please wait a moment and try again." >&2
593
- elif echo "$ai_error" | grep -qi "unauthorized\|api.*key\|authentication"; then
594
- # Try to identify which provider failed
595
- if echo "$ai_error" | grep -qi "codex\|o3"; then
596
- echo "[ERROR] Codex/O3 authentication issue. Please check your Codex API key." >&2
597
- elif echo "$ai_error" | grep -qi "gemini"; then
598
- echo "[ERROR] Gemini authentication issue. Please check your Gemini API key." >&2
599
- elif echo "$ai_error" | grep -qi "claude\|anthropic"; then
600
- echo "[ERROR] Claude authentication issue. Please check your Anthropic API key." >&2
601
- else
602
- echo "[ERROR] API authentication issue. Please check your API key configuration." >&2
603
- echo "[ERROR] Last attempted model was from generation $CURRENT_GENERATION strategy" >&2
604
- fi
605
- elif echo "$ai_error" | grep -qi "timeout\|timed.*out"; then
606
- echo "[ERROR] Request timed out. The prompt may be too complex." >&2
607
- else
608
- echo "[ERROR] AI request failed: $ai_error" >&2
609
- fi
610
- return 1
611
- fi
612
-
613
- # Process the output using helper function
614
- process_ai_ideas "$ai_output" "$count" "novel"
615
-
616
- echo "[INFO] Novel exploration ideas generated successfully"
617
- }
618
-
619
- # Generate hill climbing ideas by getting descriptions from AI
620
- generate_hill_climbing_direct() {
621
- local count="$1"
622
- local top_performers="$2"
623
-
624
- # Get next available ID for this generation
625
- local next_id_num
626
- next_id_num=$("$PYTHON_CMD" -c "
627
- import csv
628
- import re
629
- max_id = 0
630
- pattern = re.compile(r'^gen$CURRENT_GENERATION-(\d+)$')
631
- with open('$FULL_CSV_PATH', 'r') as f:
632
- reader = csv.reader(f)
633
- next(reader, None) # Skip header
634
- for row in reader:
635
- if row and len(row) > 0:
636
- match = pattern.match(row[0].strip())
637
- if match:
638
- max_id = max(max_id, int(match.group(1)))
639
- print(max_id + 1)
640
- ")
641
-
642
- local prompt="Generate exactly $count parameter tuning ideas for successful trading algorithms.
643
-
644
- Successful algorithms to tune (pick one as parent):
645
- $top_performers
646
-
647
- You MUST output EXACTLY $count lines, where each line describes a parameter tuning idea.
648
- NO PREAMBLE, NO EXPLANATION, NO NUMBERING - just $count lines of parameter tuning descriptions.
649
-
650
- Each line should:
651
- - Be one clear sentence about adjusting specific parameters
652
- - Reference the parent algorithm ID at the beginning like \"From gen02-003:\"
653
- - Focus on hyperparameters, thresholds, periods, weights, etc.
654
-
655
- Example output format:
656
- From gen02-003: Lower IBS_BUY_THRESHOLD from 0.15 to 0.12 to enter deeper oversold conditions
657
- From gen02-003: Increase TRS_RSI_PERIOD from 2 to 3 for smoother RSI signals
658
- From gen02-003: Raise WEIGHT_TDD from 0.38 to 0.42 to emphasize best performing strategy
659
-
660
- Output exactly $count lines now:"
661
-
662
- echo "[INFO] Generating $count hill climbing ideas..."
663
-
664
- # Get AI response
665
- local ai_output
666
- local ai_error
667
- if ! ai_output=$(call_ai_with_limit_check "$prompt" "$CURRENT_GENERATION" 2>&1); then
668
- ai_error="$ai_output"
669
-
670
- # Check for specific error types
671
- if echo "$ai_error" | grep -qi "rate.*limit\|too.*many.*requests"; then
672
- echo "[ERROR] Hit API rate limit. Please wait a moment and try again." >&2
673
- elif echo "$ai_error" | grep -qi "unauthorized\|api.*key\|authentication"; then
674
- echo "[ERROR] API authentication issue. Please check your API key." >&2
675
- elif echo "$ai_error" | grep -qi "timeout\|timed.*out"; then
676
- echo "[ERROR] Request timed out. The prompt may be too complex." >&2
677
- elif echo "$ai_error" | grep -qi "token.*limit\|too.*long\|maximum.*length"; then
678
- echo "[ERROR] Prompt exceeded token limit. Try reducing the number of top performers." >&2
679
- else
680
- echo "[ERROR] AI request failed: $ai_error" >&2
681
- fi
682
-
683
- echo "[INFO] Hill climbing can work with ANY completed algorithm - even baseline" >&2
684
- echo "[INFO] Each algorithm has parameters that can be tuned for improvement" >&2
685
- return 1
686
- fi
687
-
688
- # Process the output using helper function
689
- process_ai_ideas "$ai_output" "$count" "hill-climbing" "$top_performers"
690
-
691
- echo "[INFO] Hill climbing ideas generated successfully"
692
- }
693
-
694
- # Generate structural mutation ideas by getting descriptions from AI
695
- generate_structural_mutation_direct() {
696
- local count="$1"
697
- local top_performers="$2"
698
-
699
- local prompt="Generate exactly $count structural modification ideas for successful trading algorithms.
700
-
701
- Successful algorithms to modify structurally:
702
- $top_performers
703
-
704
- You MUST output EXACTLY $count lines, where each line describes a structural modification idea.
705
- NO PREAMBLE, NO EXPLANATION, NO NUMBERING - just $count lines of structural change descriptions.
706
-
707
- Each line should:
708
- - Be one clear sentence about architectural/structural changes
709
- - Reference the parent algorithm ID at the beginning like \"From gen02-003:\"
710
- - Focus on replacing components, changing architecture, adding new systems
711
-
712
- Example output format:
713
- From gen02-003: Replace 2-period RSI with LSTM-predicted momentum scores for TRS strategy
714
- From gen02-003: Add ensemble voting system where sub-strategies vote on market regime
715
- From gen02-003: Implement hierarchical risk budgeting with correlation-adjusted position sizing
716
-
717
- Output exactly $count lines now:"
718
-
719
- echo "[INFO] Generating $count structural mutation ideas..."
720
-
721
- # Get AI response
722
- local ai_output
723
- local ai_error
724
- if ! ai_output=$(call_ai_with_limit_check "$prompt" "$CURRENT_GENERATION" 2>&1); then
725
- ai_error="$ai_output"
726
-
727
- # Check for specific error types
728
- if echo "$ai_error" | grep -qi "rate.*limit\|too.*many.*requests"; then
729
- echo "[ERROR] Hit API rate limit. Please wait a moment and try again." >&2
730
- elif echo "$ai_error" | grep -qi "unauthorized\|api.*key\|authentication"; then
731
- # Try to identify which provider failed
732
- if echo "$ai_error" | grep -qi "codex\|o3"; then
733
- echo "[ERROR] Codex/O3 authentication issue. Please check your Codex API key." >&2
734
- elif echo "$ai_error" | grep -qi "gemini"; then
735
- echo "[ERROR] Gemini authentication issue. Please check your Gemini API key." >&2
736
- elif echo "$ai_error" | grep -qi "claude\|anthropic"; then
737
- echo "[ERROR] Claude authentication issue. Please check your Anthropic API key." >&2
738
- else
739
- echo "[ERROR] API authentication issue. Please check your API key configuration." >&2
740
- echo "[ERROR] Last attempted model was from generation $CURRENT_GENERATION strategy" >&2
741
- fi
742
- elif echo "$ai_error" | grep -qi "timeout\|timed.*out"; then
743
- echo "[ERROR] Request timed out. The prompt may be too complex." >&2
744
- else
745
- echo "[ERROR] AI request failed: $ai_error" >&2
746
- fi
747
- return 1
748
- fi
749
-
750
- # Process the output using helper function
751
- process_ai_ideas "$ai_output" "$count" "structural" "$top_performers"
752
-
753
- echo "[INFO] Structural mutation ideas generated successfully"
754
- }
755
-
756
- # Generate crossover hybrid ideas by getting descriptions from AI
757
- generate_crossover_direct() {
758
- local count="$1"
759
- local top_performers="$2"
760
-
761
- local prompt="Generate exactly $count hybrid combination ideas from successful trading algorithms.
762
-
763
- Top performers to combine (reference at least 2 in each idea):
764
- $top_performers
765
-
766
- You MUST output EXACTLY $count lines, where each line describes a hybrid combination idea.
767
- NO PREAMBLE, NO EXPLANATION, NO NUMBERING - just $count lines of hybrid combination descriptions.
768
-
769
- Each line should:
770
- - Be one clear sentence combining elements from 2+ successful algorithms
771
- - Reference the base parent algorithm ID at the beginning like \"From gen02-003:\"
772
- - Explicitly mention which elements to combine from which algorithms
773
-
774
- Example output format:
775
- From gen02-003: Combine VIX regime filter from gen02-003 with LSTM predictions from gen01-005
776
- From gen02-003: Merge volatility regime detection from gen02-003 with ML momentum from gen01-007
777
- From gen02-003: Integrate multi-timeframe signals from gen02-003 with correlation sizing from gen01-009
778
-
779
- Output exactly $count lines now:"
780
-
781
- echo "[INFO] Generating $count crossover hybrid ideas..."
782
-
783
- # Get AI response
784
- local ai_output
785
- local ai_error
786
- if ! ai_output=$(call_ai_with_limit_check "$prompt" "$CURRENT_GENERATION" 2>&1); then
787
- ai_error="$ai_output"
788
-
789
- # Check for specific error types
790
- if echo "$ai_error" | grep -qi "rate.*limit\|too.*many.*requests"; then
791
- echo "[ERROR] Hit API rate limit. Please wait a moment and try again." >&2
792
- elif echo "$ai_error" | grep -qi "unauthorized\|api.*key\|authentication"; then
793
- # Try to identify which provider failed
794
- if echo "$ai_error" | grep -qi "codex\|o3"; then
795
- echo "[ERROR] Codex/O3 authentication issue. Please check your Codex API key." >&2
796
- elif echo "$ai_error" | grep -qi "gemini"; then
797
- echo "[ERROR] Gemini authentication issue. Please check your Gemini API key." >&2
798
- elif echo "$ai_error" | grep -qi "claude\|anthropic"; then
799
- echo "[ERROR] Claude authentication issue. Please check your Anthropic API key." >&2
800
- else
801
- echo "[ERROR] API authentication issue. Please check your API key configuration." >&2
802
- echo "[ERROR] Last attempted model was from generation $CURRENT_GENERATION strategy" >&2
803
- fi
804
- elif echo "$ai_error" | grep -qi "timeout\|timed.*out"; then
805
- echo "[ERROR] Request timed out. The prompt may be too complex." >&2
806
- else
807
- echo "[ERROR] AI request failed: $ai_error" >&2
808
- fi
809
- return 1
810
- fi
811
-
812
- # Process the output using helper function
813
- process_ai_ideas "$ai_output" "$count" "crossover" "$top_performers"
814
-
815
- echo "[INFO] Crossover hybrid ideas generated successfully"
816
- }
817
-
818
- # Legacy AI generation mode (for backward compatibility)
819
- ideate_ai_legacy() {
820
- if [[ ! -f "$FULL_BRIEF_PATH" ]]; then
821
- echo "[ERROR] $BRIEF_FILE not found. Run 'claude-evolve setup' first." >&2
822
- exit 1
823
- fi
824
-
825
- # Get top performers for context
826
- local top_performers=""
827
- if [[ -f "$FULL_CSV_PATH" ]]; then
828
- # Simple top performers extraction (lines with non-empty performance)
829
- top_performers=$(awk -F, 'NR > 1 && $4 != "" { print $1 ": " $3 " (score: " $4 ")" }' "$FULL_CSV_PATH" | head -5)
830
- fi
831
-
832
- # Build prompt for description-only output
833
- local prompt="Generate exactly $TOTAL_IDEAS novel algorithmic ideas for trading algorithm evolution.
834
-
835
- Algorithm files for context:
836
- - Base algorithm: $FULL_ALGORITHM_PATH
837
- - Evolved algorithms: $FULL_OUTPUT_DIR/evolution_*.py
838
-
839
- IMPORTANT: Before generating ideas, you should:
840
- 1. Read the base algorithm to understand the codebase structure and possibilities
841
- 2. Read ALL existing evolution_*.py files to see what modifications have been attempted
842
- 3. Consider which approaches might work well
843
-
844
- Project Brief:
845
- $(cat "$FULL_BRIEF_PATH")"
846
-
847
- if [[ -n $top_performers ]]; then
848
- prompt+="
849
-
850
- Top Performing Algorithms So Far:
851
- $top_performers"
852
- fi
853
-
854
- prompt+="
855
-
856
- You MUST output EXACTLY $TOTAL_IDEAS lines, where each line is a single sentence describing a specific algorithmic change.
857
- NO PREAMBLE, NO EXPLANATION, NO NUMBERING - just $TOTAL_IDEAS lines of algorithm descriptions.
858
-
859
- Requirements:
860
- - Each line should be one clear sentence describing an algorithmic approach
861
- - Mix both parameter tuning and structural changes
862
- - If building on existing algorithms, start with 'From ALGORITHM_ID:'
863
-
864
- ⚠️ AVOID ONLY: Kelly floor/cap adjustments that assume leverage > 1.0 (these get clamped and have no effect)
865
-
866
- ✅ EXPLORE ALL CREATIVE POSSIBILITIES INCLUDING:
867
- - Machine Learning: Neural networks, ensemble methods, reinforcement learning (use train() method)
868
- - Advanced Indicators: Custom combinations, multi-timeframe signals, cross-asset indicators
869
- - Market Regime Detection: VIX patterns, correlation analysis, volatility clustering
870
- - Risk Management: Dynamic stops, portfolio heat, correlation-based position sizing
871
- - Alternative Strategies: New sub-strategies, momentum variants, mean reversion innovations
872
- - Multi-Asset Signals: Sector rotation, bond yields, commodity signals
873
- - Time-Based Patterns: Intraday effects, calendar anomalies, volatility timing
874
- - Parameter Optimization: Entry thresholds, indicator periods, strategy weights
875
-
876
- Output exactly $TOTAL_IDEAS lines now:"
877
-
878
- echo "[INFO] Generating $TOTAL_IDEAS ideas (legacy mode)..."
879
-
880
- # Get AI response
881
- local ai_output
882
- if ! ai_output=$(call_ai_with_limit_check "$prompt" "$CURRENT_GENERATION" 2>&1); then
883
- echo "[WARN] AI failed to generate ideas" >&2
884
- return 1
885
- fi
886
-
887
- # Process the output using helper function
888
- # For legacy mode, we treat all as "novel" since we're not specifying a strategy
889
- process_ai_ideas "$ai_output" "$TOTAL_IDEAS" "novel"
890
-
891
- echo "[INFO] Legacy ideas generated"
892
- }
893
-
894
- # Determine generation number for this ideation run
895
- CURRENT_GENERATION=$(get_next_generation)
896
- echo "[INFO] Starting ideation for generation $CURRENT_GENERATION"
897
-
898
- # Main execution
899
- if [[ $use_strategies == true ]]; then
900
- echo "[INFO] Multi-strategy AI generation mode"
901
- ideate_ai_strategies
902
- echo "[INFO] Ideation complete! Check $EVOLUTION_CSV for new ideas."
903
- else
904
- echo "[INFO] Legacy AI generation mode"
905
- ideate_ai_legacy
906
- echo "[INFO] Ideation complete! Check $EVOLUTION_CSV for new ideas."
907
- fi