Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 33 additions & 21 deletions .github/workflows/generate-indicators.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ on:
push:
branches: [main]
paths:
# Trigger when PineScript source files change
- 'example/pinescript/**/*.pine'
# Trigger when indicator sources config changes
- 'indicators/sources.json'
# Or when official PineScript files change
- 'docs/official/indicators_standard/**/*.pine'
# Or when transpiler changes
- 'packages/pine2ts/**'
workflow_dispatch: # Allow manual trigger
Expand Down Expand Up @@ -38,25 +40,35 @@ jobs:

- name: Generate indicators
run: |
# Find all .pine files and transpile them
for pine_file in example/pinescript/*.pine; do
if [ -f "$pine_file" ]; then
# Extract base name and convert to kebab-case
base_name=$(basename "$pine_file" .pine)
# Replace spaces first, then convert camelCase to kebab-case
indicator_name=$(echo "$base_name" | tr ' ' '-' | sed 's/\([a-z0-9]\)\([A-Z]\)/\1-\2/g' | tr '[:upper:]' '[:lower:]')
output_dir="indicators/$indicator_name"
output_file="$output_dir/$indicator_name.ts"

echo "Transpiling $pine_file -> $output_file"

# Create output directory if it doesn't exist
mkdir -p "$output_dir"

# Run the transpiler
node ./packages/pine2ts/bin/pine2ts.js "$pine_file" "$output_file"
fi
done
# Read the sources.json and transpile each indicator
node << 'EOF'
const fs = require('fs');
const { execSync } = require('child_process');
const path = require('path');

const sources = JSON.parse(fs.readFileSync('indicators/sources.json', 'utf-8'));

for (const indicator of sources.indicators) {
const sourcePath = indicator.sourcePath;
const outputDir = `indicators/${indicator.id}`;
const outputFile = `${outputDir}/${indicator.id}.ts`;

if (!fs.existsSync(sourcePath)) {
console.error(`Source file not found: ${sourcePath}`);
continue;
}

console.log(`Transpiling ${indicator.name} -> ${outputFile}`);

// Create output directory
fs.mkdirSync(outputDir, { recursive: true });

// Run transpiler
execSync(`node ./packages/pine2ts/bin/pine2ts.js "${sourcePath}" "${outputFile}"`, {
stdio: 'inherit'
});
}
EOF

- name: Compile indicators to JavaScript
run: |
Expand Down
5 changes: 0 additions & 5 deletions example/pinescript/ADR.pine

This file was deleted.

3 changes: 0 additions & 3 deletions example/pinescript/BOP.pine

This file was deleted.

8 changes: 0 additions & 8 deletions example/pinescript/DEMA.pine

This file was deleted.

6 changes: 0 additions & 6 deletions example/pinescript/HMA.pine

This file was deleted.

7 changes: 0 additions & 7 deletions example/pinescript/LSMA.pine

This file was deleted.

6 changes: 0 additions & 6 deletions example/pinescript/Mass Index.pine

This file was deleted.

7 changes: 0 additions & 7 deletions example/pinescript/McGinley Dynamic.pine

This file was deleted.

6 changes: 0 additions & 6 deletions example/pinescript/Momentum.pine

This file was deleted.

38 changes: 20 additions & 18 deletions example/pinescript/README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
# PineScript Sources

This directory contains the source PineScript files for indicators.
Indicator source files are now located in `docs/official/indicators_standard/`.

When you commit changes to files in this directory, a GitHub Action will automatically:
1. Run the `pine2ts` transpiler
2. Generate TypeScript indicators in `indicators/`
3. Commit the generated files
To add or modify which indicators are generated:
1. Edit `indicators/sources.json` to add/remove indicator entries
2. Each entry should have an `id`, `name`, and `sourcePath` pointing to the official .pine file
3. Commit and push - the GitHub Action will automatically transpile the indicators

## Adding a new indicator
## Example Entry

1. Create a new `.pine` file in this directory
2. Use standard PineScript v6 syntax
3. Commit and push
4. The indicator will be auto-generated in `indicators/`
```json
{
"id": "sma",
"name": "Simple Moving Average (SMA)",
"sourcePath": "docs/official/indicators_standard/Moving Average Simple.pine"
}
```

## Local Development

## Example
To generate indicators locally, run:

```pine
//@version=6
indicator(title="My Custom Indicator", shorttitle="MCI", overlay=true)
length = input.int(14, minval=1, title="Length")
src = input(close, title="Source")
result = ta.sma(src, length)
plot(result, "MCI", color=color.blue)
```bash
pnpm generate-indicators
```

This reads from `indicators/sources.json` and transpiles each indicator to TypeScript.
7 changes: 0 additions & 7 deletions example/pinescript/ROC.pine

This file was deleted.

34 changes: 0 additions & 34 deletions example/pinescript/SMA.pine

This file was deleted.

8 changes: 0 additions & 8 deletions example/pinescript/TEMA.pine

This file was deleted.

37 changes: 22 additions & 15 deletions example/scripts/generate-indicators.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,43 @@ const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');

const PINE_DIR = path.join(__dirname, '..', '..', 'example', 'pinescript');
const SOURCES_JSON = path.join(__dirname, '..', '..', 'indicators', 'sources.json');
const INDICATORS_DIR = path.join(__dirname, '..', '..', 'indicators');
const PROJECT_ROOT = path.join(__dirname, '..', '..');

// Find all .pine files
const pineFiles = fs.readdirSync(PINE_DIR).filter(f => f.endsWith('.pine'));
// Read sources.json
if (!fs.existsSync(SOURCES_JSON)) {
console.error(`Error: sources.json not found at ${SOURCES_JSON}`);
process.exit(1);
}

const sources = JSON.parse(fs.readFileSync(SOURCES_JSON, 'utf-8'));

console.log(`Found ${pineFiles.length} PineScript files to transpile`);
console.log(`Found ${sources.indicators.length} indicators to transpile`);

for (const pineFile of pineFiles) {
const baseName = path.basename(pineFile, '.pine');
// Convert to kebab-case: replace spaces first, then handle camelCase
const indicatorName = baseName
.replace(/\s+/g, '-') // Replace spaces with hyphens first
.replace(/([a-z0-9])([A-Z])/g, '$1-$2') // Insert hyphen between lowercase/digit and uppercase
.toLowerCase();
const outputDir = path.join(INDICATORS_DIR, indicatorName);
for (const indicator of sources.indicators) {
const sourcePath = path.join(PROJECT_ROOT, indicator.sourcePath);
const outputDir = path.join(INDICATORS_DIR, indicator.id);
const outputFile = path.join(outputDir, `${indicator.id}.ts`);

if (!fs.existsSync(sourcePath)) {
console.error(`Source file not found: ${sourcePath}`);
continue;
}

console.log(`Transpiling ${pineFile} -> ${indicatorName}/`);
console.log(`Transpiling ${indicator.name} -> ${indicator.id}/`);

try {
// Create output directory if it doesn't exist
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}

execSync(`node ./transpiler/bin/pine2ts.js "${path.join(PINE_DIR, pineFile)}" "${path.join(outputDir, indicatorName + '.ts')}"`, {
execSync(`node ./packages/pine2ts/bin/pine2ts.js "${sourcePath}" "${outputFile}"`, {
stdio: 'inherit'
});
} catch (error) {
console.error(`Failed to transpile ${pineFile}:`, error.message);
console.error(`Failed to transpile ${indicator.name}:`, error.message);
}
}

Expand Down
Loading
Loading