BDPO Library Documentation
Comprehensive guide to building trading strategies and indicators with JavaScript/TypeScript
Welcome to BDPO
BDPO is a powerful trading and investment analysis framework that allows you to create, test, and deploy trading strategies using JavaScript/TypeScript. Build class-based indicators, backtest strategies, and analyze real broker data with ease.
Create Class-Based Scripts
Write indicators and strategies as classes with lifecycle methods
Define Public Variables
Set user-configurable parameters that control script behavior
Backtest & Optimize
Test strategies on historical data and optimize parameters
Deploy Live
Forward test and run scenarios with real broker data
Core Features
Technical Analysis
Comprehensive library of technical indicators including moving averages, oscillators, momentum indicators, and custom calculations.
Strategy Development
Build automated trading strategies with entry/exit signals, risk management, and position sizing logic.
Data Visualization
Advanced plotting capabilities to visualize indicators, signals, and custom overlays on price charts.
Backtesting Engine
Test strategies against historical data with realistic execution simulation and performance metrics.
Portfolio Management
Track positions, calculate performance metrics, and manage multiple instruments simultaneously.
Broker Integration
Connect to real broker data feeds for live trading and realistic market simulation.
Library Structure
The BDPO library is organized into focused modules, each providing specific functionality:
BDPO Library Structure:
├── technical_indicators # 50+ technical analysis indicators
├── trade_functions # Order execution and trade management
├── plotting_functions # Chart visualization and overlays
├── position_functions # Position tracking and management
├── account_functions # Account and portfolio metrics
├── candlestick_functions # OHLCV data manipulation
├── symbol_functions # Market data and symbol utilities
├── math_functions # Mathematical utilities and statistics
└── console_functions # Debugging and logging tools
Quick Start Guide
Get up and running with BDPO in minutes
Your First Indicator
Let's create a simple moving average crossover indicator using the class-based structure:
// Simple Moving Average Crossover Indicator
class MovingAverageCrossover {
// Public variables - users can modify these
public fastPeriod: number = 10;
public slowPeriod: number = 20;
public lineColor: string = 'blue';
public signalColor: string = 'red';
// Private variables - internal use only
private _plot: any = null;
private _lastCrossover: string | null = null;
__init__(): void {
// Initialize plot builder and any one-time setup
this._plot = new PlotBuilder();
this._lastCrossover = null;
console.log(`MA Crossover initialized: Fast=${this.fastPeriod}, Slow=${this.slowPeriod}`);
}
__tick__(): any {
// Get price data
const prices: number[] = bdpo.getClosePrices();
// Calculate moving averages
const fastMA: number[] = bdpo.sma(prices, this.fastPeriod);
const slowMA: number[] = bdpo.sma(prices, this.slowPeriod);
// Plot the moving averages
this._plot.plot('splines', fastMA, {splineColor: this.lineColor, splineWidth: 2}, {name: `Fast MA (${this.fastPeriod})`});
this._plot.plot('splines', slowMA, {splineColor: this.signalColor, splineWidth: 2}, {name: `Slow MA (${this.slowPeriod})`});
// Generate signals
const signals: any[] = [];
const currentIndex: number = fastMA.length - 1;
const prevIndex: number = currentIndex - 1;
if (currentIndex > 0) {
const currentFast: number = fastMA[currentIndex];
const currentSlow: number = slowMA[currentIndex];
const prevFast: number = fastMA[prevIndex];
const prevSlow: number = slowMA[prevIndex];
if (currentFast > currentSlow && prevFast <= prevSlow) {
signals.push({type: 'BUY', price: prices[currentIndex], index: currentIndex});
this._lastCrossover = 'bullish';
} else if (currentFast < currentSlow && prevFast >= prevSlow) {
signals.push({type: 'SELL', price: prices[currentIndex], index: currentIndex});
this._lastCrossover = 'bearish';
}
}
return {
plots: this._plot.getMultiOverlayResponse(),
signals: signals,
lastCrossover: this._lastCrossover
};
}
__shutdown__(): void {
// Cleanup resources
this._plot.clear();
console.log('MA Crossover indicator shutdown');
}
}
Your First Strategy
Now let's build a complete trading strategy using the class-based structure:
// Moving Average Crossover Trading Strategy
class MACrossoverStrategy {
// Public variables - users can configure these
public fastPeriod: number = 10;
public slowPeriod: number = 20;
public riskPercent: number = 2; // Risk percentage per trade
public stopLossPercent: number = 2; // Stop loss percentage
public takeProfitPercent: number = 6; // Take profit percentage
public maxPositions: number = 1; // Maximum concurrent positions
// Private variables - internal state management
private _positions: any[] = [];
private _lastSignal: string | null = null;
private _initialized: boolean = false;
__init__(): void {
// Initialize strategy state
this._positions = [];
this._lastSignal = null;
this._initialized = true;
console.log(`MAC Strategy initialized: Fast=${this.fastPeriod}, Slow=${this.slowPeriod}, Risk=${this.riskPercent}%`);
}
__tick__(): any {
if (!this._initialized) return null;
const prices: number[] = bdpo.getClosePrices();
const currentPrice: number = prices[prices.length - 1];
// Calculate indicators
const fastMA: number[] = bdpo.sma(prices, this.fastPeriod);
const slowMA: number[] = bdpo.sma(prices, this.slowPeriod);
const currentFast: number = fastMA[fastMA.length - 1];
const currentSlow: number = slowMA[slowMA.length - 1];
const prevFast: number = fastMA[fastMA.length - 2];
const prevSlow: number = slowMA[slowMA.length - 2];
// Calculate position size based on account risk
const accountValue: number = bdpo.getAccountValue();
const positionSize: number = (accountValue * this.riskPercent / 100) / currentPrice;
// Trading logic
const activePositions: any[] = this._positions.filter(pos => pos.status === 'open');
if (activePositions.length < this.maxPositions) {
// Look for entry signals
if (currentFast > currentSlow && prevFast <= prevSlow) {
// Bullish crossover - go long
const position: any = bdpo.buy(positionSize, {
stopLoss: currentPrice * (1 - this.stopLossPercent / 100),
takeProfit: currentPrice * (1 + this.takeProfitPercent / 100),
notes: "MAC Bullish Crossover Entry"
});
if (position) {
this._positions.push(position);
this._lastSignal = 'buy';
}
}
} else {
// Manage existing positions
if (currentFast < currentSlow && prevFast >= prevSlow) {
// Bearish crossover - exit long positions
activePositions.forEach((position: any) => {
if (position.side === 'LONG') {
bdpo.closePosition(position.id);
position.status = 'closed';
this._lastSignal = 'sell';
}
});
}
}
return {
indicators: {
fastMA: currentFast,
slowMA: currentSlow,
currentPrice: currentPrice
},
positions: this._positions,
lastSignal: this._lastSignal,
activePositions: activePositions.length
};
}
__shutdown__(): void {
// Close all open positions before shutdown
const activePositions: any[] = this._positions.filter(pos => pos.status === 'open');
activePositions.forEach((position: any) => {
bdpo.closePosition(position.id);
console.log(`Closed position ${position.id} during strategy shutdown`);
});
console.log(`MAC Strategy shutdown. Total positions: ${this._positions.length}`);
}
}
Architecture Overview
Understanding the BDPO framework structure and execution model
Execution Model
BDPO scripts follow a class-based lifecycle execution model designed for real-time trading applications:
- Initialization:
__init__()
method called once when script loads -
Data Processing:
__tick__()
method called for each new market data point -
Cleanup:
__shutdown__()
method called when script is stopped or removed
Required Class Structure
class MyStrategy {
// Define public variables (user-configurable)
public fastPeriod: number = 10; // Public - users can modify
public slowPeriod: number = 20; // Public - users can modify
// Define private variables (internal use only)
private _position: any = null; // Private - users cannot modify
private _lastSignal: string | null = null; // Private - internal state
__init__(): void {
// Called once when strategy starts
// Initialize indicators, reset state, etc.
}
__tick__(): any {
// Called for every new data point/tick
// Main strategy logic goes here
// Return results for plotting/execution
}
__shutdown__(): void {
// Called when strategy is stopped
// Cleanup resources, close positions, etc.
}
}
Public vs Private Variables
Variable Type | Naming Convention | User Access | Example |
---|---|---|---|
Public | No prefix, explicit public keyword | ✅ User can modify | public period: number = 20 |
Private | Underscore prefix | ❌ Internal use only | private _cache: any[] = [] |
public
and private
decorators for all class variables. Use
public variables for user-configurable parameters like periods, thresholds, and colors. Use private
variables (with underscore prefix) for internal state, cached calculations, and temporary data.
Data Flow
Market Data → BDPO Core → Class Instance → Output Processing → Chart/Backtest
↓ ↓ ↓ ↓ ↓
OHLCV Data Data Validation __tick__() Format Results Display/Execute
Timestamps Symbol Info Indicators Plot Data Trade Orders
Volume Account State Signals Positions Performance
Global Context
BDPO provides a global context object (self.bdpo
) with access to market data and system state:
// Accessing global BDPO context
const symbol: string = self.bdpo.getSymbol(); // Current trading symbol
const timeframe: string = self.bdpo.getTimeframe(); // Chart timeframe
const data: number[][] = self.bdpo.data["0"]; // OHLCV data array
const source: string = self.bdpo.getSource(); // Data source/broker
// Data structure: [timestamp, open, high, low, close, volume]
const latestCandle: number[] = data[data.length - 1];
const currentPrice: number = latestCandle[4]; // Close price
Technical Indicators
50+ technical analysis indicators powered by the industry-standard indicators-js library
bdpo.functionName()
. Click any indicator below to see usage
examples and parameters.
Moving Averages
SMA, EMA, WMA, DEMA, TEMA, HMA, KAMA, VWMA, ALMA
Oscillators
RSI, MACD, Stochastic, CCI, Williams %R, MFI, CMO
Bands & Channels
Bollinger Bands, Keltner Channels, Donchian Channels
Volume Indicators
OBV, VWAP, A/D Line, CMF, Force Index, Klinger
Volatility Indicators
ATR, True Range, Chaikin Volatility, VHF
Trend Indicators
Parabolic SAR, DMI, DX, TRIX, Linear Regression
Moving Averages
Smoothing indicators that reduce price noise and reveal trends.
bdpo.sma() Popular
Simple Moving Average - Equal weight to all periods
bdpo.ema() Popular
Exponential Moving Average - More weight to recent prices
bdpo.wma()
Weighted Moving Average - Linear weight distribution
bdpo.hma()
Hull Moving Average - Reduced lag, increased smoothness
bdpo.kama()
Kaufman Adaptive MA - Adapts to market volatility
bdpo.vwma()
Volume Weighted MA - Incorporates volume data
Parameters
Parameter | Type | Description | Example |
---|---|---|---|
data | Array | Price data array (typically close prices) | data.close |
period | Number | Number of periods to average | 20, 50, 200 |
Usage Example
// Calculate 20-period SMA
private sma20: number[] = bdpo.sma(data.close, 20);
private currentSMA: number = this.sma20[this.sma20.length - 1];
// Multiple SMAs for crossover strategy
private smaFast: number[] = bdpo.sma(data.close, 10);
private smaSlow: number[] = bdpo.sma(data.close, 20);
Parameters
Parameter | Type | Description | Example |
---|---|---|---|
data | Array | Price data array | data.close |
period | Number | Smoothing period | 12, 26, 50 |
Usage Example
// MACD components using EMAs
private ema12: number[] = bdpo.ema(data.close, 12);
private ema26: number[] = bdpo.ema(data.close, 26);
// Check for bullish crossover
if (this.ema12[this.ema12.length - 1] > this.ema26[this.ema26.length - 1]) {
// Fast EMA above slow EMA - bullish signal
}
bdpo.dema()
Double EMA - Reduces lag compared to standard EMA
bdpo.dema(data.close, 21)
bdpo.tema()
Triple EMA - Further lag reduction for fast signals
bdpo.tema(data.close, 14)
bdpo.trima()
Triangular MA - Double smoothed for stable trends
bdpo.trima(data.close, 20)
bdpo.zlema()
Zero-Lag EMA - Eliminates lag through prediction
bdpo.zlema(data.close, 20)
bdpo.alma()
Arnaud Legoux MA - Advanced adaptive smoothing
bdpo.alma(data.close, 21, 0.85, 6)
bdpo.vidya()
Variable Index Dynamic Average - Volatility adaptive
bdpo.vidya(data.close, 14)
Parameters
Parameter | Type | Description |
---|---|---|
data | Array | Price data array |
period | Number | Number of periods to average |
Example Usage
private wma20: number[] = bdpo.wma(data.close, 20);
private currentWMA: number = this.wma20[this.wma20.length - 1];
// WMA responds faster to price changes than SMA
private priceAboveWMA: boolean = data.close[data.close.length - 1] > this.currentWMA;
Parameters
Parameter | Type | Description |
---|---|---|
data | Array | Price data array |
period | Number | EMA calculation period |
Example Usage
private dema21: number[] = bdpo.dema(data.close, 21);
private currentDEMA: number = this.dema21[this.dema21.length - 1];
// DEMA provides faster signals than regular EMA
// Good for trend following with reduced lag
Parameters
Parameter | Type | Description |
---|---|---|
data | Array | Price data array |
period | Number | EMA calculation period |
Example Usage
private tema14: number[] = bdpo.tema(data.close, 14);
private currentTEMA: number = this.tema14[this.tema14.length - 1];
// TEMA offers the fastest response with minimal noise
// Excellent for scalping and short-term trading
Parameters
Parameter | Type | Description |
---|---|---|
data | Array | Price data array |
period | Number | Triangular average period |
Example Usage
private trima20: number[] = bdpo.trima(data.close, 20);
private currentTRIMA: number = this.trima20[this.trima20.length - 1];
// TRIMA is very smooth but has significant lag
// Best for identifying major trend direction
Parameters
Parameter | Type | Description |
---|---|---|
data | Array | Price data array |
period | Number | HMA calculation period |
Example Usage
private hma21: number[] = bdpo.hma(data.close, 21);
private currentHMA: number = this.hma21[this.hma21.length - 1];
// HMA combines speed and smoothness effectively
// Popular for swing trading strategies
Parameters
Parameter | Type | Description |
---|---|---|
data | Array | Price data array |
period | Number | Efficiency ratio period (typically 10) |
Example Usage
private kama10: number[] = bdpo.kama(data.close, 10);
private currentKAMA: number = this.kama10[this.kama10.length - 1];
// KAMA adapts: fast in trends, slow in sideways markets
// Reduces whipsaws while maintaining responsiveness
Parameters
Parameter | Type | Description |
---|---|---|
data | Array | Price data array |
volume | Array | Volume data array |
period | Number | VWMA calculation period |
Example Usage
private vwma20: number[] = bdpo.vwma(data.close, data.volume, 20);
private currentVWMA: number = this.vwma20[this.vwma20.length - 1];
// VWMA gives more weight to high-volume periods
// Better reflects institutional participation
Parameters
Parameter | Type | Description |
---|---|---|
data | Array | Price data array |
period | Number | EMA calculation period |
Example Usage
private zlema20: number[] = bdpo.zlema(data.close, 20);
private currentZLEMA: number = this.zlema20[this.zlema20.length - 1];
// ZLEMA attempts to eliminate lag completely
// Can be more volatile but very responsive
Parameters
Parameter | Type | Description |
---|---|---|
data | Array | Price data array |
period | Number | Window length (typically 21) |
offset | Number | Phase offset (0.0-1.0, typically 0.85) |
sigma | Number | Smoothing factor (typically 6) |
Example Usage
private alma: number[] = bdpo.alma(data.close, 21, 0.85, 6);
private currentALMA: number = this.alma[this.alma.length - 1];
// ALMA provides excellent balance between responsiveness and smoothness
// Highly configurable for different trading styles
Parameters
Parameter | Type | Description |
---|---|---|
data | Array | Price data array |
period | Number | Smoothing period |
Example Usage
private wilders14: number[] = bdpo.wilders(data.close, 14);
private currentWilders: number = this.wilders14[this.wilders14.length - 1];
// Wilder's smoothing is less aggressive than EMA
// Foundation for many classic technical indicators
Parameters
Parameter | Type | Description |
---|---|---|
data | Array | Price data array |
period | Number | Base period for adaptation |
Example Usage
private vidya14: number[] = bdpo.vidya(data.close, 14);
private currentVIDYA: number = this.vidya14[this.vidya14.length - 1];
// VIDYA adapts smoothing factor based on volatility
// Fast response in volatile markets, stable in quiet periods
Oscillators
Momentum indicators that oscillate between fixed bounds, ideal for identifying overbought/oversold conditions.
bdpo.rsi() Most Popular
Relative Strength Index (0-100) - Momentum oscillator
bdpo.macd() Popular
MACD - Trend following momentum indicator
bdpo.stoch()
Stochastic Oscillator - Price position within range
bdpo.cci()
Commodity Channel Index - Cyclical trend identifier
bdpo.willr()
Williams %R - Momentum indicator (-100 to 0)
bdpo.mfi()
Money Flow Index - Volume-weighted RSI
Parameters
Parameter | Type | Description | Common Values |
---|---|---|---|
data | Array | Price data array | data.close |
period | Number | Calculation period | 14 (standard), 9, 21 |
Usage Example
// Standard RSI setup
private rsi14: number[] = bdpo.rsi(data.close, 14);
private currentRSI: number = this.rsi14[this.rsi14.length - 1];
// Trading signals
if (this.currentRSI > 70) {
// Overbought - consider selling
} else if (this.currentRSI < 30) {
// Oversold - consider buying
}
Parameters
Parameter | Type | Description | Standard Values |
---|---|---|---|
data | Array | Price data array | data.close |
fastPeriod | Number | Fast EMA period | 12 |
slowPeriod | Number | Slow EMA period | 26 |
signalPeriod | Number | Signal line period | 9 |
Usage Example
// Standard MACD (12,26,9)
private macdData: any = bdpo.macd(data.close, 12, 26, 9);
// Access MACD components
private macdLine: number = this.macdData.macd[this.macdData.macd.length - 1];
private signalLine: number = this.macdData.signal[this.macdData.signal.length - 1];
private histogram: number = this.macdData.histogram[this.macdData.histogram.length - 1];
// Signal detection
if (this.macdLine > this.signalLine && this.histogram > 0) {
// Bullish signal
}
bdpo.stochrsi()
Stochastic RSI - Stochastic applied to RSI values
bdpo.stochrsi(data.close, 14)
bdpo.ao()
Awesome Oscillator - Bill Williams momentum indicator
bdpo.ao(data.high, data.low)
bdpo.cmo()
Chande Momentum - Alternative momentum calculation
bdpo.cmo(data.close, 14)
bdpo.roc()
Rate of Change - Percentage price change
bdpo.roc(data.close, 12)
bdpo.ultosc()
Ultimate Oscillator - Multiple timeframe momentum
bdpo.ultosc(data.high, data.low, data.close, 7, 14, 28)
bdpo.tsi()
True Strength Index - Double smoothed momentum
bdpo.tsi(data.close, 25, 13)
Parameters
Parameter | Type | Description |
---|---|---|
high | Array | High price array |
low | Array | Low price array |
close | Array | Close price array |
kPeriod | Number | %K period (typically 14) |
kSlowPeriod | Number | %K slowing period (typically 3) |
dPeriod | Number | %D period (typically 3) |
Example Usage
// Standard Stochastic (14,3,3)
private stochastic: any = bdpo.stoch(data.high, data.low, data.close, 14, 3, 3);
private kPercent: number = this.stochastic.k[this.stochastic.k.length - 1];
private dPercent: number = this.stochastic.d[this.stochastic.d.length - 1];
// Trading signals
if (this.kPercent > 80 && this.dPercent > 80) {
// Overbought condition
} else if (this.kPercent < 20 && this.dPercent < 20) {
// Oversold condition
}
Parameters
Parameter | Type | Description |
---|---|---|
data | Array | Price data array |
period | Number | RSI and Stochastic period (typically 14) |
Example Usage
private stochRSI: number[] = bdpo.stochrsi(data.close, 14);
private currentStochRSI: number = this.stochRSI[this.stochRSI.length - 1];
// StochRSI signals (0-1 scale, more sensitive than regular RSI)
if (this.currentStochRSI > 0.8) {
// Extremely overbought
} else if (this.currentStochRSI < 0.2) {
// Extremely oversold
}
Parameters
Parameter | Type | Description |
---|---|---|
high | Array | High price array |
low | Array | Low price array |
close | Array | Close price array |
period | Number | Lookback period (typically 14) |
Example Usage
private williamsR: number[] = bdpo.willr(data.high, data.low, data.close, 14);
private currentWillR: number = this.williamsR[this.williamsR.length - 1];
// Williams %R interpretation (inverted from typical overbought/oversold)
if (this.currentWillR > -20) {
// Overbought (close to 0)
} else if (this.currentWillR < -80) {
// Oversold (close to -100)
}
Parameters
Parameter | Type | Description |
---|---|---|
high | Array | High price array |
low | Array | Low price array |
close | Array | Close price array |
period | Number | Calculation period (typically 20) |
Example Usage
private cci20: number[] = bdpo.cci(data.high, data.low, data.close, 20);
private currentCCI: number = this.cci20[this.cci20.length - 1];
// CCI interpretation
if (this.currentCCI > 100) {
// Strong uptrend, potential overbought
} else if (this.currentCCI < -100) {
// Strong downtrend, potential oversold
}
Parameters
Parameter | Type | Description |
---|---|---|
high | Array | High price array |
low | Array | Low price array |
close | Array | Close price array |
volume | Array | Volume array |
period | Number | Calculation period (typically 14) |
Example Usage
private mfi14: number[] = bdpo.mfi(data.high, data.low, data.close, data.volume, 14);
private currentMFI: number = this.mfi14[this.mfi14.length - 1];
// MFI interpretation (similar to RSI but includes volume)
if (this.currentMFI > 80) {
// Overbought with volume confirmation
} else if (this.currentMFI < 20) {
// Oversold with volume confirmation
}
Parameters
Parameter | Type | Description |
---|---|---|
high | Array | High price array |
low | Array | Low price array |
Example Usage
private awesomeOscillator: number[] = bdpo.ao(data.high, data.low);
private currentAO: number = this.awesomeOscillator[this.awesomeOscillator.length - 1];
private previousAO: number = this.awesomeOscillator[this.awesomeOscillator.length - 2];
// AO signals
if (this.currentAO > 0 && this.previousAO <= 0) {
// Bullish momentum shift
} else if (this.currentAO < 0 && this.previousAO >= 0) {
// Bearish momentum shift
}
Parameters
Parameter | Type | Description |
---|---|---|
high | Array | High price array |
low | Array | Low price array |
period | Number | Lookback period (typically 14 or 25) |
Returns
Object with aroonUp
and aroonDown
arrays (0-100 scale)
Example Usage
private aroonValues: any = bdpo.aroon(data.high, data.low, 25);
private aroonUp: number = this.aroonValues.aroonUp[this.aroonValues.aroonUp.length - 1];
private aroonDown: number = this.aroonValues.aroonDown[this.aroonValues.aroonDown.length - 1];
// Aroon trend analysis
if (this.aroonUp > 70 && this.aroonDown < 30) {
// Strong uptrend
} else if (this.aroonDown > 70 && this.aroonUp < 30) {
// Strong downtrend
} else if (Math.abs(this.aroonUp - this.aroonDown) < 20) {
// Consolidation/sideways trend
}
Parameters
Parameter | Type | Description |
---|---|---|
high | Array | High price array |
low | Array | Low price array |
period | Number | Lookback period (typically 14 or 25) |
Example Usage
private aroonOsc: number[] = bdpo.aroonosc(data.high, data.low, 25);
private currentAroonOsc: number = this.aroonOsc[this.aroonOsc.length - 1];
// Aroon Oscillator signals (-100 to +100)
if (this.currentAroonOsc > 40) {
// Strong bullish trend
} else if (this.currentAroonOsc < -40) {
// Strong bearish trend
} else {
// Weak trend or consolidation
}
Parameters
Parameter | Type | Description |
---|---|---|
open | Array | Open price array |
high | Array | High price array |
low | Array | Low price array |
close | Array | Close price array |
Example Usage
private balanceOfPower: number[] = bdpo.bop(data.open, data.high, data.low, data.close);
private currentBOP: number = this.balanceOfPower[this.balanceOfPower.length - 1];
// BOP interpretation (-1 to +1 scale)
if (this.currentBOP > 0.5) {
// Strong buying pressure
} else if (this.currentBOP < -0.5) {
// Strong selling pressure
} else {
// Balanced market
}
Parameters
Parameter | Type | Description |
---|---|---|
data | Array | Price data array |
period | Number | Calculation period (typically 14 or 20) |
Example Usage
private cmo: number[] = bdpo.cmo(data.close, 14);
private currentCMO: number = this.cmo[this.cmo.length - 1];
// CMO signals (-100 to +100 scale)
if (this.currentCMO > 50) {
// Overbought condition
} else if (this.currentCMO < -50) {
// Oversold condition
}
Parameters
Parameter | Type | Description |
---|---|---|
data | Array | Price data array (typically close prices) |
period | Number | Period for calculation (typically 20) |
Example Usage
private readonly period: number = 20;
private closePrices: number[] = bdpo.getClosePrices();
private dpo: number[] = bdpo.dpo(this.closePrices, this.period);
private currentDPO: number = this.dpo[this.dpo.length - 1];
// DPO helps identify short-term cycles
if (this.currentDPO > 0) {
// Price above detrended level - potential sell signal
} else if (this.currentDPO < 0) {
// Price below detrended level - potential buy signal
}
Parameters
Parameter | Type | Description |
---|---|---|
high | Array | High price array |
low | Array | Low price array |
period | Number | Period for calculation (typically 10) |
Example Usage
private readonly period: number = 10;
private highPrices: number[] = bdpo.getHighPrices();
private lowPrices: number[] = bdpo.getLowPrices();
private fisher: number[] = bdpo.fisher(this.highPrices, this.lowPrices, this.period);
private currentFisher: number = this.fisher[this.fisher.length - 1];
// Fisher Transform creates sharper turning points
if (this.currentFisher > 1.5) {
// Overbought - potential reversal signal
} else if (this.currentFisher < -1.5) {
// Oversold - potential reversal signal
}
Parameters
Parameter | Type | Description |
---|---|---|
data | Array | Price data array (typically close prices) |
period | Number | Period for calculation (typically 10) |
Example Usage
private readonly period: number = 10;
private closePrices: number[] = bdpo.getClosePrices();
private momentum: number[] = bdpo.mom(this.closePrices, this.period);
private currentMomentum: number = this.momentum[this.momentum.length - 1];
// Momentum indicates price velocity
if (this.currentMomentum > 0) {
// Positive momentum - upward price movement
} else if (this.currentMomentum < 0) {
// Negative momentum - downward price movement
}
// Look for momentum divergences with price
Parameters
Parameter | Type | Description |
---|---|---|
data | Array | Price data array (typically close prices) |
fastPeriod | Number | Fast EMA period (typically 12) |
slowPeriod | Number | Slow EMA period (typically 26) |
Example Usage
private readonly fastPeriod: number = 12;
private readonly slowPeriod: number = 26;
private closePrices: number[] = bdpo.getClosePrices();
private ppo: number[] = bdpo.ppo(this.closePrices, this.fastPeriod, this.slowPeriod);
private currentPPO: number = this.ppo[this.ppo.length - 1];
// PPO signals similar to MACD but normalized
if (this.currentPPO > 0) {
// Bullish momentum - fast MA above slow MA
} else if (this.currentPPO < 0) {
// Bearish momentum - fast MA below slow MA
}
// Look for PPO crossovers and divergences
Parameters
Parameter | Type | Description |
---|---|---|
data | Array | Price data array |
period | Number | Lookback period (typically 12 or 14) |
Example Usage
private roc12: number[] = bdpo.roc(data.close, 12);
private currentROC: number = this.roc12[this.roc12.length - 1];
// ROC interpretation (percentage)
if (this.currentROC > 10) {
// Strong bullish momentum (10% gain in 12 periods)
} else if (this.currentROC < -10) {
// Strong bearish momentum (10% loss in 12 periods)
}
Parameters
Parameter | Type | Description |
---|---|---|
high | Array | High price array |
low | Array | Low price array |
close | Array | Close price array |
period1 | Number | Short period (typically 7) |
period2 | Number | Medium period (typically 14) |
period3 | Number | Long period (typically 28) |
Example Usage
private ultimateOsc: number[] = bdpo.ultosc(data.high, data.low, data.close, 7, 14, 28);
private currentUO: number = this.ultimateOsc[this.ultimateOsc.length - 1];
// Ultimate Oscillator signals (0-100 scale)
if (this.currentUO > 70) {
// Overbought - potential sell signal
} else if (this.currentUO < 30) {
// Oversold - potential buy signal
}
Bands and Channels
Parameters
Parameter | Type | Description |
---|---|---|
data | Array | Array of price values |
period | Number | Moving average period |
stddev | Number | Standard deviation multiplier |
Example Usage
private bollingerBands: any = bdpo.bbands(data.close, 20, 2);
private upperBand: number = this.bollingerBands.upper[this.bollingerBands.upper.length - 1];
private lowerBand: number = this.bollingerBands.lower[this.bollingerBands.lower.length - 1];
Parameters
Parameter | Type | Description |
---|---|---|
high | Array | High price array |
low | Array | Low price array |
close | Array | Close price array |
period | Number | ATR calculation period |
multiplier | Number | ATR multiplier for bands |
Example Usage
private keltnerChannels: any = bdpo.kc(data.high, data.low, data.close, 20, 2.0);
private kcUpper: number = this.keltnerChannels.upper[this.keltnerChannels.upper.length - 1];
private kcLower: number = this.keltnerChannels.lower[this.keltnerChannels.lower.length - 1];
Parameters
Parameter | Type | Description |
---|---|---|
high | Array | High price array |
low | Array | Low price array |
period | Number | Lookback period |
Example Usage
private donchianChannels: any = bdpo.dc(data.high, data.low, 20);
private dcUpper: number = this.donchianChannels.upper[this.donchianChannels.upper.length - 1];
private dcLower: number = this.donchianChannels.lower[this.donchianChannels.lower.length - 1];
// Breakout strategy
if (data.close[data.close.length - 1] > this.dcUpper) {
// Price broke above upper channel - bullish breakout
}
Parameters
Parameter | Type | Description |
---|---|---|
data | Array | Price data array |
period | Number | Moving average period |
Example Usage
private priceBands: any = bdpo.pbands(data.close, 20);
Volume Indicators
Parameters
Parameter | Type | Description |
---|---|---|
close | Array | Array of close prices |
volume | Array | Array of volume values |
Example Usage
private onBalanceVolume: number[] = bdpo.obv(data.close, data.volume);
Parameters
Parameter | Type | Description |
---|---|---|
high | Array | High price array |
low | Array | Low price array |
close | Array | Close price array |
volume | Array | Volume array |
Example Usage
private vwap: number[] = bdpo.vwap(data.high, data.low, data.close, data.volume);
private currentVWAP: number = this.vwap[this.vwap.length - 1];
// Compare current price to VWAP
if (data.close[data.close.length - 1] > this.currentVWAP) {
// Price above VWAP - bullish sentiment
}
Parameters
Parameter | Type | Description |
---|---|---|
high | Array | High price array |
low | Array | Low price array |
close | Array | Close price array |
volume | Array | Volume data array |
Example Usage
private adLine: number[] = bdpo.ad(data.high, data.low, data.close, data.volume);
private currentAD: number = this.adLine[this.adLine.length - 1];
private previousAD: number = this.adLine[this.adLine.length - 2];
// Rising A/D line confirms uptrend
// Divergence between price and A/D suggests reversal
Parameters
Parameter | Type | Description |
---|---|---|
high | Array | High price array |
low | Array | Low price array |
close | Array | Close price array |
volume | Array | Volume data array |
period | Number | Calculation period (typically 20) |
Example Usage
private cmf20: number[] = bdpo.cmf(data.high, data.low, data.close, data.volume, 20);
private currentCMF: number = this.cmf20[this.cmf20.length - 1];
// CMF above 0.1 = buying pressure
// CMF below -0.1 = selling pressure
Parameters
Parameter | Type | Description |
---|---|---|
high | Array | High price array |
low | Array | Low price array |
volume | Array | Volume data array |
period | Number | Smoothing period (typically 14) |
Example Usage
private emv14: number[] = bdpo.emv(data.high, data.low, data.volume, 14);
private currentEMV: number = this.emv14[this.emv14.length - 1];
// Positive EMV = price rising on low volume (easy movement)
// Negative EMV = price falling or high volume required
Parameters
Parameter | Type | Description |
---|---|---|
close | Array | Close price array |
volume | Array | Volume data array |
period | Number | EMA smoothing period (typically 13) |
Example Usage
private forceIndex: number[] = bdpo.fi(data.close, data.volume, 13);
private currentFI: number = this.forceIndex[this.forceIndex.length - 1];
// Rising FI confirms uptrend with volume
// Falling FI confirms downtrend with volume
Parameters
Parameter | Type | Description |
---|---|---|
high | Array | High price array |
low | Array | Low price array |
close | Array | Close price array |
volume | Array | Volume data array |
fastPeriod | Number | Fast EMA period (typically 34) |
slowPeriod | Number | Slow EMA period (typically 55) |
Example Usage
private kvo: number[] = bdpo.kvo(data.high, data.low, data.close, data.volume, 34, 55);
private currentKVO: number = this.kvo[this.kvo.length - 1];
// KVO crossover above zero = bullish volume momentum
// KVO crossover below zero = bearish volume momentum
Parameters
Parameter | Type | Description |
---|---|---|
close | Array | Close price array |
volume | Array | Volume data array |
Example Usage
private negativeVolumeIndex: number[] = bdpo.nvi(data.close, data.volume);
private currentNVI: number = this.negativeVolumeIndex[this.negativeVolumeIndex.length - 1];
// NVI tracks uninformed traders (retail) activity
// Rising NVI during low volume suggests retail accumulation
Parameters
Parameter | Type | Description |
---|---|---|
close | Array | Close price array |
volume | Array | Volume data array |
Example Usage
private positiveVolumeIndex: number[] = bdpo.pvi(data.close, data.volume);
private currentPVI: number = this.positiveVolumeIndex[this.positiveVolumeIndex.length - 1];
// PVI tracks smart money (institutional) activity
// Rising PVI suggests institutions are accumulating
Volatility Indicators
Parameters
Parameter | Type | Description |
---|---|---|
high | Array | Array of high prices |
low | Array | Array of low prices |
close | Array | Array of close prices |
period | Number | ATR calculation period |
Example Usage
private atr14: number[] = bdpo.atr(data.high, data.low, data.close, 14);
Parameters
Parameter | Type | Description |
---|---|---|
high | Array | High price array |
low | Array | Low price array |
close | Array | Close price array |
period | Number | Calculation period |
Example Usage
// NATR gives volatility as percentage (0-100)
private natr14: number[] = bdpo.natr(data.high, data.low, data.close, 14);
private currentVolatility: number = this.natr14[this.natr14.length - 1];
// Use for position sizing based on volatility
if (this.currentVolatility > 5) {
// High volatility - reduce position size
const volatilityAdjustedSize = baseSize * (5 / this.currentVolatility);
}
Parameters
Parameter | Type | Description |
---|---|---|
high | Array | High price array |
low | Array | Low price array |
close | Array | Close price array |
Example Usage
private trueRange: number[] = bdpo.tr(data.high, data.low, data.close);
private currentTR: number = this.trueRange[this.trueRange.length - 1];
Parameters
Parameter | Type | Description |
---|---|---|
high | Array | High price array |
low | Array | Low price array |
period | Number | Calculation period |
Example Usage
private chaikinVol: number[] = bdpo.cvi(data.high, data.low, 10);
Parameters
Parameter | Type | Description |
---|---|---|
data | Array | Price data array |
period | Number | Calculation period |
Example Usage
private historicalVol: number[] = bdpo.volatility(data.close, 20);
private currentVol: number = this.historicalVol[this.historicalVol.length - 1];
Parameters
Parameter | Type | Description |
---|---|---|
data | Array | Price data array |
period | Number | Calculation period |
Example Usage
private vhf28: number[] = bdpo.vhf(data.close, 28);
private currentVHF: number = this.vhf28[this.vhf28.length - 1];
// VHF > 0.35 typically indicates trending market
// VHF < 0.25 typically indicates trading/sideways market
if (this.currentVHF > 0.35) {
// Use trend-following strategy
} else if (this.currentVHF < 0.25) {
// Use mean-reversion strategy
}
Trend Indicators
Parameters
Parameter | Type | Description |
---|---|---|
close | Array | Close price array |
shortPeriod | Number | Short period for fast MA |
longPeriod | Number | Long period for slow MA |
Example Usage
private apo: number[] = bdpo.apo(data.close, 12, 26);
Parameters
Parameter | Type | Description |
---|---|---|
high | Array | High price array |
low | Array | Low price array |
close | Array | Close price array |
period | Number | Calculation period |
Example Usage
private dx14: number[] = bdpo.dx(data.high, data.low, data.close, 14);
Parameters
Parameter | Type | Description |
---|---|---|
high | Array | High price array |
low | Array | Low price array |
close | Array | Close price array |
period | Number | Calculation period (typically 14) |
Returns
Object with plus
and minus
arrays representing +DI and -DI values
Example Usage
private diValues: any = bdpo.di(data.high, data.low, data.close, 14);
private plusDI: number = this.diValues.plus[this.diValues.plus.length - 1];
private minusDI: number = this.diValues.minus[this.diValues.minus.length - 1];
// DI crossover signals
if (this.plusDI > this.minusDI) {
// Bullish trend - +DI above -DI
} else if (this.minusDI > this.plusDI) {
// Bearish trend - -DI above +DI
}
Parameters
Parameter | Type | Description |
---|---|---|
high | Array | High price array |
low | Array | Low price array |
period | Number | Smoothing period (typically 14) |
Returns
Object with plus
and minus
arrays representing +DM and -DM values
Example Usage
private dmValues: any = bdpo.dm(data.high, data.low, 14);
private plusDM: number = this.dmValues.plus[this.dmValues.plus.length - 1];
private minusDM: number = this.dmValues.minus[this.dmValues.minus.length - 1];
// DM analysis for trend direction
private dmRatio: number = this.plusDM / this.minusDM;
if (this.dmRatio > 1.5) {
// Strong upward movement
} else if (this.dmRatio < 0.67) {
// Strong downward movement
}
Parameters
Parameter | Type | Description |
---|---|---|
high | Array | High price array |
low | Array | Low price array |
close | Array | Close price array |
period | Number | Calculation period (typically 14) |
Example Usage
private adx: number[] = bdpo.adx(data.high, data.low, data.close, 14);
private currentADX: number = this.adx[this.adx.length - 1];
// ADX trend strength interpretation
if (this.currentADX > 40) {
// Very strong trend
} else if (this.currentADX > 25) {
// Strong trend
} else if (this.currentADX > 20) {
// Moderate trend
} else {
// Weak or no trend
}
Parameters
Parameter | Type | Description |
---|---|---|
high | Array | High price array |
low | Array | Low price array |
close | Array | Close price array |
period | Number | Calculation period (typically 14) |
Example Usage
private adxr: number[] = bdpo.adxr(data.high, data.low, data.close, 14);
private currentADXR: number = this.adxr[this.adxr.length - 1];
// ADXR provides smoother trend strength readings
if (this.currentADXR > 30) {
// Strong sustained trend
} else if (this.currentADXR < 20) {
// Weak trend or sideways market
}
Parameters
Parameter | Type | Description |
---|---|---|
high | Array | High price array |
low | Array | Low price array |
step | Number | Acceleration factor step |
max | Number | Maximum acceleration factor |
Example Usage
private psar: number[] = bdpo.psar(data.high, data.low, 0.02, 0.2);
private currentPSAR: number = this.psar[this.psar.length - 1];
private currentClose: number = data.close[data.close.length - 1];
// PSAR trading signals
if (this.currentClose > this.currentPSAR) {
// Bullish signal - price above PSAR
} else if (this.currentClose < this.currentPSAR) {
// Bearish signal - price below PSAR
}
Parameters
Parameter | Type | Description |
---|---|---|
close | Array | Close price array |
period | Number | Smoothing period (typically 14) |
Example Usage
private trix: number[] = bdpo.trix(data.close, 14);
private currentTRIX: number = this.trix[this.trix.length - 1];
private previousTRIX: number = this.trix[this.trix.length - 2];
// TRIX signals
if (this.currentTRIX > 0 && this.previousTRIX <= 0) {
// Bullish signal - TRIX crosses above zero
} else if (this.currentTRIX < 0 && this.previousTRIX >= 0) {
// Bearish signal - TRIX crosses below zero
}
Parameters
Parameter | Type | Description |
---|---|---|
close | Array | Close price array |
period | Number | Regression period (typically 14-21) |
Example Usage
private linreg: number[] = bdpo.linreg(data.close, 20);
private currentLR: number = this.linreg[this.linreg.length - 1];
private currentPrice: number = data.close[data.close.length - 1];
// Linear regression trend analysis
private deviation: number = this.currentPrice - this.currentLR;
private deviationPercent: number = (deviation / this.currentLR) * 100;
if (Math.abs(this.deviationPercent) > 2) {
// Price significantly deviates from trend
}
Parameters
Parameter | Type | Description |
---|---|---|
values | Array | Input data array |
period | Number | Lookback period |
Example Usage
private midpoint: number[] = bdpo.midpoint(data.close, 14);
private currentMP: number = this.midpoint[this.midpoint.length - 1];
private currentPrice: number = data.close[data.close.length - 1];
// Midpoint as support/resistance level
if (this.currentPrice > this.currentMP) {
// Price above midpoint - potential bullish
} else if (this.currentPrice < this.currentMP) {
// Price below midpoint - potential bearish
}
Parameters
Parameter | Type | Description |
---|---|---|
high | Array | High price array |
low | Array | Low price array |
period | Number | Lookback period |
Example Usage
private midprice: number[] = bdpo.midprice(data.high, data.low, 14);
private currentMidprice: number = this.midprice[this.midprice.length - 1];
private currentClose: number = data.close[data.close.length - 1];
// Midprice as dynamic support/resistance
private distanceFromMidprice: number = this.currentClose - this.currentMidprice;
private distancePercent: number = (distanceFromMidprice / this.currentMidprice) * 100;
if (Math.abs(this.distancePercent) > 1.5) {
// Price significantly away from recent midprice
}
Cross-over Indicators
Parameters
Parameter | Type | Description |
---|---|---|
series1 | Array | First data series |
series2 | Array | Second data series |
Example Usage
private fastMA: number[] = bdpo.ema(data.close, 12);
private slowMA: number[] = bdpo.ema(data.close, 26);
private crossUp: boolean[] = bdpo.crossover(this.fastMA, this.slowMA);
Parameters
Parameter | Type | Description |
---|---|---|
series1 | Array | First data series |
series2 | Array | Second data series |
Example Usage
private fastMA: number[] = bdpo.ema(data.close, 12);
private slowMA: number[] = bdpo.ema(data.close, 26);
private crossDown: boolean[] = bdpo.crossunder(this.fastMA, this.slowMA);
// Check for bearish crossover signal
if (this.crossDown[this.crossDown.length - 1]) {
// Fast MA crossed below slow MA - bearish signal
}
Parameters
Parameter | Type | Description |
---|---|---|
series1 | Array | First data series |
series2 | Array | Second data series |
Example Usage
private rsi: number[] = bdpo.rsi(data.close, 14);
private rsiCrosses70: boolean[] = bdpo.crossany(this.rsi, [70]); // Cross 70 level
// Detect any crossover of RSI with 70 level
if (this.rsiCrosses70[this.rsiCrosses70.length - 1]) {
// RSI crossed 70 level in either direction
}
Parameters
Parameter | Type | Description |
---|---|---|
series | Array | Data series to check |
number | Number | Level to check for crossover |
Example Usage
private rsi: number[] = bdpo.rsi(data.close, 14);
private rsiCrossesAbove30: boolean[] = bdpo.crossOverNumber(this.rsi, 30);
// Check if RSI crosses above 30 (exit oversold)
if (this.rsiCrossesAbove30[this.rsiCrossesAbove30.length - 1]) {
// RSI just crossed above 30 - potential buy signal
}
Complete Strategy Example
class MultiIndicatorStrategy {
// Public configuration
public rsiPeriod: number = 14;
public smaPeriod: number = 20;
public bbandsPeriod: number = 20;
public bbandsStdDev: number = 2;
// Private variables
private rsi: number[] = [];
private sma: number[] = [];
private bbands: any = null;
private atr: number[] = [];
public __init__(): void {
console.log("Multi-indicator strategy initialized");
}
public __tick__(data: any): void {
// Calculate all indicators
this.rsi = bdpo.rsi(data.close, this.rsiPeriod);
this.sma = bdpo.sma(data.close, this.smaPeriod);
this.bbands = bdpo.bbands(data.close, this.bbandsPeriod, this.bbandsStdDev);
this.atr = bdpo.atr(data.high, data.low, data.close, 14);
const currentPrice: number = data.close[data.close.length - 1];
const currentRSI: number = this.rsi[this.rsi.length - 1];
const currentSMA: number = this.sma[this.sma.length - 1];
const upperBand: number = this.bbands.upper[this.bbands.upper.length - 1];
const lowerBand: number = this.bbands.lower[this.bbands.lower.length - 1];
const currentATR: number = this.atr[this.atr.length - 1];
// Entry conditions
const bullishCondition: boolean = currentPrice > currentSMA &&
currentRSI < 70 &&
currentPrice > lowerBand;
const bearishCondition: boolean = currentPrice < currentSMA &&
currentRSI > 30 &&
currentPrice < upperBand;
// Execute trades
if (bullishCondition) {
const stopLoss: number = currentPrice - (currentATR * 2);
const takeProfit: number = currentPrice + (currentATR * 3);
bdpo.orderSend("BUY", 0.1, stopLoss, takeProfit);
} else if (bearishCondition) {
const stopLoss: number = currentPrice + (currentATR * 2);
const takeProfit: number = currentPrice - (currentATR * 3);
bdpo.orderSend("SELL", 0.1, stopLoss, takeProfit);
}
}
public __shutdown__(): void {
console.log("Multi-indicator strategy completed");
}
}
Trade Functions
Order execution and trade management functionality
bdpo
object and handle both live trading and
backtesting scenarios automatically.
Order Execution
Parameters
Parameter | Type | Description | Required |
---|---|---|---|
options.orderType | String | Order type: BUY, SELL, BUYLIMIT, SELLLIMIT, BUYSTOP, SELLSTOP | ✅ |
options.lotSize | Number | Position size/lot size | ✅ |
options.price | Number | Order price (required for limit/stop orders) | ❌ |
options.stopLoss | Number | Stop loss price level | ❌ |
options.takeProfit | Number | Take profit price level | ❌ |
options.note | String | Order notes/comments | ❌ |
Returns
Promise - Resolves with order object or rejects with error
Example Usage
// Market buy order
await bdpo.orderSend({
orderType: "BUY",
lotSize: 1000,
stopLoss: 95.0,
takeProfit: 105.0,
note: "Bullish breakout entry"
});
// Limit order with risk management
await bdpo.orderSend({
orderType: "BUYLIMIT",
lotSize: 500,
price: 98.50,
stopLoss: 96.00,
takeProfit: 103.00
});
// Stop order for trend following
await bdpo.orderSend({
orderType: "BUYSTOP",
lotSize: 750,
price: 102.00,
stopLoss: 99.50
});
Position Management
Parameters
No parameters required
Returns
Void - Executes close orders for all positions
Example Usage
// Emergency close all positions
bdpo.orderCloseAll();
// Close all positions at end of trading session
if (bdpo.isMarketClose()) {
bdpo.orderCloseAll();
}
Order Validation & Best Practices
Example Usage
// Comprehensive order validation and execution
class OrderManager {
public maxPositionSize: number = 1000;
public maxRiskPercent: number = 2;
public async sendOrder(orderType: string, lotSize: number, options: any = {}): Promise {
try {
// Validate position size
if (lotSize > this.maxPositionSize) {
bdpo.log(`Order size ${lotSize} exceeds maximum ${this.maxPositionSize}`);
return false;
}
// Check account balance for risk management
const balance = bdpo.getBalance();
const riskAmount = balance * (this.maxRiskPercent / 100);
const currentPrice = bdpo.getPrice();
const orderValue = lotSize * currentPrice;
if (orderValue > riskAmount) {
bdpo.log(`Order value exceeds risk limit: ${orderValue} > ${riskAmount}`);
return false;
}
// Execute order with proper error handling
await bdpo.orderSend({
orderType: orderType,
lotSize: lotSize,
stopLoss: options.stopLoss,
takeProfit: options.takeProfit,
note: options.note || `Auto order: ${orderType}`
});
bdpo.log(`Order executed successfully: ${orderType} ${lotSize}`);
return true;
} catch (error) {
bdpo.log(`Order execution failed: ${error.message}`);
return false;
}
}
}
Example Usage
// Advanced position sizing with ATR-based stops
class RiskManagedTrading {
public riskPerTrade: number = 0.02; // 2% risk per trade
public calculatePositionSize(entryPrice: number, stopLoss: number): number {
const balance = bdpo.getBalance();
const riskAmount = balance * this.riskPerTrade;
const riskPerShare = Math.abs(entryPrice - stopLoss);
if (riskPerShare === 0) {
bdpo.log("Error: Stop loss equals entry price");
return 0;
}
const positionSize = Math.floor(riskAmount / riskPerShare);
bdpo.log(`Position size calculated: ${positionSize} shares`);
return positionSize;
}
public async placeManagedOrder(orderType: string, entryPrice: number, atrValue: number): Promise {
// Calculate dynamic stop loss based on ATR
const stopMultiplier = 2.0;
const stopLoss = orderType === "BUY"
? entryPrice - (atrValue * stopMultiplier)
: entryPrice + (atrValue * stopMultiplier);
const takeProfit = orderType === "BUY"
? entryPrice + (atrValue * 3.0) // 3:1 risk-reward
: entryPrice - (atrValue * 3.0);
const positionSize = this.calculatePositionSize(entryPrice, stopLoss);
if (positionSize > 0) {
await bdpo.orderSend({
orderType: orderType,
lotSize: positionSize,
price: orderType.includes("LIMIT") || orderType.includes("STOP") ? entryPrice : undefined,
stopLoss: stopLoss,
takeProfit: takeProfit,
note: `ATR-based order: R=${this.riskPerTrade * 100}%`
});
}
}
}
Order Types
Market Orders
BUY - Immediate buy at current ask price
SELL - Immediate sell at current bid price
Limit Orders
BUYLIMIT - Buy when price drops to specified level
SELLLIMIT - Sell when price rises to specified level
Stop Orders
BUYSTOP - Buy when price breaks above specified level
SELLSTOP - Sell when price breaks below specified level
Position Functions
Position tracking and portfolio management
Position Retrieval
Returns
Array - Array of open position objects
Example Usage
// Get all open positions
const openPositions: any[] = bdpo.getOpenPositions();
// Check if we have any long positions
const longPositions = openPositions.filter(pos => pos.side === 'BUY');
bdpo.log(`Currently holding ${longPositions.length} long positions`);
// Calculate total position value
const totalValue = openPositions.reduce((sum, pos) => sum + (pos.size * pos.price), 0);
Returns
Array - Array of pending order objects
Example Usage
// Get pending orders
const pendingOrders: any[] = bdpo.getPendingPositions();
// Check for pending buy orders
const pendingBuys = pendingOrders.filter(order =>
order.side === 'BUYLIMIT' || order.side === 'BUYSTOP'
);
// Cancel old pending orders (if needed)
if (pendingBuys.length > 5) {
bdpo.log("Too many pending buy orders, consider cleanup");
}
Returns
Array - Array of closed position objects
Example Usage
// Analyze trading performance
const closedPositions: any[] = bdpo.getClosedPositions();
// Calculate win rate
const winners = closedPositions.filter(pos => pos.profit > 0);
const winRate = (winners.length / closedPositions.length) * 100;
// Calculate total P&L
const totalPnL = closedPositions.reduce((sum, pos) => sum + pos.profit, 0);
bdpo.log(`Win Rate: ${winRate.toFixed(2)}%, Total P&L: $${totalPnL.toFixed(2)}`);
Returns
Number - Number of open positions
Example Usage
// Quick position count check
const openCount: number = bdpo.getTotalOpenPositions();
// Position management based on count
if (openCount >= 10) {
bdpo.log("High position count - monitoring closely");
} else if (openCount === 0) {
bdpo.log("No open positions - ready for new trades");
}
bdpo.log(`Current open positions: ${openCount}`);
Returns
Number - Number of pending orders
Example Usage
// Monitor pending order count
const pendingCount: number = bdpo.getTotalPendingPositions();
// Cleanup strategy for too many pending orders
if (pendingCount > 20) {
bdpo.log("Too many pending orders - consider cleanup");
// Implementation to cancel old orders would go here
}
bdpo.log(`Pending orders: ${pendingCount}`);
Returns
Number - Number of closed positions
Example Usage
// Track trading activity
const closedCount: number = bdpo.getTotalClosedPositions();
// Performance analysis trigger
if (closedCount >= 100) {
bdpo.log("Milestone reached - analyzing 100 completed trades");
// Trigger performance analysis
}
bdpo.log(`Total completed trades: ${closedCount}`);
Position Limits
Returns
Number - Maximum number of concurrent open positions (default: 50)
Example Usage
// Check position limits before opening new trades
const maxPositions: number = bdpo.getMaxOpenPositions();
const currentPositions: number = bdpo.getOpenPositions().length;
if (currentPositions < maxPositions) {
// Safe to open new position
await bdpo.orderSend({
orderType: "BUY",
lotSize: 1000
});
} else {
bdpo.log("Maximum positions reached, waiting for exits");
}
Position Object Structure
Property | Type | Description |
---|---|---|
id | String | Unique position identifier |
symbol | String | Trading symbol (e.g., "AAPL", "EURUSD") |
side | String | Position side: BUY, SELL, BUYLIMIT, etc. |
size | Number | Position size/lot size |
price | Number | Entry price |
stopLoss | Number | Stop loss price level |
takeProfit | Number | Take profit price level |
openDate | String | ISO timestamp when position was opened |
profit | Number | Current unrealized P&L (closed positions: realized P&L) |
Account Functions
Account balance and equity management
Balance Management
Returns
Number - Current account balance
Example Usage
// Get current balance
const balance: number = bdpo.getBalance();
// Calculate position size based on risk percentage
const riskPercent: number = 2; // 2% risk per trade
const riskAmount: number = balance * (riskPercent / 100);
// Use balance for position sizing
const currentPrice: number = bdpo.getPrice();
const maxPositionSize: number = riskAmount / currentPrice;
bdpo.log(`Account Balance: $${balance.toFixed(2)}`);
Returns
Number - Current account equity
Example Usage
// Monitor account equity
const equity: number = bdpo.getEquity();
const balance: number = bdpo.getBalance();
const unrealizedPnL: number = equity - balance;
// Check margin usage
const marginUsage: number = ((balance - equity) / balance) * 100;
if (marginUsage > 50) {
bdpo.log("Warning: High margin usage detected");
// Consider reducing position sizes
}
bdpo.log(`Equity: $${equity.toFixed(2)}, Unrealized P&L: $${unrealizedPnL.toFixed(2)}`);
Returns
Number - Current profit/loss amount
Example Usage
// Track performance metrics
const profit: number = bdpo.getProfit();
const balance: number = bdpo.getBalance();
const profitPercent: number = (profit / balance) * 100;
// Implement profit protection
if (profitPercent > 10) {
bdpo.log("Profit target reached, reducing risk");
// Reduce position sizes or close some positions
}
// Stop loss at account level
if (profitPercent < -5) {
bdpo.log("Daily loss limit reached, closing all positions");
bdpo.orderCloseAll();
}
bdpo.log(`Current P&L: $${profit.toFixed(2)} (${profitPercent.toFixed(2)}%)`);
Risk Management Examples
class RiskManagedStrategy {
public maxRiskPercent: number = 2; // Max 2% risk per trade
public maxDailyLoss: number = -5; // Stop trading at -5% daily loss
public profitTarget: number = 10; // Take profits at +10%
private _initialBalance: number = 0;
__init__(): void {
this._initialBalance = bdpo.getBalance();
}
__tick__(): any {
// Calculate current performance
const currentBalance: number = bdpo.getBalance();
const dailyPnL: number = ((currentBalance - this._initialBalance) / this._initialBalance) * 100;
// Check daily loss limit
if (dailyPnL <= this.maxDailyLoss) {
bdpo.log("Daily loss limit reached - stopping trading");
bdpo.orderCloseAll();
return { status: "stopped", reason: "daily_loss_limit" };
}
// Check profit target
if (dailyPnL >= this.profitTarget) {
bdpo.log("Daily profit target reached - reducing risk");
// Reduce position sizes or close half positions
}
// Calculate position size based on account risk
const riskAmount: number = currentBalance * (this.maxRiskPercent / 100);
const currentPrice: number = bdpo.getPrice();
const stopLossDistance: number = currentPrice * 0.02; // 2% stop loss
const positionSize: number = riskAmount / stopLossDistance;
return {
accountMetrics: {
balance: currentBalance,
dailyPnL: dailyPnL,
riskAmount: riskAmount,
positionSize: positionSize
}
};
}
}
Plotting Functions
Chart visualization and overlay capabilities for custom indicators and signals
Basic Plotting
Parameters
Parameter | Type | Description |
---|---|---|
stringType |
String | Plot type: "splines", "histogram", "area", "band", "range", "sparse", "candles", "arrowTrades", "cloud", "priceLabels", "superBands", "trades" |
dataArray |
Array | Object | Data format depends on plot type (see data formats below) |
propsObject |
Object (optional) | Styling properties: color, width, opacity, etc. |
settingsObject |
Object (optional) | Available settings: precision |
Data Formats by Plot Type
- splines: [line1, line2, ...] - Multiple line plots
- range: [value] - Single value range plot
- sparse: [value, ?direction] - Sparse data with optional direction
- area: [value] - Area fill plot
- band: [high, mid, low] - Three-line band plot
- candles: [open, high, low, close, ?volume] - Candlestick chart
- arrowTrades: {high, low, close, trades: [[dir, ?label, ?big], ...]} - Trade arrows
- cloud: [line1, line2] - Cloud between two lines
- histogram: [hist, ?value, ?signal] - Histogram with optional value and signal
- priceLabels: {text, dir, pin, color?, back?, stroke?, offset?} - Price labels
- superBands: [high1, mid1, low1, high2, mid2, low2] - Multiple bands
- trades: [dir, price, ?label] - Trade markers
Properties by Plot Type
Property | Type | Default | Description |
---|---|---|---|
showRange |
boolean | true | Show/hide the range plot |
rangeColor |
Color | #ec206e | Color of the range line |
rangeBackColor |
Color | #381e9c16 | Background color of the range area |
rangeBandColor |
Color | #535559 | Color of the range band lines |
rangeLineWidth |
number | 1 | Width of the range lines |
upperBand |
number | 70 | Upper range band value |
lowerBand |
number | 30 | Lower range band value |
Property | Type | Default | Description |
---|---|---|---|
showSpline |
boolean | true | Show/hide the spline plot |
splineColor |
Color | #31ce31 | Color of the spline line |
splineWidth |
number | 2 | Width of the spline line |
skipNan |
boolean | true | Skip NaN values when drawing the line |
Property | Type | Default | Description |
---|---|---|---|
showSparse |
boolean | true | Show/hide the sparse plot |
sparseColor |
Color | #5700ed | Color of the sparse points |
sparseSize |
number | 3 | Size of the sparse points |
sparseShape |
string | point | Shape of the sparse points |
Property | Type | Default | Description |
---|---|---|---|
showArea |
boolean | true | Show/hide the area plot |
areaColor |
Color | #31ce31 | Color of the area line |
areaLineWidth |
number | 1.25 | Width of the area line |
areaBack1 |
Color | areaColor + '65' | Primary background color (gradient start) |
areaBack2 |
Color | areaColor + '01' | Secondary background color (gradient end) |
Property | Type | Default | Description |
---|---|---|---|
showBand |
boolean | true | Show/hide the band plot |
upperBandColor |
Color | #00dbeb | Color of the upper band line |
lowerBandColor |
Color | #00dbeb | Color of the lower band line |
middleBandColor |
Color | #00dbeb | Color of the middle band line |
bandBackColor |
Color | #00dbeb22 | Background color of the band area |
bandLineWidth |
number | 1 | Width of the band lines |
showMid |
boolean | true | Show/hide the middle band line |
Property | Type | Default | Description |
---|---|---|---|
showArrowTrades |
boolean | true | Show/hide the arrow trades |
arrowBuyColor |
Color | #08c65e | Color of buy arrows |
arrowSellColor |
Color | #e42633 | Color of sell arrows |
arrowSize |
number | 7 | Size of the arrow markers |
arrowShowLabels |
boolean | true | Show/hide arrow labels |
arrowMarkerOutline |
boolean | true | Show outline around arrow markers |
arrowOutlineWidth |
number | 4 | Width of the arrow outline |
Property | Type | Default | Description |
---|---|---|---|
showCandles |
boolean | true | Show/hide the candles |
colorBodyUp |
color | core.colors.candleUp | Color of bullish candle bodies |
colorBodyDw |
color | core.colors.candleDw | Color of bearish candle bodies |
colorWickUp |
color | core.colors.wickUp | Color of bullish candle wicks |
colorWickDw |
color | core.colors.wickDw | Color of bearish candle wicks |
colorVolUp |
color | core.colors.volUp | Color of bullish volume bars |
colorVolDw |
color | core.colors.volDw | Color of bearish volume bars |
showVolume |
boolean | true | Show/hide volume bars |
currencySymbol |
string | '' | Currency symbol for price labels |
showAvgVolume |
boolean | true | Show/hide average volume line |
avgVolumeSMA |
number | 20 | Period for average volume calculation |
colorAvgVol |
color | #1cccb777 | Color of the average volume line |
Property | Type | Default | Description |
---|---|---|---|
showCloud |
boolean | true | Show/hide the cloud |
cloudColor1 |
color | #55d7b0aa | Color of the first cloud line |
cloudColor2 |
color | #d94d64aa | Color of the second cloud line |
cloudBack1 |
color | #79ffde60 | Background color when line1 > line2 |
cloudBack2 |
color | #ff246c60 | Background color when line2 > line1 |
cloudDrawLines |
boolean | true | Draw the cloud boundary lines |
Property | Type | Default | Description |
---|---|---|---|
showHistogram |
boolean | true | Show/hide the histogram |
histBarWidth |
number | 4 | Width of histogram bars |
histLineWidth |
number | 1 | Width of histogram bar outlines |
histColorUp |
Color | #35a776 | Color for positive histogram values |
histColorDw |
Color | #e54150 | Color for negative histogram values |
histColorSemiUp |
Color | #79e0b3 | Color for weak positive values |
histColorSemiDw |
Color | #ea969e | Color for weak negative values |
histColorValue |
Color | #3782f2 | Color for value line |
histColorSignal |
Color | #f48709 | Color for signal line |
Property | Type | Default | Description |
---|---|---|---|
showPriceLabels |
boolean | true | Show/hide price labels |
labelColor |
Color | core.colors.text | Text color of the labels |
labelBack |
Color | core.colors.back + '00' | Background color of the labels |
labelStroke |
Color | core.colors.scale + '00' | Stroke color of the labels |
labelBorderRadius |
number | 3 | Border radius of label boxes |
labelOffset |
number | 5 | Offset distance from pin point |
Property | Type | Default | Description |
---|---|---|---|
showSuperBands |
boolean | true | Show/hide the super bands |
superBandsColor1 |
color | #d80d3888 | Color of the first band system |
superBandsColor1dark |
color | #d80d3888 | Dark theme color of the first band system |
superBandsColor2 |
color | #1edbbe88 | Color of the second band system |
superBandsColor2dark |
color | #1edbbe88 | Dark theme color of the second band system |
Property | Type | Default | Description |
---|---|---|---|
showTrades |
boolean | true | Show/hide trade markers |
tradeBuyColor |
color | #08b2c6 | Color of buy trade markers |
tradeSellColor |
color | #e42633 | Color of sell trade markers |
tradeRadius |
number | 4 | Radius of trade marker circles |
tradeShowLabels |
boolean | true | Show/hide trade labels |
tradeMarkerOutline |
boolean | true | Show outline around trade markers |
tradeOutlineWidth |
number | 4 | Width of the trade marker outline |
Returns
void - Adds plot to chart overlay queue
Example Usage
// Basic spline plot with custom properties
const prices: number[] = bdpo.getClose(data);
const customMA: number[] = bdpo.sma(prices, 20);
bdpo.plot("splines", [customMA], {
splineColor: "#2196F3",
splineWidth: 2,
skipNan: true
}, {
precision: 4
});
// Range plot for oscillator with custom bands
const rsi: number[] = bdpo.rsi(prices, 14);
bdpo.plot("range", [rsi], {
rangeColor: "#9C27B0",
rangeBackColor: "#9C27B022",
rangeBandColor: "#FF5722",
rangeLineWidth: 2,
upperBand: 70,
lowerBand: 30
}, {
precision: 5
});
// Area plot with gradient fill
const atr: number[] = bdpo.atr(bdpo.getHigh(data), bdpo.getLow(data), prices, 14);
const upperBand: number[] = prices.map((price, i) => price + atr[i] * 2);
bdpo.plot("area", [upperBand], {
areaColor: "#4CAF50",
areaLineWidth: 1.5,
areaBack1: "#4CAF5080",
areaBack2: "#4CAF5010"
}, {
precision: 5
});
// Band plot for Bollinger Bands
const bbands = bdpo.bbands(prices, 20, 2);
bdpo.plot("band", [bbands.upper, bbands.middle, bbands.lower], {
upperBandColor: "#2196F3",
middleBandColor: "#FF9800",
lowerBandColor: "#2196F3",
bandBackColor: "#2196F320",
bandLineWidth: 1,
showMid: true
}, {
precision: 5
});
// Histogram with conditional colors
const macd = bdpo.macd(prices, 12, 26, 9);
bdpo.plot("histogram", [macd.histogram], {
histColorUp: "#4CAF50",
histColorDw: "#F44336",
histColorSemiUp: "#8BC34A",
histColorSemiDw: "#FFAB91",
histBarWidth: 6,
histLineWidth: 1
}, {
precision: 5
});
// Trade arrows with custom styling
bdpo.plot("arrowTrades", {
high: bdpo.getHigh(data),
low: bdpo.getLow(data),
close: prices,
trades: [[1, "BUY", false], [-1, "SELL", true]]
}, {
arrowBuyColor: "#00E676",
arrowSellColor: "#FF1744",
arrowSize: 8,
arrowShowLabels: true,
arrowMarkerOutline: true,
arrowOutlineWidth: 3
}, {
precision: 5
});
// Price labels with custom styling
bdpo.plot("priceLabels", {
text: "Important Level",
dir: 1,
pin: "close",
color: "#FFFFFF",
back: "#FF5722",
stroke: "#D84315",
offset: 10
}, {
labelColor: "#FFFFFF",
labelBack: "#FF572280",
labelStroke: "#D84315",
labelBorderRadius: 5,
labelOffset: 8
}, {
precision: 2
});
Spline Plots
For detailed spline properties, see the Spline Properties section above. Key properties include splineColor
, splineWidth
, and skipNan
.
Example Usage
// Moving average crossover system
const prices: number[] = bdpo.getClose(data);
const fastMA: number[] = bdpo.ema(prices, 12);
const slowMA: number[] = bdpo.ema(prices, 26);
// Plot fast MA in green using spline properties
bdpo.plot("splines", [fastMA], {
splineColor: "#00E676",
splineWidth: 2,
skipNan: true
}, {
precision: 4
});
// Plot slow MA in red using spline properties
bdpo.plot("splines", [slowMA], {
splineColor: "#FF5722",
splineWidth: 2,
skipNan: true
}, {
precision: 4
});
// Dynamic support/resistance levels
const highs: number[] = bdpo.getHigh(data);
const lows: number[] = bdpo.getLow(data);
// Rolling resistance level
const resistance: number[] = [];
for (let i = 0; i < highs.length; i++) {
const periodHighs = highs.slice(Math.max(0, i - 20), i + 1);
resistance.push(bdpo.max(periodHighs));
}
// Rolling support level
const support: number[] = [];
for (let i = 0; i < lows.length; i++) {
const periodLows = lows.slice(Math.max(0, i - 20), i + 1);
support.push(bdpo.min(periodLows));
}
bdpo.plot("splines", [resistance], {
splineColor: "#FF1744",
splineWidth: 2,
skipNan: true
}, {
precision: 4,
});
bdpo.plot("splines", [support], {
splineColor: "#00E676",
splineWidth: 2,
skipNan: true
}, {
precision: 4,
});
Histogram Plots
For detailed histogram properties, see the Histogram Properties section above. Key properties include histColorUp
, histColorDw
, histBarWidth
, and histLineWidth
.
Example Usage
// MACD histogram with proper histogram properties
const prices: number[] = bdpo.getClose(data);
const macd: any = bdpo.macd(prices, 12, 26, 9);
const histogram: number[] = macd.histogram;
bdpo.plot("histogram", [histogram], {
histColorUp: "#4CAF50",
histColorDw: "#F44336",
histColorSemiUp: "#8BC34A",
histColorSemiDw: "#FFAB91",
histBarWidth: 6,
histLineWidth: 1
}, {
precision: 4,
});
// Volume profile histogram
const volumes: number[] = bdpo.getVolume(data);
const volumeMA: number[] = bdpo.sma(volumes, 20);
// Relative volume (current vs average)
const relativeVolume: number[] = volumes.map((vol, i) =>
volumeMA[i] > 0 ? (vol / volumeMA[i] - 1) * 100 : 0
);
bdpo.plot("histogram", [relativeVolume], {
histColorUp: "#2196F3",
histColorDw: "#FF5722",
histColorValue: "#FF9800",
histBarWidth: 4,
histLineWidth: 1
}, {
precision: 1,
});
// Momentum oscillator with zero line
const rsi: number[] = bdpo.rsi(prices, 14);
const rsiCentered: number[] = rsi.map(value => value - 50); // Center around 0
bdpo.plot("histogram", [rsiCentered], {
histColorUp: "#4CAF50",
histColorDw: "#F44336",
histColorSemiUp: "#8BC34A",
histColorSemiDw: "#FFAB91",
histBarWidth: 5,
histLineWidth: 1
}, {
precision: 2,
});
Signal and Arrow Plots
For detailed signal properties, see the ArrowTrades Properties and Trades Properties sections above. Key properties include arrowBuyColor
, arrowSellColor
, arrowSize
, tradeBuyColor
, tradeSellColor
, and tradeRadius
.
Example Usage
// Moving average crossover signals with arrow trades
const prices: number[] = bdpo.getClose(data);
const fastMA: number[] = bdpo.ema(prices, 12);
const slowMA: number[] = bdpo.ema(prices, 26);
// Generate crossover signals
const buySignals: number[] = [];
const sellSignals: number[] = [];
for (let i = 1; i < prices.length; i++) {
// Buy signal: fast MA crosses above slow MA
if (fastMA[i] > slowMA[i] && fastMA[i-1] <= slowMA[i-1]) {
buySignals.push(prices[i]);
} else {
buySignals.push(null);
}
// Sell signal: fast MA crosses below slow MA
if (fastMA[i] < slowMA[i] && fastMA[i-1] >= slowMA[i-1]) {
sellSignals.push(prices[i]);
} else {
sellSignals.push(null);
}
}
// Plot buy signals with arrow trades
bdpo.plot("arrowTrades", [buySignals], {
arrowBuyColor: "#4CAF50",
arrowSellColor: "#F44336",
arrowSize: 8,
arrowShowLabels: true,
arrowMarkerOutline: true,
arrowOutlineWidth: 2
}, {
precision: 4,
});
// Plot sell signals with arrow trades
bdpo.plot("arrowTrades", [sellSignals], {
arrowBuyColor: "#4CAF50",
arrowSellColor: "#F44336",
arrowSize: 8,
arrowShowLabels: true,
arrowMarkerOutline: true,
arrowOutlineWidth: 2
}, {
precision: 4,
});
// Trade execution markers using trades plot type
const tradeData: any[] = [
// Buy trade at index 50
{ type: 'buy', price: 100.50, index: 50 },
// Sell trade at index 75
{ type: 'sell', price: 105.25, index: 75 }
];
// Convert trade data to plot format
const tradeMarkers: number[] = new Array(prices.length).fill(null);
tradeData.forEach(trade => {
tradeMarkers[trade.index] = trade.price;
});
bdpo.plot("trades", [tradeMarkers], {
tradeBuyColor: "#08b2c6",
tradeSellColor: "#e42633",
tradeRadius: 5,
tradeShowLabels: true,
tradeMarkerOutline: true,
tradeOutlineWidth: 3
}, {
title: "Trade Executions"
});
for (let i = 1; i < fastMA.length; i++) {
const currentFast = fastMA[i];
const currentSlow = slowMA[i];
const prevFast = fastMA[i - 1];
const prevSlow = slowMA[i - 1];
// Bullish crossover
if (prevFast <= prevSlow && currentFast > currentSlow) {
buySignals[i] = prices[i];
} else {
buySignals[i] = null;
}
// Bearish crossover
if (prevFast >= prevSlow && currentFast < currentSlow) {
sellSignals[i] = prices[i];
} else {
sellSignals[i] = null;
}
}
// Plot buy signals
bdpo.plot("arrowTrades", {
high: bdpo.getHigh(data),
low: bdpo.getLow(data),
close: prices,
trades: buySignals.map((signal, i) => signal ? [1, "BUY", false] : null).filter(Boolean)
}, {
arrowBuyColor: "#4CAF50",
arrowSellColor: "#F44336",
arrowSize: 8,
arrowShowLabels: true,
arrowMarkerOutline: true,
arrowOutlineWidth: 2
}, {
title: "Buy Signals"
});
// Plot sell signals
bdpo.plot("arrowTrades", {
high: bdpo.getHigh(data),
low: bdpo.getLow(data),
close: prices,
trades: sellSignals.map((signal, i) => signal ? [-1, "SELL", false] : null).filter(Boolean)
}, {
arrowBuyColor: "#4CAF50",
arrowSellColor: "#F44336",
arrowSize: 8,
arrowShowLabels: true,
arrowMarkerOutline: true,
arrowOutlineWidth: 2
}, {
title: "Sell Signals"
});
// Support/Resistance breakout alerts
const highs: number[] = bdpo.getHigh(data);
const lows: number[] = bdpo.getLow(data);
const breakoutSignals: number[] = [];
for (let i = 20; i < prices.length; i++) {
const recentHigh = bdpo.max(highs.slice(i - 20, i));
const recentLow = bdpo.min(lows.slice(i - 20, i));
// Breakout above resistance
if (prices[i] > recentHigh) {
breakoutSignals[i] = prices[i];
}
// Breakdown below support
else if (prices[i] < recentLow) {
breakoutSignals[i] = prices[i];
} else {
breakoutSignals[i] = null;
}
}
bdpo.plot("trades", breakoutSignals.map((signal, i) =>
signal ? [1, signal, "BREAKOUT"] : null
).filter(Boolean), {
tradeBuyColor: "#FF9800",
tradeSellColor: "#FF9800",
tradeRadius: 6,
tradeShowLabels: true,
tradeMarkerOutline: true,
tradeOutlineWidth: 2
}, {
title: "Breakout Alerts"
});
Advanced Plotting Techniques
Advanced Features
Feature | Description | Usage |
---|---|---|
Multi-layer Overlays | Combine multiple plot types | Layer lines, histograms, and signals |
Conditional Styling | Dynamic colors based on conditions | Trend-aware coloring systems |
Data Transformation | Pre-process data before plotting | Normalization, smoothing, scaling |
Performance Optimization | Efficient plotting for large datasets | Data sampling and caching |
Example Usage
// Advanced market structure analysis
const prices: number[] = bdpo.getClose(data);
const highs: number[] = bdpo.getHigh(data);
const lows: number[] = bdpo.getLow(data);
const volumes: number[] = bdpo.getVolume(data);
// 1. Trend strength indicator
const trendStrength: number[] = [];
const period = 20;
for (let i = period; i < prices.length; i++) {
const periodPrices = prices.slice(i - period, i + 1);
const linearReg = calculateLinearRegression(periodPrices);
const slope = linearReg.slope;
const rSquared = linearReg.rSquared;
// Combine slope and correlation for trend strength
trendStrength[i] = slope * rSquared * 100;
}
// Plot trend strength with conditional colors
const strengthColors = trendStrength.map(strength => {
if (strength > 0.5) return "#4CAF50"; // Strong uptrend
if (strength > 0.1) return "#8BC34A"; // Weak uptrend
if (strength < -0.5) return "#F44336"; // Strong downtrend
if (strength < -0.1) return "#FF5722"; // Weak downtrend
return "#9E9E9E"; // Sideways
});
bdpo.plot("histogram", [trendStrength], {
histColorUp: "#4CAF50",
histColorDw: "#F44336",
histColorSemiUp: "#8BC34A",
histColorSemiDw: "#FFAB91",
histBarWidth: 4,
histLineWidth: 1
}, {
title: "Trend Strength",
precision: 2
});
// 2. Volume-weighted price levels
const vwap: number[] = [];
const vwapBands: { upper: number[], lower: number[] } = { upper: [], lower: [] };
for (let i = 0; i < prices.length; i++) {
// Calculate VWAP for current session
const sessionStart = Math.max(0, i - 100); // 100-period session
const sessionPrices = prices.slice(sessionStart, i + 1);
const sessionVolumes = volumes.slice(sessionStart, i + 1);
let totalVolumePrice = 0;
let totalVolume = 0;
for (let j = 0; j < sessionPrices.length; j++) {
totalVolumePrice += sessionPrices[j] * sessionVolumes[j];
totalVolume += sessionVolumes[j];
}
vwap[i] = totalVolume > 0 ? totalVolumePrice / totalVolume : prices[i];
// Calculate standard deviation bands
const variance = sessionPrices.reduce((sum, price, idx) => {
const weightedPrice = price * sessionVolumes[idx];
return sum + Math.pow(weightedPrice - vwap[i] * sessionVolumes[idx], 2);
}, 0) / totalVolume;
const stdDev = Math.sqrt(variance);
vwapBands.upper[i] = vwap[i] + stdDev * 2;
vwapBands.lower[i] = vwap[i] - stdDev * 2;
}
// Plot VWAP and bands
bdpo.plot("splines", [vwap], {
splineColor: "#FF9800",
splineWidth: 2,
skipNan: true
}, {
title: "VWAP",
precision: 4
});
bdpo.plot("splines", [vwapBands.upper], {
splineColor: "#FF9800",
splineWidth: 1,
skipNan: true
}, {
title: "VWAP Upper Band"
});
bdpo.plot("splines", [vwapBands.lower], {
splineColor: "#FF9800",
splineWidth: 1,
skipNan: true
}, {
title: "VWAP Lower Band"
});
// 3. Market regime detection
const volatility: number[] = bdpo.atr(highs, lows, prices, 14);
const avgVol: number[] = bdpo.sma(volatility, 50);
const volRatio: number[] = volatility.map((vol, i) =>
avgVol[i] > 0 ? vol / avgVol[i] : 1
);
// Regime classification
const regimeSignals: number[] = volRatio.map((ratio, i) => {
if (ratio > 1.5) return 2; // High volatility regime
if (ratio < 0.7) return -2; // Low volatility regime
return 0; // Normal regime
});
bdpo.plot("histogram", [regimeSignals], {
conditionalColors: {
2: "#F44336", // High vol (red)
0: "#9E9E9E", // Normal (gray)
"-2": "#4CAF50" // Low vol (green)
},
opacity: 0.6
}, {
title: "Market Regime",
precision: 0
});
// Helper function for linear regression
function calculateLinearRegression(data: number[]): { slope: number, rSquared: number } {
const n = data.length;
const x = Array.from({ length: n }, (_, i) => i);
const y = data;
const sumX = bdpo.sum(x);
const sumY = bdpo.sum(y);
const sumXY = bdpo.sum(x.map((xi, i) => xi * y[i]));
const sumXX = bdpo.sum(x.map(xi => xi * xi));
const sumYY = bdpo.sum(y.map(yi => yi * yi));
const slope = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX);
const intercept = (sumY - slope * sumX) / n;
// Calculate R-squared
const yMean = sumY / n;
const ssRes = bdpo.sum(y.map((yi, i) => Math.pow(yi - (slope * x[i] + intercept), 2)));
const ssTot = bdpo.sum(y.map(yi => Math.pow(yi - yMean, 2)));
const rSquared = 1 - (ssRes / ssTot);
return { slope, rSquared };
}
- Use consistent color schemes across related plots for better visual coherence
- Implement conditional coloring to highlight important market conditions
- Layer multiple plot types (lines + histograms + signals) for comprehensive analysis
- Optimize plot data by filtering null values and using appropriate precision settings
- Consider chart readability - avoid overplotting with too many overlays
- Use opacity and line styles to differentiate between primary and secondary indicators
- Test plot performance with large datasets and implement data sampling if needed
- Combine plotting with logging to track indicator values and signal generation
Candlestick Functions
OHLCV data extraction and candlestick analysis functions
Data Access Functions
Parameters
Parameter | Type | Description |
---|---|---|
data |
Object | Multi-timeframe data object |
timeframe |
String|Number | Target timeframe (e.g., "5m", "1h", "1d") or 0/"current" for chart timeframe |
Returns
Array - Candlestick data array for the specified timeframe
Example Usage
// Get different timeframe data
const chartData: any[] = bdpo.getTimeframeData("current");
const hourlyData: any[] = bdpo.getTimeframeData("1h");
const dailyData: any[] = bdpo.getTimeframeData("1d");
// Use in strategy
if (chartData && chartData.length > 0) {
const currentPrice: number = chartData[chartData.length - 1][4]; // Last close
bdpo.log(`Current price: ${currentPrice}`);
}
Parameters
Parameter | Type | Description |
---|---|---|
data |
Object | Multi-timeframe data object |
Returns
Array - Current chart timeframe candlestick data
Example Usage
// Get current chart data
const chartData: any[] = bdpo.getChartData(data);
// Process current timeframe candles
if (chartData && chartData.length >= 50) {
const sma20: number[] = bdpo.sma(bdpo.getClose(chartData), 20);
const lastSMA: number = sma20[sma20.length - 1];
bdpo.log(`Current SMA20: ${lastSMA.toFixed(4)}`);
}
Parameters
Parameter | Type | Description |
---|---|---|
data |
Object | Multi-timeframe data object |
Returns
Array<String> - Array of available timeframe keys
Example Usage
// Check available timeframes
const timeframes: string[] = bdpo.getAvailableTimeframes(data);
bdpo.log(`Available timeframes: ${timeframes.join(", ")}`);
// Dynamically process multiple timeframes
timeframes.forEach((tf: string) => {
const tfData: any[] = bdpo.getTimeframeData(tf);
const closes: number[] = bdpo.getClose(tfData);
const lastClose: number = closes[closes.length - 1];
bdpo.log(`${tf} last close: ${lastClose}`);
});
OHLCV Extraction Functions
Parameters
Parameter | Type | Description |
---|---|---|
candleData |
Array | Array of candlestick data [timestamp, open, high, low, close, volume] |
Returns
Array<Number> - Array of timestamps
Example Usage
// Extract timestamps
const chartData: any[] = bdpo.getChartData(data);
const timestamps: number[] = bdpo.getTimestamps(chartData);
// Convert to dates and analyze time gaps
const dates: Date[] = timestamps.map((ts: number) => bdpo.timestampToDate(ts));
const lastCandleTime: Date = dates[dates.length - 1];
bdpo.log(`Last candle time: ${lastCandleTime.toISOString()}`);
Parameters
Parameter | Type | Description |
---|---|---|
candleData |
Array | Array of candlestick data [timestamp, open, high, low, close, volume] |
Returns
Array<Number> - Array of open prices
Example Usage
// Extract open prices
const chartData: any[] = bdpo.getChartData(data);
const opens: number[] = bdpo.getOpen(chartData);
// Calculate opening gap
const lastOpen: number = opens[opens.length - 1];
const prevClose: number = bdpo.getClose(chartData)[chartData.length - 2];
const gap: number = ((lastOpen - prevClose) / prevClose) * 100;
bdpo.log(`Opening gap: ${gap.toFixed(2)}%`);
Parameters
Parameter | Type | Description |
---|---|---|
candleData |
Array | Array of candlestick data [timestamp, open, high, low, close, volume] |
Returns
Array<Number> - Array of high prices
Example Usage
// Extract high prices
const chartData: any[] = bdpo.getChartData(data);
const highs: number[] = bdpo.getHigh(chartData);
// Find recent high
const recentHigh: number = bdpo.getHighestHigh(chartData, 20);
const currentPrice: number = bdpo.getClose(chartData)[chartData.length - 1];
const distanceFromHigh: number = ((currentPrice - recentHigh) / recentHigh) * 100;
bdpo.log(`Distance from 20-period high: ${distanceFromHigh.toFixed(2)}%`);
Parameters
Parameter | Type | Description |
---|---|---|
candleData |
Array | Array of candlestick data [timestamp, open, high, low, close, volume] |
Returns
Array<Number> - Array of low prices
Example Usage
// Extract low prices
const chartData: any[] = bdpo.getChartData(data);
const lows: number[] = bdpo.getLow(chartData);
// Find support level
const recentLow: number = bdpo.getLowestLow(chartData, 50);
const currentPrice: number = bdpo.getClose(chartData)[chartData.length - 1];
const supportLevel: number = recentLow * 1.02; // 2% above recent low
if (currentPrice <= supportLevel) {
bdpo.log("Price near support level");
}
Parameters
Parameter | Type | Description |
---|---|---|
candleData |
Array | Array of candlestick data [timestamp, open, high, low, close, volume] |
Returns
Array<Number> - Array of close prices
Example Usage
// Extract close prices for technical analysis
const chartData: any[] = bdpo.getChartData(data);
const closes: number[] = bdpo.getClose(chartData);
// Use with indicators
const sma20: number[] = bdpo.sma(closes, 20);
const ema12: number[] = bdpo.ema(closes, 12);
const rsi: number[] = bdpo.rsi(closes, 14);
// Current values
const currentClose: number = closes[closes.length - 1];
const currentSMA: number = sma20[sma20.length - 1];
const currentRSI: number = rsi[rsi.length - 1];
bdpo.log(`Close: ${currentClose}, SMA20: ${currentSMA.toFixed(4)}, RSI: ${currentRSI.toFixed(2)}`);
Parameters
Parameter | Type | Description |
---|---|---|
candleData |
Array | Array of candlestick data [timestamp, open, high, low, close, volume] |
Returns
Array<Number> - Array of volume values
Example Usage
// Extract volume for analysis
const chartData: any[] = bdpo.getChartData(data);
const volumes: number[] = bdpo.getVolume(chartData);
// Calculate volume moving average
const volumeSMA: number[] = bdpo.sma(volumes, 20);
const currentVolume: number = volumes[volumes.length - 1];
const avgVolume: number = volumeSMA[volumeSMA.length - 1];
// Volume spike detection
const volumeRatio: number = currentVolume / avgVolume;
if (volumeRatio > 2.0) {
bdpo.log(`High volume spike detected: ${volumeRatio.toFixed(2)}x average`);
}
Data Filtering and Slicing Functions
Parameters
Parameter | Type | Description |
---|---|---|
candleData |
Array | Array of candlestick data |
count |
Number | Number of candles to retrieve from the end |
Returns
Array - Last N candles
Example Usage
// Get recent data for analysis
const chartData: any[] = bdpo.getChartData(data);
const last50Candles: any[] = bdpo.getLastCandles(chartData, 50);
// Calculate short-term indicators
const recentCloses: number[] = bdpo.getClose(last50Candles);
const shortRSI: number[] = bdpo.rsi(recentCloses, 14);
const recentHigh: number = bdpo.getHighestHigh(last50Candles, 20);
bdpo.log(`Recent RSI: ${shortRSI[shortRSI.length - 1].toFixed(2)}`);
Parameters
Parameter | Type | Description |
---|---|---|
candleData |
Array | Array of candlestick data |
count |
Number | Number of candles to retrieve from the beginning |
Returns
Array - First N candles
Example Usage
// Analyze historical data
const chartData: any[] = bdpo.getChartData(data);
const first100Candles: any[] = bdpo.getFirstCandles(chartData, 100);
// Calculate historical volatility
const historicalCloses: number[] = bdpo.getClose(first100Candles);
const returns: number[] = [];
for (let i = 1; i < historicalCloses.length; i++) {
const dailyReturn: number = (historicalCloses[i] - historicalCloses[i-1]) / historicalCloses[i-1];
returns.push(dailyReturn);
}
bdpo.log(`Historical period analyzed: ${first100Candles.length} candles`);
Parameters
Parameter | Type | Description |
---|---|---|
candleData |
Array | Array of candlestick data |
startTimestamp |
Number | Start timestamp (inclusive) |
endTimestamp |
Number | End timestamp (inclusive) |
Returns
Array - Candles within the timestamp range
Example Usage
// Analyze specific time period
const chartData: any[] = bdpo.getChartData(data);
const now: number = Date.now();
const yesterday: number = now - (24 * 60 * 60 * 1000);
const recentCandles: any[] = bdpo.getCandlesByTimestamp(chartData, yesterday, now);
const periodCloses: number[] = bdpo.getClose(recentCandles);
// Calculate period performance
if (periodCloses.length > 1) {
const periodReturn: number = ((periodCloses[periodCloses.length - 1] - periodCloses[0]) / periodCloses[0]) * 100;
bdpo.log(`24h return: ${periodReturn.toFixed(2)}%`);
}
Parameters
Parameter | Type | Description |
---|---|---|
candleData |
Array | Array of candlestick data |
Returns
Array|null - Last candle [timestamp, open, high, low, close, volume] or null if no data
Example Usage
// Get current candle information
const chartData: any[] = bdpo.getChartData(data);
const lastCandle: any[] | null = bdpo.getLastCandle(chartData);
if (lastCandle) {
const [timestamp, open, high, low, close, volume] = lastCandle;
const bodySize: number = Math.abs(close - open);
const range: number = high - low;
const bodyPercentage: number = (bodySize / range) * 100;
bdpo.log(`Current candle - O:${open} H:${high} L:${low} C:${close} V:${volume}`);
bdpo.log(`Body: ${bodyPercentage.toFixed(1)}% of range`);
}
Parameters
Parameter | Type | Description |
---|---|---|
candleData |
Array | Array of candlestick data |
Returns
Array|null - First candle [timestamp, open, high, low, close, volume] or null if no data
Example Usage
// Compare first and last candles
const chartData: any[] = bdpo.getChartData(data);
const firstCandle: any[] | null = bdpo.getFirstCandle(chartData);
const lastCandle: any[] | null = bdpo.getLastCandle(chartData);
if (firstCandle && lastCandle) {
const totalReturn: number = ((lastCandle[4] - firstCandle[4]) / firstCandle[4]) * 100;
const startDate: Date = bdpo.timestampToDate(firstCandle[0]);
const endDate: Date = bdpo.timestampToDate(lastCandle[0]);
bdpo.log(`Total return from ${startDate.toDateString()} to ${endDate.toDateString()}: ${totalReturn.toFixed(2)}%`);
}
Data Analysis Functions
Parameters
Parameter | Type | Description |
---|---|---|
candleData |
Array | Array of candlestick data |
period |
Number | Optional. Number of periods to look back. If not provided, looks at all data |
Returns
Number|null - Highest high value or null if no data
Example Usage
// Find resistance levels
const chartData: any[] = bdpo.getChartData(data);
const high20: number = bdpo.getHighestHigh(chartData, 20);
const high50: number = bdpo.getHighestHigh(chartData, 50);
const allTimeHigh: number = bdpo.getHighestHigh(chartData);
const currentPrice: number = bdpo.getClose(chartData)[chartData.length - 1];
// Distance from highs
const distanceFrom20High: number = ((currentPrice - high20) / high20) * 100;
const distanceFrom50High: number = ((currentPrice - high50) / high50) * 100;
bdpo.log(`20-period high: ${high20} (${distanceFrom20High.toFixed(1)}% away)`);
bdpo.log(`50-period high: ${high50} (${distanceFrom50High.toFixed(1)}% away)`);
Parameters
Parameter | Type | Description |
---|---|---|
candleData |
Array | Array of candlestick data |
period |
Number | Optional. Number of periods to look back. If not provided, looks at all data |
Returns
Number|null - Lowest low value or null if no data
Example Usage
// Find support levels
const chartData: any[] = bdpo.getChartData(data);
const low20: number = bdpo.getLowestLow(chartData, 20);
const low50: number = bdpo.getLowestLow(chartData, 50);
const allTimeLow: number = bdpo.getLowestLow(chartData);
const currentPrice: number = bdpo.getClose(chartData)[chartData.length - 1];
// Support level analysis
const supportDistance20: number = ((currentPrice - low20) / low20) * 100;
const supportDistance50: number = ((currentPrice - low50) / low50) * 100;
if (supportDistance20 < 5) {
bdpo.log("Price approaching 20-period support level");
}
bdpo.log(`20-period low: ${low20} (+${supportDistance20.toFixed(1)}% above)`);
Parameters
Parameter | Type | Description |
---|---|---|
candleData |
Array | Array of candlestick data |
Returns
Array<Number> - Array of price ranges
Example Usage
// Analyze volatility through price ranges
const chartData: any[] = bdpo.getChartData(data);
const ranges: number[] = bdpo.getPriceRange(chartData);
// Calculate average range and volatility
const avgRange: number[] = bdpo.sma(ranges, 20);
const currentRange: number = ranges[ranges.length - 1];
const avgCurrentRange: number = avgRange[avgRange.length - 1];
// Volatility expansion/contraction
const rangeRatio: number = currentRange / avgCurrentRange;
if (rangeRatio > 1.5) {
bdpo.log("High volatility - range expansion detected");
} else if (rangeRatio < 0.5) {
bdpo.log("Low volatility - range contraction detected");
}
Parameters
Parameter | Type | Description |
---|---|---|
candleData |
Array | Array of candlestick data |
Returns
Array<Number> - Array of body sizes
Example Usage
// Analyze candle strength through body size
const chartData: any[] = bdpo.getChartData(data);
const bodySizes: number[] = bdpo.getBodySize(chartData);
const ranges: number[] = bdpo.getPriceRange(chartData);
// Calculate body-to-range ratios
const bodyRatios: number[] = bodySizes.map((body: number, i: number) =>
ranges[i] > 0 ? (body / ranges[i]) * 100 : 0
);
const lastBodyRatio: number = bodyRatios[bodyRatios.length - 1];
// Candle strength analysis
if (lastBodyRatio > 70) {
bdpo.log("Strong candle - large body relative to range");
} else if (lastBodyRatio < 30) {
bdpo.log("Weak candle - small body, likely indecision");
}
Parameters
Parameter | Type | Description |
---|---|---|
candleData |
Array | Array of candlestick data |
Returns
Array<Number> - Array of upper shadow lengths
Example Usage
// Analyze rejection at higher prices
const chartData: any[] = bdpo.getChartData(data);
const upperShadows: number[] = bdpo.getUpperShadow(chartData);
const ranges: number[] = bdpo.getPriceRange(chartData);
// Calculate shadow-to-range ratios
const shadowRatios: number[] = upperShadows.map((shadow: number, i: number) =>
ranges[i] > 0 ? (shadow / ranges[i]) * 100 : 0
);
const lastShadowRatio: number = shadowRatios[shadowRatios.length - 1];
// Rejection pattern detection
if (lastShadowRatio > 60) {
bdpo.log("Strong upper rejection - potential resistance");
// Check if it's a shooting star pattern
const bodySizes: number[] = bdpo.getBodySize(chartData);
const lastBodyRatio: number = (bodySizes[bodySizes.length - 1] / ranges[ranges.length - 1]) * 100;
if (lastBodyRatio < 30) {
bdpo.log("Shooting star pattern detected");
}
}
Parameters
Parameter | Type | Description |
---|---|---|
candleData |
Array | Array of candlestick data |
Returns
Array<Number> - Array of lower shadow lengths
Example Usage
// Analyze buying pressure and support
const chartData: any[] = bdpo.getChartData(data);
const lowerShadows: number[] = bdpo.getLowerShadow(chartData);
const ranges: number[] = bdpo.getPriceRange(chartData);
// Calculate lower shadow ratios
const shadowRatios: number[] = lowerShadows.map((shadow: number, i: number) =>
ranges[i] > 0 ? (shadow / ranges[i]) * 100 : 0
);
const lastShadowRatio: number = shadowRatios[shadowRatios.length - 1];
// Support and hammer pattern detection
if (lastShadowRatio > 60) {
bdpo.log("Strong lower rejection - potential support");
// Check for hammer pattern
const bodySizes: number[] = bdpo.getBodySize(chartData);
const lastBodyRatio: number = (bodySizes[bodySizes.length - 1] / ranges[ranges.length - 1]) * 100;
if (lastBodyRatio < 30) {
bdpo.log("Hammer pattern detected - potential reversal");
}
}
Parameters
Parameter | Type | Description |
---|---|---|
candleData |
Array | Array of candlestick data |
Returns
Array<Boolean> - Array of boolean values indicating bullish candles
Example Usage
// Analyze bullish momentum
const chartData: any[] = bdpo.getChartData(data);
const bullishCandles: boolean[] = bdpo.isBullish(chartData);
// Count consecutive bullish candles
let consecutiveBullish: number = 0;
for (let i = bullishCandles.length - 1; i >= 0; i--) {
if (bullishCandles[i]) {
consecutiveBullish++;
} else {
break;
}
}
// Calculate bullish percentage over last 20 candles
const last20: boolean[] = bullishCandles.slice(-20);
const bullishCount: number = last20.filter(Boolean).length;
const bullishPercentage: number = (bullishCount / last20.length) * 100;
bdpo.log(`Consecutive bullish candles: ${consecutiveBullish}`);
bdpo.log(`Bullish momentum (20-period): ${bullishPercentage.toFixed(1)}%`);
Parameters
Parameter | Type | Description |
---|---|---|
candleData |
Array | Array of candlestick data |
Returns
Array<Boolean> - Array of boolean values indicating bearish candles
Example Usage
// Analyze bearish pressure
const chartData: any[] = bdpo.getChartData(data);
const bearishCandles: boolean[] = bdpo.isBearish(chartData);
// Count consecutive bearish candles
let consecutiveBearish: number = 0;
for (let i = bearishCandles.length - 1; i >= 0; i--) {
if (bearishCandles[i]) {
consecutiveBearish++;
} else {
break;
}
}
// Calculate bearish pressure
const last20: boolean[] = bearishCandles.slice(-20);
const bearishCount: number = last20.filter(Boolean).length;
const bearishPercentage: number = (bearishCount / last20.length) * 100;
// Risk management based on bearish momentum
if (bearishPercentage > 70 && consecutiveBearish >= 3) {
bdpo.log("Strong bearish momentum - consider defensive positioning");
}
bdpo.log(`Consecutive bearish candles: ${consecutiveBearish}`);
Data Transformation Functions
Parameters
Parameter | Type | Description |
---|---|---|
timestamp |
Number | Unix timestamp to convert |
Returns
Date - JavaScript Date object
Example Usage
// Convert timestamps for time-based analysis
const chartData: any[] = bdpo.getChartData(data);
const lastCandle: any[] | null = bdpo.getLastCandle(chartData);
if (lastCandle) {
const candleDate: Date = bdpo.timestampToDate(lastCandle[0]);
const now: Date = new Date();
const ageMinutes: number = (now.getTime() - candleDate.getTime()) / (1000 * 60);
bdpo.log(`Last candle date: ${candleDate.toISOString()}`);
bdpo.log(`Candle age: ${ageMinutes.toFixed(1)} minutes`);
// Time-based trading rules
const hour: number = candleDate.getHours();
if (hour >= 9 && hour <= 16) {
bdpo.log("Market hours - active trading period");
} else {
bdpo.log("After hours - reduced liquidity expected");
}
}
- Always validate candlestick data before processing to avoid runtime errors
- Use data filtering functions to focus analysis on relevant time periods
- Combine OHLCV extraction with technical indicators for comprehensive analysis
- Shadow analysis can help identify reversal patterns and market sentiment
- Monitor consecutive bullish/bearish patterns for momentum confirmation
Symbol Functions
Asset metadata and trading context functions
Asset Information
Returns
Object - Complete asset object containing all available metadata
Example Usage
// Get complete asset information
const asset: any = bdpo.getAsset();
if (asset) {
bdpo.log(`Asset Details:`);
bdpo.log(`Symbol: ${asset.ticker}`);
bdpo.log(`Exchange: ${asset.exchange}`);
bdpo.log(`Type: ${asset.type}`);
bdpo.log(`Contract Size: ${asset.contract_size}`);
// Use asset properties for trading decisions
if (asset.type === 'crypto') {
// Crypto-specific logic
} else if (asset.type === 'forex') {
// Forex-specific logic
}
}
Returns
String|null - Trading symbol or null if not available
Example Usage
// Get current trading symbol
const symbol: string | null = bdpo.getSymbol();
if (symbol) {
bdpo.log(`Trading: ${symbol}`);
// Symbol-specific trading logic
if (symbol.includes('BTC')) {
// Bitcoin-related strategies
const btcSpecificRiskManagement = true;
} else if (symbol.includes('USD')) {
// USD pair strategies
const usdPairStrategy = true;
}
// Use symbol for logging and tracking
bdpo.log(`${symbol}: Strategy initialized`);
}
Returns
String|null - Data feed source or null if not available
Example Usage
// Get data source
const source: string | null = bdpo.getSource();
if (source) {
bdpo.log(`Data source: ${source}`);
// Source-specific adjustments
if (source === 'BINANCE') {
// Binance-specific considerations (crypto exchange)
const cryptoMode = true;
} else if (source === 'BLACKBULL') {
// BlackBull-specific considerations (forex broker)
const forexMode = true;
}
// Adjust strategy based on data source characteristics
bdpo.log(`Strategy adapted for ${source} data feed`);
}
Returns
String|null - Human-readable asset name or null if not available
Example Usage
// Get descriptive asset name
const assetName: string | null = bdpo.getName();
if (assetName) {
bdpo.log(`Trading: ${assetName}`);
// Use for user-friendly logging and reports
const symbol: string = bdpo.getSymbol();
const performance = calculatePerformance();
bdpo.log(`${assetName} (${symbol}) Performance: ${performance.toFixed(2)}%`);
}
Returns
String|null - Asset type or null if not available
Example Usage
// Get asset type for strategy adaptation
const assetType: string | null = bdpo.getType();
if (assetType) {
bdpo.log(`Asset type: ${assetType}`);
// Type-specific strategy configuration
switch (assetType) {
case 'crypto':
// Cryptocurrency specific settings
const volatilityMultiplier = 1.5;
const tradingHours = '24/7';
break;
case 'forex':
// Forex specific settings
const volatilityMultiplier = 1.0;
const tradingHours = 'weekdays';
break;
case 'stock':
// Stock specific settings
const volatilityMultiplier = 0.8;
const tradingHours = 'market_hours';
break;
default:
bdpo.log(`Unknown asset type: ${assetType}`);
}
}
Currency Information
Returns
String|null - Base asset currency or null if not available
Example Usage
// Get base and quote assets
const baseAsset: string | null = bdpo.getBaseAsset();
const quoteAsset: string | null = bdpo.getQuoteAsset();
if (baseAsset && quoteAsset) {
bdpo.log(`Trading pair: ${baseAsset}/${quoteAsset}`);
// Currency-specific analysis
if (baseAsset === 'BTC') {
// Bitcoin correlation analysis
const btcDominance = checkBitcoinDominance();
} else if (baseAsset === 'EUR') {
// Euro economic indicators
const euroZoneData = getEuroZoneEconomicData();
}
// Position sizing based on base asset
const positionSize = calculatePositionSize(baseAsset);
}
Returns
String|null - Quote asset currency or null if not available
Example Usage
// Get quote asset for P&L calculations
const quoteAsset: string | null = bdpo.getQuoteAsset();
if (quoteAsset) {
bdpo.log(`Profit/Loss calculated in: ${quoteAsset}`);
// Quote asset considerations
if (quoteAsset === 'USD') {
// US Dollar considerations
const dollarStrengthIndex = getDollarIndex();
} else if (quoteAsset === 'JPY') {
// Japanese Yen considerations
const yenCarryTrade = analyzeCarryTrade();
}
// Account currency conversion if needed
const accountCurrency = getAccountCurrency();
if (accountCurrency !== quoteAsset) {
const conversionRate = getConversionRate(quoteAsset, accountCurrency);
bdpo.log(`Conversion rate ${quoteAsset}/${accountCurrency}: ${conversionRate}`);
}
}
Trading Specifications
Returns
Number|null - Contract size or null if not available
Example Usage
// Get contract size for position calculations
const contractSize: number | null = bdpo.getContractSize();
if (contractSize) {
bdpo.log(`Contract size: ${contractSize}`);
// Position value calculations
const lotSize = 1.0;
const currentPrice = bdpo.getAsk();
const positionValue = lotSize * contractSize * currentPrice;
bdpo.log(`Position value: ${positionValue.toFixed(2)}`);
// Risk management based on contract size
const accountBalance = bdpo.getBalance();
const maxPositionPercent = 0.02; // 2% risk
const maxLots = (accountBalance * maxPositionPercent) / (contractSize * currentPrice);
bdpo.log(`Max recommended lots: ${maxLots.toFixed(2)}`);
}
Returns
Number|null - Minimum/Maximum quantity or null if not available
Example Usage
// Get trading quantity limits
const minQty: number | null = bdpo.getMinQty();
const maxQty: number | null = bdpo.getMaxQty();
if (minQty !== null && maxQty !== null) {
bdpo.log(`Trading limits: ${minQty} - ${maxQty} lots`);
// Validate order size before placing trades
function validateOrderSize(requestedLots: number): number {
if (requestedLots < minQty) {
bdpo.log(`Order size too small. Minimum: ${minQty}, adjusting to ${minQty}`);
return minQty;
}
if (requestedLots > maxQty) {
bdpo.log(`Order size too large. Maximum: ${maxQty}, adjusting to ${maxQty}`);
return maxQty;
}
return requestedLots;
}
// Use in order placement
const desiredLots = 5.0;
const validatedLots = validateOrderSize(desiredLots);
bdpo.orderSend({
orderType: "BUY",
lotSize: validatedLots
});
}
Price Specifications
Returns
Number|null - Pip value or null if not available
Example Usage
// Get pip value for calculations
const pipValue: number | null = bdpo.getPip();
if (pipValue) {
bdpo.log(`Pip value: ${pipValue}`);
// Calculate pip-based stop loss and take profit
const currentPrice = bdpo.getAsk();
const stopLossPips = 50;
const takeProfitPips = 100;
const stopLossPrice = currentPrice - (stopLossPips * (pipValue / 10));
const takeProfitPrice = currentPrice + (takeProfitPips * (pipValue / 10));
bdpo.log(`Stop Loss: ${stopLossPrice.toFixed(5)}`);
bdpo.log(`Take Profit: ${takeProfitPrice.toFixed(5)}`);
// Risk calculation in pips
const riskInPips = Math.abs(currentPrice - stopLossPrice) / (pipValue / 10);
bdpo.log(`Risk: ${riskInPips.toFixed(1)} pips`);
}
Returns
Number|null - Decimal precision or null if not available
Example Usage
// Get price precision for formatting
const precision: number | null = bdpo.getPrecision();
if (precision) {
bdpo.log(`Price precision: ${precision} decimal places`);
// Format prices correctly
function formatPrice(price: number): string {
return price.toFixed(precision);
}
const currentPrice = bdpo.getAsk();
const formattedPrice = formatPrice(currentPrice);
bdpo.log(`Current price: ${formattedPrice}`);
// Ensure order prices match broker precision
const calculatedStopLoss = 1.23456789;
const preciseStopLoss = parseFloat(formatPrice(calculatedStopLoss));
bdpo.orderSend({
orderType: "BUYLIMIT",
price: preciseStopLoss,
lotSize: 1.0
});
}
Returns
Number|null - Minimum price step or null if not available
Example Usage
// Get minimum price step for order placement
const stepSize: number | null = bdpo.getStepSize();
if (stepSize) {
bdpo.log(`Minimum price step: ${stepSize}`);
// Round prices to valid steps
function roundToStepSize(price: number): number {
return Math.round(price / stepSize) * stepSize;
}
// Example: Place limit order with valid price
const currentPrice = bdpo.getAsk();
const targetPrice = currentPrice * 1.02; // 2% above current
const validPrice = roundToStepSize(targetPrice);
bdpo.log(`Target: ${targetPrice}, Valid: ${validPrice}`);
bdpo.orderSend({
orderType: "BUYLIMIT",
price: validPrice,
lotSize: 1.0
});
}
Real-Time Pricing
Returns
Number - Current ask price
Example Usage
// Get current ask price for buy orders
const askPrice: number = bdpo.getAsk();
const bidPrice: number = bdpo.getBid();
bdpo.log(`Ask: ${askPrice}, Bid: ${bidPrice}`);
// Use ask price for buy order calculations
const buyOrderPrice = askPrice;
const stopLoss = askPrice * 0.98; // 2% below ask
const takeProfit = askPrice * 1.04; // 4% above ask
// Place buy order at current ask
bdpo.orderSend({
orderType: "BUY",
lotSize: 1.0,
stopLoss: stopLoss,
takeProfit: takeProfit
});
// Monitor spread
const spread = askPrice - bidPrice;
const spreadPips = bdpo.getSpread();
bdpo.log(`Spread: ${spread.toFixed(5)} (${spreadPips.toFixed(1)} pips)`);
Returns
Number - Current bid price
Example Usage
// Get current bid price for sell orders
const bidPrice: number = bdpo.getBid();
const askPrice: number = bdpo.getAsk();
bdpo.log(`Bid: ${bidPrice}, Ask: ${askPrice}`);
// Use bid price for sell order calculations
const sellOrderPrice = bidPrice;
const stopLoss = bidPrice * 1.02; // 2% above bid
const takeProfit = bidPrice * 0.96; // 4% below bid
// Place sell order at current bid
bdpo.orderSend({
orderType: "SELL",
lotSize: 1.0,
stopLoss: stopLoss,
takeProfit: takeProfit
});
// Calculate mid price
const midPrice = (askPrice + bidPrice) / 2;
bdpo.log(`Mid price: ${midPrice.toFixed(5)}`);
Returns
Number - Current spread in pips
Example Usage
// Monitor spread for optimal trade entry
const currentSpread: number = bdpo.getSpread();
const avgSpread = 2.5; // Historical average spread in pips
bdpo.log(`Current spread: ${currentSpread.toFixed(1)} pips`);
// Trade only when spread is reasonable
if (currentSpread <= avgSpread * 1.5) {
bdpo.log("Spread acceptable for trading");
// Continue with trade logic
const askPrice = bdpo.getAsk();
const bidPrice = bdpo.getBid();
// Factor spread into profit targets
const minProfitPips = currentSpread * 3; // 3x spread minimum
const takeProfitDistance = minProfitPips * (bdpo.getPip() / 10);
bdpo.orderSend({
orderType: "BUY",
lotSize: 1.0,
takeProfit: askPrice + takeProfitDistance
});
} else {
bdpo.log(`Spread too wide: ${currentSpread.toFixed(1)} pips. Waiting for better conditions.`);
}
Exchange Information
Returns
String|null - Exchange name or null if not available
Example Usage
// Get exchange information
const exchange: string | null = bdpo.getExchange();
if (exchange) {
bdpo.log(`Trading on: ${exchange}`);
// Exchange-specific configurations
switch (exchange.toLowerCase()) {
case 'blackbull markets':
// BlackBull specific settings
const maxLeverage = 500;
const supportsCrypto = true;
break;
case 'binance':
// Binance specific settings
const maxLeverage = 125;
const cryptoOnly = true;
break;
case 'mt4':
case 'mt5':
// MetaTrader platform settings
const forexFocus = true;
break;
default:
bdpo.log(`Unknown exchange: ${exchange}`);
}
// Log exchange in trade records
const symbol = bdpo.getSymbol();
bdpo.log(`${symbol} @ ${exchange}: Strategy active`);
}
- Always check symbol metadata before implementing trading logic
- Use asset type to adapt strategy parameters for different markets
- Monitor spread conditions to optimize trade entry timing
- Validate order sizes against min/max quantity limits
- Factor in pip values and precision for accurate price calculations
- Consider exchange-specific characteristics in your strategy
Math Functions
Mathematical utilities and statistical analysis functions
Basic Statistics
Parameters
Parameter | Type | Description |
---|---|---|
array |
Array<Number> | Array of numbers to sum |
Returns
Number - Sum of all array elements
Example Usage
// Calculate total volume over last 10 periods
const volumes: number[] = bdpo.getVolume(data).slice(-10);
const totalVolume: number = bdpo.sum(volumes);
// Calculate cumulative returns
const returns: number[] = [];
const prices: number[] = bdpo.getClose(data);
for (let i = 1; i < prices.length; i++) {
const dailyReturn: number = (prices[i] - prices[i-1]) / prices[i-1];
returns.push(dailyReturn);
}
const totalReturn: number = bdpo.sum(returns);
bdpo.log(`Total volume (10 periods): ${totalVolume}`);
bdpo.log(`Cumulative return: ${(totalReturn * 100).toFixed(2)}%`);
Parameters
Parameter | Type | Description |
---|---|---|
array |
Array<Number> | Array of numbers to average |
Returns
Number - Arithmetic mean of the array
Example Usage
// Calculate average price over last 20 periods
const prices: number[] = bdpo.getClose(data).slice(-20);
const avgPrice: number = bdpo.mean(prices);
// Calculate average daily range
const highs: number[] = bdpo.getHigh(data);
const lows: number[] = bdpo.getLow(data);
const ranges: number[] = highs.map((high: number, i: number) => high - lows[i]);
const avgRange: number = bdpo.mean(ranges.slice(-30));
// Performance metrics
const currentPrice: number = prices[prices.length - 1];
const distanceFromAvg: number = ((currentPrice - avgPrice) / avgPrice) * 100;
bdpo.log(`Average price (20 periods): ${avgPrice.toFixed(4)}`);
bdpo.log(`Average range (30 periods): ${avgRange.toFixed(4)}`);
bdpo.log(`Distance from average: ${distanceFromAvg.toFixed(2)}%`);
Parameters
Parameter | Type | Description |
---|---|---|
array |
Array<Number> | Array of numbers to find median |
Returns
Number - Median value of the array
Example Usage
// Find median price for support/resistance analysis
const prices: number[] = bdpo.getClose(data).slice(-50);
const medianPrice: number = bdpo.median(prices);
// Compare current price to median
const currentPrice: number = prices[prices.length - 1];
const meanPrice: number = bdpo.mean(prices);
// Median is less sensitive to outliers than mean
const meanVsMedian: number = meanPrice - medianPrice;
if (Math.abs(meanVsMedian) > medianPrice * 0.01) {
bdpo.log("Significant skew detected in price distribution");
}
bdpo.log(`Median price: ${medianPrice.toFixed(4)}`);
bdpo.log(`Mean vs Median difference: ${meanVsMedian.toFixed(4)}`);
Parameters
Parameter | Type | Description |
---|---|---|
array |
Array<Number> | Array of numbers for statistical analysis |
Returns
Number - Variance (squared standard deviation) or Standard deviation
Example Usage
// Calculate price volatility using returns
const prices: number[] = bdpo.getClose(data);
const returns: number[] = [];
// Calculate daily returns
for (let i = 1; i < prices.length; i++) {
const dailyReturn: number = (prices[i] - prices[i-1]) / prices[i-1];
returns.push(dailyReturn);
}
// Statistical measures of volatility
const returnVariance: number = bdpo.variance(returns.slice(-30));
const returnStdDev: number = bdpo.std(returns.slice(-30));
const annualizedVolatility: number = returnStdDev * Math.sqrt(252); // 252 trading days
// Risk assessment
const currentReturn: number = returns[returns.length - 1];
const zScore: number = Math.abs(currentReturn) / returnStdDev;
if (zScore > 2) {
bdpo.log("Unusual price movement detected (>2 standard deviations)");
}
bdpo.log(`30-day volatility (annualized): ${(annualizedVolatility * 100).toFixed(2)}%`);
bdpo.log(`Current move Z-score: ${zScore.toFixed(2)}`);
Parameters
Function | Parameters | Description |
---|---|---|
mode(array) |
Array<Number> | Most frequently occurring value(s) |
mad(array) |
Array<Number> | Median Absolute Deviation (robust volatility measure) |
prod(array) |
Array<Number> | Product of all elements in array |
Example Usage
// Advanced statistical analysis for trading patterns
const prices: number[] = bdpo.getClose(data).slice(-100);
// Round prices to significant levels for mode analysis
const roundedPrices: number[] = prices.map((price: number) => {
const decimalPlaces: number = 2; // Round to nearest cent
return bdpo.round(price, decimalPlaces);
});
// Find most common price levels (support/resistance)
const priceMode: number[] = bdpo.mode(roundedPrices);
const modeLevel: number = Array.isArray(priceMode) ? priceMode[0] : priceMode;
// Robust volatility using MAD (less sensitive to outliers than std dev)
const returns: number[] = [];
for (let i = 1; i < prices.length; i++) {
const dailyReturn: number = (prices[i] - prices[i-1]) / prices[i-1];
returns.push(dailyReturn);
}
const madVolatility: number = bdpo.mad(returns);
const stdVolatility: number = bdpo.std(returns);
const robustnessRatio: number = madVolatility / stdVolatility; // Lower = more outliers
// Compound growth calculation using product
const periodReturns: number[] = returns.slice(-20).map((ret: number) => 1 + ret);
const compoundReturn: number = bdpo.prod(periodReturns) - 1;
const avgDailyReturn: number = bdpo.pow(bdpo.prod(periodReturns), 1/20) - 1;
// Risk-adjusted metrics
const currentPrice: number = prices[prices.length - 1];
const distanceFromMode: number = Math.abs(currentPrice - modeLevel) / currentPrice;
const madScore: number = Math.abs(returns[returns.length - 1]) / madVolatility;
// Price clustering analysis
const priceDistances: number[] = roundedPrices.map(price => Math.abs(price - modeLevel));
const avgDistanceFromMode: number = bdpo.mean(priceDistances);
bdpo.log(`Statistical Analysis (100 periods):`);
bdpo.log(`Most common price level: ${modeLevel.toFixed(4)}`);
bdpo.log(`Current distance from mode: ${(distanceFromMode * 100).toFixed(2)}%`);
bdpo.log(`MAD volatility: ${(madVolatility * 100).toFixed(3)}%`);
bdpo.log(`Std volatility: ${(stdVolatility * 100).toFixed(3)}%`);
bdpo.log(`Robustness ratio: ${robustnessRatio.toFixed(2)} (lower = more outliers)`);
bdpo.log(`20-day compound return: ${(compoundReturn * 100).toFixed(2)}%`);
bdpo.log(`Average daily return: ${(avgDailyReturn * 100).toFixed(3)}%`);
bdpo.log(`Current MAD score: ${madScore.toFixed(2)}`);
Parameters
Parameter | Type | Description |
---|---|---|
array |
Array<Number> | Array of numbers to analyze |
Returns
Number - Minimum value, maximum value, or range (max - min)
Example Usage
// Find key levels for last 50 periods
const highs: number[] = bdpo.getHigh(data).slice(-50);
const lows: number[] = bdpo.getLow(data).slice(-50);
const closes: number[] = bdpo.getClose(data).slice(-50);
const resistance: number = bdpo.max(highs);
const support: number = bdpo.min(lows);
const priceRange: number = bdpo.range(closes);
const currentPrice: number = closes[closes.length - 1];
// Distance to key levels
const distanceToResistance: number = ((resistance - currentPrice) / currentPrice) * 100;
const distanceToSupport: number = ((currentPrice - support) / currentPrice) * 100;
// Range analysis
const avgClose: number = bdpo.mean(closes);
const rangePercentage: number = (priceRange / avgClose) * 100;
bdpo.log(`Resistance: ${resistance.toFixed(4)} (${distanceToResistance.toFixed(2)}% away)`);
bdpo.log(`Support: ${support.toFixed(4)} (${distanceToSupport.toFixed(2)}% away)`);
bdpo.log(`50-period range: ${rangePercentage.toFixed(2)}% of average price`);
Mathematical Functions
Parameters
Function | Parameters | Description |
---|---|---|
abs(x) |
Number | Returns absolute value |
sqrt(x) |
Number | Returns square root |
pow(x, y) |
Number, Number | Returns x raised to power y |
Example Usage
// Calculate distance metrics
const currentPrice: number = bdpo.getClose(data)[data.length - 1];
const sma20: number[] = bdpo.sma(bdpo.getClose(data), 20);
const currentSMA: number = sma20[sma20.length - 1];
// Absolute distance from moving average
const distance: number = bdpo.abs(currentPrice - currentSMA);
const distancePercent: number = (distance / currentSMA) * 100;
// Squared distance for penalty functions
const squaredDistance: number = bdpo.pow(distance / currentSMA, 2);
// Root mean square calculation for volatility
const returns: number[] = [];
const prices: number[] = bdpo.getClose(data);
for (let i = 1; i < 21; i++) {
const ret: number = (prices[prices.length - i] - prices[prices.length - i - 1]) / prices[prices.length - i - 1];
returns.push(bdpo.pow(ret, 2));
}
const rmsVolatility: number = bdpo.sqrt(bdpo.mean(returns));
bdpo.log(`Distance from SMA: ${distancePercent.toFixed(2)}%`);
bdpo.log(`RMS Volatility: ${(rmsVolatility * 100).toFixed(2)}%`);
Parameters
Function | Parameters | Description |
---|---|---|
log(x) |
Number | Natural logarithm (base e) |
log10(x) |
Number | Base-10 logarithm |
log2(x) |
Number | Base-2 logarithm |
Example Usage
// Calculate logarithmic returns (continuous compounding)
const prices: number[] = bdpo.getClose(data);
const logReturns: number[] = [];
for (let i = 1; i < prices.length; i++) {
const logReturn: number = bdpo.log(prices[i] / prices[i-1]);
logReturns.push(logReturn);
}
// Log returns are additive and normally distributed
const totalLogReturn: number = bdpo.sum(logReturns.slice(-30));
const avgDailyLogReturn: number = bdpo.mean(logReturns.slice(-30));
const logReturnVolatility: number = bdpo.std(logReturns.slice(-30));
// Convert back to simple returns
const totalSimpleReturn: number = Math.exp(totalLogReturn) - 1;
// Log transformation for price scaling
const logPrices: number[] = prices.map((price: number) => bdpo.log10(price));
const logPriceRange: number = bdpo.range(logPrices.slice(-50));
bdpo.log(`30-day total return: ${(totalSimpleReturn * 100).toFixed(2)}%`);
bdpo.log(`Daily log return volatility: ${(logReturnVolatility * 100).toFixed(3)}%`);
Parameters
Function | Parameters | Description |
---|---|---|
cbrt(x) |
Number | Cube root of x |
exp(x) |
Number | e raised to the power of x |
sign(x) |
Number | Sign of x (-1, 0, or 1) |
Example Usage
// Volume-price relationship analysis
const volumes: number[] = bdpo.getVolume(data).slice(-30);
const prices: number[] = bdpo.getClose(data).slice(-30);
// Cube root transformation for volume analysis (reduces outlier impact)
const cbrtVolumes: number[] = volumes.map((vol: number) => bdpo.cbrt(vol));
const avgCbrtVolume: number = bdpo.mean(cbrtVolumes);
const currentCbrtVolume: number = bdpo.cbrt(volumes[volumes.length - 1]);
// Exponential decay weighting for recent data
const decayFactor: number = 0.1;
let weightedSum: number = 0;
let totalWeight: number = 0;
for (let i = 0; i < prices.length; i++) {
const weight: number = bdpo.exp(-decayFactor * (prices.length - 1 - i));
weightedSum += prices[i] * weight;
totalWeight += weight;
}
const exponentialMA: number = weightedSum / totalWeight;
// Trend direction analysis using sign function
const priceDiffs: number[] = bdpo.diff(prices);
const trendSigns: number[] = priceDiffs.map((diff: number) => bdpo.sign(diff));
const bullishBars: number = trendSigns.filter((sign: number) => sign > 0).length;
const bearishBars: number = trendSigns.filter((sign: number) => sign < 0).length;
const trendStrength: number = (bullishBars - bearishBars) / trendSigns.length;
bdpo.log(`Volume analysis: Current/Avg = ${(currentCbrtVolume / avgCbrtVolume).toFixed(2)}`);
bdpo.log(`Exponential MA: ${exponentialMA.toFixed(4)}`);
bdpo.log(`Trend strength: ${(trendStrength * 100).toFixed(1)}% (${bullishBars}↑ ${bearishBars}↓)`);
Parameters
Function | Parameters | Description |
---|---|---|
sin(x) |
Number (radians) | Sine of x |
cos(x) |
Number (radians) | Cosine of x |
tan(x) |
Number (radians) | Tangent of x |
asin(x) |
Number (-1 to 1) | Arcsine of x |
acos(x) |
Number (-1 to 1) | Arccosine of x |
atan(x) |
Number | Arctangent of x |
atan2(y, x) |
Number, Number | Arctangent of y/x (quadrant-aware) |
Example Usage
// Cyclical market analysis using trigonometric functions
const prices: number[] = bdpo.getClose(data);
const periods: number = 20; // 20-day cycle analysis
// Generate sine wave oscillator for cycle detection
const cycleValues: number[] = [];
for (let i = 0; i < prices.length; i++) {
const angle: number = (2 * Math.PI * i) / periods;
const sineValue: number = bdpo.sin(angle);
const cosineValue: number = bdpo.cos(angle);
// Combine sine and cosine for quadrature oscillator
const cycleValue: number = sineValue * 0.7 + cosineValue * 0.3;
cycleValues.push(cycleValue);
}
// Price momentum direction analysis
const priceReturns: number[] = [];
for (let i = 1; i < prices.length; i++) {
const returnValue: number = (prices[i] - prices[i-1]) / prices[i-1];
priceReturns.push(returnValue);
}
// Convert price movements to angular representation
const currentReturn: number = priceReturns[priceReturns.length - 1];
const avgReturn: number = bdpo.mean(priceReturns.slice(-30));
// Use atan2 for quadrant-aware angle calculation
const momentumAngle: number = bdpo.atan2(currentReturn, avgReturn);
const momentumDegrees: number = (momentumAngle * 180) / Math.PI;
// Trend strength using arctangent transformation
const normalizedReturns: number[] = priceReturns.slice(-30).map((ret: number) => {
return bdpo.atan(ret * 100) * (2 / Math.PI); // Normalize to [-1, 1]
});
const trendStrength: number = bdpo.mean(normalizedReturns);
const trendConsistency: number = 1 - bdpo.std(normalizedReturns);
// Seasonal pattern detection
const dayOfCycle: number = prices.length % periods;
const seasonalPhase: number = (2 * Math.PI * dayOfCycle) / periods;
const expectedCycleValue: number = bdpo.sin(seasonalPhase);
bdpo.log(`Cycle Analysis (${periods}-day):`);
bdpo.log(`Current cycle position: ${(dayOfCycle / periods * 100).toFixed(1)}%`);
bdpo.log(`Expected cycle value: ${expectedCycleValue.toFixed(3)}`);
bdpo.log(`Momentum angle: ${momentumDegrees.toFixed(1)}°`);
bdpo.log(`Trend strength: ${(trendStrength * 100).toFixed(1)}%`);
bdpo.log(`Trend consistency: ${(trendConsistency * 100).toFixed(1)}%`);
Array Operations
Parameters
Function | Parameters | Description |
---|---|---|
cumsum(array) |
Array<Number> | Cumulative sum array |
diff(array) |
Array<Number> | Differences between consecutive elements |
sort(array) |
Array<Number> | Sorted copy of array |
Example Usage
// Cumulative P&L analysis
const prices: number[] = bdpo.getClose(data);
const returns: number[] = [];
for (let i = 1; i < prices.length; i++) {
const dailyReturn: number = (prices[i] - prices[i-1]) / prices[i-1];
returns.push(dailyReturn);
}
// Cumulative returns for equity curve
const cumulativeReturns: number[] = bdpo.cumsum(returns);
const equityCurve: number[] = cumulativeReturns.map((ret: number) => 10000 * (1 + ret));
// Price differences for momentum analysis
const priceDiffs: number[] = bdpo.diff(prices.slice(-30));
const avgPriceChange: number = bdpo.mean(priceDiffs);
// Sorted returns for percentile analysis
const sortedReturns: number[] = bdpo.sort(returns.slice(-100));
const medianReturn: number = bdpo.median(sortedReturns);
const percentile95: number = bdpo.percentile(sortedReturns, 95);
const percentile5: number = bdpo.percentile(sortedReturns, 5);
// Risk metrics
const valueAtRisk5: number = Math.abs(percentile5) * 100; // 5% VaR
bdpo.log(`Current equity: ${equityCurve[equityCurve.length - 1].toFixed(2)}`);
bdpo.log(`Average daily price change: ${avgPriceChange.toFixed(4)}`);
bdpo.log(`5% Value at Risk: ${valueAtRisk5.toFixed(2)}%`);
Parameters
Function | Parameters | Description |
---|---|---|
unique(array) |
Array<Number> | Array of unique values (removes duplicates) |
Example Usage
// Support and resistance level analysis using unique values
const highs: number[] = bdpo.getHigh(data).slice(-100);
const lows: number[] = bdpo.getLow(data).slice(-100);
// Round to meaningful price levels for clustering analysis
const precision: number = 4; // 4 decimal places
const roundedHighs: number[] = highs.map((high: number) => bdpo.round(high, precision));
const roundedLows: number[] = lows.map((low: number) => bdpo.round(low, precision));
// Find unique price levels
const uniqueHighs: number[] = bdpo.unique(roundedHighs);
const uniqueLows: number[] = bdpo.unique(roundedLows);
// Count frequency of each level for significance
const highFrequency: Map = new Map();
const lowFrequency: Map = new Map();
roundedHighs.forEach((high: number) => {
highFrequency.set(high, (highFrequency.get(high) || 0) + 1);
});
roundedLows.forEach((low: number) => {
lowFrequency.set(low, (lowFrequency.get(low) || 0) + 1);
});
// Find most significant levels (appeared multiple times)
const significantResistance: number[] = uniqueHighs.filter((level: number) =>
(highFrequency.get(level) || 0) >= 3
);
const significantSupport: number[] = uniqueLows.filter((level: number) =>
(lowFrequency.get(level) || 0) >= 3
);
// Current price analysis
const currentPrice: number = bdpo.getClose(data)[data.length - 1];
const nearestResistance: number = significantResistance
.filter((level: number) => level > currentPrice)
.reduce((nearest: number, level: number) =>
Math.abs(level - currentPrice) < Math.abs(nearest - currentPrice) ? level : nearest,
significantResistance[0]
);
const nearestSupport: number = significantSupport
.filter((level: number) => level < currentPrice)
.reduce((nearest: number, level: number) =>
Math.abs(level - currentPrice) < Math.abs(nearest - currentPrice) ? level : nearest,
significantSupport[0]
);
// Calculate distances and ratios
const resistanceDistance: number = ((nearestResistance - currentPrice) / currentPrice) * 100;
const supportDistance: number = ((currentPrice - nearestSupport) / currentPrice) * 100;
// Unique value statistics
const priceRange: number = bdpo.max(highs) - bdpo.min(lows);
const uniqueHighCount: number = uniqueHighs.length;
const uniqueLowCount: number = uniqueLows.length;
const consolidationRatio: number = 1 - (uniqueHighCount + uniqueLowCount) / (highs.length + lows.length);
bdpo.log(`Price Level Analysis (100 periods):`);
bdpo.log(`Unique high levels: ${uniqueHighCount} / Unique low levels: ${uniqueLowCount}`);
bdpo.log(`Significant resistance levels: ${significantResistance.length}`);
bdpo.log(`Significant support levels: ${significantSupport.length}`);
bdpo.log(`Nearest resistance: ${nearestResistance?.toFixed(precision)} (${resistanceDistance?.toFixed(2)}% away)`);
bdpo.log(`Nearest support: ${nearestSupport?.toFixed(precision)} (${supportDistance?.toFixed(2)}% away)`);
bdpo.log(`Consolidation ratio: ${(consolidationRatio * 100).toFixed(1)}%`);
Utility Functions
Parameters
Function | Parameters | Description |
---|---|---|
round(x, n) |
Number, Number (optional) | Round to n decimal places |
floor(x) |
Number | Round down to integer |
ceil(x) |
Number | Round up to integer |
clamp(x, min, max) |
Number, Number, Number | Constrain value between min and max |
fix(x) |
Number | Remove fractional part (truncate towards zero) |
Example Usage
// Position sizing with constraints
const accountBalance: number = bdpo.getBalance();
const riskPercent: number = 2.5;
const currentPrice: number = bdpo.getAsk();
// Calculate raw position size
const riskAmount: number = accountBalance * (riskPercent / 100);
const rawPositionSize: number = riskAmount / currentPrice;
// Apply broker constraints
const minLotSize: number = bdpo.getMinQty() || 0.01;
const maxLotSize: number = bdpo.getMaxQty() || 100;
const stepSize: number = bdpo.getStepSize() || 0.01;
// Round to valid lot size
const roundedSize: number = bdpo.floor(rawPositionSize / stepSize) * stepSize;
const constrainedSize: number = bdpo.clamp(roundedSize, minLotSize, maxLotSize);
// Price formatting for orders
const stopLoss: number = currentPrice * 0.98;
const takeProfit: number = currentPrice * 1.04;
const precision: number = bdpo.getPrecision() || 5;
const formattedStopLoss: number = bdpo.round(stopLoss, precision);
const formattedTakeProfit: number = bdpo.round(takeProfit, precision);
// Risk calculation
const actualRisk: number = constrainedSize * currentPrice * 0.02; // 2% price move
const riskPercentage: number = (actualRisk / accountBalance) * 100;
bdpo.log(`Position size: ${constrainedSize} lots`);
bdpo.log(`Stop Loss: ${formattedStopLoss}`);
bdpo.log(`Take Profit: ${formattedTakeProfit}`);
bdpo.log(`Actual risk: ${riskPercentage.toFixed(2)}%`);
Parameters
Function | Parameters | Description |
---|---|---|
percentile(array, p) |
Array<Number>, Number (0-100) | Percentile value (0-100 scale) |
quantileSeq(array, q) |
Array<Number>, Number (0-1) | Quantile value (0-1 scale) |
Example Usage
// Risk analysis using percentiles
const prices: number[] = bdpo.getClose(data);
const returns: number[] = [];
for (let i = 1; i < prices.length; i++) {
const dailyReturn: number = (prices[i] - prices[i-1]) / prices[i-1];
returns.push(dailyReturn);
}
// Value at Risk (VaR) calculations
const var95: number = bdpo.percentile(returns.slice(-252), 5); // 5th percentile = 95% VaR
const var99: number = bdpo.percentile(returns.slice(-252), 1); // 1st percentile = 99% VaR
// Expected Shortfall (average of worst 5% outcomes)
const sortedReturns: number[] = bdpo.sort(returns.slice(-252));
const worst5Percent: number[] = sortedReturns.slice(0, Math.floor(sortedReturns.length * 0.05));
const expectedShortfall: number = bdpo.mean(worst5Percent);
// Performance percentiles
const currentReturn: number = returns[returns.length - 1];
const returnRank: number = returns.filter(r => r <= currentReturn).length / returns.length;
const percentileRank: number = returnRank * 100;
// Quartile analysis
const q1: number = bdpo.quantileSeq(returns.slice(-100), 0.25);
const q2: number = bdpo.quantileSeq(returns.slice(-100), 0.50); // median
const q3: number = bdpo.quantileSeq(returns.slice(-100), 0.75);
const iqr: number = q3 - q1; // Interquartile range
bdpo.log(`95% VaR: ${(Math.abs(var95) * 100).toFixed(2)}%`);
bdpo.log(`99% VaR: ${(Math.abs(var99) * 100).toFixed(2)}%`);
bdpo.log(`Expected Shortfall: ${(Math.abs(expectedShortfall) * 100).toFixed(2)}%`);
bdpo.log(`Current return percentile: ${percentileRank.toFixed(1)}%`);
Random Functions
Parameters
Function | Parameters | Description |
---|---|---|
random() |
None | Random number between 0 and 1 |
randomInt(min, max) |
Number, Number | Random integer between min and max |
Example Usage
// Monte Carlo simulation for risk assessment
function runMonteCarloSimulation(initialPrice: number, days: number, numSimulations: number): number[] {
const finalPrices: number[] = [];
const avgReturn: number = 0.0008; // 0.08% daily return
const volatility: number = 0.02; // 2% daily volatility
for (let sim = 0; sim < numSimulations; sim++) {
let price: number = initialPrice;
for (let day = 0; day < days; day++) {
// Generate random return using Box-Muller transformation
const u1: number = bdpo.random();
const u2: number = bdpo.random();
const z: number = bdpo.sqrt(-2 * bdpo.log(u1)) * Math.cos(2 * Math.PI * u2);
const dailyReturn: number = avgReturn + volatility * z;
price = price * (1 + dailyReturn);
}
finalPrices.push(price);
}
return finalPrices;
}
// Run simulation
const currentPrice: number = bdpo.getClose(data)[data.length - 1];
const simulatedPrices: number[] = runMonteCarloSimulation(currentPrice, 30, 1000);
// Analyze results
const avgFinalPrice: number = bdpo.mean(simulatedPrices);
const priceVolatility: number = bdpo.std(simulatedPrices);
const priceRange: number = bdpo.range(simulatedPrices);
// Risk metrics from simulation
const var95Simulation: number = bdpo.percentile(simulatedPrices, 5);
const expectedPrice: number = avgFinalPrice;
const worstCase: number = bdpo.min(simulatedPrices);
const bestCase: number = bdpo.max(simulatedPrices);
// Random portfolio rebalancing
const assets: string[] = ['BTC', 'ETH', 'SOL', 'ADA'];
const randomAsset: string = assets[bdpo.randomInt(0, assets.length - 1)];
const randomWeight: number = bdpo.random() * 0.3 + 0.1; // 10-40% allocation
bdpo.log(`Monte Carlo Results (30 days, 1000 simulations):`);
bdpo.log(`Expected price: ${expectedPrice.toFixed(2)}`);
bdpo.log(`95% confidence range: ${var95Simulation.toFixed(2)} - ${bdpo.percentile(simulatedPrices, 95).toFixed(2)}`);
bdpo.log(`Random rebalance: ${randomWeight.toFixed(1)}% to ${randomAsset}`);
Advanced Operations
Parameters
Function | Parameters | Description |
---|---|---|
dot(a, b) |
Array, Array | Dot product of two vectors |
cross(a, b) |
Array, Array | Cross product of two vectors |
norm(array) |
Array<Number> | Euclidean norm (length) of vector |
Example Usage
// Correlation analysis using dot product
const prices1: number[] = bdpo.getClose(data).slice(-30);
const prices2: number[] = bdpo.getClose(referenceData).slice(-30);
// Normalize price series (subtract mean, divide by std)
const mean1: number = bdpo.mean(prices1);
const mean2: number = bdpo.mean(prices2);
const std1: number = bdpo.std(prices1);
const std2: number = bdpo.std(prices2);
const normalized1: number[] = prices1.map(p => (p - mean1) / std1);
const normalized2: number[] = prices2.map(p => (p - mean2) / std2);
// Calculate correlation using dot product
const correlation: number = bdpo.dot(normalized1, normalized2) / normalized1.length;
// Vector magnitude analysis
const returns1: number[] = bdpo.diff(prices1);
const returns2: number[] = bdpo.diff(prices2);
const magnitude1: number = bdpo.norm(returns1);
const magnitude2: number = bdpo.norm(returns2);
// Relative strength comparison
const strengthRatio: number = magnitude1 / magnitude2;
// Portfolio diversification analysis
const weights: number[] = [0.6, 0.4]; // 60% asset1, 40% asset2
const returns: number[] = [normalized1[normalized1.length - 1], normalized2[normalized2.length - 1]];
const portfolioReturn: number = bdpo.dot(weights, returns);
const portfolioRisk: number = bdpo.norm([
weights[0] * returns[0],
weights[1] * returns[1]
]);
bdpo.log(`Correlation: ${correlation.toFixed(3)}`);
bdpo.log(`Relative strength: ${strengthRatio.toFixed(2)}`);
bdpo.log(`Portfolio return: ${(portfolioReturn * 100).toFixed(2)}%`);
bdpo.log(`Portfolio risk: ${(portfolioRisk * 100).toFixed(2)}%`);
- Use logarithmic returns for better statistical properties and easier aggregation
- Apply percentile analysis for robust risk metrics that handle outliers well
- Combine multiple mathematical functions for complex financial calculations
- Use Monte Carlo simulations to test strategy robustness under various scenarios
- Leverage vector operations for portfolio analysis and correlation studies
- Always validate mathematical operations with reasonable bounds checking
Console Functions
Debugging, logging, and output management tools for strategy development
Basic Logging
Parameters
Parameter | Type | Description |
---|---|---|
message |
String | Number | Object | The message or data to log |
level |
String (optional) | Log level: "info", "warn", "error", "success", "debug". Default: "info" |
Returns
void - Sends timestamped message with level to console
Example Usage
// Basic logging (info level by default)
bdpo.log("Strategy started successfully");
// Explicit log levels
bdpo.log("Market conditions analyzed", "info");
bdpo.log("High volatility detected", "warn");
bdpo.log("Order execution failed", "error");
bdpo.log("Position opened successfully", "success");
bdpo.log("Debug: RSI calculation complete", "debug");
// Conditional logging based on strategy state
const rsi: number[] = bdpo.rsi(bdpo.getClose(data), 14);
const currentRSI: number = rsi[rsi.length - 1];
if (currentRSI > 70) {
bdpo.log(`RSI overbought: ${currentRSI.toFixed(2)}`, "warn");
} else if (currentRSI < 30) {
bdpo.log(`RSI oversold: ${currentRSI.toFixed(2)}`, "warn");
} else {
bdpo.log(`RSI normal: ${currentRSI.toFixed(2)}`, "info");
}
// Log complex trading decisions
const signal = analyzeMarketConditions();
if (signal.action === "buy") {
bdpo.log(`BUY Signal: Confidence ${signal.confidence}%, Entry ${signal.price}`, "success");
} else if (signal.action === "sell") {
bdpo.log(`SELL Signal: Confidence ${signal.confidence}%, Entry ${signal.price}`, "error");
}
Advanced Logging Functions
Parameters
Parameter | Type | Description |
---|---|---|
message |
String | Number | Object | The informational message to log |
Returns
void - Logs message with "info" level and timestamp
Example Usage
// Strategy initialization
bdpo.logInfo("Strategy initialization complete");
bdpo.logInfo(`Trading ${bdpo.getSymbol()} on ${bdpo.getExchange()}`);
// Market analysis updates
const currentPrice: number = bdpo.getClose(data)[data.length - 1];
const sma20: number[] = bdpo.sma(bdpo.getClose(data), 20);
bdpo.logInfo(`Current price: ${currentPrice.toFixed(4)}, SMA20: ${sma20[sma20.length - 1].toFixed(4)}`);
// Position updates
const openPositions: any[] = bdpo.getOpenPositions();
bdpo.logInfo(`Active positions: ${openPositions.length}`);
// Performance reporting
const balance: number = bdpo.getBalance();
const equity: number = bdpo.getEquity();
const profit: number = bdpo.getProfit();
bdpo.logInfo(`Balance: ${balance.toFixed(2)}, Equity: ${equity.toFixed(2)}, P&L: ${profit.toFixed(2)}`);
Parameters
Parameter | Type | Description |
---|---|---|
message |
String | Number | Object | The warning message to log |
Returns
void - Logs message with "warn" level and timestamp
Example Usage
// Risk management warnings
const balance: number = bdpo.getBalance();
const equity: number = bdpo.getEquity();
const drawdown: number = ((balance - equity) / balance) * 100;
if (drawdown > 10) {
bdpo.logWarn(`High drawdown detected: ${drawdown.toFixed(2)}%`);
}
// Market condition warnings
const spread: number = bdpo.getSpread();
if (spread > 3.0) {
bdpo.logWarn(`Wide spread detected: ${spread.toFixed(1)} pips`);
}
// Volatility warnings
const atr: number[] = bdpo.atr(bdpo.getHigh(data), bdpo.getLow(data), bdpo.getClose(data), 14);
const currentATR: number = atr[atr.length - 1];
const avgATR: number = bdpo.mean(atr.slice(-50));
if (currentATR > avgATR * 2) {
bdpo.logWarn(`High volatility: ATR ${currentATR.toFixed(4)} vs avg ${avgATR.toFixed(4)}`);
}
// Position size warnings
const openPositions: any[] = bdpo.getOpenPositions();
if (openPositions.length > 5) {
bdpo.logWarn(`High position count: ${openPositions.length} open trades`);
}
Parameters
Parameter | Type | Description |
---|---|---|
message |
String | Number | Object | The error message to log |
Returns
void - Logs message with "error" level and timestamp
Example Usage
// Order execution errors
try {
const result = bdpo.orderSend({
orderType: "BUY",
lotSize: 1.0,
stopLoss: 1.2500,
takeProfit: 1.2800
});
if (!result.success) {
bdpo.logError(`Order failed: ${result.message}`);
}
} catch (error) {
bdpo.logError(`Order execution exception: ${error.message}`);
}
// Data validation errors
const chartData: any[] = bdpo.getChartData(data);
if (!chartData || chartData.length === 0) {
bdpo.logError("No chart data available - cannot proceed with analysis");
return;
}
// Calculation errors
try {
const rsi: number[] = bdpo.rsi(bdpo.getClose(data), 14);
if (rsi.length === 0) {
bdpo.logError("RSI calculation failed - insufficient data");
}
} catch (error) {
bdpo.logError(`Technical indicator error: ${error.message}`);
}
// Account status errors
const balance: number = bdpo.getBalance();
if (balance <= 0) {
bdpo.logError("Account balance is zero or negative - trading halted");
}
Parameters
Parameter | Type | Description |
---|---|---|
message |
String | Number | Object | The success message to log |
Returns
void - Logs message with "success" level and timestamp
Example Usage
// Successful trade execution
const orderResult = bdpo.orderSend({
orderType: "BUY",
lotSize: 1.0,
stopLoss: 1.2500,
takeProfit: 1.2800
});
if (orderResult.success) {
bdpo.logSuccess(`Order placed successfully: ${orderResult.orderId}`);
bdpo.logSuccess(`Entry: ${orderResult.price}, SL: ${orderResult.stopLoss}, TP: ${orderResult.takeProfit}`);
}
// Profit targets achieved
const closedPositions: any[] = bdpo.getClosedPositions();
const recentTrades = closedPositions.slice(-10);
const winningTrades = recentTrades.filter(trade => trade.profit > 0);
if (winningTrades.length >= 7) {
bdpo.logSuccess(`Strong performance: ${winningTrades.length}/10 winning trades`);
}
// Milestone achievements
const totalProfit: number = bdpo.getProfit();
if (totalProfit > 1000) {
bdpo.logSuccess(`Milestone achieved: Total profit exceeds $1,000 (${totalProfit.toFixed(2)})`);
}
// Risk management success
const maxDrawdown: number = calculateMaxDrawdown();
if (maxDrawdown < 5) {
bdpo.logSuccess(`Excellent risk control: Max drawdown only ${maxDrawdown.toFixed(2)}%`);
}
Parameters
Parameter | Type | Description |
---|---|---|
message |
String | Number | Object | The debug message to log |
Returns
void - Logs message with "debug" level and timestamp
Example Usage
// Variable state debugging
const prices: number[] = bdpo.getClose(data);
const volumes: number[] = bdpo.getVolume(data);
bdpo.logDebug(`Data length: ${prices.length} candles`);
bdpo.logDebug(`Latest price: ${prices[prices.length - 1]}`);
bdpo.logDebug(`Latest volume: ${volumes[volumes.length - 1]}`);
// Calculation step debugging
const sma20: number[] = bdpo.sma(prices, 20);
const ema12: number[] = bdpo.ema(prices, 12);
const rsi: number[] = bdpo.rsi(prices, 14);
bdpo.logDebug(`SMA20: ${sma20[sma20.length - 1].toFixed(4)}`);
bdpo.logDebug(`EMA12: ${ema12[ema12.length - 1].toFixed(4)}`);
bdpo.logDebug(`RSI14: ${rsi[rsi.length - 1].toFixed(2)}`);
// Signal generation debugging
const signalStrength: number = calculateSignalStrength();
const marketCondition: string = analyzeMarketCondition();
bdpo.logDebug(`Signal strength: ${signalStrength.toFixed(3)}`);
bdpo.logDebug(`Market condition: ${marketCondition}`);
// Performance debugging
const executionStart: number = performance.now();
const complexCalculation = performComplexAnalysis();
const executionTime: number = performance.now() - executionStart;
bdpo.logDebug(`Complex analysis completed in ${executionTime.toFixed(2)}ms`);
bdpo.logDebug(`Analysis result: ${JSON.stringify(complexCalculation)}`);
Specialized Logging Functions
Functions
Function | Level | Use Case |
---|---|---|
logInfo(message) |
info | General information, status updates |
logWarn(message) |
warn | Warnings, unusual conditions, risk alerts |
logError(message) |
error | Errors, failures, critical issues |
logSuccess(message) |
success | Successful operations, achievements |
logDebug(message) |
debug | Detailed debugging information |
Example Usage
// Strategy lifecycle logging
class TradingStrategy {
public riskPercent: number = 2;
public stopLossPercent: number = 3;
private _positions: any[] = [];
private _initialized: boolean = false;
__init__(): void {
bdpo.logInfo("Initializing trading strategy...");
try {
this._positions = [];
this._initialized = true;
bdpo.logSuccess("Strategy initialization completed successfully");
bdpo.logInfo(`Risk per trade: ${this.riskPercent}%, Stop loss: ${this.stopLossPercent}%`);
} catch (error) {
bdpo.logError(`Strategy initialization failed: ${error.message}`);
this._initialized = false;
}
}
__tick__(): any {
if (!this._initialized) {
bdpo.logError("Strategy not initialized - skipping tick");
return null;
}
bdpo.logDebug("Processing new market tick...");
// Market analysis
const prices: number[] = bdpo.getClose(data);
const currentPrice: number = prices[prices.length - 1];
const sma20: number[] = bdpo.sma(prices, 20);
const rsi: number[] = bdpo.rsi(prices, 14);
bdpo.logDebug(`Price: ${currentPrice}, SMA20: ${sma20[sma20.length - 1].toFixed(4)}, RSI: ${rsi[rsi.length - 1].toFixed(2)}`);
// Risk management checks
const accountBalance: number = bdpo.getBalance();
const openPositions: any[] = bdpo.getOpenPositions();
if (openPositions.length >= 5) {
bdpo.logWarn(`Maximum positions reached: ${openPositions.length}/5`);
return null;
}
if (accountBalance < 1000) {
bdpo.logWarn(`Low account balance: $${accountBalance.toFixed(2)}`);
}
// Signal generation
const signal = this.generateSignal(currentPrice, sma20, rsi);
if (signal.type === "BUY") {
bdpo.logInfo(`BUY signal detected: ${signal.reason}`);
this.executeBuyOrder(signal);
} else if (signal.type === "SELL") {
bdpo.logInfo(`SELL signal detected: ${signal.reason}`);
this.executeSellOrder(signal);
} else {
bdpo.logDebug("No trading signal - holding position");
}
return {
currentPrice,
signal: signal.type,
openPositions: openPositions.length
};
}
private executeBuyOrder(signal: any): void {
try {
const orderResult = bdpo.orderSend({
symbol: bdpo.getSymbol(),
cmd: 0, // Buy
volume: this.calculatePositionSize(),
price: signal.price,
slippage: 3,
stoploss: signal.stopLoss,
takeprofit: signal.takeProfit
});
if (orderResult && orderResult.ticket) {
bdpo.logSuccess(`BUY order executed: Ticket #${orderResult.ticket}, Price: ${signal.price}`);
this._positions.push(orderResult);
} else {
bdpo.logError("BUY order execution failed - no ticket returned");
}
} catch (error) {
bdpo.logError(`BUY order execution error: ${error.message}`);
}
}
private executeSellOrder(signal: any): void {
try {
const openLongs = this._positions.filter(pos => pos.cmd === 0 && pos.close_time === 0);
if (openLongs.length === 0) {
bdpo.logWarn("SELL signal but no open long positions to close");
return;
}
openLongs.forEach(position => {
const closeResult = bdpo.orderClose(position.ticket, position.volume, signal.price, 3);
if (closeResult) {
bdpo.logSuccess(`Position closed: Ticket #${position.ticket}, Exit: ${signal.price}`);
} else {
bdpo.logError(`Failed to close position: Ticket #${position.ticket}`);
}
});
} catch (error) {
bdpo.logError(`SELL order execution error: ${error.message}`);
}
}
__shutdown__(): void {
bdpo.logInfo("Shutting down trading strategy...");
try {
// Close all open positions
const openPositions = this._positions.filter(pos => pos.close_time === 0);
if (openPositions.length > 0) {
bdpo.logWarn(`Closing ${openPositions.length} open positions during shutdown`);
openPositions.forEach(pos => {
bdpo.orderClose(pos.ticket, pos.volume, bdpo.getAsk(), 5);
});
}
bdpo.logSuccess("Strategy shutdown completed");
bdpo.logInfo(`Final statistics: ${this._positions.length} total trades executed`);
} catch (error) {
bdpo.logError(`Strategy shutdown error: ${error.message}`);
}
}
}
Common Debugging Patterns
Debugging Techniques
Variable Tracking
Monitor variable values and state changes throughout execution
Execution Flow
Trace program execution and decision points
Error Handling
Comprehensive error logging and recovery strategies
Performance Monitoring
Track execution times and resource usage
Example Usage
// Advanced debugging patterns for strategy development
class AdvancedTradingStrategy {
public debugMode: boolean = true;
public logLevel: string = "debug";
private _executionTimes: number[] = [];
private _decisionLog: any[] = [];
__tick__(): any {
const startTime: number = performance.now();
if (this.debugMode) {
bdpo.logDebug("=== TICK START ===");
}
try {
// 1. Variable state tracking
const marketData = this.getMarketData();
this.logVariableState("Market Data", marketData);
// 2. Decision flow tracking
const decision = this.makeDecision(marketData);
this.logDecisionFlow(decision);
// 3. Performance monitoring
const endTime: number = performance.now();
const executionTime: number = endTime - startTime;
this._executionTimes.push(executionTime);
if (this.debugMode) {
bdpo.logDebug(`Execution time: ${executionTime.toFixed(2)}ms`);
bdpo.logDebug("=== TICK END ===");
}
// 4. Periodic performance reports
if (this._executionTimes.length % 100 === 0) {
this.logPerformanceReport();
}
return decision;
} catch (error) {
// 5. Comprehensive error logging
this.logError("Tick execution error", error, {
marketData: this.getMarketData(),
executionTime: performance.now() - startTime,
tickCount: this._executionTimes.length
});
return null;
}
}
private getMarketData(): any {
const prices: number[] = bdpo.getClose(data);
const volumes: number[] = bdpo.getVolume(data);
return {
currentPrice: prices[prices.length - 1],
previousPrice: prices[prices.length - 2],
currentVolume: volumes[volumes.length - 1],
avgVolume: bdpo.mean(volumes.slice(-20)),
timestamp: Date.now()
};
}
private logVariableState(label: string, variables: any): void {
if (!this.debugMode) return;
bdpo.logDebug(`${label}:`);
Object.entries(variables).forEach(([key, value]) => {
bdpo.logDebug(` ${key}: ${typeof value === 'number' ? value.toFixed(4) : value}`);
});
}
private makeDecision(marketData: any): any {
const decision = {
action: "HOLD",
confidence: 0,
reasons: [],
timestamp: Date.now()
};
// Example decision logic with detailed logging
const priceChange = ((marketData.currentPrice - marketData.previousPrice) / marketData.previousPrice) * 100;
const volumeRatio = marketData.currentVolume / marketData.avgVolume;
bdpo.logDebug(`Price change: ${priceChange.toFixed(2)}%`);
bdpo.logDebug(`Volume ratio: ${volumeRatio.toFixed(2)}`);
if (priceChange > 1 && volumeRatio > 1.5) {
decision.action = "BUY";
decision.confidence = 75;
decision.reasons.push("Strong upward momentum with high volume");
bdpo.logInfo(`BUY decision: Price +${priceChange.toFixed(2)}%, Volume ${volumeRatio.toFixed(2)}x`);
} else if (priceChange < -1 && volumeRatio > 1.5) {
decision.action = "SELL";
decision.confidence = 70;
decision.reasons.push("Strong downward momentum with high volume");
bdpo.logInfo(`SELL decision: Price ${priceChange.toFixed(2)}%, Volume ${volumeRatio.toFixed(2)}x`);
} else {
bdpo.logDebug(`HOLD decision: Insufficient momentum (Price: ${priceChange.toFixed(2)}%, Volume: ${volumeRatio.toFixed(2)}x)`);
}
return decision;
}
private logDecisionFlow(decision: any): void {
this._decisionLog.push(decision);
if (this.debugMode) {
bdpo.logDebug(`Decision: ${decision.action} (${decision.confidence}% confidence)`);
decision.reasons.forEach((reason: string) => {
bdpo.logDebug(` Reason: ${reason}`);
});
}
// Log decision summary every 50 decisions
if (this._decisionLog.length % 50 === 0) {
this.logDecisionSummary();
}
}
private logPerformanceReport(): void {
const avgTime = bdpo.mean(this._executionTimes);
const maxTime = bdpo.max(this._executionTimes);
const minTime = bdpo.min(this._executionTimes);
bdpo.logInfo("=== PERFORMANCE REPORT ===");
bdpo.logInfo(`Avg execution time: ${avgTime.toFixed(2)}ms`);
bdpo.logInfo(`Max execution time: ${maxTime.toFixed(2)}ms`);
bdpo.logInfo(`Min execution time: ${minTime.toFixed(2)}ms`);
bdpo.logInfo(`Total ticks processed: ${this._executionTimes.length}`);
if (avgTime > 10) {
bdpo.logWarn("High average execution time detected - consider optimization");
}
}
private logDecisionSummary(): void {
const recentDecisions = this._decisionLog.slice(-50);
const buyCount = recentDecisions.filter(d => d.action === "BUY").length;
const sellCount = recentDecisions.filter(d => d.action === "SELL").length;
const holdCount = recentDecisions.filter(d => d.action === "HOLD").length;
bdpo.logInfo("=== DECISION SUMMARY (Last 50) ===");
bdpo.logInfo(`BUY: ${buyCount}, SELL: ${sellCount}, HOLD: ${holdCount}`);
const avgConfidence = bdpo.mean(recentDecisions.map(d => d.confidence));
bdpo.logInfo(`Average confidence: ${avgConfidence.toFixed(1)}%`);
}
private logError(message: string, error: any, context: any): void {
bdpo.logError(`${message}: ${error.message}`);
bdpo.logError(`Stack trace: ${error.stack}`);
// Log relevant context
bdpo.logError("Error context:");
Object.entries(context).forEach(([key, value]) => {
bdpo.logError(` ${key}: ${JSON.stringify(value)}`);
});
}
}
- Use different log levels strategically - debug for development, info for production monitoring
- Include context information like timestamps, prices, and decision factors in your logs
- Implement conditional logging based on market conditions or strategy state
- Log both successful operations and errors to maintain complete execution history
- Use structured logging with consistent formatting for easier analysis
- Consider performance impact - avoid excessive logging in production strategies
- Log entry and exit points of major functions to trace execution flow
- Include variable values and calculations in debug logs for troubleshooting
Strategy Examples
Complete trading strategy implementations using BDPO framework
Trend Following Strategies
Strategy Overview
This strategy uses two moving averages of different periods to identify trend changes. When the faster MA crosses above the slower MA, it generates a buy signal. When the faster MA crosses below the slower MA, it generates a sell signal.
class MovingAverageCrossoverStrategy {
// Public configuration parameters
public fastPeriod: number = 12;
public slowPeriod: number = 26;
public riskPercentPerTrade: number = 2; // 2% risk per trade
public stopLossPercent: number = 3; // 3% stop loss
public takeProfitPercent: number = 9; // 3:1 risk/reward ratio
public maxPositions: number = 1; // Maximum concurrent positions
public enableTrailingStop: boolean = true;
public trailingStopPercent: number = 1.5;
// Private state variables
private _positions: any[] = [];
private _fastMA: number[] = [];
private _slowMA: number[] = [];
private _lastCrossover: string | null = null;
private _tradeCount: number = 0;
private _winCount: number = 0;
__init__(): void {
// Initialize strategy state
this._positions = [];
this._lastCrossover = null;
this._tradeCount = 0;
this._winCount = 0;
bdpo.logInfo(`MA Crossover Strategy initialized:`);
bdpo.logInfo(`Fast Period: ${this.fastPeriod}, Slow Period: ${this.slowPeriod}`);
bdpo.logInfo(`Risk per trade: ${this.riskPercentPerTrade}%, Stop Loss: ${this.stopLossPercent}%`);
}
__tick__(): any {
const prices: number[] = bdpo.getClosePrices();
const currentPrice: number = prices[prices.length - 1];
if (prices.length < this.slowPeriod + 1) {
return { status: "warming_up", required: this.slowPeriod + 1, current: prices.length };
}
// Calculate moving averages
this._fastMA = bdpo.ema(prices, this.fastPeriod);
this._slowMA = bdpo.ema(prices, this.slowPeriod);
const currentFast: number = this._fastMA[this._fastMA.length - 1];
const currentSlow: number = this._slowMA[this._slowMA.length - 1];
const prevFast: number = this._fastMA[this._fastMA.length - 2];
const prevSlow: number = this._slowMA[this._slowMA.length - 2];
// Check for crossover signals
const openPositions = this.getOpenPositions();
let signal: any = null;
// Bullish crossover (fast MA crosses above slow MA)
if (prevFast <= prevSlow && currentFast > currentSlow && openPositions.length === 0) {
signal = this.generateBuySignal(currentPrice);
this._lastCrossover = 'bullish';
}
// Bearish crossover (fast MA crosses below slow MA)
else if (prevFast >= prevSlow && currentFast < currentSlow && openPositions.length > 0) {
signal = this.generateSellSignal(currentPrice);
this._lastCrossover = 'bearish';
}
// Update trailing stops
if (this.enableTrailingStop) {
this.updateTrailingStops(currentPrice);
}
// Plot indicators
bdpo.plot("splines", [this._fastMA], { splineColor: "#4CAF50", splineWidth: 2, skipNan: true }, { title: `Fast EMA ${this.fastPeriod}` });
bdpo.plot("splines", [this._slowMA], { splineColor: "#F44336", splineWidth: 2, skipNan: true }, { title: `Slow EMA ${this.slowPeriod}` });
return {
signal: signal,
indicators: {
fastMA: currentFast,
slowMA: currentSlow,
spread: ((currentFast - currentSlow) / currentSlow * 100).toFixed(2)
},
positions: openPositions.length,
lastCrossover: this._lastCrossover,
performance: {
totalTrades: this._tradeCount,
winRate: this._tradeCount > 0 ? (this._winCount / this._tradeCount * 100).toFixed(1) : 0
}
};
}
private generateBuySignal(price: number): any {
const positionSize = this.calculatePositionSize(price);
const stopLoss = price * (1 - this.stopLossPercent / 100);
const takeProfit = price * (1 + this.takeProfitPercent / 100);
try {
const order = bdpo.orderSend({
orderType: "BUY",
lotSize: positionSize,
stopLoss: stopLoss,
takeProfit: takeProfit,
note: "MA Crossover Bullish Entry"
});
if (order) {
this._positions.push({
...order,
entryPrice: price,
initialStopLoss: stopLoss,
trailingStop: this.enableTrailingStop ? stopLoss : null,
entryTime: Date.now()
});
bdpo.logSuccess(`BUY order placed: Size=${positionSize}, Entry=${price.toFixed(4)}, SL=${stopLoss.toFixed(4)}, TP=${takeProfit.toFixed(4)}`);
return {
type: "BUY",
price: price,
size: positionSize,
stopLoss: stopLoss,
takeProfit: takeProfit
};
}
} catch (error) {
bdpo.logError(`Failed to place BUY order: ${error.message}`);
}
return null;
}
private generateSellSignal(price: number): any {
const openPositions = this.getOpenPositions();
openPositions.forEach(position => {
try {
const closeOrder = bdpo.orderClose(position.ticket, position.volume, price, 3);
if (closeOrder) {
const profit = (price - position.entryPrice) * position.volume;
this._tradeCount++;
if (profit > 0) {
this._winCount++;
bdpo.logSuccess(`Position closed with profit: +$${profit.toFixed(2)}`);
} else {
bdpo.logWarn(`Position closed with loss: -$${Math.abs(profit).toFixed(2)}`);
}
// Remove from positions array
this._positions = this._positions.filter(p => p.ticket !== position.ticket);
}
} catch (error) {
bdpo.logError(`Failed to close position: ${error.message}`);
}
});
return {
type: "SELL",
price: price,
closedPositions: openPositions.length
};
}
private calculatePositionSize(price: number): number {
const accountBalance = bdpo.getBalance();
const riskAmount = accountBalance * (this.riskPercentPerTrade / 100);
const stopLossDistance = price * (this.stopLossPercent / 100);
const positionSize = riskAmount / stopLossDistance;
// Apply position size limits
const minSize = bdpo.getMinQty() || 0.01;
const maxSize = bdpo.getMaxQty() || 10;
return Math.max(minSize, Math.min(maxSize, positionSize));
}
private updateTrailingStops(currentPrice: number): void {
const openPositions = this.getOpenPositions();
openPositions.forEach(position => {
if (position.trailingStop) {
const trailingDistance = currentPrice * (this.trailingStopPercent / 100);
const newTrailingStop = currentPrice - trailingDistance;
// Only update if new trailing stop is higher than current
if (newTrailingStop > position.trailingStop) {
position.trailingStop = newTrailingStop;
// Update the actual stop loss order
try {
bdpo.orderModify(position.ticket, position.openPrice, newTrailingStop, position.takeProfit);
bdpo.logDebug(`Trailing stop updated to ${newTrailingStop.toFixed(4)} for position ${position.ticket}`);
} catch (error) {
bdpo.logError(`Failed to update trailing stop: ${error.message}`);
}
}
}
});
}
private getOpenPositions(): any[] {
return this._positions.filter(pos => pos.closeTime === 0 || pos.closeTime === undefined);
}
__shutdown__(): void {
// Close all open positions
const openPositions = this.getOpenPositions();
if (openPositions.length > 0) {
bdpo.logInfo(`Closing ${openPositions.length} open positions during shutdown`);
openPositions.forEach(position => {
try {
bdpo.orderClose(position.ticket, position.volume, bdpo.getAsk(), 5);
} catch (error) {
bdpo.logError(`Failed to close position during shutdown: ${error.message}`);
}
});
}
// Log final performance statistics
const winRate = this._tradeCount > 0 ? (this._winCount / this._tradeCount * 100) : 0;
bdpo.logInfo("=== STRATEGY PERFORMANCE SUMMARY ===");
bdpo.logInfo(`Total Trades: ${this._tradeCount}`);
bdpo.logInfo(`Winning Trades: ${this._winCount}`);
bdpo.logInfo(`Win Rate: ${winRate.toFixed(1)}%`);
bdpo.logSuccess("MA Crossover Strategy shutdown completed");
}
}
- Risk Management: Position sizing based on account risk percentage
- Stop Loss & Take Profit: Automatic risk/reward management
- Trailing Stops: Lock in profits as trades move favorably
- Performance Tracking: Win rate and trade statistics
- Configurable Parameters: Easy to optimize and customize
Strategy Overview
This strategy identifies consolidation periods and trades breakouts above resistance or below support levels. It uses Donchian Channels to define the breakout levels and includes volatility-based position sizing.
class BreakoutStrategy {
// Public configuration
public lookbackPeriod: number = 20; // Donchian Channel period
public minConsolidation: number = 10; // Minimum bars in consolidation
public volatilityPeriod: number = 14; // ATR period for volatility
public riskPercentPerTrade: number = 2; // Risk per trade
public atrMultiplier: number = 2; // ATR multiplier for stops
public maxPositionsPerSide: number = 1; // Max long/short positions
public enableVolumeFilter: boolean = true;
public minVolumeRatio: number = 1.5; // Minimum volume spike for breakout
// Private state
private _positions: any[] = [];
private _donchianUpper: number[] = [];
private _donchianLower: number[] = [];
private _atr: number[] = [];
private _consolidationBars: number = 0;
private _lastBreakout: string | null = null;
__init__(): void {
this._positions = [];
this._consolidationBars = 0;
this._lastBreakout = null;
bdpo.logInfo("Breakout Strategy initialized");
bdpo.logInfo(`Lookback: ${this.lookbackPeriod}, Min Consolidation: ${this.minConsolidation}`);
bdpo.logInfo(`Risk per trade: ${this.riskPercentPerTrade}%, ATR Multiplier: ${this.atrMultiplier}`);
}
__tick__(): any {
const highs: number[] = bdpo.getHighPrices();
const lows: number[] = bdpo.getLowPrices();
const closes: number[] = bdpo.getClosePrices();
const volumes: number[] = bdpo.getVolumePrices();
if (closes.length < this.lookbackPeriod + this.volatilityPeriod) {
return { status: "warming_up" };
}
const currentHigh = highs[highs.length - 1];
const currentLow = lows[lows.length - 1];
const currentClose = closes[closes.length - 1];
const currentVolume = volumes[volumes.length - 1];
// Calculate indicators
this._donchianUpper = this.calculateDonchianUpper(highs, this.lookbackPeriod);
this._donchianLower = this.calculateDonchianLower(lows, this.lookbackPeriod);
this._atr = bdpo.atr(highs, lows, closes, this.volatilityPeriod);
const upperChannel = this._donchianUpper[this._donchianUpper.length - 1];
const lowerChannel = this._donchianLower[this._donchianLower.length - 1];
const currentATR = this._atr[this._atr.length - 1];
// Check for consolidation
const channelRange = upperChannel - lowerChannel;
const avgRange = bdpo.mean(this._atr.slice(-20));
const isConsolidating = channelRange < avgRange * 1.5;
if (isConsolidating) {
this._consolidationBars++;
} else {
this._consolidationBars = 0;
}
// Volume filter
let volumeConfirmed = true;
if (this.enableVolumeFilter) {
const avgVolume = bdpo.mean(volumes.slice(-20));
volumeConfirmed = currentVolume > avgVolume * this.minVolumeRatio;
}
// Check for breakout signals
let signal: any = null;
const longPositions = this.getLongPositions();
const shortPositions = this.getShortPositions();
// Bullish breakout
if (currentHigh > upperChannel &&
this._consolidationBars >= this.minConsolidation &&
volumeConfirmed &&
longPositions.length < this.maxPositionsPerSide) {
signal = this.generateLongEntry(currentClose, currentATR);
this._lastBreakout = 'bullish';
this._consolidationBars = 0;
}
// Bearish breakout
else if (currentLow < lowerChannel &&
this._consolidationBars >= this.minConsolidation &&
volumeConfirmed &&
shortPositions.length < this.maxPositionsPerSide) {
signal = this.generateShortEntry(currentClose, currentATR);
this._lastBreakout = 'bearish';
this._consolidationBars = 0;
}
// Exit management
this.manageExits(currentClose, currentATR);
// Plot channels
bdpo.plot("splines", [this._donchianUpper], { splineColor: "#FF5722", splineWidth: 1, skipNan: true }, { title: "Upper Channel" });
bdpo.plot("splines", [this._donchianLower], { splineColor: "#4CAF50", splineWidth: 1, skipNan: true }, { title: "Lower Channel" });
return {
signal: signal,
indicators: {
upperChannel: upperChannel,
lowerChannel: lowerChannel,
channelRange: channelRange.toFixed(4),
consolidationBars: this._consolidationBars,
atr: currentATR.toFixed(4)
},
positions: {
long: longPositions.length,
short: shortPositions.length
},
lastBreakout: this._lastBreakout
};
}
private generateLongEntry(price: number, atr: number): any {
const stopLoss = price - (atr * this.atrMultiplier);
const takeProfit = price + (atr * this.atrMultiplier * 2); // 1:2 risk/reward
const positionSize = this.calculatePositionSize(price, stopLoss);
try {
const order = bdpo.orderSend({
orderType: "BUY",
lotSize: positionSize,
stopLoss: stopLoss,
takeProfit: takeProfit,
note: "Bullish Breakout Entry"
});
if (order) {
this._positions.push({
...order,
direction: "LONG",
entryPrice: price,
stopLoss: stopLoss,
takeProfit: takeProfit,
entryTime: Date.now()
});
bdpo.logSuccess(`LONG breakout entry: Price=${price.toFixed(4)}, SL=${stopLoss.toFixed(4)}, TP=${takeProfit.toFixed(4)}`);
return { type: "BUY", price: price, stopLoss: stopLoss, takeProfit: takeProfit };
}
} catch (error) {
bdpo.logError(`Failed to place LONG order: ${error.message}`);
}
return null;
}
private generateShortEntry(price: number, atr: number): any {
const stopLoss = price + (atr * this.atrMultiplier);
const takeProfit = price - (atr * this.atrMultiplier * 2); // 1:2 risk/reward
const positionSize = this.calculatePositionSize(price, stopLoss);
try {
const order = bdpo.orderSend({
orderType: "SELL",
lotSize: positionSize,
stopLoss: stopLoss,
takeProfit: takeProfit,
note: "Bearish Breakout Entry"
});
if (order) {
this._positions.push({
...order,
direction: "SHORT",
entryPrice: price,
stopLoss: stopLoss,
takeProfit: takeProfit,
entryTime: Date.now()
});
bdpo.logSuccess(`SHORT breakout entry: Price=${price.toFixed(4)}, SL=${stopLoss.toFixed(4)}, TP=${takeProfit.toFixed(4)}`);
return { type: "SELL", price: price, stopLoss: stopLoss, takeProfit: takeProfit };
}
} catch (error) {
bdpo.logError(`Failed to place SHORT order: ${error.message}`);
}
return null;
}
private calculatePositionSize(entryPrice: number, stopLoss: number): number {
const accountBalance = bdpo.getBalance();
const riskAmount = accountBalance * (this.riskPercentPerTrade / 100);
const riskPerUnit = Math.abs(entryPrice - stopLoss);
const positionSize = riskAmount / riskPerUnit;
const minSize = bdpo.getMinQty() || 0.01;
const maxSize = bdpo.getMaxQty() || 10;
return Math.max(minSize, Math.min(maxSize, positionSize));
}
private calculateDonchianUpper(highs: number[], period: number): number[] {
const result: number[] = [];
for (let i = period - 1; i < highs.length; i++) {
const periodHighs = highs.slice(i - period + 1, i + 1);
result.push(Math.max(...periodHighs));
}
return result;
}
private calculateDonchianLower(lows: number[], period: number): number[] {
const result: number[] = [];
for (let i = period - 1; i < lows.length; i++) {
const periodLows = lows.slice(i - period + 1, i + 1);
result.push(Math.min(...periodLows));
}
return result;
}
private manageExits(currentPrice: number, currentATR: number): void {
// Implementation for exit management
const openPositions = this.getOpenPositions();
openPositions.forEach(position => {
const holdingTime = Date.now() - position.entryTime;
const holdingHours = holdingTime / (1000 * 60 * 60);
// Exit if position held too long without progress
if (holdingHours > 24) {
this.closePosition(position, "Time-based exit");
}
});
}
private closePosition(position: any, reason: string): void {
try {
const currentPrice = position.direction === "LONG" ? bdpo.getBid() : bdpo.getAsk();
const closeOrder = bdpo.orderClose(position.ticket, position.volume, currentPrice, 3);
if (closeOrder) {
bdpo.logInfo(`Position closed: ${reason}`);
this._positions = this._positions.filter(p => p.ticket !== position.ticket);
}
} catch (error) {
bdpo.logError(`Failed to close position: ${error.message}`);
}
}
private getLongPositions(): any[] {
return this._positions.filter(pos => pos.direction === "LONG" && (pos.closeTime === 0 || pos.closeTime === undefined));
}
private getShortPositions(): any[] {
return this._positions.filter(pos => pos.direction === "SHORT" && (pos.closeTime === 0 || pos.closeTime === undefined));
}
private getOpenPositions(): any[] {
return this._positions.filter(pos => pos.closeTime === 0 || pos.closeTime === undefined);
}
__shutdown__(): void {
const openPositions = this.getOpenPositions();
openPositions.forEach(position => {
this.closePosition(position, "Strategy shutdown");
});
bdpo.logSuccess("Breakout Strategy shutdown completed");
}
}
Mean Reversion Strategies
Strategy Overview
This mean reversion strategy uses RSI to identify oversold and overbought conditions, with Bollinger Bands providing additional confirmation. It's designed for range-bound markets and includes smart position sizing.
class RSIMeanReversionStrategy {
// Public parameters
public rsiPeriod: number = 14;
public rsiOversold: number = 30;
public rsiOverbought: number = 70;
public bbPeriod: number = 20;
public bbStdDev: number = 2;
public riskPercentPerTrade: number = 1.5;
public maxPositions: number = 2;
public requireBBConfirmation: boolean = true;
public enableScaleIn: boolean = true; // Scale into positions
public maxScaleIns: number = 3;
// Private state
private _positions: any[] = [];
private _rsi: number[] = [];
private _bbands: any = null;
private _scaleInCount: Map = new Map();
__init__(): void {
this._positions = [];
this._scaleInCount.clear();
bdpo.logInfo("RSI Mean Reversion Strategy initialized");
bdpo.logInfo(`RSI: ${this.rsiPeriod}, Oversold: ${this.rsiOversold}, Overbought: ${this.rsiOverbought}`);
bdpo.logInfo(`BB: ${this.bbPeriod} period, ${this.bbStdDev} std dev`);
}
__tick__(): any {
const closes: number[] = bdpo.getClosePrices();
const currentPrice = closes[closes.length - 1];
if (closes.length < Math.max(this.rsiPeriod, this.bbPeriod) + 1) {
return { status: "warming_up" };
}
// Calculate indicators
this._rsi = bdpo.rsi(closes, this.rsiPeriod);
this._bbands = bdpo.bbands(closes, this.bbPeriod, this.bbStdDev);
const currentRSI = this._rsi[this._rsi.length - 1];
const prevRSI = this._rsi[this._rsi.length - 2];
const upperBB = this._bbands.upper[this._bbands.upper.length - 1];
const lowerBB = this._bbands.lower[this._bbands.lower.length - 1];
const middleBB = this._bbands.middle[this._bbands.middle.length - 1];
let signal: any = null;
const openPositions = this.getOpenPositions();
// Entry signals
if (openPositions.length < this.maxPositions) {
// Oversold condition - potential buy
if (currentRSI < this.rsiOversold && prevRSI >= this.rsiOversold) {
const bbConfirmed = !this.requireBBConfirmation || currentPrice <= lowerBB;
if (bbConfirmed) {
signal = this.generateBuySignal(currentPrice, middleBB);
}
}
// Overbought condition - potential sell
else if (currentRSI > this.rsiOverbought && prevRSI <= this.rsiOverbought) {
const bbConfirmed = !this.requireBBConfirmation || currentPrice >= upperBB;
if (bbConfirmed) {
signal = this.generateSellSignal(currentPrice, middleBB);
}
}
}
// Scale-in management
if (this.enableScaleIn) {
this.manageScaleIns(currentPrice, currentRSI, upperBB, lowerBB);
}
// Exit management
this.manageExits(currentPrice, currentRSI, middleBB);
// Plot indicators
bdpo.plot("splines", [this._rsi], { splineColor: "#9C27B0", splineWidth: 2, skipNan: true }, { title: `RSI ${this.rsiPeriod}` });
bdpo.plot("splines", [this._bbands.upper], { splineColor: "#2196F3", splineWidth: 1, skipNan: true }, { title: "BB Upper" });
bdpo.plot("splines", [this._bbands.lower], { splineColor: "#2196F3", splineWidth: 1, skipNan: true }, { title: "BB Lower" });
bdpo.plot("splines", [this._bbands.middle], { splineColor: "#2196F3", splineWidth: 1, skipNan: true }, { title: "BB Middle" });
return {
signal: signal,
indicators: {
rsi: currentRSI.toFixed(2),
bbUpper: upperBB.toFixed(4),
bbLower: lowerBB.toFixed(4),
bbPosition: this.getBBPosition(currentPrice, upperBB, lowerBB, middleBB)
},
positions: openPositions.length,
scaleIns: Array.from(this._scaleInCount.values()).reduce((a, b) => a + b, 0)
};
}
private generateBuySignal(price: number, target: number): any {
const stopLoss = price * 0.97; // 3% stop loss
const takeProfit = target; // Target middle BB
const positionSize = this.calculatePositionSize(price, stopLoss);
try {
const order = bdpo.orderSend({
orderType: "BUY",
lotSize: positionSize,
stopLoss: stopLoss,
takeProfit: takeProfit,
note: "RSI Oversold Mean Reversion"
});
if (order) {
const positionId = `LONG_${Date.now()}`;
this._positions.push({
...order,
id: positionId,
direction: "LONG",
entryPrice: price,
stopLoss: stopLoss,
takeProfit: takeProfit,
entryTime: Date.now(),
strategy: "mean_reversion"
});
this._scaleInCount.set(positionId, 0);
bdpo.logSuccess(`BUY signal: RSI oversold entry at ${price.toFixed(4)}`);
return { type: "BUY", price: price, reason: "RSI oversold" };
}
} catch (error) {
bdpo.logError(`Failed to place BUY order: ${error.message}`);
}
return null;
}
private generateSellSignal(price: number, target: number): any {
const stopLoss = price * 1.03; // 3% stop loss
const takeProfit = target; // Target middle BB
const positionSize = this.calculatePositionSize(price, stopLoss);
try {
const order = bdpo.orderSend({
orderType: "SELL",
lotSize: positionSize,
stopLoss: stopLoss,
takeProfit: takeProfit,
note: "RSI Overbought Mean Reversion"
});
if (order) {
const positionId = `SHORT_${Date.now()}`;
this._positions.push({
...order,
id: positionId,
direction: "SHORT",
entryPrice: price,
stopLoss: stopLoss,
takeProfit: takeProfit,
entryTime: Date.now(),
strategy: "mean_reversion"
});
this._scaleInCount.set(positionId, 0);
bdpo.logSuccess(`SELL signal: RSI overbought entry at ${price.toFixed(4)}`);
return { type: "SELL", price: price, reason: "RSI overbought" };
}
} catch (error) {
bdpo.logError(`Failed to place SELL order: ${error.message}`);
}
return null;
}
private manageScaleIns(currentPrice: number, currentRSI: number, upperBB: number, lowerBB: number): void {
const openPositions = this.getOpenPositions();
openPositions.forEach(position => {
const scaleInCount = this._scaleInCount.get(position.id) || 0;
if (scaleInCount < this.maxScaleIns) {
let shouldScaleIn = false;
if (position.direction === "LONG") {
// Scale in if RSI gets more oversold or price drops further below lower BB
shouldScaleIn = (currentRSI < this.rsiOversold - 5) ||
(currentPrice < lowerBB * 0.995);
} else if (position.direction === "SHORT") {
// Scale in if RSI gets more overbought or price rises further above upper BB
shouldScaleIn = (currentRSI > this.rsiOverbought + 5) ||
(currentPrice > upperBB * 1.005);
}
if (shouldScaleIn) {
this.executeScaleIn(position, currentPrice);
}
}
});
}
private executeScaleIn(position: any, price: number): void {
const scaleInSize = position.volume * 0.5; // 50% of original position
try {
const scaleInOrder = bdpo.orderSend({
orderType: position.direction === "LONG" ? "BUY" : "SELL",
lotSize: scaleInSize,
stopLoss: position.stopLoss,
takeProfit: position.takeProfit,
note: `Scale-in for ${position.id}`
});
if (scaleInOrder) {
const currentCount = this._scaleInCount.get(position.id) || 0;
this._scaleInCount.set(position.id, currentCount + 1);
bdpo.logInfo(`Scale-in executed for ${position.direction} position: Size=${scaleInSize}, Price=${price.toFixed(4)}`);
}
} catch (error) {
bdpo.logError(`Failed to execute scale-in: ${error.message}`);
}
}
private manageExits(currentPrice: number, currentRSI: number, middleBB: number): void {
const openPositions = this.getOpenPositions();
openPositions.forEach(position => {
let shouldExit = false;
let exitReason = "";
if (position.direction === "LONG") {
// Exit long if RSI normalizes above 50 or price reaches middle BB
if (currentRSI > 50 || currentPrice >= middleBB) {
shouldExit = true;
exitReason = currentRSI > 50 ? "RSI normalized" : "Target reached";
}
} else if (position.direction === "SHORT") {
// Exit short if RSI normalizes below 50 or price reaches middle BB
if (currentRSI < 50 || currentPrice <= middleBB) {
shouldExit = true;
exitReason = currentRSI < 50 ? "RSI normalized" : "Target reached";
}
}
if (shouldExit) {
this.closePosition(position, exitReason);
}
});
}
private closePosition(position: any, reason: string): void {
try {
const currentPrice = position.direction === "LONG" ? bdpo.getBid() : bdpo.getAsk();
const closeOrder = bdpo.orderClose(position.ticket, position.volume, currentPrice, 3);
if (closeOrder) {
bdpo.logSuccess(`Position closed: ${reason}`);
this._positions = this._positions.filter(p => p.id !== position.id);
this._scaleInCount.delete(position.id);
}
} catch (error) {
bdpo.logError(`Failed to close position: ${error.message}`);
}
}
private calculatePositionSize(entryPrice: number, stopLoss: number): number {
const accountBalance = bdpo.getBalance();
const riskAmount = accountBalance * (this.riskPercentPerTrade / 100);
const riskPerUnit = Math.abs(entryPrice - stopLoss);
const positionSize = riskAmount / riskPerUnit;
const minSize = bdpo.getMinQty() || 0.01;
const maxSize = bdpo.getMaxQty() || 5;
return Math.max(minSize, Math.min(maxSize, positionSize));
}
private getBBPosition(price: number, upper: number, lower: number, middle: number): string {
if (price > upper) return "Above Upper";
if (price < lower) return "Below Lower";
if (price > middle) return "Upper Half";
return "Lower Half";
}
private getOpenPositions(): any[] {
return this._positions.filter(pos => pos.closeTime === 0 || pos.closeTime === undefined);
}
__shutdown__(): void {
const openPositions = this.getOpenPositions();
openPositions.forEach(position => {
this.closePosition(position, "Strategy shutdown");
});
bdpo.logSuccess("RSI Mean Reversion Strategy shutdown completed");
}
}
Multi-Timeframe Strategies
Strategy Overview
This strategy analyzes multiple timeframes to ensure trades align with the broader trend. It uses higher timeframes for trend direction and lower timeframes for precise entry timing.
class MultiTimeframeStrategy {
// Public configuration
public trendTimeframe: string = "1h"; // Higher timeframe for trend
public entryTimeframe: string = "15m"; // Lower timeframe for entries
public trendMAPeriod: number = 50; // Trend MA period
public entryMAPeriod: number = 20; // Entry MA period
public riskPercentPerTrade: number = 2;
public maxPositions: number = 2;
public requireTrendAlignment: boolean = true;
// Private state
private _positions: any[] = [];
private _trendDirection: string | null = null;
private _lastAnalysis: any = null;
__init__(): void {
this._positions = [];
this._trendDirection = null;
this._lastAnalysis = null;
bdpo.logInfo("Multi-Timeframe Strategy initialized");
bdpo.logInfo(`Trend TF: ${this.trendTimeframe}, Entry TF: ${this.entryTimeframe}`);
bdpo.logInfo(`Trend MA: ${this.trendMAPeriod}, Entry MA: ${this.entryMAPeriod}`);
}
__tick__(): any {
// Get data for both timeframes
const trendData = bdpo.getTimeframeData(this.trendTimeframe);
const entryData = bdpo.getTimeframeData(this.entryTimeframe);
if (!trendData || !entryData) {
return { status: "waiting_for_data" };
}
// Analyze higher timeframe trend
const trendAnalysis = this.analyzeTrend(trendData);
this._trendDirection = trendAnalysis.direction;
// Analyze entry timeframe
const entryAnalysis = this.analyzeEntry(entryData);
// Generate signals
let signal: any = null;
const openPositions = this.getOpenPositions();
if (openPositions.length < this.maxPositions && this._trendDirection !== "SIDEWAYS") {
if (this.requireTrendAlignment) {
// Only trade in direction of higher timeframe trend
if (this._trendDirection === "BULLISH" && entryAnalysis.signal === "BUY") {
signal = this.generateBuySignal(entryAnalysis);
} else if (this._trendDirection === "BEARISH" && entryAnalysis.signal === "SELL") {
signal = this.generateSellSignal(entryAnalysis);
}
} else {
// Trade any signal
if (entryAnalysis.signal === "BUY") {
signal = this.generateBuySignal(entryAnalysis);
} else if (entryAnalysis.signal === "SELL") {
signal = this.generateSellSignal(entryAnalysis);
}
}
}
// Manage exits
this.manageExits(entryAnalysis);
this._lastAnalysis = {
trend: trendAnalysis,
entry: entryAnalysis,
timestamp: Date.now()
};
return {
signal: signal,
trendDirection: this._trendDirection,
trendStrength: trendAnalysis.strength,
entrySignal: entryAnalysis.signal,
entryStrength: entryAnalysis.strength,
positions: openPositions.length,
alignment: this._trendDirection === entryAnalysis.trendBias ? "ALIGNED" : "DIVERGENT"
};
}
private analyzeTrend(trendData: any[]): any {
const closes = bdpo.getClose(trendData);
const highs = bdpo.getHigh(trendData);
const lows = bdpo.getLow(trendData);
if (closes.length < this.trendMAPeriod + 20) {
return { direction: "UNKNOWN", strength: 0 };
}
const trendMA = bdpo.ema(closes, this.trendMAPeriod);
const currentPrice = closes[closes.length - 1];
const currentMA = trendMA[trendMA.length - 1];
const prevMA = trendMA[trendMA.length - 2];
// Calculate trend strength using various factors
const maSlope = (currentMA - prevMA) / prevMA * 100;
const priceToMADistance = (currentPrice - currentMA) / currentMA * 100;
// Higher highs and higher lows for uptrend
const recentHighs = highs.slice(-10);
const recentLows = lows.slice(-10);
const hhhl = this.calculateTrendPattern(recentHighs, recentLows);
let direction: string;
let strength: number;
if (currentPrice > currentMA && maSlope > 0.1 && hhhl.score > 0.6) {
direction = "BULLISH";
strength = Math.min(100, (Math.abs(maSlope) + Math.abs(priceToMADistance) + hhhl.score * 50));
} else if (currentPrice < currentMA && maSlope < -0.1 && hhhl.score < -0.6) {
direction = "BEARISH";
strength = Math.min(100, (Math.abs(maSlope) + Math.abs(priceToMADistance) + Math.abs(hhhl.score) * 50));
} else {
direction = "SIDEWAYS";
strength = 0;
}
return {
direction: direction,
strength: strength,
maSlope: maSlope,
priceToMA: priceToMADistance,
patternScore: hhhl.score
};
}
private analyzeEntry(entryData: any[]): any {
const closes = bdpo.getClose(entryData);
const currentPrice = closes[closes.length - 1];
if (closes.length < this.entryMAPeriod + 5) {
return { signal: "WAIT", strength: 0, trendBias: "UNKNOWN" };
}
const entryMA = bdpo.ema(closes, this.entryMAPeriod);
const rsi = bdpo.rsi(closes, 14);
const currentMA = entryMA[entryMA.length - 1];
const prevMA = entryMA[entryMA.length - 2];
const currentRSI = rsi[rsi.length - 1];
// Determine entry timeframe bias
const trendBias = currentPrice > currentMA ? "BULLISH" : "BEARISH";
let signal: string = "WAIT";
let strength: number = 0;
// Look for pullback opportunities in trend direction
if (this._trendDirection === "BULLISH") {
// Look for dips to buy in uptrend
if (currentPrice < currentMA && currentPrice > prevMA && currentRSI < 60) {
signal = "BUY";
strength = 60 + (60 - currentRSI); // Stronger signal if more oversold
}
} else if (this._trendDirection === "BEARISH") {
// Look for rallies to sell in downtrend
if (currentPrice > currentMA && currentPrice < prevMA && currentRSI > 40) {
signal = "SELL";
strength = 40 + (currentRSI - 40); // Stronger signal if more overbought
}
}
return {
signal: signal,
strength: strength,
trendBias: trendBias,
rsi: currentRSI,
priceToMA: ((currentPrice - currentMA) / currentMA * 100)
};
}
private calculateTrendPattern(highs: number[], lows: number[]): any {
// Simplified trend pattern calculation
let bullishPoints = 0;
let bearishPoints = 0;
for (let i = 1; i < highs.length; i++) {
if (highs[i] > highs[i-1]) bullishPoints++;
else bearishPoints++;
if (lows[i] > lows[i-1]) bullishPoints++;
else bearishPoints++;
}
const totalPoints = bullishPoints + bearishPoints;
const score = totalPoints > 0 ? (bullishPoints - bearishPoints) / totalPoints : 0;
return { score: score, bullish: bullishPoints, bearish: bearishPoints };
}
private generateBuySignal(entryAnalysis: any): any {
const currentPrice = bdpo.getAsk();
const stopLoss = currentPrice * 0.98; // 2% stop loss
const takeProfit = currentPrice * 1.06; // 3:1 risk/reward
const positionSize = this.calculatePositionSize(currentPrice, stopLoss);
try {
const order = bdpo.orderSend({
orderType: "BUY",
lotSize: positionSize,
stopLoss: stopLoss,
takeProfit: takeProfit,
note: `Multi-TF Bullish Entry (Strength: ${entryAnalysis.strength})`
});
if (order) {
this._positions.push({
...order,
direction: "LONG",
entryPrice: currentPrice,
entryAnalysis: entryAnalysis,
entryTime: Date.now()
});
bdpo.logSuccess(`Multi-TF BUY: Price=${currentPrice.toFixed(4)}, Trend=${this._trendDirection}, Strength=${entryAnalysis.strength}`);
return {
type: "BUY",
price: currentPrice,
trendAlignment: true,
strength: entryAnalysis.strength
};
}
} catch (error) {
bdpo.logError(`Failed to place BUY order: ${error.message}`);
}
return null;
}
private generateSellSignal(entryAnalysis: any): any {
const currentPrice = bdpo.getBid();
const stopLoss = currentPrice * 1.02; // 2% stop loss
const takeProfit = currentPrice * 0.94; // 3:1 risk/reward
const positionSize = this.calculatePositionSize(currentPrice, stopLoss);
try {
const order = bdpo.orderSend({
orderType: "SELL",
lotSize: positionSize,
stopLoss: stopLoss,
takeProfit: takeProfit,
note: `Multi-TF Bearish Entry (Strength: ${entryAnalysis.strength})`
});
if (order) {
this._positions.push({
...order,
direction: "SHORT",
entryPrice: currentPrice,
entryAnalysis: entryAnalysis,
entryTime: Date.now()
});
bdpo.logSuccess(`Multi-TF SELL: Price=${currentPrice.toFixed(4)}, Trend=${this._trendDirection}, Strength=${entryAnalysis.strength}`);
return {
type: "SELL",
price: currentPrice,
trendAlignment: true,
strength: entryAnalysis.strength
};
}
} catch (error) {
bdpo.logError(`Failed to place SELL order: ${error.message}`);
}
return null;
}
private manageExits(entryAnalysis: any): void {
const openPositions = this.getOpenPositions();
openPositions.forEach(position => {
let shouldExit = false;
let exitReason = "";
// Exit if trend changes against position
if (position.direction === "LONG" && this._trendDirection === "BEARISH") {
shouldExit = true;
exitReason = "Trend reversal";
} else if (position.direction === "SHORT" && this._trendDirection === "BULLISH") {
shouldExit = true;
exitReason = "Trend reversal";
}
// Exit if RSI shows extreme opposite conditions
if (position.direction === "LONG" && entryAnalysis.rsi > 80) {
shouldExit = true;
exitReason = "RSI overbought";
} else if (position.direction === "SHORT" && entryAnalysis.rsi < 20) {
shouldExit = true;
exitReason = "RSI oversold";
}
if (shouldExit) {
this.closePosition(position, exitReason);
}
});
}
private closePosition(position: any, reason: string): void {
try {
const currentPrice = position.direction === "LONG" ? bdpo.getBid() : bdpo.getAsk();
const closeOrder = bdpo.orderClose(position.ticket, position.volume, currentPrice, 3);
if (closeOrder) {
bdpo.logInfo(`Multi-TF position closed: ${reason}`);
this._positions = this._positions.filter(p => p.ticket !== position.ticket);
}
} catch (error) {
bdpo.logError(`Failed to close position: ${error.message}`);
}
}
private calculatePositionSize(entryPrice: number, stopLoss: number): number {
const accountBalance = bdpo.getBalance();
const riskAmount = accountBalance * (this.riskPercentPerTrade / 100);
const riskPerUnit = Math.abs(entryPrice - stopLoss);
const positionSize = riskAmount / riskPerUnit;
const minSize = bdpo.getMinQty() || 0.01;
const maxSize = bdpo.getMaxQty() || 5;
return Math.max(minSize, Math.min(maxSize, positionSize));
}
private getOpenPositions(): any[] {
return this._positions.filter(pos => pos.closeTime === 0 || pos.closeTime === undefined);
}
__shutdown__(): void {
const openPositions = this.getOpenPositions();
openPositions.forEach(position => {
this.closePosition(position, "Strategy shutdown");
});
bdpo.logSuccess("Multi-Timeframe Strategy shutdown completed");
}
}
- Risk Management: All strategies include position sizing based on account risk
- Market Conditions: Test strategies in different market conditions (trending, ranging, volatile)
- Parameter Optimization: Adjust parameters based on your market and trading style
- Live Trading: Always test strategies on demo accounts before live implementation
- Performance Monitoring: Track win rates, drawdowns, and risk-adjusted returns
Indicator Examples
Simple and practical indicator implementations for beginners
Simple Momentum Indicators
Indicator Overview
This simple RSI indicator calculates the Relative Strength Index and shows clear signals when RSI crosses overbought or oversold levels. Perfect for beginners learning technical analysis.
class SimpleRSI {
public rsiLength: number = 14;
__init__(): void {
console.log("RSI Indicator started");
}
__tick__(): any {
// Calculate RSI with configurable length
let candles = bdpo.getChartData()
let closes = bdpo.getClose(candles)
const rsi = bdpo.rsi(closes, this.rsiLength);
// Plot the RSI line
bdpo.plot("spline", rsi);
}
__shutdown__(): void {
console.log("RSI Indicator stopped");
}
}
- RSI Calculation: Uses 14-period RSI by default
- Buy Signal: When RSI drops below 30 (oversold)
- Sell Signal: When RSI rises above 70 (overbought)
- Visualization: Plots RSI with reference lines in separate panel
Indicator Overview
This indicator shows two moving averages and generates signals when they cross over. When the fast MA crosses above the slow MA, it's a bullish signal. When it crosses below, it's bearish.
class SimpleMovingAverageCrossover {
__init__(): void {
console.log("MA Crossover Indicator started");
}
__tick__(): any {
// Calculate fast and slow moving averages
const fastMA = bdpo.sma(bdpo.getClose(), 10);
const slowMA = bdpo.sma(bdpo.getClose(), 20);
// Plot both lines
bdpo.plot("splines", [fastMA]);
bdpo.plot("splines", [slowMA]);
// Check for crossover signals
const currentFast = fastMA[fastMA.length - 1];
const currentSlow = slowMA[slowMA.length - 1];
if (currentFast > currentSlow) {
console.log("BUY signal - Fast MA above Slow MA");
} else {
console.log("SELL signal - Fast MA below Slow MA");
}
return { fastMA: currentFast, slowMA: currentSlow };
}
__shutdown__(): void {
console.log("MA Crossover Indicator stopped");
}
}
- Fast MA: Short-term moving average (10 periods)
- Slow MA: Long-term moving average (20 periods)
- Bullish Signal: When fast MA crosses above slow MA
- Bearish Signal: When fast MA crosses below slow MA
Indicator Overview
This is the simplest possible indicator - it just plots the closing price with signals showing price direction.
class SimplePriceLine {
__init__(): void {
console.log("Price Line Indicator started");
}
__tick__(): any {
// Get price data
const prices = bdpo.getClose();
// Plot the price line
bdpo.plot("splines", [prices]);
// Show if price is going up or down
const currentPrice = prices[prices.length - 1];
const previousPrice = prices[prices.length - 2];
if (currentPrice > previousPrice) {
console.log("Price going UP");
} else if (currentPrice < previousPrice) {
console.log("Price going DOWN");
}
return { price: currentPrice };
}
__shutdown__(): void {
console.log("Price Line Indicator stopped");
}
}
- Simple Plot: Shows the closing price as a line
- Price Direction: Compares current price to previous price
- Perfect for beginners: The most basic indicator possible
- Start with Simple Price Line: The most basic example shows how indicators work
- Three Functions Only: Every indicator needs __init__(), __tick__(), and __shutdown__()
- Use bdpo.plot(): This single function displays all your data on charts
- Console.log for Signals: Simple way to show buy/sell alerts
- Copy and Modify: Start with these examples and change the logic to fit your needs
Best Practices
Guidelines for effective indicator and strategy development
Development Best Practices
- Keep It Simple: Start with basic indicators and gradually add complexity
- Test Thoroughly: Always backtest on historical data before live implementation
- Risk Management: Include proper position sizing and stop-loss mechanisms
- Document Your Code: Add clear comments explaining your logic
- Parameter Optimization: Don't over-optimize - ensure robustness across different market conditions
Indicator Overview
This enhanced Bollinger Bands indicator automatically adjusts its parameters based on market volatility, providing more accurate signals in both trending and ranging markets.
class DynamicBollingerBands {
// Public configuration
public basePeriod: number = 20; // Base period for moving average
public baseStdDev: number = 2; // Base standard deviation multiplier
public volatilityPeriod: number = 14; // Period for volatility calculation
public adaptiveEnabled: boolean = true; // Enable adaptive adjustment
public minPeriod: number = 10; // Minimum period allowed
public maxPeriod: number = 50; // Maximum period allowed
public minStdDev: number = 1; // Minimum std dev multiplier
public maxStdDev: number = 3; // Maximum std dev multiplier
public showSqueezeSignals: boolean = true; // Show squeeze/expansion signals
// Private state
private _closes: number[] = [];
private _volatility: number[] = [];
private _adaptivePeriod: number = 20;
private _adaptiveStdDev: number = 2;
private _upperBand: number[] = [];
private _lowerBand: number[] = [];
private _middleBand: number[] = [];
private _bandwidth: number[] = [];
private _squeezeState: boolean = false;
private _lastSqueezeBar: number = -1;
__init__(): void {
this._closes = [];
this._volatility = [];
this._adaptivePeriod = this.basePeriod;
this._adaptiveStdDev = this.baseStdDev;
this._upperBand = [];
this._lowerBand = [];
this._middleBand = [];
this._bandwidth = [];
this._squeezeState = false;
this._lastSqueezeBar = -1;
bdpo.logInfo("Dynamic Bollinger Bands initialized");
bdpo.logInfo(`Base Period: ${this.basePeriod}, Base StdDev: ${this.baseStdDev}`);
}
__tick__(): any {
this._closes = bdpo.getClose();
if (this._closes.length < Math.max(this.basePeriod, this.volatilityPeriod) + 5) {
return { status: "warming_up" };
}
// Calculate current volatility
this.calculateVolatility();
// Adjust parameters based on volatility if adaptive mode is enabled
if (this.adaptiveEnabled) {
this.adjustParameters();
}
// Calculate Bollinger Bands with current parameters
this.calculateBands();
// Calculate bandwidth and detect squeeze/expansion
this.calculateBandwidth();
const squeezeSignal = this.detectSqueezeExpansion();
// Plot bands and signals
this.plotBands();
if (squeezeSignal) {
this.plotSqueezeSignals(squeezeSignal);
}
const currentClose = this._closes[this._closes.length - 1];
const currentUpper = this._upperBand[this._upperBand.length - 1];
const currentLower = this._lowerBand[this._lowerBand.length - 1];
const currentMiddle = this._middleBand[this._middleBand.length - 1];
return {
upperBand: currentUpper,
lowerBand: currentLower,
middleBand: currentMiddle,
bandwidth: this._bandwidth[this._bandwidth.length - 1],
adaptivePeriod: this._adaptivePeriod,
adaptiveStdDev: this._adaptiveStdDev,
position: this.getBandPosition(currentClose, currentUpper, currentLower, currentMiddle),
squeeze: this._squeezeState,
signal: squeezeSignal
};
}
private calculateVolatility(): void {
// Calculate ATR-based volatility
const highs = bdpo.getHigh();
const lows = bdpo.getLow();
this._volatility = bdpo.atr(highs, lows, this._closes, this.volatilityPeriod);
}
private adjustParameters(): void {
if (this._volatility.length < 2) return;
const currentVol = this._volatility[this._volatility.length - 1];
const avgVol = bdpo.mean(this._volatility.slice(-20)); // 20-period average volatility
if (avgVol === 0) return;
const volRatio = currentVol / avgVol;
// Adjust period: shorter in high volatility, longer in low volatility
const periodAdjustment = 1 / volRatio;
this._adaptivePeriod = Math.round(this.basePeriod * periodAdjustment);
this._adaptivePeriod = Math.max(this.minPeriod, Math.min(this.maxPeriod, this._adaptivePeriod));
// Adjust standard deviation: higher in high volatility, lower in low volatility
const stdDevAdjustment = Math.sqrt(volRatio);
this._adaptiveStdDev = this.baseStdDev * stdDevAdjustment;
this._adaptiveStdDev = Math.max(this.minStdDev, Math.min(this.maxStdDev, this._adaptiveStdDev));
}
private calculateBands(): void {
// Calculate bands using current adaptive parameters
const bands = bdpo.bbands(this._closes, this._adaptivePeriod, this._adaptiveStdDev);
this._upperBand = bands.upper;
this._lowerBand = bands.lower;
this._middleBand = bands.middle;
}
private calculateBandwidth(): void {
this._bandwidth = [];
for (let i = 0; i < this._upperBand.length; i++) {
const bandwidth = (this._upperBand[i] - this._lowerBand[i]) / this._middleBand[i] * 100;
this._bandwidth.push(bandwidth);
}
}
private detectSqueezeExpansion(): any {
if (this._bandwidth.length < 20) return null;
const currentBandwidth = this._bandwidth[this._bandwidth.length - 1];
const avgBandwidth = bdpo.mean(this._bandwidth.slice(-20));
const bandwidthPercentile = this.calculatePercentile(this._bandwidth.slice(-50), currentBandwidth);
const currentBar = this._closes.length - 1;
// Detect squeeze (low bandwidth)
if (!this._squeezeState && bandwidthPercentile < 20) {
this._squeezeState = true;
this._lastSqueezeBar = currentBar;
bdpo.logInfo(`🔥 Bollinger Band SQUEEZE detected at bar ${currentBar}`);
bdpo.logInfo(`Bandwidth: ${currentBandwidth.toFixed(4)} (${bandwidthPercentile.toFixed(1)}th percentile)`);
return {
type: 'squeeze',
bandwidth: currentBandwidth,
percentile: bandwidthPercentile
};
}
// Detect expansion (breaking out of squeeze)
if (this._squeezeState && bandwidthPercentile > 50) {
this._squeezeState = false;
const squeezeLength = currentBar - this._lastSqueezeBar;
bdpo.logSuccess(`💥 Bollinger Band EXPANSION detected!`);
bdpo.logInfo(`Squeeze lasted ${squeezeLength} bars`);
bdpo.logInfo(`Bandwidth: ${currentBandwidth.toFixed(4)} (${bandwidthPercentile.toFixed(1)}th percentile)`);
return {
type: 'expansion',
bandwidth: currentBandwidth,
percentile: bandwidthPercentile,
squeezeLength: squeezeLength
};
}
return null;
}
private calculatePercentile(data: number[], value: number): number {
const sorted = [...data].sort((a, b) => a - b);
const index = sorted.findIndex(v => v >= value);
return index >= 0 ? (index / sorted.length) * 100 : 100;
}
private getBandPosition(price: number, upper: number, lower: number, middle: number): string {
if (price > upper) return "Above Upper Band";
if (price < lower) return "Below Lower Band";
if (price > middle) return "Upper Half";
return "Lower Half";
}
private plotBands(): void {
// Plot adaptive Bollinger Bands
bdpo.plot("splines", [this._upperBand], {
color: "#2196F3",
width: 2,
lineStyle: "solid"
}, {
title: `Dynamic Upper Band (${this._adaptivePeriod}, ${this._adaptiveStdDev.toFixed(2)})`
});
bdpo.plot("splines", [this._middleBand], {
color: "#FF9800",
width: 2,
lineStyle: "solid"
}, {
title: `Dynamic Middle Band (${this._adaptivePeriod})`
});
bdpo.plot("splines", [this._lowerBand], {
color: "#2196F3",
width: 2,
lineStyle: "solid"
}, {
title: `Dynamic Lower Band (${this._adaptivePeriod}, ${this._adaptiveStdDev.toFixed(2)})`
});
// Plot bandwidth in separate panel
bdpo.plot("splines", [this._bandwidth], {
color: "#9C27B0",
width: 2
}, {
title: "Bandwidth %",
panel: "bandwidth_panel"
});
}
private plotSqueezeSignals(signal: any): void {
if (!this.showSqueezeSignals) return;
const signalArray = new Array(this._closes.length).fill(null);
signalArray[signalArray.length - 1] = signal.type === 'squeeze' ?
this._lowerBand[this._lowerBand.length - 1] :
this._upperBand[this._upperBand.length - 1];
bdpo.plot("trades", signalArray.map((sig, i) =>
sig ? [signal.type === 'squeeze' ? -1 : 1, sig, signal.type.toUpperCase()] : null
).filter(Boolean), {
color: signal.type === 'squeeze' ? "#FF5722" : "#4CAF50",
size: 10,
shape: signal.type === 'squeeze' ? "circle" : "triangle"
}, {
title: `${signal.type.charAt(0).toUpperCase() + signal.type.slice(1)} Signal`
});
}
__shutdown__(): void {
const finalBandwidth = this._bandwidth[this._bandwidth.length - 1];
bdpo.logInfo(`Dynamic Bollinger Bands shutdown - Final bandwidth: ${finalBandwidth?.toFixed(4) || 'N/A'}`);
bdpo.logInfo(`Final parameters - Period: ${this._adaptivePeriod}, StdDev: ${this._adaptiveStdDev.toFixed(2)}`);
}
}
Indicator Overview
This indicator uses pivot point analysis to automatically identify significant support and resistance levels based on historical price action, helping traders identify key decision points.
class SupportResistanceLevels {
// Public configuration
public pivotLookback: number = 5; // Bars to look back for pivot identification
public strengthThreshold: number = 3; // Minimum touches to confirm level
public maxLevels: number = 10; // Maximum number of levels to track
public levelTolerance: number = 0.002; // Price tolerance for level confirmation (0.2%)
public minLevelSpacing: number = 0.01; // Minimum spacing between levels (1%)
public levelExpiryBars: number = 100; // Remove levels after N bars without touch
public showBreakouts: boolean = true; // Show breakout signals
public showRejections: boolean = true; // Show rejection signals
// Private state
private _pivotHighs: Array<{price: number, bar: number, strength: number}> = [];
private _pivotLows: Array<{price: number, bar: number, strength: number}> = [];
private _supportLevels: Array<{price: number, strength: number, touches: number[], lastTouch: number}> = [];
private _resistanceLevels: Array<{price: number, strength: number, touches: number[], lastTouch: number}> = [];
private _signals: Array<{type: string, price: number, bar: number, level: number}> = [];
__init__(): void {
this._pivotHighs = [];
this._pivotLows = [];
this._supportLevels = [];
this._resistanceLevels = [];
this._signals = [];
bdpo.logInfo("Support and Resistance Levels indicator initialized");
bdpo.logInfo(`Pivot Lookback: ${this.pivotLookback}, Strength Threshold: ${this.strengthThreshold}`);
}
__tick__(): any {
const closes = bdpo.getClose();
const highs = bdpo.getHigh();
const lows = bdpo.getLow();
const currentBar = closes.length - 1;
if (currentBar < this.pivotLookback * 2 + 5) {
return { status: "warming_up" };
}
// Identify new pivot points
this.identifyPivots(highs, lows, currentBar);
// Update support and resistance levels
this.updateLevels(closes[currentBar], currentBar);
// Check for signals
const signal = this.checkForSignals(closes[currentBar], highs[currentBar], lows[currentBar], currentBar);
// Plot levels and signals
this.plotLevels();
if (signal) {
this.plotSignal(signal);
}
return {
supportLevels: this._supportLevels.map(level => ({
price: level.price,
strength: level.strength,
touches: level.touches.length
})),
resistanceLevels: this._resistanceLevels.map(level => ({
price: level.price,
strength: level.strength,
touches: level.touches.length
})),
totalLevels: this._supportLevels.length + this._resistanceLevels.length,
lastSignal: signal
};
}
private identifyPivots(highs: number[], lows: number[], currentBar: number): void {
// Check for pivot high
if (currentBar >= this.pivotLookback * 2) {
const pivotBar = currentBar - this.pivotLookback;
const pivotHigh = highs[pivotBar];
let isValidPivotHigh = true;
// Check if this high is higher than surrounding bars
for (let i = 1; i <= this.pivotLookback; i++) {
if (highs[pivotBar - i] >= pivotHigh || highs[pivotBar + i] >= pivotHigh) {
isValidPivotHigh = false;
break;
}
}
if (isValidPivotHigh) {
this._pivotHighs.push({
price: pivotHigh,
bar: pivotBar,
strength: this.calculatePivotStrength(highs, pivotBar, 'high')
});
}
// Check for pivot low
const pivotLow = lows[pivotBar];
let isValidPivotLow = true;
for (let i = 1; i <= this.pivotLookback; i++) {
if (lows[pivotBar - i] <= pivotLow || lows[pivotBar + i] <= pivotLow) {
isValidPivotLow = false;
break;
}
}
if (isValidPivotLow) {
this._pivotLows.push({
price: pivotLow,
bar: pivotBar,
strength: this.calculatePivotStrength(lows, pivotBar, 'low')
});
}
}
}
private calculatePivotStrength(data: number[], pivotBar: number, type: 'high' | 'low'): number {
let strength = 1;
const pivotPrice = data[pivotBar];
// Calculate strength based on how much the pivot stands out
for (let i = 1; i <= this.pivotLookback; i++) {
const leftPrice = data[pivotBar - i];
const rightPrice = data[pivotBar + i];
if (type === 'high') {
const leftDiff = (pivotPrice - leftPrice) / leftPrice;
const rightDiff = (pivotPrice - rightPrice) / rightPrice;
strength += (leftDiff + rightDiff) * 100;
} else {
const leftDiff = (leftPrice - pivotPrice) / pivotPrice;
const rightDiff = (rightPrice - pivotPrice) / pivotPrice;
strength += (leftDiff + rightDiff) * 100;
}
}
return Math.max(1, strength);
}
private updateLevels(currentPrice: number, currentBar: number): void {
// Process pivot highs into resistance levels
this._pivotHighs.forEach(pivot => {
let levelFound = false;
for (let level of this._resistanceLevels) {
const priceDiff = Math.abs(pivot.price - level.price) / level.price;
if (priceDiff <= this.levelTolerance) {
// Update existing level
level.strength += pivot.strength;
level.touches.push(pivot.bar);
level.lastTouch = pivot.bar;
levelFound = true;
break;
}
}
if (!levelFound && this._resistanceLevels.length < this.maxLevels) {
// Create new resistance level
this._resistanceLevels.push({
price: pivot.price,
strength: pivot.strength,
touches: [pivot.bar],
lastTouch: pivot.bar
});
}
});
// Process pivot lows into support levels
this._pivotLows.forEach(pivot => {
let levelFound = false;
for (let level of this._supportLevels) {
const priceDiff = Math.abs(pivot.price - level.price) / level.price;
if (priceDiff <= this.levelTolerance) {
level.strength += pivot.strength;
level.touches.push(pivot.bar);
level.lastTouch = pivot.bar;
levelFound = true;
break;
}
}
if (!levelFound && this._supportLevels.length < this.maxLevels) {
this._supportLevels.push({
price: pivot.price,
strength: pivot.strength,
touches: [pivot.bar],
lastTouch: pivot.bar
});
}
});
// Clear processed pivots
this._pivotHighs = [];
this._pivotLows = [];
// Remove expired levels
this._supportLevels = this._supportLevels.filter(level =>
currentBar - level.lastTouch < this.levelExpiryBars && level.touches.length >= this.strengthThreshold
);
this._resistanceLevels = this._resistanceLevels.filter(level =>
currentBar - level.lastTouch < this.levelExpiryBars && level.touches.length >= this.strengthThreshold
);
// Sort levels by strength
this._supportLevels.sort((a, b) => b.strength - a.strength);
this._resistanceLevels.sort((a, b) => b.strength - a.strength);
}
private checkForSignals(close: number, high: number, low: number, currentBar: number): any {
// Check for breakouts and rejections
for (let resistance of this._resistanceLevels) {
const priceDiff = Math.abs(close - resistance.price) / resistance.price;
if (this.showBreakouts && high > resistance.price && close > resistance.price * (1 + this.levelTolerance)) {
const signal = {
type: 'resistance_breakout',
price: resistance.price,
bar: currentBar,
level: resistance.price,
strength: resistance.strength
};
this._signals.push(signal);
bdpo.logSuccess(`🚀 RESISTANCE BREAKOUT at ${resistance.price.toFixed(4)}`);
bdpo.logInfo(`Level strength: ${resistance.strength.toFixed(2)}, Touches: ${resistance.touches.length}`);
return signal;
}
if (this.showRejections && high > resistance.price * (1 - this.levelTolerance) &&
close < resistance.price * (1 - this.levelTolerance)) {
const signal = {
type: 'resistance_rejection',
price: resistance.price,
bar: currentBar,
level: resistance.price,
strength: resistance.strength
};
this._signals.push(signal);
bdpo.logWarn(`⬇️ RESISTANCE REJECTION at ${resistance.price.toFixed(4)}`);
return signal;
}
}
for (let support of this._supportLevels) {
if (this.showBreakouts && low < support.price && close < support.price * (1 - this.levelTolerance)) {
const signal = {
type: 'support_breakdown',
price: support.price,
bar: currentBar,
level: support.price,
strength: support.strength
};
this._signals.push(signal);
bdpo.logError(`📉 SUPPORT BREAKDOWN at ${support.price.toFixed(4)}`);
bdpo.logInfo(`Level strength: ${support.strength.toFixed(2)}, Touches: ${support.touches.length}`);
return signal;
}
if (this.showRejections && low < support.price * (1 + this.levelTolerance) &&
close > support.price * (1 + this.levelTolerance)) {
const signal = {
type: 'support_bounce',
price: support.price,
bar: currentBar,
level: support.price,
strength: support.strength
};
this._signals.push(signal);
bdpo.logSuccess(`⬆️ SUPPORT BOUNCE at ${support.price.toFixed(4)}`);
return signal;
}
}
return null;
}
private plotLevels(): void {
const dataLength = bdpo.getClose().length;
// Plot resistance levels
this._resistanceLevels.forEach((level, index) => {
const levelArray = new Array(dataLength).fill(level.price);
const opacity = Math.min(0.8, 0.3 + (level.strength / 10));
const lineWidth = Math.min(3, 1 + (level.touches.length / 2));
bdpo.plot("splines", [levelArray], {
color: "#F44336",
width: lineWidth,
lineStyle: level.touches.length >= 5 ? "solid" : "dashed",
opacity: opacity
}, {
title: `Resistance ${level.price.toFixed(4)} (${level.touches.length} touches)`
});
});
// Plot support levels
this._supportLevels.forEach((level, index) => {
const levelArray = new Array(dataLength).fill(level.price);
const opacity = Math.min(0.8, 0.3 + (level.strength / 10));
const lineWidth = Math.min(3, 1 + (level.touches.length / 2));
bdpo.plot("splines", [levelArray], {
color: "#4CAF50",
width: lineWidth,
lineStyle: level.touches.length >= 5 ? "solid" : "dashed",
opacity: opacity
}, {
title: `Support ${level.price.toFixed(4)} (${level.touches.length} touches)`
});
});
}
private plotSignal(signal: any): void {
const signalArray = new Array(bdpo.getClose().length).fill(null);
signalArray[signal.bar] = signal.price;
let color = "#FF9800";
let shape = "circle";
if (signal.type === 'resistance_breakout' || signal.type === 'support_bounce') {
color = "#4CAF50";
shape = "triangle";
} else if (signal.type === 'resistance_rejection' || signal.type === 'support_breakdown') {
color = "#F44336";
shape = "triangle-down";
}
bdpo.plot("trades", signalArray.map((sig, i) =>
sig ? [signal.type.includes('breakout') || signal.type.includes('bounce') ? 1 : -1, sig, signal.type.toUpperCase()] : null
).filter(Boolean), {
color: color,
size: 8,
shape: shape
}, {
title: signal.type.replace('_', ' ').toUpperCase()
});
}
__shutdown__(): void {
bdpo.logInfo(`Support/Resistance shutdown - ${this._supportLevels.length} support, ${this._resistanceLevels.length} resistance levels`);
bdpo.logInfo(`Total signals generated: ${this._signals.length}`);
}
}
- Automatic Level Detection: Uses pivot point analysis to find significant levels
- Strength Calculation: Levels get stronger with more touches and higher pivot significance
- Breakout Detection: Identifies when price breaks through established levels
- Rejection Signals: Detects when price respects support/resistance levels
- Adaptive Display: Line thickness and opacity indicate level strength
Trend Analysis Indicators
Indicator Overview
This indicator combines MACD signals from multiple timeframes to provide a more robust trend analysis, helping identify high-probability trade setups when multiple timeframes align.
class MultiTimeframeMacd {
// Public configuration
public fastPeriod: number = 12; // Fast EMA period
public slowPeriod: number = 26; // Slow EMA period
public signalPeriod: number = 9; // Signal line period
public timeframes: number[] = [1, 4, 15, 60]; // Timeframe multipliers
public minAlignment: number = 3; // Minimum timeframes for signal
public showDivergence: boolean = true; // Show divergence analysis
public divergenceLookback: number = 20; // Bars to look back for divergence
public alertOnAlignment: boolean = true; // Alert when timeframes align
// Private state
private _timeframeData: Map = new Map();
private _alignmentHistory: Array<{bar: number, aligned: number, direction: string}> = [];
private _divergenceSignals: Array<{bar: number, type: string, timeframe: number}> = [];
__init__(): void {
this._timeframeData.clear();
this._alignmentHistory = [];
this._divergenceSignals = [];
// Initialize data structures for each timeframe
this.timeframes.forEach(tf => {
this._timeframeData.set(tf, {
closes: [],
macdLine: [],
signalLine: [],
histogram: [],
trend: 'neutral',
strength: 0,
lastCrossover: null
});
});
bdpo.logInfo("Multi-Timeframe MACD initialized");
bdpo.logInfo(`Timeframes: ${this.timeframes.join(', ')}, Min Alignment: ${this.minAlignment}`);
}
__tick__(): any {
const closes = bdpo.getClose();
const highs = bdpo.getHigh();
const lows = bdpo.getLow();
const currentBar = closes.length - 1;
if (currentBar < Math.max(...this.timeframes) * this.slowPeriod + 10) {
return { status: "warming_up" };
}
// Update MACD for each timeframe
this.updateTimeframeData(closes, currentBar);
// Analyze alignment across timeframes
const alignment = this.analyzeAlignment(currentBar);
// Check for divergences
const divergence = this.checkDivergence(closes, highs, lows, currentBar);
// Plot multi-timeframe MACD
this.plotMultiTimeframeMacd();
// Generate alerts if needed
if (alignment.aligned >= this.minAlignment && this.alertOnAlignment) {
this.generateAlignmentAlert(alignment);
}
return {
alignment: alignment,
divergence: divergence,
timeframeSignals: this.getTimeframeSignals(),
overallTrend: this.getOverallTrend(),
strength: this.calculateOverallStrength()
};
}
private updateTimeframeData(closes: number[], currentBar: number): void {
this.timeframes.forEach(tf => {
const tfData = this._timeframeData.get(tf);
// Get timeframe-adjusted data
const tfCloses = this.getTimeframeData(closes, tf);
if (tfCloses.length < this.slowPeriod + this.signalPeriod) {
return;
}
// Calculate MACD for this timeframe
const macd = bdpo.macd(tfCloses, this.fastPeriod, this.slowPeriod, this.signalPeriod);
tfData.closes = tfCloses;
tfData.macdLine = macd.macd;
tfData.signalLine = macd.signal;
tfData.histogram = macd.histogram;
// Determine trend and strength
const latestMacd = macd.macd[macd.macd.length - 1];
const latestSignal = macd.signal[macd.signal.length - 1];
const latestHist = macd.histogram[macd.histogram.length - 1];
tfData.trend = latestMacd > latestSignal ? 'bullish' : 'bearish';
tfData.strength = Math.abs(latestHist);
// Check for crossovers
if (macd.histogram.length >= 2) {
const prevHist = macd.histogram[macd.histogram.length - 2];
if (prevHist <= 0 && latestHist > 0) {
tfData.lastCrossover = { type: 'bullish', bar: currentBar };
} else if (prevHist >= 0 && latestHist < 0) {
tfData.lastCrossover = { type: 'bearish', bar: currentBar };
}
}
});
}
private getTimeframeData(data: number[], multiplier: number): number[] {
if (multiplier === 1) return data;
const tfData: number[] = [];
for (let i = multiplier - 1; i < data.length; i += multiplier) {
// Use the close of each period
tfData.push(data[i]);
}
return tfData;
}
private analyzeAlignment(currentBar: number): any {
let bullishCount = 0;
let bearishCount = 0;
let totalStrength = 0;
const timeframeStates: any[] = [];
this.timeframes.forEach(tf => {
const tfData = this._timeframeData.get(tf);
if (tfData && tfData.trend) {
timeframeStates.push({
timeframe: tf,
trend: tfData.trend,
strength: tfData.strength,
crossover: tfData.lastCrossover
});
if (tfData.trend === 'bullish') {
bullishCount++;
} else if (tfData.trend === 'bearish') {
bearishCount++;
}
totalStrength += tfData.strength;
}
});
const alignment = {
bar: currentBar,
aligned: Math.max(bullishCount, bearishCount),
direction: bullishCount > bearishCount ? 'bullish' :
bearishCount > bullishCount ? 'bearish' : 'neutral',
bullishTimeframes: bullishCount,
bearishTimeframes: bearishCount,
totalStrength: totalStrength,
timeframeStates: timeframeStates
};
this._alignmentHistory.push(alignment);
// Keep only recent history
if (this._alignmentHistory.length > 100) {
this._alignmentHistory.shift();
}
return alignment;
}
private checkDivergence(closes: number[], highs: number[], lows: number[], currentBar: number): any {
if (!this.showDivergence || currentBar < this.divergenceLookback) {
return null;
}
// Check divergence on the primary timeframe (first in array)
const primaryTf = this.timeframes[0];
const tfData = this._timeframeData.get(primaryTf);
if (!tfData || tfData.macdLine.length < this.divergenceLookback) {
return null;
}
// Find recent highs and lows
const startBar = currentBar - this.divergenceLookback;
const priceHigh = Math.max(...highs.slice(startBar, currentBar + 1));
const priceLow = Math.min(...lows.slice(startBar, currentBar + 1));
const macdHigh = Math.max(...tfData.macdLine.slice(-this.divergenceLookback));
const macdLow = Math.min(...tfData.macdLine.slice(-this.divergenceLookback));
const currentPrice = closes[currentBar];
const currentMacd = tfData.macdLine[tfData.macdLine.length - 1];
// Check for bullish divergence (price makes lower low, MACD makes higher low)
if (currentPrice <= priceLow * 1.001 && currentMacd > macdLow * 1.1) {
const divergence = {
type: 'bullish_divergence',
bar: currentBar,
timeframe: primaryTf,
strength: (currentMacd - macdLow) / Math.abs(macdLow)
};
this._divergenceSignals.push(divergence);
bdpo.logSuccess(`📈 BULLISH DIVERGENCE detected on ${primaryTf}x timeframe`);
bdpo.logInfo(`Price: ${currentPrice.toFixed(4)}, MACD: ${currentMacd.toFixed(6)}`);
return divergence;
}
// Check for bearish divergence (price makes higher high, MACD makes lower high)
if (currentPrice >= priceHigh * 0.999 && currentMacd < macdHigh * 0.9) {
const divergence = {
type: 'bearish_divergence',
bar: currentBar,
timeframe: primaryTf,
strength: (macdHigh - currentMacd) / Math.abs(macdHigh)
};
this._divergenceSignals.push(divergence);
bdpo.logWarn(`📉 BEARISH DIVERGENCE detected on ${primaryTf}x timeframe`);
bdpo.logInfo(`Price: ${currentPrice.toFixed(4)}, MACD: ${currentMacd.toFixed(6)}`);
return divergence;
}
return null;
}
private generateAlignmentAlert(alignment: any): void {
if (alignment.aligned >= this.minAlignment) {
const strength = alignment.totalStrength > 0.001 ? 'Strong' : 'Moderate';
if (alignment.direction === 'bullish') {
bdpo.logSuccess(`🟢 ${strength} BULLISH ALIGNMENT`);
} else if (alignment.direction === 'bearish') {
bdpo.logError(`🔴 ${strength} BEARISH ALIGNMENT`);
}
bdpo.logInfo(`${alignment.aligned}/${this.timeframes.length} timeframes aligned ${alignment.direction}`);
bdpo.logInfo(`Total strength: ${alignment.totalStrength.toFixed(6)}`);
}
}
private getTimeframeSignals(): any {
const signals: any = {};
this.timeframes.forEach(tf => {
const tfData = this._timeframeData.get(tf);
if (tfData) {
signals[`tf_${tf}`] = {
trend: tfData.trend,
strength: tfData.strength,
lastCrossover: tfData.lastCrossover
};
}
});
return signals;
}
private getOverallTrend(): string {
if (this._alignmentHistory.length === 0) return 'neutral';
const recent = this._alignmentHistory[this._alignmentHistory.length - 1];
return recent.direction;
}
private calculateOverallStrength(): number {
if (this._alignmentHistory.length === 0) return 0;
const recent = this._alignmentHistory[this._alignmentHistory.length - 1];
return recent.totalStrength / this.timeframes.length;
}
private plotMultiTimeframeMacd(): void {
// Plot MACD for each timeframe in separate panels
this.timeframes.forEach((tf, index) => {
const tfData = this._timeframeData.get(tf);
if (tfData && tfData.macdLine.length > 0) {
const panelName = `macd_tf_${tf}`;
// Extend data to match main chart length
const dataLength = bdpo.getClose().length;
const extendedMacd = this.extendTimeframeData(tfData.macdLine, tf, dataLength);
const extendedSignal = this.extendTimeframeData(tfData.signalLine, tf, dataLength);
const extendedHist = this.extendTimeframeData(tfData.histogram, tf, dataLength);
// Plot MACD line
bdpo.plot("splines", [extendedMacd], {
color: "#2196F3",
width: 2
}, {
title: `MACD ${tf}x`,
panel: panelName
});
// Plot Signal line
bdpo.plot("splines", [extendedSignal], {
color: "#FF9800",
width: 1
}, {
title: `Signal ${tf}x`,
panel: panelName
});
// Plot Histogram
bdpo.plot("histogram", [extendedHist], {
color: tfData.trend === 'bullish' ? "#4CAF50" : "#F44336",
opacity: 0.6
}, {
title: `Histogram ${tf}x`,
panel: panelName
});
}
});
// Plot alignment strength
const alignmentStrengths = this._alignmentHistory.map(a => a.totalStrength);
if (alignmentStrengths.length > 0) {
const extendedStrengths = new Array(bdpo.getClose().length).fill(null);
const startIndex = Math.max(0, extendedStrengths.length - alignmentStrengths.length);
for (let i = 0; i < alignmentStrengths.length; i++) {
extendedStrengths[startIndex + i] = alignmentStrengths[i];
}
bdpo.plot("splines", [extendedStrengths], {
color: "#9C27B0",
width: 2
}, {
title: "Alignment Strength",
panel: "alignment_panel"
});
}
// Plot divergence signals
this._divergenceSignals.forEach(signal => {
const signalArray = new Array(bdpo.getClose().length).fill(null);
signalArray[signal.bar] = signal.type === 'bullish_divergence' ? 1 : -1;
bdpo.plot("trades", signalArray.map((sig, i) =>
sig ? [sig, bdpo.getClose()[i], `${signal.type.toUpperCase()}_${signal.timeframe}x`] : null
).filter(Boolean), {
color: signal.type === 'bullish_divergence' ? "#4CAF50" : "#F44336",
size: 8,
shape: signal.type === 'bullish_divergence' ? "triangle" : "triangle-down"
}, {
title: `Divergence ${signal.timeframe}x`
});
});
}
private extendTimeframeData(tfData: number[], multiplier: number, targetLength: number): number[] {
const extended = new Array(targetLength).fill(null);
for (let i = 0; i < tfData.length; i++) {
const targetIndex = (i + 1) * multiplier - 1;
if (targetIndex < targetLength) {
// Fill the range for this timeframe period
for (let j = Math.max(0, targetIndex - multiplier + 1); j <= targetIndex && j < targetLength; j++) {
extended[j] = tfData[i];
}
}
}
return extended;
}
__shutdown__(): void {
const finalAlignment = this._alignmentHistory[this._alignmentHistory.length - 1];
bdpo.logInfo(`Multi-Timeframe MACD shutdown`);
bdpo.logInfo(`Final alignment: ${finalAlignment?.aligned || 0}/${this.timeframes.length} timeframes`);
bdpo.logInfo(`Total divergence signals: ${this._divergenceSignals.length}`);
}
}
- Timeframe Data: This example uses simple period sampling - real MTF analysis may require actual timeframe data
- Performance: Multiple timeframe calculations can be computationally intensive
- Data Synchronization: Ensure proper alignment between different timeframe datasets
- Signal Filtering: Use alignment requirements to filter false signals
Indicator Overview
This enhanced Ichimoku indicator provides traditional Ichimoku components plus additional analysis features like cloud strength, momentum signals, and trend confirmation across multiple timeframes.
class CustomIchimokuCloud {
// Public configuration
public tenkanPeriod: number = 9; // Tenkan-sen (Conversion Line) period
public kijunPeriod: number = 26; // Kijun-sen (Base Line) period
public senkouBPeriod: number = 52; // Senkou Span B period
public displacement: number = 26; // Cloud displacement
public showSignals: boolean = true; // Show entry/exit signals
public showCloudStrength: boolean = true; // Show cloud strength analysis
public strongCloudThreshold: number = 0.005; // Strong cloud threshold (0.5%)
public signalConfirmation: boolean = true; // Require multiple confirmations
public alertOnSignals: boolean = true; // Generate alerts for signals
// Private state
private _tenkanSen: number[] = [];
private _kijunSen: number[] = [];
private _senkouSpanA: number[] = [];
private _senkouSpanB: number[] = [];
private _chikouSpan: number[] = [];
private _cloudStrength: number[] = [];
private _trendDirection: string[] = [];
private _signals: Array<{type: string, bar: number, price: number, strength: number}> = [];
private _lastSignalBar: number = -1;
__init__(): void {
this._tenkanSen = [];
this._kijunSen = [];
this._senkouSpanA = [];
this._senkouSpanB = [];
this._chikouSpan = [];
this._cloudStrength = [];
this._trendDirection = [];
this._signals = [];
this._lastSignalBar = -1;
bdpo.logInfo("Custom Ichimoku Cloud initialized");
bdpo.logInfo(`Periods - Tenkan: ${this.tenkanPeriod}, Kijun: ${this.kijunPeriod}, Senkou B: ${this.senkouBPeriod}`);
}
__tick__(): any {
const closes = bdpo.getClose();
const highs = bdpo.getHigh();
const lows = bdpo.getLow();
const currentBar = closes.length - 1;
if (currentBar < this.senkouBPeriod + this.displacement + 5) {
return { status: "warming_up" };
}
// Calculate Ichimoku components
this.calculateIchimokuLines(highs, lows, closes, currentBar);
// Analyze cloud strength and trend
this.analyzeCloudStrength(currentBar);
// Check for signals
const signal = this.checkForSignals(closes, highs, lows, currentBar);
// Plot Ichimoku components
this.plotIchimoku();
if (signal) {
this.plotSignal(signal);
}
return {
tenkanSen: this._tenkanSen[this._tenkanSen.length - 1],
kijunSen: this._kijunSen[this._kijunSen.length - 1],
senkouSpanA: this._senkouSpanA[this._senkouSpanA.length - 1],
senkouSpanB: this._senkouSpanB[this._senkouSpanB.length - 1],
chikouSpan: this._chikouSpan[this._chikouSpan.length - 1],
cloudStrength: this._cloudStrength[this._cloudStrength.length - 1],
trendDirection: this._trendDirection[this._trendDirection.length - 1],
aboveCloud: this.isAboveCloud(closes[currentBar], currentBar),
signal: signal,
totalSignals: this._signals.length
};
}
private calculateIchimokuLines(highs: number[], lows: number[], closes: number[], currentBar: number): void {
// Calculate Tenkan-sen (Conversion Line)
this._tenkanSen = [];
for (let i = this.tenkanPeriod - 1; i < highs.length; i++) {
const periodHigh = Math.max(...highs.slice(i - this.tenkanPeriod + 1, i + 1));
const periodLow = Math.min(...lows.slice(i - this.tenkanPeriod + 1, i + 1));
this._tenkanSen.push((periodHigh + periodLow) / 2);
}
// Calculate Kijun-sen (Base Line)
this._kijunSen = [];
for (let i = this.kijunPeriod - 1; i < highs.length; i++) {
const periodHigh = Math.max(...highs.slice(i - this.kijunPeriod + 1, i + 1));
const periodLow = Math.min(...lows.slice(i - this.kijunPeriod + 1, i + 1));
this._kijunSen.push((periodHigh + periodLow) / 2);
}
// Calculate Senkou Span A (Leading Span A)
this._senkouSpanA = [];
const minLength = Math.min(this._tenkanSen.length, this._kijunSen.length);
for (let i = 0; i < minLength; i++) {
this._senkouSpanA.push((this._tenkanSen[i] + this._kijunSen[i]) / 2);
}
// Calculate Senkou Span B (Leading Span B)
this._senkouSpanB = [];
for (let i = this.senkouBPeriod - 1; i < highs.length; i++) {
const periodHigh = Math.max(...highs.slice(i - this.senkouBPeriod + 1, i + 1));
const periodLow = Math.min(...lows.slice(i - this.senkouBPeriod + 1, i + 1));
this._senkouSpanB.push((periodHigh + periodLow) / 2);
}
// Calculate Chikou Span (Lagging Span) - just the close displaced backwards
this._chikouSpan = [...closes];
}
private analyzeCloudStrength(currentBar: number): void {
this._cloudStrength = [];
this._trendDirection = [];
for (let i = 0; i < Math.min(this._senkouSpanA.length, this._senkouSpanB.length); i++) {
const spanA = this._senkouSpanA[i];
const spanB = this._senkouSpanB[i];
// Calculate cloud thickness as percentage
const thickness = Math.abs(spanA - spanB);
const midPrice = (spanA + spanB) / 2;
const strengthPct = midPrice > 0 ? (thickness / midPrice) : 0;
this._cloudStrength.push(strengthPct);
// Determine trend direction based on cloud
if (spanA > spanB) {
this._trendDirection.push(strengthPct > this.strongCloudThreshold ? 'strong_bullish' : 'weak_bullish');
} else if (spanA < spanB) {
this._trendDirection.push(strengthPct > this.strongCloudThreshold ? 'strong_bearish' : 'weak_bearish');
} else {
this._trendDirection.push('neutral');
}
}
}
private checkForSignals(closes: number[], highs: number[], lows: number[], currentBar: number): any {
if (!this.showSignals || currentBar <= this._lastSignalBar + 3) {
return null; // Prevent signal spam
}
const currentPrice = closes[currentBar];
const tenkanIdx = currentBar - (closes.length - this._tenkanSen.length);
const kijunIdx = currentBar - (closes.length - this._kijunSen.length);
if (tenkanIdx < 1 || kijunIdx < 1) return null;
const currentTenkan = this._tenkanSen[tenkanIdx];
const prevTenkan = this._tenkanSen[tenkanIdx - 1];
const currentKijun = this._kijunSen[kijunIdx];
const prevKijun = this._kijunSen[kijunIdx - 1];
// Check for Tenkan-Kijun crossover
let signal = null;
// Bullish signal: Tenkan crosses above Kijun
if (prevTenkan <= prevKijun && currentTenkan > currentKijun) {
signal = {
type: 'tenkan_kijun_bullish',
bar: currentBar,
price: currentPrice,
strength: this.calculateSignalStrength(currentBar, 'bullish'),
tenkan: currentTenkan,
kijun: currentKijun
};
}
// Bearish signal: Tenkan crosses below Kijun
else if (prevTenkan >= prevKijun && currentTenkan < currentKijun) {
signal = {
type: 'tenkan_kijun_bearish',
bar: currentBar,
price: currentPrice,
strength: this.calculateSignalStrength(currentBar, 'bearish'),
tenkan: currentTenkan,
kijun: currentKijun
};
}
// Additional confirmation checks
if (signal && this.signalConfirmation) {
const cloudPosition = this.isAboveCloud(currentPrice, currentBar);
const chikouConfirmation = this.checkChikouConfirmation(closes, currentBar);
signal.cloudPosition = cloudPosition;
signal.chikouConfirmation = chikouConfirmation;
// Only proceed with confirmed signals
if ((signal.type.includes('bullish') && cloudPosition === 'above' && chikouConfirmation) ||
(signal.type.includes('bearish') && cloudPosition === 'below' && chikouConfirmation)) {
signal.confirmed = true;
} else {
signal.confirmed = false;
signal = null; // Filter out unconfirmed signals
}
}
if (signal) {
this._signals.push(signal);
this._lastSignalBar = currentBar;
if (this.alertOnSignals) {
this.generateSignalAlert(signal);
}
}
return signal;
}
private calculateSignalStrength(currentBar: number, direction: string): number {
let strength = 1;
// Add cloud strength to signal strength
if (this._cloudStrength.length > 0) {
const cloudIdx = Math.min(currentBar, this._cloudStrength.length - 1);
strength += this._cloudStrength[cloudIdx] * 100;
}
// Add trend direction confirmation
if (this._trendDirection.length > 0) {
const trendIdx = Math.min(currentBar, this._trendDirection.length - 1);
const trend = this._trendDirection[trendIdx];
if ((direction === 'bullish' && trend.includes('bullish')) ||
(direction === 'bearish' && trend.includes('bearish'))) {
strength *= trend.includes('strong') ? 2 : 1.5;
}
}
return Math.min(10, strength); // Cap at 10
}
private isAboveCloud(price: number, bar: number): string {
// Get cloud values at current position (accounting for displacement)
const spanAIdx = Math.max(0, bar - this.displacement);
const spanBIdx = Math.max(0, bar - this.displacement);
if (spanAIdx >= this._senkouSpanA.length || spanBIdx >= this._senkouSpanB.length) {
return 'unknown';
}
const spanA = this._senkouSpanA[spanAIdx];
const spanB = this._senkouSpanB[spanBIdx];
const cloudTop = Math.max(spanA, spanB);
const cloudBottom = Math.min(spanA, spanB);
if (price > cloudTop) return 'above';
if (price < cloudBottom) return 'below';
return 'inside';
}
private checkChikouConfirmation(closes: number[], currentBar: number): boolean {
// Check if Chikou Span is clear of price action
const chikouBar = currentBar - this.displacement;
if (chikouBar < 0 || chikouBar >= closes.length) return false;
const chikouPrice = closes[currentBar]; // Current close displaced back
const historicalPrice = closes[chikouBar];
// Simple confirmation: Chikou should be above/below historical price in trend direction
return Math.abs(chikouPrice - historicalPrice) > historicalPrice * 0.001; // 0.1% minimum difference
}
private generateSignalAlert(signal: any): void {
const direction = signal.type.includes('bullish') ? 'BULLISH' : 'BEARISH';
const emoji = signal.type.includes('bullish') ? '🟢' : '🔴';
if (signal.confirmed) {
bdpo.logSuccess(`${emoji} CONFIRMED ${direction} ICHIMOKU SIGNAL`);
} else {
bdpo.logInfo(`${emoji} ${direction} ICHIMOKU SIGNAL (Unconfirmed)`);
}
bdpo.logInfo(`Tenkan: ${signal.tenkan?.toFixed(4)}, Kijun: ${signal.kijun?.toFixed(4)}`);
bdpo.logInfo(`Strength: ${signal.strength?.toFixed(2)}, Cloud Position: ${signal.cloudPosition}`);
}
private plotIchimoku(): void {
const dataLength = bdpo.getClose().length;
// Extend arrays to match data length
const extendedTenkan = this.extendArray(this._tenkanSen, dataLength);
const extendedKijun = this.extendArray(this._kijunSen, dataLength);
const extendedSpanA = this.extendArrayWithDisplacement(this._senkouSpanA, dataLength, this.displacement);
const extendedSpanB = this.extendArrayWithDisplacement(this._senkouSpanB, dataLength, this.displacement);
const extendedChikou = this.extendArrayWithDisplacement(this._chikouSpan, dataLength, -this.displacement);
// Plot Tenkan-sen (Conversion Line)
bdpo.plot("splines", [extendedTenkan], {
color: "#FF6B6B",
width: 1,
lineStyle: "solid"
}, {
title: `Tenkan-sen (${this.tenkanPeriod})`
});
// Plot Kijun-sen (Base Line)
bdpo.plot("splines", [extendedKijun], {
color: "#4ECDC4",
width: 2,
lineStyle: "solid"
}, {
title: `Kijun-sen (${this.kijunPeriod})`
});
// Plot Senkou Span A (Leading Span A)
bdpo.plot("splines", [extendedSpanA], {
color: "#95E1D3",
width: 1,
lineStyle: "solid"
}, {
title: "Senkou Span A"
});
// Plot Senkou Span B (Leading Span B)
bdpo.plot("splines", [extendedSpanB], {
color: "#FFD93D",
width: 1,
lineStyle: "solid"
}, {
title: "Senkou Span B"
});
// Plot Chikou Span (Lagging Span)
bdpo.plot("splines", [extendedChikou], {
color: "#A8E6CF",
width: 1,
lineStyle: "dashed"
}, {
title: "Chikou Span"
});
// Plot cloud strength if enabled
if (this.showCloudStrength && this._cloudStrength.length > 0) {
const extendedStrength = this.extendArray(this._cloudStrength, dataLength);
const strengthPercent = extendedStrength.map(s => s ? s * 100 : null);
bdpo.plot("splines", [strengthPercent], {
color: "#9C27B0",
width: 2
}, {
title: "Cloud Strength %",
panel: "ichimoku_strength"
});
}
}
private plotSignal(signal: any): void {
const signalArray = new Array(bdpo.getClose().length).fill(null);
signalArray[signal.bar] = signal.price;
const color = signal.type.includes('bullish') ? "#4CAF50" : "#F44336";
const shape = signal.confirmed ? "triangle" : "circle";
bdpo.plot("trades", signalArray.map((sig, i) =>
sig ? [signal.type.includes('bullish') ? 1 : -1, sig, signal.type.toUpperCase()] : null
).filter(Boolean), {
color: color,
size: signal.confirmed ? 10 : 6,
shape: shape
}, {
title: `Ichimoku ${signal.type.replace('_', ' ')}`
});
}
private extendArray(arr: number[], targetLength: number): number[] {
const extended = new Array(targetLength).fill(null);
const startIndex = targetLength - arr.length;
for (let i = 0; i < arr.length; i++) {
if (startIndex + i >= 0) {
extended[startIndex + i] = arr[i];
}
}
return extended;
}
private extendArrayWithDisplacement(arr: number[], targetLength: number, displacement: number): number[] {
const extended = new Array(targetLength).fill(null);
for (let i = 0; i < arr.length; i++) {
const targetIndex = i + displacement + (targetLength - arr.length);
if (targetIndex >= 0 && targetIndex < targetLength) {
extended[targetIndex] = arr[i];
}
}
return extended;
}
__shutdown__(): void {
const confirmedSignals = this._signals.filter(s => s.confirmed).length;
bdpo.logInfo(`Custom Ichimoku shutdown - Total signals: ${this._signals.length}, Confirmed: ${confirmedSignals}`);
if (this._trendDirection.length > 0) {
const currentTrend = this._trendDirection[this._trendDirection.length - 1];
bdpo.logInfo(`Final trend: ${currentTrend}`);
}
}
}
- Traditional Components: All standard Ichimoku lines with proper displacement
- Cloud Strength Analysis: Quantifies cloud thickness for trend strength assessment
- Signal Confirmation: Multiple confirmation criteria for higher quality signals
- Trend Classification: Categorizes trends as strong/weak bullish/bearish
- Visual Enhancement: Color-coded components with strength indicators
- Analysis Only: Indicators provide market analysis and cannot place trades
- Visual Overlays: Use plotting functions to display indicators on charts
- Parameter Tuning: Adjust parameters based on your analysis timeframe and market
- Performance: Complex indicators may require optimization for real-time use
- Data Requirements: Ensure sufficient historical data for accurate calculations